From 5f759b03c24883069761dfc1403e49e9ab48ec48 Mon Sep 17 00:00:00 2001 From: Staars Date: Sat, 21 Mar 2020 10:17:21 +0100 Subject: [PATCH 001/547] update xsns_62_MI_HM10.ino --- tasmota/xsns_62_MI_HM10.ino | 751 +++++++++++++++++++++++++++--------- 1 file changed, 562 insertions(+), 189 deletions(-) diff --git a/tasmota/xsns_62_MI_HM10.ino b/tasmota/xsns_62_MI_HM10.ino index 17cc094ad..83252c4f6 100644 --- a/tasmota/xsns_62_MI_HM10.ino +++ b/tasmota/xsns_62_MI_HM10.ino @@ -20,7 +20,9 @@ -------------------------------------------------------------------------------------------- Version yyyymmdd Action Description -------------------------------------------------------------------------------------------- - + 0.9.2.0 20200317 added - MiBeacon-support, add Flora, MJ_HT_V1 and CGD1, add dew point, + add AUTO(-scan), RULES-message + --- 0.9.1.0 20200209 added - LYWSD02-support, including setting the time --- 0.9.0.0 20200130 started - initial development by Christian Baars (support LYWSD03 only) @@ -40,8 +42,7 @@ TasmotaSerial *HM10Serial; #define HM10_MAX_TASK_NUMBER 12 uint8_t HM10_TASK_LIST[HM10_MAX_TASK_NUMBER+1][2]; // first value: kind of task - second value: delay in x * 100ms -#define HM10_MAX_RX_BUF 512 -char HM10_RX_STRING[HM10_MAX_RX_BUF] = {0}; +#define HM10_MAX_RX_BUF 160 struct { uint8_t current_task_delay; // number of 100ms-cycles @@ -53,13 +54,14 @@ struct { uint32_t time; uint8_t timebuf[4]; }; + uint16_t autoScanInterval; struct { + uint32_t awaiting:8; uint32_t init:1; uint32_t pending_task:1; uint32_t connected:1; uint32_t subscribed:1; - uint32_t awaitingHT:1; - uint32_t awaitingB:1; + uint32_t autoScan:1; // TODO: more to come } mode; struct { @@ -68,15 +70,52 @@ struct { } state; } HM10; -#pragma pack(1) +#pragma pack(1) // byte-aligned structures to read the sensor data struct { uint16_t temp; uint8_t hum; } LYWSD0x_HT; + +struct { + uint8_t spare; + uint16_t temp; + uint16_t hum; +} CGD1_HT; + +struct { + uint16_t temp; + uint8_t spare; + uint32_t lux; + uint8_t moist; + uint16_t fert; +} Flora_TLMF; // temeprature, lux, moisture, fertility + +struct mi_beacon_t{ + uint16_t frame; + uint16_t productID; + uint8_t counter; + uint8_t Mac[6]; + uint8_t spare; + uint8_t type; + uint8_t ten; + uint8_t size; + union { + struct{ //0d + uint16_t temp; + uint16_t hum; + }HT; + uint8_t bat; //0a + uint16_t temp; //04 + uint16_t hum; //06 + uint32_t lux; //07 + uint8_t moist; //08 + uint16_t fert; //09 + }; +}; #pragma pack(0) struct mi_sensor_t{ - uint8_t type; //Flora = 1; MI-HT_V1=2; LYWSD02=3; LYWSD03=4 + uint8_t type; //Flora = 1; MI-HT_V1=2; LYWSD02=3; LYWSD03=4; CGG1=5; CGD1=6 uint8_t serial[6]; uint8_t showedUp; float temp; //Flora, MJ_HT_V1, LYWSD0x @@ -84,13 +123,13 @@ struct mi_sensor_t{ struct { float moisture; float fertility; - uint16_t lux; + uint32_t lux; }; // Flora struct { float hum; - uint8_t bat; }; // MJ_HT_V1, LYWSD0x }; + uint8_t bat; }; std::vector MIBLEsensors; @@ -103,24 +142,30 @@ std::vector MIBLEsensors; const char S_JSON_HM10_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_HM10 "%s\":%d}"; const char S_JSON_HM10_COMMAND[] PROGMEM = "{\"" D_CMND_HM10 "%s%s\"}"; -const char kHM10_Commands[] PROGMEM = "Scan|AT|Period|Baud|Time"; +const char kHM10_Commands[] PROGMEM = "Scan|AT|Period|Baud|Time|Auto"; #define FLORA 1 #define MJ_HT_V1 2 #define LYWSD02 3 #define LYWSD03MMC 4 +#define CGG1 5 +#define CGD1 6 -uint8_t kHM10SlaveID[4][3] = { 0xC4,0x7C,0x8D, // Flora - 0x58,0x2D,0x34, // MJ_HT_V1 - 0xE7,0x2E,0x00, // LYWSD02 - 0xA4,0xC1,0x38, // LYWSD03 - }; +const uint16_t kHM10SlaveID[6]={ 0x0098, // Flora + 0x01aa, // MJ_HT_V1 + 0x045b, // LYWSD02 + 0x055b, // LYWSD03 + 0x0347, // CGG1 + 0x0576 // CGD1 + }; const char kHM10SlaveType1[] PROGMEM = "Flora"; const char kHM10SlaveType2[] PROGMEM = "MJ_HT_V1"; const char kHM10SlaveType3[] PROGMEM = "LYWSD02"; const char kHM10SlaveType4[] PROGMEM = "LYWSD03"; -const char * kHM10SlaveType[] PROGMEM = {kHM10SlaveType1,kHM10SlaveType2,kHM10SlaveType3,kHM10SlaveType4}; +const char kHM10SlaveType5[] PROGMEM = "CGG1"; +const char kHM10SlaveType6[] PROGMEM = "CGD1"; +const char * kHM10SlaveType[] PROGMEM = {kHM10SlaveType1,kHM10SlaveType2,kHM10SlaveType3,kHM10SlaveType4,kHM10SlaveType5,kHM10SlaveType6}; /*********************************************************************************************\ * enumerations @@ -131,9 +176,19 @@ enum HM10_Commands { // commands useable in console or rules CMND_HM10_AT, // send AT-command for debugging and special configuration CMND_HM10_PERIOD, // set period like TELE-period in seconds between read-cycles CMND_HM10_BAUD, // serial speed of ESP8266 (<-> HM10), does not change baud rate of HM10 - CMND_HM10_TIME // set LYWSD02-Time from ESP8266-time + CMND_HM10_TIME, // set LYWSD02-Time from ESP8266-time + CMND_HM10_AUTO // do discovery scans permanently to receive MiBeacons in seconds between read-cycles }; +enum HM10_awaitData: uint8_t { + none = 0, + tempHumLY = 1, + TLMF = 2, + bat = 3, + tempHumCGD1 = 4, + discScan = 5 + }; + /*********************************************************************************************\ * Task codes defines \*********************************************************************************************/ @@ -143,7 +198,7 @@ enum HM10_Commands { // commands useable in console or rules #define TASK_HM10_IMME1 2 // change imme to 1 #define TASK_HM10_RENEW 3 // device factory setting #define TASK_HM10_RESET 4 // device reset -#define TASK_HM10_DISC 5 // device discovery scan +#define TASK_HM10_DISC 5 // device discovery scan: AT+DISA? #define TASK_HM10_CONN 6 // connect to given MAC #define TASK_HM10_VERSION 7 // query FW version #define TASK_HM10_NAME 8 // query device name @@ -151,14 +206,23 @@ enum HM10_Commands { // commands useable in console or rules #define TASK_HM10_DISCONN 10 // disconnect #define TASK_HM10_SUB_L3 11 // subscribe to service handle 37 #define TASK_HM10_READ_HT 12 // read from handle 36 -> Hum & Temp -#define TASK_HM10_FINDALLCHARS 13 // read all available characteristics -#define TASK_HM10_UN_L3 14 // subscribe service handle 37 -#define TASK_HM10_DELAY_SUB 15 // start reading from subscription delayed +#define TASK_HM10_SCAN9 13 // longest discovery scan possible +#define TASK_HM10_UN_L3 14 // unsubscribe service handle 37 +#define TASK_HM10_DELAY_SUB_LY 15 // start reading from subscription delayed #define TASK_HM10_READ_BT_L3 16 // read from handle 3A -> Battery #define TASK_HM10_SUB_L2 17 // subscribe to service handle 3C -#define TASK_HM10_UN_L2 18 // subscribe service handle 3C +#define TASK_HM10_UN_L2 18 // unsubscribe service handle 3C #define TASK_HM10_READ_BT_L2 19 // read from handle 43 -> Battery #define TASK_HM10_TIME_L2 20 // set time of LYWSD02 to system time +#define TASK_HM10_SHOW0 21 // set verbositiy to minimum +#define TASK_HM10_READ_BF_FL 22 // read battery and firmware from flower care +#define TASK_HM10_CALL_TLMF_FL 23 // write 0xa01f to handle 0x33 to init sensor readings +#define TASK_HM10_READ_TLMF_FL 24 // read temp,lux,moist and fert from flower care +#define TASK_HM10_SUB_HT_CGD1 25 // subscribe to service handle 4b +#define TASK_HM10_UN_HT_CGD1 26 // unsubscribe service handle 4b +#define TASK_HM10_READ_B_CGD1 27 // read service handle 11 +#define TASK_HM10_DELAY_SUB_CGD1 28 // start reading from subscription delayed +#define TASK_HM10_STATUS_EVENT 29 // process status for RULES #define TASK_HM10_DONE 99 // used, if there was a task in the slot or just to wait @@ -178,6 +242,14 @@ void HM10_TaskReplaceInSlot(uint8_t task, uint8_t slot){ HM10_TASK_LIST[slot][0] = task; } +void HM10_ReverseMAC(uint8_t _mac[]){ + uint8_t _reversedMAC[6]; + for (uint8_t i=0; i<6; i++){ + _reversedMAC[5-i] = _mac[i]; + } + memcpy(_mac,_reversedMAC, sizeof(_reversedMAC)); +} + /*********************************************************************************************\ * chained tasks \*********************************************************************************************/ @@ -187,12 +259,15 @@ void HM10_Reset(void) { HM10_Launchtask(TASK_HM10_DISCONN,0,1); // disco HM10_Launchtask(TASK_HM10_IMME1,2,1); // set imme to 1 HM10_Launchtask(TASK_HM10_RESET,3,1); // reset Device HM10_Launchtask(TASK_HM10_VERSION,4,10); // read SW Version - HM10_Launchtask(TASK_HM10_DISC,5,50); // discovery + HM10_Launchtask(TASK_HM10_SCAN9,5,2); // scan time 9 seconds + HM10_Launchtask(TASK_HM10_DISC,6,2); // discovery + HM10_Launchtask(TASK_HM10_STATUS_EVENT,7,2); // status } void HM10_Discovery_Scan(void) { HM10_Launchtask(TASK_HM10_DISCONN,0,1); // disconnect - HM10_Launchtask(TASK_HM10_DISC,1,1); // discovery + HM10_Launchtask(TASK_HM10_DISC,1,5); // discovery + HM10_Launchtask(TASK_HM10_STATUS_EVENT,2,2); // status } void HM10_Read_LYWSD03(void) { @@ -221,66 +296,84 @@ void HM10_Time_LYWSD02(void) { HM10_Launchtask(TASK_HM10_DISCONN,4,5); // disconnect } +void HM10_Read_Flora(void) { + HM10_Launchtask(TASK_HM10_DISCONN,0,0); // disconnect + HM10_Launchtask(TASK_HM10_CONN,1,1); // connect + HM10_Launchtask(TASK_HM10_FEEDBACK,2,5); // get OK+CONN + HM10_Launchtask(TASK_HM10_READ_BF_FL,3,20); // read battery + HM10_Launchtask(TASK_HM10_CALL_TLMF_FL,4,30); // read TLMF + HM10_Launchtask(TASK_HM10_DISCONN,5,10); // disconnect + } + +void HM10_Read_CGD1(void) { + HM10_Launchtask(TASK_HM10_CONN,0,1); // connect + HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); // get OK+CONN + HM10_Launchtask(TASK_HM10_SUB_HT_CGD1,2,20); // subscribe + HM10_Launchtask(TASK_HM10_UN_HT_CGD1,3,10); // unsubscribe + HM10_Launchtask(TASK_HM10_READ_B_CGD1,4,5); // read Battery + HM10_Launchtask(TASK_HM10_DISCONN,5,5); // disconnect + } + /** * @brief Return the slot number of a known sensor or return create new sensor slot * * @param _serial BLE address of the sensor - * @param _type Type number of the sensor, 0xff for Auto-type + * @param _type Type number of the sensor * @return uint32_t Known or new slot in the sensors-vector */ -uint32_t MIBLEgetSensorSlot(uint8_t (&_serial)[6], uint8_t _type){ - if(_type==0xff){ - DEBUG_SENSOR_LOG(PSTR("MIBLE: will test MAC-type")); - for (uint32_t i=0;i<4;i++){ - if(memcmp(_serial,kHM10SlaveID+i,3)==0){ - DEBUG_SENSOR_LOG(PSTR("MIBLE: MAC is type %u"), i); - _type = i+1; - } - else { - DEBUG_SENSOR_LOG(PSTR("MIBLE: MAC-type is unknown")); - } +uint32_t MIBLEgetSensorSlot(uint8_t (&_serial)[6], uint16_t _type){ + + DEBUG_SENSOR_LOG(PSTR("%s: will test ID-type: %x"),D_CMND_HM10, _type); + bool _success = false; + for (uint32_t i=0;i<6;i++){ // i < sizeof(kHM10SlaveID) gives compiler warning + if(_type == kHM10SlaveID[i]){ + DEBUG_SENSOR_LOG(PSTR("HM10: ID is type %u"), i); + _type = i+1; + _success = true; + } + else { + DEBUG_SENSOR_LOG(PSTR("%s: ID-type is not: %x"),D_CMND_HM10,kHM10SlaveID[i]); } } - if(_type==0xff) return _type; // error - - DEBUG_SENSOR_LOG(PSTR("MIBLE: vector size %u"), MIBLEsensors.size()); + if(!_success) return 0xff; + + DEBUG_SENSOR_LOG(PSTR("%s: vector size %u"),D_CMND_HM10, MIBLEsensors.size()); for(uint32_t i=0; ihardwareSerial()) { ClaimSerial(); - DEBUG_SENSOR_LOG(PSTR("HM10: claim HW")); + DEBUG_SENSOR_LOG(PSTR("%s: claim HW"),D_CMND_HM10); } HM10_Reset(); HM10.mode.pending_task = 1; @@ -305,68 +398,147 @@ void HM10SerialInit(void) { return; } -/** - * @brief convert Mac-String to byte array - * - * @param string Hex-string, must contain 12 chars (no error checking) - * @param _mac Must be a uint8_t[6], filled with zeros - */ - -void HM10MACStringToBytes(const char* string, uint8_t _mac[]) { - uint32_t index = 0; - while (index < 12) { - char c = string[index]; - uint32_t value = 0; - if(c >= '0' && c <= '9') - value = (c - '0'); - else if (c >= 'A' && c <= 'F') - value = (10 + (c - 'A')); - _mac[(index/2)] += value << (((index + 1) % 2) * 4); - // DEBUG_SENSOR_LOG(PSTR("HM10: Char: %c, Value: %x, Index/2: %u, valueadded: %x, MAC-index: %x"), c, value,(index/2),value << (((index + 1) % 2) * 4), _mac[index/2]); - index++; - } - DEBUG_SENSOR_LOG(PSTR("HM10: MAC-array: %x%x%x%x%x%x"),_mac[0],_mac[1],_mac[2],_mac[3],_mac[4],_mac[5]); -} - - /*********************************************************************************************\ * parse the response \*********************************************************************************************/ -void HM10ParseResponse(char *buf) { +void HM10parseMiBeacon(char * _buf, uint32_t _slot){ + float _tempFloat; + mi_beacon_t _beacon; + if (MIBLEsensors.at(_slot).type==2){ + memcpy((uint8_t*)&_beacon+1,(uint8_t*)_buf, sizeof(_beacon)); // shift by one byte for the MJ_HT_V1 + memcpy((uint8_t*)&_beacon.Mac,(uint8_t*)&_beacon.Mac+1,6); // but shift back the MAC + } + else{ + memcpy((void*)&_beacon,(void*)_buf, sizeof(_beacon)); + } + HM10_ReverseMAC(_beacon.Mac); + if(memcmp(_beacon.Mac,MIBLEsensors.at(_slot).serial,sizeof(_beacon.Mac))!=0){ + if (MIBLEsensors.at(_slot).showedUp>3) return; // probably false alarm from a damaged packet + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: remove garbage sensor"),D_CMND_HM10); + DEBUG_SENSOR_LOG(PSTR("%s i: %x %x %x %x %x %x"),D_CMND_HM10, MIBLEsensors.at(_slot).serial[5], MIBLEsensors.at(_slot).serial[4],MIBLEsensors.at(_slot).serial[3],MIBLEsensors.at(_slot).serial[2],MIBLEsensors.at(_slot).serial[1],MIBLEsensors.at(_slot).serial[0]); + DEBUG_SENSOR_LOG(PSTR("%s n: %x %x %x %x %x %x"),D_CMND_HM10, _beacon.Mac[5], _beacon.Mac[4], _beacon.Mac[3],_beacon.Mac[2],_beacon.Mac[1],_beacon.Mac[0]); + MIBLEsensors.erase(MIBLEsensors.begin()+_slot); + return; + } + if (MIBLEsensors.at(_slot).showedUp<4) MIBLEsensors.at(_slot).showedUp++; + + DEBUG_SENSOR_LOG(PSTR("MiBeacon type:%02x: %02x %02x %02x %02x %02x %02x %02x %02x"),_beacon.type, (uint8_t)_buf[0],(uint8_t)_buf[1],(uint8_t)_buf[2],(uint8_t)_buf[3],(uint8_t)_buf[4],(uint8_t)_buf[5],(uint8_t)_buf[6],(uint8_t)_buf[7]); + DEBUG_SENSOR_LOG(PSTR(" type:%02x: %02x %02x %02x %02x %02x %02x %02x %02x"),_beacon.type, (uint8_t)_buf[8],(uint8_t)_buf[9],(uint8_t)_buf[10],(uint8_t)_buf[11],(uint8_t)_buf[12],(uint8_t)_buf[13],(uint8_t)_buf[14],(uint8_t)_buf[15]); + + if(MIBLEsensors.at(_slot).type==4 || MIBLEsensors.at(_slot).type==6){ + DEBUG_SENSOR_LOG(PSTR("LYWSD03 and CGD1 no support for MiBeacon, type %u"),MIBLEsensors.at(_slot).type); + return; + } + DEBUG_SENSOR_LOG(PSTR("%s at slot %u"), kHM10SlaveType[MIBLEsensors.at(_slot).type-1],_slot); + switch(_beacon.type){ + case 0x04: + _tempFloat=(float)(_beacon.temp)/10.0f; + if(_tempFloat<60){ + MIBLEsensors.at(_slot).temp=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 4: temp updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 4: U16: %u Temp"), _beacon.temp ); + break; + case 0x06: + _tempFloat=(float)(_beacon.hum)/10.0f; + if(_tempFloat<101){ + MIBLEsensors.at(_slot).hum=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 6: hum updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 6: U16: %u Hum"), _beacon.hum); + break; + case 0x07: + MIBLEsensors.at(_slot).lux=_beacon.lux & 0x00ffffff; + DEBUG_SENSOR_LOG(PSTR("Mode 7: U24: %u Lux"), _beacon.lux & 0x00ffffff); + break; + case 0x08: + _tempFloat =(float)_beacon.moist; + if(_tempFloat<100){ + MIBLEsensors.at(_slot).moisture=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 8: moisture updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 8: U8: %u Moisture"), _beacon.moist); + break; + case 0x09: + _tempFloat=(float)(_beacon.fert); + if(_tempFloat<65535){ // ??? + MIBLEsensors.at(_slot).fertility=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 9: fertility updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 9: U16: %u Fertility"), _beacon.fert); + break; + case 0x0a: + if(_beacon.bat<101){ + MIBLEsensors.at(_slot).bat = _beacon.bat; + DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode a: U8: %u %%"), _beacon.bat); + break; + case 0x0d: + _tempFloat=(float)(_beacon.HT.temp)/10.0f; + if(_tempFloat<60){ + MIBLEsensors.at(_slot).temp = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode d: temp updated")); + } + _tempFloat=(float)(_beacon.HT.hum)/10.0f; + if(_tempFloat<100){ + MIBLEsensors.at(_slot).hum = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode d: hum updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode d: U16: %x Temp U16: %x Hum"), _beacon.HT.temp, _beacon.HT.hum); + break; + } +} + +void HM10ParseResponse(char *buf, uint16_t bufsize) { if (!strncmp(buf,"OK",2)) { - DEBUG_SENSOR_LOG(PSTR("HM10: got OK")); + DEBUG_SENSOR_LOG(PSTR("%s: got OK"),D_CMND_HM10); } if (!strncmp(buf,"HMSoft",6)) { //8 const char* _fw = "000"; memcpy((void *)_fw,(void *)(buf+8),3); HM10.firmware = atoi(_fw); - DEBUG_SENSOR_LOG(PSTR("HM10: Firmware: %d"), HM10.firmware); + DEBUG_SENSOR_LOG(PSTR("%s: Firmware: %d"),D_CMND_HM10, HM10.firmware); return; } - char * _pos = strstr(buf, "IS0:"); + char * _pos = strstr(buf, "ISA:"); if(_pos) { - const char* _mac = "000000000000"; - memcpy((void *)_mac,(void *)(_pos+4),12); - DEBUG_SENSOR_LOG(PSTR("HM10: found Mac: %s"), _mac); uint8_t _newMacArray[6] = {0}; - HM10MACStringToBytes(_mac, _newMacArray); - DEBUG_SENSOR_LOG(PSTR("HM10: MAC-array: %x%x%x%x%x%x"),_newMacArray[0],_newMacArray[1],_newMacArray[2],_newMacArray[3],_newMacArray[4],_newMacArray[5]); - MIBLEgetSensorSlot(_newMacArray, 0xff); + memcpy((void *)_newMacArray,(void *)(_pos+4),6); + HM10_ReverseMAC(_newMacArray); + DEBUG_SENSOR_LOG(PSTR("%s: MAC-array: %02x%02x%02x%02x%02x%02x"),D_CMND_HM10,_newMacArray[0],_newMacArray[1],_newMacArray[2],_newMacArray[3],_newMacArray[4],_newMacArray[5]); + uint16_t _type=0xffff; + + for (uint32_t idx =10;idxavailable()) { // delay(0); if(iwrite("AT+ROLE1"); break; case TASK_HM10_IMME1: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s set imme to 1"),D_CMND_HM10); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: set imme to 1"),D_CMND_HM10); HM10.current_task_delay = 5; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; HM10Serial->write("AT+IMME1"); break; case TASK_HM10_DISC: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s start discovery"),D_CMND_HM10); - HM10.current_task_delay = 35; // set task delay + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: start discovery"),D_CMND_HM10); + HM10.current_task_delay = 100; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; - HM10Serial->write("AT+DISC?"); + HM10.mode.awaiting = discScan; + HM10Serial->write("AT+DISA?"); break; case TASK_HM10_VERSION: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read version"),D_CMND_HM10); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read version"),D_CMND_HM10); HM10.current_task_delay = 5; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; HM10Serial->write("AT+VERR?"); break; case TASK_HM10_NAME: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read name"),D_CMND_HM10); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read name"),D_CMND_HM10); HM10.current_task_delay = 5; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; HM10Serial->write("AT+NAME?"); break; case TASK_HM10_CONN: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s connect"),D_CMND_HM10); + char _con[20]; + sprintf_P(_con,"AT+CON%02x%02x%02x%02x%02x%02x",MIBLEsensors.at(HM10.state.sensor).serial[0],MIBLEsensors.at(HM10.state.sensor).serial[1],MIBLEsensors.at(HM10.state.sensor).serial[2],MIBLEsensors.at(HM10.state.sensor).serial[3],MIBLEsensors.at(HM10.state.sensor).serial[4],MIBLEsensors.at(HM10.state.sensor).serial[5]); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: connect %s"),D_CMND_HM10, _con); HM10.current_task_delay = 2; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; - char _con[20]; - sprintf_P(_con,"AT+CON%02x%02x%02x%02x%02x%02x",MIBLEsensors.at(HM10.state.sensor).serial[0],MIBLEsensors.at(HM10.state.sensor).serial[1],MIBLEsensors.at(HM10.state.sensor).serial[2],MIBLEsensors.at(HM10.state.sensor).serial[3],MIBLEsensors.at(HM10.state.sensor).serial[4],MIBLEsensors.at(HM10.state.sensor).serial[5]); HM10Serial->write(_con); - HM10.mode.awaitingB = false; + HM10.mode.awaiting = none; HM10.mode.connected = true; break; case TASK_HM10_DISCONN: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s disconnect"),D_CMND_HM10); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: disconnect"),D_CMND_HM10); HM10.current_task_delay = 5; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; HM10Serial->write("AT"); break; case TASK_HM10_RESET: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s Reset Device"),D_CMND_HM10); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: Reset Device"),D_CMND_HM10); HM10Serial->write("AT+RESET"); HM10.current_task_delay = 5; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; break; case TASK_HM10_SUB_L3: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s subscribe"),D_CMND_HM10); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: subscribe"),D_CMND_HM10); HM10.current_task_delay = 25; // set task delay - HM10_TaskReplaceInSlot(TASK_HM10_DELAY_SUB,i); + HM10_TaskReplaceInSlot(TASK_HM10_DELAY_SUB_LY,i); runningTaskLoop = false; HM10Serial->write("AT+NOTIFY_ON0037"); break; case TASK_HM10_UN_L3: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s un-subscribe"),D_CMND_HM10); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: un-subscribe"),D_CMND_HM10); HM10.current_task_delay = 5; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; - HM10.mode.awaitingHT = false; + HM10.mode.awaiting = none; HM10Serial->write("AT+NOTIFYOFF0037"); break; case TASK_HM10_SUB_L2: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s subscribe"),D_CMND_HM10); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: subscribe"),D_CMND_HM10); HM10.current_task_delay = 25; // set task delay - HM10_TaskReplaceInSlot(TASK_HM10_DELAY_SUB,i); + HM10_TaskReplaceInSlot(TASK_HM10_DELAY_SUB_LY,i); runningTaskLoop = false; HM10Serial->write("AT+NOTIFY_ON003C"); break; case TASK_HM10_UN_L2: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s un-subscribe"),D_CMND_HM10); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: un-subscribe"),D_CMND_HM10); HM10.current_task_delay = 5; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; - HM10.mode.awaitingHT = false; + HM10.mode.awaiting = none; HM10Serial->write("AT+NOTIFYOFF003C"); break; case TASK_HM10_TIME_L2: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s set time"),D_CMND_HM10); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: set time"),D_CMND_HM10); HM10.current_task_delay = 5; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; @@ -550,49 +795,114 @@ void HM10_TaskEvery100ms(){ AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s Time-string: %x%x%x%x%x"),D_CMND_HM10, HM10.timebuf[0],HM10.timebuf[1],HM10.timebuf[2],HM10.timebuf[3],(Rtc.time_timezone /60)); break; case TASK_HM10_READ_HT: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read handle 0036"),D_CMND_HM10); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0036"),D_CMND_HM10); HM10.current_task_delay = 0; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; HM10Serial->write("AT+READDATA0036?"); - HM10.mode.awaitingHT = true; + HM10.mode.awaiting = tempHumLY; break; case TASK_HM10_READ_BT_L3: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read handle 003A"),D_CMND_HM10); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 003A"),D_CMND_HM10); HM10.current_task_delay = 2; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; HM10Serial->write("AT+READDATA003A?"); - HM10.mode.awaitingB = true; + HM10.mode.awaiting = bat; break; case TASK_HM10_READ_BT_L2: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read handle 0043"),D_CMND_HM10); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0043"),D_CMND_HM10); HM10.current_task_delay = 2; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; HM10Serial->write("AT+READDATA0043?"); - HM10.mode.awaitingB = true; + HM10.mode.awaiting = bat; + break; + case TASK_HM10_READ_BF_FL: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0038"),D_CMND_HM10); + HM10.current_task_delay = 2; // set task delay + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+READDATA0038?"); + HM10.mode.awaiting = bat; + break; + case TASK_HM10_CALL_TLMF_FL: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: write to handle 0033"),D_CMND_HM10); + HM10.current_task_delay = 5; // set task delay + HM10_TaskReplaceInSlot(TASK_HM10_READ_TLMF_FL,i); + runningTaskLoop = false; + HM10Serial->write("AT+SEND_DATAWR0033"); + HM10Serial->write(0xa0); + HM10Serial->write(0x1f); + HM10.mode.awaiting = none; + break; + case TASK_HM10_READ_TLMF_FL: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0035"),D_CMND_HM10); + HM10.current_task_delay = 2; // set task delay + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+READDATA0035?"); + HM10.mode.awaiting = TLMF; + break; + case TASK_HM10_READ_B_CGD1: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0011"),D_CMND_HM10); + HM10.current_task_delay = 2; // set task delay + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+READDATA0011?"); + HM10.mode.awaiting = bat; + break; + case TASK_HM10_SUB_HT_CGD1: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: subscribe 4b"),D_CMND_HM10); + HM10.current_task_delay = 5; // set task delay + HM10_TaskReplaceInSlot(TASK_HM10_DELAY_SUB_CGD1,i); + runningTaskLoop = false; + HM10.mode.awaiting = none; + HM10Serial->write("AT+NOTIFY_ON004b"); + break; + case TASK_HM10_UN_HT_CGD1: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: un-subscribe 4b"),D_CMND_HM10); + HM10.current_task_delay = 5; // set task delay + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10.mode.awaiting = none; + HM10Serial->write("AT+NOTIFYOFF004b"); + break; + case TASK_HM10_SCAN9: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: scan time to 9"),D_CMND_HM10); + HM10.current_task_delay = 2; // set task delay + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+SCAN9"); break; - // case TASK_HM10_FINDALLCHARS: - // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s find all chars"),D_CMND_HM10); - // HM10.current_task_delay = 5; // set task delay - // HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - // runningTaskLoop = false; - // HM10Serial->write("AT+FINDALLCHARS?"); - // break; case TASK_HM10_FEEDBACK: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s get response"),D_CMND_HM10); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: get response"),D_CMND_HM10); HM10SerialHandleFeedback(); HM10.current_task_delay = HM10_TASK_LIST[i+1][1];; // set task delay HM10_TASK_LIST[i][0] = TASK_HM10_DONE; // no feedback for reset runningTaskLoop = false; break; - case TASK_HM10_DELAY_SUB: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s start reading"),D_CMND_HM10); + case TASK_HM10_DELAY_SUB_LY: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: start reading"),D_CMND_HM10); HM10SerialHandleFeedback(); HM10.current_task_delay = HM10_TASK_LIST[i+1][1];; // set task delay HM10_TASK_LIST[i][0] = TASK_HM10_DONE; // no feedback for reset - HM10.mode.awaitingHT = true; + HM10.mode.awaiting = tempHumLY; + runningTaskLoop = false; + break; + case TASK_HM10_DELAY_SUB_CGD1: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: start reading"),D_CMND_HM10); + HM10SerialHandleFeedback(); + HM10.current_task_delay = HM10_TASK_LIST[i+1][1];; // set task delay + HM10_TASK_LIST[i][0] = TASK_HM10_DONE; // no feedback for reset + HM10.mode.awaiting = tempHumCGD1; + runningTaskLoop = false; + break; + case TASK_HM10_STATUS_EVENT: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: show status"),D_CMND_HM10); + HM10StatusInfo(); + HM10.current_task_delay = HM10_TASK_LIST[i+1][1];; // set task delay + HM10_TASK_LIST[i][0] = TASK_HM10_DONE; // no feedback for reset runningTaskLoop = false; break; case TASK_HM10_DONE: // this entry was already handled @@ -619,39 +929,74 @@ void HM10_TaskEvery100ms(){ } } +void HM10StatusInfo(){ + char stemp[20]; + snprintf_P(stemp, sizeof(stemp),PSTR("{%s:{\"found\": %u}}"),D_CMND_HM10, MIBLEsensors.size()); + AddLog_P2(LOG_LEVEL_INFO, stemp); + RulesProcessEvent(stemp); +} + /** * @brief Main loop of the driver, "high level"-loop * */ void HM10EverySecond(){ - if(HM10.firmware == 0) return; - if(HM10.mode.pending_task == 1) return; - if (MIBLEsensors.size()==0) return; - static uint32_t _counter = 0; static uint32_t _nextSensorSlot = 0; + static uint32_t _lastDiscovery = 0; + + if(HM10.firmware == 0) return; + if(HM10.mode.pending_task == 1) return; + if(MIBLEsensors.size()==0 && !HM10.mode.autoScan) return; + + if((HM10.period-_counter)>15 && HM10.mode.autoScan) { + if(_counter-_lastDiscovery>HM10.autoScanInterval){ + HM10_Discovery_Scan(); + HM10.mode.pending_task = 1; + _counter+=12; + _lastDiscovery = _counter; + return; + } + } + if(_counter==0) { HM10.state.sensor = _nextSensorSlot; _nextSensorSlot++; - if(MIBLEsensors.at(HM10.state.sensor).type==LYWSD03MMC) { - HM10.mode.pending_task = 1; - HM10_Read_LYWSD03(); - } - if(MIBLEsensors.at(HM10.state.sensor).type==LYWSD02) { - HM10.mode.pending_task = 1; - HM10_Read_LYWSD02(); + HM10.mode.pending_task = 1; + switch(MIBLEsensors.at(HM10.state.sensor).type){ + case LYWSD03MMC: + HM10_Read_LYWSD03(); + break; + case LYWSD02: + HM10_Read_LYWSD02(); + break; + case FLORA: + HM10_Read_Flora(); + break; + case CGD1: + HM10_Read_CGD1(); + break; + default: + HM10.mode.pending_task = 0; } if (HM10.state.sensor==MIBLEsensors.size()-1) { _nextSensorSlot= 0; _counter++; } - DEBUG_SENSOR_LOG(PSTR("%s active sensor now: %u"),D_CMND_HM10, HM10.state.sensor); + DEBUG_SENSOR_LOG(PSTR("%s: active sensor now: %u"),D_CMND_HM10, HM10.state.sensor); } else _counter++; - if (_counter>HM10.period) _counter = 0; + if (_counter>HM10.period) { + _counter = 0; + _lastDiscovery = 0; + } } +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + bool HM10Cmd(void) { char command[CMDSZ]; bool serviced = true; @@ -662,22 +1007,36 @@ bool HM10Cmd(void) { switch (command_code) { case CMND_HM10_PERIOD: if (XdrvMailbox.data_len > 0) { - if (command_code == CMND_HM10_PERIOD) { HM10.period = XdrvMailbox.payload; } + HM10.period = XdrvMailbox.payload; } else { - if (command_code == CMND_HM10_PERIOD) XdrvMailbox.payload = HM10.period; + XdrvMailbox.payload = HM10.period; + } + Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_HM10_AUTO: + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload>0) { + HM10.mode.autoScan = 1; + HM10.autoScanInterval = XdrvMailbox.payload; + } + else { + HM10.mode.autoScan = 0; + HM10.autoScanInterval = 0; + } + } + else { + XdrvMailbox.payload = HM10.autoScanInterval; } Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); break; case CMND_HM10_BAUD: if (XdrvMailbox.data_len > 0) { - if (command_code == CMND_HM10_BAUD) { HM10.serialSpeed = XdrvMailbox.payload; HM10Serial->begin(HM10.serialSpeed); - } } else { - if (command_code == CMND_HM10_BAUD) XdrvMailbox.payload = HM10.serialSpeed; + XdrvMailbox.payload = HM10.serialSpeed; } Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); break; @@ -702,13 +1061,13 @@ bool HM10Cmd(void) { else Response_P(S_JSON_HM10_COMMAND, ":AT",XdrvMailbox.data); break; case CMND_HM10_DISC_SCAN: - if (command_code == CMND_HM10_DISC_SCAN) { HM10_Discovery_Scan(); } + HM10_Discovery_Scan(); Response_P(S_JSON_HM10_COMMAND, command, ""); break; default: - // else for Unknown command - serviced = false; - break; + // else for Unknown command + serviced = false; + break; } } else { return false; @@ -733,21 +1092,25 @@ void HM10Show(bool json) for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { char slave[33]; sprintf_P(slave,"%s-%02x%02x%02x",kHM10SlaveType[MIBLEsensors.at(i).type-1],MIBLEsensors.at(i).serial[3],MIBLEsensors.at(i).serial[4],MIBLEsensors.at(i).serial[5]); - char temperature[33]; // all sensors have temperature + char temperature[FLOATSZ]; // all sensors have temperature dtostrfd(MIBLEsensors.at(i).temp, Settings.flag2.temperature_resolution, temperature); ResponseAppend_P(PSTR(",\"%s\":{"),slave); if(MIBLEsensors.at(i).temp!=-1000.0f){ // this is the error code -> no temperature ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), temperature); } + else { + ResponseAppend_P(PSTR("}")); + continue; + } if (MIBLEsensors.at(i).type==FLORA){ - char lux[33]; - char moisture[33]; - char fertility[33]; + char lux[FLOATSZ]; + char moisture[FLOATSZ]; + char fertility[FLOATSZ]; dtostrfd((float)MIBLEsensors.at(i).lux, 0, lux); dtostrfd(MIBLEsensors.at(i).moisture, 0, moisture); dtostrfd(MIBLEsensors.at(i).fertility, 0, fertility); - if(MIBLEsensors.at(i).lux!=0xffff){ // this is the error code -> no temperature + if(MIBLEsensors.at(i).lux!=0x0ffffff){ // this is the error code -> no temperature ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%s"), lux); } if(MIBLEsensors.at(i).moisture!=-1000.0f){ // this is the error code -> no moisture @@ -758,15 +1121,20 @@ void HM10Show(bool json) } } if (MIBLEsensors.at(i).type>FLORA){ - char humidity[33]; + char humidity[FLOATSZ]; dtostrfd(MIBLEsensors.at(i).hum, Settings.flag2.humidity_resolution, humidity); if(MIBLEsensors.at(i).hum!=-1.0f){ // this is the error code -> no humidity ResponseAppend_P(PSTR(",\"" D_JSON_HUMIDITY "\":%s"), humidity); } - if(MIBLEsensors.at(i).bat!=0xff){ // this is the error code -> no battery - ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors.at(i).bat); + if(MIBLEsensors.at(i).hum!=-1.0f && MIBLEsensors.at(i).temp!=-1000.0f){ // this is the error code -> no humidity nor temp + char dewpoint[FLOATSZ]; + dtostrfd(CalcTempHumToDew(MIBLEsensors.at(i).temp, MIBLEsensors.at(i).hum), Settings.flag2.temperature_resolution, dewpoint); + ResponseAppend_P(PSTR(",\"" D_JSON_DEWPOINT "\":%s"), dewpoint); } } + if(MIBLEsensors.at(i).bat!=0x00){ // this is the error code -> no battery + ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors.at(i).bat); + } ResponseAppend_P(PSTR("}")); } #ifdef USE_WEBSERVER @@ -776,32 +1144,37 @@ void HM10Show(bool json) WSContentSend_PD(HTTP_HM10_HL); WSContentSend_PD(HTTP_HM10_SERIAL, kHM10SlaveType[MIBLEsensors.at(i).type-1], D_MAC_ADDRESS, MIBLEsensors.at(i).serial[0], MIBLEsensors.at(i).serial[1],MIBLEsensors.at(i).serial[2],MIBLEsensors.at(i).serial[3],MIBLEsensors.at(i).serial[4],MIBLEsensors.at(i).serial[5]); if(MIBLEsensors.at(i).temp!=-1000.0f){ - char temperature[33]; + char temperature[FLOATSZ]; dtostrfd(MIBLEsensors.at(i).temp, Settings.flag2.temperature_resolution, temperature); WSContentSend_PD(HTTP_SNS_TEMP, kHM10SlaveType[MIBLEsensors.at(i).type-1], temperature, TempUnit()); } if (MIBLEsensors.at(i).type==FLORA){ - if(MIBLEsensors.at(i).lux!=0xffff){ // this is the error code -> no valid value + if(MIBLEsensors.at(i).lux!=0x00ffffff){ // this is the error code -> no valid value WSContentSend_PD(HTTP_SNS_ILLUMINANCE, kHM10SlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).lux); } if(MIBLEsensors.at(i).moisture!=-1000.0f){ // this is the error code -> no valid value WSContentSend_PD(HTTP_SNS_MOISTURE, kHM10SlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).moisture); } if(MIBLEsensors.at(i).fertility!=-1000.0f){ // this is the error code -> no valid value - char fertility[33]; + char fertility[FLOATSZ]; dtostrfd(MIBLEsensors.at(i).fertility, 0, fertility); WSContentSend_PD(HTTP_HM10_FLORA_DATA, kHM10SlaveType[MIBLEsensors.at(i).type-1], fertility); } } if (MIBLEsensors.at(i).type>FLORA){ // everything "above" Flora if(MIBLEsensors.at(i).hum!=-1.0f){ // this is the error code -> no humidity - char humidity[33]; + char humidity[FLOATSZ]; dtostrfd(MIBLEsensors.at(i).hum, Settings.flag2.humidity_resolution, humidity); WSContentSend_PD(HTTP_SNS_HUM, kHM10SlaveType[MIBLEsensors.at(i).type-1], humidity); } - if(MIBLEsensors.at(i).bat!=0xff){ - WSContentSend_PD(HTTP_BATTERY, kHM10SlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).bat); + if(MIBLEsensors.at(i).hum!=-1.0f && MIBLEsensors.at(i).temp!=-1000.0f){ // this is the error code -> no humidity nor temp + char dewpoint[FLOATSZ]; + dtostrfd(CalcTempHumToDew(MIBLEsensors.at(i).temp, MIBLEsensors.at(i).hum), Settings.flag2.temperature_resolution, dewpoint); + WSContentSend_PD(HTTP_SNS_DEW, kHM10SlaveType[MIBLEsensors.at(i).type-1], dewpoint, TempUnit()); } + } + if(MIBLEsensors.at(i).bat!=0x00){ + WSContentSend_PD(HTTP_BATTERY, kHM10SlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).bat); } } #endif // USE_WEBSERVER @@ -847,4 +1220,4 @@ bool Xsns62(uint8_t function) } return result; } -#endif //USE_HM10 \ No newline at end of file +#endif //USE_HM10 From 211520b1c8a8211879221541934644af71751713 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 21 Mar 2020 11:22:22 +0100 Subject: [PATCH 002/547] Bump version 8.2.0 --- README.md | 2 +- RELEASENOTES.md | 2 +- tasmota/CHANGELOG.md | 8 ++++++-- tasmota/tasmota_version.h | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2a32990ff..53f977ae2 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ In addition to the [release webpage](https://github.com/arendst/Tasmota/releases ## Development -[![Dev Version](https://img.shields.io/badge/development%20version-v8.1.x.x-blue.svg)](https://github.com/arendst/Tasmota) +[![Dev Version](https://img.shields.io/badge/development%20version-v8.2.x.x-blue.svg)](https://github.com/arendst/Tasmota) [![Download Dev](https://img.shields.io/badge/download-development-yellow.svg)](http://thehackbox.org/tasmota/) [![Build Status](https://img.shields.io/travis/arendst/Tasmota.svg)](https://travis-ci.org/arendst/Tasmota) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 05f63f01e..811941f43 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -52,7 +52,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c ## Changelog -### Version 8.1.0.11 +### Version 8.2.0 Elliot - Change default my_user_config.h driver and sensor support removing most sensors and adding most drivers to tasmota.bin - Change DHT driver (#7468, #7717) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 1c11c313a..02c344b58 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -1,5 +1,11 @@ ## Unreleased (development) +## Released + +### 8.2.0 20200321 + +- Release + ### 8.1.0.11 20200313 - Change Zigbee simplification of devices probing, saving Flash and memory @@ -119,8 +125,6 @@ - Add support for DS1624, DS1621 Temperature sensor by Leonid Myravjev - Add Zigbee attribute decoder for Xiaomi Aqara Cube -## Released - ### 8.1.0 20191225 - Release diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h index 636018ddc..0a3b9aa5e 100644 --- a/tasmota/tasmota_version.h +++ b/tasmota/tasmota_version.h @@ -20,7 +20,7 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x0801000B; +const uint32_t VERSION = 0x08020000; // Lowest compatible version const uint32_t VERSION_COMPATIBLE = 0x07010006; From 0ec6f886893fa0ce133b1aa33fb9f0cfce276ec7 Mon Sep 17 00:00:00 2001 From: blakadder Date: Sat, 21 Mar 2020 14:57:30 +0100 Subject: [PATCH 003/547] Update MODULES.md module 54 changed to correct version, some minor edits --- MODULES.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MODULES.md b/MODULES.md index b47769998..e16dec401 100644 --- a/MODULES.md +++ b/MODULES.md @@ -23,7 +23,7 @@ Module | Description 17 WiOn | WiOn Wifi Smart Socket 18 Generic | Any ESP8266/ESP8285 device like WeMos and NodeMCU 19 Sonoff Dev | Sonoff Dev Wifi Development Board -20 H801 | H801 Wifi RGBWW Led Controller +20 H801 | H801 Wifi 5 Channel LED Controller 21 Sonoff SC | Sonoff SC Wifi Environmental Monitor 22 Sonoff BN-SZ | Sonoff BN-SZ01 Wifi Ceiling Led (Retired) 23 Sonoff 4CH Pro | Sonoff 4CH Pro 4-gang Wifi Smart Switch @@ -57,7 +57,7 @@ Module | Description 51 OBI Socket | OBI Wifi Smart Socket 52 Teckin | Teckin SP22 Wifi Smart Switch with Energy Monitoring 53 AplicWDP303075 | Aplic WDP 303075 CSL Wifi Smart Switch with Energy Monitoring -54 Tuya Dimmer | MIUO (and other Tuya based) Wifi Dimmer for Incandescent Lights and Led +54 TuyaMCU | Devices with an MCU using Tuya communication protocol for control 55 Gosund SP1 v23 | Gosund SP1 v2.3 Wifi Smart Switch with Energy Monitoring 56 ARMTR Dimmer | ARMtronix Wifi dimmer for Incandescent Lights and Led 57 SK03 Outdoor | SK03 Outdoor Wifi Smart Switch with Energy Monitoring @@ -65,7 +65,7 @@ Module | Description 59 Teckin US | Teckin SP20 and ZooZee SA102 Wifi Smart Switch with Energy Monitoring 60 Manzoku strip | Manzoku Wifi Smart Power Strip with four Relays 61 OBI Socket 2 | OBI 2 Wifi Smart Socket -62 YTF IR Bridge | YTF Infra Red Wifi Bridge +62 YTF IR Bridge | YTF Universal IR Bridge 63 Digoo DG-SP202 | Digoo DG-SP202 Dual Wifi Smart Switch with Energy Monitoring 64 KA10 | Smanergy KA10 Wifi Smart Wall Switch with Energy Monitoring 65 Luminea ZX2820 | Luminea ZX2820 Wifi Smart Switch with Energy Monitoring From b6fc31ebb0d3dbf835d5aeb9bb5f1309f5727cab Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 21 Mar 2020 15:46:28 +0100 Subject: [PATCH 004/547] Bump version 8.2.0.1 - Bump version 8.2.0.1 - Change HM-10 sensor type detection and add features (#7962) --- RELEASENOTES.md | 74 ++------------------------------------- tasmota/CHANGELOG.md | 4 +++ tasmota/tasmota_version.h | 2 +- 3 files changed, 7 insertions(+), 73 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 811941f43..eb9da8a74 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -52,76 +52,6 @@ The following binary downloads have been compiled with ESP8266/Arduino library c ## Changelog -### Version 8.2.0 Elliot +### Version 8.2.0.1 -- Change default my_user_config.h driver and sensor support removing most sensors and adding most drivers to tasmota.bin -- Change DHT driver (#7468, #7717) -- Change Lights: simplified gamma correction and 10 bits internal computation -- Change commands ``Prefix``, ``Ssid``, ``StateText``, ``NTPServer``, and ``FriendlyName`` displaying all items -- Change Zigbee command prefix from ``Zigbee*`` to ``Zb*`` -- Change MQTT message size with additional 200 characters -- Change display of some date and time messages from "Wed Feb 19 10:45:12 2020" to "2020-02-19T10:45:12" -- Change IRremoteESP8266 library updated to v2.7.4 -- Fix ``PowerDelta`` zero power detection (#7515) -- Fix ``White`` added to light status (#7142) -- Fix ``WakeUp `` ignores provided value (#7473) -- Fix ``RGBWWTable`` ignored (#7572) -- Fix commands ``Display`` and ``Counter`` from overruling command processing (#7322) -- Fix Sonoff Bridge, Sc, L1, iFan03 and CSE7766 serial interface to forced speed, config and disable logging -- Fix Improved fade linearity with gamma correction -- Fix PWM flickering at low levels (#7415) -- Fix LCD line and column positioning (#7387) -- Fix Display handling of hexadecimal escape characters (#7387) -- Fix exception 9 restart on log message in Ticker interrupt service routines NTP, Wemos and Hue emulation (#7496) -- Fix Hass sensor discovery by Federico Leoni (#7582, #7548) -- Fix MaxPower functionality (#7647) -- Fix relation between Wifi RSSI and signal strength -- Add command ``SetOption79 0/1`` to enable reset of counters at teleperiod time by Andre Thomas (#7355) -- Add command ``SetOption82 0/1`` to limit the CT range for Alexa to 200..380 -- Add command ``SetOption84 0/1`` to send AWS IoT device shadow updates (alternative to retained) -- Add commands ``SetOption85 0/1`` and ``DevGroupShare`` supporting UDP Group command using ``GroupTopic`` without MQTT by Paul Diem (#7790) -- Add command ``SetOption86 0/1`` for PWM dimmer to turn brightness LED's off 5 seconds after last change -- Add command ``SetOption87 0/1`` for PWM dimmer to turn red LED on when powered off -- Add command ``SetOption88 0/1`` for PWM dimmer to let buttons control remote devices -- Add command ``SetOption89 0/1`` for Zigbee distinct MQTT topics per device for SENSOR, allowing retained messages (#7835) -- Add command ``ShutterButton `` to control shutter(s) by to-scho (#7403) -- Add commands ``SwitchMode 8`` ToggleMulti, ``SwitchMode 9`` FollowMulti and ``SwitchMode 10`` FollowMultiInverted (#7522) -- Add commands ``SwitchMode 11`` PushHoldMulti and ``SwitchMode 12`` PushHoldInverted (#7603) -- Add commands ``SwitchMode 13`` PushOn and ``SwitchMode 14`` PushOnInverted (#7912) -- Add command ``Buzzer -1`` for infinite mode and command ``Buzzer -2`` for following led mode (#7623) -- Add command ``HumOffset -10.0 .. 10.0`` to set global humidity sensor offset (#7934) -- Add support for ``AdcParam`` parameters to control ADC0 Current Transformer Apparent Power formula by Jodi Dillon (#7100) -- Add optional parameter ```` to command ``Scheme , `` to control initial start color -- Add web page sliders when ``SetOption37 128`` is active allowing control of white(s) -- Add SerialConfig to ``Status 1`` -- Add BootCount Reset Time as BCResetTime to ``Status 1`` -- Add WifiPower to ``Status 5`` -- Add most SetOptions as defines to my_user_config.h -- Add optional Wifi AccessPoint passphrase define WIFI_AP_PASSPHRASE in my_user_config.h (#7690) -- Add SoftwareSerial to CSE7766 driver allowing different GPIOs (#7563) -- Add rule trigger on one level deeper using syntax with two ``#`` like ``on zbreceived#vibration_sensor#aqaracubeside=0 do ...`` -- Add Zigbee attribute decoder for Xiaomi Aqara Cube -- Add ``ZbZNPReceived``and ``ZbZCLReceived`` being published to MQTT when ``SetOption66 1`` -- Add Zigbee enhanced commands decoding, added ``ZbPing`` -- Add Zigbee features and improvements -- Add Zigbee support for Hue emulation by Stefan Hadinger -- Add HAss Discovery support for Button and Switch triggers by Federico Leoni (#7901) -- Add Dew Point to Temperature and Humidity sensors -- Add optional support for Prometheus using file xsns_91_prometheus.ino (#7216) -- Add support for gzipped binaries -- Add support for Romanian language translations by Augustin Marti -- Add support for sensors DS18x20 and DHT family on Shelly 1 and Shelly 1PM using Shelly Add-On adapter (#7469) -- Add support to BMP driver to enter reset state (sleep enable) when deep sleep is used in Tasmota -- Add support for DS1624, DS1621 Temperature sensor by Leonid Myravjev -- Add support for NRF24L01 as BLE-bridge for Mijia Bluetooth sensors by Christian Baars (#7394) -- Add support for MI-BLE sensors using HM-10 Bluetooth 4.0 module by Christian Staars (#7683) -- Add support for FiF LE-01MR energy meter by saper-2 (#7584) -- Add support for Sensors AHT10 and AHT15 by Martin Wagner (#7596) -- Add support for Wemos Motor Shield V1 by Denis Sborets (#7764) -- Add support for La Crosse TX23 Anemometer by Norbert Richter (#3146, #7765) -- Add support for Martin Jerry/acenx/Tessan/NTONPOWER SD0x PWM dimmer switches by Paul Diem (#7791) -- Add support for UDP Group control without MQTT by Paul Diem (#7790) -- Add support for Jarolift rollers by Keeloq algorithm -- Add support for MaxBotix HRXL-MaxSonar ultrasonic range finders by Jon Little (#7814) -- Add support for HDC1080 Temperature and Humidity sensor by Luis Teixeira (#7888) -- Add support for ElectriQ iQ-wifiMOODL RGBW light by Ian King (#7947) +- Change HM-10 sensor type detection and add features (#7962) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 02c344b58..66f27a53e 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -1,5 +1,9 @@ ## Unreleased (development) +### 8.2.0.1 20200321 + +- Change HM-10 sensor type detection and add features (#7962) + ## Released ### 8.2.0 20200321 diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h index 0a3b9aa5e..3bc994c67 100644 --- a/tasmota/tasmota_version.h +++ b/tasmota/tasmota_version.h @@ -20,7 +20,7 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x08020000; +const uint32_t VERSION = 0x08020001; // Lowest compatible version const uint32_t VERSION_COMPATIBLE = 0x07010006; From 5c0d78ac425b1fcac4a13359c14d33c7eba00e19 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Sat, 21 Mar 2020 15:57:59 +0100 Subject: [PATCH 005/547] Generate gzip binaries --- platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/platformio.ini b/platformio.ini index 6b9459388..9e0c28e9a 100755 --- a/platformio.ini +++ b/platformio.ini @@ -85,6 +85,7 @@ extra_scripts = ${scripts_defaults.extra_scripts} [scripts_defaults] extra_scripts = pio/strip-floats.py pio/name-firmware.py + pio/gzip-firmware.py [esp82xx_defaults] build_flags = -D NDEBUG From 2c8dd288e7e0cad622749b3999bd9ec4a906fc09 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sun, 22 Mar 2020 16:11:01 +0100 Subject: [PATCH 006/547] Add command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` --- tasmota/CHANGELOG.md | 1 + tasmota/i18n.h | 1 + tasmota/xdrv_23_zigbee_1_headers.ino | 36 +++++++++++ tasmota/xdrv_23_zigbee_2_devices.ino | 95 +++++++++++++++++++++++++++- tasmota/xdrv_23_zigbee_9_impl.ino | 58 ++++++++++++++++- 5 files changed, 186 insertions(+), 5 deletions(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 66f27a53e..9851355a0 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -3,6 +3,7 @@ ### 8.2.0.1 20200321 - Change HM-10 sensor type detection and add features (#7962) +- Add command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` ## Released diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 12f3164c6..47afc7307 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -514,6 +514,7 @@ #define D_JSON_ZIGBEE_STATUS_MSG "StatusMessage" #define D_CMND_ZIGBEE_LIGHT "Light" #define D_JSON_ZIGBEE_LIGHT "Light" +#define D_CMND_ZIGBEE_RESTORE "Restore" // Commands xdrv_25_A4988_Stepper.ino #define D_CMND_MOTOR "MOTOR" diff --git a/tasmota/xdrv_23_zigbee_1_headers.ino b/tasmota/xdrv_23_zigbee_1_headers.ino index 50e028dc1..051858697 100644 --- a/tasmota/xdrv_23_zigbee_1_headers.ino +++ b/tasmota/xdrv_23_zigbee_1_headers.ino @@ -43,6 +43,42 @@ JsonVariant &getCaseInsensitive(const JsonObject &json, const char *needle) { return *(JsonVariant*)nullptr; } +// get the result as a string (const char*) and nullptr if there is no field or the string is empty +const char * getCaseInsensitiveConstCharNull(const JsonObject &json, const char *needle) { + const JsonVariant &val = getCaseInsensitive(json, needle); + if (&val) { + const char *val_cs = val.as(); + if (strlen(val_cs)) { + return val_cs; + } + } + return nullptr; +} + +// Get an JSON attribute, with case insensitive key search starting with *needle +JsonVariant &startsWithCaseInsensitive(const JsonObject &json, const char *needle) { + // key can be in PROGMEM + if ((nullptr == &json) || (nullptr == needle) || (0 == pgm_read_byte(needle))) { + return *(JsonVariant*)nullptr; + } + + String needle_s(needle); + needle_s.toLowerCase(); + + for (auto kv : json) { + String key_s(kv.key); + key_s.toLowerCase(); + JsonVariant &value = kv.value; + + if (key_s.startsWith(needle_s)) { + return value; + } + } + // if not found + return *(JsonVariant*)nullptr; +} + + uint32_t parseHex(const char **data, size_t max_len = 8) { uint32_t ret = 0; for (uint32_t i = 0; i < max_len; i++) { diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino index 0cbe9a507..7443b5457 100644 --- a/tasmota/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/xdrv_23_zigbee_2_devices.ino @@ -105,9 +105,7 @@ public: // Add an endpoint to a device void addEndpoint(uint16_t shortaddr, uint8_t endpoint); - - // Add cluster - void addCluster(uint16_t shortaddr, uint8_t endpoint, uint16_t cluster); + void clearEndpoints(uint16_t shortaddr); void setManufId(uint16_t shortaddr, const char * str); void setModelId(uint16_t shortaddr, const char * str); @@ -121,6 +119,7 @@ public: // Dump json String dumpLightState(uint16_t shortaddr) const; String dump(uint32_t dump_mode, uint16_t status_shortaddr = 0) const; + int32_t deviceRestore(const JsonObject &json); // Hue support void setHueBulbtype(uint16_t shortaddr, int8_t bulbtype); @@ -450,6 +449,20 @@ void Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) { } } +// +// Clear all endpoints +// +void Z_Devices::clearEndpoints(uint16_t shortaddr) { + if (!shortaddr) { return; } + Z_Device &device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } // don't crash if not found + + for (uint32_t i = 0; i < endpoints_max; i++) { + device.endpoints[i] = 0; + // no dirty here because it doesn't make sense to store it, does it? + } +} + // // Add an endpoint to a shortaddr // @@ -994,6 +1007,9 @@ String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const { if (device.modelId) { dev[F(D_JSON_MODEL D_JSON_ID)] = device.modelId; } + if (-1 != device.bulbtype) { + dev[F(D_JSON_ZIGBEE_LIGHT)] = device.bulbtype; // sign extend, 0xFF changed as -1 + } if (device.manufacturerId) { dev[F("Manufacturer")] = device.manufacturerId; } @@ -1013,4 +1029,77 @@ String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const { return payload; } +// Restore a single device configuration based on json export +// Input: json element as expported by `ZbStatus2`` +// Mandatory attribue: `Device` +// +// Returns: +// 0 : Ok +// <0 : Error +// +// Ex: {"Device":"0x5ADF","Name":"IKEA_Light","IEEEAddr":"0x90FD9FFFFE03B051","ModelId":"TRADFRI bulb E27 WS opal 980lm","Manufacturer":"IKEA of Sweden","Endpoints":["0x01","0xF2"]} +int32_t Z_Devices::deviceRestore(const JsonObject &json) { + + // params + uint16_t device = 0x0000; // 0x0000 is coordinator so considered invalid + uint64_t ieeeaddr = 0x0000000000000000LL; // 0 means unknown + const char * modelid = nullptr; + const char * manufid = nullptr; + const char * friendlyname = nullptr; + int8_t bulbtype = 0xFF; + size_t endpoints_len = 0; + + // read mandatory "Device" + const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); + if (nullptr != &val_device) { + device = strToUInt(val_device); + } else { + return -1; // missing "Device" attribute + } + + // read "IEEEAddr" 64 bits in format "0x0000000000000000" + const JsonVariant &val_ieeeaddr = getCaseInsensitive(json, PSTR("IEEEAddr")); + if (nullptr != &val_ieeeaddr) { + ieeeaddr = strtoull(val_ieeeaddr.as(), nullptr, 0); + } + + // read "Name" + friendlyname = getCaseInsensitiveConstCharNull(json, PSTR("Name")); + + // read "ModelId" + modelid = getCaseInsensitiveConstCharNull(json, PSTR("ModelId")); + + // read "Manufacturer" + manufid = getCaseInsensitiveConstCharNull(json, PSTR("Manufacturer")); + + // read "Light" + const JsonVariant &val_bulbtype = getCaseInsensitive(json, PSTR(D_JSON_ZIGBEE_LIGHT)); + if (nullptr != &val_bulbtype) { bulbtype = strToUInt(val_bulbtype);; } + + // update internal device information + updateDevice(device, ieeeaddr); + if (modelid) { setModelId(device, modelid); } + if (manufid) { setManufId(device, manufid); } + if (friendlyname) { setFriendlyName(device, friendlyname); } + if (&val_bulbtype) { setHueBulbtype(device, bulbtype); } + + // read "Endpoints" + const JsonVariant &val_endpoints = getCaseInsensitive(json, PSTR("Endpoints")); + if ((nullptr != &val_endpoints) && (val_endpoints.is())) { + const JsonArray &arr_ep = val_endpoints.as(); + endpoints_len = arr_ep.size(); + clearEndpoints(device); // clear even if array is empty + if (endpoints_len) { + for (auto ep_elt : arr_ep) { + uint8_t ep = strToUInt(ep_elt); + if (ep) { + addEndpoint(device, ep); + } + } + } + } + + return 0; +} + #endif // USE_ZIGBEE diff --git a/tasmota/xdrv_23_zigbee_9_impl.ino b/tasmota/xdrv_23_zigbee_9_impl.ino index e90782a8c..674f8d655 100644 --- a/tasmota/xdrv_23_zigbee_9_impl.ino +++ b/tasmota/xdrv_23_zigbee_9_impl.ino @@ -35,7 +35,7 @@ const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|" D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" D_CMND_ZIGBEE_BIND "|" D_CMND_ZIGBEE_PING "|" D_CMND_ZIGBEE_MODELID "|" - D_CMND_ZIGBEE_LIGHT + D_CMND_ZIGBEE_LIGHT "|" D_CMND_ZIGBEE_RESTORE ; void (* const ZigbeeCommand[])(void) PROGMEM = { @@ -44,7 +44,7 @@ void (* const ZigbeeCommand[])(void) PROGMEM = { &CmndZbProbe, &CmndZbRead, &CmndZbZNPReceive, &CmndZbForget, &CmndZbSave, &CmndZbName, &CmndZbBind, &CmndZbPing, &CmndZbModelId, - &CmndZbLight, + &CmndZbLight, CmndZbRestore, }; int32_t ZigbeeProcessInput(class SBuffer &buf) { @@ -790,6 +790,60 @@ void CmndZbSave(void) { ResponseCmndDone(); } + +// Restore a device configuration previously exported via `ZbStatus2`` +// Format: +// Either the entire `ZbStatus3` export, or an array or just the device configuration. +// If array, if can contain multiple devices +// ZbRestore {"ZbStatus3":[{"Device":"0x5ADF","Name":"Petite_Lampe","IEEEAddr":"0x90FD9FFFFE03B051","ModelId":"TRADFRI bulb E27 WS opal 980lm","Manufacturer":"IKEA of Sweden","Endpoints":["0x01","0xF2"]}]} +// ZbRestore [{"Device":"0x5ADF","Name":"Petite_Lampe","IEEEAddr":"0x90FD9FFFFE03B051","ModelId":"TRADFRI bulb E27 WS opal 980lm","Manufacturer":"IKEA of Sweden","Endpoints":["0x01","0xF2"]}] +// ZbRestore {"Device":"0x5ADF","Name":"Petite_Lampe","IEEEAddr":"0x90FD9FFFFE03B051","ModelId":"TRADFRI bulb E27 WS opal 980lm","Manufacturer":"IKEA of Sweden","Endpoints":["0x01","0xF2"]} +void CmndZbRestore(void) { + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + DynamicJsonBuffer jsonBuf; + const JsonVariant json_parsed = jsonBuf.parse((const char*)XdrvMailbox.data); // const to force a copy of parameter + const JsonVariant * json = &json_parsed; // root of restore, to be changed if needed + bool success = false; + + // check if parsing succeeded + if (json_parsed.is()) { + success = json_parsed.as().success(); + } else if (json_parsed.is()) { + success = json_parsed.as().success(); + } + if (!success) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } + + // Check is root contains `ZbStatus` key, if so change the root + const JsonVariant * zbstatus = &startsWithCaseInsensitive(*json, PSTR("ZbStatus")); + if (nullptr != zbstatus) { + json = zbstatus; + } + + // check if the root is an array + if (json->is()) { + const JsonArray& arr = json->as(); + for (auto elt : arr) { + // call restore on each item + int32_t res = zigbee_devices.deviceRestore(elt); + if (res < 0) { + ResponseCmndChar_P(PSTR("Restore failed")); + return; + } + } + } else if (json->is()) { + int32_t res = zigbee_devices.deviceRestore(*json); + if (res < 0) { + ResponseCmndChar_P(PSTR("Restore failed")); + return; + } + // call restore on a single object + } else { + ResponseCmndChar_P(PSTR("Missing parameters")); + return; + } + ResponseCmndDone(); +} + // Send an attribute read command to a device, specifying cluster and list of attributes void CmndZbRead(void) { // ZigbeeRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":5} From 752fad8b19ec487229e4b67847a1dc453ed1c165 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 22 Mar 2020 16:18:48 +0100 Subject: [PATCH 007/547] Disable reset pins from core Disable reset pins from core. Works with core below 2.5.0 and 2.6.3 + e64cb619f (or current STAGE). Should stop relay toggling at restart. --- tasmota/core_esp8266_wiring_digital.c | 2 ++ tasmota/support_legacy_cores.ino | 21 +++++++++++++++++++++ tasmota/tasmota_post.h | 1 + 3 files changed, 24 insertions(+) diff --git a/tasmota/core_esp8266_wiring_digital.c b/tasmota/core_esp8266_wiring_digital.c index f8d521748..4b9ab252b 100644 --- a/tasmota/core_esp8266_wiring_digital.c +++ b/tasmota/core_esp8266_wiring_digital.c @@ -196,6 +196,7 @@ void initPins(void) { U0IE = 0; U1IE = 0; +/* for (int i = 0; i <= 5; ++i) { pinMode(i, INPUT); } @@ -203,6 +204,7 @@ void initPins(void) { for (int i = 12; i <= 16; ++i) { pinMode(i, INPUT); } +*/ ETS_GPIO_INTR_ATTACH(interrupt_handler, &interrupt_reg); ETS_GPIO_INTR_ENABLE(); diff --git a/tasmota/support_legacy_cores.ino b/tasmota/support_legacy_cores.ino index f8c5b3060..0d429c75f 100644 --- a/tasmota/support_legacy_cores.ino +++ b/tasmota/support_legacy_cores.ino @@ -155,3 +155,24 @@ void* memmove_P(void *dest, const void *src, size_t n) } #endif // ARDUINO_ESP8266_RELEASE < 2_6_0 + + + +/*********************************************************************************************\ + * Core overrides +\*********************************************************************************************/ + +// Add below line to tasmota_post.h +// extern "C" void resetPins(); +void resetPins() +{ +/* + for (int i = 0; i <= 5; ++i) { + pinMode(i, INPUT); + } + // pins 6-11 are used for the SPI flash interface + for (int i = 12; i <= 16; ++i) { + pinMode(i, INPUT); + } +*/ +} diff --git a/tasmota/tasmota_post.h b/tasmota/tasmota_post.h index b9bc6a833..1bba02172 100644 --- a/tasmota/tasmota_post.h +++ b/tasmota/tasmota_post.h @@ -41,6 +41,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); void DomoticzTempHumPressureSensor(float temp, float hum, float baro = -1); char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween = '\0'); extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack, uint32_t stack_end); +extern "C" void resetPins(); /*********************************************************************************************\ * Default global defines From 5471de6f1c96cc82209fa9316e98d0a0c091ee00 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 22 Mar 2020 17:42:32 +0100 Subject: [PATCH 008/547] Change GPIO initialization solving possible Relay toggle on (OTA) restart Change GPIO initialization solving possible Relay toggle on (OTA) restart --- RELEASENOTES.md | 2 ++ tasmota/CHANGELOG.md | 1 + tasmota/support_tasmota.ino | 23 +++++++++++++++++++++++ tasmota/support_wifi.ino | 1 + 4 files changed, 27 insertions(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index eb9da8a74..6d46d8cda 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -55,3 +55,5 @@ The following binary downloads have been compiled with ESP8266/Arduino library c ### Version 8.2.0.1 - Change HM-10 sensor type detection and add features (#7962) +- Change GPIO initialization solving possible Relay toggle on (OTA) restart +- Add command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 9851355a0..bea282bd9 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -3,6 +3,7 @@ ### 8.2.0.1 20200321 - Change HM-10 sensor type detection and add features (#7962) +- Change GPIO initialization solving possible Relay toggle on (OTA) restart - Add command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` ## Released diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index d8ca2668c..1caaa2779 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -1297,6 +1297,18 @@ void SerialInput(void) /********************************************************************************************/ +void ResetPwm(void) +{ + for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only + if (pin[GPIO_PWM1 +i] < 99) { + analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range : 0); +// analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]); + } + } +} + +/********************************************************************************************/ + void GpioInit(void) { uint32_t mpin; @@ -1506,6 +1518,17 @@ void GpioInit(void) RotaryInit(); #endif + // Set any non-used GPIO to INPUT + for (uint32_t i = 0; i < sizeof(my_module.io); i++) { + mpin = ValidPin(i, my_module.io[i]); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("INI: gpio pin %d, mpin %d"), i, mpin); + if (((i < 6) || (i > 11)) && (0 == mpin)) { // Skip SPI flash interface + if (!((1 == i) || (3 == i))) { // Skip serial + pinMode(i, INPUT); + } + } + } + SetLedPower(Settings.ledstate &8); SetLedLink(Settings.ledstate &8); diff --git a/tasmota/support_wifi.ino b/tasmota/support_wifi.ino index f4e88a49d..efc0f9a26 100644 --- a/tasmota/support_wifi.ino +++ b/tasmota/support_wifi.ino @@ -694,6 +694,7 @@ void WifiShutdown(bool option = false) void EspRestart(void) { + ResetPwm(); WifiShutdown(true); CrashDumpClear(); // Clear the stack dump in RTC // ESP.restart(); // This results in exception 3 on restarts on core 2.3.0 From bb73f6c412c5271703acb00384f9cee38f97715a Mon Sep 17 00:00:00 2001 From: Federico Leoni Date: Sun, 22 Mar 2020 14:06:30 -0300 Subject: [PATCH 009/547] Update BUILDS.md --- BUILDS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILDS.md b/BUILDS.md index 0134d5fd7..04711b1c6 100644 --- a/BUILDS.md +++ b/BUILDS.md @@ -71,7 +71,7 @@ | | | | | | | | | | USE_ADC_VCC | x | x | - | - | - | - | - | | USE_COUNTER | - | - | x | x | x | x | x | -| USE_DS18x20 | - | - | x | x | x | - | x | +| USE_DS18x20 | - | - | x | x | x | x | x | | USE_DHT | - | - | x | x | x | x | x | | USE_MAX31855 | - | - | - | - | x | - | - | | USE_MAX31865 | - | - | - | - | - | - | - | From 55aad3dd372dc6284f9f360133bdd029a5cade51 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Sun, 22 Mar 2020 18:16:05 +0100 Subject: [PATCH 010/547] Use Arduino.git#e64cb619 for Platformio override. -> Use all latest development commit features --- platformio_override_sample.ini | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini index fd5c86fdc..18766c735 100644 --- a/platformio_override_sample.ini +++ b/platformio_override_sample.ini @@ -79,9 +79,9 @@ extra_scripts = ${scripts_defaults.extra_scripts} ;platform_packages = ${core_2_6_3.platform_packages} ;build_flags = ${core_2_6_3.build_flags} -platform = ${tasmota_core_stage.platform} -platform_packages = ${tasmota_core_stage.platform_packages} -build_flags = ${tasmota_core_stage.build_flags} +platform = ${tasmota_feature_stage.platform} +platform_packages = ${tasmota_feature_stage.platform_packages} +build_flags = ${tasmota_feature_stage.build_flags} ;platform = ${core_stage.platform} ;platform_packages = ${core_stage.platform_packages} @@ -177,10 +177,10 @@ build_flags = ${esp82xx_defaults.build_flags} ; -fexceptions ; -lstdc++-exc -[tasmota_core_stage] -; *** Esp8266 core for Arduino version stable beta +[tasmota_feature_stage] +; *** Esp8266 core for Arduino version Tasmota feature stage platform = espressif8266@2.3.3 -platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#372a3ec297dfe8501bed1ec4552244695b5e8ced +platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#e64cb619f79a3c888dd56b957d8f48ce74f35735 build_flags = ${esp82xx_defaults.build_flags} -DBEARSSL_SSL_BASIC ; NONOSDK221 From 4749222d95168dd7b1b4e3e3bd52adcad04d69b2 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sun, 22 Mar 2020 19:14:11 +0100 Subject: [PATCH 011/547] Fix Zigbee sending wrong Sat value with Hue emulation --- tasmota/CHANGELOG.md | 1 + tasmota/xdrv_23_zigbee_3_hue.ino | 5 +++++ tasmota/xdrv_23_zigbee_9_impl.ino | 1 + 3 files changed, 7 insertions(+) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index bea282bd9..e0ebc4d4a 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -5,6 +5,7 @@ - Change HM-10 sensor type detection and add features (#7962) - Change GPIO initialization solving possible Relay toggle on (OTA) restart - Add command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` +- Fix Zigbee sending wrong Sat value with Hue emulation ## Released diff --git a/tasmota/xdrv_23_zigbee_3_hue.ino b/tasmota/xdrv_23_zigbee_3_hue.ino index baab88d8f..c19216842 100644 --- a/tasmota/xdrv_23_zigbee_3_hue.ino +++ b/tasmota/xdrv_23_zigbee_3_hue.ino @@ -130,6 +130,7 @@ void ZigbeeHuePower(uint16_t shortaddr, uint8_t power) { // Dimmer void ZigbeeHueDimmer(uint16_t shortaddr, uint8_t dimmer) { + if (dimmer > 0xFE) { dimmer = 0xFE; } char param[8]; snprintf_P(param, sizeof(param), PSTR("%02X0A00"), dimmer); zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0008, 0x04, param); @@ -138,6 +139,7 @@ void ZigbeeHueDimmer(uint16_t shortaddr, uint8_t dimmer) { // CT void ZigbeeHueCT(uint16_t shortaddr, uint16_t ct) { + if (ct > 0xFEFF) { ct = 0xFEFF; } AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeHueCT 0x%04X - %d"), shortaddr, ct); char param[12]; snprintf_P(param, sizeof(param), PSTR("%02X%02X0A00"), ct & 0xFF, ct >> 8); @@ -149,6 +151,8 @@ void ZigbeeHueCT(uint16_t shortaddr, uint16_t ct) { // XY void ZigbeeHueXY(uint16_t shortaddr, uint16_t x, uint16_t y) { char param[16]; + if (x > 0xFEFF) { x = 0xFEFF; } + if (y > 0xFEFF) { y = 0xFEFF; } snprintf_P(param, sizeof(param), PSTR("%02X%02X%02X%02X0A00"), x & 0xFF, x >> 8, y & 0xFF, y >> 8); uint8_t colormode = 1; // "xy" zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0300, 0x07, param); @@ -159,6 +163,7 @@ void ZigbeeHueXY(uint16_t shortaddr, uint16_t x, uint16_t y) { void ZigbeeHueHS(uint16_t shortaddr, uint16_t hue, uint8_t sat) { char param[16]; uint8_t hue8 = changeUIntScale(hue, 0, 360, 0, 254); + if (sat > 0xFE) { sat = 0xFE; } snprintf_P(param, sizeof(param), PSTR("%02X%02X0A00"), hue8, sat); uint8_t colormode = 0; // "hs" zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0300, 0x06, param); diff --git a/tasmota/xdrv_23_zigbee_9_impl.ino b/tasmota/xdrv_23_zigbee_9_impl.ino index 674f8d655..f61979ea1 100644 --- a/tasmota/xdrv_23_zigbee_9_impl.ino +++ b/tasmota/xdrv_23_zigbee_9_impl.ino @@ -756,6 +756,7 @@ void CmndZbLight(void) { if (p) { int8_t bulbtype = strtol(p, nullptr, 10); + if (bulbtype > 5) { bulbtype = 5; } zigbee_devices.setHueBulbtype(shortaddr, bulbtype); } String dump = zigbee_devices.dumpLightState(shortaddr); From 12ec99ed070dcaa97f3e8e26e02ccde6a973e1ff Mon Sep 17 00:00:00 2001 From: Staars Date: Sun, 22 Mar 2020 21:35:44 +0100 Subject: [PATCH 012/547] web pagination, MJ_HT_V1 polling, some tweaks --- tasmota/xsns_62_MI_HM10.ino | 157 +++++++++++++++++++++++++++++------- 1 file changed, 127 insertions(+), 30 deletions(-) diff --git a/tasmota/xsns_62_MI_HM10.ino b/tasmota/xsns_62_MI_HM10.ino index 83252c4f6..7b72527b7 100644 --- a/tasmota/xsns_62_MI_HM10.ino +++ b/tasmota/xsns_62_MI_HM10.ino @@ -20,6 +20,8 @@ -------------------------------------------------------------------------------------------- Version yyyymmdd Action Description -------------------------------------------------------------------------------------------- + 0.9.3.0 20200322 added - multi page web view, command HM10PAGE, polling for MJ_HT_V1 + --- 0.9.2.0 20200317 added - MiBeacon-support, add Flora, MJ_HT_V1 and CGD1, add dew point, add AUTO(-scan), RULES-message --- @@ -42,11 +44,12 @@ TasmotaSerial *HM10Serial; #define HM10_MAX_TASK_NUMBER 12 uint8_t HM10_TASK_LIST[HM10_MAX_TASK_NUMBER+1][2]; // first value: kind of task - second value: delay in x * 100ms -#define HM10_MAX_RX_BUF 160 +#define HM10_MAX_RX_BUF 64 struct { uint8_t current_task_delay; // number of 100ms-cycles uint8_t last_command; + uint16_t perPage = 4; uint16_t firmware; uint32_t period; // set manually in addition to TELE-period, is set to TELE-period after start uint32_t serialSpeed; @@ -71,24 +74,24 @@ struct { } HM10; #pragma pack(1) // byte-aligned structures to read the sensor data -struct { - uint16_t temp; - uint8_t hum; -} LYWSD0x_HT; -struct { - uint8_t spare; - uint16_t temp; - uint16_t hum; -} CGD1_HT; + struct { + uint16_t temp; + uint8_t hum; + } LYWSD0x_HT; + struct { + uint8_t spare; + uint16_t temp; + uint16_t hum; + } CGD1_HT; + struct { + uint16_t temp; + uint8_t spare; + uint32_t lux; + uint8_t moist; + uint16_t fert; + } Flora_TLMF; // temperature, lux, moisture, fertility -struct { - uint16_t temp; - uint8_t spare; - uint32_t lux; - uint8_t moist; - uint16_t fert; -} Flora_TLMF; // temeprature, lux, moisture, fertility struct mi_beacon_t{ uint16_t frame; @@ -118,7 +121,7 @@ struct mi_sensor_t{ uint8_t type; //Flora = 1; MI-HT_V1=2; LYWSD02=3; LYWSD03=4; CGG1=5; CGD1=6 uint8_t serial[6]; uint8_t showedUp; - float temp; //Flora, MJ_HT_V1, LYWSD0x + float temp; //Flora, MJ_HT_V1, LYWSD0x, CGx union { struct { float moisture; @@ -129,7 +132,7 @@ struct mi_sensor_t{ float hum; }; // MJ_HT_V1, LYWSD0x }; - uint8_t bat; + uint8_t bat; // all sensors }; std::vector MIBLEsensors; @@ -142,7 +145,7 @@ std::vector MIBLEsensors; const char S_JSON_HM10_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_HM10 "%s\":%d}"; const char S_JSON_HM10_COMMAND[] PROGMEM = "{\"" D_CMND_HM10 "%s%s\"}"; -const char kHM10_Commands[] PROGMEM = "Scan|AT|Period|Baud|Time|Auto"; +const char kHM10_Commands[] PROGMEM = "Scan|AT|Period|Baud|Time|Auto|Page"; #define FLORA 1 #define MJ_HT_V1 2 @@ -177,7 +180,8 @@ enum HM10_Commands { // commands useable in console or rules CMND_HM10_PERIOD, // set period like TELE-period in seconds between read-cycles CMND_HM10_BAUD, // serial speed of ESP8266 (<-> HM10), does not change baud rate of HM10 CMND_HM10_TIME, // set LYWSD02-Time from ESP8266-time - CMND_HM10_AUTO // do discovery scans permanently to receive MiBeacons in seconds between read-cycles + CMND_HM10_AUTO, // do discovery scans permanently to receive MiBeacons in seconds between read-cycles + CMND_HM10_PAGE // sensor entries per web page, which will be shown alternated }; enum HM10_awaitData: uint8_t { @@ -186,7 +190,8 @@ enum HM10_awaitData: uint8_t { TLMF = 2, bat = 3, tempHumCGD1 = 4, - discScan = 5 + discScan = 5, + tempHumMJ = 6 }; /*********************************************************************************************\ @@ -222,7 +227,10 @@ enum HM10_awaitData: uint8_t { #define TASK_HM10_UN_HT_CGD1 26 // unsubscribe service handle 4b #define TASK_HM10_READ_B_CGD1 27 // read service handle 11 #define TASK_HM10_DELAY_SUB_CGD1 28 // start reading from subscription delayed -#define TASK_HM10_STATUS_EVENT 29 // process status for RULES +#define TASK_HM10_READ_B_MJ 29 // read service handle 18 +#define TASK_HM10_SUB_HT_MJ 30 // subscribe to service handle 0f + +#define TASK_HM10_STATUS_EVENT 32 // process status for RULES #define TASK_HM10_DONE 99 // used, if there was a task in the slot or just to wait @@ -314,6 +322,14 @@ void HM10_Read_CGD1(void) { HM10_Launchtask(TASK_HM10_DISCONN,5,5); // disconnect } +void HM10_Read_MJ_HT_V1(void) { + HM10_Launchtask(TASK_HM10_CONN,0,1); // connect + HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); // get OK+CONN + HM10_Launchtask(TASK_HM10_READ_B_MJ,2,20); // battery + HM10_Launchtask(TASK_HM10_SUB_HT_MJ,3,10); // temp hum + HM10_Launchtask(TASK_HM10_DISCONN,4,5); // disconnect + } + /** * @brief Return the slot number of a known sensor or return create new sensor slot * @@ -536,6 +552,7 @@ void HM10ParseResponse(char *buf, uint16_t bufsize) { void HM10readHT_LY(char *_buf){ DEBUG_SENSOR_LOG(PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_HM10,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); + if(_buf[0]==0x4f && _buf[1]==0x4b && _buf[2]==0x2b) return; // "OK+" if(_buf[0] != 0 && _buf[1] != 0){ memcpy(&LYWSD0x_HT,(void *)_buf,3); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: T * 100: %u, H: %u"),D_CMND_HM10,LYWSD0x_HT.temp,LYWSD0x_HT.hum); @@ -559,7 +576,8 @@ void HM10readHT_LY(char *_buf){ } void HM10readHT_CGD1(char *_buf){ - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_HM10,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); + DEBUG_SENSOR_LOG(PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_HM10,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); + if(_buf[0]==0x4f && _buf[1]==0x4b && _buf[2]==0x2b) return; // "OK+" if(_buf[0] == 0){ if(_buf[1]==0 && _buf[2]==0 && _buf[3]==0 && _buf[4]==0) return; memcpy(&CGD1_HT,(void *)_buf,5); @@ -583,8 +601,35 @@ void HM10readHT_CGD1(char *_buf){ } } +void HM10readHT_MJ_HT_V1(char *_buf){ + DEBUG_SENSOR_LOG(PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_HM10,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); + if(_buf[0]!=0x54 && _buf[1]!=0x3d) return; //"T=" + // T=22.7 H=42.2 (response as ASCII) + // 0123456789012 + uint32_t _temp = (atoi(_buf+2) * 10) + atoi(_buf+5); + uint32_t _hum = (atoi(_buf+9) * 10) + atoi(_buf+12); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: T * 10: %u, H * 10: %u"),D_CMND_HM10,_temp,_hum); + uint32_t _slot = HM10.state.sensor; + + DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); + static float _tempFloat; + _tempFloat=(float)_temp/10.0f; + if(_tempFloat<60){ + MIBLEsensors.at(_slot).temp=_tempFloat; + HM10.mode.awaiting = none; + HM10.current_task_delay = 0; + MIBLEsensors.at(_slot).showedUp=255; // this sensor is real + } + _tempFloat=(float)_hum/10.0f; + if(_tempFloat<100){ + MIBLEsensors.at(_slot).hum = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("MJ_HT_V1: hum updated")); + } +} + void HM10readTLMF(char *_buf){ DEBUG_SENSOR_LOG(PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_HM10,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); + if(_buf[0]==0x4f && _buf[1]==0x4b && _buf[2]==0x2b) return; // "OK+" if(_buf[0] != 0 || _buf[1] != 0){ // this will lose 0.0 degree, but it is not possible to measure a successful reading memcpy(&Flora_TLMF,(void *)_buf,10); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: T * 10: %u, L: %u, M: %u, F: %u"),D_CMND_HM10,Flora_TLMF.temp,Flora_TLMF.lux,Flora_TLMF.moist,Flora_TLMF.fert); @@ -612,6 +657,7 @@ void HM10readTLMF(char *_buf){ bool HM10readBat(char *_buf){ DEBUG_SENSOR_LOG(PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_HM10,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); + if(_buf[0]==0x4f && _buf[1]==0x4b && _buf[2]==0x2b) return false; // "OK+" if(_buf[0] != 0){ AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: Battery: %u"),D_CMND_HM10,_buf[0]); uint32_t _slot = HM10.state.sensor; @@ -662,12 +708,15 @@ bool HM10SerialHandleFeedback(){ // every 50 milliseconds break; case TLMF: if (HM10.mode.connected) HM10readTLMF(ret); - break; + break; case discScan: if(success) { HM10ParseResponse(ret,i); } break; + case tempHumMJ: + if (HM10.mode.connected) HM10readHT_MJ_HT_V1(ret); + break; case none: if(success) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: response: %s"),D_CMND_HM10, (char *)ret); @@ -875,6 +924,23 @@ void HM10_TaskEvery100ms(){ runningTaskLoop = false; HM10Serial->write("AT+SCAN9"); break; + case TASK_HM10_READ_B_MJ: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0x18"),D_CMND_HM10); + HM10.current_task_delay = 2; // set task delay + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+READDATA0018?"); + HM10.mode.awaiting = bat; + break; + case TASK_HM10_SUB_HT_MJ: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: subscribe to 0x0f"),D_CMND_HM10); + HM10.current_task_delay = 10; // set task delay + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10.mode.awaiting = none; + HM10Serial->write("AT+NOTIFY_ON000F"); + HM10.mode.awaiting = tempHumMJ; + break; case TASK_HM10_FEEDBACK: AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: get response"),D_CMND_HM10); HM10SerialHandleFeedback(); @@ -965,14 +1031,17 @@ void HM10EverySecond(){ _nextSensorSlot++; HM10.mode.pending_task = 1; switch(MIBLEsensors.at(HM10.state.sensor).type){ - case LYWSD03MMC: - HM10_Read_LYWSD03(); + case FLORA: + HM10_Read_Flora(); + break; + case MJ_HT_V1: + HM10_Read_MJ_HT_V1(); break; case LYWSD02: HM10_Read_LYWSD02(); break; - case FLORA: - HM10_Read_Flora(); + case LYWSD03MMC: + HM10_Read_LYWSD03(); break; case CGD1: HM10_Read_CGD1(); @@ -1051,6 +1120,13 @@ bool HM10Cmd(void) { } Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); break; + case CMND_HM10_PAGE: + if (XdrvMailbox.data_len > 0) { + HM10.perPage = XdrvMailbox.payload; + } + else XdrvMailbox.payload = HM10.perPage; + Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; case CMND_HM10_AT: HM10Serial->write("AT"); // without an argument this will disconnect if (strlen(XdrvMailbox.data)!=0) { @@ -1081,6 +1157,7 @@ bool HM10Cmd(void) { \*********************************************************************************************/ const char HTTP_HM10[] PROGMEM = "{s}HM10" " Firmware " "{m}%u{e}"; +const char HTTP_HM10_PAGE[] PROGMEM = "{s}{m}%u%s / %u{e}"; const char HTTP_HM10_SERIAL[] PROGMEM = "{s}%s %s{m}%02x:%02x:%02x:%02x:%02x:%02x%{e}"; const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u%%{e}"; const char HTTP_HM10_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%sus/cm{e}"; @@ -1139,8 +1216,21 @@ void HM10Show(bool json) } #ifdef USE_WEBSERVER } else { + static uint16_t _page = 0; + static uint16_t _counter = 0; + uint32_t i = _page * HM10.perPage; + uint32_t j = i + HM10.perPage; + if (j+1>MIBLEsensors.size()){ + j = MIBLEsensors.size(); + } + char stemp[5] ={0}; + if (MIBLEsensors.size()-(_page*HM10.perPage)>1 && HM10.perPage!=1) { + sprintf_P(stemp,"-%u",j); + } + WSContentSend_PD(HTTP_HM10, HM10.firmware); - for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { + if (MIBLEsensors.size()>0) WSContentSend_PD(HTTP_HM10_PAGE, i+1,stemp,MIBLEsensors.size()); + for (i; i3) { + _page++; + _counter=0; + } + if(MIBLEsensors.size()%HM10.perPage==0 && _page==MIBLEsensors.size()/HM10.perPage) _page=0; + if(_page>MIBLEsensors.size()/HM10.perPage) _page=0; #endif // USE_WEBSERVER } } From 8ea179aed5b1dc4666d4164db0bfcba658c7cab4 Mon Sep 17 00:00:00 2001 From: Staars Date: Sun, 22 Mar 2020 22:00:45 +0100 Subject: [PATCH 013/547] catch hm10page 0, hm10period 1 now triggers one read cycle --- tasmota/xsns_62_MI_HM10.ino | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tasmota/xsns_62_MI_HM10.ino b/tasmota/xsns_62_MI_HM10.ino index 7b72527b7..16ac499ae 100644 --- a/tasmota/xsns_62_MI_HM10.ino +++ b/tasmota/xsns_62_MI_HM10.ino @@ -1007,11 +1007,17 @@ void HM10StatusInfo(){ * */ -void HM10EverySecond(){ +void HM10EverySecond(bool restart){ static uint32_t _counter = 0; static uint32_t _nextSensorSlot = 0; static uint32_t _lastDiscovery = 0; + if(restart){ + _counter = 0; + _lastDiscovery = 0; + return; + } + if(HM10.firmware == 0) return; if(HM10.mode.pending_task == 1) return; if(MIBLEsensors.size()==0 && !HM10.mode.autoScan) return; @@ -1076,7 +1082,13 @@ bool HM10Cmd(void) { switch (command_code) { case CMND_HM10_PERIOD: if (XdrvMailbox.data_len > 0) { - HM10.period = XdrvMailbox.payload; + if (XdrvMailbox.payload==1) { + HM10EverySecond(true); + XdrvMailbox.payload = HM10.period; + } + else { + HM10.period = XdrvMailbox.payload; + } } else { XdrvMailbox.payload = HM10.period; @@ -1122,6 +1134,7 @@ bool HM10Cmd(void) { break; case CMND_HM10_PAGE: if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload == 0) XdrvMailbox.payload = HM10.perPage; // ignore 0 HM10.perPage = XdrvMailbox.payload; } else XdrvMailbox.payload = HM10.perPage; @@ -1300,7 +1313,7 @@ bool Xsns62(uint8_t function) } break; case FUNC_EVERY_SECOND: - HM10EverySecond(); + HM10EverySecond(false); break; case FUNC_COMMAND: result = HM10Cmd(); From f9d6ab1825d24e90177641bf90c5b5f9c527af0c Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 23 Mar 2020 08:25:01 +0100 Subject: [PATCH 014/547] Better test for bulbtype --- tasmota/xdrv_23_zigbee_2_devices.ino | 2 +- tasmota/xdrv_23_zigbee_9_impl.ino | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino index 7443b5457..7b02e33ff 100644 --- a/tasmota/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/xdrv_23_zigbee_2_devices.ino @@ -1007,7 +1007,7 @@ String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const { if (device.modelId) { dev[F(D_JSON_MODEL D_JSON_ID)] = device.modelId; } - if (-1 != device.bulbtype) { + if (device.bulbtype >= 0) { dev[F(D_JSON_ZIGBEE_LIGHT)] = device.bulbtype; // sign extend, 0xFF changed as -1 } if (device.manufacturerId) { diff --git a/tasmota/xdrv_23_zigbee_9_impl.ino b/tasmota/xdrv_23_zigbee_9_impl.ino index f61979ea1..12c9ff98f 100644 --- a/tasmota/xdrv_23_zigbee_9_impl.ino +++ b/tasmota/xdrv_23_zigbee_9_impl.ino @@ -756,7 +756,8 @@ void CmndZbLight(void) { if (p) { int8_t bulbtype = strtol(p, nullptr, 10); - if (bulbtype > 5) { bulbtype = 5; } + if (bulbtype > 5) { bulbtype = 5; } + if (bulbtype < -1) { bulbtype = -1; } zigbee_devices.setHueBulbtype(shortaddr, bulbtype); } String dump = zigbee_devices.dumpLightState(shortaddr); From 22eaa5795322c325a38ec22d6f77ba5b11e05645 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 23 Mar 2020 11:55:20 +0100 Subject: [PATCH 015/547] Remove gzip script... is already done in Platformio.ini --- platformio_override_sample.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini index 18766c735..deeafc3ee 100644 --- a/platformio_override_sample.ini +++ b/platformio_override_sample.ini @@ -54,7 +54,6 @@ build_flags = ${core_active.build_flags} upload_port = COM5 extra_scripts = ${scripts_defaults.extra_scripts} - pio/gzip-firmware.py pio/obj-dump.py ; *** Upload file to OTA server using SCP From 0a573bd0d68c4ef975f1802c13e29a849df9125c Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 23 Mar 2020 15:19:08 +0100 Subject: [PATCH 016/547] Update changelog and releasenotes --- RELEASENOTES.md | 5 +++-- tasmota/CHANGELOG.md | 12 ++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 6d46d8cda..1804b32c5 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -21,7 +21,7 @@ While fallback or downgrading is common practice it was never supported due to S ## Supported Core versions -This release will be supported from ESP8266/Arduino library Core version **2.6.3 + 372a3ec** due to reported security and stability issues on previous Core version. This will also support gzipped binaries. +This release will be supported from ESP8266/Arduino library Core version **2.6.3 + e64cb61** due to reported security and stability issues on previous Core version. This will also support gzipped binaries. Although it might still compile on previous Core versions all support will be removed in the near future. @@ -35,7 +35,7 @@ For initial configuration this release supports Webserver based **WifiManager** ## Provided Binary Downloads -The following binary downloads have been compiled with ESP8266/Arduino library core version **2.6.3 + 372a3ec**. +The following binary downloads have been compiled with ESP8266/Arduino library core version **2.6.3 + e64cb61**. - **tasmota.bin** = The Tasmota version with most drivers. **RECOMMENDED RELEASE BINARY** - **tasmota-BG.bin** to **tasmota-TW.bin** = The Tasmota version in different languages. @@ -56,4 +56,5 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Change HM-10 sensor type detection and add features (#7962) - Change GPIO initialization solving possible Relay toggle on (OTA) restart +- Fix Zigbee sending wrong Sat value with Hue emulation - Add command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index e0ebc4d4a..12f581ce8 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -4,14 +4,14 @@ - Change HM-10 sensor type detection and add features (#7962) - Change GPIO initialization solving possible Relay toggle on (OTA) restart -- Add command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` - Fix Zigbee sending wrong Sat value with Hue emulation +- Add command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` ## Released ### 8.2.0 20200321 -- Release +- Release Elliot ### 8.1.0.11 20200313 @@ -134,7 +134,7 @@ ### 8.1.0 20191225 -- Release +- Release Doris ### 8.0.0.3 20191224 @@ -160,7 +160,7 @@ ### 7.2.0 20191221 -- Release +- Release Constance - Change basic version string to lite (#7291) - Fix Arduino IDE compile error (#7277) - Fix restore ShutterAccuracy, MqttLog, WifiConfig, WifiPower and SerialConfig (#7281) @@ -243,7 +243,7 @@ ### 7.1.0 20191129 -- Release +- Release Doris ### 7.0.0.6 20191122 @@ -312,7 +312,7 @@ ### 6.7.1 20191026 -- Release +- Release Allison - Fix on energy monitoring devices using PowerDelta Exception0 with epc1:0x4000dce5 = Divide by zero (#6750) - Fix Script array bug (#6751) From 4a89c56182e31d349e6d5d18d10f624b121221af Mon Sep 17 00:00:00 2001 From: Staars Date: Mon, 23 Mar 2020 17:42:29 +0100 Subject: [PATCH 017/547] refactoring, more compact UI --- tasmota/xsns_62_MI_HM10.ino | 79 +++++++++++++++---------------------- 1 file changed, 32 insertions(+), 47 deletions(-) diff --git a/tasmota/xsns_62_MI_HM10.ino b/tasmota/xsns_62_MI_HM10.ino index 16ac499ae..581a32096 100644 --- a/tasmota/xsns_62_MI_HM10.ino +++ b/tasmota/xsns_62_MI_HM10.ino @@ -20,7 +20,8 @@ -------------------------------------------------------------------------------------------- Version yyyymmdd Action Description -------------------------------------------------------------------------------------------- - 0.9.3.0 20200322 added - multi page web view, command HM10PAGE, polling for MJ_HT_V1 + 0.9.3.0 20200322 added - multi page web view, command HM10PAGE, polling for MJ_HT_V1, + more stable readings, internal refactoring --- 0.9.2.0 20200317 added - MiBeacon-support, add Flora, MJ_HT_V1 and CGD1, add dew point, add AUTO(-scan), RULES-message @@ -370,17 +371,17 @@ uint32_t MIBLEgetSensorSlot(uint8_t (&_serial)[6], uint16_t _type){ memcpy(_newSensor.serial,_serial, sizeof(_serial)); _newSensor.type = _type; _newSensor.showedUp = 1; - _newSensor.temp =-1000.0f; + _newSensor.temp =NAN; _newSensor.bat=0x00; switch (_type) { case 1: - _newSensor.moisture =-1000.0f; - _newSensor.fertility =-1000.0f; + _newSensor.moisture =NAN; + _newSensor.fertility =NAN; _newSensor.lux = 0x00ffffff; break; case 2: case 3: case 4: case 5: case 6: - _newSensor.hum=-1.0f; + _newSensor.hum=NAN; break; default: break; @@ -1169,8 +1170,7 @@ bool HM10Cmd(void) { * Presentation \*********************************************************************************************/ -const char HTTP_HM10[] PROGMEM = "{s}HM10" " Firmware " "{m}%u{e}"; -const char HTTP_HM10_PAGE[] PROGMEM = "{s}{m}%u%s / %u{e}"; +const char HTTP_HM10[] PROGMEM = "{s}HM10 V%u{m}%u%s / %u{e}"; const char HTTP_HM10_SERIAL[] PROGMEM = "{s}%s %s{m}%02x:%02x:%02x:%02x:%02x:%02x%{e}"; const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u%%{e}"; const char HTTP_HM10_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%sus/cm{e}"; @@ -1182,18 +1182,17 @@ void HM10Show(bool json) for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { char slave[33]; sprintf_P(slave,"%s-%02x%02x%02x",kHM10SlaveType[MIBLEsensors.at(i).type-1],MIBLEsensors.at(i).serial[3],MIBLEsensors.at(i).serial[4],MIBLEsensors.at(i).serial[5]); - char temperature[FLOATSZ]; // all sensors have temperature - dtostrfd(MIBLEsensors.at(i).temp, Settings.flag2.temperature_resolution, temperature); - ResponseAppend_P(PSTR(",\"%s\":{"),slave); - if(MIBLEsensors.at(i).temp!=-1000.0f){ // this is the error code -> no temperature - ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), temperature); - } - else { - ResponseAppend_P(PSTR("}")); - continue; - } if (MIBLEsensors.at(i).type==FLORA){ + if(!isnan(MIBLEsensors.at(i).temp)){ // this is the error code -> no temperature + char temperature[FLOATSZ]; // all sensors have temperature + dtostrfd(MIBLEsensors.at(i).temp, Settings.flag2.temperature_resolution, temperature); + ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), temperature); + } + else { + ResponseAppend_P(PSTR("}")); + continue; + } char lux[FLOATSZ]; char moisture[FLOATSZ]; char fertility[FLOATSZ]; @@ -1203,23 +1202,16 @@ void HM10Show(bool json) if(MIBLEsensors.at(i).lux!=0x0ffffff){ // this is the error code -> no temperature ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%s"), lux); } - if(MIBLEsensors.at(i).moisture!=-1000.0f){ // this is the error code -> no moisture + if(!isnan(MIBLEsensors.at(i).moisture)){ // this is the error code -> no moisture ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%s"), moisture); } - if(MIBLEsensors.at(i).fertility!=-1000.0f){ // this is the error code -> no fertility + if(!isnan(MIBLEsensors.at(i).fertility)){ // this is the error code -> no fertility ResponseAppend_P(PSTR(",\"Fertility\":%s"), fertility); } } if (MIBLEsensors.at(i).type>FLORA){ - char humidity[FLOATSZ]; - dtostrfd(MIBLEsensors.at(i).hum, Settings.flag2.humidity_resolution, humidity); - if(MIBLEsensors.at(i).hum!=-1.0f){ // this is the error code -> no humidity - ResponseAppend_P(PSTR(",\"" D_JSON_HUMIDITY "\":%s"), humidity); - } - if(MIBLEsensors.at(i).hum!=-1.0f && MIBLEsensors.at(i).temp!=-1000.0f){ // this is the error code -> no humidity nor temp - char dewpoint[FLOATSZ]; - dtostrfd(CalcTempHumToDew(MIBLEsensors.at(i).temp, MIBLEsensors.at(i).hum), Settings.flag2.temperature_resolution, dewpoint); - ResponseAppend_P(PSTR(",\"" D_JSON_DEWPOINT "\":%s"), dewpoint); + if(!isnan(MIBLEsensors.at(i).hum) && !isnan(MIBLEsensors.at(i).temp)){ // this is the error code -> no humidity nor temp + ResponseAppendTHD(MIBLEsensors.at(i).temp, MIBLEsensors.at(i).hum); } } if(MIBLEsensors.at(i).bat!=0x00){ // this is the error code -> no battery @@ -1231,7 +1223,7 @@ void HM10Show(bool json) } else { static uint16_t _page = 0; static uint16_t _counter = 0; - uint32_t i = _page * HM10.perPage; + int32_t i = _page * HM10.perPage; uint32_t j = i + HM10.perPage; if (j+1>MIBLEsensors.size()){ j = MIBLEsensors.size(); @@ -1240,40 +1232,33 @@ void HM10Show(bool json) if (MIBLEsensors.size()-(_page*HM10.perPage)>1 && HM10.perPage!=1) { sprintf_P(stemp,"-%u",j); } + if (MIBLEsensors.size()==0) i=-1; // only for the GUI - WSContentSend_PD(HTTP_HM10, HM10.firmware); - if (MIBLEsensors.size()>0) WSContentSend_PD(HTTP_HM10_PAGE, i+1,stemp,MIBLEsensors.size()); + WSContentSend_PD(HTTP_HM10, HM10.firmware, i+1,stemp,MIBLEsensors.size()); for (i; i no valid value WSContentSend_PD(HTTP_SNS_ILLUMINANCE, kHM10SlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).lux); } - if(MIBLEsensors.at(i).moisture!=-1000.0f){ // this is the error code -> no valid value + if(!isnan(MIBLEsensors.at(i).moisture)){ // this is the error code -> no valid value WSContentSend_PD(HTTP_SNS_MOISTURE, kHM10SlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).moisture); } - if(MIBLEsensors.at(i).fertility!=-1000.0f){ // this is the error code -> no valid value + if(!isnan(MIBLEsensors.at(i).fertility)){ // this is the error code -> no valid value char fertility[FLOATSZ]; dtostrfd(MIBLEsensors.at(i).fertility, 0, fertility); WSContentSend_PD(HTTP_HM10_FLORA_DATA, kHM10SlaveType[MIBLEsensors.at(i).type-1], fertility); } } if (MIBLEsensors.at(i).type>FLORA){ // everything "above" Flora - if(MIBLEsensors.at(i).hum!=-1.0f){ // this is the error code -> no humidity - char humidity[FLOATSZ]; - dtostrfd(MIBLEsensors.at(i).hum, Settings.flag2.humidity_resolution, humidity); - WSContentSend_PD(HTTP_SNS_HUM, kHM10SlaveType[MIBLEsensors.at(i).type-1], humidity); - } - if(MIBLEsensors.at(i).hum!=-1.0f && MIBLEsensors.at(i).temp!=-1000.0f){ // this is the error code -> no humidity nor temp - char dewpoint[FLOATSZ]; - dtostrfd(CalcTempHumToDew(MIBLEsensors.at(i).temp, MIBLEsensors.at(i).hum), Settings.flag2.temperature_resolution, dewpoint); - WSContentSend_PD(HTTP_SNS_DEW, kHM10SlaveType[MIBLEsensors.at(i).type-1], dewpoint, TempUnit()); + if(!isnan(MIBLEsensors.at(i).hum) && !isnan(MIBLEsensors.at(i).temp)){ + WSContentSend_THD(kHM10SlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).temp, MIBLEsensors.at(i).hum); } } if(MIBLEsensors.at(i).bat!=0x00){ From ac4d4ac57114700e25cd54f103f777b2c6ca19b8 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 23 Mar 2020 22:46:26 +0100 Subject: [PATCH 018/547] Code optimization, cleaning and more error codes --- tasmota/support_static_buffer.ino | 4 +- tasmota/xdrv_23_zigbee_0_constants.ino | 106 +++------- tasmota/xdrv_23_zigbee_2_devices.ino | 93 ++++----- tasmota/xdrv_23_zigbee_6_commands.ino | 7 + tasmota/xdrv_23_zigbee_7_statemachine.ino | 88 ++++++++- tasmota/xdrv_23_zigbee_8_parsers.ino | 226 ++++++++++++--------- tasmota/xdrv_23_zigbee_9_impl.ino | 227 +++++++++++----------- 7 files changed, 406 insertions(+), 345 deletions(-) diff --git a/tasmota/support_static_buffer.ino b/tasmota/support_static_buffer.ino index bec831cc9..ea21b2805 100644 --- a/tasmota/support_static_buffer.ino +++ b/tasmota/support_static_buffer.ino @@ -112,7 +112,7 @@ public: } size_t addBuffer(const uint8_t *buf2, size_t len2) { - if (len() + len2 <= size()) { + if ((buf2) && (len() + len2 <= size())) { for (uint32_t i = 0; i < len2; i++) { _buf->buf[_buf->len++] = pgm_read_byte(&buf2[i]); } @@ -121,7 +121,7 @@ public: } size_t addBuffer(const char *buf2, size_t len2) { - if (len() + len2 <= size()) { + if ((buf2) && (len() + len2 <= size())) { for (uint32_t i = 0; i < len2; i++) { _buf->buf[_buf->len++] = pgm_read_byte(&buf2[i]); } diff --git a/tasmota/xdrv_23_zigbee_0_constants.ino b/tasmota/xdrv_23_zigbee_0_constants.ino index d97a26f9f..1a23c98dd 100644 --- a/tasmota/xdrv_23_zigbee_0_constants.ino +++ b/tasmota/xdrv_23_zigbee_0_constants.ino @@ -393,88 +393,36 @@ typedef struct Z_StatusLine { const char * status_msg; } Z_StatusLine; -ZF(SUCCESS) -ZF(FAILURE) -ZF(NOT_AUTHORIZED) -ZF(RESERVED_FIELD_NOT_ZERO) -ZF(MALFORMED_COMMAND) -ZF(UNSUP_CLUSTER_COMMAND) -ZF(UNSUP_GENERAL_COMMAND) -ZF(UNSUP_MANUF_CLUSTER_COMMAND) -ZF(UNSUP_MANUF_GENERAL_COMMAND) -ZF(INVALID_FIELD) -ZF(UNSUPPORTED_ATTRIBUTE) -ZF(INVALID_VALUE) -ZF(READ_ONLY) -ZF(INSUFFICIENT_SPACE) -ZF(DUPLICATE_EXISTS) -ZF(NOT_FOUND) -ZF(UNREPORTABLE_ATTRIBUTE) -ZF(INVALID_DATA_TYPE) -ZF(INVALID_SELECTOR) -ZF(WRITE_ONLY) -ZF(INCONSISTENT_STARTUP_STATE) -ZF(DEFINED_OUT_OF_BAND) -ZF(INCONSISTENT) -ZF(ACTION_DENIED) -ZF(TIMEOUT) -ZF(ABORT) -ZF(INVALID_IMAGE) -ZF(WAIT_FOR_DATA) -ZF(NO_IMAGE_AVAILABLE) -ZF(REQUIRE_MORE_IMAGE) -ZF(NOTIFICATION_PENDING) -ZF(HARDWARE_FAILURE) -ZF(SOFTWARE_FAILURE) -ZF(CALIBRATION_ERROR) -ZF(UNSUPPORTED_CLUSTER) +// Undocumented Zigbee ZCL code here: https://github.com/dresden-elektronik/deconz-rest-plugin/wiki/Zigbee-Error-Codes-in-the-Log +String getZigbeeStatusMessage(uint8_t status) { + static const char StatusMsg[] PROGMEM = "SUCCESS|FAILURE|NOT_AUTHORIZED|RESERVED_FIELD_NOT_ZERO|MALFORMED_COMMAND|UNSUP_CLUSTER_COMMAND|UNSUP_GENERAL_COMMAND" + "|UNSUP_MANUF_CLUSTER_COMMAND|UNSUP_MANUF_GENERAL_COMMAND|INVALID_FIELD|UNSUPPORTED_ATTRIBUTE|INVALID_VALE|READ_ONLY" + "|INSUFFICIENT_SPACE|DUPLICATE_EXISTS|NOT_FOUND|UNREPORTABLE_ATTRIBUTE|INVALID_DATA_TYPE|INVALID_SELECTOR|WRITE_ONLY" + "|INCONSISTENT_STARTUP_STATE|DEFINED_OUT_OF_BAND|INCONSISTENT|ACTION_DENIED|TIMEOUT|ABORT|INVALID_IMAGE|WAIT_FOR_DATA" + "|NO_IMAGE_AVAILABLE|REQUIRE_MORE_IMAGE|NOTIFICATION_PENDING|HARDWARE_FAILURE|SOFTWARE_FAILURE|CALIBRATION_ERROR|UNSUPPORTED_CLUSTER" + "|CHANNEL_ACCESS_FAILURE|NO_ACK|NO_APP_ACK|NO_ROUTE" + ; + static const uint8_t StatusIdx[] PROGMEM = { 0x00, 0x01, 0x7E, 0x7F, 0x80, 0x81, 0x82, + 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, + 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9A, 0xC0, 0xC1, 0xC2, 0xC3, + 0xE1, 0xE9, 0xA7, 0xD0}; -const Z_StatusLine Z_Status[] PROGMEM = { - 0x00, Z(SUCCESS), - 0x01, Z(FAILURE), - 0x7E, Z(NOT_AUTHORIZED), - 0x7F, Z(RESERVED_FIELD_NOT_ZERO), - 0x80, Z(MALFORMED_COMMAND), - 0x81, Z(UNSUP_CLUSTER_COMMAND), - 0x82, Z(UNSUP_GENERAL_COMMAND), - 0x83, Z(UNSUP_MANUF_CLUSTER_COMMAND), - 0x84, Z(UNSUP_MANUF_GENERAL_COMMAND), - 0x85, Z(INVALID_FIELD), - 0x86, Z(UNSUPPORTED_ATTRIBUTE), - 0x87, Z(INVALID_VALUE), - 0x88, Z(READ_ONLY), - 0x89, Z(INSUFFICIENT_SPACE), - 0x8A, Z(DUPLICATE_EXISTS), - 0x8B, Z(NOT_FOUND), - 0x8C, Z(UNREPORTABLE_ATTRIBUTE), - 0x8D, Z(INVALID_DATA_TYPE), - 0x8E, Z(INVALID_SELECTOR), - 0x8F, Z(WRITE_ONLY), - 0x90, Z(INCONSISTENT_STARTUP_STATE), - 0x91, Z(DEFINED_OUT_OF_BAND), - 0x92, Z(INCONSISTENT), - 0x93, Z(ACTION_DENIED), - 0x94, Z(TIMEOUT), - 0x95, Z(ABORT), - 0x96, Z(INVALID_IMAGE), - 0x97, Z(WAIT_FOR_DATA), - 0x98, Z(NO_IMAGE_AVAILABLE), - 0x99, Z(REQUIRE_MORE_IMAGE), - 0x9A, Z(NOTIFICATION_PENDING), - 0xC0, Z(HARDWARE_FAILURE), - 0xC1, Z(SOFTWARE_FAILURE), - 0xC2, Z(CALIBRATION_ERROR), - 0xC3, Z(UNSUPPORTED_CLUSTER), -}; - -const __FlashStringHelper* getZigbeeStatusMessage(uint8_t status) { - for (uint32_t i = 0; i < sizeof(Z_Status) / sizeof(Z_Status[0]); i++) { - const Z_StatusLine *statl = &Z_Status[i]; - if (statl->status == status) { - return (const __FlashStringHelper*) statl->status_msg; + char msg[32]; + int32_t idx = -1; + for (uint32_t i = 0; i < sizeof(StatusIdx); i++) { + if (status == pgm_read_byte(&StatusIdx[i])) { + idx = i; + break; } } - return F(""); + if (idx >= 0) { + GetTextIndexed(msg, sizeof(msg), idx, StatusMsg); + } else { + *msg = 0x00; // empty string + } + return String(msg); } #endif // USE_ZIGBEE diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino index 7b02e33ff..e4c6f6df0 100644 --- a/tasmota/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/xdrv_23_zigbee_2_devices.ino @@ -26,7 +26,9 @@ #endif const uint16_t kZigbeeSaveDelaySeconds = ZIGBEE_SAVE_DELAY_SECONDS; // wait for x seconds -typedef int32_t (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); +/*********************************************************************************************\ + * Structures for device configuration +\*********************************************************************************************/ const size_t endpoints_max = 8; // we limit to 8 endpoints @@ -53,6 +55,12 @@ typedef struct Z_Device { uint16_t x, y; // last color [x,y] } Z_Device; +/*********************************************************************************************\ + * Structures for deferred callbacks +\*********************************************************************************************/ + +typedef int32_t (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); + // Category for Deferred actions, this allows to selectively remove active deferred or update them typedef enum Z_Def_Category { Z_CAT_NONE = 0, // no category, it will happen anyways @@ -76,6 +84,10 @@ typedef struct Z_Deferred { Z_DeviceTimer func; // function to call when timer occurs } Z_Deferred; +/*********************************************************************************************\ + * Singleton for device configuration +\*********************************************************************************************/ + // All devices are stored in a Vector // Invariants: // - shortaddr is unique if not null @@ -190,13 +202,22 @@ private: // Create a new entry in the devices list - must be called if it is sure it does not already exist Z_Device & createDeviceEntry(uint16_t shortaddr, uint64_t longaddr = 0); void freeDeviceEntry(Z_Device *device); + + void setStringAttribute(char*& attr, const char * str); }; +/*********************************************************************************************\ + * Singleton variable +\*********************************************************************************************/ Z_Devices zigbee_devices = Z_Devices(); // Local coordinator information uint64_t localIEEEAddr = 0; +/*********************************************************************************************\ + * Implementation +\*********************************************************************************************/ + // https://thispointer.com/c-how-to-find-an-element-in-vector-and-get-its-index/ template < typename T> bool Z_Devices::findInVector(const std::vector & vecOfElements, const T & element) { @@ -493,73 +514,55 @@ uint8_t Z_Devices::findFirstEndpoint(uint16_t shortaddr) const { return device.endpoints[0]; // returns 0x00 if no endpoint } -void Z_Devices::setManufId(uint16_t shortaddr, const char * str) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } // don't crash if not found +void Z_Devices::setStringAttribute(char*& attr, const char * str) { size_t str_len = str ? strlen(str) : 0; // len, handle both null ptr and zero length string - if ((!device.manufacturerId) && (0 == str_len)) { return; } // if both empty, don't do anything - if (device.manufacturerId) { + if ((nullptr == attr) && (0 == str_len)) { return; } // if both empty, don't do anything + if (attr) { // we already have a value - if (strcmp(device.manufacturerId, str) != 0) { + if (strcmp(attr, str) != 0) { // new value - free(device.manufacturerId); // free previous value - device.manufacturerId = nullptr; + free(attr); // free previous value + attr = nullptr; } else { return; // same value, don't change anything } } if (str_len) { - device.manufacturerId = (char*) malloc(str_len + 1); - strlcpy(device.manufacturerId, str, str_len + 1); + attr = (char*) malloc(str_len + 1); + strlcpy(attr, str, str_len + 1); } dirty(); } +// +// Sets the ManufId for a device. +// No action taken if the device does not exist. +// Inputs: +// - shortaddr: 16-bits short address of the device. No action taken if the device is unknown +// - str: string pointer, if nullptr it is considered as empty string +// Impact: +// - Any actual change in ManufId (i.e. setting a different value) triggers a `dirty()` and saving to Flash +// +void Z_Devices::setManufId(uint16_t shortaddr, const char * str) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } // don't crash if not found + + setStringAttribute(device.manufacturerId, str); +} + void Z_Devices::setModelId(uint16_t shortaddr, const char * str) { Z_Device & device = getShortAddr(shortaddr); if (&device == nullptr) { return; } // don't crash if not found - size_t str_len = str ? strlen(str) : 0; // len, handle both null ptr and zero length string - if ((!device.modelId) && (0 == str_len)) { return; } // if both empty, don't do anything - if (device.modelId) { - // we already have a value - if (strcmp(device.modelId, str) != 0) { - // new value - free(device.modelId); // free previous value - device.modelId = nullptr; - } else { - return; // same value, don't change anything - } - } - if (str_len) { - device.modelId = (char*) malloc(str_len + 1); - strlcpy(device.modelId, str, str_len + 1); - } - dirty(); + setStringAttribute(device.modelId, str); } void Z_Devices::setFriendlyName(uint16_t shortaddr, const char * str) { Z_Device & device = getShortAddr(shortaddr); if (&device == nullptr) { return; } // don't crash if not found - size_t str_len = str ? strlen(str) : 0; // len, handle both null ptr and zero length string - if ((!device.friendlyName) && (0 == str_len)) { return; } // if both empty, don't do anything - if (device.friendlyName) { - // we already have a value - if (strcmp(device.friendlyName, str) != 0) { - // new value - free(device.friendlyName); // free previous value - device.friendlyName = nullptr; - } else { - return; // same value, don't change anything - } - } - if (str_len) { - device.friendlyName = (char*) malloc(str_len + 1); - strlcpy(device.friendlyName, str, str_len + 1); - } - dirty(); + setStringAttribute(device.friendlyName, str); } const char * Z_Devices::getFriendlyName(uint16_t shortaddr) const { diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino index 8d756c95d..4788ddf67 100644 --- a/tasmota/xdrv_23_zigbee_6_commands.ino +++ b/tasmota/xdrv_23_zigbee_6_commands.ino @@ -19,6 +19,10 @@ #ifdef USE_ZIGBEE +/*********************************************************************************************\ + * ZCL Command Structures +\*********************************************************************************************/ + typedef struct Z_CommandConverter { const char * tasmota_cmd; uint16_t cluster; @@ -130,6 +134,9 @@ const Z_CommandConverter Z_Commands[] PROGMEM = { { Z(GetSceneMembership),0x0005, 0x06, 0x82,Z(xxyyzzzz) }, // specific }; +/*********************************************************************************************\ + * ZCL Read Light status based on cluster number +\*********************************************************************************************/ #define ZLE(x) ((x) & 0xFF), ((x) >> 8) // Little Endian // Below are the attributes we wand to read from each cluster diff --git a/tasmota/xdrv_23_zigbee_7_statemachine.ino b/tasmota/xdrv_23_zigbee_7_statemachine.ino index 2073fb9eb..1c4f782b4 100644 --- a/tasmota/xdrv_23_zigbee_7_statemachine.ino +++ b/tasmota/xdrv_23_zigbee_7_statemachine.ino @@ -20,7 +20,7 @@ #ifdef USE_ZIGBEE // Status code used for ZigbeeStatus MQTT message -// Ex: {"ZigbeeStatus":{"Status": 3,"Message":"Configured, starting coordinator"}} +// Ex: {"ZbStatus":{"Status": 3,"Message":"Configured, starting coordinator"}} const uint8_t ZIGBEE_STATUS_OK = 0; // Zigbee started and working const uint8_t ZIGBEE_STATUS_BOOT = 1; // CC2530 booting const uint8_t ZIGBEE_STATUS_RESET_CONF = 2; // Resetting CC2530 configuration @@ -33,7 +33,6 @@ const uint8_t ZIGBEE_STATUS_NODE_DESC = 31; // Node descriptor const uint8_t ZIGBEE_STATUS_ACTIVE_EP = 32; // Endpoints descriptor const uint8_t ZIGBEE_STATUS_SIMPLE_DESC = 33; // Simple Descriptor (clusters) const uint8_t ZIGBEE_STATUS_DEVICE_INDICATION = 34; // Device announces its address -//const uint8_t ZIGBEE_STATUS_DEVICE_IEEE = 35; // Request of device address const uint8_t ZIGBEE_STATUS_CC_VERSION = 50; // Status: CC2530 ZNP Version const uint8_t ZIGBEE_STATUS_CC_INFO = 51; // Status: CC2530 Device Configuration const uint8_t ZIGBEE_STATUS_UNSUPPORTED_VERSION = 98; // Unsupported ZNP version @@ -50,9 +49,6 @@ typedef union Zigbee_Instruction { } i; const void *p; // pointer } Zigbee_Instruction; -// -// Zigbee_Instruction z1 = { .i = {1,2,3}}; -// Zigbee_Instruction z3 = { .p = nullptr }; typedef struct Zigbee_Instruction_Type { uint8_t instr; @@ -488,7 +484,6 @@ void ZigbeeStateMachine_Run(void) { if (zigbee.state_waiting) { // state machine is waiting for external event or timeout // checking if timeout expired if ((zigbee.next_timeout) && (now > zigbee.next_timeout)) { // if next_timeout == 0 then wait forever - //AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "timeout occured pc=%d"), zigbee.pc); if (!zigbee.state_no_timeout) { AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "timeout, goto label %d"), zigbee.on_timeout_goto); ZigbeeGotoLabel(zigbee.on_timeout_goto); @@ -505,7 +500,6 @@ void ZigbeeStateMachine_Run(void) { zigbee.recv_until = false; zigbee.state_no_timeout = false; // reset the no_timeout for next instruction -// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeStateMachine_Run PC = %d, Mem1 = %d"), zigbee.pc, ESP.getFreeHeap()); if (zigbee.pc > (sizeof(zb_prog)/sizeof(zb_prog[0]))) { AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Invalid pc: %d, aborting"), zigbee.pc); zigbee.pc = -1; @@ -516,7 +510,6 @@ void ZigbeeStateMachine_Run(void) { } // load current instruction details - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Executing instruction pc=%d"), zigbee.pc); const Zigbee_Instruction *cur_instr_line = &zb_prog[zigbee.pc]; cur_instr = pgm_read_byte(&cur_instr_line->i.i); cur_d8 = pgm_read_byte(&cur_instr_line->i.d8); @@ -553,7 +546,6 @@ void ZigbeeStateMachine_Run(void) { case ZGB_INSTR_WAIT_FOREVER: zigbee.next_timeout = 0; zigbee.state_waiting = true; - //zigbee.state_no_timeout = true; // do not generate a timeout error when waiting is done break; case ZGB_INSTR_STOP: zigbee.state_machine = false; @@ -617,4 +609,82 @@ void ZigbeeStateMachine_Run(void) { } } +// +// Process a bytes buffer and call any callback that matches the received message +// +int32_t ZigbeeProcessInput(class SBuffer &buf) { + if (!zigbee.state_machine) { return -1; } // if state machine is stopped, send 'ignore' message + + // apply the receive filter, acts as 'startsWith()' + bool recv_filter_match = true; + bool recv_prefix_match = false; // do the first 2 bytes match the response + if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) { + if (zigbee.recv_filter_len >= 2) { + recv_prefix_match = false; + if ( (pgm_read_byte(&zigbee.recv_filter[0]) == buf.get8(0)) && + (pgm_read_byte(&zigbee.recv_filter[1]) == buf.get8(1)) ) { + recv_prefix_match = true; + } + } + + for (uint32_t i = 0; i < zigbee.recv_filter_len; i++) { + if (pgm_read_byte(&zigbee.recv_filter[i]) != buf.get8(i)) { + recv_filter_match = false; + break; + } + } + } + + // if there is a recv_callback, call it now + int32_t res = -1; // default to ok + // res = 0 - proceed to next state + // res > 0 - proceed to the specified state + // res = -1 - silently ignore the message + // res <= -2 - move to error state + // pre-compute the suggested value + if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) { + if (!recv_prefix_match) { + res = -1; // ignore + } else { // recv_prefix_match + if (recv_filter_match) { + res = 0; // ok + } else { + if (zigbee.recv_until) { + res = -1; // ignore until full match + } else { + res = -2; // error, because message is expected but wrong value + } + } + } + } else { // we don't have any filter, ignore message by default + res = -1; + } + + if (recv_prefix_match) { + if (zigbee.recv_func) { + res = (*zigbee.recv_func)(res, buf); + } + } + if (-1 == res) { + // if frame was ignored up to now + if (zigbee.recv_unexpected) { + res = (*zigbee.recv_unexpected)(res, buf); + } + } + + // change state accordingly + if (0 == res) { + // if ok, continue execution + zigbee.state_waiting = false; + } else if (res > 0) { + ZigbeeGotoLabel(res); // if >0 then go to specified label + } else if (-1 == res) { + // -1 means ignore message + // just do nothing + } else { + // any other negative value means error + ZigbeeGotoLabel(zigbee.on_error_goto); + } +} + #endif // USE_ZIGBEE diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index e9c4b6368..b10c4be05 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -19,6 +19,13 @@ #ifdef USE_ZIGBEE +/*********************************************************************************************\ + * Parsers for incoming ZNP messages +\*********************************************************************************************/ + +// +// Handle a "Receive Device Info" incoming message +// int32_t Z_ReceiveDeviceInfo(int32_t res, class SBuffer &buf) { // Ex= 6700.00.6263151D004B1200.0000.07.09.02.83869991 // IEEE Adr (8 bytes) = 0x00124B001D156362 @@ -76,12 +83,12 @@ int32_t Z_CheckNVWrite(int32_t res, class SBuffer &buf) { } } -const char Z_RebootReason[] PROGMEM = "Power-up|External|Watchdog"; - int32_t Z_Reboot(int32_t res, class SBuffer &buf) { // print information about the reboot of device // 4180.02.02.00.02.06.03 // + static const char Z_RebootReason[] PROGMEM = "Power-up|External|Watchdog"; + uint8_t reason = buf.get8(2); uint8_t transport_rev = buf.get8(3); uint8_t product_id = buf.get8(4); @@ -140,6 +147,9 @@ int32_t Z_ReceiveCheckVersion(int32_t res, class SBuffer &buf) { } } +// +// Helper function, checks if the incoming buffer matches the 2-bytes prefix, i.e. message type in PMEM +// bool Z_ReceiveMatchPrefix(const class SBuffer &buf, const uint8_t *match) { if ( (pgm_read_byte(&match[0]) == buf.get8(0)) && (pgm_read_byte(&match[1]) == buf.get8(1)) ) { @@ -149,6 +159,9 @@ bool Z_ReceiveMatchPrefix(const class SBuffer &buf, const uint8_t *match) { } } +// +// Handle Permit Join response +// int32_t Z_ReceivePermitJoinStatus(int32_t res, const class SBuffer &buf) { // we received a PermitJoin status change uint8_t duration = buf.get8(2); @@ -176,22 +189,6 @@ int32_t Z_ReceivePermitJoinStatus(int32_t res, const class SBuffer &buf) { return -1; } -// Send ZDO_IEEE_ADDR_REQ request to get IEEE long address -void Z_SendIEEEAddrReq(uint16_t shortaddr) { - uint8_t IEEEAddrReq[] = { Z_SREQ | Z_ZDO, ZDO_IEEE_ADDR_REQ, - Z_B0(shortaddr), Z_B1(shortaddr), 0x00, 0x00 }; - - ZigbeeZNPSend(IEEEAddrReq, sizeof(IEEEAddrReq)); -} - -// Send ACTIVE_EP_REQ to collect active endpoints for this address -void Z_SendActiveEpReq(uint16_t shortaddr) { - uint8_t ActiveEpReq[] = { Z_SREQ | Z_ZDO, ZDO_ACTIVE_EP_REQ, - Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) }; - - ZigbeeZNPSend(ActiveEpReq, sizeof(ActiveEpReq)); -} - const char* Z_DeviceType[] = { "Coordinator", "Router", "End Device", "Unknown" }; int32_t Z_ReceiveNodeDesc(int32_t res, const class SBuffer &buf) { // Received ZDO_NODE_DESC_RSP @@ -226,6 +223,9 @@ int32_t Z_ReceiveNodeDesc(int32_t res, const class SBuffer &buf) { return -1; } +// +// Porcess Receive Active Endpoint +// int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf) { // Received ZDO_ACTIVE_EP_RSP Z_ShortAddress srcAddr = buf.get16(2); @@ -254,37 +254,14 @@ int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf) { return -1; } -void Z_SendAFInfoRequest(uint16_t shortaddr) { - uint8_t endpoint = zigbee_devices.findFirstEndpoint(shortaddr); - if (0x00 == endpoint) { endpoint = 0x01; } // if we don't know the endpoint, try 0x01 - uint8_t transacid = zigbee_devices.getNextSeqNumber(shortaddr); - - SBuffer buf(100); - buf.add8(Z_SREQ | Z_AF); // 24 - buf.add8(AF_DATA_REQUEST); // 01 - buf.add16(shortaddr); - buf.add8(endpoint); // dest endpoint - buf.add8(0x01); // source endpoint - buf.add16(0x0000); - buf.add8(transacid); - buf.add8(0x30); // 30 options - buf.add8(0x1E); // 1E radius - - buf.add8(3 + 2*sizeof(uint16_t)); // Len = 0x07 - buf.add8(0x00); // Frame Control Field - buf.add8(transacid); // Transaction Sequence Number - buf.add8(ZCL_READ_ATTRIBUTES); // 00 Command - buf.add16(0x0004); // 0400 ManufacturerName - buf.add16(0x0005); // 0500 ModelIdentifier - - ZigbeeZNPSend(buf.getBuffer(), buf.len()); -} - +// +// Handle IEEEAddr incoming message +// int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf) { uint8_t status = buf.get8(2); Z_IEEEAddress ieeeAddr = buf.get64(3); Z_ShortAddress nwkAddr = buf.get16(11); - // uint8_t startIndex = buf.get8(13); + // uint8_t startIndex = buf.get8(13); // not used // uint8_t numAssocDev = buf.get8(14); if (0 == status) { // SUCCESS @@ -308,34 +285,6 @@ int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf) { } return -1; } - -int32_t Z_BindRsp(int32_t res, const class SBuffer &buf) { - Z_ShortAddress nwkAddr = buf.get16(2); - uint8_t status = buf.get8(4); - char status_message[32]; - - strncpy_P(status_message, (const char*) getZigbeeStatusMessage(status), sizeof(status_message)); - status_message[sizeof(status_message)-1] = 0; // truncate if needed, strlcpy is safer but strlcpy_P does not exist - - const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr); - if (friendlyName) { - Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" - ",\"" D_JSON_ZIGBEE_NAME "\":\"%s\"" - ",\"" D_JSON_ZIGBEE_STATUS "\":%d" - ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" - "}}"), nwkAddr, friendlyName, status, status_message); - } else { - Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" - ",\"" D_JSON_ZIGBEE_STATUS "\":%d" - ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" - "}}"), nwkAddr, status, status_message); - } - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); - XdrvRulesProcess(); - - return -1; -} - // // Report any AF_DATA_CONFIRM message // Ex: {"ZbConfirm":{"Endpoint":1,"Status":0,"StatusMessage":"SUCCESS"}} @@ -343,17 +292,13 @@ int32_t Z_BindRsp(int32_t res, const class SBuffer &buf) { int32_t Z_DataConfirm(int32_t res, const class SBuffer &buf) { uint8_t status = buf.get8(2); uint8_t endpoint = buf.get8(3); - //uint8_t transId = buf.get8(4); - char status_message[32]; + //uint8_t transId = buf.get8(4); // unused if (status) { // only report errors - strncpy_P(status_message, (const char*) getZigbeeStatusMessage(status), sizeof(status_message)); - status_message[sizeof(status_message)-1] = 0; // truncate if needed, strlcpy is safer but strlcpy_P does not exist - Response_P(PSTR("{\"" D_JSON_ZIGBEE_CONFIRM "\":{\"" D_CMND_ZIGBEE_ENDPOINT "\":%d" ",\"" D_JSON_ZIGBEE_STATUS "\":%d" ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" - "}}"), endpoint, status, status_message); + "}}"), endpoint, status, getZigbeeStatusMessage(status).c_str()); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); XdrvRulesProcess(); } @@ -361,6 +306,10 @@ int32_t Z_DataConfirm(int32_t res, const class SBuffer &buf) { return -1; } +// +// Handle Receive End Device Announce incoming message +// Send back Active Ep Req message +// int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) { Z_ShortAddress srcAddr = buf.get16(2); Z_ShortAddress nwkAddr = buf.get16(4); @@ -386,7 +335,10 @@ int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) { return -1; } +// +// Handle Receive TC Dev Ind incoming message // 45CA +// int32_t Z_ReceiveTCDevInd(int32_t res, const class SBuffer &buf) { Z_ShortAddress srcAddr = buf.get16(2); Z_IEEEAddress ieeeAddr = buf.get64(4); @@ -404,15 +356,82 @@ int32_t Z_ReceiveTCDevInd(int32_t res, const class SBuffer &buf) { MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); XdrvRulesProcess(); - //Z_SendActiveEpReq(srcAddr); return -1; } +// +// Handle Bind Rsp incoming message +// +int32_t Z_BindRsp(int32_t res, const class SBuffer &buf) { + Z_ShortAddress nwkAddr = buf.get16(2); + uint8_t status = buf.get8(4); + + const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr); + if (friendlyName) { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" + ",\"" D_JSON_ZIGBEE_NAME "\":\"%s\"" + ",\"" D_JSON_ZIGBEE_STATUS "\":%d" + ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" + "}}"), nwkAddr, friendlyName, status, getZigbeeStatusMessage(status).c_str()); + } else { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" + ",\"" D_JSON_ZIGBEE_STATUS "\":%d" + ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" + "}}"), nwkAddr, status, getZigbeeStatusMessage(status).c_str()); + } + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + + return -1; +} + +/*********************************************************************************************\ + * Send specific ZNP messages +\*********************************************************************************************/ + +// +// Send ZDO_IEEE_ADDR_REQ request to get IEEE long address +// +void Z_SendIEEEAddrReq(uint16_t shortaddr) { + uint8_t IEEEAddrReq[] = { Z_SREQ | Z_ZDO, ZDO_IEEE_ADDR_REQ, Z_B0(shortaddr), Z_B1(shortaddr), 0x00, 0x00 }; + + ZigbeeZNPSend(IEEEAddrReq, sizeof(IEEEAddrReq)); +} + +// +// Send ACTIVE_EP_REQ to collect active endpoints for this address +// +void Z_SendActiveEpReq(uint16_t shortaddr) { + uint8_t ActiveEpReq[] = { Z_SREQ | Z_ZDO, ZDO_ACTIVE_EP_REQ, Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) }; + + ZigbeeZNPSend(ActiveEpReq, sizeof(ActiveEpReq)); +} + +// +// Send AF Info Request +// +void Z_SendAFInfoRequest(uint16_t shortaddr) { + uint8_t endpoint = zigbee_devices.findFirstEndpoint(shortaddr); + if (0x00 == endpoint) { endpoint = 0x01; } // if we don't know the endpoint, try 0x01 + uint8_t transacid = zigbee_devices.getNextSeqNumber(shortaddr); + + uint8_t AFInfoReq[] = { Z_SREQ | Z_AF, AF_DATA_REQUEST, Z_B0(shortaddr), Z_B1(shortaddr), endpoint, + 0x01, 0x00, 0x00, transacid, 0x30, 0x1E, 3 + 2*sizeof(uint16_t), + 0x00, transacid, ZCL_READ_ATTRIBUTES, 0x04, 0x00, 0x05, 0x00 + }; + ZigbeeZNPSend(AFInfoReq, sizeof(AFInfoReq)); +} + + +/*********************************************************************************************\ + * Callbacks +\*********************************************************************************************/ + + // Aqara Occupancy behavior: the Aqara device only sends Occupancy: true events every 60 seconds. // Here we add a timer so if we don't receive a Occupancy event for 90 seconds, we send Occupancy:false -const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; // 90 s - void Z_AqaraOccupancy(uint16_t shortaddr, uint16_t cluster, uint8_t endpoint, const JsonObject *json) { + static const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; // 90 s // Read OCCUPANCY value if any const JsonVariant &val_endpoint = getCaseInsensitive(*json, PSTR(OCCUPANCY)); if (nullptr != &val_endpoint) { @@ -436,6 +455,10 @@ int32_t Z_PublishAttributes(uint16_t shortaddr, uint16_t groupaddr, uint16_t clu return 1; } +/*********************************************************************************************\ + * Global dispatcher for incoming messages +\*********************************************************************************************/ + int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) { uint16_t groupid = buf.get16(2); uint16_t clusterid = buf.get16(4); @@ -506,22 +529,24 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) { return -1; } +// Structure for the Dispatcher callbacks table typedef struct Z_Dispatcher { const uint8_t* match; ZB_RecvMsgFunc func; } Z_Dispatcher; -// Filters for ZCL frames -ZBM(AREQ_AF_DATA_CONFIRM, Z_AREQ | Z_AF, AF_DATA_CONFIRM) // 4480 -ZBM(AREQ_AF_INCOMING_MESSAGE, Z_AREQ | Z_AF, AF_INCOMING_MSG) // 4481 -ZBM(AREQ_END_DEVICE_ANNCE_IND, Z_AREQ | Z_ZDO, ZDO_END_DEVICE_ANNCE_IND) // 45C1 -ZBM(AREQ_END_DEVICE_TC_DEV_IND, Z_AREQ | Z_ZDO, ZDO_TC_DEV_IND) // 45CA -ZBM(AREQ_PERMITJOIN_OPEN_XX, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND ) // 45CB -ZBM(AREQ_ZDO_ACTIVEEPRSP, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP) // 4585 -ZBM(AREQ_ZDO_SIMPLEDESCRSP, Z_AREQ | Z_ZDO, ZDO_SIMPLE_DESC_RSP) // 4584 -ZBM(AREQ_ZDO_IEEE_ADDR_RSP, Z_AREQ | Z_ZDO, ZDO_IEEE_ADDR_RSP) // 4581 -ZBM(AREQ_ZDO_BIND_RSP, Z_AREQ | Z_ZDO, ZDO_BIND_RSP) // 45A1 +// Ffilters based on ZNP frames +ZBM(AREQ_AF_DATA_CONFIRM, Z_AREQ | Z_AF, AF_DATA_CONFIRM) // 4480 +ZBM(AREQ_AF_INCOMING_MESSAGE, Z_AREQ | Z_AF, AF_INCOMING_MSG) // 4481 +ZBM(AREQ_END_DEVICE_ANNCE_IND, Z_AREQ | Z_ZDO, ZDO_END_DEVICE_ANNCE_IND) // 45C1 +ZBM(AREQ_END_DEVICE_TC_DEV_IND, Z_AREQ | Z_ZDO, ZDO_TC_DEV_IND) // 45CA +ZBM(AREQ_PERMITJOIN_OPEN_XX, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND ) // 45CB +ZBM(AREQ_ZDO_ACTIVEEPRSP, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP) // 4585 +ZBM(AREQ_ZDO_SIMPLEDESCRSP, Z_AREQ | Z_ZDO, ZDO_SIMPLE_DESC_RSP) // 4584 +ZBM(AREQ_ZDO_IEEE_ADDR_RSP, Z_AREQ | Z_ZDO, ZDO_IEEE_ADDR_RSP) // 4581 +ZBM(AREQ_ZDO_BIND_RSP, Z_AREQ | Z_ZDO, ZDO_BIND_RSP) // 45A1 +// Dispatcher callbacks table const Z_Dispatcher Z_DispatchTable[] PROGMEM = { { AREQ_AF_DATA_CONFIRM, &Z_DataConfirm }, { AREQ_AF_INCOMING_MESSAGE, &Z_ReceiveAfIncomingMessage }, @@ -534,6 +559,10 @@ const Z_Dispatcher Z_DispatchTable[] PROGMEM = { { AREQ_ZDO_BIND_RSP, &Z_BindRsp }, }; +/*********************************************************************************************\ + * Default resolver +\*********************************************************************************************/ + int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf) { // Default message handler for new messages if (zigbee.init_phase) { @@ -549,12 +578,22 @@ int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf) { } } +/*********************************************************************************************\ + * Functions called by State Machine +\*********************************************************************************************/ + +// +// Callback for loading Zigbee configuration from Flash, called by the state machine +// int32_t Z_Load_Devices(uint8_t value) { // try to hidrate from known devices loadZigbeeDevices(); return 0; // continue } +// +// Send messages to query the state of each Hue emulated light +// int32_t Z_Query_Bulbs(uint8_t value) { // Scan all devices and send deferred requests to know the state of bulbs uint32_t wait_ms = 1000; // start with 1.0 s delay @@ -588,6 +627,9 @@ int32_t Z_Query_Bulbs(uint8_t value) { return 0; // continue } +// +// Zigbee initialization is complete, let the party begin +// int32_t Z_State_Ready(uint8_t value) { zigbee.init_phase = false; // initialization phase complete return 0; // continue diff --git a/tasmota/xdrv_23_zigbee_9_impl.ino b/tasmota/xdrv_23_zigbee_9_impl.ino index 12c9ff98f..49f89c9c7 100644 --- a/tasmota/xdrv_23_zigbee_9_impl.ino +++ b/tasmota/xdrv_23_zigbee_9_impl.ino @@ -47,85 +47,10 @@ void (* const ZigbeeCommand[])(void) PROGMEM = { &CmndZbLight, CmndZbRestore, }; -int32_t ZigbeeProcessInput(class SBuffer &buf) { - if (!zigbee.state_machine) { return -1; } // if state machine is stopped, send 'ignore' message - - // apply the receive filter, acts as 'startsWith()' - bool recv_filter_match = true; - bool recv_prefix_match = false; // do the first 2 bytes match the response - if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) { - if (zigbee.recv_filter_len >= 2) { - recv_prefix_match = false; - if ( (pgm_read_byte(&zigbee.recv_filter[0]) == buf.get8(0)) && - (pgm_read_byte(&zigbee.recv_filter[1]) == buf.get8(1)) ) { - recv_prefix_match = true; - } - } - - for (uint32_t i = 0; i < zigbee.recv_filter_len; i++) { - if (pgm_read_byte(&zigbee.recv_filter[i]) != buf.get8(i)) { - recv_filter_match = false; - break; - } - } - - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "ZbProcessInput: recv_prefix_match = %d, recv_filter_match = %d"), recv_prefix_match, recv_filter_match); - } - - // if there is a recv_callback, call it now - int32_t res = -1; // default to ok - // res = 0 - proceed to next state - // res > 0 - proceed to the specified state - // res = -1 - silently ignore the message - // res <= -2 - move to error state - // pre-compute the suggested value - if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) { - if (!recv_prefix_match) { - res = -1; // ignore - } else { // recv_prefix_match - if (recv_filter_match) { - res = 0; // ok - } else { - if (zigbee.recv_until) { - res = -1; // ignore until full match - } else { - res = -2; // error, because message is expected but wrong value - } - } - } - } else { // we don't have any filter, ignore message by default - res = -1; - } - - if (recv_prefix_match) { - if (zigbee.recv_func) { - res = (*zigbee.recv_func)(res, buf); - } - } - if (-1 == res) { - // if frame was ignored up to now - if (zigbee.recv_unexpected) { - res = (*zigbee.recv_unexpected)(res, buf); - } - } - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "ZbProcessInput: res = %d"), res); - - // change state accordingly - if (0 == res) { - // if ok, continue execution - zigbee.state_waiting = false; - } else if (res > 0) { - ZigbeeGotoLabel(res); // if >0 then go to specified label - } else if (-1 == res) { - // -1 means ignore message - // just do nothing - } else { - // any other negative value means error - ZigbeeGotoLabel(zigbee.on_error_goto); - } -} - -void ZigbeeInput(void) +// +// Called at event loop, checks for incoming data from the CC2530 +// +void ZigbeeInputLoop(void) { static uint32_t zigbee_polling_window = 0; static uint8_t fcs = ZIGBEE_SOF; @@ -217,6 +142,7 @@ void ZigbeeInput(void) /********************************************************************************************/ +// Initialize internal structures void ZigbeeInit(void) { // AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem1 = %d"), ESP.getFreeHeap()); @@ -260,10 +186,10 @@ uint32_t strToUInt(const JsonVariant &val) { return 0; // couldn't parse anything } +// Do a factory reset of the CC2530 const unsigned char ZIGBEE_FACTORY_RESET[] PROGMEM = { Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 /* len */, 0x01 /* STARTOPT_CLEAR_CONFIG */}; //"2605030101"; // Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 len, 0x01 STARTOPT_CLEAR_CONFIG -// Do a factory reset of the CC2530 void CmndZbReset(void) { if (ZigbeeSerial) { switch (XdrvMailbox.payload) { @@ -279,6 +205,10 @@ void CmndZbReset(void) { } } +// +// Same code for `ZbZNPSend` and `ZbZNPReceive` +// building the complete message (intro, length) +// void CmndZbZNPSendOrReceive(bool send) { if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) { @@ -298,8 +228,10 @@ void CmndZbZNPSendOrReceive(bool send) codes += 2; } if (send) { + // Command was `ZbZNPSend` ZigbeeZNPSend(buf.getBuffer(), buf.len()); } else { + // Command was `ZbZNPReceive` ZigbeeProcessInput(buf); } } @@ -347,6 +279,21 @@ void ZigbeeZNPSend(const uint8_t *msg, size_t len) { ToHex_P(msg, len, hex_char, sizeof(hex_char))); } +// +// Internal function, send the low-level frame +// Input: +// - shortaddr: 16-bits short address, or 0x0000 if group address +// - groupaddr: 16-bits group address, or 0x0000 if unicast using shortaddr +// - clusterIf: 16-bits cluster number +// - endpoint: 8-bits target endpoint (source is always 0x01), unused for group addresses. Should not be 0x00 except when sending to group address. +// - cmdId: 8-bits ZCL command number +// - clusterSpecific: boolean, is the message general cluster or cluster specific, used to create the FC byte of ZCL +// - msg: pointer to byte array, payload of ZCL message (len is following), ignored if nullptr +// - len: length of the 'msg' payload +// - needResponse: boolean, true = we ask the target to respond, false = the target should not respond +// - transacId: 8-bits, transation id of message (should be incremented at each message), used both for Zigbee message number and ZCL message number +// Returns: None +// void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId) { SBuffer buf(32+len); @@ -379,7 +326,22 @@ void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterI ZigbeeZNPSend(buf.getBuffer(), buf.len()); } -// Send a command specified as an HEX string for the workload +/********************************************************************************************/ +// +// High-level function +// Send a command specified as an HEX string for the workload. +// The target endpoint is computed if zero, i.e. sent to the first known endpoint of the device. +// If cluster-specific, a timer may be set calling `zigbeeSetCommandTimer()`, for ex to coalesce attributes or Aqara presence sensor +// +// Inputs: +// - shortaddr: 16-bits short address, or 0x0000 if group address +// - groupaddr: 16-bits group address, or 0x0000 if unicast using shortaddr +// - endpoint: 8-bits target endpoint (source is always 0x01), if 0x00, it will be guessed from ZbStatus information (basically the first endpoint of the device) +// - clusterSpecific: boolean, is the message general cluster or cluster specific, used to create the FC byte of ZCL +// - clusterIf: 16-bits cluster number +// - param: pointer to HEX string for payload, should not be nullptr +// Returns: None +// void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint, bool clusterSpecific, uint16_t cluster, uint8_t cmd, const char *param) { size_t size = param ? strlen(param) : 0; @@ -395,15 +357,15 @@ void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint, if ((0 == endpoint) && (shortaddr)) { // endpoint is not specified, let's try to find it from shortAddr, unless it's a group address endpoint = zigbee_devices.findFirstEndpoint(shortaddr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint); + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint); } AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: shortaddr 0x%04X, groupaddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"), shortaddr, groupaddr, cluster, endpoint, cmd, param); - if ((0 == endpoint) && (shortaddr)) { + if ((0 == endpoint) && (shortaddr)) { // endpoint null is ok for group address AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbSend: unspecified endpoint")); return; - } // endpoint null is ok for group address + } // everything is good, we can send the command ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, cmd, clusterSpecific, buf.getBuffer(), buf.len(), true, zigbee_devices.getNextSeqNumber(shortaddr)); @@ -413,21 +375,24 @@ void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint, } } +// +// Command `ZbSend` +// void CmndZbSend(void) { - // ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":1} } - // ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"3"} } - // ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"0xFF"} } - // ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":null} } - // ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":false} } - // ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":true} } - // ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"true"} } - // ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"ShutterClose":null} } - // ZigbeeSend { "devicse":"0x1234", "endpoint":"0x03", "send":{"Power":1} } - // ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Color":"1,2"} } - // ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Color":"0x1122,0xFFEE"} } + // ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":1} } + // ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"3"} } + // ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"0xFF"} } + // ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":null} } + // ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":false} } + // ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":true} } + // ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"true"} } + // ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"ShutterClose":null} } + // ZbSend { "devicse":"0x1234", "endpoint":"0x03", "send":{"Power":1} } + // ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Color":"1,2"} } + // ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Color":"0x1122,0xFFEE"} } if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } DynamicJsonBuffer jsonBuf; - JsonObject &json = jsonBuf.parseObject(XdrvMailbox.data); + const JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data); if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } // params @@ -463,16 +428,16 @@ void CmndZbSend(void) { // If String, it's a low level command if (val_cmd.is()) { // we have a high-level command - JsonObject &cmd_obj = val_cmd.as(); + const JsonObject &cmd_obj = val_cmd.as(); int32_t cmd_size = cmd_obj.size(); if (cmd_size > 1) { Response_P(PSTR("Only 1 command allowed (%d)"), cmd_size); return; } else if (1 == cmd_size) { // We have exactly 1 command, parse it - JsonObject::iterator it = cmd_obj.begin(); // just get the first key/value + JsonObject::const_iterator it = cmd_obj.begin(); // just get the first key/value String key = it->key; - JsonVariant& value = it->value; + const JsonVariant& value = it->value; uint32_t x = 0, y = 0, z = 0; uint16_t cmd_var; @@ -526,7 +491,7 @@ void CmndZbSend(void) { } else { // we have zero command, pass through until last error for missing command } - } else if (val_cmd.is()) { + } else if (val_cmd.is()) { // low-level command cmd_str = val_cmd.as(); // Now parse the string to extract cluster, command, and payload @@ -564,16 +529,18 @@ void CmndZbSend(void) { Response_P(PSTR("Missing zigbee 'Send'")); return; } - } +// +// Command `ZbBind` +// void CmndZbBind(void) { - // ZbBind { "device":"0x1234", "endpoint":1, "cluster":6 } + // ZbBind {"Device":"", "Endpoint":, "Cluster":, "ToDevice":"", "ToEndpoint":, "ToGroup": } // local endpoint is always 1, IEEE addresses are calculated if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } DynamicJsonBuffer jsonBuf; - JsonObject &json = jsonBuf.parseObject(XdrvMailbox.data); + const JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data); if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } // params @@ -658,6 +625,9 @@ void CmndZbProbe(void) { CmndZbProbeOrPing(true); } +// +// Common code for `ZbProbe` and `ZbPing` +// void CmndZbProbeOrPing(boolean probe) { if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); @@ -677,12 +647,15 @@ void CmndZbPing(void) { CmndZbProbeOrPing(false); } +// +// Command `ZbName` // Specify, read or erase a Friendly Name +// void CmndZbName(void) { // Syntax is: - // ZigbeeName , - assign a friendly name - // ZigbeeName - display the current friendly name - // ZigbeeName , - remove friendly name + // ZbName , - assign a friendly name + // ZbName - display the current friendly name + // ZbName , - remove friendly name // // Where can be: short_addr, long_addr, device_index, friendly_name @@ -706,12 +679,15 @@ void CmndZbName(void) { } } +// +// Command `ZbName` // Specify, read or erase a ModelId, only for debug purposes +// void CmndZbModelId(void) { // Syntax is: - // ZigbeeName , - assign a friendly name - // ZigbeeName - display the current friendly name - // ZigbeeName , - remove friendly name + // ZbName , - assign a friendly name + // ZbName - display the current friendly name + // ZbName , - remove friendly name // // Where can be: short_addr, long_addr, device_index, friendly_name @@ -735,6 +711,8 @@ void CmndZbModelId(void) { } } +// +// Command `ZbLight` // Specify, read or erase a Light type for Hue/Alexa integration void CmndZbLight(void) { // Syntax is: @@ -768,7 +746,10 @@ void CmndZbLight(void) { ResponseCmndDone(); } +// +// Command `ZbForget` // Remove an old Zigbee device from the list of known devices, use ZigbeeStatus to know all registered devices +// void CmndZbForget(void) { if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); @@ -783,12 +764,13 @@ void CmndZbForget(void) { } } +// +// Command `ZbSave` // Save Zigbee information to flash +// void CmndZbSave(void) { if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - saveZigbeeDevices(); - ResponseCmndDone(); } @@ -803,7 +785,7 @@ void CmndZbSave(void) { void CmndZbRestore(void) { if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } DynamicJsonBuffer jsonBuf; - const JsonVariant json_parsed = jsonBuf.parse((const char*)XdrvMailbox.data); // const to force a copy of parameter + const JsonVariant json_parsed = jsonBuf.parse((const char*) XdrvMailbox.data); // const to force a copy of parameter const JsonVariant * json = &json_parsed; // root of restore, to be changed if needed bool success = false; @@ -846,14 +828,17 @@ void CmndZbRestore(void) { ResponseCmndDone(); } +// +// Command `ZbRead` // Send an attribute read command to a device, specifying cluster and list of attributes +// void CmndZbRead(void) { - // ZigbeeRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":5} - // ZigbeeRead {"Device":"0xF289","Cluster":"0x0000","Endpoint":"0x0003","Attr":"0x0005"} - // ZigbeeRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":[5,6,7,4]} + // ZbRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":5} + // ZbRead {"Device":"0xF289","Cluster":"0x0000","Endpoint":"0x0003","Attr":"0x0005"} + // ZbRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":[5,6,7,4]} if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } DynamicJsonBuffer jsonBuf; - JsonObject &json = jsonBuf.parseObject(XdrvMailbox.data); + JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data); if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } // params @@ -884,7 +869,7 @@ void CmndZbRead(void) { if (nullptr != &val_attr) { uint16_t val = strToUInt(val_attr); if (val_attr.is()) { - JsonArray& attr_arr = val_attr; + const JsonArray& attr_arr = val_attr.as(); attrs_len = attr_arr.size() * 2; attrs = new uint8_t[attrs_len]; @@ -920,7 +905,10 @@ void CmndZbRead(void) { if (attrs) { delete[] attrs; } } +// +// Command `ZbPermitJoin` // Allow or Deny pairing of new Zigbee devices +// void CmndZbPermitJoin(void) { if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } uint32_t payload = XdrvMailbox.payload; @@ -946,6 +934,9 @@ void CmndZbPermitJoin(void) { ResponseCmndDone(); } +// +// Command `ZbStatus` +// void CmndZbStatus(void) { if (ZigbeeSerial) { if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } @@ -976,7 +967,7 @@ bool Xdrv23(uint8_t function) } break; case FUNC_LOOP: - if (ZigbeeSerial) { ZigbeeInput(); } + if (ZigbeeSerial) { ZigbeeInputLoop(); } if (zigbee.state_machine) { ZigbeeStateMachine_Run(); } From aede481b6129534be4636d6d13ad976274b7fb7a Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 25 Mar 2020 11:29:46 +0100 Subject: [PATCH 019/547] Move not used GPIO init up in chain --- tasmota/support_tasmota.ino | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 1caaa2779..3c4239359 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -1433,6 +1433,18 @@ void GpioInit(void) soft_spi_flg = ((pin[GPIO_SSPI_CS] < 99) && (pin[GPIO_SSPI_SCLK] < 99) && ((pin[GPIO_SSPI_MOSI] < 99) || (pin[GPIO_SSPI_MOSI] < 99))); #endif // USE_SPI + // Set any non-used GPIO to INPUT - Related to resetPins() in support_legacy_cores.ino + // Doing it here solves relay toggles at restart. + for (uint32_t i = 0; i < sizeof(my_module.io); i++) { + mpin = ValidPin(i, my_module.io[i]); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("INI: gpio pin %d, mpin %d"), i, mpin); + if (((i < 6) || (i > 11)) && (0 == mpin)) { // Skip SPI flash interface + if (!((1 == i) || (3 == i))) { // Skip serial + pinMode(i, INPUT); + } + } + } + #ifdef USE_I2C i2c_flg = ((pin[GPIO_I2C_SCL] < 99) && (pin[GPIO_I2C_SDA] < 99)); if (i2c_flg) { @@ -1518,17 +1530,6 @@ void GpioInit(void) RotaryInit(); #endif - // Set any non-used GPIO to INPUT - for (uint32_t i = 0; i < sizeof(my_module.io); i++) { - mpin = ValidPin(i, my_module.io[i]); -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("INI: gpio pin %d, mpin %d"), i, mpin); - if (((i < 6) || (i > 11)) && (0 == mpin)) { // Skip SPI flash interface - if (!((1 == i) || (3 == i))) { // Skip serial - pinMode(i, INPUT); - } - } - } - SetLedPower(Settings.ledstate &8); SetLedLink(Settings.ledstate &8); From 2165a783741c0aed165542a599709b2a5d1d0141 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Wed, 25 Mar 2020 14:03:51 +0100 Subject: [PATCH 020/547] Use latest Platformio chain 2.4.0 --- platformio_override_sample.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini index deeafc3ee..f8ff1af64 100644 --- a/platformio_override_sample.ini +++ b/platformio_override_sample.ini @@ -134,7 +134,7 @@ build_flags = ${esp82xx_defaults.build_flags} [core_2_6_3] ; *** Esp8266 core for Arduino version 2.6.3 -platform = espressif8266@2.3.3 +platform = espressif8266@2.4.0 platform_packages = build_flags = ${esp82xx_defaults.build_flags} -DBEARSSL_SSL_BASIC @@ -178,7 +178,7 @@ build_flags = ${esp82xx_defaults.build_flags} [tasmota_feature_stage] ; *** Esp8266 core for Arduino version Tasmota feature stage -platform = espressif8266@2.3.3 +platform = espressif8266@2.4.0 platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#e64cb619f79a3c888dd56b957d8f48ce74f35735 build_flags = ${esp82xx_defaults.build_flags} -DBEARSSL_SSL_BASIC @@ -222,7 +222,7 @@ build_flags = ${esp82xx_defaults.build_flags} [core_stage] ; *** Esp8266 core for Arduino version latest beta -platform = espressif8266@2.3.3 +platform = espressif8266@2.4.0 platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git build_flags = ${esp82xx_defaults.build_flags} -DBEARSSL_SSL_BASIC From 308b145514012d9b100ce6ed4b87bc37c9fb85ae Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Wed, 25 Mar 2020 14:05:31 +0100 Subject: [PATCH 021/547] Update platformio.ini --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 9e0c28e9a..2c78a4583 100755 --- a/platformio.ini +++ b/platformio.ini @@ -108,7 +108,7 @@ build_flags = ${tasmota_core_stage.build_flags} [tasmota_core_stage] ; *** Esp8266 core for Arduino version stable beta -platform = espressif8266@2.3.3 +platform = espressif8266@2.4.0 platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#372a3ec297dfe8501bed1ec4552244695b5e8ced build_flags = ${esp82xx_defaults.build_flags} -DBEARSSL_SSL_BASIC From bec81f50895af870369648d22c9e3a3393b13a35 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Wed, 25 Mar 2020 20:36:57 +0100 Subject: [PATCH 022/547] Zigbee fix incorrect Hue value --- tasmota/xdrv_20_hue.ino | 8 ++++---- tasmota/xdrv_23_zigbee_3_hue.ino | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tasmota/xdrv_20_hue.ino b/tasmota/xdrv_20_hue.ino index 95aef13e4..c816eed02 100644 --- a/tasmota/xdrv_20_hue.ino +++ b/tasmota/xdrv_20_hue.ino @@ -298,7 +298,7 @@ void HueLightStatus1(uint8_t device, String *response) prev_x_str[0] = prev_y_str[0] = 0; } - hue = changeUIntScale(hue, 0, 359, 0, 65535); + hue = changeUIntScale(hue, 0, 360, 0, 65535); if ((hue > prev_hue ? hue - prev_hue : prev_hue - hue) < 400) { hue = prev_hue; } else { // if hue was changed outside of Alexa, reset xy @@ -598,7 +598,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { uint8_t rr,gg,bb; LightStateClass::XyToRgb(x, y, &rr, &gg, &bb); LightStateClass::RgbToHsb(rr, gg, bb, &hue, &sat, nullptr); - prev_hue = changeUIntScale(hue, 0, 359, 0, 65535); // calculate back prev_hue + prev_hue = changeUIntScale(hue, 0, 360, 0, 65535); // calculate back prev_hue prev_sat = (sat > 254 ? 254 : sat); //AddLog_P2(LOG_LEVEL_DEBUG_MORE, "XY RGB (%d %d %d) HS (%d %d)", rr,gg,bb,hue,sat); if (resp) { response += ","; } @@ -619,8 +619,8 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { device_id, "hue", hue); response += buf; if (LST_RGB <= Light.subtype) { - // change range from 0..65535 to 0..359 - hue = changeUIntScale(hue, 0, 65535, 0, 359); + // change range from 0..65535 to 0..360 + hue = changeUIntScale(hue, 0, 65535, 0, 360); g_gotct = false; change = true; } diff --git a/tasmota/xdrv_23_zigbee_3_hue.ino b/tasmota/xdrv_23_zigbee_3_hue.ino index c19216842..d1bc5b731 100644 --- a/tasmota/xdrv_23_zigbee_3_hue.ino +++ b/tasmota/xdrv_23_zigbee_3_hue.ino @@ -32,10 +32,10 @@ void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, Stri zigbee_devices.getHueState(shortaddr, &power, &colormode, &bri, &sat, &ct, &hue, &x, &y); - if (bri > 254) bri = 254; // Philips Hue bri is between 1 and 254 - if (bri < 1) bri = 1; - if (sat > 254) sat = 254; // Philips Hue only accepts 254 as max hue - uint8_t hue8 = changeUIntScale(hue, 0, 360, 0, 254); // default hue is 0..254, we don't use extended hue + if (bri > 254) bri = 254; // Philips Hue bri is between 1 and 254 + if (bri < 1) bri = 1; + if (sat > 254) sat = 254; // Philips Hue only accepts 254 as max hue + uint16_t hue16 = changeUIntScale(hue, 0, 360, 0, 65535); const size_t buf_size = 256; char * buf = (char*) malloc(buf_size); // temp buffer for strings, avoid stack @@ -56,7 +56,7 @@ void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, Stri float y_f = y / 65536.0f; snprintf_P(buf, buf_size, PSTR("%s\"xy\":[%s,%s],"), buf, String(x, 5).c_str(), String(y, 5).c_str()); } - snprintf_P(buf, buf_size, PSTR("%s\"hue\":%d,\"sat\":%d,"), buf, hue, sat); + snprintf_P(buf, buf_size, PSTR("%s\"hue\":%d,\"sat\":%d,"), buf, hue16, sat); } if (LST_COLDWARM == local_light_subtype || LST_RGBW <= local_light_subtype) { // white temp snprintf_P(buf, buf_size, PSTR("%s\"ct\":%d,"), buf, ct > 0 ? ct : 284); @@ -164,7 +164,7 @@ void ZigbeeHueHS(uint16_t shortaddr, uint16_t hue, uint8_t sat) { char param[16]; uint8_t hue8 = changeUIntScale(hue, 0, 360, 0, 254); if (sat > 0xFE) { sat = 0xFE; } - snprintf_P(param, sizeof(param), PSTR("%02X%02X0A00"), hue8, sat); + snprintf_P(param, sizeof(param), PSTR("%02X%02X0000"), hue8, sat); uint8_t colormode = 0; // "hs" zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0300, 0x06, param); zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, &sat, nullptr, &hue, nullptr, nullptr); @@ -250,8 +250,8 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) { device_id, "hue", hue); response += buf; if (LST_RGB <= bulbtype) { - // change range from 0..65535 to 0..359 - hue = changeUIntScale(hue, 0, 65535, 0, 359); + // change range from 0..65535 to 0..360 + hue = changeUIntScale(hue, 0, 65535, 0, 360); huesat_changed = true; } resp = true; From d4a9ed41c9f18da6e6d3ecc833dcc3ba9402b4e1 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Thu, 26 Mar 2020 19:34:59 +0100 Subject: [PATCH 023/547] Add support for unreachable (unplugged) Zigbee devices in Philips Hue emulation and Alexa --- tasmota/CHANGELOG.md | 1 + tasmota/xdrv_23_zigbee_2_devices.ino | 43 ++++++++++++++------ tasmota/xdrv_23_zigbee_3_hue.ino | 28 ++++++++----- tasmota/xdrv_23_zigbee_5_converters.ino | 18 ++++----- tasmota/xdrv_23_zigbee_6_commands.ino | 15 +++++++ tasmota/xdrv_23_zigbee_8_parsers.ino | 54 ++++++++++++++----------- 6 files changed, 102 insertions(+), 57 deletions(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 12f581ce8..0977c85c7 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -6,6 +6,7 @@ - Change GPIO initialization solving possible Relay toggle on (OTA) restart - Fix Zigbee sending wrong Sat value with Hue emulation - Add command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` +- Add support for unreachable (unplugged) Zigbee devices in Philips Hue emulation and Alexa ## Released diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino index e4c6f6df0..d8cb1d5bc 100644 --- a/tasmota/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/xdrv_23_zigbee_2_devices.ino @@ -46,7 +46,7 @@ typedef struct Z_Device { uint8_t seqNumber; // Light information for Hue integration integration, last known values int8_t bulbtype; // number of channel for the bulb: 0-5, or 0xFF if no Hue integration - uint8_t power; // power state (boolean) + uint8_t power; // power state (boolean), MSB (0x80) stands for reachable uint8_t colormode; // 0x00: Hue/Sat, 0x01: XY, 0x02: CT uint8_t dimmer; // last Dimmer value: 0-254 uint8_t sat; // last Sat: 0..254 @@ -66,12 +66,15 @@ typedef enum Z_Def_Category { Z_CAT_NONE = 0, // no category, it will happen anyways Z_CAT_READ_ATTR, // Attribute reporting, either READ_ATTRIBUTE or REPORT_ATTRIBUTE, we coalesce all attributes reported if we can Z_CAT_VIRTUAL_ATTR, // Creation of a virtual attribute, typically after a time-out. Ex: Aqara presence sensor + Z_CAT_REACHABILITY, // timer set to measure reachability of device, i.e. if we don't get an answer after 1s, it is marked as unreachable (for Alexa) Z_CAT_READ_0006, // Read 0x0006 cluster Z_CAT_READ_0008, // Read 0x0008 cluster Z_CAT_READ_0102, // Read 0x0300 cluster Z_CAT_READ_0300, // Read 0x0300 cluster } Z_Def_Category; +const uint32_t Z_CAT_REACHABILITY_TIMEOUT = 1000; // 1000 ms or 1s + typedef struct Z_Deferred { // below are per device timers, used for example to query the new state of the device uint32_t timer; // millis() when to fire the timer, 0 if no timer @@ -124,6 +127,7 @@ public: void setFriendlyName(uint16_t shortaddr, const char * str); const char * getFriendlyName(uint16_t shortaddr) const; const char * getModelId(uint16_t shortaddr) const; + void setReachable(uint16_t shortaddr, bool reachable); // get next sequence number for (increment at each all) uint8_t getNextSeqNumber(uint16_t shortaddr); @@ -137,15 +141,17 @@ public: void setHueBulbtype(uint16_t shortaddr, int8_t bulbtype); int8_t getHueBulbtype(uint16_t shortaddr) const ; void updateHueState(uint16_t shortaddr, - const uint8_t *power, const uint8_t *colormode, + const bool *power, const uint8_t *colormode, const uint8_t *dimmer, const uint8_t *sat, const uint16_t *ct, const uint16_t *hue, - const uint16_t *x, const uint16_t *y); + const uint16_t *x, const uint16_t *y, + const bool *reachable); bool getHueState(uint16_t shortaddr, - uint8_t *power, uint8_t *colormode, + bool *power, uint8_t *colormode, uint8_t *dimmer, uint8_t *sat, uint16_t *ct, uint16_t *hue, - uint16_t *x, uint16_t *y) const ; + uint16_t *x, uint16_t *y, + bool *reachable) const ; // Timers void resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uint8_t category); @@ -262,7 +268,7 @@ Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) { 0, // seqNumber // Hue support -1, // no Hue support - 0, // power + 0x80, // power off + reachable 0, // colormode 0, // dimmer 0, // sat @@ -583,6 +589,12 @@ const char * Z_Devices::getModelId(uint16_t shortaddr) const { return nullptr; } +void Z_Devices::setReachable(uint16_t shortaddr, bool reachable) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } // don't crash if not found + bitWrite(device.power, 7, reachable); +} + // get the next sequance number for the device, or use the global seq number if device is unknown uint8_t Z_Devices::getNextSeqNumber(uint16_t shortaddr) { int32_t short_found = findShortAddr(shortaddr); @@ -616,12 +628,13 @@ int8_t Z_Devices::getHueBulbtype(uint16_t shortaddr) const { // Hue support void Z_Devices::updateHueState(uint16_t shortaddr, - const uint8_t *power, const uint8_t *colormode, + const bool *power, const uint8_t *colormode, const uint8_t *dimmer, const uint8_t *sat, const uint16_t *ct, const uint16_t *hue, - const uint16_t *x, const uint16_t *y) { + const uint16_t *x, const uint16_t *y, + const bool *reachable) { Z_Device &device = getShortAddr(shortaddr); - if (power) { device.power = *power; } + if (power) { bitWrite(device.power, 0, *power); } if (colormode){ device.colormode = *colormode; } if (dimmer) { device.dimmer = *dimmer; } if (sat) { device.sat = *sat; } @@ -629,18 +642,20 @@ void Z_Devices::updateHueState(uint16_t shortaddr, if (hue) { device.hue = *hue; } if (x) { device.x = *x; } if (y) { device.y = *y; } + if (reachable){ bitWrite(device.power, 7, *reachable); } } // return true if ok bool Z_Devices::getHueState(uint16_t shortaddr, - uint8_t *power, uint8_t *colormode, + bool *power, uint8_t *colormode, uint8_t *dimmer, uint8_t *sat, uint16_t *ct, uint16_t *hue, - uint16_t *x, uint16_t *y) const { + uint16_t *x, uint16_t *y, + bool *reachable) const { int32_t found = findShortAddr(shortaddr); if (found >= 0) { const Z_Device &device = *(_devices[found]); - if (power) { *power = device.power; } + if (power) { *power = bitRead(device.power, 0); } if (colormode){ *colormode = device.colormode; } if (dimmer) { *dimmer = device.dimmer; } if (sat) { *sat = device.sat; } @@ -648,6 +663,7 @@ bool Z_Devices::getHueState(uint16_t shortaddr, if (hue) { *hue = device.hue; } if (x) { *x = device.x; } if (y) { *y = device.y; } + if (reachable){ *reachable = bitRead(device.power, 7); } return true; } else { return false; @@ -951,7 +967,8 @@ String Z_Devices::dumpLightState(uint16_t shortaddr) const { dev[F(D_JSON_ZIGBEE_LIGHT)] = device.bulbtype; // sign extend, 0xFF changed as -1 if (0 <= device.bulbtype) { // bulbtype is defined - dev[F("Power")] = device.power; + dev[F("Power")] = bitRead(device.power, 0); + dev[F("Reachable")] = bitRead(device.power, 7); if (1 <= device.bulbtype) { dev[F("Dimmer")] = device.dimmer; } diff --git a/tasmota/xdrv_23_zigbee_3_hue.ino b/tasmota/xdrv_23_zigbee_3_hue.ino index d1bc5b731..ffbec109c 100644 --- a/tasmota/xdrv_23_zigbee_3_hue.ino +++ b/tasmota/xdrv_23_zigbee_3_hue.ino @@ -24,13 +24,19 @@ // idx: index in the list of zigbee_devices void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, String *response) { - uint8_t power, colormode, bri, sat; + static const char HUE_LIGHTS_STATUS_JSON1_SUFFIX_ZIGBEE[] PROGMEM = + "%s\"alert\":\"none\"," + "\"effect\":\"none\"," + "\"reachable\":%s}"; + + bool power, reachable; + uint8_t colormode, bri, sat; uint16_t ct, hue; uint16_t x, y; String light_status = ""; uint32_t echo_gen = findEchoGeneration(); // 1 for 1st gen =+ Echo Dot 2nd gen, 2 for 2nd gen and above - zigbee_devices.getHueState(shortaddr, &power, &colormode, &bri, &sat, &ct, &hue, &x, &y); + zigbee_devices.getHueState(shortaddr, &power, &colormode, &bri, &sat, &ct, &hue, &x, &y, &reachable); if (bri > 254) bri = 254; // Philips Hue bri is between 1 and 254 if (bri < 1) bri = 1; @@ -40,7 +46,7 @@ void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, Stri const size_t buf_size = 256; char * buf = (char*) malloc(buf_size); // temp buffer for strings, avoid stack - snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), (power & 1) ? "true" : "false"); + snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), power ? "true" : "false"); // Brightness for all devices with PWM if ((1 == echo_gen) || (LST_SINGLE <= local_light_subtype)) { // force dimmer for 1st gen Echo snprintf_P(buf, buf_size, PSTR("%s\"bri\":%d,"), buf, bri); @@ -61,7 +67,7 @@ void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, Stri if (LST_COLDWARM == local_light_subtype || LST_RGBW <= local_light_subtype) { // white temp snprintf_P(buf, buf_size, PSTR("%s\"ct\":%d,"), buf, ct > 0 ? ct : 284); } - snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON1_SUFFIX, buf); + snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON1_SUFFIX_ZIGBEE, buf, reachable ? "true" : "false"); *response += buf; free(buf); @@ -123,9 +129,9 @@ void ZigbeeHueGroups(String * lights) { // Send commands // Power On/Off -void ZigbeeHuePower(uint16_t shortaddr, uint8_t power) { - zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0006, power, ""); - zigbee_devices.updateHueState(shortaddr, &power, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); +void ZigbeeHuePower(uint16_t shortaddr, bool power) { + zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0006, power ? 1 : 0, ""); + zigbee_devices.updateHueState(shortaddr, &power, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); } // Dimmer @@ -134,7 +140,7 @@ void ZigbeeHueDimmer(uint16_t shortaddr, uint8_t dimmer) { char param[8]; snprintf_P(param, sizeof(param), PSTR("%02X0A00"), dimmer); zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0008, 0x04, param); - zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, &dimmer, nullptr, nullptr, nullptr, nullptr, nullptr); + zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, &dimmer, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); } // CT @@ -145,7 +151,7 @@ void ZigbeeHueCT(uint16_t shortaddr, uint16_t ct) { snprintf_P(param, sizeof(param), PSTR("%02X%02X0A00"), ct & 0xFF, ct >> 8); uint8_t colormode = 2; // "ct" zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0300, 0x0A, param); - zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, nullptr, &ct, nullptr, nullptr, nullptr); + zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, nullptr, &ct, nullptr, nullptr, nullptr, nullptr); } // XY @@ -156,7 +162,7 @@ void ZigbeeHueXY(uint16_t shortaddr, uint16_t x, uint16_t y) { snprintf_P(param, sizeof(param), PSTR("%02X%02X%02X%02X0A00"), x & 0xFF, x >> 8, y & 0xFF, y >> 8); uint8_t colormode = 1; // "xy" zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0300, 0x07, param); - zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, nullptr, nullptr, nullptr, &x, &y); + zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, nullptr, nullptr, nullptr, &x, &y, nullptr); } // HueSat @@ -167,7 +173,7 @@ void ZigbeeHueHS(uint16_t shortaddr, uint16_t hue, uint8_t sat) { snprintf_P(param, sizeof(param), PSTR("%02X%02X0000"), hue8, sat); uint8_t colormode = 0; // "hs" zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0300, 0x06, param); - zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, &sat, nullptr, &hue, nullptr, nullptr); + zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, &sat, nullptr, &hue, nullptr, nullptr, nullptr); } void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) { diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index dfe544cbd..edd5ab682 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -1215,38 +1215,38 @@ void ZCLFrame::postProcessAttributes(uint16_t shortaddr, JsonObject& json) { // see if we need to update the Hue bulb status if ((cluster == 0x0006) && ((attribute == 0x0000) || (attribute == 0x8000))) { - uint8_t power = value; + bool power = value; zigbee_devices.updateHueState(shortaddr, &power, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr); + nullptr, nullptr, nullptr, nullptr, nullptr); } else if ((cluster == 0x0008) && (attribute == 0x0000)) { uint8_t dimmer = value; zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, &dimmer, nullptr, - nullptr, nullptr, nullptr, nullptr); + nullptr, nullptr, nullptr, nullptr, nullptr); } else if ((cluster == 0x0300) && (attribute == 0x0000)) { uint16_t hue8 = value; uint16_t hue = changeUIntScale(hue8, 0, 254, 0, 360); // change range from 0..254 to 0..360 zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, nullptr, nullptr, - nullptr, &hue, nullptr, nullptr); + nullptr, &hue, nullptr, nullptr, nullptr); } else if ((cluster == 0x0300) && (attribute == 0x0001)) { uint8_t sat = value; zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, nullptr, &sat, - nullptr, nullptr, nullptr, nullptr); + nullptr, nullptr, nullptr, nullptr, nullptr); } else if ((cluster == 0x0300) && (attribute == 0x0003)) { uint16_t x = value; zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, &x, nullptr); + nullptr, nullptr, &x, nullptr, nullptr); } else if ((cluster == 0x0300) && (attribute == 0x0004)) { uint16_t y = value; zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, &y); + nullptr, nullptr, nullptr, &y, nullptr), nullptr; } else if ((cluster == 0x0300) && (attribute == 0x0007)) { uint16_t ct = value; zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, nullptr, nullptr, - &ct, nullptr, nullptr, nullptr); + &ct, nullptr, nullptr, nullptr, nullptr); } else if ((cluster == 0x0300) && (attribute == 0x0008)) { uint8_t colormode = value; zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr); + nullptr, nullptr, nullptr, nullptr, nullptr); } // Iterate on filter diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino index 4788ddf67..818f8afbe 100644 --- a/tasmota/xdrv_23_zigbee_6_commands.ino +++ b/tasmota/xdrv_23_zigbee_6_commands.ino @@ -173,6 +173,14 @@ int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t clus } } + +// This callback is registered after a an attribute read command was made to a light, and fires if we don't get any response after 1000 ms +int32_t Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { + if (shortaddr) { + zigbee_devices.setReachable(shortaddr, false); // mark device as reachable + } +} + // set a timer to read back the value in the future void zigbeeSetCommandTimer(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint) { uint32_t wait_ms = 0; @@ -192,6 +200,9 @@ void zigbeeSetCommandTimer(uint16_t shortaddr, uint16_t groupaddr, uint16_t clus } if (wait_ms) { zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback); + if (shortaddr) { // reachability test is not possible for group addresses, since we don't know the list of devices in the group + zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable); + } } } @@ -300,6 +311,10 @@ void sendHueUpdate(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uin } if ((endpoint) || (groupaddr)) { // send only if we know the endpoint zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, z_cat, 0 /* value */, &Z_ReadAttrCallback); + if (shortaddr) { // reachability test is not possible for group addresses, since we don't know the list of devices in the group + zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable); + } + } } } diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index b10c4be05..5e05d6c82 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -308,6 +308,7 @@ int32_t Z_DataConfirm(int32_t res, const class SBuffer &buf) { // // Handle Receive End Device Announce incoming message +// This message is also received when a previously paired device is powered up // Send back Active Ep Req message // int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) { @@ -328,6 +329,9 @@ int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) { (capabilities & 0x08) ? "true" : "false", (capabilities & 0x40) ? "true" : "false" ); + // query the state of the bulb (for Alexa) + uint32_t wait_ms = 2000; // wait for 2s + Z_Query_Bulb(nwkAddr, wait_ms); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); XdrvRulesProcess(); @@ -513,6 +517,10 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) { // Add linkquality json[F(D_CMND_ZIGBEE_LINKQUALITY)] = linkquality; + // since we just receveived data from the device, it is reachable + zigbee_devices.resetTimersForDevice(srcaddr, 0 /* groupaddr */, Z_CAT_REACHABILITY); // remove any reachability timer already there + zigbee_devices.setReachable(srcaddr, true); // mark device as reachable + if (defer_attributes) { // Prepare for publish if (zigbee_devices.jsonIsConflict(srcaddr, json)) { @@ -591,38 +599,36 @@ int32_t Z_Load_Devices(uint8_t value) { return 0; // continue } +// +// Query the state of a bulb (light) if its type allows it +// +void Z_Query_Bulb(uint16_t shortaddr, uint32_t &wait_ms) { + const uint32_t inter_message_ms = 100; // wait 100ms between messages + + if (0 <= zigbee_devices.getHueBulbtype(shortaddr)) { + uint8_t endpoint = zigbee_devices.findFirstEndpoint(shortaddr); + + if (endpoint) { // send only if we know the endpoint + zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0006, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback); + wait_ms += inter_message_ms; + zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0008, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback); + wait_ms += inter_message_ms; + zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0300, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback); + wait_ms += inter_message_ms; + zigbee_devices.setTimer(shortaddr, 0, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, 0, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable); + } + } +} + // // Send messages to query the state of each Hue emulated light // int32_t Z_Query_Bulbs(uint8_t value) { // Scan all devices and send deferred requests to know the state of bulbs uint32_t wait_ms = 1000; // start with 1.0 s delay - const uint32_t inter_message_ms = 100; // wait 100ms between messages for (uint32_t i = 0; i < zigbee_devices.devicesSize(); i++) { const Z_Device &device = zigbee_devices.devicesAt(i); - - if (0 <= device.bulbtype) { - uint16_t cluster; - uint8_t endpoint = zigbee_devices.findFirstEndpoint(device.shortaddr); - - cluster = 0x0006; - if (endpoint) { // send only if we know the endpoint - zigbee_devices.setTimer(device.shortaddr, 0 /* groupaddr */, wait_ms, cluster, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback); - wait_ms += inter_message_ms; - } - - cluster = 0x0008; - if (endpoint) { // send only if we know the endpoint - zigbee_devices.setTimer(device.shortaddr, 0 /* groupaddr */, wait_ms, cluster, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback); - wait_ms += inter_message_ms; - } - - cluster = 0x0300; - if (endpoint) { // send only if we know the endpoint - zigbee_devices.setTimer(device.shortaddr, 0 /* groupaddr */, wait_ms, cluster, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback); - wait_ms += inter_message_ms; - } - } + Z_Query_Bulb(device.shortaddr, wait_ms); } return 0; // continue } From 6c1f5576d04ccdce58bb897955d7b56c453f2f6c Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Thu, 26 Mar 2020 20:58:59 +0100 Subject: [PATCH 024/547] Add Zigbee ZbUnbind command --- tasmota/CHANGELOG.md | 1 + tasmota/i18n.h | 2 ++ tasmota/xdrv_23_zigbee_8_parsers.ino | 27 +++++++++++++++++++++++++++ tasmota/xdrv_23_zigbee_9_impl.ino | 27 +++++++++++++++++++++++---- 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 0977c85c7..665186b0d 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -7,6 +7,7 @@ - Fix Zigbee sending wrong Sat value with Hue emulation - Add command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` - Add support for unreachable (unplugged) Zigbee devices in Philips Hue emulation and Alexa +- Add Zigbee ``ZbUnbind``command ## Released diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 47afc7307..04433788f 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -505,6 +505,8 @@ #define D_JSON_ZIGBEE_RECEIVED "ZbReceived" #define D_CMND_ZIGBEE_BIND "Bind" #define D_JSON_ZIGBEE_BIND "ZbBind" +#define D_CMND_ZIGBEE_UNBIND "Unbind" + #define D_JSON_ZIGBEE_UNBIND "ZbUnbind" #define D_CMND_ZIGBEE_PING "Ping" #define D_JSON_ZIGBEE_PING "ZbPing" #define D_JSON_ZIGBEE_IEEE "IEEEAddr" diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index 5e05d6c82..cc95d4ed0 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -388,6 +388,31 @@ int32_t Z_BindRsp(int32_t res, const class SBuffer &buf) { return -1; } +// +// Handle Unbind Rsp incoming message +// +int32_t Z_UnbindRsp(int32_t res, const class SBuffer &buf) { + Z_ShortAddress nwkAddr = buf.get16(2); + uint8_t status = buf.get8(4); + + const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr); + if (friendlyName) { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_UNBIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" + ",\"" D_JSON_ZIGBEE_NAME "\":\"%s\"" + ",\"" D_JSON_ZIGBEE_STATUS "\":%d" + ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" + "}}"), nwkAddr, friendlyName, status, getZigbeeStatusMessage(status).c_str()); + } else { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_UNBIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" + ",\"" D_JSON_ZIGBEE_STATUS "\":%d" + ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" + "}}"), nwkAddr, status, getZigbeeStatusMessage(status).c_str()); + } + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + + return -1; +} /*********************************************************************************************\ * Send specific ZNP messages @@ -553,6 +578,7 @@ ZBM(AREQ_ZDO_ACTIVEEPRSP, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP) // 4 ZBM(AREQ_ZDO_SIMPLEDESCRSP, Z_AREQ | Z_ZDO, ZDO_SIMPLE_DESC_RSP) // 4584 ZBM(AREQ_ZDO_IEEE_ADDR_RSP, Z_AREQ | Z_ZDO, ZDO_IEEE_ADDR_RSP) // 4581 ZBM(AREQ_ZDO_BIND_RSP, Z_AREQ | Z_ZDO, ZDO_BIND_RSP) // 45A1 +ZBM(AREQ_ZDO_UNBIND_RSP, Z_AREQ | Z_ZDO, ZDO_UNBIND_RSP) // 45A2 // Dispatcher callbacks table const Z_Dispatcher Z_DispatchTable[] PROGMEM = { @@ -565,6 +591,7 @@ const Z_Dispatcher Z_DispatchTable[] PROGMEM = { { AREQ_ZDO_ACTIVEEPRSP, &Z_ReceiveActiveEp }, { AREQ_ZDO_IEEE_ADDR_RSP, &Z_ReceiveIEEEAddr }, { AREQ_ZDO_BIND_RSP, &Z_BindRsp }, + { AREQ_ZDO_UNBIND_RSP, &Z_UnbindRsp }, }; /*********************************************************************************************\ diff --git a/tasmota/xdrv_23_zigbee_9_impl.ino b/tasmota/xdrv_23_zigbee_9_impl.ino index 49f89c9c7..423ac43f1 100644 --- a/tasmota/xdrv_23_zigbee_9_impl.ino +++ b/tasmota/xdrv_23_zigbee_9_impl.ino @@ -34,7 +34,7 @@ const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|" D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|" D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" - D_CMND_ZIGBEE_BIND "|" D_CMND_ZIGBEE_PING "|" D_CMND_ZIGBEE_MODELID "|" + D_CMND_ZIGBEE_BIND "|" D_CMND_ZIGBEE_UNBIND "|" D_CMND_ZIGBEE_PING "|" D_CMND_ZIGBEE_MODELID "|" D_CMND_ZIGBEE_LIGHT "|" D_CMND_ZIGBEE_RESTORE ; @@ -43,7 +43,7 @@ void (* const ZigbeeCommand[])(void) PROGMEM = { &CmndZbStatus, &CmndZbReset, &CmndZbSend, &CmndZbProbe, &CmndZbRead, &CmndZbZNPReceive, &CmndZbForget, &CmndZbSave, &CmndZbName, - &CmndZbBind, &CmndZbPing, &CmndZbModelId, + &CmndZbBind, &CmndZbUnbind, &CmndZbPing, &CmndZbModelId, &CmndZbLight, CmndZbRestore, }; @@ -534,8 +534,9 @@ void CmndZbSend(void) { // // Command `ZbBind` // -void CmndZbBind(void) { +void ZbBindUnbind(bool unbind) { // false = bind, true = unbind // ZbBind {"Device":"", "Endpoint":, "Cluster":, "ToDevice":"", "ToEndpoint":, "ToGroup": } + // ZbUnbind {"Device":"", "Endpoint":, "Cluster":, "ToDevice":"", "ToEndpoint":, "ToGroup": } // local endpoint is always 1, IEEE addresses are calculated if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } @@ -601,7 +602,11 @@ void CmndZbBind(void) { SBuffer buf(34); buf.add8(Z_SREQ | Z_ZDO); - buf.add8(ZDO_BIND_REQ); + if (unbind) { + buf.add8(ZDO_UNBIND_REQ); + } else { + buf.add8(ZDO_BIND_REQ); + } buf.add16(srcDevice); buf.add64(srcLongAddr); buf.add8(endpoint); @@ -620,6 +625,20 @@ void CmndZbBind(void) { ResponseCmndDone(); } +// +// Command ZbBind +// +void CmndZbBind(void) { + ZbBindUnbind(false); +} + +// +// Command ZbBind +// +void CmndZbUnbind(void) { + ZbBindUnbind(true); +} + // Probe a specific device to get its endpoints and supported clusters void CmndZbProbe(void) { CmndZbProbeOrPing(true); From 3bf14105631aeefed9799fc8af9971aa176daea2 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 27 Mar 2020 16:34:00 +0100 Subject: [PATCH 025/547] Fix PCF8574 and MCP230xx address confict Fix PCF8574 and MCP230xx address confict (#8010) --- tasmota/xdrv_28_pcf8574.ino | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tasmota/xdrv_28_pcf8574.ino b/tasmota/xdrv_28_pcf8574.ino index 26ae9f65a..98773c385 100644 --- a/tasmota/xdrv_28_pcf8574.ino +++ b/tasmota/xdrv_28_pcf8574.ino @@ -77,6 +77,16 @@ void Pcf8574Init(void) uint8_t pcf8574_address = PCF8574_ADDR1; while ((Pcf8574.max_devices < MAX_PCF8574) && (pcf8574_address < PCF8574_ADDR2 +8)) { +#ifdef USE_MCP230xx_ADDR + if (USE_MCP230xx_ADDR == pcf8574_address) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("PCF: Address 0x%02x reserved for MCP320xx skipped"), pcf8574_address); + pcf8574_address++; + if ((PCF8574_ADDR1 +7) == pcf8574_address) { // Support I2C addresses 0x20 to 0x26 and 0x39 to 0x3F + pcf8574_address = PCF8574_ADDR2 +1; + } + } +#endif + // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PCF: Probing addr: 0x%x for PCF8574"), pcf8574_address); if (I2cSetDevice(pcf8574_address)) { @@ -93,12 +103,6 @@ void Pcf8574Init(void) } pcf8574_address++; -#ifdef USE_MCP230xx_ADDR - if (USE_MCP230xx_ADDR == pcf8574_address) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("PCF: Addr: 0x%x reserved for MCP320xx, skipping PCF8574 probe"), pcf8574_address); - pcf8574_address++; - } -#endif if ((PCF8574_ADDR1 +7) == pcf8574_address) { // Support I2C addresses 0x20 to 0x26 and 0x39 to 0x3F pcf8574_address = PCF8574_ADDR2 +1; } From aa0ace3763cb6739cd130548771c2d4c4ef99703 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Fri, 27 Mar 2020 16:38:29 +0100 Subject: [PATCH 026/547] Add support for 64x48 SSD1306 OLED (#6740) --- .../Adafruit_SSD1306.cpp | 13 ++++++++++--- tasmota/CHANGELOG.md | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.cpp b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.cpp index 9827f7584..ec7097983 100644 --- a/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.cpp +++ b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.cpp @@ -916,15 +916,22 @@ uint8_t *Adafruit_SSD1306::getBuffer(void) { of graphics commands, as best needed by one's own application. */ void Adafruit_SSD1306::display(void) { + int16_t col_start = 0; + int16_t col_end = WIDTH - 1; + if ((64 == WIDTH) && (48 == HEIGHT)) { // for 64x48, we need to shift by 32 in both directions + col_start += 32; + col_end += 32; + } + TRANSACTION_START static const uint8_t PROGMEM dlist1[] = { SSD1306_PAGEADDR, 0, // Page start address 0xFF, // Page end (not really, but works here) - SSD1306_COLUMNADDR, - 0 }; // Column start address + SSD1306_COLUMNADDR }; ssd1306_commandList(dlist1, sizeof(dlist1)); - ssd1306_command1(WIDTH - 1); // Column end address + ssd1306_command1(col_start); // Column start address + ssd1306_command1(col_end); // Column end address #if defined(ESP8266) // ESP8266 needs a periodic yield() call to avoid watchdog reset. diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 665186b0d..c874a0ef4 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -8,6 +8,7 @@ - Add command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` - Add support for unreachable (unplugged) Zigbee devices in Philips Hue emulation and Alexa - Add Zigbee ``ZbUnbind``command +- Add support for 64x48 SSD1306 OLED (#6740) ## Released From 898fff9dbc13bae88fc8da564af76de5f0b3c8e4 Mon Sep 17 00:00:00 2001 From: Norbert Richter Date: Sat, 28 Mar 2020 11:13:01 +0100 Subject: [PATCH 027/547] Add multiple GroupTopic (x=1-4) (default disabled) grouptopic (1-4) subscription even when USE_DEVICE_GROUPS is not used. --- tasmota/my_user_config.h | 1 + tasmota/support_tasmota.ino | 4 ++-- tasmota/tasmota_post.h | 6 ++++++ tasmota/xdrv_01_webserver.ino | 11 ++++++++++- tasmota/xdrv_02_mqtt.ino | 28 ++++++++++++++++++---------- tasmota/xdrv_27_shutter.ino | 2 +- 6 files changed, 38 insertions(+), 14 deletions(-) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index ab80c4cf6..910d4c7fe 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -344,6 +344,7 @@ // -- MQTT ---------------------------------------- #define MQTT_TELE_RETAIN 0 // Tele messages may send retain flag (0 = off, 1 = on) #define MQTT_CLEAN_SESSION 1 // Mqtt clean session connection (0 = No clean session, 1 = Clean session (default)) +//#define USE_GROUPTOPIC_SET // Enable multiple GROUPTOPIC, x=1-4 (+0k1 code) // -- MQTT - Domoticz ----------------------------- #define USE_DOMOTICZ // Enable Domoticz (+6k code, +0.3k mem) diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 3c4239359..a42515899 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -130,11 +130,11 @@ char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopi return stopic; } -char* GetGroupTopic_P(char *stopic, const char* subtopic) +char* GetGroupTopic_P(char *stopic, const char* subtopic, int itopic) { // SetOption75 0: %prefix%/nothing/%topic% = cmnd/nothing//# // SetOption75 1: cmnd/ - return GetTopic_P(stopic, (Settings.flag3.grouptopic_mode) ? CMND +8 : CMND, SettingsText(SET_MQTT_GRP_TOPIC), subtopic); // SetOption75 - GroupTopic replaces %topic% (0) or fixed topic cmnd/grouptopic (1) + return GetTopic_P(stopic, (Settings.flag3.grouptopic_mode) ? CMND +8 : CMND, SettingsText(itopic), subtopic); // SetOption75 - GroupTopic replaces %topic% (0) or fixed topic cmnd/grouptopic (1) } char* GetFallbackTopic_P(char *stopic, const char* subtopic) diff --git a/tasmota/tasmota_post.h b/tasmota/tasmota_post.h index 1bba02172..d7a36f160 100644 --- a/tasmota/tasmota_post.h +++ b/tasmota/tasmota_post.h @@ -98,6 +98,7 @@ extern "C" void resetPins(); #define CODE_IMAGE_STR "sensors" #undef USE_DISCOVERY // Disable mDNS (+8k code or +23.5k code with core 2_5_x, +0.3k mem) +#undef USE_GROUPTOPIC_SET // Disable multiple GROUPTOPIC, x=1-4 (+0k1 code) // -- Optional modules ---------------------------- //#define ROTARY_V1 // Add support for MI Desk Lamp @@ -273,6 +274,7 @@ extern "C" void resetPins(); #undef USE_PWM_DIMMER_REMOTE // Disbale support for remote switches to PWM Dimmer #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code +#undef USE_GROUPTOPIC_SET // Disable multiple GROUPTOPIC, x=1-4 (+0k1 code) #endif // FIRMWARE_KNX_NO_EMULATION /*********************************************************************************************\ @@ -292,6 +294,7 @@ extern "C" void resetPins(); #undef USE_EMULATION_WEMO // Disable Belkin WeMo emulation for Alexa (+6k code, +2k mem common) #undef USE_DOMOTICZ // Disable Domoticz #undef USE_HOME_ASSISTANT // Disable Home Assistant +#undef USE_GROUPTOPIC_SET // Disable multiple GROUPTOPIC, x=1-4 (+0k1 code) // -- Optional modules ---------------------------- #undef ROTARY_V1 // Disable support for MI Desk Lamp @@ -373,6 +376,7 @@ extern "C" void resetPins(); //#undef USE_SUNRISE // Disable support for Sunrise and sunset tools //#undef USE_RULES // Disable support for rules #undef USE_DISCOVERY // Disable mDNS for the following services (+8k code or +23.5k code with core 2_5_x, +0.3k mem) +#undef USE_GROUPTOPIC_SET // Disable multiple GROUPTOPIC, x=1-4 (+0k1 code) // -- Optional modules ---------------------------- #undef ROTARY_V1 // Disable support for MI Desk Lamp @@ -474,6 +478,7 @@ extern "C" void resetPins(); #undef USE_DOMOTICZ // Disable Domoticz #undef USE_HOME_ASSISTANT // Disable Home Assistant #undef USE_MQTT_TLS // Disable TLS support won't work as the MQTTHost is not set +#undef USE_GROUPTOPIC_SET // Disable multiple GROUPTOPIC, x=1-4 (+0k1 code) #undef USE_KNX // Disable KNX IP Protocol Support //#undef USE_WEBSERVER // Disable Webserver #undef USE_WEBSEND_RESPONSE // Disable command WebSend response message (+1k code) @@ -595,6 +600,7 @@ extern "C" void resetPins(); #undef USE_DOMOTICZ // Disable Domoticz #undef USE_HOME_ASSISTANT // Disable Home Assistant //#undef USE_MQTT_TLS // Disable TLS support won't work as the MQTTHost is not set +#undef USE_GROUPTOPIC_SET // Disable multiple GROUPTOPIC, x=1-4 (+0k1 code) #undef USE_KNX // Disable KNX IP Protocol Support //#undef USE_WEBSERVER // Disable Webserver #undef USE_WEBSEND_RESPONSE // Disable command WebSend response message (+1k code) diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 7f55a01fd..31531af88 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -2127,7 +2127,16 @@ void HandleInformation(void) WSContentSend_P(PSTR("}1" D_MQTT_CLIENT "}2%s"), mqtt_client); WSContentSend_P(PSTR("}1" D_MQTT_TOPIC "}2%s"), SettingsText(SET_MQTT_TOPIC)); // WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC "}2%s"), SettingsText(SET_MQTT_GRP_TOPIC)); - WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC "}2%s"), GetGroupTopic_P(stopic, "")); +#ifdef USE_GROUPTOPIC_SET + WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC " 1}2%s"), GetGroupTopic_P(stopic, "", SET_MQTT_GRP_TOPIC)); + for(uint32_t i=0; i < 3; i++) { + if (strlen(SettingsText(SET_MQTT_GRP_TOPIC2+i))) { + WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC " %d}2%s"), 2+i, GetGroupTopic_P(stopic, "", SET_MQTT_GRP_TOPIC2+i)); + } + } +#else // USE_GROUPTOPIC_SET + WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC "}2%s"), GetGroupTopic_P(stopic, "", SET_MQTT_GRP_TOPIC)); +#endif // USE_GROUPTOPIC_SET WSContentSend_P(PSTR("}1" D_MQTT_FULL_TOPIC "}2%s"), GetTopic_P(stopic, CMND, mqtt_topic, "")); WSContentSend_P(PSTR("}1" D_MQTT " " D_FALLBACK_TOPIC "}2%s"), GetFallbackTopic_P(stopic, "")); } else { diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index 1d084862b..22fcb2d9d 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -510,8 +510,16 @@ void MqttConnected(void) GetTopic_P(stopic, CMND, mqtt_topic, PSTR("#")); MqttSubscribe(stopic); if (strstr_P(SettingsText(SET_MQTT_FULLTOPIC), MQTT_TOKEN_TOPIC) != nullptr) { - GetGroupTopic_P(stopic, PSTR("#")); // SetOption75 0: %prefix%/nothing/%topic% = cmnd/nothing//# or SetOption75 1: cmnd/ + GetGroupTopic_P(stopic, PSTR("#"), SET_MQTT_GRP_TOPIC); // SetOption75 0: %prefix%/nothing/%topic% = cmnd/nothing//# or SetOption75 1: cmnd/ MqttSubscribe(stopic); +#ifdef USE_GROUPTOPIC_SET + for(uint32_t i=0; i < 3; i++) { + if (strlen(SettingsText(SET_MQTT_GRP_TOPIC2+i))) { + GetGroupTopic_P(stopic, PSTR("#"), SET_MQTT_GRP_TOPIC2+i); + MqttSubscribe(stopic); + } + } +#endif GetFallbackTopic_P(stopic, PSTR("#")); MqttSubscribe(stopic); } @@ -522,7 +530,7 @@ void MqttConnected(void) if (Mqtt.initial_connection_state) { char stopic2[TOPSZ]; Response_P(PSTR("{\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_FALLBACKTOPIC "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\"}"), - ModuleName().c_str(), my_version, my_image, GetFallbackTopic_P(stopic, ""), GetGroupTopic_P(stopic2, "")); + ModuleName().c_str(), my_version, my_image, GetFallbackTopic_P(stopic, ""), GetGroupTopic_P(stopic2, "", SET_MQTT_GRP_TOPIC)); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "1")); #ifdef USE_WEBSERVER if (Settings.webserver) { @@ -881,26 +889,26 @@ void CmndPublish(void) void CmndGroupTopic(void) { -#ifdef USE_DEVICE_GROUPS +#if defined(USE_DEVICE_GROUPS ) || defined(USE_GROUPTOPIC_SET) if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { uint32_t settings_text_index = (XdrvMailbox.index <= 1 ? SET_MQTT_GRP_TOPIC : SET_MQTT_GRP_TOPIC2 + XdrvMailbox.index - 2); -#endif // USE_DEVICE_GROUPS +#endif // USE_DEVICE_GROUPS || USE_GROUPTOPIC_SET if (XdrvMailbox.data_len > 0) { MakeValidMqtt(0, XdrvMailbox.data); if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } -#ifdef USE_DEVICE_GROUPS +#if defined(USE_DEVICE_GROUPS ) || defined(USE_GROUPTOPIC_SET) SettingsUpdateText(settings_text_index, (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data); -#else // USE_DEVICE_GROUPS +#else // USE_DEVICE_GROUPS || USE_GROUPTOPIC_SET SettingsUpdateText(SET_MQTT_GRP_TOPIC, (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data); -#endif // USE_DEVICE_GROUPS +#endif // USE_DEVICE_GROUPS || USE_GROUPTOPIC_SET restart_flag = 2; } -#ifdef USE_DEVICE_GROUPS +#if defined(USE_DEVICE_GROUPS ) || defined(USE_GROUPTOPIC_SET) ResponseCmndChar(SettingsText(settings_text_index)); } -#else // USE_DEVICE_GROUPS +#else // USE_DEVICE_GROUPS || USE_GROUPTOPIC_SET ResponseCmndChar(SettingsText(SET_MQTT_GRP_TOPIC)); -#endif // USE_DEVICE_GROUPS +#endif // USE_DEVICE_GROUPS || USE_GROUPTOPIC_SET } void CmndTopic(void) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index cedf93e28..b68de3c8d 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -685,7 +685,7 @@ void ShutterButtonHandler(void) for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { if ((i==shutter_index) || (Settings.shutter_button[button_index] & (0x01<<30))) { snprintf_P(scommand, sizeof(scommand),PSTR("ShutterPosition%d"), i+1); - GetGroupTopic_P(stopic, scommand); + GetGroupTopic_P(stopic, scommand, SET_MQTT_GRP_TOPIC); Response_P("%d", position); MqttPublish(stopic, false); } From dbc9f16ffc5c8e6f590b38382ca444dda162c334 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 28 Mar 2020 16:48:36 +0100 Subject: [PATCH 028/547] Add support for up to four MQTT GroupTopics - Bump version to 8.2.0.2 - Add support for up to four MQTT GroupTopics using the same optional Device Group names (#8014) --- RELEASENOTES.md | 10 +++-- tasmota/CHANGELOG.md | 10 +++-- tasmota/my_user_config.h | 1 - tasmota/support_command.ino | 4 +- tasmota/support_tasmota.ino | 2 +- tasmota/tasmota.h | 1 + tasmota/tasmota_post.h | 6 --- tasmota/tasmota_version.h | 2 +- tasmota/xdrv_01_webserver.ino | 14 +++---- tasmota/xdrv_02_mqtt.ino | 76 +++++++++++++++++++++++------------ 10 files changed, 75 insertions(+), 51 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 1804b32c5..da5dd225f 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -52,9 +52,13 @@ The following binary downloads have been compiled with ESP8266/Arduino library c ## Changelog -### Version 8.2.0.1 +### Version 8.2.0.2 - Change HM-10 sensor type detection and add features (#7962) -- Change GPIO initialization solving possible Relay toggle on (OTA) restart +- Fix possible Relay toggle on (OTA) restart - Fix Zigbee sending wrong Sat value with Hue emulation -- Add command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` +- Add Zigbee command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` +- Add Zigbee command ``ZbUnbind`` +- Add support for unreachable (unplugged) Zigbee devices in Philips Hue emulation and Alexa +- Add support for 64x48 SSD1306 OLED (#6740) +- Add support for up to four MQTT GroupTopics using the same optional Device Group names (#8014) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index c874a0ef4..fa969d778 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -1,13 +1,17 @@ ## Unreleased (development) +### 8.2.0.2 20200328 + +- Add support for up to four MQTT GroupTopics using the same optional Device Group names (#8014) + ### 8.2.0.1 20200321 - Change HM-10 sensor type detection and add features (#7962) -- Change GPIO initialization solving possible Relay toggle on (OTA) restart +- Fix possible Relay toggle on (OTA) restart - Fix Zigbee sending wrong Sat value with Hue emulation -- Add command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` +- Add ZIgbee command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` +- Add Zigbee command ``ZbUnbind`` - Add support for unreachable (unplugged) Zigbee devices in Philips Hue emulation and Alexa -- Add Zigbee ``ZbUnbind``command - Add support for 64x48 SSD1306 OLED (#6740) ## Released diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 910d4c7fe..ab80c4cf6 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -344,7 +344,6 @@ // -- MQTT ---------------------------------------- #define MQTT_TELE_RETAIN 0 // Tele messages may send retain flag (0 = off, 1 = on) #define MQTT_CLEAN_SESSION 1 // Mqtt clean session connection (0 = No clean session, 1 = Clean session (default)) -//#define USE_GROUPTOPIC_SET // Enable multiple GROUPTOPIC, x=1-4 (+0k1 code) // -- MQTT - Domoticz ----------------------------- #define USE_DOMOTICZ // Enable Domoticz (+6k code, +0.3k mem) diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index e1cc9e812..133203a3e 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -107,9 +107,11 @@ void ResponseCmndIdxChar(const char* value) void ResponseCmndAll(uint32_t text_index, uint32_t count) { + uint32_t real_index = text_index; mqtt_data[0] = '\0'; for (uint32_t i = 0; i < count; i++) { - ResponseAppend_P(PSTR("%c\"%s%d\":\"%s\""), (i) ? ',' : '{', XdrvMailbox.command, i +1, SettingsText(text_index +i)); + if ((SET_MQTT_GRP_TOPIC == text_index) && (1 == i)) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + ResponseAppend_P(PSTR("%c\"%s%d\":\"%s\""), (i) ? ',' : '{', XdrvMailbox.command, i +1, SettingsText(real_index +i)); } ResponseJsonEnd(); } diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index a42515899..23a4caad7 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -130,7 +130,7 @@ char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopi return stopic; } -char* GetGroupTopic_P(char *stopic, const char* subtopic, int itopic) +char* GetGroupTopic_P(char *stopic, const char* subtopic, uint32_t itopic) { // SetOption75 0: %prefix%/nothing/%topic% = cmnd/nothing//# // SetOption75 1: cmnd/ diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index a1c7d1082..033b8d783 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -81,6 +81,7 @@ const uint8_t MAX_NTP_SERVERS = 3; // Max number of NTP servers const uint8_t MAX_RULE_MEMS = 16; // Max number of saved vars const uint8_t MAX_FRIENDLYNAMES = 8; // Max number of Friendly names const uint8_t MAX_BUTTON_TEXT = 16; // Max number of GUI button labels +const uint8_t MAX_GROUP_TOPICS = 4; // Max number of Group Topics const uint8_t MAX_HUE_DEVICES = 15; // Max number of Philips Hue device per emulation diff --git a/tasmota/tasmota_post.h b/tasmota/tasmota_post.h index d7a36f160..1bba02172 100644 --- a/tasmota/tasmota_post.h +++ b/tasmota/tasmota_post.h @@ -98,7 +98,6 @@ extern "C" void resetPins(); #define CODE_IMAGE_STR "sensors" #undef USE_DISCOVERY // Disable mDNS (+8k code or +23.5k code with core 2_5_x, +0.3k mem) -#undef USE_GROUPTOPIC_SET // Disable multiple GROUPTOPIC, x=1-4 (+0k1 code) // -- Optional modules ---------------------------- //#define ROTARY_V1 // Add support for MI Desk Lamp @@ -274,7 +273,6 @@ extern "C" void resetPins(); #undef USE_PWM_DIMMER_REMOTE // Disbale support for remote switches to PWM Dimmer #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code -#undef USE_GROUPTOPIC_SET // Disable multiple GROUPTOPIC, x=1-4 (+0k1 code) #endif // FIRMWARE_KNX_NO_EMULATION /*********************************************************************************************\ @@ -294,7 +292,6 @@ extern "C" void resetPins(); #undef USE_EMULATION_WEMO // Disable Belkin WeMo emulation for Alexa (+6k code, +2k mem common) #undef USE_DOMOTICZ // Disable Domoticz #undef USE_HOME_ASSISTANT // Disable Home Assistant -#undef USE_GROUPTOPIC_SET // Disable multiple GROUPTOPIC, x=1-4 (+0k1 code) // -- Optional modules ---------------------------- #undef ROTARY_V1 // Disable support for MI Desk Lamp @@ -376,7 +373,6 @@ extern "C" void resetPins(); //#undef USE_SUNRISE // Disable support for Sunrise and sunset tools //#undef USE_RULES // Disable support for rules #undef USE_DISCOVERY // Disable mDNS for the following services (+8k code or +23.5k code with core 2_5_x, +0.3k mem) -#undef USE_GROUPTOPIC_SET // Disable multiple GROUPTOPIC, x=1-4 (+0k1 code) // -- Optional modules ---------------------------- #undef ROTARY_V1 // Disable support for MI Desk Lamp @@ -478,7 +474,6 @@ extern "C" void resetPins(); #undef USE_DOMOTICZ // Disable Domoticz #undef USE_HOME_ASSISTANT // Disable Home Assistant #undef USE_MQTT_TLS // Disable TLS support won't work as the MQTTHost is not set -#undef USE_GROUPTOPIC_SET // Disable multiple GROUPTOPIC, x=1-4 (+0k1 code) #undef USE_KNX // Disable KNX IP Protocol Support //#undef USE_WEBSERVER // Disable Webserver #undef USE_WEBSEND_RESPONSE // Disable command WebSend response message (+1k code) @@ -600,7 +595,6 @@ extern "C" void resetPins(); #undef USE_DOMOTICZ // Disable Domoticz #undef USE_HOME_ASSISTANT // Disable Home Assistant //#undef USE_MQTT_TLS // Disable TLS support won't work as the MQTTHost is not set -#undef USE_GROUPTOPIC_SET // Disable multiple GROUPTOPIC, x=1-4 (+0k1 code) #undef USE_KNX // Disable KNX IP Protocol Support //#undef USE_WEBSERVER // Disable Webserver #undef USE_WEBSEND_RESPONSE // Disable command WebSend response message (+1k code) diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h index 3bc994c67..47879d1fe 100644 --- a/tasmota/tasmota_version.h +++ b/tasmota/tasmota_version.h @@ -20,7 +20,7 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x08020001; +const uint32_t VERSION = 0x08020002; // Lowest compatible version const uint32_t VERSION_COMPATIBLE = 0x07010006; diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 31531af88..f48ce9431 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -2126,17 +2126,13 @@ void HandleInformation(void) WSContentSend_P(PSTR("}1" D_MQTT_USER "}2%s"), SettingsText(SET_MQTT_USER)); WSContentSend_P(PSTR("}1" D_MQTT_CLIENT "}2%s"), mqtt_client); WSContentSend_P(PSTR("}1" D_MQTT_TOPIC "}2%s"), SettingsText(SET_MQTT_TOPIC)); -// WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC "}2%s"), SettingsText(SET_MQTT_GRP_TOPIC)); -#ifdef USE_GROUPTOPIC_SET - WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC " 1}2%s"), GetGroupTopic_P(stopic, "", SET_MQTT_GRP_TOPIC)); - for(uint32_t i=0; i < 3; i++) { - if (strlen(SettingsText(SET_MQTT_GRP_TOPIC2+i))) { - WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC " %d}2%s"), 2+i, GetGroupTopic_P(stopic, "", SET_MQTT_GRP_TOPIC2+i)); + uint32_t real_index = SET_MQTT_GRP_TOPIC; + for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { + if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + if (strlen(SettingsText(real_index +i))) { + WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC " %d}2%s"), 1 +i, GetGroupTopic_P(stopic, "", real_index +i)); } } -#else // USE_GROUPTOPIC_SET - WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC "}2%s"), GetGroupTopic_P(stopic, "", SET_MQTT_GRP_TOPIC)); -#endif // USE_GROUPTOPIC_SET WSContentSend_P(PSTR("}1" D_MQTT_FULL_TOPIC "}2%s"), GetTopic_P(stopic, CMND, mqtt_topic, "")); WSContentSend_P(PSTR("}1" D_MQTT " " D_FALLBACK_TOPIC "}2%s"), GetFallbackTopic_P(stopic, "")); } else { diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index 22fcb2d9d..ac3065fe8 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -510,16 +510,14 @@ void MqttConnected(void) GetTopic_P(stopic, CMND, mqtt_topic, PSTR("#")); MqttSubscribe(stopic); if (strstr_P(SettingsText(SET_MQTT_FULLTOPIC), MQTT_TOKEN_TOPIC) != nullptr) { - GetGroupTopic_P(stopic, PSTR("#"), SET_MQTT_GRP_TOPIC); // SetOption75 0: %prefix%/nothing/%topic% = cmnd/nothing//# or SetOption75 1: cmnd/ - MqttSubscribe(stopic); -#ifdef USE_GROUPTOPIC_SET - for(uint32_t i=0; i < 3; i++) { - if (strlen(SettingsText(SET_MQTT_GRP_TOPIC2+i))) { - GetGroupTopic_P(stopic, PSTR("#"), SET_MQTT_GRP_TOPIC2+i); + uint32_t real_index = SET_MQTT_GRP_TOPIC; + for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { + if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + if (strlen(SettingsText(real_index +i))) { + GetGroupTopic_P(stopic, PSTR("#"), real_index +i); // SetOption75 0: %prefix%/nothing/%topic% = cmnd/nothing//# or SetOption75 1: cmnd/ MqttSubscribe(stopic); } } -#endif GetFallbackTopic_P(stopic, PSTR("#")); MqttSubscribe(stopic); } @@ -889,26 +887,52 @@ void CmndPublish(void) void CmndGroupTopic(void) { -#if defined(USE_DEVICE_GROUPS ) || defined(USE_GROUPTOPIC_SET) - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { - uint32_t settings_text_index = (XdrvMailbox.index <= 1 ? SET_MQTT_GRP_TOPIC : SET_MQTT_GRP_TOPIC2 + XdrvMailbox.index - 2); -#endif // USE_DEVICE_GROUPS || USE_GROUPTOPIC_SET - if (XdrvMailbox.data_len > 0) { - MakeValidMqtt(0, XdrvMailbox.data); - if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } -#if defined(USE_DEVICE_GROUPS ) || defined(USE_GROUPTOPIC_SET) - SettingsUpdateText(settings_text_index, (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data); -#else // USE_DEVICE_GROUPS || USE_GROUPTOPIC_SET - SettingsUpdateText(SET_MQTT_GRP_TOPIC, (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data); -#endif // USE_DEVICE_GROUPS || USE_GROUPTOPIC_SET - restart_flag = 2; + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_GROUP_TOPICS)) { + if (XdrvMailbox.data_len > 0) { + uint32_t settings_text_index = (1 == XdrvMailbox.index) ? SET_MQTT_GRP_TOPIC : SET_MQTT_GRP_TOPIC2 + XdrvMailbox.index - 2; + MakeValidMqtt(0, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + SettingsUpdateText(settings_text_index, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data); + + // Eliminate duplicates, have at least one and fill from index 1 + char stemp[MAX_GROUP_TOPICS][TOPSZ]; + uint32_t read_index = 0; + uint32_t real_index = SET_MQTT_GRP_TOPIC; + for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { + if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + if (strlen(SettingsText(real_index +i))) { + bool not_equal = true; + for (uint32_t j = 0; j < read_index; j++) { + if (!strcmp(SettingsText(real_index +i), stemp[j])) { // Topics are case-sensitive + not_equal = false; + } + } + if (not_equal) { + strncpy(stemp[read_index], SettingsText(real_index +i), sizeof(stemp[read_index])); + read_index++; + } + } + } + if (0 == read_index) { + SettingsUpdateText(SET_MQTT_GRP_TOPIC, MQTT_GRPTOPIC); + } else { + uint32_t write_index = 0; + uint32_t real_index = SET_MQTT_GRP_TOPIC; + for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { + if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + if (write_index < read_index) { + SettingsUpdateText(real_index +i, stemp[write_index]); + write_index++; + } else { + SettingsUpdateText(real_index +i, ""); + } + } + } + + restart_flag = 2; + } + ResponseCmndAll(SET_MQTT_GRP_TOPIC, MAX_GROUP_TOPICS); } -#if defined(USE_DEVICE_GROUPS ) || defined(USE_GROUPTOPIC_SET) - ResponseCmndChar(SettingsText(settings_text_index)); - } -#else // USE_DEVICE_GROUPS || USE_GROUPTOPIC_SET - ResponseCmndChar(SettingsText(SET_MQTT_GRP_TOPIC)); -#endif // USE_DEVICE_GROUPS || USE_GROUPTOPIC_SET } void CmndTopic(void) From e5a44e63262032d1d7e0b356967306e170a4fd00 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 28 Mar 2020 18:08:43 +0100 Subject: [PATCH 029/547] Refactor template --- tasmota/settings.h | 3 +- tasmota/support.ino | 10 +- tasmota/support_command.ino | 2 +- tasmota/tasmota_template.h | 2820 ++++++++++++++++----------------- tasmota/xdrv_01_webserver.ino | 2 +- 5 files changed, 1393 insertions(+), 1444 deletions(-) diff --git a/tasmota/settings.h b/tasmota/settings.h index 737ce8a82..fa98a8ecf 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -397,7 +397,8 @@ struct SYSCFG { uint16_t mcp230xx_int_timer; // 718 uint8_t rgbwwTable[5]; // 71A uint8_t user_template_base; // 71F - mytmplt user_template; // 720 29 bytes + char user_template_name[15]; // 720 15 bytes + mytmplt user_template; // 72F 14 bytes uint8_t novasds_startingoffset; // 73D uint8_t web_color[18][3]; // 73E uint16_t display_width; // 774 diff --git a/tasmota/support.ino b/tasmota/support.ino index 490918276..458a8efd3 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -1098,9 +1098,10 @@ bool ValidModule(uint32_t index) String AnyModuleName(uint32_t index) { if (USER_MODULE == index) { - return String(Settings.user_template.name); + return String(Settings.user_template_name); } else { - return FPSTR(kModules[index].name); + char name[15]; + return String(GetTextIndexed(name, sizeof(name), index, kModuleNames)); } } @@ -1153,6 +1154,7 @@ void ModuleDefault(uint32_t module) { if (USER_MODULE == module) { module = WEMOS; } // Generic Settings.user_template_base = module; + GetTextIndexed(Settings.user_template_name, sizeof(Settings.user_template_name), module, kModuleNames); memcpy_P(&Settings.user_template, &kModules[module], sizeof(mytmplt)); } @@ -1268,7 +1270,7 @@ bool JsonTemplate(const char* dataBuf) // All parameters are optional allowing for partial changes const char* name = obj[D_JSON_NAME]; if (name != nullptr) { - strlcpy(Settings.user_template.name, name, sizeof(Settings.user_template.name)); + strlcpy(Settings.user_template_name, name, sizeof(Settings.user_template_name)); } if (obj[D_JSON_GPIO].success()) { for (uint32_t i = 0; i < sizeof(mycfgio); i++) { @@ -1289,7 +1291,7 @@ bool JsonTemplate(const char* dataBuf) void TemplateJson(void) { - Response_P(PSTR("{\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), Settings.user_template.name); + Response_P(PSTR("{\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), Settings.user_template_name); for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) { ResponseAppend_P(PSTR("%s%d"), (i>0)?",":"", Settings.user_template.gp.io[i]); } diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 133203a3e..36e5c08fd 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -1064,7 +1064,7 @@ void CmndTemplate(void) if (Settings.module != USER_MODULE) { ModuleDefault(Settings.module); } - snprintf_P(Settings.user_template.name, sizeof(Settings.user_template.name), PSTR("Merged")); + snprintf_P(Settings.user_template_name, sizeof(Settings.user_template_name), PSTR("Merged")); uint32_t j = 0; for (uint32_t i = 0; i < sizeof(mycfgio); i++) { if (6 == i) { j = 9; } diff --git a/tasmota/tasmota_template.h b/tasmota/tasmota_template.h index 4715f682f..e21260741 100644 --- a/tasmota/tasmota_template.h +++ b/tasmota/tasmota_template.h @@ -319,157 +319,6 @@ const char kSensorNamesFixed[] PROGMEM = D_SENSOR_SPI_MISO "|" D_SENSOR_SPI_MOSI "|" D_SENSOR_SPI_CLK "|" D_SENSOR_USER; -// User selectable ADC0 functionality -enum UserSelectableAdc0 { - ADC0_NONE, // Not used - ADC0_INPUT, // Analog input - ADC0_TEMP, // Thermistor - ADC0_LIGHT, // Light sensor - ADC0_BUTTON, // Button - ADC0_BUTTON_INV, - ADC0_RANGE, // Range - ADC0_CT_POWER, // Current -// ADC0_SWITCH, // Switch -// ADC0_SWITCH_INV, - ADC0_END }; - -// Programmer selectable ADC0 functionality -enum ProgramSelectableAdc0 { - ADC0_FIX_START = 14, - ADC0_USER, // User configurable needs to be 15 - ADC0_MAX }; - -// Text in webpage Module Parameters and commands ADC -const char kAdc0Names[] PROGMEM = - D_SENSOR_NONE "|" D_ANALOG_INPUT "|" - D_TEMPERATURE "|" D_LIGHT "|" - D_SENSOR_BUTTON "|" D_SENSOR_BUTTON "i|" - D_RANGE "|" - D_CT_POWER "|" -// D_SENSOR_SWITCH "|" D_SENSOR_SWITCH "i|" - ; - -/********************************************************************************************/ - -// Supported hardware modules -enum SupportedModules { - SONOFF_BASIC, - SONOFF_RF, - SONOFF_SV, - SONOFF_TH, - SONOFF_DUAL, - SONOFF_POW, - SONOFF_4CH, - SONOFF_S2X, - SLAMPHER, - SONOFF_TOUCH, - SONOFF_LED, - CH1, - CH4, - MOTOR, - ELECTRODRAGON, - EXS_RELAY, - WION, - WEMOS, - SONOFF_DEV, - H801, - SONOFF_SC, - SONOFF_BN, - SONOFF_4CHPRO, - HUAFAN_SS, - SONOFF_BRIDGE, - SONOFF_B1, - AILIGHT, - SONOFF_T11, - SONOFF_T12, - SONOFF_T13, - SUPLA1, - WITTY, - YUNSHAN, - MAGICHOME, - LUANIHVIO, - KMC_70011, - ARILUX_LC01, - ARILUX_LC11, - SONOFF_DUAL_R2, - ARILUX_LC06, - SONOFF_S31, - ZENGGE_ZF_WF017, - SONOFF_POW_R2, - SONOFF_IFAN02, - BLITZWOLF_BWSHP, - SHELLY1, - SHELLY2, - PHILIPS, - NEO_COOLCAM, - ESP_SWITCH, - OBI, - TECKIN, - APLIC_WDP303075, - TUYA_DIMMER, - GOSUND, - ARMTRONIX_DIMMERS, - SK03_TUYA, - PS_16_DZ, - TECKIN_US, - MANZOKU_EU_4, - OBI2, - YTF_IR_BRIDGE, - DIGOO, - KA10, - ZX2820, - MI_DESK_LAMP, - SP10, - WAGA, - SYF05, - SONOFF_L1, - SONOFF_IFAN03, - EXS_DIMMER, - PWM_DIMMER, - SONOFF_D1, - MAXMODULE}; - -#define USER_MODULE 255 - -/********************************************************************************************/ - -#define MAX_GPIO_PIN 17 // Number of supported GPIO -#define MIN_FLASH_PINS 4 // Number of flash chip pins unusable for configuration (GPIO6, 7, 8 and 11) - -const char PINS_WEMOS[] PROGMEM = "D3TXD4RXD2D1flashcFLFLolD6D7D5D8D0A0"; - -typedef struct MYIO { - uint8_t io[MAX_GPIO_PIN]; -} myio; - -typedef struct MYCFGIO { - uint8_t io[MAX_GPIO_PIN - MIN_FLASH_PINS]; -} mycfgio; - -#define GPIO_FLAG_USED 0 // Currently two flags used - -#define GPIO_FLAG_SPARE04 16 -#define GPIO_FLAG_SPARE05 32 -#define GPIO_FLAG_SPARE06 64 -#define GPIO_FLAG_SPARE07 128 - -typedef union { - uint8_t data; - struct { - uint8_t adc0 : 4; // Allow ADC0 when define USE_ADC_VCC is disabled - uint8_t spare04 : 1; - uint8_t spare05 : 1; - uint8_t spare06 : 1; - uint8_t spare07 : 1; - }; -} gpio_flag; - -typedef struct MYTMPLT { - char name[15]; - mycfgio gp; - gpio_flag flag; -} mytmplt; - const uint8_t kGpioNiceList[] PROGMEM = { GPIO_NONE, // Not used GPIO_KEY1, // Buttons @@ -809,6 +658,103 @@ const uint8_t kGpioNiceList[] PROGMEM = { #endif }; +/********************************************************************************************/ + +// User selectable ADC0 functionality +enum UserSelectableAdc0 { + ADC0_NONE, // Not used + ADC0_INPUT, // Analog input + ADC0_TEMP, // Thermistor + ADC0_LIGHT, // Light sensor + ADC0_BUTTON, // Button + ADC0_BUTTON_INV, + ADC0_RANGE, // Range + ADC0_CT_POWER, // Current +// ADC0_SWITCH, // Switch +// ADC0_SWITCH_INV, + ADC0_END }; + +// Programmer selectable ADC0 functionality +enum ProgramSelectableAdc0 { + ADC0_FIX_START = 14, + ADC0_USER, // User configurable needs to be 15 + ADC0_MAX }; + +// Text in webpage Module Parameters and commands ADC +const char kAdc0Names[] PROGMEM = + D_SENSOR_NONE "|" D_ANALOG_INPUT "|" + D_TEMPERATURE "|" D_LIGHT "|" + D_SENSOR_BUTTON "|" D_SENSOR_BUTTON "i|" + D_RANGE "|" + D_CT_POWER "|" +// D_SENSOR_SWITCH "|" D_SENSOR_SWITCH "i|" + ; + +/********************************************************************************************/ + +#define MAX_GPIO_PIN 17 // Number of supported GPIO +#define MIN_FLASH_PINS 4 // Number of flash chip pins unusable for configuration (GPIO6, 7, 8 and 11) + +const char PINS_WEMOS[] PROGMEM = "D3TXD4RXD2D1flashcFLFLolD6D7D5D8D0A0"; + +typedef struct MYIO { + uint8_t io[MAX_GPIO_PIN]; +} myio; + +typedef struct MYCFGIO { + uint8_t io[MAX_GPIO_PIN - MIN_FLASH_PINS]; +} mycfgio; + +#define GPIO_FLAG_USED 0 // Currently two flags used + +#define GPIO_FLAG_SPARE04 16 +#define GPIO_FLAG_SPARE05 32 +#define GPIO_FLAG_SPARE06 64 +#define GPIO_FLAG_SPARE07 128 + +typedef union { + uint8_t data; + struct { + uint8_t adc0 : 4; // Allow ADC0 when define USE_ADC_VCC is disabled + uint8_t spare04 : 1; + uint8_t spare05 : 1; + uint8_t spare06 : 1; + uint8_t spare07 : 1; + }; +} gpio_flag; + +typedef struct MYTMPLT { + mycfgio gp; + gpio_flag flag; +} mytmplt; + +/********************************************************************************************/ + +// Supported hardware modules +enum SupportedModules { + SONOFF_BASIC, SONOFF_RF, SONOFF_SV, SONOFF_TH, SONOFF_DUAL, SONOFF_POW, SONOFF_4CH, SONOFF_S2X, SLAMPHER, SONOFF_TOUCH, + SONOFF_LED, CH1, CH4, MOTOR, ELECTRODRAGON, EXS_RELAY, WION, WEMOS, SONOFF_DEV, H801, + SONOFF_SC, SONOFF_BN, SONOFF_4CHPRO, HUAFAN_SS, SONOFF_BRIDGE, SONOFF_B1, AILIGHT, SONOFF_T11, SONOFF_T12, SONOFF_T13, + SUPLA1, WITTY, YUNSHAN, MAGICHOME, LUANIHVIO, KMC_70011, ARILUX_LC01, ARILUX_LC11, SONOFF_DUAL_R2, ARILUX_LC06, + SONOFF_S31, ZENGGE_ZF_WF017, SONOFF_POW_R2, SONOFF_IFAN02, BLITZWOLF_BWSHP, SHELLY1, SHELLY2, PHILIPS, NEO_COOLCAM, ESP_SWITCH, + OBI, TECKIN, APLIC_WDP303075, TUYA_DIMMER, GOSUND, ARMTRONIX_DIMMERS, SK03_TUYA, PS_16_DZ, TECKIN_US, MANZOKU_EU_4, + OBI2, YTF_IR_BRIDGE, DIGOO, KA10, ZX2820, MI_DESK_LAMP, SP10, WAGA, SYF05, SONOFF_L1, + SONOFF_IFAN03, EXS_DIMMER, PWM_DIMMER, SONOFF_D1, + MAXMODULE}; + +#define USER_MODULE 255 + +const char kModuleNames[] PROGMEM = + "Sonoff Basic|Sonoff RF|Sonoff SV|Sonoff TH|Sonoff Dual|Sonoff Pow|Sonoff 4CH|Sonoff S2X|Slampher|Sonoff Touch|" + "Sonoff LED|1 Channel|4 Channel|Motor C/AC|ElectroDragon|EXS Relay(s)|WiOn|Generic|Sonoff Dev|H801|" + "Sonoff SC|Sonoff BN-SZ|Sonoff 4CH Pro|Huafan SS|Sonoff Bridge|Sonoff B1|AiLight|Sonoff T1 1CH|Sonoff T1 2CH|Sonoff T1 3CH|" + "Supla Espablo|Witty Cloud|Yunshan Relay|MagicHome|Luani HVIO|KMC 70011|Arilux LC01|Arilux LC11|Sonoff Dual R2|Arilux LC06|" + "Sonoff S31|Zengge WF017|Sonoff Pow R2|Sonoff iFan02|BlitzWolf SHP|Shelly 1|Shelly 2|Xiaomi Philips|Neo Coolcam|ESP Switch|" + "OBI Socket|Teckin|AplicWDP303075|Tuya MCU|Gosund SP1 v23|ARMTR Dimmer|SK03 Outdoor|PS-16-DZ|Teckin US|Manzoku strip|" + "OBI Socket 2|YTF IR Bridge|Digoo DG-SP202|KA10|Luminea ZX2820|Mi Desk Lamp|SP10|WAGA CHCZ02MB|SYF05|Sonoff L1|" + "Sonoff iFan03|EXS Dimmer|PWM Dimmer|Sonoff D1" + ; + const uint8_t kModuleNiceList[] PROGMEM = { SONOFF_BASIC, // Sonoff Relay Devices SONOFF_RF, @@ -910,1368 +856,1368 @@ const uint8_t kModuleNiceList[] PROGMEM = { // Default module settings const mytmplt kModules[MAXMODULE] PROGMEM = { - { "Sonoff Basic", // SONOFF_BASIC - Sonoff Basic (ESP8266) - GPIO_KEY1, // GPIO00 Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Only available on newer Sonoff Basic R2 V1 - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_USER, // GPIO04 Optional sensor - 0, // GPIO05 - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status - GPIO_USER, // GPIO14 Optional sensor - 0, // GPIO15 - 0, // GPIO16 - 0 // ADC0 Analog input + { // SONOFF_BASIC - Sonoff Basic (ESP8266) + GPIO_KEY1, // GPIO00 Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Only available on newer Sonoff Basic R2 V1 + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_USER, // GPIO04 Optional sensor + 0, // GPIO05 + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status + GPIO_USER, // GPIO14 Optional sensor + 0, // GPIO15 + 0, // GPIO16 + 0 // ADC0 Analog input }, - { "Sonoff RF", // SONOFF_RF - Sonoff RF (ESP8266) - GPIO_KEY1, // GPIO00 Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Optional sensor - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_USER, // GPIO04 Optional sensor - 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status - GPIO_USER, // GPIO14 Optional sensor - 0, 0, 0 + { // SONOFF_RF - Sonoff RF (ESP8266) + GPIO_KEY1, // GPIO00 Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Optional sensor + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_USER, // GPIO04 Optional sensor + 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status + GPIO_USER, // GPIO14 Optional sensor + 0, 0, 0 }, - { "Sonoff SV", // SONOFF_SV - Sonoff SV (ESP8266) - GPIO_KEY1, // GPIO00 Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - 0, - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_USER, // GPIO04 Optional sensor - GPIO_USER, // GPIO05 Optional sensor - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status - GPIO_USER, // GPIO14 Optional sensor - 0, 0, - ADC0_USER // ADC0 Analog input + { // SONOFF_SV - Sonoff SV (ESP8266) + GPIO_KEY1, // GPIO00 Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + 0, + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_USER, // GPIO04 Optional sensor + GPIO_USER, // GPIO05 Optional sensor + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status + GPIO_USER, // GPIO14 Optional sensor + 0, 0, + ADC0_USER // ADC0 Analog input }, - { "Sonoff TH", // SONOFF_TH - Sonoff TH10/16 (ESP8266) - GPIO_KEY1, // GPIO00 Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - 0, - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_USER, // GPIO04 Optional sensor - 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status - GPIO_USER, // GPIO14 Optional sensor - 0, 0, 0 + { // SONOFF_TH - Sonoff TH10/16 (ESP8266) + GPIO_KEY1, // GPIO00 Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + 0, + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_USER, // GPIO04 Optional sensor + 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status + GPIO_USER, // GPIO14 Optional sensor + 0, 0, 0 }, - { "Sonoff Dual", // SONOFF_DUAL - Sonoff Dual (ESP8266) - 0, - GPIO_TXD, // GPIO01 Relay control - 0, - GPIO_RXD, // GPIO03 Relay control - GPIO_USER, // GPIO04 Optional sensor - 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - GPIO_USER, // GPIO14 Optional sensor - 0, 0, 0 + { // SONOFF_DUAL - Sonoff Dual (ESP8266) + 0, + GPIO_TXD, // GPIO01 Relay control + 0, + GPIO_RXD, // GPIO03 Relay control + GPIO_USER, // GPIO04 Optional sensor + 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + 0, + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + GPIO_USER, // GPIO14 Optional sensor + 0, 0, 0 }, - { "Sonoff Pow", // SONOFF_POW - Sonoff Pow (ESP8266 - HLW8012) - GPIO_KEY1, // GPIO00 Button - 0, 0, 0, 0, - GPIO_NRG_SEL, // GPIO05 HLW8012 Sel output (1 = Voltage) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_NRG_CF1, // GPIO13 HLW8012 CF1 voltage / current - GPIO_HLW_CF, // GPIO14 HLW8012 CF power - GPIO_LED1, // GPIO15 Blue Led (0 = On, 1 = Off) - Link and Power status - 0, 0 + { // SONOFF_POW - Sonoff Pow (ESP8266 - HLW8012) + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, 0, + GPIO_NRG_SEL, // GPIO05 HLW8012 Sel output (1 = Voltage) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_NRG_CF1, // GPIO13 HLW8012 CF1 voltage / current + GPIO_HLW_CF, // GPIO14 HLW8012 CF power + GPIO_LED1, // GPIO15 Blue Led (0 = On, 1 = Off) - Link and Power status + 0, 0 }, - { "Sonoff 4CH", // SONOFF_4CH - Sonoff 4CH (ESP8285) - GPIO_KEY1, // GPIO00 Button 1 - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Optional sensor - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_REL3, // GPIO04 Sonoff 4CH Red Led and Relay 3 (0 = Off, 1 = On) - GPIO_REL2, // GPIO05 Sonoff 4CH Red Led and Relay 2 (0 = Off, 1 = On) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - GPIO_KEY2, // GPIO09 Button 2 - GPIO_KEY3, // GPIO10 Button 3 - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay 1 (0 = Off, 1 = On) - Link and Power status - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - GPIO_KEY4, // GPIO14 Button 4 - GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On) - 0, 0 + { // SONOFF_4CH - Sonoff 4CH (ESP8285) + GPIO_KEY1, // GPIO00 Button 1 + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Optional sensor + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_REL3, // GPIO04 Sonoff 4CH Red Led and Relay 3 (0 = Off, 1 = On) + GPIO_REL2, // GPIO05 Sonoff 4CH Red Led and Relay 2 (0 = Off, 1 = On) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + GPIO_KEY2, // GPIO09 Button 2 + GPIO_KEY3, // GPIO10 Button 3 + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay 1 (0 = Off, 1 = On) - Link and Power status + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + GPIO_KEY4, // GPIO14 Button 4 + GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On) + 0, 0 }, - { "Sonoff S2X", // SONOFF_S2X - Sonoff S20, S22 and S26 Smart Socket (ESP8266) - GPIO_KEY1, // GPIO00 Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Optional sensor - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - Link and Power status - GPIO_LED1_INV, // GPIO13 Green/Blue Led (0 = On, 1 = Off) - 0, 0, 0, 0 + { // SONOFF_S2X - Sonoff S20, S22 and S26 Smart Socket (ESP8266) + GPIO_KEY1, // GPIO00 Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Optional sensor + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - Link and Power status + GPIO_LED1_INV, // GPIO13 Green/Blue Led (0 = On, 1 = Off) + 0, 0, 0, 0 }, - { "Slampher", // SLAMPHER - Slampher (ESP8266) - GPIO_KEY1, // GPIO00 Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - 0, - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // SLAMPHER - Slampher (ESP8266) + GPIO_KEY1, // GPIO00 Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + 0, + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Sonoff Touch", // SONOFF_TOUCH - Sonoff Touch (ESP8285) - GPIO_KEY1, // GPIO00 Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - 0, - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 - 0, // GPIO10 - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - Link and Power status - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - 0, 0, 0, 0 + { // SONOFF_TOUCH - Sonoff Touch (ESP8285) + GPIO_KEY1, // GPIO00 Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + 0, + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - Link and Power status + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + 0, 0, 0, 0 }, - { "Sonoff LED", // SONOFF_LED - Sonoff LED (ESP8266) - GPIO_KEY1, // GPIO00 Button - 0, 0, 0, - GPIO_USER, // GPIO04 Optional sensor (PWM3 Green) - GPIO_USER, // GPIO05 Optional sensor (PWM2 Red) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM1, // GPIO12 Cold light (PWM0 Cold) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - GPIO_PWM2, // GPIO14 Warm light (PWM1 Warm) - GPIO_USER, // GPIO15 Optional sensor (PWM4 Blue) - 0, 0 + { // SONOFF_LED - Sonoff LED (ESP8266) + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, + GPIO_USER, // GPIO04 Optional sensor (PWM3 Green) + GPIO_USER, // GPIO05 Optional sensor (PWM2 Red) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_PWM1, // GPIO12 Cold light (PWM0 Cold) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + GPIO_PWM2, // GPIO14 Warm light (PWM1 Warm) + GPIO_USER, // GPIO15 Optional sensor (PWM4 Blue) + 0, 0 }, - { "1 Channel", // CH1 - 1 Channel Inching/Latching Relay using (PSA-B01 - ESP8266 and PSF-B01 - ESP8285) - GPIO_KEY1, // GPIO00 Button - 0, 0, 0, 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // CH1 - 1 Channel Inching/Latching Relay using (PSA-B01 - ESP8266 and PSF-B01 - ESP8285) + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "4 Channel", // CH4 - 4 Channel Inching/Latching Relays (ESP8266) - 0, - GPIO_TXD, // GPIO01 Relay control - 0, - GPIO_RXD, // GPIO03 Relay control - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // CH4 - 4 Channel Inching/Latching Relays (ESP8266) + 0, + GPIO_TXD, // GPIO01 Relay control + 0, + GPIO_RXD, // GPIO03 Relay control + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + 0, + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Motor C/AC", // MOTOR - Motor Clockwise / Anti clockwise (PSA-B01 - ESP8266) - GPIO_KEY1, // GPIO00 Button - 0, 0, 0, 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // MOTOR - Motor Clockwise / Anti clockwise (PSA-B01 - ESP8266) + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "ElectroDragon", // ELECTRODRAGON - ElectroDragon IoT Relay Board (ESP8266) - GPIO_KEY2, // GPIO00 Button 2 - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_KEY1, // GPIO02 Button 1 - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_USER, // GPIO04 Optional sensor - GPIO_USER, // GPIO05 Optional sensor - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL2, // GPIO12 Red Led and Relay 2 (0 = Off, 1 = On) - GPIO_REL1, // GPIO13 Red Led and Relay 1 (0 = Off, 1 = On) - GPIO_USER, // GPIO14 Optional sensor - GPIO_USER, // GPIO15 Optional sensor - GPIO_LED1, // GPIO16 Green/Blue Led (1 = On, 0 = Off) - Link and Power status - ADC0_USER // ADC0 A0 Analog input + { // ELECTRODRAGON - ElectroDragon IoT Relay Board (ESP8266) + GPIO_KEY2, // GPIO00 Button 2 + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_KEY1, // GPIO02 Button 1 + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_USER, // GPIO04 Optional sensor + GPIO_USER, // GPIO05 Optional sensor + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL2, // GPIO12 Red Led and Relay 2 (0 = Off, 1 = On) + GPIO_REL1, // GPIO13 Red Led and Relay 1 (0 = Off, 1 = On) + GPIO_USER, // GPIO14 Optional sensor + GPIO_USER, // GPIO15 Optional sensor + GPIO_LED1, // GPIO16 Green/Blue Led (1 = On, 0 = Off) - Link and Power status + ADC0_USER // ADC0 A0 Analog input }, - { "EXS Relay(s)", // EXS_RELAY - ES-Store Latching relay(s) (ESP8266) - // https://ex-store.de/ESP8266-WiFi-Relay-V31 - // V3.1 Module Pin 1 VCC 3V3, Module Pin 6 GND - // https://ex-store.de/2-Kanal-WiFi-WLan-Relay-V5-Blackline-fuer-Unterputzmontage - GPIO_USER, // GPIO00 V3.1 Module Pin 8 - V5.0 Module Pin 4 - GPIO_USER, // GPIO01 UART0_TXD V3.1 Module Pin 2 - V5.0 Module Pin 3 - GPIO_USER, // GPIO02 V3.1 Module Pin 7 - GPIO_USER, // GPIO03 UART0_RXD V3.1 Module Pin 3 - GPIO_USER, // GPIO04 V3.1 Module Pin 10 - V5.0 Module Pin 2 - GPIO_USER, // GPIO05 V3.1 Module Pin 9 - V5.0 Module Pin 1 - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Relay1 ( 1 = Off) - GPIO_REL2, // GPIO13 Relay1 ( 1 = On) - GPIO_USER, // GPIO14 V3.1 Module Pin 5 - V5.0 GPIO_REL3_INV Relay2 ( 1 = Off) - GPIO_LED1, // GPIO15 V5.0 LED1 - Link and Power status - GPIO_USER, // GPIO16 V3.1 Module Pin 4 - V5.0 GPIO_REL4_INV Relay2 ( 1 = On) - 0 + { // EXS_RELAY - ES-Store Latching relay(s) (ESP8266) + // https://ex-store.de/ESP8266-WiFi-Relay-V31 + // V3.1 Module Pin 1 VCC 3V3, Module Pin 6 GND + // https://ex-store.de/2-Kanal-WiFi-WLan-Relay-V5-Blackline-fuer-Unterputzmontage + GPIO_USER, // GPIO00 V3.1 Module Pin 8 - V5.0 Module Pin 4 + GPIO_USER, // GPIO01 UART0_TXD V3.1 Module Pin 2 - V5.0 Module Pin 3 + GPIO_USER, // GPIO02 V3.1 Module Pin 7 + GPIO_USER, // GPIO03 UART0_RXD V3.1 Module Pin 3 + GPIO_USER, // GPIO04 V3.1 Module Pin 10 - V5.0 Module Pin 2 + GPIO_USER, // GPIO05 V3.1 Module Pin 9 - V5.0 Module Pin 1 + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Relay1 ( 1 = Off) + GPIO_REL2, // GPIO13 Relay1 ( 1 = On) + GPIO_USER, // GPIO14 V3.1 Module Pin 5 - V5.0 GPIO_REL3_INV Relay2 ( 1 = Off) + GPIO_LED1, // GPIO15 V5.0 LED1 - Link and Power status + GPIO_USER, // GPIO16 V3.1 Module Pin 4 - V5.0 GPIO_REL4_INV Relay2 ( 1 = On) + 0 }, - { "WiOn", // WION - Indoor Tap (ESP8266) - // https://www.amazon.com/gp/product/B00ZYLUBJU/ref=s9_acsd_al_bw_c_x_3_w - GPIO_USER, // GPIO00 Optional sensor (pm clock) - 0, - GPIO_LED1, // GPIO02 Green Led (1 = On, 0 = Off) - Link and Power status - 0, 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, // GPIO12 Optional sensor (pm data) - GPIO_KEY1, // GPIO13 Button - 0, - GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) - 0, 0 + { // WION - Indoor Tap (ESP8266) + // https://www.amazon.com/gp/product/B00ZYLUBJU/ref=s9_acsd_al_bw_c_x_3_w + GPIO_USER, // GPIO00 Optional sensor (pm clock) + 0, + GPIO_LED1, // GPIO02 Green Led (1 = On, 0 = Off) - Link and Power status + 0, 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, // GPIO12 Optional sensor (pm data) + GPIO_KEY1, // GPIO13 Button + 0, + GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) + 0, 0 }, - { "Generic", // WEMOS - Any ESP8266/ESP8285 device like WeMos and NodeMCU hardware (ESP8266) - GPIO_USER, // GPIO00 D3 Wemos Button Shield - GPIO_USER, // GPIO01 TX Serial RXD - GPIO_USER, // GPIO02 D4 Wemos DHT Shield - GPIO_USER, // GPIO03 RX Serial TXD and Optional sensor - GPIO_USER, // GPIO04 D2 Wemos I2C SDA - GPIO_USER, // GPIO05 D1 Wemos I2C SCL / Wemos Relay Shield (0 = Off, 1 = On) / Wemos WS2812B RGB led Shield - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - GPIO_USER, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - GPIO_USER, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, // GPIO12 D6 - GPIO_USER, // GPIO13 D7 - GPIO_USER, // GPIO14 D5 - GPIO_USER, // GPIO15 D8 - GPIO_USER, // GPIO16 D0 Wemos Wake - ADC0_USER // ADC0 A0 Analog input + { // WEMOS - Any ESP8266/ESP8285 device like WeMos and NodeMCU hardware (ESP8266) + GPIO_USER, // GPIO00 D3 Wemos Button Shield + GPIO_USER, // GPIO01 TX Serial RXD + GPIO_USER, // GPIO02 D4 Wemos DHT Shield + GPIO_USER, // GPIO03 RX Serial TXD and Optional sensor + GPIO_USER, // GPIO04 D2 Wemos I2C SDA + GPIO_USER, // GPIO05 D1 Wemos I2C SCL / Wemos Relay Shield (0 = Off, 1 = On) / Wemos WS2812B RGB led Shield + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + GPIO_USER, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + GPIO_USER, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, // GPIO12 D6 + GPIO_USER, // GPIO13 D7 + GPIO_USER, // GPIO14 D5 + GPIO_USER, // GPIO15 D8 + GPIO_USER, // GPIO16 D0 Wemos Wake + ADC0_USER // ADC0 A0 Analog input }, - { "Sonoff Dev", // SONOFF_DEV - Sonoff Dev (ESP8266) - GPIO_KEY1, // GPIO00 E-FW Button - GPIO_USER, // GPIO01 TX Serial RXD and Optional sensor - 0, // GPIO02 - GPIO_USER, // GPIO03 RX Serial TXD and Optional sensor - GPIO_USER, // GPIO04 Optional sensor - GPIO_USER, // GPIO05 Optional sensor - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, // GPIO12 - GPIO_USER, // GPIO13 BLUE LED - GPIO_USER, // GPIO14 Optional sensor - 0, // GPIO15 - 0, // GPIO16 - ADC0_USER // ADC0 A0 Analog input + { // SONOFF_DEV - Sonoff Dev (ESP8266) + GPIO_KEY1, // GPIO00 E-FW Button + GPIO_USER, // GPIO01 TX Serial RXD and Optional sensor + 0, // GPIO02 + GPIO_USER, // GPIO03 RX Serial TXD and Optional sensor + GPIO_USER, // GPIO04 Optional sensor + GPIO_USER, // GPIO05 Optional sensor + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, // GPIO12 + GPIO_USER, // GPIO13 BLUE LED + GPIO_USER, // GPIO14 Optional sensor + 0, // GPIO15 + 0, // GPIO16 + ADC0_USER // ADC0 A0 Analog input }, - { "H801", // H801 - Lixada H801 Wifi (ESP8266) - GPIO_USER, // GPIO00 E-FW Button - GPIO_LED1, // GPIO01 Green LED - Link and Power status - GPIO_USER, // GPIO02 TX and Optional sensor - Pin next to TX on the PCB - GPIO_USER, // GPIO03 RX and Optional sensor - Pin next to GND on the PCB - GPIO_PWM5, // GPIO04 W2 - PWM5 - GPIO_LED2_INV, // GPIO05 Red LED - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM3, // GPIO12 Blue - GPIO_PWM2, // GPIO13 Green - GPIO_PWM4, // GPIO14 W1 - PWM4 - GPIO_PWM1, // GPIO15 Red - 0, 0 + { // H801 - Lixada H801 Wifi (ESP8266) + GPIO_USER, // GPIO00 E-FW Button + GPIO_LED1, // GPIO01 Green LED - Link and Power status + GPIO_USER, // GPIO02 TX and Optional sensor - Pin next to TX on the PCB + GPIO_USER, // GPIO03 RX and Optional sensor - Pin next to GND on the PCB + GPIO_PWM5, // GPIO04 W2 - PWM5 + GPIO_LED2_INV, // GPIO05 Red LED + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_PWM3, // GPIO12 Blue + GPIO_PWM2, // GPIO13 Green + GPIO_PWM4, // GPIO14 W1 - PWM4 + GPIO_PWM1, // GPIO15 Red + 0, 0 }, - { "Sonoff SC", // SONOFF_SC - onoff SC (ESP8266) - GPIO_KEY1, // GPIO00 Button - GPIO_TXD, // GPIO01 RXD to ATMEGA328P - GPIO_USER, // GPIO02 Optional sensor - GPIO_RXD, // GPIO03 TXD to ATMEGA328P - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // SONOFF_SC - onoff SC (ESP8266) + GPIO_KEY1, // GPIO00 Button + GPIO_TXD, // GPIO01 RXD to ATMEGA328P + GPIO_USER, // GPIO02 Optional sensor + GPIO_RXD, // GPIO03 TXD to ATMEGA328P + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + 0, + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Sonoff BN-SZ", // SONOFF_BN - Sonoff BN-SZ01 Ceiling led (ESP8285) - 0, 0, 0, 0, 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 - 0, // GPIO10 - // GPIO11 (SD_CMD Flash) - GPIO_PWM1, // GPIO12 Light - GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // SONOFF_BN - Sonoff BN-SZ01 Ceiling led (ESP8285) + 0, 0, 0, 0, 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) + GPIO_PWM1, // GPIO12 Light + GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Sonoff 4CH Pro", // SONOFF_4CHPRO - Sonoff 4CH Pro (ESP8285) - GPIO_KEY1, // GPIO00 Button 1 - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Optional sensor - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_REL3, // GPIO04 Sonoff 4CH Red Led and Relay 3 (0 = Off, 1 = On) - GPIO_REL2, // GPIO05 Sonoff 4CH Red Led and Relay 2 (0 = Off, 1 = On) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - GPIO_KEY2, // GPIO09 Button 2 - GPIO_KEY3, // GPIO10 Button 3 - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay 1 (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - GPIO_KEY4, // GPIO14 Button 4 - GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On) - 0, 0 + { // SONOFF_4CHPRO - Sonoff 4CH Pro (ESP8285) + GPIO_KEY1, // GPIO00 Button 1 + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Optional sensor + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_REL3, // GPIO04 Sonoff 4CH Red Led and Relay 3 (0 = Off, 1 = On) + GPIO_REL2, // GPIO05 Sonoff 4CH Red Led and Relay 2 (0 = Off, 1 = On) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + GPIO_KEY2, // GPIO09 Button 2 + GPIO_KEY3, // GPIO10 Button 3 + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay 1 (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + GPIO_KEY4, // GPIO14 Button 4 + GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On) + 0, 0 }, - { "Huafan SS", // HUAFAN_SS - Hua Fan Smart Socket (ESP8266) - like Sonoff Pow - GPIO_LEDLNK_INV, // GPIO00 Blue Led (0 = On, 1 = Off) - Link status - 0, 0, - GPIO_LED1_INV, // GPIO03 Red Led (0 = On, 1 = Off) - Power status - GPIO_KEY1, // GPIO04 Button - GPIO_REL1_INV, // GPIO05 Relay (0 = On, 1 = Off) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_CF1, // GPIO12 HLW8012 CF1 voltage / current - GPIO_NRG_SEL, // GPIO13 HLW8012 Sel output (1 = Voltage) - GPIO_HLW_CF, // GPIO14 HLW8012 CF power - 0, 0, 0 + { // HUAFAN_SS - Hua Fan Smart Socket (ESP8266) - like Sonoff Pow + GPIO_LEDLNK_INV, // GPIO00 Blue Led (0 = On, 1 = Off) - Link status + 0, 0, + GPIO_LED1_INV, // GPIO03 Red Led (0 = On, 1 = Off) - Power status + GPIO_KEY1, // GPIO04 Button + GPIO_REL1_INV, // GPIO05 Relay (0 = On, 1 = Off) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_CF1, // GPIO12 HLW8012 CF1 voltage / current + GPIO_NRG_SEL, // GPIO13 HLW8012 Sel output (1 = Voltage) + GPIO_HLW_CF, // GPIO14 HLW8012 CF power + 0, 0, 0 }, - { "Sonoff Bridge", // SONOFF_BRIDGE - Sonoff RF Bridge 433 (ESP8285) - GPIO_KEY1, // GPIO00 Button - GPIO_TXD, // GPIO01 RF bridge control - GPIO_USER, // GPIO02 Optional sensor - GPIO_RXD, // GPIO03 RF bridge control - GPIO_USER, // GPIO04 Optional sensor - GPIO_USER, // GPIO05 Optional sensor - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 - 0, // GPIO10 - // GPIO11 (SD_CMD Flash) - GPIO_USER, // GPIO12 Optional sensor - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - GPIO_USER, // GPIO14 Optional sensor - 0, 0, 0 + { // SONOFF_BRIDGE - Sonoff RF Bridge 433 (ESP8285) + GPIO_KEY1, // GPIO00 Button + GPIO_TXD, // GPIO01 RF bridge control + GPIO_USER, // GPIO02 Optional sensor + GPIO_RXD, // GPIO03 RF bridge control + GPIO_USER, // GPIO04 Optional sensor + GPIO_USER, // GPIO05 Optional sensor + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) + GPIO_USER, // GPIO12 Optional sensor + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + GPIO_USER, // GPIO14 Optional sensor + 0, 0, 0 }, - { "Sonoff B1", // SONOFF_B1 - Sonoff B1 (ESP8285 - my9231) - GPIO_KEY1, // GPIO00 Pad - GPIO_USER, // GPIO01 Serial RXD and Optional sensor pad - GPIO_USER, // GPIO02 Optional sensor SDA pad - GPIO_USER, // GPIO03 Serial TXD and Optional sensor pad - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 - 0, // GPIO10 - // GPIO11 (SD_CMD Flash) - GPIO_DI, // GPIO12 my9231 DI - 0, - GPIO_DCKI, // GPIO14 my9231 DCKI - 0, 0, 0 + { // SONOFF_B1 - Sonoff B1 (ESP8285 - my9231) + GPIO_KEY1, // GPIO00 Pad + GPIO_USER, // GPIO01 Serial RXD and Optional sensor pad + GPIO_USER, // GPIO02 Optional sensor SDA pad + GPIO_USER, // GPIO03 Serial TXD and Optional sensor pad + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) + GPIO_DI, // GPIO12 my9231 DI + 0, + GPIO_DCKI, // GPIO14 my9231 DCKI + 0, 0, 0 }, - { "AiLight", // AILIGHT - Ai-Thinker RGBW led (ESP8266 - my9291) - GPIO_KEY1, // GPIO00 Pad - GPIO_USER, // GPIO01 Serial RXD and Optional sensor pad - GPIO_USER, // GPIO02 Optional sensor SDA pad - GPIO_USER, // GPIO03 Serial TXD and Optional sensor pad - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, - GPIO_DI, // GPIO13 my9291 DI - 0, - GPIO_DCKI, // GPIO15 my9291 DCKI - 0, 0 + { // AILIGHT - Ai-Thinker RGBW led (ESP8266 - my9291) + GPIO_KEY1, // GPIO00 Pad + GPIO_USER, // GPIO01 Serial RXD and Optional sensor pad + GPIO_USER, // GPIO02 Optional sensor SDA pad + GPIO_USER, // GPIO03 Serial TXD and Optional sensor pad + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + 0, + GPIO_DI, // GPIO13 my9291 DI + 0, + GPIO_DCKI, // GPIO15 my9291 DCKI + 0, 0 }, - { "Sonoff T1 1CH", // SONOFF_T11 - Sonoff T1 1CH (ESP8285) - GPIO_KEY1, // GPIO00 Button 1 - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5) - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 - 0, // GPIO10 - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // SONOFF_T11 - Sonoff T1 1CH (ESP8285) + GPIO_KEY1, // GPIO00 Button 1 + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5) + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Sonoff T1 2CH", // SONOFF_T12 - Sonoff T1 2CH (ESP8285) - GPIO_KEY1, // GPIO00 Button 1 - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5) - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - 0, - GPIO_REL2, // GPIO05 Blue Led and Relay 2 (0 = Off, 1 = On) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - GPIO_KEY2, // GPIO09 Button 2 - 0, // GPIO10 - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // SONOFF_T12 - Sonoff T1 2CH (ESP8285) + GPIO_KEY1, // GPIO00 Button 1 + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5) + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + 0, + GPIO_REL2, // GPIO05 Blue Led and Relay 2 (0 = Off, 1 = On) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + GPIO_KEY2, // GPIO09 Button 2 + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Sonoff T1 3CH", // SONOFF_T13 - Sonoff T1 3CH (ESP8285) - GPIO_KEY1, // GPIO00 Button 1 - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5) - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_REL3, // GPIO04 Blue Led and Relay 3 (0 = Off, 1 = On) - GPIO_REL2, // GPIO05 Blue Led and Relay 2 (0 = Off, 1 = On) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - GPIO_KEY2, // GPIO09 Button 2 - GPIO_KEY3, // GPIO10 Button 3 - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // SONOFF_T13 - Sonoff T1 3CH (ESP8285) + GPIO_KEY1, // GPIO00 Button 1 + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5) + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_REL3, // GPIO04 Blue Led and Relay 3 (0 = Off, 1 = On) + GPIO_REL2, // GPIO05 Blue Led and Relay 2 (0 = Off, 1 = On) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + GPIO_KEY2, // GPIO09 Button 2 + GPIO_KEY3, // GPIO10 Button 3 + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Supla Espablo", // SUPLA1 - Supla Espablo (ESP8266) - // http://www.wykop.pl/ramka/3325399/diy-supla-do-puszki-instalacyjnej-podtynkowej-supla-org/ - 0, // GPIO00 Flash jumper - GPIO_USER, // GPIO01 Serial RXD and Optional sensor + { // SUPLA1 - Supla Espablo (ESP8266) + // http://www.wykop.pl/ramka/3325399/diy-supla-do-puszki-instalacyjnej-podtynkowej-supla-org/ + 0, // GPIO00 Flash jumper + GPIO_USER, // GPIO01 Serial RXD and Optional sensor #ifdef USE_DS18x20 - GPIO_DSB, // GPIO02 DS18B20 sensor + GPIO_DSB, // GPIO02 DS18B20 sensor #else - GPIO_USER, // GPIO02 Optional sensor + GPIO_USER, // GPIO02 Optional sensor #endif - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_KEY1, // GPIO04 Button 1 - GPIO_REL1, // GPIO05 Relay 1 (0 = Off, 1 = On) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, // GPIO12 Optional sensor - GPIO_REL2, // GPIO13 Relay 2 (0 = Off, 1 = On) - GPIO_USER, // GPIO14 Optional sensor - 0, - GPIO_LED1, // GPIO16 Led (1 = On, 0 = Off) - Link and Power status - ADC0_USER // ADC0 A0 Analog input + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_KEY1, // GPIO04 Button 1 + GPIO_REL1, // GPIO05 Relay 1 (0 = Off, 1 = On) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, // GPIO12 Optional sensor + GPIO_REL2, // GPIO13 Relay 2 (0 = Off, 1 = On) + GPIO_USER, // GPIO14 Optional sensor + 0, + GPIO_LED1, // GPIO16 Led (1 = On, 0 = Off) - Link and Power status + ADC0_USER // ADC0 A0 Analog input }, - { "Witty Cloud", // WITTY - Witty Cloud Dev Board (ESP8266) - // https://www.aliexpress.com/item/ESP8266-serial-WIFI-Witty-cloud-Development-Board-ESP-12F-module-MINI-nodemcu/32643464555.html - GPIO_USER, // GPIO00 D3 flash push button on interface board - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_LED1_INV, // GPIO02 D4 Blue Led (0 = On, 1 = Off) on ESP-12F - Link and Power status - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_KEY1, // GPIO04 D2 push button on ESP-12F board - GPIO_USER, // GPIO05 D1 optional sensor - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM2, // GPIO12 D6 RGB LED Green - GPIO_PWM3, // GPIO13 D7 RGB LED Blue - GPIO_USER, // GPIO14 D5 optional sensor - GPIO_PWM1, // GPIO15 D8 RGB LED Red - GPIO_USER, // GPIO16 D0 optional sensor - ADC0_USER // ADC0 A0 Light sensor / Requires USE_ADC_VCC in user_config.h to be disabled + { // WITTY - Witty Cloud Dev Board (ESP8266) + // https://www.aliexpress.com/item/ESP8266-serial-WIFI-Witty-cloud-Development-Board-ESP-12F-module-MINI-nodemcu/32643464555.html + GPIO_USER, // GPIO00 D3 flash push button on interface board + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_LED1_INV, // GPIO02 D4 Blue Led (0 = On, 1 = Off) on ESP-12F - Link and Power status + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_KEY1, // GPIO04 D2 push button on ESP-12F board + GPIO_USER, // GPIO05 D1 optional sensor + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_PWM2, // GPIO12 D6 RGB LED Green + GPIO_PWM3, // GPIO13 D7 RGB LED Blue + GPIO_USER, // GPIO14 D5 optional sensor + GPIO_PWM1, // GPIO15 D8 RGB LED Red + GPIO_USER, // GPIO16 D0 optional sensor + ADC0_USER // ADC0 A0 Light sensor / Requires USE_ADC_VCC in user_config.h to be disabled }, - { "Yunshan Relay", // YUNSHAN - Yunshan Wifi Relay (ESP8266) - // https://www.ebay.com/p/Esp8266-220v-10a-Network-Relay-WiFi-Module/1369583381 - // Schematics and Info https://ucexperiment.wordpress.com/2016/12/18/yunshan-esp8266-250v-15a-acdc-network-wifi-relay-module/ - 0, // GPIO00 Flash jumper - Module Pin 8 - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - Module Pin 2 - GPIO_LED1_INV, // GPIO02 Blue Led (0 = On, 1 = Off) on ESP-12F - Module Pin 7 - Link and Power status - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - Module Pin 3 - GPIO_REL1, // GPIO04 Red Led and Relay (0 = Off, 1 = On) - Module Pin 10 - GPIO_KEY1, // GPIO05 Blue Led and OptoCoupler input - Module Pin 9 - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, 0, 0, 0, 0, 0 + { // YUNSHAN - Yunshan Wifi Relay (ESP8266) + // https://www.ebay.com/p/Esp8266-220v-10a-Network-Relay-WiFi-Module/1369583381 + // Schematics and Info https://ucexperiment.wordpress.com/2016/12/18/yunshan-esp8266-250v-15a-acdc-network-wifi-relay-module/ + 0, // GPIO00 Flash jumper - Module Pin 8 + GPIO_USER, // GPIO01 Serial RXD and Optional sensor - Module Pin 2 + GPIO_LED1_INV, // GPIO02 Blue Led (0 = On, 1 = Off) on ESP-12F - Module Pin 7 - Link and Power status + GPIO_USER, // GPIO03 Serial TXD and Optional sensor - Module Pin 3 + GPIO_REL1, // GPIO04 Red Led and Relay (0 = Off, 1 = On) - Module Pin 10 + GPIO_KEY1, // GPIO05 Blue Led and OptoCoupler input - Module Pin 9 + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + 0, 0, 0, 0, 0, 0 }, - { "MagicHome", // MAGICHOME - Magic Home (aka Flux-light) (ESP8266) and Arilux LC10 (ESP8285) - // https://www.aliexpress.com/item/Magic-Home-Mini-RGB-RGBW-Wifi-Controller-For-Led-Strip-Panel-light-Timing-Function-16million-colors/32686853650.html - 0, - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_LED1_INV, // GPIO02 Blue onboard LED - Link and Power status - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_ARIRFRCV, // GPIO04 IR or RF receiver (optional) (Arilux LC10) - GPIO_PWM2, // GPIO05 RGB LED Green - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM3, // GPIO12 RGB LED Blue - GPIO_USER, // GPIO13 RGBW LED White (optional - set to PWM4 for Cold White or Warm White as used on Arilux LC10) - GPIO_PWM1, // GPIO14 RGB LED Red - GPIO_ARIRFSEL, // GPIO15 RF receiver control (Arilux LC10) - 0, 0 + { // MAGICHOME - Magic Home (aka Flux-light) (ESP8266) and Arilux LC10 (ESP8285) + // https://www.aliexpress.com/item/Magic-Home-Mini-RGB-RGBW-Wifi-Controller-For-Led-Strip-Panel-light-Timing-Function-16million-colors/32686853650.html + 0, + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_LED1_INV, // GPIO02 Blue onboard LED - Link and Power status + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_ARIRFRCV, // GPIO04 IR or RF receiver (optional) (Arilux LC10) + GPIO_PWM2, // GPIO05 RGB LED Green + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_PWM3, // GPIO12 RGB LED Blue + GPIO_USER, // GPIO13 RGBW LED White (optional - set to PWM4 for Cold White or Warm White as used on Arilux LC10) + GPIO_PWM1, // GPIO14 RGB LED Red + GPIO_ARIRFSEL, // GPIO15 RF receiver control (Arilux LC10) + 0, 0 }, - { "Luani HVIO", // LUANIHVIO - ESP8266_HVIO - // https://luani.de/projekte/esp8266-hvio/ - 0, // GPIO00 Flash jumper - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Optional sensor / I2C SDA pad - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_REL1, // GPIO04 Relay 1 (0 = Off, 1 = On) - GPIO_REL2, // GPIO05 Relay 2 (0 = Off, 1 = On) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_SWT1, // GPIO12 External input 1 (0 = On, 1 = Off) - GPIO_SWT2, // GPIO13 External input 2 (0 = On, 1 = Off) - GPIO_USER, // GPIO14 Optional sensor / I2C SCL pad - GPIO_LED1, // GPIO15 Led (1 = On, 0 = Off) - Link and Power status - 0, - ADC0_USER // ADC0 A0 Analog input + { // LUANIHVIO - ESP8266_HVIO + // https://luani.de/projekte/esp8266-hvio/ + 0, // GPIO00 Flash jumper + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Optional sensor / I2C SDA pad + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_REL1, // GPIO04 Relay 1 (0 = Off, 1 = On) + GPIO_REL2, // GPIO05 Relay 2 (0 = Off, 1 = On) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_SWT1, // GPIO12 External input 1 (0 = On, 1 = Off) + GPIO_SWT2, // GPIO13 External input 2 (0 = On, 1 = Off) + GPIO_USER, // GPIO14 Optional sensor / I2C SCL pad + GPIO_LED1, // GPIO15 Led (1 = On, 0 = Off) - Link and Power status + 0, + ADC0_USER // ADC0 A0 Analog input }, - { "KMC 70011", // KMC_70011 - KMC 70011 - // https://www.amazon.com/KMC-Timing-Monitoring-Network-125V-240V/dp/B06XRX2GTQ - GPIO_KEY1, // GPIO00 Button - 0, 0, 0, - GPIO_HLW_CF, // GPIO04 HLW8012 CF power - GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 voltage / current - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL, // GPIO12 HLW8012 SEL (1 = Voltage) - GPIO_LED1_INV, // GPIO13 Green Led - Link and Power status - GPIO_REL1, // GPIO14 Relay - 0, 0, 0 + { // KMC_70011 - KMC 70011 + // https://www.amazon.com/KMC-Timing-Monitoring-Network-125V-240V/dp/B06XRX2GTQ + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, + GPIO_HLW_CF, // GPIO04 HLW8012 CF power + GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 voltage / current + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL, // GPIO12 HLW8012 SEL (1 = Voltage) + GPIO_LED1_INV, // GPIO13 Green Led - Link and Power status + GPIO_REL1, // GPIO14 Relay + 0, 0, 0 }, - { "Arilux LC01", // ARILUX_LC01 - Arilux AL-LC01 (ESP8285) - // https://www.banggood.com/nl/ARILUX-AL-LC01-Super-Mini-LED-WIFI-Smart-RGB-Controller-For-RGB-LED-Strip-Light-DC-9-12V-p-1058603.html - // (PwmFrequency 1111Hz) - GPIO_KEY1, // GPIO00 Optional Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_ARIRFSEL, // GPIO02 RF receiver control - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_ARIRFRCV, // GPIO04 IR or RF receiver (optional) - GPIO_PWM1, // GPIO05 RGB LED Red - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM2, // GPIO12 RGB LED Green - GPIO_PWM3, // GPIO13 RGB LED Blue - GPIO_USER, // GPIO14 RGBW LED White (optional - set to PWM4 for Cold White or Warm White) - 0, 0, 0 + { // ARILUX_LC01 - Arilux AL-LC01 (ESP8285) + // https://www.banggood.com/nl/ARILUX-AL-LC01-Super-Mini-LED-WIFI-Smart-RGB-Controller-For-RGB-LED-Strip-Light-DC-9-12V-p-1058603.html + // (PwmFrequency 1111Hz) + GPIO_KEY1, // GPIO00 Optional Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_ARIRFSEL, // GPIO02 RF receiver control + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_ARIRFRCV, // GPIO04 IR or RF receiver (optional) + GPIO_PWM1, // GPIO05 RGB LED Red + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_PWM2, // GPIO12 RGB LED Green + GPIO_PWM3, // GPIO13 RGB LED Blue + GPIO_USER, // GPIO14 RGBW LED White (optional - set to PWM4 for Cold White or Warm White) + 0, 0, 0 }, - { "Arilux LC11", // ARILUX_LC11 - Arilux AL-LC11 (ESP8266) - // https://www.banggood.com/nl/ARILUX-AL-LC11-Super-Mini-LED-WIFI-APP-Controller-RF-Remote-Control-For-RGBWW-LED-Strip-DC9-28V-p-1085112.html - // (PwmFrequency 540Hz) - GPIO_KEY1, // GPIO00 Optional Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_ARIRFSEL, // GPIO02 RF receiver control - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_PWM2, // GPIO04 RGB LED Green - GPIO_PWM1, // GPIO05 RGB LED Red - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM5, // GPIO12 RGBCW LED Warm - GPIO_PWM4, // GPIO13 RGBW LED Cold - GPIO_PWM3, // GPIO14 RGB LED Blue - GPIO_ARIRFRCV, // GPIO15 RF receiver input - 0, 0 + { // ARILUX_LC11 - Arilux AL-LC11 (ESP8266) + // https://www.banggood.com/nl/ARILUX-AL-LC11-Super-Mini-LED-WIFI-APP-Controller-RF-Remote-Control-For-RGBWW-LED-Strip-DC9-28V-p-1085112.html + // (PwmFrequency 540Hz) + GPIO_KEY1, // GPIO00 Optional Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_ARIRFSEL, // GPIO02 RF receiver control + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_PWM2, // GPIO04 RGB LED Green + GPIO_PWM1, // GPIO05 RGB LED Red + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_PWM5, // GPIO12 RGBCW LED Warm + GPIO_PWM4, // GPIO13 RGBW LED Cold + GPIO_PWM3, // GPIO14 RGB LED Blue + GPIO_ARIRFRCV, // GPIO15 RF receiver input + 0, 0 }, - { "Sonoff Dual R2", // SONOFF_DUAL_R2 - Sonoff Dual R2 (ESP8285) - GPIO_USER, // GPIO00 Button 0 on header (0 = On, 1 = Off) - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - 0, - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - 0, - GPIO_REL2, // GPIO05 Relay 2 (0 = Off, 1 = On) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - GPIO_USER, // GPIO09 Button 1 on header (0 = On, 1 = Off) - GPIO_KEY1, // GPIO10 Button on casing - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Relay 1 (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // SONOFF_DUAL_R2 - Sonoff Dual R2 (ESP8285) + GPIO_USER, // GPIO00 Button 0 on header (0 = On, 1 = Off) + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + 0, + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + 0, + GPIO_REL2, // GPIO05 Relay 2 (0 = Off, 1 = On) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + GPIO_USER, // GPIO09 Button 1 on header (0 = On, 1 = Off) + GPIO_KEY1, // GPIO10 Button on casing + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Relay 1 (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Arilux LC06", // ARILUX_LC06 - Arilux AL-LC06 (ESP8285) - // https://www.banggood.com/ARILUX-AL-LC06-LED-WIFI-Smartphone-Controller-Romote-5-Channels-DC12-24V-For-RGBWW-Strip-light-p-1061476.html - GPIO_KEY1, // GPIO00 Optional Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_USER, // GPIO02 Empty pad - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_USER, // GPIO04 W2 - PWM5 - 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM2, // GPIO12 RGB LED Green - GPIO_PWM3, // GPIO13 RGB LED Blue - GPIO_PWM1, // GPIO14 RGB LED Red - GPIO_USER, // GPIO15 RGBW LED White - 0, 0 + { // ARILUX_LC06 - Arilux AL-LC06 (ESP8285) + // https://www.banggood.com/ARILUX-AL-LC06-LED-WIFI-Smartphone-Controller-Romote-5-Channels-DC12-24V-For-RGBWW-Strip-light-p-1061476.html + GPIO_KEY1, // GPIO00 Optional Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_USER, // GPIO02 Empty pad + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_USER, // GPIO04 W2 - PWM5 + 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_PWM2, // GPIO12 RGB LED Green + GPIO_PWM3, // GPIO13 RGB LED Blue + GPIO_PWM1, // GPIO14 RGB LED Red + GPIO_USER, // GPIO15 RGBW LED White + 0, 0 }, - { "Sonoff S31", // SONOFF_S31 - Sonoff S31 (ESP8266 - CSE7766) - GPIO_KEY1, // GPIO00 Button - GPIO_CSE7766_TX, // GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor - 0, - GPIO_CSE7766_RX, // GPIO03 Serial TXD - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // SONOFF_S31 - Sonoff S31 (ESP8266 - CSE7766) + GPIO_KEY1, // GPIO00 Button + GPIO_CSE7766_TX, // GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor + 0, + GPIO_CSE7766_RX, // GPIO03 Serial TXD + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Zengge WF017", // ZENGGE_ZF_WF017 - Zenggee ZJ-WF017-A (ESP12S)) - // https://www.ebay.com/p/Smartphone-Android-IOS-WiFi-Music-Controller-for-RGB-5050-3528-LED-Strip-Light/534446632?_trksid=p2047675.l2644 - GPIO_KEY1, // GPIO00 Optional Button - 0, - GPIO_USER, // GPIO02 Empty pad - 0, - GPIO_USER, // GPIO04 W2 - PWM5 - 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM2, // GPIO12 RGB LED Green - GPIO_PWM1, // GPIO13 RGB LED Red - GPIO_PWM3, // GPIO14 RGB LED Blue - 0, 0, 0 + { // ZENGGE_ZF_WF017 - Zenggee ZJ-WF017-A (ESP12S)) + // https://www.ebay.com/p/Smartphone-Android-IOS-WiFi-Music-Controller-for-RGB-5050-3528-LED-Strip-Light/534446632?_trksid=p2047675.l2644 + GPIO_KEY1, // GPIO00 Optional Button + 0, + GPIO_USER, // GPIO02 Empty pad + 0, + GPIO_USER, // GPIO04 W2 - PWM5 + 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_PWM2, // GPIO12 RGB LED Green + GPIO_PWM1, // GPIO13 RGB LED Red + GPIO_PWM3, // GPIO14 RGB LED Blue + 0, 0, 0 }, - { "Sonoff Pow R2", // SONOFF_POW_R2 - Sonoff Pow R2 (ESP8285 - CSE7766) - GPIO_KEY1, // GPIO00 Button - GPIO_CSE7766_TX, // GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor - 0, - GPIO_CSE7766_RX, // GPIO03 Serial TXD - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 + { // SONOFF_POW_R2 - Sonoff Pow R2 (ESP8285 - CSE7766) + GPIO_KEY1, // GPIO00 Button + GPIO_CSE7766_TX, // GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor + 0, + GPIO_CSE7766_RX, // GPIO03 Serial TXD + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, - { "Sonoff iFan02", // SONOFF_IFAN02 - Sonoff iFan02 (ESP8285) - GPIO_KEY1, // GPIO00 WIFI_KEY0 Virtual button 1 as feedback from RC - GPIO_USER, // GPIO01 ESP_TXD Serial RXD and Optional sensor - 0, // GPIO02 ESP_LOG - GPIO_USER, // GPIO03 ESP_RXD Serial TXD and Optional sensor - GPIO_REL3, // GPIO04 WIFI_O2 Relay 3 (0 = Off, 1 = On) controlling the fan - GPIO_REL2, // GPIO05 WIFI_O1 Relay 2 (0 = Off, 1 = On) controlling the fan - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - GPIO_KEY2, // GPIO09 WIFI_KEY1 Virtual button 2 as feedback from RC - GPIO_KEY3, // GPIO10 WIFI_KEY2 Virtual button 3 as feedback from RC - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 WIFI_O0 Relay 1 (0 = Off, 1 = On) controlling the light - GPIO_LED1_INV, // GPIO13 WIFI_CHK Blue Led on PCA (0 = On, 1 = Off) - Link and Power status - GPIO_KEY4, // GPIO14 WIFI_KEY3 Virtual button 4 as feedback from RC - GPIO_REL4, // GPIO15 WIFI_O3 Relay 4 (0 = Off, 1 = On) controlling the fan - 0, 0 + { // SONOFF_IFAN02 - Sonoff iFan02 (ESP8285) + GPIO_KEY1, // GPIO00 WIFI_KEY0 Virtual button 1 as feedback from RC + GPIO_USER, // GPIO01 ESP_TXD Serial RXD and Optional sensor + 0, // GPIO02 ESP_LOG + GPIO_USER, // GPIO03 ESP_RXD Serial TXD and Optional sensor + GPIO_REL3, // GPIO04 WIFI_O2 Relay 3 (0 = Off, 1 = On) controlling the fan + GPIO_REL2, // GPIO05 WIFI_O1 Relay 2 (0 = Off, 1 = On) controlling the fan + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + GPIO_KEY2, // GPIO09 WIFI_KEY1 Virtual button 2 as feedback from RC + GPIO_KEY3, // GPIO10 WIFI_KEY2 Virtual button 3 as feedback from RC + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 WIFI_O0 Relay 1 (0 = Off, 1 = On) controlling the light + GPIO_LED1_INV, // GPIO13 WIFI_CHK Blue Led on PCA (0 = On, 1 = Off) - Link and Power status + GPIO_KEY4, // GPIO14 WIFI_KEY3 Virtual button 4 as feedback from RC + GPIO_REL4, // GPIO15 WIFI_O3 Relay 4 (0 = Off, 1 = On) controlling the fan + 0, 0 }, - { "BlitzWolf SHP", // BLITZWOLF_BWSHP - BlitzWolf BW-SHP2 and BW-SHP6 (ESP8285 - BL0937 or HJL-01 Energy Monitoring) - // https://www.banggood.com/BlitzWolf-BW-SHP2-Smart-WIFI-Socket-EU-Plug-220V-16A-Work-with-Amazon-Alexa-Google-Assistant-p-1292899.html - // https://www.amazon.de/Steckdose-Homecube-intelligente-Verbrauchsanzeige-funktioniert/dp/B076Q2LKHG/ref=sr_1_fkmr0_1 - // https://www.amazon.de/Intelligente-Stromverbrauch-Fernsteurung-Schaltbare-Energieklasse/dp/B076WZQS4S/ref=sr_1_1 - // https://www.aliexpress.com/store/product/BlitzWolf-BW-SHP6-EU-Plug-Metering-Version-WIFI-Smart-Socket-220V-240V-10A-Work-with-Amazon/1965360_32945504669.html - GPIO_LED1_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_LEDLNK_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - 0, - GPIO_HJL_CF, // GPIO05 BL0937 or HJL-01 CF power - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) - GPIO_KEY1, // GPIO13 Button - GPIO_NRG_CF1, // GPIO14 BL0937 or HJL-01 CF1 current / voltage - GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) - 0, 0 + { // BLITZWOLF_BWSHP - BlitzWolf BW-SHP2 and BW-SHP6 (ESP8285 - BL0937 or HJL-01 Energy Monitoring) + // https://www.banggood.com/BlitzWolf-BW-SHP2-Smart-WIFI-Socket-EU-Plug-220V-16A-Work-with-Amazon-Alexa-Google-Assistant-p-1292899.html + // https://www.amazon.de/Steckdose-Homecube-intelligente-Verbrauchsanzeige-funktioniert/dp/B076Q2LKHG/ref=sr_1_fkmr0_1 + // https://www.amazon.de/Intelligente-Stromverbrauch-Fernsteurung-Schaltbare-Energieklasse/dp/B076WZQS4S/ref=sr_1_1 + // https://www.aliexpress.com/store/product/BlitzWolf-BW-SHP6-EU-Plug-Metering-Version-WIFI-Smart-Socket-220V-240V-10A-Work-with-Amazon/1965360_32945504669.html + GPIO_LED1_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_LEDLNK_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + 0, + GPIO_HJL_CF, // GPIO05 BL0937 or HJL-01 CF power + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) + GPIO_KEY1, // GPIO13 Button + GPIO_NRG_CF1, // GPIO14 BL0937 or HJL-01 CF1 current / voltage + GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) + 0, 0 }, - { "Shelly 1", // SHELLY1 - Shelly1 Open Source (ESP8266 - 2MB) - https://shelly.cloud/shelly1-open-source/ - GPIO_USER, // GPIO00 - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC - GPIO_USER, // GPIO01 Serial RXD - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC - 0, - GPIO_USER, // GPIO03 Serial TXD - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC - GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On) - GPIO_SWT1_NP, // GPIO05 SW pin - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, 0, 0, 0, 0, 0 + { // SHELLY1 - Shelly1 Open Source (ESP8266 - 2MB) - https://shelly.cloud/shelly1-open-source/ + GPIO_USER, // GPIO00 - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC + GPIO_USER, // GPIO01 Serial RXD - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC + 0, + GPIO_USER, // GPIO03 Serial TXD - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC + GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On) + GPIO_SWT1_NP, // GPIO05 SW pin + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + 0, 0, 0, 0, 0, 0 }, - { "Shelly 2", // SHELLY2 - Shelly2 (ESP8266 - 2MB) - https://shelly.cloud/shelly2/ - 0, - GPIO_MCP39F5_TX, // GPIO01 MCP39F501 Serial input - 0, - GPIO_MCP39F5_RX, // GPIO03 MCP39F501 Serial output - GPIO_REL1, // GPIO04 - GPIO_REL2, // GPIO05 - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_SWT1, // GPIO12 - 0, - GPIO_SWT2, // GPIO14 - GPIO_MCP39F5_RST, // GPIO15 MCP39F501 Reset - 0, - 0 + { // SHELLY2 - Shelly2 (ESP8266 - 2MB) - https://shelly.cloud/shelly2/ + 0, + GPIO_MCP39F5_TX, // GPIO01 MCP39F501 Serial input + 0, + GPIO_MCP39F5_RX, // GPIO03 MCP39F501 Serial output + GPIO_REL1, // GPIO04 + GPIO_REL2, // GPIO05 + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_SWT1, // GPIO12 + 0, + GPIO_SWT2, // GPIO14 + GPIO_MCP39F5_RST, // GPIO15 MCP39F501 Reset + 0, + 0 }, - { "Xiaomi Philips", // PHILIPS - Xiaomi Philips bulb (ESP8266) - 0, 0, 0, 0, 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM2, // GPIO12 cold/warm light - 0, 0, - GPIO_PWM1, // GPIO15 light intensity - 0, 0 + { // PHILIPS - Xiaomi Philips bulb (ESP8266) + 0, 0, 0, 0, 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_PWM2, // GPIO12 cold/warm light + 0, 0, + GPIO_PWM1, // GPIO15 light intensity + 0, 0 }, - { "Neo Coolcam", // NEO_COOLCAM - Neo Coolcam (ESP8266) - // https://www.banggood.com/NEO-COOLCAM-WiFi-Mini-Smart-Plug-APP-Remote-Control-Timing-Smart-Socket-EU-Plug-p-1288562.html?cur_warehouse=CN - 0, 0, 0, 0, - GPIO_LED1_INV, // GPIO04 Red Led (0 = On, 1 = Off) - Link and Power status - 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_KEY1, // GPIO13 Button - 0, 0, 0, 0 + { // NEO_COOLCAM - Neo Coolcam (ESP8266) + // https://www.banggood.com/NEO-COOLCAM-WiFi-Mini-Smart-Plug-APP-Remote-Control-Timing-Smart-Socket-EU-Plug-p-1288562.html?cur_warehouse=CN + 0, 0, 0, 0, + GPIO_LED1_INV, // GPIO04 Red Led (0 = On, 1 = Off) - Link and Power status + 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + GPIO_KEY1, // GPIO13 Button + 0, 0, 0, 0 }, - { "ESP Switch", // ESP_SWITCH - Michael Haustein 4 channel wall switch (ESP07 = ESP8266) - // Use rules for further actions like - rule on power1#state do publish cmnd/other_device/power %value% endon - GPIO_KEY2, // GPIO00 Button 2 - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_REL3_INV, // GPIO02 Yellow Led 3 (0 = On, 1 = Off) - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_KEY1, // GPIO04 Button 1 - GPIO_REL2_INV, // GPIO05 Red Led 2 (0 = On, 1 = Off) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL4_INV, // GPIO12 Blue Led 4 (0 = On, 1 = Off) - GPIO_KEY4, // GPIO13 Button 4 - GPIO_KEY3, // GPIO14 Button 3 - GPIO_LED1, // GPIO15 Optional sensor - GPIO_REL1_INV, // GPIO16 Green Led 1 (0 = On, 1 = Off) - 0 + { // ESP_SWITCH - Michael Haustein 4 channel wall switch (ESP07 = ESP8266) + // Use rules for further actions like - rule on power1#state do publish cmnd/other_device/power %value% endon + GPIO_KEY2, // GPIO00 Button 2 + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_REL3_INV, // GPIO02 Yellow Led 3 (0 = On, 1 = Off) + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_KEY1, // GPIO04 Button 1 + GPIO_REL2_INV, // GPIO05 Red Led 2 (0 = On, 1 = Off) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL4_INV, // GPIO12 Blue Led 4 (0 = On, 1 = Off) + GPIO_KEY4, // GPIO13 Button 4 + GPIO_KEY3, // GPIO14 Button 3 + GPIO_LED1, // GPIO15 Optional sensor + GPIO_REL1_INV, // GPIO16 Green Led 1 (0 = On, 1 = Off) + 0 }, - { "OBI Socket", // OBI - OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko/p/2291706 - GPIO_USER, // GPIO00 - GPIO_USER, // GPIO01 Serial RXD - 0, - GPIO_USER, // GPIO03 Serial TXD - GPIO_LED1, // GPIO04 Blue LED - Link and Power status - GPIO_REL1, // GPIO05 (Relay OFF, but used as Relay Switch) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_LED3, // GPIO12 (Relay ON, but set to LOW, so we can switch with GPIO05) - GPIO_USER, // GPIO13 - GPIO_KEY1, // GPIO14 Button - 0, - GPIO_USER, // GPIO16 - ADC0_USER // ADC0 A0 Analog input + { // OBI - OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko/p/2291706 + GPIO_USER, // GPIO00 + GPIO_USER, // GPIO01 Serial RXD + 0, + GPIO_USER, // GPIO03 Serial TXD + GPIO_LED1, // GPIO04 Blue LED - Link and Power status + GPIO_REL1, // GPIO05 (Relay OFF, but used as Relay Switch) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_LED3, // GPIO12 (Relay ON, but set to LOW, so we can switch with GPIO05) + GPIO_USER, // GPIO13 + GPIO_KEY1, // GPIO14 Button + 0, + GPIO_USER, // GPIO16 + ADC0_USER // ADC0 A0 Analog input }, - { "Teckin", // TECKIN - https://www.amazon.de/gp/product/B07D5V139R - 0, - GPIO_KEY1, // GPIO01 Serial TXD and Button - 0, - GPIO_LED1_INV, // GPIO03 Serial RXD and Red Led (0 = On, 1 = Off) - Power status - GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power - GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) - GPIO_LEDLNK_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link status - GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On) - 0, 0, 0 + { // TECKIN - https://www.amazon.de/gp/product/B07D5V139R + 0, + GPIO_KEY1, // GPIO01 Serial TXD and Button + 0, + GPIO_LED1_INV, // GPIO03 Serial RXD and Red Led (0 = On, 1 = Off) - Power status + GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power + GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) + GPIO_LEDLNK_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link status + GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On) + 0, 0, 0 }, - { "AplicWDP303075", // APLIC_WDP303075 - Aplic WDP 303075 (ESP8285 - HLW8012 Energy Monitoring) - // https://www.amazon.de/dp/B07CNWVNJ2 - 0, 0, 0, - GPIO_KEY1, // GPIO03 Button - GPIO_HLW_CF, // GPIO04 HLW8012 CF power - GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 current / voltage - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 HLW8012 CF Sel output (0 = Voltage) - GPIO_LED1_INV, // GPIO13 LED (0 = On, 1 = Off) - Link and Power status - GPIO_REL1, // GPIO14 Relay SRU 5VDC SDA (0 = Off, 1 = On ) - 0, 0, 0 + { // APLIC_WDP303075 - Aplic WDP 303075 (ESP8285 - HLW8012 Energy Monitoring) + // https://www.amazon.de/dp/B07CNWVNJ2 + 0, 0, 0, + GPIO_KEY1, // GPIO03 Button + GPIO_HLW_CF, // GPIO04 HLW8012 CF power + GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 current / voltage + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 HLW8012 CF Sel output (0 = Voltage) + GPIO_LED1_INV, // GPIO13 LED (0 = On, 1 = Off) - Link and Power status + GPIO_REL1, // GPIO14 Relay SRU 5VDC SDA (0 = Off, 1 = On ) + 0, 0, 0 }, - { "Tuya MCU", // TUYA_DIMMER - 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 - GPIO_USER, - GPIO_USER, // GPIO03 MCU serial control - GPIO_USER, - GPIO_USER, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, - GPIO_USER, - GPIO_USER, // GPIO14 Green Led - GPIO_USER, - GPIO_USER, - 0 + { // TUYA_DIMMER - 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 + GPIO_USER, + GPIO_USER, // GPIO03 MCU serial control + GPIO_USER, + GPIO_USER, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, + GPIO_USER, + GPIO_USER, // GPIO14 Green Led + GPIO_USER, + GPIO_USER, + 0 }, - { "Gosund SP1 v23", // GOSUND - https://www.amazon.de/gp/product/B0777BWS1P - 0, - GPIO_LEDLNK_INV, // GPIO01 Serial RXD and LED1 (blue) inv - Link status - 0, - GPIO_KEY1, // GPIO03 Serial TXD and Button - GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power - GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) - GPIO_LED1_INV, // GPIO13 LED2 (red) inv - Power status - GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On) - 0, 0, 0 + { // GOSUND - https://www.amazon.de/gp/product/B0777BWS1P + 0, + GPIO_LEDLNK_INV, // GPIO01 Serial RXD and LED1 (blue) inv - Link status + 0, + GPIO_KEY1, // GPIO03 Serial TXD and Button + GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power + GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) + GPIO_LED1_INV, // GPIO13 LED2 (red) inv - Power status + GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On) + 0, 0, 0 }, - { "ARMTR Dimmer", // ARMTRONIX_DIMMERS - ARMTRONIX Dimmer, one or two channel (ESP8266 w/ separate MCU dimmer) - // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-two-triac-board/ - // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-esp8266-one-triac-board-alexaecho/ - GPIO_USER, - GPIO_TXD, // GPIO01 MCU serial control - GPIO_USER, - GPIO_RXD, // GPIO03 MCU serial control - GPIO_USER, - GPIO_USER, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, - GPIO_USER, - GPIO_USER, - GPIO_USER, - GPIO_USER, - 0 + { // ARMTRONIX_DIMMERS - ARMTRONIX Dimmer, one or two channel (ESP8266 w/ separate MCU dimmer) + // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-two-triac-board/ + // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-esp8266-one-triac-board-alexaecho/ + GPIO_USER, + GPIO_TXD, // GPIO01 MCU serial control + GPIO_USER, + GPIO_RXD, // GPIO03 MCU serial control + GPIO_USER, + GPIO_USER, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, + GPIO_USER, + GPIO_USER, + GPIO_USER, + GPIO_USER, + 0 }, - { "SK03 Outdoor", // SK03_TUYA - Outdoor smart plug with power monitoring HLW8012 chip - https://www.amazon.com/gp/product/B07CG7MBPV - GPIO_KEY1, // GPIO00 Button - 0, 0, 0, - GPIO_HLW_CF, // GPIO04 HLW8012 CF power - GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 current / voltage - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 HLW8012 CF Sel output (0 = Voltage) - GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off) - Power status - GPIO_LEDLNK_INV, // GPIO14 Blue Led (0 = On, 1 = Off) - Link status - GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) - 0, 0 + { // SK03_TUYA - Outdoor smart plug with power monitoring HLW8012 chip - https://www.amazon.com/gp/product/B07CG7MBPV + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, + GPIO_HLW_CF, // GPIO04 HLW8012 CF power + GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 current / voltage + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 HLW8012 CF Sel output (0 = Voltage) + GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off) - Power status + GPIO_LEDLNK_INV, // GPIO14 Blue Led (0 = On, 1 = Off) - Link status + GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) + 0, 0 }, - { "PS-16-DZ", // PS_16_DZ - PS-16-DZ Dimmer (ESP8266 w/ separate Nuvoton MCU dimmer) - // https://www.aliexpress.com/item/SM-Smart-WIFI-Wall-Dimmer-Light-Switch-US-Ewelink-APP-Remote-Control-Wi-Fi-Wirele-Work/32871151902.html - GPIO_USER, - GPIO_TXD, // GPIO01 MCU serial control - GPIO_USER, - GPIO_RXD, // GPIO03 MCU serial control - GPIO_USER, - GPIO_USER, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, - GPIO_LED1, // GPIO13 WiFi LED - Link and Power status - GPIO_USER, - GPIO_USER, - GPIO_USER, - 0 + { // PS_16_DZ - PS-16-DZ Dimmer (ESP8266 w/ separate Nuvoton MCU dimmer) + // https://www.aliexpress.com/item/SM-Smart-WIFI-Wall-Dimmer-Light-Switch-US-Ewelink-APP-Remote-Control-Wi-Fi-Wirele-Work/32871151902.html + GPIO_USER, + GPIO_TXD, // GPIO01 MCU serial control + GPIO_USER, + GPIO_RXD, // GPIO03 MCU serial control + GPIO_USER, + GPIO_USER, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, + GPIO_LED1, // GPIO13 WiFi LED - Link and Power status + GPIO_USER, + GPIO_USER, + GPIO_USER, + 0 }, - { "Teckin US", // TECKIN_US - Teckin SP20 US with Energy Monitoring - // https://www.amazon.com/Outlet-Compatible-Monitoring-Function-Required/dp/B079Q5W22B - // https://www.amazon.com/Outlet-ZOOZEE-Monitoring-Function-Compatible/dp/B07J2LR5KN - GPIO_LED1_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status - 0, - GPIO_LEDLNK_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status - 0, - GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On) - GPIO_HJL_CF, // GPIO05 BL0937 or HJL-01 CF power - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) - GPIO_KEY1, // GPIO13 Button - GPIO_NRG_CF1, // GPIO14 BL0937 or HJL-01 CF1 current / voltage - 0, 0, 0 + { // TECKIN_US - Teckin SP20 US with Energy Monitoring + // https://www.amazon.com/Outlet-Compatible-Monitoring-Function-Required/dp/B079Q5W22B + // https://www.amazon.com/Outlet-ZOOZEE-Monitoring-Function-Compatible/dp/B07J2LR5KN + GPIO_LED1_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status + 0, + GPIO_LEDLNK_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status + 0, + GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On) + GPIO_HJL_CF, // GPIO05 BL0937 or HJL-01 CF power + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) + GPIO_KEY1, // GPIO13 Button + GPIO_NRG_CF1, // GPIO14 BL0937 or HJL-01 CF1 current / voltage + 0, 0, 0 }, - { "Manzoku strip", // MANZOKU_EU_4 - "MANZOKU" labeled power strip, EU version - // https://www.amazon.de/Steckdosenleiste-AOFO-Mehrfachsteckdose-Überspannungsschutz-Sprachsteuerung/dp/B07GBSD11P/ - // https://www.amazon.de/Steckdosenleiste-Geekbes-USB-Anschluss-Kompatibel-gesteuert/dp/B078W23BW9/ - 0, // GPIO00 - 0, // GPIO01 Serial RXD - 0, - GPIO_KEY1, // GPIO03 Serial TXD + Button - GPIO_REL2, // GPIO04 Relay 2 - GPIO_REL1, // GPIO05 Relay 1 - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL3, // GPIO12 Relay 3 - GPIO_REL4, // GPIO13 Relay 4 - GPIO_USER, // GPIO14 - 0, - GPIO_USER, // GPIO16 - 0 + { // MANZOKU_EU_4 - "MANZOKU" labeled power strip, EU version + // https://www.amazon.de/Steckdosenleiste-AOFO-Mehrfachsteckdose-Überspannungsschutz-Sprachsteuerung/dp/B07GBSD11P/ + // https://www.amazon.de/Steckdosenleiste-Geekbes-USB-Anschluss-Kompatibel-gesteuert/dp/B078W23BW9/ + 0, // GPIO00 + 0, // GPIO01 Serial RXD + 0, + GPIO_KEY1, // GPIO03 Serial TXD + Button + GPIO_REL2, // GPIO04 Relay 2 + GPIO_REL1, // GPIO05 Relay 1 + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL3, // GPIO12 Relay 3 + GPIO_REL4, // GPIO13 Relay 4 + GPIO_USER, // GPIO14 + 0, + GPIO_USER, // GPIO16 + 0 }, - { "OBI Socket 2", // OBI2 - OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko-2-stueck-weiss/p/4077673 - 0, // GPIO00 - 0, // GPIO01 Serial RXD - 0, - 0, // GPIO03 Serial TXD - GPIO_REL1, // GPIO04 Relay 1 - GPIO_KEY1, // GPIO05 Button - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_LEDLNK_INV, // GPIO12 Green LED - Link status - GPIO_LED1, // GPIO13 Red LED - Power status - 0, 0, 0, 0 + { // OBI2 - OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko-2-stueck-weiss/p/4077673 + 0, // GPIO00 + 0, // GPIO01 Serial RXD + 0, + 0, // GPIO03 Serial TXD + GPIO_REL1, // GPIO04 Relay 1 + GPIO_KEY1, // GPIO05 Button + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_LEDLNK_INV, // GPIO12 Green LED - Link status + GPIO_LED1, // GPIO13 Red LED - Power status + 0, 0, 0, 0 }, - { "YTF IR Bridge", // YTF_IR_BRIDGE - https://www.aliexpress.com/item/Tuya-universal-Smart-IR-Hub-remote-control-Voice-Control-AC-TV-Work-With-Alexa-Google-Home/32951202513.html - GPIO_USER, // GPIO00 - GPIO_USER, // GPIO01 Serial RXD - GPIO_USER, // GPIO02 - GPIO_USER, // GPIO03 Serial TXD - GPIO_LED1_INV, // GPIO04 Blue Led - Link status - GPIO_IRRECV, // GPIO05 IR Receiver - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, // GPIO12 - GPIO_KEY1, // GPIO13 Button - GPIO_IRSEND, // GPIO14 IR Transmitter - 0, 0, 0 + { // YTF_IR_BRIDGE - https://www.aliexpress.com/item/Tuya-universal-Smart-IR-Hub-remote-control-Voice-Control-AC-TV-Work-With-Alexa-Google-Home/32951202513.html + GPIO_USER, // GPIO00 + GPIO_USER, // GPIO01 Serial RXD + GPIO_USER, // GPIO02 + GPIO_USER, // GPIO03 Serial TXD + GPIO_LED1_INV, // GPIO04 Blue Led - Link status + GPIO_IRRECV, // GPIO05 IR Receiver + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + 0, // GPIO12 + GPIO_KEY1, // GPIO13 Button + GPIO_IRSEND, // GPIO14 IR Transmitter + 0, 0, 0 }, - { "Digoo DG-SP202", // DIGOO - Digoo DG-SP202 - // https://www.banggood.com/DIGOO-DG-SP202-Dual-EU-Plug-Smart-WIFI-Socket-Individual-Controllable-Energy-Monitor-Remote-Control-Timing-Smart-Home-Outlet-let-p-1375323.html - GPIO_KEY1, // GPIO00 Button1 - 0, // GPIO01 Serial RXD - 0, // GPIO02 - 0, // GPIO03 Serial TXD - GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power - GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) - GPIO_LED1, // GPIO13 Blue Leds - Link Status - GPIO_REL2, // GPIO14 Relay2 (0 = Off, 1 = On) and Red Led - GPIO_REL1, // GPIO15 Relay1 (0 = Off, 1 = On) and Red Led - GPIO_KEY2_NP, // GPIO16 Button2, externally pulled up - 0 + { // DIGOO - Digoo DG-SP202 + // https://www.banggood.com/DIGOO-DG-SP202-Dual-EU-Plug-Smart-WIFI-Socket-Individual-Controllable-Energy-Monitor-Remote-Control-Timing-Smart-Home-Outlet-let-p-1375323.html + GPIO_KEY1, // GPIO00 Button1 + 0, // GPIO01 Serial RXD + 0, // GPIO02 + 0, // GPIO03 Serial TXD + GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power + GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) + GPIO_LED1, // GPIO13 Blue Leds - Link Status + GPIO_REL2, // GPIO14 Relay2 (0 = Off, 1 = On) and Red Led + GPIO_REL1, // GPIO15 Relay1 (0 = Off, 1 = On) and Red Led + GPIO_KEY2_NP, // GPIO16 Button2, externally pulled up + 0 }, - { "KA10", // KA10 - SMANERGY KA10 (ESP8285 - BL0937 Energy Monitoring) - https://www.amazon.es/dp/B07MBTCH2Y - 0, // GPIO00 - GPIO_LEDLNK_INV, // GPIO01 Blue LED - Link status - 0, // GPIO02 - GPIO_KEY1, // GPIO03 Button - GPIO_HJL_CF, // GPIO04 BL0937 CF power - GPIO_NRG_CF1, // GPIO05 BL0937 CF1 voltage / current - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 BL0937 Sel output (1 = Voltage) - GPIO_LED1, // GPIO13 Red LED - Power status - GPIO_REL1, // GPIO14 Relay 1 - 0, 0, 0 + { // KA10 - SMANERGY KA10 (ESP8285 - BL0937 Energy Monitoring) - https://www.amazon.es/dp/B07MBTCH2Y + 0, // GPIO00 + GPIO_LEDLNK_INV, // GPIO01 Blue LED - Link status + 0, // GPIO02 + GPIO_KEY1, // GPIO03 Button + GPIO_HJL_CF, // GPIO04 BL0937 CF power + GPIO_NRG_CF1, // GPIO05 BL0937 CF1 voltage / current + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 BL0937 Sel output (1 = Voltage) + GPIO_LED1, // GPIO13 Red LED - Power status + GPIO_REL1, // GPIO14 Relay 1 + 0, 0, 0 }, - { "Luminea ZX2820", // ZX2820 - GPIO_KEY1, // GPIO00 Button - 0, 0, 0, - GPIO_HLW_CF, // GPIO04 HLW8012 CF power - GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 voltage / current - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 HLW8012 SEL (0 = Voltage) - GPIO_LED1_INV, // GPIO13 Green Led - Link and Power status - GPIO_REL1, // GPIO14 Relay - 0, 0, 0 + { // ZX2820 + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, + GPIO_HLW_CF, // GPIO04 HLW8012 CF power + GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 voltage / current + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 HLW8012 SEL (0 = Voltage) + GPIO_LED1_INV, // GPIO13 Green Led - Link and Power status + GPIO_REL1, // GPIO14 Relay + 0, 0, 0 }, - { "Mi Desk Lamp", // MI_DESK_LAMP - Mi LED Desk Lamp - https://www.mi.com/global/smartlamp/ - 0, 0, - GPIO_KEY1, // GPIO02 Button - 0, - GPIO_PWM1, // GPIO04 Cold White - GPIO_PWM2, // GPIO05 Warm White - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_ROT1A, // GPIO12 Rotary switch A pin - GPIO_ROT1B, // GPIO13 Rotary switch B pin - 0, 0, 0, 0 + { // MI_DESK_LAMP - Mi LED Desk Lamp - https://www.mi.com/global/smartlamp/ + 0, 0, + GPIO_KEY1, // GPIO02 Button + 0, + GPIO_PWM1, // GPIO04 Cold White + GPIO_PWM2, // GPIO05 Warm White + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_ROT1A, // GPIO12 Rotary switch A pin + GPIO_ROT1B, // GPIO13 Rotary switch B pin + 0, 0, 0, 0 }, - { "SP10", // SP10 - Tuya SP10 (BL0937 Energy Monitoring) - // https://www.aliexpress.com/item/Smart-Mini-WiFi-Plug-Outlet-Switch-Work-With-ForEcho-Alexa-Google-Home-Remote-EU-Smart-Socket/32963670423.html - 0, // GPIO00 - GPIO_PWM1, // GPIO01 Nightlight - 0, // GPIO02 - GPIO_KEY1, // GPIO03 Button - GPIO_HJL_CF, // GPIO04 BL0937 CF power - GPIO_NRG_CF1, // GPIO05 BL0937 CF1 voltage / current - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 BL0937 Sel output (1 = Voltage) - GPIO_LED1, // GPIO13 Blue LED - Link status - GPIO_REL1, // GPIO14 Relay and red LED - 0, 0, 0 + { // SP10 - Tuya SP10 (BL0937 Energy Monitoring) + // https://www.aliexpress.com/item/Smart-Mini-WiFi-Plug-Outlet-Switch-Work-With-ForEcho-Alexa-Google-Home-Remote-EU-Smart-Socket/32963670423.html + 0, // GPIO00 + GPIO_PWM1, // GPIO01 Nightlight + 0, // GPIO02 + GPIO_KEY1, // GPIO03 Button + GPIO_HJL_CF, // GPIO04 BL0937 CF power + GPIO_NRG_CF1, // GPIO05 BL0937 CF1 voltage / current + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 BL0937 Sel output (1 = Voltage) + GPIO_LED1, // GPIO13 Blue LED - Link status + GPIO_REL1, // GPIO14 Relay and red LED + 0, 0, 0 }, - { "WAGA CHCZ02MB", // WAGA - WAGA life CHCZ02MB (HJL-01 Energy Monitoring) - // https://www.ebay.com/itm/332595697006 - GPIO_LED1_INV, // GPIO00 Red LED - 0, // GPIO01 Serial RXD - 0, // GPIO02 - GPIO_NRG_SEL_INV, // GPIO03 HJL-01 Sel output (1 = Voltage) - 0, // GPIO04 - GPIO_HJL_CF, // GPIO05 HJL-01 CF power - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Relay - GPIO_KEY1, // GPIO13 Button - GPIO_NRG_CF1, // GPIO14 HJL-01 CF1 voltage / current - GPIO_LEDLNK_INV, // GPIO15 Blue LED - Link status - 0, 0 + { // WAGA - WAGA life CHCZ02MB (HJL-01 Energy Monitoring) + // https://www.ebay.com/itm/332595697006 + GPIO_LED1_INV, // GPIO00 Red LED + 0, // GPIO01 Serial RXD + 0, // GPIO02 + GPIO_NRG_SEL_INV, // GPIO03 HJL-01 Sel output (1 = Voltage) + 0, // GPIO04 + GPIO_HJL_CF, // GPIO05 HJL-01 CF power + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Relay + GPIO_KEY1, // GPIO13 Button + GPIO_NRG_CF1, // GPIO14 HJL-01 CF1 voltage / current + GPIO_LEDLNK_INV, // GPIO15 Blue LED - Link status + 0, 0 }, - { "SYF05", // SYF05 - Sunyesmart SYF05 (a.k.a. Fcmila) = TYWE3S + SM16726 - // Also works with Merkury 904 RGBW Bulbs with 13 set to GPIO_SM16716_SEL - // https://www.flipkart.com/fc-mila-bxav-xs-ad-smart-bulb/p/itmf85zgs45fzr7n - // https://docs.tuya.com/en/hardware/WiFi-module/wifi-e3s-module.html - // http://www.datasheet-pdf.com/PDF/SM16716-Datasheet-Sunmoon-932771 - GPIO_USER, // GPIO00 N.C. - 0, // GPIO01 Serial RXD - GPIO_USER, // GPIO02 N.C. - 0, // GPIO03 Serial TXD - GPIO_SM16716_CLK, // GPIO04 SM16716 Clock - GPIO_PWM1, // GPIO05 White - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, // GPIO12 Alt. White on some devices - GPIO_USER, // GPIO13 SM16716 Enable on some devices - GPIO_SM16716_DAT, // GPIO14 SM16716 Data - 0, // GPIO15 wired to GND - GPIO_USER, // GPIO16 N.C. - ADC0_USER // ADC0 A0 Analog input + { // SYF05 - Sunyesmart SYF05 (a.k.a. Fcmila) = TYWE3S + SM16726 + // Also works with Merkury 904 RGBW Bulbs with 13 set to GPIO_SM16716_SEL + // https://www.flipkart.com/fc-mila-bxav-xs-ad-smart-bulb/p/itmf85zgs45fzr7n + // https://docs.tuya.com/en/hardware/WiFi-module/wifi-e3s-module.html + // http://www.datasheet-pdf.com/PDF/SM16716-Datasheet-Sunmoon-932771 + GPIO_USER, // GPIO00 N.C. + 0, // GPIO01 Serial RXD + GPIO_USER, // GPIO02 N.C. + 0, // GPIO03 Serial TXD + GPIO_SM16716_CLK, // GPIO04 SM16716 Clock + GPIO_PWM1, // GPIO05 White + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, // GPIO12 Alt. White on some devices + GPIO_USER, // GPIO13 SM16716 Enable on some devices + GPIO_SM16716_DAT, // GPIO14 SM16716 Data + 0, // GPIO15 wired to GND + GPIO_USER, // GPIO16 N.C. + ADC0_USER // ADC0 A0 Analog input }, - { "Sonoff L1", // SONOFF_L1 - Sonoff L1 RGB LED controller (ESP8266 w/ separate Nuvoton MCU) - 0, - GPIO_TXD, // GPIO01 MCU serial control - 0, - GPIO_RXD, // GPIO03 MCU serial control - 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, - GPIO_LED1_INV, // GPIO13 WiFi Blue Led - Link and Power status - 0, 0, 0, 0 + { // SONOFF_L1 - Sonoff L1 RGB LED controller (ESP8266 w/ separate Nuvoton MCU) + 0, + GPIO_TXD, // GPIO01 MCU serial control + 0, + GPIO_RXD, // GPIO03 MCU serial control + 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + 0, + GPIO_LED1_INV, // GPIO13 WiFi Blue Led - Link and Power status + 0, 0, 0, 0 }, - { "Sonoff iFan03", // SONOFF_IFAN03 - Sonoff iFan03 (ESP8285) - GPIO_KEY1, // GPIO00 WIFI_KEY0 Button 1 - GPIO_TXD, // GPIO01 ESP_TXD Serial RXD connection to P0.5 of RF microcontroller - 0, // GPIO02 ESP_LOG - GPIO_RXD, // GPIO03 ESP_RXD Serial TXD connection to P0.4 of RF microcontroller - 0, // GPIO04 DEBUG_RX - 0, // GPIO05 DEBUG_TX - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - GPIO_REL1_INV, // GPIO09 WIFI_O0 Relay 1 (0 = Off, 1 = On) controlling the light - GPIO_BUZZER_INV, // GPIO10 WIFI_O4 Buzzer (0 = Off, 1 = On) - // GPIO11 (SD_CMD Flash) - GPIO_REL3, // GPIO12 WIFI_O2 Relay 3 (0 = Off, 1 = On) controlling the fan - GPIO_LED1_INV, // GPIO13 WIFI_CHK Blue Led on PCA (0 = On, 1 = Off) - Link and Power status - GPIO_REL2, // GPIO14 WIFI_O1 Relay 2 (0 = Off, 1 = On) controlling the fan - GPIO_REL4, // GPIO15 WIFI_O3 Relay 4 (0 = Off, 1 = On) controlling the fan - 0, 0 + { // SONOFF_IFAN03 - Sonoff iFan03 (ESP8285) + GPIO_KEY1, // GPIO00 WIFI_KEY0 Button 1 + GPIO_TXD, // GPIO01 ESP_TXD Serial RXD connection to P0.5 of RF microcontroller + 0, // GPIO02 ESP_LOG + GPIO_RXD, // GPIO03 ESP_RXD Serial TXD connection to P0.4 of RF microcontroller + 0, // GPIO04 DEBUG_RX + 0, // GPIO05 DEBUG_TX + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + GPIO_REL1_INV, // GPIO09 WIFI_O0 Relay 1 (0 = Off, 1 = On) controlling the light + GPIO_BUZZER_INV, // GPIO10 WIFI_O4 Buzzer (0 = Off, 1 = On) + // GPIO11 (SD_CMD Flash) + GPIO_REL3, // GPIO12 WIFI_O2 Relay 3 (0 = Off, 1 = On) controlling the fan + GPIO_LED1_INV, // GPIO13 WIFI_CHK Blue Led on PCA (0 = On, 1 = Off) - Link and Power status + GPIO_REL2, // GPIO14 WIFI_O1 Relay 2 (0 = Off, 1 = On) controlling the fan + GPIO_REL4, // GPIO15 WIFI_O3 Relay 4 (0 = Off, 1 = On) controlling the fan + 0, 0 }, - { "EXS Dimmer", // EXS_DIMMER - EX-Store WiFi Dimmer v4, two channel (ESP8266 w/ separate MCU dimmer) - // https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A - // https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A-ESP8266-V12-Stift-und-Buchsenleisten - 0, - GPIO_TXD, // GPIO01 MCU serial control - GPIO_LEDLNK, // GPIO02 LED Link - GPIO_RXD, // GPIO03 MCU serial control - GPIO_USER, // GPIO04 - GPIO_USER, // GPIO05 - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, // GPIO12 - GPIO_EXS_ENABLE, // GPIO13 EXS MCU Enable - GPIO_USER, // GPIO14 - 0, // GPIO15 - 0, 0 + { // EXS_DIMMER - EX-Store WiFi Dimmer v4, two channel (ESP8266 w/ separate MCU dimmer) + // https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A + // https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A-ESP8266-V12-Stift-und-Buchsenleisten + 0, + GPIO_TXD, // GPIO01 MCU serial control + GPIO_LEDLNK, // GPIO02 LED Link + GPIO_RXD, // GPIO03 MCU serial control + GPIO_USER, // GPIO04 + GPIO_USER, // GPIO05 + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, // GPIO12 + GPIO_EXS_ENABLE, // GPIO13 EXS MCU Enable + GPIO_USER, // GPIO14 + 0, // GPIO15 + 0, 0 }, - { "PWM Dimmer", // PWM_DIMMER - Support for Martin Jerry/acenx/Tessan/NTONPOWER SD0x PWM - // dimmer switches. The brightness of the load for these dimmers is - // controlled by a PWM GPIO pin. There are typically power, up & down - // buttons and 4 LED's. Examples are: - // https://www.amazon.com/dp/B07FXYSVR1 - // https://www.amazon.com/dp/B07V26Q3VD - // https://www.amazon.com/dp/B07K67D43J - // https://www.amazon.com/dp/B07TTGFWFM - GPIO_KEY3, // GPIO00 Up button - GPIO_KEY2, // GPIO01 Down button - 0, // GPIO02 - GPIO_LED4_INV, // GPIO03 Level 5 LED - GPIO_LEDLNK_INV, // GPIO04 LED Link - GPIO_LED3_INV, // GPIO05 Level 4 LED - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_LED2_INV, // GPIO12 Level 3 LED - GPIO_PWM1, // GPIO13 Dimmer PWM - GPIO_LED1_INV, // GPIO12 Level 2 LED - GPIO_KEY1_INV, // GPIO15 Power button - GPIO_REL1_INV, // GPIO16 Power relay/Level 1 LED - 0 + { // PWM_DIMMER - Support for Martin Jerry/acenx/Tessan/NTONPOWER SD0x PWM + // dimmer switches. The brightness of the load for these dimmers is + // controlled by a PWM GPIO pin. There are typically power, up & down + // buttons and 4 LED's. Examples are: + // https://www.amazon.com/dp/B07FXYSVR1 + // https://www.amazon.com/dp/B07V26Q3VD + // https://www.amazon.com/dp/B07K67D43J + // https://www.amazon.com/dp/B07TTGFWFM + GPIO_KEY3, // GPIO00 Up button + GPIO_KEY2, // GPIO01 Down button + 0, // GPIO02 + GPIO_LED4_INV, // GPIO03 Level 5 LED + GPIO_LEDLNK_INV, // GPIO04 LED Link + GPIO_LED3_INV, // GPIO05 Level 4 LED + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_LED2_INV, // GPIO12 Level 3 LED + GPIO_PWM1, // GPIO13 Dimmer PWM + GPIO_LED1_INV, // GPIO12 Level 2 LED + GPIO_KEY1_INV, // GPIO15 Power button + GPIO_REL1_INV, // GPIO16 Power relay/Level 1 LED + 0 }, - { "Sonoff D1", // SONOFF_D1 - Sonoff D1 RF Dimmer 433 (ESP8285) - GPIO_USER, // GPIO00 Pad - GPIO_TXD, // GPIO01 D1 control - 0, // GPIO02 - GPIO_RXD, // GPIO03 D1 control - 0, // GPIO04 DEBUG_RX - 0, // GPIO05 DEBUG_TX - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 - 0, // GPIO10 - // GPIO11 (SD_CMD Flash) - 0, // GPIO12 - GPIO_LED1_INV, // GPIO13 WiFi Blue Led - Link and Power status - 0, 0, 0, 0 + { // SONOFF_D1 - Sonoff D1 RF Dimmer 433 (ESP8285) + GPIO_USER, // GPIO00 Pad + GPIO_TXD, // GPIO01 D1 control + 0, // GPIO02 + GPIO_RXD, // GPIO03 D1 control + 0, // GPIO04 DEBUG_RX + 0, // GPIO05 DEBUG_TX + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) + 0, // GPIO12 + GPIO_LED1_INV, // GPIO13 WiFi Blue Led - Link and Power status + 0, 0, 0, 0 } }; diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index f48ce9431..c6cf97580 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -1494,7 +1494,7 @@ void HandleTemplateConfiguration(void) void TemplateSaveSettings(void) { - char tmp[sizeof(Settings.user_template.name)]; // WebGetArg NAME and GPIO/BASE/FLAG byte value + char tmp[sizeof(Settings.user_template_name)]; // WebGetArg NAME and GPIO/BASE/FLAG byte value char webindex[5]; // WebGetArg name char svalue[128]; // Template command string From 397fea3bb2d65f501c592af53f8dd22f19943af6 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 29 Mar 2020 15:17:54 +0200 Subject: [PATCH 030/547] Add Console command history Add Console command history (#8015) --- tasmota/xdrv_01_webserver.ino | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index c6cf97580..e4336b736 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -31,7 +31,7 @@ #define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 11 as used by WifiManager web GUI #endif -const uint16_t CHUNKED_BUFFER_SIZE = 400; // Chunk buffer size (should be smaller than half mqtt_date size = MESSZ) +const uint16_t CHUNKED_BUFFER_SIZE = (MESSZ / 2) - 100; // Chunk buffer size (should be smaller than half mqtt_data size = MESSZ) const uint16_t HTTP_REFRESH_TIME = 2345; // milliseconds #define HTTP_RESTART_RECONNECT_TIME 9000 // milliseconds @@ -184,7 +184,7 @@ const char HTTP_SCRIPT_CONSOL[] PROGMEM = "clearTimeout(lt);" "t=eb('t1');" "if(p==1){" - "c=eb('c1');" + "c=eb('c1');" // Console command id "o='&c1='+encodeURIComponent(c.value);" "c.value='';" "t.scrollTop=99999;" @@ -211,6 +211,16 @@ const char HTTP_SCRIPT_CONSOL[] PROGMEM = "lt=setTimeout(l,%d);" "return false;" "}" + + // Console command history - part 2 + "var mh=20,hc=[],cn=0;" + "function cH(a){" + "var b=qs('#c1'),c=a.keyCode;" // c1 = Console command id + "38==c?(++cn>hc.length&&(cn=hc.length),b.value=hc[cn-1]||''):" // Arrow Up + "40==c?(0>--cn&&(cn=0),b.value=hc[cn-1]||''):" // Arrow Down + "13==c&&(hc.length>19&&hc.pop(),hc.unshift(b.value),cn=0)" // Enter + "}" + "wl(l);"; const char HTTP_MODULE_TEMPLATE_REPLACE[] PROGMEM = @@ -329,6 +339,12 @@ const char HTTP_HEAD_LAST_SCRIPT[] PROGMEM = "}" "t++;" "}" + + // Console command history - part 1 + "if(qs('#c1')){" // c1 = Console command id + "qs('#c1').addEventListener('keydown',cH);" + "}" + "}" "wl(jd);" // Add name='' to any id='' in input,button,textarea,select ""; @@ -745,7 +761,7 @@ void _WSContentSend(const String& content) // Low level sendContent for a #ifdef USE_DEBUG_DRIVER ShowFreeMem(PSTR("WSContentSend")); #endif - DEBUG_CORE_LOG(PSTR("WEB: Chunk size %d"), len); + DEBUG_CORE_LOG(PSTR("WEB: Chunk size %d/%d"), len, sizeof(mqtt_data)); } void WSContentFlush(void) @@ -1735,7 +1751,7 @@ void HandleWifiConfiguration(void) //display networks in page for (uint32_t i = 0; i < n; i++) { if (-1 == indices[i]) { continue; } // skip dups - int32_t rssi = WiFi.RSSI(indices[i]) + int32_t rssi = WiFi.RSSI(indices[i]); DEBUG_CORE_LOG(PSTR(D_LOG_WIFI D_SSID " %s, " D_BSSID " %s, " D_CHANNEL " %d, " D_RSSI " %d"), WiFi.SSID(indices[i]).c_str(), WiFi.BSSIDstr(indices[i]).c_str(), WiFi.channel(indices[i]), rssi); int quality = WifiGetRssiAsQuality(rssi); From 257dd1879611a08090eaa2e3fe9d5bb464bf34a9 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 29 Mar 2020 15:35:58 +0200 Subject: [PATCH 031/547] Add console command history (#7483, #8015) Add console command history (#7483, #8015) --- RELEASENOTES.md | 1 + tasmota/CHANGELOG.md | 1 + tasmota/xdrv_01_webserver.ino | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index da5dd225f..9e22461e2 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -62,3 +62,4 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Add support for unreachable (unplugged) Zigbee devices in Philips Hue emulation and Alexa - Add support for 64x48 SSD1306 OLED (#6740) - Add support for up to four MQTT GroupTopics using the same optional Device Group names (#8014) +- Add console command history (#7483, #8015) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index fa969d778..1d9d2e919 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -3,6 +3,7 @@ ### 8.2.0.2 20200328 - Add support for up to four MQTT GroupTopics using the same optional Device Group names (#8014) +- Add console command history (#7483, #8015) ### 8.2.0.1 20200321 diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index e4336b736..308fd5ed9 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -213,12 +213,12 @@ const char HTTP_SCRIPT_CONSOL[] PROGMEM = "}" // Console command history - part 2 - "var mh=20,hc=[],cn=0;" + "var hc=[],cn=0;" // hc = History commands, cn = Number of history being shown "function cH(a){" "var b=qs('#c1'),c=a.keyCode;" // c1 = Console command id "38==c?(++cn>hc.length&&(cn=hc.length),b.value=hc[cn-1]||''):" // Arrow Up "40==c?(0>--cn&&(cn=0),b.value=hc[cn-1]||''):" // Arrow Down - "13==c&&(hc.length>19&&hc.pop(),hc.unshift(b.value),cn=0)" // Enter + "13==c&&(hc.length>19&&hc.pop(),hc.unshift(b.value),cn=0)" // Enter, 19 = Max number -1 of commands in history "}" "wl(l);"; From 61b97350daefe4581162994b38482d241f7ecfcd Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Sun, 29 Mar 2020 16:29:22 +0200 Subject: [PATCH 032/547] ed300L support --- tasmota/xdrv_10_scripter.ino | 15 ++++++++++----- tasmota/xsns_53_sml.ino | 37 ++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index 30ed26598..330512c9d 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -1508,9 +1508,9 @@ chknext: lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); SCRIPT_SKIP_SPACES fvar=fvar1; - if ((*opp=='<' && fvar1' && fvar1>fvar2) || - (*opp=='=' && fvar1==fvar2)) + if ((*opp=='<' && fvar1' && fvar1>fvar2) || + (*opp=='=' && fvar1==fvar2)) { if (*lp!='<' && *lp!='>' && *lp!='=' && *lp!=')' && *lp!=SCRIPT_EOL) { float fvar3; @@ -1759,13 +1759,18 @@ chknext: float fvar3; lp=GetNumericResult(lp,OPER_EQU,&fvar3,0); fvar=SML_SetBaud(fvar1,fvar3); - } else { + } else if (fvar2==1) { char str[SCRIPT_MAXSSIZE]; lp=GetStringResult(lp,OPER_EQU,str,0); fvar=SML_Write(fvar1,str); + } else { +#ifdef ED300L + fvar=SML_Status(fvar1); +#else + fvar=0; +#endif } lp++; - fvar=0; len=0; goto exit; } diff --git a/tasmota/xsns_53_sml.ino b/tasmota/xsns_53_sml.ino index 53e6fff25..c5f2d3cc2 100755 --- a/tasmota/xsns_53_sml.ino +++ b/tasmota/xsns_53_sml.ino @@ -901,6 +901,11 @@ uint8_t dchars[16]; } } +#ifdef ED300L +uint8_t sml_status[MAX_METERS]; +uint8_t g_mindex; +#endif + // skip sml entries uint8_t *skip_sml(uint8_t *cp,int16_t *res) { uint8_t len,len1,type; @@ -933,6 +938,14 @@ double dval; // scan for values // check status +#ifdef ED300L + unsigned char *cpx=cp-5; + // decode OBIS 0180 amd extract direction info + if (*cp==0x64 && *cpx==0 && *(cpx+1)==0x01 && *(cpx+2)==0x08 && *(cpx+3)==0) { + sml_status[g_mindex]=*(cp+3); + } +#endif + cp=skip_sml(cp,&result); // check time cp=skip_sml(cp,&result); @@ -941,6 +954,7 @@ double dval; // check scaler cp=skip_sml(cp,&result); scaler=result; + // get value type=*cp&0x70; len=*cp&0x0f; @@ -1033,6 +1047,15 @@ double dval; } else if (scaler==3) { dval*=1000; } + #ifdef ED300L + // decode current power OBIS 00 0F 07 00 + if (*cpx==0x00 && *(cpx+1)==0x0f && *(cpx+2)==0x07 && *(cpx+3)==0) { + if (sml_status[g_mindex]&0x20) { + // and invert sign on solar feed + dval*=-1; + } + } + #endif return dval; } @@ -1465,6 +1488,9 @@ void SML_Decode(uint8_t index) { if (found) { // matches, get value mp++; +#ifdef ED300L + g_mindex=mindex; +#endif if (*mp=='#') { // get string value mp++; @@ -2061,6 +2087,17 @@ uint32_t SML_SetBaud(uint32_t meter, uint32_t br) { return 1; } +uint32_t SML_Status(uint32_t meter) { + if (meter<1 || meter>meters_used) return 0; + meter--; +#ifdef ED300L + return sml_status[meter]; +#else + return 0; +#endif +} + + uint32_t SML_Write(uint32_t meter,char *hstr) { if (meter<1 || meter>meters_used) return 0; meter--; From 4a2f3d87c5bd8e9ffc9369dccfdb8940ee3656ea Mon Sep 17 00:00:00 2001 From: to-scho Date: Sun, 29 Mar 2020 17:05:52 +0200 Subject: [PATCH 033/547] Enhanced counter debouncing New commands CounterDebounceLow and CounterDebounceHigh to allow individual debounce times for low and high pulse widths to discard non valid falling edges. These are checked before legacy CounterDebounce checks distance between to valid falling edges. Useful to have robust counter results when using e.g. an TCRT5000 optical sensor to count electromechanical "Ferraris" electricity meters. --- tasmota/settings.h | 4 ++- tasmota/xsns_01_counter.ino | 59 ++++++++++++++++++++++++++++++++++--- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/tasmota/settings.h b/tasmota/settings.h index fa98a8ecf..7bbb0e5de 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -470,8 +470,10 @@ struct SYSCFG { uint8_t bri_preset_high; // F07 int8_t hum_comp; // F08 - uint8_t free_f09[179]; // F09 + uint8_t free_f09[175]; // F09 + uint16_t pulse_counter_debounce_low; // FB8 + uint16_t pulse_counter_debounce_high; // FBA uint32_t keeloq_master_msb; // FBC uint32_t keeloq_master_lsb; // FC0 uint32_t keeloq_serial; // FC4 diff --git a/tasmota/xsns_01_counter.ino b/tasmota/xsns_01_counter.ino index 095693771..663d9413a 100644 --- a/tasmota/xsns_01_counter.ino +++ b/tasmota/xsns_01_counter.ino @@ -27,16 +27,20 @@ #define D_PRFX_COUNTER "Counter" #define D_CMND_COUNTERTYPE "Type" #define D_CMND_COUNTERDEBOUNCE "Debounce" +#define D_CMND_COUNTERDEBOUNCELOW "DebounceLow" +#define D_CMND_COUNTERDEBOUNCEHIGH "DebounceHigh" const char kCounterCommands[] PROGMEM = D_PRFX_COUNTER "|" // Prefix - "|" D_CMND_COUNTERTYPE "|" D_CMND_COUNTERDEBOUNCE ; + "|" D_CMND_COUNTERTYPE "|" D_CMND_COUNTERDEBOUNCE "|" D_CMND_COUNTERDEBOUNCELOW "|" D_CMND_COUNTERDEBOUNCEHIGH ; void (* const CounterCommand[])(void) PROGMEM = { - &CmndCounter, &CmndCounterType, &CmndCounterDebounce }; + &CmndCounter, &CmndCounterType, &CmndCounterDebounce, &CmndCounterDebounceLow, &CmndCounterDebounceHigh }; struct COUNTER { uint32_t timer[MAX_COUNTERS]; // Last counter time in micro seconds + uint32_t timer_low_high[MAX_COUNTERS]; // Last low/high counter time in micro seconds uint8_t no_pullup = 0; // Counter input pullup flag (1 = No pullup) + uint8_t pin_state = 0; // LSB0..3 Last state of counter pin; LSB7==0 IRQ is FALLING, LSB7==1 IRQ is CHANGE bool any_counter = false; } Counter; @@ -51,7 +55,30 @@ void CounterUpdate4(void) ICACHE_RAM_ATTR; void CounterUpdate(uint8_t index) { uint32_t time = micros(); - uint32_t debounce_time = time - Counter.timer[index]; + uint32_t debounce_time; + + if (Counter.pin_state) { + // handle low and high debounce times when configured + if (digitalRead(pin[GPIO_CNTR1 +index]) == bitRead(Counter.pin_state, index)) { + // new pin state to be ignored because debounce time was not met during last IRQ + return; + } + debounce_time = time - Counter.timer_low_high[index]; + if bitRead(Counter.pin_state, index) { + // last valid pin state was high, current pin state is low + if (debounce_time <= Settings.pulse_counter_debounce_high * 1000) return; + } else { + // last valid pin state was low, current pin state is high + if (debounce_time <= Settings.pulse_counter_debounce_low * 1000) return; + } + // passed debounce check, save pin state and timing + Counter.timer_low_high[index] = time; + Counter.pin_state ^= (1< Settings.pulse_counter_debounce * 1000) { Counter.timer[index] = time; if (bitRead(Settings.pulse_counter_type, index)) { @@ -103,7 +130,13 @@ void CounterInit(void) if (pin[GPIO_CNTR1 +i] < 99) { Counter.any_counter = true; pinMode(pin[GPIO_CNTR1 +i], bitRead(Counter.no_pullup, i) ? INPUT : INPUT_PULLUP); - attachInterrupt(pin[GPIO_CNTR1 +i], counter_callbacks[i], FALLING); + if ((0 == Settings.pulse_counter_debounce_low) && (0 == Settings.pulse_counter_debounce_high)) { + Counter.pin_state = 0; + attachInterrupt(pin[GPIO_CNTR1 +i], counter_callbacks[i], FALLING); + } else { + Counter.pin_state = 0x8f; + attachInterrupt(pin[GPIO_CNTR1 +i], counter_callbacks[i], CHANGE); + } } } } @@ -213,6 +246,24 @@ void CmndCounterDebounce(void) ResponseCmndNumber(Settings.pulse_counter_debounce); } +void CmndCounterDebounceLow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { + Settings.pulse_counter_debounce_low = XdrvMailbox.payload; + CounterInit(); + } + ResponseCmndNumber(Settings.pulse_counter_debounce_low); +} + +void CmndCounterDebounceHigh(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { + Settings.pulse_counter_debounce_high = XdrvMailbox.payload; + CounterInit(); + } + ResponseCmndNumber(Settings.pulse_counter_debounce_high); +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ From d713468c0c4699dba64000e6b388495c38ccdcdb Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 29 Mar 2020 17:41:31 +0200 Subject: [PATCH 034/547] Add support for longer template names Add support for longer template names --- RELEASENOTES.md | 3 ++- tasmota/CHANGELOG.md | 4 ++++ tasmota/settings.h | 4 +++- tasmota/settings.ino | 11 ++++++++++- tasmota/support.ino | 13 +++++++------ tasmota/support_command.ino | 2 +- tasmota/tasmota.h | 1 + tasmota/tasmota_version.h | 2 +- tasmota/xdrv_01_webserver.ino | 8 ++++---- 9 files changed, 33 insertions(+), 15 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 9e22461e2..43a769512 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -52,7 +52,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c ## Changelog -### Version 8.2.0.2 +### Version 8.2.0.3 - Change HM-10 sensor type detection and add features (#7962) - Fix possible Relay toggle on (OTA) restart @@ -63,3 +63,4 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Add support for 64x48 SSD1306 OLED (#6740) - Add support for up to four MQTT GroupTopics using the same optional Device Group names (#8014) - Add console command history (#7483, #8015) +- Add support for longer template names diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 1d9d2e919..0248d4ff7 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -1,5 +1,9 @@ ## Unreleased (development) +### 8.2.0.3 20200329 + +- Add support for longer template names + ### 8.2.0.2 20200328 - Add support for up to four MQTT GroupTopics using the same optional Device Group names (#8014) diff --git a/tasmota/settings.h b/tasmota/settings.h index fa98a8ecf..df15a0bfd 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -397,7 +397,9 @@ struct SYSCFG { uint16_t mcp230xx_int_timer; // 718 uint8_t rgbwwTable[5]; // 71A uint8_t user_template_base; // 71F - char user_template_name[15]; // 720 15 bytes + + char user_template_name[15]; // 720 15 bytes - Backward compatibility since v8.2.0.3 + mytmplt user_template; // 72F 14 bytes uint8_t novasds_startingoffset; // 73D uint8_t web_color[18][3]; // 73E diff --git a/tasmota/settings.ino b/tasmota/settings.ino index 5ebf970d4..3bb58ff43 100644 --- a/tasmota/settings.ino +++ b/tasmota/settings.ino @@ -589,6 +589,12 @@ char* SettingsText(uint32_t index) * Config Save - Save parameters to Flash ONLY if any parameter has changed \*********************************************************************************************/ +void UpdateBackwardCompatibility(void) +{ + // Perform updates for backward compatibility + strlcpy(Settings.user_template_name, SettingsText(SET_TEMPLATE_NAME), sizeof(Settings.user_template_name)); +} + uint32_t GetSettingsAddress(void) { return settings_location * SPI_FLASH_SEC_SIZE; @@ -605,6 +611,7 @@ void SettingsSave(uint8_t rotate) * stop_flash_rotate 1 = Allow only eeprom flash slot use (SetOption12 1) */ #ifndef FIRMWARE_MINIMAL + UpdateBackwardCompatibility(); 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; @@ -1394,7 +1401,6 @@ void SettingsDelta(void) Settings.mqtt_port = Settings.ex_mqtt_port; // 20A -> EFC memcpy((char*)&Settings.serial_config, (char*)&Settings.ex_serial_config, 5); // 1E4 -> EFE } - if (Settings.version < 0x08000000) { char temp[strlen(Settings.text_pool) +1]; strncpy(temp, Settings.text_pool, sizeof(temp)); // Was ota_url char temp21[strlen(Settings.ex_mqtt_prefix[0]) +1]; strncpy(temp21, Settings.ex_mqtt_prefix[0], sizeof(temp21)); @@ -1466,6 +1472,9 @@ void SettingsDelta(void) SettingsUpdateText(SET_FRIENDLYNAME3, Settings.ex_friendlyname[2]); SettingsUpdateText(SET_FRIENDLYNAME4, Settings.ex_friendlyname[3]); } + if (Settings.version < 0x08020003) { + SettingsUpdateText(SET_TEMPLATE_NAME, Settings.user_template_name); + } Settings.version = VERSION; SettingsSave(1); diff --git a/tasmota/support.ino b/tasmota/support.ino index 458a8efd3..81cb1b002 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -1098,9 +1098,9 @@ bool ValidModule(uint32_t index) String AnyModuleName(uint32_t index) { if (USER_MODULE == index) { - return String(Settings.user_template_name); + return String(SettingsText(SET_TEMPLATE_NAME)); } else { - char name[15]; + char name[TOPSZ]; return String(GetTextIndexed(name, sizeof(name), index, kModuleNames)); } } @@ -1154,7 +1154,8 @@ void ModuleDefault(uint32_t module) { if (USER_MODULE == module) { module = WEMOS; } // Generic Settings.user_template_base = module; - GetTextIndexed(Settings.user_template_name, sizeof(Settings.user_template_name), module, kModuleNames); + char name[TOPSZ]; + SettingsUpdateText(SET_TEMPLATE_NAME, GetTextIndexed(name, sizeof(name), module, kModuleNames)); memcpy_P(&Settings.user_template, &kModules[module], sizeof(mytmplt)); } @@ -1263,14 +1264,14 @@ bool JsonTemplate(const char* dataBuf) if (strlen(dataBuf) < 9) { return false; } // Workaround exception if empty JSON like {} - Needs checks - StaticJsonBuffer<350> jb; // 331 from https://arduinojson.org/v5/assistant/ + StaticJsonBuffer<400> jb; // 331 from https://arduinojson.org/v5/assistant/ JsonObject& obj = jb.parseObject(dataBuf); if (!obj.success()) { return false; } // All parameters are optional allowing for partial changes const char* name = obj[D_JSON_NAME]; if (name != nullptr) { - strlcpy(Settings.user_template_name, name, sizeof(Settings.user_template_name)); + SettingsUpdateText(SET_TEMPLATE_NAME, name); } if (obj[D_JSON_GPIO].success()) { for (uint32_t i = 0; i < sizeof(mycfgio); i++) { @@ -1291,7 +1292,7 @@ bool JsonTemplate(const char* dataBuf) void TemplateJson(void) { - Response_P(PSTR("{\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), Settings.user_template_name); + Response_P(PSTR("{\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), SettingsText(SET_TEMPLATE_NAME)); for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) { ResponseAppend_P(PSTR("%s%d"), (i>0)?",":"", Settings.user_template.gp.io[i]); } diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 36e5c08fd..64fd45997 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -1064,7 +1064,7 @@ void CmndTemplate(void) if (Settings.module != USER_MODULE) { ModuleDefault(Settings.module); } - snprintf_P(Settings.user_template_name, sizeof(Settings.user_template_name), PSTR("Merged")); + SettingsUpdateText(SET_TEMPLATE_NAME, "Merged"); uint32_t j = 0; for (uint32_t i = 0; i < sizeof(mycfgio); i++) { if (6 == i) { j = 9; } diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index 033b8d783..48a853891 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -302,6 +302,7 @@ enum SettingsTextIndex { SET_OTAURL, SET_BUTTON1, SET_BUTTON2, SET_BUTTON3, SET_BUTTON4, SET_BUTTON5, SET_BUTTON6, SET_BUTTON7, SET_BUTTON8, SET_BUTTON9, SET_BUTTON10, SET_BUTTON11, SET_BUTTON12, SET_BUTTON13, SET_BUTTON14, SET_BUTTON15, SET_BUTTON16, SET_MQTT_GRP_TOPIC2, SET_MQTT_GRP_TOPIC3, SET_MQTT_GRP_TOPIC4, + SET_TEMPLATE_NAME, SET_MAX }; enum DeviceGroupMessageType { DGR_MSGTYP_FULL_STATUS, DGR_MSGTYP_PARTIAL_UPDATE, DGR_MSGTYP_UPDATE, DGR_MSGTYP_UPDATE_MORE_TO_COME, DGR_MSGTYP_UPDATE_DIRECT, DGR_MSGTYP_REUPDATE }; diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h index 47879d1fe..3b8314f65 100644 --- a/tasmota/tasmota_version.h +++ b/tasmota/tasmota_version.h @@ -20,7 +20,7 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x08020002; +const uint32_t VERSION = 0x08020003; // Lowest compatible version const uint32_t VERSION_COMPATIBLE = 0x07010006; diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 308fd5ed9..80c4bf417 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -1510,9 +1510,9 @@ void HandleTemplateConfiguration(void) void TemplateSaveSettings(void) { - char tmp[sizeof(Settings.user_template_name)]; // WebGetArg NAME and GPIO/BASE/FLAG byte value + char tmp[TOPSZ]; // WebGetArg NAME and GPIO/BASE/FLAG byte value char webindex[5]; // WebGetArg name - char svalue[128]; // Template command string + char svalue[200]; // Template command string WebGetArg("s1", tmp, sizeof(tmp)); // NAME snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_TEMPLATE " {\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), tmp); @@ -1528,11 +1528,11 @@ void TemplateSaveSettings(void) j++; } - WebGetArg("g17", tmp, sizeof(tmp)); // FLAG - ADC0 + WebGetArg("g17", tmp, sizeof(tmp)); // FLAG - ADC0 uint32_t flag = atoi(tmp); for (uint32_t i = 0; i < GPIO_FLAG_USED; i++) { snprintf_P(webindex, sizeof(webindex), PSTR("c%d"), i); - uint32_t state = WebServer->hasArg(webindex) << i +4; // FLAG + uint32_t state = WebServer->hasArg(webindex) << i +4; // FLAG flag += state; } WebGetArg("g99", tmp, sizeof(tmp)); // BASE From dd9f7920dec84f35c969b45fdefdc2cc26b7ab03 Mon Sep 17 00:00:00 2001 From: blakadder Date: Sun, 29 Mar 2020 23:28:41 +0200 Subject: [PATCH 035/547] changed links to new docs architecture --- .github/ISSUE_TEMPLATE/Bug_report.md | 4 ++-- .github/ISSUE_TEMPLATE/config.yml | 2 +- .github/issue-close-app.yml | 2 +- API.md | 2 +- CONTRIBUTING.md | 2 +- README.md | 12 ++++++------ RELEASENOTES.md | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index 19c9bd00d..993c31ea3 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -13,7 +13,7 @@ Please DO NOT OPEN AN ISSUE: - If your issue is a flashing issue, please address it to the [Tasmota Support Chat](https://discord.gg/Ks2Kzd4) - If your issue is compilation problem, please address it to the [Tasmota Support Chat](https://discord.gg/Ks2Kzd4) - If your issue has been addressed before (i.e., duplicated issue), please ask in the original issue - - If your issue is a Wi-Fi problem or MQTT problem, please try the steps provided in the [FAQ](https://tasmota.github.io/docs/#/help/FAQ) and [Troubleshooting](https://tasmota.github.io/docs/#/help/Troubleshooting) + - If your issue is a Wi-Fi problem or MQTT problem, please try the steps provided in the [FAQ](https://tasmota.github.io/docs/FAQ) and [Troubleshooting](https://tasmota.github.io/docs/Troubleshooting) Please take a few minutes to complete the requested information below. Our ability to provide assistance is greatly hampered without it. The details requested potentially affect which options to pursue. The small amount of time you spend completing the template will also help the volunteers providing the assistance to you to reduce the time required to help you. @@ -29,7 +29,7 @@ _Make sure your have performed every step and checked the applicable boxes befor - [ ] Read the [Contributing Guide and Policy](https://github.com/arendst/Tasmota/blob/development/CONTRIBUTING.md) and [the Code of Conduct](https://github.com/arendst/Tasmota/blob/development/CODE_OF_CONDUCT.md) - [ ] Searched the problem in [issues](https://github.com/arendst/Tasmota/issues) -- [ ] Searched the problem in the [docs](https://tasmota.github.io/docs/#/help/FAQ) +- [ ] Searched the problem in the [docs](https://tasmota.github.io/docs/FAQ) - [ ] Searched the problem in the [forum](https://groups.google.com/d/forum/sonoffusers) - [ ] Searched the problem in the [chat](https://discord.gg/Ks2Kzd4) - [ ] Device used (e.g., Sonoff Basic): _____ diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 9c78f2c84..225df7f86 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,7 +1,7 @@ blank_issues_enabled: false contact_links: - name: Tasmota Docs - url: https://tasmota.github.io/docs/#/ + url: https://tasmota.github.io/docs about: All the information related to Tasmota - name: Tasmota Support Chat url: https://discord.gg/Ks2Kzd4 diff --git a/.github/issue-close-app.yml b/.github/issue-close-app.yml index d222d8980..6a6947a9c 100644 --- a/.github/issue-close-app.yml +++ b/.github/issue-close-app.yml @@ -19,7 +19,7 @@ comment: >- [Support Information](https://github.com/arendst/Sonoff-Tasmota/blob/development/SUPPORT.md) - [Wiki](https://tasmota.github.io/docs/#/) for more information. + [Wiki](https://tasmota.github.io/docs/) for more information. [Chat](https://discord.gg/Ks2Kzd4) for more user experience. diff --git a/API.md b/API.md index f10ae2c93..7bb77735b 100644 --- a/API.md +++ b/API.md @@ -2,7 +2,7 @@ # Basic API information -Tasmota can easily be extended by developers using provided function pointers as callback Ids. This document lists the available callback function Ids. Read [Sensor API](https://tasmota.github.io/docs/#/Sensor-API) for more information. +Tasmota can easily be extended by developers using provided function pointers as callback Ids. This document lists the available callback function Ids. Read [Sensor API](https://tasmota.github.io/docs/Sensor-API) for more information. Callback availability can be checked by searching for either XdrvCall, XsnsCall, XdspCall, XnrgCall and XlgtCall. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 43cead24a..26db830ac 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ Everybody is welcome and invited to contribute to Tasmota Project by: * Testing newly released features and reporting issues. * Providing Pull Requests (Features, Proof of Concepts, Language files or Fixes) -* Contributing missing documentation for features and devices in our [documentation](https://tasmota.github.io/docs/#/Contributing) +* Contributing missing documentation for features and devices in our [documentation](https://tasmota.github.io/docs/Contributing) This document describes rules that are in effect for this repository, meant for handling issues by contributors in the issue tracker and PRs. diff --git a/README.md b/README.md index 53f977ae2..23359fe22 100644 --- a/README.md +++ b/README.md @@ -42,10 +42,10 @@ We don't take any responsibility nor liability for using this software nor for t ## Note -Please do not ask to add new devices unless it requires additional code for new features. If the device is not listed as a module, try using [Templates](https://tasmota.github.io/docs/#/Templates) first. If it is not listed in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates) create your own [Template](https://tasmota.github.io/docs/#/Templates?id=creating-your-template). +Please do not ask to add new devices unless it requires additional code for new features. If the device is not listed as a module, try using [Templates](https://tasmota.github.io/docs/Templates) first. If it is not listed in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates) create your own [Template](https://tasmota.github.io/docs/Templates#creating-your-template). ## Quick Install -Download one of the released binaries from https://github.com/arendst/Tasmota/releases and flash it to your hardware [using our installation guide](https://tasmota.github.io/docs/#/installation/). +Download one of the released binaries from https://github.com/arendst/Tasmota/releases and flash it to your hardware [using our installation guide](https://tasmota.github.io/docs/Getting-Started). ## Important User Compilation Information If you want to compile Tasmota yourself keep in mind the following: @@ -60,7 +60,7 @@ Please refer to the installation and configuration articles in our [documentatio ## Migration Information -See [wiki migration path](https://tasmota.github.io/docs/#/Upgrading?id=migration-path) for instructions how to migrate to a major version. Pay attention to the following version breaks due to dynamic settings updates: +See [wiki migration path](https://tasmota.github.io/docs/Upgrading#migration-path) for instructions how to migrate to a major version. Pay attention to the following version breaks due to dynamic settings updates: 1. Migrate to **Sonoff-Tasmota 3.9.x** 2. Migrate to **Sonoff-Tasmota 4.x** @@ -77,15 +77,15 @@ See [wiki migration path](https://tasmota.github.io/docs/#/Upgrading?id=migratio -For a database of supported devices see [Tasmota Device Templates Repository](https://blakadder.github.io/templates) +For a database of supported devices see [Tasmota Device Templates Repository](https://templates.blakadder.com) If you're looking for support on **Tasmota** there are some options available: ### Documentation * [Documentation Site](https://tasmota.github.io/docs): For information on how to flash Tasmota, configure, use and expand it -* [FAQ and Troubleshooting](https://tasmota.github.io/docs/#/help/): For information on common problems and solutions. -* [Commands Information](https://tasmota.github.io/docs/#/Commands): For information on all the commands supported by Tasmota. +* [FAQ and Troubleshooting](https://tasmota.github.io/docs/FAQ/): For information on common problems and solutions. +* [Commands Information](https://tasmota.github.io/docs/Commands): For information on all the commands supported by Tasmota. ### Support's Community diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 43a769512..c4c1c4c61 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -4,7 +4,7 @@ ## Migration Information -See [migration path](https://tasmota.github.io/docs/#/Upgrading?id=migration-path) for instructions how to migrate to a major version. Pay attention to the following version breaks due to dynamic settings updates: +See [migration path](https://tasmota.github.io/docs/Upgrading#migration-path) for instructions how to migrate to a major version. Pay attention to the following version breaks due to dynamic settings updates: 1. Migrate to **Sonoff-Tasmota 3.9.x** 2. Migrate to **Sonoff-Tasmota 4.x** @@ -27,7 +27,7 @@ Although it might still compile on previous Core versions all support will be re ## Support of TLS -To save resources when TLS is enabled mDNS needs to be disabled. In addition to TLS using fingerprints now also user supplied CA certs and AWS IoT is supported. Read [full documentation](https://tasmota.github.io/docs/#/integrations/AWS-IoT) +To save resources when TLS is enabled mDNS needs to be disabled. In addition to TLS using fingerprints now also user supplied CA certs and AWS IoT is supported. Read [full documentation](https://tasmota.github.io/docs/AWS-IoT) ## Initial configuration tools From 4631fb729ae2539012d18f65007340fff172a362 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 30 Mar 2020 12:52:09 +0200 Subject: [PATCH 036/547] Add compile error message Add compile error message when both rules and scripts are selected (#8026) --- tasmota/my_user_config.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index ab80c4cf6..70504293c 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -684,4 +684,8 @@ #error "Select either USE_DISCOVERY or USE_MQTT_AWS_IOT, mDNS takes too much code space and is not needed for AWS IoT" #endif +#if defined(USE_RULES) && defined(USE_SCRIPT) + #error "Select either USE_RULES or USE_SCRIPT. They can't both be used at the same time" +#endif + #endif // _MY_USER_CONFIG_H_ From 490a7f2d9f123ae96d8fcbe9564b99adbfa2aa17 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 30 Mar 2020 17:11:07 +0200 Subject: [PATCH 037/547] Turn browser autocomplete off whan arrow is pressed Turn browser autocomplete off and use command history as soon as an arrow key is pressed allowing mobiles and tablets to keep using browser autocomplete. (#8015) --- tasmota/xdrv_01_webserver.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 80c4bf417..ea874b7f2 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -216,6 +216,7 @@ const char HTTP_SCRIPT_CONSOL[] PROGMEM = "var hc=[],cn=0;" // hc = History commands, cn = Number of history being shown "function cH(a){" "var b=qs('#c1'),c=a.keyCode;" // c1 = Console command id + "if(38==c||40==c){b.autocomplete='off';}" // Arrow up or down must be a keyboard so stop browser autocomplete "38==c?(++cn>hc.length&&(cn=hc.length),b.value=hc[cn-1]||''):" // Arrow Up "40==c?(0>--cn&&(cn=0),b.value=hc[cn-1]||''):" // Arrow Down "13==c&&(hc.length>19&&hc.pop(),hc.unshift(b.value),cn=0)" // Enter, 19 = Max number -1 of commands in history From 89014a915e77d2614b3f1b66aac3d9e7e42a29f6 Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Mon, 30 Mar 2020 17:33:12 +0200 Subject: [PATCH 038/547] Update Italian language Update italian language Revised with feedback of @effeelle Fixed a typo about D_SENSOR_RELAY --- tasmota/language/it-IT.h | 726 +++++++++++++++++++-------------------- 1 file changed, 363 insertions(+), 363 deletions(-) diff --git a/tasmota/language/it-IT.h b/tasmota/language/it-IT.h index d9a4b9a6b..12b730bbc 100644 --- a/tasmota/language/it-IT.h +++ b/tasmota/language/it-IT.h @@ -56,14 +56,14 @@ #define D_AP "AP" // Access Point #define D_AS "come" #define D_AUTO "AUTO" -#define D_BLINK "Blink" -#define D_BLINKOFF "BlinkOff" +#define D_BLINK "Lampeggia" +#define D_BLINKOFF "Lampeggia OFF" #define D_BOOT_COUNT "Numero di boot" #define D_BRIGHTLIGHT "Luminoso" #define D_BSSID "BSSId" #define D_BUTTON "Pulsante" #define D_BY "da" // Written by me -#define D_BYTES "Bytes" +#define D_BYTES "Byte" #define D_CELSIUS "Celsius" #define D_CHANNEL "Canale" #define D_CO2 "CO2" @@ -71,24 +71,24 @@ #define D_COLDLIGHT "Fredda" #define D_COMMAND "Comando" #define D_CONNECTED "Connesso" -#define D_CORS_DOMAIN "CORS Domain" +#define D_CORS_DOMAIN "Dominio CORS" #define D_COUNT "Conteggio" #define D_COUNTER "Contatore" -#define D_CT_POWER "CT Power" +#define D_CT_POWER "Alimentazione CT" #define D_CURRENT "Corrente" // As in Voltage and Current #define D_DATA "Dati" #define D_DARKLIGHT "Scuro" #define D_DEBUG "Debug" -#define D_DEWPOINT "Dew point" +#define D_DEWPOINT "Punto rugiada" // #define D_DISABLED "Disabilitato" #define D_DISTANCE "Distanza" -#define D_DNS_SERVER "DNS Server" -#define D_DONE "Fatto" +#define D_DNS_SERVER "Server DNS" +#define D_DONE "Completato" #define D_DST_TIME "DST" #define D_ECO2 "eCO₂" #define D_EMULATION "Emulazione" #define D_ENABLED "Abilitato" -#define D_ERASE "Cancellare" +#define D_ERASE "Cancella" #define D_ERROR "Errore" #define D_FAHRENHEIT "Fahrenheit" #define D_FAILED "Fallito" @@ -96,14 +96,14 @@ #define D_FALLBACK_TOPIC "Topic Riserva" #define D_FALSE "Falso" #define D_FILE "File" -#define D_FLOW_RATE "Flow rate" +#define D_FLOW_RATE "Flusso dati" #define D_FREE_MEMORY "Memoria Libera" #define D_FREQUENCY "Frequenza" #define D_GAS "Gas" #define D_GATEWAY "Gateway" #define D_GROUP "Gruppo" #define D_HOST "Host" -#define D_HOSTNAME "Nome Host" +#define D_HOSTNAME "Nome host" #define D_HUMIDITY "Umidità" #define D_ILLUMINANCE "Illuminazione" #define D_IMMEDIATE "immediato" // Button immediate @@ -120,55 +120,55 @@ #define D_MULTI_PRESS "multi-pressione" #define D_NOISE "Rumore" #define D_NONE "Nessuno" -#define D_OFF "Off" +#define D_OFF "OFF" #define D_OFFLINE "Offline" -#define D_OK "Ok" -#define D_ON "On" +#define D_OK "OK" +#define D_ON "ON" #define D_ONLINE "Online" #define D_PASSWORD "Password" #define D_PORT "Porta" #define D_POWER_FACTOR "Fattore di potenza" #define D_POWERUSAGE "Potenza" -#define D_POWERUSAGE_ACTIVE "Potenza Attiva" -#define D_POWERUSAGE_APPARENT "Potenza Apparente" -#define D_POWERUSAGE_REACTIVE "Potenza Reattiva" +#define D_POWERUSAGE_ACTIVE "Potenza attiva" +#define D_POWERUSAGE_APPARENT "Potenza apparente" +#define D_POWERUSAGE_REACTIVE "Potenza reattiva" #define D_PRESSURE "Pressione" #define D_PRESSUREATSEALEVEL "Pressione al livello del mare" -#define D_PROGRAM_FLASH_SIZE "Dimensione Flash Programma" -#define D_PROGRAM_SIZE "Dimensione Programma" +#define D_PROGRAM_FLASH_SIZE "Dimensione flash programma" +#define D_PROGRAM_SIZE "Dimensione programma" #define D_PROJECT "Progetto" -#define D_RAIN "Rain" -#define D_RANGE "Range" +#define D_RAIN "Pioggia" +#define D_RANGE "Intervallo" #define D_RECEIVED "Ricevuto" #define D_RESTART "Riavvio" -#define D_RESTARTING "Riavviando" -#define D_RESTART_REASON "Causa Riavvio" +#define D_RESTARTING "Riavvio" +#define D_RESTART_REASON "Causa riavvio" #define D_RESTORE "ripristino" #define D_RETAINED "salvato" -#define D_RULE "Rule" +#define D_RULE "Regola" #define D_SAVE "Salva" #define D_SENSOR "Sensore" -#define D_SSID "SSId" -#define D_START "Start" +#define D_SSID "SSID" +#define D_START "Avvia" #define D_STD_TIME "STD" #define D_STOP "Stop" -#define D_SUBNET_MASK "Maschera Subnet" -#define D_SUBSCRIBE_TO "Sottoscrivi a" -#define D_UNSUBSCRIBE_FROM "Cancella da" -#define D_SUCCESSFUL "Riuscito" +#define D_SUBNET_MASK "Maschera sottorete" +#define D_SUBSCRIBE_TO "Abbonati a" +#define D_UNSUBSCRIBE_FROM "Rimuovi abbonamento da" +#define D_SUCCESSFUL "Completato" #define D_SUNRISE "Alba" #define D_SUNSET "Tramonto" #define D_TEMPERATURE "Temperatura" #define D_TO "a" -#define D_TOGGLE "Toggle" +#define D_TOGGLE "ON/OFF" #define D_TOPIC "Topic" -#define D_TOTAL_USAGE "Total Usage" +#define D_TOTAL_USAGE "Uso totale" #define D_TRANSMIT "Trasmesso" #define D_TRUE "Vero" #define D_TVOC "TVOC" -#define D_UPGRADE "Aggiornamento" +#define D_UPGRADE "Aggiorna" #define D_UPLOAD "Invio" -#define D_UPTIME "Uptime" +#define D_UPTIME "Tempo accensione" #define D_USER "Utente" #define D_UTC_TIME "UTC" #define D_UV_INDEX "Indice UV" @@ -178,242 +178,242 @@ #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" +#define D_UV_INDEX_7 "OoR" // Out of Range #define D_UV_LEVEL "Livello UV" #define D_UV_POWER "Intensità UV" #define D_VERSION "Versione" #define D_VOLTAGE "Tensione" #define D_WEIGHT "Peso" #define D_WARMLIGHT "Calda" -#define D_WEB_SERVER "Web Server" +#define D_WEB_SERVER "Server web" // tasmota.ino #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_LEVEL_10 "livello 1-0" +#define D_LEVEL_01 "livello 0-1" +#define D_SERIAL_LOGGING_DISABLED "Registro attività seriale disabilitato" #define D_SYSLOG_LOGGING_REENABLED "Syslog ri-abilitato" -#define D_SET_BAUDRATE_TO "Baudrate impostato a" +#define D_SET_BAUDRATE_TO "Imposta baudrate" #define D_RECEIVED_TOPIC "Topic Ricevuto" -#define D_DATA_SIZE "Dimensione Dati" -#define D_ANALOG_INPUT "Ingresso Analogico" +#define D_DATA_SIZE "Dimensione dati" +#define D_ANALOG_INPUT "Ingresso analogico" // support.ino #define D_OSWATCH "osWatch" -#define D_BLOCKED_LOOP "Ciclo Bloccato" -#define D_WPS_FAILED_WITH_STATUS "WPSconfig Fallito con stato" +#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_FAILED_TO_START "Partenza fallita" -#define D_PATCH_ISSUE_2186 "Patch issue 2186" +#define D_FAILED_TO_START "Avvio fallito" +#define D_PATCH_ISSUE_2186 "Patch problema 2186" #define D_CONNECTING_TO_AP "Connessione ad AP" #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" -#define D_CONNECT_FAILED_AP_TIMEOUT "Connessione fallita, timeout AP" +#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" +#define D_CONNECT_FAILED_AP_TIMEOUT "Connessione fallita - timeout AP" #define D_ATTEMPTING_CONNECTION "Tentativo di connessione..." #define D_CHECKING_CONNECTION "Controllo connessione..." #define D_QUERY_DONE "Query eseguita. Servizio MQTT trovato" -#define D_MQTT_SERVICE_FOUND "Servizio MQTT trovato su" -#define D_FOUND_AT "trovato a" -#define D_SYSLOG_HOST_NOT_FOUND "Syslog Host non trovato" +#define D_MQTT_SERVICE_FOUND "Servizio MQTT trovato in" +#define D_FOUND_AT "trovato in" +#define D_SYSLOG_HOST_NOT_FOUND "Host Syslog non trovato" // settings.ino #define D_SAVED_TO_FLASH_AT "Salvato nella flash in" #define D_LOADED_FROM_FLASH_AT "Caricato dalla flash da" -#define D_USE_DEFAULTS "Utilizzo valori default" +#define D_USE_DEFAULTS "Usa valori predefiniti" #define D_ERASED_SECTOR "Settore cancellato" // xdrv_02_webserver.ino -#define D_NOSCRIPT "Abilitare JavaScript per utilizzare Tasmota" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
effettuare aggiornamento" -#define D_WEBSERVER_ACTIVE_ON "Web server attivo su" +#define D_NOSCRIPT "Per usare Tasmota abilita JavaScript" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "FFirmware MINIMALE
Effettua aggiornamento" +#define D_WEBSERVER_ACTIVE_ON "Server web attivo in" #define D_WITH_IP_ADDRESS "con indirizzo IP" -#define D_WEBSERVER_STOPPED "Web server arrestato" -#define D_FILE_NOT_FOUND "File Non Trovato" +#define D_WEBSERVER_STOPPED "Server web arrestato" +#define D_FILE_NOT_FOUND "File non trovato" #define D_REDIRECTED "Redirezione al captive portal" #define D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION "Impostazione Wifimanager come AccessPoint e Station" #define D_WIFIMANAGER_SET_ACCESSPOINT "Impostazione Wifimanager come AccessPoint" -#define D_TRYING_TO_CONNECT "Tentativo di connessione del dispositivo alla rete" +#define D_TRYING_TO_CONNECT "Tentativo connessione dispositivo alla rete" -#define D_RESTART_IN "Riavvio in" +#define D_RESTART_IN "Riavvio tra" #define D_SECONDS "secondi" #define D_DEVICE_WILL_RESTART "Il dispositivo verrà riavviato tra pochi secondi" -#define D_BUTTON_TOGGLE "On/Off" +#define D_BUTTON_TOGGLE "ON/OFF" #define D_CONFIGURATION "Configurazione" #define D_INFORMATION "Informazioni" -#define D_FIRMWARE_UPGRADE "Aggiornamento Firmware" +#define D_FIRMWARE_UPGRADE "Aggiornamento firmware" #define D_CONSOLE "Console" -#define D_CONFIRM_RESTART "Conferma Riavvio" +#define D_CONFIRM_RESTART "Conferma riavvio" -#define D_CONFIGURE_MODULE "Configurazione Modulo" -#define D_CONFIGURE_WIFI "Configurazione WiFi" -#define D_CONFIGURE_MQTT "Configurazione MQTT" -#define D_CONFIGURE_DOMOTICZ "Configurazione Domoticz" -#define D_CONFIGURE_LOGGING "Configurazione Logging" -#define D_CONFIGURE_OTHER "Configurazione Extra" -#define D_CONFIRM_RESET_CONFIGURATION "Conferma Reset Configurazione" -#define D_RESET_CONFIGURATION "Reset Configurazione" -#define D_BACKUP_CONFIGURATION "Backup Configurazione" -#define D_RESTORE_CONFIGURATION "Ripristino Configurazione" -#define D_MAIN_MENU "Menu Principale" +#define D_CONFIGURE_MODULE "Configura modulo" +#define D_CONFIGURE_WIFI "Configura WiFi" +#define D_CONFIGURE_MQTT "Configura MQTT" +#define D_CONFIGURE_DOMOTICZ "Configura Domoticz" +#define D_CONFIGURE_LOGGING "Configura registrazione" +#define D_CONFIGURE_OTHER "Configura extra" +#define D_CONFIRM_RESET_CONFIGURATION "Conferma ripristino configurazione" +#define D_RESET_CONFIGURATION "Ripristino configurazione" +#define D_BACKUP_CONFIGURATION "Backup configurazione" +#define D_RESTORE_CONFIGURATION "Ripristino configurazione" +#define D_MAIN_MENU "Menu principale" -#define D_MODULE_PARAMETERS "Parametri del modulo" +#define D_MODULE_PARAMETERS "Parametri modulo" #define D_MODULE_TYPE "Tipo modulo" -#define D_PULLUP_ENABLE "No Button/Switch pull-up" +#define D_PULLUP_ENABLE "Nessun pulsante/switch pull-up" #define D_ADC "ADC" #define D_GPIO "GPIO" -#define D_SERIAL_IN "Seriale In" -#define D_SERIAL_OUT "Seriale 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" -#define D_SCAN_DONE "Scansione effettuata" +#define D_WIFI_PARAMETERS "Parametri WiFi" +#define D_SCAN_FOR_WIFI_NETWORKS "Scansione reti WiFi" +#define D_SCAN_DONE "Scansione completata" #define D_NO_NETWORKS_FOUND "Nessuna rete trovata" -#define D_REFRESH_TO_SCAN_AGAIN "Ricarica per nuova scansione" +#define D_REFRESH_TO_SCAN_AGAIN "Aggiorna per nuova scansione" #define D_DUPLICATE_ACCESSPOINT "AccessPoint duplicato" #define D_SKIPPING_LOW_QUALITY "Ignorato a causa di bassa qualità" #define D_RSSI "RSSI" #define D_WEP "WEP" #define D_WPA_PSK "WPA PSK" #define D_WPA2_PSK "WPA2 PSK" -#define D_AP1_SSID "AP1 SSId" -#define D_AP1_PASSWORD "AP1 Password" -#define D_AP2_SSID "AP2 SSId" -#define D_AP2_PASSWORD "AP2 Password" +#define D_AP1_SSID "AP1 - SSID" +#define D_AP1_PASSWORD "AP1 - Password" +#define D_AP2_SSID "AP2 - SSID" +#define D_AP2_PASSWORD "AP2 - Password" #define D_MQTT_PARAMETERS "Parametri MQTT" #define D_CLIENT "Client" #define D_FULL_TOPIC "Full Topic" -#define D_LOGGING_PARAMETERS "Parametri Logging" -#define D_SERIAL_LOG_LEVEL "Livello di log Seriale" -#define D_MQTT_LOG_LEVEL "Mqtt log level" -#define D_WEB_LOG_LEVEL "livello di log Web" -#define D_SYS_LOG_LEVEL "livello di log Sys" +#define D_LOGGING_PARAMETERS "Parametri registrazione" +#define D_SERIAL_LOG_LEVEL "Livello registrazione seriale" +#define D_MQTT_LOG_LEVEL "Livello registrazione MQTT" +#define D_WEB_LOG_LEVEL "Livello registrazione web" +#define D_SYS_LOG_LEVEL "Livello registrazione Sys" #define D_MORE_DEBUG "Debug aggiuntivo" -#define D_SYSLOG_HOST "Syslog host" -#define D_SYSLOG_PORT "Syslog porta" +#define D_SYSLOG_HOST "Host Syslog" +#define D_SYSLOG_PORT "Porta Syslog" #define D_TELEMETRY_PERIOD "Periodo Telemetria" #define D_OTHER_PARAMETERS "Altri parametri" #define D_TEMPLATE "Modello" -#define D_ACTIVATE "Attivare" -#define D_WEB_ADMIN_PASSWORD "Password Amministratore Web" +#define D_ACTIVATE "Attiva" +#define D_WEB_ADMIN_PASSWORD "Password amministratore web" #define D_MQTT_ENABLE "Abilita MQTT" #define D_FRIENDLY_NAME "Nome amichevole" #define D_BELKIN_WEMO "Belkin WeMo" -#define D_HUE_BRIDGE "Hue Bridge" +#define D_HUE_BRIDGE "Bridge Hue" #define D_SINGLE_DEVICE "dispositivo singolo" #define D_MULTI_DEVICE "dispositivo multiplo" -#define D_CONFIGURE_TEMPLATE "Configurare Modello" -#define D_TEMPLATE_PARAMETERS "Parametri Modello" +#define D_CONFIGURE_TEMPLATE "Configura modello" +#define D_TEMPLATE_PARAMETERS "Parametri modello" #define D_TEMPLATE_NAME "Nome" -#define D_BASE_TYPE "Basato nel" +#define D_BASE_TYPE "Basato su" #define D_TEMPLATE_FLAGS "Opzioni" #define D_SAVE_CONFIGURATION "Salva configurazione" #define D_CONFIGURATION_SAVED "Configurazione salvata" -#define D_CONFIGURATION_RESET "Configurazione azzerata" +#define D_CONFIGURATION_RESET "Configurazione ripristinata" -#define D_PROGRAM_VERSION "Versione del programma" +#define D_PROGRAM_VERSION "Versione programma" #define D_BUILD_DATE_AND_TIME "Data e ora compilazione" -#define D_CORE_AND_SDK_VERSION "Versione Core/SDK" -#define D_FLASH_WRITE_COUNT "Contatore scrittura Flash" +#define D_CORE_AND_SDK_VERSION "Versione core/SDK" +#define D_FLASH_WRITE_COUNT "Contatore scritture flash" #define D_MAC_ADDRESS "Indirizzo MAC" -#define D_MQTT_HOST "MQTT Host" -#define D_MQTT_PORT "MQTT Porta" -#define D_MQTT_CLIENT "MQTT Client" -#define D_MQTT_USER "MQTT Utente" -#define D_MQTT_TOPIC "MQTT Topic" -#define D_MQTT_GROUP_TOPIC "MQTT Group Topic" -#define D_MQTT_FULL_TOPIC "MQTT Full Topic" -#define D_MDNS_DISCOVERY "mDNS Discovery" -#define D_MDNS_ADVERTISE "mDNS Advertise" -#define D_ESP_CHIP_ID "ESP Chip Id" -#define D_FLASH_CHIP_ID "Flash Chip Id" -#define D_FLASH_CHIP_SIZE "Dimensione Flash" -#define D_FREE_PROGRAM_SPACE "Spazio Libero Memoria Programma" +#define D_MQTT_HOST "Host MQTT" +#define D_MQTT_PORT "Porta MQTT" +#define D_MQTT_CLIENT "Client MQTT" +#define D_MQTT_USER "Utente MQTT" +#define D_MQTT_TOPIC "Topic MQTT" +#define D_MQTT_GROUP_TOPIC "Gruppo topic MQTT" +#define D_MQTT_FULL_TOPIC "Full topic MQTT" +#define D_MDNS_DISCOVERY "Ricerca mDNS" +#define D_MDNS_ADVERTISE "Notifica mDNS" +#define D_ESP_CHIP_ID "ID chip ESP" +#define D_FLASH_CHIP_ID "ID chip flash" +#define D_FLASH_CHIP_SIZE "Dimensione flash" +#define D_FREE_PROGRAM_SPACE "Spazio libero memoria programma" -#define D_UPGRADE_BY_WEBSERVER "Aggiornamento da web server" -#define D_OTA_URL "OTA Url" +#define D_UPGRADE_BY_WEBSERVER "Aggiornamento via server web" +#define D_OTA_URL "OTA URL" #define D_START_UPGRADE "Avvio aggiornamento" -#define D_UPGRADE_BY_FILE_UPLOAD "Aggiornamento tramite invio file" -#define D_UPLOAD_STARTED "Invio iniziato" +#define D_UPGRADE_BY_FILE_UPLOAD "Aggiornamento tramite upload file" +#define D_UPLOAD_STARTED "Upload iniziato" #define D_UPGRADE_STARTED "Aggiornamento avviato" -#define D_UPLOAD_DONE "Invio effettuato" +#define D_UPLOAD_DONE "Upload completato" #define D_UPLOAD_ERR_1 "Nessun file selezionato" #define D_UPLOAD_ERR_2 "Spazio insufficiente" #define D_UPLOAD_ERR_3 "Magic byte non corrispondente a 0xE9" -#define D_UPLOAD_ERR_4 "Dimensione della memoria programma maggiore della dimensione reale della flash" -#define D_UPLOAD_ERR_5 "Errore di comparazione del buffer di invio" -#define D_UPLOAD_ERR_6 "Invio fallito. Abilitare logging 3" -#define D_UPLOAD_ERR_7 "Invio annullato" +#define D_UPLOAD_ERR_4 "Dimensione memoria programma maggiore della dimensione reale della flash" +#define D_UPLOAD_ERR_5 "Errore comparazione buffer upload" +#define D_UPLOAD_ERR_6 "Invio fallito. Abilita registrazione logging 3" +#define D_UPLOAD_ERR_7 "Upload annullato" #define D_UPLOAD_ERR_8 "File non valido" #define D_UPLOAD_ERR_9 "File troppo grande" -#define D_UPLOAD_ERR_10 "Inizializzazione fallita del chip RF" -#define D_UPLOAD_ERR_11 "Cancellazione fallita del chip RF" -#define D_UPLOAD_ERR_12 "Scrittura fallita del chip RF" -#define D_UPLOAD_ERR_13 "Decodifica fallita del firmware RF" -#define D_UPLOAD_ERR_14 "Not compatible" -#define D_UPLOAD_ERROR_CODE "Codice errore invio" +#define D_UPLOAD_ERR_10 "Inizializzazione chip RF fallita" +#define D_UPLOAD_ERR_11 "Cancellazione chip RF fallita" +#define D_UPLOAD_ERR_12 "Scrittura chip RF fallita" +#define D_UPLOAD_ERR_13 "Decodifica firmware RF fallita" +#define D_UPLOAD_ERR_14 "Non compatibile" +#define D_UPLOAD_ERROR_CODE "Codice errore upload" -#define D_ENTER_COMMAND "Inserire comando" -#define D_ENABLE_WEBLOG_FOR_RESPONSE "Abilitare weblog 2 se è attesa una risposta" -#define D_NEED_USER_AND_PASSWORD "Richiesto user=&password=" +#define D_ENTER_COMMAND "Inserisci comando" +#define D_ENABLE_WEBLOG_FOR_RESPONSE "Abilita weblog 2 se è attesa una risposta" +#define D_NEED_USER_AND_PASSWORD "Richiesto utente=&password=" // xdrv_01_mqtt.ino -#define D_FINGERPRINT "Verifica TLS fingerprint..." +#define D_FINGERPRINT "Verifica chiave TLS..." #define D_TLS_CONNECT_FAILED_TO "Connessione TLS fallita a" #define D_RETRY_IN "Nuovo tentativo in" -#define D_VERIFIED "Verificato Fingerprint" -#define D_INSECURE "Connessione insicura a causa di Fingerprint non valido" -#define D_CONNECT_FAILED_TO "Connessione Fallita a" +#define D_VERIFIED "Verificato tramite chiave" +#define D_INSECURE "Connessione non sicura a causa di chiave non valida" +#define D_CONNECT_FAILED_TO "Connessione fallita a" // xplg_wemohue.ino #define D_MULTICAST_DISABLED "Multicast disabilitato" #define D_MULTICAST_REJOINED "Multicast (ri)associato" -#define D_MULTICAST_JOIN_FAILED "Associazione Multicast fallita" +#define D_MULTICAST_JOIN_FAILED "Associazione multicast fallita" #define D_FAILED_TO_SEND_RESPONSE "Invio risposta fallito" #define D_WEMO "WeMo" -#define D_WEMO_BASIC_EVENT "WeMo evento base" -#define D_WEMO_EVENT_SERVICE "WeMo servizio eventi" -#define D_WEMO_META_SERVICE "WeMo meta service" +#define D_WEMO_BASIC_EVENT "Evento base WeMo" +#define D_WEMO_EVENT_SERVICE "Servizio eventi WeMo" +#define D_WEMO_META_SERVICE "Servizio meta WeMo" #define D_WEMO_SETUP "Impostazione WeMo" #define D_RESPONSE_SENT "Risposta inviata" #define D_HUE "Hue" #define D_HUE_BRIDGE_SETUP "Impostazione Hue" -#define D_HUE_API_NOT_IMPLEMENTED "Hue API non implementata" -#define D_HUE_API "Hue API" -#define D_HUE_POST_ARGS "Hue POST argomenti" +#define D_HUE_API_NOT_IMPLEMENTED "API Hue non implementata" +#define D_HUE_API "API Hue" +#define D_HUE_POST_ARGS "POST argomenti Hue" #define D_3_RESPONSE_PACKETS_SENT "3 pacchetti di risposta inviati" // xdrv_07_domoticz.ino #define D_DOMOTICZ_PARAMETERS "Parametri Domoticz" #define D_DOMOTICZ_IDX "Idx" -#define D_DOMOTICZ_KEY_IDX "Key idx" -#define D_DOMOTICZ_SWITCH_IDX "Switch idx" -#define D_DOMOTICZ_SENSOR_IDX "Sensor idx" +#define D_DOMOTICZ_KEY_IDX "Idx chiave" +#define D_DOMOTICZ_SWITCH_IDX "Idx switch" +#define D_DOMOTICZ_SENSOR_IDX "Idx sensore" #define D_DOMOTICZ_TEMP "Temp" - #define D_DOMOTICZ_TEMP_HUM "Temp,Hum" - #define D_DOMOTICZ_TEMP_HUM_BARO "Temp,Hum,Baro" - #define D_DOMOTICZ_POWER_ENERGY "Power,Energy" - #define D_DOMOTICZ_ILLUMINANCE "Illuminance" - #define D_DOMOTICZ_COUNT "Count/PM1" - #define D_DOMOTICZ_VOLTAGE "Voltage/PM2.5" - #define D_DOMOTICZ_CURRENT "Current/PM10" - #define D_DOMOTICZ_AIRQUALITY "AirQuality" + #define D_DOMOTICZ_TEMP_HUM "Temp,Umd" + #define D_DOMOTICZ_TEMP_HUM_BARO "Temp,Umd,Baro" + #define D_DOMOTICZ_POWER_ENERGY "Alim,Energia" + #define D_DOMOTICZ_ILLUMINANCE "Illuminazione" + #define D_DOMOTICZ_COUNT "Cont/PM1" + #define D_DOMOTICZ_VOLTAGE "Tensione/PM2.5" + #define D_DOMOTICZ_CURRENT "Corrente/PM10" + #define D_DOMOTICZ_AIRQUALITY "QualitàAria" #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" -#define D_DOMOTICZ_UPDATE_TIMER "Intervallo di aggiornamento" +#define D_DOMOTICZ_UPDATE_TIMER "Intervallo aggiornamento" // xdrv_09_timers.ino -#define D_CONFIGURE_TIMER "Configura Timer" -#define D_TIMER_PARAMETERS "Parametri Timer" -#define D_TIMER_ENABLE "Abilita Timer" +#define D_CONFIGURE_TIMER "Configura timer" +#define D_TIMER_PARAMETERS "Parametri timer" +#define D_TIMER_ENABLE "Abilita timer" #define D_TIMER_ARM "Attiva" #define D_TIMER_TIME "Ora" #define D_TIMER_DAYS "Giorni" @@ -425,57 +425,57 @@ #define D_CONFIGURE_KNX "Configura KNX" #define D_KNX_PARAMETERS "Parametri KNX" #define D_KNX_GENERAL_CONFIG "Generale" -#define D_KNX_PHYSICAL_ADDRESS "Indirizzo Fisico" -#define D_KNX_PHYSICAL_ADDRESS_NOTE "( Deve essere univoco nella rete KNX )" +#define D_KNX_PHYSICAL_ADDRESS "Indirizzo fisico" +#define D_KNX_PHYSICAL_ADDRESS_NOTE "(deve essere univoco nella rete KNX )" #define D_KNX_ENABLE "Abilita KNX" -#define D_KNX_GROUP_ADDRESS_TO_WRITE "Dati da Inviare al Gruppo di Indirizzi" +#define D_KNX_GROUP_ADDRESS_TO_WRITE "Dati da inviare al gruppo di indirizzi" #define D_ADD "Aggiungi" #define D_DELETE "Elimina" #define D_REPLY "Rispondi" -#define D_KNX_GROUP_ADDRESS_TO_READ "Gruppo di Indirizzi da cui Ricevere Dati" -#define D_RECEIVED_FROM "Ricevuto Da" +#define D_KNX_GROUP_ADDRESS_TO_READ "Gruppo di indirizzi da cui ricevere dati" +#define D_RECEIVED_FROM "Ricevuto da" #define D_KNX_COMMAND_WRITE "Scrivi" #define D_KNX_COMMAND_READ "Leggi" #define D_KNX_COMMAND_OTHER "Altro" #define D_SENT_TO "invia a" #define D_KNX_WARNING "L'indirizzo del gruppo ( 0 / 0 / 0 ) è riservato e non può essere usato." -#define D_KNX_ENHANCEMENT "Miglioramento Comunicazione" -#define D_KNX_TX_SLOT "KNX TX" -#define D_KNX_RX_SLOT "KNX RX" +#define D_KNX_ENHANCEMENT "Miglioramento comunicazione" +#define D_KNX_TX_SLOT "KNX - TX" +#define D_KNX_RX_SLOT "KNX - RX" // xdrv_03_energy.ino -#define D_ENERGY_TODAY "Energia Oggi" -#define D_ENERGY_YESTERDAY "Energia Ieri" -#define D_ENERGY_TOTAL "Energia Totale" +#define D_ENERGY_TODAY "Energia - oggi" +#define D_ENERGY_YESTERDAY "Energia - ieri" +#define D_ENERGY_TOTAL "Energia - totale" // xdrv_27_shutter.ino -#define D_OPEN "Aperta" -#define D_CLOSE "Chiusa" +#define D_OPEN "Apri" +#define D_CLOSE "Chiudi" #define D_DOMOTICZ_SHUTTER "Serranda" // xdrv_28_pcf8574.ino #define D_CONFIGURE_PCF8574 "Configura PCF8574" #define D_PCF8574_PARAMETERS "Parametri PCF8574" -#define D_INVERT_PORTS "Porte Invertite" +#define D_INVERT_PORTS "Inverti porte" #define D_DEVICE "Dispositivo" #define D_DEVICE_INPUT "Input" #define D_DEVICE_OUTPUT "Output" // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Sensore occupato" -#define D_SENSOR_CRC_ERROR "Sensore errore CRC" +#define D_SENSOR_CRC_ERROR "Errore CRC sensore" #define D_SENSORS_FOUND "Sensori trovati" // xsns_06_dht.ino -#define D_TIMEOUT_WAITING_FOR "Attesa timeout per" +#define D_TIMEOUT_WAITING_FOR "Timeout attesa per" #define D_START_SIGNAL_LOW "inizio segnale basso" #define D_START_SIGNAL_HIGH "inizio segnale alto" #define D_PULSE "impulso" #define D_CHECKSUM_FAILURE "Checksum fallito" // xsns_07_sht1x.ino -#define D_SENSOR_DID_NOT_ACK_COMMAND "Sensore non ha eseguito il comando ACK" -#define D_SHT1X_FOUND "SHT1X trovato" +#define D_SENSOR_DID_NOT_ACK_COMMAND "Il sensore non ha eseguito il comando ACK" +#define D_SHT1X_FOUND "Trovato SHT1X" // xsns_18_pms5003.ino #define D_STANDARD_CONCENTRATION "CF-1 PM" // Standard Particle CF-1 Particle Matter @@ -483,55 +483,55 @@ #define D_PARTICALS_BEYOND "Particelle" // xsns_32_mpu6050.ino -#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" +#define D_AX_AXIS "Accelerazione asse X" +#define D_AY_AXIS "Accelerazione asse Y" +#define D_AZ_AXIS "Accelerazione asse Z" +#define D_GX_AXIS "Giroscopio asse X" +#define D_GY_AXIS "Giroscopio asse Y" +#define D_GZ_AXIS "Giroscopio asse Z" // xsns_34_hx711.ino -#define D_HX_CAL_REMOVE "Rimuovere peso" -#define D_HX_CAL_REFERENCE "Caricare peso di riferimento" +#define D_HX_CAL_REMOVE "Rimuovi peso" +#define D_HX_CAL_REFERENCE "Carica riferimento peso" #define D_HX_CAL_DONE "Calibrato" #define D_HX_CAL_FAIL "Calibrazione fallita" -#define D_RESET_HX711 "Reset Scala" -#define D_CONFIGURE_HX711 "Configura Scala" -#define D_HX711_PARAMETERS "Parametri Scala" +#define D_RESET_HX711 "Ripristino scala" +#define D_CONFIGURE_HX711 "Configura scala" +#define D_HX711_PARAMETERS "Parametri scala" #define D_ITEM_WEIGHT "Peso oggetto" #define D_REFERENCE_WEIGHT "Peso di riferimento" #define D_CALIBRATE "Calibrato" #define D_CALIBRATION "Calibrazione" //xsns_35_tx20.ino -#define D_TX20_WIND_DIRECTION "Direzione Vento" -#define D_TX20_WIND_SPEED "Velocità Vento" -#define D_TX20_WIND_SPEED_MIN "Velocità Minima Vento" -#define D_TX20_WIND_SPEED_MAX "Velocità Massima Vento" +#define D_TX20_WIND_DIRECTION "Direzione vento" +#define D_TX20_WIND_SPEED "Velocità vento" +#define D_TX20_WIND_SPEED_MIN "Velocità minima vento" +#define D_TX20_WIND_SPEED_MAX "Velocità massima vento" #define D_TX20_NORTH "N" #define D_TX20_EAST "E" #define D_TX20_SOUTH "S" #define D_TX20_WEST "O" // xsns_53_sml.ino -#define D_TPWRIN "Energy Total-In" -#define D_TPWROUT "Energy Total-Out" -#define D_TPWRCURR "Active Power-In/Out" -#define D_TPWRCURR1 "Active Power-In p1" -#define D_TPWRCURR2 "Active Power-In p2" -#define D_TPWRCURR3 "Active Power-In p3" -#define D_Strom_L1 "Current L1" -#define D_Strom_L2 "Current L2" -#define D_Strom_L3 "Current L3" -#define D_Spannung_L1 "Voltage L1" -#define D_Spannung_L2 "Voltage L2" -#define D_Spannung_L3 "Voltage L3" +#define D_TPWRIN "Energia totale IN" +#define D_TPWROUT "Energia totale OUT" +#define D_TPWRCURR "Corrente IN/OUT" +#define D_TPWRCURR1 "Corrente IN p1" +#define D_TPWRCURR2 "Corrente IN p2" +#define D_TPWRCURR3 "Corrente IN p3" +#define D_Strom_L1 "Corrente L1" +#define D_Strom_L2 "Corrente L2" +#define D_Strom_L3 "Corrente L3" +#define D_Spannung_L1 "Tensione L1" +#define D_Spannung_L2 "Tensione L2" +#define D_Spannung_L3 "Tensione L3" #define D_METERNR "Meter_number" -#define D_METERSID "Service ID" -#define D_GasIN "Counter" -#define D_H2oIN "Counter" -#define D_StL1L2L3 "Current L1+L2+L3" -#define D_SpL1L2L3 "Voltage L1+L2+L3/3" +#define D_METERSID "ID servizio" +#define D_GasIN "Contatore" +#define D_H2oIN "Contatore" +#define D_StL1L2L3 "Corrente L1+L2+L3" +#define D_SpL1L2L3 "Tensione L1+L2+L3/3" // tasmota_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Nessuno" @@ -543,133 +543,133 @@ #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" #define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "Switch" // Suffix "1" -#define D_SENSOR_BUTTON "Button" // Suffix "1" -#define D_SENSOR_RELAY "Relay" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Counter" // Suffix "1" -#define D_SENSOR_IRRECV "IRrecv" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_SPI_MISO "SPI MISO" -#define D_SENSOR_SPI_MOSI "SPI MOSI" -#define D_SENSOR_SPI_CLK "SPI CLK" -#define D_SENSOR_BACKLIGHT "Backlight" +#define D_SENSOR_DFR562 "Riproduttore MP3" +#define D_SENSOR_IRSEND "IR - TX" +#define D_SENSOR_SWITCH "Switch" // Suffix "1" +#define D_SENSOR_BUTTON "Pulsante" // Suffix "1" +#define D_SENSOR_RELAY "Relè" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "Led - Lampeggio" // Suffix "i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Contatore" // Suffix "1" +#define D_SENSOR_IRRECV "IR - RX" +#define D_SENSOR_MHZ_RX "MHZ - RX" +#define D_SENSOR_MHZ_TX "MHZ - TX" +#define D_SENSOR_PZEM004_RX "PZEM004 - RX" +#define D_SENSOR_PZEM016_RX "PZEM016 - RX" +#define D_SENSOR_PZEM017_RX "PZEM017 - RX" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX - TX" +#define D_SENSOR_SAIR_RX "SAir - RX" +#define D_SENSOR_SAIR_TX "SAir - TX" +#define D_SENSOR_SPI_CS "SPI - CS" +#define D_SENSOR_SPI_DC "SPI - DC" +#define D_SENSOR_SPI_MISO "SPI - MISO" +#define D_SENSOR_SPI_MOSI "SPI - MOSI" +#define D_SENSOR_SPI_CLK "SPI - CLK" +#define D_SENSOR_BACKLIGHT "Retroilluminazione" #define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_HPMA_RX "HPMA Rx" -#define D_SENSOR_HPMA_TX "HPMA Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri/TX" -#define D_SENSOR_SR04_ECHO "SR04 Ech/RX" -#define D_SENSOR_SDM120_TX "SDMx20 Tx" -#define D_SENSOR_SDM120_RX "SDMx20 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_SDS0X1_RX "SDS0X1 - RX" +#define D_SENSOR_SDS0X1_TX "SDS0X1 - TX" +#define D_SENSOR_HPMA_RX "HPMA - RX" +#define D_SENSOR_HPMA_TX "HPMA - TX" +#define D_SENSOR_SBR_RX "SerBr - RX" +#define D_SENSOR_SBR_TX "SerBr - TX" +#define D_SENSOR_SR04_TRIG "SR04 Tri - TX" +#define D_SENSOR_SR04_ECHO "SR04 Ech - RX" +#define D_SENSOR_SDM120_TX "SDMx20 - TX" +#define D_SENSOR_SDM120_RX "SDMx20 - RX" +#define D_SENSOR_SDM630_TX "SDM630 - TX" +#define D_SENSOR_SDM630_RX "SDM630 - RX" +#define D_SENSOR_TM1638_CLK "TM16 - CLK" +#define D_SENSOR_TM1638_DIO "TM16 - DIO" +#define D_SENSOR_TM1638_STB "TM16 - STB" +#define D_SENSOR_HX711_SCK "HX711 - SCK" +#define D_SENSOR_HX711_DAT "HX711 - DAT" #define D_SENSOR_TX2X_TX "TX2x" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" -#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" -#define D_SENSOR_MAX31855_CS "MX31855 CS" -#define D_SENSOR_MAX31855_CLK "MX31855 CLK" -#define D_SENSOR_MAX31855_DO "MX31855 DO" -#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" -#define D_SENSOR_NRG_CF1 "HLWBL CF1" -#define D_SENSOR_HLW_CF "HLW8012 CF" -#define D_SENSOR_HJL_CF "BL0937 CF" -#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" -#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_RFSEND "RF - TX" +#define D_SENSOR_RFRECV "RF - RX" +#define D_SENSOR_TUYA_TX "Tuya - TX" +#define D_SENSOR_TUYA_RX "Tuya - RX" +#define D_SENSOR_MGC3130_XFER "MGC3130 - XFR" +#define D_SENSOR_MGC3130_RESET "MGC3130 - RST" +#define D_SENSOR_SSPI_MISO "SSPI - MISO" +#define D_SENSOR_SSPI_MOSI "SSPI - MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI - SCLK" +#define D_SENSOR_SSPI_CS "SSPI - CS" +#define D_SENSOR_SSPI_DC "SSPI - DC" +#define D_SENSOR_RF_SENSOR "Sensore RF" +#define D_SENSOR_AZ_RX "AZ - RX" +#define D_SENSOR_AZ_TX "AZ - TX" +#define D_SENSOR_MAX31855_CS "MX31855 - CS" +#define D_SENSOR_MAX31855_CLK "MX31855 - CLK" +#define D_SENSOR_MAX31855_DO "MX31855 - DO" +#define D_SENSOR_NRG_SEL "HLWBL - SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL - CF1" +#define D_SENSOR_HLW_CF "HLW8012 - CF" +#define D_SENSOR_HJL_CF "BL0937 - CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 - TX" +#define D_SENSOR_MCP39F5_RX "MCP39F5 - RX" #define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" -#define D_SENSOR_CSE7766_TX "CSE7766 Tx" -#define D_SENSOR_CSE7766_RX "CSE7766 Rx" -#define D_SENSOR_PN532_TX "PN532 Tx" -#define D_SENSOR_PN532_RX "PN532 Rx" -#define D_SENSOR_SM16716_CLK "SM16716 CLK" -#define D_SENSOR_SM16716_DAT "SM16716 DAT" -#define D_SENSOR_SM16716_POWER "SM16716 PWR" -#define D_SENSOR_MY92X1_DI "MY92x1 DI" -#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" -#define D_SENSOR_ARIRFRCV "ALux IrRcv" -#define D_SENSOR_ARIRFSEL "ALux IrSel" -#define D_SENSOR_TXD "Serial Tx" -#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_CSE7766_TX "CSE7766 - TX" +#define D_SENSOR_CSE7766_RX "CSE7766 - RX" +#define D_SENSOR_PN532_TX "PN532 - TX" +#define D_SENSOR_PN532_RX "PN532 - RX" +#define D_SENSOR_SM16716_CLK "SM16716 - CLK" +#define D_SENSOR_SM16716_DAT "SM16716 - DAT" +#define D_SENSOR_SM16716_POWER "SM16716 - PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 - DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 - DCKI" +#define D_SENSOR_ARIRFRCV "IR ALux - RCV" +#define D_SENSOR_ARIRFSEL "IR ALux - SEL" +#define D_SENSOR_TXD "Seriale - TX" +#define D_SENSOR_RXD "Seriale - RX" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" -#define D_SENSOR_HRE_CLOCK "HRE Clock" -#define D_SENSOR_HRE_DATA "HRE Data" -#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" -#define D_SENSOR_BUZZER "Buzzer" -#define D_SENSOR_OLED_RESET "OLED Reset" -#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" -#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" -#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" -#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" -#define D_SENSOR_IBEACON_TX "iBeacon TX" -#define D_SENSOR_IBEACON_RX "iBeacon RX" -#define D_SENSOR_RDM6300_RX "RDM6300 RX" -#define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" -#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" -#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" -#define D_SENSOR_DDSU666_TX "DDSU666 Tx" -#define D_SENSOR_DDSU666_RX "DDSU666 Rx" -#define D_SENSOR_SM2135_CLK "SM2135 Clk" -#define D_SENSOR_SM2135_DAT "SM2135 Dat" -#define D_SENSOR_DEEPSLEEP "DeepSleep" -#define D_SENSOR_EXS_ENABLE "EXS Enable" -#define D_SENSOR_SLAVE_TX "Slave TX" -#define D_SENSOR_SLAVE_RX "Slave RX" -#define D_SENSOR_SLAVE_RESET "Slave RST" -#define D_SENSOR_GPS_RX "GPS RX" -#define D_SENSOR_GPS_TX "GPS TX" -#define D_SENSOR_HM10_RX "HM10 RX" -#define D_SENSOR_HM10_TX "HM10 TX" -#define D_SENSOR_LE01MR_RX "LE-01MR Rx" -#define D_SENSOR_LE01MR_TX "LE-01MR Tx" -#define D_SENSOR_CC1101_GDO0 "CC1101 GDO0" -#define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" -#define D_SENSOR_HRXL_RX "HRXL Rx" -#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_HRE_CLOCK "HRE - Clock" +#define D_SENSOR_HRE_DATA "HRE - Dati" +#define D_SENSOR_ADE7953_IRQ "ADE7953 - IRQ" +#define D_SENSOR_BUZZER "Cicalino" +#define D_SENSOR_OLED_RESET "Ripristino OLED" +#define D_SENSOR_ZIGBEE_TXD "Zigbee - TX" +#define D_SENSOR_ZIGBEE_RXD "Zigbee - RX" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 - TX" +#define D_SENSOR_SOLAXX1_RX "SolaxX1- RX" +#define D_SENSOR_IBEACON_TX "iBeacon - TX" +#define D_SENSOR_IBEACON_RX "iBeacon - RX" +#define D_SENSOR_RDM6300_RX "RDM6300 - RX" +#define D_SENSOR_CC1101_CS "CC1101 - CS" +#define D_SENSOR_A4988_DIR "A4988 - DIR" +#define D_SENSOR_A4988_STP "A4988 - STP" +#define D_SENSOR_A4988_ENA "A4988 - ENA" +#define D_SENSOR_A4988_MS1 "A4988 - MS1" +#define D_SENSOR_A4988_MS2 "A4988 - MS2" +#define D_SENSOR_A4988_MS3 "A4988 - MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 - TX" +#define D_SENSOR_DDS2382_RX "DDS238-2 - RX" +#define D_SENSOR_DDSU666_TX "DDSU666 - TX" +#define D_SENSOR_DDSU666_RX "DDSU666 - RX" +#define D_SENSOR_SM2135_CLK "SM2135 - CLK" +#define D_SENSOR_SM2135_DAT "SM2135 - DATI" +#define D_SENSOR_DEEPSLEEP "Deep sleep" +#define D_SENSOR_EXS_ENABLE "EXS - Abilita" +#define D_SENSOR_SLAVE_TX "Slave - TX" +#define D_SENSOR_SLAVE_RX "Slave - RX" +#define D_SENSOR_SLAVE_RESET "Slave - RST" +#define D_SENSOR_GPS_RX "GPS - RX" +#define D_SENSOR_GPS_TX "GPS - TX" +#define D_SENSOR_HM10_RX "HM10 - RX" +#define D_SENSOR_HM10_TX "HM10 - TX" +#define D_SENSOR_LE01MR_RX "LE-01MR - RX" +#define D_SENSOR_LE01MR_TX "LE-01MR - TX" +#define D_SENSOR_CC1101_GDO0 "CC1101 - GDO0" +#define D_SENSOR_CC1101_GDO2 "CC1101 - GDO2" +#define D_SENSOR_HRXL_RX "HRXL - RX" +#define D_SENSOR_ELECTRIQ_MOODL "MOODL - TX" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" -#define D_UNIT_HOUR "h" +#define D_UNIT_HOUR "o" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" @@ -701,50 +701,50 @@ //SDM220, SDM120, LE01MR #define D_PHASE_ANGLE "Angolo Fase" -#define D_IMPORT_ACTIVE "Potenza Attiva Importata" -#define D_EXPORT_ACTIVE "Potenza Attiva Esportata" -#define D_IMPORT_REACTIVE "Potenza Reattiva Importata" -#define D_EXPORT_REACTIVE "Potenza Reattiva Esportata" -#define D_TOTAL_REACTIVE "Potenza Reattiva Totale" +#define D_IMPORT_ACTIVE "Potenza attiva importata" +#define D_EXPORT_ACTIVE "Potenza attiva esportata" +#define D_IMPORT_REACTIVE "Potenza reattiva importata" +#define D_EXPORT_REACTIVE "Potenza reattiva esportata" +#define D_TOTAL_REACTIVE "Potenza reattiva totale" #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "°" -#define D_TOTAL_ACTIVE "Total Active" +#define D_TOTAL_ACTIVE "Potenza attiva totale" //SOLAXX1 -#define D_PV1_VOLTAGE "PV1 Voltaggio" -#define D_PV1_CURRENT "PV1 Corrente" -#define D_PV1_POWER "PV1 Eergia" -#define D_PV2_VOLTAGE "PV2 Voltaggio" -#define D_PV2_CURRENT "PV2 Corrente" -#define D_PV2_POWER "PV2 Energia" -#define D_SOLAR_POWER "Energia Solar" -#define D_INVERTER_POWER "Energia Inverter" +#define D_PV1_VOLTAGE "PV1 - Voltaggio" +#define D_PV1_CURRENT "PV1 - Corrente" +#define D_PV1_POWER "PV1 - Energia" +#define D_PV2_VOLTAGE "PV2 - Voltaggio" +#define D_PV2_CURRENT "PV2 - Corrente" +#define D_PV2_POWER "PV2 - Energia" +#define D_SOLAR_POWER "Energia solare" +#define D_INVERTER_POWER "Energia inverter" #define D_STATUS "Stato" #define D_WAITING "In attesa" -#define D_CHECKING "Controllando" -#define D_WORKING "Lavorando" +#define D_CHECKING "Controllo" +#define D_WORKING "Attivo" #define D_FAILURE "Errore" -#define D_SOLAX_ERROR_0 "No Codice Errore" -#define D_SOLAX_ERROR_1 "Errore Grid Persa" -#define D_SOLAX_ERROR_2 "Errore Voltaggio Grid" -#define D_SOLAX_ERROR_3 "Errore Frequenza Grid" -#define D_SOLAX_ERROR_4 "Errore Voltaggio Pv" -#define D_SOLAX_ERROR_5 "Errore Isolamento" -#define D_SOLAX_ERROR_6 "Errore Temperatura Eccessiva" -#define D_SOLAX_ERROR_7 "Errore Ventilatore" -#define D_SOLAX_ERROR_8 "Altro Errore del Dispositivo" +#define D_SOLAX_ERROR_0 "Nessun codice errore" +#define D_SOLAX_ERROR_1 "Griglia errore persa" +#define D_SOLAX_ERROR_2 "Griglia errore tensione" +#define D_SOLAX_ERROR_3 "Griglia errore frequenza" +#define D_SOLAX_ERROR_4 "Errore tensione PV" +#define D_SOLAX_ERROR_5 "Errore isolamento" +#define D_SOLAX_ERROR_6 "Errore temperatura eccessiva" +#define D_SOLAX_ERROR_7 "Errore ventilatore" +#define D_SOLAX_ERROR_8 "Altro errore dispositivo" //xdrv_10_scripter.ino -#define D_CONFIGURE_SCRIPT "Edit script" -#define D_SCRIPT "edit script" -#define D_SDCARD_UPLOAD "file upload" -#define D_SDCARD_DIR "sd card directory" -#define D_UPL_DONE "Done" -#define D_SCRIPT_CHARS_LEFT "chars left" -#define D_SCRIPT_CHARS_NO_MORE "no more chars" +#define D_CONFIGURE_SCRIPT "Modifica script" +#define D_SCRIPT "modifica script" +#define D_SDCARD_UPLOAD "upload file" +#define D_SDCARD_DIR "cartella scheda SD" +#define D_UPL_DONE "Completato" +#define D_SCRIPT_CHARS_LEFT "caratteri rimanenti" +#define D_SCRIPT_CHARS_NO_MORE "nessun altro carattere" #define D_SCRIPT_DOWNLOAD "Download" -#define D_SCRIPT_ENABLE "script enable" +#define D_SCRIPT_ENABLE "abilita script" #define D_SCRIPT_UPLOAD "Upload" -#define D_SCRIPT_UPLOAD_FILES "Upload files" +#define D_SCRIPT_UPLOAD_FILES "Upload file" #endif // _LANGUAGE_IT_IT_H_ From 06774daba6e39739c2c8aed90fe8ef2e6c0bd3ba Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 30 Mar 2020 19:23:06 +0200 Subject: [PATCH 039/547] Add Zigbee commands ``ZbBindState`` and ``manuf``attribute --- tasmota/CHANGELOG.md | 1 + tasmota/i18n.h | 2 + tasmota/xdrv_23_zigbee_0_constants.ino | 4 +- tasmota/xdrv_23_zigbee_3_hue.ino | 10 +-- tasmota/xdrv_23_zigbee_6_commands.ino | 6 +- tasmota/xdrv_23_zigbee_7_statemachine.ino | 6 +- tasmota/xdrv_23_zigbee_8_parsers.ino | 70 +++++++++++++++++ tasmota/xdrv_23_zigbee_9_impl.ino | 95 ++++++++++++++++------- 8 files changed, 152 insertions(+), 42 deletions(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 0248d4ff7..affa7f008 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -3,6 +3,7 @@ ### 8.2.0.3 20200329 - Add support for longer template names +- Add Zigbee commands ``ZbBindState`` and ``manuf``attribute ### 8.2.0.2 20200328 diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 04433788f..7f225838e 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -507,6 +507,8 @@ #define D_JSON_ZIGBEE_BIND "ZbBind" #define D_CMND_ZIGBEE_UNBIND "Unbind" #define D_JSON_ZIGBEE_UNBIND "ZbUnbind" +#define D_CMND_ZIGBEE_BIND_STATE "BindState" + #define D_JSON_ZIGBEE_BIND_STATE "ZbBindState" #define D_CMND_ZIGBEE_PING "Ping" #define D_JSON_ZIGBEE_PING "ZbPing" #define D_JSON_ZIGBEE_IEEE "IEEEAddr" diff --git a/tasmota/xdrv_23_zigbee_0_constants.ino b/tasmota/xdrv_23_zigbee_0_constants.ino index 1a23c98dd..2aeade28d 100644 --- a/tasmota/xdrv_23_zigbee_0_constants.ino +++ b/tasmota/xdrv_23_zigbee_0_constants.ino @@ -399,14 +399,14 @@ String getZigbeeStatusMessage(uint8_t status) { "|UNSUP_MANUF_CLUSTER_COMMAND|UNSUP_MANUF_GENERAL_COMMAND|INVALID_FIELD|UNSUPPORTED_ATTRIBUTE|INVALID_VALE|READ_ONLY" "|INSUFFICIENT_SPACE|DUPLICATE_EXISTS|NOT_FOUND|UNREPORTABLE_ATTRIBUTE|INVALID_DATA_TYPE|INVALID_SELECTOR|WRITE_ONLY" "|INCONSISTENT_STARTUP_STATE|DEFINED_OUT_OF_BAND|INCONSISTENT|ACTION_DENIED|TIMEOUT|ABORT|INVALID_IMAGE|WAIT_FOR_DATA" - "|NO_IMAGE_AVAILABLE|REQUIRE_MORE_IMAGE|NOTIFICATION_PENDING|HARDWARE_FAILURE|SOFTWARE_FAILURE|CALIBRATION_ERROR|UNSUPPORTED_CLUSTER" + "|NO_IMAGE_AVAILABLE|REQUIRE_MORE_IMAGE|NOTIFICATION_PENDING|HARDWARE_FAILURE|SOFTWARE_FAILURE|CALIBRATION_ERROR|UNSUPPORTED_CLUSTER|NO_ROUTE" "|CHANNEL_ACCESS_FAILURE|NO_ACK|NO_APP_ACK|NO_ROUTE" ; static const uint8_t StatusIdx[] PROGMEM = { 0x00, 0x01, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, - 0x98, 0x99, 0x9A, 0xC0, 0xC1, 0xC2, 0xC3, + 0x98, 0x99, 0x9A, 0xC0, 0xC1, 0xC2, 0xC3, 0xCD, 0xE1, 0xE9, 0xA7, 0xD0}; char msg[32]; diff --git a/tasmota/xdrv_23_zigbee_3_hue.ino b/tasmota/xdrv_23_zigbee_3_hue.ino index ffbec109c..0d00ad9e0 100644 --- a/tasmota/xdrv_23_zigbee_3_hue.ino +++ b/tasmota/xdrv_23_zigbee_3_hue.ino @@ -130,7 +130,7 @@ void ZigbeeHueGroups(String * lights) { // Send commands // Power On/Off void ZigbeeHuePower(uint16_t shortaddr, bool power) { - zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0006, power ? 1 : 0, ""); + zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0006, power ? 1 : 0, ""); zigbee_devices.updateHueState(shortaddr, &power, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); } @@ -139,7 +139,7 @@ void ZigbeeHueDimmer(uint16_t shortaddr, uint8_t dimmer) { if (dimmer > 0xFE) { dimmer = 0xFE; } char param[8]; snprintf_P(param, sizeof(param), PSTR("%02X0A00"), dimmer); - zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0008, 0x04, param); + zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0008, 0x04, param); zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, &dimmer, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); } @@ -150,7 +150,7 @@ void ZigbeeHueCT(uint16_t shortaddr, uint16_t ct) { char param[12]; snprintf_P(param, sizeof(param), PSTR("%02X%02X0A00"), ct & 0xFF, ct >> 8); uint8_t colormode = 2; // "ct" - zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0300, 0x0A, param); + zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x0A, param); zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, nullptr, &ct, nullptr, nullptr, nullptr, nullptr); } @@ -161,7 +161,7 @@ void ZigbeeHueXY(uint16_t shortaddr, uint16_t x, uint16_t y) { if (y > 0xFEFF) { y = 0xFEFF; } snprintf_P(param, sizeof(param), PSTR("%02X%02X%02X%02X0A00"), x & 0xFF, x >> 8, y & 0xFF, y >> 8); uint8_t colormode = 1; // "xy" - zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0300, 0x07, param); + zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x07, param); zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, nullptr, nullptr, nullptr, &x, &y, nullptr); } @@ -172,7 +172,7 @@ void ZigbeeHueHS(uint16_t shortaddr, uint16_t hue, uint8_t sat) { if (sat > 0xFE) { sat = 0xFE; } snprintf_P(param, sizeof(param), PSTR("%02X%02X0000"), hue8, sat); uint8_t colormode = 0; // "hs" - zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0300, 0x06, param); + zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x06, param); zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, &sat, nullptr, &hue, nullptr, nullptr, nullptr); } diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino index 818f8afbe..1893925f3 100644 --- a/tasmota/xdrv_23_zigbee_6_commands.ino +++ b/tasmota/xdrv_23_zigbee_6_commands.ino @@ -169,7 +169,7 @@ int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t clus break; } if (attrs) { - ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(shortaddr)); + ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, 0, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(shortaddr)); } } @@ -306,10 +306,10 @@ void sendHueUpdate(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uin } if (z_cat >= 0) { uint8_t endpoint = 0; - if (!groupaddr) { + if (shortaddr) { endpoint = zigbee_devices.findFirstEndpoint(shortaddr); } - if ((endpoint) || (groupaddr)) { // send only if we know the endpoint + if ((!shortaddr) || (endpoint)) { // send if group address or endpoint is known zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, z_cat, 0 /* value */, &Z_ReadAttrCallback); if (shortaddr) { // reachability test is not possible for group addresses, since we don't know the list of devices in the group zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable); diff --git a/tasmota/xdrv_23_zigbee_7_statemachine.ino b/tasmota/xdrv_23_zigbee_7_statemachine.ino index 1c4f782b4..fc93e8a8d 100644 --- a/tasmota/xdrv_23_zigbee_7_statemachine.ino +++ b/tasmota/xdrv_23_zigbee_7_statemachine.ino @@ -56,7 +56,7 @@ typedef struct Zigbee_Instruction_Type { } Zigbee_Instruction_Type; enum Zigbee_StateMachine_Instruction_Set { - // 2 bytes instructions + // 4 bytes instructions ZGB_INSTR_4_BYTES = 0, ZGB_INSTR_NOOP = 0, // do nothing ZGB_INSTR_LABEL, // define a label @@ -67,7 +67,7 @@ enum Zigbee_StateMachine_Instruction_Set { ZGB_INSTR_WAIT_FOREVER, // wait forever but state machine still active ZGB_INSTR_STOP, // stop state machine with optional error code - // 6 bytes instructions + // 8 bytes instructions ZGB_INSTR_8_BYTES = 0x80, ZGB_INSTR_CALL = 0x80, // call a function ZGB_INSTR_LOG, // log a message, if more detailed logging required, call a function @@ -77,7 +77,7 @@ enum Zigbee_StateMachine_Instruction_Set { ZGB_INSTR_WAIT_RECV, // wait for a message according to the filter ZGB_ON_RECV_UNEXPECTED, // function to handle unexpected messages, or nullptr - // 10 bytes instructions + // 12 bytes instructions ZGB_INSTR_12_BYTES = 0xF0, ZGB_INSTR_WAIT_RECV_CALL, // wait for a filtered message and call function upon receive }; diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index cc95d4ed0..7de0d56f4 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -388,6 +388,7 @@ int32_t Z_BindRsp(int32_t res, const class SBuffer &buf) { return -1; } + // // Handle Unbind Rsp incoming message // @@ -413,6 +414,72 @@ int32_t Z_UnbindRsp(int32_t res, const class SBuffer &buf) { return -1; } +// +// Handle MgMt Bind Rsp incoming message +// +int32_t Z_MgmtBindRsp(int32_t res, const class SBuffer &buf) { + uint16_t shortaddr = buf.get16(2); + uint8_t status = buf.get8(4); + uint8_t bind_total = buf.get8(5); + uint8_t bind_start = buf.get8(6); + uint8_t bind_len = buf.get8(7); + + const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND_STATE "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""), shortaddr); + if (friendlyName) { + ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""), friendlyName); + } + ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_STATUS "\":%d" + ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" + ",\"BindingsTotal\":%d" + //",\"BindingsStart\":%d" + ",\"Bindings\":[" + ), status, getZigbeeStatusMessage(status).c_str(), bind_total); + + uint32_t idx = 8; + for (uint32_t i = 0; i < bind_len; i++) { + if (idx + 14 > buf.len()) { break; } // overflow, frame size is between 14 and 21 + + //uint64_t srcaddr = buf.get16(idx); // unused + uint8_t srcep = buf.get8(idx + 8); + uint8_t cluster = buf.get16(idx + 9); + uint8_t addrmode = buf.get8(idx + 11); + uint16_t group = 0x0000; + uint64_t dstaddr = 0; + uint8_t dstep = 0x00; + if (Z_Addr_Group == addrmode) { // Group address mode + group = buf.get16(idx + 12); + idx += 14; + } else if (Z_Addr_IEEEAddress == addrmode) { // IEEE address mode + dstaddr = buf.get64(idx + 12); + dstep = buf.get8(idx + 20); + idx += 21; + } else { + //AddLog_P2(LOG_LEVEL_INFO, PSTR("Z_MgmtBindRsp unknwon address mode %d"), addrmode); + break; // abort for any other value since we don't know the length of the field + } + + if (i > 0) { + ResponseAppend_P(PSTR(",")); + } + ResponseAppend_P(PSTR("{\"Cluster\":\"0x%04X\",\"Endpoint\":%d,"), cluster, srcep); + if (Z_Addr_Group == addrmode) { // Group address mode + ResponseAppend_P(PSTR("\"ToGroup\":%d}"), group); + } else if (Z_Addr_IEEEAddress == addrmode) { // IEEE address mode + char hex[20]; + Uint64toHex(dstaddr, hex, 64); + ResponseAppend_P(PSTR("\"ToDevice\":\"0x%s\",\"ToEndpoint\":%d}"), hex, dstep); + } + } + + ResponseAppend_P(PSTR("]}}")); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_BIND_STATE)); + XdrvRulesProcess(); + + return -1; +} /*********************************************************************************************\ * Send specific ZNP messages @@ -579,6 +646,7 @@ ZBM(AREQ_ZDO_SIMPLEDESCRSP, Z_AREQ | Z_ZDO, ZDO_SIMPLE_DESC_RSP) // 4 ZBM(AREQ_ZDO_IEEE_ADDR_RSP, Z_AREQ | Z_ZDO, ZDO_IEEE_ADDR_RSP) // 4581 ZBM(AREQ_ZDO_BIND_RSP, Z_AREQ | Z_ZDO, ZDO_BIND_RSP) // 45A1 ZBM(AREQ_ZDO_UNBIND_RSP, Z_AREQ | Z_ZDO, ZDO_UNBIND_RSP) // 45A2 +ZBM(AREQ_ZDO_MGMT_BIND_RSP, Z_AREQ | Z_ZDO, ZDO_MGMT_BIND_RSP) // 45B3 // Dispatcher callbacks table const Z_Dispatcher Z_DispatchTable[] PROGMEM = { @@ -592,6 +660,7 @@ const Z_Dispatcher Z_DispatchTable[] PROGMEM = { { AREQ_ZDO_IEEE_ADDR_RSP, &Z_ReceiveIEEEAddr }, { AREQ_ZDO_BIND_RSP, &Z_BindRsp }, { AREQ_ZDO_UNBIND_RSP, &Z_UnbindRsp }, + { AREQ_ZDO_MGMT_BIND_RSP, &Z_MgmtBindRsp }, }; /*********************************************************************************************\ @@ -643,6 +712,7 @@ void Z_Query_Bulb(uint16_t shortaddr, uint32_t &wait_ms) { zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0300, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback); wait_ms += inter_message_ms; zigbee_devices.setTimer(shortaddr, 0, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, 0, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable); + wait_ms += 1000; // wait 1 second between devices } } } diff --git a/tasmota/xdrv_23_zigbee_9_impl.ino b/tasmota/xdrv_23_zigbee_9_impl.ino index 423ac43f1..163580255 100644 --- a/tasmota/xdrv_23_zigbee_9_impl.ino +++ b/tasmota/xdrv_23_zigbee_9_impl.ino @@ -35,7 +35,7 @@ const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|" D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" D_CMND_ZIGBEE_BIND "|" D_CMND_ZIGBEE_UNBIND "|" D_CMND_ZIGBEE_PING "|" D_CMND_ZIGBEE_MODELID "|" - D_CMND_ZIGBEE_LIGHT "|" D_CMND_ZIGBEE_RESTORE + D_CMND_ZIGBEE_LIGHT "|" D_CMND_ZIGBEE_RESTORE "|" D_CMND_ZIGBEE_BIND_STATE ; void (* const ZigbeeCommand[])(void) PROGMEM = { @@ -44,7 +44,7 @@ void (* const ZigbeeCommand[])(void) PROGMEM = { &CmndZbProbe, &CmndZbRead, &CmndZbZNPReceive, &CmndZbForget, &CmndZbSave, &CmndZbName, &CmndZbBind, &CmndZbUnbind, &CmndZbPing, &CmndZbModelId, - &CmndZbLight, CmndZbRestore, + &CmndZbLight, &CmndZbRestore, &CmndZbBindState, }; // @@ -294,12 +294,12 @@ void ZigbeeZNPSend(const uint8_t *msg, size_t len) { // - transacId: 8-bits, transation id of message (should be incremented at each message), used both for Zigbee message number and ZCL message number // Returns: None // -void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId) { +void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, uint16_t manuf, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId) { SBuffer buf(32+len); buf.add8(Z_SREQ | Z_AF); // 24 buf.add8(AF_DATA_REQUEST_EXT); // 02 - if (groupaddr) { + if (0x0000 == shortaddr) { // if no shortaddr we assume group address buf.add8(Z_Addr_Group); // 01 buf.add64(groupaddr); // group address, only 2 LSB, upper 6 MSB are discarded buf.add8(0xFF); // dest endpoint is not used for group addresses @@ -315,8 +315,11 @@ void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterI buf.add8(0x30); // 30 options buf.add8(0x1E); // 1E radius - buf.add16(3 + len); - buf.add8((needResponse ? 0x00 : 0x10) | (clusterSpecific ? 0x01 : 0x00)); // Frame Control Field + buf.add16(3 + len + (manuf ? 2 : 0)); + buf.add8((needResponse ? 0x00 : 0x10) | (clusterSpecific ? 0x01 : 0x00) | (manuf ? 0x04 : 0x00)); // Frame Control Field + if (manuf) { + buf.add16(manuf); // add Manuf Id if not null + } buf.add8(transacId); // Transaction Sequance Number buf.add8(cmdId); if (len > 0) { @@ -342,7 +345,7 @@ void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterI // - param: pointer to HEX string for payload, should not be nullptr // Returns: None // -void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint, bool clusterSpecific, +void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint, bool clusterSpecific, uint16_t manuf, uint16_t cluster, uint8_t cmd, const char *param) { size_t size = param ? strlen(param) : 0; SBuffer buf((size+2)/2); // actual bytes buffer for data @@ -368,7 +371,7 @@ void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint, } // everything is good, we can send the command - ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, cmd, clusterSpecific, buf.getBuffer(), buf.len(), true, zigbee_devices.getNextSeqNumber(shortaddr)); + ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, cmd, clusterSpecific, manuf, buf.getBuffer(), buf.len(), true, zigbee_devices.getNextSeqNumber(shortaddr)); // now set the timer, if any, to read back the state later if (clusterSpecific) { zigbeeSetCommandTimer(shortaddr, groupaddr, cluster, endpoint); @@ -397,9 +400,10 @@ void CmndZbSend(void) { // params static char delim[] = ", "; // delimiters for parameters - uint16_t device = 0x0000; // 0xFFFF is broadcast, so considered valid - uint16_t groupaddr = 0x0000; // ignore group address if 0x0000 + uint16_t device = 0x0000; // 0x0000 is local, so considered invalid + uint16_t groupaddr = 0x0000; // group address uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint + uint16_t manuf = 0x0000; // Manuf Id in ZCL frame // Command elements uint16_t cluster = 0; uint8_t cmd = 0; @@ -408,19 +412,25 @@ void CmndZbSend(void) { bool clusterSpecific = true; // parse JSON - const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group")); - if (nullptr != &val_group) { groupaddr = strToUInt(val_group); } - if (0x0000 == groupaddr) { // if no group address, we need a device address - const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); - if (nullptr != &val_device) { - device = zigbee_devices.parseDeviceParam(val_device.as()); - if (0xFFFF == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); + if (nullptr != &val_device) { + device = zigbee_devices.parseDeviceParam(val_device.as()); + if (0xFFFF == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + } + if (0x0000 == device) { // if not found, check if we have a group + const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group")); + if (nullptr != &val_group) { + groupaddr = strToUInt(val_group); + } else { // no device nor group + ResponseCmndChar_P(PSTR("Unknown device")); + return; } - if ((nullptr == &val_device) || (0x0000 == device)) { ResponseCmndChar_P(PSTR("Unknown device")); return; } } const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint")); if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); } + const JsonVariant &val_manuf = getCaseInsensitive(json, PSTR("Manuf")); + if (nullptr != &val_manuf) { manuf = strToUInt(val_manuf); } const JsonVariant &val_cmd = getCaseInsensitive(json, PSTR("Send")); if (nullptr != &val_cmd) { // probe the type of the argument @@ -523,7 +533,7 @@ void CmndZbSend(void) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeZCLSend device: 0x%04X, group: 0x%04X, endpoint:%d, cluster:0x%04X, cmd:0x%02X, send:\"%s\""), device, groupaddr, endpoint, cluster, cmd, cmd_s); - zigbeeZCLSendStr(device, groupaddr, endpoint, clusterSpecific, cluster, cmd, cmd_s); + zigbeeZCLSendStr(device, groupaddr, endpoint, clusterSpecific, manuf, cluster, cmd, cmd_s); ResponseCmndDone(); } else { Response_P(PSTR("Missing zigbee 'Send'")); @@ -639,6 +649,26 @@ void CmndZbUnbind(void) { ZbBindUnbind(true); } +// +// Command `ZbBindState` +// +void CmndZbBindState(void) { + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); + if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + + SBuffer buf(10); + buf.add8(Z_SREQ | Z_ZDO); // 25 + buf.add8(ZDO_MGMT_BIND_REQ); // 33 + buf.add16(shortaddr); // shortaddr + buf.add8(0); // StartIndex = 0 + + ZigbeeZNPSend(buf.getBuffer(), buf.len()); + + ResponseCmndDone(); +} + // Probe a specific device to get its endpoints and supported clusters void CmndZbProbe(void) { CmndZbProbeOrPing(true); @@ -865,24 +895,31 @@ void CmndZbRead(void) { uint16_t groupaddr = 0x0000; // if 0x0000 ignore group adress uint16_t cluster = 0x0000; // default to general cluster uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint + uint16_t manuf = 0x0000; // Manuf Id in ZCL frame size_t attrs_len = 0; uint8_t* attrs = nullptr; // empty string is valid - const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group")); - if (nullptr != &val_group) { groupaddr = strToUInt(val_group); } - if (0x0000 == groupaddr) { // if no group address, we need a device address - const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); - if (nullptr != &val_device) { - device = zigbee_devices.parseDeviceParam(val_device.as()); - if (0xFFFF == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); + if (nullptr != &val_device) { + device = zigbee_devices.parseDeviceParam(val_device.as()); + if (0xFFFF == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + } + if (0x0000 == device) { // if not found, check if we have a group + const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group")); + if (nullptr != &val_group) { + groupaddr = strToUInt(val_group); + } else { // no device nor group + ResponseCmndChar_P(PSTR("Unknown device")); + return; } - if ((nullptr == &val_device) || (0x0000 == device)) { ResponseCmndChar_P(PSTR("Unknown device")); return; } } const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster")); if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); } const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint")); if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); } + const JsonVariant &val_manuf = getCaseInsensitive(json, PSTR("Manuf")); + if (nullptr != &val_manuf) { manuf = strToUInt(val_manuf); } const JsonVariant &val_attr = getCaseInsensitive(json, PSTR("Read")); if (nullptr != &val_attr) { @@ -910,12 +947,12 @@ void CmndZbRead(void) { endpoint = zigbee_devices.findFirstEndpoint(device); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint); } - if (groupaddr) { + if (0x0000 == device) { endpoint = 0xFF; // endpoint not used for group addresses } if ((0 != endpoint) && (attrs_len > 0)) { - ZigbeeZCLSend_Raw(device, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(device)); + ZigbeeZCLSend_Raw(device, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, manuf, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(device)); ResponseCmndDone(); } else { ResponseCmndChar_P(PSTR("Missing parameters")); From 86667651080199f6d786d488459f8cdc147a62a5 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 30 Mar 2020 21:38:48 +0200 Subject: [PATCH 040/547] Zigbee change boolean attributes to int BREAKING CHANGE, "Power" attribute will be reported as `0`/`1` insteas of `false`/`true` --- tasmota/xdrv_23_zigbee_5_converters.ino | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index edd5ab682..7a8ea4806 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -202,13 +202,6 @@ uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer case 0xFF: // unk break; case 0x10: // bool - { - uint8_t val_bool = buf.get8(i++); - if (0xFF != val_bool) { - json[attrid_str] = (bool) (val_bool ? true : false); - } - } - break; case 0x20: // uint8 case 0x30: // enum8 { From eccd5cdd19270a7a652b51c2fe25b56cc6b99216 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 31 Mar 2020 13:01:50 +0200 Subject: [PATCH 041/547] Refactor web command history --- tasmota/xdrv_01_webserver.ino | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index ea874b7f2..1bfc841c2 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -211,18 +211,21 @@ const char HTTP_SCRIPT_CONSOL[] PROGMEM = "lt=setTimeout(l,%d);" "return false;" "}" + "wl(l);" // Load initial console text - // Console command history - part 2 + // Console command history "var hc=[],cn=0;" // hc = History commands, cn = Number of history being shown - "function cH(a){" - "var b=qs('#c1'),c=a.keyCode;" // c1 = Console command id - "if(38==c||40==c){b.autocomplete='off';}" // Arrow up or down must be a keyboard so stop browser autocomplete - "38==c?(++cn>hc.length&&(cn=hc.length),b.value=hc[cn-1]||''):" // Arrow Up - "40==c?(0>--cn&&(cn=0),b.value=hc[cn-1]||''):" // Arrow Down - "13==c&&(hc.length>19&&hc.pop(),hc.unshift(b.value),cn=0)" // Enter, 19 = Max number -1 of commands in history + "function h(){" +// "if(!(navigator.maxTouchPoints||'ontouchstart'in document.documentElement)){eb('c1').autocomplete='off';}" // No touch so stop browser autocomplete + "eb('c1').addEventListener('keydown',function(e){" + "var b=eb('c1'),c=e.keyCode;" // c1 = Console command id + "if(38==c||40==c){b.autocomplete='off';}" // ArrowUp or ArrowDown must be a keyboard so stop browser autocomplete + "38==c?(++cn>hc.length&&(cn=hc.length),b.value=hc[cn-1]||''):" // ArrowUp + "40==c?(0>--cn&&(cn=0),b.value=hc[cn-1]||''):" // ArrowDown + "13==c&&(hc.length>19&&hc.pop(),hc.unshift(b.value),cn=0)" // Enter, 19 = Max number -1 of commands in history + "});" "}" - - "wl(l);"; + "wl(h);"; // Add console command key eventlistener after name has been synced with id (= wl(jd)) const char HTTP_MODULE_TEMPLATE_REPLACE[] PROGMEM = "}2%d'>%s (%d}3"; // }2 and }3 are used in below os.replace @@ -340,12 +343,6 @@ const char HTTP_HEAD_LAST_SCRIPT[] PROGMEM = "}" "t++;" "}" - - // Console command history - part 1 - "if(qs('#c1')){" // c1 = Console command id - "qs('#c1').addEventListener('keydown',cH);" - "}" - "}" "wl(jd);" // Add name='' to any id='' in input,button,textarea,select ""; From 7ebb5f3cb9c8eef0eec1033cf3d72006006f4b02 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 31 Mar 2020 16:30:30 +0200 Subject: [PATCH 042/547] Add commands ``CounterDebounceLow`` and ``CounterDebounceHigh`` Add commands ``CounterDebounceLow`` and ``CounterDebounceHigh`` to control debouncing (#8021) --- RELEASENOTES.md | 2 ++ tasmota/CHANGELOG.md | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c4c1c4c61..6d4c9c1c7 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -59,6 +59,8 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Fix Zigbee sending wrong Sat value with Hue emulation - Add Zigbee command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` - Add Zigbee command ``ZbUnbind`` +- Add Zigbee command ``ZbBindState`` and ``manuf``attribute +- Add commands ``CounterDebounceLow`` and ``CounterDebounceHigh`` to control debouncing (#8021) - Add support for unreachable (unplugged) Zigbee devices in Philips Hue emulation and Alexa - Add support for 64x48 SSD1306 OLED (#6740) - Add support for up to four MQTT GroupTopics using the same optional Device Group names (#8014) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index affa7f008..59985d6fc 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -3,7 +3,8 @@ ### 8.2.0.3 20200329 - Add support for longer template names -- Add Zigbee commands ``ZbBindState`` and ``manuf``attribute +- Add Zigbee command ``ZbBindState`` and ``manuf``attribute +- Add commands ``CounterDebounceLow`` and ``CounterDebounceHigh`` to control debouncing (#8021) ### 8.2.0.2 20200328 @@ -15,7 +16,7 @@ - Change HM-10 sensor type detection and add features (#7962) - Fix possible Relay toggle on (OTA) restart - Fix Zigbee sending wrong Sat value with Hue emulation -- Add ZIgbee command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` +- Add Zigbee command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` - Add Zigbee command ``ZbUnbind`` - Add support for unreachable (unplugged) Zigbee devices in Philips Hue emulation and Alexa - Add support for 64x48 SSD1306 OLED (#6740) From 39d8011c89807e4490342c7de13af3d524c937bc Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 31 Mar 2020 17:27:33 +0200 Subject: [PATCH 043/547] Change light scheme 2,3,4 cycle time Change light scheme 2,3,4 cycle time speed from 24,48,72,... seconds to 4,6,12,24,36,48,... seconds (#8034) --- RELEASENOTES.md | 1 + tasmota/CHANGELOG.md | 1 + tasmota/xdrv_04_light.ino | 9 +++++++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 6d4c9c1c7..23bd31fef 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -55,6 +55,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c ### Version 8.2.0.3 - Change HM-10 sensor type detection and add features (#7962) +- Change light scheme 2,3,4 cycle time speed from 24,48,72,... seconds to 4,6,12,24,36,48,... seconds (#8034) - Fix possible Relay toggle on (OTA) restart - Fix Zigbee sending wrong Sat value with Hue emulation - Add Zigbee command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 59985d6fc..bfd407b22 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -2,6 +2,7 @@ ### 8.2.0.3 20200329 +- Change light scheme 2,3,4 cycle time speed from 24,48,72,... seconds to 4,6,12,24,36,48,... seconds (#8034) - Add support for longer template names - Add Zigbee command ``ZbBindState`` and ``manuf``attribute - Add commands ``CounterDebounceLow`` and ``CounterDebounceHigh`` to control debouncing (#8021) diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index 6b72da9b2..174ef74a9 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -1612,8 +1612,9 @@ void LightPreparePower(power_t channels = 0xFFFFFFFF) { // 1 = only RGB, 2 = void LightCycleColor(int8_t direction) { - if (Light.strip_timer_counter % (Settings.light_speed * 2)) { - return; + if (Settings.light_speed > 3) { +// if (Light.strip_timer_counter % (Settings.light_speed * 2)) { return; } + if (Light.strip_timer_counter % (Settings.light_speed - 2)) { return; } // Speed 4: 24sec, 5: 36sec, 6: 48sec, etc } if (0 == direction) { @@ -1630,7 +1631,11 @@ void LightCycleColor(int8_t direction) // direction = (Light.random < Light.wheel) ? -1 : 1; direction = (Light.random &0x01) ? 1 : -1; } + + if (Settings.light_speed < 3) { direction *= (4 - Settings.light_speed); } // Speed 1: 12/3=4sec, 2: 12/2=6sec, 3: 12sec +// if (Settings.light_speed < 3) { direction <<= (3 - Settings.light_speed); } // Speed 1: 12/4=3sec, 2: 12/2=6sec, 3: 12sec Light.wheel += direction; + uint16_t hue = changeUIntScale(Light.wheel, 0, 255, 0, 359); // Scale to hue to keep amount of steps low (max 255 instead of 359) // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("LGT: random %d, wheel %d, hue %d"), Light.random, Light.wheel, hue); From e319f4ec40aa06907b4e280fd8f292cff821de1a Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 31 Mar 2020 17:36:23 +0200 Subject: [PATCH 044/547] Refactor light --- tasmota/xdrv_04_light.ino | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index 174ef74a9..a3f7a6be5 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -1612,8 +1612,8 @@ void LightPreparePower(power_t channels = 0xFFFFFFFF) { // 1 = only RGB, 2 = void LightCycleColor(int8_t direction) { +// if (Light.strip_timer_counter % (Settings.light_speed * 2)) { return; } // Speed 1: 24sec, 2: 48sec, 3: 72sec, etc if (Settings.light_speed > 3) { -// if (Light.strip_timer_counter % (Settings.light_speed * 2)) { return; } if (Light.strip_timer_counter % (Settings.light_speed - 2)) { return; } // Speed 4: 24sec, 5: 36sec, 6: 48sec, etc } @@ -1632,10 +1632,9 @@ void LightCycleColor(int8_t direction) direction = (Light.random &0x01) ? 1 : -1; } - if (Settings.light_speed < 3) { direction *= (4 - Settings.light_speed); } // Speed 1: 12/3=4sec, 2: 12/2=6sec, 3: 12sec // if (Settings.light_speed < 3) { direction <<= (3 - Settings.light_speed); } // Speed 1: 12/4=3sec, 2: 12/2=6sec, 3: 12sec + if (Settings.light_speed < 3) { direction *= (4 - Settings.light_speed); } // Speed 1: 12/3=4sec, 2: 12/2=6sec, 3: 12sec Light.wheel += direction; - uint16_t hue = changeUIntScale(Light.wheel, 0, 255, 0, 359); // Scale to hue to keep amount of steps low (max 255 instead of 359) // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("LGT: random %d, wheel %d, hue %d"), Light.random, Light.wheel, hue); From a515a10c4250c6fdab42f5263af0b02423fb3a13 Mon Sep 17 00:00:00 2001 From: Markus Peter Date: Tue, 31 Mar 2020 18:04:32 +0200 Subject: [PATCH 045/547] Use unicode decimal code for shutter buttons Use unicode decimal code for up and down triangles in shutter button labels instead of unicode character --- tasmota/xdrv_01_webserver.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 1bfc841c2..3521c0adb 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -1178,7 +1178,7 @@ void HandleRoot(void) int32_t ShutterWebButton; if (ShutterWebButton = IsShutterWebButton(idx)) { WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, - (set_button) ? SettingsText(SET_BUTTON1 + idx -1) : ((Settings.shutter_options[abs(ShutterWebButton)-1] & 2) /* is locked */ ? "-" : ((Settings.shutter_options[abs(ShutterWebButton)-1] & 8) /* invert web buttons */ ? ((ShutterWebButton>0) ? "â–¼" : "â–²") : ((ShutterWebButton>0) ? "â–²" : "â–¼"))), + (set_button) ? SettingsText(SET_BUTTON1 + idx -1) : ((Settings.shutter_options[abs(ShutterWebButton)-1] & 2) /* is locked */ ? "-" : ((Settings.shutter_options[abs(ShutterWebButton)-1] & 8) /* invert web buttons */ ? ((ShutterWebButton>0) ? "▼" : "▲") : ((ShutterWebButton>0) ? "▲" : "▼"))), ""); continue; } From f46923ba1e53d5da4e12d2ddadccb0969a34c2f3 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Tue, 31 Mar 2020 23:04:17 +0200 Subject: [PATCH 046/547] Change remove floating point libs from IRAM --- platformio.ini | 3 +- tasmota/CHANGELOG.md | 1 + tasmota/ld/eagle.flash.1m.ld_FP_IN_IROM | 336 ++++++++++++++++++++++ tasmota/ld/eagle.rom.addr.v6.ld | 352 ++++++++++++++++++++++++ 4 files changed, 691 insertions(+), 1 deletion(-) create mode 100644 tasmota/ld/eagle.flash.1m.ld_FP_IN_IROM create mode 100644 tasmota/ld/eagle.rom.addr.v6.ld diff --git a/platformio.ini b/platformio.ini index 2c78a4583..6b0bb6edc 100755 --- a/platformio.ini +++ b/platformio.ini @@ -57,7 +57,8 @@ default_envs = framework = arduino board = esp01_1m board_build.flash_mode = dout -board_build.ldscript = eagle.flash.1m.ld +; board_build.ldscript = eagle.flash.1m.ld +board_build.ldscript = ${PROJECTSRC_DIR}/ld/eagle.flash.1m.ld_FP_IN_IROM platform = ${core_active.platform} platform_packages = ${core_active.platform_packages} diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index bfd407b22..cf6941a6d 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -6,6 +6,7 @@ - Add support for longer template names - Add Zigbee command ``ZbBindState`` and ``manuf``attribute - Add commands ``CounterDebounceLow`` and ``CounterDebounceHigh`` to control debouncing (#8021) +- Change remove floating point libs from IRAM ### 8.2.0.2 20200328 diff --git a/tasmota/ld/eagle.flash.1m.ld_FP_IN_IROM b/tasmota/ld/eagle.flash.1m.ld_FP_IN_IROM new file mode 100644 index 000000000..3e338cebd --- /dev/null +++ b/tasmota/ld/eagle.flash.1m.ld_FP_IN_IROM @@ -0,0 +1,336 @@ +/* Flash Split for 1M chips */ +/* sketch @0x40200000 (~999KB) (1023984B) */ +/* empty @0x402F9FF0 (~4KB) (4112B) */ +/* spiffs @0x402FB000 (~0KB) (0B) */ +/* eeprom @0x402FB000 (4KB) */ +/* rfcal @0x402FC000 (4KB) */ +/* wifi @0x402FD000 (12KB) */ + +MEMORY +{ + dport0_0_seg : org = 0x3FF00000, len = 0x10 + dram0_0_seg : org = 0x3FFE8000, len = 0x14000 + iram1_0_seg : org = 0x40100000, len = 0x8000 + irom0_0_seg : org = 0x40201010, len = 0xf9ff0 +} + +PROVIDE ( _FS_start = 0x402FB000 ); +PROVIDE ( _FS_end = 0x402FB000 ); +PROVIDE ( _FS_page = 0x0 ); +PROVIDE ( _FS_block = 0x0 ); +PROVIDE ( _EEPROM_start = 0x402fb000 ); +/* The following symbols are DEPRECATED and will be REMOVED in a future release */ +PROVIDE ( _SPIFFS_start = 0x402FB000 ); +PROVIDE ( _SPIFFS_end = 0x402FB000 ); +PROVIDE ( _SPIFFS_page = 0x0 ); +PROVIDE ( _SPIFFS_block = 0x0 ); + +/* This linker script generated from xt-genldscripts.tpp for LSP . */ +/* Linker Script for ld -N */ + +PHDRS +{ + dport0_0_phdr PT_LOAD; + dram0_0_phdr PT_LOAD; + dram0_0_bss_phdr PT_LOAD; + iram1_0_phdr PT_LOAD; + irom0_0_phdr PT_LOAD; +} + + +/* Default entry point: */ +ENTRY(app_entry) +EXTERN(_DebugExceptionVector) +EXTERN(_DoubleExceptionVector) +EXTERN(_KernelExceptionVector) +EXTERN(_NMIExceptionVector) +EXTERN(_UserExceptionVector) +EXTERN(core_version) +PROVIDE(_memmap_vecbase_reset = 0x40000000); +/* Various memory-map dependent cache attribute settings: */ +_memmap_cacheattr_wb_base = 0x00000110; +_memmap_cacheattr_wt_base = 0x00000110; +_memmap_cacheattr_bp_base = 0x00000220; +_memmap_cacheattr_unused_mask = 0xFFFFF00F; +_memmap_cacheattr_wb_trapnull = 0x2222211F; +_memmap_cacheattr_wba_trapnull = 0x2222211F; +_memmap_cacheattr_wbna_trapnull = 0x2222211F; +_memmap_cacheattr_wt_trapnull = 0x2222211F; +_memmap_cacheattr_bp_trapnull = 0x2222222F; +_memmap_cacheattr_wb_strict = 0xFFFFF11F; +_memmap_cacheattr_wt_strict = 0xFFFFF11F; +_memmap_cacheattr_bp_strict = 0xFFFFF22F; +_memmap_cacheattr_wb_allvalid = 0x22222112; +_memmap_cacheattr_wt_allvalid = 0x22222112; +_memmap_cacheattr_bp_allvalid = 0x22222222; +PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull); + +SECTIONS +{ + + .dport0.rodata : ALIGN(4) + { + _dport0_rodata_start = ABSOLUTE(.); + *(.dport0.rodata) + *(.dport.rodata) + _dport0_rodata_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.literal : ALIGN(4) + { + _dport0_literal_start = ABSOLUTE(.); + *(.dport0.literal) + *(.dport.literal) + _dport0_literal_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .dport0.data : ALIGN(4) + { + _dport0_data_start = ABSOLUTE(.); + *(.dport0.data) + *(.dport.data) + _dport0_data_end = ABSOLUTE(.); + } >dport0_0_seg :dport0_0_phdr + + .data : ALIGN(4) + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + . = ALIGN(4); + _Pri_3_HandlerAddress = ABSOLUTE(.); + _data_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .noinit : ALIGN(4) + { + *(.noinit) + } >dram0_0_seg :dram0_0_phdr + + /* IRAM is split into .text and .text1 to allow for moving specific */ + /* functions into IRAM that would be matched by the irom0.text matcher */ + .text : ALIGN(4) + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.UserEnter.text) + . = ALIGN(16); + *(.DebugExceptionVector.text) + . = ALIGN(16); + *(.NMIExceptionVector.text) + . = ALIGN(16); + *(.KernelExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.UserExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN(16); + *(.DoubleExceptionVector.text) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + + *(.text.app_entry*) /* The main startup code */ + + *(.text.gdbstub*, .text.gdb_init) /* Any GDB hooks */ + + /* all functional callers are placed in IRAM (including SPI/IRQ callbacks/etc) here */ + *(.text._ZNKSt8functionIF*EE*) /* std::function::operator()() const */ + } >iram1_0_seg :iram1_0_phdr + + .irom0.text : ALIGN(4) + { + _irom0_text_start = ABSOLUTE(.); + *(.ver_number) + *.c.o(.literal*, .text*) + *.cpp.o(EXCLUDE_FILE (umm_malloc.cpp.o) .literal*, EXCLUDE_FILE (umm_malloc.cpp.o) .text*) + *.cc.o(.literal*, .text*) +/* #ifdef VTABLES_IN_FLASH */ + *(.rodata._ZTV*) /* C++ vtables */ +/* #endif */ + + *libgcc.a:unwind-dw2.o(.literal .text .rodata .literal.* .text.* .rodata.*) + *libgcc.a:unwind-dw2-fde.o(.literal .text .rodata .literal.* .text.* .rodata.*) + + *libc.a:(.literal .text .literal.* .text.*) + *libm.a:(.literal .text .literal.* .text.*) +/* #ifdef FP_IN_IROM */ + *libgcc.a:*f2.o(.literal .text) + *libgcc.a:*f3.o(.literal .text) + *libgcc.a:*fsi.o(.literal .text) + *libgcc.a:*fdi.o(.literal .text) + *libgcc.a:*ifs.o(.literal .text) + *libgcc.a:*idf.o(.literal .text) +/* #endif */ + *libgcc.a:_umoddi3.o(.literal .text) + *libgcc.a:_udivdi3.o(.literal .text) + *libstdc++.a:( .literal .text .literal.* .text.*) + *libstdc++-exc.a:( .literal .text .literal.* .text.*) + *libsmartconfig.a:(.literal .text .literal.* .text.*) + *liblwip_gcc.a:(.literal .text .literal.* .text.*) + *liblwip_src.a:(.literal .text .literal.* .text.*) + *liblwip2-536.a:(.literal .text .literal.* .text.*) + *liblwip2-1460.a:(.literal .text .literal.* .text.*) + *liblwip2-536-feat.a:(.literal .text .literal.* .text.*) + *liblwip2-1460-feat.a:(.literal .text .literal.* .text.*) + *liblwip6-536-feat.a:(.literal .text .literal.* .text.*) + *liblwip6-1460-feat.a:(.literal .text .literal.* .text.*) + *libbearssl.a:(.literal .text .literal.* .text.*) + *libaxtls.a:(.literal .text .literal.* .text.*) + *libat.a:(.literal.* .text.*) + *libcrypto.a:(.literal.* .text.*) + *libespnow.a:(.literal.* .text.*) + *libjson.a:(.literal.* .text.*) + *liblwip.a:(.literal.* .text.*) + *libmesh.a:(.literal.* .text.*) + *libnet80211.a:(.literal.* .text.*) + *libsmartconfig.a:(.literal.* .text.*) + *libssl.a:(.literal.* .text.*) + *libupgrade.a:(.literal.* .text.*) + *libwpa.a:(.literal.* .text.*) + *libwpa2.a:(.literal.* .text.*) + *libwps.a:(.literal.* .text.*) + *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom0.text.* .irom.text .irom.text.*) + + /* Constant strings in flash (PSTRs) */ + *(.irom0.pstr.*) + + /* __FUNCTION__ locals */ + *(.rodata._ZZ*__FUNCTION__) + *(.rodata._ZZ*__PRETTY_FUNCTION__) + *(.rodata._ZZ*__func__) + + /* std::* exception strings, in their own section to allow string coalescing */ + *(.irom.exceptiontext) + + /* c++ typeof IDs, etc. */ + *(.rodata._ZTIN* .rodata._ZTSN10* .rodata._ZTISt* .rodata._ZTSSt*) + + /* Fundamental type info */ + *(.rodata._ZTIPKc .rodata._ZTIc .rodata._ZTIv .rodata._ZTSv .rodata._ZTSc .rodata._ZTSPKc .rodata._ZTSi .rodata._ZTIi) + + . = ALIGN(4); + *(.gcc_except_table .gcc_except_table.*) + . = ALIGN(4); + __eh_frame = ABSOLUTE(.); + KEEP(*(.eh_frame)) + . = (. + 7) & ~ 3; /* Add a 0 entry to terminate the list */ + + _irom0_text_end = ABSOLUTE(.); + _flash_code_end = ABSOLUTE(.); + } >irom0_0_seg :irom0_0_phdr + + + + .text1 : ALIGN(4) + { + *(.literal .text .iram.literal .iram.text .iram.text.* .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } >iram1_0_seg :iram1_0_phdr + + .rodata : ALIGN(4) + { + _rodata_start = ABSOLUTE(.); + *(.sdk.version) + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + . = (. + 3) & ~ 3; + /* C++ constructor and destructor tables, properly ordered: */ + __init_array_start = ABSOLUTE(.); + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __init_array_end = ABSOLUTE(.); + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + _bss_table_start = ABSOLUTE(.); + LONG(_bss_start) + LONG(_bss_end) + _bss_table_end = ABSOLUTE(.); + _rodata_end = ABSOLUTE(.); + } >dram0_0_seg :dram0_0_phdr + + .bss ALIGN(8) (NOLOAD) : ALIGN(4) + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + _heap_start = ABSOLUTE(.); +/* _stack_sentry = ALIGN(0x8); */ + } >dram0_0_seg :dram0_0_bss_phdr +/* __stack = 0x3ffc8000; */ + + + .lit4 : ALIGN(4) + { + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + } >iram1_0_seg :iram1_0_phdr + + +} + +/* get ROM code address */ +INCLUDE "../ld/eagle.rom.addr.v6.ld" diff --git a/tasmota/ld/eagle.rom.addr.v6.ld b/tasmota/ld/eagle.rom.addr.v6.ld new file mode 100644 index 000000000..84c5e3acf --- /dev/null +++ b/tasmota/ld/eagle.rom.addr.v6.ld @@ -0,0 +1,352 @@ +PROVIDE ( Cache_Read_Disable = 0x400047f0 ); +PROVIDE ( Cache_Read_Enable = 0x40004678 ); +PROVIDE ( FilePacketSendReqMsgProc = 0x400035a0 ); +PROVIDE ( FlashDwnLdParamCfgMsgProc = 0x4000368c ); +PROVIDE ( FlashDwnLdStartMsgProc = 0x40003538 ); +PROVIDE ( FlashDwnLdStopReqMsgProc = 0x40003658 ); +PROVIDE ( GetUartDevice = 0x40003f4c ); +PROVIDE ( MD5Final = 0x40009900 ); +PROVIDE ( MD5Init = 0x40009818 ); +PROVIDE ( MD5Update = 0x40009834 ); +PROVIDE ( MemDwnLdStartMsgProc = 0x400036c4 ); +PROVIDE ( MemDwnLdStopReqMsgProc = 0x4000377c ); +PROVIDE ( MemPacketSendReqMsgProc = 0x400036f0 ); +PROVIDE ( RcvMsg = 0x40003eac ); +PROVIDE ( SHA1Final = 0x4000b648 ); +PROVIDE ( SHA1Init = 0x4000b584 ); +PROVIDE ( SHA1Transform = 0x4000a364 ); +PROVIDE ( SHA1Update = 0x4000b5a8 ); +PROVIDE ( SPI_read_status = 0x400043c8 ); +PROVIDE ( SPI_write_status = 0x40004400 ); +PROVIDE ( SPI_write_enable = 0x4000443c ); +PROVIDE ( Wait_SPI_Idle = 0x4000448c ); +PROVIDE ( Enable_QMode = 0x400044c0 ); +PROVIDE ( SPIEraseArea = 0x40004b44 ); +PROVIDE ( SPIEraseBlock = 0x400049b4 ); +PROVIDE ( SPIEraseChip = 0x40004984 ); +PROVIDE ( SPIEraseSector = 0x40004a00 ); +PROVIDE ( SPILock = 0x400048a8 ); +PROVIDE ( SPIParamCfg = 0x40004c2c ); +PROVIDE ( SPIRead = 0x40004b1c ); +PROVIDE ( SPIReadModeCnfig = 0x400048ec ); +PROVIDE ( SPIUnlock = 0x40004878 ); +PROVIDE ( SPIWrite = 0x40004a4c ); +PROVIDE ( SelectSpiFunction = 0x40003f58 ); +PROVIDE ( SendMsg = 0x40003cf4 ); +PROVIDE ( UartConnCheck = 0x40003230 ); +PROVIDE ( UartConnectProc = 0x400037a0 ); +PROVIDE ( UartDwnLdProc = 0x40003368 ); +PROVIDE ( UartGetCmdLn = 0x40003ef4 ); +PROVIDE ( UartRegReadProc = 0x4000381c ); +PROVIDE ( UartRegWriteProc = 0x400037ac ); +PROVIDE ( UartRxString = 0x40003c30 ); +PROVIDE ( Uart_Init = 0x40003a14 ); +PROVIDE ( _ResetHandler = 0x400000a4 ); +PROVIDE ( _ResetVector = 0x40000080 ); +PROVIDE ( __adddf3 = 0x4000c538 ); +PROVIDE ( __addsf3 = 0x4000c180 ); +PROVIDE ( __divdf3 = 0x4000cb94 ); +PROVIDE ( __divdi3 = 0x4000ce60 ); +PROVIDE ( __divsi3 = 0x4000dc88 ); +PROVIDE ( __extendsfdf2 = 0x4000cdfc ); +PROVIDE ( __fixdfsi = 0x4000ccb8 ); +PROVIDE ( __fixunsdfsi = 0x4000cd00 ); +PROVIDE ( __fixunssfsi = 0x4000c4c4 ); +PROVIDE ( __floatsidf = 0x4000e2f0 ); +PROVIDE ( __floatsisf = 0x4000e2ac ); +PROVIDE ( __floatunsidf = 0x4000e2e8 ); +PROVIDE ( __floatunsisf = 0x4000e2a4 ); +PROVIDE ( __muldf3 = 0x4000c8f0 ); +PROVIDE ( __muldi3 = 0x40000650 ); +PROVIDE ( __mulsf3 = 0x4000c3dc ); +PROVIDE ( __subdf3 = 0x4000c688 ); +PROVIDE ( __subsf3 = 0x4000c268 ); +PROVIDE ( __truncdfsf2 = 0x4000cd5c ); +PROVIDE ( __udivdi3 = 0x4000d310 ); +PROVIDE ( __udivsi3 = 0x4000e21c ); +PROVIDE ( __umoddi3 = 0x4000d770 ); +PROVIDE ( __umodsi3 = 0x4000e268 ); +PROVIDE ( __umulsidi3 = 0x4000dcf0 ); +PROVIDE ( _rom_store = 0x4000e388 ); +PROVIDE ( _rom_store_table = 0x4000e328 ); +PROVIDE ( _start = 0x4000042c ); +PROVIDE ( _xtos_alloca_handler = 0x4000dbe0 ); +PROVIDE ( _xtos_c_wrapper_handler = 0x40000598 ); +PROVIDE ( _xtos_cause3_handler = 0x40000590 ); +PROVIDE ( _xtos_ints_off = 0x4000bda4 ); +PROVIDE ( _xtos_ints_on = 0x4000bd84 ); +PROVIDE ( _xtos_l1int_handler = 0x4000048c ); +PROVIDE ( _xtos_p_none = 0x4000dbf8 ); +PROVIDE ( _xtos_restore_intlevel = 0x4000056c ); +PROVIDE ( _xtos_return_from_exc = 0x4000dc54 ); +PROVIDE ( _xtos_set_exception_handler = 0x40000454 ); +PROVIDE ( _xtos_set_interrupt_handler = 0x4000bd70 ); +PROVIDE ( _xtos_set_interrupt_handler_arg = 0x4000bd28 ); +PROVIDE ( _xtos_set_intlevel = 0x4000dbfc ); +PROVIDE ( _xtos_set_min_intlevel = 0x4000dc18 ); +PROVIDE ( _xtos_set_vpri = 0x40000574 ); +PROVIDE ( _xtos_syscall_handler = 0x4000dbe4 ); +PROVIDE ( _xtos_unhandled_exception = 0x4000dc44 ); +PROVIDE ( _xtos_unhandled_interrupt = 0x4000dc3c ); +PROVIDE ( aes_decrypt = 0x400092d4 ); +PROVIDE ( aes_decrypt_deinit = 0x400092e4 ); +PROVIDE ( aes_decrypt_init = 0x40008ea4 ); +PROVIDE ( aes_unwrap = 0x40009410 ); +PROVIDE ( base64_decode = 0x40009648 ); +PROVIDE ( base64_encode = 0x400094fc ); +PROVIDE ( bzero = 0x4000de84 ); +PROVIDE ( cmd_parse = 0x40000814 ); +PROVIDE ( conv_str_decimal = 0x40000b24 ); +PROVIDE ( conv_str_hex = 0x40000cb8 ); +PROVIDE ( convert_para_str = 0x40000a60 ); +PROVIDE ( dtm_get_intr_mask = 0x400026d0 ); +PROVIDE ( dtm_params_init = 0x4000269c ); +PROVIDE ( dtm_set_intr_mask = 0x400026c8 ); +PROVIDE ( dtm_set_params = 0x400026dc ); +PROVIDE ( eprintf = 0x40001d14 ); +PROVIDE ( eprintf_init_buf = 0x40001cb8 ); +PROVIDE ( eprintf_to_host = 0x40001d48 ); +PROVIDE ( est_get_printf_buf_remain_len = 0x40002494 ); +PROVIDE ( est_reset_printf_buf_len = 0x4000249c ); +PROVIDE ( ets_bzero = 0x40002ae8 ); +PROVIDE ( ets_char2xdigit = 0x40002b74 ); +PROVIDE ( ets_delay_us = 0x40002ecc ); +PROVIDE ( ets_enter_sleep = 0x400027b8 ); +PROVIDE ( ets_external_printf = 0x40002578 ); +PROVIDE ( ets_get_cpu_frequency = 0x40002f0c ); +PROVIDE ( ets_getc = 0x40002bcc ); +PROVIDE ( ets_install_external_printf = 0x40002450 ); +PROVIDE ( ets_install_putc1 = 0x4000242c ); +PROVIDE ( ets_install_putc2 = 0x4000248c ); +PROVIDE ( ets_install_uart_printf = 0x40002438 ); +/* Undocumented function to print character to UART */ +PROVIDE ( ets_uart_putc1 = 0x40001dcc ); +/* permanently hide reimplemented ets_intr_*lock(), see #6484 +PROVIDE ( ets_intr_lock = 0x40000f74 ); +PROVIDE ( ets_intr_unlock = 0x40000f80 ); +*/ +PROVIDE ( ets_isr_attach = 0x40000f88 ); +PROVIDE ( ets_isr_mask = 0x40000f98 ); +PROVIDE ( ets_isr_unmask = 0x40000fa8 ); +PROVIDE ( ets_memcmp = 0x400018d4 ); +PROVIDE ( ets_memcpy = 0x400018b4 ); +PROVIDE ( ets_memmove = 0x400018c4 ); +PROVIDE ( ets_memset = 0x400018a4 ); +/* renamed to ets_post_rom(), see #6484 +PROVIDE ( ets_post = 0x40000e24 ); +*/ +PROVIDE ( ets_post_rom = 0x40000e24 ); +PROVIDE ( ets_printf = 0x400024cc ); +PROVIDE ( ets_putc = 0x40002be8 ); +PROVIDE ( ets_rtc_int_register = 0x40002a40 ); +PROVIDE ( ets_run = 0x40000e04 ); +PROVIDE ( ets_set_idle_cb = 0x40000dc0 ); +PROVIDE ( ets_set_user_start = 0x40000fbc ); +PROVIDE ( ets_str2macaddr = 0x40002af8 ); +PROVIDE ( ets_strcmp = 0x40002aa8 ); +PROVIDE ( ets_strcpy = 0x40002a88 ); +PROVIDE ( ets_strlen = 0x40002ac8 ); +PROVIDE ( ets_strncmp = 0x40002ab8 ); +PROVIDE ( ets_strncpy = 0x40002a98 ); +PROVIDE ( ets_strstr = 0x40002ad8 ); +PROVIDE ( ets_task = 0x40000dd0 ); +PROVIDE ( ets_timer_arm = 0x40002cc4 ); +PROVIDE ( ets_timer_disarm = 0x40002d40 ); +PROVIDE ( ets_timer_done = 0x40002d80 ); +PROVIDE ( ets_timer_handler_isr = 0x40002da8 ); +PROVIDE ( ets_timer_init = 0x40002e68 ); +PROVIDE ( ets_timer_setfn = 0x40002c48 ); +PROVIDE ( ets_uart_printf = 0x40002544 ); +PROVIDE ( ets_update_cpu_frequency = 0x40002f04 ); +PROVIDE ( ets_vprintf = 0x40001f00 ); +PROVIDE ( ets_wdt_disable = 0x400030f0 ); +PROVIDE ( ets_wdt_enable = 0x40002fa0 ); +PROVIDE ( ets_wdt_get_mode = 0x40002f34 ); +PROVIDE ( ets_wdt_init = 0x40003170 ); +PROVIDE ( ets_wdt_restore = 0x40003158 ); +PROVIDE ( ets_write_char = 0x40001da0 ); +PROVIDE ( get_first_seg = 0x4000091c ); +PROVIDE ( gpio_init = 0x40004c50 ); +PROVIDE ( gpio_input_get = 0x40004cf0 ); +PROVIDE ( gpio_intr_ack = 0x40004dcc ); +PROVIDE ( gpio_intr_handler_register = 0x40004e28 ); +PROVIDE ( gpio_intr_pending = 0x40004d88 ); +PROVIDE ( gpio_intr_test = 0x40004efc ); +PROVIDE ( gpio_output_set = 0x40004cd0 ); +PROVIDE ( gpio_pin_intr_state_set = 0x40004d90 ); +PROVIDE ( gpio_pin_wakeup_disable = 0x40004ed4 ); +PROVIDE ( gpio_pin_wakeup_enable = 0x40004e90 ); +PROVIDE ( gpio_register_get = 0x40004d5c ); +PROVIDE ( gpio_register_set = 0x40004d04 ); +PROVIDE ( hmac_md5 = 0x4000a2cc ); +PROVIDE ( hmac_md5_vector = 0x4000a160 ); +PROVIDE ( hmac_sha1 = 0x4000ba28 ); +PROVIDE ( hmac_sha1_vector = 0x4000b8b4 ); +PROVIDE ( lldesc_build_chain = 0x40004f40 ); +PROVIDE ( lldesc_num2link = 0x40005050 ); +PROVIDE ( lldesc_set_owner = 0x4000507c ); +PROVIDE ( main = 0x40000fec ); +PROVIDE ( md5_vector = 0x400097ac ); +PROVIDE ( mem_calloc = 0x40001c2c ); +PROVIDE ( mem_free = 0x400019e0 ); +PROVIDE ( mem_init = 0x40001998 ); +PROVIDE ( mem_malloc = 0x40001b40 ); +PROVIDE ( mem_realloc = 0x40001c6c ); +PROVIDE ( mem_trim = 0x40001a14 ); +PROVIDE ( mem_zalloc = 0x40001c58 ); +PROVIDE ( memcmp = 0x4000dea8 ); +PROVIDE ( memcpy = 0x4000df48 ); +PROVIDE ( memmove = 0x4000e04c ); +PROVIDE ( memset = 0x4000e190 ); +PROVIDE ( multofup = 0x400031c0 ); +PROVIDE ( pbkdf2_sha1 = 0x4000b840 ); +PROVIDE ( phy_get_romfuncs = 0x40006b08 ); +PROVIDE ( rand = 0x40000600 ); +PROVIDE ( rc4_skip = 0x4000dd68 ); +PROVIDE ( recv_packet = 0x40003d08 ); +PROVIDE ( remove_head_space = 0x40000a04 ); +PROVIDE ( rijndaelKeySetupDec = 0x40008dd0 ); +PROVIDE ( rijndaelKeySetupEnc = 0x40009300 ); +PROVIDE ( rom_abs_temp = 0x400060c0 ); +PROVIDE ( rom_ana_inf_gating_en = 0x40006b10 ); +PROVIDE ( rom_cal_tos_v50 = 0x40007a28 ); +PROVIDE ( rom_chip_50_set_channel = 0x40006f84 ); +PROVIDE ( rom_chip_v5_disable_cca = 0x400060d0 ); +PROVIDE ( rom_chip_v5_enable_cca = 0x400060ec ); +PROVIDE ( rom_chip_v5_rx_init = 0x4000711c ); +PROVIDE ( rom_chip_v5_sense_backoff = 0x4000610c ); +PROVIDE ( rom_chip_v5_tx_init = 0x4000718c ); +PROVIDE ( rom_dc_iq_est = 0x4000615c ); +PROVIDE ( rom_en_pwdet = 0x400061b8 ); +PROVIDE ( rom_get_bb_atten = 0x40006238 ); +PROVIDE ( rom_get_corr_power = 0x40006260 ); +PROVIDE ( rom_get_fm_sar_dout = 0x400062dc ); +PROVIDE ( rom_get_noisefloor = 0x40006394 ); +PROVIDE ( rom_get_power_db = 0x400063b0 ); +PROVIDE ( rom_i2c_readReg = 0x40007268 ); +PROVIDE ( rom_i2c_readReg_Mask = 0x4000729c ); +PROVIDE ( rom_i2c_writeReg = 0x400072d8 ); +PROVIDE ( rom_i2c_writeReg_Mask = 0x4000730c ); +PROVIDE ( rom_iq_est_disable = 0x40006400 ); +PROVIDE ( rom_iq_est_enable = 0x40006430 ); +PROVIDE ( rom_linear_to_db = 0x40006484 ); +PROVIDE ( rom_mhz2ieee = 0x400065a4 ); +PROVIDE ( rom_pbus_dco___SA2 = 0x40007bf0 ); +PROVIDE ( rom_pbus_debugmode = 0x4000737c ); +PROVIDE ( rom_pbus_enter_debugmode = 0x40007410 ); +PROVIDE ( rom_pbus_exit_debugmode = 0x40007448 ); +PROVIDE ( rom_pbus_force_test = 0x4000747c ); +PROVIDE ( rom_pbus_rd = 0x400074d8 ); +PROVIDE ( rom_pbus_set_rxgain = 0x4000754c ); +PROVIDE ( rom_pbus_set_txgain = 0x40007610 ); +PROVIDE ( rom_pbus_workmode = 0x40007648 ); +PROVIDE ( rom_pbus_xpd_rx_off = 0x40007688 ); +PROVIDE ( rom_pbus_xpd_rx_on = 0x400076cc ); +PROVIDE ( rom_pbus_xpd_tx_off = 0x400076fc ); +PROVIDE ( rom_pbus_xpd_tx_on = 0x40007740 ); +PROVIDE ( rom_pbus_xpd_tx_on__low_gain = 0x400077a0 ); +PROVIDE ( rom_phy_reset_req = 0x40007804 ); +PROVIDE ( rom_restart_cal = 0x4000781c ); +PROVIDE ( rom_rfcal_pwrctrl = 0x40007eb4 ); +PROVIDE ( rom_rfcal_rxiq = 0x4000804c ); +PROVIDE ( rom_rfcal_rxiq_set_reg = 0x40008264 ); +PROVIDE ( rom_rfcal_txcap = 0x40008388 ); +PROVIDE ( rom_rfcal_txiq = 0x40008610 ); +PROVIDE ( rom_rfcal_txiq_cover = 0x400088b8 ); +PROVIDE ( rom_rfcal_txiq_set_reg = 0x40008a70 ); +PROVIDE ( rom_rfpll_reset = 0x40007868 ); +PROVIDE ( rom_rfpll_set_freq = 0x40007968 ); +PROVIDE ( rom_rxiq_cover_mg_mp = 0x40008b6c ); +PROVIDE ( rom_rxiq_get_mis = 0x40006628 ); +PROVIDE ( rom_sar_init = 0x40006738 ); +PROVIDE ( rom_set_ana_inf_tx_scale = 0x4000678c ); +PROVIDE ( rom_set_channel_freq = 0x40006c50 ); +PROVIDE ( rom_set_loopback_gain = 0x400067c8 ); +PROVIDE ( rom_set_noise_floor = 0x40006830 ); +PROVIDE ( rom_set_rxclk_en = 0x40006550 ); +PROVIDE ( rom_set_txbb_atten = 0x40008c6c ); +PROVIDE ( rom_set_txclk_en = 0x4000650c ); +PROVIDE ( rom_set_txiq_cal = 0x40008d34 ); +PROVIDE ( rom_start_noisefloor = 0x40006874 ); +PROVIDE ( rom_start_tx_tone = 0x400068b4 ); +PROVIDE ( rom_stop_tx_tone = 0x4000698c ); +PROVIDE ( rom_tx_mac_disable = 0x40006a98 ); +PROVIDE ( rom_tx_mac_enable = 0x40006ad4 ); +PROVIDE ( rom_txtone_linear_pwr = 0x40006a1c ); +PROVIDE ( rom_write_rfpll_sdm = 0x400078dc ); +PROVIDE ( roundup2 = 0x400031b4 ); +PROVIDE ( rtc_enter_sleep = 0x40002870 ); +PROVIDE ( rtc_get_reset_reason = 0x400025e0 ); +PROVIDE ( rtc_intr_handler = 0x400029ec ); +PROVIDE ( rtc_set_sleep_mode = 0x40002668 ); +PROVIDE ( save_rxbcn_mactime = 0x400027a4 ); +PROVIDE ( save_tsf_us = 0x400027ac ); +PROVIDE ( send_packet = 0x40003c80 ); +PROVIDE ( sha1_prf = 0x4000ba48 ); +PROVIDE ( sha1_vector = 0x4000a2ec ); +PROVIDE ( sip_alloc_to_host_evt = 0x40005180 ); +PROVIDE ( sip_get_ptr = 0x400058a8 ); +PROVIDE ( sip_get_state = 0x40005668 ); +PROVIDE ( sip_init_attach = 0x4000567c ); +PROVIDE ( sip_install_rx_ctrl_cb = 0x4000544c ); +PROVIDE ( sip_install_rx_data_cb = 0x4000545c ); +PROVIDE ( sip_post = 0x400050fc ); +PROVIDE ( sip_post_init = 0x400056c4 ); +PROVIDE ( sip_reclaim_from_host_cmd = 0x4000534c ); +PROVIDE ( sip_reclaim_tx_data_pkt = 0x400052c0 ); +PROVIDE ( sip_send = 0x40005808 ); +PROVIDE ( sip_to_host_chain_append = 0x40005864 ); +PROVIDE ( sip_to_host_evt_send_done = 0x40005234 ); +PROVIDE ( slc_add_credits = 0x400060ac ); +PROVIDE ( slc_enable = 0x40005d90 ); +PROVIDE ( slc_from_host_chain_fetch = 0x40005f24 ); +PROVIDE ( slc_from_host_chain_recycle = 0x40005e94 ); +PROVIDE ( slc_init_attach = 0x40005c50 ); +PROVIDE ( slc_init_credit = 0x4000608c ); +PROVIDE ( slc_pause_from_host = 0x40006014 ); +PROVIDE ( slc_reattach = 0x40005c1c ); +PROVIDE ( slc_resume_from_host = 0x4000603c ); +PROVIDE ( slc_select_tohost_gpio = 0x40005dc0 ); +PROVIDE ( slc_select_tohost_gpio_mode = 0x40005db8 ); +PROVIDE ( slc_send_to_host_chain = 0x40005de4 ); +PROVIDE ( slc_set_host_io_max_window = 0x40006068 ); +PROVIDE ( slc_to_host_chain_recycle = 0x40005f10 ); +PROVIDE ( software_reset = 0x4000264c ); +PROVIDE ( spi_flash_attach = 0x40004644 ); +PROVIDE ( srand = 0x400005f0 ); +PROVIDE ( strcmp = 0x4000bdc8 ); +PROVIDE ( strcpy = 0x4000bec8 ); +PROVIDE ( strlen = 0x4000bf4c ); +PROVIDE ( strncmp = 0x4000bfa8 ); +PROVIDE ( strncpy = 0x4000c0a0 ); +PROVIDE ( strstr = 0x4000e1e0 ); +PROVIDE ( timer_insert = 0x40002c64 ); +PROVIDE ( uartAttach = 0x4000383c ); +PROVIDE ( uart_baudrate_detect = 0x40003924 ); +PROVIDE ( uart_buff_switch = 0x400038a4 ); +PROVIDE ( uart_rx_intr_handler = 0x40003bbc ); +PROVIDE ( uart_rx_one_char = 0x40003b8c ); +PROVIDE ( uart_rx_one_char_block = 0x40003b64 ); +PROVIDE ( uart_rx_readbuff = 0x40003ec8 ); +PROVIDE ( uart_tx_one_char = 0x40003b30 ); +PROVIDE ( wepkey_128 = 0x4000bc40 ); +PROVIDE ( wepkey_64 = 0x4000bb3c ); +PROVIDE ( xthal_bcopy = 0x40000688 ); +PROVIDE ( xthal_copy123 = 0x4000074c ); +PROVIDE ( xthal_get_ccompare = 0x4000dd4c ); +PROVIDE ( xthal_get_ccount = 0x4000dd38 ); +PROVIDE ( xthal_get_interrupt = 0x4000dd58 ); +PROVIDE ( xthal_get_intread = 0x4000dd58 ); +PROVIDE ( xthal_memcpy = 0x400006c4 ); +PROVIDE ( xthal_set_ccompare = 0x4000dd40 ); +PROVIDE ( xthal_set_intclear = 0x4000dd60 ); +PROVIDE ( xthal_spill_registers_into_stack_nw = 0x4000e320 ); +PROVIDE ( xthal_window_spill = 0x4000e324 ); +PROVIDE ( xthal_window_spill_nw = 0x4000e320 ); + +PROVIDE ( Te0 = 0x3fffccf0 ); +PROVIDE ( Td0 = 0x3fffd100 ); +PROVIDE ( Td4s = 0x3fffd500); +PROVIDE ( rcons = 0x3fffd0f0); +PROVIDE ( UartDev = 0x3fffde10 ); +PROVIDE ( flashchip = 0x3fffc714); From ec899817aa84c07faba4f9eb2df9a2e96ce72f0b Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 1 Apr 2020 14:05:30 +0200 Subject: [PATCH 047/547] Change some deepsleep wake messages - Change remove MQTT Info messages on restart for DeepSleep Wake (#8044) - Add command ``SetOption90 1`` to disable non-json MQTT messages (#8044) --- RELEASENOTES.md | 3 +++ tasmota/CHANGELOG.md | 4 ++- tasmota/settings.h | 2 +- tasmota/xdrv_02_mqtt.ino | 55 ++++++++++++++++++++++------------------ 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 23bd31fef..864a562e1 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -56,12 +56,15 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Change HM-10 sensor type detection and add features (#7962) - Change light scheme 2,3,4 cycle time speed from 24,48,72,... seconds to 4,6,12,24,36,48,... seconds (#8034) +- Change remove floating point libs from IRAM +- Change remove MQTT Info messages on restart for DeepSleep Wake (#8044) - Fix possible Relay toggle on (OTA) restart - Fix Zigbee sending wrong Sat value with Hue emulation - Add Zigbee command ``ZbRestore`` to restore device configuration dumped with ``ZbStatus 2`` - Add Zigbee command ``ZbUnbind`` - Add Zigbee command ``ZbBindState`` and ``manuf``attribute - Add commands ``CounterDebounceLow`` and ``CounterDebounceHigh`` to control debouncing (#8021) +- Add command ``SetOption90 1`` to disable non-json MQTT messages (#8044) - Add support for unreachable (unplugged) Zigbee devices in Philips Hue emulation and Alexa - Add support for 64x48 SSD1306 OLED (#6740) - Add support for up to four MQTT GroupTopics using the same optional Device Group names (#8014) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index cf6941a6d..0b4f503bd 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -3,10 +3,12 @@ ### 8.2.0.3 20200329 - Change light scheme 2,3,4 cycle time speed from 24,48,72,... seconds to 4,6,12,24,36,48,... seconds (#8034) +- Change remove floating point libs from IRAM +- Change remove MQTT Info messages on restart for DeepSleep Wake (#8044) - Add support for longer template names - Add Zigbee command ``ZbBindState`` and ``manuf``attribute - Add commands ``CounterDebounceLow`` and ``CounterDebounceHigh`` to control debouncing (#8021) -- Change remove floating point libs from IRAM +- Add command ``SetOption90 1`` to disable non-json MQTT messages (#8044) ### 8.2.0.2 20200328 diff --git a/tasmota/settings.h b/tasmota/settings.h index 95b4779e6..18bd7f1c6 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -109,7 +109,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t powered_off_led : 1; // bit 5 (v8.1.0.9) - SetOption87 - PWM Dimmer Turn red LED on when powered off uint32_t remote_device_mode : 1; // bit 6 (v8.1.0.9) - SetOption88 - PWM Dimmer Buttons control remote devices uint32_t zigbee_distinct_topics : 1; // bit 7 (v8.1.0.10) - SetOption89 - Distinct MQTT topics per device for Zigbee (#7835) - uint32_t spare08 : 1; + uint32_t only_json_message : 1; // bit 8 (v8.2.0.3) - SetOption90 - Disable non-json MQTT response uint32_t spare09 : 1; uint32_t spare10 : 1; uint32_t spare11 : 1; diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index ac3065fe8..912e7c929 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -440,9 +440,11 @@ void MqttPublishPowerState(uint32_t device) Response_P(S_JSON_COMMAND_SVALUE, scommand, GetStateText(bitRead(power, device -1))); MqttPublish(stopic); - GetTopic_P(stopic, STAT, mqtt_topic, scommand); - Response_P(GetStateText(bitRead(power, device -1))); - MqttPublish(stopic, Settings.flag.mqtt_power_retain); // CMND_POWERRETAIN + if (!Settings.flag4.only_json_message) { // SetOption90 - Disable non-json MQTT response + GetTopic_P(stopic, STAT, mqtt_topic, scommand); + Response_P(GetStateText(bitRead(power, device -1))); + MqttPublish(stopic, Settings.flag.mqtt_power_retain); // CMND_POWERRETAIN + } #ifdef USE_SONOFF_IFAN } #endif // USE_SONOFF_IFAN @@ -503,9 +505,11 @@ void MqttConnected(void) Response_P(PSTR(D_ONLINE)); MqttPublish(stopic, true); - // Satisfy iobroker (#299) - mqtt_data[0] = '\0'; - MqttPublishPrefixTopic_P(CMND, S_RSLT_POWER); + if (!Settings.flag4.only_json_message) { // SetOption90 - Disable non-json MQTT response + // Satisfy iobroker (#299) + mqtt_data[0] = '\0'; + MqttPublishPrefixTopic_P(CMND, S_RSLT_POWER); + } GetTopic_P(stopic, CMND, mqtt_topic, PSTR("#")); MqttSubscribe(stopic); @@ -526,30 +530,33 @@ void MqttConnected(void) } if (Mqtt.initial_connection_state) { - char stopic2[TOPSZ]; - Response_P(PSTR("{\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_FALLBACKTOPIC "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\"}"), - ModuleName().c_str(), my_version, my_image, GetFallbackTopic_P(stopic, ""), GetGroupTopic_P(stopic2, "", SET_MQTT_GRP_TOPIC)); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "1")); + if (ResetReason() != REASON_DEEP_SLEEP_AWAKE) { + char stopic2[TOPSZ]; + Response_P(PSTR("{\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_FALLBACKTOPIC "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\"}"), + ModuleName().c_str(), my_version, my_image, GetFallbackTopic_P(stopic, ""), GetGroupTopic_P(stopic2, "", SET_MQTT_GRP_TOPIC)); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "1")); #ifdef USE_WEBSERVER - if (Settings.webserver) { + if (Settings.webserver) { #if LWIP_IPV6 - Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"IPv6Address\":\"%s\"}"), - (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str(),WifiGetIPv6().c_str()); + Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"IPv6Address\":\"%s\"}"), + (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str(),WifiGetIPv6().c_str()); #else - Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\"}"), - (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str()); + Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\"}"), + (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str()); #endif // LWIP_IPV6 = 1 - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "2")); - } + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "2")); + } #endif // USE_WEBSERVER - Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":")); - if (CrashFlag()) { - CrashDump(); - } else { - ResponseAppend_P(PSTR("\"%s\""), GetResetReason().c_str()); + Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":")); + if (CrashFlag()) { + CrashDump(); + } else { + ResponseAppend_P(PSTR("\"%s\""), GetResetReason().c_str()); + } + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "3")); } - ResponseJsonEnd(); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "3")); + MqttPublishAllPowerState(); if (Settings.tele_period) { tele_period = Settings.tele_period -5; // Enable TelePeriod in 5 seconds From caff54da7ccadf437a1d5e4bc72599cdc0f530dc Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 1 Apr 2020 14:39:43 +0200 Subject: [PATCH 048/547] Fix unquoted non-json data Fix unquoted non-json data (#8040) --- tasmota/support_tasmota.ino | 4 ++-- tasmota/xdrv_08_serial_bridge.ino | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 23a4caad7..ed0355a88 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -1286,9 +1286,9 @@ void SerialInput(void) char hex_char[(serial_in_byte_counter * 2) + 2]; bool assume_json = (!Settings.flag.mqtt_serial_raw && (serial_in_buffer[0] == '{')); Response_P(PSTR("{\"" D_JSON_SERIALRECEIVED "\":%s%s%s}"), - (assume_json) ? "" : """", + (assume_json) ? "" : "\"", (Settings.flag.mqtt_serial_raw) ? ToHex_P((unsigned char*)serial_in_buffer, serial_in_byte_counter, hex_char, sizeof(hex_char)) : serial_in_buffer, - (assume_json) ? "" : """"); + (assume_json) ? "" : "\""); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SERIALRECEIVED)); XdrvRulesProcess(); serial_in_byte_counter = 0; diff --git a/tasmota/xdrv_08_serial_bridge.ino b/tasmota/xdrv_08_serial_bridge.ino index c4f530a21..990bf9c32 100644 --- a/tasmota/xdrv_08_serial_bridge.ino +++ b/tasmota/xdrv_08_serial_bridge.ino @@ -73,9 +73,9 @@ void SerialBridgeInput(void) char hex_char[(serial_bridge_in_byte_counter * 2) + 2]; bool assume_json = (!serial_bridge_raw && (serial_bridge_buffer[0] == '{')); Response_P(PSTR("{\"" D_JSON_SSERIALRECEIVED "\":%s%s%s}"), - (assume_json) ? "" : """", + (assume_json) ? "" : "\"", (serial_bridge_raw) ? ToHex_P((unsigned char*)serial_bridge_buffer, serial_bridge_in_byte_counter, hex_char, sizeof(hex_char)) : serial_bridge_buffer, - (assume_json) ? "" : """"); + (assume_json) ? "" : "\""); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SSERIALRECEIVED)); XdrvRulesProcess(); serial_bridge_in_byte_counter = 0; From 4b09af8a36924324cd99cef91085221a0dbd3fdf Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 1 Apr 2020 16:46:12 +0200 Subject: [PATCH 049/547] Add BH1750 resolution control Add command ``Sensor10 0/1/2`` to control BH1750 resolution - 0 = High (default), 1 = High2, 2 = Low (#8016) --- RELEASENOTES.md | 1 + tasmota/CHANGELOG.md | 1 + tasmota/settings.h | 3 +- tasmota/xsns_10_bh1750.ino | 103 +++++++++++++++++++++++++++---------- 4 files changed, 79 insertions(+), 29 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 864a562e1..44162fb7d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -65,6 +65,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Add Zigbee command ``ZbBindState`` and ``manuf``attribute - Add commands ``CounterDebounceLow`` and ``CounterDebounceHigh`` to control debouncing (#8021) - Add command ``SetOption90 1`` to disable non-json MQTT messages (#8044) +- Add command ``Sensor10 0/1/2`` to control BH1750 resolution - 0 = High (default), 1 = High2, 2 = Low (#8016) - Add support for unreachable (unplugged) Zigbee devices in Philips Hue emulation and Alexa - Add support for 64x48 SSD1306 OLED (#6740) - Add support for up to four MQTT GroupTopics using the same optional Device Group names (#8014) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 0b4f503bd..ab18b9a3e 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -9,6 +9,7 @@ - Add Zigbee command ``ZbBindState`` and ``manuf``attribute - Add commands ``CounterDebounceLow`` and ``CounterDebounceHigh`` to control debouncing (#8021) - Add command ``SetOption90 1`` to disable non-json MQTT messages (#8044) +- Add command ``Sensor10 0/1/2`` to control BH1750 resolution - 0 = High (default), 1 = High2, 2 = Low (#8016) ### 8.2.0.2 20200328 diff --git a/tasmota/settings.h b/tasmota/settings.h index 18bd7f1c6..44c2e061a 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -205,8 +205,7 @@ typedef union { uint8_t spare1 : 1; uint8_t spare2 : 1; uint8_t spare3 : 1; - uint8_t spare4 : 1; - uint8_t spare5 : 1; + uint8_t bh1750_resolution : 2; // Sensor10 1,2,3 uint8_t hx711_json_weight_change : 1; // Sensor34 8,x - Enable JSON message on weight change uint8_t mhz19b_abc_disable : 1; // Disable ABC (Automatic Baseline Correction for MHZ19(B) (0 = Enabled (default), 1 = Disabled with Sensor15 command) }; diff --git a/tasmota/xsns_10_bh1750.ino b/tasmota/xsns_10_bh1750.ino index 044089368..47bc6953a 100644 --- a/tasmota/xsns_10_bh1750.ino +++ b/tasmota/xsns_10_bh1750.ino @@ -31,24 +31,41 @@ #define BH1750_ADDR1 0x23 #define BH1750_ADDR2 0x5C -#define BH1750_CONTINUOUS_HIGH_RES_MODE 0x10 // Start measurement at 1lx resolution. Measurement time is approx 120ms. +#define BH1750_CONTINUOUS_HIGH_RES_MODE2 0x11 // Start measurement at 0.5 lx resolution. Measurement time is approx 120ms. +#define BH1750_CONTINUOUS_HIGH_RES_MODE 0x10 // Start measurement at 1 lx resolution. Measurement time is approx 120ms. +#define BH1750_CONTINUOUS_LOW_RES_MODE 0x13 // Start measurement at 4 lx resolution. Measurement time is approx 16ms. -uint8_t bh1750_address; -uint8_t bh1750_addresses[] = { BH1750_ADDR1, BH1750_ADDR2 }; -uint8_t bh1750_type = 0; -uint8_t bh1750_valid = 0; -uint16_t bh1750_illuminance = 0; -char bh1750_types[] = "BH1750"; +const char kBhMessages[] PROGMEM = "Resolution High|Resolution High2|Resolution Low"; + +struct BH1750DATA { + uint8_t address; + uint8_t addresses[2] = { BH1750_ADDR1, BH1750_ADDR2 }; + uint8_t resolution[3] = { BH1750_CONTINUOUS_HIGH_RES_MODE, BH1750_CONTINUOUS_HIGH_RES_MODE2, BH1750_CONTINUOUS_LOW_RES_MODE }; + uint8_t type = 0; + uint8_t valid = 0; + uint16_t illuminance = 0; + char types[7] = "BH1750"; +} Bh1750; + +/*********************************************************************************************/ + +bool Bh1750SetResolution(void) +{ + Wire.beginTransmission(Bh1750.address); + Wire.write(Bh1750.resolution[Settings.SensorBits1.bh1750_resolution]); + return (!Wire.endTransmission()); +} bool Bh1750Read(void) { - if (bh1750_valid) { bh1750_valid--; } + if (Bh1750.valid) { Bh1750.valid--; } - if (2 != Wire.requestFrom(bh1750_address, (uint8_t)2)) { return false; } - uint8_t msb = Wire.read(); - uint8_t lsb = Wire.read(); - bh1750_illuminance = ((msb << 8) | lsb) / 1.2; - bh1750_valid = SENSOR_MAX_MISS; + if (2 != Wire.requestFrom(Bh1750.address, (uint8_t)2)) { return false; } + Bh1750.illuminance = (Wire.read() << 8) | Wire.read(); + if (1 == Settings.SensorBits1.bh1750_resolution) { Bh1750.illuminance >>= 1; } + Bh1750.illuminance /= 1.2; + + Bh1750.valid = SENSOR_MAX_MISS; return true; } @@ -56,14 +73,13 @@ bool Bh1750Read(void) void Bh1750Detect(void) { - for (uint32_t i = 0; i < sizeof(bh1750_addresses); i++) { - bh1750_address = bh1750_addresses[i]; - if (I2cActive(bh1750_address)) { continue; } - Wire.beginTransmission(bh1750_address); - Wire.write(BH1750_CONTINUOUS_HIGH_RES_MODE); - if (!Wire.endTransmission()) { - I2cSetActiveFound(bh1750_address, bh1750_types); - bh1750_type = 1; + for (uint32_t i = 0; i < sizeof(Bh1750.addresses); i++) { + Bh1750.address = Bh1750.addresses[i]; + if (I2cActive(Bh1750.address)) { continue; } + + if (Bh1750SetResolution()) { + I2cSetActiveFound(Bh1750.address, Bh1750.types); + Bh1750.type = 1; break; } } @@ -73,23 +89,51 @@ void Bh1750EverySecond(void) { // 1mS if (!Bh1750Read()) { - AddLogMissed(bh1750_types, bh1750_valid); + AddLogMissed(Bh1750.types, Bh1750.valid); } } +/*********************************************************************************************\ + * Command Sensor10 + * + * 0 - High resolution mode (default) + * 1 - High resolution mode 2 + * 2 - Low resolution mode +\*********************************************************************************************/ + +bool Bh1750CommandSensor(void) +{ + uint32_t message_index = Settings.SensorBits1.bh1750_resolution; + if (XdrvMailbox.data_len) { + switch (XdrvMailbox.payload) { + case 0: + case 1: + case 2: + Settings.SensorBits1.bh1750_resolution = XdrvMailbox.payload; + Bh1750SetResolution(); + message_index = Settings.SensorBits1.bh1750_resolution; + break; + } + } + char res_text[64]; + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_10, GetTextIndexed(res_text, sizeof(res_text), message_index, kBhMessages)); + + return true; +} + void Bh1750Show(bool json) { - if (bh1750_valid) { + if (Bh1750.valid) { if (json) { - ResponseAppend_P(JSON_SNS_ILLUMINANCE, bh1750_types, bh1750_illuminance); + ResponseAppend_P(JSON_SNS_ILLUMINANCE, Bh1750.types, Bh1750.illuminance); #ifdef USE_DOMOTICZ if (0 == tele_period) { - DomoticzSensor(DZ_ILLUMINANCE, bh1750_illuminance); + DomoticzSensor(DZ_ILLUMINANCE, Bh1750.illuminance); } #endif // USE_DOMOTICZ #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_SNS_ILLUMINANCE, bh1750_types, bh1750_illuminance); + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, Bh1750.types, Bh1750.illuminance); #endif // USE_WEBSERVER } } @@ -108,11 +152,16 @@ bool Xsns10(uint8_t function) if (FUNC_INIT == function) { Bh1750Detect(); } - else if (bh1750_type) { + else if (Bh1750.type) { switch (function) { case FUNC_EVERY_SECOND: Bh1750EverySecond(); break; + case FUNC_COMMAND_SENSOR: + if (XSNS_10 == XdrvMailbox.index) { + result = Bh1750CommandSensor(); + } + break; case FUNC_JSON_APPEND: Bh1750Show(1); break; From 49febe4a54310b5e128a16403a2f769d1182a573 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 2 Apr 2020 15:17:54 +0200 Subject: [PATCH 050/547] Add BH1750 Measurement Time control Add command ``Sensor10 31..254`` to control BH1750 measurement time which defaults to 69 (#8016) --- RELEASENOTES.md | 1 + tasmota/CHANGELOG.md | 1 + tasmota/xsns_10_bh1750.ino | 65 +++++++++++++++++++++++--------------- 3 files changed, 42 insertions(+), 25 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 44162fb7d..ec242c079 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -66,6 +66,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Add commands ``CounterDebounceLow`` and ``CounterDebounceHigh`` to control debouncing (#8021) - Add command ``SetOption90 1`` to disable non-json MQTT messages (#8044) - Add command ``Sensor10 0/1/2`` to control BH1750 resolution - 0 = High (default), 1 = High2, 2 = Low (#8016) +- Add command ``Sensor10 31..254`` to control BH1750 measurement time which defaults to 69 (#8016) - Add support for unreachable (unplugged) Zigbee devices in Philips Hue emulation and Alexa - Add support for 64x48 SSD1306 OLED (#6740) - Add support for up to four MQTT GroupTopics using the same optional Device Group names (#8014) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index ab18b9a3e..7dbbf3290 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -10,6 +10,7 @@ - Add commands ``CounterDebounceLow`` and ``CounterDebounceHigh`` to control debouncing (#8021) - Add command ``SetOption90 1`` to disable non-json MQTT messages (#8044) - Add command ``Sensor10 0/1/2`` to control BH1750 resolution - 0 = High (default), 1 = High2, 2 = Low (#8016) +- Add command ``Sensor10 31..254`` to control BH1750 measurement time which defaults to 69 (#8016) ### 8.2.0.2 20200328 diff --git a/tasmota/xsns_10_bh1750.ino b/tasmota/xsns_10_bh1750.ino index 47bc6953a..1ba00b46e 100644 --- a/tasmota/xsns_10_bh1750.ino +++ b/tasmota/xsns_10_bh1750.ino @@ -25,17 +25,18 @@ * I2C Address: 0x23 or 0x5C \*********************************************************************************************/ -#define XSNS_10 10 -#define XI2C_11 11 // See I2CDEVICES.md +#define XSNS_10 10 +#define XI2C_11 11 // See I2CDEVICES.md -#define BH1750_ADDR1 0x23 -#define BH1750_ADDR2 0x5C +#define BH1750_ADDR1 0x23 +#define BH1750_ADDR2 0x5C -#define BH1750_CONTINUOUS_HIGH_RES_MODE2 0x11 // Start measurement at 0.5 lx resolution. Measurement time is approx 120ms. -#define BH1750_CONTINUOUS_HIGH_RES_MODE 0x10 // Start measurement at 1 lx resolution. Measurement time is approx 120ms. -#define BH1750_CONTINUOUS_LOW_RES_MODE 0x13 // Start measurement at 4 lx resolution. Measurement time is approx 16ms. +#define BH1750_CONTINUOUS_HIGH_RES_MODE2 0x11 // Start measurement at 0.5 lx resolution. Measurement time is approx 120ms. +#define BH1750_CONTINUOUS_HIGH_RES_MODE 0x10 // Start measurement at 1 lx resolution. Measurement time is approx 120ms. +#define BH1750_CONTINUOUS_LOW_RES_MODE 0x13 // Start measurement at 4 lx resolution. Measurement time is approx 16ms. -const char kBhMessages[] PROGMEM = "Resolution High|Resolution High2|Resolution Low"; +#define BH1750_MEASUREMENT_TIME_HIGH 0x40 // Measurement Time register high 3 bits +#define BH1750_MEASUREMENT_TIME_LOW 0x60 // Measurement Time register low 5 bits struct BH1750DATA { uint8_t address; @@ -43,6 +44,7 @@ struct BH1750DATA { uint8_t resolution[3] = { BH1750_CONTINUOUS_HIGH_RES_MODE, BH1750_CONTINUOUS_HIGH_RES_MODE2, BH1750_CONTINUOUS_LOW_RES_MODE }; uint8_t type = 0; uint8_t valid = 0; + uint8_t mtreg = 69; // Default Measurement Time uint16_t illuminance = 0; char types[7] = "BH1750"; } Bh1750; @@ -56,14 +58,29 @@ bool Bh1750SetResolution(void) return (!Wire.endTransmission()); } +bool Bh1750SetMTreg(void) +{ + Wire.beginTransmission(Bh1750.address); + uint8_t data = BH1750_MEASUREMENT_TIME_HIGH | ((Bh1750.mtreg >> 5) & 0x07); + Wire.write(data); + if (Wire.endTransmission()) { return false; } + Wire.beginTransmission(Bh1750.address); + data = BH1750_MEASUREMENT_TIME_LOW | (Bh1750.mtreg & 0x1F); + Wire.write(data); + if (Wire.endTransmission()) { return false; } + return Bh1750SetResolution(); +} + bool Bh1750Read(void) { if (Bh1750.valid) { Bh1750.valid--; } if (2 != Wire.requestFrom(Bh1750.address, (uint8_t)2)) { return false; } Bh1750.illuminance = (Wire.read() << 8) | Wire.read(); - if (1 == Settings.SensorBits1.bh1750_resolution) { Bh1750.illuminance >>= 1; } - Bh1750.illuminance /= 1.2; + Bh1750.illuminance /= (1.2 * (69 / Bh1750.mtreg)); + if (1 == Settings.SensorBits1.bh1750_resolution) { + Bh1750.illuminance >>= 1; + } Bh1750.valid = SENSOR_MAX_MISS; return true; @@ -77,7 +94,7 @@ void Bh1750Detect(void) Bh1750.address = Bh1750.addresses[i]; if (I2cActive(Bh1750.address)) { continue; } - if (Bh1750SetResolution()) { + if (Bh1750SetMTreg()) { I2cSetActiveFound(Bh1750.address, Bh1750.types); Bh1750.type = 1; break; @@ -96,27 +113,25 @@ void Bh1750EverySecond(void) /*********************************************************************************************\ * Command Sensor10 * - * 0 - High resolution mode (default) - * 1 - High resolution mode 2 - * 2 - Low resolution mode + * 0 - High resolution mode (default) + * 1 - High resolution mode 2 + * 2 - Low resolution mode + * 31..254 - Measurement Time value (not persistent, default is 69) \*********************************************************************************************/ bool Bh1750CommandSensor(void) { - uint32_t message_index = Settings.SensorBits1.bh1750_resolution; if (XdrvMailbox.data_len) { - switch (XdrvMailbox.payload) { - case 0: - case 1: - case 2: - Settings.SensorBits1.bh1750_resolution = XdrvMailbox.payload; - Bh1750SetResolution(); - message_index = Settings.SensorBits1.bh1750_resolution; - break; + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { + Settings.SensorBits1.bh1750_resolution = XdrvMailbox.payload; + Bh1750SetResolution(); + } + else if ((XdrvMailbox.payload > 30) && (XdrvMailbox.payload < 255)) { + Bh1750.mtreg = XdrvMailbox.payload; + Bh1750SetMTreg(); } } - char res_text[64]; - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_10, GetTextIndexed(res_text, sizeof(res_text), message_index, kBhMessages)); + Response_P(PSTR("{\"" D_CMND_SENSOR "10\":{\"Resolution\":%d,\"MTime\":%d}}"), Settings.SensorBits1.bh1750_resolution, Bh1750.mtreg); return true; } From c066b0c8e2245645952c9836045fad0e7b0de8d5 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 3 Apr 2020 18:34:02 +0200 Subject: [PATCH 051/547] Fix compile error if no USE_LIGHT --- tasmota/xsns_15_mhz19.ino | 2 ++ tasmota/xsns_17_senseair.ino | 2 ++ tasmota/xsns_38_az7798.ino | 2 ++ 3 files changed, 6 insertions(+) diff --git a/tasmota/xsns_15_mhz19.ino b/tasmota/xsns_15_mhz19.ino index 860eec8ce..633ca3f59 100644 --- a/tasmota/xsns_15_mhz19.ino +++ b/tasmota/xsns_15_mhz19.ino @@ -230,7 +230,9 @@ void MhzEverySecond(void) mhz_type = (s) ? 1 : 2; if (MhzCheckAndApplyFilter(ppm, s)) { mhz_retry = MHZ19_RETRY_COUNT; +#ifdef USE_LIGHT LightSetSignal(CO2_LOW, CO2_HIGH, mhz_last_ppm); +#endif // USE_LIGHT if (0 == s || 64 == s) { // Reading is stable. if (mhz_abc_must_apply) { diff --git a/tasmota/xsns_17_senseair.ino b/tasmota/xsns_17_senseair.ino index dee516dff..60614b4c7 100644 --- a/tasmota/xsns_17_senseair.ino +++ b/tasmota/xsns_17_senseair.ino @@ -84,7 +84,9 @@ void Senseair250ms(void) // Every 250 mSec break; case 2: // 0x03 (3) READ_CO2 - fe 04 02 06 2c af 59 senseair_co2 = value; +#ifdef USE_LIGHT LightSetSignal(CO2_LOW, CO2_HIGH, senseair_co2); +#endif // USE_LIGHT break; case 3: // 0x04 (4) READ_TEMPERATURE - S8: fe 84 02 f2 f1 - Illegal Data Address senseair_temperature = ConvertTemp((float)value / 100); diff --git a/tasmota/xsns_38_az7798.ino b/tasmota/xsns_38_az7798.ino index 18705dce5..b6b1dffbe 100644 --- a/tasmota/xsns_38_az7798.ino +++ b/tasmota/xsns_38_az7798.ino @@ -215,7 +215,9 @@ void AzEverySecond(void) } response_substr[j] = 0; // add null terminator az_co2 = atoi((char*)response_substr); +#ifdef USE_LIGHT LightSetSignal(CO2_LOW, CO2_HIGH, az_co2); +#endif // USE_LIGHT i += 3; // advance to second delimiter if(az_response[i] != ':') { AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error second delimiter")); From f393e2ade45cefb69da6096b1cbfa6900433a39c Mon Sep 17 00:00:00 2001 From: Paul C Diem Date: Fri, 3 Apr 2020 11:49:01 -0500 Subject: [PATCH 052/547] Use SetOption32 for hold time, Use SendKey to process ButtonTopic/Rules and ignore handled button presses/holds. --- PWM_Dimmer.md | 6 +- tasmota/xdrv_35_pwm_dimmer.ino | 533 ++++++++++++++++----------------- 2 files changed, 253 insertions(+), 286 deletions(-) diff --git a/PWM_Dimmer.md b/PWM_Dimmer.md index 7254a5da2..af6f7abe7 100644 --- a/PWM_Dimmer.md +++ b/PWM_Dimmer.md @@ -25,9 +25,9 @@ Holding the power button, tapping the down button and then tapping or holding th Holding the power button, tapping the up button and then tapping or holding the down or up button publishes an MQTT Event command. The command is sent/value is adjusted once every .5 seconds for as long as the button is held. The MQTT topic is as described above. The MQTT payload is Trigger#, where # is 3 if the down button is held or 4 if the up button is held. -Holding the down or up button alone for over 10 seconds executes the WiFiConfig 2 command. +Holding any button alone for over 10 seconds executes the WiFiConfig 2 command. -Pressing and releasing any button publishes an MQTT TOGGLE command for the button. Holding a button publishes an MQTT HOLD command followed by an MQTT OFF command when the button is released. +SetOption32 defines the button hold time. When the PWM Dimmer module is initially selected, SetOption32 is set to 5 (1/2 second). Button presses and holds execute the normal ButtonTopic and Rule processing. If ButtonTopic is set and SetOption61 is 0 or a the button press/hold matches a rule, the button press/hold is ignored by PWM Dimmer. When Device Groups are enabled, the PWM Dimmer brightness presets are kept in sync across all switches in the group. The powered-off LED and LED timeout settings are specific to each switch. Changing them does not replicate the change to the other switches in the group. @@ -81,7 +81,7 @@ When Device Groups are enabled, the PWM Dimmer brightness presets are kept in sy Remote device mode allows PWM Dimmer switches to control remote devices. With remote device mode enabled, each button controls a different device. Note that dimmer switches with toggle-style down/up buttons have limited functionality as remote device mode switches because you can not push the down and up buttons simultaneously. -To include remote device mode support in the build, define USE_PWM_DIMMER_REMOTE in your user_config_override. Remote device mode support requires device group support so USE_DEVICE_GROUPS is automatically defined if USE_PWM_DIMMER_REMOTE is defined. Remote device mode support adds 0.7K to the code size in addition to the code size required for device groups support. +To include remote device mode support in the build, define USE_PWM_DIMMER_REMOTE in your user_config_override. Remote device mode support requires device group support so USE_DEVICE_GROUPS is automatically defined if USE_PWM_DIMMER_REMOTE is defined. Remote device mode support adds 1.2K to the code size in addition to the code size required for device groups support. To enable remote device mode, execute the command SetOption88 1 (the device will restart). Each remote device must be running firmware with device group support and have remote device support enabled. The remote devices do not need to be built with PWM dimmer support nor do they need to be switches. diff --git a/tasmota/xdrv_35_pwm_dimmer.ino b/tasmota/xdrv_35_pwm_dimmer.ino index 54f270c4c..0e0594a8b 100644 --- a/tasmota/xdrv_35_pwm_dimmer.ino +++ b/tasmota/xdrv_35_pwm_dimmer.ino @@ -50,37 +50,42 @@ struct remote_pwm_dimmer { }; #endif // USE_PWM_DIMMER_REMOTE -uint32_t led_timeout_time = 0; -uint32_t turn_off_brightness_leds_time = 0; -uint32_t button_press_time; +uint32_t last_button_press_time; uint32_t button_hold_time[3]; -uint8_t restore_powered_off_led = 0; +uint8_t led_timeout_seconds = 0; +uint8_t restore_powered_off_led_counter = 0; uint8_t power_button_index = 0; uint8_t down_button_index = 1; -uint8_t up_button_index = 2; uint8_t buttons_pressed = 0; uint8_t tap_count = 0; -bool ignore_power_button_hold; -bool ignore_power_button_release; bool down_button_tapped = false; -bool button_was_held = false; bool power_button_increases_bri = true; bool invert_power_button_bri_direction = false; bool restore_brightness_leds = false; -bool button_hold_sent[3]; bool button_pressed[3] = { false, false, false }; +bool button_hold_sent[3]; +bool button_hold_processed[3]; #ifdef USE_PWM_DIMMER_REMOTE struct remote_pwm_dimmer * remote_pwm_dimmers; struct remote_pwm_dimmer * active_remote_pwm_dimmer; bool active_device_is_local; #endif // USE_PWM_DIMMER_REMOTE -void PWMModulePreInit() +void PWMModulePreInit(void) { Settings.seriallog_level = 0; Settings.flag.mqtt_serial = 0; // Disable serial logging Settings.ledstate = 0; // Disable LED usage + // If the module was just changed to PWM Dimmer, set the defaults. + if (Settings.last_module != Settings.module) { + Settings.flag.pwm_control = true; // SetOption15 - Switch between commands PWM or COLOR/DIMMER/CT/CHANNEL + Settings.param[P_HOLD_TIME] = 5; // SetOption32 - Button held for factor times longer + Settings.bri_power_on = Settings.bri_preset_low = Settings.bri_preset_high = 0; + Settings.last_module = Settings.module; + } + + // Make sure the brightness level settings are sensible. if (!Settings.bri_power_on) Settings.bri_power_on = 128; if (!Settings.bri_preset_low) Settings.bri_preset_low = 10; if (Settings.bri_preset_high < Settings.bri_preset_low) Settings.bri_preset_high = 255; @@ -142,7 +147,7 @@ void PWMDimmerSetBrightnessLeds(int32_t operation) } // If enabled, set the LED timeout. - if (!operation) led_timeout_time = (current_bri && Settings.flag4.led_timeout ? millis() + 5000 : 0); + if (!operation) led_timeout_seconds = (current_bri && Settings.flag4.led_timeout ? 5 : 0); } } @@ -164,9 +169,9 @@ void PWMDimmerSetPower(void) } #ifdef USE_DEVICE_GROUPS -void PWMDimmerHandleDeviceGroupItem() +void PWMDimmerHandleDeviceGroupItem(void) { - uint8_t value = XdrvMailbox.payload; + uint32_t value = XdrvMailbox.payload; #ifdef USE_PWM_DIMMER_REMOTE uint8_t device_group_index = XdrvMailbox.index >> 16 & 0xff; bool device_is_local = device_groups[device_group_index].local; @@ -178,6 +183,12 @@ void PWMDimmerHandleDeviceGroupItem() case DGR_ITEM_LIGHT_BRI: if (!device_is_local) remote_pwm_dimmer->bri = value; break; + case DGR_ITEM_POWER: + if (!device_is_local) { + remote_pwm_dimmer->power = value; + remote_pwm_dimmer->power_button_increases_bri = (remote_pwm_dimmer->bri < 128); + } + break; case DGR_ITEM_LIGHT_FIXED_COLOR: if (!device_is_local) remote_pwm_dimmer->fixed_color_index = value; break; @@ -217,7 +228,7 @@ void PWMDimmerHandleDeviceGroupItem() } #endif // USE_DEVICE_GROUPS -void PWMDimmerHandleButton() +void PWMDimmerHandleButton(void) { /* * Power Button Up/Down Buttons State Remote Mode Action @@ -251,16 +262,16 @@ void PWMDimmerHandleButton() // If the button is not pressed and was not just released (the most common case), ... if (XdrvMailbox.payload && !button_pressed[XdrvMailbox.index]) { - // If no buttons have been pressed for 250ms, reset the button press counts. - if (button_press_time && !buttons_pressed && millis() - button_press_time > 400) { - button_was_held = false; + // If no buttons have been pressed for 400ms, reset the button press counts. + if (last_button_press_time && !buttons_pressed && millis() - last_button_press_time > 400) { + last_button_press_time = 0; tap_count = 0; } - return; } bool state_updated = false; + int8_t bri_offset = 0; uint8_t power_on_bri = 0; uint8_t dgr_item = 0; uint8_t dgr_value; @@ -268,265 +279,192 @@ void PWMDimmerHandleButton() uint32_t button_index = XdrvMailbox.index; uint32_t now = millis(); - // Set a bool indicating if the power is on. + // Initialize some variables. #ifdef USE_PWM_DIMMER_REMOTE bool power_is_on = (!active_device_is_local ? active_remote_pwm_dimmer->power : power); + bool is_power_button = (button_index == power_button_index); #else // USE_PWM_DIMMER_REMOTE bool power_is_on = power; + bool is_power_button = !button_index; #endif // USE_PWM_DIMMER_REMOTE + bool is_down_button = (button_index == down_button_index); // If the button is pressed, ... if (!XdrvMailbox.payload) { - int8_t bri_direction = 0; // If the button was just pressed, flag the button as pressed, clear the hold sent flag and // increment the buttons pressed count. if (!button_pressed[button_index]) { - button_press_time = now; + last_button_press_time = now; button_pressed[button_index] = true; + button_hold_time[button_index] = now + Settings.param[P_HOLD_TIME] * 100; button_hold_sent[button_index] = false; buttons_pressed++; -#ifdef USE_PWM_DIMMER_REMOTE +#ifdef USE_PWM_DIMMER_REMOTE // If there are no other buttons pressed right now and remote mode is enabled, make the device // associated with this button the device we're going to control. if (buttons_pressed == 1 && Settings.flag4.remote_device_mode) { power_button_index = button_index; - up_button_index = (button_index == 2 ? 1 : 2); down_button_index = (button_index ? 0 : 1); active_device_is_local = device_groups[power_button_index].local; if (!active_device_is_local) active_remote_pwm_dimmer = &remote_pwm_dimmers[power_button_index - 1]; } - if (button_index == power_button_index) { -#else // USE_PWM_DIMMER_REMOTE - // If this is about the power button, initialize some variables. - if (!button_index) { #endif // USE_PWM_DIMMER_REMOTE - button_hold_time[button_index] = now + 500; - ignore_power_button_hold = false; - ignore_power_button_release = false; + + return; + } + + // If the button is being held, ... + if (button_hold_time[button_index] < now) { + + // If the power button is not also pressed and the button has been held for over 10 seconds, + // execute the WiFiConfig 2 command. + if (!button_pressed[power_button_index] && now - button_hold_time[button_index] > 10000) { + button_hold_time[button_index] = now + 90000; + char scmnd[20]; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " 2")); + ExecuteCommand(scmnd, SRC_BUTTON); return; } - // If this is not about the power button, load the new hold time. Note that the hold time for - // the power button is longer than the hold time for the other buttons. - button_hold_time[button_index] = now + (tap_count ? 0 : 250); - } - - // If the button is being held, send a button hold. - else if (button_hold_time[button_index] < now) { + // Send a button hold if we haven't already. If it is handled (by the button topic or by a + // rule), ignore the this button until it's released. if (!button_hold_sent[button_index]) { button_hold_sent[button_index] = true; - SendKey(KEY_BUTTON, button_index + 1, POWER_HOLD); + button_hold_processed[button_index] = (!is_power_button && tap_count ? false : SendKey(KEY_BUTTON, button_index + 1, POWER_HOLD)); } - } + if (!button_hold_processed[button_index]) { - // If this is about the power button, ... + // If this is about the power button, ... + if (is_power_button) { + + // If no other buttons are pressed, ... + if (buttons_pressed == 1) { + + // If the power is on, adjust the brightness. Set the direction based on the current + // direction for the device and then invert the direction when the power button is + // released. The new brightness will be calculated below. + if (power_is_on) { #ifdef USE_PWM_DIMMER_REMOTE - if (button_index == power_button_index) { + bri_offset = (!active_device_is_local ? (active_remote_pwm_dimmer->power_button_increases_bri ? 1 : -1) : (power_button_increases_bri ? 1 : -1)); #else // USE_PWM_DIMMER_REMOTE - if (!button_index) { + bri_offset = (power_button_increases_bri ? 1 : -1); #endif // USE_PWM_DIMMER_REMOTE - - // If the power button has been held with no other buttons pressed, ... - if (!ignore_power_button_hold && button_hold_time[button_index] < now) { - ignore_power_button_release = true; - - // If the power is on, adjust the brightness. Set the direction based on the current - // direction for the device and then invert the direction when the power button is released. - // The new brightness will be calculated below. - if (power_is_on) { -#ifdef USE_PWM_DIMMER_REMOTE - bri_direction = (!active_device_is_local ? (active_remote_pwm_dimmer->power_button_increases_bri ? 1 : -1) : (power_button_increases_bri ? 1 : -1)); -#else // USE_PWM_DIMMER_REMOTE - bri_direction = (power_button_increases_bri ? 1 : -1); -#endif // USE_PWM_DIMMER_REMOTE - invert_power_button_bri_direction = true; - } - - // If the power is not on, turn it on using an initial brightness of bri_preset_low, set the - // power button hold dimmer direction to true so holding the power switch increases the - // brightness and the power button hold time to delay before we start increasing the - // brightness. - else { -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) { - power_on_bri = active_remote_pwm_dimmer->bri = active_remote_pwm_dimmer->bri_preset_low; - active_remote_pwm_dimmer->power_button_increases_bri = true; - button_hold_time[button_index] = now + 1000; - } - else { -#endif // USE_PWM_DIMMER_REMOTE - power_on_bri = Settings.bri_preset_low; - power_button_increases_bri = true; - button_hold_time[button_index] = now + 500; -#ifdef USE_PWM_DIMMER_REMOTE - } -#endif // USE_PWM_DIMMER_REMOTE - } - } - } - - // If this is about the down or up buttons, ... - else { - bool is_down_button = (button_index == down_button_index); - - // If the power button is also pressed, set flags to ignore the power button being held and - // the next power button release. - if (button_pressed[power_button_index]) { - ignore_power_button_release = ignore_power_button_hold = true; - } - - // If the button is being held, ... - if (button_hold_time[button_index] < now) { - uint8_t mqtt_trigger = 0; - - // If the up or down button was tapped while holding the power button before this, handle - // the operation. - if (tap_count) { - - // Send a device group update to select the previous/next fixed color. - if (down_button_tapped) { -#ifdef USE_DEVICE_GROUPS - uint8_t uint8_value; -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - uint8_value = active_remote_pwm_dimmer->fixed_color_index; - else -#endif // USE_PWM_DIMMER_REMOTE - uint8_value = Light.fixed_color_index; - if (is_down_button) { - if (uint8_value) - uint8_value--; - else - uint8_value = MAX_FIXED_COLOR; + invert_power_button_bri_direction = true; } + + // If the power is not on, turn it on using an initial brightness of bri_preset_low and + // set the power button hold time to delay before we start increasing the brightness. else { - if (uint8_value < MAX_FIXED_COLOR) - uint8_value++; +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local) + power_on_bri = active_remote_pwm_dimmer->bri = active_remote_pwm_dimmer->bri_preset_low; else - uint8_value = 0; +#endif // USE_PWM_DIMMER_REMOTE + power_on_bri = Settings.bri_preset_low; + button_hold_time[button_index] = now + 500; } -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - active_remote_pwm_dimmer->fixed_color_index = uint8_value; - else -#endif // USE_PWM_DIMMER_REMOTE - Light.fixed_color_index = uint8_value; - dgr_item = DGR_ITEM_LIGHT_FIXED_COLOR; - dgr_value = uint8_value; - dgr_more_to_come = true; -#endif // USE_DEVICE_GROUPS - ; - } - - // Publish MQTT Event Trigger#. - else { - mqtt_trigger = (is_down_button ? 3 : 4); } } - // If the power button is not also pressed and the button has been held for over 10 seconds, - // execute the WiFiConfig 2 command. - else if (!button_pressed[power_button_index] && now - button_hold_time[button_index] > 10000) { - button_hold_time[button_index] = now + 90000; - char scmnd[20]; - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " 2")); - ExecuteCommand(scmnd, SRC_BUTTON); - return; - } - - // If the power is not on, publish MQTT Event Trigger#. - else if (!power_is_on) { - mqtt_trigger = (is_down_button ? 1 : 2); - } - - button_was_held = true; - button_hold_time[button_index] = now + 500; - - // If we need to publish an MQTT trigger, do it. - if (mqtt_trigger) { - char topic[TOPSZ]; - sprintf_P(mqtt_data, PSTR("Trigger%u"), mqtt_trigger); -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) { - snprintf_P(topic, sizeof(topic), PSTR("cmnd/%s/Event"), device_groups[power_button_index].group_name); - MqttPublish(topic); - } - else -#endif // USE_PWM_DIMMER_REMOTE - MqttPublishPrefixTopic_P(CMND, PSTR("Event")); - } - } - - // If the power is on and the up or down button was not tapped while holding the power button - // before this, adjust the brightness. Set the direction based on which button is pressed. The - // new brightness will be calculated below. - if (power_is_on && !tap_count) { - bri_direction = (is_down_button ? -1 : 1); - } - } - - // If we need to adjust the brightness, do it. - if (bri_direction) { - int32_t bri; -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - bri = active_remote_pwm_dimmer->bri; - else -#endif // USE_PWM_DIMMER_REMOTE - bri = light_state.getBri(); - int32_t new_bri; - int32_t offset = (Settings.light_correction ? 4 : bri / 16 + 1); - if (bri_direction > 0) { - new_bri = bri + offset; - if (new_bri > 255) new_bri = 255; - } - else { - new_bri = bri - offset; - if (new_bri < 1) new_bri = 1; - } - if (new_bri != bri) { -#ifdef USE_DEVICE_GROUPS - SendDeviceGroupMessage(power_button_index, DGR_MSGTYP_UPDATE_MORE_TO_COME, DGR_ITEM_LIGHT_BRI, new_bri); -#endif // USE_DEVICE_GROUPS -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - active_remote_pwm_dimmer->bri_power_on = active_remote_pwm_dimmer->bri = new_bri; + // If this is about the down or up buttons, ... else { -#endif // USE_PWM_DIMMER_REMOTE - skip_light_fade = true; - light_state.setBri(new_bri); - LightAnimate(); - skip_light_fade = false; - Settings.bri_power_on = new_bri; + + // If the power is on and the up or down button was not tapped while holding the power + // button before this, adjust the brightness. Set the direction based on which button is + // pressed. The new brightness will be calculated below. + if (power_is_on && !tap_count) { + bri_offset = (is_down_button ? -1 : 1); + } + + else { + uint8_t mqtt_trigger = 0; + + // If the up or down button was tapped while holding the power button before this, + // handle the operation. + if (tap_count) { + + // Send a device group update to select the previous/next fixed color. + if (down_button_tapped) { +#ifdef USE_DEVICE_GROUPS + uint8_t uint8_value; #ifdef USE_PWM_DIMMER_REMOTE - } + if (!active_device_is_local) + uint8_value = active_remote_pwm_dimmer->fixed_color_index; + else #endif // USE_PWM_DIMMER_REMOTE - } - else { - PWMDimmerSetBrightnessLeds(0); + uint8_value = Light.fixed_color_index; + if (is_down_button) { + if (uint8_value) + uint8_value--; + else + uint8_value = MAX_FIXED_COLOR; + } + else { + if (uint8_value < MAX_FIXED_COLOR) + uint8_value++; + else + uint8_value = 0; + } +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local) + active_remote_pwm_dimmer->fixed_color_index = uint8_value; + else +#endif // USE_PWM_DIMMER_REMOTE + Light.fixed_color_index = uint8_value; + dgr_item = DGR_ITEM_LIGHT_FIXED_COLOR; + dgr_value = uint8_value; + dgr_more_to_come = true; +#endif // USE_DEVICE_GROUPS + ; + } + + // Publish MQTT Event Trigger#. + else { + mqtt_trigger = (is_down_button ? 3 : 4); + } + } + + // If the power is not on, publish MQTT Event Trigger#. + else if (!power_is_on) { + mqtt_trigger = (is_down_button ? 1 : 2); + } + + // If we need to publish an MQTT trigger, do it. + if (mqtt_trigger) { + char topic[TOPSZ]; + sprintf_P(mqtt_data, PSTR("Trigger%u"), mqtt_trigger); +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local) { + snprintf_P(topic, sizeof(topic), PSTR("cmnd/%s/Event"), device_groups[power_button_index].group_name); + MqttPublish(topic); + } + else +#endif // USE_PWM_DIMMER_REMOTE + MqttPublishPrefixTopic_P(CMND, PSTR("Event")); + } + + button_hold_time[button_index] = now + 500; + } + } } } } // If the button was just released, ... else { -// if (now - button_press_time > Settings.button_debounce) { + bool button_was_held = button_hold_sent[button_index]; - // If the button was held, send a button off; otherwise, send a button toggle. - SendKey(KEY_BUTTON, button_index + 1, (button_hold_sent[button_index] ? POWER_OFF : POWER_TOGGLE)); + // If the button was not held, send a button toggle. If the button was held but not processes by + // support_button or support_buttondoesn't process the toggle (is not handled by a rule), ... + if (!(button_hold_sent[button_index] ? button_hold_processed[button_index] : SendKey(KEY_BUTTON, button_index + 1, POWER_TOGGLE))) { // If this is about the power button, ... -#ifdef USE_PWM_DIMMER_REMOTE - if (button_index == power_button_index) { -#else // USE_PWM_DIMMER_REMOTE - if (!button_index) { -#endif // USE_PWM_DIMMER_REMOTE + if (is_power_button) { - // If we're ignoring the next power button released, ... - if (ignore_power_button_release) { - ignore_power_button_release = false; + // If the power button was held, ... + if (button_was_held) { // If the power button was held with no other buttons pressed, we changed the brightness // so invert the bri direction for the next time and send a final update. @@ -544,7 +482,7 @@ void PWMDimmerHandleButton() #endif // USE_PWM_DIMMER_REMOTE } - // If the up or down button was tapped while the power button was pressed, ... + // If the up or down button was tapped while the power button was held, ... else if (tap_count) { // If the button was tapped but not held, handle the operation based on which button was @@ -579,93 +517,116 @@ void PWMDimmerHandleButton() } } - // If we're not ignoring the power button until it's released, toggle the power. + // If the power button was not held, toggle the power. else { #ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) { + if (!active_device_is_local) power_on_bri = active_remote_pwm_dimmer->bri_power_on; - if (active_remote_pwm_dimmer->bri > 251) - active_remote_pwm_dimmer->power_button_increases_bri = false; - else if (active_remote_pwm_dimmer->bri < 4) - active_remote_pwm_dimmer->power_button_increases_bri = true; - } - else { + else #endif // USE_PWM_DIMMER_REMOTE power_on_bri = Settings.bri_power_on; - if (power_on_bri > 251) - power_button_increases_bri = false; - else if (power_on_bri < 4) - power_button_increases_bri = true; -#ifdef USE_PWM_DIMMER_REMOTE - } -#endif // USE_PWM_DIMMER_REMOTE } } // If this is about the up or down buttons, ... else { - bool is_down_button = (button_index == down_button_index); if (restore_brightness_leds) { restore_brightness_leds = false; PWMDimmerSetBrightnessLeds(Settings.flag4.led_timeout ? -1 : 0); } - // If the power is on and the up or down button was not tapped while holding the power - // button before this, we changed the brightness and sent updates with the more-to-come - // message type. Send a final update. - if (power_is_on && !tap_count) { - dgr_item = 255; - state_updated = true; - - // If the power button is also pressed, set the power button hold dimmer direction so - // holding the power switch adjusts the brightness away from the brightness we just set. -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local && button_pressed[power_button_index]) active_remote_pwm_dimmer->power_button_increases_bri = is_down_button; -#endif // USE_PWM_DIMMER_REMOTE + // If the button was not held and the power button is also pressed, increment the count of + // how many times the button has been tapped. + if (!button_was_held && button_pressed[power_button_index]) { + down_button_tapped = is_down_button; + tap_count++; } - // If the button was not held, ... - if (!button_was_held) { + // If the button wasn't tapped while the power button was pressed, ... + if (!tap_count) { - // If the power button is also pressed, increment the count of how many times a button has - // been tapped. - if (button_pressed[power_button_index]) { - down_button_tapped = (button_index == down_button_index); - tap_count++; + // If the power is on, ... + if (power_is_on) { + + // If the button was not held, adjust the brightness. Set the direction based on which + // button is pressed. The new brightness will be calculated below. + if (button_hold_time[button_index] >= now) { + bri_offset = (is_down_button ? -10 : 10); + dgr_item = 255; + } + + // If the button was held and the hold was not processed by a rule, we changed the + // brightness and sent updates with the more-to-come message type while the button was + // held. Send a final update. + else if (!button_hold_processed[button_index]) { + dgr_item = 255; + state_updated = true; + } } // If the power is off, turn it on using a temporary brightness of bri_preset_low if the // down button is pressed or bri_preset_low if the up button is pressed. + else { #ifdef USE_PWM_DIMMER_REMOTE - else if ((!active_device_is_local ? !active_remote_pwm_dimmer->power : !power)) { -#else // USE_PWM_DIMMER_REMOTE - else if (!power) { -#endif // USE_PWM_DIMMER_REMOTE -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) { + if (!active_device_is_local) power_on_bri = active_remote_pwm_dimmer->bri = (is_down_button ? active_remote_pwm_dimmer->bri_preset_low : active_remote_pwm_dimmer->bri_preset_high); - active_remote_pwm_dimmer->power_button_increases_bri = is_down_button; - } - else { + else #endif // USE_PWM_DIMMER_REMOTE power_on_bri = (is_down_button ? Settings.bri_preset_low : Settings.bri_preset_high); - power_button_increases_bri = is_down_button; -#ifdef USE_PWM_DIMMER_REMOTE - } -#endif // USE_PWM_DIMMER_REMOTE - button_hold_time[button_index] = now + 500; } } } -// } + } // Flag the button as released. button_pressed[button_index] = false; buttons_pressed--; } - if (power_on_bri) { + // If we need to adjust the brightness, do it. + if (bri_offset) { + int32_t bri; +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local) + bri = active_remote_pwm_dimmer->bri; + else +#endif // USE_PWM_DIMMER_REMOTE + bri = light_state.getBri(); + int32_t new_bri; + bri_offset *= (Settings.light_correction ? 4 : bri / 16 + 1); + new_bri = bri + bri_offset; + if (bri_offset > 0) { + if (new_bri > 255) new_bri = 255; + } + else { + if (new_bri < 1) new_bri = 1; + } + if (new_bri != bri) { +#ifdef USE_DEVICE_GROUPS + SendDeviceGroupMessage(power_button_index, (dgr_item ? DGR_MSGTYP_UPDATE : DGR_MSGTYP_UPDATE_MORE_TO_COME), DGR_ITEM_LIGHT_BRI, new_bri); +#endif // USE_DEVICE_GROUPS +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local) + active_remote_pwm_dimmer->bri_power_on = active_remote_pwm_dimmer->bri = new_bri; + else { +#endif // USE_PWM_DIMMER_REMOTE + skip_light_fade = true; + light_state.setBri(new_bri); + LightAnimate(); + skip_light_fade = false; + Settings.bri_power_on = new_bri; +#ifdef USE_PWM_DIMMER_REMOTE + } +#endif // USE_PWM_DIMMER_REMOTE + } + else { + PWMDimmerSetBrightnessLeds(0); + } + } + + // If we're supposed to toggle the power on, do it. + else if (power_on_bri) { power_t new_power; #ifdef USE_DEVICE_GROUPS #ifdef USE_PWM_DIMMER_REMOTE @@ -686,16 +647,19 @@ void PWMDimmerHandleButton() #endif // USE_DEVICE_GROUPS #ifdef USE_PWM_DIMMER_REMOTE - if (active_device_is_local) { + if (!active_device_is_local) + active_remote_pwm_dimmer->power_button_increases_bri = (power_on_bri < 128); + else { #endif // USE_PWM_DIMMER_REMOTE - ExecuteCommandPower(1, POWER_TOGGLE, SRC_RETRY); light_state.setBri(power_on_bri); + ExecuteCommandPower(1, POWER_TOGGLE, SRC_RETRY); #ifdef USE_PWM_DIMMER_REMOTE } #endif // USE_PWM_DIMMER_REMOTE } - // If we're not toggling the power and we made changes, send a group update. + // If we're not changing the brightness or toggling the power and we made changes, send a group + // update. else if (dgr_item) { #ifdef USE_DEVICE_GROUPS if (dgr_item == 255) dgr_item = 0; @@ -776,8 +740,7 @@ bool Xdrv35(uint8_t function) switch (function) { case FUNC_EVERY_SECOND: // Turn off the brightness LED's if it's time. - if (led_timeout_time && led_timeout_time < millis()) { - led_timeout_time = 0; + if (led_timeout_seconds && !led_timeout_seconds--) { PWMDimmerSetBrightnessLeds(-1); } @@ -786,10 +749,10 @@ bool Xdrv35(uint8_t function) // restored. If the state is blinking now, set a flag so we know that we need to restore it // when it stops blinking. if (global_state.data) - restore_powered_off_led = 5; - else if (restore_powered_off_led) { + restore_powered_off_led_counter = 5; + else if (restore_powered_off_led_counter) { PWMDimmerSetPoweredOffLed(); - restore_powered_off_led--; + restore_powered_off_led_counter--; } break; @@ -811,9 +774,13 @@ bool Xdrv35(uint8_t function) case FUNC_SET_DEVICE_POWER: // If we're turning the power on, turn the relay and the brightness LEDs on and turn the // powered-off LED off. - if (XdrvMailbox.index) + if (XdrvMailbox.index) { PWMDimmerSetPower(); + // Set the power button hold dimmer direction based on the current brightness. + power_button_increases_bri = (light_state.getBri() < 128); + } + // If we're turning the power off, return true so SetDevicePower doesn't turn the relay off. // It will be turned off in LightApplyFade when the fade is done. else From 1e1fd7aece3251554714083883c01aaf90e2d546 Mon Sep 17 00:00:00 2001 From: Paul C Diem Date: Fri, 3 Apr 2020 18:24:48 -0500 Subject: [PATCH 053/547] Handle schemes shared in device groups --- tasmota/xdrv_04_light.ino | 59 ++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index a3f7a6be5..348835ca4 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -273,6 +273,9 @@ struct LIGHT { bool fade_initialized = false; // dont't fade at startup bool fade_running = false; +#ifdef USE_DEVICE_GROUPS + bool devgrp_no_channels_out = false; // don't share channels with device group (e.g. if scheme set by other device) +#endif // USE_DEVICE_GROUPS uint16_t fade_start_10[LST_MAX] = {0,0,0,0,0}; uint16_t fade_cur_10[LST_MAX]; uint16_t fade_end_10[LST_MAX]; // 10 bits resolution target channel values @@ -1429,6 +1432,9 @@ void LightSetSignal(uint16_t lo, uint16_t hi, uint16_t value) // AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Light signal %d"), signal); light_controller.changeRGB(signal, 255 - signal, 0, true); // keep bri Settings.light_scheme = 0; +#ifdef USE_DEVICE_GROUPS + LightUpdateScheme(); +#endif // USE_DEVICE_GROUPS if (0 == light_state.getBri()) { light_controller.changeBri(50); } @@ -1742,6 +1748,9 @@ void LightAnimate(void) Light.wakeup_active = 0; Settings.light_scheme = LS_POWER; +#ifdef USE_DEVICE_GROUPS + LightUpdateScheme(); +#endif // USE_DEVICE_GROUPS } } break; @@ -1775,7 +1784,7 @@ void LightAnimate(void) } if (Light.update) { #ifdef USE_DEVICE_GROUPS - if (Light.power) LightSendDeviceGroupStatus(); + if (Light.power) LightSendDeviceGroupStatus(false); #endif // USE_DEVICE_GROUPS uint16_t cur_col_10[LST_MAX]; // 10 bits resolution @@ -2097,18 +2106,29 @@ void calcGammaBulbs(uint16_t cur_col_10[5]) { } #ifdef USE_DEVICE_GROUPS -void LightSendDeviceGroupStatus() +void LightSendDeviceGroupStatus(bool force) { - if (Light.subtype > LST_SINGLE) { + static uint8_t last_channels[LST_MAX]; + static uint8_t last_bri; + + uint8_t bri = light_state.getBri(); + bool send_bri_update = (force || bri != last_bri); + + if (Light.subtype > LST_SINGLE && !Light.devgrp_no_channels_out) { uint8_t channels[LST_MAX]; light_state.getChannels(channels); - SendLocalDeviceGroupMessage(DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_LIGHT_CHANNELS, channels); + if (force || memcmp(last_channels, channels, LST_MAX)) { + memcpy(last_channels, channels, LST_MAX); + SendLocalDeviceGroupMessage((send_bri_update ? DGR_MSGTYP_PARTIAL_UPDATE : DGR_MSGTYP_UPDATE), DGR_ITEM_LIGHT_CHANNELS, channels); + } + } + if (send_bri_update) { + last_bri = bri; + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_BRI, light_state.getBri()); } - SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_SCHEME, Settings.light_scheme, - DGR_ITEM_LIGHT_BRI, light_state.getBri()); } -void LightHandleDeviceGroupItem() +void LightHandleDeviceGroupItem(void) { static bool send_state = false; static bool restore_power = false; @@ -2144,6 +2164,7 @@ void LightHandleDeviceGroupItem() case DGR_ITEM_LIGHT_SCHEME: if (Settings.light_scheme != value) { Settings.light_scheme = value; + Light.devgrp_no_channels_out = (value != 0); send_state = true; } break; @@ -2162,6 +2183,7 @@ void LightHandleDeviceGroupItem() light_controller.changeChannels(Light.entry_color); light_controller.changeBri(old_bri); Settings.light_scheme = 0; + Light.devgrp_no_channels_out = false; } else { light_state.setColorMode(LCM_CT); @@ -2188,11 +2210,22 @@ void LightHandleDeviceGroupItem() break; case DGR_ITEM_STATUS: SendLocalDeviceGroupMessage(DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_LIGHT_FADE, Settings.light_fade, - DGR_ITEM_LIGHT_SPEED, Settings.light_speed); - LightSendDeviceGroupStatus(); + DGR_ITEM_LIGHT_SPEED, Settings.light_speed, DGR_ITEM_LIGHT_SCHEME, Settings.light_scheme); + LightSendDeviceGroupStatus(true); break; } } + +void LightUpdateScheme(void) +{ + static uint8_t last_scheme; + + if (Settings.light_scheme != last_scheme) { + last_scheme = Settings.light_scheme; + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_SCHEME, Settings.light_scheme); + } + Light.devgrp_no_channels_out = false; +} #endif // USE_DEVICE_GROUPS /*********************************************************************************************\ @@ -2291,6 +2324,9 @@ void CmndSupportColor(void) } Settings.light_scheme = 0; +#ifdef USE_DEVICE_GROUPS + LightUpdateScheme(); +#endif // USE_DEVICE_GROUPS coldim = true; } else { // Color3, 4, 5 and 6 for (uint32_t i = 0; i < LST_RGB; i++) { @@ -2458,7 +2494,7 @@ void CmndScheme(void) } Settings.light_scheme = XdrvMailbox.payload; #ifdef USE_DEVICE_GROUPS - SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_SCHEME, Settings.light_scheme); + LightUpdateScheme(); #endif // USE_DEVICE_GROUPS if (LS_WAKEUP == Settings.light_scheme) { Light.wakeup_active = 3; @@ -2483,6 +2519,9 @@ void CmndWakeup(void) } Light.wakeup_active = 3; Settings.light_scheme = LS_WAKEUP; +#ifdef USE_DEVICE_GROUPS + LightUpdateScheme(); +#endif // USE_DEVICE_GROUPS LightPowerOn(); ResponseCmndChar(D_JSON_STARTED); } From f1ed412acb74b1e34f2b16afb3cd2b3bbc0e0e95 Mon Sep 17 00:00:00 2001 From: Khoa Ton Date: Sat, 4 Apr 2020 02:01:38 -0700 Subject: [PATCH 054/547] Better support for LCD 2004A 20x4 #8062 --- tasmota/my_user_config.h | 2 ++ tasmota/settings.ino | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 70504293c..2b1ef9378 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -502,6 +502,8 @@ // #define USE_DISPLAY // Add I2C Display Support (+2k code) #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 #define USE_DISPLAY_LCD // [DisplayModel 1] [I2cDriver3] Enable Lcd display (I2C addresses 0x27 and 0x3F) (+6k code) + #define USE_DISPLAY_LCD_ROWS 4 // Rows of display + #define USE_DISPLAY_LCD_COLS 20 // Columns of display #define USE_DISPLAY_SSD1306 // [DisplayModel 2] [I2cDriver4] Enable SSD1306 Oled 128x64 display (I2C addresses 0x3C and 0x3D) (+16k code) #define USE_DISPLAY_MATRIX // [DisplayModel 3] [I2cDriver5] Enable 8x8 Matrix display (I2C adresseses see below) (+11k code) #define MTX_ADDRESS1 0x71 // [DisplayAddress1] I2C address of first 8x8 matrix module diff --git a/tasmota/settings.ino b/tasmota/settings.ino index 3bb58ff43..7cd6bcc58 100644 --- a/tasmota/settings.ino +++ b/tasmota/settings.ino @@ -1087,9 +1087,18 @@ void SettingsDefaultSet2(void) // Settings.display_model = 0; Settings.display_mode = 1; Settings.display_refresh = 2; + #ifdef USE_DISPLAY_LCD_ROWS + Settings.display_rows = USE_DISPLAY_LCD_ROWS; + #else Settings.display_rows = 2; + #endif + #ifdef USE_DISPLAY_LCD_COLS + Settings.display_cols[0] = USE_DISPLAY_LCD_COLS; + Settings.display_cols[1] = USE_DISPLAY_LCD_COLS/2; + #else Settings.display_cols[0] = 16; Settings.display_cols[1] = 8; + #endif Settings.display_dimmer = 1; Settings.display_size = 1; Settings.display_font = 1; From 60e45bc7aad2c2cdc413e547310c47e566d370d2 Mon Sep 17 00:00:00 2001 From: Khoa Ton Date: Sat, 4 Apr 2020 02:11:30 -0700 Subject: [PATCH 055/547] Pushing for travis build --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 5e5885f65..d4e3eeed4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,3 +44,5 @@ script: - platformio run -e $ENV before_deploy: - for file in .pioenvs/*/firmware.bin; do cp $file ${file%/*}.bin; done + + \ No newline at end of file From cca5b8e35b9b5f2cc06ff539891fb7c9234f7773 Mon Sep 17 00:00:00 2001 From: Paul C Diem Date: Sat, 4 Apr 2020 10:27:40 -0500 Subject: [PATCH 056/547] Call XdrvCall even for power. Fix devgrp index mask in xdrv_04_light --- tasmota/support_device_groups.ino | 4 +--- tasmota/xdrv_04_light.ino | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tasmota/support_device_groups.ino b/tasmota/support_device_groups.ino index f88bc5c93..4e8b48041 100644 --- a/tasmota/support_device_groups.ino +++ b/tasmota/support_device_groups.ino @@ -601,9 +601,7 @@ void ProcessDeviceGroupMessage(char * packet, int packet_length) } } } - else { - XdrvCall(FUNC_DEVICE_GROUP_ITEM); - } + XdrvCall(FUNC_DEVICE_GROUP_ITEM); } } diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index a3f7a6be5..bcbfba042 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -2115,7 +2115,7 @@ void LightHandleDeviceGroupItem() bool more_to_come; uint32_t value = XdrvMailbox.payload; #ifdef USE_PWM_DIMMER_REMOTE - if (XdrvMailbox.index & 0xff00) return; // Ignore updates from other device groups + if (XdrvMailbox.index & 0xff0000) return; // Ignore updates from other device groups #endif // USE_PWM_DIMMER_REMOTE switch (XdrvMailbox.command_code) { case DGR_ITEM_EOL: From b33fa68c0101c79f3d06b70903c72573a8f03323 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 5 Apr 2020 11:52:02 +0200 Subject: [PATCH 057/547] Fix BH1750 MT lux calculation Fix BH1750 MT lux calculation (#8057) --- tasmota/xsns_10_bh1750.ino | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tasmota/xsns_10_bh1750.ino b/tasmota/xsns_10_bh1750.ino index 1ba00b46e..a098166df 100644 --- a/tasmota/xsns_10_bh1750.ino +++ b/tasmota/xsns_10_bh1750.ino @@ -76,11 +76,12 @@ bool Bh1750Read(void) if (Bh1750.valid) { Bh1750.valid--; } if (2 != Wire.requestFrom(Bh1750.address, (uint8_t)2)) { return false; } - Bh1750.illuminance = (Wire.read() << 8) | Wire.read(); - Bh1750.illuminance /= (1.2 * (69 / Bh1750.mtreg)); + float illuminance = (Wire.read() << 8) | Wire.read(); + illuminance /= (1.2 * (69 / (float)Bh1750.mtreg)); if (1 == Settings.SensorBits1.bh1750_resolution) { - Bh1750.illuminance >>= 1; + illuminance /= 2; } + Bh1750.illuminance = illuminance; Bh1750.valid = SENSOR_MAX_MISS; return true; From 17c605ac6a1d26056db02d3af85d2b60c9b793b1 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 5 Apr 2020 14:11:49 +0200 Subject: [PATCH 058/547] Make checkbox and radiobox label clickable Make checkbox and radio label clickable (#8066) --- tasmota/xdrv_01_webserver.ino | 12 ++++++------ tasmota/xdrv_02_mqtt.ino | 2 +- tasmota/xdrv_09_timers.ino | 18 +++++++++++------- tasmota/xdrv_10_scripter.ino | 2 +- tasmota/xdrv_11_knx.ino | 6 +++--- tasmota/xdrv_28_pcf8574.ino | 2 +- 6 files changed, 23 insertions(+), 19 deletions(-) diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 3521c0adb..1a45434a3 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -413,7 +413,7 @@ const char HTTP_FORM_TEMPLATE[] PROGMEM = const char HTTP_FORM_TEMPLATE_FLAG[] PROGMEM = "

" // Keep close so do not use
"
 " D_TEMPLATE_FLAGS " 

" -// "" D_OPTION_TEXT "
" +// "
" "

"; const char HTTP_FORM_MODULE[] PROGMEM = @@ -426,9 +426,9 @@ const char HTTP_FORM_WIFI[] PROGMEM = "
 " D_WIFI_PARAMETERS " " "
" "

" D_AP1_SSID " (" STA_SSID1 ")

" - "

" D_AP1_PASSWORD "

" + "


" "

" D_AP2_SSID " (" STA_SSID2 ")

" - "

" D_AP2_PASSWORD "

" + "


" "

" D_HOSTNAME " (%s)

" "

" D_CORS_DOMAIN "

"; @@ -446,12 +446,12 @@ const char HTTP_FORM_OTHER[] PROGMEM = "

" "
 " D_TEMPLATE " " "

" - "

" D_ACTIVATE "

" + "

" "
" "
" - "" D_WEB_ADMIN_PASSWORD "

" + "

" "
" - "" D_MQTT_ENABLE "
" + "
" "
"; const char HTTP_FORM_END[] PROGMEM = diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index 912e7c929..7c8f0b29a 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -1242,7 +1242,7 @@ const char HTTP_FORM_MQTT1[] PROGMEM = "

" D_CLIENT " (%s)

"; const char HTTP_FORM_MQTT2[] PROGMEM = "

" D_USER " (" MQTT_USER ")

" - "

" D_PASSWORD "

" + "


" "

" D_TOPIC " = %%topic%% (%s)

" "

" D_FULL_TOPIC " (%s)

"; diff --git a/tasmota/xdrv_09_timers.ino b/tasmota/xdrv_09_timers.ino index 0804ce286..f751d853f 100644 --- a/tasmota/xdrv_09_timers.ino +++ b/tasmota/xdrv_09_timers.ino @@ -648,7 +648,11 @@ const char HTTP_TIMER_SCRIPT6[] PROGMEM = "o=qs('#mw');for(i=0;i<=15;i++){ce((i<10)?('0'+i):i,o);}" // Create window minutes select options "o=qs('#d1');for(i=0;i<%d;i++){ce(i+1,o);}" // Create outputs "var a='" D_DAY3LIST "';" - "s='';for(i=0;i<7;i++){s+=\"\"+a.substring(i*3,(i*3)+3)+\" \"}" + +// "s='';for(i=0;i<7;i++){s+=\"\"+a.substring(i*3,(i*3)+3)+\" \"}" +// "s='';for(i=0;i<7;i++){s+=\" \"}" + "s='';for(i=0;i<7;i++){s+=\" \"}" + "eb('ds').innerHTML=s;" // Create weekdays "eb('dP').click();" // Get the element with id='dP' and click on it "}" @@ -659,22 +663,22 @@ const char HTTP_FORM_TIMER1[] PROGMEM = "
" " " D_TIMER_PARAMETERS " " "" - "
" D_TIMER_ENABLE "


" + "



" "



" "

" "
" - "" D_TIMER_ARM " " - "" D_TIMER_REPEAT "" + " " + "" "

" "
"; #ifdef USE_SUNRISE const char HTTP_FORM_TIMER3[] PROGMEM = "
" - "" D_TIMER_TIME "
" - "" D_SUNRISE " (%s)
" - "" D_SUNSET " (%s)
" + "
" + "
" + "
" "
" "

" "" diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index 330512c9d..6f43229a9 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -3106,7 +3106,7 @@ const char HTTP_FORM_SCRIPT[] PROGMEM = const char HTTP_FORM_SCRIPT1[] PROGMEM = "
" - "" D_SCRIPT_ENABLE "
" + "
" "


" + "" + "
" + + ""; + +const char HTTP_TABLE100[] PROGMEM = + ""; + +const char HTTP_COUNTER[] PROGMEM = + "
"; + +const char HTTP_END[] PROGMEM = + "" + "" + "" + ""; + +const char HTTP_DEVICE_CONTROL[] PROGMEM = ""; +const char HTTP_DEVICE_STATE[] PROGMEM = ""; + +enum ButtonTitle { + BUTTON_RESTART, BUTTON_RESET_CONFIGURATION, + BUTTON_MAIN, BUTTON_CONFIGURATION, BUTTON_INFORMATION, BUTTON_FIRMWARE_UPGRADE, BUTTON_CONSOLE, + BUTTON_MODULE, BUTTON_WIFI, BUTTON_LOGGING, BUTTON_OTHER, BUTTON_TEMPLATE, BUTTON_BACKUP, BUTTON_RESTORE }; +const char kButtonTitle[] PROGMEM = + D_RESTART "|" D_RESET_CONFIGURATION "|" + D_MAIN_MENU "|" D_CONFIGURATION "|" D_INFORMATION "|" D_FIRMWARE_UPGRADE "|" D_CONSOLE "|" + D_CONFIGURE_MODULE "|" D_CONFIGURE_WIFI"|" D_CONFIGURE_LOGGING "|" D_CONFIGURE_OTHER "|" D_CONFIGURE_TEMPLATE "|" D_BACKUP_CONFIGURATION "|" D_RESTORE_CONFIGURATION; +const char kButtonAction[] PROGMEM = + ".|rt|" + ".|cn|in|up|cs|" + "md|wi|lg|co|tp|dl|rs"; +const char kButtonConfirm[] PROGMEM = D_CONFIRM_RESTART "|" D_CONFIRM_RESET_CONFIGURATION; + +enum CTypes { CT_HTML, CT_PLAIN, CT_XML, CT_JSON, CT_STREAM }; +const char kContentTypes[] PROGMEM = "text/html|text/plain|text/xml|application/json|application/octet-stream"; + +const char kLoggingOptions[] PROGMEM = D_SERIAL_LOG_LEVEL "|" D_WEB_LOG_LEVEL "|" D_MQTT_LOG_LEVEL "|" D_SYS_LOG_LEVEL; +const char kLoggingLevels[] PROGMEM = D_NONE "|" D_ERROR "|" D_INFO "|" D_DEBUG "|" D_MORE_DEBUG; + +const char kEmulationOptions[] PROGMEM = D_NONE "|" D_BELKIN_WEMO "|" D_HUE_BRIDGE; + +const char kUploadErrors[] PROGMEM = + D_UPLOAD_ERR_1 "|" D_UPLOAD_ERR_2 "|" D_UPLOAD_ERR_3 "|" D_UPLOAD_ERR_4 "|" D_UPLOAD_ERR_5 "|" D_UPLOAD_ERR_6 "|" D_UPLOAD_ERR_7 "|" D_UPLOAD_ERR_8 "|" D_UPLOAD_ERR_9 +#ifdef USE_RF_FLASH + "|" D_UPLOAD_ERR_10 "|" D_UPLOAD_ERR_11 "|" D_UPLOAD_ERR_12 "|" D_UPLOAD_ERR_13 +#endif + "|" D_UPLOAD_ERR_14 + ; + +const uint16_t DNS_PORT = 53; +enum HttpOptions {HTTP_OFF, HTTP_USER, HTTP_ADMIN, HTTP_MANAGER, HTTP_MANAGER_RESET_ONLY}; + +DNSServer *DnsServer; +ESP8266WebServer *Webserver; + +struct WEB { + String chunk_buffer = ""; + bool reset_web_log_flag = false; + uint8_t state = HTTP_OFF; + uint8_t upload_error = 0; + uint8_t upload_file_type; + uint8_t upload_progress_dot_count; + uint8_t config_block_count = 0; + uint8_t config_xor_on = 0; + uint8_t config_xor_on_set = CONFIG_FILE_XOR; +} Web; + + +static void WebGetArg(const char* arg, char* out, size_t max) +{ + String s = Webserver->arg(arg); + strlcpy(out, s.c_str(), max); + +} + +static bool WifiIsInManagerMode(){ + return (HTTP_MANAGER == Web.state || HTTP_MANAGER_RESET_ONLY == Web.state); +} + +void ShowWebSource(uint32_t source) +{ + if ((source > 0) && (source < SRC_MAX)) { + char stemp1[20]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SRC: %s from %s"), GetTextIndexed(stemp1, sizeof(stemp1), source, kCommandSource), Webserver->client().remoteIP().toString().c_str()); + } +} + +void ExecuteWebCommand(char* svalue, uint32_t source) +{ + ShowWebSource(source); + last_source = source; + ExecuteCommand(svalue, SRC_IGNORE); +} + +void StartWebserver(int type, IPAddress ipweb) +{ + if (!Settings.web_refresh) { Settings.web_refresh = HTTP_REFRESH_TIME; } + if (!Web.state) { + if (!Webserver) { + Webserver = new ESP8266WebServer((HTTP_MANAGER == type || HTTP_MANAGER_RESET_ONLY == type) ? 80 : WEB_PORT); + Webserver->on("/", HandleRoot); + Webserver->onNotFound(HandleNotFound); + Webserver->on("/up", HandleUpgradeFirmware); + Webserver->on("/u1", HandleUpgradeFirmwareStart); + Webserver->on("/u2", HTTP_POST, HandleUploadDone, HandleUploadLoop); + Webserver->on("/u2", HTTP_OPTIONS, HandlePreflightRequest); + Webserver->on("/cs", HTTP_GET, HandleConsole); + Webserver->on("/cs", HTTP_OPTIONS, HandlePreflightRequest); + Webserver->on("/cm", HandleHttpCommand); +#ifndef FIRMWARE_MINIMAL + Webserver->on("/cn", HandleConfiguration); + Webserver->on("/md", HandleModuleConfiguration); + Webserver->on("/wi", HandleWifiConfiguration); + Webserver->on("/lg", HandleLoggingConfiguration); + Webserver->on("/tp", HandleTemplateConfiguration); + Webserver->on("/co", HandleOtherConfiguration); + Webserver->on("/dl", HandleBackupConfiguration); + Webserver->on("/rs", HandleRestoreConfiguration); + Webserver->on("/rt", HandleResetConfiguration); + Webserver->on("/in", HandleInformation); + XdrvCall(FUNC_WEB_ADD_HANDLER); + XsnsCall(FUNC_WEB_ADD_HANDLER); +#endif + } + Web.reset_web_log_flag = false; + + + + Webserver->collectHeaders(HEADER_KEYS, sizeof(HEADER_KEYS)/sizeof(char*)); + + Webserver->begin(); + } + if (Web.state != type) { +#if LWIP_IPV6 + String ipv6_addr = WifiGetIPv6(); + if(ipv6_addr!="") AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s and IPv6 global address %s "), my_hostname, (Wifi.mdns_begun) ? ".local" : "", ipweb.toString().c_str(),ipv6_addr.c_str()); + else AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s"), my_hostname, (Wifi.mdns_begun) ? ".local" : "", ipweb.toString().c_str()); +#else + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s"), my_hostname, (Wifi.mdns_begun) ? ".local" : "", ipweb.toString().c_str()); +#endif + rules_flag.http_init = 1; + } + if (type) { Web.state = type; } +} + +void StopWebserver(void) +{ + if (Web.state) { + Webserver->close(); + Web.state = HTTP_OFF; + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_STOPPED)); + } +} + +void WifiManagerBegin(bool reset_only) +{ + + if (!global_state.wifi_down) { + + WifiSetMode(WIFI_AP_STA); + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION)); + } else { + + WifiSetMode(WIFI_AP); + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_WIFIMANAGER_SET_ACCESSPOINT)); + } + + StopWebserver(); + + DnsServer = new DNSServer(); + + int channel = WIFI_SOFT_AP_CHANNEL; + if ((channel < 1) || (channel > 13)) { channel = 1; } + +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 + + WiFi.softAP(my_hostname, WIFI_AP_PASSPHRASE, channel, 0); +#else + + WiFi.softAP(my_hostname, WIFI_AP_PASSPHRASE, channel, 0, 1); +#endif + + delay(500); + + DnsServer->setErrorReplyCode(DNSReplyCode::NoError); + DnsServer->start(DNS_PORT, "*", WiFi.softAPIP()); + + StartWebserver((reset_only ? HTTP_MANAGER_RESET_ONLY : HTTP_MANAGER), WiFi.softAPIP()); +} + +void PollDnsWebserver(void) +{ + if (DnsServer) { DnsServer->processNextRequest(); } + if (Webserver) { Webserver->handleClient(); } +} + + + +bool WebAuthenticate(void) +{ + if (strlen(SettingsText(SET_WEBPWD)) && (HTTP_MANAGER_RESET_ONLY != Web.state)) { + return Webserver->authenticate(WEB_USERNAME, SettingsText(SET_WEBPWD)); + } else { + return true; + } +} + +bool HttpCheckPriviledgedAccess(bool autorequestauth = true) +{ + if (HTTP_USER == Web.state) { + HandleRoot(); + return false; + } + if (autorequestauth && !WebAuthenticate()) { + Webserver->requestAuthentication(); + return false; + } + return true; +} + +void HttpHeaderCors(void) +{ + if (strlen(SettingsText(SET_CORS))) { + Webserver->sendHeader(F("Access-Control-Allow-Origin"), SettingsText(SET_CORS)); + } +} + +void WSHeaderSend(void) +{ + Webserver->sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate")); + Webserver->sendHeader(F("Pragma"), F("no-cache")); + Webserver->sendHeader(F("Expires"), F("-1")); + HttpHeaderCors(); +} + + + + + +void WSSend(int code, int ctype, const String& content) +{ + char ct[25]; + Webserver->send(code, GetTextIndexed(ct, sizeof(ct), ctype, kContentTypes), content); +} + + + + + +void WSContentBegin(int code, int ctype) +{ + Webserver->client().flush(); + WSHeaderSend(); +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 + Webserver->sendHeader(F("Accept-Ranges"),F("none")); + Webserver->sendHeader(F("Transfer-Encoding"),F("chunked")); +#endif + Webserver->setContentLength(CONTENT_LENGTH_UNKNOWN); + WSSend(code, ctype, ""); + Web.chunk_buffer = ""; +} + +void _WSContentSend(const String& content) +{ + size_t len = content.length(); + +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 + const char * footer = "\r\n"; + char chunk_size[11]; + sprintf(chunk_size, "%x\r\n", len); + Webserver->sendContent(String() + chunk_size + content + footer); +#else + Webserver->sendContent(content); +#endif + +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("WSContentSend")); +#endif + DEBUG_CORE_LOG(PSTR("WEB: Chunk size %d/%d"), len, sizeof(mqtt_data)); +} + +void WSContentFlush(void) +{ + if (Web.chunk_buffer.length() > 0) { + _WSContentSend(Web.chunk_buffer); + Web.chunk_buffer = ""; + } +} + +void _WSContentSendBuffer(void) +{ + int len = strlen(mqtt_data); + + if (0 == len) { + return; + } + else if (len == sizeof(mqtt_data)) { + AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: Content too large")); + } + else if (len < CHUNKED_BUFFER_SIZE) { + Web.chunk_buffer += mqtt_data; + len = Web.chunk_buffer.length(); + } + + if (len >= CHUNKED_BUFFER_SIZE) { + WSContentFlush(); + } + if (strlen(mqtt_data) >= CHUNKED_BUFFER_SIZE) { + _WSContentSend(mqtt_data); + } +} + +void WSContentSend_P(const char* formatP, ...) +{ + + va_list arg; + va_start(arg, formatP); + int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), formatP, arg); + va_end(arg); + +#ifdef DEBUG_TASMOTA_CORE + if (len > (sizeof(mqtt_data) -1)) { + mqtt_data[33] = '\0'; + DEBUG_CORE_LOG(PSTR("ERROR: WSContentSend_P size %d > mqtt_data size %d. Start of data [%s...]"), len, sizeof(mqtt_data), mqtt_data); + } +#endif + + _WSContentSendBuffer(); +} + +void WSContentSend_PD(const char* formatP, ...) +{ + + va_list arg; + va_start(arg, formatP); + int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), formatP, arg); + va_end(arg); + +#ifdef DEBUG_TASMOTA_CORE + if (len > (sizeof(mqtt_data) -1)) { + mqtt_data[33] = '\0'; + DEBUG_CORE_LOG(PSTR("ERROR: WSContentSend_PD size %d > mqtt_data size %d. Start of data [%s...]"), len, sizeof(mqtt_data), mqtt_data); + } +#endif + + if (D_DECIMAL_SEPARATOR[0] != '.') { + for (uint32_t i = 0; i < len; i++) { + if ('.' == mqtt_data[i]) { + mqtt_data[i] = D_DECIMAL_SEPARATOR[0]; + } + } + } + + _WSContentSendBuffer(); +} + +void WSContentStart_P(const char* title, bool auth) +{ + if (auth && strlen(SettingsText(SET_WEBPWD)) && !Webserver->authenticate(WEB_USERNAME, SettingsText(SET_WEBPWD))) { + return Webserver->requestAuthentication(); + } + + WSContentBegin(200, CT_HTML); + + if (title != nullptr) { + char ctitle[strlen_P(title) +1]; + strcpy_P(ctitle, title); + WSContentSend_P(HTTP_HEADER1, SettingsText(SET_FRIENDLYNAME1), ctitle); + } +} + +void WSContentStart_P(const char* title) +{ + WSContentStart_P(title, true); +} + +void WSContentSendStyle_P(const char* formatP, ...) +{ + if (WifiIsInManagerMode()) { + if (WifiConfigCounter()) { + WSContentSend_P(HTTP_SCRIPT_COUNTER); + } + } + WSContentSend_P(HTTP_HEAD_LAST_SCRIPT); + + WSContentSend_P(HTTP_HEAD_STYLE1, WebColor(COL_FORM), WebColor(COL_INPUT), WebColor(COL_INPUT_TEXT), WebColor(COL_INPUT), + WebColor(COL_INPUT_TEXT), WebColor(COL_CONSOLE), WebColor(COL_CONSOLE_TEXT), WebColor(COL_BACKGROUND)); + WSContentSend_P(HTTP_HEAD_STYLE2, WebColor(COL_BUTTON), WebColor(COL_BUTTON_TEXT), WebColor(COL_BUTTON_HOVER), + WebColor(COL_BUTTON_RESET), WebColor(COL_BUTTON_RESET_HOVER), WebColor(COL_BUTTON_SAVE), WebColor(COL_BUTTON_SAVE_HOVER), + WebColor(COL_BUTTON)); + if (formatP != nullptr) { + + va_list arg; + va_start(arg, formatP); + int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), formatP, arg); + va_end(arg); + +#ifdef DEBUG_TASMOTA_CORE + if (len > (sizeof(mqtt_data) -1)) { + mqtt_data[33] = '\0'; + DEBUG_CORE_LOG(PSTR("ERROR: WSContentSendStyle_P size %d > mqtt_data size %d. Start of data [%s...]"), len, sizeof(mqtt_data), mqtt_data); + } +#endif + + _WSContentSendBuffer(); + } + WSContentSend_P(HTTP_HEAD_STYLE3, WebColor(COL_TEXT), +#ifdef FIRMWARE_MINIMAL + WebColor(COL_TEXT_WARNING), +#endif + WebColor(COL_TITLE), + ModuleName().c_str(), SettingsText(SET_FRIENDLYNAME1)); + if (Settings.flag3.gui_hostname_ip) { + bool lip = (static_cast(WiFi.localIP()) != 0); + bool sip = (static_cast(WiFi.softAPIP()) != 0); + WSContentSend_P(PSTR("

%s%s (%s%s%s)

"), + my_hostname, + (Wifi.mdns_begun) ? ".local" : "", + (lip) ? WiFi.localIP().toString().c_str() : "", + (lip && sip) ? ", " : "", + (sip) ? WiFi.softAPIP().toString().c_str() : ""); + } + WSContentSend_P(PSTR("")); +} + +void WSContentSendStyle(void) +{ + WSContentSendStyle_P(nullptr); +} + +void WSContentButton(uint32_t title_index) +{ + char action[4]; + char title[100]; + + if (title_index <= BUTTON_RESET_CONFIGURATION) { + char confirm[100]; + WSContentSend_P(PSTR("

"), + GetTextIndexed(action, sizeof(action), title_index, kButtonAction), + GetTextIndexed(confirm, sizeof(confirm), title_index, kButtonConfirm), + (!title_index) ? "rst" : "non", + GetTextIndexed(title, sizeof(title), title_index, kButtonTitle)); + } else { + WSContentSend_P(PSTR("

"), + GetTextIndexed(action, sizeof(action), title_index, kButtonAction), + GetTextIndexed(title, sizeof(title), title_index, kButtonTitle)); + } +} + +void WSContentSpaceButton(uint32_t title_index) +{ + WSContentSend_P(PSTR("
")); + WSContentButton(title_index); +} + +void WSContentSend_THD(const char *types, float f_temperature, float f_humidity) +{ + char parameter[FLOATSZ]; + dtostrfd(f_temperature, Settings.flag2.temperature_resolution, parameter); + WSContentSend_PD(HTTP_SNS_TEMP, types, parameter, TempUnit()); + dtostrfd(f_humidity, Settings.flag2.humidity_resolution, parameter); + WSContentSend_PD(HTTP_SNS_HUM, types, parameter); + dtostrfd(CalcTempHumToDew(f_temperature, f_humidity), Settings.flag2.temperature_resolution, parameter); + WSContentSend_PD(HTTP_SNS_DEW, types, parameter, TempUnit()); +} + +void WSContentEnd(void) +{ + WSContentFlush(); + _WSContentSend(""); + Webserver->client().stop(); +} + +void WSContentStop(void) +{ + if (WifiIsInManagerMode()) { + if (WifiConfigCounter()) { + WSContentSend_P(HTTP_COUNTER); + } + } + WSContentSend_P(HTTP_END, my_version); + WSContentEnd(); +} + + + +void WebRestart(uint32_t type) +{ + + + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTART); + + bool reset_only = (HTTP_MANAGER_RESET_ONLY == Web.state); + + WSContentStart_P((type) ? S_SAVE_CONFIGURATION : S_RESTART, !reset_only); + WSContentSend_P(HTTP_SCRIPT_RELOAD); + WSContentSendStyle(); + if (type) { + WSContentSend_P(PSTR("
" D_CONFIGURATION_SAVED "
")); + if (2 == type) { + WSContentSend_P(PSTR("
" D_TRYING_TO_CONNECT "
")); + } + WSContentSend_P(PSTR("
")); + } + WSContentSend_P(HTTP_MSG_RSTRT); + if (HTTP_MANAGER == Web.state || reset_only) { + Web.state = HTTP_ADMIN; + } else { + WSContentSpaceButton(BUTTON_MAIN); + } + WSContentStop(); + + ShowWebSource(SRC_WEBGUI); + restart_flag = 2; +} + + + +void HandleWifiLogin(void) +{ + WSContentStart_P(S_CONFIGURE_WIFI, false); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_LOGIN); + + if (HTTP_MANAGER_RESET_ONLY == Web.state) { + WSContentSpaceButton(BUTTON_RESTART); +#ifndef FIRMWARE_MINIMAL + WSContentSpaceButton(BUTTON_RESET_CONFIGURATION); +#endif + } + + WSContentStop(); +} + +#ifdef USE_LIGHT +void WebSliderColdWarm(void) +{ + WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, + "a", + "#fff", "#ff0", + 1, + 153, 500, + LightGetColorTemp(), + 't', 0); +} +#endif + +void HandleRoot(void) +{ + if (CaptivePortal()) { return; } + + if (Webserver->hasArg("rst")) { + WebRestart(0); + return; + } + + if (WifiIsInManagerMode()) { +#ifndef FIRMWARE_MINIMAL + if (strlen(SettingsText(SET_WEBPWD)) && !(Webserver->hasArg("USER1")) && !(Webserver->hasArg("PASS1")) && HTTP_MANAGER_RESET_ONLY != Web.state) { + HandleWifiLogin(); + } else { + if (!strlen(SettingsText(SET_WEBPWD)) || (((Webserver->arg("USER1") == WEB_USERNAME ) && (Webserver->arg("PASS1") == SettingsText(SET_WEBPWD) )) || HTTP_MANAGER_RESET_ONLY == Web.state)) { + HandleWifiConfiguration(); + } else { + + HandleWifiLogin(); + } + } +#endif + return; + } + + if (HandleRootStatusRefresh()) { + return; + } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_MAIN_MENU); + + char stemp[33]; + + WSContentStart_P(S_MAIN_MENU); +#ifdef USE_SCRIPT_WEB_DISPLAY + WSContentSend_P(HTTP_SCRIPT_ROOT, Settings.web_refresh, Settings.web_refresh); +#else + WSContentSend_P(HTTP_SCRIPT_ROOT, Settings.web_refresh); +#endif + WSContentSend_P(HTTP_SCRIPT_ROOT_PART2); + + WSContentSendStyle(); + + WSContentSend_P(PSTR("
")); + if (devices_present) { +#ifdef USE_LIGHT + if (light_type) { + uint8_t light_subtype = light_type &7; + if (!Settings.flag3.pwm_multi_channels) { + bool split_white = ((LST_RGBW <= light_subtype) && (devices_present > 1)); + + if ((LST_COLDWARM == light_subtype) || ((LST_RGBCW == light_subtype) && !split_white)) { + WebSliderColdWarm(); + } + + if (light_subtype > 2) { + uint16_t hue; + uint8_t sat; + LightGetHSB(&hue, &sat, nullptr); + + WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, + "b", + "#800", "#f00 5%,#ff0 20%,#0f0 35%,#0ff 50%,#00f 65%,#f0f 80%,#f00 95%,#800", + 2, + 1, 359, + hue, + 'h', 0); + + uint8_t dcolor = changeUIntScale(Settings.light_dimmer, 0, 100, 0, 255); + char scolor[8]; + snprintf_P(scolor, sizeof(scolor), PSTR("#%02X%02X%02X"), dcolor, dcolor, dcolor); + uint8_t red, green, blue; + LightHsToRgb(hue, 255, &red, &green, &blue); + snprintf_P(stemp, sizeof(stemp), PSTR("#%02X%02X%02X"), red, green, blue); + + WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, + "s", + scolor, stemp, + 3, + 0, 100, + changeUIntScale(sat, 0, 255, 0, 100), + 'n', 0); + } + + WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, + "c", + "#000", "#fff", + 4, + Settings.flag3.slider_dimmer_stay_on, 100, + Settings.light_dimmer, + 'd', 0); + + if (split_white) { + if (LST_RGBCW == light_subtype) { + WebSliderColdWarm(); + } + WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, + "f", + "#000", "#fff", + 5, + Settings.flag3.slider_dimmer_stay_on, 100, + LightGetDimmer(2), + 'w', 0); + } + } else { + uint32_t pwm_channels = light_subtype > LST_MAX ? LST_MAX : light_subtype; + stemp[0] = 'e'; stemp[1] = '0'; stemp[2] = '\0'; + for (uint32_t i = 0; i < pwm_channels; i++) { + stemp[1]++; + + WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, + stemp, + "#000", "#fff", + i+1, + 1, 100, + changeUIntScale(Settings.light_color[i], 0, 255, 0, 100), + 'e', i+1); + } + } + } +#endif +#ifdef USE_SHUTTER + if (Settings.flag3.shutter_mode) { + for (uint32_t i = 0; i < shutters_present; i++) { + WSContentSend_P(HTTP_MSG_SLIDER_SHUTTER, Settings.shutter_position[i], i+1); + } + } +#endif + WSContentSend_P(HTTP_TABLE100); + WSContentSend_P(PSTR("
")); +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { + WSContentSend_P(HTTP_DEVICE_CONTROL, 36, 1, + (strlen(SettingsText(SET_BUTTON1))) ? SettingsText(SET_BUTTON1) : D_BUTTON_TOGGLE, + ""); + for (uint32_t i = 0; i < MaxFanspeed(); i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i); + WSContentSend_P(HTTP_DEVICE_CONTROL, 16, i +2, + (strlen(SettingsText(SET_BUTTON2 + i))) ? SettingsText(SET_BUTTON2 + i) : stemp, + ""); + } + } else { +#endif + for (uint32_t idx = 1; idx <= devices_present; idx++) { + bool set_button = ((idx <= MAX_BUTTON_TEXT) && strlen(SettingsText(SET_BUTTON1 + idx -1))); +#ifdef USE_SHUTTER + int32_t ShutterWebButton; + if (ShutterWebButton = IsShutterWebButton(idx)) { + WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, + (set_button) ? SettingsText(SET_BUTTON1 + idx -1) : ((Settings.shutter_options[abs(ShutterWebButton)-1] & 2) ? "-" : ((Settings.shutter_options[abs(ShutterWebButton)-1] & 8) ? ((ShutterWebButton>0) ? "▼" : "▲") : ((ShutterWebButton>0) ? "▲" : "▼"))), + ""); + continue; + } +#endif + snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), idx); + WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, + (set_button) ? SettingsText(SET_BUTTON1 + idx -1) : (devices_present < 5) ? D_BUTTON_TOGGLE : "", + (set_button) ? "" : (devices_present > 1) ? stemp : ""); + } +#ifdef USE_SONOFF_IFAN + } +#endif + WSContentSend_P(PSTR("
%s
")); + } +#ifdef USE_SONOFF_RF + if (SONOFF_BRIDGE == my_module_type) { + WSContentSend_P(HTTP_TABLE100); + WSContentSend_P(PSTR("")); + uint32_t idx = 0; + for (uint32_t i = 0; i < 4; i++) { + if (idx > 0) { WSContentSend_P(PSTR("")); } + for (uint32_t j = 0; j < 4; j++) { + idx++; + snprintf_P(stemp, sizeof(stemp), PSTR("%d"), idx); + WSContentSend_P(PSTR(""), idx, + (strlen(SettingsText(SET_BUTTON1 + idx -1))) ? SettingsText(SET_BUTTON1 + idx -1) : stemp); + } + } + WSContentSend_P(PSTR("")); + } +#endif + +#ifndef FIRMWARE_MINIMAL + XdrvCall(FUNC_WEB_ADD_MAIN_BUTTON); + XsnsCall(FUNC_WEB_ADD_MAIN_BUTTON); +#endif + + if (HTTP_ADMIN == Web.state) { +#ifdef FIRMWARE_MINIMAL + WSContentSpaceButton(BUTTON_FIRMWARE_UPGRADE); +#else + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentButton(BUTTON_INFORMATION); + WSContentButton(BUTTON_FIRMWARE_UPGRADE); +#endif + WSContentButton(BUTTON_CONSOLE); + WSContentButton(BUTTON_RESTART); + } + WSContentStop(); +} + +bool HandleRootStatusRefresh(void) +{ + if (!WebAuthenticate()) { + Webserver->requestAuthentication(); + return true; + } + + if (!Webserver->hasArg("m")) { + return false; + } + + #ifdef USE_SCRIPT_WEB_DISPLAY + Script_Check_HTML_Setvars(); + #endif + + char tmp[8]; + char svalue[32]; + char webindex[5]; + + WebGetArg("o", tmp, sizeof(tmp)); + if (strlen(tmp)) { + ShowWebSource(SRC_WEBGUI); + uint32_t device = atoi(tmp); +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { + if (device < 2) { + ExecuteCommandPower(1, POWER_TOGGLE, SRC_IGNORE); + } else { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_FANSPEED " %d"), device -2); + ExecuteCommand(svalue, SRC_WEBGUI); + } + } else { +#endif +#ifdef USE_SHUTTER + int32_t ShutterWebButton; + if (ShutterWebButton = IsShutterWebButton(device)) { + snprintf_P(svalue, sizeof(svalue), PSTR("ShutterPosition%d %s"), abs(ShutterWebButton), (ShutterWebButton>0) ? PSTR(D_CMND_SHUTTER_STOPOPEN) : PSTR(D_CMND_SHUTTER_STOPCLOSE)); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } else { +#endif + ExecuteCommandPower(device, POWER_TOGGLE, SRC_IGNORE); +#ifdef USE_SHUTTER + } +#endif +#ifdef USE_SONOFF_IFAN + } +#endif + } +#ifdef USE_LIGHT + WebGetArg("d0", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_DIMMER " %s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + WebGetArg("w0", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_WHITE " %s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + uint32_t light_device = LightDevice(); + uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); + for (uint32_t j = 0; j < pwm_channels; j++) { + snprintf_P(webindex, sizeof(webindex), PSTR("e%d"), j +1); + WebGetArg(webindex, tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_CHANNEL "%d %s"), j +light_device, tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + } + WebGetArg("t0", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_COLORTEMPERATURE " %s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + WebGetArg("h0", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_HSBCOLOR "1 %s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + WebGetArg("n0", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_HSBCOLOR "2 %s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } +#endif +#ifdef USE_SHUTTER + for (uint32_t j = 1; j <= shutters_present; j++) { + snprintf_P(webindex, sizeof(webindex), PSTR("u%d"), j); + WebGetArg(webindex, tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR("ShutterPosition%d %s"), j, tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + } +#endif +#ifdef USE_SONOFF_RF + WebGetArg("k", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_RFKEY "%s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } +#endif + WSContentBegin(200, CT_HTML); + WSContentSend_P(PSTR("{t}")); + XsnsCall(FUNC_WEB_SENSOR); +#ifdef USE_SCRIPT_WEB_DISPLAY + XdrvCall(FUNC_WEB_SENSOR); +#endif + + WSContentSend_P(PSTR("")); + + if (devices_present) { + WSContentSend_P(PSTR("{t}")); + uint32_t fsize = (devices_present < 5) ? 70 - (devices_present * 8) : 32; +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { + WSContentSend_P(HTTP_DEVICE_STATE, 36, (bitRead(power, 0)) ? "bold" : "normal", 54, GetStateText(bitRead(power, 0))); + uint32_t fanspeed = GetFanspeed(); + snprintf_P(svalue, sizeof(svalue), PSTR("%d"), fanspeed); + WSContentSend_P(HTTP_DEVICE_STATE, 64, (fanspeed) ? "bold" : "normal", 54, (fanspeed) ? svalue : GetStateText(0)); + } else { +#endif + for (uint32_t idx = 1; idx <= devices_present; idx++) { + snprintf_P(svalue, sizeof(svalue), PSTR("%d"), bitRead(power, idx -1)); + WSContentSend_P(HTTP_DEVICE_STATE, 100 / devices_present, (bitRead(power, idx -1)) ? "bold" : "normal", fsize, (devices_present < 5) ? GetStateText(bitRead(power, idx -1)) : svalue); + } +#ifdef USE_SONOFF_IFAN + } +#endif + WSContentSend_P(PSTR("")); + } + WSContentEnd(); + + return true; +} + +#ifdef USE_SHUTTER +int32_t IsShutterWebButton(uint32_t idx) { + + int32_t ShutterWebButton = 0; + if (Settings.flag3.shutter_mode) { + for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { + if (Settings.shutter_startrelay[i] && ((Settings.shutter_startrelay[i] == idx) || (Settings.shutter_startrelay[i] == (idx-1)))) { + ShutterWebButton = (Settings.shutter_startrelay[i] == idx) ? (i+1): (-1-i); + break; + } + } + } + return ShutterWebButton; +} +#endif + + + +#ifndef FIRMWARE_MINIMAL + +void HandleConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURATION); + + WSContentStart_P(S_CONFIGURATION); + WSContentSendStyle(); + + WSContentButton(BUTTON_MODULE); + WSContentButton(BUTTON_WIFI); + + XdrvCall(FUNC_WEB_ADD_BUTTON); + XsnsCall(FUNC_WEB_ADD_BUTTON); + + WSContentButton(BUTTON_LOGGING); + WSContentButton(BUTTON_OTHER); + WSContentButton(BUTTON_TEMPLATE); + + WSContentSpaceButton(BUTTON_RESET_CONFIGURATION); + WSContentButton(BUTTON_BACKUP); + WSContentButton(BUTTON_RESTORE); + + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); +} + + + +void HandleTemplateConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + if (Webserver->hasArg("save")) { + TemplateSaveSettings(); + WebRestart(1); + return; + } + + char stemp[30]; + + if (Webserver->hasArg("m")) { + WSContentBegin(200, CT_PLAIN); + for (uint32_t i = 0; i < sizeof(kModuleNiceList); i++) { + uint32_t midx = pgm_read_byte(kModuleNiceList + i); + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), midx +1); + } + WSContentEnd(); + return; + } + + WebGetArg("t", stemp, sizeof(stemp)); + if (strlen(stemp)) { + uint32_t module = atoi(stemp); + uint32_t module_save = Settings.module; + Settings.module = module; + myio cmodule; + ModuleGpios(&cmodule); + gpio_flag flag = ModuleFlag(); + Settings.module = module_save; + + WSContentBegin(200, CT_PLAIN); + WSContentSend_P(PSTR("%s}1"), AnyModuleName(module).c_str()); + for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { + if (1 == i) { + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, 255, D_SENSOR_USER, 255); + } + uint32_t midx = pgm_read_byte(kGpioNiceList + i); + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); + } + WSContentSend_P(PSTR("}1")); + + for (uint32_t i = 0; i < ADC0_END; i++) { + if (1 == i) { + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, ADC0_USER, D_SENSOR_USER, ADC0_USER); + } + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, i, GetTextIndexed(stemp, sizeof(stemp), i, kAdc0Names), i); + } + WSContentSend_P(PSTR("}1")); + + for (uint32_t i = 0; i < sizeof(cmodule); i++) { + if (!FlashPin(i)) { + WSContentSend_P(PSTR("%s%d"), (i>0)?",":"", cmodule.io[i]); + } + } + WSContentSend_P(PSTR("}1%d}1%d"), flag, Settings.user_template_base); + WSContentEnd(); + return; + } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_TEMPLATE); + + WSContentStart_P(S_CONFIGURE_TEMPLATE); + WSContentSend_P(HTTP_SCRIPT_MODULE_TEMPLATE); + WSContentSend_P(HTTP_SCRIPT_TEMPLATE); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_TEMPLATE); + WSContentSend_P(HTTP_TABLE100); + WSContentSend_P(PSTR("" D_TEMPLATE_NAME "" + "" D_BASE_TYPE "" + "" + "
")); + WSContentSend_P(HTTP_TABLE100); + for (uint32_t i = 0; i < MAX_GPIO_PIN; i++) { + if (!FlashPin(i)) { + WSContentSend_P(PSTR("" D_GPIO "%d"), + ((9==i)||(10==i)) ? WebColor(COL_TEXT_WARNING) : WebColor(COL_TEXT), i, (0==i) ? " style='width:200px'" : "", i); + } + } +#ifdef ESP8266 + WSContentSend_P(PSTR("" D_ADC "0"), WebColor(COL_TEXT)); +#endif + WSContentSend_P(PSTR("")); + gpio_flag flag = ModuleFlag(); + if (flag.data > ADC0_USER) { + WSContentSend_P(HTTP_FORM_TEMPLATE_FLAG); + } + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void TemplateSaveSettings(void) +{ + char tmp[TOPSZ]; + char webindex[5]; + char svalue[300]; + + WebGetArg("s1", tmp, sizeof(tmp)); + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_TEMPLATE " {\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), tmp); + + uint32_t j = 0; + for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) { +#ifdef ESP8266 + if (6 == i) { j = 9; } + if (8 == i) { j = 12; } +#else + if (6 == i) { j = 12; } +#endif + snprintf_P(webindex, sizeof(webindex), PSTR("g%d"), j); + WebGetArg(webindex, tmp, sizeof(tmp)); + uint8_t gpio = atoi(tmp); + snprintf_P(svalue, sizeof(svalue), PSTR("%s%s%d"), svalue, (i>0)?",":"", gpio); + j++; + } + + WebGetArg("g" STR(ADC0_PIN), tmp, sizeof(tmp)); + uint32_t flag = atoi(tmp); + for (uint32_t i = 0; i < GPIO_FLAG_USED; i++) { + snprintf_P(webindex, sizeof(webindex), PSTR("c%d"), i); + uint32_t state = Webserver->hasArg(webindex) << i +4; + flag += state; + } + WebGetArg("g99", tmp, sizeof(tmp)); + uint32_t base = atoi(tmp) +1; + + snprintf_P(svalue, sizeof(svalue), PSTR("%s],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), svalue, flag, base); + ExecuteWebCommand(svalue, SRC_WEBGUI); +} + + + +void HandleModuleConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + if (Webserver->hasArg("save")) { + ModuleSaveSettings(); + WebRestart(1); + return; + } + + char stemp[30]; + uint32_t midx; + myio cmodule; + ModuleGpios(&cmodule); + + if (Webserver->hasArg("m")) { + WSContentBegin(200, CT_PLAIN); + uint32_t vidx = 0; + for (uint32_t i = 0; i <= sizeof(kModuleNiceList); i++) { + if (0 == i) { + midx = USER_MODULE; + vidx = 0; + } else { + midx = pgm_read_byte(kModuleNiceList + i -1); + vidx = midx +1; + } + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), vidx); + } + WSContentEnd(); + return; + } + + if (Webserver->hasArg("g")) { + WSContentBegin(200, CT_PLAIN); + for (uint32_t j = 0; j < sizeof(kGpioNiceList); j++) { + midx = pgm_read_byte(kGpioNiceList + j); + if (!GetUsedInModule(midx, cmodule.io)) { + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); + } + } + WSContentEnd(); + return; + } + +#ifndef USE_ADC_VCC + if (Webserver->hasArg("a")) { + WSContentBegin(200, CT_PLAIN); + for (uint32_t j = 0; j < ADC0_END; j++) { + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, j, GetTextIndexed(stemp, sizeof(stemp), j, kAdc0Names), j); + } + WSContentEnd(); + return; + } +#endif + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MODULE); + + WSContentStart_P(S_CONFIGURE_MODULE); + WSContentSend_P(HTTP_SCRIPT_MODULE_TEMPLATE); + WSContentSend_P(HTTP_SCRIPT_MODULE1, Settings.module); + for (uint32_t i = 0; i < sizeof(cmodule); i++) { + if (ValidGPIO(i, cmodule.io[i])) { + WSContentSend_P(PSTR("sk(%d,%d);"), my_module.io[i], i); + } + } + WSContentSend_P(HTTP_SCRIPT_MODULE2, Settings.my_adc0); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_MODULE, AnyModuleName(MODULE).c_str()); + for (uint32_t i = 0; i < sizeof(cmodule); i++) { + if (ValidGPIO(i, cmodule.io[i])) { + snprintf_P(stemp, 3, PINS_WEMOS +i*2); +#ifdef ESP8266 + char sesp8285[40]; + snprintf_P(sesp8285, sizeof(sesp8285), PSTR("ESP8285"), WebColor(COL_TEXT_WARNING)); + WSContentSend_P(PSTR("%s " D_GPIO "%d %s"), + (WEMOS==my_module_type)?stemp:"", i, (0==i)? D_SENSOR_BUTTON "1":(1==i)? D_SERIAL_OUT :(3==i)? D_SERIAL_IN :((9==i)||(10==i))? sesp8285 :(12==i)? D_SENSOR_RELAY "1":(13==i)? D_SENSOR_LED "1i":(14==i)? D_SENSOR :"", i); +#else + WSContentSend_P(PSTR("%s " D_GPIO "%d"), + (WEMOS==my_module_type)?stemp:"", i, i); +#endif + } + } +#ifdef ESP8266 +#ifndef USE_ADC_VCC + if (ValidAdc()) { + WSContentSend_P(PSTR("%s " D_ADC "0"), (WEMOS==my_module_type)?"A0":""); + } +#endif +#endif + WSContentSend_P(PSTR("")); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void ModuleSaveSettings(void) +{ + char tmp[8]; + char webindex[5]; + + WebGetArg("g99", tmp, sizeof(tmp)); + uint32_t new_module = (!strlen(tmp)) ? MODULE : atoi(tmp); + Settings.last_module = Settings.module; + Settings.module = new_module; + SetModuleType(); + myio cmodule; + ModuleGpios(&cmodule); + String gpios = ""; + for (uint32_t i = 0; i < sizeof(cmodule); i++) { + if (Settings.last_module != new_module) { + Settings.my_gp.io[i] = GPIO_NONE; + } else { + if (ValidGPIO(i, cmodule.io[i])) { + snprintf_P(webindex, sizeof(webindex), PSTR("g%d"), i); + WebGetArg(webindex, tmp, sizeof(tmp)); + uint8_t value = (!strlen(tmp)) ? 0 : atoi(tmp); +#ifdef ESP8266 + Settings.my_gp.io[i] = value; +#else + if (i == ADC0_PIN) { + Settings.my_adc0 = value; + } else { + Settings.my_gp.io[i] = value; + } +#endif + gpios += F(", " D_GPIO ); gpios += String(i); gpios += F(" "); gpios += String(value); + } + } + } +#ifdef ESP8266 +#ifndef USE_ADC_VCC + + WebGetArg("g" STR(ADC0_PIN), tmp, sizeof(tmp)); + Settings.my_adc0 = (!strlen(tmp)) ? 0 : atoi(tmp); + gpios += F(", " D_ADC "0 "); gpios += String(Settings.my_adc0); +#endif +#endif + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MODULE "%s " D_CMND_MODULE "%s"), ModuleName().c_str(), gpios.c_str()); +} + + + +const char kUnescapeCode[] = "&><\"\'"; +const char kEscapeCode[] PROGMEM = "&|>|<|"|'"; + +String HtmlEscape(const String unescaped) { + char escaped[10]; + size_t ulen = unescaped.length(); + String result = ""; + for (size_t i = 0; i < ulen; i++) { + char c = unescaped[i]; + char *p = strchr(kUnescapeCode, c); + if (p != nullptr) { + result += GetTextIndexed(escaped, sizeof(escaped), p - kUnescapeCode, kEscapeCode); + } else { + result += c; + } + } + return result; +} + + +const char kEncryptionType[] PROGMEM = "|||" D_WPA_PSK "||" D_WPA2_PSK "|" D_WEP "||" D_NONE "|" D_AUTO; + +void HandleWifiConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess(!WifiIsInManagerMode())) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_WIFI); + + if (Webserver->hasArg("save") && HTTP_MANAGER_RESET_ONLY != Web.state) { + WifiSaveSettings(); + WebRestart(2); + return; + } + + WSContentStart_P(S_CONFIGURE_WIFI, !WifiIsInManagerMode()); + WSContentSend_P(HTTP_SCRIPT_WIFI); + WSContentSendStyle(); + + if (HTTP_MANAGER_RESET_ONLY != Web.state) { + if (Webserver->hasArg("scan")) { +#ifdef USE_EMULATION + UdpDisconnect(); +#endif + int n = WiFi.scanNetworks(); + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_SCAN_DONE)); + + if (0 == n) { + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, S_NO_NETWORKS_FOUND); + WSContentSend_P(S_NO_NETWORKS_FOUND); + WSContentSend_P(PSTR(". " D_REFRESH_TO_SCAN_AGAIN ".")); + } else { + + int indices[n]; + for (uint32_t i = 0; i < n; i++) { + indices[i] = i; + } + + + for (uint32_t i = 0; i < n; i++) { + for (uint32_t j = i + 1; j < n; j++) { + if (WiFi.RSSI(indices[j]) > WiFi.RSSI(indices[i])) { + std::swap(indices[i], indices[j]); + } + } + } + + + String cssid; + for (uint32_t i = 0; i < n; i++) { + if (-1 == indices[i]) { continue; } + cssid = WiFi.SSID(indices[i]); + uint32_t cschn = WiFi.channel(indices[i]); + for (uint32_t j = i + 1; j < n; j++) { + if ((cssid == WiFi.SSID(indices[j])) && (cschn == WiFi.channel(indices[j]))) { + DEBUG_CORE_LOG(PSTR(D_LOG_WIFI D_DUPLICATE_ACCESSPOINT " %s"), WiFi.SSID(indices[j]).c_str()); + indices[j] = -1; + } + } + } + + + for (uint32_t i = 0; i < n; i++) { + if (-1 == indices[i]) { continue; } + int32_t rssi = WiFi.RSSI(indices[i]); + DEBUG_CORE_LOG(PSTR(D_LOG_WIFI D_SSID " %s, " D_BSSID " %s, " D_CHANNEL " %d, " D_RSSI " %d"), + WiFi.SSID(indices[i]).c_str(), WiFi.BSSIDstr(indices[i]).c_str(), WiFi.channel(indices[i]), rssi); + int quality = WifiGetRssiAsQuality(rssi); + int auth = WiFi.encryptionType(indices[i]); + char encryption[20]; + WSContentSend_P(PSTR("
%s (%d) %s %d%% (%d dBm)
"), + HtmlEscape(WiFi.SSID(indices[i])).c_str(), + WiFi.channel(indices[i]), + GetTextIndexed(encryption, sizeof(encryption), auth +1, kEncryptionType), + quality, rssi + ); + delay(0); + + } + WSContentSend_P(PSTR("
")); + } + } else { + WSContentSend_P(PSTR("
")); + } + + + WSContentSend_P(HTTP_FORM_WIFI, SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), WIFI_HOSTNAME, WIFI_HOSTNAME, SettingsText(SET_HOSTNAME), SettingsText(SET_CORS)); + WSContentSend_P(HTTP_FORM_END); + } + + if (WifiIsInManagerMode()) { +#ifndef FIRMWARE_MINIMAL + WSContentSpaceButton(BUTTON_RESTORE); + WSContentButton(BUTTON_RESET_CONFIGURATION); +#endif + WSContentSpaceButton(BUTTON_RESTART); + } else { + WSContentSpaceButton(BUTTON_CONFIGURATION); + } + WSContentStop(); +} + +void WifiSaveSettings(void) +{ + char tmp[TOPSZ]; + + WebGetArg("h", tmp, sizeof(tmp)); + SettingsUpdateText(SET_HOSTNAME, (!strlen(tmp)) ? WIFI_HOSTNAME : tmp); + if (strstr(SettingsText(SET_HOSTNAME), "%") != nullptr) { + SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); + } + WebGetArg("c", tmp, sizeof(tmp)); + SettingsUpdateText(SET_CORS, (!strlen(tmp)) ? CORS_DOMAIN : tmp); + WebGetArg("s1", tmp, sizeof(tmp)); + SettingsUpdateText(SET_STASSID1, (!strlen(tmp)) ? STA_SSID1 : tmp); + WebGetArg("s2", tmp, sizeof(tmp)); + SettingsUpdateText(SET_STASSID2, (!strlen(tmp)) ? STA_SSID2 : tmp); + WebGetArg("p1", tmp, sizeof(tmp)); + SettingsUpdateText(SET_STAPWD1, (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? SettingsText(SET_STAPWD1) : tmp); + WebGetArg("p2", tmp, sizeof(tmp)); + SettingsUpdateText(SET_STAPWD2, (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? SettingsText(SET_STAPWD2) : tmp); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_HOSTNAME " %s, " D_CMND_SSID "1 %s, " D_CMND_SSID "2 %s, " D_CMND_CORS " %s"), + SettingsText(SET_HOSTNAME), SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), SettingsText(SET_CORS)); +} + + + +void HandleLoggingConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_LOGGING); + + if (Webserver->hasArg("save")) { + LoggingSaveSettings(); + HandleConfiguration(); + return; + } + + WSContentStart_P(S_CONFIGURE_LOGGING); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_LOG1); + char stemp1[45]; + char stemp2[32]; + uint8_t dlevel[4] = { LOG_LEVEL_INFO, LOG_LEVEL_INFO, LOG_LEVEL_NONE, LOG_LEVEL_NONE }; + for (uint32_t idx = 0; idx < 4; idx++) { + if ((2==idx) && !Settings.flag.mqtt_enabled) { continue; } + uint32_t llevel = (0==idx)?Settings.seriallog_level:(1==idx)?Settings.weblog_level:(2==idx)?Settings.mqttlog_level:Settings.syslog_level; + WSContentSend_P(PSTR("

%s (%s)

")); + } + WSContentSend_P(HTTP_FORM_LOG2, SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, Settings.tele_period); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void LoggingSaveSettings(void) +{ + char tmp[TOPSZ]; + + WebGetArg("l0", tmp, sizeof(tmp)); + SetSeriallog((!strlen(tmp)) ? SERIAL_LOG_LEVEL : atoi(tmp)); + WebGetArg("l1", tmp, sizeof(tmp)); + Settings.weblog_level = (!strlen(tmp)) ? WEB_LOG_LEVEL : atoi(tmp); + WebGetArg("l2", tmp, sizeof(tmp)); + Settings.mqttlog_level = (!strlen(tmp)) ? MQTT_LOG_LEVEL : atoi(tmp); + WebGetArg("l3", tmp, sizeof(tmp)); + SetSyslog((!strlen(tmp)) ? SYS_LOG_LEVEL : atoi(tmp)); + WebGetArg("lh", tmp, sizeof(tmp)); + SettingsUpdateText(SET_SYSLOG_HOST, (!strlen(tmp)) ? SYS_LOG_HOST : tmp); + WebGetArg("lp", tmp, sizeof(tmp)); + Settings.syslog_port = (!strlen(tmp)) ? SYS_LOG_PORT : atoi(tmp); + WebGetArg("lt", tmp, sizeof(tmp)); + Settings.tele_period = (!strlen(tmp)) ? TELE_PERIOD : atoi(tmp); + if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) { + Settings.tele_period = 10; + } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_LOG D_CMND_SERIALLOG " %d, " D_CMND_WEBLOG " %d, " D_CMND_MQTTLOG " %d, " D_CMND_SYSLOG " %d, " D_CMND_LOGHOST " %s, " D_CMND_LOGPORT " %d, " D_CMND_TELEPERIOD " %d"), + Settings.seriallog_level, Settings.weblog_level, Settings.mqttlog_level, Settings.syslog_level, SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, Settings.tele_period); +} + + + +void HandleOtherConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_OTHER); + + if (Webserver->hasArg("save")) { + OtherSaveSettings(); + WebRestart(1); + return; + } + + WSContentStart_P(S_CONFIGURE_OTHER); + WSContentSendStyle(); + + TemplateJson(); + char stemp[strlen(mqtt_data) +1]; + strlcpy(stemp, mqtt_data, sizeof(stemp)); + WSContentSend_P(HTTP_FORM_OTHER, stemp, (USER_MODULE == Settings.module) ? " checked disabled" : "", (Settings.flag.mqtt_enabled) ? " checked" : ""); + + uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present; +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { maxfn = 1; } +#endif + for (uint32_t i = 0; i < maxfn; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i +1); + WSContentSend_P(PSTR("" D_FRIENDLY_NAME " %d (" FRIENDLY_NAME "%s)

"), + i +1, + (i) ? stemp : "", + i, + (i) ? stemp : "", + SettingsText(SET_FRIENDLYNAME1 + i)); + } + +#ifdef USE_EMULATION +#if defined(USE_EMULATION_WEMO) || defined(USE_EMULATION_HUE) + WSContentSend_P(PSTR("

 " D_EMULATION " 

")); + for (uint32_t i = 0; i < EMUL_MAX; i++) { +#ifndef USE_EMULATION_WEMO + if (i == EMUL_WEMO) { i++; } +#endif +#ifndef USE_EMULATION_HUE + if (i == EMUL_HUE) { i++; } +#endif + if (i < EMUL_MAX) { + WSContentSend_P(PSTR("%s %s
"), + i, i, + (i == Settings.flag2.emulation) ? " checked" : "", + GetTextIndexed(stemp, sizeof(stemp), i, kEmulationOptions), + (i == EMUL_NONE) ? "" : (i == EMUL_WEMO) ? D_SINGLE_DEVICE : D_MULTI_DEVICE); + } + } + WSContentSend_P(PSTR("

")); +#endif +#endif + + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void OtherSaveSettings(void) +{ + char tmp[TOPSZ]; + char webindex[5]; + char friendlyname[TOPSZ]; + char message[LOGSZ]; + + WebGetArg("wp", tmp, sizeof(tmp)); + SettingsUpdateText(SET_WEBPWD, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? SettingsText(SET_WEBPWD) : tmp); + Settings.flag.mqtt_enabled = Webserver->hasArg("b1"); +#ifdef USE_EMULATION + UdpDisconnect(); +#if defined(USE_EMULATION_WEMO) || defined(USE_EMULATION_HUE) + WebGetArg("b2", tmp, sizeof(tmp)); + Settings.flag2.emulation = (!strlen(tmp)) ? 0 : atoi(tmp); +#endif +#endif + + snprintf_P(message, sizeof(message), PSTR(D_LOG_OTHER D_MQTT_ENABLE " %s, " D_CMND_EMULATION " %d, " D_CMND_FRIENDLYNAME), GetStateText(Settings.flag.mqtt_enabled), Settings.flag2.emulation); + for (uint32_t i = 0; i < MAX_FRIENDLYNAMES; i++) { + snprintf_P(webindex, sizeof(webindex), PSTR("a%d"), i); + WebGetArg(webindex, tmp, sizeof(tmp)); + snprintf_P(friendlyname, sizeof(friendlyname), PSTR(FRIENDLY_NAME"%d"), i +1); + SettingsUpdateText(SET_FRIENDLYNAME1 +i, (!strlen(tmp)) ? (i) ? friendlyname : FRIENDLY_NAME : tmp); + snprintf_P(message, sizeof(message), PSTR("%s%s %s"), message, (i) ? "," : "", SettingsText(SET_FRIENDLYNAME1 +i)); + } + AddLog_P(LOG_LEVEL_INFO, message); +# 2014 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_01_webserver.ino" + WebGetArg("t1", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(message, sizeof(message), PSTR(D_CMND_BACKLOG " " D_CMND_TEMPLATE " %s%s"), tmp, (Webserver->hasArg("t2")) ? "; " D_CMND_MODULE " 0" : ""); + ExecuteWebCommand(message, SRC_WEBGUI); + } +} + + + +void HandleBackupConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_BACKUP_CONFIGURATION)); + + if (!SettingsBufferAlloc()) { return; } + + WiFiClient myClient = Webserver->client(); + Webserver->setContentLength(sizeof(Settings)); + + char attachment[TOPSZ]; + + + + + char hostname[sizeof(my_hostname)]; + snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=Config_%s_%s.dmp"), NoAlNumToUnderscore(hostname, my_hostname), my_version); + + Webserver->sendHeader(F("Content-Disposition"), attachment); + + WSSend(200, CT_STREAM, ""); + + uint32_t cfg_crc32 = Settings.cfg_crc32; + Settings.cfg_crc32 = GetSettingsCrc32(); + + memcpy(settings_buffer, &Settings, sizeof(Settings)); + if (Web.config_xor_on_set) { + for (uint32_t i = 2; i < sizeof(Settings); i++) { + settings_buffer[i] ^= (Web.config_xor_on_set +i); + } + } + +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 + size_t written = myClient.write((const char*)settings_buffer, sizeof(Settings)); + if (written < sizeof(Settings)) { + myClient.write((const char*)settings_buffer +written, sizeof(Settings) -written); + } +#else + myClient.write((const char*)settings_buffer, sizeof(Settings)); +#endif + + SettingsBufferFree(); + + Settings.cfg_crc32 = cfg_crc32; +} + + + +void HandleResetConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess(!WifiIsInManagerMode())) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESET_CONFIGURATION); + + WSContentStart_P(S_RESET_CONFIGURATION, !WifiIsInManagerMode()); + WSContentSendStyle(); + WSContentSend_P(PSTR("
" D_CONFIGURATION_RESET "
")); + WSContentSend_P(HTTP_MSG_RSTRT); + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); + + char command[CMDSZ]; + snprintf_P(command, sizeof(command), PSTR(D_CMND_RESET " 1")); + ExecuteWebCommand(command, SRC_WEBGUI); +} + +void HandleRestoreConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTORE_CONFIGURATION); + + WSContentStart_P(S_RESTORE_CONFIGURATION); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_RST); + WSContentSend_P(HTTP_FORM_RST_UPG, D_RESTORE); + if (WifiIsInManagerMode()) { + WSContentSpaceButton(BUTTON_MAIN); + } else { + WSContentSpaceButton(BUTTON_CONFIGURATION); + } + WSContentStop(); + + Web.upload_error = 0; + Web.upload_file_type = UPL_SETTINGS; +} + + + +void HandleInformation(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_INFORMATION); + + char stopic[TOPSZ]; + + int freeMem = ESP.getFreeHeap(); + + WSContentStart_P(S_INFORMATION); + + + + WSContentSend_P(HTTP_SCRIPT_INFO_BEGIN); + WSContentSend_P(PSTR("
")); + WSContentSend_P(PSTR(D_PROGRAM_VERSION "}2%s%s"), my_version, my_image); + WSContentSend_P(PSTR("}1" D_BUILD_DATE_AND_TIME "}2%s"), GetBuildDateAndTime().c_str()); + WSContentSend_P(PSTR("}1" D_CORE_AND_SDK_VERSION "}2" ARDUINO_CORE_RELEASE "/%s"), ESP.getSdkVersion()); + WSContentSend_P(PSTR("}1" D_UPTIME "}2%s"), GetUptime().c_str()); + WSContentSend_P(PSTR("}1" D_FLASH_WRITE_COUNT "}2%d at 0x%X"), Settings.save_flag, GetSettingsAddress()); + WSContentSend_P(PSTR("}1" D_BOOT_COUNT "}2%d"), Settings.bootcount); + WSContentSend_P(PSTR("}1" D_RESTART_REASON "}2%s"), GetResetReason().c_str()); + uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : devices_present; +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { maxfn = 1; } +#endif + for (uint32_t i = 0; i < maxfn; i++) { + WSContentSend_P(PSTR("}1" D_FRIENDLY_NAME " %d}2%s"), i +1, SettingsText(SET_FRIENDLYNAME1 +i)); + } + WSContentSend_P(PSTR("}1}2 ")); + int32_t rssi = WiFi.RSSI(); + WSContentSend_P(PSTR("}1" D_AP "%d " D_SSID " (" D_RSSI ")}2%s (%d%%, %d dBm)"), Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), WifiGetRssiAsQuality(rssi), rssi); + WSContentSend_P(PSTR("}1" D_HOSTNAME "}2%s%s"), my_hostname, (Wifi.mdns_begun) ? ".local" : ""); +#if LWIP_IPV6 + String ipv6_addr = WifiGetIPv6(); + if(ipv6_addr != ""){ + WSContentSend_P(PSTR("}1 IPv6 Address }2%s"), ipv6_addr.c_str()); + } +#endif + if (static_cast(WiFi.localIP()) != 0) { + WSContentSend_P(PSTR("}1" D_IP_ADDRESS "}2%s"), WiFi.localIP().toString().c_str()); + WSContentSend_P(PSTR("}1" D_GATEWAY "}2%s"), IPAddress(Settings.ip_address[1]).toString().c_str()); + WSContentSend_P(PSTR("}1" D_SUBNET_MASK "}2%s"), IPAddress(Settings.ip_address[2]).toString().c_str()); + WSContentSend_P(PSTR("}1" D_DNS_SERVER "}2%s"), IPAddress(Settings.ip_address[3]).toString().c_str()); + WSContentSend_P(PSTR("}1" D_MAC_ADDRESS "}2%s"), WiFi.macAddress().c_str()); + } + if (static_cast(WiFi.softAPIP()) != 0) { + WSContentSend_P(PSTR("}1" D_IP_ADDRESS "}2%s"), WiFi.softAPIP().toString().c_str()); + WSContentSend_P(PSTR("}1" D_GATEWAY "}2%s"), WiFi.softAPIP().toString().c_str()); + WSContentSend_P(PSTR("}1" D_MAC_ADDRESS "}2%s"), WiFi.softAPmacAddress().c_str()); + } + WSContentSend_P(PSTR("}1}2 ")); + if (Settings.flag.mqtt_enabled) { + WSContentSend_P(PSTR("}1" D_MQTT_HOST "}2%s"), SettingsText(SET_MQTT_HOST)); + WSContentSend_P(PSTR("}1" D_MQTT_PORT "}2%d"), Settings.mqtt_port); + WSContentSend_P(PSTR("}1" D_MQTT_USER "}2%s"), SettingsText(SET_MQTT_USER)); + WSContentSend_P(PSTR("}1" D_MQTT_CLIENT "}2%s"), mqtt_client); + WSContentSend_P(PSTR("}1" D_MQTT_TOPIC "}2%s"), SettingsText(SET_MQTT_TOPIC)); + uint32_t real_index = SET_MQTT_GRP_TOPIC; + for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { + if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + if (strlen(SettingsText(real_index +i))) { + WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC " %d}2%s"), 1 +i, GetGroupTopic_P(stopic, "", real_index +i)); + } + } + WSContentSend_P(PSTR("}1" D_MQTT_FULL_TOPIC "}2%s"), GetTopic_P(stopic, CMND, mqtt_topic, "")); + WSContentSend_P(PSTR("}1" D_MQTT " " D_FALLBACK_TOPIC "}2%s"), GetFallbackTopic_P(stopic, "")); + } else { + WSContentSend_P(PSTR("}1" D_MQTT "}2" D_DISABLED)); + } + WSContentSend_P(PSTR("}1}2 ")); + +#ifdef USE_EMULATION + WSContentSend_P(PSTR("}1" D_EMULATION "}2%s"), GetTextIndexed(stopic, sizeof(stopic), Settings.flag2.emulation, kEmulationOptions)); +#else + WSContentSend_P(PSTR("}1" D_EMULATION "}2" D_DISABLED)); +#endif + +#ifdef USE_DISCOVERY + WSContentSend_P(PSTR("}1" D_MDNS_DISCOVERY "}2%s"), (Settings.flag3.mdns_enabled) ? D_ENABLED : D_DISABLED); + if (Settings.flag3.mdns_enabled) { +#ifdef WEBSERVER_ADVERTISE + WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_WEB_SERVER)); +#else + WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_DISABLED)); +#endif + } +#else + WSContentSend_P(PSTR("}1" D_MDNS_DISCOVERY "}2" D_DISABLED)); +#endif + + WSContentSend_P(PSTR("}1}2 ")); + WSContentSend_P(PSTR("}1" D_ESP_CHIP_ID "}2%d"), ESP_getChipId()); + WSContentSend_P(PSTR("}1" D_FLASH_CHIP_ID "}20x%06X"), ESP_getFlashChipId()); + WSContentSend_P(PSTR("}1" D_FLASH_CHIP_SIZE "}2%dkB"), ESP.getFlashChipRealSize() / 1024); + WSContentSend_P(PSTR("}1" D_PROGRAM_FLASH_SIZE "}2%dkB"), ESP.getFlashChipSize() / 1024); + WSContentSend_P(PSTR("}1" D_PROGRAM_SIZE "}2%dkB"), ESP_getSketchSize() / 1024); + WSContentSend_P(PSTR("}1" D_FREE_PROGRAM_SPACE "}2%dkB"), ESP.getFreeSketchSpace() / 1024); + WSContentSend_P(PSTR("}1" D_FREE_MEMORY "}2%dkB"), freeMem / 1024); + WSContentSend_P(PSTR("
")); + + WSContentSend_P(HTTP_SCRIPT_INFO_END); + WSContentSendStyle(); + + WSContentSend_P(PSTR("" + "
")); + + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); +} +#endif + + + +void HandleUpgradeFirmware(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_FIRMWARE_UPGRADE); + + WSContentStart_P(S_FIRMWARE_UPGRADE); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_UPG, SettingsText(SET_OTAURL)); + WSContentSend_P(HTTP_FORM_RST_UPG, D_UPGRADE); + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); + + Web.upload_error = 0; + Web.upload_file_type = UPL_TASMOTA; +} + +void HandleUpgradeFirmwareStart(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + char command[TOPSZ + 10]; + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPGRADE_STARTED)); + WifiConfigCounter(); + + char otaurl[TOPSZ]; + WebGetArg("o", otaurl, sizeof(otaurl)); + if (strlen(otaurl)) { + snprintf_P(command, sizeof(command), PSTR(D_CMND_OTAURL " %s"), otaurl); + ExecuteWebCommand(command, SRC_WEBGUI); + } + + WSContentStart_P(S_INFORMATION); + WSContentSend_P(HTTP_SCRIPT_RELOAD_OTA); + WSContentSendStyle(); + WSContentSend_P(PSTR("
" D_UPGRADE_STARTED " ...
")); + WSContentSend_P(HTTP_MSG_RSTRT); + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); + + snprintf_P(command, sizeof(command), PSTR(D_CMND_UPGRADE " 1")); + ExecuteWebCommand(command, SRC_WEBGUI); +} + +void HandleUploadDone(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPLOAD_DONE)); + + char error[100]; + + WifiConfigCounter(); + restart_flag = 0; + MqttRetryCounter(0); + + WSContentStart_P(S_INFORMATION); + if (!Web.upload_error) { + WSContentSend_P(HTTP_SCRIPT_RELOAD_OTA); + } + WSContentSendStyle(); + WSContentSend_P(PSTR("
" D_UPLOAD " " D_FAILED "

"), WebColor(COL_TEXT_WARNING)); +#ifdef USE_RF_FLASH + if (Web.upload_error < 15) { +#else + if ((Web.upload_error < 10) || (14 == Web.upload_error)) { + if (14 == Web.upload_error) { Web.upload_error = 10; } +#endif + GetTextIndexed(error, sizeof(error), Web.upload_error -1, kUploadErrors); + } else { + snprintf_P(error, sizeof(error), PSTR(D_UPLOAD_ERROR_CODE " %d"), Web.upload_error); + } + WSContentSend_P(error); + DEBUG_CORE_LOG(PSTR("UPL: %s"), error); + stop_flash_rotate = Settings.flag.stop_flash_rotate; + } else { + WSContentSend_P(PSTR("%06x'>" D_SUCCESSFUL "
"), WebColor(COL_TEXT_SUCCESS)); + WSContentSend_P(HTTP_MSG_RSTRT); + ShowWebSource(SRC_WEBGUI); +#ifdef USE_TASMOTA_SLAVE + if (TasmotaSlave_GetFlagFlashing()) { + restart_flag = 0; + } else { + restart_flag = 2; + } +#else + restart_flag = 2; +#endif + } + SettingsBufferFree(); + WSContentSend_P(PSTR("

")); + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); +#ifdef USE_TASMOTA_SLAVE + if (TasmotaSlave_GetFlagFlashing()) { + TasmotaSlave_Flash(); + } +#endif +} + +void HandleUploadLoop(void) +{ + + bool _serialoutput = (LOG_LEVEL_DEBUG <= seriallog_level); + + if (HTTP_USER == Web.state) { return; } + if (Web.upload_error) { + if (UPL_TASMOTA == Web.upload_file_type) { Update.end(); } + return; + } + + HTTPUpload& upload = Webserver->upload(); + + if (UPLOAD_FILE_START == upload.status) { + restart_flag = 60; + if (0 == upload.filename.c_str()[0]) { + Web.upload_error = 1; + return; + } + SettingsSave(1); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_FILE " %s ..."), upload.filename.c_str()); + if (UPL_SETTINGS == Web.upload_file_type) { + if (!SettingsBufferAlloc()) { + Web.upload_error = 2; + return; + } + } else { + MqttRetryCounter(60); +#ifdef USE_EMULATION + UdpDisconnect(); +#endif +#ifdef USE_ARILUX_RF + AriluxRfDisable(); +#endif + if (Settings.flag.mqtt_enabled) { + MqttDisconnect(); + } + uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; + if (!Update.begin(maxSketchSpace)) { + + + + + + + Web.upload_error = 2; + return; + } + } + Web.upload_progress_dot_count = 0; + } else if (!Web.upload_error && (UPLOAD_FILE_WRITE == upload.status)) { + if (0 == upload.totalSize) { + if (UPL_SETTINGS == Web.upload_file_type) { + Web.config_block_count = 0; + } + else { +#ifdef USE_RF_FLASH + if ((SONOFF_BRIDGE == my_module_type) && (upload.buf[0] == ':')) { + Update.end(); + Web.upload_file_type = UPL_EFM8BB1; + + Web.upload_error = SnfBrUpdateInit(); + if (Web.upload_error != 0) { return; } + } else +#endif +#ifdef USE_TASMOTA_SLAVE + if ((WEMOS == my_module_type) && (upload.buf[0] == ':')) { + Update.end(); + Web.upload_file_type = UPL_TASMOTASLAVE; + Web.upload_error = TasmotaSlave_UpdateInit(); + if (Web.upload_error != 0) { return; } + } else +#endif + { + if ((upload.buf[0] != 0xE9) && (upload.buf[0] != 0x1F)) { + Web.upload_error = 3; + return; + } + if (0xE9 == upload.buf[0]) { + uint32_t bin_flash_size = ESP.magicFlashChipSize((upload.buf[3] & 0xf0) >> 4); + if (bin_flash_size > ESP.getFlashChipRealSize()) { + Web.upload_error = 4; + return; + } + + } + } + } + } + if (UPL_SETTINGS == Web.upload_file_type) { + if (!Web.upload_error) { + if (upload.currentSize > (sizeof(Settings) - (Web.config_block_count * HTTP_UPLOAD_BUFLEN))) { + Web.upload_error = 9; + return; + } + memcpy(settings_buffer + (Web.config_block_count * HTTP_UPLOAD_BUFLEN), upload.buf, upload.currentSize); + Web.config_block_count++; + } + } +#ifdef USE_RF_FLASH + else if (UPL_EFM8BB1 == Web.upload_file_type) { + if (efm8bb1_update != nullptr) { + ssize_t result = rf_glue_remnant_with_new_data_and_write(efm8bb1_update, upload.buf, upload.currentSize); + free(efm8bb1_update); + efm8bb1_update = nullptr; + if (result != 0) { + Web.upload_error = abs(result); + return; + } + } + ssize_t result = rf_search_and_write(upload.buf, upload.currentSize); + if (result < 0) { + Web.upload_error = abs(result); + return; + } else if (result > 0) { + if ((size_t)result > upload.currentSize) { + + Web.upload_error = 9; + return; + } + + size_t remnant_sz = upload.currentSize - result; + efm8bb1_update = (uint8_t *) malloc(remnant_sz + 1); + if (efm8bb1_update == nullptr) { + Web.upload_error = 2; + return; + } + memcpy(efm8bb1_update, upload.buf + result, remnant_sz); + + efm8bb1_update[remnant_sz] = '\0'; + } + } +#endif +#ifdef USE_TASMOTA_SLAVE + else if (UPL_TASMOTASLAVE == Web.upload_file_type) { + TasmotaSlave_WriteBuffer(upload.buf, upload.currentSize); + } +#endif + else { + if (!Web.upload_error && (Update.write(upload.buf, upload.currentSize) != upload.currentSize)) { + Web.upload_error = 5; + return; + } + if (_serialoutput) { + Serial.printf("."); + Web.upload_progress_dot_count++; + if (!(Web.upload_progress_dot_count % 80)) { Serial.println(); } + } + } + } else if(!Web.upload_error && (UPLOAD_FILE_END == upload.status)) { + if (_serialoutput && (Web.upload_progress_dot_count % 80)) { + Serial.println(); + } + if (UPL_SETTINGS == Web.upload_file_type) { + if (Web.config_xor_on_set) { + for (uint32_t i = 2; i < sizeof(Settings); i++) { + settings_buffer[i] ^= (Web.config_xor_on_set +i); + } + } + bool valid_settings = false; + 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]; + 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); + } + } else { + valid_settings = (settings_buffer[0] == CONFIG_FILE_SIGN); + } + + if (valid_settings) { +#ifdef ESP8266 + valid_settings = (0 == settings_buffer[0xF36]); +#endif +#ifdef ESP32 + valid_settings = (1 == settings_buffer[0xF36]); +#endif + } + + if (valid_settings) { + SettingsDefaultSet2(); + memcpy((char*)&Settings +16, settings_buffer +16, sizeof(Settings) -16); + Settings.version = buffer_version; + SettingsBufferFree(); + } else { + Web.upload_error = 8; + return; + } + } +#ifdef USE_RF_FLASH + else if (UPL_EFM8BB1 == Web.upload_file_type) { + + Web.upload_file_type = UPL_TASMOTA; + } +#endif +#ifdef USE_TASMOTA_SLAVE + else if (UPL_TASMOTASLAVE == Web.upload_file_type) { + + TasmotaSlave_SetFlagFlashing(true); + Web.upload_file_type = UPL_TASMOTA; + } +#endif + else { + if (!Update.end(true)) { + if (_serialoutput) { Update.printError(Serial); } + Web.upload_error = 6; + return; + } + if (!VersionCompatible()) { + Web.upload_error = 14; + return; + } + } + if (!Web.upload_error) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_SUCCESSFUL " %u bytes. " D_RESTARTING), upload.totalSize); + } + } else if (UPLOAD_FILE_ABORTED == upload.status) { + restart_flag = 0; + MqttRetryCounter(0); + Web.upload_error = 7; + if (UPL_TASMOTA == Web.upload_file_type) { Update.end(); } + } + delay(0); +} + + + +void HandlePreflightRequest(void) +{ + HttpHeaderCors(); + Webserver->sendHeader(F("Access-Control-Allow-Methods"), F("GET, POST")); + Webserver->sendHeader(F("Access-Control-Allow-Headers"), F("authorization")); + WSSend(200, CT_HTML, ""); +} + + + +void HandleHttpCommand(void) +{ + if (!HttpCheckPriviledgedAccess(false)) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_COMMAND)); + + if (strlen(SettingsText(SET_WEBPWD))) { + char tmp1[33]; + WebGetArg("user", tmp1, sizeof(tmp1)); + char tmp2[strlen(SettingsText(SET_WEBPWD)) +1]; + WebGetArg("password", tmp2, sizeof(tmp2)); + if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, SettingsText(SET_WEBPWD)))) { + WSContentBegin(401, CT_JSON); + WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_NEED_USER_AND_PASSWORD "\"}")); + WSContentEnd(); + return; + } + } + + WSContentBegin(200, CT_JSON); + uint32_t curridx = web_log_index; + String svalue = Webserver->arg("cmnd"); + if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) { + ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCOMMAND); + if (web_log_index != curridx) { + uint32_t counter = curridx; + WSContentSend_P(PSTR("{")); + bool cflg = false; + do { + char* tmp; + size_t len; + GetLog(counter, &tmp, &len); + if (len) { + + char* JSON = (char*)memchr(tmp, '{', len); + if (JSON) { + size_t JSONlen = len - (JSON - tmp); + if (JSONlen > sizeof(mqtt_data)) { JSONlen = sizeof(mqtt_data); } + char stemp[JSONlen]; + strlcpy(stemp, JSON +1, JSONlen -2); + WSContentSend_P(PSTR("%s%s"), (cflg) ? "," : "", stemp); + cflg = true; + } + } + counter++; + counter &= 0xFF; + if (!counter) counter++; + } while (counter != web_log_index); + WSContentSend_P(PSTR("}")); + } else { + WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_ENABLE_WEBLOG_FOR_RESPONSE "\"}")); + } + } else { + WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_ENTER_COMMAND " cmnd=\"}")); + } + WSContentEnd(); +} + + + +void HandleConsole(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + if (Webserver->hasArg("c2")) { + HandleConsoleRefresh(); + return; + } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONSOLE); + + WSContentStart_P(S_CONSOLE); + WSContentSend_P(HTTP_SCRIPT_CONSOL, Settings.web_refresh); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_CMND); + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); +} + +void HandleConsoleRefresh(void) +{ + bool cflg = true; + uint32_t counter = 0; + + String svalue = Webserver->arg("c1"); + if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), svalue.c_str()); + ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCONSOLE); + } + + char stmp[8]; + WebGetArg("c2", stmp, sizeof(stmp)); + if (strlen(stmp)) { counter = atoi(stmp); } + + WSContentBegin(200, CT_PLAIN); + WSContentSend_P(PSTR("%d}1%d}1"), web_log_index, Web.reset_web_log_flag); + if (!Web.reset_web_log_flag) { + counter = 0; + Web.reset_web_log_flag = true; + } + if (counter != web_log_index) { + if (!counter) { + counter = web_log_index; + cflg = false; + } + do { + char* tmp; + size_t len; + GetLog(counter, &tmp, &len); + if (len) { + if (len > sizeof(mqtt_data) -2) { len = sizeof(mqtt_data); } + char stemp[len +1]; + strlcpy(stemp, tmp, len); + WSContentSend_P(PSTR("%s%s"), (cflg) ? "\n" : "", stemp); + cflg = true; + } + counter++; + counter &= 0xFF; + if (!counter) { counter++; } + } while (counter != web_log_index); + } + WSContentSend_P(PSTR("}1")); + WSContentEnd(); +} + + + +void HandleNotFound(void) +{ + + + if (CaptivePortal()) { return; } + +#ifdef USE_EMULATION +#ifdef USE_EMULATION_HUE + String path = Webserver->uri(); + if ((EMUL_HUE == Settings.flag2.emulation) && (path.startsWith("/api"))) { + HandleHueApi(&path); + } else +#endif +#endif + { + WSContentBegin(404, CT_PLAIN); + WSContentSend_P(PSTR(D_FILE_NOT_FOUND "\n\nURI: %s\nMethod: %s\nArguments: %d\n"), Webserver->uri().c_str(), (Webserver->method() == HTTP_GET) ? "GET" : "POST", Webserver->args()); + for (uint32_t i = 0; i < Webserver->args(); i++) { + WSContentSend_P(PSTR(" %s: %s\n"), Webserver->argName(i).c_str(), Webserver->arg(i).c_str()); + } + WSContentEnd(); + } +} + + +bool CaptivePortal(void) +{ + + if ((WifiIsInManagerMode()) && !ValidIpAddress(Webserver->hostHeader().c_str())) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_REDIRECTED)); + + Webserver->sendHeader(F("Location"), String("http://") + Webserver->client().localIP().toString(), true); + WSSend(302, CT_PLAIN, ""); + Webserver->client().stop(); + return true; + } + return false; +} + + + +String UrlEncode(const String& text) +{ + const char hex[] = "0123456789ABCDEF"; + + String encoded = ""; + int len = text.length(); + int i = 0; + while (i < len) { + char decodedChar = text.charAt(i++); +# 2762 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_01_webserver.ino" + if ((' ' == decodedChar) || ('+' == decodedChar)) { + encoded += '%'; + encoded += hex[decodedChar >> 4]; + encoded += hex[decodedChar & 0xF]; + } else { + encoded += decodedChar; + } + + } + return encoded; +} + +int WebSend(char *buffer) +{ + + + + + + char *host; + char *user; + char *password; + char *command; + int status = 1; + + + host = strtok_r(buffer, "]", &command); + if (host && command) { + RemoveSpace(host); + host++; + host = strtok_r(host, ",", &user); + String url = F("http://"); + url += host; + + command = Trim(command); + if (command[0] != '/') { + url += F("/cm?"); + if (user) { + user = strtok_r(user, ":", &password); + if (user && password) { + char userpass[200]; + snprintf_P(userpass, sizeof(userpass), PSTR("user=%s&password=%s&"), user, password); + url += userpass; + } + } + url += F("cmnd="); + } + url += command; + + DEBUG_CORE_LOG(PSTR("WEB: Uri |%s|"), url.c_str()); + +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) + HTTPClient http; + if (http.begin(UrlEncode(url))) { +#else + WiFiClient http_client; + HTTPClient http; + if (http.begin(http_client, UrlEncode(url))) { +#endif + int http_code = http.GET(); + if (http_code > 0) { + if (http_code == HTTP_CODE_OK || http_code == HTTP_CODE_MOVED_PERMANENTLY) { +#ifdef USE_WEBSEND_RESPONSE + + const char* read = http.getString().c_str(); + uint32_t j = 0; + char text = '.'; + while (text != '\0') { + text = *read++; + if (text > 31) { + mqtt_data[j++] = text; + if (j == sizeof(mqtt_data) -2) { break; } + } + } + mqtt_data[j] = '\0'; + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_WEBSEND)); +#ifdef USE_SCRIPT +extern uint8_t tasm_cmd_activ; + + tasm_cmd_activ=0; + XdrvRulesProcess(); +#endif +#endif + } + status = 0; + } else { + status = 2; + } + http.end(); + } else { + status = 3; + } + } + return status; +} + +bool JsonWebColor(const char* dataBuf) +{ + + + + + + char dataBufLc[strlen(dataBuf) +1]; + LowerCase(dataBufLc, dataBuf); + RemoveSpace(dataBufLc); + if (strlen(dataBufLc) < 9) { return false; } + + StaticJsonBuffer<450> jb; + JsonObject& obj = jb.parseObject(dataBufLc); + if (!obj.success()) { return false; } + + char parm_lc[10]; + if (obj[LowerCase(parm_lc, D_CMND_WEBCOLOR)].success()) { + for (uint32_t i = 0; i < COL_LAST; i++) { + const char* color = obj[parm_lc][i]; + if (color != nullptr) { + WebHexCode(i, color); + } + } + } + return true; +} + +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 = "|" +#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 "|" D_CMND_WEBBUTTON "|" D_CMND_CORS; + +void (* const WebCommand[])(void) PROGMEM = { +#ifdef USE_EMULATION + &CmndEmulation, +#endif +#ifdef USE_SENDMAIL + &CmndSendmail, +#endif + &CmndWebServer, &CmndWebPassword, &CmndWeblog, &CmndWebRefresh, &CmndWebSend, &CmndWebColor, + &CmndWebSensor, &CmndWebButton, &CmndCors }; + + + + + +#ifdef USE_EMULATION +void CmndEmulation(void) +{ +#if defined(USE_EMULATION_WEMO) || defined(USE_EMULATION_HUE) +#if defined(USE_EMULATION_WEMO) && defined(USE_EMULATION_HUE) + if ((XdrvMailbox.payload >= EMUL_NONE) && (XdrvMailbox.payload < EMUL_MAX)) { +#else +#ifndef USE_EMULATION_WEMO + if ((EMUL_NONE == XdrvMailbox.payload) || (EMUL_HUE == XdrvMailbox.payload)) { +#endif +#ifndef USE_EMULATION_HUE + if ((EMUL_NONE == XdrvMailbox.payload) || (EMUL_WEMO == XdrvMailbox.payload)) { +#endif +#endif + Settings.flag2.emulation = XdrvMailbox.payload; + restart_flag = 2; + } +#endif + ResponseCmndNumber(Settings.flag2.emulation); +} +#endif + +#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 + + +void CmndWebServer(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { + Settings.webserver = XdrvMailbox.payload; + } + if (Settings.webserver) { + Response_P(PSTR("{\"" D_CMND_WEBSERVER "\":\"" D_JSON_ACTIVE_FOR " %s " D_JSON_ON_DEVICE " %s " D_JSON_WITH_IP_ADDRESS " %s\"}"), + (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str()); + } else { + ResponseCmndStateText(0); + } +} + +void CmndWebPassword(void) +{ + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_WEBPWD, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? WEB_PASSWORD : XdrvMailbox.data); + ResponseCmndChar(SettingsText(SET_WEBPWD)); + } else { + Response_P(S_JSON_COMMAND_ASTERISK, XdrvMailbox.command); + } +} + +void CmndWeblog(void) +{ + if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { + Settings.weblog_level = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.weblog_level); +} + +void CmndWebRefresh(void) +{ + if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload <= 10000)) { + Settings.web_refresh = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.web_refresh); +} + +void CmndWebSend(void) +{ + if (XdrvMailbox.data_len > 0) { + uint32_t result = WebSend(XdrvMailbox.data); + char stemp1[20]; + ResponseCmndChar(GetTextIndexed(stemp1, sizeof(stemp1), result, kWebSendStatus)); + } +} + +void CmndWebColor(void) +{ + if (XdrvMailbox.data_len > 0) { + if (strstr(XdrvMailbox.data, "{") == nullptr) { + if ((XdrvMailbox.data_len > 3) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= COL_LAST)) { + WebHexCode(XdrvMailbox.index -1, XdrvMailbox.data); + } + else if (0 == XdrvMailbox.payload) { + SettingsDefaultWebColor(); + } + } + else { + JsonWebColor(XdrvMailbox.data); + } + } + Response_P(PSTR("{\"" D_CMND_WEBCOLOR "\":[")); + for (uint32_t i = 0; i < COL_LAST; i++) { + if (i) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"#%06x\""), WebColor(i)); + } + ResponseAppend_P(PSTR("]}")); +} + +void CmndWebSensor(void) +{ + if (XdrvMailbox.index < MAX_XSNS_DRIVERS) { + if (XdrvMailbox.payload >= 0) { + bitWrite(Settings.sensors[XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1); + } + } + Response_P(PSTR("{\"" D_CMND_WEBSENSOR "\":")); + XsnsSensorState(); + ResponseJsonEnd(); +} + +void CmndWebButton(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_BUTTON_TEXT)) { + if (!XdrvMailbox.usridx) { + ResponseCmndAll(SET_BUTTON1, MAX_BUTTON_TEXT); + } else { + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_BUTTON1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data); + } + ResponseCmndIdxChar(SettingsText(SET_BUTTON1 + XdrvMailbox.index -1)); + } + } +} + +void CmndCors(void) +{ + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_CORS, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? WEB_PASSWORD : XdrvMailbox.data); + } + ResponseCmndChar(SettingsText(SET_CORS)); +} + + + + + +bool Xdrv01(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_LOOP: + PollDnsWebserver(); +#ifdef USE_EMULATION +#ifdef USE_DEVICE_GROUPS + if (Settings.flag2.emulation || Settings.flag4.device_groups_enabled) { PollUdp(); } +#else + if (Settings.flag2.emulation) { PollUdp(); } +#endif +#endif + break; + case FUNC_COMMAND: + result = DecodeCommand(kWebCommands, WebCommand); + break; + } + return result; +} +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_02_mqtt.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_02_mqtt.ino" +#define XDRV_02 2 + + + +#ifdef USE_MQTT_TLS + #include "WiFiClientSecureLightBearSSL.h" + BearSSL::WiFiClientSecure_light *tlsClient; +#else + WiFiClient EspClient; +#endif + +const char kMqttCommands[] PROGMEM = "|" +#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) + D_CMND_MQTTFINGERPRINT "|" +#endif + D_CMND_MQTTUSER "|" D_CMND_MQTTPASSWORD "|" +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + D_CMND_TLSKEY "|" +#endif + D_CMND_MQTTHOST "|" D_CMND_MQTTPORT "|" D_CMND_MQTTRETRY "|" D_CMND_STATETEXT "|" D_CMND_MQTTCLIENT "|" + D_CMND_FULLTOPIC "|" D_CMND_PREFIX "|" D_CMND_GROUPTOPIC "|" D_CMND_TOPIC "|" D_CMND_PUBLISH "|" D_CMND_MQTTLOG "|" + D_CMND_BUTTONTOPIC "|" D_CMND_SWITCHTOPIC "|" D_CMND_BUTTONRETAIN "|" D_CMND_SWITCHRETAIN "|" D_CMND_POWERRETAIN "|" D_CMND_SENSORRETAIN ; + +void (* const MqttCommand[])(void) PROGMEM = { +#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) + &CmndMqttFingerprint, +#endif + &CmndMqttUser, &CmndMqttPassword, +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + &CmndTlsKey, +#endif + &CmndMqttHost, &CmndMqttPort, &CmndMqttRetry, &CmndStateText, &CmndMqttClient, + &CmndFullTopic, &CmndPrefix, &CmndGroupTopic, &CmndTopic, &CmndPublish, &CmndMqttlog, + &CmndButtonTopic, &CmndSwitchTopic, &CmndButtonRetain, &CmndSwitchRetain, &CmndPowerRetain, &CmndSensorRetain }; + +struct MQTT { + uint16_t connect_count = 0; + uint16_t retry_counter = 1; + uint8_t initial_connection_state = 2; + bool connected = false; + bool allowed = false; +} Mqtt; + +#ifdef USE_MQTT_TLS + +#ifdef USE_MQTT_AWS_IOT +#include + +const br_ec_private_key *AWS_IoT_Private_Key = nullptr; +const br_x509_certificate *AWS_IoT_Client_Certificate = nullptr; + +class tls_entry_t { +public: + uint32_t name; + uint16_t start; + uint16_t len; +}; + +const static uint32_t TLS_NAME_SKEY = 0x2079656B; +const static uint32_t TLS_NAME_CRT = 0x20747263; + +class tls_dir_t { +public: + tls_entry_t entry[4]; +}; + +tls_dir_t tls_dir; + +#endif + + + + +bool is_fingerprint_mono_value(uint8_t finger[20], uint8_t value) { + for (uint32_t i = 0; i<20; i++) { + if (finger[i] != value) { + return false; + } + } + return true; +} +#endif + +void MakeValidMqtt(uint32_t option, char* str) +{ + + + uint32_t i = 0; + while (str[i] > 0) { + + if ((str[i] == '+') || (str[i] == '#') || (str[i] == ' ')) { + if (option) { + uint32_t j = i; + while (str[j] > 0) { + str[j] = str[j +1]; + j++; + } + i--; + } else { + str[i] = '_'; + } + } + i++; + } +} + +#ifdef USE_DISCOVERY +#ifdef MQTT_HOST_DISCOVERY +void MqttDiscoverServer(void) +{ + if (!Wifi.mdns_begun) { return; } + + int n = MDNS.queryService("mqtt", "tcp"); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_QUERY_DONE " %d"), n); + + if (n > 0) { + uint32_t i = 0; +#ifdef MDNS_HOSTNAME + for (i = n; i > 0; i--) { + if (!strcmp(MDNS.hostname(i).c_str(), MDNS_HOSTNAME)) { + break; + } + } +#endif + SettingsUpdateText(SET_MQTT_HOST, MDNS.IP(i).toString().c_str()); + Settings.mqtt_port = MDNS.port(i); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_MQTT_SERVICE_FOUND " %s, " D_IP_ADDRESS " %s, " D_PORT " %d"), MDNS.hostname(i).c_str(), SettingsText(SET_MQTT_HOST), Settings.mqtt_port); + } +} +#endif +#endif +# 163 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_02_mqtt.ino" +#include + + +#if (MQTT_MAX_PACKET_SIZE -TOPSZ -7) < MIN_MESSZ + #error "MQTT_MAX_PACKET_SIZE is too small in libraries/PubSubClient/src/PubSubClient.h, increase it to at least 1200" +#endif + +#ifdef USE_MQTT_TLS +PubSubClient MqttClient; +#else +PubSubClient MqttClient(EspClient); +#endif + +void MqttInit(void) +{ +#ifdef USE_MQTT_TLS + tlsClient = new BearSSL::WiFiClientSecure_light(1024,1024); + +#ifdef USE_MQTT_AWS_IOT + loadTlsDir(); + tlsClient->setClientECCert(AWS_IoT_Client_Certificate, + AWS_IoT_Private_Key, + 0xFFFF , 0); +#endif + +#ifdef USE_MQTT_TLS_CA_CERT +#ifdef USE_MQTT_AWS_IOT + tlsClient->setTrustAnchor(&AmazonRootCA1_TA); +#else + tlsClient->setTrustAnchor(&LetsEncryptX3CrossSigned_TA); +#endif +#endif + + MqttClient.setClient(*tlsClient); +#endif +} + +bool MqttIsConnected(void) +{ + return MqttClient.connected(); +} + +void MqttDisconnect(void) +{ + MqttClient.disconnect(); +} + +void MqttSubscribeLib(const char *topic) +{ + MqttClient.subscribe(topic); + MqttClient.loop(); +} + +void MqttUnsubscribeLib(const char *topic) +{ + MqttClient.unsubscribe(topic); + MqttClient.loop(); +} + +bool MqttPublishLib(const char* topic, bool retained) +{ + + if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) { + char *str = strstr(topic, SettingsText(SET_MQTTPREFIX1)); + if (str == topic) { + mqtt_cmnd_blocked_reset = 4; + mqtt_cmnd_blocked++; + } + } + + bool result = MqttClient.publish(topic, mqtt_data, retained); + yield(); + return result; +} + +void MqttDataHandler(char* mqtt_topic, uint8_t* mqtt_data, unsigned int data_len) +{ +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("MqttDataHandler")); +#endif + + + if (data_len >= MQTT_MAX_PACKET_SIZE) { return; } + + + if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) { + char *str = strstr(mqtt_topic, SettingsText(SET_MQTTPREFIX1)); + if ((str == mqtt_topic) && mqtt_cmnd_blocked) { + mqtt_cmnd_blocked--; + return; + } + } + + + char topic[TOPSZ]; + strlcpy(topic, mqtt_topic, sizeof(topic)); + mqtt_data[data_len] = 0; + char data[data_len +1]; + memcpy(data, mqtt_data, sizeof(data)); + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_MQTT D_RECEIVED_TOPIC " \"%s\", " D_DATA_SIZE " %d, " D_DATA " \"%s\""), topic, data_len, data); + + + + XdrvMailbox.index = strlen(topic); + XdrvMailbox.data_len = data_len; + XdrvMailbox.topic = topic; + XdrvMailbox.data = (char*)data; + if (XdrvCall(FUNC_MQTT_DATA)) { return; } + + ShowSource(SRC_MQTT); + + CommandHandler(topic, data, data_len); +} + + + +void MqttRetryCounter(uint8_t value) +{ + Mqtt.retry_counter = value; +} + +void MqttSubscribe(const char *topic) +{ + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT D_SUBSCRIBE_TO " %s"), topic); + MqttSubscribeLib(topic); +} + +void MqttUnsubscribe(const char *topic) +{ + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT D_UNSUBSCRIBE_FROM " %s"), topic); + MqttUnsubscribeLib(topic); +} + +void MqttPublishLogging(const char *mxtime) +{ + char saved_mqtt_data[strlen(mqtt_data) +1]; + memcpy(saved_mqtt_data, mqtt_data, sizeof(saved_mqtt_data)); + + + Response_P(PSTR("%s%s"), mxtime, log_data); + char stopic[TOPSZ]; + GetTopic_P(stopic, STAT, mqtt_topic, PSTR("LOGGING")); + MqttPublishLib(stopic, false); + + memcpy(mqtt_data, saved_mqtt_data, sizeof(saved_mqtt_data)); +} + +void MqttPublish(const char* topic, bool retained) +{ +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("MqttPublish")); +#endif + +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) || defined(MQTT_NO_RETAIN) + + + + retained = false; +#endif + + char sretained[CMDSZ]; + sretained[0] = '\0'; + char slog_type[20]; + snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_RESULT)); + + if (Settings.flag.mqtt_enabled) { + if (MqttPublishLib(topic, retained)) { + snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_MQTT)); + if (retained) { + snprintf_P(sretained, sizeof(sretained), PSTR(" (" D_RETAINED ")")); + } + } + } + + snprintf_P(log_data, sizeof(log_data), PSTR("%s%s = %s"), slog_type, (Settings.flag.mqtt_enabled) ? topic : strrchr(topic,'/')+1, mqtt_data); + if (strlen(log_data) >= (sizeof(log_data) - strlen(sretained) -1)) { + log_data[sizeof(log_data) - strlen(sretained) -5] = '\0'; + snprintf_P(log_data, sizeof(log_data), PSTR("%s ..."), log_data); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s%s"), log_data, sretained); + AddLog(LOG_LEVEL_INFO); + + if (Settings.ledstate &0x04) { + blinks++; + } +} + +void MqttPublish(const char* topic) +{ + MqttPublish(topic, false); +} + +void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retained) +{ + + + + + + + + char romram[64]; + char stopic[TOPSZ]; + + snprintf_P(romram, sizeof(romram), ((prefix > 3) && !Settings.flag.mqtt_response) ? S_RSLT_RESULT : subtopic); + for (uint32_t i = 0; i < strlen(romram); i++) { + romram[i] = toupper(romram[i]); + } + prefix &= 3; + GetTopic_P(stopic, prefix, mqtt_topic, romram); + MqttPublish(stopic, retained); + +#ifdef USE_MQTT_AWS_IOT + if ((prefix > 0) && (Settings.flag4.awsiot_shadow) && (Mqtt.connected)) { + + char *topic = SettingsText(SET_MQTT_TOPIC); + char topic2[strlen(topic)+1]; + strcpy(topic2, topic); + + char *s = topic2; + while (*s) { + if ('/' == *s) { + *s = '_'; + } + s++; + } + + snprintf_P(romram, sizeof(romram), PSTR("$aws/things/%s/shadow/update"), topic2); + + + char *mqtt_save = (char*) malloc(strlen(mqtt_data)+1); + if (!mqtt_save) { return; } + strcpy(mqtt_save, mqtt_data); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"state\":{\"reported\":%s}}"), mqtt_save); + free(mqtt_save); + + bool result = MqttClient.publish(romram, mqtt_data, false); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT "Updated shadow: %s"), romram); + yield(); + } +#endif +} + +void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic) +{ + MqttPublishPrefixTopic_P(prefix, subtopic, false); +} + +void MqttPublishTeleSensor(void) +{ + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); + XdrvRulesProcess(); +} + +void MqttPublishPowerState(uint32_t device) +{ + char stopic[TOPSZ]; + char scommand[33]; + + if ((device < 1) || (device > devices_present)) { device = 1; } + +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (device > 1)) { + if (GetFanspeed() < MaxFanspeed()) { +#ifdef USE_DOMOTICZ + DomoticzUpdateFanState(); +#endif + snprintf_P(scommand, sizeof(scommand), PSTR(D_CMND_FANSPEED)); + GetTopic_P(stopic, STAT, mqtt_topic, (Settings.flag.mqtt_response) ? scommand : S_RSLT_RESULT); + Response_P(S_JSON_COMMAND_NVALUE, scommand, GetFanspeed()); + MqttPublish(stopic); + } + } else { +#endif + GetPowerDevice(scommand, device, sizeof(scommand), Settings.flag.device_index_enable); + GetTopic_P(stopic, STAT, mqtt_topic, (Settings.flag.mqtt_response) ? scommand : S_RSLT_RESULT); + Response_P(S_JSON_COMMAND_SVALUE, scommand, GetStateText(bitRead(power, device -1))); + MqttPublish(stopic); + + if (!Settings.flag4.only_json_message) { + GetTopic_P(stopic, STAT, mqtt_topic, scommand); + Response_P(GetStateText(bitRead(power, device -1))); + MqttPublish(stopic, Settings.flag.mqtt_power_retain); + } +#ifdef USE_SONOFF_IFAN + } +#endif +} + +void MqttPublishAllPowerState(void) +{ + for (uint32_t i = 1; i <= devices_present; i++) { + MqttPublishPowerState(i); +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { break; } +#endif + } +} + +void MqttPublishPowerBlinkState(uint32_t device) +{ + char scommand[33]; + + if ((device < 1) || (device > devices_present)) { + device = 1; + } + Response_P(PSTR("{\"%s\":\"" D_JSON_BLINK " %s\"}"), + GetPowerDevice(scommand, device, sizeof(scommand), Settings.flag.device_index_enable), GetStateText(bitRead(blink_mask, device -1))); + + MqttPublishPrefixTopic_P(RESULT_OR_STAT, S_RSLT_POWER); +} + + + +uint16_t MqttConnectCount(void) +{ + return Mqtt.connect_count; +} + +void MqttDisconnected(int state) +{ + Mqtt.connected = false; + Mqtt.retry_counter = Settings.mqtt_retry; + + MqttClient.disconnect(); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), SettingsText(SET_MQTT_HOST), Settings.mqtt_port, state, Mqtt.retry_counter); + rules_flag.mqtt_disconnected = 1; +} + +void MqttConnected(void) +{ + char stopic[TOPSZ]; + + if (Mqtt.allowed) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_CONNECTED)); + Mqtt.connected = true; + Mqtt.retry_counter = 0; + Mqtt.connect_count++; + + GetTopic_P(stopic, TELE, mqtt_topic, S_LWT); + Response_P(PSTR(D_ONLINE)); + MqttPublish(stopic, true); + + if (!Settings.flag4.only_json_message) { + + mqtt_data[0] = '\0'; + MqttPublishPrefixTopic_P(CMND, S_RSLT_POWER); + } + + GetTopic_P(stopic, CMND, mqtt_topic, PSTR("#")); + MqttSubscribe(stopic); + if (strstr_P(SettingsText(SET_MQTT_FULLTOPIC), MQTT_TOKEN_TOPIC) != nullptr) { + uint32_t real_index = SET_MQTT_GRP_TOPIC; + for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { + if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + if (strlen(SettingsText(real_index +i))) { + GetGroupTopic_P(stopic, PSTR("#"), real_index +i); + MqttSubscribe(stopic); + } + } + GetFallbackTopic_P(stopic, PSTR("#")); + MqttSubscribe(stopic); + } + + XdrvCall(FUNC_MQTT_SUBSCRIBE); + } + + if (Mqtt.initial_connection_state) { + if (ResetReason() != REASON_DEEP_SLEEP_AWAKE) { + char stopic2[TOPSZ]; + Response_P(PSTR("{\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_FALLBACKTOPIC "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\"}"), + ModuleName().c_str(), my_version, my_image, GetFallbackTopic_P(stopic, ""), GetGroupTopic_P(stopic2, "", SET_MQTT_GRP_TOPIC)); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "1")); +#ifdef USE_WEBSERVER + if (Settings.webserver) { +#if LWIP_IPV6 + Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"IPv6Address\":\"%s\"}"), + (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str(),WifiGetIPv6().c_str()); +#else + Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\"}"), + (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str()); +#endif + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "2")); + } +#endif + Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":")); + if (CrashFlag()) { + CrashDump(); + } else { + ResponseAppend_P(PSTR("\"%s\""), GetResetReason().c_str()); + } + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "3")); + } + + MqttPublishAllPowerState(); + if (Settings.tele_period) { + tele_period = Settings.tele_period -5; + } + rules_flag.system_boot = 1; + XdrvCall(FUNC_MQTT_INIT); + } + Mqtt.initial_connection_state = 0; + + global_state.mqtt_down = 0; + if (Settings.flag.mqtt_enabled) { + rules_flag.mqtt_connected = 1; + } +} + +void MqttReconnect(void) +{ + char stopic[TOPSZ]; + + Mqtt.allowed = Settings.flag.mqtt_enabled; + if (Mqtt.allowed) { +#ifdef USE_DISCOVERY +#ifdef MQTT_HOST_DISCOVERY + MqttDiscoverServer(); +#endif +#endif + if (!strlen(SettingsText(SET_MQTT_HOST)) || !Settings.mqtt_port) { + Mqtt.allowed = false; + } +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + + if (!AWS_IoT_Private_Key || !AWS_IoT_Client_Certificate) { + Mqtt.allowed = false; + } +#endif + } + if (!Mqtt.allowed) { + MqttConnected(); + return; + } + +#ifdef USE_EMULATION + UdpDisconnect(); +#endif + + AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_ATTEMPTING_CONNECTION)); + + Mqtt.connected = false; + Mqtt.retry_counter = Settings.mqtt_retry; + global_state.mqtt_down = 1; + + char *mqtt_user = nullptr; + char *mqtt_pwd = nullptr; + if (strlen(SettingsText(SET_MQTT_USER))) { + mqtt_user = SettingsText(SET_MQTT_USER); + } + if (strlen(SettingsText(SET_MQTT_PWD))) { + mqtt_pwd = SettingsText(SET_MQTT_PWD); + } + + GetTopic_P(stopic, TELE, mqtt_topic, S_LWT); + Response_P(S_OFFLINE); + + if (MqttClient.connected()) { MqttClient.disconnect(); } +#ifdef USE_MQTT_TLS + tlsClient->stop(); +#else + EspClient = WiFiClient(); + MqttClient.setClient(EspClient); +#endif + + if (2 == Mqtt.initial_connection_state) { + Mqtt.initial_connection_state = 1; + } + + MqttClient.setCallback(MqttDataHandler); +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + + tlsClient->setClientECCert(AWS_IoT_Client_Certificate, + AWS_IoT_Private_Key, + 0xFFFF , 0); +#endif + MqttClient.setServer(SettingsText(SET_MQTT_HOST), Settings.mqtt_port); + + uint32_t mqtt_connect_time = millis(); +#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) + bool allow_all_fingerprints = false; + bool learn_fingerprint1 = is_fingerprint_mono_value(Settings.mqtt_fingerprint[0], 0x00); + bool learn_fingerprint2 = is_fingerprint_mono_value(Settings.mqtt_fingerprint[1], 0x00); + allow_all_fingerprints |= is_fingerprint_mono_value(Settings.mqtt_fingerprint[0], 0xff); + allow_all_fingerprints |= is_fingerprint_mono_value(Settings.mqtt_fingerprint[1], 0xff); + allow_all_fingerprints |= learn_fingerprint1; + allow_all_fingerprints |= learn_fingerprint2; + tlsClient->setPubKeyFingerprint(Settings.mqtt_fingerprint[0], Settings.mqtt_fingerprint[1], allow_all_fingerprints); +#endif +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "AWS IoT endpoint: %s"), SettingsText(SET_MQTT_HOST)); + if (MqttClient.connect(mqtt_client, nullptr, nullptr, stopic, 1, false, mqtt_data, MQTT_CLEAN_SESSION)) { +#else + if (MqttClient.connect(mqtt_client, mqtt_user, mqtt_pwd, stopic, 1, true, mqtt_data, MQTT_CLEAN_SESSION)) { +#endif +#ifdef USE_MQTT_TLS + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "TLS connected in %d ms, max ThunkStack used %d"), + millis() - mqtt_connect_time, tlsClient->getMaxThunkStackUse()); + if (!tlsClient->getMFLNStatus()) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR("MFLN not supported by TLS server")); + } +#ifndef USE_MQTT_TLS_CA_CERT + + char buf_fingerprint[64]; + ToHex_P((unsigned char *)tlsClient->getRecvPubKeyFingerprint(), 20, buf_fingerprint, sizeof(buf_fingerprint), ' '); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT "Server fingerprint: %s"), buf_fingerprint); + + if (learn_fingerprint1 || learn_fingerprint2) { + + bool fingerprint_matched = false; + const uint8_t *recv_fingerprint = tlsClient->getRecvPubKeyFingerprint(); + if (0 == memcmp(recv_fingerprint, Settings.mqtt_fingerprint[0], 20)) { + fingerprint_matched = true; + } + if (0 == memcmp(recv_fingerprint, Settings.mqtt_fingerprint[1], 20)) { + fingerprint_matched = true; + } + if (!fingerprint_matched) { + + if (learn_fingerprint1) { + memcpy(Settings.mqtt_fingerprint[0], recv_fingerprint, 20); + } + if (learn_fingerprint2) { + memcpy(Settings.mqtt_fingerprint[1], recv_fingerprint, 20); + } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "Fingerprint learned: %s"), buf_fingerprint); + + SettingsSaveAll(); + } + } +#endif +#endif + MqttConnected(); + } else { +#ifdef USE_MQTT_TLS + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "TLS connection error: %d"), tlsClient->getLastError()); +#endif + MqttDisconnected(MqttClient.state()); + } +} + +void MqttCheck(void) +{ + if (Settings.flag.mqtt_enabled) { + if (!MqttIsConnected()) { + global_state.mqtt_down = 1; + if (!Mqtt.retry_counter) { + MqttReconnect(); + } else { + Mqtt.retry_counter--; + } + } else { + global_state.mqtt_down = 0; + } + } else { + global_state.mqtt_down = 0; + if (Mqtt.initial_connection_state) { + MqttReconnect(); + } + } +} + +bool KeyTopicActive(uint32_t key) +{ + + + key &= 1; + char key_topic[TOPSZ]; + Format(key_topic, SettingsText(SET_MQTT_BUTTON_TOPIC + key), sizeof(key_topic)); + return ((strlen(key_topic) != 0) && strcmp(key_topic, "0")); +} + + + + + +#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) +void CmndMqttFingerprint(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { + char fingerprint[60]; + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(fingerprint))) { + strlcpy(fingerprint, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? MQTT_FINGERPRINT1 : MQTT_FINGERPRINT2 : XdrvMailbox.data, sizeof(fingerprint)); + char *p = fingerprint; + for (uint32_t i = 0; i < 20; i++) { + Settings.mqtt_fingerprint[XdrvMailbox.index -1][i] = strtol(p, &p, 16); + } + restart_flag = 2; + } + ResponseCmndIdxChar(ToHex_P((unsigned char *)Settings.mqtt_fingerprint[XdrvMailbox.index -1], 20, fingerprint, sizeof(fingerprint), ' ')); + } +} +#endif + +void CmndMqttUser(void) +{ + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_MQTT_USER, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_USER : XdrvMailbox.data); + restart_flag = 2; + } + ResponseCmndChar(SettingsText(SET_MQTT_USER)); +} + +void CmndMqttPassword(void) +{ + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_MQTT_PWD, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_PASS : XdrvMailbox.data); + ResponseCmndChar(SettingsText(SET_MQTT_PWD)); + restart_flag = 2; + } else { + Response_P(S_JSON_COMMAND_ASTERISK, XdrvMailbox.command); + } +} + +void CmndMqttlog(void) +{ + if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { + Settings.mqttlog_level = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.mqttlog_level); +} + +void CmndMqttHost(void) +{ + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_MQTT_HOST, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_HOST : XdrvMailbox.data); + restart_flag = 2; + } + ResponseCmndChar(SettingsText(SET_MQTT_HOST)); +} + +void CmndMqttPort(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 65536)) { + Settings.mqtt_port = (1 == XdrvMailbox.payload) ? MQTT_PORT : XdrvMailbox.payload; + restart_flag = 2; + } + ResponseCmndNumber(Settings.mqtt_port); +} + +void CmndMqttRetry(void) +{ + if ((XdrvMailbox.payload >= MQTT_RETRY_SECS) && (XdrvMailbox.payload < 32001)) { + Settings.mqtt_retry = XdrvMailbox.payload; + Mqtt.retry_counter = Settings.mqtt_retry; + } + ResponseCmndNumber(Settings.mqtt_retry); +} + +void CmndStateText(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_STATE_TEXT)) { + if (!XdrvMailbox.usridx) { + ResponseCmndAll(SET_STATE_TXT1, MAX_STATE_TEXT); + } else { + if (XdrvMailbox.data_len > 0) { + for (uint32_t i = 0; i <= XdrvMailbox.data_len; i++) { + if (XdrvMailbox.data[i] == ' ') XdrvMailbox.data[i] = '_'; + } + SettingsUpdateText(SET_STATE_TXT1 + XdrvMailbox.index -1, XdrvMailbox.data); + } + ResponseCmndIdxChar(GetStateText(XdrvMailbox.index -1)); + } + } +} + +void CmndMqttClient(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { + SettingsUpdateText(SET_MQTT_CLIENT, (SC_DEFAULT == Shortcut()) ? MQTT_CLIENT_ID : XdrvMailbox.data); + restart_flag = 2; + } + ResponseCmndChar(SettingsText(SET_MQTT_CLIENT)); +} + +void CmndFullTopic(void) +{ + if (XdrvMailbox.data_len > 0) { + MakeValidMqtt(1, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + char stemp1[TOPSZ]; + strlcpy(stemp1, (SC_DEFAULT == Shortcut()) ? MQTT_FULLTOPIC : XdrvMailbox.data, sizeof(stemp1)); + if (strcmp(stemp1, SettingsText(SET_MQTT_FULLTOPIC))) { + Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); + MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); + SettingsUpdateText(SET_MQTT_FULLTOPIC, stemp1); + restart_flag = 2; + } + } + ResponseCmndChar(SettingsText(SET_MQTT_FULLTOPIC)); +} + +void CmndPrefix(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_MQTT_PREFIXES)) { + if (!XdrvMailbox.usridx) { + ResponseCmndAll(SET_MQTTPREFIX1, MAX_MQTT_PREFIXES); + } else { + if (XdrvMailbox.data_len > 0) { + MakeValidMqtt(0, XdrvMailbox.data); + SettingsUpdateText(SET_MQTTPREFIX1 + XdrvMailbox.index -1, + (SC_DEFAULT == Shortcut()) ? (1==XdrvMailbox.index) ? SUB_PREFIX : (2==XdrvMailbox.index) ? PUB_PREFIX : PUB_PREFIX2 : XdrvMailbox.data); + restart_flag = 2; + } + ResponseCmndIdxChar(SettingsText(SET_MQTTPREFIX1 + XdrvMailbox.index -1)); + } + } +} + +void CmndPublish(void) +{ + if (XdrvMailbox.data_len > 0) { + char *payload_part; + char *mqtt_part = strtok_r(XdrvMailbox.data, " ", &payload_part); + if (mqtt_part) { + char stemp1[TOPSZ]; + strlcpy(stemp1, mqtt_part, sizeof(stemp1)); + if ((payload_part != nullptr) && strlen(payload_part)) { + strlcpy(mqtt_data, payload_part, sizeof(mqtt_data)); + } else { + mqtt_data[0] = '\0'; + } + MqttPublish(stemp1, (XdrvMailbox.index == 2)); + + mqtt_data[0] = '\0'; + } + } +} + +void CmndGroupTopic(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_GROUP_TOPICS)) { + if (XdrvMailbox.data_len > 0) { + uint32_t settings_text_index = (1 == XdrvMailbox.index) ? SET_MQTT_GRP_TOPIC : SET_MQTT_GRP_TOPIC2 + XdrvMailbox.index - 2; + MakeValidMqtt(0, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + SettingsUpdateText(settings_text_index, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data); + + + char stemp[MAX_GROUP_TOPICS][TOPSZ]; + uint32_t read_index = 0; + uint32_t real_index = SET_MQTT_GRP_TOPIC; + for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { + if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + if (strlen(SettingsText(real_index +i))) { + bool not_equal = true; + for (uint32_t j = 0; j < read_index; j++) { + if (!strcmp(SettingsText(real_index +i), stemp[j])) { + not_equal = false; + } + } + if (not_equal) { + strncpy(stemp[read_index], SettingsText(real_index +i), sizeof(stemp[read_index])); + read_index++; + } + } + } + if (0 == read_index) { + SettingsUpdateText(SET_MQTT_GRP_TOPIC, MQTT_GRPTOPIC); + } else { + uint32_t write_index = 0; + uint32_t real_index = SET_MQTT_GRP_TOPIC; + for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { + if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } + if (write_index < read_index) { + SettingsUpdateText(real_index +i, stemp[write_index]); + write_index++; + } else { + SettingsUpdateText(real_index +i, ""); + } + } + } + + restart_flag = 2; + } + ResponseCmndAll(SET_MQTT_GRP_TOPIC, MAX_GROUP_TOPICS); + } +} + +void CmndTopic(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { + MakeValidMqtt(0, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + char stemp1[TOPSZ]; + strlcpy(stemp1, (SC_DEFAULT == Shortcut()) ? MQTT_TOPIC : XdrvMailbox.data, sizeof(stemp1)); + if (strcmp(stemp1, SettingsText(SET_MQTT_TOPIC))) { + Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); + MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); + SettingsUpdateText(SET_MQTT_TOPIC, stemp1); + restart_flag = 2; + } + } + ResponseCmndChar(SettingsText(SET_MQTT_TOPIC)); +} + +void CmndButtonTopic(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { + MakeValidMqtt(0, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + switch (Shortcut()) { + case SC_CLEAR: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, ""); break; + case SC_DEFAULT: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, mqtt_topic); break; + case SC_USER: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, MQTT_BUTTON_TOPIC); break; + default: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, XdrvMailbox.data); + } + } + ResponseCmndChar(SettingsText(SET_MQTT_BUTTON_TOPIC)); +} + +void CmndSwitchTopic(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { + MakeValidMqtt(0, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + switch (Shortcut()) { + case SC_CLEAR: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, ""); break; + case SC_DEFAULT: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, mqtt_topic); break; + case SC_USER: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, MQTT_SWITCH_TOPIC); break; + default: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, XdrvMailbox.data); + } + } + ResponseCmndChar(SettingsText(SET_MQTT_SWITCH_TOPIC)); +} + +void CmndButtonRetain(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + if (!XdrvMailbox.payload) { + for (uint32_t i = 1; i <= MAX_KEYS; i++) { + SendKey(KEY_BUTTON, i, CLEAR_RETAIN); + } + } + Settings.flag.mqtt_button_retain = XdrvMailbox.payload; + } + ResponseCmndStateText(Settings.flag.mqtt_button_retain); +} + +void CmndSwitchRetain(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + if (!XdrvMailbox.payload) { + for (uint32_t i = 1; i <= MAX_SWITCHES; i++) { + SendKey(KEY_SWITCH, i, CLEAR_RETAIN); + } + } + Settings.flag.mqtt_switch_retain = XdrvMailbox.payload; + } + ResponseCmndStateText(Settings.flag.mqtt_switch_retain); +} + +void CmndPowerRetain(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + if (!XdrvMailbox.payload) { + char stemp1[TOPSZ]; + char scommand[CMDSZ]; + for (uint32_t i = 1; i <= devices_present; i++) { + GetTopic_P(stemp1, STAT, mqtt_topic, GetPowerDevice(scommand, i, sizeof(scommand), Settings.flag.device_index_enable)); + mqtt_data[0] = '\0'; + MqttPublish(stemp1, Settings.flag.mqtt_power_retain); + } + } + Settings.flag.mqtt_power_retain = XdrvMailbox.payload; + } + ResponseCmndStateText(Settings.flag.mqtt_power_retain); +} + +void CmndSensorRetain(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + if (!XdrvMailbox.payload) { + mqtt_data[0] = '\0'; + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_ENERGY), Settings.flag.mqtt_sensor_retain); + } + Settings.flag.mqtt_sensor_retain = XdrvMailbox.payload; + } + ResponseCmndStateText(Settings.flag.mqtt_sensor_retain); +} + + + + +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + + + +const static uint16_t tls_spi_start_sector = 0xFF; +const static uint8_t* tls_spi_start = (uint8_t*) 0x402FF000; +const static size_t tls_spi_len = 0x1000; +const static size_t tls_block_offset = 0x0400; +const static size_t tls_block_len = 0x0400; +const static size_t tls_obj_store_offset = tls_block_offset + sizeof(tls_dir_t); + + +inline void TlsEraseBuffer(uint8_t *buffer) { + memset(buffer + tls_block_offset, 0xFF, tls_block_len); +} + + + +static br_ec_private_key EC = { + 23, + nullptr, 0 +}; + +static br_x509_certificate CHAIN[] = { + { nullptr, 0 } +}; + + + +void loadTlsDir(void) { + memcpy_P(&tls_dir, tls_spi_start + tls_block_offset, sizeof(tls_dir)); + + + if ((TLS_NAME_SKEY == tls_dir.entry[0].name) && (tls_dir.entry[0].len > 0)) { + EC.x = (unsigned char *)(tls_spi_start + tls_obj_store_offset + tls_dir.entry[0].start); + EC.xlen = tls_dir.entry[0].len; + AWS_IoT_Private_Key = &EC; + } else { + AWS_IoT_Private_Key = nullptr; + } + if ((TLS_NAME_CRT == tls_dir.entry[1].name) && (tls_dir.entry[1].len > 0)) { + CHAIN[0].data = (unsigned char *) (tls_spi_start + tls_obj_store_offset + tls_dir.entry[1].start); + CHAIN[0].data_len = tls_dir.entry[1].len; + AWS_IoT_Client_Certificate = CHAIN; + } else { + AWS_IoT_Client_Certificate = nullptr; + } + +} + +const char ALLOCATE_ERROR[] PROGMEM = "TLSKey " D_JSON_ERROR ": cannot allocate buffer."; + +void CmndTlsKey(void) { +#ifdef DEBUG_DUMP_TLS + if (0 == XdrvMailbox.index){ + CmndTlsDump(); + } +#endif + if ((XdrvMailbox.index >= 1) && (XdrvMailbox.index <= 2)) { + tls_dir_t *tls_dir_write; + + if (XdrvMailbox.data_len > 0) { + + uint8_t *spi_buffer = (uint8_t*) malloc(tls_spi_len); + if (!spi_buffer) { + AddLog_P(LOG_LEVEL_ERROR, ALLOCATE_ERROR); + return; + } + memcpy_P(spi_buffer, tls_spi_start, tls_spi_len); + + + RemoveAllSpaces(XdrvMailbox.data); + + + uint32_t bin_len = decode_base64_length((unsigned char*)XdrvMailbox.data); + uint8_t *bin_buf = nullptr; + if (bin_len > 0) { + bin_buf = (uint8_t*) malloc(bin_len + 4); + if (!bin_buf) { + AddLog_P(LOG_LEVEL_ERROR, ALLOCATE_ERROR); + free(spi_buffer); + return; + } + } + + + if (bin_len > 0) { + decode_base64((unsigned char*)XdrvMailbox.data, bin_buf); + } + + + tls_dir_write = (tls_dir_t*) (spi_buffer + tls_block_offset); + + if (1 == XdrvMailbox.index) { + + + TlsEraseBuffer(spi_buffer); + if (bin_len > 0) { + if (bin_len != 32) { + + AddLog_P2(LOG_LEVEL_INFO, PSTR("TLSKey: Certificate must be 32 bytes: %d."), bin_len); + free(spi_buffer); + free(bin_buf); + return; + } + tls_entry_t *entry = &tls_dir_write->entry[0]; + entry->name = TLS_NAME_SKEY; + entry->start = 0; + entry->len = bin_len; + memcpy(spi_buffer + tls_obj_store_offset + entry->start, bin_buf, entry->len); + } else { + + } + } else if (2 == XdrvMailbox.index) { + + if (TLS_NAME_SKEY != tls_dir.entry[0].name) { + + AddLog_P(LOG_LEVEL_INFO, PSTR("TLSKey: cannot store Cert if no Key previously stored.")); + free(spi_buffer); + free(bin_buf); + return; + } + if (bin_len <= 256) { + + AddLog_P2(LOG_LEVEL_INFO, PSTR("TLSKey: Certificate length too short: %d."), bin_len); + free(spi_buffer); + free(bin_buf); + return; + } + tls_entry_t *entry = &tls_dir_write->entry[1]; + entry->name = TLS_NAME_CRT; + entry->start = (tls_dir_write->entry[0].start + tls_dir_write->entry[0].len + 3) & ~0x03; + entry->len = bin_len; + memcpy(spi_buffer + tls_obj_store_offset + entry->start, bin_buf, entry->len); + } + + if (ESP.flashEraseSector(tls_spi_start_sector)) { + ESP.flashWrite(tls_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); + } + free(spi_buffer); + free(bin_buf); + } + + loadTlsDir(); + Response_P(PSTR("{\"%s1\":%d,\"%s2\":%d}"), + XdrvMailbox.command, AWS_IoT_Private_Key ? tls_dir.entry[0].len : -1, + XdrvMailbox.command, AWS_IoT_Client_Certificate ? tls_dir.entry[1].len : -1); + } +} + +#ifdef DEBUG_DUMP_TLS + +uint32_t bswap32(uint32_t x) { + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} +void CmndTlsDump(void) { + uint32_t start = (uint32_t)tls_spi_start + tls_block_offset; + uint32_t end = start + tls_block_len -1; + for (uint32_t pos = start; pos < end; pos += 0x10) { + uint32_t* values = (uint32_t*)(pos); +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 + Serial.printf("%08x: %08x %08x %08x %08x\n", pos, bswap32(values[0]), bswap32(values[1]), bswap32(values[2]), bswap32(values[3])); +#else + Serial.printf_P(PSTR("%08x: %08x %08x %08x %08x\n"), pos, bswap32(values[0]), bswap32(values[1]), bswap32(values[2]), bswap32(values[3])); +#endif + } +} +#endif +#endif + + + + + +#ifdef USE_WEBSERVER + +#define WEB_HANDLE_MQTT "mq" + +const char S_CONFIGURE_MQTT[] PROGMEM = D_CONFIGURE_MQTT; + +const char HTTP_BTN_MENU_MQTT[] PROGMEM = + "

"; + +const char HTTP_FORM_MQTT1[] PROGMEM = + "
 " D_MQTT_PARAMETERS " " + "
" + "

" D_HOST " (" MQTT_HOST ")

" + "

" D_PORT " (" STR(MQTT_PORT) ")

" + "

" D_CLIENT " (%s)

"; +const char HTTP_FORM_MQTT2[] PROGMEM = + "

" D_USER " (" MQTT_USER ")

" + "


" + "

" D_TOPIC " = %%topic%% (%s)

" + "

" D_FULL_TOPIC " (%s)

"; + +void HandleMqttConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MQTT); + + if (Webserver->hasArg("save")) { + MqttSaveSettings(); + WebRestart(1); + return; + } + + char str[TOPSZ]; + + WSContentStart_P(S_CONFIGURE_MQTT); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_MQTT1, + SettingsText(SET_MQTT_HOST), + Settings.mqtt_port, + Format(str, MQTT_CLIENT_ID, sizeof(str)), MQTT_CLIENT_ID, SettingsText(SET_MQTT_CLIENT)); + WSContentSend_P(HTTP_FORM_MQTT2, + (!strlen(SettingsText(SET_MQTT_USER))) ? "0" : SettingsText(SET_MQTT_USER), + Format(str, MQTT_TOPIC, sizeof(str)), MQTT_TOPIC, SettingsText(SET_MQTT_TOPIC), + MQTT_FULLTOPIC, MQTT_FULLTOPIC, SettingsText(SET_MQTT_FULLTOPIC)); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void MqttSaveSettings(void) +{ + char tmp[TOPSZ]; + char stemp[TOPSZ]; + char stemp2[TOPSZ]; + + WebGetArg("mt", tmp, sizeof(tmp)); + strlcpy(stemp, (!strlen(tmp)) ? MQTT_TOPIC : tmp, sizeof(stemp)); + MakeValidMqtt(0, stemp); + WebGetArg("mf", tmp, sizeof(tmp)); + strlcpy(stemp2, (!strlen(tmp)) ? MQTT_FULLTOPIC : tmp, sizeof(stemp2)); + MakeValidMqtt(1, stemp2); + if ((strcmp(stemp, SettingsText(SET_MQTT_TOPIC))) || (strcmp(stemp2, SettingsText(SET_MQTT_FULLTOPIC)))) { + Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); + MqttPublishPrefixTopic_P(TELE, S_LWT, true); + } + SettingsUpdateText(SET_MQTT_TOPIC, stemp); + SettingsUpdateText(SET_MQTT_FULLTOPIC, stemp2); + WebGetArg("mh", tmp, sizeof(tmp)); + SettingsUpdateText(SET_MQTT_HOST, (!strlen(tmp)) ? MQTT_HOST : (!strcmp(tmp,"0")) ? "" : tmp); + WebGetArg("ml", tmp, sizeof(tmp)); + Settings.mqtt_port = (!strlen(tmp)) ? MQTT_PORT : atoi(tmp); + WebGetArg("mc", tmp, sizeof(tmp)); + SettingsUpdateText(SET_MQTT_CLIENT, (!strlen(tmp)) ? MQTT_CLIENT_ID : tmp); +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"), + SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), SettingsText(SET_MQTT_TOPIC), SettingsText(SET_MQTT_FULLTOPIC)); +#else + WebGetArg("mu", tmp, sizeof(tmp)); + SettingsUpdateText(SET_MQTT_USER, (!strlen(tmp)) ? MQTT_USER : (!strcmp(tmp,"0")) ? "" : tmp); + WebGetArg("mp", tmp, sizeof(tmp)); + SettingsUpdateText(SET_MQTT_PWD, (!strlen(tmp)) ? "" : (!strcmp(tmp, D_ASTERISK_PWD)) ? SettingsText(SET_MQTT_PWD) : tmp); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_MQTTUSER " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"), + SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), SettingsText(SET_MQTT_USER), SettingsText(SET_MQTT_TOPIC), SettingsText(SET_MQTT_FULLTOPIC)); +#endif +} +#endif + + + + + +bool Xdrv02(uint8_t function) +{ + bool result = false; + + if (Settings.flag.mqtt_enabled) { + switch (function) { + case FUNC_PRE_INIT: + MqttInit(); + break; + case FUNC_EVERY_50_MSECOND: + MqttClient.loop(); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_MQTT); + break; + case FUNC_WEB_ADD_HANDLER: + Webserver->on("/" WEB_HANDLE_MQTT, HandleMqttConfiguration); + break; +#endif + case FUNC_COMMAND: + result = DecodeCommand(kMqttCommands, MqttCommand); + break; + } + } + return result; +} +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_03_energy.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_03_energy.ino" +#ifdef USE_ENERGY_SENSOR + + + + +#define XDRV_03 3 +#define XSNS_03 3 + + + + +#define ENERGY_NONE 0 +#define ENERGY_WATCHDOG 4 + +#include + +#define D_CMND_POWERCAL "PowerCal" +#define D_CMND_VOLTAGECAL "VoltageCal" +#define D_CMND_CURRENTCAL "CurrentCal" +#define D_CMND_TARIFF "Tariff" +#define D_CMND_MODULEADDRESS "ModuleAddress" + +enum EnergyCommands { + CMND_POWERCAL, CMND_VOLTAGECAL, CMND_CURRENTCAL, + CMND_POWERSET, CMND_VOLTAGESET, CMND_CURRENTSET, CMND_FREQUENCYSET, CMND_MODULEADDRESS }; + +const char kEnergyCommands[] PROGMEM = "|" + D_CMND_POWERCAL "|" D_CMND_VOLTAGECAL "|" D_CMND_CURRENTCAL "|" + D_CMND_POWERSET "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTSET "|" D_CMND_FREQUENCYSET "|" D_CMND_MODULEADDRESS "|" +#ifdef USE_ENERGY_MARGIN_DETECTION + D_CMND_POWERDELTA "|" D_CMND_POWERLOW "|" D_CMND_POWERHIGH "|" D_CMND_VOLTAGELOW "|" D_CMND_VOLTAGEHIGH "|" D_CMND_CURRENTLOW "|" D_CMND_CURRENTHIGH "|" +#ifdef USE_ENERGY_POWER_LIMIT + D_CMND_MAXENERGY "|" D_CMND_MAXENERGYSTART "|" + D_CMND_MAXPOWER "|" D_CMND_MAXPOWERHOLD "|" D_CMND_MAXPOWERWINDOW "|" + D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW "|" +#endif +#endif + D_CMND_ENERGYRESET "|" D_CMND_TARIFF ; + +void (* const EnergyCommand[])(void) PROGMEM = { + &CmndPowerCal, &CmndVoltageCal, &CmndCurrentCal, + &CmndPowerSet, &CmndVoltageSet, &CmndCurrentSet, &CmndFrequencySet, &CmndModuleAddress, +#ifdef USE_ENERGY_MARGIN_DETECTION + &CmndPowerDelta, &CmndPowerLow, &CmndPowerHigh, &CmndVoltageLow, &CmndVoltageHigh, &CmndCurrentLow, &CmndCurrentHigh, +#ifdef USE_ENERGY_POWER_LIMIT + &CmndMaxEnergy, &CmndMaxEnergyStart, + &CmndMaxPower, &CmndMaxPowerHold, &CmndMaxPowerWindow, + &CmndSafePower, &CmndSafePowerHold, &CmndSafePowerWindow, +#endif +#endif + &CmndEnergyReset, &CmndTariff }; + +const char kEnergyPhases[] PROGMEM = "|%s / %s|%s / %s / %s||[%s,%s]|[%s,%s,%s]"; + +struct ENERGY { + float voltage[3] = { 0, 0, 0 }; + float current[3] = { 0, 0, 0 }; + float active_power[3] = { 0, 0, 0 }; + float apparent_power[3] = { NAN, NAN, NAN }; + float reactive_power[3] = { NAN, NAN, NAN }; + float power_factor[3] = { NAN, NAN, NAN }; + float frequency[3] = { NAN, NAN, NAN }; + + float start_energy = 0; + float daily = 0; + float total = 0; + float export_active = NAN; + + unsigned long kWhtoday_delta = 0; + unsigned long kWhtoday_offset = 0; + unsigned long kWhtoday; + unsigned long period = 0; + + uint8_t fifth_second = 0; + uint8_t command_code = 0; + uint8_t data_valid[3] = { 0, 0, 0 }; + + uint8_t phase_count = 1; + bool voltage_common = false; + + bool voltage_available = true; + bool current_available = true; + + bool type_dc = false; + bool power_on = true; + +#ifdef USE_ENERGY_MARGIN_DETECTION + uint16_t power_history[3] = { 0 }; + uint8_t power_steady_counter = 8; + bool power_delta = false; + bool min_power_flag = false; + bool max_power_flag = false; + bool min_voltage_flag = false; + bool max_voltage_flag = false; + bool min_current_flag = false; + bool max_current_flag = false; + +#ifdef USE_ENERGY_POWER_LIMIT + uint16_t mplh_counter = 0; + uint16_t mplw_counter = 0; + uint8_t mplr_counter = 0; + uint8_t max_energy_state = 0; +#endif +#endif +} Energy; + +Ticker ticker_energy; + + + +bool EnergyTariff1Active() +{ + uint8_t dst = 0; + if (IsDst() && (Settings.tariff[0][1] != Settings.tariff[1][1])) { + dst = 1; + } + if (Settings.tariff[0][dst] != Settings.tariff[1][dst]) { + if (Settings.flag3.energy_weekend && ((RtcTime.day_of_week == 1) || + (RtcTime.day_of_week == 7))) { + return true; + } + uint32_t minutes = MinutesPastMidnight(); + if (Settings.tariff[0][dst] > Settings.tariff[1][dst]) { + + return ((minutes >= Settings.tariff[0][dst]) || (minutes < Settings.tariff[1][dst])); + } else { + + return ((minutes >= Settings.tariff[0][dst]) && (minutes < Settings.tariff[1][dst])); + } + } else { + return false; + } +} + +void EnergyUpdateToday(void) +{ + if (Energy.kWhtoday_delta > 1000) { + unsigned long delta = Energy.kWhtoday_delta / 1000; + Energy.kWhtoday_delta -= (delta * 1000); + Energy.kWhtoday += delta; + } + + 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.valid){ + + uint32_t energy_diff = (uint32_t)(Energy.total * 100000) - RtcSettings.energy_usage.last_usage_kWhtotal; + RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total * 100000); + + uint32_t return_diff = 0; + if (!isnan(Energy.export_active)) { + return_diff = (uint32_t)(Energy.export_active * 100000) - RtcSettings.energy_usage.last_return_kWhtotal; + RtcSettings.energy_usage.last_return_kWhtotal = (uint32_t)(Energy.export_active * 100000); + } + + if (EnergyTariff1Active()) { + RtcSettings.energy_usage.usage1_kWhtotal += energy_diff; + RtcSettings.energy_usage.return1_kWhtotal += return_diff; + } else { + RtcSettings.energy_usage.usage2_kWhtotal += energy_diff; + RtcSettings.energy_usage.return2_kWhtotal += return_diff; + } + } +} + +void EnergyUpdateTotal(float value, bool kwh) +{ + + + + + uint32_t multiplier = (kwh) ? 100000 : 100; + + if (0 == Energy.start_energy || (value < Energy.start_energy)) { + Energy.start_energy = value; + } + else if (value != Energy.start_energy) { + Energy.kWhtoday = (unsigned long)((value - Energy.start_energy) * multiplier); + } + + if ((Energy.total < (value - 0.01)) && + Settings.flag3.hardware_energy_total) { + RtcSettings.energy_kWhtotal = (unsigned long)((value * multiplier) - Energy.kWhtoday_offset - Energy.kWhtoday); + Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; + Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000; + Settings.energy_kWhtotal_time = (!Energy.kWhtoday_offset) ? LocalTime() : Midnight(); + + } + EnergyUpdateToday(); +} + + + +void Energy200ms(void) +{ + Energy.power_on = (power != 0) | Settings.flag.no_power_on_check; + + Energy.fifth_second++; + if (5 == Energy.fifth_second) { + Energy.fifth_second = 0; + + XnrgCall(FUNC_ENERGY_EVERY_SECOND); + + if (RtcTime.valid) { + if (LocalTime() == Midnight()) { + Settings.energy_kWhyesterday = RtcSettings.energy_kWhtoday; + + 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; + + Energy.kWhtoday_delta = 0; + Energy.period = Energy.kWhtoday; + EnergyUpdateToday(); +#if defined(USE_ENERGY_MARGIN_DETECTION) && defined(USE_ENERGY_POWER_LIMIT) + Energy.max_energy_state = 3; +#endif + } +#if defined(USE_ENERGY_MARGIN_DETECTION) && defined(USE_ENERGY_POWER_LIMIT) + if ((RtcTime.hour == Settings.energy_max_energy_start) && (3 == Energy.max_energy_state )) { + Energy.max_energy_state = 0; + } +#endif + + } + } + + XnrgCall(FUNC_EVERY_200_MSECOND); +} + +void EnergySaveState(void) +{ + Settings.energy_kWhdoy = (RtcTime.valid) ? RtcTime.day_of_year : 0; + + Settings.energy_kWhtoday = RtcSettings.energy_kWhtoday; + Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; + + Settings.energy_usage = RtcSettings.energy_usage; +} + +#ifdef USE_ENERGY_MARGIN_DETECTION +bool EnergyMargin(bool type, uint16_t margin, uint16_t value, bool &flag, bool &save_flag) +{ + bool change; + + if (!margin) return false; + change = save_flag; + if (type) { + flag = (value > margin); + } else { + flag = (value < margin); + } + save_flag = flag; + return (change != save_flag); +} + +void EnergyMarginCheck(void) +{ + if (Energy.power_steady_counter) { + Energy.power_steady_counter--; + return; + } + + uint16_t energy_power_u = (uint16_t)(Energy.active_power[0]); + + if (Settings.energy_power_delta) { + uint16_t delta = abs(Energy.power_history[0] - energy_power_u); + if (delta > 0) { + if (Settings.energy_power_delta < 101) { + uint16_t min_power = (Energy.power_history[0] > energy_power_u) ? energy_power_u : Energy.power_history[0]; + if (0 == min_power) { min_power++; } + if (((delta * 100) / min_power) > Settings.energy_power_delta) { + Energy.power_delta = true; + } + } else { + if (delta > (Settings.energy_power_delta -100)) { + Energy.power_delta = true; + } + } + if (Energy.power_delta) { + Energy.power_history[1] = Energy.active_power[0]; + Energy.power_history[2] = Energy.active_power[0]; + } + } + } + Energy.power_history[0] = Energy.power_history[1]; + Energy.power_history[1] = Energy.power_history[2]; + Energy.power_history[2] = energy_power_u; + + if (Energy.power_on && (Settings.energy_min_power || Settings.energy_max_power || Settings.energy_min_voltage || Settings.energy_max_voltage || Settings.energy_min_current || Settings.energy_max_current)) { + uint16_t energy_voltage_u = (uint16_t)(Energy.voltage[0]); + uint16_t energy_current_u = (uint16_t)(Energy.current[0] * 1000); + + DEBUG_DRIVER_LOG(PSTR("NRG: W %d, U %d, I %d"), energy_power_u, energy_voltage_u, energy_current_u); + + Response_P(PSTR("{")); + bool flag; + bool jsonflg = false; + if (EnergyMargin(false, Settings.energy_min_power, energy_power_u, flag, Energy.min_power_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_POWERLOW "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + jsonflg = true; + } + if (EnergyMargin(true, Settings.energy_max_power, energy_power_u, flag, Energy.max_power_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_POWERHIGH "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + jsonflg = true; + } + if (EnergyMargin(false, Settings.energy_min_voltage, energy_voltage_u, flag, Energy.min_voltage_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_VOLTAGELOW "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + jsonflg = true; + } + if (EnergyMargin(true, Settings.energy_max_voltage, energy_voltage_u, flag, Energy.max_voltage_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_VOLTAGEHIGH "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + jsonflg = true; + } + if (EnergyMargin(false, Settings.energy_min_current, energy_current_u, flag, Energy.min_current_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_CURRENTLOW "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + jsonflg = true; + } + if (EnergyMargin(true, Settings.energy_max_current, energy_current_u, flag, Energy.max_current_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_CURRENTHIGH "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + jsonflg = true; + } + if (jsonflg) { + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_MARGINS), MQTT_TELE_RETAIN); + EnergyMqttShow(); + } + } + +#ifdef USE_ENERGY_POWER_LIMIT + + if (Settings.energy_max_power_limit) { + if (Energy.active_power[0] > Settings.energy_max_power_limit) { + if (!Energy.mplh_counter) { + Energy.mplh_counter = Settings.energy_max_power_limit_hold; + } else { + Energy.mplh_counter--; + if (!Energy.mplh_counter) { + ResponseTime_P(PSTR(",\"" D_JSON_MAXPOWERREACHED "\":%d}"), energy_power_u); + MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); + EnergyMqttShow(); + SetAllPower(POWER_ALL_OFF, SRC_MAXPOWER); + if (!Energy.mplr_counter) { + Energy.mplr_counter = Settings.param[P_MAX_POWER_RETRY] +1; + } + Energy.mplw_counter = Settings.energy_max_power_limit_window; + } + } + } + else if (power && (energy_power_u <= Settings.energy_max_power_limit)) { + Energy.mplh_counter = 0; + Energy.mplr_counter = 0; + Energy.mplw_counter = 0; + } + if (!power) { + if (Energy.mplw_counter) { + Energy.mplw_counter--; + } else { + if (Energy.mplr_counter) { + Energy.mplr_counter--; + if (Energy.mplr_counter) { + ResponseTime_P(PSTR(",\"" D_JSON_POWERMONITOR "\":\"%s\"}"), GetStateText(1)); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_JSON_POWERMONITOR)); + RestorePower(true, SRC_MAXPOWER); + } else { + ResponseTime_P(PSTR(",\"" D_JSON_MAXPOWERREACHEDRETRY "\":\"%s\"}"), GetStateText(0)); + MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); + EnergyMqttShow(); + SetAllPower(POWER_ALL_OFF, SRC_MAXPOWER); + } + } + } + } + } + + + if (Settings.energy_max_energy) { + uint16_t 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; + ResponseTime_P(PSTR(",\"" D_JSON_ENERGYMONITOR "\":\"%s\"}"), GetStateText(1)); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_JSON_ENERGYMONITOR)); + RestorePower(true, SRC_MAXENERGY); + } + else if ((1 == Energy.max_energy_state ) && (energy_daily_u >= Settings.energy_max_energy)) { + Energy.max_energy_state = 2; + char stemp[FLOATSZ]; + dtostrfd(Energy.daily, 3, stemp); + ResponseTime_P(PSTR(",\"" D_JSON_MAXENERGYREACHED "\":%s}"), stemp); + MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); + EnergyMqttShow(); + SetAllPower(POWER_ALL_OFF, SRC_MAXENERGY); + } + } +#endif + + if (Energy.power_delta) { EnergyMqttShow(); } +} + +void EnergyMqttShow(void) +{ + + int tele_period_save = tele_period; + tele_period = 2; + mqtt_data[0] = '\0'; + ResponseAppendTime(); + EnergyShow(true); + tele_period = tele_period_save; + ResponseJsonEnd(); + MqttPublishTeleSensor(); + Energy.power_delta = false; +} +#endif + +void EnergyEverySecond(void) +{ + + if (global_update) { + if (power && (global_temperature != 9999) && (global_temperature > Settings.param[P_OVER_TEMP])) { + SetAllPower(POWER_ALL_OFF, SRC_OVERTEMP); + } + } + + + uint32_t data_valid = Energy.phase_count; + for (uint32_t i = 0; i < Energy.phase_count; i++) { + if (Energy.data_valid[i] <= ENERGY_WATCHDOG) { + Energy.data_valid[i]++; + if (Energy.data_valid[i] > ENERGY_WATCHDOG) { + + Energy.voltage[i] = 0; + Energy.current[i] = 0; + Energy.active_power[i] = 0; + if (!isnan(Energy.apparent_power[i])) { Energy.apparent_power[i] = 0; } + if (!isnan(Energy.reactive_power[i])) { Energy.reactive_power[i] = 0; } + if (!isnan(Energy.frequency[i])) { Energy.frequency[i] = 0; } + if (!isnan(Energy.power_factor[i])) { Energy.power_factor[i] = 0; } + + data_valid--; + } + } + } + if (!data_valid) { + if (!isnan(Energy.export_active)) { Energy.export_active = 0; } + Energy.start_energy = 0; + + XnrgCall(FUNC_ENERGY_RESET); + } + +#ifdef USE_ENERGY_MARGIN_DETECTION + EnergyMarginCheck(); +#endif +} + + + + + +void EnergyCommandCalResponse(uint32_t nvalue) +{ + snprintf_P(XdrvMailbox.command, CMDSZ, PSTR("%sCal"), XdrvMailbox.command); + ResponseCmndNumber(nvalue); +} + +void CmndEnergyReset(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 3)) { + char *p; + unsigned long lnum = strtoul(XdrvMailbox.data, &p, 10); + if (p != XdrvMailbox.data) { + switch (XdrvMailbox.index) { + case 1: + + Energy.kWhtoday_offset = lnum *100; + Energy.kWhtoday = 0; + Energy.kWhtoday_delta = 0; + Energy.start_energy = 0; + 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; + case 2: + + Settings.energy_kWhyesterday = lnum *100; + break; + case 3: + + RtcSettings.energy_kWhtotal = lnum *100; + Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; + + Settings.energy_kWhtotal_time = (!Energy.kWhtoday_offset) ? LocalTime() : Midnight(); + RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total * 1000); + break; + } + } + } + else if ((XdrvMailbox.index > 3) && (XdrvMailbox.index <= 5)) { + uint32_t values[2] = { 0 }; + uint32_t position = ParseParameters(2, values); + values[0] *= 100; + values[1] *= 100; + + switch (XdrvMailbox.index) + { + case 4: + + if (position > 0) { + RtcSettings.energy_usage.usage1_kWhtotal = values[0]; + } + if (position > 1) { + RtcSettings.energy_usage.usage2_kWhtotal = values[1]; + } + Settings.energy_usage.usage1_kWhtotal = RtcSettings.energy_usage.usage1_kWhtotal; + Settings.energy_usage.usage2_kWhtotal = RtcSettings.energy_usage.usage2_kWhtotal; + break; + case 5: + + if (position > 0) { + RtcSettings.energy_usage.return1_kWhtotal = values[0]; + } + if (position > 1) { + RtcSettings.energy_usage.return2_kWhtotal = values[1]; + } + Settings.energy_usage.return1_kWhtotal = RtcSettings.energy_usage.return1_kWhtotal; + Settings.energy_usage.return2_kWhtotal = RtcSettings.energy_usage.return2_kWhtotal; + break; + } + } + + Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000; + + char energy_total_chr[FLOATSZ]; + dtostrfd(Energy.total, Settings.flag2.energy_resolution, energy_total_chr); + char energy_daily_chr[FLOATSZ]; + dtostrfd(Energy.daily, Settings.flag2.energy_resolution, energy_daily_chr); + char energy_yesterday_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr); + + char energy_usage1_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_usage.usage1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_usage1_chr); + char energy_usage2_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_usage.usage2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_usage2_chr); + char energy_return1_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_usage.return1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_return1_chr); + char energy_return2_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_usage.return2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_return2_chr); + + Response_P(PSTR("{\"%s\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s,\"" D_JSON_USAGE "\":[%s,%s],\"" D_JSON_EXPORT "\":[%s,%s]}}"), + XdrvMailbox.command, energy_total_chr, energy_yesterday_chr, energy_daily_chr, energy_usage1_chr, energy_usage2_chr, energy_return1_chr, energy_return2_chr); +} + +void CmndTariff(void) +{ + + + + + + + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { + uint32_t tariff = XdrvMailbox.index -1; + uint32_t time_type = 0; + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); + while ((str != nullptr) && (time_type < 2)) { + char *q; + uint32_t value = strtol(str, &q, 10); + Settings.tariff[tariff][time_type] = value; + if (value < 24) { + Settings.tariff[tariff][time_type] *= 60; + char *minute = strtok_r(nullptr, ":", &q); + if (minute) { + value = strtol(minute, nullptr, 10); + if (value > 59) { + value = 59; + } + Settings.tariff[tariff][time_type] += value; + } + } + if (Settings.tariff[tariff][time_type] > 1439) { + Settings.tariff[tariff][time_type] = 1439; + } + str = strtok_r(nullptr, ", ", &p); + time_type++; + } + } + else if (XdrvMailbox.index == 9) { + Settings.flag3.energy_weekend = XdrvMailbox.payload & 1; + } + Response_P(PSTR("{\"%s\":{\"Off-Peak\":{\"STD\":\"%s\",\"DST\":\"%s\"},\"Standard\":{\"STD\":\"%s\",\"DST\":\"%s\"},\"Weekend\":\"%s\"}}"), + XdrvMailbox.command, + GetMinuteTime(Settings.tariff[0][0]).c_str(),GetMinuteTime(Settings.tariff[0][1]).c_str(), + GetMinuteTime(Settings.tariff[1][0]).c_str(),GetMinuteTime(Settings.tariff[1][1]).c_str(), + GetStateText(Settings.flag3.energy_weekend)); +} + +void CmndPowerCal(void) +{ + Energy.command_code = CMND_POWERCAL; + if (XnrgCall(FUNC_COMMAND)) { + if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { + Settings.energy_power_calibration = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_power_calibration); + } +} + +void CmndVoltageCal(void) +{ + Energy.command_code = CMND_VOLTAGECAL; + if (XnrgCall(FUNC_COMMAND)) { + if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { + Settings.energy_voltage_calibration = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_voltage_calibration); + } +} + +void CmndCurrentCal(void) +{ + Energy.command_code = CMND_CURRENTCAL; + if (XnrgCall(FUNC_COMMAND)) { + if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { + Settings.energy_current_calibration = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_current_calibration); + } +} + +void CmndPowerSet(void) +{ + Energy.command_code = CMND_POWERSET; + if (XnrgCall(FUNC_COMMAND)) { + EnergyCommandCalResponse(Settings.energy_power_calibration); + } +} + +void CmndVoltageSet(void) +{ + Energy.command_code = CMND_VOLTAGESET; + if (XnrgCall(FUNC_COMMAND)) { + EnergyCommandCalResponse(Settings.energy_voltage_calibration); + } +} + +void CmndCurrentSet(void) +{ + Energy.command_code = CMND_CURRENTSET; + if (XnrgCall(FUNC_COMMAND)) { + EnergyCommandCalResponse(Settings.energy_current_calibration); + } +} + +void CmndFrequencySet(void) +{ + Energy.command_code = CMND_FREQUENCYSET; + if (XnrgCall(FUNC_COMMAND)) { + EnergyCommandCalResponse(Settings.energy_frequency_calibration); + } +} + +void CmndModuleAddress(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4) && (1 == Energy.phase_count)) { + Energy.command_code = CMND_MODULEADDRESS; + if (XnrgCall(FUNC_COMMAND)) { + ResponseCmndDone(); + } + } +} + +#ifdef USE_ENERGY_MARGIN_DETECTION +void CmndPowerDelta(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32000)) { + Settings.energy_power_delta = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_power_delta); +} + +void CmndPowerLow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_min_power = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_min_power); +} + +void CmndPowerHigh(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power); +} + +void CmndVoltageLow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 501)) { + Settings.energy_min_voltage = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_min_voltage); +} + +void CmndVoltageHigh(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 501)) { + Settings.energy_max_voltage = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_voltage); +} + +void CmndCurrentLow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 16001)) { + Settings.energy_min_current = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_min_current); +} + +void CmndCurrentHigh(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 16001)) { + Settings.energy_max_current = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_current); +} + +#ifdef USE_ENERGY_POWER_LIMIT +void CmndMaxPower(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_limit = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_limit); +} + +void CmndMaxPowerHold(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_limit_hold = (1 == XdrvMailbox.payload) ? MAX_POWER_HOLD : XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_limit_hold); +} + +void CmndMaxPowerWindow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_limit_window = (1 == XdrvMailbox.payload) ? MAX_POWER_WINDOW : XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_limit_window); +} + +void CmndSafePower(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_safe_limit = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_safe_limit); +} + +void CmndSafePowerHold(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_safe_limit_hold = (1 == XdrvMailbox.payload) ? SAFE_POWER_HOLD : XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_safe_limit_hold); +} + +void CmndSafePowerWindow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 1440)) { + Settings.energy_max_power_safe_limit_window = (1 == XdrvMailbox.payload) ? SAFE_POWER_WINDOW : XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_safe_limit_window); +} + +void CmndMaxEnergy(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_energy = XdrvMailbox.payload; + Energy.max_energy_state = 3; + } + ResponseCmndNumber(Settings.energy_max_energy); +} + +void CmndMaxEnergyStart(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 24)) { + Settings.energy_max_energy_start = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_energy_start); +} +#endif +#endif + +void EnergySnsInit(void) +{ + XnrgCall(FUNC_INIT); + + if (energy_flg) { + if (RtcSettingsValid()) { + Energy.kWhtoday_offset = RtcSettings.energy_kWhtoday; + } + else if (RtcTime.day_of_year == Settings.energy_kWhdoy) { + Energy.kWhtoday_offset = Settings.energy_kWhtoday; + } + else { + Energy.kWhtoday_offset = 0; + } + Energy.kWhtoday = 0; + Energy.kWhtoday_delta = 0; + Energy.period = Energy.kWhtoday_offset; + EnergyUpdateToday(); + ticker_energy.attach_ms(200, Energy200ms); + } +} + +#ifdef USE_WEBSERVER +const char HTTP_ENERGY_SNS1[] PROGMEM = + "{s}" D_POWERUSAGE_APPARENT "{m}%s " D_UNIT_VA "{e}" + "{s}" D_POWERUSAGE_REACTIVE "{m}%s " D_UNIT_VAR "{e}" + "{s}" D_POWER_FACTOR "{m}%s{e}"; + +const char HTTP_ENERGY_SNS2[] PROGMEM = + "{s}" D_ENERGY_TODAY "{m}%s " D_UNIT_KILOWATTHOUR "{e}" + "{s}" D_ENERGY_YESTERDAY "{m}%s " D_UNIT_KILOWATTHOUR "{e}" + "{s}" D_ENERGY_TOTAL "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; + +const char HTTP_ENERGY_SNS3[] PROGMEM = + "{s}" D_EXPORT_ACTIVE "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; +#endif + +char* EnergyFormatIndex(char* result, char* input, bool json, uint32_t index, bool single = false) +{ + char layout[16]; + GetTextIndexed(layout, sizeof(layout), (index -1) + (3 * json), kEnergyPhases); + switch (index) { + case 2: + snprintf_P(result, FLOATSZ *3, layout, input, input + FLOATSZ); + break; + case 3: + snprintf_P(result, FLOATSZ *3, layout, input, input + FLOATSZ, input + FLOATSZ + FLOATSZ); + break; + default: + snprintf_P(result, FLOATSZ *3, input); + } + return result; +} + +char* EnergyFormat(char* result, char* input, bool json, bool single = false) +{ + uint8_t index = (single) ? 1 : Energy.phase_count; + return EnergyFormatIndex(result, input, json, index, single); +} + +void EnergyShow(bool json) +{ + for (uint32_t i = 0; i < Energy.phase_count; i++) { + if (Energy.voltage_common) { + Energy.voltage[i] = Energy.voltage[0]; + } + } + + float power_factor_knx = Energy.power_factor[0]; + + char apparent_power_chr[Energy.phase_count][FLOATSZ]; + char reactive_power_chr[Energy.phase_count][FLOATSZ]; + char power_factor_chr[Energy.phase_count][FLOATSZ]; + char frequency_chr[Energy.phase_count][FLOATSZ]; + if (!Energy.type_dc) { + if (Energy.current_available && Energy.voltage_available) { + for (uint32_t i = 0; i < Energy.phase_count; i++) { + float apparent_power = Energy.apparent_power[i]; + if (isnan(apparent_power)) { + apparent_power = Energy.voltage[i] * Energy.current[i]; + } + if (apparent_power < Energy.active_power[i]) { + Energy.active_power[i] = apparent_power; + } + + float power_factor = Energy.power_factor[i]; + if (isnan(power_factor)) { + power_factor = (Energy.active_power[i] && apparent_power) ? Energy.active_power[i] / apparent_power : 0; + if (power_factor > 1) { + power_factor = 1; + } + } + if (0 == i) { power_factor_knx = power_factor; } + + float reactive_power = Energy.reactive_power[i]; + if (isnan(reactive_power)) { + reactive_power = 0; + uint32_t difference = ((uint32_t)(apparent_power * 100) - (uint32_t)(Energy.active_power[i] * 100)) / 10; + if ((Energy.current[i] > 0.005) && ((difference > 15) || (difference > (uint32_t)(apparent_power * 100 / 1000)))) { + + + reactive_power = (float)(RoundSqrtInt((uint32_t)(apparent_power * apparent_power * 100) - (uint32_t)(Energy.active_power[i] * Energy.active_power[i] * 100))) / 10; + } + } + + dtostrfd(apparent_power, Settings.flag2.wattage_resolution, apparent_power_chr[i]); + dtostrfd(reactive_power, Settings.flag2.wattage_resolution, reactive_power_chr[i]); + dtostrfd(power_factor, 2, power_factor_chr[i]); + } + } + for (uint32_t i = 0; i < Energy.phase_count; i++) { + float frequency = Energy.frequency[i]; + if (isnan(Energy.frequency[i])) { + frequency = 0; + } + dtostrfd(frequency, Settings.flag2.frequency_resolution, frequency_chr[i]); + } + } + + char voltage_chr[Energy.phase_count][FLOATSZ]; + char current_chr[Energy.phase_count][FLOATSZ]; + char active_power_chr[Energy.phase_count][FLOATSZ]; + for (uint32_t i = 0; i < Energy.phase_count; i++) { + dtostrfd(Energy.voltage[i], Settings.flag2.voltage_resolution, voltage_chr[i]); + dtostrfd(Energy.current[i], Settings.flag2.current_resolution, current_chr[i]); + dtostrfd(Energy.active_power[i], Settings.flag2.wattage_resolution, active_power_chr[i]); + } + + char energy_daily_chr[FLOATSZ]; + dtostrfd(Energy.daily, Settings.flag2.energy_resolution, energy_daily_chr); + char energy_yesterday_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr); + + char energy_total_chr[3][FLOATSZ]; + dtostrfd(Energy.total, Settings.flag2.energy_resolution, energy_total_chr[0]); + char export_active_chr[3][FLOATSZ]; + dtostrfd(Energy.export_active, Settings.flag2.energy_resolution, export_active_chr[0]); + uint8_t energy_total_fields = 1; + + if (Settings.tariff[0][0] != Settings.tariff[1][0]) { + dtostrfd((float)RtcSettings.energy_usage.usage1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_total_chr[1]); + dtostrfd((float)RtcSettings.energy_usage.usage2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_total_chr[2]); + dtostrfd((float)RtcSettings.energy_usage.return1_kWhtotal / 100000, Settings.flag2.energy_resolution, export_active_chr[1]); + dtostrfd((float)RtcSettings.energy_usage.return2_kWhtotal / 100000, Settings.flag2.energy_resolution, export_active_chr[2]); + energy_total_fields = 3; + } + + char value_chr[FLOATSZ *3]; + char value2_chr[FLOATSZ *3]; + char value3_chr[FLOATSZ *3]; + + if (json) { + bool show_energy_period = (0 == tele_period); + + ResponseAppend_P(PSTR(",\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL_START_TIME "\":\"%s\",\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s"), + GetDateAndTime(DT_ENERGY).c_str(), + EnergyFormatIndex(value_chr, energy_total_chr[0], json, energy_total_fields), + energy_yesterday_chr, + energy_daily_chr); + + if (!isnan(Energy.export_active)) { + ResponseAppend_P(PSTR(",\"" D_JSON_EXPORT_ACTIVE "\":%s"), + EnergyFormatIndex(value_chr, export_active_chr[0], json, energy_total_fields)); + } + + if (show_energy_period) { + float energy = 0; + if (Energy.period) { + energy = (float)(RtcSettings.energy_kWhtoday - Energy.period) / 100; + } + Energy.period = RtcSettings.energy_kWhtoday; + char energy_period_chr[FLOATSZ]; + dtostrfd(energy, Settings.flag2.wattage_resolution, energy_period_chr); + ResponseAppend_P(PSTR(",\"" D_JSON_PERIOD "\":%s"), energy_period_chr); + } + ResponseAppend_P(PSTR(",\"" D_JSON_POWERUSAGE "\":%s"), + EnergyFormat(value_chr, active_power_chr[0], json)); + if (!Energy.type_dc) { + if (Energy.current_available && Energy.voltage_available) { + ResponseAppend_P(PSTR(",\"" D_JSON_APPARENT_POWERUSAGE "\":%s,\"" D_JSON_REACTIVE_POWERUSAGE "\":%s,\"" D_JSON_POWERFACTOR "\":%s"), + EnergyFormat(value_chr, apparent_power_chr[0], json), + EnergyFormat(value2_chr, reactive_power_chr[0], json), + EnergyFormat(value3_chr, power_factor_chr[0], json)); + } + if (!isnan(Energy.frequency[0])) { + ResponseAppend_P(PSTR(",\"" D_JSON_FREQUENCY "\":%s"), + EnergyFormat(value_chr, frequency_chr[0], json, Energy.voltage_common)); + } + } + if (Energy.voltage_available) { + ResponseAppend_P(PSTR(",\"" D_JSON_VOLTAGE "\":%s"), + EnergyFormat(value_chr, voltage_chr[0], json, Energy.voltage_common)); + } + if (Energy.current_available) { + ResponseAppend_P(PSTR(",\"" D_JSON_CURRENT "\":%s"), + EnergyFormat(value_chr, current_chr[0], json)); + } + XnrgCall(FUNC_JSON_APPEND); + ResponseJsonEnd(); + +#ifdef USE_DOMOTICZ + if (show_energy_period) { + dtostrfd(Energy.total * 1000, 1, energy_total_chr[0]); + DomoticzSensorPowerEnergy((int)Energy.active_power[0], energy_total_chr[0]); + + dtostrfd((float)RtcSettings.energy_usage.usage1_kWhtotal / 100, 1, energy_total_chr[1]); + dtostrfd((float)RtcSettings.energy_usage.usage2_kWhtotal / 100, 1, energy_total_chr[2]); + dtostrfd((float)RtcSettings.energy_usage.return1_kWhtotal / 100, 1, export_active_chr[1]); + dtostrfd((float)RtcSettings.energy_usage.return2_kWhtotal / 100, 1, export_active_chr[2]); + DomoticzSensorP1SmartMeter(energy_total_chr[1], energy_total_chr[2], export_active_chr[1], export_active_chr[2], (int)Energy.active_power[0]); + + if (Energy.voltage_available) { + DomoticzSensor(DZ_VOLTAGE, voltage_chr[0]); + } + if (Energy.current_available) { + DomoticzSensor(DZ_CURRENT, current_chr[0]); + } + } +#endif +#ifdef USE_KNX + if (show_energy_period) { + if (Energy.voltage_available) { + KnxSensor(KNX_ENERGY_VOLTAGE, Energy.voltage[0]); + } + if (Energy.current_available) { + KnxSensor(KNX_ENERGY_CURRENT, Energy.current[0]); + } + KnxSensor(KNX_ENERGY_POWER, Energy.active_power[0]); + if (!Energy.type_dc) { + KnxSensor(KNX_ENERGY_POWERFACTOR, power_factor_knx); + } + KnxSensor(KNX_ENERGY_DAILY, Energy.daily); + KnxSensor(KNX_ENERGY_TOTAL, Energy.total); + KnxSensor(KNX_ENERGY_START, Energy.start_energy); + } +#endif +#ifdef USE_WEBSERVER + } else { + if (Energy.voltage_available) { + WSContentSend_PD(HTTP_SNS_VOLTAGE, EnergyFormat(value_chr, voltage_chr[0], json, Energy.voltage_common)); + } + if (Energy.current_available) { + WSContentSend_PD(HTTP_SNS_CURRENT, EnergyFormat(value_chr, current_chr[0], json)); + } + WSContentSend_PD(HTTP_SNS_POWER, EnergyFormat(value_chr, active_power_chr[0], json)); + if (!Energy.type_dc) { + if (Energy.current_available && Energy.voltage_available) { + WSContentSend_PD(HTTP_ENERGY_SNS1, EnergyFormat(value_chr, apparent_power_chr[0], json), + EnergyFormat(value2_chr, reactive_power_chr[0], json), + EnergyFormat(value3_chr, power_factor_chr[0], json)); + } + if (!isnan(Energy.frequency[0])) { + WSContentSend_PD(PSTR("{s}" D_FREQUENCY "{m}%s " D_UNIT_HERTZ "{e}"), + EnergyFormat(value_chr, frequency_chr[0], json, Energy.voltage_common)); + } + } + WSContentSend_PD(HTTP_ENERGY_SNS2, energy_daily_chr, energy_yesterday_chr, energy_total_chr[0]); + if (!isnan(Energy.export_active)) { + WSContentSend_PD(HTTP_ENERGY_SNS3, export_active_chr[0]); + } + + XnrgCall(FUNC_WEB_SENSOR); +#endif + } +} + + + + + +bool Xdrv03(uint8_t function) +{ + bool result = false; + + if (FUNC_PRE_INIT == function) { + energy_flg = ENERGY_NONE; + XnrgCall(FUNC_PRE_INIT); + } + else if (energy_flg) { + switch (function) { + case FUNC_LOOP: + XnrgCall(FUNC_LOOP); + break; + case FUNC_EVERY_250_MSECOND: + XnrgCall(FUNC_EVERY_250_MSECOND); + break; + case FUNC_SERIAL: + result = XnrgCall(FUNC_SERIAL); + break; +#ifdef USE_ENERGY_MARGIN_DETECTION + case FUNC_SET_POWER: + Energy.power_steady_counter = 2; + break; +#endif + case FUNC_COMMAND: + result = DecodeCommand(kEnergyCommands, EnergyCommand); + break; + } + } + return result; +} + +bool Xsns03(uint8_t function) +{ + bool result = false; + + if (energy_flg) { + switch (function) { + case FUNC_EVERY_SECOND: + EnergyEverySecond(); + break; + case FUNC_JSON_APPEND: + EnergyShow(true); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + EnergyShow(false); + break; +#endif + case FUNC_SAVE_BEFORE_RESTART: + EnergySaveState(); + break; + case FUNC_INIT: + EnergySnsInit(); + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_04_light.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_04_light.ino" +#ifdef USE_LIGHT +# 124 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_04_light.ino" +#define XDRV_04 4 + + +enum LightSchemes { LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MAX }; + +const uint8_t LIGHT_COLOR_SIZE = 25; + +const char kLightCommands[] PROGMEM = "|" + D_CMND_COLOR "|" D_CMND_COLORTEMPERATURE "|" D_CMND_DIMMER "|" D_CMND_DIMMER_RANGE "|" D_CMND_LEDTABLE "|" D_CMND_FADE "|" + D_CMND_RGBWWTABLE "|" D_CMND_SCHEME "|" D_CMND_SPEED "|" D_CMND_WAKEUP "|" D_CMND_WAKEUPDURATION "|" + D_CMND_WHITE "|" D_CMND_CHANNEL "|" D_CMND_HSBCOLOR +#ifdef USE_LIGHT_PALETTE + "|" D_CMND_PALETTE +#endif + "|UNDOCA" ; + +void (* const LightCommand[])(void) PROGMEM = { + &CmndColor, &CmndColorTemperature, &CmndDimmer, &CmndDimmerRange, &CmndLedTable, &CmndFade, + &CmndRgbwwTable, &CmndScheme, &CmndSpeed, &CmndWakeup, &CmndWakeupDuration, + &CmndWhite, &CmndChannel, &CmndHsbColor, +#ifdef USE_LIGHT_PALETTE + &CmndPalette, +#endif + &CmndUndocA }; + + +enum LightColorModes { + LCM_RGB = 1, LCM_CT = 2, LCM_BOTH = 3 }; + +struct LRgbColor { + uint8_t R, G, B; +}; +const uint8_t MAX_FIXED_COLOR = 12; +const LRgbColor kFixedColor[MAX_FIXED_COLOR] PROGMEM = + { 255,0,0, 0,255,0, 0,0,255, 228,32,0, 0,228,32, 0,32,228, 188,64,0, 0,160,96, 160,32,240, 255,255,0, 255,0,170, 255,255,255 }; + +struct LWColor { + uint8_t W; +}; +const uint8_t MAX_FIXED_WHITE = 4; +const LWColor kFixedWhite[MAX_FIXED_WHITE] PROGMEM = { 0, 255, 128, 32 }; + +struct LCwColor { + uint8_t C, W; +}; +const uint8_t MAX_FIXED_COLD_WARM = 4; +const LCwColor kFixedColdWarm[MAX_FIXED_COLD_WARM] PROGMEM = { 0,0, 255,0, 0,255, 128,128 }; + + +const uint16_t CT_MIN = 153; +const uint16_t CT_MAX = 500; + +const uint16_t CT_MIN_ALEXA = 200; +const uint16_t CT_MAX_ALEXA = 380; + + + + + + +typedef struct gamma_table_t { + uint16_t to_src; + uint16_t to_gamma; +} gamma_table_t; + +const gamma_table_t gamma_table[] = { + { 1, 1 }, + { 4, 1 }, + { 209, 13 }, + { 312, 41 }, + { 457, 106 }, + { 626, 261 }, + { 762, 450 }, + { 895, 703 }, + { 1023, 1023 }, + { 0xFFFF, 0xFFFF } +}; + + +const gamma_table_t gamma_table_fast[] = { + { 384, 192 }, + { 768, 576 }, + { 1023, 1023 }, + { 0xFFFF, 0xFFFF } +}; +# 256 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_04_light.ino" +struct LIGHT { + uint32_t strip_timer_counter = 0; + power_t power = 0; + + uint16_t wakeup_counter = 0; + + uint8_t entry_color[LST_MAX]; + uint8_t current_color[LST_MAX]; + uint8_t new_color[LST_MAX]; + uint8_t last_color[LST_MAX]; + uint8_t color_remap[LST_MAX]; + + uint8_t wheel = 0; + uint8_t random = 0; + uint8_t subtype = 0; + uint8_t device = 0; + uint8_t old_power = 1; + uint8_t wakeup_active = 0; + uint8_t wakeup_dimmer = 0; + uint8_t fixed_color_index = 1; + uint8_t pwm_offset = 0; + uint8_t max_scheme = LS_MAX -1; + + bool update = true; + bool pwm_multi_channels = false; + + bool fade_initialized = false; + bool fade_running = false; +#ifdef USE_DEVICE_GROUPS + bool devgrp_no_channels_out = false; +#endif +#ifdef USE_LIGHT_PALETTE + uint8_t palette_count = 0; + uint8_t * palette; +#endif + uint16_t fade_start_10[LST_MAX] = {0,0,0,0,0}; + uint16_t fade_cur_10[LST_MAX]; + uint16_t fade_end_10[LST_MAX]; + uint16_t fade_duration = 0; + uint32_t fade_start = 0; + + uint16_t pwm_min = 0; + uint16_t pwm_max = 1023; +} Light; + +power_t LightPower(void) +{ + return Light.power; +} + + +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 +power_t LightPowerIRAM(void) ICACHE_RAM_ATTR; +#endif + +power_t LightPowerIRAM(void) +{ + return Light.power; +} + +uint8_t LightDevice(void) +{ + return Light.device; +} + +static uint32_t min3(uint32_t a, uint32_t b, uint32_t c) { + return (a < b && a < c) ? a : (b < c) ? b : c; +} +# 362 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_04_light.ino" +class LightStateClass { + private: + uint16_t _hue = 0; + uint8_t _sat = 255; + uint8_t _briRGB = 255; + + uint8_t _r = 255; + uint8_t _g = 255; + uint8_t _b = 255; + + uint8_t _subtype = 0; + uint16_t _ct = CT_MIN; + uint8_t _wc = 255; + uint8_t _ww = 0; + uint8_t _briCT = 255; + + uint8_t _color_mode = LCM_RGB; + + + + + + uint16_t _ct_min_range = CT_MIN; + uint16_t _ct_max_range = CT_MAX; + + public: + LightStateClass() { + + } + + void setSubType(uint8_t sub_type) { + _subtype = sub_type; + } +# 404 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_04_light.ino" + uint8_t setColorMode(uint8_t cm) { + uint8_t prev_cm = _color_mode; + if (cm < LCM_RGB) { cm = LCM_RGB; } + if (cm > LCM_BOTH) { cm = LCM_BOTH; } + uint8_t maxbri = (_briRGB >= _briCT) ? _briRGB : _briCT; + + switch (_subtype) { + case LST_COLDWARM: + _color_mode = LCM_CT; + break; + + case LST_NONE: + case LST_SINGLE: + case LST_RGB: + default: + _color_mode = LCM_RGB; + break; + + case LST_RGBW: + case LST_RGBCW: + _color_mode = cm; + break; + } + if (LCM_RGB == _color_mode) { + _briCT = 0; + if (0 == _briRGB) { _briRGB = maxbri; } + } + if (LCM_CT == _color_mode) { + _briRGB = 0; + if (0 == _briCT) { _briCT = maxbri; } + } +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setColorMode prev_cm (%d) req_cm (%d) new_cm (%d)", prev_cm, cm, _color_mode); +#endif + return prev_cm; + } + + inline uint8_t getColorMode() { + return _color_mode; + } + + void addRGBMode() { + setColorMode(_color_mode | LCM_RGB); + } + void addCTMode() { + setColorMode(_color_mode | LCM_CT); + } + + + void getRGB(uint8_t *r, uint8_t *g, uint8_t *b) { + if (r) { *r = _r; } + if (g) { *g = _g; } + if (b) { *b = _b; } + } + + + + void getCW(uint8_t *rc, uint8_t *rw) { + if (rc) { *rc = _wc; } + if (rw) { *rw = _ww; } + } + + + void getActualRGBCW(uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *c, uint8_t *w) { + bool rgb_channels_on = _color_mode & LCM_RGB; + bool ct_channels_on = _color_mode & LCM_CT; + + if (r) { *r = rgb_channels_on ? changeUIntScale(_r, 0, 255, 0, _briRGB) : 0; } + if (g) { *g = rgb_channels_on ? changeUIntScale(_g, 0, 255, 0, _briRGB) : 0; } + if (b) { *b = rgb_channels_on ? changeUIntScale(_b, 0, 255, 0, _briRGB) : 0; } + + if (c) { *c = ct_channels_on ? changeUIntScale(_wc, 0, 255, 0, _briCT) : 0; } + if (w) { *w = ct_channels_on ? changeUIntScale(_ww, 0, 255, 0, _briCT) : 0; } + } + + uint8_t getChannels(uint8_t *channels) { + getActualRGBCW(&channels[0], &channels[1], &channels[2], &channels[3], &channels[4]); + } + + void getChannelsRaw(uint8_t *channels) { + channels[0] = _r; + channels[1] = _g; + channels[2] = _b; + channels[3] = _wc; + channels[4] = _ww; + } + + void getHSB(uint16_t *hue, uint8_t *sat, uint8_t *bri) { + if (hue) { *hue = _hue; } + if (sat) { *sat = _sat; } + if (bri) { *bri = _briRGB; } + } + + + uint8_t getBri(void) { + + return (_briRGB >= _briCT) ? _briRGB : _briCT; + } + + + inline uint8_t getBriCT() { + return _briCT; + } + + static inline uint8_t DimmerToBri(uint8_t dimmer) { + return changeUIntScale(dimmer, 0, 100, 0, 255); + } + static uint8_t BriToDimmer(uint8_t bri) { + uint8_t dimmer = changeUIntScale(bri, 0, 255, 0, 100); + + if ((dimmer == 0) && (bri > 0)) { dimmer = 1; } + return dimmer; + } + + uint8_t getDimmer(uint32_t mode = 0) { + uint8_t bri; + switch (mode) { + case 1: + bri = getBriRGB(); + break; + case 2: + bri = getBriCT(); + break; + default: + bri = getBri(); + break; + } + return BriToDimmer(bri); + } + + inline uint16_t getCT() const { + return _ct; + } + + + uint16_t getCT10bits() const { + return changeUIntScale(_ct, _ct_min_range, _ct_max_range, 0, 1023); + } + + inline void setCTRange(uint16_t ct_min_range, uint16_t ct_max_range) { + _ct_min_range = ct_min_range; + _ct_max_range = ct_max_range; + } + + inline void getCTRange(uint16_t *ct_min_range, uint16_t *ct_max_range) const { + if (ct_min_range) { *ct_min_range = _ct_min_range; } + if (ct_max_range) { *ct_max_range = _ct_max_range; } + } + + + void getXY(float *x, float *y) { + RgbToXy(_r, _g, _b, x, y); + } + + + + void setBri(uint8_t bri) { + setBriRGB(_color_mode & LCM_RGB ? bri : 0); + setBriCT(_color_mode & LCM_CT ? bri : 0); +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setBri RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB); +#endif +#ifdef USE_PWM_DIMMER + if (PWM_DIMMER == my_module_type) PWMDimmerSetBrightnessLeds(0); +#endif + } + + + uint8_t setBriRGB(uint8_t bri_rgb) { + uint8_t prev_bri = _briRGB; + _briRGB = bri_rgb; + if (bri_rgb > 0) { addRGBMode(); } + return prev_bri; + } + + + uint8_t setBriCT(uint8_t bri_ct) { + uint8_t prev_bri = _briCT; + _briCT = bri_ct; + if (bri_ct > 0) { addCTMode(); } + return prev_bri; + } + + inline uint8_t getBriRGB() { + return _briRGB; + } + + void setDimmer(uint8_t dimmer) { + setBri(DimmerToBri(dimmer)); + } + + void setCT(uint16_t ct) { + if (0 == ct) { + + setColorMode(LCM_RGB); + } else { + ct = (ct < CT_MIN ? CT_MIN : (ct > CT_MAX ? CT_MAX : ct)); + _ww = changeUIntScale(ct, _ct_min_range, _ct_max_range, 0, 255); + _wc = 255 - _ww; + _ct = ct; + addCTMode(); + } +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setCT RGB raw (%d %d %d) HS (%d %d) briRGB (%d) briCT (%d) CT (%d)", _r, _g, _b, _hue, _sat, _briRGB, _briCT, _ct); +#endif + } +# 625 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_04_light.ino" + void setCW(uint8_t c, uint8_t w, bool free_range = false) { + uint16_t max = (w > c) ? w : c; + uint16_t sum = c + w; + + if (0 == max) { + _briCT = 0; + setColorMode(LCM_RGB); + } else { + if (!free_range) { + + _ww = changeUIntScale(w, 0, sum, 0, 255); + _wc = 255 - _ww; + } else { + _ww = changeUIntScale(w, 0, max, 0, 255); + _wc = changeUIntScale(c, 0, max, 0, 255); + } + _ct = changeUIntScale(w, 0, sum, _ct_min_range, _ct_max_range); + addCTMode(); + if (_color_mode & LCM_CT) { _briCT = free_range ? max : (sum > 255 ? 255 : sum); } + } +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setCW CW (%d %d) CT (%d) briCT (%d)", c, w, _ct, _briCT); +#endif + } + + + uint8_t setRGB(uint8_t r, uint8_t g, uint8_t b, bool keep_bri = false) { + uint16_t hue; + uint8_t sat; +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setRGB RGB input (%d %d %d)", r, g, b); +#endif + + uint32_t max = (r > g && r > b) ? r : (g > b) ? g : b; + + if (0 == max) { + r = g = b = 255; + setColorMode(LCM_CT); + } else { + if (255 > max) { + + r = changeUIntScale(r, 0, max, 0, 255); + g = changeUIntScale(g, 0, max, 0, 255); + b = changeUIntScale(b, 0, max, 0, 255); + } + addRGBMode(); + } + if (!keep_bri) { + _briRGB = (_color_mode & LCM_RGB) ? max : 0; + } + + RgbToHsb(r, g, b, &hue, &sat, nullptr); + _r = r; + _g = g; + _b = b; + _hue = hue; + _sat = sat; +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setRGB RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB); +#endif + return max; + } + + void setHS(uint16_t hue, uint8_t sat) { + uint8_t r, g, b; + HsToRgb(hue, sat, &r, &g, &b); + _r = r; + _g = g; + _b = b; + _hue = hue; + _sat = sat; + addRGBMode(); +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setHS HS (%d %d) rgb (%d %d %d)", hue, sat, r, g, b); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setHS RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB); +#endif + } + + + + void setChannelsRaw(uint8_t *channels) { + _r = channels[0]; + _g = channels[1]; + _b = channels[2]; + _wc = channels[3]; + _ww = channels[4]; +} + + + + + void setChannels(uint8_t *channels) { + setRGB(channels[0], channels[1], channels[2]); + setCW(channels[3], channels[4], true); +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels (%d %d %d %d %d)", + channels[0], channels[1], channels[2], channels[3], channels[4]); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels CT (%d) briRGB (%d) briCT (%d)", _ct, _briRGB, _briCT); +#endif + } + + + static void RgbToHsb(uint8_t r, uint8_t g, uint8_t b, uint16_t *r_hue, uint8_t *r_sat, uint8_t *r_bri); + static void HsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b); + static void RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x, float *r_y); + static void XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_t *rb); + +}; +# 741 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_04_light.ino" +void LightStateClass::RgbToHsb(uint8_t ir, uint8_t ig, uint8_t ib, uint16_t *r_hue, uint8_t *r_sat, uint8_t *r_bri) { + uint32_t r = ir; + uint32_t g = ig; + uint32_t b = ib; + uint32_t max = (r > g && r > b) ? r : (g > b) ? g : b; + uint32_t min = (r < g && r < b) ? r : (g < b) ? g : b; + uint32_t d = max - min; + + uint16_t hue = 0; + uint8_t sat = 0; + uint8_t bri = max; + + if (d != 0) { + sat = changeUIntScale(d, 0, max, 0, 255); + if (r == max) { + hue = (g > b) ? changeUIntScale(g-b,0,d,0,60) : 360 - changeUIntScale(b-g,0,d,0,60); + } else if (g == max) { + hue = (b > r) ? 120 + changeUIntScale(b-r,0,d,0,60) : 120 - changeUIntScale(r-b,0,d,0,60); + } else { + hue = (r > g) ? 240 + changeUIntScale(r-g,0,d,0,60) : 240 - changeUIntScale(g-r,0,d,0,60); + } + hue = hue % 360; + } + + if (r_hue) *r_hue = hue; + if (r_sat) *r_sat = sat; + if (r_bri) *r_bri = bri; + +} + +void LightStateClass::HsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b) { + uint32_t r = 255; + uint32_t g = 255; + uint32_t b = 255; + + hue = hue % 360; + + if (sat > 0) { + uint32_t i = hue / 60; + uint32_t f = hue % 60; + uint32_t q = 255 - changeUIntScale(f, 0, 60, 0, sat); + uint32_t p = 255 - sat; + uint32_t t = 255 - changeUIntScale(60 - f, 0, 60, 0, sat); + + switch (i) { + case 0: + + g = t; + b = p; + break; + case 1: + r = q; + + b = p; + break; + case 2: + r = p; + + b = t; + break; + case 3: + r = p; + g = q; + + break; + case 4: + r = t; + g = p; + + break; + default: + + g = p; + b = q; + break; + } + } + if (r_r) *r_r = r; + if (r_g) *r_g = g; + if (r_b) *r_b = b; +} + +#define POW FastPrecisePowf + +void LightStateClass::RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x, float *r_y) { + float x = 0.31271f; + float y = 0.32902f; + + if (i_r + i_b + i_g > 0) { + float r = (float)i_r / 255.0f; + float g = (float)i_g / 255.0f; + float b = (float)i_b / 255.0f; + + + r = (r > 0.04045f) ? POW((r + 0.055f) / (1.0f + 0.055f), 2.4f) : (r / 12.92f); + g = (g > 0.04045f) ? POW((g + 0.055f) / (1.0f + 0.055f), 2.4f) : (g / 12.92f); + b = (b > 0.04045f) ? POW((b + 0.055f) / (1.0f + 0.055f), 2.4f) : (b / 12.92f); + + + + float X = r * 0.649926f + g * 0.103455f + b * 0.197109f; + float Y = r * 0.234327f + g * 0.743075f + b * 0.022598f; + float Z = r * 0.000000f + g * 0.053077f + b * 1.035763f; + + x = X / (X + Y + Z); + y = Y / (X + Y + Z); + + } + if (r_x) *r_x = x; + if (r_y) *r_y = y; +} + +void LightStateClass::XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_t *rb) +{ + x = (x > 0.99f ? 0.99f : (x < 0.01f ? 0.01f : x)); + y = (y > 0.99f ? 0.99f : (y < 0.01f ? 0.01f : y)); + float z = 1.0f - x - y; + + float X = x / y; + float Z = z / y; + + + + float r = X * 3.2406f - 1.5372f - Z * 0.4986f; + float g = -X * 0.9689f + 1.8758f + Z * 0.0415f; + float b = X * 0.0557f - 0.2040f + Z * 1.0570f; + float max = (r > g && r > b) ? r : (g > b) ? g : b; + r = r / max; + g = g / max; + b = b / max; + r = (r <= 0.0031308f) ? 12.92f * r : 1.055f * POW(r, (1.0f / 2.4f)) - 0.055f; + g = (g <= 0.0031308f) ? 12.92f * g : 1.055f * POW(g, (1.0f / 2.4f)) - 0.055f; + b = (b <= 0.0031308f) ? 12.92f * b : 1.055f * POW(b, (1.0f / 2.4f)) - 0.055f; + + + + + + int32_t ir = r * 255.0f + 0.5f; + int32_t ig = g * 255.0f + 0.5f; + int32_t ib = b * 255.0f + 0.5f; + if (rr) { *rr = (ir > 255 ? 255: (ir < 0 ? 0 : ir)); } + if (rg) { *rg = (ig > 255 ? 255: (ig < 0 ? 0 : ig)); } + if (rb) { *rb = (ib > 255 ? 255: (ib < 0 ? 0 : ib)); } +} + +class LightControllerClass { +private: + LightStateClass *_state; + + + bool _ct_rgb_linked = true; + bool _pwm_multi_channels = false; + +public: + LightControllerClass(LightStateClass& state) { + _state = &state; + } + + void setSubType(uint8_t sub_type) { + _state->setSubType(sub_type); + } + + inline bool setCTRGBLinked(bool ct_rgb_linked) { + bool prev = _ct_rgb_linked; + if (_pwm_multi_channels) { + _ct_rgb_linked = false; + } else { + _ct_rgb_linked = ct_rgb_linked; + } + return prev; + } + + void setAlexaCTRange(bool alexa_ct_range) { + + if (alexa_ct_range) { + _state->setCTRange(CT_MIN_ALEXA, CT_MAX_ALEXA); + } else { + _state->setCTRange(CT_MIN, CT_MAX); + } + } + + inline bool isCTRGBLinked() { + return _ct_rgb_linked; + } + + inline bool setPWMMultiChannel(bool pwm_multi_channels) { + bool prev = _pwm_multi_channels; + _pwm_multi_channels = pwm_multi_channels; + if (pwm_multi_channels) setCTRGBLinked(false); + return prev; + } + + inline bool isPWMMultiChannel(void) { + return _pwm_multi_channels; + } + +#ifdef DEBUG_LIGHT + void debugLogs() { + uint8_t r,g,b,c,w; + _state->getActualRGBCW(&r,&g,&b,&c,&w); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs rgb (%d %d %d) cw (%d %d)", + r, g, b, c, w); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs lightCurrent (%d %d %d %d %d)", + Light.current_color[0], Light.current_color[1], Light.current_color[2], + Light.current_color[3], Light.current_color[4]); + } +#endif + + void loadSettings() { +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::loadSettings Settings.light_color (%d %d %d %d %d - %d)", + Settings.light_color[0], Settings.light_color[1], Settings.light_color[2], + Settings.light_color[3], Settings.light_color[4], Settings.light_dimmer); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::loadSettings light_type/sub (%d %d)", + light_type, Light.subtype); +#endif + if (_pwm_multi_channels) { + _state->setChannelsRaw(Settings.light_color); + } else { + + _state->setCW(Settings.light_color[3], Settings.light_color[4], true); + _state->setRGB(Settings.light_color[0], Settings.light_color[1], Settings.light_color[2]); + + + + uint8_t bri = _state->DimmerToBri(Settings.light_dimmer); + + + + if ((DEFAULT_LIGHT_COMPONENT == Settings.light_color[0]) && + (DEFAULT_LIGHT_COMPONENT == Settings.light_color[1]) && + (DEFAULT_LIGHT_COMPONENT == Settings.light_color[2]) && + (DEFAULT_LIGHT_COMPONENT == Settings.light_color[3]) && + (DEFAULT_LIGHT_COMPONENT == Settings.light_color[4]) && + (DEFAULT_LIGHT_DIMMER == Settings.light_dimmer) ) { + _state->setBriCT(bri); + _state->setBriRGB(bri); + _state->setColorMode(LCM_RGB); + } + + if (Settings.light_color[0] + Settings.light_color[1] + Settings.light_color[2] > 0) { + _state->setBriRGB(bri); + } else { + _state->setBriCT(bri); + } + } + } + + void changeCTB(uint16_t new_ct, uint8_t briCT) { + + + + + + + if ((LST_COLDWARM != Light.subtype) && (LST_RGBW > Light.subtype)) { + return; + } + _state->setCT(new_ct); + _state->setBriCT(briCT); + if (_ct_rgb_linked) { _state->setColorMode(LCM_CT); } + saveSettings(); + calcLevels(); + + } + + void changeDimmer(uint8_t dimmer, uint32_t mode = 0) { + uint8_t bri = changeUIntScale(dimmer, 0, 100, 0, 255); + switch (mode) { + case 1: + changeBriRGB(bri); + if (_ct_rgb_linked) { _state->setColorMode(LCM_RGB); } + break; + case 2: + changeBriCT(bri); + if (_ct_rgb_linked) { _state->setColorMode(LCM_CT); } + break; + default: + changeBri(bri); + break; + } + } + + void changeBri(uint8_t bri) { + _state->setBri(bri); + saveSettings(); + calcLevels(); + } + + void changeBriRGB(uint8_t bri) { + _state->setBriRGB(bri); + saveSettings(); + calcLevels(); + } + + void changeBriCT(uint8_t bri) { + _state->setBriCT(bri); + saveSettings(); + calcLevels(); + } + + void changeRGB(uint8_t r, uint8_t g, uint8_t b, bool keep_bri = false) { + _state->setRGB(r, g, b, keep_bri); + if (_ct_rgb_linked) { _state->setColorMode(LCM_RGB); } + saveSettings(); + calcLevels(); + } + + + + void calcLevels(uint8_t *current_color = nullptr) { + uint8_t r,g,b,c,w,briRGB,briCT; + if (current_color == nullptr) { current_color = Light.current_color; } + + if (_pwm_multi_channels) { + _state->getChannelsRaw(current_color); + return; + } + + _state->getActualRGBCW(&r,&g,&b,&c,&w); + briRGB = _state->getBriRGB(); + briCT = _state->getBriCT(); + + current_color[0] = current_color[1] = current_color[2] = 0; + current_color[3] = current_color[4] = 0; + switch (Light.subtype) { + case LST_NONE: + current_color[0] = 255; + break; + case LST_SINGLE: + current_color[0] = briRGB; + break; + case LST_COLDWARM: + current_color[0] = c; + current_color[1] = w; + break; + case LST_RGBW: + case LST_RGBCW: + if (LST_RGBCW == Light.subtype) { + current_color[3] = c; + current_color[4] = w; + } else { + current_color[3] = briCT; + } + + case LST_RGB: + current_color[0] = r; + current_color[1] = g; + current_color[2] = b; + break; + } + } + + void changeHSB(uint16_t hue, uint8_t sat, uint8_t briRGB) { + _state->setHS(hue, sat); + _state->setBriRGB(briRGB); + if (_ct_rgb_linked) { _state->setColorMode(LCM_RGB); } + saveSettings(); + calcLevels(); + } + + + void saveSettings() { + if (Light.pwm_multi_channels) { + + _state->getChannelsRaw(Settings.light_color); + Settings.light_dimmer = 100; + } else { + uint8_t cm = _state->getColorMode(); + + memset(&Settings.light_color[0], 0, sizeof(Settings.light_color)); + if (LCM_RGB & cm) { + _state->getRGB(&Settings.light_color[0], &Settings.light_color[1], &Settings.light_color[2]); + Settings.light_dimmer = _state->BriToDimmer(_state->getBriRGB()); + + if (LCM_BOTH == cm) { + + _state->getActualRGBCW(nullptr, nullptr, nullptr, &Settings.light_color[3], &Settings.light_color[4]); + } + } else if (LCM_CT == cm) { + _state->getCW(&Settings.light_color[3], &Settings.light_color[4]); + Settings.light_dimmer = _state->BriToDimmer(_state->getBriCT()); + } + } +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::saveSettings Settings.light_color (%d %d %d %d %d - %d)", + Settings.light_color[0], Settings.light_color[1], Settings.light_color[2], + Settings.light_color[3], Settings.light_color[4], Settings.light_dimmer); +#endif + } + + + + + void changeChannels(uint8_t *channels) { + if (Light.pwm_multi_channels) { + _state->setChannelsRaw(channels); + } else if (LST_COLDWARM == Light.subtype) { + + uint8_t remapped_channels[5] = {0,0,0,channels[0],channels[1]}; + _state->setChannels(remapped_channels); + } else { + _state->setChannels(channels); + } + + saveSettings(); + calcLevels(); + } +}; + + + +LightStateClass light_state = LightStateClass(); +LightControllerClass light_controller = LightControllerClass(light_state); + + + + + +uint16_t change8to10(uint8_t v) { + return changeUIntScale(v, 0, 255, 0, 1023); +} + +uint8_t change10to8(uint16_t v) { + return (0 == v) ? 0 : changeUIntScale(v, 4, 1023, 1, 255); +} + + + + + +uint16_t ledGamma_internal(uint16_t v, const struct gamma_table_t *gt_ptr) { + uint16_t from_src = 0; + uint16_t from_gamma = 0; + + for (const gamma_table_t *gt = gt_ptr; ; gt++) { + uint16_t to_src = gt->to_src; + uint16_t to_gamma = gt->to_gamma; + if (v <= to_src) { + return changeUIntScale(v, from_src, to_src, from_gamma, to_gamma); + } + from_src = to_src; + from_gamma = to_gamma; + } +} + +uint16_t ledGammaReverse_internal(uint16_t vg, const struct gamma_table_t *gt_ptr) { + uint16_t from_src = 0; + uint16_t from_gamma = 0; + + for (const gamma_table_t *gt = gt_ptr; ; gt++) { + uint16_t to_src = gt->to_src; + uint16_t to_gamma = gt->to_gamma; + if (vg <= to_gamma) { + return changeUIntScale(vg, from_gamma, to_gamma, from_src, to_src); + } + from_src = to_src; + from_gamma = to_gamma; + } +} + + +uint16_t ledGamma10_10(uint16_t v) { + return ledGamma_internal(v, gamma_table); +} + +uint16_t ledGamma10(uint8_t v) { + return ledGamma10_10(change8to10(v)); +} + + +uint8_t ledGamma(uint8_t v) { + return change10to8(ledGamma10(v)); +} + + + +void LightPwmOffset(uint32_t offset) +{ + Light.pwm_offset = offset; +} + +bool LightModuleInit(void) +{ + light_type = LT_BASIC; + + if (Settings.flag.pwm_control) { + for (uint32_t i = 0; i < MAX_PWMS; i++) { + if (pin[GPIO_PWM1 +i] < 99) { light_type++; } + } + } + + light_flg = 0; + if (XlgtCall(FUNC_MODULE_INIT)) { + + } +#ifdef ESP8266 + else if (SONOFF_BN == my_module_type) { + light_type = LT_PWM1; + } + else if (SONOFF_LED == my_module_type) { + if (!my_module.io[4]) { + pinMode(4, OUTPUT); + digitalWrite(4, LOW); + } + if (!my_module.io[5]) { + pinMode(5, OUTPUT); + digitalWrite(5, LOW); + } + if (!my_module.io[14]) { + pinMode(14, OUTPUT); + digitalWrite(14, LOW); + } + light_type = LT_PWM2; + } +#endif + + if (light_type > LT_BASIC) { + devices_present++; + } + + + if (Settings.flag3.pwm_multi_channels) { + uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); + if (0 == pwm_channels) { pwm_channels = 1; } + devices_present += pwm_channels - 1; + } else if ((Settings.param[P_RGB_REMAP] & 128) && (LST_RGBW <= (light_type & 7))) { + + devices_present++; + } + + return (light_type > LT_BASIC); +} + + + +void LightCalcPWMRange(void) { + uint16_t pwm_min, pwm_max; + + pwm_min = change8to10(LightStateClass::DimmerToBri(Settings.dimmer_hw_min)); + pwm_max = change8to10(LightStateClass::DimmerToBri(Settings.dimmer_hw_max)); + if (Settings.light_correction) { + pwm_min = ledGamma10_10(pwm_min); + pwm_max = ledGamma10_10(pwm_max); + } + pwm_min = pwm_min > 0 ? changeUIntScale(pwm_min, 1, 1023, 1, Settings.pwm_range) : 0; + pwm_max = changeUIntScale(pwm_max, 1, 1023, 1, Settings.pwm_range); + + Light.pwm_min = pwm_min; + Light.pwm_max = pwm_max; + +} + +void LightInit(void) +{ + Light.device = devices_present; + Light.subtype = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); + Light.pwm_multi_channels = Settings.flag3.pwm_multi_channels; + + if (LST_RGBW <= Light.subtype) { + + + bool ct_rgb_linked = !(Settings.param[P_RGB_REMAP] & 128); + light_controller.setCTRGBLinked(ct_rgb_linked); + } + + if ((LST_SINGLE <= Light.subtype) && Light.pwm_multi_channels) { + + light_controller.setPWMMultiChannel(true); + Light.device = devices_present - Light.subtype + 1; + } else if (!light_controller.isCTRGBLinked()) { + + Light.device--; + } + LightCalcPWMRange(); +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightInit Light.pwm_multi_channels=%d Light.subtype=%d Light.device=%d devices_present=%d", + Light.pwm_multi_channels, Light.subtype, Light.device, devices_present); +#endif + + light_controller.setSubType(Light.subtype); + light_controller.loadSettings(); + light_controller.setAlexaCTRange(Settings.flag4.alexa_ct_range); + light_controller.calcLevels(); + + if (LST_SINGLE == Light.subtype) { + Settings.light_color[0] = 255; + } + if (light_type < LT_PWM6) { + for (uint32_t i = 0; i < light_type; i++) { + Settings.pwm_value[i] = 0; + if (pin[GPIO_PWM1 +i] < 99) { + pinMode(pin[GPIO_PWM1 +i], OUTPUT); + } + } + if (pin[GPIO_ARIRFRCV] < 99) { + if (pin[GPIO_ARIRFSEL] < 99) { + pinMode(pin[GPIO_ARIRFSEL], OUTPUT); + digitalWrite(pin[GPIO_ARIRFSEL], 1); + } + } + } + + uint32_t max_scheme = Light.max_scheme; + if (Light.subtype < LST_RGB) { + max_scheme = LS_POWER; + } + if ((LS_WAKEUP == Settings.light_scheme) || (Settings.light_scheme > max_scheme)) { + Settings.light_scheme = LS_POWER; + } + Light.power = 0; + Light.update = true; + Light.wakeup_active = 0; + if (Settings.flag4.fade_at_startup) { + Light.fade_initialized = true; + } + + LightUpdateColorMapping(); +} + +void LightUpdateColorMapping(void) +{ + uint8_t param = Settings.param[P_RGB_REMAP] & 127; + if (param > 119){ param = 0; } + + uint8_t tmp[] = {0,1,2,3,4}; + Light.color_remap[0] = tmp[param / 24]; + for (uint32_t i = param / 24; i<4; ++i){ + tmp[i] = tmp[i+1]; + } + param = param % 24; + Light.color_remap[1] = tmp[(param / 6)]; + for (uint32_t i = param / 6; i<3; ++i){ + tmp[i] = tmp[i+1]; + } + param = param % 6; + Light.color_remap[2] = tmp[(param / 2)]; + for (uint32_t i = param / 2; i<2; ++i){ + tmp[i] = tmp[i+1]; + } + param = param % 2; + Light.color_remap[3] = tmp[param]; + Light.color_remap[4] = tmp[1-param]; + + Light.update = true; + +} + +uint8_t LightGetDimmer(uint8_t dimmer) { + return light_state.getDimmer(dimmer); +} + +void LightSetDimmer(uint8_t dimmer) { + light_controller.changeDimmer(dimmer); +} + +void LightGetHSB(uint16_t *hue, uint8_t *sat, uint8_t *bri) { + light_state.getHSB(hue, sat, bri); +} + +void LightHsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b) { + light_state.HsToRgb(hue, sat, r_r, r_g, r_b); +} + + +uint8_t LightGetBri(uint8_t device) { + uint8_t bri = 254; + if (Light.pwm_multi_channels) { + if ((device >= Light.device) && (device < Light.device + LST_MAX) && (device <= devices_present)) { + bri = Light.current_color[device - Light.device]; + } + } else if (light_controller.isCTRGBLinked()) { + if (device == Light.device) { + bri = light_state.getBri(); + } + } else { + if (device == Light.device) { + bri = light_state.getBriRGB(); + } else if (device == Light.device + 1) { + bri = light_state.getBriCT(); + } + } + return bri; +} + + +void LightSetBri(uint8_t device, uint8_t bri) { + if (Light.pwm_multi_channels) { + if ((device >= Light.device) && (device < Light.device + LST_MAX) && (device <= devices_present)) { + Light.current_color[device - Light.device] = bri; + light_controller.changeChannels(Light.current_color); + } + } else if (light_controller.isCTRGBLinked()) { + if (device == Light.device) { + light_controller.changeBri(bri); + } + } else { + if (device == Light.device) { + light_controller.changeBriRGB(bri); + } else if (device == Light.device + 1) { + light_controller.changeBriCT(bri); + } + } +} + +void LightSetColorTemp(uint16_t ct) +{ + + + + + + + if ((LST_COLDWARM != Light.subtype) && (LST_RGBCW != Light.subtype)) { + return; + } + light_controller.changeCTB(ct, light_state.getBriCT()); +} + +uint16_t LightGetColorTemp(void) +{ + + if ((LST_COLDWARM != Light.subtype) && (LST_RGBCW != Light.subtype)) { + return 0; + } + return (light_state.getColorMode() & LCM_CT) ? light_state.getCT() : 0; +} + +void LightSetSignal(uint16_t lo, uint16_t hi, uint16_t value) +{ + + + + if (Settings.flag.light_signal) { + uint16_t signal = changeUIntScale(value, lo, hi, 0, 255); + + light_controller.changeRGB(signal, 255 - signal, 0, true); + Settings.light_scheme = 0; +#ifdef USE_DEVICE_GROUPS + LightUpdateScheme(); +#endif + if (0 == light_state.getBri()) { + light_controller.changeBri(50); + } + } +} + + +char* LightGetColor(char* scolor, boolean force_hex = false) +{ + if ((0 == Settings.light_scheme) || (!Light.pwm_multi_channels)) { + light_controller.calcLevels(); + } + scolor[0] = '\0'; + for (uint32_t i = 0; i < Light.subtype; i++) { + if (!force_hex && Settings.flag.decimal_text) { + snprintf_P(scolor, LIGHT_COLOR_SIZE, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Light.current_color[i]); + } else { + snprintf_P(scolor, LIGHT_COLOR_SIZE, PSTR("%s%02X"), scolor, Light.current_color[i]); + } + } + return scolor; +} + +void LightPowerOn(void) +{ + if (light_state.getBri() && !(Light.power)) { + ExecuteCommandPower(Light.device, POWER_ON, SRC_LIGHT); + } +} + +void LightState(uint8_t append) +{ + char scolor[LIGHT_COLOR_SIZE]; + char scommand[33]; + bool unlinked = !light_controller.isCTRGBLinked() && (Light.subtype >= LST_RGBW); + + if (append) { + ResponseAppend_P(PSTR(",")); + } else { + Response_P(PSTR("{")); + } + if (!Light.pwm_multi_channels) { + if (unlinked) { + + ResponseAppend_P(PSTR("\"" D_RSLT_POWER "%d\":\"%s\",\"" D_CMND_DIMMER "%d\":%d" + ",\"" D_RSLT_POWER "%d\":\"%s\",\"" D_CMND_DIMMER "%d\":%d"), + Light.device, GetStateText(Light.power & 1), Light.device, light_state.getDimmer(1), + Light.device + 1, GetStateText(Light.power & 2 ? 1 : 0), Light.device + 1, light_state.getDimmer(2)); + } else { + GetPowerDevice(scommand, Light.device, sizeof(scommand), Settings.flag.device_index_enable); + ResponseAppend_P(PSTR("\"%s\":\"%s\",\"" D_CMND_DIMMER "\":%d"), scommand, GetStateText(Light.power & 1), + light_state.getDimmer()); + } + + + if (Light.subtype > LST_SINGLE) { + ResponseAppend_P(PSTR(",\"" D_CMND_COLOR "\":\"%s\""), LightGetColor(scolor)); + if (LST_RGB <= Light.subtype) { + uint16_t hue; + uint8_t sat, bri; + light_state.getHSB(&hue, &sat, &bri); + sat = changeUIntScale(sat, 0, 255, 0, 100); + bri = changeUIntScale(bri, 0, 255, 0, 100); + + ResponseAppend_P(PSTR(",\"" D_CMND_HSBCOLOR "\":\"%d,%d,%d\""), hue,sat,bri); + } + + if ((LST_COLDWARM == Light.subtype) || (LST_RGBW <= Light.subtype)) { + ResponseAppend_P(PSTR(",\"" D_CMND_WHITE "\":%d"), light_state.getDimmer(2)); + } + + if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { + ResponseAppend_P(PSTR(",\"" D_CMND_COLORTEMPERATURE "\":%d"), light_state.getCT()); + } + + ResponseAppend_P(PSTR(",\"" D_CMND_CHANNEL "\":[" )); + for (uint32_t i = 0; i < Light.subtype; i++) { + uint8_t channel_raw = Light.current_color[i]; + uint8_t channel = changeUIntScale(channel_raw,0,255,0,100); + + if ((0 == channel) && (channel_raw > 0)) { channel = 1; } + ResponseAppend_P(PSTR("%s%d" ), (i > 0 ? "," : ""), channel); + } + ResponseAppend_P(PSTR("]")); + } + + if (append) { + if (Light.subtype >= LST_RGB) { + ResponseAppend_P(PSTR(",\"" D_CMND_SCHEME "\":%d"), Settings.light_scheme); + } + if (Light.max_scheme > LS_MAX) { + ResponseAppend_P(PSTR(",\"" D_CMND_WIDTH "\":%d"), Settings.light_width); + } + ResponseAppend_P(PSTR(",\"" D_CMND_FADE "\":\"%s\",\"" D_CMND_SPEED "\":%d,\"" D_CMND_LEDTABLE "\":\"%s\""), + GetStateText(Settings.light_fade), Settings.light_speed, GetStateText(Settings.light_correction)); + } + } else { + for (uint32_t i = 0; i < Light.subtype; i++) { + GetPowerDevice(scommand, Light.device + i, sizeof(scommand), 1); + uint32_t light_power_masked = Light.power & (1 << i); + light_power_masked = light_power_masked ? 1 : 0; + ResponseAppend_P(PSTR("\"%s\":\"%s\",\"" D_CMND_CHANNEL "%d\":%d,"), scommand, GetStateText(light_power_masked), Light.device + i, + changeUIntScale(Light.current_color[i], 0, 255, 0, 100)); + } + ResponseAppend_P(PSTR("\"" D_CMND_COLOR "\":\"%s\""), LightGetColor(scolor)); + } + + if (!append) { + ResponseJsonEnd(); + } +} + +void LightPreparePower(power_t channels = 0xFFFFFFFF) { +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG, "LightPreparePower power=%d Light.power=%d", power, Light.power); +#endif + + if (Light.pwm_multi_channels) { + for (uint32_t i = 0; i < Light.subtype; i++) { + if (bitRead(channels, i)) { + + if ((Light.current_color[i]) && (!bitRead(Light.power, i))) { + if (!Settings.flag.not_power_linked) { + ExecuteCommandPower(Light.device + i, POWER_ON_NO_STATE, SRC_LIGHT); + } + } else { + + if ((0 == Light.current_color[i]) && bitRead(Light.power, i)) { + ExecuteCommandPower(Light.device + i, POWER_OFF_NO_STATE, SRC_LIGHT); + } + } + #ifdef USE_DOMOTICZ + DomoticzUpdatePowerState(Light.device + i); + #endif + } + } + } else { + if (light_controller.isCTRGBLinked()) { + if (light_state.getBri() && !(Light.power)) { + if (!Settings.flag.not_power_linked) { + ExecuteCommandPower(Light.device, POWER_ON_NO_STATE, SRC_LIGHT); + } + } else if (!light_state.getBri() && Light.power) { + ExecuteCommandPower(Light.device, POWER_OFF_NO_STATE, SRC_LIGHT); + } + } else { + + if (channels & 1) { + if (light_state.getBriRGB() && !(Light.power & 1)) { + if (!Settings.flag.not_power_linked) { + ExecuteCommandPower(Light.device, POWER_ON_NO_STATE, SRC_LIGHT); + } + } else if (!light_state.getBriRGB() && (Light.power & 1)) { + ExecuteCommandPower(Light.device, POWER_OFF_NO_STATE, SRC_LIGHT); + } + } + + if (channels & 2) { + if (light_state.getBriCT() && !(Light.power & 2)) { + if (!Settings.flag.not_power_linked) { + ExecuteCommandPower(Light.device + 1, POWER_ON_NO_STATE, SRC_LIGHT); + } + } else if (!light_state.getBriCT() && (Light.power & 2)) { + ExecuteCommandPower(Light.device + 1, POWER_OFF_NO_STATE, SRC_LIGHT); + } + } + } +#ifdef USE_DOMOTICZ + DomoticzUpdatePowerState(Light.device); +#endif + } + + if (Settings.flag3.hass_tele_on_power) { + MqttPublishTeleState(); + } + +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG, "LightPreparePower End power=%d Light.power=%d", power, Light.power); +#endif + Light.power = power >> (Light.device - 1); + LightState(0); +} + +#ifdef USE_LIGHT_PALETTE +void LightSetPaletteEntry(void) +{ + uint8_t bri = light_state.getBri(); + uint8_t * palette_entry = &Light.palette[Light.wheel * LST_MAX]; + for (int i = 0; i < LST_MAX; i++) { + Light.new_color[i] = changeUIntScale(palette_entry[i], 0, 255, 0, bri); + } + light_state.setChannelsRaw(Light.new_color); + if (!Light.pwm_multi_channels) { + light_state.setCW(Light.new_color[3], Light.new_color[4], true); + if (Light.new_color[0] || Light.new_color[1] || Light.new_color[2]) light_state.addRGBMode(); + } +} +#endif + +void LightCycleColor(int8_t direction) +{ + + if (Settings.light_speed > 3) { + if (Light.strip_timer_counter % (Settings.light_speed - 2)) { return; } + } + +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count) { + if (0 == direction) { + Light.wheel = random(Light.palette_count); + } + else { + Light.wheel += direction; + if (Light.wheel >= Light.palette_count) { + Light.wheel = 0; + if (direction < 0) Light.wheel = Light.palette_count - 1; + } + } + LightSetPaletteEntry(); + return; + } +#endif + + if (0 == direction) { + if (Light.random == Light.wheel) { + Light.random = random(255); + + uint8_t my_dir = (Light.random < Light.wheel -128) ? 1 : + (Light.random < Light.wheel ) ? 0 : + (Light.random > Light.wheel +128) ? 0 : 1; + Light.random = (Light.random & 0xFE) | my_dir; + + + } + + direction = (Light.random &0x01) ? 1 : -1; + } + + + if (Settings.light_speed < 3) { direction *= (4 - Settings.light_speed); } + Light.wheel += direction; + uint16_t hue = changeUIntScale(Light.wheel, 0, 255, 0, 359); + + + + if (!Light.pwm_multi_channels) { + uint8_t sat; + light_state.getHSB(nullptr, &sat, nullptr); + light_state.setHS(hue, sat); + } else { + light_state.setHS(hue, 255); + light_state.setBri(255); + } + light_controller.calcLevels(Light.new_color); +} + +void LightSetPower(void) +{ + + Light.old_power = Light.power; + + uint32_t mask = 1; + if (Light.pwm_multi_channels) { + mask = (1 << Light.subtype) - 1; + } else if (!light_controller.isCTRGBLinked()) { + mask = 3; + } + uint32_t shift = Light.device - 1; + + + + + + Light.power = (XdrvMailbox.index & (mask << shift)) >> shift; + if (Light.wakeup_active) { + Light.wakeup_active--; + } +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightSetPower XdrvMailbox.index=%d Light.old_power=%d Light.power=%d mask=%d shift=%d", + XdrvMailbox.index, Light.old_power, Light.power, mask, shift); +#endif + if (Light.power != Light.old_power) { + Light.update = true; + } + LightAnimate(); +} + + + + +void LightAnimate(void) +{ + uint16_t light_still_on = 0; + bool power_off = false; + + + light_controller.setAlexaCTRange(Settings.flag4.alexa_ct_range); + Light.strip_timer_counter++; + + + + if (Light.power || Light.fade_running) { + if (Settings.sleep > PWM_MAX_SLEEP) { + ssleep = PWM_MAX_SLEEP; + } else { + ssleep = Settings.sleep; + } + } else { + ssleep = Settings.sleep; + } + + if (!Light.power) { + Light.strip_timer_counter = 0; + if (Settings.light_scheme >= LS_MAX) { + power_off = true; + } + } else { + switch (Settings.light_scheme) { + case LS_POWER: + light_controller.calcLevels(Light.new_color); + break; + case LS_WAKEUP: + if (2 == Light.wakeup_active) { + Light.wakeup_active = 1; + for (uint32_t i = 0; i < Light.subtype; i++) { + Light.new_color[i] = 0; + } + Light.wakeup_counter = 0; + Light.wakeup_dimmer = 0; + } + Light.wakeup_counter++; + if (Light.wakeup_counter > ((Settings.light_wakeup * STATES) / Settings.light_dimmer)) { + Light.wakeup_counter = 0; + Light.wakeup_dimmer++; + if (Light.wakeup_dimmer <= Settings.light_dimmer) { + light_state.setDimmer(Light.wakeup_dimmer); + light_controller.calcLevels(); + for (uint32_t i = 0; i < Light.subtype; i++) { + Light.new_color[i] = Light.current_color[i]; + } + } else { + + + + + Response_P(PSTR("{\"" D_CMND_WAKEUP "\":\"" D_JSON_DONE "\"")); + LightState(1); + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_WAKEUP)); + XdrvRulesProcess(); + + Light.wakeup_active = 0; + Settings.light_scheme = LS_POWER; +#ifdef USE_DEVICE_GROUPS + LightUpdateScheme(); +#endif + } + } + break; + case LS_CYCLEUP: + case LS_CYCLEDN: + case LS_RANDOM: + if (LS_CYCLEUP == Settings.light_scheme) { + LightCycleColor(1); + } else if (LS_CYCLEDN == Settings.light_scheme) { + LightCycleColor(-1); + } else { + LightCycleColor(0); + } + if (Light.pwm_multi_channels) { + Light.new_color[0] = changeUIntScale(Light.new_color[0], 0, 255, 0, Settings.light_color[0]); + Light.new_color[1] = changeUIntScale(Light.new_color[1], 0, 255, 0, Settings.light_color[1]); + Light.new_color[2] = changeUIntScale(Light.new_color[2], 0, 255, 0, Settings.light_color[2]); + } + break; + default: + XlgtCall(FUNC_SET_SCHEME); + } + } + + if ((Settings.light_scheme < LS_MAX) || power_off) { + + + LightApplyPower(Light.new_color, Light.power); + + + + + + + + if (memcmp(Light.last_color, Light.new_color, Light.subtype)) { + Light.update = true; + } + if (Light.update) { +#ifdef USE_DEVICE_GROUPS + if (Light.power) LightSendDeviceGroupStatus(false); +#endif + + uint16_t cur_col_10[LST_MAX]; + Light.update = false; + + + for (uint32_t i = 0; i < LST_MAX; i++) { + Light.last_color[i] = Light.new_color[i]; + + cur_col_10[i] = change8to10(Light.new_color[i]); + } + + if (Light.pwm_multi_channels) { + calcGammaMultiChannels(cur_col_10); + } else { + calcGammaBulbs(cur_col_10); + + + + if ((LST_RGBW <= Light.subtype) && (0 == Settings.rgbwwTable[4]) && (0 == cur_col_10[3]+cur_col_10[4])) { + uint32_t min_rgb_10 = min3(cur_col_10[0], cur_col_10[1], cur_col_10[2]); + for (uint32_t i=0; i<3; i++) { + + uint32_t adjust10 = change8to10(Settings.rgbwwTable[i]); + cur_col_10[i] = changeUIntScale(cur_col_10[i] - min_rgb_10, 0, 1023, 0, adjust10); + } + + + uint32_t adjust_w_10 = changeUIntScale(Settings.rgbwwTable[3], 0, 255, 0, 1023); + uint32_t white_10 = changeUIntScale(min_rgb_10, 0, 1023, 0, adjust_w_10); + if (LST_RGBW == Light.subtype) { + + cur_col_10[3] = white_10; + } else { + + uint32_t ct = light_state.getCT10bits(); + cur_col_10[4] = changeUIntScale(ct, 0, 1023, 0, white_10); + cur_col_10[3] = white_10 - cur_col_10[4]; + } + } + } + + + if (0 != Settings.rgbwwTable[4]) { + for (uint32_t i = 0; i 0) ? changeUIntScale(cur_col_10[i], 1, 1023, 1, Settings.pwm_range) : 0; + } + + + uint16_t orig_col_10bits[LST_MAX]; + memcpy(orig_col_10bits, cur_col_10, sizeof(orig_col_10bits)); + for (uint32_t i = 0; i < LST_MAX; i++) { + cur_col_10[i] = orig_col_10bits[Light.color_remap[i]]; + } + + if (!Settings.light_fade || skip_light_fade || power_off || (!Light.fade_initialized)) { + + memcpy(Light.fade_start_10, cur_col_10, sizeof(Light.fade_start_10)); + + LightSetOutputs(cur_col_10); + Light.fade_initialized = true; + } else { + if (Light.fade_running) { + + memcpy(Light.fade_start_10, Light.fade_cur_10, sizeof(Light.fade_start_10)); + } + memcpy(Light.fade_end_10, cur_col_10, sizeof(Light.fade_start_10)); + Light.fade_running = true; + Light.fade_duration = 0; + Light.fade_start = 0; + + } + } + if (Light.fade_running) { + if (LightApplyFade()) { + + + + LightSetOutputs(Light.fade_cur_10); + } + } +#ifdef USE_PWM_DIMMER + + if (PWM_DIMMER == my_module_type && !Light.power && !Light.fade_running) PWMDimmerSetPower(); +#endif + } +} + +bool isChannelGammaCorrected(uint32_t channel) { + if (!Settings.light_correction) { return false; } + if (channel >= Light.subtype) { return false; } +#ifdef ESP8266 + if (PHILIPS == my_module_type) { + if ((LST_COLDWARM == Light.subtype) && (1 == channel)) { return false; } + if ((LST_RGBCW == Light.subtype) && (4 == channel)) { return false; } + } +#endif + return true; +} + + +bool isChannelCT(uint32_t channel) { +#ifdef ESP8266 + if (PHILIPS == my_module_type) { + if ((LST_COLDWARM == Light.subtype) && (1 == channel)) { return true; } + if ((LST_RGBCW == Light.subtype) && (4 == channel)) { return true; } + } +#endif + return false; +} + + +uint16_t fadeGamma(uint32_t channel, uint16_t v) { + if (isChannelGammaCorrected(channel)) { + return ledGamma_internal(v, gamma_table_fast); + } else { + return v; + } +} +uint16_t fadeGammaReverse(uint32_t channel, uint16_t vg) { + if (isChannelGammaCorrected(channel)) { + return ledGammaReverse_internal(vg, gamma_table_fast); + } else { + return vg; + } +} + +bool LightApplyFade(void) { + static uint32_t last_millis = 0; + uint32_t now = millis(); + + if ((now - last_millis) <= 5) { + return false; + } + last_millis = now; + + + if (0 == Light.fade_duration) { + Light.fade_start = now; + + uint32_t distance = 0; + for (uint32_t i = 0; i < Light.subtype; i++) { + int32_t channel_distance = fadeGammaReverse(i, Light.fade_end_10[i]) - fadeGammaReverse(i, Light.fade_start_10[i]); + if (channel_distance < 0) { channel_distance = - channel_distance; } + if (channel_distance > distance) { distance = channel_distance; } + } + if (distance > 0) { + + + + Light.fade_duration = (distance * Settings.light_speed * 500) / 1023; + if (Settings.save_data) { + + uint32_t delay_seconds = 1 + (Light.fade_duration + 999) / 1000; + + if (save_data_counter < delay_seconds) { + save_data_counter = delay_seconds; + } + } + } else { + + Light.fade_running = false; + } + } + + uint16_t fade_current = now - Light.fade_start; + if (fade_current <= Light.fade_duration) { + + for (uint32_t i = 0; i < Light.subtype; i++) { + Light.fade_cur_10[i] = fadeGamma(i, + changeUIntScale(fadeGammaReverse(i, fade_current), + 0, Light.fade_duration, + fadeGammaReverse(i, Light.fade_start_10[i]), + fadeGammaReverse(i, Light.fade_end_10[i]))); + + + + } + } else { + + + Light.fade_running = false; + Light.fade_start = 0; + Light.fade_duration = 0; + + memcpy(Light.fade_cur_10, Light.fade_end_10, sizeof(Light.fade_end_10)); + + memcpy(Light.fade_start_10, Light.fade_end_10, sizeof(Light.fade_start_10)); + } + return true; +} + + + +void LightApplyPower(uint8_t new_color[LST_MAX], power_t power) { + + if (Light.pwm_multi_channels) { + + for (uint32_t i = 0; i < LST_MAX; i++) { + if (0 == bitRead(power,i)) { + new_color[i] = 0; + } + } + + + + + + } else { + if (!light_controller.isCTRGBLinked()) { + + if (0 == (power & 1)) { + new_color[0] = new_color[1] = new_color[2] = 0; + } + if (0 == (power & 2)) { + new_color[3] = new_color[4] = 0; + } + } else if (!power) { + for (uint32_t i = 0; i < LST_MAX; i++) { + new_color[i] = 0; + } + } + } +} + +void LightSetOutputs(const uint16_t *cur_col_10) { + + if (light_type < LT_PWM6) { + for (uint32_t i = 0; i < (Light.subtype - Light.pwm_offset); i++) { + if (pin[GPIO_PWM1 +i] < 99) { + + uint16_t cur_col = cur_col_10[i + Light.pwm_offset]; + if (!isChannelCT(i)) { + cur_col = cur_col > 0 ? changeUIntScale(cur_col, 0, Settings.pwm_range, Light.pwm_min, Light.pwm_max) : 0; + } + analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - cur_col : cur_col); + } + } + } + + + + + uint8_t cur_col[LST_MAX]; + for (uint32_t i = 0; i < LST_MAX; i++) { + cur_col[i] = change10to8(cur_col_10[i]); + } + + + uint8_t scale_col[3]; + uint32_t max = (cur_col[0] > cur_col[1] && cur_col[0] > cur_col[2]) ? cur_col[0] : (cur_col[1] > cur_col[2]) ? cur_col[1] : cur_col[2]; + for (uint32_t i = 0; i < 3; i++) { + scale_col[i] = (0 == max) ? 255 : (255 > max) ? changeUIntScale(cur_col[i], 0, max, 0, 255) : cur_col[i]; + } + + char *tmp_data = XdrvMailbox.data; + char *tmp_topic = XdrvMailbox.topic; + XdrvMailbox.data = (char*)cur_col; + XdrvMailbox.topic = (char*)scale_col; + if (XlgtCall(FUNC_SET_CHANNELS)) { } + else if (XdrvCall(FUNC_SET_CHANNELS)) { } + XdrvMailbox.data = tmp_data; + XdrvMailbox.topic = tmp_topic; +} + + +void calcGammaMultiChannels(uint16_t cur_col_10[5]) { + + if (Settings.light_correction) { + for (uint32_t i = 0; i < LST_MAX; i++) { + cur_col_10[i] = ledGamma10_10(cur_col_10[i]); + } + } +} + +void calcGammaBulbs(uint16_t cur_col_10[5]) { + + + + if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { + + uint32_t cw1 = Light.subtype - 1; + uint32_t cw0 = Light.subtype - 2; + uint16_t white_bri10 = cur_col_10[cw0] + cur_col_10[cw1]; + uint16_t white_bri10_1023 = (white_bri10 > 1023) ? 1023 : white_bri10; + +#ifdef ESP8266 + if (PHILIPS == my_module_type) { + + cur_col_10[cw1] = light_state.getCT10bits(); + + if (Settings.light_correction) { + cur_col_10[cw0] = ledGamma10_10(white_bri10_1023); + } else { + cur_col_10[cw0] = white_bri10_1023; + } + } else +#endif + if (Settings.light_correction) { + + if (white_bri10 <= 1031) { + + uint16_t white_bri_gamma10 = ledGamma10_10(white_bri10_1023); + + cur_col_10[cw0] = changeUIntScale(cur_col_10[cw0], 0, white_bri10_1023, 0, white_bri_gamma10); + cur_col_10[cw1] = changeUIntScale(cur_col_10[cw1], 0, white_bri10_1023, 0, white_bri_gamma10); + } else { + cur_col_10[cw0] = ledGamma10_10(cur_col_10[cw0]); + cur_col_10[cw1] = ledGamma10_10(cur_col_10[cw1]); + } + } + } + + if (Settings.light_correction) { + + if (LST_RGB <= Light.subtype) { + for (uint32_t i = 0; i < 3; i++) { + cur_col_10[i] = ledGamma10_10(cur_col_10[i]); + } + } + + if ((LST_SINGLE == Light.subtype) || (LST_RGBW == Light.subtype)) { + cur_col_10[Light.subtype - 1] = ledGamma10_10(cur_col_10[Light.subtype - 1]); + } + } +} + +#ifdef USE_DEVICE_GROUPS +void LightSendDeviceGroupStatus(bool force) +{ + static uint8_t last_channels[LST_MAX]; + static uint8_t last_bri; + + uint8_t bri = light_state.getBri(); + bool send_bri_update = (force || bri != last_bri); + + if (Light.subtype > LST_SINGLE && !Light.devgrp_no_channels_out) { + uint8_t channels[LST_MAX]; + light_state.getChannels(channels); + if (force || memcmp(last_channels, channels, LST_MAX)) { + memcpy(last_channels, channels, LST_MAX); + SendLocalDeviceGroupMessage((send_bri_update ? DGR_MSGTYP_PARTIAL_UPDATE : DGR_MSGTYP_UPDATE), DGR_ITEM_LIGHT_CHANNELS, channels); + } + } + if (send_bri_update) { + last_bri = bri; + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_BRI, light_state.getBri()); + } +} + +void LightHandleDevGroupItem(void) +{ + static bool send_state = false; + static bool restore_power = false; + bool more_to_come; + uint32_t value = XdrvMailbox.payload; +#ifdef USE_PWM_DIMMER_REMOTE + if (*XdrvMailbox.topic) return; +#endif + switch (XdrvMailbox.command_code) { + case DGR_ITEM_EOL: + more_to_come = (XdrvMailbox.index & DGR_FLAG_MORE_TO_COME); + if (restore_power && !more_to_come) { + restore_power = false; + Light.power = Light.old_power; + } + + LightAnimate(); + + if (send_state && !more_to_come) { + light_controller.saveSettings(); + if (Settings.flag3.hass_tele_on_power) { + MqttPublishTeleState(); + } + send_state = false; + } + break; + case DGR_ITEM_LIGHT_BRI: + if (light_state.getBri() != value) { + light_state.setBri(value); + send_state = true; + } + break; + case DGR_ITEM_LIGHT_SCHEME: + if (Settings.light_scheme != value) { + Settings.light_scheme = value; + Light.devgrp_no_channels_out = (value != 0); + send_state = true; + } + break; + case DGR_ITEM_LIGHT_CHANNELS: + light_controller.changeChannels((uint8_t *)XdrvMailbox.data); + send_state = true; + break; + case DGR_ITEM_LIGHT_FIXED_COLOR: + if (Light.subtype >= LST_RGBW) { + send_state = true; +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count) { + Light.wheel = value % Light.palette_count; + LightSetPaletteEntry(); + break; + } +#endif + value = value % MAX_FIXED_COLOR; + if (value) { + bool save_decimal_text = Settings.flag.decimal_text; + char str[16]; + LightColorEntry(str, sprintf_P(str, PSTR("%u"), value)); + Settings.flag.decimal_text = save_decimal_text; + uint32_t old_bri = light_state.getBri(); + light_controller.changeChannels(Light.entry_color); + light_controller.changeBri(old_bri); + Settings.light_scheme = 0; + Light.devgrp_no_channels_out = false; + } + else { + light_state.setColorMode(LCM_CT); + } + if (!restore_power && !Light.power) { + Light.old_power = Light.power; + Light.power = 0xff; + restore_power = true; + } + } + break; + case DGR_ITEM_LIGHT_FADE: + if (Settings.light_fade != value) { + Settings.light_fade = value; + send_state = true; + } + break; + case DGR_ITEM_LIGHT_SPEED: + if (Settings.light_speed != value && value > 0 && value <= 40) { + Settings.light_speed = value; + send_state = true; + } + break; + case DGR_ITEM_STATUS: + SendLocalDeviceGroupMessage(DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_LIGHT_FADE, Settings.light_fade, + DGR_ITEM_LIGHT_SPEED, Settings.light_speed, DGR_ITEM_LIGHT_SCHEME, Settings.light_scheme); + LightSendDeviceGroupStatus(true); + break; + } +} + +void LightUpdateScheme(void) +{ + static uint8_t last_scheme; + + if (Settings.light_scheme != last_scheme) { + last_scheme = Settings.light_scheme; + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_SCHEME, Settings.light_scheme); + } + Light.devgrp_no_channels_out = false; +} +#endif + + + + + +bool LightColorEntry(char *buffer, uint32_t buffer_length) +{ + char scolor[10]; + char *p; + char *str; + uint32_t entry_type = 0; + uint8_t value = Light.fixed_color_index; +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count) value = Light.wheel; +#endif + + if (buffer[0] == '#') { + buffer++; + buffer_length--; + } + + if (Light.subtype >= LST_RGB) { + char option = (1 == buffer_length) ? buffer[0] : '\0'; + if ('+' == option) { +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count || Light.fixed_color_index < MAX_FIXED_COLOR) { +#else + if (Light.fixed_color_index < MAX_FIXED_COLOR) { +#endif + value++; + } + } + else if ('-' == option) { +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count || Light.fixed_color_index > 1) { +#else + if (Light.fixed_color_index > 1) { +#endif + value--; + } + } else { + value = atoi(buffer); +#ifdef USE_LIGHT_PALETTE + value--; +#endif + } +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count) value = value % Light.palette_count; +#endif + } + + memset(&Light.entry_color, 0x00, sizeof(Light.entry_color)); + + while ((buffer_length > 0) && ('=' == buffer[buffer_length - 1])) { + buffer_length--; + memcpy(&Light.entry_color, &Light.current_color, sizeof(Light.entry_color)); + } + if (strstr(buffer, ",") != nullptr) { + int8_t i = 0; + for (str = strtok_r(buffer, ",", &p); str && i < 6; str = strtok_r(nullptr, ",", &p)) { + if (i < LST_MAX) { + Light.entry_color[i++] = atoi(str); + } + } + entry_type = 2; + } + else if (((2 * Light.subtype) == buffer_length) || (buffer_length > 3)) { + for (uint32_t i = 0; i < tmin((uint)(buffer_length / 2), sizeof(Light.entry_color)); i++) { + strlcpy(scolor, buffer + (i *2), 3); + Light.entry_color[i] = (uint8_t)strtol(scolor, &p, 16); + } + entry_type = 1; + } +#ifdef USE_LIGHT_PALETTE + else if (Light.palette_count) { + Light.wheel = value; + memcpy_P(&Light.entry_color, &Light.palette[value * LST_MAX], LST_MAX); + entry_type = 1; + } +#endif + else if ((Light.subtype >= LST_RGB) && (value > 0) && (value <= MAX_FIXED_COLOR)) { + Light.fixed_color_index = value; + memcpy_P(&Light.entry_color, &kFixedColor[value -1], 3); + entry_type = 1; + } + else if ((value > 199) && (value <= 199 + MAX_FIXED_COLD_WARM)) { + if (LST_RGBW == Light.subtype) { + memcpy_P(&Light.entry_color[3], &kFixedWhite[value -200], 1); + entry_type = 1; + } + else if (LST_COLDWARM == Light.subtype) { + memcpy_P(&Light.entry_color, &kFixedColdWarm[value -200], 2); + entry_type = 1; + } + else if (LST_RGBCW == Light.subtype) { + memcpy_P(&Light.entry_color[3], &kFixedColdWarm[value -200], 2); + entry_type = 1; + } + } + if (entry_type) { + Settings.flag.decimal_text = entry_type -1; + } + return (entry_type); +} + + + +void CmndSupportColor(void) +{ + bool valid_entry = false; + bool coldim = false; + + if (XdrvMailbox.data_len > 0) { + valid_entry = LightColorEntry(XdrvMailbox.data, XdrvMailbox.data_len); + if (valid_entry) { + if (XdrvMailbox.index <= 2) { +#ifdef USE_LIGHT_PALETTE + if (Light.palette_count && XdrvMailbox.index == 2) { + LightSetPaletteEntry(); + } + else { +#endif + uint32_t old_bri = light_state.getBri(); + + light_controller.changeChannels(Light.entry_color); + if (2 == XdrvMailbox.index) { + + light_controller.changeBri(old_bri); + } +#ifdef USE_LIGHT_PALETTE + } +#endif + Settings.light_scheme = 0; +#ifdef USE_DEVICE_GROUPS + LightUpdateScheme(); +#endif + coldim = true; + } else { + for (uint32_t i = 0; i < LST_RGB; i++) { + Settings.ws_color[XdrvMailbox.index -3][i] = Light.entry_color[i]; + } + } + } + } + char scolor[LIGHT_COLOR_SIZE]; + if (!valid_entry && (XdrvMailbox.index <= 2)) { + ResponseCmndChar(LightGetColor(scolor)); + } + if (XdrvMailbox.index >= 3) { + scolor[0] = '\0'; + for (uint32_t i = 0; i < LST_RGB; i++) { + if (Settings.flag.decimal_text) { + snprintf_P(scolor, sizeof(scolor), PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Settings.ws_color[XdrvMailbox.index -3][i]); + } else { + snprintf_P(scolor, sizeof(scolor), PSTR("%s%02X"), scolor, Settings.ws_color[XdrvMailbox.index -3][i]); + } + } + ResponseCmndIdxChar(scolor); + } + if (coldim) { + LightPreparePower(); + } +} + +void CmndColor(void) +{ + + + + + + + + if ((Light.subtype > LST_SINGLE) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 6)) { + CmndSupportColor(); + } +} + +void CmndWhite(void) +{ + + + if (Light.pwm_multi_channels) { return; } + if ( ((Light.subtype >= LST_RGBW) || (LST_COLDWARM == Light.subtype)) && (XdrvMailbox.index == 1)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + light_controller.changeDimmer(XdrvMailbox.payload, 2); + LightPreparePower(2); + } else { + ResponseCmndNumber(light_state.getDimmer(2)); + } + } +} + +void CmndChannel(void) +{ + + + + + if ((XdrvMailbox.index >= Light.device) && (XdrvMailbox.index < Light.device + Light.subtype )) { + uint32_t light_index = XdrvMailbox.index - Light.device; + power_t coldim = 0; + + + if (1 == XdrvMailbox.data_len) { + uint8_t channel = changeUIntScale(Light.current_color[light_index],0,255,0,100); + if ('+' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (channel > 89) ? 100 : channel + 10; + } else if ('-' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (channel < 11) ? 1 : channel - 10; + } + } + + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + Light.current_color[light_index] = changeUIntScale(XdrvMailbox.payload,0,100,0,255); + if (Light.pwm_multi_channels) { + coldim = 1 << light_index; + } else { + if (light_controller.isCTRGBLinked()) { + + if ((light_index < 3) && (light_controller.isCTRGBLinked())) { + Light.current_color[3] = Light.current_color[4] = 0; + } else { + Light.current_color[0] = Light.current_color[1] = Light.current_color[2] = 0; + } + coldim = 1; + } else { + if (light_index < 3) { coldim = 1; } + else { coldim = 2; } + } + } + light_controller.changeChannels(Light.current_color); + } + ResponseCmndIdxNumber(changeUIntScale(Light.current_color[light_index],0,255,0,100)); + if (coldim) { + LightPreparePower(coldim); + } + } +} + +void CmndHsbColor(void) +{ + + + + + + + + if (Light.subtype >= LST_RGB) { + if (XdrvMailbox.data_len > 0) { + uint16_t c_hue; + uint8_t c_sat; + light_state.getHSB(&c_hue, &c_sat, nullptr); + uint32_t HSB[3]; + HSB[0] = c_hue; + HSB[1] = c_sat; + HSB[2] = light_state.getBriRGB(); + if ((2 == XdrvMailbox.index) || (3 == XdrvMailbox.index)) { + if ((uint32_t)XdrvMailbox.payload > 100) { XdrvMailbox.payload = 100; } + HSB[XdrvMailbox.index-1] = changeUIntScale(XdrvMailbox.payload, 0, 100, 0, 255); + } else { + uint32_t paramcount = ParseParameters(3, HSB); + if (HSB[0] > 360) { HSB[0] = 360; } + for (uint32_t i = 1; i < paramcount; i++) { + if (HSB[i] > 100) { HSB[i] == 100; } + HSB[i] = changeUIntScale(HSB[i], 0, 100, 0, 255); + } + } + light_controller.changeHSB(HSB[0], HSB[1], HSB[2]); + LightPreparePower(1); + } else { + LightState(0); + } + } +} + +void CmndScheme(void) +{ + + + + + + if (Light.subtype >= LST_RGB) { + uint32_t max_scheme = Light.max_scheme; + + if (1 == XdrvMailbox.data_len) { + if (('+' == XdrvMailbox.data[0]) && (Settings.light_scheme < max_scheme)) { + XdrvMailbox.payload = Settings.light_scheme + ((0 == Settings.light_scheme) ? 2 : 1); + } + else if (('-' == XdrvMailbox.data[0]) && (Settings.light_scheme > 0)) { + XdrvMailbox.payload = Settings.light_scheme - ((2 == Settings.light_scheme) ? 2 : 1); + } + } + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= max_scheme)) { + uint32_t parm[2]; + if (ParseParameters(2, parm) > 1) { + Light.wheel = parm[1]; +#ifdef USE_LIGHT_PALETTE + Light.wheel--; +#endif + } + Settings.light_scheme = XdrvMailbox.payload; +#ifdef USE_DEVICE_GROUPS + LightUpdateScheme(); +#endif + if (LS_WAKEUP == Settings.light_scheme) { + Light.wakeup_active = 3; + } + LightPowerOn(); + Light.strip_timer_counter = 0; + + if (Settings.flag3.hass_tele_on_power) { + MqttPublishTeleState(); + } + } + ResponseCmndNumber(Settings.light_scheme); + } +} + +void CmndWakeup(void) +{ + + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + light_controller.changeDimmer(XdrvMailbox.payload); + } + Light.wakeup_active = 3; + Settings.light_scheme = LS_WAKEUP; +#ifdef USE_DEVICE_GROUPS + LightUpdateScheme(); +#endif + LightPowerOn(); + ResponseCmndChar(D_JSON_STARTED); +} + +void CmndColorTemperature(void) +{ + + + + + if (Light.pwm_multi_channels) { return; } + if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { + uint32_t ct = light_state.getCT(); + if (1 == XdrvMailbox.data_len) { + if ('+' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (ct > (CT_MAX-34)) ? CT_MAX : ct + 34; + } + else if ('-' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (ct < (CT_MIN+34)) ? CT_MIN : ct - 34; + } + } + if ((XdrvMailbox.payload >= CT_MIN) && (XdrvMailbox.payload <= CT_MAX)) { + light_controller.changeCTB(XdrvMailbox.payload, light_state.getBriCT()); + LightPreparePower(2); + } else { + ResponseCmndNumber(ct); + } + } +} + +void CmndDimmer(void) +{ + + + + + + + + uint32_t dimmer; + if (XdrvMailbox.index == 3) { + skip_light_fade = true; + XdrvMailbox.index = 0; + } + else if (XdrvMailbox.index > 2) { + XdrvMailbox.index = 1; + } + + if ((light_controller.isCTRGBLinked()) || (0 == XdrvMailbox.index)) { + dimmer = light_state.getDimmer(); + } else { + dimmer = light_state.getDimmer(XdrvMailbox.index); + } + + if (1 == XdrvMailbox.data_len) { + if ('+' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (dimmer > 89) ? 100 : dimmer + 10; + } else if ('-' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (dimmer < 11) ? 1 : dimmer - 10; + } + } + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + if (light_controller.isCTRGBLinked()) { + + light_controller.changeDimmer(XdrvMailbox.payload); + LightPreparePower(); + } else { + if (0 != XdrvMailbox.index) { + light_controller.changeDimmer(XdrvMailbox.payload, XdrvMailbox.index); + LightPreparePower(1 << (XdrvMailbox.index - 1)); + } else { + + light_controller.changeDimmer(XdrvMailbox.payload, 1); + light_controller.changeDimmer(XdrvMailbox.payload, 2); + LightPreparePower(); + } + } +#if defined(USE_PWM_DIMMER) && defined(USE_DEVICE_GROUPS) + Settings.bri_power_on = light_state.getBri();; + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_BRI_POWER_ON, Settings.bri_power_on); +#endif + Light.update = true; + if (skip_light_fade) LightAnimate(); + } else { + ResponseCmndNumber(dimmer); + } + skip_light_fade = false; +} + +void CmndDimmerRange(void) +{ + + + if (XdrvMailbox.data_len > 0) { + uint32_t parm[2]; + parm[0] = Settings.dimmer_hw_min; + parm[1] = Settings.dimmer_hw_max; + ParseParameters(2, parm); + if (parm[0] < parm[1]) { + Settings.dimmer_hw_min = parm[0]; + Settings.dimmer_hw_max = parm[1]; + } else { + Settings.dimmer_hw_min = parm[1]; + Settings.dimmer_hw_max = parm[0]; + } + LightCalcPWMRange(); + Light.update = true; + } + Response_P(PSTR("{\"" D_CMND_DIMMER_RANGE "\":{\"Min\":%d,\"Max\":%d}}"), Settings.dimmer_hw_min, Settings.dimmer_hw_max); +} + +void CmndLedTable(void) +{ + + + + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { + switch (XdrvMailbox.payload) { + case 0: + case 1: + Settings.light_correction = XdrvMailbox.payload; + break; + case 2: + Settings.light_correction ^= 1; + break; + } + LightCalcPWMRange(); + Light.update = true; + } + ResponseCmndStateText(Settings.light_correction); +} + +void CmndRgbwwTable(void) +{ + + + if ((XdrvMailbox.data_len > 0)) { + uint32_t parm[LST_RGBCW -1]; + uint32_t parmcount = ParseParameters(LST_RGBCW, parm); + for (uint32_t i = 0; i < parmcount; i++) { + Settings.rgbwwTable[i] = parm[i]; + } + Light.update = true; + } + char scolor[LIGHT_COLOR_SIZE]; + scolor[0] = '\0'; + for (uint32_t i = 0; i < LST_RGBCW; i++) { + snprintf_P(scolor, sizeof(scolor), PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Settings.rgbwwTable[i]); + } + ResponseCmndChar(scolor); +} + +void CmndFade(void) +{ + + + + + switch (XdrvMailbox.payload) { + case 0: + case 1: + Settings.light_fade = XdrvMailbox.payload; + break; + case 2: + Settings.light_fade ^= 1; + break; + } +#ifdef USE_DEVICE_GROUPS + if (XdrvMailbox.payload >= 0 && XdrvMailbox.payload <= 2) SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_FADE, Settings.light_fade); +#endif +#ifdef USE_LIGHT + if (!Settings.light_fade) { Light.fade_running = false; } +#endif + ResponseCmndStateText(Settings.light_fade); +} + +void CmndSpeed(void) +{ + + + + + if (1 == XdrvMailbox.data_len) { + if (('+' == XdrvMailbox.data[0]) && (Settings.light_speed > 1)) { + XdrvMailbox.payload = Settings.light_speed - 1; + } + else if (('-' == XdrvMailbox.data[0]) && (Settings.light_speed < 40)) { + XdrvMailbox.payload = Settings.light_speed + 1; + } + } + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 40)) { + Settings.light_speed = XdrvMailbox.payload; +#ifdef USE_DEVICE_GROUPS + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_SPEED, Settings.light_speed); +#endif + } + ResponseCmndNumber(Settings.light_speed); +} + +void CmndWakeupDuration(void) +{ + + + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 3001)) { + Settings.light_wakeup = XdrvMailbox.payload; + Light.wakeup_active = 0; + } + ResponseCmndNumber(Settings.light_wakeup); +} + +#ifdef USE_LIGHT_PALETTE +void CmndPalette(void) +{ + uint8_t * palette_entry; + char * p; + + + if (XdrvMailbox.data_len) { + Light.wheel = 0; + Light.palette_count = 0; + if (Light.palette) { + free(Light.palette); + Light.palette = nullptr; + } + if (XdrvMailbox.data_len > 1 || XdrvMailbox.data[0] != '0') { + uint8_t palette_count = 0; + char * color = XdrvMailbox.data; + if (!(Light.palette = (uint8_t *)malloc(255 * Light.subtype))) return; + palette_entry = Light.palette; + for (;;) { + p = strchr(color, ' '); + if (p) *p = 0; + color = Trim(color); + if (*color && LightColorEntry(color, strlen(color))) { + memcpy(palette_entry, Light.entry_color, Light.subtype); + palette_entry += Light.subtype; + palette_count++; + } + if (!p) break; + color = p + 1; + } + if (!(Light.palette = (uint8_t *)realloc(Light.palette, palette_count * Light.subtype))) return; + Light.palette_count = palette_count; + } + } + + char palette_str[5 * Light.subtype * Light.palette_count + 3]; + p = palette_str; + *p++ = '['; + if (Light.palette_count) { + palette_entry = Light.palette; + for (int entry = 0; entry < Light.palette_count; entry++) { + if (Settings.flag.decimal_text) { + *p++ = '"'; + } + memcpy(Light.current_color, palette_entry, Light.subtype); + LightGetColor(p); + p += strlen(p); + if (Settings.flag.decimal_text) { + *p++ = '"'; + } + *p++ = ','; + } + p--; + } + *p++ = ']'; + *p = 0; + ResponseCmndChar(palette_str); +} +#endif + +void CmndUndocA(void) +{ + + char scolor[LIGHT_COLOR_SIZE]; + LightGetColor(scolor, true); + scolor[6] = '\0'; + Response_P(PSTR("%s,%d,%d,%d,%d,%d"), scolor, Settings.light_fade, Settings.light_correction, Settings.light_scheme, Settings.light_speed, Settings.light_width); + MqttPublishPrefixTopic_P(STAT, XdrvMailbox.topic); + mqtt_data[0] = '\0'; +} + + + + + +bool Xdrv04(uint8_t function) +{ + bool result = false; + + if (FUNC_MODULE_INIT == function) { + return LightModuleInit(); + } + else if (light_type) { + switch (function) { + case FUNC_SERIAL: + result = XlgtCall(FUNC_SERIAL); + break; + case FUNC_LOOP: + if (Light.fade_running) { + if (LightApplyFade()) { + LightSetOutputs(Light.fade_cur_10); + } + } + break; + case FUNC_EVERY_50_MSECOND: + LightAnimate(); + break; +#ifdef USE_DEVICE_GROUPS + case FUNC_DEVICE_GROUP_ITEM: + LightHandleDevGroupItem(); + break; +#endif + case FUNC_SET_POWER: + LightSetPower(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kLightCommands, LightCommand); + if (!result) { + result = XlgtCall(FUNC_COMMAND); + } + break; + case FUNC_PRE_INIT: + LightInit(); + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_05_irremote.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_05_irremote.ino" +#if defined(USE_IR_REMOTE) && !defined(USE_IR_REMOTE_FULL) + + + + +#define XDRV_05 5 + +#include + +enum IrErrors { IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND }; + +const char kIrRemoteCommands[] PROGMEM = "|" D_CMND_IRSEND ; + + +void (* const IrRemoteCommand[])(void) PROGMEM = { + &CmndIrSend }; + + +static const uint8_t MAX_STANDARD_IR = NEC; +const char kIrRemoteProtocols[] PROGMEM = "UNKNOWN|RC5|RC6|NEC"; + + + + + +#include + +IRsend *irsend = nullptr; +bool irsend_active = false; + +void IrSendInit(void) +{ + irsend = new IRsend(pin[GPIO_IRSEND]); + irsend->begin(); +} + +#ifdef USE_IR_RECEIVE + + + + +const bool IR_RCV_SAVE_BUFFER = false; +const uint32_t IR_TIME_AVOID_DUPLICATE = 500; + +#include + +IRrecv *irrecv = nullptr; + +unsigned long ir_lasttime = 0; + +void IrReceiveUpdateThreshold(void) +{ + if (irrecv != nullptr) { + if (Settings.param[P_IR_UNKNOW_THRESHOLD] < 6) { Settings.param[P_IR_UNKNOW_THRESHOLD] = 6; } + irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); + } +} + +void IrReceiveInit(void) +{ + + irrecv = new IRrecv(pin[GPIO_IRRECV], IR_RCV_BUFFER_SIZE, IR_RCV_TIMEOUT, IR_RCV_SAVE_BUFFER); + irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); + irrecv->enableIRIn(); + + +} + +void IrReceiveCheck(void) +{ + char sirtype[8]; + int8_t iridx = 0; + + decode_results results; + + if (irrecv->decode(&results)) { + char hvalue[65]; + + iridx = results.decode_type; + if ((iridx < 0) || (iridx > MAX_STANDARD_IR)) { iridx = 0; } + + if (iridx) { + if (results.bits > 64) { + + uint32_t digits2 = results.bits / 8; + if (results.bits % 8) { digits2++; } + ToHex_P((unsigned char*)results.state, digits2, hvalue, sizeof(hvalue)); + } else { + Uint64toHex(results.value, hvalue, results.bits); + } + } else { + Uint64toHex(results.value, hvalue, 32); + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_IRR "Echo %d, RawLen %d, Overflow %d, Bits %d, Value 0x%s, Decode %d"), + irsend_active, results.rawlen, results.overflow, results.bits, hvalue, results.decode_type); + + unsigned long now = millis(); + + if (!irsend_active && (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE)) { + ir_lasttime = now; + + char svalue[64]; + if (Settings.flag.ir_receive_decimal) { + ulltoa(results.value, svalue, 10); + } else { + snprintf_P(svalue, sizeof(svalue), PSTR("\"0x%s\""), hvalue); + } + ResponseTime_P(PSTR(",\"" D_JSON_IRRECEIVED "\":{\"" D_JSON_IR_PROTOCOL "\":\"%s\",\"" D_JSON_IR_BITS "\":%d"), + GetTextIndexed(sirtype, sizeof(sirtype), iridx, kIrRemoteProtocols), results.bits); + if (iridx) { + ResponseAppend_P(PSTR(",\"" D_JSON_IR_DATA "\":%s"), svalue); + } else { + ResponseAppend_P(PSTR(",\"" D_JSON_IR_HASH "\":%s"), svalue); + } + + if (Settings.flag3.receive_raw) { + ResponseAppend_P(PSTR(",\"" D_JSON_IR_RAWDATA "\":[")); + uint16_t i; + for (i = 1; i < results.rawlen; i++) { + if (i > 1) { ResponseAppend_P(PSTR(",")); } + uint32_t usecs; + for (usecs = results.rawbuf[i] * kRawTick; usecs > UINT16_MAX; usecs -= UINT16_MAX) { + ResponseAppend_P(PSTR("%d,0,"), UINT16_MAX); + } + ResponseAppend_P(PSTR("%d"), usecs); + if (strlen(mqtt_data) > sizeof(mqtt_data) - 40) { break; } + } + uint16_t extended_length = results.rawlen - 1; + for (uint32_t j = 0; j < results.rawlen - 1; j++) { + uint32_t usecs = results.rawbuf[j] * kRawTick; + + extended_length += (usecs / (UINT16_MAX + 1)) * 2; + } + ResponseAppend_P(PSTR("],\"" D_JSON_IR_RAWDATA "Info\":[%d,%d,%d]"), extended_length, i -1, results.overflow); + } + + ResponseJsonEndEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED)); + + XdrvRulesProcess(); +#ifdef USE_DOMOTICZ + if (iridx) { + unsigned long value = results.value | (iridx << 28); + DomoticzSensor(DZ_COUNT, value); + } +#endif + } + + irrecv->resume(); + } +} +#endif + + + + + +uint32_t IrRemoteCmndIrSendJson(void) +{ + + + + + char dataBufUc[XdrvMailbox.data_len + 1]; + UpperCase(dataBufUc, XdrvMailbox.data); + RemoveSpace(dataBufUc); + if (strlen(dataBufUc) < 8) { + return IE_INVALID_JSON; + } + + StaticJsonBuffer<140> jsonBuf; + JsonObject &root = jsonBuf.parseObject(dataBufUc); + if (!root.success()) { + return IE_INVALID_JSON; + } + + + + char parm_uc[10]; + const char *protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_PROTOCOL))]; + uint16_t bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS))]; + uint64_t data = strtoull(root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA))], nullptr, 0); + uint16_t repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT))]; + + if (XdrvMailbox.index > repeat + 1) { + repeat = XdrvMailbox.index - 1; + } + if (!(protocol && bits)) { + return IE_SYNTAX_IRSEND; + } + + char protocol_text[20]; + int protocol_code = GetCommandCode(protocol_text, sizeof(protocol_text), protocol, kIrRemoteProtocols); + + char dvalue[64]; + char hvalue[20]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol_text %s, protocol %s, bits %d, data %s (0x%s), repeat %d, protocol_code %d"), + protocol_text, protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat, protocol_code); + + irsend_active = true; + switch (protocol_code) { +#ifdef USE_IR_SEND_RC5 + case RC5: + irsend->sendRC5(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_RC6 + case RC6: + irsend->sendRC6(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_NEC + case NEC: + irsend->sendNEC(data, (bits > NEC_BITS) ? NEC_BITS : bits, repeat); break; +#endif + default: + irsend_active = false; + ResponseCmndChar(D_JSON_PROTOCOL_NOT_SUPPORTED); + } + + return IE_NO_ERROR; +} + +void CmndIrSend(void) +{ + uint8_t error = IE_SYNTAX_IRSEND; + + if (XdrvMailbox.data_len) { + + if (strstr(XdrvMailbox.data, "{") == nullptr) { + error = IE_INVALID_JSON; + } else { + error = IrRemoteCmndIrSendJson(); + } + } + IrRemoteCmndResponse(error); +} + +void IrRemoteCmndResponse(uint32_t error) +{ + switch (error) { + case IE_INVALID_RAWDATA: + ResponseCmndChar_P(PSTR(D_JSON_INVALID_RAWDATA)); + break; + case IE_INVALID_JSON: + ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); + break; + case IE_SYNTAX_IRSEND: + Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_NO " " D_JSON_IR_PROTOCOL ", " D_JSON_IR_BITS " " D_JSON_OR " " D_JSON_IR_DATA "\"}")); + break; + default: + ResponseCmndDone(); + } +} + + + + + +bool Xdrv05(uint8_t function) +{ + bool result = false; + + if ((pin[GPIO_IRSEND] < 99) || (pin[GPIO_IRRECV] < 99)) { + switch (function) { + case FUNC_PRE_INIT: + if (pin[GPIO_IRSEND] < 99) { + IrSendInit(); + } +#ifdef USE_IR_RECEIVE + if (pin[GPIO_IRRECV] < 99) { + IrReceiveInit(); + } +#endif + break; + case FUNC_EVERY_50_MSECOND: +#ifdef USE_IR_RECEIVE + if (pin[GPIO_IRRECV] < 99) { + IrReceiveCheck(); + } +#endif + irsend_active = false; + break; + case FUNC_COMMAND: + if (pin[GPIO_IRSEND] < 99) { + result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand); + } + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_05_irremote_full.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_05_irremote_full.ino" +#ifdef USE_IR_REMOTE_FULL + + + + +#define XDRV_05 5 + +#include +#include +#include +#include +#include + +enum IrErrors { IE_RESPONSE_PROVIDED, IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND, IE_SYNTAX_IRHVAC, + IE_UNSUPPORTED_HVAC, IE_UNSUPPORTED_PROTOCOL }; + +const char kIrRemoteCommands[] PROGMEM = "|" + D_CMND_IRHVAC "|" D_CMND_IRSEND ; + +void (* const IrRemoteCommand[])(void) PROGMEM = { + &CmndIrHvac, &CmndIrSend }; + + + + + +IRsend *irsend = nullptr; +bool irsend_active = false; + +void IrSendInit(void) +{ + irsend = new IRsend(pin[GPIO_IRSEND]); + irsend->begin(); +} + + + +uint8_t reverseBitsInByte(uint8_t b) { + b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; + b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + return b; +} + + +uint64_t reverseBitsInBytes64(uint64_t b) { + union { + uint8_t b[8]; + uint64_t i; + } a; + a.i = b; + for (uint32_t i=0; i<8; i++) { + a.b[i] = reverseBitsInByte(a.b[i]); + } + return a.i; +} + + + + + +const bool IR_FULL_RCV_SAVE_BUFFER = false; +const uint32_t IR_TIME_AVOID_DUPLICATE = 500; + + + + +const uint16_t IR_FULL_BUFFER_SIZE = 1024; + + + +const uint8_t IR__FULL_RCV_TIMEOUT = 50; + +IRrecv *irrecv = nullptr; + +unsigned long ir_lasttime = 0; + +void IrReceiveUpdateThreshold(void) +{ + if (irrecv != nullptr) { + if (Settings.param[P_IR_UNKNOW_THRESHOLD] < 6) { Settings.param[P_IR_UNKNOW_THRESHOLD] = 6; } + irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); + } +} + +void IrReceiveInit(void) +{ + + irrecv = new IRrecv(pin[GPIO_IRRECV], IR_FULL_BUFFER_SIZE, IR__FULL_RCV_TIMEOUT, IR_FULL_RCV_SAVE_BUFFER); + irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); + irrecv->enableIRIn(); +} + +String sendACJsonState(const stdAc::state_t &state) { + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.createObject(); + json[D_JSON_IRHVAC_VENDOR] = typeToString(state.protocol); + json[D_JSON_IRHVAC_MODEL] = state.model; + json[D_JSON_IRHVAC_POWER] = IRac::boolToString(state.power); + json[D_JSON_IRHVAC_MODE] = IRac::opmodeToString(state.mode); + + if (state.mode == stdAc::opmode_t::kOff || !state.power) { + json[D_JSON_IRHVAC_MODE] = IRac::opmodeToString(stdAc::opmode_t::kOff); + json[D_JSON_IRHVAC_POWER] = IRac::boolToString(false); + } + json[D_JSON_IRHVAC_CELSIUS] = IRac::boolToString(state.celsius); + if (floorf(state.degrees) == state.degrees) { + json[D_JSON_IRHVAC_TEMP] = floorf(state.degrees); + } else { + json[D_JSON_IRHVAC_TEMP] = RawJson(String(state.degrees, 1)); + } + json[D_JSON_IRHVAC_FANSPEED] = IRac::fanspeedToString(state.fanspeed); + json[D_JSON_IRHVAC_SWINGV] = IRac::swingvToString(state.swingv); + json[D_JSON_IRHVAC_SWINGH] = IRac::swinghToString(state.swingh); + json[D_JSON_IRHVAC_QUIET] = IRac::boolToString(state.quiet); + json[D_JSON_IRHVAC_TURBO] = IRac::boolToString(state.turbo); + json[D_JSON_IRHVAC_ECONO] = IRac::boolToString(state.econo); + json[D_JSON_IRHVAC_LIGHT] = IRac::boolToString(state.light); + json[D_JSON_IRHVAC_FILTER] = IRac::boolToString(state.filter); + json[D_JSON_IRHVAC_CLEAN] = IRac::boolToString(state.clean); + json[D_JSON_IRHVAC_BEEP] = IRac::boolToString(state.beep); + json[D_JSON_IRHVAC_SLEEP] = state.sleep; + + String payload = ""; + payload.reserve(200); + json.printTo(payload); + return payload; +} + +String sendIRJsonState(const struct decode_results &results) { + String json("{"); + json += "\"" D_JSON_IR_PROTOCOL "\":\""; + json += typeToString(results.decode_type); + json += "\",\"" D_JSON_IR_BITS "\":"; + json += results.bits; + + if (hasACState(results.decode_type)) { + json += ",\"" D_JSON_IR_DATA "\":\"0x"; + json += resultToHexidecimal(&results); + json += "\""; + } else { + if (UNKNOWN != results.decode_type) { + json += ",\"" D_JSON_IR_DATA "\":"; + } else { + json += ",\"" D_JSON_IR_HASH "\":"; + } + if (Settings.flag.ir_receive_decimal) { + char svalue[32]; + ulltoa(results.value, svalue, 10); + json += svalue; + } else { + char hvalue[64]; + if (UNKNOWN != results.decode_type) { + Uint64toHex(results.value, hvalue, results.bits); + json += "\"0x"; + json += hvalue; + json += "\",\"" D_JSON_IR_DATALSB "\":\"0x"; + Uint64toHex(reverseBitsInBytes64(results.value), hvalue, results.bits); + json += hvalue; + json += "\""; + } else { + Uint64toHex(results.value, hvalue, 32); + json += "\"0x"; + json += hvalue; + json += "\""; + } + } + } + json += ",\"" D_JSON_IR_REPEAT "\":"; + json += results.repeat; + + stdAc::state_t ac_result; + if (IRAcUtils::decodeToState(&results, &ac_result, nullptr)) { + + json += ",\"" D_CMND_IRHVAC "\":"; + json += sendACJsonState(ac_result); + } + + return json; +} + +void IrReceiveCheck(void) +{ + decode_results results; + + if (irrecv->decode(&results)) { + uint32_t now = millis(); + + + if (!irsend_active && (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE)) { + ir_lasttime = now; + Response_P(PSTR("{\"" D_JSON_IRRECEIVED "\":%s"), sendIRJsonState(results).c_str()); + + if (Settings.flag3.receive_raw) { + ResponseAppend_P(PSTR(",\"" D_JSON_IR_RAWDATA "\":[")); + uint16_t i; + for (i = 1; i < results.rawlen; i++) { + if (i > 1) { ResponseAppend_P(PSTR(",")); } + uint32_t usecs; + for (usecs = results.rawbuf[i] * kRawTick; usecs > UINT16_MAX; usecs -= UINT16_MAX) { + ResponseAppend_P(PSTR("%d,0,"), UINT16_MAX); + } + ResponseAppend_P(PSTR("%d"), usecs); + if (strlen(mqtt_data) > sizeof(mqtt_data) - 40) { break; } + } + uint16_t extended_length = results.rawlen - 1; + for (uint32_t j = 0; j < results.rawlen - 1; j++) { + uint32_t usecs = results.rawbuf[j] * kRawTick; + + extended_length += (usecs / (UINT16_MAX + 1)) * 2; + } + ResponseAppend_P(PSTR("],\"" D_JSON_IR_RAWDATA "Info\":[%d,%d,%d]"), extended_length, i -1, results.overflow); + } + + ResponseJsonEndEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED)); + + XdrvRulesProcess(); + } + + irrecv->resume(); + } +} + + + + + + + +String listSupportedProtocols(bool hvac) { + String l(""); + bool first = true; + for (uint32_t i = UNUSED + 1; i <= kLastDecodeType; i++) { + bool found = false; + if (hvac) { + found = IRac::isProtocolSupported((decode_type_t)i); + } else { + found = (IRsend::defaultBits((decode_type_t)i) > 0) && (!IRac::isProtocolSupported((decode_type_t)i)); + } + if (found) { + if (first) { + first = false; + } else { + l += "|"; + } + l += typeToString((decode_type_t)i); + } + } + return l; +} + + +const stdAc::fanspeed_t IrHvacFanSpeed[] PROGMEM = { stdAc::fanspeed_t::kAuto, + stdAc::fanspeed_t::kMin, stdAc::fanspeed_t::kLow,stdAc::fanspeed_t::kMedium, + stdAc::fanspeed_t::kHigh, stdAc::fanspeed_t::kMax }; + +uint32_t IrRemoteCmndIrHvacJson(void) +{ + stdAc::state_t state, prev; + char parm_uc[12]; + + + char dataBufUc[XdrvMailbox.data_len + 1]; + UpperCase(dataBufUc, XdrvMailbox.data); + RemoveSpace(dataBufUc); + if (strlen(dataBufUc) < 8) { return IE_INVALID_JSON; } + + DynamicJsonBuffer jsonBuf; + JsonObject &json = jsonBuf.parseObject(dataBufUc); + if (!json.success()) { return IE_INVALID_JSON; } + + + state.protocol = decode_type_t::UNKNOWN; + state.model = 1; + state.mode = stdAc::opmode_t::kAuto; + state.power = false; + state.celsius = true; + state.degrees = 21.0f; + state.fanspeed = stdAc::fanspeed_t::kMedium; + state.swingv = stdAc::swingv_t::kOff; + state.swingh = stdAc::swingh_t::kOff; + state.light = false; + state.beep = false; + state.econo = false; + state.filter = false; + state.turbo = false; + state.quiet = false; + state.sleep = -1; + state.clean = false; + state.clock = -1; + + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_VENDOR)); + if (json.containsKey(parm_uc)) { state.protocol = strToDecodeType(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_PROTOCOL)); + if (json.containsKey(parm_uc)) { state.protocol = strToDecodeType(json[parm_uc]); } + if (decode_type_t::UNKNOWN == state.protocol) { return IE_UNSUPPORTED_HVAC; } + if (!IRac::isProtocolSupported(state.protocol)) { return IE_UNSUPPORTED_HVAC; } + + + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_FANSPEED)); + if (json.containsKey(parm_uc)) { + uint32_t fan_speed = json[parm_uc]; + if ((fan_speed >= 1) && (fan_speed <= 5)) { + state.fanspeed = (stdAc::fanspeed_t) pgm_read_byte(&IrHvacFanSpeed[fan_speed]); + } else { + state.fanspeed = IRac::strToFanspeed(json[parm_uc]); + } + } + + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_MODEL)); + if (json.containsKey(parm_uc)) { state.model = IRac::strToModel(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_MODE)); + if (json.containsKey(parm_uc)) { state.mode = IRac::strToOpmode(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_SWINGV)); + if (json.containsKey(parm_uc)) { state.swingv = IRac::strToSwingV(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_SWINGH)); + if (json.containsKey(parm_uc)) { state.swingh = IRac::strToSwingH(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_TEMP)); + if (json.containsKey(parm_uc)) { state.degrees = json[parm_uc]; } + + + + + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_POWER)); + if (json.containsKey(parm_uc)) { state.power = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_CELSIUS)); + if (json.containsKey(parm_uc)) { state.celsius = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_LIGHT)); + if (json.containsKey(parm_uc)) { state.light = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_BEEP)); + if (json.containsKey(parm_uc)) { state.beep = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_ECONO)); + if (json.containsKey(parm_uc)) { state.econo = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_FILTER)); + if (json.containsKey(parm_uc)) { state.filter = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_TURBO)); + if (json.containsKey(parm_uc)) { state.turbo = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_QUIET)); + if (json.containsKey(parm_uc)) { state.quiet = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_CLEAN)); + if (json.containsKey(parm_uc)) { state.clean = IRac::strToBool(json[parm_uc]); } + + + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_SLEEP)); + if (json[parm_uc]) { state.sleep = json[parm_uc]; } + + + IRac ac(pin[GPIO_IRSEND]); + bool success = ac.sendAc(state, &prev); + if (!success) { return IE_SYNTAX_IRHVAC; } + + Response_P(PSTR("{\"" D_CMND_IRHVAC "\":%s}"), sendACJsonState(state).c_str()); + return IE_RESPONSE_PROVIDED; +} + +void CmndIrHvac(void) +{ + uint8_t error = IE_SYNTAX_IRHVAC; + + if (XdrvMailbox.data_len) { + error = IrRemoteCmndIrHvacJson(); + } + if (error != IE_RESPONSE_PROVIDED) { IrRemoteCmndResponse(error); } +} + + + + + +uint32_t IrRemoteCmndIrSendJson(void) +{ + char parm_uc[12]; + + + + char dataBufUc[XdrvMailbox.data_len + 1]; + UpperCase(dataBufUc, XdrvMailbox.data); + RemoveSpace(dataBufUc); + if (strlen(dataBufUc) < 8) { return IE_INVALID_JSON; } + + DynamicJsonBuffer jsonBuf; + JsonObject &json = jsonBuf.parseObject(dataBufUc); + if (!json.success()) { return IE_INVALID_JSON; } + + + + decode_type_t protocol = decode_type_t::UNKNOWN; + uint16_t bits = 0; + uint64_t data; + uint8_t repeat = 0; + + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_VENDOR)); + if (json.containsKey(parm_uc)) { protocol = strToDecodeType(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_PROTOCOL)); + if (json.containsKey(parm_uc)) { protocol = strToDecodeType(json[parm_uc]); } + if (decode_type_t::UNKNOWN == protocol) { return IE_UNSUPPORTED_PROTOCOL; } + + UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS)); + if (json.containsKey(parm_uc)) { bits = json[parm_uc]; } + UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT)); + if (json.containsKey(parm_uc)) { repeat = json[parm_uc]; } + UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATALSB)); + if (json.containsKey(parm_uc)) { data = reverseBitsInBytes64(strtoull(json[parm_uc], nullptr, 0)); } + UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA)); + if (json.containsKey(parm_uc)) { data = strtoull(json[parm_uc], nullptr, 0); } + if (0 == bits) { return IE_SYNTAX_IRSEND; } + + + if (XdrvMailbox.index > repeat + 1) { repeat = XdrvMailbox.index - 1; } + + char dvalue[32]; + char hvalue[32]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol %d, bits %d, data 0x%s (%s), repeat %d"), + protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat); + + irsend_active = true; + bool success = irsend->send(protocol, data, bits, repeat); + + if (!success) { + irsend_active = false; + ResponseCmndChar(D_JSON_PROTOCOL_NOT_SUPPORTED); + } + return IE_NO_ERROR; +} + +uint32_t IrRemoteCmndIrSendRaw(void) +{ + + + + + + + + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); + if (p == nullptr) { + return IE_INVALID_RAWDATA; + } + + + uint16_t repeat = XdrvMailbox.index > 0 ? XdrvMailbox.index - 1 : 0; + + uint16_t freq = atoi(str); + if (!freq && (*str != '0')) { + uint16_t count = 0; + char *q = p; + for (; *q; count += (*q++ == ',')); + if (count < 2) { + return IE_INVALID_RAWDATA; + } + + uint16_t parm[count]; + for (uint32_t i = 0; i < count; i++) { + parm[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); + if (!parm[i]) { + if (!i) { + parm[0] = 38000; + } else { + return IE_INVALID_RAWDATA; + } + } + } + + uint16_t i = 0; + if (count < 4) { + + uint16_t mark = parm[1] *2; + if (3 == count) { + if (parm[2] < parm[1]) { + + mark = parm[1] * parm[2]; + } else { + + mark = parm[2]; + } + } + uint16_t raw_array[strlen(p)]; + for (; *p; *p++) { + if (*p == '0') { + raw_array[i++] = parm[1]; + } + else if (*p == '1') { + raw_array[i++] = mark; + } + } + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendRaw(raw_array, i, parm[0]); + if (r < repeat) { + irsend->space(40000); + } + } + } + else if (6 == count) { + + uint16_t raw_array[strlen(p)*2+3]; + raw_array[i++] = parm[1]; + raw_array[i++] = parm[2]; + uint32_t inter_message_32 = (parm[1] + parm[2]) * 3; + uint16_t inter_message = (inter_message_32 > 65000) ? 65000 : inter_message_32; + for (; *p; *p++) { + if (*p == '0') { + raw_array[i++] = parm[3]; + raw_array[i++] = parm[4]; + } + else if (*p == '1') { + raw_array[i++] = parm[3]; + raw_array[i++] = parm[5]; + } + } + raw_array[i++] = parm[3]; + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendRaw(raw_array, i, parm[0]); + if (r < repeat) { + irsend->space(inter_message); + } + } + } + else { + return IE_INVALID_RAWDATA; + } + } else { + if (!freq) { freq = 38000; } + uint16_t count = 0; + char *q = p; + for (; *q; count += (*q++ == ',')); + if (0 == count) { + return IE_INVALID_RAWDATA; + } + + + count++; + if (count < 200) { + uint16_t raw_array[count]; + for (uint32_t i = 0; i < count; i++) { + raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); + } + + + + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendRaw(raw_array, count, freq); + } + } else { + uint16_t *raw_array = reinterpret_cast(malloc(count * sizeof(uint16_t))); + if (raw_array == nullptr) { + return IE_INVALID_RAWDATA; + } + + for (uint32_t i = 0; i < count; i++) { + raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); + } + + + + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendRaw(raw_array, count, freq); + } + free(raw_array); + } + } + + return IE_NO_ERROR; +} + +void CmndIrSend(void) +{ + uint8_t error = IE_SYNTAX_IRSEND; + + if (XdrvMailbox.data_len) { + if (strstr(XdrvMailbox.data, "{") == nullptr) { + error = IrRemoteCmndIrSendRaw(); + } else { + error = IrRemoteCmndIrSendJson(); + } + } + IrRemoteCmndResponse(error); +} + +void IrRemoteCmndResponse(uint32_t error) +{ + switch (error) { + case IE_INVALID_RAWDATA: + ResponseCmndChar_P(PSTR(D_JSON_INVALID_RAWDATA)); + break; + case IE_INVALID_JSON: + ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); + break; + case IE_SYNTAX_IRSEND: + Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_NO " " D_JSON_IR_BITS " " D_JSON_OR " " D_JSON_IR_DATA "\"}")); + break; + case IE_SYNTAX_IRHVAC: + Response_P(PSTR("{\"" D_CMND_IRHVAC "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_VENDOR ", " D_JSON_IRHVAC_MODE " " D_JSON_OR " " D_JSON_IRHVAC_FANSPEED "\"}")); + break; + case IE_UNSUPPORTED_HVAC: + Response_P(PSTR("{\"" D_CMND_IRHVAC "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_VENDOR " (%s)\"}"), listSupportedProtocols(true).c_str()); + break; + case IE_UNSUPPORTED_PROTOCOL: + Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_PROTOCOL " (%s)\"}"), listSupportedProtocols(false).c_str()); + break; + default: + ResponseCmndDone(); + } +} + + + + + +bool Xdrv05(uint8_t function) +{ + bool result = false; + + if ((pin[GPIO_IRSEND] < 99) || (pin[GPIO_IRRECV] < 99)) { + switch (function) { + case FUNC_PRE_INIT: + if (pin[GPIO_IRSEND] < 99) { + IrSendInit(); + } + if (pin[GPIO_IRRECV] < 99) { + IrReceiveInit(); + } + break; + case FUNC_EVERY_50_MSECOND: + if (pin[GPIO_IRRECV] < 99) { + IrReceiveCheck(); + } + irsend_active = false; + break; + case FUNC_COMMAND: + if (pin[GPIO_IRSEND] < 99) { + result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand); + } + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_06_snfbridge.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_06_snfbridge.ino" +#ifdef USE_SONOFF_RF + + + + +#define XDRV_06 6 + +const uint32_t SFB_TIME_AVOID_DUPLICATE = 2000; + +enum SonoffBridgeCommands { + CMND_RFSYNC, CMND_RFLOW, CMND_RFHIGH, CMND_RFHOST, CMND_RFCODE }; + +const char kSonoffBridgeCommands[] PROGMEM = "|" + D_CMND_RFSYNC "|" D_CMND_RFLOW "|" D_CMND_RFHIGH "|" D_CMND_RFHOST "|" D_CMND_RFCODE "|" D_CMND_RFKEY "|" D_CMND_RFRAW; + +void (* const SonoffBridgeCommand[])(void) PROGMEM = { + &CmndRfBridge, &CmndRfBridge, &CmndRfBridge, &CmndRfBridge, &CmndRfBridge, &CmndRfKey, &CmndRfRaw }; + +struct SONOFFBRIDGE { + uint32_t last_received_id = 0; + uint32_t last_send_code = 0; + uint32_t last_time = 0; + uint32_t last_learn_time = 0; + uint8_t receive_flag = 0; + uint8_t receive_raw_flag = 0; + uint8_t learn_key = 1; + uint8_t learn_active = 0; + uint8_t expected_bytes = 0; +} SnfBridge; + +#ifdef USE_RF_FLASH + + + + + + + +#include "ihx.h" +#include "c2.h" + +const ssize_t RF_RECORD_NO_START_FOUND = -1; +const ssize_t RF_RECORD_NO_END_FOUND = -2; + +ssize_t rf_find_hex_record_start(uint8_t *buf, size_t size) +{ + for (size_t i = 0; i < size; i++) { + if (buf[i] == ':') { + return i; + } + } + return RF_RECORD_NO_START_FOUND; +} + +ssize_t rf_find_hex_record_end(uint8_t *buf, size_t size) +{ + for (size_t i = 0; i < size; i++) { + if (buf[i] == '\n') { + return i; + } + } + return RF_RECORD_NO_END_FOUND; +} + +ssize_t rf_glue_remnant_with_new_data_and_write(const uint8_t *remnant_data, uint8_t *new_data, size_t new_data_len) +{ + ssize_t record_start; + ssize_t record_end; + ssize_t glue_record_sz; + uint8_t *glue_buf; + ssize_t result; + + if (remnant_data[0] != ':') { return -8; } + + + record_end = rf_find_hex_record_end(new_data, new_data_len); + record_start = rf_find_hex_record_start(new_data, new_data_len); + + + + + if ((record_start != RF_RECORD_NO_START_FOUND) && (record_start < record_end)) { + return -8; + } + + glue_record_sz = strlen((const char *) remnant_data) + record_end; + + glue_buf = (uint8_t *) malloc(glue_record_sz); + if (glue_buf == nullptr) { return -2; } + + + memcpy(glue_buf, remnant_data, strlen((const char *) remnant_data)); + memcpy(glue_buf + strlen((const char *) remnant_data), new_data, record_end); + + result = rf_decode_and_write(glue_buf, glue_record_sz); + free(glue_buf); + return result; +} + +ssize_t rf_decode_and_write(uint8_t *record, size_t size) +{ + uint8_t err = ihx_decode(record, size); + if (err != IHX_SUCCESS) { return -13; } + + ihx_t *h = (ihx_t *) record; + if (h->record_type == IHX_RT_DATA) { + int retries = 5; + uint16_t address = h->address_high * 0x100 + h->address_low; + + do { + err = c2_programming_init(); + err = c2_block_write(address, h->data, h->len); + } while (err != C2_SUCCESS && retries--); + } else if (h->record_type == IHX_RT_END_OF_FILE) { + + err = c2_reset(); + } + + if (err != C2_SUCCESS) { return -12; } + + return 0; +} + +ssize_t rf_search_and_write(uint8_t *buf, size_t size) +{ + + ssize_t rec_end; + ssize_t rec_start; + ssize_t err; + + for (size_t i = 0; i < size; i++) { + + rec_start = rf_find_hex_record_start(buf + i, size - i); + if (rec_start == RF_RECORD_NO_START_FOUND) { + + return -8; + } + + + rec_start += i; + rec_end = rf_find_hex_record_end(buf + rec_start, size - rec_start); + if (rec_end == RF_RECORD_NO_END_FOUND) { + + return rec_start; + } + + + rec_end += rec_start; + + err = rf_decode_and_write(buf + rec_start, rec_end - rec_start); + if (err < 0) { return err; } + i = rec_end; + } + + return 0; +} + +uint8_t rf_erase_flash(void) +{ + uint8_t err; + + for (uint32_t i = 0; i < 4; i++) { + err = c2_programming_init(); + if (err != C2_SUCCESS) { + return 10; + } + err = c2_device_erase(); + if (err != C2_SUCCESS) { + if (i < 3) { + c2_reset(); + } else { + return 11; + } + } else { + break; + } + } + return 0; +} + +uint8_t SnfBrUpdateInit(void) +{ + pinMode(PIN_C2CK, OUTPUT); + pinMode(PIN_C2D, INPUT); + + return rf_erase_flash(); +} +#endif + + + +void SonoffBridgeReceivedRaw(void) +{ + + uint8_t buckets = 0; + + if (0xB1 == serial_in_buffer[1]) { buckets = serial_in_buffer[2] << 1; } + + 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]) { + if ((i > 3) && buckets) { buckets--; } + if ((i < 3) || (buckets % 2) || (i == serial_in_byte_counter -2)) { + ResponseAppend_P(PSTR(" ")); + } + } + } + ResponseAppend_P(PSTR("\"}}")); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_CMND_RFRAW)); + + XdrvRulesProcess(); +} + + + +void SonoffBridgeLearnFailed(void) +{ + SnfBridge.learn_active = 0; + Response_P(S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, SnfBridge.learn_key, D_JSON_LEARN_FAILED); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RFKEY)); +} + +void SonoffBridgeReceived(void) +{ + uint16_t sync_time = 0; + uint16_t low_time = 0; + uint16_t high_time = 0; + uint32_t received_id = 0; + char rfkey[8]; + char stemp[16]; + + AddLogSerial(LOG_LEVEL_DEBUG); + + if (0xA2 == serial_in_buffer[0]) { + SonoffBridgeLearnFailed(); + } + else if (0xA3 == serial_in_buffer[0]) { + SnfBridge.learn_active = 0; + low_time = serial_in_buffer[3] << 8 | serial_in_buffer[4]; + high_time = serial_in_buffer[5] << 8 | serial_in_buffer[6]; + if (low_time && high_time) { + for (uint32_t i = 0; i < 9; i++) { + Settings.rf_code[SnfBridge.learn_key][i] = serial_in_buffer[i +1]; + } + Response_P(S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, SnfBridge.learn_key, D_JSON_LEARNED); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RFKEY)); + } else { + SonoffBridgeLearnFailed(); + } + } + else if (0xA4 == serial_in_buffer[0]) { + if (SnfBridge.learn_active) { + SonoffBridgeLearnFailed(); + } else { + sync_time = serial_in_buffer[1] << 8 | serial_in_buffer[2]; + low_time = serial_in_buffer[3] << 8 | serial_in_buffer[4]; + high_time = serial_in_buffer[5] << 8 | serial_in_buffer[6]; + received_id = serial_in_buffer[7] << 16 | serial_in_buffer[8] << 8 | serial_in_buffer[9]; + + unsigned long now = millis(); + if (!((received_id == SnfBridge.last_received_id) && (now - SnfBridge.last_time < SFB_TIME_AVOID_DUPLICATE))) { + SnfBridge.last_received_id = received_id; + SnfBridge.last_time = now; + strncpy_P(rfkey, PSTR("\"" D_JSON_NONE "\""), sizeof(rfkey)); + for (uint32_t i = 1; i <= 16; i++) { + if (Settings.rf_code[i][0]) { + uint32_t send_id = Settings.rf_code[i][6] << 16 | Settings.rf_code[i][7] << 8 | Settings.rf_code[i][8]; + if (send_id == received_id) { + snprintf_P(rfkey, sizeof(rfkey), PSTR("%d"), i); + break; + } + } + } + if (Settings.flag.rf_receive_decimal) { + snprintf_P(stemp, sizeof(stemp), PSTR("%u"), received_id); + } else { + snprintf_P(stemp, sizeof(stemp), PSTR("\"%06X\""), received_id); + } + 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(); + #ifdef USE_DOMOTICZ + DomoticzSensor(DZ_COUNT, received_id); + #endif + } + } + } +} + +bool SonoffBridgeSerialInput(void) +{ + + static int8_t receive_len = 0; + + if (SnfBridge.receive_flag) { + if (SnfBridge.receive_raw_flag) { + if (!serial_in_byte_counter) { + serial_in_buffer[serial_in_byte_counter++] = 0xAA; + } + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + if (serial_in_byte_counter == 3) { + if ((0xA6 == serial_in_buffer[1]) || (0xAB == serial_in_buffer[1])) { + receive_len = serial_in_buffer[2] + 4; + } + } + if ((!receive_len && (0x55 == serial_in_byte)) || (receive_len && (serial_in_byte_counter == receive_len))) { + SonoffBridgeReceivedRaw(); + SnfBridge.receive_flag = 0; + return 1; + } + } + else if (!((0 == serial_in_byte_counter) && (0 == serial_in_byte))) { + if (0 == serial_in_byte_counter) { + SnfBridge.expected_bytes = 2; + if (serial_in_byte >= 0xA3) { + SnfBridge.expected_bytes = 11; + } + if (serial_in_byte == 0xA6) { + SnfBridge.expected_bytes = 0; + serial_in_buffer[serial_in_byte_counter++] = 0xAA; + SnfBridge.receive_raw_flag = 1; + } + } + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + if ((SnfBridge.expected_bytes == serial_in_byte_counter) && (0x55 == serial_in_byte)) { + SonoffBridgeReceived(); + SnfBridge.receive_flag = 0; + return 1; + } + } + serial_in_byte = 0; + } + if (0xAA == serial_in_byte) { + serial_in_byte_counter = 0; + serial_in_byte = 0; + SnfBridge.receive_flag = 1; + receive_len = 0; + } + return 0; +} + +void SonoffBridgeSendCommand(uint8_t code) +{ + Serial.write(0xAA); + Serial.write(code); + Serial.write(0x55); +} + +void SonoffBridgeSendAck(void) +{ + Serial.write(0xAA); + Serial.write(0xA0); + Serial.write(0x55); +} + +void SonoffBridgeSendCode(uint32_t code) +{ + Serial.write(0xAA); + Serial.write(0xA5); + for (uint32_t i = 0; i < 6; i++) { + Serial.write(Settings.rf_code[0][i]); + } + Serial.write((code >> 16) & 0xff); + Serial.write((code >> 8) & 0xff); + Serial.write(code & 0xff); + Serial.write(0x55); + Serial.flush(); +} + +void SonoffBridgeSend(uint8_t idx, uint8_t key) +{ + uint8_t code; + + key--; + Serial.write(0xAA); + Serial.write(0xA5); + for (uint32_t i = 0; i < 8; i++) { + Serial.write(Settings.rf_code[idx][i]); + } + if (0 == idx) { + code = (0x10 << (key >> 2)) | (1 << (key & 3)); + } else { + code = Settings.rf_code[idx][8]; + } + Serial.write(code); + Serial.write(0x55); + Serial.flush(); +#ifdef USE_DOMOTICZ + + +#endif +} + +void SonoffBridgeLearn(uint8_t key) +{ + SnfBridge.learn_key = key; + SnfBridge.learn_active = 1; + SnfBridge.last_learn_time = millis(); + Serial.write(0xAA); + Serial.write(0xA1); + Serial.write(0x55); +} + + + + + +void CmndRfBridge(void) +{ + char *p; + char stemp [10]; + uint32_t code = 0; + uint8_t radix = 10; + + uint32_t set_index = XdrvMailbox.command_code *2; + + if (XdrvMailbox.data[0] == '#') { + XdrvMailbox.data++; + XdrvMailbox.data_len--; + radix = 16; + } + + if (XdrvMailbox.data_len) { + code = strtol(XdrvMailbox.data, &p, radix); + if (code) { + if (CMND_RFCODE == XdrvMailbox.command_code) { + SnfBridge.last_send_code = code; + SonoffBridgeSendCode(code); + } else { + if (1 == XdrvMailbox.payload) { + code = pgm_read_byte(kDefaultRfCode + set_index) << 8 | pgm_read_byte(kDefaultRfCode + set_index +1); + } + uint8_t msb = code >> 8; + uint8_t lsb = code & 0xFF; + if ((code > 0) && (code < 0x7FFF) && (msb != 0x55) && (lsb != 0x55)) { + Settings.rf_code[0][set_index] = msb; + Settings.rf_code[0][set_index +1] = lsb; + } + } + } + } + if (CMND_RFCODE == XdrvMailbox.command_code) { + code = SnfBridge.last_send_code; + } else { + code = Settings.rf_code[0][set_index] << 8 | Settings.rf_code[0][set_index +1]; + } + if (10 == radix) { + snprintf_P(stemp, sizeof(stemp), PSTR("%d"), code); + } else { + snprintf_P(stemp, sizeof(stemp), PSTR("\"#%06X\""), code); + } + Response_P(S_JSON_COMMAND_XVALUE, XdrvMailbox.command, stemp); +} + +void CmndRfKey(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 16)) { + unsigned long now = millis(); + if ((!SnfBridge.learn_active) || (now - SnfBridge.last_learn_time > 60100)) { + SnfBridge.learn_active = 0; + if (2 == XdrvMailbox.payload) { + SonoffBridgeLearn(XdrvMailbox.index); + ResponseCmndIdxChar(D_JSON_START_LEARNING); + } + else if (3 == XdrvMailbox.payload) { + Settings.rf_code[XdrvMailbox.index][0] = 0; + ResponseCmndIdxChar(D_JSON_SET_TO_DEFAULT); + } + else if (4 == XdrvMailbox.payload) { + for (uint32_t i = 0; i < 6; i++) { + Settings.rf_code[XdrvMailbox.index][i] = Settings.rf_code[0][i]; + } + Settings.rf_code[XdrvMailbox.index][6] = (SnfBridge.last_send_code >> 16) & 0xff; + Settings.rf_code[XdrvMailbox.index][7] = (SnfBridge.last_send_code >> 8) & 0xff; + Settings.rf_code[XdrvMailbox.index][8] = SnfBridge.last_send_code & 0xff; + ResponseCmndIdxChar(D_JSON_SAVED); + } else if (5 == XdrvMailbox.payload) { + uint8_t key = XdrvMailbox.index; + uint8_t index = (0 == Settings.rf_code[key][0]) ? 0 : key; + uint16_t sync_time = (Settings.rf_code[index][0] << 8) | Settings.rf_code[index][1]; + uint16_t low_time = (Settings.rf_code[index][2] << 8) | Settings.rf_code[index][3]; + uint16_t high_time = (Settings.rf_code[index][4] << 8) | Settings.rf_code[index][5]; + uint32_t code = (Settings.rf_code[index][6] << 16) | (Settings.rf_code[index][7] << 8); + if (0 == index) { + key--; + code |= (uint8_t)((0x10 << (key >> 2)) | (1 << (key & 3))); + } else { + code |= Settings.rf_code[index][8]; + } + Response_P(PSTR("{\"%s%d\":{\"" D_JSON_SYNC "\":%d,\"" D_JSON_LOW "\":%d,\"" D_JSON_HIGH "\":%d,\"" D_JSON_DATA "\":\"%06X\"}}"), + XdrvMailbox.command, XdrvMailbox.index, sync_time, low_time, high_time, code); + } else { + if ((1 == XdrvMailbox.payload) || (0 == Settings.rf_code[XdrvMailbox.index][0])) { + SonoffBridgeSend(0, XdrvMailbox.index); + ResponseCmndIdxChar(D_JSON_DEFAULT_SENT); + } else { + SonoffBridgeSend(XdrvMailbox.index, 0); + ResponseCmndIdxChar(D_JSON_LEARNED_SENT); + } + } + } else { + Response_P(S_JSON_COMMAND_INDEX_SVALUE, XdrvMailbox.command, SnfBridge.learn_key, D_JSON_LEARNING_ACTIVE); + } + } +} + +void CmndRfRaw(void) +{ + if (XdrvMailbox.data_len) { + if (XdrvMailbox.data_len < 6) { + switch (XdrvMailbox.payload) { + case 0: + SonoffBridgeSendCommand(0xA7); + case 1: + SnfBridge.receive_raw_flag = XdrvMailbox.payload; + break; + case 166: + case 167: + case 169: + case 176: + case 177: + case 255: + SonoffBridgeSendCommand(XdrvMailbox.payload); + SnfBridge.receive_raw_flag = 1; + break; + case 192: + char beep[] = "AAC000C055\0"; + SerialSendRaw(beep); + break; + } + } else { + SerialSendRaw(RemoveSpace(XdrvMailbox.data)); + SnfBridge.receive_raw_flag = 1; + } + } + ResponseCmndStateText(SnfBridge.receive_raw_flag); +} + + + + + +bool Xdrv06(uint8_t function) +{ + bool result = false; + +#ifdef ESP8266 + if (SONOFF_BRIDGE == my_module_type) { + switch (function) { + case FUNC_SERIAL: + result = SonoffBridgeSerialInput(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kSonoffBridgeCommands, SonoffBridgeCommand); + break; + case FUNC_INIT: + SnfBridge.receive_raw_flag = 0; + SonoffBridgeSendCommand(0xA7); + break; + case FUNC_PRE_INIT: + SetSerial(19200, TS_SERIAL_8N1); + break; + } + } +#endif + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_07_domoticz.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_07_domoticz.ino" +#ifdef USE_DOMOTICZ + +#define XDRV_07 7 + +#define D_PRFX_DOMOTICZ "Domoticz" +#define D_CMND_IDX "Idx" +#define D_CMND_KEYIDX "KeyIdx" +#define D_CMND_SWITCHIDX "SwitchIdx" +#define D_CMND_SENSORIDX "SensorIdx" +#define D_CMND_UPDATETIMER "UpdateTimer" + +const char kDomoticzCommands[] PROGMEM = D_PRFX_DOMOTICZ "|" + D_CMND_IDX "|" D_CMND_KEYIDX "|" D_CMND_SWITCHIDX "|" D_CMND_SENSORIDX "|" D_CMND_UPDATETIMER ; + +void (* const DomoticzCommand[])(void) PROGMEM = { + &CmndDomoticzIdx, &CmndDomoticzKeyIdx, &CmndDomoticzSwitchIdx, &CmndDomoticzSensorIdx, &CmndDomoticzUpdateTimer }; + +const char DOMOTICZ_MESSAGE[] PROGMEM = "{\"idx\":%d,\"nvalue\":%d,\"svalue\":\"%s\",\"Battery\":%d,\"RSSI\":%d}"; + +#if MAX_DOMOTICZ_SNS_IDX < DZ_MAX_SENSORS + #error "Domoticz: Too many sensors or change settings.h layout" +#endif + +const char kDomoticzSensors[] PROGMEM = + D_DOMOTICZ_TEMP "|" D_DOMOTICZ_TEMP_HUM "|" D_DOMOTICZ_TEMP_HUM_BARO "|" D_DOMOTICZ_POWER_ENERGY "|" D_DOMOTICZ_ILLUMINANCE "|" + D_DOMOTICZ_COUNT "|" D_DOMOTICZ_VOLTAGE "|" D_DOMOTICZ_CURRENT "|" D_DOMOTICZ_AIRQUALITY "|" D_DOMOTICZ_P1_SMART_METER "|" D_DOMOTICZ_SHUTTER ; + +char domoticz_in_topic[] = DOMOTICZ_IN_TOPIC; + +int domoticz_update_timer = 0; +uint32_t domoticz_fan_debounce = 0; +bool domoticz_subscribe = false; +bool domoticz_update_flag = true; + +#ifdef USE_SHUTTER +bool domoticz_is_shutter = false; +#endif + +int DomoticzBatteryQuality(void) +{ + + + + + int quality = 100; + +#ifdef USE_ADC_VCC + uint16_t voltage = ESP.getVcc(); + if (voltage <= 2600) { + quality = 0; + } else if (voltage >= 4600) { + quality = 200; + } else { + quality = (voltage - 2600) / 10; + } +#endif + return quality; +} + +int DomoticzRssiQuality(void) +{ + + + return WifiGetRssiAsQuality(WiFi.RSSI()) / 10; +} + +#ifdef USE_SONOFF_IFAN +void MqttPublishDomoticzFanState(void) +{ + if (Settings.flag.mqtt_enabled && Settings.domoticz_relay_idx[1]) { + char svalue[8]; + + int fan_speed = GetFanspeed(); + snprintf_P(svalue, sizeof(svalue), PSTR("%d"), fan_speed * 10); + Response_P(DOMOTICZ_MESSAGE, (int)Settings.domoticz_relay_idx[1], (0 == fan_speed) ? 0 : 2, svalue, DomoticzBatteryQuality(), DomoticzRssiQuality()); + MqttPublish(domoticz_in_topic); + + domoticz_fan_debounce = millis(); + } +} + +void DomoticzUpdateFanState(void) +{ + if (domoticz_update_flag) { + MqttPublishDomoticzFanState(); + } + domoticz_update_flag = true; +} +#endif + +void MqttPublishDomoticzPowerState(uint8_t device) +{ + if (Settings.flag.mqtt_enabled) { + if ((device < 1) || (device > devices_present)) { device = 1; } + if (Settings.domoticz_relay_idx[device -1]) { +#ifdef USE_SHUTTER + if (domoticz_is_shutter) { + + } else { +#endif +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (device > 1)) { + + } else { +#endif + char svalue[8]; + + snprintf_P(svalue, sizeof(svalue), PSTR("%d"), Settings.light_dimmer); + Response_P(DOMOTICZ_MESSAGE, (int)Settings.domoticz_relay_idx[device -1], (power & (1 << (device -1))) ? 1 : 0, (light_type) ? svalue : "", DomoticzBatteryQuality(), DomoticzRssiQuality()); + MqttPublish(domoticz_in_topic); +#ifdef USE_SONOFF_IFAN + } +#endif +#ifdef USE_SHUTTER + } +#endif + } + } +} + +void DomoticzUpdatePowerState(uint8_t device) +{ + if (domoticz_update_flag) { + MqttPublishDomoticzPowerState(device); + } + domoticz_update_flag = true; +} + +void DomoticzMqttUpdate(void) +{ + if (domoticz_subscribe && (Settings.domoticz_update_timer || domoticz_update_timer)) { + domoticz_update_timer--; + if (domoticz_update_timer <= 0) { + domoticz_update_timer = Settings.domoticz_update_timer; + for (uint32_t i = 1; i <= devices_present; i++) { +#ifdef USE_SHUTTER + if (domoticz_is_shutter) + { + + break; + } +#endif +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (i > 1)) { + MqttPublishDomoticzFanState(); + break; + } else { +#endif + MqttPublishDomoticzPowerState(i); +#ifdef USE_SONOFF_IFAN + } +#endif + } + } + } +} + +void DomoticzMqttSubscribe(void) +{ + uint8_t maxdev = (devices_present > MAX_DOMOTICZ_IDX) ? MAX_DOMOTICZ_IDX : devices_present; + for (uint32_t i = 0; i < maxdev; i++) { + if (Settings.domoticz_relay_idx[i]) { + domoticz_subscribe = true; + } + } + + if (domoticz_subscribe) { + char stopic[TOPSZ]; + snprintf_P(stopic, sizeof(stopic), PSTR(DOMOTICZ_OUT_TOPIC "/#")); + MqttSubscribe(stopic); + } +} +# 219 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_07_domoticz.ino" +bool DomoticzMqttData(void) +{ + domoticz_update_flag = true; + + if (strncasecmp_P(XdrvMailbox.topic, PSTR(DOMOTICZ_OUT_TOPIC), strlen(DOMOTICZ_OUT_TOPIC)) != 0) { + return false; + } + + + if (XdrvMailbox.data_len < 20) { + return true; + } + StaticJsonBuffer<400> jsonBuf; + JsonObject& domoticz = jsonBuf.parseObject(XdrvMailbox.data); + if (!domoticz.success()) { + return true; + } + + + + uint32_t idx = domoticz["idx"]; + int16_t nvalue = -1; + if (domoticz.containsKey("nvalue")) { + nvalue = domoticz["nvalue"]; + } + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_DOMOTICZ "idx %d, nvalue %d"), idx, nvalue); + + bool found = false; + if ((idx > 0) && (nvalue >= 0) && (nvalue <= 15)) { + uint8_t maxdev = (devices_present > MAX_DOMOTICZ_IDX) ? MAX_DOMOTICZ_IDX : devices_present; + for (uint32_t i = 0; i < maxdev; i++) { + if (idx == Settings.domoticz_relay_idx[i]) { + bool iscolordimmer = strcmp_P(domoticz["dtype"],PSTR("Color Switch")) == 0; + bool isShutter = strcmp_P(domoticz["dtype"],PSTR("Light/Switch")) == 0 & strncmp_P(domoticz["switchType"],PSTR("Blinds"), 6) == 0; + + char stemp1[10]; + snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), i +1); +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (1 == i)) { + uint8_t svalue = 0; + if (domoticz.containsKey("svalue1")) { + svalue = domoticz["svalue1"]; + } else { + return true; + } + svalue = (nvalue == 2) ? svalue / 10 : 0; + if (GetFanspeed() == svalue) { + return true; + } + if (TimePassedSince(domoticz_fan_debounce) < 1000) { + return true; + } + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_FANSPEED)); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), svalue); + found = true; + } else +#endif +#ifdef USE_SHUTTER + if (isShutter) + { + if (domoticz.containsKey("nvalue")) { + nvalue = domoticz["nvalue"]; + } + + uint8_t position = 0; + if (domoticz.containsKey("svalue1")) { + position = domoticz["svalue1"]; + } + if (nvalue != 2) { + position = nvalue == 0 ? 0 : 100; + } + + snprintf_P(XdrvMailbox.topic, TOPSZ, PSTR("/" D_PRFX_SHUTTER D_CMND_SHUTTER_POSITION)); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), position); + XdrvMailbox.data_len = position > 99 ? 3 : (position > 9 ? 2 : 1); + + found = true; + } else +#endif +#ifdef USE_LIGHT + if (iscolordimmer && 10 == nvalue) { + + JsonObject& color = domoticz["Color"]; + uint16_t level = nvalue = domoticz["svalue1"]; + uint16_t r = color["r"]; r = r * level / 100; + uint16_t g = color["g"]; g = g * level / 100; + uint16_t b = color["b"]; b = b * level / 100; + uint16_t cw = color["cw"]; cw = cw * level / 100; + uint16_t ww = color["ww"]; ww = ww * level / 100; + uint16_t m = 0; + uint16_t t = 0; + if (color.containsKey("m")) { + m = color["m"]; + t = color["t"]; + } + if (2 == m) { + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_BACKLOG)); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR(D_CMND_COLORTEMPERATURE " %d;" D_CMND_DIMMER " %d"), changeUIntScale(t, 0, 255, CT_MIN, CT_MAX), level); + } else { + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_COLOR)); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%02x%02x%02x%02x%02x"), r, g, b, cw, ww); + } + found = true; + } + else if ((!iscolordimmer && 2 == nvalue) || + (iscolordimmer && 15 == nvalue)) { + if (domoticz.containsKey("svalue1")) { + nvalue = domoticz["svalue1"]; + } else { + return true; + } + if (light_type && (Settings.light_dimmer == nvalue) && ((power >> i) &1)) { + return true; + } + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_DIMMER)); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), nvalue); + found = true; + } else +#endif + if (1 == nvalue || 0 == nvalue) { + if (((power >> i) &1) == (power_t)nvalue) { + return true; + } + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_POWER "%s"), (devices_present > 1) ? stemp1 : ""); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), nvalue); + found = true; + } + break; + } + } + } + if (!found) { return true; } + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_DOMOTICZ D_RECEIVED_TOPIC " %s, " D_DATA " %s"), XdrvMailbox.topic, XdrvMailbox.data); + + domoticz_update_flag = false; + return false; +} + + + +bool DomoticzSendKey(uint8_t key, uint8_t device, uint8_t state, uint8_t svalflg) +{ + bool result = false; + + 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) ? (POWER_TOGGLE == state) ? "Toggle" : "On" : "Off"); + MqttPublish(domoticz_in_topic); + result = true; + } + } + return result; +} +# 393 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_07_domoticz.ino" +uint8_t DomoticzHumidityState(float h) +{ + return (!h) ? 0 : (h < 40) ? 2 : (h > 70) ? 3 : 1; +} + +void DomoticzSensor(uint8_t idx, char *data) +{ + if (Settings.domoticz_sensor_idx[idx]) { + char dmess[128]; + + memcpy(dmess, mqtt_data, sizeof(dmess)); + if (DZ_AIRQUALITY == idx) { + Response_P(PSTR("{\"idx\":%d,\"nvalue\":%s,\"Battery\":%d,\"RSSI\":%d}"), + Settings.domoticz_sensor_idx[idx], data, DomoticzBatteryQuality(), DomoticzRssiQuality()); + } else { + uint8_t nvalue = 0; +#ifdef USE_SHUTTER + if (DZ_SHUTTER == idx) { + uint8_t position = atoi(data); + nvalue = position < 2 ? 0 : (position == 100 ? 1 : 2); + } +#endif + Response_P(DOMOTICZ_MESSAGE, + Settings.domoticz_sensor_idx[idx], nvalue, data, DomoticzBatteryQuality(), DomoticzRssiQuality()); + } + MqttPublish(domoticz_in_topic); + memcpy(mqtt_data, dmess, sizeof(dmess)); + } +} + +void DomoticzSensor(uint8_t idx, uint32_t value) +{ + char data[16]; + snprintf_P(data, sizeof(data), PSTR("%d"), value); + DomoticzSensor(idx, data); +} + + +void DomoticzTempHumPressureSensor(float temp, float hum, float baro) +{ + char temperature[FLOATSZ]; + dtostrfd(temp, 2, temperature); + char humidity[FLOATSZ]; + dtostrfd(hum, 2, humidity); + + char data[32]; + if (baro > -1) { + char pressure[FLOATSZ]; + dtostrfd(baro, 2, pressure); + + snprintf_P(data, sizeof(data), PSTR("%s;%s;%d;%s;5"), temperature, humidity, DomoticzHumidityState(hum), pressure); + DomoticzSensor(DZ_TEMP_HUM_BARO, data); + } else { + snprintf_P(data, sizeof(data), PSTR("%s;%s;%d"), temperature, humidity, DomoticzHumidityState(hum)); + DomoticzSensor(DZ_TEMP_HUM, data); + } +} + +void DomoticzSensorPowerEnergy(int power, char *energy) +{ + char data[16]; + snprintf_P(data, sizeof(data), PSTR("%d;%s"), power, energy); + DomoticzSensor(DZ_POWER_ENERGY, data); +} + +void DomoticzSensorP1SmartMeter(char *usage1, char *usage2, char *return1, char *return2, int power) +{ + + + + + + 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); +} + + + + + +void CmndDomoticzIdx(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { + if (XdrvMailbox.payload >= 0) { + Settings.domoticz_relay_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; + restart_flag = 2; + } + ResponseCmndIdxNumber(Settings.domoticz_relay_idx[XdrvMailbox.index -1]); + } +} + +void CmndDomoticzKeyIdx(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { + if (XdrvMailbox.payload >= 0) { + Settings.domoticz_key_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.domoticz_key_idx[XdrvMailbox.index -1]); + } +} + +void CmndDomoticzSwitchIdx(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { + if (XdrvMailbox.payload >= 0) { + Settings.domoticz_switch_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.domoticz_switch_idx[XdrvMailbox.index -1]); + } +} + +void CmndDomoticzSensorIdx(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= DZ_MAX_SENSORS)) { + if (XdrvMailbox.payload >= 0) { + Settings.domoticz_sensor_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.domoticz_sensor_idx[XdrvMailbox.index -1]); + } +} + +void CmndDomoticzUpdateTimer(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.domoticz_update_timer = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.domoticz_update_timer); +} + + + + + +#ifdef USE_WEBSERVER + +#define WEB_HANDLE_DOMOTICZ "dm" + +const char S_CONFIGURE_DOMOTICZ[] PROGMEM = D_CONFIGURE_DOMOTICZ; + +const char HTTP_BTN_MENU_DOMOTICZ[] PROGMEM = + "

"; + +const char HTTP_FORM_DOMOTICZ[] PROGMEM = + "
 " D_DOMOTICZ_PARAMETERS " " + "
" + ""; +const char HTTP_FORM_DOMOTICZ_RELAY[] PROGMEM = + "" + ""; +const char HTTP_FORM_DOMOTICZ_SWITCH[] PROGMEM = + ""; +const char HTTP_FORM_DOMOTICZ_SENSOR[] PROGMEM = + ""; +const char HTTP_FORM_DOMOTICZ_TIMER[] PROGMEM = + ""; + +void HandleDomoticzConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_DOMOTICZ); + + if (Webserver->hasArg("save")) { + DomoticzSaveSettings(); + WebRestart(1); + return; + } + + char stemp[40]; + + WSContentStart_P(S_CONFIGURE_DOMOTICZ); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_DOMOTICZ); + for (uint32_t i = 0; i < MAX_DOMOTICZ_IDX; i++) { + if (i < devices_present) { + WSContentSend_P(HTTP_FORM_DOMOTICZ_RELAY, + i +1, i, Settings.domoticz_relay_idx[i], + i +1, i, Settings.domoticz_key_idx[i]); + } + if (pin[GPIO_SWT1 +i] < 99) { + WSContentSend_P(HTTP_FORM_DOMOTICZ_SWITCH, + i +1, i, Settings.domoticz_switch_idx[i]); + } +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (1 == i)) { break; } +#endif + } + for (uint32_t i = 0; i < DZ_MAX_SENSORS; i++) { + WSContentSend_P(HTTP_FORM_DOMOTICZ_SENSOR, + i +1, GetTextIndexed(stemp, sizeof(stemp), i, kDomoticzSensors), i, Settings.domoticz_sensor_idx[i]); + } + WSContentSend_P(HTTP_FORM_DOMOTICZ_TIMER, Settings.domoticz_update_timer); + WSContentSend_P(PSTR("
" D_DOMOTICZ_IDX " %d
" D_DOMOTICZ_KEY_IDX " %d
" D_DOMOTICZ_SWITCH_IDX " %d
" D_DOMOTICZ_SENSOR_IDX " %d %s
" D_DOMOTICZ_UPDATE_TIMER " (" STR(DOMOTICZ_UPDATE_TIMER) ")
")); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void DomoticzSaveSettings(void) +{ + char stemp[20]; + char ssensor_indices[6 * MAX_DOMOTICZ_SNS_IDX]; + char tmp[100]; + + for (uint32_t i = 0; i < MAX_DOMOTICZ_IDX; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("r%d"), i); + WebGetArg(stemp, tmp, sizeof(tmp)); + Settings.domoticz_relay_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); + snprintf_P(stemp, sizeof(stemp), PSTR("k%d"), i); + WebGetArg(stemp, tmp, sizeof(tmp)); + Settings.domoticz_key_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); + snprintf_P(stemp, sizeof(stemp), PSTR("s%d"), i); + WebGetArg(stemp, tmp, sizeof(tmp)); + Settings.domoticz_switch_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); + } + ssensor_indices[0] = '\0'; + for (uint32_t i = 0; i < DZ_MAX_SENSORS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("l%d"), i); + WebGetArg(stemp, tmp, sizeof(tmp)); + Settings.domoticz_sensor_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); + snprintf_P(ssensor_indices, sizeof(ssensor_indices), PSTR("%s%s%d"), ssensor_indices, (strlen(ssensor_indices)) ? "," : "", Settings.domoticz_sensor_idx[i]); + } + WebGetArg("ut", tmp, sizeof(tmp)); + Settings.domoticz_update_timer = (!strlen(tmp)) ? DOMOTICZ_UPDATE_TIMER : atoi(tmp); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_DOMOTICZ D_CMND_IDX " %d,%d,%d,%d, " D_CMND_KEYIDX " %d,%d,%d,%d, " D_CMND_SWITCHIDX " %d,%d,%d,%d, " D_CMND_SENSORIDX " %s, " D_CMND_UPDATETIMER " %d"), + Settings.domoticz_relay_idx[0], Settings.domoticz_relay_idx[1], Settings.domoticz_relay_idx[2], Settings.domoticz_relay_idx[3], + Settings.domoticz_key_idx[0], Settings.domoticz_key_idx[1], Settings.domoticz_key_idx[2], Settings.domoticz_key_idx[3], + Settings.domoticz_switch_idx[0], Settings.domoticz_switch_idx[1], Settings.domoticz_switch_idx[2], Settings.domoticz_switch_idx[3], + ssensor_indices, Settings.domoticz_update_timer); +} +#endif + + + + + +bool Xdrv07(uint8_t function) +{ + bool result = false; + + if (Settings.flag.mqtt_enabled) { + switch (function) { + case FUNC_EVERY_SECOND: + DomoticzMqttUpdate(); + break; + case FUNC_MQTT_DATA: + result = DomoticzMqttData(); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_DOMOTICZ); + break; + case FUNC_WEB_ADD_HANDLER: + Webserver->on("/" WEB_HANDLE_DOMOTICZ, HandleDomoticzConfiguration); + break; +#endif + case FUNC_MQTT_SUBSCRIBE: + DomoticzMqttSubscribe(); +#ifdef USE_SHUTTER + if (Settings.domoticz_sensor_idx[DZ_SHUTTER]) { domoticz_is_shutter = true; } +#endif + break; + case FUNC_MQTT_INIT: + domoticz_update_timer = 2; + break; + case FUNC_SHOW_SENSOR: + + break; + case FUNC_COMMAND: + result = DecodeCommand(kDomoticzCommands, DomoticzCommand); + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_08_serial_bridge.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_08_serial_bridge.ino" +#ifdef USE_SERIAL_BRIDGE + + + + +#define XDRV_08 8 + +const uint8_t SERIAL_BRIDGE_BUFFER_SIZE = 130; + +const char kSerialBridgeCommands[] PROGMEM = "|" + D_CMND_SSERIALSEND "|" D_CMND_SBAUDRATE; + +void (* const SerialBridgeCommand[])(void) PROGMEM = { + &CmndSSerialSend, &CmndSBaudrate }; + +#include + +TasmotaSerial *SerialBridgeSerial = nullptr; + +unsigned long serial_bridge_polling_window = 0; +char *serial_bridge_buffer = nullptr; +int serial_bridge_in_byte_counter = 0; +bool serial_bridge_active = true; +bool serial_bridge_raw = false; + +void SerialBridgeInput(void) +{ + while (SerialBridgeSerial->available()) { + yield(); + uint8_t serial_in_byte = SerialBridgeSerial->read(); + + if ((serial_in_byte > 127) && !serial_bridge_raw) { + serial_bridge_in_byte_counter = 0; + SerialBridgeSerial->flush(); + return; + } + if (serial_in_byte || serial_bridge_raw) { + + if ((serial_bridge_in_byte_counter < SERIAL_BRIDGE_BUFFER_SIZE -1) && + ((isprint(serial_in_byte) && (128 == Settings.serial_delimiter)) || + ((serial_in_byte != Settings.serial_delimiter) && (128 != Settings.serial_delimiter)) || + serial_bridge_raw)) { + serial_bridge_buffer[serial_bridge_in_byte_counter++] = serial_in_byte; + serial_bridge_polling_window = millis(); + } else { + serial_bridge_polling_window = 0; + break; + } + } + } + + if (serial_bridge_in_byte_counter && (millis() > (serial_bridge_polling_window + SERIAL_POLLING))) { + serial_bridge_buffer[serial_bridge_in_byte_counter] = 0; + char hex_char[(serial_bridge_in_byte_counter * 2) + 2]; + bool assume_json = (!serial_bridge_raw && (serial_bridge_buffer[0] == '{')); + Response_P(PSTR("{\"" D_JSON_SSERIALRECEIVED "\":%s%s%s}"), + (assume_json) ? "" : "\"", + (serial_bridge_raw) ? ToHex_P((unsigned char*)serial_bridge_buffer, serial_bridge_in_byte_counter, hex_char, sizeof(hex_char)) : serial_bridge_buffer, + (assume_json) ? "" : "\""); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SSERIALRECEIVED)); + XdrvRulesProcess(); + serial_bridge_in_byte_counter = 0; + } +} + + + +void SerialBridgeInit(void) +{ + serial_bridge_active = false; + if ((pin[GPIO_SBR_RX] < 99) && (pin[GPIO_SBR_TX] < 99)) { + SerialBridgeSerial = new TasmotaSerial(pin[GPIO_SBR_RX], pin[GPIO_SBR_TX]); + if (SerialBridgeSerial->begin(Settings.sbaudrate * 300)) { + if (SerialBridgeSerial->hardwareSerial()) { + ClaimSerial(); + serial_bridge_buffer = serial_in_buffer; + } else { + serial_bridge_buffer = (char*)(malloc(SERIAL_BRIDGE_BUFFER_SIZE)); + } + serial_bridge_active = true; + SerialBridgeSerial->flush(); + } + } +} + + + + + +void CmndSSerialSend(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 5)) { + serial_bridge_raw = (XdrvMailbox.index > 3); + if (XdrvMailbox.data_len > 0) { + if (1 == XdrvMailbox.index) { + SerialBridgeSerial->write(XdrvMailbox.data, XdrvMailbox.data_len); + SerialBridgeSerial->write("\n"); + } + else if ((2 == XdrvMailbox.index) || (4 == XdrvMailbox.index)) { + SerialBridgeSerial->write(XdrvMailbox.data, XdrvMailbox.data_len); + } + else if (3 == XdrvMailbox.index) { + SerialBridgeSerial->write(Unescape(XdrvMailbox.data, &XdrvMailbox.data_len), XdrvMailbox.data_len); + } + else if (5 == XdrvMailbox.index) { + char *p; + char stemp[3]; + uint8_t code; + + char *codes = RemoveSpace(XdrvMailbox.data); + int size = strlen(XdrvMailbox.data); + + while (size > 1) { + strlcpy(stemp, codes, sizeof(stemp)); + code = strtol(stemp, &p, 16); + SerialBridgeSerial->write(code); + size -= 2; + codes += 2; + } + } + ResponseCmndDone(); + } + } +} + +void CmndSBaudrate(void) +{ + if (XdrvMailbox.payload >= 300) { + XdrvMailbox.payload /= 300; + Settings.sbaudrate = XdrvMailbox.payload; + SerialBridgeSerial->begin(Settings.sbaudrate * 300); + } + ResponseCmndNumber(Settings.sbaudrate * 300); +} + + + + + +bool Xdrv08(uint8_t function) +{ + bool result = false; + + if (serial_bridge_active) { + switch (function) { + case FUNC_LOOP: + if (SerialBridgeSerial) { SerialBridgeInput(); } + break; + case FUNC_PRE_INIT: + SerialBridgeInit(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kSerialBridgeCommands, SerialBridgeCommand); + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_09_timers.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_09_timers.ino" +#ifdef USE_TIMERS +# 39 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_09_timers.ino" +#define XDRV_09 9 + +const char kTimerCommands[] PROGMEM = "|" + D_CMND_TIMER "|" D_CMND_TIMERS +#ifdef USE_SUNRISE + "|" D_CMND_LATITUDE "|" D_CMND_LONGITUDE +#endif + ; + +void (* const TimerCommand[])(void) PROGMEM = { + &CmndTimer, &CmndTimers +#ifdef USE_SUNRISE + , &CmndLatitude, &CmndLongitude +#endif + }; + +uint16_t timer_last_minute = 60; +int8_t timer_window[MAX_TIMERS] = { 0 }; + +#ifdef USE_SUNRISE +# 67 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_09_timers.ino" +const float pi2 = TWO_PI; +const float pi = PI; +const float RAD = DEG_TO_RAD; + +float JulianischesDatum(void) +{ + + int Gregor; + int Jahr = RtcTime.year; + int Monat = RtcTime.month; + int Tag = RtcTime.day_of_month; + + if (Monat <= 2) { + Monat += 12; + Jahr -= 1; + } + Gregor = (Jahr / 400) - (Jahr / 100) + (Jahr / 4); + return 2400000.5f + 365.0f*Jahr - 679004.0f + Gregor + (int)(30.6001f * (Monat +1)) + Tag + 0.5f; +} + +float InPi(float x) +{ + int n = (int)(x / pi2); + x = x - n*pi2; + if (x < 0) x += pi2; + return x; +} + +float eps(float T) +{ + + return RAD * (23.43929111f + (-46.8150f*T - 0.00059f*T*T + 0.001813f*T*T*T)/3600.0f); +} + +float BerechneZeitgleichung(float *DK,float T) +{ + float RA_Mittel = 18.71506921f + 2400.0513369f*T +(2.5862e-5f - 1.72e-9f*T)*T*T; + float M = InPi(pi2 * (0.993133f + 99.997361f*T)); + float L = InPi(pi2 * (0.7859453f + M/pi2 + (6893.0f*sinf(M)+72.0f*sinf(2.0f*M)+6191.2f*T) / 1296.0e3f)); + float e = eps(T); + float RA = atanf(tanf(L)*cosf(e)); + if (RA < 0.0) RA += pi; + if (L > pi) RA += pi; + RA = 24.0*RA/pi2; + *DK = asinf(sinf(e)*sinf(L)); + + RA_Mittel = 24.0f * InPi(pi2*RA_Mittel/24.0f)/pi2; + float dRA = RA_Mittel - RA; + if (dRA < -12.0f) dRA += 24.0f; + if (dRA > 12.0f) dRA -= 24.0f; + dRA = dRA * 1.0027379f; + return dRA; +} + +void DuskTillDawn(uint8_t *hour_up,uint8_t *minute_up, uint8_t *hour_down, uint8_t *minute_down) +{ + float JD2000 = 2451545.0f; + float JD = JulianischesDatum(); + float T = (JD - JD2000) / 36525.0f; + float DK; + + + + + + + + float h = SUNRISE_DAWN_ANGLE *RAD; + float B = (((float)Settings.latitude)/1000000) * RAD; + float GeographischeLaenge = ((float)Settings.longitude)/1000000; + + + + float Zeitzone = ((float)Rtc.time_timezone) / 60; + float Zeitgleichung = BerechneZeitgleichung(&DK, T); + float Zeitdifferenz = 12.0f*acosf((sinf(h) - sinf(B)*sinf(DK)) / (cosf(B)*cosf(DK)))/pi; + float AufgangOrtszeit = 12.0f - Zeitdifferenz - Zeitgleichung; + float UntergangOrtszeit = 12.0f + Zeitdifferenz - Zeitgleichung; + float AufgangWeltzeit = AufgangOrtszeit - GeographischeLaenge / 15.0f; + float UntergangWeltzeit = UntergangOrtszeit - GeographischeLaenge / 15.0f; + float Aufgang = AufgangWeltzeit + Zeitzone; + if (Aufgang < 0.0f) { + Aufgang += 24.0f; + } else { + if (Aufgang >= 24.0f) Aufgang -= 24.0f; + } + float Untergang = UntergangWeltzeit + Zeitzone; + if (Untergang < 0.0f) { + Untergang += 24.0f; + } else { + if (Untergang >= 24.0f) Untergang -= 24.0f; + } + int AufgangMinuten = (int)(60.0f*(Aufgang - (int)Aufgang)+0.5f); + int AufgangStunden = (int)Aufgang; + if (AufgangMinuten >= 60.0f) { + AufgangMinuten -= 60.0f; + AufgangStunden++; + } else { + if (AufgangMinuten < 0.0f) { + AufgangMinuten += 60.0f; + AufgangStunden--; + if (AufgangStunden < 0.0f) AufgangStunden += 24.0f; + } + } + int UntergangMinuten = (int)(60.0f*(Untergang - (int)Untergang)+0.5f); + int UntergangStunden = (int)Untergang; + if (UntergangMinuten >= 60.0f) { + UntergangMinuten -= 60.0f; + UntergangStunden++; + } else { + if (UntergangMinuten<0) { + UntergangMinuten += 60.0f; + UntergangStunden--; + if (UntergangStunden < 0.0f) UntergangStunden += 24.0f; + } + } + *hour_up = AufgangStunden; + *minute_up = AufgangMinuten; + *hour_down = UntergangStunden; + *minute_down = UntergangMinuten; +} + +void ApplyTimerOffsets(Timer *duskdawn) +{ + uint8_t hour[2]; + uint8_t minute[2]; + Timer stored = (Timer)*duskdawn; + + + DuskTillDawn(&hour[0], &minute[0], &hour[1], &minute[1]); + uint8_t mode = (duskdawn->mode -1) &1; + duskdawn->time = (hour[mode] *60) + minute[mode]; + + + uint16_t timeBuffer; + if ((uint16_t)stored.time > 719) { + + timeBuffer = (uint16_t)stored.time - 720; + + if (timeBuffer > (uint16_t)duskdawn->time) { + timeBuffer = 1440 - (timeBuffer - (uint16_t)duskdawn->time); + duskdawn->days = duskdawn->days >> 1; + duskdawn->days |= (stored.days << 6); + } else { + timeBuffer = (uint16_t)duskdawn->time - timeBuffer; + } + } else { + + timeBuffer = (uint16_t)duskdawn->time + (uint16_t)stored.time; + + if (timeBuffer > 1440) { + timeBuffer -= 1440; + duskdawn->days = duskdawn->days << 1; + duskdawn->days |= (stored.days >> 6); + } + } + duskdawn->time = timeBuffer; +} + +String GetSun(uint32_t dawn) +{ + char stime[6]; + + uint8_t hour[2]; + uint8_t minute[2]; + + DuskTillDawn(&hour[0], &minute[0], &hour[1], &minute[1]); + dawn &= 1; + snprintf_P(stime, sizeof(stime), PSTR("%02d:%02d"), hour[dawn], minute[dawn]); + return String(stime); +} + +uint16_t SunMinutes(uint32_t dawn) +{ + uint8_t hour[2]; + uint8_t minute[2]; + + DuskTillDawn(&hour[0], &minute[0], &hour[1], &minute[1]); + dawn &= 1; + return (hour[dawn] *60) + minute[dawn]; +} + +#endif + + + +void TimerSetRandomWindow(uint32_t index) +{ + timer_window[index] = 0; + if (Settings.timer[index].window) { + timer_window[index] = (random(0, (Settings.timer[index].window << 1) +1)) - Settings.timer[index].window; + } +} + +void TimerSetRandomWindows(void) +{ + for (uint32_t i = 0; i < MAX_TIMERS; i++) { TimerSetRandomWindow(i); } +} + +void TimerEverySecond(void) +{ + if (RtcTime.valid) { + if (!RtcTime.hour && !RtcTime.minute && !RtcTime.second) { TimerSetRandomWindows(); } + if (Settings.flag3.timers_enable && + (uptime > 60) && (RtcTime.minute != timer_last_minute)) { + timer_last_minute = RtcTime.minute; + int32_t time = (RtcTime.hour *60) + RtcTime.minute; + uint8_t days = 1 << (RtcTime.day_of_week -1); + + for (uint32_t i = 0; i < MAX_TIMERS; i++) { + + Timer xtimer = Settings.timer[i]; +#ifdef USE_SUNRISE + if ((1 == xtimer.mode) || (2 == xtimer.mode)) { + ApplyTimerOffsets(&xtimer); + } +#endif + if (xtimer.arm) { + int32_t set_time = xtimer.time + timer_window[i]; + if (set_time < 0) { + set_time = abs(timer_window[i]); + } + if (set_time > 1439) { + set_time = xtimer.time - abs(timer_window[i]); + } + if (set_time > 1439) { set_time = 1439; } + + DEBUG_DRIVER_LOG(PSTR("TIM: Timer %d, Time %d, Window %d, SetTime %d"), i +1, xtimer.time, timer_window[i], set_time); + + if (time == set_time) { + if (xtimer.days & days) { + Settings.timer[i].arm = xtimer.repeat; +#if defined(USE_RULES) || defined(USE_SCRIPT) + if (POWER_BLINK == xtimer.power) { + Response_P(PSTR("{\"Clock\":{\"Timer\":%d}}"), i +1); + XdrvRulesProcess(); + } else +#endif + if (devices_present) { ExecuteCommandPower(xtimer.device +1, xtimer.power, SRC_TIMER); } + } + } + } + } + } + } +} + +void PrepShowTimer(uint32_t index) +{ + Timer xtimer = Settings.timer[index -1]; + + char days[8] = { 0 }; + for (uint32_t i = 0; i < 7; i++) { + uint8_t mask = 1 << i; + snprintf(days, sizeof(days), "%s%d", days, ((xtimer.days & mask) > 0)); + } + + char soutput[80]; + soutput[0] = '\0'; + if (devices_present) { + snprintf_P(soutput, sizeof(soutput), PSTR(",\"" D_JSON_TIMER_OUTPUT "\":%d"), xtimer.device +1); + } +#ifdef USE_SUNRISE + char sign[2] = { 0 }; + int16_t hour = xtimer.time / 60; + if ((1 == xtimer.mode) || (2 == xtimer.mode)) { + if (hour > 11) { + hour -= 12; + sign[0] = '-'; + } + } + ResponseAppend_P(PSTR("\"" D_CMND_TIMER "%d\":{\"" D_JSON_TIMER_ARM "\":%d,\"" D_JSON_TIMER_MODE "\":%d,\"" D_JSON_TIMER_TIME "\":\"%s%02d:%02d\",\"" D_JSON_TIMER_WINDOW "\":%d,\"" D_JSON_TIMER_DAYS "\":\"%s\",\"" D_JSON_TIMER_REPEAT "\":%d%s,\"" D_JSON_TIMER_ACTION "\":%d}"), + index, xtimer.arm, xtimer.mode, sign, hour, xtimer.time % 60, xtimer.window, days, xtimer.repeat, soutput, xtimer.power); +#else + ResponseAppend_P(PSTR("\"" D_CMND_TIMER "%d\":{\"" D_JSON_TIMER_ARM "\":%d,\"" D_JSON_TIMER_TIME "\":\"%02d:%02d\",\"" D_JSON_TIMER_WINDOW "\":%d,\"" D_JSON_TIMER_DAYS "\":\"%s\",\"" D_JSON_TIMER_REPEAT "\":%d%s,\"" D_JSON_TIMER_ACTION "\":%d}"), + index, xtimer.arm, xtimer.time / 60, xtimer.time % 60, xtimer.window, days, xtimer.repeat, soutput, xtimer.power); +#endif +} + + + + + +void CmndTimer(void) +{ + uint32_t index = XdrvMailbox.index; + if ((index > 0) && (index <= MAX_TIMERS)) { + uint32_t error = 0; + if (XdrvMailbox.data_len) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAX_TIMERS)) { + if (XdrvMailbox.payload == 0) { + Settings.timer[index -1].data = 0; + } else { + Settings.timer[index -1].data = Settings.timer[XdrvMailbox.payload -1].data; + } + } else { + +#if defined(USE_RULES)==0 && defined(USE_SCRIPT)==0 + if (devices_present) { +#endif + char dataBufUc[XdrvMailbox.data_len + 1]; + UpperCase(dataBufUc, XdrvMailbox.data); + StaticJsonBuffer<256> jsonBuffer; + JsonObject& root = jsonBuffer.parseObject(dataBufUc); + if (!root.success()) { + Response_P(PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_INVALID_JSON "\"}"), index); + error = 1; + } + else { + char parm_uc[10]; + index--; + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_ARM))].success()) { + Settings.timer[index].arm = (root[parm_uc] != 0); + } +#ifdef USE_SUNRISE + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_MODE))].success()) { + Settings.timer[index].mode = (uint8_t)root[parm_uc] & 0x03; + } +#endif + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_TIME))].success()) { + uint16_t itime = 0; + int8_t value = 0; + uint8_t sign = 0; + char time_str[10]; + + strlcpy(time_str, root[parm_uc], sizeof(time_str)); + const char *substr = strtok(time_str, ":"); + if (substr != nullptr) { + if (strchr(substr, '-')) { + sign = 1; + substr++; + } + value = atoi(substr); + if (sign) { value += 12; } + if (value > 23) { value = 23; } + itime = value * 60; + substr = strtok(nullptr, ":"); + if (substr != nullptr) { + value = atoi(substr); + if (value < 0) { value = 0; } + if (value > 59) { value = 59; } + itime += value; + } + } + Settings.timer[index].time = itime; + } + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_WINDOW))].success()) { + Settings.timer[index].window = (uint8_t)root[parm_uc] & 0x0F; + TimerSetRandomWindow(index); + } + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_DAYS))].success()) { + + Settings.timer[index].days = 0; + const char *tday = root[parm_uc]; + uint8_t i = 0; + char ch = *tday++; + while ((ch != '\0') && (i < 7)) { + if (ch == '-') { ch = '0'; } + uint8_t mask = 1 << i++; + Settings.timer[index].days |= (ch == '0') ? 0 : mask; + ch = *tday++; + } + } + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_REPEAT))].success()) { + Settings.timer[index].repeat = (root[parm_uc] != 0); + } + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_OUTPUT))].success()) { + uint8_t device = ((uint8_t)root[parm_uc] -1) & 0x0F; + Settings.timer[index].device = (device < devices_present) ? device : 0; + } + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_ACTION))].success()) { + uint8_t action = (uint8_t)root[parm_uc] & 0x03; + Settings.timer[index].power = (devices_present) ? action : 3; + } + + index++; + } + +#if defined(USE_RULES)==0 && defined(USE_SCRIPT)==0 + } else { + Response_P(PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_TIMER_NO_DEVICE "\"}"), index); + error = 1; + } +#endif + } + } + if (!error) { + Response_P(PSTR("{")); + PrepShowTimer(index); + ResponseJsonEnd(); + } + } +} + +void CmndTimers(void) +{ + if (XdrvMailbox.data_len) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.flag3.timers_enable = XdrvMailbox.payload; + } + if (XdrvMailbox.payload == 2) { + Settings.flag3.timers_enable = !Settings.flag3.timers_enable; + } + } + + ResponseCmndStateText(Settings.flag3.timers_enable); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, XdrvMailbox.command); + + uint32_t jsflg = 0; + uint32_t lines = 1; + for (uint32_t i = 0; i < MAX_TIMERS; i++) { + if (!jsflg) { + Response_P(PSTR("{\"" D_CMND_TIMERS "%d\":{"), lines++); + } else { + ResponseAppend_P(PSTR(",")); + } + jsflg++; + PrepShowTimer(i +1); + if (jsflg > 3) { + ResponseJsonEndEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_TIMERS)); + jsflg = 0; + } + } + mqtt_data[0] = '\0'; +} + +#ifdef USE_SUNRISE +void CmndLongitude(void) +{ + if (XdrvMailbox.data_len) { + Settings.longitude = (int)(CharToFloat(XdrvMailbox.data) *1000000); + } + ResponseCmndFloat((float)(Settings.longitude) /1000000, 6); +} + +void CmndLatitude(void) +{ + if (XdrvMailbox.data_len) { + Settings.latitude = (int)(CharToFloat(XdrvMailbox.data) *1000000); + } + ResponseCmndFloat((float)(Settings.latitude) /1000000, 6); +} +#endif + + + + + +#ifdef USE_WEBSERVER +#ifdef USE_TIMERS_WEB + +#define WEB_HANDLE_TIMER "tm" + +const char S_CONFIGURE_TIMER[] PROGMEM = D_CONFIGURE_TIMER; + +const char HTTP_BTN_MENU_TIMER[] PROGMEM = + "

"; + +const char HTTP_TIMER_SCRIPT1[] PROGMEM = + "var pt=[],ct=99;" + "function ce(i,q){" + "var o=document.createElement('option');" + "o.textContent=i;" + "q.appendChild(o);" + "}"; +#ifdef USE_SUNRISE +const char HTTP_TIMER_SCRIPT2[] PROGMEM = + "function gt(){" + "var m,p,q;" + "m=qs('input[name=\"rd\"]:checked').value;" + "p=pt[ct]&0x7FF;" + "if(m==0){" + "so(0);" + "q=Math.floor(p/60);if(q<10){q='0'+q;}qs('#ho').value=q;" + "q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" + "}" + "if((m==1)||(m==2)){" + "so(1);" + "q=Math.floor(p/60);" + "if(q>=12){q-=12;qs('#dr').selectedIndex=1;}" + "else{qs('#dr').selectedIndex=0;}" + "if(q<10){q='0'+q;}qs('#ho').value=q;" + "q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" + "}" + "}" + "function so(b){" + "o=qs('#ho');" + "e=o.childElementCount;" + "if(b==1){" + "qs('#dr').style.visibility='';" + "if(e>12){for(i=12;i<=23;i++){o.removeChild(o.lastElementChild);}}" + "}else{" + "qs('#dr').style.visibility='hidden';" + "if(e<23){for(i=12;i<=23;i++){ce(i,o);}}" + "}" + "}"; +#endif +const char HTTP_TIMER_SCRIPT3[] PROGMEM = + "function st(){" + "var i,l,m,n,p,s;" + "m=0;s=0;" + "n=1<<31;if(eb('a0').checked){s|=n;}" + "n=1<<15;if(eb('r0').checked){s|=n;}" + "for(i=0;i<7;i++){n=1<<(16+i);if(eb('w'+i).checked){s|=n;}}" +#ifdef USE_SUNRISE + "m=qs('input[name=\"rd\"]:checked').value;" + "s|=(qs('input[name=\"rd\"]:checked').value<<29);" +#endif + "if(%d>0){" + "i=qs('#d1').selectedIndex;if(i>=0){s|=(i<<23);}" + "s|=(qs('#p1').selectedIndex<<27);" + "}else{" + "s|=3<<27;" + "}" + "l=((qs('#ho').selectedIndex*60)+qs('#mi').selectedIndex)&0x7FF;" + "if(m==0){s|=l;}" +#ifdef USE_SUNRISE + "if((m==1)||(m==2)){" + "if(qs('#dr').selectedIndex>0){if(l>0){l+=720;}}" + "s|=l&0x7FF;" + "}" +#endif + "s|=((qs('#mw').selectedIndex)&0x0F)<<11;" + "pt[ct]=s;" + "eb('t0').value=pt.join();" + "}"; +const char HTTP_TIMER_SCRIPT4[] PROGMEM = + "function ot(t,e){" + "var i,n,o,p,q,s;" + "if(ct<99){st();}" + "ct=t;" + "o=document.getElementsByClassName('tl');" + "for(i=0;i>29)&3;eb('b'+p).checked=1;" + "gt();" +#else + "p=s&0x7FF;" + "q=Math.floor(p/60);if(q<10){q='0'+q;}qs('#ho').value=q;" + "q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" +#endif + "q=(s>>11)&0xF;if(q<10){q='0'+q;}qs('#mw').value=q;" + "for(i=0;i<7;i++){p=(s>>(16+i))&1;eb('w'+i).checked=p;}" + "if(%d>0){" + "p=(s>>23)&0xF;qs('#d1').value=p+1;" + "p=(s>>27)&3;qs('#p1').selectedIndex=p;" + "}" + "p=(s>>15)&1;eb('r0').checked=p;" + "p=(s>>31)&1;eb('a0').checked=p;" + "}"; +const char HTTP_TIMER_SCRIPT5[] PROGMEM = + "function it(){" + "var b,i,o,s;" + "pt=eb('t0').value.split(',').map(Number);" + "s='';" + "for(i=0;i<%d;i++){" + "b='';" + "if(0==i){b=\" id='dP'\";}" + "s+=\"\"" + "}" + "eb('bt').innerHTML=s;" + "if(%d>0){" + "eb('oa').innerHTML=\"" D_TIMER_OUTPUT " " D_TIMER_ACTION " \";" + "o=qs('#p1');ce('" D_OFF "',o);ce('" D_ON "',o);ce('" D_TOGGLE "',o);" +#if defined(USE_RULES) || defined(USE_SCRIPT) + "ce('" D_RULE "',o);" +#else + "ce('" D_BLINK "',o);" +#endif + "}else{" + "eb('oa').innerHTML=\"" D_TIMER_ACTION " " D_RULE "\";" + "}"; +const char HTTP_TIMER_SCRIPT6[] PROGMEM = +#ifdef USE_SUNRISE + "o=qs('#dr');ce('+',o);ce('-',o);" +#endif + "o=qs('#ho');for(i=0;i<=23;i++){ce((i<10)?('0'+i):i,o);}" + "o=qs('#mi');for(i=0;i<=59;i++){ce((i<10)?('0'+i):i,o);}" + "o=qs('#mw');for(i=0;i<=15;i++){ce((i<10)?('0'+i):i,o);}" + "o=qs('#d1');for(i=0;i<%d;i++){ce(i+1,o);}" + "var a='" D_DAY3LIST "';" + + + + "s='';for(i=0;i<7;i++){s+=\" \"}" + + "eb('ds').innerHTML=s;" + "eb('dP').click();" + "}" + "wl(it);"; +const char HTTP_TIMER_STYLE[] PROGMEM = + ".tl{float:left;border-radius:0;border:1px solid #%06x;padding:1px;width:6.25%%;}"; +const char HTTP_FORM_TIMER1[] PROGMEM = + "
" + " " D_TIMER_PARAMETERS " " + "
" + "



" + "



" + "

" + "
" + " " + "" + "

" + "
"; +#ifdef USE_SUNRISE +const char HTTP_FORM_TIMER3[] PROGMEM = + "
" + "
" + "
" + "
" + "
" + "

" + "" + " "; +#else +const char HTTP_FORM_TIMER3[] PROGMEM = + "" D_TIMER_TIME " "; +#endif +const char HTTP_FORM_TIMER4[] PROGMEM = + "" + " " D_HOUR_MINUTE_SEPARATOR " " + "" + " +/- " + "" + "

" + "
"; + +void HandleTimerConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_TIMER); + + if (Webserver->hasArg("save")) { + TimerSaveSettings(); + HandleConfiguration(); + return; + } + + WSContentStart_P(S_CONFIGURE_TIMER); + WSContentSend_P(HTTP_TIMER_SCRIPT1); +#ifdef USE_SUNRISE + WSContentSend_P(HTTP_TIMER_SCRIPT2); +#endif + WSContentSend_P(HTTP_TIMER_SCRIPT3, devices_present); + WSContentSend_P(HTTP_TIMER_SCRIPT4, WebColor(COL_TIMER_TAB_BACKGROUND), WebColor(COL_TIMER_TAB_TEXT), WebColor(COL_FORM), WebColor(COL_TEXT), devices_present); + WSContentSend_P(HTTP_TIMER_SCRIPT5, MAX_TIMERS, devices_present); + WSContentSend_P(HTTP_TIMER_SCRIPT6, devices_present); + WSContentSendStyle_P(HTTP_TIMER_STYLE, WebColor(COL_FORM)); + WSContentSend_P(HTTP_FORM_TIMER1, (Settings.flag3.timers_enable) ? " checked" : ""); + for (uint32_t i = 0; i < MAX_TIMERS; i++) { + WSContentSend_P(PSTR("%s%u"), (i > 0) ? "," : "", Settings.timer[i].data); + } + WSContentSend_P(HTTP_FORM_TIMER2); +#ifdef USE_SUNRISE + WSContentSend_P(HTTP_FORM_TIMER3, 100 + (strlen(D_SUNSET) *12), GetSun(0).c_str(), GetSun(1).c_str()); +#else + WSContentSend_P(HTTP_FORM_TIMER3); +#endif + WSContentSend_P(HTTP_FORM_TIMER4); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void TimerSaveSettings(void) +{ + char tmp[MAX_TIMERS *12]; + char message[LOGSZ]; + Timer timer; + + Settings.flag3.timers_enable = Webserver->hasArg("e0"); + WebGetArg("t0", tmp, sizeof(tmp)); + char *p = tmp; + snprintf_P(message, sizeof(message), PSTR(D_LOG_MQTT D_CMND_TIMERS " %d"), Settings.flag3.timers_enable); + for (uint32_t i = 0; i < MAX_TIMERS; i++) { + timer.data = strtol(p, &p, 10); + p++; + if (timer.time < 1440) { + bool flag = (timer.window != Settings.timer[i].window); + Settings.timer[i].data = timer.data; + if (flag) TimerSetRandomWindow(i); + } + snprintf_P(message, sizeof(message), PSTR("%s,0x%08X"), message, Settings.timer[i].data); + } + AddLog_P(LOG_LEVEL_DEBUG, message); +} +#endif +#endif + + + + + +bool Xdrv09(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_PRE_INIT: + TimerSetRandomWindows(); + break; +#ifdef USE_WEBSERVER +#ifdef USE_TIMERS_WEB + case FUNC_WEB_ADD_BUTTON: +#if defined(USE_RULES) || defined(USE_SCRIPT) + WSContentSend_P(HTTP_BTN_MENU_TIMER); +#else + if (devices_present) { WSContentSend_P(HTTP_BTN_MENU_TIMER); } +#endif + break; + case FUNC_WEB_ADD_HANDLER: + Webserver->on("/" WEB_HANDLE_TIMER, HandleTimerConfiguration); + break; +#endif +#endif + case FUNC_EVERY_SECOND: + TimerEverySecond(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kTimerCommands, TimerCommand); + break; + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +#ifdef USE_RULES +#ifndef USE_SCRIPT +# 67 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +#define XDRV_10 10 + +#define D_CMND_RULE "Rule" +#define D_CMND_RULETIMER "RuleTimer" +#define D_CMND_EVENT "Event" +#define D_CMND_VAR "Var" +#define D_CMND_MEM "Mem" +#define D_CMND_ADD "Add" +#define D_CMND_SUB "Sub" +#define D_CMND_MULT "Mult" +#define D_CMND_SCALE "Scale" +#define D_CMND_CALC_RESOLUTION "CalcRes" +#define D_CMND_SUBSCRIBE "Subscribe" +#define D_CMND_UNSUBSCRIBE "Unsubscribe" +#define D_CMND_IF "If" + +#define D_JSON_INITIATED "Initiated" + +#define COMPARE_OPERATOR_NONE -1 +#define COMPARE_OPERATOR_EQUAL 0 +#define COMPARE_OPERATOR_BIGGER 1 +#define COMPARE_OPERATOR_SMALLER 2 +#define COMPARE_OPERATOR_EXACT_DIVISION 3 +#define COMPARE_OPERATOR_NUMBER_EQUAL 4 +#define COMPARE_OPERATOR_NOT_EQUAL 5 +#define COMPARE_OPERATOR_BIGGER_EQUAL 6 +#define COMPARE_OPERATOR_SMALLER_EQUAL 7 +#define MAXIMUM_COMPARE_OPERATOR COMPARE_OPERATOR_SMALLER_EQUAL +const char kCompareOperators[] PROGMEM = "=\0>\0<\0|\0==!=>=<="; + +#ifdef USE_EXPRESSION + #include + + const char kExpressionOperators[] PROGMEM = "+-*/%^\0"; + #define EXPRESSION_OPERATOR_ADD 0 + #define EXPRESSION_OPERATOR_SUBTRACT 1 + #define EXPRESSION_OPERATOR_MULTIPLY 2 + #define EXPRESSION_OPERATOR_DIVIDEDBY 3 + #define EXPRESSION_OPERATOR_MODULO 4 + #define EXPRESSION_OPERATOR_POWER 5 + + const uint8_t kExpressionOperatorsPriorities[] PROGMEM = {1, 1, 2, 2, 3, 4}; + #define MAX_EXPRESSION_OPERATOR_PRIORITY 4 + + + #define LOGIC_OPERATOR_AND 1 + #define LOGIC_OPERATOR_OR 2 + + #define IF_BLOCK_INVALID -1 + #define IF_BLOCK_ANY 0 + #define IF_BLOCK_ELSEIF 1 + #define IF_BLOCK_ELSE 2 + #define IF_BLOCK_ENDIF 3 +#endif + +const char kRulesCommands[] PROGMEM = "|" + D_CMND_RULE "|" D_CMND_RULETIMER "|" D_CMND_EVENT "|" D_CMND_VAR "|" D_CMND_MEM "|" + D_CMND_ADD "|" D_CMND_SUB "|" D_CMND_MULT "|" D_CMND_SCALE "|" D_CMND_CALC_RESOLUTION +#ifdef SUPPORT_MQTT_EVENT + "|" D_CMND_SUBSCRIBE "|" D_CMND_UNSUBSCRIBE +#endif +#ifdef SUPPORT_IF_STATEMENT + "|" D_CMND_IF +#endif + ; + +void (* const RulesCommand[])(void) PROGMEM = { + &CmndRule, &CmndRuleTimer, &CmndEvent, &CmndVariable, &CmndMemory, + &CmndAddition, &CmndSubtract, &CmndMultiply, &CmndScale, &CmndCalcResolution +#ifdef SUPPORT_MQTT_EVENT + , &CmndSubscribe, &CmndUnsubscribe +#endif +#ifdef SUPPORT_IF_STATEMENT + , &CmndIf +#endif + }; + +#ifdef SUPPORT_MQTT_EVENT + #include + typedef struct { + String Event; + String Topic; + String Key; + } MQTT_Subscription; + LinkedList subscriptions; +#endif + +struct RULES { + String event_value; + unsigned long timer[MAX_RULE_TIMERS] = { 0 }; + uint32_t triggers[MAX_RULE_SETS] = { 0 }; + uint8_t trigger_count[MAX_RULE_SETS] = { 0 }; + + long new_power = -1; + long old_power = -1; + long old_dimm = -1; + + uint16_t last_minute = 60; + uint16_t vars_event = 0; + uint8_t mems_event = 0; + bool teleperiod = false; + + char event_data[100]; +} Rules; + +char rules_vars[MAX_RULE_VARS][33] = {{ 0 }}; + +#if (MAX_RULE_VARS>16) +#error MAX_RULE_VARS is bigger than 16 +#endif +#if (MAX_RULE_MEMS>16) +#error MAX_RULE_MEMS is bigger than 16 +#endif + + + +bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) +{ + + + + + bool match = false; + char stemp[10]; + + + int pos = rule.indexOf('#'); + if (pos == -1) { return false; } + + String rule_task = rule.substring(0, pos); + if (Rules.teleperiod) { + int ppos = rule_task.indexOf("TELE-"); + if (ppos == -1) { return false; } + rule_task = rule.substring(5, pos); + } + + String rule_expr = rule.substring(pos +1); + String rule_name, rule_param; + int8_t compareOperator = parseCompareExpression(rule_expr, rule_name, rule_param); + + char rule_svalue[80] = { 0 }; + float rule_value = 0; + if (compareOperator != COMPARE_OPERATOR_NONE) { + for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%%VAR%d%%"), i +1); + if (rule_param.startsWith(stemp)) { + rule_param = rules_vars[i]; + break; + } + } + for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%%MEM%d%%"), i +1); + if (rule_param.startsWith(stemp)) { + rule_param = SettingsText(SET_MEM1 + i); + break; + } + } + snprintf_P(stemp, sizeof(stemp), PSTR("%%TIME%%")); + if (rule_param.startsWith(stemp)) { + rule_param = String(MinutesPastMidnight()); + } + snprintf_P(stemp, sizeof(stemp), PSTR("%%UPTIME%%")); + if (rule_param.startsWith(stemp)) { + rule_param = String(MinutesUptime()); + } + snprintf_P(stemp, sizeof(stemp), PSTR("%%TIMESTAMP%%")); + if (rule_param.startsWith(stemp)) { + rule_param = GetDateAndTime(DT_LOCAL).c_str(); + } +#if defined(USE_TIMERS) && defined(USE_SUNRISE) + snprintf_P(stemp, sizeof(stemp), PSTR("%%SUNRISE%%")); + if (rule_param.startsWith(stemp)) { + rule_param = String(SunMinutes(0)); + } + snprintf_P(stemp, sizeof(stemp), PSTR("%%SUNSET%%")); + if (rule_param.startsWith(stemp)) { + rule_param = String(SunMinutes(1)); + } +#endif + rule_param.toUpperCase(); + strlcpy(rule_svalue, rule_param.c_str(), sizeof(rule_svalue)); + + int temp_value = GetStateNumber(rule_svalue); + if (temp_value > -1) { + rule_value = temp_value; + } else { + rule_value = CharToFloat((char*)rule_svalue); + } + } + + + int rule_name_idx = 0; + if ((pos = rule_name.indexOf("[")) > 0) { + rule_name_idx = rule_name.substring(pos +1).toInt(); + if ((rule_name_idx < 1) || (rule_name_idx > 6)) { + rule_name_idx = 1; + } + rule_name = rule_name.substring(0, pos); + } + + StaticJsonBuffer<1024> jsonBuf; + JsonObject &root = jsonBuf.parseObject(event); + if (!root.success()) { return false; } + if (!root[rule_task].success()) { return false; } + + JsonObject &obj1 = root[rule_task]; + JsonObject *obj = &obj1; + String subtype; + uint32_t i = 0; + while ((pos = rule_name.indexOf("#")) > 0) { + subtype = rule_name.substring(0, pos); + if (!(*obj)[subtype].success()) { return false; } + JsonObject &obj2 = (*obj)[subtype]; + obj = &obj2; + rule_name = rule_name.substring(pos +1); + if (i++ > 10) { return false; } + } + if (!(*obj)[rule_name].success()) { return false; } + const char* str_value; + if (rule_name_idx) { + str_value = (*obj)[rule_name][rule_name_idx -1]; + } else { + str_value = (*obj)[rule_name]; + } + + + + + Rules.event_value = str_value; + + + float value = 0; + if (str_value) { + value = CharToFloat((char*)str_value); + int int_value = int(value); + int int_rule_value = int(rule_value); + switch (compareOperator) { + case COMPARE_OPERATOR_EXACT_DIVISION: + match = (int_rule_value && (int_value % int_rule_value) == 0); + break; + case COMPARE_OPERATOR_EQUAL: + match = (!strcasecmp(str_value, rule_svalue)); + break; + case COMPARE_OPERATOR_BIGGER: + match = (value > rule_value); + break; + case COMPARE_OPERATOR_SMALLER: + match = (value < rule_value); + break; + case COMPARE_OPERATOR_NUMBER_EQUAL: + match = (value == rule_value); + break; + case COMPARE_OPERATOR_NOT_EQUAL: + match = (value != rule_value); + break; + case COMPARE_OPERATOR_BIGGER_EQUAL: + match = (value >= rule_value); + break; + case COMPARE_OPERATOR_SMALLER_EQUAL: + match = (value <= rule_value); + break; + default: + match = true; + } + } else match = true; + + + + + if (bitRead(Settings.rule_once, rule_set)) { + if (match) { + if (!bitRead(Rules.triggers[rule_set], Rules.trigger_count[rule_set])) { + bitSet(Rules.triggers[rule_set], Rules.trigger_count[rule_set]); + } else { + match = false; + } + } else { + bitClear(Rules.triggers[rule_set], Rules.trigger_count[rule_set]); + } + } + + + + return match; +} +# 368 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +int8_t parseCompareExpression(String &expr, String &leftExpr, String &rightExpr) +{ + char compare_operator[3]; + int8_t compare = COMPARE_OPERATOR_NONE; + leftExpr = expr; + int position; + for (int8_t i = MAXIMUM_COMPARE_OPERATOR; i >= 0; i--) { + snprintf_P(compare_operator, sizeof(compare_operator), kCompareOperators + (i *2)); + if ((position = expr.indexOf(compare_operator)) > 0) { + compare = i; + leftExpr = expr.substring(0, position); + leftExpr.trim(); + rightExpr = expr.substring(position + strlen(compare_operator)); + rightExpr.trim(); + break; + } + } + return compare; +} + +void RulesVarReplace(String &commands, const String &sfind, const String &replace) +{ + + + + char *find = (char*)sfind.c_str(); + uint32_t flen = strlen(find); + + String ucommand = commands; + ucommand.toUpperCase(); + char *read_from = (char*)ucommand.c_str(); + char *write_to = (char*)commands.c_str(); + char *found_at; + while ((found_at = strstr(read_from, find)) != nullptr) { + write_to += (found_at - read_from); + memmove_P(write_to, find, flen); + write_to += flen; + read_from = found_at + flen; + } + + commands.replace(find, replace); +} + + + +bool RuleSetProcess(uint8_t rule_set, String &event_saved) +{ + bool serviced = false; + char stemp[10]; + + delay(0); + + + + String rules = Settings.rules[rule_set]; + + Rules.trigger_count[rule_set] = 0; + int plen = 0; + int plen2 = 0; + bool stop_all_rules = false; + while (true) { + rules = rules.substring(plen); + rules.trim(); + if (!rules.length()) { return serviced; } + + String rule = rules; + rule.toUpperCase(); + if (!rule.startsWith("ON ")) { return serviced; } + + int pevt = rule.indexOf(" DO "); + if (pevt == -1) { return serviced; } + String event_trigger = rule.substring(3, pevt); + + plen = rule.indexOf(" ENDON"); + plen2 = rule.indexOf(" BREAK"); + if ((plen == -1) && (plen2 == -1)) { return serviced; } + + if (plen == -1) { plen = 9999; } + if (plen2 == -1) { plen2 = 9999; } + plen = tmin(plen, plen2); + + String commands = rules.substring(pevt +4, plen); + Rules.event_value = ""; + String event = event_saved; + + + + if (RulesRuleMatch(rule_set, event, event_trigger)) { + if (plen == plen2) { stop_all_rules = true; } + commands.trim(); + String ucommand = commands; + ucommand.toUpperCase(); + + + + if ((ucommand.indexOf("IF ") == -1) && + (ucommand.indexOf("EVENT ") != -1) && + (ucommand.indexOf("BACKLOG ") == -1)) { + commands = "backlog " + commands; + } + + RulesVarReplace(commands, F("%VALUE%"), Rules.event_value); + for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%%VAR%d%%"), i +1); + RulesVarReplace(commands, stemp, rules_vars[i]); + } + for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%%MEM%d%%"), i +1); + RulesVarReplace(commands, stemp, SettingsText(SET_MEM1 +i)); + } + RulesVarReplace(commands, F("%TIME%"), String(MinutesPastMidnight())); + RulesVarReplace(commands, F("%UTCTIME%"), String(UtcTime())); + RulesVarReplace(commands, F("%UPTIME%"), String(MinutesUptime())); + RulesVarReplace(commands, F("%TIMESTAMP%"), GetDateAndTime(DT_LOCAL)); + RulesVarReplace(commands, F("%TOPIC%"), SettingsText(SET_MQTT_TOPIC)); +#if defined(USE_TIMERS) && defined(USE_SUNRISE) + RulesVarReplace(commands, F("%SUNRISE%"), String(SunMinutes(0))); + RulesVarReplace(commands, F("%SUNSET%"), String(SunMinutes(1))); +#endif + + char command[commands.length() +1]; + strlcpy(command, commands.c_str(), sizeof(command)); + + AddLog_P2(LOG_LEVEL_INFO, PSTR("RUL: %s performs \"%s\""), event_trigger.c_str(), command); + + + +#ifdef SUPPORT_IF_STATEMENT + char *pCmd = command; + RulesPreprocessCommand(pCmd); +#endif + ExecuteCommand(command, SRC_RULE); + serviced = true; + if (stop_all_rules) { return serviced; } + } + plen += 6; + Rules.trigger_count[rule_set]++; + } + return serviced; +} + + + +bool RulesProcessEvent(char *json_event) +{ + bool serviced = false; + +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("RulesProcessEvent")); +#endif + + String event_saved = json_event; + + + + char *p = strchr(json_event, ':'); + if ((p != NULL) && !(strchr(++p, ':'))) { + event_saved.replace(F(":"), F(":{\"Data\":")); + event_saved += F("}"); + + } + event_saved.toUpperCase(); + + + + for (uint32_t i = 0; i < MAX_RULE_SETS; i++) { + if (strlen(Settings.rules[i]) && bitRead(Settings.rule_enabled, i)) { + if (RuleSetProcess(i, event_saved)) { serviced = true; } + } + } + return serviced; +} + +bool RulesProcess(void) +{ + return RulesProcessEvent(mqtt_data); +} + +void RulesInit(void) +{ + rules_flag.data = 0; + for (uint32_t i = 0; i < MAX_RULE_SETS; i++) { + if (Settings.rules[i][0] == '\0') { + bitWrite(Settings.rule_enabled, i, 0); + bitWrite(Settings.rule_once, i, 0); + } + } + Rules.teleperiod = false; +} + +void RulesEvery50ms(void) +{ + if (Settings.rule_enabled) { + char json_event[120]; + + if (-1 == Rules.new_power) { Rules.new_power = power; } + if (Rules.new_power != Rules.old_power) { + if (Rules.old_power != -1) { + for (uint32_t i = 0; i < devices_present; i++) { + uint8_t new_state = (Rules.new_power >> i) &1; + if (new_state != ((Rules.old_power >> i) &1)) { + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Power%d\":{\"State\":%d}}"), i +1, new_state); + RulesProcessEvent(json_event); + } + } + } else { + + for (uint32_t i = 0; i < devices_present; i++) { + uint8_t new_state = (Rules.new_power >> i) &1; + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Power%d\":{\"Boot\":%d}}"), i +1, new_state); + RulesProcessEvent(json_event); + } + + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { +#ifdef USE_TM1638 + if ((pin[GPIO_SWT1 +i] < 99) || ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99))) { +#else + if (pin[GPIO_SWT1 +i] < 99) { +#endif + snprintf_P(json_event, sizeof(json_event), PSTR("{\"" D_JSON_SWITCH "%d\":{\"Boot\":%d}}"), i +1, (SwitchState(i))); + RulesProcessEvent(json_event); + } + } + } + Rules.old_power = Rules.new_power; + } + else if (Rules.old_dimm != Settings.light_dimmer) { + if (Rules.old_dimm != -1) { + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Dimmer\":{\"State\":%d}}"), Settings.light_dimmer); + } else { + + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Dimmer\":{\"Boot\":%d}}"), Settings.light_dimmer); + } + RulesProcessEvent(json_event); + Rules.old_dimm = Settings.light_dimmer; + } + else if (Rules.event_data[0]) { + char *event; + char *parameter; + event = strtok_r(Rules.event_data, "=", ¶meter); + if (event) { + event = Trim(event); + if (parameter) { + parameter = Trim(parameter); + } else { + parameter = event + strlen(event); + } + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Event\":{\"%s\":\"%s\"}}"), event, parameter); + Rules.event_data[0] ='\0'; + RulesProcessEvent(json_event); + } else { + Rules.event_data[0] ='\0'; + } + } + else if (Rules.vars_event || Rules.mems_event){ + if (Rules.vars_event) { + for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { + if (bitRead(Rules.vars_event, i)) { + bitClear(Rules.vars_event, i); + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Var%d\":{\"State\":%s}}"), i+1, rules_vars[i]); + RulesProcessEvent(json_event); + break; + } + } + } + if (Rules.mems_event) { + for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { + if (bitRead(Rules.mems_event, i)) { + bitClear(Rules.mems_event, i); + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Mem%d\":{\"State\":%s}}"), i+1, SettingsText(SET_MEM1 +i)); + RulesProcessEvent(json_event); + break; + } + } + } + } + else if (rules_flag.data) { + uint16_t mask = 1; + for (uint32_t i = 0; i < MAX_RULES_FLAG; i++) { + if (rules_flag.data & mask) { + rules_flag.data ^= mask; + json_event[0] = '\0'; + switch (i) { + case 0: strncpy_P(json_event, PSTR("{\"System\":{\"Boot\":1}}"), sizeof(json_event)); break; + case 1: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Initialized\":%d}}"), MinutesPastMidnight()); break; + case 2: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Set\":%d}}"), MinutesPastMidnight()); break; + case 3: strncpy_P(json_event, PSTR("{\"MQTT\":{\"Connected\":1}}"), sizeof(json_event)); break; + case 4: strncpy_P(json_event, PSTR("{\"MQTT\":{\"Disconnected\":1}}"), sizeof(json_event)); break; + case 5: strncpy_P(json_event, PSTR("{\"WIFI\":{\"Connected\":1}}"), sizeof(json_event)); break; + case 6: strncpy_P(json_event, PSTR("{\"WIFI\":{\"Disconnected\":1}}"), sizeof(json_event)); break; + case 7: strncpy_P(json_event, PSTR("{\"HTTP\":{\"Initialized\":1}}"), sizeof(json_event)); break; +#ifdef USE_SHUTTER + case 8: strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moved\":1}}"), sizeof(json_event)); break; + case 9: strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moving\":1}}"), sizeof(json_event)); break; +#endif + } + if (json_event[0]) { + RulesProcessEvent(json_event); + break; + } + } + mask <<= 1; + } + } + } +} + +uint8_t rules_xsns_index = 0; + +void RulesEvery100ms(void) +{ + if (Settings.rule_enabled && (uptime > 4)) { + mqtt_data[0] = '\0'; + int tele_period_save = tele_period; + tele_period = 2; + XsnsNextCall(FUNC_JSON_APPEND, rules_xsns_index); + tele_period = tele_period_save; + if (strlen(mqtt_data)) { + mqtt_data[0] = '{'; + ResponseJsonEnd(); + RulesProcess(); + } + } +} + +void RulesEverySecond(void) +{ + if (Settings.rule_enabled) { + char json_event[120]; + + if (RtcTime.valid) { + if ((uptime > 60) && (RtcTime.minute != Rules.last_minute)) { + Rules.last_minute = RtcTime.minute; + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Minute\":%d}}"), MinutesPastMidnight()); + RulesProcessEvent(json_event); + } + } + for (uint32_t i = 0; i < MAX_RULE_TIMERS; i++) { + if (Rules.timer[i] != 0L) { + if (TimeReached(Rules.timer[i])) { + Rules.timer[i] = 0L; + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Rules\":{\"Timer\":%d}}"), i +1); + RulesProcessEvent(json_event); + } + } + } + } +} + +void RulesSaveBeforeRestart(void) +{ + if (Settings.rule_enabled) { + char json_event[32]; + + strncpy_P(json_event, PSTR("{\"System\":{\"Save\":1}}"), sizeof(json_event)); + RulesProcessEvent(json_event); + } +} + +void RulesSetPower(void) +{ + Rules.new_power = XdrvMailbox.index; +} + +void RulesTeleperiod(void) +{ + Rules.teleperiod = true; + RulesProcess(); + Rules.teleperiod = false; +} + +#ifdef SUPPORT_MQTT_EVENT +# 750 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +bool RulesMqttData(void) +{ + if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > 256) { + return false; + } + bool serviced = false; + String sTopic = XdrvMailbox.topic; + String sData = XdrvMailbox.data; + + MQTT_Subscription event_item; + + for (uint32_t index = 0; index < subscriptions.size(); index++) { + event_item = subscriptions.get(index); + + + if (sTopic.startsWith(event_item.Topic)) { + + serviced = true; + String value; + if (event_item.Key.length() == 0) { + value = sData; + } else { + StaticJsonBuffer<500> jsonBuf; + JsonObject& jsonData = jsonBuf.parseObject(sData); + String key1 = event_item.Key; + String key2; + if (!jsonData.success()) break; + int dot; + if ((dot = key1.indexOf('.')) > 0) { + key2 = key1.substring(dot+1); + key1 = key1.substring(0, dot); + if (!jsonData[key1][key2].success()) break; + value = (const char *)jsonData[key1][key2]; + } else { + if (!jsonData[key1].success()) break; + value = (const char *)jsonData[key1]; + } + } + value.trim(); + + snprintf_P(Rules.event_data, sizeof(Rules.event_data), PSTR("%s=%s"), event_item.Event.c_str(), value.c_str()); + } + } + return serviced; +} +# 812 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +void CmndSubscribe(void) +{ + MQTT_Subscription subscription_item; + String events; + if (XdrvMailbox.data_len > 0) { + char parameters[XdrvMailbox.data_len+1]; + memcpy(parameters, XdrvMailbox.data, XdrvMailbox.data_len); + parameters[XdrvMailbox.data_len] = '\0'; + String event_name, topic, key; + + char * pos = strtok(parameters, ","); + if (pos) { + event_name = Trim(pos); + pos = strtok(nullptr, ","); + if (pos) { + topic = Trim(pos); + pos = strtok(nullptr, ","); + if (pos) { + key = Trim(pos); + } + } + } + + event_name.toUpperCase(); + if (event_name.length() > 0 && topic.length() > 0) { + + for (uint32_t index=0; index < subscriptions.size(); index++) { + if (subscriptions.get(index).Event.equals(event_name)) { + + String stopic = subscriptions.get(index).Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + subscriptions.remove(index); + break; + } + } + + if (!topic.endsWith("#")) { + if (topic.endsWith("/")) { + topic.concat("#"); + } else { + topic.concat("/#"); + } + } + + + subscription_item.Event = event_name; + subscription_item.Topic = topic.substring(0, topic.length() - 2); + subscription_item.Key = key; + subscriptions.add(subscription_item); + + MqttSubscribe(topic.c_str()); + events.concat(event_name + "," + topic + + (key.length()>0 ? "," : "") + + key); + } else { + events = D_JSON_WRONG_PARAMETERS; + } + } else { + + for (uint32_t index=0; index < subscriptions.size(); index++) { + subscription_item = subscriptions.get(index); + events.concat(subscription_item.Event + "," + subscription_item.Topic + + (subscription_item.Key.length()>0 ? "," : "") + + subscription_item.Key + "; "); + } + } + ResponseCmndChar(events.c_str()); +} +# 892 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +void CmndUnsubscribe(void) +{ + MQTT_Subscription subscription_item; + String events; + if (XdrvMailbox.data_len > 0) { + for (uint32_t index = 0; index < subscriptions.size(); index++) { + subscription_item = subscriptions.get(index); + if (subscription_item.Event.equalsIgnoreCase(XdrvMailbox.data)) { + String stopic = subscription_item.Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + events = subscription_item.Event; + subscriptions.remove(index); + break; + } + } + } else { + + String stopic; + while (subscriptions.size() > 0) { + events.concat(subscriptions.get(0).Event + "; "); + stopic = subscriptions.get(0).Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + subscriptions.remove(0); + } + } + ResponseCmndChar(events.c_str()); +} + +#endif + +#ifdef USE_EXPRESSION +# 934 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +char * findClosureBracket(char * pStart) +{ + char * pointer = pStart + 1; + + bool bFindClosures = false; + uint8_t matchClosures = 1; + while (*pointer) + { + if (*pointer == ')') { + matchClosures--; + if (matchClosures == 0) { + bFindClosures = true; + break; + } + } else if (*pointer == '(') { + matchClosures++; + } + pointer++; + } + if (bFindClosures) { + return pointer; + } else { + return nullptr; + } +} +# 973 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +bool findNextNumber(char * &pNumber, float &value) +{ + bool bSucceed = false; + String sNumber = ""; + if (*pNumber == '-') { + sNumber = "-"; + pNumber++; + } + while (*pNumber) { + if (isdigit(*pNumber) || (*pNumber == '.')) { + sNumber += *pNumber; + pNumber++; + } else { + break; + } + } + if (sNumber.length() > 0) { + value = CharToFloat(sNumber.c_str()); + bSucceed = true; + } + return bSucceed; +} +# 1009 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +bool findNextVariableValue(char * &pVarname, float &value) +{ + bool succeed = true; + value = 0; + String sVarName = ""; + while (*pVarname) { + if (isalpha(*pVarname) || isdigit(*pVarname)) { + sVarName.concat(*pVarname); + pVarname++; + } else { + break; + } + } + sVarName.toUpperCase(); + if (sVarName.startsWith(F("VAR"))) { + int index = sVarName.substring(3).toInt(); + if (index > 0 && index <= MAX_RULE_VARS) { + value = CharToFloat(rules_vars[index -1]); + } + } else if (sVarName.startsWith(F("MEM"))) { + int index = sVarName.substring(3).toInt(); + if (index > 0 && index <= MAX_RULE_MEMS) { + value = CharToFloat(SettingsText(SET_MEM1 + index -1)); + } + } else if (sVarName.equals(F("TIME"))) { + value = MinutesPastMidnight(); + } else if (sVarName.equals(F("UPTIME"))) { + value = MinutesUptime(); + } else if (sVarName.equals(F("UTCTIME"))) { + value = UtcTime(); + } else if (sVarName.equals(F("LOCALTIME"))) { + value = LocalTime(); +#if defined(USE_TIMERS) && defined(USE_SUNRISE) + } else if (sVarName.equals(F("SUNRISE"))) { + value = SunMinutes(0); + } else if (sVarName.equals(F("SUNSET"))) { + value = SunMinutes(1); +#endif + } else { + succeed = false; + } + + return succeed; +} +# 1071 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +bool findNextObjectValue(char * &pointer, float &value) +{ + bool bSucceed = false; + while (*pointer) + { + if (isspace(*pointer)) { + pointer++; + continue; + } + if (isdigit(*pointer) || (*pointer) == '-') { + bSucceed = findNextNumber(pointer, value); + break; + } else if (isalpha(*pointer)) { + bSucceed = findNextVariableValue(pointer, value); + break; + } else if (*pointer == '(') { + char * closureBracket = findClosureBracket(pointer); + if (closureBracket != nullptr) { + value = evaluateExpression(pointer+1, closureBracket - pointer - 1); + pointer = closureBracket + 1; + bSucceed = true; + } + break; + } else { + break; + } + } + return bSucceed; +} +# 1115 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +bool findNextOperator(char * &pointer, int8_t &op) +{ + bool bSucceed = false; + while (*pointer) + { + if (isspace(*pointer)) { + pointer++; + continue; + } + op = EXPRESSION_OPERATOR_ADD; + const char *pch = kExpressionOperators; + char ch; + while ((ch = pgm_read_byte(pch++)) != '\0') { + if (ch == *pointer) { + bSucceed = true; + pointer++; + break; + } + op++; + } + break; + } + return bSucceed; +} +# 1152 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +float calculateTwoValues(float v1, float v2, uint8_t op) +{ + switch (op) + { + case EXPRESSION_OPERATOR_ADD: + return v1 + v2; + case EXPRESSION_OPERATOR_SUBTRACT: + return v1 - v2; + case EXPRESSION_OPERATOR_MULTIPLY: + return v1 * v2; + case EXPRESSION_OPERATOR_DIVIDEDBY: + return (0 == v2) ? 0 : (v1 / v2); + case EXPRESSION_OPERATOR_MODULO: + return (0 == v2) ? 0 : (int(v1) % int(v2)); + case EXPRESSION_OPERATOR_POWER: + return FastPrecisePow(v1, v2); + } + return 0; +} +# 1205 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +float evaluateExpression(const char * expression, unsigned int len) +{ + char expbuf[len + 1]; + memcpy(expbuf, expression, len); + expbuf[len] = '\0'; + char * scan_pointer = expbuf; + + LinkedList object_values; + LinkedList operators; + int8_t op; + float va; + + if (findNextObjectValue(scan_pointer, va)) { + object_values.add(va); + } else { + return 0; + } + while (*scan_pointer) + { + if (findNextOperator(scan_pointer, op) + && *scan_pointer + && findNextObjectValue(scan_pointer, va)) + { + operators.add(op); + object_values.add(va); + } else { + + break; + } + } + + + + for (int32_t priority = MAX_EXPRESSION_OPERATOR_PRIORITY; priority>0; priority--) { + int index = 0; + while (index < operators.size()) { + if (priority == pgm_read_byte(kExpressionOperatorsPriorities + operators.get(index))) { + + va = calculateTwoValues(object_values.get(index), object_values.remove(index + 1), operators.remove(index)); + + object_values.set(index, va); + } else { + index++; + } + } + } + return object_values.get(0); +} +#endif + +#ifdef SUPPORT_IF_STATEMENT +void CmndIf(void) +{ + if (XdrvMailbox.data_len > 0) { + char parameters[XdrvMailbox.data_len+1]; + memcpy(parameters, XdrvMailbox.data, XdrvMailbox.data_len); + parameters[XdrvMailbox.data_len] = '\0'; + ProcessIfStatement(parameters); + } + ResponseCmndDone(); +} +# 1279 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +bool evaluateComparisonExpression(const char *expression, int len) +{ + bool bResult = true; + char expbuf[len + 1]; + memcpy(expbuf, expression, len); + expbuf[len] = '\0'; + String compare_expression = expbuf; + String leftExpr, rightExpr; + int8_t compareOp = parseCompareExpression(compare_expression, leftExpr, rightExpr); + + double leftValue = evaluateExpression(leftExpr.c_str(), leftExpr.length()); + double rightValue = evaluateExpression(rightExpr.c_str(), rightExpr.length()); + switch (compareOp) { + case COMPARE_OPERATOR_EXACT_DIVISION: + bResult = (rightValue != 0 && leftValue == int(leftValue) + && rightValue == int(rightValue) && (int(leftValue) % int(rightValue)) == 0); + break; + case COMPARE_OPERATOR_EQUAL: + bResult = leftExpr.equalsIgnoreCase(rightExpr); + break; + case COMPARE_OPERATOR_BIGGER: + bResult = (leftValue > rightValue); + break; + case COMPARE_OPERATOR_SMALLER: + bResult = (leftValue < rightValue); + break; + case COMPARE_OPERATOR_NUMBER_EQUAL: + bResult = (leftValue == rightValue); + break; + case COMPARE_OPERATOR_NOT_EQUAL: + bResult = (leftValue != rightValue); + break; + case COMPARE_OPERATOR_BIGGER_EQUAL: + bResult = (leftValue >= rightValue); + break; + case COMPARE_OPERATOR_SMALLER_EQUAL: + bResult = (leftValue <= rightValue); + break; + } + return bResult; +} +# 1335 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +bool findNextLogicOperator(char * &pointer, int8_t &op) +{ + bool bSucceed = false; + while (*pointer && isspace(*pointer)) { + + pointer++; + } + if (*pointer) { + if (strncasecmp_P(pointer, PSTR("AND "), 4) == 0) { + op = LOGIC_OPERATOR_AND; + pointer += 4; + bSucceed = true; + } else if (strncasecmp_P(pointer, PSTR("OR "), 3) == 0) { + op = LOGIC_OPERATOR_OR; + pointer += 3; + bSucceed = true; + } + } + return bSucceed; +} +# 1372 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +bool findNextLogicObjectValue(char * &pointer, bool &value) +{ + bool bSucceed = false; + while (*pointer && isspace(*pointer)) { + + pointer++; + } + char * pExpr = pointer; + while (*pointer) { + if (isalpha(*pointer) + && (strncasecmp_P(pointer, PSTR("AND "), 4) == 0 + || strncasecmp_P(pointer, PSTR("OR "), 3) == 0)) + { + value = evaluateComparisonExpression(pExpr, pointer - pExpr); + bSucceed = true; + break; + } else if (*pointer == '(') { + char * closureBracket = findClosureBracket(pointer); + if (closureBracket != nullptr) { + value = evaluateLogicalExpression(pointer+1, closureBracket - pointer - 1); + pointer = closureBracket + 1; + bSucceed = true; + } + break; + } + pointer++; + } + if (!bSucceed && pointer > pExpr) { + + value = evaluateComparisonExpression(pExpr, pointer - pExpr); + bSucceed = true; + } + return bSucceed; +} +# 1421 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +bool evaluateLogicalExpression(const char * expression, int len) +{ + bool bResult = false; + + char expbuff[len + 1]; + memcpy(expbuff, expression, len); + expbuff[len] = '\0'; + + + char * pointer = expbuff; + LinkedList values; + LinkedList logicOperators; + + bool bValue; + if (findNextLogicObjectValue(pointer, bValue)) { + values.add(bValue); + } else { + return false; + } + int8_t op; + while (*pointer) { + if (findNextLogicOperator(pointer, op) + && (*pointer) && findNextLogicObjectValue(pointer, bValue)) + { + logicOperators.add(op); + values.add(bValue); + } else { + break; + } + } + + int index = 0; + while (index < logicOperators.size()) { + if (logicOperators.get(index) == LOGIC_OPERATOR_AND) { + values.set(index, values.get(index) && values.get(index+1)); + values.remove(index + 1); + logicOperators.remove(index); + } else { + index++; + } + } + + index = 0; + while (index < logicOperators.size()) { + if (logicOperators.get(index) == LOGIC_OPERATOR_OR) { + values.set(index, values.get(index) || values.get(index+1)); + values.remove(index + 1); + logicOperators.remove(index); + } else { + index++; + } + } + return values.get(0); +} +# 1492 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +int8_t findIfBlock(char * &pointer, int &lenWord, int8_t block_type) +{ + int8_t foundBlock = IF_BLOCK_INVALID; + + const char * word; + while (*pointer) { + if (!isalpha(*pointer)) { + pointer++; + continue; + } + word = pointer; + while (*pointer && isalpha(*pointer)) { + pointer++; + } + lenWord = pointer - word; + + if (2 == lenWord && 0 == strncasecmp_P(word, PSTR("IF"), 2)) { + + + if (findIfBlock(pointer, lenWord, IF_BLOCK_ENDIF) != IF_BLOCK_ENDIF) { + + break; + } + } else if ( (IF_BLOCK_ENDIF == block_type || IF_BLOCK_ANY == block_type) + && (5 == lenWord) && (0 == strncasecmp_P(word, PSTR("ENDIF"), 5))) + { + + foundBlock = IF_BLOCK_ENDIF; + break; + } else if ( (IF_BLOCK_ELSEIF == block_type || IF_BLOCK_ANY == block_type) + && (6 == lenWord) && (0 == strncasecmp_P(word, PSTR("ELSEIF"), 6))) + { + + foundBlock = IF_BLOCK_ELSEIF; + break; + } else if ( (IF_BLOCK_ELSE == block_type || IF_BLOCK_ANY == block_type) + && (4 == lenWord) && (0 == strncasecmp_P(word, PSTR("ELSE"), 4))) + { + + foundBlock = IF_BLOCK_ELSE; + break; + } + } + return foundBlock; +} +# 1549 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +void ExecuteCommandBlock(const char * commands, int len) +{ + char cmdbuff[len + 1]; + memcpy(cmdbuff, commands, len); + cmdbuff[len] = '\0'; + + + char oneCommand[len + 1]; + int insertPosition = 0; + char * pos = cmdbuff; + int lenEndBlock = 0; + while (*pos) { + if (isspace(*pos) || '\x1e' == *pos || ';' == *pos) { + pos++; + continue; + } + if (strncasecmp_P(pos, PSTR("BACKLOG "), 8) == 0) { + + pos += 8; + continue; + } + if (strncasecmp_P(pos, PSTR("IF "), 3) == 0) { + + + char *pEndif = pos + 3; + if (IF_BLOCK_ENDIF != findIfBlock(pEndif, lenEndBlock, IF_BLOCK_ENDIF)) { + + break; + } + + memcpy(oneCommand, pos, pEndif - pos); + oneCommand[pEndif - pos] = '\0'; + pos = pEndif; + } else { + + char *pEndOfCommand = strpbrk(pos, "\x1e;"); + if (NULL == pEndOfCommand) { + pEndOfCommand = pos + strlen(pos); + } + memcpy(oneCommand, pos, pEndOfCommand - pos); + oneCommand[pEndOfCommand - pos] = '\0'; + pos = pEndOfCommand; + } + + + String sCurrentCommand = oneCommand; + sCurrentCommand.trim(); + if (sCurrentCommand.length() > 0 + && backlog.size() < MAX_BACKLOG && !backlog_mutex) + { + + backlog_mutex = true; + backlog.add(insertPosition, sCurrentCommand); + backlog_mutex = false; + insertPosition++; + } + } + return; +} +# 1619 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +void ProcessIfStatement(const char* statements) +{ + String conditionExpression; + int len = strlen(statements); + char statbuff[len + 1]; + memcpy(statbuff, statements, len + 1); + char *pos = statbuff; + int lenEndBlock = 0; + while (true) { + + + while (*pos && *pos != '(') { + pos++; + } + if (0 == *pos) { break; } + char * posEnd = findClosureBracket(pos); + + if (true == evaluateLogicalExpression(pos + 1, posEnd - (pos + 1))) { + + char * cmdBlockStart = posEnd + 1; + char * cmdBlockEnd = cmdBlockStart; + int8_t nextBlock = findIfBlock(cmdBlockEnd, lenEndBlock, IF_BLOCK_ANY); + if (IF_BLOCK_INVALID == nextBlock) { + + break; + } + ExecuteCommandBlock(cmdBlockStart, cmdBlockEnd - cmdBlockStart - lenEndBlock); + pos = cmdBlockEnd; + break; + } else { + pos = posEnd + 1; + int8_t nextBlock = findIfBlock(pos, lenEndBlock, IF_BLOCK_ANY); + if (IF_BLOCK_ELSEIF == nextBlock) { + + continue; + } else if (IF_BLOCK_ELSE == nextBlock) { + + char * cmdBlockEnd = pos; + int8_t nextBlock = findIfBlock(cmdBlockEnd, lenEndBlock, IF_BLOCK_ENDIF); + if (IF_BLOCK_ENDIF != nextBlock) { + + break; + } + ExecuteCommandBlock(pos, cmdBlockEnd - pos - lenEndBlock); + break; + } else { + + break; + } + } + } +} +# 1683 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" +void RulesPreprocessCommand(char *pCommands) +{ + char * cmd = pCommands; + int lenEndBlock = 0; + while (*cmd) { + + if (';' == *cmd || isspace(*cmd)) { + cmd++; + } + else if (strncasecmp_P(cmd, PSTR("IF "), 3) == 0) { + + char * pIfStart = cmd; + char * pIfEnd = pIfStart + 3; + + if (IF_BLOCK_ENDIF == findIfBlock(pIfEnd, lenEndBlock, IF_BLOCK_ENDIF)) { + + cmd = pIfEnd; + + + while (pIfStart < pIfEnd) { + if (';' == *pIfStart) + *pIfStart = '\x1e'; + pIfStart++; + } + } + else { + break; + } + } + else { + while (*cmd && ';' != *cmd) { + cmd++; + } + } + } + return; +} +#endif + + + + + +void CmndRule(void) +{ + uint8_t index = XdrvMailbox.index; + if ((index > 0) && (index <= MAX_RULE_SETS)) { + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.rules[index -1]))) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10)) { + switch (XdrvMailbox.payload) { + case 0: + case 1: + bitWrite(Settings.rule_enabled, index -1, XdrvMailbox.payload); + break; + case 2: + bitWrite(Settings.rule_enabled, index -1, bitRead(Settings.rule_enabled, index -1) ^1); + break; + case 4: + case 5: + bitWrite(Settings.rule_once, index -1, XdrvMailbox.payload &1); + break; + case 6: + bitWrite(Settings.rule_once, index -1, bitRead(Settings.rule_once, index -1) ^1); + break; + case 8: + case 9: + bitWrite(Settings.rule_stop, index -1, XdrvMailbox.payload &1); + break; + case 10: + bitWrite(Settings.rule_stop, index -1, bitRead(Settings.rule_stop, index -1) ^1); + break; + } + } else { + int offset = 0; + if ('+' == XdrvMailbox.data[0]) { + offset = strlen(Settings.rules[index -1]); + if (XdrvMailbox.data_len < (sizeof(Settings.rules[index -1]) - offset -1)) { + XdrvMailbox.data[0] = ' '; + } else { + offset = -1; + } + } + if (offset != -1) { + strlcpy(Settings.rules[index -1] + offset, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(Settings.rules[index -1])); + } + } + Rules.triggers[index -1] = 0; + } + snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\",\"Once\":\"%s\",\"StopOnError\":\"%s\",\"Free\":%d,\"Rules\":\"%s\"}"), + XdrvMailbox.command, index, GetStateText(bitRead(Settings.rule_enabled, index -1)), GetStateText(bitRead(Settings.rule_once, index -1)), + GetStateText(bitRead(Settings.rule_stop, index -1)), sizeof(Settings.rules[index -1]) - strlen(Settings.rules[index -1]) -1, Settings.rules[index -1]); + } +} + +void CmndRuleTimer(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_TIMERS)) { + if (XdrvMailbox.data_len > 0) { +#ifdef USE_EXPRESSION + float timer_set = evaluateExpression(XdrvMailbox.data, XdrvMailbox.data_len); + Rules.timer[XdrvMailbox.index -1] = (timer_set > 0) ? millis() + (1000 * timer_set) : 0; +#else + Rules.timer[XdrvMailbox.index -1] = (XdrvMailbox.payload > 0) ? millis() + (1000 * XdrvMailbox.payload) : 0; +#endif + } + mqtt_data[0] = '\0'; + for (uint32_t i = 0; i < MAX_RULE_TIMERS; i++) { + ResponseAppend_P(PSTR("%c\"T%d\":%d"), (i) ? ',' : '{', i +1, (Rules.timer[i]) ? (Rules.timer[i] - millis()) / 1000 : 0); + } + ResponseJsonEnd(); + } +} + +void CmndEvent(void) +{ + if (XdrvMailbox.data_len > 0) { + strlcpy(Rules.event_data, XdrvMailbox.data, sizeof(Rules.event_data)); + } + ResponseCmndDone(); +} + +void CmndVariable(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { + if (!XdrvMailbox.usridx) { + mqtt_data[0] = '\0'; + for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { + ResponseAppend_P(PSTR("%c\"Var%d\":\"%s\""), (i) ? ',' : '{', i +1, rules_vars[i]); + } + ResponseJsonEnd(); + } else { + if (XdrvMailbox.data_len > 0) { +#ifdef USE_EXPRESSION + if (XdrvMailbox.data[0] == '=') { + dtostrfd(evaluateExpression(XdrvMailbox.data + 1, XdrvMailbox.data_len - 1), Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + } else { + strlcpy(rules_vars[XdrvMailbox.index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(rules_vars[XdrvMailbox.index -1])); + } +#else + strlcpy(rules_vars[XdrvMailbox.index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(rules_vars[XdrvMailbox.index -1])); +#endif + bitSet(Rules.vars_event, XdrvMailbox.index -1); + } + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); + } + } +} + +void CmndMemory(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_MEMS)) { + if (!XdrvMailbox.usridx) { + ResponseCmndAll(SET_MEM1, MAX_RULE_MEMS); + } else { + if (XdrvMailbox.data_len > 0) { +#ifdef USE_EXPRESSION + if (XdrvMailbox.data[0] == '=') { + dtostrfd(evaluateExpression(XdrvMailbox.data + 1, XdrvMailbox.data_len - 1), Settings.flag2.calc_resolution, SettingsText(SET_MEM1 + XdrvMailbox.index -1)); + } else { + SettingsUpdateText(SET_MEM1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data); + } +#else + SettingsUpdateText(SET_MEM1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data); +#endif + bitSet(Rules.mems_event, XdrvMailbox.index -1); + } + ResponseCmndIdxChar(SettingsText(SET_MEM1 + XdrvMailbox.index -1)); + } + } +} + +void CmndCalcResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 7)) { + Settings.flag2.calc_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.calc_resolution); +} + +void CmndAddition(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { + if (XdrvMailbox.data_len > 0) { + float tempvar = CharToFloat(rules_vars[XdrvMailbox.index -1]) + CharToFloat(XdrvMailbox.data); + dtostrfd(tempvar, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + bitSet(Rules.vars_event, XdrvMailbox.index -1); + } + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); + } +} + +void CmndSubtract(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { + if (XdrvMailbox.data_len > 0) { + float tempvar = CharToFloat(rules_vars[XdrvMailbox.index -1]) - CharToFloat(XdrvMailbox.data); + dtostrfd(tempvar, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + bitSet(Rules.vars_event, XdrvMailbox.index -1); + } + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); + } +} + +void CmndMultiply(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { + if (XdrvMailbox.data_len > 0) { + float tempvar = CharToFloat(rules_vars[XdrvMailbox.index -1]) * CharToFloat(XdrvMailbox.data); + dtostrfd(tempvar, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + bitSet(Rules.vars_event, XdrvMailbox.index -1); + } + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); + } +} + +void CmndScale(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { + if (XdrvMailbox.data_len > 0) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { + char sub_string[XdrvMailbox.data_len +1]; + + float valueIN = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 1)); + float fromLow = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)); + float fromHigh = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 3)); + float toLow = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)); + float toHigh = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 5)); + float value = map_double(valueIN, fromLow, fromHigh, toLow, toHigh); + dtostrfd(value, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + bitSet(Rules.vars_event, XdrvMailbox.index -1); + } + } + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); + } +} + +float map_double(float x, float in_min, float in_max, float out_min, float out_max) +{ + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + + + + + +bool Xdrv10(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_50_MSECOND: + RulesEvery50ms(); + break; + case FUNC_EVERY_100_MSECOND: + RulesEvery100ms(); + break; + case FUNC_EVERY_SECOND: + RulesEverySecond(); + break; + case FUNC_SET_POWER: + RulesSetPower(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kRulesCommands, RulesCommand); + break; + case FUNC_RULES_PROCESS: + result = RulesProcess(); + break; + case FUNC_SAVE_BEFORE_RESTART: + RulesSaveBeforeRestart(); + break; +#ifdef SUPPORT_MQTT_EVENT + case FUNC_MQTT_DATA: + result = RulesMqttData(); + break; +#endif + case FUNC_PRE_INIT: + RulesInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_scripter.ino" +# 21 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_scripter.ino" +#ifdef USE_SCRIPT +#ifndef USE_RULES +# 41 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_scripter.ino" +#define XDRV_10 10 +#define XI2C_37 37 + +#define SCRIPT_DEBUG 0 + + +#ifndef MAXVARS +#define MAXVARS 50 +#endif +#ifndef MAXSVARS +#define MAXSVARS 5 +#endif +#define MAXNVARS MAXVARS-MAXSVARS + +#define MAXFILT 5 +#define SCRIPT_SVARSIZE 20 +#define SCRIPT_MAXSSIZE 48 +#define SCRIPT_EOL '\n' +#define SCRIPT_FLOAT_PRECISION 2 +#define PMEM_SIZE sizeof(Settings.script_pram) +#define SCRIPT_MAXPERM (PMEM_SIZE)-4/sizeof(float) +#define MAX_SCRIPT_SIZE MAX_RULE_SIZE*MAX_RULE_SETS + + +uint32_t EncodeLightId(uint8_t relay_id); +uint32_t DecodeLightId(uint32_t hue_id); + +#if defined(ESP32) && defined(ESP32_SCRIPT_SIZE) && !defined(USE_24C256) && !defined(USE_SCRIPT_FATFS) + +#include "FS.h" +#include "SPIFFS.h" +void SaveFile(const char *name,const uint8_t *buf,uint32_t len) { + File file = SPIFFS.open(name, FILE_WRITE); + if (!file) return; + file.write(buf, len); + file.close(); +} + +#define FORMAT_SPIFFS_IF_FAILED true +uint8_t spiffs_mounted=0; + +void LoadFile(const char *name,uint8_t *buf,uint32_t len) { + if (!spiffs_mounted) { + if(!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)){ + + return; + } + spiffs_mounted=1; + } + File file = SPIFFS.open(name); + if (!file) return; + file.read(buf, len); + file.close(); +} +#endif + + +#define EPOCH_OFFSET 1546300800 + +enum {OPER_EQU=1,OPER_PLS,OPER_MIN,OPER_MUL,OPER_DIV,OPER_PLSEQU,OPER_MINEQU,OPER_MULEQU,OPER_DIVEQU,OPER_EQUEQU,OPER_NOTEQU,OPER_GRTEQU,OPER_LOWEQU,OPER_GRT,OPER_LOW,OPER_PERC,OPER_XOR,OPER_AND,OPER_OR,OPER_ANDEQU,OPER_OREQU,OPER_XOREQU,OPER_PERCEQU}; +enum {SCRIPT_LOGLEVEL=1,SCRIPT_TELEPERIOD}; + +#ifdef USE_SCRIPT_FATFS +#include +#include +#ifndef FAT_SCRIPT_SIZE +#define FAT_SCRIPT_SIZE 4096 +#endif +#define FAT_SCRIPT_NAME "script.txt" +#if USE_LONG_FILE_NAMES==1 +#warning ("FATFS long filenames not supported"); +#endif +#if USE_STANDARD_SPI_LIBRARY==0 +#warning ("FATFS standard spi should be used"); +#endif +#endif + +#ifdef SUPPORT_MQTT_EVENT + #include + typedef struct { + String Event; + String Topic; + String Key; + } MQTT_Subscription; + LinkedList subscriptions; +#endif + +#ifdef USE_DISPLAY +#ifdef USE_TOUCH_BUTTONS +#include +extern VButton *buttons[MAXBUTTONS]; +#endif +#endif + +typedef union { + uint8_t data; + struct { + uint8_t is_string : 1; + uint8_t is_permanent : 1; + uint8_t is_timer : 1; + uint8_t is_autoinc : 1; + uint8_t changed : 1; + uint8_t settable : 1; + uint8_t is_filter : 1; + uint8_t constant : 1; + }; +} SCRIPT_TYPE; + +struct T_INDEX { + uint8_t index; + SCRIPT_TYPE bits; +}; + +struct M_FILT { + uint8_t numvals; + uint8_t index; + float maccu; + float rbuff[1]; +}; + +typedef union { + uint8_t data; + struct { + uint8_t nutu8 : 1; + uint8_t nutu7 : 1; + uint8_t nutu6 : 1; + uint8_t nutu5 : 1; + uint8_t nutu4 : 1; + uint8_t nutu3 : 1; + uint8_t is_dir : 1; + uint8_t is_open : 1; + }; +} FILE_FLAGS; + +#define SFS_MAX 4 + +struct SCRIPT_MEM { + float *fvars; + float *s_fvars; + struct T_INDEX *type; + struct M_FILT *mfilt; + char *glob_vnp; + uint8_t *vnp_offset; + char *glob_snp; + char *scriptptr; + char *section_ptr; + char *scriptptr_bu; + char *script_ram; + uint16_t script_size; + uint8_t *script_pram; + uint16_t script_pram_size; + uint8_t numvars; + void *script_mem; + uint16_t script_mem_size; + uint8_t script_dprec; + uint8_t var_not_found; + uint8_t glob_error; + uint8_t max_ssize; + uint8_t script_loglevel; + uint8_t flags; +#ifdef USE_SCRIPT_FATFS + File files[SFS_MAX]; + FILE_FLAGS file_flags[SFS_MAX]; + uint8_t script_sd_found; + char flink[2][14]; +#endif +} glob_script_mem; + + +int16_t last_findex; +uint8_t tasm_cmd_activ=0; +uint8_t fast_script=0; +uint32_t script_lastmillis; + + +#ifdef USE_BUTTON_EVENT +int8_t script_button[MAX_KEYS]; +#endif + +char *GetNumericResult(char *lp,uint8_t lastop,float *fp,JsonObject *jo); +char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo); +char *ForceStringVar(char *lp,char *dstr); +void send_download(void); +uint8_t reject(char *name); + +void ScriptEverySecond(void) { + + if (bitRead(Settings.rule_enabled, 0)) { + struct T_INDEX *vtp=glob_script_mem.type; + float delta=(millis()-script_lastmillis)/1000; + script_lastmillis=millis(); + for (uint8_t count=0; count0) { + + *fp-=delta; + if (*fp<0) *fp=0; + } + } + if (vtp[count].bits.is_autoinc) { + + float *fp=&glob_script_mem.fvars[vtp[count].index]; + if (*fp>=0) { + *fp+=delta; + } + } + } + Run_Scripter(">S",2,0); + } +} + +void RulesTeleperiod(void) { + if (bitRead(Settings.rule_enabled, 0) && mqtt_data[0]) Run_Scripter(">T",2, mqtt_data); +} + + +#ifdef USE_24C256 +#ifndef USE_SCRIPT_FATFS + +#include +#define EEPROM_ADDRESS 0x50 + +#ifndef EEP_SCRIPT_SIZE +#define EEP_SCRIPT_SIZE 4095 +#endif + + +static Eeprom24C128_256 eeprom(EEPROM_ADDRESS); + +#define EEP_WRITE(A,B,C) eeprom.writeBytes(A,B,(uint8_t*)C); + +#define EEP_READ(A,B,C) eeprom.readBytes(A,B,(uint8_t*)C); +#endif +#endif + +#define SCRIPT_SKIP_SPACES while (*lp==' ' || *lp=='\t') lp++; +#define SCRIPT_SKIP_EOL while (*lp==SCRIPT_EOL) lp++; + + +int16_t Init_Scripter(void) { +char *script; + + script=glob_script_mem.script_ram; + + + uint16_t lines=0,nvars=0,svars=0,vars=0; + char *lp=script; + char vnames[MAXVARS*10]; + char *vnames_p=vnames; + char *vnp[MAXVARS]; + char **vnp_p=vnp; + char strings[MAXSVARS*SCRIPT_MAXSSIZE]; + struct M_FILT mfilt[MAXFILT]; + + char *strings_p=strings; + char *snp[MAXSVARS]; + char **snp_p=snp; + uint8_t numperm=0,numflt=0,count; + + glob_script_mem.max_ssize=SCRIPT_SVARSIZE; + glob_script_mem.scriptptr=0; + + if (!*script) return -999; + + float fvalues[MAXVARS]; + struct T_INDEX vtypes[MAXVARS]; + char init=0; + while (1) { + + + SCRIPT_SKIP_SPACES + + if (*lp=='\n' || *lp=='\r') goto next_line; + + if (*lp==';') goto next_line; + if (init) { + + if (*lp=='>') { + init=0; + break; + } + char *op=strchr(lp,'='); + if (op) { + vtypes[vars].bits.data=0; + + if (*lp=='p' && *(lp+1)==':') { + lp+=2; + if (numpermMAXFILT) { + return -6; + } + } else { + vtypes[vars].bits.is_filter=0; + } + *vnp_p++=vnames_p; + while (lpMAXNVARS) { + return -1; + } + if (vtypes[vars].bits.is_filter) { + while (isdigit(*op) || *op=='.' || *op=='-') { + op++; + } + while (*op==' ') op++; + if (isdigit(*op)) { + + uint8_t flen=atoi(op); + mfilt[numflt-1].numvals&=0x80; + mfilt[numflt-1].numvals|=flen&0x7f; + } + } + + } else { + + op++; + *snp_p++=strings_p; + while (*op!='\"') { + if (*op==SCRIPT_EOL) break; + *strings_p++=*op++; + } + *strings_p++=0; + vtypes[vars].bits.is_string=1; + vtypes[vars].index=svars; + svars++; + if (svars>MAXSVARS) { + return -2; + } + } + vars++; + if (vars>MAXVARS) { + return -3; + } + } + } else { + if (!strncmp(lp,">D",2)) { + lp+=2; + SCRIPT_SKIP_SPACES + if (isdigit(*lp)) { + uint8_t ssize=atoi(lp)+1; + if (ssize<10 || ssize>SCRIPT_MAXSSIZE) ssize=SCRIPT_MAXSSIZE; + glob_script_mem.max_ssize=ssize; + } + init=1; + } + } + + next_line: + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + + uint16_t fsize=0; + for (count=0; count255) { + free(glob_script_mem.script_mem); + return -5; + } + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR("Script: nv=%d, tv=%d, vns=%d, ram=%d"), nvars, svars, index, glob_script_mem.script_mem_size); + + + char *cp1=glob_script_mem.glob_snp; + char *sp=strings; + for (count=0; countnumvals=mfilt[count].numvals; + mp+=sizeof(struct M_FILT)+((mfilt[count].numvals&0x7f)-1)*sizeof(float); + } + + glob_script_mem.numvars=vars; + glob_script_mem.script_dprec=SCRIPT_FLOAT_PRECISION; + glob_script_mem.script_loglevel=LOG_LEVEL_INFO; + + +#if SCRIPT_DEBUG>2 + struct T_INDEX *dvtp=glob_script_mem.type; + for (uint8_t count=0; count0 + ClaimSerial(); + SetSerialBaudrate(9600); +#endif + + + glob_script_mem.scriptptr=lp-1; + glob_script_mem.scriptptr_bu=glob_script_mem.scriptptr; + return 0; + +} + +#ifdef USE_LIGHT +#ifdef USE_WS2812 +void ws2812_set_array(float *array ,uint8_t len) { + + Ws2812ForceSuspend(); + for (uint8_t cnt=0;cntSettings.light_pixels) break; + uint32_t col=array[cnt]; + Ws2812SetColor(cnt+1,col>>16,col>>8,col,0); + } + Ws2812ForceUpdate(); +} +#endif +#endif + +#define NUM_RES 0xfe +#define STR_RES 0xfd +#define VAR_NV 0xff + +#define NTYPE 0 +#define STYPE 0x80 + +#ifndef FLT_MAX +#define FLT_MAX 99999999 +#endif + +float median_array(float *array,uint8_t len) { + uint8_t ind[len]; + uint8_t mind=0,index=0,flg; + float min=FLT_MAX; + + for (uint8_t hcnt=0; hcntnumvals&0x7f; + return mflp->rbuff; + } + mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); + } + return 0; +} + + +float Get_MFVal(uint8_t index,uint8_t bind) { + uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; + for (uint8_t count=0; countnumvals&0x7f; + if (!bind) { + return mflp->index; + } + if (bind<1 || bind>maxind) bind=maxind; + return mflp->rbuff[bind-1]; + } + mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); + } + return 0; +} + +void Set_MFVal(uint8_t index,uint8_t bind,float val) { + uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; + for (uint8_t count=0; countnumvals&0x7f; + if (!bind) { + mflp->index=val; + } else { + if (bind<1 || bind>maxind) bind=maxind; + mflp->rbuff[bind-1]=val; + } + return; + } + mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); + } +} + + +float Get_MFilter(uint8_t index) { + uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; + for (uint8_t count=0; countnumvals&0x80) { + + return mflp->maccu/(mflp->numvals&0x7f); + } else { + + return median_array(mflp->rbuff,mflp->numvals); + } + } + mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); + } + return 0; +} + +void Set_MFilter(uint8_t index, float invar) { + uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; + for (uint8_t count=0; countnumvals&0x80) { + + mflp->maccu-=mflp->rbuff[mflp->index]; + mflp->maccu+=invar; + mflp->rbuff[mflp->index]=invar; + mflp->index++; + if (mflp->index>=(mflp->numvals&0x7f)) mflp->index=0; + } else { + + mflp->rbuff[mflp->index]=invar; + mflp->index++; + if (mflp->index>=mflp->numvals) mflp->index=0; + } + break; + } + mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); + } +} + +#define MEDIAN_SIZE 5 +#define MEDIAN_FILTER_NUM 2 + +struct MEDIAN_FILTER { +float buffer[MEDIAN_SIZE]; +int8_t index; +} script_mf[MEDIAN_FILTER_NUM]; + +float DoMedian5(uint8_t index, float in) { + + if (index>=MEDIAN_FILTER_NUM) index=0; + + struct MEDIAN_FILTER* mf=&script_mf[index]; + mf->buffer[mf->index]=in; + mf->index++; + if (mf->index>=MEDIAN_SIZE) mf->index=0; + return median_array(mf->buffer,MEDIAN_SIZE); +} + +#ifdef USE_LIGHT + +uint32_t HSVToRGB(uint16_t hue, uint8_t saturation, uint8_t value) { +float r = 0, g = 0, b = 0; +struct HSV { + float H; + float S; + float V; +} hsv; + +hsv.H=hue; +hsv.S=(float)saturation/100.0; +hsv.V=(float)value/100.0; + +if (hsv.S == 0) { + r = hsv.V; + g = hsv.V; + b = hsv.V; + } else { + int i; + float f, p, q, t; + + if (hsv.H == 360) + hsv.H = 0; + else + hsv.H = hsv.H / 60; + + i = (int)trunc(hsv.H); + f = hsv.H - i; + + p = hsv.V * (1.0 - hsv.S); + q = hsv.V * (1.0 - (hsv.S * f)); + t = hsv.V * (1.0 - (hsv.S * (1.0 - f))); + + switch (i) + { + case 0: + r = hsv.V; + g = t; + b = p; + break; + + case 1: + r = q; + g = hsv.V; + b = p; + break; + + case 2: + r = p; + g = hsv.V; + b = t; + break; + + case 3: + r = p; + g = q; + b = hsv.V; + break; + + case 4: + r = t; + g = p; + b = hsv.V; + break; + + default: + r = hsv.V; + g = p; + b = q; + break; + } + + } + + uint8_t ir,ig,ib; + ir=r*255; + ig=g*255; + ib=b*255; + + uint32_t rgb=(ir<<16)|(ig<<8)|ib; + return rgb; +} +#endif + + + + +char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,JsonObject *jo) { + uint16_t count,len=0; + uint8_t nres=0; + char vname[32]; + float fvar=0; + tind->index=0; + tind->bits.data=0; + + if (isdigit(*lp) || (*lp=='-' && isdigit(*(lp+1))) || *lp=='.') { + + if (fp) { + if (*lp=='0' && *(lp+1)=='x') { + lp+=2; + *fp=strtol(lp,0,16); + } else { + *fp=CharToFloat(lp); + } + } + if (*lp=='-') lp++; + while (isdigit(*lp) || *lp=='.') { + if (*lp==0 || *lp==SCRIPT_EOL) break; + lp++; + } + tind->bits.constant=1; + tind->bits.is_string=0; + *vtype=NUM_RES; + return lp; + } + if (*lp=='"') { + lp++; + while (*lp!='"') { + if (*lp==0 || *lp==SCRIPT_EOL) break; + uint8_t iob=*lp; + if (iob=='\\') { + lp++; + if (*lp=='t') { + iob='\t'; + } else if (*lp=='n') { + iob='\n'; + } else if (*lp=='r') { + iob='\r'; + } else if (*lp=='\\') { + iob='\\'; + } else { + lp--; + } + if (sp) *sp++=iob; + } else { + if (sp) *sp++=iob; + } + lp++; + } + if (sp) *sp=0; + *vtype=STR_RES; + tind->bits.constant=1; + tind->bits.is_string=1; + return lp+1; + } + + if (*lp=='-') { + + nres=1; + lp++; + } + + const char *term="\n\r ])=+-/*%>index=VAR_NV; + glob_script_mem.var_not_found=1; + return lp; + } + + struct T_INDEX *vtp=glob_script_mem.type; + char dvnam[32]; + strcpy (dvnam,vname); + uint8_t olen=len; + last_findex=-1; + char *ja=strchr(dvnam,'['); + if (ja) { + *ja=0; + ja++; + olen=strlen(dvnam); + } + for (count=0; countindex=count; + if (vtp[count].bits.is_string==0) { + *vtype=NTYPE|index; + if (vtp[count].bits.is_filter) { + if (ja) { + lp+=olen+1; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + last_findex=fvar; + fvar=Get_MFVal(index,fvar); + len=1; + } else { + fvar=Get_MFilter(index); + } + } else { + fvar=glob_script_mem.fvars[index]; + } + if (nres) fvar=-fvar; + if (fp) *fp=fvar; + } else { + *vtype=STYPE|index; + if (sp) strlcpy(sp,glob_script_mem.glob_snp+(index*glob_script_mem.max_ssize),SCRIPT_MAXSSIZE); + } + return lp+len; + } + } + } + + if (jo) { + + const char* str_value; + uint8_t aindex; + String vn; + char *ja=strchr(vname,'['); + if (ja) { + + *ja=0; + ja++; + + float fvar; + GetNumericResult(ja,OPER_EQU,&fvar,0); + aindex=fvar; + if (aindex<1 || aindex>6) aindex=1; + aindex--; + } + if (jo->success()) { + char *subtype=strchr(vname,'#'); + char *subtype2; + if (subtype) { + *subtype=0; + subtype++; + subtype2=strchr(subtype,'#'); + if (subtype2) { + *subtype2=0; + *subtype2++; + } + } + vn=vname; + str_value = (*jo)[vn]; + if ((*jo)[vn].success()) { + if (subtype) { + JsonObject &jobj1=(*jo)[vn]; + if (jobj1.success()) { + vn=subtype; + jo=&jobj1; + str_value = (*jo)[vn]; + if ((*jo)[vn].success()) { + + if (subtype2) { + JsonObject &jobj2=(*jo)[vn]; + if ((*jo)[vn].success()) { + vn=subtype2; + jo=&jobj2; + str_value = (*jo)[vn]; + if ((*jo)[vn].success()) { + goto skip; + } else { + goto chknext; + } + } else { + goto chknext; + } + } + + goto skip; + } + } else { + goto chknext; + } + } + skip: + if (ja) { + + str_value = (*jo)[vn][aindex]; + } + if (str_value && *str_value) { + if ((*jo).is(vn)) { + if (!strncmp(str_value,"ON",2)) { + if (fp) *fp=1; + goto nexit; + } else if (!strncmp(str_value,"OFF",3)) { + if (fp) *fp=0; + goto nexit; + } else { + *vtype=STR_RES; + tind->bits.constant=1; + tind->bits.is_string=1; + if (sp) strlcpy(sp,str_value,SCRIPT_MAXSSIZE); + return lp+len; + } + + } else { + if (fp) { + if (!strncmp(vn.c_str(),"Epoch",5)) { + *fp=atoi(str_value)-(uint32_t)EPOCH_OFFSET; + } else { + *fp=CharToFloat((char*)str_value); + } + } + nexit: + *vtype=NUM_RES; + tind->bits.constant=1; + tind->bits.is_string=0; + return lp+len; + } + } + } + } + } + +chknext: + switch (vname[0]) { + case 'a': +#ifdef USE_ANGLE_FUNC + if (!strncmp(vname,"acos(",5)) { + lp+=5; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + fvar=acosf(fvar); + lp++; + len=0; + goto exit; + } +#endif + break; + + case 'b': + if (!strncmp(vname,"boot",4)) { + if (rules_flag.system_boot) { + rules_flag.system_boot=0; + fvar=1; + } + goto exit; + } +#ifdef USE_BUTTON_EVENT + if (!strncmp(vname,"bt[",3)) { + + GetNumericResult(vname+3,OPER_EQU,&fvar,0); + uint32_t index=fvar; + if (index<1 || index>MAX_KEYS) index=1; + fvar=script_button[index-1]; + script_button[index-1]|=0x80; + len++; + goto exit; + } +#endif + break; + case 'c': + if (!strncmp(vname,"chg[",4)) { + + struct T_INDEX ind; + uint8_t vtype; + isvar(vname+4,&vtype,&ind,0,0,0); + if (!ind.bits.constant) { + uint8_t index=glob_script_mem.type[ind.index].index; + if (glob_script_mem.fvars[index]!=glob_script_mem.s_fvars[index]) { + + glob_script_mem.s_fvars[index]=glob_script_mem.fvars[index]; + fvar=1; + len++; + goto exit; + } else { + fvar=0; + len++; + goto exit; + } + } + } + break; + case 'd': + if (!strncmp(vname,"day",3)) { + fvar=RtcTime.day_of_month; + goto exit; + } + break; + case 'e': + if (!strncmp(vname,"epoch",5)) { + fvar=UtcTime()-(uint32_t)EPOCH_OFFSET; + goto exit; + } + break; +#ifdef USE_SCRIPT_FATFS + case 'f': + if (!strncmp(vname,"fo(",3)) { + lp+=3; + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,0); + while (*lp==' ') lp++; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + uint8_t mode=fvar; + fvar=-1; + for (uint8_t cnt=0;cnt=SFS_MAX) ind=SFS_MAX-1; + glob_script_mem.files[ind].close(); + glob_script_mem.file_flags[ind].is_open=0; + fvar=0; + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"ff(",3)) { + lp+=3; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + uint8_t ind=fvar; + if (ind>=SFS_MAX) ind=SFS_MAX-1; + glob_script_mem.files[ind].flush(); + fvar=0; + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"fw(",3)) { + lp+=3; + char str[SCRIPT_MAXSSIZE]; + lp=ForceStringVar(lp,str); + while (*lp==' ') lp++; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + uint8_t ind=fvar; + if (ind>=SFS_MAX) ind=SFS_MAX-1; + if (glob_script_mem.file_flags[ind].is_open) { + fvar=glob_script_mem.files[ind].print(str); + } else { + fvar=0; + } + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"fr(",3)) { + lp+=3; + struct T_INDEX ind; + uint8_t vtype; + uint8_t sindex=0; + lp=isvar(lp,&vtype,&ind,0,0,0); + if (vtype!=VAR_NV) { + + if ((vtype&STYPE)==0) { + + fvar=0; + goto exit; + } else { + + sindex=glob_script_mem.type[ind.index].index; + } + } else { + + fvar=0; + goto exit; + } + while (*lp==' ') lp++; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + uint8_t find=fvar; + if (find>=SFS_MAX) find=SFS_MAX-1; + uint8_t index=0; + char str[glob_script_mem.max_ssize+1]; + char *cp=str; + if (glob_script_mem.file_flags[find].is_open) { + if (glob_script_mem.file_flags[find].is_dir) { + while (true) { + File entry=glob_script_mem.files[find].openNextFile(); + if (entry) { + if (!reject((char*)entry.name())) { + strcpy(str,entry.name()); + entry.close(); + break; + } + } else { + *cp=0; + break; + } + entry.close(); + } + index=strlen(str); + } else { + while (glob_script_mem.files[find].available()) { + uint8_t buf[1]; + glob_script_mem.files[find].read(buf,1); + if (buf[0]=='\t' || buf[0]==',' || buf[0]=='\n' || buf[0]=='\r') { + break; + } else { + *cp++=buf[0]; + index++; + if (index>=glob_script_mem.max_ssize-1) break; + } + } + *cp=0; + } + } else { + strcpy(str,"file error"); + } + lp++; + strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); + fvar=index; + len=0; + goto exit; + } + if (!strncmp(vname,"fd(",3)) { + lp+=3; + char str[glob_script_mem.max_ssize+1]; + lp=GetStringResult(lp,OPER_EQU,str,0); + SD.remove(str); + lp++; + len=0; + goto exit; + } +#ifdef USE_SCRIPT_FATFS_EXT + if (!strncmp(vname,"fe(",3)) { + lp+=3; + char str[glob_script_mem.max_ssize+1]; + lp=GetStringResult(lp,OPER_EQU,str,0); + + File ef=SD.open(str); + if (ef) { + uint16_t fsiz=ef.size(); + if (fsiz<2048) { + char *script=(char*)calloc(fsiz+16,1); + if (script) { + ef.read((uint8_t*)script,fsiz); + execute_script(script); + free(script); + fvar=1; + } + } + ef.close(); + } + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"fmd(",4)) { + lp+=4; + char str[glob_script_mem.max_ssize+1]; + lp=GetStringResult(lp,OPER_EQU,str,0); + fvar=SD.mkdir(str); + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"frd(",4)) { + lp+=4; + char str[glob_script_mem.max_ssize+1]; + lp=GetStringResult(lp,OPER_EQU,str,0); + fvar=SD.rmdir(str); + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"fx(",3)) { + lp+=3; + char str[glob_script_mem.max_ssize+1]; + lp=GetStringResult(lp,OPER_EQU,str,0); + if (SD.exists(str)) fvar=1; + else fvar=0; + lp++; + len=0; + goto exit; + } +#endif + if (!strncmp(vname,"fl1(",4) || !strncmp(vname,"fl2(",4) ) { + uint8_t lknum=*(lp+2)&3; + lp+=4; + char str[glob_script_mem.max_ssize+1]; + lp=GetStringResult(lp,OPER_EQU,str,0); + if (lknum<1 || lknum>2) lknum=1; + strlcpy(glob_script_mem.flink[lknum-1],str,14); + lp++; + fvar=0; + len=0; + goto exit; + } + if (!strncmp(vname,"fsm",3)) { + fvar=glob_script_mem.script_sd_found; + + goto exit; + } + break; + +#endif + case 'g': + if (!strncmp(vname,"gtmp",4)) { + fvar=global_temperature; + goto exit; + } + if (!strncmp(vname,"ghum",4)) { + fvar=global_humidity; + goto exit; + } + if (!strncmp(vname,"gprs",4)) { + fvar=global_pressure; + goto exit; + } + if (!strncmp(vname,"gtopic",6)) { + if (sp) strlcpy(sp,SettingsText(SET_MQTT_GRP_TOPIC),glob_script_mem.max_ssize); + goto strexit; + } + break; + case 'h': + if (!strncmp(vname,"hours",5)) { + fvar=RtcTime.hour; + goto exit; + } + if (!strncmp(vname,"heap",4)) { + fvar=ESP.getFreeHeap(); + goto exit; + } + if (!strncmp(vname,"hn(",3)) { + lp=GetNumericResult(lp+3,OPER_EQU,&fvar,0); + if (fvar<0 || fvar>255) fvar=0; + lp++; + len=0; + if (sp) { + sprintf(sp,"%02x",(uint8_t)fvar); + } + goto strexit; + } + if (!strncmp(vname,"hx(",3)) { + lp=GetNumericResult(lp+3,OPER_EQU,&fvar,0); + lp++; + len=0; + if (sp) { + sprintf(sp,"%08x",(uint32_t)fvar); + } + goto strexit; + } +#ifdef USE_LIGHT + + if (!strncmp(vname,"hsvrgb(",7)) { + lp=GetNumericResult(lp+7,OPER_EQU,&fvar,0); + if (fvar<0 || fvar>360) fvar=0; + SCRIPT_SKIP_SPACES + + float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + if (fvar2<0 || fvar2>100) fvar2=0; + SCRIPT_SKIP_SPACES + + float fvar3; + lp=GetNumericResult(lp,OPER_EQU,&fvar3,0); + if (fvar3<0 || fvar3>100) fvar3=0; + + fvar=HSVToRGB(fvar,fvar2,fvar3); + + lp++; + len=0; + goto exit; + } + +#endif + break; + case 'i': + if (!strncmp(vname,"int(",4)) { + lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0); + fvar=floor(fvar); + lp++; + len=0; + goto exit; + } + break; + case 'l': + if (!strncmp(vname,"loglvl",6)) { + fvar=glob_script_mem.script_loglevel; + tind->index=SCRIPT_LOGLEVEL; + exit_settable: + if (fp) *fp=fvar; + *vtype=NTYPE; + tind->bits.settable=1; + tind->bits.is_string=0; + return lp+len; + } + break; + case 'm': + if (!strncmp(vname,"med(",4)) { + float fvar1; + lp=GetNumericResult(lp+4,OPER_EQU,&fvar1,0); + SCRIPT_SKIP_SPACES + + float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + fvar=DoMedian5(fvar1,fvar2); + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"micros",6)) { + fvar=micros(); + goto exit; + } + if (!strncmp(vname,"millis",6)) { + fvar=millis(); + goto exit; + } + if (!strncmp(vname,"mins",4)) { + fvar=RtcTime.minute; + goto exit; + } + if (!strncmp(vname,"month",5)) { + fvar=RtcTime.month; + goto exit; + } + if (!strncmp(vname,"mqttc",5)) { + if (rules_flag.mqtt_connected) { + rules_flag.mqtt_connected=0; + fvar=1; + } + goto exit; + } + if (!strncmp(vname,"mqttd",5)) { + if (rules_flag.mqtt_disconnected) { + rules_flag.mqtt_disconnected=0; + fvar=1; + } + goto exit; + } + if (!strncmp(vname,"mqtts",5)) { + fvar=!global_state.mqtt_down; + goto exit; + } + if (!strncmp(vname,"mp(",3)) { + lp+=3; + float fvar1; + lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); + SCRIPT_SKIP_SPACES + while (*lp!=')') { + char *opp=lp; + lp++; + float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + SCRIPT_SKIP_SPACES + fvar=fvar1; + if ((*opp=='<' && fvar1' && fvar1>fvar2) || + (*opp=='=' && fvar1==fvar2)) + { + if (*lp!='<' && *lp!='>' && *lp!='=' && *lp!=')' && *lp!=SCRIPT_EOL) { + float fvar3; + lp=GetNumericResult(lp,OPER_EQU,&fvar3,0); + SCRIPT_SKIP_SPACES + fvar=fvar3; + } else { + fvar=fvar2; + } + break; + } + while (*lp!='<' && *lp!='>' && *lp!='=' && *lp!=')' && *lp!=SCRIPT_EOL) lp++; + } + len=0; + goto exit; + } + break; + case 'p': + if (!strncmp(vname,"pin[",4)) { + + GetNumericResult(vname+4,OPER_EQU,&fvar,0); + fvar=digitalRead((uint8_t)fvar); + + len++; + goto exit; + } + if (!strncmp(vname,"pn[",3)) { + GetNumericResult(vname+3,OPER_EQU,&fvar,0); + fvar=pin[(uint8_t)fvar]; + + len++; + goto exit; + } + if (!strncmp(vname,"pd[",3)) { + GetNumericResult(vname+3,OPER_EQU,&fvar,0); + uint8_t gpiopin=fvar; + for (uint8_t i=0;iMAX_COUNTERS) index=1; + fvar=RtcSettings.pulse_counter[index-1]; + len+=1; + goto exit; + } + break; + + case 'r': + if (!strncmp(vname,"ram",3)) { + fvar=glob_script_mem.script_mem_size+(glob_script_mem.script_size)+(PMEM_SIZE); + goto exit; + } + break; + case 's': + if (!strncmp(vname,"secs",4)) { + fvar=RtcTime.second; + goto exit; + } + if (!strncmp(vname,"sw[",3)) { + + GetNumericResult(vname+3,OPER_EQU,&fvar,0); + fvar=SwitchLastState((uint32_t)fvar); + + len++; + goto exit; + } + if (!strncmp(vname,"stack",5)) { + fvar=GetStack(); + goto exit; + } + if (!strncmp(vname,"slen",4)) { + fvar=strlen(glob_script_mem.script_ram); + goto exit; + } + if (!strncmp(vname,"sl(",3)) { + lp+=3; + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,0); + lp++; + len=0; + fvar=strlen(str); + goto exit; + } + if (!strncmp(vname,"sb(",3)) { + lp+=3; + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,0); + SCRIPT_SKIP_SPACES + float fvar1; + lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); + SCRIPT_SKIP_SPACES + float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + lp++; + len=0; + if (fvar1<0) { + fvar1=strlen(str)+fvar1; + } + memcpy(sp,&str[(uint8_t)fvar1],(uint8_t)fvar2); + sp[(uint8_t)fvar2] = '\0'; + goto strexit; + } + if (!strncmp(vname,"st(",3)) { + lp+=3; + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,0); + while (*lp==' ') lp++; + char token[2]; + token[0]=*lp++; + token[1]=0; + while (*lp==' ') lp++; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + + lp++; + len=0; + if (sp) { + + char *st=strtok(str,token); + if (!st) { + *sp=0; + } else { + for (uint8_t cnt=1; cnt<=fvar; cnt++) { + if (cnt==fvar) { + strcpy(sp,st); + break; + } + st=strtok(NULL,token); + if (!st) { + *sp=0; + break; + } + } + } + } + goto strexit; + } + if (!strncmp(vname,"s(",2)) { + lp+=2; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + char str[glob_script_mem.max_ssize+1]; + dtostrfd(fvar,glob_script_mem.script_dprec,str); + if (sp) strlcpy(sp,str,glob_script_mem.max_ssize); + lp++; + len=0; + goto strexit; + } +#if defined(USE_TIMERS) && defined(USE_SUNRISE) + if (!strncmp(vname,"sunrise",7)) { + fvar=SunMinutes(0); + goto exit; + } + if (!strncmp(vname,"sunset",6)) { + fvar=SunMinutes(1); + goto exit; + } +#endif + +#ifdef USE_SHUTTER + if (!strncmp(vname,"sht[",4)) { + GetNumericResult(vname+4,OPER_EQU,&fvar,0); + uint8_t index=fvar; + if (index<=shutters_present) { + fvar=Settings.shutter_position[index-1]; + } else { + fvar=-1; + } + len+=1; + goto exit; + } +#endif +#ifdef USE_ANGLE_FUNC + if (!strncmp(vname,"sin(",4)) { + lp+=4; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + fvar=sinf(fvar); + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"sqrt(",5)) { + lp+=5; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + fvar=sqrtf(fvar); + lp++; + len=0; + goto exit; + } +#endif +#ifdef USE_SML_SCRIPT_CMD + if (!strncmp(vname,"sml(",4)) { + lp+=4; + float fvar1; + lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); + SCRIPT_SKIP_SPACES + float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + SCRIPT_SKIP_SPACES + if (fvar2==0) { + float fvar3; + lp=GetNumericResult(lp,OPER_EQU,&fvar3,0); + fvar=SML_SetBaud(fvar1,fvar3); + } else if (fvar2==1) { + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,0); + fvar=SML_Write(fvar1,str); + } else { +#ifdef ED300L + fvar=SML_Status(fvar1); +#else + fvar=0; +#endif + } + lp++; + len=0; + goto exit; + } +#endif + break; + case 't': + if (!strncmp(vname,"time",4)) { + fvar=MinutesPastMidnight(); + goto exit; + } + if (!strncmp(vname,"tper",4)) { + fvar=Settings.tele_period; + tind->index=SCRIPT_TELEPERIOD; + goto exit_settable; + } + if (!strncmp(vname,"tinit",5)) { + if (rules_flag.time_init) { + rules_flag.time_init=0; + fvar=1; + } + goto exit; + } + if (!strncmp(vname,"tset",4)) { + if (rules_flag.time_set) { + rules_flag.time_set=0; + fvar=1; + } + goto exit; + } + if (!strncmp(vname,"tstamp",6)) { + if (sp) strlcpy(sp,GetDateAndTime(DT_LOCAL).c_str(),glob_script_mem.max_ssize); + goto strexit; + } + if (!strncmp(vname,"topic",5)) { + if (sp) strlcpy(sp,SettingsText(SET_MQTT_TOPIC),glob_script_mem.max_ssize); + goto strexit; + } +#ifdef USE_DISPLAY +#ifdef USE_TOUCH_BUTTONS + if (!strncmp(vname,"tbut[",5)) { + GetNumericResult(vname+5,OPER_EQU,&fvar,0); + uint8_t index=fvar; + if (index<1 || index>MAXBUTTONS) index=1; + index--; + if (buttons[index]) { + fvar=buttons[index]->vpower&0x80; + } else { + fvar=-1; + } + len+=1; + goto exit; + } + +#endif +#endif + break; + case 'u': + if (!strncmp(vname,"uptime",6)) { + fvar=MinutesUptime(); + goto exit; + } + if (!strncmp(vname,"upsecs",6)) { + fvar=uptime; + goto exit; + } + if (!strncmp(vname,"upd[",4)) { + + struct T_INDEX ind; + uint8_t vtype; + isvar(vname+4,&vtype,&ind,0,0,0); + if (!ind.bits.constant) { + if (!ind.bits.changed) { + fvar=0; + len++; + goto exit; + } else { + glob_script_mem.type[ind.index].bits.changed=0; + fvar=1; + len++; + goto exit; + } + } + goto notfound; + } + break; + + case 'w': + if (!strncmp(vname,"wday",4)) { + fvar=RtcTime.day_of_week; + goto exit; + } + if (!strncmp(vname,"wific",5)) { + if (rules_flag.wifi_connected) { + rules_flag.wifi_connected=0; + fvar=1; + } + goto exit; + } + if (!strncmp(vname,"wifid",5)) { + if (rules_flag.wifi_disconnected) { + rules_flag.wifi_disconnected=0; + fvar=1; + } + goto exit; + } + if (!strncmp(vname,"wifis",5)) { + fvar=!global_state.wifi_down; + goto exit; + } + break; + case 'y': + if (!strncmp(vname,"year",4)) { + fvar=RtcTime.year; + goto exit; + } + break; + default: + break; + } + +notfound: + if (fp) *fp=0; + *vtype=VAR_NV; + tind->index=VAR_NV; + glob_script_mem.var_not_found=1; + return lp; + +exit: + if (fp) *fp=fvar; + *vtype=NUM_RES; + tind->bits.constant=1; + tind->bits.is_string=0; + return lp+len; + +strexit: + *vtype=STYPE; + tind->bits.constant=1; + tind->bits.is_string=1; + return lp+len; +} + + + +char *getop(char *lp, uint8_t *operand) { + switch (*lp) { + case '=': + if (*(lp+1)=='=') { + *operand=OPER_EQUEQU; + return lp+2; + } else { + *operand=OPER_EQU; + return lp+1; + } + break; + case '+': + if (*(lp+1)=='=') { + *operand=OPER_PLSEQU; + return lp+2; + } else { + *operand=OPER_PLS; + return lp+1; + } + break; + case '-': + if (*(lp+1)=='=') { + *operand=OPER_MINEQU; + return lp+2; + } else { + *operand=OPER_MIN; + return lp+1; + } + break; + case '*': + if (*(lp+1)=='=') { + *operand=OPER_MULEQU; + return lp+2; + } else { + *operand=OPER_MUL; + return lp+1; + } + break; + case '/': + if (*(lp+1)=='=') { + *operand=OPER_DIVEQU; + return lp+2; + } else { + *operand=OPER_DIV; + return lp+1; + } + break; + case '!': + if (*(lp+1)=='=') { + *operand=OPER_NOTEQU; + return lp+2; + } + break; + case '>': + if (*(lp+1)=='=') { + *operand=OPER_GRTEQU; + return lp+2; + } else { + *operand=OPER_GRT; + return lp+1; + + } + break; + case '<': + if (*(lp+1)=='=') { + *operand=OPER_LOWEQU; + return lp+2; + } else { + *operand=OPER_LOW; + return lp+1; + } + break; + case '%': + if (*(lp+1)=='=') { + *operand=OPER_PERCEQU; + return lp+2; + } else { + *operand=OPER_PERC; + return lp+1; + } + break; + case '^': + if (*(lp+1)=='=') { + *operand=OPER_XOREQU; + return lp+2; + } else { + *operand=OPER_XOR; + return lp+1; + } + break; + case '&': + if (*(lp+1)=='=') { + *operand=OPER_ANDEQU; + return lp+2; + } else { + *operand=OPER_AND; + return lp+1; + } + break; + case '|': + if (*(lp+1)=='=') { + *operand=OPER_OREQU; + return lp+2; + } else { + *operand=OPER_OR; + return lp+1; + } + break; + } + *operand=0; + return lp; +} + + +#ifdef ESP8266 +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) + + +extern "C" { +#include + extern cont_t g_cont; +} +uint16_t GetStack(void) { + register uint32_t *sp asm("a1"); + return (4 * (sp - g_cont.stack)); +} + +#else +extern "C" { +#include + extern cont_t* g_pcont; +} +uint16_t GetStack(void) { + register uint32_t *sp asm("a1"); + return (4 * (sp - g_pcont->stack)); +} +#endif +#else +uint16_t GetStack(void) { + register uint8_t *sp asm("a1"); + return (sp - pxTaskGetStackStart(NULL)); +} +#endif + +char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo) { + uint8_t operand=0; + uint8_t vtype; + char *slp; + struct T_INDEX ind; + char str[SCRIPT_MAXSSIZE],str1[SCRIPT_MAXSSIZE]; + while (1) { + lp=isvar(lp,&vtype,&ind,0,str1,jo); + if (vtype!=STR_RES && !(vtype&STYPE)) { + + glob_script_mem.glob_error=1; + return lp; + } + switch (lastop) { + case OPER_EQU: + strlcpy(str,str1,sizeof(str)); + break; + case OPER_PLS: + strncat(str,str1,sizeof(str)); + break; + } + slp=lp; + lp=getop(lp,&operand); + switch (operand) { + case OPER_EQUEQU: + case OPER_NOTEQU: + case OPER_LOW: + case OPER_LOWEQU: + case OPER_GRT: + case OPER_GRTEQU: + lp=slp; + strcpy(cp,str); + return lp; + break; + default: + break; + } + lastop=operand; + if (!operand) { + strcpy(cp,str); + return lp; + } + } +} + +char *GetNumericResult(char *lp,uint8_t lastop,float *fp,JsonObject *jo) { +uint8_t operand=0; +float fvar1,fvar; +char *slp; +uint8_t vtype; +struct T_INDEX ind; + while (1) { + + if (*lp=='(') { + lp++; + lp=GetNumericResult(lp,OPER_EQU,&fvar1,jo); + lp++; + + } else { + lp=isvar(lp,&vtype,&ind,&fvar1,0,jo); + if (vtype!=NUM_RES && vtype&STYPE) { + + glob_script_mem.glob_error=1; + } + } + switch (lastop) { + case OPER_EQU: + fvar=fvar1; + break; + case OPER_PLS: + fvar+=fvar1; + break; + case OPER_MIN: + fvar-=fvar1; + break; + case OPER_MUL: + fvar*=fvar1; + break; + case OPER_DIV: + fvar/=fvar1; + break; + case OPER_PERC: + fvar=fmodf(fvar,fvar1); + break; + case OPER_XOR: + fvar=(uint32_t)fvar^(uint32_t)fvar1; + break; + case OPER_AND: + fvar=(uint32_t)fvar&(uint32_t)fvar1; + break; + case OPER_OR: + fvar=(uint32_t)fvar|(uint32_t)fvar1; + break; + default: + break; + + } + slp=lp; + lp=getop(lp,&operand); + switch (operand) { + case OPER_EQUEQU: + case OPER_NOTEQU: + case OPER_LOW: + case OPER_LOWEQU: + case OPER_GRT: + case OPER_GRTEQU: + lp=slp; + *fp=fvar; + return lp; + break; + default: + break; + } + lastop=operand; + if (!operand) { + *fp=fvar; + return lp; + } + } +} + + +char *ForceStringVar(char *lp,char *dstr) { + float fvar; + char *slp=lp; + glob_script_mem.glob_error=0; + lp=GetStringResult(lp,OPER_EQU,dstr,0); + if (glob_script_mem.glob_error) { + + lp=GetNumericResult(slp,OPER_EQU,&fvar,0); + dtostrfd(fvar,6,dstr); + glob_script_mem.glob_error=0; + } + return lp; +} + + +void Replace_Cmd_Vars(char *srcbuf,char *dstbuf,uint16_t dstsize) { + char *cp; + uint16_t count; + uint8_t vtype; + uint8_t dprec=glob_script_mem.script_dprec; + float fvar; + cp=srcbuf; + struct T_INDEX ind; + char string[SCRIPT_MAXSSIZE]; + dstsize-=2; + for (count=0;count=sizeof(str)) len=len>=sizeof(str); + strlcpy(str,cp,len); + toSLog(str); +} + +void toLogEOL(const char *s1,const char *str) { + if (!str) return; + uint8_t index=0; + char *cp=log_data; + strcpy(cp,s1); + cp+=strlen(s1); + while (*str) { + if (*str==SCRIPT_EOL) break; + *cp++=*str++; + } + *cp=0; + AddLog(LOG_LEVEL_INFO); +} + + +void toSLog(const char *str) { + if (!str) return; +#if SCRIPT_DEBUG>0 + while (*str) { + Serial.write(*str); + str++; + } +#endif +} + +char *Evaluate_expression(char *lp,uint8_t and_or, uint8_t *result,JsonObject *jo) { + float fvar,*dfvar,fvar1; + uint8_t numeric; + struct T_INDEX ind; + uint8_t vtype=0,lastop; + uint8_t res=0; + char *llp=lp; + char *slp; + + SCRIPT_SKIP_SPACES + if (*lp=='(') { + uint8_t res=0; + uint8_t xand_or=0; + lp++; + +loop: + SCRIPT_SKIP_SPACES + lp=Evaluate_expression(lp,xand_or,&res,jo); + if (*lp==')') { + lp++; + goto exit0; + } + + SCRIPT_SKIP_SPACES + if (!strncmp(lp,"or",2)) { + lp+=2; + xand_or=1; + goto loop; + } else if (!strncmp(lp,"and",3)) { + lp+=3; + xand_or=2; + goto loop; + } +exit0: + if (!and_or) { + *result=res; + } else if (and_or==1) { + *result|=res; + } else { + *result&=res; + } + goto exit10; + } + + llp=lp; + + dfvar=&fvar; + glob_script_mem.glob_error=0; + slp=lp; + numeric=1; + lp=GetNumericResult(lp,OPER_EQU,dfvar,0); + if (glob_script_mem.glob_error==1) { + + char cmpstr[SCRIPT_MAXSSIZE]; + lp=slp; + numeric=0; + + lp=isvar(lp,&vtype,&ind,0,cmpstr,0); + lp=getop(lp,&lastop); + + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,jo); + if (lastop==OPER_EQUEQU || lastop==OPER_NOTEQU) { + res=strcmp(cmpstr,str); + if (lastop==OPER_EQUEQU) res=!res; + goto exit; + } + + } else { + + + lp=getop(lp,&lastop); + lp=GetNumericResult(lp,OPER_EQU,&fvar1,jo); + switch (lastop) { + case OPER_EQUEQU: + res=(*dfvar==fvar1); + break; + case OPER_NOTEQU: + res=(*dfvar!=fvar1); + break; + case OPER_LOW: + res=(*dfvarfvar1); + break; + case OPER_GRTEQU: + res=(*dfvar>=fvar1); + break; + default: + + break; + } + +exit: + if (!and_or) { + *result=res; + } else if (and_or==1) { + *result|=res; + } else { + *result&=res; + } + } + + +exit10: +#if SCRIPT_DEBUG>0 + char tbuff[128]; + sprintf(tbuff,"p1=%d,p2=%d,cmpres=%d,and_or=%d line: ",(int32_t)*dfvar,(int32_t)fvar1,*result,and_or); + toLogEOL(tbuff,llp); +#endif + return lp; +} + + + +#define IF_NEST 8 + +int16_t Run_Scripter(const char *type, int8_t tlen, char *js) { + + if (tasm_cmd_activ && tlen>0) return 0; + + uint8_t vtype=0,sindex,xflg,floop=0,globvindex,fromscriptcmd=0; + int8_t globaindex; + struct T_INDEX ind; + uint8_t operand,lastop,numeric=1,if_state[IF_NEST],if_exe[IF_NEST],if_result[IF_NEST],and_or,ifstck=0; + if_state[ifstck]=0; + if_result[ifstck]=0; + if_exe[ifstck]=1; + char cmpstr[SCRIPT_MAXSSIZE]; + uint8_t check=0; + if (tlen<0) { + tlen=abs(tlen); + check=1; + } + + float *dfvar,*cv_count,cv_max,cv_inc; + char *cv_ptr; + float fvar=0,fvar1,sysvar,swvar; + uint8_t section=0,sysv_type=0,swflg=0; + + if (!glob_script_mem.scriptptr) { + return -99; + } + + DynamicJsonBuffer jsonBuffer; + JsonObject &jobj=jsonBuffer.parseObject(js); + JsonObject *jo; + if (js) jo=&jobj; + else jo=0; + + char *lp=glob_script_mem.scriptptr; + + while (1) { + + + startline: + SCRIPT_SKIP_SPACES + + SCRIPT_SKIP_EOL + + if (*lp==';') goto next_line; + if (!*lp) break; + + if (section) { + + if (*lp=='>') { + return 0; + } + if (*lp=='#') { + return 0; + } + glob_script_mem.var_not_found=0; + + +#ifdef IFTHEN_DEBUG + char tbuff[128]; + sprintf(tbuff,"stack=%d,exe=%d,state=%d,cmpres=%d line: ",ifstck,if_exe[ifstck],if_state[ifstck],if_result[ifstck]); + toLogEOL(tbuff,lp); +#endif + + + + + if (!strncmp(lp,"if",2)) { + lp+=2; + if (ifstck=2) { + lp+=5; + if (ifstck>0) { + if_state[ifstck]=0; + ifstck--; + } + goto next_line; + } else if (!strncmp(lp,"or",2) && if_state[ifstck]==1) { + lp+=2; + and_or=1; + } else if (!strncmp(lp,"and",3) && if_state[ifstck]==1) { + lp+=3; + and_or=2; + } + + if (*lp=='{' && if_state[ifstck]==1) { + lp+=1; + if_state[ifstck]=2; + if (if_exe[ifstck-1]) if_exe[ifstck]=if_result[ifstck]; + } else if (*lp=='{' && if_state[ifstck]==3) { + lp+=1; + + } else if (*lp=='}' && if_state[ifstck]>=2) { + lp++; + char *slp=lp; + uint8_t iselse=0; + for (uint8_t count=0; count<8;count++) { + if (*lp=='}') { + + break; + } + if (!strncmp(lp,"else",4)) { + + if_state[ifstck]=3; + if (if_exe[ifstck-1]) if_exe[ifstck]=!if_result[ifstck]; + lp+=4; + iselse=1; + SCRIPT_SKIP_SPACES + if (*lp=='{') lp++; + break; + } + lp++; + } + if (!iselse) { + lp=slp; + + if (ifstck>0) { + if_state[ifstck]=0; + ifstck--; + } + goto next_line; + } + } + + if (!strncmp(lp,"for",3)) { + + + lp+=3; + SCRIPT_SKIP_SPACES + lp=isvar(lp,&vtype,&ind,0,0,0); + if ((vtype!=VAR_NV) && (vtype&STYPE)==0) { + + uint8_t index=glob_script_mem.type[ind.index].index; + cv_count=&glob_script_mem.fvars[index]; + SCRIPT_SKIP_SPACES + lp=GetNumericResult(lp,OPER_EQU,cv_count,0); + SCRIPT_SKIP_SPACES + lp=GetNumericResult(lp,OPER_EQU,&cv_max,0); + SCRIPT_SKIP_SPACES + lp=GetNumericResult(lp,OPER_EQU,&cv_inc,0); + + cv_ptr=lp; + floop=1; + } else { + + toLogEOL("for error",lp); + } + } else if (!strncmp(lp,"next",4) && floop>0) { + + *cv_count+=cv_inc; + if (*cv_count<=cv_max) { + lp=cv_ptr; + } else { + lp+=4; + floop=0; + } + } + + if (!strncmp(lp,"switch",6)) { + lp+=6; + SCRIPT_SKIP_SPACES + char *slp=lp; + lp=GetNumericResult(lp,OPER_EQU,&swvar,0); + if (glob_script_mem.glob_error==1) { + + lp=slp; + + lp=isvar(lp,&vtype,&ind,0,cmpstr,0); + swflg=0x81; + } else { + swflg=1; + } + } else if (!strncmp(lp,"case",4) && swflg>0) { + lp+=4; + SCRIPT_SKIP_SPACES + float cvar; + if (!(swflg&0x80)) { + lp=GetNumericResult(lp,OPER_EQU,&cvar,0); + if (swvar!=cvar) { + swflg=2; + } else { + swflg=1; + } + } else { + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,0); + if (!strcmp(cmpstr,str)) { + swflg=0x81; + } else { + swflg=0x82; + } + } + } else if (!strncmp(lp,"ends",4) && swflg>0) { + lp+=4; + swflg=0; + } + if ((swflg&3)==2) goto next_line; + + SCRIPT_SKIP_SPACES + + if (*lp==SCRIPT_EOL) { + goto next_line; + } + + + if (!if_exe[ifstck] && if_state[ifstck]!=1) goto next_line; + +#ifdef IFTHEN_DEBUG + sprintf(tbuff,"stack=%d,exe=%d,state=%d,cmpres=%d execute line: ",ifstck,if_exe[ifstck],if_state[ifstck],if_result[ifstck]); + toLogEOL(tbuff,lp); +#endif + + if (!strncmp(lp,"break",5)) { + if (floop) { + + floop=0; + } else { + section=0; + } + break; + } else if (!strncmp(lp,"dp",2) && isdigit(*(lp+2))) { + lp+=2; + + glob_script_mem.script_dprec=atoi(lp); + goto next_line; + } else if (!strncmp(lp,"delay(",6)) { + lp+=5; + + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + delay(fvar); + goto next_line; + } else if (!strncmp(lp,"spinm(",6)) { + lp+=6; + + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + int8_t pinnr=fvar; + SCRIPT_SKIP_SPACES + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + int8_t mode=fvar; + pinMode(pinnr,mode&3); + goto next_line; + } else if (!strncmp(lp,"spin(",5)) { + lp+=5; + + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + int8_t pinnr=fvar; + SCRIPT_SKIP_SPACES + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + int8_t mode=fvar; + digitalWrite(pinnr,mode&1); + goto next_line; + } else if (!strncmp(lp,"svars(",5)) { + lp+=5; + + Scripter_save_pvars(); + goto next_line; + } +#ifdef USE_LIGHT +#ifdef USE_WS2812 + else if (!strncmp(lp,"ws2812(",7)) { + lp+=7; + lp=isvar(lp,&vtype,&ind,0,0,0); + if (vtype!=VAR_NV) { + + uint8_t index=glob_script_mem.type[ind.index].index; + if ((vtype&STYPE)==0) { + + if (glob_script_mem.type[index].bits.is_filter) { + uint8_t len=0; + float *fa=Get_MFAddr(index,&len); + + if (fa && len) ws2812_set_array(fa,len); + } + } + } + goto next_line; + } +#endif +#endif + + else if (!strncmp(lp,"=>",2) || !strncmp(lp,"->",2) || !strncmp(lp,"+>",2) || !strncmp(lp,"print",5)) { + + uint8_t sflag=0,pflg=0,svmqtt,swll; + if (*lp=='p') { + pflg=1; + lp+=5; + } + else { + if (*lp=='-') sflag=1; + if (*lp=='+') sflag=2; + lp+=2; + } + char *slp=lp; + SCRIPT_SKIP_SPACES + #define SCRIPT_CMDMEM 512 + char *cmdmem=(char*)malloc(SCRIPT_CMDMEM); + if (cmdmem) { + char *cmd=cmdmem; + uint16_t count; + for (count=0; count=0) { + Set_MFVal(glob_script_mem.type[globvindex].index,globaindex,*dfvar); + } else { + Set_MFilter(glob_script_mem.type[globvindex].index,*dfvar); + } + } + + if (sysv_type) { + switch (sysv_type) { + case SCRIPT_LOGLEVEL: + glob_script_mem.script_loglevel=*dfvar; + break; + case SCRIPT_TELEPERIOD: + if (*dfvar<10) *dfvar=10; + if (*dfvar>300) *dfvar=300; + Settings.tele_period=*dfvar; + break; + } + sysv_type=0; + } + } else { + + numeric=0; + sindex=index; + + char str[SCRIPT_MAXSSIZE]; + lp=getop(lp,&lastop); + char *slp=lp; + glob_script_mem.glob_error=0; + lp=GetStringResult(lp,OPER_EQU,str,jo); + if (!js && glob_script_mem.glob_error) { + + lp=GetNumericResult(slp,OPER_EQU,&fvar,0); + dtostrfd(fvar,6,str); + glob_script_mem.glob_error=0; + } + + if (!glob_script_mem.var_not_found) { + + glob_script_mem.type[globvindex].bits.changed=1; + if (lastop==OPER_EQU) { + strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); + } else if (lastop==OPER_PLSEQU) { + strncat(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); + } + } + } + + } + SCRIPT_SKIP_SPACES + if (*lp=='{' && if_state[ifstck]==3) { + lp+=1; + + } + goto next_line; + } + } else { + + if (*lp=='>' && tlen==1) { + + lp++; + section=1; + fromscriptcmd=1; + goto startline; + } + if (!strncmp(lp,type,tlen)) { + + section=1; + glob_script_mem.section_ptr=lp; + if (check) { + return 99; + } + + char *ctype=(char*)type; + if (*ctype=='#') { + + ctype+=tlen; + if (*ctype=='(' && *(lp+tlen)=='(') { + float fparam; + numeric=1; + glob_script_mem.glob_error=0; + GetNumericResult((char*)ctype,OPER_EQU,&fparam,0); + if (glob_script_mem.glob_error==1) { + + numeric=0; + + GetStringResult((char*)ctype+1,OPER_EQU,cmpstr,0); + } + lp+=tlen; + if (*lp=='(') { + + lp++; + lp=isvar(lp,&vtype,&ind,0,0,0); + if (vtype!=VAR_NV) { + + uint8_t index=glob_script_mem.type[ind.index].index; + if ((vtype&STYPE)==0) { + + dfvar=&glob_script_mem.fvars[index]; + if (numeric) { + *dfvar=fparam; + } else { + + *dfvar=CharToFloat(cmpstr); + } + } else { + + sindex=index; + if (!numeric) { + strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),cmpstr,glob_script_mem.max_ssize); + } else { + + dtostrfd(fparam,6,glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize)); + } + } + } + } + } else { + lp+=tlen; + if (*ctype=='(' || (*lp!=SCRIPT_EOL && *lp!='?')) { + + section=0; + } + } + } + } + } + + next_line: + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) { + if (section) { + return 0; + } else { + return -1; + } + } + lp++; + } + } + return -1; +} + +uint8_t script_xsns_index = 0; + + +void ScripterEvery100ms(void) { + + if (Settings.rule_enabled && (uptime > 4)) { + mqtt_data[0] = '\0'; + uint16_t script_tele_period_save = tele_period; + tele_period = 2; + XsnsNextCall(FUNC_JSON_APPEND, script_xsns_index); + tele_period = script_tele_period_save; + if (strlen(mqtt_data)) { + mqtt_data[0] = '{'; + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + Run_Scripter(">T",2, mqtt_data); + } + } + if (fast_script==99) Run_Scripter(">F",2,0); +} + + + + +void Scripter_save_pvars(void) { + int16_t mlen=0; + float *fp=(float*)glob_script_mem.script_pram; + mlen+=sizeof(float); + struct T_INDEX *vtp=glob_script_mem.type; + for (uint8_t count=0; countPMEM_SIZE) { + vtp[count].bits.is_permanent=0; + return; + } + *fp++=glob_script_mem.fvars[index]; + } + } + char *cp=(char*)fp; + for (uint8_t count=0; countPMEM_SIZE) { + vtp[count].bits.is_permanent=0; + return; + } + strcpy(cp,sp); + cp+=slen+1; + } + } +} + + +#ifdef USE_WEBSERVER + +#define WEB_HANDLE_SCRIPT "s10" + +const char S_CONFIGURE_SCRIPT[] PROGMEM = D_CONFIGURE_SCRIPT; + +const char HTTP_BTN_MENU_RULES[] PROGMEM = + "

"; + + +const char HTTP_FORM_SCRIPT[] PROGMEM = + "
 " D_SCRIPT " " + "
"; + +const char HTTP_FORM_SCRIPT1[] PROGMEM = + "
" + "
" + "
" + ""; + +const char HTTP_SCRIPT_FORM_END[] PROGMEM = + "
" + "" + "
"; + +#ifdef USE_SCRIPT_FATFS +const char HTTP_FORM_SCRIPT1c[] PROGMEM = + ""; +#ifdef SDCARD_DIR +const char HTTP_FORM_SCRIPT1d[] PROGMEM = + ""; +#else +const char HTTP_FORM_SCRIPT1d[] PROGMEM = + ""; +#endif + +#ifdef SDCARD_DIR +const char S_SCRIPT_FILE_UPLOAD[] PROGMEM = D_SDCARD_DIR; +#else +const char S_SCRIPT_FILE_UPLOAD[] PROGMEM = D_SDCARD_UPLOAD; +#endif + +const char HTTP_FORM_FILE_UPLOAD[] PROGMEM = +"
" +"
 %s" " "; +const char HTTP_FORM_FILE_UPG[] PROGMEM = +"
" +"

" +"
"; + +const char HTTP_FORM_FILE_UPGb[] PROGMEM = +"
" +"
" +""; + +const char HTTP_FORM_SDC_DIRa[] PROGMEM = +"
"; +const char HTTP_FORM_SDC_DIRb[] PROGMEM = + "
%s    %d
"; +const char HTTP_FORM_SDC_DIRd[] PROGMEM = +"
%s
"; +const char HTTP_FORM_SDC_DIRc[] PROGMEM = +"
"; +const char HTTP_FORM_SDC_HREF[] PROGMEM = +"http://%s/upl?download=%s/%s"; +#endif + + + +#ifdef USE_SCRIPT_FATFS + +#if USE_LONG_FILE_NAMES>0 +#undef REJCMPL +#define REJCMPL 6 +#else +#undef REJCMPL +#define REJCMPL 8 +#endif + +uint8_t reject(char *name) { + + if (*name=='_') return 1; + if (*name=='.') return 1; + +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 + if (!strncasecmp(name,"SPOTLI~1",REJCMPL)) return 1; + if (!strncasecmp(name,"TRASHE~1",REJCMPL)) return 1; + if (!strncasecmp(name,"FSEVEN~1",REJCMPL)) return 1; + if (!strncasecmp(name,"SYSTEM~1",REJCMPL)) return 1; +#else + if (!strcasecmp(name,"SPOTLI~1")) return 1; + if (!strcasecmp(name,"TRASHE~1")) return 1; + if (!strcasecmp(name,"FSEVEN~1")) return 1; + if (!strcasecmp(name,"SYSTEM~1")) return 1; +#endif + return 0; +} + +void ListDir(char *path, uint8_t depth) { + char name[32]; + char npath[128]; + char format[12]; + sprintf(format,"%%-%ds",24-depth); + + File dir=SD.open(path); + if (dir) { + dir.rewindDirectory(); + if (strlen(path)>1) { + snprintf_P(npath,sizeof(npath),PSTR("http://%s/upl?download=%s"),WiFi.localIP().toString().c_str(),path); + for (uint8_t cnt=strlen(npath)-1;cnt>0;cnt--) { + if (npath[cnt]=='/') { + if (npath[cnt-1]=='=') npath[cnt+1]=0; + else npath[cnt]=0; + break; + } + } + WSContentSend_P(HTTP_FORM_SDC_DIRd,npath,path,".."); + } + while (true) { + File entry=dir.openNextFile(); + if (!entry) { + break; + } + char *pp=path; + if (!*(pp+1)) pp++; + char *cp=name; + + if (reject((char*)entry.name())) goto fclose; + + for (uint8_t cnt=0;cnt1) { + strcat(path,"/"); + } + strcat(path,entry.name()); + ListDir(path,depth+4); + path[plen]=0; + } else { + snprintf_P(npath,sizeof(npath),HTTP_FORM_SDC_HREF,WiFi.localIP().toString().c_str(),pp,entry.name()); + WSContentSend_P(HTTP_FORM_SDC_DIRb,npath,entry.name(),name,entry.size()); + } + fclose: + entry.close(); + } + dir.close(); + } +} + +char path[48]; + +void Script_FileUploadConfiguration(void) +{ + uint8_t depth=0; + strcpy(path,"/"); + + if (!HttpCheckPriviledgedAccess()) { return; } + + if (Webserver->hasArg("download")) { + String stmp = Webserver->arg("download"); + char *cp=(char*)stmp.c_str(); + if (DownloadFile(cp)) { + + strcpy(path,cp); + } + } + + WSContentStart_P(S_SCRIPT_FILE_UPLOAD); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_FILE_UPLOAD,D_SDCARD_DIR); + WSContentSend_P(HTTP_FORM_FILE_UPG, D_SCRIPT_UPLOAD); +#ifdef SDCARD_DIR + WSContentSend_P(HTTP_FORM_SDC_DIRa); + if (glob_script_mem.script_sd_found) { + ListDir(path,depth); + } + WSContentSend_P(HTTP_FORM_SDC_DIRc); +#endif + WSContentSend_P(HTTP_FORM_FILE_UPGb); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); + Web.upload_error = 0; +} + +File upload_file; + +void ScriptFileUploadSuccess(void) { + WSContentStart_P(S_INFORMATION); + WSContentSendStyle(); + WSContentSend_P(PSTR("
" D_UPLOAD " " D_SUCCESSFUL "
"), WebColor(COL_TEXT_SUCCESS)); + WSContentSend_P(PSTR("

")); + WSContentSend_P(PSTR("

"),"/upl",D_UPL_DONE); + + WSContentStop(); +} + + + +void script_upload(void) { + + + + HTTPUpload& upload = Webserver->upload(); + if (upload.status == UPLOAD_FILE_START) { + char npath[48]; + sprintf(npath,"%s/%s",path,upload.filename.c_str()); + SD.remove(npath); + upload_file=SD.open(npath,FILE_WRITE); + if (!upload_file) Web.upload_error=1; + } else if(upload.status == UPLOAD_FILE_WRITE) { + if (upload_file) upload_file.write(upload.buf,upload.currentSize); + } else if(upload.status == UPLOAD_FILE_END) { + if (upload_file) upload_file.close(); + if (Web.upload_error) { + AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload error")); + } + } else { + Web.upload_error=1; + Webserver->send(500, "text/plain", "500: couldn't create file"); + } +} + +uint8_t DownloadFile(char *file) { + File download_file; + WiFiClient download_Client; + + if (!SD.exists(file)) { + AddLog_P(LOG_LEVEL_INFO,PSTR("file not found")); + return 0; + } + + download_file=SD.open(file,FILE_READ); + if (!download_file) { + AddLog_P(LOG_LEVEL_INFO,PSTR("could not open file")); + return 0; + } + + if (download_file.isDirectory()) { + download_file.close(); + return 1; + } + + uint32_t flen=download_file.size(); + + download_Client = Webserver->client(); + Webserver->setContentLength(flen); + + char attachment[100]; + char *cp; + for (uint8_t cnt=strlen(file); cnt>=0; cnt--) { + if (file[cnt]=='/') { + cp=&file[cnt+1]; + break; + } + } + snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=%s"),cp); + Webserver->sendHeader(F("Content-Disposition"), attachment); + WSSend(200, CT_STREAM, ""); + + uint8_t buff[512]; + uint16_t bread; + + + uint8_t cnt=0; + while (download_file.available()) { + bread=download_file.read(buff,sizeof(buff)); + uint16_t bw=download_Client.write((const char*)buff,bread); + if (!bw) break; + cnt++; + if (cnt>7) { + cnt=0; + if (glob_script_mem.script_loglevel&0x80) { + + loop(); + } + } + } + download_file.close(); + download_Client.stop(); + return 0; +} + +#endif + + +void HandleScriptTextareaConfiguration(void) { + if (!HttpCheckPriviledgedAccess()) { return; } + + if (Webserver->hasArg("save")) { + ScriptSaveSettings(); + HandleConfiguration(); + return; + } +} + +void HandleScriptConfiguration(void) { + + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_SCRIPT); + +#ifdef USE_SCRIPT_FATFS + if (Webserver->hasArg("d1")) { + DownloadFile(glob_script_mem.flink[0]); + } + if (Webserver->hasArg("d2")) { + DownloadFile(glob_script_mem.flink[1]); + } + if (Webserver->hasArg("upl")) { + Script_FileUploadConfiguration(); + } +#endif + + WSContentStart_P(S_CONFIGURE_SCRIPT); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_SCRIPT); + + +#ifdef xSCRIPT_STRIP_COMMENTS + uint16_t ssize=glob_script_mem.script_size; + if (bitRead(Settings.rule_enabled, 1)) ssize*=2; + WSContentSend_P(HTTP_FORM_SCRIPT1,1,1,bitRead(Settings.rule_enabled,0) ? " checked" : "",ssize); +#else + WSContentSend_P(HTTP_FORM_SCRIPT1,1,1,bitRead(Settings.rule_enabled,0) ? " checked" : "",glob_script_mem.script_size); +#endif + + + if (glob_script_mem.script_ram[0]) { + _WSContentSend(glob_script_mem.script_ram); + } + WSContentSend_P(HTTP_FORM_SCRIPT1b); + +#ifdef USE_SCRIPT_FATFS + if (glob_script_mem.script_sd_found) { + WSContentSend_P(HTTP_FORM_SCRIPT1d); + if (glob_script_mem.flink[0][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c,1,glob_script_mem.flink[0]); + if (glob_script_mem.flink[1][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c,2,glob_script_mem.flink[1]); + } +#endif + + WSContentSend_P(HTTP_SCRIPT_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); + } + + +void ScriptSaveSettings(void) { + + if (Webserver->hasArg("c1")) { + bitWrite(Settings.rule_enabled,0,1); + } else { + bitWrite(Settings.rule_enabled,0,0); + } + + + String str = Webserver->arg("t1"); + + if (*str.c_str()) { + + str.replace("\r\n","\n"); + str.replace("\r","\n"); + +#ifdef xSCRIPT_STRIP_COMMENTS + if (bitRead(Settings.rule_enabled, 1)) { + char *sp=(char*)str.c_str(); + char *sp1=sp; + char *dp=sp; + uint8_t flg=0; + while (*sp) { + while (*sp==' ') sp++; + sp1=sp; + sp=strchr(sp,'\n'); + if (!sp) { + flg=1; + } else { + *sp=0; + } + if (*sp1!=';') { + uint8_t slen=strlen(sp1); + if (slen) { + strcpy(dp,sp1); + dp+=slen; + *dp++='\n'; + } + } + if (flg) { + *dp=0; + break; + } + sp++; + } + } +#endif + + strlcpy(glob_script_mem.script_ram,str.c_str(), glob_script_mem.script_size); + +#ifdef USE_24C256 +#ifndef USE_SCRIPT_FATFS + if (glob_script_mem.flags&1) { + EEP_WRITE(0,EEP_SCRIPT_SIZE,glob_script_mem.script_ram); + } +#endif +#endif + +#ifdef USE_SCRIPT_FATFS + if (glob_script_mem.flags&1) { + SD.remove(FAT_SCRIPT_NAME); + File file=SD.open(FAT_SCRIPT_NAME,FILE_WRITE); + file.write(glob_script_mem.script_ram,FAT_SCRIPT_SIZE); + file.close(); + } +#endif + +#if defined(ESP32) && defined(ESP32_SCRIPT_SIZE) && !defined(USE_24C256) && !defined(USE_SCRIPT_FATFS) + if (glob_script_mem.flags&1) { + SaveFile("/script.txt",(uint8_t*)glob_script_mem.script_ram,ESP32_SCRIPT_SIZE); + } +#endif + } + + if (glob_script_mem.script_mem) { + Scripter_save_pvars(); + free(glob_script_mem.script_mem); + glob_script_mem.script_mem=0; + glob_script_mem.script_mem_size=0; + } + + if (bitRead(Settings.rule_enabled, 0)) { + int16_t res=Init_Scripter(); + if (res) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("script init error: %d"), res); + return; + } + Run_Scripter(">B",2,0); + fast_script=Run_Scripter(">F",-2,0); + } +} + +#endif + + +#if defined(USE_SCRIPT_HUE) && defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) + + +#define HUE_DEV_MVNUM 5 +#define HUE_DEV_NSIZE 16 +struct HUE_SCRIPT { + char name[HUE_DEV_NSIZE]; + uint8_t type; + uint8_t index[HUE_DEV_MVNUM]; + uint8_t vindex[HUE_DEV_MVNUM]; +} hue_script[32]; + + +const char SCRIPT_HUE_LIGHTS_STATUS_JSON1[] PROGMEM = + "{\"state\":" + "{\"on\":{state}," + "{light_status}" + "\"alert\":\"none\"," + "\"effect\":\"none\"," + "\"reachable\":true}" + ",\"type\":\"{type}\"," + "\"name\":\"{j1\"," + "\"modelid\":\"{m1}\"," + "\"uniqueid\":\"{j2\"," + "\"swversion\":\"5.50.1.19085\"}"; +# 3706 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_scripter.ino" +const char SCRIPT_HUE_LIGHTS_STATUS_JSON2[] PROGMEM = +"{\"state\":{" +"\"presence\":{state}," +"\"lastupdated\":\"2017-10-01T12:37:30\"" +"}," +"\"swupdate\":{" +"\"state\":\"noupdates\"," +"\"lastinstall\": null" +"}," +"\"config\":{" +"\"on\":true," +"\"battery\":100," +"\"reachable\":true," +"\"alert\":\"none\"," +"\"ledindication\":false," +"\"usertest\":false," +"\"sensitivity\":2," +"\"sensitivitymax\":2," +"\"pending\":[]" +"}," +"\"name\":\"{j1\"," +"\"type\":\"ZLLPresence\"," +"\"modelid\":\"SML001\"," +"\"manufacturername\":\"Philips\"," +"\"swversion\":\"6.1.0.18912\"," +"\"uniqueid\":\"{j2\"" +"}"; +# 3787 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_scripter.ino" +void Script_HueStatus(String *response, uint16_t hue_devs) { + + if (hue_script[hue_devs].type=='p') { + *response+=FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON2); + response->replace("{j1",hue_script[hue_devs].name); + response->replace("{j2", GetHueDeviceId(hue_devs)); + uint8_t pwr=glob_script_mem.fvars[hue_script[hue_devs].index[0]-1]; + response->replace("{state}", (pwr ? "true" : "false")); + return; + } + + *response+=FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON1); + uint8_t pwr=glob_script_mem.fvars[hue_script[hue_devs].index[0]-1]; + response->replace("{state}", (pwr ? "true" : "false")); + String light_status = ""; + if (hue_script[hue_devs].index[1]>0) { + + light_status += "\"bri\":"; + uint32_t bri=glob_script_mem.fvars[hue_script[hue_devs].index[1]-1]; + if (bri > 254) bri = 254; + if (bri < 1) bri = 1; + light_status += String(bri); + light_status += ","; + } + if (hue_script[hue_devs].index[2]>0) { + + uint32_t hue=glob_script_mem.fvars[hue_script[hue_devs].index[2]-1]; + + light_status += "\"hue\":"; + light_status += String(hue); + light_status += ","; + } + if (hue_script[hue_devs].index[3]>0) { + + uint32_t sat=glob_script_mem.fvars[hue_script[hue_devs].index[3]-1] ; + if (sat > 254) sat = 254; + if (sat < 1) sat = 1; + light_status += "\"sat\":"; + light_status += String(sat); + light_status += ","; + } + if (hue_script[hue_devs].index[4]>0) { + + uint32_t ct=glob_script_mem.fvars[hue_script[hue_devs].index[4]-1]; + light_status += "\"ct\":"; + light_status += String(ct); + light_status += ","; + } + + float temp; + switch (hue_script[hue_devs].type) { + case 'C': + response->replace("{type}","Color Ligh"); + response->replace("{m1","LST001"); + break; + case 'D': + response->replace("{type}","Dimmable Light"); + response->replace("{m1","LWB004"); + break; + case 'T': + response->replace("{type}","Color Temperature Light"); + response->replace("{m1","LTW011"); + break; + case 'E': + response->replace("{type}","Extended color light"); + response->replace("{m1","LCT007"); + break; + case 'S': + response->replace("{type}","On/Off light"); + response->replace("{m1","LCT007"); + break; + default: + response->replace("{type}","color light"); + response->replace("{m1","LST001"); + break; + } + + response->replace("{light_status}", light_status); + response->replace("{j1",hue_script[hue_devs].name); + response->replace("{j2", GetHueDeviceId(hue_devs)); + +} + +void Script_Check_Hue(String *response) { + if (!bitRead(Settings.rule_enabled, 0)) return; + + uint8_t hue_script_found=Run_Scripter(">H",-2,0); + if (hue_script_found!=99) return; + + char line[128]; + char tmp[128]; + uint8_t hue_devs=0; + uint8_t vindex=0; + char *cp; + char *lp=glob_script_mem.section_ptr+2; + while (lp) { + SCRIPT_SKIP_SPACES + while (*lp==SCRIPT_EOL) { + lp++; + } + if (!*lp || *lp=='#' || *lp=='>') { + break; + } + if (*lp!=';') { + + memcpy(line,lp,sizeof(line)); + line[sizeof(line)-1]=0; + cp=line; + for (uint32_t i=0; i0) *response+=",\""; + } + *response+=String(EncodeLightId(hue_devs+devices_present+1))+"\":"; + Script_HueStatus(response,hue_devs); + } + + hue_devs++; + } + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + } +#if 0 + if (response) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Hue: %d"), hue_devs); + toLog(">>>>"); + toLog(response->c_str()); + toLog(response->c_str()+LOGSZ); + } +#endif +} + +const char sHUE_LIGHT_RESPONSE_JSON[] PROGMEM = + "{\"success\":{\"/lights/{id/state/{cm\":{re}}"; + +const char sHUE_SENSOR_RESPONSE_JSON[] PROGMEM = + "{\"success\":{\"/lights/{id/state/{cm\":{re}}"; + +const char sHUE_ERROR_JSON[] PROGMEM = + "[{\"error\":{\"type\":901,\"address\":\"/\",\"description\":\"Internal Error\"}}]"; + + + +void Script_Handle_Hue(String *path) { + String response; + int code = 200; + uint16_t tmp = 0; + uint16_t hue = 0; + uint8_t sat = 0; + uint8_t bri = 254; + uint16_t ct = 0; + bool resp = false; + + uint8_t device = DecodeLightId(atoi(path->c_str())); + uint8_t index = device-devices_present-1; + + if (Webserver->args()) { + response = "["; + + StaticJsonBuffer<400> jsonBuffer; + JsonObject &hue_json = jsonBuffer.parseObject(Webserver->arg((Webserver->args())-1)); + if (hue_json.containsKey("on")) { + + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "on"); + + bool on = hue_json["on"]; + switch(on) + { + case false : glob_script_mem.fvars[hue_script[index].index[0]-1]=0; + response.replace("{re", "false"); + break; + case true : glob_script_mem.fvars[hue_script[index].index[0]-1]=1; + response.replace("{re", "true"); + break; + } + glob_script_mem.type[hue_script[index].vindex[0]].bits.changed=1; + resp = true; + } + if (hue_json.containsKey("bri")) { + tmp = hue_json["bri"]; + bri=tmp; + if (254 <= bri) { bri = 255; } + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "bri"); + response.replace("{re", String(tmp)); + glob_script_mem.fvars[hue_script[index].index[1]-1]=bri; + glob_script_mem.type[hue_script[index].vindex[1]].bits.changed=1; + resp = true; + } + if (hue_json.containsKey("xy")) { + float x, y; + x = hue_json["xy"][0]; + y = hue_json["xy"][1]; + const String &x_str = hue_json["xy"][0]; + const String &y_str = hue_json["xy"][1]; + uint8_t rr,gg,bb; + LightStateClass::XyToRgb(x, y, &rr, &gg, &bb); + LightStateClass::RgbToHsb(rr, gg, bb, &hue, &sat, nullptr); + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(device)); + response.replace("{cm", "xy"); + response.replace("{re", "[" + x_str + "," + y_str + "]"); + glob_script_mem.fvars[hue_script[index].index[2]-1]=hue; + glob_script_mem.type[hue_script[index].vindex[2]].bits.changed=1; + glob_script_mem.fvars[hue_script[index].index[3]-1]=sat; + glob_script_mem.type[hue_script[index].vindex[3]].bits.changed=1; + resp = true; + } + + if (hue_json.containsKey("hue")) { + tmp = hue_json["hue"]; + + + hue=tmp; + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "hue"); + response.replace("{re", String(tmp)); + glob_script_mem.fvars[hue_script[index].index[2]-1]=hue; + glob_script_mem.type[hue_script[index].vindex[2]].bits.changed=1; + resp = true; + } + if (hue_json.containsKey("sat")) { + tmp = hue_json["sat"]; + sat=tmp; + if (254 <= sat) { sat = 255; } + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "sat"); + response.replace("{re", String(tmp)); + glob_script_mem.fvars[hue_script[index].index[3]-1]=sat; + glob_script_mem.type[hue_script[index].vindex[3]].bits.changed=1; + resp = true; + } + if (hue_json.containsKey("ct")) { + ct = hue_json["ct"]; + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "ct"); + response.replace("{re", String(ct)); + glob_script_mem.fvars[hue_script[index].index[4]-1]=ct; + glob_script_mem.type[hue_script[index].vindex[4]].bits.changed=1; + resp = true; + } + response += "]"; + + } else { + response = FPSTR(sHUE_ERROR_JSON); + } + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str()); + WSSend(code, CT_JSON, response); + if (resp) { + Run_Scripter(">E",2,0); + } +} +#endif + + +#ifdef USE_SCRIPT_SUB_COMMAND +bool Script_SubCmd(void) { + if (!bitRead(Settings.rule_enabled, 0)) return false; + + if (tasm_cmd_activ) return false; + + char command[CMDSZ]; + strlcpy(command,XdrvMailbox.topic,CMDSZ); + uint32_t pl=XdrvMailbox.payload; + char pld[64]; + strlcpy(pld,XdrvMailbox.data,sizeof(pld)); + + char cmdbuff[128]; + char *cp=cmdbuff; + *cp++='#'; + strcpy(cp,XdrvMailbox.topic); + uint8_t tlen=strlen(XdrvMailbox.topic); + cp+=tlen; + if (XdrvMailbox.index > 0) { + *cp++=XdrvMailbox.index|0x30; + tlen++; + } + if ((XdrvMailbox.payload>0) || (XdrvMailbox.data_len>0)) { + *cp++='('; + strncpy(cp,XdrvMailbox.data,XdrvMailbox.data_len); + cp+=XdrvMailbox.data_len; + *cp++=')'; + *cp=0; + } + + uint32_t res=Run_Scripter(cmdbuff,tlen+1,0); + + if (res) return false; + else { + if (pl>=0) { + Response_P(S_JSON_COMMAND_NVALUE, command, pl); + } else { + Response_P(S_JSON_COMMAND_SVALUE, command, pld); + } + } + return true; +} +#endif + +void execute_script(char *script) { + char *svd_sp=glob_script_mem.scriptptr; + strcat(script,"\n#"); + glob_script_mem.scriptptr=script; + Run_Scripter(">",1,0); + glob_script_mem.scriptptr=svd_sp; +} +#define D_CMND_SCRIPT "Script" +#define D_CMND_SUBSCRIBE "Subscribe" +#define D_CMND_UNSUBSCRIBE "Unsubscribe" + +enum ScriptCommands { CMND_SCRIPT,CMND_SUBSCRIBE, CMND_UNSUBSCRIBE }; +const char kScriptCommands[] PROGMEM = D_CMND_SCRIPT "|" D_CMND_SUBSCRIBE "|" D_CMND_UNSUBSCRIBE; + +bool ScriptCommand(void) { + char command[CMDSZ]; + bool serviced = true; + uint8_t index = XdrvMailbox.index; + + if (tasm_cmd_activ) return false; + + int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kScriptCommands); + if (-1 == command_code) { + serviced = false; + } + else if ((CMND_SCRIPT == command_code) && (index > 0)) { + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 4)) { + switch (XdrvMailbox.payload) { + case 0: + case 1: + bitWrite(Settings.rule_enabled, index -1, XdrvMailbox.payload); + break; +#ifdef xSCRIPT_STRIP_COMMENTS + case 2: + bitWrite(Settings.rule_enabled, 1,0); + break; + case 3: + bitWrite(Settings.rule_enabled, 1,1); + break; +#endif + } + } else { + if ('>' == XdrvMailbox.data[0]) { + + snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%s\"}"),command,XdrvMailbox.data); + if (bitRead(Settings.rule_enabled, 0)) { + for (uint8_t count=0; count> 1; +} + +void dateTime(uint16_t* date, uint16_t* time) { + + *date = xFAT_DATE(RtcTime.year,RtcTime.month, RtcTime.day_of_month); + + *time = xFAT_TIME(RtcTime.hour,RtcTime.minute,RtcTime.second); +} + +#endif + + + +#ifdef SUPPORT_MQTT_EVENT +# 4281 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_scripter.ino" +bool ScriptMqttData(void) +{ + bool serviced = false; + + toLog(XdrvMailbox.data); + if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > 256) { + return false; + } + String sTopic = XdrvMailbox.topic; + String sData = XdrvMailbox.data; + + MQTT_Subscription event_item; + + for (uint32_t index = 0; index < subscriptions.size(); index++) { + event_item = subscriptions.get(index); + + + if (sTopic.startsWith(event_item.Topic)) { + + serviced = true; + String value; + String lkey; + if (event_item.Key.length() == 0) { + value = sData; + } else { + StaticJsonBuffer<400> jsonBuf; + JsonObject& jsonData = jsonBuf.parseObject(sData); + String key1 = event_item.Key; + String key2; + if (!jsonData.success()) break; + int dot; + if ((dot = key1.indexOf('.')) > 0) { + key2 = key1.substring(dot+1); + key1 = key1.substring(0, dot); + lkey=key2; + if (!jsonData[key1][key2].success()) break; + value = (const char *)jsonData[key1][key2]; + } else { + if (!jsonData[key1].success()) break; + value = (const char *)jsonData[key1]; + lkey=key1; + } + } + value.trim(); + char sbuffer[128]; + + if (!strncmp(lkey.c_str(),"Epoch",5)) { + uint32_t ep=atoi(value.c_str())-(uint32_t)EPOCH_OFFSET; + snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=%d\n"), event_item.Event.c_str(),ep); + } else { + snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=\"%s\"\n"), event_item.Event.c_str(), value.c_str()); + } + + execute_script(sbuffer); + } + } + return serviced; +} +# 4356 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_scripter.ino" +String ScriptSubscribe(const char *data, int data_len) +{ + MQTT_Subscription subscription_item; + String events; + if (data_len > 0) { + char parameters[data_len+1]; + memcpy(parameters, data, data_len); + parameters[data_len] = '\0'; + String event_name, topic, key; + + char * pos = strtok(parameters, ","); + if (pos) { + event_name = Trim(pos); + pos = strtok(nullptr, ","); + if (pos) { + topic = Trim(pos); + pos = strtok(nullptr, ","); + if (pos) { + key = Trim(pos); + } + } + } + + + if (event_name.length() > 0 && topic.length() > 0) { + + for (uint32_t index=0; index < subscriptions.size(); index++) { + if (subscriptions.get(index).Event.equals(event_name)) { + + String stopic = subscriptions.get(index).Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + subscriptions.remove(index); + break; + } + } + + if (!topic.endsWith("#")) { + if (topic.endsWith("/")) { + topic.concat("#"); + } else { + topic.concat("/#"); + } + } + + + subscription_item.Event = event_name; + subscription_item.Topic = topic.substring(0, topic.length() - 2); + subscription_item.Key = key; + subscriptions.add(subscription_item); + + MqttSubscribe(topic.c_str()); + events.concat(event_name + "," + topic + + (key.length()>0 ? "," : "") + + key); + } else { + events = D_JSON_WRONG_PARAMETERS; + } + } else { + + for (uint32_t index=0; index < subscriptions.size(); index++) { + subscription_item = subscriptions.get(index); + events.concat(subscription_item.Event + "," + subscription_item.Topic + + (subscription_item.Key.length()>0 ? "," : "") + + subscription_item.Key + "; "); + } + } + return events; +} +# 4436 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_scripter.ino" +String ScriptUnsubscribe(const char * data, int data_len) +{ + MQTT_Subscription subscription_item; + String events; + if (data_len > 0) { + for (uint32_t index = 0; index < subscriptions.size(); index++) { + subscription_item = subscriptions.get(index); + if (subscription_item.Event.equalsIgnoreCase(data)) { + String stopic = subscription_item.Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + events = subscription_item.Event; + subscriptions.remove(index); + break; + } + } + } else { + + String stopic; + while (subscriptions.size() > 0) { + events.concat(subscriptions.get(0).Event + "; "); + stopic = subscriptions.get(0).Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + subscriptions.remove(0); + } + } + return events; +} +#endif + + + +#ifdef USE_SCRIPT_WEB_DISPLAY + +void Script_Check_HTML_Setvars(void) { + + if (!HttpCheckPriviledgedAccess()) { return; } + + if (Webserver->hasArg("sv")) { + String stmp = Webserver->arg("sv"); + char cmdbuf[64]; + memset(cmdbuf,0,sizeof(cmdbuf)); + char *cp=cmdbuf; + *cp++='>'; + strncpy(cp,stmp.c_str(),sizeof(cmdbuf)-1); + char *cp1=strchr(cp,'_'); + if (!cp1) return; + *cp1=0; + char vname[32]; + strncpy(vname,cp,sizeof(vname)); + *cp1='='; + cp1++; + + struct T_INDEX ind; + uint8_t vtype; + isvar(vname,&vtype,&ind,0,0,0); + if (vtype!=NUM_RES && vtype&STYPE) { + + uint8_t tlen=strlen(cp1); + memmove(cp1+1,cp1,tlen); + *cp1='\"'; + *(cp1+tlen+1)='\"'; + } + + + execute_script(cmdbuf); + Run_Scripter(">E",2,0); + } +} + + +const char SCRIPT_MSG_BUTTONa[] PROGMEM = + ""; + +const char SCRIPT_MSG_BUTTONa_TBL[] PROGMEM = + ""; + +const char SCRIPT_MSG_BUTTONb[] PROGMEM = + ""; + +const char SCRIPT_MSG_BUT_START[] PROGMEM = + "
"; +const char SCRIPT_MSG_BUT_START_TBL[] PROGMEM = + ""; + +const char SCRIPT_MSG_BUT_STOP[] PROGMEM = + ""; +const char SCRIPT_MSG_BUT_STOP_TBL[] PROGMEM = + "
"; + +const char SCRIPT_MSG_SLIDER[] PROGMEM = + "
%s
%s%s
" + "
"; + +const char SCRIPT_MSG_CHKBOX[] PROGMEM = + "
"; + +const char SCRIPT_MSG_TEXTINP[] PROGMEM = + "
"; + +const char SCRIPT_MSG_NUMINP[] PROGMEM = + "
"; + + +void ScriptGetVarname(char *nbuf,char *sp, uint32_t blen) { +uint32_t cnt; + for (cnt=0;cntW",-2,0); + if (web_script==99) { + char line[128]; + char tmp[128]; + uint8_t optflg=0; + char *lp=glob_script_mem.section_ptr+2; + while (lp) { + while (*lp==SCRIPT_EOL) { + lp++; + } + if (!*lp || *lp=='#' || *lp=='>') { + break; + } + if (*lp!=';') { + + memcpy(line,lp,sizeof(line)); + line[sizeof(line)-1]=0; + char *cp=line; + for (uint32_t i=0; i0) { + cp="checked='checked'"; + uval=0; + } else { + cp=""; + uval=1; + } + WSContentSend_PD(SCRIPT_MSG_CHKBOX,label,(char*)cp,uval,vname); + + } else if (!strncmp(lin,"bu(",3)) { + char *lp=lin+3; + uint8_t bcnt=0; + char *found=lin; + while (bcnt<4) { + found=strstr(found,"bu("); + if (!found) break; + found+=3; + bcnt++; + } + uint8_t proz=100/bcnt; + if (!optflg && bcnt>1) proz-=2; + if (optflg) WSContentSend_PD(SCRIPT_MSG_BUT_START_TBL); + else WSContentSend_PD(SCRIPT_MSG_BUT_START); + for (uint32_t cnt=0;cnt0) { + cp=ontxt; + uval=0; + } else { + cp=offtxt; + uval=1; + } + if (bcnt>1 && cnt==bcnt-1) { + if (!optflg) proz+=2; + } + if (!optflg) { + WSContentSend_PD(SCRIPT_MSG_BUTTONa,proz,uval,vname,cp); + } else { + WSContentSend_PD(SCRIPT_MSG_BUTTONa_TBL,proz,uval,vname,cp); + } + if (bcnt>1 && cnt%s
"),tmp); + } else { + WSContentSend_PD(PSTR("{s}%s{e}"),tmp); + } + } + } + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + } + } +} +#endif + + +#ifdef USE_SENDMAIL + +#ifdef ESP8266 +void script_send_email_body(BearSSL::WiFiClientSecure_light *client) { +#else +void script_send_email_body(WiFiClient *client) { +#endif + +uint8_t msect=Run_Scripter(">m",-2,0); + if (msect==99) { + char line[128]; + char tmp[128]; + char *lp=glob_script_mem.section_ptr+2; + while (lp) { + while (*lp==SCRIPT_EOL) { + lp++; + } + if (!*lp || *lp=='#' || *lp=='>') { + break; + } + if (*lp!=';') { + + memcpy(line,lp,sizeof(line)); + line[sizeof(line)-1]=0; + char *cp=line; + for (uint32_t i=0; iprintln(tmp); + } + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + } + } else { + client->println("*"); + } +} +#endif + +#ifdef USE_SCRIPT_JSON_EXPORT +void ScriptJsonAppend(void) { + uint8_t web_script=Run_Scripter(">J",-2,0); + if (web_script==99) { + char line[128]; + char tmp[128]; + char *lp=glob_script_mem.section_ptr+2; + while (lp) { + while (*lp==SCRIPT_EOL) { + lp++; + } + if (!*lp || *lp=='#' || *lp=='>') { + break; + } + if (*lp!=';') { + + memcpy(line,lp,sizeof(line)); + line[sizeof(line)-1]=0; + char *cp=line; + for (uint32_t i=0; iB",2,0); + fast_script=Run_Scripter(">F",-2,0); +#if defined(USE_SCRIPT_HUE) && defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) + Script_Check_Hue(0); +#endif + } + break; + case FUNC_EVERY_100_MSECOND: + ScripterEvery100ms(); + break; + case FUNC_EVERY_SECOND: + ScriptEverySecond(); + break; + case FUNC_COMMAND: + result = ScriptCommand(); + break; + case FUNC_SET_POWER: +#ifdef SCRIPT_POWER_SECTION + if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">P",2,0); +#else + if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">E",2,0); +#endif + break; + case FUNC_RULES_PROCESS: + if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">E",2,mqtt_data); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_RULES); + break; + case FUNC_WEB_ADD_HANDLER: + Webserver->on("/" WEB_HANDLE_SCRIPT, HandleScriptConfiguration); + Webserver->on("/ta",HTTP_POST, HandleScriptTextareaConfiguration); + +#ifdef USE_SCRIPT_FATFS + Webserver->on("/u3", HTTP_POST,[]() { Webserver->sendHeader("Location","/u3");Webserver->send(303);},script_upload); + Webserver->on("/u3", HTTP_GET,ScriptFileUploadSuccess); + Webserver->on("/upl", HTTP_GET,Script_FileUploadConfiguration); +#endif + break; +#endif + case FUNC_SAVE_BEFORE_RESTART: + if (bitRead(Settings.rule_enabled, 0)) { + Run_Scripter(">R",2,0); + Scripter_save_pvars(); + } + break; +#ifdef SUPPORT_MQTT_EVENT + case FUNC_MQTT_DATA: + if (bitRead(Settings.rule_enabled, 0)) { + result = ScriptMqttData(); + } + break; +#endif +#ifdef USE_SCRIPT_WEB_DISPLAY + case FUNC_WEB_SENSOR: + if (bitRead(Settings.rule_enabled, 0)) { + ScriptWebShow(); + } + break; +#endif + +#ifdef USE_SCRIPT_JSON_EXPORT + case FUNC_JSON_APPEND: + if (bitRead(Settings.rule_enabled, 0)) { + ScriptJsonAppend(); + } + break; +#endif + +#ifdef USE_BUTTON_EVENT + case FUNC_BUTTON_PRESSED: + if (bitRead(Settings.rule_enabled, 0)) { + if ((script_button[XdrvMailbox.index]&1)!=(XdrvMailbox.payload&1)) { + script_button[XdrvMailbox.index]=XdrvMailbox.payload; + Run_Scripter(">b",2,0); + } + } + break; +#endif + + } + return result; +} + + + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_11_knx.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_11_knx.ino" +#ifdef USE_KNX +# 51 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_11_knx.ino" +#define XDRV_11 11 + +#include + +address_t KNX_physs_addr; +address_t KNX_addr; + +#define KNX_Empty 255 + +#define TOGGLE_INHIBIT_TIME 15 + +float last_temp; +float last_hum; +uint8_t toggle_inhibit; + +typedef struct __device_parameters +{ + uint8_t type; + + + + + bool show; + + bool last_state; + + callback_id_t CB_id; + + + + + +} device_parameters_t; + + +device_parameters_t device_param[] = { + { 1, false, false, KNX_Empty }, + { 2, false, false, KNX_Empty }, + { 3, false, false, KNX_Empty }, + { 4, false, false, KNX_Empty }, + { 5, false, false, KNX_Empty }, + { 6, false, false, KNX_Empty }, + { 7, false, false, KNX_Empty }, + { 8, false, false, KNX_Empty }, + { 9, false, false, KNX_Empty }, + { 10, false, false, KNX_Empty }, + { 11, false, false, KNX_Empty }, + { 12, false, false, KNX_Empty }, + { 13, false, false, KNX_Empty }, + { 14, false, false, KNX_Empty }, + { 15, false, false, KNX_Empty }, + { 16, false, false, KNX_Empty }, + { KNX_TEMPERATURE, false, false, KNX_Empty }, + { KNX_HUMIDITY , false, false, KNX_Empty }, + { KNX_ENERGY_VOLTAGE , false, false, KNX_Empty }, + { KNX_ENERGY_CURRENT , false, false, KNX_Empty }, + { KNX_ENERGY_POWER , false, false, KNX_Empty }, + { KNX_ENERGY_POWERFACTOR , false, false, KNX_Empty }, + { KNX_ENERGY_DAILY , false, false, KNX_Empty }, + { KNX_ENERGY_START , false, false, KNX_Empty }, + { KNX_ENERGY_TOTAL , false, false, KNX_Empty }, + { KNX_SLOT1 , false, false, KNX_Empty }, + { KNX_SLOT2 , false, false, KNX_Empty }, + { KNX_SLOT3 , false, false, KNX_Empty }, + { KNX_SLOT4 , false, false, KNX_Empty }, + { KNX_SLOT5 , false, false, KNX_Empty }, + { KNX_Empty, false, false, KNX_Empty} +}; + + +const char * device_param_ga[] = { + D_TIMER_OUTPUT " 1", + D_TIMER_OUTPUT " 2", + D_TIMER_OUTPUT " 3", + D_TIMER_OUTPUT " 4", + D_TIMER_OUTPUT " 5", + D_TIMER_OUTPUT " 6", + D_TIMER_OUTPUT " 7", + D_TIMER_OUTPUT " 8", + D_SENSOR_BUTTON " 1", + D_SENSOR_BUTTON " 2", + D_SENSOR_BUTTON " 3", + D_SENSOR_BUTTON " 4", + D_SENSOR_BUTTON " 5", + D_SENSOR_BUTTON " 6", + D_SENSOR_BUTTON " 7", + D_SENSOR_BUTTON " 8", + D_TEMPERATURE , + D_HUMIDITY , + D_VOLTAGE , + D_CURRENT , + D_POWERUSAGE , + D_POWER_FACTOR , + D_ENERGY_TODAY , + D_ENERGY_YESTERDAY , + D_ENERGY_TOTAL , + D_KNX_TX_SLOT " 1", + D_KNX_TX_SLOT " 2", + D_KNX_TX_SLOT " 3", + D_KNX_TX_SLOT " 4", + D_KNX_TX_SLOT " 5", + nullptr +}; + + +const char *device_param_cb[] = { + D_TIMER_OUTPUT " 1", + D_TIMER_OUTPUT " 2", + D_TIMER_OUTPUT " 3", + D_TIMER_OUTPUT " 4", + D_TIMER_OUTPUT " 5", + D_TIMER_OUTPUT " 6", + D_TIMER_OUTPUT " 7", + D_TIMER_OUTPUT " 8", + D_TIMER_OUTPUT " 1 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 2 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 3 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 4 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 5 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 6 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 7 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 8 " D_BUTTON_TOGGLE, + D_REPLY " " D_TEMPERATURE, + D_REPLY " " D_HUMIDITY, + D_REPLY " " D_VOLTAGE , + D_REPLY " " D_CURRENT , + D_REPLY " " D_POWERUSAGE , + D_REPLY " " D_POWER_FACTOR , + D_REPLY " " D_ENERGY_TODAY , + D_REPLY " " D_ENERGY_YESTERDAY , + D_REPLY " " D_ENERGY_TOTAL , + D_KNX_RX_SLOT " 1", + D_KNX_RX_SLOT " 2", + D_KNX_RX_SLOT " 3", + D_KNX_RX_SLOT " 4", + D_KNX_RX_SLOT " 5", + nullptr +}; + + +#define D_PRFX_KNX "Knx" +#define D_CMND_KNXTXCMND "Tx_Cmnd" +#define D_CMND_KNXTXVAL "Tx_Val" +#define D_CMND_KNX_ENABLED "_Enabled" +#define D_CMND_KNX_ENHANCED "_Enhanced" +#define D_CMND_KNX_PA "_PA" +#define D_CMND_KNX_GA "_GA" +#define D_CMND_KNX_CB "_CB" + +const char kKnxCommands[] PROGMEM = D_PRFX_KNX "|" + D_CMND_KNXTXCMND "|" D_CMND_KNXTXVAL "|" D_CMND_KNX_ENABLED "|" D_CMND_KNX_ENHANCED "|" D_CMND_KNX_PA "|" D_CMND_KNX_GA "|" D_CMND_KNX_CB ; + +void (* const KnxCommand[])(void) PROGMEM = { + &CmndKnxTxCmnd, &CmndKnxTxVal, &CmndKnxEnabled, &CmndKnxEnhanced, &CmndKnxPa, &CmndKnxGa, &CmndKnxCb }; + +uint8_t KNX_GA_Search( uint8_t param, uint8_t start = 0 ) +{ + for (uint32_t i = start; i < Settings.knx_GA_registered; ++i) + { + if ( Settings.knx_GA_param[i] == param ) + { + if ( Settings.knx_GA_addr[i] != 0 ) + { + if ( i >= start ) { return i; } + } + } + } + return KNX_Empty; +} + + +uint8_t KNX_CB_Search( uint8_t param, uint8_t start = 0 ) +{ + for (uint32_t i = start; i < Settings.knx_CB_registered; ++i) + { + if ( Settings.knx_CB_param[i] == param ) + { + if ( Settings.knx_CB_addr[i] != 0 ) + { + if ( i >= start ) { return i; } + } + } + } + return KNX_Empty; +} + + +void KNX_ADD_GA( uint8_t GAop, uint8_t GA_FNUM, uint8_t GA_AREA, uint8_t GA_FDEF ) +{ + + if ( Settings.knx_GA_registered >= MAX_KNX_GA ) { return; } + if ( GA_FNUM == 0 && GA_AREA == 0 && GA_FDEF == 0 ) { return; } + + + Settings.knx_GA_param[Settings.knx_GA_registered] = GAop; + KNX_addr.ga.area = GA_FNUM; + KNX_addr.ga.line = GA_AREA; + KNX_addr.ga.member = GA_FDEF; + Settings.knx_GA_addr[Settings.knx_GA_registered] = KNX_addr.value; + + Settings.knx_GA_registered++; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ADD " GA #%d: %s " D_TO " %d/%d/%d"), + Settings.knx_GA_registered, + device_param_ga[GAop-1], + GA_FNUM, GA_AREA, GA_FDEF ); +} + + +void KNX_DEL_GA( uint8_t GAnum ) +{ + + uint8_t dest_offset = 0; + uint8_t src_offset = 0; + uint8_t len = 0; + + + Settings.knx_GA_param[GAnum-1] = 0; + + if (GAnum == 1) + { + + src_offset = 1; + + + + len = (Settings.knx_GA_registered - 1); + } + else if (GAnum == Settings.knx_GA_registered) + { + + } + else + { + + + + + dest_offset = GAnum -1 ; + src_offset = dest_offset + 1; + len = (Settings.knx_GA_registered - GAnum); + } + + if (len > 0) + { + memmove(Settings.knx_GA_param + dest_offset, Settings.knx_GA_param + src_offset, len * sizeof(uint8_t)); + memmove(Settings.knx_GA_addr + dest_offset, Settings.knx_GA_addr + src_offset, len * sizeof(uint16_t)); + } + + Settings.knx_GA_registered--; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " GA #%d"), + GAnum ); +} + + +void KNX_ADD_CB( uint8_t CBop, uint8_t CB_FNUM, uint8_t CB_AREA, uint8_t CB_FDEF ) +{ + + if ( Settings.knx_CB_registered >= MAX_KNX_CB ) { return; } + if ( CB_FNUM == 0 && CB_AREA == 0 && CB_FDEF == 0 ) { return; } + + + if ( device_param[CBop-1].CB_id == KNX_Empty ) + { + + device_param[CBop-1].CB_id = knx.callback_register("", KNX_CB_Action, &device_param[CBop-1]); + + + + + } + + Settings.knx_CB_param[Settings.knx_CB_registered] = CBop; + KNX_addr.ga.area = CB_FNUM; + KNX_addr.ga.line = CB_AREA; + KNX_addr.ga.member = CB_FDEF; + Settings.knx_CB_addr[Settings.knx_CB_registered] = KNX_addr.value; + + knx.callback_assign( device_param[CBop-1].CB_id, KNX_addr ); + + Settings.knx_CB_registered++; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ADD " CB #%d: %d/%d/%d " D_TO " %s"), + Settings.knx_CB_registered, + CB_FNUM, CB_AREA, CB_FDEF, + device_param_cb[CBop-1] ); +} + + +void KNX_DEL_CB( uint8_t CBnum ) +{ + uint8_t oldparam = Settings.knx_CB_param[CBnum-1]; + uint8_t dest_offset = 0; + uint8_t src_offset = 0; + uint8_t len = 0; + + + knx.callback_unassign(CBnum-1); + Settings.knx_CB_param[CBnum-1] = 0; + + if (CBnum == 1) + { + + src_offset = 1; + + + + len = (Settings.knx_CB_registered - 1); + } + else if (CBnum == Settings.knx_CB_registered) + { + + } + else + { + + + + + dest_offset = CBnum -1 ; + src_offset = dest_offset + 1; + len = (Settings.knx_CB_registered - CBnum); + } + + if (len > 0) + { + memmove(Settings.knx_CB_param + dest_offset, Settings.knx_CB_param + src_offset, len * sizeof(uint8_t)); + memmove(Settings.knx_CB_addr + dest_offset, Settings.knx_CB_addr + src_offset, len * sizeof(uint16_t)); + } + + Settings.knx_CB_registered--; + + + if ( KNX_CB_Search( oldparam ) == KNX_Empty ) { + knx.callback_deregister( device_param[oldparam-1].CB_id ); + device_param[oldparam-1].CB_id = KNX_Empty; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " CB #%d"), CBnum ); +} + + +bool KNX_CONFIG_NOT_MATCH(void) +{ + + for (uint32_t i = 0; i < KNX_MAX_device_param; ++i) + { + if ( !device_param[i].show ) { + + + + if ( KNX_GA_Search(i+1) != KNX_Empty ) { return true; } + + if ( i < 8 ) + { + if ( KNX_CB_Search(i+1) != KNX_Empty ) { return true; } + if ( KNX_CB_Search(i+9) != KNX_Empty ) { return true; } + } + + if ( i > 15 ) + { + if ( KNX_CB_Search(i+1) != KNX_Empty ) { return true; } + } + } + } + + + for (uint32_t i = 0; i < Settings.knx_GA_registered; ++i) + { + if ( Settings.knx_GA_param[i] != 0 ) + { + if ( Settings.knx_GA_addr[i] == 0 ) + { + return true; + } + } + } + for (uint32_t i = 0; i < Settings.knx_CB_registered; ++i) + { + if ( Settings.knx_CB_param[i] != 0 ) + { + if ( Settings.knx_CB_addr[i] == 0 ) + { + return true; + } + } + } + + return false; +} + + +void KNXStart(void) +{ + knx.start(nullptr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_START)); +} + + +void KNX_INIT(void) +{ + + if (Settings.knx_GA_registered > MAX_KNX_GA) { Settings.knx_GA_registered = MAX_KNX_GA; } + if (Settings.knx_CB_registered > MAX_KNX_CB) { Settings.knx_CB_registered = MAX_KNX_CB; } + + + KNX_physs_addr.value = Settings.knx_physsical_addr; + knx.physical_address_set( KNX_physs_addr ); +# 472 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_11_knx.ino" + for (uint32_t i = 0; i < devices_present; ++i) + { + device_param[i].show = true; + } + for (uint32_t i = GPIO_SWT1; i < GPIO_SWT4 + 1; ++i) + { + if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_SWT1 + 8].show = true; } + } + for (uint32_t i = GPIO_KEY1; i < GPIO_KEY4 + 1; ++i) + { + if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_KEY1 + 8].show = true; } + } + for (uint32_t i = GPIO_SWT1_NP; i < GPIO_SWT4_NP + 1; ++i) + { + if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_SWT1_NP + 8].show = true; } + } + for (uint32_t i = GPIO_KEY1_NP; i < GPIO_KEY4_NP + 1; ++i) + { + if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_KEY1_NP + 8].show = true; } + } + if (GetUsedInModule(GPIO_DHT11, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } + if (GetUsedInModule(GPIO_DHT22, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } + if (GetUsedInModule(GPIO_SI7021, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } +#ifdef USE_DS18x20 + if (GetUsedInModule(GPIO_DSB, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } +#endif + if (GetUsedInModule(GPIO_DHT11, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } + if (GetUsedInModule(GPIO_DHT22, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } + if (GetUsedInModule(GPIO_SI7021, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } + +#if defined(USE_ENERGY_SENSOR) + + if ( energy_flg != ENERGY_NONE ) { + device_param[KNX_ENERGY_POWER-1].show = true; + device_param[KNX_ENERGY_DAILY-1].show = true; + device_param[KNX_ENERGY_START-1].show = true; + device_param[KNX_ENERGY_TOTAL-1].show = true; + device_param[KNX_ENERGY_VOLTAGE-1].show = true; + device_param[KNX_ENERGY_CURRENT-1].show = true; + device_param[KNX_ENERGY_POWERFACTOR-1].show = true; + } +#endif + +#ifdef USE_RULES + device_param[KNX_SLOT1-1].show = true; + device_param[KNX_SLOT2-1].show = true; + device_param[KNX_SLOT3-1].show = true; + device_param[KNX_SLOT4-1].show = true; + device_param[KNX_SLOT5-1].show = true; +#endif + + + if (KNX_CONFIG_NOT_MATCH()) { + Settings.knx_GA_registered = 0; + Settings.knx_CB_registered = 0; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " " D_KNX_PARAMETERS)); + } + + + + + uint8_t j; + for (uint32_t i = 0; i < Settings.knx_CB_registered; ++i) + { + j = Settings.knx_CB_param[i]; + if ( j > 0 ) + { + device_param[j-1].CB_id = knx.callback_register("", KNX_CB_Action, &device_param[j-1]); + + + + KNX_addr.value = Settings.knx_CB_addr[i]; + knx.callback_assign( device_param[j-1].CB_id, KNX_addr ); + } + } +} + + +void KNX_CB_Action(message_t const &msg, void *arg) +{ + device_parameters_t *chan = (device_parameters_t *)arg; + if (!(Settings.flag.knx_enabled)) { return; } + + char tempchar[33]; + + if (msg.data_len == 1) { + + sprintf(tempchar,"%d",msg.data[0]); + } else { + + float tempvar = knx.data_to_2byte_float(msg.data); + dtostrfd(tempvar,2,tempchar); + } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX D_RECEIVED_FROM " %d.%d.%d " D_COMMAND " %s: %s " D_TO " %s"), + msg.received_on.ga.area, msg.received_on.ga.line, msg.received_on.ga.member, + (msg.ct == KNX_CT_WRITE) ? D_KNX_COMMAND_WRITE : (msg.ct == KNX_CT_READ) ? D_KNX_COMMAND_READ : D_KNX_COMMAND_OTHER, + tempchar, + device_param_cb[(chan->type)-1]); + + switch (msg.ct) + { + case KNX_CT_WRITE: + if (chan->type < 9) + { + ExecuteCommandPower(chan->type, msg.data[0], SRC_KNX); + } + else if (chan->type < 17) + { + if (!toggle_inhibit) { + ExecuteCommandPower((chan->type) -8, POWER_TOGGLE, SRC_KNX); + if (Settings.flag.knx_enable_enhancement) { + toggle_inhibit = TOGGLE_INHIBIT_TIME; + } + } + } +#ifdef USE_RULES + else if ((chan->type >= KNX_SLOT1) && (chan->type <= KNX_SLOT5)) + { + if (!toggle_inhibit) { + char command[25]; + if (msg.data_len == 1) { + + snprintf_P(command, sizeof(command), PSTR("event KNXRX_CMND%d=%d"), ((chan->type) - KNX_SLOT1 + 1 ), msg.data[0]); + } else { + + snprintf_P(command, sizeof(command), PSTR("event KNXRX_VAL%d=%s"), ((chan->type) - KNX_SLOT1 + 1 ), tempchar); + } + ExecuteCommand(command, SRC_KNX); + if (Settings.flag.knx_enable_enhancement) { + toggle_inhibit = TOGGLE_INHIBIT_TIME; + } + } + } +#endif + break; + + case KNX_CT_READ: + if (chan->type < 9) + { + knx.answer_1bit(msg.received_on, chan->last_state); + if (Settings.flag.knx_enable_enhancement) { + knx.answer_1bit(msg.received_on, chan->last_state); + knx.answer_1bit(msg.received_on, chan->last_state); + } + } + else if (chan->type == KNX_TEMPERATURE) + { + knx.answer_2byte_float(msg.received_on, last_temp); + if (Settings.flag.knx_enable_enhancement) { + knx.answer_2byte_float(msg.received_on, last_temp); + knx.answer_2byte_float(msg.received_on, last_temp); + } + } + else if (chan->type == KNX_HUMIDITY) + { + knx.answer_2byte_float(msg.received_on, last_hum); + if (Settings.flag.knx_enable_enhancement) { + knx.answer_2byte_float(msg.received_on, last_hum); + knx.answer_2byte_float(msg.received_on, last_hum); + } + } +#ifdef USE_RULES + else if ((chan->type >= KNX_SLOT1) && (chan->type <= KNX_SLOT5)) + { + if (!toggle_inhibit) { + char command[25]; + snprintf_P(command, sizeof(command), PSTR("event KNXRX_REQ%d"), ((chan->type) - KNX_SLOT1 + 1 ) ); + ExecuteCommand(command, SRC_KNX); + if (Settings.flag.knx_enable_enhancement) { + toggle_inhibit = TOGGLE_INHIBIT_TIME; + } + } + } +#endif + break; + } +} + + +void KnxUpdatePowerState(uint8_t device, power_t state) +{ + if (!(Settings.flag.knx_enabled)) { return; } + + device_param[device -1].last_state = bitRead(state, device -1); + + + uint8_t i = KNX_GA_Search(device); + while ( i != KNX_Empty ) { + KNX_addr.value = Settings.knx_GA_addr[i]; + knx.write_1bit(KNX_addr, device_param[device -1].last_state); + if (Settings.flag.knx_enable_enhancement) { + knx.write_1bit(KNX_addr, device_param[device -1].last_state); + knx.write_1bit(KNX_addr, device_param[device -1].last_state); + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), + device_param_ga[device -1], device_param[device -1].last_state, + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); + + i = KNX_GA_Search(device, i + 1); + } +} + + +void KnxSendButtonPower(void) +{ + if (!(Settings.flag.knx_enabled)) { return; } + + uint32_t key = (XdrvMailbox.payload >> 16) & 0xFF; + uint32_t device = XdrvMailbox.payload & 0xFF; + uint32_t state = (XdrvMailbox.payload >> 8) & 0xFF; +# 692 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_11_knx.ino" + uint8_t i = KNX_GA_Search(device + 8); + while ( i != KNX_Empty ) { + KNX_addr.value = Settings.knx_GA_addr[i]; + knx.write_1bit(KNX_addr, !(state == 0)); + if (Settings.flag.knx_enable_enhancement) { + knx.write_1bit(KNX_addr, !(state == 0)); + knx.write_1bit(KNX_addr, !(state == 0)); + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), + device_param_ga[device + 7], !(state == 0), + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); + + i = KNX_GA_Search(device + 8, i + 1); + } + +} + + +void KnxSensor(uint8_t sensor_type, float value) +{ + if (sensor_type == KNX_TEMPERATURE) + { + last_temp = value; + } else if (sensor_type == KNX_HUMIDITY) + { + last_hum = value; + } + + if (!(Settings.flag.knx_enabled)) { return; } + + uint8_t i = KNX_GA_Search(sensor_type); + while ( i != KNX_Empty ) { + KNX_addr.value = Settings.knx_GA_addr[i]; + knx.write_2byte_float(KNX_addr, value); + if (Settings.flag.knx_enable_enhancement) { + knx.write_2byte_float(KNX_addr, value); + knx.write_2byte_float(KNX_addr, value); + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s " D_SENT_TO " %d.%d.%d "), + device_param_ga[sensor_type -1], + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); + + i = KNX_GA_Search(sensor_type, i+1); + } +} + + + + + + +#ifdef USE_WEBSERVER +#ifdef USE_KNX_WEB_MENU +const char S_CONFIGURE_KNX[] PROGMEM = D_CONFIGURE_KNX; + +const char HTTP_BTN_MENU_KNX[] PROGMEM = + "

"; + +const char HTTP_FORM_KNX[] PROGMEM = + "
" + " " D_KNX_PARAMETERS " " + "
" + "
" + "" D_KNX_PHYSICAL_ADDRESS " " + " . " + " . " + "" + "

" D_KNX_PHYSICAL_ADDRESS_NOTE "

" + "

" + + "
" + "" D_KNX_GROUP_ADDRESS_TO_WRITE "
" + + " / " + " / " + " "; + +const char HTTP_FORM_KNX_ADD_BTN[] PROGMEM = + "

" + ""; + +const char HTTP_FORM_KNX_ADD_TABLE_ROW[] PROGMEM = + "" + ""; + +const char HTTP_FORM_KNX3[] PROGMEM = + "
%s -> %d / %d / %d

" + "
" + "" D_KNX_GROUP_ADDRESS_TO_READ "
"; + +const char HTTP_FORM_KNX4[] PROGMEM = + "-> -> ")); + WSContentSend_P(HTTP_FORM_KNX_GA, "GA_FNUM", "GA_AREA", "GA_FDEF"); + WSContentSend_P(HTTP_FORM_KNX_ADD_BTN, "GAwarning", (Settings.knx_GA_registered < MAX_KNX_GA) ? "" : "disabled", 1); + for (uint32_t i = 0; i < Settings.knx_GA_registered ; ++i) + { + if ( Settings.knx_GA_param[i] ) + { + KNX_addr.value = Settings.knx_GA_addr[i]; + WSContentSend_P(HTTP_FORM_KNX_ADD_TABLE_ROW, device_param_ga[Settings.knx_GA_param[i]-1], KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member, i +1); + } + } + + WSContentSend_P(HTTP_FORM_KNX3); + WSContentSend_P(HTTP_FORM_KNX_GA, "CB_FNUM", "CB_AREA", "CB_FDEF"); + WSContentSend_P(HTTP_FORM_KNX4); + + uint8_t j; + for (uint32_t i = 0; i < KNX_MAX_device_param ; i++) + { + + if ( (i > 8) && (i < 16) ) { j=i-8; } else { j=i; } + if ( i == 8 ) { j = 0; } + if ( device_param[j].show ) + { + WSContentSend_P(HTTP_FORM_KNX_OPT, device_param[i].type, device_param_cb[i]); + } + } + WSContentSend_P(PSTR(" ")); + WSContentSend_P(HTTP_FORM_KNX_ADD_BTN, "CBwarning", (Settings.knx_CB_registered < MAX_KNX_CB) ? "" : "disabled", 2); + + for (uint32_t i = 0; i < Settings.knx_CB_registered ; ++i) + { + if ( Settings.knx_CB_param[i] ) + { + KNX_addr.value = Settings.knx_CB_addr[i]; + WSContentSend_P(HTTP_FORM_KNX_ADD_TABLE_ROW2, KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member, device_param_cb[Settings.knx_CB_param[i]-1], i +1); + } + } + WSContentSend_P(PSTR("
")); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); + } + +} + + +void KNX_Save_Settings(void) +{ + String stmp; + address_t KNX_addr; + + Settings.flag.knx_enabled = Webserver->hasArg("b1"); + Settings.flag.knx_enable_enhancement = Webserver->hasArg("b2"); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ENABLED ": %d, " D_KNX_ENHANCEMENT ": %d"), + Settings.flag.knx_enabled, Settings.flag.knx_enable_enhancement ); + + stmp = Webserver->arg("area"); + KNX_addr.pa.area = stmp.toInt(); + stmp = Webserver->arg("line"); + KNX_addr.pa.line = stmp.toInt(); + stmp = Webserver->arg("member"); + KNX_addr.pa.member = stmp.toInt(); + Settings.knx_physsical_addr = KNX_addr.value; + knx.physical_address_set( KNX_addr ); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_KNX_PHYSICAL_ADDRESS ": %d.%d.%d "), + KNX_addr.pa.area, KNX_addr.pa.line, KNX_addr.pa.member ); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "GA: %d"), + Settings.knx_GA_registered ); + for (uint32_t i = 0; i < Settings.knx_GA_registered ; ++i) + { + KNX_addr.value = Settings.knx_GA_addr[i]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "GA #%d: %s " D_TO " %d/%d/%d"), + i+1, device_param_ga[Settings.knx_GA_param[i]-1], + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member ); + + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "CB: %d"), + Settings.knx_CB_registered ); + for (uint32_t i = 0; i < Settings.knx_CB_registered ; ++i) + { + KNX_addr.value = Settings.knx_CB_addr[i]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "CB #%d: %d/%d/%d " D_TO " %s"), + i+1, + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member, + device_param_cb[Settings.knx_CB_param[i]-1] ); + } +} + +#endif +#endif + + + + + +void CmndKnxTxCmnd(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings.flag.knx_enabled) { + + + + uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1); + while ( i != KNX_Empty ) { + KNX_addr.value = Settings.knx_GA_addr[i]; + knx.write_1bit(KNX_addr, !(XdrvMailbox.payload == 0)); + if (Settings.flag.knx_enable_enhancement) { + knx.write_1bit(KNX_addr, !(XdrvMailbox.payload == 0)); + knx.write_1bit(KNX_addr, !(XdrvMailbox.payload == 0)); + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), + device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], !(XdrvMailbox.payload == 0), + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); + + i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1); + } + ResponseCmndIdxChar (XdrvMailbox.data ); + } +} + +void CmndKnxTxVal(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings.flag.knx_enabled) { + + + + uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1); + while ( i != KNX_Empty ) { + KNX_addr.value = Settings.knx_GA_addr[i]; + + float tempvar = CharToFloat(XdrvMailbox.data); + dtostrfd(tempvar,2,XdrvMailbox.data); + + knx.write_2byte_float(KNX_addr, tempvar); + if (Settings.flag.knx_enable_enhancement) { + knx.write_2byte_float(KNX_addr, tempvar); + knx.write_2byte_float(KNX_addr, tempvar); + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d.%d.%d"), + device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], XdrvMailbox.data, + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); + + i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1); + } + ResponseCmndIdxChar (XdrvMailbox.data ); + } +} + +void CmndKnxEnabled(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.flag.knx_enabled = XdrvMailbox.payload; + } + ResponseCmndChar (GetStateText(Settings.flag.knx_enabled) ); +} + +void CmndKnxEnhanced(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.flag.knx_enable_enhancement = XdrvMailbox.payload; + } + ResponseCmndChar (GetStateText(Settings.flag.knx_enable_enhancement) ); +} + +void CmndKnxPa(void) +{ + if (XdrvMailbox.data_len) { + if (strstr(XdrvMailbox.data, ".") != nullptr) { + char sub_string[XdrvMailbox.data_len]; + + int pa_area = atoi(subStr(sub_string, XdrvMailbox.data, ".", 1)); + int pa_line = atoi(subStr(sub_string, XdrvMailbox.data, ".", 2)); + int pa_member = atoi(subStr(sub_string, XdrvMailbox.data, ".", 3)); + + if ( ((pa_area == 0) && (pa_line == 0) && (pa_member == 0)) + || (pa_area > 15) || (pa_line > 15) || (pa_member > 255) ) { + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; + } + + KNX_addr.pa.area = pa_area; + KNX_addr.pa.line = pa_line; + KNX_addr.pa.member = pa_member; + Settings.knx_physsical_addr = KNX_addr.value; + } + } + KNX_addr.value = Settings.knx_physsical_addr; + Response_P (PSTR("{\"%s\":\"%d.%d.%d\"}"), + XdrvMailbox.command, KNX_addr.pa.area, KNX_addr.pa.line, KNX_addr.pa.member ); +} + +void CmndKnxGa(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNX_GA)) { + if (XdrvMailbox.data_len) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { + char sub_string[XdrvMailbox.data_len]; + + int ga_option = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1)); + int ga_area = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + int ga_line = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + int ga_member = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4)); + + if ( ((ga_area == 0) && (ga_line == 0) && (ga_member == 0)) + || (ga_area > 31) || (ga_line > 7) || (ga_member > 255) + || (ga_option < 0) || ((ga_option > KNX_MAX_device_param ) && (ga_option != KNX_Empty)) + || (!device_param[ga_option-1].show) ) { + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; + } + + KNX_addr.ga.area = ga_area; + KNX_addr.ga.line = ga_line; + KNX_addr.ga.member = ga_member; + + if ( XdrvMailbox.index > Settings.knx_GA_registered ) { + Settings.knx_GA_registered ++; + XdrvMailbox.index = Settings.knx_GA_registered; + } + + Settings.knx_GA_addr[XdrvMailbox.index -1] = KNX_addr.value; + Settings.knx_GA_param[XdrvMailbox.index -1] = ga_option; + } else { + if ( (XdrvMailbox.payload <= Settings.knx_GA_registered) && (XdrvMailbox.payload > 0) ) { + XdrvMailbox.index = XdrvMailbox.payload; + } else { + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; + } + } + if ( XdrvMailbox.index <= Settings.knx_GA_registered ) { + KNX_addr.value = Settings.knx_GA_addr[XdrvMailbox.index -1]; + Response_P (PSTR("{\"%s%d\":\"%s, %d/%d/%d\"}"), + XdrvMailbox.command, XdrvMailbox.index, device_param_ga[Settings.knx_GA_param[XdrvMailbox.index-1]-1], + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member ); + } + } else { + ResponseCmndNumber (Settings.knx_GA_registered ); + } + } +} + +void CmndKnxCb(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNX_CB)) { + if (XdrvMailbox.data_len) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { + char sub_string[XdrvMailbox.data_len]; + + int cb_option = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1)); + int cb_area = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + int cb_line = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + int cb_member = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4)); + + if ( ((cb_area == 0) && (cb_line == 0) && (cb_member == 0)) + || (cb_area > 31) || (cb_line > 7) || (cb_member > 255) + || (cb_option < 0) || ((cb_option > KNX_MAX_device_param ) && (cb_option != KNX_Empty)) + || (!device_param[cb_option-1].show) ) { + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; + } + + KNX_addr.ga.area = cb_area; + KNX_addr.ga.line = cb_line; + KNX_addr.ga.member = cb_member; + + if ( XdrvMailbox.index > Settings.knx_CB_registered ) { + Settings.knx_CB_registered ++; + XdrvMailbox.index = Settings.knx_CB_registered; + } + + Settings.knx_CB_addr[XdrvMailbox.index -1] = KNX_addr.value; + Settings.knx_CB_param[XdrvMailbox.index -1] = cb_option; + } else { + if ( (XdrvMailbox.payload <= Settings.knx_CB_registered) && (XdrvMailbox.payload > 0) ) { + XdrvMailbox.index = XdrvMailbox.payload; + } else { + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; + } + } + if ( XdrvMailbox.index <= Settings.knx_CB_registered ) { + KNX_addr.value = Settings.knx_CB_addr[XdrvMailbox.index -1]; + Response_P (PSTR("{\"%s%d\":\"%s, %d/%d/%d\"}"), + XdrvMailbox.command, XdrvMailbox.index, device_param_cb[Settings.knx_CB_param[XdrvMailbox.index-1]-1], + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member ); + } + } else { + ResponseCmndNumber (Settings.knx_CB_registered ); + } + } +} + + + + + +bool Xdrv11(uint8_t function) +{ + bool result = false; + switch (function) { + case FUNC_LOOP: + if (!global_state.wifi_down) { knx.loop(); } + break; + case FUNC_EVERY_50_MSECOND: + if (toggle_inhibit) { + toggle_inhibit--; + } + break; + case FUNC_ANY_KEY: + KnxSendButtonPower(); + break; +#ifdef USE_WEBSERVER +#ifdef USE_KNX_WEB_MENU + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_KNX); + break; + case FUNC_WEB_ADD_HANDLER: + Webserver->on("/kn", HandleKNXConfiguration); + break; +#endif +#endif + case FUNC_COMMAND: + result = DecodeCommand(kKnxCommands, KnxCommand); + break; + case FUNC_PRE_INIT: + KNX_INIT(); + break; + + + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_12_home_assistant.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_12_home_assistant.ino" +#ifdef USE_HOME_ASSISTANT + +#define XDRV_12 12 + + +const char kHAssJsonSensorTypes[] PROGMEM = + D_JSON_TEMPERATURE "|" D_JSON_DEWPOINT "|" D_JSON_PRESSURE "|" D_JSON_PRESSUREATSEALEVEL "|" + D_JSON_APPARENT_POWERUSAGE "|Battery|" D_JSON_CURRENT "|" D_JSON_DISTANCE "|" D_JSON_FREQUENCY "|" D_JSON_HUMIDITY "|" D_JSON_ILLUMINANCE "|" + D_JSON_MOISTURE "|PB0.3|PB0.5|PB1|PB2.5|PB5|PB10|PM1|PM2.5|PM10|" D_JSON_POWERFACTOR "|" D_JSON_POWERUSAGE "|" + D_JSON_REACTIVE_POWERUSAGE "|" D_JSON_TODAY "|" D_JSON_TOTAL "|" D_JSON_VOLTAGE "|" D_JSON_WEIGHT "|" D_JSON_YESTERDAY "|" + D_JSON_CO2 "|" D_JSON_ECO2 "|" D_JSON_TVOC "|"; +const char kHAssJsonSensorUnits[] PROGMEM = + "||||" + "VA|%|A|Cm|Hz|%|LX|" + "%|ppd|ppd|ppd|ppd|ppd|ppd|µg/m³|µg/m³|µg/m³|Cos φ|W|" + "VAr|kWh|kWh|V|Kg|kWh|" + "ppm|ppm|ppb|"; +const char kHAssJsonSensorDevCla[] PROGMEM = + "dev_cla\":\"temperature|ic\":\"mdi:weather-rainy|dev_cla\":\"pressure|dev_cla\":\"pressure|" + "dev_cla\":\"power|dev_cla\":\"battery|ic\":\"mdi:alpha-a-circle-outline|ic\":\"mdi:leak|ic\":\"mdi:current-ac|dev_cla\":\"humidity|dev_cla\":\"illuminance|" + "ic\":\"mdi:cup-water|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|" + "ic\":\"mdi:air-filter|ic\":\"mdi:air-filter|ic\":\"mdi:air-filter|ic\":\"mdi:alpha-f-circle-outline|dev_cla\":\"power|" + "dev_cla\":\"power|dev_cla\":\"power|dev_cla\":\"power|ic\":\"mdi:alpha-v-circle-outline|ic\":\"mdi:scale|dev_cla\":\"power|" + "ic\":\"mdi:periodic-table-co2|ic\":\"mdi:air-filter|ic\":\"mdi:periodic-table-co2|"; + + + +const char HASS_DISCOVER_SENSOR[] PROGMEM = + ",\"unit_of_meas\":\"%s\",\"%s\"," + "\"frc_upd\":true," + "\"val_tpl\":\"{{value_json['%s']['%s']"; + +const char HASS_DISCOVER_BASE[] PROGMEM = + "{\"name\":\"%s\"," + "\"stat_t\":\"%s\"," + "\"avty_t\":\"%s\"," + "\"pl_avail\":\"" D_ONLINE "\"," + "\"pl_not_avail\":\"" D_OFFLINE "\""; + +const char HASS_DISCOVER_RELAY[] PROGMEM = + ",\"cmd_t\":\"%s\"," + "\"val_tpl\":\"{{value_json.%s}}\"," + "\"pl_off\":\"%s\"," + "\"pl_on\":\"%s\""; + +const char HASS_DISCOVER_BIN_SWITCH[] PROGMEM = + ",\"val_tpl\":\"{{value_json.%s}}\"," + "\"frc_upd\":true," + "\"pl_on\":\"%s\"," + "\"pl_off\":\"%s\""; + +const char HASS_DISCOVER_BIN_PIR[] PROGMEM = + ",\"val_tpl\":\"{{value_json.%s}}\"," + "\"frc_upd\":true," + "\"pl_on\":\"%s\"," + "\"off_dly\":1"; + +const char HASS_DISCOVER_LIGHT_DIMMER[] PROGMEM = + ",\"bri_cmd_t\":\"%s\"," + "\"bri_stat_t\":\"%s\"," + "\"bri_scl\":100," + "\"on_cmd_type\":\"%s\"," + "\"bri_val_tpl\":\"{{value_json." D_CMND_DIMMER "}}\""; + +const char HASS_DISCOVER_LIGHT_COLOR[] PROGMEM = + ",\"rgb_cmd_t\":\"%s2\"," + "\"rgb_stat_t\":\"%s\"," + "\"rgb_val_tpl\":\"{{value_json." D_CMND_COLOR ".split(',')[0:3]|join(',')}}\""; + +const char HASS_DISCOVER_LIGHT_WHITE[] PROGMEM = + ",\"whit_val_cmd_t\":\"%s\"," + "\"whit_val_stat_t\":\"%s\"," + "\"whit_val_scl\":100," + "\"whit_val_tpl\":\"{{value_json.Channel[3]}}\""; + +const char HASS_DISCOVER_LIGHT_CT[] PROGMEM = + ",\"clr_temp_cmd_t\":\"%s\"," + "\"clr_temp_stat_t\":\"%s\"," + "\"clr_temp_val_tpl\":\"{{value_json." D_CMND_COLORTEMPERATURE "}}\""; + +const char HASS_DISCOVER_LIGHT_SCHEME[] PROGMEM = + ",\"fx_cmd_t\":\"%s\"," + "\"fx_stat_t\":\"%s\"," + "\"fx_val_tpl\":\"{{value_json." D_CMND_SCHEME "}}\"," + "\"fx_list\":[\"0\",\"1\",\"2\",\"3\",\"4\"]"; + +const char HASS_DISCOVER_SENSOR_HASS_STATUS[] PROGMEM = + ",\"json_attr_t\":\"%s\"," + "\"unit_of_meas\":\"%%\"," + "\"val_tpl\":\"{{value_json['" D_JSON_RSSI "']}}\"," + "\"ic\":\"mdi:information-outline\""; + +const char HASS_DISCOVER_DEVICE_INFO[] PROGMEM = + ",\"uniq_id\":\"%s\"," + "\"dev\":{\"ids\":[\"%06X\"]," + "\"name\":\"%s\"," + "\"mdl\":\"%s\"," + "\"sw\":\"%s%s\"," + "\"mf\":\"Tasmota\"}"; + +const char HASS_DISCOVER_DEVICE_INFO_SHORT[] PROGMEM = + ",\"uniq_id\":\"%s\"," + "\"dev\":{\"ids\":[\"%06X\"]}"; + +const char HASS_TRIGGER_TYPE[] PROGMEM = + "{\"atype\":\"trigger\"," + "\"t\":\"%sT\"," + "\"pl\":\"{\\\"TRIG\\\":\\\"%s\\\"}\"," + "\"type\":\"%s\"," + "\"stype\":\"%s\"," + "\"dev\":{\"ids\":[\"%06X\"]}}"; + +const char kHAssTriggerType[] PROGMEM = + "none|button_short_press|button_long_press|button_double_press"; + +uint8_t hass_init_step = 0; +uint8_t hass_mode = 0; +int hass_tele_period = 0; + +void TryResponseAppend_P(const char *format, ...) +{ + va_list args; + va_start(args, format); + char dummy[2]; + int dlen = vsnprintf_P(dummy, 1, format, args); + + int mlen = strlen(mqtt_data); + int slen = sizeof(mqtt_data) - 1 - mlen; + if (dlen >= slen) + { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: MQTT discovery failed due to too long topic or friendly name. " + "Please shorten topic and friendly name. Failed to format(%u/%u):"), + dlen, slen); + va_start(args, format); + vsnprintf_P(log_data, sizeof(log_data), format, args); + AddLog(LOG_LEVEL_ERROR); + } + else + { + va_start(args, format); + vsnprintf_P(mqtt_data + mlen, slen, format, args); + } + va_end(args); +} + +void HAssAnnounceRelayLight(void) +{ + char stopic[TOPSZ]; + char stemp1[TOPSZ]; + char stemp2[TOPSZ]; + char stemp3[TOPSZ]; + char unique_id[30]; + bool is_light = false; + bool is_topic_light = false; + + for (uint32_t i = 1; i <= MAX_RELAYS; i++) + { + is_light = ((i == devices_present) && (light_type)); + is_topic_light = Settings.flag.hass_light || is_light; + + mqtt_data[0] = '\0'; + + + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%d"), ESP_getChipId(), (is_topic_light) ? "RL" : "LI", i); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s/config"), + (is_topic_light) ? "switch" : "light", unique_id); + MqttPublish(stopic, true); + + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%d"), ESP_getChipId(), (is_topic_light) ? "LI" : "RL", i); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s/config"), + (is_topic_light) ? "light" : "switch", unique_id); + + if (Settings.flag.hass_discovery && (i <= devices_present)) + { + char name[33 + 2]; + char value_template[33]; + char prefix[TOPSZ]; + char *command_topic = stemp1; + char *state_topic = stemp2; + char *availability_topic = stemp3; + + if (i > MAX_FRIENDLYNAMES) { + snprintf_P(name, sizeof(name), PSTR("%s %d"), SettingsText(SET_FRIENDLYNAME1), i); + } else { + snprintf_P(name, sizeof(name), SettingsText(SET_FRIENDLYNAME1 + i - 1)); + } + GetPowerDevice(value_template, i, sizeof(value_template), Settings.flag.device_index_enable); + GetTopic_P(command_topic, CMND, mqtt_topic, value_template); + GetTopic_P(state_topic, TELE, mqtt_topic, D_RSLT_STATE); + GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); + + Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); + TryResponseAppend_P(HASS_DISCOVER_RELAY, command_topic, value_template, SettingsText(SET_STATE_TXT1), SettingsText(SET_STATE_TXT2)); + TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId()); + +#ifdef USE_LIGHT + if (is_light +#ifdef ESP8266 + || PWM_DIMMER == my_module_type +#endif + ) + { + char *brightness_command_topic = stemp1; + + GetTopic_P(brightness_command_topic, CMND, mqtt_topic, D_CMND_DIMMER); + strncpy_P(stemp3, Settings.flag.not_power_linked ? PSTR("last") : PSTR("brightness"), sizeof(stemp3)); + TryResponseAppend_P(HASS_DISCOVER_LIGHT_DIMMER, brightness_command_topic, state_topic, stemp3); + + if (Light.subtype >= LST_RGB) + { + char *rgb_command_topic = stemp1; + + GetTopic_P(rgb_command_topic, CMND, mqtt_topic, D_CMND_COLOR); + TryResponseAppend_P(HASS_DISCOVER_LIGHT_COLOR, rgb_command_topic, state_topic); + + char *effect_command_topic = stemp1; + GetTopic_P(effect_command_topic, CMND, mqtt_topic, D_CMND_SCHEME); + TryResponseAppend_P(HASS_DISCOVER_LIGHT_SCHEME, effect_command_topic, state_topic); + } + if (LST_RGBW == Light.subtype) + { + char *white_temp_command_topic = stemp1; + + GetTopic_P(white_temp_command_topic, CMND, mqtt_topic, D_CMND_WHITE); + TryResponseAppend_P(HASS_DISCOVER_LIGHT_WHITE, white_temp_command_topic, state_topic); + } + if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) + { + char *color_temp_command_topic = stemp1; + + GetTopic_P(color_temp_command_topic, CMND, mqtt_topic, D_CMND_COLORTEMPERATURE); + TryResponseAppend_P(HASS_DISCOVER_LIGHT_CT, color_temp_command_topic, state_topic); + } + } +#endif + TryResponseAppend_P(PSTR("}")); + } + MqttPublish(stopic, true); + } +} + +void HAssAnnouncerTriggers(uint8_t device, uint8_t present, uint8_t key, uint8_t toggle, uint8_t hold) +{ + + + char stopic[TOPSZ]; + char stemp1[TOPSZ]; + char stemp2[TOPSZ]; + char unique_id[30]; + + mqtt_data[0] = '\0'; + + for (uint8_t i = 2; i <= 3; i++) { + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%d_%s"), ESP_getChipId(), key ? "SW" : "BTN", device + 1, GetStateText(i)); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/device_automation/%s/config"), unique_id); + + if (Settings.flag.hass_discovery && present) { + char name[33 + 6]; + char value_template[33]; + char prefix[TOPSZ]; + char *state_topic = stemp1; + char *availability_topic = stemp2; + char jsoname[8]; + + GetPowerDevice(value_template, device + 1, sizeof(value_template), key + Settings.flag.device_index_enable); + snprintf_P(jsoname, sizeof(jsoname), PSTR("%s%d"), key ? "SWITCH" : "BUTTON", device + 1); + GetTopic_P(state_topic, STAT, mqtt_topic, jsoname); + GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); + + char param[21]; + char subtype[9]; + uint8_t pload = toggle; + + if ((i == 2 && toggle != 0) || (i == 3 && hold != 0)) { + if (i == 3) { pload = hold; } + GetTextIndexed(param, sizeof(param), pload, kHAssTriggerType); + snprintf_P(subtype, sizeof(subtype), PSTR("%s_%d"), key ? "switch" : "button", device + 1); + Response_P(HASS_TRIGGER_TYPE, state_topic, GetStateText(i), param, subtype, ESP_getChipId()); + } else { mqtt_data[0] = '\0'; } + } + MqttPublish(stopic, true); + } +} + +void HAssAnnouncerBinSensors(uint8_t device, uint8_t present, uint8_t dual, uint8_t toggle, uint8_t pir) +{ + char stopic[TOPSZ]; + char stemp1[TOPSZ]; + char stemp2[TOPSZ]; + char unique_id[30]; + + mqtt_data[0] = '\0'; + + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_SW_%d"), ESP_getChipId(), device + 1); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/binary_sensor/%s/config"), unique_id); + + + if (Settings.flag.hass_discovery && present ) { + if (!toggle || dual) { + char name[33 + 6]; + char value_template[33]; + char prefix[TOPSZ]; + char *state_topic = stemp1; + char *availability_topic = stemp2; + char jsoname[8]; + + GetPowerDevice(value_template, device + 1, sizeof(value_template), 1 + Settings.flag.device_index_enable); + snprintf_P(jsoname, sizeof(jsoname), PSTR("SWITCH%d"), device + 1); + GetTopic_P(state_topic, STAT, mqtt_topic, jsoname); + GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); + + snprintf_P(name, sizeof(name), PSTR("%s Switch%d"), SettingsText(SET_FRIENDLYNAME1), device + 1); + Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); + if (!pir) { + TryResponseAppend_P(HASS_DISCOVER_BIN_SWITCH, PSTR(D_RSLT_STATE), SettingsText(SET_STATE_TXT2), SettingsText(SET_STATE_TXT1)); + } else { + TryResponseAppend_P(HASS_DISCOVER_BIN_PIR, PSTR(D_RSLT_STATE), SettingsText(SET_STATE_TXT2)); + } + TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId()); + TryResponseAppend_P(PSTR("}")); + } + } + MqttPublish(stopic, true); +} + +void HAssAnnounceSwitches(void) +{ + for (uint32_t switch_index = 0; switch_index < MAX_SWITCHES; switch_index++) + { + uint8_t switch_present = 0; + uint8_t dual = 0; + uint8_t toggle = 1; + uint8_t hold = 0; + uint8_t pir = 0; + + if (pin[GPIO_SWT1 + switch_index] < 99) { switch_present = 1; } + + if (KeyTopicActive(1) && strcmp(SettingsText(SET_MQTT_SWITCH_TOPIC), mqtt_topic)) + { +# 383 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_12_home_assistant.ino" + uint8_t swmode = Settings.switchmode[switch_index]; + + switch (swmode) { + case FOLLOW: + case FOLLOW_INV: + toggle = 0; + break; + case PUSHBUTTON: + case PUSHBUTTON_INV: + dual = 1; + break; + case PUSHBUTTONHOLD: + case PUSHBUTTONHOLD_INV: + dual = 1; + hold = 2; + break; + case TOGGLEMULTI: + hold = 3; + break; + case FOLLOWMULTI: + case FOLLOWMULTI_INV: + dual = 1; + toggle = 0; + hold = 3; + break; + case PUSHON: + case PUSHON_INV: + toggle = 0; + pir = 1; + } + + } else { switch_present = 0;} + + HAssAnnouncerTriggers(switch_index, switch_present, 1, toggle, hold); + HAssAnnouncerBinSensors(switch_index, switch_present, dual, toggle, pir); + } +} + + +void HAssAnnounceButtons(void) +{ + for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) + { + uint8_t button_present = 0; + uint8_t toggle = 1; + uint8_t hold = 0; + +#ifdef ESP8266 + if (!button_index && ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type))) + { + button_present = 1; + } else +#endif + { + if (pin[GPIO_KEY1 + button_index] < 99) { + button_present = 1; + } + } +# 455 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_12_home_assistant.ino" + if (Settings.flag.button_restrict) { + if (!Settings.flag.button_single) { + hold = 2; + } + } + + if (Settings.flag.button_swap) { + if (!Settings.flag.button_single) { + if (!Settings.flag.button_restrict) { + hold = 0; + } + toggle = 3; + } else {toggle = 0; hold = 0;} + } + + if (KeyTopicActive(0)) { + + if (!strcmp(SettingsText(SET_MQTT_BUTTON_TOPIC), mqtt_topic)) { + toggle = 0; + } + + } else { button_present = 0; } + + HAssAnnouncerTriggers(button_index, button_present, 0, toggle, hold); + } +} + +void HAssAnnounceSensor(const char *sensorname, const char *subsensortype, const char *MultiSubName, uint8_t subqty, uint8_t subidx, uint8_t nested, const char* SubKey) +{ + char stopic[TOPSZ]; + char stemp1[TOPSZ]; + char stemp2[TOPSZ]; + char unique_id[30]; + char subname[20]; + + mqtt_data[0] = '\0'; + + + NoAlNumToUnderscore(subname, MultiSubName); + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%s"), ESP_getChipId(), sensorname, subname); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id); + + if (Settings.flag.hass_discovery) + { + char name[33 + 42]; + char prefix[TOPSZ]; + char *state_topic = stemp1; + char *availability_topic = stemp2; + + GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_SENSOR)); + snprintf_P(name, sizeof(name), PSTR("%s %s %s"), SettingsText(SET_FRIENDLYNAME1), sensorname, MultiSubName); + GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); + + Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); + TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId()); + + + char jname[32]; + int sensor_index = GetCommandCode(jname, sizeof(jname), SubKey, kHAssJsonSensorTypes); + if (sensor_index > -1) { + + char param1[20]; + GetTextIndexed(param1, sizeof(param1), sensor_index, kHAssJsonSensorUnits); + switch (sensor_index) { + case 0: + case 1: + snprintf_P(param1, sizeof(param1), PSTR("°%c"),TempUnit()); + break; + case 2: + case 3: + snprintf_P(param1, sizeof(param1), PSTR("%s"), PressureUnit().c_str()); + break; +# 537 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_12_home_assistant.ino" + } + char param2[50]; + GetTextIndexed(param2, sizeof(param2), sensor_index, kHAssJsonSensorDevCla); + TryResponseAppend_P(HASS_DISCOVER_SENSOR, param1, param2, sensorname, subsensortype); + + if (subidx) { + TryResponseAppend_P(PSTR("[%d]"), subqty -1); + } + } else { + TryResponseAppend_P(HASS_DISCOVER_SENSOR, " ", "ic\":\"mdi:eye", sensorname, subsensortype); + } + if (nested) { + TryResponseAppend_P(PSTR("['%s']"), SubKey); + } + TryResponseAppend_P(PSTR("}}\"}")); + } + MqttPublish(stopic, true); +} + +void HAssAnnounceSensors(void) +{ + uint8_t hass_xsns_index = 0; + do + { + mqtt_data[0] = '\0'; + int tele_period_save = tele_period; + tele_period = 2; + XsnsNextCall(FUNC_JSON_APPEND, hass_xsns_index); + tele_period = tele_period_save; + + char sensordata[512]; + strlcpy(sensordata, mqtt_data, sizeof(sensordata)); + + if (strlen(sensordata)) + { + sensordata[0] = '{'; + snprintf_P(sensordata, sizeof(sensordata), PSTR("%s}"), sensordata); + + + + + StaticJsonBuffer<500> jsonBuffer; + JsonObject &root = jsonBuffer.parseObject(sensordata); + if (!root.success()) + { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: jsonBuffer failed to parse '%s'"), sensordata); + continue; + } + for (auto sensor : root) + { + const char *sensorname = sensor.key; + JsonObject &sensors = sensor.value.as(); + if (!sensors.success()) + { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: JsonObject failed to parse '%s'"), sensordata); + continue; + } + + for (auto subsensor : sensors) + { + if (subsensor.value.is()) { + + char NestedName[20]; + char NewSensorName[20]; + snprintf_P(NestedName, sizeof(NestedName), PSTR("%s"), subsensor.key); + JsonObject& subsensors = subsensor.value.as(); + for (auto subsensor : subsensors) { + snprintf_P(NewSensorName, sizeof(NewSensorName), PSTR("%s %s"), NestedName, subsensor.key); + HAssAnnounceSensor(sensorname, NestedName, NewSensorName, 0, 0, 1, subsensor.key); + } + } else if (subsensor.value.is()) { + + JsonArray& subsensors = subsensor.value.as(); + uint8_t subqty = subsensors.size(); + char MultiSubName[20]; + for (int i = 1; i <= subqty; i++) { + snprintf_P(MultiSubName, sizeof(MultiSubName), PSTR("%s %d"), subsensor.key, i); + HAssAnnounceSensor(sensorname, subsensor.key, MultiSubName, i, 1, 0, subsensor.key); + } + } else { HAssAnnounceSensor(sensorname, subsensor.key, subsensor.key, 0, 0, 0, subsensor.key);} + } + } + } + yield(); + } while (hass_xsns_index != 0); +} + +void HAssAnnounceStatusSensor(void) +{ + char stopic[TOPSZ]; + char stemp1[TOPSZ]; + char stemp2[TOPSZ]; + char unique_id[30]; + + + mqtt_data[0] = '\0'; + + + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_status"), ESP_getChipId()); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id); + + if (Settings.flag.hass_discovery) + { + char name[33 + 7]; + char prefix[TOPSZ]; + char *state_topic = stemp1; + char *availability_topic = stemp2; + + snprintf_P(name, sizeof(name), PSTR("%s status"), SettingsText(SET_FRIENDLYNAME1)); + GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_HASS_STATE)); + GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); + + Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); + TryResponseAppend_P(HASS_DISCOVER_SENSOR_HASS_STATUS, state_topic); + TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO, unique_id, ESP_getChipId(), SettingsText(SET_FRIENDLYNAME1), + ModuleName().c_str(), my_version, my_image); + TryResponseAppend_P(PSTR("}")); + } + MqttPublish(stopic, true); +} + +void HAssPublishStatus(void) +{ + Response_P(PSTR("{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\"," + "\"" D_JSON_COREVERSION "\":\"" ARDUINO_CORE_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\"," + "\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\"," + "\"WiFi " D_JSON_LINK_COUNT "\":%d,\"WiFi " D_JSON_DOWNTIME "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d," + "\"" D_JSON_BOOTCOUNT "\":%d,\"" D_JSON_SAVECOUNT "\":%d,\"" D_CMND_IPADDRESS "\":\"%s\"," + "\"" D_JSON_RSSI "\":\"%d\",\"LoadAvg\":%lu}"), + my_version, my_image, GetBuildDateAndTime().c_str(), ESP.getSdkVersion(), ModuleName().c_str(), + GetResetReason().c_str(), GetUptime().c_str(), WifiLinkCount(), WifiDowntime().c_str(), MqttConnectCount(), + Settings.bootcount, Settings.save_flag, WiFi.localIP().toString().c_str(), + WifiGetRssiAsQuality(WiFi.RSSI()), loop_load_avg); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_HASS_STATE)); +} + +void HAssDiscovery(void) +{ + + if (Settings.flag.hass_discovery) + { + Settings.flag.mqtt_response = 0; + Settings.flag.decimal_text = 1; + Settings.flag3.hass_tele_on_power = 1; + + + Settings.light_scheme = 0; + } + + if (Settings.flag.hass_discovery || (1 == hass_mode)) + { + + HAssAnnounceRelayLight(); + + + HAssAnnounceButtons(); + + + HAssAnnounceSwitches(); + + + HAssAnnounceSensors(); + + + HAssAnnounceStatusSensor(); + } +} + +void HAssDiscover(void) +{ + hass_mode = 1; + hass_init_step = 1; +} + +void HAssAnyKey(void) +{ + if (!Settings.flag.hass_discovery) + { + return; + } + + uint32_t key = (XdrvMailbox.payload >> 16) & 0xFF; + uint32_t device = XdrvMailbox.payload & 0xFF; + uint32_t state = (XdrvMailbox.payload >> 8) & 0xFF; + + if (!key && KeyTopicActive(0)) { + device = (XdrvMailbox.payload >> 24) & 0xFF; + } + + char scommand[CMDSZ]; + char sw_topic[TOPSZ]; + char key_topic[TOPSZ]; + char *tmpbtn = SettingsText(SET_MQTT_BUTTON_TOPIC); + char *tmpsw = SettingsText(SET_MQTT_SWITCH_TOPIC); + uint8_t evkey = 0; + Format(sw_topic, tmpsw, sizeof(sw_topic)); + Format(key_topic, tmpbtn, sizeof(key_topic)); + + if (state == 2 || state == 3 ) { evkey = 1;} + snprintf_P(scommand, sizeof(scommand), PSTR("%s%d%s"), (key) ? "SWITCH" : "BUTTON", device, (evkey) ? "T" : ""); + + char stopic[TOPSZ]; + + GetTopic_P(stopic, STAT, mqtt_topic, scommand); + Response_P(S_JSON_COMMAND_SVALUE, (evkey) ? "TRIG" : PSTR(D_RSLT_STATE), GetStateText(state)); + MqttPublish(stopic); +} + + + + + +bool Xdrv12(uint8_t function) +{ + bool result = false; + + if (Settings.flag.mqtt_enabled) + { + switch (function) + { + case FUNC_EVERY_SECOND: + if (hass_init_step) + { + hass_init_step--; + if (!hass_init_step) + { + HAssDiscovery(); + } + } + else if (Settings.flag.hass_discovery && Settings.tele_period) + { + hass_tele_period++; + if (hass_tele_period >= Settings.tele_period) + { + hass_tele_period = 0; + + mqtt_data[0] = '\0'; + HAssPublishStatus(); + } + } + break; + case FUNC_ANY_KEY: + HAssAnyKey(); + break; + case FUNC_MQTT_INIT: + hass_mode = 0; + hass_init_step = 2; + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_13_display.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_13_display.ino" +#if defined(USE_I2C) || defined(USE_SPI) +#ifdef USE_DISPLAY + +#define XDRV_13 13 + +#include +#include + +Renderer *renderer; + +enum ColorType { COLOR_BW, COLOR_COLOR }; + +#ifndef MAXBUTTONS +#define MAXBUTTONS 16 +#endif + +#ifdef USE_TOUCH_BUTTONS +VButton *buttons[MAXBUTTONS]; +#endif + + + +uint16_t fg_color = 1; +uint16_t bg_color = 0; +uint8_t color_type = COLOR_BW; +uint8_t auto_draw=1; + +const uint8_t DISPLAY_MAX_DRIVERS = 16; +const uint8_t DISPLAY_MAX_COLS = 44; +const uint8_t DISPLAY_MAX_ROWS = 32; + +const uint8_t DISPLAY_LOG_ROWS = 32; + +#define D_PRFX_DISPLAY "Display" +#define D_CMND_DISP_ADDRESS "Address" +#define D_CMND_DISP_COLS "Cols" +#define D_CMND_DISP_DIMMER "Dimmer" +#define D_CMND_DISP_MODE "Mode" +#define D_CMND_DISP_MODEL "Model" +#define D_CMND_DISP_REFRESH "Refresh" +#define D_CMND_DISP_ROWS "Rows" +#define D_CMND_DISP_SIZE "Size" +#define D_CMND_DISP_FONT "Font" +#define D_CMND_DISP_ROTATE "Rotate" +#define D_CMND_DISP_TEXT "Text" +#define D_CMND_DISP_WIDTH "Width" +#define D_CMND_DISP_HEIGHT "Height" + +enum XdspFunctions { FUNC_DISPLAY_INIT_DRIVER, FUNC_DISPLAY_INIT, FUNC_DISPLAY_EVERY_50_MSECOND, FUNC_DISPLAY_EVERY_SECOND, + FUNC_DISPLAY_MODEL, FUNC_DISPLAY_MODE, FUNC_DISPLAY_POWER, + FUNC_DISPLAY_CLEAR, FUNC_DISPLAY_DRAW_FRAME, + FUNC_DISPLAY_DRAW_HLINE, FUNC_DISPLAY_DRAW_VLINE, FUNC_DISPLAY_DRAW_LINE, + FUNC_DISPLAY_DRAW_CIRCLE, FUNC_DISPLAY_FILL_CIRCLE, + FUNC_DISPLAY_DRAW_RECTANGLE, FUNC_DISPLAY_FILL_RECTANGLE, + FUNC_DISPLAY_TEXT_SIZE, FUNC_DISPLAY_FONT_SIZE, FUNC_DISPLAY_ROTATION, FUNC_DISPLAY_DRAW_STRING, FUNC_DISPLAY_ONOFF }; + +enum DisplayInitModes { DISPLAY_INIT_MODE, DISPLAY_INIT_PARTIAL, DISPLAY_INIT_FULL }; + +const char kDisplayCommands[] PROGMEM = D_PRFX_DISPLAY "|" + "|" D_CMND_DISP_MODEL "|" D_CMND_DISP_WIDTH "|" D_CMND_DISP_HEIGHT "|" D_CMND_DISP_MODE "|" D_CMND_DISP_REFRESH "|" + D_CMND_DISP_DIMMER "|" D_CMND_DISP_COLS "|" D_CMND_DISP_ROWS "|" D_CMND_DISP_SIZE "|" D_CMND_DISP_FONT "|" + D_CMND_DISP_ROTATE "|" D_CMND_DISP_TEXT "|" D_CMND_DISP_ADDRESS ; + +void (* const DisplayCommand[])(void) PROGMEM = { + &CmndDisplay, &CmndDisplayModel, &CmndDisplayWidth, &CmndDisplayHeight, &CmndDisplayMode, &CmndDisplayRefresh, + &CmndDisplayDimmer, &CmndDisplayColumns, &CmndDisplayRows, &CmndDisplaySize, &CmndDisplayFont, + &CmndDisplayRotate, &CmndDisplayText, &CmndDisplayAddress }; + +char *dsp_str; + +uint16_t dsp_x; +uint16_t dsp_y; +uint16_t dsp_x2; +uint16_t dsp_y2; +uint16_t dsp_rad; +uint16_t dsp_color; +int16_t dsp_len; +int16_t disp_xpos = 0; +int16_t disp_ypos = 0; + +uint8_t disp_power = 0; +uint8_t disp_device = 0; +uint8_t disp_refresh = 1; +uint8_t disp_autodraw = 1; +uint8_t dsp_init; +uint8_t dsp_font; +uint8_t dsp_flag; +uint8_t dsp_on; + +#ifdef USE_DISPLAY_MODES1TO5 + +char **disp_log_buffer; +char **disp_screen_buffer; +char disp_temp[2]; +char disp_pres[5]; + +uint8_t disp_log_buffer_cols = 0; +uint8_t disp_log_buffer_idx = 0; +uint8_t disp_log_buffer_ptr = 0; +uint8_t disp_screen_buffer_cols = 0; +uint8_t disp_screen_buffer_rows = 0; +bool disp_subscribed = false; + +#endif + + + +void DisplayInit(uint8_t mode) +{ + if (renderer) { + renderer->DisplayInit(mode, Settings.display_size, Settings.display_rotate, Settings.display_font); + } + else { + dsp_init = mode; + XdspCall(FUNC_DISPLAY_INIT); + } +} + +void DisplayClear(void) +{ + XdspCall(FUNC_DISPLAY_CLEAR); +} + +void DisplayDrawHLine(uint16_t x, uint16_t y, int16_t len, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_len = len; + dsp_color = color; + XdspCall(FUNC_DISPLAY_DRAW_HLINE); +} + +void DisplayDrawVLine(uint16_t x, uint16_t y, int16_t len, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_len = len; + dsp_color = color; + XdspCall(FUNC_DISPLAY_DRAW_VLINE); +} + +void DisplayDrawLine(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_x2 = x2; + dsp_y2 = y2; + dsp_color = color; + XdspCall(FUNC_DISPLAY_DRAW_LINE); +} + +void DisplayDrawCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_rad = rad; + dsp_color = color; + XdspCall(FUNC_DISPLAY_DRAW_CIRCLE); +} + +void DisplayDrawFilledCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_rad = rad; + dsp_color = color; + XdspCall(FUNC_DISPLAY_FILL_CIRCLE); +} + +void DisplayDrawRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_x2 = x2; + dsp_y2 = y2; + dsp_color = color; + XdspCall(FUNC_DISPLAY_DRAW_RECTANGLE); +} + +void DisplayDrawFilledRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_x2 = x2; + dsp_y2 = y2; + dsp_color = color; + XdspCall(FUNC_DISPLAY_FILL_RECTANGLE); +} + +void DisplayDrawFrame(void) +{ + XdspCall(FUNC_DISPLAY_DRAW_FRAME); +} + +void DisplaySetSize(uint8_t size) +{ + Settings.display_size = size &3; + XdspCall(FUNC_DISPLAY_TEXT_SIZE); +} + +void DisplaySetFont(uint8_t font) +{ + Settings.display_font = font &3; + XdspCall(FUNC_DISPLAY_FONT_SIZE); +} + +void DisplaySetRotation(uint8_t rotation) +{ + Settings.display_rotate = rotation &3; + XdspCall(FUNC_DISPLAY_ROTATION); +} + +void DisplayDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) +{ + dsp_x = x; + dsp_y = y; + dsp_str = str; + dsp_color = color; + dsp_flag = flag; + XdspCall(FUNC_DISPLAY_DRAW_STRING); +} + +void DisplayOnOff(uint8_t on) +{ + dsp_on = on; + XdspCall(FUNC_DISPLAY_ONOFF); +} + + + + +uint8_t fatoiv(char *cp,float *res) { + uint8_t index=0; + *res=CharToFloat(cp); + while (*cp) { + if ((*cp>='0' && *cp<='9') || (*cp=='-') || (*cp=='.')) { + cp++; + index++; + } else { + break; + } + } + return index; +} + + +uint8_t atoiv(char *cp, int16_t *res) +{ + uint8_t index = 0; + *res = atoi(cp); + while (*cp) { + if ((*cp>='0' && *cp<='9') || (*cp=='-')) { + cp++; + index++; + } else { + break; + } + } + return index; +} + + +uint8_t atoiV(char *cp, uint16_t *res) +{ + uint8_t index = 0; + *res = atoi(cp); + while (*cp) { + if (*cp>='0' && *cp<='9') { + cp++; + index++; + } else { + break; + } + } + return index; +} + + +void alignright(char *string) { + uint16_t slen=strlen(string); + uint16_t len=slen; + while (len) { + + if (string[len-1]!=' ') { + break; + } + len--; + } + uint16_t diff=slen-len; + if (diff>0) { + + memmove(&string[diff],string,len); + memset(string,' ',diff); + } +} + +char *get_string(char *buff,uint8_t len,char *cp) { +uint8_t index=0; + while (*cp!=':') { + buff[index]=*cp++; + index++; + if (index>=len) break; + } + buff[index]=0; + cp++; + return cp; +} + +#define ESCAPE_CHAR '~' + + +uint32_t decode_te(char *line) { + uint32_t skip = 0; + char sbuf[3],*cp; + while (*line) { + if (*line==ESCAPE_CHAR) { + cp=line+1; + if (*cp!=0 && *cp==ESCAPE_CHAR) { + + memmove(cp,cp+1,strlen(cp)); + skip++; + } else { + + if (strlen(cp)<2) { + + return skip; + } + + sbuf[0]=*(cp); + sbuf[1]=*(cp+1); + sbuf[2]=0; + *line=strtol(sbuf,0,16); + + memmove(cp,cp+2,strlen(cp)-1); + skip += 2; + } + } + line++; + } + return skip; +} + + + +#define DISPLAY_BUFFER_COLS 128 + +void DisplayText(void) +{ + uint8_t lpos; + uint8_t escape = 0; + uint8_t var; + int16_t lin = 0; + int16_t col = 0; + int16_t fill = 0; + int16_t temp; + int16_t temp1; + float ftemp; + + char linebuf[DISPLAY_BUFFER_COLS]; + char *dp = linebuf; + char *cp = XdrvMailbox.data; + + memset(linebuf, ' ', sizeof(linebuf)); + linebuf[sizeof(linebuf)-1] = 0; + *dp = 0; + + while (*cp) { + if (!escape) { + + if (*cp == '[') { + escape = 1; + cp++; + + if ((uint32_t)dp - (uint32_t)linebuf) { + if (!fill) { *dp = 0; } + if (col > 0 && lin > 0) { + + if (!renderer) DisplayDrawStringAt(col, lin, linebuf, fg_color, 1); + else renderer->DrawStringAt(col, lin, linebuf, fg_color, 1); + } else { + + if (!renderer) DisplayDrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); + else renderer->DrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); + } + memset(linebuf, ' ', sizeof(linebuf)); + linebuf[sizeof(linebuf)-1] = 0; + dp = linebuf; + } + } else { + + if (dp < (linebuf + DISPLAY_BUFFER_COLS)) { *dp++ = *cp++; } + } + } else { + + if (*cp == ']') { + escape = 0; + cp++; + } else { + + switch (*cp++) { + case 'z': + + if (!renderer) DisplayClear(); + else renderer->fillScreen(bg_color); + disp_xpos = 0; + disp_ypos = 0; + col = 0; + lin = 0; + break; + case 'i': + + DisplayInit(DISPLAY_INIT_PARTIAL); + break; + case 'I': + + DisplayInit(DISPLAY_INIT_FULL); + break; + case 'o': + if (!renderer) { + DisplayOnOff(0); + } else { + renderer->DisplayOnff(0); + } + break; + case 'O': + if (!renderer) { + DisplayOnOff(1); + } else { + renderer->DisplayOnff(1); + } + break; + case 'x': + + var = atoiv(cp, &disp_xpos); + cp += var; + break; + case 'y': + + var = atoiv(cp, &disp_ypos); + cp += var; + break; + case 'l': + + var = atoiv(cp, &lin); + cp += var; + + break; + case 'c': + + var = atoiv(cp, &col); + cp += var; + + break; + case 'C': + + if (*cp=='i') { + + cp++; + var = atoiv(cp, &temp); + if (renderer) ftemp=renderer->GetColorFromIndex(temp); + } else { + + var = fatoiv(cp,&ftemp); + } + fg_color=ftemp; + cp += var; + if (renderer) renderer->setTextColor(fg_color,bg_color); + break; + case 'B': + + if (*cp=='i') { + + cp++; + var = atoiv(cp, &temp); + if (renderer) ftemp=renderer->GetColorFromIndex(temp); + } else { + var = fatoiv(cp,&ftemp); + } + bg_color=ftemp; + cp += var; + if (renderer) renderer->setTextColor(fg_color,bg_color); + break; + case 'p': + + var = atoiv(cp, &fill); + cp += var; + linebuf[fill] = 0; + break; +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) + case 'P': + { char *ep=strchr(cp,':'); + if (ep) { + *ep=0; + ep++; + Draw_RGB_Bitmap(cp,disp_xpos,disp_ypos); + cp=ep; + } + } + break; +#endif + case 'h': + + var = atoiv(cp, &temp); + cp += var; + if (temp < 0) { + if (renderer) renderer->writeFastHLine(disp_xpos + temp, disp_ypos, -temp, fg_color); + else DisplayDrawHLine(disp_xpos + temp, disp_ypos, -temp, fg_color); + } else { + if (renderer) renderer->writeFastHLine(disp_xpos, disp_ypos, temp, fg_color); + else DisplayDrawHLine(disp_xpos, disp_ypos, temp, fg_color); + } + disp_xpos += temp; + break; + case 'v': + + var = atoiv(cp, &temp); + cp += var; + if (temp < 0) { + if (renderer) renderer->writeFastVLine(disp_xpos, disp_ypos + temp, -temp, fg_color); + else DisplayDrawVLine(disp_xpos, disp_ypos + temp, -temp, fg_color); + } else { + if (renderer) renderer->writeFastVLine(disp_xpos, disp_ypos, temp, fg_color); + else DisplayDrawVLine(disp_xpos, disp_ypos, temp, fg_color); + } + disp_ypos += temp; + break; + case 'L': + + var = atoiv(cp, &temp); + cp += var; + cp++; + var = atoiv(cp, &temp1); + cp += var; + if (renderer) renderer->writeLine(disp_xpos, disp_ypos, temp, temp1, fg_color); + else DisplayDrawLine(disp_xpos, disp_ypos, temp, temp1, fg_color); + disp_xpos += temp; + disp_ypos += temp1; + break; + case 'k': + + var = atoiv(cp, &temp); + cp += var; + if (renderer) renderer->drawCircle(disp_xpos, disp_ypos, temp, fg_color); + else DisplayDrawCircle(disp_xpos, disp_ypos, temp, fg_color); + break; + case 'K': + + var = atoiv(cp, &temp); + cp += var; + if (renderer) renderer->fillCircle(disp_xpos, disp_ypos, temp, fg_color); + else DisplayDrawFilledCircle(disp_xpos, disp_ypos, temp, fg_color); + break; + case 'r': + + var = atoiv(cp, &temp); + cp += var; + cp++; + var = atoiv(cp, &temp1); + cp += var; + if (renderer) renderer->drawRect(disp_xpos, disp_ypos, temp, temp1, fg_color); + else DisplayDrawRectangle(disp_xpos, disp_ypos, temp, temp1, fg_color); + break; + case 'R': + + var = atoiv(cp, &temp); + cp += var; + cp++; + var = atoiv(cp, &temp1); + cp += var; + if (renderer) renderer->fillRect(disp_xpos, disp_ypos, temp, temp1, fg_color); + else DisplayDrawFilledRectangle(disp_xpos, disp_ypos, temp, temp1, fg_color); + break; + case 'u': + + { int16_t rad; + var = atoiv(cp, &temp); + cp += var; + cp++; + var = atoiv(cp, &temp1); + cp += var; + cp++; + var = atoiv(cp, &rad); + cp += var; + if (renderer) renderer->drawRoundRect(disp_xpos, disp_ypos, temp, temp1, rad, fg_color); + + } + break; + case 'U': + + { int16_t rad; + var = atoiv(cp, &temp); + cp += var; + cp++; + var = atoiv(cp, &temp1); + cp += var; + cp++; + var = atoiv(cp, &rad); + cp += var; + if (renderer) renderer->fillRoundRect(disp_xpos, disp_ypos, temp, temp1, rad, fg_color); + + } + break; + + case 't': + if (*cp=='S') { + cp++; + if (dp < (linebuf + DISPLAY_BUFFER_COLS) -8) { + snprintf_P(dp, 9, PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + dp += 8; + } + } else { + if (dp < (linebuf + DISPLAY_BUFFER_COLS) -5) { + snprintf_P(dp, 6, PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute); + dp += 5; + } + } + break; + case 'T': + if (dp < (linebuf + DISPLAY_BUFFER_COLS) -8) { + snprintf_P(dp, 9, PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%02d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year%2000); + dp += 8; + } + break; + case 'd': + + if (renderer) renderer->Updateframe(); + else DisplayDrawFrame(); + break; + case 'D': + + auto_draw=*cp&3; + if (renderer) renderer->setDrawMode(auto_draw>>1); + cp += 1; + break; + case 's': + + if (renderer) renderer->setTextSize(*cp&7); + else DisplaySetSize(*cp&3); + cp += 1; + break; + case 'f': + + if (renderer) renderer->setTextFont(*cp&7); + else DisplaySetFont(*cp&7); + cp += 1; + break; + case 'a': + + if (renderer) renderer->setRotation(*cp&3); + else DisplaySetRotation(*cp&3); + cp+=1; + break; + +#ifdef USE_GRAPH + case 'G': + + if (*cp=='d') { + cp++; + var=atoiv(cp,&temp); + cp+=var; + cp++; + var=atoiv(cp,&temp1); + cp+=var; + RedrawGraph(temp,temp1); + break; + } +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) + if (*cp=='s') { + cp++; + var=atoiv(cp,&temp); + cp+=var; + cp++; + + char bbuff[128]; + cp=get_string(bbuff,sizeof(bbuff),cp); + Save_graph(temp,bbuff); + break; + } + if (*cp=='r') { + cp++; + var=atoiv(cp,&temp); + cp+=var; + cp++; + + char bbuff[128]; + cp=get_string(bbuff,sizeof(bbuff),cp); + Restore_graph(temp,bbuff); + break; + } +#endif + { int16_t num,gxp,gyp,gxs,gys,dec,icol; + float ymin,ymax; + var=atoiv(cp,&num); + cp+=var; + cp++; + var=atoiv(cp,&gxp); + cp+=var; + cp++; + var=atoiv(cp,&gyp); + cp+=var; + cp++; + var=atoiv(cp,&gxs); + cp+=var; + cp++; + var=atoiv(cp,&gys); + cp+=var; + cp++; + var=atoiv(cp,&dec); + cp+=var; + cp++; + var=fatoiv(cp,&ymin); + cp+=var; + cp++; + var=fatoiv(cp,&ymax); + cp+=var; + if (color_type==COLOR_COLOR) { + + cp++; + var=atoiv(cp,&icol); + cp+=var; + } else { + icol=0; + } + DefineGraph(num,gxp,gyp,gxs,gys,dec,ymin,ymax,icol); + } + break; + case 'g': + { float temp; + int16_t num; + var=atoiv(cp,&num); + cp+=var; + cp++; + var=fatoiv(cp,&temp); + cp+=var; + AddValue(num,temp); + } + break; +#endif + +#ifdef USE_AWATCH + case 'w': + var = atoiv(cp, &temp); + cp += var; + DrawAClock(temp); + break; +#endif + +#ifdef USE_TOUCH_BUTTONS + case 'b': + { int16_t num,gxp,gyp,gxs,gys,outline,fill,textcolor,textsize; + var=atoiv(cp,&num); + cp+=var; + cp++; + uint8_t bflags=num>>8; + num=num%MAXBUTTONS; + var=atoiv(cp,&gxp); + cp+=var; + cp++; + var=atoiv(cp,&gyp); + cp+=var; + cp++; + var=atoiv(cp,&gxs); + cp+=var; + cp++; + var=atoiv(cp,&gys); + cp+=var; + cp++; + var=atoiv(cp,&outline); + cp+=var; + cp++; + var=atoiv(cp,&fill); + cp+=var; + cp++; + var=atoiv(cp,&textcolor); + cp+=var; + cp++; + var=atoiv(cp,&textsize); + cp+=var; + cp++; + + char bbuff[32]; + cp=get_string(bbuff,sizeof(bbuff),cp); + + if (buttons[num]) { + delete buttons[num]; + } + if (renderer) { + buttons[num]= new VButton(); + if (buttons[num]) { + buttons[num]->vpower=bflags; + buttons[num]->initButtonUL(renderer,gxp,gyp,gxs,gys,renderer->GetColorFromIndex(outline),\ + renderer->GetColorFromIndex(fill),renderer->GetColorFromIndex(textcolor),bbuff,textsize); + if (!bflags) { + + buttons[num]->xdrawButton(bitRead(power,num)); + } else { + + buttons[num]->vpower&=0x7f; + buttons[num]->xdrawButton(buttons[num]->vpower&0x80); + } + } + } + } + break; +#endif + default: + + Response_P(PSTR("Unknown Escape")); + goto exit; + break; + } + } + } + } + exit: + + dp -= decode_te(linebuf); + if ((uint32_t)dp - (uint32_t)linebuf) { + if (!fill) { + *dp = 0; + } else { + linebuf[abs(int(fill))] = 0; + } + if (fill<0) { + + alignright(linebuf); + } + if (col > 0 && lin > 0) { + + if (!renderer) DisplayDrawStringAt(col, lin, linebuf, fg_color, 1); + else renderer->DrawStringAt(col, lin, linebuf, fg_color, 1); + } else { + + if (!renderer) DisplayDrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); + else renderer->DrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); + } + } + + if (auto_draw&1) { + if (renderer) renderer->Updateframe(); + else DisplayDrawFrame(); + } +} + + + +#ifdef USE_DISPLAY_MODES1TO5 + +void DisplayClearScreenBuffer(void) +{ + if (disp_screen_buffer_cols) { + for (uint32_t i = 0; i < disp_screen_buffer_rows; i++) { + memset(disp_screen_buffer[i], 0, disp_screen_buffer_cols); + } + } +} + +void DisplayFreeScreenBuffer(void) +{ + if (disp_screen_buffer != nullptr) { + for (uint32_t i = 0; i < disp_screen_buffer_rows; i++) { + if (disp_screen_buffer[i] != nullptr) { free(disp_screen_buffer[i]); } + } + free(disp_screen_buffer); + disp_screen_buffer_cols = 0; + disp_screen_buffer_rows = 0; + } +} + +void DisplayAllocScreenBuffer(void) +{ + if (!disp_screen_buffer_cols) { + disp_screen_buffer_rows = Settings.display_rows; + disp_screen_buffer = (char**)malloc(sizeof(*disp_screen_buffer) * disp_screen_buffer_rows); + if (disp_screen_buffer != nullptr) { + for (uint32_t i = 0; i < disp_screen_buffer_rows; i++) { + disp_screen_buffer[i] = (char*)malloc(sizeof(*disp_screen_buffer[i]) * (Settings.display_cols[0] +1)); + if (disp_screen_buffer[i] == nullptr) { + DisplayFreeScreenBuffer(); + break; + } + } + } + if (disp_screen_buffer != nullptr) { + disp_screen_buffer_cols = Settings.display_cols[0] +1; + DisplayClearScreenBuffer(); + } + } +} + +void DisplayReAllocScreenBuffer(void) +{ + DisplayFreeScreenBuffer(); + DisplayAllocScreenBuffer(); +} + +void DisplayFillScreen(uint32_t line) +{ + uint32_t len = disp_screen_buffer_cols - strlen(disp_screen_buffer[line]); + if (len) { + memset(disp_screen_buffer[line] + strlen(disp_screen_buffer[line]), 0x20, len); + disp_screen_buffer[line][disp_screen_buffer_cols -1] = 0; + } +} + + + +void DisplayClearLogBuffer(void) +{ + if (disp_log_buffer_cols) { + for (uint32_t i = 0; i < DISPLAY_LOG_ROWS; i++) { + memset(disp_log_buffer[i], 0, disp_log_buffer_cols); + } + } +} + +void DisplayFreeLogBuffer(void) +{ + if (disp_log_buffer != nullptr) { + for (uint32_t i = 0; i < DISPLAY_LOG_ROWS; i++) { + if (disp_log_buffer[i] != nullptr) { free(disp_log_buffer[i]); } + } + free(disp_log_buffer); + disp_log_buffer_cols = 0; + } +} + +void DisplayAllocLogBuffer(void) +{ + if (!disp_log_buffer_cols) { + disp_log_buffer = (char**)malloc(sizeof(*disp_log_buffer) * DISPLAY_LOG_ROWS); + if (disp_log_buffer != nullptr) { + for (uint32_t i = 0; i < DISPLAY_LOG_ROWS; i++) { + disp_log_buffer[i] = (char*)malloc(sizeof(*disp_log_buffer[i]) * (Settings.display_cols[0] +1)); + if (disp_log_buffer[i] == nullptr) { + DisplayFreeLogBuffer(); + break; + } + } + } + if (disp_log_buffer != nullptr) { + disp_log_buffer_cols = Settings.display_cols[0] +1; + DisplayClearLogBuffer(); + } + } +} + +void DisplayReAllocLogBuffer(void) +{ + DisplayFreeLogBuffer(); + DisplayAllocLogBuffer(); +} + +void DisplayLogBufferAdd(char* txt) +{ + if (disp_log_buffer_cols) { + strlcpy(disp_log_buffer[disp_log_buffer_idx], txt, disp_log_buffer_cols); + disp_log_buffer_idx++; + if (DISPLAY_LOG_ROWS == disp_log_buffer_idx) { disp_log_buffer_idx = 0; } + } +} + +char* DisplayLogBuffer(char temp_code) +{ + char* result = nullptr; + if (disp_log_buffer_cols) { + if (disp_log_buffer_idx != disp_log_buffer_ptr) { + result = disp_log_buffer[disp_log_buffer_ptr]; + disp_log_buffer_ptr++; + if (DISPLAY_LOG_ROWS == disp_log_buffer_ptr) { disp_log_buffer_ptr = 0; } + + char *pch = strchr(result, '~'); + if (pch != nullptr) { result[pch - result] = temp_code; } + } + } + return result; +} + +void DisplayLogBufferInit(void) +{ + if (Settings.display_mode) { + disp_log_buffer_idx = 0; + disp_log_buffer_ptr = 0; + disp_refresh = Settings.display_refresh; + + snprintf_P(disp_temp, sizeof(disp_temp), PSTR("%c"), TempUnit()); + snprintf_P(disp_pres, sizeof(disp_pres), PressureUnit().c_str()); + + DisplayReAllocLogBuffer(); + + char buffer[40]; + snprintf_P(buffer, sizeof(buffer), PSTR(D_VERSION " %s%s"), my_version, my_image); + DisplayLogBufferAdd(buffer); + snprintf_P(buffer, sizeof(buffer), PSTR("Display mode %d"), Settings.display_mode); + DisplayLogBufferAdd(buffer); + + snprintf_P(buffer, sizeof(buffer), PSTR(D_CMND_HOSTNAME " %s"), my_hostname); + DisplayLogBufferAdd(buffer); + snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_SSID " %s"), SettingsText(SET_STASSID1 + Settings.sta_active)); + DisplayLogBufferAdd(buffer); + snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_MAC " %s"), WiFi.macAddress().c_str()); + DisplayLogBufferAdd(buffer); + if (!global_state.wifi_down) { + snprintf_P(buffer, sizeof(buffer), PSTR("IP %s"), WiFi.localIP().toString().c_str()); + DisplayLogBufferAdd(buffer); + snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_RSSI " %d%%"), WifiGetRssiAsQuality(WiFi.RSSI())); + DisplayLogBufferAdd(buffer); + } + } +} + + + + + +enum SensorQuantity { + JSON_TEMPERATURE, + JSON_HUMIDITY, JSON_LIGHT, JSON_NOISE, JSON_AIRQUALITY, + JSON_PRESSURE, JSON_PRESSUREATSEALEVEL, + JSON_ILLUMINANCE, + JSON_GAS, + JSON_YESTERDAY, JSON_TOTAL, JSON_TODAY, + JSON_PERIOD, + JSON_POWERFACTOR, JSON_COUNTER, JSON_ANALOG_INPUT, JSON_UV_LEVEL, + JSON_CURRENT, + JSON_VOLTAGE, + JSON_POWERUSAGE, + JSON_CO2, + JSON_FREQUENCY }; +const char kSensorQuantity[] PROGMEM = + D_JSON_TEMPERATURE "|" + D_JSON_HUMIDITY "|" D_JSON_LIGHT "|" D_JSON_NOISE "|" D_JSON_AIRQUALITY "|" + D_JSON_PRESSURE "|" D_JSON_PRESSUREATSEALEVEL "|" + D_JSON_ILLUMINANCE "|" + D_JSON_GAS "|" + D_JSON_YESTERDAY "|" D_JSON_TOTAL "|" D_JSON_TODAY "|" + D_JSON_PERIOD "|" + D_JSON_POWERFACTOR "|" D_JSON_COUNTER "|" D_JSON_ANALOG_INPUT "|" D_JSON_UV_LEVEL "|" + D_JSON_CURRENT "|" + D_JSON_VOLTAGE "|" + D_JSON_POWERUSAGE "|" + D_JSON_CO2 "|" + D_JSON_FREQUENCY ; + +void DisplayJsonValue(const char* topic, const char* device, const char* mkey, const char* value) +{ + char quantity[TOPSZ]; + char buffer[Settings.display_cols[0] +1]; + char spaces[Settings.display_cols[0]]; + char source[Settings.display_cols[0] - Settings.display_cols[1]]; + char svalue[Settings.display_cols[1] +1]; + +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("DisplayJsonValue")); +#endif + + memset(spaces, 0x20, sizeof(spaces)); + spaces[sizeof(spaces) -1] = '\0'; + snprintf_P(source, sizeof(source), PSTR("%s%s%s%s"), topic, (strlen(topic))?"/":"", mkey, spaces); + + int quantity_code = GetCommandCode(quantity, sizeof(quantity), mkey, kSensorQuantity); + if ((-1 == quantity_code) || !strcmp_P(mkey, S_RSLT_POWER)) { + return; + } + if (JSON_TEMPERATURE == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s~%s"), value, disp_temp); + } + else if ((quantity_code >= JSON_HUMIDITY) && (quantity_code <= JSON_AIRQUALITY)) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s%%"), value); + } + else if ((quantity_code >= JSON_PRESSURE) && (quantity_code <= JSON_PRESSUREATSEALEVEL)) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s%s"), value, disp_pres); + } + else if (JSON_ILLUMINANCE == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_LUX), value); + } + else if (JSON_GAS == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_KILOOHM), value); + } + else if ((quantity_code >= JSON_YESTERDAY) && (quantity_code <= JSON_TODAY)) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_KILOWATTHOUR), value); + } + else if (JSON_PERIOD == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_WATTHOUR), value); + } + else if ((quantity_code >= JSON_POWERFACTOR) && (quantity_code <= JSON_UV_LEVEL)) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s"), value); + } + else if (JSON_CURRENT == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_AMPERE), value); + } + else if (JSON_VOLTAGE == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_VOLT), value); + } + else if (JSON_POWERUSAGE == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_WATT), value); + } + else if (JSON_CO2 == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_PARTS_PER_MILLION), value); + } + else if (JSON_FREQUENCY == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_HERTZ), value); + } + snprintf_P(buffer, sizeof(buffer), PSTR("%s %s"), source, svalue); + + + + DisplayLogBufferAdd(buffer); +} + +void DisplayAnalyzeJson(char *topic, char *json) +{ +# 1145 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_13_display.ino" + String jsonStr = json; + + StaticJsonBuffer<1024> jsonBuf; + JsonObject &root = jsonBuf.parseObject(jsonStr); + if (root.success()) { + + const char *unit; + unit = root[D_JSON_TEMPERATURE_UNIT]; + if (unit) { + snprintf_P(disp_temp, sizeof(disp_temp), PSTR("%s"), unit); + } + unit = root[D_JSON_PRESSURE_UNIT]; + if (unit) { + snprintf_P(disp_pres, sizeof(disp_pres), PSTR("%s"), unit); + } + + for (JsonObject::iterator it = root.begin(); it != root.end(); ++it) { + JsonVariant value = it->value; + if (value.is()) { + JsonObject& Object2 = value; + for (JsonObject::iterator it2 = Object2.begin(); it2 != Object2.end(); ++it2) { + JsonVariant value2 = it2->value; + if (value2.is()) { + JsonObject& Object3 = value2; + for (JsonObject::iterator it3 = Object3.begin(); it3 != Object3.end(); ++it3) { + const char* value = it3->value; + if (value != nullptr) { + DisplayJsonValue(topic, it->key, it3->key, value); + } + } + } else { + const char* value = it2->value; + if (value != nullptr) { + DisplayJsonValue(topic, it->key, it2->key, value); + } + } + } + } else { + const char* value = it->value; + if (value != nullptr) { + DisplayJsonValue(topic, it->key, it->key, value); + } + } + } + } +} + +void DisplayMqttSubscribe(void) +{ + + + + + + + if (Settings.display_model && (Settings.display_mode &0x04)) { + + char stopic[TOPSZ]; + char ntopic[TOPSZ]; + + ntopic[0] = '\0'; + strlcpy(stopic, SettingsText(SET_MQTT_FULLTOPIC), sizeof(stopic)); + char *tp = strtok(stopic, "/"); + while (tp != nullptr) { + if (!strcmp_P(tp, MQTT_TOKEN_PREFIX)) { + break; + } + strncat_P(ntopic, PSTR("+/"), sizeof(ntopic) - strlen(ntopic) -1); + tp = strtok(nullptr, "/"); + } + strncat(ntopic, SettingsText(SET_MQTTPREFIX3), sizeof(ntopic) - strlen(ntopic) -1); + strncat_P(ntopic, PSTR("/#"), sizeof(ntopic) - strlen(ntopic) -1); + MqttSubscribe(ntopic); + disp_subscribed = true; + } else { + disp_subscribed = false; + } +} + +bool DisplayMqttData(void) +{ + if (disp_subscribed) { + char stopic[TOPSZ]; + + snprintf_P(stopic, sizeof(stopic) , PSTR("%s/"), SettingsText(SET_MQTTPREFIX3)); + char *tp = strstr(XdrvMailbox.topic, stopic); + if (tp) { + if (Settings.display_mode &0x04) { + tp = tp + strlen(stopic); + char *topic = strtok(tp, "/"); + DisplayAnalyzeJson(topic, XdrvMailbox.data); + } + return true; + } + } + return false; +} + +void DisplayLocalSensor(void) +{ + if ((Settings.display_mode &0x02) && (0 == tele_period)) { + char no_topic[1] = { 0 }; + + DisplayAnalyzeJson(no_topic, mqtt_data); + } +} + +#endif + + + + + +void DisplayInitDriver(void) +{ + XdspCall(FUNC_DISPLAY_INIT_DRIVER); + + if (renderer) { + renderer->setTextFont(Settings.display_font); + renderer->setTextSize(Settings.display_size); + } + + + + + if (Settings.display_model) { + devices_present++; + disp_device = devices_present; + +#ifndef USE_DISPLAY_MODES1TO5 + Settings.display_mode = 0; +#else + DisplayLogBufferInit(); +#endif + } +} + +void DisplaySetPower(void) +{ + disp_power = bitRead(XdrvMailbox.index, disp_device -1); + + + + if (Settings.display_model) { + if (!renderer) { + XdspCall(FUNC_DISPLAY_POWER); + } else { + renderer->DisplayOnff(disp_power); + } + } +} + + + + + +void CmndDisplay(void) +{ + Response_P(PSTR("{\"" D_PRFX_DISPLAY "\":{\"" D_CMND_DISP_MODEL "\":%d,\"" D_CMND_DISP_WIDTH "\":%d,\"" D_CMND_DISP_HEIGHT "\":%d,\"" + D_CMND_DISP_MODE "\":%d,\"" D_CMND_DISP_DIMMER "\":%d,\"" D_CMND_DISP_SIZE "\":%d,\"" D_CMND_DISP_FONT "\":%d,\"" + D_CMND_DISP_ROTATE "\":%d,\"" D_CMND_DISP_REFRESH "\":%d,\"" D_CMND_DISP_COLS "\":[%d,%d],\"" D_CMND_DISP_ROWS "\":%d}}"), + Settings.display_model, Settings.display_width, Settings.display_height, + Settings.display_mode, Settings.display_dimmer, Settings.display_size, Settings.display_font, + Settings.display_rotate, Settings.display_refresh, Settings.display_cols[0], Settings.display_cols[1], Settings.display_rows); +} + +void CmndDisplayModel(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < DISPLAY_MAX_DRIVERS)) { + uint32_t last_display_model = Settings.display_model; + Settings.display_model = XdrvMailbox.payload; + if (XdspCall(FUNC_DISPLAY_MODEL)) { + restart_flag = 2; + } else { + Settings.display_model = last_display_model; + } + } + ResponseCmndNumber(Settings.display_model); +} + +void CmndDisplayWidth(void) +{ + if (XdrvMailbox.payload > 0) { + if (XdrvMailbox.payload != Settings.display_width) { + Settings.display_width = XdrvMailbox.payload; + restart_flag = 2; + } + } + ResponseCmndNumber(Settings.display_width); +} + +void CmndDisplayHeight(void) +{ + if (XdrvMailbox.payload > 0) { + if (XdrvMailbox.payload != Settings.display_height) { + Settings.display_height = XdrvMailbox.payload; + restart_flag = 2; + } + } + ResponseCmndNumber(Settings.display_height); +} + +void CmndDisplayMode(void) +{ +#ifdef USE_DISPLAY_MODES1TO5 + + + + + + + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) { + uint32_t last_display_mode = Settings.display_mode; + Settings.display_mode = XdrvMailbox.payload; + + if (disp_subscribed != (Settings.display_mode &0x04)) { + restart_flag = 2; + } else { + if (last_display_mode && !Settings.display_mode) { + DisplayInit(DISPLAY_INIT_MODE); + if (renderer) renderer->fillScreen(bg_color); + else DisplayClear(); + } else { + DisplayLogBufferInit(); + DisplayInit(DISPLAY_INIT_MODE); + } + } + } +#endif + ResponseCmndNumber(Settings.display_mode); +} + +void CmndDisplayDimmer(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + Settings.display_dimmer = ((XdrvMailbox.payload +1) * 100) / 666; + if (Settings.display_dimmer && !(disp_power)) { + ExecuteCommandPower(disp_device, POWER_ON, SRC_DISPLAY); + } + else if (!Settings.display_dimmer && disp_power) { + ExecuteCommandPower(disp_device, POWER_OFF, SRC_DISPLAY); + } + if (renderer) renderer->dim(Settings.display_dimmer); + } + ResponseCmndNumber(Settings.display_dimmer); +} + +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)) { + Settings.display_font = XdrvMailbox.payload; + if (renderer) renderer->setTextFont(Settings.display_font); + else DisplaySetFont(Settings.display_font); + } + ResponseCmndNumber(Settings.display_font); +} + +void CmndDisplayRotate(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 4)) { + if (Settings.display_rotate != XdrvMailbox.payload) { +# 1428 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_13_display.ino" + Settings.display_rotate = XdrvMailbox.payload; + DisplayInit(DISPLAY_INIT_MODE); +#ifdef USE_DISPLAY_MODES1TO5 + DisplayLogBufferInit(); +#endif + } + } + ResponseCmndNumber(Settings.display_rotate); +} + +void CmndDisplayText(void) +{ + if (disp_device && XdrvMailbox.data_len > 0) { +#ifndef USE_DISPLAY_MODES1TO5 + DisplayText(); +#else + if (!Settings.display_mode) { + DisplayText(); + } else { + DisplayLogBufferAdd(XdrvMailbox.data); + } +#endif + ResponseCmndChar(XdrvMailbox.data); + } +} + +void CmndDisplayAddress(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 8)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 255)) { + Settings.display_address[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.display_address[XdrvMailbox.index -1]); + } +} + +void CmndDisplayRefresh(void) +{ + if ((XdrvMailbox.payload >= 1) && (XdrvMailbox.payload <= 7)) { + Settings.display_refresh = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.display_refresh); +} + +void CmndDisplayColumns(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_COLS)) { + Settings.display_cols[XdrvMailbox.index -1] = XdrvMailbox.payload; +#ifdef USE_DISPLAY_MODES1TO5 + if (1 == XdrvMailbox.index) { + DisplayLogBufferInit(); + DisplayReAllocScreenBuffer(); + } +#endif + } + ResponseCmndIdxNumber(Settings.display_cols[XdrvMailbox.index -1]); + } +} + +void CmndDisplayRows(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_ROWS)) { + Settings.display_rows = XdrvMailbox.payload; +#ifdef USE_DISPLAY_MODES1TO5 + DisplayLogBufferInit(); + DisplayReAllocScreenBuffer(); +#endif + } + ResponseCmndNumber(Settings.display_rows); +} + + + + + +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) +void Draw_RGB_Bitmap(char *file,uint16_t xp, uint16_t yp) { + if (!renderer) return; + + + File fp; + fp=SD.open(file,FILE_READ); + if (!fp) return; + uint16_t xsize; + fp.read((uint8_t*)&xsize,2); + uint16_t ysize; + fp.read((uint8_t*)&ysize,2); + +#if 1 +#define XBUFF 128 + uint16_t xdiv=xsize/XBUFF; + renderer->setAddrWindow(xp,yp,xp+xsize,yp+ysize); + for(int16_t j=0; j=2) renderer->pushColors(rgb,len/2,true); + } + OsWatchLoop(); + } + renderer->setAddrWindow(0,0,0,0); +#else + for(int16_t j=0; jwritePixel(xp+i,yp,rgb); + } + delay(0); + OsWatchLoop(); + yp++; + } +#endif + fp.close(); +} +#endif + +#ifdef USE_AWATCH +#define MINUTE_REDUCT 4 + +#ifndef pi +#define pi 3.14159265359 +#endif + + +void DrawAClock(uint16_t rad) { + if (!renderer) return; + float frad=rad; + uint16_t hred=frad/3.0; + renderer->fillCircle(disp_xpos, disp_ypos, rad, bg_color); + renderer->drawCircle(disp_xpos, disp_ypos, rad, fg_color); + renderer->fillCircle(disp_xpos, disp_ypos, 4, fg_color); + for (uint8_t count=0; count<60; count+=5) { + float p1=((float)count*(pi/30)-(pi/2)); + uint8_t len; + if ((count%15)==0) { + len=4; + } else { + len=2; + } + renderer->writeLine(disp_xpos+((float)(rad-len)*cosf(p1)), disp_ypos+((float)(rad-len)*sinf(p1)), disp_xpos+(frad*cosf(p1)), disp_ypos+(frad*sinf(p1)), fg_color); + } + + + float hour=((float)RtcTime.hour*60.0+(float)RtcTime.minute)/60.0; + float temp=(hour*(pi/6.0)-(pi/2.0)); + renderer->writeLine(disp_xpos, disp_ypos,disp_xpos+(frad-hred)*cosf(temp),disp_ypos+(frad-hred)*sinf(temp), fg_color); + + + temp=((float)RtcTime.minute*(pi/30.0)-(pi/2.0)); + renderer->writeLine(disp_xpos, disp_ypos,disp_xpos+(frad-MINUTE_REDUCT)*cosf(temp),disp_ypos+(frad-MINUTE_REDUCT)*sinf(temp), fg_color); +} +#endif + + +#ifdef USE_GRAPH + +typedef union { + uint8_t data; + struct { + uint8_t overlay : 1; + uint8_t draw : 1; + uint8_t nu3 : 1; + uint8_t nu4 : 1; + uint8_t nu5 : 1; + uint8_t nu6 : 1; + uint8_t nu7 : 1; + uint8_t nu8 : 1; + }; +} GFLAGS; + +struct GRAPH { + uint16_t xp; + uint16_t yp; + uint16_t xs; + uint16_t ys; + float ymin; + float ymax; + float range; + uint32_t x_time; + uint32_t last_ms; + uint32_t last_ms_redrawn; + int16_t decimation; + uint16_t dcnt; + uint32_t summ; + uint16_t xcnt; + uint8_t *values; + uint8_t xticks; + uint8_t yticks; + uint8_t last_val; + uint8_t color_index; + GFLAGS flags; +}; + + +struct GRAPH *graph[NUM_GRAPHS]; + +#define TICKLEN 4 +void ClrGraph(uint16_t num) { + struct GRAPH *gp=graph[num]; + + uint16_t xticks=gp->xticks; + uint16_t yticks=gp->yticks; + uint16_t count; + + + if (gp->flags.overlay) return; + + renderer->fillRect(gp->xp+1,gp->yp+1,gp->xs-2,gp->ys-2,bg_color); + + if (xticks) { + float cxp=gp->xp,xd=(float)gp->xs/(float)xticks; + for (count=0; countwriteFastVLine(cxp,gp->yp+gp->ys-TICKLEN,TICKLEN,fg_color); + cxp+=xd; + } + } + if (yticks) { + if (gp->ymin<0 && gp->ymax>0) { + + float cxp=0; + float czp=gp->yp+(gp->ymax/gp->range); + while (cxpxs) { + renderer->writeFastHLine(gp->xp+cxp,czp,2,fg_color); + cxp+=6.0; + } + + float cyp=0,yd=gp->ys/yticks; + for (count=0; countgp->yp) { + renderer->writeFastHLine(gp->xp,czp-cyp,TICKLEN,fg_color); + renderer->writeFastHLine(gp->xp+gp->xs-TICKLEN,czp-cyp,TICKLEN,fg_color); + } + if ((czp+cyp)<(gp->yp+gp->ys)) { + renderer->writeFastHLine(gp->xp,czp+cyp,TICKLEN,fg_color); + renderer->writeFastHLine(gp->xp+gp->xs-TICKLEN,czp+cyp,TICKLEN,fg_color); + } + cyp+=yd; + } + } else { + float cyp=gp->yp,yd=gp->ys/yticks; + for (count=0; countwriteFastHLine(gp->xp,cyp,TICKLEN,fg_color); + renderer->writeFastHLine(gp->xp+gp->xs-TICKLEN,cyp,TICKLEN,fg_color); + cyp+=yd; + } + } + } +} + + +void DefineGraph(uint16_t num,uint16_t xp,uint16_t yp,int16_t xs,uint16_t ys,int16_t dec,float ymin, float ymax,uint8_t icol) { + if (!renderer) return; + uint8_t rflg=0; + if (xs<0) { + rflg=1; + xs=abs(xs); + } + struct GRAPH *gp; + uint16_t count; + uint16_t index=num%NUM_GRAPHS; + if (!graph[index]) { + gp=(struct GRAPH*)calloc(sizeof(struct GRAPH),1); + if (!gp) return; + graph[index]=gp; + } else { + gp=graph[index]; + if (rflg) { + RedrawGraph(index,1); + return; + } + } + + + gp->xticks=(num>>4)&0x3f; + gp->yticks=(num>>10)&0x3f; + gp->xp=xp; + gp->yp=yp; + gp->xs=xs; + gp->ys=ys; + if (!dec) dec=1; + gp->decimation=dec; + if (dec>0) { + + gp->x_time=((float)dec*60000.0)/(float)xs; + gp->last_ms=millis()+gp->x_time; + } + gp->ymin=ymin; + gp->ymax=ymax; + gp->range=(ymax-ymin)/ys; + gp->xcnt=0; + gp->dcnt=0; + gp->summ=0; + if (gp->values) free(gp->values); + gp->values=(uint8_t*) calloc(1,xs+2); + if (!gp->values) { + free(gp); + graph[index]=0; + return; + } + + gp->values[0]=0; + + gp->last_ms_redrawn=millis(); + + if (!icol) icol=1; + gp->color_index=icol; + gp->flags.overlay=0; + gp->flags.draw=1; + + + if (index>0) { + for (uint8_t count=0; countxp==gp1->xp) && (gp->yp==gp1->yp)) { + gp->flags.overlay=1; + break; + } + } + } + } + + + renderer->drawRect(xp,yp,xs,ys,fg_color); + + ClrGraph(index); + +} + + +void DisplayCheckGraph() { + int16_t count; + struct GRAPH *gp; + for (count=0;countdecimation>0) { + + while (millis()>gp->last_ms) { + gp->last_ms+=gp->x_time; + uint8_t val; + if (gp->dcnt) { + val=gp->summ/gp->dcnt; + gp->dcnt=0; + gp->summ=0; + gp->last_val=val; + } else { + val=gp->last_val; + } + AddGraph(count,val); + } + } + } + } +} + + +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) +#include + +void Save_graph(uint8_t num, char *path) { + if (!renderer) return; + uint16_t index=num%NUM_GRAPHS; + struct GRAPH *gp=graph[index]; + if (!gp) return; + File fp; + SD.remove(path); + fp=SD.open(path,FILE_WRITE); + if (!fp) return; + char str[32]; + sprintf_P(str,PSTR("%d\t%d\t%d\t"),gp->xcnt,gp->xs,gp->ys); + fp.print(str); + dtostrfd(gp->ymin,2,str); + fp.print(str); + fp.print("\t"); + dtostrfd(gp->ymax,2,str); + fp.print(str); + fp.print("\t"); + for (uint32_t count=0;countxs;count++) { + dtostrfd(gp->values[count],0,str); + fp.print(str); + fp.print("\t"); + } + fp.print("\n"); + fp.close(); +} +void Restore_graph(uint8_t num, char *path) { + if (!renderer) return; + uint16_t index=num%NUM_GRAPHS; + struct GRAPH *gp=graph[index]; + if (!gp) return; + File fp; + fp=SD.open(path,FILE_READ); + if (!fp) return; + char vbuff[32]; + char *cp=vbuff; + uint8_t buf[2]; + uint8_t findex=0; + + for (uint32_t count=0;count<=gp->xs+4;count++) { + cp=vbuff; + findex=0; + while (fp.available()) { + fp.read(buf,1); + if (buf[0]=='\t' || buf[0]==',' || buf[0]=='\n' || buf[0]=='\r') { + break; + } else { + *cp++=buf[0]; + findex++; + if (findex>=sizeof(vbuff)-1) break; + } + } + *cp=0; + if (count<=4) { + if (count==0) gp->xcnt=atoi(vbuff); + } else { + gp->values[count-5]=atoi(vbuff); + } + } + fp.close(); + RedrawGraph(num,1); +} +#endif + +void RedrawGraph(uint8_t num, uint8_t flags) { + uint16_t index=num%NUM_GRAPHS; + struct GRAPH *gp=graph[index]; + if (!gp) return; + if (!flags) { + gp->flags.draw=0; + return; + } + if (!renderer) return; + + gp->flags.draw=1; + uint16_t linecol=fg_color; + + if (color_type==COLOR_COLOR) { + linecol=renderer->GetColorFromIndex(gp->color_index); + } + + if (!gp->flags.overlay) { + + renderer->drawRect(gp->xp,gp->yp,gp->xs,gp->ys,fg_color); + + ClrGraph(index); + } + + for (uint16_t count=0;countxs-1;count++) { + renderer->writeLine(gp->xp+count,gp->yp+gp->ys-gp->values[count]-1,gp->xp+count+1,gp->yp+gp->ys-gp->values[count+1]-1,linecol); + } +} + + +void AddGraph(uint8_t num,uint8_t val) { + struct GRAPH *gp=graph[num]; + if (!renderer) return; + + uint16_t linecol=fg_color; + if (color_type==COLOR_COLOR) { + linecol=renderer->GetColorFromIndex(gp->color_index); + } + gp->xcnt++; + if (gp->xcnt>gp->xs) { + gp->xcnt=gp->xs; + int16_t count; + + for (count=0;countxs-1;count++) { + gp->values[count]=gp->values[count+1]; + } + gp->values[gp->xcnt-1]=val; + + if (!gp->flags.draw) return; + + + if (millis()-gp->last_ms_redrawn>1000) { + gp->last_ms_redrawn=millis(); + + if (!gp->flags.overlay) { + + renderer->drawRect(gp->xp,gp->yp,gp->xs,gp->ys,fg_color); + + ClrGraph(num); + } + + for (count=0;countxs-1;count++) { + renderer->writeLine(gp->xp+count,gp->yp+gp->ys-gp->values[count]-1,gp->xp+count+1,gp->yp+gp->ys-gp->values[count+1]-1,linecol); + } + } + } else { + + gp->values[gp->xcnt]=val; + if (!gp->flags.draw) return; + renderer->writeLine(gp->xp+gp->xcnt-1,gp->yp+gp->ys-gp->values[gp->xcnt-1]-1,gp->xp+gp->xcnt,gp->yp+gp->ys-gp->values[gp->xcnt]-1,linecol); + } +} + + + +void AddValue(uint8_t num,float fval) { + + num=num%NUM_GRAPHS; + struct GRAPH *gp=graph[num]; + if (!gp) return; + + if (fval>gp->ymax) fval=gp->ymax; + if (fvalymin) fval=gp->ymin; + + int16_t val; + val=(fval-gp->ymin)/gp->range; + + if (val>gp->ys-1) val=gp->ys-1; + if (val<0) val=0; + + + gp->summ+=val; + gp->dcnt++; + + + if (gp->decimation<0) { + if (gp->dcnt>=-gp->decimation) { + gp->dcnt=0; + + val=gp->summ/-gp->decimation; + gp->summ=0; + + AddGraph(num,val); + } + } +} +#endif + + + + + +bool Xdrv13(uint8_t function) +{ + bool result = false; + + if ((i2c_flg || spi_flg || soft_spi_flg) && XdspPresent()) { + switch (function) { + case FUNC_PRE_INIT: + DisplayInitDriver(); +#ifdef USE_GRAPH + for (uint8_t count=0;count + +TasmotaSerial *MP3Player; + + + + + +#define D_CMND_MP3 "MP3" + +const char S_JSON_MP3_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_MP3 "%s\":%d}"; +const char S_JSON_MP3_COMMAND[] PROGMEM = "{\"" D_CMND_MP3 "%s\"}"; +const char kMP3_Commands[] PROGMEM = "Track|Play|Pause|Stop|Volume|EQ|Device|Reset|DAC"; + + + + + +enum MP3_Commands { + CMND_MP3_TRACK, + CMND_MP3_PLAY, + CMND_MP3_PAUSE, + CMND_MP3_STOP, + CMND_MP3_VOLUME, + CMND_MP3_EQ, + CMND_MP3_DEVICE, + CMND_MP3_RESET, + CMND_MP3_DAC }; + + + + + + +#define MP3_CMD_RESET_VALUE 0 + +#define MP3_CMD_TRACK 0x03 +#define MP3_CMD_PLAY 0x0d +#define MP3_CMD_PAUSE 0x0e +#define MP3_CMD_STOP 0x16 +#define MP3_CMD_VOLUME 0x06 +#define MP3_CMD_EQ 0x07 +#define MP3_CMD_DEVICE 0x09 +#define MP3_CMD_RESET 0x0C +#define MP3_CMD_DAC 0x1A + + + + + + +uint16_t MP3_Checksum(uint8_t *array) +{ + uint16_t checksum = 0; + for (uint32_t i = 0; i < 6; i++) { + checksum += array[i]; + } + checksum = checksum^0xffff; + return (checksum+1); +} + + + + + + +void MP3PlayerInit(void) { + MP3Player = new TasmotaSerial(-1, pin[GPIO_MP3_DFR562]); + + if (MP3Player->begin(9600)) { + MP3Player->flush(); + delay(1000); + MP3_CMD(MP3_CMD_RESET, MP3_CMD_RESET_VALUE); + delay(3000); + MP3_CMD(MP3_CMD_VOLUME, MP3_VOLUME); + } + return; +} +# 159 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_14_mp3.ino" +void MP3_CMD(uint8_t mp3cmd,uint16_t val) { + uint8_t i = 0; + uint8_t cmd[10] = {0x7e,0xff,6,0,0,0,0,0,0,0xef}; + cmd[3] = mp3cmd; + cmd[4] = 0; + cmd[5] = val>>8; + cmd[6] = val; + uint16_t chks = MP3_Checksum(&cmd[1]); + cmd[7] = chks>>8; + cmd[8] = chks; + MP3Player->write(cmd, sizeof(cmd)); + delay(1000); + if (mp3cmd == MP3_CMD_RESET) { + MP3_CMD(MP3_CMD_VOLUME, MP3_VOLUME); + } + return; +} + + + + + +bool MP3PlayerCmd(void) { + char command[CMDSZ]; + bool serviced = true; + uint8_t disp_len = strlen(D_CMND_MP3); + + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_MP3), disp_len)) { + int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kMP3_Commands); + + switch (command_code) { + case CMND_MP3_TRACK: + case CMND_MP3_VOLUME: + case CMND_MP3_EQ: + case CMND_MP3_DEVICE: + case CMND_MP3_DAC: + + if (XdrvMailbox.data_len > 0) { + if (command_code == CMND_MP3_TRACK) { MP3_CMD(MP3_CMD_TRACK, XdrvMailbox.payload); } + if (command_code == CMND_MP3_VOLUME) { MP3_CMD(MP3_CMD_VOLUME, XdrvMailbox.payload * 30 / 100); } + if (command_code == CMND_MP3_EQ) { MP3_CMD(MP3_CMD_EQ, XdrvMailbox.payload); } + if (command_code == CMND_MP3_DEVICE) { MP3_CMD(MP3_CMD_DEVICE, XdrvMailbox.payload); } + if (command_code == CMND_MP3_DAC) { MP3_CMD(MP3_CMD_DAC, XdrvMailbox.payload); } + } + Response_P(S_JSON_MP3_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_MP3_PLAY: + case CMND_MP3_PAUSE: + case CMND_MP3_STOP: + case CMND_MP3_RESET: + + if (command_code == CMND_MP3_PLAY) { MP3_CMD(MP3_CMD_PLAY, 0); } + if (command_code == CMND_MP3_PAUSE) { MP3_CMD(MP3_CMD_PAUSE, 0); } + if (command_code == CMND_MP3_STOP) { MP3_CMD(MP3_CMD_STOP, 0); } + if (command_code == CMND_MP3_RESET) { MP3_CMD(MP3_CMD_RESET, 0); } + Response_P(S_JSON_MP3_COMMAND, command, XdrvMailbox.payload); + break; + default: + + serviced = false; + break; + } + } else { + return false; + } + return serviced; +} + + + + + +bool Xdrv14(uint8_t function) +{ + bool result = false; + + if (pin[GPIO_MP3_DFR562] < 99) { + switch (function) { + case FUNC_PRE_INIT: + MP3PlayerInit(); + break; + case FUNC_COMMAND: + result = MP3PlayerCmd(); + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_15_pca9685.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_15_pca9685.ino" +#ifdef USE_I2C +#ifdef USE_PCA9685 + + + + + + +#define XDRV_15 15 +#define XI2C_01 1 + +#define PCA9685_REG_MODE1 0x00 +#define PCA9685_REG_LED0_ON_L 0x06 +#define PCA9685_REG_PRE_SCALE 0xFE + +#ifndef USE_PCA9685_ADDR + #define USE_PCA9685_ADDR 0x40 +#endif +#ifndef USE_PCA9685_FREQ + #define USE_PCA9685_FREQ 50 +#endif + +bool pca9685_detected = false; +uint16_t pca9685_freq = USE_PCA9685_FREQ; +uint16_t pca9685_pin_pwm_value[16]; + +void PCA9685_Detect(void) +{ + if (I2cActive(USE_PCA9685_ADDR)) { return; } + + uint8_t buffer; + if (I2cValidRead8(&buffer, USE_PCA9685_ADDR, PCA9685_REG_MODE1)) { + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, 0x20); + if (I2cValidRead8(&buffer, USE_PCA9685_ADDR, PCA9685_REG_MODE1)) { + if (0x20 == buffer) { + pca9685_detected = true; + I2cSetActiveFound(USE_PCA9685_ADDR, "PCA9685"); + PCA9685_Reset(); + } + } + } +} + +void PCA9685_Reset(void) +{ + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, 0x80); + PCA9685_SetPWMfreq(USE_PCA9685_FREQ); + for (uint32_t pin=0;pin<16;pin++) { + PCA9685_SetPWM(pin,0,false); + pca9685_pin_pwm_value[pin] = 0; + } + Response_P(PSTR("{\"PCA9685\":{\"RESET\":\"OK\"}}")); +} + +void PCA9685_SetPWMfreq(double freq) { + + + + + if (freq > 23 && freq < 1527) { + pca9685_freq=freq; + } else { + pca9685_freq=50; + } + uint8_t pre_scale_osc = round(25000000/(4096*pca9685_freq))-1; + if (1526 == pca9685_freq) pre_scale_osc=0xFF; + uint8_t current_mode1 = I2cRead8(USE_PCA9685_ADDR, PCA9685_REG_MODE1); + uint8_t sleep_mode1 = (current_mode1&0x7F) | 0x10; + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, sleep_mode1); + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_PRE_SCALE, pre_scale_osc); + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, current_mode1 | 0xA0); +} + +void PCA9685_SetPWM_Reg(uint8_t pin, uint16_t on, uint16_t off) { + uint8_t led_reg = PCA9685_REG_LED0_ON_L + 4 * pin; + uint32_t led_data = 0; + I2cWrite8(USE_PCA9685_ADDR, led_reg, on); + I2cWrite8(USE_PCA9685_ADDR, led_reg+1, (on >> 8)); + I2cWrite8(USE_PCA9685_ADDR, led_reg+2, off); + I2cWrite8(USE_PCA9685_ADDR, led_reg+3, (off >> 8)); +} + +void PCA9685_SetPWM(uint8_t pin, uint16_t pwm, bool inverted) { + if (4096 == pwm) { + PCA9685_SetPWM_Reg(pin, 4096, 0); + } else { + PCA9685_SetPWM_Reg(pin, 0, pwm); + } + pca9685_pin_pwm_value[pin] = pwm; +} + +bool PCA9685_Command(void) +{ + bool serviced = true; + bool validpin = false; + uint8_t paramcount = 0; + if (XdrvMailbox.data_len > 0) { + paramcount=1; + } else { + serviced = false; + return serviced; + } + char sub_string[XdrvMailbox.data_len]; + for (uint32_t ca=0;ca 1) { + uint16_t new_freq = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if ((new_freq >= 24) && (new_freq <= 1526)) { + PCA9685_SetPWMfreq(new_freq); + Response_P(PSTR("{\"PCA9685\":{\"PWMF\":%i, \"Result\":\"OK\"}}"),new_freq); + return serviced; + } + } else { + Response_P(PSTR("{\"PCA9685\":{\"PWMF\":%i}}"),pca9685_freq); + return serviced; + } + } + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"PWM")) { + if (paramcount > 1) { + uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if (paramcount > 2) { + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 3), "ON")) { + PCA9685_SetPWM(pin, 4096, false); + Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,4096); + serviced = true; + return serviced; + } + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 3), "OFF")) { + PCA9685_SetPWM(pin, 0, false); + Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,0); + serviced = true; + return serviced; + } + uint16_t pwm = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + if ((pin >= 0 && pin <= 15) && (pwm >= 0 && pwm <= 4096)) { + PCA9685_SetPWM(pin, pwm, false); + Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,pwm); + serviced = true; + return serviced; + } + } + } + } + return serviced; +} + +void PCA9685_OutputTelemetry(bool telemetry) +{ + 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]); + } + ResponseAppend_P(PSTR("\"END\":1}}")); + if (telemetry) { + MqttPublishTeleSensor(); + } +} + +bool Xdrv15(uint8_t function) +{ + if (!I2cEnabled(XI2C_01)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + PCA9685_Detect(); + } + else if (pca9685_detected) { + switch (function) { + case FUNC_EVERY_SECOND: + if (tele_period == 0) { + PCA9685_OutputTelemetry(true); + } + break; + case FUNC_COMMAND_DRIVER: + if (XDRV_15 == XdrvMailbox.index) { + result = PCA9685_Command(); + } + break; + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_16_tuyamcu.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_16_tuyamcu.ino" +#ifdef USE_LIGHT +#ifdef USE_TUYA_MCU + +#define XDRV_16 16 +#define XNRG_16 16 + +#ifndef TUYA_DIMMER_ID +#define TUYA_DIMMER_ID 0 +#endif + +#define TUYA_CMD_HEARTBEAT 0x00 +#define TUYA_CMD_QUERY_PRODUCT 0x01 +#define TUYA_CMD_MCU_CONF 0x02 +#define TUYA_CMD_WIFI_STATE 0x03 +#define TUYA_CMD_WIFI_RESET 0x04 +#define TUYA_CMD_WIFI_SELECT 0x05 +#define TUYA_CMD_SET_DP 0x06 +#define TUYA_CMD_STATE 0x07 +#define TUYA_CMD_QUERY_STATE 0x08 + +#define TUYA_LOW_POWER_CMD_WIFI_STATE 0x02 +#define TUYA_LOW_POWER_CMD_WIFI_RESET 0x03 +#define TUYA_LOW_POWER_CMD_WIFI_CONFIG 0x04 +#define TUYA_LOW_POWER_CMD_STATE 0x05 + +#define TUYA_TYPE_BOOL 0x01 +#define TUYA_TYPE_VALUE 0x02 +#define TUYA_TYPE_STRING 0x03 +#define TUYA_TYPE_ENUM 0x04 + +#define TUYA_BUFFER_SIZE 256 + +#include + +TasmotaSerial *TuyaSerial = nullptr; + +struct TUYA { + uint16_t new_dim = 0; + bool ignore_dim = false; + uint8_t cmd_status = 0; + uint8_t cmd_checksum = 0; + uint8_t data_len = 0; + uint8_t wifi_state = -2; + uint8_t heartbeat_timer = 0; +#ifdef USE_ENERGY_SENSOR + uint32_t lastPowerCheckTime = 0; +#endif + char *buffer = nullptr; + int byte_counter = 0; + bool low_power_mode = false; + bool send_success_next_second = false; + uint32_t ignore_dimmer_cmd_timeout = 0; +} Tuya; + + +enum TuyaSupportedFunctions { + TUYA_MCU_FUNC_NONE, + TUYA_MCU_FUNC_SWT1 = 1, + TUYA_MCU_FUNC_SWT2, + TUYA_MCU_FUNC_SWT3, + TUYA_MCU_FUNC_SWT4, + TUYA_MCU_FUNC_REL1 = 11, + 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_BATTERY_STATE, + TUYA_MCU_FUNC_BATTERY_PERCENTAGE, + TUYA_MCU_FUNC_REL1_INV = 41, + 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_LOWPOWER_MODE = 51, + TUYA_MCU_FUNC_LAST = 255 +}; + +const char kTuyaCommand[] PROGMEM = "|" + D_CMND_TUYA_MCU "|" D_CMND_TUYA_MCU_SEND_STATE; + +void (* const TuyaCommand[])(void) PROGMEM = { + &CmndTuyaMcu, &CmndTuyaSend +}; +# 128 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_16_tuyamcu.ino" +void CmndTuyaSend(void) { + if (XdrvMailbox.index > 4) { + return; + } + if (XdrvMailbox.index == 0) { + TuyaRequestState(); + } else { + if (XdrvMailbox.data_len > 0) { + char *p; + char *data; + uint8_t i = 0; + uint8_t dpId = 0; + for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 2; str = strtok_r(nullptr, ", ", &p)) { + if ( i == 0) { + dpId = strtoul(str, nullptr, 0); + } else { + data = str; + } + i++; + } + + if (1 == XdrvMailbox.index) { + TuyaSendBool(dpId, strtoul(data, nullptr, 0)); + } else if (2 == XdrvMailbox.index) { + TuyaSendValue(dpId, strtoull(data, nullptr, 0)); + } else if (3 == XdrvMailbox.index) { + TuyaSendString(dpId, data); + } else if (4 == XdrvMailbox.index) { + TuyaSendEnum(dpId, strtoul(data, nullptr, 0)); + } + } + } + ResponseCmndDone(); +} + + + + + + + +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("{\"" D_CMND_TUYA_MCU "\":[")); + 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("]}")); +} + + + + + +void TuyaAddMcuFunc(uint8_t fnId, uint8_t dpId) { + bool added = false; + + if (fnId == 0 || dpId == 0) { + 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 { + 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) { + 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) { + 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) { + bitClear(rel_inverted, fnId - TUYA_MCU_FUNC_REL1); + } else if (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) { + 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) || + (fnId == TUYA_MCU_FUNC_LOWPOWER_MODE); +} + +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)); + TuyaSerial->write(0x55); + TuyaSerial->write(0xAA); + TuyaSerial->write((uint8_t)0x00); + TuyaSerial->write(cmd); + TuyaSerial->write(payload_len >> 8); + TuyaSerial->write(payload_len & 0xFF); + snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Send \"55aa00%02x%02x%02x"), cmd, payload_len >> 8, payload_len & 0xFF); + for (uint32_t i = 0; i < payload_len; ++i) { + TuyaSerial->write(payload[i]); + checksum += payload[i]; + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, payload[i]); + } + TuyaSerial->write(checksum); + TuyaSerial->flush(); + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x\""), log_data, checksum); + AddLog(LOG_LEVEL_DEBUG); +} + +void TuyaSendState(uint8_t id, uint8_t type, uint8_t* value) +{ + uint16_t payload_len = 4; + uint8_t payload_buffer[8]; + payload_buffer[0] = id; + payload_buffer[1] = type; + switch (type) { + case TUYA_TYPE_BOOL: + case TUYA_TYPE_ENUM: + payload_len += 1; + payload_buffer[2] = 0x00; + payload_buffer[3] = 0x01; + payload_buffer[4] = value[0]; + break; + case TUYA_TYPE_VALUE: + payload_len += 4; + payload_buffer[2] = 0x00; + payload_buffer[3] = 0x04; + payload_buffer[4] = value[3]; + payload_buffer[5] = value[2]; + payload_buffer[6] = value[1]; + payload_buffer[7] = value[0]; + break; + + } + + TuyaSendCmd(TUYA_CMD_SET_DP, payload_buffer, payload_len); +} + +void TuyaSendBool(uint8_t id, bool value) +{ + TuyaSendState(id, TUYA_TYPE_BOOL, (uint8_t*)&value); +} + +void TuyaSendValue(uint8_t id, uint32_t value) +{ + TuyaSendState(id, TUYA_TYPE_VALUE, (uint8_t*)(&value)); +} + +void TuyaSendEnum(uint8_t id, uint32_t value) +{ + TuyaSendState(id, TUYA_TYPE_ENUM, (uint8_t*)(&value)); +} + +void TuyaSendString(uint8_t id, char data[]) { + + uint16_t len = strlen(data); + uint16_t payload_len = 4 + len; + uint8_t payload_buffer[payload_len]; + payload_buffer[0] = id; + payload_buffer[1] = TUYA_TYPE_STRING; + payload_buffer[2] = len >> 8; + payload_buffer[3] = len & 0xFF; + + for (uint16_t i = 0; i < len; i++) { + payload_buffer[4+i] = data[i]; + } + + TuyaSendCmd(TUYA_CMD_SET_DP, payload_buffer, payload_len); +} + +bool TuyaSetPower(void) +{ + bool status = false; + + uint8_t rpower = XdrvMailbox.index; + int16_t source = XdrvMailbox.payload; + + uint8_t dpid = TuyaGetDpId(TUYA_MCU_FUNC_REL1 + active_device - 1); + if (dpid == 0) dpid = TuyaGetDpId(TUYA_MCU_FUNC_REL1_INV + active_device - 1); + + if (source != SRC_SWITCH && TuyaSerial) { + TuyaSendBool(dpid, bitRead(rpower, active_device-1) ^ bitRead(rel_inverted, active_device-1)); + delay(20); + status = true; + } + return status; +} + +bool TuyaSetChannels(void) +{ + LightSerialDuty(((uint8_t*)XdrvMailbox.data)[0]); + + return true; +} + +void LightSerialDuty(uint16_t duty) +{ + uint8_t dpid = TuyaGetDpId(TUYA_MCU_FUNC_DIMMER); + if (duty > 0 && !Tuya.ignore_dim && TuyaSerial && dpid > 0) { + duty = changeUIntScale(duty, 0, 255, 0, Settings.dimmer_hw_max); + if (duty < Settings.dimmer_hw_min) { duty = Settings.dimmer_hw_min; } + if (Tuya.new_dim != duty) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim value=%d (id=%d)"), duty, dpid); + Tuya.ignore_dimmer_cmd_timeout = millis() + 250; + TuyaSendValue(dpid, duty); + } + } else if (dpid > 0) { + Tuya.ignore_dim = false; + duty = changeUIntScale(duty, 0, 255, 0, Settings.dimmer_hw_max); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim skipped value=%d"), duty); + } else { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Cannot set dimmer. Dimmer Id unknown")); + } +} + +void TuyaRequestState(void) +{ + if (TuyaSerial) { + + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Read MCU state")); + + TuyaSendCmd(TUYA_CMD_QUERY_STATE); + } +} + +void TuyaResetWifi(void) +{ + if (!Settings.flag.button_restrict) { + char scmnd[20]; + snprintf_P(scmnd, sizeof(scmnd), D_CMND_WIFICONFIG " %d", 2); + ExecuteCommand(scmnd, SRC_BUTTON); + } +} + +void TuyaProcessStatePacket(void) { + char scmnd[20]; + uint8_t dpidStart = 6; + uint8_t fnId; + uint16_t dpDataLen; + + while (dpidStart + 4 < Tuya.byte_counter) { + dpDataLen = Tuya.buffer[dpidStart + 2] << 8 | Tuya.buffer[dpidStart + 3]; + fnId = TuyaGetFuncId(Tuya.buffer[dpidStart]); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: fnId=%d is set for dpId=%d"), fnId, Tuya.buffer[dpidStart]); + + if (Tuya.buffer[dpidStart + 1] == 1) { + + 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[dpidStart + 4]?"On":"Off",bitRead(power, fnId - TUYA_MCU_FUNC_REL1)?"On":"Off"); + if ((power || Settings.light_dimmer > 0) && (Tuya.buffer[dpidStart + 4] != bitRead(power, fnId - TUYA_MCU_FUNC_REL1))) { + ExecuteCommandPower(fnId - TUYA_MCU_FUNC_REL1 + 1, Tuya.buffer[dpidStart + 4], SRC_SWITCH); + } + } 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[dpidStart + 4]?"Off":"On",bitRead(power, fnId - TUYA_MCU_FUNC_REL1_INV) ^ 1?"Off":"On"); + if (Tuya.buffer[dpidStart + 4] != bitRead(power, fnId - TUYA_MCU_FUNC_REL1_INV) ^ 1) { + ExecuteCommandPower(fnId - TUYA_MCU_FUNC_REL1_INV + 1, Tuya.buffer[dpidStart + 4] ^ 1, SRC_SWITCH); + } + } 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[dpidStart + 4], SwitchGetVirtual(fnId - TUYA_MCU_FUNC_SWT1)); + + if (SwitchGetVirtual(fnId - TUYA_MCU_FUNC_SWT1) != Tuya.buffer[dpidStart + 4]) { + SwitchSetVirtual(fnId - TUYA_MCU_FUNC_SWT1, Tuya.buffer[dpidStart + 4]); + SwitchHandler(1); + } + } + + } + else if (Tuya.buffer[dpidStart + 1] == 2) { + bool tuya_energy_enabled = (XNRG_16 == energy_flg); + uint16_t packetValue = Tuya.buffer[dpidStart + 6] << 8 | Tuya.buffer[dpidStart + 7]; + if (fnId == TUYA_MCU_FUNC_DIMMER) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Dim State=%d"), packetValue); + Tuya.new_dim = changeUIntScale(packetValue, 0, Settings.dimmer_hw_max, 0, 100); + if (Tuya.ignore_dimmer_cmd_timeout < millis()) { + if ((power || Settings.flag3.tuya_apply_o20) && + (Tuya.new_dim > 0) && (abs(Tuya.new_dim - Settings.light_dimmer) > 1)) { + Tuya.ignore_dim = true; + + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER "3 %d"), Tuya.new_dim ); + ExecuteCommand(scmnd, SRC_SWITCH); + } + } + } + + #ifdef USE_ENERGY_SENSOR + else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_VOLTAGE) { + Energy.voltage[0] = (float)packetValue / 10; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Voltage=%d"), Tuya.buffer[dpidStart], packetValue); + } else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_CURRENT) { + Energy.current[0] = (float)packetValue / 1000; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Current=%d"), Tuya.buffer[dpidStart], packetValue); + } else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_POWER) { + Energy.active_power[0] = (float)packetValue / 10; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Active_Power=%d"), Tuya.buffer[dpidStart], packetValue); + + if (Tuya.lastPowerCheckTime != 0 && Energy.active_power[0] > 0) { + Energy.kWhtoday += (float)Energy.active_power[0] * (Rtc.utc_time - Tuya.lastPowerCheckTime) / 36; + EnergyUpdateToday(); + } + Tuya.lastPowerCheckTime = Rtc.utc_time; + } + #endif + + } + + + dpidStart += dpDataLen + 4; + } +} + +void TuyaLowPowerModePacketProcess(void) { + switch (Tuya.buffer[3]) { + case TUYA_CMD_QUERY_PRODUCT: + TuyaHandleProductInfoPacket(); + TuyaSetWifiLed(); + break; + + case TUYA_LOW_POWER_CMD_STATE: + TuyaProcessStatePacket(); + Tuya.send_success_next_second = true; + break; + } + +} + +void TuyaHandleProductInfoPacket(void) { + uint16_t dataLength = Tuya.buffer[4] << 8 | Tuya.buffer[5]; + char *data = &Tuya.buffer[6]; + AddLog_P2(LOG_LEVEL_INFO, PSTR("TYA: MCU Product ID: %.*s"), dataLength, data); +} + +void TuyaSendLowPowerSuccessIfNeeded(void) { + uint8_t success = 1; + + if (Tuya.send_success_next_second) { + TuyaSendCmd(TUYA_LOW_POWER_CMD_STATE, &success, 1); + Tuya.send_success_next_second = false; + } +} + +void TuyaNormalPowerModePacketProcess(void) +{ + switch (Tuya.buffer[3]) { + case TUYA_CMD_QUERY_PRODUCT: + TuyaHandleProductInfoPacket(); + TuyaSendCmd(TUYA_CMD_MCU_CONF); + break; + + case TUYA_CMD_HEARTBEAT: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Heartbeat")); + if (Tuya.buffer[6] == 0) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Detected MCU restart")); + Tuya.wifi_state = -2; + } + break; + + case TUYA_CMD_STATE: + TuyaProcessStatePacket(); + break; + + case TUYA_CMD_WIFI_RESET: + case TUYA_CMD_WIFI_SELECT: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX WiFi Reset")); + TuyaResetWifi(); + break; + + case TUYA_CMD_WIFI_STATE: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX WiFi LED set ACK")); + Tuya.wifi_state = TuyaGetTuyaWifiState(); + break; + + case TUYA_CMD_MCU_CONF: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX MCU configuration Mode=%d"), Tuya.buffer[5]); + + if (Tuya.buffer[5] == 2) { + uint8_t led1_gpio = Tuya.buffer[6]; + uint8_t key1_gpio = Tuya.buffer[7]; + bool key1_set = false; + bool led1_set = false; + for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { + if (Settings.my_gp.io[i] == GPIO_LED1) led1_set = true; + else if (Settings.my_gp.io[i] == GPIO_KEY1) key1_set = true; + } + if (!Settings.my_gp.io[led1_gpio] && !led1_set) { + Settings.my_gp.io[led1_gpio] = GPIO_LED1; + restart_flag = 2; + } + if (!Settings.my_gp.io[key1_gpio] && !key1_set) { + Settings.my_gp.io[key1_gpio] = GPIO_KEY1; + restart_flag = 2; + } + } + TuyaRequestState(); + break; + + default: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX unknown command")); + } +} + + + + + +bool TuyaModuleSelected(void) +{ + if (!(pin[GPIO_TUYA_RX] < 99) || !(pin[GPIO_TUYA_TX] < 99)) { + pin[GPIO_TUYA_TX] = 1; + pin[GPIO_TUYA_RX] = 3; + Settings.my_gp.io[1] = GPIO_TUYA_TX; + Settings.my_gp.io[3] = GPIO_TUYA_RX; + restart_flag = 2; + } + + if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER) == 0 && TUYA_DIMMER_ID > 0) { + TuyaAddMcuFunc(TUYA_MCU_FUNC_DIMMER, TUYA_DIMMER_ID); + } + + bool relaySet = false; + + 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; + } + + if (TuyaGetDpId(TUYA_MCU_FUNC_LOWPOWER_MODE) != 0) { + Tuya.low_power_mode = true; + Settings.flag3.fast_power_cycle_disable = true; + } + + UpdateDevices(); + return true; +} + +void TuyaInit(void) +{ + Tuya.buffer = (char*)(malloc(TUYA_BUFFER_SIZE)); + if (Tuya.buffer != nullptr) { + TuyaSerial = new TasmotaSerial(pin[GPIO_TUYA_RX], pin[GPIO_TUYA_TX], 2); + if (TuyaSerial->begin(9600)) { + if (TuyaSerial->hardwareSerial()) { ClaimSerial(); } + + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Request MCU configuration")); + + TuyaSendCmd(TUYA_CMD_QUERY_PRODUCT); + } + } + Tuya.heartbeat_timer = 0; +} + +void TuyaSerialInput(void) +{ + while (TuyaSerial->available()) { + yield(); + uint8_t serial_in_byte = TuyaSerial->read(); + + if (serial_in_byte == 0x55) { + Tuya.cmd_status = 1; + Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; + Tuya.cmd_checksum += serial_in_byte; + } + else if (Tuya.cmd_status == 1 && serial_in_byte == 0xAA) { + Tuya.cmd_status = 2; + + Tuya.byte_counter = 0; + Tuya.buffer[Tuya.byte_counter++] = 0x55; + Tuya.buffer[Tuya.byte_counter++] = 0xAA; + Tuya.cmd_checksum = 0xFF; + } + else if (Tuya.cmd_status == 2) { + if (Tuya.byte_counter == 5) { + Tuya.cmd_status = 3; + Tuya.data_len = serial_in_byte; + } + Tuya.cmd_checksum += serial_in_byte; + Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; + } + else if ((Tuya.cmd_status == 3) && (Tuya.byte_counter == (6 + Tuya.data_len)) && (Tuya.cmd_checksum == serial_in_byte)) { + Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; + + char hex_char[(Tuya.byte_counter * 2) + 2]; + uint16_t len = Tuya.buffer[4] << 8 | Tuya.buffer[5]; + Response_P(PSTR("{\"" D_JSON_TUYA_MCU_RECEIVED "\":{\"Data\":\"%s\",\"Cmnd\":%d"), ToHex_P((unsigned char*)Tuya.buffer, Tuya.byte_counter, hex_char, sizeof(hex_char)), Tuya.buffer[3]); + + if (len > 0) { + ResponseAppend_P(PSTR(",\"CmndData\":\"%s\""), ToHex_P((unsigned char*)&Tuya.buffer[6], len, hex_char, sizeof(hex_char))); + if (TUYA_CMD_STATE == Tuya.buffer[3]) { + + + uint8_t dpidStart = 6; + while (dpidStart + 4 < Tuya.byte_counter) { + uint8_t dpId = Tuya.buffer[dpidStart]; + uint8_t dpDataType = Tuya.buffer[dpidStart + 1]; + uint16_t dpDataLen = Tuya.buffer[dpidStart + 2] << 8 | Tuya.buffer[dpidStart + 3]; + const unsigned char *dpData = (unsigned char*)&Tuya.buffer[dpidStart + 4]; + const char *dpHexData = ToHex_P(dpData, dpDataLen, hex_char, sizeof(hex_char)); + + if (TUYA_CMD_STATE == Tuya.buffer[3]) { + ResponseAppend_P(PSTR(",\"DpType%uId%u\":"), dpDataType, dpId); + if (TUYA_TYPE_BOOL == dpDataType && dpDataLen == 1) { + ResponseAppend_P(PSTR("%u"), dpData[0]); + } else if (TUYA_TYPE_VALUE == dpDataType && dpDataLen == 4) { + uint32_t dpValue = (uint32_t)dpData[0] << 24 | (uint32_t)dpData[1] << 16 | (uint32_t)dpData[2] << 8 | (uint32_t)dpData[3] << 0; + ResponseAppend_P(PSTR("%u"), dpValue); + } else if (TUYA_TYPE_STRING == dpDataType) { + ResponseAppend_P(PSTR("\"%.*s\""), dpDataLen, dpData); + } else if (TUYA_TYPE_ENUM == dpDataType && dpDataLen == 1) { + ResponseAppend_P(PSTR("%u"), dpData[0]); + } else { + ResponseAppend_P(PSTR("\"0x%s\""), dpHexData); + } + } + + ResponseAppend_P(PSTR(",\"%d\":{\"DpId\":%d,\"DpIdType\":%d,\"DpIdData\":\"%s\""), dpId, dpId, dpDataType, dpHexData); + if (TUYA_TYPE_STRING == dpDataType) { + ResponseAppend_P(PSTR(",\"Type3Data\":\"%.*s\""), dpDataLen, dpData); + } + ResponseAppend_P(PSTR("}")); + dpidStart += dpDataLen + 4; + } + } + } + + ResponseAppend_P(PSTR("}}")); + + if (Settings.flag3.tuya_serial_mqtt_publish) { + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_TUYA_MCU_RECEIVED)); + } else { + AddLog_P(LOG_LEVEL_DEBUG, mqtt_data); + } + XdrvRulesProcess(); + + if (!Tuya.low_power_mode) { + TuyaNormalPowerModePacketProcess(); + } else { + TuyaLowPowerModePacketProcess(); + } + + Tuya.byte_counter = 0; + Tuya.cmd_status = 0; + Tuya.cmd_checksum = 0; + Tuya.data_len = 0; + } + else if (Tuya.byte_counter < TUYA_BUFFER_SIZE -1) { + Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; + Tuya.cmd_checksum += serial_in_byte; + } else { + Tuya.byte_counter = 0; + Tuya.cmd_status = 0; + Tuya.cmd_checksum = 0; + Tuya.data_len = 0; + } + } +} + +bool TuyaButtonPressed(void) +{ + if (!XdrvMailbox.index && ((PRESSED == XdrvMailbox.payload) && (NOT_PRESSED == Button.last_state[XdrvMailbox.index]))) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Reset GPIO triggered")); + TuyaResetWifi(); + return true; + } + return false; +} + +uint8_t TuyaGetTuyaWifiState(void) { + + uint8_t wifi_state = 0x02; + switch(WifiState()){ + case WIFI_MANAGER: + wifi_state = 0x01; + break; + case WIFI_RESTART: + wifi_state = 0x03; + break; + } + + if (MqttIsConnected()) { + wifi_state = 0x04; + } + + return wifi_state; +} + +void TuyaSetWifiLed(void) +{ + Tuya.wifi_state = TuyaGetTuyaWifiState(); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Set WiFi LED %d (%d)"), Tuya.wifi_state, WifiState()); + + if (Tuya.low_power_mode) { + TuyaSendCmd(TUYA_LOW_POWER_CMD_WIFI_STATE, &Tuya.wifi_state, 1); + } else { + TuyaSendCmd(TUYA_CMD_WIFI_STATE, &Tuya.wifi_state, 1); + } +} + +#ifdef USE_ENERGY_SENSOR + + + + +bool Xnrg16(uint8_t function) +{ + bool result = false; + + if (TUYA_DIMMER == my_module_type) { + if (FUNC_PRE_INIT == function) { + if (TuyaGetDpId(TUYA_MCU_FUNC_POWER) != 0) { + if (TuyaGetDpId(TUYA_MCU_FUNC_CURRENT) == 0) { + Energy.current_available = false; + } + if (TuyaGetDpId(TUYA_MCU_FUNC_VOLTAGE) == 0) { + Energy.voltage_available = false; + } + energy_flg = XNRG_16; + } + } + } + return result; +} +#endif + + + + + +bool Xdrv16(uint8_t function) +{ + bool result = false; + + if (TUYA_DIMMER == my_module_type) { + switch (function) { + case FUNC_LOOP: + if (TuyaSerial) { TuyaSerialInput(); } + break; + case FUNC_MODULE_INIT: + result = TuyaModuleSelected(); + break; + case FUNC_PRE_INIT: + TuyaInit(); + break; + case FUNC_SET_DEVICE_POWER: + result = TuyaSetPower(); + break; + case FUNC_BUTTON_PRESSED: + result = TuyaButtonPressed(); + break; + case FUNC_EVERY_SECOND: + if (TuyaSerial && Tuya.wifi_state != TuyaGetTuyaWifiState()) { TuyaSetWifiLed(); } + if (!Tuya.low_power_mode) { + Tuya.heartbeat_timer++; + if (Tuya.heartbeat_timer > 10) { + Tuya.heartbeat_timer = 0; + TuyaSendCmd(TUYA_CMD_HEARTBEAT); + } + } else { + TuyaSendLowPowerSuccessIfNeeded(); + } + break; + case FUNC_SET_CHANNELS: + result = TuyaSetChannels(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kTuyaCommand, TuyaCommand); + break; + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_17_rcswitch.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_17_rcswitch.ino" +#ifdef USE_RC_SWITCH + + + + +#define XDRV_17 17 + +#define D_JSON_RF_PROTOCOL "Protocol" +#define D_JSON_RF_BITS "Bits" +#define D_JSON_RF_DATA "Data" + +#define D_CMND_RFSEND "RFSend" +#define D_JSON_RF_PULSE "Pulse" +#define D_JSON_RF_REPEAT "Repeat" + +const char kRfSendCommands[] PROGMEM = "|" + D_CMND_RFSEND; + +void (* const RfSendCommand[])(void) PROGMEM = { + &CmndRfSend }; + +#include + +RCSwitch mySwitch = RCSwitch(); + +#define RF_TIME_AVOID_DUPLICATE 1000 + +uint32_t rf_lasttime = 0; + +void RfReceiveCheck(void) +{ + if (mySwitch.available()) { + + unsigned long data = mySwitch.getReceivedValue(); + unsigned int bits = mySwitch.getReceivedBitlength(); + int protocol = mySwitch.getReceivedProtocol(); + int delay = mySwitch.getReceivedDelay(); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFR: Data 0x%lX (%u), Bits %d, Protocol %d, Delay %d"), data, data, bits, protocol, delay); + + uint32_t now = millis(); + if ((now - rf_lasttime > RF_TIME_AVOID_DUPLICATE) && (data > 0)) { + rf_lasttime = now; + + char stemp[16]; + if (Settings.flag.rf_receive_decimal) { + snprintf_P(stemp, sizeof(stemp), PSTR("%u"), (uint32_t)data); + } else { + snprintf_P(stemp, sizeof(stemp), PSTR("\"0x%lX\""), (uint32_t)data); + } + 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(); +#ifdef USE_DOMOTICZ + DomoticzSensor(DZ_COUNT, data); +#endif + } + mySwitch.resetAvailable(); + } +} + +void RfInit(void) +{ + if (pin[GPIO_RFSEND] < 99) { + mySwitch.enableTransmit(pin[GPIO_RFSEND]); + } + if (pin[GPIO_RFRECV] < 99) { + pinMode( pin[GPIO_RFRECV], INPUT); + mySwitch.enableReceive(pin[GPIO_RFRECV]); + } +} + + + + + +void CmndRfSend(void) +{ + bool error = false; + + if (XdrvMailbox.data_len) { + unsigned long data = 0; + unsigned int bits = 24; + int protocol = 1; + int repeat = 10; + int pulse = 350; + + char dataBufUc[XdrvMailbox.data_len + 1]; + UpperCase(dataBufUc, XdrvMailbox.data); + StaticJsonBuffer<150> jsonBuf; + JsonObject &root = jsonBuf.parseObject(dataBufUc); + if (root.success()) { + + char parm_uc[10]; + data = strtoul(root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_DATA))], nullptr, 0); + bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_BITS))]; + protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_PROTOCOL))]; + repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_REPEAT))]; + pulse = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_PULSE))]; + } else { + + char *p; + uint8_t i = 0; + for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 5; str = strtok_r(nullptr, ", ", &p)) { + switch (i++) { + case 0: + data = strtoul(str, nullptr, 0); + break; + case 1: + bits = atoi(str); + break; + case 2: + protocol = atoi(str); + break; + case 3: + repeat = atoi(str); + break; + case 4: + pulse = atoi(str); + } + } + } + + if (!protocol) { protocol = 1; } + mySwitch.setProtocol(protocol); + if (!pulse) { pulse = 350; } + mySwitch.setPulseLength(pulse); + if (!repeat) { repeat = 10; } + mySwitch.setRepeatTransmit(repeat); + if (!bits) { bits = 24; } + if (data) { + mySwitch.send(data, bits); + ResponseCmndDone(); + } else { + error = true; + } + } else { + error = true; + } + if (error) { + Response_P(PSTR("{\"" D_CMND_RFSEND "\":\"" D_JSON_NO " " D_JSON_RF_DATA ", " D_JSON_RF_BITS ", " D_JSON_RF_PROTOCOL ", " D_JSON_RF_REPEAT " " D_JSON_OR " " D_JSON_RF_PULSE "\"}")); + } +} + + + + + +bool Xdrv17(uint8_t function) +{ + bool result = false; + + if ((pin[GPIO_RFSEND] < 99) || (pin[GPIO_RFRECV] < 99)) { + switch (function) { + case FUNC_EVERY_50_MSECOND: + if (pin[GPIO_RFRECV] < 99) { + RfReceiveCheck(); + } + break; + case FUNC_COMMAND: + if (pin[GPIO_RFSEND] < 99) { + result = DecodeCommand(kRfSendCommands, RfSendCommand); + } + break; + case FUNC_INIT: + RfInit(); + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_18_armtronix_dimmers.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_18_armtronix_dimmers.ino" +#ifdef USE_LIGHT +#ifdef USE_ARMTRONIX_DIMMERS + + + + + + + +#define XDRV_18 18 + +#include + +TasmotaSerial *ArmtronixSerial = nullptr; + +struct ARMTRONIX { + bool ignore_dim = false; + int8_t wifi_state = -2; + int8_t dim_state[2]; + int8_t knob_state[2]; +} Armtronix; + + + + + +bool ArmtronixSetChannels(void) +{ + LightSerial2Duty(((uint8_t*)XdrvMailbox.data)[0], ((uint8_t*)XdrvMailbox.data)[1]); + return true; +} + +void LightSerial2Duty(uint8_t duty1, uint8_t duty2) +{ + if (ArmtronixSerial && !Armtronix.ignore_dim) { + duty1 = ((float)duty1)/2.575757; + duty2 = ((float)duty2)/2.575757; + Armtronix.dim_state[0] = duty1; + Armtronix.dim_state[1] = duty2; + ArmtronixSerial->print("Dimmer1:"); + ArmtronixSerial->print(duty1); + ArmtronixSerial->print("\nDimmer2:"); + ArmtronixSerial->println(duty2); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send Serial Packet Dim Values=%d,%d"), Armtronix.dim_state[0],Armtronix.dim_state[1]); + + } else { + Armtronix.ignore_dim = false; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send Dim Level skipped due to already set. Value=%d,%d"), Armtronix.dim_state[0],Armtronix.dim_state[1]); + + } +} + +void ArmtronixRequestState(void) +{ + if (ArmtronixSerial) { + + AddLog_P(LOG_LEVEL_DEBUG, PSTR("ARM: Request MCU state")); + ArmtronixSerial->println("Status"); + + } +} + + + + + +bool ArmtronixModuleSelected(void) +{ + devices_present++; + light_type = LT_SERIAL2; + return true; +} + +void ArmtronixInit(void) +{ + Armtronix.dim_state[0] = -1; + Armtronix.dim_state[1] = -1; + Armtronix.knob_state[0] = -1; + Armtronix.knob_state[1] = -1; + ArmtronixSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); + if (ArmtronixSerial->begin(115200)) { + if (ArmtronixSerial->hardwareSerial()) { ClaimSerial(); } + ArmtronixSerial->println("Status"); + } +} + +void ArmtronixSerialInput(void) +{ + String answer; + int8_t newDimState[2]; + uint8_t temp; + int commaIndex; + char scmnd[20]; + if (ArmtronixSerial->available()) { + yield(); + answer = ArmtronixSerial->readStringUntil('\n'); + if (answer.substring(0,7) == "Status:") { + commaIndex = 6; + for (uint32_t i =0; i<2; i++) { + newDimState[i] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); + if (newDimState[i] != Armtronix.dim_state[i]) { + temp = ((float)newDimState[i])*1.01010101010101; + Armtronix.dim_state[i] = newDimState[i]; + Armtronix.ignore_dim = true; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_CHANNEL "%d %d"),i+1, temp); + ExecuteCommand(scmnd,SRC_SWITCH); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send CMND_CHANNEL=%s"), scmnd ); + } + commaIndex = answer.indexOf(',',commaIndex+1); + } + Armtronix.knob_state[0] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); + commaIndex = answer.indexOf(',',commaIndex+1); + Armtronix.knob_state[1] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); + } + } +} + +void ArmtronixSetWifiLed(void) +{ + uint8_t wifi_state = 0x02; + + switch (WifiState()) { + case WIFI_MANAGER: + wifi_state = 0x01; + break; + case WIFI_RESTART: + wifi_state = 0x03; + break; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Set WiFi LED to state %d (%d)"), wifi_state, WifiState()); + + char state = '0' + ((wifi_state & 1) > 0); + ArmtronixSerial->print("Setled:"); + ArmtronixSerial->write(state); + ArmtronixSerial->write(','); + state = '0' + ((wifi_state & 2) > 0); + ArmtronixSerial->write(state); + ArmtronixSerial->write(10); + Armtronix.wifi_state = WifiState(); +} + + + + + +bool Xdrv18(uint8_t function) +{ + bool result = false; + + if (ARMTRONIX_DIMMERS == my_module_type) { + switch (function) { + case FUNC_LOOP: + if (ArmtronixSerial) { ArmtronixSerialInput(); } + break; + case FUNC_MODULE_INIT: + result = ArmtronixModuleSelected(); + break; + case FUNC_INIT: + ArmtronixInit(); + break; + case FUNC_EVERY_SECOND: + if (ArmtronixSerial) { + if (Armtronix.wifi_state!=WifiState()) { ArmtronixSetWifiLed(); } + if (uptime &1) { + ArmtronixSerial->println("Status"); + } + } + break; + case FUNC_SET_CHANNELS: + result = ArmtronixSetChannels(); + break; + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_19_ps16dz_dimmer.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_19_ps16dz_dimmer.ino" +#ifdef USE_LIGHT +#ifdef USE_PS_16_DZ + + + + +#define XDRV_19 19 + +#define PS16DZ_BUFFER_SIZE 80 + +#include + +TasmotaSerial *PS16DZSerial = nullptr; + +struct PS16DZ { + char *rx_buffer = nullptr; + int byte_counter = 0; + uint8_t dimmer = 0; +} Ps16dz; + + + + + +void PS16DZSerialSend(const char *tx_buffer) +{ + + + PS16DZSerial->print(tx_buffer); + PS16DZSerial->write(0x1B); + PS16DZSerial->flush(); +} + +void PS16DZSerialSendOk(void) +{ + char tx_buffer[16]; + snprintf_P(tx_buffer, sizeof(tx_buffer), PSTR("AT+SEND=ok")); + PS16DZSerialSend(tx_buffer); +} + + + + +void PS16DZSerialSendUpdateCommand(void) +{ + uint8_t light_state_dimmer = light_state.getDimmer(); + + light_state_dimmer = (light_state_dimmer < Settings.dimmer_hw_min) ? Settings.dimmer_hw_min : light_state_dimmer; + light_state_dimmer = (light_state_dimmer > Settings.dimmer_hw_max) ? Settings.dimmer_hw_max : light_state_dimmer; + + char tx_buffer[80]; + snprintf_P(tx_buffer, sizeof(tx_buffer), PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"bright\":%d"), + LocalTime(), millis()%1000, power?"on":"off", light_state_dimmer); + + PS16DZSerialSend(tx_buffer); +} + + + + + +void PS16DZSerialInput(void) +{ + char scmnd[20]; + while (PS16DZSerial->available()) { + yield(); + uint8_t serial_in_byte = PS16DZSerial->read(); + if (serial_in_byte != 0x1B) { + if (Ps16dz.byte_counter >= PS16DZ_BUFFER_SIZE - 1) { + memset(Ps16dz.rx_buffer, 0, PS16DZ_BUFFER_SIZE); + Ps16dz.byte_counter = 0; + } + if (Ps16dz.byte_counter || (!Ps16dz.byte_counter && ('A' == serial_in_byte))) { + Ps16dz.rx_buffer[Ps16dz.byte_counter++] = serial_in_byte; + } + } else { + Ps16dz.rx_buffer[Ps16dz.byte_counter++] = 0x00; + + + + + if (!strncmp(Ps16dz.rx_buffer+3, "RESULT", 6)) { + + } + else if (!strncmp(Ps16dz.rx_buffer+3, "UPDATE", 6)) { + + char *end_str; + char *string = Ps16dz.rx_buffer+10; + char *token = strtok_r(string, ",", &end_str); + + bool is_switch_change = false; + bool is_brightness_change = false; + + while (token != nullptr) { + char* end_token; + char* token2 = strtok_r(token, ":", &end_token); + char* token3 = strtok_r(nullptr, ":", &end_token); + + if (!strncmp(token2, "\"switch\"", 8)) { + bool switch_state = !strncmp(token3, "\"on\"", 4) ? true : false; + + + + is_switch_change = (switch_state != power); + if (is_switch_change) { + ExecuteCommandPower(1, switch_state, SRC_SWITCH); + } + } + else if (!strncmp(token2, "\"bright\"", 8)) { + Ps16dz.dimmer = atoi(token3); + + + + is_brightness_change = Ps16dz.dimmer != Settings.light_dimmer; + if (power && (Ps16dz.dimmer > 0) && is_brightness_change) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), Ps16dz.dimmer); + ExecuteCommand(scmnd, SRC_SWITCH); + } + } + else if (!strncmp(token2, "\"sequence\"", 10)) { + + + + } + token = strtok_r(nullptr, ",", &end_str); + } + + if (!is_brightness_change) { + + + + PS16DZSerialSendOk(); + } + } + else if (!strncmp(Ps16dz.rx_buffer+3, "SETTING", 7)) { + + + if (!Settings.flag.button_restrict) { + int state = WIFI_MANAGER; + if (!strncmp(Ps16dz.rx_buffer+10, "=exit", 5)) { state = WIFI_RETRY; } + if (state != Settings.sta_config) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " %d"), state); + ExecuteCommand(scmnd, SRC_BUTTON); + } + } + } + memset(Ps16dz.rx_buffer, 0, PS16DZ_BUFFER_SIZE); + Ps16dz.byte_counter = 0; + } + } +} + +bool PS16DZSerialSendUpdateCommandIfRequired(void) +{ + if (!PS16DZSerial) { return true; } + + bool is_switch_change = (XdrvMailbox.payload != SRC_SWITCH); + bool is_brightness_change = (light_state.getDimmer() != Ps16dz.dimmer); + + if (is_switch_change || is_brightness_change) { + PS16DZSerialSendUpdateCommand(); + } + + return true; +} + +void PS16DZInit(void) +{ + Ps16dz.rx_buffer = (char*)(malloc(PS16DZ_BUFFER_SIZE)); + if (Ps16dz.rx_buffer != nullptr) { + PS16DZSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); + if (PS16DZSerial->begin(19200)) { + if (PS16DZSerial->hardwareSerial()) { ClaimSerial(); } + } + } +} + +bool PS16DZModuleSelected(void) +{ + devices_present++; + light_type = LT_SERIAL1; + + return true; +} + + + + + +bool Xdrv19(uint8_t function) +{ + bool result = false; + + if (PS_16_DZ == my_module_type) { + switch (function) { + case FUNC_LOOP: + if (PS16DZSerial) { PS16DZSerialInput(); } + break; + case FUNC_SET_DEVICE_POWER: + case FUNC_SET_CHANNELS: + result = PS16DZSerialSendUpdateCommandIfRequired(); + break; + case FUNC_INIT: + PS16DZInit(); + break; + case FUNC_MODULE_INIT: + result = PS16DZModuleSelected(); + break; + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_20_hue.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_20_hue.ino" +#if defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) +# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_20_hue.ino" +#define XDRV_20 20 + +const char HUE_RESPONSE[] PROGMEM = + "HTTP/1.1 200 OK\r\n" + "HOST: 239.255.255.250:1900\r\n" + "CACHE-CONTROL: max-age=100\r\n" + "EXT:\r\n" + "LOCATION: http://%s:80/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.24.0\r\n" + "hue-bridgeid: %s\r\n"; +const char HUE_ST1[] PROGMEM = + "ST: upnp:rootdevice\r\n" + "USN: uuid:%s::upnp:rootdevice\r\n" + "\r\n"; +const char HUE_ST2[] PROGMEM = + "ST: uuid:%s\r\n" + "USN: uuid:%s\r\n" + "\r\n"; +const char HUE_ST3[] PROGMEM = + "ST: urn:schemas-upnp-org:device:basic:1\r\n" + "USN: uuid:%s\r\n" + "\r\n"; + +String HueBridgeId(void) +{ + String temp = WiFi.macAddress(); + temp.replace(":", ""); + String bridgeid = temp.substring(0, 6); + bridgeid += "FFFE"; + bridgeid += temp.substring(6); + return bridgeid; +} + +String HueSerialnumber(void) +{ + String serial = WiFi.macAddress(); + serial.replace(":", ""); + serial.toLowerCase(); + return serial; +} + +String HueUuid(void) +{ + String uuid = F("f6543a06-da50-11ba-8d8f-"); + uuid += HueSerialnumber(); + return uuid; +} + +void HueRespondToMSearch(void) +{ + char message[TOPSZ]; + + TickerMSearch.detach(); + if (PortUdp.beginPacket(udp_remote_ip, udp_remote_port)) { + char response[320]; + snprintf_P(response, sizeof(response), HUE_RESPONSE, WiFi.localIP().toString().c_str(), HueBridgeId().c_str()); + int len = strlen(response); + String uuid = HueUuid(); + + snprintf_P(response + len, sizeof(response) - len, HUE_ST1, uuid.c_str()); + PortUdp.write(response); + PortUdp.endPacket(); + + snprintf_P(response + len, sizeof(response) - len, HUE_ST2, uuid.c_str(), uuid.c_str()); + PortUdp.write(response); + PortUdp.endPacket(); + + snprintf_P(response + len, sizeof(response) - len, HUE_ST3, uuid.c_str()); + PortUdp.write(response); + PortUdp.endPacket(); + + snprintf_P(message, sizeof(message), PSTR(D_3_RESPONSE_PACKETS_SENT)); + } else { + snprintf_P(message, sizeof(message), PSTR(D_FAILED_TO_SEND_RESPONSE)); + } + + PrepLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_HUE " %s " D_TO " %s:%d"), + message, udp_remote_ip.toString().c_str(), udp_remote_port); + + udp_response_mutex = false; +} + + + + + +const char HUE_DESCRIPTION_XML[] PROGMEM = + "" + "" + "" + "1" + "0" + "" + + "http://{x1:80/" + "" + "urn:schemas-upnp-org:device:Basic:1" + "Amazon-Echo-HA-Bridge ({x1)" + + "Royal Philips Electronics" + "http://www.philips.com" + "Philips hue Personal Wireless Lighting" + "Philips hue bridge 2012" + "929000226503" + "{x3" + "uuid:{x2" + "" + "\r\n" + "\r\n"; +const char HUE_LIGHTS_STATUS_JSON1_SUFFIX[] PROGMEM = + "%s\"alert\":\"none\"," + "\"effect\":\"none\"," + "\"reachable\":true}"; +const char HUE_LIGHTS_STATUS_JSON2[] PROGMEM = + ",\"type\":\"Extended color light\"," + "\"name\":\"%s\"," + "\"modelid\":\"LCT007\"," + "\"uniqueid\":\"%s\"," + "\"swversion\":\"5.50.1.19085\"}"; +const char HUE_GROUP0_STATUS_JSON[] PROGMEM = + "{\"name\":\"Group 0\"," + "\"lights\":[{l1]," + "\"type\":\"LightGroup\"," + "\"action\":"; + +const char HueConfigResponse_JSON[] PROGMEM = + "{\"name\":\"Philips hue\"," + "\"mac\":\"{ma\"," + "\"dhcp\":true," + "\"ipaddress\":\"{ip\"," + "\"netmask\":\"{ms\"," + "\"gateway\":\"{gw\"," + "\"proxyaddress\":\"none\"," + "\"proxyport\":0," + "\"bridgeid\":\"{br\"," + "\"UTC\":\"{dt\"," + "\"whitelist\":{\"{id\":{" + "\"last use date\":\"{dt\"," + "\"create date\":\"{dt\"," + "\"name\":\"Remote\"}}," + "\"swversion\":\"01041302\"," + "\"apiversion\":\"1.17.0\"," + "\"swupdate\":{\"updatestate\":0,\"url\":\"\",\"text\":\"\",\"notify\": false}," + "\"linkbutton\":false," + "\"portalservices\":false" + "}"; +const char HUE_ERROR_JSON[] PROGMEM = + "[{\"error\":{\"type\":901,\"address\":\"/\",\"description\":\"Internal Error\"}}]"; + + + +String GetHueDeviceId(uint16_t id) +{ + String deviceid = WiFi.macAddress(); + deviceid += F(":00:11-"); + deviceid += String(id); + deviceid.toLowerCase(); + return deviceid; +} + +String GetHueUserId(void) +{ + char userid[7]; + + snprintf_P(userid, sizeof(userid), PSTR("%03x"), ESP_getChipId()); + return String(userid); +} + +void HandleUpnpSetupHue(void) +{ + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_HUE_BRIDGE_SETUP)); + String description_xml = FPSTR(HUE_DESCRIPTION_XML); + description_xml.replace("{x1", WiFi.localIP().toString()); + description_xml.replace("{x2", HueUuid()); + description_xml.replace("{x3", HueSerialnumber()); + WSSend(200, CT_XML, description_xml); +} + +void HueNotImplemented(String *path) +{ + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_API_NOT_IMPLEMENTED " (%s)"), path->c_str()); + + WSSend(200, CT_JSON, "{}"); +} + +void HueConfigResponse(String *response) +{ + *response += FPSTR(HueConfigResponse_JSON); + response->replace("{ma", WiFi.macAddress()); + response->replace("{ip", WiFi.localIP().toString()); + response->replace("{ms", WiFi.subnetMask().toString()); + response->replace("{gw", WiFi.gatewayIP().toString()); + response->replace("{br", HueBridgeId()); + response->replace("{dt", GetDateAndTime(DT_UTC)); + response->replace("{id", GetHueUserId()); +} + +void HueConfig(String *path) +{ + String response = ""; + HueConfigResponse(&response); + WSSend(200, CT_JSON, response); +} + + + +bool g_gotct = false; + + + + +uint16_t prev_hue = 0; +uint8_t prev_sat = 0; +uint8_t prev_bri = 254; +uint16_t prev_ct = 254; +char prev_x_str[24] = "\0"; +char prev_y_str[24] = "\0"; + +uint8_t getLocalLightSubtype(uint8_t device) { + if (light_type) { + if (device >= Light.device) { + if (Settings.flag3.pwm_multi_channels) { + return LST_SINGLE; + } else { + return Light.subtype; + } + } else { + return LST_NONE; + } + } else { + return LST_NONE; + } +} + +void HueLightStatus1(uint8_t device, String *response) +{ + uint16_t ct = 0; + uint8_t color_mode; + String light_status = ""; + uint16_t hue = 0; + uint8_t sat = 0; + uint8_t bri = 254; + uint32_t echo_gen = findEchoGeneration(); + + + uint8_t local_light_subtype = getLocalLightSubtype(device); + + bri = LightGetBri(device); + if (bri > 254) bri = 254; + if (bri < 1) bri = 1; + +#ifdef USE_SHUTTER + if (ShutterState(device)) { + bri = (float)((Settings.shutter_options[device-1] & 1) ? 100 - Settings.shutter_position[device-1] : Settings.shutter_position[device-1]) / 100; + } +#endif + + if (light_type) { + light_state.getHSB(&hue, &sat, nullptr); + + if ((bri > prev_bri ? bri - prev_bri : prev_bri - bri) < 1) + bri = prev_bri; + + if (sat > 254) sat = 254; + if ((sat > prev_sat ? sat - prev_sat : prev_sat - sat) < 1) { + sat = prev_sat; + } else { + prev_x_str[0] = prev_y_str[0] = 0; + } + + hue = changeUIntScale(hue, 0, 360, 0, 65535); + if ((hue > prev_hue ? hue - prev_hue : prev_hue - hue) < 400) { + hue = prev_hue; + } else { + prev_x_str[0] = prev_y_str[0] = 0; + } + + color_mode = light_state.getColorMode(); + ct = light_state.getCT(); + if (LCM_RGB == color_mode) { g_gotct = false; } + if (LCM_CT == color_mode) { g_gotct = true; } + + + + if ((ct > prev_ct ? ct - prev_ct : prev_ct - ct) < 1) + ct = prev_ct; + + + + } + + const size_t buf_size = 256; + char * buf = (char*) malloc(buf_size); + + snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), (power & (1 << (device-1))) ? "true" : "false"); + + if ((1 == echo_gen) || (LST_SINGLE <= local_light_subtype)) { + snprintf_P(buf, buf_size, PSTR("%s\"bri\":%d,"), buf, bri); + } + if (LST_COLDWARM <= local_light_subtype) { + snprintf_P(buf, buf_size, PSTR("%s\"colormode\":\"%s\","), buf, g_gotct ? "ct" : "hs"); + } + if (LST_RGB <= local_light_subtype) { + if (prev_x_str[0] && prev_y_str[0]) { + snprintf_P(buf, buf_size, PSTR("%s\"xy\":[%s,%s],"), buf, prev_x_str, prev_y_str); + } else { + float x, y; + light_state.getXY(&x, &y); + snprintf_P(buf, buf_size, PSTR("%s\"xy\":[%s,%s],"), buf, String(x, 5).c_str(), String(y, 5).c_str()); + } + snprintf_P(buf, buf_size, PSTR("%s\"hue\":%d,\"sat\":%d,"), buf, hue, sat); + } + if (LST_COLDWARM == local_light_subtype || LST_RGBW <= local_light_subtype) { + snprintf_P(buf, buf_size, PSTR("%s\"ct\":%d,"), buf, ct > 0 ? ct : 284); + } + snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON1_SUFFIX, buf); + + *response += buf; + free(buf); +} + + + +bool HueActive(uint8_t device) { + if (device > MAX_FRIENDLYNAMES) { device = MAX_FRIENDLYNAMES; } + return '$' != *SettingsText(SET_FRIENDLYNAME1 +device -1); +} + +void HueLightStatus2(uint8_t device, String *response) +{ + const size_t buf_size = 192; + char * buf = (char*) malloc(buf_size); + const size_t max_name_len = 32; + char fname[max_name_len + 1]; + + strlcpy(fname, SettingsText(device <= MAX_FRIENDLYNAMES ? SET_FRIENDLYNAME1 + device -1 : SET_FRIENDLYNAME1 + MAX_FRIENDLYNAMES -1), max_name_len + 1); + + if (device > MAX_FRIENDLYNAMES) { + uint32_t fname_len = strlen(fname); + if (fname_len > max_name_len - 2) { fname_len = max_name_len - 2; } + fname[fname_len++] = '-'; + if (device - MAX_FRIENDLYNAMES < 10) { + fname[fname_len++] = '0' + device - MAX_FRIENDLYNAMES; + } else { + fname[fname_len++] = 'A' + device - MAX_FRIENDLYNAMES - 10; + } + fname[fname_len] = 0x00; + } + snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON2, fname, GetHueDeviceId(device).c_str()); + *response += buf; + free(buf); +} + + + + + +#ifndef USE_ZIGBEE +uint32_t EncodeLightId(uint8_t relay_id) +#else +uint32_t EncodeLightId(uint8_t relay_id, uint16_t z_shortaddr = 0) +#endif +{ + uint8_t mac[6]; + WiFi.macAddress(mac); + uint32_t id = (mac[3] << 20) | (mac[4] << 12) | (mac[5] << 4); + + if (relay_id >= 32) { + relay_id = 0; + } + if (relay_id > 15) { + id |= (1 << 28); + } + id |= (relay_id & 0xF); +#ifdef USE_ZIGBEE + if ((z_shortaddr) && (!relay_id)) { + + id = (1 << 29) | z_shortaddr; + } +#endif + + return id; +} + + + + + + + +#ifndef USE_ZIGBEE +uint32_t DecodeLightId(uint32_t hue_id) +#else +uint32_t DecodeLightId(uint32_t hue_id, uint16_t * shortaddr = nullptr) +#endif +{ + uint8_t relay_id = hue_id & 0xF; + if (hue_id & (1 << 28)) { + relay_id += 16; + } + if (0 == relay_id) { + relay_id = 32; + } +#ifdef USE_ZIGBEE + if (hue_id & (1 << 29)) { + + if (shortaddr) { *shortaddr = hue_id & 0xFFFF; } + relay_id = 0; + } +#endif + return relay_id; +} + +static const char * FIRST_GEN_UA[] = { + "AEOBC", +}; + + +uint32_t findEchoGeneration(void) { + + String user_agent = Webserver->header("User-Agent"); + uint32_t gen = 2; + + for (uint32_t i = 0; i < sizeof(FIRST_GEN_UA)/sizeof(char*); i++) { + if (user_agent.indexOf(FIRST_GEN_UA[i]) >= 0) { + gen = 1; + break; + } + } + if (0 == user_agent.length()) { + gen = 1; + } + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " User-Agent: %s, gen=%d"), user_agent.c_str(), gen); + + return gen; +} + +void HueGlobalConfig(String *path) { + String response; + + path->remove(0,1); + response = F("{\"lights\":{"); + bool appending = false; + CheckHue(&response, appending); +#ifdef USE_ZIGBEE + ZigbeeCheckHue(&response, appending); +#endif + response += F("},\"groups\":{},\"schedules\":{},\"config\":"); + HueConfigResponse(&response); + response += "}"; + WSSend(200, CT_JSON, response); +} + +void HueAuthentication(String *path) +{ + char response[38]; + + snprintf_P(response, sizeof(response), PSTR("[{\"success\":{\"username\":\"%s\"}}]"), GetHueUserId().c_str()); + WSSend(200, CT_JSON, response); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Authentication Result (%s)"), response); +} + + +void CheckHue(String * response, bool &appending) { + uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present; + for (uint32_t i = 1; i <= maxhue; i++) { + if (HueActive(i)) { + if (appending) { *response += ","; } + *response += "\""; + *response += EncodeLightId(i); + *response += F("\":{\"state\":"); + HueLightStatus1(i, response); + HueLightStatus2(i, response); + appending = true; + } + } +} + +void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { + uint16_t tmp = 0; + uint16_t hue = 0; + uint8_t sat = 0; + uint8_t bri = 254; + uint16_t ct = 0; + bool on = false; + bool resp = false; + bool change = false; + uint8_t local_light_subtype = getLocalLightSubtype(device); + + const size_t buf_size = 100; + char * buf = (char*) malloc(buf_size); + + if (Webserver->args()) { + response = "["; + + StaticJsonBuffer<300> jsonBuffer; + JsonObject &hue_json = jsonBuffer.parseObject(Webserver->arg((Webserver->args())-1)); + if (hue_json.containsKey("on")) { + on = hue_json["on"]; + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/on\":%s}}"), + device_id, on ? "true" : "false"); + +#ifdef USE_SHUTTER + if (ShutterState(device)) { + if (!change) { + bri = on ? 1.0f : 0.0f; + change = true; + resp = true; + response += buf; + } + } else { +#endif +# 554 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_20_hue.ino" + ExecuteCommandPower(device, (on) ? POWER_ON : POWER_OFF, SRC_HUE); + response += buf; + resp = true; +#ifdef USE_SHUTTER + } +#endif + } + + if (light_type && (local_light_subtype >= LST_SINGLE)) { + if (!Settings.flag3.pwm_multi_channels) { + light_state.getHSB(&hue, &sat, nullptr); + bri = light_state.getBri(); + ct = light_state.getCT(); + uint8_t color_mode = light_state.getColorMode(); + if (LCM_RGB == color_mode) { g_gotct = false; } + if (LCM_CT == color_mode) { g_gotct = true; } + + } else { + bri = LightGetBri(device); + } + } + prev_x_str[0] = prev_y_str[0] = 0; + + if (hue_json.containsKey("bri")) { + bri = hue_json["bri"]; + prev_bri = bri; + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), + device_id, "bri", bri); + response += buf; + if (LST_SINGLE <= Light.subtype) { + + if (254 <= bri) { bri = 255; } + change = true; + } + resp = true; + } + + + if (hue_json.containsKey("xy")) { + float x = hue_json["xy"][0]; + float y = hue_json["xy"][1]; + const String &x_str = hue_json["xy"][0]; + const String &y_str = hue_json["xy"][1]; + x_str.toCharArray(prev_x_str, sizeof(prev_x_str)); + y_str.toCharArray(prev_y_str, sizeof(prev_y_str)); + uint8_t rr,gg,bb; + LightStateClass::XyToRgb(x, y, &rr, &gg, &bb); + LightStateClass::RgbToHsb(rr, gg, bb, &hue, &sat, nullptr); + prev_hue = changeUIntScale(hue, 0, 360, 0, 65535); + prev_sat = (sat > 254 ? 254 : sat); + + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/xy\":[%s,%s]}}"), + device_id, prev_x_str, prev_y_str); + response += buf; + g_gotct = false; + resp = true; + change = true; + } + if (hue_json.containsKey("hue")) { + hue = hue_json["hue"]; + prev_hue = hue; + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), + device_id, "hue", hue); + response += buf; + if (LST_RGB <= Light.subtype) { + + hue = changeUIntScale(hue, 0, 65535, 0, 360); + g_gotct = false; + change = true; + } + resp = true; + } + if (hue_json.containsKey("sat")) { + sat = hue_json["sat"]; + prev_sat = sat; + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), + device_id, "sat", sat); + response += buf; + if (LST_RGB <= Light.subtype) { + + if (254 <= sat) { sat = 255; } + g_gotct = false; + change = true; + } + resp = true; + } + if (hue_json.containsKey("ct")) { + ct = hue_json["ct"]; + prev_ct = ct; + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), + device_id, "ct", ct); + response += buf; + if ((LST_COLDWARM == Light.subtype) || (LST_RGBW <= Light.subtype)) { + g_gotct = true; + change = true; + } + resp = true; + } + if (change) { +#ifdef USE_SHUTTER + if (ShutterState(device)) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Settings.shutter_invert: %d"), Settings.shutter_options[device-1] & 1); + ShutterSetPosition(device, bri * 100.0f ); + } else +#endif + if (light_type && (local_light_subtype > LST_NONE)) { + if (!Settings.flag3.pwm_multi_channels) { + if (g_gotct) { + light_controller.changeCTB(ct, bri); + } else { + light_controller.changeHSB(hue, sat, bri); + } + LightPreparePower(); + } else { + LightSetBri(device, bri); + } + if (LST_COLDWARM <= local_light_subtype) { + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_COLOR)); + } else { + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_DIMMER)); + } + XdrvRulesProcess(); + } + change = false; + } + response += "]"; + if (2 == response.length()) { + response = FPSTR(HUE_ERROR_JSON); + } + } + else { + response = FPSTR(HUE_ERROR_JSON); + } + free(buf); +} + +void HueLights(String *path) +{ + + + + String response; + int code = 200; + uint8_t device = 1; + uint32_t device_id; + uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present; + + path->remove(0,path->indexOf(F("/lights"))); + if (path->endsWith(F("/lights"))) { + response = "{"; + bool appending = false; + CheckHue(&response, appending); +#ifdef USE_ZIGBEE + ZigbeeCheckHue(&response, appending); +#endif +#ifdef USE_SCRIPT_HUE + Script_Check_Hue(&response); +#endif + response += "}"; + } + else if (path->endsWith(F("/state"))) { + path->remove(0,8); + path->remove(path->indexOf(F("/state"))); + device_id = atoi(path->c_str()); + device = DecodeLightId(device_id); +#ifdef USE_ZIGBEE + uint16_t shortaddr; + device = DecodeLightId(device_id, &shortaddr); + if (shortaddr) { + return ZigbeeHandleHue(shortaddr, device_id, response); + } +#endif + +#ifdef USE_SCRIPT_HUE + if (device > devices_present) { + return Script_Handle_Hue(path); + } +#endif + if ((device >= 1) || (device <= maxhue)) { + HueLightsCommand(device, device_id, response); + } + + } + else if(path->indexOf(F("/lights/")) >= 0) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("/lights path=%s"), path->c_str()); + path->remove(0,8); + device_id = atoi(path->c_str()); + device = DecodeLightId(device_id); +#ifdef USE_ZIGBEE + uint16_t shortaddr; + device = DecodeLightId(device_id, &shortaddr); + if (shortaddr) { + ZigbeeHueStatus(&response, shortaddr); + goto exit; + } +#endif + +#ifdef USE_SCRIPT_HUE + if (device > devices_present) { + Script_HueStatus(&response, device-devices_present - 1); + goto exit; + } +#endif + + if ((device < 1) || (device > maxhue)) { + device = 1; + } + response += F("{\"state\":"); + HueLightStatus1(device, &response); + HueLightStatus2(device, &response); + } + else { + response = "{}"; + code = 406; + } + exit: + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str()); + WSSend(code, CT_JSON, response); +} + +void HueGroups(String *path) +{ + + + + String response = "{}"; + uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present; + + + if (path->endsWith("/0")) { + response = FPSTR(HUE_GROUP0_STATUS_JSON); + String lights = F("\"1\""); + for (uint32_t i = 2; i <= maxhue; i++) { + lights += ",\""; + lights += EncodeLightId(i); + lights += "\""; + } + +#ifdef USE_ZIGBEE + ZigbeeHueGroups(&response); +#endif + response.replace("{l1", lights); + HueLightStatus1(1, &response); + response += F("}"); + } + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " HueGroups Result (%s)"), path->c_str()); + WSSend(200, CT_JSON, response); +} + +void HandleHueApi(String *path) +{ +# 828 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_20_hue.ino" + uint8_t args = 0; + + path->remove(0, 4); + uint16_t apilen = path->length(); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_API " (%s)"), path->c_str()); + for (args = 0; args < Webserver->args(); args++) { + String json = Webserver->arg(args); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_POST_ARGS " (%s)"), json.c_str()); + } + + if (path->endsWith(F("/invalid/"))) {} + else if (!apilen) HueAuthentication(path); + else if (path->endsWith(F("/"))) HueAuthentication(path); + else if (path->endsWith(F("/config"))) HueConfig(path); + else if (path->indexOf(F("/lights")) >= 0) HueLights(path); + else if (path->indexOf(F("/groups")) >= 0) HueGroups(path); + else if (path->endsWith(F("/schedules"))) HueNotImplemented(path); + else if (path->endsWith(F("/sensors"))) HueNotImplemented(path); + else if (path->endsWith(F("/scenes"))) HueNotImplemented(path); + else if (path->endsWith(F("/rules"))) HueNotImplemented(path); + else if (path->endsWith(F("/resourcelinks"))) HueNotImplemented(path); + else HueGlobalConfig(path); +} + + + + + +bool Xdrv20(uint8_t function) +{ + bool result = false; + +#if defined(USE_SCRIPT_HUE) || defined(USE_ZIGBEE) + if ((EMUL_HUE == Settings.flag2.emulation)) { +#else + if (devices_present && (EMUL_HUE == Settings.flag2.emulation)) { +#endif + switch (function) { + case FUNC_WEB_ADD_HANDLER: + Webserver->on(F("/description.xml"), HandleUpnpSetupHue); + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_21_wemo.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_21_wemo.ino" +#if defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined (USE_EMULATION_WEMO) + + + + +#define XDRV_21 21 + +const char WEMO_MSEARCH[] PROGMEM = + "HTTP/1.1 200 OK\r\n" + "CACHE-CONTROL: max-age=86400\r\n" + "DATE: Fri, 15 Apr 2016 04:56:29 GMT\r\n" + "EXT:\r\n" + "LOCATION: http://%s:80/setup.xml\r\n" + "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" + "01-NLS: b9200ebb-736d-4b93-bf03-835149d13983\r\n" + "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n" + "ST: %s\r\n" + "USN: uuid:%s::%s\r\n" + "X-User-Agent: redsonic\r\n" + "\r\n"; + +String WemoSerialnumber(void) +{ + char serial[16]; + + snprintf_P(serial, sizeof(serial), PSTR("201612K%08X"), ESP_getChipId()); + return String(serial); +} + +String WemoUuid(void) +{ + char uuid[27]; + + snprintf_P(uuid, sizeof(uuid), PSTR("Socket-1_0-%s"), WemoSerialnumber().c_str()); + return String(uuid); +} + +void WemoRespondToMSearch(int echo_type) +{ + char message[TOPSZ]; + + TickerMSearch.detach(); + if (PortUdp.beginPacket(udp_remote_ip, udp_remote_port)) { + char type[24]; + if (1 == echo_type) { + strcpy_P(type, URN_BELKIN_DEVICE_CAP); + } else { + strcpy_P(type, UPNP_ROOTDEVICE); + } + char response[400]; + snprintf_P(response, sizeof(response), WEMO_MSEARCH, WiFi.localIP().toString().c_str(), type, WemoUuid().c_str(), type); + PortUdp.write(response); + PortUdp.endPacket(); + snprintf_P(message, sizeof(message), PSTR(D_RESPONSE_SENT)); + } else { + snprintf_P(message, sizeof(message), PSTR(D_FAILED_TO_SEND_RESPONSE)); + } + + PrepLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_WEMO " " D_JSON_TYPE " %d, %s " D_TO " %s:%d"), + echo_type, message, udp_remote_ip.toString().c_str(), udp_remote_port); + + udp_response_mutex = false; +} + + + + + +const char WEMO_EVENTSERVICE_XML[] PROGMEM = + "" + "" + "" + "SetBinaryState" + "" + "" + "" + "BinaryState" + "BinaryState" + "in" + "" + "" + "" + "" + "GetBinaryState" + "" + "" + "" + "BinaryState" + "BinaryState" + "out" + "" + "" + "" + "" + "" + "" + "BinaryState" + "bool" + "0" + "" + "" + "level" + "string" + "0" + "" + "" + "\r\n\r\n"; + +const char WEMO_METASERVICE_XML[] PROGMEM = + "" + "" + "1" + "0" + "" + "" + "" + "GetMetaInfo" + "" + "" + "GetMetaInfo" + "MetaInfo" + "in" + "" + "" + "" + "" + "" + "MetaInfo" + "string" + "0" + "" + "" + "\r\n\r\n"; + +const char WEMO_RESPONSE_STATE_SOAP[] PROGMEM = + "" + "" + "" + "%d" + "" + "" + "\r\n"; + +const char WEMO_SETUP_XML[] PROGMEM = + "" + "" + "" + "urn:Belkin:device:controllee:1" + "{x1" + "Belkin International Inc." + "Socket" + "3.1415" + "uuid:{x2" + "{x3" + "0" + "" + "" + "urn:Belkin:service:basicevent:1" + "urn:Belkin:serviceId:basicevent1" + "/upnp/control/basicevent1" + "/upnp/event/basicevent1" + "/eventservice.xml" + "" + "" + "urn:Belkin:service:metainfo:1" + "urn:Belkin:serviceId:metainfo1" + "/upnp/control/metainfo1" + "/upnp/event/metainfo1" + "/metainfoservice.xml" + "" + "" + "" + "\r\n"; + + + +void HandleUpnpEvent(void) +{ + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_WEMO_BASIC_EVENT)); + + char event[500]; + strlcpy(event, Webserver->arg(0).c_str(), sizeof(event)); + + + + + char state = 'G'; + if (strstr_P(event, PSTR("SetBinaryState")) != nullptr) { + state = 'S'; + uint8_t power = POWER_TOGGLE; + if (strstr_P(event, PSTR("State>10on("/upnp/control/basicevent1", HTTP_POST, HandleUpnpEvent); + Webserver->on("/eventservice.xml", HandleUpnpService); + Webserver->on("/metainfoservice.xml", HandleUpnpMetaService); + Webserver->on("/setup.xml", HandleUpnpSetupWemo); + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_22_sonoff_ifan.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_22_sonoff_ifan.ino" +#ifdef USE_SONOFF_IFAN + + + + +#define XDRV_22 22 + +const uint8_t MAX_FAN_SPEED = 4; + +const uint8_t kIFan02Speed[MAX_FAN_SPEED] = { 0x00, 0x01, 0x03, 0x05 }; +const uint8_t kIFan03Speed[MAX_FAN_SPEED +2] = { 0x00, 0x01, 0x03, 0x04, 0x05, 0x06 }; +const uint8_t kIFan03Sequence[MAX_FAN_SPEED][MAX_FAN_SPEED] = {{0, 2, 2, 2}, {0, 1, 2, 4}, {1, 1, 2, 5}, {4, 4, 5, 3}}; + +const char kSonoffIfanCommands[] PROGMEM = "|" + D_CMND_FANSPEED; + +void (* const SonoffIfanCommand[])(void) PROGMEM = { + &CmndFanspeed }; + +uint8_t ifan_fanspeed_timer = 0; +uint8_t ifan_fanspeed_goal = 0; +bool ifan_receive_flag = false; +bool ifan_restart_flag = true; + + + +bool IsModuleIfan(void) +{ + return ((SONOFF_IFAN02 == my_module_type) || (SONOFF_IFAN03 == my_module_type)); +} + +uint8_t MaxFanspeed(void) +{ + return MAX_FAN_SPEED; +} + +uint8_t GetFanspeed(void) +{ + if (ifan_fanspeed_timer) { + return ifan_fanspeed_goal; + } else { + + + + + + + uint8_t fanspeed = (uint8_t)(power &0xF) >> 1; + if (fanspeed) { fanspeed = (fanspeed >> 1) +1; } + return fanspeed; + } +} + + + +void SonoffIFanSetFanspeed(uint8_t fanspeed, bool sequence) +{ + ifan_fanspeed_timer = 0; + ifan_fanspeed_goal = fanspeed; + + uint8_t fanspeed_now = GetFanspeed(); + + if (fanspeed == fanspeed_now) { return; } + + uint8_t fans = kIFan02Speed[fanspeed]; + if (SONOFF_IFAN03 == my_module_type) { + if (sequence) { + fanspeed = kIFan03Sequence[fanspeed_now][ifan_fanspeed_goal]; + if (fanspeed != ifan_fanspeed_goal) { + if (0 == fanspeed_now) { + ifan_fanspeed_timer = 20; + } else { + ifan_fanspeed_timer = 2; + } + } + } + fans = kIFan03Speed[fanspeed]; + } + for (uint32_t i = 2; i < 5; i++) { + uint8_t state = (fans &1) + POWER_OFF_NO_STATE; + ExecuteCommandPower(i, state, SRC_IGNORE); + fans >>= 1; + } + +#ifdef USE_DOMOTICZ + if (sequence) { DomoticzUpdateFanState(); } +#endif +} + + + +void SonoffIfanReceived(void) +{ + char svalue[32]; + + uint8_t mode = serial_in_buffer[3]; + uint8_t action = serial_in_buffer[6]; + + if (4 == mode) { + if (action < 4) { + + + + + if (action != GetFanspeed()) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_FANSPEED " %d"), action); + ExecuteCommand(svalue, SRC_REMOTE); +#ifdef USE_BUZZER + BuzzerEnabledBeep((action) ? action : 1, (action) ? 1 : 4); +#endif + } + } else { + + ExecuteCommandPower(1, POWER_TOGGLE, SRC_REMOTE); + } + } + if (6 == mode) { + + Settings.flag3.buzzer_enable = !Settings.flag3.buzzer_enable; + } + if (7 == mode) { + +#ifdef USE_BUZZER + BuzzerEnabledBeep(4, 1); +#endif + } + + + + serial_in_buffer[5] = 0; + serial_in_buffer[6] = 0; + for (uint32_t i = 0; i < 7; i++) { + if ((i > 1) && (i < 6)) { serial_in_buffer[6] += serial_in_buffer[i]; } + Serial.write(serial_in_buffer[i]); + } +} + +bool SonoffIfanSerialInput(void) +{ + if (SONOFF_IFAN03 == my_module_type) { + if (0xAA == serial_in_byte) { + serial_in_byte_counter = 0; + ifan_receive_flag = true; + } + if (ifan_receive_flag) { + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + if (serial_in_byte_counter == 8) { +# 176 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_22_sonoff_ifan.ino" + AddLogSerial(LOG_LEVEL_DEBUG); + uint8_t crc = 0; + for (uint32_t i = 2; i < 7; i++) { + crc += serial_in_buffer[i]; + } + if (crc == serial_in_buffer[7]) { + SonoffIfanReceived(); + ifan_receive_flag = false; + return true; + } + } + serial_in_byte = 0; + } + return false; + } +} + + + + + +void CmndFanspeed(void) +{ + if (XdrvMailbox.data_len > 0) { + if ('-' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (int16_t)GetFanspeed() -1; + if (XdrvMailbox.payload < 0) { XdrvMailbox.payload = MAX_FAN_SPEED -1; } + } + else if ('+' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = GetFanspeed() +1; + if (XdrvMailbox.payload > MAX_FAN_SPEED -1) { XdrvMailbox.payload = 0; } + } + } + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_FAN_SPEED)) { + SonoffIFanSetFanspeed(XdrvMailbox.payload, true); + } + ResponseCmndNumber(GetFanspeed()); +} + + + +bool SonoffIfanInit(void) +{ + if (SONOFF_IFAN03 == my_module_type) { + SetSerial(9600, TS_SERIAL_8N1); + } + return false; +} + +void SonoffIfanUpdate(void) +{ + if (SONOFF_IFAN03 == my_module_type) { + if (ifan_fanspeed_timer) { + ifan_fanspeed_timer--; + if (!ifan_fanspeed_timer) { + SonoffIFanSetFanspeed(ifan_fanspeed_goal, false); + } + } + } + + if (ifan_restart_flag && (4 == uptime) && (SONOFF_IFAN02 == my_module_type)) { + ifan_restart_flag = false; + SetDevicePower(1, SRC_RETRY); + SetDevicePower(power, SRC_RETRY); + } +} + + + + + +bool Xdrv22(uint8_t function) +{ + bool result = false; + + if (IsModuleIfan()) { + switch (function) { + case FUNC_EVERY_250_MSECOND: + SonoffIfanUpdate(); + break; + case FUNC_SERIAL: + result = SonoffIfanSerialInput(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kSonoffIfanCommands, SonoffIfanCommand); + break; + case FUNC_MODULE_INIT: + result = SonoffIfanInit(); + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_0_constants.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_0_constants.ino" +#ifdef USE_ZIGBEE + +#define OCCUPANCY "Occupancy" + +typedef uint64_t Z_IEEEAddress; +typedef uint16_t Z_ShortAddress; + +enum ZnpCommandType { + Z_POLL = 0x00, + Z_SREQ = 0x20, + Z_AREQ = 0x40, + Z_SRSP = 0x60 +}; + +enum ZnpSubsystem { + Z_RPC_Error = 0x00, + Z_SYS = 0x01, + Z_MAC = 0x02, + Z_NWK = 0x03, + Z_AF = 0x04, + Z_ZDO = 0x05, + Z_SAPI = 0x06, + Z_UTIL = 0x07, + Z_DEBUG = 0x08, + Z_APP = 0x09 +}; + + +enum SysCommand { + SYS_RESET = 0x00, + SYS_PING = 0x01, + SYS_VERSION = 0x02, + SYS_SET_EXTADDR = 0x03, + SYS_GET_EXTADDR = 0x04, + SYS_RAM_READ = 0x05, + SYS_RAM_WRITE = 0x06, + SYS_OSAL_NV_ITEM_INIT = 0x07, + SYS_OSAL_NV_READ = 0x08, + SYS_OSAL_NV_WRITE = 0x09, + SYS_OSAL_START_TIMER = 0x0A, + SYS_OSAL_STOP_TIMER = 0x0B, + SYS_RANDOM = 0x0C, + SYS_ADC_READ = 0x0D, + SYS_GPIO = 0x0E, + SYS_STACK_TUNE = 0x0F, + SYS_SET_TIME = 0x10, + SYS_GET_TIME = 0x11, + SYS_OSAL_NV_DELETE = 0x12, + SYS_OSAL_NV_LENGTH = 0x13, + SYS_TEST_RF = 0x40, + SYS_TEST_LOOPBACK = 0x41, + SYS_RESET_IND = 0x80, + SYS_OSAL_TIMER_EXPIRED = 0x81, +}; + +enum SapiCommand { + SAPI_START_REQUEST = 0x00, + SAPI_BIND_DEVICE = 0x01, + SAPI_ALLOW_BIND = 0x02, + SAPI_SEND_DATA_REQUEST = 0x03, + SAPI_READ_CONFIGURATION = 0x04, + SAPI_WRITE_CONFIGURATION = 0x05, + SAPI_GET_DEVICE_INFO = 0x06, + SAPI_FIND_DEVICE_REQUEST = 0x07, + SAPI_PERMIT_JOINING_REQUEST = 0x08, + SAPI_SYSTEM_RESET = 0x09, + SAPI_START_CONFIRM = 0x80, + SAPI_BIND_CONFIRM = 0x81, + SAPI_ALLOW_BIND_CONFIRM = 0x82, + SAPI_SEND_DATA_CONFIRM = 0x83, + SAPI_FIND_DEVICE_CONFIRM = 0x85, + SAPI_RECEIVE_DATA_INDICATION = 0x87, +}; +enum Z_configuration { + CONF_EXTADDR = 0x01, + CONF_BOOTCOUNTER = 0x02, + CONF_STARTUP_OPTION = 0x03, + CONF_START_DELAY = 0x04, + CONF_NIB = 0x21, + CONF_DEVICE_LIST = 0x22, + CONF_ADDRMGR = 0x23, + CONF_POLL_RATE = 0x24, + CONF_QUEUED_POLL_RATE = 0x25, + CONF_RESPONSE_POLL_RATE = 0x26, + CONF_REJOIN_POLL_RATE = 0x27, + CONF_DATA_RETRIES = 0x28, + CONF_POLL_FAILURE_RETRIES = 0x29, + CONF_STACK_PROFILE = 0x2A, + CONF_INDIRECT_MSG_TIMEOUT = 0x2B, + CONF_ROUTE_EXPIRY_TIME = 0x2C, + CONF_EXTENDED_PAN_ID = 0x2D, + CONF_BCAST_RETRIES = 0x2E, + CONF_PASSIVE_ACK_TIMEOUT = 0x2F, + CONF_BCAST_DELIVERY_TIME = 0x30, + CONF_NWK_MODE = 0x31, + CONF_CONCENTRATOR_ENABLE = 0x32, + CONF_CONCENTRATOR_DISCOVERY = 0x33, + CONF_CONCENTRATOR_RADIUS = 0x34, + CONF_CONCENTRATOR_RC = 0x36, + CONF_NWK_MGR_MODE = 0x37, + CONF_SRC_RTG_EXPIRY_TIME = 0x38, + CONF_ROUTE_DISCOVERY_TIME = 0x39, + CONF_NWK_ACTIVE_KEY_INFO = 0x3A, + CONF_NWK_ALTERN_KEY_INFO = 0x3B, + CONF_ROUTER_OFF_ASSOC_CLEANUP = 0x3C, + CONF_NWK_LEAVE_REQ_ALLOWED = 0x3D, + CONF_NWK_CHILD_AGE_ENABLE = 0x3E, + CONF_DEVICE_LIST_KA_TIMEOUT = 0x3F, + CONF_BINDING_TABLE = 0x41, + CONF_GROUP_TABLE = 0x42, + CONF_APS_FRAME_RETRIES = 0x43, + CONF_APS_ACK_WAIT_DURATION = 0x44, + CONF_APS_ACK_WAIT_MULTIPLIER = 0x45, + CONF_BINDING_TIME = 0x46, + CONF_APS_USE_EXT_PANID = 0x47, + CONF_APS_USE_INSECURE_JOIN = 0x48, + CONF_COMMISSIONED_NWK_ADDR = 0x49, + CONF_APS_NONMEMBER_RADIUS = 0x4B, + CONF_APS_LINK_KEY_TABLE = 0x4C, + CONF_APS_DUPREJ_TIMEOUT_INC = 0x4D, + CONF_APS_DUPREJ_TIMEOUT_COUNT = 0x4E, + CONF_APS_DUPREJ_TABLE_SIZE = 0x4F, + CONF_DIAGNOSTIC_STATS = 0x50, + CONF_SECURITY_LEVEL = 0x61, + CONF_PRECFGKEY = 0x62, + CONF_PRECFGKEYS_ENABLE = 0x63, + CONF_SECURITY_MODE = 0x64, + CONF_SECURE_PERMIT_JOIN = 0x65, + CONF_APS_LINK_KEY_TYPE = 0x66, + CONF_APS_ALLOW_R19_SECURITY = 0x67, + CONF_IMPLICIT_CERTIFICATE = 0x69, + CONF_DEVICE_PRIVATE_KEY = 0x6A, + CONF_CA_PUBLIC_KEY = 0x6B, + CONF_KE_MAX_DEVICES = 0x6C, + CONF_USE_DEFAULT_TCLK = 0x6D, + CONF_RNG_COUNTER = 0x6F, + CONF_RANDOM_SEED = 0x70, + CONF_TRUSTCENTER_ADDR = 0x71, + CONF_USERDESC = 0x81, + CONF_NWKKEY = 0x82, + CONF_PANID = 0x83, + CONF_CHANLIST = 0x84, + CONF_LEAVE_CTRL = 0x85, + CONF_SCAN_DURATION = 0x86, + CONF_LOGICAL_TYPE = 0x87, + CONF_NWKMGR_MIN_TX = 0x88, + CONF_NWKMGR_ADDR = 0x89, + CONF_ZDO_DIRECT_CB = 0x8F, + CONF_TCLK_TABLE_START = 0x0101, + ZNP_HAS_CONFIGURED = 0xF00 +}; + + +enum Z_Status { + Z_SUCCESS = 0x00, + Z_FAILURE = 0x01, + Z_INVALIDPARAMETER = 0x02, + Z_MEMERROR = 0x03, + Z_CREATED = 0x09, + Z_BUFFERFULL = 0x11 +}; + +enum Z_App_Profiles { + Z_PROF_IPM = 0x0101, + Z_PROF_HA = 0x0104, + Z_PROF_CBA = 0x0105, + Z_PROF_TA = 0x0107, + Z_PROF_PHHC = 0x0108, + Z_PROF_AMI = 0x0109, +}; + +enum Z_Device_Ids { + Z_DEVID_CONF_TOOL = 0x0005, +# 225 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_0_constants.ino" +}; + + enum Z_AddrMode : uint8_t { + Z_Addr_NotPresent = 0, + Z_Addr_Group = 1, + Z_Addr_ShortAddress = 2, + Z_Addr_IEEEAddress = 3, + Z_Addr_Broadcast = 0xFF +}; + + +enum AfCommand : uint8_t { + AF_REGISTER = 0x00, + AF_DATA_REQUEST = 0x01, + AF_DATA_REQUEST_EXT = 0x02, + AF_DATA_REQUEST_SRC_RTG = 0x03, + AF_INTER_PAN_CTL = 0x10, + AF_DATA_STORE = 0x11, + AF_DATA_RETRIEVE = 0x12, + AF_APSF_CONFIG_SET = 0x13, + AF_DATA_CONFIRM = 0x80, + AF_REFLECT_ERROR = 0x83, + AF_INCOMING_MSG = 0x81, + AF_INCOMING_MSG_EXT = 0x82 +}; + + +enum : uint8_t { + ZDO_NWK_ADDR_REQ = 0x00, + ZDO_IEEE_ADDR_REQ = 0x01, + ZDO_NODE_DESC_REQ = 0x02, + ZDO_POWER_DESC_REQ = 0x03, + ZDO_SIMPLE_DESC_REQ = 0x04, + ZDO_ACTIVE_EP_REQ = 0x05, + ZDO_MATCH_DESC_REQ = 0x06, + ZDO_COMPLEX_DESC_REQ = 0x07, + ZDO_USER_DESC_REQ = 0x08, + ZDO_DEVICE_ANNCE = 0x0A, + ZDO_USER_DESC_SET = 0x0B, + ZDO_SERVER_DISC_REQ = 0x0C, + ZDO_END_DEVICE_BIND_REQ = 0x20, + ZDO_BIND_REQ = 0x21, + ZDO_UNBIND_REQ = 0x22, + ZDO_SET_LINK_KEY = 0x23, + ZDO_REMOVE_LINK_KEY = 0x24, + ZDO_GET_LINK_KEY = 0x25, + ZDO_MGMT_NWK_DISC_REQ = 0x30, + ZDO_MGMT_LQI_REQ = 0x31, + ZDO_MGMT_RTQ_REQ = 0x32, + ZDO_MGMT_BIND_REQ = 0x33, + ZDO_MGMT_LEAVE_REQ = 0x34, + ZDO_MGMT_DIRECT_JOIN_REQ = 0x35, + ZDO_MGMT_PERMIT_JOIN_REQ = 0x36, + ZDO_MGMT_NWK_UPDATE_REQ = 0x37, + ZDO_MSG_CB_REGISTER = 0x3E, + ZDO_MGS_CB_REMOVE = 0x3F, + ZDO_STARTUP_FROM_APP = 0x40, + ZDO_AUTO_FIND_DESTINATION = 0x41, + ZDO_EXT_REMOVE_GROUP = 0x47, + ZDO_EXT_REMOVE_ALL_GROUP = 0x48, + ZDO_EXT_FIND_ALL_GROUPS_ENDPOINT = 0x49, + ZDO_EXT_FIND_GROUP = 0x4A, + ZDO_EXT_ADD_GROUP = 0x4B, + ZDO_EXT_COUNT_ALL_GROUPS = 0x4C, + ZDO_NWK_ADDR_RSP = 0x80, + ZDO_IEEE_ADDR_RSP = 0x81, + ZDO_NODE_DESC_RSP = 0x82, + ZDO_POWER_DESC_RSP = 0x83, + ZDO_SIMPLE_DESC_RSP = 0x84, + ZDO_ACTIVE_EP_RSP = 0x85, + ZDO_MATCH_DESC_RSP = 0x86, + ZDO_COMPLEX_DESC_RSP = 0x87, + ZDO_USER_DESC_RSP = 0x88, + ZDO_USER_DESC_CONF = 0x89, + ZDO_SERVER_DISC_RSP = 0x8A, + ZDO_END_DEVICE_BIND_RSP = 0xA0, + ZDO_BIND_RSP = 0xA1, + ZDO_UNBIND_RSP = 0xA2, + ZDO_MGMT_NWK_DISC_RSP = 0xB0, + ZDO_MGMT_LQI_RSP = 0xB1, + ZDO_MGMT_RTG_RSP = 0xB2, + ZDO_MGMT_BIND_RSP = 0xB3, + ZDO_MGMT_LEAVE_RSP = 0xB4, + ZDO_MGMT_DIRECT_JOIN_RSP = 0xB5, + ZDO_MGMT_PERMIT_JOIN_RSP = 0xB6, + ZDO_STATE_CHANGE_IND = 0xC0, + ZDO_END_DEVICE_ANNCE_IND = 0xC1, + ZDO_MATCH_DESC_RSP_SENT = 0xC2, + ZDO_STATUS_ERROR_RSP = 0xC3, + ZDO_SRC_RTG_IND = 0xC4, + ZDO_LEAVE_IND = 0xC9, + ZDO_TC_DEV_IND = 0xCA, + ZDO_PERMIT_JOIN_IND = 0xCB, + ZDO_MSG_CB_INCOMING = 0xFF +}; + + +enum ZdoStates { + ZDO_DEV_HOLD = 0x00, + ZDO_DEV_INIT = 0x01, + ZDO_DEV_NWK_DISC = 0x02, + ZDO_DEV_NWK_JOINING = 0x03, + ZDO_DEV_NWK_REJOIN = 0x04, + ZDO_DEV_END_DEVICE_UNAUTH = 0x05, + ZDO_DEV_END_DEVICE = 0x06, + ZDO_DEV_ROUTER = 0x07, + ZDO_DEV_COORD_STARTING = 0x08, + ZDO_DEV_ZB_COORD = 0x09, + ZDO_DEV_NWK_ORPHAN = 0x0A, +}; + + +enum Z_Util { + Z_UTIL_GET_DEVICE_INFO = 0x00, + Z_UTIL_GET_NV_INFO = 0x01, + Z_UTIL_SET_PANID = 0x02, + Z_UTIL_SET_CHANNELS = 0x03, + Z_UTIL_SET_SECLEVEL = 0x04, + Z_UTIL_SET_PRECFGKEY = 0x05, + Z_UTIL_CALLBACK_SUB_CMD = 0x06, + Z_UTIL_KEY_EVENT = 0x07, + Z_UTIL_TIME_ALIVE = 0x09, + Z_UTIL_LED_CONTROL = 0x0A, + Z_UTIL_TEST_LOOPBACK = 0x10, + Z_UTIL_DATA_REQ = 0x11, + Z_UTIL_SRC_MATCH_ENABLE = 0x20, + Z_UTIL_SRC_MATCH_ADD_ENTRY = 0x21, + Z_UTIL_SRC_MATCH_DEL_ENTRY = 0x22, + Z_UTIL_SRC_MATCH_CHECK_SRC_ADDR = 0x23, + Z_UTIL_SRC_MATCH_ACK_ALL_PENDING = 0x24, + Z_UTIL_SRC_MATCH_CHECK_ALL_PENDING = 0x25, + Z_UTIL_ADDRMGR_EXT_ADDR_LOOKUP = 0x40, + Z_UTIL_ADDRMGR_NWK_ADDR_LOOKUP = 0x41, + Z_UTIL_APSME_LINK_KEY_DATA_GET = 0x44, + Z_UTIL_APSME_LINK_KEY_NV_ID_GET = 0x45, + Z_UTIL_ASSOC_COUNT = 0x48, + Z_UTIL_ASSOC_FIND_DEVICE = 0x49, + Z_UTIL_ASSOC_GET_WITH_ADDRESS = 0x4A, + Z_UTIL_APSME_REQUEST_KEY_CMD = 0x4B, + Z_UTIL_ZCL_KEY_EST_INIT_EST = 0x80, + Z_UTIL_ZCL_KEY_EST_SIGN = 0x81, + Z_UTIL_UTIL_SYNC_REQ = 0xE0, + Z_UTIL_ZCL_KEY_ESTABLISH_IND = 0xE1 +}; + +enum ZCL_Global_Commands { + ZCL_READ_ATTRIBUTES = 0x00, + ZCL_READ_ATTRIBUTES_RESPONSE = 0x01, + ZCL_WRITE_ATTRIBUTES = 0x02, + ZCL_WRITE_ATTRIBUTES_UNDIVIDED = 0x03, + ZCL_WRITE_ATTRIBUTES_RESPONSE = 0x04, + ZCL_WRITE_ATTRIBUTES_NORESPONSE = 0x05, + ZCL_CONFIGURE_REPORTING = 0x06, + ZCL_CONFIGURE_REPORTING_RESPONSE = 0x07, + ZCL_READ_REPORTING_CONFIGURATION = 0x08, + ZCL_READ_REPORTING_CONFIGURATION_RESPONSE = 0x09, + ZCL_REPORT_ATTRIBUTES = 0x0a, + ZCL_DEFAULT_RESPONSE = 0x0b, + ZCL_DISCOVER_ATTRIBUTES = 0x0c, + ZCL_DISCOVER_ATTRIBUTES_RESPONSE = 0x0d + +}; + +#define ZF(s) static const char ZS_ ## s[] PROGMEM = #s; +#define Z(s) ZS_ ## s + +typedef struct Z_StatusLine { + uint32_t status; + const char * status_msg; +} Z_StatusLine; + + +String getZigbeeStatusMessage(uint8_t status) { + static const char StatusMsg[] PROGMEM = "SUCCESS|FAILURE|NOT_AUTHORIZED|RESERVED_FIELD_NOT_ZERO|MALFORMED_COMMAND|UNSUP_CLUSTER_COMMAND|UNSUP_GENERAL_COMMAND" + "|UNSUP_MANUF_CLUSTER_COMMAND|UNSUP_MANUF_GENERAL_COMMAND|INVALID_FIELD|UNSUPPORTED_ATTRIBUTE|INVALID_VALE|READ_ONLY" + "|INSUFFICIENT_SPACE|DUPLICATE_EXISTS|NOT_FOUND|UNREPORTABLE_ATTRIBUTE|INVALID_DATA_TYPE|INVALID_SELECTOR|WRITE_ONLY" + "|INCONSISTENT_STARTUP_STATE|DEFINED_OUT_OF_BAND|INCONSISTENT|ACTION_DENIED|TIMEOUT|ABORT|INVALID_IMAGE|WAIT_FOR_DATA" + "|NO_IMAGE_AVAILABLE|REQUIRE_MORE_IMAGE|NOTIFICATION_PENDING|HARDWARE_FAILURE|SOFTWARE_FAILURE|CALIBRATION_ERROR|UNSUPPORTED_CLUSTER|NO_ROUTE" + "|CHANNEL_ACCESS_FAILURE|NO_ACK|NO_APP_ACK|NO_ROUTE" + ; + static const uint8_t StatusIdx[] PROGMEM = { 0x00, 0x01, 0x7E, 0x7F, 0x80, 0x81, 0x82, + 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, + 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9A, 0xC0, 0xC1, 0xC2, 0xC3, 0xCD, + 0xE1, 0xE9, 0xA7, 0xD0}; + + char msg[32]; + int32_t idx = -1; + for (uint32_t i = 0; i < sizeof(StatusIdx); i++) { + if (status == pgm_read_byte(&StatusIdx[i])) { + idx = i; + break; + } + } + if (idx >= 0) { + GetTextIndexed(msg, sizeof(msg), idx, StatusMsg); + } else { + *msg = 0x00; + } + return String(msg); +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_1_headers.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_1_headers.ino" +#ifdef USE_ZIGBEE + + + +void ZigbeeZCLSend_Raw(uint16_t dtsAddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId); + + + +const JsonVariant &getCaseInsensitive(const JsonObject &json, const char *needle) { + + if ((nullptr == &json) || (nullptr == needle) || (0 == pgm_read_byte(needle))) { + return *(JsonVariant*)nullptr; + } + + for (JsonObject::const_iterator it=json.begin(); it!=json.end(); ++it) { + const char *key = it->key; + const JsonVariant &value = it->value; + + if (0 == strcasecmp_P(key, needle)) { + return value; + } + } + + return *(JsonVariant*)nullptr; +} + + +const char * getCaseInsensitiveConstCharNull(const JsonObject &json, const char *needle) { + const JsonVariant &val = getCaseInsensitive(json, needle); + if (&val) { + const char *val_cs = val.as(); + if (strlen(val_cs)) { + return val_cs; + } + } + return nullptr; +} + + +JsonVariant &startsWithCaseInsensitive(const JsonObject &json, const char *needle) { + + if ((nullptr == &json) || (nullptr == needle) || (0 == pgm_read_byte(needle))) { + return *(JsonVariant*)nullptr; + } + + String needle_s(needle); + needle_s.toLowerCase(); + + for (auto kv : json) { + String key_s(kv.key); + key_s.toLowerCase(); + JsonVariant &value = kv.value; + + if (key_s.startsWith(needle_s)) { + return value; + } + } + + return *(JsonVariant*)nullptr; +} + + +uint32_t parseHex(const char **data, size_t max_len = 8) { + uint32_t ret = 0; + for (uint32_t i = 0; i < max_len; i++) { + int8_t v = hexValue(**data); + if (v < 0) { break; } + ret = (ret << 4) | v; + *data += 1; + } + return ret; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" +#ifdef USE_ZIGBEE + +#include + +#ifndef ZIGBEE_SAVE_DELAY_SECONDS +#define ZIGBEE_SAVE_DELAY_SECONDS 2; +#endif +const uint16_t kZigbeeSaveDelaySeconds = ZIGBEE_SAVE_DELAY_SECONDS; + + + + + +const size_t endpoints_max = 8; + +typedef struct Z_Device { + uint64_t longaddr; + char * manufacturerId; + char * modelId; + char * friendlyName; + uint8_t endpoints[endpoints_max]; + + DynamicJsonBuffer *json_buffer; + JsonObject *json; + + uint16_t shortaddr; + uint8_t seqNumber; + + int8_t bulbtype; + uint8_t power; + uint8_t colormode; + uint8_t dimmer; + uint8_t sat; + uint16_t ct; + uint16_t hue; + uint16_t x, y; +} Z_Device; + + + + + +typedef int32_t (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); + + +typedef enum Z_Def_Category { + Z_CAT_NONE = 0, + Z_CAT_READ_ATTR, + Z_CAT_VIRTUAL_OCCUPANCY, + Z_CAT_REACHABILITY, + Z_CAT_READ_0006, + Z_CAT_READ_0008, + Z_CAT_READ_0102, + Z_CAT_READ_0300, +} Z_Def_Category; + +const uint32_t Z_CAT_REACHABILITY_TIMEOUT = 1000; + +typedef struct Z_Deferred { + + uint32_t timer; + uint16_t shortaddr; + uint16_t groupaddr; + uint16_t cluster; + uint8_t endpoint; + uint8_t category; + uint32_t value; + Z_DeviceTimer func; +} Z_Deferred; +# 99 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" +class Z_Devices { +public: + Z_Devices() {}; + + + + + + + uint16_t isKnownShortAddr(uint16_t shortaddr) const; + uint16_t isKnownLongAddr(uint64_t longaddr) const; + uint16_t isKnownIndex(uint32_t index) const; + uint16_t isKnownFriendlyName(const char * name) const; + + uint64_t getDeviceLongAddr(uint16_t shortaddr) const; + + uint8_t findFirstEndpoint(uint16_t shortaddr) const; + + + + void updateDevice(uint16_t shortaddr, uint64_t longaddr = 0); + + + void addEndpoint(uint16_t shortaddr, uint8_t endpoint); + void clearEndpoints(uint16_t shortaddr); + + void setManufId(uint16_t shortaddr, const char * str); + void setModelId(uint16_t shortaddr, const char * str); + void setFriendlyName(uint16_t shortaddr, const char * str); + const char * getFriendlyName(uint16_t shortaddr) const; + const char * getModelId(uint16_t shortaddr) const; + void setReachable(uint16_t shortaddr, bool reachable); + + + uint8_t getNextSeqNumber(uint16_t shortaddr); + + + String dumpLightState(uint16_t shortaddr) const; + String dump(uint32_t dump_mode, uint16_t status_shortaddr = 0) const; + int32_t deviceRestore(const JsonObject &json); + + + void setHueBulbtype(uint16_t shortaddr, int8_t bulbtype); + int8_t getHueBulbtype(uint16_t shortaddr) const ; + void updateHueState(uint16_t shortaddr, + const bool *power, const uint8_t *colormode, + const uint8_t *dimmer, const uint8_t *sat, + const uint16_t *ct, const uint16_t *hue, + const uint16_t *x, const uint16_t *y, + const bool *reachable); + bool getHueState(uint16_t shortaddr, + bool *power, uint8_t *colormode, + uint8_t *dimmer, uint8_t *sat, + uint16_t *ct, uint16_t *hue, + uint16_t *x, uint16_t *y, + bool *reachable) const ; + + + void resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uint8_t category); + void setTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func); + void runTimer(void); + + + void jsonClear(uint16_t shortaddr); + void jsonAppend(uint16_t shortaddr, const JsonObject &values); + const JsonObject *jsonGet(uint16_t shortaddr); + void jsonPublishFlush(uint16_t shortaddr); + bool jsonIsConflict(uint16_t shortaddr, const JsonObject &values); + void jsonPublishNow(uint16_t shortaddr, JsonObject &values); + + + size_t devicesSize(void) const { + return _devices.size(); + } + const Z_Device &devicesAt(size_t i) const { + return *(_devices.at(i)); + } + + + bool removeDevice(uint16_t shortaddr); + + + void dirty(void); + void clean(void); + void shrinkToFit(uint16_t shortaddr); + + + uint16_t parseDeviceParam(const char * param, bool short_must_be_known = false) const; + +private: + std::vector _devices = {}; + std::vector _deferred = {}; + uint32_t _saveTimer = 0; + uint8_t _seqNumber = 0; + + template < typename T> + static bool findInVector(const std::vector & vecOfElements, const T & element); + + template < typename T> + static int32_t findEndpointInVector(const std::vector & vecOfElements, uint8_t element); + + Z_Device & getShortAddr(uint16_t shortaddr); + const Z_Device & getShortAddrConst(uint16_t shortaddr) const ; + Z_Device & getLongAddr(uint64_t longaddr); + + int32_t findShortAddr(uint16_t shortaddr) const; + int32_t findLongAddr(uint64_t longaddr) const; + int32_t findFriendlyName(const char * name) const; + + + Z_Device & createDeviceEntry(uint16_t shortaddr, uint64_t longaddr = 0); + void freeDeviceEntry(Z_Device *device); + + void setStringAttribute(char*& attr, const char * str); +}; + + + + +Z_Devices zigbee_devices = Z_Devices(); + + +uint64_t localIEEEAddr = 0; + + + + + + +template < typename T> +bool Z_Devices::findInVector(const std::vector & vecOfElements, const T & element) { + + auto it = std::find(vecOfElements.begin(), vecOfElements.end(), element); + + if (it != vecOfElements.end()) { + return true; + } else { + return false; + } +} + +template < typename T> +int32_t Z_Devices::findEndpointInVector(const std::vector & vecOfElements, uint8_t element) { + + + int32_t found = 0; + for (auto &elem : vecOfElements) { + if (elem == element) { return found; } + found++; + } + + return -1; +} + + + + + +Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) { + if (!shortaddr && !longaddr) { return *(Z_Device*) nullptr; } + + Z_Device* device_alloc = new Z_Device{ + longaddr, + nullptr, + nullptr, + nullptr, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + nullptr, nullptr, + shortaddr, + 0, + + -1, + 0x80, + 0, + 0, + 0, + 200, + 0, + 0, 0, + }; + + device_alloc->json_buffer = new DynamicJsonBuffer(16); + _devices.push_back(device_alloc); + dirty(); + return *(_devices.back()); +} + +void Z_Devices::freeDeviceEntry(Z_Device *device) { + if (device->manufacturerId) { free(device->manufacturerId); } + if (device->modelId) { free(device->modelId); } + if (device->friendlyName) { free(device->friendlyName); } + free(device); +} +# 301 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" +int32_t Z_Devices::findShortAddr(uint16_t shortaddr) const { + if (!shortaddr) { return -1; } + int32_t found = 0; + if (shortaddr) { + for (auto &elem : _devices) { + if (elem->shortaddr == shortaddr) { return found; } + found++; + } + } + return -1; +} +# 320 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" +int32_t Z_Devices::findLongAddr(uint64_t longaddr) const { + if (!longaddr) { return -1; } + int32_t found = 0; + if (longaddr) { + for (auto &elem : _devices) { + if (elem->longaddr == longaddr) { return found; } + found++; + } + } + return -1; +} +# 339 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" +int32_t Z_Devices::findFriendlyName(const char * name) const { + if (!name) { return -1; } + size_t name_len = strlen(name); + int32_t found = 0; + if (name_len) { + for (auto &elem : _devices) { + if (elem->friendlyName) { + if (strcmp(elem->friendlyName, name) == 0) { return found; } + } + found++; + } + } + return -1; +} + + +uint16_t Z_Devices::isKnownShortAddr(uint16_t shortaddr) const { + int32_t found = findShortAddr(shortaddr); + if (found >= 0) { + return shortaddr; + } else { + return 0; + } +} + +uint16_t Z_Devices::isKnownLongAddr(uint64_t longaddr) const { + int32_t found = findLongAddr(longaddr); + if (found >= 0) { + const Z_Device & device = devicesAt(found); + return device.shortaddr; + } else { + return 0; + } +} + +uint16_t Z_Devices::isKnownIndex(uint32_t index) const { + if (index < devicesSize()) { + const Z_Device & device = devicesAt(index); + return device.shortaddr; + } else { + return 0; + } +} + +uint16_t Z_Devices::isKnownFriendlyName(const char * name) const { + if ((!name) || (0 == strlen(name))) { return 0xFFFF; } + int32_t found = findFriendlyName(name); + if (found >= 0) { + const Z_Device & device = devicesAt(found); + return device.shortaddr; + } else { + return 0; + } +} + +uint64_t Z_Devices::getDeviceLongAddr(uint16_t shortaddr) const { + const Z_Device & device = getShortAddrConst(shortaddr); + return device.longaddr; +} + + + + +Z_Device & Z_Devices::getShortAddr(uint16_t shortaddr) { + if (!shortaddr) { return *(Z_Device*) nullptr; } + int32_t found = findShortAddr(shortaddr); + if (found >= 0) { + return *(_devices[found]); + } + + return createDeviceEntry(shortaddr, 0); +} + +const Z_Device & Z_Devices::getShortAddrConst(uint16_t shortaddr) const { + if (!shortaddr) { return *(Z_Device*) nullptr; } + int32_t found = findShortAddr(shortaddr); + if (found >= 0) { + return *(_devices[found]); + } + return *((Z_Device*)nullptr); +} + + +Z_Device & Z_Devices::getLongAddr(uint64_t longaddr) { + if (!longaddr) { return *(Z_Device*) nullptr; } + int32_t found = findLongAddr(longaddr); + if (found > 0) { + return *(_devices[found]); + } + return createDeviceEntry(0, longaddr); +} + + +bool Z_Devices::removeDevice(uint16_t shortaddr) { + int32_t found = findShortAddr(shortaddr); + if (found >= 0) { + freeDeviceEntry(_devices.at(found)); + _devices.erase(_devices.begin() + found); + dirty(); + return true; + } + return false; +} + + + + + + +void Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) { + int32_t s_found = findShortAddr(shortaddr); + int32_t l_found = findLongAddr(longaddr); + + if ((s_found >= 0) && (l_found >= 0)) { + if (s_found == l_found) { + } else { + + _devices[l_found]->shortaddr = shortaddr; + + freeDeviceEntry(_devices.at(s_found)); + _devices.erase(_devices.begin() + s_found); + dirty(); + } + } else if (s_found >= 0) { + + + _devices[s_found]->longaddr = longaddr; + dirty(); + } else if (l_found >= 0) { + + _devices[l_found]->shortaddr = shortaddr; + dirty(); + } else { + + if (shortaddr || longaddr) { + createDeviceEntry(shortaddr, longaddr); + } + } +} + + + + +void Z_Devices::clearEndpoints(uint16_t shortaddr) { + if (!shortaddr) { return; } + Z_Device &device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + + for (uint32_t i = 0; i < endpoints_max; i++) { + device.endpoints[i] = 0; + + } +} + + + + +void Z_Devices::addEndpoint(uint16_t shortaddr, uint8_t endpoint) { + if (!shortaddr) { return; } + if (0x00 == endpoint) { return; } + Z_Device &device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + + for (uint32_t i = 0; i < endpoints_max; i++) { + if (endpoint == device.endpoints[i]) { + return; + } + if (0 == device.endpoints[i]) { + device.endpoints[i] = endpoint; + dirty(); + return; + } + } +} + + +uint8_t Z_Devices::findFirstEndpoint(uint16_t shortaddr) const { + int32_t found = findShortAddr(shortaddr); + if (found < 0) return 0; + const Z_Device &device = devicesAt(found); + + return device.endpoints[0]; +} + +void Z_Devices::setStringAttribute(char*& attr, const char * str) { + size_t str_len = str ? strlen(str) : 0; + + if ((nullptr == attr) && (0 == str_len)) { return; } + if (attr) { + + if (strcmp(attr, str) != 0) { + + free(attr); + attr = nullptr; + } else { + return; + } + } + if (str_len) { + attr = (char*) malloc(str_len + 1); + strlcpy(attr, str, str_len + 1); + } + dirty(); +} +# 553 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" +void Z_Devices::setManufId(uint16_t shortaddr, const char * str) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + + setStringAttribute(device.manufacturerId, str); +} + +void Z_Devices::setModelId(uint16_t shortaddr, const char * str) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + + setStringAttribute(device.modelId, str); +} + +void Z_Devices::setFriendlyName(uint16_t shortaddr, const char * str) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + + setStringAttribute(device.friendlyName, str); +} + +const char * Z_Devices::getFriendlyName(uint16_t shortaddr) const { + int32_t found = findShortAddr(shortaddr); + if (found >= 0) { + const Z_Device & device = devicesAt(found); + return device.friendlyName; + } + return nullptr; +} + +const char * Z_Devices::getModelId(uint16_t shortaddr) const { + int32_t found = findShortAddr(shortaddr); + if (found >= 0) { + const Z_Device & device = devicesAt(found); + return device.modelId; + } + return nullptr; +} + +void Z_Devices::setReachable(uint16_t shortaddr, bool reachable) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + bitWrite(device.power, 7, reachable); +} + + +uint8_t Z_Devices::getNextSeqNumber(uint16_t shortaddr) { + int32_t short_found = findShortAddr(shortaddr); + if (short_found >= 0) { + Z_Device &device = getShortAddr(shortaddr); + device.seqNumber += 1; + return device.seqNumber; + } else { + _seqNumber += 1; + return _seqNumber; + } +} + + + +void Z_Devices::setHueBulbtype(uint16_t shortaddr, int8_t bulbtype) { + Z_Device &device = getShortAddr(shortaddr); + if (bulbtype != device.bulbtype) { + device.bulbtype = bulbtype; + dirty(); + } +} +int8_t Z_Devices::getHueBulbtype(uint16_t shortaddr) const { + int32_t found = findShortAddr(shortaddr); + if (found >= 0) { + return _devices[found]->bulbtype; + } else { + return -1; + } +} + + +void Z_Devices::updateHueState(uint16_t shortaddr, + const bool *power, const uint8_t *colormode, + const uint8_t *dimmer, const uint8_t *sat, + const uint16_t *ct, const uint16_t *hue, + const uint16_t *x, const uint16_t *y, + const bool *reachable) { + Z_Device &device = getShortAddr(shortaddr); + if (power) { bitWrite(device.power, 0, *power); } + if (colormode){ device.colormode = *colormode; } + if (dimmer) { device.dimmer = *dimmer; } + if (sat) { device.sat = *sat; } + if (ct) { device.ct = *ct; } + if (hue) { device.hue = *hue; } + if (x) { device.x = *x; } + if (y) { device.y = *y; } + if (reachable){ bitWrite(device.power, 7, *reachable); } +} + + +bool Z_Devices::getHueState(uint16_t shortaddr, + bool *power, uint8_t *colormode, + uint8_t *dimmer, uint8_t *sat, + uint16_t *ct, uint16_t *hue, + uint16_t *x, uint16_t *y, + bool *reachable) const { + int32_t found = findShortAddr(shortaddr); + if (found >= 0) { + const Z_Device &device = *(_devices[found]); + if (power) { *power = bitRead(device.power, 0); } + if (colormode){ *colormode = device.colormode; } + if (dimmer) { *dimmer = device.dimmer; } + if (sat) { *sat = device.sat; } + if (ct) { *ct = device.ct; } + if (hue) { *hue = device.hue; } + if (x) { *x = device.x; } + if (y) { *y = device.y; } + if (reachable){ *reachable = bitRead(device.power, 7); } + return true; + } else { + return false; + } +} + + + +void Z_Devices::resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uint8_t category) { + + for (auto it = _deferred.begin(); it != _deferred.end(); it++) { + + + + if ((it->shortaddr == shortaddr) && (it->groupaddr == groupaddr)) { + if ((0xFF == category) || (it->category == category)) { + _deferred.erase(it--); + } + } + } +} + + +void Z_Devices::setTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func) { + + if (category) { + resetTimersForDevice(shortaddr, groupaddr, category); + } + + + Z_Deferred deferred = { wait_ms + millis(), + shortaddr, + groupaddr, + cluster, + endpoint, + category, + value, + func }; + _deferred.push_back(deferred); +} + + + +void Z_Devices::runTimer(void) { + + for (auto it = _deferred.begin(); it != _deferred.end(); it++) { + Z_Deferred &defer = *it; + + uint32_t timer = defer.timer; + if (TimeReached(timer)) { + (*defer.func)(defer.shortaddr, defer.groupaddr, defer.cluster, defer.endpoint, defer.value); + _deferred.erase(it--); + } + } + + + if ((_saveTimer) && TimeReached(_saveTimer)) { + saveZigbeeDevices(); + _saveTimer = 0; + } +} + + +void Z_Devices::jsonClear(uint16_t shortaddr) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + + device.json = nullptr; + device.json_buffer->clear(); +} + + +void CopyJsonVariant(JsonObject &to, const String &key, const JsonVariant &val) { + + to.remove(key); + + if (val.is()) { + String sval = val.as(); + to.set(key, sval); + } else if (val.is()) { + JsonArray &nested_arr = to.createNestedArray(key); + CopyJsonArray(nested_arr, val.as()); + } else if (val.is()) { + JsonObject &nested_obj = to.createNestedObject(key); + CopyJsonObject(nested_obj, val.as()); + } else { + to.set(key, val); + } +} + + +void CopyJsonArray(JsonArray &to, const JsonArray &arr) { + for (auto v : arr) { + if (v.is()) { + String sval = v.as(); + to.add(sval); + } else if (v.is()) { + } else if (v.is()) { + } else { + to.add(v); + } + } +} + + +void CopyJsonObject(JsonObject &to, const JsonObject &from) { + for (auto kv : from) { + String key_string = kv.key; + JsonVariant &val = kv.value; + + CopyJsonVariant(to, key_string, val); + } +} + + + + +bool Z_Devices::jsonIsConflict(uint16_t shortaddr, const JsonObject &values) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return false; } + if (&values == nullptr) { return false; } + + if (nullptr == device.json) { + return false; + } +# 800 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" + uint16_t group1 = device.json->get(D_CMND_ZIGBEE_GROUP); + uint16_t group2 = values.get(D_CMND_ZIGBEE_GROUP); + if (group1 != group2) { + return true; + } + + + for (auto kv : values) { + String key_string = kv.key; + + if (0 == strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_GROUP))) { + + } else if (0 == strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_ENDPOINT))) { + + if (device.json->containsKey(kv.key)) { + if (kv.value.as() != device.json->get(kv.key)) { + return true; + } + } + } else if (strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_LINKQUALITY))) { + if (device.json->containsKey(kv.key)) { + return true; + } + } + } + return false; +} + +void Z_Devices::jsonAppend(uint16_t shortaddr, const JsonObject &values) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + if (&values == nullptr) { return; } + + if (nullptr == device.json) { + device.json = &(device.json_buffer->createObject()); + } + + char sa[8]; + snprintf_P(sa, sizeof(sa), PSTR("0x%04X"), shortaddr); + device.json->set(F(D_JSON_ZIGBEE_DEVICE), sa); + + const char * fname = zigbee_devices.getFriendlyName(shortaddr); + if (fname) { + device.json->set(F(D_JSON_ZIGBEE_NAME), (char*) fname); + } + + + CopyJsonObject(*device.json, values); +} + +const JsonObject *Z_Devices::jsonGet(uint16_t shortaddr) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return nullptr; } + return device.json; +} + +void Z_Devices::jsonPublishFlush(uint16_t shortaddr) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + JsonObject * json = device.json; + if (json == nullptr) { return; } + + const char * fname = zigbee_devices.getFriendlyName(shortaddr); + bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); + + + if (use_fname) { + json->remove(F(D_JSON_ZIGBEE_NAME)); + } else { + json->remove(F(D_JSON_ZIGBEE_DEVICE)); + } + + String msg = ""; + json->printTo(msg); + zigbee_devices.jsonClear(shortaddr); + + if (use_fname) { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"%s\":%s}}"), fname, msg.c_str()); + } else { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str()); + } + if (Settings.flag4.zigbee_distinct_topics) { + char subtopic[16]; + snprintf_P(subtopic, sizeof(subtopic), PSTR("%04X/" D_RSLT_SENSOR), shortaddr); + MqttPublishPrefixTopic_P(TELE, subtopic, Settings.flag.mqtt_sensor_retain); + } else { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); + } + XdrvRulesProcess(); +} + +void Z_Devices::jsonPublishNow(uint16_t shortaddr, JsonObject & values) { + jsonPublishFlush(shortaddr); + jsonAppend(shortaddr, values); + jsonPublishFlush(shortaddr); +} + +void Z_Devices::dirty(void) { + _saveTimer = kZigbeeSaveDelaySeconds * 1000 + millis(); +} +void Z_Devices::clean(void) { + _saveTimer = 0; +} + + + + + + +uint16_t Z_Devices::parseDeviceParam(const char * param, bool short_must_be_known) const { + if (nullptr == param) { return 0; } + size_t param_len = strlen(param); + char dataBuf[param_len + 1]; + strcpy(dataBuf, param); + RemoveSpace(dataBuf); + uint16_t shortaddr = 0; + + if (strlen(dataBuf) < 4) { + + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 99)) { + shortaddr = zigbee_devices.isKnownIndex(XdrvMailbox.payload - 1); + } + } else if ((dataBuf[0] == '0') && (dataBuf[1] == 'x')) { + + if (strlen(dataBuf) < 18) { + + shortaddr = strtoull(dataBuf, nullptr, 0); + if (short_must_be_known) { + shortaddr = zigbee_devices.isKnownShortAddr(shortaddr); + } + + } else { + + uint64_t longaddr = strtoull(dataBuf, nullptr, 0); + shortaddr = zigbee_devices.isKnownLongAddr(longaddr); + } + } else { + + shortaddr = zigbee_devices.isKnownFriendlyName(dataBuf); + } + + return shortaddr; +} + + +String Z_Devices::dumpLightState(uint16_t shortaddr) const { + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.createObject(); + char hex[8]; + + int32_t found = findShortAddr(shortaddr); + if (found >= 0) { + const Z_Device & device = devicesAt(found); + const char * fname = getFriendlyName(shortaddr); + + bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); + + snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr); + + JsonObject& dev = use_fname ? json.createNestedObject((char*) fname) + : json.createNestedObject(hex); + if (use_fname) { + dev[F(D_JSON_ZIGBEE_DEVICE)] = hex; + } else if (fname) { + dev[F(D_JSON_ZIGBEE_NAME)] = (char*) fname; + } + + + dev[F(D_JSON_ZIGBEE_LIGHT)] = device.bulbtype; + if (0 <= device.bulbtype) { + + dev[F("Power")] = bitRead(device.power, 0); + dev[F("Reachable")] = bitRead(device.power, 7); + if (1 <= device.bulbtype) { + dev[F("Dimmer")] = device.dimmer; + } + if (2 <= device.bulbtype) { + dev[F("Colormode")] = device.colormode; + } + if ((2 == device.bulbtype) || (5 == device.bulbtype)) { + dev[F("CT")] = device.ct; + } + if (3 <= device.bulbtype) { + dev[F("Sat")] = device.sat; + dev[F("Hue")] = device.hue; + dev[F("X")] = device.x; + dev[F("Y")] = device.y; + } + } + } + + String payload = ""; + payload.reserve(200); + json.printTo(payload); + return payload; +} + + + + + +String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const { + DynamicJsonBuffer jsonBuffer; + JsonArray& json = jsonBuffer.createArray(); + JsonArray& devices = json; + + for (std::vector::const_iterator it = _devices.begin(); it != _devices.end(); ++it) { + const Z_Device &device = **it; + uint16_t shortaddr = device.shortaddr; + char hex[22]; + + + if ((status_shortaddr) && (status_shortaddr != shortaddr)) { continue; } + + JsonObject& dev = devices.createNestedObject(); + + snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr); + dev[F(D_JSON_ZIGBEE_DEVICE)] = hex; + + if (device.friendlyName > 0) { + dev[F(D_JSON_ZIGBEE_NAME)] = (char*) device.friendlyName; + } + + if (2 <= dump_mode) { + hex[0] = '0'; + hex[1] = 'x'; + Uint64toHex(device.longaddr, &hex[2], 64); + dev[F("IEEEAddr")] = hex; + if (device.modelId) { + dev[F(D_JSON_MODEL D_JSON_ID)] = device.modelId; + } + if (device.bulbtype >= 0) { + dev[F(D_JSON_ZIGBEE_LIGHT)] = device.bulbtype; + } + if (device.manufacturerId) { + dev[F("Manufacturer")] = device.manufacturerId; + } + JsonArray& dev_endpoints = dev.createNestedArray(F("Endpoints")); + for (uint32_t i = 0; i < endpoints_max; i++) { + uint8_t endpoint = device.endpoints[i]; + if (0x00 == endpoint) { break; } + + snprintf_P(hex, sizeof(hex), PSTR("0x%02X"), endpoint); + dev_endpoints.add(hex); + } + } + } + String payload = ""; + payload.reserve(200); + json.printTo(payload); + return payload; +} +# 1062 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" +int32_t Z_Devices::deviceRestore(const JsonObject &json) { + + + uint16_t device = 0x0000; + uint64_t ieeeaddr = 0x0000000000000000LL; + const char * modelid = nullptr; + const char * manufid = nullptr; + const char * friendlyname = nullptr; + int8_t bulbtype = 0xFF; + size_t endpoints_len = 0; + + + const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); + if (nullptr != &val_device) { + device = strToUInt(val_device); + } else { + return -1; + } + + + const JsonVariant &val_ieeeaddr = getCaseInsensitive(json, PSTR("IEEEAddr")); + if (nullptr != &val_ieeeaddr) { + ieeeaddr = strtoull(val_ieeeaddr.as(), nullptr, 0); + } + + + friendlyname = getCaseInsensitiveConstCharNull(json, PSTR("Name")); + + + modelid = getCaseInsensitiveConstCharNull(json, PSTR("ModelId")); + + + manufid = getCaseInsensitiveConstCharNull(json, PSTR("Manufacturer")); + + + const JsonVariant &val_bulbtype = getCaseInsensitive(json, PSTR(D_JSON_ZIGBEE_LIGHT)); + if (nullptr != &val_bulbtype) { bulbtype = strToUInt(val_bulbtype);; } + + + updateDevice(device, ieeeaddr); + if (modelid) { setModelId(device, modelid); } + if (manufid) { setManufId(device, manufid); } + if (friendlyname) { setFriendlyName(device, friendlyname); } + if (&val_bulbtype) { setHueBulbtype(device, bulbtype); } + + + const JsonVariant &val_endpoints = getCaseInsensitive(json, PSTR("Endpoints")); + if ((nullptr != &val_endpoints) && (val_endpoints.is())) { + const JsonArray &arr_ep = val_endpoints.as(); + endpoints_len = arr_ep.size(); + clearEndpoints(device); + if (endpoints_len) { + for (auto ep_elt : arr_ep) { + uint8_t ep = strToUInt(ep_elt); + if (ep) { + addEndpoint(device, ep); + } + } + } + } + + return 0; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_3_hue.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_3_hue.ino" +#ifdef USE_ZIGBEE +#if defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) + + + + +void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, String *response) { + static const char HUE_LIGHTS_STATUS_JSON1_SUFFIX_ZIGBEE[] PROGMEM = + "%s\"alert\":\"none\"," + "\"effect\":\"none\"," + "\"reachable\":%s}"; + + bool power, reachable; + uint8_t colormode, bri, sat; + uint16_t ct, hue; + uint16_t x, y; + String light_status = ""; + uint32_t echo_gen = findEchoGeneration(); + + zigbee_devices.getHueState(shortaddr, &power, &colormode, &bri, &sat, &ct, &hue, &x, &y, &reachable); + + if (bri > 254) bri = 254; + if (bri < 1) bri = 1; + if (sat > 254) sat = 254; + uint16_t hue16 = changeUIntScale(hue, 0, 360, 0, 65535); + + const size_t buf_size = 256; + char * buf = (char*) malloc(buf_size); + + snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), power ? "true" : "false"); + + if ((1 == echo_gen) || (LST_SINGLE <= local_light_subtype)) { + snprintf_P(buf, buf_size, PSTR("%s\"bri\":%d,"), buf, bri); + } + if (LST_COLDWARM <= local_light_subtype) { + snprintf_P(buf, buf_size, PSTR("%s\"colormode\":\"%s\","), buf, (0 == colormode) ? "hs" : (1 == colormode) ? "xy" : "ct"); + } + if (LST_RGB <= local_light_subtype) { + if (prev_x_str[0] && prev_y_str[0]) { + snprintf_P(buf, buf_size, PSTR("%s\"xy\":[%s,%s],"), buf, prev_x_str, prev_y_str); + } else { + float x_f = x / 65536.0f; + float y_f = y / 65536.0f; + snprintf_P(buf, buf_size, PSTR("%s\"xy\":[%s,%s],"), buf, String(x, 5).c_str(), String(y, 5).c_str()); + } + snprintf_P(buf, buf_size, PSTR("%s\"hue\":%d,\"sat\":%d,"), buf, hue16, sat); + } + if (LST_COLDWARM == local_light_subtype || LST_RGBW <= local_light_subtype) { + snprintf_P(buf, buf_size, PSTR("%s\"ct\":%d,"), buf, ct > 0 ? ct : 284); + } + snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON1_SUFFIX_ZIGBEE, buf, reachable ? "true" : "false"); + + *response += buf; + free(buf); +} + +void HueLightStatus2Zigbee(uint16_t shortaddr, String *response) +{ + const size_t buf_size = 192; + char * buf = (char*) malloc(buf_size); + + const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr); + char shortaddrname[8]; + snprintf_P(shortaddrname, sizeof(shortaddrname), PSTR("0x%04X"), shortaddr); + + snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON2, + (friendlyName) ? friendlyName : shortaddrname, + GetHueDeviceId(shortaddr).c_str()); + *response += buf; + free(buf); +} + +void ZigbeeHueStatus(String * response, uint16_t shortaddr) { + *response += F("{\"state\":"); + HueLightStatus1Zigbee(shortaddr, zigbee_devices.getHueBulbtype(shortaddr), response); + HueLightStatus2Zigbee(shortaddr, response); +} + +void ZigbeeCheckHue(String * response, bool &appending) { + uint32_t zigbee_num = zigbee_devices.devicesSize(); + for (uint32_t i = 0; i < zigbee_num; i++) { + int8_t bulbtype = zigbee_devices.devicesAt(i).bulbtype; + + if (bulbtype >= 0) { + uint16_t shortaddr = zigbee_devices.devicesAt(i).shortaddr; + + if (appending) { *response += ","; } + *response += "\""; + *response += EncodeLightId(0, shortaddr); + *response += F("\":{\"state\":"); + HueLightStatus1Zigbee(shortaddr, bulbtype, response); + HueLightStatus2Zigbee(shortaddr, response); + appending = true; + } + } +} + +void ZigbeeHueGroups(String * lights) { + uint32_t zigbee_num = zigbee_devices.devicesSize(); + for (uint32_t i = 0; i < zigbee_num; i++) { + int8_t bulbtype = zigbee_devices.devicesAt(i).bulbtype; + + if (bulbtype >= 0) { + *lights += ",\""; + *lights += EncodeLightId(i); + *lights += "\""; + } + } +} + + + +void ZigbeeHuePower(uint16_t shortaddr, bool power) { + zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0006, power ? 1 : 0, ""); + zigbee_devices.updateHueState(shortaddr, &power, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); +} + + +void ZigbeeHueDimmer(uint16_t shortaddr, uint8_t dimmer) { + if (dimmer > 0xFE) { dimmer = 0xFE; } + char param[8]; + snprintf_P(param, sizeof(param), PSTR("%02X0A00"), dimmer); + zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0008, 0x04, param); + zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, &dimmer, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); +} + + +void ZigbeeHueCT(uint16_t shortaddr, uint16_t ct) { + if (ct > 0xFEFF) { ct = 0xFEFF; } + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeHueCT 0x%04X - %d"), shortaddr, ct); + char param[12]; + snprintf_P(param, sizeof(param), PSTR("%02X%02X0A00"), ct & 0xFF, ct >> 8); + uint8_t colormode = 2; + zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x0A, param); + zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, nullptr, &ct, nullptr, nullptr, nullptr, nullptr); +} + + +void ZigbeeHueXY(uint16_t shortaddr, uint16_t x, uint16_t y) { + char param[16]; + if (x > 0xFEFF) { x = 0xFEFF; } + if (y > 0xFEFF) { y = 0xFEFF; } + snprintf_P(param, sizeof(param), PSTR("%02X%02X%02X%02X0A00"), x & 0xFF, x >> 8, y & 0xFF, y >> 8); + uint8_t colormode = 1; + zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x07, param); + zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, nullptr, nullptr, nullptr, &x, &y, nullptr); +} + + +void ZigbeeHueHS(uint16_t shortaddr, uint16_t hue, uint8_t sat) { + char param[16]; + uint8_t hue8 = changeUIntScale(hue, 0, 360, 0, 254); + if (sat > 0xFE) { sat = 0xFE; } + snprintf_P(param, sizeof(param), PSTR("%02X%02X0000"), hue8, sat); + uint8_t colormode = 0; + zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x06, param); + zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, &sat, nullptr, &hue, nullptr, nullptr, nullptr); +} + +void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) { + uint8_t power, colormode, bri, sat; + uint16_t ct, hue; + float x, y; + int code = 200; + + bool resp = false; + bool on = false; + + uint8_t bulbtype = zigbee_devices.getHueBulbtype(shortaddr); + + const size_t buf_size = 100; + char * buf = (char*) malloc(buf_size); + + if (Webserver->args()) { + response = "["; + + StaticJsonBuffer<300> jsonBuffer; + JsonObject &hue_json = jsonBuffer.parseObject(Webserver->arg((Webserver->args())-1)); + if (hue_json.containsKey("on")) { + on = hue_json["on"]; + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/on\":%s}}"), + device_id, on ? "true" : "false"); + + if (on) { + ZigbeeHuePower(shortaddr, 0x01); + } else { + ZigbeeHuePower(shortaddr, 0x00); + } + response += buf; + resp = true; + } + + if (hue_json.containsKey("bri")) { + bri = hue_json["bri"]; + prev_bri = bri; + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), + device_id, "bri", bri); + response += buf; + if (LST_SINGLE <= bulbtype) { + + if (254 <= bri) { bri = 255; } + ZigbeeHueDimmer(shortaddr, bri); + } + resp = true; + } + + + if (hue_json.containsKey("xy")) { + float x = hue_json["xy"][0]; + float y = hue_json["xy"][1]; + const String &x_str = hue_json["xy"][0]; + const String &y_str = hue_json["xy"][1]; + x_str.toCharArray(prev_x_str, sizeof(prev_x_str)); + y_str.toCharArray(prev_y_str, sizeof(prev_y_str)); + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/xy\":[%s,%s]}}"), + device_id, prev_x_str, prev_y_str); + response += buf; + resp = true; + uint16_t xi = x * 65536.0f; + uint16_t yi = y * 65536.0f; + ZigbeeHueXY(shortaddr, xi, yi); + } + bool huesat_changed = false; + if (hue_json.containsKey("hue")) { + hue = hue_json["hue"]; + prev_hue = hue; + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), + device_id, "hue", hue); + response += buf; + if (LST_RGB <= bulbtype) { + + hue = changeUIntScale(hue, 0, 65535, 0, 360); + huesat_changed = true; + } + resp = true; + } + if (hue_json.containsKey("sat")) { + sat = hue_json["sat"]; + prev_sat = sat; + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), + device_id, "sat", sat); + response += buf; + if (LST_RGB <= bulbtype) { + + if (254 <= sat) { sat = 255; } + huesat_changed = true; + } + if (huesat_changed) { + ZigbeeHueHS(shortaddr, hue, sat); + } + resp = true; + } + if (hue_json.containsKey("ct")) { + ct = hue_json["ct"]; + prev_ct = ct; + if (resp) { response += ","; } + snprintf_P(buf, buf_size, + PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), + device_id, "ct", ct); + response += buf; + if ((LST_COLDWARM == bulbtype) || (LST_RGBW <= bulbtype)) { + ZigbeeHueCT(shortaddr, ct); + } + resp = true; + } + + response += "]"; + if (2 == response.length()) { + response = FPSTR(HUE_ERROR_JSON); + } + } + else { + response = FPSTR(HUE_ERROR_JSON); + } + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str()); + WSSend(code, CT_JSON, response); + + free(buf); +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_4_persistence.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_4_persistence.ino" +#ifdef USE_ZIGBEE +# 52 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_4_persistence.ino" +const static uint16_t z_spi_start_sector = 0xFF; +const static uint8_t* z_spi_start = (uint8_t*) 0x402FF000; +const static uint8_t* z_dev_start = z_spi_start + 0x0800; +const static size_t z_spi_len = 0x1000; +const static size_t z_block_offset = 0x0800; +const static size_t z_block_len = 0x0800; + +class z_flashdata_t { +public: + uint32_t name; + uint16_t len; + uint16_t reserved; +}; + +const static uint32_t ZIGB_NAME = 0x3167697A; +const static size_t Z_MAX_FLASH = z_block_len - sizeof(z_flashdata_t); + + +const uint16_t Z_ClusterNumber[] PROGMEM = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0100, 0x0101, 0x0102, + 0x0201, 0x0202, 0x0203, 0x0204, + 0x0300, 0x0301, + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, + 0x0500, 0x0501, 0x0502, + 0x0700, 0x0701, 0x0702, + 0x0B00, 0x0B01, 0x0B02, 0x0B03, 0x0B04, 0x0B05, + 0x1000, + 0xFC0F, +}; + + +uint16_t fromClusterCode(uint8_t c) { + if (c >= sizeof(Z_ClusterNumber)/sizeof(Z_ClusterNumber[0])) { + return 0xFFFF; + } + return pgm_read_word(&Z_ClusterNumber[c]); +} + + +uint8_t toClusterCode(uint16_t c) { + for (uint32_t i = 0; i < sizeof(Z_ClusterNumber)/sizeof(Z_ClusterNumber[0]); i++) { + if (c == pgm_read_word(&Z_ClusterNumber[i])) { + return i; + } + } + return 0xFF; +} + +class SBuffer hibernateDevice(const struct Z_Device &device) { + SBuffer buf(128); + + buf.add8(0x00); + buf.add16(device.shortaddr); + buf.add64(device.longaddr); + + uint32_t endpoints_count = 0; + for (endpoints_count = 0; endpoints_count < endpoints_max; endpoints_count++) { + if (0x00 == device.endpoints[endpoints_count]) { break; } + } + + buf.add8(endpoints_count); + + for (uint32_t i = 0; i < endpoints_max; i++) { + uint8_t endpoint = device.endpoints[i]; + if (0x00 == endpoint) { break; } + + buf.add8(endpoint); + buf.add16(0x0000); + + + buf.add8(0xFF); + + + buf.add8(0xFF); + } + + + if (device.modelId) { + size_t model_len = strlen(device.modelId); + if (model_len > 32) { model_len = 32; } + buf.addBuffer(device.modelId, model_len); + } + buf.add8(0x00); + + + if (device.manufacturerId) { + size_t manuf_len = strlen(device.manufacturerId); + if (manuf_len > 32) { manuf_len = 32; } + buf.addBuffer(device.manufacturerId, manuf_len); + } + buf.add8(0x00); + + + if (device.friendlyName) { + size_t frname_len = strlen(device.friendlyName); + if (frname_len > 32) {frname_len = 32; } + buf.addBuffer(device.friendlyName, frname_len); + } + buf.add8(0x00); + + + buf.add8(device.bulbtype); + + + buf.set8(0, buf.len()); + + return buf; +} + +class SBuffer hibernateDevices(void) { + SBuffer buf(2048); + + size_t devices_size = zigbee_devices.devicesSize(); + if (devices_size > 32) { devices_size = 32; } + buf.add8(devices_size); + + for (uint32_t i = 0; i < devices_size; i++) { + const Z_Device & device = zigbee_devices.devicesAt(i); + const SBuffer buf_device = hibernateDevice(device); + buf.addBuffer(buf_device); + } + + size_t buf_len = buf.len(); + if (buf_len > 2040) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Devices list too big to fit in Flash (%d)"), buf_len); + } + + + char *hex_char = (char*) malloc((buf_len * 2) + 2); + if (hex_char) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "ZbFlashStore %s"), + ToHex_P(buf.getBuffer(), buf_len, hex_char, (buf_len * 2) + 2)); + free(hex_char); + } + + return buf; +} + +void hydrateDevices(const SBuffer &buf) { + uint32_t buf_len = buf.len(); + if (buf_len <= 10) { return; } + + uint32_t k = 0; + uint32_t num_devices = buf.get8(k++); + + for (uint32_t i = 0; (i < num_devices) && (k < buf_len); i++) { + uint32_t dev_record_len = buf.get8(k); + + + + + SBuffer buf_d = buf.subBuffer(k, dev_record_len); +# 217 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_4_persistence.ino" + uint32_t d = 1; + uint16_t shortaddr = buf_d.get16(d); d += 2; + uint64_t longaddr = buf_d.get64(d); d += 8; + zigbee_devices.updateDevice(shortaddr, longaddr); + + uint32_t endpoints = buf_d.get8(d++); + for (uint32_t j = 0; j < endpoints; j++) { + uint8_t ep = buf_d.get8(d++); + uint16_t ep_profile = buf_d.get16(d); d += 2; + zigbee_devices.addEndpoint(shortaddr, ep); + + + while (d < dev_record_len) { + uint8_t ep_cluster = buf_d.get8(d++); + if (0xFF == ep_cluster) { break; } + + } + + while (d < dev_record_len) { + uint8_t ep_cluster = buf_d.get8(d++); + if (0xFF == ep_cluster) { break; } + + } + } + + + + char empty[] = ""; + + + uint32_t s_len = buf_d.strlen_s(d); + char *ptr = s_len ? buf_d.charptr(d) : empty; + zigbee_devices.setModelId(shortaddr, ptr); + d += s_len + 1; + + + s_len = buf_d.strlen_s(d); + ptr = s_len ? buf_d.charptr(d) : empty; + zigbee_devices.setManufId(shortaddr, ptr); + d += s_len + 1; + + + s_len = buf_d.strlen_s(d); + ptr = s_len ? buf_d.charptr(d) : empty; + zigbee_devices.setFriendlyName(shortaddr, ptr); + d += s_len + 1; + + + if (d < dev_record_len) { + zigbee_devices.setHueBulbtype(shortaddr, buf_d.get8(d)); + d++; + } + + + k += dev_record_len; + + } +} + +void loadZigbeeDevices(void) { + z_flashdata_t flashdata; + memcpy_P(&flashdata, z_dev_start, sizeof(z_flashdata_t)); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Zigbee signature in Flash: %08X - %d"), flashdata.name, flashdata.len); + + + if ((flashdata.name == ZIGB_NAME) && (flashdata.len > 0)) { + uint16_t buf_len = flashdata.len; + + SBuffer buf(buf_len); + buf.addBuffer(z_dev_start + sizeof(z_flashdata_t), buf_len); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee devices data in Flash (%d bytes)"), buf_len); + hydrateDevices(buf); + zigbee_devices.clean(); + } else { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No zigbee devices data in Flash")); + } + +} + +void saveZigbeeDevices(void) { + SBuffer buf = hibernateDevices(); + size_t buf_len = buf.len(); + if (buf_len > Z_MAX_FLASH) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Buffer too big to fit in Flash (%d bytes)"), buf_len); + return; + } + + + uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len); + if (!spi_buffer) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer")); + return; + } + + ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); + + z_flashdata_t *flashdata = (z_flashdata_t*)(spi_buffer + z_block_offset); + flashdata->name = ZIGB_NAME; + flashdata->len = buf_len; + flashdata->reserved = 0; + + memcpy(spi_buffer + z_block_offset + sizeof(z_flashdata_t), buf.getBuffer(), buf_len); + + + if (ESP.flashEraseSector(z_spi_start_sector)) { + ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); + } + + free(spi_buffer); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data store in Flash (0x%08X - %d bytes)"), z_dev_start, buf_len); +} + + +void eraseZigbeeDevices(void) { + zigbee_devices.clean(); + + uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len); + if (!spi_buffer) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer")); + return; + } + + ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); + + + memset(spi_buffer + z_block_offset, 0xFF, z_block_len); + + + if (ESP.flashEraseSector(z_spi_start_sector)) { + ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); + } + + free(spi_buffer); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data erased (0x%08X - %d bytes)"), z_dev_start, z_block_len); +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" +#ifdef USE_ZIGBEE + + + + + + +enum Z_DataTypes { + Znodata = 0x00, + Zdata8 = 0x08, Zdata16, Zdata24, Zdata32, Zdata40, Zdata48, Zdata56, Zdata64, + Zbool = 0x10, + Zmap8 = 0x18, Zmap16, Zmap24, Zmap32, Zmap40, Zmap48, Zmap56, Zmap64, + Zuint8 = 0x20, Zuint16, Zuint24, Zuint32, Zuint40, Zuint48, Zuint56, Zuint64, + Zint8 = 0x28, Zint16, Zint24, Zint32, Zint40, Zint48, Zint56, Zint64, + Zenum8 = 0x30, Zenum16 = 0x31, + Zsemi = 0x38, Zsingle = 0x39, Zdouble = 0x3A, + Zoctstr = 0x41, Zstring = 0x42, Zoctstr16 = 0x43, Zstring16 = 0x44, + Arrray = 0x48, + Zstruct = 0x4C, + Zset = 0x50, Zbag = 0x51, + ZToD = 0xE0, Zdate = 0xE1, ZUTC = 0xE2, + ZclusterId = 0xE8, ZattribId = 0xE9, ZbacOID = 0xEA, + ZEUI64 = 0xF0, Zkey128 = 0xF1, + Zunk = 0xFF +}; + + + + + + +uint8_t Z_getDatatypeLen(uint8_t t) { + if ( ((t >= 0x08) && (t <= 0x0F)) || + ((t >= 0x18) && (t <= 0x2F)) ) { + return (t & 0x07) + 1; + } + switch (t) { + case Zbool: + case Zenum8: + return 1; + case Zenum16: + case Zsemi: + case ZclusterId: + case ZattribId: + return 2; + case Zsingle: + case ZToD: + case Zdate: + case ZUTC: + case ZbacOID: + return 4; + case Zdouble: + case ZEUI64: + return 8; + case Zkey128: + return 16; + case Znodata: + default: + return 0; + } +} +# 92 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" +typedef union ZCLHeaderFrameControl_t { + struct { + uint8_t frame_type : 2; + uint8_t manuf_specific : 1; + uint8_t direction : 1; + uint8_t disable_def_resp : 1; + uint8_t reserved : 3; + } b; + uint32_t d8; +} ZCLHeaderFrameControl_t; + + +class ZCLFrame { +public: + + ZCLFrame(uint8_t frame_control, uint16_t manuf_code, uint8_t transact_seq, uint8_t cmd_id, + const char *buf, size_t buf_len, uint16_t clusterid, uint16_t groupaddr, + uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast, + uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber, + uint32_t timestamp): + _manuf_code(manuf_code), _transact_seq(transact_seq), _cmd_id(cmd_id), + _payload(buf_len ? buf_len : 250), + _cluster_id(clusterid), _groupaddr(groupaddr), + _srcaddr(srcaddr), _srcendpoint(srcendpoint), _dstendpoint(dstendpoint), _wasbroadcast(wasbroadcast), + _linkquality(linkquality), _securityuse(securityuse), _seqnumber(seqnumber), + _timestamp(timestamp) + { + _frame_control.d8 = frame_control; + _payload.addBuffer(buf, buf_len); + }; + + + void log(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_ZIGBEEZCL_RECEIVED "\":{" + "\"groupid\":%d," "\"clusterid\":%d," "\"srcaddr\":\"0x%04X\"," + "\"srcendpoint\":%d," "\"dstendpoint\":%d," "\"wasbroadcast\":%d," + "\"" D_CMND_ZIGBEE_LINKQUALITY "\":%d," "\"securityuse\":%d," "\"seqnumber\":%d," + "\"timestamp\":%d," + "\"fc\":\"0x%02X\",\"manuf\":\"0x%04X\",\"transact\":%d," + "\"cmdid\":\"0x%02X\",\"payload\":\"%s\"}}"), + _groupaddr, _cluster_id, _srcaddr, + _srcendpoint, _dstendpoint, _wasbroadcast, + _linkquality, _securityuse, _seqnumber, + _timestamp, + _frame_control, _manuf_code, _transact_seq, _cmd_id, + hex_char); + if (Settings.flag3.tuya_serial_mqtt_publish) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); + XdrvRulesProcess(); + } else { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "%s"), mqtt_data); + } + } + + static ZCLFrame parseRawFrame(const SBuffer &buf, uint8_t offset, uint8_t len, uint16_t clusterid, uint16_t groupid, + uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast, + uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber, + uint32_t timestamp) { + uint32_t i = offset; + ZCLHeaderFrameControl_t frame_control; + uint16_t manuf_code = 0; + uint8_t transact_seq; + uint8_t cmd_id; + + frame_control.d8 = buf.get8(i++); + if (frame_control.b.manuf_specific) { + manuf_code = buf.get16(i); + i += 2; + } + transact_seq = buf.get8(i++); + cmd_id = buf.get8(i++); + ZCLFrame zcl_frame(frame_control.d8, manuf_code, transact_seq, cmd_id, + (const char *)(buf.buf() + i), len + offset - i, + clusterid, groupid, + srcaddr, srcendpoint, dstendpoint, wasbroadcast, + linkquality, securityuse, seqnumber, + timestamp); + return zcl_frame; + } + + bool isClusterSpecificCommand(void) { + return _frame_control.b.frame_type & 1; + } + + static void generateAttributeName(const JsonObject& json, uint16_t cluster, uint16_t attr, char *key, size_t key_len); + void parseRawAttributes(JsonObject& json, uint8_t offset = 0); + void parseReadAttributes(JsonObject& json, uint8_t offset = 0); + void parseResponse(void); + void parseClusterSpecificCommand(JsonObject& json, uint8_t offset = 0); + void postProcessAttributes(uint16_t shortaddr, JsonObject& json); + + inline void setGroupId(uint16_t groupid) { + _groupaddr = groupid; + } + + inline void setClusterId(uint16_t clusterid) { + _cluster_id = clusterid; + } + + inline uint8_t getCmdId(void) const { + return _cmd_id; + } + + inline uint16_t getClusterId(void) const { + return _cluster_id; + } + + inline uint16_t getSrcEndpoint(void) const { + return _srcendpoint; + } + + const SBuffer &getPayload(void) const { + return _payload; + } + + uint16_t getManufCode(void) const { + return _manuf_code; + } + +private: + ZCLHeaderFrameControl_t _frame_control = { .d8 = 0 }; + uint16_t _manuf_code = 0; + uint8_t _transact_seq = 0; + uint8_t _cmd_id = 0; + SBuffer _payload; + uint16_t _cluster_id = 0; + uint16_t _groupaddr = 0; + + uint16_t _srcaddr; + uint8_t _srcendpoint; + uint8_t _dstendpoint; + uint8_t _wasbroadcast; + uint8_t _linkquality; + uint8_t _securityuse; + uint8_t _seqnumber; + uint32_t _timestamp; +}; + + + + + + +uint8_t toPercentageCR2032(uint32_t voltage) { + uint32_t percentage; + if (voltage < 2100) { + percentage = 0; + } else if (voltage < 2440) { + percentage = 6 - ((2440 - voltage) * 6) / 340; + } else if (voltage < 2740) { + percentage = 18 - ((2740 - voltage) * 12) / 300; + } else if (voltage < 2900) { + percentage = 42 - ((2900 - voltage) * 24) / 160; + } else if (voltage < 3000) { + percentage = 100 - ((3000 - voltage) * 58) / 100; + } else if (voltage >= 3000) { + percentage = 100; + } + return percentage; +} + + +uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer &buf, + uint32_t offset, uint32_t buflen) { + + uint32_t i = offset; + uint32_t attrtype = buf.get8(i++); + + + json[attrid_str] = (char*) nullptr; + + uint32_t len = Z_getDatatypeLen(attrtype); + + + switch (attrtype) { + + + + case Zbool: + case Zuint8: + case Zenum8: + { + uint8_t uint8_val = buf.get8(i); + + if (0xFF != uint8_val) { + json[attrid_str] = uint8_val; + } + } + break; + case Zuint16: + case Zenum16: + { + uint16_t uint16_val = buf.get16(i); + + if (0xFFFF != uint16_val) { + json[attrid_str] = uint16_val; + } + } + break; + case Zuint32: + { + uint32_t uint32_val = buf.get32(i); + + if (0xFFFFFFFF != uint32_val) { + json[attrid_str] = uint32_val; + } + } + break; + + + case Zuint40: + case Zuint48: + case Zuint56: + case Zuint64: + case Zint40: + case Zint48: + case Zint56: + case Zint64: + { + + + char hex[2*len+1]; + ToHex_P(buf.buf(i), len, hex, sizeof(hex)); + json[attrid_str] = hex; + + } + break; + case Zint8: + { + int8_t int8_val = buf.get8(i); + + if (0x80 != int8_val) { + json[attrid_str] = int8_val; + } + } + break; + case Zint16: + { + int16_t int16_val = buf.get16(i); + + if (0x8000 != int16_val) { + json[attrid_str] = int16_val; + } + } + break; + case Zint32: + { + int32_t int32_val = buf.get32(i); + + if (0x80000000 != int32_val) { + json[attrid_str] = int32_val; + } + } + break; + + case Zoctstr: + case Zstring: + case Zoctstr16: + case Zstring16: + + { + bool parse_as_string = true; + len = (attrtype <= 0x42) ? buf.get8(i) : buf.get16(i); + i += (attrtype <= 0x42) ? 1 : 2; + if (i + len > buf.len()) { + len = buf.len() - i; + } + + + if ((0x41 == attrtype) || (0x43 == attrtype)) { parse_as_string = false; } + + if (parse_as_string) { + char str[len+1]; + strncpy(str, buf.charptr(i), len); + str[len] = 0x00; + json[attrid_str] = str; + } else { + + char hex[2*len+1]; + ToHex_P(buf.buf(i), len, hex, sizeof(hex)); + json[attrid_str] = hex; + } + + + + } + + break; + + case Zdata8: + case Zmap8: + { + uint8_t uint8_val = buf.get8(i); + + json[attrid_str] = uint8_val; + } + break; + case Zdata16: + case Zmap16: + { + uint16_t uint16_val = buf.get16(i); + + json[attrid_str] = uint16_val; + } + break; + case Zdata32: + case Zmap32: + { + uint32_t uint32_val = buf.get32(i); + + json[attrid_str] = uint32_val; + } + break; + + case Zsingle: + { + uint32_t uint32_val = buf.get32(i); + float * float_val = (float*) &uint32_val; + + json[attrid_str] = *float_val; + } + break; + + + case ZToD: + case Zdate: + case ZUTC: + case ZclusterId: + case ZattribId: + case ZbacOID: + case ZEUI64: + case Zkey128: + case Zsemi: + break; + + + case Zdata24: + case Zdata40: + case Zdata48: + case Zdata56: + case Zdata64: + break; + + case Zmap24: + case Zmap40: + case Zmap48: + case Zmap56: + case Zmap64: + break; + case Zdouble: + { + uint64_t uint64_val = buf.get64(i); + double * double_val = (double*) &uint64_val; + + json[attrid_str] = *double_val; + } + break; + } + i += len; + + + + + + + return i - offset; +} + + +void ZCLFrame::generateAttributeName(const JsonObject& json, uint16_t cluster, uint16_t attr, char *key, size_t key_len) { + uint32_t suffix = 1; + + snprintf_P(key, key_len, PSTR("%04X/%04X"), cluster, attr); + while (json.containsKey(key)) { + suffix++; + snprintf_P(key, key_len, PSTR("%04X/%04X+%d"), cluster, attr, suffix); + } +} + + +void ZCLFrame::parseRawAttributes(JsonObject& json, uint8_t offset) { + uint32_t i = offset; + uint32_t len = _payload.len(); + + while (len >= i + 3) { + uint16_t attrid = _payload.get16(i); + i += 2; + + char key[16]; + generateAttributeName(json, _cluster_id, attrid, key, sizeof(key)); + + + if ((0x0000 == _cluster_id) && (0xFF01 == attrid)) { + if (0x42 == _payload.get8(i)) { + _payload.set8(i, 0x41); + } + } + i += parseSingleAttribute(json, key, _payload, i, len); + } +} + + +void ZCLFrame::parseReadAttributes(JsonObject& json, uint8_t offset) { + uint32_t i = offset; + uint32_t len = _payload.len(); + + while (len - i >= 4) { + uint16_t attrid = _payload.get16(i); + i += 2; + uint8_t status = _payload.get8(i++); + + if (0 == status) { + char key[16]; + generateAttributeName(json, _cluster_id, attrid, key, sizeof(key)); + + i += parseSingleAttribute(json, key, _payload, i, len); + } + } +} + + +void ZCLFrame::parseResponse(void) { + if (_payload.len() < 2) { return; } + uint8_t cmd = _payload.get8(0); + uint8_t status = _payload.get8(1); + + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.createObject(); + + + char s[12]; + snprintf_P(s, sizeof(s), PSTR("0x%04X"), _srcaddr); + json[F(D_JSON_ZIGBEE_DEVICE)] = s; + + const char * friendlyName = zigbee_devices.getFriendlyName(_srcaddr); + if (friendlyName) { + json[F(D_JSON_ZIGBEE_NAME)] = (char*) friendlyName; + } + + snprintf_P(s, sizeof(s), PSTR("%04X!%02X"), _cluster_id, cmd); + json[F(D_JSON_ZIGBEE_CMD)] = s; + + json[F(D_JSON_ZIGBEE_STATUS)] = status; + + json[F(D_JSON_ZIGBEE_STATUS_MSG)] = getZigbeeStatusMessage(status); + + json[F(D_CMND_ZIGBEE_ENDPOINT)] = _srcendpoint; + + if (_groupaddr) { + json[F(D_CMND_ZIGBEE_GROUP)] = _groupaddr; + } + + json[F(D_CMND_ZIGBEE_LINKQUALITY)] = _linkquality; + + String msg(""); + msg.reserve(100); + json.printTo(msg); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_RESPONSE "\":%s}"), msg.c_str()); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); +} + + + +void ZCLFrame::parseClusterSpecificCommand(JsonObject& json, uint8_t offset) { + convertClusterSpecific(json, _cluster_id, _cmd_id, _frame_control.b.direction, _payload); + sendHueUpdate(_srcaddr, _groupaddr, _cluster_id, _cmd_id, _frame_control.b.direction); +} + + + + +typedef int32_t (*Z_AttrConverter)(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); +typedef struct Z_AttributeConverter { + uint8_t type; + uint8_t cluster_short; + uint16_t attribute; + const char * name; + Z_AttrConverter func; +} Z_AttributeConverter; + +enum Cx_cluster_short { + Cx0000, Cx0001, Cx0002, Cx0003, Cx0004, Cx0005, Cx0006, Cx0007, + Cx0008, Cx0009, Cx000A, Cx000B, Cx000C, Cx000D, Cx000E, Cx000F, + Cx0010, Cx0011, Cx0012, Cx0013, Cx0014, Cx001A, Cx0020, Cx0100, + Cx0101, Cx0102, Cx0300, Cx0400, Cx0401, Cx0402, Cx0403, Cx0404, + Cx0405, Cx0406, Cx0B01, Cx0B05, +}; + +const uint16_t Cx_cluster[] PROGMEM = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x001A, 0x0020, 0x0100, + 0x0101, 0x0102, 0x0300, 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, + 0x0405, 0x0406, 0x0B01, 0x0B05, +}; + +uint16_t CxToCluster(uint8_t cx) { + if (cx < sizeof(Cx_cluster)/sizeof(Cx_cluster[0])) { + return pgm_read_word(&Cx_cluster[cx]); + } + return 0xFFFF; +} + +ZF(ZCLVersion) ZF(AppVersion) ZF(StackVersion) ZF(HWVersion) ZF(Manufacturer) ZF(ModelId) +ZF(DateCode) ZF(PowerSource) ZF(SWBuildID) ZF(Power) ZF(SwitchType) ZF(Dimmer) +ZF(MainsVoltage) ZF(MainsFrequency) ZF(BatteryVoltage) ZF(BatteryPercentage) +ZF(CurrentTemperature) ZF(MinTempExperienced) ZF(MaxTempExperienced) ZF(OverTempTotalDwell) +ZF(SceneCount) ZF(CurrentScene) ZF(CurrentGroup) ZF(SceneValid) +ZF(AlarmCount) ZF(Time) ZF(TimeStatus) ZF(TimeZone) ZF(DstStart) ZF(DstEnd) +ZF(DstShift) ZF(StandardTime) ZF(LocalTime) ZF(LastSetTime) ZF(ValidUntilTime) + +ZF(LocationType) ZF(LocationMethod) ZF(LocationAge) ZF(QualityMeasure) ZF(NumberOfDevices) + +ZF(AnalogInActiveText) ZF(AnalogInDescription) ZF(AnalogInInactiveText) ZF(AnalogInMaxValue) +ZF(AnalogInMinValue) ZF(AnalogInOutOfService) ZF(AqaraRotate) ZF(AnalogInPriorityArray) +ZF(AnalogInReliability) ZF(AnalogInRelinquishDefault) ZF(AnalogInResolution) ZF(AnalogInStatusFlags) +ZF(AnalogInEngineeringUnits) ZF(AnalogInApplicationType) ZF(Aqara_FF05) + +ZF(AnalogOutDescription) ZF(AnalogOutMaxValue) ZF(AnalogOutMinValue) ZF(AnalogOutOutOfService) +ZF(AnalogOutValue) ZF(AnalogOutPriorityArray) ZF(AnalogOutReliability) ZF(AnalogOutRelinquishDefault) +ZF(AnalogOutResolution) ZF(AnalogOutStatusFlags) ZF(AnalogOutEngineeringUnits) ZF(AnalogOutApplicationType) + +ZF(AnalogDescription) ZF(AnalogOutOfService) ZF(AnalogValue) ZF(AnalogPriorityArray) ZF(AnalogReliability) +ZF(AnalogRelinquishDefault) ZF(AnalogStatusFlags) ZF(AnalogEngineeringUnits) ZF(AnalogApplicationType) + +ZF(BinaryInActiveText) ZF(BinaryInDescription) ZF(BinaryInInactiveText) ZF(BinaryInOutOfService) +ZF(BinaryInPolarity) ZF(BinaryInValue) ZF(BinaryInPriorityArray) ZF(BinaryInReliability) +ZF(BinaryInStatusFlags) ZF(BinaryInApplicationType) + +ZF(BinaryOutActiveText) ZF(BinaryOutDescription) ZF(BinaryOutInactiveText) ZF(BinaryOutMinimumOffTime) +ZF(BinaryOutMinimumOnTime) ZF(BinaryOutOutOfService) ZF(BinaryOutPolarity) ZF(BinaryOutValue) +ZF(BinaryOutPriorityArray) ZF(BinaryOutReliability) ZF(BinaryOutRelinquishDefault) ZF(BinaryOutStatusFlags) +ZF(BinaryOutApplicationType) + +ZF(BinaryActiveText) ZF(BinaryDescription) ZF(BinaryInactiveText) ZF(BinaryMinimumOffTime) +ZF(BinaryMinimumOnTime) ZF(BinaryOutOfService) ZF(BinaryValue) ZF(BinaryPriorityArray) ZF(BinaryReliability) +ZF(BinaryRelinquishDefault) ZF(BinaryStatusFlags) ZF(BinaryApplicationType) + +ZF(MultiInStateText) ZF(MultiInDescription) ZF(MultiInNumberOfStates) ZF(MultiInOutOfService) +ZF(MultiInValue) ZF(MultiInReliability) ZF(MultiInStatusFlags) ZF(MultiInApplicationType) + +ZF(MultiOutStateText) ZF(MultiOutDescription) ZF(MultiOutNumberOfStates) ZF(MultiOutOutOfService) +ZF(MultiOutValue) ZF(MultiOutPriorityArray) ZF(MultiOutReliability) ZF(MultiOutRelinquishDefault) +ZF(MultiOutStatusFlags) ZF(MultiOutApplicationType) + +ZF(MultiStateText) ZF(MultiDescription) ZF(MultiNumberOfStates) ZF(MultiOutOfService) ZF(MultiValue) +ZF(MultiReliability) ZF(MultiRelinquishDefault) ZF(MultiStatusFlags) ZF(MultiApplicationType) + +ZF(TotalProfileNum) ZF(MultipleScheduling) ZF(EnergyFormatting) ZF(EnergyRemote) ZF(ScheduleMode) + +ZF(CheckinInterval) ZF(LongPollInterval) ZF(ShortPollInterval) ZF(FastPollTimeout) ZF(CheckinIntervalMin) +ZF(LongPollIntervalMin) ZF(FastPollTimeoutMax) + +ZF(PhysicalClosedLimit) ZF(MotorStepSize) ZF(Status) ZF(ClosedLimit) ZF(Mode) + +ZF(LockState) ZF(LockType) ZF(ActuatorEnabled) ZF(DoorState) ZF(DoorOpenEvents) +ZF(DoorClosedEvents) ZF(OpenPeriod) + +ZF(AqaraVibrationMode) ZF(AqaraVibrationsOrAngle) ZF(AqaraVibration505) ZF(AqaraAccelerometer) + +ZF(WindowCoveringType) ZF(PhysicalClosedLimitLift) ZF(PhysicalClosedLimitTilt) ZF(CurrentPositionLift) +ZF(CurrentPositionTilt) ZF(NumberofActuationsLift) ZF(NumberofActuationsTilt) ZF(ConfigStatus) +ZF(CurrentPositionLiftPercentage) ZF(CurrentPositionTiltPercentage) ZF(InstalledOpenLimitLift) +ZF(InstalledClosedLimitLift) ZF(InstalledOpenLimitTilt) ZF(InstalledClosedLimitTilt) ZF(VelocityLift) +ZF(AccelerationTimeLift) ZF(DecelerationTimeLift) ZF(IntermediateSetpointsLift) +ZF(IntermediateSetpointsTilt) + +ZF(Hue) ZF(Sat) ZF(RemainingTime) ZF(X) ZF(Y) ZF(DriftCompensation) ZF(CompensationText) ZF(CT) +ZF(ColorMode) ZF(NumberOfPrimaries) ZF(Primary1X) ZF(Primary1Y) ZF(Primary1Intensity) ZF(Primary2X) +ZF(Primary2Y) ZF(Primary2Intensity) ZF(Primary3X) ZF(Primary3Y) ZF(Primary3Intensity) ZF(WhitePointX) +ZF(WhitePointY) ZF(ColorPointRX) ZF(ColorPointRY) ZF(ColorPointRIntensity) ZF(ColorPointGX) ZF(ColorPointGY) +ZF(ColorPointGIntensity) ZF(ColorPointBX) ZF(ColorPointBY) ZF(ColorPointBIntensity) + +ZF(Illuminance) ZF(IlluminanceMinMeasuredValue) ZF(IlluminanceMaxMeasuredValue) ZF(IlluminanceTolerance) +ZF(IlluminanceLightSensorType) ZF(IlluminanceLevelStatus) ZF(IlluminanceTargetLevel) + +ZF(Temperature) ZF(TemperatureMinMeasuredValue) ZF(TemperatureMaxMeasuredValue) ZF(TemperatureTolerance) + +ZF(PressureUnit) ZF(Pressure) ZF(PressureMinMeasuredValue) ZF(PressureMaxMeasuredValue) ZF(PressureTolerance) +ZF(PressureScaledValue) ZF(PressureMinScaledValue) ZF(PressureMaxScaledValue) ZF(PressureScaledTolerance) +ZF(PressureScale) + +ZF(FlowRate) ZF(FlowMinMeasuredValue) ZF(FlowMaxMeasuredValue) ZF(FlowTolerance) + +ZF(Humidity) ZF(HumidityMinMeasuredValue) ZF(HumidityMaxMeasuredValue) ZF(HumidityTolerance) + +ZF(Occupancy) ZF(OccupancySensorType) + +ZF(CompanyName) ZF(MeterTypeID) ZF(DataQualityID) ZF(CustomerName) ZF(Model) ZF(PartNumber) +ZF(SoftwareRevision) ZF(POD) ZF(AvailablePower) ZF(PowerThreshold) ZF(ProductRevision) ZF(UtilityName) + +ZF(NumberOfResets) ZF(PersistentMemoryWrites) ZF(LastMessageLQI) ZF(LastMessageRSSI) + +const Z_AttributeConverter Z_PostProcess[] PROGMEM = { + { Zuint8, Cx0000, 0x0000, Z(ZCLVersion), &Z_Copy }, + { Zuint8, Cx0000, 0x0001, Z(AppVersion), &Z_Copy }, + { Zuint8, Cx0000, 0x0002, Z(StackVersion), &Z_Copy }, + { Zuint8, Cx0000, 0x0003, Z(HWVersion), &Z_Copy }, + { Zstring, Cx0000, 0x0004, Z(Manufacturer), &Z_ManufKeep }, + { Zstring, Cx0000, 0x0005, Z(ModelId), &Z_ModelKeep }, + { Zstring, Cx0000, 0x0006, Z(DateCode), &Z_Copy }, + { Zenum8, Cx0000, 0x0007, Z(PowerSource), &Z_Copy }, + { Zstring, Cx0000, 0x4000, Z(SWBuildID), &Z_Copy }, + { Zunk, Cx0000, 0xFFFF, nullptr, &Z_Remove }, + + { Zmap8, Cx0000, 0xFF01, nullptr, &Z_AqaraSensor }, + + + { Zuint16, Cx0001, 0x0000, Z(MainsVoltage), &Z_Copy }, + { Zuint8, Cx0001, 0x0001, Z(MainsFrequency), &Z_Copy }, + { Zuint8, Cx0001, 0x0020, Z(BatteryVoltage), &Z_FloatDiv10 }, + { Zuint8, Cx0001, 0x0021, Z(BatteryPercentage), &Z_Copy }, + + + { Zint16, Cx0002, 0x0000, Z(CurrentTemperature), &Z_Copy }, + { Zint16, Cx0002, 0x0001, Z(MinTempExperienced), &Z_Copy }, + { Zint16, Cx0002, 0x0002, Z(MaxTempExperienced), &Z_Copy }, + { Zuint16, Cx0002, 0x0003, Z(OverTempTotalDwell), &Z_Copy }, + + + { Zuint8, Cx0005, 0x0000, Z(SceneCount), &Z_Copy }, + { Zuint8, Cx0005, 0x0001, Z(CurrentScene), &Z_Copy }, + { Zuint16, Cx0005, 0x0002, Z(CurrentGroup), &Z_Copy }, + { Zbool, Cx0005, 0x0003, Z(SceneValid), &Z_Copy }, + + + + { Zbool, Cx0006, 0x0000, Z(Power), &Z_Copy }, + { Zbool, Cx0006, 0x8000, Z(Power), &Z_Copy }, + + + { Zenum8, Cx0007, 0x0000, Z(SwitchType), &Z_Copy }, + + + { Zuint8, Cx0008, 0x0000, Z(Dimmer), &Z_Copy }, +# 738 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" + { Zuint16, Cx0009, 0x0000, Z(AlarmCount), &Z_Copy }, + + + { ZUTC, Cx000A, 0x0000, Z(Time), &Z_Copy }, + { Zmap8, Cx000A, 0x0001, Z(TimeStatus), &Z_Copy }, + { Zint32, Cx000A, 0x0002, Z(TimeZone), &Z_Copy }, + { Zuint32, Cx000A, 0x0003, Z(DstStart), &Z_Copy }, + { Zuint32, Cx000A, 0x0004, Z(DstEnd), &Z_Copy }, + { Zint32, Cx000A, 0x0005, Z(DstShift), &Z_Copy }, + { Zuint32, Cx000A, 0x0006, Z(StandardTime), &Z_Copy }, + { Zuint32, Cx000A, 0x0007, Z(LocalTime), &Z_Copy }, + { ZUTC, Cx000A, 0x0008, Z(LastSetTime), &Z_Copy }, + { ZUTC, Cx000A, 0x0009, Z(ValidUntilTime), &Z_Copy }, + + + { Zdata8, Cx000B, 0x0000, Z(LocationType), &Z_Copy }, + { Zenum8, Cx000B, 0x0001, Z(LocationMethod), &Z_Copy }, + { Zuint16, Cx000B, 0x0002, Z(LocationAge), &Z_Copy }, + { Zuint8, Cx000B, 0x0003, Z(QualityMeasure), &Z_Copy }, + { Zuint8, Cx000B, 0x0004, Z(NumberOfDevices), &Z_Copy }, + + + + { Zstring, Cx000C, 0x001C, Z(AnalogInDescription), &Z_Copy }, + + { Zsingle, Cx000C, 0x0041, Z(AnalogInMaxValue), &Z_Copy }, + { Zsingle, Cx000C, 0x0045, Z(AnalogInMinValue), &Z_Copy }, + { Zbool, Cx000C, 0x0051, Z(AnalogInOutOfService), &Z_Copy }, + { Zsingle, Cx000C, 0x0055, Z(AqaraRotate), &Z_Copy }, + + { Zenum8, Cx000C, 0x0067, Z(AnalogInReliability), &Z_Copy }, + + { Zsingle, Cx000C, 0x006A, Z(AnalogInResolution), &Z_Copy }, + { Zmap8, Cx000C, 0x006F, Z(AnalogInStatusFlags), &Z_Copy }, + { Zenum16, Cx000C, 0x0075, Z(AnalogInEngineeringUnits),&Z_Copy }, + { Zuint32, Cx000C, 0x0100, Z(AnalogInApplicationType),&Z_Copy }, + { Zuint16, Cx000C, 0xFF05, Z(Aqara_FF05), &Z_Copy }, + + + { Zstring, Cx000D, 0x001C, Z(AnalogOutDescription), &Z_Copy }, + { Zsingle, Cx000D, 0x0041, Z(AnalogOutMaxValue), &Z_Copy }, + { Zsingle, Cx000D, 0x0045, Z(AnalogOutMinValue), &Z_Copy }, + { Zbool, Cx000D, 0x0051, Z(AnalogOutOutOfService),&Z_Copy }, + { Zsingle, Cx000D, 0x0055, Z(AnalogOutValue), &Z_Copy }, + + { Zenum8, Cx000D, 0x0067, Z(AnalogOutReliability), &Z_Copy }, + { Zsingle, Cx000D, 0x0068, Z(AnalogOutRelinquishDefault),&Z_Copy }, + { Zsingle, Cx000D, 0x006A, Z(AnalogOutResolution), &Z_Copy }, + { Zmap8, Cx000D, 0x006F, Z(AnalogOutStatusFlags), &Z_Copy }, + { Zenum16, Cx000D, 0x0075, Z(AnalogOutEngineeringUnits),&Z_Copy }, + { Zuint32, Cx000D, 0x0100, Z(AnalogOutApplicationType),&Z_Copy }, + + + { Zstring, Cx000E, 0x001C, Z(AnalogDescription), &Z_Copy }, + { Zbool, Cx000E, 0x0051, Z(AnalogOutOfService), &Z_Copy }, + { Zsingle, Cx000E, 0x0055, Z(AnalogValue), &Z_Copy }, + { Zunk, Cx000E, 0x0057, Z(AnalogPriorityArray), &Z_Copy }, + { Zenum8, Cx000E, 0x0067, Z(AnalogReliability), &Z_Copy }, + { Zsingle, Cx000E, 0x0068, Z(AnalogRelinquishDefault),&Z_Copy }, + { Zmap8, Cx000E, 0x006F, Z(AnalogStatusFlags), &Z_Copy }, + { Zenum16, Cx000E, 0x0075, Z(AnalogEngineeringUnits),&Z_Copy }, + { Zuint32, Cx000E, 0x0100, Z(AnalogApplicationType),&Z_Copy }, + + + { Zstring, Cx000F, 0x0004, Z(BinaryInActiveText), &Z_Copy }, + { Zstring, Cx000F, 0x001C, Z(BinaryInDescription), &Z_Copy }, + { Zstring, Cx000F, 0x002E, Z(BinaryInInactiveText),&Z_Copy }, + { Zbool, Cx000F, 0x0051, Z(BinaryInOutOfService),&Z_Copy }, + { Zenum8, Cx000F, 0x0054, Z(BinaryInPolarity), &Z_Copy }, + { Zstring, Cx000F, 0x0055, Z(BinaryInValue), &Z_Copy }, + + { Zenum8, Cx000F, 0x0067, Z(BinaryInReliability), &Z_Copy }, + { Zmap8, Cx000F, 0x006F, Z(BinaryInStatusFlags), &Z_Copy }, + { Zuint32, Cx000F, 0x0100, Z(BinaryInApplicationType),&Z_Copy }, + + + { Zstring, Cx0010, 0x0004, Z(BinaryOutActiveText), &Z_Copy }, + { Zstring, Cx0010, 0x001C, Z(BinaryOutDescription), &Z_Copy }, + { Zstring, Cx0010, 0x002E, Z(BinaryOutInactiveText),&Z_Copy }, + { Zuint32, Cx0010, 0x0042, Z(BinaryOutMinimumOffTime),&Z_Copy }, + { Zuint32, Cx0010, 0x0043, Z(BinaryOutMinimumOnTime),&Z_Copy }, + { Zbool, Cx0010, 0x0051, Z(BinaryOutOutOfService),&Z_Copy }, + { Zenum8, Cx0010, 0x0054, Z(BinaryOutPolarity), &Z_Copy }, + { Zbool, Cx0010, 0x0055, Z(BinaryOutValue), &Z_Copy }, + + { Zenum8, Cx0010, 0x0067, Z(BinaryOutReliability), &Z_Copy }, + { Zbool, Cx0010, 0x0068, Z(BinaryOutRelinquishDefault),&Z_Copy }, + { Zmap8, Cx0010, 0x006F, Z(BinaryOutStatusFlags), &Z_Copy }, + { Zuint32, Cx0010, 0x0100, Z(BinaryOutApplicationType),&Z_Copy }, + + + { Zstring, Cx0011, 0x0004, Z(BinaryActiveText), &Z_Copy }, + { Zstring, Cx0011, 0x001C, Z(BinaryDescription), &Z_Copy }, + { Zstring, Cx0011, 0x002E, Z(BinaryInactiveText), &Z_Copy }, + { Zuint32, Cx0011, 0x0042, Z(BinaryMinimumOffTime), &Z_Copy }, + { Zuint32, Cx0011, 0x0043, Z(BinaryMinimumOnTime), &Z_Copy }, + { Zbool, Cx0011, 0x0051, Z(BinaryOutOfService), &Z_Copy }, + { Zbool, Cx0011, 0x0055, Z(BinaryValue), &Z_Copy }, + + { Zenum8, Cx0011, 0x0067, Z(BinaryReliability), &Z_Copy }, + { Zbool, Cx0011, 0x0068, Z(BinaryRelinquishDefault),&Z_Copy }, + { Zmap8, Cx0011, 0x006F, Z(BinaryStatusFlags), &Z_Copy }, + { Zuint32, Cx0011, 0x0100, Z(BinaryApplicationType),&Z_Copy }, + + + + { Zstring, Cx0012, 0x001C, Z(MultiInDescription), &Z_Copy }, + { Zuint16, Cx0012, 0x004A, Z(MultiInNumberOfStates),&Z_Copy }, + { Zbool, Cx0012, 0x0051, Z(MultiInOutOfService), &Z_Copy }, + { Zuint16, Cx0012, 0x0055, Z(MultiInValue), &Z_AqaraCube }, + { Zenum8, Cx0012, 0x0067, Z(MultiInReliability), &Z_Copy }, + { Zmap8, Cx0012, 0x006F, Z(MultiInStatusFlags), &Z_Copy }, + { Zuint32, Cx0012, 0x0100, Z(MultiInApplicationType),&Z_Copy }, + + + + { Zstring, Cx0013, 0x001C, Z(MultiOutDescription), &Z_Copy }, + { Zuint16, Cx0013, 0x004A, Z(MultiOutNumberOfStates),&Z_Copy }, + { Zbool, Cx0013, 0x0051, Z(MultiOutOutOfService), &Z_Copy }, + { Zuint16, Cx0013, 0x0055, Z(MultiOutValue), &Z_Copy }, + + { Zenum8, Cx0013, 0x0067, Z(MultiOutReliability), &Z_Copy }, + { Zuint16, Cx0013, 0x0068, Z(MultiOutRelinquishDefault),&Z_Copy }, + { Zmap8, Cx0013, 0x006F, Z(MultiOutStatusFlags), &Z_Copy }, + { Zuint32, Cx0013, 0x0100, Z(MultiOutApplicationType),&Z_Copy }, + + + + { Zstring, Cx0014, 0x001C, Z(MultiDescription), &Z_Copy }, + { Zuint16, Cx0014, 0x004A, Z(MultiNumberOfStates), &Z_Copy }, + { Zbool, Cx0014, 0x0051, Z(MultiOutOfService), &Z_Copy }, + { Zuint16, Cx0014, 0x0055, Z(MultiValue), &Z_Copy }, + { Zenum8, Cx0014, 0x0067, Z(MultiReliability), &Z_Copy }, + { Zuint16, Cx0014, 0x0068, Z(MultiRelinquishDefault),&Z_Copy }, + { Zmap8, Cx0014, 0x006F, Z(MultiStatusFlags), &Z_Copy }, + { Zuint32, Cx0014, 0x0100, Z(MultiApplicationType), &Z_Copy }, + + + { Zuint8, Cx001A, 0x0000, Z(TotalProfileNum), &Z_Copy }, + { Zbool, Cx001A, 0x0001, Z(MultipleScheduling), &Z_Copy }, + { Zmap8, Cx001A, 0x0002, Z(EnergyFormatting), &Z_Copy }, + { Zbool, Cx001A, 0x0003, Z(EnergyRemote), &Z_Copy }, + { Zmap8, Cx001A, 0x0004, Z(ScheduleMode), &Z_Copy }, + + + { Zuint32, Cx0020, 0x0000, Z(CheckinInterval), &Z_Copy }, + { Zuint32, Cx0020, 0x0001, Z(LongPollInterval), &Z_Copy }, + { Zuint16, Cx0020, 0x0002, Z(ShortPollInterval), &Z_Copy }, + { Zuint16, Cx0020, 0x0003, Z(FastPollTimeout), &Z_Copy }, + { Zuint32, Cx0020, 0x0004, Z(CheckinIntervalMin), &Z_Copy }, + { Zuint32, Cx0020, 0x0005, Z(LongPollIntervalMin), &Z_Copy }, + { Zuint16, Cx0020, 0x0006, Z(FastPollTimeoutMax), &Z_Copy }, + + + { Zuint16, Cx0100, 0x0000, Z(PhysicalClosedLimit), &Z_Copy }, + { Zuint8, Cx0100, 0x0001, Z(MotorStepSize), &Z_Copy }, + { Zmap8, Cx0100, 0x0002, Z(Status), &Z_Copy }, + { Zuint16, Cx0100, 0x0010, Z(ClosedLimit), &Z_Copy }, + { Zenum8, Cx0100, 0x0011, Z(Mode), &Z_Copy }, + + + { Zenum8, Cx0101, 0x0000, Z(LockState), &Z_Copy }, + { Zenum8, Cx0101, 0x0001, Z(LockType), &Z_Copy }, + { Zbool, Cx0101, 0x0002, Z(ActuatorEnabled), &Z_Copy }, + { Zenum8, Cx0101, 0x0003, Z(DoorState), &Z_Copy }, + { Zuint32, Cx0101, 0x0004, Z(DoorOpenEvents), &Z_Copy }, + { Zuint32, Cx0101, 0x0005, Z(DoorClosedEvents), &Z_Copy }, + { Zuint16, Cx0101, 0x0006, Z(OpenPeriod), &Z_Copy }, + + + { Zuint16, Cx0101, 0x0055, Z(AqaraVibrationMode), &Z_AqaraVibration }, + { Zuint16, Cx0101, 0x0503, Z(AqaraVibrationsOrAngle), &Z_Copy }, + { Zuint32, Cx0101, 0x0505, Z(AqaraVibration505), &Z_Copy }, + { Zuint48, Cx0101, 0x0508, Z(AqaraAccelerometer), &Z_AqaraVibration }, + + + { Zenum8, Cx0102, 0x0000, Z(WindowCoveringType), &Z_Copy }, + { Zuint16, Cx0102, 0x0001, Z(PhysicalClosedLimitLift),&Z_Copy }, + { Zuint16, Cx0102, 0x0002, Z(PhysicalClosedLimitTilt),&Z_Copy }, + { Zuint16, Cx0102, 0x0003, Z(CurrentPositionLift), &Z_Copy }, + { Zuint16, Cx0102, 0x0004, Z(CurrentPositionTilt), &Z_Copy }, + { Zuint16, Cx0102, 0x0005, Z(NumberofActuationsLift),&Z_Copy }, + { Zuint16, Cx0102, 0x0006, Z(NumberofActuationsTilt),&Z_Copy }, + { Zmap8, Cx0102, 0x0007, Z(ConfigStatus), &Z_Copy }, + { Zuint8, Cx0102, 0x0008, Z(CurrentPositionLiftPercentage),&Z_Copy }, + { Zuint8, Cx0102, 0x0009, Z(CurrentPositionTiltPercentage),&Z_Copy }, + { Zuint16, Cx0102, 0x0010, Z(InstalledOpenLimitLift),&Z_Copy }, + { Zuint16, Cx0102, 0x0011, Z(InstalledClosedLimitLift),&Z_Copy }, + { Zuint16, Cx0102, 0x0012, Z(InstalledOpenLimitTilt),&Z_Copy }, + { Zuint16, Cx0102, 0x0013, Z(InstalledClosedLimitTilt),&Z_Copy }, + { Zuint16, Cx0102, 0x0014, Z(VelocityLift), &Z_Copy }, + { Zuint16, Cx0102, 0x0015, Z(AccelerationTimeLift),&Z_Copy }, + { Zuint16, Cx0102, 0x0016, Z(DecelerationTimeLift), &Z_Copy }, + { Zmap8, Cx0102, 0x0017, Z(Mode), &Z_Copy }, + { Zoctstr, Cx0102, 0x0018, Z(IntermediateSetpointsLift),&Z_Copy }, + { Zoctstr, Cx0102, 0x0019, Z(IntermediateSetpointsTilt),&Z_Copy }, + + + { Zuint8, Cx0300, 0x0000, Z(Hue), &Z_Copy }, + { Zuint8, Cx0300, 0x0001, Z(Sat), &Z_Copy }, + { Zuint16, Cx0300, 0x0002, Z(RemainingTime), &Z_Copy }, + { Zuint16, Cx0300, 0x0003, Z(X), &Z_Copy }, + { Zuint16, Cx0300, 0x0004, Z(Y), &Z_Copy }, + { Zenum8, Cx0300, 0x0005, Z(DriftCompensation), &Z_Copy }, + { Zstring, Cx0300, 0x0006, Z(CompensationText), &Z_Copy }, + { Zuint16, Cx0300, 0x0007, Z(CT), &Z_Copy }, + { Zenum8, Cx0300, 0x0008, Z(ColorMode), &Z_Copy }, + { Zuint8, Cx0300, 0x0010, Z(NumberOfPrimaries), &Z_Copy }, + { Zuint16, Cx0300, 0x0011, Z(Primary1X), &Z_Copy }, + { Zuint16, Cx0300, 0x0012, Z(Primary1Y), &Z_Copy }, + { Zuint8, Cx0300, 0x0013, Z(Primary1Intensity), &Z_Copy }, + { Zuint16, Cx0300, 0x0015, Z(Primary2X), &Z_Copy }, + { Zuint16, Cx0300, 0x0016, Z(Primary2Y), &Z_Copy }, + { Zuint8, Cx0300, 0x0017, Z(Primary2Intensity), &Z_Copy }, + { Zuint16, Cx0300, 0x0019, Z(Primary3X), &Z_Copy }, + { Zuint16, Cx0300, 0x001A, Z(Primary3Y), &Z_Copy }, + { Zuint8, Cx0300, 0x001B, Z(Primary3Intensity), &Z_Copy }, + { Zuint16, Cx0300, 0x0030, Z(WhitePointX), &Z_Copy }, + { Zuint16, Cx0300, 0x0031, Z(WhitePointY), &Z_Copy }, + { Zuint16, Cx0300, 0x0032, Z(ColorPointRX), &Z_Copy }, + { Zuint16, Cx0300, 0x0033, Z(ColorPointRY), &Z_Copy }, + { Zuint8, Cx0300, 0x0034, Z(ColorPointRIntensity), &Z_Copy }, + { Zuint16, Cx0300, 0x0036, Z(ColorPointGX), &Z_Copy }, + { Zuint16, Cx0300, 0x0037, Z(ColorPointGY), &Z_Copy }, + { Zuint8, Cx0300, 0x0038, Z(ColorPointGIntensity), &Z_Copy }, + { Zuint16, Cx0300, 0x003A, Z(ColorPointBX), &Z_Copy }, + { Zuint16, Cx0300, 0x003B, Z(ColorPointBY), &Z_Copy }, + { Zuint8, Cx0300, 0x003C, Z(ColorPointBIntensity), &Z_Copy }, + + + { Zuint16, Cx0400, 0x0000, Z(Illuminance), &Z_Copy }, + { Zuint16, Cx0400, 0x0001, Z(IlluminanceMinMeasuredValue), &Z_Copy }, + { Zuint16, Cx0400, 0x0002, Z(IlluminanceMaxMeasuredValue), &Z_Copy }, + { Zuint16, Cx0400, 0x0003, Z(IlluminanceTolerance), &Z_Copy }, + { Zenum8, Cx0400, 0x0004, Z(IlluminanceLightSensorType), &Z_Copy }, + { Zunk, Cx0400, 0xFFFF, nullptr, &Z_Remove }, + + + { Zenum8, Cx0401, 0x0000, Z(IlluminanceLevelStatus), &Z_Copy }, + { Zenum8, Cx0401, 0x0001, Z(IlluminanceLightSensorType), &Z_Copy }, + { Zuint16, Cx0401, 0x0010, Z(IlluminanceTargetLevel), &Z_Copy }, + { Zunk, Cx0401, 0xFFFF, nullptr, &Z_Remove }, + + + { Zint16, Cx0402, 0x0000, Z(Temperature), &Z_FloatDiv100 }, + { Zint16, Cx0402, 0x0001, Z(TemperatureMinMeasuredValue), &Z_FloatDiv100 }, + { Zint16, Cx0402, 0x0002, Z(TemperatureMaxMeasuredValue), &Z_FloatDiv100 }, + { Zuint16, Cx0402, 0x0003, Z(TemperatureTolerance), &Z_FloatDiv100 }, + { Zunk, Cx0402, 0xFFFF, nullptr, &Z_Remove }, + + + { Zunk, Cx0403, 0x0000, Z(PressureUnit), &Z_AddPressureUnit }, + { Zint16, Cx0403, 0x0000, Z(Pressure), &Z_Copy }, + { Zint16, Cx0403, 0x0001, Z(PressureMinMeasuredValue), &Z_Copy }, + { Zint16, Cx0403, 0x0002, Z(PressureMaxMeasuredValue), &Z_Copy }, + { Zuint16, Cx0403, 0x0003, Z(PressureTolerance), &Z_Copy }, + { Zint16, Cx0403, 0x0010, Z(PressureScaledValue), &Z_Copy }, + { Zint16, Cx0403, 0x0011, Z(PressureMinScaledValue), &Z_Copy }, + { Zint16, Cx0403, 0x0012, Z(PressureMaxScaledValue), &Z_Copy }, + { Zuint16, Cx0403, 0x0013, Z(PressureScaledTolerance), &Z_Copy }, + { Zint8, Cx0403, 0x0014, Z(PressureScale), &Z_Copy }, + { Zunk, Cx0403, 0xFFFF, nullptr, &Z_Remove }, + + + { Zuint16, Cx0404, 0x0000, Z(FlowRate), &Z_FloatDiv10 }, + { Zuint16, Cx0404, 0x0001, Z(FlowMinMeasuredValue), &Z_Copy }, + { Zuint16, Cx0404, 0x0002, Z(FlowMaxMeasuredValue), &Z_Copy }, + { Zuint16, Cx0404, 0x0003, Z(FlowTolerance), &Z_Copy }, + { Zunk, Cx0404, 0xFFFF, nullptr, &Z_Remove }, + + + { Zuint16, Cx0405, 0x0000, Z(Humidity), &Z_FloatDiv100 }, + { Zuint16, Cx0405, 0x0001, Z(HumidityMinMeasuredValue), &Z_Copy }, + { Zuint16, Cx0405, 0x0002, Z(HumidityMaxMeasuredValue), &Z_Copy }, + { Zuint16, Cx0405, 0x0003, Z(HumidityTolerance), &Z_Copy }, + { Zunk, Cx0405, 0xFFFF, nullptr, &Z_Remove }, + + + { Zmap8, Cx0406, 0x0000, Z(Occupancy), &Z_Copy }, + { Zenum8, Cx0406, 0x0001, Z(OccupancySensorType), &Z_Copy }, + { Zunk, Cx0406, 0xFFFF, nullptr, &Z_Remove }, + + + { Zstring, Cx0B01, 0x0000, Z(CompanyName), &Z_Copy }, + { Zuint16, Cx0B01, 0x0001, Z(MeterTypeID), &Z_Copy }, + { Zuint16, Cx0B01, 0x0004, Z(DataQualityID), &Z_Copy }, + { Zstring, Cx0B01, 0x0005, Z(CustomerName), &Z_Copy }, + { Zoctstr, Cx0B01, 0x0006, Z(Model), &Z_Copy }, + { Zoctstr, Cx0B01, 0x0007, Z(PartNumber), &Z_Copy }, + { Zoctstr, Cx0B01, 0x0008, Z(ProductRevision), &Z_Copy }, + { Zoctstr, Cx0B01, 0x000A, Z(SoftwareRevision), &Z_Copy }, + { Zstring, Cx0B01, 0x000B, Z(UtilityName), &Z_Copy }, + { Zstring, Cx0B01, 0x000C, Z(POD), &Z_Copy }, + { Zint24, Cx0B01, 0x000D, Z(AvailablePower), &Z_Copy }, + { Zint24, Cx0B01, 0x000E, Z(PowerThreshold), &Z_Copy }, + + + { Zuint16, Cx0B05, 0x0000, Z(NumberOfResets), &Z_Copy }, + { Zuint16, Cx0B05, 0x0001, Z(PersistentMemoryWrites),&Z_Copy }, + { Zuint8, Cx0B05, 0x011C, Z(LastMessageLQI), &Z_Copy }, + { Zuint8, Cx0B05, 0x011D, Z(LastMessageRSSI), &Z_Copy }, + +}; + + + +int32_t Z_ManufKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + json[new_name] = value; + zigbee_devices.setManufId(shortaddr, value.as()); + return 1; +} + +int32_t Z_ModelKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + json[new_name] = value; + zigbee_devices.setModelId(shortaddr, value.as()); + return 1; +} + + + +int32_t Z_Remove(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + return 1; +} + + +int32_t Z_Copy(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + json[new_name] = value; + return 1; +} + + +int32_t Z_AddPressureUnit(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + json[new_name] = F(D_UNIT_PRESSURE); + return 0; +} + + +int32_t Z_FloatDiv100(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + json[new_name] = ((float)value) / 100.0f; + return 1; +} + +int32_t Z_FloatDiv10(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + json[new_name] = ((float)value) / 10.0f; + return 1; +} + +int32_t Z_FloatDiv2(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + json[new_name] = ((float)value) / 2.0f; + return 1; +} + + +int32_t Z_OccupancyCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.createObject(); + json[F(OCCUPANCY)] = 0; + zigbee_devices.jsonPublishNow(shortaddr, json); +} + + +int32_t Z_AqaraCube(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + json[new_name] = value; + int32_t val = value; + const __FlashStringHelper *aqara_cube = F("AqaraCube"); + const __FlashStringHelper *aqara_cube_side = F("AqaraCubeSide"); + const __FlashStringHelper *aqara_cube_from_side = F("AqaraCubeFromSide"); + + switch (val) { + case 0: + json[aqara_cube] = F("shake"); + break; + case 2: + json[aqara_cube] = F("wakeup"); + break; + case 3: + json[aqara_cube] = F("fall"); + break; + case 64 ... 127: + json[aqara_cube] = F("flip90"); + json[aqara_cube_side] = val % 8; + json[aqara_cube_from_side] = (val - 64) / 8; + break; + case 128 ... 132: + json[aqara_cube] = F("flip180"); + json[aqara_cube_side] = val - 128; + break; + case 256 ... 261: + json[aqara_cube] = F("slide"); + json[aqara_cube_side] = val - 256; + break; + case 512 ... 517: + json[aqara_cube] = F("tap"); + json[aqara_cube_side] = val - 512; + break; + } +# 1154 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" + return 1; +} + + +int32_t Z_AqaraVibration(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + + switch (attr) { + case 0x0055: + { + int32_t ivalue = value; + const __FlashStringHelper * svalue; + switch (ivalue) { + case 1: svalue = F("vibrate"); break; + case 2: svalue = F("tilt"); break; + case 3: svalue = F("drop"); break; + default: svalue = F("unknown"); break; + } + json[new_name] = svalue; + } + break; + + + + + case 0x0508: + { + + + String hex = value; + SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); + int16_t x, y, z; + z = buf2.get16(0); + y = buf2.get16(2); + x = buf2.get16(4); + JsonArray& xyz = json.createNestedArray(new_name); + xyz.add(x); + xyz.add(y); + xyz.add(z); + + float X = x; + float Y = y; + float Z = z; + int32_t Angle_X = 0.5f + atanf(X/sqrtf(z*z+y*y)) * f_180pi; + int32_t Angle_Y = 0.5f + atanf(Y/sqrtf(x*x+z*z)) * f_180pi; + int32_t Angle_Z = 0.5f + atanf(Z/sqrtf(x*x+y*y)) * f_180pi; + JsonArray& angles = json.createNestedArray(F("AqaraAngles")); + angles.add(Angle_X); + angles.add(Angle_Y); + angles.add(Angle_Z); + } + break; + } + return 1; +} + +int32_t Z_AqaraSensor(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + String hex = value; + SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); + uint32_t i = 0; + uint32_t len = buf2.len(); + char tmp[] = "tmp"; + + JsonVariant sub_value; + const char * modelId_c = zigbee_devices.getModelId(shortaddr); + String modelId((char*) modelId_c); + + while (len - i >= 2) { + uint8_t attrid = buf2.get8(i++); + + i += parseSingleAttribute(json, tmp, buf2, i, len); + float val = json[tmp]; + json.remove(tmp); + bool translated = false; + if (0x01 == attrid) { + json[F(D_JSON_VOLTAGE)] = val / 1000.0f; + json[F("Battery")] = toPercentageCR2032(val); + } else if ((nullptr != modelId) && (0 == zcl->getManufCode())) { + translated = true; + if (modelId.startsWith(F("lumi.sensor_ht")) || + modelId.startsWith(F("lumi.weather"))) { + + + if (0x64 == attrid) { + json[F(D_JSON_TEMPERATURE)] = val / 100.0f; + } else if (0x65 == attrid) { + json[F(D_JSON_HUMIDITY)] = val / 100.0f; + } else if (0x66 == attrid) { + json[F(D_JSON_PRESSURE)] = val / 100.0f; + json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); + } + } else if (modelId.startsWith(F("lumi.sensor_smoke"))) { + if (0x64 == attrid) { + json[F("SmokeDensity")] = val; + } + } else if (modelId.startsWith(F("lumi.sensor_natgas"))) { + if (0x64 == attrid) { + json[F("GasDensity")] = val; + } + } else { + translated = false; + } + + } + if (!translated) { + if (attrid >= 100) { + char attr_name[12]; + snprintf_P(attr_name, sizeof(attr_name), PSTR("Xiaomi_%02X"), attrid); + json[attr_name] = val; + } + } + } + return 1; +} + + +void ZCLFrame::postProcessAttributes(uint16_t shortaddr, JsonObject& json) { + + for (auto kv : json) { + String key_string = kv.key; + const char * key = key_string.c_str(); + JsonVariant& value = kv.value; + + char * delimiter = strchr(key, '/'); + char * delimiter2 = strchr(key, '+'); + if (delimiter) { + uint16_t attribute; + uint16_t suffix = 1; + uint16_t cluster = strtoul(key, &delimiter, 16); + if (!delimiter2) { + attribute = strtoul(delimiter+1, nullptr, 16); + } else { + attribute = strtoul(delimiter+1, &delimiter2, 16); + suffix = strtoul(delimiter2+1, nullptr, 10); + } + + + if ((cluster == 0x0006) && ((attribute == 0x0000) || (attribute == 0x8000))) { + bool power = value; + zigbee_devices.updateHueState(shortaddr, &power, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr); + } else if ((cluster == 0x0008) && (attribute == 0x0000)) { + uint8_t dimmer = value; + zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, &dimmer, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr); + } else if ((cluster == 0x0300) && (attribute == 0x0000)) { + uint16_t hue8 = value; + uint16_t hue = changeUIntScale(hue8, 0, 254, 0, 360); + zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, nullptr, nullptr, + nullptr, &hue, nullptr, nullptr, nullptr); + } else if ((cluster == 0x0300) && (attribute == 0x0001)) { + uint8_t sat = value; + zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, nullptr, &sat, + nullptr, nullptr, nullptr, nullptr, nullptr); + } else if ((cluster == 0x0300) && (attribute == 0x0003)) { + uint16_t x = value; + zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, &x, nullptr, nullptr); + } else if ((cluster == 0x0300) && (attribute == 0x0004)) { + uint16_t y = value; + zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, &y, nullptr), nullptr; + } else if ((cluster == 0x0300) && (attribute == 0x0007)) { + uint16_t ct = value; + zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, nullptr, nullptr, + &ct, nullptr, nullptr, nullptr, nullptr); + } else if ((cluster == 0x0300) && (attribute == 0x0008)) { + uint8_t colormode = value; + zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr); + } + + + for (uint32_t i = 0; i < sizeof(Z_PostProcess) / sizeof(Z_PostProcess[0]); i++) { + const Z_AttributeConverter *converter = &Z_PostProcess[i]; + uint16_t conv_cluster = CxToCluster(pgm_read_byte(&converter->cluster_short)); + uint16_t conv_attribute = pgm_read_word(&converter->attribute); + + if ((conv_cluster == cluster) && + ((conv_attribute == attribute) || (conv_attribute == 0xFFFF)) ) { + String new_name_str = (const __FlashStringHelper*) converter->name; + if (suffix > 1) { new_name_str += suffix; } + int32_t drop = (*converter->func)(this, shortaddr, json, key, value, new_name_str, conv_cluster, conv_attribute); + if (drop) { + json.remove(key); + } + + } + } + } + } +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_6_commands.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_6_commands.ino" +#ifdef USE_ZIGBEE + + + + + +typedef struct Z_CommandConverter { + const char * tasmota_cmd; + uint16_t cluster; + uint8_t cmd; + uint8_t direction; + const char * param; +} Z_CommandConverter; + +typedef struct Z_XYZ_Var { + uint32_t x = 0; + uint32_t y = 0; + uint32_t z = 0; + uint8_t x_type = 0; + uint8_t y_type = 0; + uint8_t z_type = 0; +} Z_XYZ_Var; + +ZF(AddGroup) ZF(ViewGroup) ZF(GetGroup) ZF(GetAllGroups) ZF(RemoveGroup) ZF(RemoveAllGroups) +ZF(AddScene) ZF(ViewScene) ZF(RemoveScene) ZF(RemoveAllScenes) ZF(RecallScene) ZF(StoreScene) ZF(GetSceneMembership) + +ZF(DimmerUp) ZF(DimmerDown) ZF(DimmerStop) +ZF(ResetAlarm) ZF(ResetAllAlarms) + +ZF(HueSat) ZF(Color) +ZF(ShutterOpen) ZF(ShutterClose) ZF(ShutterStop) ZF(ShutterLift) ZF(ShutterTilt) ZF(Shutter) + +ZF(DimmerMove) ZF(DimmerStep) +ZF(HueMove) ZF(HueStep) ZF(SatMove) ZF(SatStep) ZF(ColorMove) ZF(ColorStep) +ZF(ArrowClick) ZF(ArrowHold) ZF(ArrowRelease) ZF(ZoneStatusChange) + +ZF(xxxx00) ZF(xxxx) ZF(01xxxx) ZF(00) ZF(01) ZF() ZF(xxxxyy) ZF(00190200) ZF(01190200) ZF(xxyyyy) ZF(xx) +ZF(xx000A00) ZF(xx0A00) ZF(xxyy0A00) ZF(xxxxyyyy0A00) ZF(xxxx0A00) ZF(xx0A) +ZF(xx190A00) ZF(xx19) ZF(xx190A) ZF(xxxxyyyy) ZF(xxxxyyzz) ZF(xxyyzzzz) ZF(xxyyyyzz) +# 67 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_6_commands.ino" +const Z_CommandConverter Z_Commands[] PROGMEM = { + + { Z(AddGroup), 0x0004, 0x00, 0x01, Z(xxxx00) }, + { Z(ViewGroup), 0x0004, 0x01, 0x01, Z(xxxx) }, + { Z(GetGroup), 0x0004, 0x02, 0x01, Z(01xxxx) }, + { Z(GetAllGroups), 0x0004, 0x02, 0x01, Z(00) }, + { Z(RemoveGroup), 0x0004, 0x03, 0x01, Z(xxxx) }, + { Z(RemoveAllGroups),0x0004, 0x04, 0x01, Z() }, + + + { Z(ViewScene), 0x0005, 0x01, 0x01, Z(xxxxyy) }, + { Z(RemoveScene), 0x0005, 0x02, 0x01, Z(xxxxyy) }, + { Z(RemoveAllScenes),0x0005, 0x03, 0x01, Z(xxxx) }, + { Z(RecallScene), 0x0005, 0x05, 0x01, Z(xxxxyy) }, + { Z(GetSceneMembership),0x0005, 0x06, 0x01, Z(xxxx) }, + + { Z(Power), 0x0006, 0xFF, 0x01, Z() }, + { Z(Dimmer), 0x0008, 0x04, 0x01, Z(xx0A00) }, + { Z(DimmerUp), 0x0008, 0x06, 0x01, Z(00190200) }, + { Z(DimmerDown), 0x0008, 0x06, 0x01, Z(01190200) }, + { Z(DimmerStop), 0x0008, 0x03, 0x01, Z() }, + { Z(ResetAlarm), 0x0009, 0x00, 0x01, Z(xxyyyy) }, + { Z(ResetAllAlarms), 0x0009, 0x01, 0x01, Z() }, + { Z(Hue), 0x0300, 0x00, 0x01, Z(xx000A00) }, + { Z(Sat), 0x0300, 0x03, 0x01, Z(xx0A00) }, + { Z(HueSat), 0x0300, 0x06, 0x01, Z(xxyy0A00) }, + { Z(Color), 0x0300, 0x07, 0x01, Z(xxxxyyyy0A00) }, + { Z(CT), 0x0300, 0x0A, 0x01, Z(xxxx0A00) }, + { Z(ShutterOpen), 0x0102, 0x00, 0x01, Z() }, + { Z(ShutterClose), 0x0102, 0x01, 0x01, Z() }, + { Z(ShutterStop), 0x0102, 0x02, 0x01, Z() }, + { Z(ShutterLift), 0x0102, 0x05, 0x01, Z(xx) }, + { Z(ShutterTilt), 0x0102, 0x08, 0x01, Z(xx) }, + { Z(Shutter), 0x0102, 0xFF, 0x01, Z() }, + + { Z(Occupancy), 0xEF00, 0x01, 0x82, Z()}, + + { Z(Dimmer), 0x0008, 0x00, 0x01, Z(xx) }, + { Z(DimmerMove), 0x0008, 0x01, 0x01, Z(xx0A) }, + { Z(DimmerStep), 0x0008, 0x02, 0x01, Z(xx190A00) }, + { Z(DimmerMove), 0x0008, 0x05, 0x01, Z(xx0A) }, + { Z(DimmerUp), 0x0008, 0x06, 0x01, Z(00) }, + { Z(DimmerDown), 0x0008, 0x06, 0x01, Z(01) }, + { Z(DimmerStop), 0x0008, 0x07, 0x01, Z() }, + { Z(HueMove), 0x0300, 0x01, 0x01, Z(xx19) }, + { Z(HueStep), 0x0300, 0x02, 0x01, Z(xx190A00) }, + { Z(SatMove), 0x0300, 0x04, 0x01, Z(xx19) }, + { Z(SatStep), 0x0300, 0x05, 0x01, Z(xx190A) }, + { Z(ColorMove), 0x0300, 0x08, 0x01, Z(xxxxyyyy) }, + { Z(ColorStep), 0x0300, 0x09, 0x01, Z(xxxxyyyy0A00) }, + + { Z(ArrowClick), 0x0005, 0x07, 0x01, Z(xx) }, + { Z(ArrowHold), 0x0005, 0x08, 0x01, Z(xx) }, + { Z(ArrowRelease), 0x0005, 0x09, 0x01, Z() }, + + { Z(ZoneStatusChange),0x0500, 0x00, 0x82, Z(xxxxyyzz) }, + + { Z(AddGroup), 0x0004, 0x00, 0x82, Z(xxyyyy) }, + { Z(ViewGroup), 0x0004, 0x01, 0x82, Z(xxyyyy) }, + { Z(GetGroup), 0x0004, 0x02, 0x82, Z(xxyyzzzz) }, + { Z(RemoveGroup), 0x0004, 0x03, 0x82, Z(xxyyyy) }, + + { Z(AddScene), 0x0005, 0x00, 0x82, Z(xxyyyyzz) }, + { Z(ViewScene), 0x0005, 0x01, 0x82, Z(xxyyyyzz) }, + { Z(RemoveScene), 0x0005, 0x02, 0x82, Z(xxyyyyzz) }, + { Z(RemoveAllScenes),0x0005, 0x03, 0x82, Z(xxyyyy) }, + { Z(StoreScene), 0x0005, 0x04, 0x82, Z(xxyyyyzz) }, + { Z(GetSceneMembership),0x0005, 0x06, 0x82,Z(xxyyzzzz) }, +}; + + + + +#define ZLE(x) ((x) & 0xFF), ((x) >> 8) + + +const uint8_t CLUSTER_0006[] = { ZLE(0x0000) }; +const uint8_t CLUSTER_0008[] = { ZLE(0x0000) }; +const uint8_t CLUSTER_0009[] = { ZLE(0x0000) }; +const uint8_t CLUSTER_0300[] = { ZLE(0x0000), ZLE(0x0001), ZLE(0x0003), ZLE(0x0004), ZLE(0x0007), ZLE(0x0008) }; + + +int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { + size_t attrs_len = 0; + const uint8_t* attrs = nullptr; + + switch (cluster) { + case 0x0006: + attrs = CLUSTER_0006; + attrs_len = sizeof(CLUSTER_0006); + break; + case 0x0008: + attrs = CLUSTER_0008; + attrs_len = sizeof(CLUSTER_0008); + break; + case 0x0009: + attrs = CLUSTER_0009; + attrs_len = sizeof(CLUSTER_0009); + break; + case 0x0300: + attrs = CLUSTER_0300; + attrs_len = sizeof(CLUSTER_0300); + break; + } + if (attrs) { + ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, 0, attrs, attrs_len, true , zigbee_devices.getNextSeqNumber(shortaddr)); + } +} + + + +int32_t Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { + if (shortaddr) { + zigbee_devices.setReachable(shortaddr, false); + } +} + + +void zigbeeSetCommandTimer(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint) { + uint32_t wait_ms = 0; + + switch (cluster) { + case 0x0006: + case 0x0009: + wait_ms = 200; + break; + case 0x0008: + case 0x0300: + wait_ms = 1050; + break; + case 0x0102: + wait_ms = 10000; + break; + } + if (wait_ms) { + zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, Z_CAT_NONE, 0 , &Z_ReadAttrCallback); + if (shortaddr) { + zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 , &Z_Unreachable); + } + } +} + + +inline bool isXYZ(char c) { + return (c >= 'x') && (c <= 'z'); +} + + + + +inline int8_t hexValue(char c) { + if ((c >= '0') && (c <= '9')) { + return c - '0'; + } + if ((c >= 'A') && (c <= 'F')) { + return 10 + c - 'A'; + } + if ((c >= 'a') && (c <= 'f')) { + return 10 + c - 'a'; + } + return -1; +} + + +uint32_t parseHex_P(const char **data, size_t max_len = 8) { + uint32_t ret = 0; + for (uint32_t i = 0; i < max_len; i++) { + int8_t v = hexValue(pgm_read_byte(*data)); + if (v < 0) { break; } + ret = (ret << 4) | v; + *data += 1; + } + return ret; +} + + + + + +void parseXYZ(const char *model, const SBuffer &payload, struct Z_XYZ_Var *xyz) { + const char *p = model; + uint32_t v = 0; + char c = pgm_read_byte(p); + while (c) { + char c1 = pgm_read_byte(p+1); + if (!c1) { break; } + if (isXYZ(c) && (c == c1) && (v < payload.len())) { + uint8_t val = payload.get8(v); + switch (c) { + case 'x': + xyz->x = xyz->x | (val << (xyz->x_type * 8)); + xyz->x_type++; + break; + case 'y': + xyz->y = xyz->y | (val << (xyz->y_type * 8)); + xyz->y_type++; + break; + case 'z': + xyz->z = xyz->z | (val << (xyz->z_type * 8)); + xyz->z_type++; + break; + } + } + p += 2; + v++; + c = pgm_read_byte(p); + } +} + + + + + + +void sendHueUpdate(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t cmd, bool direction) { + if (direction) { return; } + + int32_t z_cat = -1; + uint32_t wait_ms = 0; + + switch (cluster) { + case 0x0006: + z_cat = Z_CAT_READ_0006; + wait_ms = 200; + break; + case 0x0008: + z_cat = Z_CAT_READ_0008; + wait_ms = 1050; + break; + case 0x0102: + z_cat = Z_CAT_READ_0102; + wait_ms = 10000; + break; + case 0x0300: + z_cat = Z_CAT_READ_0300; + wait_ms = 1050; + break; + default: + break; + } + if (z_cat >= 0) { + uint8_t endpoint = 0; + if (shortaddr) { + endpoint = zigbee_devices.findFirstEndpoint(shortaddr); + } + if ((!shortaddr) || (endpoint)) { + zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, z_cat, 0 , &Z_ReadAttrCallback); + if (shortaddr) { + zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 , &Z_Unreachable); + } + + } + } +} + + + +void convertClusterSpecific(JsonObject& json, uint16_t cluster, uint8_t cmd, bool direction, const SBuffer &payload) { + size_t hex_char_len = payload.len()*2+2; + char *hex_char = (char*) malloc(hex_char_len); + if (!hex_char) { return; } + ToHex_P((unsigned char*)payload.getBuffer(), payload.len(), hex_char, hex_char_len); + + const __FlashStringHelper* command_name = nullptr; + uint8_t conv_direction; + Z_XYZ_Var xyz; + + + for (uint32_t i = 0; i < sizeof(Z_Commands) / sizeof(Z_Commands[0]); i++) { + const Z_CommandConverter *conv = &Z_Commands[i]; + uint16_t conv_cluster = pgm_read_word(&conv->cluster); + if (conv_cluster == cluster) { + + uint8_t conv_cmd = pgm_read_byte(&conv->cmd); + conv_direction = pgm_read_byte(&conv->direction); + if ((0xFF == conv_cmd) || (cmd == conv_cmd)) { + + if ((direction && (conv_direction & 0x02)) || (!direction && (conv_direction & 0x01))) { + + + + + const char * p = conv->param; + + bool match = true; + for (uint8_t i = 0; i < payload.len(); i++) { + const char c1 = pgm_read_byte(p); + const char c2 = pgm_read_byte(p+1); + + if ((0x00 == c1) || isXYZ(c1)) { + break; + } + const char * p2 = p; + uint32_t nextbyte = parseHex_P(&p2, 2); + + if (nextbyte != payload.get8(i)) { + match = false; + break; + } + p += 2; + } + if (match) { + command_name = (const __FlashStringHelper*) conv->tasmota_cmd; + parseXYZ(conv->param, payload, &xyz); + if (0xFF == conv_cmd) { + + xyz.z = xyz.y; + xyz.z_type = xyz.y_type; + xyz.y = xyz.x; + xyz.y_type = xyz.x_type; + xyz.x = cmd; + xyz.x_type = 1; + } + break; + } + } + } + } + } + + + + + char attrid_str[12]; + snprintf_P(attrid_str, sizeof(attrid_str), PSTR("%04X%c%02X"), cluster, direction ? '<' : '!', cmd); + json[attrid_str] = hex_char; + free(hex_char); + + if (command_name) { + + + if (conv_direction & 0x80) { + + + String command_name2 = String(command_name); + if ((cluster == 0x0500) && (cmd == 0x00)) { + + json[command_name] = xyz.x; + json[command_name2 + F("Ext")] = xyz.y; + json[command_name2 + F("Zone")] = xyz.z; + } else if ((cluster == 0x0004) && ((cmd == 0x00) || (cmd == 0x01) || (cmd == 0x03))) { + + json[command_name] = xyz.y; + json[command_name2 + F("Status")] = xyz.x; + json[command_name2 + F("StatusMsg")] = getZigbeeStatusMessage(xyz.x); + } else if ((cluster == 0x0004) && (cmd == 0x02)) { + + json[command_name2 + F("Capacity")] = xyz.x; + json[command_name2 + F("Count")] = xyz.y; + JsonArray &arr = json.createNestedArray(command_name); + for (uint32_t i = 0; i < xyz.y; i++) { + arr.add(payload.get16(2 + 2*i)); + } + } else if ((cluster == 0x0005) && ((cmd == 0x00) || (cmd == 0x02) || (cmd == 0x03))) { + + json[command_name2 + F("Status")] = xyz.x; + json[command_name2 + F("StatusMsg")] = getZigbeeStatusMessage(xyz.x); + json[F("GroupId")] = xyz.y; + json[F("SceneId")] = xyz.z; + } else if ((cluster == 0x0005) && (cmd == 0x01)) { + + json[command_name2 + F("Status")] = xyz.x; + json[command_name2 + F("StatusMsg")] = getZigbeeStatusMessage(xyz.x); + json[F("GroupId")] = xyz.y; + json[F("SceneId")] = xyz.z; + String scene_payload = json[attrid_str]; + json[F("ScenePayload")] = scene_payload.substring(8); + } else if ((cluster == 0x0005) && (cmd == 0x03)) { + + json[command_name2 + F("Status")] = xyz.x; + json[command_name2 + F("StatusMsg")] = getZigbeeStatusMessage(xyz.x); + json[F("GroupId")] = xyz.y; + } else if ((cluster == 0x0005) && (cmd == 0x06)) { + + json[command_name2 + F("Status")] = xyz.x; + json[command_name2 + F("StatusMsg")] = getZigbeeStatusMessage(xyz.x); + json[F("Capacity")] = xyz.y; + json[F("GroupId")] = xyz.z; + String scene_payload = json[attrid_str]; + json[F("ScenePayload")] = scene_payload.substring(8); + } + } else { + if (0 == xyz.x_type) { + json[command_name] = true; + } else if (0 == xyz.y_type) { + json[command_name] = xyz.x; + } else { + + JsonArray &arr = json.createNestedArray(command_name); + arr.add(xyz.x); + arr.add(xyz.y); + if (xyz.z_type) { + arr.add(xyz.z); + } + } + } + } +} + + + + + +const __FlashStringHelper* zigbeeFindCommand(const char *command, uint16_t *cluster, uint16_t *cmd) { + for (uint32_t i = 0; i < sizeof(Z_Commands) / sizeof(Z_Commands[0]); i++) { + const Z_CommandConverter *conv = &Z_Commands[i]; + uint8_t conv_direction = pgm_read_byte(&conv->direction); + uint8_t conv_cmd = pgm_read_byte(&conv->cmd); + uint16_t conv_cluster = pgm_read_word(&conv->cluster); + if ((conv_direction & 0x01) && (0 == strcasecmp_P(command, conv->tasmota_cmd))) { + *cluster = conv_cluster; + *cmd = conv_cmd; + return (const __FlashStringHelper*) conv->param; + } + } + + return nullptr; +} + + +inline char hexDigit(uint32_t h) { + uint32_t nybble = h & 0x0F; + return (nybble > 9) ? 'A' - 10 + nybble : '0' + nybble; +} + + +String zigbeeCmdAddParams(const char *zcl_cmd_P, uint32_t x, uint32_t y, uint32_t z) { + size_t len = strlen_P(zcl_cmd_P); + char zcl_cmd[len+1]; + strcpy_P(zcl_cmd, zcl_cmd_P); + + char *p = zcl_cmd; + while (*p) { + if (isXYZ(*p) && (*p == *(p+1))) { + uint8_t val; + switch (*p) { + case 'x': + val = x & 0xFF; + x = x >> 8; + break; + case 'y': + val = y & 0xFF; + y = y >> 8; + break; + case 'z': + val = z & 0xFF; + z = z >> 8; + break; + } + *p = hexDigit(val >> 4); + *(p+1) = hexDigit(val); + p++; + } + p++; + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SendZCLCommand_P: zcl_cmd = %s"), zcl_cmd); + + return String(zcl_cmd); +} + +const char kZ_Alias[] PROGMEM = "OFF|" D_OFF "|" D_FALSE "|" D_STOP "|" "OPEN" "|" + "ON|" D_ON "|" D_TRUE "|" D_START "|" "CLOSE" "|" + "TOGGLE|" D_TOGGLE "|" + "ALL" ; + +const uint8_t kZ_Numbers[] PROGMEM = { 0,0,0,0,0, + 1,1,1,1,1, + 2,2, + 255 }; + + +uint32_t ZigbeeAliasOrNumber(const char *state_text) { + char command[16]; + int state_number = GetCommandCode(command, sizeof(command), state_text, kZ_Alias); + if (state_number >= 0) { + + return pgm_read_byte(kZ_Numbers + state_number); + } else { + + return strtoul(state_text, nullptr, 0); + } +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" +#ifdef USE_ZIGBEE + + + +const uint8_t ZIGBEE_STATUS_OK = 0; +const uint8_t ZIGBEE_STATUS_BOOT = 1; +const uint8_t ZIGBEE_STATUS_RESET_CONF = 2; +const uint8_t ZIGBEE_STATUS_STARTING = 3; +const uint8_t ZIGBEE_STATUS_PERMITJOIN_CLOSE = 20; +const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_60 = 21; +const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_XX = 22; +const uint8_t ZIGBEE_STATUS_DEVICE_ANNOUNCE = 30; +const uint8_t ZIGBEE_STATUS_NODE_DESC = 31; +const uint8_t ZIGBEE_STATUS_ACTIVE_EP = 32; +const uint8_t ZIGBEE_STATUS_SIMPLE_DESC = 33; +const uint8_t ZIGBEE_STATUS_DEVICE_INDICATION = 34; +const uint8_t ZIGBEE_STATUS_CC_VERSION = 50; +const uint8_t ZIGBEE_STATUS_CC_INFO = 51; +const uint8_t ZIGBEE_STATUS_UNSUPPORTED_VERSION = 98; +const uint8_t ZIGBEE_STATUS_ABORT = 99; + +typedef int32_t (*ZB_Func)(uint8_t value); +typedef int32_t (*ZB_RecvMsgFunc)(int32_t res, const class SBuffer &buf); + +typedef union Zigbee_Instruction { + struct { + uint8_t i; + uint8_t d8; + uint16_t d16; + } i; + const void *p; +} Zigbee_Instruction; + +typedef struct Zigbee_Instruction_Type { + uint8_t instr; + uint8_t data; +} Zigbee_Instruction_Type; + +enum Zigbee_StateMachine_Instruction_Set { + + ZGB_INSTR_4_BYTES = 0, + ZGB_INSTR_NOOP = 0, + ZGB_INSTR_LABEL, + ZGB_INSTR_GOTO, + ZGB_INSTR_ON_ERROR_GOTO, + ZGB_INSTR_ON_TIMEOUT_GOTO, + ZGB_INSTR_WAIT, + ZGB_INSTR_WAIT_FOREVER, + ZGB_INSTR_STOP, + + + ZGB_INSTR_8_BYTES = 0x80, + ZGB_INSTR_CALL = 0x80, + ZGB_INSTR_LOG, + ZGB_INSTR_MQTT_STATE, + ZGB_INSTR_SEND, + ZGB_INSTR_WAIT_UNTIL, + ZGB_INSTR_WAIT_RECV, + ZGB_ON_RECV_UNEXPECTED, + + + ZGB_INSTR_12_BYTES = 0xF0, + ZGB_INSTR_WAIT_RECV_CALL, +}; + +#define ZI_NOOP() { .i = { ZGB_INSTR_NOOP, 0x00, 0x0000} }, +#define ZI_LABEL(x) { .i = { ZGB_INSTR_LABEL, (x), 0x0000} }, +#define ZI_GOTO(x) { .i = { ZGB_INSTR_GOTO, (x), 0x0000} }, +#define ZI_ON_ERROR_GOTO(x) { .i = { ZGB_INSTR_ON_ERROR_GOTO, (x), 0x0000} }, +#define ZI_ON_TIMEOUT_GOTO(x) { .i = { ZGB_INSTR_ON_TIMEOUT_GOTO, (x), 0x0000} }, +#define ZI_WAIT(x) { .i = { ZGB_INSTR_WAIT, 0x00, (x)} }, +#define ZI_WAIT_FOREVER() { .i = { ZGB_INSTR_WAIT_FOREVER, 0x00, 0x0000} }, +#define ZI_STOP(x) { .i = { ZGB_INSTR_STOP, (x), 0x0000} }, + +#define ZI_CALL(f,x) { .i = { ZGB_INSTR_CALL, (x), 0x0000} }, { .p = (const void*)(f) }, +#define ZI_LOG(x,m) { .i = { ZGB_INSTR_LOG, (x), 0x0000 } }, { .p = ((const void*)(m)) }, +#define ZI_MQTT_STATE(x,m) { .i = { ZGB_INSTR_MQTT_STATE, (x), 0x0000 } }, { .p = ((const void*)(m)) }, +#define ZI_ON_RECV_UNEXPECTED(f) { .i = { ZGB_ON_RECV_UNEXPECTED, 0x00, 0x0000} }, { .p = (const void*)(f) }, +#define ZI_SEND(m) { .i = { ZGB_INSTR_SEND, sizeof(m), 0x0000} }, { .p = (const void*)(m) }, +#define ZI_WAIT_RECV(x,m) { .i = { ZGB_INSTR_WAIT_RECV, sizeof(m), (x)} }, { .p = (const void*)(m) }, +#define ZI_WAIT_UNTIL(x,m) { .i = { ZGB_INSTR_WAIT_UNTIL, sizeof(m), (x)} }, { .p = (const void*)(m) }, +#define ZI_WAIT_RECV_FUNC(x,m,f) { .i = { ZGB_INSTR_WAIT_RECV_CALL, sizeof(m), (x)} }, { .p = (const void*)(m) }, { .p = (const void*)(f) }, + + +const uint8_t ZIGBEE_LABEL_START = 10; +const uint8_t ZIGBEE_LABEL_READY = 20; +const uint8_t ZIGBEE_LABEL_MAIN_LOOP = 21; +const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_CLOSE = 30; +const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60 = 31; +const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX = 32; + +const uint8_t ZIGBEE_LABEL_ABORT = 99; +const uint8_t ZIGBEE_LABEL_UNSUPPORTED_VERSION = 98; + +struct ZigbeeStatus { + bool active = true; + bool state_machine = false; + bool state_waiting = false; + bool state_no_timeout = false; + bool ready = false; + uint8_t on_error_goto = ZIGBEE_LABEL_ABORT; + uint8_t on_timeout_goto = ZIGBEE_LABEL_ABORT; + int16_t pc = 0; + uint32_t next_timeout = 0; + + uint8_t *recv_filter = nullptr; + bool recv_until = false; + size_t recv_filter_len = 0; + ZB_RecvMsgFunc recv_func = nullptr; + ZB_RecvMsgFunc recv_unexpected = nullptr; + + bool init_phase = true; +}; +struct ZigbeeStatus zigbee; + +SBuffer *zigbee_buffer = nullptr; + + + + + +#define Z_B0(a) (uint8_t)( ((a) ) & 0xFF ) +#define Z_B1(a) (uint8_t)( ((a) >> 8) & 0xFF ) +#define Z_B2(a) (uint8_t)( ((a) >> 16) & 0xFF ) +#define Z_B3(a) (uint8_t)( ((a) >> 24) & 0xFF ) +#define Z_B4(a) (uint8_t)( ((a) >> 32) & 0xFF ) +#define Z_B5(a) (uint8_t)( ((a) >> 40) & 0xFF ) +#define Z_B6(a) (uint8_t)( ((a) >> 48) & 0xFF ) +#define Z_B7(a) (uint8_t)( ((a) >> 56) & 0xFF ) + +#define ZBM(n,x...) const uint8_t n[] PROGMEM = { x }; + +#define ZBR(n,x...) uint8_t n[] = { x }; +#define ZBW(n,x...) { const uint8_t n ##t[] = { x }; memcpy(n, n ##t, sizeof(n)); } + +#define USE_ZIGBEE_CHANNEL_MASK (1 << (USE_ZIGBEE_CHANNEL)) + + + +ZBM(ZBS_RESET, Z_AREQ | Z_SYS, SYS_RESET, 0x00 ) +ZBM(ZBR_RESET, Z_AREQ | Z_SYS, SYS_RESET_IND ) + +ZBM(ZBS_VERSION, Z_SREQ | Z_SYS, SYS_VERSION ) +ZBM(ZBR_VERSION, Z_SRSP | Z_SYS, SYS_VERSION ) + + +ZBM(ZBS_ZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_READ, ZNP_HAS_CONFIGURED & 0xFF, ZNP_HAS_CONFIGURED >> 8, 0x00 ) +ZBM(ZBR_ZNPHC, Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_SUCCESS, 0x01 , 0x55) + + +ZBM(ZBS_PAN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PANID ) +ZBR(ZBR_PAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PANID, 0x02 , + Z_B0(USE_ZIGBEE_PANID), Z_B1(USE_ZIGBEE_PANID) ) + +ZBM(ZBS_EXTPAN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_EXTENDED_PAN_ID ) +ZBR(ZBR_EXTPAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_EXTENDED_PAN_ID, + 0x08 , + Z_B0(USE_ZIGBEE_EXTPANID), Z_B1(USE_ZIGBEE_EXTPANID), Z_B2(USE_ZIGBEE_EXTPANID), Z_B3(USE_ZIGBEE_EXTPANID), + Z_B4(USE_ZIGBEE_EXTPANID), Z_B5(USE_ZIGBEE_EXTPANID), Z_B6(USE_ZIGBEE_EXTPANID), Z_B7(USE_ZIGBEE_EXTPANID), + ) + +ZBM(ZBS_CHANN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_CHANLIST ) +ZBR(ZBR_CHANN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_CHANLIST, + 0x04 , + Z_B0(USE_ZIGBEE_CHANNEL_MASK), Z_B1(USE_ZIGBEE_CHANNEL_MASK), Z_B2(USE_ZIGBEE_CHANNEL_MASK), Z_B3(USE_ZIGBEE_CHANNEL_MASK), + ) + +ZBM(ZBS_PFGK, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PRECFGKEY ) +ZBR(ZBR_PFGK, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PRECFGKEY, + 0x10 , + Z_B0(USE_ZIGBEE_PRECFGKEY_L), Z_B1(USE_ZIGBEE_PRECFGKEY_L), Z_B2(USE_ZIGBEE_PRECFGKEY_L), Z_B3(USE_ZIGBEE_PRECFGKEY_L), + Z_B4(USE_ZIGBEE_PRECFGKEY_L), Z_B5(USE_ZIGBEE_PRECFGKEY_L), Z_B6(USE_ZIGBEE_PRECFGKEY_L), Z_B7(USE_ZIGBEE_PRECFGKEY_L), + Z_B0(USE_ZIGBEE_PRECFGKEY_H), Z_B1(USE_ZIGBEE_PRECFGKEY_H), Z_B2(USE_ZIGBEE_PRECFGKEY_H), Z_B3(USE_ZIGBEE_PRECFGKEY_H), + Z_B4(USE_ZIGBEE_PRECFGKEY_H), Z_B5(USE_ZIGBEE_PRECFGKEY_H), Z_B6(USE_ZIGBEE_PRECFGKEY_H), Z_B7(USE_ZIGBEE_PRECFGKEY_H), + + ) + +ZBM(ZBS_PFGKEN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PRECFGKEYS_ENABLE ) +ZBM(ZBR_PFGKEN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PRECFGKEYS_ENABLE, + 0x01 , 0x00 ) + + + +ZBM(ZBR_W_OK, Z_SRSP | Z_SAPI, SAPI_WRITE_CONFIGURATION, Z_SUCCESS ) +ZBM(ZBR_WNV_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_WRITE, Z_SUCCESS ) + + +ZBM(ZBS_FACTRES, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 , 0x02 ) + +ZBR(ZBS_W_PAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PANID, 0x02 , Z_B0(USE_ZIGBEE_PANID), Z_B1(USE_ZIGBEE_PANID) ) + +ZBR(ZBS_W_EXTPAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_EXTENDED_PAN_ID, 0x08 , + Z_B0(USE_ZIGBEE_EXTPANID), Z_B1(USE_ZIGBEE_EXTPANID), Z_B2(USE_ZIGBEE_EXTPANID), Z_B3(USE_ZIGBEE_EXTPANID), + Z_B4(USE_ZIGBEE_EXTPANID), Z_B5(USE_ZIGBEE_EXTPANID), Z_B6(USE_ZIGBEE_EXTPANID), Z_B7(USE_ZIGBEE_EXTPANID) + ) + +ZBR(ZBS_W_CHANN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_CHANLIST, 0x04 , + Z_B0(USE_ZIGBEE_CHANNEL_MASK), Z_B1(USE_ZIGBEE_CHANNEL_MASK), Z_B2(USE_ZIGBEE_CHANNEL_MASK), Z_B3(USE_ZIGBEE_CHANNEL_MASK), + ) + +ZBM(ZBS_W_LOGTYP, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_LOGICAL_TYPE, 0x01 , 0x00 ) + +ZBR(ZBS_W_PFGK, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PRECFGKEY, + 0x10 , + Z_B0(USE_ZIGBEE_PRECFGKEY_L), Z_B1(USE_ZIGBEE_PRECFGKEY_L), Z_B2(USE_ZIGBEE_PRECFGKEY_L), Z_B3(USE_ZIGBEE_PRECFGKEY_L), + Z_B4(USE_ZIGBEE_PRECFGKEY_L), Z_B5(USE_ZIGBEE_PRECFGKEY_L), Z_B6(USE_ZIGBEE_PRECFGKEY_L), Z_B7(USE_ZIGBEE_PRECFGKEY_L), + Z_B0(USE_ZIGBEE_PRECFGKEY_H), Z_B1(USE_ZIGBEE_PRECFGKEY_H), Z_B2(USE_ZIGBEE_PRECFGKEY_H), Z_B3(USE_ZIGBEE_PRECFGKEY_H), + Z_B4(USE_ZIGBEE_PRECFGKEY_H), Z_B5(USE_ZIGBEE_PRECFGKEY_H), Z_B6(USE_ZIGBEE_PRECFGKEY_H), Z_B7(USE_ZIGBEE_PRECFGKEY_H), + + ) + +ZBM(ZBS_W_PFGKEN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PRECFGKEYS_ENABLE, 0x01 , 0x00 ) + +ZBM(ZBS_WNV_SECMODE, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, Z_B0(CONF_TCLK_TABLE_START), Z_B1(CONF_TCLK_TABLE_START), + 0x00 , 0x20 , + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x5a, 0x69, 0x67, 0x42, 0x65, 0x65, 0x41, 0x6c, + 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) + +ZBM(ZBS_W_ZDODCB, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_ZDO_DIRECT_CB, 0x01 , 0x01 ) + +ZBM(ZBS_WNV_INITZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_ITEM_INIT, ZNP_HAS_CONFIGURED & 0xFF, ZNP_HAS_CONFIGURED >> 8, + 0x01, 0x00 , 0x01 , 0x00 ) + + +ZBM(ZBR_WNV_INIT_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_ITEM_INIT ) + + +ZBM(ZBS_WNV_ZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, Z_B0(ZNP_HAS_CONFIGURED), Z_B1(ZNP_HAS_CONFIGURED), + 0x00 , 0x01 , 0x55 ) + +ZBM(ZBS_STARTUPFROMAPP, Z_SREQ | Z_ZDO, ZDO_STARTUP_FROM_APP, 100, 0 ) +ZBM(ZBR_STARTUPFROMAPP, Z_SRSP | Z_ZDO, ZDO_STARTUP_FROM_APP ) +ZBM(AREQ_STARTUPFROMAPP, Z_AREQ | Z_ZDO, ZDO_STATE_CHANGE_IND, ZDO_DEV_ZB_COORD ) + +ZBM(ZBS_GETDEVICEINFO, Z_SREQ | Z_UTIL, Z_UTIL_GET_DEVICE_INFO ) +ZBM(ZBR_GETDEVICEINFO, Z_SRSP | Z_UTIL, Z_UTIL_GET_DEVICE_INFO, Z_SUCCESS ) +# 268 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" +ZBM(ZBS_ZDO_NODEDESCREQ, Z_SREQ | Z_ZDO, ZDO_NODE_DESC_REQ, 0x00, 0x00 , 0x00, 0x00 ) +ZBM(ZBR_ZDO_NODEDESCREQ, Z_SRSP | Z_ZDO, ZDO_NODE_DESC_REQ, Z_SUCCESS ) + +ZBM(AREQ_ZDO_NODEDESCRSP, Z_AREQ | Z_ZDO, ZDO_NODE_DESC_RSP) +# 286 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" +ZBM(ZBS_ZDO_ACTIVEEPREQ, Z_SREQ | Z_ZDO, ZDO_ACTIVE_EP_REQ, 0x00, 0x00, 0x00, 0x00) +ZBM(ZBR_ZDO_ACTIVEEPREQ, Z_SRSP | Z_ZDO, ZDO_ACTIVE_EP_REQ, Z_SUCCESS) +ZBM(ZBR_ZDO_ACTIVEEPRSP_NONE, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP, 0x00, 0x00 , Z_SUCCESS, + 0x00, 0x00 , 0x00 ) +ZBM(ZBR_ZDO_ACTIVEEPRSP_OK, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP, 0x00, 0x00 , Z_SUCCESS, + 0x00, 0x00 , 0x02 , 0x0B, 0x01 ) + + +ZBM(ZBS_AF_REGISTER01, Z_SREQ | Z_AF, AF_REGISTER, 0x01 , Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), + 0x05, 0x00 , 0x00 , 0x00 , + 0x00 , 0x00 ) +ZBM(ZBR_AF_REGISTER, Z_SRSP | Z_AF, AF_REGISTER, Z_SUCCESS) +ZBM(ZBS_AF_REGISTER0B, Z_SREQ | Z_AF, AF_REGISTER, 0x0B , Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), + 0x05, 0x00 , 0x00 , 0x00 , + 0x00 , 0x00 ) + +ZBM(ZBS_PERMITJOINREQ_CLOSE, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x02 , + 0x00, 0x00 , 0x00 , 0x00 ) +ZBM(ZBR_PERMITJOINREQ, Z_SRSP | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, Z_SUCCESS) +ZBM(ZBR_PERMITJOIN_AREQ_RSP, Z_AREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_RSP, 0x00, 0x00 , Z_SUCCESS ) + + +void Z_UpdateConfig(uint8_t zb_channel, uint16_t zb_pan_id, uint64_t zb_ext_panid, uint64_t zb_precfgkey_l, uint64_t zb_precfgkey_h) { + uint32_t zb_channel_mask = (1 << zb_channel); + + ZBW(ZBR_PAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PANID, 0x02 , + Z_B0(zb_pan_id), Z_B1(zb_pan_id) ) + + ZBW(ZBR_EXTPAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_EXTENDED_PAN_ID, + 0x08 , + Z_B0(zb_ext_panid), Z_B1(zb_ext_panid), Z_B2(zb_ext_panid), Z_B3(zb_ext_panid), + Z_B4(zb_ext_panid), Z_B5(zb_ext_panid), Z_B6(zb_ext_panid), Z_B7(zb_ext_panid), + ) + + ZBW(ZBR_CHANN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_CHANLIST, + 0x04 , + Z_B0(zb_channel_mask), Z_B1(zb_channel_mask), Z_B2(zb_channel_mask), Z_B3(zb_channel_mask), + ) + + ZBW(ZBR_PFGK, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PRECFGKEY, + 0x10 , + Z_B0(zb_precfgkey_l), Z_B1(zb_precfgkey_l), Z_B2(zb_precfgkey_l), Z_B3(zb_precfgkey_l), + Z_B4(zb_precfgkey_l), Z_B5(zb_precfgkey_l), Z_B6(zb_precfgkey_l), Z_B7(zb_precfgkey_l), + Z_B0(zb_precfgkey_h), Z_B1(zb_precfgkey_h), Z_B2(zb_precfgkey_h), Z_B3(zb_precfgkey_h), + Z_B4(zb_precfgkey_h), Z_B5(zb_precfgkey_h), Z_B6(zb_precfgkey_h), Z_B7(zb_precfgkey_h), + + ) + + ZBW(ZBS_W_PAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PANID, 0x02 , Z_B0(zb_pan_id), Z_B1(zb_pan_id) ) + + ZBW(ZBS_W_EXTPAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_EXTENDED_PAN_ID, 0x08 , + Z_B0(zb_ext_panid), Z_B1(zb_ext_panid), Z_B2(zb_ext_panid), Z_B3(zb_ext_panid), + Z_B4(zb_ext_panid), Z_B5(zb_ext_panid), Z_B6(zb_ext_panid), Z_B7(zb_ext_panid) + ) + + ZBW(ZBS_W_CHANN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_CHANLIST, 0x04 , + Z_B0(zb_channel_mask), Z_B1(zb_channel_mask), Z_B2(zb_channel_mask), Z_B3(zb_channel_mask), + ) + + ZBW(ZBS_W_PFGK, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PRECFGKEY, + 0x10 , + Z_B0(zb_precfgkey_l), Z_B1(zb_precfgkey_l), Z_B2(zb_precfgkey_l), Z_B3(zb_precfgkey_l), + Z_B4(zb_precfgkey_l), Z_B5(zb_precfgkey_l), Z_B6(zb_precfgkey_l), Z_B7(zb_precfgkey_l), + Z_B0(zb_precfgkey_h), Z_B1(zb_precfgkey_h), Z_B2(zb_precfgkey_h), Z_B3(zb_precfgkey_h), + Z_B4(zb_precfgkey_h), Z_B5(zb_precfgkey_h), Z_B6(zb_precfgkey_h), Z_B7(zb_precfgkey_h), + ) +} + +const char kCheckingDeviceConfiguration[] PROGMEM = D_LOG_ZIGBEE "checking device configuration"; +const char kConfigured[] PROGMEM = "Configured, starting coordinator"; +const char kStarted[] PROGMEM = "Started"; +const char kZigbeeStarted[] PROGMEM = D_LOG_ZIGBEE "Zigbee started"; +const char kResetting[] PROGMEM = "Resetting configuration"; +const char kZNP12[] PROGMEM = "Only ZNP 1.2 is currently supported"; +const char kAbort[] PROGMEM = "Abort"; +const char kZigbeeAbort[] PROGMEM = D_LOG_ZIGBEE "Abort"; + +static const Zigbee_Instruction zb_prog[] PROGMEM = { + ZI_LABEL(0) + ZI_NOOP() + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) + ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT) + ZI_ON_RECV_UNEXPECTED(&Z_Recv_Default) + ZI_WAIT(10500) + ZI_ON_ERROR_GOTO(50) + + + + ZI_SEND(ZBS_RESET) + ZI_WAIT_RECV_FUNC(5000, ZBR_RESET, &Z_Reboot) + ZI_WAIT(100) + ZI_LOG(LOG_LEVEL_DEBUG, kCheckingDeviceConfiguration) + ZI_SEND(ZBS_ZNPHC) + ZI_WAIT_RECV(2000, ZBR_ZNPHC) + ZI_SEND(ZBS_VERSION) + ZI_WAIT_RECV_FUNC(2000, ZBR_VERSION, &Z_ReceiveCheckVersion) + ZI_SEND(ZBS_PAN) + ZI_WAIT_RECV(1000, ZBR_PAN) + ZI_SEND(ZBS_EXTPAN) + ZI_WAIT_RECV(1000, ZBR_EXTPAN) + ZI_SEND(ZBS_CHANN) + ZI_WAIT_RECV(1000, ZBR_CHANN) + ZI_SEND(ZBS_PFGK) + ZI_WAIT_RECV(1000, ZBR_PFGK) + ZI_SEND(ZBS_PFGKEN) + ZI_WAIT_RECV(1000, ZBR_PFGKEN) + + + + ZI_LABEL(ZIGBEE_LABEL_START) + ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfigured) + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) + + +ZI_SEND(ZBS_STARTUPFROMAPP) + ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) + ZI_WAIT_UNTIL(10000, AREQ_STARTUPFROMAPP) + ZI_SEND(ZBS_GETDEVICEINFO) + ZI_WAIT_RECV_FUNC(2000, ZBR_GETDEVICEINFO, &Z_ReceiveDeviceInfo) + + ZI_SEND(ZBS_ZDO_NODEDESCREQ) + ZI_WAIT_RECV(1000, ZBR_ZDO_NODEDESCREQ) + ZI_WAIT_UNTIL(5000, AREQ_ZDO_NODEDESCRSP) + ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) + ZI_WAIT_RECV(1000, ZBR_ZDO_ACTIVEEPREQ) + ZI_WAIT_UNTIL(1000, ZBR_ZDO_ACTIVEEPRSP_NONE) + ZI_SEND(ZBS_AF_REGISTER01) + ZI_WAIT_RECV(1000, ZBR_AF_REGISTER) + ZI_SEND(ZBS_AF_REGISTER0B) + ZI_WAIT_RECV(1000, ZBR_AF_REGISTER) + + ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) + ZI_WAIT_RECV(1000, ZBR_ZDO_ACTIVEEPREQ) + ZI_WAIT_UNTIL(1000, ZBR_ZDO_ACTIVEEPRSP_OK) + ZI_SEND(ZBS_PERMITJOINREQ_CLOSE) + ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) + ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) + + ZI_LABEL(ZIGBEE_LABEL_READY) + ZI_MQTT_STATE(ZIGBEE_STATUS_OK, kStarted) + ZI_LOG(LOG_LEVEL_INFO, kZigbeeStarted) + ZI_CALL(&Z_State_Ready, 1) + ZI_CALL(&Z_Load_Devices, 0) + ZI_CALL(&Z_Query_Bulbs, 0) + ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP) + ZI_WAIT_FOREVER() + ZI_GOTO(ZIGBEE_LABEL_READY) + + ZI_LABEL(50) + ZI_MQTT_STATE(ZIGBEE_STATUS_RESET_CONF, kResetting) + + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) + ZI_SEND(ZBS_FACTRES) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_RESET) + ZI_WAIT_RECV(5000, ZBR_RESET) + ZI_SEND(ZBS_W_PAN) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_EXTPAN) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_CHANN) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_LOGTYP) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_PFGK) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_PFGKEN) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_WNV_SECMODE) + ZI_WAIT_RECV(1000, ZBR_WNV_OK) + ZI_SEND(ZBS_W_ZDODCB) + ZI_WAIT_RECV(1000, ZBR_W_OK) + + ZI_SEND(ZBS_WNV_INITZNPHC) + ZI_WAIT_RECV_FUNC(1000, ZBR_WNV_INIT_OK, &Z_CheckNVWrite) + ZI_SEND(ZBS_WNV_ZNPHC) + ZI_WAIT_RECV(1000, ZBR_WNV_OK) + + + ZI_GOTO(ZIGBEE_LABEL_START) + + ZI_LABEL(ZIGBEE_LABEL_UNSUPPORTED_VERSION) + ZI_MQTT_STATE(ZIGBEE_STATUS_UNSUPPORTED_VERSION, kZNP12) + ZI_GOTO(ZIGBEE_LABEL_ABORT) + + ZI_LABEL(ZIGBEE_LABEL_ABORT) + ZI_MQTT_STATE(ZIGBEE_STATUS_ABORT, kAbort) + ZI_LOG(LOG_LEVEL_ERROR, kZigbeeAbort) + ZI_STOP(ZIGBEE_LABEL_ABORT) +}; + +uint8_t ZigbeeGetInstructionSize(uint8_t instr) { + if (instr >= ZGB_INSTR_12_BYTES) { + return 3; + } else if (instr >= ZGB_INSTR_8_BYTES) { + return 2; + } else { + return 1; + } +} + +void ZigbeeGotoLabel(uint8_t label) { + + uint16_t goto_pc = 0xFFFF; + uint8_t cur_instr = 0; + uint8_t cur_d8 = 0; + uint8_t cur_instr_len = 1; + + for (uint32_t i = 0; i < sizeof(zb_prog)/sizeof(zb_prog[0]); i += cur_instr_len) { + const Zigbee_Instruction *cur_instr_line = &zb_prog[i]; + cur_instr = pgm_read_byte(&cur_instr_line->i.i); + cur_d8 = pgm_read_byte(&cur_instr_line->i.d8); + + + if (ZGB_INSTR_LABEL == cur_instr) { + + if (label == cur_d8) { + + zigbee.pc = i; + zigbee.state_machine = true; + zigbee.state_waiting = false; + return; + } + } + + cur_instr_len = ZigbeeGetInstructionSize(cur_instr); + } + + + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Goto label not found, label=%d pc=%d"), label, zigbee.pc); + if (ZIGBEE_LABEL_ABORT != label) { + + ZigbeeGotoLabel(ZIGBEE_LABEL_ABORT); + } else { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Label Abort (%d) not present, aborting Zigbee"), ZIGBEE_LABEL_ABORT); + zigbee.state_machine = false; + zigbee.active = false; + } +} + +void ZigbeeStateMachine_Run(void) { + uint8_t cur_instr = 0; + uint8_t cur_d8 = 0; + uint16_t cur_d16 = 0; + const void* cur_ptr1 = nullptr; + const void* cur_ptr2 = nullptr; + uint32_t now = millis(); + + if (zigbee.state_waiting) { + + if ((zigbee.next_timeout) && (now > zigbee.next_timeout)) { + if (!zigbee.state_no_timeout) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "timeout, goto label %d"), zigbee.on_timeout_goto); + ZigbeeGotoLabel(zigbee.on_timeout_goto); + } else { + zigbee.state_waiting = false; + } + } + } + + while ((zigbee.state_machine) && (!zigbee.state_waiting)) { + + zigbee.recv_filter = nullptr; + zigbee.recv_func = nullptr; + zigbee.recv_until = false; + zigbee.state_no_timeout = false; + + if (zigbee.pc > (sizeof(zb_prog)/sizeof(zb_prog[0]))) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Invalid pc: %d, aborting"), zigbee.pc); + zigbee.pc = -1; + } + if (zigbee.pc < 0) { + zigbee.state_machine = false; + return; + } + + + const Zigbee_Instruction *cur_instr_line = &zb_prog[zigbee.pc]; + cur_instr = pgm_read_byte(&cur_instr_line->i.i); + cur_d8 = pgm_read_byte(&cur_instr_line->i.d8); + cur_d16 = pgm_read_word(&cur_instr_line->i.d16); + if (cur_instr >= ZGB_INSTR_8_BYTES) { + cur_instr_line++; + cur_ptr1 = cur_instr_line->p; + } + if (cur_instr >= ZGB_INSTR_12_BYTES) { + cur_instr_line++; + cur_ptr2 = cur_instr_line->p; + } + + zigbee.pc += ZigbeeGetInstructionSize(cur_instr); + + switch (cur_instr) { + case ZGB_INSTR_NOOP: + case ZGB_INSTR_LABEL: + break; + case ZGB_INSTR_GOTO: + ZigbeeGotoLabel(cur_d8); + break; + case ZGB_INSTR_ON_ERROR_GOTO: + zigbee.on_error_goto = cur_d8; + break; + case ZGB_INSTR_ON_TIMEOUT_GOTO: + zigbee.on_timeout_goto = cur_d8; + break; + case ZGB_INSTR_WAIT: + zigbee.next_timeout = now + cur_d16; + zigbee.state_waiting = true; + zigbee.state_no_timeout = true; + break; + case ZGB_INSTR_WAIT_FOREVER: + zigbee.next_timeout = 0; + zigbee.state_waiting = true; + break; + case ZGB_INSTR_STOP: + zigbee.state_machine = false; + if (cur_d8) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Stopping (%d)"), cur_d8); + } + break; + case ZGB_INSTR_CALL: + if (cur_ptr1) { + uint32_t res; + res = (*((ZB_Func)cur_ptr1))(cur_d8); + if (res > 0) { + ZigbeeGotoLabel(res); + continue; + } else if (res == 0) { + + } else if (res == -1) { + + } else { + ZigbeeGotoLabel(zigbee.on_error_goto); + continue; + } + } + break; + case ZGB_INSTR_LOG: + AddLog_P(cur_d8, (char*) cur_ptr1); + break; + case ZGB_INSTR_MQTT_STATE: + { + const char *f_msg = (const char*) cur_ptr1; + char buf[strlen_P(f_msg) + 1]; + strcpy_P(buf, f_msg); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{\"Status\":%d,\"Message\":\"%s\"}}"), + cur_d8, buf); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + XdrvRulesProcess(); + } + break; + case ZGB_INSTR_SEND: + ZigbeeZNPSend((uint8_t*) cur_ptr1, cur_d8 ); + break; + case ZGB_INSTR_WAIT_UNTIL: + zigbee.recv_until = true; + case ZGB_INSTR_WAIT_RECV: + zigbee.recv_filter = (uint8_t *) cur_ptr1; + zigbee.recv_filter_len = cur_d8; + zigbee.next_timeout = now + cur_d16; + zigbee.state_waiting = true; + break; + case ZGB_ON_RECV_UNEXPECTED: + zigbee.recv_unexpected = (ZB_RecvMsgFunc) cur_ptr1; + break; + case ZGB_INSTR_WAIT_RECV_CALL: + zigbee.recv_filter = (uint8_t *) cur_ptr1; + zigbee.recv_filter_len = cur_d8; + zigbee.recv_func = (ZB_RecvMsgFunc) cur_ptr2; + zigbee.next_timeout = now + cur_d16; + zigbee.state_waiting = true; + break; + } + } +} + + + + +int32_t ZigbeeProcessInput(class SBuffer &buf) { + if (!zigbee.state_machine) { return -1; } + + + bool recv_filter_match = true; + bool recv_prefix_match = false; + if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) { + if (zigbee.recv_filter_len >= 2) { + recv_prefix_match = false; + if ( (pgm_read_byte(&zigbee.recv_filter[0]) == buf.get8(0)) && + (pgm_read_byte(&zigbee.recv_filter[1]) == buf.get8(1)) ) { + recv_prefix_match = true; + } + } + + for (uint32_t i = 0; i < zigbee.recv_filter_len; i++) { + if (pgm_read_byte(&zigbee.recv_filter[i]) != buf.get8(i)) { + recv_filter_match = false; + break; + } + } + } + + + int32_t res = -1; + + + + + + if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) { + if (!recv_prefix_match) { + res = -1; + } else { + if (recv_filter_match) { + res = 0; + } else { + if (zigbee.recv_until) { + res = -1; + } else { + res = -2; + } + } + } + } else { + res = -1; + } + + if (recv_prefix_match) { + if (zigbee.recv_func) { + res = (*zigbee.recv_func)(res, buf); + } + } + if (-1 == res) { + + if (zigbee.recv_unexpected) { + res = (*zigbee.recv_unexpected)(res, buf); + } + } + + + if (0 == res) { + + zigbee.state_waiting = false; + } else if (res > 0) { + ZigbeeGotoLabel(res); + } else if (-1 == res) { + + + } else { + + ZigbeeGotoLabel(zigbee.on_error_goto); + } +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" +#ifdef USE_ZIGBEE +# 29 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" +int32_t Z_ReceiveDeviceInfo(int32_t res, class SBuffer &buf) { + + + + + + + + Z_IEEEAddress long_adr = buf.get64(3); + Z_ShortAddress short_adr = buf.get16(11); + uint8_t device_type = buf.get8(13); + uint8_t device_state = buf.get8(14); + uint8_t device_associated = buf.get8(15); + + + localIEEEAddr = long_adr; + + char hex[20]; + Uint64toHex(long_adr, hex, 64); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\"" + ",\"DeviceType\":%d,\"DeviceState\":%d" + ",\"NumAssocDevices\":%d"), + ZIGBEE_STATUS_CC_INFO, hex, short_adr, device_type, device_state, + device_associated); + + if (device_associated > 0) { + uint idx = 16; + ResponseAppend_P(PSTR(",\"AssocDevicesList\":[")); + for (uint32_t i = 0; i < device_associated; i++) { + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(idx)); + idx += 2; + } + ResponseAppend_P(PSTR("]")); + } + + ResponseJsonEnd(); + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + XdrvRulesProcess(); + + return res; +} + +int32_t Z_CheckNVWrite(int32_t res, class SBuffer &buf) { + + + + uint8_t status = buf.get8(2); + if ((0x00 == status) || (0x09 == status)) { + return 0; + } else { + return -2; + } +} + +int32_t Z_Reboot(int32_t res, class SBuffer &buf) { + + + + static const char Z_RebootReason[] PROGMEM = "Power-up|External|Watchdog"; + + uint8_t reason = buf.get8(2); + uint8_t transport_rev = buf.get8(3); + uint8_t product_id = buf.get8(4); + uint8_t major_rel = buf.get8(5); + uint8_t minor_rel = buf.get8(6); + uint8_t hw_rev = buf.get8(7); + char reason_str[12]; + + if (reason > 3) { reason = 3; } + GetTextIndexed(reason_str, sizeof(reason_str), reason, Z_RebootReason); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"Message\":\"CC2530 booted\",\"RestartReason\":\"%s\"" + ",\"MajorRel\":%d,\"MinorRel\":%d}}"), + ZIGBEE_STATUS_BOOT, reason_str, + major_rel, minor_rel); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + XdrvRulesProcess(); + + if ((0x02 == major_rel) && (0x06 == minor_rel)) { + return 0; + } else { + return ZIGBEE_LABEL_UNSUPPORTED_VERSION; + } +} + +int32_t Z_ReceiveCheckVersion(int32_t res, class SBuffer &buf) { +# 129 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" + uint8_t major_rel = buf.get8(4); + uint8_t minor_rel = buf.get8(5); + uint8_t maint_rel = buf.get8(6); + uint32_t revision = buf.get32(7); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"MajorRel\":%d,\"MinorRel\":%d" + ",\"MaintRel\":%d,\"Revision\":%d}}"), + ZIGBEE_STATUS_CC_VERSION, major_rel, minor_rel, + maint_rel, revision); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + XdrvRulesProcess(); + + if ((0x02 == major_rel) && (0x06 == minor_rel)) { + return 0; + } else { + return ZIGBEE_LABEL_UNSUPPORTED_VERSION; + } +} + + + + +bool Z_ReceiveMatchPrefix(const class SBuffer &buf, const uint8_t *match) { + if ( (pgm_read_byte(&match[0]) == buf.get8(0)) && + (pgm_read_byte(&match[1]) == buf.get8(1)) ) { + return true; + } else { + return false; + } +} + + + + +int32_t Z_ReceivePermitJoinStatus(int32_t res, const class SBuffer &buf) { + + uint8_t duration = buf.get8(2); + uint8_t status_code; + const char* message; + + if (0xFF == duration) { + status_code = ZIGBEE_STATUS_PERMITJOIN_OPEN_XX; + message = PSTR("Enable Pairing mode until next boot"); + } else if (duration > 0) { + status_code = ZIGBEE_STATUS_PERMITJOIN_OPEN_60; + message = PSTR("Enable Pairing mode for %d seconds"); + } else { + status_code = ZIGBEE_STATUS_PERMITJOIN_CLOSE; + message = PSTR("Disable Pairing mode"); + } + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"Message\":\""), + status_code); + ResponseAppend_P(message, duration); + ResponseAppend_P(PSTR("\"}}")); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + XdrvRulesProcess(); + return -1; +} + +const char* Z_DeviceType[] = { "Coordinator", "Router", "End Device", "Unknown" }; +int32_t Z_ReceiveNodeDesc(int32_t res, const class SBuffer &buf) { + + Z_ShortAddress srcAddr = buf.get16(2); + uint8_t status = buf.get8(4); + Z_ShortAddress nwkAddr = buf.get16(5); + uint8_t logicalType = buf.get8(7); + uint8_t apsFlags = buf.get8(8); + uint8_t MACCapabilityFlags = buf.get8(9); + uint16_t manufacturerCapabilities = buf.get16(10); + uint8_t maxBufferSize = buf.get8(12); + uint16_t maxInTransferSize = buf.get16(13); + uint16_t serverMask = buf.get16(15); + uint16_t maxOutTransferSize = buf.get16(17); + uint8_t descriptorCapabilities = buf.get8(19); + + if (0 == status) { + uint8_t deviceType = logicalType & 0x7; + if (deviceType > 3) { deviceType = 3; } + bool complexDescriptorAvailable = (logicalType & 0x08) ? 1 : 0; + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"NodeType\":\"%s\",\"ComplexDesc\":%s}}"), + ZIGBEE_STATUS_NODE_DESC, Z_DeviceType[deviceType], + complexDescriptorAvailable ? "true" : "false" + ); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + } + + return -1; +} + + + + +int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf) { + + Z_ShortAddress srcAddr = buf.get16(2); + uint8_t status = buf.get8(4); + Z_ShortAddress nwkAddr = buf.get16(5); + uint8_t activeEpCount = buf.get8(7); + uint8_t* activeEpList = (uint8_t*) buf.charptr(8); + + for (uint32_t i = 0; i < activeEpCount; i++) { + zigbee_devices.addEndpoint(nwkAddr, activeEpList[i]); + } + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"ActiveEndpoints\":["), + ZIGBEE_STATUS_ACTIVE_EP); + for (uint32_t i = 0; i < activeEpCount; i++) { + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"0x%02X\""), activeEpList[i]); + } + ResponseAppend_P(PSTR("]}}")); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + + Z_SendAFInfoRequest(nwkAddr); + + return -1; +} + + + + +int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf) { + uint8_t status = buf.get8(2); + Z_IEEEAddress ieeeAddr = buf.get64(3); + Z_ShortAddress nwkAddr = buf.get16(11); + + + + if (0 == status) { + zigbee_devices.updateDevice(nwkAddr, ieeeAddr); + char hex[20]; + Uint64toHex(ieeeAddr, hex, 64); + + const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr); + if (friendlyName) { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_PING "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" + ",\"" D_JSON_ZIGBEE_IEEE "\":\"0x%s\"" + ",\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), nwkAddr, hex, friendlyName); + } else { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_PING "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" + ",\"" D_JSON_ZIGBEE_IEEE "\":\"0x%s\"" + "}}"), nwkAddr, hex); + } + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + } + return -1; +} + + + + +int32_t Z_DataConfirm(int32_t res, const class SBuffer &buf) { + uint8_t status = buf.get8(2); + uint8_t endpoint = buf.get8(3); + + + if (status) { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_CONFIRM "\":{\"" D_CMND_ZIGBEE_ENDPOINT "\":%d" + ",\"" D_JSON_ZIGBEE_STATUS "\":%d" + ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" + "}}"), endpoint, status, getZigbeeStatusMessage(status).c_str()); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + } + + return -1; +} + + + + + + +int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) { + Z_ShortAddress srcAddr = buf.get16(2); + Z_ShortAddress nwkAddr = buf.get16(4); + Z_IEEEAddress ieeeAddr = buf.get64(6); + uint8_t capabilities = buf.get8(14); + + zigbee_devices.updateDevice(nwkAddr, ieeeAddr); + + char hex[20]; + Uint64toHex(ieeeAddr, hex, 64); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\"" + ",\"PowerSource\":%s,\"ReceiveWhenIdle\":%s,\"Security\":%s}}"), + ZIGBEE_STATUS_DEVICE_ANNOUNCE, hex, nwkAddr, + (capabilities & 0x04) ? "true" : "false", + (capabilities & 0x08) ? "true" : "false", + (capabilities & 0x40) ? "true" : "false" + ); + + uint32_t wait_ms = 2000; + Z_Query_Bulb(nwkAddr, wait_ms); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + Z_SendActiveEpReq(nwkAddr); + return -1; +} + + + + + +int32_t Z_ReceiveTCDevInd(int32_t res, const class SBuffer &buf) { + Z_ShortAddress srcAddr = buf.get16(2); + Z_IEEEAddress ieeeAddr = buf.get64(4); + Z_ShortAddress parentNw = buf.get16(12); + + zigbee_devices.updateDevice(srcAddr, ieeeAddr); + + char hex[20]; + Uint64toHex(ieeeAddr, hex, 64); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\"" + ",\"ParentNetwork\":\"0x%04X\"}}"), + ZIGBEE_STATUS_DEVICE_INDICATION, hex, srcAddr, parentNw + ); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + return -1; +} + + + + +int32_t Z_BindRsp(int32_t res, const class SBuffer &buf) { + Z_ShortAddress nwkAddr = buf.get16(2); + uint8_t status = buf.get8(4); + + const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr); + if (friendlyName) { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" + ",\"" D_JSON_ZIGBEE_NAME "\":\"%s\"" + ",\"" D_JSON_ZIGBEE_STATUS "\":%d" + ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" + "}}"), nwkAddr, friendlyName, status, getZigbeeStatusMessage(status).c_str()); + } else { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" + ",\"" D_JSON_ZIGBEE_STATUS "\":%d" + ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" + "}}"), nwkAddr, status, getZigbeeStatusMessage(status).c_str()); + } + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + + return -1; +} + + + + +int32_t Z_UnbindRsp(int32_t res, const class SBuffer &buf) { + Z_ShortAddress nwkAddr = buf.get16(2); + uint8_t status = buf.get8(4); + + const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr); + if (friendlyName) { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_UNBIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" + ",\"" D_JSON_ZIGBEE_NAME "\":\"%s\"" + ",\"" D_JSON_ZIGBEE_STATUS "\":%d" + ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" + "}}"), nwkAddr, friendlyName, status, getZigbeeStatusMessage(status).c_str()); + } else { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_UNBIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" + ",\"" D_JSON_ZIGBEE_STATUS "\":%d" + ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" + "}}"), nwkAddr, status, getZigbeeStatusMessage(status).c_str()); + } + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + + return -1; +} + + + +int32_t Z_MgmtBindRsp(int32_t res, const class SBuffer &buf) { + uint16_t shortaddr = buf.get16(2); + uint8_t status = buf.get8(4); + uint8_t bind_total = buf.get8(5); + uint8_t bind_start = buf.get8(6); + uint8_t bind_len = buf.get8(7); + + const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND_STATE "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""), shortaddr); + if (friendlyName) { + ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""), friendlyName); + } + ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_STATUS "\":%d" + ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" + ",\"BindingsTotal\":%d" + + ",\"Bindings\":[" + ), status, getZigbeeStatusMessage(status).c_str(), bind_total); + + uint32_t idx = 8; + for (uint32_t i = 0; i < bind_len; i++) { + if (idx + 14 > buf.len()) { break; } + + + uint8_t srcep = buf.get8(idx + 8); + uint8_t cluster = buf.get16(idx + 9); + uint8_t addrmode = buf.get8(idx + 11); + uint16_t group = 0x0000; + uint64_t dstaddr = 0; + uint8_t dstep = 0x00; + if (Z_Addr_Group == addrmode) { + group = buf.get16(idx + 12); + idx += 14; + } else if (Z_Addr_IEEEAddress == addrmode) { + dstaddr = buf.get64(idx + 12); + dstep = buf.get8(idx + 20); + idx += 21; + } else { + + break; + } + + if (i > 0) { + ResponseAppend_P(PSTR(",")); + } + ResponseAppend_P(PSTR("{\"Cluster\":\"0x%04X\",\"Endpoint\":%d,"), cluster, srcep); + if (Z_Addr_Group == addrmode) { + ResponseAppend_P(PSTR("\"ToGroup\":%d}"), group); + } else if (Z_Addr_IEEEAddress == addrmode) { + char hex[20]; + Uint64toHex(dstaddr, hex, 64); + ResponseAppend_P(PSTR("\"ToDevice\":\"0x%s\",\"ToEndpoint\":%d}"), hex, dstep); + } + } + + ResponseAppend_P(PSTR("]}}")); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_BIND_STATE)); + XdrvRulesProcess(); + + return -1; +} +# 491 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" +void Z_SendIEEEAddrReq(uint16_t shortaddr) { + uint8_t IEEEAddrReq[] = { Z_SREQ | Z_ZDO, ZDO_IEEE_ADDR_REQ, Z_B0(shortaddr), Z_B1(shortaddr), 0x00, 0x00 }; + + ZigbeeZNPSend(IEEEAddrReq, sizeof(IEEEAddrReq)); +} + + + + +void Z_SendActiveEpReq(uint16_t shortaddr) { + uint8_t ActiveEpReq[] = { Z_SREQ | Z_ZDO, ZDO_ACTIVE_EP_REQ, Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) }; + + ZigbeeZNPSend(ActiveEpReq, sizeof(ActiveEpReq)); +} + + + + +void Z_SendAFInfoRequest(uint16_t shortaddr) { + uint8_t endpoint = zigbee_devices.findFirstEndpoint(shortaddr); + if (0x00 == endpoint) { endpoint = 0x01; } + uint8_t transacid = zigbee_devices.getNextSeqNumber(shortaddr); + + uint8_t AFInfoReq[] = { Z_SREQ | Z_AF, AF_DATA_REQUEST, Z_B0(shortaddr), Z_B1(shortaddr), endpoint, + 0x01, 0x00, 0x00, transacid, 0x30, 0x1E, 3 + 2*sizeof(uint16_t), + 0x00, transacid, ZCL_READ_ATTRIBUTES, 0x04, 0x00, 0x05, 0x00 + }; + ZigbeeZNPSend(AFInfoReq, sizeof(AFInfoReq)); +} +# 529 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" +void Z_AqaraOccupancy(uint16_t shortaddr, uint16_t cluster, uint8_t endpoint, const JsonObject &json) { + static const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; + + const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR(OCCUPANCY)); + if (nullptr != &val_endpoint) { + uint32_t occupancy = strToUInt(val_endpoint); + + if (occupancy) { + zigbee_devices.setTimer(shortaddr, 0 , OCCUPANCY_TIMEOUT, cluster, endpoint, Z_CAT_VIRTUAL_OCCUPANCY, 0, &Z_OccupancyCallback); + } else { + zigbee_devices.resetTimersForDevice(shortaddr, 0 , Z_CAT_VIRTUAL_OCCUPANCY); + } + } +} + + + +int32_t Z_PublishAttributes(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { + const JsonObject *json = zigbee_devices.jsonGet(shortaddr); + if (json == nullptr) { return 0; } + + zigbee_devices.jsonPublishFlush(shortaddr); + return 1; +} + + + + + +int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) { + uint16_t groupid = buf.get16(2); + uint16_t clusterid = buf.get16(4); + Z_ShortAddress srcaddr = buf.get16(6); + uint8_t srcendpoint = buf.get8(8); + uint8_t dstendpoint = buf.get8(9); + uint8_t wasbroadcast = buf.get8(10); + uint8_t linkquality = buf.get8(11); + uint8_t securityuse = buf.get8(12); + uint32_t timestamp = buf.get32(13); + uint8_t seqnumber = buf.get8(17); + + bool defer_attributes = false; + + ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 19, buf.get8(18), clusterid, groupid, + srcaddr, + srcendpoint, dstendpoint, wasbroadcast, + linkquality, securityuse, seqnumber, + timestamp); + zcl_received.log(); + char shortaddr[8]; + snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr); + + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.createObject(); + + if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_DEFAULT_RESPONSE == zcl_received.getCmdId())) { + zcl_received.parseResponse(); + } else { + + if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) { + zcl_received.parseRawAttributes(json); + if (clusterid) { defer_attributes = true; } + } else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) { + zcl_received.parseReadAttributes(json); + if (clusterid) { defer_attributes = true; } + } else if (zcl_received.isClusterSpecificCommand()) { + zcl_received.parseClusterSpecificCommand(json); + } + String msg(""); + msg.reserve(100); + json.printTo(msg); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":%s}"), srcaddr, msg.c_str()); + + zcl_received.postProcessAttributes(srcaddr, json); + + json[F(D_CMND_ZIGBEE_ENDPOINT)] = srcendpoint; + + if (groupid) { + json[F(D_CMND_ZIGBEE_GROUP)] = groupid; + } + + json[F(D_CMND_ZIGBEE_LINKQUALITY)] = linkquality; + + + zigbee_devices.resetTimersForDevice(srcaddr, 0 , Z_CAT_REACHABILITY); + zigbee_devices.setReachable(srcaddr, true); + + + Z_AqaraOccupancy(srcaddr, clusterid, srcendpoint, json); + + if (defer_attributes) { + + if (zigbee_devices.jsonIsConflict(srcaddr, json)) { + + zigbee_devices.jsonPublishFlush(srcaddr); + } + zigbee_devices.jsonAppend(srcaddr, json); + zigbee_devices.setTimer(srcaddr, 0 , USE_ZIGBEE_COALESCE_ATTR_TIMER, clusterid, srcendpoint, Z_CAT_READ_ATTR, 0, &Z_PublishAttributes); + } else { + + zigbee_devices.jsonPublishNow(srcaddr, json); + } + } + return -1; +} + + +typedef struct Z_Dispatcher { + const uint8_t* match; + ZB_RecvMsgFunc func; +} Z_Dispatcher; + + +ZBM(AREQ_AF_DATA_CONFIRM, Z_AREQ | Z_AF, AF_DATA_CONFIRM) +ZBM(AREQ_AF_INCOMING_MESSAGE, Z_AREQ | Z_AF, AF_INCOMING_MSG) +ZBM(AREQ_END_DEVICE_ANNCE_IND, Z_AREQ | Z_ZDO, ZDO_END_DEVICE_ANNCE_IND) +ZBM(AREQ_END_DEVICE_TC_DEV_IND, Z_AREQ | Z_ZDO, ZDO_TC_DEV_IND) +ZBM(AREQ_PERMITJOIN_OPEN_XX, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND ) +ZBM(AREQ_ZDO_ACTIVEEPRSP, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP) +ZBM(AREQ_ZDO_SIMPLEDESCRSP, Z_AREQ | Z_ZDO, ZDO_SIMPLE_DESC_RSP) +ZBM(AREQ_ZDO_IEEE_ADDR_RSP, Z_AREQ | Z_ZDO, ZDO_IEEE_ADDR_RSP) +ZBM(AREQ_ZDO_BIND_RSP, Z_AREQ | Z_ZDO, ZDO_BIND_RSP) +ZBM(AREQ_ZDO_UNBIND_RSP, Z_AREQ | Z_ZDO, ZDO_UNBIND_RSP) +ZBM(AREQ_ZDO_MGMT_BIND_RSP, Z_AREQ | Z_ZDO, ZDO_MGMT_BIND_RSP) + + +const Z_Dispatcher Z_DispatchTable[] PROGMEM = { + { AREQ_AF_DATA_CONFIRM, &Z_DataConfirm }, + { AREQ_AF_INCOMING_MESSAGE, &Z_ReceiveAfIncomingMessage }, + { AREQ_END_DEVICE_ANNCE_IND, &Z_ReceiveEndDeviceAnnonce }, + { AREQ_END_DEVICE_TC_DEV_IND, &Z_ReceiveTCDevInd }, + { AREQ_PERMITJOIN_OPEN_XX, &Z_ReceivePermitJoinStatus }, + { AREQ_ZDO_NODEDESCRSP, &Z_ReceiveNodeDesc }, + { AREQ_ZDO_ACTIVEEPRSP, &Z_ReceiveActiveEp }, + { AREQ_ZDO_IEEE_ADDR_RSP, &Z_ReceiveIEEEAddr }, + { AREQ_ZDO_BIND_RSP, &Z_BindRsp }, + { AREQ_ZDO_UNBIND_RSP, &Z_UnbindRsp }, + { AREQ_ZDO_MGMT_BIND_RSP, &Z_MgmtBindRsp }, +}; + + + + + +int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf) { + + if (zigbee.init_phase) { + + return -1; + } else { + for (uint32_t i = 0; i < sizeof(Z_DispatchTable)/sizeof(Z_Dispatcher); i++) { + if (Z_ReceiveMatchPrefix(buf, Z_DispatchTable[i].match)) { + (*Z_DispatchTable[i].func)(res, buf); + } + } + return -1; + } +} +# 695 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" +int32_t Z_Load_Devices(uint8_t value) { + + loadZigbeeDevices(); + return 0; +} + + + + +void Z_Query_Bulb(uint16_t shortaddr, uint32_t &wait_ms) { + const uint32_t inter_message_ms = 100; + + if (0 <= zigbee_devices.getHueBulbtype(shortaddr)) { + uint8_t endpoint = zigbee_devices.findFirstEndpoint(shortaddr); + + if (endpoint) { + zigbee_devices.setTimer(shortaddr, 0 , wait_ms, 0x0006, endpoint, Z_CAT_NONE, 0 , &Z_ReadAttrCallback); + wait_ms += inter_message_ms; + zigbee_devices.setTimer(shortaddr, 0 , wait_ms, 0x0008, endpoint, Z_CAT_NONE, 0 , &Z_ReadAttrCallback); + wait_ms += inter_message_ms; + zigbee_devices.setTimer(shortaddr, 0 , wait_ms, 0x0300, endpoint, Z_CAT_NONE, 0 , &Z_ReadAttrCallback); + wait_ms += inter_message_ms; + zigbee_devices.setTimer(shortaddr, 0, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, 0, endpoint, Z_CAT_REACHABILITY, 0 , &Z_Unreachable); + wait_ms += 1000; + } + } +} + + + + +int32_t Z_Query_Bulbs(uint8_t value) { + + uint32_t wait_ms = 1000; + for (uint32_t i = 0; i < zigbee_devices.devicesSize(); i++) { + const Z_Device &device = zigbee_devices.devicesAt(i); + Z_Query_Bulb(device.shortaddr, wait_ms); + } + return 0; +} + + + + +int32_t Z_State_Ready(uint8_t value) { + zigbee.init_phase = false; + return 0; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" +#ifdef USE_ZIGBEE + +#define XDRV_23 23 + +const uint32_t ZIGBEE_BUFFER_SIZE = 256; +const uint8_t ZIGBEE_SOF = 0xFE; +const uint8_t ZIGBEE_SOF_ALT = 0xFF; + +#include +TasmotaSerial *ZigbeeSerial = nullptr; + + +const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" + D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|" + D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|" + D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|" + D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" + D_CMND_ZIGBEE_BIND "|" D_CMND_ZIGBEE_UNBIND "|" D_CMND_ZIGBEE_PING "|" D_CMND_ZIGBEE_MODELID "|" + D_CMND_ZIGBEE_LIGHT "|" D_CMND_ZIGBEE_RESTORE "|" D_CMND_ZIGBEE_BIND_STATE "|" + D_CMND_ZIGBEE_CONFIG + ; + +void (* const ZigbeeCommand[])(void) PROGMEM = { + &CmndZbZNPSend, &CmndZbPermitJoin, + &CmndZbStatus, &CmndZbReset, &CmndZbSend, + &CmndZbProbe, &CmndZbRead, &CmndZbZNPReceive, + &CmndZbForget, &CmndZbSave, &CmndZbName, + &CmndZbBind, &CmndZbUnbind, &CmndZbPing, &CmndZbModelId, + &CmndZbLight, &CmndZbRestore, &CmndZbBindState, + &CmndZbConfig, + }; + + + + +void ZigbeeInputLoop(void) +{ + static uint32_t zigbee_polling_window = 0; + static uint8_t fcs = ZIGBEE_SOF; + static uint32_t zigbee_frame_len = 5; +# 68 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" + while (ZigbeeSerial->available()) { + yield(); + uint8_t zigbee_in_byte = ZigbeeSerial->read(); + + + if (0 == zigbee_buffer->len()) { + zigbee_frame_len = 5; + fcs = ZIGBEE_SOF; + + + + if (ZIGBEE_SOF_ALT == zigbee_in_byte) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbInput forgiven first byte %02X (only for statistics)"), zigbee_in_byte); + zigbee_in_byte = ZIGBEE_SOF; + } + } + + if ((0 == zigbee_buffer->len()) && (ZIGBEE_SOF != zigbee_in_byte)) { + + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbInput discarding byte %02X"), zigbee_in_byte); + continue; + } + + if (zigbee_buffer->len() < zigbee_frame_len) { + zigbee_buffer->add8(zigbee_in_byte); + zigbee_polling_window = millis(); + fcs ^= zigbee_in_byte; + } + + if (zigbee_buffer->len() >= zigbee_frame_len) { + zigbee_polling_window = 0; + break; + } + + + if (02 == zigbee_buffer->len()) { + + uint8_t len_byte = zigbee_buffer->get8(1); + if (len_byte > 250) len_byte = 250; + + zigbee_frame_len = len_byte + 5; + } + } + + if (zigbee_buffer->len() && (millis() > (zigbee_polling_window + ZIGBEE_POLLING))) { + char hex_char[(zigbee_buffer->len() * 2) + 2]; + ToHex_P((unsigned char*)zigbee_buffer->getBuffer(), zigbee_buffer->len(), hex_char, sizeof(hex_char)); + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Bytes follow_read_metric = %0d"), ZigbeeSerial->getLoopReadMetric()); + + if (zigbee_buffer->len() != zigbee_frame_len) { + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received frame of wrong size %s, len %d, expected %d"), hex_char, zigbee_buffer->len(), zigbee_frame_len); + } else if (0x00 != fcs) { + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received bad FCS frame %s, %d"), hex_char, fcs); + } else { + + + + SBuffer znp_buffer = zigbee_buffer->subBuffer(2, zigbee_frame_len - 3); + + ToHex_P((unsigned char*)znp_buffer.getBuffer(), znp_buffer.len(), hex_char, sizeof(hex_char)); + Response_P(PSTR("{\"" D_JSON_ZIGBEEZNPRECEIVED "\":\"%s\"}"), hex_char); + if (Settings.flag3.tuya_serial_mqtt_publish) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); + XdrvRulesProcess(); + } else { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "%s"), mqtt_data); + } + + ZigbeeProcessInput(znp_buffer); + } + zigbee_buffer->setLen(0); + } +} + + + + +void ZigbeeInit(void) +{ + + if (0 == Settings.zb_channel) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Initializing Zigbee parameters from defaults")); + Settings.zb_ext_panid = USE_ZIGBEE_EXTPANID; + Settings.zb_precfgkey_l = USE_ZIGBEE_PRECFGKEY_L; + Settings.zb_precfgkey_h = USE_ZIGBEE_PRECFGKEY_H; + Settings.zb_pan_id = USE_ZIGBEE_PANID; + Settings.zb_channel = USE_ZIGBEE_CHANNEL; + Settings.zb_free_byte = 0; + } + + Z_UpdateConfig(Settings.zb_channel, Settings.zb_pan_id, Settings.zb_ext_panid, Settings.zb_precfgkey_l, Settings.zb_precfgkey_h); + + + zigbee.active = false; + if ((pin[GPIO_ZIGBEE_RX] < 99) && (pin[GPIO_ZIGBEE_TX] < 99)) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "GPIOs Rx:%d Tx:%d"), pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX]); + + ZigbeeSerial = new TasmotaSerial(pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX], seriallog_level ? 1 : 2, 0, 256); + ZigbeeSerial->begin(115200); + if (ZigbeeSerial->hardwareSerial()) { + ClaimSerial(); + uint32_t aligned_buffer = ((uint32_t)serial_in_buffer + 3) & ~3; + zigbee_buffer = new PreAllocatedSBuffer(sizeof(serial_in_buffer) - 3, (char*) aligned_buffer); + } else { + + zigbee_buffer = new SBuffer(ZIGBEE_BUFFER_SIZE); + + } + zigbee.active = true; + zigbee.init_phase = true; + zigbee.state_machine = true; + ZigbeeSerial->flush(); + } + +} + + + + + +uint32_t strToUInt(const JsonVariant &val) { + + if (val.is()) { + return val.as(); + } else { + if (val.is()) { + String sval = val.as(); + return strtoull(sval.c_str(), nullptr, 0); + } + } + return 0; +} + + +const unsigned char ZIGBEE_FACTORY_RESET[] PROGMEM = + { Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 , 0x01 }; + +void CmndZbReset(void) { + if (ZigbeeSerial) { + switch (XdrvMailbox.payload) { + case 1: + ZigbeeZNPSend(ZIGBEE_FACTORY_RESET, sizeof(ZIGBEE_FACTORY_RESET)); + eraseZigbeeDevices(); + restart_flag = 2; + ResponseCmndChar_P(PSTR(D_JSON_ZIGBEE_CC2530 " " D_JSON_RESET_AND_RESTARTING)); + break; + default: + ResponseCmndChar_P(PSTR(D_JSON_ONE_TO_RESET)); + } + } +} + + + + + +void CmndZbZNPSendOrReceive(bool send) +{ + if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) { + uint8_t code; + + char *codes = RemoveSpace(XdrvMailbox.data); + int32_t size = strlen(XdrvMailbox.data); + + SBuffer buf((size+1)/2); + + while (size > 1) { + char stemp[3]; + strlcpy(stemp, codes, sizeof(stemp)); + code = strtol(stemp, nullptr, 16); + buf.add8(code); + size -= 2; + codes += 2; + } + if (send) { + + ZigbeeZNPSend(buf.getBuffer(), buf.len()); + } else { + + ZigbeeProcessInput(buf); + } + } + ResponseCmndDone(); +} + + +void CmndZbZNPReceive(void) +{ + CmndZbZNPSendOrReceive(false); +} + +void CmndZbZNPSend(void) +{ + CmndZbZNPSendOrReceive(true); +} + +void ZigbeeZNPSend(const uint8_t *msg, size_t len) { + if ((len < 2) || (len > 252)) { + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_JSON_ZIGBEEZNPSENT ": bad message len %d"), len); + return; + } + uint8_t data_len = len - 2; + + if (ZigbeeSerial) { + uint8_t fcs = data_len; + + ZigbeeSerial->write(ZIGBEE_SOF); + + ZigbeeSerial->write(data_len); + + for (uint32_t i = 0; i < len; i++) { + uint8_t b = pgm_read_byte(msg + i); + ZigbeeSerial->write(b); + fcs ^= b; + + } + ZigbeeSerial->write(fcs); + + } + + char hex_char[(len * 2) + 2]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZNPSENT " %s"), + ToHex_P(msg, len, hex_char, sizeof(hex_char))); +} +# 312 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" +void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, uint16_t manuf, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId) { + + SBuffer buf(32+len); + buf.add8(Z_SREQ | Z_AF); + buf.add8(AF_DATA_REQUEST_EXT); + if (0x0000 == shortaddr) { + buf.add8(Z_Addr_Group); + buf.add64(groupaddr); + buf.add8(0xFF); + } else { + buf.add8(Z_Addr_ShortAddress); + buf.add64(shortaddr); + buf.add8(endpoint); + } + buf.add16(0x0000); + buf.add8(0x01); + buf.add16(clusterId); + buf.add8(transacId); + buf.add8(0x30); + buf.add8(0x1E); + + buf.add16(3 + len + (manuf ? 2 : 0)); + buf.add8((needResponse ? 0x00 : 0x10) | (clusterSpecific ? 0x01 : 0x00) | (manuf ? 0x04 : 0x00)); + if (manuf) { + buf.add16(manuf); + } + buf.add8(transacId); + buf.add8(cmdId); + if (len > 0) { + buf.addBuffer(msg, len); + } + + ZigbeeZNPSend(buf.getBuffer(), buf.len()); +} +# 363 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" +void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint, bool clusterSpecific, uint16_t manuf, + uint16_t cluster, uint8_t cmd, const char *param) { + size_t size = param ? strlen(param) : 0; + SBuffer buf((size+2)/2); + + if (param) { + while (*param) { + uint8_t code = parseHex_P(¶m, 2); + buf.add8(code); + } + } + + if ((0 == endpoint) && (shortaddr)) { + + endpoint = zigbee_devices.findFirstEndpoint(shortaddr); + + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: shortaddr 0x%04X, groupaddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"), + shortaddr, groupaddr, cluster, endpoint, cmd, param); + + if ((0 == endpoint) && (shortaddr)) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbSend: unspecified endpoint")); + return; + } + + + ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, cmd, clusterSpecific, manuf, buf.getBuffer(), buf.len(), true, zigbee_devices.getNextSeqNumber(shortaddr)); + + if (clusterSpecific) { + zigbeeSetCommandTimer(shortaddr, groupaddr, cluster, endpoint); + } +} + + + + +void CmndZbSend(void) { +# 411 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + DynamicJsonBuffer jsonBuf; + const JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data); + if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } + + + static char delim[] = ", "; + uint16_t device = 0x0000; + uint16_t groupaddr = 0x0000; + uint8_t endpoint = 0x00; + uint16_t manuf = 0x0000; + + uint16_t cluster = 0; + uint8_t cmd = 0; + String cmd_str = ""; + const char *cmd_s; + bool clusterSpecific = true; + + + const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); + if (nullptr != &val_device) { + device = zigbee_devices.parseDeviceParam(val_device.as()); + if (0xFFFF == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + } + if (0x0000 == device) { + const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group")); + if (nullptr != &val_group) { + groupaddr = strToUInt(val_group); + } else { + ResponseCmndChar_P(PSTR("Unknown device")); + return; + } + } + + const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint")); + if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); } + const JsonVariant &val_manuf = getCaseInsensitive(json, PSTR("Manuf")); + if (nullptr != &val_manuf) { manuf = strToUInt(val_manuf); } + const JsonVariant &val_cmd = getCaseInsensitive(json, PSTR("Send")); + if (nullptr != &val_cmd) { + + + + if (val_cmd.is()) { + + const JsonObject &cmd_obj = val_cmd.as(); + int32_t cmd_size = cmd_obj.size(); + if (cmd_size > 1) { + Response_P(PSTR("Only 1 command allowed (%d)"), cmd_size); + return; + } else if (1 == cmd_size) { + + JsonObject::const_iterator it = cmd_obj.begin(); + String key = it->key; + const JsonVariant& value = it->value; + uint32_t x = 0, y = 0, z = 0; + uint16_t cmd_var; + + const __FlashStringHelper* tasmota_cmd = zigbeeFindCommand(key.c_str(), &cluster, &cmd_var); + if (tasmota_cmd) { + cmd_str = tasmota_cmd; + } else { + Response_P(PSTR("Unrecognized zigbee command: %s"), key.c_str()); + return; + } + + + if (value.is()) { + x = value.as() ? 1 : 0; + } else if (value.is()) { + x = value.as(); + } else { + + const char *s_const = value.as(); + if (s_const != nullptr) { + char s[strlen(s_const)+1]; + strcpy(s, s_const); + if ((nullptr != s) && (0x00 != *s)) { + char *sval = strtok(s, delim); + if (sval) { + x = ZigbeeAliasOrNumber(sval); + sval = strtok(nullptr, delim); + if (sval) { + y = ZigbeeAliasOrNumber(sval); + sval = strtok(nullptr, delim); + if (sval) { + z = ZigbeeAliasOrNumber(sval); + } + } + } + } + } + } + + + if (0xFF == cmd_var) { + cmd = x; + x = y; + y = z; + } else { + cmd = cmd_var; + } + cmd_str = zigbeeCmdAddParams(cmd_str.c_str(), x, y, z); + + cmd_s = cmd_str.c_str(); + } else { + + } + } else if (val_cmd.is()) { + + cmd_str = val_cmd.as(); + + + + + const char * data = cmd_str.c_str(); + cluster = parseHex(&data, 4); + + + if (('_' == *data) || ('!' == *data)) { + if ('_' == *data) { clusterSpecific = false; } + data++; + } else { + ResponseCmndChar_P(PSTR("Wrong delimiter for payload")); + return; + } + + cmd = parseHex(&data, 2); + + + + if ('/' == *data) { data++; } + + cmd_s = data; + } else { + + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeZCLSend device: 0x%04X, group: 0x%04X, endpoint:%d, cluster:0x%04X, cmd:0x%02X, send:\"%s\""), + device, groupaddr, endpoint, cluster, cmd, cmd_s); + zigbeeZCLSendStr(device, groupaddr, endpoint, clusterSpecific, manuf, cluster, cmd, cmd_s); + ResponseCmndDone(); + } else { + Response_P(PSTR("Missing zigbee 'Send'")); + return; + } +} + + + + +void ZbBindUnbind(bool unbind) { + + + + + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + DynamicJsonBuffer jsonBuf; + const JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data); + if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } + + + + uint16_t srcDevice = 0xFFFF; + uint16_t dstDevice = 0xFFFF; + uint64_t dstLongAddr = 0; + uint8_t endpoint = 0x00; + uint8_t toendpoint = 0x00; + uint16_t toGroup = 0x0000; + uint16_t cluster = 0; + uint32_t group = 0xFFFFFFFF; + + + + const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); + if (nullptr != &val_device) { + srcDevice = zigbee_devices.parseDeviceParam(val_device.as()); + if (0xFFFF == srcDevice) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + } + if ((nullptr == &val_device) || (0x0000 == srcDevice)) { ResponseCmndChar_P(PSTR("Unknown source device")); return; } + + uint64_t srcLongAddr = zigbee_devices.getDeviceLongAddr(srcDevice); + if (0 == srcLongAddr) { ResponseCmndChar_P(PSTR("Unknown source IEEE address")); return; } + + const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint")); + if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); } + + const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster")); + if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); } + + + + + + const JsonVariant &dst_device = getCaseInsensitive(json, PSTR("ToDevice")); + if (nullptr != &dst_device) { + dstDevice = zigbee_devices.parseDeviceParam(dst_device.as()); + if (0xFFFF == dstDevice) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + if (0x0000 == dstDevice) { + dstLongAddr = localIEEEAddr; + } else { + dstLongAddr = zigbee_devices.getDeviceLongAddr(dstDevice); + } + if (0 == dstLongAddr) { ResponseCmndChar_P(PSTR("Unknown dest IEEE address")); return; } + + const JsonVariant &val_toendpoint = getCaseInsensitive(json, PSTR("ToEndpoint")); + if (nullptr != &val_toendpoint) { toendpoint = strToUInt(val_endpoint); } else { toendpoint = endpoint; } + } + + + const JsonVariant &to_group = getCaseInsensitive(json, PSTR("ToGroup")); + if (nullptr != &to_group) { toGroup = strToUInt(to_group); } + + + if (toGroup && dstLongAddr) { ResponseCmndChar_P(PSTR("Cannot have both \"ToDevice\" and \"ToGroup\"")); return; } + if (!toGroup && !dstLongAddr) { ResponseCmndChar_P(PSTR("Missing \"ToDevice\" or \"ToGroup\"")); return; } + + SBuffer buf(34); + buf.add8(Z_SREQ | Z_ZDO); + if (unbind) { + buf.add8(ZDO_UNBIND_REQ); + } else { + buf.add8(ZDO_BIND_REQ); + } + buf.add16(srcDevice); + buf.add64(srcLongAddr); + buf.add8(endpoint); + buf.add16(cluster); + if (dstLongAddr) { + buf.add8(Z_Addr_IEEEAddress); + buf.add64(dstLongAddr); + buf.add8(toendpoint); + } else { + buf.add8(Z_Addr_Group); + buf.add16(toGroup); + } + + ZigbeeZNPSend(buf.getBuffer(), buf.len()); + + ResponseCmndDone(); +} + + + + +void CmndZbBind(void) { + ZbBindUnbind(false); +} + + + + +void CmndZbUnbind(void) { + ZbBindUnbind(true); +} + + + + +void CmndZbBindState(void) { + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); + if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + + SBuffer buf(10); + buf.add8(Z_SREQ | Z_ZDO); + buf.add8(ZDO_MGMT_BIND_REQ); + buf.add16(shortaddr); + buf.add8(0); + + ZigbeeZNPSend(buf.getBuffer(), buf.len()); + + ResponseCmndDone(); +} + + +void CmndZbProbe(void) { + CmndZbProbeOrPing(true); +} + + + + +void CmndZbProbeOrPing(boolean probe) { + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); + if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + + + Z_SendIEEEAddrReq(shortaddr); + if (probe) { + Z_SendActiveEpReq(shortaddr); + } + ResponseCmndDone(); +} + + +void CmndZbPing(void) { + CmndZbProbeOrPing(false); +} + + + + + +void CmndZbName(void) { + + + + + + + + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + + + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); + + + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); + if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + + if (p == nullptr) { + const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr); + Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, friendlyName ? friendlyName : ""); + } else { + zigbee_devices.setFriendlyName(shortaddr, p); + Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, p); + } +} + + + + + +void CmndZbModelId(void) { + + + + + + + + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + + + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); + + + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); + if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + + if (p == nullptr) { + const char * modelId = zigbee_devices.getModelId(shortaddr); + Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), shortaddr, modelId ? modelId : ""); + } else { + zigbee_devices.setModelId(shortaddr, p); + Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), shortaddr, p); + } +} + + + + +void CmndZbLight(void) { + + + + + + + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + + + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); + + + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); + if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + + if (p) { + int8_t bulbtype = strtol(p, nullptr, 10); + if (bulbtype > 5) { bulbtype = 5; } + if (bulbtype < -1) { bulbtype = -1; } + zigbee_devices.setHueBulbtype(shortaddr, bulbtype); + } + String dump = zigbee_devices.dumpLightState(shortaddr); + Response_P(PSTR("{\"" D_PRFX_ZB D_CMND_ZIGBEE_LIGHT "\":%s}"), dump.c_str()); + + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_PRFX_ZB D_CMND_ZIGBEE_LIGHT)); + XdrvRulesProcess(); + ResponseCmndDone(); +} + + + + + +void CmndZbForget(void) { + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); + if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + + + if (zigbee_devices.removeDevice(shortaddr)) { + ResponseCmndDone(); + } else { + ResponseCmndChar_P(PSTR("Unknown device")); + } +} + + + + + +void CmndZbSave(void) { + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + saveZigbeeDevices(); + ResponseCmndDone(); +} +# 849 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" +void CmndZbRestore(void) { + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + DynamicJsonBuffer jsonBuf; + const JsonVariant json_parsed = jsonBuf.parse((const char*) XdrvMailbox.data); + const JsonVariant * json = &json_parsed; + bool success = false; + + + if (json_parsed.is()) { + success = json_parsed.as().success(); + } else if (json_parsed.is()) { + success = json_parsed.as().success(); + } + if (!success) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } + + + const JsonVariant * zbstatus = &startsWithCaseInsensitive(*json, PSTR("ZbStatus")); + if (nullptr != zbstatus) { + json = zbstatus; + } + + + if (json->is()) { + const JsonArray& arr = json->as(); + for (auto elt : arr) { + + int32_t res = zigbee_devices.deviceRestore(elt); + if (res < 0) { + ResponseCmndChar_P(PSTR("Restore failed")); + return; + } + } + } else if (json->is()) { + int32_t res = zigbee_devices.deviceRestore(*json); + if (res < 0) { + ResponseCmndChar_P(PSTR("Restore failed")); + return; + } + + } else { + ResponseCmndChar_P(PSTR("Missing parameters")); + return; + } + ResponseCmndDone(); +} + + + + + +void CmndZbRead(void) { + + + + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + DynamicJsonBuffer jsonBuf; + JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data); + if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } + + + uint16_t device = 0xFFFF; + uint16_t groupaddr = 0x0000; + uint16_t cluster = 0x0000; + uint8_t endpoint = 0x00; + uint16_t manuf = 0x0000; + size_t attrs_len = 0; + uint8_t* attrs = nullptr; + + const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); + if (nullptr != &val_device) { + device = zigbee_devices.parseDeviceParam(val_device.as()); + if (0xFFFF == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + } + if (0x0000 == device) { + const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group")); + if (nullptr != &val_group) { + groupaddr = strToUInt(val_group); + } else { + ResponseCmndChar_P(PSTR("Unknown device")); + return; + } + } + + const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster")); + if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); } + const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint")); + if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); } + const JsonVariant &val_manuf = getCaseInsensitive(json, PSTR("Manuf")); + if (nullptr != &val_manuf) { manuf = strToUInt(val_manuf); } + + const JsonVariant &val_attr = getCaseInsensitive(json, PSTR("Read")); + if (nullptr != &val_attr) { + uint16_t val = strToUInt(val_attr); + if (val_attr.is()) { + const JsonArray& attr_arr = val_attr.as(); + attrs_len = attr_arr.size() * 2; + attrs = new uint8_t[attrs_len]; + + uint32_t i = 0; + for (auto value : attr_arr) { + uint16_t val = strToUInt(value); + attrs[i++] = val & 0xFF; + attrs[i++] = val >> 8; + } + } else { + attrs_len = 2; + attrs = new uint8_t[attrs_len]; + attrs[0] = val & 0xFF; + attrs[1] = val >> 8; + } + } + + if ((0 == endpoint) && (device)) { + endpoint = zigbee_devices.findFirstEndpoint(device); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint); + } + if (0x0000 == device) { + endpoint = 0xFF; + } + + if ((0 != endpoint) && (attrs_len > 0)) { + ZigbeeZCLSend_Raw(device, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, manuf, attrs, attrs_len, true , zigbee_devices.getNextSeqNumber(device)); + ResponseCmndDone(); + } else { + ResponseCmndChar_P(PSTR("Missing parameters")); + } + + if (attrs) { delete[] attrs; } +} + + + + + +void CmndZbPermitJoin(void) { + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + uint32_t payload = XdrvMailbox.payload; + uint16_t dstAddr = 0xFFFC; + uint8_t duration = 60; + + if (payload <= 0) { + duration = 0; + } else if (99 == payload) { + duration = 0xFF; + } + + SBuffer buf(34); + buf.add8(Z_SREQ | Z_ZDO); + buf.add8(ZDO_MGMT_PERMIT_JOIN_REQ); + buf.add8(0x0F); + buf.add16(0xFFFC); + buf.add8(duration); + buf.add8(0x00); + + ZigbeeZNPSend(buf.getBuffer(), buf.len()); + + ResponseCmndDone(); +} + + + + +void CmndZbStatus(void) { + if (ZigbeeSerial) { + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); + if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + if (XdrvMailbox.payload > 0) { + if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + } + + String dump = zigbee_devices.dump(XdrvMailbox.index, shortaddr); + Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.index, dump.c_str()); + } +} + + + + +void CmndZbConfig(void) { + + + uint8_t zb_channel = Settings.zb_channel; + uint16_t zb_pan_id = Settings.zb_pan_id; + uint64_t zb_ext_panid = Settings.zb_ext_panid; + uint64_t zb_precfgkey_l = Settings.zb_precfgkey_l; + uint64_t zb_precfgkey_h = Settings.zb_precfgkey_h; + + + RemoveAllSpaces(XdrvMailbox.data); + if (strlen(XdrvMailbox.data) > 0) { + DynamicJsonBuffer jsonBuf; + const JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data); + if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } + + + const JsonVariant &val_channel = getCaseInsensitive(json, PSTR("Channel")); + if (nullptr != &val_channel) { zb_channel = strToUInt(val_channel); } + if (zb_channel < 11) { zb_channel = 11; } + if (zb_channel > 26) { zb_channel = 26; } + + const JsonVariant &val_pan_id = getCaseInsensitive(json, PSTR("PanID")); + if (nullptr != &val_pan_id) { zb_pan_id = strToUInt(val_pan_id); } + + const JsonVariant &val_ext_pan_id = getCaseInsensitive(json, PSTR("ExtPanID")); + if (nullptr != &val_ext_pan_id) { zb_ext_panid = strtoull(val_ext_pan_id.as(), nullptr, 0); } + + const JsonVariant &val_key_l = getCaseInsensitive(json, PSTR("KeyL")); + if (nullptr != &val_key_l) { zb_precfgkey_l = strtoull(val_key_l.as(), nullptr, 0); } + + const JsonVariant &val_key_h = getCaseInsensitive(json, PSTR("KeyH")); + if (nullptr != &val_key_h) { zb_precfgkey_h = strtoull(val_key_h.as(), nullptr, 0); } + + + if ( (zb_channel != Settings.zb_channel) || + (zb_pan_id != Settings.zb_pan_id) || + (zb_ext_panid != Settings.zb_ext_panid) || + (zb_precfgkey_l != Settings.zb_precfgkey_l) || + (zb_precfgkey_h != Settings.zb_precfgkey_h) ) { + Settings.zb_channel = zb_channel; + Settings.zb_pan_id = zb_pan_id; + Settings.zb_ext_panid = zb_ext_panid; + Settings.zb_precfgkey_l = zb_precfgkey_l; + Settings.zb_precfgkey_h = zb_precfgkey_h; + restart_flag = 2; + } + } + + + char hex_ext_panid[20] = "0x"; + Uint64toHex(zb_ext_panid, &hex_ext_panid[2], 64); + char hex_precfgkey_l[20] = "0x"; + Uint64toHex(zb_precfgkey_l, &hex_precfgkey_l[2], 64); + char hex_precfgkey_h[20] = "0x"; + Uint64toHex(zb_precfgkey_h, &hex_precfgkey_h[2], 64); + + + Response_P(PSTR("{\"" D_PRFX_ZB D_JSON_ZIGBEE_CONFIG "\":{" + "\"Channel\":%d" + ",\"PanID\":\"0x%04X\"" + ",\"ExtPanID\":\"%s\"" + ",\"KeyL\":\"%s\"" + ",\"KeyH\":\"%s\"" + "}}"), + zb_channel, zb_pan_id, + hex_ext_panid, + hex_precfgkey_l, hex_precfgkey_h); +} + + + + + +bool Xdrv23(uint8_t function) +{ + bool result = false; + + if (zigbee.active) { + switch (function) { + case FUNC_EVERY_50_MSECOND: + if (!zigbee.init_phase) { + zigbee_devices.runTimer(); + } + break; + case FUNC_LOOP: + if (ZigbeeSerial) { ZigbeeInputLoop(); } + if (zigbee.state_machine) { + ZigbeeStateMachine_Run(); + } + break; + case FUNC_PRE_INIT: + ZigbeeInit(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kZbCommands, ZigbeeCommand); + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_24_buzzer.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_24_buzzer.ino" +#ifdef USE_BUZZER + + + + +#define XDRV_24 24 + +struct BUZZER { + uint32_t tune = 0; + uint32_t tune_reload = 0; + bool active = true; + bool enable = false; + uint8_t inverted = 0; + uint8_t count = 0; + uint8_t mode = 0; + uint8_t set[2]; + uint8_t duration; + uint8_t state = 0; +} Buzzer; + + + +void BuzzerOff(void) +{ + DigitalWrite(GPIO_BUZZER, Buzzer.inverted); +} + + +void BuzzerBeep(uint32_t count, uint32_t on, uint32_t off, uint32_t tune, uint32_t mode) +{ + Buzzer.set[0] = off; + Buzzer.set[1] = on; + Buzzer.duration = 1; + Buzzer.tune_reload = 0; + Buzzer.mode = mode; + + if (tune) { + uint32_t tune1 = tune; + uint32_t tune2 = tune; + for (uint32_t i = 0; i < 32; i++) { + if (!(tune2 & 0x80000000)) { + tune2 <<= 1; + } else { + Buzzer.tune_reload <<= 1; + Buzzer.tune_reload |= tune1 & 1; + tune1 >>= 1; + } + } + Buzzer.tune = Buzzer.tune_reload; + } + Buzzer.count = count * 2; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BUZ: %d(%d),%d,%d,0x%08X(0x%08X)"), count, Buzzer.count, on, off, tune, Buzzer.tune); + + Buzzer.enable = (Buzzer.count > 0); + if (!Buzzer.enable) { + BuzzerOff(); + } +} + +void BuzzerSetStateToLed(uint32_t state) +{ + if (Buzzer.enable && (2 == Buzzer.mode)) { + Buzzer.state = (state != 0); + DigitalWrite(GPIO_BUZZER, (Buzzer.inverted) ? !Buzzer.state : Buzzer.state); + } +} + +void BuzzerBeep(uint32_t count) +{ + BuzzerBeep(count, 1, 1, 0, 0); +} + +void BuzzerEnabledBeep(uint32_t count, uint32_t duration) +{ + if (Settings.flag3.buzzer_enable) { + BuzzerBeep(count, duration, 1, 0, 0); + } +} + + + +bool BuzzerPinState(void) +{ + if (XdrvMailbox.index == GPIO_BUZZER_INV) { + Buzzer.inverted = 1; + XdrvMailbox.index -= (GPIO_BUZZER_INV - GPIO_BUZZER); + return true; + } + return false; +} + +void BuzzerInit(void) +{ + if (pin[GPIO_BUZZER] < 99) { + pinMode(pin[GPIO_BUZZER], OUTPUT); + BuzzerOff(); + } else { + Buzzer.active = false; + } +} + +void BuzzerEvery100mSec(void) +{ + if (Buzzer.enable && (Buzzer.mode != 2)) { + if (Buzzer.count) { + if (Buzzer.duration) { + Buzzer.duration--; + if (!Buzzer.duration) { + if (Buzzer.tune) { + Buzzer.state = Buzzer.tune & 1; + Buzzer.tune >>= 1; + } else { + Buzzer.tune = Buzzer.tune_reload; + Buzzer.count -= (Buzzer.tune_reload) ? 2 : 1; + Buzzer.state = Buzzer.count & 1; + if (Buzzer.mode) { + Buzzer.count |= 2; + } + } + Buzzer.duration = Buzzer.set[Buzzer.state]; + } + } + DigitalWrite(GPIO_BUZZER, (Buzzer.inverted) ? !Buzzer.state : Buzzer.state); + } else { + Buzzer.enable = false; + } + } +} + + + + + +const char kBuzzerCommands[] PROGMEM = "|" + "Buzzer" ; + +void (* const BuzzerCommand[])(void) PROGMEM = { + &CmndBuzzer }; + +void CmndBuzzer(void) +{ +# 174 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_24_buzzer.ino" + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload != 0) { + uint32_t parm[4] = { 0 }; + uint32_t mode = 0; + ParseParameters(4, parm); + if (XdrvMailbox.payload <= 0) { + parm[0] = 1; + mode = -XdrvMailbox.payload; + } + for (uint32_t i = 1; i < 3; i++) { + if (parm[i] < 1) { parm[i] = 1; } + } + BuzzerBeep(parm[0], parm[1], parm[2], parm[3], mode); + } else { + BuzzerBeep(0); + } + } else { + BuzzerBeep(1); + } + ResponseCmndDone(); +} + + + + + +bool Xdrv24(uint8_t function) +{ + bool result = false; + + if (Buzzer.active) { + switch (function) { + case FUNC_EVERY_100_MSECOND: + BuzzerEvery100mSec(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kBuzzerCommands, BuzzerCommand); + break; + case FUNC_PRE_INIT: + BuzzerInit(); + break; + case FUNC_PIN_STATE: + result = BuzzerPinState(); + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_25_A4988_Stepper.ino" +# 21 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_25_A4988_Stepper.ino" +#ifdef USE_A4988_STEPPER + + + + +#define XDRV_25 25 + +#include + +short A4988_dir_pin = pin[GPIO_MAX]; +short A4988_stp_pin = pin[GPIO_MAX]; +short A4988_ms1_pin = pin[GPIO_MAX]; +short A4988_ms2_pin = pin[GPIO_MAX]; +short A4988_ms3_pin = pin[GPIO_MAX]; +short A4988_ena_pin = pin[GPIO_MAX]; +int A4988_spr = 0; +float A4988_rpm = 0; +short A4988_mis = 0; + +A4988_Stepper* myA4988 = nullptr; + +void A4988Init(void) +{ + A4988_dir_pin = pin[GPIO_A4988_DIR]; + A4988_stp_pin = pin[GPIO_A4988_STP]; + A4988_ena_pin = pin[GPIO_A4988_ENA]; + A4988_ms1_pin = pin[GPIO_A4988_MS1]; + A4988_ms2_pin = pin[GPIO_A4988_MS2]; + A4988_ms3_pin = pin[GPIO_A4988_MS3]; + A4988_spr = 200; + A4988_rpm = 30; + A4988_mis = 1; + + myA4988 = new A4988_Stepper( A4988_spr + , A4988_rpm + , A4988_mis + , A4988_dir_pin + , A4988_stp_pin + , A4988_ena_pin + , A4988_ms1_pin + , A4988_ms2_pin + , A4988_ms3_pin ); +} + +const char kA4988Commands[] PROGMEM = "Motor|" + "Move|Rotate|Turn|MIS|SPR|RPM"; + +void (* const A4988Command[])(void) PROGMEM = { + &CmndDoMove,&CmndDoRotate,&CmndDoTurn,&CmndSetMIS,&CmndSetSPR,&CmndSetRPM}; + +void CmndDoMove(void) { + if (XdrvMailbox.data_len > 0) { + long stepsPlease = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->doMove(stepsPlease); + ResponseCmndDone(); + } +} + +void CmndDoRotate(void) { + if (XdrvMailbox.data_len > 0) { + long degrsPlease = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->doRotate(degrsPlease); + ResponseCmndDone(); + } +} + +void CmndDoTurn(void) { + if (XdrvMailbox.data_len > 0) { + float turnsPlease = strtod(XdrvMailbox.data,nullptr); + myA4988->doTurn(turnsPlease); + ResponseCmndDone(); + } +} + +void CmndSetMIS(void) { + if ((pin[GPIO_A4988_MS1] < 99) && (pin[GPIO_A4988_MS2] < 99) && (pin[GPIO_A4988_MS3] < 99) && (XdrvMailbox.data_len > 0)) { + short newMIS = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->setMIS(newMIS); + ResponseCmndDone(); + } +} + +void CmndSetSPR(void) { + if (XdrvMailbox.data_len > 0) { + int newSPR = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->setSPR(newSPR); + ResponseCmndDone(); + } +} + +void CmndSetRPM(void) { + if (XdrvMailbox.data_len > 0) { + short newRPM = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->setRPM(newRPM); + ResponseCmndDone(); + } +} + + + + +bool Xdrv25(uint8_t function) +{ + bool result = false; + if ((pin[GPIO_A4988_DIR] < 99) && (pin[GPIO_A4988_STP] < 99)) { + switch (function) { + case FUNC_INIT: + A4988Init(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kA4988Commands, A4988Command); + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_26_ariluxrf.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_26_ariluxrf.ino" +#ifdef USE_LIGHT +#ifdef USE_ARILUX_RF + + + + +#define XDRV_26 26 + +const uint32_t ARILUX_RF_TIME_AVOID_DUPLICATE = 1000; + +const uint8_t ARILUX_RF_MAX_CHANGES = 51; +const uint32_t ARILUX_RF_SEPARATION_LIMIT = 4300; +const uint32_t ARILUX_RF_RECEIVE_TOLERANCE = 60; + +struct ARILUX { + unsigned int rf_timings[ARILUX_RF_MAX_CHANGES]; + + unsigned long rf_received_value = 0; + unsigned long rf_last_received_value = 0; + unsigned long rf_last_time = 0; + unsigned long rf_lasttime = 0; + + unsigned int rf_change_count = 0; + unsigned int rf_repeat_count = 0; + + uint8_t rf_toggle = 0; +} Arilux; + +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 +#ifndef USE_WS2812_DMA +void AriluxRfInterrupt(void) ICACHE_RAM_ATTR; +#endif +#endif + +void AriluxRfInterrupt(void) +{ + unsigned long time = micros(); + unsigned int duration = time - Arilux.rf_lasttime; + + if (duration > ARILUX_RF_SEPARATION_LIMIT) { + if (abs(duration - Arilux.rf_timings[0]) < 200) { + Arilux.rf_repeat_count++; + if (Arilux.rf_repeat_count == 2) { + unsigned long code = 0; + const unsigned int delay = Arilux.rf_timings[0] / 31; + const unsigned int delayTolerance = delay * ARILUX_RF_RECEIVE_TOLERANCE / 100; + for (unsigned int i = 1; i < Arilux.rf_change_count -1; i += 2) { + code <<= 1; + if (abs(Arilux.rf_timings[i] - (delay *3)) < delayTolerance && abs(Arilux.rf_timings[i +1] - delay) < delayTolerance) { + code |= 1; + } + } + if (Arilux.rf_change_count > 49) { + Arilux.rf_received_value = code; + } + Arilux.rf_repeat_count = 0; + } + } + Arilux.rf_change_count = 0; + } + if (Arilux.rf_change_count >= ARILUX_RF_MAX_CHANGES) { + Arilux.rf_change_count = 0; + Arilux.rf_repeat_count = 0; + } + Arilux.rf_timings[Arilux.rf_change_count++] = duration; + Arilux.rf_lasttime = time; +} + +void AriluxRfHandler(void) +{ + unsigned long now = millis(); + if (Arilux.rf_received_value && !((Arilux.rf_received_value == Arilux.rf_last_received_value) && (now - Arilux.rf_last_time < ARILUX_RF_TIME_AVOID_DUPLICATE))) { + Arilux.rf_last_received_value = Arilux.rf_received_value; + Arilux.rf_last_time = now; + + uint16_t hostcode = Arilux.rf_received_value >> 8 & 0xFFFF; + if (Settings.rf_code[1][6] == Settings.rf_code[1][7]) { + Settings.rf_code[1][6] = hostcode >> 8 & 0xFF; + Settings.rf_code[1][7] = hostcode & 0xFF; + } + uint16_t stored_hostcode = Settings.rf_code[1][6] << 8 | Settings.rf_code[1][7]; + + DEBUG_DRIVER_LOG(PSTR(D_LOG_RFR D_HOST D_CODE " 0x%04X, " D_RECEIVED " 0x%06X"), stored_hostcode, Arilux.rf_received_value); + + if (hostcode == stored_hostcode) { + char command[33]; + char value = '-'; + command[0] = '\0'; + uint8_t keycode = Arilux.rf_received_value & 0xFF; + switch (keycode) { + case 1: + case 3: + snprintf_P(command, sizeof(command), PSTR(D_CMND_POWER " %d"), (1 == keycode) ? 1 : 0); + break; + case 2: + Arilux.rf_toggle++; + Arilux.rf_toggle &= 0x3; + snprintf_P(command, sizeof(command), PSTR(D_CMND_COLOR " %d"), 200 + Arilux.rf_toggle); + break; + case 4: + value = '+'; + case 7: + snprintf_P(command, sizeof(command), PSTR(D_CMND_SPEED " %c"), value); + break; + case 5: + value = '+'; + case 8: + snprintf_P(command, sizeof(command), PSTR(D_CMND_SCHEME " %c"), value); + break; + case 6: + value = '+'; + case 9: + snprintf_P(command, sizeof(command), PSTR(D_CMND_DIMMER " %c"), value); + break; + default: { + if ((keycode >= 10) && (keycode <= 21)) { + snprintf_P(command, sizeof(command), PSTR(D_CMND_COLOR " %d"), keycode -9); + } + } + } + if (strlen(command)) { + ExecuteCommand(command, SRC_LIGHT); + } + } + } + Arilux.rf_received_value = 0; +} + +void AriluxRfInit(void) +{ + if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_ARIRFSEL] < 99)) { + if (Settings.last_module != Settings.module) { + Settings.rf_code[1][6] = 0; + Settings.rf_code[1][7] = 0; + Settings.last_module = Settings.module; + } + Arilux.rf_received_value = 0; + + digitalWrite(pin[GPIO_ARIRFSEL], 0); + attachInterrupt(pin[GPIO_ARIRFRCV], AriluxRfInterrupt, CHANGE); + } +} + +void AriluxRfDisable(void) +{ + if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_ARIRFSEL] < 99)) { + detachInterrupt(pin[GPIO_ARIRFRCV]); + digitalWrite(pin[GPIO_ARIRFSEL], 1); + } +} + + + + + +bool Xdrv26(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_50_MSECOND: + if (pin[GPIO_ARIRFRCV] < 99) { AriluxRfHandler(); } + break; + case FUNC_EVERY_SECOND: + if (10 == uptime) { AriluxRfInit(); } + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_27_shutter.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_27_shutter.ino" +#ifdef USE_SHUTTER + + + + +#define XDRV_27 27 + +#define D_SHUTTER "SHUTTER" + +const uint16_t MOTOR_STOP_TIME = 500; +const uint8_t steps_per_second = 20; + +uint8_t calibrate_pos[6] = {0,30,50,70,90,100}; +uint16_t messwerte[5] = {30,50,70,90,100}; +uint16_t last_execute_step; + +enum ShutterModes { SHT_OFF_OPEN__OFF_CLOSE, SHT_OFF_ON__OPEN_CLOSE, SHT_PULSE_OPEN__PULSE_CLOSE, SHT_OFF_ON__OPEN_CLOSE_STEPPER,}; +enum ShutterButtonStates { SHT_NOT_PRESSED, SHT_PRESSED_MULTI, SHT_PRESSED_HOLD, SHT_PRESSED_IMMEDIATE, SHT_PRESSED_EXT_HOLD, SHT_PRESSED_MULTI_SIMULTANEOUS, SHT_PRESSED_HOLD_SIMULTANEOUS, SHT_PRESSED_EXT_HOLD_SIMULTANEOUS,}; + +const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|" + D_CMND_SHUTTER_OPEN "|" D_CMND_SHUTTER_CLOSE "|" D_CMND_SHUTTER_TOGGLE "|" D_CMND_SHUTTER_STOP "|" D_CMND_SHUTTER_POSITION "|" + D_CMND_SHUTTER_OPENTIME "|" D_CMND_SHUTTER_CLOSETIME "|" D_CMND_SHUTTER_RELAY "|" + D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|" + D_CMND_SHUTTER_MOTORDELAY "|" D_CMND_SHUTTER_FREQUENCY "|" D_CMND_SHUTTER_BUTTON "|" D_CMND_SHUTTER_LOCK "|" D_CMND_SHUTTER_ENABLEENDSTOPTIME "|" D_CMND_SHUTTER_INVERTWEBBUTTONS "|" + D_CMND_SHUTTER_STOPOPEN "|" D_CMND_SHUTTER_STOPCLOSE "|" D_CMND_SHUTTER_STOPTOGGLE "|" D_CMND_SHUTTER_STOPPOSITION; + +void (* const ShutterCommand[])(void) PROGMEM = { + &CmndShutterOpen, &CmndShutterClose, &CmndShutterToggle, &CmndShutterStop, &CmndShutterPosition, + &CmndShutterOpenTime, &CmndShutterCloseTime, &CmndShutterRelay, + &CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay, + &CmndShutterFrequency, &CmndShutterButton, &CmndShutterLock, &CmndShutterEnableEndStopTime, &CmndShutterInvertWebButtons, + &CmndShutterStopOpen, &CmndShutterStopClose, &CmndShutterStopToggle, &CmndShutterStopPosition}; + + const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"Direction\":%d,\"Target\":%d}"; + const char JSON_SHUTTER_BUTTON[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Button%d\":%d}"; + +#include + +Ticker TickerShutter; + +struct SHUTTER { + power_t mask = 0; + power_t old_power = 0; + power_t switched_relay = 0; + uint32_t time[MAX_SHUTTERS]; + int32_t open_max[MAX_SHUTTERS]; + int32_t target_position[MAX_SHUTTERS]; + int32_t start_position[MAX_SHUTTERS]; + int32_t real_position[MAX_SHUTTERS]; + uint16_t open_time[MAX_SHUTTERS]; + uint16_t close_time[MAX_SHUTTERS]; + uint16_t close_velocity[MAX_SHUTTERS]; + int8_t direction[MAX_SHUTTERS]; + uint8_t mode = 0; + int16_t motordelay[MAX_SHUTTERS]; + int16_t pwm_frequency[MAX_SHUTTERS]; + uint16_t max_pwm_frequency = 1000; + uint16_t max_close_pwm_frequency[MAX_SHUTTERS]; + uint8_t skip_relay_change; + int32_t accelerator[MAX_SHUTTERS]; + uint8_t start_reported = 0; +} Shutter; + +void ShutterLogPos(uint32_t i) +{ + char stemp2[10]; + dtostrfd((float)Shutter.time[i] / steps_per_second, 2, stemp2); + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter%d Real %d, Start %d, Stop %d, Dir %d, Delay %d, Rtc %s [s], Freq %d"), + i+1, Shutter.real_position[i], Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i], Shutter.motordelay[i], stemp2, Shutter.pwm_frequency[i]); +} + +void ShutterRtc50mS(void) +{ + for (uint8_t i = 0; i < shutters_present; i++) { + Shutter.time[i]++; + if (Shutter.accelerator[i]) { + + Shutter.pwm_frequency[i] += Shutter.accelerator[i]; + Shutter.pwm_frequency[i] = tmax(0,tmin(Shutter.direction[i]==1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i],Shutter.pwm_frequency[i])); + analogWriteFreq(Shutter.pwm_frequency[i]); + analogWrite(pin[GPIO_PWM1+i], 50); + } + } +} + +#define SHT_DIV_ROUND(__A,__B) (((__A) + (__B)/2) / (__B)) + +int32_t ShutterPercentToRealPosition(uint32_t percent, uint32_t index) +{ + if (Settings.shutter_set50percent[index] != 50) { + return (percent <= 5) ? Settings.shuttercoeff[2][index] * percent : Settings.shuttercoeff[1][index] * percent + Settings.shuttercoeff[0][index]; + } else { + uint32_t realpos; + + for (uint32_t j = 0; j < 5; j++) { + if (0 == Settings.shuttercoeff[j][index]) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SHT: RESET/INIT CALIBRATION MATRIX DIV 0")); + for (uint32_t k = 0; k < 5; k++) { + Settings.shuttercoeff[k][index] = SHT_DIV_ROUND(calibrate_pos[k+1] * 1000, calibrate_pos[5]); + } + } + } + for (uint32_t i = 0; i < 5; i++) { + if ((percent * 10) >= Settings.shuttercoeff[i][index]) { + realpos = SHT_DIV_ROUND(Shutter.open_max[index] * calibrate_pos[i+1], 100); + + } else { + if (0 == i) { + realpos = SHT_DIV_ROUND(SHT_DIV_ROUND(percent * Shutter.open_max[index] * calibrate_pos[i+1], Settings.shuttercoeff[i][index]), 10); + } else { + + + realpos += SHT_DIV_ROUND(SHT_DIV_ROUND((percent*10 - Settings.shuttercoeff[i-1][index] ) * Shutter.open_max[index] * (calibrate_pos[i+1] - calibrate_pos[i]), Settings.shuttercoeff[i][index] - Settings.shuttercoeff[i-1][index]), 100); + } + break; + } + } + return realpos; + } +} + +uint8_t ShutterRealToPercentPosition(int32_t realpos, uint32_t index) +{ + if (Settings.shutter_set50percent[index] != 50) { + return (Settings.shuttercoeff[2][index] * 5 > realpos) ? SHT_DIV_ROUND(realpos, Settings.shuttercoeff[2][index]) : SHT_DIV_ROUND(realpos-Settings.shuttercoeff[0][index], Settings.shuttercoeff[1][index]); + } else { + uint16_t realpercent; + + for (uint32_t i = 0; i < 5; i++) { + if (realpos >= Shutter.open_max[index] * calibrate_pos[i+1] / 100) { + realpercent = SHT_DIV_ROUND(Settings.shuttercoeff[i][index], 10); + + } else { + if (0 == i) { + realpercent = SHT_DIV_ROUND(SHT_DIV_ROUND((realpos - SHT_DIV_ROUND(Shutter.open_max[index] * calibrate_pos[i], 100)) * 10 * Settings.shuttercoeff[i][index], calibrate_pos[i+1]), Shutter.open_max[index]); + } else { + + + + realpercent += SHT_DIV_ROUND(SHT_DIV_ROUND((realpos - SHT_DIV_ROUND(Shutter.open_max[index] * calibrate_pos[i], 100)) * 10 * (Settings.shuttercoeff[i][index] - Settings.shuttercoeff[i-1][index]), (calibrate_pos[i+1] - calibrate_pos[i])), Shutter.open_max[index]) ; + } + break; + } + } + return realpercent; + } +} + +void ShutterInit(void) +{ + shutters_present = 0; + Shutter.mask = 0; + + Shutter.old_power = power; + bool relay_in_interlock = false; + + + if (Settings.shutter_startrelay[MAX_SHUTTERS] == 0) { + Shutter.max_pwm_frequency = Settings.shuttercoeff[4][3] > 0 ? Settings.shuttercoeff[4][3] : Shutter.max_pwm_frequency; + } + for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { + + Settings.shutter_startrelay[i] = (Settings.shutter_startrelay[i] == 0 && i == 0? 1 : Settings.shutter_startrelay[i]); + if (Settings.shutter_startrelay[i] && (Settings.shutter_startrelay[i] < 9)) { + shutters_present++; + + + Shutter.mask |= 3 << (Settings.shutter_startrelay[i] -1) ; + + for (uint32_t j = 0; j < MAX_INTERLOCKS * Settings.flag.interlock; j++) { + + if (Settings.interlock[j] && (Settings.interlock[j] & Shutter.mask)) { + + relay_in_interlock = true; + } + } + if (relay_in_interlock) { + if (Settings.pulse_timer[i] > 0) { + Shutter.mode = SHT_PULSE_OPEN__PULSE_CLOSE; + } else { + Shutter.mode = SHT_OFF_OPEN__OFF_CLOSE; + } + } else { + Shutter.mode = SHT_OFF_ON__OPEN_CLOSE; + if ((pin[GPIO_PWM1+i] < 99) && (pin[GPIO_CNTR1+i] < 99)) { + Shutter.mode = SHT_OFF_ON__OPEN_CLOSE_STEPPER; + Shutter.pwm_frequency[i] = 0; + Shutter.accelerator[i] = 0; + analogWriteFreq(Shutter.pwm_frequency[i]); + analogWrite(pin[GPIO_PWM1+i], 50); + } + } + + TickerShutter.attach_ms(50, ShutterRtc50mS ); + + Settings.shutter_set50percent[i] = (Settings.shutter_set50percent[i] > 0) ? Settings.shutter_set50percent[i] : 50; + + + Shutter.open_time[i] = (Settings.shutter_opentime[i] > 0) ? Settings.shutter_opentime[i] : 100; + Shutter.close_time[i] = (Settings.shutter_closetime[i] > 0) ? Settings.shutter_closetime[i] : 100; + + + Shutter.open_max[i] = 200 * Shutter.open_time[i]; + Shutter.close_velocity[i] = Shutter.open_max[i] / Shutter.close_time[i] / 2 ; + Shutter.max_close_pwm_frequency[i] = Shutter.max_pwm_frequency*Shutter.open_time[i] / Shutter.close_time[i]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d Closefreq: %d"),i, Shutter.max_close_pwm_frequency[i]); + + + if (Settings.shutter_set50percent[i] != 50) { + Settings.shuttercoeff[1][i] = Shutter.open_max[i] * (100 - Settings.shutter_set50percent[i] ) / 5000; + Settings.shuttercoeff[0][i] = Shutter.open_max[i] - (Settings.shuttercoeff[1][i] * 100); + Settings.shuttercoeff[2][i] = (Settings.shuttercoeff[0][i] + 5 * Settings.shuttercoeff[1][i]) / 5; + } + Shutter.mask |= 3 << (Settings.shutter_startrelay[i] -1); + + Shutter.real_position[i] = ShutterPercentToRealPosition(Settings.shutter_position[i], i); + + Shutter.start_position[i] = Shutter.target_position[i] = Shutter.real_position[i]; + Shutter.motordelay[i] = Settings.shutter_motordelay[i]; + + char shutter_open_chr[10]; + dtostrfd((float)Shutter.open_time[i] / 10 , 1, shutter_open_chr); + char shutter_close_chr[10]; + dtostrfd((float)Shutter.close_time[i] / 10, 1, shutter_close_chr); + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d (Relay:%d): Init. Pos: %d [%d %%], Open Vel.: 100, Close Vel.: %d , Max Way: %d, Opentime %s [s], Closetime %s [s], CoeffCalc: c0: %d, c1 %d, c2: %d, c3: %d, c4: %d, binmask %d, is inverted %d, is locked %d, end stop time enabled %d, webButtons inverted %d, shuttermode %d, motordelay %d"), + i+1, Settings.shutter_startrelay[i], Shutter.real_position[i], Settings.shutter_position[i], Shutter.close_velocity[i], Shutter.open_max[i], shutter_open_chr, shutter_close_chr, + Settings.shuttercoeff[0][i], Settings.shuttercoeff[1][i], Settings.shuttercoeff[2][i], Settings.shuttercoeff[3][i], Settings.shuttercoeff[4][i], + Shutter.mask, (Settings.shutter_options[i]&1) ? 1 : 0, (Settings.shutter_options[i]&2) ? 1 : 0, (Settings.shutter_options[i]&4) ? 1 : 0, (Settings.shutter_options[i]&8) ? 1 : 0, Shutter.mode, Shutter.motordelay[i]); + + } else { + + break; + } + ShutterLimitRealAndTargetPositions(i); + Settings.shutter_accuracy = 1; + } +} + +void ShutterReportPosition(bool always) +{ + Response_P(PSTR("{")); + rules_flag.shutter_moving = 0; + for (uint32_t i = 0; i < shutters_present; i++) { + + uint32_t position = ShutterRealToPercentPosition(Shutter.real_position[i], i); + if (Shutter.direction[i] != 0) { + rules_flag.shutter_moving = 1; + ShutterLogPos(i); + } + if (i) { ResponseAppend_P(PSTR(",")); } + uint32_t target = ShutterRealToPercentPosition(Shutter.target_position[i], i); + ResponseAppend_P(JSON_SHUTTER_POS, i+1, (Settings.shutter_options[i] & 1) ? 100-position : position, Shutter.direction[i],(Settings.shutter_options[i] & 1) ? 100-target : target ); + } + ResponseJsonEnd(); + if (always || (rules_flag.shutter_moving)) { + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_PRFX_SHUTTER)); + + } + + + +} + +void ShutterLimitRealAndTargetPositions(uint32_t i) { + if (Shutter.real_position[i]<0) Shutter.real_position[i] = 0; + if (Shutter.real_position[i]>Shutter.open_max[i]) Shutter.real_position[i] = Shutter.open_max[i]; + if (Shutter.target_position[i]<0) Shutter.target_position[i] = 0; + if (Shutter.target_position[i]>Shutter.open_max[i]) Shutter.target_position[i] = Shutter.open_max[i]; +} + +void ShutterUpdatePosition(void) +{ + + char scommand[CMDSZ]; + char stopic[TOPSZ]; + + for (uint32_t i = 0; i < shutters_present; i++) { + if (Shutter.direction[i] != 0) { + int32_t stop_position_delta = 20; + if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) { + + + Shutter.real_position[i] = ShutterCounterBasedPosition(i); + if (!Shutter.start_reported) { + ShutterReportPosition(true); + XdrvRulesProcess(); + Shutter.start_reported = 1; + } + + int32_t max_frequency = Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i]; + int32_t max_freq_change_per_sec = Shutter.max_pwm_frequency*steps_per_second / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); + int32_t min_runtime_ms = Shutter.pwm_frequency[i]*1000 / max_freq_change_per_sec; + int32_t velocity = Shutter.direction[i] == 1 ? 100 : Shutter.close_velocity[i]; + int32_t minstopway = min_runtime_ms * velocity / 100 * Shutter.pwm_frequency[i] / max_frequency * Shutter.direction[i] ; + + int32_t next_possible_stop = Shutter.real_position[i] + minstopway ; + stop_position_delta =200 * Shutter.pwm_frequency[i]/max_frequency + Shutter.direction[i] * (next_possible_stop - Shutter.target_position[i]); + + + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, velocity %d, minstopway %d,cur_freq %d, max_frequency %d, act_freq_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d"),Shutter.time[i],velocity,minstopway, + Shutter.pwm_frequency[i],max_frequency, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i]); + + if (Shutter.accelerator[i] < 0 || next_possible_stop * Shutter.direction[i] > (Shutter.target_position[i]- (100 * Shutter.direction[i])) * Shutter.direction[i] ) { + + Shutter.accelerator[i] = - tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*11/200); + + } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_frequency[i] == max_frequency) { + Shutter.accelerator[i] = 0; + } + } else { + Shutter.real_position[i] = Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); + } + if ( Shutter.real_position[i] * Shutter.direction[i] + stop_position_delta >= Shutter.target_position[i] * Shutter.direction[i] ) { + + + uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter.direction[i] == 1 ? 0 : 1) ; + int16_t missing_steps; + + switch (Shutter.mode) { + case SHT_PULSE_OPEN__PULSE_CLOSE: + + if (SRC_PULSETIMER == last_source || SRC_SHUTTER == last_source || SRC_WEBGUI == last_source) { + ExecuteCommandPower(cur_relay, 1, SRC_SHUTTER); + } else { + last_source = SRC_SHUTTER; + } + break; + case SHT_OFF_ON__OPEN_CLOSE_STEPPER: + missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) - RtcSettings.pulse_counter[i]; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter.pwm_frequency[i]); + Shutter.accelerator[i] = 0; + Shutter.pwm_frequency[i] = Shutter.pwm_frequency[i] > 250 ? 250 : Shutter.pwm_frequency[i]; + analogWriteFreq(Shutter.pwm_frequency[i]); + analogWrite(pin[GPIO_PWM1+i], 50); + Shutter.pwm_frequency[i] = 0; + analogWriteFreq(Shutter.pwm_frequency[i]); + while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) { + delay(1); + } + analogWrite(pin[GPIO_PWM1+i], 0); + Shutter.real_position[i] = ShutterCounterBasedPosition(i); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Real %d, pulsecount %d, start %d"), Shutter.real_position[i],RtcSettings.pulse_counter[i], Shutter.start_position[i]); + + if ((1 << (Settings.shutter_startrelay[i]-1)) & power) { + ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); + ExecuteCommandPower(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER); + } + break; + case SHT_OFF_ON__OPEN_CLOSE: + if ((1 << (Settings.shutter_startrelay[i]-1)) & power) { + ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); + ExecuteCommandPower(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER); + } + break; + case SHT_OFF_OPEN__OFF_CLOSE: + + if ((1 << (cur_relay-1)) & power) { + + ExecuteCommandPower(cur_relay, 0, SRC_SHUTTER); + } + break; + } + ShutterLimitRealAndTargetPositions(i); + Settings.shutter_position[i] = ShutterRealToPercentPosition(Shutter.real_position[i], i); + + ShutterLogPos(i); + + Shutter.start_position[i] = Shutter.real_position[i]; + + + snprintf_P(scommand, sizeof(scommand),PSTR(D_SHUTTER "%d"), i+1); + GetTopic_P(stopic, STAT, mqtt_topic, scommand); + Response_P("%d", (Settings.shutter_options[i] & 1) ? 100 - Settings.shutter_position[i]: Settings.shutter_position[i]); + MqttPublish(stopic, Settings.flag.mqtt_power_retain); + + Shutter.direction[i] = 0; + ShutterReportPosition(true); + rules_flag.shutter_moved = 1; + XdrvRulesProcess(); + } + } + } +} + +bool ShutterState(uint32_t device) +{ + device--; + device &= 3; + return (Settings.flag3.shutter_mode && + (Shutter.mask & (1 << (Settings.shutter_startrelay[device]-1))) ); +} + +void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos) +{ + + if ( ( (1 == direction) && ((Shutter.open_max[i] - Shutter.real_position[i]) / 100 <= 2) ) + || ( (-1 == direction) && (Shutter.real_position[i] / Shutter.close_velocity[i] <= 2)) ) { + Shutter.skip_relay_change = 1; + } else { + if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) { + Shutter.pwm_frequency[i] = 0; + analogWriteFreq(Shutter.pwm_frequency[i]); + analogWrite(pin[GPIO_PWM1+i], 0); + RtcSettings.pulse_counter[i] = 0; + Shutter.accelerator[i] = Shutter.max_pwm_frequency / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Ramp up: %d"), Shutter.accelerator[i]); + } + Shutter.target_position[i] = target_pos; + Shutter.start_position[i] = Shutter.real_position[i]; + Shutter.time[i] = 0; + Shutter.skip_relay_change = 0; + Shutter.direction[i] = direction; + rules_flag.shutter_moving = 1; + rules_flag.shutter_moved = 0; + Shutter.start_reported = 0; + + } + +} + +void ShutterWaitForMotorStop(uint32_t i) +{ + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Wait for Motorstop..")); + if ((SHT_OFF_ON__OPEN_CLOSE == Shutter.mode) || (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode)) { + if (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode) { + + while (Shutter.pwm_frequency[i] > 0) { + + Shutter.pwm_frequency[i] = tmax(Shutter.pwm_frequency[i]-((Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i])/(Shutter.motordelay[i]+1)) , 0); + + analogWriteFreq(Shutter.pwm_frequency[i]); + analogWrite(pin[GPIO_PWM1+i], 50); + delay(50); + } + analogWrite(pin[GPIO_PWM1+i], 0); + Shutter.real_position[i] = ShutterCounterBasedPosition(i); + } else { + ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); + delay(MOTOR_STOP_TIME); + } + } else { + delay(MOTOR_STOP_TIME); + } +} + +int32_t ShutterCounterBasedPosition(uint32_t i) +{ + return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_frequency)+Shutter.start_position[i]; +} + +void ShutterRelayChanged(void) +{ + + + + + char stemp1[10]; + + for (uint32_t i = 0; i < shutters_present; i++) { + power_t powerstate_local = (power >> (Settings.shutter_startrelay[i] -1)) & 3; + + uint8 manual_relays_changed = ((Shutter.switched_relay >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ; + + if (manual_relays_changed) { + + ShutterLimitRealAndTargetPositions(i); + if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE || Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) { + ShutterWaitForMotorStop(i); + switch (powerstate_local) { + case 1: + ShutterStartInit(i, 1, Shutter.open_max[i]); + break; + case 3: + ShutterStartInit(i, -1, 0); + break; + default: + + Shutter.target_position[i] = Shutter.real_position[i]; + } + } else { + if (Shutter.direction[i] != 0 && (!powerstate_local || (powerstate_local && Shutter.mode == SHT_PULSE_OPEN__PULSE_CLOSE))) { + Shutter.target_position[i] = Shutter.real_position[i]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed); + } else { + last_source = SRC_SHUTTER; + if (powerstate_local == 2) { + + ShutterWaitForMotorStop(i); + ShutterStartInit(i, -1, 0); + } else { + + ShutterWaitForMotorStop(i); + ShutterStartInit(i, 1, Shutter.open_max[i]); + } + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Target: %ld, powerstatelocal %d"), i+1, Shutter.target_position[i], powerstate_local); + } + } + } +} + +bool ShutterButtonIsSimultaneousHold(uint32_t button_index, uint32_t shutter_index) { + + uint32 min_shutterbutton_hold_timer = -1; + for (uint32_t i = 0; i < MAX_KEYS; i++) { + if ((button_index != i) && (Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index) && (Button.hold_timer[i] < min_shutterbutton_hold_timer)) + min_shutterbutton_hold_timer = Button.hold_timer[i]; + } + return ((-1 != min_shutterbutton_hold_timer) && (min_shutterbutton_hold_timer > (Button.hold_timer[button_index]>>1))); +} + +void ShutterButtonHandler(void) +{ + uint8_t buttonState = SHT_NOT_PRESSED; + uint8_t button = XdrvMailbox.payload; + uint8_t press_index; + uint32_t button_index = XdrvMailbox.index; + uint8_t shutter_index = Settings.shutter_button[button_index] & 0x03; + uint16_t loops_per_second = 1000 / Settings.button_debounce; + + if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { + if (Settings.flag.button_single) { + buttonState = SHT_PRESSED_MULTI; + press_index = 1; + } else { + if ((Shutter.direction[shutter_index]) && (Button.press_counter[button_index]==0)) { + buttonState = SHT_PRESSED_IMMEDIATE; + press_index = 1; + Button.press_counter[button_index] = 99; + } else { + Button.press_counter[button_index] = (Button.window_timer[button_index]) ? Button.press_counter[button_index] +1 : 1; + + Button.window_timer[button_index] = (loops_per_second >> 2) * 3; + } + } + blinks = 201; + } + + if (NOT_PRESSED == button) { + Button.hold_timer[button_index] = 0; + } else { + Button.hold_timer[button_index]++; + if (!Settings.flag.button_single) { + if (Settings.param[P_HOLD_IGNORE] > 0) { + if (Button.hold_timer[button_index] > loops_per_second * Settings.param[P_HOLD_IGNORE] / 10) { + Button.hold_timer[button_index] = 0; + Button.press_counter[button_index] = 0; + } + } + if ((Button.press_counter[button_index]<99) && (Button.hold_timer[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10)) { + + if (ShutterButtonIsSimultaneousHold(button_index, shutter_index)) { + + for (uint32_t i = 0; i < MAX_KEYS; i++) + if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index)) + Button.press_counter[i] = 99; + press_index = 0; + buttonState = SHT_PRESSED_HOLD_SIMULTANEOUS; + } + if (Button.press_counter[button_index]<99) { + press_index = 0; + buttonState = SHT_PRESSED_HOLD; + } + Button.press_counter[button_index] = 0; + } + if ((Button.press_counter[button_index]==0) && (Button.hold_timer[button_index] == loops_per_second * IMMINENT_RESET_FACTOR * Settings.param[P_HOLD_TIME] / 10)) { + press_index = -1; + + if (ShutterButtonIsSimultaneousHold(button_index, shutter_index)) { + + buttonState = SHT_PRESSED_EXT_HOLD_SIMULTANEOUS; + } else { + buttonState = SHT_PRESSED_EXT_HOLD; + } + } + } + } + + if (!Settings.flag.button_single) { + if (Button.window_timer[button_index]) { + Button.window_timer[button_index]--; + } else { + if (!restart_flag && !Button.hold_timer[button_index] && (Button.press_counter[button_index] > 0)) { + if (Button.press_counter[button_index]<99) { + + uint32 min_shutterbutton_press_counter = -1; + for (uint32_t i = 0; i < MAX_KEYS; i++) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Settings.shutter_button[i] %ld, shutter_index %d, Button.press_counter[i] %d, min_shutterbutton_press_counter %d, i %d"), Settings.shutter_button[i], shutter_index, Button.press_counter[i] , min_shutterbutton_press_counter, i); + if ((button_index != i) && (Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index) && (i != button_index) && (Button.press_counter[i] < min_shutterbutton_press_counter)) { + min_shutterbutton_press_counter = Button.press_counter[i]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: min_shutterbutton_press_counter %d"), min_shutterbutton_press_counter); + } + } + if (min_shutterbutton_press_counter == Button.press_counter[button_index]) { + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: simultanous presss deteced")); + press_index = Button.press_counter[button_index]; + for (uint32_t i = 0; i < MAX_KEYS; i++) + if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) != shutter_index)) + Button.press_counter[i] = 99; + buttonState = SHT_PRESSED_MULTI_SIMULTANEOUS; + } + if ((buttonState != SHT_PRESSED_MULTI_SIMULTANEOUS) && (Button.press_counter[button_index]<99)) { + + press_index = Button.press_counter[button_index]; + buttonState = SHT_PRESSED_MULTI; + } + } + Button.press_counter[button_index] = 0; + } + } + } + + if (buttonState != SHT_NOT_PRESSED) { + if ((!Settings.flag.button_restrict) && (((press_index>=5) && (press_index<=7)) || (buttonState == SHT_PRESSED_EXT_HOLD) || (buttonState == SHT_PRESSED_EXT_HOLD_SIMULTANEOUS))){ + + uint8_t shutter_index_num_buttons = 0; + for (uint32_t i = 0; i < MAX_KEYS; i++) { + if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index)) { + shutter_index_num_buttons++; + } + } + if ((buttonState == SHT_PRESSED_MULTI_SIMULTANEOUS) || ((shutter_index_num_buttons==1) && (buttonState == SHT_PRESSED_MULTI))){ + + + char scmnd[20]; + GetTextIndexed(scmnd, sizeof(scmnd), press_index -3, kCommands); + ExecuteCommand(scmnd, SRC_BUTTON); + return; + } else if ((buttonState == SHT_PRESSED_EXT_HOLD_SIMULTANEOUS) || ((shutter_index_num_buttons==1) && (buttonState == SHT_PRESSED_EXT_HOLD))){ + + + char scmnd[20]; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); + ExecuteCommand(scmnd, SRC_BUTTON); + return; + } + } + if (buttonState <= SHT_PRESSED_IMMEDIATE) { + if (Settings.shutter_startrelay[shutter_index] && Settings.shutter_startrelay[shutter_index] <9) { + uint8_t pos_press_index = (buttonState == SHT_PRESSED_HOLD) ? 3 : (press_index-1); + if (pos_press_index>3) pos_press_index=3; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: shutter %d, button %d = %d (single=1, double=2, tripple=3, hold=4)"), shutter_index+1, button_index+1, pos_press_index+1); + XdrvMailbox.index = shutter_index +1; + last_source = SRC_BUTTON; + XdrvMailbox.data_len = 0; + char databuf[1] = ""; + XdrvMailbox.data = databuf; + XdrvMailbox.command = NULL; + if (buttonState == SHT_PRESSED_IMMEDIATE) { + XdrvMailbox.payload = XdrvMailbox.index; + CmndShutterStop(); + } else { + uint8_t position = (Settings.shutter_button[button_index]>>(6*pos_press_index + 2)) & 0x03f; + if (position) { + if (Shutter.direction[shutter_index]) { + XdrvMailbox.payload = XdrvMailbox.index; + CmndShutterStop(); + } else { + XdrvMailbox.payload = position = (position-1)<<1; + + if (102 == position) { + XdrvMailbox.payload = XdrvMailbox.index; + CmndShutterToggle(); + } else { + CmndShutterPosition(); + } + if (Settings.shutter_button[button_index] & ((0x01<<26)< 0) && (XdrvMailbox.index <= shutters_present)) { + uint32_t index = XdrvMailbox.index-1; + if (Shutter.direction[index]) { + CmndShutterStop(); + } else { + CmndShutterOpen(); + } + } +} + +void CmndShutterClose(void) +{ + + if ((1 == XdrvMailbox.index) && (XdrvMailbox.payload != -99)) { + XdrvMailbox.index = XdrvMailbox.payload; + } + XdrvMailbox.payload = 0; + XdrvMailbox.data_len = 0; + last_source = SRC_WEBGUI; + CmndShutterPosition(); +} + +void CmndShutterStopClose(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + uint32_t index = XdrvMailbox.index-1; + if (Shutter.direction[index]) { + CmndShutterStop(); + } else { + CmndShutterClose(); + } + } +} + +void CmndShutterToggle(void) +{ + + if ((1 == XdrvMailbox.index) && (XdrvMailbox.payload != -99)) { + XdrvMailbox.index = XdrvMailbox.payload; + } + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + uint32_t index = XdrvMailbox.index-1; + XdrvMailbox.payload = (50 < ShutterRealToPercentPosition(Shutter.real_position[index], index)) ? 0 : 100; + XdrvMailbox.data_len = 0; + last_source = SRC_WEBGUI; + CmndShutterPosition(); + } +} + +void CmndShutterStopToggle(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + uint32_t index = XdrvMailbox.index-1; + if (Shutter.direction[index]) { + CmndShutterStop(); + } else { + CmndShutterToggle(); + } + } +} + +void CmndShutterStop(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (!(Settings.shutter_options[XdrvMailbox.index-1] & 2)) { + if ((1 == XdrvMailbox.index) && (XdrvMailbox.payload != -99)) { + XdrvMailbox.index = XdrvMailbox.payload; + } + uint32_t i = XdrvMailbox.index -1; + if (Shutter.direction[i] != 0) { + + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Stop moving %d: dir: %d"), XdrvMailbox.index, Shutter.direction[i]); + + int32_t temp_realpos = Shutter.start_position[i] + ( (Shutter.time[i]+10) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); + XdrvMailbox.payload = ShutterRealToPercentPosition(temp_realpos, i); + + last_source = SRC_WEBGUI; + CmndShutterPosition(); + } else { + if (XdrvMailbox.command) + ResponseCmndDone(); + } + } else { + if (XdrvMailbox.command) + ResponseCmndIdxChar("Locked"); + } + } +} + +void CmndShutterPosition(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (!(Settings.shutter_options[XdrvMailbox.index-1] & 2)) { + uint32_t index = XdrvMailbox.index-1; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Pos. in: payload %s (%d), payload %d, idx %d, src %d"), XdrvMailbox.data , XdrvMailbox.data_len, XdrvMailbox.payload , XdrvMailbox.index, last_source ); + + + + if ((XdrvMailbox.data_len > 1) && (XdrvMailbox.payload <= 0)) { + + if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_UP) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_OPEN) || ((Shutter.direction[index]==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPOPEN))) { + CmndShutterOpen(); + return; + } + if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_DOWN) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_CLOSE) || ((Shutter.direction[index]==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPCLOSE))) { + CmndShutterClose(); + return; + } + if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_TOGGLE)) { + CmndShutterToggle(); + return; + } + if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOP) || ((Shutter.direction[index]) && (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPOPEN) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPCLOSE)))) { + XdrvMailbox.payload = -99; + CmndShutterStop(); + return; + } + } + + int8_t target_pos_percent = (XdrvMailbox.payload < 0) ? (XdrvMailbox.payload == -99 ? ShutterRealToPercentPosition(Shutter.real_position[index], index) : 0) : ((XdrvMailbox.payload > 100) ? 100 : XdrvMailbox.payload); + + target_pos_percent = ((Settings.shutter_options[index] & 1) && (SRC_WEBGUI != last_source)) ? 100 - target_pos_percent : target_pos_percent; + if (XdrvMailbox.payload != -99) { + + Shutter.target_position[index] = ShutterPercentToRealPosition(target_pos_percent, index); + + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: lastsource %d:, real %d, target %d, payload %d"), last_source, Shutter.real_position[index] ,Shutter.target_position[index],target_pos_percent); + } + if ( (target_pos_percent >= 0) && (target_pos_percent <= 100) && abs(Shutter.target_position[index] - Shutter.real_position[index] ) / Shutter.close_velocity[index] > 2) { + if (Settings.shutter_options[index] & 4) { + if (0 == target_pos_percent) Shutter.target_position[index] -= 1 * 2000; + if (100 == target_pos_percent) Shutter.target_position[index] += 1 * 2000; + } + int8_t new_shutterdirection = Shutter.real_position[index] < Shutter.target_position[index] ? 1 : -1; + if (Shutter.direction[index] == -new_shutterdirection) { + + if (SHT_PULSE_OPEN__PULSE_CLOSE == Shutter.mode) { + + ExecuteCommandPower(Settings.shutter_startrelay[index] + ((new_shutterdirection == 1) ? 0 : 1), 1, SRC_SHUTTER); + delay(100); + } else { + if (SHT_OFF_OPEN__OFF_CLOSE == Shutter.mode) { + ExecuteCommandPower(Settings.shutter_startrelay[index] + ((new_shutterdirection == 1) ? 1 : 0), 0, SRC_SHUTTER); + ShutterWaitForMotorStop(index); + } + } + } + if (Shutter.direction[index] != new_shutterdirection) { + if ((SHT_OFF_ON__OPEN_CLOSE == Shutter.mode) || (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode)) { + + ShutterWaitForMotorStop(index); + ExecuteCommandPower(Settings.shutter_startrelay[index], 0, SRC_SHUTTER); + ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]); + if (Shutter.skip_relay_change == 0) { + + ExecuteCommandPower(Settings.shutter_startrelay[index] +1, new_shutterdirection == 1 ? 0 : 1, SRC_SHUTTER); + + ExecuteCommandPower(Settings.shutter_startrelay[index], 1, SRC_SHUTTER); + } + } else { + + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Start in dir %d"), Shutter.direction[index]); + ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]); + if (Shutter.skip_relay_change == 0) { + ExecuteCommandPower(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER); + } + + } + Shutter.switched_relay = 0; + } + } else { + target_pos_percent = ShutterRealToPercentPosition(Shutter.real_position[index], index); + ShutterReportPosition(true); + } + XdrvMailbox.index = index +1; + if (XdrvMailbox.command) + ResponseCmndIdxNumber((Settings.shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent); + } else { + ShutterReportPosition(true); + if (XdrvMailbox.command) + ResponseCmndIdxChar("Locked"); + } + } +} + +void CmndShutterStopPosition(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + uint32_t index = XdrvMailbox.index-1; + if (Shutter.direction[index]) { + XdrvMailbox.payload = -99; + CmndShutterStop(); + } else { + CmndShutterPosition(); + } + } +} +void CmndShutterOpenTime(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.data_len > 0) { + Settings.shutter_opentime[XdrvMailbox.index -1] = (uint16_t)(10 * CharToFloat(XdrvMailbox.data)); + ShutterInit(); + } + char time_chr[10]; + dtostrfd((float)(Settings.shutter_opentime[XdrvMailbox.index -1]) / 10, 1, time_chr); + ResponseCmndIdxChar(time_chr); + } +} + +void CmndShutterCloseTime(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.data_len > 0) { + Settings.shutter_closetime[XdrvMailbox.index -1] = (uint16_t)(10 * CharToFloat(XdrvMailbox.data)); + ShutterInit(); + } + char time_chr[10]; + dtostrfd((float)(Settings.shutter_closetime[XdrvMailbox.index -1]) / 10, 1, time_chr); + ResponseCmndIdxChar(time_chr); + } +} + +void CmndShutterMotorDelay(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.data_len > 0) { + Settings.shutter_motordelay[XdrvMailbox.index -1] = (uint16_t)(steps_per_second * CharToFloat(XdrvMailbox.data)); + ShutterInit(); + } + char time_chr[10]; + dtostrfd((float)(Settings.shutter_motordelay[XdrvMailbox.index -1]) / steps_per_second, 2, time_chr); + ResponseCmndIdxChar(time_chr); + } +} + +void CmndShutterRelay(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SHUTTERS)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 64)) { + Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload; + if (XdrvMailbox.payload > 0) { + Shutter.mask |= 3 << (XdrvMailbox.payload - 1); + } else { + Shutter.mask ^= 3 << (Settings.shutter_startrelay[XdrvMailbox.index -1] - 1); + } + Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload; + ShutterInit(); + + } + ResponseCmndIdxNumber(Settings.shutter_startrelay[XdrvMailbox.index -1]); + } +} + +void CmndShutterButton(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SHUTTERS)) { + uint32_t setting = 0; +# 1010 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_27_shutter.ino" + if (XdrvMailbox.data_len > 0) { + uint32_t i = 0; + uint32_t button_index = 0; + bool done = false; + bool isShortCommand = false; + char *str_ptr; + + char data_copy[strlen(XdrvMailbox.data) +1]; + strncpy(data_copy, XdrvMailbox.data, sizeof(data_copy)); + + for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < (1+4+4+1); str = strtok_r(nullptr, " ", &str_ptr), i++) { + int field; + switch (str[0]) { + case '-': + field = -1; + break; + case 't': + field = 102; + break; + default: + field = atoi(str); + break; + } + switch (i) { + case 0: + if ((field >= -1) && (field<=4)) { + button_index = (field<=0)?(-1):field; + done = (button_index==-1); + } else + done = true; + break; + case 1: + if (!strcmp_P(str, PSTR("up"))) { + setting |= (((100>>1)+1)<<2) | (((50>>1)+1)<<8) | (((75>>1)+1)<<14) | (((100>>1)+1)<<20); + isShortCommand = true; + break; + } else if (!strcmp_P(str, PSTR("down"))) { + setting |= (((0>>1)+1)<<2) | (((50>>1)+1)<<8) | (((25>>1)+1)<<14) | (((0>>1)+1)<<20); + isShortCommand = true; + break; + } else if (!strcmp_P(str, PSTR("updown"))) { + setting |= (((100>>1)+1)<<2) | (((0>>1)+1)<<8) | (((50>>1)+1)<<14); + isShortCommand = true; + break; + } else if (!strcmp_P(str, PSTR("toggle"))) { + setting |= (((102>>1)+1)<<2) | (((50>>1)+1)<<8); + isShortCommand = true; + break; + } + case 2: + if (isShortCommand) { + if ((field==1) && (setting & (0x3F<<(2+6*3)))) + + setting |= (0x3<<29); + done = true; + break; + } + case 3: + case 4: + if ((field >= -1) && (field<=102)) + setting |= (((field>>1)+1)<<(i*6 + (2-6))); + break; + case 5: + case 6: + case 7: + case 8: + case 9: + if (field==1) + setting |= (1<<(i + (26-5))); + break; + } + if (done) break; + } + + if (button_index) { + if (button_index==-1) { + + for (uint32_t i=0 ; i < MAX_KEYS ; i++) + if ((Settings.shutter_button[i]&0x3) == (XdrvMailbox.index-1)) + Settings.shutter_button[i] = 0; + } else { + if (setting) { + + setting |= (1<<31); + setting |= (XdrvMailbox.index-1) & 0x3; + } + Settings.shutter_button[button_index-1] = setting; + } + } + } + char setting_chr[30*MAX_KEYS] = "-", *setting_chr_ptr = setting_chr; + for (uint32_t i=0 ; i < MAX_KEYS ; i++) { + setting = Settings.shutter_button[i]; + if ((setting&(1<<31)) && ((setting&0x3) == (XdrvMailbox.index-1))) { + if (*setting_chr_ptr == 0) + setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR("|")); + setting_chr_ptr += snprintf_P(setting_chr_ptr, 2, PSTR("%d"), i+1); + + for (uint32_t j=0 ; j < 4 ; j++) { + int8_t pos = (((setting>> (2+6*j))&(0x3f))-1)<<1; + if (0 <= pos) + if (102 == pos) { + setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" t")); + } else { + setting_chr_ptr += snprintf_P(setting_chr_ptr, 5, PSTR(" %d"), pos); + } + else + setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" -")); + } + for (uint32_t j=0 ; j < 5 ; j++) { + bool mqtt = ((setting>>(26+j))&(0x01)!=0); + if (mqtt) + setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" 1")); + else + setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" -")); + } + } + } + ResponseCmndIdxChar(setting_chr); + } +} + +void CmndShutterSetHalfway(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + Settings.shutter_set50percent[XdrvMailbox.index -1] = (Settings.shutter_options[XdrvMailbox.index -1] & 1) ? 100 - XdrvMailbox.payload : XdrvMailbox.payload; + ShutterInit(); + } + ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 1) ? 100 - Settings.shutter_set50percent[XdrvMailbox.index -1] : Settings.shutter_set50percent[XdrvMailbox.index -1]); + } +} + +void CmndShutterFrequency(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 20000)) { + Shutter.max_pwm_frequency = XdrvMailbox.payload; + if (shutters_present < 4) { + Settings.shuttercoeff[4][3] = Shutter.max_pwm_frequency; + } + ShutterInit(); + ResponseCmndNumber(XdrvMailbox.payload); + } else { + ResponseCmndNumber(Shutter.max_pwm_frequency); + } +} + +void CmndShutterSetClose(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + Shutter.real_position[XdrvMailbox.index -1] = 0; + ShutterStartInit(XdrvMailbox.index -1, 0, 0); + Settings.shutter_position[XdrvMailbox.index -1] = 0; + ResponseCmndIdxChar(D_CONFIGURATION_RESET); + } +} + +void CmndShutterInvert(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.payload == 0) { + Settings.shutter_options[XdrvMailbox.index -1] &= ~(1); + } else if (XdrvMailbox.payload == 1) { + Settings.shutter_options[XdrvMailbox.index -1] |= (1); + } + ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 1) ? 1 : 0); + } +} + +void CmndShutterCalibration(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.data_len > 0) { + uint32_t i = 0; + char *str_ptr; + + char data_copy[strlen(XdrvMailbox.data) +1]; + strncpy(data_copy, XdrvMailbox.data, sizeof(data_copy)); + + for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < 5; str = strtok_r(nullptr, " ", &str_ptr), i++) { + int field = atoi(str); + + + if ((field <= 0) || (field > 30000) || ( (i>0) && (field <= messwerte[i-1]) ) ) { + break; + } + messwerte[i] = field; + } + for (i = 0; i < 5; i++) { + Settings.shuttercoeff[i][XdrvMailbox.index -1] = SHT_DIV_ROUND((uint32_t)messwerte[i] * 1000, messwerte[4]); + AddLog_P2(LOG_LEVEL_INFO, PSTR("Settings.shuttercoeff: %d, i: %d, value: %d, messwert %d"), i,XdrvMailbox.index -1,Settings.shuttercoeff[i][XdrvMailbox.index -1], messwerte[i]); + } + ShutterInit(); + ResponseCmndIdxChar(XdrvMailbox.data); + } else { + char setting_chr[30] = "0"; + snprintf_P(setting_chr, sizeof(setting_chr), PSTR("%d %d %d %d %d"), Settings.shuttercoeff[0][XdrvMailbox.index -1], Settings.shuttercoeff[1][XdrvMailbox.index -1], Settings.shuttercoeff[2][XdrvMailbox.index -1], Settings.shuttercoeff[3][XdrvMailbox.index -1], Settings.shuttercoeff[4][XdrvMailbox.index -1]); + ResponseCmndIdxChar(setting_chr); + } + } +} + +void CmndShutterLock(void) { + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.payload == 0) { + Settings.shutter_options[XdrvMailbox.index -1] &= ~(2); + } else if (XdrvMailbox.payload == 1) { + Settings.shutter_options[XdrvMailbox.index -1] |= (2); + } + ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 2) ? 1 : 0); + } +} + +void CmndShutterEnableEndStopTime(void) { + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.payload == 0) { + Settings.shutter_options[XdrvMailbox.index -1] &= ~(4); + } else if (XdrvMailbox.payload == 1) { + Settings.shutter_options[XdrvMailbox.index -1] |= (4); + } + ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 4) ? 1 : 0); + } +} + +void CmndShutterInvertWebButtons(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.payload == 0) { + Settings.shutter_options[XdrvMailbox.index -1] &= ~(8); + } else if (XdrvMailbox.payload == 1) { + Settings.shutter_options[XdrvMailbox.index -1] |= (8); + } + ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 8) ? 1 : 0); + } +} + + + + + +bool Xdrv27(uint8_t function) +{ + bool result = false; + + if (Settings.flag3.shutter_mode) { + switch (function) { + case FUNC_PRE_INIT: + ShutterInit(); + break; + case FUNC_EVERY_50_MSECOND: + ShutterUpdatePosition(); + break; + case FUNC_EVERY_SECOND: + + ShutterReportPosition(false); + break; + + case FUNC_COMMAND: + result = DecodeCommand(kShutterCommands, ShutterCommand); + break; + case FUNC_JSON_APPEND: + for (uint8_t i = 0; i < shutters_present; i++) { + uint8_t position = (Settings.shutter_options[i] & 1) ? 100 - Settings.shutter_position[i] : Settings.shutter_position[i]; + uint8_t target = (Settings.shutter_options[i] & 1) ? 100 - ShutterRealToPercentPosition(Shutter.target_position[i], i) : ShutterRealToPercentPosition(Shutter.target_position[i], i); + + ResponseAppend_P(","); + ResponseAppend_P(JSON_SHUTTER_POS, i+1, position, Shutter.direction[i],target); +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (0 == i)) { + DomoticzSensor(DZ_SHUTTER, position); + } +#endif + } + break; + case FUNC_SET_POWER: + char stemp1[10]; + + Shutter.switched_relay = XdrvMailbox.index ^ Shutter.old_power; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Switched relay: %d by %s"), Shutter.switched_relay,GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource)); + ShutterRelayChanged(); + Shutter.old_power = XdrvMailbox.index; + break; + case FUNC_SET_DEVICE_POWER: + if (Shutter.skip_relay_change ) { + uint8_t i; + for (i = 0; i < devices_present; i++) { + if (Shutter.switched_relay &1) { + break; + } + Shutter.switched_relay >>= 1; + } + + result = true; + Shutter.skip_relay_change = 0; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Skipping switch off relay %d"),i); + ExecuteCommandPower(i+1, 0, SRC_SHUTTER); + } + break; + case FUNC_BUTTON_PRESSED: + if (Settings.shutter_button[XdrvMailbox.index] & (1<<31)) { + ShutterButtonHandler(); + result = true; + } + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_28_pcf8574.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_28_pcf8574.ino" +#ifdef USE_I2C +#ifdef USE_PCF8574 + + + + + + + +#define XDRV_28 28 +#define XI2C_02 2 + +#define PCF8574_ADDR1 0x20 +#define PCF8574_ADDR2 0x38 + +struct PCF8574 { + int error; + uint8_t pin[64]; + uint8_t address[MAX_PCF8574]; + uint8_t pin_mask[MAX_PCF8574] = { 0 }; + uint8_t max_connected_ports = 0; + uint8_t max_devices = 0; + char stype[9]; + bool type = false; +} Pcf8574; + +void Pcf8574SwitchRelay(void) +{ + for (uint32_t i = 0; i < devices_present; i++) { + uint8_t relay_state = bitRead(XdrvMailbox.index, i); + + + + if (Pcf8574.max_devices > 0 && Pcf8574.pin[i] < 99) { + uint8_t board = Pcf8574.pin[i]>>3; + uint8_t oldpinmask = Pcf8574.pin_mask[board]; + uint8_t _val = bitRead(rel_inverted, i) ? !relay_state : relay_state; + + + + if (_val) { + Pcf8574.pin_mask[board] |= _val << (Pcf8574.pin[i]&0x7); + } else { + Pcf8574.pin_mask[board] &= ~(1 << (Pcf8574.pin[i]&0x7)); + } + if (oldpinmask != Pcf8574.pin_mask[board]) { + Wire.beginTransmission(Pcf8574.address[board]); + Wire.write(Pcf8574.pin_mask[board]); + Pcf8574.error = Wire.endTransmission(); + } + + } + } +} + +void Pcf8574Init(void) +{ + uint8_t pcf8574_address = PCF8574_ADDR1; + while ((Pcf8574.max_devices < MAX_PCF8574) && (pcf8574_address < PCF8574_ADDR2 +8)) { + +#ifdef USE_MCP230xx_ADDR + if (USE_MCP230xx_ADDR == pcf8574_address) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("PCF: Address 0x%02x reserved for MCP320xx skipped"), pcf8574_address); + pcf8574_address++; + if ((PCF8574_ADDR1 +7) == pcf8574_address) { + pcf8574_address = PCF8574_ADDR2 +1; + } + } +#endif + + + + if (I2cSetDevice(pcf8574_address)) { + Pcf8574.type = true; + + Pcf8574.address[Pcf8574.max_devices] = pcf8574_address; + Pcf8574.max_devices++; + + strcpy(Pcf8574.stype, "PCF8574"); + if (pcf8574_address >= PCF8574_ADDR2) { + strcpy(Pcf8574.stype, "PCF8574A"); + } + I2cSetActiveFound(pcf8574_address, Pcf8574.stype); + } + + pcf8574_address++; + if ((PCF8574_ADDR1 +7) == pcf8574_address) { + pcf8574_address = PCF8574_ADDR2 +1; + } + } + if (Pcf8574.type) { + for (uint32_t i = 0; i < sizeof(Pcf8574.pin); i++) { + Pcf8574.pin[i] = 99; + } + devices_present = devices_present - Pcf8574.max_connected_ports; + Pcf8574.max_connected_ports = 0; + for (uint32_t idx = 0; idx < Pcf8574.max_devices; idx++) { + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PCF: Device %d config 0x%02x"), idx +1, Settings.pcf8574_config[idx]); + + for (uint32_t i = 0; i < 8; i++) { + uint8_t _result = Settings.pcf8574_config[idx] >> i &1; + + if (_result > 0) { + Pcf8574.pin[devices_present] = i + 8 * idx; + bitWrite(rel_inverted, devices_present, Settings.flag3.pcf8574_ports_inverted); + devices_present++; + Pcf8574.max_connected_ports++; + } + } + } + AddLog_P2(LOG_LEVEL_INFO, PSTR("PCF: Total devices %d, PCF8574 output ports %d"), Pcf8574.max_devices, Pcf8574.max_connected_ports); + } +} + + + + + +#ifdef USE_WEBSERVER + +#define WEB_HANDLE_PCF8574 "pcf" + +const char HTTP_BTN_MENU_PCF8574[] PROGMEM = + "

"; + +const char HTTP_FORM_I2C_PCF8574_1[] PROGMEM = + "
 " D_PCF8574_PARAMETERS " " + "
" + "


"; + +const char HTTP_FORM_I2C_PCF8574_2[] PROGMEM = + "" D_DEVICE " %d " D_PORT " %d"; + +void HandlePcf8574(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_PCF8574)); + + if (Webserver->hasArg("save")) { + Pcf8574SaveSettings(); + WebRestart(1); + return; + } + + WSContentStart_P(D_CONFIGURE_PCF8574); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_I2C_PCF8574_1, (Settings.flag3.pcf8574_ports_inverted) ? " checked" : ""); + WSContentSend_P(HTTP_TABLE100); + for (uint32_t idx = 0; idx < Pcf8574.max_devices; idx++) { + for (uint32_t idx2 = 0; idx2 < 8; idx2++) { + uint8_t helper = 1 << idx2; + WSContentSend_P(HTTP_FORM_I2C_PCF8574_2, + idx +1, idx2, + idx2 + 8*idx, + idx2 + 8*idx, + ((helper & Settings.pcf8574_config[idx]) >> idx2 == 0) ? " selected " : " ", + ((helper & Settings.pcf8574_config[idx]) >> idx2 == 1) ? " selected " : " " + ); + } + } + WSContentSend_P(PSTR("")); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void Pcf8574SaveSettings(void) +{ + char stemp[7]; + char tmp[100]; + + + + Settings.flag3.pcf8574_ports_inverted = Webserver->hasArg("b1"); + for (byte idx = 0; idx < Pcf8574.max_devices; idx++) { + byte count=0; + byte n = Settings.pcf8574_config[idx]; + while(n!=0) { + n = n&(n-1); + count++; + } + if (count <= devices_present) { + devices_present = devices_present - count; + } + for (byte i = 0; i < 8; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("i2cs%d"), i+8*idx); + WebGetArg(stemp, tmp, sizeof(tmp)); + byte _value = (!strlen(tmp)) ? 0 : atoi(tmp); + if (_value) { + Settings.pcf8574_config[idx] = Settings.pcf8574_config[idx] | 1 << i; + devices_present++; + Pcf8574.max_connected_ports++; + } else { + Settings.pcf8574_config[idx] = Settings.pcf8574_config[idx] & ~(1 << i ); + } + } + + + + } +} +#endif + + + + + +bool Xdrv28(uint8_t function) +{ + if (!I2cEnabled(XI2C_02)) { return false; } + + bool result = false; + + if (FUNC_PRE_INIT == function) { + Pcf8574Init(); + } + else if (Pcf8574.type) { + switch (function) { + case FUNC_SET_POWER: + Pcf8574SwitchRelay(); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_PCF8574); + break; + case FUNC_WEB_ADD_HANDLER: + Webserver->on("/" WEB_HANDLE_PCF8574, HandlePcf8574); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_29_deepsleep.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_29_deepsleep.ino" +#ifdef USE_DEEPSLEEP +# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_29_deepsleep.ino" +#define XDRV_29 29 + +#define D_PRFX_DEEPSLEEP "DeepSleep" +#define D_CMND_DEEPSLEEP_TIME "Time" + +const uint32_t DEEPSLEEP_MAX = 10 * 366 * 24 * 60 * 60; +const uint32_t DEEPSLEEP_MAX_CYCLE = 60 * 60; +const uint32_t DEEPSLEEP_MIN_TIME = 5; +const uint32_t DEEPSLEEP_START_COUNTDOWN = 4; + +const char kDeepsleepCommands[] PROGMEM = D_PRFX_DEEPSLEEP "|" + D_CMND_DEEPSLEEP_TIME ; + +void (* const DeepsleepCommand[])(void) PROGMEM = { + &CmndDeepsleepTime }; + +uint32_t deepsleep_sleeptime = 0; +uint8_t deepsleep_flag = 0; + +bool DeepSleepEnabled(void) +{ + if ((Settings.deepsleep < 10) || (Settings.deepsleep > DEEPSLEEP_MAX)) { + Settings.deepsleep = 0; + return false; + } + + if (pin[GPIO_DEEPSLEEP] < 99) { + pinMode(pin[GPIO_DEEPSLEEP], INPUT_PULLUP); + return (digitalRead(pin[GPIO_DEEPSLEEP])); + } + + return true; +} + +void DeepSleepReInit(void) +{ + if ((ResetReason() == REASON_DEEP_SLEEP_AWAKE) && DeepSleepEnabled()) { + if ((RtcSettings.ultradeepsleep > DEEPSLEEP_MAX_CYCLE) && (RtcSettings.ultradeepsleep < 1700000000)) { + + RtcSettings.ultradeepsleep = RtcSettings.ultradeepsleep - DEEPSLEEP_MAX_CYCLE; + AddLog_P2(LOG_LEVEL_ERROR, PSTR("DSL: Remain DeepSleep %d"), RtcSettings.ultradeepsleep); + RtcSettingsSave(); + RtcRebootReset(); + ESP.deepSleep(100 * RtcSettings.deepsleep_slip * (DEEPSLEEP_MAX_CYCLE < RtcSettings.ultradeepsleep ? DEEPSLEEP_MAX_CYCLE : RtcSettings.ultradeepsleep), WAKE_RF_DEFAULT); + yield(); + + } + } + + RtcSettings.ultradeepsleep = 0; +} + +void DeepSleepPrepare(void) +{ + + + + + if ((RtcSettings.nextwakeup == 0) || + (RtcSettings.deepsleep_slip < 9000) || + (RtcSettings.deepsleep_slip > 11000) || + (RtcSettings.nextwakeup > (UtcTime() + Settings.deepsleep))) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("DSL: Reset wrong settings wakeup: %ld, slip %ld"), RtcSettings.nextwakeup, RtcSettings.deepsleep_slip ); + RtcSettings.nextwakeup = 0; + RtcSettings.deepsleep_slip = 10000; + } + + + + int16_t timeslip = (int16_t)(RtcSettings.nextwakeup + millis() / 1000 - UtcTime()) * 10; + + + + timeslip = (timeslip < -(int32_t)Settings.deepsleep) ? 0 : (timeslip > (int32_t)Settings.deepsleep) ? 0 : 1; + if (timeslip) { + RtcSettings.deepsleep_slip = (Settings.deepsleep + RtcSettings.nextwakeup - UtcTime()) * RtcSettings.deepsleep_slip / tmax((Settings.deepsleep - (millis() / 1000)),5); + + RtcSettings.deepsleep_slip = tmin(tmax(RtcSettings.deepsleep_slip, 9000), 11000); + RtcSettings.nextwakeup += Settings.deepsleep; + } + + + + if (RtcSettings.nextwakeup <= (UtcTime() - DEEPSLEEP_MIN_TIME)) { + + RtcSettings.nextwakeup += (((UtcTime() + DEEPSLEEP_MIN_TIME - RtcSettings.nextwakeup) / Settings.deepsleep) + 1) * Settings.deepsleep; + } + + String dt = GetDT(RtcSettings.nextwakeup + LocalTime() - UtcTime()); + + + deepsleep_sleeptime = tmin((uint32_t)DEEPSLEEP_MAX_CYCLE ,RtcSettings.nextwakeup - UtcTime()); + + + Response_P(PSTR("{\"" D_PRFX_DEEPSLEEP "\":{\"" D_JSON_TIME "\":\"%s\",\"Epoch\":%d}}"), (char*)dt.c_str(), RtcSettings.nextwakeup); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_STATUS)); + + + +} + +void DeepSleepStart(void) +{ + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION "Sleeping")); + + WifiShutdown(); + RtcSettings.ultradeepsleep = RtcSettings.nextwakeup - UtcTime(); + RtcSettingsSave(); + + ESP.deepSleep(100 * RtcSettings.deepsleep_slip * deepsleep_sleeptime); + yield(); +} + +void DeepSleepEverySecond(void) +{ + if (!deepsleep_flag) { return; } + + if (DeepSleepEnabled()) { + if (DEEPSLEEP_START_COUNTDOWN == deepsleep_flag) { + SettingsSaveAll(); + DeepSleepPrepare(); + } + deepsleep_flag--; + if (deepsleep_flag <= 0) { + DeepSleepStart(); + } + } else { + deepsleep_flag = 0; + } +} + + + + + +void CmndDeepsleepTime(void) +{ + if ((0 == XdrvMailbox.payload) || + ((XdrvMailbox.payload > 10) && (XdrvMailbox.payload < DEEPSLEEP_MAX))) { + Settings.deepsleep = XdrvMailbox.payload; + RtcSettings.nextwakeup = 0; + deepsleep_flag = (0 == XdrvMailbox.payload) ? 0 : DEEPSLEEP_START_COUNTDOWN; + if (deepsleep_flag) { + if (!Settings.tele_period) { + Settings.tele_period = TELE_PERIOD; + } + } + } + Response_P(S_JSON_COMMAND_NVALUE, XdrvMailbox.command, Settings.deepsleep); +} + + + + + +bool Xdrv29(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_SECOND: + DeepSleepEverySecond(); + break; + case FUNC_AFTER_TELEPERIOD: + if (DeepSleepEnabled() && !deepsleep_flag && (Settings.tele_period == 10 || Settings.tele_period == 300 || UpTime() > Settings.tele_period)) { + deepsleep_flag = DEEPSLEEP_START_COUNTDOWN; + } + break; + case FUNC_COMMAND: + result = DecodeCommand(kDeepsleepCommands, DeepsleepCommand); + break; + case FUNC_PRE_INIT: + DeepSleepReInit(); + break; + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" +#ifdef USE_LIGHT +#ifdef USE_EXS_DIMMER + + + + + + + +#define XDRV_30 30 + +#define EXS_GATE_1_ON 0x20 +#define EXS_GATE_1_OFF 0x21 +#define EXS_DIMM_1_ON 0x22 +#define EXS_DIMM_1_OFF 0x23 +#define EXS_DIMM_1_TBL 0x24 +#define EXS_DIMM_1_VAL 0x25 +#define EXS_GATE_2_ON 0x30 +#define EXS_GATE_2_OFF 0x31 +#define EXS_DIMM_2_ON 0x32 +#define EXS_DIMM_2_OFF 0x33 +#define EXS_DIMM_2_TBL 0x34 +#define EXS_DIMM_2_VAL 0x35 +#define EXS_GATES_ON 0x40 +#define EXS_GATES_OFF 0x41 +#define EXS_DIMMS_ON 0x50 +#define EXS_DIMMS_OFF 0x51 +#define EXS_CH_LOCK 0x60 +#define EXS_GET_VALUES 0xFA +#define EXS_WRITE_EE 0xFC +#define EXS_READ_EE 0xFD +#define EXS_GET_VERSION 0xFE +#define EXS_RESET 0xFF + +#define EXS_BUFFER_SIZE 256 +#define EXS_ACK_TIMEOUT 200 + +#include + +TasmotaSerial *ExsSerial = nullptr; + +typedef struct +{ + uint8_t on = 0; + uint8_t bright_tbl = 0; + uint8_t dimm = 0; + uint8_t impuls_start = 0; + uint32_t impuls_len = 0; +} CHANNEL; + +typedef struct +{ + uint8_t version_major = 0; + uint8_t version_minor = 0; + CHANNEL channel[2]; + uint8_t gate_lock = 0; +} DIMMER; + +struct EXS +{ + uint8_t *buffer = nullptr; + int byte_counter = 0; + int cmd_status = 0; + uint8_t power = 0; + uint8_t dimm[2] = {0, 0}; + DIMMER dimmer; +} Exs; + + + + + +uint8_t crc8(const uint8_t *p, uint8_t len) +{ + const uint8_t table[] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, + 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D}; + + const uint8_t table_rev[] = { + 0x00, 0x70, 0xE0, 0x90, 0xC1, 0xB1, 0x21, 0x51, + 0x83, 0xF3, 0x63, 0x13, 0x42, 0x32, 0xA2, 0xD2}; + + uint8_t offset; + uint8_t temp, crc8_temp; + uint8_t crc8 = 0; + + for (int i = 0; i < len; i++) + { + temp = *(p + i); + offset = temp ^ crc8; + offset >>= 4; + crc8_temp = crc8 & 0x0f; + crc8 = crc8_temp ^ table_rev[offset]; + offset = crc8 ^ temp; + offset &= 0x0f; + crc8_temp = crc8 & 0xf0; + crc8 = crc8_temp ^ table[offset]; + } + return crc8 ^ 0x55; +} + +void ExsSerialSend(const uint8_t data[] = nullptr, uint16_t len = 0) +{ + int retries = 3; + char rc; + +#ifdef EXS_DEBUG + snprintf_P(log_data, sizeof(log_data), PSTR("EXS: Tx Packet: \"")); + for (uint32_t i = 0; i < len; i++) + { + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, data[i]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s\""), log_data); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + + while (retries) + { + retries--; + + ExsSerial->write(data, len); + ExsSerial->flush(); + + + uint32_t snd_time = millis(); + while ((TimePassedSince(snd_time) < EXS_ACK_TIMEOUT) && + (!ExsSerial->available())) + ; + + if (!ExsSerial->available()) + { + +#ifdef EXS_DEBUG + AddLog_P(LOG_LEVEL_DEBUG, PSTR("ESX: serial send timeout")); +#endif + continue; + } + + rc = ExsSerial->read(); + if (rc == 0xFF) + break; + } +} + +void ExsSendCmd(uint8_t cmd, uint8_t value) +{ + uint8_t buffer[8]; + uint16_t len; + + buffer[0] = 0x7b; + buffer[3] = cmd; + + switch (cmd) + { + case EXS_GATE_1_ON: + case EXS_GATE_1_OFF: + case EXS_DIMM_1_ON: + case EXS_DIMM_1_OFF: + case EXS_GATE_2_ON: + case EXS_GATE_2_OFF: + case EXS_DIMM_2_ON: + case EXS_DIMM_2_OFF: + case EXS_GATES_ON: + case EXS_GATES_OFF: + case EXS_DIMMS_ON: + case EXS_DIMMS_OFF: + case EXS_GET_VALUES: + case EXS_GET_VERSION: + case EXS_RESET: + buffer[2] = 1; + len = 4; + break; + + case EXS_CH_LOCK: + case EXS_DIMM_1_TBL: + case EXS_DIMM_1_VAL: + case EXS_DIMM_2_TBL: + case EXS_DIMM_2_VAL: + buffer[2] = 2; + buffer[4] = value; + len = 5; + break; + } + buffer[1] = crc8(&buffer[3], buffer[2]); + + ExsSerialSend(buffer, len); +} + +uint8_t ExsSetPower(uint8_t device, uint8_t power) +{ + Exs.dimmer.channel[device].dimm = power; + ExsSendCmd(EXS_DIMM_1_ON + 0x10 * device + power ^ 1, 0); +} + +uint8_t ExsSetBri(uint8_t device, uint8_t bri) +{ + Exs.dimmer.channel[device].bright_tbl = bri; + ExsSendCmd(EXS_DIMM_1_TBL + 0x10 * device, bri); +} + +uint8_t ExsSyncState(uint8_t device) +{ +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Channel %d Power Want %d, Is %d"), + device, bitRead(Exs.power, device), Exs.dimmer.channel[device].dimm); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Set Channel %d Brightness Want %d, Is %d"), + device, Exs.dimm[device], Exs.dimmer.channel[device].bright_tbl); +#endif + + if (bitRead(Exs.power, device) && + Exs.dimm[device] != Exs.dimmer.channel[device].bright_tbl) { + ExsSetBri(device, Exs.dimm[device]); + } + + if (!Exs.dimm[device]) { + Exs.dimmer.channel[device].dimm = 0; + } else if (Exs.dimmer.channel[device].dimm != bitRead(Exs.power, device)) { + ExsSetPower(device, bitRead(Exs.power, device)); + } +} + +bool ExsSyncState() +{ +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Serial %p, Cmd %d"), ExsSerial, Exs.cmd_status); +#endif + + if (!ExsSerial || Exs.cmd_status != 0) + return false; + + ExsSyncState(0); + ExsSyncState(1); +} + +void ExsDebugState() +{ +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: MCU v%d.%d, c0: On:%d,Dim:%d,Tbl:%d(%d%%), c1: On:%d,Dim:%d,Tbl:%d(%d%%), ChLock: %d"), + Exs.dimmer.version_major, Exs.dimmer.version_minor, + Exs.dimmer.channel[0].on, Exs.dimmer.channel[0].dimm, + Exs.dimmer.channel[0].bright_tbl, + changeUIntScale(Exs.dimmer.channel[0].bright_tbl, 0, 255, 0, 100), + Exs.dimmer.channel[1].on, Exs.dimmer.channel[1].dimm, + Exs.dimmer.channel[1].bright_tbl, + changeUIntScale(Exs.dimmer.channel[1].bright_tbl, 0, 255, 0, 100), + Exs.dimmer.gate_lock); +#endif +} + +void ExsPacketProcess(void) +{ + uint8_t len = Exs.buffer[1]; + uint8_t cmd = Exs.buffer[2]; + + switch (cmd) + { + case EXS_GET_VALUES: +# 294 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" + if (len > 9) + { + Exs.dimmer.version_major = Exs.buffer[3]; + Exs.dimmer.version_minor = Exs.buffer[4]; + + + Exs.dimmer.channel[0].on = Exs.buffer[6]; + Exs.dimmer.channel[0].dimm = Exs.buffer[6]; + Exs.dimmer.channel[0].bright_tbl = Exs.buffer[7]; + + + Exs.dimmer.channel[1].on = Exs.buffer[9]; + Exs.dimmer.channel[1].dimm = Exs.buffer[9]; + Exs.dimmer.channel[1].bright_tbl = Exs.buffer[10]; + + Exs.dimmer.gate_lock = Exs.buffer[11]; + } + else +# 327 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" + { + Exs.dimmer.version_major = 1; + Exs.dimmer.version_minor = 0; + + + Exs.dimmer.channel[0].on = Exs.buffer[4] - 48; + Exs.dimmer.channel[0].dimm = Exs.buffer[4] - 48; + Exs.dimmer.channel[0].bright_tbl = Exs.buffer[5] - 48; + + + Exs.dimmer.channel[1].on = Exs.buffer[7] - 48; + Exs.dimmer.channel[1].dimm = Exs.buffer[7] - 48; + Exs.dimmer.channel[1].bright_tbl = Exs.buffer[8] - 48; + + Exs.dimmer.gate_lock = Exs.buffer[9] - 48; + } + + ExsDebugState(); + ExsSyncState(); + ExsDebugState(); + break; + default: + break; + } +} + + + +bool ExsModuleSelected(void) +{ + Settings.light_correction = 0; + Settings.flag.mqtt_serial = 0; + Settings.flag3.pwm_multi_channels = 1; + SetSeriallog(LOG_LEVEL_NONE); + + devices_present = +2; + light_type = LT_SERIAL2; + return true; +} + +bool ExsSetChannels(void) +{ +#ifdef EXS_DEBUG + snprintf_P(log_data, sizeof(log_data), PSTR("EXS: SetChannels: \"")); + for (int i = 0; i < XdrvMailbox.data_len; i++) + { + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, ((uint8_t *)XdrvMailbox.data)[i]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s\""), log_data); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + + Exs.dimm[0] = ((uint8_t *)XdrvMailbox.data)[0]; + Exs.dimm[1] = ((uint8_t *)XdrvMailbox.data)[1]; + return ExsSyncState(); +} + +bool ExsSetPower(void) +{ + AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Set Power, Device %d, Power 0x%02x"), + active_device, XdrvMailbox.index); + + Exs.power = XdrvMailbox.index; + return ExsSyncState(); +} + +void EsxMcuStart(void) +{ + int retries = 3; + +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Request MCU configuration, PIN %d to Low"), pin[GPIO_EXS_ENABLE]); +#endif + + pinMode(pin[GPIO_EXS_ENABLE], OUTPUT); + digitalWrite(pin[GPIO_EXS_ENABLE], LOW); + + delay(1); + + while (ExsSerial->available()) + { + + ExsSerial->read(); + } +} + +void ExsInit(void) +{ +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Starting Tx %d Rx %d"), pin[GPIO_TXD], pin[GPIO_RXD]); +#endif + + Exs.buffer = (uint8_t *)malloc(EXS_BUFFER_SIZE); + if (Exs.buffer != nullptr) + { + ExsSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); + if (ExsSerial->begin(9600)) + { + if (ExsSerial->hardwareSerial()) + { + ClaimSerial(); + } + ExsSerial->flush(); + EsxMcuStart(); + ExsSendCmd(EXS_CH_LOCK, 0); + ExsSendCmd(EXS_GET_VALUES, 0); + } + } +} + +void ExsSerialInput(void) +{ + while (ExsSerial->available()) + { + yield(); + uint8_t serial_in_byte = ExsSerial->read(); + + AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Serial In Byte 0x%02x"), serial_in_byte); + + if (Exs.cmd_status == 0 && + serial_in_byte == 0x7B) + { + Exs.cmd_status = 1; + Exs.byte_counter = 0; + } + else if (Exs.byte_counter >= EXS_BUFFER_SIZE) + { + Exs.cmd_status = 0; + } + else if (Exs.cmd_status == 1) + { + Exs.buffer[Exs.byte_counter++] = serial_in_byte; + + if (Exs.byte_counter > 2 && Exs.byte_counter == Exs.buffer[1] + 2) + { + uint8_t crc = crc8(&Exs.buffer[2], Exs.buffer[1]); + + + Exs.cmd_status = 0; + +#ifdef EXS_DEBUG + snprintf_P(log_data, sizeof(log_data), PSTR("EXS: RX Packet: \"")); + for (uint32_t i = 0; i < Exs.byte_counter; i++) + { + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, Exs.buffer[i]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s\", CRC: 0x%02x"), log_data, crc); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + + if (Exs.buffer[0] == crc) + { + ExsSerial->write(0xFF); + ExsPacketProcess(); + } + else + { + ExsSerial->write(0x00); + } + + } + } + } +} + + + + + +#ifdef EXS_MCU_CMNDS + +#define D_PRFX_EXS "Exs" +#define D_CMND_EXS_DIMM "Dimm" +#define D_CMND_EXS_DIMM_TBL "DimmTbl" +#define D_CMND_EXS_DIMM_VAL "DimmVal" +#define D_CMND_EXS_DIMMS "Dimms" +#define D_CMND_EXS_CH_LOCK "ChLock" +#define D_CMND_EXS_STATE "State" + +const char kExsCommands[] PROGMEM = D_PRFX_EXS "|" + D_CMND_EXS_DIMM "|" D_CMND_EXS_DIMM_TBL "|" D_CMND_EXS_DIMM_VAL "|" + D_CMND_EXS_DIMMS "|" D_CMND_EXS_CH_LOCK "|" + D_CMND_EXS_STATE; + +void (* const ExsCommand[])(void) PROGMEM = { + &CmndExsDimm, &CmndExsDimmTbl, &CmndExsDimmVal, + &CmndExsDimms, &CmndExsChLock, + &CmndExsState }; + +void CmndExsDimm(void) +{ + if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) && + (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1)) { + ExsSendCmd(EXS_DIMM_1_ON + 0x10 * (XdrvMailbox.index - 1) + + XdrvMailbox.payload ^ 1, 0); + } + CmndExsState(); +} + +void CmndExsDimmTbl(void) +{ + if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) && + (XdrvMailbox.payload > 0 || XdrvMailbox.payload <= 255)) { + ExsSendCmd(EXS_DIMM_1_TBL + 0x10 * (XdrvMailbox.index - 1), + XdrvMailbox.payload); + } + CmndExsState(); +} + +void CmndExsDimmVal(void) +{ + if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) && + (XdrvMailbox.payload > 0 || XdrvMailbox.payload <= 255)) { + ExsSendCmd(EXS_DIMM_1_VAL + 0x10 * (XdrvMailbox.index - 1), + XdrvMailbox.payload); + } + CmndExsState(); +} + +void CmndExsDimms(void) +{ + if (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1) { + ExsSendCmd(EXS_DIMMS_ON + XdrvMailbox.payload ^ 1, 0); + } + CmndExsState(); +} + +void CmndExsChLock(void) +{ + if (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1) { + ExsSendCmd(EXS_CH_LOCK, XdrvMailbox.payload); + } + CmndExsState(); +} + +void CmndExsState(void) +{ + ExsSendCmd(EXS_GET_VALUES, 0); + + + uint32_t snd_time = millis(); + while ((TimePassedSince(snd_time) < EXS_ACK_TIMEOUT) && + (!ExsSerial->available())) + ; + ExsSerialInput(); + + Response_P(PSTR("{\"" D_CMND_EXS_STATE "\":{")); + ResponseAppend_P(PSTR("\"McuVersion\":\"%d.%d\"," + "\"Channels\":["), + Exs.dimmer.version_major, Exs.dimmer.version_minor); + + for (uint32_t i = 0; i < 2; i++) { + if (i != 0) { + ResponseAppend_P(PSTR(",")); + } + ResponseAppend_P(PSTR("{\"On\":\"%d\"," + "\"BrightProz\":\"%d\"," + "\"BrightTab\":\"%d\"," + "\"Dimm\":\"%d\"}"), + Exs.dimmer.channel[i].on, + changeUIntScale(Exs.dimmer.channel[i].bright_tbl, 0, 255, 0, 100), + Exs.dimmer.channel[i].bright_tbl, + Exs.dimmer.channel[i].dimm); + } + ResponseAppend_P(PSTR("],")); + ResponseAppend_P(PSTR("\"GateLock\":\"%d\""), Exs.dimmer.gate_lock); + ResponseJsonEndEnd(); +} + +#endif + + + + + +bool Xdrv30(uint8_t function) +{ + bool result = false; + + if (EXS_DIMMER == my_module_type) + { + switch (function) + { + case FUNC_LOOP: + if (ExsSerial) + ExsSerialInput(); + break; + case FUNC_MODULE_INIT: + result = ExsModuleSelected(); + break; + case FUNC_INIT: + ExsInit(); + break; + case FUNC_SET_DEVICE_POWER: + result = ExsSetPower(); + break; + case FUNC_SET_CHANNELS: + result = ExsSetChannels(); + break; +#ifdef EXS_MCU_CMNDS + case FUNC_COMMAND: + result = DecodeCommand(kExsCommands, ExsCommand); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_31_tasmota_slave.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_31_tasmota_slave.ino" +#ifdef USE_TASMOTA_SLAVE + + + + +#define XDRV_31 31 + +#define CONST_STK_CRC_EOP 0x20 + +#define CMND_STK_GET_SYNC 0x30 +#define CMND_STK_SET_DEVICE 0x42 +#define CMND_STK_SET_DEVICE_EXT 0x45 +#define CMND_STK_ENTER_PROGMODE 0x50 +#define CMND_STK_LEAVE_PROGMODE 0x51 +#define CMND_STK_LOAD_ADDRESS 0x55 +#define CMND_STK_PROG_PAGE 0x64 + + + + + +#define CMND_START 0xFC +#define CMND_END 0xFD + +#define CMND_FEATURES 0x01 +#define CMND_JSON 0x02 +#define CMND_FUNC_EVERY_SECOND 0x03 +#define CMND_FUNC_EVERY_100_MSECOND 0x04 +#define CMND_SLAVE_SEND 0x05 +#define CMND_PUBLISH_TELE 0x06 +#define CMND_EXECUTE_CMND 0x07 + +#define PARAM_DATA_START 0xFE +#define PARAM_DATA_END 0xFF + +#include + + + + + +class SimpleHexParse { + public: + SimpleHexParse(void); + uint8_t parseLine(char *hexline); + uint8_t ptr_l = 0; + uint8_t ptr_h = 0; + bool PageIsReady = false; + bool firstrun = true; + bool EndOfFile = false; + uint8_t FlashPage[128]; + uint8_t FlashPageIdx = 0; + uint8_t layoverBuffer[16]; + uint8_t layoverIdx = 0; + uint8_t getByte(char *hexline, uint8_t idx); +}; + +SimpleHexParse::SimpleHexParse(void) +{ + +} + +uint8_t SimpleHexParse::parseLine(char *hexline) +{ + if (layoverIdx) { + memcpy(&FlashPage[0], &layoverBuffer[0], layoverIdx); + FlashPageIdx = layoverIdx; + layoverIdx = 0; + } + uint8_t len = getByte(hexline, 1); + uint8_t addr_h = getByte(hexline, 2); + uint8_t addr_l = getByte(hexline, 3); + uint8_t rectype = getByte(hexline, 4); + for (uint8_t idx = 0; idx < len; idx++) { + if (FlashPageIdx < 128) { + FlashPage[FlashPageIdx] = getByte(hexline, idx+5); + FlashPageIdx++; + } else { + layoverBuffer[layoverIdx] = getByte(hexline, idx+5); + layoverIdx++; + } + } + if (1 == rectype) { + EndOfFile = true; + while (FlashPageIdx < 128) { + FlashPage[FlashPageIdx] = 0xFF; + FlashPageIdx++; + } + } + if (FlashPageIdx == 128) { + if (firstrun) { + firstrun = false; + } else { + ptr_l += 0x40; + if (ptr_l == 0) { + ptr_l = 0; + ptr_h++; + } + } + firstrun = false; + PageIsReady = true; + } + return 0; +} + +uint8_t SimpleHexParse::getByte(char* hexline, uint8_t idx) +{ + char buff[3]; + buff[3] = '\0'; + memcpy(&buff, &hexline[(idx*2)-1], 2); + return strtol(buff, 0, 16); +} + + + + + +struct TSLAVE { + uint32_t spi_hex_size = 0; + uint32_t spi_sector_counter = 0; + uint8_t spi_sector_cursor = 0; + uint8_t inverted = LOW; + bool type = false; + bool flashing = false; + bool SerialEnabled = false; + uint8_t waitstate = 0; + bool unsupported = false; +} TSlave; + +typedef union { + uint32_t data; + struct { + uint32_t func_json_append : 1; + uint32_t func_every_second : 1; + uint32_t func_every_100_msecond : 1; + uint32_t func_slave_send : 1; + uint32_t spare4 : 1; + uint32_t spare5 : 1; + uint32_t spare6 : 1; + uint32_t spare7 : 1; + uint32_t spare8 : 1; + uint32_t spare9 : 1; + uint32_t spare10 : 1; + uint32_t spare11 : 1; + uint32_t spare12 : 1; + uint32_t spare13 : 1; + uint32_t spare14 : 1; + uint32_t spare15 : 1; + uint32_t spare16 : 1; + uint32_t spare17 : 1; + uint32_t spare18 : 1; + uint32_t spare19 : 1; + uint32_t spare20 : 1; + uint32_t spare21 : 1; + uint32_t spare22 : 1; + uint32_t spare23 : 1; + uint32_t spare24 : 1; + uint32_t spare25 : 1; + uint32_t spare26 : 1; + uint32_t spare27 : 1; + uint32_t spare28 : 1; + uint32_t spare29 : 1; + uint32_t spare30 : 1; + uint32_t spare31 : 1; + }; +} TSlaveFeatureCfg; + + + + + + +struct TSLAVE_FEATURES { + uint32_t features_version; + TSlaveFeatureCfg features; +} TSlaveSettings; + +struct TSLAVE_COMMAND { + uint8_t command; + uint8_t parameter; + uint8_t unused2; + uint8_t unused3; +} TSlaveCommand; + +TasmotaSerial *TasmotaSlave_Serial; + +uint32_t TasmotaSlave_FlashStart(void) +{ + return (ESP.getSketchSize() / SPI_FLASH_SEC_SIZE) + 2; +} + +uint8_t TasmotaSlave_UpdateInit(void) +{ + TSlave.spi_hex_size = 0; + TSlave.spi_sector_counter = TasmotaSlave_FlashStart(); + TSlave.spi_sector_cursor = 0; + return 0; +} + +void TasmotaSlave_Reset(void) +{ + if (TSlave.SerialEnabled) { + digitalWrite(pin[GPIO_TASMOTASLAVE_RST], !TSlave.inverted); + delay(1); + digitalWrite(pin[GPIO_TASMOTASLAVE_RST], TSlave.inverted); + delay(1); + digitalWrite(pin[GPIO_TASMOTASLAVE_RST], !TSlave.inverted); + delay(5); + } +} + +uint8_t TasmotaSlave_waitForSerialData(int dataCount, int timeout) +{ + int timer = 0; + while (timer < timeout) { + if (TasmotaSlave_Serial->available() >= dataCount) { + return 1; + } + delay(1); + timer++; + } + return 0; +} + +uint8_t TasmotaSlave_sendBytes(uint8_t* bytes, int count) +{ + TasmotaSlave_Serial->write(bytes, count); + TasmotaSlave_waitForSerialData(2, 250); + uint8_t sync = TasmotaSlave_Serial->read(); + uint8_t ok = TasmotaSlave_Serial->read(); + if ((sync == 0x14) && (ok == 0x10)) { + return 1; + } + return 0; +} + +uint8_t TasmotaSlave_execCmd(uint8_t cmd) +{ + uint8_t bytes[] = { cmd, CONST_STK_CRC_EOP }; + return TasmotaSlave_sendBytes(bytes, 2); +} + +uint8_t TasmotaSlave_execParam(uint8_t cmd, uint8_t* params, int count) +{ + uint8_t bytes[32]; + bytes[0] = cmd; + int i = 0; + while (i < count) { + bytes[i + 1] = params[i]; + i++; + } + bytes[i + 1] = CONST_STK_CRC_EOP; + return TasmotaSlave_sendBytes(bytes, i + 2); +} + +uint8_t TasmotaSlave_exitProgMode(void) +{ + return TasmotaSlave_execCmd(CMND_STK_LEAVE_PROGMODE); +} + +uint8_t TasmotaSlave_SetupFlash(void) +{ + uint8_t ProgParams[] = {0x86, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00}; + uint8_t ExtProgParams[] = {0x05, 0x04, 0xd7, 0xc2, 0x00}; + TasmotaSlave_Serial->begin(USE_TASMOTA_SLAVE_FLASH_SPEED); + if (TasmotaSlave_Serial->hardwareSerial()) { + ClaimSerial(); + } + + TasmotaSlave_Reset(); + + uint8_t timeout = 0; + uint8_t no_error = 0; + while (50 > timeout) { + if (TasmotaSlave_execCmd(CMND_STK_GET_SYNC)) { + timeout = 200; + no_error = 1; + } + timeout++; + delay(1); + } + if (no_error) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Found bootloader")); + } else { + no_error = 0; + AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Bootloader could not be found")); + } + if (no_error) { + if (TasmotaSlave_execParam(CMND_STK_SET_DEVICE, ProgParams, sizeof(ProgParams))) { + } else { + no_error = 0; + AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Could not configure device for programming (1)")); + } + } + if (no_error) { + if (TasmotaSlave_execParam(CMND_STK_SET_DEVICE_EXT, ExtProgParams, sizeof(ExtProgParams))) { + } else { + no_error = 0; + AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Could not configure device for programming (2)")); + } + } + if (no_error) { + if (TasmotaSlave_execCmd(CMND_STK_ENTER_PROGMODE)) { + } else { + no_error = 0; + AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Failed to put bootloader into programming mode")); + } + } + return no_error; +} + +uint8_t TasmotaSlave_loadAddress(uint8_t adrHi, uint8_t adrLo) +{ + uint8_t params[] = { adrLo, adrHi }; + return TasmotaSlave_execParam(CMND_STK_LOAD_ADDRESS, params, sizeof(params)); +} + +void TasmotaSlave_FlashPage(uint8_t addr_h, uint8_t addr_l, uint8_t* data) +{ + uint8_t Header[] = {CMND_STK_PROG_PAGE, 0x00, 0x80, 0x46}; + TasmotaSlave_loadAddress(addr_h, addr_l); + TasmotaSlave_Serial->write(Header, 4); + for (int i = 0; i < 128; i++) { + TasmotaSlave_Serial->write(data[i]); + } + TasmotaSlave_Serial->write(CONST_STK_CRC_EOP); + TasmotaSlave_waitForSerialData(2, 250); + TasmotaSlave_Serial->read(); + TasmotaSlave_Serial->read(); +} + +void TasmotaSlave_Flash(void) +{ + bool reading = true; + uint32_t read = 0; + uint32_t processed = 0; + char thishexline[50]; + uint8_t position = 0; + char* flash_buffer; + + SimpleHexParse hexParse = SimpleHexParse(); + + if (!TasmotaSlave_SetupFlash()) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Flashing aborted!")); + TSlave.flashing = false; + restart_flag = 2; + return; + } + + flash_buffer = new char[SPI_FLASH_SEC_SIZE]; + uint32_t flash_start = TasmotaSlave_FlashStart() * SPI_FLASH_SEC_SIZE; + while (reading) { + ESP.flashRead(flash_start + read, (uint32_t*)flash_buffer, SPI_FLASH_SEC_SIZE); + read = read + SPI_FLASH_SEC_SIZE; + if (read >= TSlave.spi_hex_size) { + reading = false; + } + for (uint32_t ca = 0; ca < SPI_FLASH_SEC_SIZE; ca++) { + processed++; + if ((processed <= TSlave.spi_hex_size) && (!hexParse.EndOfFile)) { + if (':' == flash_buffer[ca]) { + position = 0; + } + if (0x0D == flash_buffer[ca]) { + thishexline[position] = 0; + hexParse.parseLine(thishexline); + if (hexParse.PageIsReady) { + TasmotaSlave_FlashPage(hexParse.ptr_h, hexParse.ptr_l, hexParse.FlashPage); + hexParse.PageIsReady = false; + hexParse.FlashPageIdx = 0; + } + } else { + if (0x0A != flash_buffer[ca]) { + thishexline[position] = flash_buffer[ca]; + position++; + } + } + } + } + } + TasmotaSlave_exitProgMode(); + AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Flash done!")); + TSlave.flashing = false; + restart_flag = 2; +} + +void TasmotaSlave_SetFlagFlashing(bool value) +{ + TSlave.flashing = value; +} + +bool TasmotaSlave_GetFlagFlashing(void) +{ + return TSlave.flashing; +} + +void TasmotaSlave_WriteBuffer(uint8_t *buf, size_t size) +{ + if (0 == TSlave.spi_sector_cursor) { + ESP.flashEraseSector(TSlave.spi_sector_counter); + } + TSlave.spi_sector_cursor++; + ESP.flashWrite((TSlave.spi_sector_counter * SPI_FLASH_SEC_SIZE) + ((TSlave.spi_sector_cursor-1)*2048), (uint32_t*)buf, size); + TSlave.spi_hex_size = TSlave.spi_hex_size + size; + if (2 == TSlave.spi_sector_cursor) { + TSlave.spi_sector_cursor = 0; + TSlave.spi_sector_counter++; + } +} + +void TasmotaSlave_Init(void) +{ + if (TSlave.type) { + return; + } + if (10 > TSlave.waitstate) { + TSlave.waitstate++; + return; + } + if (!TSlave.SerialEnabled) { + if ((pin[GPIO_TASMOTASLAVE_RXD] < 99) && (pin[GPIO_TASMOTASLAVE_TXD] < 99) && + ((pin[GPIO_TASMOTASLAVE_RST] < 99) || (pin[GPIO_TASMOTASLAVE_RST_INV] < 99))) { + TasmotaSlave_Serial = new TasmotaSerial(pin[GPIO_TASMOTASLAVE_RXD], pin[GPIO_TASMOTASLAVE_TXD], 1, 0, 200); + if (TasmotaSlave_Serial->begin(USE_TASMOTA_SLAVE_SERIAL_SPEED)) { + if (TasmotaSlave_Serial->hardwareSerial()) { + ClaimSerial(); + } + TasmotaSlave_Serial->setTimeout(50); + if (pin[GPIO_TASMOTASLAVE_RST_INV] < 99) { + pin[GPIO_TASMOTASLAVE_RST] = pin[GPIO_TASMOTASLAVE_RST_INV]; + pin[GPIO_TASMOTASLAVE_RST_INV] = 99; + TSlave.inverted = HIGH; + } + pinMode(pin[GPIO_TASMOTASLAVE_RST], OUTPUT); + TSlave.SerialEnabled = true; + TasmotaSlave_Reset(); + AddLog_P2(LOG_LEVEL_INFO, PSTR("Tasmota Slave Enabled")); + } + } + } + if (TSlave.SerialEnabled) { + TasmotaSlave_sendCmnd(CMND_FEATURES, 0); + char buffer[32]; + TasmotaSlave_Serial->readBytesUntil(char(PARAM_DATA_START), buffer, sizeof(buffer)); + uint8_t len = TasmotaSlave_Serial->readBytesUntil(char(PARAM_DATA_END), buffer, sizeof(buffer)); + memcpy(&TSlaveSettings, &buffer, sizeof(TSlaveSettings)); + if (20191129 == TSlaveSettings.features_version) { + TSlave.type = true; + AddLog_P2(LOG_LEVEL_INFO, PSTR("Tasmota Slave Version %u"), TSlaveSettings.features_version); + } else { + if ((!TSlave.unsupported) && (TSlaveSettings.features_version > 0)) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("Tasmota Slave Version %u not supported!"), TSlaveSettings.features_version); + TSlave.unsupported = true; + } + } + } +} + +void TasmotaSlave_Show(void) +{ + if ((TSlave.type) && (TSlaveSettings.features.func_json_append)) { + char buffer[100]; + TasmotaSlave_sendCmnd(CMND_JSON, 0); + TasmotaSlave_Serial->readBytesUntil(char(PARAM_DATA_START), buffer, sizeof(buffer)-1); + uint8_t len = TasmotaSlave_Serial->readBytesUntil(char(PARAM_DATA_END), buffer, sizeof(buffer)-1); + buffer[len] = '\0'; + ResponseAppend_P(PSTR(",\"TasmotaSlave\":%s"), buffer); + } +} + +void TasmotaSlave_sendCmnd(uint8_t cmnd, uint8_t param) +{ + TSlaveCommand.command = cmnd; + TSlaveCommand.parameter = param; + char buffer[sizeof(TSlaveCommand)+2]; + buffer[0] = CMND_START; + memcpy(&buffer[1], &TSlaveCommand, sizeof(TSlaveCommand)); + buffer[sizeof(TSlaveCommand)+1] = CMND_END; + for (uint8_t ca = 0; ca < sizeof(buffer); ca++) { + TasmotaSlave_Serial->write(buffer[ca]); + } +} + +#define D_PRFX_SLAVE "Slave" +#define D_CMND_SLAVE_RESET "Reset" +#define D_CMND_SLAVE_SEND "Send" + +const char kTasmotaSlaveCommands[] PROGMEM = D_PRFX_SLAVE "|" + D_CMND_SLAVE_RESET "|" D_CMND_SLAVE_SEND; + +void (* const TasmotaSlaveCommand[])(void) PROGMEM = { + &CmndTasmotaSlaveReset, &CmndTasmotaSlaveSend }; + +void CmndTasmotaSlaveReset(void) +{ + TasmotaSlave_Reset(); + TSlave.type = false; + TSlave.waitstate = 7; + TSlave.unsupported = false; + ResponseCmndDone(); +} + +void CmndTasmotaSlaveSend(void) +{ + if (0 < XdrvMailbox.data_len) { + TasmotaSlave_sendCmnd(CMND_SLAVE_SEND, XdrvMailbox.data_len); + TasmotaSlave_Serial->write(char(PARAM_DATA_START)); + for (uint8_t idx = 0; idx < XdrvMailbox.data_len; idx++) { + TasmotaSlave_Serial->write(XdrvMailbox.data[idx]); + } + TasmotaSlave_Serial->write(char(PARAM_DATA_END)); + } + ResponseCmndDone(); +} + +void TasmotaSlave_ProcessIn(void) +{ + uint8_t cmnd = TasmotaSlave_Serial->read(); + switch (cmnd) { + case CMND_START: + TasmotaSlave_waitForSerialData(sizeof(TSlaveCommand),50); + uint8_t buffer[sizeof(TSlaveCommand)]; + for (uint8_t idx = 0; idx < sizeof(TSlaveCommand); idx++) { + buffer[idx] = TasmotaSlave_Serial->read(); + } + TasmotaSlave_Serial->read(); + memcpy(&TSlaveCommand, &buffer, sizeof(TSlaveCommand)); + char inbuf[TSlaveCommand.parameter+1]; + TasmotaSlave_waitForSerialData(TSlaveCommand.parameter, 50); + TasmotaSlave_Serial->read(); + for (uint8_t idx = 0; idx < TSlaveCommand.parameter; idx++) { + inbuf[idx] = TasmotaSlave_Serial->read(); + } + TasmotaSlave_Serial->read(); + inbuf[TSlaveCommand.parameter] = '\0'; + + if (CMND_PUBLISH_TELE == TSlaveCommand.command) { + Response_P(PSTR("{\"TasmotaSlave\":")); + ResponseAppend_P("%s", inbuf); + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); + XdrvRulesProcess(); + } + if (CMND_EXECUTE_CMND == TSlaveCommand.command) { + ExecuteCommand(inbuf, SRC_IGNORE); + } + break; + default: + break; + } +} + + + + + + +bool Xdrv31(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_100_MSECOND: + if (TSlave.type) { + if (TasmotaSlave_Serial->available()) { + TasmotaSlave_ProcessIn(); + } + if (TSlaveSettings.features.func_every_100_msecond) { + TasmotaSlave_sendCmnd(CMND_FUNC_EVERY_100_MSECOND, 0); + } + } + break; + case FUNC_EVERY_SECOND: + if ((TSlave.type) && (TSlaveSettings.features.func_every_second)) { + TasmotaSlave_sendCmnd(CMND_FUNC_EVERY_SECOND, 0); + } + TasmotaSlave_Init(); + break; + case FUNC_JSON_APPEND: + if ((TSlave.type) && (TSlaveSettings.features.func_json_append)) { + TasmotaSlave_Show(); + } + break; + case FUNC_COMMAND: + result = DecodeCommand(kTasmotaSlaveCommands, TasmotaSlaveCommand); + break; + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_32_hotplug.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_32_hotplug.ino" +#ifdef USE_HOTPLUG + + + + + + + +#define XDRV_32 32 + +const uint32_t HOTPLUG_MAX = 254; + +const char kHotPlugCommands[] PROGMEM = "|" + D_CMND_HOTPLUG; + +void (* const HotPlugCommand[])(void) PROGMEM = { + &CmndHotPlugTime }; + +struct { + + bool enabled = false; + uint8_t timeout = 0; +} Hotplug; + +void HotPlugInit(void) +{ + + if (Settings.hotplug_scan == 0xFF) { Settings.hotplug_scan = 0; } + if (Settings.hotplug_scan != 0) { + Hotplug.enabled = true; + Hotplug.timeout = 1; + } else + Hotplug.enabled = false; +} + +void HotPlugEverySecond(void) +{ + if (Hotplug.enabled) { + if (Hotplug.timeout == 0) { + XsnsCall(FUNC_HOTPLUG_SCAN); + Hotplug.timeout = Settings.hotplug_scan; + } + Hotplug.timeout--; + } +} + + + + + +void CmndHotPlugTime(void) +{ + if (XdrvMailbox.payload <= HOTPLUG_MAX) { + Settings.hotplug_scan = XdrvMailbox.payload; + HotPlugInit(); + } + ResponseCmndNumber(Settings.hotplug_scan); +} + + + + + +bool Xdrv32(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_SECOND: + HotPlugEverySecond(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kHotPlugCommands, HotPlugCommand); + break; + case FUNC_PRE_INIT: + HotPlugInit(); + break; + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_33_nrf24l01.ino" +# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_33_nrf24l01.ino" +#ifdef USE_SPI +#ifdef USE_NRF24 + + + + + + + +#define XDRV_33 33 + +#define MOSI 13 +#define MISO 12 +#define SCK 14 + +#include +#include + +const char NRF24type[] PROGMEM = "NRF24"; + +const char HTTP_NRF24[] PROGMEM = + "{s}%sL01%c: " "{m}started{e}"; + +struct { + uint8_t chipType = 0; +} NRF24; + + + +RF24 NRF24radio; + +bool NRF24initRadio() +{ + NRF24radio.begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_DC]); + NRF24radio.powerUp(); + + if(NRF24radio.isChipConnected()){ + DEBUG_DRIVER_LOG(PSTR("NRF24 chip connected")); + return true; + } + DEBUG_DRIVER_LOG(PSTR("NRF24 chip NOT !!!! connected")); + return false; +} + +bool NRF24Detect(void) +{ + if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_DC]<99)){ + SPI.pins(SCK,MOSI,MISO,-1); + if(NRF24initRadio()){ + NRF24.chipType = 32; + AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF24L01 initialized")); + if(NRF24radio.isPVariant()){ + NRF24.chipType = 43; + AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF24L01+ detected")); + } + return true; + } + } + return false; +} + + + + + +bool Xdrv33(uint8_t function) +{ + bool result = false; + + if (FUNC_INIT == function) { + result = NRF24Detect(); + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_34_wemos_motor_v1.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_34_wemos_motor_v1.ino" +#ifdef USE_I2C +#ifdef USE_WEMOS_MOTOR_V1 +# 45 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_34_wemos_motor_v1.ino" +#define XDRV_34 34 +#define XI2C_44 44 + +#ifndef WEMOS_MOTOR_V1_ADDR +#define WEMOS_MOTOR_V1_ADDR 0x30 +#endif +#ifndef WEMOS_MOTOR_V1_FREQ +#define WEMOS_MOTOR_V1_FREQ 1000 +#endif + +#define MOTOR_A 0 +#define MOTOR_B 1 + +#define SHORT_BRAKE 0 +#define CCW 1 +#define CW 2 +#define STOP 3 +#define STANDBY 4 + +struct WMOTORV1 { + bool detected = false; + uint8_t motor; +} WMotorV1; + +void WMotorV1Detect(void) +{ + if (I2cSetDevice(WEMOS_MOTOR_V1_ADDR)) { + WMotorV1.detected = true; + I2cSetActiveFound(WEMOS_MOTOR_V1_ADDR, "WEMOS_MOTOR_V1"); + WMotorV1Reset(); + } +} + +void WMotorV1Reset(void) +{ + + WMotorV1SetFrequency(WEMOS_MOTOR_V1_FREQ); +} + +void WMotorV1SetFrequency(uint32_t freq) +{ + Wire.beginTransmission(WEMOS_MOTOR_V1_ADDR); + Wire.write(((byte)(freq >> 16)) & (byte)0x0f); + Wire.write((byte)(freq >> 16)); + Wire.write((byte)(freq >> 8)); + Wire.write((byte)freq); + Wire.endTransmission(); + +} + +void WMotorV1SetMotor(uint8_t motor, uint8_t dir, float pwm_val) +{ + Wire.beginTransmission(WEMOS_MOTOR_V1_ADDR); + Wire.write(motor | (byte)0x10); + Wire.write(dir); + + uint16_t _pwm_val = uint16_t(pwm_val * 100); + if (_pwm_val > 10000) { + _pwm_val = 10000; + } + + Wire.write((byte)(_pwm_val >> 8)); + Wire.write((byte)_pwm_val); + Wire.endTransmission(); + +} + +bool WMotorV1Command(void) +{ + uint8_t args_count = 0; + + if (XdrvMailbox.data_len > 0) { + args_count = 1; + } else { + return false; + } + + for (uint32_t idx = 0; idx < XdrvMailbox.data_len; idx++) { + if (' ' == XdrvMailbox.data[idx]) { + XdrvMailbox.data[idx] = ','; + } + if (',' == XdrvMailbox.data[idx]) { + args_count++; + } + } + UpperCase(XdrvMailbox.data, XdrvMailbox.data); + + char *command = strtok(XdrvMailbox.data, ","); + + if (strcmp(command, "RESET") == 0) { + WMotorV1Reset(); + Response_P(PSTR("{\"WEMOS_MOTOR_V1\":{\"RESET\":\"OK\"}}")); + return true; + } + + if (strcmp(command, "SETMOTOR") == 0) { + if (args_count >= 3) { + + int motor = atoi(strtok(NULL, ",")); + int dir = atoi(strtok(NULL, ",")); + int duty = 100; + if (args_count == 4) { + duty = atoi(strtok(NULL, ",")); + } + + WMotorV1SetMotor(motor, dir, duty); + Response_P(PSTR("{\"WEMOS_MOTOR_V1\":{\"SETMOTOR\":\"OK\"}}")); + return true; + } + } + return false; +} + + + + + +bool Xdrv34(uint8_t function) +{ + if (!I2cEnabled(XI2C_44)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + WMotorV1Detect(); + } + else if (WMotorV1.detected) { + switch (function) { + case FUNC_COMMAND_DRIVER: + if (XI2C_44 == XdrvMailbox.index) { + result = WMotorV1Command(); + } + break; + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_35_pwm_dimmer.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_35_pwm_dimmer.ino" +#ifdef USE_PWM_DIMMER +# 33 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_35_pwm_dimmer.ino" +#define XDRV_35 35 + +const char kPWMDimmerCommands[] PROGMEM = "|" + D_CMND_BRI_PRESET; + +void (* const PWMDimmerCommand[])(void) PROGMEM = { + &CmndBriPreset }; + +#ifdef USE_PWM_DIMMER_REMOTE +struct remote_pwm_dimmer { + power_t power; + uint8_t bri_power_on; + uint8_t bri_preset_low; + uint8_t bri_preset_high; + uint8_t fixed_color_index; + uint8_t bri; + bool power_button_increases_bri; +}; +#endif + +uint32_t last_button_press_time; +uint32_t button_hold_time[3]; +uint8_t led_timeout_seconds = 0; +uint8_t restore_powered_off_led_counter = 0; +uint8_t power_button_index = 0; +uint8_t down_button_index = 1; +uint8_t buttons_pressed = 0; +uint8_t tap_count = 0; +bool down_button_tapped = false; +bool power_button_increases_bri = true; +bool invert_power_button_bri_direction = false; +bool restore_brightness_leds = false; +bool button_pressed[3] = { false, false, false }; +bool button_hold_sent[3]; +bool button_hold_processed[3]; +#ifdef USE_PWM_DIMMER_REMOTE +struct remote_pwm_dimmer * remote_pwm_dimmers; +struct remote_pwm_dimmer * active_remote_pwm_dimmer; +uint8_t remote_pwm_dimmer_count; +bool active_device_is_local; +#endif + +void PWMModulePreInit(void) +{ + Settings.seriallog_level = 0; + Settings.flag.mqtt_serial = 0; + Settings.ledstate = 0; + + + if (Settings.last_module != Settings.module) { + Settings.flag.pwm_control = true; + Settings.param[P_HOLD_TIME] = 5; + Settings.bri_power_on = Settings.bri_preset_low = Settings.bri_preset_high = 0; + Settings.last_module = Settings.module; + } + + + if (!Settings.bri_power_on) Settings.bri_power_on = 128; + if (!Settings.bri_preset_low) Settings.bri_preset_low = 10; + if (Settings.bri_preset_high < Settings.bri_preset_low) Settings.bri_preset_high = 255; + + PWMDimmerSetPoweredOffLed(); + + + if (!power && pin[GPIO_REL1] < 99) digitalWrite(pin[GPIO_REL1], bitRead(rel_inverted, 0) ? 1 : 0); + +#ifdef USE_PWM_DIMMER_REMOTE + + + if (Settings.flag4.remote_device_mode) { + Settings.flag4.device_groups_enabled = true; + + device_group_count = 0; + for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) { + if (pin[GPIO_KEY1 + button_index] < 99) device_group_count++; + } + + remote_pwm_dimmer_count = device_group_count - 1; + if (remote_pwm_dimmer_count) { + if ((remote_pwm_dimmers = (struct remote_pwm_dimmer *) calloc(remote_pwm_dimmer_count, sizeof(struct remote_pwm_dimmer))) == nullptr) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("PWMDimmer: error allocating PWM dimmer array")); + Settings.flag4.remote_device_mode = false; + } + else { + for (uint8_t i = 0; i < remote_pwm_dimmer_count; i++) { + active_remote_pwm_dimmer = &remote_pwm_dimmers[i]; + active_remote_pwm_dimmer->bri_power_on = 128; + active_remote_pwm_dimmer->bri_preset_low = 10; + active_remote_pwm_dimmer->bri_preset_high = 255; + } + } + } + } + active_device_is_local = true; +#endif +} + + +void PWMDimmerSetBrightnessLeds(int32_t operation) +{ + if (leds_present) { + uint32_t step = (!operation ? 256 / (leds_present + 1) : operation < 0 ? 256 : 0); + uint32_t current_bri = (Light.power ? light_state.getBri() : 0); + uint32_t level = step; + SetLedPowerIdx(0, current_bri >= level); + if (leds_present > 1) { + level += step; + SetLedPowerIdx(1, current_bri >= level); + if (leds_present > 2) { + level += step; + SetLedPowerIdx(2, current_bri >= level); + if (leds_present > 3) { + level += step; + SetLedPowerIdx(3, current_bri >= level); + } + } + } + + + if (!operation) led_timeout_seconds = (current_bri && Settings.flag4.led_timeout ? 5 : 0); + } +} + +void PWMDimmerSetPoweredOffLed(void) +{ + + if (pin[GPIO_LEDLNK] < 99) { + bool power_off_led_on = !power && Settings.flag4.powered_off_led; + if (ledlnk_inverted) power_off_led_on ^= 1; + digitalWrite(pin[GPIO_LEDLNK], power_off_led_on); + } +} + +void PWMDimmerSetPower(void) +{ + DigitalWrite(GPIO_REL1, bitRead(rel_inverted, 0) ? !power : power); + PWMDimmerSetBrightnessLeds(0); + PWMDimmerSetPoweredOffLed(); +} + +#ifdef USE_DEVICE_GROUPS +void PWMDimmerHandleDevGroupItem(void) +{ + uint32_t value = XdrvMailbox.payload; +#ifdef USE_PWM_DIMMER_REMOTE + uint8_t device_group_index = *(uint8_t *)XdrvMailbox.topic; + if (device_group_index > remote_pwm_dimmer_count) return; + bool device_is_local = device_groups[device_group_index].local; + struct remote_pwm_dimmer * remote_pwm_dimmer = &remote_pwm_dimmers[device_group_index]; +#else + if (*(uint8_t *)XdrvMailbox.topic) return; +#endif + + switch (XdrvMailbox.command_code) { +#ifdef USE_PWM_DIMMER_REMOTE + case DGR_ITEM_LIGHT_BRI: + if (!device_is_local) remote_pwm_dimmer->bri = value; + break; + case DGR_ITEM_POWER: + if (!device_is_local) { + remote_pwm_dimmer->power = value; + remote_pwm_dimmer->power_button_increases_bri = (remote_pwm_dimmer->bri < 128); + } + break; + case DGR_ITEM_LIGHT_FIXED_COLOR: + if (!device_is_local) remote_pwm_dimmer->fixed_color_index = value; + break; +#endif + case DGR_ITEM_BRI_POWER_ON: +#ifdef USE_PWM_DIMMER_REMOTE + if (!device_is_local) + remote_pwm_dimmer->bri_power_on = value; + else +#endif + Settings.bri_power_on = value; + break; + case DGR_ITEM_BRI_PRESET_LOW: +#ifdef USE_PWM_DIMMER_REMOTE + if (!device_is_local) + remote_pwm_dimmer->bri_preset_low = value; + else +#endif + Settings.bri_preset_low = value; + break; + case DGR_ITEM_BRI_PRESET_HIGH: +#ifdef USE_PWM_DIMMER_REMOTE + if (!device_is_local) + remote_pwm_dimmer->bri_preset_high = value; + else +#endif + Settings.bri_preset_high = value; + break; + case DGR_ITEM_STATUS: +#ifdef USE_PWM_DIMMER_REMOTE + if (device_is_local) +#endif + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_BRI_POWER_ON, Settings.bri_power_on, + DGR_ITEM_BRI_PRESET_LOW, Settings.bri_preset_low, DGR_ITEM_BRI_PRESET_HIGH, Settings.bri_preset_high); + break; + } +} +#endif + +void PWMDimmerHandleButton(void) +{ +# 268 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_35_pwm_dimmer.ino" + if (XdrvMailbox.payload && !button_pressed[XdrvMailbox.index]) { + + + if (last_button_press_time && !buttons_pressed && millis() - last_button_press_time > 400) { + last_button_press_time = 0; + tap_count = 0; + } + return; + } + + bool state_updated = false; + int8_t bri_offset = 0; + uint8_t power_on_bri = 0; + uint8_t dgr_item = 0; + uint8_t dgr_value; + uint8_t dgr_more_to_come = false; + uint32_t button_index = XdrvMailbox.index; + uint32_t now = millis(); + + +#ifdef USE_PWM_DIMMER_REMOTE + bool power_is_on = (!active_device_is_local ? active_remote_pwm_dimmer->power : power); + bool is_power_button = (button_index == power_button_index); +#else + bool power_is_on = power; + bool is_power_button = !button_index; +#endif + bool is_down_button = (button_index == down_button_index); + + + if (!XdrvMailbox.payload) { + + + + if (!button_pressed[button_index]) { + last_button_press_time = now; + button_pressed[button_index] = true; + button_hold_time[button_index] = now + Settings.param[P_HOLD_TIME] * 100; + button_hold_sent[button_index] = false; + buttons_pressed++; + +#ifdef USE_PWM_DIMMER_REMOTE + + + if (buttons_pressed == 1 && Settings.flag4.remote_device_mode) { + power_button_index = button_index; + down_button_index = (button_index ? 0 : 1); + active_device_is_local = device_groups[power_button_index].local; + if (!active_device_is_local) active_remote_pwm_dimmer = &remote_pwm_dimmers[power_button_index - 1]; + } +#endif + + return; + } + + + if (button_hold_time[button_index] < now) { + + + + if (!button_pressed[power_button_index] && now - button_hold_time[button_index] > 10000) { + button_hold_time[button_index] = now + 90000; + char scmnd[20]; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " 2")); + ExecuteCommand(scmnd, SRC_BUTTON); + return; + } + + + + if (!button_hold_sent[button_index]) { + button_hold_sent[button_index] = true; + button_hold_processed[button_index] = (!is_power_button && tap_count ? false : SendKey(KEY_BUTTON, button_index + 1, POWER_HOLD)); + } + if (!button_hold_processed[button_index]) { + + + if (is_power_button) { + + + if (buttons_pressed == 1) { + + + + + if (power_is_on) { +#ifdef USE_PWM_DIMMER_REMOTE + bri_offset = (!active_device_is_local ? (active_remote_pwm_dimmer->power_button_increases_bri ? 1 : -1) : (power_button_increases_bri ? 1 : -1)); +#else + bri_offset = (power_button_increases_bri ? 1 : -1); +#endif + invert_power_button_bri_direction = true; + } + + + + else { +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local) + power_on_bri = active_remote_pwm_dimmer->bri = active_remote_pwm_dimmer->bri_preset_low; + else +#endif + power_on_bri = Settings.bri_preset_low; + button_hold_time[button_index] = now + 500; + } + } + } + + + else { + + + + + if (power_is_on && !tap_count) { + bri_offset = (is_down_button ? -1 : 1); + } + + else { + uint8_t mqtt_trigger = 0; + + + + if (tap_count) { + + + if (down_button_tapped) { +#ifdef USE_DEVICE_GROUPS + uint8_t uint8_value; +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local) + uint8_value = active_remote_pwm_dimmer->fixed_color_index; + else +#endif + uint8_value = Light.fixed_color_index; + if (is_down_button) + uint8_value--; + else + uint8_value++; +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local) + active_remote_pwm_dimmer->fixed_color_index = uint8_value; + else +#endif + Light.fixed_color_index = uint8_value; + dgr_item = DGR_ITEM_LIGHT_FIXED_COLOR; + dgr_value = uint8_value; + dgr_more_to_come = true; +#endif + ; + } + + + else { + mqtt_trigger = (is_down_button ? 3 : 4); + } + } + + + else if (!power_is_on) { + mqtt_trigger = (is_down_button ? 1 : 2); + } + + + if (mqtt_trigger) { + char topic[TOPSZ]; + sprintf_P(mqtt_data, PSTR("Trigger%u"), mqtt_trigger); +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local) { + snprintf_P(topic, sizeof(topic), PSTR("cmnd/%s/Event"), device_groups[power_button_index].group_name); + MqttPublish(topic); + } + else +#endif + MqttPublishPrefixTopic_P(CMND, PSTR("Event")); + } + + button_hold_time[button_index] = now + 500; + } + } + } + } + } + + + else { + bool button_was_held = button_hold_sent[button_index]; + + + + if (!(button_hold_sent[button_index] ? button_hold_processed[button_index] : SendKey(KEY_BUTTON, button_index + 1, POWER_TOGGLE))) { + + + if (is_power_button) { + + + if (button_was_held) { + + + + if (invert_power_button_bri_direction) { + invert_power_button_bri_direction = false; +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local) + active_remote_pwm_dimmer->power_button_increases_bri ^= 1; + else +#endif + power_button_increases_bri ^= 1; +#ifdef USE_PWM_DIMMER_REMOTE + dgr_item = 255; + state_updated = true; +#endif + } + + + else if (tap_count) { + + + + if (!button_was_held) { + +#ifdef USE_PWM_DIMMER_REMOTE + if (active_device_is_local) { +#endif + + + if (down_button_tapped) { + Settings.flag4.led_timeout ^= 1; + if (Light.power) PWMDimmerSetBrightnessLeds(Settings.flag4.led_timeout ? -1 : 0); + } + + + else { + Settings.flag4.powered_off_led ^= 1; + PWMDimmerSetPoweredOffLed(); + } +#ifdef USE_PWM_DIMMER_REMOTE + } +#endif + } + + + + else if (down_button_tapped) { + dgr_item = 255; + } + } + } + + + else { +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local) + power_on_bri = active_remote_pwm_dimmer->bri_power_on; + else +#endif + power_on_bri = Settings.bri_power_on; + } + } + + + else { + + if (restore_brightness_leds) { + restore_brightness_leds = false; + PWMDimmerSetBrightnessLeds(Settings.flag4.led_timeout ? -1 : 0); + } + + + + if (!button_was_held && button_pressed[power_button_index]) { + down_button_tapped = is_down_button; + tap_count++; + } + + + if (!tap_count) { + + + if (power_is_on) { + + + + if (button_hold_time[button_index] >= now) { + bri_offset = (is_down_button ? -10 : 10); + dgr_item = 255; + } + + + + + else if (!button_hold_processed[button_index]) { + dgr_item = 255; + state_updated = true; + } + } + + + + else { +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local) + power_on_bri = active_remote_pwm_dimmer->bri = (is_down_button ? active_remote_pwm_dimmer->bri_preset_low : active_remote_pwm_dimmer->bri_preset_high); + else +#endif + power_on_bri = (is_down_button ? Settings.bri_preset_low : Settings.bri_preset_high); + } + } + } + } + + + button_pressed[button_index] = false; + buttons_pressed--; + } + + + if (bri_offset) { + int32_t bri; +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local) + bri = active_remote_pwm_dimmer->bri; + else +#endif + bri = light_state.getBri(); + int32_t new_bri; + bri_offset *= (Settings.light_correction ? 4 : bri / 16 + 1); + new_bri = bri + bri_offset; + if (bri_offset > 0) { + if (new_bri > 255) new_bri = 255; + } + else { + if (new_bri < 1) new_bri = 1; + } + if (new_bri != bri) { +#ifdef USE_DEVICE_GROUPS + SendDeviceGroupMessage(power_button_index, (dgr_item ? DGR_MSGTYP_UPDATE : DGR_MSGTYP_UPDATE_MORE_TO_COME), DGR_ITEM_LIGHT_BRI, new_bri); +#endif +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local) + active_remote_pwm_dimmer->bri_power_on = active_remote_pwm_dimmer->bri = new_bri; + else { +#endif + skip_light_fade = true; + light_state.setBri(new_bri); + LightAnimate(); + skip_light_fade = false; + Settings.bri_power_on = new_bri; +#ifdef USE_PWM_DIMMER_REMOTE + } +#endif + } + else { + PWMDimmerSetBrightnessLeds(0); + } + } + + + else if (power_on_bri) { + power_t new_power; +#ifdef USE_DEVICE_GROUPS +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local) { + active_remote_pwm_dimmer->power ^= 1; + new_power = active_remote_pwm_dimmer->power; + } + else { +#endif + new_power = power ^ 1; +#ifdef USE_PWM_DIMMER_REMOTE + } +#endif + if (new_power) + SendDeviceGroupMessage(power_button_index, DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_BRI, power_on_bri, DGR_ITEM_POWER, new_power); + else + SendDeviceGroupMessage(power_button_index, DGR_MSGTYP_UPDATE, DGR_ITEM_POWER, new_power); +#endif + +#ifdef USE_PWM_DIMMER_REMOTE + if (!active_device_is_local) + active_remote_pwm_dimmer->power_button_increases_bri = (power_on_bri < 128); + else { +#endif + light_state.setBri(power_on_bri); + ExecuteCommandPower(1, POWER_TOGGLE, SRC_RETRY); +#ifdef USE_PWM_DIMMER_REMOTE + } +#endif + } + + + + else if (dgr_item) { +#ifdef USE_DEVICE_GROUPS + if (dgr_item == 255) dgr_item = 0; + SendDeviceGroupMessage(power_button_index, (dgr_more_to_come ? DGR_MSGTYP_UPDATE_MORE_TO_COME : DGR_MSGTYP_UPDATE_DIRECT), dgr_item, dgr_value); +#endif +#ifdef USE_PWM_DIMMER_REMOTE + if (active_device_is_local) { +#endif + light_controller.saveSettings(); + if (state_updated && Settings.flag3.hass_tele_on_power) { + MqttPublishTeleState(); + } +#ifdef USE_PWM_DIMMER_REMOTE + } +#endif + } +} + + + + + +void CmndBriPreset(void) +{ + if (XdrvMailbox.data_len > 0) { + bool valid = true; + uint32_t value; + uint8_t parm[2]; + parm[0] = Settings.bri_preset_low; + parm[1] = Settings.bri_preset_high; + char * ptr = XdrvMailbox.data; + for (uint32_t i = 0; i < 2; i++) { + while (*ptr == ' ') ptr++; + if (*ptr == '+') { + if (parm[i] < 255) parm[i]++; + } + else if (*ptr == '-') { + if (parm[i] > 1) parm[i]--; + } + else { + value = strtoul(ptr, &ptr, 0); + if (value < 1 || value > 255) { + valid = false; + break; + } + parm[i] = value; + if (*ptr != ',') break; + } + ptr++; + } + if (valid && !*ptr) { + if (parm[0] < parm[1]) { + Settings.bri_preset_low = parm[0]; + Settings.bri_preset_high = parm[1]; + } else + { + Settings.bri_preset_low = parm[1]; + Settings.bri_preset_high = parm[0]; + } +#ifdef USE_DEVICE_GROUPS + SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_BRI_PRESET_LOW, Settings.bri_preset_low, DGR_ITEM_BRI_PRESET_HIGH, Settings.bri_preset_high); +#endif + } + } + Response_P(PSTR("{\"" D_CMND_BRI_PRESET "\":{\"Low\":%d,\"High\":%d}}"), Settings.bri_preset_low, Settings.bri_preset_high); +} + + + + + +bool Xdrv35(uint8_t function) +{ + bool result = false; + + if (PWM_DIMMER != my_module_type) return result; + + switch (function) { + case FUNC_EVERY_SECOND: + + if (led_timeout_seconds && !led_timeout_seconds--) { + PWMDimmerSetBrightnessLeds(-1); + } + + + + + + if (global_state.data) + restore_powered_off_led_counter = 5; + else if (restore_powered_off_led_counter) { + PWMDimmerSetPoweredOffLed(); + restore_powered_off_led_counter--; + } + break; + + case FUNC_BUTTON_PRESSED: + PWMDimmerHandleButton(); + result = true; + break; + +#ifdef USE_DEVICE_GROUPS + case FUNC_DEVICE_GROUP_ITEM: + PWMDimmerHandleDevGroupItem(); + break; +#endif + + case FUNC_COMMAND: + result = DecodeCommand(kPWMDimmerCommands, PWMDimmerCommand); + break; + + case FUNC_SET_DEVICE_POWER: + + + if (XdrvMailbox.index) { + PWMDimmerSetPower(); + + + power_button_increases_bri = (light_state.getBri() < 128); + } + + + + else + result = true; + break; + + case FUNC_PRE_INIT: + PWMModulePreInit(); + break; + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_36_keeloq.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_36_keeloq.ino" +#ifdef USE_KEELOQ +# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_36_keeloq.ino" +#define XDRV_36 36 + +#include "cc1101.h" +#include + +#define SYNC_WORD 199 + +#define Lowpulse 400 +#define Highpulse 800 + +const char kJaroliftCommands[] PROGMEM = "Keeloq|" + "SendRaw|SendButton|Set"; + +void (* const jaroliftCommand[])(void) PROGMEM = { + &CmndSendRaw, &CmdSendButton, &CmdSet}; + +CC1101 cc1101; + +struct JAROLIFT_DEVICE { + int device_key_msb = 0x0; + int device_key_lsb = 0x0; + uint64_t button = 0x0; + int disc = 0x0100; + uint32_t enc = 0x0; + uint64_t pack = 0; + int count = 0; + uint32_t serial = 0x0; + uint8_t port_tx; + uint8_t port_rx; +} jaroliftDevice; + +void CmdSet(void) +{ + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload > 0) { + char *p; + uint32_t i = 0; + uint32_t param[4] = { 0 }; + for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 4; str = strtok_r(nullptr, ", ", &p)) { + param[i] = strtoul(str, nullptr, 0); + i++; + } + for (uint32_t i = 0; i < 3; i++) { + if (param[i] < 1) { param[i] = 1; } + } + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("params: %08x %08x %08x %08x"), param[0], param[1], param[2], param[3]); + Settings.keeloq_master_msb = param[0]; + Settings.keeloq_master_lsb = param[1]; + Settings.keeloq_serial = param[2]; + Settings.keeloq_count = param[3]; + + jaroliftDevice.serial = param[2]; + jaroliftDevice.count = param[3]; + + GenerateDeviceCryptKey(); + ResponseCmndDone(); + } else { + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("no payload")); + } + } else { + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("no param")); + } +} + +void GenerateDeviceCryptKey() +{ + Keeloq k(Settings.keeloq_master_msb, Settings.keeloq_master_lsb); + jaroliftDevice.device_key_msb = k.decrypt(jaroliftDevice.serial | 0x60000000L); + jaroliftDevice.device_key_lsb = k.decrypt(jaroliftDevice.serial | 0x20000000L); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("generated device keys: %08x %08x"), jaroliftDevice.device_key_msb, jaroliftDevice.device_key_lsb); +} + +void CmdSendButton(void) +{ + noInterrupts(); + entertx(); + + if (XdrvMailbox.data_len > 0) + { + if (XdrvMailbox.payload > 0) + { + jaroliftDevice.button = strtoul(XdrvMailbox.data, nullptr, 0); + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("msb: %08x"), jaroliftDevice.device_key_msb); + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("lsb: %08x"), jaroliftDevice.device_key_lsb); + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("serial: %08x"), jaroliftDevice.serial); + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("disc: %08x"), jaroliftDevice.disc); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("KLQ: count: %08x"), jaroliftDevice.count); + + CreateKeeloqPacket(); + jaroliftDevice.count++; + Settings.keeloq_count = jaroliftDevice.count; + + for(int repeat = 0; repeat <= 1; repeat++) + { + uint64_t bitsToSend = jaroliftDevice.pack; + digitalWrite(jaroliftDevice.port_tx, LOW); + delayMicroseconds(1150); + SendSyncPreamble(13); + delayMicroseconds(3500); + for(int i=72; i>0; i--) + { + SendBit(bitsToSend & 0x0000000000000001); + bitsToSend >>= 1; + } + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("finished sending bits at %d"), micros()); + + delay(16); + } + } + } + + interrupts(); + enterrx(); + + ResponseCmndDone(); +} + +void SendBit(byte bitToSend) +{ + if (bitToSend==1) + { + digitalWrite(jaroliftDevice.port_tx, LOW); + delayMicroseconds(Lowpulse); + digitalWrite(jaroliftDevice.port_tx, HIGH); + delayMicroseconds(Highpulse); + } + else + { + digitalWrite(jaroliftDevice.port_tx, LOW); + delayMicroseconds(Highpulse); + digitalWrite(jaroliftDevice.port_tx, HIGH); + delayMicroseconds(Lowpulse); + } +} + +void CmndSendRaw(void) +{ + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("cmd send called at %d"), micros()); + noInterrupts(); + entertx(); + for(int repeat = 0; repeat <= 1; repeat++) + { + if (XdrvMailbox.data_len > 0) + { + digitalWrite(jaroliftDevice.port_tx, LOW); + delayMicroseconds(1150); + SendSyncPreamble(13); + delayMicroseconds(3500); + + for(int i=XdrvMailbox.data_len-1; i>=0; i--) + { + SendBit(XdrvMailbox.data[i] == '1'); + } + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("finished sending bits at %d"), micros()); + + delay(16); + } + interrupts(); + } + enterrx(); + ResponseCmndDone(); +} + +void enterrx() { + unsigned char marcState = 0; + cc1101.setRxState(); + delay(2); + unsigned long rx_time = micros(); + while (((marcState = cc1101.readStatusReg(CC1101_MARCSTATE)) & 0x1F) != 0x0D ) + { + if (micros() - rx_time > 50000) break; + } +} + +void entertx() { + unsigned char marcState = 0; + cc1101.setTxState(); + delay(2); + unsigned long rx_time = micros(); + while (((marcState = cc1101.readStatusReg(CC1101_MARCSTATE)) & 0x1F) != 0x13 && 0x14 && 0x15) + { + if (micros() - rx_time > 50000) break; + } +} + +void SendSyncPreamble(int l) +{ + for (int i = 0; i < l; ++i) + { + digitalWrite(jaroliftDevice.port_tx, LOW); + delayMicroseconds(400); + digitalWrite(jaroliftDevice.port_tx, HIGH); + delayMicroseconds(380); + } +} + +void CreateKeeloqPacket() +{ + Keeloq k(jaroliftDevice.device_key_msb, jaroliftDevice.device_key_lsb); + unsigned int result = (jaroliftDevice.disc << 16) | jaroliftDevice.count; + jaroliftDevice.pack = (uint64_t)0; + jaroliftDevice.pack |= jaroliftDevice.serial & 0xfffffffL; + jaroliftDevice.pack |= (jaroliftDevice.button & 0xfL) << 28; + jaroliftDevice.pack <<= 32; + + jaroliftDevice.enc = k.encrypt(result); + jaroliftDevice.pack |= jaroliftDevice.enc; + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("pack high: %08x"), jaroliftDevice.pack>>32); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("pack low: %08x"), jaroliftDevice.pack); +} + +void KeeloqInit() +{ + jaroliftDevice.port_tx = pin[GPIO_CC1101_GDO2]; + jaroliftDevice.port_rx = pin[GPIO_CC1101_GDO0]; + + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("cc1101.init()")); + delay(100); + cc1101.init(); + AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("CC1101 done.")); + cc1101.setSyncWord(SYNC_WORD, false); + cc1101.setCarrierFreq(CFREQ_433); + cc1101.disableAddressCheck(); + + pinMode(jaroliftDevice.port_tx, OUTPUT); + pinMode(jaroliftDevice.port_rx, INPUT_PULLUP); + + jaroliftDevice.serial = Settings.keeloq_serial; + jaroliftDevice.count = Settings.keeloq_count; + GenerateDeviceCryptKey(); +} + + + + +bool Xdrv36(uint8_t function) +{ + if ((99 == pin[GPIO_CC1101_GDO0]) || (99 == pin[GPIO_CC1101_GDO2])) { return false; } + + bool result = false; + + switch (function) { + case FUNC_COMMAND: + AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("calling command")); + result = DecodeCommand(kJaroliftCommands, jaroliftCommand); + break; + case FUNC_INIT: + KeeloqInit(); + DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("init done.")); + break; + } + + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_37_sonoff_d1.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_37_sonoff_d1.ino" +#ifdef USE_SONOFF_D1 +# 34 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_37_sonoff_d1.ino" +#define XDRV_37 37 + +struct SONOFFD1 { + uint8_t receive_len = 0; + uint8_t power = 255; + uint8_t dimmer = 255; +} SnfD1; + + + +void SonoffD1Received(void) +{ + if (serial_in_byte_counter < 8) { return; } + + uint8_t action = serial_in_buffer[6] & 1; + if (action != SnfD1.power) { + SnfD1.power = action; + + + + ExecuteCommandPower(1, action, SRC_SWITCH); + } + + uint8_t dimmer = serial_in_buffer[7]; + if (dimmer != SnfD1.dimmer) { + SnfD1.dimmer = dimmer; + + + + char scmnd[20]; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), SnfD1.dimmer); + ExecuteCommand(scmnd, SRC_SWITCH); + } +# 78 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_37_sonoff_d1.ino" +} + +bool SonoffD1SerialInput(void) +{ + if (0xAA == serial_in_byte) { + serial_in_byte_counter = 0; + SnfD1.receive_len = 7; + } + if (SnfD1.receive_len) { + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + if (6 == serial_in_byte_counter) { + SnfD1.receive_len += serial_in_byte; + } + if (serial_in_byte_counter == SnfD1.receive_len) { +# 101 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_37_sonoff_d1.ino" + AddLogSerial(LOG_LEVEL_DEBUG); + uint8_t crc = 0; + for (uint32_t i = 2; i < SnfD1.receive_len -1; i++) { + crc += serial_in_buffer[i]; + } + if (crc == serial_in_buffer[SnfD1.receive_len -1]) { + SonoffD1Received(); + SnfD1.receive_len = 0; + return true; + } + } + serial_in_byte = 0; + } + return false; +} + + + +void SonoffD1Send() +{ + + uint8_t buffer[17] = { 0xAA,0x55,0x01,0x04,0x00,0x0A,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00 }; + + buffer[6] = SnfD1.power; + buffer[7] = SnfD1.dimmer; + + for (uint32_t i = 0; i < sizeof(buffer); i++) { + if ((i > 1) && (i < sizeof(buffer) -1)) { buffer[16] += buffer[i]; } + Serial.write(buffer[i]); + } +} + +bool SonoffD1SendPower(void) +{ + uint8_t action = XdrvMailbox.index &1; + if (action != SnfD1.power) { + SnfD1.power = action; + + + + SonoffD1Send(); + } + return true; +} + +bool SonoffD1SendDimmer(void) +{ + uint8_t dimmer = LightGetDimmer(1); + dimmer = (dimmer < Settings.dimmer_hw_min) ? Settings.dimmer_hw_min : dimmer; + dimmer = (dimmer > Settings.dimmer_hw_max) ? Settings.dimmer_hw_max : dimmer; + if (dimmer != SnfD1.dimmer) { + SnfD1.dimmer = dimmer; + + + + SonoffD1Send(); + } + return true; +} + +bool SonoffD1ModuleSelected(void) +{ + SetSerial(9600, TS_SERIAL_8N1); + + devices_present++; + light_type = LT_SERIAL1; + + return true; +} + + + + + +bool Xdrv37(uint8_t function) +{ + bool result = false; + + if (SONOFF_D1 == my_module_type) { + switch (function) { + case FUNC_SERIAL: + result = SonoffD1SerialInput(); + break; + case FUNC_SET_DEVICE_POWER: + result = SonoffD1SendPower(); + break; + case FUNC_SET_CHANNELS: + result = SonoffD1SendDimmer(); + break; + case FUNC_MODULE_INIT: + result = SonoffD1ModuleSelected(); + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_38_ping.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_38_ping.ino" +#ifdef USE_PING + +#define XDRV_38 38 + +#include "lwip/icmp.h" +#include "lwip/inet_chksum.h" +#include "lwip/raw.h" +#include "lwip/timeouts.h" + +const char kPingCommands[] PROGMEM = "|" + D_CMND_PING + ; + +void (* const PingCommand[])(void) PROGMEM = { + &CmndPing, + }; + +extern "C" { + + extern uint32 system_relative_time(uint32 time); + extern void ets_bzero(void *s, size_t n); + + const uint16_t Ping_ID = 0xAFAF; + const size_t Ping_data_size = 32; + const uint32_t Ping_timeout_ms = 1000; + const uint32_t Ping_coarse = 1000; + + typedef struct Ping_t { + uint32 ip; + Ping_t *next; + uint16_t seq_num; + uint16_t seqno; + uint8_t success_count; + uint8_t timeout_count; + uint8_t to_send_count; + uint32_t ping_time_sent; + uint32_t min_time; + uint32_t max_time; + uint32_t sum_time; + bool done; + bool fast; + } Ping_t; + + + Ping_t *ping_head = nullptr; + struct raw_pcb *t_ping_pcb = nullptr; + + + + + + + + Ping_t ICACHE_FLASH_ATTR * t_ping_find(uint32_t ip) { + Ping_t *ping = ping_head; + while (ping != nullptr) { + if (ping->ip == ip) { + return ping; + } + ping = ping->next; + } + return nullptr; + } +# 91 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_38_ping.ino" + void ICACHE_FLASH_ATTR t_ping_timeout(void* arg) { + Ping_t *ping = (Ping_t*) arg; + ping->timeout_count++; + } + + + + + + + void ICACHE_FLASH_ATTR t_ping_prepare_echo(struct icmp_echo_hdr *iecho, uint16_t len, Ping_t *ping) { + size_t data_len = len - sizeof(struct icmp_echo_hdr); + + ICMPH_TYPE_SET(iecho, ICMP_ECHO); + ICMPH_CODE_SET(iecho, 0); + iecho->chksum = 0; + iecho->id = Ping_ID; + ping->seq_num++; + if (ping->seq_num == 0x7fff) { ping->seq_num = 0; } + + iecho->seqno = htons(ping->seq_num); + + + for (uint32_t i = 0; i < data_len; i++) { + ((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i; + } + + iecho->chksum = inet_chksum(iecho, len); + } + + + + void ICACHE_FLASH_ATTR t_ping_send(struct raw_pcb *raw, Ping_t *ping) { + struct pbuf *p; + uint16_t ping_size = sizeof(struct icmp_echo_hdr) + Ping_data_size; + + ping->ping_time_sent = system_get_time(); + p = pbuf_alloc(PBUF_IP, ping_size, PBUF_RAM); + if (!p) { return; } + if ((p->len == p->tot_len) && (p->next == nullptr)) { + ip_addr_t ping_target; + struct icmp_echo_hdr *iecho; + + ping_target.addr = ping->ip; + iecho = (struct icmp_echo_hdr *) p->payload; + + t_ping_prepare_echo(iecho, ping_size, ping); + raw_sendto(raw, p, &ping_target); + } + pbuf_free(p); + } + + + + + + static void ICACHE_FLASH_ATTR t_ping_coarse_tmr(void *arg) { + Ping_t *ping = (Ping_t*) arg; + if (ping->to_send_count > 0) { + ping->to_send_count--; + + t_ping_send(t_ping_pcb, ping); + + sys_timeout(Ping_timeout_ms, t_ping_timeout, ping); + sys_timeout(Ping_coarse, t_ping_coarse_tmr, ping); + } else { + sys_untimeout(t_ping_coarse_tmr, ping); + ping->done = true; + } + } + + + + + + + + static uint8_t ICACHE_FLASH_ATTR t_ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *addr) { + Ping_t *ping = t_ping_find(addr->addr); + + if (nullptr == ping) { + return 0; + } + + if (pbuf_header( p, -PBUF_IP_HLEN)==0) { + struct icmp_echo_hdr *iecho; + iecho = (struct icmp_echo_hdr *)p->payload; + + if ((iecho->id == Ping_ID) && (iecho->seqno == htons(ping->seq_num)) && iecho->type == ICMP_ER) { + + if (iecho->seqno != ping->seqno){ + + sys_untimeout(t_ping_timeout, ping); + uint32_t delay = system_relative_time(ping->ping_time_sent); + delay /= 1000; + + ping->sum_time += delay; + if (delay < ping->min_time) { ping->min_time = delay; } + if (delay > ping->max_time) { ping->max_time = delay; } + + ping->success_count++; + ping->seqno = iecho->seqno; + if (ping->fast) { + sys_untimeout(t_ping_coarse_tmr, ping); + ping->done = true; + ping->to_send_count = 0; + } + } + + pbuf_free(p); + return 1; + } + } + + return 0; + } + + + + + + void t_ping_register_pcb(void) { + if (nullptr == t_ping_pcb) { + t_ping_pcb = raw_new(IP_PROTO_ICMP); + + raw_recv(t_ping_pcb, t_ping_recv, nullptr); + raw_bind(t_ping_pcb, IP_ADDR_ANY); + } + } + + + void t_ping_deregister_pcb(void) { + if (nullptr == ping_head) { + raw_remove(t_ping_pcb); + t_ping_pcb = nullptr; + } + } + + + + + bool t_ping_start(uint32_t ip, uint32_t count) { + + if (t_ping_find(ip)) { + return false; + } + + Ping_t *ping = new Ping_t(); + if (0 == count) { + count = 4; + ping->fast = true; + } + ping->min_time = UINT32_MAX; + ping->ip = ip; + ping->to_send_count = count - 1; + + + ping->next = ping_head; + ping_head = ping; + + t_ping_register_pcb(); + t_ping_send(t_ping_pcb, ping); + + + sys_timeout(Ping_timeout_ms, t_ping_timeout, ping); + sys_timeout(Ping_coarse, t_ping_coarse_tmr, ping); + } + +} + + +void PingResponsePoll(void) { + Ping_t *ping = ping_head; + Ping_t **prev_link = &ping_head; + + while (ping != nullptr) { + if (ping->done) { + uint32_t success = ping->success_count; + uint32_t ip = ping->ip; + + Response_P(PSTR("{\"" D_JSON_PING "\":{\"%d.%d.%d.%d\":{" + "\"Reachable\":%s" + ",\"Success\":%d" + ",\"Timeout\":%d" + ",\"MinTime\":%d" + ",\"MaxTime\":%d" + ",\"AvgTime\":%d" + "}}}"), + ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, ip >> 24, + success ? "true" : "false", + success, ping->timeout_count, + success ? ping->min_time : 0, ping->max_time, + success ? ping->sum_time / success : 0 + ); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_PING)); + XdrvRulesProcess(); + + + *prev_link = ping->next; + + Ping_t *ping_to_delete = ping; + ping = ping->next; + delete ping_to_delete; + } else { + prev_link = &ping->next; + ping = ping->next; + } + } +} + + + + + +void CmndPing(void) { + uint32_t count = XdrvMailbox.index; + IPAddress ip; + + RemoveSpace(XdrvMailbox.data); + if (count > 10) { count = 8; } + + if (WiFi.hostByName(XdrvMailbox.data, ip)) { + bool ok = t_ping_start(ip, count); + if (ok) { + ResponseCmndDone(); + } else { + ResponseCmndChar_P(PSTR("Ping already ongoing for this IP")); + } + } else { + ResponseCmndChar_P(PSTR("Unable to resolve IP address")); + } +} + + + + + + +bool Xdrv38(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + PingResponsePoll(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kPingCommands, PingCommand); + break; + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_39_thermostat.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_39_thermostat.ino" +#ifdef USE_THERMOSTAT + +#define XDRV_39 39 + + +#define DEBUG_THERMOSTAT + +#ifdef DEBUG_THERMOSTAT +#define DOMOTICZ_IDX1 791 +#define DOMOTICZ_IDX2 792 +#define DOMOTICZ_IDX3 793 +#endif + + +#define D_CMND_THERMOSTATMODESET "ThermostatModeSet" +#define D_CMND_TEMPFROSTPROTECTSET "TempFrostProtectSet" +#define D_CMND_CONTROLLERMODESET "ControllerModeSet" +#define D_CMND_INPUTSWITCHSET "InputSwitchSet" +#define D_CMND_OUTPUTRELAYSET "OutputRelaySet" +#define D_CMND_TIMEALLOWRAMPUPSET "TimeAllowRampupSet" +#define D_CMND_TEMPMEASUREDSET "TempMeasuredSet" +#define D_CMND_TEMPTARGETSET "TempTargetSet" +#define D_CMND_TEMPTARGETREAD "TempTargetRead" +#define D_CMND_TEMPMEASUREDREAD "TempMeasuredRead" +#define D_CMND_TEMPMEASUREDGRDREAD "TempMeasuredGrdRead" +#define D_CMND_TEMPSENSNUMBERSET "TempSensNumberSet" +#define D_CMND_STATEEMERGENCYSET "StateEmergencySet" +#define D_CMND_POWERMAXSET "PowerMaxSet" +#define D_CMND_TIMEMANUALTOAUTOSET "TimeManualToAutoSet" +#define D_CMND_TIMEONLIMITSET "TimeOnLimitSet" +#define D_CMND_PROPBANDSET "PropBandSet" +#define D_CMND_TIMERESETSET "TimeResetSet" +#define D_CMND_TIMEPICYCLESET "TimePiCycleSet" +#define D_CMND_TEMPANTIWINDUPRESETSET "TempAntiWindupResetSet" +#define D_CMND_TEMPHYSTSET "TempHystSet" +#define D_CMND_TIMEMAXACTIONSET "TimeMaxActionSet" +#define D_CMND_TIMEMINACTIONSET "TimeMinActionSet" +#define D_CMND_TIMEMINTURNOFFACTIONSET "TimeMinTurnoffActionSet" +#define D_CMND_TEMPRUPDELTINSET "TempRupDeltInSet" +#define D_CMND_TEMPRUPDELTOUTSET "TempRupDeltOutSet" +#define D_CMND_TIMERAMPUPMAXSET "TimeRampupMaxSet" +#define D_CMND_TIMERAMPUPCYCLESET "TimeRampupCycleSet" +#define D_CMND_TEMPRAMPUPPIACCERRSET "TempRampupPiAccErrSet" +#define D_CMND_TIMEPIPROPORTREAD "TimePiProportRead" +#define D_CMND_TIMEPIINTEGRREAD "TimePiIntegrRead" +#define D_CMND_TIMESENSLOSTSET "TimeSensLostSet" + +enum ThermostatModes { THERMOSTAT_OFF, THERMOSTAT_AUTOMATIC_OP, THERMOSTAT_MANUAL_OP, THERMOSTAT_MODES_MAX }; +enum ControllerModes { CTR_HYBRID, CTR_PI, CTR_RAMP_UP, CTR_MODES_MAX }; +enum ControllerHybridPhases { CTR_HYBRID_RAMP_UP, CTR_HYBRID_PI }; +enum InterfaceStates { IFACE_OFF, IFACE_ON }; +enum CtrCycleStates { CYCLE_OFF, CYCLE_ON }; +enum EmergencyStates { EMERGENCY_OFF, EMERGENCY_ON }; +enum ThermostatSupportedInputSwitches { + THERMOSTAT_INPUT_NONE, + THERMOSTAT_INPUT_SWT1 = 1, + THERMOSTAT_INPUT_SWT2, + THERMOSTAT_INPUT_SWT3, + THERMOSTAT_INPUT_SWT4 +}; +enum ThermostatSupportedOutputRelays { + THERMOSTAT_OUTPUT_NONE, + THERMOSTAT_OUTPUT_REL1 = 1, + THERMOSTAT_OUTPUT_REL2, + THERMOSTAT_OUTPUT_REL3, + THERMOSTAT_OUTPUT_REL4, + THERMOSTAT_OUTPUT_REL5, + THERMOSTAT_OUTPUT_REL6, + THERMOSTAT_OUTPUT_REL7, + THERMOSTAT_OUTPUT_REL8 +}; + +typedef union { + uint16_t data; + struct { + uint16_t thermostat_mode : 2; + uint16_t controller_mode : 2; + uint16_t sensor_alive : 1; + uint16_t command_output : 1; + uint16_t phase_hybrid_ctr : 1; + uint16_t status_output : 1; + uint16_t status_cycle_active : 1; + uint16_t state_emergency : 1; + uint16_t counter_seconds : 6; + }; +} ThermostatBitfield; + +#ifdef DEBUG_THERMOSTAT +const char DOMOTICZ_MES[] PROGMEM = "{\"idx\":%d,\"nvalue\":%d,\"svalue\":\"%s\"}"; +#endif + +const char kThermostatCommands[] PROGMEM = "|" D_CMND_THERMOSTATMODESET "|" D_CMND_TEMPFROSTPROTECTSET "|" + D_CMND_CONTROLLERMODESET "|" D_CMND_INPUTSWITCHSET "|" D_CMND_OUTPUTRELAYSET "|" D_CMND_TIMEALLOWRAMPUPSET "|" + D_CMND_TEMPMEASUREDSET "|" D_CMND_TEMPTARGETSET "|" D_CMND_TEMPTARGETREAD "|" + D_CMND_TEMPMEASUREDREAD "|" D_CMND_TEMPMEASUREDGRDREAD "|" D_CMND_TEMPSENSNUMBERSET "|" + D_CMND_STATEEMERGENCYSET "|" D_CMND_POWERMAXSET "|" D_CMND_TIMEMANUALTOAUTOSET "|" D_CMND_TIMEONLIMITSET "|" + D_CMND_PROPBANDSET "|" D_CMND_TIMERESETSET "|" D_CMND_TIMEPICYCLESET "|" D_CMND_TEMPANTIWINDUPRESETSET "|" + D_CMND_TEMPHYSTSET "|" D_CMND_TIMEMAXACTIONSET "|" D_CMND_TIMEMINACTIONSET "|" D_CMND_TIMEMINTURNOFFACTIONSET "|" + D_CMND_TEMPRUPDELTINSET "|" D_CMND_TEMPRUPDELTOUTSET "|" D_CMND_TIMERAMPUPMAXSET "|" D_CMND_TIMERAMPUPCYCLESET "|" + D_CMND_TEMPRAMPUPPIACCERRSET "|" D_CMND_TIMEPIPROPORTREAD "|" D_CMND_TIMEPIINTEGRREAD "|" D_CMND_TIMESENSLOSTSET; + +void (* const ThermostatCommand[])(void) PROGMEM = { + &CmndThermostatModeSet, &CmndTempFrostProtectSet, &CmndControllerModeSet, &CmndInputSwitchSet, &CmndOutputRelaySet, + &CmndTimeAllowRampupSet, &CmndTempMeasuredSet, &CmndTempTargetSet, &CmndTempTargetRead, + &CmndTempMeasuredRead, &CmndTempMeasuredGrdRead, &CmndTempSensNumberSet, &CmndStateEmergencySet, + &CmndPowerMaxSet, &CmndTimeManualToAutoSet, &CmndTimeOnLimitSet, &CmndPropBandSet, &CmndTimeResetSet, + &CmndTimePiCycleSet, &CmndTempAntiWindupResetSet, &CmndTempHystSet, &CmndTimeMaxActionSet, + &CmndTimeMinActionSet, &CmndTimeMinTurnoffActionSet, &CmndTempRupDeltInSet, &CmndTempRupDeltOutSet, + &CmndTimeRampupMaxSet, &CmndTimeRampupCycleSet, &CmndTempRampupPiAccErrSet, &CmndTimePiProportRead, + &CmndTimePiIntegrRead, &CmndTimeSensLostSet }; + +struct THERMOSTAT { + uint32_t timestamp_temp_measured_update = 0; + uint32_t timestamp_temp_meas_change_update = 0; + uint32_t timestamp_output_off = 0; + uint32_t timestamp_input_on = 0; + uint32_t time_thermostat_total = 0; + uint32_t time_ctr_checkpoint = 0; + uint32_t time_ctr_changepoint = 0; + int32_t temp_measured_gradient = 0; + int16_t temp_target_level = THERMOSTAT_TEMP_INIT; + int16_t temp_target_level_ctr = THERMOSTAT_TEMP_INIT; + int16_t temp_pi_accum_error = 0; + int16_t temp_pi_error = 0; + int32_t time_proportional_pi; + int32_t time_integral_pi; + int32_t time_total_pi; + uint16_t kP_pi = 0; + uint16_t kI_pi = 0; + int32_t temp_rampup_meas_gradient = 0; + uint32_t timestamp_rampup_start = 0; + uint32_t time_rampup_deadtime = 0; + uint32_t time_rampup_nextcycle = 0; + int16_t temp_measured = 0; + uint8_t time_output_delay = THERMOSTAT_TIME_OUTPUT_DELAY; + uint8_t counter_rampup_cycles = 0; + uint8_t output_relay_number = THERMOSTAT_RELAY_NUMBER; + uint8_t input_switch_number = THERMOSTAT_SWITCH_NUMBER; + uint8_t temp_sens_number = THERMOSTAT_TEMP_SENS_NUMBER; + uint8_t temp_rampup_pi_acc_error = THERMOSTAT_TEMP_PI_RAMPUP_ACC_E; + uint8_t temp_rampup_delta_out = THERMOSTAT_TEMP_RAMPUP_DELTA_OUT; + uint8_t temp_rampup_delta_in = THERMOSTAT_TEMP_RAMPUP_DELTA_IN; + int16_t temp_rampup_output_off = 0; + int16_t temp_rampup_start = 0; + int16_t temp_rampup_cycle = 0; + uint16_t time_rampup_max = THERMOSTAT_TIME_RAMPUP_MAX; + uint16_t time_rampup_cycle = THERMOSTAT_TIME_RAMPUP_CYCLE; + uint16_t time_allow_rampup = THERMOSTAT_TIME_ALLOW_RAMPUP; + uint16_t time_sens_lost = THERMOSTAT_TIME_SENS_LOST; + uint16_t time_manual_to_auto = THERMOSTAT_TIME_MANUAL_TO_AUTO; + uint16_t time_on_limit = THERMOSTAT_TIME_ON_LIMIT; + uint32_t time_reset = THERMOSTAT_TIME_RESET; + uint16_t time_pi_cycle = THERMOSTAT_TIME_PI_CYCLE; + uint16_t time_max_action = THERMOSTAT_TIME_MAX_ACTION; + uint16_t time_min_action = THERMOSTAT_TIME_MIN_ACTION; + uint16_t time_min_turnoff_action = THERMOSTAT_TIME_MIN_TURNOFF_ACTION; + uint8_t val_prop_band = THERMOSTAT_PROP_BAND; + uint8_t temp_reset_anti_windup = THERMOSTAT_TEMP_RESET_ANTI_WINDUP; + int8_t temp_hysteresis = THERMOSTAT_TEMP_HYSTERESIS; + uint8_t temp_frost_protect = THERMOSTAT_TEMP_FROST_PROTECT; + uint16_t power_max = THERMOSTAT_POWER_MAX; + ThermostatBitfield status; +} Thermostat; + + + +void ThermostatInit(void) +{ + ExecuteCommandPower(Thermostat.output_relay_number, POWER_OFF, SRC_THERMOSTAT); + + Thermostat.status.thermostat_mode = THERMOSTAT_OFF; + Thermostat.status.controller_mode = CTR_HYBRID; + Thermostat.status.sensor_alive = IFACE_OFF; + Thermostat.status.command_output = IFACE_OFF; + Thermostat.status.phase_hybrid_ctr = CTR_HYBRID_PI; + Thermostat.status.status_output = IFACE_OFF; + Thermostat.status.status_cycle_active = CYCLE_OFF; + Thermostat.status.state_emergency = EMERGENCY_OFF; + Thermostat.status.counter_seconds = 0; +} + +bool ThermostatMinuteCounter(void) +{ + bool result = false; + Thermostat.status.counter_seconds++; + + if ((Thermostat.status.counter_seconds % 60) == 0) { + result = true; + Thermostat.status.counter_seconds = 0; + } + return result; +} + +inline bool ThermostatSwitchIdValid(uint8_t switchId) +{ + return (switchId >= THERMOSTAT_INPUT_SWT1 && switchId <= THERMOSTAT_INPUT_SWT4); +} + +inline bool ThermostatRelayIdValid(uint8_t relayId) +{ + return (relayId >= THERMOSTAT_OUTPUT_REL1 && relayId <= THERMOSTAT_OUTPUT_REL8); +} + +uint8_t ThermostatSwitchStatus(uint8_t input_switch) +{ + bool ifId = ThermostatSwitchIdValid(input_switch); + if(ifId) { + return(SwitchGetVirtual(ifId - THERMOSTAT_INPUT_SWT1)); + } + else return 255; +} + +void ThermostatSignalProcessingSlow(void) +{ + if ((uptime - Thermostat.timestamp_temp_measured_update) > ((uint32_t)Thermostat.time_sens_lost * 60)) { + Thermostat.status.sensor_alive = IFACE_OFF; + Thermostat.temp_measured_gradient = 0; + Thermostat.temp_measured = 0; + } +} + +void ThermostatSignalProcessingFast(void) +{ + if (ThermostatSwitchStatus(Thermostat.input_switch_number)) { + Thermostat.timestamp_input_on = uptime; + } +} + +void ThermostatCtrState(void) +{ + switch (Thermostat.status.controller_mode) { + case CTR_HYBRID: + ThermostatHybridCtrPhase(); + break; + case CTR_PI: + break; + case CTR_RAMP_UP: + break; + } +} + +void ThermostatHybridCtrPhase(void) +{ + if (Thermostat.status.controller_mode == CTR_HYBRID) { + switch (Thermostat.status.phase_hybrid_ctr) { + case CTR_HYBRID_RAMP_UP: + + + if((Thermostat.time_ctr_checkpoint != 0) + && (uptime >= Thermostat.time_ctr_checkpoint)) { + + Thermostat.time_ctr_checkpoint = 0; + + Thermostat.time_ctr_changepoint = 0; + + Thermostat.status.phase_hybrid_ctr = CTR_HYBRID_PI; + } + break; + case CTR_HYBRID_PI: + + + + + if (((uptime - Thermostat.timestamp_output_off) > (60 * (uint32_t)Thermostat.time_allow_rampup)) + && (Thermostat.temp_target_level != Thermostat.temp_target_level_ctr) + &&((Thermostat.temp_target_level - Thermostat.temp_measured) > Thermostat.temp_rampup_delta_in)) { + Thermostat.timestamp_rampup_start = uptime; + Thermostat.temp_rampup_start = Thermostat.temp_measured; + Thermostat.temp_rampup_meas_gradient = 0; + Thermostat.time_rampup_deadtime = 0; + Thermostat.counter_rampup_cycles = 1; + Thermostat.time_ctr_changepoint = 0; + Thermostat.time_ctr_checkpoint = 0; + Thermostat.status.phase_hybrid_ctr = CTR_HYBRID_RAMP_UP; + } + break; + } + } +#ifdef DEBUG_THERMOSTAT + ThermostatVirtualSwitchCtrState(); +#endif +} + +bool HeatStateAutoToManual(void) +{ + bool change_state = false; + + + + + if ((ThermostatSwitchStatus(Thermostat.input_switch_number) == 1) + || (Thermostat.status.sensor_alive == IFACE_OFF)) { + change_state = true; + } + return change_state; +} + +bool HeatStateManualToAuto(void) +{ + bool change_state; + + + + + + if ((ThermostatSwitchStatus(Thermostat.input_switch_number) == 0) + &&(Thermostat.status.sensor_alive == IFACE_ON) + && ((uptime - Thermostat.timestamp_input_on) > ((uint32_t)Thermostat.time_manual_to_auto * 60))) { + change_state = true; + } + return change_state; +} + +bool HeatStateAllToOff(void) +{ + bool change_state; + + + if (Thermostat.status.state_emergency == EMERGENCY_ON) { + Thermostat.status.thermostat_mode = THERMOSTAT_OFF; + } + return change_state; +} + +void ThermostatState(void) +{ + switch (Thermostat.status.thermostat_mode) { + case THERMOSTAT_OFF: + + break; + case THERMOSTAT_AUTOMATIC_OP: + if (HeatStateAllToOff()) { + Thermostat.status.thermostat_mode = THERMOSTAT_OFF; + } + if (HeatStateAutoToManual()) { + Thermostat.status.thermostat_mode = THERMOSTAT_MANUAL_OP; + } + ThermostatCtrState(); + break; + case THERMOSTAT_MANUAL_OP: + if (HeatStateAllToOff()) { + Thermostat.status.thermostat_mode = THERMOSTAT_OFF; + } + if (HeatStateManualToAuto()) { + Thermostat.status.thermostat_mode = THERMOSTAT_AUTOMATIC_OP; + } + break; + } +} + +void ThermostatOutputRelay(bool active) +{ + + + + + if ((active == true) + && (Thermostat.status.status_output == IFACE_OFF)) { + ExecuteCommandPower(Thermostat.output_relay_number, POWER_ON, SRC_THERMOSTAT); + Thermostat.status.status_output = IFACE_ON; +#ifdef DEBUG_THERMOSTAT + ThermostatVirtualSwitch(); +#endif + } + + + + else if ((active == false) && (Thermostat.status.status_output == IFACE_ON)) { + ExecuteCommandPower(Thermostat.output_relay_number, POWER_OFF, SRC_THERMOSTAT); + Thermostat.timestamp_output_off = uptime; + Thermostat.status.status_output = IFACE_OFF; +#ifdef DEBUG_THERMOSTAT + ThermostatVirtualSwitch(); +#endif + } +} + +void ThermostatCalculatePI(void) +{ + int32_t aux_time_error; + + + aux_time_error = (int32_t)(Thermostat.temp_target_level_ctr - Thermostat.temp_measured) * 10; + + + if (aux_time_error <= (int32_t)(INT16_MIN)) { + Thermostat.temp_pi_error = (int16_t)(INT16_MIN); + } + else if (aux_time_error >= (int32_t)INT16_MAX) { + Thermostat.temp_pi_error = (int16_t)INT16_MAX; + } + else { + Thermostat.temp_pi_error = (int16_t)aux_time_error; + } + + + Thermostat.kP_pi = 100 / (uint16_t)(Thermostat.val_prop_band); + + Thermostat.time_proportional_pi = ((int32_t)(Thermostat.temp_pi_error * (int16_t)Thermostat.kP_pi) * ((int32_t)Thermostat.time_pi_cycle * 60)) / 10000; + + + + + + if ((Thermostat.time_proportional_pi < abs(((int32_t)Thermostat.time_min_action * 60))) + && (Thermostat.time_proportional_pi > 0)) { + Thermostat.time_proportional_pi = ((int32_t)Thermostat.time_min_action * 60); + } + + if (Thermostat.time_proportional_pi < 0) { + Thermostat.time_proportional_pi = 0; + } + else if (Thermostat.time_proportional_pi > ((int32_t)Thermostat.time_pi_cycle * 60)) { + Thermostat.time_proportional_pi = ((int32_t)Thermostat.time_pi_cycle * 60); + } + + + + Thermostat.kI_pi = (uint16_t)((((uint32_t)Thermostat.kP_pi * (uint32_t)Thermostat.time_pi_cycle * 6000)) / (uint32_t)Thermostat.time_reset); + + + + + if (abs((Thermostat.temp_pi_error) / 10) > Thermostat.temp_reset_anti_windup) { + Thermostat.time_integral_pi = 0; + Thermostat.temp_pi_accum_error = 0; + } + + + + else { +# 459 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_39_thermostat.ino" + aux_time_error = (int32_t)Thermostat.temp_pi_accum_error + (int32_t)Thermostat.temp_pi_error; + + + if (aux_time_error <= (int32_t)INT16_MIN) { + Thermostat.temp_pi_accum_error = INT16_MIN; + } + else if (aux_time_error >= (int32_t)INT16_MAX) { + Thermostat.temp_pi_accum_error = INT16_MAX; + } + else { + Thermostat.temp_pi_accum_error = (int16_t)aux_time_error; + } + + + + + if ((Thermostat.temp_pi_error >= 0) + && (abs((Thermostat.temp_pi_error) / 10) <= (int16_t)Thermostat.temp_hysteresis) + && (Thermostat.temp_measured_gradient > 0)) { + + Thermostat.temp_pi_accum_error *= 0.8; + } + + + else if ((Thermostat.temp_pi_error < 0) + && (Thermostat.temp_measured_gradient > 0)) { + + Thermostat.temp_pi_accum_error *= 0.8; + } + + + if (Thermostat.temp_pi_accum_error < 0) { + Thermostat.temp_pi_accum_error = 0; + } + + + Thermostat.time_integral_pi = (((int32_t)Thermostat.temp_pi_accum_error * (int32_t)Thermostat.kI_pi) * (int32_t)((uint32_t)Thermostat.time_pi_cycle * 60)) / 1000000; + + + + + if (Thermostat.time_integral_pi > ((uint32_t)Thermostat.time_pi_cycle * 60)) { + Thermostat.time_integral_pi = ((uint32_t)Thermostat.time_pi_cycle * 60); + } + } + + + Thermostat.time_total_pi = Thermostat.time_proportional_pi + Thermostat.time_integral_pi; + + + + + if (Thermostat.time_total_pi >= ((int32_t)Thermostat.time_pi_cycle * 60)) { + + Thermostat.time_total_pi = ((int32_t)Thermostat.time_pi_cycle * 60); + } + else if (Thermostat.time_total_pi < 0) { + Thermostat.time_total_pi = 0; + } + + + + if (Thermostat.temp_pi_error <= 0) { + + if ((abs((Thermostat.temp_pi_error) / 10) > Thermostat.temp_hysteresis) + || (Thermostat.temp_measured_gradient >= 0)) { + Thermostat.time_total_pi = 0; + } + } + + + + + else if ((Thermostat.temp_pi_error > 0) + && (abs((Thermostat.temp_pi_error) / 10) <= Thermostat.temp_hysteresis) + && (Thermostat.temp_measured_gradient > 0)) { + Thermostat.time_total_pi = 0; + } + + + + if ((Thermostat.time_total_pi <= abs(((uint32_t)Thermostat.time_min_action * 60))) + && (Thermostat.time_total_pi != 0)) { + Thermostat.time_total_pi = ((int32_t)Thermostat.time_min_action * 60); + } + + + else if (Thermostat.time_total_pi > abs(((int32_t)Thermostat.time_max_action * 60))) { + Thermostat.time_total_pi = ((int32_t)Thermostat.time_max_action * 60); + } + + else if (Thermostat.time_total_pi > (((int32_t)Thermostat.time_pi_cycle * 60) - ((int32_t)Thermostat.time_min_turnoff_action * 60))) { + Thermostat.time_total_pi = ((int32_t)Thermostat.time_pi_cycle * 60); + } + + + Thermostat.time_ctr_changepoint = uptime + (uint32_t)Thermostat.time_total_pi; + + Thermostat.time_ctr_checkpoint = uptime + ((uint32_t)Thermostat.time_pi_cycle * 60); +} + +void ThermostatWorkAutomaticPI(void) +{ + char result_chr[FLOATSZ]; + + if ((uptime >= Thermostat.time_ctr_checkpoint) + || (Thermostat.temp_target_level != Thermostat.temp_target_level_ctr) + || ((Thermostat.temp_measured < Thermostat.temp_target_level) + && (Thermostat.temp_measured_gradient < 0) + && (Thermostat.status.status_cycle_active == CYCLE_OFF))) { + Thermostat.temp_target_level_ctr = Thermostat.temp_target_level; + ThermostatCalculatePI(); + + Thermostat.status.status_cycle_active = CYCLE_OFF; + } + if (uptime < Thermostat.time_ctr_changepoint) { + Thermostat.status.status_cycle_active = CYCLE_ON; + Thermostat.status.command_output = IFACE_ON; + } + else { + Thermostat.status.command_output = IFACE_OFF; + } +} + +void ThermostatWorkAutomaticRampUp(void) +{ + int32_t aux_temp_delta; + uint32_t time_in_rampup; + int16_t temp_delta_rampup; + + + if (Thermostat.temp_measured < Thermostat.temp_rampup_start) { + Thermostat.temp_rampup_start = Thermostat.temp_measured; + } + + + time_in_rampup = uptime - Thermostat.timestamp_rampup_start; + temp_delta_rampup = Thermostat.temp_measured - Thermostat.temp_rampup_start; + + Thermostat.status.command_output = IFACE_ON; + + Thermostat.temp_target_level_ctr = Thermostat.temp_target_level; + + + + if ((time_in_rampup <= (60 * (uint32_t)Thermostat.time_rampup_max)) + && (Thermostat.temp_measured < Thermostat.temp_target_level)) { + + + + if ((temp_delta_rampup >= Thermostat.temp_rampup_delta_out) + && (Thermostat.time_rampup_deadtime == 0)) { + + + int32_t time_aux; + time_aux = ((time_in_rampup / 2) - Thermostat.time_output_delay); + if (time_aux >= Thermostat.time_output_delay) { + Thermostat.time_rampup_deadtime = (uint32_t)time_aux; + } + else { + Thermostat.time_rampup_deadtime = Thermostat.time_output_delay; + } + + Thermostat.temp_rampup_meas_gradient = (int32_t)((360000 * (int32_t)temp_delta_rampup) / (int32_t)time_in_rampup); + Thermostat.time_rampup_nextcycle = uptime + (uint32_t)Thermostat.time_rampup_cycle; + + Thermostat.temp_rampup_cycle = Thermostat.temp_measured; + Thermostat.time_ctr_changepoint = uptime + (60 * (uint32_t)Thermostat.time_rampup_max); + Thermostat.temp_rampup_output_off = Thermostat.temp_target_level_ctr; + } + + else if ((Thermostat.time_rampup_deadtime > 0) && (uptime >= Thermostat.time_rampup_nextcycle)) { + + + temp_delta_rampup = Thermostat.temp_measured - Thermostat.temp_rampup_cycle; + uint32_t time_total_rampup = (uint32_t)Thermostat.time_rampup_cycle * Thermostat.counter_rampup_cycles; + + Thermostat.temp_rampup_meas_gradient = int32_t((360000 * (int32_t)temp_delta_rampup) / (int32_t)time_total_rampup); + if (Thermostat.temp_rampup_meas_gradient > 0) { + + + + + + + + aux_temp_delta = (int32_t)(Thermostat.temp_target_level_ctr - Thermostat.temp_rampup_cycle); + + + if ((aux_temp_delta < 0) + ||(temp_delta_rampup <= 0)) { + Thermostat.time_ctr_changepoint = uptime + (uint32_t)(60 * Thermostat.time_rampup_max); + } + else { + Thermostat.time_ctr_changepoint = (uint32_t)(uint32_t)(((uint32_t)(aux_temp_delta) * (uint32_t)(time_total_rampup)) / (uint32_t)temp_delta_rampup) + (uint32_t)Thermostat.time_rampup_nextcycle - (uint32_t)time_total_rampup - (uint32_t)Thermostat.time_rampup_deadtime; + } + + + + + Thermostat.temp_rampup_output_off = (int16_t)(((float)temp_delta_rampup * (float)(Thermostat.time_ctr_changepoint - (uptime - (time_total_rampup)))) / (float)(time_total_rampup * Thermostat.counter_rampup_cycles)) + Thermostat.temp_rampup_cycle; + + Thermostat.time_rampup_nextcycle = uptime + (uint32_t)Thermostat.time_rampup_cycle; + Thermostat.temp_rampup_cycle = Thermostat.temp_measured; + + Thermostat.counter_rampup_cycles = 1; + } + else { + + Thermostat.counter_rampup_cycles++; + + Thermostat.time_rampup_nextcycle = uptime + (uint32_t)Thermostat.time_rampup_cycle; + + Thermostat.time_ctr_changepoint = uptime + (60 * (uint32_t)Thermostat.time_rampup_max) - time_in_rampup; + Thermostat.temp_rampup_output_off = Thermostat.temp_target_level_ctr; + } + + Thermostat.time_ctr_checkpoint = Thermostat.time_ctr_changepoint + Thermostat.time_rampup_deadtime; + } + + + + + + + if ((Thermostat.time_rampup_deadtime == 0) + || (Thermostat.time_ctr_checkpoint == 0) + || (uptime < Thermostat.time_ctr_changepoint) + || (Thermostat.temp_measured < Thermostat.temp_rampup_output_off) + || (Thermostat.temp_rampup_meas_gradient <= 0)) { + Thermostat.status.command_output = IFACE_ON; + } + else { + Thermostat.status.command_output = IFACE_OFF; + } + } + else { + + if (Thermostat.temp_measured < Thermostat.temp_target_level_ctr) { + Thermostat.temp_pi_accum_error = Thermostat.temp_rampup_pi_acc_error; + } + + Thermostat.time_ctr_checkpoint = uptime; + + Thermostat.status.command_output = IFACE_OFF; + } +} + +void ThermostatCtrWork(void) +{ + switch (Thermostat.status.controller_mode) { + case CTR_HYBRID: + switch (Thermostat.status.phase_hybrid_ctr) { + case CTR_HYBRID_RAMP_UP: + ThermostatWorkAutomaticRampUp(); + break; + case CTR_HYBRID_PI: + ThermostatWorkAutomaticPI(); + break; + } + break; + case CTR_PI: + ThermostatWorkAutomaticPI(); + break; + case CTR_RAMP_UP: + ThermostatWorkAutomaticRampUp(); + break; + } +} + +void ThermostatWork(void) +{ + switch (Thermostat.status.thermostat_mode) { + case THERMOSTAT_OFF: + Thermostat.status.command_output = IFACE_OFF; + break; + case THERMOSTAT_AUTOMATIC_OP: + ThermostatCtrWork(); + break; + case THERMOSTAT_MANUAL_OP: + Thermostat.time_ctr_checkpoint = 0; + if (ThermostatSwitchStatus(Thermostat.input_switch_number) == 1) { + Thermostat.status.command_output = IFACE_ON; + } + else { + Thermostat.status.command_output = IFACE_OFF; + } + break; + } + bool output_command; + if (Thermostat.status.command_output == IFACE_OFF) { + output_command = false; + } + else { + output_command = true; + } + ThermostatOutputRelay(output_command); +} + +void ThermostatDiagnostics(void) +{ + + + + +} + +void ThermostatController(void) +{ + ThermostatState(); + ThermostatWork(); +} + +bool ThermostatTimerArm(int16_t tempVal) +{ + bool result = false; + + if ((tempVal >= -1000) + && (tempVal <= 1000) + && (tempVal >= (int16_t)Thermostat.temp_frost_protect)) { + Thermostat.temp_target_level = tempVal; + Thermostat.status.thermostat_mode = THERMOSTAT_AUTOMATIC_OP; + result = true; + } + + return result; +} + +void ThermostatTimerDisarm(void) +{ + Thermostat.temp_target_level = THERMOSTAT_TEMP_INIT; + Thermostat.status.thermostat_mode = THERMOSTAT_OFF; +} + +#ifdef DEBUG_THERMOSTAT +void ThermostatVirtualSwitch(void) +{ + char domoticz_in_topic[] = DOMOTICZ_IN_TOPIC; + Response_P(DOMOTICZ_MES, DOMOTICZ_IDX1, (0 == Thermostat.status.status_output) ? 0 : 1, ""); + MqttPublish(domoticz_in_topic); +} + +void ThermostatVirtualSwitchCtrState(void) +{ + char domoticz_in_topic[] = DOMOTICZ_IN_TOPIC; + Response_P(DOMOTICZ_MES, DOMOTICZ_IDX2, (0 == Thermostat.status.phase_hybrid_ctr) ? 0 : 1, ""); + MqttPublish(domoticz_in_topic); + + + +} +#endif + + + + + +void CmndThermostatModeSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(CharToFloat(XdrvMailbox.data)); + if ((value >= THERMOSTAT_OFF) && (value < THERMOSTAT_MODES_MAX)) { + Thermostat.status.thermostat_mode = value; + Thermostat.timestamp_input_on = 0; + } + } + ResponseCmndNumber((int)Thermostat.status.thermostat_mode); +} + +void CmndTempFrostProtectSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10); + if ((value >= 0) && (value <= 255)) { + Thermostat.temp_frost_protect = value; + } + } + ResponseCmndFloat((float)(Thermostat.temp_frost_protect) / 10, 1); +} + +void CmndControllerModeSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(XdrvMailbox.payload); + if ((value >= 0) && (value < CTR_MODES_MAX)) { + Thermostat.status.controller_mode = value; + } + } + ResponseCmndNumber((int)Thermostat.status.controller_mode); +} + +void CmndInputSwitchSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(XdrvMailbox.payload); + if (ThermostatSwitchIdValid(value)) { + Thermostat.input_switch_number = value; + Thermostat.timestamp_input_on = uptime; + } + } + ResponseCmndNumber((int)Thermostat.input_switch_number); +} + +void CmndOutputRelaySet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(XdrvMailbox.payload); + if (ThermostatRelayIdValid(value)) { + Thermostat.output_relay_number = value; + } + } + ResponseCmndNumber((int)Thermostat.output_relay_number); +} + +void CmndTimeAllowRampupSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value < 86400)) { + Thermostat.time_allow_rampup = (uint16_t)(value / 60); + } + } + ResponseCmndNumber((int)((uint32_t)Thermostat.time_allow_rampup * 60)); +} + +void CmndTempMeasuredSet(void) +{ + if (XdrvMailbox.data_len > 0) { + int16_t value = (int16_t)(CharToFloat(XdrvMailbox.data) * 10); + if ((value >= -1000) && (value <= 1000)) { + uint32_t timestamp = uptime; + + if (value != Thermostat.temp_measured) { + int32_t temp_delta = (value - Thermostat.temp_measured); + uint32_t time_delta = (timestamp - Thermostat.timestamp_temp_meas_change_update); + Thermostat.temp_measured_gradient = (int32_t)((360000 * temp_delta) / ((int32_t)time_delta)); + Thermostat.temp_measured = value; + Thermostat.timestamp_temp_meas_change_update = timestamp; + } + Thermostat.timestamp_temp_measured_update = timestamp; + Thermostat.status.sensor_alive = IFACE_ON; + } + } + ResponseCmndFloat(((float)Thermostat.temp_measured) / 10, 1); +} + +void CmndTempTargetSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint16_t value = (uint16_t)(CharToFloat(XdrvMailbox.data) * 10); + if ((value >= -1000) + && (value <= 1000) + && (value >= (int16_t)Thermostat.temp_frost_protect)) { + Thermostat.temp_target_level = value; + } + } + ResponseCmndFloat(((float)Thermostat.temp_target_level) / 10, 1); +} + +void CmndTempTargetRead(void) +{ + ResponseCmndFloat(((float)Thermostat.temp_target_level) / 10, 1); +} + +void CmndTempMeasuredRead(void) +{ + ResponseCmndFloat((float)(Thermostat.temp_measured) / 10, 1); +} + +void CmndTempMeasuredGrdRead(void) +{ + ResponseCmndFloat((float)(Thermostat.temp_measured_gradient) / 1000, 1); +} + +void CmndTempSensNumberSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 255)) { + Thermostat.temp_sens_number = value; + } + } + ResponseCmndNumber((int)Thermostat.temp_sens_number); +} + +void CmndStateEmergencySet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 1)) { + Thermostat.status.state_emergency = (uint16_t)value; + } + } + ResponseCmndNumber((int)Thermostat.status.state_emergency); +} + +void CmndPowerMaxSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint16_t value = (uint16_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 1300)) { + Thermostat.power_max = value; + } + } + ResponseCmndNumber((int)Thermostat.power_max); +} + +void CmndTimeManualToAutoSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 86400)) { + Thermostat.time_manual_to_auto = (uint16_t)(value / 60); + } + } + ResponseCmndNumber((int)((uint32_t)Thermostat.time_manual_to_auto * 60)); +} + +void CmndTimeOnLimitSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 86400)) { + Thermostat.time_on_limit = (uint16_t)(value / 60); + } + } + ResponseCmndNumber((int)((uint32_t)Thermostat.time_on_limit * 60)); +} + +void CmndPropBandSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 20)) { + Thermostat.val_prop_band = value; + } + } + ResponseCmndNumber((int)Thermostat.val_prop_band); +} + +void CmndTimeResetSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 86400)) { + Thermostat.time_reset = value; + } + } + ResponseCmndNumber((int)Thermostat.time_reset); +} + +void CmndTimePiCycleSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 86400)) { + Thermostat.time_pi_cycle = (uint16_t)(value / 60); + } + } + ResponseCmndNumber((int)((uint32_t)Thermostat.time_pi_cycle * 60)); +} + +void CmndTempAntiWindupResetSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10); + if ((value >= (float)(0)) && (value <= (float)(100.0))) { + Thermostat.temp_reset_anti_windup = value; + } + } + ResponseCmndFloat((float)(Thermostat.temp_reset_anti_windup) / 10, 1); +} + +void CmndTempHystSet(void) +{ + if (XdrvMailbox.data_len > 0) { + int8_t value = (int8_t)(CharToFloat(XdrvMailbox.data) * 10); + if ((value >= -100) && (value <= 100)) { + Thermostat.temp_hysteresis = value; + } + } + ResponseCmndFloat((float)(Thermostat.temp_hysteresis) / 10, 1); +} + +void CmndTimeMaxActionSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 86400)) { + Thermostat.time_max_action = (uint16_t)(value / 60); + } + } + ResponseCmndNumber((int)((uint32_t)Thermostat.time_max_action * 60)); +} + +void CmndTimeMinActionSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 86400)) { + Thermostat.time_min_action = (uint16_t)(value / 60); + } + } + ResponseCmndNumber((int)((uint32_t)Thermostat.time_min_action * 60)); +} + +void CmndTimeSensLostSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 86400)) { + Thermostat.time_sens_lost = (uint16_t)(value / 60); + } + } + ResponseCmndNumber((int)((uint32_t)Thermostat.time_sens_lost * 60)); +} + +void CmndTimeMinTurnoffActionSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 86400)) { + Thermostat.time_min_turnoff_action = (uint16_t)(value / 60); + } + } + ResponseCmndNumber((int)((uint32_t)Thermostat.time_min_turnoff_action * 60)); +} + +void CmndTempRupDeltInSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10); + if ((value >= 0) && (value <= 100)) { + Thermostat.temp_rampup_delta_in = value; + } + } + ResponseCmndFloat((float)(Thermostat.temp_rampup_delta_in) / 10, 1); +} + +void CmndTempRupDeltOutSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10); + if ((value >= 0) && (value <= 100)) { + Thermostat.temp_rampup_delta_out = value; + } + } + ResponseCmndFloat((float)(Thermostat.temp_rampup_delta_out) / 10, 1); +} + +void CmndTimeRampupMaxSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 86400)) { + Thermostat.time_rampup_max = (uint16_t)(value / 60); + } + } + ResponseCmndNumber((int)(((uint32_t)Thermostat.time_rampup_max) * 60)); +} + +void CmndTimeRampupCycleSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint32_t value = (uint32_t)(XdrvMailbox.payload); + if ((value >= 0) && (value <= 54000)) { + Thermostat.time_rampup_cycle = (uint16_t)value; + } + } + ResponseCmndNumber((int)Thermostat.time_rampup_cycle); +} + +void CmndTempRampupPiAccErrSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint16_t value = (uint8_t)(CharToFloat(XdrvMailbox.data) * 100); + if ((value >= 0) && (value <= 2500)) { + Thermostat.temp_rampup_pi_acc_error = value; + } + } + ResponseCmndFloat((float)(Thermostat.temp_rampup_pi_acc_error) / 100, 1); +} + +void CmndTimePiProportRead(void) +{ + ResponseCmndNumber((int)Thermostat.time_proportional_pi); +} + +void CmndTimePiIntegrRead(void) +{ + ResponseCmndNumber((int)Thermostat.time_integral_pi); +} + + + + + +bool Xdrv39(uint8_t function) +{ +#ifdef DEBUG_THERMOSTAT + char result_chr[FLOATSZ]; +#endif + bool result = false; + + switch (function) { + case FUNC_INIT: + ThermostatInit(); + break; + case FUNC_LOOP: + ThermostatSignalProcessingFast(); + ThermostatDiagnostics(); + break; + case FUNC_SERIAL: + break; + case FUNC_EVERY_SECOND: + if (ThermostatMinuteCounter()) { + ThermostatSignalProcessingSlow(); + ThermostatController(); +#ifdef DEBUG_THERMOSTAT + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("")); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("------ Thermostat Start ------")); + dtostrfd(Thermostat.status.counter_seconds, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.status.counter_seconds: %s"), result_chr); + dtostrfd(Thermostat.status.thermostat_mode, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.status.thermostat_mode: %s"), result_chr); + dtostrfd(Thermostat.status.controller_mode, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.status.controller_mode: %s"), result_chr); + dtostrfd(Thermostat.status.phase_hybrid_ctr, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.status.phase_hybrid_ctr: %s"), result_chr); + dtostrfd(Thermostat.status.sensor_alive, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.status.sensor_alive: %s"), result_chr); + dtostrfd(Thermostat.status.status_output, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.status.status_output: %s"), result_chr); + dtostrfd(Thermostat.status.status_cycle_active, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.status.status_cycle_active: %s"), result_chr); + dtostrfd(Thermostat.temp_pi_error, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.temp_pi_error: %s"), result_chr); + dtostrfd(Thermostat.temp_pi_accum_error, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.temp_pi_accum_error: %s"), result_chr); + dtostrfd(Thermostat.time_proportional_pi, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.time_proportional_pi: %s"), result_chr); + dtostrfd(Thermostat.time_integral_pi, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.time_integral_pi: %s"), result_chr); + dtostrfd(Thermostat.time_total_pi, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.time_total_pi: %s"), result_chr); + dtostrfd(Thermostat.temp_measured_gradient, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.temp_measured_gradient: %s"), result_chr); + dtostrfd(Thermostat.time_rampup_deadtime, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.time_rampup_deadtime: %s"), result_chr); + dtostrfd(Thermostat.temp_rampup_meas_gradient, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.temp_rampup_meas_gradient: %s"), result_chr); + dtostrfd(Thermostat.time_ctr_changepoint, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.time_ctr_changepoint: %s"), result_chr); + dtostrfd(Thermostat.temp_rampup_output_off, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.temp_rampup_output_off: %s"), result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("------ Thermostat End ------")); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("")); +#endif + } + break; + case FUNC_COMMAND: + result = DecodeCommand(kThermostatCommands, ThermostatCommand); + break; + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_99_debug.ino" +# 22 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_99_debug.ino" +#ifdef DEBUG_THEO +#ifndef USE_DEBUG_DRIVER +#define USE_DEBUG_DRIVER +#endif +#endif + +#ifdef USE_DEBUG_DRIVER + + + + + + +#define XDRV_99 99 + +#ifndef CPU_LOAD_CHECK +#define CPU_LOAD_CHECK 1 +#endif + + + + + +#define D_CMND_CFGDUMP "CfgDump" +#define D_CMND_CFGPEEK "CfgPeek" +#define D_CMND_CFGPOKE "CfgPoke" +#define D_CMND_CFGSHOW "CfgShow" +#define D_CMND_CFGXOR "CfgXor" +#define D_CMND_CPUCHECK "CpuChk" +#define D_CMND_EXCEPTION "Exception" +#define D_CMND_FLASHDUMP "FlashDump" +#define D_CMND_FLASHMODE "FlashMode" +#define D_CMND_FREEMEM "FreeMem" +#define D_CMND_HELP "Help" +#define D_CMND_RTCDUMP "RtcDump" +#define D_CMND_SETSENSOR "SetSensor" +#define D_CMND_I2CWRITE "I2CWrite" +#define D_CMND_I2CREAD "I2CRead" +#define D_CMND_I2CSTRETCH "I2CStretch" +#define D_CMND_I2CCLOCK "I2CClock" + +const char kDebugCommands[] PROGMEM = "|" + D_CMND_CFGDUMP "|" D_CMND_CFGPEEK "|" D_CMND_CFGPOKE "|" +#ifdef USE_WEBSERVER + D_CMND_CFGXOR "|" +#endif + D_CMND_CPUCHECK "|" +#ifdef DEBUG_THEO + D_CMND_EXCEPTION "|" +#endif + D_CMND_FLASHDUMP "|" D_CMND_FLASHMODE "|" D_CMND_FREEMEM"|" D_CMND_HELP "|" D_CMND_RTCDUMP "|" D_CMND_SETSENSOR "|" +#ifdef USE_I2C + D_CMND_I2CWRITE "|" D_CMND_I2CREAD "|" D_CMND_I2CSTRETCH "|" D_CMND_I2CCLOCK +#endif + ; + +void (* const DebugCommand[])(void) PROGMEM = { + &CmndCfgDump, &CmndCfgPeek, &CmndCfgPoke, +#ifdef USE_WEBSERVER + &CmndCfgXor, +#endif + &CmndCpuCheck, +#ifdef DEBUG_THEO + &CmndException, +#endif + &CmndFlashDump, &CmndFlashMode, &CmndFreemem, &CmndHelp, &CmndRtcDump, &CmndSetSensor, +#ifdef USE_I2C + &CmndI2cWrite, &CmndI2cRead, &CmndI2cStretch, &CmndI2cClock +#endif + }; + +uint32_t CPU_loops = 0; +uint32_t CPU_last_millis = 0; +uint32_t CPU_last_loop_time = 0; +uint8_t CPU_load_check = 0; +uint8_t CPU_show_freemem = 0; + + + +#ifdef DEBUG_THEO +void ExceptionTest(uint8_t type) +{ +# 145 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_99_debug.ino" + if (1 == type) { + char svalue[10]; + snprintf_P(svalue, sizeof(svalue), PSTR("%s"), 7); + } +# 159 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_99_debug.ino" + if (2 == type) { + while(1) delay(1000); + } +} + +#endif + + + +void CpuLoadLoop(void) +{ + CPU_last_loop_time = millis(); + if (CPU_load_check && CPU_last_millis) { + CPU_loops ++; + if ((CPU_last_millis + (CPU_load_check *1000)) <= CPU_last_loop_time) { +#if defined(F_CPU) && (F_CPU == 160000000L) + int CPU_load = 100 - ( (CPU_loops*(1 + 30*ssleep)) / (CPU_load_check *800) ); + CPU_loops = CPU_loops / CPU_load_check; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(160MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops); +#else + int CPU_load = 100 - ( (CPU_loops*(1 + 30*ssleep)) / (CPU_load_check *400) ); + CPU_loops = CPU_loops / CPU_load_check; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(80MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops); +#endif + CPU_last_millis = CPU_last_loop_time; + CPU_loops = 0; + } + } +} + + + +#ifdef ESP8266 +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) + + + +extern "C" { +#include + extern cont_t g_cont; +} + +void DebugFreeMem(void) +{ + register uint32_t *sp asm("a1"); + + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), 4 * (sp - g_cont.stack), XdrvMailbox.data); +} + +#else + + + + +extern "C" { +#include + extern cont_t* g_pcont; +} + +void DebugFreeMem(void) +{ + register uint32_t *sp asm("a1"); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), 4 * (sp - g_pcont->stack), XdrvMailbox.data); +} + +#endif + +#else + +void DebugFreeMem(void) +{ + register uint8_t *sp asm("a1"); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), sp - pxTaskGetStackStart(NULL), XdrvMailbox.data); +} + +#endif + + + +void DebugRtcDump(char* parms) +{ +#ifdef ESP8266 + #define CFG_COLS 16 + + uint16_t idx; + uint16_t maxrow; + uint16_t row; + uint16_t col; + char *p; +# 259 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_99_debug.ino" + uint8_t buffer[768]; + + system_rtc_mem_read(0, (uint32_t*)&buffer, sizeof(buffer)); + + maxrow = ((sizeof(buffer)+CFG_COLS)/CFG_COLS); + + uint16_t srow = strtol(parms, &p, 16) / CFG_COLS; + uint16_t mrow = strtol(p, &p, 10); + + + + if (0 == mrow) { + mrow = 8; + } + if (srow > maxrow) { + srow = maxrow - mrow; + } + if (mrow < (maxrow - srow)) { + maxrow = srow + mrow; + } + + for (row = srow; row < maxrow; row++) { + idx = row * CFG_COLS; + snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx); + for (col = 0; col < CFG_COLS; col++) { + if (!(col%4)) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); + for (col = 0; col < CFG_COLS; col++) { + + + + snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' '); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data); + AddLog(LOG_LEVEL_INFO); + } +#endif +} + + + +void DebugCfgDump(char* parms) +{ + #define CFG_COLS 16 + + uint16_t idx; + uint16_t maxrow; + uint16_t row; + uint16_t col; + char *p; + + uint8_t *buffer = (uint8_t *) &Settings; + maxrow = ((sizeof(SYSCFG)+CFG_COLS)/CFG_COLS); + + uint16_t srow = strtol(parms, &p, 16) / CFG_COLS; + uint16_t mrow = strtol(p, &p, 10); + + + + if (0 == mrow) { + mrow = 8; + } + if (srow > maxrow) { + srow = maxrow - mrow; + } + if (mrow < (maxrow - srow)) { + maxrow = srow + mrow; + } + + for (row = srow; row < maxrow; row++) { + idx = row * CFG_COLS; + snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx); + for (col = 0; col < CFG_COLS; col++) { + if (!(col%4)) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); + for (col = 0; col < CFG_COLS; col++) { + + + + snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' '); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data); + AddLog(LOG_LEVEL_INFO); + delay(1); + } +} + +void DebugCfgPeek(char* parms) +{ + char *p; + + uint16_t address = strtol(parms, &p, 16); + if (address > sizeof(SYSCFG)) address = sizeof(SYSCFG) -4; + address = (address >> 2) << 2; + + uint8_t *buffer = (uint8_t *) &Settings; + uint8_t data8 = buffer[address]; + uint16_t data16 = (buffer[address +1] << 8) + buffer[address]; + uint32_t data32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + data16; + + snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), address); + for (uint32_t i = 0; i < 4; i++) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[address +i]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); + for (uint32_t i = 0; i < 4; i++) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[address +i] > 0x20) && (buffer[address +i] < 0x7F)) ? (char)buffer[address +i] : ' '); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s| 0x%02X (%d), 0x%04X (%d), 0x%0LX (%lu)"), log_data, data8, data8, data16, data16, data32, data32); + AddLog(LOG_LEVEL_INFO); +} + +void DebugCfgPoke(char* parms) +{ + char *p; + + uint16_t address = strtol(parms, &p, 16); + if (address > sizeof(SYSCFG)) address = sizeof(SYSCFG) -4; + address = (address >> 2) << 2; + + uint32_t data = strtol(p, &p, 16); + + uint8_t *buffer = (uint8_t *) &Settings; + uint32_t data32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + (buffer[address +1] << 8) + buffer[address]; + + uint8_t *nbuffer = (uint8_t *) &data; + for (uint32_t i = 0; i < 4; i++) { buffer[address +i] = nbuffer[+i]; } + + uint32_t ndata32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + (buffer[address +1] << 8) + buffer[address]; + + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: 0x%0LX (%lu) poked to 0x%0LX (%lu)"), address, data32, data32, ndata32, ndata32); +} + +void SetFlashMode(uint8_t mode) +{ +#ifdef ESP8266 + uint8_t *_buffer; + uint32_t address; + + address = 0; + _buffer = new uint8_t[FLASH_SECTOR_SIZE]; + + if (ESP.flashRead(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE)) { + if (_buffer[2] != mode) { + _buffer[2] = mode; + if (ESP.flashEraseSector(address / FLASH_SECTOR_SIZE)) { + ESP.flashWrite(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE); + } + } + } + delete[] _buffer; +#endif +} + + + + + +void CmndHelp(void) +{ + AddLog_P(LOG_LEVEL_INFO, PSTR("HLP: "), kDebugCommands); + ResponseCmndDone(); +} + +void CmndRtcDump(void) +{ + DebugRtcDump(XdrvMailbox.data); + ResponseCmndDone(); +} + +void CmndCfgDump(void) +{ + DebugCfgDump(XdrvMailbox.data); + ResponseCmndDone(); +} + +void CmndCfgPeek(void) +{ + DebugCfgPeek(XdrvMailbox.data); + ResponseCmndDone(); +} + +void CmndCfgPoke(void) +{ + DebugCfgPoke(XdrvMailbox.data); + ResponseCmndDone(); +} + +#ifdef USE_WEBSERVER +void CmndCfgXor(void) +{ + if (XdrvMailbox.data_len > 0) { + Web.config_xor_on_set = XdrvMailbox.payload; + } + ResponseCmndNumber(Web.config_xor_on_set); +} +#endif + +#ifdef DEBUG_THEO +void CmndException(void) +{ + if (XdrvMailbox.data_len > 0) { ExceptionTest(XdrvMailbox.payload); } + ResponseCmndDone(); +} +#endif + +void CmndCpuCheck(void) +{ + if (XdrvMailbox.data_len > 0) { + CPU_load_check = XdrvMailbox.payload; + CPU_last_millis = CPU_last_loop_time; + } + ResponseCmndNumber(CPU_load_check); +} + +void CmndFreemem(void) +{ + if (XdrvMailbox.data_len > 0) { + CPU_show_freemem = XdrvMailbox.payload; + } + ResponseCmndNumber(CPU_show_freemem); +} + +void CmndSetSensor(void) +{ + if (XdrvMailbox.index < MAX_XSNS_DRIVERS) { + if (XdrvMailbox.payload >= 0) { + bitWrite(Settings.sensors[XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1); + if (1 == XdrvMailbox.payload) { + restart_flag = 2; + } + } + Response_P(PSTR("{\"" D_CMND_SETSENSOR "\":")); + XsnsSensorState(); + ResponseJsonEnd(); + } +} + +void CmndFlashMode(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + SetFlashMode(XdrvMailbox.payload); + } + ResponseCmndNumber(ESP.getFlashChipMode()); +} + +uint32_t DebugSwap32(uint32_t x) { + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} + +void CmndFlashDump(void) +{ +#ifdef ESP8266 + + + + const uint32_t flash_start = 0x40200000; + const uint8_t bytes_per_cols = 0x20; + const uint32_t max = (SPIFFS_END + 5) * SPI_FLASH_SEC_SIZE; + + uint32_t start = flash_start; + uint32_t rows = 8; + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= (max - bytes_per_cols))) { + start += (XdrvMailbox.payload &0x7FFFFFFC); + + char *p; + uint32_t is_payload = strtol(XdrvMailbox.data, &p, 16); + rows = strtol(p, &p, 10); + if (0 == rows) { rows = 8; } + } + uint32_t end = start + (rows * bytes_per_cols); + if ((end - flash_start) > max) { + end = flash_start + max; + } + + for (uint32_t pos = start; pos < end; pos += bytes_per_cols) { + uint32_t* values = (uint32_t*)(pos); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%06X: %08X %08X %08X %08X %08X %08X %08X %08X"), pos - flash_start, + DebugSwap32(values[0]), DebugSwap32(values[1]), DebugSwap32(values[2]), DebugSwap32(values[3]), + DebugSwap32(values[4]), DebugSwap32(values[5]), DebugSwap32(values[6]), DebugSwap32(values[7])); + } + ResponseCmndDone(); +#endif +} + +#ifdef USE_I2C +void CmndI2cWrite(void) +{ + + if (i2c_flg) { + char* parms = XdrvMailbox.data; + uint8_t buffer[100]; + uint32_t index = 0; + + char *p; + char *data = strtok_r(parms, " ,", &p); + while (data != NULL && index < sizeof(buffer)) { + buffer[index++] = strtol(data, nullptr, 16); + data = strtok_r(nullptr, " ,", &p); + } + + if (index > 1) { + AddLogBuffer(LOG_LEVEL_INFO, buffer, index); + + Wire.beginTransmission(buffer[0]); + for (uint32_t i = 1; i < index; i++) { + Wire.write(buffer[i]); + } + int result = Wire.endTransmission(); + AddLog_P2(LOG_LEVEL_INFO, PSTR("I2C: Result %d"), result); + } + } + ResponseCmndDone(); +} + +void CmndI2cRead(void) +{ + + if (i2c_flg) { + char* parms = XdrvMailbox.data; + uint8_t buffer[100]; + uint32_t index = 0; + + char *p; + char *data = strtok_r(parms, " ,", &p); + while (data != NULL && index < sizeof(buffer)) { + buffer[index++] = strtol(data, nullptr, 16); + data = strtok_r(nullptr, " ,", &p); + } + + if (index > 0) { + uint8_t size = 1; + if (index > 1) { + size = buffer[1]; + } + Wire.requestFrom(buffer[0], size); + index = 0; + while (Wire.available() && index < sizeof(buffer)) { + buffer[index++] = Wire.read(); + } + if (index > 0) { + AddLogBuffer(LOG_LEVEL_INFO, buffer, index); + } + } + } + ResponseCmndDone(); +} + +void CmndI2cStretch(void) +{ +#ifdef ESP8266 + if (i2c_flg && (XdrvMailbox.payload > 0)) { + Wire.setClockStretchLimit(XdrvMailbox.payload); + } + ResponseCmndDone(); +#endif +} + +void CmndI2cClock(void) +{ + if (i2c_flg && (XdrvMailbox.payload > 0)) { + Wire.setClock(XdrvMailbox.payload); + } + ResponseCmndDone(); +} +#endif + + + + + +bool Xdrv99(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_LOOP: + CpuLoadLoop(); + break; + case FUNC_FREE_MEM: + if (CPU_show_freemem) { DebugFreeMem(); } + break; + case FUNC_PRE_INIT: + CPU_last_millis = millis(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kDebugCommands, DebugCommand); + break; + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_interface.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_interface.ino" +#ifdef XFUNC_PTR_IN_ROM +bool (* const xdrv_func_ptr[])(uint8_t) PROGMEM = { +#else +bool (* const xdrv_func_ptr[])(uint8_t) = { +#endif + +#ifdef XDRV_01 + &Xdrv01, +#endif + +#ifdef XDRV_02 + &Xdrv02, +#endif + +#ifdef XDRV_03 + &Xdrv03, +#endif + +#ifdef XDRV_04 + &Xdrv04, +#endif + +#ifdef XDRV_05 + &Xdrv05, +#endif + +#ifdef XDRV_06 + &Xdrv06, +#endif + +#ifdef XDRV_07 + &Xdrv07, +#endif + +#ifdef XDRV_08 + &Xdrv08, +#endif + +#ifdef XDRV_09 + &Xdrv09, +#endif + +#ifdef XDRV_10 + &Xdrv10, +#endif + +#ifdef XDRV_11 + &Xdrv11, +#endif + +#ifdef XDRV_12 + &Xdrv12, +#endif + +#ifdef XDRV_13 + &Xdrv13, +#endif + +#ifdef XDRV_14 + &Xdrv14, +#endif + +#ifdef XDRV_15 + &Xdrv15, +#endif + +#ifdef XDRV_16 + &Xdrv16, +#endif + +#ifdef XDRV_17 + &Xdrv17, +#endif + +#ifdef XDRV_18 + &Xdrv18, +#endif + +#ifdef XDRV_19 + &Xdrv19, +#endif + +#ifdef XDRV_20 + &Xdrv20, +#endif + +#ifdef XDRV_21 + &Xdrv21, +#endif + +#ifdef XDRV_22 + &Xdrv22, +#endif + +#ifdef XDRV_23 + &Xdrv23, +#endif + +#ifdef XDRV_24 + &Xdrv24, +#endif + +#ifdef XDRV_25 + &Xdrv25, +#endif + +#ifdef XDRV_26 + &Xdrv26, +#endif + +#ifdef XDRV_27 + &Xdrv27, +#endif + +#ifdef XDRV_28 + &Xdrv28, +#endif + +#ifdef XDRV_29 + &Xdrv29, +#endif + +#ifdef XDRV_30 + &Xdrv30, +#endif + +#ifdef XDRV_31 + &Xdrv31, +#endif + +#ifdef XDRV_32 + &Xdrv32, +#endif + +#ifdef XDRV_33 + &Xdrv33, +#endif + +#ifdef XDRV_34 + &Xdrv34, +#endif + +#ifdef XDRV_35 + &Xdrv35, +#endif + +#ifdef XDRV_36 + &Xdrv36, +#endif + +#ifdef XDRV_37 + &Xdrv37, +#endif + +#ifdef XDRV_38 + &Xdrv38, +#endif + +#ifdef XDRV_39 + &Xdrv39, +#endif + +#ifdef XDRV_40 + &Xdrv40, +#endif + +#ifdef XDRV_41 + &Xdrv41, +#endif + +#ifdef XDRV_42 + &Xdrv42, +#endif + +#ifdef XDRV_43 + &Xdrv43, +#endif + +#ifdef XDRV_44 + &Xdrv44, +#endif + +#ifdef XDRV_45 + &Xdrv45, +#endif + +#ifdef XDRV_46 + &Xdrv46, +#endif + +#ifdef XDRV_47 + &Xdrv47, +#endif + +#ifdef XDRV_48 + &Xdrv48, +#endif + +#ifdef XDRV_49 + &Xdrv49, +#endif + +#ifdef XDRV_50 + &Xdrv50, +#endif + +#ifdef XDRV_51 + &Xdrv51, +#endif + +#ifdef XDRV_52 + &Xdrv52, +#endif + +#ifdef XDRV_53 + &Xdrv53, +#endif + +#ifdef XDRV_54 + &Xdrv54, +#endif + +#ifdef XDRV_55 + &Xdrv55, +#endif + +#ifdef XDRV_56 + &Xdrv56, +#endif + +#ifdef XDRV_57 + &Xdrv57, +#endif + +#ifdef XDRV_58 + &Xdrv58, +#endif + +#ifdef XDRV_59 + &Xdrv59, +#endif + +#ifdef XDRV_60 + &Xdrv60, +#endif + +#ifdef XDRV_61 + &Xdrv61, +#endif + +#ifdef XDRV_62 + &Xdrv62, +#endif + +#ifdef XDRV_63 + &Xdrv63, +#endif + +#ifdef XDRV_64 + &Xdrv64, +#endif + +#ifdef XDRV_65 + &Xdrv65, +#endif + +#ifdef XDRV_66 + &Xdrv66, +#endif + +#ifdef XDRV_67 + &Xdrv67, +#endif + +#ifdef XDRV_68 + &Xdrv68, +#endif + +#ifdef XDRV_69 + &Xdrv69, +#endif + +#ifdef XDRV_70 + &Xdrv70, +#endif + +#ifdef XDRV_71 + &Xdrv71, +#endif + +#ifdef XDRV_72 + &Xdrv72, +#endif + +#ifdef XDRV_73 + &Xdrv73, +#endif + +#ifdef XDRV_74 + &Xdrv74, +#endif + +#ifdef XDRV_75 + &Xdrv75, +#endif + +#ifdef XDRV_76 + &Xdrv76, +#endif + +#ifdef XDRV_77 + &Xdrv77, +#endif + +#ifdef XDRV_78 + &Xdrv78, +#endif + +#ifdef XDRV_79 + &Xdrv79, +#endif + +#ifdef XDRV_80 + &Xdrv80, +#endif + +#ifdef XDRV_81 + &Xdrv81, +#endif + +#ifdef XDRV_82 + &Xdrv82, +#endif + +#ifdef XDRV_83 + &Xdrv83, +#endif + +#ifdef XDRV_84 + &Xdrv84, +#endif + +#ifdef XDRV_85 + &Xdrv85, +#endif + +#ifdef XDRV_86 + &Xdrv86, +#endif + +#ifdef XDRV_87 + &Xdrv87, +#endif + +#ifdef XDRV_88 + &Xdrv88, +#endif + +#ifdef XDRV_89 + &Xdrv89, +#endif + +#ifdef XDRV_90 + &Xdrv90, +#endif + +#ifdef XDRV_91 + &Xdrv91, +#endif + +#ifdef XDRV_92 + &Xdrv92, +#endif + +#ifdef XDRV_93 + &Xdrv93, +#endif + +#ifdef XDRV_94 + &Xdrv94, +#endif + +#ifdef XDRV_95 + &Xdrv95, +#endif + +#ifdef XDRV_96 + &Xdrv96, +#endif + +#ifdef XDRV_97 + &Xdrv97, +#endif + +#ifdef XDRV_98 + &Xdrv98, +#endif + +#ifdef XDRV_99 + &Xdrv99 +#endif +}; + +const uint8_t xdrv_present = sizeof(xdrv_func_ptr) / sizeof(xdrv_func_ptr[0]); + + + + + +#ifdef XFUNC_PTR_IN_ROM +const uint8_t kXdrvList[] PROGMEM = { +#else +const uint8_t kXdrvList[] = { +#endif + +#ifdef XDRV_01 + XDRV_01, +#endif + +#ifdef XDRV_02 + XDRV_02, +#endif + +#ifdef XDRV_03 + XDRV_03, +#endif + +#ifdef XDRV_04 + XDRV_04, +#endif + +#ifdef XDRV_05 + XDRV_05, +#endif + +#ifdef XDRV_06 + XDRV_06, +#endif + +#ifdef XDRV_07 + XDRV_07, +#endif + +#ifdef XDRV_08 + XDRV_08, +#endif + +#ifdef XDRV_09 + XDRV_09, +#endif + +#ifdef XDRV_10 + XDRV_10, +#endif + +#ifdef XDRV_11 + XDRV_11, +#endif + +#ifdef XDRV_12 + XDRV_12, +#endif + +#ifdef XDRV_13 + XDRV_13, +#endif + +#ifdef XDRV_14 + XDRV_14, +#endif + +#ifdef XDRV_15 + XDRV_15, +#endif + +#ifdef XDRV_16 + XDRV_16, +#endif + +#ifdef XDRV_17 + XDRV_17, +#endif + +#ifdef XDRV_18 + XDRV_18, +#endif + +#ifdef XDRV_19 + XDRV_19, +#endif + +#ifdef XDRV_20 + XDRV_20, +#endif + +#ifdef XDRV_21 + XDRV_21, +#endif + +#ifdef XDRV_22 + XDRV_22, +#endif + +#ifdef XDRV_23 + XDRV_23, +#endif + +#ifdef XDRV_24 + XDRV_24, +#endif + +#ifdef XDRV_25 + XDRV_25, +#endif + +#ifdef XDRV_26 + XDRV_26, +#endif + +#ifdef XDRV_27 + XDRV_27, +#endif + +#ifdef XDRV_28 + XDRV_28, +#endif + +#ifdef XDRV_29 + XDRV_29, +#endif + +#ifdef XDRV_30 + XDRV_30, +#endif + +#ifdef XDRV_31 + XDRV_31, +#endif + +#ifdef XDRV_32 + XDRV_32, +#endif + +#ifdef XDRV_33 + XDRV_33, +#endif + +#ifdef XDRV_34 + XDRV_34, +#endif + +#ifdef XDRV_35 + XDRV_35, +#endif + +#ifdef XDRV_36 + XDRV_36, +#endif + +#ifdef XDRV_37 + XDRV_37, +#endif + +#ifdef XDRV_38 + XDRV_38, +#endif + +#ifdef XDRV_39 + XDRV_39, +#endif + +#ifdef XDRV_40 + XDRV_40, +#endif + +#ifdef XDRV_41 + XDRV_41, +#endif + +#ifdef XDRV_42 + XDRV_42, +#endif + +#ifdef XDRV_43 + XDRV_43, +#endif + +#ifdef XDRV_44 + XDRV_44, +#endif + +#ifdef XDRV_45 + XDRV_45, +#endif + +#ifdef XDRV_46 + XDRV_46, +#endif + +#ifdef XDRV_47 + XDRV_47, +#endif + +#ifdef XDRV_48 + XDRV_48, +#endif + +#ifdef XDRV_49 + XDRV_49, +#endif + +#ifdef XDRV_50 + XDRV_50, +#endif + +#ifdef XDRV_51 + XDRV_51, +#endif + +#ifdef XDRV_52 + XDRV_52, +#endif + +#ifdef XDRV_53 + XDRV_53, +#endif + +#ifdef XDRV_54 + XDRV_54, +#endif + +#ifdef XDRV_55 + XDRV_55, +#endif + +#ifdef XDRV_56 + XDRV_56, +#endif + +#ifdef XDRV_57 + XDRV_57, +#endif + +#ifdef XDRV_58 + XDRV_58, +#endif + +#ifdef XDRV_59 + XDRV_59, +#endif + +#ifdef XDRV_60 + XDRV_60, +#endif + +#ifdef XDRV_61 + XDRV_61, +#endif + +#ifdef XDRV_62 + XDRV_62, +#endif + +#ifdef XDRV_63 + XDRV_63, +#endif + +#ifdef XDRV_64 + XDRV_64, +#endif + +#ifdef XDRV_65 + XDRV_65, +#endif + +#ifdef XDRV_66 + XDRV_66, +#endif + +#ifdef XDRV_67 + XDRV_67, +#endif + +#ifdef XDRV_68 + XDRV_68, +#endif + +#ifdef XDRV_69 + XDRV_69, +#endif + +#ifdef XDRV_70 + XDRV_70, +#endif + +#ifdef XDRV_71 + XDRV_71, +#endif + +#ifdef XDRV_72 + XDRV_72, +#endif + +#ifdef XDRV_73 + XDRV_73, +#endif + +#ifdef XDRV_74 + XDRV_74, +#endif + +#ifdef XDRV_75 + XDRV_75, +#endif + +#ifdef XDRV_76 + XDRV_76, +#endif + +#ifdef XDRV_77 + XDRV_77, +#endif + +#ifdef XDRV_78 + XDRV_78, +#endif + +#ifdef XDRV_79 + XDRV_79, +#endif + +#ifdef XDRV_80 + XDRV_80, +#endif + +#ifdef XDRV_81 + XDRV_81, +#endif + +#ifdef XDRV_82 + XDRV_82, +#endif + +#ifdef XDRV_83 + XDRV_83, +#endif + +#ifdef XDRV_84 + XDRV_84, +#endif + +#ifdef XDRV_85 + XDRV_85, +#endif + +#ifdef XDRV_86 + XDRV_86, +#endif + +#ifdef XDRV_87 + XDRV_87, +#endif + +#ifdef XDRV_88 + XDRV_88, +#endif + +#ifdef XDRV_89 + XDRV_89, +#endif + +#ifdef XDRV_90 + XDRV_90, +#endif + +#ifdef XDRV_91 + XDRV_91, +#endif + +#ifdef XDRV_92 + XDRV_92, +#endif + +#ifdef XDRV_93 + XDRV_93, +#endif + +#ifdef XDRV_94 + XDRV_94, +#endif + +#ifdef XDRV_95 + XDRV_95, +#endif + +#ifdef XDRV_96 + XDRV_96, +#endif + +#ifdef XDRV_97 + XDRV_97, +#endif + +#ifdef XDRV_98 + XDRV_98, +#endif + +#ifdef XDRV_99 + XDRV_99 +#endif +}; + + + +void XsnsDriverState(void) +{ + ResponseAppend_P(PSTR(",\"Drivers\":\"")); + for (uint32_t i = 0; i < sizeof(kXdrvList); i++) { +#ifdef XFUNC_PTR_IN_ROM + uint32_t driverid = pgm_read_byte(kXdrvList + i); +#else + uint32_t driverid = kXdrvList[i]; +#endif + ResponseAppend_P(PSTR("%s%d"), (i) ? "," : "", driverid); + } + ResponseAppend_P(PSTR("\"")); +} + + + +bool XdrvRulesProcess(void) +{ + return XdrvCallDriver(10, FUNC_RULES_PROCESS); +} + +#ifdef USE_DEBUG_DRIVER +void ShowFreeMem(const char *where) +{ + char stemp[30]; + snprintf_P(stemp, sizeof(stemp), where); + XdrvMailbox.data = stemp; + XdrvCall(FUNC_FREE_MEM); +} +#endif + + + + + +bool XdrvCallDriver(uint32_t driver, uint8_t Function) +{ + for (uint32_t x = 0; x < xdrv_present; x++) { +#ifdef XFUNC_PTR_IN_ROM + uint32_t listed = pgm_read_byte(kXdrvList + x); +#else + uint32_t listed = kXdrvList[x]; +#endif + if (driver == listed) { + return xdrv_func_ptr[x](Function); + } + } + return false; +} + + + + + +bool XdrvCall(uint8_t Function) +{ + bool result = false; + + DEBUG_TRACE_LOG(PSTR("DRV: %d"), Function); + + for (uint32_t x = 0; x < xdrv_present; x++) { + result = xdrv_func_ptr[x](Function); + + if (result && ((FUNC_COMMAND == Function) || + (FUNC_COMMAND_DRIVER == Function) || + (FUNC_MQTT_DATA == Function) || + (FUNC_RULES_PROCESS == Function) || + (FUNC_BUTTON_PRESSED == Function) || + (FUNC_SERIAL == Function) || + (FUNC_MODULE_INIT == Function) || + (FUNC_SET_CHANNELS == Function) || + (FUNC_PIN_STATE == Function) || + (FUNC_SET_DEVICE_POWER == Function) + )) { + break; + } + } + + return result; +} +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_01_lcd.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_01_lcd.ino" +#ifdef USE_I2C +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_LCD + +#define XDSP_01 1 +#define XI2C_03 3 + +#define LCD_ADDRESS1 0x27 +#define LCD_ADDRESS2 0x3F + +#include +#include + +LiquidCrystal_I2C *lcd; + + + +void LcdInitMode(void) +{ + lcd->init(); + lcd->clear(); +} + +void LcdInit(uint8_t mode) +{ + switch(mode) { + case DISPLAY_INIT_MODE: + LcdInitMode(); +#ifdef USE_DISPLAY_MODES1TO5 + DisplayClearScreenBuffer(); +#endif + break; + case DISPLAY_INIT_PARTIAL: + case DISPLAY_INIT_FULL: + break; + } +} + +void LcdInitDriver(void) +{ + if (!Settings.display_model) { + if (I2cSetDevice(LCD_ADDRESS1)) { + Settings.display_address[0] = LCD_ADDRESS1; + Settings.display_model = XDSP_01; + } + else if (I2cSetDevice(LCD_ADDRESS2)) { + Settings.display_address[0] = LCD_ADDRESS2; + Settings.display_model = XDSP_01; + } + } + + if (XDSP_01 == Settings.display_model) { + I2cSetActiveFound(Settings.display_address[0], "LCD"); + + Settings.display_width = Settings.display_cols[0]; + Settings.display_height = Settings.display_rows; + lcd = new LiquidCrystal_I2C(Settings.display_address[0], Settings.display_cols[0], Settings.display_rows); + +#ifdef USE_DISPLAY_MODES1TO5 + DisplayAllocScreenBuffer(); +#endif + + LcdInitMode(); + } +} + +void LcdDrawStringAt(void) +{ + if (dsp_flag) { + dsp_x--; + dsp_y--; + } + lcd->setCursor(dsp_x, dsp_y); + lcd->print(dsp_str); +} + +void LcdDisplayOnOff(uint8_t on) +{ + if (on) { + lcd->backlight(); + } else { + lcd->noBacklight(); + } +} + + + +#ifdef USE_DISPLAY_MODES1TO5 + +void LcdCenter(uint8_t row, char* txt) +{ + char line[Settings.display_cols[0] +2]; + + int len = strlen(txt); + int offset = 0; + if (len >= Settings.display_cols[0]) { + len = Settings.display_cols[0]; + } else { + offset = (Settings.display_cols[0] - len) / 2; + } + memset(line, 0x20, Settings.display_cols[0]); + line[Settings.display_cols[0]] = 0; + for (uint32_t i = 0; i < len; i++) { + line[offset +i] = txt[i]; + } + lcd->setCursor(0, row); + lcd->print(line); +} + +bool LcdPrintLog(void) +{ + bool result = false; + + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + + char* txt = DisplayLogBuffer('\337'); + if (txt != nullptr) { + uint8_t last_row = Settings.display_rows -1; + + for (uint32_t i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + lcd->setCursor(0, i); + lcd->print(disp_screen_buffer[i +1]); + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + + lcd->setCursor(0, last_row); + lcd->print(disp_screen_buffer[last_row]); + + result = true; + } + } + return result; +} + +void LcdTime(void) +{ + char line[Settings.display_cols[0] +1]; + + snprintf_P(line, sizeof(line), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + LcdCenter(0, line); + snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); + LcdCenter(1, line); +} + +void LcdRefresh(void) +{ + if (Settings.display_mode) { + switch (Settings.display_mode) { + case 1: + LcdTime(); + break; + case 2: + case 4: + LcdPrintLog(); + break; + case 3: + case 5: { + if (!LcdPrintLog()) { LcdTime(); } + break; + } + } + } +} + +#endif + + + + + +bool Xdsp01(uint8_t function) +{ + if (!I2cEnabled(XI2C_03)) { return false; } + + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + LcdInitDriver(); + } + else if (XDSP_01 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_INIT: + LcdInit(dsp_init); + break; + case FUNC_DISPLAY_POWER: + LcdDisplayOnOff(disp_power); + break; + case FUNC_DISPLAY_CLEAR: + lcd->clear(); + break; +# 238 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_01_lcd.ino" + case FUNC_DISPLAY_DRAW_STRING: + LcdDrawStringAt(); + break; + case FUNC_DISPLAY_ONOFF: + LcdDisplayOnOff(dsp_on); + break; + + +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + LcdRefresh(); + break; +#endif + } + } + return result; +} + +#endif +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_02_ssd1306.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_02_ssd1306.ino" +#ifdef USE_I2C +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_SSD1306 + +#define XDSP_02 2 +#define XI2C_04 4 + +#define OLED_RESET 4 + +#define SPRINT(A) char str[32];sprintf(str,"val: %d ",A);Serial.println((char*)str); + +#define OLED_ADDRESS1 0x3C +#define OLED_ADDRESS2 0x3D + +#define OLED_BUFFER_COLS 40 +#define OLED_BUFFER_ROWS 16 + +#define OLED_FONT_WIDTH 6 +#define OLED_FONT_HEIGTH 8 + +#include +#include +#include + +Adafruit_SSD1306 *oled1306; + +extern uint8_t *buffer; + + + +void SSD1306InitDriver(void) +{ + if (!Settings.display_model) { + if (I2cSetDevice(OLED_ADDRESS1)) { + Settings.display_address[0] = OLED_ADDRESS1; + Settings.display_model = XDSP_02; + } + else if (I2cSetDevice(OLED_ADDRESS2)) { + Settings.display_address[0] = OLED_ADDRESS2; + Settings.display_model = XDSP_02; + } + } + + if (XDSP_02 == Settings.display_model) { + I2cSetActiveFound(Settings.display_address[0], "SSD1306"); + + if ((Settings.display_width != 64) && (Settings.display_width != 96) && (Settings.display_width != 128)) { + Settings.display_width = 128; + } + if ((Settings.display_height != 16) && (Settings.display_height != 32) && (Settings.display_height != 48) && (Settings.display_height != 64)) { + Settings.display_height = 64; + } + + uint8_t reset_pin = -1; + if (pin[GPIO_OLED_RESET] < 99) { + reset_pin = pin[GPIO_OLED_RESET]; + } + + + if (buffer) { free(buffer); } + buffer = (unsigned char*)calloc((Settings.display_width * Settings.display_height) / 8,1); + if (!buffer) { return; } + + + + oled1306 = new Adafruit_SSD1306(Settings.display_width, Settings.display_height, &Wire, reset_pin); + oled1306->begin(SSD1306_SWITCHCAPVCC, Settings.display_address[0], reset_pin >= 0); + renderer = oled1306; + renderer->DisplayInit(DISPLAY_INIT_MODE, Settings.display_size, Settings.display_rotate, Settings.display_font); + renderer->setTextColor(1,0); + +#ifdef SHOW_SPLASH + renderer->setTextFont(0); + renderer->setTextSize(2); + renderer->setCursor(20,20); + renderer->println(F("SSD1306")); + renderer->Updateframe(); + renderer->DisplayOnff(1); +#endif + + } +} + + +#ifdef USE_DISPLAY_MODES1TO5 + +void Ssd1306PrintLog(void) +{ + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + + char* txt = DisplayLogBuffer('\370'); + if (txt != NULL) { + uint8_t last_row = Settings.display_rows -1; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setCursor(0,0); + for (byte i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + renderer->println(disp_screen_buffer[i]); + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + + renderer->println(disp_screen_buffer[last_row]); + renderer->Updateframe(); + } + } +} + +void Ssd1306Time(void) +{ + char line[12]; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setTextFont(Settings.display_font); + renderer->setCursor(0, 0); + snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + renderer->println(line); + snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); + renderer->println(line); + renderer->Updateframe(); +} + +void Ssd1306Refresh(void) +{ + if (!renderer) return; + + if (Settings.display_mode) { + switch (Settings.display_mode) { + case 1: + Ssd1306Time(); + break; + case 2: + case 3: + case 4: + case 5: + Ssd1306PrintLog(); + break; + } + } +} + +#endif + + + + + +bool Xdsp02(byte function) +{ + if (!I2cEnabled(XI2C_04)) { return false; } + + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + SSD1306InitDriver(); + } + else if (XDSP_02 == Settings.display_model) { + switch (function) { +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + Ssd1306Refresh(); + break; +#endif + case FUNC_DISPLAY_MODEL: + result = true; + break; + } + } + return result; +} + +#endif +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_03_matrix.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_03_matrix.ino" +#ifdef USE_I2C +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_MATRIX + +#define XDSP_03 3 +#define XI2C_05 5 + +#define MTX_MAX_SCREEN_BUFFER 80 + +#include +#include +#include + +Adafruit_8x8matrix *matrix[8]; +uint8_t mtx_matrices = 0; +uint8_t mtx_state = 0; +uint8_t mtx_counter = 0; +int16_t mtx_x = 0; +int16_t mtx_y = 0; + + +char *mtx_buffer = nullptr; + +uint8_t mtx_mode = 0; +uint8_t mtx_loop = 0; +uint8_t mtx_done = 0; + + + +void MatrixWrite(void) +{ + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->writeDisplay(); + } +} + +void MatrixClear(void) +{ + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->clear(); + } + MatrixWrite(); +} + +void MatrixFixed(char* txt) +{ + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->clear(); + matrix[i]->setCursor(-i *8, 0); + matrix[i]->print(txt); + matrix[i]->setBrightness(Settings.display_dimmer); + } + MatrixWrite(); +} + +void MatrixCenter(char* txt) +{ + int offset; + + int len = strlen(txt); + offset = (len < 8) ? offset = ((mtx_matrices *8) - (len *6)) / 2 : 0; + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->clear(); + matrix[i]->setCursor(-(i *8)+offset, 0); + matrix[i]->print(txt); + matrix[i]->setBrightness(Settings.display_dimmer); + } + MatrixWrite(); +} + +void MatrixScrollLeft(char* txt, int loop) +{ + switch (mtx_state) { + case 1: + mtx_state = 2; + + mtx_x = 8 * mtx_matrices; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), txt); + + disp_refresh = Settings.display_refresh; + case 2: + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->clear(); + matrix[i]->setCursor(mtx_x - i *8, 0); + matrix[i]->print(txt); + matrix[i]->setBrightness(Settings.display_dimmer); + } + MatrixWrite(); + + mtx_x--; + int16_t len = strlen(txt); + if (mtx_x < -(len *6)) { mtx_state = loop; } + } + break; + } +} + +void MatrixScrollUp(char* txt, int loop) +{ + int wordcounter = 0; + char tmpbuf[200]; + char *words[100]; + + + + char separators[] = " /"; + + switch (mtx_state) { + case 1: + mtx_state = 2; + + mtx_y = 8; + mtx_counter = 0; + disp_refresh = Settings.display_refresh; + case 2: + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + strlcpy(tmpbuf, txt, sizeof(tmpbuf)); + char *p = strtok(tmpbuf, separators); + while (p != nullptr && wordcounter < 40) { + words[wordcounter++] = p; + p = strtok(nullptr, separators); + } + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->clear(); + for (uint32_t j = 0; j < wordcounter; j++) { + matrix[i]->setCursor(-i *8, mtx_y + (j *8)); + matrix[i]->println(words[j]); + } + matrix[i]->setBrightness(Settings.display_dimmer); + } + MatrixWrite(); + if (((mtx_y %8) == 0) && mtx_counter) { + mtx_counter--; + } else { + mtx_y--; + mtx_counter = STATES * 1; + } + if (mtx_y < -(wordcounter *8)) { mtx_state = loop; } + } + break; + } +} + + + +void MatrixInitMode(void) +{ + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->setRotation(Settings.display_rotate); + matrix[i]->setBrightness(Settings.display_dimmer); + matrix[i]->blinkRate(0); + matrix[i]->setTextWrap(false); + + + matrix[i]->cp437(true); + } + MatrixClear(); +} + +void MatrixInit(uint8_t mode) +{ + switch(mode) { + case DISPLAY_INIT_MODE: + MatrixInitMode(); + break; + case DISPLAY_INIT_PARTIAL: + case DISPLAY_INIT_FULL: + break; + } +} + +void MatrixInitDriver(void) +{ + mtx_buffer = (char*)(malloc(MTX_MAX_SCREEN_BUFFER)); + if (mtx_buffer != nullptr) { + if (!Settings.display_model) { + if (I2cSetDevice(Settings.display_address[1])) { + Settings.display_model = XDSP_03; + } + } + + if (XDSP_03 == Settings.display_model) { + mtx_state = 1; + for (mtx_matrices = 0; mtx_matrices < 8; mtx_matrices++) { + if (Settings.display_address[mtx_matrices]) { + I2cSetActiveFound(Settings.display_address[mtx_matrices], "8x8Matrix"); + matrix[mtx_matrices] = new Adafruit_8x8matrix(); + matrix[mtx_matrices]->begin(Settings.display_address[mtx_matrices]); + } else { + break; + } + } + + Settings.display_width = mtx_matrices * 8; + Settings.display_height = 8; + + MatrixInitMode(); + } + } +} + +void MatrixOnOff(void) +{ + if (!disp_power) { MatrixClear(); } +} + +void MatrixDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) +{ + strlcpy(mtx_buffer, str, MTX_MAX_SCREEN_BUFFER); + mtx_mode = x &1; + mtx_loop = y &1; + if (!mtx_state) { mtx_state = 1; } +} + + + +#ifdef USE_DISPLAY_MODES1TO5 + +void MatrixPrintLog(uint8_t direction) +{ + char* txt = (!mtx_done) ? DisplayLogBuffer('\370') : mtx_buffer; + if (txt != nullptr) { + if (!mtx_state) { mtx_state = 1; } + + if (!mtx_done) { + + uint8_t space = 0; + uint8_t max_cols = (disp_log_buffer_cols < MTX_MAX_SCREEN_BUFFER) ? disp_log_buffer_cols : MTX_MAX_SCREEN_BUFFER; + mtx_buffer[0] = '\0'; + uint8_t i = 0; + while ((txt[i] != '\0') && (i < max_cols)) { + if (txt[i] == ' ') { + space++; + } else { + space = 0; + } + if (space < 2) { + strncat(mtx_buffer, (const char*)txt +i, (strlen(mtx_buffer) < MTX_MAX_SCREEN_BUFFER -1) ? 1 : 0); + } + i++; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), mtx_buffer); + + mtx_done = 1; + } + + if (direction) { + MatrixScrollUp(mtx_buffer, 0); + } else { + MatrixScrollLeft(mtx_buffer, 0); + } + if (!mtx_state) { mtx_done = 0; } + } else { + char disp_time[9]; + + snprintf_P(disp_time, sizeof(disp_time), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + MatrixFixed(disp_time); + } +} + +#endif + +void MatrixRefresh(void) +{ + if (disp_power) { + switch (Settings.display_mode) { + case 0: { + switch (mtx_mode) { + case 0: + MatrixScrollLeft(mtx_buffer, mtx_loop); + break; + case 1: + MatrixScrollUp(mtx_buffer, mtx_loop); + break; + } + break; + } +#ifdef USE_DISPLAY_MODES1TO5 + case 2: { + char disp_date[9]; + snprintf_P(disp_date, sizeof(disp_date), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%02d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year -2000); + MatrixFixed(disp_date); + break; + } + case 3: { + char disp_day[10]; + snprintf_P(disp_day, sizeof(disp_day), PSTR("%d %s"), RtcTime.day_of_month, RtcTime.name_of_month); + MatrixCenter(disp_day); + break; + } + case 4: + MatrixPrintLog(0); + break; + case 1: + case 5: + MatrixPrintLog(1); + break; +#endif + } + } +} + + + + + +bool Xdsp03(uint8_t function) +{ + if (!I2cEnabled(XI2C_05)) { return false; } + + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + MatrixInitDriver(); + } + else if (XDSP_03 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_INIT: + MatrixInit(dsp_init); + break; + case FUNC_DISPLAY_EVERY_50_MSECOND: + MatrixRefresh(); + break; + case FUNC_DISPLAY_POWER: + MatrixOnOff(); + break; + case FUNC_DISPLAY_DRAW_STRING: + MatrixDrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag); + break; + } + } + return result; +} + +#endif +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_04_ili9341.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_04_ili9341.ino" +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_ILI9341 + +#define XDSP_04 4 + +#define TFT_TOP 16 +#define TFT_BOTTOM 16 +#define TFT_FONT_WIDTH 6 +#define TFT_FONT_HEIGTH 8 + +#include +#include +#include + +Adafruit_ILI9341 *tft; + +uint16_t tft_scroll; + + + +void Ili9341InitMode(void) +{ + tft->setRotation(Settings.display_rotate); + tft->invertDisplay(0); + tft->fillScreen(ILI9341_BLACK); + tft->setTextWrap(false); + tft->cp437(true); + if (!Settings.display_mode) { + tft->setCursor(0, 0); + tft->setTextColor(ILI9341_WHITE, ILI9341_BLACK); + tft->setTextSize(1); + } else { + tft->setScrollMargins(TFT_TOP, TFT_BOTTOM); + tft->setCursor(0, 0); + tft->setTextColor(ILI9341_YELLOW, ILI9341_BLACK); + tft->setTextSize(2); + + + tft_scroll = TFT_TOP; + } +} + +void Ili9341Init(uint8_t mode) +{ + switch(mode) { + case DISPLAY_INIT_MODE: + Ili9341InitMode(); +#ifdef USE_DISPLAY_MODES1TO5 + if (Settings.display_rotate) { + DisplayClearScreenBuffer(); + } +#endif + break; + case DISPLAY_INIT_PARTIAL: + case DISPLAY_INIT_FULL: + break; + } +} + +void Ili9341InitDriver(void) +{ + if (!Settings.display_model) { + Settings.display_model = XDSP_04; + } + + if (XDSP_04 == Settings.display_model) { + if (Settings.display_width != ILI9341_TFTWIDTH) { + Settings.display_width = ILI9341_TFTWIDTH; + } + if (Settings.display_height != ILI9341_TFTHEIGHT) { + Settings.display_height = ILI9341_TFTHEIGHT; + } + tft = new Adafruit_ILI9341(pin[GPIO_SPI_CS], pin[GPIO_SPI_DC]); + tft->begin(); + +#ifdef USE_DISPLAY_MODES1TO5 + if (Settings.display_rotate) { + DisplayAllocScreenBuffer(); + } +#endif + + Ili9341InitMode(); + } +} + +void Ili9341Clear(void) +{ + tft->fillScreen(ILI9341_BLACK); + tft->setCursor(0, 0); +} + +void Ili9341DrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) +{ + uint16_t active_color = ILI9341_WHITE; + + tft->setTextSize(Settings.display_size); + if (!flag) { + tft->setCursor(x, y); + } else { + tft->setCursor((x-1) * TFT_FONT_WIDTH * Settings.display_size, (y-1) * TFT_FONT_HEIGTH * Settings.display_size); + } + if (color) { active_color = color; } + tft->setTextColor(active_color, ILI9341_BLACK); + tft->println(str); +} + +void Ili9341DisplayOnOff(uint8_t on) +{ + + + if (pin[GPIO_BACKLIGHT] < 99) { + pinMode(pin[GPIO_BACKLIGHT], OUTPUT); + digitalWrite(pin[GPIO_BACKLIGHT], on); + } +} + +void Ili9341OnOff(void) +{ + Ili9341DisplayOnOff(disp_power); +} + + + +#ifdef USE_DISPLAY_MODES1TO5 + +void Ili9341PrintLog(void) +{ + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + if (Settings.display_rotate) { + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + } + + char* txt = DisplayLogBuffer('\370'); + if (txt != nullptr) { + uint8_t size = Settings.display_size; + uint16_t theight = size * TFT_FONT_HEIGTH; + + tft->setTextSize(size); + tft->setTextColor(ILI9341_CYAN, ILI9341_BLACK); + if (!Settings.display_rotate) { + tft->setCursor(0, tft_scroll); + tft->fillRect(0, tft_scroll, tft->width(), theight, ILI9341_BLACK); + tft->print(txt); + tft_scroll += theight; + if (tft_scroll >= (tft->height() - TFT_BOTTOM)) { + tft_scroll = TFT_TOP; + } + tft->scrollTo(tft_scroll); + } else { + uint8_t last_row = Settings.display_rows -1; + + tft_scroll = theight; + tft->setCursor(0, tft_scroll); + for (uint32_t i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + + tft->print(disp_screen_buffer[i]); + tft_scroll += theight; + tft->setCursor(0, tft_scroll); + delay(1); + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + tft->print(disp_screen_buffer[last_row]); + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), txt); + } + } +} + +void Ili9341Refresh(void) +{ + if (Settings.display_mode) { + char tftdt[Settings.display_cols[0] +1]; + char date4[11]; + char space[Settings.display_cols[0] - 17]; + char time[9]; + + tft->setTextSize(2); + tft->setTextColor(ILI9341_YELLOW, ILI9341_RED); + tft->setCursor(0, 0); + + snprintf_P(date4, sizeof(date4), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); + memset(space, 0x20, sizeof(space)); + space[sizeof(space) -1] = '\0'; + snprintf_P(time, sizeof(time), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + snprintf_P(tftdt, sizeof(tftdt), PSTR("%s%s%s"), date4, space, time); + + tft->print(tftdt); + + switch (Settings.display_mode) { + case 1: + case 2: + case 3: + case 4: + case 5: + Ili9341PrintLog(); + break; + } + } +} + +#endif + + + + + +bool Xdsp04(uint8_t function) +{ + bool result = false; + + if (spi_flg) { + if (FUNC_DISPLAY_INIT_DRIVER == function) { + Ili9341InitDriver(); + } + else if (XDSP_04 == Settings.display_model) { + + if (!dsp_color) { dsp_color = ILI9341_WHITE; } + + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_INIT: + Ili9341Init(dsp_init); + break; + case FUNC_DISPLAY_POWER: + Ili9341OnOff(); + break; + case FUNC_DISPLAY_CLEAR: + Ili9341Clear(); + break; + case FUNC_DISPLAY_DRAW_HLINE: + tft->writeFastHLine(dsp_x, dsp_y, dsp_len, dsp_color); + break; + case FUNC_DISPLAY_DRAW_VLINE: + tft->writeFastVLine(dsp_x, dsp_y, dsp_len, dsp_color); + break; + case FUNC_DISPLAY_DRAW_LINE: + tft->writeLine(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color); + break; + case FUNC_DISPLAY_DRAW_CIRCLE: + tft->drawCircle(dsp_x, dsp_y, dsp_rad, dsp_color); + break; + case FUNC_DISPLAY_FILL_CIRCLE: + tft->fillCircle(dsp_x, dsp_y, dsp_rad, dsp_color); + break; + case FUNC_DISPLAY_DRAW_RECTANGLE: + tft->drawRect(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color); + break; + case FUNC_DISPLAY_FILL_RECTANGLE: + tft->fillRect(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color); + break; + + + + case FUNC_DISPLAY_TEXT_SIZE: + tft->setTextSize(Settings.display_size); + break; + case FUNC_DISPLAY_FONT_SIZE: + + break; + case FUNC_DISPLAY_DRAW_STRING: + Ili9341DrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag); + break; + case FUNC_DISPLAY_ONOFF: + Ili9341DisplayOnOff(dsp_on); + break; + case FUNC_DISPLAY_ROTATION: + tft->setRotation(Settings.display_rotate); + break; +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + Ili9341Refresh(); + break; +#endif + } + } + } + return result; +} + +#endif +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_05_epaper_29.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_05_epaper_29.ino" +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_EPAPER_29 + +#define XDSP_05 5 + +#define EPD_TOP 12 +#define EPD_FONT_HEIGTH 12 + +#define COLORED 1 +#define UNCOLORED 0 + + + +#define USE_TINY_FONT + +#include +#include + + +extern uint8_t *buffer; +uint16_t epd_scroll; + +Epd *epd; + + + +void EpdInitDriver29() +{ + if (!Settings.display_model) { + Settings.display_model = XDSP_05; + } + + if (XDSP_05 == Settings.display_model) { + if (Settings.display_width != EPD_WIDTH) { + Settings.display_width = EPD_WIDTH; + } + if (Settings.display_height != EPD_HEIGHT) { + Settings.display_height = EPD_HEIGHT; + } + + + if (buffer) free(buffer); + buffer=(unsigned char*)calloc((EPD_WIDTH * EPD_HEIGHT) / 8,1); + if (!buffer) return; + + + epd = new Epd(EPD_WIDTH,EPD_HEIGHT); + + + if ((pin[GPIO_SPI_CS] < 99) && (pin[GPIO_SPI_CLK] < 99) && (pin[GPIO_SPI_MOSI] < 99)) { + epd->Begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: HardSPI CS %d, CLK %d, MOSI %d"),pin[GPIO_SPI_CS], pin[GPIO_SPI_CLK], pin[GPIO_SPI_MOSI]); + } + else if ((pin[GPIO_SSPI_CS] < 99) && (pin[GPIO_SSPI_SCLK] < 99) && (pin[GPIO_SSPI_MOSI] < 99)) { + epd->Begin(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: SoftSPI CS %d, CLK %d, MOSI %d"),pin[GPIO_SSPI_CS], pin[GPIO_SSPI_SCLK], pin[GPIO_SSPI_MOSI]); + } else { + free(buffer); + return; + } + + renderer = epd; + epd->Init(DISPLAY_INIT_FULL); + epd->Init(DISPLAY_INIT_PARTIAL); + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + + renderer->setTextColor(1,0); + +#ifdef SHOW_SPLASH + + renderer->setTextFont(1); + renderer->DrawStringAt(50, 50, "Waveshare E-Paper Display!", COLORED,0); + renderer->Updateframe(); + delay(1000); + renderer->fillScreen(0); +#endif + + } +} + + + + + + + +#ifdef USE_DISPLAY_MODES1TO5 +#define EPD_FONT_HEIGTH 12 +void EpdPrintLog29(void) +{ + + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + + + char* txt = DisplayLogBuffer('\040'); + if (txt != nullptr) { + uint8_t size = Settings.display_size; + uint16_t theight = size * EPD_FONT_HEIGTH; + + renderer->setTextFont(size); + uint8_t last_row = Settings.display_rows -1; + + + epd_scroll = 0; + for (uint32_t i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + renderer->DrawStringAt(0, epd_scroll, disp_screen_buffer[i], COLORED, 0); + epd_scroll += theight; + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + renderer->DrawStringAt(0, epd_scroll, disp_screen_buffer[last_row], COLORED, 0); + + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), txt); + } + } +} + +void EpdRefresh29(void) +{ + if (Settings.display_mode) { + + if (!renderer) return; +# 165 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_05_epaper_29.ino" + switch (Settings.display_mode) { + case 1: + case 2: + case 3: + case 4: + case 5: + EpdPrintLog29(); + renderer->Updateframe(); + break; + } + + + } +} + +#endif + + + + + +bool Xdsp05(uint8_t function) +{ + bool result = false; + if (FUNC_DISPLAY_INIT_DRIVER == function) { + EpdInitDriver29(); + } + else if (XDSP_05 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + EpdRefresh29(); + break; +#endif + } + } + return result; +} + +#endif +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_06_epaper_42.ino" +# 21 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_06_epaper_42.ino" +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_EPAPER_42 + +#define XDSP_06 6 + +#define COLORED42 1 +#define UNCOLORED42 0 + + + +#define USE_TINY_FONT + +#include +#include + +extern uint8_t *buffer; + +Epd42 *epd42; + + + + +void EpdInitDriver42() +{ + if (!Settings.display_model) { + Settings.display_model = XDSP_06; + } + + if (XDSP_06 == Settings.display_model) { + + if (Settings.display_width != EPD_WIDTH42) { + Settings.display_width = EPD_WIDTH42; + } + if (Settings.display_height != EPD_HEIGHT42) { + Settings.display_height = EPD_HEIGHT42; + } + + + if (buffer) free(buffer); + buffer=(unsigned char*)calloc((EPD_WIDTH42 * EPD_HEIGHT42) / 8,1); + if (!buffer) return; + + + epd42 = new Epd42(EPD_WIDTH42,EPD_HEIGHT42); + + #ifdef USE_SPI + if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)) { + epd42->Begin(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); + } else { + free(buffer); + return; + } + #else + if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)) { + epd42->Begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); + } else { + free(buffer); + return; + } + #endif + + renderer = epd42; + + epd42->Init(); + + renderer->fillScreen(0); + + + epd42->Init(DISPLAY_INIT_FULL); + + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + + epd42->ClearFrame(); + renderer->Updateframe(); + delay(3000); + renderer->setTextColor(1,0); + +#ifdef SHOW_SPLASH + + renderer->setTextFont(2); + renderer->DrawStringAt(50, 140, "Waveshare E-Paper!", COLORED42,0); + renderer->Updateframe(); + delay(350); + renderer->fillScreen(0); +#endif + + } +} + + + + + + + +#ifdef USE_DISPLAY_MODES1TO5 + +void EpdRefresh42() +{ + if (Settings.display_mode) { + + } +} + +#endif + + + + + + +bool Xdsp06(uint8_t function) +{ + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + EpdInitDriver42(); + } + else if (XDSP_06 == Settings.display_model) { + + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + EpdRefresh42(); + break; +#endif + } + } + return result; +} + + +#endif +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_07_sh1106.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_07_sh1106.ino" +#ifdef USE_I2C +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_SH1106 + +#define OLED_RESET 4 + +#define SPRINT(A) char str[32];sprintf(str,"val: %d ",A);Serial.println((char*)str); + +extern uint8_t *buffer; + +#define XDSP_07 7 +#define XI2C_06 6 + +#define OLED_ADDRESS1 0x3C +#define OLED_ADDRESS2 0x3D + +#define OLED_BUFFER_COLS 40 +#define OLED_BUFFER_ROWS 16 + +#define OLED_FONT_WIDTH 6 +#define OLED_FONT_HEIGTH 8 + +#include +#include +#include + +Adafruit_SH1106 *oled1106; + + + + +void SH1106InitDriver() +{ + if (!Settings.display_model) { + if (I2cSetDevice(OLED_ADDRESS1)) { + Settings.display_address[0] = OLED_ADDRESS1; + Settings.display_model = XDSP_07; + } + else if (I2cSetDevice(OLED_ADDRESS2)) { + Settings.display_address[0] = OLED_ADDRESS2; + Settings.display_model = XDSP_07; + } + } + + if (XDSP_07 == Settings.display_model) { + I2cSetActiveFound(Settings.display_address[0], "SH1106"); + + if (Settings.display_width != SH1106_LCDWIDTH) { + Settings.display_width = SH1106_LCDWIDTH; + } + if (Settings.display_height != SH1106_LCDHEIGHT) { + Settings.display_height = SH1106_LCDHEIGHT; + } + + + if (buffer) free(buffer); + buffer=(unsigned char*)calloc((SH1106_LCDWIDTH * SH1106_LCDHEIGHT) / 8,1); + if (!buffer) return; + + + oled1106 = new Adafruit_SH1106(SH1106_LCDWIDTH,SH1106_LCDHEIGHT); + renderer=oled1106; + renderer->Begin(SH1106_SWITCHCAPVCC, Settings.display_address[0],0); + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + renderer->setTextColor(1,0); + +#ifdef SHOW_SPLASH + renderer->setTextFont(0); + renderer->setTextSize(2); + renderer->setCursor(20,20); + renderer->println(F("SH1106")); + renderer->Updateframe(); + renderer->DisplayOnff(1); +#endif + } +} + + + +#ifdef USE_DISPLAY_MODES1TO5 + +void SH1106PrintLog(void) +{ + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + + char* txt = DisplayLogBuffer('\370'); + if (txt != NULL) { + uint8_t last_row = Settings.display_rows -1; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setCursor(0,0); + for (byte i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + renderer->println(disp_screen_buffer[i]); + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + + renderer->println(disp_screen_buffer[last_row]); + renderer->Updateframe(); + } + } +} + +void SH1106Time(void) +{ + char line[12]; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setTextFont(Settings.display_font); + renderer->setCursor(0, 0); + snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + renderer->println(line); + snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); + renderer->println(line); + renderer->Updateframe(); +} + +void SH1106Refresh(void) +{ + if (!renderer) return; + if (Settings.display_mode) { + switch (Settings.display_mode) { + case 1: + SH1106Time(); + break; + case 2: + case 3: + case 4: + case 5: + SH1106PrintLog(); + break; + } + } +} + +#endif + + + + + +bool Xdsp07(uint8_t function) +{ + if (!I2cEnabled(XI2C_06)) { return false; } + + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + SH1106InitDriver(); + } + else if (XDSP_07 == Settings.display_model) { + + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + SH1106Refresh(); + break; +#endif + } + } + return result; +} + +#endif +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_08_ILI9488.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_08_ILI9488.ino" +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_ILI9488 + +#define XDSP_08 8 +#define XI2C_38 38 + +#define COLORED 1 +#define UNCOLORED 0 + + +#define FT6236_address 0x38 + + + +#define USE_TINY_FONT + + +#include +#include + +TouchLocation ili9488_pLoc; +uint8_t ili9488_ctouch_counter = 0; + + +#define BACKPLANE_PIN 2 + +extern uint8_t *buffer; +extern uint8_t color_type; +ILI9488 *ili9488; + +#ifdef USE_TOUCH_BUTTONS +extern VButton *buttons[]; +#endif + +extern const uint16_t picture[]; +uint8_t FT6236_found; + + + +void ILI9488_InitDriver() +{ + if (!Settings.display_model) { + Settings.display_model = XDSP_08; + } + + if (XDSP_08 == Settings.display_model) { + + if (Settings.display_width != ILI9488_TFTWIDTH) { + Settings.display_width = ILI9488_TFTWIDTH; + } + if (Settings.display_height != ILI9488_TFTHEIGHT) { + Settings.display_height = ILI9488_TFTHEIGHT; + } + + + buffer=NULL; + + + fg_color = ILI9488_WHITE; + bg_color = ILI9488_BLACK; + + uint8_t bppin=BACKPLANE_PIN; + if (pin[GPIO_BACKLIGHT]<99) { + bppin=pin[GPIO_BACKLIGHT]; + } + + + if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)){ + ili9488 = new ILI9488(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK],bppin); + } else { + if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)) { + ili9488 = new ILI9488(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK],bppin); + } else { + return; + } + } + + SPI.begin(); + ili9488->begin(); + renderer = ili9488; + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + +#ifdef SHOW_SPLASH + + renderer->setTextFont(2); + renderer->setTextColor(ILI9488_WHITE,ILI9488_BLACK); + renderer->DrawStringAt(50, 50, "ILI9488 TFT Display!", ILI9488_WHITE,0); + delay(1000); + + +#endif + + color_type = COLOR_COLOR; + + + if (I2cEnabled(XI2C_38) && I2cSetDevice(FT6236_address)) { + FT6236begin(FT6236_address); + FT6236_found=1; + I2cSetActiveFound(FT6236_address, "FT6236"); + } else { + FT6236_found=0; + } + + } +} + +#ifdef USE_TOUCH_BUTTONS +void ILI9488_MQTT(uint8_t count,const char *cp) { + ResponseTime_P(PSTR(",\"RA8876\":{\"%s%d\":\"%d\"}}"), cp,count+1,(buttons[count]->vpower&0x80)>>7); + MqttPublishTeleSensor(); +} + +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; +} + +void FT6236Check() { +uint16_t temp; +uint8_t rbutt=0,vbutt=0; +ili9488_ctouch_counter++; +if (2 == ili9488_ctouch_counter) { + + ili9488_ctouch_counter=0; + if (FT6236readTouchLocation(&ili9488_pLoc,1)) { + + if (renderer) { + uint8_t rot=renderer->getRotation(); + switch (rot) { + case 0: + temp=ili9488_pLoc.y; + ili9488_pLoc.y=renderer->height()-ili9488_pLoc.x; + ili9488_pLoc.x=temp; + break; + case 1: + break; + case 2: + break; + case 3: + temp=ili9488_pLoc.y; + ili9488_pLoc.y=ili9488_pLoc.x; + ili9488_pLoc.x=renderer->width()-temp; + break; + } + + for (uint8_t count=0; countvpower&0x7f; + if (buttons[count]->contains(ili9488_pLoc.x,ili9488_pLoc.y)) { + + buttons[count]->press(true); + if (buttons[count]->justPressed()) { + if (!bflags) { + 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); + } + } else { + + const char *cp; + if (bflags==1) { + + buttons[count]->vpower^=0x80; + cp="TBT"; + } else { + + buttons[count]->vpower|=0x80; + cp="PBT"; + } + buttons[count]->xdrawButton(buttons[count]->vpower&0x80); + ILI9488_MQTT(count,cp); + } + } + } + if (!bflags) { + rbutt++; + } else { + vbutt++; + } + } + } + } + } else { + + for (uint8_t count=0; countvpower&0x7f; + buttons[count]->press(false); + if (buttons[count]->justReleased()) { + uint8_t bflags=buttons[count]->vpower&0x7f; + if (bflags>0) { + if (bflags>1) { + + buttons[count]->vpower&=0x7f; + ILI9488_MQTT(count,"PBT"); + } + buttons[count]->xdrawButton(buttons[count]->vpower&0x80); + } + } + if (!bflags) { + + uint8_t pwr=bitRead(power,rbutt); + uint8_t vpwr=(buttons[count]->vpower&0x80)>>7; + if (pwr!=vpwr) { + ILI9488_RDW_BUTT(count,pwr); + } + rbutt++; + } + } + } + ili9488_pLoc.x=0; + ili9488_pLoc.y=0; + } +} +} +#endif + + + + +bool Xdsp08(uint8_t function) +{ + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + ILI9488_InitDriver(); + } + else if (XDSP_08 == Settings.display_model) { + + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_EVERY_50_MSECOND: +#ifdef USE_TOUCH_BUTTONS + if (FT6236_found) FT6236Check(); +#endif + break; + } + } + + return result; +} + +#endif +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_09_SSD1351.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_09_SSD1351.ino" +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_SSD1351 + +#define XDSP_09 9 + +#define COLORED 1 +#define UNCOLORED 0 + + + + +#define USE_TINY_FONT + +#include + +extern uint8_t *buffer; +extern uint8_t color_type; +SSD1351 *ssd1351; + + + +void SSD1351_InitDriver() { + if (!Settings.display_model) { + Settings.display_model = XDSP_09; + } + + if (XDSP_09 == Settings.display_model) { + + if (Settings.display_width != SSD1351_WIDTH) { + Settings.display_width = SSD1351_WIDTH; + } + if (Settings.display_height != SSD1351_HEIGHT) { + Settings.display_height = SSD1351_HEIGHT; + } + + buffer=0; + + + fg_color = SSD1351_WHITE; + bg_color = SSD1351_BLACK; + + + if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)){ + ssd1351 = new SSD1351(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); + } else { + if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)){ + ssd1351 = new SSD1351(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); + } else { + return; + } + } + + delay(100); + SPI.begin(); + ssd1351->begin(); + renderer = ssd1351; + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + renderer->dim(Settings.display_dimmer); + +#ifdef SHOW_SPLASH + + renderer->setTextFont(2); + renderer->setTextColor(SSD1351_WHITE,SSD1351_BLACK); + renderer->DrawStringAt(10, 60, "SSD1351", SSD1351_RED,0); + delay(1000); + +#endif + color_type = COLOR_COLOR; + } +} + +#ifdef USE_DISPLAY_MODES1TO5 + +void SSD1351PrintLog(void) +{ + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + + char* txt = DisplayLogBuffer('\370'); + if (txt != NULL) { + uint8_t last_row = Settings.display_rows -1; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setCursor(0,0); + for (byte i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + renderer->println(disp_screen_buffer[i]); + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + + renderer->println(disp_screen_buffer[last_row]); + renderer->Updateframe(); + } + } +} + +void SSD1351Time(void) +{ + char line[12]; + + renderer->clearDisplay(); + renderer->setTextSize(2); + renderer->setCursor(0, 0); + snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + renderer->println(line); + snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); + renderer->println(line); + renderer->Updateframe(); +} + +void SSD1351Refresh(void) +{ + if (Settings.display_mode) { + switch (Settings.display_mode) { + case 1: + SSD1351Time(); + break; + case 2: + case 3: + case 4: + case 5: + SSD1351PrintLog(); + break; + } + } +} + +#endif + + + + +bool Xdsp09(uint8_t function) +{ + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + SSD1351_InitDriver(); + } + else if (XDSP_09 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + SSD1351Refresh(); + break; +#endif + } + } + return result; +} +#endif +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_10_RA8876.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_10_RA8876.ino" +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_RA8876 + +#define XDSP_10 10 +#define XI2C_39 39 + +#define COLORED 1 +#define UNCOLORED 0 + + +#define FT5316_address 0x38 + + + +#define USE_TINY_FONT + +#include +#include + +TouchLocation ra8876_pLoc; +uint8_t ra8876_ctouch_counter = 0; + +#ifdef USE_TOUCH_BUTTONS +extern VButton *buttons[]; +#endif + +extern uint8_t *buffer; +extern uint8_t color_type; +RA8876 *ra8876; + +uint8_t FT5316_found; + + +void RA8876_InitDriver() +{ + if (!Settings.display_model) { + Settings.display_model = XDSP_10; + } + + if (XDSP_10 == Settings.display_model) { + + if (Settings.display_width != RA8876_TFTWIDTH) { + Settings.display_width = RA8876_TFTWIDTH; + } + if (Settings.display_height != RA8876_TFTHEIGHT) { + Settings.display_height = RA8876_TFTHEIGHT; + } + buffer=0; + + + fg_color = RA8876_WHITE; + bg_color = RA8876_BLACK; + + + if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]==13) && (pin[GPIO_SSPI_MISO]==12) && (pin[GPIO_SSPI_SCLK]==14)) { + ra8876 = new RA8876(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_MISO],pin[GPIO_SSPI_SCLK],pin[GPIO_BACKLIGHT]); + } else { + if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]==13) && (pin[GPIO_SPI_MISO]==12) && (pin[GPIO_SPI_CLK]==14)) { + ra8876 = new RA8876(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_MISO],pin[GPIO_SPI_CLK],pin[GPIO_BACKLIGHT]); + } else { + return; + } + } + + ra8876->begin(); + renderer = ra8876; + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + renderer->dim(Settings.display_dimmer); + + +#ifdef SHOW_SPLASH + + renderer->setTextFont(2); + renderer->setTextColor(RA8876_WHITE,RA8876_BLACK); + renderer->DrawStringAt(600, 300, "RA8876", RA8876_RED,0); + delay(1000); + +#endif + color_type = COLOR_COLOR; + + if (I2cEnabled(XI2C_39) && I2cSetDevice(FT5316_address)) { + FT6236begin(FT5316_address); + FT5316_found=1; + I2cSetActiveFound(FT5316_address, "FT5316"); + } else { + FT5316_found=0; + } + + } +} + +#ifdef USE_TOUCH_BUTTONS +void RA8876_MQTT(uint8_t count,const char *cp) { + ResponseTime_P(PSTR(",\"RA8876\":{\"%s%d\":\"%d\"}}"), cp,count+1,(buttons[count]->vpower&0x80)>>7); + MqttPublishTeleSensor(); +} + +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; +} + + +void FT5316Check() { +uint16_t temp; +uint8_t rbutt=0,vbutt=0; +ra8876_ctouch_counter++; +if (2 == ra8876_ctouch_counter) { + + ra8876_ctouch_counter=0; + + if (FT6236readTouchLocation(&ra8876_pLoc,1)) { + ra8876_pLoc.x=ra8876_pLoc.x*RA8876_TFTWIDTH/800; + ra8876_pLoc.y=ra8876_pLoc.y*RA8876_TFTHEIGHT/480; + + + if (renderer) { + + + ra8876_pLoc.x=RA8876_TFTWIDTH-ra8876_pLoc.x; + ra8876_pLoc.y=RA8876_TFTHEIGHT-ra8876_pLoc.y; +# 170 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_10_RA8876.ino" + for (uint8_t count=0; countvpower&0x7f; + if (buttons[count]->contains(ra8876_pLoc.x,ra8876_pLoc.y)) { + + buttons[count]->press(true); + if (buttons[count]->justPressed()) { + if (!bflags) { + + 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); + } + } else { + + const char *cp; + if (bflags==1) { + + buttons[count]->vpower^=0x80; + cp="TBT"; + } else { + + buttons[count]->vpower|=0x80; + cp="PBT"; + } + buttons[count]->xdrawButton(buttons[count]->vpower&0x80); + RA8876_MQTT(count,cp); + } + } + } + if (!bflags) { + rbutt++; + } else { + vbutt++; + } + } + } + } + } else { + + for (uint8_t count=0; countvpower&0x7f; + buttons[count]->press(false); + if (buttons[count]->justReleased()) { + if (bflags>0) { + if (bflags>1) { + + buttons[count]->vpower&=0x7f; + RA8876_MQTT(count,"PBT"); + } + buttons[count]->xdrawButton(buttons[count]->vpower&0x80); + } + } + if (!bflags) { + + uint8_t pwr=bitRead(power,rbutt); + uint8_t vpwr=(buttons[count]->vpower&0x80)>>7; + if (pwr!=vpwr) { + RA8876_RDW_BUTT(count,pwr); + } + rbutt++; + } + } + } + ra8876_pLoc.x=0; + ra8876_pLoc.y=0; + } +} +} +#endif +# 426 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_10_RA8876.ino" +bool Xdsp10(uint8_t function) +{ + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + RA8876_InitDriver(); + } + else if (XDSP_10 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_EVERY_50_MSECOND: +#ifdef USE_TOUCH_BUTTONS + if (FT5316_found) FT5316Check(); +#endif + break; + } + } + return result; +} +#endif +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_11_sevenseg.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_11_sevenseg.ino" +#ifdef USE_I2C +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_SEVENSEG + +#define XDSP_11 11 +#define XI2C_47 47 + +#include +#include +#include + +Adafruit_7segment sevenseg = Adafruit_7segment(); + +uint8_t sevenseg_state = 0; + + + +void SevensegWrite(void) +{ + sevenseg.writeDisplay(); +} + +void SevensegClear(void) +{ + sevenseg.clear(); + SevensegWrite(); +} + + + + +void SevensegInitMode(void) +{ + sevenseg.setBrightness(Settings.display_dimmer); + sevenseg.blinkRate(0); + SevensegClear(); +} + +void SevensegInit(uint8_t mode) +{ + switch(mode) { + case DISPLAY_INIT_MODE: + case DISPLAY_INIT_PARTIAL: + case DISPLAY_INIT_FULL: + SevensegInitMode(); + break; + } +} + +void SevensegInitDriver(void) +{ + if (!Settings.display_model) { + if (I2cSetDevice(SEVENSEG_ADDRESS1)) { + Settings.display_model = XDSP_11; + } + } + + if (XDSP_11 == Settings.display_model) { + sevenseg_state = 1; + sevenseg.begin(SEVENSEG_ADDRESS1); + + Settings.display_width = 4; + Settings.display_height = 1; + + SevensegInitMode(); + } +} + +void SevensegOnOff(void) +{ + if (!disp_power) { SevensegClear(); } +} + +void SevensegDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) +{ + uint16_t number = 0; + boolean hasnumber= false; + uint8_t dots= 0; + boolean t=false; + boolean T=false; + boolean d=false; + boolean hex=false; + boolean done=false; + boolean s=false; + for (int i=0; (str[i]!='\0') && (!done); i++) { +# 113 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_11_sevenseg.ino" + switch (str[i]) { + case 'x': + hex = true; + break; + case ':': + dots |= 0x02; + break; + case '^': + dots |= 0x08; + break; + case 'v': + dots |= 0x04; + break; + case '.': + dots |= 0x10; + break; + case 'T': + t = true; + break; + case 't': + T = true; + break; + case 's': + s = true; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + hasnumber= true; + number = atoi(str+i); + done = true; + break; + default: + break; + } + } + + if (s) { + + hex = false; + int hour = number/60/60; + int minute = (number/60)%60; + + if (hour) { + + number = hour*100 + minute; + } else { + + number = minute*100 + number%60; + } + } + + if (hasnumber) { + if (hex) { + sevenseg.print(number, HEX); + } else { + sevenseg.print(number, DEC); + } + } + + if (dots) { + sevenseg.writeDigitRaw(2, dots); + } + + sevenseg.writeDisplay(); +} + + + +#ifdef USE_DISPLAY_MODES1TO5 +void SevensegTime(boolean time_24) +{ + + uint hours = RtcTime.hour; + uint minutes = RtcTime.minute; + uint second = RtcTime.second; + uint16_t displayValue = hours * 100 + minutes; + uint16_t dots = 0; + + + if (!time_24) { + + if (hours > 12) { + displayValue -= 1200; + } + + else if (hours == 0) { + displayValue += 1200; + } + } + + + + sevenseg.print(displayValue, DEC); + + + + + if (time_24) { + if (hours == 0) { + + sevenseg.writeDigitNum(1, 0); + + if (minutes < 10) { + sevenseg.writeDigitNum(3, 0); + } + } + if (hours < 10) { + + sevenseg.writeDigitNum(0, 0); + } + } else { + + if (hours >= 12) { + dots |= 0x10; + } + } + + sevenseg.writeDigitRaw(2, dots |= ((second%2) << 1)); + sevenseg.writeDisplay(); +} + +#endif + +void SevensegRefresh(void) +{ + if (disp_power) { + if (Settings.display_mode) { + switch (Settings.display_mode) { + case 1: + SevensegTime(false); + break; + case 2: + SevensegTime(true); + break; + case 4: + case 3: + case 5: { + break; + } + } + } + } +} + + + + + +bool Xdsp11(uint8_t function) +{ + if (!I2cEnabled(XI2C_47)) { return false; } + + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + SevensegInitDriver(); + } + else if (XDSP_11 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_INIT: + SevensegInit(dsp_init); + break; + case FUNC_DISPLAY_CLEAR: + SevensegClear(); + break; + case FUNC_DISPLAY_EVERY_SECOND: + SevensegRefresh(); + break; + case FUNC_DISPLAY_ONOFF: + case FUNC_DISPLAY_POWER: + SevensegOnOff(); + break; + case FUNC_DISPLAY_DRAW_STRING: + SevensegDrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag); + break; + } + } + return result; +} + +#endif +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_interface.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_interface.ino" +#if defined(USE_I2C) || defined(USE_SPI) +#ifdef USE_DISPLAY + +#ifdef XFUNC_PTR_IN_ROM +bool (* const xdsp_func_ptr[])(uint8_t) PROGMEM = { +#else +bool (* const xdsp_func_ptr[])(uint8_t) = { +#endif + +#ifdef XDSP_01 + &Xdsp01, +#endif + +#ifdef XDSP_02 + &Xdsp02, +#endif + +#ifdef XDSP_03 + &Xdsp03, +#endif + +#ifdef XDSP_04 + &Xdsp04, +#endif + +#ifdef XDSP_05 + &Xdsp05, +#endif + +#ifdef XDSP_06 + &Xdsp06, +#endif + +#ifdef XDSP_07 + &Xdsp07, +#endif + +#ifdef XDSP_08 + &Xdsp08, +#endif + +#ifdef XDSP_09 + &Xdsp09, +#endif + +#ifdef XDSP_10 + &Xdsp10, +#endif + +#ifdef XDSP_11 + &Xdsp11, +#endif + +#ifdef XDSP_12 + &Xdsp12, +#endif + +#ifdef XDSP_13 + &Xdsp13, +#endif + +#ifdef XDSP_14 + &Xdsp14, +#endif + +#ifdef XDSP_15 + &Xdsp15, +#endif + +#ifdef XDSP_16 + &Xdsp16 +#endif +}; + +const uint8_t xdsp_present = sizeof(xdsp_func_ptr) / sizeof(xdsp_func_ptr[0]); +# 118 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_interface.ino" +uint8_t XdspPresent(void) +{ + return xdsp_present; +} + +bool XdspCall(uint8_t Function) +{ + bool result = false; + + DEBUG_TRACE_LOG(PSTR("DSP: %d"), Function); + + for (uint32_t x = 0; x < xdsp_present; x++) { + result = xdsp_func_ptr[x](Function); + + if (result && (FUNC_DISPLAY_MODEL == Function)) { + break; + } + } + + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_01_ws2812.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_01_ws2812.ino" +#ifdef USE_LIGHT +#ifdef USE_WS2812 +# 38 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_01_ws2812.ino" +#define XLGT_01 1 + +const uint8_t WS2812_SCHEMES = 8; + +const char kWs2812Commands[] PROGMEM = "|" + D_CMND_LED "|" D_CMND_PIXELS "|" D_CMND_ROTATION "|" D_CMND_WIDTH ; + +void (* const Ws2812Command[])(void) PROGMEM = { + &CmndLed, &CmndPixels, &CmndRotation, &CmndWidth }; + +#include + +#if (USE_WS2812_CTYPE == NEO_GRB) + typedef NeoGrbFeature selectedNeoFeatureType; +#elif (USE_WS2812_CTYPE == NEO_BRG) + typedef NeoBrgFeature selectedNeoFeatureType; +#elif (USE_WS2812_CTYPE == NEO_RBG) + typedef NeoRbgFeature selectedNeoFeatureType; +#elif (USE_WS2812_CTYPE == NEO_RGBW) + typedef NeoRgbwFeature selectedNeoFeatureType; +#elif (USE_WS2812_CTYPE == NEO_GRBW) + typedef NeoGrbwFeature selectedNeoFeatureType; +#else + typedef NeoRgbFeature selectedNeoFeatureType; +#endif + +#ifdef USE_WS2812_DMA + + +#if (USE_WS2812_HARDWARE == NEO_HW_WS2812X) + typedef NeoEsp8266DmaWs2812xMethod selectedNeoSpeedType; +#elif (USE_WS2812_HARDWARE == NEO_HW_SK6812) + typedef NeoEsp8266DmaSk6812Method selectedNeoSpeedType; +#elif (USE_WS2812_HARDWARE == NEO_HW_APA106) + typedef NeoEsp8266DmaApa106Method selectedNeoSpeedType; +#else + typedef NeoEsp8266Dma800KbpsMethod selectedNeoSpeedType; +#endif + +#else + + +#if (USE_WS2812_HARDWARE == NEO_HW_WS2812X) + typedef NeoEsp8266BitBangWs2812xMethod selectedNeoSpeedType; +#elif (USE_WS2812_HARDWARE == NEO_HW_SK6812) + typedef NeoEsp8266BitBangSk6812Method selectedNeoSpeedType; +#else + typedef NeoEsp8266BitBang800KbpsMethod selectedNeoSpeedType; +#endif + +#endif + +NeoPixelBus *strip = nullptr; + +struct WsColor { + uint8_t red, green, blue; +}; + +struct ColorScheme { + WsColor* colors; + uint8_t count; +}; + +WsColor kIncandescent[2] = { 255,140,20, 0,0,0 }; +WsColor kRgb[3] = { 255,0,0, 0,255,0, 0,0,255 }; +WsColor kChristmas[2] = { 255,0,0, 0,255,0 }; +WsColor kHanukkah[2] = { 0,0,255, 255,255,255 }; +WsColor kwanzaa[3] = { 255,0,0, 0,0,0, 0,255,0 }; +WsColor kRainbow[7] = { 255,0,0, 255,128,0, 255,255,0, 0,255,0, 0,0,255, 128,0,255, 255,0,255 }; +WsColor kFire[3] = { 255,0,0, 255,102,0, 255,192,0 }; +ColorScheme kSchemes[WS2812_SCHEMES -1] = { + kIncandescent, 2, + kRgb, 3, + kChristmas, 2, + kHanukkah, 2, + kwanzaa, 3, + kRainbow, 7, + kFire, 3 }; + +uint8_t kWidth[5] = { + 1, + 2, + 4, + 8, + 255 }; +uint8_t kWsRepeat[5] = { + 8, + 6, + 4, + 2, + 1 }; + +struct WS2812 { + uint8_t show_next = 1; + uint8_t scheme_offset = 0; + bool suspend_update = false; +} Ws2812; + + + +void Ws2812StripShow(void) +{ +#if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor c; +#else + RgbColor c; +#endif + + if (Settings.light_correction) { + for (uint32_t i = 0; i < Settings.light_pixels; i++) { + c = strip->GetPixelColor(i); + c.R = ledGamma(c.R); + c.G = ledGamma(c.G); + c.B = ledGamma(c.B); +#if (USE_WS2812_CTYPE > NEO_3LED) + c.W = ledGamma(c.W); +#endif + strip->SetPixelColor(i, c); + } + } + strip->Show(); +} + +int mod(int a, int b) +{ + int ret = a % b; + if (ret < 0) ret += b; + return ret; +} + +void Ws2812UpdatePixelColor(int position, struct WsColor hand_color, float offset) +{ +#if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor color; +#else + RgbColor color; +#endif + + uint32_t mod_position = mod(position, (int)Settings.light_pixels); + + color = strip->GetPixelColor(mod_position); + float dimmer = 100 / (float)Settings.light_dimmer; + color.R = tmin(color.R + ((hand_color.red / dimmer) * offset), 255); + color.G = tmin(color.G + ((hand_color.green / dimmer) * offset), 255); + color.B = tmin(color.B + ((hand_color.blue / dimmer) * offset), 255); + strip->SetPixelColor(mod_position, color); +} + +void Ws2812UpdateHand(int position, uint32_t index) +{ + uint32_t width = Settings.light_width; + if (index < WS_MARKER) { width = Settings.ws_width[index]; } + if (!width) { return; } + + position = (position + Settings.light_rotation) % Settings.light_pixels; + + if (Settings.flag.ws_clock_reverse) { + position = Settings.light_pixels -position; + } + WsColor hand_color = { Settings.ws_color[index][WS_RED], Settings.ws_color[index][WS_GREEN], Settings.ws_color[index][WS_BLUE] }; + + Ws2812UpdatePixelColor(position, hand_color, 1); + + uint32_t range = ((width -1) / 2) +1; + for (uint32_t h = 1; h < range; h++) { + float offset = (float)(range - h) / (float)range; + Ws2812UpdatePixelColor(position -h, hand_color, offset); + Ws2812UpdatePixelColor(position +h, hand_color, offset); + } +} + +void Ws2812Clock(void) +{ + strip->ClearTo(0); + int clksize = 60000 / (int)Settings.light_pixels; + + Ws2812UpdateHand((RtcTime.second * 1000) / clksize, WS_SECOND); + Ws2812UpdateHand((RtcTime.minute * 1000) / clksize, WS_MINUTE); + Ws2812UpdateHand((((RtcTime.hour % 12) * 5000) + ((RtcTime.minute * 1000) / 12 )) / clksize, WS_HOUR); + if (Settings.ws_color[WS_MARKER][WS_RED] + Settings.ws_color[WS_MARKER][WS_GREEN] + Settings.ws_color[WS_MARKER][WS_BLUE]) { + for (uint32_t i = 0; i < 12; i++) { + Ws2812UpdateHand((i * 5000) / clksize, WS_MARKER); + } + } + + Ws2812StripShow(); +} + +void Ws2812GradientColor(uint32_t schemenr, struct WsColor* mColor, uint32_t range, uint32_t gradRange, uint32_t i) +{ + + + + + ColorScheme scheme = kSchemes[schemenr]; + uint32_t curRange = i / range; + uint32_t rangeIndex = i % range; + uint32_t colorIndex = rangeIndex / gradRange; + uint32_t start = colorIndex; + uint32_t end = colorIndex +1; + if (curRange % 2 != 0) { + start = (scheme.count -1) - start; + end = (scheme.count -1) - end; + } + float dimmer = 100 / (float)Settings.light_dimmer; + float fmyRed = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].red, scheme.colors[end].red) / dimmer; + float fmyGrn = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].green, scheme.colors[end].green) / dimmer; + float fmyBlu = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].blue, scheme.colors[end].blue) / dimmer; + mColor->red = (uint8_t)fmyRed; + mColor->green = (uint8_t)fmyGrn; + mColor->blue = (uint8_t)fmyBlu; +} + +void Ws2812Gradient(uint32_t schemenr) +{ + + + + + +#if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor c; + c.W = 0; +#else + RgbColor c; +#endif + + ColorScheme scheme = kSchemes[schemenr]; + if (scheme.count < 2) { return; } + + uint32_t repeat = kWsRepeat[Settings.light_width]; + uint32_t range = (uint32_t)ceil((float)Settings.light_pixels / (float)repeat); + uint32_t gradRange = (uint32_t)ceil((float)range / (float)(scheme.count - 1)); + uint32_t speed = ((Settings.light_speed * 2) -1) * (STATES / 10); + uint32_t offset = speed > 0 ? Light.strip_timer_counter / speed : 0; + + WsColor oldColor, currentColor; + Ws2812GradientColor(schemenr, &oldColor, range, gradRange, offset); + currentColor = oldColor; + for (uint32_t i = 0; i < Settings.light_pixels; i++) { + if (kWsRepeat[Settings.light_width] > 1) { + Ws2812GradientColor(schemenr, ¤tColor, range, gradRange, i +offset); + } + if (Settings.light_speed > 0) { + + c.R = map(Light.strip_timer_counter % speed, 0, speed, oldColor.red, currentColor.red); + c.G = map(Light.strip_timer_counter % speed, 0, speed, oldColor.green, currentColor.green); + c.B = map(Light.strip_timer_counter % speed, 0, speed, oldColor.blue, currentColor.blue); + } + else { + + c.R = currentColor.red; + c.G = currentColor.green; + c.B = currentColor.blue; + } + strip->SetPixelColor(i, c); + oldColor = currentColor; + } + Ws2812StripShow(); +} + +void Ws2812Bars(uint32_t schemenr) +{ + + + + + +#if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor c; + c.W = 0; +#else + RgbColor c; +#endif + + ColorScheme scheme = kSchemes[schemenr]; + + uint32_t maxSize = Settings.light_pixels / scheme.count; + if (kWidth[Settings.light_width] > maxSize) { maxSize = 0; } + + uint32_t speed = ((Settings.light_speed * 2) -1) * (STATES / 10); + uint32_t offset = (speed > 0) ? Light.strip_timer_counter / speed : 0; + + WsColor mcolor[scheme.count]; + memcpy(mcolor, scheme.colors, sizeof(mcolor)); + float dimmer = 100 / (float)Settings.light_dimmer; + for (uint32_t i = 0; i < scheme.count; i++) { + float fmyRed = (float)mcolor[i].red / dimmer; + float fmyGrn = (float)mcolor[i].green / dimmer; + float fmyBlu = (float)mcolor[i].blue / dimmer; + mcolor[i].red = (uint8_t)fmyRed; + mcolor[i].green = (uint8_t)fmyGrn; + mcolor[i].blue = (uint8_t)fmyBlu; + } + uint32_t colorIndex = offset % scheme.count; + for (uint32_t i = 0; i < Settings.light_pixels; i++) { + if (maxSize) { colorIndex = ((i + offset) % (scheme.count * kWidth[Settings.light_width])) / kWidth[Settings.light_width]; } + c.R = mcolor[colorIndex].red; + c.G = mcolor[colorIndex].green; + c.B = mcolor[colorIndex].blue; + strip->SetPixelColor(i, c); + } + Ws2812StripShow(); +} + +void Ws2812Clear(void) +{ + strip->ClearTo(0); + strip->Show(); + Ws2812.show_next = 1; +} + +void Ws2812SetColor(uint32_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t white) +{ +#if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor lcolor; + lcolor.W = white; +#else + RgbColor lcolor; +#endif + + lcolor.R = red; + lcolor.G = green; + lcolor.B = blue; + if (led) { + strip->SetPixelColor(led -1, lcolor); + } else { + + for (uint32_t i = 0; i < Settings.light_pixels; i++) { + strip->SetPixelColor(i, lcolor); + } + } + + if (!Ws2812.suspend_update) { + strip->Show(); + Ws2812.show_next = 1; + } +} + +char* Ws2812GetColor(uint32_t led, char* scolor) +{ + uint8_t sl_ledcolor[4]; + + #if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor lcolor = strip->GetPixelColor(led -1); + sl_ledcolor[3] = lcolor.W; + #else + RgbColor lcolor = strip->GetPixelColor(led -1); + #endif + sl_ledcolor[0] = lcolor.R; + sl_ledcolor[1] = lcolor.G; + sl_ledcolor[2] = lcolor.B; + scolor[0] = '\0'; + for (uint32_t i = 0; i < Light.subtype; i++) { + if (Settings.flag.decimal_text) { + snprintf_P(scolor, 25, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", sl_ledcolor[i]); + } else { + snprintf_P(scolor, 25, PSTR("%s%02X"), scolor, sl_ledcolor[i]); + } + } + return scolor; +} + + + + + +void Ws2812ForceSuspend (void) +{ + Ws2812.suspend_update = true; +} + +void Ws2812ForceUpdate (void) +{ + Ws2812.suspend_update = false; + strip->Show(); + Ws2812.show_next = 1; +} + + + +bool Ws2812SetChannels(void) +{ + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + + Ws2812SetColor(0, cur_col[0], cur_col[1], cur_col[2], cur_col[3]); + + return true; +} + +void Ws2812ShowScheme(void) +{ + uint32_t scheme = Settings.light_scheme - Ws2812.scheme_offset; + + switch (scheme) { + case 0: + if ((1 == state_250mS) || (Ws2812.show_next)) { + Ws2812Clock(); + Ws2812.show_next = 0; + } + break; + default: + if (1 == Settings.light_fade) { + Ws2812Gradient(scheme -1); + } else { + Ws2812Bars(scheme -1); + } + Ws2812.show_next = 1; + break; + } +} + +void Ws2812ModuleSelected(void) +{ + if (pin[GPIO_WS2812] < 99) { + + + strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); + strip->Begin(); + + Ws2812Clear(); + + Ws2812.scheme_offset = Light.max_scheme +1; + Light.max_scheme += WS2812_SCHEMES; + +#if (USE_WS2812_CTYPE > NEO_3LED) + light_type = LT_RGBW; +#else + light_type = LT_RGB; +#endif + light_flg = XLGT_01; + } +} + + + +void CmndLed(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Settings.light_pixels)) { + if (XdrvMailbox.data_len > 0) { + char *p; + uint16_t idx = XdrvMailbox.index; + Ws2812ForceSuspend(); + for (char *color = strtok_r(XdrvMailbox.data, " ", &p); color; color = strtok_r(nullptr, " ", &p)) { + if (LightColorEntry(color, strlen(color))) { + Ws2812SetColor(idx, Light.entry_color[0], Light.entry_color[1], Light.entry_color[2], Light.entry_color[3]); + idx++; + if (idx > Settings.light_pixels) { break; } + } else { + break; + } + } + Ws2812ForceUpdate(); + } + char scolor[LIGHT_COLOR_SIZE]; + ResponseCmndIdxChar(Ws2812GetColor(XdrvMailbox.index, scolor)); + } +} + +void CmndPixels(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= WS2812_MAX_LEDS)) { + Settings.light_pixels = XdrvMailbox.payload; + Settings.light_rotation = 0; + Ws2812Clear(); + Light.update = true; + } + ResponseCmndNumber(Settings.light_pixels); +} + +void CmndRotation(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < Settings.light_pixels)) { + Settings.light_rotation = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.light_rotation); +} + +void CmndWidth(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { + if (1 == XdrvMailbox.index) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 4)) { + Settings.light_width = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.light_width); + } else { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32)) { + Settings.ws_width[XdrvMailbox.index -2] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.ws_width[XdrvMailbox.index -2]); + } + } +} + + + + + +bool Xlgt01(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = Ws2812SetChannels(); + break; + case FUNC_SET_SCHEME: + Ws2812ShowScheme(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kWs2812Commands, Ws2812Command); + break; + case FUNC_MODULE_INIT: + Ws2812ModuleSelected(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_02_my92x1.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_02_my92x1.ino" +#ifdef USE_LIGHT +#ifdef USE_MY92X1 + + + + +#define XLGT_02 2 + +struct MY92X1 { + uint8_t pdi_pin = 0; + uint8_t pdcki_pin = 0; + uint8_t model = 0; +} My92x1; + +extern "C" { + void os_delay_us(unsigned int); +} + +void LightDiPulse(uint8_t times) +{ + for (uint32_t i = 0; i < times; i++) { + digitalWrite(My92x1.pdi_pin, HIGH); + digitalWrite(My92x1.pdi_pin, LOW); + } +} + +void LightDckiPulse(uint8_t times) +{ + for (uint32_t i = 0; i < times; i++) { + digitalWrite(My92x1.pdcki_pin, HIGH); + digitalWrite(My92x1.pdcki_pin, LOW); + } +} + +void LightMy92x1Write(uint8_t data) +{ + for (uint32_t i = 0; i < 4; i++) { + digitalWrite(My92x1.pdcki_pin, LOW); + digitalWrite(My92x1.pdi_pin, (data & 0x80)); + digitalWrite(My92x1.pdcki_pin, HIGH); + data = data << 1; + digitalWrite(My92x1.pdi_pin, (data & 0x80)); + digitalWrite(My92x1.pdcki_pin, LOW); + digitalWrite(My92x1.pdi_pin, LOW); + data = data << 1; + } +} + +void LightMy92x1Init(void) +{ + uint8_t chips[3] = { 1, 2, 2 }; + + LightDckiPulse(chips[My92x1.model] * 32); + os_delay_us(12); + + + LightDiPulse(12); + os_delay_us(12); + for (uint32_t n = 0; n < chips[My92x1.model]; n++) { + LightMy92x1Write(0x18); + } + os_delay_us(12); + + + LightDiPulse(16); + os_delay_us(12); +} + +void LightMy92x1Duty(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b, uint8_t duty_w, uint8_t duty_c) +{ + uint8_t channels[3] = { 4, 6, 6 }; + + uint8_t duty[3][6] = {{ duty_r, duty_g, duty_b, duty_w, 0, 0 }, + { duty_w, duty_c, 0, duty_g, duty_r, duty_b }, + { duty_r, duty_g, duty_b, duty_w, duty_w, duty_w }}; + + os_delay_us(12); + for (uint32_t channel = 0; channel < channels[My92x1.model]; channel++) { + LightMy92x1Write(duty[My92x1.model][channel]); + } + os_delay_us(12); + LightDiPulse(8); + os_delay_us(12); +} + + + +bool My92x1SetChannels(void) +{ + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + + LightMy92x1Duty(cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]); + + return true; +} + +void My92x1ModuleSelected(void) +{ + if ((pin[GPIO_DCKI] < 99) && (pin[GPIO_DI] < 99)) { + My92x1.pdi_pin = pin[GPIO_DI]; + My92x1.pdcki_pin = pin[GPIO_DCKI]; + + pinMode(My92x1.pdi_pin, OUTPUT); + pinMode(My92x1.pdcki_pin, OUTPUT); + digitalWrite(My92x1.pdi_pin, LOW); + digitalWrite(My92x1.pdcki_pin, LOW); + + My92x1.model = 2; + light_type = LT_RGBW; + if (AILIGHT == my_module_type) { + My92x1.model = 0; + + } + else if (SONOFF_B1 == my_module_type) { + My92x1.model = 1; + light_type = LT_RGBWC; + } + + LightMy92x1Init(); + + light_flg = XLGT_02; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: MY29x1 Found")); + } +} + + + + + +bool Xlgt02(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = My92x1SetChannels(); + break; + case FUNC_MODULE_INIT: + My92x1ModuleSelected(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_03_sm16716.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_03_sm16716.ino" +#ifdef USE_LIGHT +#ifdef USE_SM16716 + + + + + + + +#define XLGT_03 3 + +#define D_LOG_SM16716 "SM16716: " + +struct SM16716 { + uint8_t pin_clk = 0; + uint8_t pin_dat = 0; + uint8_t pin_sel = 0; + bool enabled = false; +} Sm16716; + +void SM16716_SendBit(uint8_t v) +{ + + + + + + digitalWrite(Sm16716.pin_dat, (v != 0) ? HIGH : LOW); + + digitalWrite(Sm16716.pin_clk, HIGH); + + digitalWrite(Sm16716.pin_clk, LOW); +} + +void SM16716_SendByte(uint8_t v) +{ + uint8_t mask; + + for (mask = 0x80; mask; mask >>= 1) { + SM16716_SendBit(v & mask); + } +} + +void SM16716_Update(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b) +{ + if (Sm16716.pin_sel < 99) { + bool should_enable = (duty_r | duty_g | duty_b); + if (!Sm16716.enabled && should_enable) { + DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color on")); + Sm16716.enabled = true; + digitalWrite(Sm16716.pin_sel, HIGH); + + + delayMicroseconds(1000); + SM16716_Init(); + } + else if (Sm16716.enabled && !should_enable) { + DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color off")); + Sm16716.enabled = false; + digitalWrite(Sm16716.pin_sel, LOW); + } + } + DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "Update; rgb=%02x%02x%02x"), duty_r, duty_g, duty_b); + + + SM16716_SendBit(1); + SM16716_SendByte(duty_r); + SM16716_SendByte(duty_g); + SM16716_SendByte(duty_b); + + + + + + SM16716_SendBit(0); + SM16716_SendByte(0); + SM16716_SendByte(0); + SM16716_SendByte(0); +} +# 111 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_03_sm16716.ino" +void SM16716_Init(void) +{ + for (uint32_t t_init = 0; t_init < 50; ++t_init) { + SM16716_SendBit(0); + } +} + + + +bool Sm16716SetChannels(void) +{ +# 132 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_03_sm16716.ino" + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + + SM16716_Update(cur_col[0], cur_col[1], cur_col[2]); + + return true; +} + +void Sm16716ModuleSelected(void) +{ + if ((pin[GPIO_SM16716_CLK] < 99) && (pin[GPIO_SM16716_DAT] < 99)) { + Sm16716.pin_clk = pin[GPIO_SM16716_CLK]; + Sm16716.pin_dat = pin[GPIO_SM16716_DAT]; + Sm16716.pin_sel = pin[GPIO_SM16716_SEL]; +# 157 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_03_sm16716.ino" + pinMode(Sm16716.pin_clk, OUTPUT); + digitalWrite(Sm16716.pin_clk, LOW); + + pinMode(Sm16716.pin_dat, OUTPUT); + digitalWrite(Sm16716.pin_dat, LOW); + + if (Sm16716.pin_sel < 99) { + pinMode(Sm16716.pin_sel, OUTPUT); + digitalWrite(Sm16716.pin_sel, LOW); + + } else { + + SM16716_Init(); + } + + LightPwmOffset(LST_RGB); + light_type += LST_RGB; + light_flg = XLGT_03; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: SM16716 Found")); + } +} + + + + + +bool Xlgt03(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = Sm16716SetChannels(); + break; + case FUNC_MODULE_INIT: + Sm16716ModuleSelected(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_04_sm2135.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_04_sm2135.ino" +#ifdef USE_LIGHT +#ifdef USE_SM2135 + + + + + + +#define XLGT_04 4 + +#define SM2135_ADDR_MC 0xC0 +#define SM2135_ADDR_CH 0xC1 +#define SM2135_ADDR_R 0xC2 +#define SM2135_ADDR_G 0xC3 +#define SM2135_ADDR_B 0xC4 +#define SM2135_ADDR_C 0xC5 +#define SM2135_ADDR_W 0xC6 + +#define SM2135_RGB 0x00 +#define SM2135_CW 0x80 + +#define SM2135_10MA 0x00 +#define SM2135_15MA 0x01 +#define SM2135_20MA 0x02 +#define SM2135_25MA 0x03 +#define SM2135_30MA 0x04 +#define SM2135_35MA 0x05 +#define SM2135_40MA 0x06 +#define SM2135_45MA 0x07 +#define SM2135_50MA 0x08 +#define SM2135_55MA 0x09 +#define SM2135_60MA 0x0A + + +const uint8_t SM2135_CURRENT = (SM2135_20MA << 4) | SM2135_15MA; + +struct SM2135 { + uint8_t clk = 0; + uint8_t data = 0; +} Sm2135; + +uint8_t Sm2135Write(uint8_t data) +{ + for (uint32_t i = 0; i < 8; i++) { + digitalWrite(Sm2135.clk, LOW); + digitalWrite(Sm2135.data, (data & 0x80)); + digitalWrite(Sm2135.clk, HIGH); + data = data << 1; + } + digitalWrite(Sm2135.clk, LOW); + digitalWrite(Sm2135.data, HIGH); + pinMode(Sm2135.data, INPUT); + digitalWrite(Sm2135.clk, HIGH); + uint8_t ack = digitalRead(Sm2135.data); + pinMode(Sm2135.data, OUTPUT); + return ack; +} + +void Sm2135Send(uint8_t *buffer, uint8_t size) +{ + digitalWrite(Sm2135.data, LOW); + for (uint32_t i = 0; i < size; i++) { + Sm2135Write(buffer[i]); + } + digitalWrite(Sm2135.clk, LOW); + digitalWrite(Sm2135.clk, HIGH); + digitalWrite(Sm2135.data, HIGH); +} + + + +bool Sm2135SetChannels(void) +{ + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + uint8_t data[6]; + + if ((0 == cur_col[0]) && (0 == cur_col[1]) && (0 == cur_col[2])) { +# 106 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_04_sm2135.ino" + data[0] = SM2135_ADDR_MC; + data[1] = SM2135_CURRENT; + data[2] = SM2135_CW; + Sm2135Send(data, 3); + delay(1); + data[0] = SM2135_ADDR_C; + data[1] = cur_col[4]; + data[2] = cur_col[3]; + Sm2135Send(data, 3); + } else { + + + + + + + + data[0] = SM2135_ADDR_MC; + data[1] = SM2135_CURRENT; + data[2] = SM2135_RGB; + data[3] = cur_col[1]; + data[4] = cur_col[0]; + data[5] = cur_col[2]; + Sm2135Send(data, 6); + } + + return true; +} + +void Sm2135ModuleSelected(void) +{ + if ((pin[GPIO_SM2135_CLK] < 99) && (pin[GPIO_SM2135_DAT] < 99)) { + Sm2135.clk = pin[GPIO_SM2135_CLK]; + Sm2135.data = pin[GPIO_SM2135_DAT]; + + pinMode(Sm2135.data, OUTPUT); + digitalWrite(Sm2135.data, HIGH); + pinMode(Sm2135.clk, OUTPUT); + digitalWrite(Sm2135.clk, HIGH); + + light_type = LT_RGBWC; + light_flg = XLGT_04; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: SM2135 Found")); + } +} + + + + + +bool Xlgt04(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = Sm2135SetChannels(); + break; + case FUNC_MODULE_INIT: + Sm2135ModuleSelected(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_05_sonoff_l1.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_05_sonoff_l1.ino" +#ifdef USE_LIGHT +#ifdef USE_SONOFF_L1 + + + + +#define XLGT_05 5 + +#define SONOFF_L1_BUFFER_SIZE 140 + +#define SONOFF_L1_MODE_COLORFUL 1 +#define SONOFF_L1_MODE_COLORFUL_GRADIENT 2 +#define SONOFF_L1_MODE_COLORFUL_BREATH 3 +#define SONOFF_L1_MODE_DIY_GRADIENT 4 +#define SONOFF_L1_MODE_DIY_PULSE 5 +#define SONOFF_L1_MODE_DIY_BREATH 6 +#define SONOFF_L1_MODE_DIY_STROBE 7 +#define SONOFF_L1_MODE_RGB_GRADIENT 8 +#define SONOFF_L1_MODE_RGB_PULSE 9 +#define SONOFF_L1_MODE_RGB_BREATH 10 +#define SONOFF_L1_MODE_RGB_STROBE 11 +#define SONOFF_L1_MODE_SYNC_TO_MUSIC 12 + +struct SNFL1 { + uint32_t unlock = 0; + bool receive_ready = true; +} Snfl1; + + + +void SnfL1Send(const char *buffer) +{ + + + Serial.print(buffer); + Serial.write(0x1B); + Serial.flush(); +} + +void SnfL1SerialSendOk(void) +{ + char buffer[16]; + snprintf_P(buffer, sizeof(buffer), PSTR("AT+SEND=ok")); + + SnfL1Send(buffer); +} + +bool SnfL1SerialInput(void) +{ + if (serial_in_byte != 0x1B) { + if (serial_in_byte_counter >= 140) { + serial_in_byte_counter = 0; + } + if (serial_in_byte_counter || (!serial_in_byte_counter && ('A' == serial_in_byte))) { + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + } + } else { + serial_in_buffer[serial_in_byte_counter++] = 0x00; + + + + + + + if (!strncmp(serial_in_buffer +3, "RESULT", 6)) { + Snfl1.receive_ready = true; + } + else if (!strncmp(serial_in_buffer +3, "UPDATE", 6)) { + char cmnd_dimmer[20]; + char cmnd_color[20]; + char *end_str; + char *string = serial_in_buffer +10; + char *token = strtok_r(string, ",", &end_str); + + bool color_updated[3] = { false, false, false }; + uint8_t current_color[3]; + memcpy(current_color, Settings.light_color, 3); + + bool switch_state = false; + bool is_power_change = false; + bool is_color_change = false; + bool is_brightness_change = false; + + while (token != nullptr) { + char* end_token; + char* token2 = strtok_r(token, ":", &end_token); + char* token3 = strtok_r(nullptr, ":", &end_token); + + if (!strncmp(token2, "\"sequence\"", 10)) { + + + + token = nullptr; + } + + else if (!strncmp(token2, "\"switch\"", 8)) { + switch_state = !strncmp(token3, "\"on\"", 4) ? true : false; + + + + is_power_change = (switch_state != Light.power); + } + + else if (!strncmp(token2, "\"color", 6)) { + char color_channel_name = token2[6]; + int color_index; + switch(color_channel_name) + { + case 'R': color_index = 0; + break; + case 'G': color_index = 1; + break; + case 'B': color_index = 2; + break; + } + int color_value = atoi(token3); + current_color[color_index] = color_value; + color_updated[color_index] = true; + + bool all_color_channels_updated = color_updated[0] && color_updated[1] && color_updated[2]; + if (all_color_channels_updated) { + + + + + + is_color_change = (Light.power && (memcmp(current_color, Settings.light_color, 3) != 0)); + } + snprintf_P(cmnd_color, sizeof(cmnd_color), PSTR(D_CMND_COLOR "2 %02x%02x%02x"), current_color[0], current_color[1], current_color[2]); + } + + else if (!strncmp(token2, "\"bright\"", 8)) { + uint8_t dimmer = atoi(token3); + + + + is_brightness_change = (Light.power && (dimmer > 0) && (dimmer != Settings.light_dimmer)); + snprintf_P(cmnd_dimmer, sizeof(cmnd_dimmer), PSTR(D_CMND_DIMMER " %d"), dimmer); + } + + token = strtok_r(nullptr, ",", &end_str); + } + + if (is_power_change) { + if (Settings.light_scheme > 0) { + if (!switch_state) { + char cmnd_scheme[20]; + snprintf_P(cmnd_scheme, sizeof(cmnd_scheme), PSTR(D_CMND_SCHEME " 0")); + ExecuteCommand(cmnd_scheme, SRC_SWITCH); + } + } else { + ExecuteCommandPower(1, switch_state, SRC_SWITCH); + } + } + else if (is_brightness_change) { + ExecuteCommand(cmnd_dimmer, SRC_SWITCH); + } + else if (Light.power && is_color_change) { + if (0 == Settings.light_scheme) { + if (Settings.light_fade) { + char cmnd_fade[20]; + snprintf_P(cmnd_fade, sizeof(cmnd_fade), PSTR(D_CMND_FADE " 0")); + ExecuteCommand(cmnd_fade, SRC_SWITCH); + } + ExecuteCommand(cmnd_color, SRC_SWITCH); + } + } + } + + SnfL1SerialSendOk(); + + return true; + } + serial_in_byte = 0; + return false; +} + + + +bool SnfL1SetChannels(void) +{ + if (Snfl1.receive_ready || TimeReached(Snfl1.unlock)) { + + uint8_t *scale_col = (uint8_t*)XdrvMailbox.topic; + + char buffer[140]; + snprintf_P(buffer, sizeof(buffer), PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"light_type\":1,\"colorR\":%d,\"colorG\":%d,\"colorB\":%d,\"bright\":%d,\"mode\":%d"), + LocalTime(), millis()%1000, + Light.power ? "on" : "off", + scale_col[0], scale_col[1], scale_col[2], + light_state.getDimmer(), + SONOFF_L1_MODE_COLORFUL); + + SnfL1Send(buffer); + + Snfl1.unlock = millis() + 500; + Snfl1.receive_ready = false; + } + return true; +} + +void SnfL1ModuleSelected(void) +{ + if (SONOFF_L1 == my_module_type) { + if ((pin[GPIO_RXD] < 99) && (pin[GPIO_TXD] < 99)) { + SetSerial(19200, TS_SERIAL_8N1); + + light_type = LT_RGB; + light_flg = XLGT_05; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("LGT: Sonoff L1 Found")); + } + } +} + + + + + +bool Xlgt05(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SERIAL: + result = SnfL1SerialInput(); + break; + case FUNC_SET_CHANNELS: + result = SnfL1SetChannels(); + break; + case FUNC_MODULE_INIT: + SnfL1ModuleSelected(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_06_electriq_moodl.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_06_electriq_moodl.ino" +#ifdef USE_LIGHT +#ifdef USE_ELECTRIQ_MOODL +# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_06_electriq_moodl.ino" +#define XLGT_06 6 + + + +bool ElectriqMoodLSetChannels(void) +{ + uint8_t *col = (uint8_t*)XdrvMailbox.data; + uint8_t checksum = (uint8_t)(0x65 + 0xAA + 0x01 + 0x0A); + + Serial.write(0x65); + Serial.write(0xAA); + Serial.write(0x00); + Serial.write(0x01); + Serial.write(0x0A); + + uint8_t payload[5]; + payload[0] = col[0]; + payload[1] = col[1]; + payload[2] = col[2]; + payload[3] = col[3]; + payload[4] = 0x0; + + + for (uint32_t i = 0; i < 5; i++) { + Serial.write(payload[i]); + checksum += payload[i]; + } + + + for (uint32_t i = 0; i < 5; i++) { + Serial.write(payload[i]); + checksum += payload[i]; + } + + Serial.write(checksum); + Serial.flush(); + + return true; +} + +void ElectriqMoodLModuleSelected(void) +{ + if (pin[GPIO_ELECTRIQ_MOODL_TX] < 99) { + SetSerial(9600, TS_SERIAL_8N1); + light_type = LT_RGBW; + light_flg = XLGT_06; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("LGT: ElectriQ Mood Lamp Found")); + } +} + + + + + +bool Xlgt06(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = ElectriqMoodLSetChannels(); + break; + case FUNC_MODULE_INIT: + ElectriqMoodLModuleSelected(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_interface.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_interface.ino" +#ifdef USE_LIGHT + +#ifdef XFUNC_PTR_IN_ROM +bool (* const xlgt_func_ptr[])(uint8_t) PROGMEM = { +#else +bool (* const xlgt_func_ptr[])(uint8_t) = { +#endif + +#ifdef XLGT_01 + &Xlgt01, +#endif + +#ifdef XLGT_02 + &Xlgt02, +#endif + +#ifdef XLGT_03 + &Xlgt03, +#endif + +#ifdef XLGT_04 + &Xlgt04, +#endif + +#ifdef XLGT_05 + &Xlgt05, +#endif + +#ifdef XLGT_06 + &Xlgt06, +#endif + +#ifdef XLGT_07 + &Xlgt07, +#endif + +#ifdef XLGT_08 + &Xlgt08, +#endif + +#ifdef XLGT_09 + &Xlgt09, +#endif + +#ifdef XLGT_10 + &Xlgt10, +#endif + +#ifdef XLGT_11 + &Xlgt11, +#endif + +#ifdef XLGT_12 + &Xlgt12, +#endif + +#ifdef XLGT_13 + &Xlgt13, +#endif + +#ifdef XLGT_14 + &Xlgt14, +#endif + +#ifdef XLGT_15 + &Xlgt15, +#endif + +#ifdef XLGT_16 + &Xlgt16 +#endif +}; + +const uint8_t xlgt_present = sizeof(xlgt_func_ptr) / sizeof(xlgt_func_ptr[0]); + +uint8_t xlgt_active = 0; + +bool XlgtCall(uint8_t function) +{ + DEBUG_TRACE_LOG(PSTR("LGT: %d"), function); + + if (FUNC_MODULE_INIT == function) { + for (uint32_t x = 0; x < xlgt_present; x++) { + xlgt_func_ptr[x](function); + if (light_flg) { + xlgt_active = x; + return true; + } + } + } + else if (light_flg) { + return xlgt_func_ptr[xlgt_active](function); + } + return false; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_01_hlw8012.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_01_hlw8012.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_HLW8012 + + + + + + +#define XNRG_01 1 + + +#define HLW_PREF 10000 +#define HLW_UREF 2200 +#define HLW_IREF 4545 + + +#define HJL_PREF 1362 +#define HJL_UREF 822 +#define HJL_IREF 3300 + +#define HLW_POWER_PROBE_TIME 10 +#define HLW_SAMPLE_COUNT 10 + + + +struct HLW { +#ifdef HLW_DEBUG + unsigned long debug[HLW_SAMPLE_COUNT]; +#endif + unsigned long cf_pulse_length = 0; + unsigned long cf_pulse_last_time = 0; + unsigned long cf_power_pulse_length = 0; + + unsigned long cf1_pulse_length = 0; + unsigned long cf1_pulse_last_time = 0; + unsigned long cf1_summed_pulse_length = 0; + unsigned long cf1_pulse_counter = 0; + unsigned long cf1_voltage_pulse_length = 0; + unsigned long cf1_current_pulse_length = 0; + + unsigned long energy_period_counter = 0; + + unsigned long power_ratio = 0; + unsigned long voltage_ratio = 0; + unsigned long current_ratio = 0; + + uint8_t model_type = 0; + uint8_t cf1_timer = 0; + uint8_t power_retry = 0; + bool select_ui_flag = false; + bool ui_flag = true; + bool load_off = true; +} Hlw; + + +#ifndef USE_WS2812_DMA +void HlwCfInterrupt(void) ICACHE_RAM_ATTR; +void HlwCf1Interrupt(void) ICACHE_RAM_ATTR; +#endif + +void HlwCfInterrupt(void) +{ + unsigned long us = micros(); + + if (Hlw.load_off) { + Hlw.cf_pulse_last_time = us; + Hlw.load_off = false; + } else { + Hlw.cf_pulse_length = us - Hlw.cf_pulse_last_time; + Hlw.cf_pulse_last_time = us; + Hlw.energy_period_counter++; + } + Energy.data_valid[0] = 0; +} + +void HlwCf1Interrupt(void) +{ + unsigned long us = micros(); + + Hlw.cf1_pulse_length = us - Hlw.cf1_pulse_last_time; + Hlw.cf1_pulse_last_time = us; + if ((Hlw.cf1_timer > 2) && (Hlw.cf1_timer < 8)) { + Hlw.cf1_summed_pulse_length += Hlw.cf1_pulse_length; +#ifdef HLW_DEBUG + Hlw.debug[Hlw.cf1_pulse_counter] = Hlw.cf1_pulse_length; +#endif + Hlw.cf1_pulse_counter++; + if (HLW_SAMPLE_COUNT == Hlw.cf1_pulse_counter) { + Hlw.cf1_timer = 8; + } + } + Energy.data_valid[0] = 0; +} + + + +void HlwEvery200ms(void) +{ + unsigned long cf1_pulse_length = 0; + unsigned long hlw_w = 0; + unsigned long hlw_u = 0; + unsigned long hlw_i = 0; + + if (micros() - Hlw.cf_pulse_last_time > (HLW_POWER_PROBE_TIME * 1000000)) { + Hlw.cf_pulse_length = 0; + Hlw.load_off = true; + } + Hlw.cf_power_pulse_length = Hlw.cf_pulse_length; + + if (Hlw.cf_power_pulse_length && Energy.power_on && !Hlw.load_off) { + hlw_w = (Hlw.power_ratio * Settings.energy_power_calibration) / Hlw.cf_power_pulse_length ; + Energy.active_power[0] = (float)hlw_w / 10; + Hlw.power_retry = 1; + } else { + if (Hlw.power_retry) { + Hlw.power_retry--; + } else { + Energy.active_power[0] = 0; + } + } + + if (pin[GPIO_NRG_CF1] < 99) { + Hlw.cf1_timer++; + if (Hlw.cf1_timer >= 8) { + Hlw.cf1_timer = 0; + Hlw.select_ui_flag = (Hlw.select_ui_flag) ? false : true; + DigitalWrite(GPIO_NRG_SEL, Hlw.select_ui_flag); + + if (Hlw.cf1_pulse_counter) { + cf1_pulse_length = Hlw.cf1_summed_pulse_length / Hlw.cf1_pulse_counter; + } + +#ifdef HLW_DEBUG + + char stemp[100]; + stemp[0] = '\0'; + for (uint32_t i = 0; i < Hlw.cf1_pulse_counter; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%s %d"), stemp, Hlw.debug[i]); + } + for (uint32_t i = 0; i < Hlw.cf1_pulse_counter; i++) { + for (uint32_t j = i + 1; j < Hlw.cf1_pulse_counter; j++) { + if (Hlw.debug[i] > Hlw.debug[j]) { + std::swap(Hlw.debug[i], Hlw.debug[j]); + } + } + } + unsigned long median = Hlw.debug[(Hlw.cf1_pulse_counter +1) / 2]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NRG: power %d, ui %d, cnt %d, smpl%s, sum %d, mean %d, median %d"), + Hlw.cf_power_pulse_length , Hlw.select_ui_flag, Hlw.cf1_pulse_counter, stemp, Hlw.cf1_summed_pulse_length, cf1_pulse_length, median); +#endif + + if (Hlw.select_ui_flag == Hlw.ui_flag) { + Hlw.cf1_voltage_pulse_length = cf1_pulse_length; + + if (Hlw.cf1_voltage_pulse_length && Energy.power_on) { + hlw_u = (Hlw.voltage_ratio * Settings.energy_voltage_calibration) / Hlw.cf1_voltage_pulse_length ; + Energy.voltage[0] = (float)hlw_u / 10; + } else { + Energy.voltage[0] = 0; + } + + } else { + Hlw.cf1_current_pulse_length = cf1_pulse_length; + + if (Hlw.cf1_current_pulse_length && Energy.active_power[0]) { + hlw_i = (Hlw.current_ratio * Settings.energy_current_calibration) / Hlw.cf1_current_pulse_length; + Energy.current[0] = (float)hlw_i / 1000; + } else { + Energy.current[0] = 0; + } + + } + Hlw.cf1_summed_pulse_length = 0; + Hlw.cf1_pulse_counter = 0; + } + } +} + +void HlwEverySecond(void) +{ + if (Energy.data_valid[0] > ENERGY_WATCHDOG) { + Hlw.cf1_voltage_pulse_length = 0; + Hlw.cf1_current_pulse_length = 0; + Hlw.cf_power_pulse_length = 0; + } else { + unsigned long hlw_len; + + if (Hlw.energy_period_counter) { + hlw_len = 10000 / Hlw.energy_period_counter; + Hlw.energy_period_counter = 0; + if (hlw_len) { + Energy.kWhtoday_delta += ((Hlw.power_ratio * Settings.energy_power_calibration) / hlw_len) / 36; + EnergyUpdateToday(); + } + } + } +} + +void HlwSnsInit(void) +{ + if (!Settings.energy_power_calibration || (4975 == Settings.energy_power_calibration)) { + Settings.energy_power_calibration = HLW_PREF_PULSE; + Settings.energy_voltage_calibration = HLW_UREF_PULSE; + Settings.energy_current_calibration = HLW_IREF_PULSE; + } + + if (Hlw.model_type) { + Hlw.power_ratio = HJL_PREF; + Hlw.voltage_ratio = HJL_UREF; + Hlw.current_ratio = HJL_IREF; + } else { + Hlw.power_ratio = HLW_PREF; + Hlw.voltage_ratio = HLW_UREF; + Hlw.current_ratio = HLW_IREF; + } + + if (pin[GPIO_NRG_SEL] < 99) { + pinMode(pin[GPIO_NRG_SEL], OUTPUT); + digitalWrite(pin[GPIO_NRG_SEL], Hlw.select_ui_flag); + } + if (pin[GPIO_NRG_CF1] < 99) { + pinMode(pin[GPIO_NRG_CF1], INPUT_PULLUP); + attachInterrupt(pin[GPIO_NRG_CF1], HlwCf1Interrupt, FALLING); + } + pinMode(pin[GPIO_HLW_CF], INPUT_PULLUP); + attachInterrupt(pin[GPIO_HLW_CF], HlwCfInterrupt, FALLING); +} + +void HlwDrvInit(void) +{ + Hlw.model_type = 0; + if (pin[GPIO_HJL_CF] < 99) { + pin[GPIO_HLW_CF] = pin[GPIO_HJL_CF]; + pin[GPIO_HJL_CF] = 99; + Hlw.model_type = 1; + } + + if (pin[GPIO_HLW_CF] < 99) { + + Hlw.ui_flag = true; + if (pin[GPIO_NRG_SEL_INV] < 99) { + pin[GPIO_NRG_SEL] = pin[GPIO_NRG_SEL_INV]; + pin[GPIO_NRG_SEL_INV] = 99; + Hlw.ui_flag = false; + } + + if (pin[GPIO_NRG_CF1] < 99) { + if (99 == pin[GPIO_NRG_SEL]) { + Energy.current_available = false; + } + } else { + Energy.current_available = false; + Energy.voltage_available = false; + } + + energy_flg = XNRG_01; + } +} + +bool HlwCommand(void) +{ + bool serviced = true; + + if ((CMND_POWERCAL == Energy.command_code) || (CMND_VOLTAGECAL == Energy.command_code) || (CMND_CURRENTCAL == Energy.command_code)) { + + } + else if (CMND_POWERSET == Energy.command_code) { + if (XdrvMailbox.data_len && Hlw.cf_power_pulse_length ) { + Settings.energy_power_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data) * 10) * Hlw.cf_power_pulse_length ) / Hlw.power_ratio; + } + } + else if (CMND_VOLTAGESET == Energy.command_code) { + if (XdrvMailbox.data_len && Hlw.cf1_voltage_pulse_length ) { + Settings.energy_voltage_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data) * 10) * Hlw.cf1_voltage_pulse_length ) / Hlw.voltage_ratio; + } + } + else if (CMND_CURRENTSET == Energy.command_code) { + if (XdrvMailbox.data_len && Hlw.cf1_current_pulse_length) { + Settings.energy_current_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data)) * Hlw.cf1_current_pulse_length) / Hlw.current_ratio; + } + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg01(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_200_MSECOND: + HlwEvery200ms(); + break; + case FUNC_ENERGY_EVERY_SECOND: + HlwEverySecond(); + break; + case FUNC_COMMAND: + result = HlwCommand(); + break; + case FUNC_INIT: + HlwSnsInit(); + break; + case FUNC_PRE_INIT: + HlwDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_02_cse7766.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_02_cse7766.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_CSE7766 + + + + + + + +#define XNRG_02 2 + +#define CSE_MAX_INVALID_POWER 128 + +#define CSE_NOT_CALIBRATED 0xAA + +#define CSE_PULSES_NOT_INITIALIZED -1 + +#define CSE_PREF 1000 +#define CSE_UREF 100 + +#define CSE_BUFFER_SIZE 25 + +#include + +TasmotaSerial *CseSerial = nullptr; + +struct CSE { + long voltage_cycle = 0; + long current_cycle = 0; + long power_cycle = 0; + long power_cycle_first = 0; + long cf_pulses = 0; + long cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; + + int byte_counter = 0; + uint8_t *rx_buffer = nullptr; + uint8_t power_invalid = 0; + bool received = false; +} Cse; + +void CseReceived(void) +{ + + + + + + + uint8_t header = Cse.rx_buffer[0]; + if ((header & 0xFC) == 0xFC) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: Abnormal hardware")); + return; + } + + + if (HLW_UREF_PULSE == Settings.energy_voltage_calibration) { + long voltage_coefficient = 191200; + if (CSE_NOT_CALIBRATED != header) { + voltage_coefficient = Cse.rx_buffer[2] << 16 | Cse.rx_buffer[3] << 8 | Cse.rx_buffer[4]; + } + Settings.energy_voltage_calibration = voltage_coefficient / CSE_UREF; + } + if (HLW_IREF_PULSE == Settings.energy_current_calibration) { + long current_coefficient = 16140; + if (CSE_NOT_CALIBRATED != header) { + current_coefficient = Cse.rx_buffer[8] << 16 | Cse.rx_buffer[9] << 8 | Cse.rx_buffer[10]; + } + Settings.energy_current_calibration = current_coefficient; + } + if (HLW_PREF_PULSE == Settings.energy_power_calibration) { + long power_coefficient = 5364000; + if (CSE_NOT_CALIBRATED != header) { + power_coefficient = Cse.rx_buffer[14] << 16 | Cse.rx_buffer[15] << 8 | Cse.rx_buffer[16]; + } + Settings.energy_power_calibration = power_coefficient / CSE_PREF; + } + + uint8_t adjustement = Cse.rx_buffer[20]; + Cse.voltage_cycle = Cse.rx_buffer[5] << 16 | Cse.rx_buffer[6] << 8 | Cse.rx_buffer[7]; + Cse.current_cycle = Cse.rx_buffer[11] << 16 | Cse.rx_buffer[12] << 8 | Cse.rx_buffer[13]; + Cse.power_cycle = Cse.rx_buffer[17] << 16 | Cse.rx_buffer[18] << 8 | Cse.rx_buffer[19]; + Cse.cf_pulses = Cse.rx_buffer[21] << 8 | Cse.rx_buffer[22]; + + if (Energy.power_on) { + if (adjustement & 0x40) { + Energy.voltage[0] = (float)(Settings.energy_voltage_calibration * CSE_UREF) / (float)Cse.voltage_cycle; + } + if (adjustement & 0x10) { + Cse.power_invalid = 0; + if ((header & 0xF2) == 0xF2) { + Energy.active_power[0] = 0; + } else { + if (0 == Cse.power_cycle_first) { Cse.power_cycle_first = Cse.power_cycle; } + if (Cse.power_cycle_first != Cse.power_cycle) { + Cse.power_cycle_first = -1; + Energy.active_power[0] = (float)(Settings.energy_power_calibration * CSE_PREF) / (float)Cse.power_cycle; + } else { + Energy.active_power[0] = 0; + } + } + } else { + if (Cse.power_invalid < Settings.param[P_CSE7766_INVALID_POWER]) { + Cse.power_invalid++; + } else { + Cse.power_cycle_first = 0; + Energy.active_power[0] = 0; + } + } + if (adjustement & 0x20) { + if (0 == Energy.active_power[0]) { + Energy.current[0] = 0; + } else { + Energy.current[0] = (float)Settings.energy_current_calibration / (float)Cse.current_cycle; + } + } + } else { + Cse.power_cycle_first = 0; + Energy.voltage[0] = 0; + Energy.active_power[0] = 0; + Energy.current[0] = 0; + } +} + +bool CseSerialInput(void) +{ + while (CseSerial->available()) { + yield(); + uint8_t serial_in_byte = CseSerial->read(); + + if (Cse.received) { + Cse.rx_buffer[Cse.byte_counter++] = serial_in_byte; + if (24 == Cse.byte_counter) { + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, Cse.rx_buffer, 24); + + uint8_t checksum = 0; + for (uint32_t i = 2; i < 23; i++) { checksum += Cse.rx_buffer[i]; } + if (checksum == Cse.rx_buffer[23]) { + Energy.data_valid[0] = 0; + CseReceived(); + Cse.received = false; + return true; + } else { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: " D_CHECKSUM_FAILURE)); + do { + memmove(Cse.rx_buffer, Cse.rx_buffer +1, 24); + Cse.byte_counter--; + } while ((Cse.byte_counter > 2) && (0x5A != Cse.rx_buffer[1])); + if (0x5A != Cse.rx_buffer[1]) { + Cse.received = false; + Cse.byte_counter = 0; + } + } + } + } else { + if ((0x5A == serial_in_byte) && (1 == Cse.byte_counter)) { + Cse.received = true; + } else { + Cse.byte_counter = 0; + } + Cse.rx_buffer[Cse.byte_counter++] = serial_in_byte; + } + } +} + + + +void CseEverySecond(void) +{ + if (Energy.data_valid[0] > ENERGY_WATCHDOG) { + Cse.voltage_cycle = 0; + Cse.current_cycle = 0; + Cse.power_cycle = 0; + } else { + long cf_frequency = 0; + + if (CSE_PULSES_NOT_INITIALIZED == Cse.cf_pulses_last_time) { + Cse.cf_pulses_last_time = Cse.cf_pulses; + } else { + if (Cse.cf_pulses < Cse.cf_pulses_last_time) { + cf_frequency = (65536 - Cse.cf_pulses_last_time) + Cse.cf_pulses; + } else { + cf_frequency = Cse.cf_pulses - Cse.cf_pulses_last_time; + } + if (cf_frequency && Energy.active_power[0]) { + unsigned long delta = (cf_frequency * Settings.energy_power_calibration) / 36; + + + + if (delta <= (4000*100/36) * 10 ) { + Cse.cf_pulses_last_time = Cse.cf_pulses; + Energy.kWhtoday_delta += delta; + } + else { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: Load overflow")); + Cse.cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; + } + EnergyUpdateToday(); + } + } + } +} + +void CseSnsInit(void) +{ + + + CseSerial = new TasmotaSerial(pin[GPIO_CSE7766_RX], -1, 1); + if (CseSerial->begin(4800, 2)) { + if (CseSerial->hardwareSerial()) { + SetSerial(4800, TS_SERIAL_8E1); + ClaimSerial(); + } + if (0 == Settings.param[P_CSE7766_INVALID_POWER]) { + Settings.param[P_CSE7766_INVALID_POWER] = CSE_MAX_INVALID_POWER; + } + Cse.power_invalid = Settings.param[P_CSE7766_INVALID_POWER]; + } else { + energy_flg = ENERGY_NONE; + } +} + +void CseDrvInit(void) +{ + Cse.rx_buffer = (uint8_t*)(malloc(CSE_BUFFER_SIZE)); + if (Cse.rx_buffer != nullptr) { + + if (pin[GPIO_CSE7766_RX] < 99) { + energy_flg = XNRG_02; + } + } +} + +bool CseCommand(void) +{ + bool serviced = true; + + if (CMND_POWERSET == Energy.command_code) { + if (XdrvMailbox.data_len && Cse.power_cycle) { + Settings.energy_power_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * Cse.power_cycle) / CSE_PREF; + } + } + else if (CMND_VOLTAGESET == Energy.command_code) { + if (XdrvMailbox.data_len && Cse.voltage_cycle) { + Settings.energy_voltage_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * Cse.voltage_cycle) / CSE_UREF; + } + } + else if (CMND_CURRENTSET == Energy.command_code) { + if (XdrvMailbox.data_len && Cse.current_cycle) { + Settings.energy_current_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * Cse.current_cycle) / 1000; + } + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg02(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_LOOP: + if (CseSerial) { CseSerialInput(); } + break; + case FUNC_ENERGY_EVERY_SECOND: + CseEverySecond(); + break; + case FUNC_COMMAND: + result = CseCommand(); + break; + case FUNC_INIT: + CseSnsInit(); + break; + case FUNC_PRE_INIT: + CseDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_03_pzem004t.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_03_pzem004t.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_PZEM004T +# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_03_pzem004t.ino" +#define XNRG_03 3 + +const uint32_t PZEM_STABILIZE = 30; + +#include + +TasmotaSerial *PzemSerial = nullptr; + +#define PZEM_VOLTAGE (uint8_t)0xB0 +#define RESP_VOLTAGE (uint8_t)0xA0 + +#define PZEM_CURRENT (uint8_t)0xB1 +#define RESP_CURRENT (uint8_t)0xA1 + +#define PZEM_POWER (uint8_t)0xB2 +#define RESP_POWER (uint8_t)0xA2 + +#define PZEM_ENERGY (uint8_t)0xB3 +#define RESP_ENERGY (uint8_t)0xA3 + +#define PZEM_SET_ADDRESS (uint8_t)0xB4 +#define RESP_SET_ADDRESS (uint8_t)0xA4 + +#define PZEM_POWER_ALARM (uint8_t)0xB5 +#define RESP_POWER_ALARM (uint8_t)0xA5 + +#define PZEM_DEFAULT_READ_TIMEOUT 500 + + + +struct PZEM { + float energy = 0; + float last_energy = 0; + uint8_t send_retry = 0; + uint8_t read_state = 0; + uint8_t phase = 0; + uint8_t address = 0; +} Pzem; + +struct PZEMCommand { + uint8_t command; + uint8_t addr[4]; + uint8_t data; + uint8_t crc; +}; + +uint8_t PzemCrc(uint8_t *data) +{ + uint16_t crc = 0; + for (uint32_t i = 0; i < sizeof(PZEMCommand) -1; i++) { + crc += *data++; + } + return (uint8_t)(crc & 0xFF); +} + +void PzemSend(uint8_t cmd) +{ + PZEMCommand pzem; + + pzem.command = cmd; + pzem.addr[0] = 192; + pzem.addr[1] = 168; + pzem.addr[2] = 1; + pzem.addr[3] = ((PZEM_SET_ADDRESS == cmd) && Pzem.address) ? Pzem.address : 1 + Pzem.phase; + pzem.data = 0; + + uint8_t *bytes = (uint8_t*)&pzem; + pzem.crc = PzemCrc(bytes); + + PzemSerial->flush(); + PzemSerial->write(bytes, sizeof(pzem)); + + Pzem.address = 0; +} + +bool PzemReceiveReady(void) +{ + return PzemSerial->available() >= (int)sizeof(PZEMCommand); +} + +bool PzemRecieve(uint8_t resp, float *data) +{ +# 124 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_03_pzem004t.ino" + uint8_t buffer[sizeof(PZEMCommand)] = { 0 }; + + unsigned long start = millis(); + uint8_t len = 0; + while ((len < sizeof(PZEMCommand)) && (millis() - start < PZEM_DEFAULT_READ_TIMEOUT)) { + if (PzemSerial->available() > 0) { + uint8_t c = (uint8_t)PzemSerial->read(); + if (!len && ((c & 0xF8) != 0xA0)) { + continue; + } + buffer[len++] = c; + } + } + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, len); + + if (len != sizeof(PZEMCommand)) { + + return false; + } + if (buffer[6] != PzemCrc(buffer)) { + + return false; + } + if (buffer[0] != resp) { + + return false; + } + + switch (resp) { + case RESP_VOLTAGE: + *data = (float)(buffer[1] << 8) + buffer[2] + (buffer[3] / 10.0); + break; + case RESP_CURRENT: + *data = (float)(buffer[1] << 8) + buffer[2] + (buffer[3] / 100.0); + break; + case RESP_POWER: + *data = (float)(buffer[1] << 8) + buffer[2]; + break; + case RESP_ENERGY: + *data = (float)((uint32_t)buffer[1] << 16) + ((uint16_t)buffer[2] << 8) + buffer[3]; + break; + } + return true; +} + + + +const uint8_t pzem_commands[] { PZEM_SET_ADDRESS, PZEM_VOLTAGE, PZEM_CURRENT, PZEM_POWER, PZEM_ENERGY }; +const uint8_t pzem_responses[] { RESP_SET_ADDRESS, RESP_VOLTAGE, RESP_CURRENT, RESP_POWER, RESP_ENERGY }; + +void PzemEvery250ms(void) +{ + bool data_ready = PzemReceiveReady(); + + if (data_ready) { + float value = 0; + if (PzemRecieve(pzem_responses[Pzem.read_state], &value)) { + Energy.data_valid[Pzem.phase] = 0; + switch (Pzem.read_state) { + case 1: + Energy.voltage[Pzem.phase] = value; + break; + case 2: + Energy.current[Pzem.phase] = value; + break; + case 3: + Energy.active_power[Pzem.phase] = value; + break; + case 4: + Pzem.energy += value; + if (Pzem.phase == Energy.phase_count -1) { + if (Pzem.energy > Pzem.last_energy) { + if (uptime > PZEM_STABILIZE) { + EnergyUpdateTotal(Pzem.energy, false); + } + Pzem.last_energy = Pzem.energy; + } + Pzem.energy = 0; + } + break; + } + Pzem.read_state++; + if (5 == Pzem.read_state) { + Pzem.read_state = 1; + } + + + } + } + + if (0 == Pzem.send_retry || data_ready) { + if (1 == Pzem.read_state) { + if (0 == Pzem.phase) { + Pzem.phase = Energy.phase_count -1; + } else { + Pzem.phase--; + } + + + } + + if (Pzem.address) { + Pzem.read_state = 0; + } + + Pzem.send_retry = 5; + PzemSend(pzem_commands[Pzem.read_state]); + } + else { + Pzem.send_retry--; + if ((Energy.phase_count > 1) && (0 == Pzem.send_retry) && (uptime < PZEM_STABILIZE)) { + Energy.phase_count--; + } + } +} + +void PzemSnsInit(void) +{ + + PzemSerial = new TasmotaSerial(pin[GPIO_PZEM004_RX], pin[GPIO_PZEM0XX_TX], 1); + if (PzemSerial->begin(9600)) { + if (PzemSerial->hardwareSerial()) { + ClaimSerial(); + } + Energy.phase_count = 3; + Pzem.phase = 0; + Pzem.read_state = 1; + } else { + energy_flg = ENERGY_NONE; + } +} + +void PzemDrvInit(void) +{ + if ((pin[GPIO_PZEM004_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { + energy_flg = XNRG_03; + } +} + +bool PzemCommand(void) +{ + bool serviced = true; + + if (CMND_MODULEADDRESS == Energy.command_code) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4)) { + Pzem.address = XdrvMailbox.payload; + } + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg03(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (PzemSerial && (uptime > 4)) { PzemEvery250ms(); } + break; + case FUNC_COMMAND: + result = PzemCommand(); + break; + case FUNC_INIT: + PzemSnsInit(); + break; + case FUNC_PRE_INIT: + PzemDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_04_mcp39f501.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_04_mcp39f501.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_MCP39F501 + + + + + + + +#define XNRG_04 4 + +#define MCP_BAUDRATE 4800 +#define MCP_TIMEOUT 4 +#define MCP_CALIBRATION_TIMEOUT 2 + +#define MCP_CALIBRATE_POWER 0x001 +#define MCP_CALIBRATE_VOLTAGE 0x002 +#define MCP_CALIBRATE_CURRENT 0x004 +#define MCP_CALIBRATE_FREQUENCY 0x008 +#define MCP_SINGLE_WIRE_FLAG 0x100 + +#define MCP_START_FRAME 0xA5 +#define MCP_ACK_FRAME 0x06 +#define MCP_ERROR_NAK 0x15 +#define MCP_ERROR_CRC 0x51 + +#define MCP_SINGLE_WIRE 0xAB + +#define MCP_SET_ADDRESS 0x41 + +#define MCP_READ 0x4E +#define MCP_READ_16 0x52 +#define MCP_READ_32 0x44 + +#define MCP_WRITE 0x4D +#define MCP_WRITE_16 0x57 +#define MCP_WRITE_32 0x45 + +#define MCP_SAVE_REGISTERS 0x53 + +#define MCP_CALIBRATION_BASE 0x0028 +#define MCP_CALIBRATION_LEN 52 + +#define MCP_FREQUENCY_REF_BASE 0x0094 +#define MCP_FREQUENCY_GAIN_BASE 0x00AE +#define MCP_FREQUENCY_LEN 4 + +#define MCP_BUFFER_SIZE 60 + +#include +TasmotaSerial *McpSerial = nullptr; + +typedef struct mcp_cal_registers_type { + uint16_t gain_current_rms; + uint16_t gain_voltage_rms; + uint16_t gain_active_power; + uint16_t gain_reactive_power; + sint32_t offset_current_rms; + sint32_t offset_active_power; + sint32_t offset_reactive_power; + sint16_t dc_offset_current; + sint16_t phase_compensation; + uint16_t apparent_power_divisor; + + uint32_t system_configuration; + uint16_t dio_configuration; + uint32_t range; + + uint32_t calibration_current; + uint16_t calibration_voltage; + uint32_t calibration_active_power; + uint32_t calibration_reactive_power; + uint16_t accumulation_interval; +} mcp_cal_registers_type; + +char *mcp_buffer = nullptr; +unsigned long mcp_window = 0; +unsigned long mcp_kWhcounter = 0; +uint32_t mcp_system_configuration = 0x03000000; +uint32_t mcp_active_power; + + +uint32_t mcp_current_rms; +uint16_t mcp_voltage_rms; +uint16_t mcp_line_frequency; + +uint8_t mcp_address = 0; +uint8_t mcp_calibration_active = 0; +uint8_t mcp_init = 0; +uint8_t mcp_timeout = 0; +uint8_t mcp_calibrate = 0; +uint8_t mcp_byte_counter = 0; + + + + + + +uint8_t McpChecksum(uint8_t *data) +{ + uint8_t checksum = 0; + uint8_t offset = 0; + uint8_t len = data[1] -1; + + for (uint32_t i = offset; i < len; i++) { checksum += data[i]; } + return checksum; +} + +unsigned long McpExtractInt(char *data, uint8_t offset, uint8_t size) +{ + unsigned long result = 0; + unsigned long pow = 1; + + for (uint32_t i = 0; i < size; i++) { + result = result + (uint8_t)data[offset + i] * pow; + pow = pow * 256; + } + return result; +} + +void McpSetInt(unsigned long value, uint8_t *data, uint8_t offset, size_t size) +{ + for (uint32_t i = 0; i < size; i++) { + data[offset + i] = ((value >> (i * 8)) & 0xFF); + } +} + +void McpSend(uint8_t *data) +{ + if (mcp_timeout) { return; } + mcp_timeout = MCP_TIMEOUT; + + data[0] = MCP_START_FRAME; + data[data[1] -1] = McpChecksum(data); + + + + for (uint32_t i = 0; i < data[1]; i++) { + McpSerial->write(data[i]); + } +} + + + +void McpGetAddress(void) +{ + uint8_t data[] = { MCP_START_FRAME, 7, MCP_SET_ADDRESS, 0x00, 0x26, MCP_READ_16, 0x00 }; + + McpSend(data); +} + +void McpAddressReceive(void) +{ + + mcp_address = mcp_buffer[3]; +} + + + +void McpGetCalibration(void) +{ + if (mcp_calibration_active) { return; } + mcp_calibration_active = MCP_CALIBRATION_TIMEOUT; + + uint8_t data[] = { MCP_START_FRAME, 8, MCP_SET_ADDRESS, (MCP_CALIBRATION_BASE >> 8) & 0xFF, MCP_CALIBRATION_BASE & 0xFF, MCP_READ, MCP_CALIBRATION_LEN, 0x00 }; + + McpSend(data); +} + +void McpParseCalibration(void) +{ + bool action = false; + mcp_cal_registers_type cal_registers; + + + cal_registers.gain_current_rms = McpExtractInt(mcp_buffer, 2, 2); + cal_registers.gain_voltage_rms = McpExtractInt(mcp_buffer, 4, 2); + cal_registers.gain_active_power = McpExtractInt(mcp_buffer, 6, 2); + cal_registers.gain_reactive_power = McpExtractInt(mcp_buffer, 8, 2); + cal_registers.offset_current_rms = McpExtractInt(mcp_buffer, 10, 4); + cal_registers.offset_active_power = McpExtractInt(mcp_buffer, 14, 4); + cal_registers.offset_reactive_power = McpExtractInt(mcp_buffer, 18, 4); + cal_registers.dc_offset_current = McpExtractInt(mcp_buffer, 22, 2); + cal_registers.phase_compensation = McpExtractInt(mcp_buffer, 24, 2); + cal_registers.apparent_power_divisor = McpExtractInt(mcp_buffer, 26, 2); + + cal_registers.system_configuration = McpExtractInt(mcp_buffer, 28, 4); + cal_registers.dio_configuration = McpExtractInt(mcp_buffer, 32, 2); + cal_registers.range = McpExtractInt(mcp_buffer, 34, 4); + + cal_registers.calibration_current = McpExtractInt(mcp_buffer, 38, 4); + cal_registers.calibration_voltage = McpExtractInt(mcp_buffer, 42, 2); + cal_registers.calibration_active_power = McpExtractInt(mcp_buffer, 44, 4); + cal_registers.calibration_reactive_power = McpExtractInt(mcp_buffer, 48, 4); + cal_registers.accumulation_interval = McpExtractInt(mcp_buffer, 52, 2); + + if (mcp_calibrate & MCP_CALIBRATE_POWER) { + cal_registers.calibration_active_power = Settings.energy_power_calibration; + if (McpCalibrationCalc(&cal_registers, 16)) { action = true; } + } + if (mcp_calibrate & MCP_CALIBRATE_VOLTAGE) { + cal_registers.calibration_voltage = Settings.energy_voltage_calibration; + if (McpCalibrationCalc(&cal_registers, 0)) { action = true; } + } + if (mcp_calibrate & MCP_CALIBRATE_CURRENT) { + cal_registers.calibration_current = Settings.energy_current_calibration; + if (McpCalibrationCalc(&cal_registers, 8)) { action = true; } + } + mcp_timeout = 0; + if (action) { McpSetCalibration(&cal_registers); } + + mcp_calibrate = 0; + + Settings.energy_power_calibration = cal_registers.calibration_active_power; + Settings.energy_voltage_calibration = cal_registers.calibration_voltage; + Settings.energy_current_calibration = cal_registers.calibration_current; + + mcp_system_configuration = cal_registers.system_configuration; + + if (mcp_system_configuration & MCP_SINGLE_WIRE_FLAG) { + mcp_system_configuration &= ~MCP_SINGLE_WIRE_FLAG; + McpSetSystemConfiguration(2); + } +} + +bool McpCalibrationCalc(struct mcp_cal_registers_type *cal_registers, uint8_t range_shift) +{ + uint32_t measured; + uint32_t expected; + uint16_t *gain; + uint32_t new_gain; + + if (range_shift == 0) { + measured = mcp_voltage_rms; + expected = cal_registers->calibration_voltage; + gain = &(cal_registers->gain_voltage_rms); + } else if (range_shift == 8) { + measured = mcp_current_rms; + expected = cal_registers->calibration_current; + gain = &(cal_registers->gain_current_rms); + } else if (range_shift == 16) { + measured = mcp_active_power; + expected = cal_registers->calibration_active_power; + gain = &(cal_registers->gain_active_power); + } else { + return false; + } + + if (measured == 0) { + return false; + } + + uint32_t range = (cal_registers->range >> range_shift) & 0xFF; + +calc: + new_gain = (*gain) * expected / measured; + + if (new_gain < 25000) { + range++; + if (measured > 6) { + measured = measured / 2; + goto calc; + } + } + + if (new_gain > 55000) { + range--; + measured = measured * 2; + goto calc; + } + + *gain = new_gain; + uint32_t old_range = (cal_registers->range >> range_shift) & 0xFF; + cal_registers->range = cal_registers->range ^ (old_range << range_shift); + cal_registers->range = cal_registers->range | (range << range_shift); + + return true; +} + + + + + + +void McpSetCalibration(struct mcp_cal_registers_type *cal_registers) +{ + uint8_t data[7 + MCP_CALIBRATION_LEN + 2 + 1]; + + data[1] = sizeof(data); + data[2] = MCP_SET_ADDRESS; + data[3] = (MCP_CALIBRATION_BASE >> 8) & 0xFF; + data[4] = (MCP_CALIBRATION_BASE >> 0) & 0xFF; + + data[5] = MCP_WRITE; + data[6] = MCP_CALIBRATION_LEN; + + McpSetInt(cal_registers->gain_current_rms, data, 0+7, 2); + McpSetInt(cal_registers->gain_voltage_rms, data, 2+7, 2); + McpSetInt(cal_registers->gain_active_power, data, 4+7, 2); + McpSetInt(cal_registers->gain_reactive_power, data, 6+7, 2); + McpSetInt(cal_registers->offset_current_rms, data, 8+7, 4); + McpSetInt(cal_registers->offset_active_power, data, 12+7, 4); + McpSetInt(cal_registers->offset_reactive_power, data, 16+7, 4); + McpSetInt(cal_registers->dc_offset_current, data, 20+7, 2); + McpSetInt(cal_registers->phase_compensation, data, 22+7, 2); + McpSetInt(cal_registers->apparent_power_divisor, data, 24+7, 2); + + McpSetInt(cal_registers->system_configuration, data, 26+7, 4); + McpSetInt(cal_registers->dio_configuration, data, 30+7, 2); + McpSetInt(cal_registers->range, data, 32+7, 4); + + McpSetInt(cal_registers->calibration_current, data, 36+7, 4); + McpSetInt(cal_registers->calibration_voltage, data, 40+7, 2); + McpSetInt(cal_registers->calibration_active_power, data, 42+7, 4); + McpSetInt(cal_registers->calibration_reactive_power, data, 46+7, 4); + McpSetInt(cal_registers->accumulation_interval, data, 50+7, 2); + + data[MCP_CALIBRATION_LEN+7] = MCP_SAVE_REGISTERS; + data[MCP_CALIBRATION_LEN+8] = mcp_address; + + McpSend(data); +} + + + +void McpSetSystemConfiguration(uint16 interval) +{ + + uint8_t data[17]; + + data[ 1] = sizeof(data); + data[ 2] = MCP_SET_ADDRESS; + data[ 3] = 0x00; + data[ 4] = 0x42; + data[ 5] = MCP_WRITE_32; + data[ 6] = (mcp_system_configuration >> 24) & 0xFF; + data[ 7] = (mcp_system_configuration >> 16) & 0xFF; + data[ 8] = (mcp_system_configuration >> 8) & 0xFF; + data[ 9] = (mcp_system_configuration >> 0) & 0xFF; + data[10] = MCP_SET_ADDRESS; + data[11] = 0x00; + data[12] = 0x5A; + data[13] = MCP_WRITE_16; + data[14] = (interval >> 8) & 0xFF; + data[15] = (interval >> 0) & 0xFF; + + McpSend(data); +} + + + +void McpGetFrequency(void) +{ + if (mcp_calibration_active) { return; } + mcp_calibration_active = MCP_CALIBRATION_TIMEOUT; + + uint8_t data[] = { MCP_START_FRAME, 11, MCP_SET_ADDRESS, (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF, MCP_FREQUENCY_REF_BASE & 0xFF, MCP_READ_16, + MCP_SET_ADDRESS, (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF, MCP_FREQUENCY_GAIN_BASE & 0xFF, MCP_READ_16, 0x00 }; + + McpSend(data); +} + +void McpParseFrequency(void) +{ + + uint16_t line_frequency_ref = mcp_buffer[2] * 256 + mcp_buffer[3]; + uint16_t gain_line_frequency = mcp_buffer[4] * 256 + mcp_buffer[5]; + + if (mcp_calibrate & MCP_CALIBRATE_FREQUENCY) { + line_frequency_ref = Settings.energy_frequency_calibration; + + if ((0xFFFF == mcp_line_frequency) || (0 == gain_line_frequency)) { + mcp_line_frequency = 50000; + gain_line_frequency = 0x8000; + } + gain_line_frequency = gain_line_frequency * line_frequency_ref / mcp_line_frequency; + + mcp_timeout = 0; + McpSetFrequency(line_frequency_ref, gain_line_frequency); + } + + Settings.energy_frequency_calibration = line_frequency_ref; + + mcp_calibrate = 0; +} + +void McpSetFrequency(uint16_t line_frequency_ref, uint16_t gain_line_frequency) +{ + + uint8_t data[17]; + + data[ 1] = sizeof(data); + data[ 2] = MCP_SET_ADDRESS; + data[ 3] = (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF; + data[ 4] = (MCP_FREQUENCY_REF_BASE >> 0) & 0xFF; + + data[ 5] = MCP_WRITE_16; + data[ 6] = (line_frequency_ref >> 8) & 0xFF; + data[ 7] = (line_frequency_ref >> 0) & 0xFF; + + data[ 8] = MCP_SET_ADDRESS; + data[ 9] = (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF; + data[10] = (MCP_FREQUENCY_GAIN_BASE >> 0) & 0xFF; + + data[11] = MCP_WRITE_16; + data[12] = (gain_line_frequency >> 8) & 0xFF; + data[13] = (gain_line_frequency >> 0) & 0xFF; + + data[14] = MCP_SAVE_REGISTERS; + data[15] = mcp_address; + + McpSend(data); +} + + + +void McpGetData(void) +{ + uint8_t data[] = { MCP_START_FRAME, 8, MCP_SET_ADDRESS, 0x00, 0x04, MCP_READ, 22, 0x00 }; + + McpSend(data); +} + +void McpParseData(void) +{ + + + + + + mcp_current_rms = McpExtractInt(mcp_buffer, 2, 4); + mcp_voltage_rms = McpExtractInt(mcp_buffer, 6, 2); + mcp_active_power = McpExtractInt(mcp_buffer, 8, 4); + + + mcp_line_frequency = McpExtractInt(mcp_buffer, 22, 2); + + if (Energy.power_on) { + Energy.data_valid[0] = 0; + Energy.frequency[0] = (float)mcp_line_frequency / 1000; + Energy.voltage[0] = (float)mcp_voltage_rms / 10; + Energy.active_power[0] = (float)mcp_active_power / 100; + if (0 == Energy.active_power[0]) { + Energy.current[0] = 0; + } else { + Energy.current[0] = (float)mcp_current_rms / 10000; + } + } else { + Energy.data_valid[0] = ENERGY_WATCHDOG; + } +} + + + +void McpSerialInput(void) +{ + while ((McpSerial->available()) && (mcp_byte_counter < MCP_BUFFER_SIZE)) { + yield(); + mcp_buffer[mcp_byte_counter++] = McpSerial->read(); + mcp_window = millis(); + } + + + if ((mcp_byte_counter) && (millis() - mcp_window > (24000 / MCP_BAUDRATE) +1)) { + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, (uint8_t*)mcp_buffer, mcp_byte_counter); + + if (MCP_BUFFER_SIZE == mcp_byte_counter) { + + } + else if (1 == mcp_byte_counter) { + if (MCP_ERROR_CRC == mcp_buffer[0]) { + + mcp_timeout = 0; + } + else if (MCP_ERROR_NAK == mcp_buffer[0]) { + + mcp_timeout = 0; + } + } + else if (MCP_ACK_FRAME == mcp_buffer[0]) { + if (mcp_byte_counter == mcp_buffer[1]) { + + if (McpChecksum((uint8_t *)mcp_buffer) != mcp_buffer[mcp_byte_counter -1]) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: " D_CHECKSUM_FAILURE)); + } else { + if (5 == mcp_buffer[1]) { McpAddressReceive(); } + if (25 == mcp_buffer[1]) { McpParseData(); } + if (MCP_CALIBRATION_LEN + 3 == mcp_buffer[1]) { McpParseCalibration(); } + if (MCP_FREQUENCY_LEN + 3 == mcp_buffer[1]) { McpParseFrequency(); } + } + + } + mcp_timeout = 0; + } + else if (MCP_SINGLE_WIRE == mcp_buffer[0]) { + mcp_timeout = 0; + } + + mcp_byte_counter = 0; + McpSerial->flush(); + } +} + + + +void McpEverySecond(void) +{ + if (Energy.data_valid[0] > ENERGY_WATCHDOG) { + mcp_voltage_rms = 0; + mcp_current_rms = 0; + mcp_active_power = 0; + mcp_line_frequency = 0; + } + + if (mcp_active_power) { + Energy.kWhtoday_delta += ((mcp_active_power * 10) / 36); + EnergyUpdateToday(); + } + + if (mcp_timeout) { + mcp_timeout--; + } + else if (mcp_calibration_active) { + mcp_calibration_active--; + } + else if (mcp_init) { + if (2 == mcp_init) { + McpGetCalibration(); + } + else if (1 == mcp_init) { + McpGetFrequency(); + } + mcp_init--; + } + else if (!mcp_address) { + McpGetAddress(); + } + else { + McpGetData(); + } +} + +void McpSnsInit(void) +{ + + McpSerial = new TasmotaSerial(pin[GPIO_MCP39F5_RX], pin[GPIO_MCP39F5_TX], 1); + if (McpSerial->begin(MCP_BAUDRATE)) { + if (McpSerial->hardwareSerial()) { + ClaimSerial(); + mcp_buffer = serial_in_buffer; + } else { + mcp_buffer = (char*)(malloc(MCP_BUFFER_SIZE)); + } + DigitalWrite(GPIO_MCP39F5_RST, 1); + } else { + energy_flg = ENERGY_NONE; + } +} + +void McpDrvInit(void) +{ + if ((pin[GPIO_MCP39F5_RX] < 99) && (pin[GPIO_MCP39F5_TX] < 99)) { + if (pin[GPIO_MCP39F5_RST] < 99) { + pinMode(pin[GPIO_MCP39F5_RST], OUTPUT); + digitalWrite(pin[GPIO_MCP39F5_RST], 0); + } + mcp_calibrate = 0; + mcp_timeout = 2; + mcp_init = 2; + energy_flg = XNRG_04; + } +} + +bool McpCommand(void) +{ + bool serviced = true; + unsigned long value = 0; + + if (CMND_POWERSET == Energy.command_code) { + if (XdrvMailbox.data_len && mcp_active_power) { + value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 100); + if ((value > 100) && (value < 200000)) { + Settings.energy_power_calibration = value; + mcp_calibrate |= MCP_CALIBRATE_POWER; + McpGetCalibration(); + } + } + } + else if (CMND_VOLTAGESET == Energy.command_code) { + if (XdrvMailbox.data_len && mcp_voltage_rms) { + value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 10); + if ((value > 1000) && (value < 2600)) { + Settings.energy_voltage_calibration = value; + mcp_calibrate |= MCP_CALIBRATE_VOLTAGE; + McpGetCalibration(); + } + } + } + else if (CMND_CURRENTSET == Energy.command_code) { + if (XdrvMailbox.data_len && mcp_current_rms) { + value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 10); + if ((value > 100) && (value < 80000)) { + Settings.energy_current_calibration = value; + mcp_calibrate |= MCP_CALIBRATE_CURRENT; + McpGetCalibration(); + } + } + } + else if (CMND_FREQUENCYSET == Energy.command_code) { + if (XdrvMailbox.data_len && mcp_line_frequency) { + value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 1000); + if ((value > 45000) && (value < 65000)) { + Settings.energy_frequency_calibration = value; + mcp_calibrate |= MCP_CALIBRATE_FREQUENCY; + McpGetFrequency(); + } + } + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg04(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_LOOP: + if (McpSerial) { McpSerialInput(); } + break; + case FUNC_ENERGY_EVERY_SECOND: + if (McpSerial) { McpEverySecond(); } + break; + case FUNC_COMMAND: + result = McpCommand(); + break; + case FUNC_INIT: + McpSnsInit(); + break; + case FUNC_PRE_INIT: + McpDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_05_pzem_ac.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_05_pzem_ac.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_PZEM_AC +# 33 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_05_pzem_ac.ino" +#define XNRG_05 5 + +const uint8_t PZEM_AC_DEVICE_ADDRESS = 0x01; +const uint32_t PZEM_AC_STABILIZE = 30; + +#include +TasmotaModbus *PzemAcModbus; + +struct PZEMAC { + float energy = 0; + float last_energy = 0; + uint8_t send_retry = 0; + uint8_t phase = 0; + uint8_t address = 0; + uint8_t address_step = ADDR_IDLE; +} PzemAc; + +void PzemAcEverySecond(void) +{ + bool data_ready = PzemAcModbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[30]; + + uint8_t registers = 10; + if (ADDR_RECEIVE == PzemAc.address_step) { + registers = 2; + PzemAc.address_step--; + } + uint8_t error = PzemAcModbus->ReceiveBuffer(buffer, registers); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemAcModbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAC: PzemAc %d error %d"), PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, error); + } else { + Energy.data_valid[PzemAc.phase] = 0; + if (10 == registers) { + + + + + + Energy.voltage[PzemAc.phase] = (float)((buffer[3] << 8) + buffer[4]) / 10.0; + Energy.current[PzemAc.phase] = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; + Energy.active_power[PzemAc.phase] = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; + Energy.frequency[PzemAc.phase] = (float)((buffer[17] << 8) + buffer[18]) / 10.0; + Energy.power_factor[PzemAc.phase] = (float)((buffer[19] << 8) + buffer[20]) / 100.0; + + PzemAc.energy += (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); + if (PzemAc.phase == Energy.phase_count -1) { + if (PzemAc.energy > PzemAc.last_energy) { + if (uptime > PZEM_AC_STABILIZE) { + EnergyUpdateTotal(PzemAc.energy, false); + } + PzemAc.last_energy = PzemAc.energy; + } + PzemAc.energy = 0; + } + + } + } + } + + if (0 == PzemAc.send_retry || data_ready) { + if (0 == PzemAc.phase) { + PzemAc.phase = Energy.phase_count -1; + } else { + PzemAc.phase--; + } + PzemAc.send_retry = ENERGY_WATCHDOG; + if (ADDR_SEND == PzemAc.address_step) { + PzemAcModbus->Send(0xF8, 0x06, 0x0002, (uint16_t)PzemAc.address); + PzemAc.address_step--; + } else { + PzemAcModbus->Send(PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, 0x04, 0, 10); + } + } + else { + PzemAc.send_retry--; + if ((Energy.phase_count > 1) && (0 == PzemAc.send_retry) && (uptime < PZEM_AC_STABILIZE)) { + Energy.phase_count--; + } + } +} + +void PzemAcSnsInit(void) +{ + PzemAcModbus = new TasmotaModbus(pin[GPIO_PZEM016_RX], pin[GPIO_PZEM0XX_TX]); + uint8_t result = PzemAcModbus->Begin(9600); + if (result) { + if (2 == result) { ClaimSerial(); } + Energy.phase_count = 3; + PzemAc.phase = 0; + } else { + energy_flg = ENERGY_NONE; + } +} + +void PzemAcDrvInit(void) +{ + if ((pin[GPIO_PZEM016_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { + energy_flg = XNRG_05; + } +} + +bool PzemAcCommand(void) +{ + bool serviced = true; + + if (CMND_MODULEADDRESS == Energy.command_code) { + PzemAc.address = XdrvMailbox.payload; + PzemAc.address_step = ADDR_SEND; + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg05(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_ENERGY_EVERY_SECOND: + if (uptime > 4) { PzemAcEverySecond(); } + break; + case FUNC_COMMAND: + result = PzemAcCommand(); + break; + case FUNC_INIT: + PzemAcSnsInit(); + break; + case FUNC_PRE_INIT: + PzemAcDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_06_pzem_dc.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_06_pzem_dc.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_PZEM_DC +# 32 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_06_pzem_dc.ino" +#define XNRG_06 6 + +const uint8_t PZEM_DC_DEVICE_ADDRESS = 0x01; +const uint32_t PZEM_DC_STABILIZE = 30; + +#include +TasmotaModbus *PzemDcModbus; + +struct PZEMDC { + float energy = 0; + float last_energy = 0; + uint8_t send_retry = 0; + uint8_t channel = 0; + uint8_t address = 0; + uint8_t address_step = ADDR_IDLE; +} PzemDc; + +void PzemDcEverySecond(void) +{ + bool data_ready = PzemDcModbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[26]; + + uint8_t registers = 8; + if (ADDR_RECEIVE == PzemDc.address_step) { + registers = 2; + PzemDc.address_step--; + } + uint8_t error = PzemDcModbus->ReceiveBuffer(buffer, registers); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemDcModbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PDC: PzemDc %d error %d"), PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, error); + } else { + Energy.data_valid[PzemDc.channel] = 0; + if (8 == registers) { + + + + + + Energy.voltage[PzemDc.channel] = (float)((buffer[3] << 8) + buffer[4]) / 100.0; + Energy.current[PzemDc.channel] = (float)((buffer[5] << 8) + buffer[6]) / 100.0; + Energy.active_power[PzemDc.channel] = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; + + PzemDc.energy += (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); + if (PzemDc.channel == Energy.phase_count -1) { + if (PzemDc.energy > PzemDc.last_energy) { + if (uptime > PZEM_DC_STABILIZE) { + EnergyUpdateTotal(PzemDc.energy, false); + } + PzemDc.last_energy = PzemDc.energy; + } + PzemDc.energy = 0; + } + } + } + } + + if (0 == PzemDc.send_retry || data_ready) { + if (0 == PzemDc.channel) { + PzemDc.channel = Energy.phase_count -1; + } else { + PzemDc.channel--; + } + PzemDc.send_retry = ENERGY_WATCHDOG; + if (ADDR_SEND == PzemDc.address_step) { + PzemDcModbus->Send(0xF8, 0x06, 0x0002, (uint16_t)PzemDc.address); + PzemDc.address_step--; + } else { + PzemDcModbus->Send(PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, 0x04, 0, 8); + } + } + else { + PzemDc.send_retry--; + if ((Energy.phase_count > 1) && (0 == PzemDc.send_retry) && (uptime < PZEM_DC_STABILIZE)) { + Energy.phase_count--; + } + } +} + +void PzemDcSnsInit(void) +{ + PzemDcModbus = new TasmotaModbus(pin[GPIO_PZEM017_RX], pin[GPIO_PZEM0XX_TX]); + uint8_t result = PzemDcModbus->Begin(9600, 2); + if (result) { + if (2 == result) { ClaimSerial(); } + Energy.type_dc = true; + Energy.phase_count = 3; + PzemDc.channel = 0; + } else { + energy_flg = ENERGY_NONE; + } +} + +void PzemDcDrvInit(void) +{ + if ((pin[GPIO_PZEM017_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { + energy_flg = XNRG_06; + } +} + +bool PzemDcCommand(void) +{ + bool serviced = true; + + if (CMND_MODULEADDRESS == Energy.command_code) { + PzemDc.address = XdrvMailbox.payload; + PzemDc.address_step = ADDR_SEND; + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg06(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_ENERGY_EVERY_SECOND: + if (uptime > 4) { PzemDcEverySecond(); } + break; + case FUNC_COMMAND: + result = PzemDcCommand(); + break; + case FUNC_INIT: + PzemDcSnsInit(); + break; + case FUNC_PRE_INIT: + PzemDcDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_07_ade7953.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_07_ade7953.ino" +#ifdef USE_I2C +#ifdef USE_ENERGY_SENSOR +#ifdef USE_ADE7953 +# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_07_ade7953.ino" +#define XNRG_07 7 +#define XI2C_07 7 + +#define ADE7953_PREF 1540 +#define ADE7953_UREF 26000 +#define ADE7953_IREF 10000 + +#define ADE7953_ADDR 0x38 + +const uint16_t Ade7953Registers[] { + 0x31B, + 0x313, + 0x311, + 0x315, + 0x31A, + 0x312, + 0x310, + 0x314, + 0x31C, + 0x10E +}; + +struct Ade7953 { + uint32_t voltage_rms = 0; + uint32_t period = 0; + uint32_t current_rms[2] = { 0, 0 }; + uint32_t active_power[2] = { 0, 0 }; + uint8_t init_step = 0; +} Ade7953; + +int Ade7953RegSize(uint16_t reg) +{ + int size = 0; + switch ((reg >> 8) & 0x0F) { + case 0x03: + size++; + case 0x02: + size++; + case 0x01: + size++; + case 0x00: + case 0x07: + case 0x08: + size++; + } + return size; +} + +void Ade7953Write(uint16_t reg, uint32_t val) +{ + int size = Ade7953RegSize(reg); + if (size) { + Wire.beginTransmission(ADE7953_ADDR); + Wire.write((reg >> 8) & 0xFF); + Wire.write(reg & 0xFF); + while (size--) { + Wire.write((val >> (8 * size)) & 0xFF); + } + Wire.endTransmission(); + delayMicroseconds(5); + } +} + +int32_t Ade7953Read(uint16_t reg) +{ + uint32_t response = 0; + + int size = Ade7953RegSize(reg); + if (size) { + Wire.beginTransmission(ADE7953_ADDR); + Wire.write((reg >> 8) & 0xFF); + Wire.write(reg & 0xFF); + Wire.endTransmission(0); + Wire.requestFrom(ADE7953_ADDR, size); + if (size <= Wire.available()) { + for (uint32_t i = 0; i < size; i++) { + response = response << 8 | Wire.read(); + } + } + } + return response; +} + +void Ade7953Init(void) +{ + Ade7953Write(0x102, 0x0004); + Ade7953Write(0x0FE, 0x00AD); + Ade7953Write(0x120, 0x0030); +} + +void Ade7953GetData(void) +{ + int32_t reg[2][4]; + for (uint32_t i = 0; i < sizeof(Ade7953Registers)/sizeof(uint16_t); i++) { + int32_t value = Ade7953Read(Ade7953Registers[i]); + if (8 == i) { + Ade7953.voltage_rms = value; + } else if (9 == i) { + Ade7953.period = value; + } else { + reg[i >> 2][i &3] = value; + } + } + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ADE: %d, %d, [%d, %d, %d, %d], [%d, %d, %d, %d]"), + Ade7953.voltage_rms, Ade7953.period, + reg[0][0], reg[0][1], reg[0][2], reg[0][3], + reg[1][0], reg[1][1], reg[1][2], reg[1][3]); + + uint32_t apparent_power[2] = { 0, 0 }; + uint32_t reactive_power[2] = { 0, 0 }; + + for (uint32_t channel = 0; channel < 2; channel++) { + Ade7953.current_rms[channel] = reg[channel][0]; + if (Ade7953.current_rms[channel] < 2000) { + Ade7953.current_rms[channel] = 0; + Ade7953.active_power[channel] = 0; + } else { + Ade7953.active_power[channel] = abs(reg[channel][1]); + apparent_power[channel] = abs(reg[channel][2]); + reactive_power[channel] = abs(reg[channel][3]); + } + } + + uint32_t current_rms_sum = Ade7953.current_rms[0] + Ade7953.current_rms[1]; + uint32_t active_power_sum = Ade7953.active_power[0] + Ade7953.active_power[1]; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ADE: U %d, C %d, I %d + %d = %d, P %d + %d = %d"), + Ade7953.voltage_rms, Ade7953.period, + Ade7953.current_rms[0], Ade7953.current_rms[1], current_rms_sum, + Ade7953.active_power[0], Ade7953.active_power[1], active_power_sum); + + if (Energy.power_on) { + Energy.voltage[0] = (float)Ade7953.voltage_rms / Settings.energy_voltage_calibration; + Energy.frequency[0] = 223750.0f / ( (float)Ade7953.period + 1); + + for (uint32_t channel = 0; channel < 2; channel++) { + Energy.data_valid[channel] = 0; + Energy.active_power[channel] = (float)Ade7953.active_power[channel] / (Settings.energy_power_calibration / 10); + Energy.reactive_power[channel] = (float)reactive_power[channel] / (Settings.energy_power_calibration / 10); + Energy.apparent_power[channel] = (float)apparent_power[channel] / (Settings.energy_power_calibration / 10); + if (0 == Energy.active_power[channel]) { + Energy.current[channel] = 0; + } else { + Energy.current[channel] = (float)Ade7953.current_rms[channel] / (Settings.energy_current_calibration * 10); + } + } + } else { + Energy.data_valid[0] = ENERGY_WATCHDOG; + Energy.data_valid[1] = ENERGY_WATCHDOG; + } + + if (active_power_sum) { + Energy.kWhtoday_delta += ((active_power_sum * (100000 / (Settings.energy_power_calibration / 10))) / 3600); + EnergyUpdateToday(); + } +} + +void Ade7953EnergyEverySecond(void) +{ + if (Ade7953.init_step) { + if (1 == Ade7953.init_step) { + Ade7953Init(); + } + Ade7953.init_step--; + } else { + Ade7953GetData(); + } +} + +void Ade7953DrvInit(void) +{ + if (pin[GPIO_ADE7953_IRQ] < 99) { + delay(100); + if (I2cSetDevice(ADE7953_ADDR)) { + if (HLW_PREF_PULSE == Settings.energy_power_calibration) { + Settings.energy_power_calibration = ADE7953_PREF; + Settings.energy_voltage_calibration = ADE7953_UREF; + Settings.energy_current_calibration = ADE7953_IREF; + } + I2cSetActiveFound(ADE7953_ADDR, "ADE7953"); + Ade7953.init_step = 2; + + Energy.phase_count = 2; + Energy.voltage_common = true; + + energy_flg = XNRG_07; + } + } +} + +bool Ade7953Command(void) +{ + bool serviced = true; + + uint32_t channel = (2 == XdrvMailbox.index) ? 1 : 0; + uint32_t value = (uint32_t)(CharToFloat(XdrvMailbox.data) * 100); + + if (CMND_POWERCAL == Energy.command_code) { + if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_PREF; } + + } + else if (CMND_VOLTAGECAL == Energy.command_code) { + if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_UREF; } + + } + else if (CMND_CURRENTCAL == Energy.command_code) { + if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_IREF; } + + } + else if (CMND_POWERSET == Energy.command_code) { + if (XdrvMailbox.data_len && Ade7953.active_power[channel]) { + if ((value > 100) && (value < 200000)) { + Settings.energy_power_calibration = (Ade7953.active_power[channel] * 1000) / value; + } + } + } + else if (CMND_VOLTAGESET == Energy.command_code) { + if (XdrvMailbox.data_len && Ade7953.voltage_rms) { + if ((value > 10000) && (value < 26000)) { + Settings.energy_voltage_calibration = (Ade7953.voltage_rms * 100) / value; + } + } + } + else if (CMND_CURRENTSET == Energy.command_code) { + if (XdrvMailbox.data_len && Ade7953.current_rms[channel]) { + if ((value > 2000) && (value < 1000000)) { + Settings.energy_current_calibration = ((Ade7953.current_rms[channel] * 100) / value) * 100; + } + } + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg07(uint8_t function) +{ + if (!I2cEnabled(XI2C_07)) { return false; } + + bool result = false; + + switch (function) { + case FUNC_ENERGY_EVERY_SECOND: + Ade7953EnergyEverySecond(); + break; + case FUNC_COMMAND: + result = Ade7953Command(); + break; + case FUNC_PRE_INIT: + Ade7953DrvInit(); + break; + } + return result; +} + +#endif +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_08_sdm120.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_08_sdm120.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_SDM120 + + + + + + +#define XNRG_08 8 + + +#ifndef SDM120_SPEED + #define SDM120_SPEED 2400 +#endif + +#ifndef SDM120_ADDR + #define SDM120_ADDR 1 +#endif + +#include +TasmotaModbus *Sdm120Modbus; + +const uint8_t sdm120_table = 8; +const uint8_t sdm220_table = 13; + +const uint16_t sdm120_start_addresses[] { + 0x0000, + 0x0006, + 0x000C, + 0x0012, + 0x0018, + 0x001E, + 0x0046, + 0x0156, + + 0X0048, + 0X004A, + 0X004C, + 0X004E, + 0X0024 +}; + +struct SDM120 { + float total_active = 0; + float import_active = NAN; + float import_reactive = 0; + float export_reactive = 0; + float phase_angle = 0; + uint8_t read_state = 0; + uint8_t send_retry = 0; + uint8_t start_address_count = sdm220_table; +} Sdm120; + + + +void SDM120Every250ms(void) +{ + bool data_ready = Sdm120Modbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[14]; + + uint32_t error = Sdm120Modbus->ReceiveBuffer(buffer, 2); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Sdm120Modbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: SDM120 error %d"), error); + } else { + Energy.data_valid[0] = 0; + + + + + float value; + ((uint8_t*)&value)[3] = buffer[3]; + ((uint8_t*)&value)[2] = buffer[4]; + ((uint8_t*)&value)[1] = buffer[5]; + ((uint8_t*)&value)[0] = buffer[6]; + + switch(Sdm120.read_state) { + case 0: + Energy.voltage[0] = value; + break; + + case 1: + Energy.current[0] = value; + break; + + case 2: + Energy.active_power[0] = value; + break; + + case 3: + Energy.apparent_power[0] = value; + break; + + case 4: + Energy.reactive_power[0] = value; + break; + + case 5: + Energy.power_factor[0] = value; + break; + + case 6: + Energy.frequency[0] = value; + break; + + case 7: + Sdm120.total_active = value; + break; + + case 8: + Sdm120.import_active = value; + break; + + case 9: + Energy.export_active = value; + break; + + case 10: + Sdm120.import_reactive = value; + break; + + case 11: + Sdm120.export_reactive = value; + break; + + case 12: + Sdm120.phase_angle = value; + break; + } + + Sdm120.read_state++; + if (Sdm120.read_state == Sdm120.start_address_count) { + Sdm120.read_state = 0; + + if (Sdm120.start_address_count > sdm120_table) { + if (!isnan(Sdm120.import_active)) { + Sdm120.total_active = Sdm120.import_active; + } else { + Sdm120.start_address_count = sdm120_table; + } + } + EnergyUpdateTotal(Sdm120.total_active, true); + } + } + } + + if (0 == Sdm120.send_retry || data_ready) { + Sdm120.send_retry = 5; + Sdm120Modbus->Send(SDM120_ADDR, 0x04, sdm120_start_addresses[Sdm120.read_state], 2); + } else { + Sdm120.send_retry--; + } +} + +void Sdm120SnsInit(void) +{ + Sdm120Modbus = new TasmotaModbus(pin[GPIO_SDM120_RX], pin[GPIO_SDM120_TX]); + uint8_t result = Sdm120Modbus->Begin(SDM120_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void Sdm120DrvInit(void) +{ + if ((pin[GPIO_SDM120_RX] < 99) && (pin[GPIO_SDM120_TX] < 99)) { + energy_flg = XNRG_08; + } +} + +void Sdm220Reset(void) +{ + if (isnan(Sdm120.import_active)) { return; } + + Sdm120.import_active = 0; + Sdm120.import_reactive = 0; + Sdm120.export_reactive = 0; + Sdm120.phase_angle = 0; +} + +#ifdef USE_WEBSERVER +const char HTTP_ENERGY_SDM220[] PROGMEM = + "{s}" D_IMPORT_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" + "{s}" D_EXPORT_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" + "{s}" D_PHASE_ANGLE "{m}%s " D_UNIT_ANGLE "{e}"; +#endif + +void Sdm220Show(bool json) +{ + if (isnan(Sdm120.import_active)) { return; } + + char import_active_chr[FLOATSZ]; + dtostrfd(Sdm120.import_active, Settings.flag2.energy_resolution, import_active_chr); + char import_reactive_chr[FLOATSZ]; + dtostrfd(Sdm120.import_reactive, Settings.flag2.energy_resolution, import_reactive_chr); + char export_reactive_chr[FLOATSZ]; + dtostrfd(Sdm120.export_reactive, Settings.flag2.energy_resolution, export_reactive_chr); + char phase_angle_chr[FLOATSZ]; + dtostrfd(Sdm120.phase_angle, 2, phase_angle_chr); + + if (json) { + ResponseAppend_P(PSTR(",\"" D_JSON_IMPORT_ACTIVE "\":%s,\"" D_JSON_IMPORT_REACTIVE "\":%s,\"" D_JSON_EXPORT_REACTIVE "\":%s,\"" D_JSON_PHASE_ANGLE "\":%s"), + import_active_chr, import_reactive_chr, export_reactive_chr, phase_angle_chr); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_ENERGY_SDM220, import_reactive_chr, export_reactive_chr, phase_angle_chr); +#endif + } +} + + + + + +bool Xnrg08(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { SDM120Every250ms(); } + break; + case FUNC_JSON_APPEND: + Sdm220Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Sdm220Show(0); + break; +#endif + case FUNC_ENERGY_RESET: + Sdm220Reset(); + break; + case FUNC_INIT: + Sdm120SnsInit(); + break; + case FUNC_PRE_INIT: + Sdm120DrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_09_dds2382.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_09_dds2382.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_DDS2382 + + + + + + +#define XNRG_09 9 + +#ifndef DDS2382_SPEED +#define DDS2382_SPEED 9600 +#endif +#ifndef DDS2382_ADDR +#define DDS2382_ADDR 1 +#endif + +#include +TasmotaModbus *Dds2382Modbus; + +uint8_t Dds2382_send_retry = 0; + +void Dds2382EverySecond(void) +{ + bool data_ready = Dds2382Modbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[46]; + + uint32_t error = Dds2382Modbus->ReceiveBuffer(buffer, 18); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Dds2382Modbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "DDS2382 response error %d"), error); + } else { + Energy.data_valid[0] = 0; +# 67 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_09_dds2382.ino" + Energy.voltage[0] = (float)((buffer[27] << 8) + buffer[28]) / 10.0; + Energy.current[0] = (float)((buffer[29] << 8) + buffer[30]) / 100.0; + Energy.active_power[0] = (float)((buffer[31] << 8) + buffer[32]); + Energy.reactive_power[0] = (float)((buffer[33] << 8) + buffer[34]); + Energy.power_factor[0] = (float)((buffer[35] << 8) + buffer[36]) / 1000.0; + Energy.frequency[0] = (float)((buffer[37] << 8) + buffer[38]) / 100.0; + uint8_t offset = 11; + if (Settings.flag3.dds2382_model) { + offset = 19; + } + Energy.export_active = (float)((buffer[offset] << 24) + (buffer[offset +1] << 16) + (buffer[offset +2] << 8) + buffer[offset +3]) / 100.0; + float import_active = (float)((buffer[offset +4] << 24) + (buffer[offset +5] << 16) + (buffer[offset +6] << 8) + buffer[offset +7]) / 100.0; + + EnergyUpdateTotal(import_active, true); + } + } + + if (0 == Dds2382_send_retry || data_ready) { + Dds2382_send_retry = 5; + Dds2382Modbus->Send(DDS2382_ADDR, 0x03, 0, 18); + } else { + Dds2382_send_retry--; + } +} + +void Dds2382SnsInit(void) +{ + Dds2382Modbus = new TasmotaModbus(pin[GPIO_DDS2382_RX], pin[GPIO_DDS2382_TX]); + uint8_t result = Dds2382Modbus->Begin(DDS2382_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void Dds2382DrvInit(void) +{ + if ((pin[GPIO_DDS2382_RX] < 99) && (pin[GPIO_DDS2382_TX] < 99)) { + energy_flg = XNRG_09; + } +} + + + + + +bool Xnrg09(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_ENERGY_EVERY_SECOND: + if (uptime > 4) { Dds2382EverySecond(); } + break; + case FUNC_INIT: + Dds2382SnsInit(); + break; + case FUNC_PRE_INIT: + Dds2382DrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_10_sdm630.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_10_sdm630.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_SDM630 + + + + + + +#define XNRG_10 10 + + +#ifndef SDM630_SPEED + #define SDM630_SPEED 9600 +#endif + +#ifndef SDM630_ADDR + #define SDM630_ADDR 1 +#endif + +#include +TasmotaModbus *Sdm630Modbus; + +const uint16_t sdm630_start_addresses[] { + 0x0000, + 0x0002, + 0x0004, + 0x0006, + 0x0008, + 0x000A, + 0x000C, + 0x000E, + 0x0010, + 0x0018, + 0x001A, + 0x001C, + 0x001E, + 0x0020, + 0x0022, + 0x0156 +}; + +struct SDM630 { + uint8_t read_state = 0; + uint8_t send_retry = 0; +} Sdm630; + + + +void SDM630Every250ms(void) +{ + bool data_ready = Sdm630Modbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[14]; + + uint32_t error = Sdm630Modbus->ReceiveBuffer(buffer, 2); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Sdm630Modbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: SDM630 error %d"), error); + } else { + Energy.data_valid[0] = 0; + Energy.data_valid[1] = 0; + Energy.data_valid[2] = 0; + + + + + float value; + ((uint8_t*)&value)[3] = buffer[3]; + ((uint8_t*)&value)[2] = buffer[4]; + ((uint8_t*)&value)[1] = buffer[5]; + ((uint8_t*)&value)[0] = buffer[6]; + + switch(Sdm630.read_state) { + case 0: + Energy.voltage[0] = value; + break; + + case 1: + Energy.voltage[1] = value; + break; + + case 2: + Energy.voltage[2] = value; + break; + + case 3: + Energy.current[0] = value; + break; + + case 4: + Energy.current[1] = value; + break; + + case 5: + Energy.current[2] = value; + break; + + case 6: + Energy.active_power[0] = value; + break; + + case 7: + Energy.active_power[1] = value; + break; + + case 8: + Energy.active_power[2] = value; + break; + + case 9: + Energy.reactive_power[0] = value; + break; + + case 10: + Energy.reactive_power[1] = value; + break; + + case 11: + Energy.reactive_power[2] = value; + break; + + case 12: + Energy.power_factor[0] = value; + break; + + case 13: + Energy.power_factor[1] = value; + break; + + case 14: + Energy.power_factor[2] = value; + break; + + case 15: + EnergyUpdateTotal(value, true); + break; + } + + Sdm630.read_state++; + if (sizeof(sdm630_start_addresses)/2 == Sdm630.read_state) { + Sdm630.read_state = 0; + } + } + } + + if (0 == Sdm630.send_retry || data_ready) { + Sdm630.send_retry = 5; + Sdm630Modbus->Send(SDM630_ADDR, 0x04, sdm630_start_addresses[Sdm630.read_state], 2); + } else { + Sdm630.send_retry--; + } +} + +void Sdm630SnsInit(void) +{ + Sdm630Modbus = new TasmotaModbus(pin[GPIO_SDM630_RX], pin[GPIO_SDM630_TX]); + uint8_t result = Sdm630Modbus->Begin(SDM630_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + Energy.phase_count = 3; + } else { + energy_flg = ENERGY_NONE; + } +} + +void Sdm630DrvInit(void) +{ + if ((pin[GPIO_SDM630_RX] < 99) && (pin[GPIO_SDM630_TX] < 99)) { + energy_flg = XNRG_10; + } +} + + + + + +bool Xnrg10(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { SDM630Every250ms(); } + break; + case FUNC_INIT: + Sdm630SnsInit(); + break; + case FUNC_PRE_INIT: + Sdm630DrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_11_ddsu666.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_11_ddsu666.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_DDSU666 + + + + +#define XNRG_11 11 + + +#ifndef DDSU666_SPEED + #define DDSU666_SPEED 9600 +#endif + +#ifndef DDSU666_ADDR + #define DDSU666_ADDR 1 +#endif + +#include +TasmotaModbus *Ddsu666Modbus; + +const uint16_t Ddsu666_start_addresses[] { + 0x2000, + 0x2002, + 0x2004, + 0x2006, + 0x200A, + 0x200E, + 0X4000, + 0X400A, +}; + +struct DDSU666 { + float import_active = NAN; + uint8_t read_state = 0; + uint8_t send_retry = 0; +} Ddsu666; + + + +void DDSU666Every250ms(void) +{ + bool data_ready = Ddsu666Modbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[14]; + + uint32_t error = Ddsu666Modbus->ReceiveBuffer(buffer, 2); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Ddsu666Modbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: Ddsu666 error %d"), error); + } else { + Energy.data_valid[0] = 0; + + + + + float value; + ((uint8_t*)&value)[3] = buffer[3]; + ((uint8_t*)&value)[2] = buffer[4]; + ((uint8_t*)&value)[1] = buffer[5]; + ((uint8_t*)&value)[0] = buffer[6]; + + switch(Ddsu666.read_state) { + case 0: + Energy.voltage[0] = value; + break; + + case 1: + Energy.current[0] = value; + break; + + case 2: + Energy.active_power[0] = value * 1000; + break; + + case 3: + Energy.reactive_power[0] = value * 1000; + break; + + case 4: + Energy.power_factor[0] = value; + break; + + case 5: + Energy.frequency[0] = value; + break; + + case 6: + Ddsu666.import_active = value; + break; + + case 7: + Energy.export_active = value; + break; + } + + Ddsu666.read_state++; + + if (Ddsu666.read_state == 8) { + Ddsu666.read_state = 0; + EnergyUpdateTotal(Ddsu666.import_active, true); + } + } + } + + if (0 == Ddsu666.send_retry || data_ready) { + Ddsu666.send_retry = 5; + Ddsu666Modbus->Send(DDSU666_ADDR, 0x04, Ddsu666_start_addresses[Ddsu666.read_state], 2); + } else { + Ddsu666.send_retry--; + } +} + +void Ddsu666SnsInit(void) +{ + Ddsu666Modbus = new TasmotaModbus(pin[GPIO_DDSU666_RX], pin[GPIO_DDSU666_TX]); + uint8_t result = Ddsu666Modbus->Begin(DDSU666_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void Ddsu666DrvInit(void) +{ + if ((pin[GPIO_DDSU666_RX] < 99) && (pin[GPIO_DDSU666_TX] < 99)) { + energy_flg = XNRG_11; + } +} + + + + + +bool Xnrg11(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { DDSU666Every250ms(); } + break; + case FUNC_INIT: + Ddsu666SnsInit(); + break; + case FUNC_PRE_INIT: + Ddsu666DrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_12_solaxX1.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_12_solaxX1.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_SOLAX_X1 + + + + +#define XNRG_12 12 + +#ifndef SOLAXX1_SPEED +#define SOLAXX1_SPEED 9600 +#endif + +#define INVERTER_ADDRESS 0x0A + +#define D_SOLAX_X1 "SolaxX1" + +#include + +enum solaxX1_Error +{ + solaxX1_ERR_NO_ERROR, + solaxX1_ERR_CRC_ERROR +}; + +union { + uint32_t ErrMessage; + struct { + + uint8_t TzProtectFault:1; + uint8_t MainsLostFault:1; + uint8_t GridVoltFault:1; + uint8_t GridFreqFault:1; + uint8_t PLLLostFault:1; + uint8_t BusVoltFault:1; + uint8_t ErrBit06:1; + uint8_t OciFault:1; + + uint8_t Dci_OCP_Fault:1; + uint8_t ResidualCurrentFault:1; + uint8_t PvVoltFault:1; + uint8_t Ac10Mins_Voltage_Fault:1; + uint8_t IsolationFault:1; + uint8_t TemperatureOverFault:1; + uint8_t FanFault:1; + uint8_t ErrBit15:1; + + uint8_t SpiCommsFault:1; + uint8_t SciCommsFault:1; + uint8_t ErrBit18:1; + uint8_t InputConfigFault:1; + uint8_t EepromFault:1; + uint8_t RelayFault:1; + uint8_t SampleConsistenceFault:1; + uint8_t ResidualCurrent_DeviceFault:1; + + uint8_t ErrBit24:1; + uint8_t ErrBit25:1; + uint8_t ErrBit26:1; + uint8_t ErrBit27:1; + uint8_t ErrBit28:1; + uint8_t DCI_DeviceFault:1; + uint8_t OtherDeviceFault:1; + uint8_t ErrBit31:1; + }; +} ErrCode; + +const char kSolaxMode[] PROGMEM = D_WAITING "|" D_CHECKING "|" D_WORKING "|" D_FAILURE; + +const char kSolaxError[] PROGMEM = + D_SOLAX_ERROR_0 "|" D_SOLAX_ERROR_1 "|" D_SOLAX_ERROR_2 "|" D_SOLAX_ERROR_3 "|" D_SOLAX_ERROR_4 "|" D_SOLAX_ERROR_5 "|" + D_SOLAX_ERROR_6 "|" D_SOLAX_ERROR_7 "|" D_SOLAX_ERROR_8; + + + +TasmotaSerial *solaxX1Serial; + +uint8_t solaxX1_Init = 1; + +struct SOLAXX1 { + float temperature = 0; + float energy_today = 0; + float dc1_voltage = 0; + float dc2_voltage = 0; + float dc1_current = 0; + float dc2_current = 0; + float energy_total = 0; + float runtime_total = 0; + float dc1_power = 0; + float dc2_power = 0; + + uint8_t status = 0; + uint32_t errorCode = 0; +} solaxX1; + +union { + uint8_t status; + struct { + uint8_t freeBit7:1; + uint8_t freeBit6:1; + uint8_t freeBit5:1; + uint8_t queryOffline:1; + uint8_t queryOfflineSend:1; + uint8_t hasAddress:1; + uint8_t inverterAddressSend:1; + uint8_t inverterSnReceived:1; + }; +} protocolStatus; + +uint8_t header[2] = {0xAA, 0x55}; +uint8_t source[2] = {0x00, 0x00}; +uint8_t destination[2] = {0x00, 0x00}; +uint8_t controlCode[1] = {0x00}; +uint8_t functionCode[1] = {0x00}; +uint8_t dataLength[1] = {0x00}; +uint8_t data[16] = {0}; + +uint8_t message[30]; + + + +bool solaxX1_RS485ReceiveReady(void) +{ + return (solaxX1Serial->available() > 1); +} + +void solaxX1_RS485Send(uint16_t msgLen) +{ + memcpy(message, header, 2); + memcpy(message + 2, source, 2); + memcpy(message + 4, destination, 2); + memcpy(message + 6, controlCode, 1); + memcpy(message + 7, functionCode, 1); + memcpy(message + 8, dataLength, 1); + memcpy(message + 9, data, sizeof(data)); + uint16_t crc = solaxX1_calculateCRC(message, msgLen); + + while (solaxX1Serial->available() > 0) + { + solaxX1Serial->read(); + } + + solaxX1Serial->flush(); + solaxX1Serial->write(message, msgLen); + solaxX1Serial->write(highByte(crc)); + solaxX1Serial->write(lowByte(crc)); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, message, msgLen); +} + +uint8_t solaxX1_RS485Receive(uint8_t *value) +{ + uint8_t len = 0; + + while (solaxX1Serial->available() > 0) + { + value[len++] = (uint8_t)solaxX1Serial->read(); + } + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, value, len); + + uint16_t crc = solaxX1_calculateCRC(value, len - 2); + + if (value[len - 1] == lowByte(crc) && value[len - 2] == highByte(crc)) + { + return solaxX1_ERR_NO_ERROR; + } + else + { + return solaxX1_ERR_CRC_ERROR; + } +} + +uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen) +{ + uint8_t i; + uint16_t wChkSum; + wChkSum = 0; + + for (i = 0; i < bLen; i++) + { + wChkSum = wChkSum + bExternTxPackage[i]; + } + return wChkSum; +} + +void solaxX1_SendInverterAddress(void) +{ + source[0] = 0x00; + destination[0] = 0x00; + destination[1] = 0x00; + controlCode[0] = 0x10; + functionCode[0] = 0x01; + dataLength[0] = 0x0F; + data[14] = INVERTER_ADDRESS; + solaxX1_RS485Send(24); +} + +void solaxX1_QueryLiveData(void) +{ + source[0] = 0x01; + destination[0] = 0x00; + destination[1] = INVERTER_ADDRESS; + controlCode[0] = 0x11; + functionCode[0] = 0x02; + dataLength[0] = 0x00; + solaxX1_RS485Send(9); +} + +uint8_t solaxX1_ParseErrorCode(uint32_t code){ + ErrCode.ErrMessage = code; + + if (code == 0) return 0; + if (ErrCode.MainsLostFault) return 1; + if (ErrCode.GridVoltFault) return 2; + if (ErrCode.GridFreqFault) return 3; + if (ErrCode.PvVoltFault) return 4; + if (ErrCode.IsolationFault) return 5; + if (ErrCode.TemperatureOverFault) return 6; + if (ErrCode.FanFault) return 7; + if (ErrCode.OtherDeviceFault) return 8; +} + + + +uint8_t solaxX1_send_retry = 0; +uint8_t solaxX1_nodata_count = 0; + +void solaxX1250MSecond(void) +{ + uint8_t value[61] = {0}; + bool data_ready = solaxX1_RS485ReceiveReady(); + + if (protocolStatus.hasAddress && (data_ready || solaxX1_send_retry == 0)) + { + if (data_ready) + { + uint8_t error = solaxX1_RS485Receive(value); + if (error) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Data response CRC error")); + } + else + { + solaxX1_nodata_count = 0; + solaxX1_send_retry = 12; + Energy.data_valid[0] = 0; + + solaxX1.temperature = (float)((value[9] << 8) | value[10]); + solaxX1.energy_today = (float)((value[11] << 8) | value[12]) * 0.1f; + solaxX1.dc1_voltage = (float)((value[13] << 8) | value[14]) * 0.1f; + solaxX1.dc2_voltage = (float)((value[15] << 8) | value[16]) * 0.1f; + solaxX1.dc1_current = (float)((value[17] << 8) | value[18]) * 0.1f; + solaxX1.dc2_current = (float)((value[19] << 8) | value[20]) * 0.1f; + Energy.current[0] = (float)((value[21] << 8) | value[22]) * 0.1f; + Energy.voltage[0] = (float)((value[23] << 8) | value[24]) * 0.1f; + Energy.frequency[0] = (float)((value[25] << 8) | value[26]) * 0.01f; + Energy.active_power[0] = (float)((value[27] << 8) | value[28]); + + solaxX1.energy_total = (float)((value[31] << 8) | (value[32] << 8) | (value[33] << 8) | value[34]) * 0.1f; + solaxX1.runtime_total = (float)((value[35] << 8) | (value[36] << 8) | (value[37] << 8) | value[38]); + solaxX1.status = (uint8_t)((value[39] << 8) | value[40]); + + + + + + + + solaxX1.errorCode = (uint32_t)((value[58] << 8) | (value[57] << 8) | (value[56] << 8) | value[55]); + + solaxX1.dc1_power = solaxX1.dc1_voltage * solaxX1.dc1_current; + solaxX1.dc2_power = solaxX1.dc2_voltage * solaxX1.dc2_current; + + solaxX1_QueryLiveData(); + EnergyUpdateTotal(solaxX1.energy_total, true); + } + } + + if (0 == solaxX1_send_retry && 255 != solaxX1_nodata_count) { + solaxX1_send_retry = 12; + solaxX1_QueryLiveData(); + } + + + + if (255 == solaxX1_nodata_count) { + solaxX1_nodata_count = 0; + solaxX1_send_retry = 12; + } + } + else + { + if ((solaxX1_nodata_count % 4) == 0) { DEBUG_SENSOR_LOG(PSTR("SX1: No Data count: %d"), solaxX1_nodata_count); } + if (solaxX1_nodata_count < 10 * 4) + { + solaxX1_nodata_count++; + } + else if (255 != solaxX1_nodata_count) + { + + solaxX1_nodata_count = 255; + solaxX1_send_retry = 12; + protocolStatus.status = 0b00001000; + Energy.data_valid[0] = ENERGY_WATCHDOG; + + solaxX1.temperature = solaxX1.dc1_voltage = solaxX1.dc2_voltage = solaxX1.dc1_current = solaxX1.dc2_current = solaxX1.dc1_power = 0; + solaxX1.dc2_power = solaxX1.status = Energy.current[0] = Energy.voltage[0] = Energy.frequency[0] = Energy.active_power[0] = 0; + + } + } + + if (!protocolStatus.hasAddress && (data_ready || solaxX1_send_retry == 0)) + { + if (data_ready) + { + + if (protocolStatus.inverterAddressSend) + { + uint8_t error = solaxX1_RS485Receive(value); + if (error) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Address confirmation response CRC error")); + } + else + { + if (value[6] == 0x10 && value[7] == 0x81 && value[9] == 0x06) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Set hasAddress")); + protocolStatus.status = 0b00100000; + } + } + } + + + if (protocolStatus.queryOfflineSend) + { + uint8_t error = solaxX1_RS485Receive(value); + if (error) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Query Offline response CRC error")); + } + else + { + + if (value[6] == 0x10 && value[7] == 0x80 && protocolStatus.inverterSnReceived == false) + { + for (uint8_t i = 9; i <= 22; i++) + { + data[i - 9] = value[i]; + } + solaxX1_SendInverterAddress(); + protocolStatus.status = 0b1100000; + DEBUG_SENSOR_LOG(PSTR("SX1: Set inverterSnReceived and inverterAddressSend")); + } + } + } + } + + if (solaxX1_send_retry == 0) + { + if (protocolStatus.queryOfflineSend) + { + protocolStatus.status = 0b00001000; + DEBUG_SENSOR_LOG(PSTR("SX1: Set Query Offline")); + } + solaxX1_send_retry = 12; + } + + + if (protocolStatus.queryOffline) + { + + source[0] = 0x01; + destination[1] = 0x00; + controlCode[0] = 0x10; + functionCode[0] = 0x00; + dataLength[0] = 0x00; + solaxX1_RS485Send(9); + protocolStatus.status = 0b00010000; + DEBUG_SENSOR_LOG(PSTR("SX1: Query Offline Send")); + } + } + + if (!data_ready) + solaxX1_send_retry--; +} + +void solaxX1SnsInit(void) +{ + AddLog_P(LOG_LEVEL_DEBUG, PSTR("SX1: Solax X1 Inverter Init")); + DEBUG_SENSOR_LOG(PSTR("SX1: RX pin: %d, TX pin: %d"), pin[GPIO_SOLAXX1_RX], pin[GPIO_SOLAXX1_TX]); + protocolStatus.status = 0b00100000; + + solaxX1Serial = new TasmotaSerial(pin[GPIO_SOLAXX1_RX], pin[GPIO_SOLAXX1_TX], 1); + if (solaxX1Serial->begin(SOLAXX1_SPEED)) { + if (solaxX1Serial->hardwareSerial()) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void solaxX1DrvInit(void) +{ + if ((pin[GPIO_SOLAXX1_RX] < 99) && (pin[GPIO_SOLAXX1_TX] < 99)) { + energy_flg = XNRG_12; + } +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_solaxX1_DATA1[] PROGMEM = + "{s}" D_SOLAX_X1 " " D_SOLAR_POWER "{m}%s " D_UNIT_WATT "{e}" + "{s}" D_SOLAX_X1 " " D_PV1_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" + "{s}" D_SOLAX_X1 " " D_PV1_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" + "{s}" D_SOLAX_X1 " " D_PV1_POWER "{m}%s " D_UNIT_WATT "{e}"; +#ifdef SOLAXX1_PV2 +const char HTTP_SNS_solaxX1_DATA2[] PROGMEM = + "{s}" D_SOLAX_X1 " " D_PV2_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" + "{s}" D_SOLAX_X1 " " D_PV2_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" + "{s}" D_SOLAX_X1 " " D_PV2_POWER "{m}%s " D_UNIT_WATT "{e}"; +#endif +const char HTTP_SNS_solaxX1_DATA3[] PROGMEM = + "{s}" D_SOLAX_X1 " " D_UPTIME "{m}%s " D_UNIT_HOUR "{e}" + "{s}" D_SOLAX_X1 " " D_STATUS "{m}%s" + "{s}" D_SOLAX_X1 " " D_ERROR "{m}%s"; +#endif + +void solaxX1Show(bool json) +{ + char solar_power[33]; + dtostrfd(solaxX1.dc1_power + solaxX1.dc2_power, Settings.flag2.wattage_resolution, solar_power); + char pv1_voltage[33]; + dtostrfd(solaxX1.dc1_voltage, Settings.flag2.voltage_resolution, pv1_voltage); + char pv1_current[33]; + dtostrfd(solaxX1.dc1_current, Settings.flag2.current_resolution, pv1_current); + char pv1_power[33]; + dtostrfd(solaxX1.dc1_power, Settings.flag2.wattage_resolution, pv1_power); +#ifdef SOLAXX1_PV2 + char pv2_voltage[33]; + dtostrfd(solaxX1.dc2_voltage, Settings.flag2.voltage_resolution, pv2_voltage); + char pv2_current[33]; + dtostrfd(solaxX1.dc2_current, Settings.flag2.current_resolution, pv2_current); + char pv2_power[33]; + dtostrfd(solaxX1.dc2_power, Settings.flag2.wattage_resolution, pv2_power); +#endif + char temperature[33]; + dtostrfd(solaxX1.temperature, Settings.flag2.temperature_resolution, temperature); + char runtime[33]; + dtostrfd(solaxX1.runtime_total, 0, runtime); + char status[33]; + GetTextIndexed(status, sizeof(status), solaxX1.status, kSolaxMode); + + if (json) + { + ResponseAppend_P(PSTR(",\"" D_JSON_SOLAR_POWER "\":%s,\"" D_JSON_PV1_VOLTAGE "\":%s,\"" D_JSON_PV1_CURRENT "\":%s,\"" D_JSON_PV1_POWER "\":%s"), + solar_power, pv1_voltage, pv1_current, pv1_power); +#ifdef SOLAXX1_PV2 + ResponseAppend_P(PSTR(",\"" D_JSON_PV2_VOLTAGE "\":%s,\"" D_JSON_PV2_CURRENT "\":%s,\"" D_JSON_PV2_POWER "\":%s"), + pv2_voltage, pv2_current, pv2_power); +#endif + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_RUNTIME "\":%s,\"" D_JSON_STATUS "\":\"%s\",\"" D_JSON_ERROR "\":%d"), + temperature, runtime, status, solaxX1.errorCode); + +#ifdef USE_WEBSERVER + } + else + { + WSContentSend_PD(HTTP_SNS_solaxX1_DATA1, solar_power, pv1_voltage, pv1_current, pv1_power); +#ifdef SOLAXX1_PV2 + WSContentSend_PD(HTTP_SNS_solaxX1_DATA2, pv2_voltage, pv2_current, pv2_power); +#endif + WSContentSend_PD(HTTP_SNS_TEMP, D_SOLAX_X1, temperature, TempUnit()); + char errorCodeString[33]; + WSContentSend_PD(HTTP_SNS_solaxX1_DATA3, runtime, status, + GetTextIndexed(errorCodeString, sizeof(errorCodeString), solaxX1_ParseErrorCode(solaxX1.errorCode), kSolaxError)); +#endif + } +} + + + + + +bool Xnrg12(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { solaxX1250MSecond(); } + break; + case FUNC_JSON_APPEND: + solaxX1Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + solaxX1Show(0); + break; +#endif + case FUNC_INIT: + solaxX1SnsInit(); + break; + case FUNC_PRE_INIT: + solaxX1DrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_LE01MR +# 71 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" +#define XNRG_13 13 + + +#ifndef LE01MR_SPEED + #define LE01MR_SPEED 2400 +#endif + +#ifndef LE01MR_ADDR + #define LE01MR_ADDR 1 +#endif + +#include +TasmotaModbus *FifLEModbus; + +const uint8_t le01mr_table_sz = 9; + +const uint16_t le01mr_register_addresses[] { + + 0x0130, + 0x0131, + 0x0158, + 0x0139, + 0x0140, + 0x0148, + 0x0150, + 0xA000, + 0xA01E +}; + +struct LE01MR { + float total_active = 0; + float total_reactive = 0; + uint8_t read_state = 0; + uint8_t send_retry = 0; + uint8_t start_address_count = le01mr_table_sz; +} Le01mr; + + + +void FifLEEvery250ms(void) +{ + bool data_ready = FifLEModbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[14]; + uint8_t reg_count = 2; + if (Le01mr.read_state < 3) { + reg_count=1; + } + + uint32_t error = FifLEModbus->ReceiveBuffer(buffer, reg_count); + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, FifLEModbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("FiF-LE: LE01MR Modbus error %d"), error); + } else { + Energy.data_valid[0] = 0; +# 146 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" + uint32_t value_buff = 0; + + if (Le01mr.read_state >= 0 && Le01mr.read_state < 3) { + value_buff = ((uint32_t)buffer[3])<<8 | buffer[4]; + } else { + value_buff = ((uint32_t)buffer[3])<<24 | ((uint32_t)buffer[4])<<16 | ((uint32_t)buffer[5])<<8 | buffer[6]; + } + + switch(Le01mr.read_state) { + case 0: + Energy.frequency[0] = value_buff * 0.01f; + break; + + case 1: + Energy.voltage[0] = value_buff * 0.01f; + break; + + case 2: + Energy.power_factor[0] = ((int16_t)value_buff) * 0.001f; + break; + + case 3: + Energy.current[0] = value_buff * 0.001f; + break; + + case 4: + Energy.active_power[0] = value_buff * 1.0f; + break; + + case 5: + Energy.reactive_power[0] = value_buff * 1.0f; + break; + + case 6: + Energy.apparent_power[0] = value_buff * 1.0f; + break; + + case 7: + Le01mr.total_active = value_buff * 0.01f; + break; + + case 8: + Le01mr.total_reactive = value_buff * 0.01f; + break; + } + + Le01mr.read_state++; + if (Le01mr.read_state == Le01mr.start_address_count) { + Le01mr.read_state = 0; + + EnergyUpdateTotal(Le01mr.total_active, true); + } + } + } + + if (0 == Le01mr.send_retry || data_ready) { + uint8_t reg_count = 2; + + Le01mr.send_retry = 5; + + if (Le01mr.read_state < 3) reg_count=1; + + FifLEModbus->Send(LE01MR_ADDR, 0x03, le01mr_register_addresses[Le01mr.read_state], reg_count); + } else { + Le01mr.send_retry--; + } +} + +void FifLESnsInit(void) +{ + FifLEModbus = new TasmotaModbus(pin[GPIO_LE01MR_RX], pin[GPIO_LE01MR_TX]); + uint8_t result = FifLEModbus->Begin(LE01MR_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void FifLEDrvInit(void) +{ + if ((pin[GPIO_LE01MR_RX] < 99) && (pin[GPIO_LE01MR_TX] < 99)) { + energy_flg = XNRG_13; + } +} + +void FifLEReset(void) +{ + Le01mr.total_active = 0; + Le01mr.total_reactive = 0; +} + +#ifdef USE_WEBSERVER +const char HTTP_ENERGY_LE01MR[] PROGMEM = + "{s}" D_TOTAL_ACTIVE "{m}%s " D_UNIT_KILOWATTHOUR "{e}" + "{s}" D_TOTAL_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" + ; +#endif + +void FifLEShow(bool json) +{ + char total_reactive_chr[FLOATSZ]; + dtostrfd(Le01mr.total_reactive, Settings.flag2.energy_resolution, total_reactive_chr); + char total_active_chr[FLOATSZ]; + dtostrfd(Le01mr.total_active, Settings.flag2.energy_resolution, total_active_chr); + + if (json) { + ResponseAppend_P(PSTR(",\"" D_JSON_TOTAL_ACTIVE "\":%s,\"" D_JSON_TOTAL_REACTIVE "\":%s"), + total_active_chr, total_reactive_chr); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_ENERGY_LE01MR, total_active_chr, total_reactive_chr); +#endif + } +} + + + + + +bool Xnrg13(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { + FifLEEvery250ms(); + } + break; + case FUNC_JSON_APPEND: + FifLEShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + FifLEShow(0); + break; +#endif + case FUNC_ENERGY_RESET: + FifLEReset(); + break; + case FUNC_INIT: + FifLESnsInit(); + break; + case FUNC_PRE_INIT: + FifLEDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_interface.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_interface.ino" +#ifdef USE_ENERGY_SENSOR + +#ifdef XFUNC_PTR_IN_ROM +bool (* const xnrg_func_ptr[])(uint8_t) PROGMEM = { +#else +bool (* const xnrg_func_ptr[])(uint8_t) = { +#endif + +#ifdef XNRG_01 + &Xnrg01, +#endif + +#ifdef XNRG_02 + &Xnrg02, +#endif + +#ifdef XNRG_03 + &Xnrg03, +#endif + +#ifdef XNRG_04 + &Xnrg04, +#endif + +#ifdef XNRG_05 + &Xnrg05, +#endif + +#ifdef XNRG_06 + &Xnrg06, +#endif + +#ifdef XNRG_07 + &Xnrg07, +#endif + +#ifdef XNRG_08 + &Xnrg08, +#endif + +#ifdef XNRG_09 + &Xnrg09, +#endif + +#ifdef XNRG_10 + &Xnrg10, +#endif + +#ifdef XNRG_11 + &Xnrg11, +#endif + +#ifdef XNRG_12 + &Xnrg12, +#endif + +#ifdef XNRG_13 + &Xnrg13, +#endif + +#ifdef XNRG_14 + &Xnrg14, +#endif + +#ifdef XNRG_15 + &Xnrg15, +#endif + +#ifdef XNRG_16 + &Xnrg16 +#endif +}; + +const uint8_t xnrg_present = sizeof(xnrg_func_ptr) / sizeof(xnrg_func_ptr[0]); + +uint8_t xnrg_active = 0; + +bool XnrgCall(uint8_t function) +{ + DEBUG_TRACE_LOG(PSTR("NRG: %d"), function); + + if (FUNC_PRE_INIT == function) { + for (uint32_t x = 0; x < xnrg_present; x++) { + xnrg_func_ptr[x](function); + if (energy_flg) { + xnrg_active = x; + return true; + } + } + } + else if (energy_flg) { + return xnrg_func_ptr[xnrg_active](function); + } + return false; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_01_counter.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_01_counter.ino" +#ifdef USE_COUNTER + + + + +#define XSNS_01 1 + +#define D_PRFX_COUNTER "Counter" +#define D_CMND_COUNTERTYPE "Type" +#define D_CMND_COUNTERDEBOUNCE "Debounce" +#define D_CMND_COUNTERDEBOUNCELOW "DebounceLow" +#define D_CMND_COUNTERDEBOUNCEHIGH "DebounceHigh" + +const char kCounterCommands[] PROGMEM = D_PRFX_COUNTER "|" + "|" D_CMND_COUNTERTYPE "|" D_CMND_COUNTERDEBOUNCE "|" D_CMND_COUNTERDEBOUNCELOW "|" D_CMND_COUNTERDEBOUNCEHIGH ; + +void (* const CounterCommand[])(void) PROGMEM = { + &CmndCounter, &CmndCounterType, &CmndCounterDebounce, &CmndCounterDebounceLow, &CmndCounterDebounceHigh }; + +struct COUNTER { + uint32_t timer[MAX_COUNTERS]; + uint32_t timer_low_high[MAX_COUNTERS]; + uint8_t no_pullup = 0; + uint8_t pin_state = 0; + bool any_counter = false; +} Counter; + +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 +void CounterUpdate(uint8_t index) ICACHE_RAM_ATTR; +void CounterUpdate1(void) ICACHE_RAM_ATTR; +void CounterUpdate2(void) ICACHE_RAM_ATTR; +void CounterUpdate3(void) ICACHE_RAM_ATTR; +void CounterUpdate4(void) ICACHE_RAM_ATTR; +#endif + +void CounterUpdate(uint8_t index) +{ + uint32_t time = micros(); + uint32_t debounce_time; + + if (Counter.pin_state) { + + if (digitalRead(pin[GPIO_CNTR1 +index]) == bitRead(Counter.pin_state, index)) { + + return; + } + debounce_time = time - Counter.timer_low_high[index]; + if bitRead(Counter.pin_state, index) { + + if (debounce_time <= Settings.pulse_counter_debounce_high * 1000) return; + } else { + + if (debounce_time <= Settings.pulse_counter_debounce_low * 1000) return; + } + + Counter.timer_low_high[index] = time; + Counter.pin_state ^= (1< Settings.pulse_counter_debounce * 1000) { + Counter.timer[index] = time; + if (bitRead(Settings.pulse_counter_type, index)) { + RtcSettings.pulse_counter[index] = debounce_time; + } else { + RtcSettings.pulse_counter[index]++; + } + } +} + +void CounterUpdate1(void) +{ + CounterUpdate(0); +} + +void CounterUpdate2(void) +{ + CounterUpdate(1); +} + +void CounterUpdate3(void) +{ + CounterUpdate(2); +} + +void CounterUpdate4(void) +{ + CounterUpdate(3); +} + + + +bool CounterPinState(void) +{ + if ((XdrvMailbox.index >= GPIO_CNTR1_NP) && (XdrvMailbox.index < (GPIO_CNTR1_NP + MAX_COUNTERS))) { + bitSet(Counter.no_pullup, XdrvMailbox.index - GPIO_CNTR1_NP); + XdrvMailbox.index -= (GPIO_CNTR1_NP - GPIO_CNTR1); + return true; + } + return false; +} + +void CounterInit(void) +{ + typedef void (*function) () ; + function counter_callbacks[] = { CounterUpdate1, CounterUpdate2, CounterUpdate3, CounterUpdate4 }; + + for (uint32_t i = 0; i < MAX_COUNTERS; i++) { + if (pin[GPIO_CNTR1 +i] < 99) { + Counter.any_counter = true; + pinMode(pin[GPIO_CNTR1 +i], bitRead(Counter.no_pullup, i) ? INPUT : INPUT_PULLUP); + if ((0 == Settings.pulse_counter_debounce_low) && (0 == Settings.pulse_counter_debounce_high)) { + Counter.pin_state = 0; + attachInterrupt(pin[GPIO_CNTR1 +i], counter_callbacks[i], FALLING); + } else { + Counter.pin_state = 0x8f; + attachInterrupt(pin[GPIO_CNTR1 +i], counter_callbacks[i], CHANGE); + } + } + } +} + +void CounterEverySecond(void) +{ + for (uint32_t i = 0; i < MAX_COUNTERS; i++) { + if (pin[GPIO_CNTR1 +i] < 99) { + if (bitRead(Settings.pulse_counter_type, i)) { + uint32_t time = micros() - Counter.timer[i]; + if (time > 4200000000) { + RtcSettings.pulse_counter[i] = 4200000000; + } + } + } + } +} + +void CounterSaveState(void) +{ + for (uint32_t i = 0; i < MAX_COUNTERS; i++) { + if (pin[GPIO_CNTR1 +i] < 99) { + Settings.pulse_counter[i] = RtcSettings.pulse_counter[i]; + } + } +} + +void CounterShow(bool json) +{ + bool header = false; + uint8_t dsxflg = 0; + for (uint32_t i = 0; i < MAX_COUNTERS; i++) { + if (pin[GPIO_CNTR1 +i] < 99) { + char counter[33]; + if (bitRead(Settings.pulse_counter_type, i)) { + dtostrfd((double)RtcSettings.pulse_counter[i] / 1000000, 6, counter); + } else { + dsxflg++; + snprintf_P(counter, sizeof(counter), PSTR("%lu"), RtcSettings.pulse_counter[i]); + } + + if (json) { + if (!header) { + ResponseAppend_P(PSTR(",\"COUNTER\":{")); + } + ResponseAppend_P(PSTR("%s\"C%d\":%s"), (header)?",":"", i +1, counter); + header = true; +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (1 == dsxflg)) { + DomoticzSensor(DZ_COUNT, RtcSettings.pulse_counter[i]); + dsxflg++; + } +#endif + if ((0 == tele_period ) && (Settings.flag3.counter_reset_on_tele)) { + RtcSettings.pulse_counter[i] = 0; + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(PSTR("{s}" D_COUNTER "%d{m}%s%s{e}"), + i +1, counter, (bitRead(Settings.pulse_counter_type, i)) ? " " D_UNIT_SECOND : ""); +#endif + } + } + } + if (header) { + ResponseJsonEnd(); + } +} + + + + + +void CmndCounter(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) { + if ((XdrvMailbox.data_len > 0) && (pin[GPIO_CNTR1 + XdrvMailbox.index -1] < 99)) { + if ((XdrvMailbox.data[0] == '-') || (XdrvMailbox.data[0] == '+')) { + RtcSettings.pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload; + Settings.pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload; + } else { + RtcSettings.pulse_counter[XdrvMailbox.index -1] = XdrvMailbox.payload; + Settings.pulse_counter[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + } + ResponseCmndIdxNumber(RtcSettings.pulse_counter[XdrvMailbox.index -1]); + } +} + +void CmndCounterType(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1) && (pin[GPIO_CNTR1 + XdrvMailbox.index -1] < 99)) { + bitWrite(Settings.pulse_counter_type, XdrvMailbox.index -1, XdrvMailbox.payload &1); + RtcSettings.pulse_counter[XdrvMailbox.index -1] = 0; + Settings.pulse_counter[XdrvMailbox.index -1] = 0; + } + ResponseCmndIdxNumber(bitRead(Settings.pulse_counter_type, XdrvMailbox.index -1)); + } +} + +void CmndCounterDebounce(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { + Settings.pulse_counter_debounce = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.pulse_counter_debounce); +} + +void CmndCounterDebounceLow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { + Settings.pulse_counter_debounce_low = XdrvMailbox.payload; + CounterInit(); + } + ResponseCmndNumber(Settings.pulse_counter_debounce_low); +} + +void CmndCounterDebounceHigh(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { + Settings.pulse_counter_debounce_high = XdrvMailbox.payload; + CounterInit(); + } + ResponseCmndNumber(Settings.pulse_counter_debounce_high); +} + + + + + +bool Xsns01(uint8_t function) +{ + bool result = false; + + if (Counter.any_counter) { + switch (function) { + case FUNC_EVERY_SECOND: + CounterEverySecond(); + break; + case FUNC_JSON_APPEND: + CounterShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + CounterShow(0); + break; +#endif + case FUNC_SAVE_BEFORE_RESTART: + case FUNC_SAVE_AT_MIDNIGHT: + CounterSaveState(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kCounterCommands, CounterCommand); + break; + } + } else { + switch (function) { + case FUNC_INIT: + CounterInit(); + break; + case FUNC_PIN_STATE: + result = CounterPinState(); + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_02_analog.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_02_analog.ino" +#ifndef USE_ADC_VCC + + + + +#define XSNS_02 2 + +#define TO_CELSIUS(x) ((x) - 273.15) +#define TO_KELVIN(x) ((x) + 273.15) + + +#define ANALOG_V33 3.3 +#define ANALOG_T0 TO_KELVIN(25.0) + + + + + +#define ANALOG_NTC_BRIDGE_RESISTANCE 32000 +#define ANALOG_NTC_RESISTANCE 10000 +#define ANALOG_NTC_B_COEFFICIENT 3350 + + + + + +#define ANALOG_LDR_BRIDGE_RESISTANCE 10000 +#define ANALOG_LDR_LUX_CALC_SCALAR 12518931 +#define ANALOG_LDR_LUX_CALC_EXPONENT -1.4050 +# 58 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_02_analog.ino" +#define ANALOG_CT_FLAGS 0 +#define ANALOG_CT_MULTIPLIER 2146 +#define ANALOG_CT_VOLTAGE 2300 + +#define CT_FLAG_ENERGY_RESET (1 << 0) + +struct { + float temperature = 0; + float current = 0; + float energy = 0; + uint32_t previous_millis = 0; + uint16_t last_value = 0; +} Adc; + +void AdcInit(void) +{ + if ((Settings.adc_param_type != my_adc0) || (Settings.adc_param1 > 1000000)) { + if (ADC0_TEMP == my_adc0) { + + Settings.adc_param_type = ADC0_TEMP; + Settings.adc_param1 = ANALOG_NTC_BRIDGE_RESISTANCE; + Settings.adc_param2 = ANALOG_NTC_RESISTANCE; + Settings.adc_param3 = ANALOG_NTC_B_COEFFICIENT * 10000; + } + else if (ADC0_LIGHT == my_adc0) { + Settings.adc_param_type = ADC0_LIGHT; + Settings.adc_param1 = ANALOG_LDR_BRIDGE_RESISTANCE; + Settings.adc_param2 = ANALOG_LDR_LUX_CALC_SCALAR; + Settings.adc_param3 = ANALOG_LDR_LUX_CALC_EXPONENT * 10000; + } + else if (ADC0_RANGE == my_adc0) { + Settings.adc_param_type = ADC0_RANGE; + Settings.adc_param1 = 0; + Settings.adc_param2 = 1023; + Settings.adc_param3 = 0; + Settings.adc_param4 = 100; + } + else if (ADC0_CT_POWER == my_adc0) { + Settings.adc_param_type = ADC0_CT_POWER; + Settings.adc_param1 = ANALOG_CT_FLAGS; + Settings.adc_param2 = ANALOG_CT_MULTIPLIER; + Settings.adc_param3 = ANALOG_CT_VOLTAGE; + } + } +} + +uint16_t AdcRead(uint8_t factor) +{ + + + + + + uint8_t samples = 1 << factor; + uint16_t analog = 0; + for (uint32_t i = 0; i < samples; i++) { + analog += analogRead(A0); + delay(1); + } + analog >>= factor; + return analog; +} + +#ifdef USE_RULES +void AdcEvery250ms(void) +{ + if (ADC0_INPUT == my_adc0) { + uint16_t new_value = AdcRead(5); + if ((new_value < Adc.last_value -10) || (new_value > Adc.last_value +10)) { + Adc.last_value = new_value; + uint16_t value = Adc.last_value / 10; + Response_P(PSTR("{\"ANALOG\":{\"A0div10\":%d}}"), (value > 99) ? 100 : value); + XdrvRulesProcess(); + } + } +} +#endif + +uint16_t AdcGetLux(void) +{ + int adc = AdcRead(2); + + double resistorVoltage = ((double)adc / 1023) * ANALOG_V33; + double ldrVoltage = ANALOG_V33 - resistorVoltage; + double ldrResistance = ldrVoltage / resistorVoltage * (double)Settings.adc_param1; + double ldrLux = (double)Settings.adc_param2 * FastPrecisePow(ldrResistance, (double)Settings.adc_param3 / 10000); + + return (uint16_t)ldrLux; +} + +uint16_t AdcGetRange(void) +{ + + + + int adc = AdcRead(2); + double adcrange = ( ((double)Settings.adc_param2 - (double)adc) / ( ((double)Settings.adc_param2 - (double)Settings.adc_param1)) * ((double)Settings.adc_param3 - (double)Settings.adc_param4) + (double)Settings.adc_param4 ); + return (uint16_t)adcrange; +} + +void AdcGetCurrentPower(uint8_t factor) +{ + + + + + + uint8_t samples = 1 << factor; + uint16_t analog = 0; + uint16_t analog_min = 1023; + uint16_t analog_max = 0; + for (uint32_t i = 0; i < samples; i++) { + analog = analogRead(A0); + if (analog < analog_min) { + analog_min = analog; + } + if (analog > analog_max) { + analog_max = analog; + } + delay(1); + } + + Adc.current = (float)(analog_max-analog_min) * ((float)(Settings.adc_param2) / 100000); + float power = Adc.current * (float)(Settings.adc_param3) / 10; + uint32_t current_millis = millis(); + Adc.energy = Adc.energy + ((power * (current_millis - Adc.previous_millis)) / 3600000000); + Adc.previous_millis = current_millis; +} + +void AdcEverySecond(void) +{ + if (ADC0_TEMP == my_adc0) { + int adc = AdcRead(2); + + double Rt = (adc * Settings.adc_param1) / (1024.0 * ANALOG_V33 - (double)adc); + double BC = (double)Settings.adc_param3 / 10000; + double T = BC / (BC / ANALOG_T0 + TaylorLog(Rt / (double)Settings.adc_param2)); + Adc.temperature = ConvertTemp(TO_CELSIUS(T)); + } + else if (ADC0_CT_POWER == my_adc0) { + AdcGetCurrentPower(5); + } +} + +void AdcShow(bool json) +{ + if (ADC0_INPUT == my_adc0) { + uint16_t analog = AdcRead(5); + + if (json) { + ResponseAppend_P(PSTR(",\"ANALOG\":{\"A0\":%d}"), analog); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_ANALOG, "", 0, analog); +#endif + } + } + + else if (ADC0_TEMP == my_adc0) { + char temperature[33]; + dtostrfd(Adc.temperature, Settings.flag2.temperature_resolution, temperature); + + if (json) { + ResponseAppend_P(JSON_SNS_TEMP, "ANALOG", temperature); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_TEMP, temperature); + } +#endif +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, Adc.temperature); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, "", temperature, TempUnit()); +#endif + } + } + + else if (ADC0_LIGHT == my_adc0) { + uint16_t adc_light = AdcGetLux(); + + if (json) { + ResponseAppend_P(JSON_SNS_ILLUMINANCE, "ANALOG", adc_light); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_ILLUMINANCE, adc_light); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, "", adc_light); +#endif + } + } + + else if (ADC0_RANGE == my_adc0) { + uint16_t adc_range = AdcGetRange(); + + if (json) { + ResponseAppend_P(JSON_SNS_RANGE, "ANALOG", adc_range); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_RANGE, "", adc_range); +#endif + } + } + + else if (ADC0_CT_POWER == my_adc0) { + AdcGetCurrentPower(5); + + float voltage = (float)(Settings.adc_param3) / 10; + char voltage_chr[FLOATSZ]; + dtostrfd(voltage, Settings.flag2.voltage_resolution, voltage_chr); + char current_chr[FLOATSZ]; + dtostrfd(Adc.current, Settings.flag2.current_resolution, current_chr); + char power_chr[FLOATSZ]; + dtostrfd(voltage * Adc.current, Settings.flag2.wattage_resolution, power_chr); + char energy_chr[FLOATSZ]; + dtostrfd(Adc.energy, Settings.flag2.energy_resolution, energy_chr); + + if (json) { + ResponseAppend_P(PSTR(",\"ANALOG\":{\"" D_JSON_ENERGY "\":%s,\"" D_JSON_POWERUSAGE "\":%s,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s}"), + energy_chr, power_chr, voltage_chr, current_chr); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_POWER_ENERGY, power_chr); + DomoticzSensor(DZ_VOLTAGE, voltage_chr); + DomoticzSensor(DZ_CURRENT, current_chr); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_VOLTAGE, voltage_chr); + WSContentSend_PD(HTTP_SNS_CURRENT, current_chr); + WSContentSend_PD(HTTP_SNS_POWER, power_chr); + WSContentSend_PD(HTTP_SNS_ENERGY_TOTAL, energy_chr); +#endif + } + } + +} + + + + + +const char kAdcCommands[] PROGMEM = "|" + D_CMND_ADC "|" D_CMND_ADCS "|" D_CMND_ADCPARAM; + +void (* const AdcCommand[])(void) PROGMEM = { + &CmndAdc, &CmndAdcs, &CmndAdcParam }; + +void CmndAdc(void) +{ + if (ValidAdc() && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < ADC0_END)) { + Settings.my_adc0 = XdrvMailbox.payload; + restart_flag = 2; + } + char stemp1[TOPSZ]; + Response_P(PSTR("{\"" D_CMND_ADC "0\":{\"%d\":\"%s\"}}"), Settings.my_adc0, GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_adc0, kAdc0Names)); +} + +void CmndAdcs(void) +{ + Response_P(PSTR("{\"" D_CMND_ADCS "\":{")); + bool jsflg = false; + char stemp1[TOPSZ]; + for (uint32_t i = 0; i < ADC0_END; i++) { + if (jsflg) { + ResponseAppend_P(PSTR(",")); + } + jsflg = true; + ResponseAppend_P(PSTR("\"%d\":\"%s\""), i, GetTextIndexed(stemp1, sizeof(stemp1), i, kAdc0Names)); + } + ResponseJsonEndEnd(); +} + +void CmndAdcParam(void) +{ + if (XdrvMailbox.data_len) { + if ((ADC0_TEMP == XdrvMailbox.payload) || + (ADC0_LIGHT == XdrvMailbox.payload) || + (ADC0_RANGE == XdrvMailbox.payload) || + (ADC0_CT_POWER == XdrvMailbox.payload)) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { + char sub_string[XdrvMailbox.data_len +1]; + + + + Settings.adc_param_type = XdrvMailbox.payload; + Settings.adc_param1 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + Settings.adc_param2 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 3), nullptr, 10); + if (ADC0_RANGE == XdrvMailbox.payload) { + Settings.adc_param3 = abs(strtol(subStr(sub_string, XdrvMailbox.data, ",", 4), nullptr, 10)); + Settings.adc_param4 = abs(strtol(subStr(sub_string, XdrvMailbox.data, ",", 5), nullptr, 10)); + } else { + Settings.adc_param3 = (int)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)) * 10000); + } + if (ADC0_CT_POWER == XdrvMailbox.payload) { + if ((Settings.adc_param1 & CT_FLAG_ENERGY_RESET) > 0) { + Adc.energy = 0; + Settings.adc_param1 ^= CT_FLAG_ENERGY_RESET; + } + } + } else { + + + + + Settings.adc_param_type = 0; + AdcInit(); + } + } + } + + + Response_P(PSTR("{\"" D_CMND_ADCPARAM "\":[%d,%d,%d"), Settings.adc_param_type, Settings.adc_param1, Settings.adc_param2); + if (ADC0_RANGE == my_adc0) { + ResponseAppend_P(PSTR(",%d,%d"), Settings.adc_param3, Settings.adc_param4); + } else { + int value = Settings.adc_param3; + uint8_t precision; + for (precision = 4; precision > 0; precision--) { + if (value % 10) { break; } + value /= 10; + } + char param3[33]; + dtostrfd(((double)Settings.adc_param3)/10000, precision, param3); + ResponseAppend_P(PSTR(",%s"), param3); + } + ResponseAppend_P(PSTR("]}")); +} + + + + + +bool Xsns02(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_COMMAND: + result = DecodeCommand(kAdcCommands, AdcCommand); + break; + default: + if ((ADC0_INPUT == my_adc0) || + (ADC0_TEMP == my_adc0) || + (ADC0_LIGHT == my_adc0) || + (ADC0_RANGE == my_adc0) || + (ADC0_CT_POWER == my_adc0)) { + switch (function) { +#ifdef USE_RULES + case FUNC_EVERY_250_MSECOND: + AdcEvery250ms(); + break; +#endif + case FUNC_EVERY_SECOND: + AdcEverySecond(); + break; + case FUNC_INIT: + AdcInit(); + break; + case FUNC_JSON_APPEND: + AdcShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + AdcShow(0); + break; +#endif + } + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_04_snfsc.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_04_snfsc.ino" +#ifdef USE_SONOFF_SC +# 57 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_04_snfsc.ino" +#define XSNS_04 4 + +uint16_t sc_value[5] = { 0 }; + +void SonoffScSend(const char *data) +{ + Serial.write(data); + Serial.write('\x1B'); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SERIAL D_TRANSMIT " %s"), data); +} + +void SonoffScInit(void) +{ + + SonoffScSend("AT+START"); + +} + +void SonoffScSerialInput(char *rcvstat) +{ + char *p; + char *str; + uint16_t value[5] = { 0 }; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SERIAL D_RECEIVED " %s"), rcvstat); + + if (!strncasecmp_P(rcvstat, PSTR("AT+UPDATE="), 10)) { + int8_t i = -1; + for (str = strtok_r(rcvstat, ":", &p); str && i < 5; str = strtok_r(nullptr, ":", &p)) { + value[i++] = atoi(str); + } + if (value[0] > 0) { + for (uint32_t i = 0; i < 5; i++) { + sc_value[i] = value[i]; + } + sc_value[2] = (11 - sc_value[2]) * 10; + sc_value[3] *= 10; + sc_value[4] = (11 - sc_value[4]) * 10; + SonoffScSend("AT+SEND=ok"); + } else { + SonoffScSend("AT+SEND=fail"); + } + } + else if (!strcasecmp_P(rcvstat, PSTR("AT+STATUS?"))) { + SonoffScSend("AT+STATUS=4"); + } +} + + + +#ifdef USE_WEBSERVER +const char HTTP_SNS_SCPLUS[] PROGMEM = + "{s}" D_LIGHT "{m}%d%%{e}{s}" D_NOISE "{m}%d%%{e}{s}" D_AIR_QUALITY "{m}%d%%{e}"; +#endif + +void SonoffScShow(bool json) +{ + if (sc_value[0] > 0) { + float t = ConvertTemp(sc_value[1]); + float h = ConvertHumidity(sc_value[0]); + + if (json) { + ResponseAppend_P(PSTR(",\"SonoffSC\":{")); + ResponseAppendTHD(t, h); + ResponseAppend_P(PSTR(",\"" D_JSON_LIGHT "\":%d,\"" D_JSON_NOISE "\":%d,\"" D_JSON_AIRQUALITY "\":%d}"), sc_value[2], sc_value[3], sc_value[4]); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzTempHumPressureSensor(t, h); + DomoticzSensor(DZ_ILLUMINANCE, sc_value[2]); + DomoticzSensor(DZ_COUNT, sc_value[3]); + DomoticzSensor(DZ_AIRQUALITY, 500 + ((100 - sc_value[4]) * 20)); + } +#endif + +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, t); + KnxSensor(KNX_HUMIDITY, h); + } +#endif + +#ifdef USE_WEBSERVER + } else { + WSContentSend_THD("", t, h); + WSContentSend_PD(HTTP_SNS_SCPLUS, sc_value[2], sc_value[3], sc_value[4]); +#endif + } + } +} + + + + + +bool Xsns04(uint8_t function) +{ + bool result = false; + + if (SONOFF_SC == my_module_type) { + switch (function) { + case FUNC_JSON_APPEND: + SonoffScShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + SonoffScShow(0); + break; +#endif + case FUNC_INIT: + SonoffScInit(); + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_05_ds18x20.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_05_ds18x20.ino" +#ifdef USE_DS18x20 + + + + +#define XSNS_05 5 + + + +#define DS18S20_CHIPID 0x10 +#define DS1822_CHIPID 0x22 +#define DS18B20_CHIPID 0x28 +#define MAX31850_CHIPID 0x3B + +#define W1_SKIP_ROM 0xCC +#define W1_CONVERT_TEMP 0x44 +#define W1_WRITE_EEPROM 0x48 +#define W1_WRITE_SCRATCHPAD 0x4E +#define W1_READ_SCRATCHPAD 0xBE + +#define DS18X20_MAX_SENSORS 8 + +const char kDs18x20Types[] PROGMEM = "DS18x20|DS18S20|DS1822|DS18B20|MAX31850"; + +uint8_t ds18x20_chipids[] = { 0, DS18S20_CHIPID, DS1822_CHIPID, DS18B20_CHIPID, MAX31850_CHIPID }; + +struct DS18X20STRUCT { + uint8_t address[8]; + uint8_t index; + uint8_t valid; + float temperature; +} ds18x20_sensor[DS18X20_MAX_SENSORS]; +uint8_t ds18x20_sensors = 0; +uint8_t ds18x20_pin = 0; +uint8_t ds18x20_pin_out = 0; +bool ds18x20_dual_mode = false; +char ds18x20_types[12]; +#ifdef W1_PARASITE_POWER +uint8_t ds18x20_sensor_curr = 0; +unsigned long w1_power_until = 0; +#endif + + + + + +#define W1_MATCH_ROM 0x55 +#define W1_SEARCH_ROM 0xF0 + +uint8_t onewire_last_discrepancy = 0; +uint8_t onewire_last_family_discrepancy = 0; +bool onewire_last_device_flag = false; +unsigned char onewire_rom_id[8] = { 0 }; + + + +uint8_t OneWireReset(void) +{ + uint8_t retries = 125; + + if (!ds18x20_dual_mode) { + pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); + do { + if (--retries == 0) { + return 0; + } + delayMicroseconds(2); + } while (!digitalRead(ds18x20_pin)); + pinMode(ds18x20_pin, OUTPUT); + digitalWrite(ds18x20_pin, LOW); + delayMicroseconds(480); + pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); + delayMicroseconds(70); + uint8_t r = !digitalRead(ds18x20_pin); + delayMicroseconds(410); + return r; + } else { + digitalWrite(ds18x20_pin_out, HIGH); + do { + if (--retries == 0) { + return 0; + } + delayMicroseconds(2); + } while (!digitalRead(ds18x20_pin)); + digitalWrite(ds18x20_pin_out, LOW); + delayMicroseconds(480); + digitalWrite(ds18x20_pin_out, HIGH); + delayMicroseconds(70); + uint8_t r = !digitalRead(ds18x20_pin); + delayMicroseconds(410); + return r; + } +} + +void OneWireWriteBit(uint8_t v) +{ + static const uint8_t delay_low[2] = { 65, 10 }; + static const uint8_t delay_high[2] = { 5, 55 }; + + v &= 1; + if (!ds18x20_dual_mode) { + digitalWrite(ds18x20_pin, LOW); + pinMode(ds18x20_pin, OUTPUT); + delayMicroseconds(delay_low[v]); + digitalWrite(ds18x20_pin, HIGH); + } else { + digitalWrite(ds18x20_pin_out, LOW); + delayMicroseconds(delay_low[v]); + digitalWrite(ds18x20_pin_out, HIGH); + } + delayMicroseconds(delay_high[v]); +} + +uint8_t OneWire1ReadBit(void) +{ + pinMode(ds18x20_pin, OUTPUT); + digitalWrite(ds18x20_pin, LOW); + delayMicroseconds(3); + pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); + delayMicroseconds(10); + uint8_t r = digitalRead(ds18x20_pin); + delayMicroseconds(53); + return r; +} + +uint8_t OneWire2ReadBit(void) +{ + digitalWrite(ds18x20_pin_out, LOW); + delayMicroseconds(3); + digitalWrite(ds18x20_pin_out, HIGH); + delayMicroseconds(10); + uint8_t r = digitalRead(ds18x20_pin); + delayMicroseconds(53); + return r; +} + + + +void OneWireWrite(uint8_t v) +{ + for (uint8_t bit_mask = 0x01; bit_mask; bit_mask <<= 1) { + OneWireWriteBit((bit_mask & v) ? 1 : 0); + } +} + +uint8_t OneWireRead(void) +{ + uint8_t r = 0; + + if (!ds18x20_dual_mode) { + for (uint8_t bit_mask = 0x01; bit_mask; bit_mask <<= 1) { + if (OneWire1ReadBit()) { + r |= bit_mask; + } + } + } else { + for (uint8_t bit_mask = 0x01; bit_mask; bit_mask <<= 1) { + if (OneWire2ReadBit()) { + r |= bit_mask; + } + } + } + return r; +} + +void OneWireSelect(const uint8_t rom[8]) +{ + OneWireWrite(W1_MATCH_ROM); + for (uint32_t i = 0; i < 8; i++) { + OneWireWrite(rom[i]); + } +} + +void OneWireResetSearch(void) +{ + onewire_last_discrepancy = 0; + onewire_last_device_flag = false; + onewire_last_family_discrepancy = 0; + for (uint32_t i = 0; i < 8; i++) { + onewire_rom_id[i] = 0; + } +} + +uint8_t OneWireSearch(uint8_t *newAddr) +{ + uint8_t id_bit_number = 1; + uint8_t last_zero = 0; + uint8_t rom_byte_number = 0; + uint8_t search_result = 0; + uint8_t id_bit; + uint8_t cmp_id_bit; + unsigned char rom_byte_mask = 1; + unsigned char search_direction; + + if (!onewire_last_device_flag) { + if (!OneWireReset()) { + onewire_last_discrepancy = 0; + onewire_last_device_flag = false; + onewire_last_family_discrepancy = 0; + return false; + } + OneWireWrite(W1_SEARCH_ROM); + do { + if (!ds18x20_dual_mode) { + id_bit = OneWire1ReadBit(); + cmp_id_bit = OneWire1ReadBit(); + } else { + id_bit = OneWire2ReadBit(); + cmp_id_bit = OneWire2ReadBit(); + } + if ((id_bit == 1) && (cmp_id_bit == 1)) { + break; + } else { + if (id_bit != cmp_id_bit) { + search_direction = id_bit; + } else { + if (id_bit_number < onewire_last_discrepancy) { + search_direction = ((onewire_rom_id[rom_byte_number] & rom_byte_mask) > 0); + } else { + search_direction = (id_bit_number == onewire_last_discrepancy); + } + if (search_direction == 0) { + last_zero = id_bit_number; + if (last_zero < 9) { + onewire_last_family_discrepancy = last_zero; + } + } + } + if (search_direction == 1) { + onewire_rom_id[rom_byte_number] |= rom_byte_mask; + } else { + onewire_rom_id[rom_byte_number] &= ~rom_byte_mask; + } + OneWireWriteBit(search_direction); + id_bit_number++; + rom_byte_mask <<= 1; + if (rom_byte_mask == 0) { + rom_byte_number++; + rom_byte_mask = 1; + } + } + } while (rom_byte_number < 8); + if (!(id_bit_number < 65)) { + onewire_last_discrepancy = last_zero; + if (onewire_last_discrepancy == 0) { + onewire_last_device_flag = true; + } + search_result = true; + } + } + if (!search_result || !onewire_rom_id[0]) { + onewire_last_discrepancy = 0; + onewire_last_device_flag = false; + onewire_last_family_discrepancy = 0; + search_result = false; + } + for (uint32_t i = 0; i < 8; i++) { + newAddr[i] = onewire_rom_id[i]; + } + return search_result; +} + +bool OneWireCrc8(uint8_t *addr) +{ + uint8_t crc = 0; + uint8_t len = 8; + + while (len--) { + uint8_t inbyte = *addr++; + for (uint32_t i = 8; i; i--) { + uint8_t mix = (crc ^ inbyte) & 0x01; + crc >>= 1; + if (mix) { + crc ^= 0x8C; + } + inbyte >>= 1; + } + } + return (crc == *addr); +} + + + +void Ds18x20Init(void) +{ + uint64_t ids[DS18X20_MAX_SENSORS]; + + ds18x20_pin = pin[GPIO_DSB]; + + if (pin[GPIO_DSB_OUT] < 99) { + ds18x20_pin_out = pin[GPIO_DSB_OUT]; + ds18x20_dual_mode = true; + pinMode(ds18x20_pin_out, OUTPUT); + pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); + } + + OneWireResetSearch(); + + ds18x20_sensors = 0; + while (ds18x20_sensors < DS18X20_MAX_SENSORS) { + if (!OneWireSearch(ds18x20_sensor[ds18x20_sensors].address)) { + break; + } + if (OneWireCrc8(ds18x20_sensor[ds18x20_sensors].address) && + ((ds18x20_sensor[ds18x20_sensors].address[0] == DS18S20_CHIPID) || + (ds18x20_sensor[ds18x20_sensors].address[0] == DS1822_CHIPID) || + (ds18x20_sensor[ds18x20_sensors].address[0] == DS18B20_CHIPID) || + (ds18x20_sensor[ds18x20_sensors].address[0] == MAX31850_CHIPID))) { + ds18x20_sensor[ds18x20_sensors].index = ds18x20_sensors; + ids[ds18x20_sensors] = ds18x20_sensor[ds18x20_sensors].address[0]; + for (uint32_t j = 6; j > 0; j--) { + ids[ds18x20_sensors] = ids[ds18x20_sensors] << 8 | ds18x20_sensor[ds18x20_sensors].address[j]; + } + ds18x20_sensors++; + } + } + for (uint32_t i = 0; i < ds18x20_sensors; i++) { + for (uint32_t j = i + 1; j < ds18x20_sensors; j++) { + if (ids[ds18x20_sensor[i].index] > ids[ds18x20_sensor[j].index]) { + std::swap(ds18x20_sensor[i].index, ds18x20_sensor[j].index); + } + } + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSORS_FOUND " %d"), ds18x20_sensors); +} + +void Ds18x20Convert(void) +{ + OneWireReset(); +#ifdef W1_PARASITE_POWER + + if (++ds18x20_sensor_curr >= ds18x20_sensors) + ds18x20_sensor_curr = 0; + OneWireSelect(ds18x20_sensor[ds18x20_sensor_curr].address); +#else + OneWireWrite(W1_SKIP_ROM); +#endif + OneWireWrite(W1_CONVERT_TEMP); + +} + +bool Ds18x20Read(uint8_t sensor) +{ + uint8_t data[9]; + int8_t sign = 1; + + uint8_t index = ds18x20_sensor[sensor].index; + if (ds18x20_sensor[index].valid) { ds18x20_sensor[index].valid--; } + for (uint32_t retry = 0; retry < 3; retry++) { + OneWireReset(); + OneWireSelect(ds18x20_sensor[index].address); + OneWireWrite(W1_READ_SCRATCHPAD); + for (uint32_t i = 0; i < 9; i++) { + data[i] = OneWireRead(); + } + if (OneWireCrc8(data)) { + switch(ds18x20_sensor[index].address[0]) { + case DS18S20_CHIPID: { + if (data[1] > 0x80) { + data[0] = (~data[0]) +1; + sign = -1; + } + float temp9 = (float)(data[0] >> 1) * sign; + ds18x20_sensor[index].temperature = ConvertTemp((temp9 - 0.25) + ((16.0 - data[6]) / 16.0)); + ds18x20_sensor[index].valid = SENSOR_MAX_MISS; + return true; + } + case DS1822_CHIPID: + case DS18B20_CHIPID: { + if (data[4] != 0x7F) { + data[4] = 0x7F; + OneWireReset(); + OneWireSelect(ds18x20_sensor[index].address); + OneWireWrite(W1_WRITE_SCRATCHPAD); + OneWireWrite(data[2]); + OneWireWrite(data[3]); + OneWireWrite(data[4]); + OneWireSelect(ds18x20_sensor[index].address); + OneWireWrite(W1_WRITE_EEPROM); +#ifdef W1_PARASITE_POWER + w1_power_until = millis() + 10; +#endif + } + uint16_t temp12 = (data[1] << 8) + data[0]; + if (temp12 > 2047) { + temp12 = (~temp12) +1; + sign = -1; + } + ds18x20_sensor[index].temperature = ConvertTemp(sign * temp12 * 0.0625); + ds18x20_sensor[index].valid = SENSOR_MAX_MISS; + return true; + } + case MAX31850_CHIPID: { + int16_t temp14 = (data[1] << 8) + (data[0] & 0xFC); + ds18x20_sensor[index].temperature = ConvertTemp(temp14 * 0.0625); + ds18x20_sensor[index].valid = SENSOR_MAX_MISS; + return true; + } + } + } + } + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSOR_CRC_ERROR)); + return false; +} + +void Ds18x20Name(uint8_t sensor) +{ + uint8_t index = sizeof(ds18x20_chipids); + while (index) { + if (ds18x20_sensor[ds18x20_sensor[sensor].index].address[0] == ds18x20_chipids[index]) { + break; + } + index--; + } + GetTextIndexed(ds18x20_types, sizeof(ds18x20_types), index, kDs18x20Types); + if (ds18x20_sensors > 1) { + snprintf_P(ds18x20_types, sizeof(ds18x20_types), PSTR("%s%c%d"), ds18x20_types, IndexSeparator(), sensor +1); + } +} + + + +void Ds18x20EverySecond(void) +{ + if (!ds18x20_sensors) { return; } + +#ifdef W1_PARASITE_POWER + + unsigned long now = millis(); + if (now < w1_power_until) + return; +#endif + if (uptime & 1 +#ifdef W1_PARASITE_POWER + + || ds18x20_sensors >= 2 +#endif + ) { + + Ds18x20Convert(); + } else { + for (uint32_t i = 0; i < ds18x20_sensors; i++) { + + if (!Ds18x20Read(i)) { + Ds18x20Name(i); + AddLogMissed(ds18x20_types, ds18x20_sensor[ds18x20_sensor[i].index].valid); +#ifdef USE_DS18x20_RECONFIGURE + if (!ds18x20_sensor[ds18x20_sensor[i].index].valid) { + memset(&ds18x20_sensor, 0, sizeof(ds18x20_sensor)); + Ds18x20Init(); + } +#endif + } + } + } +} + +void Ds18x20Show(bool json) +{ + for (uint32_t i = 0; i < ds18x20_sensors; i++) { + uint8_t index = ds18x20_sensor[i].index; + + if (ds18x20_sensor[index].valid) { + char temperature[33]; + dtostrfd(ds18x20_sensor[index].temperature, Settings.flag2.temperature_resolution, temperature); + + Ds18x20Name(i); + + if (json) { + char address[17]; + for (uint32_t j = 0; j < 6; j++) { + sprintf(address+2*j, "%02X", ds18x20_sensor[index].address[6-j]); + } + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_ID "\":\"%s\",\"" D_JSON_TEMPERATURE "\":%s}"), ds18x20_types, address, temperature); +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (0 == i)) { + DomoticzSensor(DZ_TEMP, temperature); + } +#endif +#ifdef USE_KNX + if ((0 == tele_period) && (0 == i)) { + KnxSensor(KNX_TEMPERATURE, ds18x20_sensor[index].temperature); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, ds18x20_types, temperature, TempUnit()); +#endif + } + } + } +} + + + + + +bool Xsns05(uint8_t function) +{ + bool result = false; + + if (pin[GPIO_DSB] < 99) { + switch (function) { + case FUNC_INIT: + Ds18x20Init(); + break; + case FUNC_EVERY_SECOND: + Ds18x20EverySecond(); + break; + case FUNC_JSON_APPEND: + Ds18x20Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Ds18x20Show(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_06_dht.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_06_dht.ino" +#ifdef USE_DHT +# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_06_dht.ino" +#define XSNS_06 6 + +#define DHT_MAX_SENSORS 4 +#define DHT_MAX_RETRY 8 + +uint8_t dht_data[5]; +uint8_t dht_sensors = 0; +uint8_t dht_pin_out = 0; +bool dht_active = true; +bool dht_dual_mode = false; + +struct DHTSTRUCT { + uint8_t pin; + uint8_t type; + uint8_t lastresult; + char stype[12]; + float t = NAN; + float h = NAN; +} Dht[DHT_MAX_SENSORS]; + +bool DhtWaitState(uint32_t sensor, uint32_t level) +{ + unsigned long timeout = micros() + 100; + while (digitalRead(Dht[sensor].pin) != level) { + if (TimeReachedUsec(timeout)) { + PrepLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " %s " D_PULSE), + (level) ? D_START_SIGNAL_HIGH : D_START_SIGNAL_LOW); + return false; + } + delayMicroseconds(1); + } + return true; +} + +bool DhtRead(uint32_t sensor) +{ + dht_data[0] = dht_data[1] = dht_data[2] = dht_data[3] = dht_data[4] = 0; + + if (!dht_dual_mode) { + pinMode(Dht[sensor].pin, OUTPUT); + digitalWrite(Dht[sensor].pin, LOW); + } else { + digitalWrite(dht_pin_out, LOW); + } + + switch (Dht[sensor].type) { + case GPIO_DHT11: + delay(19); + break; + case GPIO_DHT22: + delay(2); + break; + case GPIO_SI7021: + delayMicroseconds(500); + break; + } + + if (!dht_dual_mode) { + pinMode(Dht[sensor].pin, INPUT_PULLUP); + } else { + digitalWrite(dht_pin_out, HIGH); + } + + switch (Dht[sensor].type) { + case GPIO_DHT11: + case GPIO_DHT22: + delayMicroseconds(50); + break; + case GPIO_SI7021: + delayMicroseconds(20); + break; + } +# 133 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_06_dht.ino" + uint32_t i = 0; + noInterrupts(); + if (DhtWaitState(sensor, 0) && DhtWaitState(sensor, 1) && DhtWaitState(sensor, 0)) { + for (i = 0; i < 40; i++) { + if (!DhtWaitState(sensor, 1)) { break; } + delayMicroseconds(35); + if (digitalRead(Dht[sensor].pin)) { + dht_data[i / 8] |= (1 << (7 - i % 8)); + } + if (!DhtWaitState(sensor, 0)) { break; } + } + } + interrupts(); + if (i < 40) { return false; } + + uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; + if (dht_data[4] != checksum) { + char hex_char[15]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"), + ToHex_P(dht_data, 5, hex_char, sizeof(hex_char), ' '), checksum); + return false; + } + + float temperature = NAN; + float humidity = NAN; + switch (Dht[sensor].type) { + case GPIO_DHT11: + humidity = dht_data[0]; + temperature = dht_data[2] + ((float)dht_data[3] * 0.1f); + break; + case GPIO_DHT22: + case GPIO_SI7021: + humidity = ((dht_data[0] << 8) | dht_data[1]) * 0.1; + temperature = (((dht_data[2] & 0x7F) << 8 ) | dht_data[3]) * 0.1; + if (dht_data[2] & 0x80) { + temperature *= -1; + } + break; + } + if (isnan(temperature) || isnan(humidity)) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "Invalid NAN reading")); + return false; + } + + if (humidity > 100) { humidity = 100.0; } + if (humidity < 0) { humidity = 0.1; } + Dht[sensor].h = ConvertHumidity(humidity); + Dht[sensor].t = ConvertTemp(temperature); + Dht[sensor].lastresult = 0; + + return true; +} + + + +bool DhtPinState() +{ + if ((XdrvMailbox.index >= GPIO_DHT11) && (XdrvMailbox.index <= GPIO_SI7021)) { + if (dht_sensors < DHT_MAX_SENSORS) { + Dht[dht_sensors].pin = XdrvMailbox.payload; + Dht[dht_sensors].type = XdrvMailbox.index; + dht_sensors++; + XdrvMailbox.index = GPIO_DHT11; + } else { + XdrvMailbox.index = 0; + } + return true; + } + return false; +} + +void DhtInit(void) +{ + if (dht_sensors) { + if (pin[GPIO_DHT11_OUT] < 99) { + dht_pin_out = pin[GPIO_DHT11_OUT]; + dht_dual_mode = true; + dht_sensors = 1; + pinMode(dht_pin_out, OUTPUT); + } + + for (uint32_t i = 0; i < dht_sensors; i++) { + pinMode(Dht[i].pin, INPUT_PULLUP); + Dht[i].lastresult = DHT_MAX_RETRY; + GetTextIndexed(Dht[i].stype, sizeof(Dht[i].stype), Dht[i].type, kSensorNames); + if (dht_sensors > 1) { + snprintf_P(Dht[i].stype, sizeof(Dht[i].stype), PSTR("%s%c%02d"), Dht[i].stype, IndexSeparator(), Dht[i].pin); + } + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "(v5) " D_SENSORS_FOUND " %d"), dht_sensors); + } else { + dht_active = false; + } +} + +void DhtEverySecond(void) +{ + if (uptime &1) { + for (uint32_t sensor = 0; sensor < dht_sensors; sensor++) { + + if (!DhtRead(sensor)) { + Dht[sensor].lastresult++; + if (Dht[sensor].lastresult > DHT_MAX_RETRY) { + Dht[sensor].t = NAN; + Dht[sensor].h = NAN; + } + } + } + } +} + +void DhtShow(bool json) +{ + for (uint32_t i = 0; i < dht_sensors; i++) { + TempHumDewShow(json, ((0 == tele_period) && (0 == i)), Dht[i].stype, Dht[i].t, Dht[i].h); + } +} + + + + + +bool Xsns06(uint8_t function) +{ + bool result = false; + + if (dht_active) { + switch (function) { + case FUNC_EVERY_SECOND: + DhtEverySecond(); + break; + case FUNC_JSON_APPEND: + DhtShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + DhtShow(0); + break; +#endif + case FUNC_INIT: + DhtInit(); + break; + case FUNC_PIN_STATE: + result = DhtPinState(); + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_07_sht1x.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_07_sht1x.ino" +#ifdef USE_I2C +#ifdef USE_SHT +# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_07_sht1x.ino" +#define XSNS_07 7 +#define XI2C_08 8 + +enum { + SHT1X_CMD_MEASURE_TEMP = B00000011, + SHT1X_CMD_MEASURE_RH = B00000101, + SHT1X_CMD_SOFT_RESET = B00011110 +}; + +uint8_t sht_sda_pin; +uint8_t sht_scl_pin; +uint8_t sht_type = 0; +char sht_types[] = "SHT1X"; +uint8_t sht_valid = 0; +float sht_temperature = 0; +float sht_humidity = 0; + +bool ShtReset(void) +{ + pinMode(sht_sda_pin, INPUT_PULLUP); + pinMode(sht_scl_pin, OUTPUT); + delay(11); + for (uint32_t i = 0; i < 9; i++) { + digitalWrite(sht_scl_pin, HIGH); + digitalWrite(sht_scl_pin, LOW); + } + bool success = ShtSendCommand(SHT1X_CMD_SOFT_RESET); + delay(11); + return success; +} + +bool ShtSendCommand(const uint8_t cmd) +{ + pinMode(sht_sda_pin, OUTPUT); + + digitalWrite(sht_sda_pin, HIGH); + digitalWrite(sht_scl_pin, HIGH); + digitalWrite(sht_sda_pin, LOW); + digitalWrite(sht_scl_pin, LOW); + digitalWrite(sht_scl_pin, HIGH); + digitalWrite(sht_sda_pin, HIGH); + digitalWrite(sht_scl_pin, LOW); + + shiftOut(sht_sda_pin, sht_scl_pin, MSBFIRST, cmd); + + bool ackerror = false; + digitalWrite(sht_scl_pin, HIGH); + pinMode(sht_sda_pin, INPUT_PULLUP); + if (digitalRead(sht_sda_pin) != LOW) { + ackerror = true; + } + digitalWrite(sht_scl_pin, LOW); + delayMicroseconds(1); + if (digitalRead(sht_sda_pin) != HIGH) { + ackerror = true; + } + if (ackerror) { + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_SHT1 D_SENSOR_DID_NOT_ACK_COMMAND)); + } + return (!ackerror); +} + +bool ShtAwaitResult(void) +{ + + for (uint32_t i = 0; i < 16; i++) { + if (LOW == digitalRead(sht_sda_pin)) { + return true; + } + delay(20); + } + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_SHT1 D_SENSOR_BUSY)); + + return false; +} + +int ShtReadData(void) +{ + int val = 0; + + + val = shiftIn(sht_sda_pin, sht_scl_pin, 8); + val <<= 8; + + pinMode(sht_sda_pin, OUTPUT); + digitalWrite(sht_sda_pin, LOW); + digitalWrite(sht_scl_pin, HIGH); + digitalWrite(sht_scl_pin, LOW); + pinMode(sht_sda_pin, INPUT_PULLUP); + + val |= shiftIn(sht_sda_pin, sht_scl_pin, 8); + + digitalWrite(sht_scl_pin, HIGH); + digitalWrite(sht_scl_pin, LOW); + return val; +} + +bool ShtRead(void) +{ + if (sht_valid) { sht_valid--; } + if (!ShtReset()) { return false; } + if (!ShtSendCommand(SHT1X_CMD_MEASURE_TEMP)) { return false; } + if (!ShtAwaitResult()) { return false; } + float tempRaw = ShtReadData(); + if (!ShtSendCommand(SHT1X_CMD_MEASURE_RH)) { return false; } + if (!ShtAwaitResult()) { return false; } + float humRaw = ShtReadData(); + + + const float d1 = -39.7; + const float d2 = 0.01; + sht_temperature = d1 + (tempRaw * d2); + const float c1 = -2.0468; + const float c2 = 0.0367; + const float c3 = -1.5955E-6; + const float t1 = 0.01; + const float t2 = 0.00008; + float rhLinear = c1 + c2 * humRaw + c3 * humRaw * humRaw; + sht_humidity = (sht_temperature - 25) * (t1 + t2 * humRaw) + rhLinear; + sht_temperature = ConvertTemp(sht_temperature); + sht_humidity = ConvertHumidity(sht_humidity); + + sht_valid = SENSOR_MAX_MISS; + return true; +} + + + +void ShtDetect(void) +{ + sht_sda_pin = pin[GPIO_I2C_SDA]; + sht_scl_pin = pin[GPIO_I2C_SCL]; + if (ShtRead()) { + sht_type = 1; + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C D_SHT1X_FOUND)); + } else { + Wire.begin(sht_sda_pin, sht_scl_pin); + sht_type = 0; + } +} + +void ShtEverySecond(void) +{ + if (!(uptime %4)) { + + if (!ShtRead()) { + AddLogMissed(sht_types, sht_valid); + } + } +} + +void ShtShow(bool json) +{ + if (sht_valid) { + TempHumDewShow(json, (0 == tele_period), sht_types, sht_temperature, sht_humidity); + } +} + + + + + +bool Xsns07(uint8_t function) +{ + if (!I2cEnabled(XI2C_08)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + ShtDetect(); + } + else if (sht_type) { + switch (function) { + case FUNC_EVERY_SECOND: + ShtEverySecond(); + break; + case FUNC_JSON_APPEND: + ShtShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + ShtShow(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_08_htu21.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_08_htu21.ino" +#ifdef USE_I2C +#ifdef USE_HTU +# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_08_htu21.ino" +#define XSNS_08 8 +#define XI2C_09 9 + +#define HTU21_ADDR 0x40 + +#define SI7013_CHIPID 0x0D +#define SI7020_CHIPID 0x14 +#define SI7021_CHIPID 0x15 +#define HTU21_CHIPID 0x32 + +#define HTU21_READTEMP 0xE3 +#define HTU21_READHUM 0xE5 +#define HTU21_WRITEREG 0xE6 +#define HTU21_READREG 0xE7 +#define HTU21_RESET 0xFE +#define HTU21_HEATER_WRITE 0x51 +#define HTU21_HEATER_READ 0x11 +#define HTU21_SERIAL2_READ1 0xFC +#define HTU21_SERIAL2_READ2 0xC9 + +#define HTU21_HEATER_ON 0x04 +#define HTU21_HEATER_OFF 0xFB + +#define HTU21_RES_RH12_T14 0x00 +#define HTU21_RES_RH8_T12 0x01 +#define HTU21_RES_RH10_T13 0x80 +#define HTU21_RES_RH11_T11 0x81 + +#define HTU21_CRC8_POLYNOM 0x13100 + +const char kHtuTypes[] PROGMEM = "HTU21|SI7013|SI7020|SI7021|T/RH?"; + +uint8_t htu_address; +uint8_t htu_type = 0; +uint8_t htu_delay_temp; +uint8_t htu_delay_humidity = 50; +uint8_t htu_valid = 0; +float htu_temperature = 0; +float htu_humidity = 0; +char htu_types[7]; + +uint8_t HtuCheckCrc8(uint16_t data) +{ + for (uint32_t bit = 0; bit < 16; bit++) { + if (data & 0x8000) { + data = (data << 1) ^ HTU21_CRC8_POLYNOM; + } else { + data <<= 1; + } + } + return data >>= 8; +} + +uint8_t HtuReadDeviceId(void) +{ + uint16_t deviceID = 0; + uint8_t checksum = 0; + + Wire.beginTransmission(HTU21_ADDR); + Wire.write(HTU21_SERIAL2_READ1); + Wire.write(HTU21_SERIAL2_READ2); + Wire.endTransmission(); + + Wire.requestFrom(HTU21_ADDR, 3); + deviceID = Wire.read() << 8; + deviceID |= Wire.read(); + checksum = Wire.read(); + if (HtuCheckCrc8(deviceID) == checksum) { + deviceID = deviceID >> 8; + } else { + deviceID = 0; + } + return (uint8_t)deviceID; +} + +void HtuSetResolution(uint8_t resolution) +{ + uint8_t current = I2cRead8(HTU21_ADDR, HTU21_READREG); + current &= 0x7E; + current |= resolution; + I2cWrite8(HTU21_ADDR, HTU21_WRITEREG, current); +} + +void HtuReset(void) +{ + Wire.beginTransmission(HTU21_ADDR); + Wire.write(HTU21_RESET); + Wire.endTransmission(); + delay(15); +} + +void HtuHeater(uint8_t heater) +{ + uint8_t current = I2cRead8(HTU21_ADDR, HTU21_READREG); + + switch(heater) + { + case HTU21_HEATER_ON : current |= heater; + break; + case HTU21_HEATER_OFF : current &= heater; + break; + default : current &= heater; + break; + } + I2cWrite8(HTU21_ADDR, HTU21_WRITEREG, current); +} + +void HtuInit(void) +{ + HtuReset(); + HtuHeater(HTU21_HEATER_OFF); + HtuSetResolution(HTU21_RES_RH12_T14); +} + +bool HtuRead(void) +{ + uint8_t checksum = 0; + uint16_t sensorval = 0; + + if (htu_valid) { htu_valid--; } + + Wire.beginTransmission(HTU21_ADDR); + Wire.write(HTU21_READTEMP); + if (Wire.endTransmission() != 0) { return false; } + delay(htu_delay_temp); + + Wire.requestFrom(HTU21_ADDR, 3); + if (3 == Wire.available()) { + sensorval = Wire.read() << 8; + sensorval |= Wire.read(); + checksum = Wire.read(); + } + if (HtuCheckCrc8(sensorval) != checksum) { return false; } + + htu_temperature = ConvertTemp(0.002681 * (float)sensorval - 46.85); + + Wire.beginTransmission(HTU21_ADDR); + Wire.write(HTU21_READHUM); + if (Wire.endTransmission() != 0) { return false; } + delay(htu_delay_humidity); + + Wire.requestFrom(HTU21_ADDR, 3); + if (3 <= Wire.available()) { + sensorval = Wire.read() << 8; + sensorval |= Wire.read(); + checksum = Wire.read(); + } + if (HtuCheckCrc8(sensorval) != checksum) { return false; } + + sensorval ^= 0x02; + htu_humidity = 0.001907 * (float)sensorval - 6; + if (htu_humidity > 100) { htu_humidity = 100.0; } + if (htu_humidity < 0) { htu_humidity = 0.01; } + + if ((0.00 == htu_humidity) && (0.00 == htu_temperature)) { + htu_humidity = 0.0; + } + if ((htu_temperature > 0.00) && (htu_temperature < 80.00)) { + htu_humidity = (-0.15) * (25 - htu_temperature) + htu_humidity; + } + htu_humidity = ConvertHumidity(htu_humidity); + + htu_valid = SENSOR_MAX_MISS; + return true; +} + + + +void HtuDetect(void) +{ + htu_address = HTU21_ADDR; + if (I2cActive(htu_address)) { return; } + + htu_type = HtuReadDeviceId(); + if (htu_type) { + uint8_t index = 0; + HtuInit(); + switch (htu_type) { + case HTU21_CHIPID: + htu_delay_temp = 50; + htu_delay_humidity = 16; + break; + case SI7021_CHIPID: + index++; + case SI7020_CHIPID: + index++; + case SI7013_CHIPID: + index++; + htu_delay_temp = 12; + htu_delay_humidity = 23; + break; + default: + index = 4; + htu_delay_temp = 50; + htu_delay_humidity = 23; + } + GetTextIndexed(htu_types, sizeof(htu_types), index, kHtuTypes); + I2cSetActiveFound(htu_address, htu_types); + } +} + +void HtuEverySecond(void) +{ + if (uptime &1) { + + if (!HtuRead()) { + AddLogMissed(htu_types, htu_valid); + } + } +} + +void HtuShow(bool json) +{ + if (htu_valid) { + TempHumDewShow(json, (0 == tele_period), htu_types, htu_temperature, htu_humidity); + } +} + + + + + +bool Xsns08(uint8_t function) +{ + if (!I2cEnabled(XI2C_09)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + HtuDetect(); + } + else if (htu_type) { + switch (function) { + case FUNC_EVERY_SECOND: + HtuEverySecond(); + break; + case FUNC_JSON_APPEND: + HtuShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + HtuShow(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_09_bmp.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_09_bmp.ino" +#ifdef USE_I2C +#ifdef USE_BMP +# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_09_bmp.ino" +#define XSNS_09 9 +#define XI2C_10 10 + +#define BMP_ADDR1 0x76 +#define BMP_ADDR2 0x77 + +#define BMP180_CHIPID 0x55 +#define BMP280_CHIPID 0x58 +#define BME280_CHIPID 0x60 +#define BME680_CHIPID 0x61 + +#define BMP_REGISTER_CHIPID 0xD0 + +#define BMP_REGISTER_RESET 0xE0 + +#define BMP_CMND_RESET 0xB6 + +#define BMP_MAX_SENSORS 2 + +const char kBmpTypes[] PROGMEM = "BMP180|BMP280|BME280|BME680"; + +typedef struct { + uint8_t bmp_address; + char bmp_name[7]; + uint8_t bmp_type; + uint8_t bmp_model; +#ifdef USE_BME680 + uint8_t bme680_state; + float bmp_gas_resistance; +#endif + float bmp_temperature; + float bmp_pressure; + float bmp_humidity; +} bmp_sensors_t; + +uint8_t bmp_addresses[] = { BMP_ADDR1, BMP_ADDR2 }; +uint8_t bmp_count = 0; +uint8_t bmp_once = 1; + +bmp_sensors_t *bmp_sensors = nullptr; + + + + + +#define BMP180_REG_CONTROL 0xF4 +#define BMP180_REG_RESULT 0xF6 +#define BMP180_TEMPERATURE 0x2E +#define BMP180_PRESSURE3 0xF4 + +#define BMP180_AC1 0xAA +#define BMP180_AC2 0xAC +#define BMP180_AC3 0xAE +#define BMP180_AC4 0xB0 +#define BMP180_AC5 0xB2 +#define BMP180_AC6 0xB4 +#define BMP180_VB1 0xB6 +#define BMP180_VB2 0xB8 +#define BMP180_MB 0xBA +#define BMP180_MC 0xBC +#define BMP180_MD 0xBE + +#define BMP180_OSS 3 + +typedef struct { + int16_t cal_ac1; + int16_t cal_ac2; + int16_t cal_ac3; + int16_t cal_b1; + int16_t cal_b2; + int16_t cal_mc; + int16_t cal_md; + uint16_t cal_ac4; + uint16_t cal_ac5; + uint16_t cal_ac6; +} bmp180_cal_data_t; + +bmp180_cal_data_t *bmp180_cal_data = nullptr; + +bool Bmp180Calibration(uint8_t bmp_idx) +{ + if (!bmp180_cal_data) { + bmp180_cal_data = (bmp180_cal_data_t*)malloc(BMP_MAX_SENSORS * sizeof(bmp180_cal_data_t)); + } + if (!bmp180_cal_data) { return false; } + + bmp180_cal_data[bmp_idx].cal_ac1 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC1); + bmp180_cal_data[bmp_idx].cal_ac2 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC2); + bmp180_cal_data[bmp_idx].cal_ac3 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC3); + bmp180_cal_data[bmp_idx].cal_ac4 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC4); + bmp180_cal_data[bmp_idx].cal_ac5 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC5); + bmp180_cal_data[bmp_idx].cal_ac6 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC6); + bmp180_cal_data[bmp_idx].cal_b1 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_VB1); + bmp180_cal_data[bmp_idx].cal_b2 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_VB2); + bmp180_cal_data[bmp_idx].cal_mc = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_MC); + bmp180_cal_data[bmp_idx].cal_md = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_MD); + + + if (!bmp180_cal_data[bmp_idx].cal_ac1 | + !bmp180_cal_data[bmp_idx].cal_ac2 | + !bmp180_cal_data[bmp_idx].cal_ac3 | + !bmp180_cal_data[bmp_idx].cal_ac4 | + !bmp180_cal_data[bmp_idx].cal_ac5 | + !bmp180_cal_data[bmp_idx].cal_ac6 | + !bmp180_cal_data[bmp_idx].cal_b1 | + !bmp180_cal_data[bmp_idx].cal_b2 | + !bmp180_cal_data[bmp_idx].cal_mc | + !bmp180_cal_data[bmp_idx].cal_md) { + return false; + } + + if ((bmp180_cal_data[bmp_idx].cal_ac1 == (int16_t)0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_ac2 == (int16_t)0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_ac3 == (int16_t)0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_ac4 == 0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_ac5 == 0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_ac6 == 0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_b1 == (int16_t)0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_b2 == (int16_t)0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_mc == (int16_t)0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_md == (int16_t)0xFFFF)) { + return false; + } + return true; +} + +void Bmp180Read(uint8_t bmp_idx) +{ + if (!bmp180_cal_data) { return; } + + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_CONTROL, BMP180_TEMPERATURE); + delay(5); + int ut = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_RESULT); + int32_t xt1 = (ut - (int32_t)bmp180_cal_data[bmp_idx].cal_ac6) * ((int32_t)bmp180_cal_data[bmp_idx].cal_ac5) >> 15; + int32_t xt2 = ((int32_t)bmp180_cal_data[bmp_idx].cal_mc << 11) / (xt1 + (int32_t)bmp180_cal_data[bmp_idx].cal_md); + int32_t bmp180_b5 = xt1 + xt2; + bmp_sensors[bmp_idx].bmp_temperature = ((bmp180_b5 + 8) >> 4) / 10.0; + + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_CONTROL, BMP180_PRESSURE3); + delay(2 + (4 << BMP180_OSS)); + uint32_t up = I2cRead24(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_RESULT); + up >>= (8 - BMP180_OSS); + + int32_t b6 = bmp180_b5 - 4000; + int32_t x1 = ((int32_t)bmp180_cal_data[bmp_idx].cal_b2 * ((b6 * b6) >> 12)) >> 11; + int32_t x2 = ((int32_t)bmp180_cal_data[bmp_idx].cal_ac2 * b6) >> 11; + int32_t x3 = x1 + x2; + int32_t b3 = ((((int32_t)bmp180_cal_data[bmp_idx].cal_ac1 * 4 + x3) << BMP180_OSS) + 2) >> 2; + + x1 = ((int32_t)bmp180_cal_data[bmp_idx].cal_ac3 * b6) >> 13; + x2 = ((int32_t)bmp180_cal_data[bmp_idx].cal_b1 * ((b6 * b6) >> 12)) >> 16; + x3 = ((x1 + x2) + 2) >> 2; + uint32_t b4 = ((uint32_t)bmp180_cal_data[bmp_idx].cal_ac4 * (uint32_t)(x3 + 32768)) >> 15; + uint32_t b7 = ((uint32_t)up - b3) * (uint32_t)(50000UL >> BMP180_OSS); + + int32_t p; + if (b7 < 0x80000000) { + p = (b7 * 2) / b4; + } + else { + p = (b7 / b4) * 2; + } + x1 = (p >> 8) * (p >> 8); + x1 = (x1 * 3038) >> 16; + x2 = (-7357 * p) >> 16; + p += ((x1 + x2 + (int32_t)3791) >> 4); + bmp_sensors[bmp_idx].bmp_pressure = (float)p / 100.0; +} + + + + + + + +#define BME280_REGISTER_CONTROLHUMID 0xF2 +#define BME280_REGISTER_CONTROL 0xF4 +#define BME280_REGISTER_CONFIG 0xF5 +#define BME280_REGISTER_PRESSUREDATA 0xF7 +#define BME280_REGISTER_TEMPDATA 0xFA +#define BME280_REGISTER_HUMIDDATA 0xFD + +#define BME280_REGISTER_DIG_T1 0x88 +#define BME280_REGISTER_DIG_T2 0x8A +#define BME280_REGISTER_DIG_T3 0x8C +#define BME280_REGISTER_DIG_P1 0x8E +#define BME280_REGISTER_DIG_P2 0x90 +#define BME280_REGISTER_DIG_P3 0x92 +#define BME280_REGISTER_DIG_P4 0x94 +#define BME280_REGISTER_DIG_P5 0x96 +#define BME280_REGISTER_DIG_P6 0x98 +#define BME280_REGISTER_DIG_P7 0x9A +#define BME280_REGISTER_DIG_P8 0x9C +#define BME280_REGISTER_DIG_P9 0x9E +#define BME280_REGISTER_DIG_H1 0xA1 +#define BME280_REGISTER_DIG_H2 0xE1 +#define BME280_REGISTER_DIG_H3 0xE3 +#define BME280_REGISTER_DIG_H4 0xE4 +#define BME280_REGISTER_DIG_H5 0xE5 +#define BME280_REGISTER_DIG_H6 0xE7 + +typedef struct { + uint16_t dig_T1; + int16_t dig_T2; + int16_t dig_T3; + uint16_t dig_P1; + int16_t dig_P2; + int16_t dig_P3; + int16_t dig_P4; + int16_t dig_P5; + int16_t dig_P6; + int16_t dig_P7; + int16_t dig_P8; + int16_t dig_P9; + int16_t dig_H2; + int16_t dig_H4; + int16_t dig_H5; + uint8_t dig_H1; + uint8_t dig_H3; + int8_t dig_H6; +} Bme280CalibrationData_t; + +Bme280CalibrationData_t *Bme280CalibrationData = nullptr; + +bool Bmx280Calibrate(uint8_t bmp_idx) +{ + + + if (!Bme280CalibrationData) { + Bme280CalibrationData = (Bme280CalibrationData_t*)malloc(BMP_MAX_SENSORS * sizeof(Bme280CalibrationData_t)); + } + if (!Bme280CalibrationData) { return false; } + + Bme280CalibrationData[bmp_idx].dig_T1 = I2cRead16LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T1); + Bme280CalibrationData[bmp_idx].dig_T2 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T2); + Bme280CalibrationData[bmp_idx].dig_T3 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T3); + Bme280CalibrationData[bmp_idx].dig_P1 = I2cRead16LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P1); + Bme280CalibrationData[bmp_idx].dig_P2 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P2); + Bme280CalibrationData[bmp_idx].dig_P3 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P3); + Bme280CalibrationData[bmp_idx].dig_P4 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P4); + Bme280CalibrationData[bmp_idx].dig_P5 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P5); + Bme280CalibrationData[bmp_idx].dig_P6 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P6); + Bme280CalibrationData[bmp_idx].dig_P7 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P7); + Bme280CalibrationData[bmp_idx].dig_P8 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P8); + Bme280CalibrationData[bmp_idx].dig_P9 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P9); + if (BME280_CHIPID == bmp_sensors[bmp_idx].bmp_type) { + Bme280CalibrationData[bmp_idx].dig_H1 = I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H1); + Bme280CalibrationData[bmp_idx].dig_H2 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H2); + Bme280CalibrationData[bmp_idx].dig_H3 = I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H3); + Bme280CalibrationData[bmp_idx].dig_H4 = (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H4) << 4) | (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H4 + 1) & 0xF); + Bme280CalibrationData[bmp_idx].dig_H5 = (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H5 + 1) << 4) | (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H5) >> 4); + Bme280CalibrationData[bmp_idx].dig_H6 = (int8_t)I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H6); + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROL, 0x00); + + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROLHUMID, 0x01); + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONFIG, 0xA0); + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROL, 0x27); + } else { + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROL, 0xB7); + } + + return true; +} + +void Bme280Read(uint8_t bmp_idx) +{ + if (!Bme280CalibrationData) { return; } + + int32_t adc_T = I2cRead24(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_TEMPDATA); + adc_T >>= 4; + + int32_t vart1 = ((((adc_T >> 3) - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T1 << 1))) * ((int32_t)Bme280CalibrationData[bmp_idx].dig_T2)) >> 11; + int32_t vart2 = (((((adc_T >> 4) - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T1)) * ((adc_T >> 4) - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T1))) >> 12) * + ((int32_t)Bme280CalibrationData[bmp_idx].dig_T3)) >> 14; + int32_t t_fine = vart1 + vart2; + float T = (t_fine * 5 + 128) >> 8; + bmp_sensors[bmp_idx].bmp_temperature = T / 100.0; + + int32_t adc_P = I2cRead24(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_PRESSUREDATA); + adc_P >>= 4; + + int64_t var1 = ((int64_t)t_fine) - 128000; + int64_t var2 = var1 * var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P6; + var2 = var2 + ((var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P5) << 17); + var2 = var2 + (((int64_t)Bme280CalibrationData[bmp_idx].dig_P4) << 35); + var1 = ((var1 * var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P3) >> 8) + ((var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P2) << 12); + var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)Bme280CalibrationData[bmp_idx].dig_P1) >> 33; + if (0 == var1) { + return; + } + int64_t p = 1048576 - adc_P; + p = (((p << 31) - var2) * 3125) / var1; + var1 = (((int64_t)Bme280CalibrationData[bmp_idx].dig_P9) * (p >> 13) * (p >> 13)) >> 25; + var2 = (((int64_t)Bme280CalibrationData[bmp_idx].dig_P8) * p) >> 19; + p = ((p + var1 + var2) >> 8) + (((int64_t)Bme280CalibrationData[bmp_idx].dig_P7) << 4); + bmp_sensors[bmp_idx].bmp_pressure = (float)p / 25600.0; + + if (BMP280_CHIPID == bmp_sensors[bmp_idx].bmp_type) { return; } + + int32_t adc_H = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_HUMIDDATA); + + int32_t v_x1_u32r = (t_fine - ((int32_t)76800)); + v_x1_u32r = (((((adc_H << 14) - (((int32_t)Bme280CalibrationData[bmp_idx].dig_H4) << 20) - + (((int32_t)Bme280CalibrationData[bmp_idx].dig_H5) * v_x1_u32r)) + ((int32_t)16384)) >> 15) * + (((((((v_x1_u32r * ((int32_t)Bme280CalibrationData[bmp_idx].dig_H6)) >> 10) * + (((v_x1_u32r * ((int32_t)Bme280CalibrationData[bmp_idx].dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + + ((int32_t)2097152)) * ((int32_t)Bme280CalibrationData[bmp_idx].dig_H2) + 8192) >> 14)); + v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * + ((int32_t)Bme280CalibrationData[bmp_idx].dig_H1)) >> 4)); + v_x1_u32r = (v_x1_u32r < 0) ? 0 : v_x1_u32r; + v_x1_u32r = (v_x1_u32r > 419430400) ? 419430400 : v_x1_u32r; + float h = (v_x1_u32r >> 12); + bmp_sensors[bmp_idx].bmp_humidity = h / 1024.0; +} + +#ifdef USE_BME680 + + + + +#include + +struct bme680_dev *gas_sensor = nullptr; + +static void BmeDelayMs(uint32_t ms) +{ + delay(ms); +} + +bool Bme680Init(uint8_t bmp_idx) +{ + if (!gas_sensor) { + gas_sensor = (bme680_dev*)malloc(BMP_MAX_SENSORS * sizeof(bme680_dev)); + } + if (!gas_sensor) { return false; } + + gas_sensor[bmp_idx].dev_id = bmp_sensors[bmp_idx].bmp_address; + gas_sensor[bmp_idx].intf = BME680_I2C_INTF; + gas_sensor[bmp_idx].read = &I2cReadBuffer; + gas_sensor[bmp_idx].write = &I2cWriteBuffer; + gas_sensor[bmp_idx].delay_ms = BmeDelayMs; + + + + gas_sensor[bmp_idx].amb_temp = 25; + + int8_t rslt = BME680_OK; + rslt = bme680_init(&gas_sensor[bmp_idx]); + if (rslt != BME680_OK) { return false; } + + + gas_sensor[bmp_idx].tph_sett.os_hum = BME680_OS_2X; + gas_sensor[bmp_idx].tph_sett.os_pres = BME680_OS_4X; + gas_sensor[bmp_idx].tph_sett.os_temp = BME680_OS_8X; + gas_sensor[bmp_idx].tph_sett.filter = BME680_FILTER_SIZE_3; + + + gas_sensor[bmp_idx].gas_sett.run_gas = BME680_ENABLE_GAS_MEAS; + + gas_sensor[bmp_idx].gas_sett.heatr_temp = 320; + gas_sensor[bmp_idx].gas_sett.heatr_dur = 150; + + + + gas_sensor[bmp_idx].power_mode = BME680_FORCED_MODE; + + + uint8_t set_required_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_FILTER_SEL | BME680_GAS_SENSOR_SEL; + + + rslt = bme680_set_sensor_settings(set_required_settings,&gas_sensor[bmp_idx]); + if (rslt != BME680_OK) { return false; } + + bmp_sensors[bmp_idx].bme680_state = 0; + + return true; +} + +void Bme680Read(uint8_t bmp_idx) +{ + if (!gas_sensor) { return; } + + int8_t rslt = BME680_OK; + + if (BME680_CHIPID == bmp_sensors[bmp_idx].bmp_type) { + if (0 == bmp_sensors[bmp_idx].bme680_state) { + + rslt = bme680_set_sensor_mode(&gas_sensor[bmp_idx]); + if (rslt != BME680_OK) { return; } + + + + + + + + bmp_sensors[bmp_idx].bme680_state = 1; + } else { + bmp_sensors[bmp_idx].bme680_state = 0; + + struct bme680_field_data data; + rslt = bme680_get_sensor_data(&data, &gas_sensor[bmp_idx]); + if (rslt != BME680_OK) { return; } + + bmp_sensors[bmp_idx].bmp_temperature = data.temperature / 100.0; + bmp_sensors[bmp_idx].bmp_humidity = data.humidity / 1000.0; + bmp_sensors[bmp_idx].bmp_pressure = data.pressure / 100.0; + + if (data.status & BME680_GASM_VALID_MSK) { + bmp_sensors[bmp_idx].bmp_gas_resistance = data.gas_resistance / 1000.0; + } else { + bmp_sensors[bmp_idx].bmp_gas_resistance = 0; + } + } + } + return; +} + +#endif + + + +void BmpDetect(void) +{ + int bmp_sensor_size = BMP_MAX_SENSORS * sizeof(bmp_sensors_t); + if (!bmp_sensors) { + bmp_sensors = (bmp_sensors_t*)malloc(bmp_sensor_size); + } + if (!bmp_sensors) { return; } + memset(bmp_sensors, 0, bmp_sensor_size); + + for (uint32_t i = 0; i < BMP_MAX_SENSORS; i++) { + if (I2cActive(bmp_addresses[i])) { continue; } + uint8_t bmp_type = I2cRead8(bmp_addresses[i], BMP_REGISTER_CHIPID); + if (bmp_type) { + bmp_sensors[bmp_count].bmp_address = bmp_addresses[i]; + bmp_sensors[bmp_count].bmp_type = bmp_type; + bmp_sensors[bmp_count].bmp_model = 0; + + bool success = false; + switch (bmp_type) { + case BMP180_CHIPID: + success = Bmp180Calibration(bmp_count); + break; + case BME280_CHIPID: + bmp_sensors[bmp_count].bmp_model++; + case BMP280_CHIPID: + bmp_sensors[bmp_count].bmp_model++; + success = Bmx280Calibrate(bmp_count); + break; +#ifdef USE_BME680 + case BME680_CHIPID: + bmp_sensors[bmp_count].bmp_model = 3; + success = Bme680Init(bmp_count); + break; +#endif + } + if (success) { + GetTextIndexed(bmp_sensors[bmp_count].bmp_name, sizeof(bmp_sensors[bmp_count].bmp_name), bmp_sensors[bmp_count].bmp_model, kBmpTypes); + I2cSetActiveFound(bmp_sensors[bmp_count].bmp_address, bmp_sensors[bmp_count].bmp_name); + bmp_count++; + } + } + } +} + +void BmpRead(void) +{ + for (uint32_t bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { + switch (bmp_sensors[bmp_idx].bmp_type) { + case BMP180_CHIPID: + Bmp180Read(bmp_idx); + break; + case BMP280_CHIPID: + case BME280_CHIPID: + Bme280Read(bmp_idx); + break; +#ifdef USE_BME680 + case BME680_CHIPID: + Bme680Read(bmp_idx); + break; +#endif + } + } +} + +void BmpShow(bool json) +{ + for (uint32_t bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { + if (bmp_sensors[bmp_idx].bmp_type) { + float bmp_sealevel = 0.0; + if (bmp_sensors[bmp_idx].bmp_pressure != 0.0) { + bmp_sealevel = (bmp_sensors[bmp_idx].bmp_pressure / FastPrecisePow(1.0 - ((float)Settings.altitude / 44330.0), 5.255)) - 21.6; + bmp_sealevel = ConvertPressure(bmp_sealevel); + } + float bmp_temperature = ConvertTemp(bmp_sensors[bmp_idx].bmp_temperature); + float bmp_pressure = ConvertPressure(bmp_sensors[bmp_idx].bmp_pressure); + + char name[10]; + strlcpy(name, bmp_sensors[bmp_idx].bmp_name, sizeof(name)); + if (bmp_count > 1) { + snprintf_P(name, sizeof(name), PSTR("%s%c%02X"), name, IndexSeparator(), bmp_sensors[bmp_idx].bmp_address); + } + + char temperature[33]; + dtostrfd(bmp_temperature, Settings.flag2.temperature_resolution, temperature); + char pressure[33]; + dtostrfd(bmp_pressure, Settings.flag2.pressure_resolution, pressure); + char sea_pressure[33]; + dtostrfd(bmp_sealevel, Settings.flag2.pressure_resolution, sea_pressure); + + float bmp_humidity = ConvertHumidity(bmp_sensors[bmp_idx].bmp_humidity); + char humidity[33]; + dtostrfd(bmp_humidity, Settings.flag2.humidity_resolution, humidity); + float f_dewpoint = CalcTempHumToDew(bmp_temperature, bmp_humidity); + char dewpoint[33]; + dtostrfd(f_dewpoint, Settings.flag2.temperature_resolution, dewpoint); +#ifdef USE_BME680 + char gas_resistance[33]; + dtostrfd(bmp_sensors[bmp_idx].bmp_gas_resistance, 2, gas_resistance); +#endif + + if (json) { + char json_humidity[80]; + snprintf_P(json_humidity, sizeof(json_humidity), PSTR(",\"" D_JSON_HUMIDITY "\":%s,\"" D_JSON_DEWPOINT "\":%s"), humidity, dewpoint); + char json_sealevel[40]; + snprintf_P(json_sealevel, sizeof(json_sealevel), PSTR(",\"" D_JSON_PRESSUREATSEALEVEL "\":%s"), sea_pressure); +#ifdef USE_BME680 + char json_gas[40]; + snprintf_P(json_gas, sizeof(json_gas), PSTR(",\"" D_JSON_GAS "\":%s"), gas_resistance); + + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s,\"" D_JSON_PRESSURE "\":%s%s%s}"), + name, + temperature, + (bmp_sensors[bmp_idx].bmp_model >= 2) ? json_humidity : "", + pressure, + (Settings.altitude != 0) ? json_sealevel : "", + (bmp_sensors[bmp_idx].bmp_model >= 3) ? json_gas : ""); +#else + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s,\"" D_JSON_PRESSURE "\":%s%s}"), + name, temperature, (bmp_sensors[bmp_idx].bmp_model >= 2) ? json_humidity : "", pressure, (Settings.altitude != 0) ? json_sealevel : ""); +#endif + +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (0 == bmp_idx)) { + DomoticzTempHumPressureSensor(bmp_temperature, bmp_humidity, bmp_pressure); +#ifdef USE_BME680 + if (bmp_sensors[bmp_idx].bmp_model >= 3) { DomoticzSensor(DZ_AIRQUALITY, (uint32_t)bmp_sensors[bmp_idx].bmp_gas_resistance); } +#endif + } +#endif + +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, bmp_temperature); + KnxSensor(KNX_HUMIDITY, bmp_humidity); + } +#endif + +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, name, temperature, TempUnit()); + if (bmp_sensors[bmp_idx].bmp_model >= 2) { + WSContentSend_PD(HTTP_SNS_HUM, name, humidity); + WSContentSend_PD(HTTP_SNS_DEW, name, dewpoint, TempUnit()); + } + WSContentSend_PD(HTTP_SNS_PRESSURE, name, pressure, PressureUnit().c_str()); + if (Settings.altitude != 0) { + WSContentSend_PD(HTTP_SNS_SEAPRESSURE, name, sea_pressure, PressureUnit().c_str()); + } +#ifdef USE_BME680 + if (bmp_sensors[bmp_idx].bmp_model >= 3) { + WSContentSend_PD(PSTR("{s}%s " D_GAS "{m}%s " D_UNIT_KILOOHM "{e}"), name, gas_resistance); + } +#endif + +#endif + } + } + } +} + +#ifdef USE_DEEPSLEEP + +void BMP_EnterSleep(void) +{ + for (uint32_t bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { + switch (bmp_sensors[bmp_idx].bmp_type) { + case BMP180_CHIPID: + case BMP280_CHIPID: + case BME280_CHIPID: + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP_REGISTER_RESET, BMP_CMND_RESET); + break; + default: + break; + } + } +} + +#endif + + + + + +bool Xsns09(uint8_t function) +{ + if (!I2cEnabled(XI2C_10)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + BmpDetect(); + } + else if (bmp_count) { + switch (function) { + case FUNC_EVERY_SECOND: + BmpRead(); + break; + case FUNC_JSON_APPEND: + BmpShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + BmpShow(0); + break; +#endif +#ifdef USE_DEEPSLEEP + case FUNC_SAVE_BEFORE_RESTART: + BMP_EnterSleep(); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_10_bh1750.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_10_bh1750.ino" +#ifdef USE_I2C +#ifdef USE_BH1750 + + + + + + +#define XSNS_10 10 +#define XI2C_11 11 + +#define BH1750_ADDR1 0x23 +#define BH1750_ADDR2 0x5C + +#define BH1750_CONTINUOUS_HIGH_RES_MODE2 0x11 +#define BH1750_CONTINUOUS_HIGH_RES_MODE 0x10 +#define BH1750_CONTINUOUS_LOW_RES_MODE 0x13 + +#define BH1750_MEASUREMENT_TIME_HIGH 0x40 +#define BH1750_MEASUREMENT_TIME_LOW 0x60 + +struct BH1750DATA { + uint8_t address; + uint8_t addresses[2] = { BH1750_ADDR1, BH1750_ADDR2 }; + uint8_t resolution[3] = { BH1750_CONTINUOUS_HIGH_RES_MODE, BH1750_CONTINUOUS_HIGH_RES_MODE2, BH1750_CONTINUOUS_LOW_RES_MODE }; + uint8_t type = 0; + uint8_t valid = 0; + uint8_t mtreg = 69; + uint16_t illuminance = 0; + char types[7] = "BH1750"; +} Bh1750; + + + +bool Bh1750SetResolution(void) +{ + Wire.beginTransmission(Bh1750.address); + Wire.write(Bh1750.resolution[Settings.SensorBits1.bh1750_resolution]); + return (!Wire.endTransmission()); +} + +bool Bh1750SetMTreg(void) +{ + Wire.beginTransmission(Bh1750.address); + uint8_t data = BH1750_MEASUREMENT_TIME_HIGH | ((Bh1750.mtreg >> 5) & 0x07); + Wire.write(data); + if (Wire.endTransmission()) { return false; } + Wire.beginTransmission(Bh1750.address); + data = BH1750_MEASUREMENT_TIME_LOW | (Bh1750.mtreg & 0x1F); + Wire.write(data); + if (Wire.endTransmission()) { return false; } + return Bh1750SetResolution(); +} + +bool Bh1750Read(void) +{ + if (Bh1750.valid) { Bh1750.valid--; } + + if (2 != Wire.requestFrom(Bh1750.address, (uint8_t)2)) { return false; } + float illuminance = (Wire.read() << 8) | Wire.read(); + illuminance /= (1.2 * (69 / (float)Bh1750.mtreg)); + if (1 == Settings.SensorBits1.bh1750_resolution) { + illuminance /= 2; + } + Bh1750.illuminance = illuminance; + + Bh1750.valid = SENSOR_MAX_MISS; + return true; +} + + + +void Bh1750Detect(void) +{ + for (uint32_t i = 0; i < sizeof(Bh1750.addresses); i++) { + Bh1750.address = Bh1750.addresses[i]; + if (I2cActive(Bh1750.address)) { continue; } + + if (Bh1750SetMTreg()) { + I2cSetActiveFound(Bh1750.address, Bh1750.types); + Bh1750.type = 1; + break; + } + } +} + +void Bh1750EverySecond(void) +{ + + if (!Bh1750Read()) { + AddLogMissed(Bh1750.types, Bh1750.valid); + } +} +# 123 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_10_bh1750.ino" +bool Bh1750CommandSensor(void) +{ + if (XdrvMailbox.data_len) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { + Settings.SensorBits1.bh1750_resolution = XdrvMailbox.payload; + Bh1750SetResolution(); + } + else if ((XdrvMailbox.payload > 30) && (XdrvMailbox.payload < 255)) { + Bh1750.mtreg = XdrvMailbox.payload; + Bh1750SetMTreg(); + } + } + Response_P(PSTR("{\"" D_CMND_SENSOR "10\":{\"Resolution\":%d,\"MTime\":%d}}"), Settings.SensorBits1.bh1750_resolution, Bh1750.mtreg); + + return true; +} + +void Bh1750Show(bool json) +{ + if (Bh1750.valid) { + if (json) { + ResponseAppend_P(JSON_SNS_ILLUMINANCE, Bh1750.types, Bh1750.illuminance); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_ILLUMINANCE, Bh1750.illuminance); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, Bh1750.types, Bh1750.illuminance); +#endif + } + } +} + + + + + +bool Xsns10(uint8_t function) +{ + if (!I2cEnabled(XI2C_11)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Bh1750Detect(); + } + else if (Bh1750.type) { + switch (function) { + case FUNC_EVERY_SECOND: + Bh1750EverySecond(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_10 == XdrvMailbox.index) { + result = Bh1750CommandSensor(); + } + break; + case FUNC_JSON_APPEND: + Bh1750Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Bh1750Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_11_veml6070.ino" +# 89 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_11_veml6070.ino" +#ifdef USE_I2C +#ifdef USE_VEML6070 + + + + + + +#define XSNS_11 11 +#define XI2C_12 12 + +#define VEML6070_ADDR_H 0x39 +#define VEML6070_ADDR_L 0x38 +#define VEML6070_INTEGRATION_TIME 3 +#define VEML6070_ENABLE 1 +#define VEML6070_DISABLE 0 +#define VEML6070_RSET_DEFAULT 270000 +#define VEML6070_UV_MAX_INDEX 15 +#define VEML6070_UV_MAX_DEFAULT 11 +#define VEML6070_POWER_COEFFCIENT 0.025 +#define VEML6070_TABLE_COEFFCIENT 32.86270591 + + + + + +const char kVemlTypes[] PROGMEM = "VEML6070"; +double uv_risk_map[VEML6070_UV_MAX_INDEX] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +double uvrisk = 0; +double uvpower = 0; +uint16_t uvlevel = 0; +uint8_t veml6070_addr_low = VEML6070_ADDR_L; +uint8_t veml6070_addr_high = VEML6070_ADDR_H; +uint8_t itime = VEML6070_INTEGRATION_TIME; +uint8_t veml6070_type = 0; +char veml6070_name[9]; +char str_uvrisk_text[10]; + + + +void Veml6070Detect(void) +{ + if (I2cActive(VEML6070_ADDR_L)) { return; } + + + Wire.beginTransmission(VEML6070_ADDR_L); + Wire.write((itime << 2) | 0x02); + uint8_t status = Wire.endTransmission(); + + if (!status) { + veml6070_type = 1; + Veml6070UvTableInit(); + uint8_t veml_model = 0; + GetTextIndexed(veml6070_name, sizeof(veml6070_name), veml_model, kVemlTypes); + I2cSetActiveFound(VEML6070_ADDR_L, veml6070_name); + } +} + + + +void Veml6070UvTableInit(void) +{ + + for (uint32_t i = 0; i < VEML6070_UV_MAX_INDEX; i++) { +#ifdef USE_VEML6070_RSET + if ( (USE_VEML6070_RSET >= 220000) && (USE_VEML6070_RSET <= 1000000) ) { + uv_risk_map[i] = ( (USE_VEML6070_RSET / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); + } else { + uv_risk_map[i] = ( (VEML6070_RSET_DEFAULT / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 resistor error %d"), USE_VEML6070_RSET); + } +#else + uv_risk_map[i] = ( (VEML6070_RSET_DEFAULT / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 resistor default used %d"), VEML6070_RSET_DEFAULT); +#endif + } +} + + + +void Veml6070EverySecond(void) +{ + + Veml6070ModeCmd(1); + uvlevel = Veml6070ReadUv(); + uvrisk = Veml6070UvRiskLevel(uvlevel); + uvpower = Veml6070UvPower(uvrisk); + Veml6070ModeCmd(0); +} + + + +void Veml6070ModeCmd(bool mode_cmd) +{ + + + Wire.beginTransmission(VEML6070_ADDR_L); + Wire.write((mode_cmd << 0) | 0x02 | (itime << 2)); + uint8_t status = Wire.endTransmission(); + + if (!status) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 mode_cmd")); + } +} + + + +uint16_t Veml6070ReadUv(void) +{ + uint16_t uv_raw = 0; + + if (Wire.requestFrom(VEML6070_ADDR_H, 1) != 1) { + return -1; + } + uv_raw = Wire.read(); + uv_raw <<= 8; + + if (Wire.requestFrom(VEML6070_ADDR_L, 1) != 1) { + return -1; + } + uv_raw |= Wire.read(); + + return uv_raw; +} + + + +double Veml6070UvRiskLevel(uint16_t uv_level) +{ + double risk = 0; + if (uv_level < uv_risk_map[VEML6070_UV_MAX_INDEX-1]) { + risk = (double)uv_level / uv_risk_map[0]; + + if ( (risk >= 0) && (risk <= 2.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_1); } + else if ( (risk >= 3.0) && (risk <= 5.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_2); } + else if ( (risk >= 6.0) && (risk <= 7.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_3); } + else if ( (risk >= 8.0) && (risk <= 10.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_4); } + else if ( (risk >= 11.0) && (risk <= 12.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_5); } + else if ( (risk >= 13.0) && (risk <= 25.0) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_6); } + else { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_7); } + return risk; + } else { + + snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_7); + return ( risk = 99 ); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 out of range %d"), risk); + } +} + + + +double Veml6070UvPower(double uvrisk) +{ + + double power = 0; + return ( power = VEML6070_POWER_COEFFCIENT * uvrisk ); +} + + + + +#ifdef USE_WEBSERVER + +#ifdef USE_VEML6070_SHOW_RAW + const char HTTP_SNS_UV_LEVEL[] PROGMEM = "{s}VEML6070 " D_UV_LEVEL "{m}%s " D_UNIT_INCREMENTS "{e}"; +#endif + + const char HTTP_SNS_UV_INDEX[] PROGMEM = "{s}VEML6070 " D_UV_INDEX "{m}%s %s{e}"; + const char HTTP_SNS_UV_POWER[] PROGMEM = "{s}VEML6070 " D_UV_POWER "{m}%s " D_UNIT_WATT_METER_QUADRAT "{e}"; +#endif + + + +void Veml6070Show(bool json) +{ + + char str_uvlevel[33]; + dtostrfd((double)uvlevel, 0, str_uvlevel); + char str_uvrisk[33]; + dtostrfd(uvrisk, 2, str_uvrisk); + char str_uvpower[33]; + dtostrfd(uvpower, 3, str_uvpower); + if (json) { +#ifdef USE_VEML6070_SHOW_RAW + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_UV_LEVEL "\":%s,\"" D_JSON_UV_INDEX "\":%s,\"" D_JSON_UV_INDEX_TEXT "\":\"%s\",\"" D_JSON_UV_POWER "\":%s}"), + veml6070_name, str_uvlevel, str_uvrisk, str_uvrisk_text, str_uvpower); +#else + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_UV_INDEX "\":%s,\"" D_JSON_UV_INDEX_TEXT "\":\"%s\",\"" D_JSON_UV_POWER "\":%s}"), + veml6070_name, str_uvrisk, str_uvrisk_text, str_uvpower); +#endif +#ifdef USE_DOMOTICZ + if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, uvlevel); } +#endif +#ifdef USE_WEBSERVER + } else { +#ifdef USE_VEML6070_SHOW_RAW + WSContentSend_PD(HTTP_SNS_UV_LEVEL, str_uvlevel); +#endif + WSContentSend_PD(HTTP_SNS_UV_INDEX, str_uvrisk, str_uvrisk_text); + WSContentSend_PD(HTTP_SNS_UV_POWER, str_uvpower); +#endif + } +} + + + + + +bool Xsns11(uint8_t function) +{ + if (!I2cEnabled(XI2C_12)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Veml6070Detect(); + } + else if (veml6070_type) { + switch (function) { + case FUNC_EVERY_SECOND: + Veml6070EverySecond(); + break; + case FUNC_JSON_APPEND: + Veml6070Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Veml6070Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_12_ads1115.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_12_ads1115.ino" +#ifdef USE_I2C +#ifdef USE_ADS1115 +# 43 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_12_ads1115.ino" +#define XSNS_12 12 +#define XI2C_13 13 + +#define ADS1115_ADDRESS_ADDR_GND 0x48 +#define ADS1115_ADDRESS_ADDR_VDD 0x49 +#define ADS1115_ADDRESS_ADDR_SDA 0x4A +#define ADS1115_ADDRESS_ADDR_SCL 0x4B + +#define ADS1115_CONVERSIONDELAY (8) + + + + +#define ADS1115_REG_POINTER_MASK (0x03) +#define ADS1115_REG_POINTER_CONVERT (0x00) +#define ADS1115_REG_POINTER_CONFIG (0x01) +#define ADS1115_REG_POINTER_LOWTHRESH (0x02) +#define ADS1115_REG_POINTER_HITHRESH (0x03) + + + + +#define ADS1115_REG_CONFIG_OS_MASK (0x8000) +#define ADS1115_REG_CONFIG_OS_SINGLE (0x8000) +#define ADS1115_REG_CONFIG_OS_BUSY (0x0000) +#define ADS1115_REG_CONFIG_OS_NOTBUSY (0x8000) + +#define ADS1115_REG_CONFIG_MUX_MASK (0x7000) +#define ADS1115_REG_CONFIG_MUX_DIFF_0_1 (0x0000) +#define ADS1115_REG_CONFIG_MUX_DIFF_0_3 (0x1000) +#define ADS1115_REG_CONFIG_MUX_DIFF_1_3 (0x2000) +#define ADS1115_REG_CONFIG_MUX_DIFF_2_3 (0x3000) +#define ADS1115_REG_CONFIG_MUX_SINGLE_0 (0x4000) +#define ADS1115_REG_CONFIG_MUX_SINGLE_1 (0x5000) +#define ADS1115_REG_CONFIG_MUX_SINGLE_2 (0x6000) +#define ADS1115_REG_CONFIG_MUX_SINGLE_3 (0x7000) + +#define ADS1115_REG_CONFIG_PGA_MASK (0x0E00) +#define ADS1115_REG_CONFIG_PGA_6_144V (0x0000) +#define ADS1115_REG_CONFIG_PGA_4_096V (0x0200) +#define ADS1115_REG_CONFIG_PGA_2_048V (0x0400) +#define ADS1115_REG_CONFIG_PGA_1_024V (0x0600) +#define ADS1115_REG_CONFIG_PGA_0_512V (0x0800) +#define ADS1115_REG_CONFIG_PGA_0_256V (0x0A00) + +#define ADS1115_REG_CONFIG_MODE_MASK (0x0100) +#define ADS1115_REG_CONFIG_MODE_CONTIN (0x0000) +#define ADS1115_REG_CONFIG_MODE_SINGLE (0x0100) + +#define ADS1115_REG_CONFIG_DR_MASK (0x00E0) +#define ADS1115_REG_CONFIG_DR_128SPS (0x0000) +#define ADS1115_REG_CONFIG_DR_250SPS (0x0020) +#define ADS1115_REG_CONFIG_DR_490SPS (0x0040) +#define ADS1115_REG_CONFIG_DR_920SPS (0x0060) +#define ADS1115_REG_CONFIG_DR_1600SPS (0x0080) +#define ADS1115_REG_CONFIG_DR_2400SPS (0x00A0) +#define ADS1115_REG_CONFIG_DR_3300SPS (0x00C0) +#define ADS1115_REG_CONFIG_DR_6000SPS (0x00E0) + +#define ADS1115_REG_CONFIG_CMODE_MASK (0x0010) +#define ADS1115_REG_CONFIG_CMODE_TRAD (0x0000) +#define ADS1115_REG_CONFIG_CMODE_WINDOW (0x0010) + +#define ADS1115_REG_CONFIG_CPOL_MASK (0x0008) +#define ADS1115_REG_CONFIG_CPOL_ACTVLOW (0x0000) +#define ADS1115_REG_CONFIG_CPOL_ACTVHI (0x0008) + +#define ADS1115_REG_CONFIG_CLAT_MASK (0x0004) +#define ADS1115_REG_CONFIG_CLAT_NONLAT (0x0000) +#define ADS1115_REG_CONFIG_CLAT_LATCH (0x0004) + +#define ADS1115_REG_CONFIG_CQUE_MASK (0x0003) +#define ADS1115_REG_CONFIG_CQUE_1CONV (0x0000) +#define ADS1115_REG_CONFIG_CQUE_2CONV (0x0001) +#define ADS1115_REG_CONFIG_CQUE_4CONV (0x0002) +#define ADS1115_REG_CONFIG_CQUE_NONE (0x0003) + +struct ADS1115 { + uint8_t count = 0; + uint8_t address; + uint8_t addresses[4] = { ADS1115_ADDRESS_ADDR_GND, ADS1115_ADDRESS_ADDR_VDD, ADS1115_ADDRESS_ADDR_SDA, ADS1115_ADDRESS_ADDR_SCL }; + uint8_t found[4] = {false,false,false,false}; +} Ads1115; + + + +void Ads1115StartComparator(uint8_t channel, uint16_t mode) +{ + + uint16_t config = mode | + ADS1115_REG_CONFIG_CQUE_NONE | + ADS1115_REG_CONFIG_CLAT_NONLAT | + ADS1115_REG_CONFIG_PGA_6_144V | + ADS1115_REG_CONFIG_CPOL_ACTVLOW | + ADS1115_REG_CONFIG_CMODE_TRAD | + ADS1115_REG_CONFIG_DR_6000SPS; + + + config |= (ADS1115_REG_CONFIG_MUX_SINGLE_0 + (0x1000 * channel)); + + + I2cWrite16(Ads1115.address, ADS1115_REG_POINTER_CONFIG, config); +} + +int16_t Ads1115GetConversion(uint8_t channel) +{ + Ads1115StartComparator(channel, ADS1115_REG_CONFIG_MODE_SINGLE); + + delay(ADS1115_CONVERSIONDELAY); + + I2cRead16(Ads1115.address, ADS1115_REG_POINTER_CONVERT); + + Ads1115StartComparator(channel, ADS1115_REG_CONFIG_MODE_CONTIN); + delay(ADS1115_CONVERSIONDELAY); + + uint16_t res = I2cRead16(Ads1115.address, ADS1115_REG_POINTER_CONVERT); + return (int16_t)res; +} + + + +void Ads1115Detect(void) +{ + for (uint32_t i = 0; i < sizeof(Ads1115.addresses); i++) { + if (!Ads1115.found[i]) { + Ads1115.address = Ads1115.addresses[i]; + if (I2cActive(Ads1115.address)) { continue; } + uint16_t buffer; + if (I2cValidRead16(&buffer, Ads1115.address, ADS1115_REG_POINTER_CONVERT) && + I2cValidRead16(&buffer, Ads1115.address, ADS1115_REG_POINTER_CONFIG)) { + Ads1115StartComparator(i, ADS1115_REG_CONFIG_MODE_CONTIN); + I2cSetActiveFound(Ads1115.address, "ADS1115"); + Ads1115.found[i] = 1; + Ads1115.count++; + } + } + } +} + +void Ads1115Show(bool json) +{ + int16_t values[4]; + + for (uint32_t t = 0; t < sizeof(Ads1115.addresses); t++) { + + if (Ads1115.found[t]) { + + uint8_t old_address = Ads1115.address; + Ads1115.address = Ads1115.addresses[t]; + for (uint32_t i = 0; i < 4; i++) { + values[i] = Ads1115GetConversion(i); + + } + Ads1115.address = old_address; + + char label[15]; + if (1 == Ads1115.count) { + + snprintf_P(label, sizeof(label), PSTR("ADS1115")); + } else { + + snprintf_P(label, sizeof(label), PSTR("ADS1115%c%02x"), IndexSeparator(), Ads1115.addresses[t]); + } + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{"), label); + for (uint32_t i = 0; i < 4; i++) { + ResponseAppend_P(PSTR("%s\"A%d\":%d"), (0 == i) ? "" : ",", i, values[i]); + } + ResponseJsonEnd(); + } +#ifdef USE_WEBSERVER + else { + for (uint32_t i = 0; i < 4; i++) { + WSContentSend_PD(HTTP_SNS_ANALOG, label, i, values[i]); + } + } +#endif + } + } +} + + + + + +bool Xsns12(uint8_t function) +{ + if (!I2cEnabled(XI2C_13)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Ads1115Detect(); + } + else if (Ads1115.count) { + switch (function) { + case FUNC_JSON_APPEND: + Ads1115Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Ads1115Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_13_ina219.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_13_ina219.ino" +#ifdef USE_I2C +#ifdef USE_INA219 +# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_13_ina219.ino" +#define XSNS_13 13 +#define XI2C_14 14 + +#define INA219_ADDRESS1 (0x40) +#define INA219_ADDRESS2 (0x41) +#define INA219_ADDRESS3 (0x44) +#define INA219_ADDRESS4 (0x45) + +#define INA219_READ (0x01) +#define INA219_REG_CONFIG (0x00) + +#define INA219_CONFIG_RESET (0x8000) + +#define INA219_CONFIG_BVOLTAGERANGE_MASK (0x2000) +#define INA219_CONFIG_BVOLTAGERANGE_16V (0x0000) +#define INA219_CONFIG_BVOLTAGERANGE_32V (0x2000) + +#define INA219_CONFIG_GAIN_MASK (0x1800) +#define INA219_CONFIG_GAIN_1_40MV (0x0000) +#define INA219_CONFIG_GAIN_2_80MV (0x0800) +#define INA219_CONFIG_GAIN_4_160MV (0x1000) +#define INA219_CONFIG_GAIN_8_320MV (0x1800) + +#define INA219_CONFIG_BADCRES_MASK (0x0780) +#define INA219_CONFIG_BADCRES_9BIT_1S_84US (0x0<<7) +#define INA219_CONFIG_BADCRES_10BIT_1S_148US (0x1<<7) +#define INA219_CONFIG_BADCRES_11BIT_1S_276US (0x2<<7) +#define INA219_CONFIG_BADCRES_12BIT_1S_532US (0x3<<7) +#define INA219_CONFIG_BADCRES_12BIT_2S_1060US (0x9<<7) +#define INA219_CONFIG_BADCRES_12BIT_4S_2130US (0xA<<7) +#define INA219_CONFIG_BADCRES_12BIT_8S_4260US (0xB<<7) +#define INA219_CONFIG_BADCRES_12BIT_16S_8510US (0xC<<7) +#define INA219_CONFIG_BADCRES_12BIT_32S_17MS (0xD<<7) +#define INA219_CONFIG_BADCRES_12BIT_64S_34MS (0xE<<7) +#define INA219_CONFIG_BADCRES_12BIT_128S_69MS (0xF<<7) + +#define INA219_CONFIG_SADCRES_MASK (0x0078) +#define INA219_CONFIG_SADCRES_9BIT_1S_84US (0x0<<3) +#define INA219_CONFIG_SADCRES_10BIT_1S_148US (0x1<<3) +#define INA219_CONFIG_SADCRES_11BIT_1S_276US (0x2<<3) +#define INA219_CONFIG_SADCRES_12BIT_1S_532US (0x3<<3) +#define INA219_CONFIG_SADCRES_12BIT_2S_1060US (0x9<<3) +#define INA219_CONFIG_SADCRES_12BIT_4S_2130US (0xA<<3) +#define INA219_CONFIG_SADCRES_12BIT_8S_4260US (0xB<<3) +#define INA219_CONFIG_SADCRES_12BIT_16S_8510US (0xC<<3) +#define INA219_CONFIG_SADCRES_12BIT_32S_17MS (0xD<<3) +#define INA219_CONFIG_SADCRES_12BIT_64S_34MS (0xE<<3) +#define INA219_CONFIG_SADCRES_12BIT_128S_69MS (0xF<<3) + +#define INA219_CONFIG_MODE_MASK (0x0007) +#define INA219_CONFIG_MODE_POWERDOWN (0x0000) +#define INA219_CONFIG_MODE_SVOLT_TRIGGERED (0x0001) +#define INA219_CONFIG_MODE_BVOLT_TRIGGERED (0x0002) +#define INA219_CONFIG_MODE_SANDBVOLT_TRIGGERED (0x0003) +#define INA219_CONFIG_MODE_ADCOFF (0x0004) +#define INA219_CONFIG_MODE_SVOLT_CONTINUOUS (0x0005) +#define INA219_CONFIG_MODE_BVOLT_CONTINUOUS (0x0006) +#define INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS (0x0007) + +#define INA219_REG_SHUNTVOLTAGE (0x01) +#define INA219_REG_BUSVOLTAGE (0x02) +#define INA219_REG_POWER (0x03) +#define INA219_REG_CURRENT (0x04) +#define INA219_REG_CALIBRATION (0x05) + +#define INA219_DEFAULT_SHUNT_RESISTOR_MILLIOHMS (100.0) + +uint8_t ina219_type[4] = {0,0,0,0}; +uint8_t ina219_addresses[] = { INA219_ADDRESS1, INA219_ADDRESS2, INA219_ADDRESS3, INA219_ADDRESS4 }; + +#ifdef DEBUG_TASMOTA_SENSOR + +char __ina219_dbg1[10]; +char __ina219_dbg2[10]; +#endif + + + + +float ina219_current_multiplier; + +uint8_t ina219_valid[4] = {0,0,0,0}; +float ina219_voltage[4] = {0,0,0,0}; +float ina219_current[4] = {0,0,0,0}; +char ina219_types[] = "INA219"; +uint8_t ina219_count = 0; +# 132 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_13_ina219.ino" +bool Ina219SetCalibration(uint8_t mode, uint16_t addr) +{ + uint16_t config = 0; + + DEBUG_SENSOR_LOG("Ina219SetCalibration: mode=%d",mode); + if (mode < 5) + { + + ina219_current_multiplier = 1.0 / INA219_DEFAULT_SHUNT_RESISTOR_MILLIOHMS; + #ifdef DEBUG_TASMOTA_SENSOR + dtostrfd(ina219_current_multiplier,5,__ina219_dbg1); + DEBUG_SENSOR_LOG("Ina219SetCalibration: cur_mul=%s",__ina219_dbg1); + #endif + } + else if (mode >= 10) + { + int mult = mode % 10; + int shunt_milliOhms = mode / 10; + for ( ; mult > 0 ; mult-- ) + shunt_milliOhms *= 10; + ina219_current_multiplier = 1.0 / shunt_milliOhms; + #ifdef DEBUG_TASMOTA_SENSOR + dtostrfd(ina219_current_multiplier,5,__ina219_dbg1); + DEBUG_SENSOR_LOG("Ina219SetCalibration: shunt=%dmO => cur_mul=%s",shunt_milliOhms,__ina219_dbg1); + #endif + } + config = INA219_CONFIG_BVOLTAGERANGE_32V + | INA219_CONFIG_GAIN_8_320MV + | INA219_CONFIG_BADCRES_12BIT_16S_8510US + | INA219_CONFIG_SADCRES_12BIT_16S_8510US + | INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS; + + return I2cWrite16(addr, INA219_REG_CONFIG, config); +} + +float Ina219GetShuntVoltage_mV(uint16_t addr) +{ + + int16_t value = I2cReadS16(addr, INA219_REG_SHUNTVOLTAGE); + DEBUG_SENSOR_LOG("Ina219GetShuntVoltage_mV: ShReg = 0x%04X",value); + + return value * 0.01; +} + +float Ina219GetBusVoltage_V(uint16_t addr) +{ + + uint16_t value = I2cRead16(addr, INA219_REG_BUSVOLTAGE) >> 3; + DEBUG_SENSOR_LOG("Ina219GetBusVoltage_V: BusReg = 0x%04X",value); + + return value * 0.004; +} +# 201 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_13_ina219.ino" +bool Ina219Read(void) +{ + for (int i=0; i= 0) && (XdrvMailbox.payload <= 255)) { + Settings.ina219_mode = XdrvMailbox.payload; + restart_flag = 2; + } + Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_13, Settings.ina219_mode); + + return true; +} + + + +void Ina219Detect(void) +{ + for (uint32_t i = 0; i < sizeof(ina219_type); i++) { + uint16_t addr = ina219_addresses[i]; + if (I2cActive(addr)) { continue; } + if (Ina219SetCalibration(Settings.ina219_mode, addr)) { + I2cSetActiveFound(addr, ina219_types); + ina219_type[i] = 1; + ina219_count++; + } + } +} + +void Ina219EverySecond(void) +{ + + Ina219Read(); +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_INA219_DATA[] PROGMEM = + "{s}%s " D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" + "{s}%s " D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" + "{s}%s " D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"; +#endif + +void Ina219Show(bool json) +{ + int num_found=0; + for (int i=0; i1) + snprintf_P(name, sizeof(name), PSTR("%s%c%d"), ina219_types, IndexSeparator(), sensor_num); + else + snprintf_P(name, sizeof(name), PSTR("%s"), ina219_types); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"Id\":%02x,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s,\"" D_JSON_POWERUSAGE "\":%s}"), + name, ina219_addresses[i], voltage, current, power); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_VOLTAGE, voltage); + DomoticzSensor(DZ_CURRENT, current); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_INA219_DATA, name, voltage, name, current, name, power); +#endif + } + } +} + + + + + +bool Xsns13(uint8_t function) +{ + if (!I2cEnabled(XI2C_14)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Ina219Detect(); + } + else if (ina219_count) { + switch (function) { + case FUNC_COMMAND_SENSOR: + if (XSNS_13 == XdrvMailbox.index) { + result = Ina219CommandSensor(); + } + break; + case FUNC_EVERY_SECOND: + Ina219EverySecond(); + break; + case FUNC_JSON_APPEND: + Ina219Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Ina219Show(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_14_sht3x.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_14_sht3x.ino" +#ifdef USE_I2C +#ifdef USE_SHT3X + + + + + + +#define XSNS_14 14 +#define XI2C_15 15 + +#define SHT3X_ADDR_GND 0x44 +#define SHT3X_ADDR_VDD 0x45 +#define SHTC3_ADDR 0x70 + +#define SHT3X_MAX_SENSORS 3 + +const char kShtTypes[] PROGMEM = "SHT3X|SHT3X|SHTC3"; +uint8_t sht3x_addresses[] = { SHT3X_ADDR_GND, SHT3X_ADDR_VDD, SHTC3_ADDR }; + +uint8_t sht3x_count = 0; +struct SHT3XSTRUCT { + uint8_t address; + char types[6]; +} sht3x_sensors[SHT3X_MAX_SENSORS]; + +bool Sht3xRead(float &t, float &h, uint8_t sht3x_address) +{ + unsigned int data[6]; + + t = NAN; + h = NAN; + + Wire.beginTransmission(sht3x_address); + if (SHTC3_ADDR == sht3x_address) { + Wire.write(0x35); + Wire.write(0x17); + Wire.endTransmission(); + Wire.beginTransmission(sht3x_address); + Wire.write(0x78); + Wire.write(0x66); + } else { + Wire.write(0x2C); + Wire.write(0x06); + } + if (Wire.endTransmission() != 0) { + return false; + } + delay(30); + Wire.requestFrom(sht3x_address, (uint8_t)6); + for (uint32_t i = 0; i < 6; i++) { + data[i] = Wire.read(); + }; + t = ConvertTemp((float)((((data[0] << 8) | data[1]) * 175) / 65535.0) - 45); + h = ConvertHumidity((float)((((data[3] << 8) | data[4]) * 100) / 65535.0)); + return (!isnan(t) && !isnan(h) && (h != 0)); +} + + + +void Sht3xDetect(void) +{ + for (uint32_t i = 0; i < SHT3X_MAX_SENSORS; i++) { + if (I2cActive(sht3x_addresses[i])) { continue; } + float t; + float h; + if (Sht3xRead(t, h, sht3x_addresses[i])) { + sht3x_sensors[sht3x_count].address = sht3x_addresses[i]; + GetTextIndexed(sht3x_sensors[sht3x_count].types, sizeof(sht3x_sensors[sht3x_count].types), i, kShtTypes); + I2cSetActiveFound(sht3x_sensors[sht3x_count].address, sht3x_sensors[sht3x_count].types); + sht3x_count++; + } + } +} + +void Sht3xShow(bool json) +{ + for (uint32_t i = 0; i < sht3x_count; i++) { + float t; + float h; + if (Sht3xRead(t, h, sht3x_sensors[i].address)) { + char types[11]; + snprintf_P(types, sizeof(types), PSTR("%s%c0x%02X"), sht3x_sensors[i].types, IndexSeparator(), sht3x_sensors[i].address); + TempHumDewShow(json, ((0 == tele_period) && (0 == i)), types, t, h); + } + } +} + + + + + +bool Xsns14(uint8_t function) +{ + if (!I2cEnabled(XI2C_15)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Sht3xDetect(); + } + else if (sht3x_count) { + switch (function) { + case FUNC_JSON_APPEND: + Sht3xShow(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Sht3xShow(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_15_mhz19.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_15_mhz19.ino" +#ifdef USE_MHZ19 +# 33 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_15_mhz19.ino" +#define XSNS_15 15 + +enum MhzFilterOptions {MHZ19_FILTER_OFF, MHZ19_FILTER_OFF_ALLSAMPLES, MHZ19_FILTER_FAST, MHZ19_FILTER_MEDIUM, MHZ19_FILTER_SLOW}; + +#define MHZ19_FILTER_OPTION MHZ19_FILTER_FAST +# 58 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_15_mhz19.ino" +#include + +#ifndef CO2_LOW +#define CO2_LOW 800 +#endif +#ifndef CO2_HIGH +#define CO2_HIGH 1200 +#endif + +#define MHZ19_READ_TIMEOUT 400 +#define MHZ19_RETRY_COUNT 8 + +TasmotaSerial *MhzSerial; + +const char kMhzModels[] PROGMEM = "|B"; + +const char ABC_ENABLED[] = "ABC is Enabled"; +const char ABC_DISABLED[] = "ABC is Disabled"; + +enum MhzCommands { MHZ_CMND_READPPM, MHZ_CMND_ABCENABLE, MHZ_CMND_ABCDISABLE, MHZ_CMND_ZEROPOINT, MHZ_CMND_RESET, MHZ_CMND_RANGE_1000, MHZ_CMND_RANGE_2000, MHZ_CMND_RANGE_3000, MHZ_CMND_RANGE_5000 }; +const uint8_t kMhzCommands[][4] PROGMEM = { + + {0x86,0x00,0x00,0x00}, + {0x79,0xA0,0x00,0x00}, + {0x79,0x00,0x00,0x00}, + {0x87,0x00,0x00,0x00}, + {0x8D,0x00,0x00,0x00}, + {0x99,0x00,0x03,0xE8}, + {0x99,0x00,0x07,0xD0}, + {0x99,0x00,0x0B,0xB8}, + {0x99,0x00,0x13,0x88}}; + +uint8_t mhz_type = 1; +uint16_t mhz_last_ppm = 0; +uint8_t mhz_filter = MHZ19_FILTER_OPTION; +bool mhz_abc_must_apply = false; + +float mhz_temperature = 0; +uint8_t mhz_retry = MHZ19_RETRY_COUNT; +uint8_t mhz_received = 0; +uint8_t mhz_state = 0; + + + +uint8_t MhzCalculateChecksum(uint8_t *array) +{ + uint8_t checksum = 0; + for (uint32_t i = 1; i < 8; i++) { + checksum += array[i]; + } + checksum = 255 - checksum; + return (checksum +1); +} + +size_t MhzSendCmd(uint8_t command_id) +{ + uint8_t mhz_send[9] = { 0 }; + + mhz_send[0] = 0xFF; + mhz_send[1] = 0x01; + memcpy_P(&mhz_send[2], kMhzCommands[command_id], sizeof(uint16_t)); + + + + + memcpy_P(&mhz_send[6], kMhzCommands[command_id] + sizeof(uint16_t), sizeof(uint16_t)); + mhz_send[8] = MhzCalculateChecksum(mhz_send); + + + + return MhzSerial->write(mhz_send, sizeof(mhz_send)); +} + + + +bool MhzCheckAndApplyFilter(uint16_t ppm, uint8_t s) +{ + if (1 == s) { + return false; + } + if (mhz_last_ppm < 400 || mhz_last_ppm > 5000) { + + + mhz_last_ppm = ppm; + return true; + } + int32_t difference = ppm - mhz_last_ppm; + if (s > 0 && s < 64 && mhz_filter != MHZ19_FILTER_OFF) { +# 154 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_15_mhz19.ino" + difference *= s; + difference /= 64; + } + if (MHZ19_FILTER_OFF == mhz_filter) { + if (s != 0 && s != 64) { + return false; + } + } else { + difference >>= (mhz_filter -1); + } + mhz_last_ppm = static_cast(mhz_last_ppm + difference); + return true; +} + +void MhzEverySecond(void) +{ + mhz_state++; + if (8 == mhz_state) { + mhz_state = 0; + + if (mhz_retry) { + mhz_retry--; + if (!mhz_retry) { + mhz_last_ppm = 0; + mhz_temperature = 0; + } + } + + MhzSerial->flush(); + MhzSendCmd(MHZ_CMND_READPPM); + mhz_received = 0; + } + + if ((mhz_state > 2) && !mhz_received) { + uint8_t mhz_response[9]; + + unsigned long start = millis(); + uint8_t counter = 0; + while (((millis() - start) < MHZ19_READ_TIMEOUT) && (counter < 9)) { + if (MhzSerial->available() > 0) { + mhz_response[counter++] = MhzSerial->read(); + } else { + delay(5); + } + } + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, mhz_response, counter); + + if (counter < 9) { + + return; + } + + uint8_t crc = MhzCalculateChecksum(mhz_response); + if (mhz_response[8] != crc) { + + return; + } + if (0xFF != mhz_response[0] || 0x86 != mhz_response[1]) { + + return; + } + + mhz_received = 1; + + uint16_t u = (mhz_response[6] << 8) | mhz_response[7]; + if (15000 == u) { + if (Settings.SensorBits1.mhz19b_abc_disable) { + + + mhz_abc_must_apply = true; + } + } else { + uint16_t ppm = (mhz_response[2] << 8) | mhz_response[3]; + mhz_temperature = ConvertTemp((float)mhz_response[4] - 40); + uint8_t s = mhz_response[5]; + mhz_type = (s) ? 1 : 2; + if (MhzCheckAndApplyFilter(ppm, s)) { + mhz_retry = MHZ19_RETRY_COUNT; +#ifdef USE_LIGHT + LightSetSignal(CO2_LOW, CO2_HIGH, mhz_last_ppm); +#endif + + if (0 == s || 64 == s) { + if (mhz_abc_must_apply) { + mhz_abc_must_apply = false; + if (!Settings.SensorBits1.mhz19b_abc_disable) { + MhzSendCmd(MHZ_CMND_ABCENABLE); + } else { + MhzSendCmd(MHZ_CMND_ABCDISABLE); + } + } + } + + } + } + + } +} +# 268 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_15_mhz19.ino" +#define D_JSON_RANGE_1000 "1000 ppm range" +#define D_JSON_RANGE_2000 "2000 ppm range" +#define D_JSON_RANGE_3000 "3000 ppm range" +#define D_JSON_RANGE_5000 "5000 ppm range" + +bool MhzCommandSensor(void) +{ + bool serviced = true; + + switch (XdrvMailbox.payload) { + case 0: + Settings.SensorBits1.mhz19b_abc_disable = true; + MhzSendCmd(MHZ_CMND_ABCDISABLE); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_DISABLED); + break; + case 1: + Settings.SensorBits1.mhz19b_abc_disable = false; + MhzSendCmd(MHZ_CMND_ABCENABLE); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_ENABLED); + break; + case 2: + MhzSendCmd(MHZ_CMND_ZEROPOINT); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_ZERO_POINT_CALIBRATION); + break; + case 9: + MhzSendCmd(MHZ_CMND_RESET); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RESET); + break; + case 1000: + MhzSendCmd(MHZ_CMND_RANGE_1000); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_1000); + break; + case 2000: + MhzSendCmd(MHZ_CMND_RANGE_2000); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_2000); + break; + case 3000: + MhzSendCmd(MHZ_CMND_RANGE_3000); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_3000); + break; + case 5000: + MhzSendCmd(MHZ_CMND_RANGE_5000); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_5000); + break; + default: + if (!Settings.SensorBits1.mhz19b_abc_disable) { + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_ENABLED); + } else { + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_DISABLED); + } + } + + return serviced; +} + + + +void MhzInit(void) +{ + mhz_type = 0; + if ((pin[GPIO_MHZ_RXD] < 99) && (pin[GPIO_MHZ_TXD] < 99)) { + MhzSerial = new TasmotaSerial(pin[GPIO_MHZ_RXD], pin[GPIO_MHZ_TXD], 1); + if (MhzSerial->begin(9600)) { + if (MhzSerial->hardwareSerial()) { ClaimSerial(); } + mhz_type = 1; + } + + } +} + +void MhzShow(bool json) +{ + char types[7] = "MHZ19B"; + char temperature[33]; + dtostrfd(mhz_temperature, Settings.flag2.temperature_resolution, temperature); + char model[3]; + GetTextIndexed(model, sizeof(model), mhz_type -1, kMhzModels); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_MODEL "\":\"%s\",\"" D_JSON_CO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s}"), types, model, mhz_last_ppm, temperature); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_AIRQUALITY, mhz_last_ppm); + DomoticzSensor(DZ_TEMP, temperature); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_CO2, types, mhz_last_ppm); + WSContentSend_PD(HTTP_SNS_TEMP, types, temperature, TempUnit()); +#endif + } +} + + + + + +bool Xsns15(uint8_t function) +{ + bool result = false; + + if (mhz_type) { + switch (function) { + case FUNC_INIT: + MhzInit(); + break; + case FUNC_EVERY_SECOND: + MhzEverySecond(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_15 == XdrvMailbox.index) { + result = MhzCommandSensor(); + } + break; + case FUNC_JSON_APPEND: + MhzShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MhzShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_16_tsl2561.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_16_tsl2561.ino" +#ifdef USE_I2C +#ifdef USE_TSL2561 +# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_16_tsl2561.ino" +#define XSNS_16 16 +#define XI2C_16 16 + +#include + +Tsl2561 Tsl(Wire); + +uint8_t tsl2561_type = 0; +uint8_t tsl2561_valid = 0; +uint32_t tsl2561_milliLux = 0; +char tsl2561_types[] = "TSL2561"; + +bool Tsl2561Read(void) +{ + if (tsl2561_valid) { tsl2561_valid--; } + + uint8_t id; + bool gain; + Tsl2561::exposure_t exposure; + uint16_t scaledFull, scaledIr; + uint32_t full, ir; + + if (Tsl.on()) { + if (Tsl.id(id) + && Tsl2561Util::autoGain(Tsl, gain, exposure, scaledFull, scaledIr) + && Tsl2561Util::normalizedLuminosity(gain, exposure, full = scaledFull, ir = scaledIr) + && Tsl2561Util::milliLux(full, ir, tsl2561_milliLux, Tsl2561::packageCS(id))) { + } else{ + tsl2561_milliLux = 0; + } + } + tsl2561_valid = SENSOR_MAX_MISS; + return true; +} + +void Tsl2561Detect(void) +{ + if (I2cSetDevice(0x29) || I2cSetDevice(0x39) || I2cSetDevice(0x49)) { + uint8_t id; + Tsl.begin(); + if (!Tsl.id(id)) return; + if (Tsl.on()) { + tsl2561_type = 1; + I2cSetActiveFound(Tsl.address(), tsl2561_types); + } + } +} + +void Tsl2561EverySecond(void) +{ + if (!(uptime %2)) { + + if (!Tsl2561Read()) { + AddLogMissed(tsl2561_types, tsl2561_valid); + } + } +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_TSL2561[] PROGMEM = + "{s}TSL2561 " D_ILLUMINANCE "{m}%u.%03u " D_UNIT_LUX "{e}"; +#endif + +void Tsl2561Show(bool json) +{ + if (tsl2561_valid) { + if (json) { + ResponseAppend_P(PSTR(",\"TSL2561\":{\"" D_JSON_ILLUMINANCE "\":%u.%03u}"), + tsl2561_milliLux / 1000, tsl2561_milliLux % 1000); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, (tsl2561_milliLux + 500) / 1000); } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TSL2561, tsl2561_milliLux / 1000, tsl2561_milliLux % 1000); +#endif + } + } +} + + + + + +bool Xsns16(uint8_t function) +{ + if (!I2cEnabled(XI2C_16)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Tsl2561Detect(); + } + else if (tsl2561_type) { + switch (function) { + case FUNC_EVERY_SECOND: + Tsl2561EverySecond(); + break; + case FUNC_JSON_APPEND: + Tsl2561Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Tsl2561Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_17_senseair.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_17_senseair.ino" +#ifdef USE_SENSEAIR +# 29 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_17_senseair.ino" +#define XSNS_17 17 + +#define SENSEAIR_MODBUS_SPEED 9600 +#define SENSEAIR_DEVICE_ADDRESS 0xFE +#define SENSEAIR_READ_REGISTER 0x04 + +#ifndef CO2_LOW +#define CO2_LOW 800 +#endif +#ifndef CO2_HIGH +#define CO2_HIGH 1200 +#endif + +#include +TasmotaModbus *SenseairModbus; + +const char kSenseairTypes[] PROGMEM = "Kx0|S8"; + +uint8_t senseair_type = 1; +char senseair_types[7]; + +uint16_t senseair_co2 = 0; +float senseair_temperature = 0; +float senseair_humidity = 0; + + + +const uint8_t start_addresses[] { 0x1A, 0x00, 0x03, 0x04, 0x05, 0x1C, 0x0A }; + +uint8_t senseair_read_state = 0; +uint8_t senseair_send_retry = 0; + +void Senseair250ms(void) +{ + + + + + uint16_t value = 0; + bool data_ready = SenseairModbus->ReceiveReady(); + + if (data_ready) { + uint8_t error = SenseairModbus->Receive16BitRegister(&value); + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir response error %d"), error); + } else { + switch(senseair_read_state) { + case 0: + senseair_type = 2; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir type id low %04X"), value); + break; + case 1: + if (value) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir error %04X"), value); + } + break; + case 2: + senseair_co2 = value; +#ifdef USE_LIGHT + LightSetSignal(CO2_LOW, CO2_HIGH, senseair_co2); +#endif + break; + case 3: + senseair_temperature = ConvertTemp((float)value / 100); + break; + case 4: + senseair_humidity = ConvertHumidity((float)value / 100); + break; + case 5: + { + bool relay_state = value >> 8 & 1; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir relay state %d"), relay_state); + break; + } + case 6: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir temp adjustment %d"), value); + break; + } + } + senseair_read_state++; + if (2 == senseair_type) { + if (3 == senseair_read_state) { + senseair_read_state = 1; + } + } else { + if (sizeof(start_addresses) == senseair_read_state) { + senseair_read_state = 1; + } + } + } + + if (0 == senseair_send_retry || data_ready) { + senseair_send_retry = 5; + SenseairModbus->Send(SENSEAIR_DEVICE_ADDRESS, SENSEAIR_READ_REGISTER, (uint16_t)start_addresses[senseair_read_state], 1); + } else { + senseair_send_retry--; + } + + +} + + + +void SenseairInit(void) +{ + senseair_type = 0; + if ((pin[GPIO_SAIR_RX] < 99) && (pin[GPIO_SAIR_TX] < 99)) { + SenseairModbus = new TasmotaModbus(pin[GPIO_SAIR_RX], pin[GPIO_SAIR_TX]); + uint8_t result = SenseairModbus->Begin(SENSEAIR_MODBUS_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + senseair_type = 1; + } + } +} + +void SenseairShow(bool json) +{ + GetTextIndexed(senseair_types, sizeof(senseair_types), senseair_type -1, kSenseairTypes); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_CO2 "\":%d"), senseair_types, senseair_co2); + if (senseair_type != 2) { + ResponseAppend_P(PSTR(",")); + ResponseAppendTHD(senseair_temperature, senseair_humidity); + } + ResponseJsonEnd(); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_AIRQUALITY, senseair_co2); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_CO2, senseair_types, senseair_co2); + if (senseair_type != 2) { + WSContentSend_THD(senseair_types, senseair_temperature, senseair_humidity); + } +#endif + } +} + + + + + +bool Xsns17(uint8_t function) +{ + bool result = false; + + if (senseair_type) { + switch (function) { + case FUNC_INIT: + SenseairInit(); + break; + case FUNC_EVERY_250_MSECOND: + Senseair250ms(); + break; + case FUNC_JSON_APPEND: + SenseairShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + SenseairShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_18_pms5003.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_18_pms5003.ino" +#ifdef USE_PMS5003 +# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_18_pms5003.ino" +#define XSNS_18 18 + +#include + +#ifndef WARMUP_PERIOD +#define WARMUP_PERIOD 30 +#endif + +#ifndef MIN_INTERVAL_PERIOD +#define MIN_INTERVAL_PERIOD 60 +#endif + +TasmotaSerial *PmsSerial; + +struct PMS5003 { + uint16_t time = 0; + uint8_t type = 1; + uint8_t valid = 0; + uint8_t wake_mode = 1; + uint8_t ready = 1; +} Pms; + +enum PmsCommands +{ + CMD_MODE_ACTIVE, + CMD_SLEEP, + CMD_WAKEUP, + CMD_MODE_PASSIVE, + CMD_READ_DATA +}; + +const uint8_t kPmsCommands[][7] PROGMEM = { + + {0x42, 0x4D, 0xE1, 0x00, 0x01, 0x01, 0x71}, + {0x42, 0x4D, 0xE4, 0x00, 0x00, 0x01, 0x73}, + {0x42, 0x4D, 0xE4, 0x00, 0x01, 0x01, 0x74}, + {0x42, 0x4D, 0xE1, 0x00, 0x00, 0x01, 0x70}, + {0x42, 0x4D, 0xE2, 0x00, 0x00, 0x01, 0x71}}; + +struct pmsX003data { + uint16_t framelen; + uint16_t pm10_standard, pm25_standard, pm100_standard; + uint16_t pm10_env, pm25_env, pm100_env; +#ifdef PMS_MODEL_PMS3003 + uint16_t reserved1, reserved2, reserved3; +#else + uint16_t particles_03um, particles_05um, particles_10um, particles_25um, particles_50um, particles_100um; + uint16_t unused; +#endif + uint16_t checksum; +} pms_data; + + + +size_t PmsSendCmd(uint8_t command_id) +{ + return PmsSerial->write(kPmsCommands[command_id], sizeof(kPmsCommands[command_id])); +} + + + +bool PmsReadData(void) +{ + if (! PmsSerial->available()) { + return false; + } + while ((PmsSerial->peek() != 0x42) && PmsSerial->available()) { + PmsSerial->read(); + } +#ifdef PMS_MODEL_PMS3003 + if (PmsSerial->available() < 22) { +#else + if (PmsSerial->available() < 32) { +#endif + return false; + } + +#ifdef PMS_MODEL_PMS3003 + uint8_t buffer[22]; + PmsSerial->readBytes(buffer, 22); +#else + uint8_t buffer[32]; + PmsSerial->readBytes(buffer, 32); +#endif + uint16_t sum = 0; + PmsSerial->flush(); + +#ifdef PMS_MODEL_PMS3003 + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 22); +#else + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 32); +#endif + + +#ifdef PMS_MODEL_PMS3003 + for (uint32_t i = 0; i < 20; i++) { +#else + for (uint32_t i = 0; i < 30; i++) { +#endif + sum += buffer[i]; + } + +#ifdef PMS_MODEL_PMS3003 + uint16_t buffer_u16[10]; + for (uint32_t i = 0; i < 10; i++) { +#else + uint16_t buffer_u16[15]; + for (uint32_t i = 0; i < 15; i++) { +#endif + buffer_u16[i] = buffer[2 + i*2 + 1]; + buffer_u16[i] += (buffer[2 + i*2] << 8); + } +#ifdef PMS_MODEL_PMS3003 + if (sum != buffer_u16[9]) { +#else + if (sum != buffer_u16[14]) { +#endif + AddLog_P(LOG_LEVEL_DEBUG, PSTR("PMS: " D_CHECKSUM_FAILURE)); + return false; + } + +#ifdef PMS_MODEL_PMS3003 + memcpy((void *)&pms_data, (void *)buffer_u16, 20); +#else + memcpy((void *)&pms_data, (void *)buffer_u16, 30); +#endif + Pms.valid = 10; + + return true; +} +# 172 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_18_pms5003.ino" +bool PmsCommandSensor(void) +{ + if ((pin[GPIO_PMS5003_TX] < 99) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { + if (XdrvMailbox.payload < MIN_INTERVAL_PERIOD) { + + Settings.pms_wake_interval = 0; + Pms.wake_mode = 1; + Pms.ready = 1; + PmsSendCmd(CMD_MODE_ACTIVE); + PmsSendCmd(CMD_WAKEUP); + } else { + + Settings.pms_wake_interval = XdrvMailbox.payload; + PmsSendCmd(CMD_MODE_PASSIVE); + PmsSendCmd(CMD_SLEEP); + Pms.wake_mode = 0; + Pms.ready = 0; + } + } + + Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_18, Settings.pms_wake_interval); + + return true; +} + + + +void PmsSecond(void) +{ + if (Settings.pms_wake_interval >= MIN_INTERVAL_PERIOD) { + + Pms.time++; + if ((Settings.pms_wake_interval - Pms.time <= WARMUP_PERIOD) && !Pms.wake_mode) { + + Pms.wake_mode = 1; + PmsSendCmd(CMD_WAKEUP); + } + if (Pms.time >= Settings.pms_wake_interval) { + + PmsSendCmd(CMD_READ_DATA); + Pms.ready = 1; + Pms.time = 0; + } + } + + if (Pms.ready) { + if (PmsReadData()) { + Pms.valid = 10; + if (Settings.pms_wake_interval >= MIN_INTERVAL_PERIOD) { + PmsSendCmd(CMD_SLEEP); + Pms.wake_mode = 0; + Pms.ready = 0; + } + } else { + if (Pms.valid) { + Pms.valid--; + if (Settings.pms_wake_interval >= MIN_INTERVAL_PERIOD) { + PmsSendCmd(CMD_READ_DATA); + Pms.ready = 1; + } + } + } + } +} + + + +void PmsInit(void) +{ + Pms.type = 0; + if (pin[GPIO_PMS5003_RX] < 99) { + PmsSerial = new TasmotaSerial(pin[GPIO_PMS5003_RX], (pin[GPIO_PMS5003_TX] < 99) ? pin[GPIO_PMS5003_TX] : -1, 1); + if (PmsSerial->begin(9600)) { + if (PmsSerial->hardwareSerial()) { ClaimSerial(); } + + if (99 == pin[GPIO_PMS5003_TX]) { + Settings.pms_wake_interval = 0; + Pms.ready = 1; + } + + Pms.type = 1; + } + } +} + +#ifdef USE_WEBSERVER +#ifdef PMS_MODEL_PMS3003 +const char HTTP_PMS3003_SNS[] PROGMEM = + + + + "{s}PMS3003 " D_ENVIRONMENTAL_CONCENTRATION " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS3003 " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS3003 " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"; +#else +const char HTTP_PMS5003_SNS[] PROGMEM = + + + + "{s}PMS5003 " D_ENVIRONMENTAL_CONCENTRATION " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS5003 " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS5003 " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 0.3 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 0.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}"; +#endif +#endif + +void PmsShow(bool json) +{ + if (Pms.valid) { + if (json) { +#ifdef PMS_MODEL_PMS3003 + ResponseAppend_P(PSTR(",\"PMS3003\":{\"CF1\":%d,\"CF2.5\":%d,\"CF10\":%d,\"PM1\":%d,\"PM2.5\":%d,\"PM10\":%d}"), + pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard, + pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env); +#else + ResponseAppend_P(PSTR(",\"PMS5003\":{\"CF1\":%d,\"CF2.5\":%d,\"CF10\":%d,\"PM1\":%d,\"PM2.5\":%d,\"PM10\":%d,\"PB0.3\":%d,\"PB0.5\":%d,\"PB1\":%d,\"PB2.5\":%d,\"PB5\":%d,\"PB10\":%d}"), + pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard, + pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env, + pms_data.particles_03um, pms_data.particles_05um, pms_data.particles_10um, pms_data.particles_25um, pms_data.particles_50um, pms_data.particles_100um); +#endif +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_COUNT, pms_data.pm10_env); + DomoticzSensor(DZ_VOLTAGE, pms_data.pm25_env); + DomoticzSensor(DZ_CURRENT, pms_data.pm100_env); + } +#endif +#ifdef USE_WEBSERVER + } else { + +#ifdef PMS_MODEL_PMS3003 + WSContentSend_PD(HTTP_PMS3003_SNS, + + pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env); +#else + WSContentSend_PD(HTTP_PMS5003_SNS, + + pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env, + pms_data.particles_03um, pms_data.particles_05um, pms_data.particles_10um, pms_data.particles_25um, pms_data.particles_50um, pms_data.particles_100um); +#endif +#endif + } + } +} + + + + + +bool Xsns18(uint8_t function) +{ + bool result = false; + + if (Pms.type) { + switch (function) { + case FUNC_INIT: + PmsInit(); + break; + case FUNC_EVERY_SECOND: + PmsSecond(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_18 == XdrvMailbox.index) { + result = PmsCommandSensor(); + } + break; + case FUNC_JSON_APPEND: + PmsShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + PmsShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_19_mgs.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_19_mgs.ino" +#ifdef USE_I2C +#ifdef USE_MGS + + + + + + + +#define XSNS_19 19 +#define XI2C_17 17 + +#ifndef MGS_SENSOR_ADDR +#define MGS_SENSOR_ADDR 0x04 +#endif + +#include "MutichannelGasSensor.h" + +bool mgs_detected = false; + +void MGSInit(void) { + gas.begin(MGS_SENSOR_ADDR); +} + +void MGSPrepare(void) +{ + if (I2cActive(MGS_SENSOR_ADDR)) { return; } + + gas.begin(MGS_SENSOR_ADDR); + if (!gas.isError()) { + I2cSetActiveFound(MGS_SENSOR_ADDR, "MultiGas"); + mgs_detected = true; + } +} + +char* measure_gas(int gas_type, char* buffer) +{ + float f = gas.calcGas(gas_type); + dtostrfd(f, 2, buffer); + return buffer; +} + +#ifdef USE_WEBSERVER +const char HTTP_MGS_GAS[] PROGMEM = "{s}MGS %s{m}%s " D_UNIT_PARTS_PER_MILLION "{e}"; +#endif + +void MGSShow(bool json) +{ + char buffer[33]; + if (json) { + ResponseAppend_P(PSTR(",\"MGS\":{\"NH3\":%s"), measure_gas(NH3, buffer)); + ResponseAppend_P(PSTR(",\"CO\":%s"), measure_gas(CO, buffer)); + ResponseAppend_P(PSTR(",\"NO2\":%s"), measure_gas(NO2, buffer)); + ResponseAppend_P(PSTR(",\"C3H8\":%s"), measure_gas(C3H8, buffer)); + ResponseAppend_P(PSTR(",\"C4H10\":%s"), measure_gas(C4H10, buffer)); + ResponseAppend_P(PSTR(",\"CH4\":%s"), measure_gas(GAS_CH4, buffer)); + ResponseAppend_P(PSTR(",\"H2\":%s"), measure_gas(H2, buffer)); + ResponseAppend_P(PSTR(",\"C2H5OH\":%s}"), measure_gas(C2H5OH, buffer)); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_MGS_GAS, "NH3", measure_gas(NH3, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "CO", measure_gas(CO, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "NO2", measure_gas(NO2, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "C3H8", measure_gas(C3H8, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "C4H10", measure_gas(C4H10, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "CH4", measure_gas(GAS_CH4, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "H2", measure_gas(H2, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "C2H5OH", measure_gas(C2H5OH, buffer)); +#endif + } +} + + + + + +bool Xsns19(uint8_t function) +{ + if (!I2cEnabled(XI2C_17)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + MGSPrepare(); + } + else if (mgs_detected) { + switch (function) { + case FUNC_JSON_APPEND: + MGSShow(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MGSShow(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_20_novasds.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_20_novasds.ino" +#ifdef USE_NOVA_SDS +# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_20_novasds.ino" +#define XSNS_20 20 + +#include + +#ifndef STARTING_OFFSET +#define STARTING_OFFSET 30 +#endif +#if STARTING_OFFSET < 10 +#error "Please set STARTING_OFFSET >= 10" +#endif +#ifndef NOVA_SDS_RECDATA_TIMEOUT +#define NOVA_SDS_RECDATA_TIMEOUT 150 +#endif +#ifndef NOVA_SDS_DEVICE_ID +#define NOVA_SDS_DEVICE_ID 0xFFFF +#endif + +TasmotaSerial *NovaSdsSerial; + +uint8_t novasds_type = 1; +uint8_t novasds_valid = 0; +uint8_t cont_mode = 1; + +struct sds011data { + uint16_t pm100; + uint16_t pm25; +} novasds_data; +uint16_t pm100_sum; +uint16_t pm25_sum; + + +#define NOVA_SDS_REPORTING_MODE 2 +#define NOVA_SDS_QUERY_DATA 4 +#define NOVA_SDS_SET_DEVICE_ID 5 +#define NOVA_SDS_SLEEP_AND_WORK 6 +#define NOVA_SDS_WORKING_PERIOD 8 +#define NOVA_SDS_CHECK_FIRMWARE_VER 7 + #define NOVA_SDS_QUERY_MODE 0 + #define NOVA_SDS_SET_MODE 1 + #define NOVA_SDS_REPORT_ACTIVE 0 + #define NOVA_SDS_REPORT_QUERY 1 + #define NOVA_SDS_SLEEP 0 + #define NOVA_SDS_WORK 1 + + +bool NovaSdsCommand(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint16_t sensorid, uint8_t *buffer) +{ + uint8_t novasds_cmnd[19] = {0xAA, 0xB4, byte1, byte2, byte3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (uint8_t)(sensorid & 0xFF), (uint8_t)((sensorid>>8) & 0xFF), 0x00, 0xAB}; + + + for (uint32_t i = 2; i < 17; i++) { + novasds_cmnd[17] += novasds_cmnd[i]; + } + + + + + + NovaSdsSerial->write(novasds_cmnd, sizeof(novasds_cmnd)); + NovaSdsSerial->flush(); + + + unsigned long cmndtime = millis(); + while ( (TimePassedSince(cmndtime) < NOVA_SDS_RECDATA_TIMEOUT) && ( ! NovaSdsSerial->available() ) ); + if ( ! NovaSdsSerial->available() ) { + + return false; + } + uint8_t recbuf[10]; + memset(recbuf, 0, sizeof(recbuf)); + + while ( (TimePassedSince(cmndtime) < NOVA_SDS_RECDATA_TIMEOUT) && ( NovaSdsSerial->available() > 0) && (0xAA != (recbuf[0] = NovaSdsSerial->read())) ); + if ( 0xAA != recbuf[0] ) { + + return false; + } + + + NovaSdsSerial->readBytes(&recbuf[1], 9); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, recbuf, sizeof(recbuf)); + + if ( nullptr != buffer ) { + + memcpy(buffer, recbuf, sizeof(recbuf)); + } + + + if ((0xAB != recbuf[9] ) || (recbuf[8] != ((recbuf[2] + recbuf[3] + recbuf[4] + recbuf[5] + recbuf[6] + recbuf[7]) & 0xFF))) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("SDS: " D_CHECKSUM_FAILURE)); + return false; + } + + return true; +} + +void NovaSdsSetWorkPeriod(void) +{ + + NovaSdsCommand(NOVA_SDS_WORKING_PERIOD, NOVA_SDS_SET_MODE, 0, NOVA_SDS_DEVICE_ID, nullptr); + + NovaSdsCommand(NOVA_SDS_REPORTING_MODE, NOVA_SDS_SET_MODE, NOVA_SDS_REPORT_QUERY, NOVA_SDS_DEVICE_ID, nullptr); +} + +bool NovaSdsReadData(void) +{ + uint8_t d[10]; + if ( ! NovaSdsCommand(NOVA_SDS_QUERY_DATA, 0, 0, NOVA_SDS_DEVICE_ID, d) ) { + return false; + } + novasds_data.pm25 = (d[2] + 256 * d[3]); + novasds_data.pm100 = (d[4] + 256 * d[5]); + + return true; +} + + + +void NovaSdsSecond(void) +{ + if (!novasds_valid) + { + NovaSdsSetWorkPeriod(); + novasds_valid=1; + } + if((Settings.tele_period - Settings.novasds_startingoffset <= 0)) + { + if(!cont_mode) + { + cont_mode = 1; + NovaSdsCommand(NOVA_SDS_SLEEP_AND_WORK, NOVA_SDS_SET_MODE, NOVA_SDS_WORK, NOVA_SDS_DEVICE_ID, nullptr); + } + } + else + cont_mode = 0; + + if(tele_period == Settings.tele_period - Settings.novasds_startingoffset && !cont_mode) + { + NovaSdsCommand(NOVA_SDS_SLEEP_AND_WORK, NOVA_SDS_SET_MODE, NOVA_SDS_WORK, NOVA_SDS_DEVICE_ID, nullptr); + } + if(tele_period >= Settings.tele_period-5 && tele_period <= Settings.tele_period-2) + { + if(!(NovaSdsReadData())) novasds_valid=0; + pm100_sum += novasds_data.pm100; + pm25_sum += novasds_data.pm25; + } + if(tele_period == Settings.tele_period-1) + { + novasds_data.pm100 = pm100_sum >> 2; + novasds_data.pm25 = pm25_sum >> 2; + if(!cont_mode) + NovaSdsCommand(NOVA_SDS_SLEEP_AND_WORK, NOVA_SDS_SET_MODE, NOVA_SDS_SLEEP, NOVA_SDS_DEVICE_ID, nullptr); + pm100_sum = pm25_sum = 0; + } +} + + + + + + + +bool NovaSdsCommandSensor(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 256)) { + if( XdrvMailbox.payload < 10 ) Settings.novasds_startingoffset = 10; + else Settings.novasds_startingoffset = XdrvMailbox.payload; + } + Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_20, Settings.novasds_startingoffset); + + return true; +} + +void NovaSdsInit(void) +{ + novasds_type = 0; + if (pin[GPIO_SDS0X1_RX] < 99 && pin[GPIO_SDS0X1_TX] < 99) { + NovaSdsSerial = new TasmotaSerial(pin[GPIO_SDS0X1_RX], pin[GPIO_SDS0X1_TX], 1); + if (NovaSdsSerial->begin(9600)) { + if (NovaSdsSerial->hardwareSerial()) { + ClaimSerial(); + } + novasds_type = 1; + NovaSdsSetWorkPeriod(); + } + } +} + +#ifdef USE_WEBSERVER +const char HTTP_SDS0X1_SNS[] PROGMEM = + "{s}SDS0X1 " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}SDS0X1 " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"; +#endif + +void NovaSdsShow(bool json) +{ + if (novasds_valid) { + float pm10f = (float)(novasds_data.pm100) / 10.0f; + float pm2_5f = (float)(novasds_data.pm25) / 10.0f; + char pm10[33]; + dtostrfd(pm10f, 1, pm10); + char pm2_5[33]; + dtostrfd(pm2_5f, 1, pm2_5); + if (json) { + ResponseAppend_P(PSTR(",\"SDS0X1\":{\"PM2.5\":%s,\"PM10\":%s}"), pm2_5, pm10); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_VOLTAGE, pm2_5); + DomoticzSensor(DZ_CURRENT, pm10); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SDS0X1_SNS, pm2_5, pm10); +#endif + } + } +} + + + + + +bool Xsns20(uint8_t function) +{ + bool result = false; + + if (novasds_type) { + switch (function) { + case FUNC_INIT: + NovaSdsInit(); + break; + case FUNC_EVERY_SECOND: + NovaSdsSecond(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_20 == XdrvMailbox.index) { + result = NovaSdsCommandSensor(); + } + break; + case FUNC_JSON_APPEND: + NovaSdsShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + NovaSdsShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_21_sgp30.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_21_sgp30.ino" +#ifdef USE_I2C +#ifdef USE_SGP30 +# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_21_sgp30.ino" +#define XSNS_21 21 +#define XI2C_18 18 + +#define SGP30_ADDRESS 0x58 + +#include "Adafruit_SGP30.h" +Adafruit_SGP30 sgp; + +bool sgp30_type = false; +bool sgp30_ready = false; +float sgp30_abshum; + + + +void sgp30_Init(void) +{ + if (I2cActive(SGP30_ADDRESS)) { return; } + + if (sgp.begin()) { + sgp30_type = true; + + I2cSetActiveFound(SGP30_ADDRESS, "SGP30"); + } +} + + +#define POW_FUNC FastPrecisePow + +float sgp30_AbsoluteHumidity(float temperature, float humidity,char tempUnit) { + + + + + + float temp = NAN; + const float mw = 18.01534; + const float r = 8.31447215; + + if (isnan(temperature) || isnan(humidity) ) { + return NAN; + } + + if (tempUnit != 'C') { + temperature = (temperature - 32.0) * (5.0 / 9.0); + } + + temp = POW_FUNC(2.718281828, (17.67 * temperature) / (temperature + 243.5)); + + + return (6.112 * temp * humidity * mw) / ((273.15 + temperature) * r); +} + +#define SAVE_PERIOD 30 + +void Sgp30Update(void) +{ + sgp30_ready = false; + if (!sgp.IAQmeasure()) { + return; + } + if (global_update && (global_humidity > 0) && (global_temperature != 9999)) { + + sgp30_abshum=sgp30_AbsoluteHumidity(global_temperature,global_humidity,TempUnit()); + sgp.setHumidity(sgp30_abshum*1000); + } + sgp30_ready = true; + + + if (!(uptime%SAVE_PERIOD)) { + + uint16_t TVOC_base; + uint16_t eCO2_base; + + if (!sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) return; + + } +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_SGP30[] PROGMEM = + "{s}SGP30 " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}" + "{s}SGP30 " D_TVOC "{m}%d " D_UNIT_PARTS_PER_BILLION "{e}"; +const char HTTP_SNS_AHUM[] PROGMEM = "{s}SGP30 Abs Humidity{m}%s g/m3{e}"; +#endif + +#define D_JSON_AHUM "aHumidity" + +void Sgp30Show(bool json) +{ + if (sgp30_ready) { + char abs_hum[33]; + + if (json) { + ResponseAppend_P(PSTR(",\"SGP30\":{\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TVOC "\":%d"), sgp.eCO2, sgp.TVOC); + if (global_update && global_humidity>0 && global_temperature!=9999) { + + dtostrfd(sgp30_abshum,4,abs_hum); + ResponseAppend_P(PSTR(",\"" D_JSON_AHUM "\":%s"),abs_hum); + } + ResponseJsonEnd(); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, sgp.eCO2); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_SGP30, sgp.eCO2, sgp.TVOC); + if (global_update) { + WSContentSend_PD(HTTP_SNS_AHUM, abs_hum); + } +#endif + } + } +} + + + + + +bool Xsns21(uint8_t function) +{ + if (!I2cEnabled(XI2C_18)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + sgp30_Init(); + } + else if (sgp30_type) { + switch (function) { + case FUNC_EVERY_SECOND: + Sgp30Update(); + break; + case FUNC_JSON_APPEND: + Sgp30Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Sgp30Show(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_22_sr04.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_22_sr04.ino" +#ifdef USE_SR04 + +#include +#include +# 32 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_22_sr04.ino" +#define XSNS_22 22 + +uint8_t sr04_type = 1; +real64_t distance; + +NewPing* sonar = nullptr; +TasmotaSerial* sonar_serial = nullptr; + +uint8_t Sr04TModeDetect(void) +{ + sr04_type = 0; + if (99 == pin[GPIO_SR04_ECHO]) { return sr04_type; } + + int sr04_echo_pin = pin[GPIO_SR04_ECHO]; + int sr04_trig_pin = (pin[GPIO_SR04_TRIG] < 99) ? pin[GPIO_SR04_TRIG] : -1; + sonar_serial = new TasmotaSerial(sr04_echo_pin, sr04_trig_pin, 1); + + if (sonar_serial->begin(9600,1)) { + DEBUG_SENSOR_LOG(PSTR("SR04: Detect mode")); + + if (sr04_trig_pin != -1) { + sr04_type = (Sr04TMiddleValue(Sr04TMode3Distance(), Sr04TMode3Distance(), Sr04TMode3Distance()) != NO_ECHO) ? 3 : 1; + } else { + sr04_type = 2; + } + } else { + sr04_type = 1; + } + + if (sr04_type < 2) { + delete sonar_serial; + sonar_serial = nullptr; + if (-1 == sr04_trig_pin) { + sr04_trig_pin = pin[GPIO_SR04_ECHO]; + } + sonar = new NewPing(sr04_trig_pin, sr04_echo_pin, 300); + } else { + if (sonar_serial->hardwareSerial()) { + ClaimSerial(); + } + } + + AddLog_P2(LOG_LEVEL_INFO,PSTR("SR04: Mode %d"), sr04_type); + return sr04_type; +} + +uint16_t Sr04TMiddleValue(uint16_t first, uint16_t second, uint16_t third) +{ + uint16_t ret = first; + if (first > second) { + first = second; + second = ret; + } + + if (third < first) { + return first; + } else if (third > second) { + return second; + } else { + return third; + } +} + +uint16_t Sr04TMode3Distance() { + + sonar_serial->write(0x55); + sonar_serial->flush(); + + return Sr04TMode2Distance(); +} + +uint16_t Sr04TMode2Distance(void) +{ + sonar_serial->setTimeout(300); + const char startByte = 0xff; + + if (!sonar_serial->find(startByte)) { + + return NO_ECHO; + } + + delay(5); + + uint8_t crc = sonar_serial->read(); + + uint16_t distance = ((uint16_t)crc) << 8; + + + distance += sonar_serial->read(); + crc += distance & 0x00ff; + crc += 0x00FF; + + + if (crc != sonar_serial->read()) { + AddLog_P2(LOG_LEVEL_ERROR,PSTR("SR04: Reading CRC error.")); + return NO_ECHO; + } + + return distance; +} + +void Sr04TReading(void) { + + if (sonar_serial==nullptr && sonar==nullptr) { + Sr04TModeDetect(); + } + + switch (sr04_type) { + case 3: + distance = (real64_t)(Sr04TMiddleValue(Sr04TMode3Distance(),Sr04TMode3Distance(),Sr04TMode3Distance()))/ 10; + break; + case 2: + + while(sonar_serial->available()) sonar_serial->read(); + distance = (real64_t)(Sr04TMiddleValue(Sr04TMode2Distance(),Sr04TMode2Distance(),Sr04TMode2Distance()))/10; + break; + case 1: + distance = (real64_t)(sonar->ping_median(5))/ US_ROUNDTRIP_CM; + break; + default: + distance = NO_ECHO; + } + + return; +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_DISTANCE[] PROGMEM = + "{s}SR04 " D_DISTANCE "{m}%s" D_UNIT_CENTIMETER "{e}"; +#endif + +void Sr04Show(bool json) +{ + + if (distance != 0) { + char distance_chr[33]; + dtostrfd(distance, 3, distance_chr); + + if(json) { + ResponseAppend_P(PSTR(",\"SR04\":{\"" D_JSON_DISTANCE "\":%s}"), distance_chr); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_COUNT, distance_chr); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_DISTANCE, distance_chr); +#endif + } + } +} + + + + + +bool Xsns22(uint8_t function) +{ + bool result = false; + + if (sr04_type) { + switch (function) { + case FUNC_INIT: + result = (pin[GPIO_SR04_ECHO]<99); + break; + case FUNC_EVERY_SECOND: + Sr04TReading(); + result = true; + break; + case FUNC_JSON_APPEND: + Sr04Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Sr04Show(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_24_si1145.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_24_si1145.ino" +#ifdef USE_I2C +#ifdef USE_SI1145 +# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_24_si1145.ino" +#define XSNS_24 24 +#define XI2C_19 19 + +#define SI114X_ADDR 0X60 + + + +#define SI114X_QUERY 0X80 +#define SI114X_SET 0XA0 +#define SI114X_NOP 0X00 +#define SI114X_RESET 0X01 +#define SI114X_BUSADDR 0X02 +#define SI114X_PS_FORCE 0X05 +#define SI114X_GET_CAL 0X12 +#define SI114X_ALS_FORCE 0X06 +#define SI114X_PSALS_FORCE 0X07 +#define SI114X_PS_PAUSE 0X09 +#define SI114X_ALS_PAUSE 0X0A +#define SI114X_PSALS_PAUSE 0X0B +#define SI114X_PS_AUTO 0X0D +#define SI114X_ALS_AUTO 0X0E +#define SI114X_PSALS_AUTO 0X0F + + + +#define SI114X_PART_ID 0X00 +#define SI114X_REV_ID 0X01 +#define SI114X_SEQ_ID 0X02 +#define SI114X_INT_CFG 0X03 +#define SI114X_IRQ_ENABLE 0X04 +#define SI114X_IRQ_MODE1 0x05 +#define SI114X_IRQ_MODE2 0x06 +#define SI114X_HW_KEY 0X07 +#define SI114X_MEAS_RATE0 0X08 +#define SI114X_MEAS_RATE1 0X09 +#define SI114X_PS_RATE 0X0A +#define SI114X_PS_LED21 0X0F +#define SI114X_PS_LED3 0X10 +#define SI114X_UCOEFF0 0X13 +#define SI114X_UCOEFF1 0X14 +#define SI114X_UCOEFF2 0X15 +#define SI114X_UCOEFF3 0X16 +#define SI114X_WR 0X17 +#define SI114X_COMMAND 0X18 +#define SI114X_RESPONSE 0X20 +#define SI114X_IRQ_STATUS 0X21 +#define SI114X_ALS_VIS_DATA0 0X22 +#define SI114X_ALS_VIS_DATA1 0X23 +#define SI114X_ALS_IR_DATA0 0X24 +#define SI114X_ALS_IR_DATA1 0X25 +#define SI114X_PS1_DATA0 0X26 +#define SI114X_PS1_DATA1 0X27 +#define SI114X_PS2_DATA0 0X28 +#define SI114X_PS2_DATA1 0X29 +#define SI114X_PS3_DATA0 0X2A +#define SI114X_PS3_DATA1 0X2B +#define SI114X_AUX_DATA0_UVINDEX0 0X2C +#define SI114X_AUX_DATA1_UVINDEX1 0X2D +#define SI114X_RD 0X2E +#define SI114X_CHIP_STAT 0X30 + + + +#define SI114X_CHLIST 0X01 +#define SI114X_CHLIST_ENUV 0x80 +#define SI114X_CHLIST_ENAUX 0x40 +#define SI114X_CHLIST_ENALSIR 0x20 +#define SI114X_CHLIST_ENALSVIS 0x10 +#define SI114X_CHLIST_ENPS1 0x01 +#define SI114X_CHLIST_ENPS2 0x02 +#define SI114X_CHLIST_ENPS3 0x04 + +#define SI114X_PSLED12_SELECT 0X02 +#define SI114X_PSLED3_SELECT 0X03 + +#define SI114X_PS_ENCODE 0X05 +#define SI114X_ALS_ENCODE 0X06 + +#define SI114X_PS1_ADCMUX 0X07 +#define SI114X_PS2_ADCMUX 0X08 +#define SI114X_PS3_ADCMUX 0X09 + +#define SI114X_PS_ADC_COUNTER 0X0A +#define SI114X_PS_ADC_GAIN 0X0B +#define SI114X_PS_ADC_MISC 0X0C + +#define SI114X_ALS_IR_ADC_MUX 0X0E +#define SI114X_AUX_ADC_MUX 0X0F + +#define SI114X_ALS_VIS_ADC_COUNTER 0X10 +#define SI114X_ALS_VIS_ADC_GAIN 0X11 +#define SI114X_ALS_VIS_ADC_MISC 0X12 + +#define SI114X_LED_REC 0X1C + +#define SI114X_ALS_IR_ADC_COUNTER 0X1D +#define SI114X_ALS_IR_ADC_GAIN 0X1E +#define SI114X_ALS_IR_ADC_MISC 0X1F + + + + +#define SI114X_ADCMUX_SMALL_IR 0x00 +#define SI114X_ADCMUX_VISIABLE 0x02 +#define SI114X_ADCMUX_LARGE_IR 0x03 +#define SI114X_ADCMUX_NO 0x06 +#define SI114X_ADCMUX_GND 0x25 +#define SI114X_ADCMUX_TEMPERATURE 0x65 +#define SI114X_ADCMUX_VDD 0x75 + +#define SI114X_PSLED12_SELECT_PS1_NONE 0x00 +#define SI114X_PSLED12_SELECT_PS1_LED1 0x01 +#define SI114X_PSLED12_SELECT_PS1_LED2 0x02 +#define SI114X_PSLED12_SELECT_PS1_LED3 0x04 +#define SI114X_PSLED12_SELECT_PS2_NONE 0x00 +#define SI114X_PSLED12_SELECT_PS2_LED1 0x10 +#define SI114X_PSLED12_SELECT_PS2_LED2 0x20 +#define SI114X_PSLED12_SELECT_PS2_LED3 0x40 +#define SI114X_PSLED3_SELECT_PS2_NONE 0x00 +#define SI114X_PSLED3_SELECT_PS2_LED1 0x10 +#define SI114X_PSLED3_SELECT_PS2_LED2 0x20 +#define SI114X_PSLED3_SELECT_PS2_LED3 0x40 + +#define SI114X_ADC_GAIN_DIV1 0X00 +#define SI114X_ADC_GAIN_DIV2 0X01 +#define SI114X_ADC_GAIN_DIV4 0X02 +#define SI114X_ADC_GAIN_DIV8 0X03 +#define SI114X_ADC_GAIN_DIV16 0X04 +#define SI114X_ADC_GAIN_DIV32 0X05 + +#define SI114X_LED_CURRENT_5MA 0X01 +#define SI114X_LED_CURRENT_11MA 0X02 +#define SI114X_LED_CURRENT_22MA 0X03 +#define SI114X_LED_CURRENT_45MA 0X04 + +#define SI114X_ADC_COUNTER_1ADCCLK 0X00 +#define SI114X_ADC_COUNTER_7ADCCLK 0X01 +#define SI114X_ADC_COUNTER_15ADCCLK 0X02 +#define SI114X_ADC_COUNTER_31ADCCLK 0X03 +#define SI114X_ADC_COUNTER_63ADCCLK 0X04 +#define SI114X_ADC_COUNTER_127ADCCLK 0X05 +#define SI114X_ADC_COUNTER_255ADCCLK 0X06 +#define SI114X_ADC_COUNTER_511ADCCLK 0X07 + +#define SI114X_ADC_MISC_LOWRANGE 0X00 +#define SI114X_ADC_MISC_HIGHRANGE 0X20 +#define SI114X_ADC_MISC_ADC_NORMALPROXIMITY 0X00 +#define SI114X_ADC_MISC_ADC_RAWADC 0X04 + +#define SI114X_INT_CFG_INTOE 0X01 + +#define SI114X_IRQEN_ALS 0x01 +#define SI114X_IRQEN_PS1 0x04 +#define SI114X_IRQEN_PS2 0x08 +#define SI114X_IRQEN_PS3 0x10 + +uint16_t si1145_visible; +uint16_t si1145_infrared; +uint16_t si1145_uvindex; + +bool si1145_type = false; +uint8_t si1145_valid = 0; + + + +uint8_t Si1145ReadByte(uint8_t reg) +{ + return I2cRead8(SI114X_ADDR, reg); +} + +uint16_t Si1145ReadHalfWord(uint8_t reg) +{ + return I2cRead16LE(SI114X_ADDR, reg); +} + +bool Si1145WriteByte(uint8_t reg, uint16_t val) +{ + I2cWrite8(SI114X_ADDR, reg, val); +} + +uint8_t Si1145WriteParamData(uint8_t p, uint8_t v) +{ + Si1145WriteByte(SI114X_WR, v); + Si1145WriteByte(SI114X_COMMAND, p | SI114X_SET); + return Si1145ReadByte(SI114X_RD); +} + + + +bool Si1145Present(void) +{ + return (Si1145ReadByte(SI114X_PART_ID) == 0X45); +} + +void Si1145Reset(void) +{ + Si1145WriteByte(SI114X_MEAS_RATE0, 0); + Si1145WriteByte(SI114X_MEAS_RATE1, 0); + Si1145WriteByte(SI114X_IRQ_ENABLE, 0); + Si1145WriteByte(SI114X_IRQ_MODE1, 0); + Si1145WriteByte(SI114X_IRQ_MODE2, 0); + Si1145WriteByte(SI114X_INT_CFG, 0); + Si1145WriteByte(SI114X_IRQ_STATUS, 0xFF); + + Si1145WriteByte(SI114X_COMMAND, SI114X_RESET); + delay(10); + Si1145WriteByte(SI114X_HW_KEY, 0x17); + delay(10); +} + +void Si1145DeInit(void) +{ + + + Si1145WriteByte(SI114X_UCOEFF0, 0x29); + Si1145WriteByte(SI114X_UCOEFF1, 0x89); + Si1145WriteByte(SI114X_UCOEFF2, 0x02); + Si1145WriteByte(SI114X_UCOEFF3, 0x00); + Si1145WriteParamData(SI114X_CHLIST, SI114X_CHLIST_ENUV | SI114X_CHLIST_ENALSIR | SI114X_CHLIST_ENALSVIS | SI114X_CHLIST_ENPS1); + + + + Si1145WriteParamData(SI114X_PS1_ADCMUX, SI114X_ADCMUX_LARGE_IR); + Si1145WriteByte(SI114X_PS_LED21, SI114X_LED_CURRENT_22MA); + Si1145WriteParamData(SI114X_PSLED12_SELECT, SI114X_PSLED12_SELECT_PS1_LED1); + + + + Si1145WriteParamData(SI114X_PS_ADC_GAIN, SI114X_ADC_GAIN_DIV1); + Si1145WriteParamData(SI114X_PS_ADC_COUNTER, SI114X_ADC_COUNTER_511ADCCLK); + Si1145WriteParamData(SI114X_PS_ADC_MISC, SI114X_ADC_MISC_HIGHRANGE | SI114X_ADC_MISC_ADC_RAWADC); + + + + Si1145WriteParamData(SI114X_ALS_VIS_ADC_GAIN, SI114X_ADC_GAIN_DIV1); + Si1145WriteParamData(SI114X_ALS_VIS_ADC_COUNTER, SI114X_ADC_COUNTER_511ADCCLK); + Si1145WriteParamData(SI114X_ALS_VIS_ADC_MISC, SI114X_ADC_MISC_HIGHRANGE); + + + + Si1145WriteParamData(SI114X_ALS_IR_ADC_GAIN, SI114X_ADC_GAIN_DIV1); + Si1145WriteParamData(SI114X_ALS_IR_ADC_COUNTER, SI114X_ADC_COUNTER_511ADCCLK); + Si1145WriteParamData(SI114X_ALS_IR_ADC_MISC, SI114X_ADC_MISC_HIGHRANGE); + + + + Si1145WriteByte(SI114X_INT_CFG, SI114X_INT_CFG_INTOE); + Si1145WriteByte(SI114X_IRQ_ENABLE, SI114X_IRQEN_ALS); + + + + Si1145WriteByte(SI114X_MEAS_RATE0, 0xFF); + Si1145WriteByte(SI114X_COMMAND, SI114X_PSALS_AUTO); +} + +bool Si1145Begin(void) +{ + if (!Si1145Present()) { return false; } + + Si1145Reset(); + Si1145DeInit(); + return true; +} + + +uint16_t Si1145ReadUV(void) +{ + return Si1145ReadHalfWord(SI114X_AUX_DATA0_UVINDEX0); +} + + +uint16_t Si1145ReadVisible(void) +{ + return Si1145ReadHalfWord(SI114X_ALS_VIS_DATA0); +} + + +uint16_t Si1145ReadIR(void) +{ + return Si1145ReadHalfWord(SI114X_ALS_IR_DATA0); +} + + + +bool Si1145Read(void) +{ + if (si1145_valid) { si1145_valid--; } + + if (!Si1145Present()) { return false; } + + si1145_visible = Si1145ReadVisible(); + si1145_infrared = Si1145ReadIR(); + si1145_uvindex = Si1145ReadUV(); + si1145_valid = SENSOR_MAX_MISS; + return true; +} + +void Si1145Detect(void) +{ + if (I2cActive(SI114X_ADDR)) { return; } + + if (Si1145Begin()) { + si1145_type = true; + I2cSetActiveFound(SI114X_ADDR, "SI1145"); + } +} + +void Si1145Update(void) +{ + if (!Si1145Read()) { + AddLogMissed("SI1145", si1145_valid); + } +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_SI1145[] PROGMEM = + "{s}SI1145 " D_ILLUMINANCE "{m}%d " D_UNIT_LUX "{e}" + "{s}SI1145 " D_INFRARED "{m}%d " D_UNIT_LUX "{e}" + "{s}SI1145 " D_UV_INDEX "{m}%d.%d{e}"; +#endif + +void Si1145Show(bool json) +{ + if (si1145_valid) { + if (json) { + ResponseAppend_P(PSTR(",\"SI1145\":{\"" D_JSON_ILLUMINANCE "\":%d,\"" D_JSON_INFRARED "\":%d,\"" D_JSON_UV_INDEX "\":%d.%d}"), + si1145_visible, si1145_infrared, si1145_uvindex /100, si1145_uvindex %100); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_ILLUMINANCE, si1145_visible); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_SI1145, si1145_visible, si1145_infrared, si1145_uvindex /100, si1145_uvindex %100); +#endif + } + } +} + + + + + +bool Xsns24(uint8_t function) +{ + if (!I2cEnabled(XI2C_19)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Si1145Detect(); + } + else if (si1145_type) { + switch (function) { + case FUNC_EVERY_SECOND: + Si1145Update(); + break; + case FUNC_JSON_APPEND: + Si1145Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Si1145Show(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_26_lm75ad.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_26_lm75ad.ino" +#ifdef USE_I2C +#ifdef USE_LM75AD +# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_26_lm75ad.ino" +#define XSNS_26 26 +#define XI2C_20 20 + +#define LM75AD_ADDRESS1 0x48 +#define LM75AD_ADDRESS2 0x49 +#define LM75AD_ADDRESS3 0x4A +#define LM75AD_ADDRESS4 0x4B +#define LM75AD_ADDRESS5 0x4C +#define LM75AD_ADDRESS6 0x4D +#define LM75AD_ADDRESS7 0x4E +#define LM75AD_ADDRESS8 0x4F + +#define LM75_TEMP_REGISTER 0x00 +#define LM75_CONF_REGISTER 0x01 +#define LM75_THYST_REGISTER 0x02 +#define LM75_TOS_REGISTER 0x03 + +bool lm75ad_type = false; +uint8_t lm75ad_address; +uint8_t lm75ad_addresses[] = { LM75AD_ADDRESS1, LM75AD_ADDRESS2, LM75AD_ADDRESS3, LM75AD_ADDRESS4, LM75AD_ADDRESS5, LM75AD_ADDRESS6, LM75AD_ADDRESS7, LM75AD_ADDRESS8 }; + +void LM75ADDetect(void) +{ + for (uint32_t i = 0; i < sizeof(lm75ad_addresses); i++) { + lm75ad_address = lm75ad_addresses[i]; + if (I2cActive(lm75ad_address)) { + continue; } + if (!I2cSetDevice(lm75ad_address)) { + break; + } + uint16_t buffer; + if (I2cValidRead16(&buffer, lm75ad_address, LM75_THYST_REGISTER)) { + if (buffer == 0x4B00) { + lm75ad_type = true; + I2cSetActiveFound(lm75ad_address, "LM75AD"); + break; + } + } + } +} + +float LM75ADGetTemp(void) +{ + int16_t sign = 1; + + uint16_t t = I2cRead16(lm75ad_address, LM75_TEMP_REGISTER); + if (t & 0x8000) { + t = (~t) +0x20; + sign = -1; + } + t = t >> 5; + return ConvertTemp(sign * t * 0.125); +} + +void LM75ADShow(bool json) +{ + float t = LM75ADGetTemp(); + char temperature[33]; + dtostrfd(t, Settings.flag2.temperature_resolution, temperature); + + if (json) { + ResponseAppend_P(PSTR(",\"LM75AD\":{\"" D_JSON_TEMPERATURE "\":%s}"), temperature); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_TEMP, temperature); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, "LM75AD", temperature, TempUnit()); +#endif + } +} + + + + + +bool Xsns26(uint8_t function) +{ + if (!I2cEnabled(XI2C_20)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + LM75ADDetect(); + } + else if (lm75ad_type) { + switch (function) { + case FUNC_JSON_APPEND: + LM75ADShow(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + LM75ADShow(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" +# 28 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" +#ifdef USE_I2C +#ifdef USE_APDS9960 +# 39 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" +#define XSNS_27 27 +#define XI2C_21 21 +# 55 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" +#define APDS9960_I2C_ADDR 0x39 + +#define APDS9960_CHIPID_1 0xAB +#define APDS9960_CHIPID_2 0x9C +#define APDS9960_CHIPID_3 0xA8 + +#define APDS9930_CHIPID_1 0x12 +#define APDS9930_CHIPID_2 0x39 + + +#define GESTURE_THRESHOLD_OUT 10 +#define GESTURE_SENSITIVITY_1 50 +#define GESTURE_SENSITIVITY_2 20 + +uint8_t APDS9960addr; +uint8_t APDS9960type = 0; +char APDS9960stype[] = "APDS9960"; +char currentGesture[6]; +uint8_t gesture_mode = 1; + + +volatile uint8_t recovery_loop_counter = 0; +#define APDS9960_LONG_RECOVERY 50 +#define APDS9960_MAX_GESTURE_CYCLES 50 +bool APDS9960_overload = false; + +#ifdef USE_WEBSERVER +const char HTTP_APDS_9960_SNS[] PROGMEM = + "{s}" "Red" "{m}%s{e}" + "{s}" "Green" "{m}%s{e}" + "{s}" "Blue" "{m}%s{e}" + "{s}" "Ambient" "{m}%s " D_UNIT_LUX "{e}" + "{s}" "CCT" "{m}%s " "K" "{e}" + "{s}" "Proximity" "{m}%s{e}"; +#endif +# 98 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" +#define FIFO_PAUSE_TIME 30 + + +#define APDS9960_ENABLE 0x80 +#define APDS9960_ATIME 0x81 +#define APDS9960_WTIME 0x83 +#define APDS9960_AILTL 0x84 +#define APDS9960_AILTH 0x85 +#define APDS9960_AIHTL 0x86 +#define APDS9960_AIHTH 0x87 +#define APDS9960_PILT 0x89 +#define APDS9960_PIHT 0x8B +#define APDS9960_PERS 0x8C +#define APDS9960_CONFIG1 0x8D +#define APDS9960_PPULSE 0x8E +#define APDS9960_CONTROL 0x8F +#define APDS9960_CONFIG2 0x90 +#define APDS9960_ID 0x92 +#define APDS9960_STATUS 0x93 +#define APDS9960_CDATAL 0x94 +#define APDS9960_CDATAH 0x95 +#define APDS9960_RDATAL 0x96 +#define APDS9960_RDATAH 0x97 +#define APDS9960_GDATAL 0x98 +#define APDS9960_GDATAH 0x99 +#define APDS9960_BDATAL 0x9A +#define APDS9960_BDATAH 0x9B +#define APDS9960_PDATA 0x9C +#define APDS9960_POFFSET_UR 0x9D +#define APDS9960_POFFSET_DL 0x9E +#define APDS9960_CONFIG3 0x9F +#define APDS9960_GPENTH 0xA0 +#define APDS9960_GEXTH 0xA1 +#define APDS9960_GCONF1 0xA2 +#define APDS9960_GCONF2 0xA3 +#define APDS9960_GOFFSET_U 0xA4 +#define APDS9960_GOFFSET_D 0xA5 +#define APDS9960_GOFFSET_L 0xA7 +#define APDS9960_GOFFSET_R 0xA9 +#define APDS9960_GPULSE 0xA6 +#define APDS9960_GCONF3 0xAA +#define APDS9960_GCONF4 0xAB +#define APDS9960_GFLVL 0xAE +#define APDS9960_GSTATUS 0xAF +#define APDS9960_IFORCE 0xE4 +#define APDS9960_PICLEAR 0xE5 +#define APDS9960_CICLEAR 0xE6 +#define APDS9960_AICLEAR 0xE7 +#define APDS9960_GFIFO_U 0xFC +#define APDS9960_GFIFO_D 0xFD +#define APDS9960_GFIFO_L 0xFE +#define APDS9960_GFIFO_R 0xFF + + +#define APDS9960_PON 0b00000001 +#define APDS9960_AEN 0b00000010 +#define APDS9960_PEN 0b00000100 +#define APDS9960_WEN 0b00001000 +#define APSD9960_AIEN 0b00010000 +#define APDS9960_PIEN 0b00100000 +#define APDS9960_GEN 0b01000000 +#define APDS9960_GVALID 0b00000001 + + +#define OFF 0 +#define ON 1 + + +#define POWER 0 +#define AMBIENT_LIGHT 1 +#define PROXIMITY 2 +#define WAIT 3 +#define AMBIENT_LIGHT_INT 4 +#define PROXIMITY_INT 5 +#define GESTURE 6 +#define ALL 7 + + +#define LED_DRIVE_100MA 0 +#define LED_DRIVE_50MA 1 +#define LED_DRIVE_25MA 2 +#define LED_DRIVE_12_5MA 3 + + +#define PGAIN_1X 0 +#define PGAIN_2X 1 +#define PGAIN_4X 2 +#define PGAIN_8X 3 + + +#define AGAIN_1X 0 +#define AGAIN_4X 1 +#define AGAIN_16X 2 +#define AGAIN_64X 3 + + +#define GGAIN_1X 0 +#define GGAIN_2X 1 +#define GGAIN_4X 2 +#define GGAIN_8X 3 + + +#define LED_BOOST_100 0 +#define LED_BOOST_150 1 +#define LED_BOOST_200 2 +#define LED_BOOST_300 3 + + +#define GWTIME_0MS 0 +#define GWTIME_2_8MS 1 +#define GWTIME_5_6MS 2 +#define GWTIME_8_4MS 3 +#define GWTIME_14_0MS 4 +#define GWTIME_22_4MS 5 +#define GWTIME_30_8MS 6 +#define GWTIME_39_2MS 7 + + +#define DEFAULT_ATIME 0xdb +#define DEFAULT_WTIME 246 +#define DEFAULT_PROX_PPULSE 0x87 +#define DEFAULT_GESTURE_PPULSE 0x89 +#define DEFAULT_POFFSET_UR 0 +#define DEFAULT_POFFSET_DL 0 +#define DEFAULT_CONFIG1 0x60 +#define DEFAULT_LDRIVE LED_DRIVE_100MA +#define DEFAULT_PGAIN PGAIN_4X +#define DEFAULT_AGAIN AGAIN_4X +#define DEFAULT_PILT 0 +#define DEFAULT_PIHT 50 +#define DEFAULT_AILT 0xFFFF +#define DEFAULT_AIHT 0 +#define DEFAULT_PERS 0x11 +#define DEFAULT_CONFIG2 0x01 +#define DEFAULT_CONFIG3 0 +#define DEFAULT_GPENTH 40 +#define DEFAULT_GEXTH 30 +#define DEFAULT_GCONF1 0x40 +#define DEFAULT_GGAIN GGAIN_4X +#define DEFAULT_GLDRIVE LED_DRIVE_100MA +#define DEFAULT_GWTIME GWTIME_2_8MS +#define DEFAULT_GOFFSET 0 +#define DEFAULT_GPULSE 0xC9 +#define DEFAULT_GCONF3 0 +#define DEFAULT_GIEN 0 + +#define APDS9960_ERROR 0xFF + + +enum { + DIR_NONE, + DIR_LEFT, + DIR_RIGHT, + DIR_UP, + DIR_DOWN, + DIR_ALL +}; + + +enum { + APDS9960_NA_STATE, + APDS9960_ALL_STATE +}; + + +typedef struct gesture_data_type { + uint8_t u_data[32]; + uint8_t d_data[32]; + uint8_t l_data[32]; + uint8_t r_data[32]; + uint8_t index; + uint8_t total_gestures; + uint8_t in_threshold; + uint8_t out_threshold; +} gesture_data_type; + + + gesture_data_type gesture_data_; + int16_t gesture_ud_delta_ = 0; + int16_t gesture_lr_delta_ = 0; + int16_t gesture_ud_count_ = 0; + int16_t gesture_lr_count_ = 0; + int16_t gesture_state_ = 0; + int16_t gesture_motion_ = DIR_NONE; + + typedef struct color_data_type { + uint16_t a; + uint16_t r; + uint16_t g; + uint16_t b; + uint8_t p; + uint16_t cct; + uint16_t lux; + } color_data_type; + + color_data_type color_data; + uint8_t APDS9960_aTime = DEFAULT_ATIME; +# 307 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" + bool wireWriteByte(uint8_t val) + { + Wire.beginTransmission(APDS9960_I2C_ADDR); + Wire.write(val); + if( Wire.endTransmission() != 0 ) { + return false; + } + + return true; + } +# 326 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" +int8_t wireReadDataBlock( uint8_t reg, + uint8_t *val, + uint16_t len) +{ + unsigned char i = 0; + + + if (!wireWriteByte(reg)) { + return -1; + } + + + Wire.requestFrom(APDS9960_I2C_ADDR, len); + while (Wire.available()) { + if (i >= len) { + return -1; + } + val[i] = Wire.read(); + i++; + } + + return i; +} + + + + + + + +void calculateColorTemperature(void) +{ + float X, Y, Z; + float xc, yc; + float n; + float cct; + + + + + + X = (-0.14282F * color_data.r) + (1.54924F * color_data.g) + (-0.95641F * color_data.b); + Y = (-0.32466F * color_data.r) + (1.57837F * color_data.g) + (-0.73191F * color_data.b); + Z = (-0.68202F * color_data.r) + (0.77073F * color_data.g) + ( 0.56332F * color_data.b); + + + xc = (X) / (X + Y + Z); + yc = (Y) / (X + Y + Z); + + + n = (xc - 0.3320F) / (0.1858F - yc); + + + color_data.cct = (449.0F * FastPrecisePowf(n, 3)) + (3525.0F * FastPrecisePowf(n, 2)) + (6823.3F * n) + 5520.33F; + + return; +} +# 393 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" + uint8_t getProxIntLowThresh(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PILT) ; + return val; + } + + + + + + + void setProxIntLowThresh(uint8_t threshold) + { + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PILT, threshold); + } + + + + + + + uint8_t getProxIntHighThresh(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PIHT) ; + return val; + } + + + + + + + + void setProxIntHighThresh(uint8_t threshold) + { + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PIHT, threshold); + } +# 449 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" + uint8_t getLEDDrive(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL) ; + + val = (val >> 6) & 0b00000011; + + return val; + } +# 472 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" + void setLEDDrive(uint8_t drive) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); + + + drive &= 0b00000011; + drive = drive << 6; + val &= 0b00111111; + val |= drive; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); + } +# 501 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" + uint8_t getProximityGain(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL) ; + + val = (val >> 2) & 0b00000011; + + return val; + } +# 524 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" + void setProximityGain(uint8_t drive) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); + + + drive &= 0b00000011; + drive = drive << 2; + val &= 0b11110011; + val |= drive; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); + } +# 565 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" + void setAmbientLightGain(uint8_t drive) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); + + + drive &= 0b00000011; + val &= 0b11111100; + val |= drive; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); + } +# 592 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" + uint8_t getLEDBoost(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG2) ; + + + val = (val >> 4) & 0b00000011; + + return val; + } +# 616 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" + void setLEDBoost(uint8_t boost) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG2) ; + + boost &= 0b00000011; + boost = boost << 4; + val &= 0b11001111; + val |= boost; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG2, val) ; + } + + + + + + + uint8_t getProxGainCompEnable(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; + + + val = (val >> 5) & 0b00000001; + + return val; + } + + + + + + + void setProxGainCompEnable(uint8_t enable) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; + + + enable &= 0b00000001; + enable = enable << 5; + val &= 0b11011111; + val |= enable; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, val) ; + } +# 684 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" + uint8_t getProxPhotoMask(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; + + + val &= 0b00001111; + + return val; + } +# 709 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" + void setProxPhotoMask(uint8_t mask) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; + + + mask &= 0b00001111; + val &= 0b11110000; + val |= mask; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, val) ; + } + + + + + + + uint8_t getGestureEnterThresh(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GPENTH) ; + + return val; + } + + + + + + + void setGestureEnterThresh(uint8_t threshold) + { + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GPENTH, threshold) ; + + } + + + + + + + uint8_t getGestureExitThresh(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GEXTH) ; + + return val; + } + + + + + + + void setGestureExitThresh(uint8_t threshold) + { + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GEXTH, threshold) ; + } +# 787 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" + uint8_t getGestureGain(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; + + + val = (val >> 5) & 0b00000011; + + return val; + } +# 811 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" + void setGestureGain(uint8_t gain) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; + + + gain &= 0b00000011; + gain = gain << 5; + val &= 0b10011111; + val |= gain; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val) ; + } +# 839 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" + uint8_t getGestureLEDDrive(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; + + + val = (val >> 3) & 0b00000011; + + return val; + } +# 863 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" + void setGestureLEDDrive(uint8_t drive) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; + + + drive &= 0b00000011; + drive = drive << 3; + val &= 0b11100111; + val |= drive; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val) ; + } +# 895 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" + uint8_t getGestureWaitTime(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; + + + val &= 0b00000111; + + return val; + } +# 923 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" + void setGestureWaitTime(uint8_t time) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; + + + time &= 0b00000111; + val &= 0b11111000; + val |= time; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val) ; + } + + + + + + + void getLightIntLowThreshold(uint16_t &threshold) + { + uint8_t val_byte; + threshold = 0; + + + val_byte = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AILTL) ; + threshold = val_byte; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTH, val_byte) ; + threshold = threshold + ((uint16_t)val_byte << 8); + } + + + + + + + + void setLightIntLowThreshold(uint16_t threshold) + { + uint8_t val_low; + uint8_t val_high; + + + val_low = threshold & 0x00FF; + val_high = (threshold & 0xFF00) >> 8; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTL, val_low) ; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTH, val_high) ; + + } + + + + + + + + void getLightIntHighThreshold(uint16_t &threshold) + { + uint8_t val_byte; + threshold = 0; + + + val_byte = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AIHTL); + threshold = val_byte; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTH, val_byte) ; + threshold = threshold + ((uint16_t)val_byte << 8); + } + + + + + + + void setLightIntHighThreshold(uint16_t threshold) + { + uint8_t val_low; + uint8_t val_high; + + + val_low = threshold & 0x00FF; + val_high = (threshold & 0xFF00) >> 8; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTL, val_low); + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTH, val_high) ; + } + + + + + + + + void getProximityIntLowThreshold(uint8_t &threshold) + { + threshold = 0; + + + threshold = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PILT); + + } + + + + + + + void setProximityIntLowThreshold(uint8_t threshold) + { + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PILT, threshold) ; + } +# 1056 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" + void getProximityIntHighThreshold(uint8_t &threshold) + { + threshold = 0; + + + threshold = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PIHT) ; + + } + + + + + + + void setProximityIntHighThreshold(uint8_t threshold) + { + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PIHT, threshold) ; + } + + + + + + + uint8_t getAmbientLightIntEnable(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; + + + val = (val >> 4) & 0b00000001; + + return val; + } + + + + + + + void setAmbientLightIntEnable(uint8_t enable) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE); + + + enable &= 0b00000001; + enable = enable << 4; + val &= 0b11101111; + val |= enable; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, val) ; + } + + + + + + + uint8_t getProximityIntEnable(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; + + + val = (val >> 5) & 0b00000001; + + return val; + } + + + + + + + void setProximityIntEnable(uint8_t enable) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; + + + enable &= 0b00000001; + enable = enable << 5; + val &= 0b11011111; + val |= enable; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, val) ; + } + + + + + + + uint8_t getGestureIntEnable(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; + + + val = (val >> 1) & 0b00000001; + + return val; + } + + + + + + + void setGestureIntEnable(uint8_t enable) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; + + + enable &= 0b00000001; + enable = enable << 1; + val &= 0b11111101; + val |= enable; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF4, val) ; + } + + + + + + void clearAmbientLightInt(void) + { + uint8_t throwaway; + throwaway = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AICLEAR); + } + + + + + + void clearProximityInt(void) + { + uint8_t throwaway; + throwaway = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PICLEAR) ; + + } + + + + + + + uint8_t getGestureMode(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; + + + val &= 0b00000001; + + return val; + } + + + + + + + void setGestureMode(uint8_t mode) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; + + + mode &= 0b00000001; + val &= 0b11111110; + val |= mode; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF4, val) ; + } + + +bool APDS9960_init(void) +{ + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, DEFAULT_ATIME) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_WTIME, DEFAULT_WTIME) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PPULSE, DEFAULT_PROX_PPULSE) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_POFFSET_UR, DEFAULT_POFFSET_UR) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_POFFSET_DL, DEFAULT_POFFSET_DL) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG1, DEFAULT_CONFIG1) ; + + setLEDDrive(DEFAULT_LDRIVE); + + setProximityGain(DEFAULT_PGAIN); + + setAmbientLightGain(DEFAULT_AGAIN); + + setProxIntLowThresh(DEFAULT_PILT) ; + + setProxIntHighThresh(DEFAULT_PIHT); + + setLightIntLowThreshold(DEFAULT_AILT) ; + + setLightIntHighThreshold(DEFAULT_AIHT) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PERS, DEFAULT_PERS) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG2, DEFAULT_CONFIG2) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, DEFAULT_CONFIG3) ; + + + setGestureEnterThresh(DEFAULT_GPENTH); + + setGestureExitThresh(DEFAULT_GEXTH) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF1, DEFAULT_GCONF1) ; + + setGestureGain(DEFAULT_GGAIN) ; + + setGestureLEDDrive(DEFAULT_GLDRIVE) ; + + setGestureWaitTime(DEFAULT_GWTIME) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_U, DEFAULT_GOFFSET) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_D, DEFAULT_GOFFSET) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_L, DEFAULT_GOFFSET) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_R, DEFAULT_GOFFSET) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GPULSE, DEFAULT_GPULSE) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF3, DEFAULT_GCONF3) ; + + setGestureIntEnable(DEFAULT_GIEN); + + disablePower(); + + return true; +} +# 1334 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" +uint8_t getMode(void) +{ + uint8_t enable_value; + + + enable_value = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; + + return enable_value; +} + + + + + + + +void setMode(uint8_t mode, uint8_t enable) +{ + uint8_t reg_val; + + + reg_val = getMode(); + + + + enable = enable & 0x01; + if( mode >= 0 && mode <= 6 ) { + if (enable) { + reg_val |= (1 << mode); + } else { + reg_val &= ~(1 << mode); + } + } else if( mode == ALL ) { + if (enable) { + reg_val = 0x7F; + } else { + reg_val = 0x00; + } + } + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, reg_val) ; +} + + + + + + +void enableLightSensor(void) +{ + + setAmbientLightGain(DEFAULT_AGAIN); + setAmbientLightIntEnable(0); + enablePower() ; + setMode(AMBIENT_LIGHT, 1) ; +} + + + + + +void disableLightSensor(void) +{ + setAmbientLightIntEnable(0) ; + setMode(AMBIENT_LIGHT, 0) ; +} + + + + + + +void enableProximitySensor(void) +{ + + setProximityGain(DEFAULT_PGAIN); + setLEDDrive(DEFAULT_LDRIVE) ; + setProximityIntEnable(0) ; + enablePower(); + setMode(PROXIMITY, 1) ; +} + + + + + +void disableProximitySensor(void) +{ + setProximityIntEnable(0) ; + setMode(PROXIMITY, 0) ; +} + + + + + + +void enableGestureSensor(void) +{ + + + + + + + + resetGestureParameters(); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_WTIME, 0xFF) ; + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PPULSE, DEFAULT_GESTURE_PPULSE) ; + setLEDBoost(LED_BOOST_100); + setGestureIntEnable(0) ; + setGestureMode(1); + enablePower() ; + setMode(WAIT, 1) ; + setMode(PROXIMITY, 1) ; + setMode(GESTURE, 1); +} + + + + + +void disableGestureSensor(void) +{ + resetGestureParameters(); + setGestureIntEnable(0) ; + setGestureMode(0) ; + setMode(GESTURE, 0) ; +} + + + + + + +bool isGestureAvailable(void) +{ + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GSTATUS) ; + + + val &= APDS9960_GVALID; + + + if( val == 1) { + return true; + } else { + return false; + } +} + + + + + + +int16_t readGesture(void) +{ + uint8_t fifo_level = 0; + uint8_t bytes_read = 0; + uint8_t fifo_data[128]; + uint8_t gstatus; + uint16_t motion; + uint16_t i; + uint8_t gesture_loop_counter = 0; + + + if( !isGestureAvailable() || !(getMode() & 0b01000001) ) { + return DIR_NONE; + } + + + while(1) { + if (gesture_loop_counter == APDS9960_MAX_GESTURE_CYCLES){ + disableGestureSensor(); + APDS9960_overload = true; + AddLog_P(LOG_LEVEL_DEBUG, PSTR("Sensor overload")); + } + gesture_loop_counter += 1; + + delay(FIFO_PAUSE_TIME); + + + gstatus = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GSTATUS); + + + if( (gstatus & APDS9960_GVALID) == APDS9960_GVALID ) { + + + fifo_level = I2cRead8(APDS9960_I2C_ADDR,APDS9960_GFLVL) ; + + + if( fifo_level > 0) { + bytes_read = wireReadDataBlock( APDS9960_GFIFO_U, + (uint8_t*)fifo_data, + (fifo_level * 4) ); + if( bytes_read == -1 ) { + return APDS9960_ERROR; + } + + + if( bytes_read >= 4 ) { + for( i = 0; i < bytes_read; i += 4 ) { + gesture_data_.u_data[gesture_data_.index] = \ + fifo_data[i + 0]; + gesture_data_.d_data[gesture_data_.index] = \ + fifo_data[i + 1]; + gesture_data_.l_data[gesture_data_.index] = \ + fifo_data[i + 2]; + gesture_data_.r_data[gesture_data_.index] = \ + fifo_data[i + 3]; + gesture_data_.index++; + gesture_data_.total_gestures++; + } + + if( processGestureData() ) { + if( decodeGesture() ) { + + } + } + + gesture_data_.index = 0; + gesture_data_.total_gestures = 0; + } + } + } else { + + + delay(FIFO_PAUSE_TIME); + decodeGesture(); + motion = gesture_motion_; + resetGestureParameters(); + return motion; + } + } +} + + + + + +void enablePower(void) +{ + setMode(POWER, 1) ; +} + + + + + +void disablePower(void) +{ + setMode(POWER, 0) ; +} +# 1601 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" +void readAllColorAndProximityData(void) +{ + if (I2cReadBuffer(APDS9960_I2C_ADDR, APDS9960_CDATAL, (uint8_t *) &color_data, (uint16_t)9)) + { + + + } +} +# 1617 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" +void resetGestureParameters(void) +{ + gesture_data_.index = 0; + gesture_data_.total_gestures = 0; + + gesture_ud_delta_ = 0; + gesture_lr_delta_ = 0; + + gesture_ud_count_ = 0; + gesture_lr_count_ = 0; + + gesture_state_ = 0; + gesture_motion_ = DIR_NONE; +} + + + + + + +bool processGestureData(void) +{ + uint8_t u_first = 0; + uint8_t d_first = 0; + uint8_t l_first = 0; + uint8_t r_first = 0; + uint8_t u_last = 0; + uint8_t d_last = 0; + uint8_t l_last = 0; + uint8_t r_last = 0; + uint16_t ud_ratio_first; + uint16_t lr_ratio_first; + uint16_t ud_ratio_last; + uint16_t lr_ratio_last; + uint16_t ud_delta; + uint16_t lr_delta; + uint16_t i; + + + if( gesture_data_.total_gestures <= 4 ) { + return false; + } + + + if( (gesture_data_.total_gestures <= 32) && \ + (gesture_data_.total_gestures > 0) ) { + + + for( i = 0; i < gesture_data_.total_gestures; i++ ) { + if( (gesture_data_.u_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data_.d_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data_.l_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data_.r_data[i] > GESTURE_THRESHOLD_OUT) ) { + + u_first = gesture_data_.u_data[i]; + d_first = gesture_data_.d_data[i]; + l_first = gesture_data_.l_data[i]; + r_first = gesture_data_.r_data[i]; + break; + } + } + + + if( (u_first == 0) || (d_first == 0) || \ + (l_first == 0) || (r_first == 0) ) { + + return false; + } + + for( i = gesture_data_.total_gestures - 1; i >= 0; i-- ) { + + if( (gesture_data_.u_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data_.d_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data_.l_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data_.r_data[i] > GESTURE_THRESHOLD_OUT) ) { + + u_last = gesture_data_.u_data[i]; + d_last = gesture_data_.d_data[i]; + l_last = gesture_data_.l_data[i]; + r_last = gesture_data_.r_data[i]; + break; + } + } + } + + + ud_ratio_first = ((u_first - d_first) * 100) / (u_first + d_first); + lr_ratio_first = ((l_first - r_first) * 100) / (l_first + r_first); + ud_ratio_last = ((u_last - d_last) * 100) / (u_last + d_last); + lr_ratio_last = ((l_last - r_last) * 100) / (l_last + r_last); + + + ud_delta = ud_ratio_last - ud_ratio_first; + lr_delta = lr_ratio_last - lr_ratio_first; + + + gesture_ud_delta_ += ud_delta; + gesture_lr_delta_ += lr_delta; + + + if( gesture_ud_delta_ >= GESTURE_SENSITIVITY_1 ) { + gesture_ud_count_ = 1; + } else if( gesture_ud_delta_ <= -GESTURE_SENSITIVITY_1 ) { + gesture_ud_count_ = -1; + } else { + gesture_ud_count_ = 0; + } + + + if( gesture_lr_delta_ >= GESTURE_SENSITIVITY_1 ) { + gesture_lr_count_ = 1; + } else if( gesture_lr_delta_ <= -GESTURE_SENSITIVITY_1 ) { + gesture_lr_count_ = -1; + } else { + gesture_lr_count_ = 0; + } + return false; +} + + + + + + +bool decodeGesture(void) +{ + + + if( (gesture_ud_count_ == -1) && (gesture_lr_count_ == 0) ) { + gesture_motion_ = DIR_UP; + } else if( (gesture_ud_count_ == 1) && (gesture_lr_count_ == 0) ) { + gesture_motion_ = DIR_DOWN; + } else if( (gesture_ud_count_ == 0) && (gesture_lr_count_ == 1) ) { + gesture_motion_ = DIR_RIGHT; + } else if( (gesture_ud_count_ == 0) && (gesture_lr_count_ == -1) ) { + gesture_motion_ = DIR_LEFT; + } else if( (gesture_ud_count_ == -1) && (gesture_lr_count_ == 1) ) { + if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { + gesture_motion_ = DIR_UP; + } else { + gesture_motion_ = DIR_RIGHT; + } + } else if( (gesture_ud_count_ == 1) && (gesture_lr_count_ == -1) ) { + if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { + gesture_motion_ = DIR_DOWN; + } else { + gesture_motion_ = DIR_LEFT; + } + } else if( (gesture_ud_count_ == -1) && (gesture_lr_count_ == -1) ) { + if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { + gesture_motion_ = DIR_UP; + } else { + gesture_motion_ = DIR_LEFT; + } + } else if( (gesture_ud_count_ == 1) && (gesture_lr_count_ == 1) ) { + if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { + gesture_motion_ = DIR_DOWN; + } else { + gesture_motion_ = DIR_RIGHT; + } + } else { + return false; + } + + return true; +} + +void handleGesture(void) { + if (isGestureAvailable() ) { + switch (readGesture()) { + case DIR_UP: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("UP")); + snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Up")); + break; + case DIR_DOWN: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("DOWN")); + snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Down")); + break; + case DIR_LEFT: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("LEFT")); + snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Left")); + break; + case DIR_RIGHT: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("RIGHT")); + snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Right")); + break; + default: + if(APDS9960_overload) + { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("LONG")); + snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Long")); + } + else{ + AddLog_P(LOG_LEVEL_DEBUG, PSTR("NONE")); + snprintf_P(currentGesture, sizeof(currentGesture), PSTR("None")); + } + } + MqttPublishSensor(); + } +} + +void APDS9960_adjustATime(void) +{ + + I2cValidRead16LE(&color_data.a, APDS9960_I2C_ADDR, APDS9960_CDATAL); + + + if (color_data.a < (uint16_t)20){ + APDS9960_aTime = 0x40; + } + else if (color_data.a < (uint16_t)40){ + APDS9960_aTime = 0x80; + } + else if (color_data.a < (uint16_t)50){ + APDS9960_aTime = DEFAULT_ATIME; + } + else if (color_data.a < (uint16_t)70){ + APDS9960_aTime = 0xc0; + } + if (color_data.a < 200){ + APDS9960_aTime = 0xe9; + } + + + + else{ + APDS9960_aTime = 0xff; + } + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, APDS9960_aTime); + enablePower(); + enableLightSensor(); + delay(20); +} + + +void APDS9960_loop(void) +{ + if (recovery_loop_counter > 0){ + recovery_loop_counter -= 1; + } + if (recovery_loop_counter == 1 && APDS9960_overload){ + enableGestureSensor(); + APDS9960_overload = false; + Response_P(PSTR("{\"Gesture\":\"On\"}")); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); + gesture_mode = 1; + } + + if (gesture_mode) { + if (recovery_loop_counter == 0){ + handleGesture(); + + if (APDS9960_overload) + { + disableGestureSensor(); + recovery_loop_counter = APDS9960_LONG_RECOVERY; + Response_P(PSTR("{\"Gesture\":\"Off\"}")); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); + gesture_mode = 0; + } + } + } +} + +void APDS9960_detect(void) +{ + if (APDS9960type || I2cActive(APDS9960_I2C_ADDR)) { return; } + + APDS9960type = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ID); + if (APDS9960type == APDS9960_CHIPID_1 || APDS9960type == APDS9960_CHIPID_2 || APDS9960type == APDS9960_CHIPID_3) { + if (APDS9960_init()) { + I2cSetActiveFound(APDS9960_I2C_ADDR, APDS9960stype); + + enableProximitySensor(); + enableGestureSensor(); + } else { + APDS9960type = 0; + } + } else { + APDS9960type = 0; + } + currentGesture[0] = '\0'; +} + + + + + +void APDS9960_show(bool json) +{ + if (!APDS9960type) { return; } + + if (!gesture_mode && !APDS9960_overload) { + char red_chr[10]; + char green_chr[10]; + char blue_chr[10]; + char ambient_chr[10]; + char cct_chr[10]; + char prox_chr[10]; + + readAllColorAndProximityData(); + + sprintf (ambient_chr, "%u", color_data.a/4); + sprintf (red_chr, "%u", color_data.r); + sprintf (green_chr, "%u", color_data.g); + sprintf (blue_chr, "%u", color_data.b ); + sprintf (prox_chr, "%u", color_data.p ); + + + + + + calculateColorTemperature(); + sprintf (cct_chr, "%u", color_data.cct); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"Red\":%s,\"Green\":%s,\"Blue\":%s,\"Ambient\":%s,\"CCT\":%s,\"Proximity\":%s}"), + APDS9960stype, red_chr, green_chr, blue_chr, ambient_chr, cct_chr, prox_chr); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_APDS_9960_SNS, red_chr, green_chr, blue_chr, ambient_chr, cct_chr, prox_chr ); +#endif + } + } + else { + if (json && (currentGesture[0] != '\0' )) { + ResponseAppend_P(PSTR(",\"%s\":{\"%s\":1}"), APDS9960stype, currentGesture); + currentGesture[0] = '\0'; + } + } +} +# 1962 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" +bool APDS9960CommandSensor(void) +{ + bool serviced = true; + + switch (XdrvMailbox.payload) { + case 0: + disableGestureSensor(); + gesture_mode = 0; + enableLightSensor(); + APDS9960_overload = false; + break; + case 1: + if (APDS9960type) { + setGestureGain(DEFAULT_GGAIN); + setProximityGain(DEFAULT_PGAIN); + disableLightSensor(); + enableGestureSensor(); + gesture_mode = 1; + } + break; + case 2: + if (APDS9960type) { + setGestureGain(GGAIN_2X); + setProximityGain(PGAIN_2X); + disableLightSensor(); + enableGestureSensor(); + gesture_mode = 1; + } + break; + default: + int temp_aTime = (uint8_t)XdrvMailbox.payload; + if (temp_aTime > 2 && temp_aTime < 256){ + disablePower(); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, temp_aTime); + enablePower(); + enableLightSensor(); + } + break; + } + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_27, GetStateText(gesture_mode)); + + return serviced; +} + + + + + +bool Xsns27(uint8_t function) +{ + if (!I2cEnabled(XI2C_21)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + APDS9960_detect(); + } + else if (APDS9960type) { + switch (function) { + case FUNC_EVERY_50_MSECOND: + APDS9960_loop(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_27 == XdrvMailbox.index) { + result = APDS9960CommandSensor(); + } + break; + case FUNC_JSON_APPEND: + APDS9960_show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + APDS9960_show(0); + break; +#endif + } + } + return result; +} +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_28_tm1638.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_28_tm1638.ino" +#ifdef USE_TM1638 + + + + + + +#define XSNS_28 28 + +#define TM1638_COLOR_NONE 0 +#define TM1638_COLOR_RED 1 +#define TM1638_COLOR_GREEN 2 + +#define TM1638_CLOCK_DELAY 1 + +uint8_t tm1638_type = 1; +uint8_t tm1638_clock_pin = 0; +uint8_t tm1638_data_pin = 0; +uint8_t tm1638_strobe_pin = 0; +uint8_t tm1638_displays = 8; +uint8_t tm1638_active_display = 1; +uint8_t tm1638_intensity = 0; +uint8_t tm1638_state = 0; + + + + + + +void Tm16XXSend(uint8_t data) +{ + for (uint32_t i = 0; i < 8; i++) { + digitalWrite(tm1638_data_pin, !!(data & (1 << i))); + digitalWrite(tm1638_clock_pin, LOW); + delayMicroseconds(TM1638_CLOCK_DELAY); + digitalWrite(tm1638_clock_pin, HIGH); + } +} + +void Tm16XXSendCommand(uint8_t cmd) +{ + digitalWrite(tm1638_strobe_pin, LOW); + Tm16XXSend(cmd); + digitalWrite(tm1638_strobe_pin, HIGH); +} + +void TM16XXSendData(uint8_t address, uint8_t data) +{ + Tm16XXSendCommand(0x44); + digitalWrite(tm1638_strobe_pin, LOW); + Tm16XXSend(0xC0 | address); + Tm16XXSend(data); + digitalWrite(tm1638_strobe_pin, HIGH); +} + +uint8_t Tm16XXReceive(void) +{ + uint8_t temp = 0; + + + pinMode(tm1638_data_pin, INPUT); + digitalWrite(tm1638_data_pin, HIGH); + + for (uint32_t i = 0; i < 8; ++i) { + digitalWrite(tm1638_clock_pin, LOW); + delayMicroseconds(TM1638_CLOCK_DELAY); + temp |= digitalRead(tm1638_data_pin) << i; + digitalWrite(tm1638_clock_pin, HIGH); + } + + + pinMode(tm1638_data_pin, OUTPUT); + digitalWrite(tm1638_data_pin, LOW); + + return temp; +} + + + +void Tm16XXClearDisplay(void) +{ + for (uint32_t i = 0; i < tm1638_displays; i++) { + TM16XXSendData(i << 1, 0); + } +} + +void Tm1638SetLED(uint8_t color, uint8_t pos) +{ + TM16XXSendData((pos << 1) + 1, color); +} + +void Tm1638SetLEDs(word leds) +{ + for (uint32_t i = 0; i < tm1638_displays; i++) { + uint8_t color = 0; + + if ((leds & (1 << i)) != 0) { + color |= TM1638_COLOR_RED; + } + + if ((leds & (1 << (i + 8))) != 0) { + color |= TM1638_COLOR_GREEN; + } + + Tm1638SetLED(color, i); + } +} + +uint8_t Tm1638GetButtons(void) +{ + uint8_t keys = 0; + + digitalWrite(tm1638_strobe_pin, LOW); + Tm16XXSend(0x42); + for (uint32_t i = 0; i < 4; i++) { + keys |= Tm16XXReceive() << i; + } + digitalWrite(tm1638_strobe_pin, HIGH); + + return keys; +} + + + +void TmInit(void) +{ + tm1638_type = 0; + if ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99)) { + tm1638_clock_pin = pin[GPIO_TM16CLK]; + tm1638_data_pin = pin[GPIO_TM16DIO]; + tm1638_strobe_pin = pin[GPIO_TM16STB]; + + pinMode(tm1638_data_pin, OUTPUT); + pinMode(tm1638_clock_pin, OUTPUT); + pinMode(tm1638_strobe_pin, OUTPUT); + + digitalWrite(tm1638_strobe_pin, HIGH); + digitalWrite(tm1638_clock_pin, HIGH); + + Tm16XXSendCommand(0x40); + Tm16XXSendCommand(0x80 | (tm1638_active_display ? 8 : 0) | tmin(7, tm1638_intensity)); + + digitalWrite(tm1638_strobe_pin, LOW); + Tm16XXSend(0xC0); + for (uint32_t i = 0; i < 16; i++) { + Tm16XXSend(0x00); + } + digitalWrite(tm1638_strobe_pin, HIGH); + + tm1638_type = 1; + tm1638_state = 1; + } +} + +void TmLoop(void) +{ + if (tm1638_state) { + uint8_t buttons = Tm1638GetButtons(); + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { + SwitchSetVirtual(i, (buttons &1) ^1); + uint8_t color = (SwitchGetVirtual(i)) ? TM1638_COLOR_NONE : TM1638_COLOR_RED; + Tm1638SetLED(color, i); + buttons >>= 1; + } + SwitchHandler(1); + } +} +# 201 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_28_tm1638.ino" +bool Xsns28(uint8_t function) +{ + bool result = false; + + if (tm1638_type) { + switch (function) { + case FUNC_INIT: + TmInit(); + break; + case FUNC_EVERY_50_MSECOND: + TmLoop(); + break; +# 223 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_28_tm1638.ino" + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_29_mcp230xx.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_29_mcp230xx.ino" +#ifdef USE_I2C +#ifdef USE_MCP230xx +# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_29_mcp230xx.ino" +#define XSNS_29 29 +#define XI2C_22 22 + + + + + +uint8_t MCP230xx_IODIR = 0x00; +uint8_t MCP230xx_GPINTEN = 0x02; +uint8_t MCP230xx_IOCON = 0x05; +uint8_t MCP230xx_GPPU = 0x06; +uint8_t MCP230xx_INTF = 0x07; +uint8_t MCP230xx_INTCAP = 0x08; +uint8_t MCP230xx_GPIO = 0x09; + +uint8_t mcp230xx_type = 0; +uint8_t mcp230xx_pincount = 0; +uint8_t mcp230xx_int_en = 0; +uint8_t mcp230xx_int_prio_counter = 0; +uint8_t mcp230xx_int_counter_en = 0; +uint8_t mcp230xx_int_retainer_en = 0; +uint8_t mcp230xx_int_sec_counter = 0; + +uint8_t mcp230xx_int_report_defer_counter[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +uint16_t mcp230xx_int_counter[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +uint8_t mcp230xx_int_retainer[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +unsigned long int_millis[16]; + +const char MCP230XX_SENSOR_RESPONSE[] PROGMEM = "{\"Sensor29_D%i\":{\"MODE\":%i,\"PULL_UP\":\"%s\",\"INT_MODE\":\"%s\",\"STATE\":\"%s\"}}"; + +const char MCP230XX_INTCFG_RESPONSE[] PROGMEM = "{\"MCP230xx_INT%s\":{\"D_%i\":%i}}"; + +#ifdef USE_MCP230xx_OUTPUT +const char MCP230XX_CMND_RESPONSE[] PROGMEM = "{\"S29cmnd_D%i\":{\"COMMAND\":\"%s\",\"STATE\":\"%s\"}}"; +#endif + +void MCP230xx_CheckForIntCounter(void) { + uint8_t en = 0; + for (uint32_t ca=0;ca<16;ca++) { + if (Settings.mcp230xx_config[ca].int_count_en) { + en=1; + } + } + if (!Settings.mcp230xx_int_timer) en=0; + mcp230xx_int_counter_en=en; + if (!mcp230xx_int_counter_en) { + for (uint32_t ca=0;ca<16;ca++) { + mcp230xx_int_counter[ca] = 0; + } + } +} + +void MCP230xx_CheckForIntRetainer(void) { + uint8_t en = 0; + for (uint32_t ca=0;ca<16;ca++) { + if (Settings.mcp230xx_config[ca].int_retain_flag) { + en=1; + } + } + mcp230xx_int_retainer_en=en; + if (!mcp230xx_int_retainer_en) { + for (uint32_t ca=0;ca<16;ca++) { + mcp230xx_int_retainer[ca] = 0; + } + } +} + +const char* ConvertNumTxt(uint8_t statu, uint8_t pinmod=0) { +#ifdef USE_MCP230xx_OUTPUT +if ((6 == pinmod) && (statu < 2)) { statu = abs(statu-1); } +#endif + switch (statu) { + case 0: + return "OFF"; + break; + case 1: + return "ON"; + break; +#ifdef USE_MCP230xx_OUTPUT + case 2: + return "TOGGLE"; + break; +#endif + } + return ""; +} + +const char* IntModeTxt(uint8_t intmo) { + switch (intmo) { + case 0: + return "ALL"; + break; + case 1: + return "EVENT"; + break; + case 2: + return "TELE"; + break; + case 3: + return "DISABLED"; + break; + } + return ""; +} + +uint8_t MCP230xx_readGPIO(uint8_t port) { + return I2cRead8(USE_MCP230xx_ADDR, MCP230xx_GPIO + port); +} + +void MCP230xx_ApplySettings(void) +{ + uint8_t int_en = 0; + for (uint32_t mcp230xx_port = 0; mcp230xx_port < mcp230xx_type; mcp230xx_port++) { + uint8_t reg_gppu = 0; + uint8_t reg_gpinten = 0; + uint8_t reg_iodir = 0xFF; +#ifdef USE_MCP230xx_OUTPUT + uint8_t reg_portpins = 0x00; +#endif + for (uint32_t idx = 0; idx < 8; idx++) { + switch (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pinmode) { + case 0 ... 1: + reg_iodir |= (1 << idx); + break; + case 2 ... 4: + reg_iodir |= (1 << idx); + reg_gpinten |= (1 << idx); + int_en = 1; + break; +#ifdef USE_MCP230xx_OUTPUT + case 5 ... 6: + reg_iodir &= ~(1 << idx); + if (Settings.flag.save_state) { + reg_portpins |= (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].saved_state << idx); + } else { + if (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pullup) { + reg_portpins |= (1 << idx); + } + } + break; +#endif + default: + break; + } +#ifdef USE_MCP230xx_OUTPUT + if ((Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pullup) && (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pinmode < 5)) { + reg_gppu |= (1 << idx); + } +#else + if (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pullup) { + reg_gppu |= (1 << idx); + } +#endif + } + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPPU+mcp230xx_port, reg_gppu); + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPINTEN+mcp230xx_port, reg_gpinten); + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_IODIR+mcp230xx_port, reg_iodir); +#ifdef USE_MCP230xx_OUTPUT + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO+mcp230xx_port, reg_portpins); +#endif + } + for (uint32_t idx=0;idx 0) { + if (I2cValidRead8(&mcp230xx_intcap, USE_MCP230xx_ADDR, MCP230xx_INTCAP+mcp230xx_port)) { + for (uint32_t intp = 0; intp < 8; intp++) { + if ((intf >> intp) & 0x01) { + report_int = 0; + if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].pinmode > 1) { + switch (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].pinmode) { + case 2: + report_int = 1; + break; + case 3: + if (((mcp230xx_intcap >> intp) & 0x01) == 0) report_int = 1; + break; + case 4: + if (((mcp230xx_intcap >> intp) & 0x01) == 1) report_int = 1; + break; + default: + break; + } + + if ((mcp230xx_int_counter_en) && (report_int)) { + if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_count_en) { + mcp230xx_int_counter[intp+(mcp230xx_port*8)]++; + } + } + + if (report_int) { + if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_report_defer) { + mcp230xx_int_report_defer_counter[intp+(mcp230xx_port*8)]++; + if (mcp230xx_int_report_defer_counter[intp+(mcp230xx_port*8)] >= Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_report_defer) { + mcp230xx_int_report_defer_counter[intp+(mcp230xx_port*8)]=0; + } else { + report_int = 0; + } + } + } + + if (report_int) { + if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_retain_flag) { + mcp230xx_int_retainer[intp+(mcp230xx_port*8)] = 1; + report_int = 0; + } + } + if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_count_en) { + report_int = 0; + } + if (report_int) { + bool int_tele = false; + bool int_event = false; + unsigned long millis_now = millis(); + unsigned long millis_since_last_int = millis_now - int_millis[intp+(mcp230xx_port*8)]; + int_millis[intp+(mcp230xx_port*8)]=millis_now; + switch (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_report_mode) { + case 0: + int_tele=true; + int_event=true; + break; + case 1: + int_event=true; + break; + case 2: + int_tele=true; + break; + } + if (int_tele) { + 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")); + } + if (int_event) { + char command[19]; + sprintf(command,"event MCPINT_D%i=%i",intp+(mcp230xx_port*8),((mcp230xx_intcap >> intp) & 0x01)); + ExecuteCommand(command, SRC_RULE); + } + } + } + } + } + } + } + } + } +} + +void MCP230xx_Show(bool json) +{ + if (json) { + uint8_t gpio = MCP230xx_readGPIO(0); + ResponseAppend_P(PSTR(",\"MCP230XX\":{\"D0\":%i,\"D1\":%i,\"D2\":%i,\"D3\":%i,\"D4\":%i,\"D5\":%i,\"D6\":%i,\"D7\":%i"), + (gpio>>0)&1,(gpio>>1)&1,(gpio>>2)&1,(gpio>>3)&1,(gpio>>4)&1,(gpio>>5)&1,(gpio>>6)&1,(gpio>>7)&1); + if (2 == mcp230xx_type) { + gpio = MCP230xx_readGPIO(1); + ResponseAppend_P(PSTR(",\"D8\":%i,\"D9\":%i,\"D10\":%i,\"D11\":%i,\"D12\":%i,\"D13\":%i,\"D14\":%i,\"D15\":%i"), + (gpio>>0)&1,(gpio>>1)&1,(gpio>>2)&1,(gpio>>3)&1,(gpio>>4)&1,(gpio>>5)&1,(gpio>>6)&1,(gpio>>7)&1); + } + ResponseJsonEnd(); + } +} + +#ifdef USE_MCP230xx_OUTPUT + +void MCP230xx_SetOutPin(uint8_t pin,uint8_t pinstate) { + uint8_t portpins; + uint8_t port = 0; + uint8_t pinmo = Settings.mcp230xx_config[pin].pinmode; + uint8_t interlock = Settings.flag.interlock; + int pinadd = (pin % 2)+1-(3*(pin % 2)); + char cmnd[7], stt[4]; + if (pin > 7) { port = 1; } + portpins = MCP230xx_readGPIO(port); + if (interlock && (pinmo == Settings.mcp230xx_config[pin+pinadd].pinmode)) { + if (pinstate < 2) { + if (6 == pinmo) { + if (pinstate) portpins |= (1 << (pin-(port*8))); else portpins |= (1 << (pin+pinadd-(port*8))),portpins &= ~(1 << (pin-(port*8))); + } else { + if (pinstate) portpins &= ~(1 << (pin+pinadd-(port*8))),portpins |= (1 << (pin-(port*8))); else portpins &= ~(1 << (pin-(port*8))); + } + } else { + if (6 == pinmo) { + portpins |= (1 << (pin+pinadd-(port*8))),portpins ^= (1 << (pin-(port*8))); + } else { + portpins &= ~(1 << (pin+pinadd-(port*8))),portpins ^= (1 << (pin-(port*8))); + } + } + } else { + if (pinstate < 2) { + if (pinstate) portpins |= (1 << (pin-(port*8))); else portpins &= ~(1 << (pin-(port*8))); + } else { + portpins ^= (1 << (pin-(port*8))); + } + } + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO + port, portpins); + if (Settings.flag.save_state) { + Settings.mcp230xx_config[pin].saved_state=portpins>>(pin-(port*8))&1; + Settings.mcp230xx_config[pin+pinadd].saved_state=portpins>>(pin+pinadd-(port*8))&1; + } + sprintf(cmnd,ConvertNumTxt(pinstate, pinmo)); + sprintf(stt,ConvertNumTxt((portpins >> (pin-(port*8))&1), pinmo)); + if (interlock && (pinmo == Settings.mcp230xx_config[pin+pinadd].pinmode)) { + char stt1[4]; + sprintf(stt1,ConvertNumTxt((portpins >> (pin+pinadd-(port*8))&1), pinmo)); + Response_P(PSTR("{\"S29cmnd_D%i\":{\"COMMAND\":\"%s\",\"STATE\":\"%s\"},\"S29cmnd_D%i\":{\"STATE\":\"%s\"}}"),pin, cmnd, stt, pin+pinadd, stt1); + } else { + Response_P(MCP230XX_CMND_RESPONSE, pin, cmnd, stt); + } +} + +#endif + +void MCP230xx_Reset(uint8_t pinmode) { + uint8_t pullup = 0; + if ((pinmode > 1) && (pinmode < 5)) { pullup=1; } + for (uint32_t pinx=0;pinx<16;pinx++) { + Settings.mcp230xx_config[pinx].pinmode=pinmode; + Settings.mcp230xx_config[pinx].pullup=pullup; + Settings.mcp230xx_config[pinx].saved_state=0; + if ((pinmode > 1) && (pinmode < 5)) { + Settings.mcp230xx_config[pinx].int_report_mode=0; + } else { + Settings.mcp230xx_config[pinx].int_report_mode=3; + } + Settings.mcp230xx_config[pinx].int_report_defer=0; + Settings.mcp230xx_config[pinx].int_count_en=0; + Settings.mcp230xx_config[pinx].int_retain_flag=0; + Settings.mcp230xx_config[pinx].spare13=0; + Settings.mcp230xx_config[pinx].spare14=0; + Settings.mcp230xx_config[pinx].spare15=0; + } + Settings.mcp230xx_int_prio = 0; + Settings.mcp230xx_int_timer = 0; + MCP230xx_ApplySettings(); + char pulluptxt[7]; + char intmodetxt[9]; + sprintf(pulluptxt,ConvertNumTxt(pullup)); + uint8_t intmode = 3; + if ((pinmode > 1) && (pinmode < 5)) { intmode = 0; } + sprintf(intmodetxt,IntModeTxt(intmode)); + Response_P(MCP230XX_SENSOR_RESPONSE,99,pinmode,pulluptxt,intmodetxt,""); +} + +bool MCP230xx_Command(void) +{ + bool serviced = true; + bool validpin = false; + uint8_t paramcount = 0; + if (XdrvMailbox.data_len > 0) { + paramcount=1; + } else { + serviced = false; + return serviced; + } + char sub_string[XdrvMailbox.data_len]; + for (uint32_t ca=0;ca 1) { + uint8_t intpri = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if ((intpri >= 0) && (intpri <= 20)) { + Settings.mcp230xx_int_prio = intpri; + Response_P(MCP230XX_INTCFG_RESPONSE,"PRI",99,Settings.mcp230xx_int_prio); + return serviced; + } + } else { + Response_P(MCP230XX_INTCFG_RESPONSE,"PRI",99,Settings.mcp230xx_int_prio); + return serviced; + } + } + + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTTIMER")) { + if (paramcount > 1) { + uint8_t inttim = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if ((inttim >= 0) && (inttim <= 3600)) { + Settings.mcp230xx_int_timer = inttim; + MCP230xx_CheckForIntCounter(); + Response_P(MCP230XX_INTCFG_RESPONSE,"TIMER",99,Settings.mcp230xx_int_timer); + return serviced; + } + } else { + Response_P(MCP230XX_INTCFG_RESPONSE,"TIMER",99,Settings.mcp230xx_int_timer); + return serviced; + } + } + + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTDEF")) { + if (paramcount > 1) { + uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if (pin < mcp230xx_pincount) { + if (pin == 0) { + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0")) validpin=true; + } else { + validpin = true; + } + } + if (validpin) { + if (paramcount > 2) { + uint8_t intdef = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + if ((intdef >= 0) && (intdef <= 15)) { + Settings.mcp230xx_config[pin].int_report_defer=intdef; + if (Settings.mcp230xx_config[pin].int_count_en) { + Settings.mcp230xx_config[pin].int_count_en=0; + MCP230xx_CheckForIntCounter(); + AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled INTCNT for pin D%i"),pin); + } + Response_P(MCP230XX_INTCFG_RESPONSE,"DEF",pin,Settings.mcp230xx_config[pin].int_report_defer); + return serviced; + } else { + serviced=false; + return serviced; + } + } else { + Response_P(MCP230XX_INTCFG_RESPONSE,"DEF",pin,Settings.mcp230xx_config[pin].int_report_defer); + return serviced; + } + } + serviced = false; + return serviced; + } else { + serviced = false; + return serviced; + } + } + + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTCNT")) { + if (paramcount > 1) { + uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if (pin < mcp230xx_pincount) { + if (pin == 0) { + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0")) validpin=true; + } else { + validpin = true; + } + } + if (validpin) { + if (paramcount > 2) { + uint8_t intcnt = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + if ((intcnt >= 0) && (intcnt <= 1)) { + Settings.mcp230xx_config[pin].int_count_en=intcnt; + if (Settings.mcp230xx_config[pin].int_report_defer) { + Settings.mcp230xx_config[pin].int_report_defer=0; + AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled INTDEF for pin D%i"),pin); + } + if (Settings.mcp230xx_config[pin].int_report_mode < 3) { + Settings.mcp230xx_config[pin].int_report_mode=3; + AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled immediate interrupt/telemetry reporting for pin D%i"),pin); + } + if ((Settings.mcp230xx_config[pin].int_count_en) && (!Settings.mcp230xx_int_timer)) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - INTCNT enabled for pin D%i but global INTTIMER is disabled!"),pin); + } + MCP230xx_CheckForIntCounter(); + Response_P(MCP230XX_INTCFG_RESPONSE,"CNT",pin,Settings.mcp230xx_config[pin].int_count_en); + return serviced; + } else { + serviced=false; + return serviced; + } + } else { + Response_P(MCP230XX_INTCFG_RESPONSE,"CNT",pin,Settings.mcp230xx_config[pin].int_count_en); + return serviced; + } + } + serviced = false; + return serviced; + } else { + serviced = false; + return serviced; + } + } + + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTRETAIN")) { + if (paramcount > 1) { + uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if (pin < mcp230xx_pincount) { + if (pin == 0) { + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0")) validpin=true; + } else { + validpin = true; + } + } + if (validpin) { + if (paramcount > 2) { + uint8_t int_retain = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + if ((int_retain >= 0) && (int_retain <= 1)) { + Settings.mcp230xx_config[pin].int_retain_flag=int_retain; + Response_P(MCP230XX_INTCFG_RESPONSE,"INT_RETAIN",pin,Settings.mcp230xx_config[pin].int_retain_flag); + MCP230xx_CheckForIntRetainer(); + return serviced; + } else { + serviced=false; + return serviced; + } + } else { + Response_P(MCP230XX_INTCFG_RESPONSE,"INT_RETAIN",pin,Settings.mcp230xx_config[pin].int_retain_flag); + return serviced; + } + } + serviced = false; + return serviced; + } else { + serviced = false; + return serviced; + } + } + + uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1)); + + if (pin < mcp230xx_pincount) { + if (0 == pin) { + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1), "0")) validpin=true; + } else { + validpin=true; + } + } + if (validpin && (paramcount > 1)) { + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "?")) { + uint8_t port = 0; + if (pin > 7) { port = 1; } + uint8_t portdata = MCP230xx_readGPIO(port); + char pulluptxtr[7],pinstatustxtr[7]; + char intmodetxt[9]; + sprintf(intmodetxt,IntModeTxt(Settings.mcp230xx_config[pin].int_report_mode)); + sprintf(pulluptxtr,ConvertNumTxt(Settings.mcp230xx_config[pin].pullup)); +#ifdef USE_MCP230xx_OUTPUT + uint8_t pinmod = Settings.mcp230xx_config[pin].pinmode; + sprintf(pinstatustxtr,ConvertNumTxt(portdata>>(pin-(port*8))&1,pinmod)); + Response_P(MCP230XX_SENSOR_RESPONSE,pin,pinmod,pulluptxtr,intmodetxt,pinstatustxtr); +#else + sprintf(pinstatustxtr,ConvertNumTxt(portdata>>(pin-(port*8))&1)); + Response_P(MCP230XX_SENSOR_RESPONSE,pin,Settings.mcp230xx_config[pin].pinmode,pulluptxtr,intmodetxt,pinstatustxtr); +#endif + return serviced; + } +#ifdef USE_MCP230xx_OUTPUT + if (Settings.mcp230xx_config[pin].pinmode >= 5) { + uint8_t pincmd = Settings.mcp230xx_config[pin].pinmode - 5; + if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "ON")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "1"))) { + MCP230xx_SetOutPin(pin,abs(pincmd-1)); + return serviced; + } + if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "OFF")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0"))) { + MCP230xx_SetOutPin(pin,pincmd); + return serviced; + } + if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "T")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "2"))) { + MCP230xx_SetOutPin(pin,2); + return serviced; + } + } +#endif + uint8_t pinmode = 0; + uint8_t pullup = 0; + uint8_t intmode = 0; + if (paramcount > 1) { + pinmode = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + } + if (paramcount > 2) { + pullup = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + } + if (paramcount > 3) { + intmode = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4)); + } +#ifdef USE_MCP230xx_OUTPUT + if ((pin < mcp230xx_pincount) && (pinmode > 0) && (pinmode < 7) && (pullup < 2) && (paramcount > 2)) { +#else + if ((pin < mcp230xx_pincount) && (pinmode > 0) && (pinmode < 5) && (pullup < 2) && (paramcount > 2)) { +#endif + Settings.mcp230xx_config[pin].pinmode=pinmode; + Settings.mcp230xx_config[pin].pullup=pullup; + if ((pinmode > 1) && (pinmode < 5)) { + if ((intmode >= 0) && (intmode <= 3)) { + Settings.mcp230xx_config[pin].int_report_mode=intmode; + } + } else { + Settings.mcp230xx_config[pin].int_report_mode=3; + } + MCP230xx_ApplySettings(); + uint8_t port = 0; + if (pin > 7) { port = 1; } + uint8_t portdata = MCP230xx_readGPIO(port); + char pulluptxtc[7], pinstatustxtc[7]; + char intmodetxt[9]; + sprintf(pulluptxtc,ConvertNumTxt(pullup)); + sprintf(intmodetxt,IntModeTxt(Settings.mcp230xx_config[pin].int_report_mode)); +#ifdef USE_MCP230xx_OUTPUT + sprintf(pinstatustxtc,ConvertNumTxt(portdata>>(pin-(port*8))&1,Settings.mcp230xx_config[pin].pinmode)); +#else + sprintf(pinstatustxtc,ConvertNumTxt(portdata>>(pin-(port*8))&1)); +#endif + Response_P(MCP230XX_SENSOR_RESPONSE,pin,pinmode,pulluptxtc,intmodetxt,pinstatustxtc); + return serviced; + } + } else { + serviced=false; + return serviced; + } + return serviced; +} + +#ifdef USE_MCP230xx_DISPLAYOUTPUT + +const char HTTP_SNS_MCP230xx_OUTPUT[] PROGMEM = "{s}MCP230XX D%d{m}%s{e}"; + +void MCP230xx_UpdateWebData(void) +{ + uint8_t gpio1 = MCP230xx_readGPIO(0); + uint8_t gpio2 = 0; + if (2 == mcp230xx_type) { + gpio2 = MCP230xx_readGPIO(1); + } + uint16_t gpio = (gpio2 << 8) + gpio1; + for (uint32_t pin = 0; pin < mcp230xx_pincount; pin++) { + if (Settings.mcp230xx_config[pin].pinmode >= 5) { + char stt[7]; + sprintf(stt,ConvertNumTxt((gpio>>pin)&1,Settings.mcp230xx_config[pin].pinmode)); + WSContentSend_PD(HTTP_SNS_MCP230xx_OUTPUT, pin, stt); + } + } +} + +#endif + +#ifdef USE_MCP230xx_OUTPUT + +void MCP230xx_OutputTelemetry(void) +{ + uint8_t outputcount = 0; + uint16_t gpiototal = 0; + uint8_t gpioa = 0; + uint8_t gpiob = 0; + gpioa=MCP230xx_readGPIO(0); + if (2 == mcp230xx_type) { gpiob=MCP230xx_readGPIO(1); } + gpiototal=((uint16_t)gpiob << 8) | gpioa; + for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { + if (Settings.mcp230xx_config[pinx].pinmode >= 5) outputcount++; + } + if (outputcount) { + char stt[7]; + 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)); + ResponseAppend_P(PSTR("\"OUT_D%i\":\"%s\","),pinx,stt); + } + } + ResponseAppend_P(PSTR("\"END\":1}}")); + MqttPublishTeleSensor(); + } +} + +#endif + +void MCP230xx_Interrupt_Counter_Report(void) { + ResponseTime_P(PSTR(",\"MCP230_INTTIMER\":{")); + for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { + if (Settings.mcp230xx_config[pinx].int_count_en) { + ResponseAppend_P(PSTR("\"INTCNT_D%i\":%i,"),pinx,mcp230xx_int_counter[pinx]); + mcp230xx_int_counter[pinx]=0; + } + } + ResponseAppend_P(PSTR("\"END\":1}}")); + MqttPublishTeleSensor(); + mcp230xx_int_sec_counter = 0; +} + +void MCP230xx_Interrupt_Retain_Report(void) { + uint16_t retainresult = 0; + 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]); + retainresult |= (((mcp230xx_int_retainer[pinx])&1) << pinx); + mcp230xx_int_retainer[pinx]=0; + } + } + ResponseAppend_P(PSTR("\"Value\":%u}}"),retainresult); + MqttPublishTeleSensor(); +} + + + + + +bool Xsns29(uint8_t function) +{ + if (!I2cEnabled(XI2C_22)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + MCP230xx_Detect(); + } + else if (mcp230xx_type) { + switch (function) { + case FUNC_EVERY_50_MSECOND: + if (mcp230xx_int_en) { + mcp230xx_int_prio_counter++; + if ((mcp230xx_int_prio_counter) >= (Settings.mcp230xx_int_prio)) { + MCP230xx_CheckForInterrupt(); + mcp230xx_int_prio_counter=0; + } + } + break; + case FUNC_EVERY_SECOND: + if (mcp230xx_int_counter_en) { + mcp230xx_int_sec_counter++; + if (mcp230xx_int_sec_counter >= Settings.mcp230xx_int_timer) { + MCP230xx_Interrupt_Counter_Report(); + } + } + if (tele_period == 0) { + if (mcp230xx_int_retainer_en) { + MCP230xx_Interrupt_Retain_Report(); + } +#ifdef USE_MCP230xx_OUTPUT + MCP230xx_OutputTelemetry(); +#endif + } + break; + case FUNC_JSON_APPEND: + MCP230xx_Show(1); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_29 == XdrvMailbox.index) { + result = MCP230xx_Command(); + } + break; +#ifdef USE_WEBSERVER +#ifdef USE_MCP230xx_OUTPUT +#ifdef USE_MCP230xx_DISPLAYOUTPUT + case FUNC_WEB_SENSOR: + MCP230xx_UpdateWebData(); + break; +#endif +#endif +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_30_mpr121.ino" +# 46 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_30_mpr121.ino" +#ifdef USE_I2C +#ifdef USE_MPR121 + + + + + +#define XSNS_30 30 +#define XI2C_23 23 + + + + + + + +#define MPR121_ELEX_REG 0x00 + + +#define MPR121_MHDR_REG 0x2B + + +#define MPR121_MHDR_VAL 0x01 + + +#define MPR121_NHDR_REG 0x2C + + +#define MPR121_NHDR_VAL 0x01 + + +#define MPR121_NCLR_REG 0x2D + + +#define MPR121_NCLR_VAL 0x0E + + +#define MPR121_MHDF_REG 0x2F + + +#define MPR121_MHDF_VAL 0x01 + + +#define MPR121_NHDF_REG 0x30 + + +#define MPR121_NHDF_VAL 0x05 + + +#define MPR121_NCLF_REG 0x31 + + +#define MPR121_NCLF_VAL 0x01 + + +#define MPR121_MHDPROXR_REG 0x36 + + +#define MPR121_MHDPROXR_VAL 0x3F + + +#define MPR121_NHDPROXR_REG 0x37 + + +#define MPR121_NHDPROXR_VAL 0x5F + + +#define MPR121_NCLPROXR_REG 0x38 + + +#define MPR121_NCLPROXR_VAL 0x04 + + +#define MPR121_FDLPROXR_REG 0x39 + + +#define MPR121_FDLPROXR_VAL 0x00 + + +#define MPR121_MHDPROXF_REG 0x3A + + +#define MPR121_MHDPROXF_VAL 0x01 + + +#define MPR121_NHDPROXF_REG 0x3B + + +#define MPR121_NHDPROXF_VAL 0x01 + + +#define MPR121_NCLPROXF_REG 0x3C + + +#define MPR121_NCLPROXF_VAL 0x1F + + +#define MPR121_FDLPROXF_REG 0x3D + + +#define MPR121_FDLPROXF_VAL 0x04 + + +#define MPR121_E0TTH_REG 0x41 + + +#define MPR121_E0TTH_VAL 12 + + +#define MPR121_E0RTH_REG 0x42 + + +#define MPR121_E0RTH_VAL 6 + + +#define MPR121_CDT_REG 0x5D + + +#define MPR121_CDT_VAL 0x20 + + +#define MPR121_ECR_REG 0x5E + + +#define MPR121_ECR_VAL 0x8F + + + +#define MPR121_SRST_REG 0x80 + + +#define MPR121_SRST_VAL 0x63 + + +#define BITC(sensor,position) ((pS->current[sensor] >> position) & 1) + + +#define BITP(sensor,position) ((pS->previous[sensor] >> position) & 1) +# 195 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_30_mpr121.ino" +typedef struct mpr121 mpr121; +struct mpr121 { + const uint8_t i2c_addr[4] = { 0x5A, 0x5B, 0x5C, 0x5D }; + const char id[4] = { 'A', 'B', 'C', 'D' }; + bool connected[4] = { false, false, false, false }; + bool running[4] = { false, false, false, false }; + uint16_t current[4] = { 0x0000, 0x0000, 0x0000, 0x0000 }; + uint16_t previous[4] = { 0x0000, 0x0000, 0x0000, 0x0000 }; +}; + +bool mpr21_found = false; +# 217 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_30_mpr121.ino" +void Mpr121Init(struct mpr121 *pS, bool initial) +{ + + for (uint32_t i = 0; i < sizeof(pS->i2c_addr[i]); i++) { + + if (initial && I2cActive(pS->i2c_addr[i])) { continue; } + + + pS->connected[i] = (I2cWrite8(pS->i2c_addr[i], MPR121_SRST_REG, MPR121_SRST_VAL) + && (0x24 == I2cRead8(pS->i2c_addr[i], 0x5D))); + if (pS->connected[i]) { + + + mpr21_found = true; + char device_name[16]; + snprintf_P(device_name, sizeof(device_name), PSTR("MPR121(%c)"), pS->id[i]); + I2cSetActiveFound(pS->i2c_addr[i], device_name); + + + for (uint32_t j = 0; j < 13; j++) { + + + I2cWrite8(pS->i2c_addr[i], MPR121_E0TTH_REG + 2 * j, MPR121_E0TTH_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_E0RTH_REG + 2 * j, MPR121_E0RTH_VAL); + } + + + I2cWrite8(pS->i2c_addr[i], MPR121_MHDR_REG, MPR121_MHDR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NHDR_REG, MPR121_NHDR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NCLR_REG, MPR121_NCLR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_MHDF_REG, MPR121_MHDF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NHDF_REG, MPR121_NHDF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NCLF_REG, MPR121_NCLF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_MHDPROXR_REG, MPR121_MHDPROXR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NHDPROXR_REG, MPR121_NHDPROXR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NCLPROXR_REG, MPR121_NCLPROXR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_FDLPROXR_REG, MPR121_FDLPROXR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_MHDPROXF_REG, MPR121_MHDPROXF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NHDPROXF_REG, MPR121_NHDPROXF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NCLPROXF_REG, MPR121_NCLPROXF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_FDLPROXF_REG, MPR121_FDLPROXF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_CDT_REG, MPR121_CDT_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_ECR_REG, MPR121_ECR_VAL); + + + pS->running[i] = (0x00 != I2cRead8(pS->i2c_addr[i], MPR121_ECR_REG)); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_I2C "MPR121%c: %sRunning"), pS->id[i], (pS->running[i]) ? "" : "NOT"); + + } else { + + + pS->running[i] = false; + } + } + + + if (!(pS->connected[0] || pS->connected[1] || pS->connected[2] + || pS->connected[3])) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C "MPR121: No sensors found")); + } +} +# 326 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_30_mpr121.ino" +void Mpr121Show(struct mpr121 *pS, uint8_t function) +{ + + + for (uint32_t i = 0; i < sizeof(pS->i2c_addr[i]); i++) { + + + if (pS->connected[i]) { + + + if (!I2cValidRead16LE(&pS->current[i], pS->i2c_addr[i], MPR121_ELEX_REG)) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_I2C "MPR121%c: ERROR: Cannot read data!"), pS->id[i]); + Mpr121Init(pS, false); + return; + } + + if (BITC(i, 15)) { + + + I2cWrite8(pS->i2c_addr[i], MPR121_ELEX_REG, 0x00); + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_I2C "MPR121%c: ERROR: Excess current detected! Fix circuits if it happens repeatedly! Soft-resetting MPR121 ..."), pS->id[i]); + Mpr121Init(pS, false); + return; + } + } + + if (pS->running[i]) { + + + if (FUNC_JSON_APPEND == function) { + ResponseAppend_P(PSTR(",\"MPR121%c\":{"), pS->id[i]); + } + + for (uint32_t j = 0; j < 13; j++) { + + + if ((FUNC_EVERY_50_MSECOND == function) + && (BITC(i, j) != BITP(i, j))) { + Response_P(PSTR("{\"MPR121%c\":{\"Button%i\":%i}}"), pS->id[i], j, BITC(i, j)); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, mqtt_data); + } + +#ifdef USE_WEBSERVER + if (FUNC_WEB_SENSOR == function) { + WSContentSend_PD(PSTR("{s}MPR121%c Button%d{m}%d{e}"), pS->id[i], j, BITC(i, j)); + } +#endif + + + if (FUNC_JSON_APPEND == function) { + ResponseAppend_P(PSTR("%s\"Button%i\":%i"), (j > 0 ? "," : ""), j, BITC(i, j)); + } + } + + + pS->previous[i] = pS->current[i]; + + + if (FUNC_JSON_APPEND == function) { + ResponseJsonEnd(); + } + } + } +} +# 410 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_30_mpr121.ino" +bool Xsns30(uint8_t function) +{ + if (!I2cEnabled(XI2C_23)) { return false; } + + bool result = false; + + + static struct mpr121 mpr121; + + if (FUNC_INIT == function) { + + Mpr121Init(&mpr121, true); + } + else if (mpr21_found) { + + switch (function) { + + + case FUNC_EVERY_50_MSECOND: + Mpr121Show(&mpr121, FUNC_EVERY_50_MSECOND); + break; + + + case FUNC_JSON_APPEND: + Mpr121Show(&mpr121, FUNC_JSON_APPEND); + break; + +#ifdef USE_WEBSERVER + + case FUNC_WEB_SENSOR: + Mpr121Show(&mpr121, FUNC_WEB_SENSOR); + break; +#endif + } + } + + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_31_ccs811.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_31_ccs811.ino" +#ifdef USE_I2C +#ifdef USE_CCS811 +# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_31_ccs811.ino" +#define XSNS_31 31 +#define XI2C_24 24 + +#define EVERYNSECONDS 5 + +#include "Adafruit_CCS811.h" + +Adafruit_CCS811 ccs; +uint8_t CCS811_ready = 0; +uint8_t CCS811_type = 0;; +uint16_t eCO2; +uint16_t TVOC; +uint8_t tcnt = 0; +uint8_t ecnt = 0; + + + +void CCS811Detect(void) +{ + if (I2cActive(CCS811_ADDRESS)) { return; } + + if (!ccs.begin(CCS811_ADDRESS)) { + CCS811_type = 1; + I2cSetActiveFound(CCS811_ADDRESS, "CCS811"); + } +} + +void CCS811Update(void) +{ + tcnt++; + if (tcnt >= EVERYNSECONDS) { + tcnt = 0; + CCS811_ready = 0; + if (ccs.available()) { + if (!ccs.readData()){ + TVOC = ccs.getTVOC(); + eCO2 = ccs.geteCO2(); + CCS811_ready = 1; + if (global_update && global_humidity>0 && global_temperature!=9999) { ccs.setEnvironmentalData((uint8_t)global_humidity, global_temperature); } + ecnt = 0; + } + } else { + + ecnt++; + if (ecnt > 6) { + + ccs.begin(CCS811_ADDRESS); + } + } + } +} + +const char HTTP_SNS_CCS811[] PROGMEM = + "{s}CCS811 " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}" + "{s}CCS811 " D_TVOC "{m}%d " D_UNIT_PARTS_PER_BILLION "{e}"; + +void CCS811Show(bool json) +{ + if (CCS811_ready) { + if (json) { + ResponseAppend_P(PSTR(",\"CCS811\":{\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TVOC "\":%d}"), eCO2,TVOC); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, eCO2); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_CCS811, eCO2, TVOC); +#endif + } + } +} + + + + + +bool Xsns31(uint8_t function) +{ + if (!I2cEnabled(XI2C_24)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + CCS811Detect(); + } + else if (CCS811_type) { + switch (function) { + case FUNC_EVERY_SECOND: + CCS811Update(); + break; + case FUNC_JSON_APPEND: + CCS811Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + CCS811Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_32_mpu6050.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_32_mpu6050.ino" +#ifdef USE_I2C +#ifdef USE_MPU6050 +# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_32_mpu6050.ino" +#define XSNS_32 32 +#define XI2C_25 25 + +#define D_SENSOR_MPU6050 "MPU6050" + +#define MPU_6050_ADDR_AD0_LOW 0x68 +#define MPU_6050_ADDR_AD0_HIGH 0x69 + +uint8_t MPU_6050_address; +uint8_t MPU_6050_addresses[] = { MPU_6050_ADDR_AD0_LOW, MPU_6050_ADDR_AD0_HIGH }; +uint8_t MPU_6050_found; + +int16_t MPU_6050_ax = 0, MPU_6050_ay = 0, MPU_6050_az = 0; +int16_t MPU_6050_gx = 0, MPU_6050_gy = 0, MPU_6050_gz = 0; +int16_t MPU_6050_temperature = 0; + +#ifdef USE_MPU6050_DMP + #include "MPU6050_6Axis_MotionApps20.h" + #include "I2Cdev.h" + #include + typedef struct MPU6050_DMP{ + uint8_t devStatus; + uint16_t packetSize; + uint16_t fifoCount; + uint8_t fifoBuffer[64]; + Quaternion q; + VectorInt16 aa; + VectorInt16 aaReal; + VectorFloat gravity; + float euler[3]; + float yawPitchRoll[3]; + } MPU6050_DMP; + + MPU6050_DMP MPU6050_dmp; +#else + #include +#endif +MPU6050 mpu6050; + +void MPU_6050PerformReading(void) +{ +#ifdef USE_MPU6050_DMP + mpu6050.resetFIFO(); + MPU6050_dmp.fifoCount = mpu6050.getFIFOCount(); + while (MPU6050_dmp.fifoCount < MPU6050_dmp.packetSize) MPU6050_dmp.fifoCount = mpu6050.getFIFOCount(); + mpu6050.getFIFOBytes(MPU6050_dmp.fifoBuffer, MPU6050_dmp.packetSize); + MPU6050_dmp.fifoCount -= MPU6050_dmp.packetSize; + + mpu6050.dmpGetQuaternion(&MPU6050_dmp.q, MPU6050_dmp.fifoBuffer); + mpu6050.dmpGetEuler(MPU6050_dmp.euler, &MPU6050_dmp.q); + mpu6050.dmpGetAccel(&MPU6050_dmp.aa, MPU6050_dmp.fifoBuffer); + mpu6050.dmpGetGravity(&MPU6050_dmp.gravity, &MPU6050_dmp.q); + mpu6050.dmpGetLinearAccel(&MPU6050_dmp.aaReal, &MPU6050_dmp.aa, &MPU6050_dmp.gravity); + mpu6050.dmpGetYawPitchRoll(MPU6050_dmp.yawPitchRoll, &MPU6050_dmp.q, &MPU6050_dmp.gravity); + MPU_6050_gx = MPU6050_dmp.euler[0] * 180/M_PI; + MPU_6050_gy = MPU6050_dmp.euler[1] * 180/M_PI; + MPU_6050_gz = MPU6050_dmp.euler[2] * 180/M_PI; + MPU_6050_ax = MPU6050_dmp.aaReal.x; + MPU_6050_ay = MPU6050_dmp.aaReal.y; + MPU_6050_az = MPU6050_dmp.aaReal.z; +#else + mpu6050.getMotion6( + &MPU_6050_ax, + &MPU_6050_ay, + &MPU_6050_az, + &MPU_6050_gx, + &MPU_6050_gy, + &MPU_6050_gz + ); +#endif + MPU_6050_temperature = mpu6050.getTemperature(); +} +# 119 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_32_mpu6050.ino" +void MPU_6050Detect(void) +{ + for (uint32_t i = 0; i < sizeof(MPU_6050_addresses); i++) + { + MPU_6050_address = MPU_6050_addresses[i]; + if (!I2cSetDevice(MPU_6050_address)) { break; } + mpu6050.setAddr(MPU_6050_addresses[i]); + +#ifdef USE_MPU6050_DMP + MPU6050_dmp.devStatus = mpu6050.dmpInitialize(); + mpu6050.setXGyroOffset(220); + mpu6050.setYGyroOffset(76); + mpu6050.setZGyroOffset(-85); + mpu6050.setZAccelOffset(1788); + if (MPU6050_dmp.devStatus == 0) { + mpu6050.setDMPEnabled(true); + MPU6050_dmp.packetSize = mpu6050.dmpGetFIFOPacketSize(); + MPU_6050_found = true; + } +#else + mpu6050.initialize(); + MPU_6050_found = mpu6050.testConnection(); +#endif + Settings.flag2.axis_resolution = 2; + } + + if (MPU_6050_found) { + I2cSetActiveFound(MPU_6050_address, D_SENSOR_MPU6050); + } +} + +#define D_YAW "Yaw" +#define D_PITCH "Pitch" +#define D_ROLL "Roll" + +#ifdef USE_WEBSERVER +const char HTTP_SNS_AXIS[] PROGMEM = + "{s}" D_SENSOR_MPU6050 " " D_AX_AXIS "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_AY_AXIS "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_AZ_AXIS "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_GX_AXIS "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_GY_AXIS "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_GZ_AXIS "{m}%s{e}"; +#ifdef USE_MPU6050_DMP +const char HTTP_SNS_YPR[] PROGMEM = + "{s}" D_SENSOR_MPU6050 " " D_YAW "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_PITCH "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_ROLL "{m}%s{e}"; +#endif +#endif + +#define D_JSON_AXIS_AX "AccelXAxis" +#define D_JSON_AXIS_AY "AccelYAxis" +#define D_JSON_AXIS_AZ "AccelZAxis" +#define D_JSON_AXIS_GX "GyroXAxis" +#define D_JSON_AXIS_GY "GyroYAxis" +#define D_JSON_AXIS_GZ "GyroZAxis" +#define D_JSON_YAW "Yaw" +#define D_JSON_PITCH "Pitch" +#define D_JSON_ROLL "Roll" + +void MPU_6050Show(bool json) +{ + MPU_6050PerformReading(); + + double tempConv = (MPU_6050_temperature / 340.0 + 35.53); + char temperature[33]; + dtostrfd(tempConv, Settings.flag2.temperature_resolution, temperature); + char axis_ax[33]; + dtostrfd(MPU_6050_ax, Settings.flag2.axis_resolution, axis_ax); + char axis_ay[33]; + dtostrfd(MPU_6050_ay, Settings.flag2.axis_resolution, axis_ay); + char axis_az[33]; + dtostrfd(MPU_6050_az, Settings.flag2.axis_resolution, axis_az); + char axis_gx[33]; + dtostrfd(MPU_6050_gx, Settings.flag2.axis_resolution, axis_gx); + char axis_gy[33]; + dtostrfd(MPU_6050_gy, Settings.flag2.axis_resolution, axis_gy); + char axis_gz[33]; + dtostrfd(MPU_6050_gz, Settings.flag2.axis_resolution, axis_gz); +#ifdef USE_MPU6050_DMP + char axis_yaw[33]; + dtostrfd(MPU6050_dmp.yawPitchRoll[0] / PI * 180.0, Settings.flag2.axis_resolution, axis_yaw); + char axis_pitch[33]; + dtostrfd(MPU6050_dmp.yawPitchRoll[1] / PI * 180.0, Settings.flag2.axis_resolution, axis_pitch); + char axis_roll[33]; + dtostrfd(MPU6050_dmp.yawPitchRoll[2] / PI * 180.0, Settings.flag2.axis_resolution, axis_roll); +#endif + + if (json) { + char json_axis_ax[25]; + snprintf_P(json_axis_ax, sizeof(json_axis_ax), PSTR(",\"" D_JSON_AXIS_AX "\":%s"), axis_ax); + char json_axis_ay[25]; + snprintf_P(json_axis_ay, sizeof(json_axis_ay), PSTR(",\"" D_JSON_AXIS_AY "\":%s"), axis_ay); + char json_axis_az[25]; + snprintf_P(json_axis_az, sizeof(json_axis_az), PSTR(",\"" D_JSON_AXIS_AZ "\":%s"), axis_az); + char json_axis_gx[25]; + snprintf_P(json_axis_gx, sizeof(json_axis_gx), PSTR(",\"" D_JSON_AXIS_GX "\":%s"), axis_gx); + char json_axis_gy[25]; + snprintf_P(json_axis_gy, sizeof(json_axis_gy), PSTR(",\"" D_JSON_AXIS_GY "\":%s"), axis_gy); + char json_axis_gz[25]; + snprintf_P(json_axis_gz, sizeof(json_axis_gz), PSTR(",\"" D_JSON_AXIS_GZ "\":%s"), axis_gz); +#ifdef USE_MPU6050_DMP + char json_ypr_y[25]; + snprintf_P(json_ypr_y, sizeof(json_ypr_y), PSTR(",\"" D_JSON_YAW "\":%s"), axis_yaw); + char json_ypr_p[25]; + snprintf_P(json_ypr_p, sizeof(json_ypr_p), PSTR(",\"" D_JSON_PITCH "\":%s"), axis_pitch); + char json_ypr_r[25]; + snprintf_P(json_ypr_r, sizeof(json_ypr_r), PSTR(",\"" D_JSON_ROLL "\":%s"), axis_roll); + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s%s%s%s%s%s%s%s%s}"), + D_SENSOR_MPU6050, temperature, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz, + json_ypr_y, json_ypr_p, json_ypr_r); +#else + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s%s%s%s%s%s}"), + D_SENSOR_MPU6050, temperature, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz); +#endif +#ifdef USE_DOMOTICZ + DomoticzSensor(DZ_TEMP, temperature); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, D_SENSOR_MPU6050, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_AXIS, axis_ax, axis_ay, axis_az, axis_gx, axis_gy, axis_gz); +#ifdef USE_MPU6050_DMP + WSContentSend_PD(HTTP_SNS_YPR, axis_yaw, axis_pitch, axis_roll); +#endif +#endif + } +} + + + + + +bool Xsns32(uint8_t function) +{ + if (!I2cEnabled(XI2C_25)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + MPU_6050Detect(); + } + else if (MPU_6050_found) { + switch (function) { + case FUNC_EVERY_SECOND: + if (tele_period == Settings.tele_period -3) { + MPU_6050PerformReading(); + } + break; + case FUNC_JSON_APPEND: + MPU_6050Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MPU_6050Show(0); + MPU_6050PerformReading(); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_33_ds3231.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_33_ds3231.ino" +#ifdef USE_I2C +#ifdef USE_DS3231 +# 35 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_33_ds3231.ino" +#define XSNS_33 33 +#define XI2C_26 26 + + +#ifndef USE_RTC_ADDR +#define USE_RTC_ADDR 0x68 +#endif + + +#define RTC_SECONDS 0x00 +#define RTC_MINUTES 0x01 +#define RTC_HOURS 0x02 +#define RTC_DAY 0x03 +#define RTC_DATE 0x04 +#define RTC_MONTH 0x05 +#define RTC_YEAR 0x06 +#define RTC_CONTROL 0x0E +#define RTC_STATUS 0x0F + +#define OSF 7 +#define EOSC 7 +#define BBSQW 6 +#define CONV 5 +#define RS2 4 +#define RS1 3 +#define INTCN 2 + + +#define HR1224 6 +#define CENTURY 7 +#define DYDT 6 +bool ds3231ReadStatus = false; +bool ds3231WriteStatus = false; +bool DS3231chipDetected = false; + + + + +void DS3231Detect(void) +{ + if (I2cActive(USE_RTC_ADDR)) { return; } + + if (I2cValidRead(USE_RTC_ADDR, RTC_STATUS, 1)) { + I2cSetActiveFound(USE_RTC_ADDR, "DS3231"); + DS3231chipDetected = true; + } +} + + + + +uint8_t bcd2dec(uint8_t n) +{ + return n - 6 * (n >> 4); +} + + + + +uint8_t dec2bcd(uint8_t n) +{ + return n + 6 * (n / 10); +} + + + + +uint32_t ReadFromDS3231(void) +{ + TIME_T tm; + tm.second = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_SECONDS)); + tm.minute = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_MINUTES)); + tm.hour = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_HOURS) & ~_BV(HR1224)); + tm.day_of_week = I2cRead8(USE_RTC_ADDR, RTC_DAY); + tm.day_of_month = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_DATE)); + tm.month = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_MONTH) & ~_BV(CENTURY)); + tm.year = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_YEAR)); + return MakeTime(tm); +} + + + +void SetDS3231Time (uint32_t epoch_time) { + TIME_T tm; + BreakTime(epoch_time, tm); + I2cWrite8(USE_RTC_ADDR, RTC_SECONDS, dec2bcd(tm.second)); + I2cWrite8(USE_RTC_ADDR, RTC_MINUTES, dec2bcd(tm.minute)); + I2cWrite8(USE_RTC_ADDR, RTC_HOURS, dec2bcd(tm.hour)); + I2cWrite8(USE_RTC_ADDR, RTC_DAY, tm.day_of_week); + I2cWrite8(USE_RTC_ADDR, RTC_DATE, dec2bcd(tm.day_of_month)); + I2cWrite8(USE_RTC_ADDR, RTC_MONTH, dec2bcd(tm.month)); + I2cWrite8(USE_RTC_ADDR, RTC_YEAR, dec2bcd(tm.year)); + I2cWrite8(USE_RTC_ADDR, RTC_STATUS, I2cRead8(USE_RTC_ADDR, RTC_STATUS) & ~_BV(OSF)); +} + +void DS3231EverySecond(void) +{ + TIME_T tmpTime; + if (!ds3231ReadStatus && Rtc.utc_time < START_VALID_TIME ) { + ntp_force_sync = true; + Rtc.utc_time = ReadFromDS3231(); + + + BreakTime(Rtc.utc_time, tmpTime); + if (Rtc.utc_time < START_VALID_TIME ) { + ds3231ReadStatus = true; + } + RtcTime.year = tmpTime.year + 1970; + Rtc.daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year); + Rtc.standard_time = RuleToTime(Settings.tflag[0], RtcTime.year); + AddLog_P2(LOG_LEVEL_INFO, PSTR("Set time from DS3231 to RTC (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), + GetDateAndTime(DT_UTC).c_str(), GetDateAndTime(DT_DST).c_str(), GetDateAndTime(DT_STD).c_str()); + if (Rtc.local_time < START_VALID_TIME) { + rules_flag.time_init = 1; + } else { + rules_flag.time_set = 1; + } + } + else if (!ds3231WriteStatus && Rtc.utc_time > START_VALID_TIME && abs(Rtc.utc_time - ReadFromDS3231()) > 60) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("Write Time TO DS3231 from NTP (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), + GetDateAndTime(DT_UTC).c_str(), GetDateAndTime(DT_DST).c_str(), GetDateAndTime(DT_STD).c_str()); + SetDS3231Time (Rtc.utc_time); + ds3231WriteStatus = true; + } +} + + + + + +bool Xsns33(uint8_t function) +{ + if (!I2cEnabled(XI2C_26)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + DS3231Detect(); + } + else if (DS3231chipDetected) { + switch (function) { + case FUNC_EVERY_SECOND: + DS3231EverySecond(); + break; + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_34_hx711.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_34_hx711.ino" +#ifdef USE_HX711 +# 35 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_34_hx711.ino" +#define XSNS_34 34 + +#ifndef HX_MAX_WEIGHT +#define HX_MAX_WEIGHT 20000 +#endif +#ifndef HX_REFERENCE +#define HX_REFERENCE 250 +#endif +#ifndef HX_SCALE +#define HX_SCALE 120 +#endif + +#define HX_TIMEOUT 120 +#define HX_SAMPLES 10 +#define HX_CAL_TIMEOUT 15 + +#define HX_GAIN_128 1 +#define HX_GAIN_32 2 +#define HX_GAIN_64 3 + +#define D_JSON_WEIGHT_REF "WeightRef" +#define D_JSON_WEIGHT_CAL "WeightCal" +#define D_JSON_WEIGHT_MAX "WeightMax" +#define D_JSON_WEIGHT_ITEM "WeightItem" +#define D_JSON_WEIGHT_CHANGE "WeightChange" +#define D_JSON_WEIGHT_RAW "WeightRaw" +#define D_JSON_WEIGHT_DELTA "WeightDelta" + +enum HxCalibrationSteps { HX_CAL_END, HX_CAL_LIMBO, HX_CAL_FINISH, HX_CAL_FAIL, HX_CAL_DONE, HX_CAL_FIRST, HX_CAL_RESET, HX_CAL_START }; + +const char kHxCalibrationStates[] PROGMEM = D_HX_CAL_FAIL "|" D_HX_CAL_DONE "|" D_HX_CAL_REFERENCE "|" D_HX_CAL_REMOVE; + +struct HX { + long weight = 0; + long raw = 0; + long last_weight = 0; + long sum_weight = 0; + long sum_raw = 0; + long offset = 0; + long scale = 1; + long weight_diff = 0; + uint8_t type = 1; + uint8_t sample_count = 0; + uint8_t calibrate_step = HX_CAL_END; + uint8_t calibrate_timer = 0; + uint8_t calibrate_msg = 0; + uint8_t pin_sck; + uint8_t pin_dout; + bool tare_flg = false; + bool weight_changed = false; + uint16_t weight_delta = 4; +} Hx; + + + +bool HxIsReady(uint16_t timeout) +{ + + uint32_t start = millis(); + while ((digitalRead(Hx.pin_dout) == HIGH) && (millis() - start < timeout)) { yield(); } + return (digitalRead(Hx.pin_dout) == LOW); +} + +long HxRead(void) +{ + if (!HxIsReady(HX_TIMEOUT)) { return -1; } + + uint8_t data[3] = { 0 }; + uint8_t filler = 0x00; + + + data[2] = shiftIn(Hx.pin_dout, Hx.pin_sck, MSBFIRST); + data[1] = shiftIn(Hx.pin_dout, Hx.pin_sck, MSBFIRST); + data[0] = shiftIn(Hx.pin_dout, Hx.pin_sck, MSBFIRST); + + + for (unsigned int i = 0; i < HX_GAIN_128; i++) { + digitalWrite(Hx.pin_sck, HIGH); + digitalWrite(Hx.pin_sck, LOW); + } + + + if (data[2] & 0x80) { filler = 0xFF; } + + + unsigned long value = ( static_cast(filler) << 24 + | static_cast(data[2]) << 16 + | static_cast(data[1]) << 8 + | static_cast(data[0]) ); + + return static_cast(value); +} + + + +void HxResetPart(void) +{ + Hx.tare_flg = true; + Hx.sum_weight = 0; + Hx.sample_count = 0; + Hx.last_weight = 0; +} + +void HxReset(void) +{ + HxResetPart(); + Settings.energy_frequency_calibration = 0; +} + +void HxCalibrationStateTextJson(uint8_t msg_id) +{ + char cal_text[30]; + + Hx.calibrate_msg = msg_id; + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, GetTextIndexed(cal_text, sizeof(cal_text), Hx.calibrate_msg, kHxCalibrationStates)); + + if (msg_id < 3) { MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("Sensor34")); } +} + +void SetWeightDelta() +{ + + if (Settings.weight_change == 0) { + Hx.weight_delta = 4; + return; + } + + + if (Settings.weight_change > 100) { + Hx.weight_delta = (Settings.weight_change - 100) * 10 + 100; + return; + } + + + Hx.weight_delta = Settings.weight_change - 1; +} +# 192 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_34_hx711.ino" +bool HxCommand(void) +{ + bool serviced = true; + bool show_parms = false; + char sub_string[XdrvMailbox.data_len +1]; + + for (uint32_t ca = 0; ca < XdrvMailbox.data_len; ca++) { + if ((' ' == XdrvMailbox.data[ca]) || ('=' == XdrvMailbox.data[ca])) { XdrvMailbox.data[ca] = ','; } + } + + switch (XdrvMailbox.payload) { + case 1: + HxReset(); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, "Reset"); + break; + case 2: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.weight_reference = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + } + Hx.scale = 1; + HxReset(); + Hx.calibrate_step = HX_CAL_START; + Hx.calibrate_timer = 1; + HxCalibrationStateTextJson(3); + break; + case 3: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.weight_reference = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + } + show_parms = true; + break; + case 4: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.weight_calibration = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + Hx.scale = Settings.weight_calibration; + } + show_parms = true; + break; + case 5: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.weight_max = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10) / 1000; + } + show_parms = true; + break; + case 6: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.weight_item = (unsigned long)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)) * 10); + } + show_parms = true; + break; + case 7: + Settings.energy_frequency_calibration = Hx.weight; + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, D_JSON_DONE); + break; + case 8: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.SensorBits1.hx711_json_weight_change = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10) & 1; + } + show_parms = true; + break; + case 9: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.weight_change = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + SetWeightDelta(); + } + show_parms = true; + break; + default: + show_parms = true; + } + + if (show_parms) { + char item[33]; + dtostrfd((float)Settings.weight_item / 10, 1, item); + Response_P(PSTR("{\"Sensor34\":{\"" D_JSON_WEIGHT_REF "\":%d,\"" D_JSON_WEIGHT_CAL "\":%d,\"" D_JSON_WEIGHT_MAX "\":%d,\"" + D_JSON_WEIGHT_ITEM "\":%s,\"" D_JSON_WEIGHT_CHANGE "\":%s,\"" D_JSON_WEIGHT_DELTA "\":%d}}"), + Settings.weight_reference, Settings.weight_calibration, Settings.weight_max * 1000, + item, GetStateText(Settings.SensorBits1.hx711_json_weight_change), Settings.weight_change); + } + + return serviced; +} + + + +long HxWeight(void) +{ + return (Hx.calibrate_step < HX_CAL_FAIL) ? Hx.weight : 0; +} + +void HxInit(void) +{ + Hx.type = 0; + if ((pin[GPIO_HX711_DAT] < 99) && (pin[GPIO_HX711_SCK] < 99)) { + Hx.pin_sck = pin[GPIO_HX711_SCK]; + Hx.pin_dout = pin[GPIO_HX711_DAT]; + + pinMode(Hx.pin_sck, OUTPUT); + pinMode(Hx.pin_dout, INPUT); + + digitalWrite(Hx.pin_sck, LOW); + + SetWeightDelta(); + + if (HxIsReady(8 * HX_TIMEOUT)) { + if (!Settings.weight_max) { Settings.weight_max = HX_MAX_WEIGHT / 1000; } + if (!Settings.weight_calibration) { Settings.weight_calibration = HX_SCALE; } + if (!Settings.weight_reference) { Settings.weight_reference = HX_REFERENCE; } + Hx.scale = Settings.weight_calibration; + HxRead(); + HxResetPart(); + Hx.type = 1; + } + } +} + +void HxEvery100mSecond(void) +{ + long raw = HxRead(); + Hx.sum_raw += raw; + Hx.sum_weight += raw; + + Hx.sample_count++; + if (HX_SAMPLES == Hx.sample_count) { + long average = Hx.sum_weight / Hx.sample_count; + long raw_average = Hx.sum_raw / Hx.sample_count; + long value = average - Hx.offset; + Hx.weight = value / Hx.scale; + Hx.raw = raw_average / Hx.scale; + if (Hx.weight < 0) { + if (Settings.energy_frequency_calibration) { + long difference = Settings.energy_frequency_calibration + Hx.weight; + Hx.last_weight = difference; + if (difference < 0) { HxReset(); } + } + Hx.weight = 0; + } else { + Hx.last_weight = Settings.energy_frequency_calibration; + } + + if (Hx.tare_flg) { + Hx.tare_flg = false; + Hx.offset = average; + } + + if (Hx.calibrate_step) { + Hx.calibrate_timer--; + + if (HX_CAL_START == Hx.calibrate_step) { + Hx.calibrate_step--; + Hx.calibrate_timer = HX_CAL_TIMEOUT * (10 / HX_SAMPLES); + } + else if (HX_CAL_RESET == Hx.calibrate_step) { + if (Hx.calibrate_timer) { + if (Hx.weight < (long)Settings.weight_reference) { + Hx.calibrate_step--; + Hx.calibrate_timer = HX_CAL_TIMEOUT * (10 / HX_SAMPLES); + HxCalibrationStateTextJson(2); + } + } else { + Hx.calibrate_step = HX_CAL_FAIL; + } + } + else if (HX_CAL_FIRST == Hx.calibrate_step) { + if (Hx.calibrate_timer) { + if (Hx.weight > (long)Settings.weight_reference) { + Hx.calibrate_step--; + } + } else { + Hx.calibrate_step = HX_CAL_FAIL; + } + } + else if (HX_CAL_DONE == Hx.calibrate_step) { + if (Hx.weight > (long)Settings.weight_reference) { + Hx.calibrate_step = HX_CAL_FINISH; + Settings.weight_calibration = Hx.weight / Settings.weight_reference; + Hx.weight = 0; + HxCalibrationStateTextJson(1); + } else { + Hx.calibrate_step = HX_CAL_FAIL; + } + } + + if (HX_CAL_FAIL == Hx.calibrate_step) { + Hx.calibrate_step--; + Hx.tare_flg = true; + HxCalibrationStateTextJson(0); + } + if (HX_CAL_FINISH == Hx.calibrate_step) { + Hx.calibrate_step--; + Hx.calibrate_timer = 3 * (10 / HX_SAMPLES); + Hx.scale = Settings.weight_calibration; + } + + if (!Hx.calibrate_timer) { + Hx.calibrate_step = HX_CAL_END; + } + } else { + Hx.weight += Hx.last_weight; + + if (Settings.SensorBits1.hx711_json_weight_change) { + if (abs(Hx.weight - Hx.weight_diff) > Hx.weight_delta) { + Hx.weight_diff = Hx.weight; + Hx.weight_changed = true; + } + else if (Hx.weight_changed && (Hx.weight == Hx.weight_diff)) { + mqtt_data[0] = '\0'; + ResponseAppendTime(); + HxShow(true); + ResponseJsonEnd(); + MqttPublishTeleSensor(); + Hx.weight_changed = false; + } + } + } + + Hx.sum_weight = 0; + Hx.sum_raw = 0; + Hx.sample_count = 0; + } +} + +void HxSaveBeforeRestart(void) +{ + Settings.energy_frequency_calibration = Hx.weight; + Hx.sample_count = HX_SAMPLES +1; +} + +#ifdef USE_WEBSERVER +const char HTTP_HX711_WEIGHT[] PROGMEM = + "{s}HX711 " D_WEIGHT "{m}%s " D_UNIT_KILOGRAM "{e}"; +const char HTTP_HX711_COUNT[] PROGMEM = + "{s}HX711 " D_COUNT "{m}%d{e}"; +const char HTTP_HX711_CAL[] PROGMEM = + "{s}HX711 %s{m}{e}"; +#endif + +void HxShow(bool json) +{ + char scount[30] = { 0 }; + + uint16_t count = 0; + float weight = 0; + if (Hx.calibrate_step < HX_CAL_FAIL) { + if (Hx.weight && Settings.weight_item) { + count = (Hx.weight * 10) / Settings.weight_item; + if (count > 1) { + snprintf_P(scount, sizeof(scount), PSTR(",\"" D_JSON_COUNT "\":%d"), count); + } + } + weight = (float)Hx.weight / 1000; + } + char weight_chr[33]; + dtostrfd(weight, Settings.flag2.weight_resolution, weight_chr); + + if (json) { + ResponseAppend_P(PSTR(",\"HX711\":{\"" D_JSON_WEIGHT "\":%s%s, \"" D_JSON_WEIGHT_RAW "\":%d}"), weight_chr, scount, Hx.raw); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_HX711_WEIGHT, weight_chr); + if (count > 1) { + WSContentSend_PD(HTTP_HX711_COUNT, count); + } + if (Hx.calibrate_step) { + char cal_text[30]; + WSContentSend_PD(HTTP_HX711_CAL, GetTextIndexed(cal_text, sizeof(cal_text), Hx.calibrate_msg, kHxCalibrationStates)); + } +#endif + } +} + +#ifdef USE_WEBSERVER +#ifdef USE_HX711_GUI + + + + +#define WEB_HANDLE_HX711 "s34" + +const char S_CONFIGURE_HX711[] PROGMEM = D_CONFIGURE_HX711; + +const char HTTP_BTN_MENU_MAIN_HX711[] PROGMEM = + "

"; + +const char HTTP_BTN_MENU_HX711[] PROGMEM = + "

"; + +const char HTTP_FORM_HX711[] PROGMEM = + "
 " D_CALIBRATION " " + "
" + "

" D_REFERENCE_WEIGHT " (" D_UNIT_KILOGRAM ")

" + "
" + "
" + "


" + + "
 " D_HX711_PARAMETERS " " + "
" + "

" D_ITEM_WEIGHT " (" D_UNIT_KILOGRAM ")

"; + +void HandleHxAction(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_HX711); + + if (Webserver->hasArg("save")) { + HxSaveSettings(); + HandleConfiguration(); + return; + } + + char stemp1[20]; + + if (Webserver->hasArg("reset")) { + snprintf_P(stemp1, sizeof(stemp1), PSTR("Sensor34 1")); + ExecuteWebCommand(stemp1, SRC_WEBGUI); + + HandleRoot(); + return; + } + + if (Webserver->hasArg("calibrate")) { + WebGetArg("p1", stemp1, sizeof(stemp1)); + Settings.weight_reference = (!strlen(stemp1)) ? 0 : (unsigned long)(CharToFloat(stemp1) * 1000); + + HxLogUpdates(); + + snprintf_P(stemp1, sizeof(stemp1), PSTR("Sensor34 2")); + ExecuteWebCommand(stemp1, SRC_WEBGUI); + + HandleRoot(); + return; + } + + WSContentStart_P(S_CONFIGURE_HX711); + WSContentSendStyle(); + dtostrfd((float)Settings.weight_reference / 1000, 3, stemp1); + char stemp2[20]; + dtostrfd((float)Settings.weight_item / 10000, 4, stemp2); + WSContentSend_P(HTTP_FORM_HX711, stemp1, stemp2); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void HxSaveSettings(void) +{ + char tmp[100]; + + WebGetArg("p2", tmp, sizeof(tmp)); + Settings.weight_item = (!strlen(tmp)) ? 0 : (unsigned long)(CharToFloat(tmp) * 10000); + + HxLogUpdates(); +} + +void HxLogUpdates(void) +{ + char weigth_ref_chr[33]; + dtostrfd((float)Settings.weight_reference / 1000, Settings.flag2.weight_resolution, weigth_ref_chr); + char weigth_item_chr[33]; + dtostrfd((float)Settings.weight_item / 10000, 4, weigth_item_chr); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_JSON_WEIGHT_REF " %s, " D_JSON_WEIGHT_ITEM " %s"), weigth_ref_chr, weigth_item_chr); +} + +#endif +#endif + + + + + +bool Xsns34(uint8_t function) +{ + bool result = false; + + if (Hx.type) { + switch (function) { + case FUNC_EVERY_100_MSECOND: + HxEvery100mSecond(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_34 == XdrvMailbox.index) { + result = HxCommand(); + } + break; + case FUNC_JSON_APPEND: + HxShow(1); + break; + case FUNC_SAVE_BEFORE_RESTART: + HxSaveBeforeRestart(); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + HxShow(0); + break; +#ifdef USE_HX711_GUI + case FUNC_WEB_ADD_MAIN_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_MAIN_HX711); + break; + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_HX711); + break; + case FUNC_WEB_ADD_HANDLER: + Webserver->on("/" WEB_HANDLE_HX711, HandleHxAction); + break; +#endif +#endif + case FUNC_INIT: + HxInit(); + break; + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_35_tx20.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_35_tx20.ino" +#if defined(USE_TX20_WIND_SENSOR) || defined(USE_TX23_WIND_SENSOR) +# 51 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_35_tx20.ino" +#define XSNS_35 35 + +#if defined(USE_TX20_WIND_SENSOR) && defined(USE_TX23_WIND_SENSOR) +#undef USE_TX20_WIND_SENSOR +#warning **** use USE_TX20_WIND_SENSOR or USE_TX23_WIND_SENSOR but not both together, TX20 disabled **** +#endif + + +#define TX2X_BIT_TIME 1220 +#define TX2X_WEIGHT_AVG_SAMPLE 150 +#define TX2X_TIMEOUT 10 +#define TX23_READ_INTERVAL 4 + + + +extern "C" { +#include "gpio.h" +} + +#ifdef USE_TX20_WIND_SENSOR +#undef D_TX2x_NAME +#define D_TX2x_NAME "TX20" +#else +#undef D_TX2x_NAME +#define D_TX2x_NAME "TX23" +#endif + +#ifdef USE_WEBSERVER +#define D_TX20_WIND_AVG "∅" +#define D_TX20_WIND_ANGLE "∠" +#define D_TX20_WIND_DEGREE "°" +const char HTTP_SNS_TX2X[] PROGMEM = + "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED "{m}%s %s{e}" +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED " " D_TX20_WIND_AVG "{m}%s %s{e}" + "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED_MIN "{m}%s %s{e}" + "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED_MAX "{m}%s %s{e}" +#endif + "{s}" D_TX2x_NAME " " D_TX20_WIND_DIRECTION "{m}%s %s" D_TX20_WIND_DEGREE "{e}" +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + "{s}" D_TX2x_NAME " " D_TX20_WIND_DIRECTION " " D_TX20_WIND_AVG "{m}%s %s" D_TX20_WIND_DEGREE "{e}" + "{s}" D_TX2x_NAME " " D_TX20_WIND_DIRECTION " " D_TX20_WIND_ANGLE "{m}%s" D_TX20_WIND_DEGREE " (%s,%s)" D_TX20_WIND_DEGREE; +#endif + ; +#endif + + +float const tx2x_f_pi = 3.1415926535897932384626433; +float const tx2x_f_halfpi = tx2x_f_pi / 2.0; +float const tx2x_f_pi180 = tx2x_f_pi / 180.0; + +#define TX2X_DIRECTIONS_MAXSIZE 3 +const char kTx2xDirections[] PROGMEM = D_TX20_NORTH "|" + D_TX20_NORTH D_TX20_NORTH D_TX20_EAST "|" + D_TX20_NORTH D_TX20_EAST "|" + D_TX20_EAST D_TX20_NORTH D_TX20_EAST "|" + D_TX20_EAST "|" + D_TX20_EAST D_TX20_SOUTH D_TX20_EAST "|" + D_TX20_SOUTH D_TX20_EAST "|" + D_TX20_SOUTH D_TX20_SOUTH D_TX20_EAST "|" + D_TX20_SOUTH "|" + D_TX20_SOUTH D_TX20_SOUTH D_TX20_WEST "|" + D_TX20_SOUTH D_TX20_WEST "|" + D_TX20_WEST D_TX20_SOUTH D_TX20_WEST "|" + D_TX20_WEST "|" + D_TX20_WEST D_TX20_NORTH D_TX20_WEST "|" + D_TX20_NORTH D_TX20_WEST "|" + D_TX20_NORTH D_TX20_NORTH D_TX20_WEST; + +int32_t tx2x_wind_speed = 0; +int32_t tx2x_wind_direction = 0; + +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS +int32_t tx2x_wind_speed_min = 0xfff; +int32_t tx2x_wind_speed_max = 0; +float tx2x_wind_speed_avg = 0; +float tx2x_wind_direction_avg_x = 0; +float tx2x_wind_direction_avg_y = 0; +float tx2x_wind_direction_avg = 0; +int32_t tx2x_wind_direction_min = 0; +int32_t tx2x_wind_direction_max = 0; + +uint32_t tx2x_count = 0; +uint32_t tx2x_avg_samples; +uint32_t tx2x_last_uptime = 0; +bool tx2x_valuesread = false; +#endif + +#ifdef DEBUG_TASMOTA_SENSOR +uint32_t tx2x_sa = 0; +uint32_t tx2x_sb = 0; +uint32_t tx2x_sc = 0; +uint32_t tx2x_sd = 0; +uint32_t tx2x_se = 0; +uint32_t tx2x_sf = 0; +#endif +uint32_t tx2x_last_available = 0; + +#ifdef USE_TX23_WIND_SENSOR +uint32_t tx23_stage = 0; +#endif + +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 +void TX2xStartRead(void) ICACHE_RAM_ATTR; +#endif + +void TX2xStartRead(void) +{ +# 184 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_35_tx20.ino" +#ifdef USE_TX23_WIND_SENSOR + if (0!=tx23_stage) + { + if ((2==tx23_stage) || (3==tx23_stage)) + { +#endif +#ifdef DEBUG_TASMOTA_SENSOR + tx2x_sa = 0; + tx2x_sb = 0; + tx2x_sc = 0; + tx2x_sd = 0; + tx2x_se = 0; + tx2x_sf = 0; +#else + uint32_t tx2x_sa = 0; + uint32_t tx2x_sb = 0; + uint32_t tx2x_sc = 0; + uint32_t tx2x_sd = 0; + uint32_t tx2x_se = 0; + uint32_t tx2x_sf = 0; +#endif + + delayMicroseconds(TX2X_BIT_TIME / 2); + + for (int32_t bitcount = 41; bitcount > 0; bitcount--) { + uint32_t dpin = (digitalRead(pin[GPIO_TX2X_TXD_BLACK])); +#ifdef USE_TX23_WIND_SENSOR + dpin ^= 1; +#endif + if (bitcount > 41 - 5) { + + tx2x_sa = (tx2x_sa << 1) | (dpin ^ 1); + } else if (bitcount > 41 - 5 - 4) { + + tx2x_sb = tx2x_sb >> 1 | ((dpin ^ 1) << 3); + } else if (bitcount > 41 - 5 - 4 - 12) { + + tx2x_sc = tx2x_sc >> 1 | ((dpin ^ 1) << 11); + } else if (bitcount > 41 - 5 - 4 - 12 - 4) { + + tx2x_sd = tx2x_sd >> 1 | ((dpin ^ 1) << 3); + } else if (bitcount > 41 - 5 - 4 - 12 - 4 - 4) { + + tx2x_se = tx2x_se >> 1 | (dpin << 3); + } else { + + tx2x_sf = tx2x_sf >> 1 | (dpin << 11); + } + delayMicroseconds(TX2X_BIT_TIME); + } + + uint32_t chk = (tx2x_sb + (tx2x_sc & 0xf) + ((tx2x_sc >> 4) & 0xf) + ((tx2x_sc >> 8) & 0xf)); + chk &= 0xf; + + + ; +#ifdef USE_TX23_WIND_SENSOR + if ((chk == tx2x_sd) && (0x1b==tx2x_sa) && (tx2x_sb==tx2x_se) && (tx2x_sc==tx2x_sf) && (tx2x_sc < 511)) { +#else + if ((chk == tx2x_sd) && (tx2x_sb==tx2x_se) && (tx2x_sc==tx2x_sf) && (tx2x_sc < 511)) { +#endif + tx2x_last_available = uptime; + + tx2x_wind_speed = tx2x_sc; + tx2x_wind_direction = tx2x_sb; +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + if (!tx2x_valuesread) { + tx2x_wind_direction_min = tx2x_wind_direction; + tx2x_wind_direction_max = tx2x_wind_direction; + tx2x_valuesread = true; + } +#endif + } + +#ifdef USE_TX23_WIND_SENSOR + } + tx23_stage++; + } +#endif + + + + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << pin[GPIO_TX2X_TXD_BLACK]); +} + +bool Tx2xAvailable(void) +{ + return ((uptime - tx2x_last_available) < TX2X_TIMEOUT); +} + +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS +float atan2f(float a, float b) +{ + float atan2val; + if (b > 0) { + atan2val = atanf(a/b); + } else if ((b < 0) && (a >= 0)) { + atan2val = atanf(a/b) + tx2x_f_pi; + } else if ((b < 0) && (a < 0)) { + atan2val = atanf(a/b) - tx2x_f_pi; + } else if ((b == 0) && (a > 0)) { + atan2val = tx2x_f_halfpi; + } else if ((b == 0) && (a < 0)) { + atan2val = 0 - (tx2x_f_halfpi); + } else if ((b == 0) && (a == 0)) { + atan2val = 1000; + } + return atan2val; +} + +void Tx2xCheckSampleCount(void) +{ + uint32_t tx2x_prev_avg_samples = tx2x_avg_samples; + if (Settings.tele_period) { + + tx2x_avg_samples = Settings.tele_period; + } else { + + tx2x_avg_samples = TX2X_WEIGHT_AVG_SAMPLE; + } + if (tx2x_prev_avg_samples != tx2x_avg_samples) { + tx2x_wind_speed_avg = tx2x_wind_speed; + tx2x_count = 0; + } +} + +void Tx2xResetStat(void) +{ + DEBUG_SENSOR_LOG(PSTR(D_TX2x_NAME ": reset statistics")); + tx2x_last_uptime = uptime; + Tx2xResetStatData(); +} + +void Tx2xResetStatData(void) +{ + tx2x_wind_speed_min = tx2x_wind_speed; + tx2x_wind_speed_max = tx2x_wind_speed; + + tx2x_wind_direction_min = tx2x_wind_direction; + tx2x_wind_direction_max = tx2x_wind_direction; +} +#endif + +void Tx2xRead(void) +{ +#ifdef USE_TX23_WIND_SENSOR + + + + + + + + if ((uptime % TX23_READ_INTERVAL)==0) { + + + tx23_stage = 0; + pinMode(pin[GPIO_TX2X_TXD_BLACK], OUTPUT); + digitalWrite(pin[GPIO_TX2X_TXD_BLACK], LOW); + } else if ((uptime % TX23_READ_INTERVAL)==1) { + + + tx23_stage = 1; + pinMode(pin[GPIO_TX2X_TXD_BLACK], INPUT_PULLUP); + } +#endif + if (Tx2xAvailable()) { +#ifdef DEBUG_TASMOTA_SENSOR + DEBUG_SENSOR_LOG(PSTR(D_TX2x_NAME ": sa=0x%02lx sb=%ld (0x%02lx), sc=%ld (0x%03lx), sd=0x%02lx, se=%ld, sf=%ld"), tx2x_sa,tx2x_sb,tx2x_sb,tx2x_sc,tx2x_sc,tx2x_sd,tx2x_se,tx2x_sf); +#endif +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + if (tx2x_wind_speed < tx2x_wind_speed_min) { + tx2x_wind_speed_min = tx2x_wind_speed; + } + if (tx2x_wind_speed > tx2x_wind_speed_max) { + tx2x_wind_speed_max = tx2x_wind_speed; + } + + + + + if (tx2x_count <= tx2x_avg_samples) { + tx2x_count++; + } + tx2x_wind_speed_avg -= tx2x_wind_speed_avg / tx2x_count; + tx2x_wind_speed_avg += float(tx2x_wind_speed) / tx2x_count; + + tx2x_wind_direction_avg_x -= tx2x_wind_direction_avg_x / tx2x_count; + tx2x_wind_direction_avg_x += cosf((tx2x_wind_direction*22.5) * tx2x_f_pi180) / tx2x_count; + tx2x_wind_direction_avg_y -= tx2x_wind_direction_avg_y / tx2x_count; + tx2x_wind_direction_avg_y += sinf((tx2x_wind_direction*22.5) * tx2x_f_pi180) / tx2x_count; + tx2x_wind_direction_avg = atan2f(tx2x_wind_direction_avg_y, tx2x_wind_direction_avg_x) * 180.0f / tx2x_f_pi; + if (tx2x_wind_direction_avg<0.0) { + tx2x_wind_direction_avg += 360.0; + } + if (tx2x_wind_direction_avg>360.0) { + tx2x_wind_direction_avg -= 360.0; + } + + int32_t tx2x_wind_direction_avg_int = int((tx2x_wind_direction_avg/22.5)+0.5) % 16; + + + if (tx2x_wind_direction > tx2x_wind_direction_avg_int) { + + if ((tx2x_wind_direction-tx2x_wind_direction_avg_int)>8) { + + if ((tx2x_wind_direction - 16) < tx2x_wind_direction_min) { + + tx2x_wind_direction_min = tx2x_wind_direction - 16; + } + } else { + + if (tx2x_wind_direction > tx2x_wind_direction_max) { + + tx2x_wind_direction_max = tx2x_wind_direction; + } + } + } else { + + if ((tx2x_wind_direction_avg_int-tx2x_wind_direction)>8) { + + if ((tx2x_wind_direction + 16) > tx2x_wind_direction_max) { + + tx2x_wind_direction_max = tx2x_wind_direction + 16; + } + } else { + + if (tx2x_wind_direction < tx2x_wind_direction_min) { + + tx2x_wind_direction_min = tx2x_wind_direction; + } + } + } + +#ifdef DEBUG_TASMOTA_SENSOR + char diravg[FLOATSZ]; + dtostrfd(tx2x_wind_direction_avg, 1, diravg); + char cosx[FLOATSZ]; + dtostrfd(tx2x_wind_direction_avg_x, 1, cosx); + char siny[FLOATSZ]; + dtostrfd(tx2x_wind_direction_avg_y, 1, siny); + DEBUG_SENSOR_LOG(PSTR(D_TX2x_NAME ": dir stat - counter=%ld, actint=%ld, avgint=%ld, avg=%s (cosx=%s, siny=%s), min %d, max %d"), + (uptime-tx2x_last_uptime), + tx2x_wind_direction, + tx2x_wind_direction_avg_int, + diravg, + cosx, + siny, + tx2x_wind_direction_min, + tx2x_wind_direction_max + ); +#endif +#endif + } else { + DEBUG_SENSOR_LOG(PSTR(D_TX2x_NAME ": not available")); + tx2x_wind_speed = 0; + tx2x_wind_direction = 0; +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + tx2x_wind_speed_avg = 0; + tx2x_wind_direction_avg = 0; + Tx2xResetStatData(); +#endif + } + +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + Tx2xCheckSampleCount(); + if (0==Settings.tele_period && (uptime-tx2x_last_uptime)>=tx2x_avg_samples) { + Tx2xResetStat(); + } +#endif +} + +void Tx2xInit(void) +{ + if (!Settings.flag2.speed_conversion) { + Settings.flag2.speed_conversion = 2; + } +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + tx2x_valuesread = false; + Tx2xResetStat(); + Tx2xCheckSampleCount(); +#endif +#ifdef USE_TX23_WIND_SENSOR + tx23_stage = 0; + pinMode(pin[GPIO_TX2X_TXD_BLACK], OUTPUT); + digitalWrite(pin[GPIO_TX2X_TXD_BLACK], LOW); +#else + pinMode(pin[GPIO_TX2X_TXD_BLACK], INPUT); +#endif + attachInterrupt(pin[GPIO_TX2X_TXD_BLACK], TX2xStartRead, RISING); +} + +int32_t Tx2xNormalize(int32_t value) +{ + while (value>15) { + value -= 16; + } + while (value<0) { + value += 16; + } + return value; +} + +void Tx2xShow(bool json) +{ + if (!Tx2xAvailable()) { return; } + + char wind_speed_string[FLOATSZ]; + dtostrfd(ConvertSpeed(tx2x_wind_speed)/10, 1, wind_speed_string); + char wind_direction_string[FLOATSZ]; + dtostrfd(tx2x_wind_direction*22.5, 1, wind_direction_string); + char wind_direction_cardinal_string[TX2X_DIRECTIONS_MAXSIZE+1]; + GetTextIndexed(wind_direction_cardinal_string, sizeof(wind_direction_cardinal_string), tx2x_wind_direction, kTx2xDirections); +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + char wind_speed_min_string[FLOATSZ]; + dtostrfd(ConvertSpeed(tx2x_wind_speed_min)/10, 1, wind_speed_min_string); + char wind_speed_max_string[FLOATSZ]; + dtostrfd(ConvertSpeed(tx2x_wind_speed_max)/10, 1, wind_speed_max_string); + char wind_speed_avg_string[FLOATSZ]; + dtostrfd(ConvertSpeed(tx2x_wind_speed_avg)/10, 1, wind_speed_avg_string); + char wind_direction_avg_string[FLOATSZ]; + dtostrfd(tx2x_wind_direction_avg, 1, wind_direction_avg_string); + char wind_direction_avg_cardinal_string[4]; + GetTextIndexed(wind_direction_avg_cardinal_string, sizeof(wind_direction_avg_cardinal_string), int((tx2x_wind_direction_avg/22.5f)+0.5f) % 16, kTx2xDirections); + char wind_direction_range_string[FLOATSZ]; + dtostrfd(Tx2xNormalize(tx2x_wind_direction_max-tx2x_wind_direction_min)*22.5, 1, wind_direction_range_string); + char wind_direction_min_string[FLOATSZ]; + dtostrfd(Tx2xNormalize(tx2x_wind_direction_min)*22.5, 1, wind_direction_min_string); + char wind_direction_max_string[FLOATSZ]; + dtostrfd(Tx2xNormalize(tx2x_wind_direction_max)*22.5, 1, wind_direction_max_string); +#endif + + if (json) { +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS +#ifdef USE_TX2x_LEGACY_JSON + ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":%s,\"SpeedAvg\":%s,\"SpeedMax\":%s,\"Direction\":\"%s\",\"Degree\":%s}"), + wind_speed_string, + wind_speed_avg_string, + wind_speed_max_string, + wind_direction_cardinal_string, + wind_direction_string + ); +#else + ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":{\"Act\":%s,\"Avg\":%s,\"Min\":%s,\"Max\":%s},\"Dir\":{\"Card\":\"%s\",\"Deg\":%s,\"Avg\":%s,\"AvgCard\":\"%s\",\"Min\":%s,\"Max\":%s,\"Range\":%s}}"), + wind_speed_string, + wind_speed_avg_string, + wind_speed_min_string, + wind_speed_max_string, + wind_direction_cardinal_string, + wind_direction_string, + wind_direction_avg_string, + wind_direction_avg_cardinal_string, + wind_direction_min_string, + wind_direction_max_string, + wind_direction_range_string + ); +#endif +#else +#ifdef USE_TX2x_LEGACY_JSON + ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":%s,\"Direction\":\"%s\",\"Degree\":%s}"), + wind_speed_string, wind_direction_cardinal_string, wind_direction_string); +#else + ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":{\"Act\":%s},\"Dir\":{\"Card\":\"%s\",\"Deg\":%s}}"), + wind_speed_string, wind_direction_cardinal_string, wind_direction_string); +#endif +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TX2X, + wind_speed_string, + SpeedUnit().c_str(), +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + wind_speed_avg_string, + SpeedUnit().c_str(), + wind_speed_min_string, + SpeedUnit().c_str(), + wind_speed_max_string, + SpeedUnit().c_str(), +#endif + wind_direction_cardinal_string, + wind_direction_string +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + ,wind_direction_avg_cardinal_string, + wind_direction_avg_string, + wind_direction_range_string, + wind_direction_min_string, + wind_direction_max_string +#endif + ); +#endif + } +} + + + + + +bool Xsns35(uint8_t function) +{ + bool result = false; + + if (pin[GPIO_TX2X_TXD_BLACK] < 99) { + switch (function) { + case FUNC_INIT: + Tx2xInit(); + break; + case FUNC_EVERY_SECOND: + Tx2xRead(); + break; +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + case FUNC_AFTER_TELEPERIOD: + Tx2xResetStat(); + break; +#endif + case FUNC_JSON_APPEND: + Tx2xShow(true); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Tx2xShow(false); + break; +#endif + + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_36_mgc3130.ino" +# 22 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_36_mgc3130.ino" +#ifdef USE_I2C +#ifdef USE_MGC3130 +# 35 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_36_mgc3130.ino" +#define XSNS_36 36 +#define XI2C_27 27 + +#warning **** MGC3130: It is recommended to disable all unneeded I2C-drivers **** + +#define MGC3130_I2C_ADDR 0x42 + +#define MGC3130_xfer pin[GPIO_MGC3130_XFER] +#define MGC3130_reset pin[GPIO_MGC3130_RESET] + + +bool MGC3130_type = false; +char MGC3130stype[] = "MGC3130"; + + +#define MGC3130_SYSTEM_STATUS 0x15 +#define MGC3130_REQUEST_MSG 0x06 +#define MGC3130_FW_VERSION 0x83 +#define MGC3130_SET_RUNTIME 0xA2 +#define MGC3130_SENSOR_DATA 0x91 + + +#define MGC3130_GESTURE_GARBAGE 1 +#define MGC3130_FLICK_WEST_EAST 2 +#define MGC3130_FLICK_EAST_WEST 3 +#define MGC3130_FLICK_SOUTH_NORTH 4 +#define MGC3130_FLICK_NORTH_SOUTH 5 +#define MGC3130_CIRCLE_CLOCKWISE 6 +#define MGC3130_CIRCLE_CCLOCKWISE 7 + +#define MGC3130_MIN_ROTVALUE 0 +#define MGC3130_MAX_ROTVALUE 1023 +#define MGC3130_MIN_ZVALUE 32768 + + +#ifdef USE_WEBSERVER +const char HTTP_MGC_3130_SNS[] PROGMEM = + "{s}" "%s" "{m}%s{e}" + "{s}" "HwRev" "{m}%u.%u{e}" + "{s}" "loaderVer" "{m}%u.%u{e}" + "{s}" "platVer" "{m}%u{e}"; +#endif + + + + + + + +#pragma pack(1) +union MGC3130_Union{ + uint8_t buffer[132]; + struct + { + + uint8_t msgSize; + uint8_t flag; + uint8_t counter; + uint8_t id; + + struct { + uint8_t DSPStatus:1; + uint8_t gestureInfo:1; + uint8_t touchInfo:1; + uint8_t airWheelInfo:1; + uint8_t xyzPosition:1; + uint8_t noisePower:1; + uint8_t reserved:2; + uint8_t electrodeConfiguration:3; + uint8_t CICData:1; + uint8_t SDData:1; + uint16_t reserved2:3; + } outputConfigMask; + uint8_t timestamp; + struct { + uint8_t positionValid:1; + uint8_t airWheelValid:1; + uint8_t rawDataValid:1; + uint8_t noisePowerValid:1; + uint8_t environmentalNoise:1; + uint8_t clipping:1; + uint8_t reserved:1; + uint8_t DSPRunning:1; + } systemInfo; + uint16_t dspInfo; + struct { + uint8_t gestureCode:8; + uint8_t reserved:4; + uint8_t gestureType:4; + uint8_t edgeFlick:1; + uint16_t reserved2:14; + uint8_t gestureInProgress:1; + } gestureInfo; + struct { + uint8_t touchSouth:1; + uint8_t touchWest:1; + uint8_t touchNorth:1; + uint8_t touchEast:1; + uint8_t touchCentre:1; + uint8_t tapSouth:1; + uint8_t tapWest:1; + uint8_t tapNorth:1; + uint8_t tapEast :1; + uint8_t tapCentre:1; + uint8_t doubleTapSouth:1; + uint8_t doubleTapWest:1; + uint8_t doubleTapNorth:1; + uint8_t doubleTapEast:1; + uint8_t doubleTapCentre:1; + uint8_t reserved:1; + uint8_t touchCounter; + uint8_t reserved2; + } touchInfo; + int8_t airWheel; + uint8_t reserved; + uint16_t x; + uint16_t y; + uint16_t z; + float noisePower; + float CICData[4]; + float SDData[4]; + } out; + struct { + uint8_t header[3]; + + uint8_t valid; + uint8_t hwRev[2]; + uint8_t parameterStartAddr; + uint8_t loaderVersion[2]; + uint8_t loaderPlatform; + uint8_t fwStartAddr; + char fwVersion[120]; + } fw; + struct{ + uint8_t id; + uint8_t size; + uint16_t error; + uint32_t reserved; + uint32_t reserved1; + } status; +} MGC_data; +#pragma pack() + +char MGC3130_currentGesture[12]; + +int8_t MGC3130_delta, MGC3130_lastrotation = 0; +int16_t MGC3130_rotValue, MGC3130_lastSentRotValue = 0; + +uint16_t MGC3130_lastSentX, MGC3130_lastSentY, MGC3130_lastSentZ = 0; + +uint8_t hwRev[2], loaderVersion[2], loaderPlatform = 0; +char MGC3130_firmwareInfo[20]; + +uint8_t MGC3130_touchTimeout = 0; +uint16_t MGC3130_touchCounter = 1; +uint32_t MGC3130_touchTimeStamp = millis(); +bool MGC3130_triggeredByTouch = false; + +uint8_t MGC3130_mode = 1; + + + +uint8_t MGC3130autoCal[] = {0x10, 0x00, 0x00, 0xA2, 0x80, 0x00 , 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}; +uint8_t MGC3130disableAirwheel[] = {0x10, 0x00, 0x00, 0xA2, 0x90, 0x00 , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; +uint8_t MGC3130enableAirwheel[] = {0x10, 0x00, 0x00, 0xA2, 0x90, 0x00 , 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; + +void MGC3130_handleSensorData(){ + if ( MGC_data.out.outputConfigMask.touchInfo && MGC3130_touchTimeout == 0){ + if (MGC3130_handleTouch()){ + MGC3130_triggeredByTouch = true; + MqttPublishSensor(); + } + } + + if(MGC3130_mode == 1){ + if( MGC_data.out.outputConfigMask.gestureInfo && MGC_data.out.gestureInfo.gestureCode > 0){ + MGC3130_handleGesture(); + MqttPublishSensor(); + } + } + if(MGC3130_mode == 2){ + if(MGC_data.out.outputConfigMask.airWheelInfo && MGC_data.out.systemInfo.airWheelValid){ + MGC3130_handleAirWheel(); + MqttPublishSensor(); + } + } + if(MGC3130_mode == 3){ + if(MGC_data.out.systemInfo.positionValid && (MGC_data.out.z > MGC3130_MIN_ZVALUE)){ + MqttPublishSensor(); + } + } +} + +void MGC3130_sendMessage(uint8_t data[], uint8_t length){ + Wire.beginTransmission(MGC3130_I2C_ADDR); + Wire.write(data,length); + Wire.endTransmission(); + delay(2); + MGC3130_receiveMessage(); +} + + +void MGC3130_handleGesture(){ + + char edge[5]; + if (MGC_data.out.gestureInfo.edgeFlick){ + snprintf_P(edge, sizeof(edge), PSTR("ED_")); + } + else{ + snprintf_P(edge, sizeof(edge), PSTR("")); + } + switch(MGC_data.out.gestureInfo.gestureCode){ + case MGC3130_GESTURE_GARBAGE: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("NONE")); + break; + case MGC3130_FLICK_WEST_EAST: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_WE"), edge); + break; + case MGC3130_FLICK_EAST_WEST: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_EW"), edge); + break; + case MGC3130_FLICK_SOUTH_NORTH: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_SN"), edge); + break; + case MGC3130_FLICK_NORTH_SOUTH: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_NS"), edge); + break; + case MGC3130_CIRCLE_CLOCKWISE: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("CW")); + break; + case MGC3130_CIRCLE_CCLOCKWISE: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("CCW")); + break; + } + +} + +bool MGC3130_handleTouch(){ + + bool success = false; + if (MGC_data.out.touchInfo.doubleTapCentre && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_C")); + MGC3130_touchTimeout = 5; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.doubleTapEast && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_E")); + MGC3130_touchTimeout = 5; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.doubleTapNorth && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_N")); + MGC3130_touchTimeout = 5; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.doubleTapWest && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_W")); + MGC3130_touchTimeout = 5; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.doubleTapSouth && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_S")); + MGC3130_touchTimeout = 5; + success = true; + MGC3130_touchCounter = 1; + } + if (MGC_data.out.touchInfo.tapCentre && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_C")); + MGC3130_touchTimeout = 2; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.tapEast && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_E")); + MGC3130_touchTimeout = 2; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.tapNorth && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_N")); + MGC3130_touchTimeout = 2; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.tapWest && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_W")); + MGC3130_touchTimeout = 2; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.tapSouth && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_S")); + MGC3130_touchTimeout = 2; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.touchCentre && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_C")); + success = true; + MGC3130_touchCounter++; + } + else if (MGC_data.out.touchInfo.touchEast && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_E")); + success = true; + MGC3130_touchCounter++; + } + else if (MGC_data.out.touchInfo.touchNorth && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_N")); + success = true; + MGC3130_touchCounter++; + } + else if (MGC_data.out.touchInfo.touchWest && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_W")); + success = true; + MGC3130_touchCounter++; + } + else if (MGC_data.out.touchInfo.touchSouth && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_S")); + success = true; + MGC3130_touchCounter++; + } + + return success; +} + +void MGC3130_handleAirWheel(){ + MGC3130_delta = MGC_data.out.airWheel - MGC3130_lastrotation; + MGC3130_lastrotation = MGC_data.out.airWheel; + + MGC3130_rotValue = MGC3130_rotValue + MGC3130_delta; + if(MGC3130_rotValue < MGC3130_MIN_ROTVALUE){ + MGC3130_rotValue = MGC3130_MIN_ROTVALUE; + } + if(MGC3130_rotValue > MGC3130_MAX_ROTVALUE){ + MGC3130_rotValue = MGC3130_MAX_ROTVALUE; + } +} + +void MGC3130_handleSystemStatus(){ + +} + +bool MGC3130_receiveMessage(){ + if(MGC3130_readData()){ + switch(MGC_data.out.id){ + case MGC3130_SENSOR_DATA: + MGC3130_handleSensorData(); + break; + case MGC3130_SYSTEM_STATUS: + MGC3130_handleSystemStatus(); + break; + case MGC3130_FW_VERSION: + hwRev[0] = MGC_data.fw.hwRev[1]; + hwRev[1] = MGC_data.fw.hwRev[0]; + loaderVersion[0] = MGC_data.fw.loaderVersion[0]; + loaderVersion[1] = MGC_data.fw.loaderVersion[1]; + loaderPlatform = MGC_data.fw.loaderPlatform; + snprintf_P(MGC3130_firmwareInfo, sizeof(MGC3130_firmwareInfo), PSTR("FW: %s"), MGC_data.fw.fwVersion); + MGC3130_firmwareInfo[20] = '\0'; + + break; + } + return true; + } + return false; +} + +bool MGC3130_readData() +{ + bool success = false; + if (!digitalRead(MGC3130_xfer)){ + pinMode(MGC3130_xfer, OUTPUT); + digitalWrite(MGC3130_xfer, LOW); + Wire.requestFrom(MGC3130_I2C_ADDR, (uint16_t)32); + + MGC_data.buffer[0] = 4; + unsigned char i = 0; + while(Wire.available() && (i < MGC_data.buffer[0])){ + MGC_data.buffer[i] = Wire.read(); + i++; + } + digitalWrite(MGC3130_xfer, HIGH); + pinMode(MGC3130_xfer, INPUT); + success = true; + } + return success; +} + +void MGC3130_nextMode(){ + if (MGC3130_mode < 3){ + MGC3130_mode++; + } + else{ + MGC3130_mode = 1; + } + switch(MGC3130_mode){ + case 1: + MGC3130_sendMessage(MGC3130disableAirwheel,16); + break; + case 2: + MGC3130_sendMessage(MGC3130enableAirwheel,16); + break; + case 3: + MGC3130_sendMessage(MGC3130disableAirwheel,16); + break; + } +} + +void MGC3130_loop() +{ + if(MGC3130_touchTimeout > 0){ + MGC3130_touchTimeout--; + } + MGC3130_receiveMessage(); +} + +void MGC3130_detect(void) +{ + if (MGC3130_type || I2cActive(MGC3130_I2C_ADDR)) { return; } + + pinMode(MGC3130_xfer, INPUT_PULLUP); + pinMode(MGC3130_reset, OUTPUT); + digitalWrite(MGC3130_reset, LOW); + delay(10); + digitalWrite(MGC3130_reset, HIGH); + delay(50); + + if (MGC3130_receiveMessage()) { + I2cSetActiveFound(MGC3130_I2C_ADDR, MGC3130stype); + MGC3130_currentGesture[0] = '\0'; + MGC3130_type = true; + } +} + + + + + +void MGC3130_show(bool json) +{ + if (!MGC3130_type) { return; } + + char status_chr[2]; + if (MGC_data.out.systemInfo.DSPRunning) { + sprintf (status_chr, "1"); + } + else{ + sprintf (status_chr, "0"); + } + + if (json) { + if (MGC3130_mode == 3 && !MGC3130_triggeredByTouch) { + if (MGC_data.out.systemInfo.positionValid && !(MGC_data.out.x == MGC3130_lastSentX && MGC_data.out.y == MGC3130_lastSentY && MGC_data.out.z == MGC3130_lastSentZ)) { + ResponseAppend_P(PSTR(",\"%s\":{\"X\":%u,\"Y\":%u,\"Z\":%u}"), + MGC3130stype, MGC_data.out.x/64, MGC_data.out.y/64, (MGC_data.out.z-(uint16_t)MGC3130_MIN_ZVALUE)/64); + MGC3130_lastSentX = MGC_data.out.x; + MGC3130_lastSentY = MGC_data.out.y; + MGC3130_lastSentZ = MGC_data.out.z; + } + } + MGC3130_triggeredByTouch = false; + + if (MGC3130_mode == 2) { + if (MGC_data.out.systemInfo.airWheelValid && (MGC3130_rotValue != MGC3130_lastSentRotValue)) { + ResponseAppend_P(PSTR(",\"%s\":{\"AW\":%i}"), MGC3130stype, MGC3130_rotValue); + MGC3130_lastSentRotValue = MGC3130_rotValue; + } + } + + if (MGC3130_currentGesture[0] != '\0') { + if (millis() - MGC3130_touchTimeStamp > 220 ) { + MGC3130_touchCounter = 1; + } + ResponseAppend_P(PSTR(",\"%s\":{\"%s\":%u}"), MGC3130stype, MGC3130_currentGesture, MGC3130_touchCounter); + MGC3130_currentGesture[0] = '\0'; + MGC3130_touchTimeStamp = millis(); + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_MGC_3130_SNS, MGC3130stype, status_chr, hwRev[0], hwRev[1], loaderVersion[0], loaderVersion[1], loaderPlatform ); +#endif + } +} +# 557 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_36_mgc3130.ino" +bool MGC3130CommandSensor() +{ + bool serviced = true; + + switch (XdrvMailbox.payload) { + case 0: + MGC3130_nextMode(); + break; + case 1: + MGC3130_mode = 1; + MGC3130_sendMessage(MGC3130disableAirwheel,16); + break; + case 2: + MGC3130_mode = 2; + MGC3130_sendMessage(MGC3130enableAirwheel,16); + break; + case 3: + MGC3130_mode = 3; + MGC3130_sendMessage(MGC3130disableAirwheel,16); + break; + } + return serviced; +} + + + + + +bool Xsns36(uint8_t function) +{ + if (!I2cEnabled(XI2C_27)) { return false; } + + bool result = false; + + if ((FUNC_INIT == function) && (pin[GPIO_MGC3130_XFER] < 99) && (pin[GPIO_MGC3130_RESET] < 99)) { + MGC3130_detect(); + } + else if (MGC3130_type) { + switch (function) { + case FUNC_EVERY_50_MSECOND: + MGC3130_loop(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_36 == XdrvMailbox.index) { + result = MGC3130CommandSensor(); + } + break; + case FUNC_JSON_APPEND: + MGC3130_show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MGC3130_show(0); + break; +#endif + } + } + return result; +} +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_37_rfsensor.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_37_rfsensor.ino" +#ifdef USE_RF_SENSOR +# 33 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_37_rfsensor.ino" +#define XSNS_37 37 + + + + +#define RFSNS_VALID_WINDOW 1800 + +#define RFSNS_LOOPS_PER_MILLI 1900 +#define RFSNS_RAW_BUFFER_SIZE 180 +#define RFSNS_MIN_RAW_PULSES 112 + +#define RFSNS_MIN_PULSE_LENGTH 300 +#define RFSNS_RAWSIGNAL_SAMPLE 50 +#define RFSNS_SIGNAL_TIMEOUT 10 +#define RFSNS_SIGNAL_REPEAT_TIME 500 + +typedef struct RawSignalStruct +{ + int Number; + uint8_t Repeats; + uint8_t Multiply; + unsigned long Time; + uint8_t Pulses[RFSNS_RAW_BUFFER_SIZE+2]; + +} raw_signal_t; + +raw_signal_t *rfsns_raw_signal = nullptr; +uint8_t rfsns_rf_bit; +uint8_t rfsns_rf_port; +uint8_t rfsns_any_sensor = 0; + + + + + +bool RfSnsFetchSignal(uint8_t DataPin, bool StateSignal) +{ + uint8_t Fbit = digitalPinToBitMask(DataPin); + uint8_t Fport = digitalPinToPort(DataPin); + uint8_t FstateMask = (StateSignal ? Fbit : 0); + + if ((*portInputRegister(Fport) & Fbit) == FstateMask) { + const unsigned long LoopsPerMilli = RFSNS_LOOPS_PER_MILLI; + + + + + + + unsigned long PulseLength = 0; + if (rfsns_raw_signal->Time) { + if (rfsns_raw_signal->Repeats && (rfsns_raw_signal->Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) { + PulseLength = micros() + RFSNS_SIGNAL_TIMEOUT *1000; + while (((rfsns_raw_signal->Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) && (PulseLength > micros())) { + if ((*portInputRegister(Fport) & Fbit) == FstateMask) { + PulseLength = micros() + RFSNS_SIGNAL_TIMEOUT *1000; + } + } + while (((rfsns_raw_signal->Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) && ((*portInputRegister(Fport) & Fbit) != FstateMask)); + } + } + + int RawCodeLength = 1; + bool Ftoggle = false; + unsigned long numloops = 0; + unsigned long maxloops = RFSNS_SIGNAL_TIMEOUT * LoopsPerMilli; + rfsns_raw_signal->Multiply = RFSNS_RAWSIGNAL_SAMPLE; + do { + numloops = 0; + while(((*portInputRegister(Fport) & Fbit) == FstateMask) ^ Ftoggle) { + if (numloops++ == maxloops) { break; } + } + PulseLength = (numloops *1000) / LoopsPerMilli; + if (PulseLength < RFSNS_MIN_PULSE_LENGTH) { break; } + Ftoggle = !Ftoggle; + rfsns_raw_signal->Pulses[RawCodeLength++] = PulseLength / (unsigned long)rfsns_raw_signal->Multiply; + } + while(RawCodeLength < RFSNS_RAW_BUFFER_SIZE && numloops <= maxloops); + + if ((RawCodeLength >= RFSNS_MIN_RAW_PULSES) && (RawCodeLength < RFSNS_RAW_BUFFER_SIZE -1)) { + rfsns_raw_signal->Repeats = 0; + rfsns_raw_signal->Number = RawCodeLength -1; + rfsns_raw_signal->Pulses[rfsns_raw_signal->Number] = 0; + rfsns_raw_signal->Time = millis(); + return true; + } + else + rfsns_raw_signal->Number = 0; + } + + return false; +} + +#ifdef USE_THEO_V2 +# 149 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_37_rfsensor.ino" +#define RFSNS_THEOV2_MAX_CHANNEL 2 + +#define RFSNS_THEOV2_PULSECOUNT 114 +#define RFSNS_THEOV2_RF_PULSE_MID 1000 + +typedef struct { + uint32_t time; + int16_t temp; + uint16_t lux; + uint8_t volt; +} theo_v2_t1_t; + +typedef struct { + uint32_t time; + int16_t temp; + uint16_t hum; + uint8_t volt; +} theo_v2_t2_t; + +theo_v2_t1_t *rfsns_theo_v2_t1 = nullptr; +theo_v2_t2_t *rfsns_theo_v2_t2 = nullptr; + +void RfSnsInitTheoV2(void) +{ + rfsns_theo_v2_t1 = (theo_v2_t1_t*)malloc(RFSNS_THEOV2_MAX_CHANNEL * sizeof(theo_v2_t1_t)); + rfsns_theo_v2_t2 = (theo_v2_t2_t*)malloc(RFSNS_THEOV2_MAX_CHANNEL * sizeof(theo_v2_t2_t)); + rfsns_any_sensor++; +} + +void RfSnsAnalyzeTheov2(void) +{ + if (rfsns_raw_signal->Number != RFSNS_THEOV2_PULSECOUNT) { return; } + + uint8_t Checksum; + uint8_t Channel; + uint8_t Type; + uint8_t Voltage; + int Payload1; + int Payload2; + + uint8_t b, bytes, bits, id; + + uint8_t idx = 3; + uint8_t chksum = 0; + for (bytes = 0; bytes < 7; bytes++) { + b = 0; + for (bits = 0; bits <= 7; bits++) + { + if ((rfsns_raw_signal->Pulses[idx] * rfsns_raw_signal->Multiply) > RFSNS_THEOV2_RF_PULSE_MID) { + b |= 1 << bits; + } + idx += 2; + } + if (bytes > 0) { chksum += b; } + + switch (bytes) { + case 0: + Checksum = b; + break; + case 1: + id = b; + Channel = b & 0x7; + Type = (b >> 3) & 0x1f; + break; + case 2: + Voltage = b; + break; + case 3: + Payload1 = b; + break; + case 4: + Payload1 = (b << 8) | Payload1; + break; + case 5: + Payload2 = b; + break; + case 6: + Payload2 = (b << 8) | Payload2; + break; + } + } + + if (Checksum != chksum) { return; } + if ((Channel == 0) || (Channel > RFSNS_THEOV2_MAX_CHANNEL)) { return; } + Channel--; + + rfsns_raw_signal->Repeats = 1; + + int Payload3 = Voltage & 0x3f; + + switch (Type) { + case 1: + rfsns_theo_v2_t1[Channel].time = LocalTime(); + rfsns_theo_v2_t1[Channel].volt = Payload3; + rfsns_theo_v2_t1[Channel].temp = Payload1; + rfsns_theo_v2_t1[Channel].lux = Payload2; + break; + case 2: + rfsns_theo_v2_t2[Channel].time = LocalTime(); + rfsns_theo_v2_t2[Channel].volt = Payload3; + rfsns_theo_v2_t2[Channel].temp = Payload1; + rfsns_theo_v2_t2[Channel].hum = Payload2; + break; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFS: TheoV2, ChkCalc %d, Chksum %d, id %d, Type %d, Ch %d, Volt %d, BattLo %d, Pld1 %d, Pld2 %d"), + chksum, Checksum, id, Type, Channel +1, Payload3, (Voltage & 0x80) >> 7, Payload1, Payload2); +} + +void RfSnsTheoV2Show(bool json) +{ + bool sensor_once = false; + + for (uint32_t i = 0; i < RFSNS_THEOV2_MAX_CHANNEL; i++) { + if (rfsns_theo_v2_t1[i].time) { + char sensor[10]; + snprintf_P(sensor, sizeof(sensor), PSTR("TV2T1C%d"), i +1); + char voltage[33]; + dtostrfd((float)rfsns_theo_v2_t1[i].volt / 10, 1, voltage); + + if (rfsns_theo_v2_t1[i].time < LocalTime() - RFSNS_VALID_WINDOW) { + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_RFRECEIVED "\":\"%s\",\"" D_JSON_VOLTAGE "\":%s}"), + sensor, GetDT(rfsns_theo_v2_t1[i].time).c_str(), voltage); + } + } else { + char temperature[33]; + dtostrfd(ConvertTemp((float)rfsns_theo_v2_t1[i].temp / 100), Settings.flag2.temperature_resolution, temperature); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_ILLUMINANCE "\":%d,\"" D_JSON_VOLTAGE "\":%s}"), + sensor, temperature, rfsns_theo_v2_t1[i].lux, voltage); +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && !sensor_once) { + DomoticzSensor(DZ_TEMP, temperature); + DomoticzSensor(DZ_ILLUMINANCE, rfsns_theo_v2_t1[i].lux); + sensor_once = true; + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, sensor, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, sensor, rfsns_theo_v2_t1[i].lux); +#endif + } + } + } + } + + sensor_once = false; + for (uint32_t i = 0; i < RFSNS_THEOV2_MAX_CHANNEL; i++) { + if (rfsns_theo_v2_t2[i].time) { + char sensor[10]; + snprintf_P(sensor, sizeof(sensor), PSTR("TV2T2C%d"), i +1); + char voltage[33]; + dtostrfd((float)rfsns_theo_v2_t2[i].volt / 10, 1, voltage); + + if (rfsns_theo_v2_t2[i].time < LocalTime() - RFSNS_VALID_WINDOW) { + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_RFRECEIVED" \":\"%s\",\"" D_JSON_VOLTAGE "\":%s}"), + sensor, GetDT(rfsns_theo_v2_t2[i].time).c_str(), voltage); + } + } else { + float temp = ConvertTemp((float)rfsns_theo_v2_t2[i].temp / 100); + float humi = ConvertHumidity((float)rfsns_theo_v2_t2[i].hum / 100); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{"), sensor); + ResponseAppendTHD(temp, humi); + ResponseAppend_P(PSTR(",\"" D_JSON_VOLTAGE "\":%s}"), voltage); + + if ((0 == tele_period) && !sensor_once) { +#ifdef USE_DOMOTICZ + DomoticzTempHumPressureSensor(temp, humi); +#endif +#ifdef USE_KNX + KnxSensor(KNX_TEMPERATURE, temp); + KnxSensor(KNX_HUMIDITY, humi); +#endif + sensor_once = true; + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_THD(sensor, temp, humi); +#endif + } + } + } + } +} + +#endif + +#ifdef USE_ALECTO_V2 +# 389 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_37_rfsensor.ino" +#define RFSNS_DKW2012_PULSECOUNT 176 +#define RFSNS_ACH2010_MIN_PULSECOUNT 160 +#define RFSNS_ACH2010_MAX_PULSECOUNT 160 + +#define D_ALECTOV2 "AlectoV2" + +const char kAlectoV2Directions[] PROGMEM = D_TX20_NORTH "|" + D_TX20_NORTH D_TX20_NORTH D_TX20_EAST "|" + D_TX20_NORTH D_TX20_EAST "|" + D_TX20_EAST D_TX20_NORTH D_TX20_EAST "|" + D_TX20_EAST "|" + D_TX20_EAST D_TX20_SOUTH D_TX20_EAST "|" + D_TX20_SOUTH D_TX20_EAST "|" + D_TX20_SOUTH D_TX20_SOUTH D_TX20_EAST "|" + D_TX20_SOUTH "|" + D_TX20_SOUTH D_TX20_SOUTH D_TX20_WEST "|" + D_TX20_SOUTH D_TX20_WEST "|" + D_TX20_WEST D_TX20_SOUTH D_TX20_WEST "|" + D_TX20_WEST "|" + D_TX20_WEST D_TX20_NORTH D_TX20_WEST "|" + D_TX20_NORTH D_TX20_WEST "|" + D_TX20_NORTH D_TX20_NORTH D_TX20_WEST; + +typedef struct { + uint32_t time; + float temp; + float rain; + float wind; + float gust; + uint8_t type; + uint8_t humi; + uint8_t wdir; +} alecto_v2_t; + +alecto_v2_t *rfsns_alecto_v2 = nullptr; +uint16_t rfsns_alecto_rain_base = 0; + +void RfSnsInitAlectoV2(void) +{ + rfsns_alecto_v2 = (alecto_v2_t*)malloc(sizeof(alecto_v2_t)); + rfsns_any_sensor++; +} + +void RfSnsAnalyzeAlectov2() +{ + if (!(((rfsns_raw_signal->Number >= RFSNS_ACH2010_MIN_PULSECOUNT) && + (rfsns_raw_signal->Number <= RFSNS_ACH2010_MAX_PULSECOUNT)) || (rfsns_raw_signal->Number == RFSNS_DKW2012_PULSECOUNT))) { return; } + + uint8_t c = 0; + uint8_t rfbit; + uint8_t data[9] = { 0 }; + uint8_t msgtype = 0; + uint8_t rc = 0; + int temp; + uint8_t checksum = 0; + uint8_t checksumcalc = 0; + uint8_t maxidx = 8; + unsigned long atime; + float factor; + char buf1[16]; + + if (rfsns_raw_signal->Number > RFSNS_ACH2010_MAX_PULSECOUNT) { maxidx = 9; } + + uint8_t idx = maxidx; + for (uint32_t x = rfsns_raw_signal->Number; x > 0; x = x-2) { + if (rfsns_raw_signal->Pulses[x-1] * rfsns_raw_signal->Multiply < 0x300) { + rfbit = 0x80; + } else { + rfbit = 0; + } + data[idx] = (data[idx] >> 1) | rfbit; + c++; + if (c == 8) { + if (idx == 0) { break; } + c = 0; + idx--; + } + } + + checksum = data[maxidx]; + checksumcalc = RfSnsAlectoCRC8(data, maxidx); + + msgtype = (data[0] >> 4) & 0xf; + rc = (data[0] << 4) | (data[1] >> 4); + + if (checksum != checksumcalc) { return; } + if ((msgtype != 10) && (msgtype != 5)) { return; } + + rfsns_raw_signal->Repeats = 1; + + + + + + factor = 1.22; + + + + + + rfsns_alecto_v2->time = LocalTime(); + rfsns_alecto_v2->type = (RFSNS_DKW2012_PULSECOUNT == rfsns_raw_signal->Number); + rfsns_alecto_v2->temp = (float)(((data[1] & 0x3) * 256 + data[2]) - 400) / 10; + rfsns_alecto_v2->humi = data[3]; + uint16_t rain = (data[6] * 256) + data[7]; + + if (rain < rfsns_alecto_rain_base) { rfsns_alecto_rain_base = rain; } + if (rfsns_alecto_rain_base > 0) { + rfsns_alecto_v2->rain += ((float)rain - rfsns_alecto_rain_base) * 0.30; + } + rfsns_alecto_rain_base = rain; + rfsns_alecto_v2->wind = (float)data[4] * factor; + rfsns_alecto_v2->gust = (float)data[5] * factor; + if (rfsns_alecto_v2->type) { + rfsns_alecto_v2->wdir = data[8] & 0xf; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFS: " D_ALECTOV2 ", ChkCalc %d, Chksum %d, rc %d, Temp %d, Hum %d, Rain %d, Wind %d, Gust %d, Dir %d, Factor %s"), + checksumcalc, checksum, rc, ((data[1] & 0x3) * 256 + data[2]) - 400, data[3], (data[6] * 256) + data[7], data[4], data[5], data[8] & 0xf, dtostrfd(factor, 3, buf1)); +} + +void RfSnsAlectoResetRain(void) +{ + if ((RtcTime.hour == 0) && (RtcTime.minute == 0) && (RtcTime.second == 5)) { + rfsns_alecto_v2->rain = 0; + } +} + + + + + + + +uint8_t RfSnsAlectoCRC8(uint8_t *addr, uint8_t len) +{ + uint8_t crc = 0; + while (len--) { + uint8_t inbyte = *addr++; + for (uint32_t i = 8; i; i--) { + uint8_t mix = (crc ^ inbyte) & 0x80; + crc <<= 1; + if (mix) { crc ^= 0x31; } + inbyte <<= 1; + } + } + return crc; +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_ALECTOV2[] PROGMEM = + "{s}" D_ALECTOV2 " " D_RAIN "{m}%s " D_UNIT_MILLIMETER "{e}" + "{s}" D_ALECTOV2 " " D_TX20_WIND_SPEED "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}" + "{s}" D_ALECTOV2 " " D_TX20_WIND_SPEED_MAX "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}"; +const char HTTP_SNS_ALECTOV2_WDIR[] PROGMEM = + "{s}" D_ALECTOV2 " " D_TX20_WIND_DIRECTION "{m}%s{e}"; +#endif + +void RfSnsAlectoV2Show(bool json) +{ + if (rfsns_alecto_v2->time) { + if (rfsns_alecto_v2->time < LocalTime() - RFSNS_VALID_WINDOW) { + if (json) { + ResponseAppend_P(PSTR(",\"" D_ALECTOV2 "\":{\"" D_JSON_RFRECEIVED "\":\"%s\"}"), GetDT(rfsns_alecto_v2->time).c_str()); + } + } else { + float temp = ConvertTemp(rfsns_alecto_v2->temp); + float humi = ConvertHumidity((float)rfsns_alecto_v2->humi); + + char rain[33]; + dtostrfd(rfsns_alecto_v2->rain, 2, rain); + char wind[33]; + dtostrfd(rfsns_alecto_v2->wind, 2, wind); + char gust[33]; + dtostrfd(rfsns_alecto_v2->gust, 2, gust); + char wdir[4]; + char direction[20]; + if (rfsns_alecto_v2->type) { + GetTextIndexed(wdir, sizeof(wdir), rfsns_alecto_v2->wdir, kAlectoV2Directions); + snprintf_P(direction, sizeof(direction), PSTR(",\"Direction\":\"%s\""), wdir); + } + + if (json) { + ResponseAppend_P(PSTR(",\"" D_ALECTOV2 "\":{")); + ResponseAppendTHD(temp, humi); + ResponseAppend_P(PSTR(",\"Rain\":%s,\"Wind\":%s,\"Gust\":%s%s}"), rain, wind, gust, (rfsns_alecto_v2->type) ? direction : ""); + + if (0 == tele_period) { +#ifdef USE_DOMOTICZ + + + + +#endif + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_THD(D_ALECTOV2, temp, humi); + WSContentSend_PD(HTTP_SNS_ALECTOV2, rain, wind, gust); + if (rfsns_alecto_v2->type) { + WSContentSend_PD(HTTP_SNS_ALECTOV2_WDIR, wdir); + } +#endif + } + } + } +} +#endif + +void RfSnsInit(void) +{ + rfsns_raw_signal = (raw_signal_t*)(malloc(sizeof(raw_signal_t))); + if (rfsns_raw_signal) { + memset(rfsns_raw_signal, 0, sizeof(raw_signal_t)); +#ifdef USE_THEO_V2 + RfSnsInitTheoV2(); +#endif +#ifdef USE_ALECTO_V2 + RfSnsInitAlectoV2(); +#endif + if (rfsns_any_sensor) { + rfsns_rf_bit = digitalPinToBitMask(pin[GPIO_RF_SENSOR]); + rfsns_rf_port = digitalPinToPort(pin[GPIO_RF_SENSOR]); + pinMode(pin[GPIO_RF_SENSOR], INPUT); + } else { + free(rfsns_raw_signal); + rfsns_raw_signal = nullptr; + } + } +} + +void RfSnsAnalyzeRawSignal(void) +{ + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFS: Pulses %d"), (int)rfsns_raw_signal->Number); + +#ifdef USE_THEO_V2 + RfSnsAnalyzeTheov2(); +#endif +#ifdef USE_ALECTO_V2 + RfSnsAnalyzeAlectov2(); +#endif +} + +void RfSnsEverySecond(void) +{ +#ifdef USE_ALECTO_V2 + RfSnsAlectoResetRain(); +#endif +} + +void RfSnsShow(bool json) +{ +#ifdef USE_THEO_V2 + RfSnsTheoV2Show(json); +#endif +#ifdef USE_ALECTO_V2 + RfSnsAlectoV2Show(json); +#endif +} + + + + + +bool Xsns37(uint8_t function) +{ + bool result = false; + + if ((pin[GPIO_RF_SENSOR] < 99) && (FUNC_INIT == function)) { + RfSnsInit(); + } + else if (rfsns_raw_signal) { + switch (function) { + case FUNC_LOOP: + if ((*portInputRegister(rfsns_rf_port) &rfsns_rf_bit) == rfsns_rf_bit) { + if (RfSnsFetchSignal(pin[GPIO_RF_SENSOR], HIGH)) { + RfSnsAnalyzeRawSignal(); + } + } + ssleep = 0; + break; + case FUNC_EVERY_SECOND: + RfSnsEverySecond(); + break; + case FUNC_JSON_APPEND: + RfSnsShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + RfSnsShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_38_az7798.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_38_az7798.ino" +#ifdef USE_AZ7798 + +#define XSNS_38 38 +# 112 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_38_az7798.ino" +#include + +#ifndef CO2_LOW +#define CO2_LOW 800 +#endif +#ifndef CO2_HIGH +#define CO2_HIGH 1200 +#endif + +#define AZ_READ_TIMEOUT 400 + +#define AZ_CLOCK_UPDATE_INTERVAL (24UL * 60 * 60) +#define AZ_EPOCH (946684800UL) + +TasmotaSerial *AzSerial; + +const char ktype[] = "AZ7798"; +uint8_t az_type = 1; +uint16_t az_co2 = 0; +double az_temperature = 0; +double az_humidity = 0; +uint8_t az_received = 0; +uint8_t az_state = 0; +unsigned long az_clock_update = 10; + + + +void AzEverySecond(void) +{ + unsigned long start = millis(); + + az_state++; + if (5 == az_state) { + az_state = 0; + + AzSerial->flush(); + AzSerial->write(":\r", 2); + az_received = 0; + + uint8_t az_response[32]; + uint8_t counter = 0; + uint8_t i, j; + uint8_t response_substr[16]; + + do { + if (AzSerial->available() > 0) { + az_response[counter] = AzSerial->read(); + if(az_response[counter] == 0x0d) { az_received = 1; } + counter++; + } else { + delay(5); + } + } while(((millis() - start) < AZ_READ_TIMEOUT) && (counter < sizeof(az_response)) && !az_received); + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, az_response, counter); + + if (!az_received) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 comms timeout")); + return; + } + + i = 0; + while((az_response[i] != 'T') && (i < counter)) {i++;} + if(az_response[i] != 'T') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find start of response")); + return; + } + i++; + j = 0; + + while((az_response[i] != 'C') && (az_response[i] != 'F') && (i < counter)) { + response_substr[j++] = az_response[i++]; + } + if((az_response[i] != 'C') && (az_response[i] != 'F')){ + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of temperature")); + return; + } + response_substr[j] = 0; + az_temperature = CharToFloat((char*)response_substr); + if(az_response[i] == 'C') { + az_temperature = ConvertTemp((float)az_temperature); + } else { + az_temperature = ConvertTemp((az_temperature - 32) / 1.8); + } + i++; + if(az_response[i] != ':') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error first delimiter")); + return; + } + i++; + if(az_response[i] != 'C') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error start of CO2")); + return; + } + i++; + j = 0; + + while((az_response[i] != 'p') && (i < counter)) { + response_substr[j++] = az_response[i++]; + } + if(az_response[i] != 'p') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of CO2")); + return; + } + response_substr[j] = 0; + az_co2 = atoi((char*)response_substr); +#ifdef USE_LIGHT + LightSetSignal(CO2_LOW, CO2_HIGH, az_co2); +#endif + i += 3; + if(az_response[i] != ':') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error second delimiter")); + return; + } + i++; + if(az_response[i] != 'H') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error start of humidity")); + return; + } + i++; + j = 0; + + while((az_response[i] != '%') && (i < counter)) { + response_substr[j++] = az_response[i++]; + } + if(az_response[i] != '%') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of humidity")); + return; + } + response_substr[j] = 0; + az_humidity = ConvertHumidity(CharToFloat((char*)response_substr)); + } + + + if ((az_clock_update == 0) && (LocalTime() > AZ_EPOCH)) { + char tmpString[16]; + sprintf(tmpString, "C %d\r", (int)(LocalTime() - AZ_EPOCH)); + AzSerial->write(tmpString); + + do { + if (AzSerial->available() > 0) { + if(AzSerial->read() == 0x0d) { break; } + } else { + delay(5); + } + } while(((millis() - start) < AZ_READ_TIMEOUT)); + az_clock_update = AZ_CLOCK_UPDATE_INTERVAL; + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 clock updated")); + } else { + az_clock_update--; + } +} + + + +void AzInit(void) +{ + az_type = 0; + if ((pin[GPIO_AZ_RXD] < 99) && (pin[GPIO_AZ_TXD] < 99)) { + AzSerial = new TasmotaSerial(pin[GPIO_AZ_RXD], pin[GPIO_AZ_TXD], 1); + if (AzSerial->begin(9600)) { + if (AzSerial->hardwareSerial()) { ClaimSerial(); } + az_type = 1; + } + } +} + +void AzShow(bool json) +{ + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_CO2 "\":%d,"), ktype, az_co2); + ResponseAppendTHD(az_temperature, az_humidity); + ResponseJsonEnd(); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, az_co2); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_CO2, ktype, az_co2); + WSContentSend_THD(ktype, az_temperature, az_humidity); +#endif + } +} + + + + + +bool Xsns38(uint8_t function) +{ + bool result = false; + + if(az_type){ + switch (function) { + case FUNC_INIT: + AzInit(); + break; + case FUNC_EVERY_SECOND: + AzEverySecond(); + break; + case FUNC_JSON_APPEND: + AzShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + AzShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_39_max31855.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_39_max31855.ino" +#ifdef USE_MAX31855 + +#define XSNS_39 39 + +bool initialized = false; + +struct MAX31855_ResultStruct{ + uint8_t ErrorCode; + float ProbeTemperature; + float ReferenceTemperature; +} MAX31855_Result; + +void MAX31855_Init(void){ + if(initialized) + return; + + + pinMode(pin[GPIO_MAX31855CS], OUTPUT); + pinMode(pin[GPIO_MAX31855CLK], OUTPUT); + pinMode(pin[GPIO_MAX31855DO], INPUT); + + + digitalWrite(pin[GPIO_MAX31855CS], HIGH); + digitalWrite(pin[GPIO_MAX31855CLK], LOW); + + initialized = true; +} + + + + + +void MAX31855_GetResult(void){ + int32_t RawData = MAX31855_ShiftIn(32); + uint8_t probeerror = RawData & 0x7; + + MAX31855_Result.ErrorCode = probeerror; + MAX31855_Result.ReferenceTemperature = MAX31855_GetReferenceTemperature(RawData); + if(probeerror) + MAX31855_Result.ProbeTemperature = NAN; + else + MAX31855_Result.ProbeTemperature = MAX31855_GetProbeTemperature(RawData); +} + + + + + + +float MAX31855_GetProbeTemperature(int32_t RawData){ + if(RawData & 0x80000000) + RawData = (RawData >> 18) | 0xFFFFC000; + else + RawData >>= 18; + + float result = (RawData * 0.25); + + return ConvertTemp(result); +} + + + + + +float MAX31855_GetReferenceTemperature(int32_t RawData){ + if(RawData & 0x8000) + RawData = (RawData >> 4) | 0xFFFFF000; + else + RawData = (RawData >> 4) & 0x00000FFF; + + float result = (RawData * 0.0625); + + return ConvertTemp(result); +} + + + + + +int32_t MAX31855_ShiftIn(uint8_t Length){ + int32_t dataIn = 0; + + digitalWrite(pin[GPIO_MAX31855CS], LOW); + delayMicroseconds(1); + + for (uint32_t i = 0; i < Length; i++) + { + digitalWrite(pin[GPIO_MAX31855CLK], LOW); + delayMicroseconds(1); + dataIn <<= 1; + if(digitalRead(pin[GPIO_MAX31855DO])) + dataIn |= 1; + digitalWrite(pin[GPIO_MAX31855CLK], HIGH); + delayMicroseconds(1); + } + + digitalWrite(pin[GPIO_MAX31855CS], HIGH); + digitalWrite(pin[GPIO_MAX31855CLK], LOW); + return dataIn; +} + +void MAX31855_Show(bool Json){ + char probetemp[33]; + char referencetemp[33]; + dtostrfd(MAX31855_Result.ProbeTemperature, Settings.flag2.temperature_resolution, probetemp); + dtostrfd(MAX31855_Result.ReferenceTemperature, Settings.flag2.temperature_resolution, referencetemp); + + if(Json){ + ResponseAppend_P(PSTR(",\"MAX31855\":{\"" D_JSON_PROBETEMPERATURE "\":%s,\"" D_JSON_REFERENCETEMPERATURE "\":%s,\"" D_JSON_ERROR "\":%d}"), \ + probetemp, referencetemp, MAX31855_Result.ErrorCode); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_TEMP, probetemp); + } +#endif +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, MAX31855_Result.ProbeTemperature); + } +#endif + } else { +#ifdef USE_WEBSERVER + WSContentSend_PD(HTTP_SNS_TEMP, "MAX31855", probetemp, TempUnit()); +#endif + } +} + + + + + +bool Xsns39(uint8_t function) +{ + bool result = false; + if((pin[GPIO_MAX31855CS] < 99) && (pin[GPIO_MAX31855CLK] < 99) && (pin[GPIO_MAX31855DO] < 99)){ + + switch (function) { + case FUNC_INIT: + MAX31855_Init(); + break; + case FUNC_EVERY_SECOND: + MAX31855_GetResult(); + break; + case FUNC_JSON_APPEND: + MAX31855_Show(true); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MAX31855_Show(false); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_40_pn532.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_40_pn532.ino" +#ifdef USE_PN532_HSU + +#define XSNS_40 40 + +#include + +TasmotaSerial *PN532_Serial; + +#define PN532_INVALID_ACK -1 +#define PN532_TIMEOUT -2 +#define PN532_INVALID_FRAME -3 +#define PN532_NO_SPACE -4 + +#define PN532_PREAMBLE 0x00 +#define PN532_STARTCODE1 0x00 +#define PN532_STARTCODE2 0xFF +#define PN532_POSTAMBLE 0x00 + +#define PN532_HOSTTOPN532 0xD4 +#define PN532_PN532TOHOST 0xD5 + +#define PN532_ACK_WAIT_TIME 0x0A + +#define PN532_COMMAND_GETFIRMWAREVERSION 0x02 +#define PN532_COMMAND_SAMCONFIGURATION 0x14 +#define PN532_COMMAND_RFCONFIGURATION 0x32 +#define PN532_COMMAND_INDATAEXCHANGE 0x40 +#define PN532_COMMAND_INLISTPASSIVETARGET 0x4A + +#define PN532_MIFARE_ISO14443A 0x00 +#define MIFARE_CMD_READ 0x30 +#define MIFARE_CMD_AUTH_A 0x60 +#define MIFARE_CMD_AUTH_B 0x61 +#define MIFARE_CMD_WRITE 0xA0 + +uint8_t pn532_model = 0; +uint8_t pn532_command = 0; +uint8_t pn532_scantimer = 0; + +uint8_t pn532_packetbuffer[64]; + +#ifdef USE_PN532_DATA_FUNCTION +uint8_t pn532_function = 0; +uint8_t pn532_newdata[16]; +uint8_t pn532_newdata_len = 0; +#endif + +void PN532_Init(void) +{ + if ((pin[GPIO_PN532_RXD] < 99) && (pin[GPIO_PN532_TXD] < 99)) { + PN532_Serial = new TasmotaSerial(pin[GPIO_PN532_RXD], pin[GPIO_PN532_TXD], 1); + if (PN532_Serial->begin(115200)) { + if (PN532_Serial->hardwareSerial()) { ClaimSerial(); } + PN532_wakeup(); + uint32_t ver = PN532_getFirmwareVersion(); + if (ver) { + PN532_setPassiveActivationRetries(0xFF); + PN532_SAMConfig(); + pn532_model = 1; + AddLog_P2(LOG_LEVEL_INFO,"NFC: PN532 NFC Reader detected (V%u.%u)",(ver>>16) & 0xFF, (ver>>8) & 0xFF); + } + } + } +} + +int8_t PN532_receive(uint8_t *buf, int len, uint16_t timeout) +{ + int read_bytes = 0; + int ret; + unsigned long start_millis; + while (read_bytes < len) { + start_millis = millis(); + do { + ret = PN532_Serial->read(); + if (ret >= 0) { + break; + } + } while((timeout == 0) || ((millis()- start_millis ) < timeout)); + + if (ret < 0) { + if (read_bytes) { + return read_bytes; + } else { + return PN532_TIMEOUT; + } + } + buf[read_bytes] = (uint8_t)ret; + read_bytes++; + } + return read_bytes; +} + +int8_t PN532_readAckFrame(void) +{ + const uint8_t PN532_ACK[] = {0, 0, 0xFF, 0, 0xFF, 0}; + uint8_t ackBuf[sizeof(PN532_ACK)]; + + if (PN532_receive(ackBuf, sizeof(PN532_ACK), PN532_ACK_WAIT_TIME) <= 0) { + return PN532_TIMEOUT; + } + + if (memcmp(&ackBuf, &PN532_ACK, sizeof(PN532_ACK))) { + return PN532_INVALID_ACK; + } + return 0; +} + +int8_t PN532_writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0) +{ + + PN532_Serial->flush(); + + pn532_command = header[0]; + PN532_Serial->write((uint8_t)PN532_PREAMBLE); + PN532_Serial->write((uint8_t)PN532_STARTCODE1); + PN532_Serial->write(PN532_STARTCODE2); + + uint8_t length = hlen + blen + 1; + PN532_Serial->write(length); + PN532_Serial->write(~length + 1); + + PN532_Serial->write(PN532_HOSTTOPN532); + uint8_t sum = PN532_HOSTTOPN532; + + PN532_Serial->write(header, hlen); + for (uint32_t i = 0; i < hlen; i++) { + sum += header[i]; + } + + PN532_Serial->write(body, blen); + for (uint32_t i = 0; i < blen; i++) { + sum += body[i]; + } + + uint8_t checksum = ~sum + 1; + PN532_Serial->write(checksum); + PN532_Serial->write((uint8_t)PN532_POSTAMBLE); + + return PN532_readAckFrame(); +} + +int16_t PN532_readResponse(uint8_t buf[], uint8_t len, uint16_t timeout = 50) +{ + uint8_t tmp[3]; + + + if (PN532_receive(tmp, 3, timeout)<=0) { + return PN532_TIMEOUT; + } + if (0 != tmp[0] || 0!= tmp[1] || 0xFF != tmp[2]) { + return PN532_INVALID_FRAME; + } + + + uint8_t length[2]; + if (PN532_receive(length, 2, timeout) <= 0) { + return PN532_TIMEOUT; + } + + if (0 != (uint8_t)(length[0] + length[1])) { + return PN532_INVALID_FRAME; + } + length[0] -= 2; + if (length[0] > len) { + return PN532_NO_SPACE; + } + + + uint8_t cmd = pn532_command + 1; + if (PN532_receive(tmp, 2, timeout) <= 0) { + return PN532_TIMEOUT; + } + if (PN532_PN532TOHOST != tmp[0] || cmd != tmp[1]) { + return PN532_INVALID_FRAME; + } + + if (PN532_receive(buf, length[0], timeout) != length[0]) { + return PN532_TIMEOUT; + } + + uint8_t sum = PN532_PN532TOHOST + cmd; + for (uint32_t i=0; i status) { + return 0; + } + + response = pn532_packetbuffer[0]; + response <<= 8; + response |= pn532_packetbuffer[1]; + response <<= 8; + response |= pn532_packetbuffer[2]; + response <<= 8; + response |= pn532_packetbuffer[3]; + + return response; +} + +void PN532_wakeup(void) +{ + uint8_t wakeup[5] = {0x55, 0x55, 0, 0, 0 }; + PN532_Serial->write(wakeup,sizeof(wakeup)); + + + PN532_Serial->flush(); +} + +bool PN532_readPassiveTargetID(uint8_t cardbaudrate, uint8_t *uid, uint8_t *uidLength, uint16_t timeout = 50) +{ + pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET; + pn532_packetbuffer[1] = 1; + pn532_packetbuffer[2] = cardbaudrate; + if (PN532_writeCommand(pn532_packetbuffer, 3)) { + return 0x0; + } + + if (PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer), timeout) < 0) { + return 0x0; + } +# 274 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_40_pn532.ino" + if (pn532_packetbuffer[0] != 1) { + return 0; + } + + uint16_t sens_res = pn532_packetbuffer[2]; + sens_res <<= 8; + sens_res |= pn532_packetbuffer[3]; + + + *uidLength = pn532_packetbuffer[5]; + + for (uint32_t i = 0; i < pn532_packetbuffer[5]; i++) { + uid[i] = pn532_packetbuffer[6 + i]; + } + + return 1; +} + +bool PN532_setPassiveActivationRetries(uint8_t maxRetries) +{ + pn532_packetbuffer[0] = PN532_COMMAND_RFCONFIGURATION; + pn532_packetbuffer[1] = 5; + pn532_packetbuffer[2] = 0xFF; + pn532_packetbuffer[3] = 0x01; + pn532_packetbuffer[4] = maxRetries; + if (PN532_writeCommand(pn532_packetbuffer, 5)) { + return 0; + } + return (0 < PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer))); +} + +bool PN532_SAMConfig(void) +{ + pn532_packetbuffer[0] = PN532_COMMAND_SAMCONFIGURATION; + pn532_packetbuffer[1] = 0x01; + pn532_packetbuffer[2] = 0x14; + pn532_packetbuffer[3] = 0x00; + if (PN532_writeCommand(pn532_packetbuffer, 4)) { + return false; + } + return (0 < PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer))); +} + +#ifdef USE_PN532_DATA_FUNCTION + +uint8_t mifareclassic_AuthenticateBlock (uint8_t *uid, uint8_t uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t *keyData) +{ + uint8_t i; + uint8_t _key[6]; + uint8_t _uid[7]; + uint8_t _uidLen; + + + memcpy(&_key, keyData, 6); + memcpy(&_uid, uid, uidLen); + _uidLen = uidLen; + + + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; + pn532_packetbuffer[2] = (keyNumber) ? MIFARE_CMD_AUTH_B : MIFARE_CMD_AUTH_A; + pn532_packetbuffer[3] = blockNumber; + memcpy(&pn532_packetbuffer[4], &_key, 6); + for (i = 0; i < _uidLen; i++) { + pn532_packetbuffer[10 + i] = _uid[i]; + } + + if (PN532_writeCommand(pn532_packetbuffer, 10 + _uidLen)) { return 0; } + + + PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer)); + + + + + if (pn532_packetbuffer[0] != 0x00) { + + return 0; + } + + return 1; +} + +uint8_t mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t *data) +{ + + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; + pn532_packetbuffer[2] = MIFARE_CMD_READ; + pn532_packetbuffer[3] = blockNumber; + + + if (PN532_writeCommand(pn532_packetbuffer, 4)) { + return 0; + } + + + PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer)); + + + if (pn532_packetbuffer[0] != 0x00) { + return 0; + } + + + + memcpy (data, &pn532_packetbuffer[1], 16); + + return 1; +} + +uint8_t mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t *data) +{ + + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; + pn532_packetbuffer[2] = MIFARE_CMD_WRITE; + pn532_packetbuffer[3] = blockNumber; + memcpy(&pn532_packetbuffer[4], data, 16); + + + if (PN532_writeCommand(pn532_packetbuffer, 20)) { + return 0; + } + + + return (0 < PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer))); +} + +#endif + +void PN532_ScanForTag(void) +{ + if (!pn532_model) { return; } + uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; + uint8_t uid_len = 0; + uint8_t card_data[16]; + bool erase_success = false; + bool set_success = false; + if (PN532_readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uid_len)) { + char uids[15]; + +#ifdef USE_PN532_DATA_FUNCTION + char card_datas[34]; +#endif + + ToHex_P((unsigned char*)uid, uid_len, uids, sizeof(uids)); + +#ifdef USE_PN532_DATA_FUNCTION + if (uid_len == 4) { + uint8_t keyuniversal[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + if (mifareclassic_AuthenticateBlock (uid, uid_len, 1, 1, keyuniversal)) { + if (mifareclassic_ReadDataBlock(1, card_data)) { +#ifdef USE_PN532_DATA_RAW + memcpy(&card_datas,&card_data,sizeof(card_data)); +#else + for (uint32_t i = 0;i < sizeof(card_data);i++) { + if ((isalpha(card_data[i])) || ((isdigit(card_data[i])))) { + card_datas[i] = char(card_data[i]); + } else { + card_datas[i] = '\0'; + } + } +#endif + } + if (pn532_function == 1) { + for (uint32_t i = 0;i<16;i++) { + card_data[i] = 0x00; + } + if (mifareclassic_WriteDataBlock(1, card_data)) { + erase_success = true; + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Erase success")); + memcpy(&card_datas,&card_data,sizeof(card_data)); + } + } + if (pn532_function == 2) { +#ifdef USE_PN532_DATA_RAW + memcpy(&card_data,&pn532_newdata,sizeof(card_data)); + if (mifareclassic_WriteDataBlock(1, card_data)) { + set_success = true; + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data write successful")); + memcpy(&card_datas,&card_data,sizeof(card_data)); + } +#else + bool IsAlphaNumeric = true; + for (uint32_t i = 0;i < pn532_newdata_len;i++) { + if ((!isalpha(pn532_newdata[i])) && (!isdigit(pn532_newdata[i]))) { + IsAlphaNumeric = false; + } + } + if (IsAlphaNumeric) { + memcpy(&card_data,&pn532_newdata,pn532_newdata_len); + card_data[pn532_newdata_len] = '\0'; + if (mifareclassic_WriteDataBlock(1, card_data)) { + set_success = true; + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data write successful")); + memcpy(&card_datas,&card_data,sizeof(card_data)); + } + } else { + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data must be alphanumeric")); + } +#endif + } + } else { + sprintf(card_datas,"AUTHFAIL"); + } + } + switch (pn532_function) { + case 0x01: + if (!erase_success) { + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Erase fail - exiting erase mode")); + } + break; + case 0x02: + if (!set_success) { + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Write failed - exiting set mode")); + } + default: + break; + } + pn532_function = 0; +#endif + +#ifdef USE_PN532_DATA_FUNCTION + ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\", \"DATA\":\"%s\"}}"), uids, card_datas); +#else + ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\"}}"), uids); +#endif + + MqttPublishTeleSensor(); + +#ifdef USE_PN532_CAUSE_EVENTS + + char command[71]; +#ifdef USE_PN532_DATA_FUNCTION + sprintf(command,"backlog event PN532_UID=%s;event PN532_DATA=%s",uids,card_datas); +#else + sprintf(command,"event PN532_UID=%s",uids); +#endif + ExecuteCommand(command, SRC_RULE); +#endif + + pn532_scantimer = 7; + } +} + +#ifdef USE_PN532_DATA_FUNCTION + +bool PN532_Command(void) +{ + bool serviced = true; + uint8_t paramcount = 0; + if (XdrvMailbox.data_len > 0) { + paramcount=1; + } else { + serviced = false; + return serviced; + } + char sub_string[XdrvMailbox.data_len]; + char sub_string_tmp[XdrvMailbox.data_len]; + for (uint32_t ca=0;ca 1) { + if (XdrvMailbox.data[XdrvMailbox.data_len-1] == ',') { + serviced = false; + return serviced; + } + sprintf(sub_string_tmp,subStr(sub_string, XdrvMailbox.data, ",", 2)); + pn532_newdata_len = strlen(sub_string_tmp); + if (pn532_newdata_len > 15) { pn532_newdata_len = 15; } + memcpy(&pn532_newdata,&sub_string_tmp,pn532_newdata_len); + pn532_newdata[pn532_newdata_len] = 0x00; + 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); + ResponseTime_P(PSTR(",\"PN532\":{\"COMMAND\":\"S\"}}")); + return serviced; + } + } +} + +#endif + +bool Xsns40(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_INIT: + PN532_Init(); + result = true; + break; + case FUNC_EVERY_50_MSECOND: + break; + case FUNC_EVERY_100_MSECOND: + break; + case FUNC_EVERY_250_MSECOND: + if (pn532_scantimer > 0) { + pn532_scantimer--; + } else { + PN532_ScanForTag(); + } + break; + case FUNC_EVERY_SECOND: + break; +#ifdef USE_PN532_DATA_FUNCTION + case FUNC_COMMAND_SENSOR: + if (XSNS_40 == XdrvMailbox.index) { + result = PN532_Command(); + } + break; +#endif + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_41_max44009.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_41_max44009.ino" +#ifdef USE_I2C +#ifdef USE_MAX44009 + + + + + + +#define XSNS_41 41 +#define XI2C_28 28 + +#define MAX44009_ADDR1 0x4A +#define MAX44009_ADDR2 0x4B +#define MAX44009_NO_REGISTERS 8 +#define REG_CONFIG 0x02 +#define REG_LUMINANCE 0x03 +#define REG_LOWER_THRESHOLD 0x06 +#define REG_THRESHOLD_TIMER 0x07 + +#define MAX44009_CONTINUOUS_AUTO_MODE 0x80 + +uint8_t max44009_address; +uint8_t max44009_addresses[] = { MAX44009_ADDR1, MAX44009_ADDR2, 0 }; +uint8_t max44009_found = 0; +uint8_t max44009_valid = 0; +float max44009_illuminance = 0; +char max44009_types[] = "MAX44009"; + +bool Max4409Read_lum(void) +{ + max44009_valid = 0; + uint8_t regdata[2]; + + + if (I2cValidRead16((uint16_t *)®data, max44009_address, REG_LUMINANCE)) { + int exponent = (regdata[0] & 0xF0) >> 4; + int mantissa = ((regdata[0] & 0x0F) << 4) | (regdata[1] & 0x0F); + max44009_illuminance = (float)(((0x00000001 << exponent) * (float)mantissa) * 0.045); + max44009_valid = 1; + return true; + } else { + return false; + } +} + + + +void Max4409Detect(void) +{ + uint8_t buffer1; + uint8_t buffer2; + for (uint32_t i = 0; 0 != max44009_addresses[i]; i++) { + + max44009_address = max44009_addresses[i]; + if (I2cActive(max44009_address)) { continue; } + + if ((I2cValidRead8(&buffer1, max44009_address, REG_LOWER_THRESHOLD)) && + (I2cValidRead8(&buffer2, max44009_address, REG_THRESHOLD_TIMER))) { + + if ((0x00 == buffer1) && + (0xFF == buffer2)) { + + + + Wire.beginTransmission(max44009_address); + + + Wire.write(REG_CONFIG); + Wire.write(MAX44009_CONTINUOUS_AUTO_MODE); + if (0 == Wire.endTransmission()) { + I2cSetActiveFound(max44009_address, max44009_types); + max44009_found = 1; + break; + } + } + } + } +} + +void Max4409EverySecond(void) +{ + Max4409Read_lum(); +} + +void Max4409Show(bool json) +{ + char illum_str[8]; + + if (max44009_valid) { + + + + uint8_t prec = 0; + if (10 > max44009_illuminance ) { + prec = 3; + } else if (100 > max44009_illuminance) { + prec = 2; + } else if (1000 > max44009_illuminance) { + prec = 1; + } + dtostrf(max44009_illuminance, sizeof(illum_str) -1, prec, illum_str); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_ILLUMINANCE "\":%s}"), max44009_types, illum_str); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_ILLUMINANCE, illum_str); + } +#endif +#ifdef USE_WEBSERVER + } else { + + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, max44009_types, (int)max44009_illuminance); +#endif + } + } +} + + + + + +bool Xsns41(uint8_t function) +{ + if (!I2cEnabled(XI2C_28)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Max4409Detect(); + } + else if (max44009_found) { + switch (function) { + case FUNC_EVERY_SECOND: + Max4409EverySecond(); + break; + case FUNC_JSON_APPEND: + Max4409Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Max4409Show(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_42_scd30.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_42_scd30.ino" +#ifdef USE_I2C +#ifdef USE_SCD30 + +#define XSNS_42 42 +#define XI2C_29 29 + + + +#define SCD30_ADDRESS 0x61 + +#define SCD30_MAX_MISSED_READS 3 +#define SCD30_STATE_NO_ERROR 0 +#define SCD30_STATE_ERROR_DATA_CRC 1 +#define SCD30_STATE_ERROR_READ_MEAS 2 +#define SCD30_STATE_ERROR_SOFT_RESET 3 +#define SCD30_STATE_ERROR_I2C_RESET 4 +#define SCD30_STATE_ERROR_UNKNOWN 5 + +#include "Arduino.h" +#include + +#define D_CMND_SCD30 "SCD30" + +const char S_JSON_SCD30_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_SCD30 "%s\":%d}"; +const char S_JSON_SCD30_COMMAND_NFW_VALUE[] PROGMEM = "{\"" D_CMND_SCD30 "%s\":%d.%d}"; +const char S_JSON_SCD30_COMMAND[] PROGMEM = "{\"" D_CMND_SCD30 "%s\"}"; +const char kSCD30_Commands[] PROGMEM = "Alt|Auto|Cal|FW|Int|Pres|TOff"; + + + + + +enum SCD30_Commands { + CMND_SCD30_ALTITUDE, + CMND_SCD30_AUTOMODE, + CMND_SCD30_CALIBRATE, + CMND_SCD30_FW, + CMND_SCD30_INTERVAL, + CMND_SCD30_PRESSURE, + CMND_SCD30_TEMPOFFSET +}; + +FrogmoreScd30 scd30; + +bool scd30Found = false; +bool scd30IsDataValid = false; +int scd30ErrorState = SCD30_STATE_NO_ERROR; +uint16_t scd30Interval_sec; +int scd30Loop_count = 0; +int scd30DataNotAvailable_count = 0; +int scd30GoodMeas_count = 0; +int scd30Reset_count = 0; +int scd30CrcError_count = 0; +int scd30Co2Zero_count = 0; +int i2cReset_count = 0; +uint16_t scd30_CO2 = 0; +uint16_t scd30_CO2EAvg = 0; +float scd30_Humid = 0.0; +float scd30_Temp = 0.0; + +void Scd30Detect(void) +{ + if (I2cActive(SCD30_ADDRESS)) { return; } + + scd30.begin(); + + uint8_t major = 0; + uint8_t minor = 0; + if (scd30.getFirmwareVersion(&major, &minor)) { return; } + uint16_t interval_sec; + if (scd30.getMeasurementInterval(&scd30Interval_sec)) { return; } + if (scd30.beginMeasuring()) { return; } + + I2cSetActiveFound(SCD30_ADDRESS, "SCD30"); + scd30Found = true; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SCD: FW v%d.%d"), major, minor); +} + + +void Scd30Update(void) +{ + scd30Loop_count++; + if (scd30Loop_count > (scd30Interval_sec - 1)) { + int error = 0; + switch (scd30ErrorState) { + case SCD30_STATE_NO_ERROR: { + error = scd30.readMeasurement(&scd30_CO2, &scd30_CO2EAvg, &scd30_Temp, &scd30_Humid); + switch (error) { + case ERROR_SCD30_NO_ERROR: + scd30Loop_count = 0; + scd30IsDataValid = true; + scd30GoodMeas_count++; + break; + + case ERROR_SCD30_NO_DATA: + scd30DataNotAvailable_count++; + break; + + case ERROR_SCD30_CRC_ERROR: + scd30ErrorState = SCD30_STATE_ERROR_DATA_CRC; + scd30CrcError_count++; +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: CRC error, CRC error: %ld, CO2 zero: %ld, good: %ld, no data: %ld, sc30_reset: %ld, i2c_reset: %ld"), + scd30CrcError_count, scd30Co2Zero_count, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); +#endif + break; + + case ERROR_SCD30_CO2_ZERO: + scd30Co2Zero_count++; +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: CO2 zero, CRC error: %ld, CO2 zero: %ld, good: %ld, no data: %ld, sc30_reset: %ld, i2c_reset: %ld"), + scd30CrcError_count, scd30Co2Zero_count, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); +#endif + break; + + default: { + scd30ErrorState = SCD30_STATE_ERROR_READ_MEAS; +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: Update: ReadMeasurement error: 0x%lX, counter: %ld"), error, scd30Loop_count); +#endif + return; + } + break; + } + } + break; + + case SCD30_STATE_ERROR_DATA_CRC: { + +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld"), + scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: got CRC error, try again, counter: %ld"), scd30Loop_count); +#endif + scd30ErrorState = ERROR_SCD30_NO_ERROR; + } + break; + + case SCD30_STATE_ERROR_READ_MEAS: { + +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld"), + scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: not answering, sending soft reset, counter: %ld"), scd30Loop_count); +#endif + scd30Reset_count++; + error = scd30.softReset(); + if (error) { +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: resetting got error: 0x%lX"), error); +#endif + error >>= 8; + if (error == 4) { + scd30ErrorState = SCD30_STATE_ERROR_SOFT_RESET; + } else { + scd30ErrorState = SCD30_STATE_ERROR_UNKNOWN; + } + } else { + scd30ErrorState = ERROR_SCD30_NO_ERROR; + } + } + break; + + case SCD30_STATE_ERROR_SOFT_RESET: { + +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld"), + scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); + AddLog_P(LOG_LEVEL_ERROR, PSTR("SCD30: clearing i2c bus")); +#endif + i2cReset_count++; + error = scd30.clearI2CBus(); + if (error) { + scd30ErrorState = SCD30_STATE_ERROR_I2C_RESET; +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: error clearing i2c bus: 0x%lX"), error); +#endif + } else { + scd30ErrorState = ERROR_SCD30_NO_ERROR; + } + } + break; + + default: { + +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: unknown error state: 0x%lX"), scd30ErrorState); +#endif + scd30ErrorState = SCD30_STATE_ERROR_SOFT_RESET; + } + } + + if (scd30Loop_count > (SCD30_MAX_MISSED_READS * scd30Interval_sec)) { + scd30IsDataValid = false; + } + } +} + + +int Scd30GetCommand(int command_code, uint16_t *pvalue) +{ + switch (command_code) + { + case CMND_SCD30_ALTITUDE: + return scd30.getAltitudeCompensation(pvalue); + break; + + case CMND_SCD30_AUTOMODE: + return scd30.getCalibrationType(pvalue); + break; + + case CMND_SCD30_CALIBRATE: + return scd30.getForcedRecalibrationFactor(pvalue); + break; + + case CMND_SCD30_INTERVAL: + return scd30.getMeasurementInterval(pvalue); + break; + + case CMND_SCD30_PRESSURE: + return scd30.getAmbientPressure(pvalue); + break; + + case CMND_SCD30_TEMPOFFSET: + return scd30.getTemperatureOffset(pvalue); + break; + + default: + + break; + } +} + +int Scd30SetCommand(int command_code, uint16_t value) +{ + switch (command_code) + { + case CMND_SCD30_ALTITUDE: + return scd30.setAltitudeCompensation(value); + break; + + case CMND_SCD30_AUTOMODE: + return scd30.setCalibrationType(value); + break; + + case CMND_SCD30_CALIBRATE: + return scd30.setForcedRecalibrationFactor(value); + break; + + case CMND_SCD30_INTERVAL: + { + int error = scd30.setMeasurementInterval(value); + if (!error) + { + scd30Interval_sec = value; + } + + return error; + } + break; + + case CMND_SCD30_PRESSURE: + return scd30.setAmbientPressure(value); + break; + + case CMND_SCD30_TEMPOFFSET: + return scd30.setTemperatureOffset(value); + break; + + default: + + break; + } +} + + + + + +bool Scd30CommandSensor() +{ + char command[CMDSZ]; + bool serviced = true; + uint8_t prefix_len = strlen(D_CMND_SCD30); + + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_SCD30), prefix_len)) { + int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + prefix_len, kSCD30_Commands); + + switch (command_code) { + case CMND_SCD30_ALTITUDE: + case CMND_SCD30_AUTOMODE: + case CMND_SCD30_CALIBRATE: + case CMND_SCD30_INTERVAL: + case CMND_SCD30_PRESSURE: + case CMND_SCD30_TEMPOFFSET: + { + uint16_t value = 0; + if (XdrvMailbox.data_len > 0) + { + value = XdrvMailbox.payload; + Scd30SetCommand(command_code, value); + } + else + { + Scd30GetCommand(command_code, &value); + } + + Response_P(S_JSON_SCD30_COMMAND_NVALUE, command, value); + } + break; + + case CMND_SCD30_FW: + { + uint8_t major = 0; + uint8_t minor = 0; + int error; + error = scd30.getFirmwareVersion(&major, &minor); + if (error) + { +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: error getting FW version: 0x%lX"), error); +#endif + serviced = false; + } + else + { + Response_P(S_JSON_SCD30_COMMAND_NFW_VALUE, command, major, minor); + } + } + break; + + default: + + serviced = false; + break; + } + } + return serviced; +} + +void Scd30Show(bool json) +{ + if (scd30IsDataValid) + { + float t = ConvertTemp(scd30_Temp); + float h = ConvertHumidity(scd30_Humid); + + if (json) { + ResponseAppend_P(PSTR(",\"SCD30\":{\"" D_JSON_CO2 "\":%d,\"" D_JSON_ECO2 "\":%d,"), scd30_CO2, scd30_CO2EAvg); + ResponseAppendTHD(t, h); + ResponseJsonEnd(); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_AIRQUALITY, scd30_CO2); + DomoticzTempHumPressureSensor(t, h); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_CO2EAVG, "SCD30", scd30_CO2EAvg); + WSContentSend_PD(HTTP_SNS_CO2, "SCD30", scd30_CO2); + WSContentSend_THD("SCD30", t, h); +#endif + } + } +} + + + + + +bool Xsns42(byte function) +{ + if (!I2cEnabled(XI2C_29)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Scd30Detect(); + } + else if (scd30Found) { + switch (function) { + case FUNC_EVERY_SECOND: + Scd30Update(); + break; + case FUNC_COMMAND: + result = Scd30CommandSensor(); + break; + case FUNC_JSON_APPEND: + Scd30Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Scd30Show(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_43_hre.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_43_hre.ino" +#ifdef USE_HRE +# 49 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_43_hre.ino" +#define XSNS_43 43 + +enum hre_states { + hre_idle, + hre_sync, + hre_syncing, + hre_read, + hre_reading, + hre_sleep, + hre_sleeping +}; + +hre_states hre_state = hre_idle; + +float hre_usage = 0; +float hre_rate = 0; +uint32_t hre_usage_time = 0; + +int hre_read_errors = 0; +bool hre_good = false; + + + +int hreReadBit() +{ + digitalWrite(pin[GPIO_HRE_CLOCK], HIGH); + delay(1); + int bit = digitalRead(pin[GPIO_HRE_DATA]); + digitalWrite(pin[GPIO_HRE_CLOCK], LOW); + delay(1); + return bit; +} + + + +char hreReadChar(int &parity_errors) +{ + + hreReadBit(); + + unsigned ch=0; + int sum=0; + for (uint32_t i=0; i<7; i++) + { + int b = hreReadBit(); + ch |= b << i; + sum += b; + } + + + if ( (sum & 0x1) != hreReadBit()) + parity_errors++; + + + hreReadBit(); + + return ch; +} + +void hreInit(void) +{ + hre_read_errors = 0; + hre_good = false; + + pinMode(pin[GPIO_HRE_CLOCK], OUTPUT); + pinMode(pin[GPIO_HRE_DATA], INPUT); + + + + digitalWrite(pin[GPIO_HRE_CLOCK], LOW); + + hre_state = hre_sync; +} + + +void hreEvery50ms(void) +{ + static int sync_counter = 0; + static int sync_run = 0; + + static uint32_t curr_start = 0; + static int read_counter = 0; + static int parity_errors = 0; + static char buff[46]; + + static char ch; + static size_t i; + + switch (hre_state) + { + case hre_sync: + if (uptime < 10) + break; + sync_run = 0; + sync_counter = 0; + hre_state = hre_syncing; + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_syncing")); + break; + + case hre_syncing: + + + for (uint32_t i=0; i<20; i++) + { + if (hreReadBit()) + sync_run++; + else + sync_run = 0; + if (sync_run == 62) + { + hre_state = hre_read; + break; + } + sync_counter++; + } + + if (sync_counter > 1000) + { + hre_state = hre_sleep; + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE D_ERROR)); + } + break; + + + case hre_read: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "sync_run:%d, sync_counter:%d"), sync_run, sync_counter); + read_counter = 0; + parity_errors = 0; + curr_start = uptime; + memset(buff, 0, sizeof(buff)); + hre_state = hre_reading; + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_reading")); + + + + + + case hre_reading: + + buff[read_counter++] = hreReadChar(parity_errors); + buff[read_counter++] = hreReadChar(parity_errors); + + if (read_counter == 46) + { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "pe:%d, re:%d, buff:%s"), + parity_errors, hre_read_errors, buff); + if (parity_errors == 0) + { + float curr_usage; + curr_usage = 0.01 * atol(buff+24); + if (hre_usage_time) + { + double dt = 1.666e-2 * (curr_start - hre_usage_time); + hre_rate = (curr_usage - hre_usage)/dt; + } + hre_usage = curr_usage; + hre_usage_time = curr_start; + hre_good = true; + + hre_state = hre_sleep; + } + else + { + hre_read_errors++; + hre_state = hre_sleep; + } + } + break; + + case hre_sleep: + hre_usage_time = curr_start; + hre_state = hre_sleeping; + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_sleeping")); + + case hre_sleeping: + + + if (uptime - hre_usage_time >= 27) + hre_state = hre_sync; + } +} + +void hreShow(boolean json) +{ + if (!hre_good) + return; + + const char *id = "HRE"; + + char usage[16]; + char rate[16]; + dtostrfd(hre_usage, 2, usage); + dtostrfd(hre_rate, 3, rate); + + if (json) + { + ResponseAppend_P(JSON_SNS_GNGPM, id, usage, rate); +#ifdef USE_WEBSERVER + } + else + { + WSContentSend_PD(HTTP_SNS_GALLONS, id, usage); + WSContentSend_PD(HTTP_SNS_GPM, id, rate); +#endif + } +} + + + + + +bool Xsns43(byte function) +{ + + if (pin[GPIO_HRE_CLOCK] >= 99 || pin[GPIO_HRE_DATA] >= 99) + return false; + + switch (function) + { + case FUNC_INIT: + hreInit(); + break; + case FUNC_EVERY_50_MSECOND: + hreEvery50ms(); + break; + case FUNC_EVERY_SECOND: + break; + case FUNC_JSON_APPEND: + hreShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + hreShow(0); + break; +#endif + } + return false; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_44_sps30.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_44_sps30.ino" +#ifdef USE_I2C +#ifdef USE_SPS30 + +#define XSNS_44 44 +#define XI2C_30 30 + +#define SPS30_ADDR 0x69 + +#include +#include + +uint8_t sps30_ready = 0; +uint8_t sps30_running; + +struct SPS30 { + float PM1_0; + float PM2_5; + float PM4_0; + float PM10; + float NCPM0_5; + float NCPM1_0; + float NCPM2_5; + float NCPM4_0; + float NCPM10; + float TYPSIZ; +} sps30_result; + +#define SPS_CMD_START_MEASUREMENT 0x0010 +#define SPS_CMD_START_MEASUREMENT_ARG 0x0300 +#define SPS_CMD_STOP_MEASUREMENT 0x0104 +#define SPS_CMD_READ_MEASUREMENT 0x0300 +#define SPS_CMD_GET_DATA_READY 0x0202 +#define SPS_CMD_AUTOCLEAN_INTERVAL 0x8004 +#define SPS_CMD_CLEAN 0x5607 +#define SPS_CMD_GET_ACODE 0xd025 +#define SPS_CMD_GET_SERIAL 0xd033 +#define SPS_CMD_RESET 0xd304 +#define SPS_WRITE_DELAY_US 20000 +#define SPS_MAX_SERIAL_LEN 32 + +uint8_t sps30_calc_CRC(uint8_t *data) { + uint8_t crc = 0xFF; + for (uint32_t i = 0; i < 2; i++) { + crc ^= data[i]; + for (uint32_t bit = 8; bit > 0; --bit) { + if(crc & 0x80) { + crc = (crc << 1) ^ 0x31u; + } else { + crc = (crc << 1); + } + } + } + return crc; +} + +void CmdClean(void); + +unsigned char twi_readFrom(unsigned char address, unsigned char* buf, unsigned int len, unsigned char sendStop); + +void sps30_get_data(uint16_t cmd, uint8_t *data, uint8_t dlen) { +unsigned char cmdb[2]; +uint8_t tmp[3]; +uint8_t index=0; +memset(data,0,dlen); +uint8_t twi_buff[64]; + + Wire.beginTransmission(SPS30_ADDR); + cmdb[0]=cmd>>8; + cmdb[1]=cmd; + Wire.write(cmdb,2); + Wire.endTransmission(); + + + dlen/=2; + dlen*=3; + + twi_readFrom(SPS30_ADDR,twi_buff,dlen,1); + + uint8_t bind=0; + while (bind>8; + cmdb[1]=cmd; + + if (cmd==SPS_CMD_START_MEASUREMENT) { + cmdb[2]=SPS_CMD_START_MEASUREMENT_ARG>>8; + cmdb[3]=SPS_CMD_START_MEASUREMENT_ARG&0xff; + cmdb[4]=sps30_calc_CRC(&cmdb[2]); + Wire.write(cmdb,5); + } else { + Wire.write(cmdb,2); + } + Wire.endTransmission(); +} + +void SPS30_Detect(void) +{ + if (!I2cSetDevice(SPS30_ADDR)) { return; } + I2cSetActiveFound(SPS30_ADDR, "SPS30"); + + uint8_t dcode[32]; + sps30_get_data(SPS_CMD_GET_SERIAL,dcode,sizeof(dcode)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("sps30 found with serial: %s"),dcode); + sps30_cmd(SPS_CMD_START_MEASUREMENT); + sps30_running = 1; + sps30_ready = 1; +} + +#define D_UNIT_PM "ug/m3" +#define D_UNIT_NCPM "#/m3" + +#ifdef USE_WEBSERVER +const char HTTP_SNS_SPS30_a[] PROGMEM ="{s}SPS30 " "%s" "{m}%s " D_UNIT_PM "{e}"; +const char HTTP_SNS_SPS30_b[] PROGMEM ="{s}SPS30 " "%s" "{m}%s " D_UNIT_NCPM "{e}"; +const char HTTP_SNS_SPS30_c[] PROGMEM ="{s}SPS30 " "TYPSIZ" "{m}%s " "um" "{e}"; +#endif + +#define PMDP 2 + +#define SPS30_HOURS Settings.sps30_inuse_hours + + + +void SPS30_Every_Second() { + if (!sps30_running) return; + + if (uptime%10==0) { + uint8_t vars[sizeof(float)*10]; + sps30_get_data(SPS_CMD_READ_MEASUREMENT,vars,sizeof(vars)); + float *fp=&sps30_result.PM1_0; + + typedef union { + uint8_t array[4]; + float value; + } ByteToFloat; + + ByteToFloat conv; + + for (uint32_t count=0; count<10; count++) { + for (uint32_t i = 0; i < 4; i++){ + conv.array[3-i] = vars[count*sizeof(float)+i]; + } + *fp++=conv.value; + } + } + + if (uptime%3600==0 && uptime>60) { + + + SPS30_HOURS++; + if (SPS30_HOURS>(7*24)) { + CmdClean(); + SPS30_HOURS=0; + } + } + +} + +void SPS30_Show(bool json) +{ + if (!sps30_running) { return; } + + char str[64]; + if (json) { + dtostrfd(sps30_result.PM1_0,PMDP,str); + ResponseAppend_P(PSTR(",\"SPS30\":{\"" "PM1_0" "\":%s"), str); + dtostrfd(sps30_result.PM2_5,PMDP,str); + ResponseAppend_P(PSTR(",\"" "PM2_5" "\":%s"), str); + dtostrfd(sps30_result.PM4_0,PMDP,str); + ResponseAppend_P(PSTR(",\"" "PM4_0" "\":%s"), str); + dtostrfd(sps30_result.PM10,PMDP,str); + ResponseAppend_P(PSTR(",\"" "PM10" "\":%s"), str); + dtostrfd(sps30_result.NCPM0_5,PMDP,str); + ResponseAppend_P(PSTR(",\"" "NCPM0_5" "\":%s"), str); + dtostrfd(sps30_result.NCPM1_0,PMDP,str); + ResponseAppend_P(PSTR(",\"" "NCPM1_0" "\":%s"), str); + dtostrfd(sps30_result.NCPM2_5,PMDP,str); + ResponseAppend_P(PSTR(",\"" "NCPM2_5" "\":%s"), str); + dtostrfd(sps30_result.NCPM4_0,PMDP,str); + ResponseAppend_P(PSTR(",\"" "NCPM4_0" "\":%s"), str); + dtostrfd(sps30_result.NCPM10,PMDP,str); + ResponseAppend_P(PSTR(",\"" "NCPM10" "\":%s"), str); + dtostrfd(sps30_result.TYPSIZ,PMDP,str); + ResponseAppend_P(PSTR(",\"" "TYPSIZ" "\":%s}"), str); + +#ifdef USE_WEBSERVER + } else { + dtostrfd(sps30_result.PM1_0,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 1.0",str); + dtostrfd(sps30_result.PM2_5,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 2.5",str); + dtostrfd(sps30_result.PM4_0,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 4.0",str); + dtostrfd(sps30_result.PM10,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 10",str); + dtostrfd(sps30_result.NCPM0_5,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 0.5",str); + dtostrfd(sps30_result.NCPM1_0,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 1.0",str); + dtostrfd(sps30_result.NCPM2_5,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 2.5",str); + dtostrfd(sps30_result.NCPM4_0,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 4.0",str); + dtostrfd(sps30_result.NCPM10,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 10",str); + dtostrfd(sps30_result.TYPSIZ,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_c,str); +#endif + } +} + +void CmdClean(void) +{ + sps30_cmd(SPS_CMD_CLEAN); + ResponseTime_P(PSTR(",\"SPS30\":{\"CFAN\":\"true\"}}")); + MqttPublishTeleSensor(); +} + +bool SPS30_cmd(void) +{ + bool serviced = true; + if (XdrvMailbox.data_len > 0) { + char *cp=XdrvMailbox.data; + if (*cp=='c') { + + CmdClean(); + } else if (*cp=='0' || *cp=='1') { + sps30_running=*cp&1; + sps30_cmd(sps30_running?SPS_CMD_START_MEASUREMENT:SPS_CMD_STOP_MEASUREMENT); + } else { + serviced=false; + } + } + Response_P(PSTR("{\"SPS30\":\"%s\"}"), sps30_running?"running":"stopped"); + + return serviced; +} + + + + + +bool Xsns44(byte function) +{ + if (!I2cEnabled(XI2C_30)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + SPS30_Detect(); + } + else if (sps30_ready) { + switch (function) { + case FUNC_EVERY_SECOND: + SPS30_Every_Second(); + break; + case FUNC_JSON_APPEND: + SPS30_Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + SPS30_Show(0); + break; + #endif + case FUNC_COMMAND_SENSOR: + if (XSNS_44 == XdrvMailbox.index) { + result = SPS30_cmd(); + } + break; + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_45_vl53l0x.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_45_vl53l0x.ino" +#ifdef USE_I2C +#ifdef USE_VL53L0X + +#define XSNS_45 45 +#define XI2C_31 31 + +#include +#include "VL53L0X.h" +VL53L0X sensor; + +uint8_t vl53l0x_ready = 0; +uint16_t vl53l0x_distance; +uint16_t Vl53l0_buffer[5]; +uint8_t Vl53l0_index; + + + +void Vl53l0Detect(void) +{ + if (!I2cSetDevice(0x29)) { return; } + + if (!sensor.init()) { return; } + + I2cSetActiveFound(sensor.getAddress(), "VL53L0X"); + + sensor.setTimeout(500); + + + + + + sensor.startContinuous(); + vl53l0x_ready = 1; + + Vl53l0_index=0; +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_VL53L0X[] PROGMEM = + "{s}VL53L0X " D_DISTANCE "{m}%d" D_UNIT_MILLIMETER "{e}"; +#endif + +#define USE_VL_MEDIAN + +void Vl53l0Every_250MSecond(void) +{ + uint16_t tbuff[5],tmp; + uint8_t flag; + + + uint16_t dist = sensor.readRangeContinuousMillimeters(); + if (dist==0 || dist>2000) { + dist=9999; + } + +#ifdef USE_VL_MEDIAN + + Vl53l0_buffer[Vl53l0_index]=dist; + Vl53l0_index++; + if (Vl53l0_index>=5) Vl53l0_index=0; + + + memmove(tbuff,Vl53l0_buffer,sizeof(tbuff)); + for (byte ocnt=0; ocnt<5; ocnt++) { + flag=0; + for (byte count=0; count<4; count++) { + if (tbuff[count]>tbuff[count+1]) { + tmp=tbuff[count]; + tbuff[count]=tbuff[count+1]; + tbuff[count+1]=tmp; + flag=1; + } + } + if (!flag) break; + } + vl53l0x_distance=tbuff[2]; +#else + vl53l0x_distance=dist; +#endif +} + +void Vl53l0Show(boolean json) +{ + if (json) { + ResponseAppend_P(PSTR(",\"VL53L0X\":{\"" D_JSON_DISTANCE "\":%d}"), vl53l0x_distance); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_VL53L0X, vl53l0x_distance); +#endif + } +} + + + + + +bool Xsns45(byte function) +{ + if (!I2cEnabled(XI2C_31)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Vl53l0Detect(); + } + else if (vl53l0x_ready) { + switch (function) { + case FUNC_EVERY_250_MSECOND: + Vl53l0Every_250MSecond(); + break; + case FUNC_JSON_APPEND: + Vl53l0Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Vl53l0Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_46_MLX90614.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_46_MLX90614.ino" +#ifdef USE_I2C +#ifdef USE_MLX90614 + +#define XSNS_46 46 +#define XI2C_32 32 + +#define I2_ADR_IRT 0x5a + +#define MLX90614_RAWIR1 0x04 +#define MLX90614_RAWIR2 0x05 +#define MLX90614_TA 0x06 +#define MLX90614_TOBJ1 0x07 +#define MLX90614_TOBJ2 0x08 + +struct { + union { + uint16_t value; + uint32_t i2c_buf; + }; + float obj_temp; + float amb_temp; + bool ready = false; +} mlx90614; + +void MLX90614_Init(void) +{ + if (!I2cSetDevice(I2_ADR_IRT)) { return; } + I2cSetActiveFound(I2_ADR_IRT, "MLX90614"); + mlx90614.ready = true; +} + +void MLX90614_Every_Second(void) +{ + mlx90614.i2c_buf = I2cRead24(I2_ADR_IRT, MLX90614_TOBJ1); + if (mlx90614.value & 0x8000) { + mlx90614.obj_temp = -999; + } else { + mlx90614.obj_temp = ((float)mlx90614.value * 0.02) - 273.15; + } + mlx90614.i2c_buf = I2cRead24(I2_ADR_IRT,MLX90614_TA); + if (mlx90614.value & 0x8000) { + mlx90614.amb_temp = -999; + } else { + mlx90614.amb_temp = ((float)mlx90614.value * 0.02) - 273.15; + } +} + +#ifdef USE_WEBSERVER + const char HTTP_IRTMP[] PROGMEM = + "{s}MXL90614 " "OBJ-" D_TEMPERATURE "{m}%s C" "{e}" + "{s}MXL90614 " "AMB-" D_TEMPERATURE "{m}%s C" "{e}"; +#endif + +void MLX90614_Show(uint8_t json) +{ + char obj_tstr[16]; + dtostrfd(mlx90614.obj_temp, Settings.flag2.temperature_resolution, obj_tstr); + char amb_tstr[16]; + dtostrfd(mlx90614.amb_temp, Settings.flag2.temperature_resolution, amb_tstr); + + if (json) { + ResponseAppend_P(PSTR(",\"MLX90614\":{\"OBJTMP\":%s,\"AMBTMP\":%s}"), obj_tstr, amb_tstr); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_IRTMP, obj_tstr, amb_tstr); +#endif + } +} + + + + + +bool Xsns46(byte function) +{ + if (!I2cEnabled(XI2C_32)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + MLX90614_Init(); + } + else if (mlx90614.ready) { + switch (function) { + case FUNC_EVERY_SECOND: + MLX90614_Every_Second(); + break; + case FUNC_JSON_APPEND: + MLX90614_Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MLX90614_Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_47_max31865.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_47_max31865.ino" +#ifdef USE_MAX31865 + +#ifndef USE_SPI +#error "MAX31865 requires USE_SPI enabled" +#endif + +#include "Adafruit_MAX31865.h" + +#define XSNS_47 47 + +#if MAX31865_PTD_WIRES == 4 + #define PTD_WIRES MAX31865_4WIRE +#elif MAX31865_PTD_WIRES == 3 + #define PTD_WIRES MAX31865_3WIRE +#else + #define PTD_WIRES MAX31865_2WIRE +#endif + +int8_t init_status = 0; + +Adafruit_MAX31865 max31865; + +struct MAX31865_Result_Struct { + uint8_t ErrorCode; + uint16_t Rtd; + float PtdResistance; + float PtdTemp; +} MAX31865_Result; + +void MAX31865_Init(void){ + if(init_status) + return; + + max31865.setPins( + pin[GPIO_SSPI_CS], + pin[GPIO_SSPI_MOSI], + pin[GPIO_SSPI_MISO], + pin[GPIO_SSPI_SCLK] + ); + + if(max31865.begin(PTD_WIRES)) + init_status = 1; + else + init_status = -1; +} + + + + + +void MAX31865_GetResult(void){ + uint16_t rtd; + + rtd = max31865.readRTD(); + MAX31865_Result.Rtd = rtd; + MAX31865_Result.PtdResistance = max31865.rtd_to_resistance(rtd, MAX31865_REF_RES); + MAX31865_Result.PtdTemp = max31865.rtd_to_temperature(rtd, MAX31865_PTD_RES, MAX31865_REF_RES) + MAX31865_PTD_BIAS; +} + +void MAX31865_Show(bool Json){ + char temperature[33]; + char resistance[33]; + + dtostrfd(MAX31865_Result.PtdResistance, Settings.flag2.temperature_resolution, resistance); + dtostrfd(MAX31865_Result.PtdTemp, Settings.flag2.temperature_resolution, temperature); + + if(Json){ + ResponseAppend_P(PSTR(",\"MAX31865\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_RESISTANCE "\":%s,\"" D_JSON_ERROR "\":%d}"), \ + temperature, resistance, MAX31865_Result.ErrorCode); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_TEMP, temperature); + } +#endif +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, MAX31865_Result.PtdTemp); + } +#endif + } else { +#ifdef USE_WEBSERVER + WSContentSend_PD(HTTP_SNS_TEMP, "MAX31865", temperature, TempUnit()); +#endif + } +} + + + + + +bool Xsns47(uint8_t function) +{ + bool result = false; + if((pin[GPIO_SSPI_MISO] < 99) && (pin[GPIO_SSPI_MOSI] < 99) && + (pin[GPIO_SSPI_SCLK] < 99) && (pin[GPIO_SSPI_CS] < 99)) { + + switch (function) { + case FUNC_INIT: + MAX31865_Init(); + break; + + case FUNC_EVERY_SECOND: + MAX31865_GetResult(); + break; + + case FUNC_JSON_APPEND: + MAX31865_Show(true); + break; + +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MAX31865_Show(false); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_48_chirp.ino" +# 35 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_48_chirp.ino" +#ifdef USE_I2C +#ifdef USE_CHIRP +# 47 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_48_chirp.ino" +#define XSNS_48 48 +#define XI2C_33 33 + +#define CHIRP_MAX_SENSOR_COUNT 3 + +#define CHIRP_ADDR_STANDARD 0x20 + + + + + +#define D_CMND_CHIRP "CHIRP" + +const char S_JSON_CHIRP_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_CHIRP "%s\":%d}"; +const char S_JSON_CHIRP_COMMAND[] PROGMEM = "{\"" D_CMND_CHIRP "%s\"}"; +const char kCHIRP_Commands[] PROGMEM = "Select|Set|Scan|Reset|Sleep|Wake"; + +const char kChirpTypes[] PROGMEM = "CHIRP"; + + + + + +enum CHIRP_Commands { + CMND_CHIRP_SELECT, + CMND_CHIRP_SET, + CMND_CHIRP_SCAN, + CMND_CHIRP_RESET, + CMND_CHIRP_SLEEP, + CMND_CHIRP_WAKE }; + + + + + + +#define CHIRP_GET_CAPACITANCE 0x00 +#define CHIRP_SET_ADDRESS 0x01 +#define CHIRP_GET_ADDRESS 0x02 +#define CHIRP_MEASURE_LIGHT 0x03 +#define CHIRP_GET_LIGHT 0x04 +#define CHIRP_GET_TEMPERATURE 0x05 +#define CHIRP_RESET 0x06 +#define CHIRP_GET_VERSION 0x07 +#define CHIRP_SLEEP 0x08 +#define CHIRP_GET_BUSY 0x09 + + + + + +void ChirpWriteI2CRegister(uint8_t addr, uint8_t reg) { + Wire.beginTransmission(addr); + Wire.write(reg); + Wire.endTransmission(); +} + +uint16_t ChirpFinishReadI2CRegister16bit(uint8_t addr) { + Wire.requestFrom(addr,(uint8_t)2); + uint16_t t = Wire.read() << 8; + t = t | Wire.read(); + return t; +} + + + + + +uint8_t chirp_current = 0; +uint8_t chirp_found_sensors = 0; + +char chirp_name[7]; +uint8_t chirp_next_job = 0; +uint32_t chirp_timeout_count = 0; + +#pragma pack(1) +struct ChirpSensor_t{ + uint16_t moisture = 0; + uint16_t light = 0; + int16_t temperature = 0; + uint8_t version = 0; + uint8_t address:7; + uint8_t explicitSleep:1; +}; +#pragma pack() + +ChirpSensor_t chirp_sensor[CHIRP_MAX_SENSOR_COUNT]; + + + +void ChirpReset(uint8_t addr) { + ChirpWriteI2CRegister(addr, CHIRP_RESET); +} + + + +void ChirpResetAll(void) { + for (uint32_t i = 0; i < chirp_found_sensors; i++) { + if (chirp_sensor[i].version) { + ChirpReset(chirp_sensor[i].address); + } + } +} + + +void ChirpClockSet() { + Wire.setClockStretchLimit(4000); + Wire.setClock(50000); +} + + + +void ChirpSleep(uint8_t addr) { + ChirpWriteI2CRegister(addr, CHIRP_SLEEP); +} +# 185 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_48_chirp.ino" +void ChirpSelect(uint8_t sensor) { + if(sensor < chirp_found_sensors) { + chirp_current = sensor; + DEBUG_SENSOR_LOG(PSTR("CHIRP: Sensor %u now active."), chirp_current); + } + if (sensor == 255) { + DEBUG_SENSOR_LOG(PSTR("CHIRP: Sensor %u active at address 0x%x."), chirp_current, chirp_sensor[chirp_current].address); + } +} + + + +uint8_t ChirpReadVersion(uint8_t addr) { + return (I2cRead8(addr, CHIRP_GET_VERSION)); +} + + + +bool ChirpSet(uint8_t addr) { + if(addr < 128){ + if (I2cWrite8(chirp_sensor[chirp_current].address, CHIRP_SET_ADDRESS, addr)){ + if(chirp_sensor[chirp_current].version>0x25 && chirp_sensor[chirp_current].version != 255){ + delay(5); + I2cWrite8(chirp_sensor[chirp_current].address, CHIRP_SET_ADDRESS, addr); + + } + DEBUG_SENSOR_LOG(PSTR("CHIRP: Wrote adress %u "), addr); + ChirpReset(chirp_sensor[chirp_current].address); + chirp_sensor[chirp_current].address = addr; + chirp_timeout_count = 10; + chirp_next_job = 0; + if(chirp_sensor[chirp_current].version == 255){ + AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: wrote new address %u, please power off device"), addr); + chirp_sensor[chirp_current].version == 0; + } + return true; + } + } + AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: address %u incorrect and not used"), addr); + return false; +} + + + +bool ChirpScan() +{ + ChirpClockSet(); + chirp_found_sensors = 0; + for (uint8_t address = 1; address <= 127; address++) { + chirp_sensor[chirp_found_sensors].version = 0; + chirp_sensor[chirp_found_sensors].version = ChirpReadVersion(address); + delay(2); + chirp_sensor[chirp_found_sensors].version = ChirpReadVersion(address); + if (chirp_sensor[chirp_found_sensors].version > 0) { + I2cSetActiveFound(address, "CHIRP"); + if (chirp_found_sensors 0); +} + + + +void ChirpDetect(void) +{ + if (chirp_next_job > 0) { return; } + + DEBUG_SENSOR_LOG(PSTR("CHIRP: scan will start ...")); + if (ChirpScan()) { + uint8_t chirp_model = 0; + GetTextIndexed(chirp_name, sizeof(chirp_name), chirp_model, kChirpTypes); + } +} + + +void ChirpServiceAllSensors(uint8_t job){ + for (uint32_t i = 0; i < chirp_found_sensors; i++) { + if (chirp_sensor[i].version && !chirp_sensor[i].explicitSleep) { + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare for sensor at address 0x%x"), chirp_sensor[i].address); + switch(job){ + case 0: + ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_CAPACITANCE); + break; + case 1: + chirp_sensor[i].moisture = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address); + break; + case 2: + ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_TEMPERATURE); + break; + case 3: + chirp_sensor[i].temperature = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address); + break; + case 4: + ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_MEASURE_LIGHT); + break; + case 5: + ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_LIGHT); + break; + case 6: + chirp_sensor[i].light = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address); + break; + default: + break; + } + } + } +} + + + +void ChirpEvery100MSecond(void) +{ + + if(chirp_timeout_count == 0) { + switch(chirp_next_job) { + case 0: + DEBUG_SENSOR_LOG(PSTR("CHIRP: reset all")); + ChirpResetAll(); + chirp_timeout_count = 10; + chirp_next_job++; + break; + case 1: + + + chirp_next_job++; + break; + case 2: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare moisture read")); + ChirpServiceAllSensors(0); + chirp_timeout_count = 11; + chirp_next_job++; + break; + case 3: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish moisture read")); + ChirpServiceAllSensors(1); + chirp_next_job++; + break; + case 4: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare moisture read - 2nd")); + ChirpServiceAllSensors(0); + chirp_timeout_count = 11; + chirp_next_job++; + break; + case 5: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish moisture read - 2nd")); + ChirpServiceAllSensors(1); + chirp_next_job++; + break; + case 6: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare temperature read")); + ChirpServiceAllSensors(2); + chirp_timeout_count = 11; + chirp_next_job++; + break; + case 7: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish temperature read")); + ChirpServiceAllSensors(3); + chirp_next_job++; + break; + case 8: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare temperature read - 2nd")); + ChirpServiceAllSensors(2); + chirp_timeout_count = 11; + chirp_next_job++; + break; + case 9: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish temperature read - 2nd")); + ChirpServiceAllSensors(3); + chirp_next_job++; + break; + case 10: + DEBUG_SENSOR_LOG(PSTR("CHIRP: start light measure process")); + ChirpServiceAllSensors(4); + chirp_timeout_count = 90; + chirp_next_job++; + break; + case 11: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare light read")); + ChirpServiceAllSensors(5); + chirp_timeout_count = 11; + chirp_next_job++; + break; + case 12: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish light read")); + ChirpServiceAllSensors(6); + chirp_next_job++; + break; + case 13: + DEBUG_SENSOR_LOG(PSTR("CHIRP: paused, waiting for TELE")); + break; + case 14: + if (Settings.tele_period > 16){ + chirp_timeout_count = (Settings.tele_period - 17) * 10; + DEBUG_SENSOR_LOG(PSTR("CHIRP: timeout 1/10 sec: %u, tele: %u"), chirp_timeout_count, Settings.tele_period); + } + else{ + AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: TELEPERIOD must be > 16 seconds !")); + + } + chirp_next_job = 1; + break; + } + } + else { + chirp_timeout_count--; + } +} + + + + +#ifdef USE_WEBSERVER + + +const char HTTP_SNS_DARKNESS[] PROGMEM = "{s} " D_JSON_DARKNESS "{m}%s %%{e}"; +const char HTTP_SNS_CHIRPVER[] PROGMEM = "{s} CHIRP-sensor %u at address{m}0x%x{e}" + "{s} FW-version{m}%s {e}"; ; +const char HTTP_SNS_CHIRPSLEEP[] PROGMEM = "{s} {m} is sleeping ...{e}"; +#endif + + + +void ChirpShow(bool json) +{ + for (uint32_t i = 0; i < chirp_found_sensors; i++) { + if (chirp_sensor[i].version) { + + char str_temperature[33]; + double t_temperature = ((double) chirp_sensor[i].temperature )/10.0; + dtostrfd(t_temperature, Settings.flag2.temperature_resolution, str_temperature); + char str_light[33]; + dtostrfd(chirp_sensor[i].light, 0, str_light); + char str_version[7]; + if(chirp_sensor[i].version == 0xff){ + strncpy_P(str_version, PSTR("Chirp!"), sizeof(str_version)); + } + else{ + sprintf(str_version, "%x", chirp_sensor[i].version); + } + + if (json) { + if(!chirp_sensor[i].explicitSleep) { + ResponseAppend_P(PSTR(",\"%s%u\":{\"" D_JSON_MOISTURE "\":%d"), chirp_name, i, chirp_sensor[i].moisture); + if(chirp_sensor[i].temperature!=-1){ + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s"),str_temperature); + } + ResponseAppend_P(PSTR(",\"" D_JSON_DARKNESS "\":%s}"),str_light); + } + else { + ResponseAppend_P(PSTR(",\"%s%u\":{\"sleeping\"}"),chirp_name, i); + } + #ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzTempHumPressureSensor(t_temperature, chirp_sensor[i].moisture); + DomoticzSensor(DZ_ILLUMINANCE,chirp_sensor[i].light); + } + #endif + #ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_CHIRPVER, i, chirp_sensor[i].address, str_version); + if (chirp_sensor[i].explicitSleep){ + WSContentSend_PD(HTTP_SNS_CHIRPSLEEP); + } + else { + WSContentSend_PD(HTTP_SNS_MOISTURE, "", chirp_sensor[i].moisture); + WSContentSend_PD(HTTP_SNS_DARKNESS, str_light); + if (chirp_sensor[i].temperature!=-1) { + WSContentSend_PD(HTTP_SNS_TEMP, "", str_temperature, TempUnit()); + } + } + + #endif + } + } + } +} + + + + + +bool ChirpCmd(void) { + char command[CMDSZ]; + bool serviced = true; + uint8_t disp_len = strlen(D_CMND_CHIRP); + + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_CHIRP), disp_len)) { + int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kCHIRP_Commands); + + switch (command_code) { + case CMND_CHIRP_SELECT: + case CMND_CHIRP_SET: + if (XdrvMailbox.data_len > 0) { + if (command_code == CMND_CHIRP_SELECT) { ChirpSelect(XdrvMailbox.payload); } + if (command_code == CMND_CHIRP_SET) { ChirpSet((uint8_t)XdrvMailbox.payload); } + Response_P(S_JSON_CHIRP_COMMAND_NVALUE, command, XdrvMailbox.payload); + } + else { + if (command_code == CMND_CHIRP_SELECT) { ChirpSelect(255); } + Response_P(S_JSON_CHIRP_COMMAND, command, XdrvMailbox.payload); + } + break; + case CMND_CHIRP_SCAN: + case CMND_CHIRP_SLEEP: + case CMND_CHIRP_WAKE: + case CMND_CHIRP_RESET: + if (command_code == CMND_CHIRP_SCAN) { chirp_next_job = 0; + ChirpDetect(); } + if (command_code == CMND_CHIRP_SLEEP) { chirp_sensor[chirp_current].explicitSleep = true; + ChirpSleep(chirp_sensor[chirp_current].address); } + if (command_code == CMND_CHIRP_WAKE) { chirp_sensor[chirp_current].explicitSleep = false; + ChirpReadVersion(chirp_sensor[chirp_current].address); } + if (command_code == CMND_CHIRP_RESET) { ChirpReset(chirp_sensor[chirp_current].address); } + Response_P(S_JSON_CHIRP_COMMAND, command, XdrvMailbox.payload); + break; + default: + + serviced = false; + break; + } + } + return serviced; +} + + + + + +bool Xsns48(uint8_t function) +{ + if (!I2cEnabled(XI2C_33)) { return false; } + + bool result = false; + + switch (function) { + case FUNC_EVERY_100_MSECOND: + if(chirp_found_sensors > 0){ + ChirpEvery100MSecond(); + } + break; + case FUNC_COMMAND: + result = ChirpCmd(); + break; + case FUNC_JSON_APPEND: + ChirpShow(1); + chirp_next_job = 14; + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + ChirpShow(0); + break; +#endif + case FUNC_INIT: + ChirpDetect(); + break; + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_50_paj7620.ino" +# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_50_paj7620.ino" +#ifdef USE_I2C +#ifdef USE_PAJ7620 + + + + + + +#define XSNS_50 50 +#define XI2C_34 34 + +#define PAJ7620_ADDR 0x73 + +#define PAJ7620_BANK_SEL 0xEF + + + +#define PAJ7620_GET_GESTURE 0x43 +#define PAJ7620_PROXIMITY_AVG_Y 0x6c + +#define PAJ7620_OBJECT_CENTER_X 0xad +#define PAJ7620_OBJECT_CENTER_Y 0xaf + +#define PAJ7620_DOWN 1 +#define PAJ7620_UP 2 +#define PAJ7620_RIGHT 4 +#define PAJ7620_LEFT 8 +#define PAJ7620_NEAR 16 +#define PAJ7620_FAR 32 +#define PAJ7620_CW 64 +#define PAJ7620_CCW 128 + + + + +const uint8_t PAJ7620initRegisterArray[][2] PROGMEM = { + {0xEF,0x00}, + {0x32,0x29}, {0x33,0x01}, {0x34,0x00}, {0x35,0x01}, {0x36,0x00}, {0x37,0x07}, {0x38,0x17}, {0x39,0x06}, + {0x3A,0x12}, {0x3F,0x00}, {0x40,0x02}, {0x41,0xFF}, {0x42,0x01}, {0x46,0x2D}, {0x47,0x0F}, {0x48,0x3C}, + {0x49,0x00}, {0x4A,0x1E}, {0x4B,0x00}, {0x4C,0x20}, {0x4D,0x00}, {0x4E,0x1A}, {0x4F,0x14}, {0x50,0x00}, + {0x51,0x10}, {0x52,0x00}, {0x5C,0x02}, {0x5D,0x00}, {0x5E,0x10}, {0x5F,0x3F}, {0x60,0x27}, {0x61,0x28}, + {0x62,0x00}, {0x63,0x03}, {0x64,0xF7}, {0x65,0x03}, {0x66,0xD9}, {0x67,0x03}, {0x68,0x01}, {0x69,0xC8}, + {0x6A,0x40}, {0x6D,0x04}, {0x6E,0x00}, {0x6F,0x00}, {0x70,0x80}, {0x71,0x00}, {0x72,0x00}, {0x73,0x00}, + {0x74,0xF0}, {0x75,0x00}, {0x80,0x42}, {0x81,0x44}, {0x82,0x04}, {0x83,0x20}, {0x84,0x20}, {0x85,0x00}, + {0x86,0x10}, {0x87,0x00}, {0x88,0x05}, {0x89,0x18}, {0x8A,0x10}, {0x8B,0x01}, {0x8C,0x37}, {0x8D,0x00}, + {0x8E,0xF0}, {0x8F,0x81}, {0x90,0x06}, {0x91,0x06}, {0x92,0x1E}, {0x93,0x0D}, {0x94,0x0A}, {0x95,0x0A}, + {0x96,0x0C}, {0x97,0x05}, {0x98,0x0A}, {0x99,0x41}, {0x9A,0x14}, {0x9B,0x0A}, {0x9C,0x3F}, {0x9D,0x33}, + {0x9E,0xAE}, {0x9F,0xF9}, {0xA0,0x48}, {0xA1,0x13}, {0xA2,0x10}, {0xA3,0x08}, {0xA4,0x30}, {0xA5,0x19}, + {0xA6,0x10}, {0xA7,0x08}, {0xA8,0x24}, {0xA9,0x04}, {0xAA,0x1E}, {0xAB,0x1E}, {0xCC,0x19}, {0xCD,0x0B}, + {0xCE,0x13}, {0xCF,0x64}, {0xD0,0x21}, {0xD1,0x0F}, {0xD2,0x88}, {0xE0,0x01}, {0xE1,0x04}, {0xE2,0x41}, + {0xE3,0xD6}, {0xE4,0x00}, {0xE5,0x0C}, {0xE6,0x0A}, {0xE7,0x00}, {0xE8,0x00}, {0xE9,0x00}, {0xEE,0x07}, + {0xEF,0x01}, + {0x00,0x1E}, {0x01,0x1E}, {0x02,0x0F}, {0x03,0x10}, {0x04,0x02}, {0x05,0x00}, {0x06,0xB0}, {0x07,0x04}, + {0x08,0x0D}, {0x09,0x0E}, {0x0A,0x9C}, {0x0B,0x04}, {0x0C,0x05}, {0x0D,0x0F}, {0x0E,0x02}, {0x0F,0x12}, + {0x10,0x02}, {0x11,0x02}, {0x12,0x00}, {0x13,0x01}, {0x14,0x05}, {0x15,0x07}, {0x16,0x05}, {0x17,0x07}, + {0x18,0x01}, {0x19,0x04}, {0x1A,0x05}, {0x1B,0x0C}, {0x1C,0x2A}, {0x1D,0x01}, {0x1E,0x00}, {0x21,0x00}, + {0x22,0x00}, {0x23,0x00}, {0x25,0x01}, {0x26,0x00}, {0x27,0x39}, {0x28,0x7F}, {0x29,0x08}, {0x30,0x03}, + {0x31,0x00}, {0x32,0x1A}, {0x33,0x1A}, {0x34,0x07}, {0x35,0x07}, {0x36,0x01}, {0x37,0xFF}, {0x38,0x36}, + {0x39,0x07}, {0x3A,0x00}, {0x3E,0xFF}, {0x3F,0x00}, {0x40,0x77}, {0x41,0x40}, {0x42,0x00}, {0x43,0x30}, + {0x44,0xA0}, {0x45,0x5C}, {0x46,0x00}, {0x47,0x00}, {0x48,0x58}, {0x4A,0x1E}, {0x4B,0x1E}, {0x4C,0x00}, + {0x4D,0x00}, {0x4E,0xA0}, {0x4F,0x80}, {0x50,0x00}, {0x51,0x00}, {0x52,0x00}, {0x53,0x00}, {0x54,0x00}, + {0x57,0x80}, {0x59,0x10}, {0x5A,0x08}, {0x5B,0x94}, {0x5C,0xE8}, {0x5D,0x08}, {0x5E,0x3D}, {0x5F,0x99}, + {0x60,0x45}, {0x61,0x40}, {0x63,0x2D}, {0x64,0x02}, {0x65,0x96}, {0x66,0x00}, {0x67,0x97}, {0x68,0x01}, + {0x69,0xCD}, {0x6A,0x01}, {0x6B,0xB0}, {0x6C,0x04}, {0x6D,0x2C}, {0x6E,0x01}, {0x6F,0x32}, {0x71,0x00}, + {0x72,0x01}, {0x73,0x35}, {0x74,0x00}, {0x75,0x33}, {0x76,0x31}, {0x77,0x01}, {0x7C,0x84}, {0x7D,0x03}, + {0x7E,0x01}, + {0xEF,0x00} +}; + + + + + +const char kPaj7620Directions[] PROGMEM = "Down|Up|Right|Left|Near|Far|CW|CCW"; + +const uint8_t PAJ7620_PIN[]= {1,2,3,4}; + + + + + +char PAJ7620_name[] = "PAJ7620"; + +uint32_t PAJ7620_timeout_counter = 10; +uint32_t PAJ7620_next_job = 0; +uint32_t PAJ7620_mode = 1; + +struct { + uint8_t current; + uint8_t last; + uint8_t same; + uint8_t unfinished; +} PAJ7620_gesture; + +bool PAJ7620_finished_gesture = false; +char PAJ7620_currentGestureName[6]; + +struct{ + uint8_t x; + uint8_t y; + uint8_t last_x; + uint8_t last_y; + uint8_t proximity; + uint8_t last_proximity; + uint8_t corner; + struct { + uint8_t step:3; + uint8_t countdown:3; + uint8_t valid:1; + } PIN; +} PAJ7620_state; + + + + + +void PAJ7620SelectBank(uint8_t bank) +{ + I2cWrite(PAJ7620_ADDR, PAJ7620_BANK_SEL, bank &1, 1); +} + + + +void PAJ7620DecodeGesture(void) +{ + uint32_t index = 0; + switch (PAJ7620_gesture.current) { + case PAJ7620_LEFT: + index++; + case PAJ7620_RIGHT: + index++; + case PAJ7620_UP: + index++; + case PAJ7620_DOWN: + if (PAJ7620_gesture.unfinished) { + PAJ7620_finished_gesture = true; + break; + } + PAJ7620_gesture.unfinished = PAJ7620_gesture.current; + PAJ7620_timeout_counter = 5; + break; + case PAJ7620_NEAR: + index = 4; + PAJ7620_finished_gesture = true; + PAJ7620_timeout_counter = 25; + break; + case PAJ7620_FAR: + index = 5; + PAJ7620_finished_gesture = true; + PAJ7620_timeout_counter = 25; + break; + case PAJ7620_CW: + index = 6; + PAJ7620_finished_gesture = true; + break; + case PAJ7620_CCW: + index = 7; + PAJ7620_finished_gesture = true; + break; + default: + index = 8; + if (PAJ7620_gesture.unfinished) { + PAJ7620_finished_gesture = true; + } + break; + } + if (index < 8) { + GetTextIndexed(PAJ7620_currentGestureName, sizeof(PAJ7620_currentGestureName), index, kPaj7620Directions); + } + + if (PAJ7620_finished_gesture) { + if (PAJ7620_gesture.unfinished) { + if ((PAJ7620_gesture.current != PAJ7620_NEAR) && (PAJ7620_gesture.current != PAJ7620_FAR)) { + PAJ7620_gesture.current = PAJ7620_gesture.unfinished; + } + } + if (PAJ7620_gesture.current == PAJ7620_gesture.last) { + PAJ7620_gesture.same++; + } else { + PAJ7620_gesture.same = 1; + } + PAJ7620_gesture.last = PAJ7620_gesture.current; + PAJ7620_finished_gesture = false; + PAJ7620_gesture.unfinished = 0; + PAJ7620_timeout_counter += 3; + MqttPublishSensor(); + } +} + + + +void PAJ7620ReadGesture(void) +{ + switch (PAJ7620_mode) { + case 1: + PAJ7620_gesture.current = I2cRead8(PAJ7620_ADDR,PAJ7620_GET_GESTURE); + if ((PAJ7620_gesture.current > 0) || PAJ7620_gesture.unfinished) { + DEBUG_SENSOR_LOG(PSTR("PAJ: gesture: %u"), PAJ7620_gesture.current); + PAJ7620DecodeGesture(); + } + break; + case 2: + PAJ7620_state.proximity = I2cRead8(PAJ7620_ADDR, PAJ7620_PROXIMITY_AVG_Y); + if ((PAJ7620_state.proximity > 0) || (PAJ7620_state.last_proximity > 0)) { + if (PAJ7620_state.proximity != PAJ7620_state.last_proximity) { + PAJ7620_state.last_proximity = PAJ7620_state.proximity; + DEBUG_SENSOR_LOG(PSTR("PAJ: Proximity: %u"), PAJ7620_state.proximity); + MqttPublishSensor(); + } + } + break; + case 3: + case 4: + case 5: + PAJ7620_state.x = I2cRead8(PAJ7620_ADDR, PAJ7620_OBJECT_CENTER_X); + PAJ7620_state.y = I2cRead8(PAJ7620_ADDR, PAJ7620_OBJECT_CENTER_Y); + if ((PAJ7620_state.y > 0) && (PAJ7620_state.x > 0)) { + if ((PAJ7620_state.y != PAJ7620_state.last_y) || (PAJ7620_state.x != PAJ7620_state.last_x)) { + PAJ7620_state.last_y = PAJ7620_state.y; + PAJ7620_state.last_x = PAJ7620_state.x; + DEBUG_SENSOR_LOG(PSTR("PAJ: x: %u y: %u"), PAJ7620_state.x, PAJ7620_state.y); + + PAJ7620_state.corner = 0; + + + + switch (PAJ7620_state.y) { + case 0: case 1: case 2: case 3: case 4: case 5: + PAJ7620_state.corner = 3; + break; + case 9: case 10: case 11: case 12: case 13: case 14: + PAJ7620_state.corner = 1; + break; + } + if (PAJ7620_state.corner != 0) { + switch (PAJ7620_state.x) { + case 0: case 1: case 2: case 3: case 4: case 5: + break; + case 9: case 10: case 11: case 12: case 13: case 14: + PAJ7620_state.corner++; + break; + default: + PAJ7620_state.corner = 0; + break; + } + } + DEBUG_SENSOR_LOG(PSTR("PAJ: corner: %u"), PAJ7620_state.corner); + + if (PAJ7620_state.PIN.countdown == 0) { + PAJ7620_state.PIN.step = 0; + PAJ7620_state.PIN.valid = 0; + } + if (!PAJ7620_state.PIN.step) { + if (PAJ7620_state.corner == PAJ7620_PIN[PAJ7620_state.PIN.step]) { + PAJ7620_state.PIN.step = 1; + PAJ7620_state.PIN.countdown = 7; + } + } else { + if (PAJ7620_state.corner == PAJ7620_PIN[PAJ7620_state.PIN.step]) { + PAJ7620_state.PIN.step += 1; + PAJ7620_state.PIN.countdown = 7; + } else { + PAJ7620_state.PIN.countdown -= 1; + } + } + if (PAJ7620_state.PIN.step == 4) { + PAJ7620_state.PIN.valid = 1; + DEBUG_SENSOR_LOG(PSTR("PAJ: PIN valid!!")); + PAJ7620_state.PIN.countdown = 0; + } + MqttPublishSensor(); + } + } + break; + } +} + + + +void PAJ7620Detect(void) +{ + if (I2cActive(PAJ7620_ADDR)) { return; } + + PAJ7620SelectBank(0); + PAJ7620SelectBank(0); + uint16_t PAJ7620_id = I2cRead16LE(PAJ7620_ADDR,0); + uint8_t PAJ7620_ver = I2cRead8(PAJ7620_ADDR,2); + if (0x7620 == PAJ7620_id) { + I2cSetActiveFound(PAJ7620_ADDR, PAJ7620_name); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAJ: ID: 0x%x and VER: %u"), PAJ7620_id, PAJ7620_ver); + PAJ7620_next_job = 1; + } + else { + DEBUG_SENSOR_LOG(PSTR("PAJ: sensor not found, false ID 0x%x"), PAJ7620_id); + } +} + + + +void PAJ7620Init(void) +{ + DEBUG_SENSOR_LOG(PSTR("PAJ: init sensor start %u"),millis()); + union{ + uint32_t raw; + uint8_t reg_val[4]; + } buf; + + for (uint32_t i = 0; i < (sizeof(PAJ7620initRegisterArray) / 2); i += 2) + { + buf.raw = pgm_read_dword(PAJ7620initRegisterArray + i); + DEBUG_SENSOR_LOG("PAJ: %x %x %x %x",buf.reg_val[0],buf.reg_val[1],buf.reg_val[2],buf.reg_val[3]); + I2cWrite(PAJ7620_ADDR, buf.reg_val[0], buf.reg_val[1], 1); + I2cWrite(PAJ7620_ADDR, buf.reg_val[2], buf.reg_val[3], 1); + } + DEBUG_SENSOR_LOG(PSTR("PAJ: init sensor done %u"),millis()); + PAJ7620_next_job = 2; +} + + + +void PAJ7620Loop(void) +{ + if (0 == PAJ7620_timeout_counter) { + switch (PAJ7620_next_job) { + case 1: + PAJ7620Init(); + break; + case 2: + if (PAJ7620_mode != 0) { + PAJ7620ReadGesture(); + } + break; + } + } else { + PAJ7620_timeout_counter--; + } +} + + + +void PAJ7620Show(bool json) +{ + if (json) { + if (PAJ7620_currentGestureName[0] != '\0' ) { + ResponseAppend_P(PSTR(",\"%s\":{\"%s\":%u}"), PAJ7620_name, PAJ7620_currentGestureName, PAJ7620_gesture.same); + PAJ7620_currentGestureName[0] = '\0'; + return; + } + switch (PAJ7620_mode) { + case 2: + ResponseAppend_P(PSTR(",\"%s\":{\"Proximity\":%u}"), PAJ7620_name, PAJ7620_state.proximity); + break; + case 3: + if (PAJ7620_state.corner > 0) { + ResponseAppend_P(PSTR(",\"%s\":{\"Corner\":%u}"), PAJ7620_name, PAJ7620_state.corner); + } + break; + case 4: + if (PAJ7620_state.PIN.valid) { + ResponseAppend_P(PSTR(",\"%s\":{\"PIN\":%u}"), PAJ7620_name, 1); + PAJ7620_state.PIN.valid = 0; + } + break; + case 5: + ResponseAppend_P(PSTR(",\"%s\":{\"x\":%u,\"y\":%u}"), PAJ7620_name, PAJ7620_state.x, PAJ7620_state.y); + break; + } + } +} +# 411 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_50_paj7620.ino" +bool PAJ7620CommandSensor(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) { + PAJ7620_mode = XdrvMailbox.payload; + } + Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_50, PAJ7620_mode); + + return true; +} + + + + + +bool Xsns50(uint8_t function) +{ + if (!I2cEnabled(XI2C_34)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + PAJ7620Detect(); + } + else if (PAJ7620_next_job) { + switch (function) { + case FUNC_COMMAND_SENSOR: + if (XSNS_50 == XdrvMailbox.index){ + result = PAJ7620CommandSensor(); + } + break; + case FUNC_EVERY_100_MSECOND: + PAJ7620Loop(); + break; + case FUNC_JSON_APPEND: + PAJ7620Show(1); + break; + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_51_rdm6300.ino" +# 21 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_51_rdm6300.ino" +#ifdef USE_RDM6300 + +#define XSNS_51 51 + +#define RDM6300_BAUDRATE 9600 + +#include + +#define RDM_TIMEOUT 100 +char rdm_uid_str[10]; + + +#define RDM6300_BLOCK 2*10 + +uint8_t rdm_blcnt; +TasmotaSerial *RDM6300_Serial = nullptr; + +void RDM6300_Init() { + if (pin[GPIO_RDM6300_RX] < 99) { + RDM6300_Serial = new TasmotaSerial(pin[GPIO_RDM6300_RX],-1,1); + if (RDM6300_Serial->begin(RDM6300_BAUDRATE)) { + if (RDM6300_Serial->hardwareSerial()) { + ClaimSerial(); + } + } + } + rdm_blcnt=0; +} + + +void RDM6300_ScanForTag() { + char rdm_buffer[14]; + uint8_t rdm_index; + uint8_t rdm_array[6]; + + if (!RDM6300_Serial) return; + + if (rdm_blcnt>0) { + rdm_blcnt--; + while (RDM6300_Serial->available()) RDM6300_Serial->read(); + return; + } + + if (RDM6300_Serial->available()) { + + char c=RDM6300_Serial->read(); + if (c!=2) return; + + + rdm_index=0; + uint32_t cmillis=millis(); + while (1) { + if (RDM6300_Serial->available()) { + char c=RDM6300_Serial->read(); + if (c==3) { + + break; + } + rdm_buffer[rdm_index++]=c; + if (rdm_index>13) { + + return; + } + } + if ((millis()-cmillis)>RDM_TIMEOUT) { + + return; + } + } + + + rdm_blcnt=RDM6300_BLOCK; + + + rm6300_hstring_to_array(rdm_array,sizeof(rdm_array),rdm_buffer); + uint8_t accu=0; + for (uint8_t count=0;count<5;count++) { + accu^=rdm_array[count]; + } + if (accu!=rdm_array[5]) { + + return; + } + + + memcpy(rdm_uid_str,&rdm_buffer[2],8); + rdm_uid_str[9]=0; + + ResponseTime_P(PSTR(",\"RDM6300\":{\"UID\":\"%s\"}}"), rdm_uid_str); + MqttPublishTeleSensor(); + + + + + + } + + +} + +uint8_t rm6300_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; +} + + +void rm6300_hstring_to_array(uint8_t array[], uint8_t len, char buffer[]) +{ + char *cp=buffer; + for (uint8_t i = 0; i < len; i++) { + uint8_t val = rm6300_hexnibble(*cp++) << 4; + array[i]= val | rm6300_hexnibble(*cp++); + } +} + +#ifdef USE_WEBSERVER +const char HTTP_RDM6300[] PROGMEM = + "{s}RDM6300 " "UID" "{m}%s" "{e}"; + +void RDM6300_Show(void) { + if (!RDM6300_Serial) return; + if (!rdm_uid_str[0]) strcpy(rdm_uid_str,"????"); + WSContentSend_PD(HTTP_RDM6300,rdm_uid_str); +} +#endif + + + + + +bool Xsns51(byte function) +{ + bool result = false; + + switch (function) { + case FUNC_INIT: + RDM6300_Init(); + break; + case FUNC_EVERY_100_MSECOND: + RDM6300_ScanForTag(); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + RDM6300_Show(); + break; +#endif + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_52_ibeacon.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_52_ibeacon.ino" +#ifdef USE_IBEACON + + + +#define XSNS_52 52 + +#include + +#define HM17_BAUDRATE 9600 + +#define IBEACON_DEBUG + + +#define HM17_V110 + + + +#define IB_TIMEOUT_INTERVAL 30 + +#define IB_UPDATE_TIME_INTERVAL 10 + +TasmotaSerial *IBEACON_Serial = nullptr; + + +uint8_t hm17_found,hm17_cmd,hm17_flag; + +#ifdef IBEACON_DEBUG +uint8_t hm17_debug=0; +#endif + + + +#define HM17_BSIZ 128 +char hm17_sbuffer[HM17_BSIZ]; +uint8_t hm17_sindex,hm17_result,hm17_scanning,hm17_connecting; +uint32_t hm17_lastms; +char ib_mac[14]; + + +#if 1 +uint8_t ib_upd_interval,ib_tout_interval; +#define IB_UPDATE_TIME ib_upd_interval +#define IB_TIMEOUT_TIME ib_tout_interval +#else +#undef IB_UPDATE_TIME +#undef IB_TIMEOUT_TIME +#define IB_UPDATE_TIME Settings.ib_upd_interval +#define IB_TIMEOUT_TIME Settings.ib_tout_interval +#endif + +enum {HM17_TEST,HM17_ROLE,HM17_IMME,HM17_DISI,HM17_IBEA,HM17_SCAN,HM17_DISC,HM17_RESET,HM17_RENEW,HM17_CON}; +#define HM17_SUCESS 99 + +struct IBEACON { + char FACID[8]; + char UID[32]; + char MAJOR[4]; + char MINOR[4]; + char PWR[2]; + char MAC[12]; + char RSSI[4]; +}; + +#define MAX_IBEACONS 16 + +struct IBEACON_UID { + char MAC[12]; + char RSSI[4]; + uint8_t FLAGS; + uint8_t TIME; +} ibeacons[MAX_IBEACONS]; + + +void IBEACON_Init() { + + hm17_found=0; + + + if ((pin[GPIO_IBEACON_RX] < 99) && (pin[GPIO_IBEACON_TX] < 99)) { + IBEACON_Serial = new TasmotaSerial(pin[GPIO_IBEACON_RX], pin[GPIO_IBEACON_TX],1); + if (IBEACON_Serial->begin(HM17_BAUDRATE)) { + if (IBEACON_Serial->hardwareSerial()) { + ClaimSerial(); + } + hm17_sendcmd(HM17_TEST); + hm17_lastms=millis(); + + IB_UPDATE_TIME=IB_UPDATE_TIME_INTERVAL; + IB_TIMEOUT_TIME=IB_TIMEOUT_INTERVAL; + } + } +} + +void hm17_every_second(void) { + if (!IBEACON_Serial) return; + + if (hm17_found) { + if (IB_UPDATE_TIME && (uptime%IB_UPDATE_TIME==0)) { + if (hm17_cmd!=99) { + if (hm17_flag&2) { + ib_sendbeep(); + } else { + if (!hm17_connecting) { + hm17_sendcmd(HM17_DISI); + } + } + } + } + for (uint32_t cnt=0;cntIB_TIMEOUT_TIME) { + ibeacons[cnt].FLAGS=0; + ibeacon_mqtt(ibeacons[cnt].MAC,"0000"); + } + } + } + } else { + if (uptime%20==0) { + hm17_sendcmd(HM17_TEST); + } + } +} + +void hm17_sbclr(void) { + memset(hm17_sbuffer,0,HM17_BSIZ); + hm17_sindex=0; + IBEACON_Serial->flush(); +} + +void hm17_sendcmd(uint8_t cmd) { + hm17_sbclr(); + hm17_cmd=cmd; +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("hm17cmd %d"),cmd); +#endif + switch (cmd) { + case HM17_TEST: + IBEACON_Serial->write("AT"); + break; + case HM17_ROLE: + IBEACON_Serial->write("AT+ROLE1"); + break; + case HM17_IMME: + IBEACON_Serial->write("AT+IMME1"); + break; + case HM17_DISI: + IBEACON_Serial->write("AT+DISI?"); + hm17_scanning=1; + break; + case HM17_IBEA: + IBEACON_Serial->write("AT+IBEA1"); + break; + case HM17_RESET: + IBEACON_Serial->write("AT+RESET"); + break; + case HM17_RENEW: + IBEACON_Serial->write("AT+RENEW"); + break; + case HM17_SCAN: + IBEACON_Serial->write("AT+SCAN5"); + break; + case HM17_DISC: + IBEACON_Serial->write("AT+DISC?"); + hm17_scanning=1; + break; + case HM17_CON: + IBEACON_Serial->write((const uint8_t*)"AT+CON",6); + IBEACON_Serial->write((const uint8_t*)ib_mac,12); + hm17_connecting=1; + break; + } +} + +uint32_t ibeacon_add(struct IBEACON *ib) { + + if (!strncmp(ib->MAC,"FFFF",4) || strncmp(ib->FACID,"00000000",8)) { + for (uint32_t cnt=0;cntMAC,12)) { + + memcpy(ibeacons[cnt].RSSI,ib->RSSI,4); + ibeacons[cnt].TIME=0; + return 1; + } + } + } + for (uint32_t cnt=0;cntMAC,12); + memcpy(ibeacons[cnt].RSSI,ib->RSSI,4); + ibeacons[cnt].FLAGS=1; + ibeacons[cnt].TIME=0; + return 1; + } + } + } + return 0; +} + +void hm17_decode(void) { + struct IBEACON ib; + switch (hm17_cmd) { + case HM17_TEST: + if (!strncmp(hm17_sbuffer,"OK",2)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("AT OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + hm17_found=1; + } + break; + case HM17_ROLE: + if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("ROLE OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_IMME: + if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("IMME OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_IBEA: + if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("IBEA OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_SCAN: + if (!strncmp(hm17_sbuffer,"OK+Set:5",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("SCAN OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_RESET: + if (!strncmp(hm17_sbuffer,"OK+RESET",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("RESET OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_RENEW: + if (!strncmp(hm17_sbuffer,"OK+RENEW",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("RENEW OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_CON: + if (!strncmp(hm17_sbuffer,"OK+CONNA",8)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONNA OK")); +#endif + hm17_connecting=2; + break; + } + if (!strncmp(hm17_sbuffer,"OK+CONNE",8)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONNE ERROR")); +#endif + break; + } + if (!strncmp(hm17_sbuffer,"OK+CONNF",8)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONNF ERROR")); +#endif + break; + } + if (hm17_connecting==2 && !strncmp(hm17_sbuffer,"OK+CONN",7)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONN OK")); +#endif + hm17_connecting=3; + hm17_sendcmd(HM17_TEST); + hm17_connecting=0; + break; + } + break; + + case HM17_DISI: + case HM17_DISC: + if (!strncmp(hm17_sbuffer,"OK+DISCS",8)) { + hm17_sbclr(); + hm17_result=1; +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("DISCS OK")); +#endif + break; + } + if (!strncmp(hm17_sbuffer,"OK+DISIS",8)) { + hm17_sbclr(); + hm17_result=1; +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("DISIS OK")); +#endif + break; + } + if (!strncmp(hm17_sbuffer,"OK+DISCE",8)) { + hm17_sbclr(); + hm17_result=HM17_SUCESS; +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("DISCE OK")); +#endif + hm17_scanning=0; + break; + } + if (!strncmp(hm17_sbuffer,"OK+NAME:",8)) { + if (hm17_sbuffer[hm17_sindex-1]=='\n') { + hm17_result=HM17_SUCESS; +#ifdef IBEACON_DEBUG + if (hm17_debug) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("NAME OK")); + AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); + } +#endif + hm17_sbclr(); + } + break; + } + if (!strncmp(hm17_sbuffer,"OK+DIS0:",8)) { + if (hm17_cmd==HM17_DISI) { +#ifdef HM17_V110 + goto hm17_v110; +#endif + } else { + if (hm17_sindex==20) { + hm17_result=HM17_SUCESS; +#ifdef IBEACON_DEBUG + if (hm17_debug) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("DIS0 OK")); + AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); + } +#endif + hm17_sbclr(); + } + } + break; + } + if (!strncmp(hm17_sbuffer,"OK+DISC:",8)) { +hm17_v110: + if (hm17_cmd==HM17_DISI) { + if (hm17_sindex==78) { +#ifdef IBEACON_DEBUG + if (hm17_debug) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("DISC: OK")); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); + } +#endif + memcpy(ib.FACID,&hm17_sbuffer[8],8); + memcpy(ib.UID,&hm17_sbuffer[8+8+1],32); + memcpy(ib.MAJOR,&hm17_sbuffer[8+8+1+32+1],4); + memcpy(ib.MINOR,&hm17_sbuffer[8+8+1+32+1+4],4); + memcpy(ib.PWR,&hm17_sbuffer[8+8+1+32+1+4+4],2); + memcpy(ib.MAC,&hm17_sbuffer[8+8+1+32+1+4+4+2+1],12); + memcpy(ib.RSSI,&hm17_sbuffer[8+8+1+32+1+4+4+2+1+12+1],4); + + if (ibeacon_add(&ib)) { + ibeacon_mqtt(ib.MAC,ib.RSSI); + } + hm17_sbclr(); + hm17_result=1; + } + } else { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); +#endif + } + break; + } + } +} + +void IBEACON_loop() { + + if (!IBEACON_Serial) return; + +uint32_t difftime=millis()-hm17_lastms; + + while (IBEACON_Serial->available()) { + hm17_lastms=millis(); + + if (hm17_sindexread(); + hm17_sindex++; + hm17_decode(); + } else { + hm17_sindex=0; + break; + } + } + + if (hm17_cmd==99) { + if (hm17_sindex>=HM17_BSIZ-2 || (hm17_sindex && (difftime>100))) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),hm17_sbuffer); + hm17_sbclr(); + } + } + +} + +#ifdef USE_WEBSERVER +const char HTTP_IBEACON[] PROGMEM = + "{s}IBEACON-UID : %s" " - RSSI : %s" "{m}{e}"; + +void IBEACON_Show(void) { +char mac[14]; +char rssi[6]; + + for (uint32_t cnt=0;cnt 0) { + char *cp=XdrvMailbox.data; + if (*cp>='0' && *cp<='8') { + hm17_sendcmd(*cp&7); + Response_P(S_JSON_IBEACON, XSNS_52,"hm17cmd",*cp&7); + } else if (*cp=='s') { + cp++; + len--; + while (*cp==' ') { + len--; + cp++; + } + IBEACON_Serial->write((uint8_t*)cp,len); + hm17_cmd=99; + Response_P(S_JSON_IBEACON1, XSNS_52,"hm17cmd",cp); + } else if (*cp=='u') { + cp++; + if (*cp) IB_UPDATE_TIME=atoi(cp); + Response_P(S_JSON_IBEACON, XSNS_52,"uintv",IB_UPDATE_TIME); + } else if (*cp=='t') { + cp++; + if (*cp) IB_TIMEOUT_TIME=atoi(cp); + Response_P(S_JSON_IBEACON, XSNS_52,"lintv",IB_TIMEOUT_TIME); + } else if (*cp=='c') { + for (uint32_t cnt=0;cnt + + +#define SPECIAL_SS + + + + + + +#define DJ_TPWRIN "Total_in" +#define DJ_TPWROUT "Total_out" +#define DJ_TPWRCURR "Power_curr" +#define DJ_TPWRCURR1 "Power_p1" +#define DJ_TPWRCURR2 "Power_p2" +#define DJ_TPWRCURR3 "Power_p3" +#define DJ_CURR1 "Curr_p1" +#define DJ_CURR2 "Curr_p2" +#define DJ_CURR3 "Curr_p3" +#define DJ_VOLT1 "Volt_p1" +#define DJ_VOLT2 "Volt_p2" +#define DJ_VOLT3 "Volt_p3" +#define DJ_METERNR "Meter_number" +#define DJ_METERSID "Meter_id" +#define DJ_CSUM "Curr_summ" +#define DJ_VAVG "Volt_avg" +#define DJ_COUNTER "Count" + +struct METER_DESC { + uint8_t srcpin; + uint8_t type; + uint16_t flag; + int32_t params; + char prefix[8]; + int8_t trxpin; + uint8_t tsecs; + char *txmem; + uint8_t index; + uint8_t max_index; +}; + + + + + + +#define EHZ161_0 1 +#define EHZ161_1 2 +#define EHZ363 3 +#define EHZH 4 +#define EDL300 5 +#define Q3B 6 +#define COMBO3 7 +#define COMBO2 8 +#define COMBO3a 9 +#define Q3B_V1 10 +#define EHZ363_2 11 +#define COMBO3b 12 +#define WGS_COMBO 13 +#define EBZD_G 14 + + +#define METER EHZ161_1 + + +#if METER==EHZ161_0 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [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|" +"1,1-0:21.7.0*255(@1," D_TPWRCURR1 ",W," DJ_TPWRCURR1 ",0|" +"1,1-0:41.7.0*255(@1," D_TPWRCURR2 ",W," DJ_TPWRCURR2 ",0|" +"1,1-0:61.7.0*255(@1," D_TPWRCURR3 ",W," DJ_TPWRCURR3 ",0|" +"1,=m 3+4+5 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; + +#endif + + + +#if METER==EHZ161_1 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [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|" +"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; +#endif + + + +#if METER==EHZ363 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; + +const uint8_t meter[]= + +"1,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" + +"1,77070100020800ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" + +"1,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" + +"1,77070100000009ff@#," D_METERNR ",," DJ_METERNR ",0"; +#endif + + + +#if METER==EHZH +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; + + +const uint8_t meter[]= + +"1,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" + +"1,77070100020800ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" + +"1,770701000f0700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0"; +#endif + + + +#if METER==EDL300 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; + + +const uint8_t meter[]= + +"1,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" + +"1,77070100020801ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" + +"1,770701000f0700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0"; +#endif + +#if METER==EBZD_G +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"strom",-1,1,0}}; +const uint8_t meter[]= + +"1,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" + +"1,77070100020800ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" + +"1,77070100010801ff@1000," D_TPWRCURR1 ",kWh," DJ_TPWRCURR1 ",4|" + +"1,77070100010802ff@1000," D_TPWRCURR2 ",kWh," DJ_TPWRCURR2 ",4|" + +"1,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" + +"1,77070100600100ff@#," D_METERNR ",," DJ_METERNR ",0"; +#endif + + + + +#if METER==Q3B +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; +const uint8_t meter[]= + +"1,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" + +"1,77070100020801ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" + +"1,77070100010700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0"; +#endif + +#if METER==COMBO3 + +#undef METERS_USED +#define METERS_USED 3 + +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}, + [1]={14,'s',0,SML_BAUDRATE,"SML",-1,1,0}, + [2]={4,'o',0,SML_BAUDRATE,"OBIS2",-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|" +"1,1-0:21.7.0*255(@1," D_TPWRCURR1 ",W," DJ_TPWRCURR1 ",0|" +"1,1-0:41.7.0*255(@1," D_TPWRCURR2 ",W," DJ_TPWRCURR2 ",0|" +"1,1-0:61.7.0*255(@1," D_TPWRCURR3 ",W," DJ_TPWRCURR3 ",0|" +"1,=m 3+4+5 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" +"2,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" +"2,77070100020800ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" +"2,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"3,1-0:1.8.1*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" +"3,1-0:2.8.1*255(@1," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" +"3,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"3,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; + +#endif + +#if METER==COMBO2 + +#undef METERS_USED +#define METERS_USED 2 + +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS1",-1,1,0}, + [1]={14,'o',0,SML_BAUDRATE,"OBIS2",-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|" +"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" + +"2,1-0:1.8.1*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" +"2,1-0:2.8.1*255(@1," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" +"2,=d 6 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"2,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; + +#endif + +#if METER==COMBO3a +#undef METERS_USED +#define METERS_USED 3 + +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS1",-1,1,0}, + [1]={14,'o',0,SML_BAUDRATE,"OBIS2",-1,1,0}, + [2]={1,'o',0,SML_BAUDRATE,"OBIS3",-1,1,0}}; + + +const uint8_t meter[]= +"1,=h --- Zähler Nr 1 ---|" +"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|" +"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" +"2,=h --- Zähler Nr 2 ---|" +"2,1-0:1.8.1*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" +"2,1-0:2.8.1*255(@1," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" +"2,=d 6 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"2,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" +"3,=h --- Zähler Nr 3 ---|" +"3,1-0:1.8.1*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" +"3,1-0:2.8.1*255(@1," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" +"3,=d 10 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"3,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; + +#endif + + + +#if METER==Q3B_V1 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ +[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|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; +#endif + + + +#if METER==EHZ363_2 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ +[0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; + +const uint8_t meter[]= + +"1,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" + +"1,77070100020800ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" + +"1,77070100010801ff@1000," D_TPWRCURR1 ",kWh," DJ_TPWRCURR1 ",4|" + +"1,77070100010802ff@1000," D_TPWRCURR2 ",kWh," DJ_TPWRCURR2 ",4|" + +"1,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" + +"1,77070100000009ff@#," D_METERNR ",," DJ_METERNR ",0"; +#endif + + +#if METER==COMBO3b +#undef METERS_USED +#define METERS_USED 3 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}, + [1]={14,'c',0,50,"Gas"}, + [2]={1,'c',0,10,"Wasser"}}; + + +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|" +"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" + + +"2,1-0:1.8.0*255(@100," D_GasIN ",cbm," DJ_COUNTER ",2|" + +"3,1-0:1.8.0*255(@100," D_H2oIN ",cbm," DJ_COUNTER ",2"; +#endif + + +#if METER==WGS_COMBO +#undef METERS_USED +#define METERS_USED 3 + +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={1,'c',0,10,"H20",-1,1,0}, + [1]={4,'c',0,50,"GAS",-1,1,0}, + [2]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; + +const uint8_t meter[]= + + +"1,1-0:1.8.0*255(@10000," D_H2oIN ",cbm," DJ_COUNTER ",4|" + + +"2,=h==================|" +"2,1-0:1.8.0*255(@100," D_GasIN ",cbm," DJ_COUNTER ",3|" + +"3,=h==================|" + +"3,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",3|" +"3,=h==================|" + +"3,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",2|" +"3,=h -------------------------------|" +"3,=m 10+11+12 @100," D_StL1L2L3 ",A," DJ_CSUM ",2|" + +"3,=m 13+14+15/#3 @100," D_SpL1L2L3 ",V," DJ_VAVG ",2|" +"3,=h==================|" + +"3,77070100240700ff@1," D_TPWRCURR1 ",W," DJ_TPWRCURR1 ",2|" + +"3,77070100380700ff@1," D_TPWRCURR2 ",W," DJ_TPWRCURR2 ",2|" + +"3,770701004c0700ff@1," D_TPWRCURR3 ",W," DJ_TPWRCURR3 ",2|" +"3,=h -------------------------------|" + +"3,770701001f0700ff@100," D_Strom_L1 ",A," DJ_CURR1 ",2|" + +"3,77070100330700ff@100," D_Strom_L2 ",A," DJ_CURR2 ",2|" + +"3,77070100470700ff@100," D_Strom_L3 ",A," DJ_CURR3 ",2|" +"3,=h -------------------------------|" + +"3,77070100200700ff@100," D_Spannung_L1 ",V," DJ_VOLT1 ",2|" + +"3,77070100340700ff@100," D_Spannung_L2 ",V," DJ_VOLT2 ",2|" + +"3,77070100480700ff@100," D_Spannung_L3 ",V," DJ_VOLT3 ",2|" +"3,=h==================|" + +"3,77070100000009ff@#," D_METERSID ",," DJ_METERSID ",0|" +"3,=h--------------------------------"; +#endif +# 435 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_53_sml.ino" +#define USE_SML_MEDIAN_FILTER + + +#ifndef SML_MAX_VARS +#define SML_MAX_VARS 20 +#endif + + +#define MAX_METERS 5 +double meter_vars[SML_MAX_VARS]; + +#define MAX_DVARS MAX_METERS*2 +double dvalues[MAX_DVARS]; +uint32_t dtimes[MAX_DVARS]; +uint8_t meters_used; + +struct METER_DESC const *meter_desc_p; +const uint8_t *meter_p; +uint8_t meter_spos[MAX_METERS]; + + +TasmotaSerial *meter_ss[MAX_METERS]; + + +#define SML_BSIZ 48 +uint8_t smltbuf[MAX_METERS][SML_BSIZ]; + + +#define METER_ID_SIZE 24 +char meter_id[MAX_METERS][METER_ID_SIZE]; + +#define EBUS_SYNC 0xaa +#define EBUS_ESC 0xa9 + +uint8_t sml_send_blocks; +uint8_t sml_100ms_cnt; +uint8_t sml_desc_cnt; + +#ifdef USE_SML_MEDIAN_FILTER + +#define MEDIAN_SIZE 5 +struct SML_MEDIAN_FILTER { +double buffer[MEDIAN_SIZE]; +int8_t index; +} sml_mf[SML_MAX_VARS]; + +#ifndef FLT_MAX +#define FLT_MAX 99999999 +#endif + +double sml_median_array(double *array,uint8_t len) { + uint8_t ind[len]; + uint8_t mind=0,index=0,flg; + double min=FLT_MAX; + + for (uint8_t hcnt=0; hcntbuffer[mf->index]=in; + mf->index++; + if (mf->index>=MEDIAN_SIZE) mf->index=0; + + return sml_median_array(mf->buffer,MEDIAN_SIZE); +# 539 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_53_sml.ino" +} +#endif + +#ifdef ANALOG_OPTO_SENSOR + +uint8_t ads1115_up; + + +#define SAMPLE_BIT (0x8000) + +#define ADS1115_COMP_QUEUE_SHIFT 0 +#define ADS1115_COMP_LATCH_SHIFT 2 +#define ADS1115_COMP_POLARITY_SHIFT 3 +#define ADS1115_COMP_MODE_SHIFT 4 +#define ADS1115_DATA_RATE_SHIFT 5 +#define ADS1115_MODE_SHIFT 8 +#define ADS1115_PGA_SHIFT 9 +#define ADS1115_MUX_SHIFT 12 + +enum ads1115_comp_queue { + ADS1115_COMP_QUEUE_AFTER_ONE = 0, + ADS1115_COMP_QUEUE_AFTER_TWO = 0x1 << ADS1115_COMP_QUEUE_SHIFT, + ADS1115_COMP_QUEUE_AFTER_FOUR = 0x2 << ADS1115_COMP_QUEUE_SHIFT, + ADS1115_COMP_QUEUE_DISABLE = 0x3 << ADS1115_COMP_QUEUE_SHIFT, + ADS1115_COMP_QUEUE_MASK = 0x3 << ADS1115_COMP_QUEUE_SHIFT, +}; + +enum ads1115_comp_latch { + ADS1115_COMP_LATCH_NO = 0, + ADS1115_COMP_LATCH_YES = 1 << ADS1115_COMP_LATCH_SHIFT, + ADS1115_COMP_LATCH_MASK = 1 << ADS1115_COMP_LATCH_SHIFT, +}; + +enum ads1115_comp_polarity { + ADS1115_COMP_POLARITY_ACTIVE_LOW = 0, + ADS1115_COMP_POLARITY_ACTIVE_HIGH = 1 << ADS1115_COMP_POLARITY_SHIFT, + ADS1115_COMP_POLARITY_MASK = 1 << ADS1115_COMP_POLARITY_SHIFT, +}; + +enum ads1115_comp_mode { + ADS1115_COMP_MODE_WINDOW = 0, + ADS1115_COMP_MODE_HYSTERESIS = 1 << ADS1115_COMP_MODE_SHIFT, + ADS1115_COMP_MODE_MASK = 1 << ADS1115_COMP_MODE_SHIFT, +}; + +enum ads1115_data_rate { + ADS1115_DATA_RATE_8_SPS = 0, + ADS1115_DATA_RATE_16_SPS = 0x1 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_32_SPS = 0x2 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_64_SPS = 0x3 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_128_SPS = 0x4 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_250_SPS = 0x5 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_475_SPS = 0x6 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_860_SPS = 0x7 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_MASK = 0x7 << ADS1115_DATA_RATE_SHIFT, +}; + +enum ads1115_mode { + ADS1115_MODE_CONTINUOUS = 0, + ADS1115_MODE_SINGLE_SHOT = 1 << ADS1115_MODE_SHIFT, + ADS1115_MODE_MASK = 1 << ADS1115_MODE_SHIFT, +}; + +enum ads1115_pga { + ADS1115_PGA_TWO_THIRDS = 0, + ADS1115_PGA_ONE = 0x1 << ADS1115_PGA_SHIFT, + ADS1115_PGA_TWO = 0x2 << ADS1115_PGA_SHIFT, + ADS1115_PGA_FOUR = 0x3 << ADS1115_PGA_SHIFT, + ADS1115_PGA_EIGHT = 0x4 << ADS1115_PGA_SHIFT, + ADS1115_PGA_SIXTEEN = 0x5 << ADS1115_PGA_SHIFT, + ADS1115_PGA_MASK = 0x7 << ADS1115_PGA_SHIFT, +}; + + +enum ads1115_mux { + ADS1115_MUX_DIFF_AIN0_AIN1 = 0, + ADS1115_MUX_DIFF_AIN0_AIN3 = 0x1 << ADS1115_MUX_SHIFT, + ADS1115_MUX_DIFF_AIN1_AIN3 = 0x2 << ADS1115_MUX_SHIFT, + ADS1115_MUX_DIFF_AIN2_AIN3 = 0x3 << ADS1115_MUX_SHIFT, + ADS1115_MUX_GND_AIN0 = 0x4 << ADS1115_MUX_SHIFT, + ADS1115_MUX_GND_AIN1 = 0x5 << ADS1115_MUX_SHIFT, + ADS1115_MUX_GND_AIN2 = 0x6 << ADS1115_MUX_SHIFT, + ADS1115_MUX_GND_AIN3 = 0x7 << ADS1115_MUX_SHIFT, + ADS1115_MUX_MASK = 0x7 << ADS1115_MUX_SHIFT, +}; + +class ADS1115 { +public: + ADS1115(uint8_t address = 0x48); + + void begin(); + uint8_t trigger_sample(); + uint8_t reset(); + bool is_sample_in_progress(); + int16_t read_sample(); + float sample_to_float(int16_t val); + float read_sample_float(); + + void set_comp_queue(enum ads1115_comp_queue val) { set_config(val, ADS1115_COMP_QUEUE_MASK); } + void set_comp_latching(enum ads1115_comp_latch val) { set_config(val, ADS1115_COMP_LATCH_MASK); } + void set_comp_polarity(enum ads1115_comp_polarity val) { set_config(val, ADS1115_COMP_POLARITY_MASK); } + void set_comp_mode(enum ads1115_comp_mode val) { set_config(val, ADS1115_COMP_MODE_MASK); } + void set_data_rate(enum ads1115_data_rate val) { set_config(val, ADS1115_DATA_RATE_MASK); } + void set_mode(enum ads1115_mode val) { set_config(val, ADS1115_MODE_MASK); } + void set_pga(enum ads1115_pga val) { set_config(val, ADS1115_PGA_MASK); m_voltage_range = val >> ADS1115_PGA_SHIFT; } + void set_mux(enum ads1115_mux val) { set_config(val, ADS1115_MUX_MASK); } + +private: + void set_config(uint16_t val, uint16_t mask) { + m_config = (m_config & ~mask) | val; + } + + uint8_t write_register(uint8_t reg, uint16_t val); + uint16_t read_register(uint8_t reg); + + uint8_t m_address; + uint16_t m_config; + int m_voltage_range; +}; + + +enum ads1115_register { + ADS1115_REGISTER_CONVERSION = 0, + ADS1115_REGISTER_CONFIG = 1, + ADS1115_REGISTER_LOW_THRESH = 2, + ADS1115_REGISTER_HIGH_THRESH = 3, +}; + +#define FACTOR 32768.0 +static float ranges[] = { 6.144 / FACTOR, 4.096 / FACTOR, 2.048 / FACTOR, 1.024 / FACTOR, 0.512 / FACTOR, 0.256 / FACTOR}; + +ADS1115::ADS1115(uint8_t address) +{ + m_address = address; + m_config = ADS1115_COMP_QUEUE_AFTER_ONE | + ADS1115_COMP_LATCH_NO | + ADS1115_COMP_POLARITY_ACTIVE_LOW | + ADS1115_COMP_MODE_WINDOW | + ADS1115_DATA_RATE_128_SPS | + ADS1115_MODE_SINGLE_SHOT | + ADS1115_MUX_GND_AIN0; + set_pga(ADS1115_PGA_ONE); +} + +uint8_t ADS1115::write_register(uint8_t reg, uint16_t val) +{ + Wire.beginTransmission(m_address); + Wire.write(reg); + Wire.write(val>>8); + Wire.write(val & 0xFF); + return Wire.endTransmission(); +} + +uint16_t ADS1115::read_register(uint8_t reg) +{ + Wire.beginTransmission(m_address); + Wire.write(reg); + Wire.endTransmission(); + + uint8_t result = Wire.requestFrom((int)m_address, 2, 1); + if (result != 2) { + return 0; + } + + uint16_t val; + + val = Wire.read() << 8; + val |= Wire.read(); + return val; +} + +void ADS1115::begin() +{ + Wire.begin(); +} + +uint8_t ADS1115::trigger_sample() +{ + return write_register(ADS1115_REGISTER_CONFIG, m_config | SAMPLE_BIT); +} + +uint8_t ADS1115::reset() +{ + Wire.beginTransmission(0); + Wire.write(0x6); + return Wire.endTransmission(); +} + +bool ADS1115::is_sample_in_progress() +{ + uint16_t val = read_register(ADS1115_REGISTER_CONFIG); + return (val & SAMPLE_BIT) == 0; +} + +int16_t ADS1115::read_sample() +{ + return read_register(ADS1115_REGISTER_CONVERSION); +} + +float ADS1115::sample_to_float(int16_t val) +{ + return val * ranges[m_voltage_range]; +} + +float ADS1115::read_sample_float() +{ + return sample_to_float(read_sample()); +} + +ADS1115 adc; + +void ADS1115_init(void) { + + ads1115_up=0; + if (!i2c_flg) return; + + adc.begin(); + adc.set_data_rate(ADS1115_DATA_RATE_128_SPS); + adc.set_mode(ADS1115_MODE_CONTINUOUS); + adc.set_mux(ADS1115_MUX_DIFF_AIN0_AIN3); + adc.set_pga(ADS1115_PGA_TWO); + + int16_t val = adc.read_sample(); + ads1115_up=1; +} + +#endif + +char sml_start; +uint8_t dump2log=0; + +#define SML_SAVAILABLE Serial_available() +#define SML_SREAD Serial_read() +#define SML_SPEAK Serial_peek() + +bool Serial_available() { + uint8_t num=dump2log&7; + 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) num=1; + return meter_ss[num-1]->read(); +} + +uint8_t Serial_peek() { + uint8_t num=dump2log&7; + if (num<1 || num>meters_used) num=1; + return meter_ss[num-1]->peek(); +} + +uint8_t sml_logindex; + +void Dump2log(void) { + +int16_t index=0,hcnt=0; +uint32_t d_lastms; +uint8_t dchars[16]; + + + + if (dump2log&8) { + + while (SML_SAVAILABLE) { + log_data[index]=':'; + index++; + log_data[index]=' '; + index++; + d_lastms=millis(); + while ((millis()-d_lastms)<40) { + if (SML_SAVAILABLE) { + uint8_t c=SML_SREAD; + sprintf(&log_data[index],"%02x ",c); + dchars[hcnt]=c; + index+=3; + hcnt++; + if (hcnt>15) { + + log_data[index]='='; + index++; + log_data[index]='>'; + index++; + log_data[index]=' '; + index++; + for (uint8_t ccnt=0; ccnt<16; ccnt++) { + if (isprint(dchars[ccnt])) { + log_data[index]=dchars[ccnt]; + } else { + log_data[index]=' '; + } + index++; + } + break; + } + } + } + if (index>0) { + log_data[index]=0; + AddLog(LOG_LEVEL_INFO); + index=0; + hcnt=0; + } + } + } else { + if (meter_desc_p[(dump2log&7)-1].type=='o') { + + while (SML_SAVAILABLE) { + char c=SML_SREAD&0x7f; + if (c=='\n' || c=='\r') { + log_data[sml_logindex]=0; + AddLog(LOG_LEVEL_INFO); + sml_logindex=2; + log_data[0]=':'; + log_data[1]=' '; + break; + } + log_data[sml_logindex]=c; + if (sml_logindex2) { + log_data[index]=0; + AddLog(LOG_LEVEL_INFO); + } + } + } +} + +#ifdef ED300L +uint8_t sml_status[MAX_METERS]; +uint8_t g_mindex; +#endif + + +uint8_t *skip_sml(uint8_t *cp,int16_t *res) { + uint8_t len,len1,type; + len=*cp&0xf; + type=*cp&0x70; + if (type==0x70) { + + + cp++; + while (len--) { + len1=*cp&0x0f; + cp+=len1; + } + *res=0; + } else { + + *res=(signed char)*(cp+1); + cp+=len; + } + return cp; +} + + + +double sml_getvalue(unsigned char *cp,uint8_t index) { +uint8_t len,unit,type; +int16_t scaler,result; +int64_t value; +double dval; + + + +#ifdef ED300L + unsigned char *cpx=cp-5; + + if (*cp==0x64 && *cpx==0 && *(cpx+1)==0x01 && *(cpx+2)==0x08 && *(cpx+3)==0) { + sml_status[g_mindex]=*(cp+3); + } +#endif + + cp=skip_sml(cp,&result); + + cp=skip_sml(cp,&result); + + cp=skip_sml(cp,&result); + + cp=skip_sml(cp,&result); + scaler=result; + + + type=*cp&0x70; + len=*cp&0x0f; + cp++; + if (type==0x50 || type==0x60) { + + uint64_t uvalue=0; + uint8_t nlen=len; + while (--nlen) { + uvalue<<=8; + uvalue|=*cp++; + } + if (type==0x50) { + + switch (len-1) { + case 1: + + value=(signed char)uvalue; + break; + case 2: + +#ifdef DWS74_BUG + if (scaler==-2) { + value=(uint32_t)uvalue; + } else { + value=(int16_t)uvalue; + } +#else + value=(int16_t)uvalue; +#endif + break; + case 3: + case 4: + + value=(int32_t)uvalue; + break; + case 5: + case 6: + case 7: + case 8: + + value=(int64_t)uvalue; + break; + } + } else { + + value=uvalue; + } + + } else { + if (!(type&0xf0)) { + + + + if (len==9) { + + cp++; + uint32_t s1,s2; + s1=*cp<<16|*(cp+1)<<8|*(cp+2); + cp+=4; + s2=*cp<<16|*(cp+1)<<8|*(cp+2); + sprintf(&meter_id[index][0],"%u-%u",s1,s2); + } else { + + char *str=&meter_id[index][0]; + for (type=0; type= 'A' && chr <= 'F') rVal = chr + 10 - 'A'; + } + return rVal; +} + +uint8_t sb_counter; + + +double CharToDouble(const char *str) +{ + + char strbuf[24]; + + strlcpy(strbuf, str, sizeof(strbuf)); + char *pt = strbuf; + while ((*pt != '\0') && isblank(*pt)) { pt++; } + + signed char sign = 1; + if (*pt == '-') { sign = -1; } + if (*pt == '-' || *pt=='+') { pt++; } + + double left = 0; + if (*pt != '.') { + left = atoi(pt); + while (isdigit(*pt)) { pt++; } + } + + double right = 0; + if (*pt == '.') { + pt++; + right = atoi(pt); + while (isdigit(*pt)) { + pt++; + right /= 10.0; + } + } + + double result = left + right; + if (sign < 0) { + return -result; + } + return result; +} + + + +void ebus_esc(uint8_t *ebus_buffer, unsigned char len) { + short count,count1; + for (count=0; countavailable()) { + meter_ss[meters]->read(); + } +} + + +void sml_shift_in(uint32_t meters,uint32_t shard) { + uint32_t count; + if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='M' && meter_desc_p[meters].type!='p') { + + for (count=0; countread(); + + if (meter_desc_p[meters].type=='o') { + smltbuf[meters][SML_BSIZ-1]=iob&0x7f; + } else if (meter_desc_p[meters].type=='s') { + 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' || meter_desc_p[meters].type=='M') { + smltbuf[meters][meter_spos[meters]] = iob; + meter_spos[meters]++; + if (meter_spos[meters]>=9) { + SML_Decode(meters); + sml_empty_receiver(meters); + meter_spos[meters]=0; + } + } else if (meter_desc_p[meters].type=='p') { + smltbuf[meters][meter_spos[meters]] = iob; + meter_spos[meters]++; + if (meter_spos[meters]>=7) { + SML_Decode(meters); + sml_empty_receiver(meters); + meter_spos[meters]=0; + } + } else { + if (iob==EBUS_SYNC) { + + + if (meter_spos[meters]>4+5) { + + uint8_t tlen=smltbuf[meters][4]+5; + + if (smltbuf[meters][tlen]=ebus_CalculateCRC(smltbuf[meters],tlen)) { + ebus_esc(smltbuf[meters],tlen); + SML_Decode(meters); + } else { + + + } + } + meter_spos[meters]=0; + return; + } + smltbuf[meters][meter_spos[meters]] = iob; + meter_spos[meters]++; + if (meter_spos[meters]>=SML_BSIZ) { + meter_spos[meters]=0; + } + } + sb_counter++; + if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='M' && meter_desc_p[meters].type!='p') SML_Decode(meters); +} + + + +void SML_Poll(void) { +uint32_t meters; + + for (meters=0; metersavailable()) { + sml_shift_in(meters,0); + } + } + } +} + + +void SML_Decode(uint8_t index) { + const char *mp=(const char*)meter_p; + int8_t mindex; + uint8_t *cp; + uint8_t dindex=0,vindex=0; + delay(0); + while (mp != NULL) { + + + + mindex=((*mp)&7)-1; + + if (mindex<0 || mindex>=meters_used) mindex=0; + mp+=2; + if (*mp=='=' && *(mp+1)=='h') { + mp = strchr(mp, '|'); + if (mp) mp++; + continue; + } + + if (index!=mindex) goto nextsect; + + + cp=&smltbuf[mindex][0]; + + + if (*mp=='=') { + + mp++; + + if (*mp=='m' && !sb_counter) { + + + mp++; + while (*mp==' ') mp++; + + double dvar; + uint8_t opr; + uint32_t ind; + ind=atoi(mp); + while (*mp>='0' && *mp<='9') mp++; + if (ind<1 || ind>SML_MAX_VARS) ind=1; + dvar=meter_vars[ind-1]; + for (uint8_t p=0;p<5;p++) { + if (*mp=='@') { + + meter_vars[vindex]=dvar; + mp++; + SML_Immediate_MQTT((const char*)mp,vindex,mindex); + break; + } + opr=*mp; + mp++; + uint8_t iflg=0; + if (*mp=='#') { + iflg=1; + mp++; + } + ind=atoi(mp); + while (*mp>='0' && *mp<='9') mp++; + if (ind<1 || ind>SML_MAX_VARS) ind=1; + switch (opr) { + case '+': + if (iflg) dvar+=ind; + else dvar+=meter_vars[ind-1]; + break; + case '-': + if (iflg) dvar-=ind; + else dvar-=meter_vars[ind-1]; + break; + case '*': + if (iflg) dvar*=ind; + else dvar*=meter_vars[ind-1]; + break; + case '/': + if (iflg) dvar/=ind; + else dvar/=meter_vars[ind-1]; + break; + } + while (*mp==' ') mp++; + if (*mp=='@') { + + meter_vars[vindex]=dvar; + mp++; + SML_Immediate_MQTT((const char*)mp,vindex,mindex); + break; + } + } + } else if (*mp=='d') { + + if (dindex='0' && *mp<='9') mp++; + if (ind<1 || ind>SML_MAX_VARS) ind=1; + uint32_t delay=atoi(mp)*1000; + uint32_t dtime=millis()-dtimes[dindex]; + if (dtime>delay) { + + dtimes[dindex]=millis(); + double vdiff = meter_vars[ind-1]-dvalues[dindex]; + dvalues[dindex]=meter_vars[ind-1]; + meter_vars[vindex]=(double)360000.0*vdiff/((double)dtime/10000.0); + + mp=strchr(mp,'@'); + if (mp) { + mp++; + SML_Immediate_MQTT((const char*)mp,vindex,mindex); + } + } + dindex++; + } + } else if (*mp=='h') { + + mp = strchr(mp, '|'); + if (mp) mp++; + continue; + } + } else { + + uint8_t found=1; + 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++) { + found=0; + } + } else { + if (meter_desc_p[mindex].type=='s') { + + uint8_t val = hexnibble(*mp++) << 4; + val |= hexnibble(*mp++); + if (val!=*cp++) { + found=0; + } + } else { + + + if (*mp=='x' && *(mp+1)=='x') { + + mp+=2; + cp++; + } else if (!strncmp(mp,"UUuuUUuu",8)) { + uint32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); + ebus_dval=val; + mbus_dval=val; + mp+=8; + cp+=4; + } else if (*mp=='U' && *(mp+1)=='U' && *(mp+2)=='u' && *(mp+3)=='u'){ + uint16_t val = cp[1]|(cp[0]<<8); + mbus_dval=val; + ebus_dval=val; + mp+=4; + cp+=2; + } else if (!strncmp(mp,"SSssSSss",8)) { + int32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); + ebus_dval=val; + mbus_dval=val; + mp+=8; + cp+=4; + } else if (*mp=='u' && *(mp+1)=='u' && *(mp+2)=='U' && *(mp+3)=='U'){ + uint16_t val = cp[0]|(cp[1]<<8); + mbus_dval=val; + ebus_dval=val; + mp+=4; + cp+=2; + } else if (*mp=='u' && *(mp+1)=='u') { + uint8_t val = *cp++; + mbus_dval=val; + ebus_dval=val; + mp+=2; + } else if (*mp=='s' && *(mp+1)=='s' && *(mp+2)=='S' && *(mp+3)=='S') { + int16_t val = *cp|(*(cp+1)<<8); + mbus_dval=val; + ebus_dval=val; + mp+=4; + cp+=2; + } else if (*mp=='S' && *(mp+1)=='S' && *(mp+2)=='s' && *(mp+3)=='s') { + int16_t val = cp[1]|(cp[0]<<8); + mbus_dval=val; + ebus_dval=val; + mp+=4; + cp+=2; + } + else if (*mp=='s' && *(mp+1)=='s') { + int8_t val = *cp++; + mbus_dval=val; + ebus_dval=val; + mp+=2; + } + else if (!strncmp(mp,"ffffffff",8)) { + uint32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); + float *fp=(float*)&val; + ebus_dval=*fp; + mbus_dval=*fp; + mp+=8; + cp+=4; + } + else if (!strncmp(mp,"FFffFFff",8)) { + + uint32_t val= (cp[1]<<0)|(cp[0]<<8)|(cp[3]<<16)|(cp[2]<<24); + float *fp=(float*)&val; + ebus_dval=*fp; + mbus_dval=*fp; + mp+=8; + cp+=4; + } + else if (!strncmp(mp,"eeeeee",6)) { + uint32_t val=(cp[0]<<16)|(cp[1]<<8)|(cp[2]<<0); + mbus_dval=val; + mp+=6; + cp+=3; + } + else if (!strncmp(mp,"vvvvvv",6)) { + mbus_dval=(float)((cp[0]<<8)|(cp[1])) + ((float)cp[2]/10.0); + mp+=6; + cp+=3; + } + else if (!strncmp(mp,"cccccc",6)) { + mbus_dval=(float)((cp[0]<<8)|(cp[1])) + ((float)cp[2]/100.0); + mp+=6; + cp+=3; + } + else if (!strncmp(mp,"pppp",4)) { + mbus_dval=(float)((cp[0]<<8)|cp[1]); + mp+=4; + cp+=2; + } + else { + uint8_t val = hexnibble(*mp++) << 4; + val |= hexnibble(*mp++); + if (val!=*cp++) { + found=0; + } + } + } + } + } + if (found) { + + mp++; +#ifdef ED300L + g_mindex=mindex; +#endif + if (*mp=='#') { + + mp++; + if (meter_desc_p[mindex].type=='o') { + for (uint8_t p=0;p>=shift; + ebus_dval&=1; + mp+=2; + } + if (*mp=='i') { + + 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; + + mp++; + } else { + if (meter_desc_p[mindex].type=='p') { + uint8_t crc = SML_PzemCrc(&smltbuf[mindex][0],6); + if (crc!=smltbuf[mindex][6]) goto nextsect; + dval=mbus_dval; + } else { + dval=ebus_dval; + } + } + + } +#ifdef USE_SML_MEDIAN_FILTER + if (meter_desc_p[mindex].flag&16) { + meter_vars[vindex]=sml_median(&sml_mf[vindex],dval); + } else { + meter_vars[vindex]=dval; + } +#else + meter_vars[vindex]=dval; +#endif + + + double fac=CharToDouble((char*)mp); + meter_vars[vindex]/=fac; + SML_Immediate_MQTT((const char*)mp,vindex,mindex); + } + } + } +nextsect: + + if (vindex=meters_used) lastmind=0; + while (mp != NULL) { + + mindex=((*mp)&7)-1; + if (mindex<0 || mindex>=meters_used) mindex=0; + mp+=2; + if (*mp=='=' && *(mp+1)=='h') { + mp+=2; + + if (json) { + mp = strchr(mp, '|'); + if (mp) mp++; + continue; + } + + uint8_t i; + for (i=0;isml_counters[index].sml_debounce) { + RtcSettings.pulse_counter[index]++; + InjektCounterValue(sml_counters[index].sml_cnt_old_state,RtcSettings.pulse_counter[index]); + } + } else { + + sml_counters[index].sml_counter_ltime=millis(); + } +} + +void SML_CounterUpd1(void) { + SML_CounterUpd(0); +} + +void SML_CounterUpd2(void) { + SML_CounterUpd(1); +} + +void SML_CounterUpd3(void) { + SML_CounterUpd(2); +} + +void SML_CounterUpd4(void) { + SML_CounterUpd(3); +} + +#ifdef USE_SCRIPT +struct METER_DESC script_meter_desc[MAX_METERS]; +uint8_t *script_meter; +#endif + +#ifndef METER_DEF_SIZE +#define METER_DEF_SIZE 3000 +#endif + +bool Gpio_used(uint8_t gpiopin) { + for (uint16_t i=0;iM",-2,0); + if (meter_script==99) { + + if (script_meter) free(script_meter); + script_meter=0; + uint8_t *tp=0; + uint16_t index=0; + uint8_t section=0; + uint8_t srcpin=0; + char *lp=glob_script_mem.scriptptr; + sml_send_blocks=0; + while (lp) { + if (!section) { + if (*lp=='>' && *(lp+1)=='M') { + lp+=2; + meters_used=strtol(lp,0,10); + section=1; + uint32_t mlen=0; + for (uint32_t cnt=0;cnt') { + if (*(tp-1)=='|') *(tp-1)=0; + break; + } + if (*lp=='+') { + + + lp++; + index=*lp&7; + lp+=2; + if (index<1 || index>meters_used) goto next_line; + index--; + srcpin=strtol(lp,&lp,10); + if (Gpio_used(srcpin)) { + AddLog_P(LOG_LEVEL_INFO, PSTR("gpio rx double define!")); +dddef_exit: + if (script_meter) free(script_meter); + script_meter=0; + meters_used=METERS_USED; + goto init10; + } + script_meter_desc[index].srcpin=srcpin; + if (*lp!=',') goto next_line; + lp++; + script_meter_desc[index].type=*lp; + lp+=2; + script_meter_desc[index].flag=strtol(lp,&lp,10); + if (*lp!=',') goto next_line; + lp++; + script_meter_desc[index].params=strtol(lp,&lp,10); + if (*lp!=',') goto next_line; + lp++; + script_meter_desc[index].prefix[7]=0; + for (uint32_t cnt=0; cnt<8; cnt++) { + if (*lp==SCRIPT_EOL || *lp==',') { + script_meter_desc[index].prefix[cnt]=0; + break; + } + script_meter_desc[index].prefix[cnt]=*lp++; + } + if (*lp==',') { + lp++; + script_meter_desc[index].trxpin=strtol(lp,&lp,10); + if (Gpio_used(script_meter_desc[index].trxpin)) { + AddLog_P(LOG_LEVEL_INFO, PSTR("gpio tx double define!")); + goto dddef_exit; + } + 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; cntmeters_used) goto next_line; + while (1) { + if (*lp==SCRIPT_EOL) { + if (*(tp-1)!='|') *tp++='|'; + goto next_line; + } + *tp++=*lp++; + index++; + if (index>=METER_DEF_SIZE) break; + } + } + + } + +next_line: + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + } + *tp=0; + meter_desc_p=script_meter_desc; + meter_p=script_meter; + } +#endif + +init10: + typedef void (*function)(); + function counter_callbacks[] = {SML_CounterUpd1,SML_CounterUpd2,SML_CounterUpd3,SML_CounterUpd4}; + uint8_t cindex=0; + + for (byte i = 0; i < MAX_COUNTERS; i++) { + RtcSettings.pulse_counter[i]=Settings.pulse_counter[i]; + sml_counters[i].sml_cnt_last_ts=millis(); + } + for (uint8_t meters=0; metersbegin(meter_desc_p[meters].params)) { + meter_ss[meters]->flush(); + } + if (meter_ss[meters]->hardwareSerial()) { + if (meter_desc_p[meters].type=='M') { + Serial.begin(meter_desc_p[meters].params, SERIAL_8E1); + } + ClaimSerial(); + } + + } + } + +} + + +#ifdef USE_SML_SCRIPT_CMD +uint32_t SML_SetBaud(uint32_t meter, uint32_t br) { + if (meter<1 || meter>meters_used) return 0; + meter--; + if (!meter_ss[meter]) return 0; + if (meter_ss[meter]->begin(br)) { + meter_ss[meter]->flush(); + } + if (meter_ss[meter]->hardwareSerial()) { + if (meter_desc_p[meter].type=='M') { + Serial.begin(br, SERIAL_8E1); + } + } + return 1; +} + +uint32_t SML_Status(uint32_t meter) { + if (meter<1 || meter>meters_used) return 0; + meter--; +#ifdef ED300L + return sml_status[meter]; +#else + return 0; +#endif +} + + +uint32_t SML_Write(uint32_t meter,char *hstr) { + if (meter<1 || meter>meters_used) return 0; + meter--; + if (!meter_ss[meter]) return 0; + SML_Send_Seq(meter,hstr); + return 1; +} +#endif + + +void SetDBGLed(uint8_t srcpin, uint8_t ledpin) { + pinMode(ledpin, OUTPUT); + if (digitalRead(srcpin)) { + digitalWrite(ledpin,LOW); + } else { + digitalWrite(ledpin,HIGH); + } +} + + +void SML_Counter_Poll(void) { +uint16_t meters,cindex=0; +uint32_t ctime=millis(); + + for (meters=0; meters0) { + if (ctime-sml_counters[cindex].sml_cnt_last_ts>meter_desc_p[meters].params) { + sml_counters[cindex].sml_cnt_last_ts=ctime; + + if (meter_desc_p[meters].flag&2) { + +#ifdef ANALOG_OPTO_SENSOR + if (ads1115_up) { + int16_t val = adc.read_sample(); + if (val>sml_counters[cindex].ana_max) sml_counters[cindex].ana_max=val; + if (val10) { + sml_counters[cindex].sml_cnt_last_ts=ctime; +#ifdef DEBUG_CNT_LED1 + if (cindex==0) SetDBGLed(meter_desc_p[meters].srcpin,DEBUG_CNT_LED1); +#endif +#ifdef DEBUG_CNT_LED2 + if (cindex==1) SetDBGLed(meter_desc_p[meters].srcpin,DEBUG_CNT_LED2); +#endif + } + } + cindex++; + } + } +} + +#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; + } + } + } +} + +void SML_Check_Send(void) { + sml_100ms_cnt++; + char *cp; + for (uint32_t cnt=sml_desc_cnt; cnt=0 && script_meter_desc[cnt].txmem) { + if ((sml_100ms_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; + sml_desc_cnt++; + } + cp=SML_Get_Sequence(script_meter_desc[cnt].txmem,script_meter_desc[cnt].index); + + } else { + cp=script_meter_desc[cnt].txmem; + + sml_desc_cnt++; + } + + SML_Send_Seq(cnt,cp); + if (sml_desc_cnt>=meters_used) { + sml_desc_cnt=0; + } + break; + } + } else { + sml_desc_cnt++; + } + + if (sml_desc_cnt>=meters_used) { + sml_desc_cnt=0; + } + } +} + +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; +} + + +void SML_Send_Seq(uint32_t meter,char *seq) { + uint8_t sbuff[32]; + uint8_t *ucp=sbuff,slen=0; + 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' || script_meter_desc[meter].type=='M') { + *ucp++=0; + *ucp++=2; + + uint16_t crc = MBUS_calculateCRC(sbuff,6); + *ucp++=lowByte(crc); + *ucp++=highByte(crc); + slen+=4; + } + if (script_meter_desc[meter].type=='o') { + for (uint32_t cnt=0;cntwrite(sbuff,slen); +} +#endif + +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) { + crc >>= 1; + crc ^= 0xA001; + } else { + crc >>= 1; + } + } + } + return crc; +} + +uint8_t SML_PzemCrc(uint8_t *data, uint8_t len) { + uint16_t crc = 0; + for (uint32_t i = 0; i < len; i++) crc += *data++; + return (uint8_t)(crc & 0xFF); +} + + +uint8_t CalcEvenParity(uint8_t data) { +uint8_t parity=0; + + while(data) { + parity^=(data &1); + data>>=1; + } + return parity; +} +# 2334 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_53_sml.ino" +bool XSNS_53_cmd(void) { + bool serviced = true; + if (XdrvMailbox.data_len > 0) { + char *cp=XdrvMailbox.data; + if (*cp=='d') { + + cp++; + uint8_t index=atoi(cp); + if ((index&7)>meters_used) index=1; + if (index>0 && meter_desc_p[(index&7)-1].type=='c') { + index=0; + } + dump2log=index; + ResponseTime_P(PSTR(",\"SML\":{\"CMD\":\"dump: %d\"}}"),dump2log); + } else if (*cp=='c') { + + cp++; + uint8_t index=*cp&7; + if (index<1 || index>MAX_COUNTERS) index=1; + cp++; + while (*cp==' ') cp++; + if (isdigit(*cp)) { + uint32_t cval=atoi(cp); + while (isdigit(*cp)) cp++; + RtcSettings.pulse_counter[index-1]=cval; + uint8_t cindex=0; + for (uint8_t meters=0; metersaddress, INA226_REG_CALIBRATION, si->calibrationValue); + +} + + + + + + +bool Ina226TestPresence(uint8_t device) +{ + + + + uint16_t config = I2cRead16( slaveInfo[device].address, INA226_REG_CONFIG ); + + + if (config != slaveInfo[device].config) + return false; + + return true; + +} + +void Ina226ResetActive(void) +{ + Ina226SlaveInfo_t *p = slaveInfo; + + for (uint32_t i = 0; i < INA226_MAX_ADDRESSES; i++) { + p = &slaveInfo[i]; + + uint8_t addr = p->address; + if (addr) { + I2cResetActive(addr); + } + } +} + + + + + +void Ina226Init() +{ + uint32_t i; + + slavesFound = 0; + + Ina226SlaveInfo_t *p = slaveInfo; +# 215 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_54_ina226.ino" + for (i = 0; i < 4; i++){ + *p = {0}; + } + + + + + + for (i = 0; i < INA226_MAX_ADDRESSES; i++){ + uint8_t addr = pgm_read_byte(probeAddresses + i); + + if (I2cActive(addr)) { continue; } + + + + + if (!Settings.ina226_i_fs[i]) + continue; + + + + + + + if (!I2cWrite16( addr, INA226_REG_CONFIG, INA226_CONFIG_RESET)){ + + AddLog_P2( LOG_LEVEL_DEBUG, "No INA226 at address: %02X", addr); + continue; + } + + + + uint16_t config = I2cRead16( addr, INA226_REG_CONFIG ); + + + if (INA226_RES_CONFIG != config) + continue; + + + config = INA226_DEF_CONFIG; + + + if (!I2cWrite16( addr, INA226_REG_CONFIG, config)) + continue; + + + p = &slaveInfo[i]; + + p->address = addr; + + p->config = config; + + + p->i_lsb = (((float) Settings.ina226_i_fs[i])/10.0f)/32768.0f; + + + + uint32_t r_shunt_uohms = _expand_r_shunt(Settings.ina226_r_shunt[i]); + + + + p->calibrationValue = ((uint16_t) (0.00512/(p->i_lsb * r_shunt_uohms/1000000.0f))); + + p->present = true; + + + Ina226SetCalibration(i); + + I2cSetActiveFound(addr, Ina226Str); + + slavesFound++; + } +} + + + + + +float Ina226ReadBus_v(uint8_t device) +{ + uint8_t addr = slaveInfo[device].address; + int16_t reg_bus_v = I2cReadS16( addr, INA226_REG_BUSVOLTAGE); + + float result = ((float) reg_bus_v) * 0.00125f; + + return result; + +} + + + + + +float Ina226ReadShunt_i(uint8_t device) +{ + uint8_t addr = slaveInfo[device].address; + int16_t reg_shunt_i = I2cReadS16( addr, INA226_REG_CURRENT); + + float result = ((float) reg_shunt_i) * slaveInfo[device].i_lsb; + + return result; +} + + + + + +float Ina226ReadPower_w(uint8_t device) +{ + uint8_t addr = slaveInfo[device].address; + int16_t reg_shunt_i = I2cReadS16( addr, INA226_REG_POWER); + + float result = ((float) reg_shunt_i) * (slaveInfo[device].i_lsb * 25.0); + + return result; +} + + + + + + +void Ina226Read(uint8_t device) +{ + + voltages[device] = Ina226ReadBus_v(device); + currents[device] = Ina226ReadShunt_i(device); + powers[device] = Ina226ReadPower_w(device); + + + + +} + + + + + +void Ina226EverySecond() +{ + + for (uint8_t device = 0; device < INA226_MAX_ADDRESSES; device++){ + + if (slavesFound && slaveInfo[device].present && Ina226TestPresence(device)){ + Ina226Read(device); + } + else { + powers[device] = currents[device] = voltages[device] = 0.0f; + + + + + + + slaveInfo[device].present = false; + } + } +} + + + + + +bool Ina226CommandSensor() +{ + bool serviced = true; + bool show_config = false; + char param_str[64]; + char *cp, *params[4]; + uint8_t i, param_count, device, p1 = XdrvMailbox.payload; + uint32_t r_shunt_uohms; + uint16_t compact_r_shunt_uohms; + + + + + + if (XdrvMailbox.data_len > 62){ + return false; + } + + strncpy(param_str, XdrvMailbox.data, XdrvMailbox.data_len + 1); + param_str[XdrvMailbox.data_len] = 0; + + + for (cp = param_str, i = 0, param_count = 0; *cp && (i < XdrvMailbox.data_len + 1) && (param_count <= 3); i++) + if (param_str[i] == ' ' || param_str[i] == ',' || param_str[i] == 0){ + param_str[i] = 0; + params[param_count] = cp; + + param_count++; + cp = param_str + i + 1; + } + + + if (p1 < 10 || p1 >= 50){ + + switch (p1){ + case 1: + Ina226ResetActive(); + Ina226Init(); + Response_P(PSTR("{\"Sensor54-Command-Result\":{\"SlavesFound\":%d}}"),slavesFound); + break; + + case 2: + restart_flag = 2; + Response_P(PSTR("{\"Sensor54-Command-Result\":{\"Restart_flag\":%d}}"),restart_flag); + break; + + default: + serviced = false; + } + } + else if (p1 < 50){ + + device = (p1 / 10) - 1; + switch (p1 % 10){ + case 0: + show_config = true; + break; + + case 1: + r_shunt_uohms = (uint32_t) ((CharToFloat(params[1])) * 1000000.0f); + + + + if (r_shunt_uohms > 32767){ + uint32_t r_shunt_mohms = r_shunt_uohms/1000UL; + Settings.ina226_r_shunt[device] = (uint16_t) (r_shunt_mohms | 0x8000); + } + else + Settings.ina226_r_shunt[device] = (uint16_t) r_shunt_uohms; + + + show_config = true; + break; + + case 2: + Settings.ina226_i_fs[device] = (uint16_t) ((CharToFloat(params[1])) * 10.0f); + + show_config = true; + break; + + + default: + serviced = false; + break; + } + } + else + serviced = false; + + if (show_config) { + char shunt_r_str[16]; + char fs_i_str[16]; + + + r_shunt_uohms = _expand_r_shunt(Settings.ina226_r_shunt[device]); + dtostrfd(((float)r_shunt_uohms)/1000000.0f, 6, shunt_r_str); + + dtostrfd(((float)Settings.ina226_i_fs[device])/10.0f, 1, fs_i_str); + + Response_P(PSTR("{\"Sensor54-device-settings-%d\":{\"SHUNT_R\":%s,\"FS_I\":%s}}"), + device + 1, shunt_r_str, fs_i_str); + } + + return serviced; +} + + + + + +#ifdef USE_WEBSERVER +const char HTTP_SNS_INA226_DATA[] PROGMEM = + "{s}%s " D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" + "{s}%s " D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" + "{s}%s " D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"; +#endif + +void Ina226Show(bool json) +{ + int i, num_found; + for (num_found = 0, i = 0; i < INA226_MAX_ADDRESSES; i++) { + + if (!slaveInfo[i].present) + continue; + + num_found++; + + char voltage[16]; + dtostrfd(voltages[i], Settings.flag2.voltage_resolution, voltage); + char current[16]; + dtostrfd(currents[i], Settings.flag2.current_resolution, current); + char power[16]; + dtostrfd(powers[i], Settings.flag2.wattage_resolution, power); + char name[16]; + snprintf_P(name, sizeof(name), PSTR("INA226%c%d"),IndexSeparator(), i + 1); + + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"Id\":%d,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s,\"" D_JSON_POWERUSAGE "\":%s}"), + name, i, voltage, current, power); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_VOLTAGE, voltage); + DomoticzSensor(DZ_CURRENT, current); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_INA226_DATA, name, voltage, name, current, name, power); +#endif + } + + } + +} +# 546 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_54_ina226.ino" +bool Xsns54(byte callback_id) +{ + if (!I2cEnabled(XI2C_35)) { return false; } + + + bool result = false; + + + switch (callback_id) { + case FUNC_EVERY_SECOND: + Ina226EverySecond(); + break; + case FUNC_JSON_APPEND: + Ina226Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Ina226Show(0); + break; +#endif + case FUNC_COMMAND_SENSOR: + if (XSNS_54 == XdrvMailbox.index) { + result = Ina226CommandSensor(); + } + break; + case FUNC_INIT: + Ina226Init(); + break; + } + + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_55_hih_series.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_55_hih_series.ino" +#ifdef USE_I2C +#ifdef USE_HIH6 +# 33 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_55_hih_series.ino" +#define XSNS_55 55 +#define XI2C_36 36 + +#define HIH6_ADDR 0x27 + +struct HIH6 { + float temperature = 0; + float humidity = 0; + uint8_t valid = 0; + uint8_t type = 0; + char types[4] = "HIH"; +} Hih6; + +bool Hih6Read(void) +{ + Wire.beginTransmission(HIH6_ADDR); + if (Wire.endTransmission() != 0) { return false; } + + delay(40); + + uint8_t data[4]; + Wire.requestFrom(HIH6_ADDR, 4); + if (4 == Wire.available()) { + data[0] = Wire.read(); + data[1] = Wire.read(); + data[2] = Wire.read(); + data[3] = Wire.read(); + } else { return false; } + + + + Hih6.humidity = ConvertHumidity(((float)(((data[0] & 0x3F) << 8) | data[1]) * 100.0) / 16383.0); + + int temp = ((data[2] << 8) | (data[3] & 0xFC)) / 4; + Hih6.temperature = ConvertTemp(((float)temp / 16384.0) * 165.0 - 40.0); + + Hih6.valid = SENSOR_MAX_MISS; + return true; +} + + + +void Hih6Detect(void) +{ + if (I2cActive(HIH6_ADDR)) { return; } + + if (uptime < 2) { delay(20); } + Hih6.type = Hih6Read(); + if (Hih6.type) { + I2cSetActiveFound(HIH6_ADDR, Hih6.types); + } +} + +void Hih6EverySecond(void) +{ + if (uptime &1) { + + if (!Hih6Read()) { + AddLogMissed(Hih6.types, Hih6.valid); + } + } +} + +void Hih6Show(bool json) +{ + if (Hih6.valid) { + TempHumDewShow(json, (0 == tele_period), Hih6.types, Hih6.temperature, Hih6.humidity); + } +} + + + + + +bool Xsns55(uint8_t function) +{ + if (!I2cEnabled(XI2C_36)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Hih6Detect(); + } + else if (Hih6.type) { + switch (function) { + case FUNC_EVERY_SECOND: + Hih6EverySecond(); + break; + case FUNC_JSON_APPEND: + Hih6Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Hih6Show(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_56_hpma.ino" +# 21 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_56_hpma.ino" +#ifdef USE_HPMA +# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_56_hpma.ino" +#define XSNS_56 56 + +#include +#include + +TasmotaSerial *HpmaSerial; +HPMA115S0 *hpma115S0; + +uint8_t hpma_type = 1; +uint8_t hpma_valid = 0; + +struct hpmadata { + unsigned int pm10; + unsigned int pm2_5; +} hpma_data; + + + +void HpmaSecond(void) +{ + unsigned int pm2_5, pm10; + + + + + if (hpma115S0->ReadParticleMeasurement(&pm2_5, &pm10)) { + hpma_data.pm2_5 = pm2_5; + hpma_data.pm10 = pm10; + hpma_valid = 1; + } + +} + +void HpmaInit(void) +{ + hpma_type = 0; + if (pin[GPIO_HPMA_RX] < 99 && pin[GPIO_HPMA_TX] < 99) { + HpmaSerial = new TasmotaSerial(pin[GPIO_HPMA_RX], pin[GPIO_HPMA_TX], 1); + hpma115S0 = new HPMA115S0(*HpmaSerial); + + if (HpmaSerial->begin(9600)) { + if (HpmaSerial->hardwareSerial()) { + ClaimSerial(); + } + hpma_type = 1; + hpma115S0->Init(); + hpma115S0->StartParticleMeasurement(); + } + } +} + +#ifdef USE_WEBSERVER +const char HTTP_HPMA_SNS[] PROGMEM = + "{s}HPMA " D_ENVIRONMENTAL_CONCENTRATION "2.5 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}HPMA " D_ENVIRONMENTAL_CONCENTRATION "10 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"; +#endif + +void HpmaShow(bool json) +{ + if (hpma_valid) { + char pm10[33]; + snprintf_P(pm10, 33, PSTR("%d"), hpma_data.pm10); + char pm2_5[33]; + snprintf_P(pm2_5, 33, PSTR("%d"), hpma_data.pm2_5); + + if (json) { + ResponseAppend_P(PSTR(",\"HPMA\":{\"PM2.5\":%d,\"PM10\":%d}"), hpma_data.pm2_5, hpma_data.pm10); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_VOLTAGE, pm2_5); + DomoticzSensor(DZ_CURRENT, pm10); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_HPMA_SNS, pm2_5, pm10); +#endif + } + } +} + + + + + +bool Xsns56(uint8_t function) +{ + bool result = false; + + if (hpma_type) { + switch (function) { + case FUNC_INIT: + HpmaInit(); + break; + case FUNC_EVERY_SECOND: + HpmaSecond(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_56 == XdrvMailbox.index) { + return true; + } + break; + case FUNC_JSON_APPEND: + HpmaShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + HpmaShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_57_tsl2591.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_57_tsl2591.ino" +#ifdef USE_I2C +#ifdef USE_TSL2591 + + + + + + + +#define XSNS_57 57 +#define XI2C_40 40 + +#define TSL2591_ADDRESS 0x29 + +#include + +Adafruit_TSL2591 tsl = Adafruit_TSL2591(); + +uint8_t tsl2591_type = 0; +uint8_t tsl2591_valid = 0; +float tsl2591_lux = 0; + +void Tsl2591Init(void) +{ + + if (I2cSetDevice(0x29)) { + if (tsl.begin()) { + tsl.setGain(TSL2591_GAIN_MED); + tsl.setTiming(TSL2591_INTEGRATIONTIME_300MS); + tsl2591_type = 1; + I2cSetActiveFound(TSL2591_ADDRESS, "TSL2591"); + } + } +} + +bool Tsl2591Read(void) +{ + uint32_t lum = tsl.getFullLuminosity(); + uint16_t ir, full; + ir = lum >> 16; + full = lum & 0xFFFF; + tsl2591_lux = tsl.calculateLux(full, ir); + tsl2591_valid = 1; +} + +void Tsl2591EverySecond(void) +{ + Tsl2591Read(); +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_TSL2591[] PROGMEM = + "{s}TSL2591 " D_ILLUMINANCE "{m}%s " D_UNIT_LUX "{e}"; +#endif + +void Tsl2591Show(bool json) +{ + if (tsl2591_valid) { + char lux_str[10]; + dtostrf(tsl2591_lux, sizeof(lux_str)-1, 3, lux_str); + if (json) { + ResponseAppend_P(PSTR(",\"TSL2591\":{\"" D_JSON_ILLUMINANCE "\":%s}"), lux_str); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, tsl2591_lux); } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TSL2591, lux_str); +#endif + } + } +} + + + + + +bool Xsns57(uint8_t function) +{ + if (!I2cEnabled(XI2C_40)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Tsl2591Init(); + } + else if (tsl2591_type) { + switch (function) { + case FUNC_EVERY_SECOND: + Tsl2591EverySecond(); + break; + case FUNC_JSON_APPEND: + Tsl2591Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Tsl2591Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_58_dht12.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_58_dht12.ino" +#ifdef USE_I2C +#ifdef USE_DHT12 + + + + + + +#define XSNS_58 58 +#define XI2C_41 41 + +#define DHT12_ADDR 0x5C + +struct DHT12 { + float temperature = NAN; + float humidity = NAN; + uint8_t valid = 0; + uint8_t count = 0; + char name[6] = "DHT12"; +} Dht12; + +bool Dht12Read(void) +{ + if (Dht12.valid) { Dht12.valid--; } + + Wire.beginTransmission(DHT12_ADDR); + Wire.write(0); + if (Wire.endTransmission() != 0) { return false; } + + delay(50); + + Wire.requestFrom(DHT12_ADDR, 5); + delay(5); + uint8_t humidity = Wire.read(); + uint8_t humidityTenth = Wire.read(); + uint8_t temp = Wire.read(); + uint8_t tempTenth = Wire.read(); + uint8_t checksum = Wire.read(); + + Dht12.humidity = ConvertHumidity( (float) humidity + (float) humidityTenth/(float) 10.0 ); + Dht12.temperature = ConvertTemp( (float) temp + (float) tempTenth/(float) 10.0 ); + + if (isnan(Dht12.temperature) || isnan(Dht12.humidity)) { return false; } + + Dht12.valid = SENSOR_MAX_MISS; + return true; +} + + + +void Dht12Detect(void) +{ + if (I2cActive(DHT12_ADDR)) { return; } + + if (Dht12Read()) { + I2cSetActiveFound(DHT12_ADDR, Dht12.name); + Dht12.count = 1; + } +} + +void Dht12EverySecond(void) +{ + if (uptime &1) { + + if (!Dht12Read()) { + AddLogMissed(Dht12.name, Dht12.valid); + } + } +} + +void Dht12Show(bool json) +{ + if (Dht12.valid) { + TempHumDewShow(json, (0 == tele_period), Dht12.name, Dht12.temperature, Dht12.humidity); + } +} + + + + + +bool Xsns58(uint8_t function) +{ + if (!I2cEnabled(XI2C_41)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Dht12Detect(); + } + else if (Dht12.count) { + switch (function) { + case FUNC_EVERY_SECOND: + Dht12EverySecond(); + break; + case FUNC_JSON_APPEND: + Dht12Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Dht12Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_59_ds1624.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_59_ds1624.ino" +#ifdef USE_I2C +#ifdef USE_DS1624 + + + + + + + +#define XSNS_59 59 +#define XI2C_42 42 + +#define DS1624_MEM_REGISTER 0x17 +#define DS1624_CONF_REGISTER 0xAC +#define DS1624_TEMP_REGISTER 0xAA +#define DS1624_START_REGISTER 0xEE +#define DS1624_STOP_REGISTER 0x22 + +#define DS1621_COUNTER_REGISTER 0xA8 +#define DS1621_SLOPE_REGISTER 0xA9 + +#define DS1621_CFG_1SHOT (1<<0) +#define DS1621_CFG_DONE (1<<7) + +enum { + DS1624_TYPE_DS1624, + DS1624_TYPE_DS1621 +}; + +#define DS1624_MAX_SENSORS 8 + +bool ds1624_init = false; + +struct { + float value; + uint8_t type; + int errcnt; + int misscnt; + bool valid; + char name[9]; +} ds1624_sns[DS1624_MAX_SENSORS]; + +uint32_t DS1624_Idx2Addr(uint32_t idx) { + return 0x48 + idx; +} + +int DS1624_Restart(uint8_t config, uint32_t idx) { + uint32_t addr = DS1624_Idx2Addr(idx); + if ((config & 1) == 1) { + config &= ~(DS1621_CFG_DONE|DS1621_CFG_1SHOT); + I2cWrite8(addr, DS1624_CONF_REGISTER, config); + delay(10); + AddLog_P2(LOG_LEVEL_ERROR, "%s addr %x is reset, reconfig: %x", ds1624_sns[idx].name, addr, config); + } + I2cValidRead(addr, DS1624_START_REGISTER, 1); +} + +void DS1624_HotPlugUp(uint32_t idx) +{ + uint32_t addr = DS1624_Idx2Addr(idx); + + if (I2cActive(addr)) { return; } + if (!I2cSetDevice(addr)) { return; } + + uint8_t config; + if (I2cValidRead8(&config, addr, DS1624_CONF_REGISTER)) { + uint8_t tmp; + ds1624_sns[idx].type = (I2cValidRead8(&tmp, addr, DS1624_MEM_REGISTER)) ? DS1624_TYPE_DS1624 : DS1624_TYPE_DS1621; + + snprintf_P(ds1624_sns[idx].name, sizeof(ds1624_sns[idx].name), PSTR("DS162%c%c%d"), + (ds1624_sns[idx].type == DS1624_TYPE_DS1621) ? '1' : '4', IndexSeparator(), idx); + I2cSetActiveFound(addr, ds1624_sns[idx].name); + + ds1624_sns[idx].valid = true; + ds1624_sns[idx].errcnt = 0; + ds1624_sns[idx].misscnt = 0; + DS1624_Restart(config,idx); + AddLog_P2(LOG_LEVEL_INFO, "Hot Plug %s addr %x config: %x", ds1624_sns[idx].name, addr, config); + } +} + +void DS1624_HotPlugDown(int idx) +{ + uint32_t addr = DS1624_Idx2Addr(idx); + if (!I2cActive(addr)) { return; } + I2cResetActive(addr); + ds1624_sns[idx].valid = false; + AddLog_P2(LOG_LEVEL_INFO, "Hot UnPlug %s", ds1624_sns[idx].name); +} + +bool DS1624GetTemp(float *value, int idx) +{ + uint32_t addr = DS1624_Idx2Addr(idx); + + uint8_t config; + if (!I2cValidRead8(&config, addr, DS1624_CONF_REGISTER)) { + ds1624_sns[idx].misscnt++; + AddLog_P2(LOG_LEVEL_INFO, "%s device missing (errors: %i)", ds1624_sns[idx].name, ds1624_sns[idx].misscnt); + return false; + } + ds1624_sns[idx].misscnt=0; + if (config & (DS1621_CFG_1SHOT|DS1621_CFG_DONE)) { + ds1624_sns[idx].errcnt++; + AddLog_P2(LOG_LEVEL_INFO, "%s config error, restart... (errors: %i)", ds1624_sns[idx].name, ds1624_sns[idx].errcnt); + DS1624_Restart(config, idx); + return false; + } + + uint16_t t; + if (!I2cValidRead16(&t, DS1624_Idx2Addr(idx), DS1624_TEMP_REGISTER)) { return false; } + if (ds1624_sns[idx].type == DS1624_TYPE_DS1624) { + *value = ((float)(int8_t)(t>>8)) + ((t>>4)&0xf)*0.0625; + } else { + + *value = ((float)(int8_t)(t>>8)); + uint8_t remain; + if (!I2cValidRead8(&remain, addr, DS1621_COUNTER_REGISTER)) { return true; } + uint8_t perc; + if (!I2cValidRead8(&perc, addr, DS1621_SLOPE_REGISTER)) { return true; } + float fix=(float)(perc - remain)/(float)perc; + *value+=fix; + } + ds1624_sns[idx].errcnt=0; + config &= ~(DS1621_CFG_DONE); + I2cWrite8(addr, DS1624_CONF_REGISTER, config); + return true; +} + +void DS1624HotPlugScan(void) +{ + uint16_t t; + + for (uint32_t idx = 0; idx < DS1624_MAX_SENSORS; idx++) { + uint32_t addr = DS1624_Idx2Addr(idx); + if (I2cActive(addr) && !ds1624_sns[idx].valid) { + continue; + } + if (ds1624_sns[idx].valid) { + if ((ds1624_sns[idx].misscnt>2)||(ds1624_sns[idx].errcnt>2)) { + DS1624_HotPlugDown(idx); + continue; + } + } + DS1624_HotPlugUp(idx); + } +} + +void DS1624EverySecond(void) +{ + float t; + for (uint32_t i = 0; i < DS1624_MAX_SENSORS; i++) { + if (!ds1624_sns[i].valid) { continue; } + if (!DS1624GetTemp(&t, i)) { continue; } + ds1624_sns[i].value = ConvertTemp(t); + } +} + +void DS1624Show(bool json) +{ + char temperature[33]; + bool once = true; + + for (uint32_t i = 0; i < DS1624_MAX_SENSORS; i++) { + if (!ds1624_sns[i].valid) { continue; } + + dtostrfd(ds1624_sns[i].value, Settings.flag2.temperature_resolution, temperature); + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s}"), ds1624_sns[i].name, temperature); + if ((0 == tele_period) && once) { +#ifdef USE_DOMOTICZ + DomoticzSensor(DZ_TEMP, temperature); +#endif +#ifdef USE_KNX + KnxSensor(KNX_TEMPERATURE, ds1624_sns[i].value); +#endif + once = false; + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, ds1624_sns[i].name, temperature, TempUnit()); +#endif + } + } +} + + + + + +bool Xsns59(uint8_t function) +{ + if (!I2cEnabled(XI2C_42)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + if (!ds1624_init) { + memset(ds1624_sns, 0, sizeof(ds1624_sns)); + ds1624_init = true; + DS1624HotPlugScan(); + } + } + switch (function) { + case FUNC_HOTPLUG_SCAN: + DS1624HotPlugScan(); + break; + case FUNC_EVERY_SECOND: + DS1624EverySecond(); + break; + case FUNC_JSON_APPEND: + DS1624Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + DS1624Show(0); + break; +#endif + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_60_GPS.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_60_GPS.ino" +#ifdef USE_GPS +#if defined(ESP32) && defined(USE_FLOG) + #undef USE_FLOG + #warning FLOG deactivated on ESP32 +#endif +# 117 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_60_GPS.ino" +#define XSNS_60 60 + +#include "NTPServer.h" +#include "NTPPacket.h" + +#ifdef ESP32 +#include +#endif + + + + + +#define D_CMND_UBX "UBX" + +const char S_JSON_UBX_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_UBX "%s\":%d}"; + +const char kUBXTypes[] PROGMEM = "UBX"; + +#define UBX_LAT_LON_THRESHOLD 1000 + +#define UBX_SERIAL_BUFFER_SIZE 256 +#define UBX_TCP_PORT 1234 +#define NTP_MILLIS_OFFSET 50 + + + + + +const char UBLOX_INIT[] PROGMEM = { + + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x24, + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x2B, + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x02,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x32, + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x39, + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x04,0x40, + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x05,0x00,0x00,0x00,0x00,0x00,0x01,0x05,0x47, + + + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0xDC, + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0xB9, + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0xC0, + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0x92, + + + + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x13,0xBE, + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x14,0xC5, + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x21,0x00,0x01,0x00,0x00,0x00,0x00,0x32,0x97, + + + + + + +}; + +char UBX_name[4]; + +struct UBX_t { + const char UBX_HEADER[2] = { 0xB5, 0x62 }; + const char NAV_POSLLH_HEADER[2] = { 0x01, 0x02 }; + const char NAV_STATUS_HEADER[2] = { 0x01, 0x03 }; + const char NAV_TIME_HEADER[2] = { 0x01, 0x21 }; + + struct entry_t { + int32_t lat; + int32_t lon; + uint32_t time; + }; + + union { + entry_t values; + uint8_t bytes[sizeof(entry_t)]; + } rec_buffer; + + struct POLL_MSG { + uint8_t cls; + uint8_t id; + uint16_t zero; + }; + + struct NAV_POSLLH { + uint8_t cls; + uint8_t id; + uint16_t len; + uint32_t iTOW; + int32_t lon; + int32_t lat; + int32_t alt; + int32_t hMSL; + uint32_t hAcc; + uint32_t vAcc; + }; + + struct NAV_STATUS { + uint8_t cls; + uint8_t id; + uint16_t len; + uint32_t iTOW; + uint8_t gpsFix; + uint8_t flags; + uint8_t fixStat; + uint8_t flags2; + uint32_t ttff; + uint32_t msss; + }; + + struct NAV_TIME_UTC { + uint8_t cls; + uint8_t id; + uint16_t len; + uint32_t iTOW; + uint32_t tAcc; + int32_t nano; + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t min; + uint8_t sec; + struct { + uint8_t UTC:1; + uint8_t WKN:1; + uint8_t TOW:1; + uint8_t padding:5; + } valid; + }; + + struct CFG_RATE { + uint8_t cls; + uint8_t id; + uint16_t len; + uint16_t measRate; + uint16_t navRate; + uint16_t timeRef; + char CK[2]; + }; + + struct { + uint32_t last_iTOW; + int32_t last_alt; + uint32_t last_hAcc; + uint32_t last_vAcc; + uint8_t gpsFix; + uint8_t non_empty_loops; + uint16_t log_interval; + int32_t timeOffset; + } state; + + struct { + uint32_t init:1; + uint32_t filter_noise:1; + uint32_t send_when_new:1; + uint32_t send_UI_only:1; + uint32_t runningNTP:1; + + uint32_t forceUTCupdate:1; + uint32_t runningVPort:1; + + } mode; + + union { + NAV_POSLLH navPosllh; + NAV_STATUS navStatus; + NAV_TIME_UTC navTime; + POLL_MSG pollMsg; + CFG_RATE cfgRate; + } Message; + + uint8_t TCPbuf[UBX_SERIAL_BUFFER_SIZE]; + size_t TCPbufSize; +} UBX; + +enum UBXMsgType { + MT_NONE, + MT_NAV_POSLLH, + MT_NAV_STATUS, + MT_NAV_TIME, + MT_POLL +}; + +#ifdef USE_FLOG +FLOG *Flog = nullptr; +#endif +#ifdef ESP8266 +TasmotaSerial *UBXSerial; +#else +HardwareSerial *UBXSerial; +#endif + +NtpServer timeServer(PortUdp); + +WiFiServer vPortServer(UBX_TCP_PORT); +WiFiClient vPortClient; + + + + + +void UBXcalcChecksum(char* CK, size_t msgSize) +{ + memset(CK, 0, 2); + for (int i = 0; i < msgSize; i++) { + CK[0] += ((char*)(&UBX.Message))[i]; + CK[1] += CK[0]; + } +} + +bool UBXcompareMsgHeader(const char* msgHeader) +{ + char* ptr = (char*)(&UBX.Message); + return ptr[0] == msgHeader[0] && ptr[1] == msgHeader[1]; +} + +void UBXinitCFG(void) +{ + for (uint32_t i = 0; i < sizeof(UBLOX_INIT); i++) { + UBXSerial->write( pgm_read_byte(UBLOX_INIT+i) ); + } + DEBUG_SENSOR_LOG(PSTR("UBX: turn off NMEA")); +} + +void UBXsendCFGLine(uint8_t _line) +{ + if (_line>sizeof(UBLOX_INIT)/16) return; + for (uint32_t i = 0; i < 16; i++) { + UBXSerial->write( pgm_read_byte(UBLOX_INIT+i+(_line*16)) ); + } + DEBUG_SENSOR_LOG(PSTR("UBX: send line %u of UBLOX_INIT"), _line); +} + +void UBXTriggerTele(void) +{ + mqtt_data[0] = '\0'; + if (MqttShowSensor()) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); +#ifdef USE_RULES + RulesTeleperiod(); +#endif + } +} + + + +void UBXDetect(void) +{ + UBX.mode.init = 0; + if ((pin[GPIO_GPS_RX] < 99) && (pin[GPIO_GPS_TX] < 99)) { +#ifdef ESP8266 + UBXSerial = new TasmotaSerial(pin[GPIO_GPS_RX], pin[GPIO_GPS_TX], 1, 0, UBX_SERIAL_BUFFER_SIZE); + if (UBXSerial->begin(9600)) { +#else + UBXSerial = new HardwareSerial(2); + UBXSerial->begin(9600,SERIAL_8N1,pin[GPIO_GPS_RX], pin[GPIO_GPS_TX]); + { +#endif + DEBUG_SENSOR_LOG(PSTR("UBX: started serial")); +#ifdef ESP8266 + if (UBXSerial->hardwareSerial()) { + ClaimSerial(); + DEBUG_SENSOR_LOG(PSTR("UBX: claim HW")); + } +#endif + } + } + else { + return; + } + + UBXinitCFG(); + UBX.mode.init = 1; + +#ifdef USE_FLOG + if (!Flog) { + Flog = new FLOG; + Flog->init(); + } +#endif + + UBX.state.log_interval = 10; + UBX.mode.send_UI_only = true; + UBXTriggerTele(); +} + +uint32_t UBXprocessGPS() +{ + static uint32_t fpos = 0; + static char checksum[2]; + static uint8_t currentMsgType = MT_NONE; + static size_t payloadSize = sizeof(UBX.Message); + + + uint32_t data_bytes = 0; + while ( UBXSerial->available() ) { + data_bytes++; + byte c = UBXSerial->read(); + if (UBX.mode.runningVPort){ + UBX.TCPbuf[data_bytes-1] = c; + UBX.TCPbufSize = data_bytes; + } + if ( fpos < 2 ) { + + if ( c == UBX.UBX_HEADER[fpos] ) { + fpos++; + } else { + fpos = 0; + } + } else { + + + + + + if ( (fpos-2) < payloadSize ) { + ((char*)(&UBX.Message))[fpos-2] = c; + } + fpos++; + + if ( fpos == 4 ) { + + + if ( UBXcompareMsgHeader(UBX.NAV_POSLLH_HEADER) ) { + currentMsgType = MT_NAV_POSLLH; + payloadSize = sizeof(UBX_t::NAV_POSLLH); + DEBUG_SENSOR_LOG(PSTR("UBX: got NAV_POSLLH")); + } + else if ( UBXcompareMsgHeader(UBX.NAV_STATUS_HEADER) ) { + currentMsgType = MT_NAV_STATUS; + payloadSize = sizeof(UBX_t::NAV_STATUS); + DEBUG_SENSOR_LOG(PSTR("UBX: got NAV_STATUS")); + } + else if ( UBXcompareMsgHeader(UBX.NAV_TIME_HEADER) ) { + currentMsgType = MT_NAV_TIME; + payloadSize = sizeof(UBX_t::NAV_TIME_UTC); + DEBUG_SENSOR_LOG(PSTR("UBX: got NAV_TIME_UTC")); + } + else { + + fpos = 0; + continue; + } + } + + if ( fpos == (payloadSize+2) ) { + + + UBXcalcChecksum(checksum, payloadSize); + } + else if ( fpos == (payloadSize+3) ) { + + + if ( c != checksum[0] ) { + + fpos = 0; + } + } + else if ( fpos == (payloadSize+4) ) { + + + fpos = 0; + if ( c == checksum[1] ) { + + return currentMsgType; + } + } + else if ( fpos > (payloadSize+4) ) { + + + fpos = 0; + } + } + } + + if (data_bytes!=0) { + UBX.state.non_empty_loops++; + DEBUG_SENSOR_LOG(PSTR("UBX: got %u bytes, non-empty-loop: %u"), data_bytes, UBX.state.non_empty_loops); + } else { + UBX.state.non_empty_loops = 0; + } + return MT_NONE; +} + + + + + +#ifdef USE_FLOG +void UBXsendHeader(void) +{ + Webserver->setContentLength(CONTENT_LENGTH_UNKNOWN); + Webserver->sendHeader(F("Content-Disposition"), F("attachment; filename=TASMOTA.gpx")); + WSSend(200, CT_STREAM, F( + "\r\n" + "\r\n" + "\r\n\r\n")); +} + +void UBXsendRecord(uint8_t *buf) +{ + char record[100]; + char stime[32]; + UBX_t::entry_t *entry = (UBX_t::entry_t*)buf; + snprintf_P(stime, sizeof(stime), GetDT(entry->time).c_str()); + char lat[12]; + char lon[12]; + dtostrfd((double)entry->lat/10000000.0f,7,lat); + dtostrfd((double)entry->lon/10000000.0f,7,lon); + snprintf_P(record, sizeof(record),PSTR("\n\t\n\n"),lat ,lon, stime); + + Webserver->sendContent_P(record); +} + +void UBXsendFooter(void) +{ + Webserver->sendContent(F("\n\n")); + Webserver->sendContent(""); + Rtc.user_time_entry = false; +} + + + +void UBXsendFile(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + Flog->startDownload(sizeof(UBX.rec_buffer),UBXsendHeader,UBXsendRecord,UBXsendFooter); +} +#endif + + + +void UBXSetRate(uint16_t interval) +{ + UBX.Message.cfgRate.cls = 0x06; + UBX.Message.cfgRate.id = 0x08; + UBX.Message.cfgRate.len = 6; + uint32_t measRate = (1000*(uint32_t)interval); + if (measRate > 0xffff) { + measRate = 0xffff; + } + UBX.Message.cfgRate.measRate = (uint16_t)measRate; + UBX.Message.cfgRate.navRate = 1; + UBX.Message.cfgRate.timeRef = 1; + UBXcalcChecksum(UBX.Message.cfgRate.CK, sizeof(UBX.Message.cfgRate)-sizeof(UBX.Message.cfgRate.CK)); + DEBUG_SENSOR_LOG(PSTR("UBX: requested interval: %u seconds measRate: %u ms"), interval, UBX.Message.cfgRate.measRate); + UBXSerial->write(UBX.UBX_HEADER[0]); + UBXSerial->write(UBX.UBX_HEADER[1]); + for (uint32_t i =0; iwrite(((uint8_t*)(&UBX.Message.cfgRate))[i]); + DEBUG_SENSOR_LOG(PSTR("UBX: cfgRate byte %u: %x"), i, ((uint8_t*)(&UBX.Message.cfgRate))[i]); + } + UBX.state.log_interval = 10*interval; +} + +void UBXSelectMode(uint16_t mode) +{ + DEBUG_SENSOR_LOG(PSTR("UBX: set mode to %u"),mode); + switch(mode){ +#ifdef USE_FLOG + case 0: + Flog->mode = 0; + break; + case 1: + Flog->mode = 1; + break; + case 2: + UBX.mode.filter_noise = true; + break; + case 3: + UBX.mode.filter_noise = false; + break; + case 4: + Flog->startRecording(true); + AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: start recording - appending")); + break; + case 5: + Flog->startRecording(false); + AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: start recording - new log")); + break; + case 6: + if(Flog->recording == true){ + Flog->stopRecording(); + } + AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: stop recording")); + break; +#endif + case 7: + UBX.mode.send_when_new = 1; + break; + case 8: + UBX.mode.send_when_new = 0; + break; + case 9: + if (timeServer.beginListening()) { + UBX.mode.runningNTP = true; + } + break; + case 10: + UBX.mode.runningNTP = false; + UBXsendCFGLine(10); + UBXsendCFGLine(11); + break; + case 11: + UBX.mode.forceUTCupdate = true; + break; + case 12: + UBX.mode.forceUTCupdate = false; + break; + case 13: + Settings.latitude = UBX.rec_buffer.values.lat/10; + Settings.longitude = UBX.rec_buffer.values.lon/10; + break; + case 14: + vPortServer.begin(); + UBX.mode.runningVPort = 1; + break; + case 15: + + UBX.mode.runningVPort = 0; + break; + default: + if (mode>1000 && mode <1066) { + UBXSetRate(mode-1000); + } + break; + } + UBX.mode.send_UI_only = true; + UBXTriggerTele(); +} + + + +bool UBXHandlePOSLLH() +{ + DEBUG_SENSOR_LOG(PSTR("UBX: iTOW: %u"),UBX.Message.navPosllh.iTOW); + if (UBX.state.gpsFix>1) { + if (UBX.mode.filter_noise) { + if ((UBX.Message.navPosllh.lat-UBX.rec_buffer.values.lat6) { + if(UBX.mode.runningVPort) return; + UBXinitCFG(); + AddLog_P(LOG_LEVEL_ERROR, PSTR("UBX: possible device-reset, will re-init")); + UBXSerial->flush(); + UBX.state.non_empty_loops = 0; + } +} + + + +void UBXLoop50msec(void) +{ + + if (UBX.mode.runningVPort){ + if(!vPortClient.connected()) { + vPortClient = vPortServer.available(); + } + while(vPortClient.available()) { + byte _newByte = vPortClient.read(); + UBXSerial->write(_newByte); + } + + if (UBX.TCPbufSize!=0){ + vPortClient.write((char*)UBX.TCPbuf, UBX.TCPbufSize); + UBX.TCPbufSize = 0; + } + } + + if(UBX.mode.runningNTP){ + timeServer.processOneRequest(UBX.rec_buffer.values.time, UBX.state.timeOffset - NTP_MILLIS_OFFSET); + } +} + +void UBXLoop(void) +{ + static uint16_t counter; + static bool new_position; + + uint32_t msgType = UBXprocessGPS(); + + switch(msgType){ + case MT_NAV_POSLLH: + new_position = UBXHandlePOSLLH(); + break; + case MT_NAV_STATUS: + UBXHandleSTATUS(); + break; + case MT_NAV_TIME: + UBXHandleTIME(); + break; + default: + UBXHandleOther(); + break; + } + +#ifdef USE_FLOG + if (counter>UBX.state.log_interval) { + if (Flog->recording && new_position) { + UBX.rec_buffer.values.time = Rtc.local_time; + Flog->addToBuffer(UBX.rec_buffer.bytes, sizeof(UBX.rec_buffer.bytes)); + counter = 0; + } + } +#endif + + counter++; +} + + + + +#ifdef USE_WEBSERVER + + +#ifdef USE_FLOG +#ifdef DEBUG_TASMOTA_SENSOR + const char HTTP_SNS_FLOGVER[] PROGMEM = "{s}
{m}
{e}{s} FLOG with %u sectors: {m}%u bytes{e}" + "{s} FLOG next sector for REC: {m} %u {e}" + "{s} %u sector(s) with data at sector: {m} %u {e}"; + const char HTTP_SNS_FLOGREC[] PROGMEM = "{s} RECORDING (bytes in buffer) {m}%u{e}"; +#endif + + const char HTTP_SNS_FLOG[] PROGMEM = "{s}
{m}
{e}{s} Flash-Log {m} %s{e}"; + const char kFLOG_STATE0[] PROGMEM = "ready"; + const char kFLOG_STATE1[] PROGMEM = "recording"; + const char * kFLOG_STATE[] ={kFLOG_STATE0, kFLOG_STATE1}; + + const char HTTP_BTN_FLOG_DL[] PROGMEM = ""; + +#endif + const char HTTP_SNS_NTPSERVER[] PROGMEM = "{s} NTP server {m}active{e}"; + + const char HTTP_SNS_GPS[] PROGMEM = "{s} GPS latitude {m}%s{e}" + "{s} GPS longitude {m}%s{e}" + "{s} GPS altitude {m}%s m{e}" + "{s} GPS hor. Accuracy {m}%s m{e}" + "{s} GPS vert. Accuracy {m}%s m{e}" + "{s} GPS sat-fix status {m}%s{e}"; + + const char kGPSFix0[] PROGMEM = "no fix"; + const char kGPSFix1[] PROGMEM = "dead reckoning only"; + const char kGPSFix2[] PROGMEM = "2D-fix"; + const char kGPSFix3[] PROGMEM = "3D-fix"; + const char kGPSFix4[] PROGMEM = "GPS + dead reckoning combined"; + const char kGPSFix5[] PROGMEM = "Time only fix"; + const char * kGPSFix[] PROGMEM ={kGPSFix0, kGPSFix1, kGPSFix2, kGPSFix3, kGPSFix4, kGPSFix5}; + + + + +#endif + + + +void UBXShow(bool json) +{ + char lat[12]; + char lon[12]; + char alt[12]; + char hAcc[12]; + char vAcc[12]; + dtostrfd((double)UBX.rec_buffer.values.lat/10000000.0f,7,lat); + dtostrfd((double)UBX.rec_buffer.values.lon/10000000.0f,7,lon); + dtostrfd((double)UBX.state.last_alt/1000.0f,3,alt); + dtostrfd((double)UBX.state.last_vAcc/1000.0f,3,hAcc); + dtostrfd((double)UBX.state.last_hAcc/1000.0f,3,vAcc); + + if (json) { + ResponseAppend_P(PSTR(",\"GPS\":{")); + if (UBX.mode.send_UI_only) { + uint32_t i = UBX.state.log_interval / 10; + ResponseAppend_P(PSTR("\"fil\":%u,\"int\":%u}"), UBX.mode.filter_noise, i); + } else { + ResponseAppend_P(PSTR("\"lat\":%s,\"lon\":%s,\"alt\":%s,\"hAcc\":%s,\"vAcc\":%s}"), lat, lon, alt, hAcc, vAcc); + } +#ifdef USE_FLOG + ResponseAppend_P(PSTR(",\"FLOG\":{\"rec\":%u,\"mode\":%u,\"sec\":%u}"), Flog->recording, Flog->mode, Flog->sectors_left); +#endif + UBX.mode.send_UI_only = false; +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_GPS, lat, lon, alt, hAcc, vAcc, kGPSFix[UBX.state.gpsFix]); + +#ifdef DEBUG_TASMOTA_SENSOR +#ifdef USE_FLOG + WSContentSend_PD(HTTP_SNS_FLOGVER, Flog->num_sectors, Flog->size, Flog->current_sector, Flog->sectors_left, Flog->sector.header.physical_start_sector); + if (Flog->recording) { + WSContentSend_PD(HTTP_SNS_FLOGREC, Flog->sector.header.buf_pointer - 8); + } +#endif +#endif +#ifdef USE_FLOG + if (Flog->ready) { + WSContentSend_P(HTTP_SNS_FLOG,kFLOG_STATE[Flog->recording]); + } + if (!Flog->recording && Flog->found_saved_data) { + WSContentSend_P(HTTP_BTN_FLOG_DL); + } +#endif + if (UBX.mode.runningNTP) { + WSContentSend_P(HTTP_SNS_NTPSERVER); + } +#endif + } +} + + + + + +bool UBXCmd(void) +{ + bool serviced = true; + if (XdrvMailbox.data_len > 0) { + UBXSelectMode(XdrvMailbox.payload); + Response_P(S_JSON_UBX_COMMAND_NVALUE, XdrvMailbox.command, XdrvMailbox.payload); + } + return serviced; +} + + + + + +bool Xsns60(uint8_t function) +{ + bool result = false; + + if (FUNC_INIT == function) { + UBXDetect(); + } + + if (UBX.mode.init) { + switch (function) { + case FUNC_COMMAND_SENSOR: + if (XSNS_60 == XdrvMailbox.index) { + result = UBXCmd(); + } + break; + case FUNC_EVERY_50_MSECOND: + UBXLoop50msec(); + break; + case FUNC_EVERY_100_MSECOND: +#ifdef USE_FLOG + if (!Flog->running_download) +#endif + { + UBXLoop(); + } + break; +#ifdef USE_FLOG + case FUNC_WEB_ADD_HANDLER: + Webserver->on("/UBX", UBXsendFile); + break; +#endif + case FUNC_JSON_APPEND: + UBXShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: +#ifdef USE_FLOG + if (!Flog->running_download) +#endif + { + UBXShow(0); + } + break; +#endif + } + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_61_MI_NRF24.ino" +# 44 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_61_MI_NRF24.ino" +#ifdef USE_SPI +#ifdef USE_NRF24 +#ifdef USE_MIBLE + +#ifdef DEBUG_TASMOTA_SENSOR + #define MINRF_LOG_BUFFER(x) MINRFshowBuffer(x); +#else + #define MINRF_LOG_BUFFER(x) +#endif +# 62 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_61_MI_NRF24.ino" +#define XSNS_61 61 + +#include + +#define FLORA 1 +#define MJ_HT_V1 2 +#define LYWSD02 3 +#define LYWSD03 4 +#define CGG1 5 +#define CGD1 6 + +#define D_CMND_NRF "NRF" + +const char S_JSON_NRF_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_NRF "%s\":%d}"; +const char S_JSON_NRF_COMMAND[] PROGMEM = "{\"" D_CMND_NRF "%s\":\"%s\"}"; +const char kNRF_Commands[] PROGMEM = "Ignore|Page|Scan|Beacon|Chan"; + +enum NRF_Commands { + CMND_NRF_IGNORE, + CMND_NRF_PAGE, + CMND_NRF_SCAN, + CMND_NRF_BEACON, + CMND_NRF_CHAN + }; + +const uint16_t kMINRFSlaveID[6]={ 0x0098, + 0x01aa, + 0x045b, + 0x055b, + 0x0347, + 0x0576 + }; + +const char kMINRFSlaveType1[] PROGMEM = "Flora"; +const char kMINRFSlaveType2[] PROGMEM = "MJ_HT_V1"; +const char kMINRFSlaveType3[] PROGMEM = "LYWSD02"; +const char kMINRFSlaveType4[] PROGMEM = "LYWSD03"; +const char kMINRFSlaveType5[] PROGMEM = "CGG1"; +const char kMINRFSlaveType6[] PROGMEM = "CGD1"; +const char * kMINRFSlaveType[] PROGMEM = {kMINRFSlaveType1,kMINRFSlaveType2,kMINRFSlaveType3,kMINRFSlaveType4,kMINRFSlaveType5,kMINRFSlaveType6}; + + +const uint32_t kMINRFFloPDU[3] = {0x3eaa857d,0xef3b8730,0x71da7b46}; +const uint32_t kMINRFMJPDU[3] = {0x4760cd66,0xdbcc0cd3,0x33048df5}; +const uint32_t kMINRFL2PDU[3] = {0x3eaa057d,0xef3b0730,0x71dafb46}; + +const uint32_t kMINRFL3PDU[3] = {0x4760cb78,0xdbcc0acd,0x33048beb}; +const uint32_t kMINRFCGGPDU[3] = {0x4760cd6e,0xdbcc0cdb,0x33048dfd}; +const uint32_t kMINRFCGDPDU[3] = {0x5da0d752,0xc10c16e7,0x29c497c1}; + + +const uint8_t kMINRFlsfrList_A[3] = {0x4b,0x17,0x23}; +const uint8_t kMINRFlsfrList_B[3] = {0x21,0x72,0x43}; + + +#pragma pack(1) +struct mi_beacon_t{ + uint16_t productID; + uint8_t counter; + uint8_t Mac[6]; + uint8_t spare; + uint8_t type; + uint8_t ten; + uint8_t size; + union { + struct{ + int16_t temp; + uint16_t hum; + }HT; + uint8_t bat; + uint16_t temp; + uint16_t hum; + uint32_t lux:24; + uint8_t moist; + uint16_t fert; + }; +}; + +struct CGDPacket_t { + uint8_t serial[6]; + uint16_t mode; + union { + struct { + int16_t temp; + uint16_t hum; + }; + uint8_t bat; + }; +}; + +struct bleAdvPacket_t { + uint8_t pduType; + uint8_t payloadSize; + uint8_t mac[6]; +}; + +union FIFO_t{ + bleAdvPacket_t bleAdv; + mi_beacon_t miBeacon; + CGDPacket_t CGDPacket; + uint8_t raw[32]; +}; + +#pragma pack(0) + +struct { + const uint8_t channel[3] = {37,38,39}; + const uint8_t frequency[3] = { 2,26,80}; + + uint16_t timer; + uint8_t currentChan=0; + uint8_t ignore = 0; + uint8_t channelIgnore = 0; + uint8_t confirmedSensors = 0; + uint8_t packetMode; + uint8_t perPage = 4; + uint8_t firstUsedPacketMode = 1; + + FIFO_t buffer; + + struct { + uint8_t mac[6]; + uint32_t time; + uint32_t PDU[3]; + bool active = false; + } beacon; + bool activeScan = false; + bool stopScan = false; + +#ifdef DEBUG_TASMOTA_SENSOR + uint8_t streamBuffer[sizeof(buffer)]; + uint8_t lsfrBuffer[sizeof(buffer)]; +#endif + +} MINRF; + +struct mi_sensor_t{ + uint8_t type; + uint8_t serial[6]; + uint8_t showedUp; + float temp; + union { + struct { + float moisture; + float fertility; + uint32_t lux; + }; + struct { + float hum; + uint8_t bat; + }; + }; +}; + +struct scan_entry_t { + uint8_t mac[6]; + uint16_t cid; + uint16_t svc; + uint16_t uuid; + uint8_t showedUp; +}; + +std::vector MIBLEsensors; +std::vector MINRFscanResult; + +static union{ + scan_entry_t MINRFdummyEntry; + uint8_t MINRFtempBuf[23]; +}; +# 241 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_61_MI_NRF24.ino" +bool MINRFinitBLE(uint8_t _mode) +{ + if (MINRF.timer%1000 == 0){ + NRF24radio.begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_DC]); + NRF24radio.setAutoAck(false); + NRF24radio.setDataRate(RF24_1MBPS); + NRF24radio.disableCRC(); + NRF24radio.setChannel( MINRF.frequency[MINRF.currentChan] ); + NRF24radio.setRetries(0,0); + NRF24radio.setPALevel(RF24_PA_MIN); + NRF24radio.setAddressWidth(4); + + + NRF24radio.powerUp(); + } + if(NRF24radio.isChipConnected()){ + + MINRFchangePacketModeTo(_mode); + return true; + } + + return false; +} + + + + + +void MINRFhopChannel() +{ + for (uint32_t i = 0; i<3;i++){ + MINRF.currentChan++; + if(bitRead(MINRF.channelIgnore,MINRF.currentChan)) continue; + if(MINRF.currentChan >= sizeof(MINRF.channel)) { + MINRF.currentChan = 0; + if(bitRead(MINRF.channelIgnore,MINRF.currentChan)) continue; + } + break; + } + NRF24radio.setChannel( MINRF.frequency[MINRF.currentChan] ); +} + + + + + + + +bool MINRFreceivePacket(void) +{ + if(!NRF24radio.available()) { + return false; + } + while(NRF24radio.available()) { + + + NRF24radio.read( &MINRF.buffer, sizeof(MINRF.buffer) ); +#ifdef DEBUG_TASMOTA_SENSOR + memcpy(&MINRF.streamBuffer, &MINRF.buffer, sizeof(MINRF.buffer)); +#endif + MINRFswapbuf((uint8_t*)&MINRF.buffer, sizeof(MINRF.buffer) ); + + + + switch (MINRF.packetMode) { + case 0: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), MINRF.channel[MINRF.currentChan] | 0x40); + break; + case 1: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_A[MINRF.currentChan]); + break; + case 2: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); + break; + case 3: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_A[MINRF.currentChan]); + break; + case 4: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); + break; + case 5: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); + break; + case 6: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); + break; + } + + + } + + return true; +} + +#ifdef DEBUG_TASMOTA_SENSOR +void MINRFshowBuffer(uint8_t (&buf)[32]){ + + + + + DEBUG_SENSOR_LOG(PSTR("MINRF: Buffer: %02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x ") + ,buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7],buf[8],buf[9],buf[10],buf[11], + buf[12],buf[13],buf[14],buf[15],buf[16],buf[17],buf[18],buf[19],buf[20],buf[21],buf[22],buf[23], + buf[24],buf[25],buf[26],buf[27],buf[28],buf[29],buf[30],buf[31] + ); +} +#endif + + + + + + +void MINRFswapbuf(uint8_t *buf, uint8_t len) +{ + + while(len--) { + uint8_t a = *buf; + uint8_t v = 0; + if (a & 0x80) v |= 0x01; + if (a & 0x40) v |= 0x02; + if (a & 0x20) v |= 0x04; + if (a & 0x10) v |= 0x08; + if (a & 0x08) v |= 0x10; + if (a & 0x04) v |= 0x20; + if (a & 0x02) v |= 0x40; + if (a & 0x01) v |= 0x80; + *(buf++) = v; + } +} +# 382 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_61_MI_NRF24.ino" +void MINRFwhiten(uint8_t *buf, uint8_t len, uint8_t lfsr) +{ + while(len--) { + uint8_t res = 0; + + for (uint8_t i = 1; i; i <<= 1) { + if (lfsr & 0x01) { + lfsr ^= 0x88; + res |= i; + } + lfsr >>= 1; + } + *(buf++) ^= res; +#ifdef DEBUG_TASMOTA_SENSOR + MINRF.lsfrBuffer[31-len] = lfsr; +#endif + } +} + + + + +bool MINRFhandleBeacon(scan_entry_t * entry, uint32_t offset); + + + + + +void MINRFhandleScan(void){ + if(MINRFscanResult.size()>20 || MINRF.stopScan) { + MINRF.activeScan=false; + MINRFcomputefirstUsedPacketMode(); + uint32_t i = 0; + MINRFscanResult.erase(std::remove_if(MINRFscanResult.begin(), + MINRFscanResult.end(), + [&i](scan_entry_t e) { + if(e.showedUp>2) AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: Beacon %02u: %02X%02X%02X%02X%02X%02X Cid: %04X Svc: %04X UUID: %04X"),i,e.mac[0],e.mac[1],e.mac[2],e.mac[3],e.mac[4],e.mac[5],e.cid,e.svc,e.uuid); + i++; + return ((e.showedUp < 3)); + }), + MINRFscanResult.end()); + MINRF.stopScan=false; + return; + } + + MINRFreverseMAC(MINRF.buffer.bleAdv.mac); + for(uint32_t i=0; i30) break; + uint32_t ADtype = _buf[i+1]; + + if (size+i>32+offset) size=32-i+offset-2; + if (size>30) break; + char _stemp[(size*2)]; + uint32_t backupSize; + switch(ADtype){ + case 0x01: + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Flags: %02x"), _buf[i+2]); + break; + case 0x02: case 0x03: + entry->uuid = _buf[i+3]*256 + _buf[i+2]; + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: UUID: %04x"), entry->uuid); + success = true; + break; + case 0x08: case 0x09: + backupSize = _buf[i+size+1]; + _buf[i+size+1] = 0; + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Name: %s"), (char*)&_buf[i+2]); + success = true; + _buf[i+size+1] = backupSize; + break; + case 0x0a: + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: TxPow: %02u"), _buf[i+2]); + break; + case 0xff: + entry->cid = _buf[i+3]*256 + _buf[i+2]; + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Cid: %04x"), entry->cid); + ToHex_P((unsigned char*)&_buf+i+4,size-3,_stemp,(size*2)); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); + success = true; + break; + case 0x16: + entry->svc = _buf[i+3]*256 + _buf[i+2]; + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Svc: %04x"), entry->svc); + ToHex_P((unsigned char*)&_buf+i+4,size-3,_stemp,(size*2)); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); + success = true; + break; + default: + ToHex_P((unsigned char*)&_buf+i+2,size-1,_stemp,(size*2)); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); + } + i+=size; + } + MINRF.beacon.time = 0; + } + return success; +} + + + + + +void MINRFbeaconCounter(void){ + if(MINRF.beacon.active) { + MINRF.beacon.time++; + char stemp[20]; + snprintf_P(stemp, sizeof(stemp),PSTR("{%s:{\"Beacon\": %u}}"),D_CMND_NRF, MINRF.beacon.time); + AddLog_P2(LOG_LEVEL_DEBUG, stemp); + RulesProcessEvent(stemp); + } +} + + + + + +void MINRFcomputeBeaconPDU(void){ + for (uint32_t i = 0; i<3; i++){ + bleAdvPacket_t packet; + memcpy((uint8_t *)&packet.mac, (uint8_t *)&MINRF.beacon.mac, sizeof(packet.mac)); + MINRFreverseMAC(packet.mac); + MINRFwhiten((uint8_t *)&packet, sizeof(packet), MINRF.channel[i] | 0x40); + MINRFswapbuf((uint8_t*)&packet,sizeof(packet)); + uint32_t pdu = packet.mac[0]<<24 | packet.mac[1]<<16 | packet.mac[2]<<8 | packet.mac[3]; + MINRF.beacon.PDU[i] = pdu; + } +} +# 576 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_61_MI_NRF24.ino" +void MINRFreverseMAC(uint8_t _mac[]){ + uint8_t _reversedMAC[6]; + for (uint8_t i=0; i<6; i++){ + _reversedMAC[5-i] = _mac[i]; + } + memcpy(_mac,_reversedMAC, sizeof(_reversedMAC)); +} + + + + + + + +void MINRFMACStringToBytes(char* _string, uint8_t _mac[]) { + uint32_t index = 0; + while (index < 12) { + char c = _string[index]; + uint8_t value = 0; + if(c >= '0' && c <= '9') + value = (c - '0'); + else if (c >= 'A' && c <= 'F') + value = (10 + (c - 'A')); + _mac[(index/2)] += value << (((index + 1) % 2) * 4); + index++; + } + +} + + + + + +void MINRFcomputefirstUsedPacketMode(void){ + for (uint32_t i = 0; iCGD1) MINRF.firstUsedPacketMode=0; + break; + } + } +} + + + + + + +void MINRFchangePacketModeTo(uint8_t _mode) { + uint32_t (_nextchannel) = MINRF.currentChan+1; + if (_nextchannel>2) _nextchannel=0; + + switch(_mode){ + case 0: + NRF24radio.openReadingPipe(0,0x6B7D9171); + break; + case 1: + NRF24radio.openReadingPipe(0,kMINRFFloPDU[_nextchannel]); + break; + case 2: + NRF24radio.openReadingPipe(0,kMINRFMJPDU[_nextchannel]); + break; + case 3: + NRF24radio.openReadingPipe(0,kMINRFL2PDU[_nextchannel]); + break; + case 4: + NRF24radio.openReadingPipe(0,kMINRFL3PDU[_nextchannel]); + break; + case 5: + NRF24radio.openReadingPipe(0,kMINRFCGGPDU[_nextchannel]); + break; + case 6: + NRF24radio.openReadingPipe(0,kMINRFCGDPDU[_nextchannel]); + break; + } + + MINRF.packetMode = _mode; +} +# 663 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_61_MI_NRF24.ino" +uint32_t MINRFgetSensorSlot(uint8_t (&_serial)[6], uint16_t _type){ + + DEBUG_SENSOR_LOG(PSTR("MINRF: will test ID-type: %x"), _type); + bool _success = false; + for (uint32_t i=0;i<6;i++){ + if(_type == kMINRFSlaveID[i]){ + DEBUG_SENSOR_LOG(PSTR("MINRF: ID is type %u"), i); + _type = i+1; + _success = true; + } + else { + DEBUG_SENSOR_LOG(PSTR("MINRF: ID-type is not: %x"),kMINRFSlaveID[i]); + } + } + if(!_success) return 0xff; + + DEBUG_SENSOR_LOG(PSTR("MINRF: vector size %u"), MIBLEsensors.size()); + for(uint32_t i=0; i 2){ + MINRF.confirmedSensors++; + } + } +} + + + + + +void MINRFhandleMiBeaconPacket(void){ + MINRFreverseMAC(MINRF.buffer.miBeacon.Mac); + uint32_t _slot = MINRFgetSensorSlot(MINRF.buffer.miBeacon.Mac, MINRF.buffer.miBeacon.productID); + if(_slot==0xff) return; + DEBUG_SENSOR_LOG(PSTR("MINRF: slot %u, size vector: %u %u"),_slot,MIBLEsensors.size()); + + mi_sensor_t *_sensorVec = &MIBLEsensors.at(_slot); + float _tempFloat; + + if (_sensorVec->type==MJ_HT_V1 || _sensorVec->type==CGG1){ + memcpy(MINRFtempBuf,(uint8_t*)&MINRF.buffer.miBeacon.spare, 32-9); + memcpy((uint8_t*)&MINRF.buffer.miBeacon.type,MINRFtempBuf, 32-9); + } + + DEBUG_SENSOR_LOG(PSTR("%s at slot %u"), kNRFSlaveType[_sensorVec->type-1],_slot); + switch(MINRF.buffer.miBeacon.type){ + case 0x04: + _tempFloat=(float)(MINRF.buffer.miBeacon.temp)/10.0f; + if(_tempFloat<60){ + _sensorVec->temp=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 4: temp updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 4: U16: %u Temp"), MINRF.buffer.miBeacon.temp ); + break; + case 0x06: + _tempFloat=(float)(MINRF.buffer.miBeacon.hum)/10.0f; + if(_tempFloat<101){ + _sensorVec->hum=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 6: hum updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 6: U16: %u Hum"), MINRF.buffer.miBeacon.hum); + break; + case 0x07: + _sensorVec->lux=MINRF.buffer.miBeacon.lux & 0x00ffffff; + DEBUG_SENSOR_LOG(PSTR("Mode 7: U24: %u Lux"), MINRF.buffer.miBeacon.lux & 0x00ffffff); + break; + case 0x08: + _tempFloat =(float)MINRF.buffer.miBeacon.moist; + if(_tempFloat<100){ + _sensorVec->moisture=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 8: moisture updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 8: U8: %u Moisture"), MINRF.buffer.miBeacon.moist); + break; + case 0x09: + _tempFloat=(float)(MINRF.buffer.miBeacon.fert); + if(_tempFloat<65535){ + _sensorVec->fertility=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 9: fertility updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 9: U16: %u Fertility"), MINRF.buffer.miBeacon.fert); + break; + case 0x0a: + if(MINRF.buffer.miBeacon.bat<101){ + _sensorVec->bat = MINRF.buffer.miBeacon.bat; + DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode a: U8: %u %%"), MINRF.buffer.miBeacon.bat); + break; + case 0x0d: + _tempFloat=(float)(MINRF.buffer.miBeacon.HT.temp)/10.0f; + if(_tempFloat<60){ + _sensorVec->temp = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode d: temp updated")); + } + _tempFloat=(float)(MINRF.buffer.miBeacon.HT.hum)/10.0f; + if(_tempFloat<100){ + _sensorVec->hum = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode d: hum updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode d: U16: %x Temp U16: %x Hum"), MINRF.buffer.miBeacon.HT.temp, MINRF.buffer.miBeacon.HT.hum); + break; + } +} + + + + +void MINRFhandleLYWSD03Packet(void){ + + MINRFreverseMAC(MINRF.buffer.miBeacon.Mac); + uint32_t _slot = MINRFgetSensorSlot(MINRF.buffer.miBeacon.Mac, MINRF.buffer.miBeacon.productID); + DEBUG_SENSOR_LOG(PSTR("MINRF: Sensor slot: %u"), _slot); + if(_slot==0xff) return; + + MINRF_LOG_BUFFER(MINRF.streamBuffer); + MINRF_LOG_BUFFER(MINRF.lsfrBuffer); + MINRF_LOG_BUFFER(MINRF.buffer.raw); +} + + + + + +void MINRFhandleCGD1Packet(void){ + MINRFreverseMAC(MINRF.buffer.CGDPacket.serial); + uint32_t _slot = MINRFgetSensorSlot(MINRF.buffer.CGDPacket.serial, 0x0576); + DEBUG_SENSOR_LOG(PSTR("MINRF: Sensor slot: %u"), _slot); + if(_slot==0xff) return; + + switch (MINRF.buffer.CGDPacket.mode){ + case 0x0401: + float _tempFloat; + _tempFloat=(float)(MINRF.buffer.CGDPacket.temp)/10.0f; + if(_tempFloat<60){ + MIBLEsensors.at(_slot).temp = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("CGD1: temp updated")); + } + _tempFloat=(float)(MINRF.buffer.CGDPacket.hum)/10.0f; + if(_tempFloat<100){ + MIBLEsensors.at(_slot).hum = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("CGD1: hum updated")); + } + DEBUG_SENSOR_LOG(PSTR("CGD1: U16: %x Temp U16: %x Hum"), MINRF.buffer.CGDPacket.temp, MINRF.buffer.CGDPacket.hum); + break; + case 0x0102: + if(MINRF.buffer.CGDPacket.bat<101){ + MIBLEsensors.at(_slot).bat = MINRF.buffer.CGDPacket.bat; + DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated")); + } + break; + default: + DEBUG_SENSOR_LOG(PSTR("MINRF: unexpected CGD1-packet")); + MINRF_LOG_BUFFER(MINRF.buffer.raw); + } +} + + + + + +void MINRF_EVERY_50_MSECOND() { + + if(MINRF.timer>6000){ + DEBUG_SENSOR_LOG(PSTR("MINRF: check for FAKE sensors")); + MINRFpurgeFakeSensors(); + MINRF.timer=0; + } + MINRF.timer++; + + if (!MINRFreceivePacket()){ + + } + + else { + switch (MINRF.packetMode) { + case 0: + if (MINRF.beacon.active){ + MINRFhandleBeacon(&MINRFdummyEntry,6); + } + else MINRFhandleScan(); + break; + case FLORA: case MJ_HT_V1: case LYWSD02: case CGG1: + MINRFhandleMiBeaconPacket(); + break; + case LYWSD03: + MINRFhandleLYWSD03Packet(); + break; + case CGD1: + MINRFhandleCGD1Packet(); + break; + default: + break; + } + } + if (MINRF.beacon.active || MINRF.activeScan) { + MINRF.firstUsedPacketMode=0; + } + + MINRF.packetMode = (MINRF.packetMode+1>CGD1) ? MINRF.firstUsedPacketMode : MINRF.packetMode+1; + for (uint32_t i = MINRF.packetMode; i 0) { + if (XdrvMailbox.payload == 0) XdrvMailbox.payload = MINRF.perPage; + MINRF.perPage = XdrvMailbox.payload; + } + else XdrvMailbox.payload = MINRF.perPage; + Response_P(S_JSON_NRF_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_NRF_IGNORE: + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload == 0){ + MINRF.ignore = 0; + } + else if (XdrvMailbox.payload < CGD1+1) { + bitSet(MINRF.ignore,XdrvMailbox.payload); + MINRFcomputefirstUsedPacketMode(); + MINRF.timer = 5900; + Response_P(S_JSON_NRF_COMMAND, command, kMINRFSlaveType[XdrvMailbox.payload-1]); + } + else if (XdrvMailbox.payload == 255) { + MINRF.ignore = 255; + } + } + Response_P(S_JSON_NRF_COMMAND_NVALUE, command, MINRF.ignore); + break; + case CMND_NRF_SCAN: + if (XdrvMailbox.data_len > 0) { + MINRF.beacon.active = false; + switch(XdrvMailbox.payload){ + case 0: + MINRF.activeScan = true; + MINRF.stopScan = false; + MINRFscanResult.erase(std::remove_if(MINRFscanResult.begin(), + MINRFscanResult.end(), + [](scan_entry_t&) { return true; }), + MINRFscanResult.end()); + break; + case 1: + MINRF.activeScan = true; + MINRF.stopScan = false; + break; + case 2: + MINRF.stopScan = true; + break; + } + Response_P(S_JSON_NRF_COMMAND_NVALUE, command, XdrvMailbox.payload); + } + break; + case CMND_NRF_BEACON: + if (XdrvMailbox.data_len > 0) { + if(XdrvMailbox.data_len<3){ + if (XdrvMailbox.payload < MINRFscanResult.size()) { + MINRFstartBeacon(XdrvMailbox.payload); + Response_P(S_JSON_NRF_COMMAND_NVALUE, command, XdrvMailbox.payload); + } + } + if (XdrvMailbox.data_len==12){ + memset(MINRF.beacon.mac,0,sizeof(MINRF.beacon.mac)); + MINRFMACStringToBytes(XdrvMailbox.data, MINRF.beacon.mac); + MINRF.beacon.time=0; + MINRF.beacon.active=true; + Response_P(S_JSON_NRF_COMMAND, command, XdrvMailbox.data); + } + MINRFcomputeBeaconPDU(); + } + break; + case CMND_NRF_CHAN: + if (XdrvMailbox.data_len == 1) { + switch(XdrvMailbox.payload){ + case 0: case 1: case 2: + bitRead(MINRF.channelIgnore,XdrvMailbox.payload) == 0 ? bitSet(MINRF.channelIgnore,XdrvMailbox.payload) : bitClear(MINRF.channelIgnore,XdrvMailbox.payload); + break; + } + } + Response_P(S_JSON_NRF_COMMAND_NVALUE, command, MINRF.channelIgnore); + break; + default: + + serviced = false; + break; + } + } else { + return false; + } + return serviced; +} + + + + + +const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u%%{e}"; +const char HTTP_MINRF_MAC[] PROGMEM = "{s}%s %s{m}%02x:%02x:%02x:%02x:%02x:%02x%{e}"; +const char HTTP_MINRF_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%dus/cm{e}"; +const char HTTP_MINRF_HL[] PROGMEM = "{s}
{m}
{e}"; +const char HTTP_NRF24NEW[] PROGMEM = "{s}%sL01%c{m}%u%s / %u{e}"; + +void MINRFShow(bool json) +{ + if (json) { + for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { + if(MIBLEsensors[i].showedUp < 3){ + DEBUG_SENSOR_LOG(PSTR("MINRF: sensor not fully registered yet")); + break; + } + ResponseAppend_P(PSTR(",\"%s-%02x%02x%02x\":{"),kMINRFSlaveType[MIBLEsensors[i].type-1],MIBLEsensors[i].serial[3],MIBLEsensors[i].serial[4],MIBLEsensors[i].serial[5]); + if (MIBLEsensors[i].type==FLORA && !isnan(MIBLEsensors[i].temp)){ + char stemp[FLOATSZ]; + dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, stemp); + ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), stemp); + + if(MIBLEsensors[i].lux!=0xffffffff){ + ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%u"), MIBLEsensors[i].lux); + } + if(!isnan(MIBLEsensors[i].moisture)){ + dtostrfd(MIBLEsensors[i].moisture, 0, stemp); + ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%s"), stemp); + } + if(!isnan(MIBLEsensors[i].fertility)){ + dtostrfd(MIBLEsensors[i].fertility, 0, stemp); + ResponseAppend_P(PSTR(",\"Fertility\":%s"), stemp); + } + ResponseJsonEnd(); + } + if (MIBLEsensors[i].type>FLORA){ + if(!isnan(MIBLEsensors[i].temp) && !isnan(MIBLEsensors[i].hum)){ + ResponseAppendTHD(MIBLEsensors[i].temp,MIBLEsensors[i].hum); + } + if(MIBLEsensors[i].bat!=0x00){ + ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors[i].bat); + } + ResponseJsonEnd(); + } + } + if(MINRF.beacon.active){ + ResponseAppend_P(PSTR(",\"Beacon\":{\"Timer\":%u}"),MINRF.beacon.time); + } + +#ifdef USE_WEBSERVER + } else { + static uint32_t _page = 0; + static uint32_t counter = 0; + int32_t i = _page * MINRF.perPage; + uint32_t j = i + MINRF.perPage; + + if (j+1>MINRF.confirmedSensors){ + j = MINRF.confirmedSensors; + } + char stemp[5] ={0}; + if (MINRF.confirmedSensors-(_page*MINRF.perPage)>1 && MINRF.perPage!=1) { + sprintf_P(stemp,"-%u",j); + } + if (MINRF.confirmedSensors==0) i=-1; + + WSContentSend_PD(HTTP_NRF24NEW, NRF24type, NRF24.chipType, i+1,stemp,MINRF.confirmedSensors); + for (i ; iFLORA){ + WSContentSend_THD(kMINRFSlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].temp, MIBLEsensors[i].hum); + if(MIBLEsensors[i].bat!=0x00){ + WSContentSend_PD(HTTP_BATTERY, kMINRFSlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].bat); + } + } + } + if(MINRF.beacon.active){ + WSContentSend_PD(HTTP_MINRF_HL); + WSContentSend_PD(HTTP_MINRF_HL); + WSContentSend_PD(HTTP_MINRF_MAC, F("Beacon"), D_MAC_ADDRESS, MINRF.beacon.mac[0], MINRF.beacon.mac[1],MINRF.beacon.mac[2],MINRF.beacon.mac[3],MINRF.beacon.mac[4],MINRF.beacon.mac[5]); + WSContentSend_PD(PSTR("{s}Beacon Time{m}%u seconds{e}"),MINRF.beacon.time); + } + if(counter>3) { + _page++; + counter = 0; + } + counter++; + if(MINRF.confirmedSensors%MINRF.perPage==0 && _page==MINRF.confirmedSensors/MINRF.perPage) _page=0; + if(_page>MINRF.confirmedSensors/MINRF.perPage) _page=0; +#endif + } +} + + + + + +bool Xsns61(uint8_t function) +{ + bool result = false; + if (NRF24.chipType) { + switch (function) { + case FUNC_INIT: + MINRFinitBLE(1); + AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: started")); + break; + case FUNC_EVERY_50_MSECOND: + MINRF_EVERY_50_MSECOND(); + break; + case FUNC_EVERY_SECOND: + MINRFbeaconCounter(); + break; + case FUNC_COMMAND: + result = NRFCmd(); + break; + case FUNC_JSON_APPEND: + MINRFShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MINRFShow(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_62_MI_HM10.ino" +# 37 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_62_MI_HM10.ino" +#ifdef USE_HM10 + +#define XSNS_62 62 + +#include +#include + +TasmotaSerial *HM10Serial; +#define HM10_BAUDRATE 115200 + +#define HM10_MAX_TASK_NUMBER 12 +uint8_t HM10_TASK_LIST[HM10_MAX_TASK_NUMBER+1][2]; + +#define HM10_MAX_RX_BUF 64 + +struct { + uint8_t current_task_delay; + uint8_t last_command; + uint16_t perPage = 4; + uint16_t firmware; + uint32_t period; + uint32_t serialSpeed; + union { + uint32_t time; + uint8_t timebuf[4]; + }; + uint16_t autoScanInterval; + struct { + uint32_t awaiting:8; + uint32_t init:1; + uint32_t pending_task:1; + uint32_t connected:1; + uint32_t subscribed:1; + uint32_t autoScan:1; + + } mode; + struct { + uint8_t sensor; + + } state; +} HM10; + +#pragma pack(1) + + struct { + uint16_t temp; + uint8_t hum; + } LYWSD0x_HT; + struct { + uint8_t spare; + uint16_t temp; + uint16_t hum; + } CGD1_HT; + struct { + uint16_t temp; + uint8_t spare; + uint32_t lux; + uint8_t moist; + uint16_t fert; + } Flora_TLMF; + + +struct mi_beacon_t{ + uint16_t frame; + uint16_t productID; + uint8_t counter; + uint8_t Mac[6]; + uint8_t spare; + uint8_t type; + uint8_t ten; + uint8_t size; + union { + struct{ + uint16_t temp; + uint16_t hum; + }HT; + uint8_t bat; + uint16_t temp; + uint16_t hum; + uint32_t lux; + uint8_t moist; + uint16_t fert; + }; +}; +#pragma pack(0) + +struct mi_sensor_t{ + uint8_t type; + uint8_t serial[6]; + uint8_t showedUp; + float temp; + union { + struct { + float moisture; + float fertility; + uint32_t lux; + }; + struct { + float hum; + }; + }; + uint8_t bat; +}; + +std::vector MIBLEsensors; + + + + + +#define D_CMND_HM10 "HM10" + +const char S_JSON_HM10_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_HM10 "%s\":%d}"; +const char S_JSON_HM10_COMMAND[] PROGMEM = "{\"" D_CMND_HM10 "%s%s\"}"; +const char kHM10_Commands[] PROGMEM = "Scan|AT|Period|Baud|Time|Auto|Page"; + +#define FLORA 1 +#define MJ_HT_V1 2 +#define LYWSD02 3 +#define LYWSD03MMC 4 +#define CGG1 5 +#define CGD1 6 + +const uint16_t kHM10SlaveID[6]={ 0x0098, + 0x01aa, + 0x045b, + 0x055b, + 0x0347, + 0x0576 + }; + +const char kHM10SlaveType1[] PROGMEM = "Flora"; +const char kHM10SlaveType2[] PROGMEM = "MJ_HT_V1"; +const char kHM10SlaveType3[] PROGMEM = "LYWSD02"; +const char kHM10SlaveType4[] PROGMEM = "LYWSD03"; +const char kHM10SlaveType5[] PROGMEM = "CGG1"; +const char kHM10SlaveType6[] PROGMEM = "CGD1"; +const char * kHM10SlaveType[] PROGMEM = {kHM10SlaveType1,kHM10SlaveType2,kHM10SlaveType3,kHM10SlaveType4,kHM10SlaveType5,kHM10SlaveType6}; + + + + + +enum HM10_Commands { + CMND_HM10_DISC_SCAN, + CMND_HM10_AT, + CMND_HM10_PERIOD, + CMND_HM10_BAUD, + CMND_HM10_TIME, + CMND_HM10_AUTO, + CMND_HM10_PAGE + }; + +enum HM10_awaitData: uint8_t { + none = 0, + tempHumLY = 1, + TLMF = 2, + bat = 3, + tempHumCGD1 = 4, + discScan = 5, + tempHumMJ = 6 + }; + + + + + +#define TASK_HM10_NOTASK 0 +#define TASK_HM10_ROLE1 1 +#define TASK_HM10_IMME1 2 +#define TASK_HM10_RENEW 3 +#define TASK_HM10_RESET 4 +#define TASK_HM10_DISC 5 +#define TASK_HM10_CONN 6 +#define TASK_HM10_VERSION 7 +#define TASK_HM10_NAME 8 +#define TASK_HM10_FEEDBACK 9 +#define TASK_HM10_DISCONN 10 +#define TASK_HM10_SUB_L3 11 + +#define TASK_HM10_SCAN9 13 +#define TASK_HM10_UN_L3 14 + +#define TASK_HM10_READ_BT_L3 16 +#define TASK_HM10_SUB_L2 17 +#define TASK_HM10_UN_L2 18 +#define TASK_HM10_READ_BT_L2 19 +#define TASK_HM10_TIME_L2 20 +#define TASK_HM10_SHOW0 21 +#define TASK_HM10_READ_BF_FL 22 +#define TASK_HM10_CALL_TLMF_FL 23 +#define TASK_HM10_READ_TLMF_FL 24 +#define TASK_HM10_SUB_HT_CGD1 25 +#define TASK_HM10_UN_HT_CGD1 26 +#define TASK_HM10_READ_B_CGD1 27 + +#define TASK_HM10_READ_B_MJ 29 +#define TASK_HM10_SUB_HT_MJ 30 + +#define TASK_HM10_STATUS_EVENT 32 + +#define TASK_HM10_DONE 99 + + + + + +void HM10_Launchtask(uint8_t task, uint8_t slot, uint8_t delay){ + HM10_TASK_LIST[slot][0] = task; + HM10_TASK_LIST[slot][1] = delay; + HM10_TASK_LIST[slot+1][0] = TASK_HM10_NOTASK; + HM10.current_task_delay = HM10_TASK_LIST[0][1]; +} + +void HM10_TaskReplaceInSlot(uint8_t task, uint8_t slot){ + HM10.last_command = HM10_TASK_LIST[slot][0]; + HM10_TASK_LIST[slot][0] = task; +} + +void HM10_ReverseMAC(uint8_t _mac[]){ + uint8_t _reversedMAC[6]; + for (uint8_t i=0; i<6; i++){ + _reversedMAC[5-i] = _mac[i]; + } + memcpy(_mac,_reversedMAC, sizeof(_reversedMAC)); +} + + + + + +void HM10_Reset(void) { HM10_Launchtask(TASK_HM10_DISCONN,0,1); + HM10_Launchtask(TASK_HM10_ROLE1,1,1); + HM10_Launchtask(TASK_HM10_IMME1,2,1); + HM10_Launchtask(TASK_HM10_RESET,3,1); + HM10_Launchtask(TASK_HM10_VERSION,4,10); + HM10_Launchtask(TASK_HM10_SCAN9,5,2); + HM10_Launchtask(TASK_HM10_DISC,6,2); + HM10_Launchtask(TASK_HM10_STATUS_EVENT,7,2); + } + +void HM10_Discovery_Scan(void) { + HM10_Launchtask(TASK_HM10_DISCONN,0,1); + HM10_Launchtask(TASK_HM10_DISC,1,5); + HM10_Launchtask(TASK_HM10_STATUS_EVENT,2,2); + } + +void HM10_Read_LYWSD03(void) { + HM10_Launchtask(TASK_HM10_CONN,0,1); + HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); + HM10_Launchtask(TASK_HM10_SUB_L3,2,20); + HM10_Launchtask(TASK_HM10_UN_L3,3,80); + HM10_Launchtask(TASK_HM10_READ_BT_L3,4,5); + HM10_Launchtask(TASK_HM10_DISCONN,5,5); + } + +void HM10_Read_LYWSD02(void) { + HM10_Launchtask(TASK_HM10_CONN,0,1); + HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); + HM10_Launchtask(TASK_HM10_SUB_L2,2,20); + HM10_Launchtask(TASK_HM10_UN_L2,3,80); + HM10_Launchtask(TASK_HM10_READ_BT_L2,4,5); + HM10_Launchtask(TASK_HM10_DISCONN,5,5); + } + +void HM10_Time_LYWSD02(void) { + HM10_Launchtask(TASK_HM10_DISCONN,0,0); + HM10_Launchtask(TASK_HM10_CONN,1,5); + HM10_Launchtask(TASK_HM10_FEEDBACK,2,35); + HM10_Launchtask(TASK_HM10_TIME_L2,3,20); + HM10_Launchtask(TASK_HM10_DISCONN,4,5); + } + +void HM10_Read_Flora(void) { + HM10_Launchtask(TASK_HM10_DISCONN,0,0); + HM10_Launchtask(TASK_HM10_CONN,1,1); + HM10_Launchtask(TASK_HM10_FEEDBACK,2,5); + HM10_Launchtask(TASK_HM10_READ_BF_FL,3,20); + HM10_Launchtask(TASK_HM10_CALL_TLMF_FL,4,30); + HM10_Launchtask(TASK_HM10_DISCONN,5,10); + } + +void HM10_Read_CGD1(void) { + HM10_Launchtask(TASK_HM10_CONN,0,1); + HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); + HM10_Launchtask(TASK_HM10_SUB_HT_CGD1,2,20); + HM10_Launchtask(TASK_HM10_UN_HT_CGD1,3,10); + HM10_Launchtask(TASK_HM10_READ_B_CGD1,4,5); + HM10_Launchtask(TASK_HM10_DISCONN,5,5); + } + +void HM10_Read_MJ_HT_V1(void) { + HM10_Launchtask(TASK_HM10_CONN,0,1); + HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); + HM10_Launchtask(TASK_HM10_READ_B_MJ,2,20); + HM10_Launchtask(TASK_HM10_SUB_HT_MJ,3,10); + HM10_Launchtask(TASK_HM10_DISCONN,4,5); + } +# 343 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_62_MI_HM10.ino" +uint32_t MIBLEgetSensorSlot(uint8_t (&_serial)[6], uint16_t _type){ + + DEBUG_SENSOR_LOG(PSTR("%s: will test ID-type: %x"),D_CMND_HM10, _type); + bool _success = false; + for (uint32_t i=0;i<6;i++){ + if(_type == kHM10SlaveID[i]){ + DEBUG_SENSOR_LOG(PSTR("HM10: ID is type %u"), i); + _type = i+1; + _success = true; + } + else { + DEBUG_SENSOR_LOG(PSTR("%s: ID-type is not: %x"),D_CMND_HM10,kHM10SlaveID[i]); + } + } + if(!_success) return 0xff; + + DEBUG_SENSOR_LOG(PSTR("%s: vector size %u"),D_CMND_HM10, MIBLEsensors.size()); + for(uint32_t i=0; ibegin(HM10.serialSpeed)) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s start serial communication fixed to 115200 baud"),D_CMND_HM10); + if (HM10Serial->hardwareSerial()) { + ClaimSerial(); + DEBUG_SENSOR_LOG(PSTR("%s: claim HW"),D_CMND_HM10); + } + HM10_Reset(); + HM10.mode.pending_task = 1; + HM10.mode.init = 1; + HM10.period = Settings.tele_period; + DEBUG_SENSOR_LOG(PSTR("%s_TASK_LIST initialized, now return to main loop"),D_CMND_HM10); + } + return; +} + + + + + +void HM10parseMiBeacon(char * _buf, uint32_t _slot){ + float _tempFloat; + mi_beacon_t _beacon; + if (MIBLEsensors[_slot].type==MJ_HT_V1 || MIBLEsensors[_slot].type==CGG1){ + memcpy((uint8_t*)&_beacon+1,(uint8_t*)_buf, sizeof(_beacon)); + memcpy((uint8_t*)&_beacon.Mac,(uint8_t*)&_beacon.Mac+1,6); + } + else{ + memcpy((void*)&_beacon,(void*)_buf, sizeof(_beacon)); + } + HM10_ReverseMAC(_beacon.Mac); + if(memcmp(_beacon.Mac,MIBLEsensors[_slot].serial,sizeof(_beacon.Mac))!=0){ + if (MIBLEsensors[_slot].showedUp>3) return; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: remove garbage sensor"),D_CMND_HM10); + DEBUG_SENSOR_LOG(PSTR("%s i: %x %x %x %x %x %x"),D_CMND_HM10, MIBLEsensors[_slot].serial[5], MIBLEsensors[_slot].serial[4],MIBLEsensors[_slot].serial[3],MIBLEsensors[_slot].serial[2],MIBLEsensors[_slot].serial[1],MIBLEsensors[_slot].serial[0]); + DEBUG_SENSOR_LOG(PSTR("%s n: %x %x %x %x %x %x"),D_CMND_HM10, _beacon.Mac[5], _beacon.Mac[4], _beacon.Mac[3],_beacon.Mac[2],_beacon.Mac[1],_beacon.Mac[0]); + MIBLEsensors.erase(MIBLEsensors.begin()+_slot); + return; + } + if (MIBLEsensors[_slot].showedUp<4) MIBLEsensors[_slot].showedUp++; + + DEBUG_SENSOR_LOG(PSTR("MiBeacon type:%02x: %02x %02x %02x %02x %02x %02x %02x %02x"),_beacon.type, (uint8_t)_buf[0],(uint8_t)_buf[1],(uint8_t)_buf[2],(uint8_t)_buf[3],(uint8_t)_buf[4],(uint8_t)_buf[5],(uint8_t)_buf[6],(uint8_t)_buf[7]); + DEBUG_SENSOR_LOG(PSTR(" type:%02x: %02x %02x %02x %02x %02x %02x %02x %02x"),_beacon.type, (uint8_t)_buf[8],(uint8_t)_buf[9],(uint8_t)_buf[10],(uint8_t)_buf[11],(uint8_t)_buf[12],(uint8_t)_buf[13],(uint8_t)_buf[14],(uint8_t)_buf[15]); + + if(MIBLEsensors[_slot].type==4 || MIBLEsensors[_slot].type==6){ + DEBUG_SENSOR_LOG(PSTR("LYWSD03 and CGD1 no support for MiBeacon, type %u"),MIBLEsensors[_slot].type); + return; + } + DEBUG_SENSOR_LOG(PSTR("%s at slot %u"), kHM10SlaveType[MIBLEsensors[_slot].type-1],_slot); + switch(_beacon.type){ + case 0x04: + _tempFloat=(float)(_beacon.temp)/10.0f; + if(_tempFloat<60){ + MIBLEsensors[_slot].temp=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 4: temp updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 4: U16: %u Temp"), _beacon.temp ); + break; + case 0x06: + _tempFloat=(float)(_beacon.hum)/10.0f; + if(_tempFloat<101){ + MIBLEsensors[_slot].hum=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 6: hum updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 6: U16: %u Hum"), _beacon.hum); + break; + case 0x07: + MIBLEsensors[_slot].lux=_beacon.lux & 0x00ffffff; + DEBUG_SENSOR_LOG(PSTR("Mode 7: U24: %u Lux"), _beacon.lux & 0x00ffffff); + break; + case 0x08: + _tempFloat =(float)_beacon.moist; + if(_tempFloat<100){ + MIBLEsensors[_slot].moisture=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 8: moisture updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 8: U8: %u Moisture"), _beacon.moist); + break; + case 0x09: + _tempFloat=(float)(_beacon.fert); + if(_tempFloat<65535){ + MIBLEsensors[_slot].fertility=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 9: fertility updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode 9: U16: %u Fertility"), _beacon.fert); + break; + case 0x0a: + if(_beacon.bat<101){ + MIBLEsensors[_slot].bat = _beacon.bat; + DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode a: U8: %u %%"), _beacon.bat); + break; + case 0x0d: + _tempFloat=(float)(_beacon.HT.temp)/10.0f; + if(_tempFloat<60){ + MIBLEsensors[_slot].temp = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode d: temp updated")); + } + _tempFloat=(float)(_beacon.HT.hum)/10.0f; + if(_tempFloat<100){ + MIBLEsensors[_slot].hum = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode d: hum updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode d: U16: %x Temp U16: %x Hum"), _beacon.HT.temp, _beacon.HT.hum); + break; + } +} + +void HM10ParseResponse(char *buf, uint16_t bufsize) { + if (!strncmp(buf,"OK",2)) { + DEBUG_SENSOR_LOG(PSTR("%s: got OK"),D_CMND_HM10); + } + if (!strncmp(buf,"HMSoft",6)) { + const char* _fw = "000"; + memcpy((void *)_fw,(void *)(buf+8),3); + HM10.firmware = atoi(_fw); + DEBUG_SENSOR_LOG(PSTR("%s: Firmware: %d"),D_CMND_HM10, HM10.firmware); + return; + } + char * _pos = strstr(buf, "ISA:"); + if(_pos) { + uint8_t _newMacArray[6] = {0}; + memcpy((void *)_newMacArray,(void *)(_pos+4),6); + HM10_ReverseMAC(_newMacArray); + DEBUG_SENSOR_LOG(PSTR("%s: MAC-array: %02x%02x%02x%02x%02x%02x"),D_CMND_HM10,_newMacArray[0],_newMacArray[1],_newMacArray[2],_newMacArray[3],_newMacArray[4],_newMacArray[5]); + uint16_t _type=0xffff; + + for (uint32_t idx =10;idxavailable()) { + + if(iread(); + } + i++; + success = true; + } + + if(i==0) return success; + + switch (HM10.mode.awaiting){ + case bat: + if (HM10.mode.connected) { + if (HM10readBat(ret)){ + HM10.mode.awaiting = none; + HM10.current_task_delay = 0; + } + } + break; + case tempHumLY: + if (HM10.mode.connected) HM10readHT_LY(ret); + break; + case tempHumCGD1: + if (HM10.mode.connected) HM10readHT_CGD1(ret); + break; + case TLMF: + if (HM10.mode.connected) HM10readTLMF(ret); + break; + case discScan: + if(success) { + HM10ParseResponse(ret,i); + } + break; + case tempHumMJ: + if (HM10.mode.connected) HM10readHT_MJ_HT_V1(ret); + break; + case none: + if(success) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: response: %s"),D_CMND_HM10, (char *)ret); + + + + HM10ParseResponse(ret,i); + } + break; + } + memset(ret,0,i); + return success; +} + + + + + +void HM10_TaskEvery100ms(){ + if (HM10.current_task_delay == 0) { + uint8_t i = 0; + bool runningTaskLoop = true; + while (runningTaskLoop) { + switch(HM10_TASK_LIST[i][0]) { + case TASK_HM10_ROLE1: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: set role to 1"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+ROLE1"); + break; + case TASK_HM10_IMME1: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: set imme to 1"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+IMME1"); + break; + case TASK_HM10_DISC: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: start discovery"),D_CMND_HM10); + HM10.current_task_delay = 100; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10.mode.awaiting = discScan; + HM10Serial->write("AT+DISA?"); + break; + case TASK_HM10_VERSION: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read version"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+VERR?"); + break; + case TASK_HM10_NAME: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read name"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+NAME?"); + break; + case TASK_HM10_CONN: + char _con[20]; + sprintf_P(_con,"AT+CON%02x%02x%02x%02x%02x%02x",MIBLEsensors[HM10.state.sensor].serial[0],MIBLEsensors[HM10.state.sensor].serial[1],MIBLEsensors[HM10.state.sensor].serial[2],MIBLEsensors[HM10.state.sensor].serial[3],MIBLEsensors[HM10.state.sensor].serial[4],MIBLEsensors[HM10.state.sensor].serial[5]); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: connect %s"),D_CMND_HM10, _con); + HM10.current_task_delay = 2; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write(_con); + HM10.mode.awaiting = none; + HM10.mode.connected = true; + break; + case TASK_HM10_DISCONN: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: disconnect"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT"); + break; + case TASK_HM10_RESET: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: Reset Device"),D_CMND_HM10); + HM10Serial->write("AT+RESET"); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + break; + case TASK_HM10_SUB_L3: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: subscribe"),D_CMND_HM10); + HM10.current_task_delay = 25; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + HM10.mode.awaiting = tempHumLY; + runningTaskLoop = false; + HM10Serial->write("AT+NOTIFY_ON0037"); + break; + case TASK_HM10_UN_L3: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: un-subscribe"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10.mode.awaiting = none; + HM10Serial->write("AT+NOTIFYOFF0037"); + break; + case TASK_HM10_SUB_L2: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: subscribe"),D_CMND_HM10); + HM10.current_task_delay = 25; + HM10.mode.awaiting = tempHumLY; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+NOTIFY_ON003C"); + break; + case TASK_HM10_UN_L2: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: un-subscribe"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10.mode.awaiting = none; + HM10Serial->write("AT+NOTIFYOFF003C"); + break; + case TASK_HM10_TIME_L2: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: set time"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10.time = Rtc.utc_time; + HM10Serial->write("AT+SEND_DATAWR002F"); + HM10Serial->write(HM10.timebuf,4); + HM10Serial->write(Rtc.time_timezone / 60); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s Time-string: %x%x%x%x%x"),D_CMND_HM10, HM10.timebuf[0],HM10.timebuf[1],HM10.timebuf[2],HM10.timebuf[3],(Rtc.time_timezone /60)); + break; + case TASK_HM10_READ_BT_L3: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 003A"),D_CMND_HM10); + HM10.current_task_delay = 2; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+READDATA003A?"); + HM10.mode.awaiting = bat; + break; + case TASK_HM10_READ_BT_L2: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0043"),D_CMND_HM10); + HM10.current_task_delay = 2; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+READDATA0043?"); + HM10.mode.awaiting = bat; + break; + case TASK_HM10_READ_BF_FL: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0038"),D_CMND_HM10); + HM10.current_task_delay = 2; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+READDATA0038?"); + HM10.mode.awaiting = bat; + break; + case TASK_HM10_CALL_TLMF_FL: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: write to handle 0033"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_READ_TLMF_FL,i); + runningTaskLoop = false; + HM10Serial->write("AT+SEND_DATAWR0033"); + HM10Serial->write(0xa0); + HM10Serial->write(0x1f); + HM10.mode.awaiting = none; + break; + case TASK_HM10_READ_TLMF_FL: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0035"),D_CMND_HM10); + HM10.current_task_delay = 2; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+READDATA0035?"); + HM10.mode.awaiting = TLMF; + break; + case TASK_HM10_READ_B_CGD1: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0011"),D_CMND_HM10); + HM10.current_task_delay = 2; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+READDATA0011?"); + HM10.mode.awaiting = bat; + break; + case TASK_HM10_SUB_HT_CGD1: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: subscribe 4b"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10.mode.awaiting = tempHumCGD1; + HM10Serial->write("AT+NOTIFY_ON004b"); + break; + case TASK_HM10_UN_HT_CGD1: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: un-subscribe 4b"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10.mode.awaiting = none; + HM10Serial->write("AT+NOTIFYOFF004b"); + break; + case TASK_HM10_SCAN9: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: scan time to 9"),D_CMND_HM10); + HM10.current_task_delay = 2; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+SCAN9"); + break; + case TASK_HM10_READ_B_MJ: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0x18"),D_CMND_HM10); + HM10.current_task_delay = 2; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+READDATA0018?"); + HM10.mode.awaiting = bat; + break; + case TASK_HM10_SUB_HT_MJ: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: subscribe to 0x0f"),D_CMND_HM10); + HM10.current_task_delay = 10; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+NOTIFY_ON000F"); + HM10.mode.awaiting = tempHumMJ; + break; + case TASK_HM10_FEEDBACK: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: get response"),D_CMND_HM10); + HM10SerialHandleFeedback(); + HM10.current_task_delay = HM10_TASK_LIST[i+1][1];; + HM10_TASK_LIST[i][0] = TASK_HM10_DONE; + runningTaskLoop = false; + break; + case TASK_HM10_STATUS_EVENT: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: show status"),D_CMND_HM10); + HM10StatusInfo(); + HM10.current_task_delay = HM10_TASK_LIST[i+1][1];; + HM10_TASK_LIST[i][0] = TASK_HM10_DONE; + runningTaskLoop = false; + break; + case TASK_HM10_DONE: + + + if(HM10_TASK_LIST[i+1][0] == TASK_HM10_NOTASK) { + DEBUG_SENSOR_LOG(PSTR("%sno Tasks left"),D_CMND_HM10); + DEBUG_SENSOR_LOG(PSTR("%sHM10_TASK_DONE current slot %u"),D_CMND_HM10, i); + for (uint8_t j = 0; j < HM10_MAX_TASK_NUMBER+1; j++) { + DEBUG_SENSOR_LOG(PSTR("%sHM10_TASK cleanup slot %u"),D_CMND_HM10, j); + HM10_TASK_LIST[j][0] = TASK_HM10_NOTASK; + HM10_TASK_LIST[j][1] = 0; + } + runningTaskLoop = false; + HM10.mode.pending_task = 0; + break; + } + } + i++; + } + } + else { + HM10.current_task_delay--; + } +} + +void HM10StatusInfo(){ + char stemp[20]; + snprintf_P(stemp, sizeof(stemp),PSTR("{%s:{\"found\": %u}}"),D_CMND_HM10, MIBLEsensors.size()); + AddLog_P2(LOG_LEVEL_INFO, stemp); + RulesProcessEvent(stemp); +} + + + + + + +void HM10EverySecond(bool restart){ + static uint32_t _counter = 0; + static uint32_t _nextSensorSlot = 0; + static uint32_t _lastDiscovery = 0; + + if(restart){ + _counter = 0; + _lastDiscovery = 0; + return; + } + + if(HM10.firmware == 0) return; + if(HM10.mode.pending_task == 1) return; + if(MIBLEsensors.size()==0 && !HM10.mode.autoScan) return; + + if((HM10.period-_counter)>15 && HM10.mode.autoScan) { + if(_counter-_lastDiscovery>HM10.autoScanInterval){ + HM10_Discovery_Scan(); + HM10.mode.pending_task = 1; + _counter+=12; + _lastDiscovery = _counter; + return; + } + } + + if(_counter==0) { + HM10.state.sensor = _nextSensorSlot; + _nextSensorSlot++; + HM10.mode.pending_task = 1; + switch(MIBLEsensors[HM10.state.sensor].type){ + case FLORA: + HM10_Read_Flora(); + break; + case MJ_HT_V1: case CGG1: + HM10_Read_MJ_HT_V1(); + break; + case LYWSD02: + HM10_Read_LYWSD02(); + break; + case LYWSD03MMC: + HM10_Read_LYWSD03(); + break; + case CGD1: + HM10_Read_CGD1(); + break; + default: + HM10.mode.pending_task = 0; + } + if (HM10.state.sensor==MIBLEsensors.size()-1) { + _nextSensorSlot= 0; + _counter++; + } + DEBUG_SENSOR_LOG(PSTR("%s: active sensor now: %u"),D_CMND_HM10, HM10.state.sensor); + } + else _counter++; + if (_counter>HM10.period) { + _counter = 0; + _lastDiscovery = 0; + } +} + + + + + +bool HM10Cmd(void) { + char command[CMDSZ]; + bool serviced = true; + uint8_t disp_len = strlen(D_CMND_HM10); + + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_HM10), disp_len)) { + uint32_t command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kHM10_Commands); + switch (command_code) { + case CMND_HM10_PERIOD: + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload==1) { + HM10EverySecond(true); + XdrvMailbox.payload = HM10.period; + } + else { + HM10.period = XdrvMailbox.payload; + } + } + else { + XdrvMailbox.payload = HM10.period; + } + Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_HM10_AUTO: + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload>0) { + HM10.mode.autoScan = 1; + HM10.autoScanInterval = XdrvMailbox.payload; + } + else { + HM10.mode.autoScan = 0; + HM10.autoScanInterval = 0; + } + } + else { + XdrvMailbox.payload = HM10.autoScanInterval; + } + Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_HM10_BAUD: + if (XdrvMailbox.data_len > 0) { + HM10.serialSpeed = XdrvMailbox.payload; + HM10Serial->begin(HM10.serialSpeed); + } + else { + XdrvMailbox.payload = HM10.serialSpeed; + } + Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_HM10_TIME: + if (XdrvMailbox.data_len > 0) { + if(MIBLEsensors.size()>XdrvMailbox.payload){ + if(MIBLEsensors[XdrvMailbox.payload].type == LYWSD02){ + HM10.state.sensor = XdrvMailbox.payload; + HM10_Time_LYWSD02(); + } + } + } + Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_HM10_PAGE: + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload == 0) XdrvMailbox.payload = HM10.perPage; + HM10.perPage = XdrvMailbox.payload; + } + else XdrvMailbox.payload = HM10.perPage; + Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_HM10_AT: + HM10Serial->write("AT"); + if (strlen(XdrvMailbox.data)!=0) { + HM10Serial->write("+"); + HM10Serial->write(XdrvMailbox.data); + Response_P(S_JSON_HM10_COMMAND, ":AT+",XdrvMailbox.data); + } + else Response_P(S_JSON_HM10_COMMAND, ":AT",XdrvMailbox.data); + break; + case CMND_HM10_DISC_SCAN: + HM10_Discovery_Scan(); + Response_P(S_JSON_HM10_COMMAND, command, ""); + break; + default: + + serviced = false; + break; + } + } else { + return false; + } + return serviced; +} + + + + + + +const char HTTP_HM10[] PROGMEM = "{s}HM10 V%u{m}%u%s / %u{e}"; +const char HTTP_HM10_SERIAL[] PROGMEM = "{s}%s %s{m}%02x:%02x:%02x:%02x:%02x:%02x%{e}"; +const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u%%{e}"; +const char HTTP_HM10_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%uus/cm{e}"; +const char HTTP_HM10_HL[] PROGMEM = "{s}
{m}
{e}"; + +void HM10Show(bool json) +{ + if (json) { + for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { + char slave[33]; + sprintf_P(slave,"%s-%02x%02x%02x",kHM10SlaveType[MIBLEsensors[i].type-1],MIBLEsensors[i].serial[3],MIBLEsensors[i].serial[4],MIBLEsensors[i].serial[5]); + ResponseAppend_P(PSTR(",\"%s\":{"),slave); + if (MIBLEsensors[i].type==FLORA){ + if(!isnan(MIBLEsensors[i].temp)){ + char temperature[FLOATSZ]; + dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, temperature); + ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), temperature); + } + else { + ResponseAppend_P(PSTR("}")); + continue; + } + if(MIBLEsensors[i].lux!=0x0ffffff){ + ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%u"), MIBLEsensors[i].lux); + } + if(!isnan(MIBLEsensors[i].moisture)){ + ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%d"), MIBLEsensors[i].moisture); + } + if(!isnan(MIBLEsensors[i].fertility)){ + ResponseAppend_P(PSTR(",\"Fertility\":%d"), MIBLEsensors[i].fertility); + } + } + if (MIBLEsensors[i].type>FLORA){ + if(!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp)){ + ResponseAppendTHD(MIBLEsensors[i].temp, MIBLEsensors[i].hum); + } + } + if(MIBLEsensors[i].bat!=0x00){ + ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors[i].bat); + } + ResponseAppend_P(PSTR("}")); + } +#ifdef USE_WEBSERVER + } else { + static uint16_t _page = 0; + static uint16_t _counter = 0; + int32_t i = _page * HM10.perPage; + uint32_t j = i + HM10.perPage; + if (j+1>MIBLEsensors.size()){ + j = MIBLEsensors.size(); + } + char stemp[5] ={0}; + if (MIBLEsensors.size()-(_page*HM10.perPage)>1 && HM10.perPage!=1) { + sprintf_P(stemp,"-%u",j); + } + if (MIBLEsensors.size()==0) i=-1; + + WSContentSend_PD(HTTP_HM10, HM10.firmware, i+1,stemp,MIBLEsensors.size()); + for (i; iFLORA){ + if(!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp)){ + WSContentSend_THD(kHM10SlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].temp, MIBLEsensors[i].hum); + } + } + if(MIBLEsensors[i].bat!=0x00){ + WSContentSend_PD(HTTP_BATTERY, kHM10SlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].bat); + } + } + _counter++; + if(_counter>3) { + _page++; + _counter=0; + } + if(MIBLEsensors.size()%HM10.perPage==0 && _page==MIBLEsensors.size()/HM10.perPage) _page=0; + if(_page>MIBLEsensors.size()/HM10.perPage) _page=0; +#endif + } +} + + + + + +bool Xsns62(uint8_t function) +{ + bool result = false; + + if ((pin[GPIO_HM10_RX] < 99) && (pin[GPIO_HM10_TX] < 99)) { + switch (function) { + case FUNC_INIT: + HM10SerialInit(); + break; + case FUNC_EVERY_50_MSECOND: + HM10SerialHandleFeedback(); + break; + case FUNC_EVERY_100_MSECOND: + if (HM10_TASK_LIST[0][0] != TASK_HM10_NOTASK) { + HM10_TaskEvery100ms(); + } + break; + case FUNC_EVERY_SECOND: + HM10EverySecond(false); + break; + case FUNC_COMMAND: + result = HM10Cmd(); + break; + case FUNC_JSON_APPEND: + HM10Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + HM10Show(0); + break; +#endif + } + } + return result; +} +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_63_aht1x.ino" +# 21 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_63_aht1x.ino" +#ifdef USE_I2C +#ifdef USE_AHT1x +# 38 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_63_aht1x.ino" +#define XSNS_63 63 +#define XI2C_43 43 + +#define AHT1X_ADDR1 0x38 +#define AHT1X_ADDR2 0x39 + +#define AHT1X_MAX_SENSORS 2 + +#define AHT_HUMIDITY_CONST 100 +#define AHT_TEMPERATURE_CONST 200 +#define AHT_TEMPERATURE_OFFSET 50 +#define KILOBYTE_CONST 1048576.0f + +#define AHT1X_CMD_DELAY 40 +#define AHT1X_RST_DELAY 30 +#define AHT1X_MEAS_DELAY 80 + +uint8_t AHTSetCalCmd[3] = {0xE1, 0x08, 00}; +uint8_t AHTSetCycleCmd[3] = {0xE1, 0x28, 00}; +uint8_t AHTMeasureCmd[3] = {0xAC, 0x33, 00}; +uint8_t AHTResetCmd = 0xBA; + +const char ahtTypes[] PROGMEM = "AHT1X|AHT1X"; +uint8_t aht1x_addresses[] = { AHT1X_ADDR1, AHT1X_ADDR2 }; +uint8_t aht1x_count = 0; +uint8_t aht1x_Pcount = 0; + +struct AHT1XSTRUCT +{ + float humidity = NAN; + float temperature = NAN; + uint8_t address; + char types[6]; +} aht1x_sensors[AHT1X_MAX_SENSORS]; + +bool AHT1XWrite(uint8_t aht1x_idx) +{ + Wire.beginTransmission(aht1x_sensors[aht1x_idx].address); + Wire.write(AHTMeasureCmd, 3); + if (Wire.endTransmission() != 0) + return false; +} + +bool AHT1XRead(uint8_t aht1x_idx) +{ + uint8_t data[6]; + Wire.requestFrom(aht1x_sensors[aht1x_idx].address, (uint8_t) 6); + for(uint8_t i = 0; Wire.available() > 0; i++){ + data[i] = Wire.read(); + } + if ((data[0] & 0x80) == 0x80) return false; + + aht1x_sensors[aht1x_idx].humidity = (((data[1] << 12)| (data[2] << 4) | data[3] >> 4) * AHT_HUMIDITY_CONST / KILOBYTE_CONST); + aht1x_sensors[aht1x_idx].temperature = ((AHT_TEMPERATURE_CONST * (((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5]) / KILOBYTE_CONST) - AHT_TEMPERATURE_OFFSET); + + return (!isnan(aht1x_sensors[aht1x_idx].temperature) && !isnan(aht1x_sensors[aht1x_idx].humidity) && (aht1x_sensors[aht1x_idx].humidity != 0)); +} + + + + + +void AHT1XPoll(void) +{ + aht1x_Pcount++; + switch (aht1x_Pcount) { + case 10: + AHT1XWrite(0); + break; + case 11: + AHT1XRead(0); + aht1x_Pcount = 0; + break; + } +} + +unsigned char AHT1XReadStatus(uint8_t aht1x_address) +{ + uint8_t result = 0; + Wire.requestFrom(aht1x_address, (uint8_t) 1); + result = Wire.read(); + return result; +} + +void AHT1XReset(uint8_t aht1x_address) +{ + Wire.beginTransmission(aht1x_address); + Wire.write(AHTResetCmd); + Wire.endTransmission(); + delay(AHT1X_RST_DELAY); +} + + +bool AHT1XInit(uint8_t aht1x_address) +{ + Wire.beginTransmission(aht1x_address); + Wire.write(AHTSetCalCmd, 3); + if (Wire.endTransmission() != 0) return false; + delay(AHT1X_CMD_DELAY); + if((AHT1XReadStatus(aht1x_address) & 0x68) == 0x08) + return true; + return false; +} + +void AHT1XDetect(void) +{ + for (uint8_t i = 0; i < AHT1X_MAX_SENSORS; i++) { + if (I2cActive(aht1x_addresses[i])) { continue; } + if (AHT1XInit(aht1x_addresses[i])) + { + aht1x_sensors[aht1x_count].address = aht1x_addresses[i]; + GetTextIndexed(aht1x_sensors[aht1x_count].types, sizeof(aht1x_sensors[aht1x_count].types), i, ahtTypes); + I2cSetActiveFound(aht1x_sensors[aht1x_count].address, aht1x_sensors[aht1x_count].types); + aht1x_count = 1; + break; + } + } +} + +void AHT1XShow(bool json) +{ + for (uint32_t i = 0; i < aht1x_count; i++) { + float tem = ConvertTemp(aht1x_sensors[i].temperature); + float hum = ConvertHumidity(aht1x_sensors[i].humidity); + char types[11]; + snprintf_P(types, sizeof(types), PSTR("%s%c0x%02X"), aht1x_sensors[i].types, IndexSeparator(), aht1x_sensors[i].address); + TempHumDewShow(json, ((0 == tele_period) && (0 == i)), types, tem, hum); + } +} + + + + + +bool Xsns63(uint8_t function) +{ + if (!I2cEnabled(XI2C_43)) { return false; } + bool result = false; + + if (FUNC_INIT == function) { + AHT1XDetect(); + } + else if (aht1x_count){ + switch (function) { + case FUNC_EVERY_100_MSECOND: + AHT1XPoll(); + break; + case FUNC_JSON_APPEND: + AHT1XShow(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + AHT1XShow(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_64_hrxl.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_64_hrxl.ino" +#ifdef USE_HRXL + + + + + + + +#define XSNS_64 64 + +#include + +#define HRXL_READ_TIMEOUT 400 + +TasmotaSerial *HRXLSerial = nullptr; + +uint32_t hrxl_distance_mm = 0; +bool hrxl_found = false; + + + +void HRXLInit(void) +{ + hrxl_found = false; + if ((pin[GPIO_HRXL_RX] < 99)) + { + HRXLSerial = new TasmotaSerial(pin[GPIO_HRXL_RX], -1, 1); + if (HRXLSerial->begin(9600)) + { + if (HRXLSerial->hardwareSerial()) + ClaimSerial(); + hrxl_found = true; + HRXLSerial->setTimeout(HRXL_READ_TIMEOUT); + } + } +} + +void HRXLEverySecond(void) +{ + if (!hrxl_found) + return; + + int num_read=0; + int sum=0; + while (HRXLSerial->available()>5) + { + if (HRXLSerial->read() != 'R') + continue; + + int d = HRXLSerial->parseInt(); + if (d >= 30 && d<=5000) + { + sum += d; + num_read++; + } + } + if (num_read>1) + hrxl_distance_mm = int(sum / num_read); + +} + + +void HRXLShow(bool json) +{ + char types[5] = "HRXL"; + if (json) + { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_DISTANCE "\":%d}"), types, hrxl_distance_mm); +#ifdef USE_WEBSERVER + } + else + { + WSContentSend_PD(HTTP_SNS_RANGE, types, hrxl_distance_mm); +#endif + } +} + + + + + +bool Xsns64(uint8_t function) +{ + if (pin[GPIO_HRXL_RX] >= 99) + return false; + + switch (function) + { + case FUNC_INIT: + HRXLInit(); + break; + case FUNC_EVERY_SECOND: + HRXLEverySecond(); + break; + case FUNC_JSON_APPEND: + HRXLShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + HRXLShow(0); + break; +#endif + } + return false; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_65_hdc1080.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_65_hdc1080.ino" +#ifdef USE_I2C +#ifdef USE_HDC1080 +# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_65_hdc1080.ino" +#define XSNS_65 65 +#define XI2C_45 45 + +#define HDC1080_ADDR 0x40 + + + +#define HDC_REG_TEMP 0x00 +#define HDC_REG_RH 0x01 +#define HDC_REG_CONFIG 0x02 +#define HDC_REG_SERIAL1 0xFB +#define HDC_REG_SERIAL2 0xFC +#define HDC_REG_SERIAL3 0xFD +#define HDC_REG_MAN_ID 0xFE +#define HDC_REG_DEV_ID 0xFF + + + +#define HDC1080_MAN_ID 0x5449 +#define HDC1080_DEV_ID 0x1050 + + + +#define HDC1080_RST_ON 0x8000 +#define HDC1080_HEAT_ON 0x2000 +#define HDC1080_MODE_ON 0x1000 +#define HDC1080_TRES_11 0x400 +#define HDC1080_HRES_11 0x100 +#define HDC1080_HRES_8 0x80 + + + +#define HDC1080_CONV_TIME 15 +#define HDC1080_TEMP_MULT 0.0025177 +#define HDC1080_RH_MULT 0.0025177 +#define HDC1080_TEMP_OFFSET 40.0 + +const char* hdc_type_name = "HDC1080"; +uint16_t hdc_manufacturer_id = 0; +uint16_t hdc_device_id = 0; + +float hdc_temperature = 0.0; +float hdc_humidity = 0.0; + +uint8_t hdc_valid = 0; + +bool is_reading = false; +uint32_t hdc_next_read; + + + + + +uint16_t HdcReadDeviceId(void) { + return I2cRead16(HDC1080_ADDR, HDC_REG_DEV_ID); +} + + + + + +uint16_t HdcReadManufacturerId(void) { + return I2cRead16(HDC1080_ADDR, HDC_REG_MAN_ID); +} + + + + +void HdcConfig(uint16_t config) { + I2cWrite16(HDC1080_ADDR, HDC_REG_CONFIG, config); +} + + + + + + + +void HdcReset(void) { + uint16_t current = I2cRead16(HDC1080_ADDR, HDC_REG_CONFIG); + + + + + current |= 0x8000; + + I2cWrite16(HDC1080_ADDR, HDC_REG_CONFIG, current); + + delay(HDC1080_CONV_TIME); +} +# 132 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_65_hdc1080.ino" +int8_t HdcTransactionOpen(uint8_t addr, uint8_t reg) { + Wire.beginTransmission((uint8_t) addr); + Wire.write((uint8_t) reg); + return Wire.endTransmission(); +} +# 147 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_65_hdc1080.ino" +int8_t HdcTransactionClose(uint8_t addr, uint8_t *reg_data, uint16_t len) { + if (len != Wire.requestFrom((uint8_t) addr, (uint8_t) len)) { + return 1; + } + + while (len--) { + *reg_data = (uint8_t)Wire.read(); + reg_data++; + } + + return 0; +} + + + + + +void HdcInit(void) { + HdcReset(); + HdcConfig(HDC1080_MODE_ON); +} + + + + + +bool HdcTriggerRead(void) { + int8_t status = HdcTransactionOpen(HDC1080_ADDR, HDC_REG_TEMP); + + hdc_next_read = millis() + HDC1080_CONV_TIME; + + if(status) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HdcTriggerRead: failed to open the transaction for HDC_REG_TEMP. Status = %d"), status); + + return false; + } + + is_reading = true; + + return true; +} +# 197 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_65_hdc1080.ino" +bool HdcRead(void) { + int8_t status = 0; + uint8_t sensor_data[4]; + uint16_t temp_data = 0; + uint16_t rh_data = 0; + + is_reading = false; + + status = HdcTransactionClose(HDC1080_ADDR, sensor_data, 4); + + if(status) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HdcRead: failed to read HDC_REG_TEMP. Status = %d"), status); + + return false; + } + + temp_data = (uint16_t) ((sensor_data[0] << 8) | sensor_data[1]); + rh_data = (uint16_t) ((sensor_data[2] << 8) | sensor_data[3]); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HdcRead: temperature raw data: 0x%04x; humidity raw data: 0x%04x"), temp_data, rh_data); + + + + hdc_temperature = ConvertTemp(HDC1080_TEMP_MULT * (float) (temp_data) - HDC1080_TEMP_OFFSET); + + hdc_humidity = HDC1080_RH_MULT * (float) (rh_data); + + if (hdc_humidity > 100) { hdc_humidity = 100.0; } + if (hdc_humidity < 0) { hdc_humidity = 0.01; } + hdc_humidity = ConvertHumidity(hdc_humidity); + + hdc_valid = SENSOR_MAX_MISS; + + return true; +} + + + + + + +void HdcDetect(void) { + if (I2cActive(HDC1080_ADDR)) { + + + return; + } + + hdc_manufacturer_id = HdcReadManufacturerId(); + hdc_device_id = HdcReadDeviceId(); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HdcDetect: detected device with manufacturerId = 0x%04X and deviceId = 0x%04X"), hdc_manufacturer_id, hdc_device_id); + + if (hdc_device_id == HDC1080_DEV_ID) { + HdcInit(); + I2cSetActiveFound(HDC1080_ADDR, hdc_type_name); + } +} + + + + + + +void HdcEverySecond(void) { + if (uptime &1) { + if (!HdcTriggerRead()) { + AddLogMissed((char*) hdc_type_name, hdc_valid); + } + } +} + + + + + + +void HdcShow(bool json) { + if (hdc_valid) { + TempHumDewShow(json, (0 == tele_period), hdc_type_name, hdc_temperature, hdc_humidity); + } +} + + + + + +bool Xsns65(uint8_t function) +{ + if (!I2cEnabled(XI2C_45)) { + + + return false; + } + + bool result = false; + + if (FUNC_INIT == function) { + HdcDetect(); + } + else if (hdc_device_id) { + switch (function) { + case FUNC_EVERY_50_MSECOND: + if(is_reading && TimeReached(hdc_next_read)) { + if(!HdcRead()) { + AddLogMissed((char*) hdc_type_name, hdc_valid); + } + } + break; + case FUNC_EVERY_SECOND: + HdcEverySecond(); + break; + case FUNC_JSON_APPEND: + HdcShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + HdcShow(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_66_iAQ.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_66_iAQ.ino" +#ifdef USE_I2C +#ifdef USE_IAQ + +#define XSNS_66 66 +#define XI2C_46 46 + +#define I2_ADR_IAQ 0x5a + +#define IAQ_STATUS_OK 0x00 +#define IAQ_STATUS_BUSY 0x01 +#define IAQ_STATUS_WARM 0x10 +#define IAQ_STATUS_ERR 0x80 +#define IAQ_STATUS_I2C_ERR 0xFF + +struct { + int32_t resistance; + uint16_t pred; + uint16_t Tvoc; + uint8_t status; + bool ready; +} iAQ; + +void IAQ_Init(void) +{ + if (!I2cSetDevice(I2_ADR_IAQ)) { return; } + I2cSetActiveFound(I2_ADR_IAQ, "IAQ"); + iAQ.ready = true; +} + +void IAQ_Read(void) +{ + uint8_t buf[9]; + buf[2] = IAQ_STATUS_I2C_ERR; + Wire.requestFrom((uint8_t)I2_ADR_IAQ,sizeof(buf)); + for( uint32_t i=0; i<9; i++ ) { + buf[i]= Wire.read(); + } + + iAQ.pred = (buf[0]<<8) + buf[1]; + iAQ.status = buf[2]; + iAQ.resistance = ((uint32_t)buf[3]<<24) + ((uint32_t)buf[4]<<16) + ((uint32_t)buf[5]<<8) + (uint32_t)buf[6]; + iAQ.Tvoc = (buf[7]<<8) + buf[8]; +} + + + + + +#ifdef USE_WEBSERVER +const char HTTP_SNS_IAQ[] PROGMEM = + "{s}iAQ-Core " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}" + "{s}iAQ-Core " D_TVOC "{m}%d " D_UNIT_PARTS_PER_BILLION "{e}"; + +const char HTTP_SNS_IAQ_ERROR[] PROGMEM = + "{s}iAQ-Core {m} %s {e}"; +#endif + +void IAQ_Show(uint8_t json) +{ + IAQ_Read(); + + if (json) { + if (iAQ.status!=IAQ_STATUS_OK){ + AddLog_P2(LOG_LEVEL_INFO, PSTR("iAQ: " D_ERROR " %x" ),iAQ.status); + return; + } + else { + ResponseAppend_P(PSTR(",\"IAQ\":{\"" D_JSON_ECO2 "\":%u,\"" D_JSON_TVOC "\":%u,\"" D_JSON_RESISTANCE "\":%u}"), iAQ.pred, iAQ.Tvoc, iAQ.resistance); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, iAQ.pred); +#endif + } +#ifdef USE_WEBSERVER + } else { + switch(iAQ.status){ + case IAQ_STATUS_OK: + WSContentSend_PD(HTTP_SNS_IAQ, iAQ.pred, iAQ.Tvoc); + break; + case IAQ_STATUS_WARM: + WSContentSend_PD(HTTP_SNS_IAQ_ERROR, D_START); + break; + default: + WSContentSend_PD(HTTP_SNS_IAQ_ERROR, D_ERROR); + } +#endif + } +} + + + + + +bool Xsns66(byte function) +{ + if (!I2cEnabled(XI2C_46)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + IAQ_Init(); + } + else if (iAQ.ready) { + switch (function) { + case FUNC_JSON_APPEND: + IAQ_Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + IAQ_Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_67_as3935.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_67_as3935.ino" +#ifdef USE_I2C +#ifdef USE_AS3935 + + + + + + +#define XSNS_67 67 +#define XI2C_48 48 + +#define D_NAME_AS3935 "AS3935" +#define AS3935_ADDR 0x03 + + +#define IRQ_TBL 0x03, 0x0F, 0 +#define ENERGY_RAW_1 0x04, 0xFF, 0 +#define ENERGY_RAW_2 0x05, 0xFF, 0 +#define ENERGY_RAW_3 0x06, 0x1F, 0 +#define LGHT_DIST 0x07, 0x3F, 0 +#define DISP_TRCO 0x08, 0x20, 5 +#define DISP_LCO 0x08, 0x80, 7 +#define TUNE_CAPS 0x08, 0x0F, 0 +#define AFE_GB 0x00, 0x3E, 0 +#define WDTH 0x01, 0x0F, 0 +#define NF_LEVEL 0x01, 0x70, 4 +#define SPIKE_REJECT 0x02, 0x0F, 0 +#define MIN_NUM_LIGH 0x02, 0x30, 4 +#define DISTURBER 0x03, 0x20, 5 +#define LCO_FDIV 0x03, 0xC0, 6 + +#define INDOORS 0x24 +#define OUTDOORS 0x1C + + + +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" + +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_NOMESS "listening..." + +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + + +const char HTTP_SNS_UNIT_KILOMETER[] PROGMEM = D_UNIT_KILOMETER; + +const char HTTP_SNS_AS3935_ENERGY[] PROGMEM = "{s}" D_NAME_AS3935 " " D_AS3935_ENERGY " {m}%d{e}"; +const char HTTP_SNS_AS3935_DISTANZ[] PROGMEM = "{s}" D_NAME_AS3935 " " D_AS3935_DISTANCE " {m}%u " D_UNIT_KILOMETER "{e}"; +const char HTTP_SNS_AS3935_VRMS[] PROGMEM = "{s}" D_NAME_AS3935 " " D_AS3935_VRMS "{m}%#4u (%d){e}"; + +const char HTTP_SNS_AS3935_OUTDOORS[] PROGMEM = "{s}%s " D_AS3935_GAIN " {m}" D_AS3935_OUTDOORS " {e}"; +const char HTTP_SNS_AS3935_INDOORS[] PROGMEM = "{s}%s " D_AS3935_GAIN " {m}" D_AS3935_INDOORS " {e}"; +const char* const HTTP_SNS_AS3935_GAIN[] PROGMEM = {HTTP_SNS_AS3935_INDOORS, HTTP_SNS_AS3935_OUTDOORS}; + +const char HTTP_SNS_AS3935_DIST_ON[] PROGMEM = "{s}%s " D_AS3935_DISTURBER " {m}" D_AS3935_ON " {e}"; +const char HTTP_SNS_AS3935_DIST_OFF[] PROGMEM = "{s}%s " D_AS3935_DISTURBER " {m}" D_AS3935_OFF " {e}"; +const char* const HTTP_SNS_AS3935_DISTURBER[] PROGMEM = {HTTP_SNS_AS3935_DIST_OFF, HTTP_SNS_AS3935_DIST_ON}; + +const char HTTP_SNS_AS3935_EMPTY[] PROGMEM = "{s}%s: " D_AS3935_NOMESS "{e}"; +const char HTTP_SNS_AS3935_OUT[] PROGMEM = "{s}%s: " D_AS3935_OUT "{e}"; +const char HTTP_SNS_AS3935_NOT[] PROGMEM = "{s}%s: " D_AS3935_NOT "{e}"; +const char HTTP_SNS_AS3935_ABOVE[] PROGMEM = "{s}%s: " D_AS3935_ABOVE "{e}"; +const char HTTP_SNS_AS3935_NOISE[] PROGMEM = "{s}%s: " D_AS3935_NOISE "{e}"; +const char HTTP_SNS_AS3935_DISTURB[] PROGMEM = "{s}%s: " D_AS3935_DISTDET "{e}"; +const char HTTP_SNS_AS3935_INTNOEV[] PROGMEM = "{s}%s: " D_AS3935_INTNOEV "{e}"; +const char HTTP_SNS_AS3935_MSG[] PROGMEM = "{s}%s: " D_AS3935_LIGHT " " D_AS3935_APRX " %d " D_UNIT_KILOMETER " " D_AS3935_AWAY "{e}"; +const char* const HTTP_SNS_AS3935_TABLE_1[] PROGMEM = { HTTP_SNS_AS3935_EMPTY, HTTP_SNS_AS3935_MSG, HTTP_SNS_AS3935_OUT, HTTP_SNS_AS3935_NOT, HTTP_SNS_AS3935_ABOVE, HTTP_SNS_AS3935_NOISE, HTTP_SNS_AS3935_DISTURB, HTTP_SNS_AS3935_INTNOEV }; + +const char JSON_SNS_AS3935_EVENTS[] PROGMEM = ",\"%s\":{\"" D_JSON_EVENT "\":%d,\"" D_JSON_DISTANCE "\":%d,\"" D_JSON_ENERGY "\":%u}"; + +const char* const S_JSON_AS3935_COMMAND_ONOFF[] PROGMEM = {"\"" D_AS3935_OFF "\"","\"" D_AS3935_ON"\""}; +const char* const S_JSON_AS3935_COMMAND_GAIN[] PROGMEM = {"\"" D_AS3935_INDOORS "\"", "\"" D_AS3935_OUTDOORS "\""}; +const char* const S_JSON_AS3935_COMMAND_CAL[] PROGMEM = {"" D_AS3935_CAL_FAIL "","" D_AS3935_CAL_OK ""}; + +const char S_JSON_AS3935_COMMAND_STRING[] PROGMEM = "{\"" D_NAME_AS3935 "\":{\"%s\":%s}}"; +const char S_JSON_AS3935_COMMAND_NVALUE[] PROGMEM = "{\"" D_NAME_AS3935 "\":{\"%s\":%d}}"; +const char S_JSON_AS3935_COMMAND_SETTINGS[] PROGMEM = "{\"" D_NAME_AS3935 "\":{\"Gain\":%s,\"NFfloor\":%d,\"uVrms\":%d,\"Tunecaps\":%d,\"MinNumLight\":%d,\"Rejektion\":%d,\"Wdthreshold\":%d,\"MinNFstage\":%d,\"NFAutoTime\":%d,\"DisturberAutoTime\":%d,\"Disturber\":%s,\"NFauto\":%s,\"Disturberauto\":%s,\"NFautomax\":%s,\"Mqttlightevent\":%s}}"; + +const char kAS3935_Commands[] PROGMEM = "setnf|setminstage|setml|default|setgain|settunecaps|setrej|setwdth|disttime|nftime|disturber|autonf|autodisturber|autonfmax|mqttevent|settings|calibrate"; + +enum AS3935_Commands { + CMND_AS3935_SET_NF, + CMND_AS3935_SET_MINNF, + CMND_AS3935_SET_MINLIGHT, + CMND_AS3935_SET_DEF, + CMND_AS3935_SET_GAIN, + CMND_AS3935_SET_TUNE, + CMND_AS3935_SET_REJ, + CMND_AS3935_SET_WDTH, + CMND_AS3935_DISTTIME, + CMND_AS3935_NFTIME, + CMND_AS3935_SET_DISTURBER, + CMND_AS3935_NF_AUTOTUNE, + CMND_AS3935_DIST_AUTOTUNE, + CMND_AS3935_NF_ATUNE_BOTH, + CMND_AS3935_MQTT_LIGHT_EVT, + CMND_AS3935_SETTINGS, + CMND_AS3935_CALIBRATE + }; + +struct AS3935STRUCT +{ + bool autodist_activ = false; + volatile bool detected = false; + volatile bool dispLCO = 0; + uint8_t icount = 0; + uint8_t irq = 0; + uint8_t mqtt_irq = 0; + uint8_t http_irq = 0; + uint8_t http_count_start = 0; + int16_t http_distance = 0; + int16_t distance = 0; + uint16_t http_timer = 0; + uint16_t http_count = 0; + uint16_t nftimer = 0; + uint16_t disttimer = 0; + uint32_t intensity = 0; + uint32_t http_intensity = 0; + volatile uint32_t pulse = 0; +} as3935_sensor; + +uint8_t as3935_active = 0; + +void ICACHE_RAM_ATTR AS3935Isr() { + as3935_sensor.detected = true; +} + +uint8_t AS3935ReadRegister(uint8_t reg, uint8_t mask, uint8_t shift) { + uint8_t data = I2cRead8(AS3935_ADDR, reg); + if (reg == 0x08) Settings.as3935_sensor_cfg[4] = data; + if (reg < 0x04) Settings.as3935_sensor_cfg[reg] = data; + return ((data & mask) >> shift); +} + +void AS3935WriteRegister(uint8_t reg, uint8_t mask, uint8_t shift, uint8_t data) { + uint8_t currentReg = I2cRead8(AS3935_ADDR, reg); + currentReg &= (~mask); + data <<= shift; + data &= mask; + data |= currentReg; + I2cWrite8(AS3935_ADDR, reg, data); + if (reg == 0x08) Settings.as3935_sensor_cfg[4] = I2cRead8(AS3935_ADDR, reg); + if (reg < 0x04) Settings.as3935_sensor_cfg[reg] = I2cRead8(AS3935_ADDR, reg); +} + + + +void ICACHE_RAM_ATTR AS3935CountFreq() { + if (as3935_sensor.dispLCO) + as3935_sensor.pulse++; +} + +bool AS3935AutoTuneCaps(uint8_t irqpin) { + int32_t maxtune = 17500; + uint8_t besttune; + AS3935WriteRegister(LCO_FDIV, 0); + delay(2); + for (uint8_t tune = 0; tune < 16; tune++) { + AS3935WriteRegister(TUNE_CAPS, tune); + delay(2); + AS3935WriteRegister(DISP_LCO,1); + delay(1); + as3935_sensor.dispLCO = true; + as3935_sensor.pulse = 0; + attachInterrupt(digitalPinToInterrupt(irqpin), AS3935CountFreq, RISING); + delay(200); + as3935_sensor.dispLCO = false; + detachInterrupt(irqpin); + AS3935WriteRegister(DISP_LCO,0); + int32_t currentfreq = 500000 - ((as3935_sensor.pulse * 5) * 16); + if(currentfreq < 0) currentfreq = -currentfreq; + if(maxtune > currentfreq) { + maxtune = currentfreq; + besttune = tune; + } + } + if (maxtune >= 17500) + return false; + AS3935SetTuneCaps(besttune); + return true; +} + + + +void AS3935CalibrateRCO() { + I2cWrite8(AS3935_ADDR, 0x3D, 0x96); + AS3935WriteRegister(DISP_TRCO, 1); + delay(2); + AS3935WriteRegister(DISP_TRCO, 0); +} + +uint8_t AS3935TransMinLights(uint8_t min_lights) { + if (5 > min_lights) { + return 0; + } else if (9 > min_lights) { + return 1; + } else if (16 > min_lights) { + return 2; + } else { + return 3; + } +} + +uint8_t AS3935TranslMinLightsInt(uint8_t min_lights) { + switch (min_lights) { + case 0: return 1; + case 1: return 5; + case 2: return 9; + case 3: return 16; + } +} + +uint8_t AS3935TranslIrq(uint8_t irq, uint8_t distance) { + switch(irq) { + case 0: return 7; + case 1: return 5; + case 4: return 6; + case 8: + if (distance == -1) return 2; + else if (distance == 0) return 3; + else if (distance == 1) return 4; + else return 1; + } +} + +void AS3935CalcVrmsLevel(uint16_t &vrms, uint8_t &stage) +{ + uint8_t room = AS3935GetGain(); + uint8_t nflev = AS3935GetNoiseFloor(); + if (room == 0x24) + { + switch (nflev){ + case 0x00: + vrms = 28; + break; + case 0x01: + vrms = 45; + break; + case 0x02: + vrms = 62; + break; + case 0x03: + vrms = 78; + break; + case 0x04: + vrms = 95; + break; + case 0x05: + vrms = 112; + break; + case 0x06: + vrms = 130; + break; + case 0x07: + vrms = 146; + break; + } + stage = nflev; + } + else + { + switch (nflev) + { + case 0x00: + vrms = 390; + break; + case 0x01: + vrms = 630; + break; + case 0x02: + vrms = 860; + break; + case 0x03: + vrms = 1100; + break; + case 0x04: + vrms = 1140; + break; + case 0x05: + vrms = 1570; + break; + case 0x06: + vrms = 1800; + break; + case 0x07: + vrms = 2000; + break; + } + stage = nflev + 8; + } +} + + +uint8_t AS3935GetIRQ() { + delay(2); + return AS3935ReadRegister(IRQ_TBL); +} + +uint8_t AS3935GetDistance() { + return AS3935ReadRegister(LGHT_DIST); +} + +int16_t AS3935CalcDistance() { + uint8_t dist = AS3935GetDistance(); + switch (dist) { + case 0x3F: return -1; + case 0x01: return 1; + case 0x00: return 0; + default: + if (40 < dist){ + return 40; + } + return dist; + } +} + +uint32_t AS3935GetIntensity() { + uint32_t nrgy_raw = (AS3935ReadRegister(ENERGY_RAW_3) << 8); + nrgy_raw |= AS3935ReadRegister(ENERGY_RAW_2); + nrgy_raw <<= 8; + nrgy_raw |= AS3935ReadRegister(ENERGY_RAW_1); + return nrgy_raw; +} + +uint8_t AS3935GetTuneCaps() { + return AS3935ReadRegister(TUNE_CAPS); +} + +void AS3935SetTuneCaps(uint8_t tune) { + AS3935WriteRegister(TUNE_CAPS, tune); + delay(2); + AS3935CalibrateRCO(); +} + +uint8_t AS3935GetDisturber() { + return AS3935ReadRegister(DISTURBER); +} + +uint8_t AS3935SetDisturber(uint8_t stat) { + AS3935WriteRegister(DISTURBER, stat); +} + +uint8_t AS3935GetMinLights() { + return AS3935ReadRegister(MIN_NUM_LIGH); +} + +uint8_t AS3935SetMinLights(uint8_t stat) { + AS3935WriteRegister(MIN_NUM_LIGH, stat); +} + +uint8_t AS3935GetNoiseFloor() { + return AS3935ReadRegister(NF_LEVEL); +} + +uint8_t AS3935SetNoiseFloor(uint8_t noise) { + AS3935WriteRegister(NF_LEVEL , noise); +} + +uint8_t AS3935GetGain() { + if (AS3935ReadRegister(AFE_GB) == OUTDOORS) + return OUTDOORS; + return INDOORS; +} + +uint8_t AS3935SetGain(uint8_t room) { + AS3935WriteRegister(AFE_GB, room); +} + +uint8_t AS3935GetGainInt() { + if (AS3935ReadRegister(AFE_GB) == OUTDOORS) + return 1; +return 0; +} + +uint8_t AS3935GetSpikeRejection() { + return AS3935ReadRegister(SPIKE_REJECT); +} + +void AS3935SetSpikeRejection(uint8_t rej) { + AS3935WriteRegister(SPIKE_REJECT, rej); +} + +uint8_t AS3935GetWdth() { + return AS3935ReadRegister(WDTH); +} + +void AS3935SetWdth(uint8_t wdth) { + AS3935WriteRegister(WDTH, wdth); +} + +bool AS3935AutoTune(){ + detachInterrupt(pin[GPIO_AS3935]); + bool result = AS3935AutoTuneCaps(pin[GPIO_AS3935]); + attachInterrupt(digitalPinToInterrupt(pin[GPIO_AS3935]), AS3935Isr, RISING); + return result; +} + + + +bool AS3935LowerNoiseFloor() { + uint8_t noise = AS3935GetNoiseFloor(); + uint16_t vrms; + uint8_t stage; + AS3935CalcVrmsLevel(vrms, stage); + if (Settings.as3935_functions.nf_autotune_both) { + if (stage == 8 && stage > Settings.as3935_parameter.nf_autotune_min) { + AS3935SetGain(INDOORS); + AS3935SetNoiseFloor(7); + return true; + } + } + if (0 < noise && stage > Settings.as3935_parameter.nf_autotune_min) { + noise--; + AS3935SetNoiseFloor(noise); + return true; + } + return false; +} + +bool AS3935RaiseNoiseFloor() { + uint8_t noise = AS3935GetNoiseFloor(); + uint8_t room = AS3935GetGain(); + if (Settings.as3935_functions.nf_autotune_both) { + if (7 == noise && room == INDOORS) { + AS3935SetGain(OUTDOORS); + AS3935SetNoiseFloor(0); + return true; + } + } + if (7 > noise) { + noise++; + AS3935SetNoiseFloor(noise); + return true; + } + return false; +} + + + +bool AS3935SetDefault() { + I2cWrite8(AS3935_ADDR, 0x3C, 0x96); + delay(2); + Settings.as3935_sensor_cfg[0] = I2cRead8(AS3935_ADDR, 0x00); + Settings.as3935_sensor_cfg[1] = I2cRead8(AS3935_ADDR, 0x01); + Settings.as3935_sensor_cfg[2] = I2cRead8(AS3935_ADDR, 0x02); + Settings.as3935_sensor_cfg[3] = I2cRead8(AS3935_ADDR, 0x03); + Settings.as3935_sensor_cfg[4] = I2cRead8(AS3935_ADDR, 0x08); + Settings.as3935_parameter.nf_autotune_min = 0x00; + Settings.as3935_parameter.nf_autotune_time = 4; + Settings.as3935_parameter.dist_autotune_time = 1; + return true; +} + +void AS3935InitSettings() { + if(Settings.as3935_functions.nf_autotune){ + AS3935SetGain(INDOORS); + AS3935SetNoiseFloor(0); + } + I2cWrite8(AS3935_ADDR, 0x00, Settings.as3935_sensor_cfg[0]); + I2cWrite8(AS3935_ADDR, 0x01, Settings.as3935_sensor_cfg[1]); + I2cWrite8(AS3935_ADDR, 0x02, Settings.as3935_sensor_cfg[2]); + I2cWrite8(AS3935_ADDR, 0x03, Settings.as3935_sensor_cfg[3]); + I2cWrite8(AS3935_ADDR, 0x08, Settings.as3935_sensor_cfg[4]); + delay(2); +} + +void AS3935Setup(void) { + if (Settings.as3935_sensor_cfg[0] == 0x00) { + AS3935SetDefault(); + } else { + AS3935InitSettings(); + } + AS3935CalibrateRCO(); +} + +bool AS3935init() { + uint8_t ret = I2cRead8(AS3935_ADDR, 0x00); + if(INDOORS == ret || OUTDOORS == ret) + return true; + return false; +} + +void AS3935Detect(void) { + if (I2cActive(AS3935_ADDR)) return; + if (AS3935init()) + { + I2cSetActiveFound(AS3935_ADDR, D_NAME_AS3935); + pinMode(pin[GPIO_AS3935], INPUT); + attachInterrupt(digitalPinToInterrupt(pin[GPIO_AS3935]), AS3935Isr, RISING); + AS3935Setup(); + as3935_active = 1; + } +} + +void AS3935EverySecond() { + if (as3935_sensor.detected) { + as3935_sensor.irq = AS3935GetIRQ(); + switch (as3935_sensor.irq) { + case 1: + if (Settings.as3935_functions.nf_autotune) { + if (AS3935RaiseNoiseFloor()) as3935_sensor.nftimer = 0; + } + break; + case 4: + if (Settings.as3935_functions.dist_autotune) { + AS3935SetDisturber(1); + as3935_sensor.autodist_activ = true; + } + break; + case 8: + as3935_sensor.intensity = AS3935GetIntensity(); + as3935_sensor.distance = AS3935CalcDistance(); + as3935_sensor.http_intensity = as3935_sensor.intensity; + as3935_sensor.http_distance = as3935_sensor.distance; + break; + } + + as3935_sensor.http_irq = AS3935TranslIrq(as3935_sensor.irq, as3935_sensor.distance); + + as3935_sensor.mqtt_irq = as3935_sensor.http_irq; + switch (as3935_sensor.mqtt_irq) { + case 5: + case 6: + if (!Settings.as3935_functions.mqtt_only_Light_Event) { + MqttPublishSensor(); + as3935_sensor.http_timer = 10; + } + break; + default: + as3935_sensor.http_timer = 60; + MqttPublishSensor(); + } + + as3935_sensor.intensity = 0; + as3935_sensor.distance = 0; + as3935_sensor.mqtt_irq = 0; + + as3935_sensor.http_count_start = 1; + as3935_sensor.icount++; + as3935_sensor.detected = false; + } + + if (as3935_sensor.http_count_start) as3935_sensor.http_count++; + + if (as3935_sensor.http_count == as3935_sensor.http_timer) { + as3935_sensor.http_count = 0; + as3935_sensor.http_count_start = 0; + as3935_sensor.http_intensity = 0; + as3935_sensor.http_distance = 0; + as3935_sensor.http_irq = 0; + } + + if (Settings.as3935_functions.nf_autotune) { + as3935_sensor.nftimer++; + if (as3935_sensor.nftimer > Settings.as3935_parameter.nf_autotune_time * 60) { + AS3935LowerNoiseFloor(); + as3935_sensor.nftimer = 0; + } + } + + if (Settings.as3935_functions.dist_autotune) { + if (as3935_sensor.autodist_activ) as3935_sensor.disttimer++; + if (as3935_sensor.disttimer >= Settings.as3935_parameter.dist_autotune_time * 60) { + AS3935SetDisturber(0); + as3935_sensor.disttimer = 0; + as3935_sensor.autodist_activ = false; + } + } +} + +bool AS3935Cmd(void) { + char command[CMDSZ]; + uint8_t name_len = strlen(D_NAME_AS3935); + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_NAME_AS3935), name_len)) { + uint32_t command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + name_len, kAS3935_Commands); + switch (command_code) { + case CMND_AS3935_SET_NF: + if (XdrvMailbox.data_len) { + if (15 >= XdrvMailbox.payload) { + AS3935SetNoiseFloor(XdrvMailbox.payload); + } + } + Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, AS3935GetNoiseFloor()); + break; + case CMND_AS3935_SET_MINNF: + if (XdrvMailbox.data_len) { + if (15 >= XdrvMailbox.payload) { + Settings.as3935_parameter.nf_autotune_min = XdrvMailbox.payload; + } + } + Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, Settings.as3935_parameter.nf_autotune_min); + break; + case CMND_AS3935_SET_MINLIGHT: + if (XdrvMailbox.data_len) { + AS3935SetMinLights(AS3935TransMinLights(XdrvMailbox.payload)); + } + Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, AS3935TranslMinLightsInt(AS3935GetMinLights())); + break; + case CMND_AS3935_SET_DEF: + if (!XdrvMailbox.data_len) { + Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, AS3935SetDefault()); + } + break; + case CMND_AS3935_SET_GAIN: + if (XdrvMailbox.data_len > 6) { + uint8_t data_len = strlen(D_AS3935_OUTDOORS); + if (!strncasecmp_P(XdrvMailbox.data, PSTR(D_AS3935_OUTDOORS), data_len)) { + AS3935SetGain(OUTDOORS); + } else { + AS3935SetGain(INDOORS); + } + } + Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_GAIN[AS3935GetGainInt()]); + break; + case CMND_AS3935_SET_TUNE: + if (XdrvMailbox.data_len) { + if (15 >= XdrvMailbox.payload) { + AS3935SetTuneCaps(XdrvMailbox.payload); + } + } + Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, AS3935GetTuneCaps()); + break; + case CMND_AS3935_SET_REJ: + if (XdrvMailbox.data_len) { + if (15 >= XdrvMailbox.payload) { + AS3935SetSpikeRejection(XdrvMailbox.payload); + } + } + Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, AS3935GetSpikeRejection()); + break; + case CMND_AS3935_SET_WDTH: + if (XdrvMailbox.data_len) { + if (15 >= XdrvMailbox.payload) { + AS3935SetWdth(XdrvMailbox.payload); + } + } + Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, AS3935GetWdth()); + break; + case CMND_AS3935_DISTTIME: + if (XdrvMailbox.data_len) { + if (15 >= XdrvMailbox.payload) { + Settings.as3935_parameter.dist_autotune_time = XdrvMailbox.payload; + } + } + Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, Settings.as3935_parameter.dist_autotune_time); + break; + case CMND_AS3935_NFTIME: + if (XdrvMailbox.data_len) { + if (15 >= XdrvMailbox.payload) { + Settings.as3935_parameter.nf_autotune_time = XdrvMailbox.payload; + } + } + Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, Settings.as3935_parameter.nf_autotune_time); + break; + case CMND_AS3935_SET_DISTURBER: + if (XdrvMailbox.data_len) { + if (2 > XdrvMailbox.payload) { + AS3935SetDisturber(XdrvMailbox.payload); + if (!XdrvMailbox.payload) Settings.as3935_functions.dist_autotune = 0; + } + } + Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[AS3935GetDisturber()]); + break; + case CMND_AS3935_NF_AUTOTUNE: + if (XdrvMailbox.data_len) { + if (2 > XdrvMailbox.payload) { + Settings.as3935_functions.nf_autotune = XdrvMailbox.payload; + } + } + Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[Settings.as3935_functions.nf_autotune]); + break; + case CMND_AS3935_DIST_AUTOTUNE: + if (XdrvMailbox.data_len) { + if (2 > XdrvMailbox.payload) { + Settings.as3935_functions.dist_autotune = XdrvMailbox.payload; + } + } + Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[Settings.as3935_functions.dist_autotune]); + break; + case CMND_AS3935_NF_ATUNE_BOTH: + if (XdrvMailbox.data_len) { + if (2 > XdrvMailbox.payload) { + Settings.as3935_functions.nf_autotune_both = XdrvMailbox.payload; + } + } + Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[Settings.as3935_functions.nf_autotune_both]); + break; + case CMND_AS3935_MQTT_LIGHT_EVT: + if (XdrvMailbox.data_len) { + if (2 > XdrvMailbox.payload) { + Settings.as3935_functions.mqtt_only_Light_Event = XdrvMailbox.payload; + } + } + Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[Settings.as3935_functions.mqtt_only_Light_Event]); + break; + case CMND_AS3935_SETTINGS: { + if (!XdrvMailbox.data_len) { + uint8_t gain = AS3935GetGainInt(); + uint16_t vrms; + uint8_t stage; + AS3935CalcVrmsLevel(vrms, stage); + uint8_t nf_floor = AS3935GetNoiseFloor(); + uint8_t min_nf = Settings.as3935_parameter.nf_autotune_min; + uint8_t tunecaps = AS3935GetTuneCaps(); + uint8_t minnumlight = AS3935TranslMinLightsInt(AS3935GetMinLights()); + uint8_t disturber = AS3935GetDisturber(); + uint8_t reinj = AS3935GetSpikeRejection(); + uint8_t wdth = AS3935GetWdth(); + uint8_t nfauto = Settings.as3935_functions.nf_autotune; + uint8_t distauto = Settings.as3935_functions.dist_autotune; + uint8_t nfautomax = Settings.as3935_functions.nf_autotune_both; + uint8_t jsonlight = Settings.as3935_functions.mqtt_only_Light_Event; + uint8_t nf_time = Settings.as3935_parameter.nf_autotune_time; + uint8_t dist_time =Settings.as3935_parameter.dist_autotune_time; + Response_P(S_JSON_AS3935_COMMAND_SETTINGS, S_JSON_AS3935_COMMAND_GAIN[gain], nf_floor, vrms, tunecaps, minnumlight, reinj, wdth, min_nf, nf_time, dist_time, S_JSON_AS3935_COMMAND_ONOFF[disturber], S_JSON_AS3935_COMMAND_ONOFF[nfauto], S_JSON_AS3935_COMMAND_ONOFF[distauto], S_JSON_AS3935_COMMAND_ONOFF[nfautomax], S_JSON_AS3935_COMMAND_ONOFF[jsonlight]); + } + } + break; + case CMND_AS3935_CALIBRATE: { + bool calreslt; + if (!XdrvMailbox.data_len) calreslt = AS3935AutoTune(); + Response_P(S_JSON_AS3935_COMMAND_NVALUE, S_JSON_AS3935_COMMAND_CAL[calreslt], AS3935GetTuneCaps()); + } + break; + default: + return false; + } + return true; + } else { + return false; + } +} + +void AH3935Show(bool json) +{ + if (json) { + ResponseAppend_P(JSON_SNS_AS3935_EVENTS, D_SENSOR_AS3935, as3935_sensor.mqtt_irq, as3935_sensor.distance, as3935_sensor.intensity ); + +#ifdef USE_WEBSERVER + } else { + uint8_t gain = AS3935GetGainInt(); + uint8_t disturber = AS3935GetDisturber(); + uint16_t vrms; + uint8_t stage; + AS3935CalcVrmsLevel(vrms, stage); + + WSContentSend_PD(HTTP_SNS_AS3935_TABLE_1[as3935_sensor.http_irq], D_NAME_AS3935, as3935_sensor.http_distance); + WSContentSend_PD(HTTP_SNS_AS3935_DISTANZ, as3935_sensor.http_distance); + WSContentSend_PD(HTTP_SNS_AS3935_ENERGY, as3935_sensor.http_intensity); + WSContentSend_PD(HTTP_SNS_AS3935_GAIN[gain], D_NAME_AS3935); + WSContentSend_PD(HTTP_SNS_AS3935_DISTURBER[disturber], D_NAME_AS3935); + WSContentSend_PD(HTTP_SNS_AS3935_VRMS, vrms, stage); +#endif + } +} + + + + + +bool Xsns67(uint8_t function) +{ + if (!I2cEnabled(XI2C_48)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + AS3935Detect(); + } + else if (as3935_active) { + switch (function) { + case FUNC_EVERY_SECOND: + AS3935EverySecond(); + break; + case FUNC_COMMAND: + result = AS3935Cmd(); + break; + case FUNC_JSON_APPEND: + AH3935Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + AH3935Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_91_prometheus.ino" +# 22 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_91_prometheus.ino" +#ifdef USE_PROMETHEUS + + + + +#define XSNS_91 91 + +void HandleMetrics(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR("Prometheus")); + + WSContentBegin(200, CT_PLAIN); + + + char parameter[FLOATSZ]; + + if (global_temperature != 9999) { + dtostrfd(global_temperature, Settings.flag2.temperature_resolution, parameter); + WSContentSend_P(PSTR("# TYPE global_temperature gauge\nglobal_temperature %s\n"), parameter); + } + if (global_humidity != 0) { + dtostrfd(global_humidity, Settings.flag2.humidity_resolution, parameter); + WSContentSend_P(PSTR("# TYPE global_humidity gauge\nglobal_humidity %s\n"), parameter); + } + if (global_pressure != 0) { + dtostrfd(global_pressure, Settings.flag2.pressure_resolution, parameter); + WSContentSend_P(PSTR("# TYPE global_pressure gauge\nglobal_pressure %s\n"), parameter); + } + +#ifdef USE_ENERGY_SENSOR + dtostrfd(Energy.voltage[0], Settings.flag2.voltage_resolution, parameter); + WSContentSend_P(PSTR("# TYPE voltage gauge\nvoltage %s\n"), parameter); + dtostrfd(Energy.current[0], Settings.flag2.current_resolution, parameter); + WSContentSend_P(PSTR("# TYPE current gauge\ncurrent %s\n"), parameter); + dtostrfd(Energy.active_power[0], Settings.flag2.wattage_resolution, parameter); + WSContentSend_P(PSTR("# TYPE active_power gauge\nactive_power %s\n"), parameter); + dtostrfd(Energy.daily, Settings.flag2.energy_resolution, parameter); + WSContentSend_P(PSTR("# TYPE energy_daily gauge\nenergy_daily %s\n"), parameter); + dtostrfd(Energy.total, Settings.flag2.energy_resolution, parameter); + WSContentSend_P(PSTR("# TYPE energy_total counter\nenergy_total %s\n"), parameter); +#endif +# 80 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_91_prometheus.ino" + WSContentEnd(); +} + + + + + +bool Xsns91(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_WEB_ADD_HANDLER: + Webserver->on("/metrics", HandleMetrics); + break; + } + return result; +} + +#endif +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_interface.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_interface.ino" +#ifdef XFUNC_PTR_IN_ROM +bool (* const xsns_func_ptr[])(uint8_t) PROGMEM = { +#else +bool (* const xsns_func_ptr[])(uint8_t) = { +#endif + +#ifdef XSNS_01 + &Xsns01, +#endif + +#ifdef XSNS_02 + &Xsns02, +#endif + +#ifdef XSNS_03 + &Xsns03, +#endif + +#ifdef XSNS_04 + &Xsns04, +#endif + +#ifdef XSNS_05 + &Xsns05, +#endif + +#ifdef XSNS_06 + &Xsns06, +#endif + +#ifdef XSNS_07 + &Xsns07, +#endif + +#ifdef XSNS_08 + &Xsns08, +#endif + +#ifdef XSNS_09 + &Xsns09, +#endif + +#ifdef XSNS_10 + &Xsns10, +#endif + +#ifdef XSNS_11 + &Xsns11, +#endif + +#ifdef XSNS_12 + &Xsns12, +#endif + +#ifdef XSNS_13 + &Xsns13, +#endif + +#ifdef XSNS_14 + &Xsns14, +#endif + +#ifdef XSNS_15 + &Xsns15, +#endif + +#ifdef XSNS_16 + &Xsns16, +#endif + +#ifdef XSNS_17 + &Xsns17, +#endif + +#ifdef XSNS_18 + &Xsns18, +#endif + +#ifdef XSNS_19 + &Xsns19, +#endif + +#ifdef XSNS_20 + &Xsns20, +#endif + +#ifdef XSNS_21 + &Xsns21, +#endif + +#ifdef XSNS_22 + &Xsns22, +#endif + +#ifdef XSNS_23 + &Xsns23, +#endif + +#ifdef XSNS_24 + &Xsns24, +#endif + +#ifdef XSNS_25 + &Xsns25, +#endif + +#ifdef XSNS_26 + &Xsns26, +#endif + +#ifdef XSNS_27 + &Xsns27, +#endif + +#ifdef XSNS_28 + &Xsns28, +#endif + +#ifdef XSNS_29 + &Xsns29, +#endif + +#ifdef XSNS_30 + &Xsns30, +#endif + +#ifdef XSNS_31 + &Xsns31, +#endif + +#ifdef XSNS_32 + &Xsns32, +#endif + +#ifdef XSNS_33 + &Xsns33, +#endif + +#ifdef XSNS_34 + &Xsns34, +#endif + +#ifdef XSNS_35 + &Xsns35, +#endif + +#ifdef XSNS_36 + &Xsns36, +#endif + +#ifdef XSNS_37 + &Xsns37, +#endif + +#ifdef XSNS_38 + &Xsns38, +#endif + +#ifdef XSNS_39 + &Xsns39, +#endif + +#ifdef XSNS_40 + &Xsns40, +#endif + +#ifdef XSNS_41 + &Xsns41, +#endif + +#ifdef XSNS_42 + &Xsns42, +#endif + +#ifdef XSNS_43 + &Xsns43, +#endif + +#ifdef XSNS_44 + &Xsns44, +#endif + +#ifdef XSNS_45 + &Xsns45, +#endif + +#ifdef XSNS_46 + &Xsns46, +#endif + +#ifdef XSNS_47 + &Xsns47, +#endif + +#ifdef XSNS_48 + &Xsns48, +#endif + +#ifdef XSNS_49 + &Xsns49, +#endif + +#ifdef XSNS_50 + &Xsns50, +#endif + +#ifdef XSNS_51 + &Xsns51, +#endif + +#ifdef XSNS_52 + &Xsns52, +#endif + +#ifdef XSNS_53 + &Xsns53, +#endif + +#ifdef XSNS_54 + &Xsns54, +#endif + +#ifdef XSNS_55 + &Xsns55, +#endif + +#ifdef XSNS_56 + &Xsns56, +#endif + +#ifdef XSNS_57 + &Xsns57, +#endif + +#ifdef XSNS_58 + &Xsns58, +#endif + +#ifdef XSNS_59 + &Xsns59, +#endif + +#ifdef XSNS_60 + &Xsns60, +#endif + +#ifdef XSNS_61 + &Xsns61, +#endif + +#ifdef XSNS_62 + &Xsns62, +#endif + +#ifdef XSNS_63 + &Xsns63, +#endif + +#ifdef XSNS_64 + &Xsns64, +#endif + +#ifdef XSNS_65 + &Xsns65, +#endif + +#ifdef XSNS_66 + &Xsns66, +#endif + +#ifdef XSNS_67 + &Xsns67, +#endif + +#ifdef XSNS_68 + &Xsns68, +#endif + +#ifdef XSNS_69 + &Xsns69, +#endif + +#ifdef XSNS_70 + &Xsns70, +#endif + +#ifdef XSNS_71 + &Xsns71, +#endif + +#ifdef XSNS_72 + &Xsns72, +#endif + +#ifdef XSNS_73 + &Xsns73, +#endif + +#ifdef XSNS_74 + &Xsns74, +#endif + +#ifdef XSNS_75 + &Xsns75, +#endif + +#ifdef XSNS_76 + &Xsns76, +#endif + +#ifdef XSNS_77 + &Xsns77, +#endif + +#ifdef XSNS_78 + &Xsns78, +#endif + +#ifdef XSNS_79 + &Xsns79, +#endif + +#ifdef XSNS_80 + &Xsns80, +#endif + +#ifdef XSNS_81 + &Xsns81, +#endif + +#ifdef XSNS_82 + &Xsns82, +#endif + +#ifdef XSNS_83 + &Xsns83, +#endif + +#ifdef XSNS_84 + &Xsns84, +#endif + +#ifdef XSNS_85 + &Xsns85, +#endif + +#ifdef XSNS_86 + &Xsns86, +#endif + +#ifdef XSNS_87 + &Xsns87, +#endif + +#ifdef XSNS_88 + &Xsns88, +#endif + +#ifdef XSNS_89 + &Xsns89, +#endif + +#ifdef XSNS_90 + &Xsns90, +#endif + +#ifdef XSNS_91 + &Xsns91, +#endif + +#ifdef XSNS_92 + &Xsns92, +#endif + +#ifdef XSNS_93 + &Xsns93, +#endif + +#ifdef XSNS_94 + &Xsns94, +#endif + +#ifdef XSNS_95 + &Xsns95, +#endif + +#ifdef XSNS_96 + &Xsns96, +#endif + +#ifdef XSNS_97 + &Xsns97, +#endif + +#ifdef XSNS_98 + &Xsns98, +#endif + +#ifdef XSNS_99 + &Xsns99 +#endif +}; + +const uint8_t xsns_present = sizeof(xsns_func_ptr) / sizeof(xsns_func_ptr[0]); + + + + + +#ifdef XFUNC_PTR_IN_ROM +const uint8_t kXsnsList[] PROGMEM = { +#else +const uint8_t kXsnsList[] = { +#endif + +#ifdef XSNS_01 + XSNS_01, +#endif + +#ifdef XSNS_02 + XSNS_02, +#endif + +#ifdef XSNS_03 + XSNS_03, +#endif + +#ifdef XSNS_04 + XSNS_04, +#endif + +#ifdef XSNS_05 + XSNS_05, +#endif + +#ifdef XSNS_06 + XSNS_06, +#endif + +#ifdef XSNS_07 + XSNS_07, +#endif + +#ifdef XSNS_08 + XSNS_08, +#endif + +#ifdef XSNS_09 + XSNS_09, +#endif + +#ifdef XSNS_10 + XSNS_10, +#endif + +#ifdef XSNS_11 + XSNS_11, +#endif + +#ifdef XSNS_12 + XSNS_12, +#endif + +#ifdef XSNS_13 + XSNS_13, +#endif + +#ifdef XSNS_14 + XSNS_14, +#endif + +#ifdef XSNS_15 + XSNS_15, +#endif + +#ifdef XSNS_16 + XSNS_16, +#endif + +#ifdef XSNS_17 + XSNS_17, +#endif + +#ifdef XSNS_18 + XSNS_18, +#endif + +#ifdef XSNS_19 + XSNS_19, +#endif + +#ifdef XSNS_20 + XSNS_20, +#endif + +#ifdef XSNS_21 + XSNS_21, +#endif + +#ifdef XSNS_22 + XSNS_22, +#endif + +#ifdef XSNS_23 + XSNS_23, +#endif + +#ifdef XSNS_24 + XSNS_24, +#endif + +#ifdef XSNS_25 + XSNS_25, +#endif + +#ifdef XSNS_26 + XSNS_26, +#endif + +#ifdef XSNS_27 + XSNS_27, +#endif + +#ifdef XSNS_28 + XSNS_28, +#endif + +#ifdef XSNS_29 + XSNS_29, +#endif + +#ifdef XSNS_30 + XSNS_30, +#endif + +#ifdef XSNS_31 + XSNS_31, +#endif + +#ifdef XSNS_32 + XSNS_32, +#endif + +#ifdef XSNS_33 + XSNS_33, +#endif + +#ifdef XSNS_34 + XSNS_34, +#endif + +#ifdef XSNS_35 + XSNS_35, +#endif + +#ifdef XSNS_36 + XSNS_36, +#endif + +#ifdef XSNS_37 + XSNS_37, +#endif + +#ifdef XSNS_38 + XSNS_38, +#endif + +#ifdef XSNS_39 + XSNS_39, +#endif + +#ifdef XSNS_40 + XSNS_40, +#endif + +#ifdef XSNS_41 + XSNS_41, +#endif + +#ifdef XSNS_42 + XSNS_42, +#endif + +#ifdef XSNS_43 + XSNS_43, +#endif + +#ifdef XSNS_44 + XSNS_44, +#endif + +#ifdef XSNS_45 + XSNS_45, +#endif + +#ifdef XSNS_46 + XSNS_46, +#endif + +#ifdef XSNS_47 + XSNS_47, +#endif + +#ifdef XSNS_48 + XSNS_48, +#endif + +#ifdef XSNS_49 + XSNS_49, +#endif + +#ifdef XSNS_50 + XSNS_50, +#endif + +#ifdef XSNS_51 + XSNS_51, +#endif + +#ifdef XSNS_52 + XSNS_52, +#endif + +#ifdef XSNS_53 + XSNS_53, +#endif + +#ifdef XSNS_54 + XSNS_54, +#endif + +#ifdef XSNS_55 + XSNS_55, +#endif + +#ifdef XSNS_56 + XSNS_56, +#endif + +#ifdef XSNS_57 + XSNS_57, +#endif + +#ifdef XSNS_58 + XSNS_58, +#endif + +#ifdef XSNS_59 + XSNS_59, +#endif + +#ifdef XSNS_60 + XSNS_60, +#endif + +#ifdef XSNS_61 + XSNS_61, +#endif + +#ifdef XSNS_62 + XSNS_62, +#endif + +#ifdef XSNS_63 + XSNS_63, +#endif + +#ifdef XSNS_64 + XSNS_64, +#endif + +#ifdef XSNS_65 + XSNS_65, +#endif + +#ifdef XSNS_66 + XSNS_66, +#endif + +#ifdef XSNS_67 + XSNS_67, +#endif + +#ifdef XSNS_68 + XSNS_68, +#endif + +#ifdef XSNS_69 + XSNS_69, +#endif + +#ifdef XSNS_70 + XSNS_70, +#endif + +#ifdef XSNS_71 + XSNS_71, +#endif + +#ifdef XSNS_72 + XSNS_72, +#endif + +#ifdef XSNS_73 + XSNS_73, +#endif + +#ifdef XSNS_74 + XSNS_74, +#endif + +#ifdef XSNS_75 + XSNS_75, +#endif + +#ifdef XSNS_76 + XSNS_76, +#endif + +#ifdef XSNS_77 + XSNS_77, +#endif + +#ifdef XSNS_78 + XSNS_78, +#endif + +#ifdef XSNS_79 + XSNS_79, +#endif + +#ifdef XSNS_80 + XSNS_80, +#endif + +#ifdef XSNS_81 + XSNS_81, +#endif + +#ifdef XSNS_82 + XSNS_82, +#endif + +#ifdef XSNS_83 + XSNS_83, +#endif + +#ifdef XSNS_84 + XSNS_84, +#endif + +#ifdef XSNS_85 + XSNS_85, +#endif + +#ifdef XSNS_86 + XSNS_86, +#endif + +#ifdef XSNS_87 + XSNS_87, +#endif + +#ifdef XSNS_88 + XSNS_88, +#endif + +#ifdef XSNS_89 + XSNS_89, +#endif + +#ifdef XSNS_90 + XSNS_90, +#endif + +#ifdef XSNS_91 + XSNS_91, +#endif + +#ifdef XSNS_92 + XSNS_92, +#endif + +#ifdef XSNS_93 + XSNS_93, +#endif + +#ifdef XSNS_94 + XSNS_94, +#endif + +#ifdef XSNS_95 + XSNS_95, +#endif + +#ifdef XSNS_96 + XSNS_96, +#endif + +#ifdef XSNS_97 + XSNS_97, +#endif + +#ifdef XSNS_98 + XSNS_98, +#endif + +#ifdef XSNS_99 + XSNS_99 +#endif +}; + + + +bool XsnsEnabled(uint32_t sns_index) +{ + if (sns_index < sizeof(kXsnsList)) { +#ifdef XFUNC_PTR_IN_ROM + uint32_t index = pgm_read_byte(kXsnsList + sns_index); +#else + uint32_t index = kXsnsList[sns_index]; +#endif + return bitRead(Settings.sensors[index / 32], index % 32); + } + return true; +} + +void XsnsSensorState(void) +{ + ResponseAppend_P(PSTR("\"")); + for (uint32_t i = 0; i < sizeof(kXsnsList); i++) { +#ifdef XFUNC_PTR_IN_ROM + uint32_t sensorid = pgm_read_byte(kXsnsList + i); +#else + uint32_t sensorid = kXsnsList[i]; +#endif + bool disabled = false; + if (sensorid < MAX_XSNS_DRIVERS) { + disabled = !bitRead(Settings.sensors[sensorid / 32], sensorid % 32); + } + ResponseAppend_P(PSTR("%s%s%d"), (i) ? "," : "", (disabled) ? "!" : "", sensorid); + } + ResponseAppend_P(PSTR("\"")); +} + + + + + +bool XsnsNextCall(uint8_t Function, uint8_t &xsns_index) +{ + xsns_index++; + if (xsns_index == xsns_present) { xsns_index = 0; } + +#ifndef USE_DEBUG_DRIVER + if (FUNC_WEB_SENSOR == Function) { +#endif + uint32_t max_disabled = xsns_present; + while (!XsnsEnabled(xsns_index) && max_disabled--) { + xsns_index++; + if (xsns_index == xsns_present) { xsns_index = 0; } + } +#ifndef USE_DEBUG_DRIVER + } +#endif + + return xsns_func_ptr[xsns_index](Function); +} + +bool XsnsCall(uint8_t Function) +{ + bool result = false; + + DEBUG_TRACE_LOG(PSTR("SNS: %d"), Function); + +#ifdef PROFILE_XSNS_EVERY_SECOND + uint32_t profile_start_millis = millis(); +#endif + + for (uint32_t x = 0; x < xsns_present; x++) { +#ifdef USE_DEBUG_DRIVER + if (XsnsEnabled(x)) { +#endif + + if ((FUNC_WEB_SENSOR == Function) && !XsnsEnabled(x)) { continue; } + +#ifdef PROFILE_XSNS_SENSOR_EVERY_SECOND + uint32_t profile_start_millis = millis(); +#endif + result = xsns_func_ptr[x](Function); + +#ifdef PROFILE_XSNS_SENSOR_EVERY_SECOND + uint32_t profile_millis = millis() - profile_start_millis; + if (profile_millis) { + if (FUNC_EVERY_SECOND == Function) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PRF: At %08u XsnsCall %d to Sensor %d took %u mS"), uptime, Function, x, profile_millis); + } + } +#endif + + if (result && ((FUNC_COMMAND == Function) || + (FUNC_PIN_STATE == Function) || + (FUNC_COMMAND_SENSOR == Function) + )) { + break; + } +#ifdef USE_DEBUG_DRIVER + } +#endif + } + +#ifdef PROFILE_XSNS_EVERY_SECOND + uint32_t profile_millis = millis() - profile_start_millis; + if (profile_millis) { + if (FUNC_EVERY_SECOND == Function) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PRF: At %08u XsnsCall %d took %u mS"), uptime, Function, profile_millis); + } + } +#endif + + return result; +} +# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xx2c_interface.ino" +# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xx2c_interface.ino" +#ifdef USE_I2C + +#ifdef XFUNC_PTR_IN_ROM +const uint8_t kI2cList[] PROGMEM = { +#else +const uint8_t kI2cList[] = { +#endif + +#ifdef XI2C_01 + XI2C_01, +#endif + +#ifdef XI2C_02 + XI2C_02, +#endif + +#ifdef XI2C_03 + XI2C_03, +#endif + +#ifdef XI2C_04 + XI2C_04, +#endif + +#ifdef XI2C_05 + XI2C_05, +#endif + +#ifdef XI2C_06 + XI2C_06, +#endif + +#ifdef XI2C_07 + XI2C_07, +#endif + +#ifdef XI2C_08 + XI2C_08, +#endif + +#ifdef XI2C_09 + XI2C_09, +#endif + +#ifdef XI2C_10 + XI2C_10, +#endif + +#ifdef XI2C_11 + XI2C_11, +#endif + +#ifdef XI2C_12 + XI2C_12, +#endif + +#ifdef XI2C_13 + XI2C_13, +#endif + +#ifdef XI2C_14 + XI2C_14, +#endif + +#ifdef XI2C_15 + XI2C_15, +#endif + +#ifdef XI2C_16 + XI2C_16, +#endif + +#ifdef XI2C_17 + XI2C_17, +#endif + +#ifdef XI2C_18 + XI2C_18, +#endif + +#ifdef XI2C_19 + XI2C_19, +#endif + +#ifdef XI2C_20 + XI2C_20, +#endif + +#ifdef XI2C_21 + XI2C_21, +#endif + +#ifdef XI2C_22 + XI2C_22, +#endif + +#ifdef XI2C_23 + XI2C_23, +#endif + +#ifdef XI2C_24 + XI2C_24, +#endif + +#ifdef XI2C_25 + XI2C_25, +#endif + +#ifdef XI2C_26 + XI2C_26, +#endif + +#ifdef XI2C_27 + XI2C_27, +#endif + +#ifdef XI2C_28 + XI2C_28, +#endif + +#ifdef XI2C_29 + XI2C_29, +#endif + +#ifdef XI2C_30 + XI2C_30, +#endif + +#ifdef XI2C_31 + XI2C_31, +#endif + +#ifdef XI2C_32 + XI2C_32, +#endif + +#ifdef XI2C_33 + XI2C_33, +#endif + +#ifdef XI2C_34 + XI2C_34, +#endif + +#ifdef XI2C_35 + XI2C_35, +#endif + +#ifdef XI2C_36 + XI2C_36, +#endif + +#ifdef XI2C_37 + XI2C_37, +#endif + +#ifdef XI2C_38 + XI2C_38, +#endif + +#ifdef XI2C_39 + XI2C_39, +#endif + +#ifdef XI2C_40 + XI2C_40, +#endif + +#ifdef XI2C_41 + XI2C_41, +#endif + +#ifdef XI2C_42 + XI2C_42, +#endif + +#ifdef XI2C_43 + XI2C_43, +#endif + +#ifdef XI2C_44 + XI2C_44, +#endif + +#ifdef XI2C_45 + XI2C_45, +#endif + +#ifdef XI2C_46 + XI2C_46, +#endif + +#ifdef XI2C_47 + XI2C_47, +#endif + +#ifdef XI2C_48 + XI2C_48, +#endif + +#ifdef XI2C_49 + XI2C_49, +#endif + +#ifdef XI2C_50 + XI2C_50, +#endif + +#ifdef XI2C_51 + XI2C_51, +#endif + +#ifdef XI2C_52 + XI2C_52, +#endif + +#ifdef XI2C_53 + XI2C_53, +#endif + +#ifdef XI2C_54 + XI2C_54, +#endif + +#ifdef XI2C_55 + XI2C_55, +#endif + +#ifdef XI2C_56 + XI2C_56, +#endif + +#ifdef XI2C_57 + XI2C_57, +#endif + +#ifdef XI2C_58 + XI2C_58, +#endif + +#ifdef XI2C_59 + XI2C_59, +#endif + +#ifdef XI2C_60 + XI2C_60, +#endif + +#ifdef XI2C_61 + XI2C_61, +#endif + +#ifdef XI2C_62 + XI2C_62, +#endif + +#ifdef XI2C_63 + XI2C_63, +#endif + +#ifdef XI2C_64 + XI2C_64, +#endif + +#ifdef XI2C_65 + XI2C_65, +#endif + +#ifdef XI2C_66 + XI2C_66, +#endif + +#ifdef XI2C_67 + XI2C_67, +#endif + +#ifdef XI2C_68 + XI2C_68, +#endif + +#ifdef XI2C_69 + XI2C_69, +#endif + +#ifdef XI2C_70 + XI2C_70, +#endif + +#ifdef XI2C_71 + XI2C_71, +#endif + +#ifdef XI2C_72 + XI2C_72, +#endif + +#ifdef XI2C_73 + XI2C_73, +#endif + +#ifdef XI2C_74 + XI2C_74, +#endif + +#ifdef XI2C_75 + XI2C_75, +#endif + +#ifdef XI2C_76 + XI2C_76, +#endif + +#ifdef XI2C_77 + XI2C_77, +#endif + +#ifdef XI2C_78 + XI2C_78, +#endif + +#ifdef XI2C_79 + XI2C_79, +#endif + +#ifdef XI2C_80 + XI2C_80, +#endif + +#ifdef XI2C_81 + XI2C_81, +#endif + +#ifdef XI2C_82 + XI2C_82, +#endif + +#ifdef XI2C_83 + XI2C_83, +#endif + +#ifdef XI2C_84 + XI2C_84, +#endif + +#ifdef XI2C_85 + XI2C_85, +#endif + +#ifdef XI2C_86 + XI2C_86, +#endif + +#ifdef XI2C_87 + XI2C_87, +#endif + +#ifdef XI2C_88 + XI2C_88, +#endif + +#ifdef XI2C_89 + XI2C_89, +#endif + +#ifdef XI2C_90 + XI2C_90, +#endif + +#ifdef XI2C_91 + XI2C_91, +#endif + +#ifdef XI2C_92 + XI2C_92, +#endif + +#ifdef XI2C_93 + XI2C_93, +#endif + +#ifdef XI2C_94 + XI2C_94, +#endif + +#ifdef XI2C_95 + XI2C_95, +#endif + +#ifdef XI2C_96 + XI2C_96 +#endif +}; + + + +bool I2cEnabled(uint32_t i2c_index) +{ + return (i2c_flg && bitRead(Settings.i2c_drivers[i2c_index / 32], i2c_index % 32)); +} + +void I2cDriverState(void) +{ + ResponseAppend_P(PSTR("\"")); + for (uint32_t i = 0; i < sizeof(kI2cList); i++) { +#ifdef XFUNC_PTR_IN_ROM + uint32_t i2c_driver_id = pgm_read_byte(kI2cList + i); +#else + uint32_t i2c_driver_id = kI2cList[i]; +#endif + bool disabled = false; + if (i2c_driver_id < MAX_I2C_DRIVERS) { + disabled = !bitRead(Settings.i2c_drivers[i2c_driver_id / 32], i2c_driver_id % 32); + } + ResponseAppend_P(PSTR("%s%s%d"), (i) ? "," : "", (disabled) ? "!" : "", i2c_driver_id); + } + ResponseAppend_P(PSTR("\"")); +} + +#endif \ No newline at end of file diff --git a/tasmota/xdrv_39_thermostat.ino b/tasmota/xdrv_39_thermostat.ino index c3e97f3bf..85a1b245b 100644 --- a/tasmota/xdrv_39_thermostat.ino +++ b/tasmota/xdrv_39_thermostat.ino @@ -22,7 +22,7 @@ #define XDRV_39 39 // Enable/disable debugging -//#define DEBUG_THERMOSTAT +#define DEBUG_THERMOSTAT #ifdef DEBUG_THERMOSTAT #define DOMOTICZ_IDX1 791 @@ -341,7 +341,7 @@ bool HeatStateAllToOff(void) return change_state; } -void ThermostatState() +void ThermostatState(void) { switch (Thermostat.status.thermostat_mode) { case THERMOSTAT_OFF: // State if Off or Emergency @@ -394,14 +394,28 @@ void ThermostatOutputRelay(bool active) } } -void ThermostatCalculatePI() +void ThermostatCalculatePI(void) { + int32_t aux_time_error; + // Calculate error - Thermostat.temp_pi_error = Thermostat.temp_target_level_ctr - Thermostat.temp_measured; + aux_time_error = (int32_t)(Thermostat.temp_target_level_ctr - Thermostat.temp_measured) * 10; + + // Protect overflow + if (aux_time_error <= (int32_t)(INT16_MIN)) { + Thermostat.temp_pi_error = (int16_t)(INT16_MIN); + } + else if (aux_time_error >= (int32_t)INT16_MAX) { + Thermostat.temp_pi_error = (int16_t)INT16_MAX; + } + else { + Thermostat.temp_pi_error = (int16_t)aux_time_error; + } + // Kp = 100/PI.propBand. PI.propBand(Xp) = Proportional range (4K in 4K/200 controller) Thermostat.kP_pi = 100 / (uint16_t)(Thermostat.val_prop_band); // Calculate proportional - Thermostat.time_proportional_pi = ((int32_t)(Thermostat.temp_pi_error * (int16_t)Thermostat.kP_pi) * ((int32_t)Thermostat.time_pi_cycle * 60)) / 1000; + Thermostat.time_proportional_pi = ((int32_t)(Thermostat.temp_pi_error * (int16_t)Thermostat.kP_pi) * ((int32_t)Thermostat.time_pi_cycle * 60)) / 10000; // Minimum proportional action limiter // If proportional action is less than the minimum action time @@ -419,13 +433,14 @@ void ThermostatCalculatePI() Thermostat.time_proportional_pi = ((int32_t)Thermostat.time_pi_cycle * 60); } - // Calculate integral - Thermostat.kI_pi = (uint16_t)(((float)Thermostat.kP_pi * ((float)((uint32_t)Thermostat.time_pi_cycle * 60) / (float)Thermostat.time_reset)) * 100); + // Calculate integral (resolution increased to avoid use of floats in consequent operations) + //Thermostat.kI_pi = (uint16_t)(((float)Thermostat.kP_pi * ((float)((uint32_t)Thermostat.time_pi_cycle * 60) / (float)Thermostat.time_reset)) * 100); + Thermostat.kI_pi = (uint16_t)((((uint32_t)Thermostat.kP_pi * (uint32_t)Thermostat.time_pi_cycle * 6000)) / (uint32_t)Thermostat.time_reset); // Reset of antiwindup // If error does not lay within the integrator scope range, do not use the integral // and accumulate error = 0 - if (abs(Thermostat.temp_pi_error) > (int16_t)Thermostat.temp_reset_anti_windup) { + if (abs((Thermostat.temp_pi_error) / 10) > Thermostat.temp_reset_anti_windup) { Thermostat.time_integral_pi = 0; Thermostat.temp_pi_accum_error = 0; } @@ -440,13 +455,26 @@ void ThermostatCalculatePI() // very high cummulated error when beingin hysteresis. This triggers high // integral actions + // Update accumulated error + aux_time_error = (int32_t)Thermostat.temp_pi_accum_error + (int32_t)Thermostat.temp_pi_error; + + // Protect overflow + if (aux_time_error <= (int32_t)INT16_MIN) { + Thermostat.temp_pi_accum_error = INT16_MIN; + } + else if (aux_time_error >= (int32_t)INT16_MAX) { + Thermostat.temp_pi_accum_error = INT16_MAX; + } + else { + Thermostat.temp_pi_accum_error = (int16_t)aux_time_error; + } + // If we are under setpoint // AND we are within the hysteresis // AND we are rising if ((Thermostat.temp_pi_error >= 0) - && (abs(Thermostat.temp_pi_error) <= (int16_t)Thermostat.temp_hysteresis) + && (abs((Thermostat.temp_pi_error) / 10) <= (int16_t)Thermostat.temp_hysteresis) && (Thermostat.temp_measured_gradient > 0)) { - Thermostat.temp_pi_accum_error += Thermostat.temp_pi_error; // Reduce accumulator error 20% in each cycle Thermostat.temp_pi_accum_error *= 0.8; } @@ -454,13 +482,9 @@ void ThermostatCalculatePI() // AND temperature is rising else if ((Thermostat.temp_pi_error < 0) && (Thermostat.temp_measured_gradient > 0)) { - Thermostat.temp_pi_accum_error += Thermostat.temp_pi_error; // Reduce accumulator error 20% in each cycle Thermostat.temp_pi_accum_error *= 0.8; } - else { - Thermostat.temp_pi_accum_error += Thermostat.temp_pi_error; - } // Limit lower limit of acumErr to 0 if (Thermostat.temp_pi_accum_error < 0) { @@ -468,7 +492,7 @@ void ThermostatCalculatePI() } // Integral calculation - Thermostat.time_integral_pi = (((int32_t)Thermostat.temp_pi_accum_error * (int32_t)Thermostat.kI_pi) * (int32_t)((uint32_t)Thermostat.time_pi_cycle * 60)) / 100000; + Thermostat.time_integral_pi = (((int32_t)Thermostat.temp_pi_accum_error * (int32_t)Thermostat.kI_pi) * (int32_t)((uint32_t)Thermostat.time_pi_cycle * 60)) / 1000000; // Antiwindup of the integrator // If integral calculation is bigger than cycle time, adjust result @@ -496,7 +520,7 @@ void ThermostatCalculatePI() // If target value has been reached or we are over it]] if (Thermostat.temp_pi_error <= 0) { // If we are over the hysteresis or the gradient is positive - if ((abs(Thermostat.temp_pi_error) > Thermostat.temp_hysteresis) + if ((abs((Thermostat.temp_pi_error) / 10) > Thermostat.temp_hysteresis) || (Thermostat.temp_measured_gradient >= 0)) { Thermostat.time_total_pi = 0; } @@ -506,7 +530,7 @@ void ThermostatCalculatePI() // AND gradient is positive // then set value to 0 else if ((Thermostat.temp_pi_error > 0) - && (abs(Thermostat.temp_pi_error) <= Thermostat.temp_hysteresis) + && (abs((Thermostat.temp_pi_error) / 10) <= Thermostat.temp_hysteresis) && (Thermostat.temp_measured_gradient > 0)) { Thermostat.time_total_pi = 0; } @@ -533,7 +557,7 @@ void ThermostatCalculatePI() Thermostat.time_ctr_checkpoint = uptime + ((uint32_t)Thermostat.time_pi_cycle * 60); } -void ThermostatWorkAutomaticPI() +void ThermostatWorkAutomaticPI(void) { char result_chr[FLOATSZ]; // Remove! @@ -556,8 +580,9 @@ void ThermostatWorkAutomaticPI() } } -void ThermostatWorkAutomaticRampUp() +void ThermostatWorkAutomaticRampUp(void) { + int32_t aux_temp_delta; uint32_t time_in_rampup; int16_t temp_delta_rampup; From ba3457ed96d3518ee4e1f2ca652b58646cc339f6 Mon Sep 17 00:00:00 2001 From: Javier Arigita Date: Sun, 26 Apr 2020 18:00:19 +0200 Subject: [PATCH 365/547] Correct merge --- tasmota/tasmota.ino.cpp | 86679 ------------------------------- tasmota/xdrv_39_thermostat.ino | 2 +- 2 files changed, 1 insertion(+), 86680 deletions(-) delete mode 100644 tasmota/tasmota.ino.cpp diff --git a/tasmota/tasmota.ino.cpp b/tasmota/tasmota.ino.cpp deleted file mode 100644 index 7d5161bcb..000000000 --- a/tasmota/tasmota.ino.cpp +++ /dev/null @@ -1,86679 +0,0 @@ -# 1 "/var/folders/rp/3lmq1lbj0bl553yncz6xs3nr0000gn/T/tmpoDJJjn" -#include -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/tasmota.ino" -# 34 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/tasmota.ino" -#include -#include "tasmota_compat.h" -#include "tasmota_version.h" -#include "tasmota.h" -#include "my_user_config.h" -#ifdef USE_MQTT_TLS - #include -#endif -#include "tasmota_globals.h" -#include "i18n.h" -#include "tasmota_template.h" - -#ifdef ARDUINO_ESP8266_RELEASE_2_4_0 -#include "lwip/init.h" -#if LWIP_VERSION_MAJOR != 1 - #error Please use stable lwIP v1.4 -#endif -#endif - - -#include -#include -#include -#include -#ifdef USE_ARDUINO_OTA - #include - #ifndef USE_DISCOVERY - #define USE_DISCOVERY - #endif -#endif -#ifdef USE_DISCOVERY - #include -#endif -#ifdef USE_I2C - #include -#endif -#ifdef USE_SPI - #include -#endif - - -#include "settings.h" - - - - - -WiFiUDP PortUdp; - -unsigned long feature_drv1; -unsigned long feature_drv2; -unsigned long feature_sns1; -unsigned long feature_sns2; -unsigned long feature5; -unsigned long feature6; -unsigned long serial_polling_window = 0; -unsigned long state_second = 0; -unsigned long state_50msecond = 0; -unsigned long state_100msecond = 0; -unsigned long state_250msecond = 0; -unsigned long pulse_timer[MAX_PULSETIMERS] = { 0 }; -unsigned long blink_timer = 0; -unsigned long backlog_delay = 0; -power_t power = 0; -power_t last_power = 0; -power_t blink_power; -power_t blink_mask = 0; -power_t blink_powersave; -power_t latching_power = 0; -power_t rel_inverted = 0; -int serial_in_byte_counter = 0; -int ota_state_flag = 0; -int ota_result = 0; -int restart_flag = 0; -int wifi_state_flag = WIFI_RESTART; -int blinks = 201; -uint32_t uptime = 0; -uint32_t loop_load_avg = 0; -uint32_t global_update = 0; -uint32_t web_log_index = 1; -float global_temperature = 9999; -float global_humidity = 0; -float global_pressure = 0; -uint16_t tele_period = 9999; -uint16_t blink_counter = 0; -uint16_t seriallog_timer = 0; -uint16_t syslog_timer = 0; -int16_t save_data_counter; -RulesBitfield rules_flag; -uint8_t mqtt_cmnd_blocked = 0; -uint8_t mqtt_cmnd_blocked_reset = 0; -uint8_t state_250mS = 0; -uint8_t latching_relay_pulse = 0; -uint8_t ssleep; -uint8_t blinkspeed = 1; -uint8_t pin[GPIO_MAX]; -uint8_t active_device = 1; -uint8_t leds_present = 0; -uint8_t led_inverted = 0; -uint8_t led_power = 0; -uint8_t ledlnk_inverted = 0; -uint8_t pwm_inverted = 0; -uint8_t energy_flg = 0; -uint8_t light_flg = 0; -uint8_t light_type = 0; -uint8_t serial_in_byte; -uint8_t ota_retry_counter = OTA_ATTEMPTS; -uint8_t devices_present = 0; -uint8_t seriallog_level; -uint8_t syslog_level; -uint8_t my_module_type; -uint8_t my_adc0; -uint8_t last_source = 0; -uint8_t shutters_present = 0; -uint8_t prepped_loglevel = 0; - -bool serial_local = false; -bool fallback_topic_flag = false; -bool backlog_mutex = false; -bool interlock_mutex = false; -bool stop_flash_rotate = false; -bool blinkstate = false; - -bool pwm_present = false; -bool i2c_flg = false; -bool spi_flg = false; -bool soft_spi_flg = false; -bool ntp_force_sync = false; -bool is_8285 = false; -bool skip_light_fade; -myio my_module; -gpio_flag my_module_flag; -StateBitfield global_state; -char my_version[33]; -char my_image[33]; -char my_hostname[33]; -char mqtt_client[TOPSZ]; -char mqtt_topic[TOPSZ]; -char serial_in_buffer[INPUT_BUFFER_SIZE]; -char mqtt_data[MESSZ]; -char log_data[LOGSZ]; -char web_log[WEB_LOG_SIZE] = {'\0'}; -#ifdef SUPPORT_IF_STATEMENT - #include - LinkedList backlog; - #define BACKLOG_EMPTY (backlog.size() == 0) -#else - uint8_t backlog_index = 0; - uint8_t backlog_pointer = 0; - String backlog[MAX_BACKLOG]; - #define BACKLOG_EMPTY (backlog_pointer == backlog_index) -#endif -void setup(void); -void BacklogLoop(void); -void loop(void); -uint16_t SendMail(char *buffer); -uint32_t GetRtcSettingsCrc(void); -void RtcSettingsSave(void); -void RtcSettingsLoad(void); -bool RtcSettingsValid(void); -uint32_t GetRtcRebootCrc(void); -void RtcRebootSave(void); -void RtcRebootReset(void); -void RtcRebootLoad(void); -bool RtcRebootValid(void); -void SetFlashModeDout(void); -bool VersionCompatible(void); -void SettingsBufferFree(void); -bool SettingsBufferAlloc(void); -uint16_t GetCfgCrc16(uint8_t *bytes, uint32_t size); -uint16_t GetSettingsCrc(void); -uint32_t GetCfgCrc32(uint8_t *bytes, uint32_t size); -uint32_t GetSettingsCrc32(void); -void SettingsSaveAll(void); -void UpdateQuickPowerCycle(bool update); -uint32_t GetSettingsTextLen(void); -bool SettingsUpdateText(uint32_t index, const char* replace_me); -char* SettingsText(uint32_t index); -void UpdateBackwardCompatibility(void); -uint32_t GetSettingsAddress(void); -void SettingsSave(uint8_t rotate); -void SettingsLoad(void); -void EspErase(uint32_t start_sector, uint32_t end_sector); -void SettingsErase(uint8_t type); -void SettingsSdkErase(void); -void SettingsDefault(void); -void SettingsDefaultSet1(void); -void SettingsDefaultSet2(void); -void SettingsResetStd(void); -void SettingsResetDst(void); -void SettingsDefaultWebColor(void); -void SettingsEnableAllI2cDrivers(void); -void SettingsDelta(void); -void OsWatchTicker(void); -void OsWatchInit(void); -void OsWatchLoop(void); -bool OsWatchBlockedLoop(void); -uint32_t ResetReason(void); -String GetResetReason(void); -size_t strchrspn(const char *str1, int character); -char* subStr(char* dest, char* str, const char *delim, int index); -float CharToFloat(const char *str); -int TextToInt(char *str); -char* ulltoa(unsigned long long value, char *str, int radix); -char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween); -char* Uint64toHex(uint64_t value, char *str, uint16_t bits); -char* dtostrfd(double number, unsigned char prec, char *s); -char* Unescape(char* buffer, uint32_t* size); -char* RemoveSpace(char* p); -char* ReplaceCommaWithDot(char* p); -char* LowerCase(char* dest, const char* source); -char* UpperCase(char* dest, const char* source); -char* UpperCase_P(char* dest, const char* source); -char* Trim(char* p); -char* RemoveAllSpaces(char* p); -char* NoAlNumToUnderscore(char* dest, const char* source); -char IndexSeparator(void); -void SetShortcutDefault(void); -uint8_t Shortcut(void); -bool ValidIpAddress(const char* str); -bool ParseIp(uint32_t* addr, const char* str); -uint32_t ParseParameters(uint32_t count, uint32_t *params); -bool NewerVersion(char* version_str); -char* GetPowerDevice(char* dest, uint32_t idx, size_t size, uint32_t option); -char* GetPowerDevice(char* dest, uint32_t idx, size_t size); -void GetEspHardwareType(void); -String GetDeviceHardware(void); -float ConvertTemp(float c); -float ConvertTempToCelsius(float c); -char TempUnit(void); -float ConvertHumidity(float h); -float CalcTempHumToDew(float t, float h); -float ConvertPressure(float p); -String PressureUnit(void); -float ConvertSpeed(float s); -String SpeedUnit(void); -void ResetGlobalValues(void); -uint32_t SqrtInt(uint32_t num); -uint32_t RoundSqrtInt(uint32_t num); -char* GetTextIndexed(char* destination, size_t destination_size, uint32_t index, const char* haystack); -int GetCommandCode(char* destination, size_t destination_size, const char* needle, const char* haystack); -int GetStateNumber(char *state_text); -String GetSerialConfig(void); -void SetSerialBegin(); -void SetSerialConfig(uint32_t serial_config); -void SetSerialBaudrate(uint32_t baudrate); -void SetSerial(uint32_t baudrate, uint32_t serial_config); -void ClaimSerial(void); -void SerialSendRaw(char *codes); -uint32_t GetHash(const char *buffer, size_t size); -void ShowSource(uint32_t source); -void WebHexCode(uint32_t i, const char* code); -uint32_t WebColor(uint32_t i); -char* ResponseGetTime(uint32_t format, char* time_str); -int Response_P(const char* format, ...); -int ResponseTime_P(const char* format, ...); -int ResponseAppend_P(const char* format, ...); -int ResponseAppendTimeFormat(uint32_t format); -int ResponseAppendTime(void); -int ResponseAppendTHD(float f_temperature, float f_humidity); -int ResponseJsonEnd(void); -int ResponseJsonEndEnd(void); -void DigitalWrite(uint32_t gpio_pin, uint32_t state); -uint8_t ModuleNr(void); -bool ValidTemplateModule(uint32_t index); -bool ValidModule(uint32_t index); -String AnyModuleName(uint32_t index); -String ModuleName(void); -void ModuleGpios(myio *gp); -gpio_flag ModuleFlag(void); -void ModuleDefault(uint32_t module); -void SetModuleType(void); -bool FlashPin(uint32_t pin); -uint8_t ValidPin(uint32_t pin, uint32_t gpio); -bool ValidGPIO(uint32_t pin, uint32_t gpio); -bool ValidAdc(void); -bool GetUsedInModule(uint32_t val, uint8_t *arr); -bool JsonTemplate(const char* dataBuf); -void TemplateJson(void); -inline int32_t TimeDifference(uint32_t prev, uint32_t next); -int32_t TimePassedSince(uint32_t timestamp); -bool TimeReached(uint32_t timer); -void SetNextTimeInterval(unsigned long& timer, const unsigned long step); -int32_t TimePassedSinceUsec(uint32_t timestamp); -bool TimeReachedUsec(uint32_t timer); -bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size); -bool I2cValidRead8(uint8_t *data, uint8_t addr, uint8_t reg); -bool I2cValidRead16(uint16_t *data, uint8_t addr, uint8_t reg); -bool I2cValidReadS16(int16_t *data, uint8_t addr, uint8_t reg); -bool I2cValidRead16LE(uint16_t *data, uint8_t addr, uint8_t reg); -bool I2cValidReadS16_LE(int16_t *data, uint8_t addr, uint8_t reg); -bool I2cValidRead24(int32_t *data, uint8_t addr, uint8_t reg); -uint8_t I2cRead8(uint8_t addr, uint8_t reg); -uint16_t I2cRead16(uint8_t addr, uint8_t reg); -int16_t I2cReadS16(uint8_t addr, uint8_t reg); -uint16_t I2cRead16LE(uint8_t addr, uint8_t reg); -int16_t I2cReadS16_LE(uint8_t addr, uint8_t reg); -int32_t I2cRead24(uint8_t addr, uint8_t reg); -bool I2cWrite(uint8_t addr, uint8_t reg, uint32_t val, uint8_t size); -bool I2cWrite8(uint8_t addr, uint8_t reg, uint16_t val); -bool I2cWrite16(uint8_t addr, uint8_t reg, uint16_t val); -int8_t I2cReadBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len); -int8_t I2cWriteBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len); -void I2cScan(char *devs, unsigned int devs_len); -void I2cSetActiveFound(uint32_t addr, const char *types); -bool I2cActive(uint32_t addr); -bool I2cSetDevice(uint32_t addr); -void SetSeriallog(uint32_t loglevel); -void SetSyslog(uint32_t loglevel); -void GetLog(uint32_t idx, char** entry_pp, size_t* len_p); -void Syslog(void); -void AddLog(uint32_t loglevel); -void AddLog_P(uint32_t loglevel, const char *formatP); -void AddLog_P(uint32_t loglevel, const char *formatP, const char *formatP2); -void PrepLog_P2(uint32_t loglevel, PGM_P formatP, ...); -void AddLog_P2(uint32_t loglevel, PGM_P formatP, ...); -void AddLog_Debug(PGM_P formatP, ...); -void AddLogBuffer(uint32_t loglevel, uint8_t *buffer, uint32_t count); -void AddLogSerial(uint32_t loglevel); -void AddLogMissed(const char *sensor, uint32_t misses); -void ButtonPullupFlag(uint8 button_bit); -void ButtonInvertFlag(uint8 button_bit); -void ButtonInit(void); -uint8_t ButtonSerial(uint8_t serial_in_byte); -void ButtonHandler(void); -void ButtonLoop(void); -void ButtonPullupFlag(uint8 button_bit); -void ButtonInvertFlag(uint8 button_bit); -void ButtonInit(void); -uint8_t ButtonSerial(uint8_t serial_in_byte); -void ButtonHandler(void); -void MqttButtonTopic(uint8_t button_id, uint8_t action, uint8_t hold); -void ButtonLoop(void); -void ResponseCmndNumber(int value); -void ResponseCmndFloat(float value, uint32_t decimals); -void ResponseCmndIdxNumber(int value); -void ResponseCmndChar_P(const char* value); -void ResponseCmndChar(const char* value); -void ResponseCmndStateText(uint32_t value); -void ResponseCmndDone(void); -void ResponseCmndIdxChar(const char* value); -void ResponseCmndAll(uint32_t text_index, uint32_t count); -void ExecuteCommand(const char *cmnd, uint32_t source); -void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len); -void CmndBacklog(void); -void CmndDelay(void); -void CmndPower(void); -void CmndStatus(void); -void CmndState(void); -void CmndTempOffset(void); -void CmndHumOffset(void); -void CmndGlobalTemp(void); -void CmndGlobalHum(void); -void CmndSleep(void); -void CmndUpgrade(void); -void CmndOtaUrl(void); -void CmndSeriallog(void); -void CmndRestart(void); -void CmndPowerOnState(void); -void CmndPulsetime(void); -void CmndBlinktime(void); -void CmndBlinkcount(void); -void CmndSavedata(void); -void CmndSetoption(void); -void CmndTemperatureResolution(void); -void CmndHumidityResolution(void); -void CmndPressureResolution(void); -void CmndPowerResolution(void); -void CmndVoltageResolution(void); -void CmndFrequencyResolution(void); -void CmndCurrentResolution(void); -void CmndEnergyResolution(void); -void CmndWeightResolution(void); -void CmndSpeedUnit(void); -void CmndModule(void); -void CmndModules(void); -void CmndGpio(void); -void CmndGpios(void); -void CmndTemplate(void); -void CmndPwm(void); -void CmndPwmfrequency(void); -void CmndPwmrange(void); -void CmndButtonDebounce(void); -void CmndSwitchDebounce(void); -void CmndBaudrate(void); -void CmndSerialConfig(void); -void CmndSerialSend(void); -void CmndSerialDelimiter(void); -void CmndSyslog(void); -void CmndLoghost(void); -void CmndLogport(void); -void CmndIpAddress(void); -void CmndNtpServer(void); -void CmndAp(void); -void CmndSsid(void); -void CmndPassword(void); -void CmndHostname(void); -void CmndWifiConfig(void); -void CmndFriendlyname(void); -void CmndSwitchMode(void); -void CmndInterlock(void); -void CmndTeleperiod(void); -void CmndReset(void); -void CmndTime(void); -void CmndTimezone(void); -void CmndTimeStdDst(uint32_t ts); -void CmndTimeStd(void); -void CmndTimeDst(void); -void CmndAltitude(void); -void CmndLedPower(void); -void CmndLedState(void); -void CmndLedMask(void); -void CmndWifiPower(void); -void CmndI2cScan(void); -void CmndI2cDriver(void); -void CmndDevGroupName(void); -void CmndDevGroupSend(void); -void CmndDevGroupShare(void); -void CmndDevGroupStatus(void); -void CmndSensor(void); -void CmndDriver(void); -void CmndCrash(void); -void CmndWDT(void); -void CmndBlockedLoop(void); -void CrashDumpClear(void); -bool CrashFlag(void); -void CrashDump(void); -void DeviceGroupsInit(void); -bool DeviceGroupItemShared(bool incoming, uint8_t item); -void SendDeviceGroupPacket(IPAddress ip, char * packet, int len, const char * label); -void _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType message_type, ...); -void ProcessDeviceGroupMessage(char * packet, int packet_length); -void DeviceGroupStatus(uint8_t device_group_index); -void DeviceGroupsLoop(void); -void SettingsErase(uint8_t type); -void SettingsLoad(const char *sNvsName, const char *sName, void *pSettings, unsigned nSettingsLen); -void SettingsSave(const char *sNvsName, const char *sName, const void *pSettings, unsigned nSettingsLen); -void ESP32_flashRead(uint32_t offset, uint32_t *data, size_t size); -void ESP32_flashReadHeader(uint32_t offset, uint32_t *data, size_t size); -void SettingsSaveMain(const void *pSettings, unsigned nSettingsLen); -void SettingsLoadUpg(void *pSettings, unsigned nSettingsLen); -void SettingsLoadUpgH(void *pSettings, unsigned nSettingsLen); -void SntpInit(); -uint32_t SntpGetCurrentTimestamp(void); -void CrashDump(void); -bool CrashFlag(void); -void CrashDumpClear(void); -void CmndCrash(void); -void CmndWDT(void); -void CmndBlockedLoop(void); -static bool spiflash_is_ready(void); -static void spi_write_enable(void); -bool EsptoolEraseSector(uint32_t sector); -void EsptoolErase(uint32_t start_sector, uint32_t end_sector); -void GetFeatures(void); -float fmodf(float x, float y); -double FastPrecisePow(double a, double b); -float FastPrecisePowf(const float x, const float y); -double TaylorLog(double x); -inline float sinf(float x); -inline float cosf(float x); -inline float tanf(float x); -inline float atanf(float x); -inline float asinf(float x); -inline float acosf(float x); -inline float sqrtf(float x); -inline float powf(float x, float y); -float cos_52s(float x); -float cos_52(float x); -float sin_52(float x); -float tan_56s(float x); -float tan_56(float x); -float atan_66s(float x); -float atan_66(float x); -float asinf1(float x); -float acosf1(float x); -float sqrt1(const float x); -uint16_t changeUIntScale(uint16_t inum, uint16_t ifrom_min, uint16_t ifrom_max, - uint16_t ito_min, uint16_t ito_max); -void* memchr(const void* ptr, int value, size_t num); -size_t strcspn(const char *str1, const char *str2); -char* strpbrk(const char *s1, const char *s2); -void* memmove_P(void *dest, const void *src, size_t n); -void resetPins(); -void update_rotary(void); -bool RotaryButtonPressed(void); -void RotaryInit(void); -void RotaryHandler(void); -void RotaryLoop(void); -uint32_t UtcTime(void); -uint32_t LocalTime(void); -uint32_t Midnight(void); -bool MidnightNow(void); -bool IsDst(void); -String GetBuildDateAndTime(void); -String GetMinuteTime(uint32_t minutes); -String GetTimeZone(void); -String GetDuration(uint32_t time); -String GetDT(uint32_t time); -String GetDateAndTime(uint8_t time_type); -uint32_t UpTime(void); -uint32_t MinutesUptime(void); -String GetUptime(void); -uint32_t MinutesPastMidnight(void); -void BreakTime(uint32_t time_input, TIME_T &tm); -uint32_t MakeTime(TIME_T &tm); -uint32_t RuleToTime(TimeRule r, int yr); -void RtcSecond(void); -void RtcSetTime(uint32_t epoch); -void RtcInit(void); -String GetStatistics(void); -String GetStatistics(void); -void SwitchPullupFlag(uint16 switch_bit); -void SwitchSetVirtual(uint32_t index, uint8_t state); -uint8_t SwitchGetVirtual(uint32_t index); -uint8_t SwitchLastState(uint32_t index); -bool SwitchState(uint32_t index); -void SwitchProbe(void); -void SwitchInit(void); -void SwitchHandler(uint8_t mode); -void SwitchLoop(void); -char* Format(char* output, const char* input, int size); -char* GetOtaUrl(char *otaurl, size_t otaurl_size); -char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopic); -char* GetGroupTopic_P(char *stopic, const char* subtopic, uint32_t itopic); -char* GetFallbackTopic_P(char *stopic, const char* subtopic); -char* GetStateText(uint32_t state); -void SetLatchingRelay(power_t lpower, uint32_t state); -void SetDevicePower(power_t rpower, uint32_t source); -void RestorePower(bool publish_power, uint32_t source); -void SetAllPower(uint32_t state, uint32_t source); -void SetPowerOnState(void); -void SetLedPowerIdx(uint32_t led, uint32_t state); -void SetLedPower(uint32_t state); -void SetLedPowerAll(uint32_t state); -void SetLedLink(uint32_t state); -void SetPulseTimer(uint32_t index, uint32_t time); -uint32_t GetPulseTimer(uint32_t index); -bool SendKey(uint32_t key, uint32_t device, uint32_t state); -void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source); -void StopAllPowerBlink(void); -void MqttShowPWMState(void); -void MqttShowState(void); -void MqttPublishTeleState(void); -void TempHumDewShow(bool json, bool pass_on, const char *types, float f_temperature, float f_humidity); -bool MqttShowSensor(void); -void MqttPublishSensor(void); -void PerformEverySecond(void); -void Every100mSeconds(void); -void Every250mSeconds(void); -void ArduinoOTAInit(void); -void ArduinoOtaLoop(void); -void SerialInput(void); -void ResetPwm(void); -void GpioInit(void); -bool UdpDisconnect(void); -bool UdpConnect(void); -void PollUdp(void); -int WifiGetRssiAsQuality(int rssi); -bool WifiConfigCounter(void); -void WifiConfig(uint8_t type); -void WifiSetMode(WiFiMode_t wifi_mode); -void WiFiSetSleepMode(void); -void WifiBegin(uint8_t flag, uint8_t channel); -void WifiBeginAfterScan(void); -uint16_t WifiLinkCount(void); -String WifiDowntime(void); -void WifiSetState(uint8_t state); -bool WifiCheckIPv6(void); -String WifiGetIPv6(void); -bool WifiCheckIPAddrStatus(void); -void WifiCheckIp(void); -void WifiCheck(uint8_t param); -int WifiState(void); -String WifiGetOutputPower(void); -void WifiSetOutputPower(void); -void WifiConnect(void); -void EspRestart(void); -void stationKeepAliveNow(void); -void wifiKeepAlive(void); -static void WebGetArg(const char* arg, char* out, size_t max); -static bool WifiIsInManagerMode(); -void ShowWebSource(uint32_t source); -void ExecuteWebCommand(char* svalue, uint32_t source); -void StartWebserver(int type, IPAddress ipweb); -void StopWebserver(void); -void WifiManagerBegin(bool reset_only); -void PollDnsWebserver(void); -bool WebAuthenticate(void); -void HttpHeaderCors(void); -void WSHeaderSend(void); -void WSSend(int code, int ctype, const String& content); -void WSContentBegin(int code, int ctype); -void _WSContentSend(const String& content); -void WSContentFlush(void); -void _WSContentSendBuffer(void); -void WSContentSend_P(const char* formatP, ...); -void WSContentSend_PD(const char* formatP, ...); -void WSContentStart_P(const char* title, bool auth); -void WSContentStart_P(const char* title); -void WSContentSendStyle_P(const char* formatP, ...); -void WSContentSendStyle(void); -void WSContentButton(uint32_t title_index); -void WSContentSpaceButton(uint32_t title_index); -void WSContentSend_THD(const char *types, float f_temperature, float f_humidity); -void WSContentEnd(void); -void WSContentStop(void); -void WebRestart(uint32_t type); -void HandleWifiLogin(void); -void WebSliderColdWarm(void); -void HandleRoot(void); -bool HandleRootStatusRefresh(void); -int32_t IsShutterWebButton(uint32_t idx); -void HandleConfiguration(void); -void HandleTemplateConfiguration(void); -void TemplateSaveSettings(void); -void HandleModuleConfiguration(void); -void ModuleSaveSettings(void); -String HtmlEscape(const String unescaped); -void HandleWifiConfiguration(void); -void WifiSaveSettings(void); -void HandleLoggingConfiguration(void); -void LoggingSaveSettings(void); -void HandleOtherConfiguration(void); -void OtherSaveSettings(void); -void HandleBackupConfiguration(void); -void HandleResetConfiguration(void); -void HandleRestoreConfiguration(void); -void HandleInformation(void); -void HandleUpgradeFirmware(void); -void HandleUpgradeFirmwareStart(void); -void HandleUploadDone(void); -void HandleUploadLoop(void); -void HandlePreflightRequest(void); -void HandleHttpCommand(void); -void HandleConsole(void); -void HandleConsoleRefresh(void); -void HandleNotFound(void); -bool CaptivePortal(void); -String UrlEncode(const String& text); -int WebSend(char *buffer); -bool JsonWebColor(const char* dataBuf); -void CmndEmulation(void); -void CmndSendmail(void); -void CmndWebServer(void); -void CmndWebPassword(void); -void CmndWeblog(void); -void CmndWebRefresh(void); -void CmndWebSend(void); -void CmndWebColor(void); -void CmndWebSensor(void); -void CmndWebButton(void); -void CmndCors(void); -bool Xdrv01(uint8_t function); -bool is_fingerprint_mono_value(uint8_t finger[20], uint8_t value); -void MakeValidMqtt(uint32_t option, char* str); -void MqttDiscoverServer(void); -void MqttInit(void); -bool MqttIsConnected(void); -void MqttDisconnect(void); -void MqttSubscribeLib(const char *topic); -void MqttUnsubscribeLib(const char *topic); -bool MqttPublishLib(const char* topic, bool retained); -void MqttDataHandler(char* mqtt_topic, uint8_t* mqtt_data, unsigned int data_len); -void MqttRetryCounter(uint8_t value); -void MqttSubscribe(const char *topic); -void MqttUnsubscribe(const char *topic); -void MqttPublishLogging(const char *mxtime); -void MqttPublish(const char* topic, bool retained); -void MqttPublish(const char* topic); -void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retained); -void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic); -void MqttPublishTeleSensor(void); -void MqttPublishPowerState(uint32_t device); -void MqttPublishAllPowerState(void); -void MqttPublishPowerBlinkState(uint32_t device); -uint16_t MqttConnectCount(void); -void MqttDisconnected(int state); -void MqttConnected(void); -void MqttReconnect(void); -void MqttCheck(void); -bool KeyTopicActive(uint32_t key); -void CmndMqttFingerprint(void); -void CmndMqttUser(void); -void CmndMqttPassword(void); -void CmndMqttlog(void); -void CmndMqttHost(void); -void CmndMqttPort(void); -void CmndMqttRetry(void); -void CmndStateText(void); -void CmndMqttClient(void); -void CmndFullTopic(void); -void CmndPrefix(void); -void CmndPublish(void); -void CmndGroupTopic(void); -void CmndTopic(void); -void CmndButtonTopic(void); -void CmndSwitchTopic(void); -void CmndButtonRetain(void); -void CmndSwitchRetain(void); -void CmndPowerRetain(void); -void CmndSensorRetain(void); -inline void TlsEraseBuffer(uint8_t *buffer); -void loadTlsDir(void); -void CmndTlsKey(void); -uint32_t bswap32(uint32_t x); -void CmndTlsDump(void); -void HandleMqttConfiguration(void); -void MqttSaveSettings(void); -bool Xdrv02(uint8_t function); -bool EnergyTariff1Active(); -void EnergyUpdateToday(void); -void EnergyUpdateTotal(float value, bool kwh); -void Energy200ms(void); -void EnergySaveState(void); -bool EnergyMargin(bool type, uint16_t margin, uint16_t value, bool &flag, bool &save_flag); -void EnergyMarginCheck(void); -void EnergyMqttShow(void); -void EnergyEverySecond(void); -void EnergyCommandCalResponse(uint32_t nvalue); -void CmndEnergyReset(void); -void CmndTariff(void); -void CmndPowerCal(void); -void CmndVoltageCal(void); -void CmndCurrentCal(void); -void CmndPowerSet(void); -void CmndVoltageSet(void); -void CmndCurrentSet(void); -void CmndFrequencySet(void); -void CmndModuleAddress(void); -void CmndPowerDelta(void); -void CmndPowerLow(void); -void CmndPowerHigh(void); -void CmndVoltageLow(void); -void CmndVoltageHigh(void); -void CmndCurrentLow(void); -void CmndCurrentHigh(void); -void CmndMaxPower(void); -void CmndMaxPowerHold(void); -void CmndMaxPowerWindow(void); -void CmndSafePower(void); -void CmndSafePowerHold(void); -void CmndSafePowerWindow(void); -void CmndMaxEnergy(void); -void CmndMaxEnergyStart(void); -void EnergySnsInit(void); -void EnergyShow(bool json); -bool Xdrv03(uint8_t function); -bool Xsns03(uint8_t function); -power_t LightPower(void); -power_t LightPowerIRAM(void); -uint8_t LightDevice(void); -static uint32_t min3(uint32_t a, uint32_t b, uint32_t c); -uint16_t change8to10(uint8_t v); -uint8_t change10to8(uint16_t v); -uint16_t ledGamma_internal(uint16_t v, const struct gamma_table_t *gt_ptr); -uint16_t ledGammaReverse_internal(uint16_t vg, const struct gamma_table_t *gt_ptr); -uint16_t ledGamma10_10(uint16_t v); -uint16_t ledGamma10(uint8_t v); -uint8_t ledGamma(uint8_t v); -void LightPwmOffset(uint32_t offset); -bool LightModuleInit(void); -void LightCalcPWMRange(void); -void LightInit(void); -void LightUpdateColorMapping(void); -uint8_t LightGetDimmer(uint8_t dimmer); -void LightSetDimmer(uint8_t dimmer); -void LightGetHSB(uint16_t *hue, uint8_t *sat, uint8_t *bri); -void LightHsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b); -uint8_t LightGetBri(uint8_t device); -void LightSetBri(uint8_t device, uint8_t bri); -void LightSetColorTemp(uint16_t ct); -uint16_t LightGetColorTemp(void); -void LightSetSignal(uint16_t lo, uint16_t hi, uint16_t value); -void LightPowerOn(void); -void LightState(uint8_t append); -void LightSetPaletteEntry(void); -void LightCycleColor(int8_t direction); -void LightSetPower(void); -void LightAnimate(void); -bool isChannelGammaCorrected(uint32_t channel); -bool isChannelCT(uint32_t channel); -uint16_t fadeGamma(uint32_t channel, uint16_t v); -uint16_t fadeGammaReverse(uint32_t channel, uint16_t vg); -bool LightApplyFade(void); -void LightApplyPower(uint8_t new_color[LST_MAX], power_t power); -void LightSetOutputs(const uint16_t *cur_col_10); -void calcGammaMultiChannels(uint16_t cur_col_10[5]); -void calcGammaBulbs(uint16_t cur_col_10[5]); -void LightSendDeviceGroupStatus(bool force); -void LightHandleDevGroupItem(void); -void LightUpdateScheme(void); -bool LightColorEntry(char *buffer, uint32_t buffer_length); -void CmndSupportColor(void); -void CmndColor(void); -void CmndWhite(void); -void CmndChannel(void); -void CmndHsbColor(void); -void CmndScheme(void); -void CmndWakeup(void); -void CmndColorTemperature(void); -void CmndDimmer(void); -void CmndDimmerRange(void); -void CmndLedTable(void); -void CmndRgbwwTable(void); -void CmndFade(void); -void CmndSpeed(void); -void CmndWakeupDuration(void); -void CmndPalette(void); -void CmndUndocA(void); -bool Xdrv04(uint8_t function); -void IrSendInit(void); -void IrReceiveUpdateThreshold(void); -void IrReceiveInit(void); -void IrReceiveCheck(void); -uint32_t IrRemoteCmndIrSendJson(void); -void CmndIrSend(void); -void IrRemoteCmndResponse(uint32_t error); -bool Xdrv05(uint8_t function); -void IrSendInit(void); -uint8_t reverseBitsInByte(uint8_t b); -uint64_t reverseBitsInBytes64(uint64_t b); -void IrReceiveUpdateThreshold(void); -void IrReceiveInit(void); -String sendIRJsonState(const struct decode_results &results); -void IrReceiveCheck(void); -String listSupportedProtocols(bool hvac); -uint32_t IrRemoteCmndIrHvacJson(void); -void CmndIrHvac(void); -uint32_t IrRemoteCmndIrSendJson(void); -uint32_t IrRemoteCmndIrSendRaw(void); -void CmndIrSend(void); -void IrRemoteCmndResponse(uint32_t error); -bool Xdrv05(uint8_t function); -ssize_t rf_find_hex_record_start(uint8_t *buf, size_t size); -ssize_t rf_find_hex_record_end(uint8_t *buf, size_t size); -ssize_t rf_glue_remnant_with_new_data_and_write(const uint8_t *remnant_data, uint8_t *new_data, size_t new_data_len); -ssize_t rf_decode_and_write(uint8_t *record, size_t size); -ssize_t rf_search_and_write(uint8_t *buf, size_t size); -uint8_t rf_erase_flash(void); -uint8_t SnfBrUpdateInit(void); -void SonoffBridgeReceivedRaw(void); -void SonoffBridgeLearnFailed(void); -void SonoffBridgeReceived(void); -bool SonoffBridgeSerialInput(void); -void SonoffBridgeSendCommand(uint8_t code); -void SonoffBridgeSendAck(void); -void SonoffBridgeSendCode(uint32_t code); -void SonoffBridgeSend(uint8_t idx, uint8_t key); -void SonoffBridgeLearn(uint8_t key); -void CmndRfBridge(void); -void CmndRfKey(void); -void CmndRfRaw(void); -bool Xdrv06(uint8_t function); -int DomoticzBatteryQuality(void); -int DomoticzRssiQuality(void); -void MqttPublishDomoticzFanState(void); -void DomoticzUpdateFanState(void); -void MqttPublishDomoticzPowerState(uint8_t device); -void DomoticzUpdatePowerState(uint8_t device); -void DomoticzMqttUpdate(void); -void DomoticzMqttSubscribe(void); -bool DomoticzMqttData(void); -bool DomoticzSendKey(uint8_t key, uint8_t device, uint8_t state, uint8_t svalflg); -uint8_t DomoticzHumidityState(float h); -void DomoticzSensor(uint8_t idx, char *data); -void DomoticzSensor(uint8_t idx, uint32_t value); -void DomoticzTempHumPressureSensor(float temp, float hum, float baro); -void DomoticzSensorPowerEnergy(int power, char *energy); -void DomoticzSensorP1SmartMeter(char *usage1, char *usage2, char *return1, char *return2, int power); -void CmndDomoticzIdx(void); -void CmndDomoticzKeyIdx(void); -void CmndDomoticzSwitchIdx(void); -void CmndDomoticzSensorIdx(void); -void CmndDomoticzUpdateTimer(void); -void HandleDomoticzConfiguration(void); -void DomoticzSaveSettings(void); -bool Xdrv07(uint8_t function); -void SerialBridgeInput(void); -void SerialBridgeInit(void); -void CmndSSerialSend(void); -void CmndSBaudrate(void); -bool Xdrv08(uint8_t function); -float JulianischesDatum(void); -float InPi(float x); -float eps(float T); -float BerechneZeitgleichung(float *DK,float T); -void DuskTillDawn(uint8_t *hour_up,uint8_t *minute_up, uint8_t *hour_down, uint8_t *minute_down); -void ApplyTimerOffsets(Timer *duskdawn); -String GetSun(uint32_t dawn); -uint16_t SunMinutes(uint32_t dawn); -void TimerSetRandomWindow(uint32_t index); -void TimerSetRandomWindows(void); -void TimerEverySecond(void); -void PrepShowTimer(uint32_t index); -void CmndTimer(void); -void CmndTimers(void); -void CmndLongitude(void); -void CmndLatitude(void); -void HandleTimerConfiguration(void); -void TimerSaveSettings(void); -bool Xdrv09(uint8_t function); -bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule); -int8_t parseCompareExpression(String &expr, String &leftExpr, String &rightExpr); -void RulesVarReplace(String &commands, const String &sfind, const String &replace); -bool RuleSetProcess(uint8_t rule_set, String &event_saved); -bool RulesProcessEvent(char *json_event); -bool RulesProcess(void); -void RulesInit(void); -void RulesEvery50ms(void); -void RulesEvery100ms(void); -void RulesEverySecond(void); -void RulesSaveBeforeRestart(void); -void RulesSetPower(void); -void RulesTeleperiod(void); -bool RulesMqttData(void); -void CmndSubscribe(void); -void CmndUnsubscribe(void); -bool findNextNumber(char * &pNumber, float &value); -bool findNextVariableValue(char * &pVarname, float &value); -bool findNextObjectValue(char * &pointer, float &value); -bool findNextOperator(char * &pointer, int8_t &op); -float calculateTwoValues(float v1, float v2, uint8_t op); -float evaluateExpression(const char * expression, unsigned int len); -void CmndIf(void); -bool evaluateComparisonExpression(const char *expression, int len); -bool findNextLogicOperator(char * &pointer, int8_t &op); -bool findNextLogicObjectValue(char * &pointer, bool &value); -bool evaluateLogicalExpression(const char * expression, int len); -int8_t findIfBlock(char * &pointer, int &lenWord, int8_t block_type); -void ExecuteCommandBlock(const char * commands, int len); -void ProcessIfStatement(const char* statements); -void RulesPreprocessCommand(char *pCommands); -void CmndRule(void); -void CmndRuleTimer(void); -void CmndEvent(void); -void CmndVariable(void); -void CmndMemory(void); -void CmndCalcResolution(void); -void CmndAddition(void); -void CmndSubtract(void); -void CmndMultiply(void); -void CmndScale(void); -float map_double(float x, float in_min, float in_max, float out_min, float out_max); -bool Xdrv10(uint8_t function); -void SaveFile(const char *name,const uint8_t *buf,uint32_t len); -void LoadFile(const char *name,uint8_t *buf,uint32_t len); -void ScriptEverySecond(void); -void RulesTeleperiod(void); -int16_t Init_Scripter(void); -void ws2812_set_array(float *array ,uint8_t len); -float median_array(float *array,uint8_t len); -float Get_MFVal(uint8_t index,uint8_t bind); -void Set_MFVal(uint8_t index,uint8_t bind,float val); -float Get_MFilter(uint8_t index); -void Set_MFilter(uint8_t index, float invar); -float DoMedian5(uint8_t index, float in); -uint32_t HSVToRGB(uint16_t hue, uint8_t saturation, uint8_t value); -uint16_t GetStack(void); -uint16_t GetStack(void); -uint16_t GetStack(void); -void Replace_Cmd_Vars(char *srcbuf,char *dstbuf,uint16_t dstsize); -void toLog(const char *str); -void toLogN(const char *cp,uint8_t len); -void toLogEOL(const char *s1,const char *str); -void toSLog(const char *str); -int16_t Run_Scripter(const char *type, int8_t tlen, char *js); -void ScripterEvery100ms(void); -void Scripter_save_pvars(void); -void ListDir(char *path, uint8_t depth); -void Script_FileUploadConfiguration(void); -void ScriptFileUploadSuccess(void); -void script_upload(void); -uint8_t DownloadFile(char *file); -void HandleScriptTextareaConfiguration(void); -void HandleScriptConfiguration(void); -void ScriptSaveSettings(void); -void Script_HueStatus(String *response, uint16_t hue_devs); -void Script_Check_Hue(String *response); -void Script_Handle_Hue(String *path); -bool Script_SubCmd(void); -void execute_script(char *script); -bool ScriptCommand(void); -uint16_t xFAT_DATE(uint16_t year, uint8_t month, uint8_t day); -uint16_t xFAT_TIME(uint8_t hour, uint8_t minute, uint8_t second); -void dateTime(uint16_t* date, uint16_t* time); -bool ScriptMqttData(void); -String ScriptSubscribe(const char *data, int data_len); -String ScriptUnsubscribe(const char * data, int data_len); -void Script_Check_HTML_Setvars(void); -void ScriptGetVarname(char *nbuf,char *sp, uint32_t blen); -void ScriptWebShow(void); -void ScriptJsonAppend(void); -bool Xdrv10(uint8_t function); -void KNX_ADD_GA( uint8_t GAop, uint8_t GA_FNUM, uint8_t GA_AREA, uint8_t GA_FDEF ); -void KNX_DEL_GA( uint8_t GAnum ); -void KNX_ADD_CB( uint8_t CBop, uint8_t CB_FNUM, uint8_t CB_AREA, uint8_t CB_FDEF ); -void KNX_DEL_CB( uint8_t CBnum ); -bool KNX_CONFIG_NOT_MATCH(void); -void KNXStart(void); -void KNX_INIT(void); -void KNX_CB_Action(message_t const &msg, void *arg); -void KnxUpdatePowerState(uint8_t device, power_t state); -void KnxSendButtonPower(void); -void KnxSensor(uint8_t sensor_type, float value); -void HandleKNXConfiguration(void); -void KNX_Save_Settings(void); -void CmndKnxTxCmnd(void); -void CmndKnxTxVal(void); -void CmndKnxEnabled(void); -void CmndKnxEnhanced(void); -void CmndKnxPa(void); -void CmndKnxGa(void); -void CmndKnxCb(void); -bool Xdrv11(uint8_t function); -void TryResponseAppend_P(const char *format, ...); -void HAssAnnounceRelayLight(void); -void HAssAnnouncerTriggers(uint8_t device, uint8_t present, uint8_t key, uint8_t toggle, uint8_t hold); -void HAssAnnouncerBinSensors(uint8_t device, uint8_t present, uint8_t dual, uint8_t toggle, uint8_t pir); -void HAssAnnounceSwitches(void); -void HAssAnnounceButtons(void); -void HAssAnnounceSensor(const char *sensorname, const char *subsensortype, const char *MultiSubName, uint8_t subqty, uint8_t subidx, uint8_t nested, const char* SubKey); -void HAssAnnounceSensors(void); -void HAssAnnounceStatusSensor(void); -void HAssPublishStatus(void); -void HAssDiscovery(void); -void HAssDiscover(void); -void HAssAnyKey(void); -bool Xdrv12(uint8_t function); -void DisplayInit(uint8_t mode); -void DisplayClear(void); -void DisplayDrawHLine(uint16_t x, uint16_t y, int16_t len, uint16_t color); -void DisplayDrawVLine(uint16_t x, uint16_t y, int16_t len, uint16_t color); -void DisplayDrawLine(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color); -void DisplayDrawCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color); -void DisplayDrawFilledCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color); -void DisplayDrawRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color); -void DisplayDrawFilledRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color); -void DisplayDrawFrame(void); -void DisplaySetSize(uint8_t size); -void DisplaySetFont(uint8_t font); -void DisplaySetRotation(uint8_t rotation); -void DisplayDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag); -void DisplayOnOff(uint8_t on); -uint8_t fatoiv(char *cp,float *res); -uint8_t atoiv(char *cp, int16_t *res); -uint8_t atoiV(char *cp, uint16_t *res); -void alignright(char *string); -uint32_t decode_te(char *line); -void DisplayText(void); -void DisplayClearScreenBuffer(void); -void DisplayFreeScreenBuffer(void); -void DisplayAllocScreenBuffer(void); -void DisplayReAllocScreenBuffer(void); -void DisplayFillScreen(uint32_t line); -void DisplayClearLogBuffer(void); -void DisplayFreeLogBuffer(void); -void DisplayAllocLogBuffer(void); -void DisplayReAllocLogBuffer(void); -void DisplayLogBufferAdd(char* txt); -char* DisplayLogBuffer(char temp_code); -void DisplayLogBufferInit(void); -void DisplayJsonValue(const char* topic, const char* device, const char* mkey, const char* value); -void DisplayAnalyzeJson(char *topic, char *json); -void DisplayMqttSubscribe(void); -bool DisplayMqttData(void); -void DisplayLocalSensor(void); -void DisplayInitDriver(void); -void DisplaySetPower(void); -void CmndDisplay(void); -void CmndDisplayModel(void); -void CmndDisplayWidth(void); -void CmndDisplayHeight(void); -void CmndDisplayMode(void); -void CmndDisplayDimmer(void); -void CmndDisplaySize(void); -void CmndDisplayFont(void); -void CmndDisplayRotate(void); -void CmndDisplayText(void); -void CmndDisplayAddress(void); -void CmndDisplayRefresh(void); -void CmndDisplayColumns(void); -void CmndDisplayRows(void); -void Draw_RGB_Bitmap(char *file,uint16_t xp, uint16_t yp); -void DrawAClock(uint16_t rad); -void ClrGraph(uint16_t num); -void DefineGraph(uint16_t num,uint16_t xp,uint16_t yp,int16_t xs,uint16_t ys,int16_t dec,float ymin, float ymax,uint8_t icol); -void DisplayCheckGraph(); -void Save_graph(uint8_t num, char *path); -void Restore_graph(uint8_t num, char *path); -void RedrawGraph(uint8_t num, uint8_t flags); -void AddGraph(uint8_t num,uint8_t val); -void AddValue(uint8_t num,float fval); -bool Xdrv13(uint8_t function); -uint16_t MP3_Checksum(uint8_t *array); -void MP3PlayerInit(void); -void MP3_CMD(uint8_t mp3cmd,uint16_t val); -bool MP3PlayerCmd(void); -bool Xdrv14(uint8_t function); -void PCA9685_Detect(void); -void PCA9685_Reset(void); -void PCA9685_SetPWMfreq(double freq); -void PCA9685_SetPWM_Reg(uint8_t pin, uint16_t on, uint16_t off); -void PCA9685_SetPWM(uint8_t pin, uint16_t pwm, bool inverted); -bool PCA9685_Command(void); -void PCA9685_OutputTelemetry(bool telemetry); -bool Xdrv15(uint8_t function); -void CmndTuyaSend(void); -void CmndTuyaMcu(void); -void TuyaAddMcuFunc(uint8_t fnId, uint8_t dpId); -void UpdateDevices(); -inline bool TuyaFuncIdValid(uint8_t fnId); -uint8_t TuyaGetFuncId(uint8_t dpid); -uint8_t TuyaGetDpId(uint8_t fnId); -void TuyaSendState(uint8_t id, uint8_t type, uint8_t* value); -void TuyaSendBool(uint8_t id, bool value); -void TuyaSendValue(uint8_t id, uint32_t value); -void TuyaSendEnum(uint8_t id, uint32_t value); -void TuyaSendString(uint8_t id, char data[]); -bool TuyaSetPower(void); -bool TuyaSetChannels(void); -void LightSerialDuty(uint16_t duty); -void TuyaRequestState(void); -void TuyaResetWifi(void); -void TuyaProcessStatePacket(void); -void TuyaLowPowerModePacketProcess(void); -void TuyaHandleProductInfoPacket(void); -void TuyaSendLowPowerSuccessIfNeeded(void); -void TuyaNormalPowerModePacketProcess(void); -bool TuyaModuleSelected(void); -void TuyaInit(void); -void TuyaSerialInput(void); -bool TuyaButtonPressed(void); -uint8_t TuyaGetTuyaWifiState(void); -void TuyaSetWifiLed(void); -bool Xnrg16(uint8_t function); -bool Xdrv16(uint8_t function); -void RfReceiveCheck(void); -void RfInit(void); -void CmndRfSend(void); -bool Xdrv17(uint8_t function); -bool ArmtronixSetChannels(void); -void LightSerial2Duty(uint8_t duty1, uint8_t duty2); -void ArmtronixRequestState(void); -bool ArmtronixModuleSelected(void); -void ArmtronixInit(void); -void ArmtronixSerialInput(void); -void ArmtronixSetWifiLed(void); -bool Xdrv18(uint8_t function); -void PS16DZSerialSend(const char *tx_buffer); -void PS16DZSerialSendOk(void); -void PS16DZSerialSendUpdateCommand(void); -void PS16DZSerialInput(void); -bool PS16DZSerialSendUpdateCommandIfRequired(void); -void PS16DZInit(void); -bool PS16DZModuleSelected(void); -bool Xdrv19(uint8_t function); -String HueBridgeId(void); -String HueSerialnumber(void); -String HueUuid(void); -void HueRespondToMSearch(void); -String GetHueDeviceId(uint16_t id); -String GetHueUserId(void); -void HandleUpnpSetupHue(void); -void HueNotImplemented(String *path); -void HueConfigResponse(String *response); -void HueConfig(String *path); -uint8_t getLocalLightSubtype(uint8_t device); -void HueLightStatus1(uint8_t device, String *response); -bool HueActive(uint8_t device); -void HueLightStatus2(uint8_t device, String *response); -uint32_t findEchoGeneration(void); -void HueGlobalConfig(String *path); -void HueAuthentication(String *path); -void CheckHue(String * response, bool &appending); -void HueLightsCommand(uint8_t device, uint32_t device_id, String &response); -void HueLights(String *path); -void HueGroups(String *path); -void HandleHueApi(String *path); -bool Xdrv20(uint8_t function); -String WemoSerialnumber(void); -String WemoUuid(void); -void WemoRespondToMSearch(int echo_type); -void HandleUpnpEvent(void); -void HandleUpnpService(void); -void HandleUpnpMetaService(void); -void HandleUpnpSetupWemo(void); -bool Xdrv21(uint8_t function); -bool IsModuleIfan(void); -uint8_t MaxFanspeed(void); -uint8_t GetFanspeed(void); -void SonoffIFanSetFanspeed(uint8_t fanspeed, bool sequence); -void SonoffIfanReceived(void); -bool SonoffIfanSerialInput(void); -void CmndFanspeed(void); -bool SonoffIfanInit(void); -void SonoffIfanUpdate(void); -bool Xdrv22(uint8_t function); -String getZigbeeStatusMessage(uint8_t status); -void CopyJsonVariant(JsonObject &to, const String &key, const JsonVariant &val); -void CopyJsonArray(JsonArray &to, const JsonArray &arr); -void CopyJsonObject(JsonObject &to, const JsonObject &from); -void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, String *response); -void HueLightStatus2Zigbee(uint16_t shortaddr, String *response); -void ZigbeeHueStatus(String * response, uint16_t shortaddr); -void ZigbeeCheckHue(String * response, bool &appending); -void ZigbeeHueGroups(String * lights); -void ZigbeeHuePower(uint16_t shortaddr, bool power); -void ZigbeeHueDimmer(uint16_t shortaddr, uint8_t dimmer); -void ZigbeeHueCT(uint16_t shortaddr, uint16_t ct); -void ZigbeeHueXY(uint16_t shortaddr, uint16_t x, uint16_t y); -void ZigbeeHueHS(uint16_t shortaddr, uint16_t hue, uint8_t sat); -void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response); -uint16_t fromClusterCode(uint8_t c); -uint8_t toClusterCode(uint16_t c); -class SBuffer hibernateDevice(const struct Z_Device &device); -class SBuffer hibernateDevices(void); -void hydrateDevices(const SBuffer &buf); -void loadZigbeeDevices(void); -void saveZigbeeDevices(void); -void eraseZigbeeDevices(void); -uint8_t Z_getDatatypeLen(uint8_t t); -uint8_t toPercentageCR2032(uint32_t voltage); -uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer &buf, - uint32_t offset, uint32_t buflen); -uint16_t CxToCluster(uint8_t cx); -int32_t Z_ManufKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_ModelKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_Remove(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_Copy(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_AddPressureUnit(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_FloatDiv100(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_FloatDiv10(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_FloatDiv2(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_OccupancyCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); -int32_t Z_AqaraCube(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_AqaraVibration(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_AqaraSensor(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); -int32_t Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); -void zigbeeSetCommandTimer(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint); -inline bool isXYZ(char c); -inline int8_t hexValue(char c); -void parseXYZ(const char *model, const SBuffer &payload, struct Z_XYZ_Var *xyz); -void sendHueUpdate(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t cmd, bool direction); -void convertClusterSpecific(JsonObject& json, uint16_t cluster, uint8_t cmd, bool direction, const SBuffer &payload); -const __FlashStringHelper* zigbeeFindCommand(const char *command, uint16_t *cluster, uint16_t *cmd); -inline char hexDigit(uint32_t h); -String zigbeeCmdAddParams(const char *zcl_cmd_P, uint32_t x, uint32_t y, uint32_t z); -uint32_t ZigbeeAliasOrNumber(const char *state_text); -void Z_UpdateConfig(uint8_t zb_channel, uint16_t zb_pan_id, uint64_t zb_ext_panid, uint64_t zb_precfgkey_l, uint64_t zb_precfgkey_h); -uint8_t ZigbeeGetInstructionSize(uint8_t instr); -void ZigbeeGotoLabel(uint8_t label); -void ZigbeeStateMachine_Run(void); -int32_t ZigbeeProcessInput(class SBuffer &buf); -int32_t Z_ReceiveDeviceInfo(int32_t res, class SBuffer &buf); -int32_t Z_CheckNVWrite(int32_t res, class SBuffer &buf); -int32_t Z_Reboot(int32_t res, class SBuffer &buf); -int32_t Z_ReceiveCheckVersion(int32_t res, class SBuffer &buf); -bool Z_ReceiveMatchPrefix(const class SBuffer &buf, const uint8_t *match); -int32_t Z_ReceivePermitJoinStatus(int32_t res, const class SBuffer &buf); -int32_t Z_ReceiveNodeDesc(int32_t res, const class SBuffer &buf); -int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf); -int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf); -int32_t Z_DataConfirm(int32_t res, const class SBuffer &buf); -int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf); -int32_t Z_ReceiveTCDevInd(int32_t res, const class SBuffer &buf); -int32_t Z_BindRsp(int32_t res, const class SBuffer &buf); -int32_t Z_UnbindRsp(int32_t res, const class SBuffer &buf); -int32_t Z_MgmtBindRsp(int32_t res, const class SBuffer &buf); -void Z_SendIEEEAddrReq(uint16_t shortaddr); -void Z_SendActiveEpReq(uint16_t shortaddr); -void Z_SendAFInfoRequest(uint16_t shortaddr); -void Z_AqaraOccupancy(uint16_t shortaddr, uint16_t cluster, uint8_t endpoint, const JsonObject &json); -int32_t Z_PublishAttributes(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); -int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf); -int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf); -int32_t Z_Load_Devices(uint8_t value); -void Z_Query_Bulb(uint16_t shortaddr, uint32_t &wait_ms); -int32_t Z_Query_Bulbs(uint8_t value); -int32_t Z_State_Ready(uint8_t value); -void ZigbeeInputLoop(void); -void ZigbeeInit(void); -uint32_t strToUInt(const JsonVariant &val); -void CmndZbReset(void); -void CmndZbZNPSendOrReceive(bool send); -void CmndZbZNPReceive(void); -void CmndZbZNPSend(void); -void ZigbeeZNPSend(const uint8_t *msg, size_t len); -void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, uint16_t manuf, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId); -void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint, bool clusterSpecific, uint16_t manuf, - uint16_t cluster, uint8_t cmd, const char *param); -void CmndZbSend(void); -void ZbBindUnbind(bool unbind); -void CmndZbBind(void); -void CmndZbUnbind(void); -void CmndZbBindState(void); -void CmndZbProbe(void); -void CmndZbProbeOrPing(boolean probe); -void CmndZbPing(void); -void CmndZbName(void); -void CmndZbModelId(void); -void CmndZbLight(void); -void CmndZbForget(void); -void CmndZbSave(void); -void CmndZbRestore(void); -void CmndZbRead(void); -void CmndZbPermitJoin(void); -void CmndZbStatus(void); -void CmndZbConfig(void); -bool Xdrv23(uint8_t function); -void BuzzerOff(void); -void BuzzerBeep(uint32_t count, uint32_t on, uint32_t off, uint32_t tune, uint32_t mode); -void BuzzerSetStateToLed(uint32_t state); -void BuzzerBeep(uint32_t count); -void BuzzerEnabledBeep(uint32_t count, uint32_t duration); -bool BuzzerPinState(void); -void BuzzerInit(void); -void BuzzerEvery100mSec(void); -void CmndBuzzer(void); -bool Xdrv24(uint8_t function); -void A4988Init(void); -void CmndDoMove(void); -void CmndDoRotate(void); -void CmndDoTurn(void); -void CmndSetMIS(void); -void CmndSetSPR(void); -void CmndSetRPM(void); -bool Xdrv25(uint8_t function); -void AriluxRfInterrupt(void); -void AriluxRfHandler(void); -void AriluxRfInit(void); -void AriluxRfDisable(void); -bool Xdrv26(uint8_t function); -void ShutterLogPos(uint32_t i); -void ShutterRtc50mS(void); -int32_t ShutterPercentToRealPosition(uint32_t percent, uint32_t index); -uint8_t ShutterRealToPercentPosition(int32_t realpos, uint32_t index); -void ShutterInit(void); -void ShutterReportPosition(bool always); -void ShutterLimitRealAndTargetPositions(uint32_t i); -void ShutterUpdatePosition(void); -bool ShutterState(uint32_t device); -void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos); -void ShutterWaitForMotorStop(uint32_t i); -int32_t ShutterCounterBasedPosition(uint32_t i); -void ShutterRelayChanged(void); -bool ShutterButtonIsSimultaneousHold(uint32_t button_index, uint32_t shutter_index); -void ShutterButtonHandler(void); -void ShutterSetPosition(uint32_t device, uint32_t position); -void CmndShutterOpen(void); -void CmndShutterStopOpen(void); -void CmndShutterClose(void); -void CmndShutterStopClose(void); -void CmndShutterToggle(void); -void CmndShutterStopToggle(void); -void CmndShutterStop(void); -void CmndShutterPosition(void); -void CmndShutterStopPosition(void); -void CmndShutterOpenTime(void); -void CmndShutterCloseTime(void); -void CmndShutterMotorDelay(void); -void CmndShutterRelay(void); -void CmndShutterButton(void); -void CmndShutterSetHalfway(void); -void CmndShutterFrequency(void); -void CmndShutterSetClose(void); -void CmndShutterInvert(void); -void CmndShutterCalibration(void); -void CmndShutterLock(void); -void CmndShutterEnableEndStopTime(void); -void CmndShutterInvertWebButtons(void); -bool Xdrv27(uint8_t function); -void Pcf8574SwitchRelay(void); -void Pcf8574Init(void); -void HandlePcf8574(void); -void Pcf8574SaveSettings(void); -bool Xdrv28(uint8_t function); -bool DeepSleepEnabled(void); -void DeepSleepReInit(void); -void DeepSleepPrepare(void); -void DeepSleepStart(void); -void DeepSleepEverySecond(void); -void CmndDeepsleepTime(void); -bool Xdrv29(uint8_t function); -uint8_t crc8(const uint8_t *p, uint8_t len); -void ExsSendCmd(uint8_t cmd, uint8_t value); -uint8_t ExsSetPower(uint8_t device, uint8_t power); -uint8_t ExsSetBri(uint8_t device, uint8_t bri); -uint8_t ExsSyncState(uint8_t device); -bool ExsSyncState(); -void ExsDebugState(); -void ExsPacketProcess(void); -bool ExsModuleSelected(void); -bool ExsSetChannels(void); -bool ExsSetPower(void); -void EsxMcuStart(void); -void ExsInit(void); -void ExsSerialInput(void); -void CmndExsDimm(void); -void CmndExsDimmTbl(void); -void CmndExsDimmVal(void); -void CmndExsDimms(void); -void CmndExsChLock(void); -void CmndExsState(void); -bool Xdrv30(uint8_t function); -uint32_t TasmotaSlave_FlashStart(void); -uint8_t TasmotaSlave_UpdateInit(void); -void TasmotaSlave_Reset(void); -uint8_t TasmotaSlave_waitForSerialData(int dataCount, int timeout); -uint8_t TasmotaSlave_sendBytes(uint8_t* bytes, int count); -uint8_t TasmotaSlave_execCmd(uint8_t cmd); -uint8_t TasmotaSlave_execParam(uint8_t cmd, uint8_t* params, int count); -uint8_t TasmotaSlave_exitProgMode(void); -uint8_t TasmotaSlave_SetupFlash(void); -uint8_t TasmotaSlave_loadAddress(uint8_t adrHi, uint8_t adrLo); -void TasmotaSlave_FlashPage(uint8_t addr_h, uint8_t addr_l, uint8_t* data); -void TasmotaSlave_Flash(void); -void TasmotaSlave_SetFlagFlashing(bool value); -bool TasmotaSlave_GetFlagFlashing(void); -void TasmotaSlave_WriteBuffer(uint8_t *buf, size_t size); -void TasmotaSlave_Init(void); -void TasmotaSlave_Show(void); -void TasmotaSlave_sendCmnd(uint8_t cmnd, uint8_t param); -void CmndTasmotaSlaveReset(void); -void CmndTasmotaSlaveSend(void); -void TasmotaSlave_ProcessIn(void); -bool Xdrv31(uint8_t function); -void HotPlugInit(void); -void HotPlugEverySecond(void); -void CmndHotPlugTime(void); -bool Xdrv32(uint8_t function); -bool NRF24initRadio(); -bool NRF24Detect(void); -bool Xdrv33(uint8_t function); -void WMotorV1Detect(void); -void WMotorV1Reset(void); -void WMotorV1SetFrequency(uint32_t freq); -void WMotorV1SetMotor(uint8_t motor, uint8_t dir, float pwm_val); -bool WMotorV1Command(void); -bool Xdrv34(uint8_t function); -void PWMModulePreInit(void); -void PWMDimmerSetBrightnessLeds(int32_t operation); -void PWMDimmerSetPoweredOffLed(void); -void PWMDimmerSetPower(void); -void PWMDimmerHandleDevGroupItem(void); -void PWMDimmerHandleButton(void); -void CmndBriPreset(void); -bool Xdrv35(uint8_t function); -void CmdSet(void); -void GenerateDeviceCryptKey(); -void CmdSendButton(void); -void SendBit(byte bitToSend); -void CmndSendRaw(void); -void enterrx(); -void entertx(); -void SendSyncPreamble(int l); -void CreateKeeloqPacket(); -void KeeloqInit(); -bool Xdrv36(uint8_t function); -void SonoffD1Received(void); -bool SonoffD1SerialInput(void); -void SonoffD1Send(); -bool SonoffD1SendPower(void); -bool SonoffD1SendDimmer(void); -bool SonoffD1ModuleSelected(void); -bool Xdrv37(uint8_t function); -void PingResponsePoll(void); -void CmndPing(void); -bool Xdrv38(uint8_t function); -void ThermostatInit(void); -bool ThermostatMinuteCounter(void); -inline bool ThermostatSwitchIdValid(uint8_t switchId); -inline bool ThermostatRelayIdValid(uint8_t relayId); -uint8_t ThermostatSwitchStatus(uint8_t input_switch); -void ThermostatSignalProcessingSlow(void); -void ThermostatSignalProcessingFast(void); -void ThermostatCtrState(void); -void ThermostatHybridCtrPhase(void); -bool HeatStateAutoToManual(void); -bool HeatStateManualToAuto(void); -bool HeatStateAllToOff(void); -void ThermostatState(void); -void ThermostatOutputRelay(bool active); -void ThermostatCalculatePI(void); -void ThermostatWorkAutomaticPI(void); -void ThermostatWorkAutomaticRampUp(void); -void ThermostatCtrWork(void); -void ThermostatWork(void); -void ThermostatDiagnostics(void); -void ThermostatController(void); -bool ThermostatTimerArm(int16_t tempVal); -void ThermostatTimerDisarm(void); -void ThermostatVirtualSwitch(void); -void ThermostatVirtualSwitchCtrState(void); -void CmndThermostatModeSet(void); -void CmndTempFrostProtectSet(void); -void CmndControllerModeSet(void); -void CmndInputSwitchSet(void); -void CmndOutputRelaySet(void); -void CmndTimeAllowRampupSet(void); -void CmndTempMeasuredSet(void); -void CmndTempTargetSet(void); -void CmndTempTargetRead(void); -void CmndTempMeasuredRead(void); -void CmndTempMeasuredGrdRead(void); -void CmndTempSensNumberSet(void); -void CmndStateEmergencySet(void); -void CmndPowerMaxSet(void); -void CmndTimeManualToAutoSet(void); -void CmndTimeOnLimitSet(void); -void CmndPropBandSet(void); -void CmndTimeResetSet(void); -void CmndTimePiCycleSet(void); -void CmndTempAntiWindupResetSet(void); -void CmndTempHystSet(void); -void CmndTimeMaxActionSet(void); -void CmndTimeMinActionSet(void); -void CmndTimeSensLostSet(void); -void CmndTimeMinTurnoffActionSet(void); -void CmndTempRupDeltInSet(void); -void CmndTempRupDeltOutSet(void); -void CmndTimeRampupMaxSet(void); -void CmndTimeRampupCycleSet(void); -void CmndTempRampupPiAccErrSet(void); -void CmndTimePiProportRead(void); -void CmndTimePiIntegrRead(void); -bool Xdrv39(uint8_t function); -void ExceptionTest(uint8_t type); -void CpuLoadLoop(void); -void DebugFreeMem(void); -void DebugFreeMem(void); -void DebugFreeMem(void); -void DebugRtcDump(char* parms); -void DebugCfgDump(char* parms); -void DebugCfgPeek(char* parms); -void DebugCfgPoke(char* parms); -void SetFlashMode(uint8_t mode); -void CmndHelp(void); -void CmndRtcDump(void); -void CmndCfgDump(void); -void CmndCfgPeek(void); -void CmndCfgPoke(void); -void CmndCfgXor(void); -void CmndException(void); -void CmndCpuCheck(void); -void CmndFreemem(void); -void CmndSetSensor(void); -void CmndFlashMode(void); -uint32_t DebugSwap32(uint32_t x); -void CmndFlashDump(void); -void CmndI2cWrite(void); -void CmndI2cRead(void); -void CmndI2cStretch(void); -void CmndI2cClock(void); -bool Xdrv99(uint8_t function); -void XsnsDriverState(void); -bool XdrvRulesProcess(void); -void ShowFreeMem(const char *where); -bool XdrvCallDriver(uint32_t driver, uint8_t Function); -bool XdrvCall(uint8_t Function); -void LcdInitMode(void); -void LcdInit(uint8_t mode); -void LcdInitDriver(void); -void LcdDrawStringAt(void); -void LcdDisplayOnOff(uint8_t on); -void LcdCenter(uint8_t row, char* txt); -bool LcdPrintLog(void); -void LcdTime(void); -void LcdRefresh(void); -bool Xdsp01(uint8_t function); -void SSD1306InitDriver(void); -void Ssd1306PrintLog(void); -void Ssd1306Time(void); -void Ssd1306Refresh(void); -bool Xdsp02(byte function); -void MatrixWrite(void); -void MatrixClear(void); -void MatrixFixed(char* txt); -void MatrixCenter(char* txt); -void MatrixScrollLeft(char* txt, int loop); -void MatrixScrollUp(char* txt, int loop); -void MatrixInitMode(void); -void MatrixInit(uint8_t mode); -void MatrixInitDriver(void); -void MatrixOnOff(void); -void MatrixDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag); -void MatrixPrintLog(uint8_t direction); -void MatrixRefresh(void); -bool Xdsp03(uint8_t function); -void Ili9341InitMode(void); -void Ili9341Init(uint8_t mode); -void Ili9341InitDriver(void); -void Ili9341Clear(void); -void Ili9341DrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag); -void Ili9341DisplayOnOff(uint8_t on); -void Ili9341OnOff(void); -void Ili9341PrintLog(void); -void Ili9341Refresh(void); -bool Xdsp04(uint8_t function); -void EpdInitDriver29(); -void EpdPrintLog29(void); -void EpdRefresh29(void); -bool Xdsp05(uint8_t function); -void EpdInitDriver42(); -void EpdRefresh42(); -bool Xdsp06(uint8_t function); -void SH1106InitDriver(); -void SH1106PrintLog(void); -void SH1106Time(void); -void SH1106Refresh(void); -bool Xdsp07(uint8_t function); -void ILI9488_InitDriver(); -void ILI9488_MQTT(uint8_t count,const char *cp); -void ILI9488_RDW_BUTT(uint32_t count,uint32_t pwr); -void FT6236Check(); -bool Xdsp08(uint8_t function); -void SSD1351_InitDriver(); -void SSD1351PrintLog(void); -void SSD1351Time(void); -void SSD1351Refresh(void); -bool Xdsp09(uint8_t function); -void RA8876_InitDriver(); -void RA8876_MQTT(uint8_t count,const char *cp); -void RA8876_RDW_BUTT(uint32_t count,uint32_t pwr); -void FT5316Check(); -bool Xdsp10(uint8_t function); -void SevensegWrite(void); -void SevensegClear(void); -void SevensegInitMode(void); -void SevensegInit(uint8_t mode); -void SevensegInitDriver(void); -void SevensegOnOff(void); -void SevensegDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag); -void SevensegTime(boolean time_24); -void SevensegRefresh(void); -bool Xdsp11(uint8_t function); -uint8_t XdspPresent(void); -bool XdspCall(uint8_t Function); -void Ws2812StripShow(void); -int mod(int a, int b); -void Ws2812UpdatePixelColor(int position, struct WsColor hand_color, float offset); -void Ws2812UpdateHand(int position, uint32_t index); -void Ws2812Clock(void); -void Ws2812GradientColor(uint32_t schemenr, struct WsColor* mColor, uint32_t range, uint32_t gradRange, uint32_t i); -void Ws2812Gradient(uint32_t schemenr); -void Ws2812Bars(uint32_t schemenr); -void Ws2812Clear(void); -void Ws2812SetColor(uint32_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t white); -char* Ws2812GetColor(uint32_t led, char* scolor); -void Ws2812ForceSuspend (void); -void Ws2812ForceUpdate (void); -bool Ws2812SetChannels(void); -void Ws2812ShowScheme(void); -void Ws2812ModuleSelected(void); -void CmndLed(void); -void CmndPixels(void); -void CmndRotation(void); -void CmndWidth(void); -bool Xlgt01(uint8_t function); -void LightDiPulse(uint8_t times); -void LightDckiPulse(uint8_t times); -void LightMy92x1Write(uint8_t data); -void LightMy92x1Init(void); -void LightMy92x1Duty(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b, uint8_t duty_w, uint8_t duty_c); -bool My92x1SetChannels(void); -void My92x1ModuleSelected(void); -bool Xlgt02(uint8_t function); -void SM16716_SendBit(uint8_t v); -void SM16716_SendByte(uint8_t v); -void SM16716_Update(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b); -void SM16716_Init(void); -bool Sm16716SetChannels(void); -void Sm16716ModuleSelected(void); -bool Xlgt03(uint8_t function); -uint8_t Sm2135Write(uint8_t data); -void Sm2135Send(uint8_t *buffer, uint8_t size); -bool Sm2135SetChannels(void); -void Sm2135ModuleSelected(void); -bool Xlgt04(uint8_t function); -void SnfL1Send(const char *buffer); -void SnfL1SerialSendOk(void); -bool SnfL1SerialInput(void); -bool SnfL1SetChannels(void); -void SnfL1ModuleSelected(void); -bool Xlgt05(uint8_t function); -bool ElectriqMoodLSetChannels(void); -void ElectriqMoodLModuleSelected(void); -bool Xlgt06(uint8_t function); -bool XlgtCall(uint8_t function); -void HlwCfInterrupt(void); -void HlwCf1Interrupt(void); -void HlwEvery200ms(void); -void HlwEverySecond(void); -void HlwSnsInit(void); -void HlwDrvInit(void); -bool HlwCommand(void); -bool Xnrg01(uint8_t function); -void CseReceived(void); -bool CseSerialInput(void); -void CseEverySecond(void); -void CseSnsInit(void); -void CseDrvInit(void); -bool CseCommand(void); -bool Xnrg02(uint8_t function); -uint8_t PzemCrc(uint8_t *data); -void PzemSend(uint8_t cmd); -bool PzemReceiveReady(void); -bool PzemRecieve(uint8_t resp, float *data); -void PzemEvery250ms(void); -void PzemSnsInit(void); -void PzemDrvInit(void); -bool PzemCommand(void); -bool Xnrg03(uint8_t function); -uint8_t McpChecksum(uint8_t *data); -unsigned long McpExtractInt(char *data, uint8_t offset, uint8_t size); -void McpSetInt(unsigned long value, uint8_t *data, uint8_t offset, size_t size); -void McpSend(uint8_t *data); -void McpGetAddress(void); -void McpAddressReceive(void); -void McpGetCalibration(void); -void McpParseCalibration(void); -bool McpCalibrationCalc(struct mcp_cal_registers_type *cal_registers, uint8_t range_shift); -void McpSetCalibration(struct mcp_cal_registers_type *cal_registers); -void McpSetSystemConfiguration(uint16 interval); -void McpGetFrequency(void); -void McpParseFrequency(void); -void McpSetFrequency(uint16_t line_frequency_ref, uint16_t gain_line_frequency); -void McpGetData(void); -void McpParseData(void); -void McpSerialInput(void); -void McpEverySecond(void); -void McpSnsInit(void); -void McpDrvInit(void); -bool McpCommand(void); -bool Xnrg04(uint8_t function); -void PzemAcEverySecond(void); -void PzemAcSnsInit(void); -void PzemAcDrvInit(void); -bool PzemAcCommand(void); -bool Xnrg05(uint8_t function); -void PzemDcEverySecond(void); -void PzemDcSnsInit(void); -void PzemDcDrvInit(void); -bool PzemDcCommand(void); -bool Xnrg06(uint8_t function); -int Ade7953RegSize(uint16_t reg); -void Ade7953Write(uint16_t reg, uint32_t val); -int32_t Ade7953Read(uint16_t reg); -void Ade7953Init(void); -void Ade7953GetData(void); -void Ade7953EnergyEverySecond(void); -void Ade7953DrvInit(void); -bool Ade7953Command(void); -bool Xnrg07(uint8_t function); -void SDM120Every250ms(void); -void Sdm120SnsInit(void); -void Sdm120DrvInit(void); -void Sdm220Reset(void); -void Sdm220Show(bool json); -bool Xnrg08(uint8_t function); -void Dds2382EverySecond(void); -void Dds2382SnsInit(void); -void Dds2382DrvInit(void); -bool Xnrg09(uint8_t function); -void SDM630Every250ms(void); -void Sdm630SnsInit(void); -void Sdm630DrvInit(void); -bool Xnrg10(uint8_t function); -void DDSU666Every250ms(void); -void Ddsu666SnsInit(void); -void Ddsu666DrvInit(void); -bool Xnrg11(uint8_t function); -bool solaxX1_RS485ReceiveReady(void); -void solaxX1_RS485Send(uint16_t msgLen); -uint8_t solaxX1_RS485Receive(uint8_t *value); -uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen); -void solaxX1_SendInverterAddress(void); -void solaxX1_QueryLiveData(void); -uint8_t solaxX1_ParseErrorCode(uint32_t code); -void solaxX1250MSecond(void); -void solaxX1SnsInit(void); -void solaxX1DrvInit(void); -void solaxX1Show(bool json); -bool Xnrg12(uint8_t function); -void FifLEEvery250ms(void); -void FifLESnsInit(void); -void FifLEDrvInit(void); -void FifLEReset(void); -void FifLEShow(bool json); -bool Xnrg13(uint8_t function); -bool XnrgCall(uint8_t function); -void CounterUpdate(uint8_t index); -void CounterUpdate1(void); -void CounterUpdate2(void); -void CounterUpdate3(void); -void CounterUpdate4(void); -bool CounterPinState(void); -void CounterInit(void); -void CounterEverySecond(void); -void CounterSaveState(void); -void CounterShow(bool json); -void CmndCounter(void); -void CmndCounterType(void); -void CmndCounterDebounce(void); -void CmndCounterDebounceLow(void); -void CmndCounterDebounceHigh(void); -bool Xsns01(uint8_t function); -void AdcInit(void); -uint16_t AdcRead(uint8_t factor); -void AdcEvery250ms(void); -uint16_t AdcGetLux(void); -uint16_t AdcGetRange(void); -void AdcGetCurrentPower(uint8_t factor); -void AdcEverySecond(void); -void AdcShow(bool json); -void CmndAdc(void); -void CmndAdcs(void); -void CmndAdcParam(void); -bool Xsns02(uint8_t function); -void SonoffScSend(const char *data); -void SonoffScInit(void); -void SonoffScSerialInput(char *rcvstat); -void SonoffScShow(bool json); -bool Xsns04(uint8_t function); -uint8_t OneWireReset(void); -void OneWireWriteBit(uint8_t v); -uint8_t OneWire1ReadBit(void); -uint8_t OneWire2ReadBit(void); -void OneWireWrite(uint8_t v); -uint8_t OneWireRead(void); -void OneWireSelect(const uint8_t rom[8]); -void OneWireResetSearch(void); -uint8_t OneWireSearch(uint8_t *newAddr); -bool OneWireCrc8(uint8_t *addr); -void Ds18x20Init(void); -void Ds18x20Convert(void); -bool Ds18x20Read(uint8_t sensor); -void Ds18x20Name(uint8_t sensor); -void Ds18x20EverySecond(void); -void Ds18x20Show(bool json); -bool Xsns05(uint8_t function); -bool DhtWaitState(uint32_t sensor, uint32_t level); -bool DhtRead(uint32_t sensor); -bool DhtPinState(); -void DhtInit(void); -void DhtEverySecond(void); -void DhtShow(bool json); -bool Xsns06(uint8_t function); -bool ShtReset(void); -bool ShtSendCommand(const uint8_t cmd); -bool ShtAwaitResult(void); -int ShtReadData(void); -bool ShtRead(void); -void ShtDetect(void); -void ShtEverySecond(void); -void ShtShow(bool json); -bool Xsns07(uint8_t function); -uint8_t HtuCheckCrc8(uint16_t data); -uint8_t HtuReadDeviceId(void); -void HtuSetResolution(uint8_t resolution); -void HtuReset(void); -void HtuHeater(uint8_t heater); -void HtuInit(void); -bool HtuRead(void); -void HtuDetect(void); -void HtuEverySecond(void); -void HtuShow(bool json); -bool Xsns08(uint8_t function); -bool Bmp180Calibration(uint8_t bmp_idx); -void Bmp180Read(uint8_t bmp_idx); -bool Bmx280Calibrate(uint8_t bmp_idx); -void Bme280Read(uint8_t bmp_idx); -static void BmeDelayMs(uint32_t ms); -bool Bme680Init(uint8_t bmp_idx); -void Bme680Read(uint8_t bmp_idx); -void BmpDetect(void); -void BmpRead(void); -void BmpShow(bool json); -void BMP_EnterSleep(void); -bool Xsns09(uint8_t function); -bool Bh1750SetResolution(void); -bool Bh1750SetMTreg(void); -bool Bh1750Read(void); -void Bh1750Detect(void); -void Bh1750EverySecond(void); -bool Bh1750CommandSensor(void); -void Bh1750Show(bool json); -bool Xsns10(uint8_t function); -void Veml6070Detect(void); -void Veml6070UvTableInit(void); -void Veml6070EverySecond(void); -void Veml6070ModeCmd(bool mode_cmd); -uint16_t Veml6070ReadUv(void); -double Veml6070UvRiskLevel(uint16_t uv_level); -double Veml6070UvPower(double uvrisk); -void Veml6070Show(bool json); -bool Xsns11(uint8_t function); -void Ads1115StartComparator(uint8_t channel, uint16_t mode); -int16_t Ads1115GetConversion(uint8_t channel); -void Ads1115Detect(void); -void Ads1115Show(bool json); -bool Xsns12(uint8_t function); -bool Ina219SetCalibration(uint8_t mode, uint16_t addr); -float Ina219GetShuntVoltage_mV(uint16_t addr); -float Ina219GetBusVoltage_V(uint16_t addr); -bool Ina219Read(void); -bool Ina219CommandSensor(void); -void Ina219Detect(void); -void Ina219EverySecond(void); -void Ina219Show(bool json); -bool Xsns13(uint8_t function); -bool Sht3xRead(float &t, float &h, uint8_t sht3x_address); -void Sht3xDetect(void); -void Sht3xShow(bool json); -bool Xsns14(uint8_t function); -uint8_t MhzCalculateChecksum(uint8_t *array); -size_t MhzSendCmd(uint8_t command_id); -bool MhzCheckAndApplyFilter(uint16_t ppm, uint8_t s); -void MhzEverySecond(void); -bool MhzCommandSensor(void); -void MhzInit(void); -void MhzShow(bool json); -bool Xsns15(uint8_t function); -bool Tsl2561Read(void); -void Tsl2561Detect(void); -void Tsl2561EverySecond(void); -void Tsl2561Show(bool json); -bool Xsns16(uint8_t function); -void Senseair250ms(void); -void SenseairInit(void); -void SenseairShow(bool json); -bool Xsns17(uint8_t function); -size_t PmsSendCmd(uint8_t command_id); -bool PmsReadData(void); -bool PmsCommandSensor(void); -void PmsSecond(void); -void PmsInit(void); -void PmsShow(bool json); -bool Xsns18(uint8_t function); -void MGSInit(void); -void MGSPrepare(void); -char* measure_gas(int gas_type, char* buffer); -void MGSShow(bool json); -bool Xsns19(uint8_t function); -bool NovaSdsCommand(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint16_t sensorid, uint8_t *buffer); -void NovaSdsSetWorkPeriod(void); -bool NovaSdsReadData(void); -void NovaSdsSecond(void); -bool NovaSdsCommandSensor(void); -void NovaSdsInit(void); -void NovaSdsShow(bool json); -bool Xsns20(uint8_t function); -void sgp30_Init(void); -float sgp30_AbsoluteHumidity(float temperature, float humidity,char tempUnit); -void Sgp30Update(void); -void Sgp30Show(bool json); -bool Xsns21(uint8_t function); -uint8_t Sr04TModeDetect(void); -uint16_t Sr04TMiddleValue(uint16_t first, uint16_t second, uint16_t third); -uint16_t Sr04TMode3Distance(); -uint16_t Sr04TMode2Distance(void); -void Sr04TReading(void); -void Sr04Show(bool json); -bool Xsns22(uint8_t function); -uint8_t Si1145ReadByte(uint8_t reg); -uint16_t Si1145ReadHalfWord(uint8_t reg); -bool Si1145WriteByte(uint8_t reg, uint16_t val); -uint8_t Si1145WriteParamData(uint8_t p, uint8_t v); -bool Si1145Present(void); -void Si1145Reset(void); -void Si1145DeInit(void); -bool Si1145Begin(void); -uint16_t Si1145ReadUV(void); -uint16_t Si1145ReadVisible(void); -uint16_t Si1145ReadIR(void); -bool Si1145Read(void); -void Si1145Detect(void); -void Si1145Update(void); -void Si1145Show(bool json); -bool Xsns24(uint8_t function); -void LM75ADDetect(void); -float LM75ADGetTemp(void); -void LM75ADShow(bool json); -bool Xsns26(uint8_t function); -int8_t wireReadDataBlock( uint8_t reg, - uint8_t *val, - uint16_t len); -void calculateColorTemperature(void); -bool APDS9960_init(void); -uint8_t getMode(void); -void setMode(uint8_t mode, uint8_t enable); -void enableLightSensor(void); -void disableLightSensor(void); -void enableProximitySensor(void); -void disableProximitySensor(void); -void enableGestureSensor(void); -void disableGestureSensor(void); -bool isGestureAvailable(void); -int16_t readGesture(void); -void enablePower(void); -void disablePower(void); -void readAllColorAndProximityData(void); -void resetGestureParameters(void); -bool processGestureData(void); -bool decodeGesture(void); -void handleGesture(void); -void APDS9960_adjustATime(void); -void APDS9960_loop(void); -void APDS9960_detect(void); -void APDS9960_show(bool json); -bool APDS9960CommandSensor(void); -bool Xsns27(uint8_t function); -void Tm16XXSend(uint8_t data); -void Tm16XXSendCommand(uint8_t cmd); -void TM16XXSendData(uint8_t address, uint8_t data); -uint8_t Tm16XXReceive(void); -void Tm16XXClearDisplay(void); -void Tm1638SetLED(uint8_t color, uint8_t pos); -void Tm1638SetLEDs(word leds); -uint8_t Tm1638GetButtons(void); -void TmInit(void); -void TmLoop(void); -bool Xsns28(uint8_t function); -void MCP230xx_CheckForIntCounter(void); -void MCP230xx_CheckForIntRetainer(void); -const char* IntModeTxt(uint8_t intmo); -uint8_t MCP230xx_readGPIO(uint8_t port); -void MCP230xx_ApplySettings(void); -void MCP230xx_Detect(void); -void MCP230xx_CheckForInterrupt(void); -void MCP230xx_Show(bool json); -void MCP230xx_SetOutPin(uint8_t pin,uint8_t pinstate); -void MCP230xx_Reset(uint8_t pinmode); -bool MCP230xx_Command(void); -void MCP230xx_UpdateWebData(void); -void MCP230xx_OutputTelemetry(void); -void MCP230xx_Interrupt_Counter_Report(void); -void MCP230xx_Interrupt_Retain_Report(void); -bool Xsns29(uint8_t function); -void Mpr121Init(struct mpr121 *pS, bool initial); -void Mpr121Show(struct mpr121 *pS, uint8_t function); -bool Xsns30(uint8_t function); -void CCS811Detect(void); -void CCS811Update(void); -void CCS811Show(bool json); -bool Xsns31(uint8_t function); -void MPU_6050PerformReading(void); -void MPU_6050Detect(void); -void MPU_6050Show(bool json); -bool Xsns32(uint8_t function); -void DS3231Detect(void); -uint8_t bcd2dec(uint8_t n); -uint8_t dec2bcd(uint8_t n); -uint32_t ReadFromDS3231(void); -void SetDS3231Time (uint32_t epoch_time); -void DS3231EverySecond(void); -bool Xsns33(uint8_t function); -bool HxIsReady(uint16_t timeout); -long HxRead(void); -void HxResetPart(void); -void HxReset(void); -void HxCalibrationStateTextJson(uint8_t msg_id); -void SetWeightDelta(); -bool HxCommand(void); -long HxWeight(void); -void HxInit(void); -void HxEvery100mSecond(void); -void HxSaveBeforeRestart(void); -void HxShow(bool json); -void HandleHxAction(void); -void HxSaveSettings(void); -void HxLogUpdates(void); -bool Xsns34(uint8_t function); -void TX2xStartRead(void); -bool Tx2xAvailable(void); -float atan2f(float a, float b); -void Tx2xCheckSampleCount(void); -void Tx2xResetStat(void); -void Tx2xResetStatData(void); -void Tx2xRead(void); -void Tx2xInit(void); -int32_t Tx2xNormalize(int32_t value); -void Tx2xShow(bool json); -bool Xsns35(uint8_t function); -void MGC3130_handleSensorData(); -void MGC3130_sendMessage(uint8_t data[], uint8_t length); -void MGC3130_handleGesture(); -bool MGC3130_handleTouch(); -void MGC3130_handleAirWheel(); -void MGC3130_handleSystemStatus(); -bool MGC3130_receiveMessage(); -bool MGC3130_readData(); -void MGC3130_nextMode(); -void MGC3130_loop(); -void MGC3130_detect(void); -void MGC3130_show(bool json); -bool MGC3130CommandSensor(); -bool Xsns36(uint8_t function); -bool RfSnsFetchSignal(uint8_t DataPin, bool StateSignal); -void RfSnsInitTheoV2(void); -void RfSnsAnalyzeTheov2(void); -void RfSnsTheoV2Show(bool json); -void RfSnsInitAlectoV2(void); -void RfSnsAnalyzeAlectov2(); -void RfSnsAlectoResetRain(void); -uint8_t RfSnsAlectoCRC8(uint8_t *addr, uint8_t len); -void RfSnsAlectoV2Show(bool json); -void RfSnsInit(void); -void RfSnsAnalyzeRawSignal(void); -void RfSnsEverySecond(void); -void RfSnsShow(bool json); -bool Xsns37(uint8_t function); -void AzEverySecond(void); -void AzInit(void); -void AzShow(bool json); -bool Xsns38(uint8_t function); -void MAX31855_Init(void); -void MAX31855_GetResult(void); -float MAX31855_GetProbeTemperature(int32_t RawData); -float MAX31855_GetReferenceTemperature(int32_t RawData); -int32_t MAX31855_ShiftIn(uint8_t Length); -void MAX31855_Show(bool Json); -bool Xsns39(uint8_t function); -void PN532_Init(void); -int8_t PN532_receive(uint8_t *buf, int len, uint16_t timeout); -int8_t PN532_readAckFrame(void); -uint32_t PN532_getFirmwareVersion(void); -void PN532_wakeup(void); -bool PN532_setPassiveActivationRetries(uint8_t maxRetries); -bool PN532_SAMConfig(void); -uint8_t mifareclassic_AuthenticateBlock (uint8_t *uid, uint8_t uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t *keyData); -uint8_t mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t *data); -uint8_t mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t *data); -void PN532_ScanForTag(void); -bool PN532_Command(void); -bool Xsns40(uint8_t function); -bool Max4409Read_lum(void); -void Max4409Detect(void); -void Max4409EverySecond(void); -void Max4409Show(bool json); -bool Xsns41(uint8_t function); -void Scd30Detect(void); -void Scd30Update(void); -int Scd30GetCommand(int command_code, uint16_t *pvalue); -int Scd30SetCommand(int command_code, uint16_t value); -bool Scd30CommandSensor(); -void Scd30Show(bool json); -bool Xsns42(byte function); -int hreReadBit(); -char hreReadChar(int &parity_errors); -void hreInit(void); -void hreEvery50ms(void); -void hreShow(boolean json); -bool Xsns43(byte function); -uint8_t sps30_calc_CRC(uint8_t *data); -void sps30_get_data(uint16_t cmd, uint8_t *data, uint8_t dlen); -void sps30_cmd(uint16_t cmd); -void SPS30_Detect(void); -void SPS30_Every_Second(); -void SPS30_Show(bool json); -bool SPS30_cmd(void); -bool Xsns44(byte function); -void Vl53l0Detect(void); -void Vl53l0Every_250MSecond(void); -void Vl53l0Show(boolean json); -bool Xsns45(byte function); -void MLX90614_Init(void); -void MLX90614_Every_Second(void); -void MLX90614_Show(uint8_t json); -bool Xsns46(byte function); -void MAX31865_Init(void); -void MAX31865_GetResult(void); -void MAX31865_Show(bool Json); -bool Xsns47(uint8_t function); -void ChirpWriteI2CRegister(uint8_t addr, uint8_t reg); -uint16_t ChirpFinishReadI2CRegister16bit(uint8_t addr); -void ChirpReset(uint8_t addr); -void ChirpResetAll(void); -void ChirpClockSet(); -void ChirpSleep(uint8_t addr); -void ChirpSelect(uint8_t sensor); -uint8_t ChirpReadVersion(uint8_t addr); -bool ChirpSet(uint8_t addr); -bool ChirpScan(); -void ChirpDetect(void); -void ChirpServiceAllSensors(uint8_t job); -void ChirpEvery100MSecond(void); -void ChirpShow(bool json); -bool ChirpCmd(void); -bool Xsns48(uint8_t function); -void PAJ7620SelectBank(uint8_t bank); -void PAJ7620DecodeGesture(void); -void PAJ7620ReadGesture(void); -void PAJ7620Detect(void); -void PAJ7620Init(void); -void PAJ7620Loop(void); -void PAJ7620Show(bool json); -bool PAJ7620CommandSensor(void); -bool Xsns50(uint8_t function); -void RDM6300_Init(); -void RDM6300_ScanForTag(); -uint8_t rm6300_hexnibble(char chr); -void rm6300_hstring_to_array(uint8_t array[], uint8_t len, char buffer[]); -void RDM6300_Show(void); -bool Xsns51(byte function); -void IBEACON_Init(); -void hm17_every_second(void); -void hm17_sbclr(void); -void hm17_sendcmd(uint8_t cmd); -uint32_t ibeacon_add(struct IBEACON *ib); -void hm17_decode(void); -void IBEACON_loop(); -void IBEACON_Show(void); -bool xsns52_cmd(void); -bool ibeacon_cmd(void); -void ib_sendbeep(void); -void ibeacon_mqtt(const char *mac,const char *rssi); -bool Xsns52(byte function); -double sml_median_array(double *array,uint8_t len); -double sml_median(struct SML_MEDIAN_FILTER* mf, double in); -void ADS1115_init(void); -bool Serial_available(); -uint8_t Serial_read(); -uint8_t Serial_peek(); -void Dump2log(void); -double sml_getvalue(unsigned char *cp,uint8_t index); -uint8_t hexnibble(char chr); -double CharToDouble(const char *str); -void ebus_esc(uint8_t *ebus_buffer, unsigned char len); -uint8_t ebus_crc8(uint8_t data, uint8_t crc_init); -uint8_t ebus_CalculateCRC( uint8_t *Data, uint16_t DataLen ); -void sml_empty_receiver(uint32_t meters); -void sml_shift_in(uint32_t meters,uint32_t shard); -void SML_Poll(void); -void SML_Decode(uint8_t index); -void SML_Immediate_MQTT(const char *mp,uint8_t index,uint8_t mindex); -void SML_Show(boolean json); -void SML_CounterUpd(uint8_t index); -void SML_CounterUpd1(void); -void SML_CounterUpd2(void); -void SML_CounterUpd3(void); -void SML_CounterUpd4(void); -bool Gpio_used(uint8_t gpiopin); -void SML_Init(void); -uint32_t SML_SetBaud(uint32_t meter, uint32_t br); -uint32_t SML_Status(uint32_t meter); -uint32_t SML_Write(uint32_t meter,char *hstr); -void SetDBGLed(uint8_t srcpin, uint8_t ledpin); -void SML_Counter_Poll(void); -void SML_Check_Send(void); -uint8_t sml_hexnibble(char chr); -void SML_Send_Seq(uint32_t meter,char *seq); -uint16_t MBUS_calculateCRC(uint8_t *frame, uint8_t num); -uint8_t SML_PzemCrc(uint8_t *data, uint8_t len); -uint8_t CalcEvenParity(uint8_t data); -bool XSNS_53_cmd(void); -void InjektCounterValue(uint8_t meter,uint32_t counter); -void SML_CounterSaveState(void); -bool Xsns53(byte function); -static uint32_t _expand_r_shunt(uint16_t compact_r_shunt); -void Ina226SetCalibration(uint8_t slaveIndex); -bool Ina226TestPresence(uint8_t device); -void Ina226ResetActive(void); -void Ina226Init(); -float Ina226ReadBus_v(uint8_t device); -float Ina226ReadShunt_i(uint8_t device); -float Ina226ReadPower_w(uint8_t device); -void Ina226Read(uint8_t device); -void Ina226EverySecond(); -bool Ina226CommandSensor(); -void Ina226Show(bool json); -bool Xsns54(byte callback_id); -bool Hih6Read(void); -void Hih6Detect(void); -void Hih6EverySecond(void); -void Hih6Show(bool json); -bool Xsns55(uint8_t function); -void HpmaSecond(void); -void HpmaInit(void); -void HpmaShow(bool json); -bool Xsns56(uint8_t function); -void Tsl2591Init(void); -bool Tsl2591Read(void); -void Tsl2591EverySecond(void); -void Tsl2591Show(bool json); -bool Xsns57(uint8_t function); -bool Dht12Read(void); -void Dht12Detect(void); -void Dht12EverySecond(void); -void Dht12Show(bool json); -bool Xsns58(uint8_t function); -uint32_t DS1624_Idx2Addr(uint32_t idx); -int DS1624_Restart(uint8_t config, uint32_t idx); -void DS1624_HotPlugUp(uint32_t idx); -void DS1624_HotPlugDown(int idx); -bool DS1624GetTemp(float *value, int idx); -void DS1624HotPlugScan(void); -void DS1624EverySecond(void); -void DS1624Show(bool json); -bool Xsns59(uint8_t function); -void UBXcalcChecksum(char* CK, size_t msgSize); -bool UBXcompareMsgHeader(const char* msgHeader); -void UBXinitCFG(void); -void UBXsendCFGLine(uint8_t _line); -void UBXTriggerTele(void); -void UBXDetect(void); -uint32_t UBXprocessGPS(); -void UBXsendHeader(void); -void UBXsendRecord(uint8_t *buf); -void UBXsendFooter(void); -void UBXsendFile(void); -void UBXSetRate(uint16_t interval); -void UBXSelectMode(uint16_t mode); -bool UBXHandlePOSLLH(); -void UBXHandleSTATUS(); -void UBXHandleTIME(); -void UBXHandleOther(void); -void UBXLoop50msec(void); -void UBXLoop(void); -void UBXShow(bool json); -bool UBXCmd(void); -bool Xsns60(uint8_t function); -bool MINRFinitBLE(uint8_t _mode); -void MINRFhopChannel(); -bool MINRFreceivePacket(void); -void MINRFswapbuf(uint8_t *buf, uint8_t len); -void MINRFwhiten(uint8_t *buf, uint8_t len, uint8_t lfsr); -void MINRFhandleScan(void); -void MINRFstartBeacon(uint16_t entry); -void MINRFbeaconCounter(void); -void MINRFcomputeBeaconPDU(void); -void MINRFreverseMAC(uint8_t _mac[]); -void MINRFMACStringToBytes(char* _string, uint8_t _mac[]); -void MINRFcomputefirstUsedPacketMode(void); -void MINRFchangePacketModeTo(uint8_t _mode); -void MINRFpurgeFakeSensors(void); -void MINRFconfirmSensors(void); -void MINRFhandleMiBeaconPacket(void); -void MINRFhandleLYWSD03Packet(void); -void MINRFhandleCGD1Packet(void); -void MINRF_EVERY_50_MSECOND(); -bool NRFCmd(void); -void MINRFShow(bool json); -bool Xsns61(uint8_t function); -void HM10_Launchtask(uint8_t task, uint8_t slot, uint8_t delay); -void HM10_TaskReplaceInSlot(uint8_t task, uint8_t slot); -void HM10_ReverseMAC(uint8_t _mac[]); -void HM10_Reset(void); -void HM10_Discovery_Scan(void); -void HM10_Read_LYWSD03(void); -void HM10_Read_LYWSD02(void); -void HM10_Time_LYWSD02(void); -void HM10_Read_Flora(void); -void HM10_Read_CGD1(void); -void HM10_Read_MJ_HT_V1(void); -void HM10SerialInit(void); -void HM10parseMiBeacon(char * _buf, uint32_t _slot); -void HM10ParseResponse(char *buf, uint16_t bufsize); -void HM10readHT_LY(char *_buf); -void HM10readHT_CGD1(char *_buf); -void HM10readHT_MJ_HT_V1(char *_buf); -void HM10readTLMF(char *_buf); -bool HM10readBat(char *_buf); -bool HM10SerialHandleFeedback(); -void HM10_TaskEvery100ms(); -void HM10StatusInfo(); -void HM10EverySecond(bool restart); -bool HM10Cmd(void); -void HM10Show(bool json); -bool Xsns62(uint8_t function); -bool AHT1XWrite(uint8_t aht1x_idx); -bool AHT1XRead(uint8_t aht1x_idx); -void AHT1XPoll(void); -unsigned char AHT1XReadStatus(uint8_t aht1x_address); -void AHT1XReset(uint8_t aht1x_address); -bool AHT1XInit(uint8_t aht1x_address); -void AHT1XDetect(void); -void AHT1XShow(bool json); -bool Xsns63(uint8_t function); -void HRXLInit(void); -void HRXLEverySecond(void); -void HRXLShow(bool json); -bool Xsns64(uint8_t function); -uint16_t HdcReadDeviceId(void); -uint16_t HdcReadManufacturerId(void); -void HdcConfig(uint16_t config); -void HdcReset(void); -int8_t HdcTransactionOpen(uint8_t addr, uint8_t reg); -int8_t HdcTransactionClose(uint8_t addr, uint8_t *reg_data, uint16_t len); -void HdcInit(void); -bool HdcTriggerRead(void); -bool HdcRead(void); -void HdcDetect(void); -void HdcEverySecond(void); -void HdcShow(bool json); -bool Xsns65(uint8_t function); -void IAQ_Init(void); -void IAQ_Read(void); -void IAQ_Show(uint8_t json); -bool Xsns66(byte function); -void ICACHE_RAM_ATTR AS3935Isr(); -uint8_t AS3935ReadRegister(uint8_t reg, uint8_t mask, uint8_t shift); -void AS3935WriteRegister(uint8_t reg, uint8_t mask, uint8_t shift, uint8_t data); -void ICACHE_RAM_ATTR AS3935CountFreq(); -bool AS3935AutoTuneCaps(uint8_t irqpin); -void AS3935CalibrateRCO(); -uint8_t AS3935TransMinLights(uint8_t min_lights); -uint8_t AS3935TranslMinLightsInt(uint8_t min_lights); -uint8_t AS3935TranslIrq(uint8_t irq, uint8_t distance); -void AS3935CalcVrmsLevel(uint16_t &vrms, uint8_t &stage); -uint8_t AS3935GetIRQ(); -uint8_t AS3935GetDistance(); -int16_t AS3935CalcDistance(); -uint32_t AS3935GetIntensity(); -uint8_t AS3935GetTuneCaps(); -void AS3935SetTuneCaps(uint8_t tune); -uint8_t AS3935GetDisturber(); -uint8_t AS3935SetDisturber(uint8_t stat); -uint8_t AS3935GetMinLights(); -uint8_t AS3935SetMinLights(uint8_t stat); -uint8_t AS3935GetNoiseFloor(); -uint8_t AS3935SetNoiseFloor(uint8_t noise); -uint8_t AS3935GetGain(); -uint8_t AS3935SetGain(uint8_t room); -uint8_t AS3935GetGainInt(); -uint8_t AS3935GetSpikeRejection(); -void AS3935SetSpikeRejection(uint8_t rej); -uint8_t AS3935GetWdth(); -void AS3935SetWdth(uint8_t wdth); -bool AS3935AutoTune(); -bool AS3935LowerNoiseFloor(); -bool AS3935RaiseNoiseFloor(); -bool AS3935SetDefault(); -void AS3935InitSettings(); -void AS3935Setup(void); -bool AS3935init(); -void AS3935Detect(void); -void AS3935EverySecond(); -bool AS3935Cmd(void); -void AH3935Show(bool json); -bool Xsns67(uint8_t function); -void HandleMetrics(void); -bool Xsns91(uint8_t function); -bool XsnsEnabled(uint32_t sns_index); -void XsnsSensorState(void); -bool XsnsNextCall(uint8_t Function, uint8_t &xsns_index); -bool XsnsCall(uint8_t Function); -bool I2cEnabled(uint32_t i2c_index); -void I2cDriverState(void); -#line 191 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/tasmota.ino" -void setup(void) -{ - global_state.data = 3; - -#ifdef ESP32 -#ifdef DISABLE_BROWNOUT - DisableBrownout(); -#endif -#endif - - RtcRebootLoad(); - if (!RtcRebootValid()) { - RtcReboot.fast_reboot_count = 0; - } - RtcReboot.fast_reboot_count++; - RtcRebootSave(); - - Serial.begin(APP_BAUDRATE); - seriallog_level = LOG_LEVEL_INFO; - - snprintf_P(my_version, sizeof(my_version), PSTR("%d.%d.%d"), VERSION >> 24 & 0xff, VERSION >> 16 & 0xff, VERSION >> 8 & 0xff); - if (VERSION & 0xff) { - snprintf_P(my_version, sizeof(my_version), PSTR("%s.%d"), my_version, VERSION & 0xff); - } - - snprintf_P(my_image, sizeof(my_image), PSTR("(%s)"), CODE_IMAGE_STR); - - SettingsLoad(); - SettingsDelta(); - - OsWatchInit(); - - GetFeatures(); - - if (1 == RtcReboot.fast_reboot_count) { - UpdateQuickPowerCycle(true); - XdrvCall(FUNC_SETTINGS_OVERRIDE); - } - - - seriallog_level = Settings.seriallog_level; - seriallog_timer = SERIALLOG_TIMER; - syslog_level = Settings.syslog_level; - stop_flash_rotate = Settings.flag.stop_flash_rotate; - save_data_counter = Settings.save_data; - ssleep = Settings.sleep; -#ifndef USE_EMULATION - Settings.flag2.emulation = 0; -#else -#ifndef USE_EMULATION_WEMO - if (EMUL_WEMO == Settings.flag2.emulation) { Settings.flag2.emulation = 0; } -#endif -#ifndef USE_EMULATION_HUE - if (EMUL_HUE == Settings.flag2.emulation) { Settings.flag2.emulation = 0; } -#endif -#endif - - if (Settings.param[P_BOOT_LOOP_OFFSET]) { - - if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET]) { - Settings.flag3.user_esp8285_enable = 0; - if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +1) { - for (uint32_t i = 0; i < MAX_RULE_SETS; i++) { - if (bitRead(Settings.rule_stop, i)) { - bitWrite(Settings.rule_enabled, i, 0); - } - } - } - if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +2) { - Settings.rule_enabled = 0; - } - if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +3) { - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - Settings.my_gp.io[i] = GPIO_NONE; - } - Settings.my_adc0 = ADC0_NONE; - } - if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +4) { -#ifdef ESP8266 - Settings.module = SONOFF_BASIC; - -#endif -#ifdef ESP32 - Settings.module = WEMOS; -#endif - } - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_LOG_SOME_SETTINGS_RESET " (%d)"), RtcReboot.fast_reboot_count); - } - } - - Format(mqtt_client, SettingsText(SET_MQTT_CLIENT), sizeof(mqtt_client)); - Format(mqtt_topic, SettingsText(SET_MQTT_TOPIC), sizeof(mqtt_topic)); - if (strstr(SettingsText(SET_HOSTNAME), "%") != nullptr) { - SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); - snprintf_P(my_hostname, sizeof(my_hostname)-1, SettingsText(SET_HOSTNAME), mqtt_topic, ESP_getChipId() & 0x1FFF); - } else { - snprintf_P(my_hostname, sizeof(my_hostname)-1, SettingsText(SET_HOSTNAME)); - } - - GetEspHardwareType(); - GpioInit(); - - - - WifiConnect(); - - SetPowerOnState(); - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_PROJECT " %s %s " D_VERSION " %s%s-" ARDUINO_CORE_RELEASE), PROJECT, SettingsText(SET_FRIENDLYNAME1), my_version, my_image); -#ifdef FIRMWARE_MINIMAL - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_WARNING_MINIMAL_VERSION)); -#endif - - memcpy_P(log_data, VERSION_MARKER, 1); - - RtcInit(); - -#ifdef USE_ARDUINO_OTA - ArduinoOTAInit(); -#endif - - XdrvCall(FUNC_INIT); - XsnsCall(FUNC_INIT); -} - -void BacklogLoop(void) -{ - if (TimeReached(backlog_delay)) { - if (!BACKLOG_EMPTY && !backlog_mutex) { -#ifdef SUPPORT_IF_STATEMENT - backlog_mutex = true; - String cmd = backlog.shift(); - backlog_mutex = false; - ExecuteCommand((char*)cmd.c_str(), SRC_BACKLOG); -#else - backlog_mutex = true; - ExecuteCommand((char*)backlog[backlog_pointer].c_str(), SRC_BACKLOG); - backlog_pointer++; - if (backlog_pointer >= MAX_BACKLOG) { backlog_pointer = 0; } - backlog_mutex = false; -#endif - } - } -} - -void loop(void) -{ - uint32_t my_sleep = millis(); - - XdrvCall(FUNC_LOOP); - XsnsCall(FUNC_LOOP); - - OsWatchLoop(); - - ButtonLoop(); - SwitchLoop(); -#ifdef ROTARY_V1 - RotaryLoop(); -#endif -#ifdef USE_DEVICE_GROUPS - DeviceGroupsLoop(); -#endif - BacklogLoop(); - - if (TimeReached(state_50msecond)) { - SetNextTimeInterval(state_50msecond, 50); - XdrvCall(FUNC_EVERY_50_MSECOND); - XsnsCall(FUNC_EVERY_50_MSECOND); - } - if (TimeReached(state_100msecond)) { - SetNextTimeInterval(state_100msecond, 100); - Every100mSeconds(); - XdrvCall(FUNC_EVERY_100_MSECOND); - XsnsCall(FUNC_EVERY_100_MSECOND); - } - if (TimeReached(state_250msecond)) { - SetNextTimeInterval(state_250msecond, 250); - Every250mSeconds(); - XdrvCall(FUNC_EVERY_250_MSECOND); - XsnsCall(FUNC_EVERY_250_MSECOND); - } - if (TimeReached(state_second)) { - SetNextTimeInterval(state_second, 1000); - PerformEverySecond(); - XdrvCall(FUNC_EVERY_SECOND); - XsnsCall(FUNC_EVERY_SECOND); - } - - if (!serial_local) { SerialInput(); } - -#ifdef USE_ARDUINO_OTA - ArduinoOtaLoop(); -#endif - - uint32_t my_activity = millis() - my_sleep; - - if (Settings.flag3.sleep_normal) { - - delay(ssleep); - } else { - if (my_activity < (uint32_t)ssleep) { - delay((uint32_t)ssleep - my_activity); - } else { - if (global_state.wifi_down) { - delay(my_activity /2); - } - } - } - - if (!my_activity) { my_activity++; } - uint32_t loop_delay = ssleep; - if (!loop_delay) { loop_delay++; } - uint32_t loops_per_second = 1000 / loop_delay; - uint32_t this_cycle_ratio = 100 * my_activity / loop_delay; - loop_load_avg = loop_load_avg - (loop_load_avg / loops_per_second) + (this_cycle_ratio / loops_per_second); -} -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/sendemail.ino" -#ifdef USE_SENDMAIL - -#include "sendemail.h" -# 25 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/sendemail.ino" -#ifndef SEND_MAIL_MINRAM -#define SEND_MAIL_MINRAM 12*1024 -#endif - -#define xPSTR(a) a - -uint16_t SendMail(char *buffer) { - char *params,*oparams; - const char *mserv; - uint16_t port; - const char *user; - const char *pstr; - const char *passwd; - const char *from; - const char *to; - const char *subject; - const char *cmd; - char auth=0; - uint16_t status=1; - SendEmail *mail=0; - uint16_t blen; - char *endcmd; - - - - uint16_t mem=ESP.getFreeHeap(); - if (memsend(from,to,subject,cmd); - delete mail; - if (result==true) status=0; - } - -exit: - if (oparams) free(oparams); - return status; -} - -#ifdef ESP8266 -void script_send_email_body(BearSSL::WiFiClientSecure_light *client); -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 BearSSL::WiFiClientSecure_light(1024,1024)) { -} -#else -void script_send_email_body(WiFiClient *client); -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()) { -} -#endif - -String SendEmail::readClient() { - delay(0); - String r = client->readStringUntil('\n'); - - r.trim(); - while (client->available()) { - delay(0); - r += client->readString(); - } - return r; -} - -bool SendEmail::send(const String& from, const String& to, const String& subject, const char *msg) { -bool status=false; -String buffer; - - if (!host.length()) { - return status; - } - - client->setTimeout(timeout); - -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("Connecting: %s on port %d"),host.c_str(),port); -#endif - - if (!client->connect(host.c_str(), port)) { -#ifdef DEBUG_EMAIL_PORT - AddLog_P(LOG_LEVEL_INFO, PSTR("Connection failed")); -#endif - goto exit; - } - - buffer = readClient(); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - if (!buffer.startsWith(F("220"))) { - goto exit; - } - - buffer = F("EHLO "); - buffer += client->localIP().toString(); - - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - buffer = readClient(); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - if (!buffer.startsWith(F("250"))) { - goto exit; - } - if (user.length()>0 && passwd.length()>0 ) { - - buffer = F("AUTH LOGIN"); - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - buffer = readClient(); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - if (!buffer.startsWith(F("334"))) - { - goto exit; - } - base64 b; - buffer = b.encode(user); - - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - buffer = readClient(); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - if (!buffer.startsWith(F("334"))) { - goto exit; - } - buffer = b.encode(passwd); - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - buffer = readClient(); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - if (!buffer.startsWith(F("235"))) { - goto exit; - } - } - - - buffer = F("MAIL FROM:"); - buffer += from; - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - buffer = readClient(); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - if (!buffer.startsWith(F("250"))) { - goto exit; - } - buffer = F("RCPT TO:"); - buffer += to; - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - buffer = readClient(); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - if (!buffer.startsWith(F("250"))) { - goto exit; - } - - buffer = F("DATA"); - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - buffer = readClient(); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - if (!buffer.startsWith(F("354"))) { - goto exit; - } - buffer = F("From: "); - buffer += from; - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - buffer = F("To: "); - buffer += to; - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - buffer = F("Subject: "); - buffer += subject; - buffer += F("\r\n"); - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - -#ifdef USE_SCRIPT - if (*msg=='*' && *(msg+1)==0) { - script_send_email_body(client); - } else { - client->println(msg); - } -#else - client->println(msg); -#endif - client->println('.'); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - - buffer = F("QUIT"); - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - - status=true; -exit: - - return status; -} - - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/settings.ino" -# 24 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/settings.ino" -const uint16_t RTC_MEM_VALID = 0xA55A; - -uint32_t rtc_settings_crc = 0; - -uint32_t GetRtcSettingsCrc(void) -{ - uint32_t crc = 0; - uint8_t *bytes = (uint8_t*)&RtcSettings; - - for (uint32_t i = 0; i < sizeof(RTCMEM); i++) { - crc += bytes[i]*(i+1); - } - return crc; -} - -void RtcSettingsSave(void) -{ - if (GetRtcSettingsCrc() != rtc_settings_crc) { - RtcSettings.valid = RTC_MEM_VALID; - ESP_rtcUserMemoryWrite(100, (uint32_t*)&RtcSettings, sizeof(RTCMEM)); - rtc_settings_crc = GetRtcSettingsCrc(); - } -} - -void RtcSettingsLoad(void) -{ - ESP_rtcUserMemoryRead(100, (uint32_t*)&RtcSettings, sizeof(RTCMEM)); - if (RtcSettings.valid != RTC_MEM_VALID) { - memset(&RtcSettings, 0, sizeof(RTCMEM)); - RtcSettings.valid = RTC_MEM_VALID; - RtcSettings.energy_kWhtoday = Settings.energy_kWhtoday; - RtcSettings.energy_kWhtotal = Settings.energy_kWhtotal; - RtcSettings.energy_usage = Settings.energy_usage; - for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - RtcSettings.pulse_counter[i] = Settings.pulse_counter[i]; - } - RtcSettings.power = Settings.power; - RtcSettingsSave(); - } - rtc_settings_crc = GetRtcSettingsCrc(); -} - -bool RtcSettingsValid(void) -{ - return (RTC_MEM_VALID == RtcSettings.valid); -} - - - -uint32_t rtc_reboot_crc = 0; - -uint32_t GetRtcRebootCrc(void) -{ - uint32_t crc = 0; - uint8_t *bytes = (uint8_t*)&RtcReboot; - - for (uint32_t i = 0; i < sizeof(RTCRBT); i++) { - crc += bytes[i]*(i+1); - } - return crc; -} - -void RtcRebootSave(void) -{ - if (GetRtcRebootCrc() != rtc_reboot_crc) { - RtcReboot.valid = RTC_MEM_VALID; - ESP_rtcUserMemoryWrite(100 - sizeof(RTCRBT), (uint32_t*)&RtcReboot, sizeof(RTCRBT)); - rtc_reboot_crc = GetRtcRebootCrc(); - } -} - -void RtcRebootReset(void) -{ - RtcReboot.fast_reboot_count = 0; - RtcRebootSave(); -} - -void RtcRebootLoad(void) -{ - ESP_rtcUserMemoryRead(100 - sizeof(RTCRBT), (uint32_t*)&RtcReboot, sizeof(RTCRBT)); - if (RtcReboot.valid != RTC_MEM_VALID) { - memset(&RtcReboot, 0, sizeof(RTCRBT)); - RtcReboot.valid = RTC_MEM_VALID; - - RtcRebootSave(); - } - rtc_reboot_crc = GetRtcRebootCrc(); -} - -bool RtcRebootValid(void) -{ - return (RTC_MEM_VALID == RtcReboot.valid); -} -# 139 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/settings.ino" -extern "C" { -#include "spi_flash.h" -} -#include "eboot_command.h" - -#ifdef ESP8266 - -#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) || defined(ARDUINO_ESP8266_RELEASE_2_5_0) || defined(ARDUINO_ESP8266_RELEASE_2_5_1) || defined(ARDUINO_ESP8266_RELEASE_2_5_2) - -extern "C" uint32_t _SPIFFS_end; - -const uint32_t SPIFFS_END = ((uint32_t)&_SPIFFS_end - 0x40200000) / SPI_FLASH_SEC_SIZE; - -#else - -#if AUTOFLASHSIZE - -#include "flash_hal.h" - - -const uint32_t SPIFFS_END = (FS_end - 0x40200000) / SPI_FLASH_SEC_SIZE; - -#else - -extern "C" uint32_t _FS_end; - -const uint32_t SPIFFS_END = ((uint32_t)&_FS_end - 0x40200000) / SPI_FLASH_SEC_SIZE; - -#endif - -#endif - - -const uint32_t SETTINGS_LOCATION = SPIFFS_END; - -#endif - - -const uint8_t CFG_ROTATES = 8; - -uint32_t settings_location = SETTINGS_LOCATION; -uint32_t settings_crc32 = 0; -uint8_t *settings_buffer = nullptr; - - - - - -void SetFlashModeDout(void) -{ -#ifdef ESP8266 - uint8_t *_buffer; - uint32_t address; - - eboot_command ebcmd; - eboot_command_read(&ebcmd); - address = ebcmd.args[0]; - _buffer = new uint8_t[FLASH_SECTOR_SIZE]; - - if (ESP.flashRead(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE)) { - if (_buffer[2] != 3) { - _buffer[2] = 3; - if (ESP.flashEraseSector(address / FLASH_SECTOR_SIZE)) { - ESP.flashWrite(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE); - } - } - } - delete[] _buffer; -#endif -} - -bool VersionCompatible(void) -{ -#ifdef ESP8266 - - if (Settings.flag3.compatibility_check) { - return true; - } - - eboot_command ebcmd; - eboot_command_read(&ebcmd); - uint32_t start_address = ebcmd.args[0]; - uint32_t end_address = start_address + (ebcmd.args[2] & 0xFFFFF000) + FLASH_SECTOR_SIZE; - uint32_t* buffer = new uint32_t[FLASH_SECTOR_SIZE / 4]; - - uint32_t version[3] = { 0 }; - bool found = false; - for (uint32_t address = start_address; address < end_address; address = address + FLASH_SECTOR_SIZE) { - ESP.flashRead(address, (uint32_t*)buffer, FLASH_SECTOR_SIZE); - if ((address == start_address) && (0x1F == (buffer[0] & 0xFF))) { - version[1] = 0xFFFFFFFF; - found = true; - } else { - for (uint32_t i = 0; i < (FLASH_SECTOR_SIZE / 4); i++) { - version[0] = version[1]; - version[1] = version[2]; - version[2] = buffer[i]; - if ((MARKER_START == version[0]) && (MARKER_END == version[2])) { - found = true; - break; - } - } - } - if (found) { break; } - } - delete[] buffer; - - if (!found) { version[1] = 0; } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("OTA: Version 0x%08X, Compatible 0x%08X"), version[1], VERSION_COMPATIBLE); - - if (version[1] < VERSION_COMPATIBLE) { - uint32_t eboot_magic = 0; - ESP.rtcUserMemoryWrite(0, (uint32_t*)&eboot_magic, sizeof(eboot_magic)); - return false; - } - -#endif - - return true; -} - -void SettingsBufferFree(void) -{ - if (settings_buffer != nullptr) { - free(settings_buffer); - settings_buffer = nullptr; - } -} - -bool SettingsBufferAlloc(void) -{ - SettingsBufferFree(); - if (!(settings_buffer = (uint8_t *)malloc(sizeof(Settings)))) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_UPLOAD_ERR_2)); - return false; - } - return true; -} - -uint16_t GetCfgCrc16(uint8_t *bytes, uint32_t size) -{ - uint16_t crc = 0; - - for (uint32_t i = 0; i < size; i++) { - if ((i < 14) || (i > 15)) { crc += bytes[i]*(i+1); } - } - return crc; -} - -uint16_t GetSettingsCrc(void) -{ - - 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) -{ - - 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); -} - -void SettingsSaveAll(void) -{ - if (Settings.flag.save_state) { - Settings.power = power; - } else { - Settings.power = 0; - } - XsnsCall(FUNC_SAVE_BEFORE_RESTART); - XdrvCall(FUNC_SAVE_BEFORE_RESTART); - SettingsSave(0); -} - - - - - -void UpdateQuickPowerCycle(bool update) -{ - if (Settings.flag3.fast_power_cycle_disable) { return; } - - uint32_t pc_register; - uint32_t pc_location = SETTINGS_LOCATION - CFG_ROTATES; - - ESP.flashRead(pc_location * SPI_FLASH_SEC_SIZE, (uint32*)&pc_register, sizeof(pc_register)); - if (update && ((pc_register & 0xFFFFFFF0) == 0xFFA55AB0)) { - uint32_t counter = ((pc_register & 0xF) << 1) & 0xF; - if (0 == counter) { - SettingsErase(3); - EspRestart(); - } else { - pc_register = 0xFFA55AB0 | counter; - ESP.flashWrite(pc_location * SPI_FLASH_SEC_SIZE, (uint32*)&pc_register, sizeof(pc_register)); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("QPC: Flag %02X"), counter); - } - } - else if (pc_register != 0xFFA55ABF) { - pc_register = 0xFFA55ABF; - - if (ESP.flashEraseSector(pc_location)) { - ESP.flashWrite(pc_location * SPI_FLASH_SEC_SIZE, (uint32*)&pc_register, sizeof(pc_register)); - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("QPC: Reset")); - } -} - - - - - -uint32_t GetSettingsTextLen(void) -{ - char* position = Settings.text_pool; - for (uint32_t size = 0; size < SET_MAX; size++) { - while (*position++ != '\0') { } - } - return position - Settings.text_pool; -} - -bool SettingsUpdateText(uint32_t index, const char* replace_me) -{ - if (index >= SET_MAX) { - return false; - } - - - uint32_t replace_len = strlen(replace_me); - char replace[replace_len +1]; - memcpy(replace, replace_me, sizeof(replace)); - - uint32_t start_pos = 0; - uint32_t end_pos = 0; - char* position = Settings.text_pool; - for (uint32_t size = 0; size < SET_MAX; size++) { - while (*position++ != '\0') { } - if (1 == index) { - start_pos = position - Settings.text_pool; - } - else if (0 == index) { - end_pos = position - Settings.text_pool -1; - } - index--; - } - uint32_t char_len = position - Settings.text_pool; - - uint32_t current_len = end_pos - start_pos; - int diff = replace_len - current_len; - - - - - int too_long = (char_len + diff) - settings_text_size; - if (too_long > 0) { - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_CONFIG "Text overflow by %d char(s)"), too_long); - return false; - } - - if (diff != 0) { - - memmove_P(Settings.text_pool + start_pos + replace_len, Settings.text_pool + end_pos, char_len - end_pos); - } - - memmove_P(Settings.text_pool + start_pos, replace, replace_len); - - memset(Settings.text_pool + char_len + diff, 0x00, settings_text_size - char_len - diff); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG "CR %d/%d"), GetSettingsTextLen(), settings_text_size); - - return true; -} - -char* SettingsText(uint32_t index) -{ - char* position = Settings.text_pool; - - if (index >= SET_MAX) { - position += settings_text_size -1; - } else { - for (;index > 0; index--) { - while (*position++ != '\0') { } - } - } - return position; -} - - - - - -void UpdateBackwardCompatibility(void) -{ - - strlcpy(Settings.user_template_name, SettingsText(SET_TEMPLATE_NAME), sizeof(Settings.user_template_name)); -} - -uint32_t GetSettingsAddress(void) -{ - return settings_location * SPI_FLASH_SEC_SIZE; -} - -void SettingsSave(uint8_t rotate) -{ -# 464 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/settings.ino" -#ifndef FIRMWARE_MINIMAL - UpdateBackwardCompatibility(); - if ((GetSettingsCrc32() != settings_crc32) || rotate) { - if (1 == rotate) { - stop_flash_rotate = 1; - } - if (2 == rotate) { - settings_location = SETTINGS_LOCATION +1; - } - if (stop_flash_rotate) { - settings_location = SETTINGS_LOCATION; - } else { - settings_location--; - if (settings_location <= (SETTINGS_LOCATION - CFG_ROTATES)) { - settings_location = SETTINGS_LOCATION; - } - } - - Settings.save_flag++; - if (UtcTime() > START_VALID_TIME) { - Settings.cfg_timestamp = UtcTime(); - } else { - Settings.cfg_timestamp++; - } - Settings.cfg_size = sizeof(SYSCFG); - Settings.cfg_crc = GetSettingsCrc(); - Settings.cfg_crc32 = GetSettingsCrc32(); - -#ifdef ESP8266 - if (ESP.flashEraseSector(settings_location)) { - ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); - } - - if (!stop_flash_rotate && rotate) { - for (uint32_t i = 1; i < CFG_ROTATES; i++) { - ESP.flashEraseSector(settings_location -i); - delay(1); - } - } -#else - SettingsSaveMain(&Settings, sizeof(SYSCFG)); -#endif - - 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_crc32 = Settings.cfg_crc32; - } -#endif - RtcSettingsSave(); -} - -void SettingsLoad(void) -{ - - struct SYSCFGH { - uint16_t cfg_holder; - uint16_t cfg_size; - unsigned long save_flag; - } _SettingsH; - unsigned long save_flag = 0; - - settings_location = 0; - uint32_t flash_location = SETTINGS_LOCATION +1; - uint16_t cfg_holder = 0; - for (uint32_t i = 0; i < CFG_ROTATES; i++) { - flash_location--; - ESP_flashRead(flash_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); - bool valid = false; - if (Settings.version > 0x06000000) { - bool almost_valid = (Settings.cfg_crc32 == GetSettingsCrc32()); - if (Settings.version < 0x0606000B) { - almost_valid = (Settings.cfg_crc == GetSettingsCrc()); - } - - if (almost_valid && (0 == cfg_holder)) { cfg_holder = Settings.cfg_holder; } - valid = (cfg_holder == Settings.cfg_holder); - } else { - ESP_flashReadHeader((flash_location -1) * SPI_FLASH_SEC_SIZE, (uint32*)&_SettingsH, sizeof(SYSCFGH)); - valid = (Settings.cfg_holder == _SettingsH.cfg_holder); - } - if (valid) { - if (Settings.save_flag > save_flag) { - save_flag = Settings.save_flag; - settings_location = flash_location; - if (Settings.flag.stop_flash_rotate && (0 == i)) { - break; - } - } - } - - delay(1); - } - if (settings_location > 0) { - ESP_flashRead(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); - AddLog_P2(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG D_LOADED_FROM_FLASH_AT " %X, " D_COUNT " %lu"), settings_location, Settings.save_flag); - } - -#ifndef FIRMWARE_MINIMAL - if (!settings_location || (Settings.cfg_holder != (uint16_t)CFG_HOLDER)) { - SettingsDefault(); - } - settings_crc32 = GetSettingsCrc32(); -#endif - - RtcSettingsLoad(); -} - -void EspErase(uint32_t start_sector, uint32_t end_sector) -{ - bool serial_output = (LOG_LEVEL_DEBUG_MORE <= seriallog_level); - for (uint32_t sector = start_sector; sector < end_sector; sector++) { - - bool result = ESP.flashEraseSector(sector); - - - - if (serial_output) { -#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 - Serial.printf(D_LOG_APPLICATION D_ERASED_SECTOR " %d %s\n", sector, (result) ? D_OK : D_ERROR); -#else - Serial.printf_P(PSTR(D_LOG_APPLICATION D_ERASED_SECTOR " %d %s\n"), sector, (result) ? D_OK : D_ERROR); -#endif - delay(10); - } else { - yield(); - } - OsWatchLoop(); - } -} - -#ifdef ESP8266 -void SettingsErase(uint8_t type) -{ -# 613 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/settings.ino" -#ifndef FIRMWARE_MINIMAL - uint32_t _sectorStart = (ESP.getSketchSize() / SPI_FLASH_SEC_SIZE) + 1; - uint32_t _sectorEnd = ESP.getFlashChipRealSize() / SPI_FLASH_SEC_SIZE; - if (1 == type) { - - _sectorStart = (ESP.getFlashChipSize() / SPI_FLASH_SEC_SIZE) - 4; - } - else if (2 == type) { - _sectorStart = SETTINGS_LOCATION - CFG_ROTATES; - _sectorEnd = SETTINGS_LOCATION +1; - } - else if (3 == type) { - _sectorStart = SETTINGS_LOCATION - CFG_ROTATES; - _sectorEnd = ESP.getFlashChipSize() / SPI_FLASH_SEC_SIZE; - } - else if (4 == type) { - - _sectorStart = SETTINGS_LOCATION +1; - _sectorEnd = _sectorStart +1; - } - - - - - - - else { - return; - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " from 0x%08X to 0x%08X"), _sectorStart * SPI_FLASH_SEC_SIZE, (_sectorEnd * SPI_FLASH_SEC_SIZE) -1); - - - EsptoolErase(_sectorStart, _sectorEnd); -#endif -} -#endif - -void SettingsSdkErase(void) -{ - WiFi.disconnect(false); - SettingsErase(1); - delay(1000); -} - - - -void SettingsDefault(void) -{ - AddLog_P(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG D_USE_DEFAULTS)); - SettingsDefaultSet1(); - SettingsDefaultSet2(); - SettingsSave(2); -} - -void SettingsDefaultSet1(void) -{ - memset(&Settings, 0x00, sizeof(SYSCFG)); - - Settings.cfg_holder = (uint16_t)CFG_HOLDER; - Settings.cfg_size = sizeof(SYSCFG); - - Settings.version = VERSION; - - -} - -void SettingsDefaultSet2(void) -{ - memset((char*)&Settings +16, 0x00, sizeof(SYSCFG) -16); - - Settings.flag.stop_flash_rotate = APP_FLASH_CYCLE; - Settings.flag.global_state = APP_ENABLE_LEDLINK; - Settings.flag3.sleep_normal = APP_NORMAL_SLEEP; - Settings.flag3.no_power_feedback = APP_NO_RELAY_SCAN; - Settings.flag3.fast_power_cycle_disable = APP_DISABLE_POWERCYCLE; - Settings.flag3.bootcount_update = DEEPSLEEP_BOOTCOUNT; - Settings.flag3.compatibility_check = OTA_COMPATIBILITY; - Settings.save_data = SAVE_DATA; - Settings.param[P_BACKLOG_DELAY] = MIN_BACKLOG_DELAY; - Settings.param[P_BOOT_LOOP_OFFSET] = BOOT_LOOP_OFFSET; - Settings.param[P_RGB_REMAP] = RGB_REMAP_RGBW; - Settings.sleep = APP_SLEEP; - if (Settings.sleep < 50) { - Settings.sleep = 50; - } - - - - Settings.interlock[0] = 0xFF; - Settings.module = MODULE; - ModuleDefault(WEMOS); - - SettingsUpdateText(SET_FRIENDLYNAME1, FRIENDLY_NAME); - SettingsUpdateText(SET_FRIENDLYNAME2, FRIENDLY_NAME"2"); - SettingsUpdateText(SET_FRIENDLYNAME3, FRIENDLY_NAME"3"); - SettingsUpdateText(SET_FRIENDLYNAME4, FRIENDLY_NAME"4"); - SettingsUpdateText(SET_OTAURL, OTA_URL); - - - Settings.flag.save_state = SAVE_STATE; - Settings.power = APP_POWER; - Settings.poweronstate = APP_POWERON_STATE; - Settings.blinktime = APP_BLINKTIME; - Settings.blinkcount = APP_BLINKCOUNT; - Settings.ledstate = APP_LEDSTATE; - Settings.ledmask = APP_LEDMASK; - Settings.pulse_timer[0] = APP_PULSETIME; - - - - Settings.serial_config = TS_SERIAL_8N1; - Settings.baudrate = APP_BAUDRATE / 300; - Settings.sbaudrate = SOFT_BAUDRATE / 300; - Settings.serial_delimiter = 0xff; - Settings.seriallog_level = SERIAL_LOG_LEVEL; - - - Settings.flag3.use_wifi_scan = WIFI_SCAN_AT_RESTART; - Settings.flag3.use_wifi_rescan = WIFI_SCAN_REGULARLY; - Settings.wifi_output_power = 170; - Settings.param[P_ARP_GRATUITOUS] = WIFI_ARP_INTERVAL; - ParseIp(&Settings.ip_address[0], WIFI_IP_ADDRESS); - ParseIp(&Settings.ip_address[1], WIFI_GATEWAY); - ParseIp(&Settings.ip_address[2], WIFI_SUBNETMASK); - ParseIp(&Settings.ip_address[3], WIFI_DNS); - Settings.sta_config = WIFI_CONFIG_TOOL; - - SettingsUpdateText(SET_STASSID1, STA_SSID1); - SettingsUpdateText(SET_STASSID2, STA_SSID2); - SettingsUpdateText(SET_STAPWD1, STA_PASS1); - SettingsUpdateText(SET_STAPWD2, STA_PASS2); - SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); - - - SettingsUpdateText(SET_SYSLOG_HOST, SYS_LOG_HOST); - Settings.syslog_port = SYS_LOG_PORT; - Settings.syslog_level = SYS_LOG_LEVEL; - - - Settings.flag2.emulation = EMULATION; - Settings.flag3.gui_hostname_ip = GUI_SHOW_HOSTNAME; - Settings.flag3.mdns_enabled = MDNS_ENABLED; - Settings.webserver = WEB_SERVER; - Settings.weblog_level = WEB_LOG_LEVEL; - SettingsUpdateText(SET_WEBPWD, WEB_PASSWORD); - SettingsUpdateText(SET_CORS, CORS_DOMAIN); - - - Settings.flag.button_restrict = KEY_DISABLE_MULTIPRESS; - Settings.flag.button_swap = KEY_SWAP_DOUBLE_PRESS; - Settings.flag.button_single = KEY_ONLY_SINGLE_PRESS; - Settings.param[P_HOLD_TIME] = KEY_HOLD_TIME; - - - for (uint32_t i = 0; i < MAX_SWITCHES; i++) { Settings.switchmode[i] = SWITCH_MODE; } - - - Settings.flag.mqtt_enabled = MQTT_USE; - Settings.flag.mqtt_response = MQTT_RESULT_COMMAND; - Settings.flag.mqtt_offline = MQTT_LWT_MESSAGE; - Settings.flag.mqtt_power_retain = MQTT_POWER_RETAIN; - Settings.flag.mqtt_button_retain = MQTT_BUTTON_RETAIN; - Settings.flag.mqtt_switch_retain = MQTT_SWITCH_RETAIN; - Settings.flag.mqtt_sensor_retain = MQTT_SENSOR_RETAIN; - - Settings.flag.device_index_enable = MQTT_POWER_FORMAT; - Settings.flag3.time_append_timezone = MQTT_APPEND_TIMEZONE; - Settings.flag3.button_switch_force_local = MQTT_BUTTON_SWITCH_FORCE_LOCAL; - Settings.flag3.no_hold_retain = MQTT_NO_HOLD_RETAIN; - Settings.flag3.use_underscore = MQTT_INDEX_SEPARATOR; - Settings.flag3.grouptopic_mode = MQTT_GROUPTOPIC_FORMAT; - SettingsUpdateText(SET_MQTT_HOST, MQTT_HOST); - Settings.mqtt_port = MQTT_PORT; - SettingsUpdateText(SET_MQTT_CLIENT, MQTT_CLIENT_ID); - SettingsUpdateText(SET_MQTT_USER, MQTT_USER); - SettingsUpdateText(SET_MQTT_PWD, MQTT_PASS); - SettingsUpdateText(SET_MQTT_TOPIC, MQTT_TOPIC); - SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, MQTT_BUTTON_TOPIC); - SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, MQTT_SWITCH_TOPIC); - SettingsUpdateText(SET_MQTT_GRP_TOPIC, MQTT_GRPTOPIC); - SettingsUpdateText(SET_MQTT_FULLTOPIC, MQTT_FULLTOPIC); - Settings.mqtt_retry = MQTT_RETRY_SECS; - SettingsUpdateText(SET_MQTTPREFIX1, SUB_PREFIX); - SettingsUpdateText(SET_MQTTPREFIX2, PUB_PREFIX); - SettingsUpdateText(SET_MQTTPREFIX3, PUB_PREFIX2); - SettingsUpdateText(SET_STATE_TXT1, MQTT_STATUS_OFF); - SettingsUpdateText(SET_STATE_TXT2, MQTT_STATUS_ON); - SettingsUpdateText(SET_STATE_TXT3, MQTT_CMND_TOGGLE); - SettingsUpdateText(SET_STATE_TXT4, MQTT_CMND_HOLD); - char fingerprint[60]; - strlcpy(fingerprint, MQTT_FINGERPRINT1, sizeof(fingerprint)); - char *p = fingerprint; - for (uint32_t i = 0; i < 20; i++) { - Settings.mqtt_fingerprint[0][i] = strtol(p, &p, 16); - } - strlcpy(fingerprint, MQTT_FINGERPRINT2, sizeof(fingerprint)); - p = fingerprint; - for (uint32_t i = 0; i < 20; i++) { - Settings.mqtt_fingerprint[1][i] = strtol(p, &p, 16); - } - Settings.tele_period = TELE_PERIOD; - Settings.mqttlog_level = MQTT_LOG_LEVEL; - - - Settings.flag.no_power_on_check = ENERGY_VOLTAGE_ALWAYS; - Settings.flag2.current_resolution = 3; - - - Settings.flag2.energy_resolution = ENERGY_RESOLUTION; - Settings.flag3.dds2382_model = ENERGY_DDS2382_MODE; - Settings.flag3.hardware_energy_total = ENERGY_HARDWARE_TOTALS; - Settings.param[P_MAX_POWER_RETRY] = MAX_POWER_RETRY; - - Settings.energy_power_calibration = HLW_PREF_PULSE; - Settings.energy_voltage_calibration = HLW_UREF_PULSE; - Settings.energy_current_calibration = HLW_IREF_PULSE; -# 840 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/settings.ino" - Settings.energy_max_power_limit_hold = MAX_POWER_HOLD; - Settings.energy_max_power_limit_window = MAX_POWER_WINDOW; - - Settings.energy_max_power_safe_limit_hold = SAFE_POWER_HOLD; - Settings.energy_max_power_safe_limit_window = SAFE_POWER_WINDOW; - - - - RtcSettings.energy_kWhtotal = 0; - - memset((char*)&RtcSettings.energy_usage, 0x00, sizeof(RtcSettings.energy_usage)); - Settings.param[P_OVER_TEMP] = ENERGY_OVERTEMP; - - - Settings.flag.ir_receive_decimal = IR_DATA_RADIX; - Settings.flag3.receive_raw = IR_ADD_RAW_DATA; - Settings.param[P_IR_UNKNOW_THRESHOLD] = IR_RCV_MIN_UNKNOWN_SIZE; - - - Settings.flag.rf_receive_decimal = RF_DATA_RADIX; - - memcpy_P(Settings.rf_code[0], kDefaultRfCode, 9); - - - Settings.domoticz_update_timer = DOMOTICZ_UPDATE_TIMER; -# 875 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/settings.ino" - Settings.flag.temperature_conversion = TEMP_CONVERSION; - Settings.flag.pressure_conversion = PRESSURE_CONVERSION; - Settings.flag2.pressure_resolution = PRESSURE_RESOLUTION; - Settings.flag2.humidity_resolution = HUMIDITY_RESOLUTION; - Settings.flag2.temperature_resolution = TEMP_RESOLUTION; - Settings.flag3.ds18x20_internal_pullup = DS18X20_PULL_UP; - Settings.flag3.counter_reset_on_tele = COUNTER_RESET; - - - - - - - Settings.flag2.calc_resolution = CALC_RESOLUTION; - - - Settings.flag3.timers_enable = TIMERS_ENABLED; - - - Settings.flag.hass_light = HASS_AS_LIGHT; - Settings.flag.hass_discovery = HOME_ASSISTANT_DISCOVERY_ENABLE; - Settings.flag3.hass_tele_on_power = TELE_ON_POWER; - - - Settings.flag.knx_enabled = KNX_ENABLED; - Settings.flag.knx_enable_enhancement = KNX_ENHANCED; - - - Settings.flag.pwm_control = LIGHT_MODE; - Settings.flag.ws_clock_reverse = LIGHT_CLOCK_DIRECTION; - Settings.flag.light_signal = LIGHT_PAIRS_CO2; - Settings.flag.not_power_linked = LIGHT_POWER_CONTROL; - Settings.flag.decimal_text = LIGHT_COLOR_RADIX; - Settings.flag3.pwm_multi_channels = LIGHT_CHANNEL_MODE; - Settings.flag3.slider_dimmer_stay_on = LIGHT_SLIDER_POWER; - Settings.flag4.alexa_ct_range = LIGHT_ALEXA_CT_RANGE; - - Settings.pwm_frequency = PWM_FREQ; - Settings.pwm_range = PWM_RANGE; - for (uint32_t i = 0; i < MAX_PWMS; i++) { - Settings.light_color[i] = DEFAULT_LIGHT_COMPONENT; - - } - Settings.light_correction = 1; - Settings.light_dimmer = DEFAULT_LIGHT_DIMMER; - - Settings.light_speed = 1; - - Settings.light_width = 1; - - Settings.light_pixels = WS2812_LEDS; - - Settings.ws_width[WS_SECOND] = 1; - Settings.ws_color[WS_SECOND][WS_RED] = 255; - - Settings.ws_color[WS_SECOND][WS_BLUE] = 255; - Settings.ws_width[WS_MINUTE] = 3; - - Settings.ws_color[WS_MINUTE][WS_GREEN] = 255; - - Settings.ws_width[WS_HOUR] = 5; - Settings.ws_color[WS_HOUR][WS_RED] = 255; - - - - Settings.dimmer_hw_max = DEFAULT_DIMMER_MAX; - Settings.dimmer_hw_min = DEFAULT_DIMMER_MIN; - - - - Settings.display_mode = 1; - Settings.display_refresh = 2; - Settings.display_rows = 2; - Settings.display_cols[0] = 16; - Settings.display_cols[1] = 8; - Settings.display_dimmer = 1; - Settings.display_size = 1; - Settings.display_font = 1; - - Settings.display_address[0] = MTX_ADDRESS1; - Settings.display_address[1] = MTX_ADDRESS2; - Settings.display_address[2] = MTX_ADDRESS3; - Settings.display_address[3] = MTX_ADDRESS4; - Settings.display_address[4] = MTX_ADDRESS5; - Settings.display_address[5] = MTX_ADDRESS6; - Settings.display_address[6] = MTX_ADDRESS7; - Settings.display_address[7] = MTX_ADDRESS8; - - - if (((APP_TIMEZONE > -14) && (APP_TIMEZONE < 15)) || (99 == APP_TIMEZONE)) { - Settings.timezone = APP_TIMEZONE; - Settings.timezone_minutes = 0; - } else { - Settings.timezone = APP_TIMEZONE / 60; - Settings.timezone_minutes = abs(APP_TIMEZONE % 60); - } - SettingsUpdateText(SET_NTPSERVER1, NTP_SERVER1); - SettingsUpdateText(SET_NTPSERVER2, NTP_SERVER2); - SettingsUpdateText(SET_NTPSERVER3, NTP_SERVER3); - for (uint32_t i = 0; i < MAX_NTP_SERVERS; i++) { - SettingsUpdateText(SET_NTPSERVER1 +i, ReplaceCommaWithDot(SettingsText(SET_NTPSERVER1 +i))); - } - Settings.latitude = (int)((double)LATITUDE * 1000000); - Settings.longitude = (int)((double)LONGITUDE * 1000000); - SettingsResetStd(); - SettingsResetDst(); - - Settings.button_debounce = KEY_DEBOUNCE_TIME; - Settings.switch_debounce = SWITCH_DEBOUNCE_TIME; - - for (uint32_t j = 0; j < 5; j++) { - Settings.rgbwwTable[j] = 255; - } - - Settings.novasds_startingoffset = STARTING_OFFSET; - - SettingsDefaultWebColor(); - - memset(&Settings.monitors, 0xFF, 20); - SettingsEnableAllI2cDrivers(); - - - Settings.flag3.tuya_apply_o20 = TUYA_SETOPTION_20; - Settings.flag3.tuya_serial_mqtt_publish = MQTT_TUYA_RECEIVED; - - Settings.flag3.buzzer_enable = BUZZER_ENABLE; - Settings.flag3.shutter_mode = SHUTTER_SUPPORT; - Settings.flag3.pcf8574_ports_inverted = PCF8574_INVERT_PORTS; - Settings.flag4.zigbee_use_names = ZIGBEE_FRIENDLY_NAMES; -} - - - -void SettingsResetStd(void) -{ - Settings.tflag[0].hemis = TIME_STD_HEMISPHERE; - Settings.tflag[0].week = TIME_STD_WEEK; - Settings.tflag[0].dow = TIME_STD_DAY; - Settings.tflag[0].month = TIME_STD_MONTH; - Settings.tflag[0].hour = TIME_STD_HOUR; - Settings.toffset[0] = TIME_STD_OFFSET; -} - -void SettingsResetDst(void) -{ - Settings.tflag[1].hemis = TIME_DST_HEMISPHERE; - Settings.tflag[1].week = TIME_DST_WEEK; - Settings.tflag[1].dow = TIME_DST_DAY; - Settings.tflag[1].month = TIME_DST_MONTH; - Settings.tflag[1].hour = TIME_DST_HOUR; - Settings.toffset[1] = TIME_DST_OFFSET; -} - -void SettingsDefaultWebColor(void) -{ - char scolor[10]; - for (uint32_t i = 0; i < COL_LAST; i++) { - WebHexCode(i, GetTextIndexed(scolor, sizeof(scolor), i, kWebColors)); - } -} - -void SettingsEnableAllI2cDrivers(void) -{ - Settings.i2c_drivers[0] = 0xFFFFFFFF; - Settings.i2c_drivers[1] = 0xFFFFFFFF; - Settings.i2c_drivers[2] = 0xFFFFFFFF; -} - - - -void SettingsDelta(void) -{ - if (Settings.version != VERSION) { - -#ifdef ESP8266 - if (Settings.version < 0x06000000) { - Settings.cfg_size = sizeof(SYSCFG); - Settings.cfg_crc = GetSettingsCrc(); - } - if (Settings.version < 0x06000002) { - for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - if (i < 4) { - Settings.switchmode[i] = Settings.interlock[i]; - } else { - Settings.switchmode[i] = SWITCH_MODE; - } - } - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - if (Settings.my_gp.io[i] >= GPIO_SWT5) { - Settings.my_gp.io[i] += 4; - } - } - } - if (Settings.version < 0x06000003) { - Settings.flag.mqtt_serial_raw = 0; - Settings.flag.pressure_conversion = 0; - Settings.flag3.data = 0; - } - if (Settings.version < 0x06010103) { - Settings.flag3.timers_enable = 1; - } - if (Settings.version < 0x0601010C) { - Settings.button_debounce = KEY_DEBOUNCE_TIME; - Settings.switch_debounce = SWITCH_DEBOUNCE_TIME; - } - if (Settings.version < 0x0602010A) { - for (uint32_t j = 0; j < 5; j++) { - Settings.rgbwwTable[j] = 255; - } - } - if (Settings.version < 0x06030002) { - Settings.timezone_minutes = 0; - } - if (Settings.version < 0x06030004) { - memset(&Settings.monitors, 0xFF, 20); - } - if (Settings.version < 0x0603000E) { - Settings.flag2.calc_resolution = CALC_RESOLUTION; - } - if (Settings.version < 0x0603000F) { - if (Settings.sleep < 50) { - Settings.sleep = 50; - } - } - if (Settings.version < 0x06040105) { - Settings.flag3.mdns_enabled = MDNS_ENABLED; - Settings.param[P_MDNS_DELAYED_START] = 0; - } - if (Settings.version < 0x0604010B) { - Settings.interlock[0] = 0xFF; - for (uint32_t i = 1; i < MAX_INTERLOCKS; i++) { Settings.interlock[i] = 0; } - } - if (Settings.version < 0x0604010D) { - Settings.param[P_BOOT_LOOP_OFFSET] = BOOT_LOOP_OFFSET; - } - if (Settings.version < 0x06040110) { - ModuleDefault(WEMOS); - } - if (Settings.version < 0x06040113) { - Settings.param[P_RGB_REMAP] = RGB_REMAP_RGBW; - } - if (Settings.version < 0x06050003) { - Settings.novasds_startingoffset = STARTING_OFFSET; - } - if (Settings.version < 0x06050006) { - SettingsDefaultWebColor(); - } - if (Settings.version < 0x06050007) { - Settings.ledmask = APP_LEDMASK; - } - if (Settings.version < 0x0605000A) { - Settings.my_adc0 = ADC0_NONE; - } - if (Settings.version < 0x0605000D) { - Settings.param[P_IR_UNKNOW_THRESHOLD] = IR_RCV_MIN_UNKNOWN_SIZE; - } - if (Settings.version < 0x06060001) { - Settings.param[P_OVER_TEMP] = ENERGY_OVERTEMP; - } - if (Settings.version < 0x06060007) { - memset((char*)&Settings +0xE00, 0x00, sizeof(SYSCFG) -0xE00); - } - if (Settings.version < 0x06060008) { - - if (Settings.flag3.tuya_serial_mqtt_publish) { - Settings.param[P_ex_DIMMER_MAX] = 100; - } else { - Settings.param[P_ex_DIMMER_MAX] = 255; - } - } - if (Settings.version < 0x06060009) { - Settings.baudrate = APP_BAUDRATE / 300; - Settings.sbaudrate = SOFT_BAUDRATE / 300; - } - if (Settings.version < 0x0606000A) { - uint8_t tuyaindex = 0; - if (Settings.param[P_BACKLOG_DELAY] > 0) { - Settings.tuya_fnid_map[tuyaindex].fnid = 21; - Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_BACKLOG_DELAY]; - tuyaindex++; - } else if (Settings.flag3.fast_power_cycle_disable == 1) { - Settings.tuya_fnid_map[tuyaindex].fnid = 11; - Settings.tuya_fnid_map[tuyaindex].dpid = 1; - tuyaindex++; - } - if (Settings.param[P_ARP_GRATUITOUS] > 0) { - for (uint8_t i = 0 ; i < Settings.param[P_ARP_GRATUITOUS]; i++) { - Settings.tuya_fnid_map[tuyaindex].fnid = 12 + i; - Settings.tuya_fnid_map[tuyaindex].dpid = i + 2; - tuyaindex++; - } - } - if (Settings.param[P_ex_TUYA_POWER_ID] > 0) { - Settings.tuya_fnid_map[tuyaindex].fnid = 31; - Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_ex_TUYA_POWER_ID]; - tuyaindex++; - } - if (Settings.param[P_ex_TUYA_VOLTAGE_ID] > 0) { - Settings.tuya_fnid_map[tuyaindex].fnid = 33; - Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_ex_TUYA_VOLTAGE_ID]; - tuyaindex++; - } - if (Settings.param[P_ex_TUYA_CURRENT_ID] > 0) { - Settings.tuya_fnid_map[tuyaindex].fnid = 32; - Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_ex_TUYA_CURRENT_ID]; - } - } - if (Settings.version < 0x0606000C) { - memset((char*)&Settings +0x1D6, 0x00, 16); - } - if (Settings.version < 0x0606000F) { - Settings.ex_shutter_accuracy = 0; - Settings.ex_mqttlog_level = MQTT_LOG_LEVEL; - } - if (Settings.version < 0x06060011) { - Settings.param[P_BACKLOG_DELAY] = MIN_BACKLOG_DELAY; - } - if (Settings.version < 0x06060012) { - Settings.dimmer_hw_min = DEFAULT_DIMMER_MIN; - Settings.dimmer_hw_max = DEFAULT_DIMMER_MAX; - if (TUYA_DIMMER == Settings.module) { - if (Settings.flag3.ex_tuya_dimmer_min_limit) { - Settings.dimmer_hw_min = 25; - } else { - Settings.dimmer_hw_min = 1; - } - Settings.dimmer_hw_max = Settings.param[P_ex_DIMMER_MAX]; - } - else if (PS_16_DZ == Settings.module) { - Settings.dimmer_hw_min = 10; - Settings.dimmer_hw_max = Settings.param[P_ex_DIMMER_MAX]; - } - } - if (Settings.version < 0x06060014) { -# 1221 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/settings.ino" - Settings.flag3.fast_power_cycle_disable = 0; - Settings.energy_power_delta = Settings.ex_energy_power_delta; - Settings.ex_energy_power_delta = 0; - } - if (Settings.version < 0x06060015) { - if ((EX_WIFI_SMARTCONFIG == Settings.ex_sta_config) || (EX_WIFI_WPSCONFIG == Settings.ex_sta_config)) { - Settings.ex_sta_config = WIFI_MANAGER; - } - } - - if (Settings.version < 0x07000002) { - Settings.web_color2[0][0] = Settings.web_color[0][0]; - Settings.web_color2[0][1] = Settings.web_color[0][1]; - Settings.web_color2[0][2] = Settings.web_color[0][2]; - } - if (Settings.version < 0x07000003) { - SettingsEnableAllI2cDrivers(); - } - if (Settings.version < 0x07000004) { - Settings.ex_wifi_output_power = 170; - } - if (Settings.version < 0x07010202) { - Settings.ex_serial_config = TS_SERIAL_8N1; - } - if (Settings.version < 0x07010204) { - if (Settings.flag3.mqtt_buttons == 1) { - strlcpy(Settings.ex_cors_domain, CORS_ENABLED_ALL, sizeof(Settings.ex_cors_domain)); - } else { - Settings.ex_cors_domain[0] = 0; - } - } - if (Settings.version < 0x07010205) { - Settings.seriallog_level = Settings.ex_seriallog_level; - Settings.sta_config = Settings.ex_sta_config; - Settings.sta_active = Settings.ex_sta_active; - memcpy((char*)&Settings.rule_stop, (char*)&Settings.ex_rule_stop, 47); - } - if (Settings.version < 0x07010206) { - Settings.flag4 = Settings.ex_flag4; - Settings.mqtt_port = Settings.ex_mqtt_port; - memcpy((char*)&Settings.serial_config, (char*)&Settings.ex_serial_config, 5); - } - if (Settings.version < 0x08000000) { - char temp[strlen(Settings.text_pool) +1]; strncpy(temp, Settings.text_pool, sizeof(temp)); - char temp21[strlen(Settings.ex_mqtt_prefix[0]) +1]; strncpy(temp21, Settings.ex_mqtt_prefix[0], sizeof(temp21)); - char temp22[strlen(Settings.ex_mqtt_prefix[1]) +1]; strncpy(temp22, Settings.ex_mqtt_prefix[1], sizeof(temp22)); - char temp23[strlen(Settings.ex_mqtt_prefix[2]) +1]; strncpy(temp23, Settings.ex_mqtt_prefix[2], sizeof(temp23)); - char temp31[strlen(Settings.ex_sta_ssid[0]) +1]; strncpy(temp31, Settings.ex_sta_ssid[0], sizeof(temp31)); - char temp32[strlen(Settings.ex_sta_ssid[1]) +1]; strncpy(temp32, Settings.ex_sta_ssid[1], sizeof(temp32)); - char temp41[strlen(Settings.ex_sta_pwd[0]) +1]; strncpy(temp41, Settings.ex_sta_pwd[0], sizeof(temp41)); - char temp42[strlen(Settings.ex_sta_pwd[1]) +1]; strncpy(temp42, Settings.ex_sta_pwd[1], sizeof(temp42)); - char temp5[strlen(Settings.ex_hostname) +1]; strncpy(temp5, Settings.ex_hostname, sizeof(temp5)); - char temp6[strlen(Settings.ex_syslog_host) +1]; strncpy(temp6, Settings.ex_syslog_host, sizeof(temp6)); - char temp7[strlen(Settings.ex_mqtt_host) +1]; strncpy(temp7, Settings.ex_mqtt_host, sizeof(temp7)); - char temp8[strlen(Settings.ex_mqtt_client) +1]; strncpy(temp8, Settings.ex_mqtt_client, sizeof(temp8)); - char temp9[strlen(Settings.ex_mqtt_user) +1]; strncpy(temp9, Settings.ex_mqtt_user, sizeof(temp9)); - char temp10[strlen(Settings.ex_mqtt_pwd) +1]; strncpy(temp10, Settings.ex_mqtt_pwd, sizeof(temp10)); - char temp11[strlen(Settings.ex_mqtt_topic) +1]; strncpy(temp11, Settings.ex_mqtt_topic, sizeof(temp11)); - char temp12[strlen(Settings.ex_button_topic) +1]; strncpy(temp12, Settings.ex_button_topic, sizeof(temp12)); - char temp13[strlen(Settings.ex_mqtt_grptopic) +1]; strncpy(temp13, Settings.ex_mqtt_grptopic, sizeof(temp13)); - - memset(Settings.text_pool, 0x00, settings_text_size); - SettingsUpdateText(SET_OTAURL, temp); - SettingsUpdateText(SET_MQTTPREFIX1, temp21); - SettingsUpdateText(SET_MQTTPREFIX2, temp22); - SettingsUpdateText(SET_MQTTPREFIX3, temp23); - SettingsUpdateText(SET_STASSID1, temp31); - SettingsUpdateText(SET_STASSID2, temp32); - SettingsUpdateText(SET_STAPWD1, temp41); - SettingsUpdateText(SET_STAPWD2, temp42); - SettingsUpdateText(SET_HOSTNAME, temp5); - SettingsUpdateText(SET_SYSLOG_HOST, temp6); -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - if (!strlen(Settings.ex_mqtt_user)) { - SettingsUpdateText(SET_MQTT_HOST, temp7); - SettingsUpdateText(SET_MQTT_USER, temp9); - } else { - char aws_mqtt_host[66]; - snprintf_P(aws_mqtt_host, sizeof(aws_mqtt_host), PSTR("%s%s"), temp9, temp7); - SettingsUpdateText(SET_MQTT_HOST, aws_mqtt_host); - SettingsUpdateText(SET_MQTT_USER, ""); - } -#else - SettingsUpdateText(SET_MQTT_HOST, temp7); - SettingsUpdateText(SET_MQTT_USER, temp9); -#endif - SettingsUpdateText(SET_MQTT_CLIENT, temp8); - SettingsUpdateText(SET_MQTT_PWD, temp10); - SettingsUpdateText(SET_MQTT_TOPIC, temp11); - SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, temp12); - SettingsUpdateText(SET_MQTT_GRP_TOPIC, temp13); - - SettingsUpdateText(SET_WEBPWD, Settings.ex_web_password); - SettingsUpdateText(SET_CORS, Settings.ex_cors_domain); - SettingsUpdateText(SET_MQTT_FULLTOPIC, Settings.ex_mqtt_fulltopic); - SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, Settings.ex_switch_topic); - SettingsUpdateText(SET_STATE_TXT1, Settings.ex_state_text[0]); - SettingsUpdateText(SET_STATE_TXT2, Settings.ex_state_text[1]); - SettingsUpdateText(SET_STATE_TXT3, Settings.ex_state_text[2]); - SettingsUpdateText(SET_STATE_TXT4, Settings.ex_state_text[3]); - SettingsUpdateText(SET_NTPSERVER1, Settings.ex_ntp_server[0]); - SettingsUpdateText(SET_NTPSERVER2, Settings.ex_ntp_server[1]); - SettingsUpdateText(SET_NTPSERVER3, Settings.ex_ntp_server[2]); - SettingsUpdateText(SET_MEM1, Settings.script_pram[0]); - SettingsUpdateText(SET_MEM2, Settings.script_pram[1]); - SettingsUpdateText(SET_MEM3, Settings.script_pram[2]); - SettingsUpdateText(SET_MEM4, Settings.script_pram[3]); - SettingsUpdateText(SET_MEM5, Settings.script_pram[4]); - SettingsUpdateText(SET_FRIENDLYNAME1, Settings.ex_friendlyname[0]); - SettingsUpdateText(SET_FRIENDLYNAME2, Settings.ex_friendlyname[1]); - SettingsUpdateText(SET_FRIENDLYNAME3, Settings.ex_friendlyname[2]); - SettingsUpdateText(SET_FRIENDLYNAME4, Settings.ex_friendlyname[3]); - } - if (Settings.version < 0x08020003) { - SettingsUpdateText(SET_TEMPLATE_NAME, Settings.user_template_name); - Settings.zb_channel = 0; - } -#endif - - if (Settings.version < 0x08020004) { -#ifdef ESP8266 - Settings.config_version = 0; -#endif -#ifdef ESP32 - Settings.config_version = 1; -#endif - } - - Settings.version = VERSION; - SettingsSave(1); - } -} -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support.ino" -IPAddress syslog_host_addr; -uint32_t syslog_host_hash = 0; - -extern "C" { -extern struct rst_info resetInfo; -} - - - - - -#include - -Ticker tickerOSWatch; - -const uint32_t OSWATCH_RESET_TIME = 120; - -static unsigned long oswatch_last_loop_time; -uint8_t oswatch_blocked_loop = 0; - -#ifndef USE_WS2812_DMA - -#endif - -#ifdef USE_KNX -bool knx_started = false; -#endif - -void OsWatchTicker(void) -{ - uint32_t t = millis(); - uint32_t last_run = abs(t - oswatch_last_loop_time); - -#ifdef DEBUG_THEO - int32_t rssi = WiFi.RSSI(); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_OSWATCH " FreeRam %d, rssi %d %% (%d dBm), last_run %d"), ESP.getFreeHeap(), WifiGetRssiAsQuality(rssi), rssi, last_run); -#endif - if (last_run >= (OSWATCH_RESET_TIME * 1000)) { - - RtcSettings.oswatch_blocked_loop = 1; - RtcSettingsSave(); - - - - - - volatile uint32_t dummy; - dummy = *((uint32_t*) 0x00000000); - } -} - -void OsWatchInit(void) -{ - oswatch_blocked_loop = RtcSettings.oswatch_blocked_loop; - RtcSettings.oswatch_blocked_loop = 0; - oswatch_last_loop_time = millis(); - tickerOSWatch.attach_ms(((OSWATCH_RESET_TIME / 3) * 1000), OsWatchTicker); -} - -void OsWatchLoop(void) -{ - oswatch_last_loop_time = millis(); - -} - -bool OsWatchBlockedLoop(void) -{ - return oswatch_blocked_loop; -} - -uint32_t ResetReason(void) -{ -# 102 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support.ino" -#ifdef ESP8266 - return resetInfo.reason; -#else - return ESP_ResetInfoReason(); -#endif -} - -String GetResetReason(void) -{ - if (oswatch_blocked_loop) { - char buff[32]; - strncpy_P(buff, PSTR(D_JSON_BLOCKED_LOOP), sizeof(buff)); - return String(buff); - } else { - return ESP_getResetReason(); - } -} - - - - - - -size_t strchrspn(const char *str1, int character) -{ - size_t ret = 0; - char *start = (char*)str1; - char *end = strchr(str1, character); - if (end) ret = end - start; - return ret; -} - - -char* subStr(char* dest, char* str, const char *delim, int index) -{ - char *act; - char *sub = nullptr; - char *ptr; - int i; - - - strncpy(dest, str, strlen(str)+1); - for (i = 1, act = dest; i <= index; i++, act = nullptr) { - sub = strtok_r(act, delim, &ptr); - if (sub == nullptr) break; - } - sub = Trim(sub); - return sub; -} - -float CharToFloat(const char *str) -{ - - char strbuf[24]; - - strlcpy(strbuf, str, sizeof(strbuf)); - char *pt = strbuf; - while ((*pt != '\0') && isblank(*pt)) { pt++; } - - signed char sign = 1; - if (*pt == '-') { sign = -1; } - if (*pt == '-' || *pt=='+') { pt++; } - - float left = 0; - if (*pt != '.') { - left = atoi(pt); - while (isdigit(*pt)) { pt++; } - } - - float right = 0; - if (*pt == '.') { - pt++; - right = atoi(pt); - while (isdigit(*pt)) { - pt++; - right /= 10.0f; - } - } - - float result = left + right; - if (sign < 0) { - return -result; - } - return result; -} - -int TextToInt(char *str) -{ - char *p; - uint8_t radix = 10; - if ('#' == str[0]) { - radix = 16; - str++; - } - return strtol(str, &p, radix); -} - -char* ulltoa(unsigned long long value, char *str, int radix) -{ - char digits[64]; - char *dst = str; - int i = 0; - - - - do { - int n = value % radix; - digits[i++] = (n < 10) ? (char)n+'0' : (char)n-10+'A'; - value /= radix; - } while (value != 0); - - while (i > 0) { *dst++ = digits[--i]; } - - *dst = 0; - return str; -} - - - -char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween) -{ - - - - static const char * hex = "0123456789ABCDEF"; - int between = (inbetween) ? 3 : 2; - const unsigned char * pin = in; - char * pout = out; - for (; pin < in+insz; pout += between, pin++) { - pout[0] = hex[(pgm_read_byte(pin)>>4) & 0xF]; - pout[1] = hex[ pgm_read_byte(pin) & 0xF]; - if (inbetween) { pout[2] = inbetween; } - if (pout + 3 - out > outsz) { break; } - } - pout[(inbetween && insz) ? -1 : 0] = 0; - return out; -} - -char* Uint64toHex(uint64_t value, char *str, uint16_t bits) -{ - ulltoa(value, str, 16); - - int fill = 8; - if ((bits > 3) && (bits < 65)) { - fill = bits / 4; - if (bits % 4) { fill++; } - } - int len = strlen(str); - fill -= len; - if (fill > 0) { - memmove(str + fill, str, len +1); - memset(str, '0', fill); - } - return str; -} - -char* dtostrfd(double number, unsigned char prec, char *s) -{ - if ((isnan(number)) || (isinf(number))) { - strcpy(s, "null"); - return s; - } else { - return dtostrf(number, 1, prec, s); - } -} - -char* Unescape(char* buffer, uint32_t* size) -{ - uint8_t* read = (uint8_t*)buffer; - uint8_t* write = (uint8_t*)buffer; - int32_t start_size = *size; - int32_t end_size = *size; - uint8_t che = 0; - - - - while (start_size > 0) { - uint8_t ch = *read++; - start_size--; - if (ch != '\\') { - *write++ = ch; - } else { - if (start_size > 0) { - uint8_t chi = *read++; - start_size--; - end_size--; - switch (chi) { - case '\\': che = '\\'; break; - case 'a': che = '\a'; break; - case 'b': che = '\b'; break; - case 'e': che = '\e'; break; - case 'f': che = '\f'; break; - case 'n': che = '\n'; break; - case 'r': che = '\r'; break; - case 's': che = ' '; break; - case 't': che = '\t'; break; - case 'v': che = '\v'; break; - case 'x': { - uint8_t* start = read; - che = (uint8_t)strtol((const char*)read, (char**)&read, 16); - start_size -= (uint16_t)(read - start); - end_size -= (uint16_t)(read - start); - break; - } - case '"': che = '\"'; break; - - default : { - che = chi; - *write++ = ch; - end_size++; - } - } - *write++ = che; - } - } - } - *size = end_size; - *write++ = 0; - - - return buffer; -} - -char* RemoveSpace(char* p) -{ - char* write = p; - char* read = p; - char ch = '.'; - - while (ch != '\0') { - ch = *read++; - if (!isspace(ch)) { - *write++ = ch; - } - } - - return p; -} - -char* ReplaceCommaWithDot(char* p) -{ - char* write = (char*)p; - char* read = (char*)p; - char ch = '.'; - - while (ch != '\0') { - ch = *read++; - if (ch == ',') { - ch = '.'; - } - *write++ = ch; - } - return p; -} - -char* LowerCase(char* dest, const char* source) -{ - char* write = dest; - const char* read = source; - char ch = '.'; - - while (ch != '\0') { - ch = *read++; - *write++ = tolower(ch); - } - return dest; -} - -char* UpperCase(char* dest, const char* source) -{ - char* write = dest; - const char* read = source; - char ch = '.'; - - while (ch != '\0') { - ch = *read++; - *write++ = toupper(ch); - } - return dest; -} - -char* UpperCase_P(char* dest, const char* source) -{ - char* write = dest; - const char* read = source; - char ch = '.'; - - while (ch != '\0') { - ch = pgm_read_byte(read++); - *write++ = toupper(ch); - } - return dest; -} - -char* Trim(char* p) -{ - while ((*p != '\0') && isblank(*p)) { p++; } - char* q = p + strlen(p) -1; - while ((q >= p) && isblank(*q)) { q--; } - q++; - *q = '\0'; - return p; -} - -char* RemoveAllSpaces(char* p) -{ - - char *cursor = p; - uint32_t offset = 0; - while (1) { - *cursor = *(cursor + offset); - if ((' ' == *cursor) || ('\t' == *cursor) || ('\n' == *cursor)) { - offset++; - } else { - if (0 == *cursor) { break; } - cursor++; - } - } - return p; -} - -char* NoAlNumToUnderscore(char* dest, const char* source) -{ - char* write = dest; - const char* read = source; - char ch = '.'; - - while (ch != '\0') { - ch = *read++; - *write++ = (isalnum(ch) || ('\0' == ch)) ? ch : '_'; - } - return dest; -} - -char IndexSeparator(void) -{ - - - - - - - if (Settings.flag3.use_underscore) { - return '_'; - } else { - return '-'; - } -} - -void SetShortcutDefault(void) -{ - if ('\0' != XdrvMailbox.data[0]) { - XdrvMailbox.data[0] = '0' + SC_DEFAULT; - XdrvMailbox.data[1] = '\0'; - } -} - -uint8_t Shortcut(void) -{ - uint8_t result = 10; - - if ('\0' == XdrvMailbox.data[1]) { - if (('"' == XdrvMailbox.data[0]) || ('0' == XdrvMailbox.data[0])) { - result = SC_CLEAR; - } else { - result = atoi(XdrvMailbox.data); - if (0 == result) { - result = 10; - } - } - } - return result; -} - -bool ValidIpAddress(const char* str) -{ - const char* p = str; - - while (*p && ((*p == '.') || ((*p >= '0') && (*p <= '9')))) { p++; } - return (*p == '\0'); -} - -bool ParseIp(uint32_t* addr, const char* str) -{ - uint8_t *part = (uint8_t*)addr; - uint8_t i; - - *addr = 0; - for (i = 0; i < 4; i++) { - part[i] = strtoul(str, nullptr, 10); - str = strchr(str, '.'); - if (str == nullptr || *str == '\0') { - break; - } - str++; - } - return (3 == i); -} - -uint32_t ParseParameters(uint32_t count, uint32_t *params) -{ - char *p; - uint32_t i = 0; - for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < count; str = strtok_r(nullptr, ", ", &p), i++) { - params[i] = strtoul(str, nullptr, 0); - } - return i; -} - - -bool NewerVersion(char* version_str) -{ - uint32_t version = 0; - uint32_t i = 0; - char *str_ptr; - - char version_dup[strlen(version_str) +1]; - strncpy(version_dup, version_str, sizeof(version_dup)); - - for (char *str = strtok_r(version_dup, ".", &str_ptr); str && i < sizeof(VERSION); str = strtok_r(nullptr, ".", &str_ptr), i++) { - int field = atoi(str); - - if ((field < 0) || (field > 255)) { - return false; - } - - version = (version << 8) + field; - - if ((2 == i) && isalpha(str[strlen(str)-1])) { - field = str[strlen(str)-1] & 0x1f; - version = (version << 8) + field; - i++; - } - } - - - if ((i < 2) || (i > sizeof(VERSION))) { - return false; - } - - - while (i < sizeof(VERSION)) { - version <<= 8; - i++; - } - - return (version > VERSION); -} - -char* GetPowerDevice(char* dest, uint32_t idx, size_t size, uint32_t option) -{ - strncpy_P(dest, S_RSLT_POWER, size); - if ((devices_present + option) > 1) { - char sidx[8]; - snprintf_P(sidx, sizeof(sidx), PSTR("%d"), idx); - strncat(dest, sidx, size - strlen(dest) -1); - } - return dest; -} - -char* GetPowerDevice(char* dest, uint32_t idx, size_t size) -{ - return GetPowerDevice(dest, idx, size, 0); -} - -void GetEspHardwareType(void) -{ -#ifdef ESP8266 - - uint32_t efuse1 = *(uint32_t*)(0x3FF00050); - uint32_t efuse2 = *(uint32_t*)(0x3FF00054); - - - - is_8285 = ( (efuse1 & (1 << 4)) || (efuse2 & (1 << 16)) ); - if (is_8285 && (ESP.getFlashChipRealSize() > 1048576)) { - is_8285 = false; - } -#else - is_8285 = false; -#endif -} - -String GetDeviceHardware(void) -{ - char buff[10]; -#ifdef ESP8266 - if (is_8285) { - strcpy_P(buff, PSTR("ESP8285")); - } else { - strcpy_P(buff, PSTR("ESP8266EX")); - } -#else - strcpy_P(buff, PSTR("ESP32")); -#endif - return String(buff); -} - -float ConvertTemp(float c) -{ - float result = c; - - global_update = uptime; - global_temperature = c; - - if (!isnan(c) && Settings.flag.temperature_conversion) { - result = c * 1.8 + 32; - } - result = result + (0.1 * Settings.temp_comp); - return result; -} - -float ConvertTempToCelsius(float c) -{ - float result = c; - - if (!isnan(c) && Settings.flag.temperature_conversion) { - result = (c - 32) / 1.8; - } - result = result + (0.1 * Settings.temp_comp); - return result; -} - -char TempUnit(void) -{ - return (Settings.flag.temperature_conversion) ? 'F' : 'C'; -} - -float ConvertHumidity(float h) -{ - float result = h; - - global_update = uptime; - global_humidity = h; - - result = result + (0.1 * Settings.hum_comp); - - return result; -} - -float CalcTempHumToDew(float t, float h) -{ - if (isnan(h) || isnan(t)) { return 0.0; } - - if (Settings.flag.temperature_conversion) { - t = (t - 32) / 1.8; - } - - float gamma = TaylorLog(h / 100) + 17.62 * t / (243.5 + t); - float result = (243.5 * gamma / (17.62 - gamma)); - - if (Settings.flag.temperature_conversion) { - result = result * 1.8 + 32; - } - return result; -} - -float ConvertPressure(float p) -{ - float result = p; - - global_update = uptime; - global_pressure = p; - - if (!isnan(p) && Settings.flag.pressure_conversion) { - result = p * 0.75006375541921; - } - return result; -} - -String PressureUnit(void) -{ - return (Settings.flag.pressure_conversion) ? String(D_UNIT_MILLIMETER_MERCURY) : String(D_UNIT_PRESSURE); -} - -float ConvertSpeed(float s) -{ - - return s * kSpeedConversionFactor[Settings.flag2.speed_conversion]; -} - -String SpeedUnit(void) -{ - char speed[8]; - return String(GetTextIndexed(speed, sizeof(speed), Settings.flag2.speed_conversion, kSpeedUnit)); -} - -void ResetGlobalValues(void) -{ - if ((uptime - global_update) > GLOBAL_VALUES_VALID) { - global_update = 0; - global_temperature = 9999; - global_humidity = 0; - global_pressure = 0; - } -} - -uint32_t SqrtInt(uint32_t num) -{ - if (num <= 1) { - return num; - } - - uint32_t x = num / 2; - uint32_t y; - do { - y = (x + num / x) / 2; - if (y >= x) { - return x; - } - x = y; - } while (true); -} - -uint32_t RoundSqrtInt(uint32_t num) -{ - uint32_t s = SqrtInt(4 * num); - if (s & 1) { - s++; - } - return s / 2; -} - -char* GetTextIndexed(char* destination, size_t destination_size, uint32_t index, const char* haystack) -{ - - - char* write = destination; - const char* read = haystack; - - index++; - while (index--) { - size_t size = destination_size -1; - write = destination; - char ch = '.'; - while ((ch != '\0') && (ch != '|')) { - ch = pgm_read_byte(read++); - if (size && (ch != '|')) { - *write++ = ch; - size--; - } - } - if (0 == ch) { - if (index) { - write = destination; - } - break; - } - } - *write = '\0'; - return destination; -} - -int GetCommandCode(char* destination, size_t destination_size, const char* needle, const char* haystack) -{ - - - int result = -1; - const char* read = haystack; - char* write = destination; - - while (true) { - result++; - size_t size = destination_size -1; - write = destination; - char ch = '.'; - while ((ch != '\0') && (ch != '|')) { - ch = pgm_read_byte(read++); - if (size && (ch != '|')) { - *write++ = ch; - size--; - } - } - *write = '\0'; - if (!strcasecmp(needle, destination)) { - break; - } - if (0 == ch) { - result = -1; - break; - } - } - return result; -} - -bool DecodeCommand(const char* haystack, void (* const MyCommand[])(void)) -{ - GetTextIndexed(XdrvMailbox.command, CMDSZ, 0, haystack); - int prefix_length = strlen(XdrvMailbox.command); - if (prefix_length) { - char prefix[prefix_length +1]; - snprintf_P(prefix, sizeof(prefix), XdrvMailbox.topic); - if (strcasecmp(prefix, XdrvMailbox.command)) { - return false; - } - } - int command_code = GetCommandCode(XdrvMailbox.command + prefix_length, CMDSZ, XdrvMailbox.topic + prefix_length, haystack); - if (command_code > 0) { - XdrvMailbox.command_code = command_code -1; - MyCommand[XdrvMailbox.command_code](); - return true; - } - return false; -} - -const char kOptions[] PROGMEM = "OFF|" D_OFF "|FALSE|" D_FALSE "|STOP|" D_STOP "|" D_CELSIUS "|" - "ON|" D_ON "|TRUE|" D_TRUE "|START|" D_START "|" D_FAHRENHEIT "|" D_USER "|" - "TOGGLE|" D_TOGGLE "|" D_ADMIN "|" - "BLINK|" D_BLINK "|" - "BLINKOFF|" D_BLINKOFF "|" - "ALL" ; - -const uint8_t sNumbers[] PROGMEM = { 0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1, - 2,2,2, - 3,3, - 4,4, - 255 }; - -int GetStateNumber(char *state_text) -{ - char command[CMDSZ]; - int state_number = GetCommandCode(command, sizeof(command), state_text, kOptions); - if (state_number >= 0) { - state_number = pgm_read_byte(sNumbers + state_number); - } - return state_number; -} - -String GetSerialConfig(void) -{ - - - - - - const char kParity[] = "NEOI"; - - char config[4]; - config[0] = '5' + (Settings.serial_config & 0x3); - config[1] = kParity[(Settings.serial_config >> 3) & 0x3]; - config[2] = '1' + ((Settings.serial_config >> 2) & 0x1); - config[3] = '\0'; - return String(config); -} - -void SetSerialBegin() -{ - uint32_t baudrate = Settings.baudrate * 300; - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_SERIAL "Set to %s %d bit/s"), GetSerialConfig().c_str(), baudrate); - Serial.flush(); - Serial.begin(baudrate, (SerialConfig)pgm_read_byte(kTasmotaSerialConfig + Settings.serial_config)); -} - -void SetSerialConfig(uint32_t serial_config) -{ - if (serial_config > TS_SERIAL_8O2) { - serial_config = TS_SERIAL_8N1; - } - if (serial_config != Settings.serial_config) { - Settings.serial_config = serial_config; - SetSerialBegin(); - } -} - -void SetSerialBaudrate(uint32_t baudrate) -{ - Settings.baudrate = baudrate / 300; - if (Serial.baudRate() != baudrate) { - SetSerialBegin(); - } -} - -void SetSerial(uint32_t baudrate, uint32_t serial_config) -{ - Settings.flag.mqtt_serial = 0; - Settings.serial_config = serial_config; - Settings.baudrate = baudrate / 300; - SetSeriallog(LOG_LEVEL_NONE); - SetSerialBegin(); -} - -void ClaimSerial(void) -{ - serial_local = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("SNS: Hardware Serial")); - SetSeriallog(LOG_LEVEL_NONE); - Settings.baudrate = Serial.baudRate() / 300; -} - -void SerialSendRaw(char *codes) -{ - char *p; - char stemp[3]; - uint8_t code; - - int size = strlen(codes); - - while (size > 1) { - strlcpy(stemp, codes, sizeof(stemp)); - code = strtol(stemp, &p, 16); - Serial.write(code); - size -= 2; - codes += 2; - } -} - -uint32_t GetHash(const char *buffer, size_t size) -{ - uint32_t hash = 0; - for (uint32_t i = 0; i <= size; i++) { - hash += (uint8_t)*buffer++ * (i +1); - } - return hash; -} - -void ShowSource(uint32_t source) -{ - if ((source > 0) && (source < SRC_MAX)) { - char stemp1[20]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SRC: %s"), GetTextIndexed(stemp1, sizeof(stemp1), source, kCommandSource)); - } -} - -void WebHexCode(uint32_t i, const char* code) -{ - char scolor[10]; - - strlcpy(scolor, code, sizeof(scolor)); - char* p = scolor; - if ('#' == p[0]) { p++; } - - if (3 == strlen(p)) { - p[6] = p[3]; - p[5] = p[2]; - p[4] = p[2]; - p[3] = p[1]; - p[2] = p[1]; - p[1] = p[0]; - } - - uint32_t color = strtol(p, nullptr, 16); - - - - - - - uint32_t j = sizeof(Settings.web_color) / 3; -# 962 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support.ino" - if (i >= j) { - - i += ((((uint8_t*)&Settings.web_color2 - (uint8_t*)&Settings.web_color) / 3) - j); - } - Settings.web_color[i][0] = (color >> 16) & 0xFF; - Settings.web_color[i][1] = (color >> 8) & 0xFF; - Settings.web_color[i][2] = color & 0xFF; -} - -uint32_t WebColor(uint32_t i) -{ - uint32_t j = sizeof(Settings.web_color) / 3; - - - - - if (i >= j) { - - i += ((((uint8_t*)&Settings.web_color2 - (uint8_t*)&Settings.web_color) / 3) - j); - } - uint32_t tcolor = (Settings.web_color[i][0] << 16) | (Settings.web_color[i][1] << 8) | Settings.web_color[i][2]; - - return tcolor; -} - - - - - -const uint16_t TIMESZ = 100; - -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, ...) -{ - - va_list args; - va_start(args, format); - int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), format, args); - va_end(args); - return len; -} - -int ResponseTime_P(const char* format, ...) -{ - - 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, ...) -{ - - va_list args; - va_start(args, format); - 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 ResponseAppendTimeFormat(uint32_t format) -{ - char time_str[TIMESZ]; - return ResponseAppend_P(ResponseGetTime(format, time_str)); -} - -int ResponseAppendTime(void) -{ - return ResponseAppendTimeFormat(Settings.flag2.time_format); -} - -int ResponseAppendTHD(float f_temperature, float f_humidity) -{ - char temperature[FLOATSZ]; - dtostrfd(f_temperature, Settings.flag2.temperature_resolution, temperature); - char humidity[FLOATSZ]; - dtostrfd(f_humidity, Settings.flag2.humidity_resolution, humidity); - char dewpoint[FLOATSZ]; - dtostrfd(CalcTempHumToDew(f_temperature, f_humidity), Settings.flag2.temperature_resolution, dewpoint); - - return ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s,\"" D_JSON_DEWPOINT "\":%s"), temperature, humidity, dewpoint); -} - -int ResponseJsonEnd(void) -{ - return ResponseAppend_P(PSTR("}")); -} - -int ResponseJsonEndEnd(void) -{ - return ResponseAppend_P(PSTR("}}")); -} - - - - - -void DigitalWrite(uint32_t gpio_pin, uint32_t state) -{ - if (pin[gpio_pin] < 99) { - digitalWrite(pin[gpio_pin], state &1); - } -} - -uint8_t ModuleNr(void) -{ - - - return (USER_MODULE == Settings.module) ? 0 : Settings.module +1; -} - -bool ValidTemplateModule(uint32_t index) -{ - for (uint32_t i = 0; i < sizeof(kModuleNiceList); i++) { - if (index == pgm_read_byte(kModuleNiceList + i)) { - return true; - } - } - return false; -} - -bool ValidModule(uint32_t index) -{ - if (index == USER_MODULE) { return true; } - return ValidTemplateModule(index); -} - -String AnyModuleName(uint32_t index) -{ - if (USER_MODULE == index) { - return String(SettingsText(SET_TEMPLATE_NAME)); - } else { - char name[TOPSZ]; - return String(GetTextIndexed(name, sizeof(name), index, kModuleNames)); - } -} - -String ModuleName(void) -{ - return AnyModuleName(Settings.module); -} - -void ModuleGpios(myio *gp) -{ - uint8_t *dest = (uint8_t *)gp; - memset(dest, GPIO_NONE, sizeof(myio)); - - uint8_t src[sizeof(mycfgio)]; - if (USER_MODULE == Settings.module) { - memcpy(&src, &Settings.user_template.gp, sizeof(mycfgio)); - } else { -#ifdef ESP8266 - memcpy_P(&src, &kModules[Settings.module].gp, sizeof(mycfgio)); -#else - memcpy_P(&src, &kModules.gp, sizeof(mycfgio)); -#endif - } - - - - - uint32_t j = 0; - for (uint32_t i = 0; i < sizeof(mycfgio); i++) { -#ifdef ESP8266 - if (6 == i) { j = 9; } - if (8 == i) { j = 12; } -#else - if (6 == i) { j = 12; } -#endif - dest[j] = src[i]; - j++; - } - - - -} - -gpio_flag ModuleFlag(void) -{ - gpio_flag flag; - -#ifdef ESP8266 - if (USER_MODULE == Settings.module) { - flag = Settings.user_template.flag; - } else { - memcpy_P(&flag, &kModules[Settings.module].flag, sizeof(gpio_flag)); - } -#else - if (USER_MODULE == Settings.module) { - - - - - - memcpy_P(&flag, &Settings.user_template.gp + ADC0_PIN - MIN_FLASH_PINS, sizeof(gpio_flag)); - } else { - memcpy_P(&flag, &kModules.gp + ADC0_PIN - MIN_FLASH_PINS, sizeof(gpio_flag)); - } -#endif - - return flag; -} - -void ModuleDefault(uint32_t module) -{ - if (USER_MODULE == module) { module = WEMOS; } - Settings.user_template_base = module; - char name[TOPSZ]; - SettingsUpdateText(SET_TEMPLATE_NAME, GetTextIndexed(name, sizeof(name), module, kModuleNames)); -#ifdef ESP8266 - memcpy_P(&Settings.user_template, &kModules[module], sizeof(mytmplt)); -#else - memcpy_P(&Settings.user_template, &kModules, sizeof(mytmplt)); -#endif -} - -void SetModuleType(void) -{ - my_module_type = (USER_MODULE == Settings.module) ? Settings.user_template_base : Settings.module; -} - -bool FlashPin(uint32_t pin) -{ -#ifdef ESP8266 - return (((pin > 5) && (pin < 9)) || (11 == pin)); -#endif -#ifdef ESP32 - return ((pin > 5) && (pin < 12)); -#endif -} - -uint8_t ValidPin(uint32_t pin, uint32_t gpio) -{ - if (FlashPin(pin)) { - return GPIO_NONE; - } - -#ifdef ESP8266 - - if ((WEMOS == Settings.module) && !Settings.flag3.user_esp8285_enable) { - if ((pin == 9) || (pin == 10)) { - return GPIO_NONE; - } - } -#endif - - return gpio; -} - -bool ValidGPIO(uint32_t pin, uint32_t gpio) -{ - return (GPIO_USER == ValidPin(pin, gpio)); -} - -bool ValidAdc(void) -{ - gpio_flag flag = ModuleFlag(); - uint32_t template_adc0 = flag.data &15; - return (ADC0_USER == template_adc0); -} - -bool GetUsedInModule(uint32_t val, uint8_t *arr) -{ - int offset = 0; - - if (!val) { return false; } - - if ((val >= GPIO_KEY1) && (val < GPIO_KEY1 + MAX_KEYS)) { - offset = (GPIO_KEY1_NP - GPIO_KEY1); - } - if ((val >= GPIO_KEY1_NP) && (val < GPIO_KEY1_NP + MAX_KEYS)) { - offset = -(GPIO_KEY1_NP - GPIO_KEY1); - } - if ((val >= GPIO_KEY1_INV) && (val < GPIO_KEY1_INV + MAX_KEYS)) { - offset = -(GPIO_KEY1_INV - GPIO_KEY1); - } - if ((val >= GPIO_KEY1_INV_NP) && (val < GPIO_KEY1_INV_NP + MAX_KEYS)) { - offset = -(GPIO_KEY1_INV_NP - GPIO_KEY1); - } - - if ((val >= GPIO_SWT1) && (val < GPIO_SWT1 + MAX_SWITCHES)) { - offset = (GPIO_SWT1_NP - GPIO_SWT1); - } - if ((val >= GPIO_SWT1_NP) && (val < GPIO_SWT1_NP + MAX_SWITCHES)) { - offset = -(GPIO_SWT1_NP - GPIO_SWT1); - } - - if ((val >= GPIO_REL1) && (val < GPIO_REL1 + MAX_RELAYS)) { - offset = (GPIO_REL1_INV - GPIO_REL1); - } - if ((val >= GPIO_REL1_INV) && (val < GPIO_REL1_INV + MAX_RELAYS)) { - offset = -(GPIO_REL1_INV - GPIO_REL1); - } - - if ((val >= GPIO_LED1) && (val < GPIO_LED1 + MAX_LEDS)) { - offset = (GPIO_LED1_INV - GPIO_LED1); - } - if ((val >= GPIO_LED1_INV) && (val < GPIO_LED1_INV + MAX_LEDS)) { - offset = -(GPIO_LED1_INV - GPIO_LED1); - } - - if ((val >= GPIO_PWM1) && (val < GPIO_PWM1 + MAX_PWMS)) { - offset = (GPIO_PWM1_INV - GPIO_PWM1); - } - if ((val >= GPIO_PWM1_INV) && (val < GPIO_PWM1_INV + MAX_PWMS)) { - offset = -(GPIO_PWM1_INV - GPIO_PWM1); - } - - if ((val >= GPIO_CNTR1) && (val < GPIO_CNTR1 + MAX_COUNTERS)) { - offset = (GPIO_CNTR1_NP - GPIO_CNTR1); - } - if ((val >= GPIO_CNTR1_NP) && (val < GPIO_CNTR1_NP + MAX_COUNTERS)) { - offset = -(GPIO_CNTR1_NP - GPIO_CNTR1); - } - - for (uint32_t i = 0; i < MAX_GPIO_PIN; i++) { - if (arr[i] == val) { return true; } - if (arr[i] == val + offset) { return true; } - } - return false; -} - -bool JsonTemplate(const char* dataBuf) -{ - - - if (strlen(dataBuf) < 9) { return false; } - -#ifdef ESP8266 - StaticJsonBuffer<400> jb; -#else - StaticJsonBuffer<800> jb; -#endif - JsonObject& obj = jb.parseObject(dataBuf); - if (!obj.success()) { return false; } - - - const char* name = obj[D_JSON_NAME]; - if (name != nullptr) { - SettingsUpdateText(SET_TEMPLATE_NAME, name); - } - if (obj[D_JSON_GPIO].success()) { - for (uint32_t i = 0; i < sizeof(mycfgio); i++) { - Settings.user_template.gp.io[i] = obj[D_JSON_GPIO][i] | 0; - } - } - if (obj[D_JSON_FLAG].success()) { - uint8_t flag = obj[D_JSON_FLAG] | 0; - memcpy(&Settings.user_template.flag, &flag, sizeof(gpio_flag)); - } - if (obj[D_JSON_BASE].success()) { - uint8_t base = obj[D_JSON_BASE]; - if ((0 == base) || !ValidTemplateModule(base -1)) { base = 18; } - Settings.user_template_base = base -1; - } - return true; -} - -void TemplateJson(void) -{ - Response_P(PSTR("{\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), SettingsText(SET_TEMPLATE_NAME)); - for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) { - ResponseAppend_P(PSTR("%s%d"), (i>0)?",":"", Settings.user_template.gp.io[i]); - } - ResponseAppend_P(PSTR("],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), Settings.user_template.flag, Settings.user_template_base +1); -} - - - - - -inline int32_t TimeDifference(uint32_t prev, uint32_t next) -{ - return ((int32_t) (next - prev)); -} - -int32_t TimePassedSince(uint32_t timestamp) -{ - - - return TimeDifference(timestamp, millis()); -} - -bool TimeReached(uint32_t timer) -{ - - const long passed = TimePassedSince(timer); - return (passed >= 0); -} - -void SetNextTimeInterval(unsigned long& timer, const unsigned long step) -{ - timer += step; - const long passed = TimePassedSince(timer); - if (passed < 0) { return; } - if (static_cast(passed) > step) { - - timer = millis() + step; - return; - } - - timer = millis() + (step - passed); -} - -int32_t TimePassedSinceUsec(uint32_t timestamp) -{ - return TimeDifference(timestamp, micros()); -} - -bool TimeReachedUsec(uint32_t timer) -{ - - const long passed = TimePassedSinceUsec(timer); - return (passed >= 0); -} - - - - - -#ifdef USE_I2C -const uint8_t I2C_RETRY_COUNTER = 3; - -uint32_t i2c_active[4] = { 0 }; -uint32_t i2c_buffer = 0; - -bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size) -{ - uint8_t retry = I2C_RETRY_COUNTER; - bool status = false; - - i2c_buffer = 0; - while (!status && retry) { - Wire.beginTransmission(addr); - Wire.write(reg); - if (0 == Wire.endTransmission(false)) { - Wire.requestFrom((int)addr, (int)size); - if (Wire.available() == size) { - for (uint32_t i = 0; i < size; i++) { - i2c_buffer = i2c_buffer << 8 | Wire.read(); - } - status = true; - } - } - retry--; - } - return status; -} - -bool I2cValidRead8(uint8_t *data, uint8_t addr, uint8_t reg) -{ - bool status = I2cValidRead(addr, reg, 1); - *data = (uint8_t)i2c_buffer; - return status; -} - -bool I2cValidRead16(uint16_t *data, uint8_t addr, uint8_t reg) -{ - bool status = I2cValidRead(addr, reg, 2); - *data = (uint16_t)i2c_buffer; - return status; -} - -bool I2cValidReadS16(int16_t *data, uint8_t addr, uint8_t reg) -{ - bool status = I2cValidRead(addr, reg, 2); - *data = (int16_t)i2c_buffer; - return status; -} - -bool I2cValidRead16LE(uint16_t *data, uint8_t addr, uint8_t reg) -{ - uint16_t ldata; - bool status = I2cValidRead16(&ldata, addr, reg); - *data = (ldata >> 8) | (ldata << 8); - return status; -} - -bool I2cValidReadS16_LE(int16_t *data, uint8_t addr, uint8_t reg) -{ - uint16_t ldata; - bool status = I2cValidRead16LE(&ldata, addr, reg); - *data = (int16_t)ldata; - return status; -} - -bool I2cValidRead24(int32_t *data, uint8_t addr, uint8_t reg) -{ - bool status = I2cValidRead(addr, reg, 3); - *data = i2c_buffer; - return status; -} - -uint8_t I2cRead8(uint8_t addr, uint8_t reg) -{ - I2cValidRead(addr, reg, 1); - return (uint8_t)i2c_buffer; -} - -uint16_t I2cRead16(uint8_t addr, uint8_t reg) -{ - I2cValidRead(addr, reg, 2); - return (uint16_t)i2c_buffer; -} - -int16_t I2cReadS16(uint8_t addr, uint8_t reg) -{ - I2cValidRead(addr, reg, 2); - return (int16_t)i2c_buffer; -} - -uint16_t I2cRead16LE(uint8_t addr, uint8_t reg) -{ - I2cValidRead(addr, reg, 2); - uint16_t temp = (uint16_t)i2c_buffer; - return (temp >> 8) | (temp << 8); -} - -int16_t I2cReadS16_LE(uint8_t addr, uint8_t reg) -{ - return (int16_t)I2cRead16LE(addr, reg); -} - -int32_t I2cRead24(uint8_t addr, uint8_t reg) -{ - I2cValidRead(addr, reg, 3); - return i2c_buffer; -} - -bool I2cWrite(uint8_t addr, uint8_t reg, uint32_t val, uint8_t size) -{ - uint8_t x = I2C_RETRY_COUNTER; - - do { - Wire.beginTransmission((uint8_t)addr); - Wire.write(reg); - uint8_t bytes = size; - while (bytes--) { - Wire.write((val >> (8 * bytes)) & 0xFF); - } - x--; - } while (Wire.endTransmission(true) != 0 && x != 0); - return (x); -} - -bool I2cWrite8(uint8_t addr, uint8_t reg, uint16_t val) -{ - return I2cWrite(addr, reg, val, 1); -} - -bool I2cWrite16(uint8_t addr, uint8_t reg, uint16_t val) -{ - return I2cWrite(addr, reg, val, 2); -} - -int8_t I2cReadBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len) -{ - Wire.beginTransmission((uint8_t)addr); - Wire.write((uint8_t)reg); - Wire.endTransmission(); - if (len != Wire.requestFrom((uint8_t)addr, (uint8_t)len)) { - return 1; - } - while (len--) { - *reg_data = (uint8_t)Wire.read(); - reg_data++; - } - return 0; -} - -int8_t I2cWriteBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len) -{ - Wire.beginTransmission((uint8_t)addr); - Wire.write((uint8_t)reg); - while (len--) { - Wire.write(*reg_data); - reg_data++; - } - Wire.endTransmission(); - return 0; -} - -void I2cScan(char *devs, unsigned int devs_len) -{ - - - - - - - - uint8_t error = 0; - uint8_t address = 0; - uint8_t any = 0; - - snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"" D_JSON_I2CSCAN_DEVICES_FOUND_AT)); - for (address = 1; address <= 127; address++) { - Wire.beginTransmission(address); - error = Wire.endTransmission(); - if (0 == error) { - any = 1; - snprintf_P(devs, devs_len, PSTR("%s 0x%02x"), devs, address); - } - else if (error != 2) { - any = 2; - snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"Error %d at 0x%02x"), error, address); - break; - } - } - if (any) { - strncat(devs, "\"}", devs_len - strlen(devs) -1); - } - else { - snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"" D_JSON_I2CSCAN_NO_DEVICES_FOUND "\"}")); - } -} - -void I2cResetActive(uint32_t addr, uint32_t count = 1) -{ - addr &= 0x7F; - count &= 0x7F; - while (count-- && (addr < 128)) { - i2c_active[addr / 32] &= ~(1 << (addr % 32)); - addr++; - } - -} - -void I2cSetActive(uint32_t addr, uint32_t count = 1) -{ - addr &= 0x7F; - count &= 0x7F; - while (count-- && (addr < 128)) { - i2c_active[addr / 32] |= (1 << (addr % 32)); - addr++; - } - -} - -void I2cSetActiveFound(uint32_t addr, const char *types) -{ - I2cSetActive(addr); - AddLog_P2(LOG_LEVEL_INFO, S_LOG_I2C_FOUND_AT, types, addr); -} - -bool I2cActive(uint32_t addr) -{ - addr &= 0x7F; - if (i2c_active[addr / 32] & (1 << (addr % 32))) { - return true; - } - return false; -} - -bool I2cSetDevice(uint32_t addr) -{ - addr &= 0x7F; - if (I2cActive(addr)) { - return false; - } - Wire.beginTransmission((uint8_t)addr); - return (0 == Wire.endTransmission()); -} -#endif -# 1656 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support.ino" -void SetSeriallog(uint32_t loglevel) -{ - Settings.seriallog_level = loglevel; - seriallog_level = loglevel; - seriallog_timer = 0; -} - -void SetSyslog(uint32_t loglevel) -{ - Settings.syslog_level = loglevel; - syslog_level = loglevel; - syslog_timer = 0; -} - -#ifdef USE_WEBSERVER -void GetLog(uint32_t idx, char** entry_pp, size_t* len_p) -{ - char* entry_p = nullptr; - size_t len = 0; - - if (idx) { - char* it = web_log; - do { - uint32_t cur_idx = *it; - it++; - size_t tmp = strchrspn(it, '\1'); - tmp++; - if (cur_idx == idx) { - len = tmp; - entry_p = it; - break; - } - it += tmp; - } while (it < web_log + WEB_LOG_SIZE && *it != '\0'); - } - *entry_pp = entry_p; - *len_p = len; -} -#endif - -void Syslog(void) -{ - - - uint32_t current_hash = GetHash(SettingsText(SET_SYSLOG_HOST), strlen(SettingsText(SET_SYSLOG_HOST))); - if (syslog_host_hash != current_hash) { - syslog_host_hash = current_hash; - WiFi.hostByName(SettingsText(SET_SYSLOG_HOST), syslog_host_addr); - } - if (PortUdp.beginPacket(syslog_host_addr, Settings.syslog_port)) { - char syslog_preamble[64]; - snprintf_P(syslog_preamble, sizeof(syslog_preamble), PSTR("%s ESP-"), my_hostname); - memmove(log_data + strlen(syslog_preamble), log_data, sizeof(log_data) - strlen(syslog_preamble)); - log_data[sizeof(log_data) -1] = '\0'; - memcpy(log_data, syslog_preamble, strlen(syslog_preamble)); - PortUdp_write(log_data, strlen(log_data)); - PortUdp.endPacket(); - delay(1); - } else { - syslog_level = 0; - syslog_timer = SYSLOG_TIMER; - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SYSLOG_HOST_NOT_FOUND ". " D_RETRY_IN " %d " D_UNIT_SECOND), SYSLOG_TIMER); - } -} - -void AddLog(uint32_t loglevel) -{ - char mxtime[10]; - - snprintf_P(mxtime, sizeof(mxtime), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d "), RtcTime.hour, RtcTime.minute, RtcTime.second); - - if (loglevel <= seriallog_level) { - Serial.printf("%s%s\r\n", mxtime, log_data); - } -#ifdef USE_WEBSERVER - if (Settings.webserver && (loglevel <= Settings.weblog_level)) { - - - web_log_index &= 0xFF; - if (!web_log_index) web_log_index++; - while (web_log_index == web_log[0] || - strlen(web_log) + strlen(log_data) + 13 > WEB_LOG_SIZE) - { - char* it = web_log; - it++; - it += strchrspn(it, '\1'); - it++; - memmove(web_log, it, WEB_LOG_SIZE -(it-web_log)); - } - snprintf_P(web_log, sizeof(web_log), PSTR("%s%c%s%s\1"), web_log, web_log_index++, mxtime, log_data); - web_log_index &= 0xFF; - if (!web_log_index) web_log_index++; - } -#endif - if (Settings.flag.mqtt_enabled && - !global_state.mqtt_down && - (loglevel <= Settings.mqttlog_level)) { MqttPublishLogging(mxtime); } - - if (!global_state.wifi_down && - (loglevel <= syslog_level)) { Syslog(); } - - prepped_loglevel = 0; -} - -void AddLog_P(uint32_t loglevel, const char *formatP) -{ - snprintf_P(log_data, sizeof(log_data), formatP); - AddLog(loglevel); -} - -void AddLog_P(uint32_t loglevel, const char *formatP, const char *formatP2) -{ - char message[sizeof(log_data)]; - - snprintf_P(log_data, sizeof(log_data), formatP); - snprintf_P(message, sizeof(message), formatP2); - strncat(log_data, message, sizeof(log_data) - strlen(log_data) -1); - AddLog(loglevel); -} - -void PrepLog_P2(uint32_t loglevel, PGM_P formatP, ...) -{ - va_list arg; - va_start(arg, formatP); - vsnprintf_P(log_data, sizeof(log_data), formatP, arg); - va_end(arg); - - prepped_loglevel = loglevel; -} - -void AddLog_P2(uint32_t loglevel, PGM_P formatP, ...) -{ - va_list arg; - va_start(arg, formatP); - vsnprintf_P(log_data, sizeof(log_data), formatP, arg); - va_end(arg); - - AddLog(loglevel); -} - -void AddLog_Debug(PGM_P formatP, ...) -{ - va_list arg; - va_start(arg, formatP); - vsnprintf_P(log_data, sizeof(log_data), formatP, arg); - va_end(arg); - - AddLog(LOG_LEVEL_DEBUG); -} - -void AddLogBuffer(uint32_t loglevel, uint8_t *buffer, uint32_t count) -{ -# 1820 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support.ino" - char hex_char[(count * 3) + 2]; - AddLog_P2(loglevel, PSTR("DMP: %s"), ToHex_P(buffer, count, hex_char, sizeof(hex_char), ' ')); -} - -void AddLogSerial(uint32_t loglevel) -{ - AddLogBuffer(loglevel, (uint8_t*)serial_in_buffer, serial_in_byte_counter); -} - -void AddLogMissed(const char *sensor, uint32_t misses) -{ - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SNS: %s missed %d"), sensor, SENSOR_MAX_MISS - misses); -} -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_button.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_button.ino" -#define BUTTON_V1 -#ifdef BUTTON_V1 - - - - -#define MAX_BUTTON_COMMANDS 5 - -const char kCommands[] PROGMEM = - D_CMND_WIFICONFIG " 2|" D_CMND_WIFICONFIG " 2|" D_CMND_WIFICONFIG " 2|" D_CMND_RESTART " 1|" D_CMND_UPGRADE " 1"; - -struct BUTTON { - unsigned long debounce = 0; - uint16_t hold_timer[MAX_KEYS] = { 0 }; - uint16_t dual_code = 0; - - uint8_t last_state[MAX_KEYS] = { NOT_PRESSED, NOT_PRESSED, NOT_PRESSED, NOT_PRESSED }; - uint8_t window_timer[MAX_KEYS] = { 0 }; - uint8_t press_counter[MAX_KEYS] = { 0 }; - - uint8_t dual_receive_count = 0; - uint8_t no_pullup_mask = 0; - uint8_t inverted_mask = 0; - uint8_t present = 0; - uint8_t adc = 99; -} Button; - - - -void ButtonPullupFlag(uint8 button_bit) -{ - bitSet(Button.no_pullup_mask, button_bit); -} - -void ButtonInvertFlag(uint8 button_bit) -{ - bitSet(Button.inverted_mask, button_bit); -} - -void ButtonInit(void) -{ - Button.present = 0; - for (uint32_t i = 0; i < MAX_KEYS; i++) { - if (pin[GPIO_KEY1 +i] < 99) { - Button.present++; - pinMode(pin[GPIO_KEY1 +i], bitRead(Button.no_pullup_mask, i) ? INPUT : ((16 == pin[GPIO_KEY1 +i]) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); - } -#ifndef USE_ADC_VCC - else if ((99 == Button.adc) && ((ADC0_BUTTON == my_adc0) || (ADC0_BUTTON_INV == my_adc0))) { - Button.present++; - Button.adc = i; - } -#endif - } -} - -uint8_t ButtonSerial(uint8_t serial_in_byte) -{ - if (Button.dual_receive_count) { - Button.dual_receive_count--; - if (Button.dual_receive_count) { - Button.dual_code = (Button.dual_code << 8) | serial_in_byte; - serial_in_byte = 0; - } else { - if (serial_in_byte != 0xA1) { - Button.dual_code = 0; - } - } - } - if (0xA0 == serial_in_byte) { - serial_in_byte = 0; - Button.dual_code = 0; - Button.dual_receive_count = 3; - } - - return serial_in_byte; -} -# 109 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_button.ino" -void ButtonHandler(void) -{ - if (uptime < 4) { return; } - - uint8_t hold_time_extent = IMMINENT_RESET_FACTOR; - uint16_t loops_per_second = 1000 / Settings.button_debounce; - char scmnd[20]; - - - - for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) { - uint8_t button = NOT_PRESSED; - uint8_t button_present = 0; - -#ifdef ESP8266 - if (!button_index && ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type))) { - button_present = 1; - if (Button.dual_code) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON " " D_CODE " %04X"), Button.dual_code); - button = PRESSED; - if (0xF500 == Button.dual_code) { - Button.hold_timer[button_index] = (loops_per_second * Settings.param[P_HOLD_TIME] / 10) -1; - hold_time_extent = 1; - } - Button.dual_code = 0; - } - } - else -#endif - if (pin[GPIO_KEY1 +button_index] < 99) { - button_present = 1; - button = (digitalRead(pin[GPIO_KEY1 +button_index]) != bitRead(Button.inverted_mask, button_index)); - } -#ifndef USE_ADC_VCC - if (Button.adc == button_index) { - button_present = 1; - if (ADC0_BUTTON_INV == my_adc0) { - button = (AdcRead(1) < 128); - } - else if (ADC0_BUTTON == my_adc0) { - button = (AdcRead(1) > 128); - } - } -#endif - - if (button_present) { - XdrvMailbox.index = button_index; - XdrvMailbox.payload = button; - if (XdrvCall(FUNC_BUTTON_PRESSED)) { - - } -#ifdef ESP8266 - else if (SONOFF_4CHPRO == my_module_type) { - if (Button.hold_timer[button_index]) { Button.hold_timer[button_index]--; } - - bool button_pressed = false; - if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_10), button_index +1); - Button.hold_timer[button_index] = loops_per_second; - button_pressed = true; - } - if ((NOT_PRESSED == button) && (PRESSED == Button.last_state[button_index])) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_01), button_index +1); - if (!Button.hold_timer[button_index]) { button_pressed = true; } - } - if (button_pressed) { - if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { - ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); - } - } - } -#endif - else { - if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { - if (Settings.flag.button_single) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_IMMEDIATE), button_index +1); - if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { - ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); - } - } else { - Button.press_counter[button_index] = (Button.window_timer[button_index]) ? Button.press_counter[button_index] +1 : 1; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_MULTI_PRESS " %d"), button_index +1, Button.press_counter[button_index]); - Button.window_timer[button_index] = loops_per_second / 2; - } - blinks = 201; - } - - if (NOT_PRESSED == button) { - Button.hold_timer[button_index] = 0; - } else { - Button.hold_timer[button_index]++; - if (Settings.flag.button_single) { - if (Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { - - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_SETOPTION "13 0")); - ExecuteCommand(scmnd, SRC_BUTTON); - } - } else { - if (Settings.flag.button_restrict) { - if (Settings.param[P_HOLD_IGNORE] > 0) { - if (Button.hold_timer[button_index] > loops_per_second * Settings.param[P_HOLD_IGNORE] / 10) { - Button.hold_timer[button_index] = 0; - Button.press_counter[button_index] = 0; - 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) { - Button.press_counter[button_index] = 0; - SendKey(KEY_BUTTON, button_index +1, POWER_HOLD); - } - } else { - if (Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { - Button.press_counter[button_index] = 0; - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); - ExecuteCommand(scmnd, SRC_BUTTON); - } - } - } - } - - if (!Settings.flag.button_single) { - if (Button.window_timer[button_index]) { - Button.window_timer[button_index]--; - } else { - if (!restart_flag && !Button.hold_timer[button_index] && (Button.press_counter[button_index] > 0) && (Button.press_counter[button_index] < MAX_BUTTON_COMMANDS +3)) { - bool single_press = false; - if (Button.press_counter[button_index] < 3) { -#ifdef ESP8266 - if ((SONOFF_DUAL_R2 == my_module_type) || (SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { - single_press = true; - } else -#endif - { - single_press = (Settings.flag.button_swap +1 == Button.press_counter[button_index]); - if ((1 == Button.present) && (2 == devices_present)) { - if (Settings.flag.button_swap) { - Button.press_counter[button_index] = (single_press) ? 1 : 2; - } - } else { - Button.press_counter[button_index] = 1; - } - } - } -#if defined(USE_LIGHT) && defined(ROTARY_V1) - if (!((0 == button_index) && RotaryButtonPressed())) { -#endif - if (single_press && SendKey(KEY_BUTTON, button_index + Button.press_counter[button_index], POWER_TOGGLE)) { - - } else { - if (Button.press_counter[button_index] < 3) { - if (WifiState() > WIFI_RESTART) { - restart_flag = 1; - } else { - ExecuteCommandPower(button_index + Button.press_counter[button_index], POWER_TOGGLE, SRC_BUTTON); - } - } else { - if (!Settings.flag.button_restrict) { - GetTextIndexed(scmnd, sizeof(scmnd), Button.press_counter[button_index] -3, kCommands); - ExecuteCommand(scmnd, SRC_BUTTON); - } - } - } -#if defined(USE_LIGHT) && defined(ROTARY_V1) - } -#endif - Button.press_counter[button_index] = 0; - } - } - } - } - } - Button.last_state[button_index] = button; - } -} - -void ButtonLoop(void) -{ - if (Button.present) { - if (TimeReached(Button.debounce)) { - SetNextTimeInterval(Button.debounce, Settings.button_debounce); - ButtonHandler(); - } - } -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_button_v2.ino" -# 21 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_button_v2.ino" -#ifdef BUTTON_V2 - - - - -#define MAX_BUTTON_COMMANDS_V2 3 -#define MAX_RELAY_BUTTON1 4 - -const char kCommands[] PROGMEM = - D_CMND_WIFICONFIG " 2|" D_CMND_RESTART " 1|" D_CMND_UPGRADE " 1"; - -const char kMultiPress[] PROGMEM = - "|SINGLE|DOUBLE|TRIPLE|QUAD|PENTA|"; - -struct BUTTON { - unsigned long debounce = 0; - uint16_t hold_timer[MAX_KEYS] = { 0 }; - uint16_t dual_code = 0; - - uint8_t last_state[MAX_KEYS] = { NOT_PRESSED, NOT_PRESSED, NOT_PRESSED, NOT_PRESSED }; - uint8_t window_timer[MAX_KEYS] = { 0 }; - uint8_t press_counter[MAX_KEYS] = { 0 }; - - uint8_t dual_receive_count = 0; - uint8_t no_pullup_mask = 0; - uint8_t inverted_mask = 0; - uint8_t present = 0; - uint8_t adc = 99; -} Button; - - - -void ButtonPullupFlag(uint8 button_bit) -{ - bitSet(Button.no_pullup_mask, button_bit); -} - -void ButtonInvertFlag(uint8 button_bit) -{ - bitSet(Button.inverted_mask, button_bit); -} - -void ButtonInit(void) -{ - Button.present = 0; - for (uint32_t i = 0; i < MAX_KEYS; i++) { - if (pin[GPIO_KEY1 +i] < 99) { - Button.present++; - pinMode(pin[GPIO_KEY1 +i], bitRead(Button.no_pullup_mask, i) ? INPUT : ((16 == pin[GPIO_KEY1 +i]) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); - } -#ifndef USE_ADC_VCC - else if ((99 == Button.adc) && ((ADC0_BUTTON == my_adc0) || (ADC0_BUTTON_INV == my_adc0))) { - Button.present++; - Button.adc = i; - } -#endif - } -} - -uint8_t ButtonSerial(uint8_t serial_in_byte) -{ - if (Button.dual_receive_count) { - Button.dual_receive_count--; - if (Button.dual_receive_count) { - Button.dual_code = (Button.dual_code << 8) | serial_in_byte; - serial_in_byte = 0; - } else { - if (serial_in_byte != 0xA1) { - Button.dual_code = 0; - } - } - } - if (0xA0 == serial_in_byte) { - serial_in_byte = 0; - Button.dual_code = 0; - Button.dual_receive_count = 3; - } - - return serial_in_byte; -} -# 114 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_button_v2.ino" -void ButtonHandler(void) -{ - if (uptime < 4) { return; } - - uint8_t hold_time_extent = IMMINENT_RESET_FACTOR; - uint16_t loops_per_second = 1000 / Settings.button_debounce; - char scmnd[20]; - - - - for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) { - uint8_t button = NOT_PRESSED; - uint8_t button_present = 0; - -#ifdef ESP8266 - if (!button_index && ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type))) { - button_present = 1; - if (Button.dual_code) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON " " D_CODE " %04X"), Button.dual_code); - button = PRESSED; - if (0xF500 == Button.dual_code) { - Button.hold_timer[button_index] = (loops_per_second * Settings.param[P_HOLD_TIME] / 10) -1; - hold_time_extent = 1; - } - Button.dual_code = 0; - } - } - else -#endif - if (pin[GPIO_KEY1 +button_index] < 99) { - button_present = 1; - button = (digitalRead(pin[GPIO_KEY1 +button_index]) != bitRead(Button.inverted_mask, button_index)); - } -#ifndef USE_ADC_VCC - if (Button.adc == button_index) { - button_present = 1; - if (ADC0_BUTTON_INV == my_adc0) { - button = (AdcRead(1) < 128); - } - else if (ADC0_BUTTON == my_adc0) { - button = (AdcRead(1) > 128); - } - } -#endif - - if (button_present) { - XdrvMailbox.index = button_index; - XdrvMailbox.payload = button; - if (XdrvCall(FUNC_BUTTON_PRESSED)) { - - } -#ifdef ESP8266 - else if (SONOFF_4CHPRO == my_module_type) { - if (Button.hold_timer[button_index]) { Button.hold_timer[button_index]--; } - - bool button_pressed = false; - if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_10), button_index +1); - Button.hold_timer[button_index] = loops_per_second; - button_pressed = true; - } - if ((NOT_PRESSED == button) && (PRESSED == Button.last_state[button_index])) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_01), button_index +1); - if (!Button.hold_timer[button_index]) { button_pressed = true; } - } - if (button_pressed) { - if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { - ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); - - } - } - } -#endif - else { - - if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { - - if (Settings.flag.button_single) { - if (!Settings.flag3.mqtt_buttons) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_IMMEDIATE), button_index +1); - if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { - ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); - } - } else { - MqttButtonTopic(button_index +1, 1, 0); - } - } else { - Button.press_counter[button_index] = (Button.window_timer[button_index]) ? Button.press_counter[button_index] +1 : 1; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_MULTI_PRESS " %d"), button_index +1, Button.press_counter[button_index]); - Button.window_timer[button_index] = loops_per_second / 2; - } - blinks = 201; - } - - if (NOT_PRESSED == button) { - Button.hold_timer[button_index] = 0; - } else { - Button.hold_timer[button_index]++; - if (Settings.flag.button_single) { - if (Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_SETOPTION "13 0")); - ExecuteCommand(scmnd, SRC_BUTTON); - } - } else { - if (Button.hold_timer[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10) { - Button.press_counter[button_index] = 0; - if (Settings.flag3.mqtt_buttons) { - MqttButtonTopic(button_index +1, 3, 1); - } else { - SendKey(KEY_BUTTON, button_index +1, POWER_HOLD); - } - } else { - if (Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { - Button.press_counter[button_index] = 0; - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); - ExecuteCommand(scmnd, SRC_BUTTON); - } - } - } - } - - if (!Settings.flag.button_single) { - if (Button.window_timer[button_index]) { - Button.window_timer[button_index]--; - } else { - if (!restart_flag && !Button.hold_timer[button_index] && (Button.press_counter[button_index] > 0) && (Button.press_counter[button_index] < MAX_BUTTON_COMMANDS_V2 +6)) { - bool single_press = false; - if (Button.press_counter[button_index] < 3) { -#ifdef ESP8266 - if ((SONOFF_DUAL_R2 == my_module_type) || (SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { - single_press = true; - } else -#endif - { - single_press = (Settings.flag.button_swap +1 == Button.press_counter[button_index]); - if ((1 == Button.present) && (2 == devices_present)) { - if (Settings.flag.button_swap) { - Button.press_counter[button_index] = (single_press) ? 1 : 2; - } - - - - - } - } - } -#if defined(USE_LIGHT) && defined(ROTARY_V1) - if (!((0 == button_index) && RotaryButtonPressed())) { -#endif - if (!Settings.flag3.mqtt_buttons && single_press && SendKey(KEY_BUTTON, button_index + Button.press_counter[button_index], POWER_TOGGLE)) { - - } else { - if (Button.press_counter[button_index] < 6) { - if (WifiState() > WIFI_RESTART) { - restart_flag = 1; - } - if (!Settings.flag3.mqtt_buttons) { - if (Button.press_counter[button_index] == 1) { - ExecuteCommandPower(button_index + Button.press_counter[button_index], POWER_TOGGLE, SRC_BUTTON); - } else { - SendKey(KEY_BUTTON, button_index +1, Button.press_counter[button_index] +9); - if (0 == button_index) { - if ((Button.press_counter[button_index] > 1 && pin[GPIO_REL1 + Button.press_counter[button_index]-1] < 99) && Button.press_counter[button_index] <= MAX_RELAY_BUTTON1) { - ExecuteCommandPower(button_index + Button.press_counter[button_index], POWER_TOGGLE, SRC_BUTTON); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: Relay%d found on GPIO%d"), Button.press_counter[button_index], pin[GPIO_REL1 + Button.press_counter[button_index]-1]); - } - } - } - } - } else { - GetTextIndexed(scmnd, sizeof(scmnd), Button.press_counter[button_index] -6, kCommands); - ExecuteCommand(scmnd, SRC_BUTTON); - } - if (Settings.flag3.mqtt_buttons) { - if (Button.press_counter[button_index] >= 1 && Button.press_counter[button_index] <= 5) { - MqttButtonTopic(button_index +1, Button.press_counter[button_index], 0); - } - } - } -#if defined(USE_LIGHT) && defined(ROTARY_V1) - } -#endif - Button.press_counter[button_index] = 0; - } - } - } - - } - } - Button.last_state[button_index] = button; - } -} - -void MqttButtonTopic(uint8_t button_id, uint8_t action, uint8_t hold) -{ - char scommand[CMDSZ]; - char stopic[TOPSZ]; - char mqttstate[7]; - - GetTextIndexed(mqttstate, sizeof(mqttstate), action, kMultiPress); - - SendKey(KEY_BUTTON, button_id, (hold) ? 3 : action +9); - snprintf_P(scommand, sizeof(scommand), PSTR("BUTTON%d"), button_id); - GetTopic_P(stopic, STAT, mqtt_topic, scommand); - Response_P(S_JSON_COMMAND_SVALUE, "ACTION", (hold) ? SettingsText(SET_STATE_TXT4) : mqttstate); - MqttPublish(stopic); -} - -void ButtonLoop(void) -{ - if (Button.present) { - if (TimeReached(Button.debounce)) { - SetNextTimeInterval(Button.debounce, Settings.button_debounce); - ButtonHandler(); - } - } -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_command.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_command.ino" -const char kTasmotaCommands[] PROGMEM = "|" - D_CMND_BACKLOG "|" D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_STATUS "|" D_CMND_STATE "|" D_CMND_SLEEP "|" D_CMND_UPGRADE "|" D_CMND_UPLOAD "|" D_CMND_OTAURL "|" - D_CMND_SERIALLOG "|" D_CMND_RESTART "|" D_CMND_POWERONSTATE "|" D_CMND_PULSETIME "|" D_CMND_BLINKTIME "|" D_CMND_BLINKCOUNT "|" D_CMND_SAVEDATA "|" - D_CMND_SETOPTION "|" D_CMND_TEMPERATURE_RESOLUTION "|" D_CMND_HUMIDITY_RESOLUTION "|" D_CMND_PRESSURE_RESOLUTION "|" D_CMND_POWER_RESOLUTION "|" - D_CMND_VOLTAGE_RESOLUTION "|" D_CMND_FREQUENCY_RESOLUTION "|" D_CMND_CURRENT_RESOLUTION "|" D_CMND_ENERGY_RESOLUTION "|" D_CMND_WEIGHT_RESOLUTION "|" - D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_GPIO "|" D_CMND_GPIOS "|" D_CMND_TEMPLATE "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|" - D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALCONFIG "|" - D_CMND_SERIALDELIMITER "|" D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|" - D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TELEPERIOD "|" D_CMND_RESET "|" D_CMND_TIME "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" - D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" D_CMND_HUMOFFSET "|" - D_CMND_SPEEDUNIT "|" D_CMND_GLOBAL_TEMP "|" D_CMND_GLOBAL_HUM "|" -#ifdef USE_I2C - D_CMND_I2CSCAN "|" D_CMND_I2CDRIVER "|" -#endif -#ifdef USE_DEVICE_GROUPS - D_CMND_DEVGROUP_NAME "|" -#ifdef USE_DEVICE_GROUPS_SEND - D_CMND_DEVGROUP_SEND "|" -#endif - D_CMND_DEVGROUP_SHARE "|" D_CMND_DEVGROUPSTATUS "|" -#endif - D_CMND_SENSOR "|" D_CMND_DRIVER; - -void (* const TasmotaCommand[])(void) PROGMEM = { - &CmndBacklog, &CmndDelay, &CmndPower, &CmndStatus, &CmndState, &CmndSleep, &CmndUpgrade, &CmndUpgrade, &CmndOtaUrl, - &CmndSeriallog, &CmndRestart, &CmndPowerOnState, &CmndPulsetime, &CmndBlinktime, &CmndBlinkcount, &CmndSavedata, - &CmndSetoption, &CmndTemperatureResolution, &CmndHumidityResolution, &CmndPressureResolution, &CmndPowerResolution, - &CmndVoltageResolution, &CmndFrequencyResolution, &CmndCurrentResolution, &CmndEnergyResolution, &CmndWeightResolution, - &CmndModule, &CmndModules, &CmndGpio, &CmndGpios, &CmndTemplate, &CmndPwm, &CmndPwmfrequency, &CmndPwmrange, - &CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport, &CmndSerialSend, &CmndBaudrate, &CmndSerialConfig, - &CmndSerialDelimiter, &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig, - &CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd, - &CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndWifiPower, &CmndTempOffset, &CmndHumOffset, - &CmndSpeedUnit, &CmndGlobalTemp, &CmndGlobalHum, -#ifdef USE_I2C - &CmndI2cScan, CmndI2cDriver, -#endif -#ifdef USE_DEVICE_GROUPS - &CmndDevGroupName, -#ifdef USE_DEVICE_GROUPS_SEND - &CmndDevGroupSend, -#endif - &CmndDevGroupShare, &CmndDevGroupStatus, -#endif - &CmndSensor, &CmndDriver }; - -const char kWifiConfig[] PROGMEM = - D_WCFG_0_RESTART "||" D_WCFG_2_WIFIMANAGER "||" D_WCFG_4_RETRY "|" D_WCFG_5_WAIT "|" D_WCFG_6_SERIAL "|" D_WCFG_7_WIFIMANAGER_RESET_ONLY; - - - -void ResponseCmndNumber(int value) -{ - Response_P(S_JSON_COMMAND_NVALUE, XdrvMailbox.command, value); -} - -void ResponseCmndFloat(float value, uint32_t decimals) -{ - char stemp1[TOPSZ]; - dtostrfd(value, decimals, stemp1); - Response_P(S_JSON_COMMAND_XVALUE, XdrvMailbox.command, stemp1); -} - -void ResponseCmndIdxNumber(int value) -{ - Response_P(S_JSON_COMMAND_INDEX_NVALUE, XdrvMailbox.command, XdrvMailbox.index, value); -} - -void ResponseCmndChar_P(const char* value) -{ - size_t buf_size = strlen_P(value); - char buf[buf_size + 1]; - strcpy_P(buf, value); - Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, buf); -} - -void ResponseCmndChar(const char* value) -{ - Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, value); -} - -void ResponseCmndStateText(uint32_t value) -{ - ResponseCmndChar(GetStateText(value)); -} - -void ResponseCmndDone(void) -{ - ResponseCmndChar(D_JSON_DONE); -} - -void ResponseCmndIdxChar(const char* value) -{ - Response_P(S_JSON_COMMAND_INDEX_SVALUE, XdrvMailbox.command, XdrvMailbox.index, value); -} - -void ResponseCmndAll(uint32_t text_index, uint32_t count) -{ - uint32_t real_index = text_index; - mqtt_data[0] = '\0'; - for (uint32_t i = 0; i < count; i++) { - if ((SET_MQTT_GRP_TOPIC == text_index) && (1 == i)) { real_index = SET_MQTT_GRP_TOPIC2 -1; } - ResponseAppend_P(PSTR("%c\"%s%d\":\"%s\""), (i) ? ',' : '{', XdrvMailbox.command, i +1, SettingsText(real_index +i)); - } - ResponseJsonEnd(); -} - - - -void ExecuteCommand(const char *cmnd, uint32_t source) -{ - - - -#ifdef USE_DEBUG_DRIVER - ShowFreeMem(PSTR("ExecuteCommand")); -#endif - ShowSource(source); - - const char *pos = cmnd; - while (*pos && isspace(*pos)) { - pos++; - } - - const char *start = pos; - - while (*pos && (isalpha(*pos) || isdigit(*pos) || '_' == *pos || '/' == *pos)) { - if ('/' == *pos) { - start = pos + 1; - } - pos++; - } - if ('\0' == *start || pos <= start) { - return; - } - - uint32_t size = pos - start; - char stopic[size + 2]; - stopic[0] = '/'; - memcpy(stopic+1, start, size); - stopic[size+1] = '\0'; - - char svalue[strlen(pos) +1]; - strlcpy(svalue, pos, sizeof(svalue)); - CommandHandler(stopic, svalue, strlen(svalue)); -} -# 174 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_command.ino" -void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len) -{ -#ifdef USE_DEBUG_DRIVER - ShowFreeMem(PSTR("CommandHandler")); -#endif - - while (*dataBuf && isspace(*dataBuf)) { - dataBuf++; - data_len--; - } - - bool grpflg = false; - uint32_t real_index = SET_MQTT_GRP_TOPIC; - for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { - if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } - char *group_topic = SettingsText(real_index +i); - if (*group_topic && strstr(topicBuf, group_topic) != nullptr) { - grpflg = true; - break; - } - } - - char stemp1[TOPSZ]; - GetFallbackTopic_P(stemp1, ""); - fallback_topic_flag = (!strncmp(topicBuf, stemp1, strlen(stemp1))); - - char *type = strrchr(topicBuf, '/'); - - uint32_t index = 1; - bool user_index = false; - if (type != nullptr) { - type++; - uint32_t i; - for (i = 0; i < strlen(type); i++) { - type[i] = toupper(type[i]); - } - while (isdigit(type[i-1])) { - i--; - } - if (i < strlen(type)) { - index = atoi(type +i); - user_index = true; - } - type[i] = '\0'; - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CMD: " D_GROUP " %d, " D_INDEX " %d, " D_COMMAND " \"%s\", " D_DATA " \"%s\""), grpflg, index, type, dataBuf); - - if (type != nullptr) { - Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_ERROR "\"}")); - - if (Settings.ledstate &0x02) { blinks++; } - - if (!strcmp(dataBuf,"?")) { data_len = 0; } - - char *p; - int32_t payload = strtol(dataBuf, &p, 0); - if (p == dataBuf) { payload = -99; } - int temp_payload = GetStateNumber(dataBuf); - if (temp_payload > -1) { payload = temp_payload; } - - DEBUG_CORE_LOG(PSTR("CMD: Payload %d"), payload); - - - backlog_delay = millis() + Settings.param[P_BACKLOG_DELAY]; - - char command[CMDSZ] = { 0 }; - XdrvMailbox.command = command; - XdrvMailbox.index = index; - XdrvMailbox.data_len = data_len; - XdrvMailbox.payload = payload; - XdrvMailbox.grpflg = grpflg; - XdrvMailbox.usridx = user_index; - XdrvMailbox.topic = type; - XdrvMailbox.data = dataBuf; - -#ifdef USE_SCRIPT_SUB_COMMAND - - if (!Script_SubCmd()) { - if (!DecodeCommand(kTasmotaCommands, TasmotaCommand)) { - if (!XdrvCall(FUNC_COMMAND)) { - if (!XsnsCall(FUNC_COMMAND)) { - type = nullptr; - } - } - } - } -#else - if (!DecodeCommand(kTasmotaCommands, TasmotaCommand)) { - if (!XdrvCall(FUNC_COMMAND)) { - if (!XsnsCall(FUNC_COMMAND)) { - type = nullptr; - } - } - } -#endif - - } - - if (type == nullptr) { - blinks = 201; - snprintf_P(stemp1, sizeof(stemp1), PSTR(D_JSON_COMMAND)); - Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_UNKNOWN "\"}")); - type = (char*)stemp1; - } - - if (mqtt_data[0] != '\0') { - MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); - XdrvRulesProcess(); - } - fallback_topic_flag = false; -} - - - -void CmndBacklog(void) -{ - if (XdrvMailbox.data_len) { - -#ifdef SUPPORT_IF_STATEMENT - char *blcommand = strtok(XdrvMailbox.data, ";"); - while ((blcommand != nullptr) && (backlog.size() < MAX_BACKLOG)) -#else - uint32_t bl_pointer = (!backlog_pointer) ? MAX_BACKLOG -1 : backlog_pointer; - bl_pointer--; - char *blcommand = strtok(XdrvMailbox.data, ";"); - while ((blcommand != nullptr) && (backlog_index != bl_pointer)) -#endif - { - while(true) { - blcommand = Trim(blcommand); - if (!strncasecmp_P(blcommand, PSTR(D_CMND_BACKLOG), strlen(D_CMND_BACKLOG))) { - blcommand += strlen(D_CMND_BACKLOG); - } else { - break; - } - } - if (*blcommand != '\0') { -#ifdef SUPPORT_IF_STATEMENT - if (backlog.size() < MAX_BACKLOG) { - backlog.add(blcommand); - } -#else - backlog[backlog_index] = String(blcommand); - backlog_index++; - if (backlog_index >= MAX_BACKLOG) backlog_index = 0; -#endif - } - blcommand = strtok(nullptr, ";"); - } - - mqtt_data[0] = '\0'; - } else { - bool blflag = BACKLOG_EMPTY; -#ifdef SUPPORT_IF_STATEMENT - backlog.clear(); -#else - backlog_pointer = backlog_index; -#endif - ResponseCmndChar(blflag ? D_JSON_EMPTY : D_JSON_ABORTED); - } -} - -void CmndDelay(void) -{ - if ((XdrvMailbox.payload >= (MIN_BACKLOG_DELAY / 100)) && (XdrvMailbox.payload <= 3600)) { - backlog_delay = millis() + (100 * XdrvMailbox.payload); - } - uint32_t bl_delay = 0; - long bl_delta = TimePassedSince(backlog_delay); - if (bl_delta < 0) { bl_delay = (bl_delta *-1) / 100; } - ResponseCmndNumber(bl_delay); -} - -void CmndPower(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= devices_present)) { - if ((XdrvMailbox.payload < POWER_OFF) || (XdrvMailbox.payload > POWER_BLINK_STOP)) { - XdrvMailbox.payload = POWER_SHOW_STATE; - } - - 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) -{ - uint32_t payload = ((XdrvMailbox.payload < 0) || (XdrvMailbox.payload > MAX_STATUS)) ? 99 : XdrvMailbox.payload; - - uint32_t option = STAT; - char stemp[200]; - char stemp2[TOPSZ]; - - - - - - if ((!Settings.flag.mqtt_enabled) && (6 == payload)) { payload = 99; } - if (!energy_flg && (9 == payload)) { payload = 99; } - if (!CrashFlag() && (12 == payload)) { payload = 99; } - - if ((0 == payload) || (99 == payload)) { - uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present; -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan()) { maxfn = 1; } -#endif - stemp[0] = '\0'; - for (uint32_t i = 0; i < maxfn; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%s%s\"%s\"" ), stemp, (i > 0 ? "," : ""), SettingsText(SET_FRIENDLYNAME1 +i)); - } - stemp2[0] = '\0'; - for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%d" ), stemp2, (i > 0 ? "," : ""), Settings.switchmode[i]); - } - Response_P(PSTR("{\"" D_CMND_STATUS "\":{\"" D_CMND_MODULE "\":%d,\"" D_CMND_FRIENDLYNAME "\":[%s],\"" D_CMND_TOPIC "\":\"%s\",\"" - D_CMND_BUTTONTOPIC "\":\"%s\",\"" D_CMND_POWER "\":%d,\"" D_CMND_POWERONSTATE "\":%d,\"" D_CMND_LEDSTATE "\":%d,\"" - D_CMND_LEDMASK "\":\"%04X\",\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_SWITCHTOPIC "\":\"%s\",\"" - D_CMND_SWITCHMODE "\":[%s],\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_SWITCHRETAIN "\":%d,\"" D_CMND_SENSORRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"), - ModuleNr(), stemp, mqtt_topic, - SettingsText(SET_MQTT_BUTTON_TOPIC), power, Settings.poweronstate, Settings.ledstate, - Settings.ledmask, Settings.save_data, - Settings.flag.save_state, - SettingsText(SET_MQTT_SWITCH_TOPIC), - stemp2, - Settings.flag.mqtt_button_retain, - Settings.flag.mqtt_switch_retain, - Settings.flag.mqtt_sensor_retain, - Settings.flag.mqtt_power_retain); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS)); - } - - if ((0 == payload) || (1 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS1_PARAMETER "\":{\"" D_JSON_BAUDRATE "\":%d,\"" D_CMND_SERIALCONFIG "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\",\"" D_CMND_OTAURL "\":\"%s\",\"" - D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_JSON_STARTUPUTC "\":\"%s\",\"" D_CMND_SLEEP "\":%d,\"" - D_JSON_CONFIG_HOLDER "\":%d,\"" D_JSON_BOOTCOUNT "\":%d,\"BCResetTime\":\"%s\",\"" D_JSON_SAVECOUNT "\":%d,\"" D_JSON_SAVEADDRESS "\":\"%X\"}}"), - Settings.baudrate * 300, GetSerialConfig().c_str(), SettingsText(SET_MQTT_GRP_TOPIC), SettingsText(SET_OTAURL), - GetResetReason().c_str(), GetUptime().c_str(), GetDateAndTime(DT_RESTART).c_str(), Settings.sleep, - Settings.cfg_holder, Settings.bootcount, GetDateAndTime(DT_BOOTCOUNT).c_str(), Settings.save_flag, GetSettingsAddress()); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "1")); - } - - if ((0 == payload) || (2 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS2_FIRMWARE "\":{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\",\"" - D_JSON_BOOTVERSION "\":%d,\"" D_JSON_COREVERSION "\":\"" ARDUINO_CORE_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\"," - "\"Hardware\":\"%s\"" - "%s}}"), - my_version, my_image, GetBuildDateAndTime().c_str(), - ESP_getBootVersion(), ESP.getSdkVersion(), - GetDeviceHardware().c_str(), - GetStatistics().c_str()); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "2")); - } - - if ((0 == payload) || (3 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS3_LOGGING "\":{\"" D_CMND_SERIALLOG "\":%d,\"" D_CMND_WEBLOG "\":%d,\"" D_CMND_MQTTLOG "\":%d,\"" D_CMND_SYSLOG "\":%d,\"" - D_CMND_LOGHOST "\":\"%s\",\"" D_CMND_LOGPORT "\":%d,\"" D_CMND_SSID "\":[\"%s\",\"%s\"],\"" D_CMND_TELEPERIOD "\":%d,\"" - D_JSON_RESOLUTION "\":\"%08X\",\"" D_CMND_SETOPTION "\":[\"%08X\",\"%s\",\"%08X\",\"%08X\"]}}"), - Settings.seriallog_level, Settings.weblog_level, Settings.mqttlog_level, Settings.syslog_level, - SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), Settings.tele_period, - Settings.flag2.data, Settings.flag.data, ToHex_P((unsigned char*)Settings.param, PARAM8_SIZE, stemp2, sizeof(stemp2)), - Settings.flag3.data, Settings.flag4.data); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "3")); - } - - if ((0 == payload) || (4 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS4_MEMORY "\":{\"" D_JSON_PROGRAMSIZE "\":%d,\"" D_JSON_FREEMEMORY "\":%d,\"" D_JSON_HEAPSIZE "\":%d,\"" - D_JSON_PROGRAMFLASHSIZE "\":%d,\"" D_JSON_FLASHSIZE "\":%d,\"" D_JSON_FLASHCHIPID "\":\"%06X\",\"" D_JSON_FLASHMODE "\":%d,\"" - D_JSON_FEATURES "\":[\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\"]"), - ESP_getSketchSize()/1024, ESP.getFreeSketchSpace()/1024, ESP.getFreeHeap()/1024, - ESP.getFlashChipSize()/1024, ESP.getFlashChipRealSize()/1024, ESP_getFlashChipId(), ESP.getFlashChipMode(), - LANGUAGE_LCID, feature_drv1, feature_drv2, feature_sns1, feature_sns2, feature5, feature6); - XsnsDriverState(); - ResponseAppend_P(PSTR(",\"Sensors\":")); - XsnsSensorState(); - ResponseJsonEndEnd(); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "4")); - } - - if ((0 == payload) || (5 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS5_NETWORK "\":{\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"" D_JSON_GATEWAY "\":\"%s\",\"" - D_JSON_SUBNETMASK "\":\"%s\",\"" D_JSON_DNSSERVER "\":\"%s\",\"" D_JSON_MAC "\":\"%s\",\"" - D_CMND_WEBSERVER "\":%d,\"" D_CMND_WIFICONFIG "\":%d,\"" D_CMND_WIFIPOWER "\":%s}}"), - my_hostname, WiFi.localIP().toString().c_str(), IPAddress(Settings.ip_address[1]).toString().c_str(), - IPAddress(Settings.ip_address[2]).toString().c_str(), IPAddress(Settings.ip_address[3]).toString().c_str(), WiFi.macAddress().c_str(), - Settings.webserver, Settings.sta_config, WifiGetOutputPower().c_str()); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "5")); - } - - if (((0 == payload) || (6 == payload)) && Settings.flag.mqtt_enabled) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" - D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), - SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), - mqtt_client, SettingsText(SET_MQTT_USER), MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "6")); - } - - if ((0 == payload) || (7 == payload)) { - if (99 == Settings.timezone) { - snprintf_P(stemp, sizeof(stemp), PSTR("%d" ), Settings.timezone); - } else { - snprintf_P(stemp, sizeof(stemp), PSTR("\"%s\"" ), GetTimeZone().c_str()); - } -#if defined(USE_TIMERS) && defined(USE_SUNRISE) - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" - D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s,\"" D_JSON_SUNRISE "\":\"%s\",\"" D_JSON_SUNSET "\":\"%s\"}}"), - GetDateAndTime(DT_UTC).c_str(), GetDateAndTime(DT_LOCALNOTZ).c_str(), GetDateAndTime(DT_DST).c_str(), - GetDateAndTime(DT_STD).c_str(), stemp, GetSun(0).c_str(), GetSun(1).c_str()); -#else - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" - D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s}}"), - GetDateAndTime(DT_UTC).c_str(), GetDateAndTime(DT_LOCALNOTZ).c_str(), GetDateAndTime(DT_DST).c_str(), - GetDateAndTime(DT_STD).c_str(), stemp); -#endif - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "7")); - } - -#if defined(USE_ENERGY_SENSOR) && defined(USE_ENERGY_MARGIN_DETECTION) - if (energy_flg) { - if ((0 == payload) || (9 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS9_MARGIN "\":{\"" D_CMND_POWERDELTA "\":%d,\"" D_CMND_POWERLOW "\":%d,\"" D_CMND_POWERHIGH "\":%d,\"" - D_CMND_VOLTAGELOW "\":%d,\"" D_CMND_VOLTAGEHIGH "\":%d,\"" D_CMND_CURRENTLOW "\":%d,\"" D_CMND_CURRENTHIGH "\":%d}}"), - Settings.energy_power_delta, Settings.energy_min_power, Settings.energy_max_power, - Settings.energy_min_voltage, Settings.energy_max_voltage, Settings.energy_min_current, Settings.energy_max_current); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "9")); - } - } -#endif - - if ((0 == payload) || (8 == payload) || (10 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS10_SENSOR "\":")); - MqttShowSensor(); - ResponseJsonEnd(); - if (8 == payload) { - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "8")); - } else { - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "10")); - } - } - - if ((0 == payload) || (11 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS11_STATUS "\":")); - MqttShowState(); - ResponseJsonEnd(); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "11")); - } - - if (CrashFlag()) { - if ((0 == payload) || (12 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS12_STATUS "\":")); - CrashDump(); - ResponseJsonEnd(); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "12")); - } - } - -#ifdef USE_SCRIPT_STATUS - if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">U",2,mqtt_data); -#endif - mqtt_data[0] = '\0'; -} - -void CmndState(void) -{ - mqtt_data[0] = '\0'; - MqttShowState(); - if (Settings.flag3.hass_tele_on_power) { - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN); - } -#ifdef USE_HOME_ASSISTANT - if (Settings.flag.hass_discovery) { - HAssPublishStatus(); - } -#endif -} - -void CmndTempOffset(void) -{ - if (XdrvMailbox.data_len > 0) { - int value = (int)(CharToFloat(XdrvMailbox.data) * 10); - if ((value > -127) && (value < 127)) { - Settings.temp_comp = value; - } - } - ResponseCmndFloat((float)(Settings.temp_comp) / 10, 1); -} - -void CmndHumOffset(void) -{ - if (XdrvMailbox.data_len > 0) { - int value = (int)(CharToFloat(XdrvMailbox.data) * 10); - if ((value > -101) && (value < 101)) { - Settings.hum_comp = value; - } - } - ResponseCmndFloat((float)(Settings.hum_comp) / 10, 1); -} - -void CmndGlobalTemp(void) -{ - if (XdrvMailbox.data_len > 0) { - float temperature = CharToFloat(XdrvMailbox.data); - if (!isnan(temperature) && Settings.flag.temperature_conversion) { - temperature = (temperature - 32) / 1.8; - } - if ((temperature >= -50.0) && (temperature <= 100.0)) { - ConvertTemp(temperature); - global_update = 1; - } - } - ResponseCmndFloat(global_temperature, 1); -} - -void CmndGlobalHum(void) -{ - if (XdrvMailbox.data_len > 0) { - float humidity = CharToFloat(XdrvMailbox.data); - if ((humidity >= 0.0) && (humidity <= 100.0)) { - ConvertHumidity(humidity); - global_update = 1; - } - } - ResponseCmndFloat(global_humidity, 1); -} - -void CmndSleep(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 251)) { - Settings.sleep = XdrvMailbox.payload; - ssleep = XdrvMailbox.payload; - WiFiSetSleepMode(); - } - Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.sleep, ssleep); - -} - -void CmndUpgrade(void) -{ - - - - - if (((1 == XdrvMailbox.data_len) && (1 == XdrvMailbox.payload)) || ((XdrvMailbox.data_len >= 3) && NewerVersion(XdrvMailbox.data))) { - ota_state_flag = 3; - char stemp1[TOPSZ]; - Response_P(PSTR("{\"%s\":\"" D_JSON_VERSION " %s " D_JSON_FROM " %s\"}"), XdrvMailbox.command, my_version, GetOtaUrl(stemp1, sizeof(stemp1))); - } else { - Response_P(PSTR("{\"%s\":\"" D_JSON_ONE_OR_GT "\"}"), XdrvMailbox.command, my_version); - } -} - -void CmndOtaUrl(void) -{ - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_OTAURL, (SC_DEFAULT == Shortcut()) ? OTA_URL : XdrvMailbox.data); - } - ResponseCmndChar(SettingsText(SET_OTAURL)); -} - -void CmndSeriallog(void) -{ - if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { - Settings.flag.mqtt_serial = 0; - SetSeriallog(XdrvMailbox.payload); - } - Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.seriallog_level, seriallog_level); -} - -void CmndRestart(void) -{ - switch (XdrvMailbox.payload) { - case 1: - restart_flag = 2; - ResponseCmndChar(D_JSON_RESTARTING); - break; - case -1: - CmndCrash(); - break; - case -2: - CmndWDT(); - break; - case -3: - CmndBlockedLoop(); - break; - case 99: - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING)); - EspRestart(); - break; - default: - ResponseCmndChar_P(PSTR(D_JSON_ONE_TO_RESTART)); - } -} - -void CmndPowerOnState(void) -{ -#ifdef ESP8266 - if (my_module_type != MOTOR) -#endif - { - - - - - - - - if ((XdrvMailbox.payload >= POWER_ALL_OFF) && (XdrvMailbox.payload <= POWER_ALL_OFF_PULSETIME_ON)) { - Settings.poweronstate = XdrvMailbox.payload; - if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { - for (uint32_t i = 1; i <= devices_present; i++) { - ExecuteCommandPower(i, POWER_ON, SRC_IGNORE); - } - } - } - ResponseCmndNumber(Settings.poweronstate); - } -} - -void CmndPulsetime(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PULSETIMERS)) { - uint32_t items = 1; - if (!XdrvMailbox.usridx && !XdrvMailbox.data_len) { - items = MAX_PULSETIMERS; - } else { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 65536)) { - Settings.pulse_timer[XdrvMailbox.index -1] = XdrvMailbox.payload; - SetPulseTimer(XdrvMailbox.index -1, XdrvMailbox.payload); - } - } - mqtt_data[0] = '\0'; - for (uint32_t i = 0; i < items; i++) { - uint32_t index = (1 == items) ? XdrvMailbox.index : i +1; - ResponseAppend_P(PSTR("%c\"%s%d\":{\"" D_JSON_SET "\":%d,\"" D_JSON_REMAINING "\":%d}"), - (i) ? ',' : '{', - XdrvMailbox.command, index, - Settings.pulse_timer[index -1], GetPulseTimer(index -1)); - } - ResponseJsonEnd(); - } -} - -void CmndBlinktime(void) -{ - if ((XdrvMailbox.payload > 1) && (XdrvMailbox.payload <= 3600)) { - Settings.blinktime = XdrvMailbox.payload; - if (blink_timer > 0) { blink_timer = millis() + (100 * XdrvMailbox.payload); } - } - ResponseCmndNumber(Settings.blinktime); -} - -void CmndBlinkcount(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 65536)) { - Settings.blinkcount = XdrvMailbox.payload; - if (blink_counter) { blink_counter = Settings.blinkcount *2; } - } - ResponseCmndNumber(Settings.blinkcount); -} - -void CmndSavedata(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3600)) { - Settings.save_data = XdrvMailbox.payload; - save_data_counter = Settings.save_data; - } - SettingsSaveAll(); - char stemp1[TOPSZ]; - if (Settings.save_data > 1) { - snprintf_P(stemp1, sizeof(stemp1), PSTR(D_JSON_EVERY " %d " D_UNIT_SECOND), Settings.save_data); - } - ResponseCmndChar((Settings.save_data > 1) ? stemp1 : GetStateText(Settings.save_data)); -} - -void CmndSetoption(void) -{ - if (XdrvMailbox.index < 114) { - uint32_t ptype; - uint32_t pindex; - if (XdrvMailbox.index <= 31) { - ptype = 2; - pindex = XdrvMailbox.index; - } - else if (XdrvMailbox.index <= 49) { - ptype = 1; - pindex = XdrvMailbox.index -32; - } - else if (XdrvMailbox.index <= 81) { - ptype = 3; - pindex = XdrvMailbox.index -50; - } - else { - ptype = 4; - pindex = XdrvMailbox.index -82; - } - - if (XdrvMailbox.payload >= 0) { - if (1 == ptype) { - uint32_t param_low = 0; - uint32_t param_high = 255; - switch (pindex) { - case P_HOLD_TIME: - case P_MAX_POWER_RETRY: - param_low = 1; - param_high = 250; - break; - } - if ((XdrvMailbox.payload >= param_low) && (XdrvMailbox.payload <= param_high)) { - Settings.param[pindex] = XdrvMailbox.payload; -#ifdef USE_LIGHT - if (P_RGB_REMAP == pindex) { - LightUpdateColorMapping(); - restart_flag = 2; - } -#endif -#if (defined(USE_IR_REMOTE) && defined(USE_IR_RECEIVE)) || defined(USE_IR_REMOTE_FULL) - if (P_IR_UNKNOW_THRESHOLD == pindex) { - IrReceiveUpdateThreshold(); - } -#endif - } else { - ptype = 99; - } - } else { - if (XdrvMailbox.payload <= 1) { - if (2 == ptype) { - switch (pindex) { - case 5: - case 6: - case 7: - case 9: - case 14: - case 22: - case 23: - case 25: - case 27: - ptype = 99; - break; - case 3: - case 15: - restart_flag = 2; - default: - bitWrite(Settings.flag.data, pindex, XdrvMailbox.payload); - } - if (12 == pindex) { - stop_flash_rotate = XdrvMailbox.payload; - SettingsSave(2); - } - #ifdef USE_HOME_ASSISTANT - if ((19 == pindex) || (30 == pindex)) { - HAssDiscover(); - } - #endif - } - else if (3 == ptype) { - bitWrite(Settings.flag3.data, pindex, XdrvMailbox.payload); - switch (pindex) { - case 5: - if (0 == XdrvMailbox.payload) { - restart_flag = 2; - } - break; - case 10: - WiFiSetSleepMode(); - break; - case 18: - case 25: - restart_flag = 2; - break; - } - } - else if (4 == ptype) { - bitWrite(Settings.flag4.data, pindex, XdrvMailbox.payload); - switch (pindex) { - case 6: - restart_flag = 2; - break; - } - } - } else { - ptype = 99; - } - } - } - - if (ptype < 99) { - if (1 == ptype) { - ResponseCmndIdxNumber(Settings.param[pindex]); - } else { - uint32_t flag = Settings.flag.data; - if (3 == ptype) { - flag = Settings.flag3.data; - } - else if (4 == ptype) { - flag = Settings.flag4.data; - } - ResponseCmndIdxChar(GetStateText(bitRead(flag, pindex))); - } - } - } -} - -void CmndTemperatureResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - Settings.flag2.temperature_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.temperature_resolution); -} - -void CmndHumidityResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - Settings.flag2.humidity_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.humidity_resolution); -} - -void CmndPressureResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - Settings.flag2.pressure_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.pressure_resolution); -} - -void CmndPowerResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - Settings.flag2.wattage_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.wattage_resolution); -} - -void CmndVoltageResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - Settings.flag2.voltage_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.voltage_resolution); -} - -void CmndFrequencyResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - Settings.flag2.frequency_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.frequency_resolution); -} - -void CmndCurrentResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - Settings.flag2.current_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.current_resolution); -} - -void CmndEnergyResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) { - Settings.flag2.energy_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.energy_resolution); -} - -void CmndWeightResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - Settings.flag2.weight_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.weight_resolution); -} - -void CmndSpeedUnit(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 6)) { - Settings.flag2.speed_conversion = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.speed_conversion); -} - -void CmndModule(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAXMODULE)) { - bool present = false; - if (0 == XdrvMailbox.payload) { - XdrvMailbox.payload = USER_MODULE; - present = true; - } else { - XdrvMailbox.payload--; - present = ValidTemplateModule(XdrvMailbox.payload); - } - if (present) { - Settings.last_module = Settings.module; - Settings.module = XdrvMailbox.payload; - SetModuleType(); - if (Settings.last_module != XdrvMailbox.payload) { - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - Settings.my_gp.io[i] = GPIO_NONE; - } - } - restart_flag = 2; - } - } - Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, ModuleNr(), ModuleName().c_str()); -} - -void CmndModules(void) -{ - uint32_t midx = USER_MODULE; - uint32_t lines = 1; - bool jsflg = false; - for (uint32_t i = 0; i <= sizeof(kModuleNiceList); i++) { - if (i > 0) { midx = pgm_read_byte(kModuleNiceList + i -1); } - if (!jsflg) { - Response_P(PSTR("{\"" D_CMND_MODULES "%d\":{"), lines); - } else { - ResponseAppend_P(PSTR(",")); - } - jsflg = true; - uint32_t j = i ? midx +1 : 0; - if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), j, AnyModuleName(midx).c_str()) > (LOGSZ - TOPSZ)) || (i == sizeof(kModuleNiceList))) { - ResponseJsonEndEnd(); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, UpperCase(XdrvMailbox.command, XdrvMailbox.command)); - jsflg = false; - lines++; - } - } - mqtt_data[0] = '\0'; -} - -void CmndGpio(void) -{ - if (XdrvMailbox.index < sizeof(Settings.my_gp)) { - myio cmodule; - ModuleGpios(&cmodule); - if (ValidGPIO(XdrvMailbox.index, cmodule.io[XdrvMailbox.index]) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < GPIO_SENSOR_END)) { - bool present = false; - for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { - uint32_t midx = pgm_read_byte(kGpioNiceList + i); - if (midx == XdrvMailbox.payload) { present = true; } - } - if (present) { - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - if (ValidGPIO(i, cmodule.io[i]) && (Settings.my_gp.io[i] == XdrvMailbox.payload)) { - Settings.my_gp.io[i] = GPIO_NONE; - } - } - Settings.my_gp.io[XdrvMailbox.index] = XdrvMailbox.payload; - restart_flag = 2; - } - } - Response_P(PSTR("{")); - bool jsflg = false; - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - if (ValidGPIO(i, cmodule.io[i]) || ((GPIO_USER == XdrvMailbox.payload) && !FlashPin(i))) { - if (jsflg) { ResponseAppend_P(PSTR(",")); } - jsflg = true; - uint8_t sensor_type = Settings.my_gp.io[i]; - if (!ValidGPIO(i, cmodule.io[i])) { - sensor_type = cmodule.io[i]; - if (GPIO_USER == sensor_type) { - sensor_type = GPIO_NONE; - } - } - uint8_t sensor_name_idx = sensor_type; - const char *sensor_names = kSensorNames; - if (sensor_type > GPIO_FIX_START) { - sensor_name_idx = sensor_type - GPIO_FIX_START -1; - sensor_names = kSensorNamesFixed; - } - char stemp1[TOPSZ]; - ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":{\"%d\":\"%s\"}"), - i, sensor_type, GetTextIndexed(stemp1, sizeof(stemp1), sensor_name_idx, sensor_names)); - } - } - if (jsflg) { - ResponseJsonEnd(); - } else { - ResponseCmndChar(D_JSON_NOT_SUPPORTED); - } - } -} - -void CmndGpios(void) -{ - myio cmodule; - ModuleGpios(&cmodule); - uint32_t lines = 1; - bool jsflg = false; - for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { - uint32_t midx = pgm_read_byte(kGpioNiceList + i); - if ((XdrvMailbox.payload != 255) && GetUsedInModule(midx, cmodule.io)) { continue; } - if (!jsflg) { - Response_P(PSTR("{\"" D_CMND_GPIOS "%d\":{"), lines); - } else { - ResponseAppend_P(PSTR(",")); - } - jsflg = true; - char stemp1[TOPSZ]; - if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), midx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (LOGSZ - TOPSZ)) || (i == sizeof(kGpioNiceList) -1)) { - ResponseJsonEndEnd(); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, UpperCase(XdrvMailbox.command, XdrvMailbox.command)); - jsflg = false; - lines++; - } - } - mqtt_data[0] = '\0'; -} - -void CmndTemplate(void) -{ - - bool error = false; - - if (strstr(XdrvMailbox.data, "{") == nullptr) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= MAXMODULE)) { - XdrvMailbox.payload--; - if (ValidTemplateModule(XdrvMailbox.payload)) { - ModuleDefault(XdrvMailbox.payload); - if (USER_MODULE == Settings.module) { restart_flag = 2; } - } - } - else if (0 == XdrvMailbox.payload) { - if (Settings.module != USER_MODULE) { - ModuleDefault(Settings.module); - } - } - else if (255 == XdrvMailbox.payload) { - if (Settings.module != USER_MODULE) { - ModuleDefault(Settings.module); - } - SettingsUpdateText(SET_TEMPLATE_NAME, "Merged"); - uint32_t j = 0; - for (uint32_t i = 0; i < sizeof(mycfgio); i++) { -#ifdef ESP8266 - if (6 == i) { j = 9; } - if (8 == i) { j = 12; } -#else - if (6 == i) { j = 12; } -#endif - if (my_module.io[j] > GPIO_NONE) { - Settings.user_template.gp.io[i] = my_module.io[j]; - } - j++; - } - } - } - else { - if (JsonTemplate(XdrvMailbox.data)) { - if (USER_MODULE == Settings.module) { restart_flag = 2; } - } else { - ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); - error = true; - } - } - if (!error) { TemplateJson(); } -} - -void CmndPwm(void) -{ - if (pwm_present && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PWMS)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= Settings.pwm_range) && (pin[GPIO_PWM1 + XdrvMailbox.index -1] < 99)) { - Settings.pwm_value[XdrvMailbox.index -1] = XdrvMailbox.payload; - analogWrite(pin[GPIO_PWM1 + XdrvMailbox.index -1], bitRead(pwm_inverted, XdrvMailbox.index -1) ? Settings.pwm_range - XdrvMailbox.payload : XdrvMailbox.payload); - } - Response_P(PSTR("{")); - MqttShowPWMState(); - ResponseJsonEnd(); - } -} - -void CmndPwmfrequency(void) -{ - if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload >= PWM_MIN) && (XdrvMailbox.payload <= PWM_MAX))) { - Settings.pwm_frequency = (1 == XdrvMailbox.payload) ? PWM_FREQ : XdrvMailbox.payload; - analogWriteFreq(Settings.pwm_frequency); - } - ResponseCmndNumber(Settings.pwm_frequency); -} - -void CmndPwmrange(void) -{ - if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload > 254) && (XdrvMailbox.payload < 1024))) { - Settings.pwm_range = (1 == XdrvMailbox.payload) ? PWM_RANGE : XdrvMailbox.payload; - for (uint32_t i = 0; i < MAX_PWMS; i++) { - if (Settings.pwm_value[i] > Settings.pwm_range) { - Settings.pwm_value[i] = Settings.pwm_range; - } - } - analogWriteRange(Settings.pwm_range); - } - ResponseCmndNumber(Settings.pwm_range); -} - -void CmndButtonDebounce(void) -{ - if ((XdrvMailbox.payload > 39) && (XdrvMailbox.payload < 1001)) { - Settings.button_debounce = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.button_debounce); -} - -void CmndSwitchDebounce(void) -{ - if ((XdrvMailbox.payload > 39) && (XdrvMailbox.payload < 1001)) { - Settings.switch_debounce = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.switch_debounce); -} - -void CmndBaudrate(void) -{ - if (XdrvMailbox.payload >= 300) { - XdrvMailbox.payload /= 300; - uint32_t baudrate = (XdrvMailbox.payload & 0xFFFF) * 300; - SetSerialBaudrate(baudrate); - } - ResponseCmndNumber(Settings.baudrate * 300); -} - -void CmndSerialConfig(void) -{ - - - - - if (XdrvMailbox.data_len > 0) { - if (XdrvMailbox.data_len < 3) { - if ((XdrvMailbox.payload >= TS_SERIAL_5N1) && (XdrvMailbox.payload <= TS_SERIAL_8O2)) { - SetSerialConfig(XdrvMailbox.payload); - } - } - else if ((XdrvMailbox.payload >= 5) && (XdrvMailbox.payload <= 8)) { - uint8_t serial_config = XdrvMailbox.payload -5; - - bool valid = true; - char parity = (XdrvMailbox.data[1] & 0xdf); - if ('E' == parity) { - serial_config += 0x08; - } - else if ('O' == parity) { - serial_config += 0x10; - } - else if ('N' != parity) { - valid = false; - } - - if ('2' == XdrvMailbox.data[2]) { - serial_config += 0x04; - } - else if ('1' != XdrvMailbox.data[2]) { - valid = false; - } - - if (valid) { - SetSerialConfig(serial_config); - } - } - } - ResponseCmndChar(GetSerialConfig().c_str()); -} - -void CmndSerialSend(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 5)) { - SetSeriallog(LOG_LEVEL_NONE); - Settings.flag.mqtt_serial = 1; - Settings.flag.mqtt_serial_raw = (XdrvMailbox.index > 3) ? 1 : 0; - if (XdrvMailbox.data_len > 0) { - if (1 == XdrvMailbox.index) { - Serial.printf("%s\n", XdrvMailbox.data); - } - else if (2 == XdrvMailbox.index || 4 == XdrvMailbox.index) { - for (uint32_t i = 0; i < XdrvMailbox.data_len; i++) { - Serial.write(XdrvMailbox.data[i]); - } - } - else if (3 == XdrvMailbox.index) { - uint32_t dat_len = XdrvMailbox.data_len; - Serial.printf("%s", Unescape(XdrvMailbox.data, &dat_len)); - } - else if (5 == XdrvMailbox.index) { - SerialSendRaw(RemoveSpace(XdrvMailbox.data)); - } - ResponseCmndDone(); - } - } -} - -void CmndSerialDelimiter(void) -{ - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.payload < 256)) { - if (XdrvMailbox.payload > 0) { - Settings.serial_delimiter = XdrvMailbox.payload; - } else { - uint32_t dat_len = XdrvMailbox.data_len; - Unescape(XdrvMailbox.data, &dat_len); - Settings.serial_delimiter = XdrvMailbox.data[0]; - } - } - ResponseCmndNumber(Settings.serial_delimiter); -} - -void CmndSyslog(void) -{ - if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { - SetSyslog(XdrvMailbox.payload); - } - Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.syslog_level, syslog_level); -} - -void CmndLoghost(void) -{ - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_SYSLOG_HOST, (SC_DEFAULT == Shortcut()) ? SYS_LOG_HOST : XdrvMailbox.data); - } - ResponseCmndChar(SettingsText(SET_SYSLOG_HOST)); -} - -void CmndLogport(void) -{ - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 65536)) { - Settings.syslog_port = (1 == XdrvMailbox.payload) ? SYS_LOG_PORT : XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.syslog_port); -} - -void CmndIpAddress(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { - uint32_t address; - if (ParseIp(&address, XdrvMailbox.data)) { - Settings.ip_address[XdrvMailbox.index -1] = address; - - } - char stemp1[TOPSZ]; - snprintf_P(stemp1, sizeof(stemp1), PSTR(" (%s)"), WiFi.localIP().toString().c_str()); - Response_P(S_JSON_COMMAND_INDEX_SVALUE_SVALUE, XdrvMailbox.command, XdrvMailbox.index, IPAddress(Settings.ip_address[XdrvMailbox.index -1]).toString().c_str(), (1 == XdrvMailbox.index) ? stemp1:""); - } -} - -void CmndNtpServer(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_NTP_SERVERS)) { - if (!XdrvMailbox.usridx) { - ResponseCmndAll(SET_NTPSERVER1, MAX_NTP_SERVERS); - } else { - uint32_t ntp_server = SET_NTPSERVER1 + XdrvMailbox.index -1; - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(ntp_server, - (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? NTP_SERVER1 : (2 == XdrvMailbox.index) ? NTP_SERVER2 : NTP_SERVER3 : XdrvMailbox.data); - SettingsUpdateText(ntp_server, ReplaceCommaWithDot(SettingsText(ntp_server))); - - ntp_force_sync = true; - } - ResponseCmndIdxChar(SettingsText(ntp_server)); - } - } -} - -void CmndAp(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { - switch (XdrvMailbox.payload) { - case 0: - Settings.sta_active ^= 1; - break; - case 1: - case 2: - Settings.sta_active = XdrvMailbox.payload -1; - } - restart_flag = 2; - } - Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active)); -} - -void CmndSsid(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SSIDS)) { - if (!XdrvMailbox.usridx) { - ResponseCmndAll(SET_STASSID1, MAX_SSIDS); - } else { - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_STASSID1 + XdrvMailbox.index -1, - (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_SSID1 : STA_SSID2 : XdrvMailbox.data); - Settings.sta_active = XdrvMailbox.index -1; - restart_flag = 2; - } - ResponseCmndIdxChar(SettingsText(SET_STASSID1 + XdrvMailbox.index -1)); - } - } -} - -void CmndPassword(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { - if ((XdrvMailbox.data_len > 4) || (SC_CLEAR == Shortcut()) || (SC_DEFAULT == Shortcut())) { - SettingsUpdateText(SET_STAPWD1 + XdrvMailbox.index -1, - (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_PASS1 : STA_PASS2 : XdrvMailbox.data); - Settings.sta_active = XdrvMailbox.index -1; - restart_flag = 2; - ResponseCmndIdxChar(SettingsText(SET_STAPWD1 + XdrvMailbox.index -1)); - } else { - Response_P(S_JSON_COMMAND_INDEX_ASTERISK, XdrvMailbox.command, XdrvMailbox.index); - } - } -} - -void CmndHostname(void) -{ - if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { - SettingsUpdateText(SET_HOSTNAME, (SC_DEFAULT == Shortcut()) ? WIFI_HOSTNAME : XdrvMailbox.data); - if (strstr(SettingsText(SET_HOSTNAME), "%") != nullptr) { - SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); - } - restart_flag = 2; - } - ResponseCmndChar(SettingsText(SET_HOSTNAME)); -} - -void CmndWifiConfig(void) -{ - if ((XdrvMailbox.payload >= WIFI_RESTART) && (XdrvMailbox.payload < MAX_WIFI_OPTION)) { - if ((EX_WIFI_SMARTCONFIG == XdrvMailbox.payload) || (EX_WIFI_WPSCONFIG == XdrvMailbox.payload)) { - XdrvMailbox.payload = WIFI_MANAGER; - } - Settings.sta_config = XdrvMailbox.payload; - wifi_state_flag = Settings.sta_config; - if (WifiState() > WIFI_RESTART) { - restart_flag = 2; - } - } - char stemp1[TOPSZ]; - Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings.sta_config, GetTextIndexed(stemp1, sizeof(stemp1), Settings.sta_config, kWifiConfig)); -} - -void CmndFriendlyname(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_FRIENDLYNAMES)) { - if (!XdrvMailbox.usridx && !XdrvMailbox.data_len) { - ResponseCmndAll(SET_FRIENDLYNAME1, MAX_FRIENDLYNAMES); - } else { - if (XdrvMailbox.data_len > 0) { - char stemp1[TOPSZ]; - if (1 == XdrvMailbox.index) { - snprintf_P(stemp1, sizeof(stemp1), PSTR(FRIENDLY_NAME)); - } else { - snprintf_P(stemp1, sizeof(stemp1), PSTR(FRIENDLY_NAME "%d"), XdrvMailbox.index); - } - SettingsUpdateText(SET_FRIENDLYNAME1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : (SC_DEFAULT == Shortcut()) ? stemp1 : XdrvMailbox.data); - } - ResponseCmndIdxChar(SettingsText(SET_FRIENDLYNAME1 + XdrvMailbox.index -1)); - } - } -} - -void CmndSwitchMode(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SWITCHES)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_SWITCH_OPTION)) { - Settings.switchmode[XdrvMailbox.index -1] = XdrvMailbox.payload; - } - ResponseCmndIdxNumber(Settings.switchmode[XdrvMailbox.index-1]); - } -} - -void CmndInterlock(void) -{ - - uint32_t max_relays = devices_present; - if (light_type) { max_relays--; } - if (max_relays > sizeof(Settings.interlock[0]) * 8) { max_relays = sizeof(Settings.interlock[0]) * 8; } - if (max_relays > 1) { - if (XdrvMailbox.data_len > 0) { - if (strstr(XdrvMailbox.data, ",") != nullptr) { - for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { Settings.interlock[i] = 0; } - char *group; - char *q; - uint32_t group_index = 0; - power_t relay_mask = 0; - for (group = strtok_r(XdrvMailbox.data, " ", &q); group && group_index < MAX_INTERLOCKS; group = strtok_r(nullptr, " ", &q)) { - char *str; - char *p; - for (str = strtok_r(group, ",", &p); str; str = strtok_r(nullptr, ",", &p)) { - int pbit = atoi(str); - if ((pbit > 0) && (pbit <= max_relays)) { - pbit--; - if (!bitRead(relay_mask, pbit)) { - bitSet(relay_mask, pbit); - bitSet(Settings.interlock[group_index], pbit); - } - } - } - group_index++; - } - for (uint32_t i = 0; i < group_index; i++) { - uint32_t minimal_bits = 0; - for (uint32_t j = 0; j < max_relays; j++) { - if (bitRead(Settings.interlock[i], j)) { minimal_bits++; } - } - if (minimal_bits < 2) { Settings.interlock[i] = 0; } - } - } else { - Settings.flag.interlock = XdrvMailbox.payload &1; - if (Settings.flag.interlock) { - SetDevicePower(power, SRC_IGNORE); - } - } -#ifdef USE_SHUTTER - if (Settings.flag3.shutter_mode) { - ShutterInit(); - } -#endif - } - Response_P(PSTR("{\"" D_CMND_INTERLOCK "\":\"%s\",\"" D_JSON_GROUPS "\":\""), GetStateText(Settings.flag.interlock)); - uint32_t anygroup = 0; - for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { - if (Settings.interlock[i]) { - anygroup++; - ResponseAppend_P(PSTR("%s"), (anygroup > 1) ? " " : ""); - uint32_t anybit = 0; - power_t mask = 1; - for (uint32_t j = 0; j < max_relays; j++) { - if (Settings.interlock[i] & mask) { - anybit++; - ResponseAppend_P(PSTR("%s%d"), (anybit > 1) ? "," : "", j +1); - } - mask <<= 1; - } - } - } - if (!anygroup) { - for (uint32_t j = 1; j <= max_relays; j++) { - ResponseAppend_P(PSTR("%s%d"), (j > 1) ? "," : "", j); - } - } - ResponseAppend_P(PSTR("\"}")); - } else { - Settings.flag.interlock = 0; - ResponseCmndStateText(Settings.flag.interlock); - } -} - -void CmndTeleperiod(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.tele_period = (1 == XdrvMailbox.payload) ? TELE_PERIOD : XdrvMailbox.payload; - if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) Settings.tele_period = 10; - tele_period = Settings.tele_period; - } - ResponseCmndNumber(Settings.tele_period); -} - -void CmndReset(void) -{ - switch (XdrvMailbox.payload) { - case 1: - restart_flag = 211; - ResponseCmndChar(D_JSON_RESET_AND_RESTARTING); - break; - case 2 ... 6: - 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; - Settings.bootcount_reset_time = 0; - ResponseCmndDone(); - break; - default: - ResponseCmndChar(D_JSON_ONE_TO_RESET); - } -} - -void CmndTime(void) -{ - - - - - - - - uint32_t format = Settings.flag2.time_format; - if (XdrvMailbox.data_len > 0) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4)) { - Settings.flag2.time_format = XdrvMailbox.payload -1; - format = Settings.flag2.time_format; - } else { - format = 1; - RtcSetTime(XdrvMailbox.payload); - } - } - mqtt_data[0] = '\0'; - ResponseAppendTimeFormat(format); - ResponseJsonEnd(); -} - -void CmndTimezone(void) -{ - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.payload >= -13)) { - Settings.timezone = XdrvMailbox.payload; - Settings.timezone_minutes = 0; - if (XdrvMailbox.payload < 15) { - char *p = strtok (XdrvMailbox.data, ":"); - if (p) { - p = strtok (nullptr, ":"); - if (p) { - Settings.timezone_minutes = strtol(p, nullptr, 10); - if (Settings.timezone_minutes > 59) { Settings.timezone_minutes = 59; } - } - } - } else { - Settings.timezone = 99; - } - ntp_force_sync = true; - } - if (99 == Settings.timezone) { - ResponseCmndNumber(Settings.timezone); - } else { - char stemp1[TOPSZ]; - snprintf_P(stemp1, sizeof(stemp1), PSTR("%+03d:%02d"), Settings.timezone, Settings.timezone_minutes); - ResponseCmndChar(stemp1); - } -} - -void CmndTimeStdDst(uint32_t ts) -{ - - if (XdrvMailbox.data_len > 0) { - if (strstr(XdrvMailbox.data, ",") != nullptr) { - uint32_t tpos = 0; - int value = 0; - char *p = XdrvMailbox.data; - char *q = p; - while (p && (tpos < 7)) { - if (p > q) { - if (1 == tpos) { Settings.tflag[ts].hemis = value &1; } - if (2 == tpos) { Settings.tflag[ts].week = (value < 0) ? 0 : (value > 4) ? 4 : value; } - if (3 == tpos) { Settings.tflag[ts].month = (value < 1) ? 1 : (value > 12) ? 12 : value; } - if (4 == tpos) { Settings.tflag[ts].dow = (value < 1) ? 1 : (value > 7) ? 7 : value; } - if (5 == tpos) { Settings.tflag[ts].hour = (value < 0) ? 0 : (value > 23) ? 23 : value; } - if (6 == tpos) { Settings.toffset[ts] = (value < -900) ? -900 : (value > 900) ? 900 : value; } - } - p = Trim(p); - if (tpos && (*p == ',')) { p++; } - p = Trim(p); - q = p; - value = strtol(p, &p, 10); - tpos++; - } - ntp_force_sync = true; - } else { - if (0 == XdrvMailbox.payload) { - if (0 == ts) { - SettingsResetStd(); - } else { - SettingsResetDst(); - } - } - ntp_force_sync = true; - } - } - Response_P(PSTR("{\"%s\":{\"Hemisphere\":%d,\"Week\":%d,\"Month\":%d,\"Day\":%d,\"Hour\":%d,\"Offset\":%d}}"), - XdrvMailbox.command, Settings.tflag[ts].hemis, Settings.tflag[ts].week, Settings.tflag[ts].month, Settings.tflag[ts].dow, Settings.tflag[ts].hour, Settings.toffset[ts]); -} - -void CmndTimeStd(void) -{ - CmndTimeStdDst(0); -} - -void CmndTimeDst(void) -{ - CmndTimeStdDst(1); -} - -void CmndAltitude(void) -{ - if ((XdrvMailbox.data_len > 0) && ((XdrvMailbox.payload >= -30000) && (XdrvMailbox.payload <= 30000))) { - Settings.altitude = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.altitude); -} - -void CmndLedPower(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_LEDS)) { - if (99 == pin[GPIO_LEDLNK]) { XdrvMailbox.index = 1; } - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { - Settings.ledstate &= 8; - uint32_t mask = 1 << (XdrvMailbox.index -1); - switch (XdrvMailbox.payload) { - case 0: - led_power &= (0xFF ^ mask); - Settings.ledstate = 0; - break; - case 1: - led_power |= mask; - Settings.ledstate = 8; - break; - case 2: - led_power ^= mask; - Settings.ledstate ^= 8; - break; - } - blinks = 0; - if (99 == pin[GPIO_LEDLNK]) { - SetLedPower(Settings.ledstate &8); - } else { - SetLedPowerIdx(XdrvMailbox.index -1, (led_power & mask)); - } - } - bool state = bitRead(led_power, XdrvMailbox.index -1); - if (99 == pin[GPIO_LEDLNK]) { - state = bitRead(Settings.ledstate, 3); - } - ResponseCmndIdxChar(GetStateText(state)); - } -} - -void CmndLedState(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_LED_OPTION)) { - Settings.ledstate = XdrvMailbox.payload; - if (!Settings.ledstate) { - SetLedPowerAll(0); - SetLedLink(0); - } - } - ResponseCmndNumber(Settings.ledstate); -} - -void CmndLedMask(void) -{ - if (XdrvMailbox.data_len > 0) { - Settings.ledmask = XdrvMailbox.payload; - } - char stemp1[TOPSZ]; - snprintf_P(stemp1, sizeof(stemp1), PSTR("%d (0x%04X)"), Settings.ledmask, Settings.ledmask); - ResponseCmndChar(stemp1); -} - -void CmndWifiPower(void) -{ - if (XdrvMailbox.data_len > 0) { - Settings.wifi_output_power = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10); - if (Settings.wifi_output_power > 205) { - Settings.wifi_output_power = 205; - } - WifiSetOutputPower(); - } - ResponseCmndChar(WifiGetOutputPower().c_str()); -} - -#ifdef USE_I2C -void CmndI2cScan(void) -{ - if (i2c_flg) { - I2cScan(mqtt_data, sizeof(mqtt_data)); - } -} - -void CmndI2cDriver(void) -{ - if (XdrvMailbox.index < MAX_I2C_DRIVERS) { - if (XdrvMailbox.payload >= 0) { - bitWrite(Settings.i2c_drivers[XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1); - restart_flag = 2; - } - } - Response_P(PSTR("{\"" D_CMND_I2CDRIVER "\":")); - I2cDriverState(); - ResponseJsonEnd(); -} -#endif - -#ifdef USE_DEVICE_GROUPS -void CmndDevGroupName(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DEV_GROUP_NAMES)) { - if (XdrvMailbox.data_len > 0) { - if (XdrvMailbox.data_len > TOPSZ) - XdrvMailbox.data[TOPSZ - 1] = 0; - else if (1 == XdrvMailbox.data_len && ('"' == XdrvMailbox.data[0] || '0' == XdrvMailbox.data[0])) - XdrvMailbox.data[0] = 0; - SettingsUpdateText(SET_DEV_GROUP_NAME1 + XdrvMailbox.index - 1, XdrvMailbox.data); - restart_flag = 2; - } - ResponseCmndAll(SET_DEV_GROUP_NAME1, MAX_DEV_GROUP_NAMES); - } -} - -#ifdef USE_DEVICE_GROUPS_SEND -void CmndDevGroupSend(void) -{ - uint8_t device_group_index = (XdrvMailbox.usridx ? XdrvMailbox.index - 1 : 0); - if (device_group_index < device_group_count) { - _SendDeviceGroupMessage(device_group_index, DGR_MSGTYPE_UPDATE_COMMAND); - ResponseCmndChar(XdrvMailbox.data); - } -} -#endif - -void CmndDevGroupShare(void) -{ - uint32_t parm[2] = { Settings.device_group_share_in, Settings.device_group_share_out }; - ParseParameters(2, parm); - Settings.device_group_share_in = parm[0]; - Settings.device_group_share_out = parm[1]; - Response_P(PSTR("{\"" D_CMND_DEVGROUP_SHARE "\":{\"In\":\"%X\",\"Out\":\"%X\"}}"), Settings.device_group_share_in, Settings.device_group_share_out); -} - -void CmndDevGroupStatus(void) -{ - DeviceGroupStatus((XdrvMailbox.usridx ? XdrvMailbox.index - 1 : 0)); -} -#endif - -void CmndSensor(void) -{ - XsnsCall(FUNC_COMMAND_SENSOR); -} - -void CmndDriver(void) -{ - XdrvCall(FUNC_COMMAND_DRIVER); -} -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_crash_recorder.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_crash_recorder.ino" -#ifdef ESP8266 - -const uint32_t crash_magic = 0x53415400; -const uint32_t crash_rtc_offset = 32; -const uint32_t crash_dump_max_len = 31; - - - - - - -extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack, uint32_t stack_end ) -{ - uint32_t addr_written = 0; - uint32_t value; - - for (uint32_t i = stack; i < stack_end; i += 4) { - value = *((uint32_t*) i); - if ((value >= 0x40000000) && (value < 0x40300000)) { - ESP.rtcUserMemoryWrite(crash_rtc_offset + addr_written, (uint32_t*)&value, sizeof(value)); - addr_written++; - if (addr_written >= crash_dump_max_len) { break; } - } - } - value = crash_magic + addr_written; - ESP.rtcUserMemoryWrite(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value)); -} - - -void CmndCrash(void) -{ - volatile uint32_t dummy; - dummy = *((uint32_t*) 0x00000000); -} - - -void CmndWDT(void) -{ - volatile uint32_t dummy = 0; - while (1) { - dummy++; - } -} - - -void CmndBlockedLoop(void) -{ - while (1) { - delay(1000); - } -} - - -void CrashDumpClear(void) -{ - uint32_t value = 0; - ESP.rtcUserMemoryWrite(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value)); -} - - - - - -bool CrashFlag(void) -{ - return ((ResetReason() == REASON_EXCEPTION_RST) || (ResetReason() == REASON_SOFT_WDT_RST) || oswatch_blocked_loop); -} - -void CrashDump(void) -{ - ResponseAppend_P(PSTR("{\"Exception\":%d,\"Reason\":\"%s\",\"EPC\":[\"%08x\",\"%08x\",\"%08x\"],\"EXCVADDR\":\"%08x\",\"DEPC\":\"%08x\""), - resetInfo.exccause, - GetResetReason().c_str(), - resetInfo.epc1, - resetInfo.epc2, - resetInfo.epc3, - resetInfo.excvaddr, - resetInfo.depc); - - uint32_t value; - ESP.rtcUserMemoryRead(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value)); - if (crash_magic == (value & 0xFFFFFF00)) { - ResponseAppend_P(PSTR(",\"CallChain\":[")); - uint32_t count = value & 0x3F; - for (uint32_t i = 0; i < count; i++) { - ESP.rtcUserMemoryRead(crash_rtc_offset +i, (uint32_t*)&value, sizeof(value)); - if (i > 0) { ResponseAppend_P(PSTR(",")); } - ResponseAppend_P(PSTR("\"%08x\""), value); - } - ResponseAppend_P(PSTR("]")); - } - - ResponseJsonEnd(); -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_device_groups.ino" -# 24 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_device_groups.ino" -#ifdef USE_DEVICE_GROUPS - - -#define DGR_MEMBER_TIMEOUT 45000 -#define DGR_ANNOUNCEMENT_INTERVAL 60000 - -extern bool udp_connected; - -struct device_group_member { - struct device_group_member * flink; - IPAddress ip_address; - uint16_t received_sequence; - uint16_t acked_sequence; - uint32_t unicast_count; -}; - -struct device_group { - uint32_t next_announcement_time; - uint32_t next_ack_check_time; - uint32_t member_timeout_time; - uint16_t last_full_status_sequence; - uint16_t message_length; - uint16_t ack_check_interval; - uint8_t message_header_length; - uint8_t initial_status_requests_remaining; - bool local; - char group_name[TOPSZ]; - char message[128]; - struct device_group_member * device_group_members; -#ifdef USE_DEVICE_GROUPS_SEND - uint8_t values_8bit[DGR_ITEM_LAST_8BIT]; - uint16_t values_16bit[DGR_ITEM_LAST_16BIT - DGR_ITEM_MAX_8BIT - 1]; - uint32_t values_32bit[DGR_ITEM_LAST_32BIT - DGR_ITEM_MAX_16BIT - 1]; -#endif -}; - -struct device_group * device_groups; -uint32_t next_check_time = 0; -uint16_t outgoing_sequence = 0; -bool device_groups_initialized = false; -bool device_groups_initialization_failed = false; -bool building_status_message = false; -bool processing_remote_device_message = false; -bool udp_was_connected = false; - -void DeviceGroupsInit(void) -{ - - - for (; device_group_count < MAX_DEV_GROUP_NAMES; device_group_count++) { - if (!*SettingsText(SET_DEV_GROUP_NAME1 + device_group_count)) break; - } - - - device_groups = (struct device_group *)calloc(device_group_count, sizeof(struct device_group)); - if (device_groups == nullptr) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: Error allocating %u-element device group array"), device_group_count); - device_groups_initialization_failed = true; - return; - } - - for (uint32_t device_group_index = 0; device_group_index < device_group_count; device_group_index++) { - struct device_group * device_group = &device_groups[device_group_index]; - strcpy(device_group->group_name, SettingsText(SET_DEV_GROUP_NAME1 + device_group_index)); - - - - if (!device_group->group_name[0]) { - strcpy(device_group->group_name, SettingsText(SET_MQTT_GRP_TOPIC)); - if (device_group_index) { - char str[10]; - sprintf_P(str, PSTR("%u"), device_group_index + 1); - strcat(device_group->group_name, str); - } - } - device_group->message_header_length = sprintf_P(device_group->message, PSTR("%s%s HTTP/1.1\n\n"), kDeviceGroupMessage, device_group->group_name); - device_group->last_full_status_sequence = -1; - } - - device_groups[0].local = true; - - - if (!Settings.device_group_share_in && !Settings.device_group_share_out) { - Settings.device_group_share_in = Settings.device_group_share_out = 0xffffffff; - } - - device_groups_initialized = true; -} - -char * IPAddressToString(const IPAddress& ip_address) -{ - static char buffer[16]; - sprintf_P(buffer, PSTR("%u.%u.%u.%u"), ip_address[0], ip_address[1], ip_address[2], ip_address[3]); - return buffer; -} - -char * BeginDeviceGroupMessage(struct device_group * device_group, uint16_t flags, bool hold_sequence = false) -{ - char * message_ptr = &device_group->message[device_group->message_header_length]; - if (!hold_sequence && !++outgoing_sequence) outgoing_sequence = 1; - *message_ptr++ = outgoing_sequence & 0xff; - *message_ptr++ = outgoing_sequence >> 8; - *message_ptr++ = flags & 0xff; - *message_ptr++ = flags >> 8; - return message_ptr; -} - - -bool DeviceGroupItemShared(bool incoming, uint8_t item) -{ - uint8_t mask = 0; - switch (item) { - case DGR_ITEM_LIGHT_BRI: - case DGR_ITEM_BRI_POWER_ON: - mask = DGR_SHARE_LIGHT_BRI; - break; - case DGR_ITEM_POWER: - mask = DGR_SHARE_POWER; - break; - case DGR_ITEM_LIGHT_SCHEME: - mask = DGR_SHARE_LIGHT_SCHEME; - break; - case DGR_ITEM_LIGHT_FIXED_COLOR: - case DGR_ITEM_LIGHT_CHANNELS: - mask = DGR_SHARE_LIGHT_COLOR; - break; - case DGR_ITEM_LIGHT_FADE: - case DGR_ITEM_LIGHT_SPEED: - mask = DGR_SHARE_LIGHT_FADE; - break; - case DGR_ITEM_BRI_PRESET_LOW: - case DGR_ITEM_BRI_PRESET_HIGH: - mask = DGR_SHARE_DIMMER_SETTINGS; - break; - } - return (!mask || ((incoming ? Settings.device_group_share_in : Settings.device_group_share_out) & mask)); -} - -void SendDeviceGroupPacket(IPAddress ip, char * packet, int len, const char * label) -{ - if (!ip) ip = IPAddress(239,255,255,250); - for (int attempt = 1; attempt <= 5; attempt++) { - if (PortUdp.beginPacket(ip, 1900)) { - PortUdp_write(packet, len); - if (PortUdp.endPacket()) return; - } - delay(10); - } - AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: Error sending %s packet"), label); -} - -void _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType message_type, ...) -{ - - if (!Settings.flag4.device_groups_enabled) return; - - - if (!udp_connected) return; - - - if (processing_remote_device_message && message_type != DGR_MSGTYPE_UPDATE_COMMAND) return; - - - struct device_group * device_group = &device_groups[device_group_index]; - - - if (device_group->initial_status_requests_remaining) return; - - - - - char * message_ptr = &device_group->message[device_group->message_header_length]; - if (message_type == DGR_MSGTYP_FULL_STATUS) { - - - - building_status_message = true; - - - if (!++outgoing_sequence) outgoing_sequence = 1; -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Building device group %s full status packet"), device_group->group_name); -#endif - device_group->message_length = 0; - SendDeviceGroupMessage(device_group_index, DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_POWER, power); - XdrvMailbox.index = device_group_index << 16; - XdrvMailbox.command_code = DGR_ITEM_STATUS; - XdrvMailbox.topic = (char *)&device_group_index; - XdrvCall(FUNC_DEVICE_GROUP_ITEM); - building_status_message = false; - - - if (!device_group->message_length) { - if (!--outgoing_sequence) outgoing_sequence = -1; - return; - } - device_group->last_full_status_sequence = outgoing_sequence; - - - *(message_ptr + 2) |= DGR_FLAG_FULL_STATUS; - } - - else { -#ifdef USE_DEVICE_GROUPS_SEND - bool use_command; - char oper; - uint32_t old_value; - char * delim_ptr; -#endif - bool shared; - uint8_t item; - uint32_t value; - char * value_ptr; - va_list ap; - -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Building device group %s packet"), device_group->group_name); -#endif - -#ifdef USE_DEVICE_GROUPS_SEND - use_command = (message_type == DGR_MSGTYPE_UPDATE_COMMAND); -#endif - value = 0; - if (message_type == DGR_MSGTYP_UPDATE_MORE_TO_COME) - value |= DGR_FLAG_MORE_TO_COME; - else if (message_type == DGR_MSGTYP_UPDATE_DIRECT) - value |= DGR_FLAG_DIRECT; -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(">sequence=%u, flags=%u"), outgoing_sequence, value); -#endif - message_ptr = BeginDeviceGroupMessage(device_group, value, building_status_message || message_type == DGR_MSGTYP_PARTIAL_UPDATE); - - - - - - if (device_group->message_length) { - uint8_t item_array[32]; - int item_index = 0; - int kept_item_count = 0; - - -#ifdef USE_DEVICE_GROUPS_SEND - if (use_command) - value_ptr = XdrvMailbox.data; - else -#endif - va_start(ap, message_type); -#ifdef USE_DEVICE_GROUPS_SEND - while (use_command ? (item = strtoul(value_ptr, &delim_ptr, 0)) : (item = va_arg(ap, int))) { -#else - while ((item = va_arg(ap, int))) { -#endif - item_array[item_index++] = item; -#ifdef USE_DEVICE_GROUPS_SEND - if (use_command) { - if (!*delim_ptr) break; - if (*delim_ptr == '=') { - delim_ptr = strchr(delim_ptr, ' '); - if (!delim_ptr) break; - } - value_ptr = delim_ptr + 1; - } - else { -#endif - if (item <= DGR_ITEM_MAX_32BIT) - va_arg(ap, int); - else if (item <= DGR_ITEM_MAX_STRING) - va_arg(ap, char *); - else { - switch (item) { - case DGR_ITEM_LIGHT_CHANNELS: - va_arg(ap, uint8_t *); - break; - } - } -#ifdef USE_DEVICE_GROUPS_SEND - } -#endif - } -#ifdef USE_DEVICE_GROUPS_SEND - if (!use_command) va_end(ap); -#else - va_end(ap); -#endif - item_array[item_index] = 0; - - - - char * previous_message_ptr = message_ptr; - while (item = *previous_message_ptr++) { - - - for (item_index = 0; item_array[item_index]; item_index++) { - if (item_array[item_index] == item) break; - } - - - if (item_array[item_index]) { - if (item <= DGR_ITEM_MAX_32BIT) { - previous_message_ptr++; - if (item > DGR_ITEM_MAX_8BIT) { - previous_message_ptr++; - if (item > DGR_ITEM_MAX_16BIT) { - previous_message_ptr++; - previous_message_ptr++; - } - } - } - else if (item <= DGR_ITEM_MAX_STRING) - previous_message_ptr += *previous_message_ptr++; - else { - switch (item) { - case DGR_ITEM_LIGHT_CHANNELS: - previous_message_ptr += 5; - break; - } - } - } - - - else { - *message_ptr++ = item; - if (item <= DGR_ITEM_MAX_32BIT) { - *message_ptr++ = *previous_message_ptr++; - if (item > DGR_ITEM_MAX_8BIT) { - *message_ptr++ = *previous_message_ptr++; - if (item > DGR_ITEM_MAX_16BIT) { - *message_ptr++ = *previous_message_ptr++; - *message_ptr++ = *previous_message_ptr++; - } - } - } - else if (item <= DGR_ITEM_MAX_STRING) { - *message_ptr++ = value = *previous_message_ptr++; - memmove(message_ptr, previous_message_ptr, value); - previous_message_ptr += value; - message_ptr += value; - } - else { - switch (item) { - case DGR_ITEM_LIGHT_CHANNELS: - memmove(message_ptr, previous_message_ptr, 5); - previous_message_ptr += 5; - message_ptr += 5; - break; - } - } - kept_item_count++; - } - } -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%u items carried over from previous update"), kept_item_count); -#endif - } - - -#ifdef USE_DEVICE_GROUPS_SEND - if (use_command) - value_ptr = XdrvMailbox.data; - else -#endif - va_start(ap, message_type); -#ifdef USE_DEVICE_GROUPS_SEND - while (use_command ? (item = strtoul(value_ptr, &delim_ptr, 0)) : (item = va_arg(ap, int))) { -#else - while ((item = va_arg(ap, int))) { -#endif - - - shared = true; - if (!device_group_index) shared = DeviceGroupItemShared(false, item); - if (shared) *message_ptr++ = item; - -#ifdef USE_DEVICE_GROUPS_SEND - - if (use_command) value_ptr = (*delim_ptr == '=' ? delim_ptr + 1 : nullptr); -#endif - - - if (item <= DGR_ITEM_MAX_32BIT) { - -#ifdef USE_DEVICE_GROUPS_SEND - - - if (use_command) { - value = 0; - if (value_ptr) { - oper = 0; - if (*value_ptr == '@') { - oper = value_ptr[1]; - value_ptr += 2; - } - value = strtoul(value_ptr, nullptr, 0); - if (oper) { - old_value = (item <= DGR_ITEM_MAX_8BIT ? device_group->values_8bit[item] : (item <= DGR_ITEM_MAX_16BIT ? device_group->values_16bit[item - DGR_ITEM_MAX_8BIT - 1] : device_group->values_32bit[item - DGR_ITEM_MAX_16BIT - 1])); - value = (oper == '+' ? old_value + value : (oper == '-' ? old_value - value : (oper == '^' ? old_value ^ (value ? value : 0xffffffff) : old_value))); - } - } - } - else -#endif - value = va_arg(ap, int); - - - if (shared) { -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(">item=%u, value=%u"), item, value); -#endif - *message_ptr++ = value & 0xff; - if (item > DGR_ITEM_MAX_8BIT) { -#ifdef USE_DEVICE_GROUPS_SEND - old_value = value; -#endif - value >>= 8; - *message_ptr++ = value & 0xff; - if (item > DGR_ITEM_MAX_16BIT) { - value >>= 8; - *message_ptr++ = value & 0xff; - value >>= 8; - - - if (item == DGR_ITEM_POWER) { - if (!value) - value = (device_group_index == 0 ? devices_present : 1); -#ifdef USE_DEVICE_GROUPS_SEND - else - old_value = old_value & 0xffffff; -#endif - } - - *message_ptr++ = value; -#ifdef USE_DEVICE_GROUPS_SEND - device_group->values_32bit[item - DGR_ITEM_MAX_16BIT - 1] = old_value; -#endif - } -#ifdef USE_DEVICE_GROUPS_SEND - else { - device_group->values_16bit[item - DGR_ITEM_MAX_8BIT - 1] = old_value; - } -#endif - } -#ifdef USE_DEVICE_GROUPS_SEND - else { - device_group->values_8bit[item] = value; - } -#endif - } - } - - - else if (item <= DGR_ITEM_MAX_STRING) { -#ifdef USE_DEVICE_GROUPS_SEND - if (!use_command) -#endif - value_ptr = va_arg(ap, char *); - if (shared) { - value = (value_ptr ? strlen((const char *)value_ptr) : 0); -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(">item=%u, value=%s"), item, value_ptr); -#endif - *message_ptr++ = value; - memcpy(message_ptr, value_ptr, value); - message_ptr += value; - } - } - - - else { - switch (item) { - case DGR_ITEM_LIGHT_CHANNELS: -#ifdef USE_DEVICE_GROUPS_SEND - if (use_command) { - if (shared) { - for (int i = 0; i < 5; i++) { - value = 0; - if (value_ptr) { - value = strtoul(value_ptr, &value_ptr, 0); - value_ptr = (*value_ptr == ',' ? value_ptr + 1 : nullptr); - } - *message_ptr++ = value; - } - } - } - else { -#endif - value_ptr = va_arg(ap, char *); - if (shared) { - memmove(message_ptr, value_ptr, 5); - message_ptr += 5; - } -#ifdef USE_DEVICE_GROUPS_SEND - } -#endif -#ifdef DEVICE_GROUPS_DEBUG - if (shared) AddLog_P2(LOG_LEVEL_DEBUG, PSTR(">item=%u, value=%u,%u,%u,%u,%u"), item, *(message_ptr - 5), *(message_ptr - 4), *(message_ptr - 3), *(message_ptr - 2), *(message_ptr - 1)); -#endif - break; - } - } - -#ifdef USE_DEVICE_GROUPS_SEND - - - if (use_command) { - if (!*delim_ptr) break; - if (*delim_ptr == '=') { - delim_ptr = strchr(delim_ptr, ' '); - if (!delim_ptr) break; - } - value_ptr = delim_ptr + 1; - } -#endif - } -#ifdef USE_DEVICE_GROUPS_SEND - if (!use_command) va_end(ap); -#else - va_end(ap); -#endif - - - *message_ptr++ = 0; - device_group->message_length = message_ptr - device_group->message; - - - if (building_status_message || message_type == DGR_MSGTYP_PARTIAL_UPDATE) return; - } - - -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Sending %u-byte device group %s packet via multicast, sequence=%u"), device_group->message_length, device_group->group_name, device_group->message[device_group->message_header_length] | device_group->message[device_group->message_header_length + 1] << 8); -#endif - SendDeviceGroupPacket(IPAddress(0,0,0,0), device_group->message, device_group->message_length, PSTR("Multicast")); - - uint32_t now = millis(); - if (message_type == DGR_MSGTYP_UPDATE_MORE_TO_COME) { - device_group->message_length = 0; - device_group->next_ack_check_time = 0; - } - else { - device_group->ack_check_interval = 100; - device_group->next_ack_check_time = now + device_group->ack_check_interval; - if (device_group->next_ack_check_time < next_check_time) next_check_time = device_group->next_ack_check_time; - device_group->member_timeout_time = now + DGR_MEMBER_TIMEOUT; - } - - device_group->next_announcement_time = now + DGR_ANNOUNCEMENT_INTERVAL; - if (device_group->next_announcement_time < next_check_time) next_check_time = device_group->next_announcement_time; -} - -void ProcessDeviceGroupMessage(char * packet, int packet_length) -{ - - char * message_group_name = packet + sizeof(DEVICE_GROUP_MESSAGE) - 1; - char * message_ptr = strchr(message_group_name, ' '); - if (message_ptr == nullptr) return; - *message_ptr = 0; - - - struct device_group * device_group; - uint8_t device_group_index = 0; - for (;;) { - device_group = &device_groups[device_group_index]; - if (!strcmp(message_group_name, device_group->group_name)) break; - if (++device_group_index >= device_group_count) return; - } - *message_ptr++ = ' '; - - - IPAddress remote_ip = PortUdp.remoteIP(); - struct device_group_member * * flink = &device_group->device_group_members; - struct device_group_member * device_group_member; - for (;;) { - device_group_member = *flink; - if (!device_group_member) { - device_group_member = (struct device_group_member *)calloc(1, sizeof(struct device_group_member)); - if (device_group_member == nullptr) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: Error allocating device group member block")); - return; - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Adding member %s"), IPAddressToString(remote_ip)); - device_group_member->ip_address = remote_ip; - *flink = device_group_member; - break; - } - else if (device_group_member->ip_address == remote_ip) { - break; - } - flink = &device_group_member->flink; - } - - - message_ptr = strstr_P(message_ptr, PSTR("\n\n")); - if (message_ptr == nullptr) return; - message_ptr += 2; - - bool light_fade; - uint16_t flags; - uint16_t item; - uint16_t message_sequence; - int32_t value; - - - if (packet_length - (message_ptr - packet) < 4) goto badmsg; - message_sequence = *message_ptr++; - message_sequence |= *message_ptr++ << 8; - flags = *message_ptr++; - flags |= *message_ptr++ << 8; -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Received device group %s packet from %s: sequence=%u, flags=%u"), device_group->group_name, IPAddressToString(remote_ip), message_sequence, flags); -#endif - - - if (flags == DGR_FLAG_ANNOUNCEMENT) return; - - - - if (flags == DGR_FLAG_ACK) { - if (message_sequence > device_group_member->acked_sequence || device_group_member->acked_sequence - message_sequence < 64536) { - device_group_member->acked_sequence = message_sequence; - } -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("acked_sequence != device_group->last_full_status_sequence) { - _SendDeviceGroupMessage(device_group_index, DGR_MSGTYP_FULL_STATUS); - } -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("received_sequence) { - if (message_sequence == device_group_member->received_sequence || device_group_member->received_sequence - message_sequence > 64536) { -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("received_sequence = message_sequence; - - - - processing_remote_device_message = true; -# 697 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_device_groups.ino" - XdrvMailbox.command = nullptr; - XdrvMailbox.index = flags | message_sequence << 16; - XdrvMailbox.topic = (char *)&device_group_index; - if (flags & (DGR_FLAG_MORE_TO_COME | DGR_FLAG_DIRECT)) skip_light_fade = true; - - for (;;) { - if (packet_length - (message_ptr - packet) < 1) goto badmsg; - item = *message_ptr++; - if (!item) break; - -#ifdef DEVICE_GROUPS_DEBUG - switch (item) { - case DGR_ITEM_LIGHT_FADE: - case DGR_ITEM_LIGHT_SPEED: - case DGR_ITEM_LIGHT_BRI: - case DGR_ITEM_LIGHT_SCHEME: - case DGR_ITEM_LIGHT_FIXED_COLOR: - case DGR_ITEM_BRI_PRESET_LOW: - case DGR_ITEM_BRI_PRESET_HIGH: - case DGR_ITEM_BRI_POWER_ON: - case DGR_ITEM_POWER: - case DGR_ITEM_LIGHT_CHANNELS: - break; - default: - AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: ********** Invalid item=%u received from device group %s member %s"), item, device_group->group_name, IPAddressToString(remote_ip)); - } -#endif - - XdrvMailbox.command_code = item; - if (item <= DGR_ITEM_LAST_32BIT) { - value = *message_ptr++; - if (item > DGR_ITEM_MAX_8BIT) { - value |= *message_ptr++ << 8; - if (item > DGR_ITEM_MAX_16BIT) { - value |= *message_ptr++ << 16; - value |= *message_ptr++ << 24; -#ifdef USE_DEVICE_GROUPS_SEND - device_group->values_32bit[item - DGR_ITEM_MAX_16BIT - 1] = (item == DGR_ITEM_POWER ? value & 0xffffff : value); -#endif - } -#ifdef USE_DEVICE_GROUPS_SEND - else { - device_group->values_16bit[item - DGR_ITEM_MAX_8BIT - 1] = value; - } -#endif - } -#ifdef USE_DEVICE_GROUPS_SEND - else { - device_group->values_8bit[item] = value; - } -#endif - -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("= packet_length - (message_ptr - packet)) goto badmsg; -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("local) { - uint8_t mask_devices = value >> 24; - if (mask_devices > devices_present) mask_devices = devices_present; - for (uint32_t i = 0; i < devices_present; i++) { - uint32_t mask = 1 << i; - bool on = (value & mask); - if (on != (power & mask)) ExecuteCommandPower(i + 1, (on ? POWER_ON : POWER_OFF), SRC_REMOTE); - } - } - } - XdrvCall(FUNC_DEVICE_GROUP_ITEM); - } - } - - XdrvMailbox.command_code = DGR_ITEM_EOL; - XdrvCall(FUNC_DEVICE_GROUP_ITEM); - skip_light_fade = false; - - processing_remote_device_message = false; -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("device_group_members; device_group_member; device_group_member = device_group_member->flink) { - snprintf(buffer, sizeof(buffer), PSTR("%s,{\"IPAddress\":\"%s\",\"ResendCount\":%u,\"LastRcvdSeq\":%u,\"LastAckedSeq\":%u}"), buffer, IPAddressToString(device_group_member->ip_address), device_group_member->unicast_count, device_group_member->received_sequence, device_group_member->acked_sequence); - member_count++; - } - Response_P(PSTR("{\"" D_CMND_DEVGROUPSTATUS "\":{\"Index\":%u,\"GroupName\":\"%s\",\"MessageSeq\":%u,\"MemberCount\":%d,\"Members\":[%s]}"), device_group_index, device_group->group_name, outgoing_sequence, member_count, &buffer[1]); - } -} - -void DeviceGroupsLoop(void) -{ - if (!Settings.flag4.device_groups_enabled) return; - if (udp_connected) { - uint32_t now = millis(); - - - if (!udp_was_connected) { - udp_was_connected = true; - - if (!device_groups_initialized) DeviceGroupsInit(); - if (device_groups_initialization_failed) return; - - - - next_check_time = now + 3000; - for (uint32_t device_group_index = 0; device_group_index < device_group_count; device_group_index++) { - device_group * device_group = &device_groups[device_group_index]; - device_group->message_length = BeginDeviceGroupMessage(device_group, DGR_FLAG_RESET | DGR_FLAG_STATUS_REQUEST) - device_group->message; - device_group->initial_status_requests_remaining = 10; - device_group->next_ack_check_time = next_check_time; - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: (Re)discovering device groups")); - } - - if (device_groups_initialization_failed) return; - - - if (next_check_time <= now) { -#ifdef DEVICE_GROUPS_DEBUG -AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Ckecking next_check_time=%u, now=%u"), next_check_time, now); -#endif - next_check_time = now + DGR_ANNOUNCEMENT_INTERVAL * 2; - - for (uint32_t device_group_index = 0; device_group_index < device_group_count; device_group_index++) { - device_group * device_group = &device_groups[device_group_index]; - - - if (device_group->next_ack_check_time) { - - - if (device_group->next_ack_check_time <= now) { - - - if (device_group->initial_status_requests_remaining) { - if (--device_group->initial_status_requests_remaining) { -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Sending initial status request for group %s"), device_group->group_name); -#endif - SendDeviceGroupPacket(IPAddress(0,0,0,0), device_group->message, device_group->message_length, PSTR("Initial")); - device_group->message[device_group->message_header_length + 2] = DGR_FLAG_STATUS_REQUEST; - device_group->next_ack_check_time = now + 200; - } - - - - else { - device_group->next_ack_check_time = 0; - _SendDeviceGroupMessage(device_group_index, DGR_MSGTYP_FULL_STATUS); - } - } - - - else { -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Checking for ack's")); -#endif - bool acked = true; - struct device_group_member ** flink = &device_group->device_group_members; - struct device_group_member * device_group_member; - while ((device_group_member = *flink)) { - - - if (device_group_member->acked_sequence != outgoing_sequence) { - - - - if (device_group->member_timeout_time < now) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Removing member %s"), IPAddressToString(device_group_member->ip_address)); - *flink = device_group_member->flink; - free(device_group_member); - } - - - else { -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Sending %u-byte device group %s packet via unicast to %s, sequence %u, last message acked=%u"), device_group->message_length, device_group->group_name, IPAddressToString(device_group_member->ip_address), outgoing_sequence, device_group_member->acked_sequence); -#endif - SendDeviceGroupPacket(device_group_member->ip_address, device_group->message, device_group->message_length, PSTR("Unicast")); - device_group_member->unicast_count++; - acked = false; - flink = &device_group_member->flink; - } - } - else { - flink = &device_group_member->flink; - } - } - - - - if (acked) { - device_group->next_ack_check_time = 0; - device_group->message_length = 0; - } - - - - - else { - device_group->ack_check_interval *= 2; - if (device_group->ack_check_interval > 5000) device_group->ack_check_interval = 5000; - device_group->next_ack_check_time = now + device_group->ack_check_interval; - } - } - } - - if (device_group->next_ack_check_time < next_check_time) next_check_time = device_group->next_ack_check_time; - } - - - - - - -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: next_announcement_time=%u, now=%u"), device_group->next_announcement_time, now); -#endif - if (device_group->next_announcement_time <= now) { -#ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Sending device group %s announcement"), device_group->group_name); -#endif - SendDeviceGroupPacket(IPAddress(0,0,0,0), device_group->message, BeginDeviceGroupMessage(device_group, DGR_FLAG_ANNOUNCEMENT, true) - device_group->message, PSTR("Announcement")); - device_group->next_announcement_time = now + DGR_ANNOUNCEMENT_INTERVAL + random(10000); - } - if (device_group->next_announcement_time < next_check_time) next_check_time = device_group->next_announcement_time; - } - } - } - else { - udp_was_connected = false; - } -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_esp32.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_esp32.ino" -#ifdef ESP32 - -#include -#include - -void SettingsErase(uint8_t type) -{ - if (1 == type) - { - } - else if (2 == type) - { - } - else if (3 == type) - { - } - - noInterrupts(); - nvs_handle handle; - nvs_open("main", NVS_READWRITE, &handle); - nvs_erase_all(handle); - nvs_commit(handle); - nvs_close(handle); - interrupts(); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " t=%d"), type); -} - -void SettingsLoad(const char *sNvsName, const char *sName, void *pSettings, unsigned nSettingsLen) -{ - noInterrupts(); - nvs_handle handle; - size_t size; - nvs_open(sNvsName, NVS_READONLY, &handle); - size = nSettingsLen; - nvs_get_blob(handle, sName, pSettings, &size); - nvs_close(handle); - interrupts(); -} - -void SettingsSave(const char *sNvsName, const char *sName, const void *pSettings, unsigned nSettingsLen) -{ - nvs_handle handle; - noInterrupts(); - nvs_open(sNvsName, NVS_READWRITE, &handle); - nvs_set_blob(handle, sName, pSettings, nSettingsLen); - nvs_commit(handle); - nvs_close(handle); - interrupts(); -} - -void ESP32_flashRead(uint32_t offset, uint32_t *data, size_t size) -{ - SettingsLoad("main", "Settings", data, size); -} - -void ESP32_flashReadHeader(uint32_t offset, uint32_t *data, size_t size) -{ - SettingsLoad("main", "SettingsH", data, size); -} - -void SettingsSaveMain(const void *pSettings, unsigned nSettingsLen) -{ - SettingsSave("main", "Settings", pSettings, nSettingsLen); -} -# 98 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_esp32.ino" -void SettingsLoadUpg(void *pSettings, unsigned nSettingsLen) -{ - SettingsLoad("upg", "Settings", pSettings, nSettingsLen); -} - -void SettingsLoadUpgH(void *pSettings, unsigned nSettingsLen) -{ - SettingsLoad("upg", "SettingsH", pSettings, nSettingsLen); -} - - - - -static bool bNetIsTimeSync = false; - -void SntpInit() -{ - bNetIsTimeSync = true; -} - -uint32_t SntpGetCurrentTimestamp(void) -{ - time_t now = 0; - if (bNetIsTimeSync || ntp_force_sync) - { - - - configTime(0, 0, SettingsText(SET_NTPSERVER1), SettingsText(SET_NTPSERVER2), SettingsText(SET_NTPSERVER3)); - bNetIsTimeSync = false; - ntp_force_sync = false; - } - time(&now); - return now; -} - - - - - -void CrashDump(void) -{ -} - -bool CrashFlag(void) -{ - return false; -} - -void CrashDumpClear(void) -{ -} -void CmndCrash(void) -{ - - - - -} - - -void CmndWDT(void) -{ - - - - - - -} - -void CmndBlockedLoop(void) -{ - - - - - -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_esptool.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_esptool.ino" -#ifdef ESP8266 -#define USE_ESPTOOL -#endif - -#ifdef USE_ESPTOOL - - - - - - - -#define READ_REG(REG) (*((volatile uint32_t *)(REG))) -#define WRITE_REG(REG,VAL) *((volatile uint32_t *)(REG)) = (VAL) -#define REG_SET_MASK(reg,mask) WRITE_REG((reg), (READ_REG(reg)|(mask))) - -#define SPI_BASE_REG 0x60000200 - -#define SPI_CMD_REG (SPI_BASE_REG + 0x00) -#define SPI_FLASH_RDSR (1<<27) -#define SPI_FLASH_SE (1<<24) -#define SPI_FLASH_BE (1<<23) -#define SPI_FLASH_WREN (1<<30) - -#define SPI_ADDR_REG (SPI_BASE_REG + 0x04) -#define SPI_CTRL_REG (SPI_BASE_REG + 0x08) -#define SPI_RD_STATUS_REG (SPI_BASE_REG + 0x10) -#define SPI_W0_REG (SPI_BASE_REG + 0x40) -#define SPI_EXT2_REG (SPI_BASE_REG + 0xF8) - -#define SPI_ST 0x7 - - -#define SECTORS_PER_BLOCK (FLASH_BLOCK_SIZE / SPI_FLASH_SEC_SIZE) - - -static const uint32_t STATUS_WIP_BIT = (1 << 0); - - -inline static void spi_wait_ready(void) -{ - while((READ_REG(SPI_EXT2_REG) & SPI_ST)) { } -} - - - -static bool spiflash_is_ready(void) -{ - spi_wait_ready(); - WRITE_REG(SPI_RD_STATUS_REG, 0); - WRITE_REG(SPI_CMD_REG, SPI_FLASH_RDSR); - while(READ_REG(SPI_CMD_REG) != 0) { } - uint32_t status_value = READ_REG(SPI_RD_STATUS_REG); - return (status_value & STATUS_WIP_BIT) == 0; -} - -static void spi_write_enable(void) -{ - while(!spiflash_is_ready()) { } - WRITE_REG(SPI_CMD_REG, SPI_FLASH_WREN); - while(READ_REG(SPI_CMD_REG) != 0) { } -} - -bool EsptoolEraseSector(uint32_t sector) -{ - spi_write_enable(); - spi_wait_ready(); - - WRITE_REG(SPI_ADDR_REG, (sector * SPI_FLASH_SEC_SIZE) & 0xffffff); - WRITE_REG(SPI_CMD_REG, SPI_FLASH_SE); - while(READ_REG(SPI_CMD_REG) != 0) { } - while(!spiflash_is_ready()) { } - - return true; -} - -void EsptoolErase(uint32_t start_sector, uint32_t end_sector) -{ - int next_erase_sector = start_sector; - int remaining_erase_sector = end_sector - start_sector; - - while (remaining_erase_sector > 0) { - spi_write_enable(); - - uint32_t command = SPI_FLASH_SE; - uint32_t sectors_to_erase = 1; - if (remaining_erase_sector >= SECTORS_PER_BLOCK && - next_erase_sector % SECTORS_PER_BLOCK == 0) { - command = SPI_FLASH_BE; - sectors_to_erase = SECTORS_PER_BLOCK; - } - uint32_t addr = next_erase_sector * SPI_FLASH_SEC_SIZE; - - spi_wait_ready(); - WRITE_REG(SPI_ADDR_REG, addr & 0xffffff); - WRITE_REG(SPI_CMD_REG, command); - while(READ_REG(SPI_CMD_REG) != 0) { } - remaining_erase_sector -= sectors_to_erase; - next_erase_sector += sectors_to_erase; - - while (!spiflash_is_ready()) { } - yield(); - OsWatchLoop(); - } -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_features.ino" -# 24 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_features.ino" -void GetFeatures(void) -{ - feature_drv1 = 0x00000000; - -#ifdef USE_ENERGY_MARGIN_DETECTION - feature_drv1 |= 0x00000001; -#endif -#ifdef USE_LIGHT - feature_drv1 |= 0x00000002; -#endif -#ifdef USE_I2C - feature_drv1 |= 0x00000004; -#endif -#ifdef USE_SPI - feature_drv1 |= 0x00000008; -#endif -#ifdef USE_DISCOVERY - feature_drv1 |= 0x00000010; -#endif -#ifdef USE_ARDUINO_OTA - feature_drv1 |= 0x00000020; -#endif -#ifdef USE_MQTT_TLS - feature_drv1 |= 0x00000040; -#endif -#ifdef USE_WEBSERVER - feature_drv1 |= 0x00000080; -#endif -#ifdef WEBSERVER_ADVERTISE - feature_drv1 |= 0x00000100; -#endif -#ifdef USE_EMULATION_HUE - feature_drv1 |= 0x00000200; -#endif - - feature_drv1 |= 0x00000400; - - - - - - - -#ifdef MQTT_HOST_DISCOVERY - feature_drv1 |= 0x00002000; -#endif -#ifdef USE_ARILUX_RF - feature_drv1 |= 0x00004000; -#endif -#if defined(USE_LIGHT) && defined(USE_WS2812) - feature_drv1 |= 0x00008000; -#endif -#ifdef USE_WS2812_DMA - feature_drv1 |= 0x00010000; -#endif -#if defined(USE_IR_REMOTE) || defined(USE_IR_REMOTE_FULL) - feature_drv1 |= 0x00020000; -#endif -#ifdef USE_IR_HVAC - feature_drv1 |= 0x00040000; -#endif -#ifdef USE_IR_RECEIVE - feature_drv1 |= 0x00080000; -#endif -#ifdef USE_DOMOTICZ - feature_drv1 |= 0x00100000; -#endif -#ifdef USE_DISPLAY - feature_drv1 |= 0x00200000; -#endif -#ifdef USE_HOME_ASSISTANT - feature_drv1 |= 0x00400000; -#endif -#ifdef USE_SERIAL_BRIDGE - feature_drv1 |= 0x00800000; -#endif -#ifdef USE_TIMERS - feature_drv1 |= 0x01000000; -#endif -#ifdef USE_SUNRISE - feature_drv1 |= 0x02000000; -#endif -#ifdef USE_TIMERS_WEB - feature_drv1 |= 0x04000000; -#endif -#ifdef USE_RULES - feature_drv1 |= 0x08000000; -#endif -#ifdef USE_KNX - feature_drv1 |= 0x10000000; -#endif -#ifdef USE_WPS - feature_drv1 |= 0x20000000; -#endif -#ifdef USE_SMARTCONFIG - feature_drv1 |= 0x40000000; -#endif -#ifdef USE_ENERGY_POWER_LIMIT - feature_drv1 |= 0x80000000; -#endif - - - - feature_drv2 = 0x00000000; - -#ifdef USE_CONFIG_OVERRIDE - feature_drv2 |= 0x00000001; -#endif -#ifdef FIRMWARE_MINIMAL - feature_drv2 |= 0x00000002; -#endif -#ifdef FIRMWARE_SENSORS - feature_drv2 |= 0x00000004; -#endif -#ifdef FIRMWARE_CLASSIC - feature_drv2 |= 0x00000008; -#endif -#ifdef FIRMWARE_KNX_NO_EMULATION - feature_drv2 |= 0x00000010; -#endif -#ifdef USE_DISPLAY_MODES1TO5 - feature_drv2 |= 0x00000020; -#endif -#ifdef USE_DISPLAY_GRAPH - feature_drv2 |= 0x00000040; -#endif -#ifdef USE_DISPLAY_LCD - feature_drv2 |= 0x00000080; -#endif -#ifdef USE_DISPLAY_SSD1306 - feature_drv2 |= 0x00000100; -#endif -#ifdef USE_DISPLAY_MATRIX - feature_drv2 |= 0x00000200; -#endif -#ifdef USE_DISPLAY_ILI9341 - feature_drv2 |= 0x00000400; -#endif -#ifdef USE_DISPLAY_EPAPER_29 - feature_drv2 |= 0x00000800; -#endif -#ifdef USE_DISPLAY_SH1106 - feature_drv2 |= 0x00001000; -#endif -#ifdef USE_MP3_PLAYER - feature_drv2 |= 0x00002000; -#endif -#ifdef USE_PCA9685 - feature_drv2 |= 0x00004000; -#endif -#if defined(USE_LIGHT) && defined(USE_TUYA_MCU) - feature_drv2 |= 0x00008000; -#endif -#ifdef USE_RC_SWITCH - feature_drv2 |= 0x00010000; -#endif -#if defined(USE_LIGHT) && defined(USE_ARMTRONIX_DIMMERS) - feature_drv2 |= 0x00020000; -#endif -#if defined(USE_LIGHT) && defined(USE_SM16716) - feature_drv2 |= 0x00040000; -#endif -#ifdef USE_SCRIPT - feature_drv2 |= 0x00080000; -#endif -#ifdef USE_EMULATION_WEMO - feature_drv2 |= 0x00100000; -#endif -#ifdef USE_SONOFF_IFAN - feature_drv2 |= 0x00200000; -#endif -#ifdef USE_ZIGBEE - feature_drv2 |= 0x00400000; -#endif -#ifdef NO_EXTRA_4K_HEAP - feature_drv2 |= 0x00800000; -#endif -#ifdef VTABLES_IN_IRAM - feature_drv2 |= 0x01000000; -#endif -#ifdef VTABLES_IN_DRAM - feature_drv2 |= 0x02000000; -#endif -#ifdef VTABLES_IN_FLASH - feature_drv2 |= 0x04000000; -#endif -#ifdef PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH - feature_drv2 |= 0x08000000; -#endif -#ifdef PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY - feature_drv2 |= 0x10000000; -#endif -#ifdef PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH - feature_drv2 |= 0x20000000; -#endif -#ifdef DEBUG_THEO - feature_drv2 |= 0x40000000; -#endif -#ifdef USE_DEBUG_DRIVER - feature_drv2 |= 0x80000000; -#endif - - - - feature_sns1 = 0x00000000; - -#ifdef USE_COUNTER - feature_sns1 |= 0x00000001; -#endif -#ifdef USE_ADC_VCC - feature_sns1 |= 0x00000002; -#endif -#ifdef USE_ENERGY_SENSOR - feature_sns1 |= 0x00000004; -#endif -#ifdef USE_PZEM004T - feature_sns1 |= 0x00000008; -#endif -#ifdef USE_DS18B20 - feature_sns1 |= 0x00000010; -#endif -#ifdef USE_DS18x20_LEGACY - feature_sns1 |= 0x00000020; -#endif -#ifdef USE_DS18x20 - feature_sns1 |= 0x00000040; -#endif -#ifdef USE_DHT - feature_sns1 |= 0x00000080; -#endif -#ifdef USE_SHT - feature_sns1 |= 0x00000100; -#endif -#ifdef USE_HTU - feature_sns1 |= 0x00000200; -#endif -#ifdef USE_BMP - feature_sns1 |= 0x00000400; -#endif -#ifdef USE_BME680 - feature_sns1 |= 0x00000800; -#endif -#ifdef USE_BH1750 - feature_sns1 |= 0x00001000; -#endif -#ifdef USE_VEML6070 - feature_sns1 |= 0x00002000; -#endif -#ifdef USE_ADS1115_I2CDEV - feature_sns1 |= 0x00004000; -#endif -#ifdef USE_ADS1115 - feature_sns1 |= 0x00008000; -#endif -#ifdef USE_INA219 - feature_sns1 |= 0x00010000; -#endif -#ifdef USE_SHT3X - feature_sns1 |= 0x00020000; -#endif -#ifdef USE_MHZ19 - feature_sns1 |= 0x00040000; -#endif -#ifdef USE_TSL2561 - feature_sns1 |= 0x00080000; -#endif -#ifdef USE_SENSEAIR - feature_sns1 |= 0x00100000; -#endif -#ifdef USE_PMS5003 - feature_sns1 |= 0x00200000; -#endif -#ifdef USE_MGS - feature_sns1 |= 0x00400000; -#endif -#ifdef USE_NOVA_SDS - feature_sns1 |= 0x00800000; -#endif -#ifdef USE_SGP30 - feature_sns1 |= 0x01000000; -#endif -#ifdef USE_SR04 - feature_sns1 |= 0x02000000; -#endif -#ifdef USE_SDM120 - feature_sns1 |= 0x04000000; -#endif -#ifdef USE_SI1145 - feature_sns1 |= 0x08000000; -#endif -#ifdef USE_SDM630 - feature_sns1 |= 0x10000000; -#endif -#ifdef USE_LM75AD - feature_sns1 |= 0x20000000; -#endif -#ifdef USE_APDS9960 - feature_sns1 |= 0x40000000; -#endif -#ifdef USE_TM1638 - feature_sns1 |= 0x80000000; -#endif - - - - feature_sns2 = 0x00000000; - -#ifdef USE_MCP230xx - feature_sns2 |= 0x00000001; -#endif -#ifdef USE_MPR121 - feature_sns2 |= 0x00000002; -#endif -#ifdef USE_CCS811 - feature_sns2 |= 0x00000004; -#endif -#ifdef USE_MPU6050 - feature_sns2 |= 0x00000008; -#endif -#ifdef USE_MCP230xx_OUTPUT - feature_sns2 |= 0x00000010; -#endif -#ifdef USE_MCP230xx_DISPLAYOUTPUT - feature_sns2 |= 0x00000020; -#endif -#ifdef USE_HLW8012 - feature_sns2 |= 0x00000040; -#endif -#ifdef USE_CSE7766 - feature_sns2 |= 0x00000080; -#endif -#ifdef USE_MCP39F501 - feature_sns2 |= 0x00000100; -#endif -#ifdef USE_PZEM_AC - feature_sns2 |= 0x00000200; -#endif -#ifdef USE_DS3231 - feature_sns2 |= 0x00000400; -#endif -#ifdef USE_HX711 - feature_sns2 |= 0x00000800; -#endif -#ifdef USE_PZEM_DC - feature_sns2 |= 0x00001000; -#endif -#if defined(USE_TX20_WIND_SENSOR) || defined(USE_TX23_WIND_SENSOR) - feature_sns2 |= 0x00002000; -#endif -#ifdef USE_MGC3130 - feature_sns2 |= 0x00004000; -#endif -#ifdef USE_RF_SENSOR - feature_sns2 |= 0x00008000; -#endif -#ifdef USE_THEO_V2 - feature_sns2 |= 0x00010000; -#endif -#ifdef USE_ALECTO_V2 - feature_sns2 |= 0x00020000; -#endif -#ifdef USE_AZ7798 - feature_sns2 |= 0x00040000; -#endif -#ifdef USE_MAX31855 - feature_sns2 |= 0x00080000; -#endif -#ifdef USE_PN532_HSU - feature_sns2 |= 0x00100000; -#endif -#ifdef USE_MAX44009 - feature_sns2 |= 0x00200000; -#endif -#ifdef USE_SCD30 - feature_sns2 |= 0x00400000; -#endif -#ifdef USE_HRE - feature_sns2 |= 0x00800000; -#endif -#ifdef USE_ADE7953 - feature_sns2 |= 0x01000000; -#endif -#ifdef USE_SPS30 - feature_sns2 |= 0x02000000; -#endif -#ifdef USE_VL53L0X - feature_sns2 |= 0x04000000; -#endif -#ifdef USE_MLX90614 - feature_sns2 |= 0x08000000; -#endif -#ifdef USE_MAX31865 - feature_sns2 |= 0x10000000; -#endif -#ifdef USE_CHIRP - feature_sns2 |= 0x20000000; -#endif -#ifdef USE_SOLAX_X1 - feature_sns2 |= 0x40000000; -#endif -#ifdef USE_PAJ7620 - feature_sns2 |= 0x80000000; -#endif - - - - feature5 = 0x00000000; - -#ifdef USE_BUZZER - feature5 |= 0x00000001; -#endif -#ifdef USE_RDM6300 - feature5 |= 0x00000002; -#endif -#ifdef USE_IBEACON - feature5 |= 0x00000004; -#endif -#ifdef USE_SML_M - feature5 |= 0x00000008; -#endif -#ifdef USE_INA226 - feature5 |= 0x00000010; -#endif -#ifdef USE_A4988_STEPPER - feature5 |= 0x00000020; -#endif -#ifdef USE_DDS2382 - feature5 |= 0x00000040; -#endif -#ifdef USE_SM2135 - feature5 |= 0x00000080; -#endif -#ifdef USE_SHUTTER - feature5 |= 0x00000100; -#endif -#ifdef USE_PCF8574 - feature5 |= 0x00000200; -#endif -#ifdef USE_DDSU666 - feature5 |= 0x00000400; -#endif -#ifdef USE_DEEPSLEEP - feature5 |= 0x00000800; -#endif -#ifdef USE_SONOFF_SC - feature5 |= 0x00001000; -#endif -#ifdef USE_SONOFF_RF - feature5 |= 0x00002000; -#endif -#ifdef USE_SONOFF_L1 - feature5 |= 0x00004000; -#endif -#ifdef USE_EXS_DIMMER - feature5 |= 0x00008000; -#endif -#ifdef USE_ARDUINO_SLAVE - feature5 |= 0x00010000; -#endif -#ifdef USE_HIH6 - feature5 |= 0x00020000; -#endif -#ifdef USE_HPMA - feature5 |= 0x00040000; -#endif -#ifdef USE_TSL2591 - feature5 |= 0x00080000; -#endif -#ifdef USE_DHT12 - feature5 |= 0x00100000; -#endif -#ifdef USE_DS1624 - feature5 |= 0x00200000; -#endif -#ifdef USE_GPS - feature5 |= 0x00400000; -#endif -#ifdef USE_HOTPLUG - feature5 |= 0x00800000; -#endif -#ifdef USE_NRF24 - feature5 |= 0x01000000; -#endif -#ifdef USE_MIBLE - feature5 |= 0x02000000; -#endif -#ifdef USE_HM10 - feature5 |= 0x04000000; -#endif -#ifdef USE_LE01MR - feature5 |= 0x08000000; -#endif -#ifdef USE_AHT1x - feature5 |= 0x10000000; -#endif -#ifdef USE_WEMOS_MOTOR_V1 - feature5 |= 0x20000000; -#endif -#ifdef USE_DEVICE_GROUPS - feature5 |= 0x40000000; -#endif -#ifdef USE_PWM_DIMMER - feature5 |= 0x80000000; -#endif - - - - feature6 = 0x00000000; - -#ifdef USE_KEELOQ - feature6 |= 0x00000001; -#endif -#ifdef USE_HRXL - feature6 |= 0x00000002; -#endif -#ifdef USE_SONOFF_D1 - feature6 |= 0x00000004; -#endif -#ifdef USE_HDC1080 - feature6 |= 0x00000008; -#endif -#ifdef USE_IAQ - feature6 |= 0x00000010; -#endif -#ifdef USE_DISPLAY_SEVENSEG - feature6 |= 0x00000020; -#endif -#ifdef USE_AS3935 - feature6 |= 0x00000040; -#endif -#ifdef USE_PING - feature6 |= 0x00000080; -#endif -#ifdef USE_THERMOSTAT - feature6 |= 0x00000100; -#endif -# 589 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_features.ino" -} -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_flash_log.ino" -# 39 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_flash_log.ino" -#ifdef USE_FLOG -#ifdef ESP8266 - -class FLOG - -#define MAGIC_WORD_FL 0x464c - -{ - -struct header_t{ - uint16_t magic_word; - uint16_t padding; - uint32_t physical_start_sector:10; - uint32_t number:10; - uint32_t buf_pointer:12; - }; - -private: -void _readSector(uint8_t one_sector); -void _eraseSector(uint8_t one_sector); -void _writeSector(uint8_t one_sector); -void _clearBuffer(void); -void _searchSaves(void); -void _findFirstErasedSector(void); -void _showBuffer(void); -void _initBuffer(void); -void _saveBufferToSector(void); -header_t _saved_header; - -public: - uint32_t size; - uint32_t start; - uint32_t end; - uint16_t num_sectors; - - uint16_t first_erased_sector; - uint16_t current_sector; - - uint16_t bytes_left; - uint16_t sectors_left; - - uint8_t mode = 0; - bool found_saved_data = false; - bool ready = false; - bool running_download = false; - bool recording = false; - - union sector_t{ - uint32_t dword_buffer[FLASH_SECTOR_SIZE/4]; - uint8_t byte_buffer[FLASH_SECTOR_SIZE]; - header_t header; - } sector; - - void init(void); - void addToBuffer(uint8_t src[], uint32_t size); - void startRecording(bool append); - void stopRecording(void); - - typedef void (*CallbackNoArgs) (); - typedef void (*CallbackWithArgs) (uint8_t *_record); - - void startDownload(size_t size, CallbackNoArgs sendHeader, CallbackWithArgs sendRecord, CallbackNoArgs sendFooter); -}; - -extern "C" uint32_t _SPIFFS_start; -extern "C" uint32_t _FS_start; - - - - -void FLOG::init(void) -{ -DEBUG_SENSOR_LOG(PSTR("FLOG: init ...")); -size = ESP.getSketchSize(); - -start = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); -#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) || defined(ARDUINO_ESP8266_RELEASE_2_5_0) || defined(ARDUINO_ESP8266_RELEASE_2_5_1) || defined(ARDUINO_ESP8266_RELEASE_2_5_2) -end = (uint32_t)&_SPIFFS_start - 0x40200000; -#else -end = (uint32_t)&_FS_start - 0x40200000; -#endif -num_sectors = (end - start)/FLASH_SECTOR_SIZE; -DEBUG_SENSOR_LOG(PSTR("FLOG: size: 0x%lx, start: 0x%lx, end: 0x%lx, num_sectors(dec): %lu"), size, start, end, num_sectors ); -_findFirstErasedSector(); -if(first_erased_sector == 0xffff){ - _eraseSector(0); - first_erased_sector = 0; -} -_searchSaves(); -_initBuffer(); -ready = true; -} -# 143 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_flash_log.ino" -void FLOG::_readSector(uint8_t one_sector){ - DEBUG_SENSOR_LOG(PSTR("FLOG: read sector number: %u" ), one_sector); - ESP.flashRead(start+(one_sector * FLASH_SECTOR_SIZE),(uint32_t *)§or.dword_buffer, FLASH_SECTOR_SIZE); -} - - - - - -void FLOG::_eraseSector(uint8_t one_sector){ - DEBUG_SENSOR_LOG(PSTR("FLOG: erasing sector number: %u" ), one_sector); - ESP.flashEraseSector((start/FLASH_SECTOR_SIZE)+one_sector); -} - - - - - -void FLOG::_writeSector(uint8_t one_sector){ - DEBUG_SENSOR_LOG(PSTR("FLOG: write buffer to sector number: %u" ), one_sector); - ESP.flashWrite(start+(one_sector * FLASH_SECTOR_SIZE),(uint32_t *)§or.dword_buffer, FLASH_SECTOR_SIZE); -} - - - - -void FLOG::_clearBuffer(){ - for (uint32_t i = sizeof(sector.header)/4; i<(sizeof(sector.dword_buffer)/4); i++){ - sector.dword_buffer[i] = 0; - } - sector.header.buf_pointer = sizeof(sector.header); - -} - - - - -void FLOG::_saveBufferToSector(){ - DEBUG_SENSOR_LOG(PSTR("FLOG: write buffer to current sector: %u" ),current_sector); - _writeSector(current_sector); - if(current_sector == num_sectors){ - current_sector = 0; - } - else{ - current_sector++; - } - _eraseSector(current_sector); - _clearBuffer(); - sector.header.number++; - DEBUG_SENSOR_LOG(PSTR("FLOG: new sector header number: %u" ),sector.header.number); -} - - - - - -void FLOG::_findFirstErasedSector(){ - for (uint32_t i = 0; i3){ - break; - } - } -} - - - - - - - -void FLOG::addToBuffer(uint8_t src[], uint32_t size){ - if(mode == 0){ - if(sector.header.number == num_sectors && !ready){ - return; - } - } - if((FLASH_SECTOR_SIZE-sector.header.buf_pointer-sizeof(sector.header))>size){ - - - - memcpy(sector.byte_buffer + sector.header.buf_pointer, src, size); - sector.header.buf_pointer+=size; - - } - else{ - DEBUG_SENSOR_LOG(PSTR("FLOG: save buffer to flash sector: %u"), current_sector); - DEBUG_SENSOR_LOG(PSTR("FLOG: current buf_pointer: %u"), sector.header.buf_pointer); - _saveBufferToSector(); - sectors_left++; - - if((FLASH_SECTOR_SIZE-sector.header.buf_pointer-sizeof(sector.header))>size){ - memcpy(sector.byte_buffer + sector.header.buf_pointer, src, size); - sector.header.buf_pointer+=size; - } - } -} - - - - - - -void FLOG::startRecording(bool append){ - if(recording){ - DEBUG_SENSOR_LOG(PSTR("FLOG: already recording")); - return; - } - recording = true; - DEBUG_SENSOR_LOG(PSTR("FLOG: start recording")); - _initBuffer(); - if(!found_saved_data) { - append = false; - } - if(append){ - sector.header.number = _saved_header.number+1; - sector.header.physical_start_sector = _saved_header.physical_start_sector; - } - else{ - sector.header.physical_start_sector = (uint16_t)first_erased_sector; - found_saved_data = false; - sectors_left = 0; - } - } - - - - - -void FLOG::stopRecording(void){ - _saveBufferToSector(); - _findFirstErasedSector(); - _searchSaves(); - _initBuffer(); - recording = false; - found_saved_data = true; - } -# 382 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_flash_log.ino" - void FLOG::startDownload(size_t size, CallbackNoArgs sendHeader, CallbackWithArgs sendRecord, CallbackNoArgs sendFooter){ - - _readSector(sector.header.physical_start_sector); - uint32_t next_sector = sector.header.physical_start_sector; - bytes_left = sector.header.buf_pointer - sizeof(sector.header); - DEBUG_SENSOR_LOG(PSTR("FLOG: create file for download, will process %u bytes"), bytes_left); - running_download = true; - - sendHeader(); - - while(sectors_left){ - DEBUG_SENSOR_LOG(PSTR("FLOG: next sector: %u"), next_sector); - - uint32_t k = sizeof(sector.header); - while(bytes_left){ - - uint8_t *_record_start = (uint8_t*)§or.byte_buffer[k]; - - sendRecord(_record_start); - if(k%128 == 0){ - - OsWatchLoop(); - delay(ssleep); - } - k+=size; - if(bytes_left>7){ - bytes_left-=size; - } - else{ - bytes_left = 0; - DEBUG_SENSOR_LOG(PSTR("FLOG: Flog->bytes_left not dividable by 8 ??????")); - } - } - next_sector++; - if(next_sector>num_sectors){ - next_sector = 0; - } - sectors_left--; - _readSector(next_sector); - bytes_left = sector.header.buf_pointer - sizeof(sector.header); - OsWatchLoop(); - delay(ssleep); - } - running_download = false; - - sendFooter(); - - _searchSaves(); - _initBuffer(); - } - - #endif - #endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_float.ino" -# 23 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_float.ino" -float fmodf(float x, float y) -{ - - union {float f; uint32_t i;} ux = {x}, uy = {y}; - int ex = ux.i>>23 & 0xff; - int ey = uy.i>>23 & 0xff; - uint32_t sx = ux.i & 0x80000000; - uint32_t i; - uint32_t uxi = ux.i; - - if (uy.i<<1 == 0 || isnan(y) || ex == 0xff) - return (x*y)/(x*y); - if (uxi<<1 <= uy.i<<1) { - if (uxi<<1 == uy.i<<1) - return 0*x; - return x; - } - - - if (!ex) { - for (i = uxi<<9; i>>31 == 0; ex--, i <<= 1); - uxi <<= -ex + 1; - } else { - uxi &= -1U >> 9; - uxi |= 1U << 23; - } - if (!ey) { - for (i = uy.i<<9; i>>31 == 0; ey--, i <<= 1); - uy.i <<= -ey + 1; - } else { - uy.i &= -1U >> 9; - uy.i |= 1U << 23; - } - - - for (; ex > ey; ex--) { - i = uxi - uy.i; - if (i >> 31 == 0) { - if (i == 0) - return 0*x; - uxi = i; - } - uxi <<= 1; - } - i = uxi - uy.i; - if (i >> 31 == 0) { - if (i == 0) - return 0*x; - uxi = i; - } - for (; uxi>>23 == 0; uxi <<= 1, ex--); - - - if (ex > 0) { - uxi -= 1U << 23; - uxi |= (uint32_t)ex << 23; - } else { - uxi >>= -ex + 1; - } - uxi |= sx; - ux.i = uxi; - return ux.f; -} - - -double FastPrecisePow(double a, double b) -{ - - - int e = abs((int)b); - union { - double d; - int x[2]; - } u = { a }; - u.x[1] = (int)((b - e) * (u.x[1] - 1072632447) + 1072632447); - u.x[0] = 0; - - - double r = 1.0; - while (e) { - if (e & 1) { - r *= a; - } - a *= a; - e >>= 1; - } - return r * u.d; -} - -float FastPrecisePowf(const float x, const float y) -{ - - return (float)FastPrecisePow(x, y); -} - -double TaylorLog(double x) -{ - - - if (x <= 0.0) { return NAN; } - double z = (x + 1) / (x - 1); - double step = ((x - 1) * (x - 1)) / ((x + 1) * (x + 1)); - double totalValue = 0; - double powe = 1; - for (uint32_t count = 0; count < 10; count++) { - z *= step; - double y = (1 / powe) * z; - totalValue = totalValue + y; - powe = powe + 2; - } - totalValue *= 2; -# 144 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_float.ino" - return totalValue; -} -# 154 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_float.ino" -inline float sinf(float x) { return sin_52(x); } -inline float cosf(float x) { return cos_52(x); } -inline float tanf(float x) { return tan_56(x); } -inline float atanf(float x) { return atan_66(x); } -inline float asinf(float x) { return asinf1(x); } -inline float acosf(float x) { return acosf1(x); } -inline float sqrtf(float x) { return sqrt1(x); } -inline float powf(float x, float y) { return FastPrecisePow(x, y); } - - -double const f_pi = 3.1415926535897932384626433; -double const f_twopi = 2.0 * f_pi; -double const f_two_over_pi = 2.0 / f_pi; -double const f_halfpi = f_pi / 2.0; -double const f_threehalfpi = 3.0 * f_pi / 2.0; -double const f_four_over_pi = 4.0 / f_pi; -double const f_qtrpi = f_pi / 4.0; -double const f_sixthpi = f_pi / 6.0; -double const f_tansixthpi = tan(f_sixthpi); -double const f_twelfthpi = f_pi / 12.0; -double const f_tantwelfthpi = tan(f_twelfthpi); -float const f_180pi = 180 / f_pi; -# 194 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_float.ino" -float cos_52s(float x) -{ - const float c1 = 0.9999932946; - const float c2 = -0.4999124376; - const float c3 = 0.0414877472; - const float c4 = -0.0012712095; - - float x2 = x * x; - return (c1 + x2 * (c2 + x2 * (c3 + c4 * x2))); -} - - - - - - -float cos_52(float x) -{ - x = fmodf(x, f_twopi); - if (x < 0) { x = -x; } - int quad = int(x * (float)f_two_over_pi); - switch (quad) { - case 0: return cos_52s(x); - case 1: return -cos_52s((float)f_pi - x); - case 2: return -cos_52s(x-(float)f_pi); - case 3: return cos_52s((float)f_twopi - x); - } -} - - - - -float sin_52(float x) -{ - return cos_52((float)f_halfpi - x); -} -# 247 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_float.ino" -float tan_56s(float x) -{ - const float c1 = -3.16783027; - const float c2 = 0.134516124; - const float c3 = -4.033321984; - - float x2 = x * x; - return (x * (c1 + c2 * x2) / (c3 + x2)); -} -# 267 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_float.ino" -float tan_56(float x) -{ - x = fmodf(x, (float)f_twopi); - int octant = int(x * (float)f_four_over_pi); - switch (octant){ - case 0: return tan_56s(x * (float)f_four_over_pi); - case 1: return 1.0f / tan_56s(((float)f_halfpi - x) * (float)f_four_over_pi); - case 2: return -1.0f / tan_56s((x-(float)f_halfpi) * (float)f_four_over_pi); - case 3: return - tan_56s(((float)f_pi - x) * (float)f_four_over_pi); - case 4: return tan_56s((x-(float)f_pi) * (float)f_four_over_pi); - case 5: return 1.0f / tan_56s(((float)f_threehalfpi - x) * (float)f_four_over_pi); - case 6: return -1.0f / tan_56s((x-(float)f_threehalfpi) * (float)f_four_over_pi); - case 7: return - tan_56s(((float)f_twopi - x) * (float)f_four_over_pi); - } -} -# 296 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_float.ino" -float atan_66s(float x) -{ - const float c1 = 1.6867629106; - const float c2 = 0.4378497304; - const float c3 = 1.6867633134; - - float x2 = x * x; - return (x * (c1 + x2 * c2) / (c3 + x2)); -} - - - - - -float atan_66(float x) -{ - float y; - bool complement= false; - bool region= false; - bool sign= false; - - if (x < 0) { - x = -x; - sign = true; - } - if (x > 1.0) { - x = 1.0 / x; - complement = true; - } - if (x > (float)f_tantwelfthpi) { - x = (x - (float)f_tansixthpi) / (1 + (float)f_tansixthpi * x); - region = true; - } - - y = atan_66s(x); - if (region) { y += (float)f_sixthpi; } - if (complement) { y = (float)f_halfpi-y; } - if (sign) { y = -y; } - return (y); -} - -float asinf1(float x) -{ - float d = 1.0f - x * x; - if (d < 0.0f) { return NAN; } - return 2 * atan_66(x / (1 + sqrt1(d))); -} - -float acosf1(float x) -{ - float d = 1.0f - x * x; - if (d < 0.0f) { return NAN; } - float y = asinf1(sqrt1(d)); - if (x >= 0.0f) { - return y; - } else { - return (float)f_pi - y; - } -} - - -float sqrt1(const float x) -{ - union { - int i; - float x; - } u; - u.x = x; - u.i = (1 << 29) + (u.i >> 1) - (1 << 22); - - - - - u.x = u.x + x / u.x; - u.x = 0.25f * u.x + x / u.x; - - return u.x; -} -# 387 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_float.ino" -uint16_t changeUIntScale(uint16_t inum, uint16_t ifrom_min, uint16_t ifrom_max, - uint16_t ito_min, uint16_t ito_max) { - - if (ifrom_min >= ifrom_max) { - if (ito_min > ito_max) { - return ito_max; - } else { - return ito_min; - } - } - - uint32_t num = inum; - uint32_t from_min = ifrom_min; - uint32_t from_max = ifrom_max; - uint32_t to_min = ito_min; - uint32_t to_max = ito_max; - - - num = (num > from_max ? from_max : (num < from_min ? from_min : num)); - - - if (to_min > to_max) { - - num = (from_max - num) + from_min; - to_min = ito_max; - to_max = ito_min; - } - - uint32_t numerator = (num - from_min) * (to_max - to_min); - uint32_t result; - if (numerator >= 0x80000000L) { - - result = numerator / (from_max - from_min) + to_min; - } else { - result = (((numerator * 2) / (from_max - from_min)) + 1) / 2 + to_min; - } - return (uint32_t) (result > to_max ? to_max : (result < to_min ? to_min : result)); -} -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_legacy_cores.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_legacy_cores.ino" -#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 - - - - - -void* memchr(const void* ptr, int value, size_t num) -{ - unsigned char *p = (unsigned char*)ptr; - while (num--) { - if (*p != (unsigned char)value) { - p++; - } else { - return p; - } - } - return 0; -} - - - -size_t strcspn(const char *str1, const char *str2) -{ - size_t ret = 0; - while (*str1) { - if (strchr(str2, *str1)) { - return ret; - } else { - str1++; - ret++; - } - } - return ret; -} - - - -char* strpbrk(const char *s1, const char *s2) -{ - while(*s1) { - if (strchr(s2, *s1++)) { - return (char*)--s1; - } - } - return 0; -} - - - -#ifndef __LONG_LONG_MAX__ -#define __LONG_LONG_MAX__ 9223372036854775807LL -#endif -#ifndef ULLONG_MAX -#define ULLONG_MAX (__LONG_LONG_MAX__ * 2ULL + 1) -#endif - -unsigned long long strtoull(const char *__restrict nptr, char **__restrict endptr, int base) -{ - const char *s = nptr; - char c; - do { c = *s++; } while (isspace((unsigned char)c)); - - int neg = 0; - if (c == '-') { - neg = 1; - c = *s++; - } else { - if (c == '+') { - c = *s++; - } - } - - if ((base == 0 || base == 16) && (c == '0') && (*s == 'x' || *s == 'X')) { - c = s[1]; - s += 2; - base = 16; - } - if (base == 0) { base = (c == '0') ? 8 : 10; } - - unsigned long long acc = 0; - int any = 0; - if (base > 1 && base < 37) { - unsigned long long cutoff = ULLONG_MAX / base; - int cutlim = ULLONG_MAX % base; - for ( ; ; c = *s++) { - if (c >= '0' && c <= '9') - c -= '0'; - else if (c >= 'A' && c <= 'Z') - c -= 'A' - 10; - else if (c >= 'a' && c <= 'z') - c -= 'a' - 10; - else - break; - - if (c >= base) - break; - - if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) - any = -1; - else { - any = 1; - acc *= base; - acc += c; - } - } - if (any < 0) { - acc = ULLONG_MAX; - } - else if (any && neg) { - acc = -acc; - } - } - - if (endptr != nullptr) { *endptr = (char *)(any ? s - 1 : nptr); } - - return acc; -} - -#endif - - - -#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) || defined(ARDUINO_ESP8266_RELEASE_2_5_0) || defined(ARDUINO_ESP8266_RELEASE_2_5_1) || defined(ARDUINO_ESP8266_RELEASE_2_5_2) - - - - - -void* memmove_P(void *dest, const void *src, size_t n) -{ - if (src > (void*)0x40000000) { - return memcpy_P(dest, src, n); - } else { - return memmove(dest, src, n); - } -} - -#endif -# 167 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_legacy_cores.ino" -void resetPins() -{ -# 178 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_legacy_cores.ino" -} -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_rotary.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_rotary.ino" -#ifdef USE_LIGHT - -#ifdef ROTARY_V1 - - - - -struct ROTARY { - unsigned long debounce = 0; - uint8_t present = 0; - uint8_t state = 0; - uint8_t position = 128; - uint8_t last_position = 128; - uint8_t interrupts_in_use_count = 0; - uint8_t changed = 0; -} Rotary; - - - -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 -void update_rotary(void) ICACHE_RAM_ATTR; -#endif - -void update_rotary(void) -{ - if (MI_DESK_LAMP == my_module_type) { - if (LightPowerIRAM()) { - - - - - uint8_t s = Rotary.state & 3; - if (digitalRead(pin[GPIO_ROT1A])) { s |= 4; } - if (digitalRead(pin[GPIO_ROT1B])) { s |= 8; } - switch (s) { - case 0: case 5: case 10: case 15: - break; - case 1: case 7: case 8: case 14: - Rotary.position++; break; - case 2: case 4: case 11: case 13: - Rotary.position--; break; - case 3: case 12: - Rotary.position = Rotary.position + 2; break; - default: - Rotary.position = Rotary.position - 2; break; - } - Rotary.state = (s >> 2); - } - } -} - -bool RotaryButtonPressed(void) -{ - if ((MI_DESK_LAMP == my_module_type) && (Rotary.changed) && LightPower()) { - Rotary.changed = 0; - return true; - } - return false; -} - -void RotaryInit(void) -{ - Rotary.present = 0; - if ((pin[GPIO_ROT1A] < 99) && (pin[GPIO_ROT1B] < 99)) { - Rotary.present++; - pinMode(pin[GPIO_ROT1A], INPUT_PULLUP); - pinMode(pin[GPIO_ROT1B], INPUT_PULLUP); - - - - - if ((pin[GPIO_ROT1A] < 6) || (pin[GPIO_ROT1A] > 11)) { - attachInterrupt(digitalPinToInterrupt(pin[GPIO_ROT1A]), update_rotary, CHANGE); - Rotary.interrupts_in_use_count++; - } - if ((pin[GPIO_ROT1B] < 6) || (pin[GPIO_ROT1B] > 11)) { - attachInterrupt(digitalPinToInterrupt(pin[GPIO_ROT1B]), update_rotary, CHANGE); - Rotary.interrupts_in_use_count++; - } - } -} - - - - - -void RotaryHandler(void) -{ - if (Rotary.interrupts_in_use_count < 2) { - noInterrupts(); - update_rotary(); - } else { - noInterrupts(); - } - if (Rotary.last_position != Rotary.position) { - if (MI_DESK_LAMP == my_module_type) { - if (Button.hold_timer[0]) { - Rotary.changed = 1; - - int16_t t = LightGetColorTemp(); - t = t + (Rotary.position - Rotary.last_position); - if (t < 153) { - t = 153; - } - if (t > 500) { - t = 500; - } - DEBUG_CORE_LOG(PSTR("ROT: " D_CMND_COLORTEMPERATURE " %d"), Rotary.position - Rotary.last_position); - LightSetColorTemp((uint16_t)t); - } else { - int8_t d = Settings.light_dimmer; - d = d + (Rotary.position - Rotary.last_position); - if (d < 1) { - d = 1; - } - if (d > 100) { - d = 100; - } - DEBUG_CORE_LOG(PSTR("ROT: " D_CMND_DIMMER " %d"), Rotary.position - Rotary.last_position); - - LightSetDimmer((uint8_t)d); - Settings.light_dimmer = d; - } - } - Rotary.last_position = 128; - Rotary.position = 128; - } - interrupts(); -} - -void RotaryLoop(void) -{ - if (Rotary.present) { - if (TimeReached(Rotary.debounce)) { - SetNextTimeInterval(Rotary.debounce, Settings.button_debounce); - RotaryHandler(); - } - } -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_rtc.ino" -# 25 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_rtc.ino" -const uint32_t SECS_PER_MIN = 60UL; -const uint32_t SECS_PER_HOUR = 3600UL; -const uint32_t SECS_PER_DAY = SECS_PER_HOUR * 24UL; -const uint32_t MINS_PER_HOUR = 60UL; - -#define LEAP_YEAR(Y) (((1970+Y)>0) && !((1970+Y)%4) && (((1970+Y)%100) || !((1970+Y)%400))) - -extern "C" { -#include "sntp.h" -} -#include - -Ticker TickerRtc; - -static const uint8_t kDaysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; -static const char kMonthNamesEnglish[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; - -struct RTC { - uint32_t utc_time = 0; - uint32_t local_time = 0; - uint32_t daylight_saving_time = 0; - uint32_t standard_time = 0; - uint32_t ntp_time = 0; - uint32_t midnight = 0; - uint32_t restart_time = 0; - int32_t time_timezone = 0; - uint8_t ntp_sync_minute = 0; - bool midnight_now = false; - bool user_time_entry = false; -} Rtc; - -uint32_t UtcTime(void) -{ - return Rtc.utc_time; -} - -uint32_t LocalTime(void) -{ - return Rtc.local_time; -} - -uint32_t Midnight(void) -{ - return Rtc.midnight; -} - -bool MidnightNow(void) -{ - if (Rtc.midnight_now) { - Rtc.midnight_now = false; - return true; - } - return false; -} - -bool IsDst(void) -{ - if (Rtc.time_timezone == Settings.toffset[1]) { - return true; - } - return false; -} - -String GetBuildDateAndTime(void) -{ - - char bdt[21]; - char *p; - char mdate[] = __DATE__; - char *smonth = mdate; - int day = 0; - int year = 0; - - - uint8_t i = 0; - for (char *str = strtok_r(mdate, " ", &p); str && i < 3; str = strtok_r(nullptr, " ", &p)) { - switch (i++) { - case 0: - smonth = str; - break; - case 1: - day = atoi(str); - break; - case 2: - year = atoi(str); - } - } - int month = (strstr(kMonthNamesEnglish, smonth) -kMonthNamesEnglish) /3 +1; - snprintf_P(bdt, sizeof(bdt), PSTR("%d" D_YEAR_MONTH_SEPARATOR "%02d" D_MONTH_DAY_SEPARATOR "%02d" D_DATE_TIME_SEPARATOR "%s"), year, month, day, __TIME__); - return String(bdt); -} - -String GetMinuteTime(uint32_t minutes) -{ - char tm[6]; - snprintf_P(tm, sizeof(tm), PSTR("%02d:%02d"), minutes / 60, minutes % 60); - - return String(tm); -} - -String GetTimeZone(void) -{ - char tz[7]; - snprintf_P(tz, sizeof(tz), PSTR("%+03d:%02d"), Rtc.time_timezone / 60, abs(Rtc.time_timezone % 60)); - - return String(tz); -} - -String GetDuration(uint32_t time) -{ - char dt[16]; - - TIME_T ut; - BreakTime(time, ut); - - - - - - - snprintf_P(dt, sizeof(dt), PSTR("%dT%02d:%02d:%02d"), ut.days, ut.hour, ut.minute, ut.second); - - return String(dt); -} - -String GetDT(uint32_t time) -{ - - - char dt[20]; - TIME_T tmpTime; - - BreakTime(time, tmpTime); - snprintf_P(dt, sizeof(dt), PSTR("%04d-%02d-%02dT%02d:%02d:%02d"), - tmpTime.year +1970, tmpTime.month, tmpTime.day_of_month, tmpTime.hour, tmpTime.minute, tmpTime.second); - - return String(dt); -} -# 175 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_rtc.ino" -String GetDateAndTime(uint8_t time_type) -{ - - uint32_t time = Rtc.local_time; - - switch (time_type) { - case DT_UTC: - time = Rtc.utc_time; - break; - - - - case DT_DST: - time = Rtc.daylight_saving_time; - break; - case DT_STD: - time = Rtc.standard_time; - break; - case DT_RESTART: - if (Rtc.restart_time == 0) { - return ""; - } - time = Rtc.restart_time; - break; - case DT_ENERGY: - time = Settings.energy_kWhtotal_time; - break; - case DT_BOOTCOUNT: - time = Settings.bootcount_reset_time; - break; - } - String dt = GetDT(time); - if (Settings.flag3.time_append_timezone && (DT_LOCAL == time_type)) { - dt += GetTimeZone(); - } - return dt; -} - -uint32_t UpTime(void) -{ - if (Rtc.restart_time) { - return Rtc.utc_time - Rtc.restart_time; - } else { - return uptime; - } -} - -uint32_t MinutesUptime(void) -{ - return (UpTime() / 60); -} - -String GetUptime(void) -{ - return GetDuration(UpTime()); -} - -uint32_t MinutesPastMidnight(void) -{ - uint32_t minutes = 0; - - if (RtcTime.valid) { - minutes = (RtcTime.hour *60) + RtcTime.minute; - } - return minutes; -} - -void BreakTime(uint32_t time_input, TIME_T &tm) -{ - - - - - uint8_t year; - uint8_t month; - uint8_t month_length; - uint32_t time; - unsigned long days; - - time = time_input; - tm.second = time % 60; - time /= 60; - tm.minute = time % 60; - time /= 60; - tm.hour = time % 24; - time /= 24; - tm.days = time; - tm.day_of_week = ((time + 4) % 7) + 1; - - year = 0; - days = 0; - while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) { - year++; - } - tm.year = year; - - days -= LEAP_YEAR(year) ? 366 : 365; - time -= days; - tm.day_of_year = time; - - for (month = 0; month < 12; month++) { - if (1 == month) { - if (LEAP_YEAR(year)) { - month_length = 29; - } else { - month_length = 28; - } - } else { - month_length = kDaysInMonth[month]; - } - - if (time >= month_length) { - time -= month_length; - } else { - break; - } - } - strlcpy(tm.name_of_month, kMonthNames + (month *3), 4); - tm.month = month + 1; - tm.day_of_month = time + 1; - tm.valid = (time_input > START_VALID_TIME); -} - -uint32_t MakeTime(TIME_T &tm) -{ - - - - int i; - uint32_t seconds; - - - seconds = tm.year * (SECS_PER_DAY * 365); - for (i = 0; i < tm.year; i++) { - if (LEAP_YEAR(i)) { - seconds += SECS_PER_DAY; - } - } - - - for (i = 1; i < tm.month; i++) { - if ((2 == i) && LEAP_YEAR(tm.year)) { - seconds += SECS_PER_DAY * 29; - } else { - seconds += SECS_PER_DAY * kDaysInMonth[i-1]; - } - } - seconds+= (tm.day_of_month - 1) * SECS_PER_DAY; - seconds+= tm.hour * SECS_PER_HOUR; - seconds+= tm.minute * SECS_PER_MIN; - seconds+= tm.second; - return seconds; -} - -uint32_t RuleToTime(TimeRule r, int yr) -{ - TIME_T tm; - uint32_t t; - uint8_t m; - uint8_t w; - - m = r.month; - w = r.week; - if (0 == w) { - if (++m > 12) { - m = 1; - yr++; - } - w = 1; - } - - tm.hour = r.hour; - tm.minute = 0; - tm.second = 0; - tm.day_of_month = 1; - tm.month = m; - tm.year = yr - 1970; - t = MakeTime(tm); - BreakTime(t, tm); - t += (7 * (w - 1) + (r.dow - tm.day_of_week + 7) % 7) * SECS_PER_DAY; - if (0 == r.week) { - t -= 7 * SECS_PER_DAY; - } - return t; -} - -void RtcSecond(void) -{ - TIME_T tmpTime; - - if (!Rtc.user_time_entry && !global_state.wifi_down) { - uint8_t uptime_minute = (uptime / 60) % 60; - if ((Rtc.ntp_sync_minute > 59) && (uptime_minute > 2)) { - Rtc.ntp_sync_minute = 1; - } - uint8_t offset = (uptime < 30) ? RtcTime.second : (((ESP_getChipId() & 0xF) * 3) + 3) ; - if ( (((offset == RtcTime.second) && ( (RtcTime.year < 2016) || - (Rtc.ntp_sync_minute == uptime_minute))) || - ntp_force_sync ) ) { - Rtc.ntp_time = sntp_get_current_timestamp(); - if (Rtc.ntp_time > START_VALID_TIME) { - ntp_force_sync = false; - Rtc.utc_time = Rtc.ntp_time; - Rtc.ntp_sync_minute = 60; - if (Rtc.restart_time == 0) { - Rtc.restart_time = Rtc.utc_time - uptime; - } - BreakTime(Rtc.utc_time, tmpTime); - RtcTime.year = tmpTime.year + 1970; - Rtc.daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year); - Rtc.standard_time = RuleToTime(Settings.tflag[0], RtcTime.year); - - - PrepLog_P2(LOG_LEVEL_DEBUG, PSTR("NTP: " D_UTC_TIME " %s, " D_DST_TIME " %s, " D_STD_TIME " %s"), - GetDateAndTime(DT_UTC).c_str(), GetDateAndTime(DT_DST).c_str(), GetDateAndTime(DT_STD).c_str()); - - if (Rtc.local_time < START_VALID_TIME) { - rules_flag.time_init = 1; - } else { - rules_flag.time_set = 1; - } - } else { - Rtc.ntp_sync_minute++; - } - } - } - - Rtc.utc_time++; - Rtc.local_time = Rtc.utc_time; - if (Rtc.local_time > START_VALID_TIME) { - int16_t timezone_minutes = Settings.timezone_minutes; - if (Settings.timezone < 0) { timezone_minutes *= -1; } - Rtc.time_timezone = (Settings.timezone * SECS_PER_HOUR) + (timezone_minutes * SECS_PER_MIN); - if (99 == Settings.timezone) { - int32_t dstoffset = Settings.toffset[1] * SECS_PER_MIN; - int32_t stdoffset = Settings.toffset[0] * SECS_PER_MIN; - if (Settings.tflag[1].hemis) { - - if ((Rtc.utc_time >= (Rtc.standard_time - dstoffset)) && (Rtc.utc_time < (Rtc.daylight_saving_time - stdoffset))) { - Rtc.time_timezone = stdoffset; - } else { - Rtc.time_timezone = dstoffset; - } - } else { - - if ((Rtc.utc_time >= (Rtc.daylight_saving_time - stdoffset)) && (Rtc.utc_time < (Rtc.standard_time - dstoffset))) { - Rtc.time_timezone = dstoffset; - } else { - Rtc.time_timezone = stdoffset; - } - } - } - Rtc.local_time += Rtc.time_timezone; - Rtc.time_timezone /= 60; - if (!Settings.energy_kWhtotal_time) { - Settings.energy_kWhtotal_time = Rtc.local_time; - } - if (Settings.bootcount_reset_time < START_VALID_TIME) { - Settings.bootcount_reset_time = Rtc.local_time; - } - } - - BreakTime(Rtc.local_time, RtcTime); - if (RtcTime.valid) { - if (!Rtc.midnight) { - Rtc.midnight = Rtc.local_time - (RtcTime.hour * 3600) - (RtcTime.minute * 60) - RtcTime.second; - } - if (!RtcTime.hour && !RtcTime.minute && !RtcTime.second) { - Rtc.midnight = Rtc.local_time; - Rtc.midnight_now = true; - } - } - - RtcTime.year += 1970; -} - -void RtcSetTime(uint32_t epoch) -{ - if (epoch < START_VALID_TIME) { - Rtc.user_time_entry = false; - ntp_force_sync = true; - sntp_init(); - } else { - sntp_stop(); - Rtc.user_time_entry = true; - Rtc.utc_time = epoch -1; - } - RtcSecond(); -} - -void RtcInit(void) -{ - sntp_setservername(0, SettingsText(SET_NTPSERVER1)); - sntp_setservername(1, SettingsText(SET_NTPSERVER2)); - sntp_setservername(2, SettingsText(SET_NTPSERVER3)); - sntp_stop(); - sntp_set_timezone(0); - sntp_init(); - Rtc.utc_time = 0; - BreakTime(Rtc.utc_time, RtcTime); - TickerRtc.attach(1, RtcSecond); -} -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_static_buffer.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_static_buffer.ino" -typedef struct SBuffer_impl { - uint16_t size; - uint16_t len; - uint8_t buf[]; -} SBuffer_impl; - - - -typedef class SBuffer { - -protected: - SBuffer(void) { - - } - -public: - SBuffer(const size_t size) { - _buf = (SBuffer_impl*) new char[size+4]; - _buf->size = size; - _buf->len = 0; - - } - - inline size_t getSize(void) const { return _buf->size; } - inline size_t size(void) const { return _buf->size; } - inline size_t getLen(void) const { return _buf->len; } - inline size_t len(void) const { return _buf->len; } - inline uint8_t *getBuffer(void) const { return _buf->buf; } - inline uint8_t *buf(size_t i = 0) const { return &_buf->buf[i]; } - inline char *charptr(size_t i = 0) const { return (char*) &_buf->buf[i]; } - - virtual ~SBuffer(void) { - delete[] _buf; - } - - inline void setLen(const size_t len) { - uint16_t old_len = _buf->len; - _buf->len = (len <= _buf->size) ? len : _buf->size; - if (old_len < _buf->len) { - memset((void*) &_buf->buf[old_len], 0, _buf->len - old_len); - } - } - - void set8(const size_t offset, const uint8_t data) { - if (offset < _buf->len) { - _buf->buf[offset] = data; - } - } - - size_t add8(const uint8_t data) { - if (_buf->len < _buf->size) { - _buf->buf[_buf->len++] = data; - } - return _buf->len; - } - size_t add16(const uint16_t data) { - if (_buf->len < _buf->size - 1) { - _buf->buf[_buf->len++] = data; - _buf->buf[_buf->len++] = data >> 8; - } - return _buf->len; - } - size_t add32(const uint32_t data) { - if (_buf->len < _buf->size - 3) { - _buf->buf[_buf->len++] = data; - _buf->buf[_buf->len++] = data >> 8; - _buf->buf[_buf->len++] = data >> 16; - _buf->buf[_buf->len++] = data >> 24; - } - return _buf->len; - } - size_t add64(const uint64_t data) { - if (_buf->len < _buf->size - 7) { - _buf->buf[_buf->len++] = data; - _buf->buf[_buf->len++] = data >> 8; - _buf->buf[_buf->len++] = data >> 16; - _buf->buf[_buf->len++] = data >> 24; - _buf->buf[_buf->len++] = data >> 32; - _buf->buf[_buf->len++] = data >> 40; - _buf->buf[_buf->len++] = data >> 48; - _buf->buf[_buf->len++] = data >> 56; - } - return _buf->len; - } - - size_t addBuffer(const SBuffer &buf2) { - if (len() + buf2.len() <= size()) { - for (uint32_t i = 0; i < buf2.len(); i++) { - _buf->buf[_buf->len++] = buf2.buf()[i]; - } - } - return _buf->len; - } - - size_t addBuffer(const uint8_t *buf2, size_t len2) { - if ((buf2) && (len() + len2 <= size())) { - for (uint32_t i = 0; i < len2; i++) { - _buf->buf[_buf->len++] = pgm_read_byte(&buf2[i]); - } - } - return _buf->len; - } - - size_t addBuffer(const char *buf2, size_t len2) { - if ((buf2) && (len() + len2 <= size())) { - for (uint32_t i = 0; i < len2; i++) { - _buf->buf[_buf->len++] = pgm_read_byte(&buf2[i]); - } - } - return _buf->len; - } - - uint8_t get8(size_t offset) const { - if (offset < _buf->len) { - return _buf->buf[offset]; - } else { - return 0; - } - } - uint8_t read8(const size_t offset) const { - if (offset < len()) { - return _buf->buf[offset]; - } - return 0; - } - uint16_t get16(const size_t offset) const { - if (offset < len() - 1) { - return _buf->buf[offset] | (_buf->buf[offset+1] << 8); - } - return 0; - } - uint32_t get32(const size_t offset) const { - if (offset < len() - 3) { - return _buf->buf[offset] | (_buf->buf[offset+1] << 8) | - (_buf->buf[offset+2] << 16) | (_buf->buf[offset+3] << 24); - } - return 0; - } - uint64_t get64(const size_t offset) const { - if (offset < len() - 7) { - return (uint64_t)_buf->buf[offset] | ((uint64_t)_buf->buf[offset+1] << 8) | - ((uint64_t)_buf->buf[offset+2] << 16) | ((uint64_t)_buf->buf[offset+3] << 24) | - ((uint64_t)_buf->buf[offset+4] << 32) | ((uint64_t)_buf->buf[offset+5] << 40) | - ((uint64_t)_buf->buf[offset+6] << 48) | ((uint64_t)_buf->buf[offset+7] << 56); - } - return 0; - } - - - inline size_t strlen(const size_t offset) const { - return strnlen((const char*) &_buf->buf[offset], len() - offset); - } - - size_t strlen_s(const size_t offset) const { - size_t slen = this->strlen(offset); - if (slen == len() - offset) { - return 0; - } else { - return slen; - } - } - - SBuffer subBuffer(const size_t start, size_t len) const { - if (start >= _buf->len) { - len = 0; - } else if (start + len > _buf->len) { - len = _buf->len - start; - } - - SBuffer buf2(len); - memcpy(buf2.buf(), buf()+start, len); - buf2._buf->len = len; - return buf2; - } - - static SBuffer SBufferFromHex(const char *hex, size_t len) { - size_t buf_len = (len + 3) / 2; - SBuffer buf2(buf_len); - uint8_t val; - - for (; len > 1; len -= 2) { - val = asc2byte(*hex++) << 4; - val |= asc2byte(*hex++); - buf2.add8(val); - } - return buf2; - } - -protected: - - static uint8_t asc2byte(char chr) { - uint8_t rVal = 0; - if (isdigit(chr)) { rVal = chr - '0'; } - else if (chr >= 'A' && chr <= 'F') { rVal = chr + 10 - 'A'; } - else if (chr >= 'a' && chr <= 'f') { rVal = chr + 10 - 'a'; } - return rVal; - } - - static void unHex(const char* in, uint8_t *out, size_t len) { - } - -protected: - SBuffer_impl * _buf; - -} SBuffer; - -typedef class PreAllocatedSBuffer : public SBuffer { - -public: - PreAllocatedSBuffer(const size_t size, void * buffer) { - _buf = (SBuffer_impl*) buffer; - _buf->size = size - 4; - _buf->len = 0; - } - - ~PreAllocatedSBuffer(void) { - - _buf = nullptr; - } -} PreAllocatedSBuffer; -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_statistics.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_statistics.ino" -#define USE_STATS_CODE - -#ifdef USE_STATS_CODE - - - - -String GetStatistics(void) -{ - char data[40]; - snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/%d\""), GetSettingsTextLen(), settings_text_size); - return String(data); -} - -#else - -String GetStatistics(void) -{ - return String(""); -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_switch.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_switch.ino" -#define SWITCH_V3 -#ifdef SWITCH_V3 - - - - - - -const uint8_t SWITCH_PROBE_INTERVAL = 10; - -#include - -Ticker TickerSwitch; - -struct SWITCH { - unsigned long debounce = 0; - uint16_t no_pullup_mask = 0; - uint8_t state[MAX_SWITCHES] = { 0 }; - uint8_t last_state[MAX_SWITCHES]; - uint8_t hold_timer[MAX_SWITCHES] = { 0 }; - uint8_t virtual_state[MAX_SWITCHES]; - uint8_t present = 0; -} Switch; - - - -void SwitchPullupFlag(uint16 switch_bit) -{ - bitSet(Switch.no_pullup_mask, switch_bit); -} - -void SwitchSetVirtual(uint32_t index, uint8_t state) -{ - Switch.virtual_state[index] = state; -} - -uint8_t SwitchGetVirtual(uint32_t index) -{ - return Switch.virtual_state[index]; -} - -uint8_t SwitchLastState(uint32_t index) -{ - return Switch.last_state[index]; -} - -bool SwitchState(uint32_t index) -{ - uint32_t switchmode = Settings.switchmode[index]; - return ((FOLLOW_INV == switchmode) || - (PUSHBUTTON_INV == switchmode) || - (PUSHBUTTONHOLD_INV == switchmode) || - (FOLLOWMULTI_INV == switchmode) || - (PUSHHOLDMULTI_INV == switchmode) || - (PUSHON_INV == switchmode) - ) ^ Switch.last_state[index]; -} - - - -void SwitchProbe(void) -{ - if (uptime < 4) { return; } - - uint8_t state_filter = Settings.switch_debounce / SWITCH_PROBE_INTERVAL; - uint8_t force_high = (Settings.switch_debounce % 50) &1; - uint8_t force_low = (Settings.switch_debounce % 50) &2; - - for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - if (pin[GPIO_SWT1 +i] < 99) { - - if (1 == digitalRead(pin[GPIO_SWT1 +i])) { - - if (force_high) { - if (1 == Switch.virtual_state[i]) { - Switch.state[i] = state_filter; - } - } - - if (Switch.state[i] < state_filter) { - Switch.state[i]++; - if (state_filter == Switch.state[i]) { - Switch.virtual_state[i] = 1; - } - } - } else { - - if (force_low) { - if (0 == Switch.virtual_state[i]) { - Switch.state[i] = 0; - } - } - - if (Switch.state[i] > 0) { - Switch.state[i]--; - if (0 == Switch.state[i]) { - Switch.virtual_state[i] = 0; - } - } - } - } - } - TickerSwitch.attach_ms(SWITCH_PROBE_INTERVAL, SwitchProbe); -} - -void SwitchInit(void) -{ - Switch.present = 0; - for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - Switch.last_state[i] = 1; - if (pin[GPIO_SWT1 +i] < 99) { - Switch.present++; - pinMode(pin[GPIO_SWT1 +i], bitRead(Switch.no_pullup_mask, i) ? INPUT : ((16 == pin[GPIO_SWT1 +i]) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); - Switch.last_state[i] = digitalRead(pin[GPIO_SWT1 +i]); - } - Switch.virtual_state[i] = Switch.last_state[i]; - } - if (Switch.present) { TickerSwitch.attach_ms(SWITCH_PROBE_INTERVAL, SwitchProbe); } -} - - - - - -void SwitchHandler(uint8_t mode) -{ - if (uptime < 4) { return; } - - uint16_t loops_per_second = 1000 / Settings.switch_debounce; - - for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - if ((pin[GPIO_SWT1 +i] < 99) || (mode)) { - uint8_t button = Switch.virtual_state[i]; - uint8_t switchflag = POWER_TOGGLE +1; - - if (Switch.hold_timer[i]) { - Switch.hold_timer[i]--; - if (0 == Switch.hold_timer[i]) { - - switch (Settings.switchmode[i]) { - case TOGGLEMULTI: - switchflag = POWER_TOGGLE; - break; - case FOLLOWMULTI: - switchflag = button &1; - break; - case FOLLOWMULTI_INV: - switchflag = ~button &1; - break; - case PUSHHOLDMULTI: - if (NOT_PRESSED == button) { - Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 25; - SendKey(KEY_SWITCH, i +1, POWER_INCREMENT); - } else { - SendKey(KEY_SWITCH, i +1, POWER_CLEAR); - } - break; - case PUSHHOLDMULTI_INV: - if (PRESSED == button) { - Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 25; - SendKey(KEY_SWITCH, i +1, POWER_INCREMENT); - } else { - SendKey(KEY_SWITCH, i +1, POWER_CLEAR); - } - break; - default: - SendKey(KEY_SWITCH, i +1, POWER_HOLD); - break; - } - } - } - - if (button != Switch.last_state[i]) { - switch (Settings.switchmode[i]) { - case TOGGLE: - case PUSHBUTTON_TOGGLE: - switchflag = POWER_TOGGLE; - break; - case FOLLOW: - switchflag = button &1; - break; - case FOLLOW_INV: - switchflag = ~button &1; - break; - case PUSHBUTTON: - - if (PRESSED == button) { - switchflag = POWER_TOGGLE; - } - break; - case PUSHBUTTON_INV: - - if (NOT_PRESSED == button) { - switchflag = POWER_TOGGLE; - } - break; - case PUSHBUTTONHOLD: - - if (PRESSED == button) { - Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; - } - - if ((NOT_PRESSED == button) && (Switch.hold_timer[i])) { - Switch.hold_timer[i] = 0; - switchflag = POWER_TOGGLE; - } - break; - case PUSHBUTTONHOLD_INV: - - if (NOT_PRESSED == button) { - Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; - } - - if ((PRESSED == button) && (Switch.hold_timer[i])) { - Switch.hold_timer[i] = 0; - switchflag = POWER_TOGGLE; - } - break; -# 250 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_switch.ino" - case TOGGLEMULTI: - case FOLLOWMULTI: - case FOLLOWMULTI_INV: - if (Switch.hold_timer[i]) { - Switch.hold_timer[i] = 0; - SendKey(KEY_SWITCH, i +1, POWER_HOLD); - } else { - Switch.hold_timer[i] = loops_per_second / 2; - } - break; - case PUSHHOLDMULTI: - - if (NOT_PRESSED == button) { - if (Switch.hold_timer[i] != 0) { - SendKey(KEY_SWITCH, i +1, POWER_INV); - } - Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; - } - - if (PRESSED == button) { - if (Switch.hold_timer[i] > loops_per_second * Settings.param[P_HOLD_TIME] / 25) { - switchflag = POWER_TOGGLE; - } - Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; - } - break; - case PUSHHOLDMULTI_INV: - - if (PRESSED == button) { - if (Switch.hold_timer[i] != 0) { - SendKey(KEY_SWITCH, i +1, POWER_INV); - } - Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; - } - - if (NOT_PRESSED == button) { - if (Switch.hold_timer[i] > loops_per_second * Settings.param[P_HOLD_TIME] / 25) { - switchflag = POWER_TOGGLE; - } - Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; - } - break; - case PUSHON: - if (PRESSED == button) { - switchflag = POWER_ON; - } - break; - case PUSHON_INV: - if (NOT_PRESSED == button) { - switchflag = POWER_ON; - } - break; - } - Switch.last_state[i] = button; - } - if (switchflag <= POWER_TOGGLE) { - if (!SendKey(KEY_SWITCH, i +1, switchflag)) { - ExecuteCommandPower(i +1, switchflag, SRC_SWITCH); - } - } - } - } -} - -void SwitchLoop(void) -{ - if (Switch.present) { - if (TimeReached(Switch.debounce)) { - SetNextTimeInterval(Switch.debounce, Settings.switch_debounce); - SwitchHandler(0); - } - } -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_tasmota.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_tasmota.ino" -const char kSleepMode[] PROGMEM = "Dynamic|Normal"; -const char kPrefixes[] PROGMEM = D_CMND "|" D_STAT "|" D_TELE; - -char* Format(char* output, const char* input, int size) -{ - char *token; - uint32_t digits = 0; - - if (strstr(input, "%") != nullptr) { - strlcpy(output, input, size); - token = strtok(output, "%"); - if (strstr(input, "%") == input) { - output[0] = '\0'; - } else { - token = strtok(nullptr, ""); - } - if (token != nullptr) { - digits = atoi(token); - if (digits) { - char tmp[size]; - if (strchr(token, 'd')) { - snprintf_P(tmp, size, PSTR("%s%c0%dd"), output, '%', digits); - snprintf_P(output, size, tmp, ESP_getChipId() & 0x1fff); - } else { - snprintf_P(tmp, size, PSTR("%s%c0%dX"), output, '%', digits); - snprintf_P(output, size, tmp, ESP_getChipId()); - } - } else { - if (strchr(token, 'd')) { - snprintf_P(output, size, PSTR("%s%d"), output, ESP_getChipId()); - digits = 8; - } - } - } - } - if (!digits) { - strlcpy(output, input, size); - } - return output; -} - -char* GetOtaUrl(char *otaurl, size_t otaurl_size) -{ - if (strstr(SettingsText(SET_OTAURL), "%04d") != nullptr) { - snprintf(otaurl, otaurl_size, SettingsText(SET_OTAURL), ESP_getChipId() & 0x1fff); - } - else if (strstr(SettingsText(SET_OTAURL), "%d") != nullptr) { - snprintf_P(otaurl, otaurl_size, SettingsText(SET_OTAURL), ESP_getChipId()); - } - else { - strlcpy(otaurl, SettingsText(SET_OTAURL), otaurl_size); - } - - return otaurl; -} - -char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopic) -{ -# 88 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_tasmota.ino" - char romram[CMDSZ]; - String fulltopic; - - snprintf_P(romram, sizeof(romram), subtopic); - if (fallback_topic_flag || (prefix > 3)) { - bool fallback = (prefix < 8); - prefix &= 3; - char stemp[11]; - fulltopic = GetTextIndexed(stemp, sizeof(stemp), prefix, kPrefixes); - fulltopic += F("/"); - if (fallback) { - fulltopic += mqtt_client; - fulltopic += F("_fb"); - } else { - fulltopic += topic; - } - } else { - fulltopic = SettingsText(SET_MQTT_FULLTOPIC); - if ((0 == prefix) && (-1 == fulltopic.indexOf(FPSTR(MQTT_TOKEN_PREFIX)))) { - fulltopic += F("/"); - fulltopic += FPSTR(MQTT_TOKEN_PREFIX); - } - for (uint32_t i = 0; i < MAX_MQTT_PREFIXES; i++) { - if (!strlen(SettingsText(SET_MQTTPREFIX1 + i))) { - char temp[TOPSZ]; - SettingsUpdateText(SET_MQTTPREFIX1 + i, GetTextIndexed(temp, sizeof(temp), i, kPrefixes)); - } - } - fulltopic.replace(FPSTR(MQTT_TOKEN_PREFIX), SettingsText(SET_MQTTPREFIX1 + prefix)); - - fulltopic.replace(FPSTR(MQTT_TOKEN_TOPIC), topic); - fulltopic.replace(F("%hostname%"), my_hostname); - String token_id = WiFi.macAddress(); - token_id.replace(":", ""); - fulltopic.replace(F("%id%"), token_id); - } - fulltopic.replace(F("#"), ""); - fulltopic.replace(F("//"), "/"); - if (!fulltopic.endsWith("/")) { - fulltopic += "/"; - } - snprintf_P(stopic, TOPSZ, PSTR("%s%s"), fulltopic.c_str(), romram); - return stopic; -} - -char* GetGroupTopic_P(char *stopic, const char* subtopic, uint32_t itopic) -{ - - - return GetTopic_P(stopic, (Settings.flag3.grouptopic_mode) ? CMND +8 : CMND, SettingsText(itopic), subtopic); -} - -char* GetFallbackTopic_P(char *stopic, const char* subtopic) -{ - return GetTopic_P(stopic, CMND +4, nullptr, subtopic); -} - -char* GetStateText(uint32_t state) -{ - if (state >= MAX_STATE_TEXT) { - state = 1; - } - return SettingsText(SET_STATE_TXT1 + state); -} - - - -void SetLatchingRelay(power_t lpower, uint32_t state) -{ - - - - - - if (state && !latching_relay_pulse) { - latching_power = lpower; - latching_relay_pulse = 2; - } - - for (uint32_t i = 0; i < devices_present; i++) { - uint32_t port = (i << 1) + ((latching_power >> i) &1); - DigitalWrite(GPIO_REL1 +port, bitRead(rel_inverted, port) ? !state : state); - } -} - -void SetDevicePower(power_t rpower, uint32_t source) -{ - ShowSource(source); - last_source = source; - - if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { - power = (1 << devices_present) -1; - rpower = power; - } - - if (Settings.flag.interlock) { - for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { - power_t mask = 1; - uint32_t count = 0; - for (uint32_t j = 0; j < devices_present; j++) { - if ((Settings.interlock[i] & mask) && (rpower & mask)) { - count++; - } - mask <<= 1; - } - if (count > 1) { - mask = ~Settings.interlock[i]; - power &= mask; - rpower &= mask; - } - } - } - - if (rpower) { - last_power = rpower; - } - - XdrvMailbox.index = rpower; - XdrvCall(FUNC_SET_POWER); - - XdrvMailbox.index = rpower; - XdrvMailbox.payload = source; - if (XdrvCall(FUNC_SET_DEVICE_POWER)) { - - } -#ifdef ESP8266 - else if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { - Serial.write(0xA0); - Serial.write(0x04); - Serial.write(rpower &0xFF); - Serial.write(0xA1); - Serial.write('\n'); - Serial.flush(); - } - else if (EXS_RELAY == my_module_type) { - SetLatchingRelay(rpower, 1); - } - else -#endif - { - for (uint32_t i = 0; i < devices_present; i++) { - power_t state = rpower &1; - if (i < MAX_RELAYS) { - DigitalWrite(GPIO_REL1 +i, bitRead(rel_inverted, i) ? !state : state); - } - rpower >>= 1; - } - } -} - -void RestorePower(bool publish_power, uint32_t source) -{ - if (power != last_power) { - power = last_power; - SetDevicePower(power, source); - if (publish_power) { - MqttPublishAllPowerState(); - } - } -} - -void SetAllPower(uint32_t state, uint32_t source) -{ -# 259 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_tasmota.ino" - bool publish_power = true; - if ((state >= POWER_OFF_NO_STATE) && (state <= POWER_TOGGLE_NO_STATE)) { - state &= 3; - 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; - } - SetDevicePower(power, source); - } - if (publish_power) { - MqttPublishAllPowerState(); - } -} - -void SetPowerOnState(void) -{ -#ifdef ESP8266 - if (MOTOR == my_module_type) { - Settings.poweronstate = POWER_ALL_ON; - } -#endif - if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { - SetDevicePower(1, SRC_RESTART); - } else { - if ((ResetReason() == REASON_DEFAULT_RST) || (ResetReason() == REASON_EXT_SYS_RST)) { - switch (Settings.poweronstate) { - case POWER_ALL_OFF: - case POWER_ALL_OFF_PULSETIME_ON: - power = 0; - SetDevicePower(power, SRC_RESTART); - break; - case POWER_ALL_ON: - power = (1 << devices_present) -1; - SetDevicePower(power, SRC_RESTART); - break; - case POWER_ALL_SAVED_TOGGLE: - power = (Settings.power & ((1 << devices_present) -1)) ^ POWER_MASK; - if (Settings.flag.save_state) { - SetDevicePower(power, SRC_RESTART); - } - break; - case POWER_ALL_SAVED: - power = Settings.power & ((1 << devices_present) -1); - if (Settings.flag.save_state) { - SetDevicePower(power, SRC_RESTART); - } - break; - } - } else { - power = Settings.power & ((1 << devices_present) -1); - if (Settings.flag.save_state) { - SetDevicePower(power, SRC_RESTART); - } - } - } - - - for (uint32_t i = 0; i < devices_present; i++) { - if (!Settings.flag3.no_power_feedback) { - if ((i < MAX_RELAYS) && (pin[GPIO_REL1 +i] < 99)) { - bitWrite(power, i, digitalRead(pin[GPIO_REL1 +i]) ^ bitRead(rel_inverted, i)); - } - } - if ((i < MAX_PULSETIMERS) && (bitRead(power, i) || (POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate))) { - SetPulseTimer(i, Settings.pulse_timer[i]); - } - } - blink_powersave = power; -} - -void SetLedPowerIdx(uint32_t led, uint32_t state) -{ - if ((99 == pin[GPIO_LEDLNK]) && (0 == led)) { - if (pin[GPIO_LED2] < 99) { - led = 1; - } - } - if (pin[GPIO_LED1 + led] < 99) { - uint32_t mask = 1 << led; - if (state) { - state = 1; - led_power |= mask; - } else { - led_power &= (0xFF ^ mask); - } - DigitalWrite(GPIO_LED1 + led, bitRead(led_inverted, led) ? !state : state); - } -#ifdef USE_BUZZER - if (led == 0) { - BuzzerSetStateToLed(state); - } -#endif -} - -void SetLedPower(uint32_t state) -{ - if (99 == pin[GPIO_LEDLNK]) { - SetLedPowerIdx(0, state); - } else { - power_t mask = 1; - for (uint32_t i = 0; i < leds_present; i++) { - bool tstate = (power & mask); - SetLedPowerIdx(i, tstate); - mask <<= 1; - } - } -} - -void SetLedPowerAll(uint32_t state) -{ - for (uint32_t i = 0; i < leds_present; i++) { - SetLedPowerIdx(i, state); - } -} - -void SetLedLink(uint32_t state) -{ - uint32_t led_pin = pin[GPIO_LEDLNK]; - uint32_t led_inv = ledlnk_inverted; - if (99 == led_pin) { - led_pin = pin[GPIO_LED1]; - led_inv = bitRead(led_inverted, 0); - } - if (led_pin < 99) { - if (state) { state = 1; } - digitalWrite(led_pin, (led_inv) ? !state : state); - } -#ifdef USE_BUZZER - BuzzerSetStateToLed(state); -#endif -} - -void SetPulseTimer(uint32_t index, uint32_t time) -{ - pulse_timer[index] = (time > 111) ? millis() + (1000 * (time - 100)) : (time > 0) ? millis() + (100 * time) : 0L; -} - -uint32_t GetPulseTimer(uint32_t index) -{ - long time = TimePassedSince(pulse_timer[index]); - if (time < 0) { - time *= -1; - return (time > 11100) ? (time / 1000) + 100 : (time > 0) ? time / 100 : 0; - } - return 0; -} - - - -bool SendKey(uint32_t key, uint32_t device, uint32_t state) -{ -# 428 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_tasmota.ino" - char stopic[TOPSZ]; - char scommand[CMDSZ]; - char key_topic[TOPSZ]; - bool result = false; - uint32_t device_save = device; - - char *tmp = (key) ? SettingsText(SET_MQTT_SWITCH_TOPIC) : SettingsText(SET_MQTT_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; - } - GetTopic_P(stopic, CMND, key_topic, - GetPowerDevice(scommand, device, sizeof(scommand), (key + Settings.flag.device_index_enable))); - if (CLEAR_RETAIN == state) { - mqtt_data[0] = '\0'; - } else { - if ((Settings.flag3.button_switch_force_local || - !strcmp(mqtt_topic, key_topic) || - !strcmp(SettingsText(SET_MQTT_GRP_TOPIC), key_topic)) && - (POWER_TOGGLE == state)) { - state = ~(power >> (device -1)) &1; - } - snprintf_P(mqtt_data, sizeof(mqtt_data), GetStateText(state)); - } -#ifdef USE_DOMOTICZ - if (!(DomoticzSendKey(key, device, state, strlen(mqtt_data)))) { -#endif - MqttPublish(stopic, ((key) ? Settings.flag.mqtt_switch_retain - : Settings.flag.mqtt_button_retain) && - (state != POWER_HOLD || !Settings.flag3.no_hold_retain)); -#ifdef USE_DOMOTICZ - } -#endif - result = !Settings.flag3.button_switch_force_local; - } else { - Response_P(PSTR("{\"%s%d\":{\"State\":%d}}"), (key) ? "Switch" : "Button", device, state); - result = XdrvRulesProcess(); - } - int32_t payload_save = XdrvMailbox.payload; - XdrvMailbox.payload = device_save << 24 | key << 16 | state << 8 | device; - XdrvCall(FUNC_ANY_KEY); - XdrvMailbox.payload = payload_save; - return result; -} - -void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source) -{ -# 489 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_tasmota.ino" -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan()) { - blink_mask &= 1; - Settings.flag.interlock = 0; - Settings.pulse_timer[1] = 0; - Settings.pulse_timer[2] = 0; - Settings.pulse_timer[3] = 0; - } -#endif - - bool publish_power = true; - if ((state >= POWER_OFF_NO_STATE) && (state <= POWER_TOGGLE_NO_STATE)) { - state &= 3; - publish_power = false; - } - - if ((device < 1) || (device > devices_present)) { - device = 1; - } - active_device = device; - - if (device <= MAX_PULSETIMERS) { - SetPulseTimer(device -1, 0); - } - power_t mask = 1 << (device -1); - if (state <= POWER_TOGGLE) { - if ((blink_mask & mask)) { - blink_mask &= (POWER_MASK ^ mask); - MqttPublishPowerBlinkState(device); - } - - if (Settings.flag.interlock && - !interlock_mutex && - ((POWER_ON == state) || ((POWER_TOGGLE == state) && !(power & mask))) - ) { - interlock_mutex = true; - for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { - if (Settings.interlock[i] & mask) { - for (uint32_t j = 0; j < devices_present; j++) { - power_t imask = 1 << j; - if ((Settings.interlock[i] & imask) && (power & imask) && (mask != imask)) { - ExecuteCommandPower(j +1, POWER_OFF, SRC_IGNORE); - delay(50); - } - } - break; - } - } - interlock_mutex = false; - } - - switch (state) { - case POWER_OFF: { - power &= (POWER_MASK ^ mask); - break; } - case POWER_ON: - power |= mask; - break; - case POWER_TOGGLE: - power ^= mask; - } -#ifdef USE_DEVICE_GROUPS - if (SRC_REMOTE != source && SRC_RETRY != source) SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_POWER, power); -#endif - SetDevicePower(power, source); -#ifdef USE_DOMOTICZ - DomoticzUpdatePowerState(device); -#endif -#ifdef USE_KNX - KnxUpdatePowerState(device, power); -#endif - if (publish_power && Settings.flag3.hass_tele_on_power) { - MqttPublishTeleState(); - } - if (device <= MAX_PULSETIMERS) { - SetPulseTimer(device -1, (((POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate) ? ~power : power) & mask) ? Settings.pulse_timer[device -1] : 0); - } - } - else if (POWER_BLINK == state) { - if (!(blink_mask & mask)) { - blink_powersave = (blink_powersave & (POWER_MASK ^ mask)) | (power & mask); - blink_power = (power >> (device -1))&1; - } - blink_timer = millis() + 100; - blink_counter = ((!Settings.blinkcount) ? 64000 : (Settings.blinkcount *2)) +1; - blink_mask |= mask; - MqttPublishPowerBlinkState(device); - return; - } - else if (POWER_BLINK_STOP == state) { - bool flag = (blink_mask & mask); - blink_mask &= (POWER_MASK ^ mask); - MqttPublishPowerBlinkState(device); - if (flag) { - ExecuteCommandPower(device, (blink_powersave >> (device -1))&1, SRC_IGNORE); - } - return; - } - if (publish_power) { - MqttPublishPowerState(device); - } -} - -void StopAllPowerBlink(void) -{ - power_t mask; - - for (uint32_t i = 1; i <= devices_present; i++) { - mask = 1 << (i -1); - if (blink_mask & mask) { - blink_mask &= (POWER_MASK ^ mask); - MqttPublishPowerBlinkState(i); - ExecuteCommandPower(i, (blink_powersave >> (i -1))&1, SRC_IGNORE); - } - } -} - -void MqttShowPWMState(void) -{ - ResponseAppend_P(PSTR("\"" D_CMND_PWM "\":{")); - bool first = true; - for (uint32_t i = 0; i < MAX_PWMS; i++) { - if (pin[GPIO_PWM1 + i] < 99) { - ResponseAppend_P(PSTR("%s\"" D_CMND_PWM "%d\":%d"), first ? "" : ",", i+1, Settings.pwm_value[i]); - first = false; - } - } - ResponseJsonEnd(); -} - -void MqttShowState(void) -{ - char stemp1[TOPSZ]; - - ResponseAppendTime(); - ResponseAppend_P(PSTR(",\"" D_JSON_UPTIME "\":\"%s\",\"UptimeSec\":%u"), GetUptime().c_str(), UpTime()); - -#ifdef USE_ADC_VCC - dtostrfd((double)ESP.getVcc()/1000, 3, stemp1); - ResponseAppend_P(PSTR(",\"" D_JSON_VCC "\":%s"), stemp1); -#endif - - ResponseAppend_P(PSTR(",\"" D_JSON_HEAPSIZE "\":%d,\"SleepMode\":\"%s\",\"Sleep\":%u,\"LoadAvg\":%u,\"MqttCount\":%u"), - ESP.getFreeHeap()/1024, GetTextIndexed(stemp1, sizeof(stemp1), Settings.flag3.sleep_normal, kSleepMode), - ssleep, loop_load_avg, MqttConnectCount()); - - for (uint32_t i = 1; i <= devices_present; i++) { -#ifdef USE_LIGHT - if ((LightDevice()) && (i >= LightDevice())) { - if (i == LightDevice()) { LightState(1); } - } else { -#endif - ResponseAppend_P(PSTR(",\"%s\":\"%s\""), GetPowerDevice(stemp1, i, sizeof(stemp1), Settings.flag.device_index_enable), - GetStateText(bitRead(power, i-1))); -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan()) { - ResponseAppend_P(PSTR(",\"" D_CMND_FANSPEED "\":%d"), GetFanspeed()); - break; - } -#endif -#ifdef USE_LIGHT - } -#endif - } - - if (pwm_present) { - ResponseAppend_P(PSTR(",")); - MqttShowPWMState(); - } - - int32_t rssi = WiFi.RSSI(); - ResponseAppend_P(PSTR(",\"" D_JSON_WIFI "\":{\"" D_JSON_AP "\":%d,\"" D_JSON_SSID "\":\"%s\",\"" D_JSON_BSSID "\":\"%s\",\"" D_JSON_CHANNEL "\":%d,\"" D_JSON_RSSI "\":%d,\"" D_JSON_SIGNAL "\":%d,\"" D_JSON_LINK_COUNT "\":%d,\"" D_JSON_DOWNTIME "\":\"%s\"}}"), - Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), WiFi.BSSIDstr().c_str(), WiFi.channel(), - WifiGetRssiAsQuality(rssi), rssi, WifiLinkCount(), WifiDowntime().c_str()); -} - -void MqttPublishTeleState(void) -{ - mqtt_data[0] = '\0'; - MqttShowState(); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN); -#if defined(USE_RULES) || defined(USE_SCRIPT) - RulesTeleperiod(); -#endif -} - -void TempHumDewShow(bool json, bool pass_on, const char *types, float f_temperature, float f_humidity) -{ - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{"), types); - ResponseAppendTHD(f_temperature, f_humidity); - ResponseJsonEnd(); -#ifdef USE_DOMOTICZ - if (pass_on) { - DomoticzTempHumPressureSensor(f_temperature, f_humidity); - } -#endif -#ifdef USE_KNX - if (pass_on) { - KnxSensor(KNX_TEMPERATURE, f_temperature); - KnxSensor(KNX_HUMIDITY, f_humidity); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_THD(types, f_temperature, f_humidity); -#endif - } -} - -bool MqttShowSensor(void) -{ - ResponseAppendTime(); - - int json_data_start = strlen(mqtt_data); - for (uint32_t i = 0; i < MAX_SWITCHES; i++) { -#ifdef USE_TM1638 - if ((pin[GPIO_SWT1 +i] < 99) || ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99))) { -#else - if (pin[GPIO_SWT1 +i] < 99) { -#endif - ResponseAppend_P(PSTR(",\"" D_JSON_SWITCH "%d\":\"%s\""), i +1, GetStateText(SwitchState(i))); - } - } - XsnsCall(FUNC_JSON_APPEND); - XdrvCall(FUNC_JSON_APPEND); - - bool json_data_available = (strlen(mqtt_data) - json_data_start); - if (strstr_P(mqtt_data, PSTR(D_JSON_PRESSURE)) != nullptr) { - ResponseAppend_P(PSTR(",\"" D_JSON_PRESSURE_UNIT "\":\"%s\""), PressureUnit().c_str()); - } - if (strstr_P(mqtt_data, PSTR(D_JSON_TEMPERATURE)) != nullptr) { - ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE_UNIT "\":\"%c\""), TempUnit()); - } - if ((strstr_P(mqtt_data, PSTR(D_JSON_SPEED)) != nullptr) && Settings.flag2.speed_conversion) { - ResponseAppend_P(PSTR(",\"" D_JSON_SPEED_UNIT "\":\"%s\""), SpeedUnit().c_str()); - } - ResponseJsonEnd(); - - if (json_data_available) { XdrvCall(FUNC_SHOW_SENSOR); } - return json_data_available; -} - -void MqttPublishSensor(void) -{ - mqtt_data[0] = '\0'; - if (MqttShowSensor()) { - MqttPublishTeleSensor(); - } -} -# 747 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_tasmota.ino" -void PerformEverySecond(void) -{ - uptime++; - - if (POWER_CYCLE_TIME == uptime) { - UpdateQuickPowerCycle(false); - } - - if (BOOT_LOOP_TIME == uptime) { - RtcRebootReset(); - -#ifdef USE_DEEPSLEEP - if (!(DeepSleepEnabled() && !Settings.flag3.bootcount_update)) { -#endif - Settings.bootcount++; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BOOT_COUNT " %d"), Settings.bootcount); -#ifdef USE_DEEPSLEEP - } -#endif - } - - if (mqtt_cmnd_blocked_reset) { - mqtt_cmnd_blocked_reset--; - if (!mqtt_cmnd_blocked_reset) { - mqtt_cmnd_blocked = 0; - } - } - - if (seriallog_timer) { - seriallog_timer--; - if (!seriallog_timer) { - if (seriallog_level) { - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SERIAL_LOGGING_DISABLED)); - } - seriallog_level = 0; - } - } - - if (syslog_timer) { - syslog_timer--; - if (!syslog_timer) { - syslog_level = Settings.syslog_level; - if (Settings.syslog_level) { - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SYSLOG_LOGGING_REENABLED)); - } - } - } - - ResetGlobalValues(); - - if (Settings.tele_period) { - if (tele_period >= 9999) { - if (!global_state.wifi_down) { - tele_period = 0; - } - } else { - tele_period++; - if (tele_period >= Settings.tele_period) { - tele_period = 0; - - MqttPublishTeleState(); - - mqtt_data[0] = '\0'; - if (MqttShowSensor()) { - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); -#if defined(USE_RULES) || defined(USE_SCRIPT) - RulesTeleperiod(); -#endif - } - - XsnsCall(FUNC_AFTER_TELEPERIOD); - XdrvCall(FUNC_AFTER_TELEPERIOD); - } - } - } - -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 - - wifiKeepAlive(); -#endif - - -#ifdef ESP32 - if (11 == uptime) { - ESP_getSketchSize(); - } -#endif -} - - - - - -void Every100mSeconds(void) -{ - - power_t power_now; - - if (prepped_loglevel) { - AddLog(prepped_loglevel); - } - - if (latching_relay_pulse) { - latching_relay_pulse--; - if (!latching_relay_pulse) SetLatchingRelay(0, 0); - } - - for (uint32_t i = 0; i < MAX_PULSETIMERS; i++) { - if (pulse_timer[i] != 0L) { - if (TimeReached(pulse_timer[i])) { - pulse_timer[i] = 0L; - ExecuteCommandPower(i +1, (POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate) ? POWER_ON : POWER_OFF, SRC_PULSETIMER); - } - } - } - - if (blink_mask) { - if (TimeReached(blink_timer)) { - SetNextTimeInterval(blink_timer, 100 * Settings.blinktime); - blink_counter--; - if (!blink_counter) { - StopAllPowerBlink(); - } else { - blink_power ^= 1; - power_now = (power & (POWER_MASK ^ blink_mask)) | ((blink_power) ? blink_mask : 0); - SetDevicePower(power_now, SRC_IGNORE); - } - } - } -} - - - - - -void Every250mSeconds(void) -{ - - - uint32_t blinkinterval = 1; - - state_250mS++; - state_250mS &= 0x3; - - if (!Settings.flag.global_state) { - if (global_state.data) { - if (global_state.mqtt_down) { blinkinterval = 7; } - if (global_state.wifi_down) { blinkinterval = 3; } - blinks = 201; - } - } - if (blinks || restart_flag || ota_state_flag) { - if (restart_flag || ota_state_flag) { - blinkstate = true; - } else { - blinkspeed--; - if (!blinkspeed) { - blinkspeed = blinkinterval; - blinkstate ^= 1; - } - } - if ((!(Settings.ledstate &0x08)) && ((Settings.ledstate &0x06) || (blinks > 200) || (blinkstate))) { - SetLedLink(blinkstate); - } - if (!blinkstate) { - blinks--; - if (200 == blinks) blinks = 0; - } - } - if (Settings.ledstate &1 && (pin[GPIO_LEDLNK] < 99 || !(blinks || restart_flag || ota_state_flag)) ) { - bool tstate = power & Settings.ledmask; -#ifdef ESP8266 - if ((SONOFF_TOUCH == my_module_type) || (SONOFF_T11 == my_module_type) || (SONOFF_T12 == my_module_type) || (SONOFF_T13 == my_module_type)) { - tstate = (!power) ? 1 : 0; - } -#endif - SetLedPower(tstate); - } - - - - - - switch (state_250mS) { - case 0: - if (ota_state_flag && BACKLOG_EMPTY) { - ota_state_flag--; - if (2 == ota_state_flag) { - RtcSettings.ota_loader = 0; - ota_retry_counter = OTA_ATTEMPTS; - ESPhttpUpdate.rebootOnUpdate(false); - SettingsSave(1); - } - if (ota_state_flag <= 0) { -#ifdef USE_WEBSERVER - if (Settings.webserver) StopWebserver(); -#endif -#ifdef USE_ARILUX_RF - AriluxRfDisable(); -#endif - ota_state_flag = 92; - ota_result = 0; - ota_retry_counter--; - if (ota_retry_counter) { - strlcpy(mqtt_data, GetOtaUrl(log_data, sizeof(log_data)), sizeof(mqtt_data)); -#ifndef FIRMWARE_MINIMAL - if (RtcSettings.ota_loader) { -# 970 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_tasmota.ino" - char *bch = strrchr(mqtt_data, '/'); - if (bch == nullptr) { bch = mqtt_data; } -# 981 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_tasmota.ino" - char *ech = strchr(bch, '.'); - if (ech == nullptr) { ech = mqtt_data + strlen(mqtt_data); } - - - - char ota_url_type[strlen(ech) +1]; - strncpy(ota_url_type, ech, sizeof(ota_url_type)); - - char *pch = strrchr(bch, '-'); - if (pch == nullptr) { pch = ech; } - *pch = '\0'; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s-" D_JSON_MINIMAL "%s"), mqtt_data, ota_url_type); - } -#endif - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "%s"), mqtt_data); -#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) - ota_result = (HTTP_UPDATE_FAILED != ESPhttpUpdate.update(mqtt_data)); -#else - - WiFiClient OTAclient; - ota_result = (HTTP_UPDATE_FAILED != ESPhttpUpdate.update(OTAclient, mqtt_data)); -#endif - if (!ota_result) { -#ifndef FIRMWARE_MINIMAL - int ota_error = ESPhttpUpdate.getLastError(); - DEBUG_CORE_LOG(PSTR("OTA: Error %d"), ota_error); - if ((HTTP_UE_TOO_LESS_SPACE == ota_error) || (HTTP_UE_BIN_FOR_WRONG_FLASH == ota_error)) { - RtcSettings.ota_loader = 1; - } -#endif - ota_state_flag = 2; - } - } - } - if (90 == ota_state_flag) { - ota_state_flag = 0; - Response_P(PSTR("{\"" D_CMND_UPGRADE "\":\"")); - if (ota_result) { - - if (!VersionCompatible()) { - ResponseAppend_P(PSTR(D_JSON_FAILED " " D_UPLOAD_ERR_14)); - } else { - ResponseAppend_P(PSTR(D_JSON_SUCCESSFUL ". " D_JSON_RESTARTING)); - restart_flag = 2; - } - } else { - ResponseAppend_P(PSTR(D_JSON_FAILED " %s"), ESPhttpUpdate.getLastErrorString().c_str()); - } - ResponseAppend_P(PSTR("\"}")); - - MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_UPGRADE)); - } - } - break; - case 1: - if (MidnightNow()) { - XsnsCall(FUNC_SAVE_AT_MIDNIGHT); - } - if (save_data_counter && BACKLOG_EMPTY) { - save_data_counter--; - if (save_data_counter <= 0) { - if (Settings.flag.save_state) { - power_t mask = POWER_MASK; - for (uint32_t i = 0; i < MAX_PULSETIMERS; i++) { - if ((Settings.pulse_timer[i] > 0) && (Settings.pulse_timer[i] < 30)) { - mask &= ~(1 << i); - } - } - if (!((Settings.power &mask) == (power &mask))) { - Settings.power = power; - } - } else { - Settings.power = 0; - } - SettingsSave(0); - save_data_counter = Settings.save_data; - } - } - if (restart_flag && BACKLOG_EMPTY) { - if ((214 == restart_flag) || (215 == restart_flag) || (216 == restart_flag)) { - - char storage_ssid1[strlen(SettingsText(SET_STASSID1)) +1]; - strncpy(storage_ssid1, SettingsText(SET_STASSID1), sizeof(storage_ssid1)); - char storage_ssid2[strlen(SettingsText(SET_STASSID2)) +1]; - strncpy(storage_ssid2, SettingsText(SET_STASSID2), sizeof(storage_ssid2)); - char storage_pass1[strlen(SettingsText(SET_STAPWD1)) +1]; - strncpy(storage_pass1, SettingsText(SET_STAPWD1), sizeof(storage_pass1)); - char storage_pass2[strlen(SettingsText(SET_STAPWD2)) +1]; - strncpy(storage_pass2, SettingsText(SET_STAPWD2), sizeof(storage_pass2)); - - char storage_mqtthost[strlen(SettingsText(SET_MQTT_HOST)) +1]; - strncpy(storage_mqtthost, SettingsText(SET_MQTT_HOST), sizeof(storage_mqtthost)); - char storage_mqttuser[strlen(SettingsText(SET_MQTT_USER)) +1]; - strncpy(storage_mqttuser, SettingsText(SET_MQTT_USER), sizeof(storage_mqttuser)); - char storage_mqttpwd[strlen(SettingsText(SET_MQTT_PWD)) +1]; - strncpy(storage_mqttpwd, SettingsText(SET_MQTT_PWD), sizeof(storage_mqttpwd)); - char storage_mqtttopic[strlen(SettingsText(SET_MQTT_TOPIC)) +1]; - strncpy(storage_mqtttopic, SettingsText(SET_MQTT_TOPIC), sizeof(storage_mqtttopic)); - uint16_t mqtt_port = Settings.mqtt_port; - - - - - if ((215 == restart_flag) || (216 == restart_flag)) { - SettingsErase(0); - } - SettingsDefault(); - - SettingsUpdateText(SET_STASSID1, storage_ssid1); - SettingsUpdateText(SET_STASSID2, storage_ssid2); - SettingsUpdateText(SET_STAPWD1, storage_pass1); - SettingsUpdateText(SET_STAPWD2, storage_pass2); - if (216 == restart_flag) { - - SettingsUpdateText(SET_MQTT_HOST, storage_mqtthost); - SettingsUpdateText(SET_MQTT_USER, storage_mqttuser); - SettingsUpdateText(SET_MQTT_PWD, storage_mqttpwd); - SettingsUpdateText(SET_MQTT_TOPIC, storage_mqtttopic); - Settings.mqtt_port = mqtt_port; - } - restart_flag = 2; - } - else if (213 == restart_flag) { - SettingsSdkErase(); - restart_flag = 2; - } - else if (212 == restart_flag) { - SettingsErase(0); - restart_flag = 211; - } - if (211 == restart_flag) { - SettingsDefault(); - restart_flag = 2; - } - if (2 == restart_flag) { - SettingsSaveAll(); - } - restart_flag--; - if (restart_flag <= 0) { - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING)); - EspRestart(); - } - } - break; - case 2: - WifiCheck(wifi_state_flag); - wifi_state_flag = WIFI_RESTART; - break; - case 3: - if (!global_state.wifi_down) { MqttCheck(); } - break; - } -} - -#ifdef USE_ARDUINO_OTA - - - - - - - -bool arduino_ota_triggered = false; -uint16_t arduino_ota_progress_dot_count = 0; - -void ArduinoOTAInit(void) -{ - ArduinoOTA.setPort(8266); - ArduinoOTA.setHostname(my_hostname); - if (strlen(SettingsText(SET_WEBPWD))) { - ArduinoOTA.setPassword(SettingsText(SET_WEBPWD)); - } - - ArduinoOTA.onStart([]() - { - SettingsSave(1); -#ifdef USE_WEBSERVER - if (Settings.webserver) { StopWebserver(); } -#endif -#ifdef USE_ARILUX_RF - AriluxRfDisable(); -#endif - if (Settings.flag.mqtt_enabled) { - MqttDisconnect(); - } - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_UPLOAD_STARTED)); - arduino_ota_triggered = true; - arduino_ota_progress_dot_count = 0; - delay(100); - }); - - ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) - { - if ((LOG_LEVEL_DEBUG <= seriallog_level)) { - arduino_ota_progress_dot_count++; - Serial.printf("."); - if (!(arduino_ota_progress_dot_count % 80)) { Serial.println(); } - } - }); - - ArduinoOTA.onError([](ota_error_t error) - { - - - - - char error_str[100]; - - if ((LOG_LEVEL_DEBUG <= seriallog_level) && arduino_ota_progress_dot_count) { Serial.println(); } - switch (error) { - case OTA_BEGIN_ERROR: strncpy_P(error_str, PSTR(D_UPLOAD_ERR_2), sizeof(error_str)); break; - case OTA_RECEIVE_ERROR: strncpy_P(error_str, PSTR(D_UPLOAD_ERR_5), sizeof(error_str)); break; - case OTA_END_ERROR: strncpy_P(error_str, PSTR(D_UPLOAD_ERR_7), sizeof(error_str)); break; - default: - snprintf_P(error_str, sizeof(error_str), PSTR(D_UPLOAD_ERROR_CODE " %d"), error); - } - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA %s. " D_RESTARTING), error_str); - EspRestart(); - }); - - ArduinoOTA.onEnd([]() - { - if ((LOG_LEVEL_DEBUG <= seriallog_level)) { Serial.println(); } - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_SUCCESSFUL ". " D_RESTARTING)); - EspRestart(); - }); - - ArduinoOTA.begin(); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_ENABLED " " D_PORT " 8266")); -} - -void ArduinoOtaLoop(void) -{ - MDNS.update(); - ArduinoOTA.handle(); - - while (arduino_ota_triggered) { ArduinoOTA.handle(); } -} -#endif - - - -void SerialInput(void) -{ - while (Serial.available()) { - - delay(0); - serial_in_byte = Serial.read(); - -#ifdef ESP8266 - - - - if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { - serial_in_byte = ButtonSerial(serial_in_byte); - } -#endif - - - if (XdrvCall(FUNC_SERIAL)) { - serial_in_byte_counter = 0; - Serial.flush(); - return; - } - - - - if (serial_in_byte > 127 && !Settings.flag.mqtt_serial_raw) { - serial_in_byte_counter = 0; - Serial.flush(); - return; - } - if (!Settings.flag.mqtt_serial) { - if (isprint(serial_in_byte)) { - if (serial_in_byte_counter < INPUT_BUFFER_SIZE -1) { - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; - } else { - serial_in_byte_counter = 0; - } - } - } else { - if (serial_in_byte || Settings.flag.mqtt_serial_raw) { - if ((serial_in_byte_counter < INPUT_BUFFER_SIZE -1) && - ((isprint(serial_in_byte) && (128 == Settings.serial_delimiter)) || - ((serial_in_byte != Settings.serial_delimiter) && (128 != Settings.serial_delimiter)) || - Settings.flag.mqtt_serial_raw)) { - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; - serial_polling_window = millis(); - } else { - serial_polling_window = 0; - break; - } - } - } - -#ifdef USE_SONOFF_SC - - - - if (SONOFF_SC == my_module_type) { - if (serial_in_byte == '\x1B') { - serial_in_buffer[serial_in_byte_counter] = 0; - SonoffScSerialInput(serial_in_buffer); - serial_in_byte_counter = 0; - Serial.flush(); - return; - } - } else -#endif - - - if (!Settings.flag.mqtt_serial && (serial_in_byte == '\n')) { - serial_in_buffer[serial_in_byte_counter] = 0; - seriallog_level = (Settings.seriallog_level < LOG_LEVEL_INFO) ? (uint8_t)LOG_LEVEL_INFO : Settings.seriallog_level; - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), serial_in_buffer); - ExecuteCommand(serial_in_buffer, SRC_SERIAL); - serial_in_byte_counter = 0; - serial_polling_window = 0; - Serial.flush(); - return; - } - } - - if (Settings.flag.mqtt_serial && serial_in_byte_counter && (millis() > (serial_polling_window + SERIAL_POLLING))) { - serial_in_buffer[serial_in_byte_counter] = 0; - char hex_char[(serial_in_byte_counter * 2) + 2]; - bool assume_json = (!Settings.flag.mqtt_serial_raw && (serial_in_buffer[0] == '{')); - Response_P(PSTR("{\"" D_JSON_SERIALRECEIVED "\":%s%s%s}"), - (assume_json) ? "" : "\"", - (Settings.flag.mqtt_serial_raw) ? ToHex_P((unsigned char*)serial_in_buffer, serial_in_byte_counter, hex_char, sizeof(hex_char)) : serial_in_buffer, - (assume_json) ? "" : "\""); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SERIALRECEIVED)); - XdrvRulesProcess(); - serial_in_byte_counter = 0; - } -} - - - -void ResetPwm(void) -{ - for (uint32_t i = 0; i < MAX_PWMS; i++) { - if (pin[GPIO_PWM1 +i] < 99) { - analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range : 0); - - } - } -} - - - -void GpioInit(void) -{ - uint32_t mpin; - - if (!ValidModule(Settings.module)) { - uint32_t module = MODULE; - if (!ValidModule(MODULE)) { -#ifdef ESP8266 - module = SONOFF_BASIC; -#endif -#ifdef ESP32 - module = WEMOS; -#endif - } - - Settings.module = module; - Settings.last_module = module; - } - SetModuleType(); - - if (Settings.module != Settings.last_module) { - Settings.baudrate = APP_BAUDRATE / 300; - Settings.serial_config = TS_SERIAL_8N1; - } - - for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) { - if ((Settings.user_template.gp.io[i] >= GPIO_SENSOR_END) && (Settings.user_template.gp.io[i] < GPIO_USER)) { - Settings.user_template.gp.io[i] = GPIO_USER; - } - } - - myio def_gp; - ModuleGpios(&def_gp); - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - if ((Settings.my_gp.io[i] >= GPIO_SENSOR_END) && (Settings.my_gp.io[i] < GPIO_USER)) { - Settings.my_gp.io[i] = GPIO_NONE; - } - else if (Settings.my_gp.io[i] > GPIO_NONE) { - my_module.io[i] = Settings.my_gp.io[i]; - } - if ((def_gp.io[i] > GPIO_NONE) && (def_gp.io[i] < GPIO_USER)) { - my_module.io[i] = def_gp.io[i]; - } - } - if ((Settings.my_adc0 >= ADC0_END) && (Settings.my_adc0 < ADC0_USER)) { - Settings.my_adc0 = ADC0_NONE; - } - else if (Settings.my_adc0 > ADC0_NONE) { - my_adc0 = Settings.my_adc0; - } - my_module_flag = ModuleFlag(); - uint32_t template_adc0 = my_module_flag.data &15; - if ((template_adc0 > ADC0_NONE) && (template_adc0 < ADC0_USER)) { - my_adc0 = template_adc0; - } - - for (uint32_t i = 0; i < GPIO_MAX; i++) { - pin[i] = 99; - } - for (uint32_t i = 0; i < sizeof(my_module.io); i++) { - mpin = ValidPin(i, my_module.io[i]); - - DEBUG_CORE_LOG(PSTR("INI: gpio pin %d, mpin %d"), i, mpin); - - if (mpin) { - XdrvMailbox.index = mpin; - XdrvMailbox.payload = i; - - if ((mpin >= GPIO_SWT1_NP) && (mpin < (GPIO_SWT1_NP + MAX_SWITCHES))) { - SwitchPullupFlag(mpin - GPIO_SWT1_NP); - mpin -= (GPIO_SWT1_NP - GPIO_SWT1); - } - else if ((mpin >= GPIO_KEY1_NP) && (mpin < (GPIO_KEY1_NP + MAX_KEYS))) { - ButtonPullupFlag(mpin - GPIO_KEY1_NP); - mpin -= (GPIO_KEY1_NP - GPIO_KEY1); - } - else if ((mpin >= GPIO_KEY1_INV) && (mpin < (GPIO_KEY1_INV + MAX_KEYS))) { - ButtonInvertFlag(mpin - GPIO_KEY1_INV); - mpin -= (GPIO_KEY1_INV - GPIO_KEY1); - } - else if ((mpin >= GPIO_KEY1_INV_NP) && (mpin < (GPIO_KEY1_INV_NP + MAX_KEYS))) { - ButtonPullupFlag(mpin - GPIO_KEY1_INV_NP); - ButtonInvertFlag(mpin - GPIO_KEY1_INV_NP); - mpin -= (GPIO_KEY1_INV_NP - GPIO_KEY1); - } - else if ((mpin >= GPIO_REL1_INV) && (mpin < (GPIO_REL1_INV + MAX_RELAYS))) { - bitSet(rel_inverted, mpin - GPIO_REL1_INV); - mpin -= (GPIO_REL1_INV - GPIO_REL1); - } - else if ((mpin >= GPIO_LED1_INV) && (mpin < (GPIO_LED1_INV + MAX_LEDS))) { - bitSet(led_inverted, mpin - GPIO_LED1_INV); - mpin -= (GPIO_LED1_INV - GPIO_LED1); - } - else if (mpin == GPIO_LEDLNK_INV) { - ledlnk_inverted = 1; - mpin -= (GPIO_LEDLNK_INV - GPIO_LEDLNK); - } - else if ((mpin >= GPIO_PWM1_INV) && (mpin < (GPIO_PWM1_INV + MAX_PWMS))) { - bitSet(pwm_inverted, mpin - GPIO_PWM1_INV); - mpin -= (GPIO_PWM1_INV - GPIO_PWM1); - } - else if (XdrvCall(FUNC_PIN_STATE)) { - mpin = XdrvMailbox.index; - } - else if (XsnsCall(FUNC_PIN_STATE)) { - mpin = XdrvMailbox.index; - }; - } - if (mpin) pin[mpin] = i; - } - -#ifdef ESP8266 - if ((2 == pin[GPIO_TXD]) || (H801 == my_module_type)) { Serial.set_tx(2); } -#endif - - analogWriteRange(Settings.pwm_range); - analogWriteFreq(Settings.pwm_frequency); - -#ifdef USE_SPI - spi_flg = ((((pin[GPIO_SPI_CS] < 99) && (pin[GPIO_SPI_CS] > 14)) || (pin[GPIO_SPI_CS] < 12)) || (((pin[GPIO_SPI_DC] < 99) && (pin[GPIO_SPI_DC] > 14)) || (pin[GPIO_SPI_DC] < 12))); - if (spi_flg) { - for (uint32_t i = 0; i < GPIO_MAX; i++) { - if ((pin[i] >= 12) && (pin[i] <=14)) pin[i] = 99; - } - my_module.io[12] = GPIO_SPI_MISO; - pin[GPIO_SPI_MISO] = 12; - my_module.io[13] = GPIO_SPI_MOSI; - pin[GPIO_SPI_MOSI] = 13; - my_module.io[14] = GPIO_SPI_CLK; - pin[GPIO_SPI_CLK] = 14; - } - soft_spi_flg = ((pin[GPIO_SSPI_CS] < 99) && (pin[GPIO_SSPI_SCLK] < 99) && ((pin[GPIO_SSPI_MOSI] < 99) || (pin[GPIO_SSPI_MOSI] < 99))); -#endif - - - - for (uint32_t i = 0; i < sizeof(my_module.io); i++) { - mpin = ValidPin(i, my_module.io[i]); - - if (((i < 6) || (i > 11)) && (0 == mpin)) { - if (!((1 == i) || (3 == i))) { - pinMode(i, INPUT); - } - } - } - -#ifdef USE_I2C - i2c_flg = ((pin[GPIO_I2C_SCL] < 99) && (pin[GPIO_I2C_SDA] < 99)); - if (i2c_flg) { - Wire.begin(pin[GPIO_I2C_SDA], pin[GPIO_I2C_SCL]); - } -#endif - - devices_present = 0; - light_type = LT_BASIC; - if (XdrvCall(FUNC_MODULE_INIT)) { - - } -#ifdef ESP8266 - else if (YTF_IR_BRIDGE == my_module_type) { - ClaimSerial(); - - } - else if (SONOFF_DUAL == my_module_type) { - devices_present = 2; - SetSerial(19200, TS_SERIAL_8N1); - } - else if (CH4 == my_module_type) { - devices_present = 4; - SetSerial(19200, TS_SERIAL_8N1); - } -#ifdef USE_SONOFF_SC - else if (SONOFF_SC == my_module_type) { - SetSerial(19200, TS_SERIAL_8N1); - } -#endif -#endif - - for (uint32_t i = 0; i < MAX_PWMS; i++) { - if (pin[GPIO_PWM1 +i] < 99) { - pinMode(pin[GPIO_PWM1 +i], OUTPUT); - if (light_type) { - - analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range : 0); - } else { - pwm_present = true; - analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]); - } - } - } - - for (uint32_t i = 0; i < MAX_RELAYS; i++) { - if (pin[GPIO_REL1 +i] < 99) { - pinMode(pin[GPIO_REL1 +i], OUTPUT); - devices_present++; -#ifdef ESP8266 - if (EXS_RELAY == my_module_type) { - digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? 1 : 0); - if (i &1) { devices_present--; } - } -#endif - } - } - - for (uint32_t i = 0; i < MAX_LEDS; i++) { - if (pin[GPIO_LED1 +i] < 99) { -#ifdef USE_ARILUX_RF - if ((3 == i) && (leds_present < 2) && (99 == pin[GPIO_ARIRFSEL])) { - pin[GPIO_ARIRFSEL] = pin[GPIO_LED4]; - pin[GPIO_LED4] = 99; - } else { -#endif - pinMode(pin[GPIO_LED1 +i], OUTPUT); - leds_present++; - digitalWrite(pin[GPIO_LED1 +i], bitRead(led_inverted, i)); -#ifdef USE_ARILUX_RF - } -#endif - } - } - if (pin[GPIO_LEDLNK] < 99) { - pinMode(pin[GPIO_LEDLNK], OUTPUT); - digitalWrite(pin[GPIO_LEDLNK], ledlnk_inverted); - } - -#ifdef USE_PWM_DIMMER - if (PWM_DIMMER == my_module_type && pin[GPIO_REL1] < 99) devices_present--; -#endif - - ButtonInit(); - SwitchInit(); -#ifdef ROTARY_V1 - RotaryInit(); -#endif - - SetLedPower(Settings.ledstate &8); - SetLedLink(Settings.ledstate &8); - - XdrvCall(FUNC_PRE_INIT); -} -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_udp.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_udp.ino" -#ifdef USE_EMULATION - -#define UDP_BUFFER_SIZE 200 -#define UDP_MSEARCH_SEND_DELAY 1500 - -#include -Ticker TickerMSearch; - -IPAddress udp_remote_ip; -uint16_t udp_remote_port; - -bool udp_connected = false; -bool udp_response_mutex = false; - - - - - -const char URN_BELKIN_DEVICE[] PROGMEM = "urn:belkin:device:**"; -const char URN_BELKIN_DEVICE_CAP[] PROGMEM = "urn:Belkin:device:**"; -const char UPNP_ROOTDEVICE[] PROGMEM = "upnp:rootdevice"; -const char SSDPSEARCH_ALL[] PROGMEM = "ssdpsearch:all"; -const char SSDP_ALL[] PROGMEM = "ssdp:all"; - - - - - -bool UdpDisconnect(void) -{ - if (udp_connected) { - PortUdp.flush(); - WiFiUDP::stopAll(); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_MULTICAST_DISABLED)); - udp_connected = false; - } - return udp_connected; -} - -bool UdpConnect(void) -{ - if (!udp_connected && !restart_flag) { - - if (PortUdp.beginMulticast(WiFi.localIP(), IPAddress(239,255,255,250), 1900)) { - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_REJOINED)); - udp_response_mutex = false; - udp_connected = true; - } else { - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_JOIN_FAILED)); - udp_connected = false; - } - } - return udp_connected; -} - -void PollUdp(void) -{ - if (udp_connected) { - while (PortUdp.parsePacket()) { - char packet_buffer[UDP_BUFFER_SIZE]; - - int len = PortUdp.read(packet_buffer, UDP_BUFFER_SIZE -1); - packet_buffer[len] = 0; - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: Packet (%d)"), len); - - - - if (Settings.flag2.emulation) { -#if defined(USE_SCRIPT_HUE) || defined(USE_ZIGBEE) - if (!udp_response_mutex && (strstr_P(packet_buffer, PSTR("M-SEARCH")) != nullptr)) { -#else - if (devices_present && !udp_response_mutex && (strstr_P(packet_buffer, PSTR("M-SEARCH")) != nullptr)) { -#endif - udp_response_mutex = true; - - udp_remote_ip = PortUdp.remoteIP(); - udp_remote_port = PortUdp.remotePort(); - - - - - uint32_t response_delay = UDP_MSEARCH_SEND_DELAY + ((millis() &0x7) * 100); - - LowerCase(packet_buffer, packet_buffer); - RemoveSpace(packet_buffer); - -#ifdef USE_EMULATION_WEMO - if (EMUL_WEMO == Settings.flag2.emulation) { - if (strstr_P(packet_buffer, URN_BELKIN_DEVICE) != nullptr) { - TickerMSearch.attach_ms(response_delay, WemoRespondToMSearch, 1); - return; - } - else if ((strstr_P(packet_buffer, UPNP_ROOTDEVICE) != nullptr) || - (strstr_P(packet_buffer, SSDPSEARCH_ALL) != nullptr) || - (strstr_P(packet_buffer, SSDP_ALL) != nullptr)) { - TickerMSearch.attach_ms(response_delay, WemoRespondToMSearch, 2); - return; - } - } -#endif - -#ifdef USE_EMULATION_HUE - if (EMUL_HUE == Settings.flag2.emulation) { - if ((strstr_P(packet_buffer, PSTR(":device:basic:1")) != nullptr) || - (strstr_P(packet_buffer, UPNP_ROOTDEVICE) != nullptr) || - (strstr_P(packet_buffer, SSDPSEARCH_ALL) != nullptr) || - (strstr_P(packet_buffer, SSDP_ALL) != nullptr)) { - TickerMSearch.attach_ms(response_delay, HueRespondToMSearch); - return; - } - } -#endif - - udp_response_mutex = false; - continue; - } - } - -#ifdef USE_DEVICE_GROUPS - if (Settings.flag4.device_groups_enabled && !strncmp_P(packet_buffer, kDeviceGroupMessage, sizeof(DEVICE_GROUP_MESSAGE) - 1)) { - ProcessDeviceGroupMessage(packet_buffer, len); - } -#endif - } - optimistic_yield(100); - } -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_wifi.ino" -# 29 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_wifi.ino" -#ifndef WIFI_RSSI_THRESHOLD -#define WIFI_RSSI_THRESHOLD 10 -#endif -#ifndef WIFI_RESCAN_MINUTES -#define WIFI_RESCAN_MINUTES 44 -#endif - -const uint8_t WIFI_CONFIG_SEC = 180; -const uint8_t WIFI_CHECK_SEC = 20; -const uint8_t WIFI_RETRY_OFFSET_SEC = 12; - -#include -#if LWIP_IPV6 -#include -#endif - -struct WIFI { - uint32_t last_event = 0; - uint32_t downtime = 0; - uint16_t link_count = 0; - uint8_t counter; - uint8_t retry_init; - uint8_t retry; - uint8_t status; - uint8_t config_type = 0; - uint8_t config_counter = 0; - uint8_t mdns_begun = 0; - uint8_t scan_state; - uint8_t bssid[6]; - int8_t best_network_db; -} Wifi; - -int WifiGetRssiAsQuality(int rssi) -{ - int quality = 0; - - if (rssi <= -100) { - quality = 0; - } else if (rssi >= -50) { - quality = 100; - } else { - quality = 2 * (rssi + 100); - } - return quality; -} - -bool WifiConfigCounter(void) -{ - if (Wifi.config_counter) { - Wifi.config_counter = WIFI_CONFIG_SEC; - } - return (Wifi.config_counter); -} - -void WifiConfig(uint8_t type) -{ - if (!Wifi.config_type) { - if ((WIFI_RETRY == type) || (WIFI_WAIT == type)) { return; } -#ifdef USE_EMULATION - UdpDisconnect(); -#endif - WiFi.disconnect(); - Wifi.config_type = type; - -#ifndef USE_WEBSERVER - if (WIFI_MANAGER == Wifi.config_type) { - Wifi.config_type = WIFI_SERIAL; - } -#endif - - Wifi.config_counter = WIFI_CONFIG_SEC; - Wifi.counter = Wifi.config_counter +5; - blinks = 1999; - if (WIFI_RESTART == Wifi.config_type) { - restart_flag = 2; - } - else if (WIFI_SERIAL == Wifi.config_type) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_6_SERIAL " " D_ACTIVE_FOR_3_MINUTES)); - } -#ifdef USE_WEBSERVER - else if (WIFI_MANAGER == Wifi.config_type || WIFI_MANAGER_RESET_ONLY == Wifi.config_type) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_2_WIFIMANAGER " " D_ACTIVE_FOR_3_MINUTES)); - WifiManagerBegin(WIFI_MANAGER_RESET_ONLY == Wifi.config_type); - } -#endif - } -} - -void WifiSetMode(WiFiMode_t wifi_mode) -{ - if (WiFi.getMode() == wifi_mode) { return; } - - if (wifi_mode != WIFI_OFF) { - - WiFi.forceSleepWake(); - delay(100); - } - - uint32_t retry = 2; - while (!WiFi.mode(wifi_mode) && retry--) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR("Retry set Mode...")); - delay(100); - } - - if (wifi_mode == WIFI_OFF) { - delay(1000); - WiFi.forceSleepBegin(); - delay(1); - } else { - delay(30); - } -} - -void WiFiSetSleepMode(void) -{ -# 157 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_wifi.ino" -#if defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) -#else - if (ssleep && Settings.flag3.sleep_normal) { - WiFi.setSleepMode(WIFI_LIGHT_SLEEP); - } else { - WiFi.setSleepMode(WIFI_MODEM_SLEEP); - } -#endif - WifiSetOutputPower(); -} - -void WifiBegin(uint8_t flag, uint8_t channel) -{ - const char kWifiPhyMode[] = " BGN"; - -#ifdef USE_EMULATION - UdpDisconnect(); -#endif - -#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, PSTR(D_PATCH_ISSUE_2186)); - - WifiSetMode(WIFI_OFF); -#endif - - WiFi.persistent(false); - WiFi.disconnect(true); - delay(200); - - WifiSetMode(WIFI_STA); - WiFiSetSleepMode(); - - - if (!WiFi.getAutoConnect()) { WiFi.setAutoConnect(true); } - - switch (flag) { - case 0: - case 1: - Settings.sta_active = flag; - break; - case 2: - Settings.sta_active ^= 1; - } - if (!strlen(SettingsText(SET_STASSID1 + Settings.sta_active))) { - Settings.sta_active ^= 1; - } - if (Settings.ip_address[0]) { - WiFi.config(Settings.ip_address[0], Settings.ip_address[1], Settings.ip_address[2], Settings.ip_address[3]); - } - WiFi.hostname(my_hostname); - - char stemp[40] = { 0 }; - if (channel) { - WiFi.begin(SettingsText(SET_STASSID1 + Settings.sta_active), SettingsText(SET_STAPWD1 + Settings.sta_active), channel, Wifi.bssid); - - char hex_char[18]; - snprintf_P(stemp, sizeof(stemp), PSTR(" Channel %d BSSId %s"), channel, ToHex_P((unsigned char*)Wifi.bssid, 6, hex_char, sizeof(hex_char), ':')); - } else { - WiFi.begin(SettingsText(SET_STASSID1 + Settings.sta_active), SettingsText(SET_STAPWD1 + Settings.sta_active)); - } - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTING_TO_AP "%d %s%s " D_IN_MODE " 11%c " D_AS " %s..."), - Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), stemp, kWifiPhyMode[WiFi.getPhyMode() & 0x3], my_hostname); - -#if LWIP_IPV6 - for (bool configured = false; !configured;) { - uint16_t cfgcnt = 0; - for (auto addr : addrList) { - if ((configured = !addr.isLocal() && addr.isV6()) || cfgcnt==30) { - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI "Got IPv6 global address %s"), addr.toString().c_str()); - break; - } - delay(500); - cfgcnt++; - } - } -#endif -} - -void WifiBeginAfterScan(void) -{ - - if (0 == Wifi.scan_state) { return; } - - if (1 == Wifi.scan_state) { - memset((void*) &Wifi.bssid, 0, sizeof(Wifi.bssid)); - Wifi.best_network_db = -127; - Wifi.scan_state = 3; - } - - if (2 == Wifi.scan_state) { - uint8_t* bssid = WiFi.BSSID(); - memcpy((void*) &Wifi.bssid, (void*) bssid, sizeof(Wifi.bssid)); - Wifi.best_network_db = WiFi.RSSI(); - if (Wifi.best_network_db < -WIFI_RSSI_THRESHOLD) { - Wifi.best_network_db += WIFI_RSSI_THRESHOLD; - } - Wifi.scan_state = 3; - } - - if (3 == Wifi.scan_state) { - if (WiFi.scanComplete() != WIFI_SCAN_RUNNING) { - WiFi.scanNetworks(true); - Wifi.scan_state++; - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, PSTR("Network (re)scan started...")); - return; - } - } - int8_t wifi_scan_result = WiFi.scanComplete(); - - if (4 == Wifi.scan_state) { - if (wifi_scan_result != WIFI_SCAN_RUNNING) { - Wifi.scan_state++; - } - } - - if (5 == Wifi.scan_state) { - int32_t channel = 0; - int8_t ap = 3; - uint8_t last_bssid[6]; - memcpy((void*) &last_bssid, (void*) &Wifi.bssid, sizeof(last_bssid)); - - if (wifi_scan_result > 0) { - - for (uint32_t i = 0; i < wifi_scan_result; ++i) { - - String ssid_scan; - int32_t rssi_scan; - uint8_t sec_scan; - uint8_t* bssid_scan; - int32_t chan_scan; - bool hidden_scan; - - WiFi.getNetworkInfo(i, ssid_scan, sec_scan, rssi_scan, bssid_scan, chan_scan, hidden_scan); - - bool known = false; - uint32_t j; - for (j = 0; j < MAX_SSIDS; j++) { - if (ssid_scan == SettingsText(SET_STASSID1 + j)) { - known = true; - if (rssi_scan > Wifi.best_network_db) { - if (sec_scan == ENC_TYPE_NONE || SettingsText(SET_STAPWD1 + j)) { - Wifi.best_network_db = (int8_t)rssi_scan; - channel = chan_scan; - ap = j; - memcpy((void*) &Wifi.bssid, (void*) bssid_scan, sizeof(Wifi.bssid)); - } - } - break; - } - } - char hex_char[18]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Network %d, AP%c, SSId %s, Channel %d, BSSId %s, RSSI %d, Encryption %d"), - i, - (known) ? (j) ? '2' : '1' : '-', - ssid_scan.c_str(), - chan_scan, - ToHex_P((unsigned char*)bssid_scan, 6, hex_char, sizeof(hex_char), ':'), - rssi_scan, - (sec_scan == ENC_TYPE_NONE) ? 0 : 1); - delay(0); - } - WiFi.scanDelete(); - delay(0); - } - Wifi.scan_state = 0; - - for (uint32_t i = 0; i < sizeof(Wifi.bssid); i++) { - if (last_bssid[i] != Wifi.bssid[i]) { - WifiBegin(ap, channel); - break; - } - } - } -} - -uint16_t WifiLinkCount(void) -{ - return Wifi.link_count; -} - -String WifiDowntime(void) -{ - return GetDuration(Wifi.downtime); -} - -void WifiSetState(uint8_t state) -{ - if (state == global_state.wifi_down) { - if (state) { - rules_flag.wifi_connected = 1; - Wifi.link_count++; - Wifi.downtime += UpTime() - Wifi.last_event; - } else { - rules_flag.wifi_disconnected = 1; - Wifi.last_event = UpTime(); - } - } - global_state.wifi_down = state ^1; -} - -#if LWIP_IPV6 -bool WifiCheckIPv6(void) -{ - bool ipv6_global=false; - - for (auto a : addrList) { - if(!a.isLocal() && a.isV6()) ipv6_global=true; - } - return ipv6_global; -} - -String WifiGetIPv6(void) -{ - for (auto a : addrList) { - if(!a.isLocal() && a.isV6()) return a.toString(); - } - return ""; -} - -bool WifiCheckIPAddrStatus(void) -{ - bool ip_global=false; - - for (auto a : addrList) { - if(!a.isLocal()) ip_global=true; - } - return ip_global; -} -#endif - -void WifiCheckIp(void) -{ -#if LWIP_IPV6 - if(WifiCheckIPAddrStatus()) { - Wifi.status = WL_CONNECTED; -#else - if ((WL_CONNECTED == WiFi.status()) && (static_cast(WiFi.localIP()) != 0)) { -#endif - WifiSetState(1); - Wifi.counter = WIFI_CHECK_SEC; - Wifi.retry = Wifi.retry_init; - if (Wifi.status != WL_CONNECTED) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECTED)); - - Settings.ip_address[1] = (uint32_t)WiFi.gatewayIP(); - Settings.ip_address[2] = (uint32_t)WiFi.subnetMask(); - Settings.ip_address[3] = (uint32_t)WiFi.dnsIP(); - - - Settings.wifi_channel = WiFi.channel(); - uint8_t *bssid = WiFi.BSSID(); - memcpy((void*) &Settings.wifi_bssid, (void*) bssid, sizeof(Settings.wifi_bssid)); - } - Wifi.status = WL_CONNECTED; -#ifdef USE_DISCOVERY -#ifdef WEBSERVER_ADVERTISE - if (2 == Wifi.mdns_begun) { - MDNS.update(); - AddLog_P(LOG_LEVEL_DEBUG_MORE, D_LOG_MDNS, "MDNS.update"); - } -#endif -#endif - } else { - WifiSetState(0); - uint8_t wifi_config_tool = Settings.sta_config; - Wifi.status = WiFi.status(); - switch (Wifi.status) { - case WL_CONNECTED: - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_NO_IP_ADDRESS)); - Wifi.status = 0; - Wifi.retry = Wifi.retry_init; - break; - case WL_NO_SSID_AVAIL: - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_NOT_REACHED)); - Settings.wifi_channel = 0; - if (WIFI_WAIT == Settings.sta_config) { - Wifi.retry = Wifi.retry_init; - } else { - if (Wifi.retry > (Wifi.retry_init / 2)) { - Wifi.retry = Wifi.retry_init / 2; - } - else if (Wifi.retry) { - Wifi.retry = 0; - } - } - break; - case WL_CONNECT_FAILED: - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_WRONG_PASSWORD)); - Settings.wifi_channel = 0; - if (Wifi.retry > (Wifi.retry_init / 2)) { - Wifi.retry = Wifi.retry_init / 2; - } - else if (Wifi.retry) { - Wifi.retry = 0; - } - break; - default: - if (!Wifi.retry || ((Wifi.retry_init / 2) == Wifi.retry)) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_TIMEOUT)); - Settings.wifi_channel = 0; - } else { - if (!strlen(SettingsText(SET_STASSID1)) && !strlen(SettingsText(SET_STASSID2))) { - Settings.wifi_channel = 0; - wifi_config_tool = WIFI_MANAGER; - Wifi.retry = 0; - } else { - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, PSTR(D_ATTEMPTING_CONNECTION)); - } - } - } - if (Wifi.retry) { - if (Settings.flag3.use_wifi_scan) { - if (Wifi.retry_init == Wifi.retry) { - Wifi.scan_state = 1; - } - } else { - if (Wifi.retry_init == Wifi.retry) { - WifiBegin(3, Settings.wifi_channel); - } - if ((Settings.sta_config != WIFI_WAIT) && ((Wifi.retry_init / 2) == Wifi.retry)) { - WifiBegin(2, 0); - } - } - Wifi.counter = 1; - Wifi.retry--; - } else { - WifiConfig(wifi_config_tool); - Wifi.counter = 1; - Wifi.retry = Wifi.retry_init; - } - } -} - -void WifiCheck(uint8_t param) -{ - Wifi.counter--; - switch (param) { - case WIFI_SERIAL: - case WIFI_MANAGER: - WifiConfig(param); - break; - default: - if (Wifi.config_counter) { - Wifi.config_counter--; - Wifi.counter = Wifi.config_counter +5; - if (Wifi.config_counter) { - if (!Wifi.config_counter) { - if (strlen(WiFi.SSID().c_str())) { - SettingsUpdateText(SET_STASSID1, WiFi.SSID().c_str()); - } - if (strlen(WiFi.psk().c_str())) { - SettingsUpdateText(SET_STAPWD1, WiFi.psk().c_str()); - } - Settings.sta_active = 0; - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_WCFG_2_WIFIMANAGER D_CMND_SSID "1 %s"), SettingsText(SET_STASSID1)); - } - } - if (!Wifi.config_counter) { - - restart_flag = 2; - } - } else { - if (Wifi.scan_state) { WifiBeginAfterScan(); } - - if (Wifi.counter <= 0) { - AddLog_P(LOG_LEVEL_DEBUG_MORE, S_LOG_WIFI, PSTR(D_CHECKING_CONNECTION)); - Wifi.counter = WIFI_CHECK_SEC; - WifiCheckIp(); - } -#if LWIP_IPV6 - if (WifiCheckIPAddrStatus()) { -#else - if ((WL_CONNECTED == WiFi.status()) && (static_cast(WiFi.localIP()) != 0) && !Wifi.config_type) { -#endif - WifiSetState(1); - - if (Settings.flag3.use_wifi_rescan) { - if (!(uptime % (60 * WIFI_RESCAN_MINUTES))) { - Wifi.scan_state = 2; - } - } - -#ifdef FIRMWARE_MINIMAL - if (1 == RtcSettings.ota_loader) { - RtcSettings.ota_loader = 0; - ota_state_flag = 3; - } -#endif - -#ifdef USE_DISCOVERY - if (Settings.flag3.mdns_enabled) { - if (!Wifi.mdns_begun) { - - - - - - Wifi.mdns_begun = (uint8_t)MDNS.begin(my_hostname); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS "%s"), (Wifi.mdns_begun) ? D_INITIALIZED : D_FAILED); - - } - } -#endif - -#ifdef USE_WEBSERVER - if (Settings.webserver) { - StartWebserver(Settings.webserver, WiFi.localIP()); -#ifdef USE_DISCOVERY -#ifdef WEBSERVER_ADVERTISE - if (1 == Wifi.mdns_begun) { - Wifi.mdns_begun = 2; - MDNS.addService("http", "tcp", WEB_PORT); - } -#endif -#endif - } else { - StopWebserver(); - } -#ifdef USE_EMULATION -#ifdef USE_DEVICE_GROUPS - if (Settings.flag2.emulation || Settings.flag4.device_groups_enabled) { UdpConnect(); } -#else - if (Settings.flag2.emulation) { UdpConnect(); } -#endif -#endif -#endif - -#ifdef USE_KNX - if (!knx_started && Settings.flag.knx_enabled) { - KNXStart(); - knx_started = true; - } -#endif - - } else { - WifiSetState(0); -#ifdef USE_EMULATION - UdpDisconnect(); -#endif - Wifi.mdns_begun = 0; -#ifdef USE_KNX - knx_started = false; -#endif - } - } - } -} - -int WifiState(void) -{ - int state = -1; - - if (!global_state.wifi_down) { state = WIFI_RESTART; } - if (Wifi.config_type) { state = Wifi.config_type; } - return state; -} - -String WifiGetOutputPower(void) -{ - char stemp1[TOPSZ]; - dtostrfd((float)(Settings.wifi_output_power) / 10, 1, stemp1); - return String(stemp1); -} - -void WifiSetOutputPower(void) -{ - WiFi.setOutputPower((float)(Settings.wifi_output_power) / 10); -} -# 633 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/support_wifi.ino" -#ifdef WIFI_RF_MODE_RF_CAL -#ifndef USE_DEEPSLEEP -RF_MODE(RF_CAL); -#endif -#endif - -#ifdef WIFI_RF_PRE_INIT -bool rf_pre_init_flag = false; -RF_PRE_INIT() -{ -#ifndef USE_DEEPSLEEP - system_deep_sleep_set_option(1); - system_phy_set_rfoption(RF_CAL); -#endif - system_phy_set_powerup_option(3); - rf_pre_init_flag = true; -} -#endif - -void WifiConnect(void) -{ - WifiSetState(0); - WifiSetOutputPower(); - WiFi.persistent(false); - Wifi.status = 0; - Wifi.retry_init = WIFI_RETRY_OFFSET_SEC + (ESP_getChipId() & 0xF); - Wifi.retry = Wifi.retry_init; - Wifi.counter = 1; - - memcpy((void*) &Wifi.bssid, (void*) Settings.wifi_bssid, sizeof(Wifi.bssid)); - -#ifdef WIFI_RF_PRE_INIT - if (rf_pre_init_flag) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Pre-init done")); - } -#endif -} - -void WifiShutdown(bool option = false) -{ - - - delay(100); - -#ifdef USE_EMULATION - UdpDisconnect(); - delay(100); -#endif - - if (Settings.flag.mqtt_enabled) { - MqttDisconnect(); - delay(100); - } - -#ifdef WIFI_FORCE_RF_CAL_ERASE - if (option) { - WiFi.disconnect(false); - SettingsErase(4); - } else -#endif - { - - - - - ETS_UART_INTR_DISABLE(); - wifi_station_disconnect(); - ETS_UART_INTR_ENABLE(); - - } - delay(100); -} - -void EspRestart(void) -{ - ResetPwm(); - WifiShutdown(true); - CrashDumpClear(); - - ESP_reset(); -} - -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 - - - -extern "C" { -#if LWIP_VERSION_MAJOR == 1 -#include "netif/wlan_lwip_if.h" -#include "netif/etharp.h" -#else -#include "lwip/etharp.h" -#endif -} - -unsigned long wifiTimer = 0; - -void stationKeepAliveNow(void) { - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_WIFI "Sending Gratuitous ARP")); - for (netif* interface = netif_list; interface != nullptr; interface = interface->next) - if ( - (interface->flags & NETIF_FLAG_LINK_UP) - && (interface->flags & NETIF_FLAG_UP) -#if LWIP_VERSION_MAJOR == 1 - && interface == eagle_lwip_getif(STATION_IF) - && (!ip_addr_isany(&interface->ip_addr)) -#else - && interface->num == STATION_IF - && (!ip4_addr_isany_val(*netif_ip4_addr(interface))) -#endif - ) - { - etharp_gratuitous(interface); - break; - } -} - -void wifiKeepAlive(void) { - uint32_t wifiTimerSec = Settings.param[P_ARP_GRATUITOUS]; - - if ((WL_CONNECTED != Wifi.status) || (0 == wifiTimerSec)) { return; } - - if (TimeReached(wifiTimer)) { - stationKeepAliveNow(); - if (wifiTimerSec > 100) { - wifiTimerSec = (wifiTimerSec - 100) * 60; - } - SetNextTimeInterval(wifiTimer, wifiTimerSec * 1000); - } -} -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/tasmota_ca.ino" -# 24 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/tasmota_ca.ino" -#ifdef USE_MQTT_TLS_CA_CERT - -#ifndef USE_MQTT_AWS_IOT -# 38 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/tasmota_ca.ino" -static const unsigned char PROGMEM TA0_DN[] = { - 0x30, 0x4A, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, - 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, - 0x13, 0x0D, 0x4C, 0x65, 0x74, 0x27, 0x73, 0x20, 0x45, 0x6E, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, - 0x13, 0x1A, 0x4C, 0x65, 0x74, 0x27, 0x73, 0x20, 0x45, 0x6E, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, - 0x79, 0x20, 0x58, 0x33 -}; - -static const unsigned char PROGMEM TA0_RSA_N[] = { - 0x9C, 0xD3, 0x0C, 0xF0, 0x5A, 0xE5, 0x2E, 0x47, 0xB7, 0x72, 0x5D, 0x37, - 0x83, 0xB3, 0x68, 0x63, 0x30, 0xEA, 0xD7, 0x35, 0x26, 0x19, 0x25, 0xE1, - 0xBD, 0xBE, 0x35, 0xF1, 0x70, 0x92, 0x2F, 0xB7, 0xB8, 0x4B, 0x41, 0x05, - 0xAB, 0xA9, 0x9E, 0x35, 0x08, 0x58, 0xEC, 0xB1, 0x2A, 0xC4, 0x68, 0x87, - 0x0B, 0xA3, 0xE3, 0x75, 0xE4, 0xE6, 0xF3, 0xA7, 0x62, 0x71, 0xBA, 0x79, - 0x81, 0x60, 0x1F, 0xD7, 0x91, 0x9A, 0x9F, 0xF3, 0xD0, 0x78, 0x67, 0x71, - 0xC8, 0x69, 0x0E, 0x95, 0x91, 0xCF, 0xFE, 0xE6, 0x99, 0xE9, 0x60, 0x3C, - 0x48, 0xCC, 0x7E, 0xCA, 0x4D, 0x77, 0x12, 0x24, 0x9D, 0x47, 0x1B, 0x5A, - 0xEB, 0xB9, 0xEC, 0x1E, 0x37, 0x00, 0x1C, 0x9C, 0xAC, 0x7B, 0xA7, 0x05, - 0xEA, 0xCE, 0x4A, 0xEB, 0xBD, 0x41, 0xE5, 0x36, 0x98, 0xB9, 0xCB, 0xFD, - 0x6D, 0x3C, 0x96, 0x68, 0xDF, 0x23, 0x2A, 0x42, 0x90, 0x0C, 0x86, 0x74, - 0x67, 0xC8, 0x7F, 0xA5, 0x9A, 0xB8, 0x52, 0x61, 0x14, 0x13, 0x3F, 0x65, - 0xE9, 0x82, 0x87, 0xCB, 0xDB, 0xFA, 0x0E, 0x56, 0xF6, 0x86, 0x89, 0xF3, - 0x85, 0x3F, 0x97, 0x86, 0xAF, 0xB0, 0xDC, 0x1A, 0xEF, 0x6B, 0x0D, 0x95, - 0x16, 0x7D, 0xC4, 0x2B, 0xA0, 0x65, 0xB2, 0x99, 0x04, 0x36, 0x75, 0x80, - 0x6B, 0xAC, 0x4A, 0xF3, 0x1B, 0x90, 0x49, 0x78, 0x2F, 0xA2, 0x96, 0x4F, - 0x2A, 0x20, 0x25, 0x29, 0x04, 0xC6, 0x74, 0xC0, 0xD0, 0x31, 0xCD, 0x8F, - 0x31, 0x38, 0x95, 0x16, 0xBA, 0xA8, 0x33, 0xB8, 0x43, 0xF1, 0xB1, 0x1F, - 0xC3, 0x30, 0x7F, 0xA2, 0x79, 0x31, 0x13, 0x3D, 0x2D, 0x36, 0xF8, 0xE3, - 0xFC, 0xF2, 0x33, 0x6A, 0xB9, 0x39, 0x31, 0xC5, 0xAF, 0xC4, 0x8D, 0x0D, - 0x1D, 0x64, 0x16, 0x33, 0xAA, 0xFA, 0x84, 0x29, 0xB6, 0xD4, 0x0B, 0xC0, - 0xD8, 0x7D, 0xC3, 0x93 -}; - -static const unsigned char TA0_RSA_E[] = { - 0x01, 0x00, 0x01 -}; - -static const br_x509_trust_anchor PROGMEM LetsEncryptX3CrossSigned_TA = { - { (unsigned char *)TA0_DN, sizeof TA0_DN }, - BR_X509_TA_CA, - { - BR_KEYTYPE_RSA, - { .rsa = { - (unsigned char *)TA0_RSA_N, sizeof TA0_RSA_N, - (unsigned char *)TA0_RSA_E, sizeof TA0_RSA_E, - } } - } -}; - -#define TAs_NUM 1 - -#endif - -#ifdef USE_MQTT_AWS_IOT -# 106 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/tasmota_ca.ino" -const unsigned char PROGMEM TA0_DN[] = { - 0x30, 0x39, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, - 0x02, 0x55, 0x53, 0x31, 0x0F, 0x30, 0x0D, 0x06, 0x03, 0x55, 0x04, 0x0A, - 0x13, 0x06, 0x41, 0x6D, 0x61, 0x7A, 0x6F, 0x6E, 0x31, 0x19, 0x30, 0x17, - 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, 0x41, 0x6D, 0x61, 0x7A, 0x6F, - 0x6E, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x43, 0x41, 0x20, 0x31 -}; - -const unsigned char PROGMEM TA0_RSA_N[] = { - 0xB2, 0x78, 0x80, 0x71, 0xCA, 0x78, 0xD5, 0xE3, 0x71, 0xAF, 0x47, 0x80, - 0x50, 0x74, 0x7D, 0x6E, 0xD8, 0xD7, 0x88, 0x76, 0xF4, 0x99, 0x68, 0xF7, - 0x58, 0x21, 0x60, 0xF9, 0x74, 0x84, 0x01, 0x2F, 0xAC, 0x02, 0x2D, 0x86, - 0xD3, 0xA0, 0x43, 0x7A, 0x4E, 0xB2, 0xA4, 0xD0, 0x36, 0xBA, 0x01, 0xBE, - 0x8D, 0xDB, 0x48, 0xC8, 0x07, 0x17, 0x36, 0x4C, 0xF4, 0xEE, 0x88, 0x23, - 0xC7, 0x3E, 0xEB, 0x37, 0xF5, 0xB5, 0x19, 0xF8, 0x49, 0x68, 0xB0, 0xDE, - 0xD7, 0xB9, 0x76, 0x38, 0x1D, 0x61, 0x9E, 0xA4, 0xFE, 0x82, 0x36, 0xA5, - 0xE5, 0x4A, 0x56, 0xE4, 0x45, 0xE1, 0xF9, 0xFD, 0xB4, 0x16, 0xFA, 0x74, - 0xDA, 0x9C, 0x9B, 0x35, 0x39, 0x2F, 0xFA, 0xB0, 0x20, 0x50, 0x06, 0x6C, - 0x7A, 0xD0, 0x80, 0xB2, 0xA6, 0xF9, 0xAF, 0xEC, 0x47, 0x19, 0x8F, 0x50, - 0x38, 0x07, 0xDC, 0xA2, 0x87, 0x39, 0x58, 0xF8, 0xBA, 0xD5, 0xA9, 0xF9, - 0x48, 0x67, 0x30, 0x96, 0xEE, 0x94, 0x78, 0x5E, 0x6F, 0x89, 0xA3, 0x51, - 0xC0, 0x30, 0x86, 0x66, 0xA1, 0x45, 0x66, 0xBA, 0x54, 0xEB, 0xA3, 0xC3, - 0x91, 0xF9, 0x48, 0xDC, 0xFF, 0xD1, 0xE8, 0x30, 0x2D, 0x7D, 0x2D, 0x74, - 0x70, 0x35, 0xD7, 0x88, 0x24, 0xF7, 0x9E, 0xC4, 0x59, 0x6E, 0xBB, 0x73, - 0x87, 0x17, 0xF2, 0x32, 0x46, 0x28, 0xB8, 0x43, 0xFA, 0xB7, 0x1D, 0xAA, - 0xCA, 0xB4, 0xF2, 0x9F, 0x24, 0x0E, 0x2D, 0x4B, 0xF7, 0x71, 0x5C, 0x5E, - 0x69, 0xFF, 0xEA, 0x95, 0x02, 0xCB, 0x38, 0x8A, 0xAE, 0x50, 0x38, 0x6F, - 0xDB, 0xFB, 0x2D, 0x62, 0x1B, 0xC5, 0xC7, 0x1E, 0x54, 0xE1, 0x77, 0xE0, - 0x67, 0xC8, 0x0F, 0x9C, 0x87, 0x23, 0xD6, 0x3F, 0x40, 0x20, 0x7F, 0x20, - 0x80, 0xC4, 0x80, 0x4C, 0x3E, 0x3B, 0x24, 0x26, 0x8E, 0x04, 0xAE, 0x6C, - 0x9A, 0xC8, 0xAA, 0x0D -}; - -static const unsigned char PROGMEM TA0_RSA_E[] = { - 0x01, 0x00, 0x01 -}; - -const br_x509_trust_anchor PROGMEM AmazonRootCA1_TA = { - { (unsigned char *)TA0_DN, sizeof TA0_DN }, - BR_X509_TA_CA, - { - BR_KEYTYPE_RSA, - { .rsa = { - (unsigned char *)TA0_RSA_N, sizeof TA0_RSA_N, - (unsigned char *)TA0_RSA_E, sizeof TA0_RSA_E, - } } - } -}; - -#define TAs_NUM 1 - -#endif - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_01_webserver.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_01_webserver.ino" -#ifdef USE_WEBSERVER - - - - - - - -#define XDRV_01 1 - -#ifndef WIFI_SOFT_AP_CHANNEL -#define WIFI_SOFT_AP_CHANNEL 1 -#endif - -const uint16_t CHUNKED_BUFFER_SIZE = (MESSZ / 2) - 100; - -const uint16_t HTTP_REFRESH_TIME = 2345; -#define HTTP_RESTART_RECONNECT_TIME 9000 -#define HTTP_OTA_RESTART_RECONNECT_TIME 20000 - -#include -#include - -#ifdef USE_RF_FLASH -uint8_t *efm8bb1_update = nullptr; -#endif - -enum UploadTypes { UPL_TASMOTA, UPL_SETTINGS, UPL_EFM8BB1, UPL_TASMOTASLAVE }; - -static const char * HEADER_KEYS[] = { "User-Agent", }; - -const char HTTP_HEADER1[] PROGMEM = - "" - "" - "" - "" - "%s - %s" - - ""; - -const char HTTP_HEAD_STYLE1[] PROGMEM = - "" - - "" - "" - "
" -#ifdef FIRMWARE_MINIMAL - "

" D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "

" -#endif - "
" -#ifdef LANGUAGE_MODULE_NAME - "

" D_MODULE " %s

" -#else - "

%s " D_MODULE "

" -#endif - "

%s

"; - -const char HTTP_MSG_SLIDER_GRADIENT[] PROGMEM = - "
" - "" - "
"; -const char HTTP_MSG_SLIDER_SHUTTER[] PROGMEM = - "
" D_CLOSE "" D_OPEN "
" - "
"; - -const char HTTP_MSG_RSTRT[] PROGMEM = - "
" D_DEVICE_WILL_RESTART "

"; - -const char HTTP_FORM_LOGIN[] PROGMEM = - "
" - "" - "

" D_USER "

" - "

" D_PASSWORD "

" - "
" - "" - "
"; - -const char HTTP_FORM_TEMPLATE[] PROGMEM = - "
 " D_TEMPLATE_PARAMETERS " " - "
"; -const char HTTP_FORM_TEMPLATE_FLAG[] PROGMEM = - "

" - "
 " D_TEMPLATE_FLAGS " 

" - - "

"; - -const char HTTP_FORM_MODULE[] PROGMEM = - "
 " D_MODULE_PARAMETERS " " - "" - "

" D_MODULE_TYPE " (%s)

" - "
"; - -const char HTTP_FORM_WIFI[] PROGMEM = - "
 " D_WIFI_PARAMETERS " " - "" - "

" D_AP1_SSID " (" STA_SSID1 ")

" - "


" - "

" D_AP2_SSID " (" STA_SSID2 ")

" - "


" - "

" D_HOSTNAME " (%s)

" - "

" D_CORS_DOMAIN "

"; - -const char HTTP_FORM_LOG1[] PROGMEM = - "
 " D_LOGGING_PARAMETERS " " - ""; -const char HTTP_FORM_LOG2[] PROGMEM = - "

" D_SYSLOG_HOST " (" SYS_LOG_HOST ")

" - "

" D_SYSLOG_PORT " (" STR(SYS_LOG_PORT) ")

" - "

" D_TELEMETRY_PERIOD " (" STR(TELE_PERIOD) ")

"; - -const char HTTP_FORM_OTHER[] PROGMEM = - "
 " D_OTHER_PARAMETERS " " - "" - "

" - "
 " D_TEMPLATE " " - "

" - "

" - "
" - "
" - "

" - "
" - "
" - "
"; - -const char HTTP_FORM_END[] PROGMEM = - "
" - "" - "
"; - -const char HTTP_FORM_RST[] PROGMEM = - "
" - "
 " D_RESTORE_CONFIGURATION " "; -const char HTTP_FORM_UPG[] PROGMEM = - "
" - "
 " D_UPGRADE_BY_WEBSERVER " " - "
" - "
" D_OTA_URL "

" - "
" - "


" - "
 " D_UPGRADE_BY_FILE_UPLOAD " "; -const char HTTP_FORM_RST_UPG[] PROGMEM = - "
" - "

" - "
" - "
" - "
" - ""; - -const char HTTP_FORM_CMND[] PROGMEM = - "


" - "
" - "
" - - ""; - -const char HTTP_TABLE100[] PROGMEM = - "
"; - -const char HTTP_COUNTER[] PROGMEM = - "
"; - -const char HTTP_END[] PROGMEM = - "" - "" - "" - ""; - -const char HTTP_DEVICE_CONTROL[] PROGMEM = ""; -const char HTTP_DEVICE_STATE[] PROGMEM = ""; - -enum ButtonTitle { - BUTTON_RESTART, BUTTON_RESET_CONFIGURATION, - BUTTON_MAIN, BUTTON_CONFIGURATION, BUTTON_INFORMATION, BUTTON_FIRMWARE_UPGRADE, BUTTON_CONSOLE, - BUTTON_MODULE, BUTTON_WIFI, BUTTON_LOGGING, BUTTON_OTHER, BUTTON_TEMPLATE, BUTTON_BACKUP, BUTTON_RESTORE }; -const char kButtonTitle[] PROGMEM = - D_RESTART "|" D_RESET_CONFIGURATION "|" - D_MAIN_MENU "|" D_CONFIGURATION "|" D_INFORMATION "|" D_FIRMWARE_UPGRADE "|" D_CONSOLE "|" - D_CONFIGURE_MODULE "|" D_CONFIGURE_WIFI"|" D_CONFIGURE_LOGGING "|" D_CONFIGURE_OTHER "|" D_CONFIGURE_TEMPLATE "|" D_BACKUP_CONFIGURATION "|" D_RESTORE_CONFIGURATION; -const char kButtonAction[] PROGMEM = - ".|rt|" - ".|cn|in|up|cs|" - "md|wi|lg|co|tp|dl|rs"; -const char kButtonConfirm[] PROGMEM = D_CONFIRM_RESTART "|" D_CONFIRM_RESET_CONFIGURATION; - -enum CTypes { CT_HTML, CT_PLAIN, CT_XML, CT_JSON, CT_STREAM }; -const char kContentTypes[] PROGMEM = "text/html|text/plain|text/xml|application/json|application/octet-stream"; - -const char kLoggingOptions[] PROGMEM = D_SERIAL_LOG_LEVEL "|" D_WEB_LOG_LEVEL "|" D_MQTT_LOG_LEVEL "|" D_SYS_LOG_LEVEL; -const char kLoggingLevels[] PROGMEM = D_NONE "|" D_ERROR "|" D_INFO "|" D_DEBUG "|" D_MORE_DEBUG; - -const char kEmulationOptions[] PROGMEM = D_NONE "|" D_BELKIN_WEMO "|" D_HUE_BRIDGE; - -const char kUploadErrors[] PROGMEM = - D_UPLOAD_ERR_1 "|" D_UPLOAD_ERR_2 "|" D_UPLOAD_ERR_3 "|" D_UPLOAD_ERR_4 "|" D_UPLOAD_ERR_5 "|" D_UPLOAD_ERR_6 "|" D_UPLOAD_ERR_7 "|" D_UPLOAD_ERR_8 "|" D_UPLOAD_ERR_9 -#ifdef USE_RF_FLASH - "|" D_UPLOAD_ERR_10 "|" D_UPLOAD_ERR_11 "|" D_UPLOAD_ERR_12 "|" D_UPLOAD_ERR_13 -#endif - "|" D_UPLOAD_ERR_14 - ; - -const uint16_t DNS_PORT = 53; -enum HttpOptions {HTTP_OFF, HTTP_USER, HTTP_ADMIN, HTTP_MANAGER, HTTP_MANAGER_RESET_ONLY}; - -DNSServer *DnsServer; -ESP8266WebServer *Webserver; - -struct WEB { - String chunk_buffer = ""; - bool reset_web_log_flag = false; - uint8_t state = HTTP_OFF; - uint8_t upload_error = 0; - uint8_t upload_file_type; - uint8_t upload_progress_dot_count; - uint8_t config_block_count = 0; - uint8_t config_xor_on = 0; - uint8_t config_xor_on_set = CONFIG_FILE_XOR; -} Web; - - -static void WebGetArg(const char* arg, char* out, size_t max) -{ - String s = Webserver->arg(arg); - strlcpy(out, s.c_str(), max); - -} - -static bool WifiIsInManagerMode(){ - return (HTTP_MANAGER == Web.state || HTTP_MANAGER_RESET_ONLY == Web.state); -} - -void ShowWebSource(uint32_t source) -{ - if ((source > 0) && (source < SRC_MAX)) { - char stemp1[20]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SRC: %s from %s"), GetTextIndexed(stemp1, sizeof(stemp1), source, kCommandSource), Webserver->client().remoteIP().toString().c_str()); - } -} - -void ExecuteWebCommand(char* svalue, uint32_t source) -{ - ShowWebSource(source); - last_source = source; - ExecuteCommand(svalue, SRC_IGNORE); -} - -void StartWebserver(int type, IPAddress ipweb) -{ - if (!Settings.web_refresh) { Settings.web_refresh = HTTP_REFRESH_TIME; } - if (!Web.state) { - if (!Webserver) { - Webserver = new ESP8266WebServer((HTTP_MANAGER == type || HTTP_MANAGER_RESET_ONLY == type) ? 80 : WEB_PORT); - Webserver->on("/", HandleRoot); - Webserver->onNotFound(HandleNotFound); - Webserver->on("/up", HandleUpgradeFirmware); - Webserver->on("/u1", HandleUpgradeFirmwareStart); - Webserver->on("/u2", HTTP_POST, HandleUploadDone, HandleUploadLoop); - Webserver->on("/u2", HTTP_OPTIONS, HandlePreflightRequest); - Webserver->on("/cs", HTTP_GET, HandleConsole); - Webserver->on("/cs", HTTP_OPTIONS, HandlePreflightRequest); - Webserver->on("/cm", HandleHttpCommand); -#ifndef FIRMWARE_MINIMAL - Webserver->on("/cn", HandleConfiguration); - Webserver->on("/md", HandleModuleConfiguration); - Webserver->on("/wi", HandleWifiConfiguration); - Webserver->on("/lg", HandleLoggingConfiguration); - Webserver->on("/tp", HandleTemplateConfiguration); - Webserver->on("/co", HandleOtherConfiguration); - Webserver->on("/dl", HandleBackupConfiguration); - Webserver->on("/rs", HandleRestoreConfiguration); - Webserver->on("/rt", HandleResetConfiguration); - Webserver->on("/in", HandleInformation); - XdrvCall(FUNC_WEB_ADD_HANDLER); - XsnsCall(FUNC_WEB_ADD_HANDLER); -#endif - } - Web.reset_web_log_flag = false; - - - - Webserver->collectHeaders(HEADER_KEYS, sizeof(HEADER_KEYS)/sizeof(char*)); - - Webserver->begin(); - } - if (Web.state != type) { -#if LWIP_IPV6 - String ipv6_addr = WifiGetIPv6(); - if(ipv6_addr!="") AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s and IPv6 global address %s "), my_hostname, (Wifi.mdns_begun) ? ".local" : "", ipweb.toString().c_str(),ipv6_addr.c_str()); - else AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s"), my_hostname, (Wifi.mdns_begun) ? ".local" : "", ipweb.toString().c_str()); -#else - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s"), my_hostname, (Wifi.mdns_begun) ? ".local" : "", ipweb.toString().c_str()); -#endif - rules_flag.http_init = 1; - } - if (type) { Web.state = type; } -} - -void StopWebserver(void) -{ - if (Web.state) { - Webserver->close(); - Web.state = HTTP_OFF; - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_STOPPED)); - } -} - -void WifiManagerBegin(bool reset_only) -{ - - if (!global_state.wifi_down) { - - WifiSetMode(WIFI_AP_STA); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION)); - } else { - - WifiSetMode(WIFI_AP); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_WIFIMANAGER_SET_ACCESSPOINT)); - } - - StopWebserver(); - - DnsServer = new DNSServer(); - - int channel = WIFI_SOFT_AP_CHANNEL; - if ((channel < 1) || (channel > 13)) { channel = 1; } - -#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 - - WiFi.softAP(my_hostname, WIFI_AP_PASSPHRASE, channel, 0); -#else - - WiFi.softAP(my_hostname, WIFI_AP_PASSPHRASE, channel, 0, 1); -#endif - - delay(500); - - DnsServer->setErrorReplyCode(DNSReplyCode::NoError); - DnsServer->start(DNS_PORT, "*", WiFi.softAPIP()); - - StartWebserver((reset_only ? HTTP_MANAGER_RESET_ONLY : HTTP_MANAGER), WiFi.softAPIP()); -} - -void PollDnsWebserver(void) -{ - if (DnsServer) { DnsServer->processNextRequest(); } - if (Webserver) { Webserver->handleClient(); } -} - - - -bool WebAuthenticate(void) -{ - if (strlen(SettingsText(SET_WEBPWD)) && (HTTP_MANAGER_RESET_ONLY != Web.state)) { - return Webserver->authenticate(WEB_USERNAME, SettingsText(SET_WEBPWD)); - } else { - return true; - } -} - -bool HttpCheckPriviledgedAccess(bool autorequestauth = true) -{ - if (HTTP_USER == Web.state) { - HandleRoot(); - return false; - } - if (autorequestauth && !WebAuthenticate()) { - Webserver->requestAuthentication(); - return false; - } - return true; -} - -void HttpHeaderCors(void) -{ - if (strlen(SettingsText(SET_CORS))) { - Webserver->sendHeader(F("Access-Control-Allow-Origin"), SettingsText(SET_CORS)); - } -} - -void WSHeaderSend(void) -{ - Webserver->sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate")); - Webserver->sendHeader(F("Pragma"), F("no-cache")); - Webserver->sendHeader(F("Expires"), F("-1")); - HttpHeaderCors(); -} - - - - - -void WSSend(int code, int ctype, const String& content) -{ - char ct[25]; - Webserver->send(code, GetTextIndexed(ct, sizeof(ct), ctype, kContentTypes), content); -} - - - - - -void WSContentBegin(int code, int ctype) -{ - Webserver->client().flush(); - WSHeaderSend(); -#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 - Webserver->sendHeader(F("Accept-Ranges"),F("none")); - Webserver->sendHeader(F("Transfer-Encoding"),F("chunked")); -#endif - Webserver->setContentLength(CONTENT_LENGTH_UNKNOWN); - WSSend(code, ctype, ""); - Web.chunk_buffer = ""; -} - -void _WSContentSend(const String& content) -{ - size_t len = content.length(); - -#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 - const char * footer = "\r\n"; - char chunk_size[11]; - sprintf(chunk_size, "%x\r\n", len); - Webserver->sendContent(String() + chunk_size + content + footer); -#else - Webserver->sendContent(content); -#endif - -#ifdef USE_DEBUG_DRIVER - ShowFreeMem(PSTR("WSContentSend")); -#endif - DEBUG_CORE_LOG(PSTR("WEB: Chunk size %d/%d"), len, sizeof(mqtt_data)); -} - -void WSContentFlush(void) -{ - if (Web.chunk_buffer.length() > 0) { - _WSContentSend(Web.chunk_buffer); - Web.chunk_buffer = ""; - } -} - -void _WSContentSendBuffer(void) -{ - int len = strlen(mqtt_data); - - if (0 == len) { - return; - } - else if (len == sizeof(mqtt_data)) { - AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: Content too large")); - } - else if (len < CHUNKED_BUFFER_SIZE) { - Web.chunk_buffer += mqtt_data; - len = Web.chunk_buffer.length(); - } - - if (len >= CHUNKED_BUFFER_SIZE) { - WSContentFlush(); - } - if (strlen(mqtt_data) >= CHUNKED_BUFFER_SIZE) { - _WSContentSend(mqtt_data); - } -} - -void WSContentSend_P(const char* formatP, ...) -{ - - va_list arg; - va_start(arg, formatP); - int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), formatP, arg); - va_end(arg); - -#ifdef DEBUG_TASMOTA_CORE - if (len > (sizeof(mqtt_data) -1)) { - mqtt_data[33] = '\0'; - DEBUG_CORE_LOG(PSTR("ERROR: WSContentSend_P size %d > mqtt_data size %d. Start of data [%s...]"), len, sizeof(mqtt_data), mqtt_data); - } -#endif - - _WSContentSendBuffer(); -} - -void WSContentSend_PD(const char* formatP, ...) -{ - - va_list arg; - va_start(arg, formatP); - int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), formatP, arg); - va_end(arg); - -#ifdef DEBUG_TASMOTA_CORE - if (len > (sizeof(mqtt_data) -1)) { - mqtt_data[33] = '\0'; - DEBUG_CORE_LOG(PSTR("ERROR: WSContentSend_PD size %d > mqtt_data size %d. Start of data [%s...]"), len, sizeof(mqtt_data), mqtt_data); - } -#endif - - if (D_DECIMAL_SEPARATOR[0] != '.') { - for (uint32_t i = 0; i < len; i++) { - if ('.' == mqtt_data[i]) { - mqtt_data[i] = D_DECIMAL_SEPARATOR[0]; - } - } - } - - _WSContentSendBuffer(); -} - -void WSContentStart_P(const char* title, bool auth) -{ - if (auth && strlen(SettingsText(SET_WEBPWD)) && !Webserver->authenticate(WEB_USERNAME, SettingsText(SET_WEBPWD))) { - return Webserver->requestAuthentication(); - } - - WSContentBegin(200, CT_HTML); - - if (title != nullptr) { - char ctitle[strlen_P(title) +1]; - strcpy_P(ctitle, title); - WSContentSend_P(HTTP_HEADER1, SettingsText(SET_FRIENDLYNAME1), ctitle); - } -} - -void WSContentStart_P(const char* title) -{ - WSContentStart_P(title, true); -} - -void WSContentSendStyle_P(const char* formatP, ...) -{ - if (WifiIsInManagerMode()) { - if (WifiConfigCounter()) { - WSContentSend_P(HTTP_SCRIPT_COUNTER); - } - } - WSContentSend_P(HTTP_HEAD_LAST_SCRIPT); - - WSContentSend_P(HTTP_HEAD_STYLE1, WebColor(COL_FORM), WebColor(COL_INPUT), WebColor(COL_INPUT_TEXT), WebColor(COL_INPUT), - WebColor(COL_INPUT_TEXT), WebColor(COL_CONSOLE), WebColor(COL_CONSOLE_TEXT), WebColor(COL_BACKGROUND)); - WSContentSend_P(HTTP_HEAD_STYLE2, WebColor(COL_BUTTON), WebColor(COL_BUTTON_TEXT), WebColor(COL_BUTTON_HOVER), - WebColor(COL_BUTTON_RESET), WebColor(COL_BUTTON_RESET_HOVER), WebColor(COL_BUTTON_SAVE), WebColor(COL_BUTTON_SAVE_HOVER), - WebColor(COL_BUTTON)); - if (formatP != nullptr) { - - va_list arg; - va_start(arg, formatP); - int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), formatP, arg); - va_end(arg); - -#ifdef DEBUG_TASMOTA_CORE - if (len > (sizeof(mqtt_data) -1)) { - mqtt_data[33] = '\0'; - DEBUG_CORE_LOG(PSTR("ERROR: WSContentSendStyle_P size %d > mqtt_data size %d. Start of data [%s...]"), len, sizeof(mqtt_data), mqtt_data); - } -#endif - - _WSContentSendBuffer(); - } - WSContentSend_P(HTTP_HEAD_STYLE3, WebColor(COL_TEXT), -#ifdef FIRMWARE_MINIMAL - WebColor(COL_TEXT_WARNING), -#endif - WebColor(COL_TITLE), - ModuleName().c_str(), SettingsText(SET_FRIENDLYNAME1)); - if (Settings.flag3.gui_hostname_ip) { - bool lip = (static_cast(WiFi.localIP()) != 0); - bool sip = (static_cast(WiFi.softAPIP()) != 0); - WSContentSend_P(PSTR("

%s%s (%s%s%s)

"), - my_hostname, - (Wifi.mdns_begun) ? ".local" : "", - (lip) ? WiFi.localIP().toString().c_str() : "", - (lip && sip) ? ", " : "", - (sip) ? WiFi.softAPIP().toString().c_str() : ""); - } - WSContentSend_P(PSTR("")); -} - -void WSContentSendStyle(void) -{ - WSContentSendStyle_P(nullptr); -} - -void WSContentButton(uint32_t title_index) -{ - char action[4]; - char title[100]; - - if (title_index <= BUTTON_RESET_CONFIGURATION) { - char confirm[100]; - WSContentSend_P(PSTR("

"), - GetTextIndexed(action, sizeof(action), title_index, kButtonAction), - GetTextIndexed(confirm, sizeof(confirm), title_index, kButtonConfirm), - (!title_index) ? "rst" : "non", - GetTextIndexed(title, sizeof(title), title_index, kButtonTitle)); - } else { - WSContentSend_P(PSTR("

"), - GetTextIndexed(action, sizeof(action), title_index, kButtonAction), - GetTextIndexed(title, sizeof(title), title_index, kButtonTitle)); - } -} - -void WSContentSpaceButton(uint32_t title_index) -{ - WSContentSend_P(PSTR("
")); - WSContentButton(title_index); -} - -void WSContentSend_THD(const char *types, float f_temperature, float f_humidity) -{ - char parameter[FLOATSZ]; - dtostrfd(f_temperature, Settings.flag2.temperature_resolution, parameter); - WSContentSend_PD(HTTP_SNS_TEMP, types, parameter, TempUnit()); - dtostrfd(f_humidity, Settings.flag2.humidity_resolution, parameter); - WSContentSend_PD(HTTP_SNS_HUM, types, parameter); - dtostrfd(CalcTempHumToDew(f_temperature, f_humidity), Settings.flag2.temperature_resolution, parameter); - WSContentSend_PD(HTTP_SNS_DEW, types, parameter, TempUnit()); -} - -void WSContentEnd(void) -{ - WSContentFlush(); - _WSContentSend(""); - Webserver->client().stop(); -} - -void WSContentStop(void) -{ - if (WifiIsInManagerMode()) { - if (WifiConfigCounter()) { - WSContentSend_P(HTTP_COUNTER); - } - } - WSContentSend_P(HTTP_END, my_version); - WSContentEnd(); -} - - - -void WebRestart(uint32_t type) -{ - - - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTART); - - bool reset_only = (HTTP_MANAGER_RESET_ONLY == Web.state); - - WSContentStart_P((type) ? S_SAVE_CONFIGURATION : S_RESTART, !reset_only); - WSContentSend_P(HTTP_SCRIPT_RELOAD); - WSContentSendStyle(); - if (type) { - WSContentSend_P(PSTR("
" D_CONFIGURATION_SAVED "
")); - if (2 == type) { - WSContentSend_P(PSTR("
" D_TRYING_TO_CONNECT "
")); - } - WSContentSend_P(PSTR("
")); - } - WSContentSend_P(HTTP_MSG_RSTRT); - if (HTTP_MANAGER == Web.state || reset_only) { - Web.state = HTTP_ADMIN; - } else { - WSContentSpaceButton(BUTTON_MAIN); - } - WSContentStop(); - - ShowWebSource(SRC_WEBGUI); - restart_flag = 2; -} - - - -void HandleWifiLogin(void) -{ - WSContentStart_P(S_CONFIGURE_WIFI, false); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_LOGIN); - - if (HTTP_MANAGER_RESET_ONLY == Web.state) { - WSContentSpaceButton(BUTTON_RESTART); -#ifndef FIRMWARE_MINIMAL - WSContentSpaceButton(BUTTON_RESET_CONFIGURATION); -#endif - } - - WSContentStop(); -} - -#ifdef USE_LIGHT -void WebSliderColdWarm(void) -{ - WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, - "a", - "#fff", "#ff0", - 1, - 153, 500, - LightGetColorTemp(), - 't', 0); -} -#endif - -void HandleRoot(void) -{ - if (CaptivePortal()) { return; } - - if (Webserver->hasArg("rst")) { - WebRestart(0); - return; - } - - if (WifiIsInManagerMode()) { -#ifndef FIRMWARE_MINIMAL - if (strlen(SettingsText(SET_WEBPWD)) && !(Webserver->hasArg("USER1")) && !(Webserver->hasArg("PASS1")) && HTTP_MANAGER_RESET_ONLY != Web.state) { - HandleWifiLogin(); - } else { - if (!strlen(SettingsText(SET_WEBPWD)) || (((Webserver->arg("USER1") == WEB_USERNAME ) && (Webserver->arg("PASS1") == SettingsText(SET_WEBPWD) )) || HTTP_MANAGER_RESET_ONLY == Web.state)) { - HandleWifiConfiguration(); - } else { - - HandleWifiLogin(); - } - } -#endif - return; - } - - if (HandleRootStatusRefresh()) { - return; - } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_MAIN_MENU); - - char stemp[33]; - - WSContentStart_P(S_MAIN_MENU); -#ifdef USE_SCRIPT_WEB_DISPLAY - WSContentSend_P(HTTP_SCRIPT_ROOT, Settings.web_refresh, Settings.web_refresh); -#else - WSContentSend_P(HTTP_SCRIPT_ROOT, Settings.web_refresh); -#endif - WSContentSend_P(HTTP_SCRIPT_ROOT_PART2); - - WSContentSendStyle(); - - WSContentSend_P(PSTR("
")); - if (devices_present) { -#ifdef USE_LIGHT - if (light_type) { - uint8_t light_subtype = light_type &7; - if (!Settings.flag3.pwm_multi_channels) { - bool split_white = ((LST_RGBW <= light_subtype) && (devices_present > 1)); - - if ((LST_COLDWARM == light_subtype) || ((LST_RGBCW == light_subtype) && !split_white)) { - WebSliderColdWarm(); - } - - if (light_subtype > 2) { - uint16_t hue; - uint8_t sat; - LightGetHSB(&hue, &sat, nullptr); - - WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, - "b", - "#800", "#f00 5%,#ff0 20%,#0f0 35%,#0ff 50%,#00f 65%,#f0f 80%,#f00 95%,#800", - 2, - 1, 359, - hue, - 'h', 0); - - uint8_t dcolor = changeUIntScale(Settings.light_dimmer, 0, 100, 0, 255); - char scolor[8]; - snprintf_P(scolor, sizeof(scolor), PSTR("#%02X%02X%02X"), dcolor, dcolor, dcolor); - uint8_t red, green, blue; - LightHsToRgb(hue, 255, &red, &green, &blue); - snprintf_P(stemp, sizeof(stemp), PSTR("#%02X%02X%02X"), red, green, blue); - - WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, - "s", - scolor, stemp, - 3, - 0, 100, - changeUIntScale(sat, 0, 255, 0, 100), - 'n', 0); - } - - WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, - "c", - "#000", "#fff", - 4, - Settings.flag3.slider_dimmer_stay_on, 100, - Settings.light_dimmer, - 'd', 0); - - if (split_white) { - if (LST_RGBCW == light_subtype) { - WebSliderColdWarm(); - } - WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, - "f", - "#000", "#fff", - 5, - Settings.flag3.slider_dimmer_stay_on, 100, - LightGetDimmer(2), - 'w', 0); - } - } else { - uint32_t pwm_channels = light_subtype > LST_MAX ? LST_MAX : light_subtype; - stemp[0] = 'e'; stemp[1] = '0'; stemp[2] = '\0'; - for (uint32_t i = 0; i < pwm_channels; i++) { - stemp[1]++; - - WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, - stemp, - "#000", "#fff", - i+1, - 1, 100, - changeUIntScale(Settings.light_color[i], 0, 255, 0, 100), - 'e', i+1); - } - } - } -#endif -#ifdef USE_SHUTTER - if (Settings.flag3.shutter_mode) { - for (uint32_t i = 0; i < shutters_present; i++) { - WSContentSend_P(HTTP_MSG_SLIDER_SHUTTER, Settings.shutter_position[i], i+1); - } - } -#endif - WSContentSend_P(HTTP_TABLE100); - WSContentSend_P(PSTR("
")); -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan()) { - WSContentSend_P(HTTP_DEVICE_CONTROL, 36, 1, - (strlen(SettingsText(SET_BUTTON1))) ? SettingsText(SET_BUTTON1) : D_BUTTON_TOGGLE, - ""); - for (uint32_t i = 0; i < MaxFanspeed(); i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i); - WSContentSend_P(HTTP_DEVICE_CONTROL, 16, i +2, - (strlen(SettingsText(SET_BUTTON2 + i))) ? SettingsText(SET_BUTTON2 + i) : stemp, - ""); - } - } else { -#endif - for (uint32_t idx = 1; idx <= devices_present; idx++) { - bool set_button = ((idx <= MAX_BUTTON_TEXT) && strlen(SettingsText(SET_BUTTON1 + idx -1))); -#ifdef USE_SHUTTER - int32_t ShutterWebButton; - if (ShutterWebButton = IsShutterWebButton(idx)) { - WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, - (set_button) ? SettingsText(SET_BUTTON1 + idx -1) : ((Settings.shutter_options[abs(ShutterWebButton)-1] & 2) ? "-" : ((Settings.shutter_options[abs(ShutterWebButton)-1] & 8) ? ((ShutterWebButton>0) ? "▼" : "▲") : ((ShutterWebButton>0) ? "▲" : "▼"))), - ""); - continue; - } -#endif - snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), idx); - WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, - (set_button) ? SettingsText(SET_BUTTON1 + idx -1) : (devices_present < 5) ? D_BUTTON_TOGGLE : "", - (set_button) ? "" : (devices_present > 1) ? stemp : ""); - } -#ifdef USE_SONOFF_IFAN - } -#endif - WSContentSend_P(PSTR("
%s
")); - } -#ifdef USE_SONOFF_RF - if (SONOFF_BRIDGE == my_module_type) { - WSContentSend_P(HTTP_TABLE100); - WSContentSend_P(PSTR("")); - uint32_t idx = 0; - for (uint32_t i = 0; i < 4; i++) { - if (idx > 0) { WSContentSend_P(PSTR("")); } - for (uint32_t j = 0; j < 4; j++) { - idx++; - snprintf_P(stemp, sizeof(stemp), PSTR("%d"), idx); - WSContentSend_P(PSTR(""), idx, - (strlen(SettingsText(SET_BUTTON1 + idx -1))) ? SettingsText(SET_BUTTON1 + idx -1) : stemp); - } - } - WSContentSend_P(PSTR("")); - } -#endif - -#ifndef FIRMWARE_MINIMAL - XdrvCall(FUNC_WEB_ADD_MAIN_BUTTON); - XsnsCall(FUNC_WEB_ADD_MAIN_BUTTON); -#endif - - if (HTTP_ADMIN == Web.state) { -#ifdef FIRMWARE_MINIMAL - WSContentSpaceButton(BUTTON_FIRMWARE_UPGRADE); -#else - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentButton(BUTTON_INFORMATION); - WSContentButton(BUTTON_FIRMWARE_UPGRADE); -#endif - WSContentButton(BUTTON_CONSOLE); - WSContentButton(BUTTON_RESTART); - } - WSContentStop(); -} - -bool HandleRootStatusRefresh(void) -{ - if (!WebAuthenticate()) { - Webserver->requestAuthentication(); - return true; - } - - if (!Webserver->hasArg("m")) { - return false; - } - - #ifdef USE_SCRIPT_WEB_DISPLAY - Script_Check_HTML_Setvars(); - #endif - - char tmp[8]; - char svalue[32]; - char webindex[5]; - - WebGetArg("o", tmp, sizeof(tmp)); - if (strlen(tmp)) { - ShowWebSource(SRC_WEBGUI); - uint32_t device = atoi(tmp); -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan()) { - if (device < 2) { - ExecuteCommandPower(1, POWER_TOGGLE, SRC_IGNORE); - } else { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_FANSPEED " %d"), device -2); - ExecuteCommand(svalue, SRC_WEBGUI); - } - } else { -#endif -#ifdef USE_SHUTTER - int32_t ShutterWebButton; - if (ShutterWebButton = IsShutterWebButton(device)) { - snprintf_P(svalue, sizeof(svalue), PSTR("ShutterPosition%d %s"), abs(ShutterWebButton), (ShutterWebButton>0) ? PSTR(D_CMND_SHUTTER_STOPOPEN) : PSTR(D_CMND_SHUTTER_STOPCLOSE)); - ExecuteWebCommand(svalue, SRC_WEBGUI); - } else { -#endif - ExecuteCommandPower(device, POWER_TOGGLE, SRC_IGNORE); -#ifdef USE_SHUTTER - } -#endif -#ifdef USE_SONOFF_IFAN - } -#endif - } -#ifdef USE_LIGHT - WebGetArg("d0", tmp, sizeof(tmp)); - if (strlen(tmp)) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_DIMMER " %s"), tmp); - ExecuteWebCommand(svalue, SRC_WEBGUI); - } - WebGetArg("w0", tmp, sizeof(tmp)); - if (strlen(tmp)) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_WHITE " %s"), tmp); - ExecuteWebCommand(svalue, SRC_WEBGUI); - } - uint32_t light_device = LightDevice(); - uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); - for (uint32_t j = 0; j < pwm_channels; j++) { - snprintf_P(webindex, sizeof(webindex), PSTR("e%d"), j +1); - WebGetArg(webindex, tmp, sizeof(tmp)); - if (strlen(tmp)) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_CHANNEL "%d %s"), j +light_device, tmp); - ExecuteWebCommand(svalue, SRC_WEBGUI); - } - } - WebGetArg("t0", tmp, sizeof(tmp)); - if (strlen(tmp)) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_COLORTEMPERATURE " %s"), tmp); - ExecuteWebCommand(svalue, SRC_WEBGUI); - } - WebGetArg("h0", tmp, sizeof(tmp)); - if (strlen(tmp)) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_HSBCOLOR "1 %s"), tmp); - ExecuteWebCommand(svalue, SRC_WEBGUI); - } - WebGetArg("n0", tmp, sizeof(tmp)); - if (strlen(tmp)) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_HSBCOLOR "2 %s"), tmp); - ExecuteWebCommand(svalue, SRC_WEBGUI); - } -#endif -#ifdef USE_SHUTTER - for (uint32_t j = 1; j <= shutters_present; j++) { - snprintf_P(webindex, sizeof(webindex), PSTR("u%d"), j); - WebGetArg(webindex, tmp, sizeof(tmp)); - if (strlen(tmp)) { - snprintf_P(svalue, sizeof(svalue), PSTR("ShutterPosition%d %s"), j, tmp); - ExecuteWebCommand(svalue, SRC_WEBGUI); - } - } -#endif -#ifdef USE_SONOFF_RF - WebGetArg("k", tmp, sizeof(tmp)); - if (strlen(tmp)) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_RFKEY "%s"), tmp); - ExecuteWebCommand(svalue, SRC_WEBGUI); - } -#endif - WSContentBegin(200, CT_HTML); - WSContentSend_P(PSTR("{t}")); - XsnsCall(FUNC_WEB_SENSOR); -#ifdef USE_SCRIPT_WEB_DISPLAY - XdrvCall(FUNC_WEB_SENSOR); -#endif - - WSContentSend_P(PSTR("")); - - if (devices_present) { - WSContentSend_P(PSTR("{t}")); - uint32_t fsize = (devices_present < 5) ? 70 - (devices_present * 8) : 32; -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan()) { - WSContentSend_P(HTTP_DEVICE_STATE, 36, (bitRead(power, 0)) ? "bold" : "normal", 54, GetStateText(bitRead(power, 0))); - uint32_t fanspeed = GetFanspeed(); - snprintf_P(svalue, sizeof(svalue), PSTR("%d"), fanspeed); - WSContentSend_P(HTTP_DEVICE_STATE, 64, (fanspeed) ? "bold" : "normal", 54, (fanspeed) ? svalue : GetStateText(0)); - } else { -#endif - for (uint32_t idx = 1; idx <= devices_present; idx++) { - snprintf_P(svalue, sizeof(svalue), PSTR("%d"), bitRead(power, idx -1)); - WSContentSend_P(HTTP_DEVICE_STATE, 100 / devices_present, (bitRead(power, idx -1)) ? "bold" : "normal", fsize, (devices_present < 5) ? GetStateText(bitRead(power, idx -1)) : svalue); - } -#ifdef USE_SONOFF_IFAN - } -#endif - WSContentSend_P(PSTR("")); - } - WSContentEnd(); - - return true; -} - -#ifdef USE_SHUTTER -int32_t IsShutterWebButton(uint32_t idx) { - - int32_t ShutterWebButton = 0; - if (Settings.flag3.shutter_mode) { - for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { - if (Settings.shutter_startrelay[i] && ((Settings.shutter_startrelay[i] == idx) || (Settings.shutter_startrelay[i] == (idx-1)))) { - ShutterWebButton = (Settings.shutter_startrelay[i] == idx) ? (i+1): (-1-i); - break; - } - } - } - return ShutterWebButton; -} -#endif - - - -#ifndef FIRMWARE_MINIMAL - -void HandleConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURATION); - - WSContentStart_P(S_CONFIGURATION); - WSContentSendStyle(); - - WSContentButton(BUTTON_MODULE); - WSContentButton(BUTTON_WIFI); - - XdrvCall(FUNC_WEB_ADD_BUTTON); - XsnsCall(FUNC_WEB_ADD_BUTTON); - - WSContentButton(BUTTON_LOGGING); - WSContentButton(BUTTON_OTHER); - WSContentButton(BUTTON_TEMPLATE); - - WSContentSpaceButton(BUTTON_RESET_CONFIGURATION); - WSContentButton(BUTTON_BACKUP); - WSContentButton(BUTTON_RESTORE); - - WSContentSpaceButton(BUTTON_MAIN); - WSContentStop(); -} - - - -void HandleTemplateConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - if (Webserver->hasArg("save")) { - TemplateSaveSettings(); - WebRestart(1); - return; - } - - char stemp[30]; - - if (Webserver->hasArg("m")) { - WSContentBegin(200, CT_PLAIN); - for (uint32_t i = 0; i < sizeof(kModuleNiceList); i++) { - uint32_t midx = pgm_read_byte(kModuleNiceList + i); - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), midx +1); - } - WSContentEnd(); - return; - } - - WebGetArg("t", stemp, sizeof(stemp)); - if (strlen(stemp)) { - uint32_t module = atoi(stemp); - uint32_t module_save = Settings.module; - Settings.module = module; - myio cmodule; - ModuleGpios(&cmodule); - gpio_flag flag = ModuleFlag(); - Settings.module = module_save; - - WSContentBegin(200, CT_PLAIN); - WSContentSend_P(PSTR("%s}1"), AnyModuleName(module).c_str()); - for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { - if (1 == i) { - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, 255, D_SENSOR_USER, 255); - } - uint32_t midx = pgm_read_byte(kGpioNiceList + i); - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); - } - WSContentSend_P(PSTR("}1")); - - for (uint32_t i = 0; i < ADC0_END; i++) { - if (1 == i) { - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, ADC0_USER, D_SENSOR_USER, ADC0_USER); - } - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, i, GetTextIndexed(stemp, sizeof(stemp), i, kAdc0Names), i); - } - WSContentSend_P(PSTR("}1")); - - for (uint32_t i = 0; i < sizeof(cmodule); i++) { - if (!FlashPin(i)) { - WSContentSend_P(PSTR("%s%d"), (i>0)?",":"", cmodule.io[i]); - } - } - WSContentSend_P(PSTR("}1%d}1%d"), flag, Settings.user_template_base); - WSContentEnd(); - return; - } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_TEMPLATE); - - WSContentStart_P(S_CONFIGURE_TEMPLATE); - WSContentSend_P(HTTP_SCRIPT_MODULE_TEMPLATE); - WSContentSend_P(HTTP_SCRIPT_TEMPLATE); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_TEMPLATE); - WSContentSend_P(HTTP_TABLE100); - WSContentSend_P(PSTR("" D_TEMPLATE_NAME "" - "" D_BASE_TYPE "" - "" - "
")); - WSContentSend_P(HTTP_TABLE100); - for (uint32_t i = 0; i < MAX_GPIO_PIN; i++) { - if (!FlashPin(i)) { - WSContentSend_P(PSTR("" D_GPIO "%d"), - ((9==i)||(10==i)) ? WebColor(COL_TEXT_WARNING) : WebColor(COL_TEXT), i, (0==i) ? " style='width:200px'" : "", i); - } - } -#ifdef ESP8266 - WSContentSend_P(PSTR("" D_ADC "0"), WebColor(COL_TEXT)); -#endif - WSContentSend_P(PSTR("")); - gpio_flag flag = ModuleFlag(); - if (flag.data > ADC0_USER) { - WSContentSend_P(HTTP_FORM_TEMPLATE_FLAG); - } - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); -} - -void TemplateSaveSettings(void) -{ - char tmp[TOPSZ]; - char webindex[5]; - char svalue[300]; - - WebGetArg("s1", tmp, sizeof(tmp)); - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_TEMPLATE " {\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), tmp); - - uint32_t j = 0; - for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) { -#ifdef ESP8266 - if (6 == i) { j = 9; } - if (8 == i) { j = 12; } -#else - if (6 == i) { j = 12; } -#endif - snprintf_P(webindex, sizeof(webindex), PSTR("g%d"), j); - WebGetArg(webindex, tmp, sizeof(tmp)); - uint8_t gpio = atoi(tmp); - snprintf_P(svalue, sizeof(svalue), PSTR("%s%s%d"), svalue, (i>0)?",":"", gpio); - j++; - } - - WebGetArg("g" STR(ADC0_PIN), tmp, sizeof(tmp)); - uint32_t flag = atoi(tmp); - for (uint32_t i = 0; i < GPIO_FLAG_USED; i++) { - snprintf_P(webindex, sizeof(webindex), PSTR("c%d"), i); - uint32_t state = Webserver->hasArg(webindex) << i +4; - flag += state; - } - WebGetArg("g99", tmp, sizeof(tmp)); - uint32_t base = atoi(tmp) +1; - - snprintf_P(svalue, sizeof(svalue), PSTR("%s],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), svalue, flag, base); - ExecuteWebCommand(svalue, SRC_WEBGUI); -} - - - -void HandleModuleConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - if (Webserver->hasArg("save")) { - ModuleSaveSettings(); - WebRestart(1); - return; - } - - char stemp[30]; - uint32_t midx; - myio cmodule; - ModuleGpios(&cmodule); - - if (Webserver->hasArg("m")) { - WSContentBegin(200, CT_PLAIN); - uint32_t vidx = 0; - for (uint32_t i = 0; i <= sizeof(kModuleNiceList); i++) { - if (0 == i) { - midx = USER_MODULE; - vidx = 0; - } else { - midx = pgm_read_byte(kModuleNiceList + i -1); - vidx = midx +1; - } - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), vidx); - } - WSContentEnd(); - return; - } - - if (Webserver->hasArg("g")) { - WSContentBegin(200, CT_PLAIN); - for (uint32_t j = 0; j < sizeof(kGpioNiceList); j++) { - midx = pgm_read_byte(kGpioNiceList + j); - if (!GetUsedInModule(midx, cmodule.io)) { - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); - } - } - WSContentEnd(); - return; - } - -#ifndef USE_ADC_VCC - if (Webserver->hasArg("a")) { - WSContentBegin(200, CT_PLAIN); - for (uint32_t j = 0; j < ADC0_END; j++) { - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, j, GetTextIndexed(stemp, sizeof(stemp), j, kAdc0Names), j); - } - WSContentEnd(); - return; - } -#endif - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MODULE); - - WSContentStart_P(S_CONFIGURE_MODULE); - WSContentSend_P(HTTP_SCRIPT_MODULE_TEMPLATE); - WSContentSend_P(HTTP_SCRIPT_MODULE1, Settings.module); - for (uint32_t i = 0; i < sizeof(cmodule); i++) { - if (ValidGPIO(i, cmodule.io[i])) { - WSContentSend_P(PSTR("sk(%d,%d);"), my_module.io[i], i); - } - } - WSContentSend_P(HTTP_SCRIPT_MODULE2, Settings.my_adc0); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_MODULE, AnyModuleName(MODULE).c_str()); - for (uint32_t i = 0; i < sizeof(cmodule); i++) { - if (ValidGPIO(i, cmodule.io[i])) { - snprintf_P(stemp, 3, PINS_WEMOS +i*2); -#ifdef ESP8266 - char sesp8285[40]; - snprintf_P(sesp8285, sizeof(sesp8285), PSTR("ESP8285"), WebColor(COL_TEXT_WARNING)); - WSContentSend_P(PSTR("%s " D_GPIO "%d %s"), - (WEMOS==my_module_type)?stemp:"", i, (0==i)? D_SENSOR_BUTTON "1":(1==i)? D_SERIAL_OUT :(3==i)? D_SERIAL_IN :((9==i)||(10==i))? sesp8285 :(12==i)? D_SENSOR_RELAY "1":(13==i)? D_SENSOR_LED "1i":(14==i)? D_SENSOR :"", i); -#else - WSContentSend_P(PSTR("%s " D_GPIO "%d"), - (WEMOS==my_module_type)?stemp:"", i, i); -#endif - } - } -#ifdef ESP8266 -#ifndef USE_ADC_VCC - if (ValidAdc()) { - WSContentSend_P(PSTR("%s " D_ADC "0"), (WEMOS==my_module_type)?"A0":""); - } -#endif -#endif - WSContentSend_P(PSTR("")); - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); -} - -void ModuleSaveSettings(void) -{ - char tmp[8]; - char webindex[5]; - - WebGetArg("g99", tmp, sizeof(tmp)); - uint32_t new_module = (!strlen(tmp)) ? MODULE : atoi(tmp); - Settings.last_module = Settings.module; - Settings.module = new_module; - SetModuleType(); - myio cmodule; - ModuleGpios(&cmodule); - String gpios = ""; - for (uint32_t i = 0; i < sizeof(cmodule); i++) { - if (Settings.last_module != new_module) { - Settings.my_gp.io[i] = GPIO_NONE; - } else { - if (ValidGPIO(i, cmodule.io[i])) { - snprintf_P(webindex, sizeof(webindex), PSTR("g%d"), i); - WebGetArg(webindex, tmp, sizeof(tmp)); - uint8_t value = (!strlen(tmp)) ? 0 : atoi(tmp); -#ifdef ESP8266 - Settings.my_gp.io[i] = value; -#else - if (i == ADC0_PIN) { - Settings.my_adc0 = value; - } else { - Settings.my_gp.io[i] = value; - } -#endif - gpios += F(", " D_GPIO ); gpios += String(i); gpios += F(" "); gpios += String(value); - } - } - } -#ifdef ESP8266 -#ifndef USE_ADC_VCC - - WebGetArg("g" STR(ADC0_PIN), tmp, sizeof(tmp)); - Settings.my_adc0 = (!strlen(tmp)) ? 0 : atoi(tmp); - gpios += F(", " D_ADC "0 "); gpios += String(Settings.my_adc0); -#endif -#endif - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MODULE "%s " D_CMND_MODULE "%s"), ModuleName().c_str(), gpios.c_str()); -} - - - -const char kUnescapeCode[] = "&><\"\'"; -const char kEscapeCode[] PROGMEM = "&|>|<|"|'"; - -String HtmlEscape(const String unescaped) { - char escaped[10]; - size_t ulen = unescaped.length(); - String result = ""; - for (size_t i = 0; i < ulen; i++) { - char c = unescaped[i]; - char *p = strchr(kUnescapeCode, c); - if (p != nullptr) { - result += GetTextIndexed(escaped, sizeof(escaped), p - kUnescapeCode, kEscapeCode); - } else { - result += c; - } - } - return result; -} - - -const char kEncryptionType[] PROGMEM = "|||" D_WPA_PSK "||" D_WPA2_PSK "|" D_WEP "||" D_NONE "|" D_AUTO; - -void HandleWifiConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess(!WifiIsInManagerMode())) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_WIFI); - - if (Webserver->hasArg("save") && HTTP_MANAGER_RESET_ONLY != Web.state) { - WifiSaveSettings(); - WebRestart(2); - return; - } - - WSContentStart_P(S_CONFIGURE_WIFI, !WifiIsInManagerMode()); - WSContentSend_P(HTTP_SCRIPT_WIFI); - WSContentSendStyle(); - - if (HTTP_MANAGER_RESET_ONLY != Web.state) { - if (Webserver->hasArg("scan")) { -#ifdef USE_EMULATION - UdpDisconnect(); -#endif - int n = WiFi.scanNetworks(); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_SCAN_DONE)); - - if (0 == n) { - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, S_NO_NETWORKS_FOUND); - WSContentSend_P(S_NO_NETWORKS_FOUND); - WSContentSend_P(PSTR(". " D_REFRESH_TO_SCAN_AGAIN ".")); - } else { - - int indices[n]; - for (uint32_t i = 0; i < n; i++) { - indices[i] = i; - } - - - for (uint32_t i = 0; i < n; i++) { - for (uint32_t j = i + 1; j < n; j++) { - if (WiFi.RSSI(indices[j]) > WiFi.RSSI(indices[i])) { - std::swap(indices[i], indices[j]); - } - } - } - - - String cssid; - for (uint32_t i = 0; i < n; i++) { - if (-1 == indices[i]) { continue; } - cssid = WiFi.SSID(indices[i]); - uint32_t cschn = WiFi.channel(indices[i]); - for (uint32_t j = i + 1; j < n; j++) { - if ((cssid == WiFi.SSID(indices[j])) && (cschn == WiFi.channel(indices[j]))) { - DEBUG_CORE_LOG(PSTR(D_LOG_WIFI D_DUPLICATE_ACCESSPOINT " %s"), WiFi.SSID(indices[j]).c_str()); - indices[j] = -1; - } - } - } - - - for (uint32_t i = 0; i < n; i++) { - if (-1 == indices[i]) { continue; } - int32_t rssi = WiFi.RSSI(indices[i]); - DEBUG_CORE_LOG(PSTR(D_LOG_WIFI D_SSID " %s, " D_BSSID " %s, " D_CHANNEL " %d, " D_RSSI " %d"), - WiFi.SSID(indices[i]).c_str(), WiFi.BSSIDstr(indices[i]).c_str(), WiFi.channel(indices[i]), rssi); - int quality = WifiGetRssiAsQuality(rssi); - int auth = WiFi.encryptionType(indices[i]); - char encryption[20]; - WSContentSend_P(PSTR("
%s (%d) %s %d%% (%d dBm)
"), - HtmlEscape(WiFi.SSID(indices[i])).c_str(), - WiFi.channel(indices[i]), - GetTextIndexed(encryption, sizeof(encryption), auth +1, kEncryptionType), - quality, rssi - ); - delay(0); - - } - WSContentSend_P(PSTR("
")); - } - } else { - WSContentSend_P(PSTR("
")); - } - - - WSContentSend_P(HTTP_FORM_WIFI, SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), WIFI_HOSTNAME, WIFI_HOSTNAME, SettingsText(SET_HOSTNAME), SettingsText(SET_CORS)); - WSContentSend_P(HTTP_FORM_END); - } - - if (WifiIsInManagerMode()) { -#ifndef FIRMWARE_MINIMAL - WSContentSpaceButton(BUTTON_RESTORE); - WSContentButton(BUTTON_RESET_CONFIGURATION); -#endif - WSContentSpaceButton(BUTTON_RESTART); - } else { - WSContentSpaceButton(BUTTON_CONFIGURATION); - } - WSContentStop(); -} - -void WifiSaveSettings(void) -{ - char tmp[TOPSZ]; - - WebGetArg("h", tmp, sizeof(tmp)); - SettingsUpdateText(SET_HOSTNAME, (!strlen(tmp)) ? WIFI_HOSTNAME : tmp); - if (strstr(SettingsText(SET_HOSTNAME), "%") != nullptr) { - SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); - } - WebGetArg("c", tmp, sizeof(tmp)); - SettingsUpdateText(SET_CORS, (!strlen(tmp)) ? CORS_DOMAIN : tmp); - WebGetArg("s1", tmp, sizeof(tmp)); - SettingsUpdateText(SET_STASSID1, (!strlen(tmp)) ? STA_SSID1 : tmp); - WebGetArg("s2", tmp, sizeof(tmp)); - SettingsUpdateText(SET_STASSID2, (!strlen(tmp)) ? STA_SSID2 : tmp); - WebGetArg("p1", tmp, sizeof(tmp)); - SettingsUpdateText(SET_STAPWD1, (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? SettingsText(SET_STAPWD1) : tmp); - WebGetArg("p2", tmp, sizeof(tmp)); - SettingsUpdateText(SET_STAPWD2, (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? SettingsText(SET_STAPWD2) : tmp); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_HOSTNAME " %s, " D_CMND_SSID "1 %s, " D_CMND_SSID "2 %s, " D_CMND_CORS " %s"), - SettingsText(SET_HOSTNAME), SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), SettingsText(SET_CORS)); -} - - - -void HandleLoggingConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_LOGGING); - - if (Webserver->hasArg("save")) { - LoggingSaveSettings(); - HandleConfiguration(); - return; - } - - WSContentStart_P(S_CONFIGURE_LOGGING); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_LOG1); - char stemp1[45]; - char stemp2[32]; - uint8_t dlevel[4] = { LOG_LEVEL_INFO, LOG_LEVEL_INFO, LOG_LEVEL_NONE, LOG_LEVEL_NONE }; - for (uint32_t idx = 0; idx < 4; idx++) { - if ((2==idx) && !Settings.flag.mqtt_enabled) { continue; } - uint32_t llevel = (0==idx)?Settings.seriallog_level:(1==idx)?Settings.weblog_level:(2==idx)?Settings.mqttlog_level:Settings.syslog_level; - WSContentSend_P(PSTR("

%s (%s)

")); - } - WSContentSend_P(HTTP_FORM_LOG2, SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, Settings.tele_period); - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); -} - -void LoggingSaveSettings(void) -{ - char tmp[TOPSZ]; - - WebGetArg("l0", tmp, sizeof(tmp)); - SetSeriallog((!strlen(tmp)) ? SERIAL_LOG_LEVEL : atoi(tmp)); - WebGetArg("l1", tmp, sizeof(tmp)); - Settings.weblog_level = (!strlen(tmp)) ? WEB_LOG_LEVEL : atoi(tmp); - WebGetArg("l2", tmp, sizeof(tmp)); - Settings.mqttlog_level = (!strlen(tmp)) ? MQTT_LOG_LEVEL : atoi(tmp); - WebGetArg("l3", tmp, sizeof(tmp)); - SetSyslog((!strlen(tmp)) ? SYS_LOG_LEVEL : atoi(tmp)); - WebGetArg("lh", tmp, sizeof(tmp)); - SettingsUpdateText(SET_SYSLOG_HOST, (!strlen(tmp)) ? SYS_LOG_HOST : tmp); - WebGetArg("lp", tmp, sizeof(tmp)); - Settings.syslog_port = (!strlen(tmp)) ? SYS_LOG_PORT : atoi(tmp); - WebGetArg("lt", tmp, sizeof(tmp)); - Settings.tele_period = (!strlen(tmp)) ? TELE_PERIOD : atoi(tmp); - if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) { - Settings.tele_period = 10; - } - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_LOG D_CMND_SERIALLOG " %d, " D_CMND_WEBLOG " %d, " D_CMND_MQTTLOG " %d, " D_CMND_SYSLOG " %d, " D_CMND_LOGHOST " %s, " D_CMND_LOGPORT " %d, " D_CMND_TELEPERIOD " %d"), - Settings.seriallog_level, Settings.weblog_level, Settings.mqttlog_level, Settings.syslog_level, SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, Settings.tele_period); -} - - - -void HandleOtherConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_OTHER); - - if (Webserver->hasArg("save")) { - OtherSaveSettings(); - WebRestart(1); - return; - } - - WSContentStart_P(S_CONFIGURE_OTHER); - WSContentSendStyle(); - - TemplateJson(); - char stemp[strlen(mqtt_data) +1]; - strlcpy(stemp, mqtt_data, sizeof(stemp)); - WSContentSend_P(HTTP_FORM_OTHER, stemp, (USER_MODULE == Settings.module) ? " checked disabled" : "", (Settings.flag.mqtt_enabled) ? " checked" : ""); - - uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present; -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan()) { maxfn = 1; } -#endif - for (uint32_t i = 0; i < maxfn; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i +1); - WSContentSend_P(PSTR("" D_FRIENDLY_NAME " %d (" FRIENDLY_NAME "%s)

"), - i +1, - (i) ? stemp : "", - i, - (i) ? stemp : "", - SettingsText(SET_FRIENDLYNAME1 + i)); - } - -#ifdef USE_EMULATION -#if defined(USE_EMULATION_WEMO) || defined(USE_EMULATION_HUE) - WSContentSend_P(PSTR("

 " D_EMULATION " 

")); - for (uint32_t i = 0; i < EMUL_MAX; i++) { -#ifndef USE_EMULATION_WEMO - if (i == EMUL_WEMO) { i++; } -#endif -#ifndef USE_EMULATION_HUE - if (i == EMUL_HUE) { i++; } -#endif - if (i < EMUL_MAX) { - WSContentSend_P(PSTR("%s %s
"), - i, i, - (i == Settings.flag2.emulation) ? " checked" : "", - GetTextIndexed(stemp, sizeof(stemp), i, kEmulationOptions), - (i == EMUL_NONE) ? "" : (i == EMUL_WEMO) ? D_SINGLE_DEVICE : D_MULTI_DEVICE); - } - } - WSContentSend_P(PSTR("

")); -#endif -#endif - - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); -} - -void OtherSaveSettings(void) -{ - char tmp[TOPSZ]; - char webindex[5]; - char friendlyname[TOPSZ]; - char message[LOGSZ]; - - WebGetArg("wp", tmp, sizeof(tmp)); - SettingsUpdateText(SET_WEBPWD, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? SettingsText(SET_WEBPWD) : tmp); - Settings.flag.mqtt_enabled = Webserver->hasArg("b1"); -#ifdef USE_EMULATION - UdpDisconnect(); -#if defined(USE_EMULATION_WEMO) || defined(USE_EMULATION_HUE) - WebGetArg("b2", tmp, sizeof(tmp)); - Settings.flag2.emulation = (!strlen(tmp)) ? 0 : atoi(tmp); -#endif -#endif - - snprintf_P(message, sizeof(message), PSTR(D_LOG_OTHER D_MQTT_ENABLE " %s, " D_CMND_EMULATION " %d, " D_CMND_FRIENDLYNAME), GetStateText(Settings.flag.mqtt_enabled), Settings.flag2.emulation); - for (uint32_t i = 0; i < MAX_FRIENDLYNAMES; i++) { - snprintf_P(webindex, sizeof(webindex), PSTR("a%d"), i); - WebGetArg(webindex, tmp, sizeof(tmp)); - snprintf_P(friendlyname, sizeof(friendlyname), PSTR(FRIENDLY_NAME"%d"), i +1); - SettingsUpdateText(SET_FRIENDLYNAME1 +i, (!strlen(tmp)) ? (i) ? friendlyname : FRIENDLY_NAME : tmp); - snprintf_P(message, sizeof(message), PSTR("%s%s %s"), message, (i) ? "," : "", SettingsText(SET_FRIENDLYNAME1 +i)); - } - AddLog_P(LOG_LEVEL_INFO, message); -# 2014 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_01_webserver.ino" - WebGetArg("t1", tmp, sizeof(tmp)); - if (strlen(tmp)) { - snprintf_P(message, sizeof(message), PSTR(D_CMND_BACKLOG " " D_CMND_TEMPLATE " %s%s"), tmp, (Webserver->hasArg("t2")) ? "; " D_CMND_MODULE " 0" : ""); - ExecuteWebCommand(message, SRC_WEBGUI); - } -} - - - -void HandleBackupConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_BACKUP_CONFIGURATION)); - - if (!SettingsBufferAlloc()) { return; } - - WiFiClient myClient = Webserver->client(); - Webserver->setContentLength(sizeof(Settings)); - - char attachment[TOPSZ]; - - - - - char hostname[sizeof(my_hostname)]; - snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=Config_%s_%s.dmp"), NoAlNumToUnderscore(hostname, my_hostname), my_version); - - Webserver->sendHeader(F("Content-Disposition"), attachment); - - WSSend(200, CT_STREAM, ""); - - uint32_t cfg_crc32 = Settings.cfg_crc32; - Settings.cfg_crc32 = GetSettingsCrc32(); - - memcpy(settings_buffer, &Settings, sizeof(Settings)); - if (Web.config_xor_on_set) { - for (uint32_t i = 2; i < sizeof(Settings); i++) { - settings_buffer[i] ^= (Web.config_xor_on_set +i); - } - } - -#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 - size_t written = myClient.write((const char*)settings_buffer, sizeof(Settings)); - if (written < sizeof(Settings)) { - myClient.write((const char*)settings_buffer +written, sizeof(Settings) -written); - } -#else - myClient.write((const char*)settings_buffer, sizeof(Settings)); -#endif - - SettingsBufferFree(); - - Settings.cfg_crc32 = cfg_crc32; -} - - - -void HandleResetConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess(!WifiIsInManagerMode())) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESET_CONFIGURATION); - - WSContentStart_P(S_RESET_CONFIGURATION, !WifiIsInManagerMode()); - WSContentSendStyle(); - WSContentSend_P(PSTR("
" D_CONFIGURATION_RESET "
")); - WSContentSend_P(HTTP_MSG_RSTRT); - WSContentSpaceButton(BUTTON_MAIN); - WSContentStop(); - - char command[CMDSZ]; - snprintf_P(command, sizeof(command), PSTR(D_CMND_RESET " 1")); - ExecuteWebCommand(command, SRC_WEBGUI); -} - -void HandleRestoreConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTORE_CONFIGURATION); - - WSContentStart_P(S_RESTORE_CONFIGURATION); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_RST); - WSContentSend_P(HTTP_FORM_RST_UPG, D_RESTORE); - if (WifiIsInManagerMode()) { - WSContentSpaceButton(BUTTON_MAIN); - } else { - WSContentSpaceButton(BUTTON_CONFIGURATION); - } - WSContentStop(); - - Web.upload_error = 0; - Web.upload_file_type = UPL_SETTINGS; -} - - - -void HandleInformation(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_INFORMATION); - - char stopic[TOPSZ]; - - int freeMem = ESP.getFreeHeap(); - - WSContentStart_P(S_INFORMATION); - - - - WSContentSend_P(HTTP_SCRIPT_INFO_BEGIN); - WSContentSend_P(PSTR("
")); - WSContentSend_P(PSTR(D_PROGRAM_VERSION "}2%s%s"), my_version, my_image); - WSContentSend_P(PSTR("}1" D_BUILD_DATE_AND_TIME "}2%s"), GetBuildDateAndTime().c_str()); - WSContentSend_P(PSTR("}1" D_CORE_AND_SDK_VERSION "}2" ARDUINO_CORE_RELEASE "/%s"), ESP.getSdkVersion()); - WSContentSend_P(PSTR("}1" D_UPTIME "}2%s"), GetUptime().c_str()); - WSContentSend_P(PSTR("}1" D_FLASH_WRITE_COUNT "}2%d at 0x%X"), Settings.save_flag, GetSettingsAddress()); - WSContentSend_P(PSTR("}1" D_BOOT_COUNT "}2%d"), Settings.bootcount); - WSContentSend_P(PSTR("}1" D_RESTART_REASON "}2%s"), GetResetReason().c_str()); - uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : devices_present; -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan()) { maxfn = 1; } -#endif - for (uint32_t i = 0; i < maxfn; i++) { - WSContentSend_P(PSTR("}1" D_FRIENDLY_NAME " %d}2%s"), i +1, SettingsText(SET_FRIENDLYNAME1 +i)); - } - WSContentSend_P(PSTR("}1}2 ")); - int32_t rssi = WiFi.RSSI(); - WSContentSend_P(PSTR("}1" D_AP "%d " D_SSID " (" D_RSSI ")}2%s (%d%%, %d dBm)"), Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), WifiGetRssiAsQuality(rssi), rssi); - WSContentSend_P(PSTR("}1" D_HOSTNAME "}2%s%s"), my_hostname, (Wifi.mdns_begun) ? ".local" : ""); -#if LWIP_IPV6 - String ipv6_addr = WifiGetIPv6(); - if(ipv6_addr != ""){ - WSContentSend_P(PSTR("}1 IPv6 Address }2%s"), ipv6_addr.c_str()); - } -#endif - if (static_cast(WiFi.localIP()) != 0) { - WSContentSend_P(PSTR("}1" D_IP_ADDRESS "}2%s"), WiFi.localIP().toString().c_str()); - WSContentSend_P(PSTR("}1" D_GATEWAY "}2%s"), IPAddress(Settings.ip_address[1]).toString().c_str()); - WSContentSend_P(PSTR("}1" D_SUBNET_MASK "}2%s"), IPAddress(Settings.ip_address[2]).toString().c_str()); - WSContentSend_P(PSTR("}1" D_DNS_SERVER "}2%s"), IPAddress(Settings.ip_address[3]).toString().c_str()); - WSContentSend_P(PSTR("}1" D_MAC_ADDRESS "}2%s"), WiFi.macAddress().c_str()); - } - if (static_cast(WiFi.softAPIP()) != 0) { - WSContentSend_P(PSTR("}1" D_IP_ADDRESS "}2%s"), WiFi.softAPIP().toString().c_str()); - WSContentSend_P(PSTR("}1" D_GATEWAY "}2%s"), WiFi.softAPIP().toString().c_str()); - WSContentSend_P(PSTR("}1" D_MAC_ADDRESS "}2%s"), WiFi.softAPmacAddress().c_str()); - } - WSContentSend_P(PSTR("}1}2 ")); - if (Settings.flag.mqtt_enabled) { - WSContentSend_P(PSTR("}1" D_MQTT_HOST "}2%s"), SettingsText(SET_MQTT_HOST)); - WSContentSend_P(PSTR("}1" D_MQTT_PORT "}2%d"), Settings.mqtt_port); - WSContentSend_P(PSTR("}1" D_MQTT_USER "}2%s"), SettingsText(SET_MQTT_USER)); - WSContentSend_P(PSTR("}1" D_MQTT_CLIENT "}2%s"), mqtt_client); - WSContentSend_P(PSTR("}1" D_MQTT_TOPIC "}2%s"), SettingsText(SET_MQTT_TOPIC)); - uint32_t real_index = SET_MQTT_GRP_TOPIC; - for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { - if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } - if (strlen(SettingsText(real_index +i))) { - WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC " %d}2%s"), 1 +i, GetGroupTopic_P(stopic, "", real_index +i)); - } - } - WSContentSend_P(PSTR("}1" D_MQTT_FULL_TOPIC "}2%s"), GetTopic_P(stopic, CMND, mqtt_topic, "")); - WSContentSend_P(PSTR("}1" D_MQTT " " D_FALLBACK_TOPIC "}2%s"), GetFallbackTopic_P(stopic, "")); - } else { - WSContentSend_P(PSTR("}1" D_MQTT "}2" D_DISABLED)); - } - WSContentSend_P(PSTR("}1}2 ")); - -#ifdef USE_EMULATION - WSContentSend_P(PSTR("}1" D_EMULATION "}2%s"), GetTextIndexed(stopic, sizeof(stopic), Settings.flag2.emulation, kEmulationOptions)); -#else - WSContentSend_P(PSTR("}1" D_EMULATION "}2" D_DISABLED)); -#endif - -#ifdef USE_DISCOVERY - WSContentSend_P(PSTR("}1" D_MDNS_DISCOVERY "}2%s"), (Settings.flag3.mdns_enabled) ? D_ENABLED : D_DISABLED); - if (Settings.flag3.mdns_enabled) { -#ifdef WEBSERVER_ADVERTISE - WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_WEB_SERVER)); -#else - WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_DISABLED)); -#endif - } -#else - WSContentSend_P(PSTR("}1" D_MDNS_DISCOVERY "}2" D_DISABLED)); -#endif - - WSContentSend_P(PSTR("}1}2 ")); - WSContentSend_P(PSTR("}1" D_ESP_CHIP_ID "}2%d"), ESP_getChipId()); - WSContentSend_P(PSTR("}1" D_FLASH_CHIP_ID "}20x%06X"), ESP_getFlashChipId()); - WSContentSend_P(PSTR("}1" D_FLASH_CHIP_SIZE "}2%dkB"), ESP.getFlashChipRealSize() / 1024); - WSContentSend_P(PSTR("}1" D_PROGRAM_FLASH_SIZE "}2%dkB"), ESP.getFlashChipSize() / 1024); - WSContentSend_P(PSTR("}1" D_PROGRAM_SIZE "}2%dkB"), ESP_getSketchSize() / 1024); - WSContentSend_P(PSTR("}1" D_FREE_PROGRAM_SPACE "}2%dkB"), ESP.getFreeSketchSpace() / 1024); - WSContentSend_P(PSTR("}1" D_FREE_MEMORY "}2%dkB"), freeMem / 1024); - WSContentSend_P(PSTR("
")); - - WSContentSend_P(HTTP_SCRIPT_INFO_END); - WSContentSendStyle(); - - WSContentSend_P(PSTR("" - "
")); - - WSContentSpaceButton(BUTTON_MAIN); - WSContentStop(); -} -#endif - - - -void HandleUpgradeFirmware(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_FIRMWARE_UPGRADE); - - WSContentStart_P(S_FIRMWARE_UPGRADE); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_UPG, SettingsText(SET_OTAURL)); - WSContentSend_P(HTTP_FORM_RST_UPG, D_UPGRADE); - WSContentSpaceButton(BUTTON_MAIN); - WSContentStop(); - - Web.upload_error = 0; - Web.upload_file_type = UPL_TASMOTA; -} - -void HandleUpgradeFirmwareStart(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - char command[TOPSZ + 10]; - - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPGRADE_STARTED)); - WifiConfigCounter(); - - char otaurl[TOPSZ]; - WebGetArg("o", otaurl, sizeof(otaurl)); - if (strlen(otaurl)) { - snprintf_P(command, sizeof(command), PSTR(D_CMND_OTAURL " %s"), otaurl); - ExecuteWebCommand(command, SRC_WEBGUI); - } - - WSContentStart_P(S_INFORMATION); - WSContentSend_P(HTTP_SCRIPT_RELOAD_OTA); - WSContentSendStyle(); - WSContentSend_P(PSTR("
" D_UPGRADE_STARTED " ...
")); - WSContentSend_P(HTTP_MSG_RSTRT); - WSContentSpaceButton(BUTTON_MAIN); - WSContentStop(); - - snprintf_P(command, sizeof(command), PSTR(D_CMND_UPGRADE " 1")); - ExecuteWebCommand(command, SRC_WEBGUI); -} - -void HandleUploadDone(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPLOAD_DONE)); - - char error[100]; - - WifiConfigCounter(); - restart_flag = 0; - MqttRetryCounter(0); - - WSContentStart_P(S_INFORMATION); - if (!Web.upload_error) { - WSContentSend_P(HTTP_SCRIPT_RELOAD_OTA); - } - WSContentSendStyle(); - WSContentSend_P(PSTR("
" D_UPLOAD " " D_FAILED "

"), WebColor(COL_TEXT_WARNING)); -#ifdef USE_RF_FLASH - if (Web.upload_error < 15) { -#else - if ((Web.upload_error < 10) || (14 == Web.upload_error)) { - if (14 == Web.upload_error) { Web.upload_error = 10; } -#endif - GetTextIndexed(error, sizeof(error), Web.upload_error -1, kUploadErrors); - } else { - snprintf_P(error, sizeof(error), PSTR(D_UPLOAD_ERROR_CODE " %d"), Web.upload_error); - } - WSContentSend_P(error); - DEBUG_CORE_LOG(PSTR("UPL: %s"), error); - stop_flash_rotate = Settings.flag.stop_flash_rotate; - } else { - WSContentSend_P(PSTR("%06x'>" D_SUCCESSFUL "
"), WebColor(COL_TEXT_SUCCESS)); - WSContentSend_P(HTTP_MSG_RSTRT); - ShowWebSource(SRC_WEBGUI); -#ifdef USE_TASMOTA_SLAVE - if (TasmotaSlave_GetFlagFlashing()) { - restart_flag = 0; - } else { - restart_flag = 2; - } -#else - restart_flag = 2; -#endif - } - SettingsBufferFree(); - WSContentSend_P(PSTR("

")); - WSContentSpaceButton(BUTTON_MAIN); - WSContentStop(); -#ifdef USE_TASMOTA_SLAVE - if (TasmotaSlave_GetFlagFlashing()) { - TasmotaSlave_Flash(); - } -#endif -} - -void HandleUploadLoop(void) -{ - - bool _serialoutput = (LOG_LEVEL_DEBUG <= seriallog_level); - - if (HTTP_USER == Web.state) { return; } - if (Web.upload_error) { - if (UPL_TASMOTA == Web.upload_file_type) { Update.end(); } - return; - } - - HTTPUpload& upload = Webserver->upload(); - - if (UPLOAD_FILE_START == upload.status) { - restart_flag = 60; - if (0 == upload.filename.c_str()[0]) { - Web.upload_error = 1; - return; - } - SettingsSave(1); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_FILE " %s ..."), upload.filename.c_str()); - if (UPL_SETTINGS == Web.upload_file_type) { - if (!SettingsBufferAlloc()) { - Web.upload_error = 2; - return; - } - } else { - MqttRetryCounter(60); -#ifdef USE_EMULATION - UdpDisconnect(); -#endif -#ifdef USE_ARILUX_RF - AriluxRfDisable(); -#endif - if (Settings.flag.mqtt_enabled) { - MqttDisconnect(); - } - uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; - if (!Update.begin(maxSketchSpace)) { - - - - - - - Web.upload_error = 2; - return; - } - } - Web.upload_progress_dot_count = 0; - } else if (!Web.upload_error && (UPLOAD_FILE_WRITE == upload.status)) { - if (0 == upload.totalSize) { - if (UPL_SETTINGS == Web.upload_file_type) { - Web.config_block_count = 0; - } - else { -#ifdef USE_RF_FLASH - if ((SONOFF_BRIDGE == my_module_type) && (upload.buf[0] == ':')) { - Update.end(); - Web.upload_file_type = UPL_EFM8BB1; - - Web.upload_error = SnfBrUpdateInit(); - if (Web.upload_error != 0) { return; } - } else -#endif -#ifdef USE_TASMOTA_SLAVE - if ((WEMOS == my_module_type) && (upload.buf[0] == ':')) { - Update.end(); - Web.upload_file_type = UPL_TASMOTASLAVE; - Web.upload_error = TasmotaSlave_UpdateInit(); - if (Web.upload_error != 0) { return; } - } else -#endif - { - if ((upload.buf[0] != 0xE9) && (upload.buf[0] != 0x1F)) { - Web.upload_error = 3; - return; - } - if (0xE9 == upload.buf[0]) { - uint32_t bin_flash_size = ESP.magicFlashChipSize((upload.buf[3] & 0xf0) >> 4); - if (bin_flash_size > ESP.getFlashChipRealSize()) { - Web.upload_error = 4; - return; - } - - } - } - } - } - if (UPL_SETTINGS == Web.upload_file_type) { - if (!Web.upload_error) { - if (upload.currentSize > (sizeof(Settings) - (Web.config_block_count * HTTP_UPLOAD_BUFLEN))) { - Web.upload_error = 9; - return; - } - memcpy(settings_buffer + (Web.config_block_count * HTTP_UPLOAD_BUFLEN), upload.buf, upload.currentSize); - Web.config_block_count++; - } - } -#ifdef USE_RF_FLASH - else if (UPL_EFM8BB1 == Web.upload_file_type) { - if (efm8bb1_update != nullptr) { - ssize_t result = rf_glue_remnant_with_new_data_and_write(efm8bb1_update, upload.buf, upload.currentSize); - free(efm8bb1_update); - efm8bb1_update = nullptr; - if (result != 0) { - Web.upload_error = abs(result); - return; - } - } - ssize_t result = rf_search_and_write(upload.buf, upload.currentSize); - if (result < 0) { - Web.upload_error = abs(result); - return; - } else if (result > 0) { - if ((size_t)result > upload.currentSize) { - - Web.upload_error = 9; - return; - } - - size_t remnant_sz = upload.currentSize - result; - efm8bb1_update = (uint8_t *) malloc(remnant_sz + 1); - if (efm8bb1_update == nullptr) { - Web.upload_error = 2; - return; - } - memcpy(efm8bb1_update, upload.buf + result, remnant_sz); - - efm8bb1_update[remnant_sz] = '\0'; - } - } -#endif -#ifdef USE_TASMOTA_SLAVE - else if (UPL_TASMOTASLAVE == Web.upload_file_type) { - TasmotaSlave_WriteBuffer(upload.buf, upload.currentSize); - } -#endif - else { - if (!Web.upload_error && (Update.write(upload.buf, upload.currentSize) != upload.currentSize)) { - Web.upload_error = 5; - return; - } - if (_serialoutput) { - Serial.printf("."); - Web.upload_progress_dot_count++; - if (!(Web.upload_progress_dot_count % 80)) { Serial.println(); } - } - } - } else if(!Web.upload_error && (UPLOAD_FILE_END == upload.status)) { - if (_serialoutput && (Web.upload_progress_dot_count % 80)) { - Serial.println(); - } - if (UPL_SETTINGS == Web.upload_file_type) { - if (Web.config_xor_on_set) { - for (uint32_t i = 2; i < sizeof(Settings); i++) { - settings_buffer[i] ^= (Web.config_xor_on_set +i); - } - } - bool valid_settings = false; - 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]; - 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); - } - } else { - valid_settings = (settings_buffer[0] == CONFIG_FILE_SIGN); - } - - if (valid_settings) { -#ifdef ESP8266 - valid_settings = (0 == settings_buffer[0xF36]); -#endif -#ifdef ESP32 - valid_settings = (1 == settings_buffer[0xF36]); -#endif - } - - if (valid_settings) { - SettingsDefaultSet2(); - memcpy((char*)&Settings +16, settings_buffer +16, sizeof(Settings) -16); - Settings.version = buffer_version; - SettingsBufferFree(); - } else { - Web.upload_error = 8; - return; - } - } -#ifdef USE_RF_FLASH - else if (UPL_EFM8BB1 == Web.upload_file_type) { - - Web.upload_file_type = UPL_TASMOTA; - } -#endif -#ifdef USE_TASMOTA_SLAVE - else if (UPL_TASMOTASLAVE == Web.upload_file_type) { - - TasmotaSlave_SetFlagFlashing(true); - Web.upload_file_type = UPL_TASMOTA; - } -#endif - else { - if (!Update.end(true)) { - if (_serialoutput) { Update.printError(Serial); } - Web.upload_error = 6; - return; - } - if (!VersionCompatible()) { - Web.upload_error = 14; - return; - } - } - if (!Web.upload_error) { - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_SUCCESSFUL " %u bytes. " D_RESTARTING), upload.totalSize); - } - } else if (UPLOAD_FILE_ABORTED == upload.status) { - restart_flag = 0; - MqttRetryCounter(0); - Web.upload_error = 7; - if (UPL_TASMOTA == Web.upload_file_type) { Update.end(); } - } - delay(0); -} - - - -void HandlePreflightRequest(void) -{ - HttpHeaderCors(); - Webserver->sendHeader(F("Access-Control-Allow-Methods"), F("GET, POST")); - Webserver->sendHeader(F("Access-Control-Allow-Headers"), F("authorization")); - WSSend(200, CT_HTML, ""); -} - - - -void HandleHttpCommand(void) -{ - if (!HttpCheckPriviledgedAccess(false)) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_COMMAND)); - - if (strlen(SettingsText(SET_WEBPWD))) { - char tmp1[33]; - WebGetArg("user", tmp1, sizeof(tmp1)); - char tmp2[strlen(SettingsText(SET_WEBPWD)) +1]; - WebGetArg("password", tmp2, sizeof(tmp2)); - if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, SettingsText(SET_WEBPWD)))) { - WSContentBegin(401, CT_JSON); - WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_NEED_USER_AND_PASSWORD "\"}")); - WSContentEnd(); - return; - } - } - - WSContentBegin(200, CT_JSON); - uint32_t curridx = web_log_index; - String svalue = Webserver->arg("cmnd"); - if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) { - ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCOMMAND); - if (web_log_index != curridx) { - uint32_t counter = curridx; - WSContentSend_P(PSTR("{")); - bool cflg = false; - do { - char* tmp; - size_t len; - GetLog(counter, &tmp, &len); - if (len) { - - char* JSON = (char*)memchr(tmp, '{', len); - if (JSON) { - size_t JSONlen = len - (JSON - tmp); - if (JSONlen > sizeof(mqtt_data)) { JSONlen = sizeof(mqtt_data); } - char stemp[JSONlen]; - strlcpy(stemp, JSON +1, JSONlen -2); - WSContentSend_P(PSTR("%s%s"), (cflg) ? "," : "", stemp); - cflg = true; - } - } - counter++; - counter &= 0xFF; - if (!counter) counter++; - } while (counter != web_log_index); - WSContentSend_P(PSTR("}")); - } else { - WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_ENABLE_WEBLOG_FOR_RESPONSE "\"}")); - } - } else { - WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_ENTER_COMMAND " cmnd=\"}")); - } - WSContentEnd(); -} - - - -void HandleConsole(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - if (Webserver->hasArg("c2")) { - HandleConsoleRefresh(); - return; - } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONSOLE); - - WSContentStart_P(S_CONSOLE); - WSContentSend_P(HTTP_SCRIPT_CONSOL, Settings.web_refresh); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_CMND); - WSContentSpaceButton(BUTTON_MAIN); - WSContentStop(); -} - -void HandleConsoleRefresh(void) -{ - bool cflg = true; - uint32_t counter = 0; - - String svalue = Webserver->arg("c1"); - if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) { - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), svalue.c_str()); - ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCONSOLE); - } - - char stmp[8]; - WebGetArg("c2", stmp, sizeof(stmp)); - if (strlen(stmp)) { counter = atoi(stmp); } - - WSContentBegin(200, CT_PLAIN); - WSContentSend_P(PSTR("%d}1%d}1"), web_log_index, Web.reset_web_log_flag); - if (!Web.reset_web_log_flag) { - counter = 0; - Web.reset_web_log_flag = true; - } - if (counter != web_log_index) { - if (!counter) { - counter = web_log_index; - cflg = false; - } - do { - char* tmp; - size_t len; - GetLog(counter, &tmp, &len); - if (len) { - if (len > sizeof(mqtt_data) -2) { len = sizeof(mqtt_data); } - char stemp[len +1]; - strlcpy(stemp, tmp, len); - WSContentSend_P(PSTR("%s%s"), (cflg) ? "\n" : "", stemp); - cflg = true; - } - counter++; - counter &= 0xFF; - if (!counter) { counter++; } - } while (counter != web_log_index); - } - WSContentSend_P(PSTR("}1")); - WSContentEnd(); -} - - - -void HandleNotFound(void) -{ - - - if (CaptivePortal()) { return; } - -#ifdef USE_EMULATION -#ifdef USE_EMULATION_HUE - String path = Webserver->uri(); - if ((EMUL_HUE == Settings.flag2.emulation) && (path.startsWith("/api"))) { - HandleHueApi(&path); - } else -#endif -#endif - { - WSContentBegin(404, CT_PLAIN); - WSContentSend_P(PSTR(D_FILE_NOT_FOUND "\n\nURI: %s\nMethod: %s\nArguments: %d\n"), Webserver->uri().c_str(), (Webserver->method() == HTTP_GET) ? "GET" : "POST", Webserver->args()); - for (uint32_t i = 0; i < Webserver->args(); i++) { - WSContentSend_P(PSTR(" %s: %s\n"), Webserver->argName(i).c_str(), Webserver->arg(i).c_str()); - } - WSContentEnd(); - } -} - - -bool CaptivePortal(void) -{ - - if ((WifiIsInManagerMode()) && !ValidIpAddress(Webserver->hostHeader().c_str())) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_REDIRECTED)); - - Webserver->sendHeader(F("Location"), String("http://") + Webserver->client().localIP().toString(), true); - WSSend(302, CT_PLAIN, ""); - Webserver->client().stop(); - return true; - } - return false; -} - - - -String UrlEncode(const String& text) -{ - const char hex[] = "0123456789ABCDEF"; - - String encoded = ""; - int len = text.length(); - int i = 0; - while (i < len) { - char decodedChar = text.charAt(i++); -# 2762 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_01_webserver.ino" - if ((' ' == decodedChar) || ('+' == decodedChar)) { - encoded += '%'; - encoded += hex[decodedChar >> 4]; - encoded += hex[decodedChar & 0xF]; - } else { - encoded += decodedChar; - } - - } - return encoded; -} - -int WebSend(char *buffer) -{ - - - - - - char *host; - char *user; - char *password; - char *command; - int status = 1; - - - host = strtok_r(buffer, "]", &command); - if (host && command) { - RemoveSpace(host); - host++; - host = strtok_r(host, ",", &user); - String url = F("http://"); - url += host; - - command = Trim(command); - if (command[0] != '/') { - url += F("/cm?"); - if (user) { - user = strtok_r(user, ":", &password); - if (user && password) { - char userpass[200]; - snprintf_P(userpass, sizeof(userpass), PSTR("user=%s&password=%s&"), user, password); - url += userpass; - } - } - url += F("cmnd="); - } - url += command; - - DEBUG_CORE_LOG(PSTR("WEB: Uri |%s|"), url.c_str()); - -#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) - HTTPClient http; - if (http.begin(UrlEncode(url))) { -#else - WiFiClient http_client; - HTTPClient http; - if (http.begin(http_client, UrlEncode(url))) { -#endif - int http_code = http.GET(); - if (http_code > 0) { - if (http_code == HTTP_CODE_OK || http_code == HTTP_CODE_MOVED_PERMANENTLY) { -#ifdef USE_WEBSEND_RESPONSE - - const char* read = http.getString().c_str(); - uint32_t j = 0; - char text = '.'; - while (text != '\0') { - text = *read++; - if (text > 31) { - mqtt_data[j++] = text; - if (j == sizeof(mqtt_data) -2) { break; } - } - } - mqtt_data[j] = '\0'; - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_WEBSEND)); -#ifdef USE_SCRIPT -extern uint8_t tasm_cmd_activ; - - tasm_cmd_activ=0; - XdrvRulesProcess(); -#endif -#endif - } - status = 0; - } else { - status = 2; - } - http.end(); - } else { - status = 3; - } - } - return status; -} - -bool JsonWebColor(const char* dataBuf) -{ - - - - - - char dataBufLc[strlen(dataBuf) +1]; - LowerCase(dataBufLc, dataBuf); - RemoveSpace(dataBufLc); - if (strlen(dataBufLc) < 9) { return false; } - - StaticJsonBuffer<450> jb; - JsonObject& obj = jb.parseObject(dataBufLc); - if (!obj.success()) { return false; } - - char parm_lc[10]; - if (obj[LowerCase(parm_lc, D_CMND_WEBCOLOR)].success()) { - for (uint32_t i = 0; i < COL_LAST; i++) { - const char* color = obj[parm_lc][i]; - if (color != nullptr) { - WebHexCode(i, color); - } - } - } - return true; -} - -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 = "|" -#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 "|" D_CMND_WEBBUTTON "|" D_CMND_CORS; - -void (* const WebCommand[])(void) PROGMEM = { -#ifdef USE_EMULATION - &CmndEmulation, -#endif -#ifdef USE_SENDMAIL - &CmndSendmail, -#endif - &CmndWebServer, &CmndWebPassword, &CmndWeblog, &CmndWebRefresh, &CmndWebSend, &CmndWebColor, - &CmndWebSensor, &CmndWebButton, &CmndCors }; - - - - - -#ifdef USE_EMULATION -void CmndEmulation(void) -{ -#if defined(USE_EMULATION_WEMO) || defined(USE_EMULATION_HUE) -#if defined(USE_EMULATION_WEMO) && defined(USE_EMULATION_HUE) - if ((XdrvMailbox.payload >= EMUL_NONE) && (XdrvMailbox.payload < EMUL_MAX)) { -#else -#ifndef USE_EMULATION_WEMO - if ((EMUL_NONE == XdrvMailbox.payload) || (EMUL_HUE == XdrvMailbox.payload)) { -#endif -#ifndef USE_EMULATION_HUE - if ((EMUL_NONE == XdrvMailbox.payload) || (EMUL_WEMO == XdrvMailbox.payload)) { -#endif -#endif - Settings.flag2.emulation = XdrvMailbox.payload; - restart_flag = 2; - } -#endif - ResponseCmndNumber(Settings.flag2.emulation); -} -#endif - -#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 - - -void CmndWebServer(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { - Settings.webserver = XdrvMailbox.payload; - } - if (Settings.webserver) { - Response_P(PSTR("{\"" D_CMND_WEBSERVER "\":\"" D_JSON_ACTIVE_FOR " %s " D_JSON_ON_DEVICE " %s " D_JSON_WITH_IP_ADDRESS " %s\"}"), - (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str()); - } else { - ResponseCmndStateText(0); - } -} - -void CmndWebPassword(void) -{ - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_WEBPWD, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? WEB_PASSWORD : XdrvMailbox.data); - ResponseCmndChar(SettingsText(SET_WEBPWD)); - } else { - Response_P(S_JSON_COMMAND_ASTERISK, XdrvMailbox.command); - } -} - -void CmndWeblog(void) -{ - if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { - Settings.weblog_level = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.weblog_level); -} - -void CmndWebRefresh(void) -{ - if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload <= 10000)) { - Settings.web_refresh = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.web_refresh); -} - -void CmndWebSend(void) -{ - if (XdrvMailbox.data_len > 0) { - uint32_t result = WebSend(XdrvMailbox.data); - char stemp1[20]; - ResponseCmndChar(GetTextIndexed(stemp1, sizeof(stemp1), result, kWebSendStatus)); - } -} - -void CmndWebColor(void) -{ - if (XdrvMailbox.data_len > 0) { - if (strstr(XdrvMailbox.data, "{") == nullptr) { - if ((XdrvMailbox.data_len > 3) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= COL_LAST)) { - WebHexCode(XdrvMailbox.index -1, XdrvMailbox.data); - } - else if (0 == XdrvMailbox.payload) { - SettingsDefaultWebColor(); - } - } - else { - JsonWebColor(XdrvMailbox.data); - } - } - Response_P(PSTR("{\"" D_CMND_WEBCOLOR "\":[")); - for (uint32_t i = 0; i < COL_LAST; i++) { - if (i) { ResponseAppend_P(PSTR(",")); } - ResponseAppend_P(PSTR("\"#%06x\""), WebColor(i)); - } - ResponseAppend_P(PSTR("]}")); -} - -void CmndWebSensor(void) -{ - if (XdrvMailbox.index < MAX_XSNS_DRIVERS) { - if (XdrvMailbox.payload >= 0) { - bitWrite(Settings.sensors[XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1); - } - } - Response_P(PSTR("{\"" D_CMND_WEBSENSOR "\":")); - XsnsSensorState(); - ResponseJsonEnd(); -} - -void CmndWebButton(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_BUTTON_TEXT)) { - if (!XdrvMailbox.usridx) { - ResponseCmndAll(SET_BUTTON1, MAX_BUTTON_TEXT); - } else { - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_BUTTON1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data); - } - ResponseCmndIdxChar(SettingsText(SET_BUTTON1 + XdrvMailbox.index -1)); - } - } -} - -void CmndCors(void) -{ - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_CORS, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? WEB_PASSWORD : XdrvMailbox.data); - } - ResponseCmndChar(SettingsText(SET_CORS)); -} - - - - - -bool Xdrv01(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_LOOP: - PollDnsWebserver(); -#ifdef USE_EMULATION -#ifdef USE_DEVICE_GROUPS - if (Settings.flag2.emulation || Settings.flag4.device_groups_enabled) { PollUdp(); } -#else - if (Settings.flag2.emulation) { PollUdp(); } -#endif -#endif - break; - case FUNC_COMMAND: - result = DecodeCommand(kWebCommands, WebCommand); - break; - } - return result; -} -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_02_mqtt.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_02_mqtt.ino" -#define XDRV_02 2 - - - -#ifdef USE_MQTT_TLS - #include "WiFiClientSecureLightBearSSL.h" - BearSSL::WiFiClientSecure_light *tlsClient; -#else - WiFiClient EspClient; -#endif - -const char kMqttCommands[] PROGMEM = "|" -#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) - D_CMND_MQTTFINGERPRINT "|" -#endif - D_CMND_MQTTUSER "|" D_CMND_MQTTPASSWORD "|" -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - D_CMND_TLSKEY "|" -#endif - D_CMND_MQTTHOST "|" D_CMND_MQTTPORT "|" D_CMND_MQTTRETRY "|" D_CMND_STATETEXT "|" D_CMND_MQTTCLIENT "|" - D_CMND_FULLTOPIC "|" D_CMND_PREFIX "|" D_CMND_GROUPTOPIC "|" D_CMND_TOPIC "|" D_CMND_PUBLISH "|" D_CMND_MQTTLOG "|" - D_CMND_BUTTONTOPIC "|" D_CMND_SWITCHTOPIC "|" D_CMND_BUTTONRETAIN "|" D_CMND_SWITCHRETAIN "|" D_CMND_POWERRETAIN "|" D_CMND_SENSORRETAIN ; - -void (* const MqttCommand[])(void) PROGMEM = { -#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) - &CmndMqttFingerprint, -#endif - &CmndMqttUser, &CmndMqttPassword, -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - &CmndTlsKey, -#endif - &CmndMqttHost, &CmndMqttPort, &CmndMqttRetry, &CmndStateText, &CmndMqttClient, - &CmndFullTopic, &CmndPrefix, &CmndGroupTopic, &CmndTopic, &CmndPublish, &CmndMqttlog, - &CmndButtonTopic, &CmndSwitchTopic, &CmndButtonRetain, &CmndSwitchRetain, &CmndPowerRetain, &CmndSensorRetain }; - -struct MQTT { - uint16_t connect_count = 0; - uint16_t retry_counter = 1; - uint8_t initial_connection_state = 2; - bool connected = false; - bool allowed = false; -} Mqtt; - -#ifdef USE_MQTT_TLS - -#ifdef USE_MQTT_AWS_IOT -#include - -const br_ec_private_key *AWS_IoT_Private_Key = nullptr; -const br_x509_certificate *AWS_IoT_Client_Certificate = nullptr; - -class tls_entry_t { -public: - uint32_t name; - uint16_t start; - uint16_t len; -}; - -const static uint32_t TLS_NAME_SKEY = 0x2079656B; -const static uint32_t TLS_NAME_CRT = 0x20747263; - -class tls_dir_t { -public: - tls_entry_t entry[4]; -}; - -tls_dir_t tls_dir; - -#endif - - - - -bool is_fingerprint_mono_value(uint8_t finger[20], uint8_t value) { - for (uint32_t i = 0; i<20; i++) { - if (finger[i] != value) { - return false; - } - } - return true; -} -#endif - -void MakeValidMqtt(uint32_t option, char* str) -{ - - - uint32_t i = 0; - while (str[i] > 0) { - - if ((str[i] == '+') || (str[i] == '#') || (str[i] == ' ')) { - if (option) { - uint32_t j = i; - while (str[j] > 0) { - str[j] = str[j +1]; - j++; - } - i--; - } else { - str[i] = '_'; - } - } - i++; - } -} - -#ifdef USE_DISCOVERY -#ifdef MQTT_HOST_DISCOVERY -void MqttDiscoverServer(void) -{ - if (!Wifi.mdns_begun) { return; } - - int n = MDNS.queryService("mqtt", "tcp"); - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_QUERY_DONE " %d"), n); - - if (n > 0) { - uint32_t i = 0; -#ifdef MDNS_HOSTNAME - for (i = n; i > 0; i--) { - if (!strcmp(MDNS.hostname(i).c_str(), MDNS_HOSTNAME)) { - break; - } - } -#endif - SettingsUpdateText(SET_MQTT_HOST, MDNS.IP(i).toString().c_str()); - Settings.mqtt_port = MDNS.port(i); - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_MQTT_SERVICE_FOUND " %s, " D_IP_ADDRESS " %s, " D_PORT " %d"), MDNS.hostname(i).c_str(), SettingsText(SET_MQTT_HOST), Settings.mqtt_port); - } -} -#endif -#endif -# 163 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_02_mqtt.ino" -#include - - -#if (MQTT_MAX_PACKET_SIZE -TOPSZ -7) < MIN_MESSZ - #error "MQTT_MAX_PACKET_SIZE is too small in libraries/PubSubClient/src/PubSubClient.h, increase it to at least 1200" -#endif - -#ifdef USE_MQTT_TLS -PubSubClient MqttClient; -#else -PubSubClient MqttClient(EspClient); -#endif - -void MqttInit(void) -{ -#ifdef USE_MQTT_TLS - tlsClient = new BearSSL::WiFiClientSecure_light(1024,1024); - -#ifdef USE_MQTT_AWS_IOT - loadTlsDir(); - tlsClient->setClientECCert(AWS_IoT_Client_Certificate, - AWS_IoT_Private_Key, - 0xFFFF , 0); -#endif - -#ifdef USE_MQTT_TLS_CA_CERT -#ifdef USE_MQTT_AWS_IOT - tlsClient->setTrustAnchor(&AmazonRootCA1_TA); -#else - tlsClient->setTrustAnchor(&LetsEncryptX3CrossSigned_TA); -#endif -#endif - - MqttClient.setClient(*tlsClient); -#endif -} - -bool MqttIsConnected(void) -{ - return MqttClient.connected(); -} - -void MqttDisconnect(void) -{ - MqttClient.disconnect(); -} - -void MqttSubscribeLib(const char *topic) -{ - MqttClient.subscribe(topic); - MqttClient.loop(); -} - -void MqttUnsubscribeLib(const char *topic) -{ - MqttClient.unsubscribe(topic); - MqttClient.loop(); -} - -bool MqttPublishLib(const char* topic, bool retained) -{ - - if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) { - char *str = strstr(topic, SettingsText(SET_MQTTPREFIX1)); - if (str == topic) { - mqtt_cmnd_blocked_reset = 4; - mqtt_cmnd_blocked++; - } - } - - bool result = MqttClient.publish(topic, mqtt_data, retained); - yield(); - return result; -} - -void MqttDataHandler(char* mqtt_topic, uint8_t* mqtt_data, unsigned int data_len) -{ -#ifdef USE_DEBUG_DRIVER - ShowFreeMem(PSTR("MqttDataHandler")); -#endif - - - if (data_len >= MQTT_MAX_PACKET_SIZE) { return; } - - - if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) { - char *str = strstr(mqtt_topic, SettingsText(SET_MQTTPREFIX1)); - if ((str == mqtt_topic) && mqtt_cmnd_blocked) { - mqtt_cmnd_blocked--; - return; - } - } - - - char topic[TOPSZ]; - strlcpy(topic, mqtt_topic, sizeof(topic)); - mqtt_data[data_len] = 0; - char data[data_len +1]; - memcpy(data, mqtt_data, sizeof(data)); - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_MQTT D_RECEIVED_TOPIC " \"%s\", " D_DATA_SIZE " %d, " D_DATA " \"%s\""), topic, data_len, data); - - - - XdrvMailbox.index = strlen(topic); - XdrvMailbox.data_len = data_len; - XdrvMailbox.topic = topic; - XdrvMailbox.data = (char*)data; - if (XdrvCall(FUNC_MQTT_DATA)) { return; } - - ShowSource(SRC_MQTT); - - CommandHandler(topic, data, data_len); -} - - - -void MqttRetryCounter(uint8_t value) -{ - Mqtt.retry_counter = value; -} - -void MqttSubscribe(const char *topic) -{ - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT D_SUBSCRIBE_TO " %s"), topic); - MqttSubscribeLib(topic); -} - -void MqttUnsubscribe(const char *topic) -{ - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT D_UNSUBSCRIBE_FROM " %s"), topic); - MqttUnsubscribeLib(topic); -} - -void MqttPublishLogging(const char *mxtime) -{ - char saved_mqtt_data[strlen(mqtt_data) +1]; - memcpy(saved_mqtt_data, mqtt_data, sizeof(saved_mqtt_data)); - - - Response_P(PSTR("%s%s"), mxtime, log_data); - char stopic[TOPSZ]; - GetTopic_P(stopic, STAT, mqtt_topic, PSTR("LOGGING")); - MqttPublishLib(stopic, false); - - memcpy(mqtt_data, saved_mqtt_data, sizeof(saved_mqtt_data)); -} - -void MqttPublish(const char* topic, bool retained) -{ -#ifdef USE_DEBUG_DRIVER - ShowFreeMem(PSTR("MqttPublish")); -#endif - -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) || defined(MQTT_NO_RETAIN) - - - - retained = false; -#endif - - char sretained[CMDSZ]; - sretained[0] = '\0'; - char slog_type[20]; - snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_RESULT)); - - if (Settings.flag.mqtt_enabled) { - if (MqttPublishLib(topic, retained)) { - snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_MQTT)); - if (retained) { - snprintf_P(sretained, sizeof(sretained), PSTR(" (" D_RETAINED ")")); - } - } - } - - snprintf_P(log_data, sizeof(log_data), PSTR("%s%s = %s"), slog_type, (Settings.flag.mqtt_enabled) ? topic : strrchr(topic,'/')+1, mqtt_data); - if (strlen(log_data) >= (sizeof(log_data) - strlen(sretained) -1)) { - log_data[sizeof(log_data) - strlen(sretained) -5] = '\0'; - snprintf_P(log_data, sizeof(log_data), PSTR("%s ..."), log_data); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s%s"), log_data, sretained); - AddLog(LOG_LEVEL_INFO); - - if (Settings.ledstate &0x04) { - blinks++; - } -} - -void MqttPublish(const char* topic) -{ - MqttPublish(topic, false); -} - -void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retained) -{ - - - - - - - - char romram[64]; - char stopic[TOPSZ]; - - snprintf_P(romram, sizeof(romram), ((prefix > 3) && !Settings.flag.mqtt_response) ? S_RSLT_RESULT : subtopic); - for (uint32_t i = 0; i < strlen(romram); i++) { - romram[i] = toupper(romram[i]); - } - prefix &= 3; - GetTopic_P(stopic, prefix, mqtt_topic, romram); - MqttPublish(stopic, retained); - -#ifdef USE_MQTT_AWS_IOT - if ((prefix > 0) && (Settings.flag4.awsiot_shadow) && (Mqtt.connected)) { - - char *topic = SettingsText(SET_MQTT_TOPIC); - char topic2[strlen(topic)+1]; - strcpy(topic2, topic); - - char *s = topic2; - while (*s) { - if ('/' == *s) { - *s = '_'; - } - s++; - } - - snprintf_P(romram, sizeof(romram), PSTR("$aws/things/%s/shadow/update"), topic2); - - - char *mqtt_save = (char*) malloc(strlen(mqtt_data)+1); - if (!mqtt_save) { return; } - strcpy(mqtt_save, mqtt_data); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"state\":{\"reported\":%s}}"), mqtt_save); - free(mqtt_save); - - bool result = MqttClient.publish(romram, mqtt_data, false); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT "Updated shadow: %s"), romram); - yield(); - } -#endif -} - -void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic) -{ - MqttPublishPrefixTopic_P(prefix, subtopic, false); -} - -void MqttPublishTeleSensor(void) -{ - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); - XdrvRulesProcess(); -} - -void MqttPublishPowerState(uint32_t device) -{ - char stopic[TOPSZ]; - char scommand[33]; - - if ((device < 1) || (device > devices_present)) { device = 1; } - -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan() && (device > 1)) { - if (GetFanspeed() < MaxFanspeed()) { -#ifdef USE_DOMOTICZ - DomoticzUpdateFanState(); -#endif - snprintf_P(scommand, sizeof(scommand), PSTR(D_CMND_FANSPEED)); - GetTopic_P(stopic, STAT, mqtt_topic, (Settings.flag.mqtt_response) ? scommand : S_RSLT_RESULT); - Response_P(S_JSON_COMMAND_NVALUE, scommand, GetFanspeed()); - MqttPublish(stopic); - } - } else { -#endif - GetPowerDevice(scommand, device, sizeof(scommand), Settings.flag.device_index_enable); - GetTopic_P(stopic, STAT, mqtt_topic, (Settings.flag.mqtt_response) ? scommand : S_RSLT_RESULT); - Response_P(S_JSON_COMMAND_SVALUE, scommand, GetStateText(bitRead(power, device -1))); - MqttPublish(stopic); - - if (!Settings.flag4.only_json_message) { - GetTopic_P(stopic, STAT, mqtt_topic, scommand); - Response_P(GetStateText(bitRead(power, device -1))); - MqttPublish(stopic, Settings.flag.mqtt_power_retain); - } -#ifdef USE_SONOFF_IFAN - } -#endif -} - -void MqttPublishAllPowerState(void) -{ - for (uint32_t i = 1; i <= devices_present; i++) { - MqttPublishPowerState(i); -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan()) { break; } -#endif - } -} - -void MqttPublishPowerBlinkState(uint32_t device) -{ - char scommand[33]; - - if ((device < 1) || (device > devices_present)) { - device = 1; - } - Response_P(PSTR("{\"%s\":\"" D_JSON_BLINK " %s\"}"), - GetPowerDevice(scommand, device, sizeof(scommand), Settings.flag.device_index_enable), GetStateText(bitRead(blink_mask, device -1))); - - MqttPublishPrefixTopic_P(RESULT_OR_STAT, S_RSLT_POWER); -} - - - -uint16_t MqttConnectCount(void) -{ - return Mqtt.connect_count; -} - -void MqttDisconnected(int state) -{ - Mqtt.connected = false; - Mqtt.retry_counter = Settings.mqtt_retry; - - MqttClient.disconnect(); - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), SettingsText(SET_MQTT_HOST), Settings.mqtt_port, state, Mqtt.retry_counter); - rules_flag.mqtt_disconnected = 1; -} - -void MqttConnected(void) -{ - char stopic[TOPSZ]; - - if (Mqtt.allowed) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_CONNECTED)); - Mqtt.connected = true; - Mqtt.retry_counter = 0; - Mqtt.connect_count++; - - GetTopic_P(stopic, TELE, mqtt_topic, S_LWT); - Response_P(PSTR(D_ONLINE)); - MqttPublish(stopic, true); - - if (!Settings.flag4.only_json_message) { - - mqtt_data[0] = '\0'; - MqttPublishPrefixTopic_P(CMND, S_RSLT_POWER); - } - - GetTopic_P(stopic, CMND, mqtt_topic, PSTR("#")); - MqttSubscribe(stopic); - if (strstr_P(SettingsText(SET_MQTT_FULLTOPIC), MQTT_TOKEN_TOPIC) != nullptr) { - uint32_t real_index = SET_MQTT_GRP_TOPIC; - for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { - if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } - if (strlen(SettingsText(real_index +i))) { - GetGroupTopic_P(stopic, PSTR("#"), real_index +i); - MqttSubscribe(stopic); - } - } - GetFallbackTopic_P(stopic, PSTR("#")); - MqttSubscribe(stopic); - } - - XdrvCall(FUNC_MQTT_SUBSCRIBE); - } - - if (Mqtt.initial_connection_state) { - if (ResetReason() != REASON_DEEP_SLEEP_AWAKE) { - char stopic2[TOPSZ]; - Response_P(PSTR("{\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_FALLBACKTOPIC "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\"}"), - ModuleName().c_str(), my_version, my_image, GetFallbackTopic_P(stopic, ""), GetGroupTopic_P(stopic2, "", SET_MQTT_GRP_TOPIC)); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "1")); -#ifdef USE_WEBSERVER - if (Settings.webserver) { -#if LWIP_IPV6 - Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"IPv6Address\":\"%s\"}"), - (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str(),WifiGetIPv6().c_str()); -#else - Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\"}"), - (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str()); -#endif - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "2")); - } -#endif - Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":")); - if (CrashFlag()) { - CrashDump(); - } else { - ResponseAppend_P(PSTR("\"%s\""), GetResetReason().c_str()); - } - ResponseJsonEnd(); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "3")); - } - - MqttPublishAllPowerState(); - if (Settings.tele_period) { - tele_period = Settings.tele_period -5; - } - rules_flag.system_boot = 1; - XdrvCall(FUNC_MQTT_INIT); - } - Mqtt.initial_connection_state = 0; - - global_state.mqtt_down = 0; - if (Settings.flag.mqtt_enabled) { - rules_flag.mqtt_connected = 1; - } -} - -void MqttReconnect(void) -{ - char stopic[TOPSZ]; - - Mqtt.allowed = Settings.flag.mqtt_enabled; - if (Mqtt.allowed) { -#ifdef USE_DISCOVERY -#ifdef MQTT_HOST_DISCOVERY - MqttDiscoverServer(); -#endif -#endif - if (!strlen(SettingsText(SET_MQTT_HOST)) || !Settings.mqtt_port) { - Mqtt.allowed = false; - } -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - - if (!AWS_IoT_Private_Key || !AWS_IoT_Client_Certificate) { - Mqtt.allowed = false; - } -#endif - } - if (!Mqtt.allowed) { - MqttConnected(); - return; - } - -#ifdef USE_EMULATION - UdpDisconnect(); -#endif - - AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_ATTEMPTING_CONNECTION)); - - Mqtt.connected = false; - Mqtt.retry_counter = Settings.mqtt_retry; - global_state.mqtt_down = 1; - - char *mqtt_user = nullptr; - char *mqtt_pwd = nullptr; - if (strlen(SettingsText(SET_MQTT_USER))) { - mqtt_user = SettingsText(SET_MQTT_USER); - } - if (strlen(SettingsText(SET_MQTT_PWD))) { - mqtt_pwd = SettingsText(SET_MQTT_PWD); - } - - GetTopic_P(stopic, TELE, mqtt_topic, S_LWT); - Response_P(S_OFFLINE); - - if (MqttClient.connected()) { MqttClient.disconnect(); } -#ifdef USE_MQTT_TLS - tlsClient->stop(); -#else - EspClient = WiFiClient(); - MqttClient.setClient(EspClient); -#endif - - if (2 == Mqtt.initial_connection_state) { - Mqtt.initial_connection_state = 1; - } - - MqttClient.setCallback(MqttDataHandler); -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - - tlsClient->setClientECCert(AWS_IoT_Client_Certificate, - AWS_IoT_Private_Key, - 0xFFFF , 0); -#endif - MqttClient.setServer(SettingsText(SET_MQTT_HOST), Settings.mqtt_port); - - uint32_t mqtt_connect_time = millis(); -#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) - bool allow_all_fingerprints = false; - bool learn_fingerprint1 = is_fingerprint_mono_value(Settings.mqtt_fingerprint[0], 0x00); - bool learn_fingerprint2 = is_fingerprint_mono_value(Settings.mqtt_fingerprint[1], 0x00); - allow_all_fingerprints |= is_fingerprint_mono_value(Settings.mqtt_fingerprint[0], 0xff); - allow_all_fingerprints |= is_fingerprint_mono_value(Settings.mqtt_fingerprint[1], 0xff); - allow_all_fingerprints |= learn_fingerprint1; - allow_all_fingerprints |= learn_fingerprint2; - tlsClient->setPubKeyFingerprint(Settings.mqtt_fingerprint[0], Settings.mqtt_fingerprint[1], allow_all_fingerprints); -#endif -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "AWS IoT endpoint: %s"), SettingsText(SET_MQTT_HOST)); - if (MqttClient.connect(mqtt_client, nullptr, nullptr, stopic, 1, false, mqtt_data, MQTT_CLEAN_SESSION)) { -#else - if (MqttClient.connect(mqtt_client, mqtt_user, mqtt_pwd, stopic, 1, true, mqtt_data, MQTT_CLEAN_SESSION)) { -#endif -#ifdef USE_MQTT_TLS - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "TLS connected in %d ms, max ThunkStack used %d"), - millis() - mqtt_connect_time, tlsClient->getMaxThunkStackUse()); - if (!tlsClient->getMFLNStatus()) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR("MFLN not supported by TLS server")); - } -#ifndef USE_MQTT_TLS_CA_CERT - - char buf_fingerprint[64]; - ToHex_P((unsigned char *)tlsClient->getRecvPubKeyFingerprint(), 20, buf_fingerprint, sizeof(buf_fingerprint), ' '); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT "Server fingerprint: %s"), buf_fingerprint); - - if (learn_fingerprint1 || learn_fingerprint2) { - - bool fingerprint_matched = false; - const uint8_t *recv_fingerprint = tlsClient->getRecvPubKeyFingerprint(); - if (0 == memcmp(recv_fingerprint, Settings.mqtt_fingerprint[0], 20)) { - fingerprint_matched = true; - } - if (0 == memcmp(recv_fingerprint, Settings.mqtt_fingerprint[1], 20)) { - fingerprint_matched = true; - } - if (!fingerprint_matched) { - - if (learn_fingerprint1) { - memcpy(Settings.mqtt_fingerprint[0], recv_fingerprint, 20); - } - if (learn_fingerprint2) { - memcpy(Settings.mqtt_fingerprint[1], recv_fingerprint, 20); - } - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "Fingerprint learned: %s"), buf_fingerprint); - - SettingsSaveAll(); - } - } -#endif -#endif - MqttConnected(); - } else { -#ifdef USE_MQTT_TLS - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "TLS connection error: %d"), tlsClient->getLastError()); -#endif - MqttDisconnected(MqttClient.state()); - } -} - -void MqttCheck(void) -{ - if (Settings.flag.mqtt_enabled) { - if (!MqttIsConnected()) { - global_state.mqtt_down = 1; - if (!Mqtt.retry_counter) { - MqttReconnect(); - } else { - Mqtt.retry_counter--; - } - } else { - global_state.mqtt_down = 0; - } - } else { - global_state.mqtt_down = 0; - if (Mqtt.initial_connection_state) { - MqttReconnect(); - } - } -} - -bool KeyTopicActive(uint32_t key) -{ - - - key &= 1; - char key_topic[TOPSZ]; - Format(key_topic, SettingsText(SET_MQTT_BUTTON_TOPIC + key), sizeof(key_topic)); - return ((strlen(key_topic) != 0) && strcmp(key_topic, "0")); -} - - - - - -#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) -void CmndMqttFingerprint(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { - char fingerprint[60]; - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(fingerprint))) { - strlcpy(fingerprint, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? MQTT_FINGERPRINT1 : MQTT_FINGERPRINT2 : XdrvMailbox.data, sizeof(fingerprint)); - char *p = fingerprint; - for (uint32_t i = 0; i < 20; i++) { - Settings.mqtt_fingerprint[XdrvMailbox.index -1][i] = strtol(p, &p, 16); - } - restart_flag = 2; - } - ResponseCmndIdxChar(ToHex_P((unsigned char *)Settings.mqtt_fingerprint[XdrvMailbox.index -1], 20, fingerprint, sizeof(fingerprint), ' ')); - } -} -#endif - -void CmndMqttUser(void) -{ - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_MQTT_USER, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_USER : XdrvMailbox.data); - restart_flag = 2; - } - ResponseCmndChar(SettingsText(SET_MQTT_USER)); -} - -void CmndMqttPassword(void) -{ - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_MQTT_PWD, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_PASS : XdrvMailbox.data); - ResponseCmndChar(SettingsText(SET_MQTT_PWD)); - restart_flag = 2; - } else { - Response_P(S_JSON_COMMAND_ASTERISK, XdrvMailbox.command); - } -} - -void CmndMqttlog(void) -{ - if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { - Settings.mqttlog_level = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.mqttlog_level); -} - -void CmndMqttHost(void) -{ - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_MQTT_HOST, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_HOST : XdrvMailbox.data); - restart_flag = 2; - } - ResponseCmndChar(SettingsText(SET_MQTT_HOST)); -} - -void CmndMqttPort(void) -{ - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 65536)) { - Settings.mqtt_port = (1 == XdrvMailbox.payload) ? MQTT_PORT : XdrvMailbox.payload; - restart_flag = 2; - } - ResponseCmndNumber(Settings.mqtt_port); -} - -void CmndMqttRetry(void) -{ - if ((XdrvMailbox.payload >= MQTT_RETRY_SECS) && (XdrvMailbox.payload < 32001)) { - Settings.mqtt_retry = XdrvMailbox.payload; - Mqtt.retry_counter = Settings.mqtt_retry; - } - ResponseCmndNumber(Settings.mqtt_retry); -} - -void CmndStateText(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_STATE_TEXT)) { - if (!XdrvMailbox.usridx) { - ResponseCmndAll(SET_STATE_TXT1, MAX_STATE_TEXT); - } else { - if (XdrvMailbox.data_len > 0) { - for (uint32_t i = 0; i <= XdrvMailbox.data_len; i++) { - if (XdrvMailbox.data[i] == ' ') XdrvMailbox.data[i] = '_'; - } - SettingsUpdateText(SET_STATE_TXT1 + XdrvMailbox.index -1, XdrvMailbox.data); - } - ResponseCmndIdxChar(GetStateText(XdrvMailbox.index -1)); - } - } -} - -void CmndMqttClient(void) -{ - if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { - SettingsUpdateText(SET_MQTT_CLIENT, (SC_DEFAULT == Shortcut()) ? MQTT_CLIENT_ID : XdrvMailbox.data); - restart_flag = 2; - } - ResponseCmndChar(SettingsText(SET_MQTT_CLIENT)); -} - -void CmndFullTopic(void) -{ - if (XdrvMailbox.data_len > 0) { - MakeValidMqtt(1, XdrvMailbox.data); - if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } - char stemp1[TOPSZ]; - strlcpy(stemp1, (SC_DEFAULT == Shortcut()) ? MQTT_FULLTOPIC : XdrvMailbox.data, sizeof(stemp1)); - if (strcmp(stemp1, SettingsText(SET_MQTT_FULLTOPIC))) { - Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); - MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); - SettingsUpdateText(SET_MQTT_FULLTOPIC, stemp1); - restart_flag = 2; - } - } - ResponseCmndChar(SettingsText(SET_MQTT_FULLTOPIC)); -} - -void CmndPrefix(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_MQTT_PREFIXES)) { - if (!XdrvMailbox.usridx) { - ResponseCmndAll(SET_MQTTPREFIX1, MAX_MQTT_PREFIXES); - } else { - if (XdrvMailbox.data_len > 0) { - MakeValidMqtt(0, XdrvMailbox.data); - SettingsUpdateText(SET_MQTTPREFIX1 + XdrvMailbox.index -1, - (SC_DEFAULT == Shortcut()) ? (1==XdrvMailbox.index) ? SUB_PREFIX : (2==XdrvMailbox.index) ? PUB_PREFIX : PUB_PREFIX2 : XdrvMailbox.data); - restart_flag = 2; - } - ResponseCmndIdxChar(SettingsText(SET_MQTTPREFIX1 + XdrvMailbox.index -1)); - } - } -} - -void CmndPublish(void) -{ - if (XdrvMailbox.data_len > 0) { - char *payload_part; - char *mqtt_part = strtok_r(XdrvMailbox.data, " ", &payload_part); - if (mqtt_part) { - char stemp1[TOPSZ]; - strlcpy(stemp1, mqtt_part, sizeof(stemp1)); - if ((payload_part != nullptr) && strlen(payload_part)) { - strlcpy(mqtt_data, payload_part, sizeof(mqtt_data)); - } else { - mqtt_data[0] = '\0'; - } - MqttPublish(stemp1, (XdrvMailbox.index == 2)); - - mqtt_data[0] = '\0'; - } - } -} - -void CmndGroupTopic(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_GROUP_TOPICS)) { - if (XdrvMailbox.data_len > 0) { - uint32_t settings_text_index = (1 == XdrvMailbox.index) ? SET_MQTT_GRP_TOPIC : SET_MQTT_GRP_TOPIC2 + XdrvMailbox.index - 2; - MakeValidMqtt(0, XdrvMailbox.data); - if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } - SettingsUpdateText(settings_text_index, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data); - - - char stemp[MAX_GROUP_TOPICS][TOPSZ]; - uint32_t read_index = 0; - uint32_t real_index = SET_MQTT_GRP_TOPIC; - for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { - if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } - if (strlen(SettingsText(real_index +i))) { - bool not_equal = true; - for (uint32_t j = 0; j < read_index; j++) { - if (!strcmp(SettingsText(real_index +i), stemp[j])) { - not_equal = false; - } - } - if (not_equal) { - strncpy(stemp[read_index], SettingsText(real_index +i), sizeof(stemp[read_index])); - read_index++; - } - } - } - if (0 == read_index) { - SettingsUpdateText(SET_MQTT_GRP_TOPIC, MQTT_GRPTOPIC); - } else { - uint32_t write_index = 0; - uint32_t real_index = SET_MQTT_GRP_TOPIC; - for (uint32_t i = 0; i < MAX_GROUP_TOPICS; i++) { - if (1 == i) { real_index = SET_MQTT_GRP_TOPIC2 -1; } - if (write_index < read_index) { - SettingsUpdateText(real_index +i, stemp[write_index]); - write_index++; - } else { - SettingsUpdateText(real_index +i, ""); - } - } - } - - restart_flag = 2; - } - ResponseCmndAll(SET_MQTT_GRP_TOPIC, MAX_GROUP_TOPICS); - } -} - -void CmndTopic(void) -{ - if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { - MakeValidMqtt(0, XdrvMailbox.data); - if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } - char stemp1[TOPSZ]; - strlcpy(stemp1, (SC_DEFAULT == Shortcut()) ? MQTT_TOPIC : XdrvMailbox.data, sizeof(stemp1)); - if (strcmp(stemp1, SettingsText(SET_MQTT_TOPIC))) { - Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); - MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); - SettingsUpdateText(SET_MQTT_TOPIC, stemp1); - restart_flag = 2; - } - } - ResponseCmndChar(SettingsText(SET_MQTT_TOPIC)); -} - -void CmndButtonTopic(void) -{ - if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { - MakeValidMqtt(0, XdrvMailbox.data); - if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } - switch (Shortcut()) { - case SC_CLEAR: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, ""); break; - case SC_DEFAULT: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, mqtt_topic); break; - case SC_USER: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, MQTT_BUTTON_TOPIC); break; - default: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, XdrvMailbox.data); - } - } - ResponseCmndChar(SettingsText(SET_MQTT_BUTTON_TOPIC)); -} - -void CmndSwitchTopic(void) -{ - if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { - MakeValidMqtt(0, XdrvMailbox.data); - if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } - switch (Shortcut()) { - case SC_CLEAR: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, ""); break; - case SC_DEFAULT: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, mqtt_topic); break; - case SC_USER: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, MQTT_SWITCH_TOPIC); break; - default: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, XdrvMailbox.data); - } - } - ResponseCmndChar(SettingsText(SET_MQTT_SWITCH_TOPIC)); -} - -void CmndButtonRetain(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { - if (!XdrvMailbox.payload) { - for (uint32_t i = 1; i <= MAX_KEYS; i++) { - SendKey(KEY_BUTTON, i, CLEAR_RETAIN); - } - } - Settings.flag.mqtt_button_retain = XdrvMailbox.payload; - } - ResponseCmndStateText(Settings.flag.mqtt_button_retain); -} - -void CmndSwitchRetain(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { - if (!XdrvMailbox.payload) { - for (uint32_t i = 1; i <= MAX_SWITCHES; i++) { - SendKey(KEY_SWITCH, i, CLEAR_RETAIN); - } - } - Settings.flag.mqtt_switch_retain = XdrvMailbox.payload; - } - ResponseCmndStateText(Settings.flag.mqtt_switch_retain); -} - -void CmndPowerRetain(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { - if (!XdrvMailbox.payload) { - char stemp1[TOPSZ]; - char scommand[CMDSZ]; - for (uint32_t i = 1; i <= devices_present; i++) { - GetTopic_P(stemp1, STAT, mqtt_topic, GetPowerDevice(scommand, i, sizeof(scommand), Settings.flag.device_index_enable)); - mqtt_data[0] = '\0'; - MqttPublish(stemp1, Settings.flag.mqtt_power_retain); - } - } - Settings.flag.mqtt_power_retain = XdrvMailbox.payload; - } - ResponseCmndStateText(Settings.flag.mqtt_power_retain); -} - -void CmndSensorRetain(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { - if (!XdrvMailbox.payload) { - mqtt_data[0] = '\0'; - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_ENERGY), Settings.flag.mqtt_sensor_retain); - } - Settings.flag.mqtt_sensor_retain = XdrvMailbox.payload; - } - ResponseCmndStateText(Settings.flag.mqtt_sensor_retain); -} - - - - -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - - - -const static uint16_t tls_spi_start_sector = 0xFF; -const static uint8_t* tls_spi_start = (uint8_t*) 0x402FF000; -const static size_t tls_spi_len = 0x1000; -const static size_t tls_block_offset = 0x0400; -const static size_t tls_block_len = 0x0400; -const static size_t tls_obj_store_offset = tls_block_offset + sizeof(tls_dir_t); - - -inline void TlsEraseBuffer(uint8_t *buffer) { - memset(buffer + tls_block_offset, 0xFF, tls_block_len); -} - - - -static br_ec_private_key EC = { - 23, - nullptr, 0 -}; - -static br_x509_certificate CHAIN[] = { - { nullptr, 0 } -}; - - - -void loadTlsDir(void) { - memcpy_P(&tls_dir, tls_spi_start + tls_block_offset, sizeof(tls_dir)); - - - if ((TLS_NAME_SKEY == tls_dir.entry[0].name) && (tls_dir.entry[0].len > 0)) { - EC.x = (unsigned char *)(tls_spi_start + tls_obj_store_offset + tls_dir.entry[0].start); - EC.xlen = tls_dir.entry[0].len; - AWS_IoT_Private_Key = &EC; - } else { - AWS_IoT_Private_Key = nullptr; - } - if ((TLS_NAME_CRT == tls_dir.entry[1].name) && (tls_dir.entry[1].len > 0)) { - CHAIN[0].data = (unsigned char *) (tls_spi_start + tls_obj_store_offset + tls_dir.entry[1].start); - CHAIN[0].data_len = tls_dir.entry[1].len; - AWS_IoT_Client_Certificate = CHAIN; - } else { - AWS_IoT_Client_Certificate = nullptr; - } - -} - -const char ALLOCATE_ERROR[] PROGMEM = "TLSKey " D_JSON_ERROR ": cannot allocate buffer."; - -void CmndTlsKey(void) { -#ifdef DEBUG_DUMP_TLS - if (0 == XdrvMailbox.index){ - CmndTlsDump(); - } -#endif - if ((XdrvMailbox.index >= 1) && (XdrvMailbox.index <= 2)) { - tls_dir_t *tls_dir_write; - - if (XdrvMailbox.data_len > 0) { - - uint8_t *spi_buffer = (uint8_t*) malloc(tls_spi_len); - if (!spi_buffer) { - AddLog_P(LOG_LEVEL_ERROR, ALLOCATE_ERROR); - return; - } - memcpy_P(spi_buffer, tls_spi_start, tls_spi_len); - - - RemoveAllSpaces(XdrvMailbox.data); - - - uint32_t bin_len = decode_base64_length((unsigned char*)XdrvMailbox.data); - uint8_t *bin_buf = nullptr; - if (bin_len > 0) { - bin_buf = (uint8_t*) malloc(bin_len + 4); - if (!bin_buf) { - AddLog_P(LOG_LEVEL_ERROR, ALLOCATE_ERROR); - free(spi_buffer); - return; - } - } - - - if (bin_len > 0) { - decode_base64((unsigned char*)XdrvMailbox.data, bin_buf); - } - - - tls_dir_write = (tls_dir_t*) (spi_buffer + tls_block_offset); - - if (1 == XdrvMailbox.index) { - - - TlsEraseBuffer(spi_buffer); - if (bin_len > 0) { - if (bin_len != 32) { - - AddLog_P2(LOG_LEVEL_INFO, PSTR("TLSKey: Certificate must be 32 bytes: %d."), bin_len); - free(spi_buffer); - free(bin_buf); - return; - } - tls_entry_t *entry = &tls_dir_write->entry[0]; - entry->name = TLS_NAME_SKEY; - entry->start = 0; - entry->len = bin_len; - memcpy(spi_buffer + tls_obj_store_offset + entry->start, bin_buf, entry->len); - } else { - - } - } else if (2 == XdrvMailbox.index) { - - if (TLS_NAME_SKEY != tls_dir.entry[0].name) { - - AddLog_P(LOG_LEVEL_INFO, PSTR("TLSKey: cannot store Cert if no Key previously stored.")); - free(spi_buffer); - free(bin_buf); - return; - } - if (bin_len <= 256) { - - AddLog_P2(LOG_LEVEL_INFO, PSTR("TLSKey: Certificate length too short: %d."), bin_len); - free(spi_buffer); - free(bin_buf); - return; - } - tls_entry_t *entry = &tls_dir_write->entry[1]; - entry->name = TLS_NAME_CRT; - entry->start = (tls_dir_write->entry[0].start + tls_dir_write->entry[0].len + 3) & ~0x03; - entry->len = bin_len; - memcpy(spi_buffer + tls_obj_store_offset + entry->start, bin_buf, entry->len); - } - - if (ESP.flashEraseSector(tls_spi_start_sector)) { - ESP.flashWrite(tls_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); - } - free(spi_buffer); - free(bin_buf); - } - - loadTlsDir(); - Response_P(PSTR("{\"%s1\":%d,\"%s2\":%d}"), - XdrvMailbox.command, AWS_IoT_Private_Key ? tls_dir.entry[0].len : -1, - XdrvMailbox.command, AWS_IoT_Client_Certificate ? tls_dir.entry[1].len : -1); - } -} - -#ifdef DEBUG_DUMP_TLS - -uint32_t bswap32(uint32_t x) { - return ((x << 24) & 0xff000000 ) | - ((x << 8) & 0x00ff0000 ) | - ((x >> 8) & 0x0000ff00 ) | - ((x >> 24) & 0x000000ff ); -} -void CmndTlsDump(void) { - uint32_t start = (uint32_t)tls_spi_start + tls_block_offset; - uint32_t end = start + tls_block_len -1; - for (uint32_t pos = start; pos < end; pos += 0x10) { - uint32_t* values = (uint32_t*)(pos); -#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 - Serial.printf("%08x: %08x %08x %08x %08x\n", pos, bswap32(values[0]), bswap32(values[1]), bswap32(values[2]), bswap32(values[3])); -#else - Serial.printf_P(PSTR("%08x: %08x %08x %08x %08x\n"), pos, bswap32(values[0]), bswap32(values[1]), bswap32(values[2]), bswap32(values[3])); -#endif - } -} -#endif -#endif - - - - - -#ifdef USE_WEBSERVER - -#define WEB_HANDLE_MQTT "mq" - -const char S_CONFIGURE_MQTT[] PROGMEM = D_CONFIGURE_MQTT; - -const char HTTP_BTN_MENU_MQTT[] PROGMEM = - "

"; - -const char HTTP_FORM_MQTT1[] PROGMEM = - "
 " D_MQTT_PARAMETERS " " - "
" - "

" D_HOST " (" MQTT_HOST ")

" - "

" D_PORT " (" STR(MQTT_PORT) ")

" - "

" D_CLIENT " (%s)

"; -const char HTTP_FORM_MQTT2[] PROGMEM = - "

" D_USER " (" MQTT_USER ")

" - "


" - "

" D_TOPIC " = %%topic%% (%s)

" - "

" D_FULL_TOPIC " (%s)

"; - -void HandleMqttConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MQTT); - - if (Webserver->hasArg("save")) { - MqttSaveSettings(); - WebRestart(1); - return; - } - - char str[TOPSZ]; - - WSContentStart_P(S_CONFIGURE_MQTT); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_MQTT1, - SettingsText(SET_MQTT_HOST), - Settings.mqtt_port, - Format(str, MQTT_CLIENT_ID, sizeof(str)), MQTT_CLIENT_ID, SettingsText(SET_MQTT_CLIENT)); - WSContentSend_P(HTTP_FORM_MQTT2, - (!strlen(SettingsText(SET_MQTT_USER))) ? "0" : SettingsText(SET_MQTT_USER), - Format(str, MQTT_TOPIC, sizeof(str)), MQTT_TOPIC, SettingsText(SET_MQTT_TOPIC), - MQTT_FULLTOPIC, MQTT_FULLTOPIC, SettingsText(SET_MQTT_FULLTOPIC)); - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); -} - -void MqttSaveSettings(void) -{ - char tmp[TOPSZ]; - char stemp[TOPSZ]; - char stemp2[TOPSZ]; - - WebGetArg("mt", tmp, sizeof(tmp)); - strlcpy(stemp, (!strlen(tmp)) ? MQTT_TOPIC : tmp, sizeof(stemp)); - MakeValidMqtt(0, stemp); - WebGetArg("mf", tmp, sizeof(tmp)); - strlcpy(stemp2, (!strlen(tmp)) ? MQTT_FULLTOPIC : tmp, sizeof(stemp2)); - MakeValidMqtt(1, stemp2); - if ((strcmp(stemp, SettingsText(SET_MQTT_TOPIC))) || (strcmp(stemp2, SettingsText(SET_MQTT_FULLTOPIC)))) { - Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); - MqttPublishPrefixTopic_P(TELE, S_LWT, true); - } - SettingsUpdateText(SET_MQTT_TOPIC, stemp); - SettingsUpdateText(SET_MQTT_FULLTOPIC, stemp2); - WebGetArg("mh", tmp, sizeof(tmp)); - SettingsUpdateText(SET_MQTT_HOST, (!strlen(tmp)) ? MQTT_HOST : (!strcmp(tmp,"0")) ? "" : tmp); - WebGetArg("ml", tmp, sizeof(tmp)); - Settings.mqtt_port = (!strlen(tmp)) ? MQTT_PORT : atoi(tmp); - WebGetArg("mc", tmp, sizeof(tmp)); - SettingsUpdateText(SET_MQTT_CLIENT, (!strlen(tmp)) ? MQTT_CLIENT_ID : tmp); -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"), - SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), SettingsText(SET_MQTT_TOPIC), SettingsText(SET_MQTT_FULLTOPIC)); -#else - WebGetArg("mu", tmp, sizeof(tmp)); - SettingsUpdateText(SET_MQTT_USER, (!strlen(tmp)) ? MQTT_USER : (!strcmp(tmp,"0")) ? "" : tmp); - WebGetArg("mp", tmp, sizeof(tmp)); - SettingsUpdateText(SET_MQTT_PWD, (!strlen(tmp)) ? "" : (!strcmp(tmp, D_ASTERISK_PWD)) ? SettingsText(SET_MQTT_PWD) : tmp); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_MQTTUSER " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"), - SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), SettingsText(SET_MQTT_USER), SettingsText(SET_MQTT_TOPIC), SettingsText(SET_MQTT_FULLTOPIC)); -#endif -} -#endif - - - - - -bool Xdrv02(uint8_t function) -{ - bool result = false; - - if (Settings.flag.mqtt_enabled) { - switch (function) { - case FUNC_PRE_INIT: - MqttInit(); - break; - case FUNC_EVERY_50_MSECOND: - MqttClient.loop(); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_ADD_BUTTON: - WSContentSend_P(HTTP_BTN_MENU_MQTT); - break; - case FUNC_WEB_ADD_HANDLER: - Webserver->on("/" WEB_HANDLE_MQTT, HandleMqttConfiguration); - break; -#endif - case FUNC_COMMAND: - result = DecodeCommand(kMqttCommands, MqttCommand); - break; - } - } - return result; -} -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_03_energy.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_03_energy.ino" -#ifdef USE_ENERGY_SENSOR - - - - -#define XDRV_03 3 -#define XSNS_03 3 - - - - -#define ENERGY_NONE 0 -#define ENERGY_WATCHDOG 4 - -#include - -#define D_CMND_POWERCAL "PowerCal" -#define D_CMND_VOLTAGECAL "VoltageCal" -#define D_CMND_CURRENTCAL "CurrentCal" -#define D_CMND_TARIFF "Tariff" -#define D_CMND_MODULEADDRESS "ModuleAddress" - -enum EnergyCommands { - CMND_POWERCAL, CMND_VOLTAGECAL, CMND_CURRENTCAL, - CMND_POWERSET, CMND_VOLTAGESET, CMND_CURRENTSET, CMND_FREQUENCYSET, CMND_MODULEADDRESS }; - -const char kEnergyCommands[] PROGMEM = "|" - D_CMND_POWERCAL "|" D_CMND_VOLTAGECAL "|" D_CMND_CURRENTCAL "|" - D_CMND_POWERSET "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTSET "|" D_CMND_FREQUENCYSET "|" D_CMND_MODULEADDRESS "|" -#ifdef USE_ENERGY_MARGIN_DETECTION - D_CMND_POWERDELTA "|" D_CMND_POWERLOW "|" D_CMND_POWERHIGH "|" D_CMND_VOLTAGELOW "|" D_CMND_VOLTAGEHIGH "|" D_CMND_CURRENTLOW "|" D_CMND_CURRENTHIGH "|" -#ifdef USE_ENERGY_POWER_LIMIT - D_CMND_MAXENERGY "|" D_CMND_MAXENERGYSTART "|" - D_CMND_MAXPOWER "|" D_CMND_MAXPOWERHOLD "|" D_CMND_MAXPOWERWINDOW "|" - D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW "|" -#endif -#endif - D_CMND_ENERGYRESET "|" D_CMND_TARIFF ; - -void (* const EnergyCommand[])(void) PROGMEM = { - &CmndPowerCal, &CmndVoltageCal, &CmndCurrentCal, - &CmndPowerSet, &CmndVoltageSet, &CmndCurrentSet, &CmndFrequencySet, &CmndModuleAddress, -#ifdef USE_ENERGY_MARGIN_DETECTION - &CmndPowerDelta, &CmndPowerLow, &CmndPowerHigh, &CmndVoltageLow, &CmndVoltageHigh, &CmndCurrentLow, &CmndCurrentHigh, -#ifdef USE_ENERGY_POWER_LIMIT - &CmndMaxEnergy, &CmndMaxEnergyStart, - &CmndMaxPower, &CmndMaxPowerHold, &CmndMaxPowerWindow, - &CmndSafePower, &CmndSafePowerHold, &CmndSafePowerWindow, -#endif -#endif - &CmndEnergyReset, &CmndTariff }; - -const char kEnergyPhases[] PROGMEM = "|%s / %s|%s / %s / %s||[%s,%s]|[%s,%s,%s]"; - -struct ENERGY { - float voltage[3] = { 0, 0, 0 }; - float current[3] = { 0, 0, 0 }; - float active_power[3] = { 0, 0, 0 }; - float apparent_power[3] = { NAN, NAN, NAN }; - float reactive_power[3] = { NAN, NAN, NAN }; - float power_factor[3] = { NAN, NAN, NAN }; - float frequency[3] = { NAN, NAN, NAN }; - - float start_energy = 0; - float daily = 0; - float total = 0; - float export_active = NAN; - - unsigned long kWhtoday_delta = 0; - unsigned long kWhtoday_offset = 0; - unsigned long kWhtoday; - unsigned long period = 0; - - uint8_t fifth_second = 0; - uint8_t command_code = 0; - uint8_t data_valid[3] = { 0, 0, 0 }; - - uint8_t phase_count = 1; - bool voltage_common = false; - - bool voltage_available = true; - bool current_available = true; - - bool type_dc = false; - bool power_on = true; - -#ifdef USE_ENERGY_MARGIN_DETECTION - uint16_t power_history[3] = { 0 }; - uint8_t power_steady_counter = 8; - bool power_delta = false; - bool min_power_flag = false; - bool max_power_flag = false; - bool min_voltage_flag = false; - bool max_voltage_flag = false; - bool min_current_flag = false; - bool max_current_flag = false; - -#ifdef USE_ENERGY_POWER_LIMIT - uint16_t mplh_counter = 0; - uint16_t mplw_counter = 0; - uint8_t mplr_counter = 0; - uint8_t max_energy_state = 0; -#endif -#endif -} Energy; - -Ticker ticker_energy; - - - -bool EnergyTariff1Active() -{ - uint8_t dst = 0; - if (IsDst() && (Settings.tariff[0][1] != Settings.tariff[1][1])) { - dst = 1; - } - if (Settings.tariff[0][dst] != Settings.tariff[1][dst]) { - if (Settings.flag3.energy_weekend && ((RtcTime.day_of_week == 1) || - (RtcTime.day_of_week == 7))) { - return true; - } - uint32_t minutes = MinutesPastMidnight(); - if (Settings.tariff[0][dst] > Settings.tariff[1][dst]) { - - return ((minutes >= Settings.tariff[0][dst]) || (minutes < Settings.tariff[1][dst])); - } else { - - return ((minutes >= Settings.tariff[0][dst]) && (minutes < Settings.tariff[1][dst])); - } - } else { - return false; - } -} - -void EnergyUpdateToday(void) -{ - if (Energy.kWhtoday_delta > 1000) { - unsigned long delta = Energy.kWhtoday_delta / 1000; - Energy.kWhtoday_delta -= (delta * 1000); - Energy.kWhtoday += delta; - } - - 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.valid){ - - uint32_t energy_diff = (uint32_t)(Energy.total * 100000) - RtcSettings.energy_usage.last_usage_kWhtotal; - RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total * 100000); - - uint32_t return_diff = 0; - if (!isnan(Energy.export_active)) { - return_diff = (uint32_t)(Energy.export_active * 100000) - RtcSettings.energy_usage.last_return_kWhtotal; - RtcSettings.energy_usage.last_return_kWhtotal = (uint32_t)(Energy.export_active * 100000); - } - - if (EnergyTariff1Active()) { - RtcSettings.energy_usage.usage1_kWhtotal += energy_diff; - RtcSettings.energy_usage.return1_kWhtotal += return_diff; - } else { - RtcSettings.energy_usage.usage2_kWhtotal += energy_diff; - RtcSettings.energy_usage.return2_kWhtotal += return_diff; - } - } -} - -void EnergyUpdateTotal(float value, bool kwh) -{ - - - - - uint32_t multiplier = (kwh) ? 100000 : 100; - - if (0 == Energy.start_energy || (value < Energy.start_energy)) { - Energy.start_energy = value; - } - else if (value != Energy.start_energy) { - Energy.kWhtoday = (unsigned long)((value - Energy.start_energy) * multiplier); - } - - if ((Energy.total < (value - 0.01)) && - Settings.flag3.hardware_energy_total) { - RtcSettings.energy_kWhtotal = (unsigned long)((value * multiplier) - Energy.kWhtoday_offset - Energy.kWhtoday); - Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; - Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000; - Settings.energy_kWhtotal_time = (!Energy.kWhtoday_offset) ? LocalTime() : Midnight(); - - } - EnergyUpdateToday(); -} - - - -void Energy200ms(void) -{ - Energy.power_on = (power != 0) | Settings.flag.no_power_on_check; - - Energy.fifth_second++; - if (5 == Energy.fifth_second) { - Energy.fifth_second = 0; - - XnrgCall(FUNC_ENERGY_EVERY_SECOND); - - if (RtcTime.valid) { - if (LocalTime() == Midnight()) { - Settings.energy_kWhyesterday = RtcSettings.energy_kWhtoday; - - 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; - - Energy.kWhtoday_delta = 0; - Energy.period = Energy.kWhtoday; - EnergyUpdateToday(); -#if defined(USE_ENERGY_MARGIN_DETECTION) && defined(USE_ENERGY_POWER_LIMIT) - Energy.max_energy_state = 3; -#endif - } -#if defined(USE_ENERGY_MARGIN_DETECTION) && defined(USE_ENERGY_POWER_LIMIT) - if ((RtcTime.hour == Settings.energy_max_energy_start) && (3 == Energy.max_energy_state )) { - Energy.max_energy_state = 0; - } -#endif - - } - } - - XnrgCall(FUNC_EVERY_200_MSECOND); -} - -void EnergySaveState(void) -{ - Settings.energy_kWhdoy = (RtcTime.valid) ? RtcTime.day_of_year : 0; - - Settings.energy_kWhtoday = RtcSettings.energy_kWhtoday; - Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; - - Settings.energy_usage = RtcSettings.energy_usage; -} - -#ifdef USE_ENERGY_MARGIN_DETECTION -bool EnergyMargin(bool type, uint16_t margin, uint16_t value, bool &flag, bool &save_flag) -{ - bool change; - - if (!margin) return false; - change = save_flag; - if (type) { - flag = (value > margin); - } else { - flag = (value < margin); - } - save_flag = flag; - return (change != save_flag); -} - -void EnergyMarginCheck(void) -{ - if (Energy.power_steady_counter) { - Energy.power_steady_counter--; - return; - } - - uint16_t energy_power_u = (uint16_t)(Energy.active_power[0]); - - if (Settings.energy_power_delta) { - uint16_t delta = abs(Energy.power_history[0] - energy_power_u); - if (delta > 0) { - if (Settings.energy_power_delta < 101) { - uint16_t min_power = (Energy.power_history[0] > energy_power_u) ? energy_power_u : Energy.power_history[0]; - if (0 == min_power) { min_power++; } - if (((delta * 100) / min_power) > Settings.energy_power_delta) { - Energy.power_delta = true; - } - } else { - if (delta > (Settings.energy_power_delta -100)) { - Energy.power_delta = true; - } - } - if (Energy.power_delta) { - Energy.power_history[1] = Energy.active_power[0]; - Energy.power_history[2] = Energy.active_power[0]; - } - } - } - Energy.power_history[0] = Energy.power_history[1]; - Energy.power_history[1] = Energy.power_history[2]; - Energy.power_history[2] = energy_power_u; - - if (Energy.power_on && (Settings.energy_min_power || Settings.energy_max_power || Settings.energy_min_voltage || Settings.energy_max_voltage || Settings.energy_min_current || Settings.energy_max_current)) { - uint16_t energy_voltage_u = (uint16_t)(Energy.voltage[0]); - uint16_t energy_current_u = (uint16_t)(Energy.current[0] * 1000); - - DEBUG_DRIVER_LOG(PSTR("NRG: W %d, U %d, I %d"), energy_power_u, energy_voltage_u, energy_current_u); - - Response_P(PSTR("{")); - bool flag; - bool jsonflg = false; - if (EnergyMargin(false, Settings.energy_min_power, energy_power_u, flag, Energy.min_power_flag)) { - ResponseAppend_P(PSTR("%s\"" D_CMND_POWERLOW "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); - jsonflg = true; - } - if (EnergyMargin(true, Settings.energy_max_power, energy_power_u, flag, Energy.max_power_flag)) { - ResponseAppend_P(PSTR("%s\"" D_CMND_POWERHIGH "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); - jsonflg = true; - } - if (EnergyMargin(false, Settings.energy_min_voltage, energy_voltage_u, flag, Energy.min_voltage_flag)) { - ResponseAppend_P(PSTR("%s\"" D_CMND_VOLTAGELOW "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); - jsonflg = true; - } - if (EnergyMargin(true, Settings.energy_max_voltage, energy_voltage_u, flag, Energy.max_voltage_flag)) { - ResponseAppend_P(PSTR("%s\"" D_CMND_VOLTAGEHIGH "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); - jsonflg = true; - } - if (EnergyMargin(false, Settings.energy_min_current, energy_current_u, flag, Energy.min_current_flag)) { - ResponseAppend_P(PSTR("%s\"" D_CMND_CURRENTLOW "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); - jsonflg = true; - } - if (EnergyMargin(true, Settings.energy_max_current, energy_current_u, flag, Energy.max_current_flag)) { - ResponseAppend_P(PSTR("%s\"" D_CMND_CURRENTHIGH "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); - jsonflg = true; - } - if (jsonflg) { - ResponseJsonEnd(); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_MARGINS), MQTT_TELE_RETAIN); - EnergyMqttShow(); - } - } - -#ifdef USE_ENERGY_POWER_LIMIT - - if (Settings.energy_max_power_limit) { - if (Energy.active_power[0] > Settings.energy_max_power_limit) { - if (!Energy.mplh_counter) { - Energy.mplh_counter = Settings.energy_max_power_limit_hold; - } else { - Energy.mplh_counter--; - if (!Energy.mplh_counter) { - ResponseTime_P(PSTR(",\"" D_JSON_MAXPOWERREACHED "\":%d}"), energy_power_u); - MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); - EnergyMqttShow(); - SetAllPower(POWER_ALL_OFF, SRC_MAXPOWER); - if (!Energy.mplr_counter) { - Energy.mplr_counter = Settings.param[P_MAX_POWER_RETRY] +1; - } - Energy.mplw_counter = Settings.energy_max_power_limit_window; - } - } - } - else if (power && (energy_power_u <= Settings.energy_max_power_limit)) { - Energy.mplh_counter = 0; - Energy.mplr_counter = 0; - Energy.mplw_counter = 0; - } - if (!power) { - if (Energy.mplw_counter) { - Energy.mplw_counter--; - } else { - if (Energy.mplr_counter) { - Energy.mplr_counter--; - if (Energy.mplr_counter) { - ResponseTime_P(PSTR(",\"" D_JSON_POWERMONITOR "\":\"%s\"}"), GetStateText(1)); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_JSON_POWERMONITOR)); - RestorePower(true, SRC_MAXPOWER); - } else { - ResponseTime_P(PSTR(",\"" D_JSON_MAXPOWERREACHEDRETRY "\":\"%s\"}"), GetStateText(0)); - MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); - EnergyMqttShow(); - SetAllPower(POWER_ALL_OFF, SRC_MAXPOWER); - } - } - } - } - } - - - if (Settings.energy_max_energy) { - uint16_t 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; - ResponseTime_P(PSTR(",\"" D_JSON_ENERGYMONITOR "\":\"%s\"}"), GetStateText(1)); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_JSON_ENERGYMONITOR)); - RestorePower(true, SRC_MAXENERGY); - } - else if ((1 == Energy.max_energy_state ) && (energy_daily_u >= Settings.energy_max_energy)) { - Energy.max_energy_state = 2; - char stemp[FLOATSZ]; - dtostrfd(Energy.daily, 3, stemp); - ResponseTime_P(PSTR(",\"" D_JSON_MAXENERGYREACHED "\":%s}"), stemp); - MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); - EnergyMqttShow(); - SetAllPower(POWER_ALL_OFF, SRC_MAXENERGY); - } - } -#endif - - if (Energy.power_delta) { EnergyMqttShow(); } -} - -void EnergyMqttShow(void) -{ - - int tele_period_save = tele_period; - tele_period = 2; - mqtt_data[0] = '\0'; - ResponseAppendTime(); - EnergyShow(true); - tele_period = tele_period_save; - ResponseJsonEnd(); - MqttPublishTeleSensor(); - Energy.power_delta = false; -} -#endif - -void EnergyEverySecond(void) -{ - - if (global_update) { - if (power && (global_temperature != 9999) && (global_temperature > Settings.param[P_OVER_TEMP])) { - SetAllPower(POWER_ALL_OFF, SRC_OVERTEMP); - } - } - - - uint32_t data_valid = Energy.phase_count; - for (uint32_t i = 0; i < Energy.phase_count; i++) { - if (Energy.data_valid[i] <= ENERGY_WATCHDOG) { - Energy.data_valid[i]++; - if (Energy.data_valid[i] > ENERGY_WATCHDOG) { - - Energy.voltage[i] = 0; - Energy.current[i] = 0; - Energy.active_power[i] = 0; - if (!isnan(Energy.apparent_power[i])) { Energy.apparent_power[i] = 0; } - if (!isnan(Energy.reactive_power[i])) { Energy.reactive_power[i] = 0; } - if (!isnan(Energy.frequency[i])) { Energy.frequency[i] = 0; } - if (!isnan(Energy.power_factor[i])) { Energy.power_factor[i] = 0; } - - data_valid--; - } - } - } - if (!data_valid) { - if (!isnan(Energy.export_active)) { Energy.export_active = 0; } - Energy.start_energy = 0; - - XnrgCall(FUNC_ENERGY_RESET); - } - -#ifdef USE_ENERGY_MARGIN_DETECTION - EnergyMarginCheck(); -#endif -} - - - - - -void EnergyCommandCalResponse(uint32_t nvalue) -{ - snprintf_P(XdrvMailbox.command, CMDSZ, PSTR("%sCal"), XdrvMailbox.command); - ResponseCmndNumber(nvalue); -} - -void CmndEnergyReset(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 3)) { - char *p; - unsigned long lnum = strtoul(XdrvMailbox.data, &p, 10); - if (p != XdrvMailbox.data) { - switch (XdrvMailbox.index) { - case 1: - - Energy.kWhtoday_offset = lnum *100; - Energy.kWhtoday = 0; - Energy.kWhtoday_delta = 0; - Energy.start_energy = 0; - 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; - case 2: - - Settings.energy_kWhyesterday = lnum *100; - break; - case 3: - - RtcSettings.energy_kWhtotal = lnum *100; - Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; - - Settings.energy_kWhtotal_time = (!Energy.kWhtoday_offset) ? LocalTime() : Midnight(); - RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total * 1000); - break; - } - } - } - else if ((XdrvMailbox.index > 3) && (XdrvMailbox.index <= 5)) { - uint32_t values[2] = { 0 }; - uint32_t position = ParseParameters(2, values); - values[0] *= 100; - values[1] *= 100; - - switch (XdrvMailbox.index) - { - case 4: - - if (position > 0) { - RtcSettings.energy_usage.usage1_kWhtotal = values[0]; - } - if (position > 1) { - RtcSettings.energy_usage.usage2_kWhtotal = values[1]; - } - Settings.energy_usage.usage1_kWhtotal = RtcSettings.energy_usage.usage1_kWhtotal; - Settings.energy_usage.usage2_kWhtotal = RtcSettings.energy_usage.usage2_kWhtotal; - break; - case 5: - - if (position > 0) { - RtcSettings.energy_usage.return1_kWhtotal = values[0]; - } - if (position > 1) { - RtcSettings.energy_usage.return2_kWhtotal = values[1]; - } - Settings.energy_usage.return1_kWhtotal = RtcSettings.energy_usage.return1_kWhtotal; - Settings.energy_usage.return2_kWhtotal = RtcSettings.energy_usage.return2_kWhtotal; - break; - } - } - - Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000; - - char energy_total_chr[FLOATSZ]; - dtostrfd(Energy.total, Settings.flag2.energy_resolution, energy_total_chr); - char energy_daily_chr[FLOATSZ]; - dtostrfd(Energy.daily, Settings.flag2.energy_resolution, energy_daily_chr); - char energy_yesterday_chr[FLOATSZ]; - dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr); - - char energy_usage1_chr[FLOATSZ]; - dtostrfd((float)Settings.energy_usage.usage1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_usage1_chr); - char energy_usage2_chr[FLOATSZ]; - dtostrfd((float)Settings.energy_usage.usage2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_usage2_chr); - char energy_return1_chr[FLOATSZ]; - dtostrfd((float)Settings.energy_usage.return1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_return1_chr); - char energy_return2_chr[FLOATSZ]; - dtostrfd((float)Settings.energy_usage.return2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_return2_chr); - - Response_P(PSTR("{\"%s\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s,\"" D_JSON_USAGE "\":[%s,%s],\"" D_JSON_EXPORT "\":[%s,%s]}}"), - XdrvMailbox.command, energy_total_chr, energy_yesterday_chr, energy_daily_chr, energy_usage1_chr, energy_usage2_chr, energy_return1_chr, energy_return2_chr); -} - -void CmndTariff(void) -{ - - - - - - - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { - uint32_t tariff = XdrvMailbox.index -1; - uint32_t time_type = 0; - char *p; - char *str = strtok_r(XdrvMailbox.data, ", ", &p); - while ((str != nullptr) && (time_type < 2)) { - char *q; - uint32_t value = strtol(str, &q, 10); - Settings.tariff[tariff][time_type] = value; - if (value < 24) { - Settings.tariff[tariff][time_type] *= 60; - char *minute = strtok_r(nullptr, ":", &q); - if (minute) { - value = strtol(minute, nullptr, 10); - if (value > 59) { - value = 59; - } - Settings.tariff[tariff][time_type] += value; - } - } - if (Settings.tariff[tariff][time_type] > 1439) { - Settings.tariff[tariff][time_type] = 1439; - } - str = strtok_r(nullptr, ", ", &p); - time_type++; - } - } - else if (XdrvMailbox.index == 9) { - Settings.flag3.energy_weekend = XdrvMailbox.payload & 1; - } - Response_P(PSTR("{\"%s\":{\"Off-Peak\":{\"STD\":\"%s\",\"DST\":\"%s\"},\"Standard\":{\"STD\":\"%s\",\"DST\":\"%s\"},\"Weekend\":\"%s\"}}"), - XdrvMailbox.command, - GetMinuteTime(Settings.tariff[0][0]).c_str(),GetMinuteTime(Settings.tariff[0][1]).c_str(), - GetMinuteTime(Settings.tariff[1][0]).c_str(),GetMinuteTime(Settings.tariff[1][1]).c_str(), - GetStateText(Settings.flag3.energy_weekend)); -} - -void CmndPowerCal(void) -{ - Energy.command_code = CMND_POWERCAL; - if (XnrgCall(FUNC_COMMAND)) { - if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { - Settings.energy_power_calibration = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_power_calibration); - } -} - -void CmndVoltageCal(void) -{ - Energy.command_code = CMND_VOLTAGECAL; - if (XnrgCall(FUNC_COMMAND)) { - if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { - Settings.energy_voltage_calibration = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_voltage_calibration); - } -} - -void CmndCurrentCal(void) -{ - Energy.command_code = CMND_CURRENTCAL; - if (XnrgCall(FUNC_COMMAND)) { - if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { - Settings.energy_current_calibration = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_current_calibration); - } -} - -void CmndPowerSet(void) -{ - Energy.command_code = CMND_POWERSET; - if (XnrgCall(FUNC_COMMAND)) { - EnergyCommandCalResponse(Settings.energy_power_calibration); - } -} - -void CmndVoltageSet(void) -{ - Energy.command_code = CMND_VOLTAGESET; - if (XnrgCall(FUNC_COMMAND)) { - EnergyCommandCalResponse(Settings.energy_voltage_calibration); - } -} - -void CmndCurrentSet(void) -{ - Energy.command_code = CMND_CURRENTSET; - if (XnrgCall(FUNC_COMMAND)) { - EnergyCommandCalResponse(Settings.energy_current_calibration); - } -} - -void CmndFrequencySet(void) -{ - Energy.command_code = CMND_FREQUENCYSET; - if (XnrgCall(FUNC_COMMAND)) { - EnergyCommandCalResponse(Settings.energy_frequency_calibration); - } -} - -void CmndModuleAddress(void) -{ - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4) && (1 == Energy.phase_count)) { - Energy.command_code = CMND_MODULEADDRESS; - if (XnrgCall(FUNC_COMMAND)) { - ResponseCmndDone(); - } - } -} - -#ifdef USE_ENERGY_MARGIN_DETECTION -void CmndPowerDelta(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32000)) { - Settings.energy_power_delta = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_power_delta); -} - -void CmndPowerLow(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_min_power = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_min_power); -} - -void CmndPowerHigh(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_power = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_power); -} - -void CmndVoltageLow(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 501)) { - Settings.energy_min_voltage = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_min_voltage); -} - -void CmndVoltageHigh(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 501)) { - Settings.energy_max_voltage = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_voltage); -} - -void CmndCurrentLow(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 16001)) { - Settings.energy_min_current = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_min_current); -} - -void CmndCurrentHigh(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 16001)) { - Settings.energy_max_current = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_current); -} - -#ifdef USE_ENERGY_POWER_LIMIT -void CmndMaxPower(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_power_limit = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_power_limit); -} - -void CmndMaxPowerHold(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_power_limit_hold = (1 == XdrvMailbox.payload) ? MAX_POWER_HOLD : XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_power_limit_hold); -} - -void CmndMaxPowerWindow(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_power_limit_window = (1 == XdrvMailbox.payload) ? MAX_POWER_WINDOW : XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_power_limit_window); -} - -void CmndSafePower(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_power_safe_limit = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_power_safe_limit); -} - -void CmndSafePowerHold(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_power_safe_limit_hold = (1 == XdrvMailbox.payload) ? SAFE_POWER_HOLD : XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_power_safe_limit_hold); -} - -void CmndSafePowerWindow(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 1440)) { - Settings.energy_max_power_safe_limit_window = (1 == XdrvMailbox.payload) ? SAFE_POWER_WINDOW : XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_power_safe_limit_window); -} - -void CmndMaxEnergy(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_energy = XdrvMailbox.payload; - Energy.max_energy_state = 3; - } - ResponseCmndNumber(Settings.energy_max_energy); -} - -void CmndMaxEnergyStart(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 24)) { - Settings.energy_max_energy_start = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_energy_start); -} -#endif -#endif - -void EnergySnsInit(void) -{ - XnrgCall(FUNC_INIT); - - if (energy_flg) { - if (RtcSettingsValid()) { - Energy.kWhtoday_offset = RtcSettings.energy_kWhtoday; - } - else if (RtcTime.day_of_year == Settings.energy_kWhdoy) { - Energy.kWhtoday_offset = Settings.energy_kWhtoday; - } - else { - Energy.kWhtoday_offset = 0; - } - Energy.kWhtoday = 0; - Energy.kWhtoday_delta = 0; - Energy.period = Energy.kWhtoday_offset; - EnergyUpdateToday(); - ticker_energy.attach_ms(200, Energy200ms); - } -} - -#ifdef USE_WEBSERVER -const char HTTP_ENERGY_SNS1[] PROGMEM = - "{s}" D_POWERUSAGE_APPARENT "{m}%s " D_UNIT_VA "{e}" - "{s}" D_POWERUSAGE_REACTIVE "{m}%s " D_UNIT_VAR "{e}" - "{s}" D_POWER_FACTOR "{m}%s{e}"; - -const char HTTP_ENERGY_SNS2[] PROGMEM = - "{s}" D_ENERGY_TODAY "{m}%s " D_UNIT_KILOWATTHOUR "{e}" - "{s}" D_ENERGY_YESTERDAY "{m}%s " D_UNIT_KILOWATTHOUR "{e}" - "{s}" D_ENERGY_TOTAL "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; - -const char HTTP_ENERGY_SNS3[] PROGMEM = - "{s}" D_EXPORT_ACTIVE "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; -#endif - -char* EnergyFormatIndex(char* result, char* input, bool json, uint32_t index, bool single = false) -{ - char layout[16]; - GetTextIndexed(layout, sizeof(layout), (index -1) + (3 * json), kEnergyPhases); - switch (index) { - case 2: - snprintf_P(result, FLOATSZ *3, layout, input, input + FLOATSZ); - break; - case 3: - snprintf_P(result, FLOATSZ *3, layout, input, input + FLOATSZ, input + FLOATSZ + FLOATSZ); - break; - default: - snprintf_P(result, FLOATSZ *3, input); - } - return result; -} - -char* EnergyFormat(char* result, char* input, bool json, bool single = false) -{ - uint8_t index = (single) ? 1 : Energy.phase_count; - return EnergyFormatIndex(result, input, json, index, single); -} - -void EnergyShow(bool json) -{ - for (uint32_t i = 0; i < Energy.phase_count; i++) { - if (Energy.voltage_common) { - Energy.voltage[i] = Energy.voltage[0]; - } - } - - float power_factor_knx = Energy.power_factor[0]; - - char apparent_power_chr[Energy.phase_count][FLOATSZ]; - char reactive_power_chr[Energy.phase_count][FLOATSZ]; - char power_factor_chr[Energy.phase_count][FLOATSZ]; - char frequency_chr[Energy.phase_count][FLOATSZ]; - if (!Energy.type_dc) { - if (Energy.current_available && Energy.voltage_available) { - for (uint32_t i = 0; i < Energy.phase_count; i++) { - float apparent_power = Energy.apparent_power[i]; - if (isnan(apparent_power)) { - apparent_power = Energy.voltage[i] * Energy.current[i]; - } - if (apparent_power < Energy.active_power[i]) { - Energy.active_power[i] = apparent_power; - } - - float power_factor = Energy.power_factor[i]; - if (isnan(power_factor)) { - power_factor = (Energy.active_power[i] && apparent_power) ? Energy.active_power[i] / apparent_power : 0; - if (power_factor > 1) { - power_factor = 1; - } - } - if (0 == i) { power_factor_knx = power_factor; } - - float reactive_power = Energy.reactive_power[i]; - if (isnan(reactive_power)) { - reactive_power = 0; - uint32_t difference = ((uint32_t)(apparent_power * 100) - (uint32_t)(Energy.active_power[i] * 100)) / 10; - if ((Energy.current[i] > 0.005) && ((difference > 15) || (difference > (uint32_t)(apparent_power * 100 / 1000)))) { - - - reactive_power = (float)(RoundSqrtInt((uint32_t)(apparent_power * apparent_power * 100) - (uint32_t)(Energy.active_power[i] * Energy.active_power[i] * 100))) / 10; - } - } - - dtostrfd(apparent_power, Settings.flag2.wattage_resolution, apparent_power_chr[i]); - dtostrfd(reactive_power, Settings.flag2.wattage_resolution, reactive_power_chr[i]); - dtostrfd(power_factor, 2, power_factor_chr[i]); - } - } - for (uint32_t i = 0; i < Energy.phase_count; i++) { - float frequency = Energy.frequency[i]; - if (isnan(Energy.frequency[i])) { - frequency = 0; - } - dtostrfd(frequency, Settings.flag2.frequency_resolution, frequency_chr[i]); - } - } - - char voltage_chr[Energy.phase_count][FLOATSZ]; - char current_chr[Energy.phase_count][FLOATSZ]; - char active_power_chr[Energy.phase_count][FLOATSZ]; - for (uint32_t i = 0; i < Energy.phase_count; i++) { - dtostrfd(Energy.voltage[i], Settings.flag2.voltage_resolution, voltage_chr[i]); - dtostrfd(Energy.current[i], Settings.flag2.current_resolution, current_chr[i]); - dtostrfd(Energy.active_power[i], Settings.flag2.wattage_resolution, active_power_chr[i]); - } - - char energy_daily_chr[FLOATSZ]; - dtostrfd(Energy.daily, Settings.flag2.energy_resolution, energy_daily_chr); - char energy_yesterday_chr[FLOATSZ]; - dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr); - - char energy_total_chr[3][FLOATSZ]; - dtostrfd(Energy.total, Settings.flag2.energy_resolution, energy_total_chr[0]); - char export_active_chr[3][FLOATSZ]; - dtostrfd(Energy.export_active, Settings.flag2.energy_resolution, export_active_chr[0]); - uint8_t energy_total_fields = 1; - - if (Settings.tariff[0][0] != Settings.tariff[1][0]) { - dtostrfd((float)RtcSettings.energy_usage.usage1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_total_chr[1]); - dtostrfd((float)RtcSettings.energy_usage.usage2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_total_chr[2]); - dtostrfd((float)RtcSettings.energy_usage.return1_kWhtotal / 100000, Settings.flag2.energy_resolution, export_active_chr[1]); - dtostrfd((float)RtcSettings.energy_usage.return2_kWhtotal / 100000, Settings.flag2.energy_resolution, export_active_chr[2]); - energy_total_fields = 3; - } - - char value_chr[FLOATSZ *3]; - char value2_chr[FLOATSZ *3]; - char value3_chr[FLOATSZ *3]; - - if (json) { - bool show_energy_period = (0 == tele_period); - - ResponseAppend_P(PSTR(",\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL_START_TIME "\":\"%s\",\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s"), - GetDateAndTime(DT_ENERGY).c_str(), - EnergyFormatIndex(value_chr, energy_total_chr[0], json, energy_total_fields), - energy_yesterday_chr, - energy_daily_chr); - - if (!isnan(Energy.export_active)) { - ResponseAppend_P(PSTR(",\"" D_JSON_EXPORT_ACTIVE "\":%s"), - EnergyFormatIndex(value_chr, export_active_chr[0], json, energy_total_fields)); - } - - if (show_energy_period) { - float energy = 0; - if (Energy.period) { - energy = (float)(RtcSettings.energy_kWhtoday - Energy.period) / 100; - } - Energy.period = RtcSettings.energy_kWhtoday; - char energy_period_chr[FLOATSZ]; - dtostrfd(energy, Settings.flag2.wattage_resolution, energy_period_chr); - ResponseAppend_P(PSTR(",\"" D_JSON_PERIOD "\":%s"), energy_period_chr); - } - ResponseAppend_P(PSTR(",\"" D_JSON_POWERUSAGE "\":%s"), - EnergyFormat(value_chr, active_power_chr[0], json)); - if (!Energy.type_dc) { - if (Energy.current_available && Energy.voltage_available) { - ResponseAppend_P(PSTR(",\"" D_JSON_APPARENT_POWERUSAGE "\":%s,\"" D_JSON_REACTIVE_POWERUSAGE "\":%s,\"" D_JSON_POWERFACTOR "\":%s"), - EnergyFormat(value_chr, apparent_power_chr[0], json), - EnergyFormat(value2_chr, reactive_power_chr[0], json), - EnergyFormat(value3_chr, power_factor_chr[0], json)); - } - if (!isnan(Energy.frequency[0])) { - ResponseAppend_P(PSTR(",\"" D_JSON_FREQUENCY "\":%s"), - EnergyFormat(value_chr, frequency_chr[0], json, Energy.voltage_common)); - } - } - if (Energy.voltage_available) { - ResponseAppend_P(PSTR(",\"" D_JSON_VOLTAGE "\":%s"), - EnergyFormat(value_chr, voltage_chr[0], json, Energy.voltage_common)); - } - if (Energy.current_available) { - ResponseAppend_P(PSTR(",\"" D_JSON_CURRENT "\":%s"), - EnergyFormat(value_chr, current_chr[0], json)); - } - XnrgCall(FUNC_JSON_APPEND); - ResponseJsonEnd(); - -#ifdef USE_DOMOTICZ - if (show_energy_period) { - dtostrfd(Energy.total * 1000, 1, energy_total_chr[0]); - DomoticzSensorPowerEnergy((int)Energy.active_power[0], energy_total_chr[0]); - - dtostrfd((float)RtcSettings.energy_usage.usage1_kWhtotal / 100, 1, energy_total_chr[1]); - dtostrfd((float)RtcSettings.energy_usage.usage2_kWhtotal / 100, 1, energy_total_chr[2]); - dtostrfd((float)RtcSettings.energy_usage.return1_kWhtotal / 100, 1, export_active_chr[1]); - dtostrfd((float)RtcSettings.energy_usage.return2_kWhtotal / 100, 1, export_active_chr[2]); - DomoticzSensorP1SmartMeter(energy_total_chr[1], energy_total_chr[2], export_active_chr[1], export_active_chr[2], (int)Energy.active_power[0]); - - if (Energy.voltage_available) { - DomoticzSensor(DZ_VOLTAGE, voltage_chr[0]); - } - if (Energy.current_available) { - DomoticzSensor(DZ_CURRENT, current_chr[0]); - } - } -#endif -#ifdef USE_KNX - if (show_energy_period) { - if (Energy.voltage_available) { - KnxSensor(KNX_ENERGY_VOLTAGE, Energy.voltage[0]); - } - if (Energy.current_available) { - KnxSensor(KNX_ENERGY_CURRENT, Energy.current[0]); - } - KnxSensor(KNX_ENERGY_POWER, Energy.active_power[0]); - if (!Energy.type_dc) { - KnxSensor(KNX_ENERGY_POWERFACTOR, power_factor_knx); - } - KnxSensor(KNX_ENERGY_DAILY, Energy.daily); - KnxSensor(KNX_ENERGY_TOTAL, Energy.total); - KnxSensor(KNX_ENERGY_START, Energy.start_energy); - } -#endif -#ifdef USE_WEBSERVER - } else { - if (Energy.voltage_available) { - WSContentSend_PD(HTTP_SNS_VOLTAGE, EnergyFormat(value_chr, voltage_chr[0], json, Energy.voltage_common)); - } - if (Energy.current_available) { - WSContentSend_PD(HTTP_SNS_CURRENT, EnergyFormat(value_chr, current_chr[0], json)); - } - WSContentSend_PD(HTTP_SNS_POWER, EnergyFormat(value_chr, active_power_chr[0], json)); - if (!Energy.type_dc) { - if (Energy.current_available && Energy.voltage_available) { - WSContentSend_PD(HTTP_ENERGY_SNS1, EnergyFormat(value_chr, apparent_power_chr[0], json), - EnergyFormat(value2_chr, reactive_power_chr[0], json), - EnergyFormat(value3_chr, power_factor_chr[0], json)); - } - if (!isnan(Energy.frequency[0])) { - WSContentSend_PD(PSTR("{s}" D_FREQUENCY "{m}%s " D_UNIT_HERTZ "{e}"), - EnergyFormat(value_chr, frequency_chr[0], json, Energy.voltage_common)); - } - } - WSContentSend_PD(HTTP_ENERGY_SNS2, energy_daily_chr, energy_yesterday_chr, energy_total_chr[0]); - if (!isnan(Energy.export_active)) { - WSContentSend_PD(HTTP_ENERGY_SNS3, export_active_chr[0]); - } - - XnrgCall(FUNC_WEB_SENSOR); -#endif - } -} - - - - - -bool Xdrv03(uint8_t function) -{ - bool result = false; - - if (FUNC_PRE_INIT == function) { - energy_flg = ENERGY_NONE; - XnrgCall(FUNC_PRE_INIT); - } - else if (energy_flg) { - switch (function) { - case FUNC_LOOP: - XnrgCall(FUNC_LOOP); - break; - case FUNC_EVERY_250_MSECOND: - XnrgCall(FUNC_EVERY_250_MSECOND); - break; - case FUNC_SERIAL: - result = XnrgCall(FUNC_SERIAL); - break; -#ifdef USE_ENERGY_MARGIN_DETECTION - case FUNC_SET_POWER: - Energy.power_steady_counter = 2; - break; -#endif - case FUNC_COMMAND: - result = DecodeCommand(kEnergyCommands, EnergyCommand); - break; - } - } - return result; -} - -bool Xsns03(uint8_t function) -{ - bool result = false; - - if (energy_flg) { - switch (function) { - case FUNC_EVERY_SECOND: - EnergyEverySecond(); - break; - case FUNC_JSON_APPEND: - EnergyShow(true); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - EnergyShow(false); - break; -#endif - case FUNC_SAVE_BEFORE_RESTART: - EnergySaveState(); - break; - case FUNC_INIT: - EnergySnsInit(); - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_04_light.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_04_light.ino" -#ifdef USE_LIGHT -# 124 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_04_light.ino" -#define XDRV_04 4 - - -enum LightSchemes { LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MAX }; - -const uint8_t LIGHT_COLOR_SIZE = 25; - -const char kLightCommands[] PROGMEM = "|" - D_CMND_COLOR "|" D_CMND_COLORTEMPERATURE "|" D_CMND_DIMMER "|" D_CMND_DIMMER_RANGE "|" D_CMND_LEDTABLE "|" D_CMND_FADE "|" - D_CMND_RGBWWTABLE "|" D_CMND_SCHEME "|" D_CMND_SPEED "|" D_CMND_WAKEUP "|" D_CMND_WAKEUPDURATION "|" - D_CMND_WHITE "|" D_CMND_CHANNEL "|" D_CMND_HSBCOLOR -#ifdef USE_LIGHT_PALETTE - "|" D_CMND_PALETTE -#endif - "|UNDOCA" ; - -void (* const LightCommand[])(void) PROGMEM = { - &CmndColor, &CmndColorTemperature, &CmndDimmer, &CmndDimmerRange, &CmndLedTable, &CmndFade, - &CmndRgbwwTable, &CmndScheme, &CmndSpeed, &CmndWakeup, &CmndWakeupDuration, - &CmndWhite, &CmndChannel, &CmndHsbColor, -#ifdef USE_LIGHT_PALETTE - &CmndPalette, -#endif - &CmndUndocA }; - - -enum LightColorModes { - LCM_RGB = 1, LCM_CT = 2, LCM_BOTH = 3 }; - -struct LRgbColor { - uint8_t R, G, B; -}; -const uint8_t MAX_FIXED_COLOR = 12; -const LRgbColor kFixedColor[MAX_FIXED_COLOR] PROGMEM = - { 255,0,0, 0,255,0, 0,0,255, 228,32,0, 0,228,32, 0,32,228, 188,64,0, 0,160,96, 160,32,240, 255,255,0, 255,0,170, 255,255,255 }; - -struct LWColor { - uint8_t W; -}; -const uint8_t MAX_FIXED_WHITE = 4; -const LWColor kFixedWhite[MAX_FIXED_WHITE] PROGMEM = { 0, 255, 128, 32 }; - -struct LCwColor { - uint8_t C, W; -}; -const uint8_t MAX_FIXED_COLD_WARM = 4; -const LCwColor kFixedColdWarm[MAX_FIXED_COLD_WARM] PROGMEM = { 0,0, 255,0, 0,255, 128,128 }; - - -const uint16_t CT_MIN = 153; -const uint16_t CT_MAX = 500; - -const uint16_t CT_MIN_ALEXA = 200; -const uint16_t CT_MAX_ALEXA = 380; - - - - - - -typedef struct gamma_table_t { - uint16_t to_src; - uint16_t to_gamma; -} gamma_table_t; - -const gamma_table_t gamma_table[] = { - { 1, 1 }, - { 4, 1 }, - { 209, 13 }, - { 312, 41 }, - { 457, 106 }, - { 626, 261 }, - { 762, 450 }, - { 895, 703 }, - { 1023, 1023 }, - { 0xFFFF, 0xFFFF } -}; - - -const gamma_table_t gamma_table_fast[] = { - { 384, 192 }, - { 768, 576 }, - { 1023, 1023 }, - { 0xFFFF, 0xFFFF } -}; -# 256 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_04_light.ino" -struct LIGHT { - uint32_t strip_timer_counter = 0; - power_t power = 0; - - uint16_t wakeup_counter = 0; - - uint8_t entry_color[LST_MAX]; - uint8_t current_color[LST_MAX]; - uint8_t new_color[LST_MAX]; - uint8_t last_color[LST_MAX]; - uint8_t color_remap[LST_MAX]; - - uint8_t wheel = 0; - uint8_t random = 0; - uint8_t subtype = 0; - uint8_t device = 0; - uint8_t old_power = 1; - uint8_t wakeup_active = 0; - uint8_t wakeup_dimmer = 0; - uint8_t fixed_color_index = 1; - uint8_t pwm_offset = 0; - uint8_t max_scheme = LS_MAX -1; - - bool update = true; - bool pwm_multi_channels = false; - - bool fade_initialized = false; - bool fade_running = false; -#ifdef USE_DEVICE_GROUPS - bool devgrp_no_channels_out = false; -#endif -#ifdef USE_LIGHT_PALETTE - uint8_t palette_count = 0; - uint8_t * palette; -#endif - uint16_t fade_start_10[LST_MAX] = {0,0,0,0,0}; - uint16_t fade_cur_10[LST_MAX]; - uint16_t fade_end_10[LST_MAX]; - uint16_t fade_duration = 0; - uint32_t fade_start = 0; - - uint16_t pwm_min = 0; - uint16_t pwm_max = 1023; -} Light; - -power_t LightPower(void) -{ - return Light.power; -} - - -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 -power_t LightPowerIRAM(void) ICACHE_RAM_ATTR; -#endif - -power_t LightPowerIRAM(void) -{ - return Light.power; -} - -uint8_t LightDevice(void) -{ - return Light.device; -} - -static uint32_t min3(uint32_t a, uint32_t b, uint32_t c) { - return (a < b && a < c) ? a : (b < c) ? b : c; -} -# 362 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_04_light.ino" -class LightStateClass { - private: - uint16_t _hue = 0; - uint8_t _sat = 255; - uint8_t _briRGB = 255; - - uint8_t _r = 255; - uint8_t _g = 255; - uint8_t _b = 255; - - uint8_t _subtype = 0; - uint16_t _ct = CT_MIN; - uint8_t _wc = 255; - uint8_t _ww = 0; - uint8_t _briCT = 255; - - uint8_t _color_mode = LCM_RGB; - - - - - - uint16_t _ct_min_range = CT_MIN; - uint16_t _ct_max_range = CT_MAX; - - public: - LightStateClass() { - - } - - void setSubType(uint8_t sub_type) { - _subtype = sub_type; - } -# 404 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_04_light.ino" - uint8_t setColorMode(uint8_t cm) { - uint8_t prev_cm = _color_mode; - if (cm < LCM_RGB) { cm = LCM_RGB; } - if (cm > LCM_BOTH) { cm = LCM_BOTH; } - uint8_t maxbri = (_briRGB >= _briCT) ? _briRGB : _briCT; - - switch (_subtype) { - case LST_COLDWARM: - _color_mode = LCM_CT; - break; - - case LST_NONE: - case LST_SINGLE: - case LST_RGB: - default: - _color_mode = LCM_RGB; - break; - - case LST_RGBW: - case LST_RGBCW: - _color_mode = cm; - break; - } - if (LCM_RGB == _color_mode) { - _briCT = 0; - if (0 == _briRGB) { _briRGB = maxbri; } - } - if (LCM_CT == _color_mode) { - _briRGB = 0; - if (0 == _briCT) { _briCT = maxbri; } - } -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setColorMode prev_cm (%d) req_cm (%d) new_cm (%d)", prev_cm, cm, _color_mode); -#endif - return prev_cm; - } - - inline uint8_t getColorMode() { - return _color_mode; - } - - void addRGBMode() { - setColorMode(_color_mode | LCM_RGB); - } - void addCTMode() { - setColorMode(_color_mode | LCM_CT); - } - - - void getRGB(uint8_t *r, uint8_t *g, uint8_t *b) { - if (r) { *r = _r; } - if (g) { *g = _g; } - if (b) { *b = _b; } - } - - - - void getCW(uint8_t *rc, uint8_t *rw) { - if (rc) { *rc = _wc; } - if (rw) { *rw = _ww; } - } - - - void getActualRGBCW(uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *c, uint8_t *w) { - bool rgb_channels_on = _color_mode & LCM_RGB; - bool ct_channels_on = _color_mode & LCM_CT; - - if (r) { *r = rgb_channels_on ? changeUIntScale(_r, 0, 255, 0, _briRGB) : 0; } - if (g) { *g = rgb_channels_on ? changeUIntScale(_g, 0, 255, 0, _briRGB) : 0; } - if (b) { *b = rgb_channels_on ? changeUIntScale(_b, 0, 255, 0, _briRGB) : 0; } - - if (c) { *c = ct_channels_on ? changeUIntScale(_wc, 0, 255, 0, _briCT) : 0; } - if (w) { *w = ct_channels_on ? changeUIntScale(_ww, 0, 255, 0, _briCT) : 0; } - } - - uint8_t getChannels(uint8_t *channels) { - getActualRGBCW(&channels[0], &channels[1], &channels[2], &channels[3], &channels[4]); - } - - void getChannelsRaw(uint8_t *channels) { - channels[0] = _r; - channels[1] = _g; - channels[2] = _b; - channels[3] = _wc; - channels[4] = _ww; - } - - void getHSB(uint16_t *hue, uint8_t *sat, uint8_t *bri) { - if (hue) { *hue = _hue; } - if (sat) { *sat = _sat; } - if (bri) { *bri = _briRGB; } - } - - - uint8_t getBri(void) { - - return (_briRGB >= _briCT) ? _briRGB : _briCT; - } - - - inline uint8_t getBriCT() { - return _briCT; - } - - static inline uint8_t DimmerToBri(uint8_t dimmer) { - return changeUIntScale(dimmer, 0, 100, 0, 255); - } - static uint8_t BriToDimmer(uint8_t bri) { - uint8_t dimmer = changeUIntScale(bri, 0, 255, 0, 100); - - if ((dimmer == 0) && (bri > 0)) { dimmer = 1; } - return dimmer; - } - - uint8_t getDimmer(uint32_t mode = 0) { - uint8_t bri; - switch (mode) { - case 1: - bri = getBriRGB(); - break; - case 2: - bri = getBriCT(); - break; - default: - bri = getBri(); - break; - } - return BriToDimmer(bri); - } - - inline uint16_t getCT() const { - return _ct; - } - - - uint16_t getCT10bits() const { - return changeUIntScale(_ct, _ct_min_range, _ct_max_range, 0, 1023); - } - - inline void setCTRange(uint16_t ct_min_range, uint16_t ct_max_range) { - _ct_min_range = ct_min_range; - _ct_max_range = ct_max_range; - } - - inline void getCTRange(uint16_t *ct_min_range, uint16_t *ct_max_range) const { - if (ct_min_range) { *ct_min_range = _ct_min_range; } - if (ct_max_range) { *ct_max_range = _ct_max_range; } - } - - - void getXY(float *x, float *y) { - RgbToXy(_r, _g, _b, x, y); - } - - - - void setBri(uint8_t bri) { - setBriRGB(_color_mode & LCM_RGB ? bri : 0); - setBriCT(_color_mode & LCM_CT ? bri : 0); -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setBri RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB); -#endif -#ifdef USE_PWM_DIMMER - if (PWM_DIMMER == my_module_type) PWMDimmerSetBrightnessLeds(0); -#endif - } - - - uint8_t setBriRGB(uint8_t bri_rgb) { - uint8_t prev_bri = _briRGB; - _briRGB = bri_rgb; - if (bri_rgb > 0) { addRGBMode(); } - return prev_bri; - } - - - uint8_t setBriCT(uint8_t bri_ct) { - uint8_t prev_bri = _briCT; - _briCT = bri_ct; - if (bri_ct > 0) { addCTMode(); } - return prev_bri; - } - - inline uint8_t getBriRGB() { - return _briRGB; - } - - void setDimmer(uint8_t dimmer) { - setBri(DimmerToBri(dimmer)); - } - - void setCT(uint16_t ct) { - if (0 == ct) { - - setColorMode(LCM_RGB); - } else { - ct = (ct < CT_MIN ? CT_MIN : (ct > CT_MAX ? CT_MAX : ct)); - _ww = changeUIntScale(ct, _ct_min_range, _ct_max_range, 0, 255); - _wc = 255 - _ww; - _ct = ct; - addCTMode(); - } -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setCT RGB raw (%d %d %d) HS (%d %d) briRGB (%d) briCT (%d) CT (%d)", _r, _g, _b, _hue, _sat, _briRGB, _briCT, _ct); -#endif - } -# 625 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_04_light.ino" - void setCW(uint8_t c, uint8_t w, bool free_range = false) { - uint16_t max = (w > c) ? w : c; - uint16_t sum = c + w; - - if (0 == max) { - _briCT = 0; - setColorMode(LCM_RGB); - } else { - if (!free_range) { - - _ww = changeUIntScale(w, 0, sum, 0, 255); - _wc = 255 - _ww; - } else { - _ww = changeUIntScale(w, 0, max, 0, 255); - _wc = changeUIntScale(c, 0, max, 0, 255); - } - _ct = changeUIntScale(w, 0, sum, _ct_min_range, _ct_max_range); - addCTMode(); - if (_color_mode & LCM_CT) { _briCT = free_range ? max : (sum > 255 ? 255 : sum); } - } -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setCW CW (%d %d) CT (%d) briCT (%d)", c, w, _ct, _briCT); -#endif - } - - - uint8_t setRGB(uint8_t r, uint8_t g, uint8_t b, bool keep_bri = false) { - uint16_t hue; - uint8_t sat; -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setRGB RGB input (%d %d %d)", r, g, b); -#endif - - uint32_t max = (r > g && r > b) ? r : (g > b) ? g : b; - - if (0 == max) { - r = g = b = 255; - setColorMode(LCM_CT); - } else { - if (255 > max) { - - r = changeUIntScale(r, 0, max, 0, 255); - g = changeUIntScale(g, 0, max, 0, 255); - b = changeUIntScale(b, 0, max, 0, 255); - } - addRGBMode(); - } - if (!keep_bri) { - _briRGB = (_color_mode & LCM_RGB) ? max : 0; - } - - RgbToHsb(r, g, b, &hue, &sat, nullptr); - _r = r; - _g = g; - _b = b; - _hue = hue; - _sat = sat; -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setRGB RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB); -#endif - return max; - } - - void setHS(uint16_t hue, uint8_t sat) { - uint8_t r, g, b; - HsToRgb(hue, sat, &r, &g, &b); - _r = r; - _g = g; - _b = b; - _hue = hue; - _sat = sat; - addRGBMode(); -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setHS HS (%d %d) rgb (%d %d %d)", hue, sat, r, g, b); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setHS RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB); -#endif - } - - - - void setChannelsRaw(uint8_t *channels) { - _r = channels[0]; - _g = channels[1]; - _b = channels[2]; - _wc = channels[3]; - _ww = channels[4]; -} - - - - - void setChannels(uint8_t *channels) { - setRGB(channels[0], channels[1], channels[2]); - setCW(channels[3], channels[4], true); -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels (%d %d %d %d %d)", - channels[0], channels[1], channels[2], channels[3], channels[4]); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels CT (%d) briRGB (%d) briCT (%d)", _ct, _briRGB, _briCT); -#endif - } - - - static void RgbToHsb(uint8_t r, uint8_t g, uint8_t b, uint16_t *r_hue, uint8_t *r_sat, uint8_t *r_bri); - static void HsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b); - static void RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x, float *r_y); - static void XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_t *rb); - -}; -# 741 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_04_light.ino" -void LightStateClass::RgbToHsb(uint8_t ir, uint8_t ig, uint8_t ib, uint16_t *r_hue, uint8_t *r_sat, uint8_t *r_bri) { - uint32_t r = ir; - uint32_t g = ig; - uint32_t b = ib; - uint32_t max = (r > g && r > b) ? r : (g > b) ? g : b; - uint32_t min = (r < g && r < b) ? r : (g < b) ? g : b; - uint32_t d = max - min; - - uint16_t hue = 0; - uint8_t sat = 0; - uint8_t bri = max; - - if (d != 0) { - sat = changeUIntScale(d, 0, max, 0, 255); - if (r == max) { - hue = (g > b) ? changeUIntScale(g-b,0,d,0,60) : 360 - changeUIntScale(b-g,0,d,0,60); - } else if (g == max) { - hue = (b > r) ? 120 + changeUIntScale(b-r,0,d,0,60) : 120 - changeUIntScale(r-b,0,d,0,60); - } else { - hue = (r > g) ? 240 + changeUIntScale(r-g,0,d,0,60) : 240 - changeUIntScale(g-r,0,d,0,60); - } - hue = hue % 360; - } - - if (r_hue) *r_hue = hue; - if (r_sat) *r_sat = sat; - if (r_bri) *r_bri = bri; - -} - -void LightStateClass::HsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b) { - uint32_t r = 255; - uint32_t g = 255; - uint32_t b = 255; - - hue = hue % 360; - - if (sat > 0) { - uint32_t i = hue / 60; - uint32_t f = hue % 60; - uint32_t q = 255 - changeUIntScale(f, 0, 60, 0, sat); - uint32_t p = 255 - sat; - uint32_t t = 255 - changeUIntScale(60 - f, 0, 60, 0, sat); - - switch (i) { - case 0: - - g = t; - b = p; - break; - case 1: - r = q; - - b = p; - break; - case 2: - r = p; - - b = t; - break; - case 3: - r = p; - g = q; - - break; - case 4: - r = t; - g = p; - - break; - default: - - g = p; - b = q; - break; - } - } - if (r_r) *r_r = r; - if (r_g) *r_g = g; - if (r_b) *r_b = b; -} - -#define POW FastPrecisePowf - -void LightStateClass::RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x, float *r_y) { - float x = 0.31271f; - float y = 0.32902f; - - if (i_r + i_b + i_g > 0) { - float r = (float)i_r / 255.0f; - float g = (float)i_g / 255.0f; - float b = (float)i_b / 255.0f; - - - r = (r > 0.04045f) ? POW((r + 0.055f) / (1.0f + 0.055f), 2.4f) : (r / 12.92f); - g = (g > 0.04045f) ? POW((g + 0.055f) / (1.0f + 0.055f), 2.4f) : (g / 12.92f); - b = (b > 0.04045f) ? POW((b + 0.055f) / (1.0f + 0.055f), 2.4f) : (b / 12.92f); - - - - float X = r * 0.649926f + g * 0.103455f + b * 0.197109f; - float Y = r * 0.234327f + g * 0.743075f + b * 0.022598f; - float Z = r * 0.000000f + g * 0.053077f + b * 1.035763f; - - x = X / (X + Y + Z); - y = Y / (X + Y + Z); - - } - if (r_x) *r_x = x; - if (r_y) *r_y = y; -} - -void LightStateClass::XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_t *rb) -{ - x = (x > 0.99f ? 0.99f : (x < 0.01f ? 0.01f : x)); - y = (y > 0.99f ? 0.99f : (y < 0.01f ? 0.01f : y)); - float z = 1.0f - x - y; - - float X = x / y; - float Z = z / y; - - - - float r = X * 3.2406f - 1.5372f - Z * 0.4986f; - float g = -X * 0.9689f + 1.8758f + Z * 0.0415f; - float b = X * 0.0557f - 0.2040f + Z * 1.0570f; - float max = (r > g && r > b) ? r : (g > b) ? g : b; - r = r / max; - g = g / max; - b = b / max; - r = (r <= 0.0031308f) ? 12.92f * r : 1.055f * POW(r, (1.0f / 2.4f)) - 0.055f; - g = (g <= 0.0031308f) ? 12.92f * g : 1.055f * POW(g, (1.0f / 2.4f)) - 0.055f; - b = (b <= 0.0031308f) ? 12.92f * b : 1.055f * POW(b, (1.0f / 2.4f)) - 0.055f; - - - - - - int32_t ir = r * 255.0f + 0.5f; - int32_t ig = g * 255.0f + 0.5f; - int32_t ib = b * 255.0f + 0.5f; - if (rr) { *rr = (ir > 255 ? 255: (ir < 0 ? 0 : ir)); } - if (rg) { *rg = (ig > 255 ? 255: (ig < 0 ? 0 : ig)); } - if (rb) { *rb = (ib > 255 ? 255: (ib < 0 ? 0 : ib)); } -} - -class LightControllerClass { -private: - LightStateClass *_state; - - - bool _ct_rgb_linked = true; - bool _pwm_multi_channels = false; - -public: - LightControllerClass(LightStateClass& state) { - _state = &state; - } - - void setSubType(uint8_t sub_type) { - _state->setSubType(sub_type); - } - - inline bool setCTRGBLinked(bool ct_rgb_linked) { - bool prev = _ct_rgb_linked; - if (_pwm_multi_channels) { - _ct_rgb_linked = false; - } else { - _ct_rgb_linked = ct_rgb_linked; - } - return prev; - } - - void setAlexaCTRange(bool alexa_ct_range) { - - if (alexa_ct_range) { - _state->setCTRange(CT_MIN_ALEXA, CT_MAX_ALEXA); - } else { - _state->setCTRange(CT_MIN, CT_MAX); - } - } - - inline bool isCTRGBLinked() { - return _ct_rgb_linked; - } - - inline bool setPWMMultiChannel(bool pwm_multi_channels) { - bool prev = _pwm_multi_channels; - _pwm_multi_channels = pwm_multi_channels; - if (pwm_multi_channels) setCTRGBLinked(false); - return prev; - } - - inline bool isPWMMultiChannel(void) { - return _pwm_multi_channels; - } - -#ifdef DEBUG_LIGHT - void debugLogs() { - uint8_t r,g,b,c,w; - _state->getActualRGBCW(&r,&g,&b,&c,&w); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs rgb (%d %d %d) cw (%d %d)", - r, g, b, c, w); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs lightCurrent (%d %d %d %d %d)", - Light.current_color[0], Light.current_color[1], Light.current_color[2], - Light.current_color[3], Light.current_color[4]); - } -#endif - - void loadSettings() { -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::loadSettings Settings.light_color (%d %d %d %d %d - %d)", - Settings.light_color[0], Settings.light_color[1], Settings.light_color[2], - Settings.light_color[3], Settings.light_color[4], Settings.light_dimmer); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::loadSettings light_type/sub (%d %d)", - light_type, Light.subtype); -#endif - if (_pwm_multi_channels) { - _state->setChannelsRaw(Settings.light_color); - } else { - - _state->setCW(Settings.light_color[3], Settings.light_color[4], true); - _state->setRGB(Settings.light_color[0], Settings.light_color[1], Settings.light_color[2]); - - - - uint8_t bri = _state->DimmerToBri(Settings.light_dimmer); - - - - if ((DEFAULT_LIGHT_COMPONENT == Settings.light_color[0]) && - (DEFAULT_LIGHT_COMPONENT == Settings.light_color[1]) && - (DEFAULT_LIGHT_COMPONENT == Settings.light_color[2]) && - (DEFAULT_LIGHT_COMPONENT == Settings.light_color[3]) && - (DEFAULT_LIGHT_COMPONENT == Settings.light_color[4]) && - (DEFAULT_LIGHT_DIMMER == Settings.light_dimmer) ) { - _state->setBriCT(bri); - _state->setBriRGB(bri); - _state->setColorMode(LCM_RGB); - } - - if (Settings.light_color[0] + Settings.light_color[1] + Settings.light_color[2] > 0) { - _state->setBriRGB(bri); - } else { - _state->setBriCT(bri); - } - } - } - - void changeCTB(uint16_t new_ct, uint8_t briCT) { - - - - - - - if ((LST_COLDWARM != Light.subtype) && (LST_RGBW > Light.subtype)) { - return; - } - _state->setCT(new_ct); - _state->setBriCT(briCT); - if (_ct_rgb_linked) { _state->setColorMode(LCM_CT); } - saveSettings(); - calcLevels(); - - } - - void changeDimmer(uint8_t dimmer, uint32_t mode = 0) { - uint8_t bri = changeUIntScale(dimmer, 0, 100, 0, 255); - switch (mode) { - case 1: - changeBriRGB(bri); - if (_ct_rgb_linked) { _state->setColorMode(LCM_RGB); } - break; - case 2: - changeBriCT(bri); - if (_ct_rgb_linked) { _state->setColorMode(LCM_CT); } - break; - default: - changeBri(bri); - break; - } - } - - void changeBri(uint8_t bri) { - _state->setBri(bri); - saveSettings(); - calcLevels(); - } - - void changeBriRGB(uint8_t bri) { - _state->setBriRGB(bri); - saveSettings(); - calcLevels(); - } - - void changeBriCT(uint8_t bri) { - _state->setBriCT(bri); - saveSettings(); - calcLevels(); - } - - void changeRGB(uint8_t r, uint8_t g, uint8_t b, bool keep_bri = false) { - _state->setRGB(r, g, b, keep_bri); - if (_ct_rgb_linked) { _state->setColorMode(LCM_RGB); } - saveSettings(); - calcLevels(); - } - - - - void calcLevels(uint8_t *current_color = nullptr) { - uint8_t r,g,b,c,w,briRGB,briCT; - if (current_color == nullptr) { current_color = Light.current_color; } - - if (_pwm_multi_channels) { - _state->getChannelsRaw(current_color); - return; - } - - _state->getActualRGBCW(&r,&g,&b,&c,&w); - briRGB = _state->getBriRGB(); - briCT = _state->getBriCT(); - - current_color[0] = current_color[1] = current_color[2] = 0; - current_color[3] = current_color[4] = 0; - switch (Light.subtype) { - case LST_NONE: - current_color[0] = 255; - break; - case LST_SINGLE: - current_color[0] = briRGB; - break; - case LST_COLDWARM: - current_color[0] = c; - current_color[1] = w; - break; - case LST_RGBW: - case LST_RGBCW: - if (LST_RGBCW == Light.subtype) { - current_color[3] = c; - current_color[4] = w; - } else { - current_color[3] = briCT; - } - - case LST_RGB: - current_color[0] = r; - current_color[1] = g; - current_color[2] = b; - break; - } - } - - void changeHSB(uint16_t hue, uint8_t sat, uint8_t briRGB) { - _state->setHS(hue, sat); - _state->setBriRGB(briRGB); - if (_ct_rgb_linked) { _state->setColorMode(LCM_RGB); } - saveSettings(); - calcLevels(); - } - - - void saveSettings() { - if (Light.pwm_multi_channels) { - - _state->getChannelsRaw(Settings.light_color); - Settings.light_dimmer = 100; - } else { - uint8_t cm = _state->getColorMode(); - - memset(&Settings.light_color[0], 0, sizeof(Settings.light_color)); - if (LCM_RGB & cm) { - _state->getRGB(&Settings.light_color[0], &Settings.light_color[1], &Settings.light_color[2]); - Settings.light_dimmer = _state->BriToDimmer(_state->getBriRGB()); - - if (LCM_BOTH == cm) { - - _state->getActualRGBCW(nullptr, nullptr, nullptr, &Settings.light_color[3], &Settings.light_color[4]); - } - } else if (LCM_CT == cm) { - _state->getCW(&Settings.light_color[3], &Settings.light_color[4]); - Settings.light_dimmer = _state->BriToDimmer(_state->getBriCT()); - } - } -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::saveSettings Settings.light_color (%d %d %d %d %d - %d)", - Settings.light_color[0], Settings.light_color[1], Settings.light_color[2], - Settings.light_color[3], Settings.light_color[4], Settings.light_dimmer); -#endif - } - - - - - void changeChannels(uint8_t *channels) { - if (Light.pwm_multi_channels) { - _state->setChannelsRaw(channels); - } else if (LST_COLDWARM == Light.subtype) { - - uint8_t remapped_channels[5] = {0,0,0,channels[0],channels[1]}; - _state->setChannels(remapped_channels); - } else { - _state->setChannels(channels); - } - - saveSettings(); - calcLevels(); - } -}; - - - -LightStateClass light_state = LightStateClass(); -LightControllerClass light_controller = LightControllerClass(light_state); - - - - - -uint16_t change8to10(uint8_t v) { - return changeUIntScale(v, 0, 255, 0, 1023); -} - -uint8_t change10to8(uint16_t v) { - return (0 == v) ? 0 : changeUIntScale(v, 4, 1023, 1, 255); -} - - - - - -uint16_t ledGamma_internal(uint16_t v, const struct gamma_table_t *gt_ptr) { - uint16_t from_src = 0; - uint16_t from_gamma = 0; - - for (const gamma_table_t *gt = gt_ptr; ; gt++) { - uint16_t to_src = gt->to_src; - uint16_t to_gamma = gt->to_gamma; - if (v <= to_src) { - return changeUIntScale(v, from_src, to_src, from_gamma, to_gamma); - } - from_src = to_src; - from_gamma = to_gamma; - } -} - -uint16_t ledGammaReverse_internal(uint16_t vg, const struct gamma_table_t *gt_ptr) { - uint16_t from_src = 0; - uint16_t from_gamma = 0; - - for (const gamma_table_t *gt = gt_ptr; ; gt++) { - uint16_t to_src = gt->to_src; - uint16_t to_gamma = gt->to_gamma; - if (vg <= to_gamma) { - return changeUIntScale(vg, from_gamma, to_gamma, from_src, to_src); - } - from_src = to_src; - from_gamma = to_gamma; - } -} - - -uint16_t ledGamma10_10(uint16_t v) { - return ledGamma_internal(v, gamma_table); -} - -uint16_t ledGamma10(uint8_t v) { - return ledGamma10_10(change8to10(v)); -} - - -uint8_t ledGamma(uint8_t v) { - return change10to8(ledGamma10(v)); -} - - - -void LightPwmOffset(uint32_t offset) -{ - Light.pwm_offset = offset; -} - -bool LightModuleInit(void) -{ - light_type = LT_BASIC; - - if (Settings.flag.pwm_control) { - for (uint32_t i = 0; i < MAX_PWMS; i++) { - if (pin[GPIO_PWM1 +i] < 99) { light_type++; } - } - } - - light_flg = 0; - if (XlgtCall(FUNC_MODULE_INIT)) { - - } -#ifdef ESP8266 - else if (SONOFF_BN == my_module_type) { - light_type = LT_PWM1; - } - else if (SONOFF_LED == my_module_type) { - if (!my_module.io[4]) { - pinMode(4, OUTPUT); - digitalWrite(4, LOW); - } - if (!my_module.io[5]) { - pinMode(5, OUTPUT); - digitalWrite(5, LOW); - } - if (!my_module.io[14]) { - pinMode(14, OUTPUT); - digitalWrite(14, LOW); - } - light_type = LT_PWM2; - } -#endif - - if (light_type > LT_BASIC) { - devices_present++; - } - - - if (Settings.flag3.pwm_multi_channels) { - uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); - if (0 == pwm_channels) { pwm_channels = 1; } - devices_present += pwm_channels - 1; - } else if ((Settings.param[P_RGB_REMAP] & 128) && (LST_RGBW <= (light_type & 7))) { - - devices_present++; - } - - return (light_type > LT_BASIC); -} - - - -void LightCalcPWMRange(void) { - uint16_t pwm_min, pwm_max; - - pwm_min = change8to10(LightStateClass::DimmerToBri(Settings.dimmer_hw_min)); - pwm_max = change8to10(LightStateClass::DimmerToBri(Settings.dimmer_hw_max)); - if (Settings.light_correction) { - pwm_min = ledGamma10_10(pwm_min); - pwm_max = ledGamma10_10(pwm_max); - } - pwm_min = pwm_min > 0 ? changeUIntScale(pwm_min, 1, 1023, 1, Settings.pwm_range) : 0; - pwm_max = changeUIntScale(pwm_max, 1, 1023, 1, Settings.pwm_range); - - Light.pwm_min = pwm_min; - Light.pwm_max = pwm_max; - -} - -void LightInit(void) -{ - Light.device = devices_present; - Light.subtype = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); - Light.pwm_multi_channels = Settings.flag3.pwm_multi_channels; - - if (LST_RGBW <= Light.subtype) { - - - bool ct_rgb_linked = !(Settings.param[P_RGB_REMAP] & 128); - light_controller.setCTRGBLinked(ct_rgb_linked); - } - - if ((LST_SINGLE <= Light.subtype) && Light.pwm_multi_channels) { - - light_controller.setPWMMultiChannel(true); - Light.device = devices_present - Light.subtype + 1; - } else if (!light_controller.isCTRGBLinked()) { - - Light.device--; - } - LightCalcPWMRange(); -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightInit Light.pwm_multi_channels=%d Light.subtype=%d Light.device=%d devices_present=%d", - Light.pwm_multi_channels, Light.subtype, Light.device, devices_present); -#endif - - light_controller.setSubType(Light.subtype); - light_controller.loadSettings(); - light_controller.setAlexaCTRange(Settings.flag4.alexa_ct_range); - light_controller.calcLevels(); - - if (LST_SINGLE == Light.subtype) { - Settings.light_color[0] = 255; - } - if (light_type < LT_PWM6) { - for (uint32_t i = 0; i < light_type; i++) { - Settings.pwm_value[i] = 0; - if (pin[GPIO_PWM1 +i] < 99) { - pinMode(pin[GPIO_PWM1 +i], OUTPUT); - } - } - if (pin[GPIO_ARIRFRCV] < 99) { - if (pin[GPIO_ARIRFSEL] < 99) { - pinMode(pin[GPIO_ARIRFSEL], OUTPUT); - digitalWrite(pin[GPIO_ARIRFSEL], 1); - } - } - } - - uint32_t max_scheme = Light.max_scheme; - if (Light.subtype < LST_RGB) { - max_scheme = LS_POWER; - } - if ((LS_WAKEUP == Settings.light_scheme) || (Settings.light_scheme > max_scheme)) { - Settings.light_scheme = LS_POWER; - } - Light.power = 0; - Light.update = true; - Light.wakeup_active = 0; - if (Settings.flag4.fade_at_startup) { - Light.fade_initialized = true; - } - - LightUpdateColorMapping(); -} - -void LightUpdateColorMapping(void) -{ - uint8_t param = Settings.param[P_RGB_REMAP] & 127; - if (param > 119){ param = 0; } - - uint8_t tmp[] = {0,1,2,3,4}; - Light.color_remap[0] = tmp[param / 24]; - for (uint32_t i = param / 24; i<4; ++i){ - tmp[i] = tmp[i+1]; - } - param = param % 24; - Light.color_remap[1] = tmp[(param / 6)]; - for (uint32_t i = param / 6; i<3; ++i){ - tmp[i] = tmp[i+1]; - } - param = param % 6; - Light.color_remap[2] = tmp[(param / 2)]; - for (uint32_t i = param / 2; i<2; ++i){ - tmp[i] = tmp[i+1]; - } - param = param % 2; - Light.color_remap[3] = tmp[param]; - Light.color_remap[4] = tmp[1-param]; - - Light.update = true; - -} - -uint8_t LightGetDimmer(uint8_t dimmer) { - return light_state.getDimmer(dimmer); -} - -void LightSetDimmer(uint8_t dimmer) { - light_controller.changeDimmer(dimmer); -} - -void LightGetHSB(uint16_t *hue, uint8_t *sat, uint8_t *bri) { - light_state.getHSB(hue, sat, bri); -} - -void LightHsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b) { - light_state.HsToRgb(hue, sat, r_r, r_g, r_b); -} - - -uint8_t LightGetBri(uint8_t device) { - uint8_t bri = 254; - if (Light.pwm_multi_channels) { - if ((device >= Light.device) && (device < Light.device + LST_MAX) && (device <= devices_present)) { - bri = Light.current_color[device - Light.device]; - } - } else if (light_controller.isCTRGBLinked()) { - if (device == Light.device) { - bri = light_state.getBri(); - } - } else { - if (device == Light.device) { - bri = light_state.getBriRGB(); - } else if (device == Light.device + 1) { - bri = light_state.getBriCT(); - } - } - return bri; -} - - -void LightSetBri(uint8_t device, uint8_t bri) { - if (Light.pwm_multi_channels) { - if ((device >= Light.device) && (device < Light.device + LST_MAX) && (device <= devices_present)) { - Light.current_color[device - Light.device] = bri; - light_controller.changeChannels(Light.current_color); - } - } else if (light_controller.isCTRGBLinked()) { - if (device == Light.device) { - light_controller.changeBri(bri); - } - } else { - if (device == Light.device) { - light_controller.changeBriRGB(bri); - } else if (device == Light.device + 1) { - light_controller.changeBriCT(bri); - } - } -} - -void LightSetColorTemp(uint16_t ct) -{ - - - - - - - if ((LST_COLDWARM != Light.subtype) && (LST_RGBCW != Light.subtype)) { - return; - } - light_controller.changeCTB(ct, light_state.getBriCT()); -} - -uint16_t LightGetColorTemp(void) -{ - - if ((LST_COLDWARM != Light.subtype) && (LST_RGBCW != Light.subtype)) { - return 0; - } - return (light_state.getColorMode() & LCM_CT) ? light_state.getCT() : 0; -} - -void LightSetSignal(uint16_t lo, uint16_t hi, uint16_t value) -{ - - - - if (Settings.flag.light_signal) { - uint16_t signal = changeUIntScale(value, lo, hi, 0, 255); - - light_controller.changeRGB(signal, 255 - signal, 0, true); - Settings.light_scheme = 0; -#ifdef USE_DEVICE_GROUPS - LightUpdateScheme(); -#endif - if (0 == light_state.getBri()) { - light_controller.changeBri(50); - } - } -} - - -char* LightGetColor(char* scolor, boolean force_hex = false) -{ - if ((0 == Settings.light_scheme) || (!Light.pwm_multi_channels)) { - light_controller.calcLevels(); - } - scolor[0] = '\0'; - for (uint32_t i = 0; i < Light.subtype; i++) { - if (!force_hex && Settings.flag.decimal_text) { - snprintf_P(scolor, LIGHT_COLOR_SIZE, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Light.current_color[i]); - } else { - snprintf_P(scolor, LIGHT_COLOR_SIZE, PSTR("%s%02X"), scolor, Light.current_color[i]); - } - } - return scolor; -} - -void LightPowerOn(void) -{ - if (light_state.getBri() && !(Light.power)) { - ExecuteCommandPower(Light.device, POWER_ON, SRC_LIGHT); - } -} - -void LightState(uint8_t append) -{ - char scolor[LIGHT_COLOR_SIZE]; - char scommand[33]; - bool unlinked = !light_controller.isCTRGBLinked() && (Light.subtype >= LST_RGBW); - - if (append) { - ResponseAppend_P(PSTR(",")); - } else { - Response_P(PSTR("{")); - } - if (!Light.pwm_multi_channels) { - if (unlinked) { - - ResponseAppend_P(PSTR("\"" D_RSLT_POWER "%d\":\"%s\",\"" D_CMND_DIMMER "%d\":%d" - ",\"" D_RSLT_POWER "%d\":\"%s\",\"" D_CMND_DIMMER "%d\":%d"), - Light.device, GetStateText(Light.power & 1), Light.device, light_state.getDimmer(1), - Light.device + 1, GetStateText(Light.power & 2 ? 1 : 0), Light.device + 1, light_state.getDimmer(2)); - } else { - GetPowerDevice(scommand, Light.device, sizeof(scommand), Settings.flag.device_index_enable); - ResponseAppend_P(PSTR("\"%s\":\"%s\",\"" D_CMND_DIMMER "\":%d"), scommand, GetStateText(Light.power & 1), - light_state.getDimmer()); - } - - - if (Light.subtype > LST_SINGLE) { - ResponseAppend_P(PSTR(",\"" D_CMND_COLOR "\":\"%s\""), LightGetColor(scolor)); - if (LST_RGB <= Light.subtype) { - uint16_t hue; - uint8_t sat, bri; - light_state.getHSB(&hue, &sat, &bri); - sat = changeUIntScale(sat, 0, 255, 0, 100); - bri = changeUIntScale(bri, 0, 255, 0, 100); - - ResponseAppend_P(PSTR(",\"" D_CMND_HSBCOLOR "\":\"%d,%d,%d\""), hue,sat,bri); - } - - if ((LST_COLDWARM == Light.subtype) || (LST_RGBW <= Light.subtype)) { - ResponseAppend_P(PSTR(",\"" D_CMND_WHITE "\":%d"), light_state.getDimmer(2)); - } - - if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { - ResponseAppend_P(PSTR(",\"" D_CMND_COLORTEMPERATURE "\":%d"), light_state.getCT()); - } - - ResponseAppend_P(PSTR(",\"" D_CMND_CHANNEL "\":[" )); - for (uint32_t i = 0; i < Light.subtype; i++) { - uint8_t channel_raw = Light.current_color[i]; - uint8_t channel = changeUIntScale(channel_raw,0,255,0,100); - - if ((0 == channel) && (channel_raw > 0)) { channel = 1; } - ResponseAppend_P(PSTR("%s%d" ), (i > 0 ? "," : ""), channel); - } - ResponseAppend_P(PSTR("]")); - } - - if (append) { - if (Light.subtype >= LST_RGB) { - ResponseAppend_P(PSTR(",\"" D_CMND_SCHEME "\":%d"), Settings.light_scheme); - } - if (Light.max_scheme > LS_MAX) { - ResponseAppend_P(PSTR(",\"" D_CMND_WIDTH "\":%d"), Settings.light_width); - } - ResponseAppend_P(PSTR(",\"" D_CMND_FADE "\":\"%s\",\"" D_CMND_SPEED "\":%d,\"" D_CMND_LEDTABLE "\":\"%s\""), - GetStateText(Settings.light_fade), Settings.light_speed, GetStateText(Settings.light_correction)); - } - } else { - for (uint32_t i = 0; i < Light.subtype; i++) { - GetPowerDevice(scommand, Light.device + i, sizeof(scommand), 1); - uint32_t light_power_masked = Light.power & (1 << i); - light_power_masked = light_power_masked ? 1 : 0; - ResponseAppend_P(PSTR("\"%s\":\"%s\",\"" D_CMND_CHANNEL "%d\":%d,"), scommand, GetStateText(light_power_masked), Light.device + i, - changeUIntScale(Light.current_color[i], 0, 255, 0, 100)); - } - ResponseAppend_P(PSTR("\"" D_CMND_COLOR "\":\"%s\""), LightGetColor(scolor)); - } - - if (!append) { - ResponseJsonEnd(); - } -} - -void LightPreparePower(power_t channels = 0xFFFFFFFF) { -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG, "LightPreparePower power=%d Light.power=%d", power, Light.power); -#endif - - if (Light.pwm_multi_channels) { - for (uint32_t i = 0; i < Light.subtype; i++) { - if (bitRead(channels, i)) { - - if ((Light.current_color[i]) && (!bitRead(Light.power, i))) { - if (!Settings.flag.not_power_linked) { - ExecuteCommandPower(Light.device + i, POWER_ON_NO_STATE, SRC_LIGHT); - } - } else { - - if ((0 == Light.current_color[i]) && bitRead(Light.power, i)) { - ExecuteCommandPower(Light.device + i, POWER_OFF_NO_STATE, SRC_LIGHT); - } - } - #ifdef USE_DOMOTICZ - DomoticzUpdatePowerState(Light.device + i); - #endif - } - } - } else { - if (light_controller.isCTRGBLinked()) { - if (light_state.getBri() && !(Light.power)) { - if (!Settings.flag.not_power_linked) { - ExecuteCommandPower(Light.device, POWER_ON_NO_STATE, SRC_LIGHT); - } - } else if (!light_state.getBri() && Light.power) { - ExecuteCommandPower(Light.device, POWER_OFF_NO_STATE, SRC_LIGHT); - } - } else { - - if (channels & 1) { - if (light_state.getBriRGB() && !(Light.power & 1)) { - if (!Settings.flag.not_power_linked) { - ExecuteCommandPower(Light.device, POWER_ON_NO_STATE, SRC_LIGHT); - } - } else if (!light_state.getBriRGB() && (Light.power & 1)) { - ExecuteCommandPower(Light.device, POWER_OFF_NO_STATE, SRC_LIGHT); - } - } - - if (channels & 2) { - if (light_state.getBriCT() && !(Light.power & 2)) { - if (!Settings.flag.not_power_linked) { - ExecuteCommandPower(Light.device + 1, POWER_ON_NO_STATE, SRC_LIGHT); - } - } else if (!light_state.getBriCT() && (Light.power & 2)) { - ExecuteCommandPower(Light.device + 1, POWER_OFF_NO_STATE, SRC_LIGHT); - } - } - } -#ifdef USE_DOMOTICZ - DomoticzUpdatePowerState(Light.device); -#endif - } - - if (Settings.flag3.hass_tele_on_power) { - MqttPublishTeleState(); - } - -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG, "LightPreparePower End power=%d Light.power=%d", power, Light.power); -#endif - Light.power = power >> (Light.device - 1); - LightState(0); -} - -#ifdef USE_LIGHT_PALETTE -void LightSetPaletteEntry(void) -{ - uint8_t bri = light_state.getBri(); - uint8_t * palette_entry = &Light.palette[Light.wheel * LST_MAX]; - for (int i = 0; i < LST_MAX; i++) { - Light.new_color[i] = changeUIntScale(palette_entry[i], 0, 255, 0, bri); - } - light_state.setChannelsRaw(Light.new_color); - if (!Light.pwm_multi_channels) { - light_state.setCW(Light.new_color[3], Light.new_color[4], true); - if (Light.new_color[0] || Light.new_color[1] || Light.new_color[2]) light_state.addRGBMode(); - } -} -#endif - -void LightCycleColor(int8_t direction) -{ - - if (Settings.light_speed > 3) { - if (Light.strip_timer_counter % (Settings.light_speed - 2)) { return; } - } - -#ifdef USE_LIGHT_PALETTE - if (Light.palette_count) { - if (0 == direction) { - Light.wheel = random(Light.palette_count); - } - else { - Light.wheel += direction; - if (Light.wheel >= Light.palette_count) { - Light.wheel = 0; - if (direction < 0) Light.wheel = Light.palette_count - 1; - } - } - LightSetPaletteEntry(); - return; - } -#endif - - if (0 == direction) { - if (Light.random == Light.wheel) { - Light.random = random(255); - - uint8_t my_dir = (Light.random < Light.wheel -128) ? 1 : - (Light.random < Light.wheel ) ? 0 : - (Light.random > Light.wheel +128) ? 0 : 1; - Light.random = (Light.random & 0xFE) | my_dir; - - - } - - direction = (Light.random &0x01) ? 1 : -1; - } - - - if (Settings.light_speed < 3) { direction *= (4 - Settings.light_speed); } - Light.wheel += direction; - uint16_t hue = changeUIntScale(Light.wheel, 0, 255, 0, 359); - - - - if (!Light.pwm_multi_channels) { - uint8_t sat; - light_state.getHSB(nullptr, &sat, nullptr); - light_state.setHS(hue, sat); - } else { - light_state.setHS(hue, 255); - light_state.setBri(255); - } - light_controller.calcLevels(Light.new_color); -} - -void LightSetPower(void) -{ - - Light.old_power = Light.power; - - uint32_t mask = 1; - if (Light.pwm_multi_channels) { - mask = (1 << Light.subtype) - 1; - } else if (!light_controller.isCTRGBLinked()) { - mask = 3; - } - uint32_t shift = Light.device - 1; - - - - - - Light.power = (XdrvMailbox.index & (mask << shift)) >> shift; - if (Light.wakeup_active) { - Light.wakeup_active--; - } -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightSetPower XdrvMailbox.index=%d Light.old_power=%d Light.power=%d mask=%d shift=%d", - XdrvMailbox.index, Light.old_power, Light.power, mask, shift); -#endif - if (Light.power != Light.old_power) { - Light.update = true; - } - LightAnimate(); -} - - - - -void LightAnimate(void) -{ - uint16_t light_still_on = 0; - bool power_off = false; - - - light_controller.setAlexaCTRange(Settings.flag4.alexa_ct_range); - Light.strip_timer_counter++; - - - - if (Light.power || Light.fade_running) { - if (Settings.sleep > PWM_MAX_SLEEP) { - ssleep = PWM_MAX_SLEEP; - } else { - ssleep = Settings.sleep; - } - } else { - ssleep = Settings.sleep; - } - - if (!Light.power) { - Light.strip_timer_counter = 0; - if (Settings.light_scheme >= LS_MAX) { - power_off = true; - } - } else { - switch (Settings.light_scheme) { - case LS_POWER: - light_controller.calcLevels(Light.new_color); - break; - case LS_WAKEUP: - if (2 == Light.wakeup_active) { - Light.wakeup_active = 1; - for (uint32_t i = 0; i < Light.subtype; i++) { - Light.new_color[i] = 0; - } - Light.wakeup_counter = 0; - Light.wakeup_dimmer = 0; - } - Light.wakeup_counter++; - if (Light.wakeup_counter > ((Settings.light_wakeup * STATES) / Settings.light_dimmer)) { - Light.wakeup_counter = 0; - Light.wakeup_dimmer++; - if (Light.wakeup_dimmer <= Settings.light_dimmer) { - light_state.setDimmer(Light.wakeup_dimmer); - light_controller.calcLevels(); - for (uint32_t i = 0; i < Light.subtype; i++) { - Light.new_color[i] = Light.current_color[i]; - } - } else { - - - - - Response_P(PSTR("{\"" D_CMND_WAKEUP "\":\"" D_JSON_DONE "\"")); - LightState(1); - ResponseJsonEnd(); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_WAKEUP)); - XdrvRulesProcess(); - - Light.wakeup_active = 0; - Settings.light_scheme = LS_POWER; -#ifdef USE_DEVICE_GROUPS - LightUpdateScheme(); -#endif - } - } - break; - case LS_CYCLEUP: - case LS_CYCLEDN: - case LS_RANDOM: - if (LS_CYCLEUP == Settings.light_scheme) { - LightCycleColor(1); - } else if (LS_CYCLEDN == Settings.light_scheme) { - LightCycleColor(-1); - } else { - LightCycleColor(0); - } - if (Light.pwm_multi_channels) { - Light.new_color[0] = changeUIntScale(Light.new_color[0], 0, 255, 0, Settings.light_color[0]); - Light.new_color[1] = changeUIntScale(Light.new_color[1], 0, 255, 0, Settings.light_color[1]); - Light.new_color[2] = changeUIntScale(Light.new_color[2], 0, 255, 0, Settings.light_color[2]); - } - break; - default: - XlgtCall(FUNC_SET_SCHEME); - } - } - - if ((Settings.light_scheme < LS_MAX) || power_off) { - - - LightApplyPower(Light.new_color, Light.power); - - - - - - - - if (memcmp(Light.last_color, Light.new_color, Light.subtype)) { - Light.update = true; - } - if (Light.update) { -#ifdef USE_DEVICE_GROUPS - if (Light.power) LightSendDeviceGroupStatus(false); -#endif - - uint16_t cur_col_10[LST_MAX]; - Light.update = false; - - - for (uint32_t i = 0; i < LST_MAX; i++) { - Light.last_color[i] = Light.new_color[i]; - - cur_col_10[i] = change8to10(Light.new_color[i]); - } - - if (Light.pwm_multi_channels) { - calcGammaMultiChannels(cur_col_10); - } else { - calcGammaBulbs(cur_col_10); - - - - if ((LST_RGBW <= Light.subtype) && (0 == Settings.rgbwwTable[4]) && (0 == cur_col_10[3]+cur_col_10[4])) { - uint32_t min_rgb_10 = min3(cur_col_10[0], cur_col_10[1], cur_col_10[2]); - for (uint32_t i=0; i<3; i++) { - - uint32_t adjust10 = change8to10(Settings.rgbwwTable[i]); - cur_col_10[i] = changeUIntScale(cur_col_10[i] - min_rgb_10, 0, 1023, 0, adjust10); - } - - - uint32_t adjust_w_10 = changeUIntScale(Settings.rgbwwTable[3], 0, 255, 0, 1023); - uint32_t white_10 = changeUIntScale(min_rgb_10, 0, 1023, 0, adjust_w_10); - if (LST_RGBW == Light.subtype) { - - cur_col_10[3] = white_10; - } else { - - uint32_t ct = light_state.getCT10bits(); - cur_col_10[4] = changeUIntScale(ct, 0, 1023, 0, white_10); - cur_col_10[3] = white_10 - cur_col_10[4]; - } - } - } - - - if (0 != Settings.rgbwwTable[4]) { - for (uint32_t i = 0; i 0) ? changeUIntScale(cur_col_10[i], 1, 1023, 1, Settings.pwm_range) : 0; - } - - - uint16_t orig_col_10bits[LST_MAX]; - memcpy(orig_col_10bits, cur_col_10, sizeof(orig_col_10bits)); - for (uint32_t i = 0; i < LST_MAX; i++) { - cur_col_10[i] = orig_col_10bits[Light.color_remap[i]]; - } - - if (!Settings.light_fade || skip_light_fade || power_off || (!Light.fade_initialized)) { - - memcpy(Light.fade_start_10, cur_col_10, sizeof(Light.fade_start_10)); - - LightSetOutputs(cur_col_10); - Light.fade_initialized = true; - } else { - if (Light.fade_running) { - - memcpy(Light.fade_start_10, Light.fade_cur_10, sizeof(Light.fade_start_10)); - } - memcpy(Light.fade_end_10, cur_col_10, sizeof(Light.fade_start_10)); - Light.fade_running = true; - Light.fade_duration = 0; - Light.fade_start = 0; - - } - } - if (Light.fade_running) { - if (LightApplyFade()) { - - - - LightSetOutputs(Light.fade_cur_10); - } - } -#ifdef USE_PWM_DIMMER - - if (PWM_DIMMER == my_module_type && !Light.power && !Light.fade_running) PWMDimmerSetPower(); -#endif - } -} - -bool isChannelGammaCorrected(uint32_t channel) { - if (!Settings.light_correction) { return false; } - if (channel >= Light.subtype) { return false; } -#ifdef ESP8266 - if (PHILIPS == my_module_type) { - if ((LST_COLDWARM == Light.subtype) && (1 == channel)) { return false; } - if ((LST_RGBCW == Light.subtype) && (4 == channel)) { return false; } - } -#endif - return true; -} - - -bool isChannelCT(uint32_t channel) { -#ifdef ESP8266 - if (PHILIPS == my_module_type) { - if ((LST_COLDWARM == Light.subtype) && (1 == channel)) { return true; } - if ((LST_RGBCW == Light.subtype) && (4 == channel)) { return true; } - } -#endif - return false; -} - - -uint16_t fadeGamma(uint32_t channel, uint16_t v) { - if (isChannelGammaCorrected(channel)) { - return ledGamma_internal(v, gamma_table_fast); - } else { - return v; - } -} -uint16_t fadeGammaReverse(uint32_t channel, uint16_t vg) { - if (isChannelGammaCorrected(channel)) { - return ledGammaReverse_internal(vg, gamma_table_fast); - } else { - return vg; - } -} - -bool LightApplyFade(void) { - static uint32_t last_millis = 0; - uint32_t now = millis(); - - if ((now - last_millis) <= 5) { - return false; - } - last_millis = now; - - - if (0 == Light.fade_duration) { - Light.fade_start = now; - - uint32_t distance = 0; - for (uint32_t i = 0; i < Light.subtype; i++) { - int32_t channel_distance = fadeGammaReverse(i, Light.fade_end_10[i]) - fadeGammaReverse(i, Light.fade_start_10[i]); - if (channel_distance < 0) { channel_distance = - channel_distance; } - if (channel_distance > distance) { distance = channel_distance; } - } - if (distance > 0) { - - - - Light.fade_duration = (distance * Settings.light_speed * 500) / 1023; - if (Settings.save_data) { - - uint32_t delay_seconds = 1 + (Light.fade_duration + 999) / 1000; - - if (save_data_counter < delay_seconds) { - save_data_counter = delay_seconds; - } - } - } else { - - Light.fade_running = false; - } - } - - uint16_t fade_current = now - Light.fade_start; - if (fade_current <= Light.fade_duration) { - - for (uint32_t i = 0; i < Light.subtype; i++) { - Light.fade_cur_10[i] = fadeGamma(i, - changeUIntScale(fadeGammaReverse(i, fade_current), - 0, Light.fade_duration, - fadeGammaReverse(i, Light.fade_start_10[i]), - fadeGammaReverse(i, Light.fade_end_10[i]))); - - - - } - } else { - - - Light.fade_running = false; - Light.fade_start = 0; - Light.fade_duration = 0; - - memcpy(Light.fade_cur_10, Light.fade_end_10, sizeof(Light.fade_end_10)); - - memcpy(Light.fade_start_10, Light.fade_end_10, sizeof(Light.fade_start_10)); - } - return true; -} - - - -void LightApplyPower(uint8_t new_color[LST_MAX], power_t power) { - - if (Light.pwm_multi_channels) { - - for (uint32_t i = 0; i < LST_MAX; i++) { - if (0 == bitRead(power,i)) { - new_color[i] = 0; - } - } - - - - - - } else { - if (!light_controller.isCTRGBLinked()) { - - if (0 == (power & 1)) { - new_color[0] = new_color[1] = new_color[2] = 0; - } - if (0 == (power & 2)) { - new_color[3] = new_color[4] = 0; - } - } else if (!power) { - for (uint32_t i = 0; i < LST_MAX; i++) { - new_color[i] = 0; - } - } - } -} - -void LightSetOutputs(const uint16_t *cur_col_10) { - - if (light_type < LT_PWM6) { - for (uint32_t i = 0; i < (Light.subtype - Light.pwm_offset); i++) { - if (pin[GPIO_PWM1 +i] < 99) { - - uint16_t cur_col = cur_col_10[i + Light.pwm_offset]; - if (!isChannelCT(i)) { - cur_col = cur_col > 0 ? changeUIntScale(cur_col, 0, Settings.pwm_range, Light.pwm_min, Light.pwm_max) : 0; - } - analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - cur_col : cur_col); - } - } - } - - - - - uint8_t cur_col[LST_MAX]; - for (uint32_t i = 0; i < LST_MAX; i++) { - cur_col[i] = change10to8(cur_col_10[i]); - } - - - uint8_t scale_col[3]; - uint32_t max = (cur_col[0] > cur_col[1] && cur_col[0] > cur_col[2]) ? cur_col[0] : (cur_col[1] > cur_col[2]) ? cur_col[1] : cur_col[2]; - for (uint32_t i = 0; i < 3; i++) { - scale_col[i] = (0 == max) ? 255 : (255 > max) ? changeUIntScale(cur_col[i], 0, max, 0, 255) : cur_col[i]; - } - - char *tmp_data = XdrvMailbox.data; - char *tmp_topic = XdrvMailbox.topic; - XdrvMailbox.data = (char*)cur_col; - XdrvMailbox.topic = (char*)scale_col; - if (XlgtCall(FUNC_SET_CHANNELS)) { } - else if (XdrvCall(FUNC_SET_CHANNELS)) { } - XdrvMailbox.data = tmp_data; - XdrvMailbox.topic = tmp_topic; -} - - -void calcGammaMultiChannels(uint16_t cur_col_10[5]) { - - if (Settings.light_correction) { - for (uint32_t i = 0; i < LST_MAX; i++) { - cur_col_10[i] = ledGamma10_10(cur_col_10[i]); - } - } -} - -void calcGammaBulbs(uint16_t cur_col_10[5]) { - - - - if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { - - uint32_t cw1 = Light.subtype - 1; - uint32_t cw0 = Light.subtype - 2; - uint16_t white_bri10 = cur_col_10[cw0] + cur_col_10[cw1]; - uint16_t white_bri10_1023 = (white_bri10 > 1023) ? 1023 : white_bri10; - -#ifdef ESP8266 - if (PHILIPS == my_module_type) { - - cur_col_10[cw1] = light_state.getCT10bits(); - - if (Settings.light_correction) { - cur_col_10[cw0] = ledGamma10_10(white_bri10_1023); - } else { - cur_col_10[cw0] = white_bri10_1023; - } - } else -#endif - if (Settings.light_correction) { - - if (white_bri10 <= 1031) { - - uint16_t white_bri_gamma10 = ledGamma10_10(white_bri10_1023); - - cur_col_10[cw0] = changeUIntScale(cur_col_10[cw0], 0, white_bri10_1023, 0, white_bri_gamma10); - cur_col_10[cw1] = changeUIntScale(cur_col_10[cw1], 0, white_bri10_1023, 0, white_bri_gamma10); - } else { - cur_col_10[cw0] = ledGamma10_10(cur_col_10[cw0]); - cur_col_10[cw1] = ledGamma10_10(cur_col_10[cw1]); - } - } - } - - if (Settings.light_correction) { - - if (LST_RGB <= Light.subtype) { - for (uint32_t i = 0; i < 3; i++) { - cur_col_10[i] = ledGamma10_10(cur_col_10[i]); - } - } - - if ((LST_SINGLE == Light.subtype) || (LST_RGBW == Light.subtype)) { - cur_col_10[Light.subtype - 1] = ledGamma10_10(cur_col_10[Light.subtype - 1]); - } - } -} - -#ifdef USE_DEVICE_GROUPS -void LightSendDeviceGroupStatus(bool force) -{ - static uint8_t last_channels[LST_MAX]; - static uint8_t last_bri; - - uint8_t bri = light_state.getBri(); - bool send_bri_update = (force || bri != last_bri); - - if (Light.subtype > LST_SINGLE && !Light.devgrp_no_channels_out) { - uint8_t channels[LST_MAX]; - light_state.getChannels(channels); - if (force || memcmp(last_channels, channels, LST_MAX)) { - memcpy(last_channels, channels, LST_MAX); - SendLocalDeviceGroupMessage((send_bri_update ? DGR_MSGTYP_PARTIAL_UPDATE : DGR_MSGTYP_UPDATE), DGR_ITEM_LIGHT_CHANNELS, channels); - } - } - if (send_bri_update) { - last_bri = bri; - SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_BRI, light_state.getBri()); - } -} - -void LightHandleDevGroupItem(void) -{ - static bool send_state = false; - static bool restore_power = false; - bool more_to_come; - uint32_t value = XdrvMailbox.payload; -#ifdef USE_PWM_DIMMER_REMOTE - if (*XdrvMailbox.topic) return; -#endif - switch (XdrvMailbox.command_code) { - case DGR_ITEM_EOL: - more_to_come = (XdrvMailbox.index & DGR_FLAG_MORE_TO_COME); - if (restore_power && !more_to_come) { - restore_power = false; - Light.power = Light.old_power; - } - - LightAnimate(); - - if (send_state && !more_to_come) { - light_controller.saveSettings(); - if (Settings.flag3.hass_tele_on_power) { - MqttPublishTeleState(); - } - send_state = false; - } - break; - case DGR_ITEM_LIGHT_BRI: - if (light_state.getBri() != value) { - light_state.setBri(value); - send_state = true; - } - break; - case DGR_ITEM_LIGHT_SCHEME: - if (Settings.light_scheme != value) { - Settings.light_scheme = value; - Light.devgrp_no_channels_out = (value != 0); - send_state = true; - } - break; - case DGR_ITEM_LIGHT_CHANNELS: - light_controller.changeChannels((uint8_t *)XdrvMailbox.data); - send_state = true; - break; - case DGR_ITEM_LIGHT_FIXED_COLOR: - if (Light.subtype >= LST_RGBW) { - send_state = true; -#ifdef USE_LIGHT_PALETTE - if (Light.palette_count) { - Light.wheel = value % Light.palette_count; - LightSetPaletteEntry(); - break; - } -#endif - value = value % MAX_FIXED_COLOR; - if (value) { - bool save_decimal_text = Settings.flag.decimal_text; - char str[16]; - LightColorEntry(str, sprintf_P(str, PSTR("%u"), value)); - Settings.flag.decimal_text = save_decimal_text; - uint32_t old_bri = light_state.getBri(); - light_controller.changeChannels(Light.entry_color); - light_controller.changeBri(old_bri); - Settings.light_scheme = 0; - Light.devgrp_no_channels_out = false; - } - else { - light_state.setColorMode(LCM_CT); - } - if (!restore_power && !Light.power) { - Light.old_power = Light.power; - Light.power = 0xff; - restore_power = true; - } - } - break; - case DGR_ITEM_LIGHT_FADE: - if (Settings.light_fade != value) { - Settings.light_fade = value; - send_state = true; - } - break; - case DGR_ITEM_LIGHT_SPEED: - if (Settings.light_speed != value && value > 0 && value <= 40) { - Settings.light_speed = value; - send_state = true; - } - break; - case DGR_ITEM_STATUS: - SendLocalDeviceGroupMessage(DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_LIGHT_FADE, Settings.light_fade, - DGR_ITEM_LIGHT_SPEED, Settings.light_speed, DGR_ITEM_LIGHT_SCHEME, Settings.light_scheme); - LightSendDeviceGroupStatus(true); - break; - } -} - -void LightUpdateScheme(void) -{ - static uint8_t last_scheme; - - if (Settings.light_scheme != last_scheme) { - last_scheme = Settings.light_scheme; - SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_SCHEME, Settings.light_scheme); - } - Light.devgrp_no_channels_out = false; -} -#endif - - - - - -bool LightColorEntry(char *buffer, uint32_t buffer_length) -{ - char scolor[10]; - char *p; - char *str; - uint32_t entry_type = 0; - uint8_t value = Light.fixed_color_index; -#ifdef USE_LIGHT_PALETTE - if (Light.palette_count) value = Light.wheel; -#endif - - if (buffer[0] == '#') { - buffer++; - buffer_length--; - } - - if (Light.subtype >= LST_RGB) { - char option = (1 == buffer_length) ? buffer[0] : '\0'; - if ('+' == option) { -#ifdef USE_LIGHT_PALETTE - if (Light.palette_count || Light.fixed_color_index < MAX_FIXED_COLOR) { -#else - if (Light.fixed_color_index < MAX_FIXED_COLOR) { -#endif - value++; - } - } - else if ('-' == option) { -#ifdef USE_LIGHT_PALETTE - if (Light.palette_count || Light.fixed_color_index > 1) { -#else - if (Light.fixed_color_index > 1) { -#endif - value--; - } - } else { - value = atoi(buffer); -#ifdef USE_LIGHT_PALETTE - value--; -#endif - } -#ifdef USE_LIGHT_PALETTE - if (Light.palette_count) value = value % Light.palette_count; -#endif - } - - memset(&Light.entry_color, 0x00, sizeof(Light.entry_color)); - - while ((buffer_length > 0) && ('=' == buffer[buffer_length - 1])) { - buffer_length--; - memcpy(&Light.entry_color, &Light.current_color, sizeof(Light.entry_color)); - } - if (strstr(buffer, ",") != nullptr) { - int8_t i = 0; - for (str = strtok_r(buffer, ",", &p); str && i < 6; str = strtok_r(nullptr, ",", &p)) { - if (i < LST_MAX) { - Light.entry_color[i++] = atoi(str); - } - } - entry_type = 2; - } - else if (((2 * Light.subtype) == buffer_length) || (buffer_length > 3)) { - for (uint32_t i = 0; i < tmin((uint)(buffer_length / 2), sizeof(Light.entry_color)); i++) { - strlcpy(scolor, buffer + (i *2), 3); - Light.entry_color[i] = (uint8_t)strtol(scolor, &p, 16); - } - entry_type = 1; - } -#ifdef USE_LIGHT_PALETTE - else if (Light.palette_count) { - Light.wheel = value; - memcpy_P(&Light.entry_color, &Light.palette[value * LST_MAX], LST_MAX); - entry_type = 1; - } -#endif - else if ((Light.subtype >= LST_RGB) && (value > 0) && (value <= MAX_FIXED_COLOR)) { - Light.fixed_color_index = value; - memcpy_P(&Light.entry_color, &kFixedColor[value -1], 3); - entry_type = 1; - } - else if ((value > 199) && (value <= 199 + MAX_FIXED_COLD_WARM)) { - if (LST_RGBW == Light.subtype) { - memcpy_P(&Light.entry_color[3], &kFixedWhite[value -200], 1); - entry_type = 1; - } - else if (LST_COLDWARM == Light.subtype) { - memcpy_P(&Light.entry_color, &kFixedColdWarm[value -200], 2); - entry_type = 1; - } - else if (LST_RGBCW == Light.subtype) { - memcpy_P(&Light.entry_color[3], &kFixedColdWarm[value -200], 2); - entry_type = 1; - } - } - if (entry_type) { - Settings.flag.decimal_text = entry_type -1; - } - return (entry_type); -} - - - -void CmndSupportColor(void) -{ - bool valid_entry = false; - bool coldim = false; - - if (XdrvMailbox.data_len > 0) { - valid_entry = LightColorEntry(XdrvMailbox.data, XdrvMailbox.data_len); - if (valid_entry) { - if (XdrvMailbox.index <= 2) { -#ifdef USE_LIGHT_PALETTE - if (Light.palette_count && XdrvMailbox.index == 2) { - LightSetPaletteEntry(); - } - else { -#endif - uint32_t old_bri = light_state.getBri(); - - light_controller.changeChannels(Light.entry_color); - if (2 == XdrvMailbox.index) { - - light_controller.changeBri(old_bri); - } -#ifdef USE_LIGHT_PALETTE - } -#endif - Settings.light_scheme = 0; -#ifdef USE_DEVICE_GROUPS - LightUpdateScheme(); -#endif - coldim = true; - } else { - for (uint32_t i = 0; i < LST_RGB; i++) { - Settings.ws_color[XdrvMailbox.index -3][i] = Light.entry_color[i]; - } - } - } - } - char scolor[LIGHT_COLOR_SIZE]; - if (!valid_entry && (XdrvMailbox.index <= 2)) { - ResponseCmndChar(LightGetColor(scolor)); - } - if (XdrvMailbox.index >= 3) { - scolor[0] = '\0'; - for (uint32_t i = 0; i < LST_RGB; i++) { - if (Settings.flag.decimal_text) { - snprintf_P(scolor, sizeof(scolor), PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Settings.ws_color[XdrvMailbox.index -3][i]); - } else { - snprintf_P(scolor, sizeof(scolor), PSTR("%s%02X"), scolor, Settings.ws_color[XdrvMailbox.index -3][i]); - } - } - ResponseCmndIdxChar(scolor); - } - if (coldim) { - LightPreparePower(); - } -} - -void CmndColor(void) -{ - - - - - - - - if ((Light.subtype > LST_SINGLE) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 6)) { - CmndSupportColor(); - } -} - -void CmndWhite(void) -{ - - - if (Light.pwm_multi_channels) { return; } - if ( ((Light.subtype >= LST_RGBW) || (LST_COLDWARM == Light.subtype)) && (XdrvMailbox.index == 1)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - light_controller.changeDimmer(XdrvMailbox.payload, 2); - LightPreparePower(2); - } else { - ResponseCmndNumber(light_state.getDimmer(2)); - } - } -} - -void CmndChannel(void) -{ - - - - - if ((XdrvMailbox.index >= Light.device) && (XdrvMailbox.index < Light.device + Light.subtype )) { - uint32_t light_index = XdrvMailbox.index - Light.device; - power_t coldim = 0; - - - if (1 == XdrvMailbox.data_len) { - uint8_t channel = changeUIntScale(Light.current_color[light_index],0,255,0,100); - if ('+' == XdrvMailbox.data[0]) { - XdrvMailbox.payload = (channel > 89) ? 100 : channel + 10; - } else if ('-' == XdrvMailbox.data[0]) { - XdrvMailbox.payload = (channel < 11) ? 1 : channel - 10; - } - } - - - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - Light.current_color[light_index] = changeUIntScale(XdrvMailbox.payload,0,100,0,255); - if (Light.pwm_multi_channels) { - coldim = 1 << light_index; - } else { - if (light_controller.isCTRGBLinked()) { - - if ((light_index < 3) && (light_controller.isCTRGBLinked())) { - Light.current_color[3] = Light.current_color[4] = 0; - } else { - Light.current_color[0] = Light.current_color[1] = Light.current_color[2] = 0; - } - coldim = 1; - } else { - if (light_index < 3) { coldim = 1; } - else { coldim = 2; } - } - } - light_controller.changeChannels(Light.current_color); - } - ResponseCmndIdxNumber(changeUIntScale(Light.current_color[light_index],0,255,0,100)); - if (coldim) { - LightPreparePower(coldim); - } - } -} - -void CmndHsbColor(void) -{ - - - - - - - - if (Light.subtype >= LST_RGB) { - if (XdrvMailbox.data_len > 0) { - uint16_t c_hue; - uint8_t c_sat; - light_state.getHSB(&c_hue, &c_sat, nullptr); - uint32_t HSB[3]; - HSB[0] = c_hue; - HSB[1] = c_sat; - HSB[2] = light_state.getBriRGB(); - if ((2 == XdrvMailbox.index) || (3 == XdrvMailbox.index)) { - if ((uint32_t)XdrvMailbox.payload > 100) { XdrvMailbox.payload = 100; } - HSB[XdrvMailbox.index-1] = changeUIntScale(XdrvMailbox.payload, 0, 100, 0, 255); - } else { - uint32_t paramcount = ParseParameters(3, HSB); - if (HSB[0] > 360) { HSB[0] = 360; } - for (uint32_t i = 1; i < paramcount; i++) { - if (HSB[i] > 100) { HSB[i] == 100; } - HSB[i] = changeUIntScale(HSB[i], 0, 100, 0, 255); - } - } - light_controller.changeHSB(HSB[0], HSB[1], HSB[2]); - LightPreparePower(1); - } else { - LightState(0); - } - } -} - -void CmndScheme(void) -{ - - - - - - if (Light.subtype >= LST_RGB) { - uint32_t max_scheme = Light.max_scheme; - - if (1 == XdrvMailbox.data_len) { - if (('+' == XdrvMailbox.data[0]) && (Settings.light_scheme < max_scheme)) { - XdrvMailbox.payload = Settings.light_scheme + ((0 == Settings.light_scheme) ? 2 : 1); - } - else if (('-' == XdrvMailbox.data[0]) && (Settings.light_scheme > 0)) { - XdrvMailbox.payload = Settings.light_scheme - ((2 == Settings.light_scheme) ? 2 : 1); - } - } - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= max_scheme)) { - uint32_t parm[2]; - if (ParseParameters(2, parm) > 1) { - Light.wheel = parm[1]; -#ifdef USE_LIGHT_PALETTE - Light.wheel--; -#endif - } - Settings.light_scheme = XdrvMailbox.payload; -#ifdef USE_DEVICE_GROUPS - LightUpdateScheme(); -#endif - if (LS_WAKEUP == Settings.light_scheme) { - Light.wakeup_active = 3; - } - LightPowerOn(); - Light.strip_timer_counter = 0; - - if (Settings.flag3.hass_tele_on_power) { - MqttPublishTeleState(); - } - } - ResponseCmndNumber(Settings.light_scheme); - } -} - -void CmndWakeup(void) -{ - - - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - light_controller.changeDimmer(XdrvMailbox.payload); - } - Light.wakeup_active = 3; - Settings.light_scheme = LS_WAKEUP; -#ifdef USE_DEVICE_GROUPS - LightUpdateScheme(); -#endif - LightPowerOn(); - ResponseCmndChar(D_JSON_STARTED); -} - -void CmndColorTemperature(void) -{ - - - - - if (Light.pwm_multi_channels) { return; } - if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { - uint32_t ct = light_state.getCT(); - if (1 == XdrvMailbox.data_len) { - if ('+' == XdrvMailbox.data[0]) { - XdrvMailbox.payload = (ct > (CT_MAX-34)) ? CT_MAX : ct + 34; - } - else if ('-' == XdrvMailbox.data[0]) { - XdrvMailbox.payload = (ct < (CT_MIN+34)) ? CT_MIN : ct - 34; - } - } - if ((XdrvMailbox.payload >= CT_MIN) && (XdrvMailbox.payload <= CT_MAX)) { - light_controller.changeCTB(XdrvMailbox.payload, light_state.getBriCT()); - LightPreparePower(2); - } else { - ResponseCmndNumber(ct); - } - } -} - -void CmndDimmer(void) -{ - - - - - - - - uint32_t dimmer; - if (XdrvMailbox.index == 3) { - skip_light_fade = true; - XdrvMailbox.index = 0; - } - else if (XdrvMailbox.index > 2) { - XdrvMailbox.index = 1; - } - - if ((light_controller.isCTRGBLinked()) || (0 == XdrvMailbox.index)) { - dimmer = light_state.getDimmer(); - } else { - dimmer = light_state.getDimmer(XdrvMailbox.index); - } - - if (1 == XdrvMailbox.data_len) { - if ('+' == XdrvMailbox.data[0]) { - XdrvMailbox.payload = (dimmer > 89) ? 100 : dimmer + 10; - } else if ('-' == XdrvMailbox.data[0]) { - XdrvMailbox.payload = (dimmer < 11) ? 1 : dimmer - 10; - } - } - - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - if (light_controller.isCTRGBLinked()) { - - light_controller.changeDimmer(XdrvMailbox.payload); - LightPreparePower(); - } else { - if (0 != XdrvMailbox.index) { - light_controller.changeDimmer(XdrvMailbox.payload, XdrvMailbox.index); - LightPreparePower(1 << (XdrvMailbox.index - 1)); - } else { - - light_controller.changeDimmer(XdrvMailbox.payload, 1); - light_controller.changeDimmer(XdrvMailbox.payload, 2); - LightPreparePower(); - } - } -#if defined(USE_PWM_DIMMER) && defined(USE_DEVICE_GROUPS) - Settings.bri_power_on = light_state.getBri();; - SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_BRI_POWER_ON, Settings.bri_power_on); -#endif - Light.update = true; - if (skip_light_fade) LightAnimate(); - } else { - ResponseCmndNumber(dimmer); - } - skip_light_fade = false; -} - -void CmndDimmerRange(void) -{ - - - if (XdrvMailbox.data_len > 0) { - uint32_t parm[2]; - parm[0] = Settings.dimmer_hw_min; - parm[1] = Settings.dimmer_hw_max; - ParseParameters(2, parm); - if (parm[0] < parm[1]) { - Settings.dimmer_hw_min = parm[0]; - Settings.dimmer_hw_max = parm[1]; - } else { - Settings.dimmer_hw_min = parm[1]; - Settings.dimmer_hw_max = parm[0]; - } - LightCalcPWMRange(); - Light.update = true; - } - Response_P(PSTR("{\"" D_CMND_DIMMER_RANGE "\":{\"Min\":%d,\"Max\":%d}}"), Settings.dimmer_hw_min, Settings.dimmer_hw_max); -} - -void CmndLedTable(void) -{ - - - - - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { - switch (XdrvMailbox.payload) { - case 0: - case 1: - Settings.light_correction = XdrvMailbox.payload; - break; - case 2: - Settings.light_correction ^= 1; - break; - } - LightCalcPWMRange(); - Light.update = true; - } - ResponseCmndStateText(Settings.light_correction); -} - -void CmndRgbwwTable(void) -{ - - - if ((XdrvMailbox.data_len > 0)) { - uint32_t parm[LST_RGBCW -1]; - uint32_t parmcount = ParseParameters(LST_RGBCW, parm); - for (uint32_t i = 0; i < parmcount; i++) { - Settings.rgbwwTable[i] = parm[i]; - } - Light.update = true; - } - char scolor[LIGHT_COLOR_SIZE]; - scolor[0] = '\0'; - for (uint32_t i = 0; i < LST_RGBCW; i++) { - snprintf_P(scolor, sizeof(scolor), PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Settings.rgbwwTable[i]); - } - ResponseCmndChar(scolor); -} - -void CmndFade(void) -{ - - - - - switch (XdrvMailbox.payload) { - case 0: - case 1: - Settings.light_fade = XdrvMailbox.payload; - break; - case 2: - Settings.light_fade ^= 1; - break; - } -#ifdef USE_DEVICE_GROUPS - if (XdrvMailbox.payload >= 0 && XdrvMailbox.payload <= 2) SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_FADE, Settings.light_fade); -#endif -#ifdef USE_LIGHT - if (!Settings.light_fade) { Light.fade_running = false; } -#endif - ResponseCmndStateText(Settings.light_fade); -} - -void CmndSpeed(void) -{ - - - - - if (1 == XdrvMailbox.data_len) { - if (('+' == XdrvMailbox.data[0]) && (Settings.light_speed > 1)) { - XdrvMailbox.payload = Settings.light_speed - 1; - } - else if (('-' == XdrvMailbox.data[0]) && (Settings.light_speed < 40)) { - XdrvMailbox.payload = Settings.light_speed + 1; - } - } - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 40)) { - Settings.light_speed = XdrvMailbox.payload; -#ifdef USE_DEVICE_GROUPS - SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_SPEED, Settings.light_speed); -#endif - } - ResponseCmndNumber(Settings.light_speed); -} - -void CmndWakeupDuration(void) -{ - - - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 3001)) { - Settings.light_wakeup = XdrvMailbox.payload; - Light.wakeup_active = 0; - } - ResponseCmndNumber(Settings.light_wakeup); -} - -#ifdef USE_LIGHT_PALETTE -void CmndPalette(void) -{ - uint8_t * palette_entry; - char * p; - - - if (XdrvMailbox.data_len) { - Light.wheel = 0; - Light.palette_count = 0; - if (Light.palette) { - free(Light.palette); - Light.palette = nullptr; - } - if (XdrvMailbox.data_len > 1 || XdrvMailbox.data[0] != '0') { - uint8_t palette_count = 0; - char * color = XdrvMailbox.data; - if (!(Light.palette = (uint8_t *)malloc(255 * Light.subtype))) return; - palette_entry = Light.palette; - for (;;) { - p = strchr(color, ' '); - if (p) *p = 0; - color = Trim(color); - if (*color && LightColorEntry(color, strlen(color))) { - memcpy(palette_entry, Light.entry_color, Light.subtype); - palette_entry += Light.subtype; - palette_count++; - } - if (!p) break; - color = p + 1; - } - if (!(Light.palette = (uint8_t *)realloc(Light.palette, palette_count * Light.subtype))) return; - Light.palette_count = palette_count; - } - } - - char palette_str[5 * Light.subtype * Light.palette_count + 3]; - p = palette_str; - *p++ = '['; - if (Light.palette_count) { - palette_entry = Light.palette; - for (int entry = 0; entry < Light.palette_count; entry++) { - if (Settings.flag.decimal_text) { - *p++ = '"'; - } - memcpy(Light.current_color, palette_entry, Light.subtype); - LightGetColor(p); - p += strlen(p); - if (Settings.flag.decimal_text) { - *p++ = '"'; - } - *p++ = ','; - } - p--; - } - *p++ = ']'; - *p = 0; - ResponseCmndChar(palette_str); -} -#endif - -void CmndUndocA(void) -{ - - char scolor[LIGHT_COLOR_SIZE]; - LightGetColor(scolor, true); - scolor[6] = '\0'; - Response_P(PSTR("%s,%d,%d,%d,%d,%d"), scolor, Settings.light_fade, Settings.light_correction, Settings.light_scheme, Settings.light_speed, Settings.light_width); - MqttPublishPrefixTopic_P(STAT, XdrvMailbox.topic); - mqtt_data[0] = '\0'; -} - - - - - -bool Xdrv04(uint8_t function) -{ - bool result = false; - - if (FUNC_MODULE_INIT == function) { - return LightModuleInit(); - } - else if (light_type) { - switch (function) { - case FUNC_SERIAL: - result = XlgtCall(FUNC_SERIAL); - break; - case FUNC_LOOP: - if (Light.fade_running) { - if (LightApplyFade()) { - LightSetOutputs(Light.fade_cur_10); - } - } - break; - case FUNC_EVERY_50_MSECOND: - LightAnimate(); - break; -#ifdef USE_DEVICE_GROUPS - case FUNC_DEVICE_GROUP_ITEM: - LightHandleDevGroupItem(); - break; -#endif - case FUNC_SET_POWER: - LightSetPower(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kLightCommands, LightCommand); - if (!result) { - result = XlgtCall(FUNC_COMMAND); - } - break; - case FUNC_PRE_INIT: - LightInit(); - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_05_irremote.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_05_irremote.ino" -#if defined(USE_IR_REMOTE) && !defined(USE_IR_REMOTE_FULL) - - - - -#define XDRV_05 5 - -#include - -enum IrErrors { IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND }; - -const char kIrRemoteCommands[] PROGMEM = "|" D_CMND_IRSEND ; - - -void (* const IrRemoteCommand[])(void) PROGMEM = { - &CmndIrSend }; - - -static const uint8_t MAX_STANDARD_IR = NEC; -const char kIrRemoteProtocols[] PROGMEM = "UNKNOWN|RC5|RC6|NEC"; - - - - - -#include - -IRsend *irsend = nullptr; -bool irsend_active = false; - -void IrSendInit(void) -{ - irsend = new IRsend(pin[GPIO_IRSEND]); - irsend->begin(); -} - -#ifdef USE_IR_RECEIVE - - - - -const bool IR_RCV_SAVE_BUFFER = false; -const uint32_t IR_TIME_AVOID_DUPLICATE = 500; - -#include - -IRrecv *irrecv = nullptr; - -unsigned long ir_lasttime = 0; - -void IrReceiveUpdateThreshold(void) -{ - if (irrecv != nullptr) { - if (Settings.param[P_IR_UNKNOW_THRESHOLD] < 6) { Settings.param[P_IR_UNKNOW_THRESHOLD] = 6; } - irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); - } -} - -void IrReceiveInit(void) -{ - - irrecv = new IRrecv(pin[GPIO_IRRECV], IR_RCV_BUFFER_SIZE, IR_RCV_TIMEOUT, IR_RCV_SAVE_BUFFER); - irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); - irrecv->enableIRIn(); - - -} - -void IrReceiveCheck(void) -{ - char sirtype[8]; - int8_t iridx = 0; - - decode_results results; - - if (irrecv->decode(&results)) { - char hvalue[65]; - - iridx = results.decode_type; - if ((iridx < 0) || (iridx > MAX_STANDARD_IR)) { iridx = 0; } - - if (iridx) { - if (results.bits > 64) { - - uint32_t digits2 = results.bits / 8; - if (results.bits % 8) { digits2++; } - ToHex_P((unsigned char*)results.state, digits2, hvalue, sizeof(hvalue)); - } else { - Uint64toHex(results.value, hvalue, results.bits); - } - } else { - Uint64toHex(results.value, hvalue, 32); - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_IRR "Echo %d, RawLen %d, Overflow %d, Bits %d, Value 0x%s, Decode %d"), - irsend_active, results.rawlen, results.overflow, results.bits, hvalue, results.decode_type); - - unsigned long now = millis(); - - if (!irsend_active && (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE)) { - ir_lasttime = now; - - char svalue[64]; - if (Settings.flag.ir_receive_decimal) { - ulltoa(results.value, svalue, 10); - } else { - snprintf_P(svalue, sizeof(svalue), PSTR("\"0x%s\""), hvalue); - } - ResponseTime_P(PSTR(",\"" D_JSON_IRRECEIVED "\":{\"" D_JSON_IR_PROTOCOL "\":\"%s\",\"" D_JSON_IR_BITS "\":%d"), - GetTextIndexed(sirtype, sizeof(sirtype), iridx, kIrRemoteProtocols), results.bits); - if (iridx) { - ResponseAppend_P(PSTR(",\"" D_JSON_IR_DATA "\":%s"), svalue); - } else { - ResponseAppend_P(PSTR(",\"" D_JSON_IR_HASH "\":%s"), svalue); - } - - if (Settings.flag3.receive_raw) { - ResponseAppend_P(PSTR(",\"" D_JSON_IR_RAWDATA "\":[")); - uint16_t i; - for (i = 1; i < results.rawlen; i++) { - if (i > 1) { ResponseAppend_P(PSTR(",")); } - uint32_t usecs; - for (usecs = results.rawbuf[i] * kRawTick; usecs > UINT16_MAX; usecs -= UINT16_MAX) { - ResponseAppend_P(PSTR("%d,0,"), UINT16_MAX); - } - ResponseAppend_P(PSTR("%d"), usecs); - if (strlen(mqtt_data) > sizeof(mqtt_data) - 40) { break; } - } - uint16_t extended_length = results.rawlen - 1; - for (uint32_t j = 0; j < results.rawlen - 1; j++) { - uint32_t usecs = results.rawbuf[j] * kRawTick; - - extended_length += (usecs / (UINT16_MAX + 1)) * 2; - } - ResponseAppend_P(PSTR("],\"" D_JSON_IR_RAWDATA "Info\":[%d,%d,%d]"), extended_length, i -1, results.overflow); - } - - ResponseJsonEndEnd(); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED)); - - XdrvRulesProcess(); -#ifdef USE_DOMOTICZ - if (iridx) { - unsigned long value = results.value | (iridx << 28); - DomoticzSensor(DZ_COUNT, value); - } -#endif - } - - irrecv->resume(); - } -} -#endif - - - - - -uint32_t IrRemoteCmndIrSendJson(void) -{ - - - - - char dataBufUc[XdrvMailbox.data_len + 1]; - UpperCase(dataBufUc, XdrvMailbox.data); - RemoveSpace(dataBufUc); - if (strlen(dataBufUc) < 8) { - return IE_INVALID_JSON; - } - - StaticJsonBuffer<140> jsonBuf; - JsonObject &root = jsonBuf.parseObject(dataBufUc); - if (!root.success()) { - return IE_INVALID_JSON; - } - - - - char parm_uc[10]; - const char *protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_PROTOCOL))]; - uint16_t bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS))]; - uint64_t data = strtoull(root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA))], nullptr, 0); - uint16_t repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT))]; - - if (XdrvMailbox.index > repeat + 1) { - repeat = XdrvMailbox.index - 1; - } - if (!(protocol && bits)) { - return IE_SYNTAX_IRSEND; - } - - char protocol_text[20]; - int protocol_code = GetCommandCode(protocol_text, sizeof(protocol_text), protocol, kIrRemoteProtocols); - - char dvalue[64]; - char hvalue[20]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol_text %s, protocol %s, bits %d, data %s (0x%s), repeat %d, protocol_code %d"), - protocol_text, protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat, protocol_code); - - irsend_active = true; - switch (protocol_code) { -#ifdef USE_IR_SEND_RC5 - case RC5: - irsend->sendRC5(data, bits, repeat); break; -#endif -#ifdef USE_IR_SEND_RC6 - case RC6: - irsend->sendRC6(data, bits, repeat); break; -#endif -#ifdef USE_IR_SEND_NEC - case NEC: - irsend->sendNEC(data, (bits > NEC_BITS) ? NEC_BITS : bits, repeat); break; -#endif - default: - irsend_active = false; - ResponseCmndChar(D_JSON_PROTOCOL_NOT_SUPPORTED); - } - - return IE_NO_ERROR; -} - -void CmndIrSend(void) -{ - uint8_t error = IE_SYNTAX_IRSEND; - - if (XdrvMailbox.data_len) { - - if (strstr(XdrvMailbox.data, "{") == nullptr) { - error = IE_INVALID_JSON; - } else { - error = IrRemoteCmndIrSendJson(); - } - } - IrRemoteCmndResponse(error); -} - -void IrRemoteCmndResponse(uint32_t error) -{ - switch (error) { - case IE_INVALID_RAWDATA: - ResponseCmndChar_P(PSTR(D_JSON_INVALID_RAWDATA)); - break; - case IE_INVALID_JSON: - ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); - break; - case IE_SYNTAX_IRSEND: - Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_NO " " D_JSON_IR_PROTOCOL ", " D_JSON_IR_BITS " " D_JSON_OR " " D_JSON_IR_DATA "\"}")); - break; - default: - ResponseCmndDone(); - } -} - - - - - -bool Xdrv05(uint8_t function) -{ - bool result = false; - - if ((pin[GPIO_IRSEND] < 99) || (pin[GPIO_IRRECV] < 99)) { - switch (function) { - case FUNC_PRE_INIT: - if (pin[GPIO_IRSEND] < 99) { - IrSendInit(); - } -#ifdef USE_IR_RECEIVE - if (pin[GPIO_IRRECV] < 99) { - IrReceiveInit(); - } -#endif - break; - case FUNC_EVERY_50_MSECOND: -#ifdef USE_IR_RECEIVE - if (pin[GPIO_IRRECV] < 99) { - IrReceiveCheck(); - } -#endif - irsend_active = false; - break; - case FUNC_COMMAND: - if (pin[GPIO_IRSEND] < 99) { - result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand); - } - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_05_irremote_full.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_05_irremote_full.ino" -#ifdef USE_IR_REMOTE_FULL - - - - -#define XDRV_05 5 - -#include -#include -#include -#include -#include - -enum IrErrors { IE_RESPONSE_PROVIDED, IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND, IE_SYNTAX_IRHVAC, - IE_UNSUPPORTED_HVAC, IE_UNSUPPORTED_PROTOCOL }; - -const char kIrRemoteCommands[] PROGMEM = "|" - D_CMND_IRHVAC "|" D_CMND_IRSEND ; - -void (* const IrRemoteCommand[])(void) PROGMEM = { - &CmndIrHvac, &CmndIrSend }; - - - - - -IRsend *irsend = nullptr; -bool irsend_active = false; - -void IrSendInit(void) -{ - irsend = new IRsend(pin[GPIO_IRSEND]); - irsend->begin(); -} - - - -uint8_t reverseBitsInByte(uint8_t b) { - b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; - b = (b & 0xCC) >> 2 | (b & 0x33) << 2; - b = (b & 0xAA) >> 1 | (b & 0x55) << 1; - return b; -} - - -uint64_t reverseBitsInBytes64(uint64_t b) { - union { - uint8_t b[8]; - uint64_t i; - } a; - a.i = b; - for (uint32_t i=0; i<8; i++) { - a.b[i] = reverseBitsInByte(a.b[i]); - } - return a.i; -} - - - - - -const bool IR_FULL_RCV_SAVE_BUFFER = false; -const uint32_t IR_TIME_AVOID_DUPLICATE = 500; - - - - -const uint16_t IR_FULL_BUFFER_SIZE = 1024; - - - -const uint8_t IR__FULL_RCV_TIMEOUT = 50; - -IRrecv *irrecv = nullptr; - -unsigned long ir_lasttime = 0; - -void IrReceiveUpdateThreshold(void) -{ - if (irrecv != nullptr) { - if (Settings.param[P_IR_UNKNOW_THRESHOLD] < 6) { Settings.param[P_IR_UNKNOW_THRESHOLD] = 6; } - irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); - } -} - -void IrReceiveInit(void) -{ - - irrecv = new IRrecv(pin[GPIO_IRRECV], IR_FULL_BUFFER_SIZE, IR__FULL_RCV_TIMEOUT, IR_FULL_RCV_SAVE_BUFFER); - irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); - irrecv->enableIRIn(); -} - -String sendACJsonState(const stdAc::state_t &state) { - DynamicJsonBuffer jsonBuffer; - JsonObject& json = jsonBuffer.createObject(); - json[D_JSON_IRHVAC_VENDOR] = typeToString(state.protocol); - json[D_JSON_IRHVAC_MODEL] = state.model; - json[D_JSON_IRHVAC_POWER] = IRac::boolToString(state.power); - json[D_JSON_IRHVAC_MODE] = IRac::opmodeToString(state.mode); - - if (state.mode == stdAc::opmode_t::kOff || !state.power) { - json[D_JSON_IRHVAC_MODE] = IRac::opmodeToString(stdAc::opmode_t::kOff); - json[D_JSON_IRHVAC_POWER] = IRac::boolToString(false); - } - json[D_JSON_IRHVAC_CELSIUS] = IRac::boolToString(state.celsius); - if (floorf(state.degrees) == state.degrees) { - json[D_JSON_IRHVAC_TEMP] = floorf(state.degrees); - } else { - json[D_JSON_IRHVAC_TEMP] = RawJson(String(state.degrees, 1)); - } - json[D_JSON_IRHVAC_FANSPEED] = IRac::fanspeedToString(state.fanspeed); - json[D_JSON_IRHVAC_SWINGV] = IRac::swingvToString(state.swingv); - json[D_JSON_IRHVAC_SWINGH] = IRac::swinghToString(state.swingh); - json[D_JSON_IRHVAC_QUIET] = IRac::boolToString(state.quiet); - json[D_JSON_IRHVAC_TURBO] = IRac::boolToString(state.turbo); - json[D_JSON_IRHVAC_ECONO] = IRac::boolToString(state.econo); - json[D_JSON_IRHVAC_LIGHT] = IRac::boolToString(state.light); - json[D_JSON_IRHVAC_FILTER] = IRac::boolToString(state.filter); - json[D_JSON_IRHVAC_CLEAN] = IRac::boolToString(state.clean); - json[D_JSON_IRHVAC_BEEP] = IRac::boolToString(state.beep); - json[D_JSON_IRHVAC_SLEEP] = state.sleep; - - String payload = ""; - payload.reserve(200); - json.printTo(payload); - return payload; -} - -String sendIRJsonState(const struct decode_results &results) { - String json("{"); - json += "\"" D_JSON_IR_PROTOCOL "\":\""; - json += typeToString(results.decode_type); - json += "\",\"" D_JSON_IR_BITS "\":"; - json += results.bits; - - if (hasACState(results.decode_type)) { - json += ",\"" D_JSON_IR_DATA "\":\"0x"; - json += resultToHexidecimal(&results); - json += "\""; - } else { - if (UNKNOWN != results.decode_type) { - json += ",\"" D_JSON_IR_DATA "\":"; - } else { - json += ",\"" D_JSON_IR_HASH "\":"; - } - if (Settings.flag.ir_receive_decimal) { - char svalue[32]; - ulltoa(results.value, svalue, 10); - json += svalue; - } else { - char hvalue[64]; - if (UNKNOWN != results.decode_type) { - Uint64toHex(results.value, hvalue, results.bits); - json += "\"0x"; - json += hvalue; - json += "\",\"" D_JSON_IR_DATALSB "\":\"0x"; - Uint64toHex(reverseBitsInBytes64(results.value), hvalue, results.bits); - json += hvalue; - json += "\""; - } else { - Uint64toHex(results.value, hvalue, 32); - json += "\"0x"; - json += hvalue; - json += "\""; - } - } - } - json += ",\"" D_JSON_IR_REPEAT "\":"; - json += results.repeat; - - stdAc::state_t ac_result; - if (IRAcUtils::decodeToState(&results, &ac_result, nullptr)) { - - json += ",\"" D_CMND_IRHVAC "\":"; - json += sendACJsonState(ac_result); - } - - return json; -} - -void IrReceiveCheck(void) -{ - decode_results results; - - if (irrecv->decode(&results)) { - uint32_t now = millis(); - - - if (!irsend_active && (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE)) { - ir_lasttime = now; - Response_P(PSTR("{\"" D_JSON_IRRECEIVED "\":%s"), sendIRJsonState(results).c_str()); - - if (Settings.flag3.receive_raw) { - ResponseAppend_P(PSTR(",\"" D_JSON_IR_RAWDATA "\":[")); - uint16_t i; - for (i = 1; i < results.rawlen; i++) { - if (i > 1) { ResponseAppend_P(PSTR(",")); } - uint32_t usecs; - for (usecs = results.rawbuf[i] * kRawTick; usecs > UINT16_MAX; usecs -= UINT16_MAX) { - ResponseAppend_P(PSTR("%d,0,"), UINT16_MAX); - } - ResponseAppend_P(PSTR("%d"), usecs); - if (strlen(mqtt_data) > sizeof(mqtt_data) - 40) { break; } - } - uint16_t extended_length = results.rawlen - 1; - for (uint32_t j = 0; j < results.rawlen - 1; j++) { - uint32_t usecs = results.rawbuf[j] * kRawTick; - - extended_length += (usecs / (UINT16_MAX + 1)) * 2; - } - ResponseAppend_P(PSTR("],\"" D_JSON_IR_RAWDATA "Info\":[%d,%d,%d]"), extended_length, i -1, results.overflow); - } - - ResponseJsonEndEnd(); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED)); - - XdrvRulesProcess(); - } - - irrecv->resume(); - } -} - - - - - - - -String listSupportedProtocols(bool hvac) { - String l(""); - bool first = true; - for (uint32_t i = UNUSED + 1; i <= kLastDecodeType; i++) { - bool found = false; - if (hvac) { - found = IRac::isProtocolSupported((decode_type_t)i); - } else { - found = (IRsend::defaultBits((decode_type_t)i) > 0) && (!IRac::isProtocolSupported((decode_type_t)i)); - } - if (found) { - if (first) { - first = false; - } else { - l += "|"; - } - l += typeToString((decode_type_t)i); - } - } - return l; -} - - -const stdAc::fanspeed_t IrHvacFanSpeed[] PROGMEM = { stdAc::fanspeed_t::kAuto, - stdAc::fanspeed_t::kMin, stdAc::fanspeed_t::kLow,stdAc::fanspeed_t::kMedium, - stdAc::fanspeed_t::kHigh, stdAc::fanspeed_t::kMax }; - -uint32_t IrRemoteCmndIrHvacJson(void) -{ - stdAc::state_t state, prev; - char parm_uc[12]; - - - char dataBufUc[XdrvMailbox.data_len + 1]; - UpperCase(dataBufUc, XdrvMailbox.data); - RemoveSpace(dataBufUc); - if (strlen(dataBufUc) < 8) { return IE_INVALID_JSON; } - - DynamicJsonBuffer jsonBuf; - JsonObject &json = jsonBuf.parseObject(dataBufUc); - if (!json.success()) { return IE_INVALID_JSON; } - - - state.protocol = decode_type_t::UNKNOWN; - state.model = 1; - state.mode = stdAc::opmode_t::kAuto; - state.power = false; - state.celsius = true; - state.degrees = 21.0f; - state.fanspeed = stdAc::fanspeed_t::kMedium; - state.swingv = stdAc::swingv_t::kOff; - state.swingh = stdAc::swingh_t::kOff; - state.light = false; - state.beep = false; - state.econo = false; - state.filter = false; - state.turbo = false; - state.quiet = false; - state.sleep = -1; - state.clean = false; - state.clock = -1; - - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_VENDOR)); - if (json.containsKey(parm_uc)) { state.protocol = strToDecodeType(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_PROTOCOL)); - if (json.containsKey(parm_uc)) { state.protocol = strToDecodeType(json[parm_uc]); } - if (decode_type_t::UNKNOWN == state.protocol) { return IE_UNSUPPORTED_HVAC; } - if (!IRac::isProtocolSupported(state.protocol)) { return IE_UNSUPPORTED_HVAC; } - - - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_FANSPEED)); - if (json.containsKey(parm_uc)) { - uint32_t fan_speed = json[parm_uc]; - if ((fan_speed >= 1) && (fan_speed <= 5)) { - state.fanspeed = (stdAc::fanspeed_t) pgm_read_byte(&IrHvacFanSpeed[fan_speed]); - } else { - state.fanspeed = IRac::strToFanspeed(json[parm_uc]); - } - } - - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_MODEL)); - if (json.containsKey(parm_uc)) { state.model = IRac::strToModel(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_MODE)); - if (json.containsKey(parm_uc)) { state.mode = IRac::strToOpmode(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_SWINGV)); - if (json.containsKey(parm_uc)) { state.swingv = IRac::strToSwingV(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_SWINGH)); - if (json.containsKey(parm_uc)) { state.swingh = IRac::strToSwingH(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_TEMP)); - if (json.containsKey(parm_uc)) { state.degrees = json[parm_uc]; } - - - - - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_POWER)); - if (json.containsKey(parm_uc)) { state.power = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_CELSIUS)); - if (json.containsKey(parm_uc)) { state.celsius = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_LIGHT)); - if (json.containsKey(parm_uc)) { state.light = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_BEEP)); - if (json.containsKey(parm_uc)) { state.beep = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_ECONO)); - if (json.containsKey(parm_uc)) { state.econo = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_FILTER)); - if (json.containsKey(parm_uc)) { state.filter = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_TURBO)); - if (json.containsKey(parm_uc)) { state.turbo = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_QUIET)); - if (json.containsKey(parm_uc)) { state.quiet = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_CLEAN)); - if (json.containsKey(parm_uc)) { state.clean = IRac::strToBool(json[parm_uc]); } - - - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_SLEEP)); - if (json[parm_uc]) { state.sleep = json[parm_uc]; } - - - IRac ac(pin[GPIO_IRSEND]); - bool success = ac.sendAc(state, &prev); - if (!success) { return IE_SYNTAX_IRHVAC; } - - Response_P(PSTR("{\"" D_CMND_IRHVAC "\":%s}"), sendACJsonState(state).c_str()); - return IE_RESPONSE_PROVIDED; -} - -void CmndIrHvac(void) -{ - uint8_t error = IE_SYNTAX_IRHVAC; - - if (XdrvMailbox.data_len) { - error = IrRemoteCmndIrHvacJson(); - } - if (error != IE_RESPONSE_PROVIDED) { IrRemoteCmndResponse(error); } -} - - - - - -uint32_t IrRemoteCmndIrSendJson(void) -{ - char parm_uc[12]; - - - - char dataBufUc[XdrvMailbox.data_len + 1]; - UpperCase(dataBufUc, XdrvMailbox.data); - RemoveSpace(dataBufUc); - if (strlen(dataBufUc) < 8) { return IE_INVALID_JSON; } - - DynamicJsonBuffer jsonBuf; - JsonObject &json = jsonBuf.parseObject(dataBufUc); - if (!json.success()) { return IE_INVALID_JSON; } - - - - decode_type_t protocol = decode_type_t::UNKNOWN; - uint16_t bits = 0; - uint64_t data; - uint8_t repeat = 0; - - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_VENDOR)); - if (json.containsKey(parm_uc)) { protocol = strToDecodeType(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_PROTOCOL)); - if (json.containsKey(parm_uc)) { protocol = strToDecodeType(json[parm_uc]); } - if (decode_type_t::UNKNOWN == protocol) { return IE_UNSUPPORTED_PROTOCOL; } - - UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS)); - if (json.containsKey(parm_uc)) { bits = json[parm_uc]; } - UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT)); - if (json.containsKey(parm_uc)) { repeat = json[parm_uc]; } - UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATALSB)); - if (json.containsKey(parm_uc)) { data = reverseBitsInBytes64(strtoull(json[parm_uc], nullptr, 0)); } - UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA)); - if (json.containsKey(parm_uc)) { data = strtoull(json[parm_uc], nullptr, 0); } - if (0 == bits) { return IE_SYNTAX_IRSEND; } - - - if (XdrvMailbox.index > repeat + 1) { repeat = XdrvMailbox.index - 1; } - - char dvalue[32]; - char hvalue[32]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol %d, bits %d, data 0x%s (%s), repeat %d"), - protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat); - - irsend_active = true; - bool success = irsend->send(protocol, data, bits, repeat); - - if (!success) { - irsend_active = false; - ResponseCmndChar(D_JSON_PROTOCOL_NOT_SUPPORTED); - } - return IE_NO_ERROR; -} - -uint32_t IrRemoteCmndIrSendRaw(void) -{ - - - - - - - - char *p; - char *str = strtok_r(XdrvMailbox.data, ", ", &p); - if (p == nullptr) { - return IE_INVALID_RAWDATA; - } - - - uint16_t repeat = XdrvMailbox.index > 0 ? XdrvMailbox.index - 1 : 0; - - uint16_t freq = atoi(str); - if (!freq && (*str != '0')) { - uint16_t count = 0; - char *q = p; - for (; *q; count += (*q++ == ',')); - if (count < 2) { - return IE_INVALID_RAWDATA; - } - - uint16_t parm[count]; - for (uint32_t i = 0; i < count; i++) { - parm[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); - if (!parm[i]) { - if (!i) { - parm[0] = 38000; - } else { - return IE_INVALID_RAWDATA; - } - } - } - - uint16_t i = 0; - if (count < 4) { - - uint16_t mark = parm[1] *2; - if (3 == count) { - if (parm[2] < parm[1]) { - - mark = parm[1] * parm[2]; - } else { - - mark = parm[2]; - } - } - uint16_t raw_array[strlen(p)]; - for (; *p; *p++) { - if (*p == '0') { - raw_array[i++] = parm[1]; - } - else if (*p == '1') { - raw_array[i++] = mark; - } - } - irsend_active = true; - for (uint32_t r = 0; r <= repeat; r++) { - irsend->sendRaw(raw_array, i, parm[0]); - if (r < repeat) { - irsend->space(40000); - } - } - } - else if (6 == count) { - - uint16_t raw_array[strlen(p)*2+3]; - raw_array[i++] = parm[1]; - raw_array[i++] = parm[2]; - uint32_t inter_message_32 = (parm[1] + parm[2]) * 3; - uint16_t inter_message = (inter_message_32 > 65000) ? 65000 : inter_message_32; - for (; *p; *p++) { - if (*p == '0') { - raw_array[i++] = parm[3]; - raw_array[i++] = parm[4]; - } - else if (*p == '1') { - raw_array[i++] = parm[3]; - raw_array[i++] = parm[5]; - } - } - raw_array[i++] = parm[3]; - irsend_active = true; - for (uint32_t r = 0; r <= repeat; r++) { - irsend->sendRaw(raw_array, i, parm[0]); - if (r < repeat) { - irsend->space(inter_message); - } - } - } - else { - return IE_INVALID_RAWDATA; - } - } else { - if (!freq) { freq = 38000; } - uint16_t count = 0; - char *q = p; - for (; *q; count += (*q++ == ',')); - if (0 == count) { - return IE_INVALID_RAWDATA; - } - - - count++; - if (count < 200) { - uint16_t raw_array[count]; - for (uint32_t i = 0; i < count; i++) { - raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); - } - - - - irsend_active = true; - for (uint32_t r = 0; r <= repeat; r++) { - irsend->sendRaw(raw_array, count, freq); - } - } else { - uint16_t *raw_array = reinterpret_cast(malloc(count * sizeof(uint16_t))); - if (raw_array == nullptr) { - return IE_INVALID_RAWDATA; - } - - for (uint32_t i = 0; i < count; i++) { - raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); - } - - - - irsend_active = true; - for (uint32_t r = 0; r <= repeat; r++) { - irsend->sendRaw(raw_array, count, freq); - } - free(raw_array); - } - } - - return IE_NO_ERROR; -} - -void CmndIrSend(void) -{ - uint8_t error = IE_SYNTAX_IRSEND; - - if (XdrvMailbox.data_len) { - if (strstr(XdrvMailbox.data, "{") == nullptr) { - error = IrRemoteCmndIrSendRaw(); - } else { - error = IrRemoteCmndIrSendJson(); - } - } - IrRemoteCmndResponse(error); -} - -void IrRemoteCmndResponse(uint32_t error) -{ - switch (error) { - case IE_INVALID_RAWDATA: - ResponseCmndChar_P(PSTR(D_JSON_INVALID_RAWDATA)); - break; - case IE_INVALID_JSON: - ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); - break; - case IE_SYNTAX_IRSEND: - Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_NO " " D_JSON_IR_BITS " " D_JSON_OR " " D_JSON_IR_DATA "\"}")); - break; - case IE_SYNTAX_IRHVAC: - Response_P(PSTR("{\"" D_CMND_IRHVAC "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_VENDOR ", " D_JSON_IRHVAC_MODE " " D_JSON_OR " " D_JSON_IRHVAC_FANSPEED "\"}")); - break; - case IE_UNSUPPORTED_HVAC: - Response_P(PSTR("{\"" D_CMND_IRHVAC "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_VENDOR " (%s)\"}"), listSupportedProtocols(true).c_str()); - break; - case IE_UNSUPPORTED_PROTOCOL: - Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_PROTOCOL " (%s)\"}"), listSupportedProtocols(false).c_str()); - break; - default: - ResponseCmndDone(); - } -} - - - - - -bool Xdrv05(uint8_t function) -{ - bool result = false; - - if ((pin[GPIO_IRSEND] < 99) || (pin[GPIO_IRRECV] < 99)) { - switch (function) { - case FUNC_PRE_INIT: - if (pin[GPIO_IRSEND] < 99) { - IrSendInit(); - } - if (pin[GPIO_IRRECV] < 99) { - IrReceiveInit(); - } - break; - case FUNC_EVERY_50_MSECOND: - if (pin[GPIO_IRRECV] < 99) { - IrReceiveCheck(); - } - irsend_active = false; - break; - case FUNC_COMMAND: - if (pin[GPIO_IRSEND] < 99) { - result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand); - } - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_06_snfbridge.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_06_snfbridge.ino" -#ifdef USE_SONOFF_RF - - - - -#define XDRV_06 6 - -const uint32_t SFB_TIME_AVOID_DUPLICATE = 2000; - -enum SonoffBridgeCommands { - CMND_RFSYNC, CMND_RFLOW, CMND_RFHIGH, CMND_RFHOST, CMND_RFCODE }; - -const char kSonoffBridgeCommands[] PROGMEM = "|" - D_CMND_RFSYNC "|" D_CMND_RFLOW "|" D_CMND_RFHIGH "|" D_CMND_RFHOST "|" D_CMND_RFCODE "|" D_CMND_RFKEY "|" D_CMND_RFRAW; - -void (* const SonoffBridgeCommand[])(void) PROGMEM = { - &CmndRfBridge, &CmndRfBridge, &CmndRfBridge, &CmndRfBridge, &CmndRfBridge, &CmndRfKey, &CmndRfRaw }; - -struct SONOFFBRIDGE { - uint32_t last_received_id = 0; - uint32_t last_send_code = 0; - uint32_t last_time = 0; - uint32_t last_learn_time = 0; - uint8_t receive_flag = 0; - uint8_t receive_raw_flag = 0; - uint8_t learn_key = 1; - uint8_t learn_active = 0; - uint8_t expected_bytes = 0; -} SnfBridge; - -#ifdef USE_RF_FLASH - - - - - - - -#include "ihx.h" -#include "c2.h" - -const ssize_t RF_RECORD_NO_START_FOUND = -1; -const ssize_t RF_RECORD_NO_END_FOUND = -2; - -ssize_t rf_find_hex_record_start(uint8_t *buf, size_t size) -{ - for (size_t i = 0; i < size; i++) { - if (buf[i] == ':') { - return i; - } - } - return RF_RECORD_NO_START_FOUND; -} - -ssize_t rf_find_hex_record_end(uint8_t *buf, size_t size) -{ - for (size_t i = 0; i < size; i++) { - if (buf[i] == '\n') { - return i; - } - } - return RF_RECORD_NO_END_FOUND; -} - -ssize_t rf_glue_remnant_with_new_data_and_write(const uint8_t *remnant_data, uint8_t *new_data, size_t new_data_len) -{ - ssize_t record_start; - ssize_t record_end; - ssize_t glue_record_sz; - uint8_t *glue_buf; - ssize_t result; - - if (remnant_data[0] != ':') { return -8; } - - - record_end = rf_find_hex_record_end(new_data, new_data_len); - record_start = rf_find_hex_record_start(new_data, new_data_len); - - - - - if ((record_start != RF_RECORD_NO_START_FOUND) && (record_start < record_end)) { - return -8; - } - - glue_record_sz = strlen((const char *) remnant_data) + record_end; - - glue_buf = (uint8_t *) malloc(glue_record_sz); - if (glue_buf == nullptr) { return -2; } - - - memcpy(glue_buf, remnant_data, strlen((const char *) remnant_data)); - memcpy(glue_buf + strlen((const char *) remnant_data), new_data, record_end); - - result = rf_decode_and_write(glue_buf, glue_record_sz); - free(glue_buf); - return result; -} - -ssize_t rf_decode_and_write(uint8_t *record, size_t size) -{ - uint8_t err = ihx_decode(record, size); - if (err != IHX_SUCCESS) { return -13; } - - ihx_t *h = (ihx_t *) record; - if (h->record_type == IHX_RT_DATA) { - int retries = 5; - uint16_t address = h->address_high * 0x100 + h->address_low; - - do { - err = c2_programming_init(); - err = c2_block_write(address, h->data, h->len); - } while (err != C2_SUCCESS && retries--); - } else if (h->record_type == IHX_RT_END_OF_FILE) { - - err = c2_reset(); - } - - if (err != C2_SUCCESS) { return -12; } - - return 0; -} - -ssize_t rf_search_and_write(uint8_t *buf, size_t size) -{ - - ssize_t rec_end; - ssize_t rec_start; - ssize_t err; - - for (size_t i = 0; i < size; i++) { - - rec_start = rf_find_hex_record_start(buf + i, size - i); - if (rec_start == RF_RECORD_NO_START_FOUND) { - - return -8; - } - - - rec_start += i; - rec_end = rf_find_hex_record_end(buf + rec_start, size - rec_start); - if (rec_end == RF_RECORD_NO_END_FOUND) { - - return rec_start; - } - - - rec_end += rec_start; - - err = rf_decode_and_write(buf + rec_start, rec_end - rec_start); - if (err < 0) { return err; } - i = rec_end; - } - - return 0; -} - -uint8_t rf_erase_flash(void) -{ - uint8_t err; - - for (uint32_t i = 0; i < 4; i++) { - err = c2_programming_init(); - if (err != C2_SUCCESS) { - return 10; - } - err = c2_device_erase(); - if (err != C2_SUCCESS) { - if (i < 3) { - c2_reset(); - } else { - return 11; - } - } else { - break; - } - } - return 0; -} - -uint8_t SnfBrUpdateInit(void) -{ - pinMode(PIN_C2CK, OUTPUT); - pinMode(PIN_C2D, INPUT); - - return rf_erase_flash(); -} -#endif - - - -void SonoffBridgeReceivedRaw(void) -{ - - uint8_t buckets = 0; - - if (0xB1 == serial_in_buffer[1]) { buckets = serial_in_buffer[2] << 1; } - - 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]) { - if ((i > 3) && buckets) { buckets--; } - if ((i < 3) || (buckets % 2) || (i == serial_in_byte_counter -2)) { - ResponseAppend_P(PSTR(" ")); - } - } - } - ResponseAppend_P(PSTR("\"}}")); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_CMND_RFRAW)); - - XdrvRulesProcess(); -} - - - -void SonoffBridgeLearnFailed(void) -{ - SnfBridge.learn_active = 0; - Response_P(S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, SnfBridge.learn_key, D_JSON_LEARN_FAILED); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RFKEY)); -} - -void SonoffBridgeReceived(void) -{ - uint16_t sync_time = 0; - uint16_t low_time = 0; - uint16_t high_time = 0; - uint32_t received_id = 0; - char rfkey[8]; - char stemp[16]; - - AddLogSerial(LOG_LEVEL_DEBUG); - - if (0xA2 == serial_in_buffer[0]) { - SonoffBridgeLearnFailed(); - } - else if (0xA3 == serial_in_buffer[0]) { - SnfBridge.learn_active = 0; - low_time = serial_in_buffer[3] << 8 | serial_in_buffer[4]; - high_time = serial_in_buffer[5] << 8 | serial_in_buffer[6]; - if (low_time && high_time) { - for (uint32_t i = 0; i < 9; i++) { - Settings.rf_code[SnfBridge.learn_key][i] = serial_in_buffer[i +1]; - } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, SnfBridge.learn_key, D_JSON_LEARNED); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RFKEY)); - } else { - SonoffBridgeLearnFailed(); - } - } - else if (0xA4 == serial_in_buffer[0]) { - if (SnfBridge.learn_active) { - SonoffBridgeLearnFailed(); - } else { - sync_time = serial_in_buffer[1] << 8 | serial_in_buffer[2]; - low_time = serial_in_buffer[3] << 8 | serial_in_buffer[4]; - high_time = serial_in_buffer[5] << 8 | serial_in_buffer[6]; - received_id = serial_in_buffer[7] << 16 | serial_in_buffer[8] << 8 | serial_in_buffer[9]; - - unsigned long now = millis(); - if (!((received_id == SnfBridge.last_received_id) && (now - SnfBridge.last_time < SFB_TIME_AVOID_DUPLICATE))) { - SnfBridge.last_received_id = received_id; - SnfBridge.last_time = now; - strncpy_P(rfkey, PSTR("\"" D_JSON_NONE "\""), sizeof(rfkey)); - for (uint32_t i = 1; i <= 16; i++) { - if (Settings.rf_code[i][0]) { - uint32_t send_id = Settings.rf_code[i][6] << 16 | Settings.rf_code[i][7] << 8 | Settings.rf_code[i][8]; - if (send_id == received_id) { - snprintf_P(rfkey, sizeof(rfkey), PSTR("%d"), i); - break; - } - } - } - if (Settings.flag.rf_receive_decimal) { - snprintf_P(stemp, sizeof(stemp), PSTR("%u"), received_id); - } else { - snprintf_P(stemp, sizeof(stemp), PSTR("\"%06X\""), received_id); - } - 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(); - #ifdef USE_DOMOTICZ - DomoticzSensor(DZ_COUNT, received_id); - #endif - } - } - } -} - -bool SonoffBridgeSerialInput(void) -{ - - static int8_t receive_len = 0; - - if (SnfBridge.receive_flag) { - if (SnfBridge.receive_raw_flag) { - if (!serial_in_byte_counter) { - serial_in_buffer[serial_in_byte_counter++] = 0xAA; - } - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; - if (serial_in_byte_counter == 3) { - if ((0xA6 == serial_in_buffer[1]) || (0xAB == serial_in_buffer[1])) { - receive_len = serial_in_buffer[2] + 4; - } - } - if ((!receive_len && (0x55 == serial_in_byte)) || (receive_len && (serial_in_byte_counter == receive_len))) { - SonoffBridgeReceivedRaw(); - SnfBridge.receive_flag = 0; - return 1; - } - } - else if (!((0 == serial_in_byte_counter) && (0 == serial_in_byte))) { - if (0 == serial_in_byte_counter) { - SnfBridge.expected_bytes = 2; - if (serial_in_byte >= 0xA3) { - SnfBridge.expected_bytes = 11; - } - if (serial_in_byte == 0xA6) { - SnfBridge.expected_bytes = 0; - serial_in_buffer[serial_in_byte_counter++] = 0xAA; - SnfBridge.receive_raw_flag = 1; - } - } - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; - if ((SnfBridge.expected_bytes == serial_in_byte_counter) && (0x55 == serial_in_byte)) { - SonoffBridgeReceived(); - SnfBridge.receive_flag = 0; - return 1; - } - } - serial_in_byte = 0; - } - if (0xAA == serial_in_byte) { - serial_in_byte_counter = 0; - serial_in_byte = 0; - SnfBridge.receive_flag = 1; - receive_len = 0; - } - return 0; -} - -void SonoffBridgeSendCommand(uint8_t code) -{ - Serial.write(0xAA); - Serial.write(code); - Serial.write(0x55); -} - -void SonoffBridgeSendAck(void) -{ - Serial.write(0xAA); - Serial.write(0xA0); - Serial.write(0x55); -} - -void SonoffBridgeSendCode(uint32_t code) -{ - Serial.write(0xAA); - Serial.write(0xA5); - for (uint32_t i = 0; i < 6; i++) { - Serial.write(Settings.rf_code[0][i]); - } - Serial.write((code >> 16) & 0xff); - Serial.write((code >> 8) & 0xff); - Serial.write(code & 0xff); - Serial.write(0x55); - Serial.flush(); -} - -void SonoffBridgeSend(uint8_t idx, uint8_t key) -{ - uint8_t code; - - key--; - Serial.write(0xAA); - Serial.write(0xA5); - for (uint32_t i = 0; i < 8; i++) { - Serial.write(Settings.rf_code[idx][i]); - } - if (0 == idx) { - code = (0x10 << (key >> 2)) | (1 << (key & 3)); - } else { - code = Settings.rf_code[idx][8]; - } - Serial.write(code); - Serial.write(0x55); - Serial.flush(); -#ifdef USE_DOMOTICZ - - -#endif -} - -void SonoffBridgeLearn(uint8_t key) -{ - SnfBridge.learn_key = key; - SnfBridge.learn_active = 1; - SnfBridge.last_learn_time = millis(); - Serial.write(0xAA); - Serial.write(0xA1); - Serial.write(0x55); -} - - - - - -void CmndRfBridge(void) -{ - char *p; - char stemp [10]; - uint32_t code = 0; - uint8_t radix = 10; - - uint32_t set_index = XdrvMailbox.command_code *2; - - if (XdrvMailbox.data[0] == '#') { - XdrvMailbox.data++; - XdrvMailbox.data_len--; - radix = 16; - } - - if (XdrvMailbox.data_len) { - code = strtol(XdrvMailbox.data, &p, radix); - if (code) { - if (CMND_RFCODE == XdrvMailbox.command_code) { - SnfBridge.last_send_code = code; - SonoffBridgeSendCode(code); - } else { - if (1 == XdrvMailbox.payload) { - code = pgm_read_byte(kDefaultRfCode + set_index) << 8 | pgm_read_byte(kDefaultRfCode + set_index +1); - } - uint8_t msb = code >> 8; - uint8_t lsb = code & 0xFF; - if ((code > 0) && (code < 0x7FFF) && (msb != 0x55) && (lsb != 0x55)) { - Settings.rf_code[0][set_index] = msb; - Settings.rf_code[0][set_index +1] = lsb; - } - } - } - } - if (CMND_RFCODE == XdrvMailbox.command_code) { - code = SnfBridge.last_send_code; - } else { - code = Settings.rf_code[0][set_index] << 8 | Settings.rf_code[0][set_index +1]; - } - if (10 == radix) { - snprintf_P(stemp, sizeof(stemp), PSTR("%d"), code); - } else { - snprintf_P(stemp, sizeof(stemp), PSTR("\"#%06X\""), code); - } - Response_P(S_JSON_COMMAND_XVALUE, XdrvMailbox.command, stemp); -} - -void CmndRfKey(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 16)) { - unsigned long now = millis(); - if ((!SnfBridge.learn_active) || (now - SnfBridge.last_learn_time > 60100)) { - SnfBridge.learn_active = 0; - if (2 == XdrvMailbox.payload) { - SonoffBridgeLearn(XdrvMailbox.index); - ResponseCmndIdxChar(D_JSON_START_LEARNING); - } - else if (3 == XdrvMailbox.payload) { - Settings.rf_code[XdrvMailbox.index][0] = 0; - ResponseCmndIdxChar(D_JSON_SET_TO_DEFAULT); - } - else if (4 == XdrvMailbox.payload) { - for (uint32_t i = 0; i < 6; i++) { - Settings.rf_code[XdrvMailbox.index][i] = Settings.rf_code[0][i]; - } - Settings.rf_code[XdrvMailbox.index][6] = (SnfBridge.last_send_code >> 16) & 0xff; - Settings.rf_code[XdrvMailbox.index][7] = (SnfBridge.last_send_code >> 8) & 0xff; - Settings.rf_code[XdrvMailbox.index][8] = SnfBridge.last_send_code & 0xff; - ResponseCmndIdxChar(D_JSON_SAVED); - } else if (5 == XdrvMailbox.payload) { - uint8_t key = XdrvMailbox.index; - uint8_t index = (0 == Settings.rf_code[key][0]) ? 0 : key; - uint16_t sync_time = (Settings.rf_code[index][0] << 8) | Settings.rf_code[index][1]; - uint16_t low_time = (Settings.rf_code[index][2] << 8) | Settings.rf_code[index][3]; - uint16_t high_time = (Settings.rf_code[index][4] << 8) | Settings.rf_code[index][5]; - uint32_t code = (Settings.rf_code[index][6] << 16) | (Settings.rf_code[index][7] << 8); - if (0 == index) { - key--; - code |= (uint8_t)((0x10 << (key >> 2)) | (1 << (key & 3))); - } else { - code |= Settings.rf_code[index][8]; - } - Response_P(PSTR("{\"%s%d\":{\"" D_JSON_SYNC "\":%d,\"" D_JSON_LOW "\":%d,\"" D_JSON_HIGH "\":%d,\"" D_JSON_DATA "\":\"%06X\"}}"), - XdrvMailbox.command, XdrvMailbox.index, sync_time, low_time, high_time, code); - } else { - if ((1 == XdrvMailbox.payload) || (0 == Settings.rf_code[XdrvMailbox.index][0])) { - SonoffBridgeSend(0, XdrvMailbox.index); - ResponseCmndIdxChar(D_JSON_DEFAULT_SENT); - } else { - SonoffBridgeSend(XdrvMailbox.index, 0); - ResponseCmndIdxChar(D_JSON_LEARNED_SENT); - } - } - } else { - Response_P(S_JSON_COMMAND_INDEX_SVALUE, XdrvMailbox.command, SnfBridge.learn_key, D_JSON_LEARNING_ACTIVE); - } - } -} - -void CmndRfRaw(void) -{ - if (XdrvMailbox.data_len) { - if (XdrvMailbox.data_len < 6) { - switch (XdrvMailbox.payload) { - case 0: - SonoffBridgeSendCommand(0xA7); - case 1: - SnfBridge.receive_raw_flag = XdrvMailbox.payload; - break; - case 166: - case 167: - case 169: - case 176: - case 177: - case 255: - SonoffBridgeSendCommand(XdrvMailbox.payload); - SnfBridge.receive_raw_flag = 1; - break; - case 192: - char beep[] = "AAC000C055\0"; - SerialSendRaw(beep); - break; - } - } else { - SerialSendRaw(RemoveSpace(XdrvMailbox.data)); - SnfBridge.receive_raw_flag = 1; - } - } - ResponseCmndStateText(SnfBridge.receive_raw_flag); -} - - - - - -bool Xdrv06(uint8_t function) -{ - bool result = false; - -#ifdef ESP8266 - if (SONOFF_BRIDGE == my_module_type) { - switch (function) { - case FUNC_SERIAL: - result = SonoffBridgeSerialInput(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kSonoffBridgeCommands, SonoffBridgeCommand); - break; - case FUNC_INIT: - SnfBridge.receive_raw_flag = 0; - SonoffBridgeSendCommand(0xA7); - break; - case FUNC_PRE_INIT: - SetSerial(19200, TS_SERIAL_8N1); - break; - } - } -#endif - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_07_domoticz.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_07_domoticz.ino" -#ifdef USE_DOMOTICZ - -#define XDRV_07 7 - -#define D_PRFX_DOMOTICZ "Domoticz" -#define D_CMND_IDX "Idx" -#define D_CMND_KEYIDX "KeyIdx" -#define D_CMND_SWITCHIDX "SwitchIdx" -#define D_CMND_SENSORIDX "SensorIdx" -#define D_CMND_UPDATETIMER "UpdateTimer" - -const char kDomoticzCommands[] PROGMEM = D_PRFX_DOMOTICZ "|" - D_CMND_IDX "|" D_CMND_KEYIDX "|" D_CMND_SWITCHIDX "|" D_CMND_SENSORIDX "|" D_CMND_UPDATETIMER ; - -void (* const DomoticzCommand[])(void) PROGMEM = { - &CmndDomoticzIdx, &CmndDomoticzKeyIdx, &CmndDomoticzSwitchIdx, &CmndDomoticzSensorIdx, &CmndDomoticzUpdateTimer }; - -const char DOMOTICZ_MESSAGE[] PROGMEM = "{\"idx\":%d,\"nvalue\":%d,\"svalue\":\"%s\",\"Battery\":%d,\"RSSI\":%d}"; - -#if MAX_DOMOTICZ_SNS_IDX < DZ_MAX_SENSORS - #error "Domoticz: Too many sensors or change settings.h layout" -#endif - -const char kDomoticzSensors[] PROGMEM = - D_DOMOTICZ_TEMP "|" D_DOMOTICZ_TEMP_HUM "|" D_DOMOTICZ_TEMP_HUM_BARO "|" D_DOMOTICZ_POWER_ENERGY "|" D_DOMOTICZ_ILLUMINANCE "|" - D_DOMOTICZ_COUNT "|" D_DOMOTICZ_VOLTAGE "|" D_DOMOTICZ_CURRENT "|" D_DOMOTICZ_AIRQUALITY "|" D_DOMOTICZ_P1_SMART_METER "|" D_DOMOTICZ_SHUTTER ; - -char domoticz_in_topic[] = DOMOTICZ_IN_TOPIC; - -int domoticz_update_timer = 0; -uint32_t domoticz_fan_debounce = 0; -bool domoticz_subscribe = false; -bool domoticz_update_flag = true; - -#ifdef USE_SHUTTER -bool domoticz_is_shutter = false; -#endif - -int DomoticzBatteryQuality(void) -{ - - - - - int quality = 100; - -#ifdef USE_ADC_VCC - uint16_t voltage = ESP.getVcc(); - if (voltage <= 2600) { - quality = 0; - } else if (voltage >= 4600) { - quality = 200; - } else { - quality = (voltage - 2600) / 10; - } -#endif - return quality; -} - -int DomoticzRssiQuality(void) -{ - - - return WifiGetRssiAsQuality(WiFi.RSSI()) / 10; -} - -#ifdef USE_SONOFF_IFAN -void MqttPublishDomoticzFanState(void) -{ - if (Settings.flag.mqtt_enabled && Settings.domoticz_relay_idx[1]) { - char svalue[8]; - - int fan_speed = GetFanspeed(); - snprintf_P(svalue, sizeof(svalue), PSTR("%d"), fan_speed * 10); - Response_P(DOMOTICZ_MESSAGE, (int)Settings.domoticz_relay_idx[1], (0 == fan_speed) ? 0 : 2, svalue, DomoticzBatteryQuality(), DomoticzRssiQuality()); - MqttPublish(domoticz_in_topic); - - domoticz_fan_debounce = millis(); - } -} - -void DomoticzUpdateFanState(void) -{ - if (domoticz_update_flag) { - MqttPublishDomoticzFanState(); - } - domoticz_update_flag = true; -} -#endif - -void MqttPublishDomoticzPowerState(uint8_t device) -{ - if (Settings.flag.mqtt_enabled) { - if ((device < 1) || (device > devices_present)) { device = 1; } - if (Settings.domoticz_relay_idx[device -1]) { -#ifdef USE_SHUTTER - if (domoticz_is_shutter) { - - } else { -#endif -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan() && (device > 1)) { - - } else { -#endif - char svalue[8]; - - snprintf_P(svalue, sizeof(svalue), PSTR("%d"), Settings.light_dimmer); - Response_P(DOMOTICZ_MESSAGE, (int)Settings.domoticz_relay_idx[device -1], (power & (1 << (device -1))) ? 1 : 0, (light_type) ? svalue : "", DomoticzBatteryQuality(), DomoticzRssiQuality()); - MqttPublish(domoticz_in_topic); -#ifdef USE_SONOFF_IFAN - } -#endif -#ifdef USE_SHUTTER - } -#endif - } - } -} - -void DomoticzUpdatePowerState(uint8_t device) -{ - if (domoticz_update_flag) { - MqttPublishDomoticzPowerState(device); - } - domoticz_update_flag = true; -} - -void DomoticzMqttUpdate(void) -{ - if (domoticz_subscribe && (Settings.domoticz_update_timer || domoticz_update_timer)) { - domoticz_update_timer--; - if (domoticz_update_timer <= 0) { - domoticz_update_timer = Settings.domoticz_update_timer; - for (uint32_t i = 1; i <= devices_present; i++) { -#ifdef USE_SHUTTER - if (domoticz_is_shutter) - { - - break; - } -#endif -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan() && (i > 1)) { - MqttPublishDomoticzFanState(); - break; - } else { -#endif - MqttPublishDomoticzPowerState(i); -#ifdef USE_SONOFF_IFAN - } -#endif - } - } - } -} - -void DomoticzMqttSubscribe(void) -{ - uint8_t maxdev = (devices_present > MAX_DOMOTICZ_IDX) ? MAX_DOMOTICZ_IDX : devices_present; - for (uint32_t i = 0; i < maxdev; i++) { - if (Settings.domoticz_relay_idx[i]) { - domoticz_subscribe = true; - } - } - - if (domoticz_subscribe) { - char stopic[TOPSZ]; - snprintf_P(stopic, sizeof(stopic), PSTR(DOMOTICZ_OUT_TOPIC "/#")); - MqttSubscribe(stopic); - } -} -# 219 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_07_domoticz.ino" -bool DomoticzMqttData(void) -{ - domoticz_update_flag = true; - - if (strncasecmp_P(XdrvMailbox.topic, PSTR(DOMOTICZ_OUT_TOPIC), strlen(DOMOTICZ_OUT_TOPIC)) != 0) { - return false; - } - - - if (XdrvMailbox.data_len < 20) { - return true; - } - StaticJsonBuffer<400> jsonBuf; - JsonObject& domoticz = jsonBuf.parseObject(XdrvMailbox.data); - if (!domoticz.success()) { - return true; - } - - - - uint32_t idx = domoticz["idx"]; - int16_t nvalue = -1; - if (domoticz.containsKey("nvalue")) { - nvalue = domoticz["nvalue"]; - } - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_DOMOTICZ "idx %d, nvalue %d"), idx, nvalue); - - bool found = false; - if ((idx > 0) && (nvalue >= 0) && (nvalue <= 15)) { - uint8_t maxdev = (devices_present > MAX_DOMOTICZ_IDX) ? MAX_DOMOTICZ_IDX : devices_present; - for (uint32_t i = 0; i < maxdev; i++) { - if (idx == Settings.domoticz_relay_idx[i]) { - bool iscolordimmer = strcmp_P(domoticz["dtype"],PSTR("Color Switch")) == 0; - bool isShutter = strcmp_P(domoticz["dtype"],PSTR("Light/Switch")) == 0 & strncmp_P(domoticz["switchType"],PSTR("Blinds"), 6) == 0; - - char stemp1[10]; - snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), i +1); -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan() && (1 == i)) { - uint8_t svalue = 0; - if (domoticz.containsKey("svalue1")) { - svalue = domoticz["svalue1"]; - } else { - return true; - } - svalue = (nvalue == 2) ? svalue / 10 : 0; - if (GetFanspeed() == svalue) { - return true; - } - if (TimePassedSince(domoticz_fan_debounce) < 1000) { - return true; - } - snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_FANSPEED)); - snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), svalue); - found = true; - } else -#endif -#ifdef USE_SHUTTER - if (isShutter) - { - if (domoticz.containsKey("nvalue")) { - nvalue = domoticz["nvalue"]; - } - - uint8_t position = 0; - if (domoticz.containsKey("svalue1")) { - position = domoticz["svalue1"]; - } - if (nvalue != 2) { - position = nvalue == 0 ? 0 : 100; - } - - snprintf_P(XdrvMailbox.topic, TOPSZ, PSTR("/" D_PRFX_SHUTTER D_CMND_SHUTTER_POSITION)); - snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), position); - XdrvMailbox.data_len = position > 99 ? 3 : (position > 9 ? 2 : 1); - - found = true; - } else -#endif -#ifdef USE_LIGHT - if (iscolordimmer && 10 == nvalue) { - - JsonObject& color = domoticz["Color"]; - uint16_t level = nvalue = domoticz["svalue1"]; - uint16_t r = color["r"]; r = r * level / 100; - uint16_t g = color["g"]; g = g * level / 100; - uint16_t b = color["b"]; b = b * level / 100; - uint16_t cw = color["cw"]; cw = cw * level / 100; - uint16_t ww = color["ww"]; ww = ww * level / 100; - uint16_t m = 0; - uint16_t t = 0; - if (color.containsKey("m")) { - m = color["m"]; - t = color["t"]; - } - if (2 == m) { - snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_BACKLOG)); - snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR(D_CMND_COLORTEMPERATURE " %d;" D_CMND_DIMMER " %d"), changeUIntScale(t, 0, 255, CT_MIN, CT_MAX), level); - } else { - snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_COLOR)); - snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%02x%02x%02x%02x%02x"), r, g, b, cw, ww); - } - found = true; - } - else if ((!iscolordimmer && 2 == nvalue) || - (iscolordimmer && 15 == nvalue)) { - if (domoticz.containsKey("svalue1")) { - nvalue = domoticz["svalue1"]; - } else { - return true; - } - if (light_type && (Settings.light_dimmer == nvalue) && ((power >> i) &1)) { - return true; - } - snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_DIMMER)); - snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), nvalue); - found = true; - } else -#endif - if (1 == nvalue || 0 == nvalue) { - if (((power >> i) &1) == (power_t)nvalue) { - return true; - } - snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_POWER "%s"), (devices_present > 1) ? stemp1 : ""); - snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), nvalue); - found = true; - } - break; - } - } - } - if (!found) { return true; } - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_DOMOTICZ D_RECEIVED_TOPIC " %s, " D_DATA " %s"), XdrvMailbox.topic, XdrvMailbox.data); - - domoticz_update_flag = false; - return false; -} - - - -bool DomoticzSendKey(uint8_t key, uint8_t device, uint8_t state, uint8_t svalflg) -{ - bool result = false; - - 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) ? (POWER_TOGGLE == state) ? "Toggle" : "On" : "Off"); - MqttPublish(domoticz_in_topic); - result = true; - } - } - return result; -} -# 393 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_07_domoticz.ino" -uint8_t DomoticzHumidityState(float h) -{ - return (!h) ? 0 : (h < 40) ? 2 : (h > 70) ? 3 : 1; -} - -void DomoticzSensor(uint8_t idx, char *data) -{ - if (Settings.domoticz_sensor_idx[idx]) { - char dmess[128]; - - memcpy(dmess, mqtt_data, sizeof(dmess)); - if (DZ_AIRQUALITY == idx) { - Response_P(PSTR("{\"idx\":%d,\"nvalue\":%s,\"Battery\":%d,\"RSSI\":%d}"), - Settings.domoticz_sensor_idx[idx], data, DomoticzBatteryQuality(), DomoticzRssiQuality()); - } else { - uint8_t nvalue = 0; -#ifdef USE_SHUTTER - if (DZ_SHUTTER == idx) { - uint8_t position = atoi(data); - nvalue = position < 2 ? 0 : (position == 100 ? 1 : 2); - } -#endif - Response_P(DOMOTICZ_MESSAGE, - Settings.domoticz_sensor_idx[idx], nvalue, data, DomoticzBatteryQuality(), DomoticzRssiQuality()); - } - MqttPublish(domoticz_in_topic); - memcpy(mqtt_data, dmess, sizeof(dmess)); - } -} - -void DomoticzSensor(uint8_t idx, uint32_t value) -{ - char data[16]; - snprintf_P(data, sizeof(data), PSTR("%d"), value); - DomoticzSensor(idx, data); -} - - -void DomoticzTempHumPressureSensor(float temp, float hum, float baro) -{ - char temperature[FLOATSZ]; - dtostrfd(temp, 2, temperature); - char humidity[FLOATSZ]; - dtostrfd(hum, 2, humidity); - - char data[32]; - if (baro > -1) { - char pressure[FLOATSZ]; - dtostrfd(baro, 2, pressure); - - snprintf_P(data, sizeof(data), PSTR("%s;%s;%d;%s;5"), temperature, humidity, DomoticzHumidityState(hum), pressure); - DomoticzSensor(DZ_TEMP_HUM_BARO, data); - } else { - snprintf_P(data, sizeof(data), PSTR("%s;%s;%d"), temperature, humidity, DomoticzHumidityState(hum)); - DomoticzSensor(DZ_TEMP_HUM, data); - } -} - -void DomoticzSensorPowerEnergy(int power, char *energy) -{ - char data[16]; - snprintf_P(data, sizeof(data), PSTR("%d;%s"), power, energy); - DomoticzSensor(DZ_POWER_ENERGY, data); -} - -void DomoticzSensorP1SmartMeter(char *usage1, char *usage2, char *return1, char *return2, int power) -{ - - - - - - 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); -} - - - - - -void CmndDomoticzIdx(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { - if (XdrvMailbox.payload >= 0) { - Settings.domoticz_relay_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; - restart_flag = 2; - } - ResponseCmndIdxNumber(Settings.domoticz_relay_idx[XdrvMailbox.index -1]); - } -} - -void CmndDomoticzKeyIdx(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { - if (XdrvMailbox.payload >= 0) { - Settings.domoticz_key_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; - } - ResponseCmndIdxNumber(Settings.domoticz_key_idx[XdrvMailbox.index -1]); - } -} - -void CmndDomoticzSwitchIdx(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { - if (XdrvMailbox.payload >= 0) { - Settings.domoticz_switch_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; - } - ResponseCmndIdxNumber(Settings.domoticz_switch_idx[XdrvMailbox.index -1]); - } -} - -void CmndDomoticzSensorIdx(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= DZ_MAX_SENSORS)) { - if (XdrvMailbox.payload >= 0) { - Settings.domoticz_sensor_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; - } - ResponseCmndIdxNumber(Settings.domoticz_sensor_idx[XdrvMailbox.index -1]); - } -} - -void CmndDomoticzUpdateTimer(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.domoticz_update_timer = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.domoticz_update_timer); -} - - - - - -#ifdef USE_WEBSERVER - -#define WEB_HANDLE_DOMOTICZ "dm" - -const char S_CONFIGURE_DOMOTICZ[] PROGMEM = D_CONFIGURE_DOMOTICZ; - -const char HTTP_BTN_MENU_DOMOTICZ[] PROGMEM = - "

"; - -const char HTTP_FORM_DOMOTICZ[] PROGMEM = - "
 " D_DOMOTICZ_PARAMETERS " " - "
" - ""; -const char HTTP_FORM_DOMOTICZ_RELAY[] PROGMEM = - "" - ""; -const char HTTP_FORM_DOMOTICZ_SWITCH[] PROGMEM = - ""; -const char HTTP_FORM_DOMOTICZ_SENSOR[] PROGMEM = - ""; -const char HTTP_FORM_DOMOTICZ_TIMER[] PROGMEM = - ""; - -void HandleDomoticzConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_DOMOTICZ); - - if (Webserver->hasArg("save")) { - DomoticzSaveSettings(); - WebRestart(1); - return; - } - - char stemp[40]; - - WSContentStart_P(S_CONFIGURE_DOMOTICZ); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_DOMOTICZ); - for (uint32_t i = 0; i < MAX_DOMOTICZ_IDX; i++) { - if (i < devices_present) { - WSContentSend_P(HTTP_FORM_DOMOTICZ_RELAY, - i +1, i, Settings.domoticz_relay_idx[i], - i +1, i, Settings.domoticz_key_idx[i]); - } - if (pin[GPIO_SWT1 +i] < 99) { - WSContentSend_P(HTTP_FORM_DOMOTICZ_SWITCH, - i +1, i, Settings.domoticz_switch_idx[i]); - } -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan() && (1 == i)) { break; } -#endif - } - for (uint32_t i = 0; i < DZ_MAX_SENSORS; i++) { - WSContentSend_P(HTTP_FORM_DOMOTICZ_SENSOR, - i +1, GetTextIndexed(stemp, sizeof(stemp), i, kDomoticzSensors), i, Settings.domoticz_sensor_idx[i]); - } - WSContentSend_P(HTTP_FORM_DOMOTICZ_TIMER, Settings.domoticz_update_timer); - WSContentSend_P(PSTR("
" D_DOMOTICZ_IDX " %d
" D_DOMOTICZ_KEY_IDX " %d
" D_DOMOTICZ_SWITCH_IDX " %d
" D_DOMOTICZ_SENSOR_IDX " %d %s
" D_DOMOTICZ_UPDATE_TIMER " (" STR(DOMOTICZ_UPDATE_TIMER) ")
")); - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); -} - -void DomoticzSaveSettings(void) -{ - char stemp[20]; - char ssensor_indices[6 * MAX_DOMOTICZ_SNS_IDX]; - char tmp[100]; - - for (uint32_t i = 0; i < MAX_DOMOTICZ_IDX; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("r%d"), i); - WebGetArg(stemp, tmp, sizeof(tmp)); - Settings.domoticz_relay_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); - snprintf_P(stemp, sizeof(stemp), PSTR("k%d"), i); - WebGetArg(stemp, tmp, sizeof(tmp)); - Settings.domoticz_key_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); - snprintf_P(stemp, sizeof(stemp), PSTR("s%d"), i); - WebGetArg(stemp, tmp, sizeof(tmp)); - Settings.domoticz_switch_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); - } - ssensor_indices[0] = '\0'; - for (uint32_t i = 0; i < DZ_MAX_SENSORS; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("l%d"), i); - WebGetArg(stemp, tmp, sizeof(tmp)); - Settings.domoticz_sensor_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); - snprintf_P(ssensor_indices, sizeof(ssensor_indices), PSTR("%s%s%d"), ssensor_indices, (strlen(ssensor_indices)) ? "," : "", Settings.domoticz_sensor_idx[i]); - } - WebGetArg("ut", tmp, sizeof(tmp)); - Settings.domoticz_update_timer = (!strlen(tmp)) ? DOMOTICZ_UPDATE_TIMER : atoi(tmp); - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_DOMOTICZ D_CMND_IDX " %d,%d,%d,%d, " D_CMND_KEYIDX " %d,%d,%d,%d, " D_CMND_SWITCHIDX " %d,%d,%d,%d, " D_CMND_SENSORIDX " %s, " D_CMND_UPDATETIMER " %d"), - Settings.domoticz_relay_idx[0], Settings.domoticz_relay_idx[1], Settings.domoticz_relay_idx[2], Settings.domoticz_relay_idx[3], - Settings.domoticz_key_idx[0], Settings.domoticz_key_idx[1], Settings.domoticz_key_idx[2], Settings.domoticz_key_idx[3], - Settings.domoticz_switch_idx[0], Settings.domoticz_switch_idx[1], Settings.domoticz_switch_idx[2], Settings.domoticz_switch_idx[3], - ssensor_indices, Settings.domoticz_update_timer); -} -#endif - - - - - -bool Xdrv07(uint8_t function) -{ - bool result = false; - - if (Settings.flag.mqtt_enabled) { - switch (function) { - case FUNC_EVERY_SECOND: - DomoticzMqttUpdate(); - break; - case FUNC_MQTT_DATA: - result = DomoticzMqttData(); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_ADD_BUTTON: - WSContentSend_P(HTTP_BTN_MENU_DOMOTICZ); - break; - case FUNC_WEB_ADD_HANDLER: - Webserver->on("/" WEB_HANDLE_DOMOTICZ, HandleDomoticzConfiguration); - break; -#endif - case FUNC_MQTT_SUBSCRIBE: - DomoticzMqttSubscribe(); -#ifdef USE_SHUTTER - if (Settings.domoticz_sensor_idx[DZ_SHUTTER]) { domoticz_is_shutter = true; } -#endif - break; - case FUNC_MQTT_INIT: - domoticz_update_timer = 2; - break; - case FUNC_SHOW_SENSOR: - - break; - case FUNC_COMMAND: - result = DecodeCommand(kDomoticzCommands, DomoticzCommand); - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_08_serial_bridge.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_08_serial_bridge.ino" -#ifdef USE_SERIAL_BRIDGE - - - - -#define XDRV_08 8 - -const uint8_t SERIAL_BRIDGE_BUFFER_SIZE = 130; - -const char kSerialBridgeCommands[] PROGMEM = "|" - D_CMND_SSERIALSEND "|" D_CMND_SBAUDRATE; - -void (* const SerialBridgeCommand[])(void) PROGMEM = { - &CmndSSerialSend, &CmndSBaudrate }; - -#include - -TasmotaSerial *SerialBridgeSerial = nullptr; - -unsigned long serial_bridge_polling_window = 0; -char *serial_bridge_buffer = nullptr; -int serial_bridge_in_byte_counter = 0; -bool serial_bridge_active = true; -bool serial_bridge_raw = false; - -void SerialBridgeInput(void) -{ - while (SerialBridgeSerial->available()) { - yield(); - uint8_t serial_in_byte = SerialBridgeSerial->read(); - - if ((serial_in_byte > 127) && !serial_bridge_raw) { - serial_bridge_in_byte_counter = 0; - SerialBridgeSerial->flush(); - return; - } - if (serial_in_byte || serial_bridge_raw) { - - if ((serial_bridge_in_byte_counter < SERIAL_BRIDGE_BUFFER_SIZE -1) && - ((isprint(serial_in_byte) && (128 == Settings.serial_delimiter)) || - ((serial_in_byte != Settings.serial_delimiter) && (128 != Settings.serial_delimiter)) || - serial_bridge_raw)) { - serial_bridge_buffer[serial_bridge_in_byte_counter++] = serial_in_byte; - serial_bridge_polling_window = millis(); - } else { - serial_bridge_polling_window = 0; - break; - } - } - } - - if (serial_bridge_in_byte_counter && (millis() > (serial_bridge_polling_window + SERIAL_POLLING))) { - serial_bridge_buffer[serial_bridge_in_byte_counter] = 0; - char hex_char[(serial_bridge_in_byte_counter * 2) + 2]; - bool assume_json = (!serial_bridge_raw && (serial_bridge_buffer[0] == '{')); - Response_P(PSTR("{\"" D_JSON_SSERIALRECEIVED "\":%s%s%s}"), - (assume_json) ? "" : "\"", - (serial_bridge_raw) ? ToHex_P((unsigned char*)serial_bridge_buffer, serial_bridge_in_byte_counter, hex_char, sizeof(hex_char)) : serial_bridge_buffer, - (assume_json) ? "" : "\""); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SSERIALRECEIVED)); - XdrvRulesProcess(); - serial_bridge_in_byte_counter = 0; - } -} - - - -void SerialBridgeInit(void) -{ - serial_bridge_active = false; - if ((pin[GPIO_SBR_RX] < 99) && (pin[GPIO_SBR_TX] < 99)) { - SerialBridgeSerial = new TasmotaSerial(pin[GPIO_SBR_RX], pin[GPIO_SBR_TX]); - if (SerialBridgeSerial->begin(Settings.sbaudrate * 300)) { - if (SerialBridgeSerial->hardwareSerial()) { - ClaimSerial(); - serial_bridge_buffer = serial_in_buffer; - } else { - serial_bridge_buffer = (char*)(malloc(SERIAL_BRIDGE_BUFFER_SIZE)); - } - serial_bridge_active = true; - SerialBridgeSerial->flush(); - } - } -} - - - - - -void CmndSSerialSend(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 5)) { - serial_bridge_raw = (XdrvMailbox.index > 3); - if (XdrvMailbox.data_len > 0) { - if (1 == XdrvMailbox.index) { - SerialBridgeSerial->write(XdrvMailbox.data, XdrvMailbox.data_len); - SerialBridgeSerial->write("\n"); - } - else if ((2 == XdrvMailbox.index) || (4 == XdrvMailbox.index)) { - SerialBridgeSerial->write(XdrvMailbox.data, XdrvMailbox.data_len); - } - else if (3 == XdrvMailbox.index) { - SerialBridgeSerial->write(Unescape(XdrvMailbox.data, &XdrvMailbox.data_len), XdrvMailbox.data_len); - } - else if (5 == XdrvMailbox.index) { - char *p; - char stemp[3]; - uint8_t code; - - char *codes = RemoveSpace(XdrvMailbox.data); - int size = strlen(XdrvMailbox.data); - - while (size > 1) { - strlcpy(stemp, codes, sizeof(stemp)); - code = strtol(stemp, &p, 16); - SerialBridgeSerial->write(code); - size -= 2; - codes += 2; - } - } - ResponseCmndDone(); - } - } -} - -void CmndSBaudrate(void) -{ - if (XdrvMailbox.payload >= 300) { - XdrvMailbox.payload /= 300; - Settings.sbaudrate = XdrvMailbox.payload; - SerialBridgeSerial->begin(Settings.sbaudrate * 300); - } - ResponseCmndNumber(Settings.sbaudrate * 300); -} - - - - - -bool Xdrv08(uint8_t function) -{ - bool result = false; - - if (serial_bridge_active) { - switch (function) { - case FUNC_LOOP: - if (SerialBridgeSerial) { SerialBridgeInput(); } - break; - case FUNC_PRE_INIT: - SerialBridgeInit(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kSerialBridgeCommands, SerialBridgeCommand); - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_09_timers.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_09_timers.ino" -#ifdef USE_TIMERS -# 39 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_09_timers.ino" -#define XDRV_09 9 - -const char kTimerCommands[] PROGMEM = "|" - D_CMND_TIMER "|" D_CMND_TIMERS -#ifdef USE_SUNRISE - "|" D_CMND_LATITUDE "|" D_CMND_LONGITUDE -#endif - ; - -void (* const TimerCommand[])(void) PROGMEM = { - &CmndTimer, &CmndTimers -#ifdef USE_SUNRISE - , &CmndLatitude, &CmndLongitude -#endif - }; - -uint16_t timer_last_minute = 60; -int8_t timer_window[MAX_TIMERS] = { 0 }; - -#ifdef USE_SUNRISE -# 67 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_09_timers.ino" -const float pi2 = TWO_PI; -const float pi = PI; -const float RAD = DEG_TO_RAD; - -float JulianischesDatum(void) -{ - - int Gregor; - int Jahr = RtcTime.year; - int Monat = RtcTime.month; - int Tag = RtcTime.day_of_month; - - if (Monat <= 2) { - Monat += 12; - Jahr -= 1; - } - Gregor = (Jahr / 400) - (Jahr / 100) + (Jahr / 4); - return 2400000.5f + 365.0f*Jahr - 679004.0f + Gregor + (int)(30.6001f * (Monat +1)) + Tag + 0.5f; -} - -float InPi(float x) -{ - int n = (int)(x / pi2); - x = x - n*pi2; - if (x < 0) x += pi2; - return x; -} - -float eps(float T) -{ - - return RAD * (23.43929111f + (-46.8150f*T - 0.00059f*T*T + 0.001813f*T*T*T)/3600.0f); -} - -float BerechneZeitgleichung(float *DK,float T) -{ - float RA_Mittel = 18.71506921f + 2400.0513369f*T +(2.5862e-5f - 1.72e-9f*T)*T*T; - float M = InPi(pi2 * (0.993133f + 99.997361f*T)); - float L = InPi(pi2 * (0.7859453f + M/pi2 + (6893.0f*sinf(M)+72.0f*sinf(2.0f*M)+6191.2f*T) / 1296.0e3f)); - float e = eps(T); - float RA = atanf(tanf(L)*cosf(e)); - if (RA < 0.0) RA += pi; - if (L > pi) RA += pi; - RA = 24.0*RA/pi2; - *DK = asinf(sinf(e)*sinf(L)); - - RA_Mittel = 24.0f * InPi(pi2*RA_Mittel/24.0f)/pi2; - float dRA = RA_Mittel - RA; - if (dRA < -12.0f) dRA += 24.0f; - if (dRA > 12.0f) dRA -= 24.0f; - dRA = dRA * 1.0027379f; - return dRA; -} - -void DuskTillDawn(uint8_t *hour_up,uint8_t *minute_up, uint8_t *hour_down, uint8_t *minute_down) -{ - float JD2000 = 2451545.0f; - float JD = JulianischesDatum(); - float T = (JD - JD2000) / 36525.0f; - float DK; - - - - - - - - float h = SUNRISE_DAWN_ANGLE *RAD; - float B = (((float)Settings.latitude)/1000000) * RAD; - float GeographischeLaenge = ((float)Settings.longitude)/1000000; - - - - float Zeitzone = ((float)Rtc.time_timezone) / 60; - float Zeitgleichung = BerechneZeitgleichung(&DK, T); - float Zeitdifferenz = 12.0f*acosf((sinf(h) - sinf(B)*sinf(DK)) / (cosf(B)*cosf(DK)))/pi; - float AufgangOrtszeit = 12.0f - Zeitdifferenz - Zeitgleichung; - float UntergangOrtszeit = 12.0f + Zeitdifferenz - Zeitgleichung; - float AufgangWeltzeit = AufgangOrtszeit - GeographischeLaenge / 15.0f; - float UntergangWeltzeit = UntergangOrtszeit - GeographischeLaenge / 15.0f; - float Aufgang = AufgangWeltzeit + Zeitzone; - if (Aufgang < 0.0f) { - Aufgang += 24.0f; - } else { - if (Aufgang >= 24.0f) Aufgang -= 24.0f; - } - float Untergang = UntergangWeltzeit + Zeitzone; - if (Untergang < 0.0f) { - Untergang += 24.0f; - } else { - if (Untergang >= 24.0f) Untergang -= 24.0f; - } - int AufgangMinuten = (int)(60.0f*(Aufgang - (int)Aufgang)+0.5f); - int AufgangStunden = (int)Aufgang; - if (AufgangMinuten >= 60.0f) { - AufgangMinuten -= 60.0f; - AufgangStunden++; - } else { - if (AufgangMinuten < 0.0f) { - AufgangMinuten += 60.0f; - AufgangStunden--; - if (AufgangStunden < 0.0f) AufgangStunden += 24.0f; - } - } - int UntergangMinuten = (int)(60.0f*(Untergang - (int)Untergang)+0.5f); - int UntergangStunden = (int)Untergang; - if (UntergangMinuten >= 60.0f) { - UntergangMinuten -= 60.0f; - UntergangStunden++; - } else { - if (UntergangMinuten<0) { - UntergangMinuten += 60.0f; - UntergangStunden--; - if (UntergangStunden < 0.0f) UntergangStunden += 24.0f; - } - } - *hour_up = AufgangStunden; - *minute_up = AufgangMinuten; - *hour_down = UntergangStunden; - *minute_down = UntergangMinuten; -} - -void ApplyTimerOffsets(Timer *duskdawn) -{ - uint8_t hour[2]; - uint8_t minute[2]; - Timer stored = (Timer)*duskdawn; - - - DuskTillDawn(&hour[0], &minute[0], &hour[1], &minute[1]); - uint8_t mode = (duskdawn->mode -1) &1; - duskdawn->time = (hour[mode] *60) + minute[mode]; - - - uint16_t timeBuffer; - if ((uint16_t)stored.time > 719) { - - timeBuffer = (uint16_t)stored.time - 720; - - if (timeBuffer > (uint16_t)duskdawn->time) { - timeBuffer = 1440 - (timeBuffer - (uint16_t)duskdawn->time); - duskdawn->days = duskdawn->days >> 1; - duskdawn->days |= (stored.days << 6); - } else { - timeBuffer = (uint16_t)duskdawn->time - timeBuffer; - } - } else { - - timeBuffer = (uint16_t)duskdawn->time + (uint16_t)stored.time; - - if (timeBuffer > 1440) { - timeBuffer -= 1440; - duskdawn->days = duskdawn->days << 1; - duskdawn->days |= (stored.days >> 6); - } - } - duskdawn->time = timeBuffer; -} - -String GetSun(uint32_t dawn) -{ - char stime[6]; - - uint8_t hour[2]; - uint8_t minute[2]; - - DuskTillDawn(&hour[0], &minute[0], &hour[1], &minute[1]); - dawn &= 1; - snprintf_P(stime, sizeof(stime), PSTR("%02d:%02d"), hour[dawn], minute[dawn]); - return String(stime); -} - -uint16_t SunMinutes(uint32_t dawn) -{ - uint8_t hour[2]; - uint8_t minute[2]; - - DuskTillDawn(&hour[0], &minute[0], &hour[1], &minute[1]); - dawn &= 1; - return (hour[dawn] *60) + minute[dawn]; -} - -#endif - - - -void TimerSetRandomWindow(uint32_t index) -{ - timer_window[index] = 0; - if (Settings.timer[index].window) { - timer_window[index] = (random(0, (Settings.timer[index].window << 1) +1)) - Settings.timer[index].window; - } -} - -void TimerSetRandomWindows(void) -{ - for (uint32_t i = 0; i < MAX_TIMERS; i++) { TimerSetRandomWindow(i); } -} - -void TimerEverySecond(void) -{ - if (RtcTime.valid) { - if (!RtcTime.hour && !RtcTime.minute && !RtcTime.second) { TimerSetRandomWindows(); } - if (Settings.flag3.timers_enable && - (uptime > 60) && (RtcTime.minute != timer_last_minute)) { - timer_last_minute = RtcTime.minute; - int32_t time = (RtcTime.hour *60) + RtcTime.minute; - uint8_t days = 1 << (RtcTime.day_of_week -1); - - for (uint32_t i = 0; i < MAX_TIMERS; i++) { - - Timer xtimer = Settings.timer[i]; -#ifdef USE_SUNRISE - if ((1 == xtimer.mode) || (2 == xtimer.mode)) { - ApplyTimerOffsets(&xtimer); - } -#endif - if (xtimer.arm) { - int32_t set_time = xtimer.time + timer_window[i]; - if (set_time < 0) { - set_time = abs(timer_window[i]); - } - if (set_time > 1439) { - set_time = xtimer.time - abs(timer_window[i]); - } - if (set_time > 1439) { set_time = 1439; } - - DEBUG_DRIVER_LOG(PSTR("TIM: Timer %d, Time %d, Window %d, SetTime %d"), i +1, xtimer.time, timer_window[i], set_time); - - if (time == set_time) { - if (xtimer.days & days) { - Settings.timer[i].arm = xtimer.repeat; -#if defined(USE_RULES) || defined(USE_SCRIPT) - if (POWER_BLINK == xtimer.power) { - Response_P(PSTR("{\"Clock\":{\"Timer\":%d}}"), i +1); - XdrvRulesProcess(); - } else -#endif - if (devices_present) { ExecuteCommandPower(xtimer.device +1, xtimer.power, SRC_TIMER); } - } - } - } - } - } - } -} - -void PrepShowTimer(uint32_t index) -{ - Timer xtimer = Settings.timer[index -1]; - - char days[8] = { 0 }; - for (uint32_t i = 0; i < 7; i++) { - uint8_t mask = 1 << i; - snprintf(days, sizeof(days), "%s%d", days, ((xtimer.days & mask) > 0)); - } - - char soutput[80]; - soutput[0] = '\0'; - if (devices_present) { - snprintf_P(soutput, sizeof(soutput), PSTR(",\"" D_JSON_TIMER_OUTPUT "\":%d"), xtimer.device +1); - } -#ifdef USE_SUNRISE - char sign[2] = { 0 }; - int16_t hour = xtimer.time / 60; - if ((1 == xtimer.mode) || (2 == xtimer.mode)) { - if (hour > 11) { - hour -= 12; - sign[0] = '-'; - } - } - ResponseAppend_P(PSTR("\"" D_CMND_TIMER "%d\":{\"" D_JSON_TIMER_ARM "\":%d,\"" D_JSON_TIMER_MODE "\":%d,\"" D_JSON_TIMER_TIME "\":\"%s%02d:%02d\",\"" D_JSON_TIMER_WINDOW "\":%d,\"" D_JSON_TIMER_DAYS "\":\"%s\",\"" D_JSON_TIMER_REPEAT "\":%d%s,\"" D_JSON_TIMER_ACTION "\":%d}"), - index, xtimer.arm, xtimer.mode, sign, hour, xtimer.time % 60, xtimer.window, days, xtimer.repeat, soutput, xtimer.power); -#else - ResponseAppend_P(PSTR("\"" D_CMND_TIMER "%d\":{\"" D_JSON_TIMER_ARM "\":%d,\"" D_JSON_TIMER_TIME "\":\"%02d:%02d\",\"" D_JSON_TIMER_WINDOW "\":%d,\"" D_JSON_TIMER_DAYS "\":\"%s\",\"" D_JSON_TIMER_REPEAT "\":%d%s,\"" D_JSON_TIMER_ACTION "\":%d}"), - index, xtimer.arm, xtimer.time / 60, xtimer.time % 60, xtimer.window, days, xtimer.repeat, soutput, xtimer.power); -#endif -} - - - - - -void CmndTimer(void) -{ - uint32_t index = XdrvMailbox.index; - if ((index > 0) && (index <= MAX_TIMERS)) { - uint32_t error = 0; - if (XdrvMailbox.data_len) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAX_TIMERS)) { - if (XdrvMailbox.payload == 0) { - Settings.timer[index -1].data = 0; - } else { - Settings.timer[index -1].data = Settings.timer[XdrvMailbox.payload -1].data; - } - } else { - -#if defined(USE_RULES)==0 && defined(USE_SCRIPT)==0 - if (devices_present) { -#endif - char dataBufUc[XdrvMailbox.data_len + 1]; - UpperCase(dataBufUc, XdrvMailbox.data); - StaticJsonBuffer<256> jsonBuffer; - JsonObject& root = jsonBuffer.parseObject(dataBufUc); - if (!root.success()) { - Response_P(PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_INVALID_JSON "\"}"), index); - error = 1; - } - else { - char parm_uc[10]; - index--; - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_ARM))].success()) { - Settings.timer[index].arm = (root[parm_uc] != 0); - } -#ifdef USE_SUNRISE - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_MODE))].success()) { - Settings.timer[index].mode = (uint8_t)root[parm_uc] & 0x03; - } -#endif - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_TIME))].success()) { - uint16_t itime = 0; - int8_t value = 0; - uint8_t sign = 0; - char time_str[10]; - - strlcpy(time_str, root[parm_uc], sizeof(time_str)); - const char *substr = strtok(time_str, ":"); - if (substr != nullptr) { - if (strchr(substr, '-')) { - sign = 1; - substr++; - } - value = atoi(substr); - if (sign) { value += 12; } - if (value > 23) { value = 23; } - itime = value * 60; - substr = strtok(nullptr, ":"); - if (substr != nullptr) { - value = atoi(substr); - if (value < 0) { value = 0; } - if (value > 59) { value = 59; } - itime += value; - } - } - Settings.timer[index].time = itime; - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_WINDOW))].success()) { - Settings.timer[index].window = (uint8_t)root[parm_uc] & 0x0F; - TimerSetRandomWindow(index); - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_DAYS))].success()) { - - Settings.timer[index].days = 0; - const char *tday = root[parm_uc]; - uint8_t i = 0; - char ch = *tday++; - while ((ch != '\0') && (i < 7)) { - if (ch == '-') { ch = '0'; } - uint8_t mask = 1 << i++; - Settings.timer[index].days |= (ch == '0') ? 0 : mask; - ch = *tday++; - } - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_REPEAT))].success()) { - Settings.timer[index].repeat = (root[parm_uc] != 0); - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_OUTPUT))].success()) { - uint8_t device = ((uint8_t)root[parm_uc] -1) & 0x0F; - Settings.timer[index].device = (device < devices_present) ? device : 0; - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_ACTION))].success()) { - uint8_t action = (uint8_t)root[parm_uc] & 0x03; - Settings.timer[index].power = (devices_present) ? action : 3; - } - - index++; - } - -#if defined(USE_RULES)==0 && defined(USE_SCRIPT)==0 - } else { - Response_P(PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_TIMER_NO_DEVICE "\"}"), index); - error = 1; - } -#endif - } - } - if (!error) { - Response_P(PSTR("{")); - PrepShowTimer(index); - ResponseJsonEnd(); - } - } -} - -void CmndTimers(void) -{ - if (XdrvMailbox.data_len) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { - Settings.flag3.timers_enable = XdrvMailbox.payload; - } - if (XdrvMailbox.payload == 2) { - Settings.flag3.timers_enable = !Settings.flag3.timers_enable; - } - } - - ResponseCmndStateText(Settings.flag3.timers_enable); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, XdrvMailbox.command); - - uint32_t jsflg = 0; - uint32_t lines = 1; - for (uint32_t i = 0; i < MAX_TIMERS; i++) { - if (!jsflg) { - Response_P(PSTR("{\"" D_CMND_TIMERS "%d\":{"), lines++); - } else { - ResponseAppend_P(PSTR(",")); - } - jsflg++; - PrepShowTimer(i +1); - if (jsflg > 3) { - ResponseJsonEndEnd(); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_TIMERS)); - jsflg = 0; - } - } - mqtt_data[0] = '\0'; -} - -#ifdef USE_SUNRISE -void CmndLongitude(void) -{ - if (XdrvMailbox.data_len) { - Settings.longitude = (int)(CharToFloat(XdrvMailbox.data) *1000000); - } - ResponseCmndFloat((float)(Settings.longitude) /1000000, 6); -} - -void CmndLatitude(void) -{ - if (XdrvMailbox.data_len) { - Settings.latitude = (int)(CharToFloat(XdrvMailbox.data) *1000000); - } - ResponseCmndFloat((float)(Settings.latitude) /1000000, 6); -} -#endif - - - - - -#ifdef USE_WEBSERVER -#ifdef USE_TIMERS_WEB - -#define WEB_HANDLE_TIMER "tm" - -const char S_CONFIGURE_TIMER[] PROGMEM = D_CONFIGURE_TIMER; - -const char HTTP_BTN_MENU_TIMER[] PROGMEM = - "

"; - -const char HTTP_TIMER_SCRIPT1[] PROGMEM = - "var pt=[],ct=99;" - "function ce(i,q){" - "var o=document.createElement('option');" - "o.textContent=i;" - "q.appendChild(o);" - "}"; -#ifdef USE_SUNRISE -const char HTTP_TIMER_SCRIPT2[] PROGMEM = - "function gt(){" - "var m,p,q;" - "m=qs('input[name=\"rd\"]:checked').value;" - "p=pt[ct]&0x7FF;" - "if(m==0){" - "so(0);" - "q=Math.floor(p/60);if(q<10){q='0'+q;}qs('#ho').value=q;" - "q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" - "}" - "if((m==1)||(m==2)){" - "so(1);" - "q=Math.floor(p/60);" - "if(q>=12){q-=12;qs('#dr').selectedIndex=1;}" - "else{qs('#dr').selectedIndex=0;}" - "if(q<10){q='0'+q;}qs('#ho').value=q;" - "q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" - "}" - "}" - "function so(b){" - "o=qs('#ho');" - "e=o.childElementCount;" - "if(b==1){" - "qs('#dr').style.visibility='';" - "if(e>12){for(i=12;i<=23;i++){o.removeChild(o.lastElementChild);}}" - "}else{" - "qs('#dr').style.visibility='hidden';" - "if(e<23){for(i=12;i<=23;i++){ce(i,o);}}" - "}" - "}"; -#endif -const char HTTP_TIMER_SCRIPT3[] PROGMEM = - "function st(){" - "var i,l,m,n,p,s;" - "m=0;s=0;" - "n=1<<31;if(eb('a0').checked){s|=n;}" - "n=1<<15;if(eb('r0').checked){s|=n;}" - "for(i=0;i<7;i++){n=1<<(16+i);if(eb('w'+i).checked){s|=n;}}" -#ifdef USE_SUNRISE - "m=qs('input[name=\"rd\"]:checked').value;" - "s|=(qs('input[name=\"rd\"]:checked').value<<29);" -#endif - "if(%d>0){" - "i=qs('#d1').selectedIndex;if(i>=0){s|=(i<<23);}" - "s|=(qs('#p1').selectedIndex<<27);" - "}else{" - "s|=3<<27;" - "}" - "l=((qs('#ho').selectedIndex*60)+qs('#mi').selectedIndex)&0x7FF;" - "if(m==0){s|=l;}" -#ifdef USE_SUNRISE - "if((m==1)||(m==2)){" - "if(qs('#dr').selectedIndex>0){if(l>0){l+=720;}}" - "s|=l&0x7FF;" - "}" -#endif - "s|=((qs('#mw').selectedIndex)&0x0F)<<11;" - "pt[ct]=s;" - "eb('t0').value=pt.join();" - "}"; -const char HTTP_TIMER_SCRIPT4[] PROGMEM = - "function ot(t,e){" - "var i,n,o,p,q,s;" - "if(ct<99){st();}" - "ct=t;" - "o=document.getElementsByClassName('tl');" - "for(i=0;i>29)&3;eb('b'+p).checked=1;" - "gt();" -#else - "p=s&0x7FF;" - "q=Math.floor(p/60);if(q<10){q='0'+q;}qs('#ho').value=q;" - "q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" -#endif - "q=(s>>11)&0xF;if(q<10){q='0'+q;}qs('#mw').value=q;" - "for(i=0;i<7;i++){p=(s>>(16+i))&1;eb('w'+i).checked=p;}" - "if(%d>0){" - "p=(s>>23)&0xF;qs('#d1').value=p+1;" - "p=(s>>27)&3;qs('#p1').selectedIndex=p;" - "}" - "p=(s>>15)&1;eb('r0').checked=p;" - "p=(s>>31)&1;eb('a0').checked=p;" - "}"; -const char HTTP_TIMER_SCRIPT5[] PROGMEM = - "function it(){" - "var b,i,o,s;" - "pt=eb('t0').value.split(',').map(Number);" - "s='';" - "for(i=0;i<%d;i++){" - "b='';" - "if(0==i){b=\" id='dP'\";}" - "s+=\"\"" - "}" - "eb('bt').innerHTML=s;" - "if(%d>0){" - "eb('oa').innerHTML=\"" D_TIMER_OUTPUT " " D_TIMER_ACTION " \";" - "o=qs('#p1');ce('" D_OFF "',o);ce('" D_ON "',o);ce('" D_TOGGLE "',o);" -#if defined(USE_RULES) || defined(USE_SCRIPT) - "ce('" D_RULE "',o);" -#else - "ce('" D_BLINK "',o);" -#endif - "}else{" - "eb('oa').innerHTML=\"" D_TIMER_ACTION " " D_RULE "\";" - "}"; -const char HTTP_TIMER_SCRIPT6[] PROGMEM = -#ifdef USE_SUNRISE - "o=qs('#dr');ce('+',o);ce('-',o);" -#endif - "o=qs('#ho');for(i=0;i<=23;i++){ce((i<10)?('0'+i):i,o);}" - "o=qs('#mi');for(i=0;i<=59;i++){ce((i<10)?('0'+i):i,o);}" - "o=qs('#mw');for(i=0;i<=15;i++){ce((i<10)?('0'+i):i,o);}" - "o=qs('#d1');for(i=0;i<%d;i++){ce(i+1,o);}" - "var a='" D_DAY3LIST "';" - - - - "s='';for(i=0;i<7;i++){s+=\" \"}" - - "eb('ds').innerHTML=s;" - "eb('dP').click();" - "}" - "wl(it);"; -const char HTTP_TIMER_STYLE[] PROGMEM = - ".tl{float:left;border-radius:0;border:1px solid #%06x;padding:1px;width:6.25%%;}"; -const char HTTP_FORM_TIMER1[] PROGMEM = - "
" - " " D_TIMER_PARAMETERS " " - "
" - "



" - "



" - "

" - "
" - " " - "" - "

" - "
"; -#ifdef USE_SUNRISE -const char HTTP_FORM_TIMER3[] PROGMEM = - "
" - "
" - "
" - "
" - "
" - "

" - "" - " "; -#else -const char HTTP_FORM_TIMER3[] PROGMEM = - "" D_TIMER_TIME " "; -#endif -const char HTTP_FORM_TIMER4[] PROGMEM = - "" - " " D_HOUR_MINUTE_SEPARATOR " " - "" - " +/- " - "" - "

" - "
"; - -void HandleTimerConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_TIMER); - - if (Webserver->hasArg("save")) { - TimerSaveSettings(); - HandleConfiguration(); - return; - } - - WSContentStart_P(S_CONFIGURE_TIMER); - WSContentSend_P(HTTP_TIMER_SCRIPT1); -#ifdef USE_SUNRISE - WSContentSend_P(HTTP_TIMER_SCRIPT2); -#endif - WSContentSend_P(HTTP_TIMER_SCRIPT3, devices_present); - WSContentSend_P(HTTP_TIMER_SCRIPT4, WebColor(COL_TIMER_TAB_BACKGROUND), WebColor(COL_TIMER_TAB_TEXT), WebColor(COL_FORM), WebColor(COL_TEXT), devices_present); - WSContentSend_P(HTTP_TIMER_SCRIPT5, MAX_TIMERS, devices_present); - WSContentSend_P(HTTP_TIMER_SCRIPT6, devices_present); - WSContentSendStyle_P(HTTP_TIMER_STYLE, WebColor(COL_FORM)); - WSContentSend_P(HTTP_FORM_TIMER1, (Settings.flag3.timers_enable) ? " checked" : ""); - for (uint32_t i = 0; i < MAX_TIMERS; i++) { - WSContentSend_P(PSTR("%s%u"), (i > 0) ? "," : "", Settings.timer[i].data); - } - WSContentSend_P(HTTP_FORM_TIMER2); -#ifdef USE_SUNRISE - WSContentSend_P(HTTP_FORM_TIMER3, 100 + (strlen(D_SUNSET) *12), GetSun(0).c_str(), GetSun(1).c_str()); -#else - WSContentSend_P(HTTP_FORM_TIMER3); -#endif - WSContentSend_P(HTTP_FORM_TIMER4); - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); -} - -void TimerSaveSettings(void) -{ - char tmp[MAX_TIMERS *12]; - char message[LOGSZ]; - Timer timer; - - Settings.flag3.timers_enable = Webserver->hasArg("e0"); - WebGetArg("t0", tmp, sizeof(tmp)); - char *p = tmp; - snprintf_P(message, sizeof(message), PSTR(D_LOG_MQTT D_CMND_TIMERS " %d"), Settings.flag3.timers_enable); - for (uint32_t i = 0; i < MAX_TIMERS; i++) { - timer.data = strtol(p, &p, 10); - p++; - if (timer.time < 1440) { - bool flag = (timer.window != Settings.timer[i].window); - Settings.timer[i].data = timer.data; - if (flag) TimerSetRandomWindow(i); - } - snprintf_P(message, sizeof(message), PSTR("%s,0x%08X"), message, Settings.timer[i].data); - } - AddLog_P(LOG_LEVEL_DEBUG, message); -} -#endif -#endif - - - - - -bool Xdrv09(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_PRE_INIT: - TimerSetRandomWindows(); - break; -#ifdef USE_WEBSERVER -#ifdef USE_TIMERS_WEB - case FUNC_WEB_ADD_BUTTON: -#if defined(USE_RULES) || defined(USE_SCRIPT) - WSContentSend_P(HTTP_BTN_MENU_TIMER); -#else - if (devices_present) { WSContentSend_P(HTTP_BTN_MENU_TIMER); } -#endif - break; - case FUNC_WEB_ADD_HANDLER: - Webserver->on("/" WEB_HANDLE_TIMER, HandleTimerConfiguration); - break; -#endif -#endif - case FUNC_EVERY_SECOND: - TimerEverySecond(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kTimerCommands, TimerCommand); - break; - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -#ifdef USE_RULES -#ifndef USE_SCRIPT -# 67 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -#define XDRV_10 10 - -#define D_CMND_RULE "Rule" -#define D_CMND_RULETIMER "RuleTimer" -#define D_CMND_EVENT "Event" -#define D_CMND_VAR "Var" -#define D_CMND_MEM "Mem" -#define D_CMND_ADD "Add" -#define D_CMND_SUB "Sub" -#define D_CMND_MULT "Mult" -#define D_CMND_SCALE "Scale" -#define D_CMND_CALC_RESOLUTION "CalcRes" -#define D_CMND_SUBSCRIBE "Subscribe" -#define D_CMND_UNSUBSCRIBE "Unsubscribe" -#define D_CMND_IF "If" - -#define D_JSON_INITIATED "Initiated" - -#define COMPARE_OPERATOR_NONE -1 -#define COMPARE_OPERATOR_EQUAL 0 -#define COMPARE_OPERATOR_BIGGER 1 -#define COMPARE_OPERATOR_SMALLER 2 -#define COMPARE_OPERATOR_EXACT_DIVISION 3 -#define COMPARE_OPERATOR_NUMBER_EQUAL 4 -#define COMPARE_OPERATOR_NOT_EQUAL 5 -#define COMPARE_OPERATOR_BIGGER_EQUAL 6 -#define COMPARE_OPERATOR_SMALLER_EQUAL 7 -#define MAXIMUM_COMPARE_OPERATOR COMPARE_OPERATOR_SMALLER_EQUAL -const char kCompareOperators[] PROGMEM = "=\0>\0<\0|\0==!=>=<="; - -#ifdef USE_EXPRESSION - #include - - const char kExpressionOperators[] PROGMEM = "+-*/%^\0"; - #define EXPRESSION_OPERATOR_ADD 0 - #define EXPRESSION_OPERATOR_SUBTRACT 1 - #define EXPRESSION_OPERATOR_MULTIPLY 2 - #define EXPRESSION_OPERATOR_DIVIDEDBY 3 - #define EXPRESSION_OPERATOR_MODULO 4 - #define EXPRESSION_OPERATOR_POWER 5 - - const uint8_t kExpressionOperatorsPriorities[] PROGMEM = {1, 1, 2, 2, 3, 4}; - #define MAX_EXPRESSION_OPERATOR_PRIORITY 4 - - - #define LOGIC_OPERATOR_AND 1 - #define LOGIC_OPERATOR_OR 2 - - #define IF_BLOCK_INVALID -1 - #define IF_BLOCK_ANY 0 - #define IF_BLOCK_ELSEIF 1 - #define IF_BLOCK_ELSE 2 - #define IF_BLOCK_ENDIF 3 -#endif - -const char kRulesCommands[] PROGMEM = "|" - D_CMND_RULE "|" D_CMND_RULETIMER "|" D_CMND_EVENT "|" D_CMND_VAR "|" D_CMND_MEM "|" - D_CMND_ADD "|" D_CMND_SUB "|" D_CMND_MULT "|" D_CMND_SCALE "|" D_CMND_CALC_RESOLUTION -#ifdef SUPPORT_MQTT_EVENT - "|" D_CMND_SUBSCRIBE "|" D_CMND_UNSUBSCRIBE -#endif -#ifdef SUPPORT_IF_STATEMENT - "|" D_CMND_IF -#endif - ; - -void (* const RulesCommand[])(void) PROGMEM = { - &CmndRule, &CmndRuleTimer, &CmndEvent, &CmndVariable, &CmndMemory, - &CmndAddition, &CmndSubtract, &CmndMultiply, &CmndScale, &CmndCalcResolution -#ifdef SUPPORT_MQTT_EVENT - , &CmndSubscribe, &CmndUnsubscribe -#endif -#ifdef SUPPORT_IF_STATEMENT - , &CmndIf -#endif - }; - -#ifdef SUPPORT_MQTT_EVENT - #include - typedef struct { - String Event; - String Topic; - String Key; - } MQTT_Subscription; - LinkedList subscriptions; -#endif - -struct RULES { - String event_value; - unsigned long timer[MAX_RULE_TIMERS] = { 0 }; - uint32_t triggers[MAX_RULE_SETS] = { 0 }; - uint8_t trigger_count[MAX_RULE_SETS] = { 0 }; - - long new_power = -1; - long old_power = -1; - long old_dimm = -1; - - uint16_t last_minute = 60; - uint16_t vars_event = 0; - uint8_t mems_event = 0; - bool teleperiod = false; - - char event_data[100]; -} Rules; - -char rules_vars[MAX_RULE_VARS][33] = {{ 0 }}; - -#if (MAX_RULE_VARS>16) -#error MAX_RULE_VARS is bigger than 16 -#endif -#if (MAX_RULE_MEMS>16) -#error MAX_RULE_MEMS is bigger than 16 -#endif - - - -bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) -{ - - - - - bool match = false; - char stemp[10]; - - - int pos = rule.indexOf('#'); - if (pos == -1) { return false; } - - String rule_task = rule.substring(0, pos); - if (Rules.teleperiod) { - int ppos = rule_task.indexOf("TELE-"); - if (ppos == -1) { return false; } - rule_task = rule.substring(5, pos); - } - - String rule_expr = rule.substring(pos +1); - String rule_name, rule_param; - int8_t compareOperator = parseCompareExpression(rule_expr, rule_name, rule_param); - - char rule_svalue[80] = { 0 }; - float rule_value = 0; - if (compareOperator != COMPARE_OPERATOR_NONE) { - for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%%VAR%d%%"), i +1); - if (rule_param.startsWith(stemp)) { - rule_param = rules_vars[i]; - break; - } - } - for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%%MEM%d%%"), i +1); - if (rule_param.startsWith(stemp)) { - rule_param = SettingsText(SET_MEM1 + i); - break; - } - } - snprintf_P(stemp, sizeof(stemp), PSTR("%%TIME%%")); - if (rule_param.startsWith(stemp)) { - rule_param = String(MinutesPastMidnight()); - } - snprintf_P(stemp, sizeof(stemp), PSTR("%%UPTIME%%")); - if (rule_param.startsWith(stemp)) { - rule_param = String(MinutesUptime()); - } - snprintf_P(stemp, sizeof(stemp), PSTR("%%TIMESTAMP%%")); - if (rule_param.startsWith(stemp)) { - rule_param = GetDateAndTime(DT_LOCAL).c_str(); - } -#if defined(USE_TIMERS) && defined(USE_SUNRISE) - snprintf_P(stemp, sizeof(stemp), PSTR("%%SUNRISE%%")); - if (rule_param.startsWith(stemp)) { - rule_param = String(SunMinutes(0)); - } - snprintf_P(stemp, sizeof(stemp), PSTR("%%SUNSET%%")); - if (rule_param.startsWith(stemp)) { - rule_param = String(SunMinutes(1)); - } -#endif - rule_param.toUpperCase(); - strlcpy(rule_svalue, rule_param.c_str(), sizeof(rule_svalue)); - - int temp_value = GetStateNumber(rule_svalue); - if (temp_value > -1) { - rule_value = temp_value; - } else { - rule_value = CharToFloat((char*)rule_svalue); - } - } - - - int rule_name_idx = 0; - if ((pos = rule_name.indexOf("[")) > 0) { - rule_name_idx = rule_name.substring(pos +1).toInt(); - if ((rule_name_idx < 1) || (rule_name_idx > 6)) { - rule_name_idx = 1; - } - rule_name = rule_name.substring(0, pos); - } - - StaticJsonBuffer<1024> jsonBuf; - JsonObject &root = jsonBuf.parseObject(event); - if (!root.success()) { return false; } - if (!root[rule_task].success()) { return false; } - - JsonObject &obj1 = root[rule_task]; - JsonObject *obj = &obj1; - String subtype; - uint32_t i = 0; - while ((pos = rule_name.indexOf("#")) > 0) { - subtype = rule_name.substring(0, pos); - if (!(*obj)[subtype].success()) { return false; } - JsonObject &obj2 = (*obj)[subtype]; - obj = &obj2; - rule_name = rule_name.substring(pos +1); - if (i++ > 10) { return false; } - } - if (!(*obj)[rule_name].success()) { return false; } - const char* str_value; - if (rule_name_idx) { - str_value = (*obj)[rule_name][rule_name_idx -1]; - } else { - str_value = (*obj)[rule_name]; - } - - - - - Rules.event_value = str_value; - - - float value = 0; - if (str_value) { - value = CharToFloat((char*)str_value); - int int_value = int(value); - int int_rule_value = int(rule_value); - switch (compareOperator) { - case COMPARE_OPERATOR_EXACT_DIVISION: - match = (int_rule_value && (int_value % int_rule_value) == 0); - break; - case COMPARE_OPERATOR_EQUAL: - match = (!strcasecmp(str_value, rule_svalue)); - break; - case COMPARE_OPERATOR_BIGGER: - match = (value > rule_value); - break; - case COMPARE_OPERATOR_SMALLER: - match = (value < rule_value); - break; - case COMPARE_OPERATOR_NUMBER_EQUAL: - match = (value == rule_value); - break; - case COMPARE_OPERATOR_NOT_EQUAL: - match = (value != rule_value); - break; - case COMPARE_OPERATOR_BIGGER_EQUAL: - match = (value >= rule_value); - break; - case COMPARE_OPERATOR_SMALLER_EQUAL: - match = (value <= rule_value); - break; - default: - match = true; - } - } else match = true; - - - - - if (bitRead(Settings.rule_once, rule_set)) { - if (match) { - if (!bitRead(Rules.triggers[rule_set], Rules.trigger_count[rule_set])) { - bitSet(Rules.triggers[rule_set], Rules.trigger_count[rule_set]); - } else { - match = false; - } - } else { - bitClear(Rules.triggers[rule_set], Rules.trigger_count[rule_set]); - } - } - - - - return match; -} -# 368 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -int8_t parseCompareExpression(String &expr, String &leftExpr, String &rightExpr) -{ - char compare_operator[3]; - int8_t compare = COMPARE_OPERATOR_NONE; - leftExpr = expr; - int position; - for (int8_t i = MAXIMUM_COMPARE_OPERATOR; i >= 0; i--) { - snprintf_P(compare_operator, sizeof(compare_operator), kCompareOperators + (i *2)); - if ((position = expr.indexOf(compare_operator)) > 0) { - compare = i; - leftExpr = expr.substring(0, position); - leftExpr.trim(); - rightExpr = expr.substring(position + strlen(compare_operator)); - rightExpr.trim(); - break; - } - } - return compare; -} - -void RulesVarReplace(String &commands, const String &sfind, const String &replace) -{ - - - - char *find = (char*)sfind.c_str(); - uint32_t flen = strlen(find); - - String ucommand = commands; - ucommand.toUpperCase(); - char *read_from = (char*)ucommand.c_str(); - char *write_to = (char*)commands.c_str(); - char *found_at; - while ((found_at = strstr(read_from, find)) != nullptr) { - write_to += (found_at - read_from); - memmove_P(write_to, find, flen); - write_to += flen; - read_from = found_at + flen; - } - - commands.replace(find, replace); -} - - - -bool RuleSetProcess(uint8_t rule_set, String &event_saved) -{ - bool serviced = false; - char stemp[10]; - - delay(0); - - - - String rules = Settings.rules[rule_set]; - - Rules.trigger_count[rule_set] = 0; - int plen = 0; - int plen2 = 0; - bool stop_all_rules = false; - while (true) { - rules = rules.substring(plen); - rules.trim(); - if (!rules.length()) { return serviced; } - - String rule = rules; - rule.toUpperCase(); - if (!rule.startsWith("ON ")) { return serviced; } - - int pevt = rule.indexOf(" DO "); - if (pevt == -1) { return serviced; } - String event_trigger = rule.substring(3, pevt); - - plen = rule.indexOf(" ENDON"); - plen2 = rule.indexOf(" BREAK"); - if ((plen == -1) && (plen2 == -1)) { return serviced; } - - if (plen == -1) { plen = 9999; } - if (plen2 == -1) { plen2 = 9999; } - plen = tmin(plen, plen2); - - String commands = rules.substring(pevt +4, plen); - Rules.event_value = ""; - String event = event_saved; - - - - if (RulesRuleMatch(rule_set, event, event_trigger)) { - if (plen == plen2) { stop_all_rules = true; } - commands.trim(); - String ucommand = commands; - ucommand.toUpperCase(); - - - - if ((ucommand.indexOf("IF ") == -1) && - (ucommand.indexOf("EVENT ") != -1) && - (ucommand.indexOf("BACKLOG ") == -1)) { - commands = "backlog " + commands; - } - - RulesVarReplace(commands, F("%VALUE%"), Rules.event_value); - for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%%VAR%d%%"), i +1); - RulesVarReplace(commands, stemp, rules_vars[i]); - } - for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%%MEM%d%%"), i +1); - RulesVarReplace(commands, stemp, SettingsText(SET_MEM1 +i)); - } - RulesVarReplace(commands, F("%TIME%"), String(MinutesPastMidnight())); - RulesVarReplace(commands, F("%UTCTIME%"), String(UtcTime())); - RulesVarReplace(commands, F("%UPTIME%"), String(MinutesUptime())); - RulesVarReplace(commands, F("%TIMESTAMP%"), GetDateAndTime(DT_LOCAL)); - RulesVarReplace(commands, F("%TOPIC%"), SettingsText(SET_MQTT_TOPIC)); -#if defined(USE_TIMERS) && defined(USE_SUNRISE) - RulesVarReplace(commands, F("%SUNRISE%"), String(SunMinutes(0))); - RulesVarReplace(commands, F("%SUNSET%"), String(SunMinutes(1))); -#endif - - char command[commands.length() +1]; - strlcpy(command, commands.c_str(), sizeof(command)); - - AddLog_P2(LOG_LEVEL_INFO, PSTR("RUL: %s performs \"%s\""), event_trigger.c_str(), command); - - - -#ifdef SUPPORT_IF_STATEMENT - char *pCmd = command; - RulesPreprocessCommand(pCmd); -#endif - ExecuteCommand(command, SRC_RULE); - serviced = true; - if (stop_all_rules) { return serviced; } - } - plen += 6; - Rules.trigger_count[rule_set]++; - } - return serviced; -} - - - -bool RulesProcessEvent(char *json_event) -{ - bool serviced = false; - -#ifdef USE_DEBUG_DRIVER - ShowFreeMem(PSTR("RulesProcessEvent")); -#endif - - String event_saved = json_event; - - - - char *p = strchr(json_event, ':'); - if ((p != NULL) && !(strchr(++p, ':'))) { - event_saved.replace(F(":"), F(":{\"Data\":")); - event_saved += F("}"); - - } - event_saved.toUpperCase(); - - - - for (uint32_t i = 0; i < MAX_RULE_SETS; i++) { - if (strlen(Settings.rules[i]) && bitRead(Settings.rule_enabled, i)) { - if (RuleSetProcess(i, event_saved)) { serviced = true; } - } - } - return serviced; -} - -bool RulesProcess(void) -{ - return RulesProcessEvent(mqtt_data); -} - -void RulesInit(void) -{ - rules_flag.data = 0; - for (uint32_t i = 0; i < MAX_RULE_SETS; i++) { - if (Settings.rules[i][0] == '\0') { - bitWrite(Settings.rule_enabled, i, 0); - bitWrite(Settings.rule_once, i, 0); - } - } - Rules.teleperiod = false; -} - -void RulesEvery50ms(void) -{ - if (Settings.rule_enabled) { - char json_event[120]; - - if (-1 == Rules.new_power) { Rules.new_power = power; } - if (Rules.new_power != Rules.old_power) { - if (Rules.old_power != -1) { - for (uint32_t i = 0; i < devices_present; i++) { - uint8_t new_state = (Rules.new_power >> i) &1; - if (new_state != ((Rules.old_power >> i) &1)) { - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Power%d\":{\"State\":%d}}"), i +1, new_state); - RulesProcessEvent(json_event); - } - } - } else { - - for (uint32_t i = 0; i < devices_present; i++) { - uint8_t new_state = (Rules.new_power >> i) &1; - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Power%d\":{\"Boot\":%d}}"), i +1, new_state); - RulesProcessEvent(json_event); - } - - for (uint32_t i = 0; i < MAX_SWITCHES; i++) { -#ifdef USE_TM1638 - if ((pin[GPIO_SWT1 +i] < 99) || ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99))) { -#else - if (pin[GPIO_SWT1 +i] < 99) { -#endif - snprintf_P(json_event, sizeof(json_event), PSTR("{\"" D_JSON_SWITCH "%d\":{\"Boot\":%d}}"), i +1, (SwitchState(i))); - RulesProcessEvent(json_event); - } - } - } - Rules.old_power = Rules.new_power; - } - else if (Rules.old_dimm != Settings.light_dimmer) { - if (Rules.old_dimm != -1) { - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Dimmer\":{\"State\":%d}}"), Settings.light_dimmer); - } else { - - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Dimmer\":{\"Boot\":%d}}"), Settings.light_dimmer); - } - RulesProcessEvent(json_event); - Rules.old_dimm = Settings.light_dimmer; - } - else if (Rules.event_data[0]) { - char *event; - char *parameter; - event = strtok_r(Rules.event_data, "=", ¶meter); - if (event) { - event = Trim(event); - if (parameter) { - parameter = Trim(parameter); - } else { - parameter = event + strlen(event); - } - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Event\":{\"%s\":\"%s\"}}"), event, parameter); - Rules.event_data[0] ='\0'; - RulesProcessEvent(json_event); - } else { - Rules.event_data[0] ='\0'; - } - } - else if (Rules.vars_event || Rules.mems_event){ - if (Rules.vars_event) { - for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { - if (bitRead(Rules.vars_event, i)) { - bitClear(Rules.vars_event, i); - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Var%d\":{\"State\":%s}}"), i+1, rules_vars[i]); - RulesProcessEvent(json_event); - break; - } - } - } - if (Rules.mems_event) { - for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { - if (bitRead(Rules.mems_event, i)) { - bitClear(Rules.mems_event, i); - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Mem%d\":{\"State\":%s}}"), i+1, SettingsText(SET_MEM1 +i)); - RulesProcessEvent(json_event); - break; - } - } - } - } - else if (rules_flag.data) { - uint16_t mask = 1; - for (uint32_t i = 0; i < MAX_RULES_FLAG; i++) { - if (rules_flag.data & mask) { - rules_flag.data ^= mask; - json_event[0] = '\0'; - switch (i) { - case 0: strncpy_P(json_event, PSTR("{\"System\":{\"Boot\":1}}"), sizeof(json_event)); break; - case 1: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Initialized\":%d}}"), MinutesPastMidnight()); break; - case 2: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Set\":%d}}"), MinutesPastMidnight()); break; - case 3: strncpy_P(json_event, PSTR("{\"MQTT\":{\"Connected\":1}}"), sizeof(json_event)); break; - case 4: strncpy_P(json_event, PSTR("{\"MQTT\":{\"Disconnected\":1}}"), sizeof(json_event)); break; - case 5: strncpy_P(json_event, PSTR("{\"WIFI\":{\"Connected\":1}}"), sizeof(json_event)); break; - case 6: strncpy_P(json_event, PSTR("{\"WIFI\":{\"Disconnected\":1}}"), sizeof(json_event)); break; - case 7: strncpy_P(json_event, PSTR("{\"HTTP\":{\"Initialized\":1}}"), sizeof(json_event)); break; -#ifdef USE_SHUTTER - case 8: strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moved\":1}}"), sizeof(json_event)); break; - case 9: strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moving\":1}}"), sizeof(json_event)); break; -#endif - } - if (json_event[0]) { - RulesProcessEvent(json_event); - break; - } - } - mask <<= 1; - } - } - } -} - -uint8_t rules_xsns_index = 0; - -void RulesEvery100ms(void) -{ - if (Settings.rule_enabled && (uptime > 4)) { - mqtt_data[0] = '\0'; - int tele_period_save = tele_period; - tele_period = 2; - XsnsNextCall(FUNC_JSON_APPEND, rules_xsns_index); - tele_period = tele_period_save; - if (strlen(mqtt_data)) { - mqtt_data[0] = '{'; - ResponseJsonEnd(); - RulesProcess(); - } - } -} - -void RulesEverySecond(void) -{ - if (Settings.rule_enabled) { - char json_event[120]; - - if (RtcTime.valid) { - if ((uptime > 60) && (RtcTime.minute != Rules.last_minute)) { - Rules.last_minute = RtcTime.minute; - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Minute\":%d}}"), MinutesPastMidnight()); - RulesProcessEvent(json_event); - } - } - for (uint32_t i = 0; i < MAX_RULE_TIMERS; i++) { - if (Rules.timer[i] != 0L) { - if (TimeReached(Rules.timer[i])) { - Rules.timer[i] = 0L; - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Rules\":{\"Timer\":%d}}"), i +1); - RulesProcessEvent(json_event); - } - } - } - } -} - -void RulesSaveBeforeRestart(void) -{ - if (Settings.rule_enabled) { - char json_event[32]; - - strncpy_P(json_event, PSTR("{\"System\":{\"Save\":1}}"), sizeof(json_event)); - RulesProcessEvent(json_event); - } -} - -void RulesSetPower(void) -{ - Rules.new_power = XdrvMailbox.index; -} - -void RulesTeleperiod(void) -{ - Rules.teleperiod = true; - RulesProcess(); - Rules.teleperiod = false; -} - -#ifdef SUPPORT_MQTT_EVENT -# 750 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -bool RulesMqttData(void) -{ - if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > 256) { - return false; - } - bool serviced = false; - String sTopic = XdrvMailbox.topic; - String sData = XdrvMailbox.data; - - MQTT_Subscription event_item; - - for (uint32_t index = 0; index < subscriptions.size(); index++) { - event_item = subscriptions.get(index); - - - if (sTopic.startsWith(event_item.Topic)) { - - serviced = true; - String value; - if (event_item.Key.length() == 0) { - value = sData; - } else { - StaticJsonBuffer<500> jsonBuf; - JsonObject& jsonData = jsonBuf.parseObject(sData); - String key1 = event_item.Key; - String key2; - if (!jsonData.success()) break; - int dot; - if ((dot = key1.indexOf('.')) > 0) { - key2 = key1.substring(dot+1); - key1 = key1.substring(0, dot); - if (!jsonData[key1][key2].success()) break; - value = (const char *)jsonData[key1][key2]; - } else { - if (!jsonData[key1].success()) break; - value = (const char *)jsonData[key1]; - } - } - value.trim(); - - snprintf_P(Rules.event_data, sizeof(Rules.event_data), PSTR("%s=%s"), event_item.Event.c_str(), value.c_str()); - } - } - return serviced; -} -# 812 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -void CmndSubscribe(void) -{ - MQTT_Subscription subscription_item; - String events; - if (XdrvMailbox.data_len > 0) { - char parameters[XdrvMailbox.data_len+1]; - memcpy(parameters, XdrvMailbox.data, XdrvMailbox.data_len); - parameters[XdrvMailbox.data_len] = '\0'; - String event_name, topic, key; - - char * pos = strtok(parameters, ","); - if (pos) { - event_name = Trim(pos); - pos = strtok(nullptr, ","); - if (pos) { - topic = Trim(pos); - pos = strtok(nullptr, ","); - if (pos) { - key = Trim(pos); - } - } - } - - event_name.toUpperCase(); - if (event_name.length() > 0 && topic.length() > 0) { - - for (uint32_t index=0; index < subscriptions.size(); index++) { - if (subscriptions.get(index).Event.equals(event_name)) { - - String stopic = subscriptions.get(index).Topic + "/#"; - MqttUnsubscribe(stopic.c_str()); - subscriptions.remove(index); - break; - } - } - - if (!topic.endsWith("#")) { - if (topic.endsWith("/")) { - topic.concat("#"); - } else { - topic.concat("/#"); - } - } - - - subscription_item.Event = event_name; - subscription_item.Topic = topic.substring(0, topic.length() - 2); - subscription_item.Key = key; - subscriptions.add(subscription_item); - - MqttSubscribe(topic.c_str()); - events.concat(event_name + "," + topic - + (key.length()>0 ? "," : "") - + key); - } else { - events = D_JSON_WRONG_PARAMETERS; - } - } else { - - for (uint32_t index=0; index < subscriptions.size(); index++) { - subscription_item = subscriptions.get(index); - events.concat(subscription_item.Event + "," + subscription_item.Topic - + (subscription_item.Key.length()>0 ? "," : "") - + subscription_item.Key + "; "); - } - } - ResponseCmndChar(events.c_str()); -} -# 892 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -void CmndUnsubscribe(void) -{ - MQTT_Subscription subscription_item; - String events; - if (XdrvMailbox.data_len > 0) { - for (uint32_t index = 0; index < subscriptions.size(); index++) { - subscription_item = subscriptions.get(index); - if (subscription_item.Event.equalsIgnoreCase(XdrvMailbox.data)) { - String stopic = subscription_item.Topic + "/#"; - MqttUnsubscribe(stopic.c_str()); - events = subscription_item.Event; - subscriptions.remove(index); - break; - } - } - } else { - - String stopic; - while (subscriptions.size() > 0) { - events.concat(subscriptions.get(0).Event + "; "); - stopic = subscriptions.get(0).Topic + "/#"; - MqttUnsubscribe(stopic.c_str()); - subscriptions.remove(0); - } - } - ResponseCmndChar(events.c_str()); -} - -#endif - -#ifdef USE_EXPRESSION -# 934 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -char * findClosureBracket(char * pStart) -{ - char * pointer = pStart + 1; - - bool bFindClosures = false; - uint8_t matchClosures = 1; - while (*pointer) - { - if (*pointer == ')') { - matchClosures--; - if (matchClosures == 0) { - bFindClosures = true; - break; - } - } else if (*pointer == '(') { - matchClosures++; - } - pointer++; - } - if (bFindClosures) { - return pointer; - } else { - return nullptr; - } -} -# 973 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -bool findNextNumber(char * &pNumber, float &value) -{ - bool bSucceed = false; - String sNumber = ""; - if (*pNumber == '-') { - sNumber = "-"; - pNumber++; - } - while (*pNumber) { - if (isdigit(*pNumber) || (*pNumber == '.')) { - sNumber += *pNumber; - pNumber++; - } else { - break; - } - } - if (sNumber.length() > 0) { - value = CharToFloat(sNumber.c_str()); - bSucceed = true; - } - return bSucceed; -} -# 1009 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -bool findNextVariableValue(char * &pVarname, float &value) -{ - bool succeed = true; - value = 0; - String sVarName = ""; - while (*pVarname) { - if (isalpha(*pVarname) || isdigit(*pVarname)) { - sVarName.concat(*pVarname); - pVarname++; - } else { - break; - } - } - sVarName.toUpperCase(); - if (sVarName.startsWith(F("VAR"))) { - int index = sVarName.substring(3).toInt(); - if (index > 0 && index <= MAX_RULE_VARS) { - value = CharToFloat(rules_vars[index -1]); - } - } else if (sVarName.startsWith(F("MEM"))) { - int index = sVarName.substring(3).toInt(); - if (index > 0 && index <= MAX_RULE_MEMS) { - value = CharToFloat(SettingsText(SET_MEM1 + index -1)); - } - } else if (sVarName.equals(F("TIME"))) { - value = MinutesPastMidnight(); - } else if (sVarName.equals(F("UPTIME"))) { - value = MinutesUptime(); - } else if (sVarName.equals(F("UTCTIME"))) { - value = UtcTime(); - } else if (sVarName.equals(F("LOCALTIME"))) { - value = LocalTime(); -#if defined(USE_TIMERS) && defined(USE_SUNRISE) - } else if (sVarName.equals(F("SUNRISE"))) { - value = SunMinutes(0); - } else if (sVarName.equals(F("SUNSET"))) { - value = SunMinutes(1); -#endif - } else { - succeed = false; - } - - return succeed; -} -# 1071 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -bool findNextObjectValue(char * &pointer, float &value) -{ - bool bSucceed = false; - while (*pointer) - { - if (isspace(*pointer)) { - pointer++; - continue; - } - if (isdigit(*pointer) || (*pointer) == '-') { - bSucceed = findNextNumber(pointer, value); - break; - } else if (isalpha(*pointer)) { - bSucceed = findNextVariableValue(pointer, value); - break; - } else if (*pointer == '(') { - char * closureBracket = findClosureBracket(pointer); - if (closureBracket != nullptr) { - value = evaluateExpression(pointer+1, closureBracket - pointer - 1); - pointer = closureBracket + 1; - bSucceed = true; - } - break; - } else { - break; - } - } - return bSucceed; -} -# 1115 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -bool findNextOperator(char * &pointer, int8_t &op) -{ - bool bSucceed = false; - while (*pointer) - { - if (isspace(*pointer)) { - pointer++; - continue; - } - op = EXPRESSION_OPERATOR_ADD; - const char *pch = kExpressionOperators; - char ch; - while ((ch = pgm_read_byte(pch++)) != '\0') { - if (ch == *pointer) { - bSucceed = true; - pointer++; - break; - } - op++; - } - break; - } - return bSucceed; -} -# 1152 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -float calculateTwoValues(float v1, float v2, uint8_t op) -{ - switch (op) - { - case EXPRESSION_OPERATOR_ADD: - return v1 + v2; - case EXPRESSION_OPERATOR_SUBTRACT: - return v1 - v2; - case EXPRESSION_OPERATOR_MULTIPLY: - return v1 * v2; - case EXPRESSION_OPERATOR_DIVIDEDBY: - return (0 == v2) ? 0 : (v1 / v2); - case EXPRESSION_OPERATOR_MODULO: - return (0 == v2) ? 0 : (int(v1) % int(v2)); - case EXPRESSION_OPERATOR_POWER: - return FastPrecisePow(v1, v2); - } - return 0; -} -# 1205 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -float evaluateExpression(const char * expression, unsigned int len) -{ - char expbuf[len + 1]; - memcpy(expbuf, expression, len); - expbuf[len] = '\0'; - char * scan_pointer = expbuf; - - LinkedList object_values; - LinkedList operators; - int8_t op; - float va; - - if (findNextObjectValue(scan_pointer, va)) { - object_values.add(va); - } else { - return 0; - } - while (*scan_pointer) - { - if (findNextOperator(scan_pointer, op) - && *scan_pointer - && findNextObjectValue(scan_pointer, va)) - { - operators.add(op); - object_values.add(va); - } else { - - break; - } - } - - - - for (int32_t priority = MAX_EXPRESSION_OPERATOR_PRIORITY; priority>0; priority--) { - int index = 0; - while (index < operators.size()) { - if (priority == pgm_read_byte(kExpressionOperatorsPriorities + operators.get(index))) { - - va = calculateTwoValues(object_values.get(index), object_values.remove(index + 1), operators.remove(index)); - - object_values.set(index, va); - } else { - index++; - } - } - } - return object_values.get(0); -} -#endif - -#ifdef SUPPORT_IF_STATEMENT -void CmndIf(void) -{ - if (XdrvMailbox.data_len > 0) { - char parameters[XdrvMailbox.data_len+1]; - memcpy(parameters, XdrvMailbox.data, XdrvMailbox.data_len); - parameters[XdrvMailbox.data_len] = '\0'; - ProcessIfStatement(parameters); - } - ResponseCmndDone(); -} -# 1279 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -bool evaluateComparisonExpression(const char *expression, int len) -{ - bool bResult = true; - char expbuf[len + 1]; - memcpy(expbuf, expression, len); - expbuf[len] = '\0'; - String compare_expression = expbuf; - String leftExpr, rightExpr; - int8_t compareOp = parseCompareExpression(compare_expression, leftExpr, rightExpr); - - double leftValue = evaluateExpression(leftExpr.c_str(), leftExpr.length()); - double rightValue = evaluateExpression(rightExpr.c_str(), rightExpr.length()); - switch (compareOp) { - case COMPARE_OPERATOR_EXACT_DIVISION: - bResult = (rightValue != 0 && leftValue == int(leftValue) - && rightValue == int(rightValue) && (int(leftValue) % int(rightValue)) == 0); - break; - case COMPARE_OPERATOR_EQUAL: - bResult = leftExpr.equalsIgnoreCase(rightExpr); - break; - case COMPARE_OPERATOR_BIGGER: - bResult = (leftValue > rightValue); - break; - case COMPARE_OPERATOR_SMALLER: - bResult = (leftValue < rightValue); - break; - case COMPARE_OPERATOR_NUMBER_EQUAL: - bResult = (leftValue == rightValue); - break; - case COMPARE_OPERATOR_NOT_EQUAL: - bResult = (leftValue != rightValue); - break; - case COMPARE_OPERATOR_BIGGER_EQUAL: - bResult = (leftValue >= rightValue); - break; - case COMPARE_OPERATOR_SMALLER_EQUAL: - bResult = (leftValue <= rightValue); - break; - } - return bResult; -} -# 1335 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -bool findNextLogicOperator(char * &pointer, int8_t &op) -{ - bool bSucceed = false; - while (*pointer && isspace(*pointer)) { - - pointer++; - } - if (*pointer) { - if (strncasecmp_P(pointer, PSTR("AND "), 4) == 0) { - op = LOGIC_OPERATOR_AND; - pointer += 4; - bSucceed = true; - } else if (strncasecmp_P(pointer, PSTR("OR "), 3) == 0) { - op = LOGIC_OPERATOR_OR; - pointer += 3; - bSucceed = true; - } - } - return bSucceed; -} -# 1372 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -bool findNextLogicObjectValue(char * &pointer, bool &value) -{ - bool bSucceed = false; - while (*pointer && isspace(*pointer)) { - - pointer++; - } - char * pExpr = pointer; - while (*pointer) { - if (isalpha(*pointer) - && (strncasecmp_P(pointer, PSTR("AND "), 4) == 0 - || strncasecmp_P(pointer, PSTR("OR "), 3) == 0)) - { - value = evaluateComparisonExpression(pExpr, pointer - pExpr); - bSucceed = true; - break; - } else if (*pointer == '(') { - char * closureBracket = findClosureBracket(pointer); - if (closureBracket != nullptr) { - value = evaluateLogicalExpression(pointer+1, closureBracket - pointer - 1); - pointer = closureBracket + 1; - bSucceed = true; - } - break; - } - pointer++; - } - if (!bSucceed && pointer > pExpr) { - - value = evaluateComparisonExpression(pExpr, pointer - pExpr); - bSucceed = true; - } - return bSucceed; -} -# 1421 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -bool evaluateLogicalExpression(const char * expression, int len) -{ - bool bResult = false; - - char expbuff[len + 1]; - memcpy(expbuff, expression, len); - expbuff[len] = '\0'; - - - char * pointer = expbuff; - LinkedList values; - LinkedList logicOperators; - - bool bValue; - if (findNextLogicObjectValue(pointer, bValue)) { - values.add(bValue); - } else { - return false; - } - int8_t op; - while (*pointer) { - if (findNextLogicOperator(pointer, op) - && (*pointer) && findNextLogicObjectValue(pointer, bValue)) - { - logicOperators.add(op); - values.add(bValue); - } else { - break; - } - } - - int index = 0; - while (index < logicOperators.size()) { - if (logicOperators.get(index) == LOGIC_OPERATOR_AND) { - values.set(index, values.get(index) && values.get(index+1)); - values.remove(index + 1); - logicOperators.remove(index); - } else { - index++; - } - } - - index = 0; - while (index < logicOperators.size()) { - if (logicOperators.get(index) == LOGIC_OPERATOR_OR) { - values.set(index, values.get(index) || values.get(index+1)); - values.remove(index + 1); - logicOperators.remove(index); - } else { - index++; - } - } - return values.get(0); -} -# 1492 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -int8_t findIfBlock(char * &pointer, int &lenWord, int8_t block_type) -{ - int8_t foundBlock = IF_BLOCK_INVALID; - - const char * word; - while (*pointer) { - if (!isalpha(*pointer)) { - pointer++; - continue; - } - word = pointer; - while (*pointer && isalpha(*pointer)) { - pointer++; - } - lenWord = pointer - word; - - if (2 == lenWord && 0 == strncasecmp_P(word, PSTR("IF"), 2)) { - - - if (findIfBlock(pointer, lenWord, IF_BLOCK_ENDIF) != IF_BLOCK_ENDIF) { - - break; - } - } else if ( (IF_BLOCK_ENDIF == block_type || IF_BLOCK_ANY == block_type) - && (5 == lenWord) && (0 == strncasecmp_P(word, PSTR("ENDIF"), 5))) - { - - foundBlock = IF_BLOCK_ENDIF; - break; - } else if ( (IF_BLOCK_ELSEIF == block_type || IF_BLOCK_ANY == block_type) - && (6 == lenWord) && (0 == strncasecmp_P(word, PSTR("ELSEIF"), 6))) - { - - foundBlock = IF_BLOCK_ELSEIF; - break; - } else if ( (IF_BLOCK_ELSE == block_type || IF_BLOCK_ANY == block_type) - && (4 == lenWord) && (0 == strncasecmp_P(word, PSTR("ELSE"), 4))) - { - - foundBlock = IF_BLOCK_ELSE; - break; - } - } - return foundBlock; -} -# 1549 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -void ExecuteCommandBlock(const char * commands, int len) -{ - char cmdbuff[len + 1]; - memcpy(cmdbuff, commands, len); - cmdbuff[len] = '\0'; - - - char oneCommand[len + 1]; - int insertPosition = 0; - char * pos = cmdbuff; - int lenEndBlock = 0; - while (*pos) { - if (isspace(*pos) || '\x1e' == *pos || ';' == *pos) { - pos++; - continue; - } - if (strncasecmp_P(pos, PSTR("BACKLOG "), 8) == 0) { - - pos += 8; - continue; - } - if (strncasecmp_P(pos, PSTR("IF "), 3) == 0) { - - - char *pEndif = pos + 3; - if (IF_BLOCK_ENDIF != findIfBlock(pEndif, lenEndBlock, IF_BLOCK_ENDIF)) { - - break; - } - - memcpy(oneCommand, pos, pEndif - pos); - oneCommand[pEndif - pos] = '\0'; - pos = pEndif; - } else { - - char *pEndOfCommand = strpbrk(pos, "\x1e;"); - if (NULL == pEndOfCommand) { - pEndOfCommand = pos + strlen(pos); - } - memcpy(oneCommand, pos, pEndOfCommand - pos); - oneCommand[pEndOfCommand - pos] = '\0'; - pos = pEndOfCommand; - } - - - String sCurrentCommand = oneCommand; - sCurrentCommand.trim(); - if (sCurrentCommand.length() > 0 - && backlog.size() < MAX_BACKLOG && !backlog_mutex) - { - - backlog_mutex = true; - backlog.add(insertPosition, sCurrentCommand); - backlog_mutex = false; - insertPosition++; - } - } - return; -} -# 1619 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -void ProcessIfStatement(const char* statements) -{ - String conditionExpression; - int len = strlen(statements); - char statbuff[len + 1]; - memcpy(statbuff, statements, len + 1); - char *pos = statbuff; - int lenEndBlock = 0; - while (true) { - - - while (*pos && *pos != '(') { - pos++; - } - if (0 == *pos) { break; } - char * posEnd = findClosureBracket(pos); - - if (true == evaluateLogicalExpression(pos + 1, posEnd - (pos + 1))) { - - char * cmdBlockStart = posEnd + 1; - char * cmdBlockEnd = cmdBlockStart; - int8_t nextBlock = findIfBlock(cmdBlockEnd, lenEndBlock, IF_BLOCK_ANY); - if (IF_BLOCK_INVALID == nextBlock) { - - break; - } - ExecuteCommandBlock(cmdBlockStart, cmdBlockEnd - cmdBlockStart - lenEndBlock); - pos = cmdBlockEnd; - break; - } else { - pos = posEnd + 1; - int8_t nextBlock = findIfBlock(pos, lenEndBlock, IF_BLOCK_ANY); - if (IF_BLOCK_ELSEIF == nextBlock) { - - continue; - } else if (IF_BLOCK_ELSE == nextBlock) { - - char * cmdBlockEnd = pos; - int8_t nextBlock = findIfBlock(cmdBlockEnd, lenEndBlock, IF_BLOCK_ENDIF); - if (IF_BLOCK_ENDIF != nextBlock) { - - break; - } - ExecuteCommandBlock(pos, cmdBlockEnd - pos - lenEndBlock); - break; - } else { - - break; - } - } - } -} -# 1683 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_rules.ino" -void RulesPreprocessCommand(char *pCommands) -{ - char * cmd = pCommands; - int lenEndBlock = 0; - while (*cmd) { - - if (';' == *cmd || isspace(*cmd)) { - cmd++; - } - else if (strncasecmp_P(cmd, PSTR("IF "), 3) == 0) { - - char * pIfStart = cmd; - char * pIfEnd = pIfStart + 3; - - if (IF_BLOCK_ENDIF == findIfBlock(pIfEnd, lenEndBlock, IF_BLOCK_ENDIF)) { - - cmd = pIfEnd; - - - while (pIfStart < pIfEnd) { - if (';' == *pIfStart) - *pIfStart = '\x1e'; - pIfStart++; - } - } - else { - break; - } - } - else { - while (*cmd && ';' != *cmd) { - cmd++; - } - } - } - return; -} -#endif - - - - - -void CmndRule(void) -{ - uint8_t index = XdrvMailbox.index; - if ((index > 0) && (index <= MAX_RULE_SETS)) { - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.rules[index -1]))) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10)) { - switch (XdrvMailbox.payload) { - case 0: - case 1: - bitWrite(Settings.rule_enabled, index -1, XdrvMailbox.payload); - break; - case 2: - bitWrite(Settings.rule_enabled, index -1, bitRead(Settings.rule_enabled, index -1) ^1); - break; - case 4: - case 5: - bitWrite(Settings.rule_once, index -1, XdrvMailbox.payload &1); - break; - case 6: - bitWrite(Settings.rule_once, index -1, bitRead(Settings.rule_once, index -1) ^1); - break; - case 8: - case 9: - bitWrite(Settings.rule_stop, index -1, XdrvMailbox.payload &1); - break; - case 10: - bitWrite(Settings.rule_stop, index -1, bitRead(Settings.rule_stop, index -1) ^1); - break; - } - } else { - int offset = 0; - if ('+' == XdrvMailbox.data[0]) { - offset = strlen(Settings.rules[index -1]); - if (XdrvMailbox.data_len < (sizeof(Settings.rules[index -1]) - offset -1)) { - XdrvMailbox.data[0] = ' '; - } else { - offset = -1; - } - } - if (offset != -1) { - strlcpy(Settings.rules[index -1] + offset, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(Settings.rules[index -1])); - } - } - Rules.triggers[index -1] = 0; - } - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\",\"Once\":\"%s\",\"StopOnError\":\"%s\",\"Free\":%d,\"Rules\":\"%s\"}"), - XdrvMailbox.command, index, GetStateText(bitRead(Settings.rule_enabled, index -1)), GetStateText(bitRead(Settings.rule_once, index -1)), - GetStateText(bitRead(Settings.rule_stop, index -1)), sizeof(Settings.rules[index -1]) - strlen(Settings.rules[index -1]) -1, Settings.rules[index -1]); - } -} - -void CmndRuleTimer(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_TIMERS)) { - if (XdrvMailbox.data_len > 0) { -#ifdef USE_EXPRESSION - float timer_set = evaluateExpression(XdrvMailbox.data, XdrvMailbox.data_len); - Rules.timer[XdrvMailbox.index -1] = (timer_set > 0) ? millis() + (1000 * timer_set) : 0; -#else - Rules.timer[XdrvMailbox.index -1] = (XdrvMailbox.payload > 0) ? millis() + (1000 * XdrvMailbox.payload) : 0; -#endif - } - mqtt_data[0] = '\0'; - for (uint32_t i = 0; i < MAX_RULE_TIMERS; i++) { - ResponseAppend_P(PSTR("%c\"T%d\":%d"), (i) ? ',' : '{', i +1, (Rules.timer[i]) ? (Rules.timer[i] - millis()) / 1000 : 0); - } - ResponseJsonEnd(); - } -} - -void CmndEvent(void) -{ - if (XdrvMailbox.data_len > 0) { - strlcpy(Rules.event_data, XdrvMailbox.data, sizeof(Rules.event_data)); - } - ResponseCmndDone(); -} - -void CmndVariable(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { - if (!XdrvMailbox.usridx) { - mqtt_data[0] = '\0'; - for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { - ResponseAppend_P(PSTR("%c\"Var%d\":\"%s\""), (i) ? ',' : '{', i +1, rules_vars[i]); - } - ResponseJsonEnd(); - } else { - if (XdrvMailbox.data_len > 0) { -#ifdef USE_EXPRESSION - if (XdrvMailbox.data[0] == '=') { - dtostrfd(evaluateExpression(XdrvMailbox.data + 1, XdrvMailbox.data_len - 1), Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); - } else { - strlcpy(rules_vars[XdrvMailbox.index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(rules_vars[XdrvMailbox.index -1])); - } -#else - strlcpy(rules_vars[XdrvMailbox.index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(rules_vars[XdrvMailbox.index -1])); -#endif - bitSet(Rules.vars_event, XdrvMailbox.index -1); - } - ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); - } - } -} - -void CmndMemory(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_MEMS)) { - if (!XdrvMailbox.usridx) { - ResponseCmndAll(SET_MEM1, MAX_RULE_MEMS); - } else { - if (XdrvMailbox.data_len > 0) { -#ifdef USE_EXPRESSION - if (XdrvMailbox.data[0] == '=') { - dtostrfd(evaluateExpression(XdrvMailbox.data + 1, XdrvMailbox.data_len - 1), Settings.flag2.calc_resolution, SettingsText(SET_MEM1 + XdrvMailbox.index -1)); - } else { - SettingsUpdateText(SET_MEM1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data); - } -#else - SettingsUpdateText(SET_MEM1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data); -#endif - bitSet(Rules.mems_event, XdrvMailbox.index -1); - } - ResponseCmndIdxChar(SettingsText(SET_MEM1 + XdrvMailbox.index -1)); - } - } -} - -void CmndCalcResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 7)) { - Settings.flag2.calc_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.calc_resolution); -} - -void CmndAddition(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { - if (XdrvMailbox.data_len > 0) { - float tempvar = CharToFloat(rules_vars[XdrvMailbox.index -1]) + CharToFloat(XdrvMailbox.data); - dtostrfd(tempvar, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); - bitSet(Rules.vars_event, XdrvMailbox.index -1); - } - ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); - } -} - -void CmndSubtract(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { - if (XdrvMailbox.data_len > 0) { - float tempvar = CharToFloat(rules_vars[XdrvMailbox.index -1]) - CharToFloat(XdrvMailbox.data); - dtostrfd(tempvar, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); - bitSet(Rules.vars_event, XdrvMailbox.index -1); - } - ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); - } -} - -void CmndMultiply(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { - if (XdrvMailbox.data_len > 0) { - float tempvar = CharToFloat(rules_vars[XdrvMailbox.index -1]) * CharToFloat(XdrvMailbox.data); - dtostrfd(tempvar, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); - bitSet(Rules.vars_event, XdrvMailbox.index -1); - } - ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); - } -} - -void CmndScale(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { - if (XdrvMailbox.data_len > 0) { - if (strstr(XdrvMailbox.data, ",") != nullptr) { - char sub_string[XdrvMailbox.data_len +1]; - - float valueIN = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 1)); - float fromLow = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)); - float fromHigh = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 3)); - float toLow = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)); - float toHigh = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 5)); - float value = map_double(valueIN, fromLow, fromHigh, toLow, toHigh); - dtostrfd(value, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); - bitSet(Rules.vars_event, XdrvMailbox.index -1); - } - } - ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); - } -} - -float map_double(float x, float in_min, float in_max, float out_min, float out_max) -{ - return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; -} - - - - - -bool Xdrv10(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_50_MSECOND: - RulesEvery50ms(); - break; - case FUNC_EVERY_100_MSECOND: - RulesEvery100ms(); - break; - case FUNC_EVERY_SECOND: - RulesEverySecond(); - break; - case FUNC_SET_POWER: - RulesSetPower(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kRulesCommands, RulesCommand); - break; - case FUNC_RULES_PROCESS: - result = RulesProcess(); - break; - case FUNC_SAVE_BEFORE_RESTART: - RulesSaveBeforeRestart(); - break; -#ifdef SUPPORT_MQTT_EVENT - case FUNC_MQTT_DATA: - result = RulesMqttData(); - break; -#endif - case FUNC_PRE_INIT: - RulesInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_scripter.ino" -# 21 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_scripter.ino" -#ifdef USE_SCRIPT -#ifndef USE_RULES -# 41 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_scripter.ino" -#define XDRV_10 10 -#define XI2C_37 37 - -#define SCRIPT_DEBUG 0 - - -#ifndef MAXVARS -#define MAXVARS 50 -#endif -#ifndef MAXSVARS -#define MAXSVARS 5 -#endif -#define MAXNVARS MAXVARS-MAXSVARS - -#define MAXFILT 5 -#define SCRIPT_SVARSIZE 20 -#define SCRIPT_MAXSSIZE 48 -#define SCRIPT_EOL '\n' -#define SCRIPT_FLOAT_PRECISION 2 -#define PMEM_SIZE sizeof(Settings.script_pram) -#define SCRIPT_MAXPERM (PMEM_SIZE)-4/sizeof(float) -#define MAX_SCRIPT_SIZE MAX_RULE_SIZE*MAX_RULE_SETS - - -uint32_t EncodeLightId(uint8_t relay_id); -uint32_t DecodeLightId(uint32_t hue_id); - -#if defined(ESP32) && defined(ESP32_SCRIPT_SIZE) && !defined(USE_24C256) && !defined(USE_SCRIPT_FATFS) - -#include "FS.h" -#include "SPIFFS.h" -void SaveFile(const char *name,const uint8_t *buf,uint32_t len) { - File file = SPIFFS.open(name, FILE_WRITE); - if (!file) return; - file.write(buf, len); - file.close(); -} - -#define FORMAT_SPIFFS_IF_FAILED true -uint8_t spiffs_mounted=0; - -void LoadFile(const char *name,uint8_t *buf,uint32_t len) { - if (!spiffs_mounted) { - if(!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)){ - - return; - } - spiffs_mounted=1; - } - File file = SPIFFS.open(name); - if (!file) return; - file.read(buf, len); - file.close(); -} -#endif - - -#define EPOCH_OFFSET 1546300800 - -enum {OPER_EQU=1,OPER_PLS,OPER_MIN,OPER_MUL,OPER_DIV,OPER_PLSEQU,OPER_MINEQU,OPER_MULEQU,OPER_DIVEQU,OPER_EQUEQU,OPER_NOTEQU,OPER_GRTEQU,OPER_LOWEQU,OPER_GRT,OPER_LOW,OPER_PERC,OPER_XOR,OPER_AND,OPER_OR,OPER_ANDEQU,OPER_OREQU,OPER_XOREQU,OPER_PERCEQU}; -enum {SCRIPT_LOGLEVEL=1,SCRIPT_TELEPERIOD}; - -#ifdef USE_SCRIPT_FATFS -#include -#include -#ifndef FAT_SCRIPT_SIZE -#define FAT_SCRIPT_SIZE 4096 -#endif -#define FAT_SCRIPT_NAME "script.txt" -#if USE_LONG_FILE_NAMES==1 -#warning ("FATFS long filenames not supported"); -#endif -#if USE_STANDARD_SPI_LIBRARY==0 -#warning ("FATFS standard spi should be used"); -#endif -#endif - -#ifdef SUPPORT_MQTT_EVENT - #include - typedef struct { - String Event; - String Topic; - String Key; - } MQTT_Subscription; - LinkedList subscriptions; -#endif - -#ifdef USE_DISPLAY -#ifdef USE_TOUCH_BUTTONS -#include -extern VButton *buttons[MAXBUTTONS]; -#endif -#endif - -typedef union { - uint8_t data; - struct { - uint8_t is_string : 1; - uint8_t is_permanent : 1; - uint8_t is_timer : 1; - uint8_t is_autoinc : 1; - uint8_t changed : 1; - uint8_t settable : 1; - uint8_t is_filter : 1; - uint8_t constant : 1; - }; -} SCRIPT_TYPE; - -struct T_INDEX { - uint8_t index; - SCRIPT_TYPE bits; -}; - -struct M_FILT { - uint8_t numvals; - uint8_t index; - float maccu; - float rbuff[1]; -}; - -typedef union { - uint8_t data; - struct { - uint8_t nutu8 : 1; - uint8_t nutu7 : 1; - uint8_t nutu6 : 1; - uint8_t nutu5 : 1; - uint8_t nutu4 : 1; - uint8_t nutu3 : 1; - uint8_t is_dir : 1; - uint8_t is_open : 1; - }; -} FILE_FLAGS; - -#define SFS_MAX 4 - -struct SCRIPT_MEM { - float *fvars; - float *s_fvars; - struct T_INDEX *type; - struct M_FILT *mfilt; - char *glob_vnp; - uint8_t *vnp_offset; - char *glob_snp; - char *scriptptr; - char *section_ptr; - char *scriptptr_bu; - char *script_ram; - uint16_t script_size; - uint8_t *script_pram; - uint16_t script_pram_size; - uint8_t numvars; - void *script_mem; - uint16_t script_mem_size; - uint8_t script_dprec; - uint8_t var_not_found; - uint8_t glob_error; - uint8_t max_ssize; - uint8_t script_loglevel; - uint8_t flags; -#ifdef USE_SCRIPT_FATFS - File files[SFS_MAX]; - FILE_FLAGS file_flags[SFS_MAX]; - uint8_t script_sd_found; - char flink[2][14]; -#endif -} glob_script_mem; - - -int16_t last_findex; -uint8_t tasm_cmd_activ=0; -uint8_t fast_script=0; -uint32_t script_lastmillis; - - -#ifdef USE_BUTTON_EVENT -int8_t script_button[MAX_KEYS]; -#endif - -char *GetNumericResult(char *lp,uint8_t lastop,float *fp,JsonObject *jo); -char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo); -char *ForceStringVar(char *lp,char *dstr); -void send_download(void); -uint8_t reject(char *name); - -void ScriptEverySecond(void) { - - if (bitRead(Settings.rule_enabled, 0)) { - struct T_INDEX *vtp=glob_script_mem.type; - float delta=(millis()-script_lastmillis)/1000; - script_lastmillis=millis(); - for (uint8_t count=0; count0) { - - *fp-=delta; - if (*fp<0) *fp=0; - } - } - if (vtp[count].bits.is_autoinc) { - - float *fp=&glob_script_mem.fvars[vtp[count].index]; - if (*fp>=0) { - *fp+=delta; - } - } - } - Run_Scripter(">S",2,0); - } -} - -void RulesTeleperiod(void) { - if (bitRead(Settings.rule_enabled, 0) && mqtt_data[0]) Run_Scripter(">T",2, mqtt_data); -} - - -#ifdef USE_24C256 -#ifndef USE_SCRIPT_FATFS - -#include -#define EEPROM_ADDRESS 0x50 - -#ifndef EEP_SCRIPT_SIZE -#define EEP_SCRIPT_SIZE 4095 -#endif - - -static Eeprom24C128_256 eeprom(EEPROM_ADDRESS); - -#define EEP_WRITE(A,B,C) eeprom.writeBytes(A,B,(uint8_t*)C); - -#define EEP_READ(A,B,C) eeprom.readBytes(A,B,(uint8_t*)C); -#endif -#endif - -#define SCRIPT_SKIP_SPACES while (*lp==' ' || *lp=='\t') lp++; -#define SCRIPT_SKIP_EOL while (*lp==SCRIPT_EOL) lp++; - - -int16_t Init_Scripter(void) { -char *script; - - script=glob_script_mem.script_ram; - - - uint16_t lines=0,nvars=0,svars=0,vars=0; - char *lp=script; - char vnames[MAXVARS*10]; - char *vnames_p=vnames; - char *vnp[MAXVARS]; - char **vnp_p=vnp; - char strings[MAXSVARS*SCRIPT_MAXSSIZE]; - struct M_FILT mfilt[MAXFILT]; - - char *strings_p=strings; - char *snp[MAXSVARS]; - char **snp_p=snp; - uint8_t numperm=0,numflt=0,count; - - glob_script_mem.max_ssize=SCRIPT_SVARSIZE; - glob_script_mem.scriptptr=0; - - if (!*script) return -999; - - float fvalues[MAXVARS]; - struct T_INDEX vtypes[MAXVARS]; - char init=0; - while (1) { - - - SCRIPT_SKIP_SPACES - - if (*lp=='\n' || *lp=='\r') goto next_line; - - if (*lp==';') goto next_line; - if (init) { - - if (*lp=='>') { - init=0; - break; - } - char *op=strchr(lp,'='); - if (op) { - vtypes[vars].bits.data=0; - - if (*lp=='p' && *(lp+1)==':') { - lp+=2; - if (numpermMAXFILT) { - return -6; - } - } else { - vtypes[vars].bits.is_filter=0; - } - *vnp_p++=vnames_p; - while (lpMAXNVARS) { - return -1; - } - if (vtypes[vars].bits.is_filter) { - while (isdigit(*op) || *op=='.' || *op=='-') { - op++; - } - while (*op==' ') op++; - if (isdigit(*op)) { - - uint8_t flen=atoi(op); - mfilt[numflt-1].numvals&=0x80; - mfilt[numflt-1].numvals|=flen&0x7f; - } - } - - } else { - - op++; - *snp_p++=strings_p; - while (*op!='\"') { - if (*op==SCRIPT_EOL) break; - *strings_p++=*op++; - } - *strings_p++=0; - vtypes[vars].bits.is_string=1; - vtypes[vars].index=svars; - svars++; - if (svars>MAXSVARS) { - return -2; - } - } - vars++; - if (vars>MAXVARS) { - return -3; - } - } - } else { - if (!strncmp(lp,">D",2)) { - lp+=2; - SCRIPT_SKIP_SPACES - if (isdigit(*lp)) { - uint8_t ssize=atoi(lp)+1; - if (ssize<10 || ssize>SCRIPT_MAXSSIZE) ssize=SCRIPT_MAXSSIZE; - glob_script_mem.max_ssize=ssize; - } - init=1; - } - } - - next_line: - lp = strchr(lp, SCRIPT_EOL); - if (!lp) break; - lp++; - } - - uint16_t fsize=0; - for (count=0; count255) { - free(glob_script_mem.script_mem); - return -5; - } - } - - AddLog_P2(LOG_LEVEL_INFO, PSTR("Script: nv=%d, tv=%d, vns=%d, ram=%d"), nvars, svars, index, glob_script_mem.script_mem_size); - - - char *cp1=glob_script_mem.glob_snp; - char *sp=strings; - for (count=0; countnumvals=mfilt[count].numvals; - mp+=sizeof(struct M_FILT)+((mfilt[count].numvals&0x7f)-1)*sizeof(float); - } - - glob_script_mem.numvars=vars; - glob_script_mem.script_dprec=SCRIPT_FLOAT_PRECISION; - glob_script_mem.script_loglevel=LOG_LEVEL_INFO; - - -#if SCRIPT_DEBUG>2 - struct T_INDEX *dvtp=glob_script_mem.type; - for (uint8_t count=0; count0 - ClaimSerial(); - SetSerialBaudrate(9600); -#endif - - - glob_script_mem.scriptptr=lp-1; - glob_script_mem.scriptptr_bu=glob_script_mem.scriptptr; - return 0; - -} - -#ifdef USE_LIGHT -#ifdef USE_WS2812 -void ws2812_set_array(float *array ,uint8_t len) { - - Ws2812ForceSuspend(); - for (uint8_t cnt=0;cntSettings.light_pixels) break; - uint32_t col=array[cnt]; - Ws2812SetColor(cnt+1,col>>16,col>>8,col,0); - } - Ws2812ForceUpdate(); -} -#endif -#endif - -#define NUM_RES 0xfe -#define STR_RES 0xfd -#define VAR_NV 0xff - -#define NTYPE 0 -#define STYPE 0x80 - -#ifndef FLT_MAX -#define FLT_MAX 99999999 -#endif - -float median_array(float *array,uint8_t len) { - uint8_t ind[len]; - uint8_t mind=0,index=0,flg; - float min=FLT_MAX; - - for (uint8_t hcnt=0; hcntnumvals&0x7f; - return mflp->rbuff; - } - mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); - } - return 0; -} - - -float Get_MFVal(uint8_t index,uint8_t bind) { - uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; - for (uint8_t count=0; countnumvals&0x7f; - if (!bind) { - return mflp->index; - } - if (bind<1 || bind>maxind) bind=maxind; - return mflp->rbuff[bind-1]; - } - mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); - } - return 0; -} - -void Set_MFVal(uint8_t index,uint8_t bind,float val) { - uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; - for (uint8_t count=0; countnumvals&0x7f; - if (!bind) { - mflp->index=val; - } else { - if (bind<1 || bind>maxind) bind=maxind; - mflp->rbuff[bind-1]=val; - } - return; - } - mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); - } -} - - -float Get_MFilter(uint8_t index) { - uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; - for (uint8_t count=0; countnumvals&0x80) { - - return mflp->maccu/(mflp->numvals&0x7f); - } else { - - return median_array(mflp->rbuff,mflp->numvals); - } - } - mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); - } - return 0; -} - -void Set_MFilter(uint8_t index, float invar) { - uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; - for (uint8_t count=0; countnumvals&0x80) { - - mflp->maccu-=mflp->rbuff[mflp->index]; - mflp->maccu+=invar; - mflp->rbuff[mflp->index]=invar; - mflp->index++; - if (mflp->index>=(mflp->numvals&0x7f)) mflp->index=0; - } else { - - mflp->rbuff[mflp->index]=invar; - mflp->index++; - if (mflp->index>=mflp->numvals) mflp->index=0; - } - break; - } - mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); - } -} - -#define MEDIAN_SIZE 5 -#define MEDIAN_FILTER_NUM 2 - -struct MEDIAN_FILTER { -float buffer[MEDIAN_SIZE]; -int8_t index; -} script_mf[MEDIAN_FILTER_NUM]; - -float DoMedian5(uint8_t index, float in) { - - if (index>=MEDIAN_FILTER_NUM) index=0; - - struct MEDIAN_FILTER* mf=&script_mf[index]; - mf->buffer[mf->index]=in; - mf->index++; - if (mf->index>=MEDIAN_SIZE) mf->index=0; - return median_array(mf->buffer,MEDIAN_SIZE); -} - -#ifdef USE_LIGHT - -uint32_t HSVToRGB(uint16_t hue, uint8_t saturation, uint8_t value) { -float r = 0, g = 0, b = 0; -struct HSV { - float H; - float S; - float V; -} hsv; - -hsv.H=hue; -hsv.S=(float)saturation/100.0; -hsv.V=(float)value/100.0; - -if (hsv.S == 0) { - r = hsv.V; - g = hsv.V; - b = hsv.V; - } else { - int i; - float f, p, q, t; - - if (hsv.H == 360) - hsv.H = 0; - else - hsv.H = hsv.H / 60; - - i = (int)trunc(hsv.H); - f = hsv.H - i; - - p = hsv.V * (1.0 - hsv.S); - q = hsv.V * (1.0 - (hsv.S * f)); - t = hsv.V * (1.0 - (hsv.S * (1.0 - f))); - - switch (i) - { - case 0: - r = hsv.V; - g = t; - b = p; - break; - - case 1: - r = q; - g = hsv.V; - b = p; - break; - - case 2: - r = p; - g = hsv.V; - b = t; - break; - - case 3: - r = p; - g = q; - b = hsv.V; - break; - - case 4: - r = t; - g = p; - b = hsv.V; - break; - - default: - r = hsv.V; - g = p; - b = q; - break; - } - - } - - uint8_t ir,ig,ib; - ir=r*255; - ig=g*255; - ib=b*255; - - uint32_t rgb=(ir<<16)|(ig<<8)|ib; - return rgb; -} -#endif - - - - -char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,JsonObject *jo) { - uint16_t count,len=0; - uint8_t nres=0; - char vname[32]; - float fvar=0; - tind->index=0; - tind->bits.data=0; - - if (isdigit(*lp) || (*lp=='-' && isdigit(*(lp+1))) || *lp=='.') { - - if (fp) { - if (*lp=='0' && *(lp+1)=='x') { - lp+=2; - *fp=strtol(lp,0,16); - } else { - *fp=CharToFloat(lp); - } - } - if (*lp=='-') lp++; - while (isdigit(*lp) || *lp=='.') { - if (*lp==0 || *lp==SCRIPT_EOL) break; - lp++; - } - tind->bits.constant=1; - tind->bits.is_string=0; - *vtype=NUM_RES; - return lp; - } - if (*lp=='"') { - lp++; - while (*lp!='"') { - if (*lp==0 || *lp==SCRIPT_EOL) break; - uint8_t iob=*lp; - if (iob=='\\') { - lp++; - if (*lp=='t') { - iob='\t'; - } else if (*lp=='n') { - iob='\n'; - } else if (*lp=='r') { - iob='\r'; - } else if (*lp=='\\') { - iob='\\'; - } else { - lp--; - } - if (sp) *sp++=iob; - } else { - if (sp) *sp++=iob; - } - lp++; - } - if (sp) *sp=0; - *vtype=STR_RES; - tind->bits.constant=1; - tind->bits.is_string=1; - return lp+1; - } - - if (*lp=='-') { - - nres=1; - lp++; - } - - const char *term="\n\r ])=+-/*%>index=VAR_NV; - glob_script_mem.var_not_found=1; - return lp; - } - - struct T_INDEX *vtp=glob_script_mem.type; - char dvnam[32]; - strcpy (dvnam,vname); - uint8_t olen=len; - last_findex=-1; - char *ja=strchr(dvnam,'['); - if (ja) { - *ja=0; - ja++; - olen=strlen(dvnam); - } - for (count=0; countindex=count; - if (vtp[count].bits.is_string==0) { - *vtype=NTYPE|index; - if (vtp[count].bits.is_filter) { - if (ja) { - lp+=olen+1; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - last_findex=fvar; - fvar=Get_MFVal(index,fvar); - len=1; - } else { - fvar=Get_MFilter(index); - } - } else { - fvar=glob_script_mem.fvars[index]; - } - if (nres) fvar=-fvar; - if (fp) *fp=fvar; - } else { - *vtype=STYPE|index; - if (sp) strlcpy(sp,glob_script_mem.glob_snp+(index*glob_script_mem.max_ssize),SCRIPT_MAXSSIZE); - } - return lp+len; - } - } - } - - if (jo) { - - const char* str_value; - uint8_t aindex; - String vn; - char *ja=strchr(vname,'['); - if (ja) { - - *ja=0; - ja++; - - float fvar; - GetNumericResult(ja,OPER_EQU,&fvar,0); - aindex=fvar; - if (aindex<1 || aindex>6) aindex=1; - aindex--; - } - if (jo->success()) { - char *subtype=strchr(vname,'#'); - char *subtype2; - if (subtype) { - *subtype=0; - subtype++; - subtype2=strchr(subtype,'#'); - if (subtype2) { - *subtype2=0; - *subtype2++; - } - } - vn=vname; - str_value = (*jo)[vn]; - if ((*jo)[vn].success()) { - if (subtype) { - JsonObject &jobj1=(*jo)[vn]; - if (jobj1.success()) { - vn=subtype; - jo=&jobj1; - str_value = (*jo)[vn]; - if ((*jo)[vn].success()) { - - if (subtype2) { - JsonObject &jobj2=(*jo)[vn]; - if ((*jo)[vn].success()) { - vn=subtype2; - jo=&jobj2; - str_value = (*jo)[vn]; - if ((*jo)[vn].success()) { - goto skip; - } else { - goto chknext; - } - } else { - goto chknext; - } - } - - goto skip; - } - } else { - goto chknext; - } - } - skip: - if (ja) { - - str_value = (*jo)[vn][aindex]; - } - if (str_value && *str_value) { - if ((*jo).is(vn)) { - if (!strncmp(str_value,"ON",2)) { - if (fp) *fp=1; - goto nexit; - } else if (!strncmp(str_value,"OFF",3)) { - if (fp) *fp=0; - goto nexit; - } else { - *vtype=STR_RES; - tind->bits.constant=1; - tind->bits.is_string=1; - if (sp) strlcpy(sp,str_value,SCRIPT_MAXSSIZE); - return lp+len; - } - - } else { - if (fp) { - if (!strncmp(vn.c_str(),"Epoch",5)) { - *fp=atoi(str_value)-(uint32_t)EPOCH_OFFSET; - } else { - *fp=CharToFloat((char*)str_value); - } - } - nexit: - *vtype=NUM_RES; - tind->bits.constant=1; - tind->bits.is_string=0; - return lp+len; - } - } - } - } - } - -chknext: - switch (vname[0]) { - case 'a': -#ifdef USE_ANGLE_FUNC - if (!strncmp(vname,"acos(",5)) { - lp+=5; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - fvar=acosf(fvar); - lp++; - len=0; - goto exit; - } -#endif - break; - - case 'b': - if (!strncmp(vname,"boot",4)) { - if (rules_flag.system_boot) { - rules_flag.system_boot=0; - fvar=1; - } - goto exit; - } -#ifdef USE_BUTTON_EVENT - if (!strncmp(vname,"bt[",3)) { - - GetNumericResult(vname+3,OPER_EQU,&fvar,0); - uint32_t index=fvar; - if (index<1 || index>MAX_KEYS) index=1; - fvar=script_button[index-1]; - script_button[index-1]|=0x80; - len++; - goto exit; - } -#endif - break; - case 'c': - if (!strncmp(vname,"chg[",4)) { - - struct T_INDEX ind; - uint8_t vtype; - isvar(vname+4,&vtype,&ind,0,0,0); - if (!ind.bits.constant) { - uint8_t index=glob_script_mem.type[ind.index].index; - if (glob_script_mem.fvars[index]!=glob_script_mem.s_fvars[index]) { - - glob_script_mem.s_fvars[index]=glob_script_mem.fvars[index]; - fvar=1; - len++; - goto exit; - } else { - fvar=0; - len++; - goto exit; - } - } - } - break; - case 'd': - if (!strncmp(vname,"day",3)) { - fvar=RtcTime.day_of_month; - goto exit; - } - break; - case 'e': - if (!strncmp(vname,"epoch",5)) { - fvar=UtcTime()-(uint32_t)EPOCH_OFFSET; - goto exit; - } - break; -#ifdef USE_SCRIPT_FATFS - case 'f': - if (!strncmp(vname,"fo(",3)) { - lp+=3; - char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,0); - while (*lp==' ') lp++; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - uint8_t mode=fvar; - fvar=-1; - for (uint8_t cnt=0;cnt=SFS_MAX) ind=SFS_MAX-1; - glob_script_mem.files[ind].close(); - glob_script_mem.file_flags[ind].is_open=0; - fvar=0; - lp++; - len=0; - goto exit; - } - if (!strncmp(vname,"ff(",3)) { - lp+=3; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - uint8_t ind=fvar; - if (ind>=SFS_MAX) ind=SFS_MAX-1; - glob_script_mem.files[ind].flush(); - fvar=0; - lp++; - len=0; - goto exit; - } - if (!strncmp(vname,"fw(",3)) { - lp+=3; - char str[SCRIPT_MAXSSIZE]; - lp=ForceStringVar(lp,str); - while (*lp==' ') lp++; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - uint8_t ind=fvar; - if (ind>=SFS_MAX) ind=SFS_MAX-1; - if (glob_script_mem.file_flags[ind].is_open) { - fvar=glob_script_mem.files[ind].print(str); - } else { - fvar=0; - } - lp++; - len=0; - goto exit; - } - if (!strncmp(vname,"fr(",3)) { - lp+=3; - struct T_INDEX ind; - uint8_t vtype; - uint8_t sindex=0; - lp=isvar(lp,&vtype,&ind,0,0,0); - if (vtype!=VAR_NV) { - - if ((vtype&STYPE)==0) { - - fvar=0; - goto exit; - } else { - - sindex=glob_script_mem.type[ind.index].index; - } - } else { - - fvar=0; - goto exit; - } - while (*lp==' ') lp++; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - uint8_t find=fvar; - if (find>=SFS_MAX) find=SFS_MAX-1; - uint8_t index=0; - char str[glob_script_mem.max_ssize+1]; - char *cp=str; - if (glob_script_mem.file_flags[find].is_open) { - if (glob_script_mem.file_flags[find].is_dir) { - while (true) { - File entry=glob_script_mem.files[find].openNextFile(); - if (entry) { - if (!reject((char*)entry.name())) { - strcpy(str,entry.name()); - entry.close(); - break; - } - } else { - *cp=0; - break; - } - entry.close(); - } - index=strlen(str); - } else { - while (glob_script_mem.files[find].available()) { - uint8_t buf[1]; - glob_script_mem.files[find].read(buf,1); - if (buf[0]=='\t' || buf[0]==',' || buf[0]=='\n' || buf[0]=='\r') { - break; - } else { - *cp++=buf[0]; - index++; - if (index>=glob_script_mem.max_ssize-1) break; - } - } - *cp=0; - } - } else { - strcpy(str,"file error"); - } - lp++; - strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); - fvar=index; - len=0; - goto exit; - } - if (!strncmp(vname,"fd(",3)) { - lp+=3; - char str[glob_script_mem.max_ssize+1]; - lp=GetStringResult(lp,OPER_EQU,str,0); - SD.remove(str); - lp++; - len=0; - goto exit; - } -#ifdef USE_SCRIPT_FATFS_EXT - if (!strncmp(vname,"fe(",3)) { - lp+=3; - char str[glob_script_mem.max_ssize+1]; - lp=GetStringResult(lp,OPER_EQU,str,0); - - File ef=SD.open(str); - if (ef) { - uint16_t fsiz=ef.size(); - if (fsiz<2048) { - char *script=(char*)calloc(fsiz+16,1); - if (script) { - ef.read((uint8_t*)script,fsiz); - execute_script(script); - free(script); - fvar=1; - } - } - ef.close(); - } - lp++; - len=0; - goto exit; - } - if (!strncmp(vname,"fmd(",4)) { - lp+=4; - char str[glob_script_mem.max_ssize+1]; - lp=GetStringResult(lp,OPER_EQU,str,0); - fvar=SD.mkdir(str); - lp++; - len=0; - goto exit; - } - if (!strncmp(vname,"frd(",4)) { - lp+=4; - char str[glob_script_mem.max_ssize+1]; - lp=GetStringResult(lp,OPER_EQU,str,0); - fvar=SD.rmdir(str); - lp++; - len=0; - goto exit; - } - if (!strncmp(vname,"fx(",3)) { - lp+=3; - char str[glob_script_mem.max_ssize+1]; - lp=GetStringResult(lp,OPER_EQU,str,0); - if (SD.exists(str)) fvar=1; - else fvar=0; - lp++; - len=0; - goto exit; - } -#endif - if (!strncmp(vname,"fl1(",4) || !strncmp(vname,"fl2(",4) ) { - uint8_t lknum=*(lp+2)&3; - lp+=4; - char str[glob_script_mem.max_ssize+1]; - lp=GetStringResult(lp,OPER_EQU,str,0); - if (lknum<1 || lknum>2) lknum=1; - strlcpy(glob_script_mem.flink[lknum-1],str,14); - lp++; - fvar=0; - len=0; - goto exit; - } - if (!strncmp(vname,"fsm",3)) { - fvar=glob_script_mem.script_sd_found; - - goto exit; - } - break; - -#endif - case 'g': - if (!strncmp(vname,"gtmp",4)) { - fvar=global_temperature; - goto exit; - } - if (!strncmp(vname,"ghum",4)) { - fvar=global_humidity; - goto exit; - } - if (!strncmp(vname,"gprs",4)) { - fvar=global_pressure; - goto exit; - } - if (!strncmp(vname,"gtopic",6)) { - if (sp) strlcpy(sp,SettingsText(SET_MQTT_GRP_TOPIC),glob_script_mem.max_ssize); - goto strexit; - } - break; - case 'h': - if (!strncmp(vname,"hours",5)) { - fvar=RtcTime.hour; - goto exit; - } - if (!strncmp(vname,"heap",4)) { - fvar=ESP.getFreeHeap(); - goto exit; - } - if (!strncmp(vname,"hn(",3)) { - lp=GetNumericResult(lp+3,OPER_EQU,&fvar,0); - if (fvar<0 || fvar>255) fvar=0; - lp++; - len=0; - if (sp) { - sprintf(sp,"%02x",(uint8_t)fvar); - } - goto strexit; - } - if (!strncmp(vname,"hx(",3)) { - lp=GetNumericResult(lp+3,OPER_EQU,&fvar,0); - lp++; - len=0; - if (sp) { - sprintf(sp,"%08x",(uint32_t)fvar); - } - goto strexit; - } -#ifdef USE_LIGHT - - if (!strncmp(vname,"hsvrgb(",7)) { - lp=GetNumericResult(lp+7,OPER_EQU,&fvar,0); - if (fvar<0 || fvar>360) fvar=0; - SCRIPT_SKIP_SPACES - - float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); - if (fvar2<0 || fvar2>100) fvar2=0; - SCRIPT_SKIP_SPACES - - float fvar3; - lp=GetNumericResult(lp,OPER_EQU,&fvar3,0); - if (fvar3<0 || fvar3>100) fvar3=0; - - fvar=HSVToRGB(fvar,fvar2,fvar3); - - lp++; - len=0; - goto exit; - } - -#endif - break; - case 'i': - if (!strncmp(vname,"int(",4)) { - lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0); - fvar=floor(fvar); - lp++; - len=0; - goto exit; - } - break; - case 'l': - if (!strncmp(vname,"loglvl",6)) { - fvar=glob_script_mem.script_loglevel; - tind->index=SCRIPT_LOGLEVEL; - exit_settable: - if (fp) *fp=fvar; - *vtype=NTYPE; - tind->bits.settable=1; - tind->bits.is_string=0; - return lp+len; - } - break; - case 'm': - if (!strncmp(vname,"med(",4)) { - float fvar1; - lp=GetNumericResult(lp+4,OPER_EQU,&fvar1,0); - SCRIPT_SKIP_SPACES - - float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); - fvar=DoMedian5(fvar1,fvar2); - lp++; - len=0; - goto exit; - } - if (!strncmp(vname,"micros",6)) { - fvar=micros(); - goto exit; - } - if (!strncmp(vname,"millis",6)) { - fvar=millis(); - goto exit; - } - if (!strncmp(vname,"mins",4)) { - fvar=RtcTime.minute; - goto exit; - } - if (!strncmp(vname,"month",5)) { - fvar=RtcTime.month; - goto exit; - } - if (!strncmp(vname,"mqttc",5)) { - if (rules_flag.mqtt_connected) { - rules_flag.mqtt_connected=0; - fvar=1; - } - goto exit; - } - if (!strncmp(vname,"mqttd",5)) { - if (rules_flag.mqtt_disconnected) { - rules_flag.mqtt_disconnected=0; - fvar=1; - } - goto exit; - } - if (!strncmp(vname,"mqtts",5)) { - fvar=!global_state.mqtt_down; - goto exit; - } - if (!strncmp(vname,"mp(",3)) { - lp+=3; - float fvar1; - lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); - SCRIPT_SKIP_SPACES - while (*lp!=')') { - char *opp=lp; - lp++; - float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); - SCRIPT_SKIP_SPACES - fvar=fvar1; - if ((*opp=='<' && fvar1' && fvar1>fvar2) || - (*opp=='=' && fvar1==fvar2)) - { - if (*lp!='<' && *lp!='>' && *lp!='=' && *lp!=')' && *lp!=SCRIPT_EOL) { - float fvar3; - lp=GetNumericResult(lp,OPER_EQU,&fvar3,0); - SCRIPT_SKIP_SPACES - fvar=fvar3; - } else { - fvar=fvar2; - } - break; - } - while (*lp!='<' && *lp!='>' && *lp!='=' && *lp!=')' && *lp!=SCRIPT_EOL) lp++; - } - len=0; - goto exit; - } - break; - case 'p': - if (!strncmp(vname,"pin[",4)) { - - GetNumericResult(vname+4,OPER_EQU,&fvar,0); - fvar=digitalRead((uint8_t)fvar); - - len++; - goto exit; - } - if (!strncmp(vname,"pn[",3)) { - GetNumericResult(vname+3,OPER_EQU,&fvar,0); - fvar=pin[(uint8_t)fvar]; - - len++; - goto exit; - } - if (!strncmp(vname,"pd[",3)) { - GetNumericResult(vname+3,OPER_EQU,&fvar,0); - uint8_t gpiopin=fvar; - for (uint8_t i=0;iMAX_COUNTERS) index=1; - fvar=RtcSettings.pulse_counter[index-1]; - len+=1; - goto exit; - } - break; - - case 'r': - if (!strncmp(vname,"ram",3)) { - fvar=glob_script_mem.script_mem_size+(glob_script_mem.script_size)+(PMEM_SIZE); - goto exit; - } - break; - case 's': - if (!strncmp(vname,"secs",4)) { - fvar=RtcTime.second; - goto exit; - } - if (!strncmp(vname,"sw[",3)) { - - GetNumericResult(vname+3,OPER_EQU,&fvar,0); - fvar=SwitchLastState((uint32_t)fvar); - - len++; - goto exit; - } - if (!strncmp(vname,"stack",5)) { - fvar=GetStack(); - goto exit; - } - if (!strncmp(vname,"slen",4)) { - fvar=strlen(glob_script_mem.script_ram); - goto exit; - } - if (!strncmp(vname,"sl(",3)) { - lp+=3; - char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,0); - lp++; - len=0; - fvar=strlen(str); - goto exit; - } - if (!strncmp(vname,"sb(",3)) { - lp+=3; - char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,0); - SCRIPT_SKIP_SPACES - float fvar1; - lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); - SCRIPT_SKIP_SPACES - float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); - lp++; - len=0; - if (fvar1<0) { - fvar1=strlen(str)+fvar1; - } - memcpy(sp,&str[(uint8_t)fvar1],(uint8_t)fvar2); - sp[(uint8_t)fvar2] = '\0'; - goto strexit; - } - if (!strncmp(vname,"st(",3)) { - lp+=3; - char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,0); - while (*lp==' ') lp++; - char token[2]; - token[0]=*lp++; - token[1]=0; - while (*lp==' ') lp++; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - - lp++; - len=0; - if (sp) { - - char *st=strtok(str,token); - if (!st) { - *sp=0; - } else { - for (uint8_t cnt=1; cnt<=fvar; cnt++) { - if (cnt==fvar) { - strcpy(sp,st); - break; - } - st=strtok(NULL,token); - if (!st) { - *sp=0; - break; - } - } - } - } - goto strexit; - } - if (!strncmp(vname,"s(",2)) { - lp+=2; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - char str[glob_script_mem.max_ssize+1]; - dtostrfd(fvar,glob_script_mem.script_dprec,str); - if (sp) strlcpy(sp,str,glob_script_mem.max_ssize); - lp++; - len=0; - goto strexit; - } -#if defined(USE_TIMERS) && defined(USE_SUNRISE) - if (!strncmp(vname,"sunrise",7)) { - fvar=SunMinutes(0); - goto exit; - } - if (!strncmp(vname,"sunset",6)) { - fvar=SunMinutes(1); - goto exit; - } -#endif - -#ifdef USE_SHUTTER - if (!strncmp(vname,"sht[",4)) { - GetNumericResult(vname+4,OPER_EQU,&fvar,0); - uint8_t index=fvar; - if (index<=shutters_present) { - fvar=Settings.shutter_position[index-1]; - } else { - fvar=-1; - } - len+=1; - goto exit; - } -#endif -#ifdef USE_ANGLE_FUNC - if (!strncmp(vname,"sin(",4)) { - lp+=4; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - fvar=sinf(fvar); - lp++; - len=0; - goto exit; - } - if (!strncmp(vname,"sqrt(",5)) { - lp+=5; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - fvar=sqrtf(fvar); - lp++; - len=0; - goto exit; - } -#endif -#ifdef USE_SML_SCRIPT_CMD - if (!strncmp(vname,"sml(",4)) { - lp+=4; - float fvar1; - lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); - SCRIPT_SKIP_SPACES - float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); - SCRIPT_SKIP_SPACES - if (fvar2==0) { - float fvar3; - lp=GetNumericResult(lp,OPER_EQU,&fvar3,0); - fvar=SML_SetBaud(fvar1,fvar3); - } else if (fvar2==1) { - char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,0); - fvar=SML_Write(fvar1,str); - } else { -#ifdef ED300L - fvar=SML_Status(fvar1); -#else - fvar=0; -#endif - } - lp++; - len=0; - goto exit; - } -#endif - break; - case 't': - if (!strncmp(vname,"time",4)) { - fvar=MinutesPastMidnight(); - goto exit; - } - if (!strncmp(vname,"tper",4)) { - fvar=Settings.tele_period; - tind->index=SCRIPT_TELEPERIOD; - goto exit_settable; - } - if (!strncmp(vname,"tinit",5)) { - if (rules_flag.time_init) { - rules_flag.time_init=0; - fvar=1; - } - goto exit; - } - if (!strncmp(vname,"tset",4)) { - if (rules_flag.time_set) { - rules_flag.time_set=0; - fvar=1; - } - goto exit; - } - if (!strncmp(vname,"tstamp",6)) { - if (sp) strlcpy(sp,GetDateAndTime(DT_LOCAL).c_str(),glob_script_mem.max_ssize); - goto strexit; - } - if (!strncmp(vname,"topic",5)) { - if (sp) strlcpy(sp,SettingsText(SET_MQTT_TOPIC),glob_script_mem.max_ssize); - goto strexit; - } -#ifdef USE_DISPLAY -#ifdef USE_TOUCH_BUTTONS - if (!strncmp(vname,"tbut[",5)) { - GetNumericResult(vname+5,OPER_EQU,&fvar,0); - uint8_t index=fvar; - if (index<1 || index>MAXBUTTONS) index=1; - index--; - if (buttons[index]) { - fvar=buttons[index]->vpower&0x80; - } else { - fvar=-1; - } - len+=1; - goto exit; - } - -#endif -#endif - break; - case 'u': - if (!strncmp(vname,"uptime",6)) { - fvar=MinutesUptime(); - goto exit; - } - if (!strncmp(vname,"upsecs",6)) { - fvar=uptime; - goto exit; - } - if (!strncmp(vname,"upd[",4)) { - - struct T_INDEX ind; - uint8_t vtype; - isvar(vname+4,&vtype,&ind,0,0,0); - if (!ind.bits.constant) { - if (!ind.bits.changed) { - fvar=0; - len++; - goto exit; - } else { - glob_script_mem.type[ind.index].bits.changed=0; - fvar=1; - len++; - goto exit; - } - } - goto notfound; - } - break; - - case 'w': - if (!strncmp(vname,"wday",4)) { - fvar=RtcTime.day_of_week; - goto exit; - } - if (!strncmp(vname,"wific",5)) { - if (rules_flag.wifi_connected) { - rules_flag.wifi_connected=0; - fvar=1; - } - goto exit; - } - if (!strncmp(vname,"wifid",5)) { - if (rules_flag.wifi_disconnected) { - rules_flag.wifi_disconnected=0; - fvar=1; - } - goto exit; - } - if (!strncmp(vname,"wifis",5)) { - fvar=!global_state.wifi_down; - goto exit; - } - break; - case 'y': - if (!strncmp(vname,"year",4)) { - fvar=RtcTime.year; - goto exit; - } - break; - default: - break; - } - -notfound: - if (fp) *fp=0; - *vtype=VAR_NV; - tind->index=VAR_NV; - glob_script_mem.var_not_found=1; - return lp; - -exit: - if (fp) *fp=fvar; - *vtype=NUM_RES; - tind->bits.constant=1; - tind->bits.is_string=0; - return lp+len; - -strexit: - *vtype=STYPE; - tind->bits.constant=1; - tind->bits.is_string=1; - return lp+len; -} - - - -char *getop(char *lp, uint8_t *operand) { - switch (*lp) { - case '=': - if (*(lp+1)=='=') { - *operand=OPER_EQUEQU; - return lp+2; - } else { - *operand=OPER_EQU; - return lp+1; - } - break; - case '+': - if (*(lp+1)=='=') { - *operand=OPER_PLSEQU; - return lp+2; - } else { - *operand=OPER_PLS; - return lp+1; - } - break; - case '-': - if (*(lp+1)=='=') { - *operand=OPER_MINEQU; - return lp+2; - } else { - *operand=OPER_MIN; - return lp+1; - } - break; - case '*': - if (*(lp+1)=='=') { - *operand=OPER_MULEQU; - return lp+2; - } else { - *operand=OPER_MUL; - return lp+1; - } - break; - case '/': - if (*(lp+1)=='=') { - *operand=OPER_DIVEQU; - return lp+2; - } else { - *operand=OPER_DIV; - return lp+1; - } - break; - case '!': - if (*(lp+1)=='=') { - *operand=OPER_NOTEQU; - return lp+2; - } - break; - case '>': - if (*(lp+1)=='=') { - *operand=OPER_GRTEQU; - return lp+2; - } else { - *operand=OPER_GRT; - return lp+1; - - } - break; - case '<': - if (*(lp+1)=='=') { - *operand=OPER_LOWEQU; - return lp+2; - } else { - *operand=OPER_LOW; - return lp+1; - } - break; - case '%': - if (*(lp+1)=='=') { - *operand=OPER_PERCEQU; - return lp+2; - } else { - *operand=OPER_PERC; - return lp+1; - } - break; - case '^': - if (*(lp+1)=='=') { - *operand=OPER_XOREQU; - return lp+2; - } else { - *operand=OPER_XOR; - return lp+1; - } - break; - case '&': - if (*(lp+1)=='=') { - *operand=OPER_ANDEQU; - return lp+2; - } else { - *operand=OPER_AND; - return lp+1; - } - break; - case '|': - if (*(lp+1)=='=') { - *operand=OPER_OREQU; - return lp+2; - } else { - *operand=OPER_OR; - return lp+1; - } - break; - } - *operand=0; - return lp; -} - - -#ifdef ESP8266 -#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) - - -extern "C" { -#include - extern cont_t g_cont; -} -uint16_t GetStack(void) { - register uint32_t *sp asm("a1"); - return (4 * (sp - g_cont.stack)); -} - -#else -extern "C" { -#include - extern cont_t* g_pcont; -} -uint16_t GetStack(void) { - register uint32_t *sp asm("a1"); - return (4 * (sp - g_pcont->stack)); -} -#endif -#else -uint16_t GetStack(void) { - register uint8_t *sp asm("a1"); - return (sp - pxTaskGetStackStart(NULL)); -} -#endif - -char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo) { - uint8_t operand=0; - uint8_t vtype; - char *slp; - struct T_INDEX ind; - char str[SCRIPT_MAXSSIZE],str1[SCRIPT_MAXSSIZE]; - while (1) { - lp=isvar(lp,&vtype,&ind,0,str1,jo); - if (vtype!=STR_RES && !(vtype&STYPE)) { - - glob_script_mem.glob_error=1; - return lp; - } - switch (lastop) { - case OPER_EQU: - strlcpy(str,str1,sizeof(str)); - break; - case OPER_PLS: - strncat(str,str1,sizeof(str)); - break; - } - slp=lp; - lp=getop(lp,&operand); - switch (operand) { - case OPER_EQUEQU: - case OPER_NOTEQU: - case OPER_LOW: - case OPER_LOWEQU: - case OPER_GRT: - case OPER_GRTEQU: - lp=slp; - strcpy(cp,str); - return lp; - break; - default: - break; - } - lastop=operand; - if (!operand) { - strcpy(cp,str); - return lp; - } - } -} - -char *GetNumericResult(char *lp,uint8_t lastop,float *fp,JsonObject *jo) { -uint8_t operand=0; -float fvar1,fvar; -char *slp; -uint8_t vtype; -struct T_INDEX ind; - while (1) { - - if (*lp=='(') { - lp++; - lp=GetNumericResult(lp,OPER_EQU,&fvar1,jo); - lp++; - - } else { - lp=isvar(lp,&vtype,&ind,&fvar1,0,jo); - if (vtype!=NUM_RES && vtype&STYPE) { - - glob_script_mem.glob_error=1; - } - } - switch (lastop) { - case OPER_EQU: - fvar=fvar1; - break; - case OPER_PLS: - fvar+=fvar1; - break; - case OPER_MIN: - fvar-=fvar1; - break; - case OPER_MUL: - fvar*=fvar1; - break; - case OPER_DIV: - fvar/=fvar1; - break; - case OPER_PERC: - fvar=fmodf(fvar,fvar1); - break; - case OPER_XOR: - fvar=(uint32_t)fvar^(uint32_t)fvar1; - break; - case OPER_AND: - fvar=(uint32_t)fvar&(uint32_t)fvar1; - break; - case OPER_OR: - fvar=(uint32_t)fvar|(uint32_t)fvar1; - break; - default: - break; - - } - slp=lp; - lp=getop(lp,&operand); - switch (operand) { - case OPER_EQUEQU: - case OPER_NOTEQU: - case OPER_LOW: - case OPER_LOWEQU: - case OPER_GRT: - case OPER_GRTEQU: - lp=slp; - *fp=fvar; - return lp; - break; - default: - break; - } - lastop=operand; - if (!operand) { - *fp=fvar; - return lp; - } - } -} - - -char *ForceStringVar(char *lp,char *dstr) { - float fvar; - char *slp=lp; - glob_script_mem.glob_error=0; - lp=GetStringResult(lp,OPER_EQU,dstr,0); - if (glob_script_mem.glob_error) { - - lp=GetNumericResult(slp,OPER_EQU,&fvar,0); - dtostrfd(fvar,6,dstr); - glob_script_mem.glob_error=0; - } - return lp; -} - - -void Replace_Cmd_Vars(char *srcbuf,char *dstbuf,uint16_t dstsize) { - char *cp; - uint16_t count; - uint8_t vtype; - uint8_t dprec=glob_script_mem.script_dprec; - float fvar; - cp=srcbuf; - struct T_INDEX ind; - char string[SCRIPT_MAXSSIZE]; - dstsize-=2; - for (count=0;count=sizeof(str)) len=len>=sizeof(str); - strlcpy(str,cp,len); - toSLog(str); -} - -void toLogEOL(const char *s1,const char *str) { - if (!str) return; - uint8_t index=0; - char *cp=log_data; - strcpy(cp,s1); - cp+=strlen(s1); - while (*str) { - if (*str==SCRIPT_EOL) break; - *cp++=*str++; - } - *cp=0; - AddLog(LOG_LEVEL_INFO); -} - - -void toSLog(const char *str) { - if (!str) return; -#if SCRIPT_DEBUG>0 - while (*str) { - Serial.write(*str); - str++; - } -#endif -} - -char *Evaluate_expression(char *lp,uint8_t and_or, uint8_t *result,JsonObject *jo) { - float fvar,*dfvar,fvar1; - uint8_t numeric; - struct T_INDEX ind; - uint8_t vtype=0,lastop; - uint8_t res=0; - char *llp=lp; - char *slp; - - SCRIPT_SKIP_SPACES - if (*lp=='(') { - uint8_t res=0; - uint8_t xand_or=0; - lp++; - -loop: - SCRIPT_SKIP_SPACES - lp=Evaluate_expression(lp,xand_or,&res,jo); - if (*lp==')') { - lp++; - goto exit0; - } - - SCRIPT_SKIP_SPACES - if (!strncmp(lp,"or",2)) { - lp+=2; - xand_or=1; - goto loop; - } else if (!strncmp(lp,"and",3)) { - lp+=3; - xand_or=2; - goto loop; - } -exit0: - if (!and_or) { - *result=res; - } else if (and_or==1) { - *result|=res; - } else { - *result&=res; - } - goto exit10; - } - - llp=lp; - - dfvar=&fvar; - glob_script_mem.glob_error=0; - slp=lp; - numeric=1; - lp=GetNumericResult(lp,OPER_EQU,dfvar,0); - if (glob_script_mem.glob_error==1) { - - char cmpstr[SCRIPT_MAXSSIZE]; - lp=slp; - numeric=0; - - lp=isvar(lp,&vtype,&ind,0,cmpstr,0); - lp=getop(lp,&lastop); - - char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,jo); - if (lastop==OPER_EQUEQU || lastop==OPER_NOTEQU) { - res=strcmp(cmpstr,str); - if (lastop==OPER_EQUEQU) res=!res; - goto exit; - } - - } else { - - - lp=getop(lp,&lastop); - lp=GetNumericResult(lp,OPER_EQU,&fvar1,jo); - switch (lastop) { - case OPER_EQUEQU: - res=(*dfvar==fvar1); - break; - case OPER_NOTEQU: - res=(*dfvar!=fvar1); - break; - case OPER_LOW: - res=(*dfvarfvar1); - break; - case OPER_GRTEQU: - res=(*dfvar>=fvar1); - break; - default: - - break; - } - -exit: - if (!and_or) { - *result=res; - } else if (and_or==1) { - *result|=res; - } else { - *result&=res; - } - } - - -exit10: -#if SCRIPT_DEBUG>0 - char tbuff[128]; - sprintf(tbuff,"p1=%d,p2=%d,cmpres=%d,and_or=%d line: ",(int32_t)*dfvar,(int32_t)fvar1,*result,and_or); - toLogEOL(tbuff,llp); -#endif - return lp; -} - - - -#define IF_NEST 8 - -int16_t Run_Scripter(const char *type, int8_t tlen, char *js) { - - if (tasm_cmd_activ && tlen>0) return 0; - - uint8_t vtype=0,sindex,xflg,floop=0,globvindex,fromscriptcmd=0; - int8_t globaindex; - struct T_INDEX ind; - uint8_t operand,lastop,numeric=1,if_state[IF_NEST],if_exe[IF_NEST],if_result[IF_NEST],and_or,ifstck=0; - if_state[ifstck]=0; - if_result[ifstck]=0; - if_exe[ifstck]=1; - char cmpstr[SCRIPT_MAXSSIZE]; - uint8_t check=0; - if (tlen<0) { - tlen=abs(tlen); - check=1; - } - - float *dfvar,*cv_count,cv_max,cv_inc; - char *cv_ptr; - float fvar=0,fvar1,sysvar,swvar; - uint8_t section=0,sysv_type=0,swflg=0; - - if (!glob_script_mem.scriptptr) { - return -99; - } - - DynamicJsonBuffer jsonBuffer; - JsonObject &jobj=jsonBuffer.parseObject(js); - JsonObject *jo; - if (js) jo=&jobj; - else jo=0; - - char *lp=glob_script_mem.scriptptr; - - while (1) { - - - startline: - SCRIPT_SKIP_SPACES - - SCRIPT_SKIP_EOL - - if (*lp==';') goto next_line; - if (!*lp) break; - - if (section) { - - if (*lp=='>') { - return 0; - } - if (*lp=='#') { - return 0; - } - glob_script_mem.var_not_found=0; - - -#ifdef IFTHEN_DEBUG - char tbuff[128]; - sprintf(tbuff,"stack=%d,exe=%d,state=%d,cmpres=%d line: ",ifstck,if_exe[ifstck],if_state[ifstck],if_result[ifstck]); - toLogEOL(tbuff,lp); -#endif - - - - - if (!strncmp(lp,"if",2)) { - lp+=2; - if (ifstck=2) { - lp+=5; - if (ifstck>0) { - if_state[ifstck]=0; - ifstck--; - } - goto next_line; - } else if (!strncmp(lp,"or",2) && if_state[ifstck]==1) { - lp+=2; - and_or=1; - } else if (!strncmp(lp,"and",3) && if_state[ifstck]==1) { - lp+=3; - and_or=2; - } - - if (*lp=='{' && if_state[ifstck]==1) { - lp+=1; - if_state[ifstck]=2; - if (if_exe[ifstck-1]) if_exe[ifstck]=if_result[ifstck]; - } else if (*lp=='{' && if_state[ifstck]==3) { - lp+=1; - - } else if (*lp=='}' && if_state[ifstck]>=2) { - lp++; - char *slp=lp; - uint8_t iselse=0; - for (uint8_t count=0; count<8;count++) { - if (*lp=='}') { - - break; - } - if (!strncmp(lp,"else",4)) { - - if_state[ifstck]=3; - if (if_exe[ifstck-1]) if_exe[ifstck]=!if_result[ifstck]; - lp+=4; - iselse=1; - SCRIPT_SKIP_SPACES - if (*lp=='{') lp++; - break; - } - lp++; - } - if (!iselse) { - lp=slp; - - if (ifstck>0) { - if_state[ifstck]=0; - ifstck--; - } - goto next_line; - } - } - - if (!strncmp(lp,"for",3)) { - - - lp+=3; - SCRIPT_SKIP_SPACES - lp=isvar(lp,&vtype,&ind,0,0,0); - if ((vtype!=VAR_NV) && (vtype&STYPE)==0) { - - uint8_t index=glob_script_mem.type[ind.index].index; - cv_count=&glob_script_mem.fvars[index]; - SCRIPT_SKIP_SPACES - lp=GetNumericResult(lp,OPER_EQU,cv_count,0); - SCRIPT_SKIP_SPACES - lp=GetNumericResult(lp,OPER_EQU,&cv_max,0); - SCRIPT_SKIP_SPACES - lp=GetNumericResult(lp,OPER_EQU,&cv_inc,0); - - cv_ptr=lp; - floop=1; - } else { - - toLogEOL("for error",lp); - } - } else if (!strncmp(lp,"next",4) && floop>0) { - - *cv_count+=cv_inc; - if (*cv_count<=cv_max) { - lp=cv_ptr; - } else { - lp+=4; - floop=0; - } - } - - if (!strncmp(lp,"switch",6)) { - lp+=6; - SCRIPT_SKIP_SPACES - char *slp=lp; - lp=GetNumericResult(lp,OPER_EQU,&swvar,0); - if (glob_script_mem.glob_error==1) { - - lp=slp; - - lp=isvar(lp,&vtype,&ind,0,cmpstr,0); - swflg=0x81; - } else { - swflg=1; - } - } else if (!strncmp(lp,"case",4) && swflg>0) { - lp+=4; - SCRIPT_SKIP_SPACES - float cvar; - if (!(swflg&0x80)) { - lp=GetNumericResult(lp,OPER_EQU,&cvar,0); - if (swvar!=cvar) { - swflg=2; - } else { - swflg=1; - } - } else { - char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,0); - if (!strcmp(cmpstr,str)) { - swflg=0x81; - } else { - swflg=0x82; - } - } - } else if (!strncmp(lp,"ends",4) && swflg>0) { - lp+=4; - swflg=0; - } - if ((swflg&3)==2) goto next_line; - - SCRIPT_SKIP_SPACES - - if (*lp==SCRIPT_EOL) { - goto next_line; - } - - - if (!if_exe[ifstck] && if_state[ifstck]!=1) goto next_line; - -#ifdef IFTHEN_DEBUG - sprintf(tbuff,"stack=%d,exe=%d,state=%d,cmpres=%d execute line: ",ifstck,if_exe[ifstck],if_state[ifstck],if_result[ifstck]); - toLogEOL(tbuff,lp); -#endif - - if (!strncmp(lp,"break",5)) { - if (floop) { - - floop=0; - } else { - section=0; - } - break; - } else if (!strncmp(lp,"dp",2) && isdigit(*(lp+2))) { - lp+=2; - - glob_script_mem.script_dprec=atoi(lp); - goto next_line; - } else if (!strncmp(lp,"delay(",6)) { - lp+=5; - - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - delay(fvar); - goto next_line; - } else if (!strncmp(lp,"spinm(",6)) { - lp+=6; - - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - int8_t pinnr=fvar; - SCRIPT_SKIP_SPACES - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - int8_t mode=fvar; - pinMode(pinnr,mode&3); - goto next_line; - } else if (!strncmp(lp,"spin(",5)) { - lp+=5; - - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - int8_t pinnr=fvar; - SCRIPT_SKIP_SPACES - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - int8_t mode=fvar; - digitalWrite(pinnr,mode&1); - goto next_line; - } else if (!strncmp(lp,"svars(",5)) { - lp+=5; - - Scripter_save_pvars(); - goto next_line; - } -#ifdef USE_LIGHT -#ifdef USE_WS2812 - else if (!strncmp(lp,"ws2812(",7)) { - lp+=7; - lp=isvar(lp,&vtype,&ind,0,0,0); - if (vtype!=VAR_NV) { - - uint8_t index=glob_script_mem.type[ind.index].index; - if ((vtype&STYPE)==0) { - - if (glob_script_mem.type[index].bits.is_filter) { - uint8_t len=0; - float *fa=Get_MFAddr(index,&len); - - if (fa && len) ws2812_set_array(fa,len); - } - } - } - goto next_line; - } -#endif -#endif - - else if (!strncmp(lp,"=>",2) || !strncmp(lp,"->",2) || !strncmp(lp,"+>",2) || !strncmp(lp,"print",5)) { - - uint8_t sflag=0,pflg=0,svmqtt,swll; - if (*lp=='p') { - pflg=1; - lp+=5; - } - else { - if (*lp=='-') sflag=1; - if (*lp=='+') sflag=2; - lp+=2; - } - char *slp=lp; - SCRIPT_SKIP_SPACES - #define SCRIPT_CMDMEM 512 - char *cmdmem=(char*)malloc(SCRIPT_CMDMEM); - if (cmdmem) { - char *cmd=cmdmem; - uint16_t count; - for (count=0; count=0) { - Set_MFVal(glob_script_mem.type[globvindex].index,globaindex,*dfvar); - } else { - Set_MFilter(glob_script_mem.type[globvindex].index,*dfvar); - } - } - - if (sysv_type) { - switch (sysv_type) { - case SCRIPT_LOGLEVEL: - glob_script_mem.script_loglevel=*dfvar; - break; - case SCRIPT_TELEPERIOD: - if (*dfvar<10) *dfvar=10; - if (*dfvar>300) *dfvar=300; - Settings.tele_period=*dfvar; - break; - } - sysv_type=0; - } - } else { - - numeric=0; - sindex=index; - - char str[SCRIPT_MAXSSIZE]; - lp=getop(lp,&lastop); - char *slp=lp; - glob_script_mem.glob_error=0; - lp=GetStringResult(lp,OPER_EQU,str,jo); - if (!js && glob_script_mem.glob_error) { - - lp=GetNumericResult(slp,OPER_EQU,&fvar,0); - dtostrfd(fvar,6,str); - glob_script_mem.glob_error=0; - } - - if (!glob_script_mem.var_not_found) { - - glob_script_mem.type[globvindex].bits.changed=1; - if (lastop==OPER_EQU) { - strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); - } else if (lastop==OPER_PLSEQU) { - strncat(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); - } - } - } - - } - SCRIPT_SKIP_SPACES - if (*lp=='{' && if_state[ifstck]==3) { - lp+=1; - - } - goto next_line; - } - } else { - - if (*lp=='>' && tlen==1) { - - lp++; - section=1; - fromscriptcmd=1; - goto startline; - } - if (!strncmp(lp,type,tlen)) { - - section=1; - glob_script_mem.section_ptr=lp; - if (check) { - return 99; - } - - char *ctype=(char*)type; - if (*ctype=='#') { - - ctype+=tlen; - if (*ctype=='(' && *(lp+tlen)=='(') { - float fparam; - numeric=1; - glob_script_mem.glob_error=0; - GetNumericResult((char*)ctype,OPER_EQU,&fparam,0); - if (glob_script_mem.glob_error==1) { - - numeric=0; - - GetStringResult((char*)ctype+1,OPER_EQU,cmpstr,0); - } - lp+=tlen; - if (*lp=='(') { - - lp++; - lp=isvar(lp,&vtype,&ind,0,0,0); - if (vtype!=VAR_NV) { - - uint8_t index=glob_script_mem.type[ind.index].index; - if ((vtype&STYPE)==0) { - - dfvar=&glob_script_mem.fvars[index]; - if (numeric) { - *dfvar=fparam; - } else { - - *dfvar=CharToFloat(cmpstr); - } - } else { - - sindex=index; - if (!numeric) { - strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),cmpstr,glob_script_mem.max_ssize); - } else { - - dtostrfd(fparam,6,glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize)); - } - } - } - } - } else { - lp+=tlen; - if (*ctype=='(' || (*lp!=SCRIPT_EOL && *lp!='?')) { - - section=0; - } - } - } - } - } - - next_line: - if (*lp==SCRIPT_EOL) { - lp++; - } else { - lp = strchr(lp, SCRIPT_EOL); - if (!lp) { - if (section) { - return 0; - } else { - return -1; - } - } - lp++; - } - } - return -1; -} - -uint8_t script_xsns_index = 0; - - -void ScripterEvery100ms(void) { - - if (Settings.rule_enabled && (uptime > 4)) { - mqtt_data[0] = '\0'; - uint16_t script_tele_period_save = tele_period; - tele_period = 2; - XsnsNextCall(FUNC_JSON_APPEND, script_xsns_index); - tele_period = script_tele_period_save; - if (strlen(mqtt_data)) { - mqtt_data[0] = '{'; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); - Run_Scripter(">T",2, mqtt_data); - } - } - if (fast_script==99) Run_Scripter(">F",2,0); -} - - - - -void Scripter_save_pvars(void) { - int16_t mlen=0; - float *fp=(float*)glob_script_mem.script_pram; - mlen+=sizeof(float); - struct T_INDEX *vtp=glob_script_mem.type; - for (uint8_t count=0; countPMEM_SIZE) { - vtp[count].bits.is_permanent=0; - return; - } - *fp++=glob_script_mem.fvars[index]; - } - } - char *cp=(char*)fp; - for (uint8_t count=0; countPMEM_SIZE) { - vtp[count].bits.is_permanent=0; - return; - } - strcpy(cp,sp); - cp+=slen+1; - } - } -} - - -#ifdef USE_WEBSERVER - -#define WEB_HANDLE_SCRIPT "s10" - -const char S_CONFIGURE_SCRIPT[] PROGMEM = D_CONFIGURE_SCRIPT; - -const char HTTP_BTN_MENU_RULES[] PROGMEM = - "

"; - - -const char HTTP_FORM_SCRIPT[] PROGMEM = - "
 " D_SCRIPT " " - "
"; - -const char HTTP_FORM_SCRIPT1[] PROGMEM = - "
" - "
" - "
" - ""; - -const char HTTP_SCRIPT_FORM_END[] PROGMEM = - "
" - "" - "
"; - -#ifdef USE_SCRIPT_FATFS -const char HTTP_FORM_SCRIPT1c[] PROGMEM = - ""; -#ifdef SDCARD_DIR -const char HTTP_FORM_SCRIPT1d[] PROGMEM = - ""; -#else -const char HTTP_FORM_SCRIPT1d[] PROGMEM = - ""; -#endif - -#ifdef SDCARD_DIR -const char S_SCRIPT_FILE_UPLOAD[] PROGMEM = D_SDCARD_DIR; -#else -const char S_SCRIPT_FILE_UPLOAD[] PROGMEM = D_SDCARD_UPLOAD; -#endif - -const char HTTP_FORM_FILE_UPLOAD[] PROGMEM = -"
" -"
 %s" " "; -const char HTTP_FORM_FILE_UPG[] PROGMEM = -"
" -"

" -"
"; - -const char HTTP_FORM_FILE_UPGb[] PROGMEM = -"
" -"
" -""; - -const char HTTP_FORM_SDC_DIRa[] PROGMEM = -"
"; -const char HTTP_FORM_SDC_DIRb[] PROGMEM = - "
%s    %d
"; -const char HTTP_FORM_SDC_DIRd[] PROGMEM = -"
%s
"; -const char HTTP_FORM_SDC_DIRc[] PROGMEM = -"
"; -const char HTTP_FORM_SDC_HREF[] PROGMEM = -"http://%s/upl?download=%s/%s"; -#endif - - - -#ifdef USE_SCRIPT_FATFS - -#if USE_LONG_FILE_NAMES>0 -#undef REJCMPL -#define REJCMPL 6 -#else -#undef REJCMPL -#define REJCMPL 8 -#endif - -uint8_t reject(char *name) { - - if (*name=='_') return 1; - if (*name=='.') return 1; - -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 - if (!strncasecmp(name,"SPOTLI~1",REJCMPL)) return 1; - if (!strncasecmp(name,"TRASHE~1",REJCMPL)) return 1; - if (!strncasecmp(name,"FSEVEN~1",REJCMPL)) return 1; - if (!strncasecmp(name,"SYSTEM~1",REJCMPL)) return 1; -#else - if (!strcasecmp(name,"SPOTLI~1")) return 1; - if (!strcasecmp(name,"TRASHE~1")) return 1; - if (!strcasecmp(name,"FSEVEN~1")) return 1; - if (!strcasecmp(name,"SYSTEM~1")) return 1; -#endif - return 0; -} - -void ListDir(char *path, uint8_t depth) { - char name[32]; - char npath[128]; - char format[12]; - sprintf(format,"%%-%ds",24-depth); - - File dir=SD.open(path); - if (dir) { - dir.rewindDirectory(); - if (strlen(path)>1) { - snprintf_P(npath,sizeof(npath),PSTR("http://%s/upl?download=%s"),WiFi.localIP().toString().c_str(),path); - for (uint8_t cnt=strlen(npath)-1;cnt>0;cnt--) { - if (npath[cnt]=='/') { - if (npath[cnt-1]=='=') npath[cnt+1]=0; - else npath[cnt]=0; - break; - } - } - WSContentSend_P(HTTP_FORM_SDC_DIRd,npath,path,".."); - } - while (true) { - File entry=dir.openNextFile(); - if (!entry) { - break; - } - char *pp=path; - if (!*(pp+1)) pp++; - char *cp=name; - - if (reject((char*)entry.name())) goto fclose; - - for (uint8_t cnt=0;cnt1) { - strcat(path,"/"); - } - strcat(path,entry.name()); - ListDir(path,depth+4); - path[plen]=0; - } else { - snprintf_P(npath,sizeof(npath),HTTP_FORM_SDC_HREF,WiFi.localIP().toString().c_str(),pp,entry.name()); - WSContentSend_P(HTTP_FORM_SDC_DIRb,npath,entry.name(),name,entry.size()); - } - fclose: - entry.close(); - } - dir.close(); - } -} - -char path[48]; - -void Script_FileUploadConfiguration(void) -{ - uint8_t depth=0; - strcpy(path,"/"); - - if (!HttpCheckPriviledgedAccess()) { return; } - - if (Webserver->hasArg("download")) { - String stmp = Webserver->arg("download"); - char *cp=(char*)stmp.c_str(); - if (DownloadFile(cp)) { - - strcpy(path,cp); - } - } - - WSContentStart_P(S_SCRIPT_FILE_UPLOAD); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_FILE_UPLOAD,D_SDCARD_DIR); - WSContentSend_P(HTTP_FORM_FILE_UPG, D_SCRIPT_UPLOAD); -#ifdef SDCARD_DIR - WSContentSend_P(HTTP_FORM_SDC_DIRa); - if (glob_script_mem.script_sd_found) { - ListDir(path,depth); - } - WSContentSend_P(HTTP_FORM_SDC_DIRc); -#endif - WSContentSend_P(HTTP_FORM_FILE_UPGb); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); - Web.upload_error = 0; -} - -File upload_file; - -void ScriptFileUploadSuccess(void) { - WSContentStart_P(S_INFORMATION); - WSContentSendStyle(); - WSContentSend_P(PSTR("
" D_UPLOAD " " D_SUCCESSFUL "
"), WebColor(COL_TEXT_SUCCESS)); - WSContentSend_P(PSTR("

")); - WSContentSend_P(PSTR("

"),"/upl",D_UPL_DONE); - - WSContentStop(); -} - - - -void script_upload(void) { - - - - HTTPUpload& upload = Webserver->upload(); - if (upload.status == UPLOAD_FILE_START) { - char npath[48]; - sprintf(npath,"%s/%s",path,upload.filename.c_str()); - SD.remove(npath); - upload_file=SD.open(npath,FILE_WRITE); - if (!upload_file) Web.upload_error=1; - } else if(upload.status == UPLOAD_FILE_WRITE) { - if (upload_file) upload_file.write(upload.buf,upload.currentSize); - } else if(upload.status == UPLOAD_FILE_END) { - if (upload_file) upload_file.close(); - if (Web.upload_error) { - AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload error")); - } - } else { - Web.upload_error=1; - Webserver->send(500, "text/plain", "500: couldn't create file"); - } -} - -uint8_t DownloadFile(char *file) { - File download_file; - WiFiClient download_Client; - - if (!SD.exists(file)) { - AddLog_P(LOG_LEVEL_INFO,PSTR("file not found")); - return 0; - } - - download_file=SD.open(file,FILE_READ); - if (!download_file) { - AddLog_P(LOG_LEVEL_INFO,PSTR("could not open file")); - return 0; - } - - if (download_file.isDirectory()) { - download_file.close(); - return 1; - } - - uint32_t flen=download_file.size(); - - download_Client = Webserver->client(); - Webserver->setContentLength(flen); - - char attachment[100]; - char *cp; - for (uint8_t cnt=strlen(file); cnt>=0; cnt--) { - if (file[cnt]=='/') { - cp=&file[cnt+1]; - break; - } - } - snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=%s"),cp); - Webserver->sendHeader(F("Content-Disposition"), attachment); - WSSend(200, CT_STREAM, ""); - - uint8_t buff[512]; - uint16_t bread; - - - uint8_t cnt=0; - while (download_file.available()) { - bread=download_file.read(buff,sizeof(buff)); - uint16_t bw=download_Client.write((const char*)buff,bread); - if (!bw) break; - cnt++; - if (cnt>7) { - cnt=0; - if (glob_script_mem.script_loglevel&0x80) { - - loop(); - } - } - } - download_file.close(); - download_Client.stop(); - return 0; -} - -#endif - - -void HandleScriptTextareaConfiguration(void) { - if (!HttpCheckPriviledgedAccess()) { return; } - - if (Webserver->hasArg("save")) { - ScriptSaveSettings(); - HandleConfiguration(); - return; - } -} - -void HandleScriptConfiguration(void) { - - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_SCRIPT); - -#ifdef USE_SCRIPT_FATFS - if (Webserver->hasArg("d1")) { - DownloadFile(glob_script_mem.flink[0]); - } - if (Webserver->hasArg("d2")) { - DownloadFile(glob_script_mem.flink[1]); - } - if (Webserver->hasArg("upl")) { - Script_FileUploadConfiguration(); - } -#endif - - WSContentStart_P(S_CONFIGURE_SCRIPT); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_SCRIPT); - - -#ifdef xSCRIPT_STRIP_COMMENTS - uint16_t ssize=glob_script_mem.script_size; - if (bitRead(Settings.rule_enabled, 1)) ssize*=2; - WSContentSend_P(HTTP_FORM_SCRIPT1,1,1,bitRead(Settings.rule_enabled,0) ? " checked" : "",ssize); -#else - WSContentSend_P(HTTP_FORM_SCRIPT1,1,1,bitRead(Settings.rule_enabled,0) ? " checked" : "",glob_script_mem.script_size); -#endif - - - if (glob_script_mem.script_ram[0]) { - _WSContentSend(glob_script_mem.script_ram); - } - WSContentSend_P(HTTP_FORM_SCRIPT1b); - -#ifdef USE_SCRIPT_FATFS - if (glob_script_mem.script_sd_found) { - WSContentSend_P(HTTP_FORM_SCRIPT1d); - if (glob_script_mem.flink[0][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c,1,glob_script_mem.flink[0]); - if (glob_script_mem.flink[1][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c,2,glob_script_mem.flink[1]); - } -#endif - - WSContentSend_P(HTTP_SCRIPT_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); - } - - -void ScriptSaveSettings(void) { - - if (Webserver->hasArg("c1")) { - bitWrite(Settings.rule_enabled,0,1); - } else { - bitWrite(Settings.rule_enabled,0,0); - } - - - String str = Webserver->arg("t1"); - - if (*str.c_str()) { - - str.replace("\r\n","\n"); - str.replace("\r","\n"); - -#ifdef xSCRIPT_STRIP_COMMENTS - if (bitRead(Settings.rule_enabled, 1)) { - char *sp=(char*)str.c_str(); - char *sp1=sp; - char *dp=sp; - uint8_t flg=0; - while (*sp) { - while (*sp==' ') sp++; - sp1=sp; - sp=strchr(sp,'\n'); - if (!sp) { - flg=1; - } else { - *sp=0; - } - if (*sp1!=';') { - uint8_t slen=strlen(sp1); - if (slen) { - strcpy(dp,sp1); - dp+=slen; - *dp++='\n'; - } - } - if (flg) { - *dp=0; - break; - } - sp++; - } - } -#endif - - strlcpy(glob_script_mem.script_ram,str.c_str(), glob_script_mem.script_size); - -#ifdef USE_24C256 -#ifndef USE_SCRIPT_FATFS - if (glob_script_mem.flags&1) { - EEP_WRITE(0,EEP_SCRIPT_SIZE,glob_script_mem.script_ram); - } -#endif -#endif - -#ifdef USE_SCRIPT_FATFS - if (glob_script_mem.flags&1) { - SD.remove(FAT_SCRIPT_NAME); - File file=SD.open(FAT_SCRIPT_NAME,FILE_WRITE); - file.write(glob_script_mem.script_ram,FAT_SCRIPT_SIZE); - file.close(); - } -#endif - -#if defined(ESP32) && defined(ESP32_SCRIPT_SIZE) && !defined(USE_24C256) && !defined(USE_SCRIPT_FATFS) - if (glob_script_mem.flags&1) { - SaveFile("/script.txt",(uint8_t*)glob_script_mem.script_ram,ESP32_SCRIPT_SIZE); - } -#endif - } - - if (glob_script_mem.script_mem) { - Scripter_save_pvars(); - free(glob_script_mem.script_mem); - glob_script_mem.script_mem=0; - glob_script_mem.script_mem_size=0; - } - - if (bitRead(Settings.rule_enabled, 0)) { - int16_t res=Init_Scripter(); - if (res) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("script init error: %d"), res); - return; - } - Run_Scripter(">B",2,0); - fast_script=Run_Scripter(">F",-2,0); - } -} - -#endif - - -#if defined(USE_SCRIPT_HUE) && defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) - - -#define HUE_DEV_MVNUM 5 -#define HUE_DEV_NSIZE 16 -struct HUE_SCRIPT { - char name[HUE_DEV_NSIZE]; - uint8_t type; - uint8_t index[HUE_DEV_MVNUM]; - uint8_t vindex[HUE_DEV_MVNUM]; -} hue_script[32]; - - -const char SCRIPT_HUE_LIGHTS_STATUS_JSON1[] PROGMEM = - "{\"state\":" - "{\"on\":{state}," - "{light_status}" - "\"alert\":\"none\"," - "\"effect\":\"none\"," - "\"reachable\":true}" - ",\"type\":\"{type}\"," - "\"name\":\"{j1\"," - "\"modelid\":\"{m1}\"," - "\"uniqueid\":\"{j2\"," - "\"swversion\":\"5.50.1.19085\"}"; -# 3706 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_scripter.ino" -const char SCRIPT_HUE_LIGHTS_STATUS_JSON2[] PROGMEM = -"{\"state\":{" -"\"presence\":{state}," -"\"lastupdated\":\"2017-10-01T12:37:30\"" -"}," -"\"swupdate\":{" -"\"state\":\"noupdates\"," -"\"lastinstall\": null" -"}," -"\"config\":{" -"\"on\":true," -"\"battery\":100," -"\"reachable\":true," -"\"alert\":\"none\"," -"\"ledindication\":false," -"\"usertest\":false," -"\"sensitivity\":2," -"\"sensitivitymax\":2," -"\"pending\":[]" -"}," -"\"name\":\"{j1\"," -"\"type\":\"ZLLPresence\"," -"\"modelid\":\"SML001\"," -"\"manufacturername\":\"Philips\"," -"\"swversion\":\"6.1.0.18912\"," -"\"uniqueid\":\"{j2\"" -"}"; -# 3787 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_scripter.ino" -void Script_HueStatus(String *response, uint16_t hue_devs) { - - if (hue_script[hue_devs].type=='p') { - *response+=FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON2); - response->replace("{j1",hue_script[hue_devs].name); - response->replace("{j2", GetHueDeviceId(hue_devs)); - uint8_t pwr=glob_script_mem.fvars[hue_script[hue_devs].index[0]-1]; - response->replace("{state}", (pwr ? "true" : "false")); - return; - } - - *response+=FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON1); - uint8_t pwr=glob_script_mem.fvars[hue_script[hue_devs].index[0]-1]; - response->replace("{state}", (pwr ? "true" : "false")); - String light_status = ""; - if (hue_script[hue_devs].index[1]>0) { - - light_status += "\"bri\":"; - uint32_t bri=glob_script_mem.fvars[hue_script[hue_devs].index[1]-1]; - if (bri > 254) bri = 254; - if (bri < 1) bri = 1; - light_status += String(bri); - light_status += ","; - } - if (hue_script[hue_devs].index[2]>0) { - - uint32_t hue=glob_script_mem.fvars[hue_script[hue_devs].index[2]-1]; - - light_status += "\"hue\":"; - light_status += String(hue); - light_status += ","; - } - if (hue_script[hue_devs].index[3]>0) { - - uint32_t sat=glob_script_mem.fvars[hue_script[hue_devs].index[3]-1] ; - if (sat > 254) sat = 254; - if (sat < 1) sat = 1; - light_status += "\"sat\":"; - light_status += String(sat); - light_status += ","; - } - if (hue_script[hue_devs].index[4]>0) { - - uint32_t ct=glob_script_mem.fvars[hue_script[hue_devs].index[4]-1]; - light_status += "\"ct\":"; - light_status += String(ct); - light_status += ","; - } - - float temp; - switch (hue_script[hue_devs].type) { - case 'C': - response->replace("{type}","Color Ligh"); - response->replace("{m1","LST001"); - break; - case 'D': - response->replace("{type}","Dimmable Light"); - response->replace("{m1","LWB004"); - break; - case 'T': - response->replace("{type}","Color Temperature Light"); - response->replace("{m1","LTW011"); - break; - case 'E': - response->replace("{type}","Extended color light"); - response->replace("{m1","LCT007"); - break; - case 'S': - response->replace("{type}","On/Off light"); - response->replace("{m1","LCT007"); - break; - default: - response->replace("{type}","color light"); - response->replace("{m1","LST001"); - break; - } - - response->replace("{light_status}", light_status); - response->replace("{j1",hue_script[hue_devs].name); - response->replace("{j2", GetHueDeviceId(hue_devs)); - -} - -void Script_Check_Hue(String *response) { - if (!bitRead(Settings.rule_enabled, 0)) return; - - uint8_t hue_script_found=Run_Scripter(">H",-2,0); - if (hue_script_found!=99) return; - - char line[128]; - char tmp[128]; - uint8_t hue_devs=0; - uint8_t vindex=0; - char *cp; - char *lp=glob_script_mem.section_ptr+2; - while (lp) { - SCRIPT_SKIP_SPACES - while (*lp==SCRIPT_EOL) { - lp++; - } - if (!*lp || *lp=='#' || *lp=='>') { - break; - } - if (*lp!=';') { - - memcpy(line,lp,sizeof(line)); - line[sizeof(line)-1]=0; - cp=line; - for (uint32_t i=0; i0) *response+=",\""; - } - *response+=String(EncodeLightId(hue_devs+devices_present+1))+"\":"; - Script_HueStatus(response,hue_devs); - } - - hue_devs++; - } - if (*lp==SCRIPT_EOL) { - lp++; - } else { - lp = strchr(lp, SCRIPT_EOL); - if (!lp) break; - lp++; - } - } -#if 0 - if (response) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Hue: %d"), hue_devs); - toLog(">>>>"); - toLog(response->c_str()); - toLog(response->c_str()+LOGSZ); - } -#endif -} - -const char sHUE_LIGHT_RESPONSE_JSON[] PROGMEM = - "{\"success\":{\"/lights/{id/state/{cm\":{re}}"; - -const char sHUE_SENSOR_RESPONSE_JSON[] PROGMEM = - "{\"success\":{\"/lights/{id/state/{cm\":{re}}"; - -const char sHUE_ERROR_JSON[] PROGMEM = - "[{\"error\":{\"type\":901,\"address\":\"/\",\"description\":\"Internal Error\"}}]"; - - - -void Script_Handle_Hue(String *path) { - String response; - int code = 200; - uint16_t tmp = 0; - uint16_t hue = 0; - uint8_t sat = 0; - uint8_t bri = 254; - uint16_t ct = 0; - bool resp = false; - - uint8_t device = DecodeLightId(atoi(path->c_str())); - uint8_t index = device-devices_present-1; - - if (Webserver->args()) { - response = "["; - - StaticJsonBuffer<400> jsonBuffer; - JsonObject &hue_json = jsonBuffer.parseObject(Webserver->arg((Webserver->args())-1)); - if (hue_json.containsKey("on")) { - - response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); - response.replace("{id", String(EncodeLightId(device))); - response.replace("{cm", "on"); - - bool on = hue_json["on"]; - switch(on) - { - case false : glob_script_mem.fvars[hue_script[index].index[0]-1]=0; - response.replace("{re", "false"); - break; - case true : glob_script_mem.fvars[hue_script[index].index[0]-1]=1; - response.replace("{re", "true"); - break; - } - glob_script_mem.type[hue_script[index].vindex[0]].bits.changed=1; - resp = true; - } - if (hue_json.containsKey("bri")) { - tmp = hue_json["bri"]; - bri=tmp; - if (254 <= bri) { bri = 255; } - if (resp) { response += ","; } - response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); - response.replace("{id", String(EncodeLightId(device))); - response.replace("{cm", "bri"); - response.replace("{re", String(tmp)); - glob_script_mem.fvars[hue_script[index].index[1]-1]=bri; - glob_script_mem.type[hue_script[index].vindex[1]].bits.changed=1; - resp = true; - } - if (hue_json.containsKey("xy")) { - float x, y; - x = hue_json["xy"][0]; - y = hue_json["xy"][1]; - const String &x_str = hue_json["xy"][0]; - const String &y_str = hue_json["xy"][1]; - uint8_t rr,gg,bb; - LightStateClass::XyToRgb(x, y, &rr, &gg, &bb); - LightStateClass::RgbToHsb(rr, gg, bb, &hue, &sat, nullptr); - if (resp) { response += ","; } - response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); - response.replace("{id", String(device)); - response.replace("{cm", "xy"); - response.replace("{re", "[" + x_str + "," + y_str + "]"); - glob_script_mem.fvars[hue_script[index].index[2]-1]=hue; - glob_script_mem.type[hue_script[index].vindex[2]].bits.changed=1; - glob_script_mem.fvars[hue_script[index].index[3]-1]=sat; - glob_script_mem.type[hue_script[index].vindex[3]].bits.changed=1; - resp = true; - } - - if (hue_json.containsKey("hue")) { - tmp = hue_json["hue"]; - - - hue=tmp; - if (resp) { response += ","; } - response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); - response.replace("{id", String(EncodeLightId(device))); - response.replace("{cm", "hue"); - response.replace("{re", String(tmp)); - glob_script_mem.fvars[hue_script[index].index[2]-1]=hue; - glob_script_mem.type[hue_script[index].vindex[2]].bits.changed=1; - resp = true; - } - if (hue_json.containsKey("sat")) { - tmp = hue_json["sat"]; - sat=tmp; - if (254 <= sat) { sat = 255; } - if (resp) { response += ","; } - response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); - response.replace("{id", String(EncodeLightId(device))); - response.replace("{cm", "sat"); - response.replace("{re", String(tmp)); - glob_script_mem.fvars[hue_script[index].index[3]-1]=sat; - glob_script_mem.type[hue_script[index].vindex[3]].bits.changed=1; - resp = true; - } - if (hue_json.containsKey("ct")) { - ct = hue_json["ct"]; - if (resp) { response += ","; } - response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); - response.replace("{id", String(EncodeLightId(device))); - response.replace("{cm", "ct"); - response.replace("{re", String(ct)); - glob_script_mem.fvars[hue_script[index].index[4]-1]=ct; - glob_script_mem.type[hue_script[index].vindex[4]].bits.changed=1; - resp = true; - } - response += "]"; - - } else { - response = FPSTR(sHUE_ERROR_JSON); - } - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str()); - WSSend(code, CT_JSON, response); - if (resp) { - Run_Scripter(">E",2,0); - } -} -#endif - - -#ifdef USE_SCRIPT_SUB_COMMAND -bool Script_SubCmd(void) { - if (!bitRead(Settings.rule_enabled, 0)) return false; - - if (tasm_cmd_activ) return false; - - char command[CMDSZ]; - strlcpy(command,XdrvMailbox.topic,CMDSZ); - uint32_t pl=XdrvMailbox.payload; - char pld[64]; - strlcpy(pld,XdrvMailbox.data,sizeof(pld)); - - char cmdbuff[128]; - char *cp=cmdbuff; - *cp++='#'; - strcpy(cp,XdrvMailbox.topic); - uint8_t tlen=strlen(XdrvMailbox.topic); - cp+=tlen; - if (XdrvMailbox.index > 0) { - *cp++=XdrvMailbox.index|0x30; - tlen++; - } - if ((XdrvMailbox.payload>0) || (XdrvMailbox.data_len>0)) { - *cp++='('; - strncpy(cp,XdrvMailbox.data,XdrvMailbox.data_len); - cp+=XdrvMailbox.data_len; - *cp++=')'; - *cp=0; - } - - uint32_t res=Run_Scripter(cmdbuff,tlen+1,0); - - if (res) return false; - else { - if (pl>=0) { - Response_P(S_JSON_COMMAND_NVALUE, command, pl); - } else { - Response_P(S_JSON_COMMAND_SVALUE, command, pld); - } - } - return true; -} -#endif - -void execute_script(char *script) { - char *svd_sp=glob_script_mem.scriptptr; - strcat(script,"\n#"); - glob_script_mem.scriptptr=script; - Run_Scripter(">",1,0); - glob_script_mem.scriptptr=svd_sp; -} -#define D_CMND_SCRIPT "Script" -#define D_CMND_SUBSCRIBE "Subscribe" -#define D_CMND_UNSUBSCRIBE "Unsubscribe" - -enum ScriptCommands { CMND_SCRIPT,CMND_SUBSCRIBE, CMND_UNSUBSCRIBE }; -const char kScriptCommands[] PROGMEM = D_CMND_SCRIPT "|" D_CMND_SUBSCRIBE "|" D_CMND_UNSUBSCRIBE; - -bool ScriptCommand(void) { - char command[CMDSZ]; - bool serviced = true; - uint8_t index = XdrvMailbox.index; - - if (tasm_cmd_activ) return false; - - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kScriptCommands); - if (-1 == command_code) { - serviced = false; - } - else if ((CMND_SCRIPT == command_code) && (index > 0)) { - - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 4)) { - switch (XdrvMailbox.payload) { - case 0: - case 1: - bitWrite(Settings.rule_enabled, index -1, XdrvMailbox.payload); - break; -#ifdef xSCRIPT_STRIP_COMMENTS - case 2: - bitWrite(Settings.rule_enabled, 1,0); - break; - case 3: - bitWrite(Settings.rule_enabled, 1,1); - break; -#endif - } - } else { - if ('>' == XdrvMailbox.data[0]) { - - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%s\"}"),command,XdrvMailbox.data); - if (bitRead(Settings.rule_enabled, 0)) { - for (uint8_t count=0; count> 1; -} - -void dateTime(uint16_t* date, uint16_t* time) { - - *date = xFAT_DATE(RtcTime.year,RtcTime.month, RtcTime.day_of_month); - - *time = xFAT_TIME(RtcTime.hour,RtcTime.minute,RtcTime.second); -} - -#endif - - - -#ifdef SUPPORT_MQTT_EVENT -# 4281 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_scripter.ino" -bool ScriptMqttData(void) -{ - bool serviced = false; - - toLog(XdrvMailbox.data); - if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > 256) { - return false; - } - String sTopic = XdrvMailbox.topic; - String sData = XdrvMailbox.data; - - MQTT_Subscription event_item; - - for (uint32_t index = 0; index < subscriptions.size(); index++) { - event_item = subscriptions.get(index); - - - if (sTopic.startsWith(event_item.Topic)) { - - serviced = true; - String value; - String lkey; - if (event_item.Key.length() == 0) { - value = sData; - } else { - StaticJsonBuffer<400> jsonBuf; - JsonObject& jsonData = jsonBuf.parseObject(sData); - String key1 = event_item.Key; - String key2; - if (!jsonData.success()) break; - int dot; - if ((dot = key1.indexOf('.')) > 0) { - key2 = key1.substring(dot+1); - key1 = key1.substring(0, dot); - lkey=key2; - if (!jsonData[key1][key2].success()) break; - value = (const char *)jsonData[key1][key2]; - } else { - if (!jsonData[key1].success()) break; - value = (const char *)jsonData[key1]; - lkey=key1; - } - } - value.trim(); - char sbuffer[128]; - - if (!strncmp(lkey.c_str(),"Epoch",5)) { - uint32_t ep=atoi(value.c_str())-(uint32_t)EPOCH_OFFSET; - snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=%d\n"), event_item.Event.c_str(),ep); - } else { - snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=\"%s\"\n"), event_item.Event.c_str(), value.c_str()); - } - - execute_script(sbuffer); - } - } - return serviced; -} -# 4356 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_scripter.ino" -String ScriptSubscribe(const char *data, int data_len) -{ - MQTT_Subscription subscription_item; - String events; - if (data_len > 0) { - char parameters[data_len+1]; - memcpy(parameters, data, data_len); - parameters[data_len] = '\0'; - String event_name, topic, key; - - char * pos = strtok(parameters, ","); - if (pos) { - event_name = Trim(pos); - pos = strtok(nullptr, ","); - if (pos) { - topic = Trim(pos); - pos = strtok(nullptr, ","); - if (pos) { - key = Trim(pos); - } - } - } - - - if (event_name.length() > 0 && topic.length() > 0) { - - for (uint32_t index=0; index < subscriptions.size(); index++) { - if (subscriptions.get(index).Event.equals(event_name)) { - - String stopic = subscriptions.get(index).Topic + "/#"; - MqttUnsubscribe(stopic.c_str()); - subscriptions.remove(index); - break; - } - } - - if (!topic.endsWith("#")) { - if (topic.endsWith("/")) { - topic.concat("#"); - } else { - topic.concat("/#"); - } - } - - - subscription_item.Event = event_name; - subscription_item.Topic = topic.substring(0, topic.length() - 2); - subscription_item.Key = key; - subscriptions.add(subscription_item); - - MqttSubscribe(topic.c_str()); - events.concat(event_name + "," + topic - + (key.length()>0 ? "," : "") - + key); - } else { - events = D_JSON_WRONG_PARAMETERS; - } - } else { - - for (uint32_t index=0; index < subscriptions.size(); index++) { - subscription_item = subscriptions.get(index); - events.concat(subscription_item.Event + "," + subscription_item.Topic - + (subscription_item.Key.length()>0 ? "," : "") - + subscription_item.Key + "; "); - } - } - return events; -} -# 4436 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_10_scripter.ino" -String ScriptUnsubscribe(const char * data, int data_len) -{ - MQTT_Subscription subscription_item; - String events; - if (data_len > 0) { - for (uint32_t index = 0; index < subscriptions.size(); index++) { - subscription_item = subscriptions.get(index); - if (subscription_item.Event.equalsIgnoreCase(data)) { - String stopic = subscription_item.Topic + "/#"; - MqttUnsubscribe(stopic.c_str()); - events = subscription_item.Event; - subscriptions.remove(index); - break; - } - } - } else { - - String stopic; - while (subscriptions.size() > 0) { - events.concat(subscriptions.get(0).Event + "; "); - stopic = subscriptions.get(0).Topic + "/#"; - MqttUnsubscribe(stopic.c_str()); - subscriptions.remove(0); - } - } - return events; -} -#endif - - - -#ifdef USE_SCRIPT_WEB_DISPLAY - -void Script_Check_HTML_Setvars(void) { - - if (!HttpCheckPriviledgedAccess()) { return; } - - if (Webserver->hasArg("sv")) { - String stmp = Webserver->arg("sv"); - char cmdbuf[64]; - memset(cmdbuf,0,sizeof(cmdbuf)); - char *cp=cmdbuf; - *cp++='>'; - strncpy(cp,stmp.c_str(),sizeof(cmdbuf)-1); - char *cp1=strchr(cp,'_'); - if (!cp1) return; - *cp1=0; - char vname[32]; - strncpy(vname,cp,sizeof(vname)); - *cp1='='; - cp1++; - - struct T_INDEX ind; - uint8_t vtype; - isvar(vname,&vtype,&ind,0,0,0); - if (vtype!=NUM_RES && vtype&STYPE) { - - uint8_t tlen=strlen(cp1); - memmove(cp1+1,cp1,tlen); - *cp1='\"'; - *(cp1+tlen+1)='\"'; - } - - - execute_script(cmdbuf); - Run_Scripter(">E",2,0); - } -} - - -const char SCRIPT_MSG_BUTTONa[] PROGMEM = - ""; - -const char SCRIPT_MSG_BUTTONa_TBL[] PROGMEM = - ""; - -const char SCRIPT_MSG_BUTTONb[] PROGMEM = - ""; - -const char SCRIPT_MSG_BUT_START[] PROGMEM = - "
"; -const char SCRIPT_MSG_BUT_START_TBL[] PROGMEM = - ""; - -const char SCRIPT_MSG_BUT_STOP[] PROGMEM = - ""; -const char SCRIPT_MSG_BUT_STOP_TBL[] PROGMEM = - "
"; - -const char SCRIPT_MSG_SLIDER[] PROGMEM = - "
%s
%s%s
" - "
"; - -const char SCRIPT_MSG_CHKBOX[] PROGMEM = - "
"; - -const char SCRIPT_MSG_TEXTINP[] PROGMEM = - "
"; - -const char SCRIPT_MSG_NUMINP[] PROGMEM = - "
"; - - -void ScriptGetVarname(char *nbuf,char *sp, uint32_t blen) { -uint32_t cnt; - for (cnt=0;cntW",-2,0); - if (web_script==99) { - char line[128]; - char tmp[128]; - uint8_t optflg=0; - char *lp=glob_script_mem.section_ptr+2; - while (lp) { - while (*lp==SCRIPT_EOL) { - lp++; - } - if (!*lp || *lp=='#' || *lp=='>') { - break; - } - if (*lp!=';') { - - memcpy(line,lp,sizeof(line)); - line[sizeof(line)-1]=0; - char *cp=line; - for (uint32_t i=0; i0) { - cp="checked='checked'"; - uval=0; - } else { - cp=""; - uval=1; - } - WSContentSend_PD(SCRIPT_MSG_CHKBOX,label,(char*)cp,uval,vname); - - } else if (!strncmp(lin,"bu(",3)) { - char *lp=lin+3; - uint8_t bcnt=0; - char *found=lin; - while (bcnt<4) { - found=strstr(found,"bu("); - if (!found) break; - found+=3; - bcnt++; - } - uint8_t proz=100/bcnt; - if (!optflg && bcnt>1) proz-=2; - if (optflg) WSContentSend_PD(SCRIPT_MSG_BUT_START_TBL); - else WSContentSend_PD(SCRIPT_MSG_BUT_START); - for (uint32_t cnt=0;cnt0) { - cp=ontxt; - uval=0; - } else { - cp=offtxt; - uval=1; - } - if (bcnt>1 && cnt==bcnt-1) { - if (!optflg) proz+=2; - } - if (!optflg) { - WSContentSend_PD(SCRIPT_MSG_BUTTONa,proz,uval,vname,cp); - } else { - WSContentSend_PD(SCRIPT_MSG_BUTTONa_TBL,proz,uval,vname,cp); - } - if (bcnt>1 && cnt%s
"),tmp); - } else { - WSContentSend_PD(PSTR("{s}%s{e}"),tmp); - } - } - } - if (*lp==SCRIPT_EOL) { - lp++; - } else { - lp = strchr(lp, SCRIPT_EOL); - if (!lp) break; - lp++; - } - } - } -} -#endif - - -#ifdef USE_SENDMAIL - -#ifdef ESP8266 -void script_send_email_body(BearSSL::WiFiClientSecure_light *client) { -#else -void script_send_email_body(WiFiClient *client) { -#endif - -uint8_t msect=Run_Scripter(">m",-2,0); - if (msect==99) { - char line[128]; - char tmp[128]; - char *lp=glob_script_mem.section_ptr+2; - while (lp) { - while (*lp==SCRIPT_EOL) { - lp++; - } - if (!*lp || *lp=='#' || *lp=='>') { - break; - } - if (*lp!=';') { - - memcpy(line,lp,sizeof(line)); - line[sizeof(line)-1]=0; - char *cp=line; - for (uint32_t i=0; iprintln(tmp); - } - if (*lp==SCRIPT_EOL) { - lp++; - } else { - lp = strchr(lp, SCRIPT_EOL); - if (!lp) break; - lp++; - } - } - } else { - client->println("*"); - } -} -#endif - -#ifdef USE_SCRIPT_JSON_EXPORT -void ScriptJsonAppend(void) { - uint8_t web_script=Run_Scripter(">J",-2,0); - if (web_script==99) { - char line[128]; - char tmp[128]; - char *lp=glob_script_mem.section_ptr+2; - while (lp) { - while (*lp==SCRIPT_EOL) { - lp++; - } - if (!*lp || *lp=='#' || *lp=='>') { - break; - } - if (*lp!=';') { - - memcpy(line,lp,sizeof(line)); - line[sizeof(line)-1]=0; - char *cp=line; - for (uint32_t i=0; iB",2,0); - fast_script=Run_Scripter(">F",-2,0); -#if defined(USE_SCRIPT_HUE) && defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) - Script_Check_Hue(0); -#endif - } - break; - case FUNC_EVERY_100_MSECOND: - ScripterEvery100ms(); - break; - case FUNC_EVERY_SECOND: - ScriptEverySecond(); - break; - case FUNC_COMMAND: - result = ScriptCommand(); - break; - case FUNC_SET_POWER: -#ifdef SCRIPT_POWER_SECTION - if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">P",2,0); -#else - if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">E",2,0); -#endif - break; - case FUNC_RULES_PROCESS: - if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">E",2,mqtt_data); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_ADD_BUTTON: - WSContentSend_P(HTTP_BTN_MENU_RULES); - break; - case FUNC_WEB_ADD_HANDLER: - Webserver->on("/" WEB_HANDLE_SCRIPT, HandleScriptConfiguration); - Webserver->on("/ta",HTTP_POST, HandleScriptTextareaConfiguration); - -#ifdef USE_SCRIPT_FATFS - Webserver->on("/u3", HTTP_POST,[]() { Webserver->sendHeader("Location","/u3");Webserver->send(303);},script_upload); - Webserver->on("/u3", HTTP_GET,ScriptFileUploadSuccess); - Webserver->on("/upl", HTTP_GET,Script_FileUploadConfiguration); -#endif - break; -#endif - case FUNC_SAVE_BEFORE_RESTART: - if (bitRead(Settings.rule_enabled, 0)) { - Run_Scripter(">R",2,0); - Scripter_save_pvars(); - } - break; -#ifdef SUPPORT_MQTT_EVENT - case FUNC_MQTT_DATA: - if (bitRead(Settings.rule_enabled, 0)) { - result = ScriptMqttData(); - } - break; -#endif -#ifdef USE_SCRIPT_WEB_DISPLAY - case FUNC_WEB_SENSOR: - if (bitRead(Settings.rule_enabled, 0)) { - ScriptWebShow(); - } - break; -#endif - -#ifdef USE_SCRIPT_JSON_EXPORT - case FUNC_JSON_APPEND: - if (bitRead(Settings.rule_enabled, 0)) { - ScriptJsonAppend(); - } - break; -#endif - -#ifdef USE_BUTTON_EVENT - case FUNC_BUTTON_PRESSED: - if (bitRead(Settings.rule_enabled, 0)) { - if ((script_button[XdrvMailbox.index]&1)!=(XdrvMailbox.payload&1)) { - script_button[XdrvMailbox.index]=XdrvMailbox.payload; - Run_Scripter(">b",2,0); - } - } - break; -#endif - - } - return result; -} - - - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_11_knx.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_11_knx.ino" -#ifdef USE_KNX -# 51 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_11_knx.ino" -#define XDRV_11 11 - -#include - -address_t KNX_physs_addr; -address_t KNX_addr; - -#define KNX_Empty 255 - -#define TOGGLE_INHIBIT_TIME 15 - -float last_temp; -float last_hum; -uint8_t toggle_inhibit; - -typedef struct __device_parameters -{ - uint8_t type; - - - - - bool show; - - bool last_state; - - callback_id_t CB_id; - - - - - -} device_parameters_t; - - -device_parameters_t device_param[] = { - { 1, false, false, KNX_Empty }, - { 2, false, false, KNX_Empty }, - { 3, false, false, KNX_Empty }, - { 4, false, false, KNX_Empty }, - { 5, false, false, KNX_Empty }, - { 6, false, false, KNX_Empty }, - { 7, false, false, KNX_Empty }, - { 8, false, false, KNX_Empty }, - { 9, false, false, KNX_Empty }, - { 10, false, false, KNX_Empty }, - { 11, false, false, KNX_Empty }, - { 12, false, false, KNX_Empty }, - { 13, false, false, KNX_Empty }, - { 14, false, false, KNX_Empty }, - { 15, false, false, KNX_Empty }, - { 16, false, false, KNX_Empty }, - { KNX_TEMPERATURE, false, false, KNX_Empty }, - { KNX_HUMIDITY , false, false, KNX_Empty }, - { KNX_ENERGY_VOLTAGE , false, false, KNX_Empty }, - { KNX_ENERGY_CURRENT , false, false, KNX_Empty }, - { KNX_ENERGY_POWER , false, false, KNX_Empty }, - { KNX_ENERGY_POWERFACTOR , false, false, KNX_Empty }, - { KNX_ENERGY_DAILY , false, false, KNX_Empty }, - { KNX_ENERGY_START , false, false, KNX_Empty }, - { KNX_ENERGY_TOTAL , false, false, KNX_Empty }, - { KNX_SLOT1 , false, false, KNX_Empty }, - { KNX_SLOT2 , false, false, KNX_Empty }, - { KNX_SLOT3 , false, false, KNX_Empty }, - { KNX_SLOT4 , false, false, KNX_Empty }, - { KNX_SLOT5 , false, false, KNX_Empty }, - { KNX_Empty, false, false, KNX_Empty} -}; - - -const char * device_param_ga[] = { - D_TIMER_OUTPUT " 1", - D_TIMER_OUTPUT " 2", - D_TIMER_OUTPUT " 3", - D_TIMER_OUTPUT " 4", - D_TIMER_OUTPUT " 5", - D_TIMER_OUTPUT " 6", - D_TIMER_OUTPUT " 7", - D_TIMER_OUTPUT " 8", - D_SENSOR_BUTTON " 1", - D_SENSOR_BUTTON " 2", - D_SENSOR_BUTTON " 3", - D_SENSOR_BUTTON " 4", - D_SENSOR_BUTTON " 5", - D_SENSOR_BUTTON " 6", - D_SENSOR_BUTTON " 7", - D_SENSOR_BUTTON " 8", - D_TEMPERATURE , - D_HUMIDITY , - D_VOLTAGE , - D_CURRENT , - D_POWERUSAGE , - D_POWER_FACTOR , - D_ENERGY_TODAY , - D_ENERGY_YESTERDAY , - D_ENERGY_TOTAL , - D_KNX_TX_SLOT " 1", - D_KNX_TX_SLOT " 2", - D_KNX_TX_SLOT " 3", - D_KNX_TX_SLOT " 4", - D_KNX_TX_SLOT " 5", - nullptr -}; - - -const char *device_param_cb[] = { - D_TIMER_OUTPUT " 1", - D_TIMER_OUTPUT " 2", - D_TIMER_OUTPUT " 3", - D_TIMER_OUTPUT " 4", - D_TIMER_OUTPUT " 5", - D_TIMER_OUTPUT " 6", - D_TIMER_OUTPUT " 7", - D_TIMER_OUTPUT " 8", - D_TIMER_OUTPUT " 1 " D_BUTTON_TOGGLE, - D_TIMER_OUTPUT " 2 " D_BUTTON_TOGGLE, - D_TIMER_OUTPUT " 3 " D_BUTTON_TOGGLE, - D_TIMER_OUTPUT " 4 " D_BUTTON_TOGGLE, - D_TIMER_OUTPUT " 5 " D_BUTTON_TOGGLE, - D_TIMER_OUTPUT " 6 " D_BUTTON_TOGGLE, - D_TIMER_OUTPUT " 7 " D_BUTTON_TOGGLE, - D_TIMER_OUTPUT " 8 " D_BUTTON_TOGGLE, - D_REPLY " " D_TEMPERATURE, - D_REPLY " " D_HUMIDITY, - D_REPLY " " D_VOLTAGE , - D_REPLY " " D_CURRENT , - D_REPLY " " D_POWERUSAGE , - D_REPLY " " D_POWER_FACTOR , - D_REPLY " " D_ENERGY_TODAY , - D_REPLY " " D_ENERGY_YESTERDAY , - D_REPLY " " D_ENERGY_TOTAL , - D_KNX_RX_SLOT " 1", - D_KNX_RX_SLOT " 2", - D_KNX_RX_SLOT " 3", - D_KNX_RX_SLOT " 4", - D_KNX_RX_SLOT " 5", - nullptr -}; - - -#define D_PRFX_KNX "Knx" -#define D_CMND_KNXTXCMND "Tx_Cmnd" -#define D_CMND_KNXTXVAL "Tx_Val" -#define D_CMND_KNX_ENABLED "_Enabled" -#define D_CMND_KNX_ENHANCED "_Enhanced" -#define D_CMND_KNX_PA "_PA" -#define D_CMND_KNX_GA "_GA" -#define D_CMND_KNX_CB "_CB" - -const char kKnxCommands[] PROGMEM = D_PRFX_KNX "|" - D_CMND_KNXTXCMND "|" D_CMND_KNXTXVAL "|" D_CMND_KNX_ENABLED "|" D_CMND_KNX_ENHANCED "|" D_CMND_KNX_PA "|" D_CMND_KNX_GA "|" D_CMND_KNX_CB ; - -void (* const KnxCommand[])(void) PROGMEM = { - &CmndKnxTxCmnd, &CmndKnxTxVal, &CmndKnxEnabled, &CmndKnxEnhanced, &CmndKnxPa, &CmndKnxGa, &CmndKnxCb }; - -uint8_t KNX_GA_Search( uint8_t param, uint8_t start = 0 ) -{ - for (uint32_t i = start; i < Settings.knx_GA_registered; ++i) - { - if ( Settings.knx_GA_param[i] == param ) - { - if ( Settings.knx_GA_addr[i] != 0 ) - { - if ( i >= start ) { return i; } - } - } - } - return KNX_Empty; -} - - -uint8_t KNX_CB_Search( uint8_t param, uint8_t start = 0 ) -{ - for (uint32_t i = start; i < Settings.knx_CB_registered; ++i) - { - if ( Settings.knx_CB_param[i] == param ) - { - if ( Settings.knx_CB_addr[i] != 0 ) - { - if ( i >= start ) { return i; } - } - } - } - return KNX_Empty; -} - - -void KNX_ADD_GA( uint8_t GAop, uint8_t GA_FNUM, uint8_t GA_AREA, uint8_t GA_FDEF ) -{ - - if ( Settings.knx_GA_registered >= MAX_KNX_GA ) { return; } - if ( GA_FNUM == 0 && GA_AREA == 0 && GA_FDEF == 0 ) { return; } - - - Settings.knx_GA_param[Settings.knx_GA_registered] = GAop; - KNX_addr.ga.area = GA_FNUM; - KNX_addr.ga.line = GA_AREA; - KNX_addr.ga.member = GA_FDEF; - Settings.knx_GA_addr[Settings.knx_GA_registered] = KNX_addr.value; - - Settings.knx_GA_registered++; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ADD " GA #%d: %s " D_TO " %d/%d/%d"), - Settings.knx_GA_registered, - device_param_ga[GAop-1], - GA_FNUM, GA_AREA, GA_FDEF ); -} - - -void KNX_DEL_GA( uint8_t GAnum ) -{ - - uint8_t dest_offset = 0; - uint8_t src_offset = 0; - uint8_t len = 0; - - - Settings.knx_GA_param[GAnum-1] = 0; - - if (GAnum == 1) - { - - src_offset = 1; - - - - len = (Settings.knx_GA_registered - 1); - } - else if (GAnum == Settings.knx_GA_registered) - { - - } - else - { - - - - - dest_offset = GAnum -1 ; - src_offset = dest_offset + 1; - len = (Settings.knx_GA_registered - GAnum); - } - - if (len > 0) - { - memmove(Settings.knx_GA_param + dest_offset, Settings.knx_GA_param + src_offset, len * sizeof(uint8_t)); - memmove(Settings.knx_GA_addr + dest_offset, Settings.knx_GA_addr + src_offset, len * sizeof(uint16_t)); - } - - Settings.knx_GA_registered--; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " GA #%d"), - GAnum ); -} - - -void KNX_ADD_CB( uint8_t CBop, uint8_t CB_FNUM, uint8_t CB_AREA, uint8_t CB_FDEF ) -{ - - if ( Settings.knx_CB_registered >= MAX_KNX_CB ) { return; } - if ( CB_FNUM == 0 && CB_AREA == 0 && CB_FDEF == 0 ) { return; } - - - if ( device_param[CBop-1].CB_id == KNX_Empty ) - { - - device_param[CBop-1].CB_id = knx.callback_register("", KNX_CB_Action, &device_param[CBop-1]); - - - - - } - - Settings.knx_CB_param[Settings.knx_CB_registered] = CBop; - KNX_addr.ga.area = CB_FNUM; - KNX_addr.ga.line = CB_AREA; - KNX_addr.ga.member = CB_FDEF; - Settings.knx_CB_addr[Settings.knx_CB_registered] = KNX_addr.value; - - knx.callback_assign( device_param[CBop-1].CB_id, KNX_addr ); - - Settings.knx_CB_registered++; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ADD " CB #%d: %d/%d/%d " D_TO " %s"), - Settings.knx_CB_registered, - CB_FNUM, CB_AREA, CB_FDEF, - device_param_cb[CBop-1] ); -} - - -void KNX_DEL_CB( uint8_t CBnum ) -{ - uint8_t oldparam = Settings.knx_CB_param[CBnum-1]; - uint8_t dest_offset = 0; - uint8_t src_offset = 0; - uint8_t len = 0; - - - knx.callback_unassign(CBnum-1); - Settings.knx_CB_param[CBnum-1] = 0; - - if (CBnum == 1) - { - - src_offset = 1; - - - - len = (Settings.knx_CB_registered - 1); - } - else if (CBnum == Settings.knx_CB_registered) - { - - } - else - { - - - - - dest_offset = CBnum -1 ; - src_offset = dest_offset + 1; - len = (Settings.knx_CB_registered - CBnum); - } - - if (len > 0) - { - memmove(Settings.knx_CB_param + dest_offset, Settings.knx_CB_param + src_offset, len * sizeof(uint8_t)); - memmove(Settings.knx_CB_addr + dest_offset, Settings.knx_CB_addr + src_offset, len * sizeof(uint16_t)); - } - - Settings.knx_CB_registered--; - - - if ( KNX_CB_Search( oldparam ) == KNX_Empty ) { - knx.callback_deregister( device_param[oldparam-1].CB_id ); - device_param[oldparam-1].CB_id = KNX_Empty; - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " CB #%d"), CBnum ); -} - - -bool KNX_CONFIG_NOT_MATCH(void) -{ - - for (uint32_t i = 0; i < KNX_MAX_device_param; ++i) - { - if ( !device_param[i].show ) { - - - - if ( KNX_GA_Search(i+1) != KNX_Empty ) { return true; } - - if ( i < 8 ) - { - if ( KNX_CB_Search(i+1) != KNX_Empty ) { return true; } - if ( KNX_CB_Search(i+9) != KNX_Empty ) { return true; } - } - - if ( i > 15 ) - { - if ( KNX_CB_Search(i+1) != KNX_Empty ) { return true; } - } - } - } - - - for (uint32_t i = 0; i < Settings.knx_GA_registered; ++i) - { - if ( Settings.knx_GA_param[i] != 0 ) - { - if ( Settings.knx_GA_addr[i] == 0 ) - { - return true; - } - } - } - for (uint32_t i = 0; i < Settings.knx_CB_registered; ++i) - { - if ( Settings.knx_CB_param[i] != 0 ) - { - if ( Settings.knx_CB_addr[i] == 0 ) - { - return true; - } - } - } - - return false; -} - - -void KNXStart(void) -{ - knx.start(nullptr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_START)); -} - - -void KNX_INIT(void) -{ - - if (Settings.knx_GA_registered > MAX_KNX_GA) { Settings.knx_GA_registered = MAX_KNX_GA; } - if (Settings.knx_CB_registered > MAX_KNX_CB) { Settings.knx_CB_registered = MAX_KNX_CB; } - - - KNX_physs_addr.value = Settings.knx_physsical_addr; - knx.physical_address_set( KNX_physs_addr ); -# 472 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_11_knx.ino" - for (uint32_t i = 0; i < devices_present; ++i) - { - device_param[i].show = true; - } - for (uint32_t i = GPIO_SWT1; i < GPIO_SWT4 + 1; ++i) - { - if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_SWT1 + 8].show = true; } - } - for (uint32_t i = GPIO_KEY1; i < GPIO_KEY4 + 1; ++i) - { - if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_KEY1 + 8].show = true; } - } - for (uint32_t i = GPIO_SWT1_NP; i < GPIO_SWT4_NP + 1; ++i) - { - if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_SWT1_NP + 8].show = true; } - } - for (uint32_t i = GPIO_KEY1_NP; i < GPIO_KEY4_NP + 1; ++i) - { - if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_KEY1_NP + 8].show = true; } - } - if (GetUsedInModule(GPIO_DHT11, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } - if (GetUsedInModule(GPIO_DHT22, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } - if (GetUsedInModule(GPIO_SI7021, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } -#ifdef USE_DS18x20 - if (GetUsedInModule(GPIO_DSB, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } -#endif - if (GetUsedInModule(GPIO_DHT11, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } - if (GetUsedInModule(GPIO_DHT22, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } - if (GetUsedInModule(GPIO_SI7021, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } - -#if defined(USE_ENERGY_SENSOR) - - if ( energy_flg != ENERGY_NONE ) { - device_param[KNX_ENERGY_POWER-1].show = true; - device_param[KNX_ENERGY_DAILY-1].show = true; - device_param[KNX_ENERGY_START-1].show = true; - device_param[KNX_ENERGY_TOTAL-1].show = true; - device_param[KNX_ENERGY_VOLTAGE-1].show = true; - device_param[KNX_ENERGY_CURRENT-1].show = true; - device_param[KNX_ENERGY_POWERFACTOR-1].show = true; - } -#endif - -#ifdef USE_RULES - device_param[KNX_SLOT1-1].show = true; - device_param[KNX_SLOT2-1].show = true; - device_param[KNX_SLOT3-1].show = true; - device_param[KNX_SLOT4-1].show = true; - device_param[KNX_SLOT5-1].show = true; -#endif - - - if (KNX_CONFIG_NOT_MATCH()) { - Settings.knx_GA_registered = 0; - Settings.knx_CB_registered = 0; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " " D_KNX_PARAMETERS)); - } - - - - - uint8_t j; - for (uint32_t i = 0; i < Settings.knx_CB_registered; ++i) - { - j = Settings.knx_CB_param[i]; - if ( j > 0 ) - { - device_param[j-1].CB_id = knx.callback_register("", KNX_CB_Action, &device_param[j-1]); - - - - KNX_addr.value = Settings.knx_CB_addr[i]; - knx.callback_assign( device_param[j-1].CB_id, KNX_addr ); - } - } -} - - -void KNX_CB_Action(message_t const &msg, void *arg) -{ - device_parameters_t *chan = (device_parameters_t *)arg; - if (!(Settings.flag.knx_enabled)) { return; } - - char tempchar[33]; - - if (msg.data_len == 1) { - - sprintf(tempchar,"%d",msg.data[0]); - } else { - - float tempvar = knx.data_to_2byte_float(msg.data); - dtostrfd(tempvar,2,tempchar); - } - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX D_RECEIVED_FROM " %d.%d.%d " D_COMMAND " %s: %s " D_TO " %s"), - msg.received_on.ga.area, msg.received_on.ga.line, msg.received_on.ga.member, - (msg.ct == KNX_CT_WRITE) ? D_KNX_COMMAND_WRITE : (msg.ct == KNX_CT_READ) ? D_KNX_COMMAND_READ : D_KNX_COMMAND_OTHER, - tempchar, - device_param_cb[(chan->type)-1]); - - switch (msg.ct) - { - case KNX_CT_WRITE: - if (chan->type < 9) - { - ExecuteCommandPower(chan->type, msg.data[0], SRC_KNX); - } - else if (chan->type < 17) - { - if (!toggle_inhibit) { - ExecuteCommandPower((chan->type) -8, POWER_TOGGLE, SRC_KNX); - if (Settings.flag.knx_enable_enhancement) { - toggle_inhibit = TOGGLE_INHIBIT_TIME; - } - } - } -#ifdef USE_RULES - else if ((chan->type >= KNX_SLOT1) && (chan->type <= KNX_SLOT5)) - { - if (!toggle_inhibit) { - char command[25]; - if (msg.data_len == 1) { - - snprintf_P(command, sizeof(command), PSTR("event KNXRX_CMND%d=%d"), ((chan->type) - KNX_SLOT1 + 1 ), msg.data[0]); - } else { - - snprintf_P(command, sizeof(command), PSTR("event KNXRX_VAL%d=%s"), ((chan->type) - KNX_SLOT1 + 1 ), tempchar); - } - ExecuteCommand(command, SRC_KNX); - if (Settings.flag.knx_enable_enhancement) { - toggle_inhibit = TOGGLE_INHIBIT_TIME; - } - } - } -#endif - break; - - case KNX_CT_READ: - if (chan->type < 9) - { - knx.answer_1bit(msg.received_on, chan->last_state); - if (Settings.flag.knx_enable_enhancement) { - knx.answer_1bit(msg.received_on, chan->last_state); - knx.answer_1bit(msg.received_on, chan->last_state); - } - } - else if (chan->type == KNX_TEMPERATURE) - { - knx.answer_2byte_float(msg.received_on, last_temp); - if (Settings.flag.knx_enable_enhancement) { - knx.answer_2byte_float(msg.received_on, last_temp); - knx.answer_2byte_float(msg.received_on, last_temp); - } - } - else if (chan->type == KNX_HUMIDITY) - { - knx.answer_2byte_float(msg.received_on, last_hum); - if (Settings.flag.knx_enable_enhancement) { - knx.answer_2byte_float(msg.received_on, last_hum); - knx.answer_2byte_float(msg.received_on, last_hum); - } - } -#ifdef USE_RULES - else if ((chan->type >= KNX_SLOT1) && (chan->type <= KNX_SLOT5)) - { - if (!toggle_inhibit) { - char command[25]; - snprintf_P(command, sizeof(command), PSTR("event KNXRX_REQ%d"), ((chan->type) - KNX_SLOT1 + 1 ) ); - ExecuteCommand(command, SRC_KNX); - if (Settings.flag.knx_enable_enhancement) { - toggle_inhibit = TOGGLE_INHIBIT_TIME; - } - } - } -#endif - break; - } -} - - -void KnxUpdatePowerState(uint8_t device, power_t state) -{ - if (!(Settings.flag.knx_enabled)) { return; } - - device_param[device -1].last_state = bitRead(state, device -1); - - - uint8_t i = KNX_GA_Search(device); - while ( i != KNX_Empty ) { - KNX_addr.value = Settings.knx_GA_addr[i]; - knx.write_1bit(KNX_addr, device_param[device -1].last_state); - if (Settings.flag.knx_enable_enhancement) { - knx.write_1bit(KNX_addr, device_param[device -1].last_state); - knx.write_1bit(KNX_addr, device_param[device -1].last_state); - } - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), - device_param_ga[device -1], device_param[device -1].last_state, - KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - - i = KNX_GA_Search(device, i + 1); - } -} - - -void KnxSendButtonPower(void) -{ - if (!(Settings.flag.knx_enabled)) { return; } - - uint32_t key = (XdrvMailbox.payload >> 16) & 0xFF; - uint32_t device = XdrvMailbox.payload & 0xFF; - uint32_t state = (XdrvMailbox.payload >> 8) & 0xFF; -# 692 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_11_knx.ino" - uint8_t i = KNX_GA_Search(device + 8); - while ( i != KNX_Empty ) { - KNX_addr.value = Settings.knx_GA_addr[i]; - knx.write_1bit(KNX_addr, !(state == 0)); - if (Settings.flag.knx_enable_enhancement) { - knx.write_1bit(KNX_addr, !(state == 0)); - knx.write_1bit(KNX_addr, !(state == 0)); - } - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), - device_param_ga[device + 7], !(state == 0), - KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - - i = KNX_GA_Search(device + 8, i + 1); - } - -} - - -void KnxSensor(uint8_t sensor_type, float value) -{ - if (sensor_type == KNX_TEMPERATURE) - { - last_temp = value; - } else if (sensor_type == KNX_HUMIDITY) - { - last_hum = value; - } - - if (!(Settings.flag.knx_enabled)) { return; } - - uint8_t i = KNX_GA_Search(sensor_type); - while ( i != KNX_Empty ) { - KNX_addr.value = Settings.knx_GA_addr[i]; - knx.write_2byte_float(KNX_addr, value); - if (Settings.flag.knx_enable_enhancement) { - knx.write_2byte_float(KNX_addr, value); - knx.write_2byte_float(KNX_addr, value); - } - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s " D_SENT_TO " %d.%d.%d "), - device_param_ga[sensor_type -1], - KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - - i = KNX_GA_Search(sensor_type, i+1); - } -} - - - - - - -#ifdef USE_WEBSERVER -#ifdef USE_KNX_WEB_MENU -const char S_CONFIGURE_KNX[] PROGMEM = D_CONFIGURE_KNX; - -const char HTTP_BTN_MENU_KNX[] PROGMEM = - "

"; - -const char HTTP_FORM_KNX[] PROGMEM = - "
" - " " D_KNX_PARAMETERS " " - "
" - "
" - "" D_KNX_PHYSICAL_ADDRESS " " - " . " - " . " - "" - "

" D_KNX_PHYSICAL_ADDRESS_NOTE "

" - "

" - - "
" - "" D_KNX_GROUP_ADDRESS_TO_WRITE "
" - - " / " - " / " - " "; - -const char HTTP_FORM_KNX_ADD_BTN[] PROGMEM = - "

" - ""; - -const char HTTP_FORM_KNX_ADD_TABLE_ROW[] PROGMEM = - "" - ""; - -const char HTTP_FORM_KNX3[] PROGMEM = - "
%s -> %d / %d / %d

" - "
" - "" D_KNX_GROUP_ADDRESS_TO_READ "
"; - -const char HTTP_FORM_KNX4[] PROGMEM = - "-> -> ")); - WSContentSend_P(HTTP_FORM_KNX_GA, "GA_FNUM", "GA_AREA", "GA_FDEF"); - WSContentSend_P(HTTP_FORM_KNX_ADD_BTN, "GAwarning", (Settings.knx_GA_registered < MAX_KNX_GA) ? "" : "disabled", 1); - for (uint32_t i = 0; i < Settings.knx_GA_registered ; ++i) - { - if ( Settings.knx_GA_param[i] ) - { - KNX_addr.value = Settings.knx_GA_addr[i]; - WSContentSend_P(HTTP_FORM_KNX_ADD_TABLE_ROW, device_param_ga[Settings.knx_GA_param[i]-1], KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member, i +1); - } - } - - WSContentSend_P(HTTP_FORM_KNX3); - WSContentSend_P(HTTP_FORM_KNX_GA, "CB_FNUM", "CB_AREA", "CB_FDEF"); - WSContentSend_P(HTTP_FORM_KNX4); - - uint8_t j; - for (uint32_t i = 0; i < KNX_MAX_device_param ; i++) - { - - if ( (i > 8) && (i < 16) ) { j=i-8; } else { j=i; } - if ( i == 8 ) { j = 0; } - if ( device_param[j].show ) - { - WSContentSend_P(HTTP_FORM_KNX_OPT, device_param[i].type, device_param_cb[i]); - } - } - WSContentSend_P(PSTR(" ")); - WSContentSend_P(HTTP_FORM_KNX_ADD_BTN, "CBwarning", (Settings.knx_CB_registered < MAX_KNX_CB) ? "" : "disabled", 2); - - for (uint32_t i = 0; i < Settings.knx_CB_registered ; ++i) - { - if ( Settings.knx_CB_param[i] ) - { - KNX_addr.value = Settings.knx_CB_addr[i]; - WSContentSend_P(HTTP_FORM_KNX_ADD_TABLE_ROW2, KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member, device_param_cb[Settings.knx_CB_param[i]-1], i +1); - } - } - WSContentSend_P(PSTR("
")); - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); - } - -} - - -void KNX_Save_Settings(void) -{ - String stmp; - address_t KNX_addr; - - Settings.flag.knx_enabled = Webserver->hasArg("b1"); - Settings.flag.knx_enable_enhancement = Webserver->hasArg("b2"); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ENABLED ": %d, " D_KNX_ENHANCEMENT ": %d"), - Settings.flag.knx_enabled, Settings.flag.knx_enable_enhancement ); - - stmp = Webserver->arg("area"); - KNX_addr.pa.area = stmp.toInt(); - stmp = Webserver->arg("line"); - KNX_addr.pa.line = stmp.toInt(); - stmp = Webserver->arg("member"); - KNX_addr.pa.member = stmp.toInt(); - Settings.knx_physsical_addr = KNX_addr.value; - knx.physical_address_set( KNX_addr ); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_KNX_PHYSICAL_ADDRESS ": %d.%d.%d "), - KNX_addr.pa.area, KNX_addr.pa.line, KNX_addr.pa.member ); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "GA: %d"), - Settings.knx_GA_registered ); - for (uint32_t i = 0; i < Settings.knx_GA_registered ; ++i) - { - KNX_addr.value = Settings.knx_GA_addr[i]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "GA #%d: %s " D_TO " %d/%d/%d"), - i+1, device_param_ga[Settings.knx_GA_param[i]-1], - KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member ); - - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "CB: %d"), - Settings.knx_CB_registered ); - for (uint32_t i = 0; i < Settings.knx_CB_registered ; ++i) - { - KNX_addr.value = Settings.knx_CB_addr[i]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "CB #%d: %d/%d/%d " D_TO " %s"), - i+1, - KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member, - device_param_cb[Settings.knx_CB_param[i]-1] ); - } -} - -#endif -#endif - - - - - -void CmndKnxTxCmnd(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings.flag.knx_enabled) { - - - - uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1); - while ( i != KNX_Empty ) { - KNX_addr.value = Settings.knx_GA_addr[i]; - knx.write_1bit(KNX_addr, !(XdrvMailbox.payload == 0)); - if (Settings.flag.knx_enable_enhancement) { - knx.write_1bit(KNX_addr, !(XdrvMailbox.payload == 0)); - knx.write_1bit(KNX_addr, !(XdrvMailbox.payload == 0)); - } - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), - device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], !(XdrvMailbox.payload == 0), - KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - - i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1); - } - ResponseCmndIdxChar (XdrvMailbox.data ); - } -} - -void CmndKnxTxVal(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings.flag.knx_enabled) { - - - - uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1); - while ( i != KNX_Empty ) { - KNX_addr.value = Settings.knx_GA_addr[i]; - - float tempvar = CharToFloat(XdrvMailbox.data); - dtostrfd(tempvar,2,XdrvMailbox.data); - - knx.write_2byte_float(KNX_addr, tempvar); - if (Settings.flag.knx_enable_enhancement) { - knx.write_2byte_float(KNX_addr, tempvar); - knx.write_2byte_float(KNX_addr, tempvar); - } - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d.%d.%d"), - device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], XdrvMailbox.data, - KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - - i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1); - } - ResponseCmndIdxChar (XdrvMailbox.data ); - } -} - -void CmndKnxEnabled(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { - Settings.flag.knx_enabled = XdrvMailbox.payload; - } - ResponseCmndChar (GetStateText(Settings.flag.knx_enabled) ); -} - -void CmndKnxEnhanced(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { - Settings.flag.knx_enable_enhancement = XdrvMailbox.payload; - } - ResponseCmndChar (GetStateText(Settings.flag.knx_enable_enhancement) ); -} - -void CmndKnxPa(void) -{ - if (XdrvMailbox.data_len) { - if (strstr(XdrvMailbox.data, ".") != nullptr) { - char sub_string[XdrvMailbox.data_len]; - - int pa_area = atoi(subStr(sub_string, XdrvMailbox.data, ".", 1)); - int pa_line = atoi(subStr(sub_string, XdrvMailbox.data, ".", 2)); - int pa_member = atoi(subStr(sub_string, XdrvMailbox.data, ".", 3)); - - if ( ((pa_area == 0) && (pa_line == 0) && (pa_member == 0)) - || (pa_area > 15) || (pa_line > 15) || (pa_member > 255) ) { - Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); - return; - } - - KNX_addr.pa.area = pa_area; - KNX_addr.pa.line = pa_line; - KNX_addr.pa.member = pa_member; - Settings.knx_physsical_addr = KNX_addr.value; - } - } - KNX_addr.value = Settings.knx_physsical_addr; - Response_P (PSTR("{\"%s\":\"%d.%d.%d\"}"), - XdrvMailbox.command, KNX_addr.pa.area, KNX_addr.pa.line, KNX_addr.pa.member ); -} - -void CmndKnxGa(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNX_GA)) { - if (XdrvMailbox.data_len) { - if (strstr(XdrvMailbox.data, ",") != nullptr) { - char sub_string[XdrvMailbox.data_len]; - - int ga_option = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1)); - int ga_area = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - int ga_line = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); - int ga_member = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4)); - - if ( ((ga_area == 0) && (ga_line == 0) && (ga_member == 0)) - || (ga_area > 31) || (ga_line > 7) || (ga_member > 255) - || (ga_option < 0) || ((ga_option > KNX_MAX_device_param ) && (ga_option != KNX_Empty)) - || (!device_param[ga_option-1].show) ) { - Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); - return; - } - - KNX_addr.ga.area = ga_area; - KNX_addr.ga.line = ga_line; - KNX_addr.ga.member = ga_member; - - if ( XdrvMailbox.index > Settings.knx_GA_registered ) { - Settings.knx_GA_registered ++; - XdrvMailbox.index = Settings.knx_GA_registered; - } - - Settings.knx_GA_addr[XdrvMailbox.index -1] = KNX_addr.value; - Settings.knx_GA_param[XdrvMailbox.index -1] = ga_option; - } else { - if ( (XdrvMailbox.payload <= Settings.knx_GA_registered) && (XdrvMailbox.payload > 0) ) { - XdrvMailbox.index = XdrvMailbox.payload; - } else { - Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); - return; - } - } - if ( XdrvMailbox.index <= Settings.knx_GA_registered ) { - KNX_addr.value = Settings.knx_GA_addr[XdrvMailbox.index -1]; - Response_P (PSTR("{\"%s%d\":\"%s, %d/%d/%d\"}"), - XdrvMailbox.command, XdrvMailbox.index, device_param_ga[Settings.knx_GA_param[XdrvMailbox.index-1]-1], - KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member ); - } - } else { - ResponseCmndNumber (Settings.knx_GA_registered ); - } - } -} - -void CmndKnxCb(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNX_CB)) { - if (XdrvMailbox.data_len) { - if (strstr(XdrvMailbox.data, ",") != nullptr) { - char sub_string[XdrvMailbox.data_len]; - - int cb_option = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1)); - int cb_area = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - int cb_line = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); - int cb_member = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4)); - - if ( ((cb_area == 0) && (cb_line == 0) && (cb_member == 0)) - || (cb_area > 31) || (cb_line > 7) || (cb_member > 255) - || (cb_option < 0) || ((cb_option > KNX_MAX_device_param ) && (cb_option != KNX_Empty)) - || (!device_param[cb_option-1].show) ) { - Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); - return; - } - - KNX_addr.ga.area = cb_area; - KNX_addr.ga.line = cb_line; - KNX_addr.ga.member = cb_member; - - if ( XdrvMailbox.index > Settings.knx_CB_registered ) { - Settings.knx_CB_registered ++; - XdrvMailbox.index = Settings.knx_CB_registered; - } - - Settings.knx_CB_addr[XdrvMailbox.index -1] = KNX_addr.value; - Settings.knx_CB_param[XdrvMailbox.index -1] = cb_option; - } else { - if ( (XdrvMailbox.payload <= Settings.knx_CB_registered) && (XdrvMailbox.payload > 0) ) { - XdrvMailbox.index = XdrvMailbox.payload; - } else { - Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); - return; - } - } - if ( XdrvMailbox.index <= Settings.knx_CB_registered ) { - KNX_addr.value = Settings.knx_CB_addr[XdrvMailbox.index -1]; - Response_P (PSTR("{\"%s%d\":\"%s, %d/%d/%d\"}"), - XdrvMailbox.command, XdrvMailbox.index, device_param_cb[Settings.knx_CB_param[XdrvMailbox.index-1]-1], - KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member ); - } - } else { - ResponseCmndNumber (Settings.knx_CB_registered ); - } - } -} - - - - - -bool Xdrv11(uint8_t function) -{ - bool result = false; - switch (function) { - case FUNC_LOOP: - if (!global_state.wifi_down) { knx.loop(); } - break; - case FUNC_EVERY_50_MSECOND: - if (toggle_inhibit) { - toggle_inhibit--; - } - break; - case FUNC_ANY_KEY: - KnxSendButtonPower(); - break; -#ifdef USE_WEBSERVER -#ifdef USE_KNX_WEB_MENU - case FUNC_WEB_ADD_BUTTON: - WSContentSend_P(HTTP_BTN_MENU_KNX); - break; - case FUNC_WEB_ADD_HANDLER: - Webserver->on("/kn", HandleKNXConfiguration); - break; -#endif -#endif - case FUNC_COMMAND: - result = DecodeCommand(kKnxCommands, KnxCommand); - break; - case FUNC_PRE_INIT: - KNX_INIT(); - break; - - - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_12_home_assistant.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_12_home_assistant.ino" -#ifdef USE_HOME_ASSISTANT - -#define XDRV_12 12 - - -const char kHAssJsonSensorTypes[] PROGMEM = - D_JSON_TEMPERATURE "|" D_JSON_DEWPOINT "|" D_JSON_PRESSURE "|" D_JSON_PRESSUREATSEALEVEL "|" - D_JSON_APPARENT_POWERUSAGE "|Battery|" D_JSON_CURRENT "|" D_JSON_DISTANCE "|" D_JSON_FREQUENCY "|" D_JSON_HUMIDITY "|" D_JSON_ILLUMINANCE "|" - D_JSON_MOISTURE "|PB0.3|PB0.5|PB1|PB2.5|PB5|PB10|PM1|PM2.5|PM10|" D_JSON_POWERFACTOR "|" D_JSON_POWERUSAGE "|" - D_JSON_REACTIVE_POWERUSAGE "|" D_JSON_TODAY "|" D_JSON_TOTAL "|" D_JSON_VOLTAGE "|" D_JSON_WEIGHT "|" D_JSON_YESTERDAY "|" - D_JSON_CO2 "|" D_JSON_ECO2 "|" D_JSON_TVOC "|"; -const char kHAssJsonSensorUnits[] PROGMEM = - "||||" - "VA|%|A|Cm|Hz|%|LX|" - "%|ppd|ppd|ppd|ppd|ppd|ppd|µg/m³|µg/m³|µg/m³|Cos φ|W|" - "VAr|kWh|kWh|V|Kg|kWh|" - "ppm|ppm|ppb|"; -const char kHAssJsonSensorDevCla[] PROGMEM = - "dev_cla\":\"temperature|ic\":\"mdi:weather-rainy|dev_cla\":\"pressure|dev_cla\":\"pressure|" - "dev_cla\":\"power|dev_cla\":\"battery|ic\":\"mdi:alpha-a-circle-outline|ic\":\"mdi:leak|ic\":\"mdi:current-ac|dev_cla\":\"humidity|dev_cla\":\"illuminance|" - "ic\":\"mdi:cup-water|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|" - "ic\":\"mdi:air-filter|ic\":\"mdi:air-filter|ic\":\"mdi:air-filter|ic\":\"mdi:alpha-f-circle-outline|dev_cla\":\"power|" - "dev_cla\":\"power|dev_cla\":\"power|dev_cla\":\"power|ic\":\"mdi:alpha-v-circle-outline|ic\":\"mdi:scale|dev_cla\":\"power|" - "ic\":\"mdi:periodic-table-co2|ic\":\"mdi:air-filter|ic\":\"mdi:periodic-table-co2|"; - - - -const char HASS_DISCOVER_SENSOR[] PROGMEM = - ",\"unit_of_meas\":\"%s\",\"%s\"," - "\"frc_upd\":true," - "\"val_tpl\":\"{{value_json['%s']['%s']"; - -const char HASS_DISCOVER_BASE[] PROGMEM = - "{\"name\":\"%s\"," - "\"stat_t\":\"%s\"," - "\"avty_t\":\"%s\"," - "\"pl_avail\":\"" D_ONLINE "\"," - "\"pl_not_avail\":\"" D_OFFLINE "\""; - -const char HASS_DISCOVER_RELAY[] PROGMEM = - ",\"cmd_t\":\"%s\"," - "\"val_tpl\":\"{{value_json.%s}}\"," - "\"pl_off\":\"%s\"," - "\"pl_on\":\"%s\""; - -const char HASS_DISCOVER_BIN_SWITCH[] PROGMEM = - ",\"val_tpl\":\"{{value_json.%s}}\"," - "\"frc_upd\":true," - "\"pl_on\":\"%s\"," - "\"pl_off\":\"%s\""; - -const char HASS_DISCOVER_BIN_PIR[] PROGMEM = - ",\"val_tpl\":\"{{value_json.%s}}\"," - "\"frc_upd\":true," - "\"pl_on\":\"%s\"," - "\"off_dly\":1"; - -const char HASS_DISCOVER_LIGHT_DIMMER[] PROGMEM = - ",\"bri_cmd_t\":\"%s\"," - "\"bri_stat_t\":\"%s\"," - "\"bri_scl\":100," - "\"on_cmd_type\":\"%s\"," - "\"bri_val_tpl\":\"{{value_json." D_CMND_DIMMER "}}\""; - -const char HASS_DISCOVER_LIGHT_COLOR[] PROGMEM = - ",\"rgb_cmd_t\":\"%s2\"," - "\"rgb_stat_t\":\"%s\"," - "\"rgb_val_tpl\":\"{{value_json." D_CMND_COLOR ".split(',')[0:3]|join(',')}}\""; - -const char HASS_DISCOVER_LIGHT_WHITE[] PROGMEM = - ",\"whit_val_cmd_t\":\"%s\"," - "\"whit_val_stat_t\":\"%s\"," - "\"whit_val_scl\":100," - "\"whit_val_tpl\":\"{{value_json.Channel[3]}}\""; - -const char HASS_DISCOVER_LIGHT_CT[] PROGMEM = - ",\"clr_temp_cmd_t\":\"%s\"," - "\"clr_temp_stat_t\":\"%s\"," - "\"clr_temp_val_tpl\":\"{{value_json." D_CMND_COLORTEMPERATURE "}}\""; - -const char HASS_DISCOVER_LIGHT_SCHEME[] PROGMEM = - ",\"fx_cmd_t\":\"%s\"," - "\"fx_stat_t\":\"%s\"," - "\"fx_val_tpl\":\"{{value_json." D_CMND_SCHEME "}}\"," - "\"fx_list\":[\"0\",\"1\",\"2\",\"3\",\"4\"]"; - -const char HASS_DISCOVER_SENSOR_HASS_STATUS[] PROGMEM = - ",\"json_attr_t\":\"%s\"," - "\"unit_of_meas\":\"%%\"," - "\"val_tpl\":\"{{value_json['" D_JSON_RSSI "']}}\"," - "\"ic\":\"mdi:information-outline\""; - -const char HASS_DISCOVER_DEVICE_INFO[] PROGMEM = - ",\"uniq_id\":\"%s\"," - "\"dev\":{\"ids\":[\"%06X\"]," - "\"name\":\"%s\"," - "\"mdl\":\"%s\"," - "\"sw\":\"%s%s\"," - "\"mf\":\"Tasmota\"}"; - -const char HASS_DISCOVER_DEVICE_INFO_SHORT[] PROGMEM = - ",\"uniq_id\":\"%s\"," - "\"dev\":{\"ids\":[\"%06X\"]}"; - -const char HASS_TRIGGER_TYPE[] PROGMEM = - "{\"atype\":\"trigger\"," - "\"t\":\"%sT\"," - "\"pl\":\"{\\\"TRIG\\\":\\\"%s\\\"}\"," - "\"type\":\"%s\"," - "\"stype\":\"%s\"," - "\"dev\":{\"ids\":[\"%06X\"]}}"; - -const char kHAssTriggerType[] PROGMEM = - "none|button_short_press|button_long_press|button_double_press"; - -uint8_t hass_init_step = 0; -uint8_t hass_mode = 0; -int hass_tele_period = 0; - -void TryResponseAppend_P(const char *format, ...) -{ - va_list args; - va_start(args, format); - char dummy[2]; - int dlen = vsnprintf_P(dummy, 1, format, args); - - int mlen = strlen(mqtt_data); - int slen = sizeof(mqtt_data) - 1 - mlen; - if (dlen >= slen) - { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: MQTT discovery failed due to too long topic or friendly name. " - "Please shorten topic and friendly name. Failed to format(%u/%u):"), - dlen, slen); - va_start(args, format); - vsnprintf_P(log_data, sizeof(log_data), format, args); - AddLog(LOG_LEVEL_ERROR); - } - else - { - va_start(args, format); - vsnprintf_P(mqtt_data + mlen, slen, format, args); - } - va_end(args); -} - -void HAssAnnounceRelayLight(void) -{ - char stopic[TOPSZ]; - char stemp1[TOPSZ]; - char stemp2[TOPSZ]; - char stemp3[TOPSZ]; - char unique_id[30]; - bool is_light = false; - bool is_topic_light = false; - - for (uint32_t i = 1; i <= MAX_RELAYS; i++) - { - is_light = ((i == devices_present) && (light_type)); - is_topic_light = Settings.flag.hass_light || is_light; - - mqtt_data[0] = '\0'; - - - snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%d"), ESP_getChipId(), (is_topic_light) ? "RL" : "LI", i); - snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s/config"), - (is_topic_light) ? "switch" : "light", unique_id); - MqttPublish(stopic, true); - - snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%d"), ESP_getChipId(), (is_topic_light) ? "LI" : "RL", i); - snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s/config"), - (is_topic_light) ? "light" : "switch", unique_id); - - if (Settings.flag.hass_discovery && (i <= devices_present)) - { - char name[33 + 2]; - char value_template[33]; - char prefix[TOPSZ]; - char *command_topic = stemp1; - char *state_topic = stemp2; - char *availability_topic = stemp3; - - if (i > MAX_FRIENDLYNAMES) { - snprintf_P(name, sizeof(name), PSTR("%s %d"), SettingsText(SET_FRIENDLYNAME1), i); - } else { - snprintf_P(name, sizeof(name), SettingsText(SET_FRIENDLYNAME1 + i - 1)); - } - GetPowerDevice(value_template, i, sizeof(value_template), Settings.flag.device_index_enable); - GetTopic_P(command_topic, CMND, mqtt_topic, value_template); - GetTopic_P(state_topic, TELE, mqtt_topic, D_RSLT_STATE); - GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); - - Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); - TryResponseAppend_P(HASS_DISCOVER_RELAY, command_topic, value_template, SettingsText(SET_STATE_TXT1), SettingsText(SET_STATE_TXT2)); - TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId()); - -#ifdef USE_LIGHT - if (is_light -#ifdef ESP8266 - || PWM_DIMMER == my_module_type -#endif - ) - { - char *brightness_command_topic = stemp1; - - GetTopic_P(brightness_command_topic, CMND, mqtt_topic, D_CMND_DIMMER); - strncpy_P(stemp3, Settings.flag.not_power_linked ? PSTR("last") : PSTR("brightness"), sizeof(stemp3)); - TryResponseAppend_P(HASS_DISCOVER_LIGHT_DIMMER, brightness_command_topic, state_topic, stemp3); - - if (Light.subtype >= LST_RGB) - { - char *rgb_command_topic = stemp1; - - GetTopic_P(rgb_command_topic, CMND, mqtt_topic, D_CMND_COLOR); - TryResponseAppend_P(HASS_DISCOVER_LIGHT_COLOR, rgb_command_topic, state_topic); - - char *effect_command_topic = stemp1; - GetTopic_P(effect_command_topic, CMND, mqtt_topic, D_CMND_SCHEME); - TryResponseAppend_P(HASS_DISCOVER_LIGHT_SCHEME, effect_command_topic, state_topic); - } - if (LST_RGBW == Light.subtype) - { - char *white_temp_command_topic = stemp1; - - GetTopic_P(white_temp_command_topic, CMND, mqtt_topic, D_CMND_WHITE); - TryResponseAppend_P(HASS_DISCOVER_LIGHT_WHITE, white_temp_command_topic, state_topic); - } - if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) - { - char *color_temp_command_topic = stemp1; - - GetTopic_P(color_temp_command_topic, CMND, mqtt_topic, D_CMND_COLORTEMPERATURE); - TryResponseAppend_P(HASS_DISCOVER_LIGHT_CT, color_temp_command_topic, state_topic); - } - } -#endif - TryResponseAppend_P(PSTR("}")); - } - MqttPublish(stopic, true); - } -} - -void HAssAnnouncerTriggers(uint8_t device, uint8_t present, uint8_t key, uint8_t toggle, uint8_t hold) -{ - - - char stopic[TOPSZ]; - char stemp1[TOPSZ]; - char stemp2[TOPSZ]; - char unique_id[30]; - - mqtt_data[0] = '\0'; - - for (uint8_t i = 2; i <= 3; i++) { - snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%d_%s"), ESP_getChipId(), key ? "SW" : "BTN", device + 1, GetStateText(i)); - snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/device_automation/%s/config"), unique_id); - - if (Settings.flag.hass_discovery && present) { - char name[33 + 6]; - char value_template[33]; - char prefix[TOPSZ]; - char *state_topic = stemp1; - char *availability_topic = stemp2; - char jsoname[8]; - - GetPowerDevice(value_template, device + 1, sizeof(value_template), key + Settings.flag.device_index_enable); - snprintf_P(jsoname, sizeof(jsoname), PSTR("%s%d"), key ? "SWITCH" : "BUTTON", device + 1); - GetTopic_P(state_topic, STAT, mqtt_topic, jsoname); - GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); - - char param[21]; - char subtype[9]; - uint8_t pload = toggle; - - if ((i == 2 && toggle != 0) || (i == 3 && hold != 0)) { - if (i == 3) { pload = hold; } - GetTextIndexed(param, sizeof(param), pload, kHAssTriggerType); - snprintf_P(subtype, sizeof(subtype), PSTR("%s_%d"), key ? "switch" : "button", device + 1); - Response_P(HASS_TRIGGER_TYPE, state_topic, GetStateText(i), param, subtype, ESP_getChipId()); - } else { mqtt_data[0] = '\0'; } - } - MqttPublish(stopic, true); - } -} - -void HAssAnnouncerBinSensors(uint8_t device, uint8_t present, uint8_t dual, uint8_t toggle, uint8_t pir) -{ - char stopic[TOPSZ]; - char stemp1[TOPSZ]; - char stemp2[TOPSZ]; - char unique_id[30]; - - mqtt_data[0] = '\0'; - - snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_SW_%d"), ESP_getChipId(), device + 1); - snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/binary_sensor/%s/config"), unique_id); - - - if (Settings.flag.hass_discovery && present ) { - if (!toggle || dual) { - char name[33 + 6]; - char value_template[33]; - char prefix[TOPSZ]; - char *state_topic = stemp1; - char *availability_topic = stemp2; - char jsoname[8]; - - GetPowerDevice(value_template, device + 1, sizeof(value_template), 1 + Settings.flag.device_index_enable); - snprintf_P(jsoname, sizeof(jsoname), PSTR("SWITCH%d"), device + 1); - GetTopic_P(state_topic, STAT, mqtt_topic, jsoname); - GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); - - snprintf_P(name, sizeof(name), PSTR("%s Switch%d"), SettingsText(SET_FRIENDLYNAME1), device + 1); - Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); - if (!pir) { - TryResponseAppend_P(HASS_DISCOVER_BIN_SWITCH, PSTR(D_RSLT_STATE), SettingsText(SET_STATE_TXT2), SettingsText(SET_STATE_TXT1)); - } else { - TryResponseAppend_P(HASS_DISCOVER_BIN_PIR, PSTR(D_RSLT_STATE), SettingsText(SET_STATE_TXT2)); - } - TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId()); - TryResponseAppend_P(PSTR("}")); - } - } - MqttPublish(stopic, true); -} - -void HAssAnnounceSwitches(void) -{ - for (uint32_t switch_index = 0; switch_index < MAX_SWITCHES; switch_index++) - { - uint8_t switch_present = 0; - uint8_t dual = 0; - uint8_t toggle = 1; - uint8_t hold = 0; - uint8_t pir = 0; - - if (pin[GPIO_SWT1 + switch_index] < 99) { switch_present = 1; } - - if (KeyTopicActive(1) && strcmp(SettingsText(SET_MQTT_SWITCH_TOPIC), mqtt_topic)) - { -# 383 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_12_home_assistant.ino" - uint8_t swmode = Settings.switchmode[switch_index]; - - switch (swmode) { - case FOLLOW: - case FOLLOW_INV: - toggle = 0; - break; - case PUSHBUTTON: - case PUSHBUTTON_INV: - dual = 1; - break; - case PUSHBUTTONHOLD: - case PUSHBUTTONHOLD_INV: - dual = 1; - hold = 2; - break; - case TOGGLEMULTI: - hold = 3; - break; - case FOLLOWMULTI: - case FOLLOWMULTI_INV: - dual = 1; - toggle = 0; - hold = 3; - break; - case PUSHON: - case PUSHON_INV: - toggle = 0; - pir = 1; - } - - } else { switch_present = 0;} - - HAssAnnouncerTriggers(switch_index, switch_present, 1, toggle, hold); - HAssAnnouncerBinSensors(switch_index, switch_present, dual, toggle, pir); - } -} - - -void HAssAnnounceButtons(void) -{ - for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) - { - uint8_t button_present = 0; - uint8_t toggle = 1; - uint8_t hold = 0; - -#ifdef ESP8266 - if (!button_index && ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type))) - { - button_present = 1; - } else -#endif - { - if (pin[GPIO_KEY1 + button_index] < 99) { - button_present = 1; - } - } -# 455 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_12_home_assistant.ino" - if (Settings.flag.button_restrict) { - if (!Settings.flag.button_single) { - hold = 2; - } - } - - if (Settings.flag.button_swap) { - if (!Settings.flag.button_single) { - if (!Settings.flag.button_restrict) { - hold = 0; - } - toggle = 3; - } else {toggle = 0; hold = 0;} - } - - if (KeyTopicActive(0)) { - - if (!strcmp(SettingsText(SET_MQTT_BUTTON_TOPIC), mqtt_topic)) { - toggle = 0; - } - - } else { button_present = 0; } - - HAssAnnouncerTriggers(button_index, button_present, 0, toggle, hold); - } -} - -void HAssAnnounceSensor(const char *sensorname, const char *subsensortype, const char *MultiSubName, uint8_t subqty, uint8_t subidx, uint8_t nested, const char* SubKey) -{ - char stopic[TOPSZ]; - char stemp1[TOPSZ]; - char stemp2[TOPSZ]; - char unique_id[30]; - char subname[20]; - - mqtt_data[0] = '\0'; - - - NoAlNumToUnderscore(subname, MultiSubName); - snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%s"), ESP_getChipId(), sensorname, subname); - snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id); - - if (Settings.flag.hass_discovery) - { - char name[33 + 42]; - char prefix[TOPSZ]; - char *state_topic = stemp1; - char *availability_topic = stemp2; - - GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_SENSOR)); - snprintf_P(name, sizeof(name), PSTR("%s %s %s"), SettingsText(SET_FRIENDLYNAME1), sensorname, MultiSubName); - GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); - - Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); - TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId()); - - - char jname[32]; - int sensor_index = GetCommandCode(jname, sizeof(jname), SubKey, kHAssJsonSensorTypes); - if (sensor_index > -1) { - - char param1[20]; - GetTextIndexed(param1, sizeof(param1), sensor_index, kHAssJsonSensorUnits); - switch (sensor_index) { - case 0: - case 1: - snprintf_P(param1, sizeof(param1), PSTR("°%c"),TempUnit()); - break; - case 2: - case 3: - snprintf_P(param1, sizeof(param1), PSTR("%s"), PressureUnit().c_str()); - break; -# 537 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_12_home_assistant.ino" - } - char param2[50]; - GetTextIndexed(param2, sizeof(param2), sensor_index, kHAssJsonSensorDevCla); - TryResponseAppend_P(HASS_DISCOVER_SENSOR, param1, param2, sensorname, subsensortype); - - if (subidx) { - TryResponseAppend_P(PSTR("[%d]"), subqty -1); - } - } else { - TryResponseAppend_P(HASS_DISCOVER_SENSOR, " ", "ic\":\"mdi:eye", sensorname, subsensortype); - } - if (nested) { - TryResponseAppend_P(PSTR("['%s']"), SubKey); - } - TryResponseAppend_P(PSTR("}}\"}")); - } - MqttPublish(stopic, true); -} - -void HAssAnnounceSensors(void) -{ - uint8_t hass_xsns_index = 0; - do - { - mqtt_data[0] = '\0'; - int tele_period_save = tele_period; - tele_period = 2; - XsnsNextCall(FUNC_JSON_APPEND, hass_xsns_index); - tele_period = tele_period_save; - - char sensordata[512]; - strlcpy(sensordata, mqtt_data, sizeof(sensordata)); - - if (strlen(sensordata)) - { - sensordata[0] = '{'; - snprintf_P(sensordata, sizeof(sensordata), PSTR("%s}"), sensordata); - - - - - StaticJsonBuffer<500> jsonBuffer; - JsonObject &root = jsonBuffer.parseObject(sensordata); - if (!root.success()) - { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: jsonBuffer failed to parse '%s'"), sensordata); - continue; - } - for (auto sensor : root) - { - const char *sensorname = sensor.key; - JsonObject &sensors = sensor.value.as(); - if (!sensors.success()) - { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: JsonObject failed to parse '%s'"), sensordata); - continue; - } - - for (auto subsensor : sensors) - { - if (subsensor.value.is()) { - - char NestedName[20]; - char NewSensorName[20]; - snprintf_P(NestedName, sizeof(NestedName), PSTR("%s"), subsensor.key); - JsonObject& subsensors = subsensor.value.as(); - for (auto subsensor : subsensors) { - snprintf_P(NewSensorName, sizeof(NewSensorName), PSTR("%s %s"), NestedName, subsensor.key); - HAssAnnounceSensor(sensorname, NestedName, NewSensorName, 0, 0, 1, subsensor.key); - } - } else if (subsensor.value.is()) { - - JsonArray& subsensors = subsensor.value.as(); - uint8_t subqty = subsensors.size(); - char MultiSubName[20]; - for (int i = 1; i <= subqty; i++) { - snprintf_P(MultiSubName, sizeof(MultiSubName), PSTR("%s %d"), subsensor.key, i); - HAssAnnounceSensor(sensorname, subsensor.key, MultiSubName, i, 1, 0, subsensor.key); - } - } else { HAssAnnounceSensor(sensorname, subsensor.key, subsensor.key, 0, 0, 0, subsensor.key);} - } - } - } - yield(); - } while (hass_xsns_index != 0); -} - -void HAssAnnounceStatusSensor(void) -{ - char stopic[TOPSZ]; - char stemp1[TOPSZ]; - char stemp2[TOPSZ]; - char unique_id[30]; - - - mqtt_data[0] = '\0'; - - - snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_status"), ESP_getChipId()); - snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id); - - if (Settings.flag.hass_discovery) - { - char name[33 + 7]; - char prefix[TOPSZ]; - char *state_topic = stemp1; - char *availability_topic = stemp2; - - snprintf_P(name, sizeof(name), PSTR("%s status"), SettingsText(SET_FRIENDLYNAME1)); - GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_HASS_STATE)); - GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); - - Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); - TryResponseAppend_P(HASS_DISCOVER_SENSOR_HASS_STATUS, state_topic); - TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO, unique_id, ESP_getChipId(), SettingsText(SET_FRIENDLYNAME1), - ModuleName().c_str(), my_version, my_image); - TryResponseAppend_P(PSTR("}")); - } - MqttPublish(stopic, true); -} - -void HAssPublishStatus(void) -{ - Response_P(PSTR("{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\"," - "\"" D_JSON_COREVERSION "\":\"" ARDUINO_CORE_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\"," - "\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\"," - "\"WiFi " D_JSON_LINK_COUNT "\":%d,\"WiFi " D_JSON_DOWNTIME "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d," - "\"" D_JSON_BOOTCOUNT "\":%d,\"" D_JSON_SAVECOUNT "\":%d,\"" D_CMND_IPADDRESS "\":\"%s\"," - "\"" D_JSON_RSSI "\":\"%d\",\"LoadAvg\":%lu}"), - my_version, my_image, GetBuildDateAndTime().c_str(), ESP.getSdkVersion(), ModuleName().c_str(), - GetResetReason().c_str(), GetUptime().c_str(), WifiLinkCount(), WifiDowntime().c_str(), MqttConnectCount(), - Settings.bootcount, Settings.save_flag, WiFi.localIP().toString().c_str(), - WifiGetRssiAsQuality(WiFi.RSSI()), loop_load_avg); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_HASS_STATE)); -} - -void HAssDiscovery(void) -{ - - if (Settings.flag.hass_discovery) - { - Settings.flag.mqtt_response = 0; - Settings.flag.decimal_text = 1; - Settings.flag3.hass_tele_on_power = 1; - - - Settings.light_scheme = 0; - } - - if (Settings.flag.hass_discovery || (1 == hass_mode)) - { - - HAssAnnounceRelayLight(); - - - HAssAnnounceButtons(); - - - HAssAnnounceSwitches(); - - - HAssAnnounceSensors(); - - - HAssAnnounceStatusSensor(); - } -} - -void HAssDiscover(void) -{ - hass_mode = 1; - hass_init_step = 1; -} - -void HAssAnyKey(void) -{ - if (!Settings.flag.hass_discovery) - { - return; - } - - uint32_t key = (XdrvMailbox.payload >> 16) & 0xFF; - uint32_t device = XdrvMailbox.payload & 0xFF; - uint32_t state = (XdrvMailbox.payload >> 8) & 0xFF; - - if (!key && KeyTopicActive(0)) { - device = (XdrvMailbox.payload >> 24) & 0xFF; - } - - char scommand[CMDSZ]; - char sw_topic[TOPSZ]; - char key_topic[TOPSZ]; - char *tmpbtn = SettingsText(SET_MQTT_BUTTON_TOPIC); - char *tmpsw = SettingsText(SET_MQTT_SWITCH_TOPIC); - uint8_t evkey = 0; - Format(sw_topic, tmpsw, sizeof(sw_topic)); - Format(key_topic, tmpbtn, sizeof(key_topic)); - - if (state == 2 || state == 3 ) { evkey = 1;} - snprintf_P(scommand, sizeof(scommand), PSTR("%s%d%s"), (key) ? "SWITCH" : "BUTTON", device, (evkey) ? "T" : ""); - - char stopic[TOPSZ]; - - GetTopic_P(stopic, STAT, mqtt_topic, scommand); - Response_P(S_JSON_COMMAND_SVALUE, (evkey) ? "TRIG" : PSTR(D_RSLT_STATE), GetStateText(state)); - MqttPublish(stopic); -} - - - - - -bool Xdrv12(uint8_t function) -{ - bool result = false; - - if (Settings.flag.mqtt_enabled) - { - switch (function) - { - case FUNC_EVERY_SECOND: - if (hass_init_step) - { - hass_init_step--; - if (!hass_init_step) - { - HAssDiscovery(); - } - } - else if (Settings.flag.hass_discovery && Settings.tele_period) - { - hass_tele_period++; - if (hass_tele_period >= Settings.tele_period) - { - hass_tele_period = 0; - - mqtt_data[0] = '\0'; - HAssPublishStatus(); - } - } - break; - case FUNC_ANY_KEY: - HAssAnyKey(); - break; - case FUNC_MQTT_INIT: - hass_mode = 0; - hass_init_step = 2; - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_13_display.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_13_display.ino" -#if defined(USE_I2C) || defined(USE_SPI) -#ifdef USE_DISPLAY - -#define XDRV_13 13 - -#include -#include - -Renderer *renderer; - -enum ColorType { COLOR_BW, COLOR_COLOR }; - -#ifndef MAXBUTTONS -#define MAXBUTTONS 16 -#endif - -#ifdef USE_TOUCH_BUTTONS -VButton *buttons[MAXBUTTONS]; -#endif - - - -uint16_t fg_color = 1; -uint16_t bg_color = 0; -uint8_t color_type = COLOR_BW; -uint8_t auto_draw=1; - -const uint8_t DISPLAY_MAX_DRIVERS = 16; -const uint8_t DISPLAY_MAX_COLS = 44; -const uint8_t DISPLAY_MAX_ROWS = 32; - -const uint8_t DISPLAY_LOG_ROWS = 32; - -#define D_PRFX_DISPLAY "Display" -#define D_CMND_DISP_ADDRESS "Address" -#define D_CMND_DISP_COLS "Cols" -#define D_CMND_DISP_DIMMER "Dimmer" -#define D_CMND_DISP_MODE "Mode" -#define D_CMND_DISP_MODEL "Model" -#define D_CMND_DISP_REFRESH "Refresh" -#define D_CMND_DISP_ROWS "Rows" -#define D_CMND_DISP_SIZE "Size" -#define D_CMND_DISP_FONT "Font" -#define D_CMND_DISP_ROTATE "Rotate" -#define D_CMND_DISP_TEXT "Text" -#define D_CMND_DISP_WIDTH "Width" -#define D_CMND_DISP_HEIGHT "Height" - -enum XdspFunctions { FUNC_DISPLAY_INIT_DRIVER, FUNC_DISPLAY_INIT, FUNC_DISPLAY_EVERY_50_MSECOND, FUNC_DISPLAY_EVERY_SECOND, - FUNC_DISPLAY_MODEL, FUNC_DISPLAY_MODE, FUNC_DISPLAY_POWER, - FUNC_DISPLAY_CLEAR, FUNC_DISPLAY_DRAW_FRAME, - FUNC_DISPLAY_DRAW_HLINE, FUNC_DISPLAY_DRAW_VLINE, FUNC_DISPLAY_DRAW_LINE, - FUNC_DISPLAY_DRAW_CIRCLE, FUNC_DISPLAY_FILL_CIRCLE, - FUNC_DISPLAY_DRAW_RECTANGLE, FUNC_DISPLAY_FILL_RECTANGLE, - FUNC_DISPLAY_TEXT_SIZE, FUNC_DISPLAY_FONT_SIZE, FUNC_DISPLAY_ROTATION, FUNC_DISPLAY_DRAW_STRING, FUNC_DISPLAY_ONOFF }; - -enum DisplayInitModes { DISPLAY_INIT_MODE, DISPLAY_INIT_PARTIAL, DISPLAY_INIT_FULL }; - -const char kDisplayCommands[] PROGMEM = D_PRFX_DISPLAY "|" - "|" D_CMND_DISP_MODEL "|" D_CMND_DISP_WIDTH "|" D_CMND_DISP_HEIGHT "|" D_CMND_DISP_MODE "|" D_CMND_DISP_REFRESH "|" - D_CMND_DISP_DIMMER "|" D_CMND_DISP_COLS "|" D_CMND_DISP_ROWS "|" D_CMND_DISP_SIZE "|" D_CMND_DISP_FONT "|" - D_CMND_DISP_ROTATE "|" D_CMND_DISP_TEXT "|" D_CMND_DISP_ADDRESS ; - -void (* const DisplayCommand[])(void) PROGMEM = { - &CmndDisplay, &CmndDisplayModel, &CmndDisplayWidth, &CmndDisplayHeight, &CmndDisplayMode, &CmndDisplayRefresh, - &CmndDisplayDimmer, &CmndDisplayColumns, &CmndDisplayRows, &CmndDisplaySize, &CmndDisplayFont, - &CmndDisplayRotate, &CmndDisplayText, &CmndDisplayAddress }; - -char *dsp_str; - -uint16_t dsp_x; -uint16_t dsp_y; -uint16_t dsp_x2; -uint16_t dsp_y2; -uint16_t dsp_rad; -uint16_t dsp_color; -int16_t dsp_len; -int16_t disp_xpos = 0; -int16_t disp_ypos = 0; - -uint8_t disp_power = 0; -uint8_t disp_device = 0; -uint8_t disp_refresh = 1; -uint8_t disp_autodraw = 1; -uint8_t dsp_init; -uint8_t dsp_font; -uint8_t dsp_flag; -uint8_t dsp_on; - -#ifdef USE_DISPLAY_MODES1TO5 - -char **disp_log_buffer; -char **disp_screen_buffer; -char disp_temp[2]; -char disp_pres[5]; - -uint8_t disp_log_buffer_cols = 0; -uint8_t disp_log_buffer_idx = 0; -uint8_t disp_log_buffer_ptr = 0; -uint8_t disp_screen_buffer_cols = 0; -uint8_t disp_screen_buffer_rows = 0; -bool disp_subscribed = false; - -#endif - - - -void DisplayInit(uint8_t mode) -{ - if (renderer) { - renderer->DisplayInit(mode, Settings.display_size, Settings.display_rotate, Settings.display_font); - } - else { - dsp_init = mode; - XdspCall(FUNC_DISPLAY_INIT); - } -} - -void DisplayClear(void) -{ - XdspCall(FUNC_DISPLAY_CLEAR); -} - -void DisplayDrawHLine(uint16_t x, uint16_t y, int16_t len, uint16_t color) -{ - dsp_x = x; - dsp_y = y; - dsp_len = len; - dsp_color = color; - XdspCall(FUNC_DISPLAY_DRAW_HLINE); -} - -void DisplayDrawVLine(uint16_t x, uint16_t y, int16_t len, uint16_t color) -{ - dsp_x = x; - dsp_y = y; - dsp_len = len; - dsp_color = color; - XdspCall(FUNC_DISPLAY_DRAW_VLINE); -} - -void DisplayDrawLine(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color) -{ - dsp_x = x; - dsp_y = y; - dsp_x2 = x2; - dsp_y2 = y2; - dsp_color = color; - XdspCall(FUNC_DISPLAY_DRAW_LINE); -} - -void DisplayDrawCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color) -{ - dsp_x = x; - dsp_y = y; - dsp_rad = rad; - dsp_color = color; - XdspCall(FUNC_DISPLAY_DRAW_CIRCLE); -} - -void DisplayDrawFilledCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color) -{ - dsp_x = x; - dsp_y = y; - dsp_rad = rad; - dsp_color = color; - XdspCall(FUNC_DISPLAY_FILL_CIRCLE); -} - -void DisplayDrawRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color) -{ - dsp_x = x; - dsp_y = y; - dsp_x2 = x2; - dsp_y2 = y2; - dsp_color = color; - XdspCall(FUNC_DISPLAY_DRAW_RECTANGLE); -} - -void DisplayDrawFilledRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color) -{ - dsp_x = x; - dsp_y = y; - dsp_x2 = x2; - dsp_y2 = y2; - dsp_color = color; - XdspCall(FUNC_DISPLAY_FILL_RECTANGLE); -} - -void DisplayDrawFrame(void) -{ - XdspCall(FUNC_DISPLAY_DRAW_FRAME); -} - -void DisplaySetSize(uint8_t size) -{ - Settings.display_size = size &3; - XdspCall(FUNC_DISPLAY_TEXT_SIZE); -} - -void DisplaySetFont(uint8_t font) -{ - Settings.display_font = font &3; - XdspCall(FUNC_DISPLAY_FONT_SIZE); -} - -void DisplaySetRotation(uint8_t rotation) -{ - Settings.display_rotate = rotation &3; - XdspCall(FUNC_DISPLAY_ROTATION); -} - -void DisplayDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) -{ - dsp_x = x; - dsp_y = y; - dsp_str = str; - dsp_color = color; - dsp_flag = flag; - XdspCall(FUNC_DISPLAY_DRAW_STRING); -} - -void DisplayOnOff(uint8_t on) -{ - dsp_on = on; - XdspCall(FUNC_DISPLAY_ONOFF); -} - - - - -uint8_t fatoiv(char *cp,float *res) { - uint8_t index=0; - *res=CharToFloat(cp); - while (*cp) { - if ((*cp>='0' && *cp<='9') || (*cp=='-') || (*cp=='.')) { - cp++; - index++; - } else { - break; - } - } - return index; -} - - -uint8_t atoiv(char *cp, int16_t *res) -{ - uint8_t index = 0; - *res = atoi(cp); - while (*cp) { - if ((*cp>='0' && *cp<='9') || (*cp=='-')) { - cp++; - index++; - } else { - break; - } - } - return index; -} - - -uint8_t atoiV(char *cp, uint16_t *res) -{ - uint8_t index = 0; - *res = atoi(cp); - while (*cp) { - if (*cp>='0' && *cp<='9') { - cp++; - index++; - } else { - break; - } - } - return index; -} - - -void alignright(char *string) { - uint16_t slen=strlen(string); - uint16_t len=slen; - while (len) { - - if (string[len-1]!=' ') { - break; - } - len--; - } - uint16_t diff=slen-len; - if (diff>0) { - - memmove(&string[diff],string,len); - memset(string,' ',diff); - } -} - -char *get_string(char *buff,uint8_t len,char *cp) { -uint8_t index=0; - while (*cp!=':') { - buff[index]=*cp++; - index++; - if (index>=len) break; - } - buff[index]=0; - cp++; - return cp; -} - -#define ESCAPE_CHAR '~' - - -uint32_t decode_te(char *line) { - uint32_t skip = 0; - char sbuf[3],*cp; - while (*line) { - if (*line==ESCAPE_CHAR) { - cp=line+1; - if (*cp!=0 && *cp==ESCAPE_CHAR) { - - memmove(cp,cp+1,strlen(cp)); - skip++; - } else { - - if (strlen(cp)<2) { - - return skip; - } - - sbuf[0]=*(cp); - sbuf[1]=*(cp+1); - sbuf[2]=0; - *line=strtol(sbuf,0,16); - - memmove(cp,cp+2,strlen(cp)-1); - skip += 2; - } - } - line++; - } - return skip; -} - - - -#define DISPLAY_BUFFER_COLS 128 - -void DisplayText(void) -{ - uint8_t lpos; - uint8_t escape = 0; - uint8_t var; - int16_t lin = 0; - int16_t col = 0; - int16_t fill = 0; - int16_t temp; - int16_t temp1; - float ftemp; - - char linebuf[DISPLAY_BUFFER_COLS]; - char *dp = linebuf; - char *cp = XdrvMailbox.data; - - memset(linebuf, ' ', sizeof(linebuf)); - linebuf[sizeof(linebuf)-1] = 0; - *dp = 0; - - while (*cp) { - if (!escape) { - - if (*cp == '[') { - escape = 1; - cp++; - - if ((uint32_t)dp - (uint32_t)linebuf) { - if (!fill) { *dp = 0; } - if (col > 0 && lin > 0) { - - if (!renderer) DisplayDrawStringAt(col, lin, linebuf, fg_color, 1); - else renderer->DrawStringAt(col, lin, linebuf, fg_color, 1); - } else { - - if (!renderer) DisplayDrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); - else renderer->DrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); - } - memset(linebuf, ' ', sizeof(linebuf)); - linebuf[sizeof(linebuf)-1] = 0; - dp = linebuf; - } - } else { - - if (dp < (linebuf + DISPLAY_BUFFER_COLS)) { *dp++ = *cp++; } - } - } else { - - if (*cp == ']') { - escape = 0; - cp++; - } else { - - switch (*cp++) { - case 'z': - - if (!renderer) DisplayClear(); - else renderer->fillScreen(bg_color); - disp_xpos = 0; - disp_ypos = 0; - col = 0; - lin = 0; - break; - case 'i': - - DisplayInit(DISPLAY_INIT_PARTIAL); - break; - case 'I': - - DisplayInit(DISPLAY_INIT_FULL); - break; - case 'o': - if (!renderer) { - DisplayOnOff(0); - } else { - renderer->DisplayOnff(0); - } - break; - case 'O': - if (!renderer) { - DisplayOnOff(1); - } else { - renderer->DisplayOnff(1); - } - break; - case 'x': - - var = atoiv(cp, &disp_xpos); - cp += var; - break; - case 'y': - - var = atoiv(cp, &disp_ypos); - cp += var; - break; - case 'l': - - var = atoiv(cp, &lin); - cp += var; - - break; - case 'c': - - var = atoiv(cp, &col); - cp += var; - - break; - case 'C': - - if (*cp=='i') { - - cp++; - var = atoiv(cp, &temp); - if (renderer) ftemp=renderer->GetColorFromIndex(temp); - } else { - - var = fatoiv(cp,&ftemp); - } - fg_color=ftemp; - cp += var; - if (renderer) renderer->setTextColor(fg_color,bg_color); - break; - case 'B': - - if (*cp=='i') { - - cp++; - var = atoiv(cp, &temp); - if (renderer) ftemp=renderer->GetColorFromIndex(temp); - } else { - var = fatoiv(cp,&ftemp); - } - bg_color=ftemp; - cp += var; - if (renderer) renderer->setTextColor(fg_color,bg_color); - break; - case 'p': - - var = atoiv(cp, &fill); - cp += var; - linebuf[fill] = 0; - break; -#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) - case 'P': - { char *ep=strchr(cp,':'); - if (ep) { - *ep=0; - ep++; - Draw_RGB_Bitmap(cp,disp_xpos,disp_ypos); - cp=ep; - } - } - break; -#endif - case 'h': - - var = atoiv(cp, &temp); - cp += var; - if (temp < 0) { - if (renderer) renderer->writeFastHLine(disp_xpos + temp, disp_ypos, -temp, fg_color); - else DisplayDrawHLine(disp_xpos + temp, disp_ypos, -temp, fg_color); - } else { - if (renderer) renderer->writeFastHLine(disp_xpos, disp_ypos, temp, fg_color); - else DisplayDrawHLine(disp_xpos, disp_ypos, temp, fg_color); - } - disp_xpos += temp; - break; - case 'v': - - var = atoiv(cp, &temp); - cp += var; - if (temp < 0) { - if (renderer) renderer->writeFastVLine(disp_xpos, disp_ypos + temp, -temp, fg_color); - else DisplayDrawVLine(disp_xpos, disp_ypos + temp, -temp, fg_color); - } else { - if (renderer) renderer->writeFastVLine(disp_xpos, disp_ypos, temp, fg_color); - else DisplayDrawVLine(disp_xpos, disp_ypos, temp, fg_color); - } - disp_ypos += temp; - break; - case 'L': - - var = atoiv(cp, &temp); - cp += var; - cp++; - var = atoiv(cp, &temp1); - cp += var; - if (renderer) renderer->writeLine(disp_xpos, disp_ypos, temp, temp1, fg_color); - else DisplayDrawLine(disp_xpos, disp_ypos, temp, temp1, fg_color); - disp_xpos += temp; - disp_ypos += temp1; - break; - case 'k': - - var = atoiv(cp, &temp); - cp += var; - if (renderer) renderer->drawCircle(disp_xpos, disp_ypos, temp, fg_color); - else DisplayDrawCircle(disp_xpos, disp_ypos, temp, fg_color); - break; - case 'K': - - var = atoiv(cp, &temp); - cp += var; - if (renderer) renderer->fillCircle(disp_xpos, disp_ypos, temp, fg_color); - else DisplayDrawFilledCircle(disp_xpos, disp_ypos, temp, fg_color); - break; - case 'r': - - var = atoiv(cp, &temp); - cp += var; - cp++; - var = atoiv(cp, &temp1); - cp += var; - if (renderer) renderer->drawRect(disp_xpos, disp_ypos, temp, temp1, fg_color); - else DisplayDrawRectangle(disp_xpos, disp_ypos, temp, temp1, fg_color); - break; - case 'R': - - var = atoiv(cp, &temp); - cp += var; - cp++; - var = atoiv(cp, &temp1); - cp += var; - if (renderer) renderer->fillRect(disp_xpos, disp_ypos, temp, temp1, fg_color); - else DisplayDrawFilledRectangle(disp_xpos, disp_ypos, temp, temp1, fg_color); - break; - case 'u': - - { int16_t rad; - var = atoiv(cp, &temp); - cp += var; - cp++; - var = atoiv(cp, &temp1); - cp += var; - cp++; - var = atoiv(cp, &rad); - cp += var; - if (renderer) renderer->drawRoundRect(disp_xpos, disp_ypos, temp, temp1, rad, fg_color); - - } - break; - case 'U': - - { int16_t rad; - var = atoiv(cp, &temp); - cp += var; - cp++; - var = atoiv(cp, &temp1); - cp += var; - cp++; - var = atoiv(cp, &rad); - cp += var; - if (renderer) renderer->fillRoundRect(disp_xpos, disp_ypos, temp, temp1, rad, fg_color); - - } - break; - - case 't': - if (*cp=='S') { - cp++; - if (dp < (linebuf + DISPLAY_BUFFER_COLS) -8) { - snprintf_P(dp, 9, PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); - dp += 8; - } - } else { - if (dp < (linebuf + DISPLAY_BUFFER_COLS) -5) { - snprintf_P(dp, 6, PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute); - dp += 5; - } - } - break; - case 'T': - if (dp < (linebuf + DISPLAY_BUFFER_COLS) -8) { - snprintf_P(dp, 9, PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%02d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year%2000); - dp += 8; - } - break; - case 'd': - - if (renderer) renderer->Updateframe(); - else DisplayDrawFrame(); - break; - case 'D': - - auto_draw=*cp&3; - if (renderer) renderer->setDrawMode(auto_draw>>1); - cp += 1; - break; - case 's': - - if (renderer) renderer->setTextSize(*cp&7); - else DisplaySetSize(*cp&3); - cp += 1; - break; - case 'f': - - if (renderer) renderer->setTextFont(*cp&7); - else DisplaySetFont(*cp&7); - cp += 1; - break; - case 'a': - - if (renderer) renderer->setRotation(*cp&3); - else DisplaySetRotation(*cp&3); - cp+=1; - break; - -#ifdef USE_GRAPH - case 'G': - - if (*cp=='d') { - cp++; - var=atoiv(cp,&temp); - cp+=var; - cp++; - var=atoiv(cp,&temp1); - cp+=var; - RedrawGraph(temp,temp1); - break; - } -#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) - if (*cp=='s') { - cp++; - var=atoiv(cp,&temp); - cp+=var; - cp++; - - char bbuff[128]; - cp=get_string(bbuff,sizeof(bbuff),cp); - Save_graph(temp,bbuff); - break; - } - if (*cp=='r') { - cp++; - var=atoiv(cp,&temp); - cp+=var; - cp++; - - char bbuff[128]; - cp=get_string(bbuff,sizeof(bbuff),cp); - Restore_graph(temp,bbuff); - break; - } -#endif - { int16_t num,gxp,gyp,gxs,gys,dec,icol; - float ymin,ymax; - var=atoiv(cp,&num); - cp+=var; - cp++; - var=atoiv(cp,&gxp); - cp+=var; - cp++; - var=atoiv(cp,&gyp); - cp+=var; - cp++; - var=atoiv(cp,&gxs); - cp+=var; - cp++; - var=atoiv(cp,&gys); - cp+=var; - cp++; - var=atoiv(cp,&dec); - cp+=var; - cp++; - var=fatoiv(cp,&ymin); - cp+=var; - cp++; - var=fatoiv(cp,&ymax); - cp+=var; - if (color_type==COLOR_COLOR) { - - cp++; - var=atoiv(cp,&icol); - cp+=var; - } else { - icol=0; - } - DefineGraph(num,gxp,gyp,gxs,gys,dec,ymin,ymax,icol); - } - break; - case 'g': - { float temp; - int16_t num; - var=atoiv(cp,&num); - cp+=var; - cp++; - var=fatoiv(cp,&temp); - cp+=var; - AddValue(num,temp); - } - break; -#endif - -#ifdef USE_AWATCH - case 'w': - var = atoiv(cp, &temp); - cp += var; - DrawAClock(temp); - break; -#endif - -#ifdef USE_TOUCH_BUTTONS - case 'b': - { int16_t num,gxp,gyp,gxs,gys,outline,fill,textcolor,textsize; - var=atoiv(cp,&num); - cp+=var; - cp++; - uint8_t bflags=num>>8; - num=num%MAXBUTTONS; - var=atoiv(cp,&gxp); - cp+=var; - cp++; - var=atoiv(cp,&gyp); - cp+=var; - cp++; - var=atoiv(cp,&gxs); - cp+=var; - cp++; - var=atoiv(cp,&gys); - cp+=var; - cp++; - var=atoiv(cp,&outline); - cp+=var; - cp++; - var=atoiv(cp,&fill); - cp+=var; - cp++; - var=atoiv(cp,&textcolor); - cp+=var; - cp++; - var=atoiv(cp,&textsize); - cp+=var; - cp++; - - char bbuff[32]; - cp=get_string(bbuff,sizeof(bbuff),cp); - - if (buttons[num]) { - delete buttons[num]; - } - if (renderer) { - buttons[num]= new VButton(); - if (buttons[num]) { - buttons[num]->vpower=bflags; - buttons[num]->initButtonUL(renderer,gxp,gyp,gxs,gys,renderer->GetColorFromIndex(outline),\ - renderer->GetColorFromIndex(fill),renderer->GetColorFromIndex(textcolor),bbuff,textsize); - if (!bflags) { - - buttons[num]->xdrawButton(bitRead(power,num)); - } else { - - buttons[num]->vpower&=0x7f; - buttons[num]->xdrawButton(buttons[num]->vpower&0x80); - } - } - } - } - break; -#endif - default: - - Response_P(PSTR("Unknown Escape")); - goto exit; - break; - } - } - } - } - exit: - - dp -= decode_te(linebuf); - if ((uint32_t)dp - (uint32_t)linebuf) { - if (!fill) { - *dp = 0; - } else { - linebuf[abs(int(fill))] = 0; - } - if (fill<0) { - - alignright(linebuf); - } - if (col > 0 && lin > 0) { - - if (!renderer) DisplayDrawStringAt(col, lin, linebuf, fg_color, 1); - else renderer->DrawStringAt(col, lin, linebuf, fg_color, 1); - } else { - - if (!renderer) DisplayDrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); - else renderer->DrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); - } - } - - if (auto_draw&1) { - if (renderer) renderer->Updateframe(); - else DisplayDrawFrame(); - } -} - - - -#ifdef USE_DISPLAY_MODES1TO5 - -void DisplayClearScreenBuffer(void) -{ - if (disp_screen_buffer_cols) { - for (uint32_t i = 0; i < disp_screen_buffer_rows; i++) { - memset(disp_screen_buffer[i], 0, disp_screen_buffer_cols); - } - } -} - -void DisplayFreeScreenBuffer(void) -{ - if (disp_screen_buffer != nullptr) { - for (uint32_t i = 0; i < disp_screen_buffer_rows; i++) { - if (disp_screen_buffer[i] != nullptr) { free(disp_screen_buffer[i]); } - } - free(disp_screen_buffer); - disp_screen_buffer_cols = 0; - disp_screen_buffer_rows = 0; - } -} - -void DisplayAllocScreenBuffer(void) -{ - if (!disp_screen_buffer_cols) { - disp_screen_buffer_rows = Settings.display_rows; - disp_screen_buffer = (char**)malloc(sizeof(*disp_screen_buffer) * disp_screen_buffer_rows); - if (disp_screen_buffer != nullptr) { - for (uint32_t i = 0; i < disp_screen_buffer_rows; i++) { - disp_screen_buffer[i] = (char*)malloc(sizeof(*disp_screen_buffer[i]) * (Settings.display_cols[0] +1)); - if (disp_screen_buffer[i] == nullptr) { - DisplayFreeScreenBuffer(); - break; - } - } - } - if (disp_screen_buffer != nullptr) { - disp_screen_buffer_cols = Settings.display_cols[0] +1; - DisplayClearScreenBuffer(); - } - } -} - -void DisplayReAllocScreenBuffer(void) -{ - DisplayFreeScreenBuffer(); - DisplayAllocScreenBuffer(); -} - -void DisplayFillScreen(uint32_t line) -{ - uint32_t len = disp_screen_buffer_cols - strlen(disp_screen_buffer[line]); - if (len) { - memset(disp_screen_buffer[line] + strlen(disp_screen_buffer[line]), 0x20, len); - disp_screen_buffer[line][disp_screen_buffer_cols -1] = 0; - } -} - - - -void DisplayClearLogBuffer(void) -{ - if (disp_log_buffer_cols) { - for (uint32_t i = 0; i < DISPLAY_LOG_ROWS; i++) { - memset(disp_log_buffer[i], 0, disp_log_buffer_cols); - } - } -} - -void DisplayFreeLogBuffer(void) -{ - if (disp_log_buffer != nullptr) { - for (uint32_t i = 0; i < DISPLAY_LOG_ROWS; i++) { - if (disp_log_buffer[i] != nullptr) { free(disp_log_buffer[i]); } - } - free(disp_log_buffer); - disp_log_buffer_cols = 0; - } -} - -void DisplayAllocLogBuffer(void) -{ - if (!disp_log_buffer_cols) { - disp_log_buffer = (char**)malloc(sizeof(*disp_log_buffer) * DISPLAY_LOG_ROWS); - if (disp_log_buffer != nullptr) { - for (uint32_t i = 0; i < DISPLAY_LOG_ROWS; i++) { - disp_log_buffer[i] = (char*)malloc(sizeof(*disp_log_buffer[i]) * (Settings.display_cols[0] +1)); - if (disp_log_buffer[i] == nullptr) { - DisplayFreeLogBuffer(); - break; - } - } - } - if (disp_log_buffer != nullptr) { - disp_log_buffer_cols = Settings.display_cols[0] +1; - DisplayClearLogBuffer(); - } - } -} - -void DisplayReAllocLogBuffer(void) -{ - DisplayFreeLogBuffer(); - DisplayAllocLogBuffer(); -} - -void DisplayLogBufferAdd(char* txt) -{ - if (disp_log_buffer_cols) { - strlcpy(disp_log_buffer[disp_log_buffer_idx], txt, disp_log_buffer_cols); - disp_log_buffer_idx++; - if (DISPLAY_LOG_ROWS == disp_log_buffer_idx) { disp_log_buffer_idx = 0; } - } -} - -char* DisplayLogBuffer(char temp_code) -{ - char* result = nullptr; - if (disp_log_buffer_cols) { - if (disp_log_buffer_idx != disp_log_buffer_ptr) { - result = disp_log_buffer[disp_log_buffer_ptr]; - disp_log_buffer_ptr++; - if (DISPLAY_LOG_ROWS == disp_log_buffer_ptr) { disp_log_buffer_ptr = 0; } - - char *pch = strchr(result, '~'); - if (pch != nullptr) { result[pch - result] = temp_code; } - } - } - return result; -} - -void DisplayLogBufferInit(void) -{ - if (Settings.display_mode) { - disp_log_buffer_idx = 0; - disp_log_buffer_ptr = 0; - disp_refresh = Settings.display_refresh; - - snprintf_P(disp_temp, sizeof(disp_temp), PSTR("%c"), TempUnit()); - snprintf_P(disp_pres, sizeof(disp_pres), PressureUnit().c_str()); - - DisplayReAllocLogBuffer(); - - char buffer[40]; - snprintf_P(buffer, sizeof(buffer), PSTR(D_VERSION " %s%s"), my_version, my_image); - DisplayLogBufferAdd(buffer); - snprintf_P(buffer, sizeof(buffer), PSTR("Display mode %d"), Settings.display_mode); - DisplayLogBufferAdd(buffer); - - snprintf_P(buffer, sizeof(buffer), PSTR(D_CMND_HOSTNAME " %s"), my_hostname); - DisplayLogBufferAdd(buffer); - snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_SSID " %s"), SettingsText(SET_STASSID1 + Settings.sta_active)); - DisplayLogBufferAdd(buffer); - snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_MAC " %s"), WiFi.macAddress().c_str()); - DisplayLogBufferAdd(buffer); - if (!global_state.wifi_down) { - snprintf_P(buffer, sizeof(buffer), PSTR("IP %s"), WiFi.localIP().toString().c_str()); - DisplayLogBufferAdd(buffer); - snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_RSSI " %d%%"), WifiGetRssiAsQuality(WiFi.RSSI())); - DisplayLogBufferAdd(buffer); - } - } -} - - - - - -enum SensorQuantity { - JSON_TEMPERATURE, - JSON_HUMIDITY, JSON_LIGHT, JSON_NOISE, JSON_AIRQUALITY, - JSON_PRESSURE, JSON_PRESSUREATSEALEVEL, - JSON_ILLUMINANCE, - JSON_GAS, - JSON_YESTERDAY, JSON_TOTAL, JSON_TODAY, - JSON_PERIOD, - JSON_POWERFACTOR, JSON_COUNTER, JSON_ANALOG_INPUT, JSON_UV_LEVEL, - JSON_CURRENT, - JSON_VOLTAGE, - JSON_POWERUSAGE, - JSON_CO2, - JSON_FREQUENCY }; -const char kSensorQuantity[] PROGMEM = - D_JSON_TEMPERATURE "|" - D_JSON_HUMIDITY "|" D_JSON_LIGHT "|" D_JSON_NOISE "|" D_JSON_AIRQUALITY "|" - D_JSON_PRESSURE "|" D_JSON_PRESSUREATSEALEVEL "|" - D_JSON_ILLUMINANCE "|" - D_JSON_GAS "|" - D_JSON_YESTERDAY "|" D_JSON_TOTAL "|" D_JSON_TODAY "|" - D_JSON_PERIOD "|" - D_JSON_POWERFACTOR "|" D_JSON_COUNTER "|" D_JSON_ANALOG_INPUT "|" D_JSON_UV_LEVEL "|" - D_JSON_CURRENT "|" - D_JSON_VOLTAGE "|" - D_JSON_POWERUSAGE "|" - D_JSON_CO2 "|" - D_JSON_FREQUENCY ; - -void DisplayJsonValue(const char* topic, const char* device, const char* mkey, const char* value) -{ - char quantity[TOPSZ]; - char buffer[Settings.display_cols[0] +1]; - char spaces[Settings.display_cols[0]]; - char source[Settings.display_cols[0] - Settings.display_cols[1]]; - char svalue[Settings.display_cols[1] +1]; - -#ifdef USE_DEBUG_DRIVER - ShowFreeMem(PSTR("DisplayJsonValue")); -#endif - - memset(spaces, 0x20, sizeof(spaces)); - spaces[sizeof(spaces) -1] = '\0'; - snprintf_P(source, sizeof(source), PSTR("%s%s%s%s"), topic, (strlen(topic))?"/":"", mkey, spaces); - - int quantity_code = GetCommandCode(quantity, sizeof(quantity), mkey, kSensorQuantity); - if ((-1 == quantity_code) || !strcmp_P(mkey, S_RSLT_POWER)) { - return; - } - if (JSON_TEMPERATURE == quantity_code) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s~%s"), value, disp_temp); - } - else if ((quantity_code >= JSON_HUMIDITY) && (quantity_code <= JSON_AIRQUALITY)) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s%%"), value); - } - else if ((quantity_code >= JSON_PRESSURE) && (quantity_code <= JSON_PRESSUREATSEALEVEL)) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s%s"), value, disp_pres); - } - else if (JSON_ILLUMINANCE == quantity_code) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_LUX), value); - } - else if (JSON_GAS == quantity_code) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_KILOOHM), value); - } - else if ((quantity_code >= JSON_YESTERDAY) && (quantity_code <= JSON_TODAY)) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_KILOWATTHOUR), value); - } - else if (JSON_PERIOD == quantity_code) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_WATTHOUR), value); - } - else if ((quantity_code >= JSON_POWERFACTOR) && (quantity_code <= JSON_UV_LEVEL)) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s"), value); - } - else if (JSON_CURRENT == quantity_code) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_AMPERE), value); - } - else if (JSON_VOLTAGE == quantity_code) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_VOLT), value); - } - else if (JSON_POWERUSAGE == quantity_code) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_WATT), value); - } - else if (JSON_CO2 == quantity_code) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_PARTS_PER_MILLION), value); - } - else if (JSON_FREQUENCY == quantity_code) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_HERTZ), value); - } - snprintf_P(buffer, sizeof(buffer), PSTR("%s %s"), source, svalue); - - - - DisplayLogBufferAdd(buffer); -} - -void DisplayAnalyzeJson(char *topic, char *json) -{ -# 1145 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_13_display.ino" - String jsonStr = json; - - StaticJsonBuffer<1024> jsonBuf; - JsonObject &root = jsonBuf.parseObject(jsonStr); - if (root.success()) { - - const char *unit; - unit = root[D_JSON_TEMPERATURE_UNIT]; - if (unit) { - snprintf_P(disp_temp, sizeof(disp_temp), PSTR("%s"), unit); - } - unit = root[D_JSON_PRESSURE_UNIT]; - if (unit) { - snprintf_P(disp_pres, sizeof(disp_pres), PSTR("%s"), unit); - } - - for (JsonObject::iterator it = root.begin(); it != root.end(); ++it) { - JsonVariant value = it->value; - if (value.is()) { - JsonObject& Object2 = value; - for (JsonObject::iterator it2 = Object2.begin(); it2 != Object2.end(); ++it2) { - JsonVariant value2 = it2->value; - if (value2.is()) { - JsonObject& Object3 = value2; - for (JsonObject::iterator it3 = Object3.begin(); it3 != Object3.end(); ++it3) { - const char* value = it3->value; - if (value != nullptr) { - DisplayJsonValue(topic, it->key, it3->key, value); - } - } - } else { - const char* value = it2->value; - if (value != nullptr) { - DisplayJsonValue(topic, it->key, it2->key, value); - } - } - } - } else { - const char* value = it->value; - if (value != nullptr) { - DisplayJsonValue(topic, it->key, it->key, value); - } - } - } - } -} - -void DisplayMqttSubscribe(void) -{ - - - - - - - if (Settings.display_model && (Settings.display_mode &0x04)) { - - char stopic[TOPSZ]; - char ntopic[TOPSZ]; - - ntopic[0] = '\0'; - strlcpy(stopic, SettingsText(SET_MQTT_FULLTOPIC), sizeof(stopic)); - char *tp = strtok(stopic, "/"); - while (tp != nullptr) { - if (!strcmp_P(tp, MQTT_TOKEN_PREFIX)) { - break; - } - strncat_P(ntopic, PSTR("+/"), sizeof(ntopic) - strlen(ntopic) -1); - tp = strtok(nullptr, "/"); - } - strncat(ntopic, SettingsText(SET_MQTTPREFIX3), sizeof(ntopic) - strlen(ntopic) -1); - strncat_P(ntopic, PSTR("/#"), sizeof(ntopic) - strlen(ntopic) -1); - MqttSubscribe(ntopic); - disp_subscribed = true; - } else { - disp_subscribed = false; - } -} - -bool DisplayMqttData(void) -{ - if (disp_subscribed) { - char stopic[TOPSZ]; - - snprintf_P(stopic, sizeof(stopic) , PSTR("%s/"), SettingsText(SET_MQTTPREFIX3)); - char *tp = strstr(XdrvMailbox.topic, stopic); - if (tp) { - if (Settings.display_mode &0x04) { - tp = tp + strlen(stopic); - char *topic = strtok(tp, "/"); - DisplayAnalyzeJson(topic, XdrvMailbox.data); - } - return true; - } - } - return false; -} - -void DisplayLocalSensor(void) -{ - if ((Settings.display_mode &0x02) && (0 == tele_period)) { - char no_topic[1] = { 0 }; - - DisplayAnalyzeJson(no_topic, mqtt_data); - } -} - -#endif - - - - - -void DisplayInitDriver(void) -{ - XdspCall(FUNC_DISPLAY_INIT_DRIVER); - - if (renderer) { - renderer->setTextFont(Settings.display_font); - renderer->setTextSize(Settings.display_size); - } - - - - - if (Settings.display_model) { - devices_present++; - disp_device = devices_present; - -#ifndef USE_DISPLAY_MODES1TO5 - Settings.display_mode = 0; -#else - DisplayLogBufferInit(); -#endif - } -} - -void DisplaySetPower(void) -{ - disp_power = bitRead(XdrvMailbox.index, disp_device -1); - - - - if (Settings.display_model) { - if (!renderer) { - XdspCall(FUNC_DISPLAY_POWER); - } else { - renderer->DisplayOnff(disp_power); - } - } -} - - - - - -void CmndDisplay(void) -{ - Response_P(PSTR("{\"" D_PRFX_DISPLAY "\":{\"" D_CMND_DISP_MODEL "\":%d,\"" D_CMND_DISP_WIDTH "\":%d,\"" D_CMND_DISP_HEIGHT "\":%d,\"" - D_CMND_DISP_MODE "\":%d,\"" D_CMND_DISP_DIMMER "\":%d,\"" D_CMND_DISP_SIZE "\":%d,\"" D_CMND_DISP_FONT "\":%d,\"" - D_CMND_DISP_ROTATE "\":%d,\"" D_CMND_DISP_REFRESH "\":%d,\"" D_CMND_DISP_COLS "\":[%d,%d],\"" D_CMND_DISP_ROWS "\":%d}}"), - Settings.display_model, Settings.display_width, Settings.display_height, - Settings.display_mode, Settings.display_dimmer, Settings.display_size, Settings.display_font, - Settings.display_rotate, Settings.display_refresh, Settings.display_cols[0], Settings.display_cols[1], Settings.display_rows); -} - -void CmndDisplayModel(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < DISPLAY_MAX_DRIVERS)) { - uint32_t last_display_model = Settings.display_model; - Settings.display_model = XdrvMailbox.payload; - if (XdspCall(FUNC_DISPLAY_MODEL)) { - restart_flag = 2; - } else { - Settings.display_model = last_display_model; - } - } - ResponseCmndNumber(Settings.display_model); -} - -void CmndDisplayWidth(void) -{ - if (XdrvMailbox.payload > 0) { - if (XdrvMailbox.payload != Settings.display_width) { - Settings.display_width = XdrvMailbox.payload; - restart_flag = 2; - } - } - ResponseCmndNumber(Settings.display_width); -} - -void CmndDisplayHeight(void) -{ - if (XdrvMailbox.payload > 0) { - if (XdrvMailbox.payload != Settings.display_height) { - Settings.display_height = XdrvMailbox.payload; - restart_flag = 2; - } - } - ResponseCmndNumber(Settings.display_height); -} - -void CmndDisplayMode(void) -{ -#ifdef USE_DISPLAY_MODES1TO5 - - - - - - - - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) { - uint32_t last_display_mode = Settings.display_mode; - Settings.display_mode = XdrvMailbox.payload; - - if (disp_subscribed != (Settings.display_mode &0x04)) { - restart_flag = 2; - } else { - if (last_display_mode && !Settings.display_mode) { - DisplayInit(DISPLAY_INIT_MODE); - if (renderer) renderer->fillScreen(bg_color); - else DisplayClear(); - } else { - DisplayLogBufferInit(); - DisplayInit(DISPLAY_INIT_MODE); - } - } - } -#endif - ResponseCmndNumber(Settings.display_mode); -} - -void CmndDisplayDimmer(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - Settings.display_dimmer = ((XdrvMailbox.payload +1) * 100) / 666; - if (Settings.display_dimmer && !(disp_power)) { - ExecuteCommandPower(disp_device, POWER_ON, SRC_DISPLAY); - } - else if (!Settings.display_dimmer && disp_power) { - ExecuteCommandPower(disp_device, POWER_OFF, SRC_DISPLAY); - } - if (renderer) renderer->dim(Settings.display_dimmer); - } - ResponseCmndNumber(Settings.display_dimmer); -} - -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)) { - Settings.display_font = XdrvMailbox.payload; - if (renderer) renderer->setTextFont(Settings.display_font); - else DisplaySetFont(Settings.display_font); - } - ResponseCmndNumber(Settings.display_font); -} - -void CmndDisplayRotate(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 4)) { - if (Settings.display_rotate != XdrvMailbox.payload) { -# 1428 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_13_display.ino" - Settings.display_rotate = XdrvMailbox.payload; - DisplayInit(DISPLAY_INIT_MODE); -#ifdef USE_DISPLAY_MODES1TO5 - DisplayLogBufferInit(); -#endif - } - } - ResponseCmndNumber(Settings.display_rotate); -} - -void CmndDisplayText(void) -{ - if (disp_device && XdrvMailbox.data_len > 0) { -#ifndef USE_DISPLAY_MODES1TO5 - DisplayText(); -#else - if (!Settings.display_mode) { - DisplayText(); - } else { - DisplayLogBufferAdd(XdrvMailbox.data); - } -#endif - ResponseCmndChar(XdrvMailbox.data); - } -} - -void CmndDisplayAddress(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 8)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 255)) { - Settings.display_address[XdrvMailbox.index -1] = XdrvMailbox.payload; - } - ResponseCmndIdxNumber(Settings.display_address[XdrvMailbox.index -1]); - } -} - -void CmndDisplayRefresh(void) -{ - if ((XdrvMailbox.payload >= 1) && (XdrvMailbox.payload <= 7)) { - Settings.display_refresh = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.display_refresh); -} - -void CmndDisplayColumns(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_COLS)) { - Settings.display_cols[XdrvMailbox.index -1] = XdrvMailbox.payload; -#ifdef USE_DISPLAY_MODES1TO5 - if (1 == XdrvMailbox.index) { - DisplayLogBufferInit(); - DisplayReAllocScreenBuffer(); - } -#endif - } - ResponseCmndIdxNumber(Settings.display_cols[XdrvMailbox.index -1]); - } -} - -void CmndDisplayRows(void) -{ - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_ROWS)) { - Settings.display_rows = XdrvMailbox.payload; -#ifdef USE_DISPLAY_MODES1TO5 - DisplayLogBufferInit(); - DisplayReAllocScreenBuffer(); -#endif - } - ResponseCmndNumber(Settings.display_rows); -} - - - - - -#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) -void Draw_RGB_Bitmap(char *file,uint16_t xp, uint16_t yp) { - if (!renderer) return; - - - File fp; - fp=SD.open(file,FILE_READ); - if (!fp) return; - uint16_t xsize; - fp.read((uint8_t*)&xsize,2); - uint16_t ysize; - fp.read((uint8_t*)&ysize,2); - -#if 1 -#define XBUFF 128 - uint16_t xdiv=xsize/XBUFF; - renderer->setAddrWindow(xp,yp,xp+xsize,yp+ysize); - for(int16_t j=0; j=2) renderer->pushColors(rgb,len/2,true); - } - OsWatchLoop(); - } - renderer->setAddrWindow(0,0,0,0); -#else - for(int16_t j=0; jwritePixel(xp+i,yp,rgb); - } - delay(0); - OsWatchLoop(); - yp++; - } -#endif - fp.close(); -} -#endif - -#ifdef USE_AWATCH -#define MINUTE_REDUCT 4 - -#ifndef pi -#define pi 3.14159265359 -#endif - - -void DrawAClock(uint16_t rad) { - if (!renderer) return; - float frad=rad; - uint16_t hred=frad/3.0; - renderer->fillCircle(disp_xpos, disp_ypos, rad, bg_color); - renderer->drawCircle(disp_xpos, disp_ypos, rad, fg_color); - renderer->fillCircle(disp_xpos, disp_ypos, 4, fg_color); - for (uint8_t count=0; count<60; count+=5) { - float p1=((float)count*(pi/30)-(pi/2)); - uint8_t len; - if ((count%15)==0) { - len=4; - } else { - len=2; - } - renderer->writeLine(disp_xpos+((float)(rad-len)*cosf(p1)), disp_ypos+((float)(rad-len)*sinf(p1)), disp_xpos+(frad*cosf(p1)), disp_ypos+(frad*sinf(p1)), fg_color); - } - - - float hour=((float)RtcTime.hour*60.0+(float)RtcTime.minute)/60.0; - float temp=(hour*(pi/6.0)-(pi/2.0)); - renderer->writeLine(disp_xpos, disp_ypos,disp_xpos+(frad-hred)*cosf(temp),disp_ypos+(frad-hred)*sinf(temp), fg_color); - - - temp=((float)RtcTime.minute*(pi/30.0)-(pi/2.0)); - renderer->writeLine(disp_xpos, disp_ypos,disp_xpos+(frad-MINUTE_REDUCT)*cosf(temp),disp_ypos+(frad-MINUTE_REDUCT)*sinf(temp), fg_color); -} -#endif - - -#ifdef USE_GRAPH - -typedef union { - uint8_t data; - struct { - uint8_t overlay : 1; - uint8_t draw : 1; - uint8_t nu3 : 1; - uint8_t nu4 : 1; - uint8_t nu5 : 1; - uint8_t nu6 : 1; - uint8_t nu7 : 1; - uint8_t nu8 : 1; - }; -} GFLAGS; - -struct GRAPH { - uint16_t xp; - uint16_t yp; - uint16_t xs; - uint16_t ys; - float ymin; - float ymax; - float range; - uint32_t x_time; - uint32_t last_ms; - uint32_t last_ms_redrawn; - int16_t decimation; - uint16_t dcnt; - uint32_t summ; - uint16_t xcnt; - uint8_t *values; - uint8_t xticks; - uint8_t yticks; - uint8_t last_val; - uint8_t color_index; - GFLAGS flags; -}; - - -struct GRAPH *graph[NUM_GRAPHS]; - -#define TICKLEN 4 -void ClrGraph(uint16_t num) { - struct GRAPH *gp=graph[num]; - - uint16_t xticks=gp->xticks; - uint16_t yticks=gp->yticks; - uint16_t count; - - - if (gp->flags.overlay) return; - - renderer->fillRect(gp->xp+1,gp->yp+1,gp->xs-2,gp->ys-2,bg_color); - - if (xticks) { - float cxp=gp->xp,xd=(float)gp->xs/(float)xticks; - for (count=0; countwriteFastVLine(cxp,gp->yp+gp->ys-TICKLEN,TICKLEN,fg_color); - cxp+=xd; - } - } - if (yticks) { - if (gp->ymin<0 && gp->ymax>0) { - - float cxp=0; - float czp=gp->yp+(gp->ymax/gp->range); - while (cxpxs) { - renderer->writeFastHLine(gp->xp+cxp,czp,2,fg_color); - cxp+=6.0; - } - - float cyp=0,yd=gp->ys/yticks; - for (count=0; countgp->yp) { - renderer->writeFastHLine(gp->xp,czp-cyp,TICKLEN,fg_color); - renderer->writeFastHLine(gp->xp+gp->xs-TICKLEN,czp-cyp,TICKLEN,fg_color); - } - if ((czp+cyp)<(gp->yp+gp->ys)) { - renderer->writeFastHLine(gp->xp,czp+cyp,TICKLEN,fg_color); - renderer->writeFastHLine(gp->xp+gp->xs-TICKLEN,czp+cyp,TICKLEN,fg_color); - } - cyp+=yd; - } - } else { - float cyp=gp->yp,yd=gp->ys/yticks; - for (count=0; countwriteFastHLine(gp->xp,cyp,TICKLEN,fg_color); - renderer->writeFastHLine(gp->xp+gp->xs-TICKLEN,cyp,TICKLEN,fg_color); - cyp+=yd; - } - } - } -} - - -void DefineGraph(uint16_t num,uint16_t xp,uint16_t yp,int16_t xs,uint16_t ys,int16_t dec,float ymin, float ymax,uint8_t icol) { - if (!renderer) return; - uint8_t rflg=0; - if (xs<0) { - rflg=1; - xs=abs(xs); - } - struct GRAPH *gp; - uint16_t count; - uint16_t index=num%NUM_GRAPHS; - if (!graph[index]) { - gp=(struct GRAPH*)calloc(sizeof(struct GRAPH),1); - if (!gp) return; - graph[index]=gp; - } else { - gp=graph[index]; - if (rflg) { - RedrawGraph(index,1); - return; - } - } - - - gp->xticks=(num>>4)&0x3f; - gp->yticks=(num>>10)&0x3f; - gp->xp=xp; - gp->yp=yp; - gp->xs=xs; - gp->ys=ys; - if (!dec) dec=1; - gp->decimation=dec; - if (dec>0) { - - gp->x_time=((float)dec*60000.0)/(float)xs; - gp->last_ms=millis()+gp->x_time; - } - gp->ymin=ymin; - gp->ymax=ymax; - gp->range=(ymax-ymin)/ys; - gp->xcnt=0; - gp->dcnt=0; - gp->summ=0; - if (gp->values) free(gp->values); - gp->values=(uint8_t*) calloc(1,xs+2); - if (!gp->values) { - free(gp); - graph[index]=0; - return; - } - - gp->values[0]=0; - - gp->last_ms_redrawn=millis(); - - if (!icol) icol=1; - gp->color_index=icol; - gp->flags.overlay=0; - gp->flags.draw=1; - - - if (index>0) { - for (uint8_t count=0; countxp==gp1->xp) && (gp->yp==gp1->yp)) { - gp->flags.overlay=1; - break; - } - } - } - } - - - renderer->drawRect(xp,yp,xs,ys,fg_color); - - ClrGraph(index); - -} - - -void DisplayCheckGraph() { - int16_t count; - struct GRAPH *gp; - for (count=0;countdecimation>0) { - - while (millis()>gp->last_ms) { - gp->last_ms+=gp->x_time; - uint8_t val; - if (gp->dcnt) { - val=gp->summ/gp->dcnt; - gp->dcnt=0; - gp->summ=0; - gp->last_val=val; - } else { - val=gp->last_val; - } - AddGraph(count,val); - } - } - } - } -} - - -#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) -#include - -void Save_graph(uint8_t num, char *path) { - if (!renderer) return; - uint16_t index=num%NUM_GRAPHS; - struct GRAPH *gp=graph[index]; - if (!gp) return; - File fp; - SD.remove(path); - fp=SD.open(path,FILE_WRITE); - if (!fp) return; - char str[32]; - sprintf_P(str,PSTR("%d\t%d\t%d\t"),gp->xcnt,gp->xs,gp->ys); - fp.print(str); - dtostrfd(gp->ymin,2,str); - fp.print(str); - fp.print("\t"); - dtostrfd(gp->ymax,2,str); - fp.print(str); - fp.print("\t"); - for (uint32_t count=0;countxs;count++) { - dtostrfd(gp->values[count],0,str); - fp.print(str); - fp.print("\t"); - } - fp.print("\n"); - fp.close(); -} -void Restore_graph(uint8_t num, char *path) { - if (!renderer) return; - uint16_t index=num%NUM_GRAPHS; - struct GRAPH *gp=graph[index]; - if (!gp) return; - File fp; - fp=SD.open(path,FILE_READ); - if (!fp) return; - char vbuff[32]; - char *cp=vbuff; - uint8_t buf[2]; - uint8_t findex=0; - - for (uint32_t count=0;count<=gp->xs+4;count++) { - cp=vbuff; - findex=0; - while (fp.available()) { - fp.read(buf,1); - if (buf[0]=='\t' || buf[0]==',' || buf[0]=='\n' || buf[0]=='\r') { - break; - } else { - *cp++=buf[0]; - findex++; - if (findex>=sizeof(vbuff)-1) break; - } - } - *cp=0; - if (count<=4) { - if (count==0) gp->xcnt=atoi(vbuff); - } else { - gp->values[count-5]=atoi(vbuff); - } - } - fp.close(); - RedrawGraph(num,1); -} -#endif - -void RedrawGraph(uint8_t num, uint8_t flags) { - uint16_t index=num%NUM_GRAPHS; - struct GRAPH *gp=graph[index]; - if (!gp) return; - if (!flags) { - gp->flags.draw=0; - return; - } - if (!renderer) return; - - gp->flags.draw=1; - uint16_t linecol=fg_color; - - if (color_type==COLOR_COLOR) { - linecol=renderer->GetColorFromIndex(gp->color_index); - } - - if (!gp->flags.overlay) { - - renderer->drawRect(gp->xp,gp->yp,gp->xs,gp->ys,fg_color); - - ClrGraph(index); - } - - for (uint16_t count=0;countxs-1;count++) { - renderer->writeLine(gp->xp+count,gp->yp+gp->ys-gp->values[count]-1,gp->xp+count+1,gp->yp+gp->ys-gp->values[count+1]-1,linecol); - } -} - - -void AddGraph(uint8_t num,uint8_t val) { - struct GRAPH *gp=graph[num]; - if (!renderer) return; - - uint16_t linecol=fg_color; - if (color_type==COLOR_COLOR) { - linecol=renderer->GetColorFromIndex(gp->color_index); - } - gp->xcnt++; - if (gp->xcnt>gp->xs) { - gp->xcnt=gp->xs; - int16_t count; - - for (count=0;countxs-1;count++) { - gp->values[count]=gp->values[count+1]; - } - gp->values[gp->xcnt-1]=val; - - if (!gp->flags.draw) return; - - - if (millis()-gp->last_ms_redrawn>1000) { - gp->last_ms_redrawn=millis(); - - if (!gp->flags.overlay) { - - renderer->drawRect(gp->xp,gp->yp,gp->xs,gp->ys,fg_color); - - ClrGraph(num); - } - - for (count=0;countxs-1;count++) { - renderer->writeLine(gp->xp+count,gp->yp+gp->ys-gp->values[count]-1,gp->xp+count+1,gp->yp+gp->ys-gp->values[count+1]-1,linecol); - } - } - } else { - - gp->values[gp->xcnt]=val; - if (!gp->flags.draw) return; - renderer->writeLine(gp->xp+gp->xcnt-1,gp->yp+gp->ys-gp->values[gp->xcnt-1]-1,gp->xp+gp->xcnt,gp->yp+gp->ys-gp->values[gp->xcnt]-1,linecol); - } -} - - - -void AddValue(uint8_t num,float fval) { - - num=num%NUM_GRAPHS; - struct GRAPH *gp=graph[num]; - if (!gp) return; - - if (fval>gp->ymax) fval=gp->ymax; - if (fvalymin) fval=gp->ymin; - - int16_t val; - val=(fval-gp->ymin)/gp->range; - - if (val>gp->ys-1) val=gp->ys-1; - if (val<0) val=0; - - - gp->summ+=val; - gp->dcnt++; - - - if (gp->decimation<0) { - if (gp->dcnt>=-gp->decimation) { - gp->dcnt=0; - - val=gp->summ/-gp->decimation; - gp->summ=0; - - AddGraph(num,val); - } - } -} -#endif - - - - - -bool Xdrv13(uint8_t function) -{ - bool result = false; - - if ((i2c_flg || spi_flg || soft_spi_flg) && XdspPresent()) { - switch (function) { - case FUNC_PRE_INIT: - DisplayInitDriver(); -#ifdef USE_GRAPH - for (uint8_t count=0;count - -TasmotaSerial *MP3Player; - - - - - -#define D_CMND_MP3 "MP3" - -const char S_JSON_MP3_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_MP3 "%s\":%d}"; -const char S_JSON_MP3_COMMAND[] PROGMEM = "{\"" D_CMND_MP3 "%s\"}"; -const char kMP3_Commands[] PROGMEM = "Track|Play|Pause|Stop|Volume|EQ|Device|Reset|DAC"; - - - - - -enum MP3_Commands { - CMND_MP3_TRACK, - CMND_MP3_PLAY, - CMND_MP3_PAUSE, - CMND_MP3_STOP, - CMND_MP3_VOLUME, - CMND_MP3_EQ, - CMND_MP3_DEVICE, - CMND_MP3_RESET, - CMND_MP3_DAC }; - - - - - - -#define MP3_CMD_RESET_VALUE 0 - -#define MP3_CMD_TRACK 0x03 -#define MP3_CMD_PLAY 0x0d -#define MP3_CMD_PAUSE 0x0e -#define MP3_CMD_STOP 0x16 -#define MP3_CMD_VOLUME 0x06 -#define MP3_CMD_EQ 0x07 -#define MP3_CMD_DEVICE 0x09 -#define MP3_CMD_RESET 0x0C -#define MP3_CMD_DAC 0x1A - - - - - - -uint16_t MP3_Checksum(uint8_t *array) -{ - uint16_t checksum = 0; - for (uint32_t i = 0; i < 6; i++) { - checksum += array[i]; - } - checksum = checksum^0xffff; - return (checksum+1); -} - - - - - - -void MP3PlayerInit(void) { - MP3Player = new TasmotaSerial(-1, pin[GPIO_MP3_DFR562]); - - if (MP3Player->begin(9600)) { - MP3Player->flush(); - delay(1000); - MP3_CMD(MP3_CMD_RESET, MP3_CMD_RESET_VALUE); - delay(3000); - MP3_CMD(MP3_CMD_VOLUME, MP3_VOLUME); - } - return; -} -# 159 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_14_mp3.ino" -void MP3_CMD(uint8_t mp3cmd,uint16_t val) { - uint8_t i = 0; - uint8_t cmd[10] = {0x7e,0xff,6,0,0,0,0,0,0,0xef}; - cmd[3] = mp3cmd; - cmd[4] = 0; - cmd[5] = val>>8; - cmd[6] = val; - uint16_t chks = MP3_Checksum(&cmd[1]); - cmd[7] = chks>>8; - cmd[8] = chks; - MP3Player->write(cmd, sizeof(cmd)); - delay(1000); - if (mp3cmd == MP3_CMD_RESET) { - MP3_CMD(MP3_CMD_VOLUME, MP3_VOLUME); - } - return; -} - - - - - -bool MP3PlayerCmd(void) { - char command[CMDSZ]; - bool serviced = true; - uint8_t disp_len = strlen(D_CMND_MP3); - - if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_MP3), disp_len)) { - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kMP3_Commands); - - switch (command_code) { - case CMND_MP3_TRACK: - case CMND_MP3_VOLUME: - case CMND_MP3_EQ: - case CMND_MP3_DEVICE: - case CMND_MP3_DAC: - - if (XdrvMailbox.data_len > 0) { - if (command_code == CMND_MP3_TRACK) { MP3_CMD(MP3_CMD_TRACK, XdrvMailbox.payload); } - if (command_code == CMND_MP3_VOLUME) { MP3_CMD(MP3_CMD_VOLUME, XdrvMailbox.payload * 30 / 100); } - if (command_code == CMND_MP3_EQ) { MP3_CMD(MP3_CMD_EQ, XdrvMailbox.payload); } - if (command_code == CMND_MP3_DEVICE) { MP3_CMD(MP3_CMD_DEVICE, XdrvMailbox.payload); } - if (command_code == CMND_MP3_DAC) { MP3_CMD(MP3_CMD_DAC, XdrvMailbox.payload); } - } - Response_P(S_JSON_MP3_COMMAND_NVALUE, command, XdrvMailbox.payload); - break; - case CMND_MP3_PLAY: - case CMND_MP3_PAUSE: - case CMND_MP3_STOP: - case CMND_MP3_RESET: - - if (command_code == CMND_MP3_PLAY) { MP3_CMD(MP3_CMD_PLAY, 0); } - if (command_code == CMND_MP3_PAUSE) { MP3_CMD(MP3_CMD_PAUSE, 0); } - if (command_code == CMND_MP3_STOP) { MP3_CMD(MP3_CMD_STOP, 0); } - if (command_code == CMND_MP3_RESET) { MP3_CMD(MP3_CMD_RESET, 0); } - Response_P(S_JSON_MP3_COMMAND, command, XdrvMailbox.payload); - break; - default: - - serviced = false; - break; - } - } else { - return false; - } - return serviced; -} - - - - - -bool Xdrv14(uint8_t function) -{ - bool result = false; - - if (pin[GPIO_MP3_DFR562] < 99) { - switch (function) { - case FUNC_PRE_INIT: - MP3PlayerInit(); - break; - case FUNC_COMMAND: - result = MP3PlayerCmd(); - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_15_pca9685.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_15_pca9685.ino" -#ifdef USE_I2C -#ifdef USE_PCA9685 - - - - - - -#define XDRV_15 15 -#define XI2C_01 1 - -#define PCA9685_REG_MODE1 0x00 -#define PCA9685_REG_LED0_ON_L 0x06 -#define PCA9685_REG_PRE_SCALE 0xFE - -#ifndef USE_PCA9685_ADDR - #define USE_PCA9685_ADDR 0x40 -#endif -#ifndef USE_PCA9685_FREQ - #define USE_PCA9685_FREQ 50 -#endif - -bool pca9685_detected = false; -uint16_t pca9685_freq = USE_PCA9685_FREQ; -uint16_t pca9685_pin_pwm_value[16]; - -void PCA9685_Detect(void) -{ - if (I2cActive(USE_PCA9685_ADDR)) { return; } - - uint8_t buffer; - if (I2cValidRead8(&buffer, USE_PCA9685_ADDR, PCA9685_REG_MODE1)) { - I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, 0x20); - if (I2cValidRead8(&buffer, USE_PCA9685_ADDR, PCA9685_REG_MODE1)) { - if (0x20 == buffer) { - pca9685_detected = true; - I2cSetActiveFound(USE_PCA9685_ADDR, "PCA9685"); - PCA9685_Reset(); - } - } - } -} - -void PCA9685_Reset(void) -{ - I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, 0x80); - PCA9685_SetPWMfreq(USE_PCA9685_FREQ); - for (uint32_t pin=0;pin<16;pin++) { - PCA9685_SetPWM(pin,0,false); - pca9685_pin_pwm_value[pin] = 0; - } - Response_P(PSTR("{\"PCA9685\":{\"RESET\":\"OK\"}}")); -} - -void PCA9685_SetPWMfreq(double freq) { - - - - - if (freq > 23 && freq < 1527) { - pca9685_freq=freq; - } else { - pca9685_freq=50; - } - uint8_t pre_scale_osc = round(25000000/(4096*pca9685_freq))-1; - if (1526 == pca9685_freq) pre_scale_osc=0xFF; - uint8_t current_mode1 = I2cRead8(USE_PCA9685_ADDR, PCA9685_REG_MODE1); - uint8_t sleep_mode1 = (current_mode1&0x7F) | 0x10; - I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, sleep_mode1); - I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_PRE_SCALE, pre_scale_osc); - I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, current_mode1 | 0xA0); -} - -void PCA9685_SetPWM_Reg(uint8_t pin, uint16_t on, uint16_t off) { - uint8_t led_reg = PCA9685_REG_LED0_ON_L + 4 * pin; - uint32_t led_data = 0; - I2cWrite8(USE_PCA9685_ADDR, led_reg, on); - I2cWrite8(USE_PCA9685_ADDR, led_reg+1, (on >> 8)); - I2cWrite8(USE_PCA9685_ADDR, led_reg+2, off); - I2cWrite8(USE_PCA9685_ADDR, led_reg+3, (off >> 8)); -} - -void PCA9685_SetPWM(uint8_t pin, uint16_t pwm, bool inverted) { - if (4096 == pwm) { - PCA9685_SetPWM_Reg(pin, 4096, 0); - } else { - PCA9685_SetPWM_Reg(pin, 0, pwm); - } - pca9685_pin_pwm_value[pin] = pwm; -} - -bool PCA9685_Command(void) -{ - bool serviced = true; - bool validpin = false; - uint8_t paramcount = 0; - if (XdrvMailbox.data_len > 0) { - paramcount=1; - } else { - serviced = false; - return serviced; - } - char sub_string[XdrvMailbox.data_len]; - for (uint32_t ca=0;ca 1) { - uint16_t new_freq = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - if ((new_freq >= 24) && (new_freq <= 1526)) { - PCA9685_SetPWMfreq(new_freq); - Response_P(PSTR("{\"PCA9685\":{\"PWMF\":%i, \"Result\":\"OK\"}}"),new_freq); - return serviced; - } - } else { - Response_P(PSTR("{\"PCA9685\":{\"PWMF\":%i}}"),pca9685_freq); - return serviced; - } - } - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"PWM")) { - if (paramcount > 1) { - uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - if (paramcount > 2) { - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 3), "ON")) { - PCA9685_SetPWM(pin, 4096, false); - Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,4096); - serviced = true; - return serviced; - } - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 3), "OFF")) { - PCA9685_SetPWM(pin, 0, false); - Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,0); - serviced = true; - return serviced; - } - uint16_t pwm = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); - if ((pin >= 0 && pin <= 15) && (pwm >= 0 && pwm <= 4096)) { - PCA9685_SetPWM(pin, pwm, false); - Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,pwm); - serviced = true; - return serviced; - } - } - } - } - return serviced; -} - -void PCA9685_OutputTelemetry(bool telemetry) -{ - 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]); - } - ResponseAppend_P(PSTR("\"END\":1}}")); - if (telemetry) { - MqttPublishTeleSensor(); - } -} - -bool Xdrv15(uint8_t function) -{ - if (!I2cEnabled(XI2C_01)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - PCA9685_Detect(); - } - else if (pca9685_detected) { - switch (function) { - case FUNC_EVERY_SECOND: - if (tele_period == 0) { - PCA9685_OutputTelemetry(true); - } - break; - case FUNC_COMMAND_DRIVER: - if (XDRV_15 == XdrvMailbox.index) { - result = PCA9685_Command(); - } - break; - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_16_tuyamcu.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_16_tuyamcu.ino" -#ifdef USE_LIGHT -#ifdef USE_TUYA_MCU - -#define XDRV_16 16 -#define XNRG_16 16 - -#ifndef TUYA_DIMMER_ID -#define TUYA_DIMMER_ID 0 -#endif - -#define TUYA_CMD_HEARTBEAT 0x00 -#define TUYA_CMD_QUERY_PRODUCT 0x01 -#define TUYA_CMD_MCU_CONF 0x02 -#define TUYA_CMD_WIFI_STATE 0x03 -#define TUYA_CMD_WIFI_RESET 0x04 -#define TUYA_CMD_WIFI_SELECT 0x05 -#define TUYA_CMD_SET_DP 0x06 -#define TUYA_CMD_STATE 0x07 -#define TUYA_CMD_QUERY_STATE 0x08 - -#define TUYA_LOW_POWER_CMD_WIFI_STATE 0x02 -#define TUYA_LOW_POWER_CMD_WIFI_RESET 0x03 -#define TUYA_LOW_POWER_CMD_WIFI_CONFIG 0x04 -#define TUYA_LOW_POWER_CMD_STATE 0x05 - -#define TUYA_TYPE_BOOL 0x01 -#define TUYA_TYPE_VALUE 0x02 -#define TUYA_TYPE_STRING 0x03 -#define TUYA_TYPE_ENUM 0x04 - -#define TUYA_BUFFER_SIZE 256 - -#include - -TasmotaSerial *TuyaSerial = nullptr; - -struct TUYA { - uint16_t new_dim = 0; - bool ignore_dim = false; - uint8_t cmd_status = 0; - uint8_t cmd_checksum = 0; - uint8_t data_len = 0; - uint8_t wifi_state = -2; - uint8_t heartbeat_timer = 0; -#ifdef USE_ENERGY_SENSOR - uint32_t lastPowerCheckTime = 0; -#endif - char *buffer = nullptr; - int byte_counter = 0; - bool low_power_mode = false; - bool send_success_next_second = false; - uint32_t ignore_dimmer_cmd_timeout = 0; -} Tuya; - - -enum TuyaSupportedFunctions { - TUYA_MCU_FUNC_NONE, - TUYA_MCU_FUNC_SWT1 = 1, - TUYA_MCU_FUNC_SWT2, - TUYA_MCU_FUNC_SWT3, - TUYA_MCU_FUNC_SWT4, - TUYA_MCU_FUNC_REL1 = 11, - 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_BATTERY_STATE, - TUYA_MCU_FUNC_BATTERY_PERCENTAGE, - TUYA_MCU_FUNC_REL1_INV = 41, - 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_LOWPOWER_MODE = 51, - TUYA_MCU_FUNC_LAST = 255 -}; - -const char kTuyaCommand[] PROGMEM = "|" - D_CMND_TUYA_MCU "|" D_CMND_TUYA_MCU_SEND_STATE; - -void (* const TuyaCommand[])(void) PROGMEM = { - &CmndTuyaMcu, &CmndTuyaSend -}; -# 128 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_16_tuyamcu.ino" -void CmndTuyaSend(void) { - if (XdrvMailbox.index > 4) { - return; - } - if (XdrvMailbox.index == 0) { - TuyaRequestState(); - } else { - if (XdrvMailbox.data_len > 0) { - char *p; - char *data; - uint8_t i = 0; - uint8_t dpId = 0; - for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 2; str = strtok_r(nullptr, ", ", &p)) { - if ( i == 0) { - dpId = strtoul(str, nullptr, 0); - } else { - data = str; - } - i++; - } - - if (1 == XdrvMailbox.index) { - TuyaSendBool(dpId, strtoul(data, nullptr, 0)); - } else if (2 == XdrvMailbox.index) { - TuyaSendValue(dpId, strtoull(data, nullptr, 0)); - } else if (3 == XdrvMailbox.index) { - TuyaSendString(dpId, data); - } else if (4 == XdrvMailbox.index) { - TuyaSendEnum(dpId, strtoul(data, nullptr, 0)); - } - } - } - ResponseCmndDone(); -} - - - - - - - -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("{\"" D_CMND_TUYA_MCU "\":[")); - 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("]}")); -} - - - - - -void TuyaAddMcuFunc(uint8_t fnId, uint8_t dpId) { - bool added = false; - - if (fnId == 0 || dpId == 0) { - 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 { - 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) { - 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) { - 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) { - bitClear(rel_inverted, fnId - TUYA_MCU_FUNC_REL1); - } else if (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) { - 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) || - (fnId == TUYA_MCU_FUNC_LOWPOWER_MODE); -} - -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)); - TuyaSerial->write(0x55); - TuyaSerial->write(0xAA); - TuyaSerial->write((uint8_t)0x00); - TuyaSerial->write(cmd); - TuyaSerial->write(payload_len >> 8); - TuyaSerial->write(payload_len & 0xFF); - snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Send \"55aa00%02x%02x%02x"), cmd, payload_len >> 8, payload_len & 0xFF); - for (uint32_t i = 0; i < payload_len; ++i) { - TuyaSerial->write(payload[i]); - checksum += payload[i]; - snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, payload[i]); - } - TuyaSerial->write(checksum); - TuyaSerial->flush(); - snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x\""), log_data, checksum); - AddLog(LOG_LEVEL_DEBUG); -} - -void TuyaSendState(uint8_t id, uint8_t type, uint8_t* value) -{ - uint16_t payload_len = 4; - uint8_t payload_buffer[8]; - payload_buffer[0] = id; - payload_buffer[1] = type; - switch (type) { - case TUYA_TYPE_BOOL: - case TUYA_TYPE_ENUM: - payload_len += 1; - payload_buffer[2] = 0x00; - payload_buffer[3] = 0x01; - payload_buffer[4] = value[0]; - break; - case TUYA_TYPE_VALUE: - payload_len += 4; - payload_buffer[2] = 0x00; - payload_buffer[3] = 0x04; - payload_buffer[4] = value[3]; - payload_buffer[5] = value[2]; - payload_buffer[6] = value[1]; - payload_buffer[7] = value[0]; - break; - - } - - TuyaSendCmd(TUYA_CMD_SET_DP, payload_buffer, payload_len); -} - -void TuyaSendBool(uint8_t id, bool value) -{ - TuyaSendState(id, TUYA_TYPE_BOOL, (uint8_t*)&value); -} - -void TuyaSendValue(uint8_t id, uint32_t value) -{ - TuyaSendState(id, TUYA_TYPE_VALUE, (uint8_t*)(&value)); -} - -void TuyaSendEnum(uint8_t id, uint32_t value) -{ - TuyaSendState(id, TUYA_TYPE_ENUM, (uint8_t*)(&value)); -} - -void TuyaSendString(uint8_t id, char data[]) { - - uint16_t len = strlen(data); - uint16_t payload_len = 4 + len; - uint8_t payload_buffer[payload_len]; - payload_buffer[0] = id; - payload_buffer[1] = TUYA_TYPE_STRING; - payload_buffer[2] = len >> 8; - payload_buffer[3] = len & 0xFF; - - for (uint16_t i = 0; i < len; i++) { - payload_buffer[4+i] = data[i]; - } - - TuyaSendCmd(TUYA_CMD_SET_DP, payload_buffer, payload_len); -} - -bool TuyaSetPower(void) -{ - bool status = false; - - uint8_t rpower = XdrvMailbox.index; - int16_t source = XdrvMailbox.payload; - - uint8_t dpid = TuyaGetDpId(TUYA_MCU_FUNC_REL1 + active_device - 1); - if (dpid == 0) dpid = TuyaGetDpId(TUYA_MCU_FUNC_REL1_INV + active_device - 1); - - if (source != SRC_SWITCH && TuyaSerial) { - TuyaSendBool(dpid, bitRead(rpower, active_device-1) ^ bitRead(rel_inverted, active_device-1)); - delay(20); - status = true; - } - return status; -} - -bool TuyaSetChannels(void) -{ - LightSerialDuty(((uint8_t*)XdrvMailbox.data)[0]); - - return true; -} - -void LightSerialDuty(uint16_t duty) -{ - uint8_t dpid = TuyaGetDpId(TUYA_MCU_FUNC_DIMMER); - if (duty > 0 && !Tuya.ignore_dim && TuyaSerial && dpid > 0) { - duty = changeUIntScale(duty, 0, 255, 0, Settings.dimmer_hw_max); - if (duty < Settings.dimmer_hw_min) { duty = Settings.dimmer_hw_min; } - if (Tuya.new_dim != duty) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim value=%d (id=%d)"), duty, dpid); - Tuya.ignore_dimmer_cmd_timeout = millis() + 250; - TuyaSendValue(dpid, duty); - } - } else if (dpid > 0) { - Tuya.ignore_dim = false; - duty = changeUIntScale(duty, 0, 255, 0, Settings.dimmer_hw_max); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim skipped value=%d"), duty); - } else { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Cannot set dimmer. Dimmer Id unknown")); - } -} - -void TuyaRequestState(void) -{ - if (TuyaSerial) { - - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Read MCU state")); - - TuyaSendCmd(TUYA_CMD_QUERY_STATE); - } -} - -void TuyaResetWifi(void) -{ - if (!Settings.flag.button_restrict) { - char scmnd[20]; - snprintf_P(scmnd, sizeof(scmnd), D_CMND_WIFICONFIG " %d", 2); - ExecuteCommand(scmnd, SRC_BUTTON); - } -} - -void TuyaProcessStatePacket(void) { - char scmnd[20]; - uint8_t dpidStart = 6; - uint8_t fnId; - uint16_t dpDataLen; - - while (dpidStart + 4 < Tuya.byte_counter) { - dpDataLen = Tuya.buffer[dpidStart + 2] << 8 | Tuya.buffer[dpidStart + 3]; - fnId = TuyaGetFuncId(Tuya.buffer[dpidStart]); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: fnId=%d is set for dpId=%d"), fnId, Tuya.buffer[dpidStart]); - - if (Tuya.buffer[dpidStart + 1] == 1) { - - 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[dpidStart + 4]?"On":"Off",bitRead(power, fnId - TUYA_MCU_FUNC_REL1)?"On":"Off"); - if ((power || Settings.light_dimmer > 0) && (Tuya.buffer[dpidStart + 4] != bitRead(power, fnId - TUYA_MCU_FUNC_REL1))) { - ExecuteCommandPower(fnId - TUYA_MCU_FUNC_REL1 + 1, Tuya.buffer[dpidStart + 4], SRC_SWITCH); - } - } 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[dpidStart + 4]?"Off":"On",bitRead(power, fnId - TUYA_MCU_FUNC_REL1_INV) ^ 1?"Off":"On"); - if (Tuya.buffer[dpidStart + 4] != bitRead(power, fnId - TUYA_MCU_FUNC_REL1_INV) ^ 1) { - ExecuteCommandPower(fnId - TUYA_MCU_FUNC_REL1_INV + 1, Tuya.buffer[dpidStart + 4] ^ 1, SRC_SWITCH); - } - } 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[dpidStart + 4], SwitchGetVirtual(fnId - TUYA_MCU_FUNC_SWT1)); - - if (SwitchGetVirtual(fnId - TUYA_MCU_FUNC_SWT1) != Tuya.buffer[dpidStart + 4]) { - SwitchSetVirtual(fnId - TUYA_MCU_FUNC_SWT1, Tuya.buffer[dpidStart + 4]); - SwitchHandler(1); - } - } - - } - else if (Tuya.buffer[dpidStart + 1] == 2) { - bool tuya_energy_enabled = (XNRG_16 == energy_flg); - uint16_t packetValue = Tuya.buffer[dpidStart + 6] << 8 | Tuya.buffer[dpidStart + 7]; - if (fnId == TUYA_MCU_FUNC_DIMMER) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Dim State=%d"), packetValue); - Tuya.new_dim = changeUIntScale(packetValue, 0, Settings.dimmer_hw_max, 0, 100); - if (Tuya.ignore_dimmer_cmd_timeout < millis()) { - if ((power || Settings.flag3.tuya_apply_o20) && - (Tuya.new_dim > 0) && (abs(Tuya.new_dim - Settings.light_dimmer) > 1)) { - Tuya.ignore_dim = true; - - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER "3 %d"), Tuya.new_dim ); - ExecuteCommand(scmnd, SRC_SWITCH); - } - } - } - - #ifdef USE_ENERGY_SENSOR - else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_VOLTAGE) { - Energy.voltage[0] = (float)packetValue / 10; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Voltage=%d"), Tuya.buffer[dpidStart], packetValue); - } else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_CURRENT) { - Energy.current[0] = (float)packetValue / 1000; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Current=%d"), Tuya.buffer[dpidStart], packetValue); - } else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_POWER) { - Energy.active_power[0] = (float)packetValue / 10; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Active_Power=%d"), Tuya.buffer[dpidStart], packetValue); - - if (Tuya.lastPowerCheckTime != 0 && Energy.active_power[0] > 0) { - Energy.kWhtoday += (float)Energy.active_power[0] * (Rtc.utc_time - Tuya.lastPowerCheckTime) / 36; - EnergyUpdateToday(); - } - Tuya.lastPowerCheckTime = Rtc.utc_time; - } - #endif - - } - - - dpidStart += dpDataLen + 4; - } -} - -void TuyaLowPowerModePacketProcess(void) { - switch (Tuya.buffer[3]) { - case TUYA_CMD_QUERY_PRODUCT: - TuyaHandleProductInfoPacket(); - TuyaSetWifiLed(); - break; - - case TUYA_LOW_POWER_CMD_STATE: - TuyaProcessStatePacket(); - Tuya.send_success_next_second = true; - break; - } - -} - -void TuyaHandleProductInfoPacket(void) { - uint16_t dataLength = Tuya.buffer[4] << 8 | Tuya.buffer[5]; - char *data = &Tuya.buffer[6]; - AddLog_P2(LOG_LEVEL_INFO, PSTR("TYA: MCU Product ID: %.*s"), dataLength, data); -} - -void TuyaSendLowPowerSuccessIfNeeded(void) { - uint8_t success = 1; - - if (Tuya.send_success_next_second) { - TuyaSendCmd(TUYA_LOW_POWER_CMD_STATE, &success, 1); - Tuya.send_success_next_second = false; - } -} - -void TuyaNormalPowerModePacketProcess(void) -{ - switch (Tuya.buffer[3]) { - case TUYA_CMD_QUERY_PRODUCT: - TuyaHandleProductInfoPacket(); - TuyaSendCmd(TUYA_CMD_MCU_CONF); - break; - - case TUYA_CMD_HEARTBEAT: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Heartbeat")); - if (Tuya.buffer[6] == 0) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Detected MCU restart")); - Tuya.wifi_state = -2; - } - break; - - case TUYA_CMD_STATE: - TuyaProcessStatePacket(); - break; - - case TUYA_CMD_WIFI_RESET: - case TUYA_CMD_WIFI_SELECT: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX WiFi Reset")); - TuyaResetWifi(); - break; - - case TUYA_CMD_WIFI_STATE: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX WiFi LED set ACK")); - Tuya.wifi_state = TuyaGetTuyaWifiState(); - break; - - case TUYA_CMD_MCU_CONF: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX MCU configuration Mode=%d"), Tuya.buffer[5]); - - if (Tuya.buffer[5] == 2) { - uint8_t led1_gpio = Tuya.buffer[6]; - uint8_t key1_gpio = Tuya.buffer[7]; - bool key1_set = false; - bool led1_set = false; - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - if (Settings.my_gp.io[i] == GPIO_LED1) led1_set = true; - else if (Settings.my_gp.io[i] == GPIO_KEY1) key1_set = true; - } - if (!Settings.my_gp.io[led1_gpio] && !led1_set) { - Settings.my_gp.io[led1_gpio] = GPIO_LED1; - restart_flag = 2; - } - if (!Settings.my_gp.io[key1_gpio] && !key1_set) { - Settings.my_gp.io[key1_gpio] = GPIO_KEY1; - restart_flag = 2; - } - } - TuyaRequestState(); - break; - - default: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX unknown command")); - } -} - - - - - -bool TuyaModuleSelected(void) -{ - if (!(pin[GPIO_TUYA_RX] < 99) || !(pin[GPIO_TUYA_TX] < 99)) { - pin[GPIO_TUYA_TX] = 1; - pin[GPIO_TUYA_RX] = 3; - Settings.my_gp.io[1] = GPIO_TUYA_TX; - Settings.my_gp.io[3] = GPIO_TUYA_RX; - restart_flag = 2; - } - - if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER) == 0 && TUYA_DIMMER_ID > 0) { - TuyaAddMcuFunc(TUYA_MCU_FUNC_DIMMER, TUYA_DIMMER_ID); - } - - bool relaySet = false; - - 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; - } - - if (TuyaGetDpId(TUYA_MCU_FUNC_LOWPOWER_MODE) != 0) { - Tuya.low_power_mode = true; - Settings.flag3.fast_power_cycle_disable = true; - } - - UpdateDevices(); - return true; -} - -void TuyaInit(void) -{ - Tuya.buffer = (char*)(malloc(TUYA_BUFFER_SIZE)); - if (Tuya.buffer != nullptr) { - TuyaSerial = new TasmotaSerial(pin[GPIO_TUYA_RX], pin[GPIO_TUYA_TX], 2); - if (TuyaSerial->begin(9600)) { - if (TuyaSerial->hardwareSerial()) { ClaimSerial(); } - - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Request MCU configuration")); - - TuyaSendCmd(TUYA_CMD_QUERY_PRODUCT); - } - } - Tuya.heartbeat_timer = 0; -} - -void TuyaSerialInput(void) -{ - while (TuyaSerial->available()) { - yield(); - uint8_t serial_in_byte = TuyaSerial->read(); - - if (serial_in_byte == 0x55) { - Tuya.cmd_status = 1; - Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; - Tuya.cmd_checksum += serial_in_byte; - } - else if (Tuya.cmd_status == 1 && serial_in_byte == 0xAA) { - Tuya.cmd_status = 2; - - Tuya.byte_counter = 0; - Tuya.buffer[Tuya.byte_counter++] = 0x55; - Tuya.buffer[Tuya.byte_counter++] = 0xAA; - Tuya.cmd_checksum = 0xFF; - } - else if (Tuya.cmd_status == 2) { - if (Tuya.byte_counter == 5) { - Tuya.cmd_status = 3; - Tuya.data_len = serial_in_byte; - } - Tuya.cmd_checksum += serial_in_byte; - Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; - } - else if ((Tuya.cmd_status == 3) && (Tuya.byte_counter == (6 + Tuya.data_len)) && (Tuya.cmd_checksum == serial_in_byte)) { - Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; - - char hex_char[(Tuya.byte_counter * 2) + 2]; - uint16_t len = Tuya.buffer[4] << 8 | Tuya.buffer[5]; - Response_P(PSTR("{\"" D_JSON_TUYA_MCU_RECEIVED "\":{\"Data\":\"%s\",\"Cmnd\":%d"), ToHex_P((unsigned char*)Tuya.buffer, Tuya.byte_counter, hex_char, sizeof(hex_char)), Tuya.buffer[3]); - - if (len > 0) { - ResponseAppend_P(PSTR(",\"CmndData\":\"%s\""), ToHex_P((unsigned char*)&Tuya.buffer[6], len, hex_char, sizeof(hex_char))); - if (TUYA_CMD_STATE == Tuya.buffer[3]) { - - - uint8_t dpidStart = 6; - while (dpidStart + 4 < Tuya.byte_counter) { - uint8_t dpId = Tuya.buffer[dpidStart]; - uint8_t dpDataType = Tuya.buffer[dpidStart + 1]; - uint16_t dpDataLen = Tuya.buffer[dpidStart + 2] << 8 | Tuya.buffer[dpidStart + 3]; - const unsigned char *dpData = (unsigned char*)&Tuya.buffer[dpidStart + 4]; - const char *dpHexData = ToHex_P(dpData, dpDataLen, hex_char, sizeof(hex_char)); - - if (TUYA_CMD_STATE == Tuya.buffer[3]) { - ResponseAppend_P(PSTR(",\"DpType%uId%u\":"), dpDataType, dpId); - if (TUYA_TYPE_BOOL == dpDataType && dpDataLen == 1) { - ResponseAppend_P(PSTR("%u"), dpData[0]); - } else if (TUYA_TYPE_VALUE == dpDataType && dpDataLen == 4) { - uint32_t dpValue = (uint32_t)dpData[0] << 24 | (uint32_t)dpData[1] << 16 | (uint32_t)dpData[2] << 8 | (uint32_t)dpData[3] << 0; - ResponseAppend_P(PSTR("%u"), dpValue); - } else if (TUYA_TYPE_STRING == dpDataType) { - ResponseAppend_P(PSTR("\"%.*s\""), dpDataLen, dpData); - } else if (TUYA_TYPE_ENUM == dpDataType && dpDataLen == 1) { - ResponseAppend_P(PSTR("%u"), dpData[0]); - } else { - ResponseAppend_P(PSTR("\"0x%s\""), dpHexData); - } - } - - ResponseAppend_P(PSTR(",\"%d\":{\"DpId\":%d,\"DpIdType\":%d,\"DpIdData\":\"%s\""), dpId, dpId, dpDataType, dpHexData); - if (TUYA_TYPE_STRING == dpDataType) { - ResponseAppend_P(PSTR(",\"Type3Data\":\"%.*s\""), dpDataLen, dpData); - } - ResponseAppend_P(PSTR("}")); - dpidStart += dpDataLen + 4; - } - } - } - - ResponseAppend_P(PSTR("}}")); - - if (Settings.flag3.tuya_serial_mqtt_publish) { - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_TUYA_MCU_RECEIVED)); - } else { - AddLog_P(LOG_LEVEL_DEBUG, mqtt_data); - } - XdrvRulesProcess(); - - if (!Tuya.low_power_mode) { - TuyaNormalPowerModePacketProcess(); - } else { - TuyaLowPowerModePacketProcess(); - } - - Tuya.byte_counter = 0; - Tuya.cmd_status = 0; - Tuya.cmd_checksum = 0; - Tuya.data_len = 0; - } - else if (Tuya.byte_counter < TUYA_BUFFER_SIZE -1) { - Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; - Tuya.cmd_checksum += serial_in_byte; - } else { - Tuya.byte_counter = 0; - Tuya.cmd_status = 0; - Tuya.cmd_checksum = 0; - Tuya.data_len = 0; - } - } -} - -bool TuyaButtonPressed(void) -{ - if (!XdrvMailbox.index && ((PRESSED == XdrvMailbox.payload) && (NOT_PRESSED == Button.last_state[XdrvMailbox.index]))) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Reset GPIO triggered")); - TuyaResetWifi(); - return true; - } - return false; -} - -uint8_t TuyaGetTuyaWifiState(void) { - - uint8_t wifi_state = 0x02; - switch(WifiState()){ - case WIFI_MANAGER: - wifi_state = 0x01; - break; - case WIFI_RESTART: - wifi_state = 0x03; - break; - } - - if (MqttIsConnected()) { - wifi_state = 0x04; - } - - return wifi_state; -} - -void TuyaSetWifiLed(void) -{ - Tuya.wifi_state = TuyaGetTuyaWifiState(); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Set WiFi LED %d (%d)"), Tuya.wifi_state, WifiState()); - - if (Tuya.low_power_mode) { - TuyaSendCmd(TUYA_LOW_POWER_CMD_WIFI_STATE, &Tuya.wifi_state, 1); - } else { - TuyaSendCmd(TUYA_CMD_WIFI_STATE, &Tuya.wifi_state, 1); - } -} - -#ifdef USE_ENERGY_SENSOR - - - - -bool Xnrg16(uint8_t function) -{ - bool result = false; - - if (TUYA_DIMMER == my_module_type) { - if (FUNC_PRE_INIT == function) { - if (TuyaGetDpId(TUYA_MCU_FUNC_POWER) != 0) { - if (TuyaGetDpId(TUYA_MCU_FUNC_CURRENT) == 0) { - Energy.current_available = false; - } - if (TuyaGetDpId(TUYA_MCU_FUNC_VOLTAGE) == 0) { - Energy.voltage_available = false; - } - energy_flg = XNRG_16; - } - } - } - return result; -} -#endif - - - - - -bool Xdrv16(uint8_t function) -{ - bool result = false; - - if (TUYA_DIMMER == my_module_type) { - switch (function) { - case FUNC_LOOP: - if (TuyaSerial) { TuyaSerialInput(); } - break; - case FUNC_MODULE_INIT: - result = TuyaModuleSelected(); - break; - case FUNC_PRE_INIT: - TuyaInit(); - break; - case FUNC_SET_DEVICE_POWER: - result = TuyaSetPower(); - break; - case FUNC_BUTTON_PRESSED: - result = TuyaButtonPressed(); - break; - case FUNC_EVERY_SECOND: - if (TuyaSerial && Tuya.wifi_state != TuyaGetTuyaWifiState()) { TuyaSetWifiLed(); } - if (!Tuya.low_power_mode) { - Tuya.heartbeat_timer++; - if (Tuya.heartbeat_timer > 10) { - Tuya.heartbeat_timer = 0; - TuyaSendCmd(TUYA_CMD_HEARTBEAT); - } - } else { - TuyaSendLowPowerSuccessIfNeeded(); - } - break; - case FUNC_SET_CHANNELS: - result = TuyaSetChannels(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kTuyaCommand, TuyaCommand); - break; - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_17_rcswitch.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_17_rcswitch.ino" -#ifdef USE_RC_SWITCH - - - - -#define XDRV_17 17 - -#define D_JSON_RF_PROTOCOL "Protocol" -#define D_JSON_RF_BITS "Bits" -#define D_JSON_RF_DATA "Data" - -#define D_CMND_RFSEND "RFSend" -#define D_JSON_RF_PULSE "Pulse" -#define D_JSON_RF_REPEAT "Repeat" - -const char kRfSendCommands[] PROGMEM = "|" - D_CMND_RFSEND; - -void (* const RfSendCommand[])(void) PROGMEM = { - &CmndRfSend }; - -#include - -RCSwitch mySwitch = RCSwitch(); - -#define RF_TIME_AVOID_DUPLICATE 1000 - -uint32_t rf_lasttime = 0; - -void RfReceiveCheck(void) -{ - if (mySwitch.available()) { - - unsigned long data = mySwitch.getReceivedValue(); - unsigned int bits = mySwitch.getReceivedBitlength(); - int protocol = mySwitch.getReceivedProtocol(); - int delay = mySwitch.getReceivedDelay(); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFR: Data 0x%lX (%u), Bits %d, Protocol %d, Delay %d"), data, data, bits, protocol, delay); - - uint32_t now = millis(); - if ((now - rf_lasttime > RF_TIME_AVOID_DUPLICATE) && (data > 0)) { - rf_lasttime = now; - - char stemp[16]; - if (Settings.flag.rf_receive_decimal) { - snprintf_P(stemp, sizeof(stemp), PSTR("%u"), (uint32_t)data); - } else { - snprintf_P(stemp, sizeof(stemp), PSTR("\"0x%lX\""), (uint32_t)data); - } - 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(); -#ifdef USE_DOMOTICZ - DomoticzSensor(DZ_COUNT, data); -#endif - } - mySwitch.resetAvailable(); - } -} - -void RfInit(void) -{ - if (pin[GPIO_RFSEND] < 99) { - mySwitch.enableTransmit(pin[GPIO_RFSEND]); - } - if (pin[GPIO_RFRECV] < 99) { - pinMode( pin[GPIO_RFRECV], INPUT); - mySwitch.enableReceive(pin[GPIO_RFRECV]); - } -} - - - - - -void CmndRfSend(void) -{ - bool error = false; - - if (XdrvMailbox.data_len) { - unsigned long data = 0; - unsigned int bits = 24; - int protocol = 1; - int repeat = 10; - int pulse = 350; - - char dataBufUc[XdrvMailbox.data_len + 1]; - UpperCase(dataBufUc, XdrvMailbox.data); - StaticJsonBuffer<150> jsonBuf; - JsonObject &root = jsonBuf.parseObject(dataBufUc); - if (root.success()) { - - char parm_uc[10]; - data = strtoul(root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_DATA))], nullptr, 0); - bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_BITS))]; - protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_PROTOCOL))]; - repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_REPEAT))]; - pulse = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_PULSE))]; - } else { - - char *p; - uint8_t i = 0; - for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 5; str = strtok_r(nullptr, ", ", &p)) { - switch (i++) { - case 0: - data = strtoul(str, nullptr, 0); - break; - case 1: - bits = atoi(str); - break; - case 2: - protocol = atoi(str); - break; - case 3: - repeat = atoi(str); - break; - case 4: - pulse = atoi(str); - } - } - } - - if (!protocol) { protocol = 1; } - mySwitch.setProtocol(protocol); - if (!pulse) { pulse = 350; } - mySwitch.setPulseLength(pulse); - if (!repeat) { repeat = 10; } - mySwitch.setRepeatTransmit(repeat); - if (!bits) { bits = 24; } - if (data) { - mySwitch.send(data, bits); - ResponseCmndDone(); - } else { - error = true; - } - } else { - error = true; - } - if (error) { - Response_P(PSTR("{\"" D_CMND_RFSEND "\":\"" D_JSON_NO " " D_JSON_RF_DATA ", " D_JSON_RF_BITS ", " D_JSON_RF_PROTOCOL ", " D_JSON_RF_REPEAT " " D_JSON_OR " " D_JSON_RF_PULSE "\"}")); - } -} - - - - - -bool Xdrv17(uint8_t function) -{ - bool result = false; - - if ((pin[GPIO_RFSEND] < 99) || (pin[GPIO_RFRECV] < 99)) { - switch (function) { - case FUNC_EVERY_50_MSECOND: - if (pin[GPIO_RFRECV] < 99) { - RfReceiveCheck(); - } - break; - case FUNC_COMMAND: - if (pin[GPIO_RFSEND] < 99) { - result = DecodeCommand(kRfSendCommands, RfSendCommand); - } - break; - case FUNC_INIT: - RfInit(); - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_18_armtronix_dimmers.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_18_armtronix_dimmers.ino" -#ifdef USE_LIGHT -#ifdef USE_ARMTRONIX_DIMMERS - - - - - - - -#define XDRV_18 18 - -#include - -TasmotaSerial *ArmtronixSerial = nullptr; - -struct ARMTRONIX { - bool ignore_dim = false; - int8_t wifi_state = -2; - int8_t dim_state[2]; - int8_t knob_state[2]; -} Armtronix; - - - - - -bool ArmtronixSetChannels(void) -{ - LightSerial2Duty(((uint8_t*)XdrvMailbox.data)[0], ((uint8_t*)XdrvMailbox.data)[1]); - return true; -} - -void LightSerial2Duty(uint8_t duty1, uint8_t duty2) -{ - if (ArmtronixSerial && !Armtronix.ignore_dim) { - duty1 = ((float)duty1)/2.575757; - duty2 = ((float)duty2)/2.575757; - Armtronix.dim_state[0] = duty1; - Armtronix.dim_state[1] = duty2; - ArmtronixSerial->print("Dimmer1:"); - ArmtronixSerial->print(duty1); - ArmtronixSerial->print("\nDimmer2:"); - ArmtronixSerial->println(duty2); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send Serial Packet Dim Values=%d,%d"), Armtronix.dim_state[0],Armtronix.dim_state[1]); - - } else { - Armtronix.ignore_dim = false; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send Dim Level skipped due to already set. Value=%d,%d"), Armtronix.dim_state[0],Armtronix.dim_state[1]); - - } -} - -void ArmtronixRequestState(void) -{ - if (ArmtronixSerial) { - - AddLog_P(LOG_LEVEL_DEBUG, PSTR("ARM: Request MCU state")); - ArmtronixSerial->println("Status"); - - } -} - - - - - -bool ArmtronixModuleSelected(void) -{ - devices_present++; - light_type = LT_SERIAL2; - return true; -} - -void ArmtronixInit(void) -{ - Armtronix.dim_state[0] = -1; - Armtronix.dim_state[1] = -1; - Armtronix.knob_state[0] = -1; - Armtronix.knob_state[1] = -1; - ArmtronixSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); - if (ArmtronixSerial->begin(115200)) { - if (ArmtronixSerial->hardwareSerial()) { ClaimSerial(); } - ArmtronixSerial->println("Status"); - } -} - -void ArmtronixSerialInput(void) -{ - String answer; - int8_t newDimState[2]; - uint8_t temp; - int commaIndex; - char scmnd[20]; - if (ArmtronixSerial->available()) { - yield(); - answer = ArmtronixSerial->readStringUntil('\n'); - if (answer.substring(0,7) == "Status:") { - commaIndex = 6; - for (uint32_t i =0; i<2; i++) { - newDimState[i] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); - if (newDimState[i] != Armtronix.dim_state[i]) { - temp = ((float)newDimState[i])*1.01010101010101; - Armtronix.dim_state[i] = newDimState[i]; - Armtronix.ignore_dim = true; - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_CHANNEL "%d %d"),i+1, temp); - ExecuteCommand(scmnd,SRC_SWITCH); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send CMND_CHANNEL=%s"), scmnd ); - } - commaIndex = answer.indexOf(',',commaIndex+1); - } - Armtronix.knob_state[0] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); - commaIndex = answer.indexOf(',',commaIndex+1); - Armtronix.knob_state[1] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); - } - } -} - -void ArmtronixSetWifiLed(void) -{ - uint8_t wifi_state = 0x02; - - switch (WifiState()) { - case WIFI_MANAGER: - wifi_state = 0x01; - break; - case WIFI_RESTART: - wifi_state = 0x03; - break; - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Set WiFi LED to state %d (%d)"), wifi_state, WifiState()); - - char state = '0' + ((wifi_state & 1) > 0); - ArmtronixSerial->print("Setled:"); - ArmtronixSerial->write(state); - ArmtronixSerial->write(','); - state = '0' + ((wifi_state & 2) > 0); - ArmtronixSerial->write(state); - ArmtronixSerial->write(10); - Armtronix.wifi_state = WifiState(); -} - - - - - -bool Xdrv18(uint8_t function) -{ - bool result = false; - - if (ARMTRONIX_DIMMERS == my_module_type) { - switch (function) { - case FUNC_LOOP: - if (ArmtronixSerial) { ArmtronixSerialInput(); } - break; - case FUNC_MODULE_INIT: - result = ArmtronixModuleSelected(); - break; - case FUNC_INIT: - ArmtronixInit(); - break; - case FUNC_EVERY_SECOND: - if (ArmtronixSerial) { - if (Armtronix.wifi_state!=WifiState()) { ArmtronixSetWifiLed(); } - if (uptime &1) { - ArmtronixSerial->println("Status"); - } - } - break; - case FUNC_SET_CHANNELS: - result = ArmtronixSetChannels(); - break; - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_19_ps16dz_dimmer.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_19_ps16dz_dimmer.ino" -#ifdef USE_LIGHT -#ifdef USE_PS_16_DZ - - - - -#define XDRV_19 19 - -#define PS16DZ_BUFFER_SIZE 80 - -#include - -TasmotaSerial *PS16DZSerial = nullptr; - -struct PS16DZ { - char *rx_buffer = nullptr; - int byte_counter = 0; - uint8_t dimmer = 0; -} Ps16dz; - - - - - -void PS16DZSerialSend(const char *tx_buffer) -{ - - - PS16DZSerial->print(tx_buffer); - PS16DZSerial->write(0x1B); - PS16DZSerial->flush(); -} - -void PS16DZSerialSendOk(void) -{ - char tx_buffer[16]; - snprintf_P(tx_buffer, sizeof(tx_buffer), PSTR("AT+SEND=ok")); - PS16DZSerialSend(tx_buffer); -} - - - - -void PS16DZSerialSendUpdateCommand(void) -{ - uint8_t light_state_dimmer = light_state.getDimmer(); - - light_state_dimmer = (light_state_dimmer < Settings.dimmer_hw_min) ? Settings.dimmer_hw_min : light_state_dimmer; - light_state_dimmer = (light_state_dimmer > Settings.dimmer_hw_max) ? Settings.dimmer_hw_max : light_state_dimmer; - - char tx_buffer[80]; - snprintf_P(tx_buffer, sizeof(tx_buffer), PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"bright\":%d"), - LocalTime(), millis()%1000, power?"on":"off", light_state_dimmer); - - PS16DZSerialSend(tx_buffer); -} - - - - - -void PS16DZSerialInput(void) -{ - char scmnd[20]; - while (PS16DZSerial->available()) { - yield(); - uint8_t serial_in_byte = PS16DZSerial->read(); - if (serial_in_byte != 0x1B) { - if (Ps16dz.byte_counter >= PS16DZ_BUFFER_SIZE - 1) { - memset(Ps16dz.rx_buffer, 0, PS16DZ_BUFFER_SIZE); - Ps16dz.byte_counter = 0; - } - if (Ps16dz.byte_counter || (!Ps16dz.byte_counter && ('A' == serial_in_byte))) { - Ps16dz.rx_buffer[Ps16dz.byte_counter++] = serial_in_byte; - } - } else { - Ps16dz.rx_buffer[Ps16dz.byte_counter++] = 0x00; - - - - - if (!strncmp(Ps16dz.rx_buffer+3, "RESULT", 6)) { - - } - else if (!strncmp(Ps16dz.rx_buffer+3, "UPDATE", 6)) { - - char *end_str; - char *string = Ps16dz.rx_buffer+10; - char *token = strtok_r(string, ",", &end_str); - - bool is_switch_change = false; - bool is_brightness_change = false; - - while (token != nullptr) { - char* end_token; - char* token2 = strtok_r(token, ":", &end_token); - char* token3 = strtok_r(nullptr, ":", &end_token); - - if (!strncmp(token2, "\"switch\"", 8)) { - bool switch_state = !strncmp(token3, "\"on\"", 4) ? true : false; - - - - is_switch_change = (switch_state != power); - if (is_switch_change) { - ExecuteCommandPower(1, switch_state, SRC_SWITCH); - } - } - else if (!strncmp(token2, "\"bright\"", 8)) { - Ps16dz.dimmer = atoi(token3); - - - - is_brightness_change = Ps16dz.dimmer != Settings.light_dimmer; - if (power && (Ps16dz.dimmer > 0) && is_brightness_change) { - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), Ps16dz.dimmer); - ExecuteCommand(scmnd, SRC_SWITCH); - } - } - else if (!strncmp(token2, "\"sequence\"", 10)) { - - - - } - token = strtok_r(nullptr, ",", &end_str); - } - - if (!is_brightness_change) { - - - - PS16DZSerialSendOk(); - } - } - else if (!strncmp(Ps16dz.rx_buffer+3, "SETTING", 7)) { - - - if (!Settings.flag.button_restrict) { - int state = WIFI_MANAGER; - if (!strncmp(Ps16dz.rx_buffer+10, "=exit", 5)) { state = WIFI_RETRY; } - if (state != Settings.sta_config) { - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " %d"), state); - ExecuteCommand(scmnd, SRC_BUTTON); - } - } - } - memset(Ps16dz.rx_buffer, 0, PS16DZ_BUFFER_SIZE); - Ps16dz.byte_counter = 0; - } - } -} - -bool PS16DZSerialSendUpdateCommandIfRequired(void) -{ - if (!PS16DZSerial) { return true; } - - bool is_switch_change = (XdrvMailbox.payload != SRC_SWITCH); - bool is_brightness_change = (light_state.getDimmer() != Ps16dz.dimmer); - - if (is_switch_change || is_brightness_change) { - PS16DZSerialSendUpdateCommand(); - } - - return true; -} - -void PS16DZInit(void) -{ - Ps16dz.rx_buffer = (char*)(malloc(PS16DZ_BUFFER_SIZE)); - if (Ps16dz.rx_buffer != nullptr) { - PS16DZSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); - if (PS16DZSerial->begin(19200)) { - if (PS16DZSerial->hardwareSerial()) { ClaimSerial(); } - } - } -} - -bool PS16DZModuleSelected(void) -{ - devices_present++; - light_type = LT_SERIAL1; - - return true; -} - - - - - -bool Xdrv19(uint8_t function) -{ - bool result = false; - - if (PS_16_DZ == my_module_type) { - switch (function) { - case FUNC_LOOP: - if (PS16DZSerial) { PS16DZSerialInput(); } - break; - case FUNC_SET_DEVICE_POWER: - case FUNC_SET_CHANNELS: - result = PS16DZSerialSendUpdateCommandIfRequired(); - break; - case FUNC_INIT: - PS16DZInit(); - break; - case FUNC_MODULE_INIT: - result = PS16DZModuleSelected(); - break; - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_20_hue.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_20_hue.ino" -#if defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) -# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_20_hue.ino" -#define XDRV_20 20 - -const char HUE_RESPONSE[] PROGMEM = - "HTTP/1.1 200 OK\r\n" - "HOST: 239.255.255.250:1900\r\n" - "CACHE-CONTROL: max-age=100\r\n" - "EXT:\r\n" - "LOCATION: http://%s:80/description.xml\r\n" - "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.24.0\r\n" - "hue-bridgeid: %s\r\n"; -const char HUE_ST1[] PROGMEM = - "ST: upnp:rootdevice\r\n" - "USN: uuid:%s::upnp:rootdevice\r\n" - "\r\n"; -const char HUE_ST2[] PROGMEM = - "ST: uuid:%s\r\n" - "USN: uuid:%s\r\n" - "\r\n"; -const char HUE_ST3[] PROGMEM = - "ST: urn:schemas-upnp-org:device:basic:1\r\n" - "USN: uuid:%s\r\n" - "\r\n"; - -String HueBridgeId(void) -{ - String temp = WiFi.macAddress(); - temp.replace(":", ""); - String bridgeid = temp.substring(0, 6); - bridgeid += "FFFE"; - bridgeid += temp.substring(6); - return bridgeid; -} - -String HueSerialnumber(void) -{ - String serial = WiFi.macAddress(); - serial.replace(":", ""); - serial.toLowerCase(); - return serial; -} - -String HueUuid(void) -{ - String uuid = F("f6543a06-da50-11ba-8d8f-"); - uuid += HueSerialnumber(); - return uuid; -} - -void HueRespondToMSearch(void) -{ - char message[TOPSZ]; - - TickerMSearch.detach(); - if (PortUdp.beginPacket(udp_remote_ip, udp_remote_port)) { - char response[320]; - snprintf_P(response, sizeof(response), HUE_RESPONSE, WiFi.localIP().toString().c_str(), HueBridgeId().c_str()); - int len = strlen(response); - String uuid = HueUuid(); - - snprintf_P(response + len, sizeof(response) - len, HUE_ST1, uuid.c_str()); - PortUdp.write(response); - PortUdp.endPacket(); - - snprintf_P(response + len, sizeof(response) - len, HUE_ST2, uuid.c_str(), uuid.c_str()); - PortUdp.write(response); - PortUdp.endPacket(); - - snprintf_P(response + len, sizeof(response) - len, HUE_ST3, uuid.c_str()); - PortUdp.write(response); - PortUdp.endPacket(); - - snprintf_P(message, sizeof(message), PSTR(D_3_RESPONSE_PACKETS_SENT)); - } else { - snprintf_P(message, sizeof(message), PSTR(D_FAILED_TO_SEND_RESPONSE)); - } - - PrepLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_HUE " %s " D_TO " %s:%d"), - message, udp_remote_ip.toString().c_str(), udp_remote_port); - - udp_response_mutex = false; -} - - - - - -const char HUE_DESCRIPTION_XML[] PROGMEM = - "" - "" - "" - "1" - "0" - "" - - "http://{x1:80/" - "" - "urn:schemas-upnp-org:device:Basic:1" - "Amazon-Echo-HA-Bridge ({x1)" - - "Royal Philips Electronics" - "http://www.philips.com" - "Philips hue Personal Wireless Lighting" - "Philips hue bridge 2012" - "929000226503" - "{x3" - "uuid:{x2" - "" - "\r\n" - "\r\n"; -const char HUE_LIGHTS_STATUS_JSON1_SUFFIX[] PROGMEM = - "%s\"alert\":\"none\"," - "\"effect\":\"none\"," - "\"reachable\":true}"; -const char HUE_LIGHTS_STATUS_JSON2[] PROGMEM = - ",\"type\":\"Extended color light\"," - "\"name\":\"%s\"," - "\"modelid\":\"LCT007\"," - "\"uniqueid\":\"%s\"," - "\"swversion\":\"5.50.1.19085\"}"; -const char HUE_GROUP0_STATUS_JSON[] PROGMEM = - "{\"name\":\"Group 0\"," - "\"lights\":[{l1]," - "\"type\":\"LightGroup\"," - "\"action\":"; - -const char HueConfigResponse_JSON[] PROGMEM = - "{\"name\":\"Philips hue\"," - "\"mac\":\"{ma\"," - "\"dhcp\":true," - "\"ipaddress\":\"{ip\"," - "\"netmask\":\"{ms\"," - "\"gateway\":\"{gw\"," - "\"proxyaddress\":\"none\"," - "\"proxyport\":0," - "\"bridgeid\":\"{br\"," - "\"UTC\":\"{dt\"," - "\"whitelist\":{\"{id\":{" - "\"last use date\":\"{dt\"," - "\"create date\":\"{dt\"," - "\"name\":\"Remote\"}}," - "\"swversion\":\"01041302\"," - "\"apiversion\":\"1.17.0\"," - "\"swupdate\":{\"updatestate\":0,\"url\":\"\",\"text\":\"\",\"notify\": false}," - "\"linkbutton\":false," - "\"portalservices\":false" - "}"; -const char HUE_ERROR_JSON[] PROGMEM = - "[{\"error\":{\"type\":901,\"address\":\"/\",\"description\":\"Internal Error\"}}]"; - - - -String GetHueDeviceId(uint16_t id) -{ - String deviceid = WiFi.macAddress(); - deviceid += F(":00:11-"); - deviceid += String(id); - deviceid.toLowerCase(); - return deviceid; -} - -String GetHueUserId(void) -{ - char userid[7]; - - snprintf_P(userid, sizeof(userid), PSTR("%03x"), ESP_getChipId()); - return String(userid); -} - -void HandleUpnpSetupHue(void) -{ - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_HUE_BRIDGE_SETUP)); - String description_xml = FPSTR(HUE_DESCRIPTION_XML); - description_xml.replace("{x1", WiFi.localIP().toString()); - description_xml.replace("{x2", HueUuid()); - description_xml.replace("{x3", HueSerialnumber()); - WSSend(200, CT_XML, description_xml); -} - -void HueNotImplemented(String *path) -{ - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_API_NOT_IMPLEMENTED " (%s)"), path->c_str()); - - WSSend(200, CT_JSON, "{}"); -} - -void HueConfigResponse(String *response) -{ - *response += FPSTR(HueConfigResponse_JSON); - response->replace("{ma", WiFi.macAddress()); - response->replace("{ip", WiFi.localIP().toString()); - response->replace("{ms", WiFi.subnetMask().toString()); - response->replace("{gw", WiFi.gatewayIP().toString()); - response->replace("{br", HueBridgeId()); - response->replace("{dt", GetDateAndTime(DT_UTC)); - response->replace("{id", GetHueUserId()); -} - -void HueConfig(String *path) -{ - String response = ""; - HueConfigResponse(&response); - WSSend(200, CT_JSON, response); -} - - - -bool g_gotct = false; - - - - -uint16_t prev_hue = 0; -uint8_t prev_sat = 0; -uint8_t prev_bri = 254; -uint16_t prev_ct = 254; -char prev_x_str[24] = "\0"; -char prev_y_str[24] = "\0"; - -uint8_t getLocalLightSubtype(uint8_t device) { - if (light_type) { - if (device >= Light.device) { - if (Settings.flag3.pwm_multi_channels) { - return LST_SINGLE; - } else { - return Light.subtype; - } - } else { - return LST_NONE; - } - } else { - return LST_NONE; - } -} - -void HueLightStatus1(uint8_t device, String *response) -{ - uint16_t ct = 0; - uint8_t color_mode; - String light_status = ""; - uint16_t hue = 0; - uint8_t sat = 0; - uint8_t bri = 254; - uint32_t echo_gen = findEchoGeneration(); - - - uint8_t local_light_subtype = getLocalLightSubtype(device); - - bri = LightGetBri(device); - if (bri > 254) bri = 254; - if (bri < 1) bri = 1; - -#ifdef USE_SHUTTER - if (ShutterState(device)) { - bri = (float)((Settings.shutter_options[device-1] & 1) ? 100 - Settings.shutter_position[device-1] : Settings.shutter_position[device-1]) / 100; - } -#endif - - if (light_type) { - light_state.getHSB(&hue, &sat, nullptr); - - if ((bri > prev_bri ? bri - prev_bri : prev_bri - bri) < 1) - bri = prev_bri; - - if (sat > 254) sat = 254; - if ((sat > prev_sat ? sat - prev_sat : prev_sat - sat) < 1) { - sat = prev_sat; - } else { - prev_x_str[0] = prev_y_str[0] = 0; - } - - hue = changeUIntScale(hue, 0, 360, 0, 65535); - if ((hue > prev_hue ? hue - prev_hue : prev_hue - hue) < 400) { - hue = prev_hue; - } else { - prev_x_str[0] = prev_y_str[0] = 0; - } - - color_mode = light_state.getColorMode(); - ct = light_state.getCT(); - if (LCM_RGB == color_mode) { g_gotct = false; } - if (LCM_CT == color_mode) { g_gotct = true; } - - - - if ((ct > prev_ct ? ct - prev_ct : prev_ct - ct) < 1) - ct = prev_ct; - - - - } - - const size_t buf_size = 256; - char * buf = (char*) malloc(buf_size); - - snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), (power & (1 << (device-1))) ? "true" : "false"); - - if ((1 == echo_gen) || (LST_SINGLE <= local_light_subtype)) { - snprintf_P(buf, buf_size, PSTR("%s\"bri\":%d,"), buf, bri); - } - if (LST_COLDWARM <= local_light_subtype) { - snprintf_P(buf, buf_size, PSTR("%s\"colormode\":\"%s\","), buf, g_gotct ? "ct" : "hs"); - } - if (LST_RGB <= local_light_subtype) { - if (prev_x_str[0] && prev_y_str[0]) { - snprintf_P(buf, buf_size, PSTR("%s\"xy\":[%s,%s],"), buf, prev_x_str, prev_y_str); - } else { - float x, y; - light_state.getXY(&x, &y); - snprintf_P(buf, buf_size, PSTR("%s\"xy\":[%s,%s],"), buf, String(x, 5).c_str(), String(y, 5).c_str()); - } - snprintf_P(buf, buf_size, PSTR("%s\"hue\":%d,\"sat\":%d,"), buf, hue, sat); - } - if (LST_COLDWARM == local_light_subtype || LST_RGBW <= local_light_subtype) { - snprintf_P(buf, buf_size, PSTR("%s\"ct\":%d,"), buf, ct > 0 ? ct : 284); - } - snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON1_SUFFIX, buf); - - *response += buf; - free(buf); -} - - - -bool HueActive(uint8_t device) { - if (device > MAX_FRIENDLYNAMES) { device = MAX_FRIENDLYNAMES; } - return '$' != *SettingsText(SET_FRIENDLYNAME1 +device -1); -} - -void HueLightStatus2(uint8_t device, String *response) -{ - const size_t buf_size = 192; - char * buf = (char*) malloc(buf_size); - const size_t max_name_len = 32; - char fname[max_name_len + 1]; - - strlcpy(fname, SettingsText(device <= MAX_FRIENDLYNAMES ? SET_FRIENDLYNAME1 + device -1 : SET_FRIENDLYNAME1 + MAX_FRIENDLYNAMES -1), max_name_len + 1); - - if (device > MAX_FRIENDLYNAMES) { - uint32_t fname_len = strlen(fname); - if (fname_len > max_name_len - 2) { fname_len = max_name_len - 2; } - fname[fname_len++] = '-'; - if (device - MAX_FRIENDLYNAMES < 10) { - fname[fname_len++] = '0' + device - MAX_FRIENDLYNAMES; - } else { - fname[fname_len++] = 'A' + device - MAX_FRIENDLYNAMES - 10; - } - fname[fname_len] = 0x00; - } - snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON2, fname, GetHueDeviceId(device).c_str()); - *response += buf; - free(buf); -} - - - - - -#ifndef USE_ZIGBEE -uint32_t EncodeLightId(uint8_t relay_id) -#else -uint32_t EncodeLightId(uint8_t relay_id, uint16_t z_shortaddr = 0) -#endif -{ - uint8_t mac[6]; - WiFi.macAddress(mac); - uint32_t id = (mac[3] << 20) | (mac[4] << 12) | (mac[5] << 4); - - if (relay_id >= 32) { - relay_id = 0; - } - if (relay_id > 15) { - id |= (1 << 28); - } - id |= (relay_id & 0xF); -#ifdef USE_ZIGBEE - if ((z_shortaddr) && (!relay_id)) { - - id = (1 << 29) | z_shortaddr; - } -#endif - - return id; -} - - - - - - - -#ifndef USE_ZIGBEE -uint32_t DecodeLightId(uint32_t hue_id) -#else -uint32_t DecodeLightId(uint32_t hue_id, uint16_t * shortaddr = nullptr) -#endif -{ - uint8_t relay_id = hue_id & 0xF; - if (hue_id & (1 << 28)) { - relay_id += 16; - } - if (0 == relay_id) { - relay_id = 32; - } -#ifdef USE_ZIGBEE - if (hue_id & (1 << 29)) { - - if (shortaddr) { *shortaddr = hue_id & 0xFFFF; } - relay_id = 0; - } -#endif - return relay_id; -} - -static const char * FIRST_GEN_UA[] = { - "AEOBC", -}; - - -uint32_t findEchoGeneration(void) { - - String user_agent = Webserver->header("User-Agent"); - uint32_t gen = 2; - - for (uint32_t i = 0; i < sizeof(FIRST_GEN_UA)/sizeof(char*); i++) { - if (user_agent.indexOf(FIRST_GEN_UA[i]) >= 0) { - gen = 1; - break; - } - } - if (0 == user_agent.length()) { - gen = 1; - } - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " User-Agent: %s, gen=%d"), user_agent.c_str(), gen); - - return gen; -} - -void HueGlobalConfig(String *path) { - String response; - - path->remove(0,1); - response = F("{\"lights\":{"); - bool appending = false; - CheckHue(&response, appending); -#ifdef USE_ZIGBEE - ZigbeeCheckHue(&response, appending); -#endif - response += F("},\"groups\":{},\"schedules\":{},\"config\":"); - HueConfigResponse(&response); - response += "}"; - WSSend(200, CT_JSON, response); -} - -void HueAuthentication(String *path) -{ - char response[38]; - - snprintf_P(response, sizeof(response), PSTR("[{\"success\":{\"username\":\"%s\"}}]"), GetHueUserId().c_str()); - WSSend(200, CT_JSON, response); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Authentication Result (%s)"), response); -} - - -void CheckHue(String * response, bool &appending) { - uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present; - for (uint32_t i = 1; i <= maxhue; i++) { - if (HueActive(i)) { - if (appending) { *response += ","; } - *response += "\""; - *response += EncodeLightId(i); - *response += F("\":{\"state\":"); - HueLightStatus1(i, response); - HueLightStatus2(i, response); - appending = true; - } - } -} - -void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { - uint16_t tmp = 0; - uint16_t hue = 0; - uint8_t sat = 0; - uint8_t bri = 254; - uint16_t ct = 0; - bool on = false; - bool resp = false; - bool change = false; - uint8_t local_light_subtype = getLocalLightSubtype(device); - - const size_t buf_size = 100; - char * buf = (char*) malloc(buf_size); - - if (Webserver->args()) { - response = "["; - - StaticJsonBuffer<300> jsonBuffer; - JsonObject &hue_json = jsonBuffer.parseObject(Webserver->arg((Webserver->args())-1)); - if (hue_json.containsKey("on")) { - on = hue_json["on"]; - snprintf_P(buf, buf_size, - PSTR("{\"success\":{\"/lights/%d/state/on\":%s}}"), - device_id, on ? "true" : "false"); - -#ifdef USE_SHUTTER - if (ShutterState(device)) { - if (!change) { - bri = on ? 1.0f : 0.0f; - change = true; - resp = true; - response += buf; - } - } else { -#endif -# 554 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_20_hue.ino" - ExecuteCommandPower(device, (on) ? POWER_ON : POWER_OFF, SRC_HUE); - response += buf; - resp = true; -#ifdef USE_SHUTTER - } -#endif - } - - if (light_type && (local_light_subtype >= LST_SINGLE)) { - if (!Settings.flag3.pwm_multi_channels) { - light_state.getHSB(&hue, &sat, nullptr); - bri = light_state.getBri(); - ct = light_state.getCT(); - uint8_t color_mode = light_state.getColorMode(); - if (LCM_RGB == color_mode) { g_gotct = false; } - if (LCM_CT == color_mode) { g_gotct = true; } - - } else { - bri = LightGetBri(device); - } - } - prev_x_str[0] = prev_y_str[0] = 0; - - if (hue_json.containsKey("bri")) { - bri = hue_json["bri"]; - prev_bri = bri; - if (resp) { response += ","; } - snprintf_P(buf, buf_size, - PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), - device_id, "bri", bri); - response += buf; - if (LST_SINGLE <= Light.subtype) { - - if (254 <= bri) { bri = 255; } - change = true; - } - resp = true; - } - - - if (hue_json.containsKey("xy")) { - float x = hue_json["xy"][0]; - float y = hue_json["xy"][1]; - const String &x_str = hue_json["xy"][0]; - const String &y_str = hue_json["xy"][1]; - x_str.toCharArray(prev_x_str, sizeof(prev_x_str)); - y_str.toCharArray(prev_y_str, sizeof(prev_y_str)); - uint8_t rr,gg,bb; - LightStateClass::XyToRgb(x, y, &rr, &gg, &bb); - LightStateClass::RgbToHsb(rr, gg, bb, &hue, &sat, nullptr); - prev_hue = changeUIntScale(hue, 0, 360, 0, 65535); - prev_sat = (sat > 254 ? 254 : sat); - - if (resp) { response += ","; } - snprintf_P(buf, buf_size, - PSTR("{\"success\":{\"/lights/%d/state/xy\":[%s,%s]}}"), - device_id, prev_x_str, prev_y_str); - response += buf; - g_gotct = false; - resp = true; - change = true; - } - if (hue_json.containsKey("hue")) { - hue = hue_json["hue"]; - prev_hue = hue; - if (resp) { response += ","; } - snprintf_P(buf, buf_size, - PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), - device_id, "hue", hue); - response += buf; - if (LST_RGB <= Light.subtype) { - - hue = changeUIntScale(hue, 0, 65535, 0, 360); - g_gotct = false; - change = true; - } - resp = true; - } - if (hue_json.containsKey("sat")) { - sat = hue_json["sat"]; - prev_sat = sat; - if (resp) { response += ","; } - snprintf_P(buf, buf_size, - PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), - device_id, "sat", sat); - response += buf; - if (LST_RGB <= Light.subtype) { - - if (254 <= sat) { sat = 255; } - g_gotct = false; - change = true; - } - resp = true; - } - if (hue_json.containsKey("ct")) { - ct = hue_json["ct"]; - prev_ct = ct; - if (resp) { response += ","; } - snprintf_P(buf, buf_size, - PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), - device_id, "ct", ct); - response += buf; - if ((LST_COLDWARM == Light.subtype) || (LST_RGBW <= Light.subtype)) { - g_gotct = true; - change = true; - } - resp = true; - } - if (change) { -#ifdef USE_SHUTTER - if (ShutterState(device)) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Settings.shutter_invert: %d"), Settings.shutter_options[device-1] & 1); - ShutterSetPosition(device, bri * 100.0f ); - } else -#endif - if (light_type && (local_light_subtype > LST_NONE)) { - if (!Settings.flag3.pwm_multi_channels) { - if (g_gotct) { - light_controller.changeCTB(ct, bri); - } else { - light_controller.changeHSB(hue, sat, bri); - } - LightPreparePower(); - } else { - LightSetBri(device, bri); - } - if (LST_COLDWARM <= local_light_subtype) { - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_COLOR)); - } else { - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_DIMMER)); - } - XdrvRulesProcess(); - } - change = false; - } - response += "]"; - if (2 == response.length()) { - response = FPSTR(HUE_ERROR_JSON); - } - } - else { - response = FPSTR(HUE_ERROR_JSON); - } - free(buf); -} - -void HueLights(String *path) -{ - - - - String response; - int code = 200; - uint8_t device = 1; - uint32_t device_id; - uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present; - - path->remove(0,path->indexOf(F("/lights"))); - if (path->endsWith(F("/lights"))) { - response = "{"; - bool appending = false; - CheckHue(&response, appending); -#ifdef USE_ZIGBEE - ZigbeeCheckHue(&response, appending); -#endif -#ifdef USE_SCRIPT_HUE - Script_Check_Hue(&response); -#endif - response += "}"; - } - else if (path->endsWith(F("/state"))) { - path->remove(0,8); - path->remove(path->indexOf(F("/state"))); - device_id = atoi(path->c_str()); - device = DecodeLightId(device_id); -#ifdef USE_ZIGBEE - uint16_t shortaddr; - device = DecodeLightId(device_id, &shortaddr); - if (shortaddr) { - return ZigbeeHandleHue(shortaddr, device_id, response); - } -#endif - -#ifdef USE_SCRIPT_HUE - if (device > devices_present) { - return Script_Handle_Hue(path); - } -#endif - if ((device >= 1) || (device <= maxhue)) { - HueLightsCommand(device, device_id, response); - } - - } - else if(path->indexOf(F("/lights/")) >= 0) { - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("/lights path=%s"), path->c_str()); - path->remove(0,8); - device_id = atoi(path->c_str()); - device = DecodeLightId(device_id); -#ifdef USE_ZIGBEE - uint16_t shortaddr; - device = DecodeLightId(device_id, &shortaddr); - if (shortaddr) { - ZigbeeHueStatus(&response, shortaddr); - goto exit; - } -#endif - -#ifdef USE_SCRIPT_HUE - if (device > devices_present) { - Script_HueStatus(&response, device-devices_present - 1); - goto exit; - } -#endif - - if ((device < 1) || (device > maxhue)) { - device = 1; - } - response += F("{\"state\":"); - HueLightStatus1(device, &response); - HueLightStatus2(device, &response); - } - else { - response = "{}"; - code = 406; - } - exit: - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str()); - WSSend(code, CT_JSON, response); -} - -void HueGroups(String *path) -{ - - - - String response = "{}"; - uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present; - - - if (path->endsWith("/0")) { - response = FPSTR(HUE_GROUP0_STATUS_JSON); - String lights = F("\"1\""); - for (uint32_t i = 2; i <= maxhue; i++) { - lights += ",\""; - lights += EncodeLightId(i); - lights += "\""; - } - -#ifdef USE_ZIGBEE - ZigbeeHueGroups(&response); -#endif - response.replace("{l1", lights); - HueLightStatus1(1, &response); - response += F("}"); - } - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " HueGroups Result (%s)"), path->c_str()); - WSSend(200, CT_JSON, response); -} - -void HandleHueApi(String *path) -{ -# 828 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_20_hue.ino" - uint8_t args = 0; - - path->remove(0, 4); - uint16_t apilen = path->length(); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_API " (%s)"), path->c_str()); - for (args = 0; args < Webserver->args(); args++) { - String json = Webserver->arg(args); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_POST_ARGS " (%s)"), json.c_str()); - } - - if (path->endsWith(F("/invalid/"))) {} - else if (!apilen) HueAuthentication(path); - else if (path->endsWith(F("/"))) HueAuthentication(path); - else if (path->endsWith(F("/config"))) HueConfig(path); - else if (path->indexOf(F("/lights")) >= 0) HueLights(path); - else if (path->indexOf(F("/groups")) >= 0) HueGroups(path); - else if (path->endsWith(F("/schedules"))) HueNotImplemented(path); - else if (path->endsWith(F("/sensors"))) HueNotImplemented(path); - else if (path->endsWith(F("/scenes"))) HueNotImplemented(path); - else if (path->endsWith(F("/rules"))) HueNotImplemented(path); - else if (path->endsWith(F("/resourcelinks"))) HueNotImplemented(path); - else HueGlobalConfig(path); -} - - - - - -bool Xdrv20(uint8_t function) -{ - bool result = false; - -#if defined(USE_SCRIPT_HUE) || defined(USE_ZIGBEE) - if ((EMUL_HUE == Settings.flag2.emulation)) { -#else - if (devices_present && (EMUL_HUE == Settings.flag2.emulation)) { -#endif - switch (function) { - case FUNC_WEB_ADD_HANDLER: - Webserver->on(F("/description.xml"), HandleUpnpSetupHue); - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_21_wemo.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_21_wemo.ino" -#if defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined (USE_EMULATION_WEMO) - - - - -#define XDRV_21 21 - -const char WEMO_MSEARCH[] PROGMEM = - "HTTP/1.1 200 OK\r\n" - "CACHE-CONTROL: max-age=86400\r\n" - "DATE: Fri, 15 Apr 2016 04:56:29 GMT\r\n" - "EXT:\r\n" - "LOCATION: http://%s:80/setup.xml\r\n" - "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" - "01-NLS: b9200ebb-736d-4b93-bf03-835149d13983\r\n" - "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n" - "ST: %s\r\n" - "USN: uuid:%s::%s\r\n" - "X-User-Agent: redsonic\r\n" - "\r\n"; - -String WemoSerialnumber(void) -{ - char serial[16]; - - snprintf_P(serial, sizeof(serial), PSTR("201612K%08X"), ESP_getChipId()); - return String(serial); -} - -String WemoUuid(void) -{ - char uuid[27]; - - snprintf_P(uuid, sizeof(uuid), PSTR("Socket-1_0-%s"), WemoSerialnumber().c_str()); - return String(uuid); -} - -void WemoRespondToMSearch(int echo_type) -{ - char message[TOPSZ]; - - TickerMSearch.detach(); - if (PortUdp.beginPacket(udp_remote_ip, udp_remote_port)) { - char type[24]; - if (1 == echo_type) { - strcpy_P(type, URN_BELKIN_DEVICE_CAP); - } else { - strcpy_P(type, UPNP_ROOTDEVICE); - } - char response[400]; - snprintf_P(response, sizeof(response), WEMO_MSEARCH, WiFi.localIP().toString().c_str(), type, WemoUuid().c_str(), type); - PortUdp.write(response); - PortUdp.endPacket(); - snprintf_P(message, sizeof(message), PSTR(D_RESPONSE_SENT)); - } else { - snprintf_P(message, sizeof(message), PSTR(D_FAILED_TO_SEND_RESPONSE)); - } - - PrepLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_WEMO " " D_JSON_TYPE " %d, %s " D_TO " %s:%d"), - echo_type, message, udp_remote_ip.toString().c_str(), udp_remote_port); - - udp_response_mutex = false; -} - - - - - -const char WEMO_EVENTSERVICE_XML[] PROGMEM = - "" - "" - "" - "SetBinaryState" - "" - "" - "" - "BinaryState" - "BinaryState" - "in" - "" - "" - "" - "" - "GetBinaryState" - "" - "" - "" - "BinaryState" - "BinaryState" - "out" - "" - "" - "" - "" - "" - "" - "BinaryState" - "bool" - "0" - "" - "" - "level" - "string" - "0" - "" - "" - "\r\n\r\n"; - -const char WEMO_METASERVICE_XML[] PROGMEM = - "" - "" - "1" - "0" - "" - "" - "" - "GetMetaInfo" - "" - "" - "GetMetaInfo" - "MetaInfo" - "in" - "" - "" - "" - "" - "" - "MetaInfo" - "string" - "0" - "" - "" - "\r\n\r\n"; - -const char WEMO_RESPONSE_STATE_SOAP[] PROGMEM = - "" - "" - "" - "%d" - "" - "" - "\r\n"; - -const char WEMO_SETUP_XML[] PROGMEM = - "" - "" - "" - "urn:Belkin:device:controllee:1" - "{x1" - "Belkin International Inc." - "Socket" - "3.1415" - "uuid:{x2" - "{x3" - "0" - "" - "" - "urn:Belkin:service:basicevent:1" - "urn:Belkin:serviceId:basicevent1" - "/upnp/control/basicevent1" - "/upnp/event/basicevent1" - "/eventservice.xml" - "" - "" - "urn:Belkin:service:metainfo:1" - "urn:Belkin:serviceId:metainfo1" - "/upnp/control/metainfo1" - "/upnp/event/metainfo1" - "/metainfoservice.xml" - "" - "" - "" - "\r\n"; - - - -void HandleUpnpEvent(void) -{ - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_WEMO_BASIC_EVENT)); - - char event[500]; - strlcpy(event, Webserver->arg(0).c_str(), sizeof(event)); - - - - - char state = 'G'; - if (strstr_P(event, PSTR("SetBinaryState")) != nullptr) { - state = 'S'; - uint8_t power = POWER_TOGGLE; - if (strstr_P(event, PSTR("State>10on("/upnp/control/basicevent1", HTTP_POST, HandleUpnpEvent); - Webserver->on("/eventservice.xml", HandleUpnpService); - Webserver->on("/metainfoservice.xml", HandleUpnpMetaService); - Webserver->on("/setup.xml", HandleUpnpSetupWemo); - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_22_sonoff_ifan.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_22_sonoff_ifan.ino" -#ifdef USE_SONOFF_IFAN - - - - -#define XDRV_22 22 - -const uint8_t MAX_FAN_SPEED = 4; - -const uint8_t kIFan02Speed[MAX_FAN_SPEED] = { 0x00, 0x01, 0x03, 0x05 }; -const uint8_t kIFan03Speed[MAX_FAN_SPEED +2] = { 0x00, 0x01, 0x03, 0x04, 0x05, 0x06 }; -const uint8_t kIFan03Sequence[MAX_FAN_SPEED][MAX_FAN_SPEED] = {{0, 2, 2, 2}, {0, 1, 2, 4}, {1, 1, 2, 5}, {4, 4, 5, 3}}; - -const char kSonoffIfanCommands[] PROGMEM = "|" - D_CMND_FANSPEED; - -void (* const SonoffIfanCommand[])(void) PROGMEM = { - &CmndFanspeed }; - -uint8_t ifan_fanspeed_timer = 0; -uint8_t ifan_fanspeed_goal = 0; -bool ifan_receive_flag = false; -bool ifan_restart_flag = true; - - - -bool IsModuleIfan(void) -{ - return ((SONOFF_IFAN02 == my_module_type) || (SONOFF_IFAN03 == my_module_type)); -} - -uint8_t MaxFanspeed(void) -{ - return MAX_FAN_SPEED; -} - -uint8_t GetFanspeed(void) -{ - if (ifan_fanspeed_timer) { - return ifan_fanspeed_goal; - } else { - - - - - - - uint8_t fanspeed = (uint8_t)(power &0xF) >> 1; - if (fanspeed) { fanspeed = (fanspeed >> 1) +1; } - return fanspeed; - } -} - - - -void SonoffIFanSetFanspeed(uint8_t fanspeed, bool sequence) -{ - ifan_fanspeed_timer = 0; - ifan_fanspeed_goal = fanspeed; - - uint8_t fanspeed_now = GetFanspeed(); - - if (fanspeed == fanspeed_now) { return; } - - uint8_t fans = kIFan02Speed[fanspeed]; - if (SONOFF_IFAN03 == my_module_type) { - if (sequence) { - fanspeed = kIFan03Sequence[fanspeed_now][ifan_fanspeed_goal]; - if (fanspeed != ifan_fanspeed_goal) { - if (0 == fanspeed_now) { - ifan_fanspeed_timer = 20; - } else { - ifan_fanspeed_timer = 2; - } - } - } - fans = kIFan03Speed[fanspeed]; - } - for (uint32_t i = 2; i < 5; i++) { - uint8_t state = (fans &1) + POWER_OFF_NO_STATE; - ExecuteCommandPower(i, state, SRC_IGNORE); - fans >>= 1; - } - -#ifdef USE_DOMOTICZ - if (sequence) { DomoticzUpdateFanState(); } -#endif -} - - - -void SonoffIfanReceived(void) -{ - char svalue[32]; - - uint8_t mode = serial_in_buffer[3]; - uint8_t action = serial_in_buffer[6]; - - if (4 == mode) { - if (action < 4) { - - - - - if (action != GetFanspeed()) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_FANSPEED " %d"), action); - ExecuteCommand(svalue, SRC_REMOTE); -#ifdef USE_BUZZER - BuzzerEnabledBeep((action) ? action : 1, (action) ? 1 : 4); -#endif - } - } else { - - ExecuteCommandPower(1, POWER_TOGGLE, SRC_REMOTE); - } - } - if (6 == mode) { - - Settings.flag3.buzzer_enable = !Settings.flag3.buzzer_enable; - } - if (7 == mode) { - -#ifdef USE_BUZZER - BuzzerEnabledBeep(4, 1); -#endif - } - - - - serial_in_buffer[5] = 0; - serial_in_buffer[6] = 0; - for (uint32_t i = 0; i < 7; i++) { - if ((i > 1) && (i < 6)) { serial_in_buffer[6] += serial_in_buffer[i]; } - Serial.write(serial_in_buffer[i]); - } -} - -bool SonoffIfanSerialInput(void) -{ - if (SONOFF_IFAN03 == my_module_type) { - if (0xAA == serial_in_byte) { - serial_in_byte_counter = 0; - ifan_receive_flag = true; - } - if (ifan_receive_flag) { - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; - if (serial_in_byte_counter == 8) { -# 176 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_22_sonoff_ifan.ino" - AddLogSerial(LOG_LEVEL_DEBUG); - uint8_t crc = 0; - for (uint32_t i = 2; i < 7; i++) { - crc += serial_in_buffer[i]; - } - if (crc == serial_in_buffer[7]) { - SonoffIfanReceived(); - ifan_receive_flag = false; - return true; - } - } - serial_in_byte = 0; - } - return false; - } -} - - - - - -void CmndFanspeed(void) -{ - if (XdrvMailbox.data_len > 0) { - if ('-' == XdrvMailbox.data[0]) { - XdrvMailbox.payload = (int16_t)GetFanspeed() -1; - if (XdrvMailbox.payload < 0) { XdrvMailbox.payload = MAX_FAN_SPEED -1; } - } - else if ('+' == XdrvMailbox.data[0]) { - XdrvMailbox.payload = GetFanspeed() +1; - if (XdrvMailbox.payload > MAX_FAN_SPEED -1) { XdrvMailbox.payload = 0; } - } - } - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_FAN_SPEED)) { - SonoffIFanSetFanspeed(XdrvMailbox.payload, true); - } - ResponseCmndNumber(GetFanspeed()); -} - - - -bool SonoffIfanInit(void) -{ - if (SONOFF_IFAN03 == my_module_type) { - SetSerial(9600, TS_SERIAL_8N1); - } - return false; -} - -void SonoffIfanUpdate(void) -{ - if (SONOFF_IFAN03 == my_module_type) { - if (ifan_fanspeed_timer) { - ifan_fanspeed_timer--; - if (!ifan_fanspeed_timer) { - SonoffIFanSetFanspeed(ifan_fanspeed_goal, false); - } - } - } - - if (ifan_restart_flag && (4 == uptime) && (SONOFF_IFAN02 == my_module_type)) { - ifan_restart_flag = false; - SetDevicePower(1, SRC_RETRY); - SetDevicePower(power, SRC_RETRY); - } -} - - - - - -bool Xdrv22(uint8_t function) -{ - bool result = false; - - if (IsModuleIfan()) { - switch (function) { - case FUNC_EVERY_250_MSECOND: - SonoffIfanUpdate(); - break; - case FUNC_SERIAL: - result = SonoffIfanSerialInput(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kSonoffIfanCommands, SonoffIfanCommand); - break; - case FUNC_MODULE_INIT: - result = SonoffIfanInit(); - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_0_constants.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_0_constants.ino" -#ifdef USE_ZIGBEE - -#define OCCUPANCY "Occupancy" - -typedef uint64_t Z_IEEEAddress; -typedef uint16_t Z_ShortAddress; - -enum ZnpCommandType { - Z_POLL = 0x00, - Z_SREQ = 0x20, - Z_AREQ = 0x40, - Z_SRSP = 0x60 -}; - -enum ZnpSubsystem { - Z_RPC_Error = 0x00, - Z_SYS = 0x01, - Z_MAC = 0x02, - Z_NWK = 0x03, - Z_AF = 0x04, - Z_ZDO = 0x05, - Z_SAPI = 0x06, - Z_UTIL = 0x07, - Z_DEBUG = 0x08, - Z_APP = 0x09 -}; - - -enum SysCommand { - SYS_RESET = 0x00, - SYS_PING = 0x01, - SYS_VERSION = 0x02, - SYS_SET_EXTADDR = 0x03, - SYS_GET_EXTADDR = 0x04, - SYS_RAM_READ = 0x05, - SYS_RAM_WRITE = 0x06, - SYS_OSAL_NV_ITEM_INIT = 0x07, - SYS_OSAL_NV_READ = 0x08, - SYS_OSAL_NV_WRITE = 0x09, - SYS_OSAL_START_TIMER = 0x0A, - SYS_OSAL_STOP_TIMER = 0x0B, - SYS_RANDOM = 0x0C, - SYS_ADC_READ = 0x0D, - SYS_GPIO = 0x0E, - SYS_STACK_TUNE = 0x0F, - SYS_SET_TIME = 0x10, - SYS_GET_TIME = 0x11, - SYS_OSAL_NV_DELETE = 0x12, - SYS_OSAL_NV_LENGTH = 0x13, - SYS_TEST_RF = 0x40, - SYS_TEST_LOOPBACK = 0x41, - SYS_RESET_IND = 0x80, - SYS_OSAL_TIMER_EXPIRED = 0x81, -}; - -enum SapiCommand { - SAPI_START_REQUEST = 0x00, - SAPI_BIND_DEVICE = 0x01, - SAPI_ALLOW_BIND = 0x02, - SAPI_SEND_DATA_REQUEST = 0x03, - SAPI_READ_CONFIGURATION = 0x04, - SAPI_WRITE_CONFIGURATION = 0x05, - SAPI_GET_DEVICE_INFO = 0x06, - SAPI_FIND_DEVICE_REQUEST = 0x07, - SAPI_PERMIT_JOINING_REQUEST = 0x08, - SAPI_SYSTEM_RESET = 0x09, - SAPI_START_CONFIRM = 0x80, - SAPI_BIND_CONFIRM = 0x81, - SAPI_ALLOW_BIND_CONFIRM = 0x82, - SAPI_SEND_DATA_CONFIRM = 0x83, - SAPI_FIND_DEVICE_CONFIRM = 0x85, - SAPI_RECEIVE_DATA_INDICATION = 0x87, -}; -enum Z_configuration { - CONF_EXTADDR = 0x01, - CONF_BOOTCOUNTER = 0x02, - CONF_STARTUP_OPTION = 0x03, - CONF_START_DELAY = 0x04, - CONF_NIB = 0x21, - CONF_DEVICE_LIST = 0x22, - CONF_ADDRMGR = 0x23, - CONF_POLL_RATE = 0x24, - CONF_QUEUED_POLL_RATE = 0x25, - CONF_RESPONSE_POLL_RATE = 0x26, - CONF_REJOIN_POLL_RATE = 0x27, - CONF_DATA_RETRIES = 0x28, - CONF_POLL_FAILURE_RETRIES = 0x29, - CONF_STACK_PROFILE = 0x2A, - CONF_INDIRECT_MSG_TIMEOUT = 0x2B, - CONF_ROUTE_EXPIRY_TIME = 0x2C, - CONF_EXTENDED_PAN_ID = 0x2D, - CONF_BCAST_RETRIES = 0x2E, - CONF_PASSIVE_ACK_TIMEOUT = 0x2F, - CONF_BCAST_DELIVERY_TIME = 0x30, - CONF_NWK_MODE = 0x31, - CONF_CONCENTRATOR_ENABLE = 0x32, - CONF_CONCENTRATOR_DISCOVERY = 0x33, - CONF_CONCENTRATOR_RADIUS = 0x34, - CONF_CONCENTRATOR_RC = 0x36, - CONF_NWK_MGR_MODE = 0x37, - CONF_SRC_RTG_EXPIRY_TIME = 0x38, - CONF_ROUTE_DISCOVERY_TIME = 0x39, - CONF_NWK_ACTIVE_KEY_INFO = 0x3A, - CONF_NWK_ALTERN_KEY_INFO = 0x3B, - CONF_ROUTER_OFF_ASSOC_CLEANUP = 0x3C, - CONF_NWK_LEAVE_REQ_ALLOWED = 0x3D, - CONF_NWK_CHILD_AGE_ENABLE = 0x3E, - CONF_DEVICE_LIST_KA_TIMEOUT = 0x3F, - CONF_BINDING_TABLE = 0x41, - CONF_GROUP_TABLE = 0x42, - CONF_APS_FRAME_RETRIES = 0x43, - CONF_APS_ACK_WAIT_DURATION = 0x44, - CONF_APS_ACK_WAIT_MULTIPLIER = 0x45, - CONF_BINDING_TIME = 0x46, - CONF_APS_USE_EXT_PANID = 0x47, - CONF_APS_USE_INSECURE_JOIN = 0x48, - CONF_COMMISSIONED_NWK_ADDR = 0x49, - CONF_APS_NONMEMBER_RADIUS = 0x4B, - CONF_APS_LINK_KEY_TABLE = 0x4C, - CONF_APS_DUPREJ_TIMEOUT_INC = 0x4D, - CONF_APS_DUPREJ_TIMEOUT_COUNT = 0x4E, - CONF_APS_DUPREJ_TABLE_SIZE = 0x4F, - CONF_DIAGNOSTIC_STATS = 0x50, - CONF_SECURITY_LEVEL = 0x61, - CONF_PRECFGKEY = 0x62, - CONF_PRECFGKEYS_ENABLE = 0x63, - CONF_SECURITY_MODE = 0x64, - CONF_SECURE_PERMIT_JOIN = 0x65, - CONF_APS_LINK_KEY_TYPE = 0x66, - CONF_APS_ALLOW_R19_SECURITY = 0x67, - CONF_IMPLICIT_CERTIFICATE = 0x69, - CONF_DEVICE_PRIVATE_KEY = 0x6A, - CONF_CA_PUBLIC_KEY = 0x6B, - CONF_KE_MAX_DEVICES = 0x6C, - CONF_USE_DEFAULT_TCLK = 0x6D, - CONF_RNG_COUNTER = 0x6F, - CONF_RANDOM_SEED = 0x70, - CONF_TRUSTCENTER_ADDR = 0x71, - CONF_USERDESC = 0x81, - CONF_NWKKEY = 0x82, - CONF_PANID = 0x83, - CONF_CHANLIST = 0x84, - CONF_LEAVE_CTRL = 0x85, - CONF_SCAN_DURATION = 0x86, - CONF_LOGICAL_TYPE = 0x87, - CONF_NWKMGR_MIN_TX = 0x88, - CONF_NWKMGR_ADDR = 0x89, - CONF_ZDO_DIRECT_CB = 0x8F, - CONF_TCLK_TABLE_START = 0x0101, - ZNP_HAS_CONFIGURED = 0xF00 -}; - - -enum Z_Status { - Z_SUCCESS = 0x00, - Z_FAILURE = 0x01, - Z_INVALIDPARAMETER = 0x02, - Z_MEMERROR = 0x03, - Z_CREATED = 0x09, - Z_BUFFERFULL = 0x11 -}; - -enum Z_App_Profiles { - Z_PROF_IPM = 0x0101, - Z_PROF_HA = 0x0104, - Z_PROF_CBA = 0x0105, - Z_PROF_TA = 0x0107, - Z_PROF_PHHC = 0x0108, - Z_PROF_AMI = 0x0109, -}; - -enum Z_Device_Ids { - Z_DEVID_CONF_TOOL = 0x0005, -# 225 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_0_constants.ino" -}; - - enum Z_AddrMode : uint8_t { - Z_Addr_NotPresent = 0, - Z_Addr_Group = 1, - Z_Addr_ShortAddress = 2, - Z_Addr_IEEEAddress = 3, - Z_Addr_Broadcast = 0xFF -}; - - -enum AfCommand : uint8_t { - AF_REGISTER = 0x00, - AF_DATA_REQUEST = 0x01, - AF_DATA_REQUEST_EXT = 0x02, - AF_DATA_REQUEST_SRC_RTG = 0x03, - AF_INTER_PAN_CTL = 0x10, - AF_DATA_STORE = 0x11, - AF_DATA_RETRIEVE = 0x12, - AF_APSF_CONFIG_SET = 0x13, - AF_DATA_CONFIRM = 0x80, - AF_REFLECT_ERROR = 0x83, - AF_INCOMING_MSG = 0x81, - AF_INCOMING_MSG_EXT = 0x82 -}; - - -enum : uint8_t { - ZDO_NWK_ADDR_REQ = 0x00, - ZDO_IEEE_ADDR_REQ = 0x01, - ZDO_NODE_DESC_REQ = 0x02, - ZDO_POWER_DESC_REQ = 0x03, - ZDO_SIMPLE_DESC_REQ = 0x04, - ZDO_ACTIVE_EP_REQ = 0x05, - ZDO_MATCH_DESC_REQ = 0x06, - ZDO_COMPLEX_DESC_REQ = 0x07, - ZDO_USER_DESC_REQ = 0x08, - ZDO_DEVICE_ANNCE = 0x0A, - ZDO_USER_DESC_SET = 0x0B, - ZDO_SERVER_DISC_REQ = 0x0C, - ZDO_END_DEVICE_BIND_REQ = 0x20, - ZDO_BIND_REQ = 0x21, - ZDO_UNBIND_REQ = 0x22, - ZDO_SET_LINK_KEY = 0x23, - ZDO_REMOVE_LINK_KEY = 0x24, - ZDO_GET_LINK_KEY = 0x25, - ZDO_MGMT_NWK_DISC_REQ = 0x30, - ZDO_MGMT_LQI_REQ = 0x31, - ZDO_MGMT_RTQ_REQ = 0x32, - ZDO_MGMT_BIND_REQ = 0x33, - ZDO_MGMT_LEAVE_REQ = 0x34, - ZDO_MGMT_DIRECT_JOIN_REQ = 0x35, - ZDO_MGMT_PERMIT_JOIN_REQ = 0x36, - ZDO_MGMT_NWK_UPDATE_REQ = 0x37, - ZDO_MSG_CB_REGISTER = 0x3E, - ZDO_MGS_CB_REMOVE = 0x3F, - ZDO_STARTUP_FROM_APP = 0x40, - ZDO_AUTO_FIND_DESTINATION = 0x41, - ZDO_EXT_REMOVE_GROUP = 0x47, - ZDO_EXT_REMOVE_ALL_GROUP = 0x48, - ZDO_EXT_FIND_ALL_GROUPS_ENDPOINT = 0x49, - ZDO_EXT_FIND_GROUP = 0x4A, - ZDO_EXT_ADD_GROUP = 0x4B, - ZDO_EXT_COUNT_ALL_GROUPS = 0x4C, - ZDO_NWK_ADDR_RSP = 0x80, - ZDO_IEEE_ADDR_RSP = 0x81, - ZDO_NODE_DESC_RSP = 0x82, - ZDO_POWER_DESC_RSP = 0x83, - ZDO_SIMPLE_DESC_RSP = 0x84, - ZDO_ACTIVE_EP_RSP = 0x85, - ZDO_MATCH_DESC_RSP = 0x86, - ZDO_COMPLEX_DESC_RSP = 0x87, - ZDO_USER_DESC_RSP = 0x88, - ZDO_USER_DESC_CONF = 0x89, - ZDO_SERVER_DISC_RSP = 0x8A, - ZDO_END_DEVICE_BIND_RSP = 0xA0, - ZDO_BIND_RSP = 0xA1, - ZDO_UNBIND_RSP = 0xA2, - ZDO_MGMT_NWK_DISC_RSP = 0xB0, - ZDO_MGMT_LQI_RSP = 0xB1, - ZDO_MGMT_RTG_RSP = 0xB2, - ZDO_MGMT_BIND_RSP = 0xB3, - ZDO_MGMT_LEAVE_RSP = 0xB4, - ZDO_MGMT_DIRECT_JOIN_RSP = 0xB5, - ZDO_MGMT_PERMIT_JOIN_RSP = 0xB6, - ZDO_STATE_CHANGE_IND = 0xC0, - ZDO_END_DEVICE_ANNCE_IND = 0xC1, - ZDO_MATCH_DESC_RSP_SENT = 0xC2, - ZDO_STATUS_ERROR_RSP = 0xC3, - ZDO_SRC_RTG_IND = 0xC4, - ZDO_LEAVE_IND = 0xC9, - ZDO_TC_DEV_IND = 0xCA, - ZDO_PERMIT_JOIN_IND = 0xCB, - ZDO_MSG_CB_INCOMING = 0xFF -}; - - -enum ZdoStates { - ZDO_DEV_HOLD = 0x00, - ZDO_DEV_INIT = 0x01, - ZDO_DEV_NWK_DISC = 0x02, - ZDO_DEV_NWK_JOINING = 0x03, - ZDO_DEV_NWK_REJOIN = 0x04, - ZDO_DEV_END_DEVICE_UNAUTH = 0x05, - ZDO_DEV_END_DEVICE = 0x06, - ZDO_DEV_ROUTER = 0x07, - ZDO_DEV_COORD_STARTING = 0x08, - ZDO_DEV_ZB_COORD = 0x09, - ZDO_DEV_NWK_ORPHAN = 0x0A, -}; - - -enum Z_Util { - Z_UTIL_GET_DEVICE_INFO = 0x00, - Z_UTIL_GET_NV_INFO = 0x01, - Z_UTIL_SET_PANID = 0x02, - Z_UTIL_SET_CHANNELS = 0x03, - Z_UTIL_SET_SECLEVEL = 0x04, - Z_UTIL_SET_PRECFGKEY = 0x05, - Z_UTIL_CALLBACK_SUB_CMD = 0x06, - Z_UTIL_KEY_EVENT = 0x07, - Z_UTIL_TIME_ALIVE = 0x09, - Z_UTIL_LED_CONTROL = 0x0A, - Z_UTIL_TEST_LOOPBACK = 0x10, - Z_UTIL_DATA_REQ = 0x11, - Z_UTIL_SRC_MATCH_ENABLE = 0x20, - Z_UTIL_SRC_MATCH_ADD_ENTRY = 0x21, - Z_UTIL_SRC_MATCH_DEL_ENTRY = 0x22, - Z_UTIL_SRC_MATCH_CHECK_SRC_ADDR = 0x23, - Z_UTIL_SRC_MATCH_ACK_ALL_PENDING = 0x24, - Z_UTIL_SRC_MATCH_CHECK_ALL_PENDING = 0x25, - Z_UTIL_ADDRMGR_EXT_ADDR_LOOKUP = 0x40, - Z_UTIL_ADDRMGR_NWK_ADDR_LOOKUP = 0x41, - Z_UTIL_APSME_LINK_KEY_DATA_GET = 0x44, - Z_UTIL_APSME_LINK_KEY_NV_ID_GET = 0x45, - Z_UTIL_ASSOC_COUNT = 0x48, - Z_UTIL_ASSOC_FIND_DEVICE = 0x49, - Z_UTIL_ASSOC_GET_WITH_ADDRESS = 0x4A, - Z_UTIL_APSME_REQUEST_KEY_CMD = 0x4B, - Z_UTIL_ZCL_KEY_EST_INIT_EST = 0x80, - Z_UTIL_ZCL_KEY_EST_SIGN = 0x81, - Z_UTIL_UTIL_SYNC_REQ = 0xE0, - Z_UTIL_ZCL_KEY_ESTABLISH_IND = 0xE1 -}; - -enum ZCL_Global_Commands { - ZCL_READ_ATTRIBUTES = 0x00, - ZCL_READ_ATTRIBUTES_RESPONSE = 0x01, - ZCL_WRITE_ATTRIBUTES = 0x02, - ZCL_WRITE_ATTRIBUTES_UNDIVIDED = 0x03, - ZCL_WRITE_ATTRIBUTES_RESPONSE = 0x04, - ZCL_WRITE_ATTRIBUTES_NORESPONSE = 0x05, - ZCL_CONFIGURE_REPORTING = 0x06, - ZCL_CONFIGURE_REPORTING_RESPONSE = 0x07, - ZCL_READ_REPORTING_CONFIGURATION = 0x08, - ZCL_READ_REPORTING_CONFIGURATION_RESPONSE = 0x09, - ZCL_REPORT_ATTRIBUTES = 0x0a, - ZCL_DEFAULT_RESPONSE = 0x0b, - ZCL_DISCOVER_ATTRIBUTES = 0x0c, - ZCL_DISCOVER_ATTRIBUTES_RESPONSE = 0x0d - -}; - -#define ZF(s) static const char ZS_ ## s[] PROGMEM = #s; -#define Z(s) ZS_ ## s - -typedef struct Z_StatusLine { - uint32_t status; - const char * status_msg; -} Z_StatusLine; - - -String getZigbeeStatusMessage(uint8_t status) { - static const char StatusMsg[] PROGMEM = "SUCCESS|FAILURE|NOT_AUTHORIZED|RESERVED_FIELD_NOT_ZERO|MALFORMED_COMMAND|UNSUP_CLUSTER_COMMAND|UNSUP_GENERAL_COMMAND" - "|UNSUP_MANUF_CLUSTER_COMMAND|UNSUP_MANUF_GENERAL_COMMAND|INVALID_FIELD|UNSUPPORTED_ATTRIBUTE|INVALID_VALE|READ_ONLY" - "|INSUFFICIENT_SPACE|DUPLICATE_EXISTS|NOT_FOUND|UNREPORTABLE_ATTRIBUTE|INVALID_DATA_TYPE|INVALID_SELECTOR|WRITE_ONLY" - "|INCONSISTENT_STARTUP_STATE|DEFINED_OUT_OF_BAND|INCONSISTENT|ACTION_DENIED|TIMEOUT|ABORT|INVALID_IMAGE|WAIT_FOR_DATA" - "|NO_IMAGE_AVAILABLE|REQUIRE_MORE_IMAGE|NOTIFICATION_PENDING|HARDWARE_FAILURE|SOFTWARE_FAILURE|CALIBRATION_ERROR|UNSUPPORTED_CLUSTER|NO_ROUTE" - "|CHANNEL_ACCESS_FAILURE|NO_ACK|NO_APP_ACK|NO_ROUTE" - ; - static const uint8_t StatusIdx[] PROGMEM = { 0x00, 0x01, 0x7E, 0x7F, 0x80, 0x81, 0x82, - 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, - 0x98, 0x99, 0x9A, 0xC0, 0xC1, 0xC2, 0xC3, 0xCD, - 0xE1, 0xE9, 0xA7, 0xD0}; - - char msg[32]; - int32_t idx = -1; - for (uint32_t i = 0; i < sizeof(StatusIdx); i++) { - if (status == pgm_read_byte(&StatusIdx[i])) { - idx = i; - break; - } - } - if (idx >= 0) { - GetTextIndexed(msg, sizeof(msg), idx, StatusMsg); - } else { - *msg = 0x00; - } - return String(msg); -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_1_headers.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_1_headers.ino" -#ifdef USE_ZIGBEE - - - -void ZigbeeZCLSend_Raw(uint16_t dtsAddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId); - - - -const JsonVariant &getCaseInsensitive(const JsonObject &json, const char *needle) { - - if ((nullptr == &json) || (nullptr == needle) || (0 == pgm_read_byte(needle))) { - return *(JsonVariant*)nullptr; - } - - for (JsonObject::const_iterator it=json.begin(); it!=json.end(); ++it) { - const char *key = it->key; - const JsonVariant &value = it->value; - - if (0 == strcasecmp_P(key, needle)) { - return value; - } - } - - return *(JsonVariant*)nullptr; -} - - -const char * getCaseInsensitiveConstCharNull(const JsonObject &json, const char *needle) { - const JsonVariant &val = getCaseInsensitive(json, needle); - if (&val) { - const char *val_cs = val.as(); - if (strlen(val_cs)) { - return val_cs; - } - } - return nullptr; -} - - -JsonVariant &startsWithCaseInsensitive(const JsonObject &json, const char *needle) { - - if ((nullptr == &json) || (nullptr == needle) || (0 == pgm_read_byte(needle))) { - return *(JsonVariant*)nullptr; - } - - String needle_s(needle); - needle_s.toLowerCase(); - - for (auto kv : json) { - String key_s(kv.key); - key_s.toLowerCase(); - JsonVariant &value = kv.value; - - if (key_s.startsWith(needle_s)) { - return value; - } - } - - return *(JsonVariant*)nullptr; -} - - -uint32_t parseHex(const char **data, size_t max_len = 8) { - uint32_t ret = 0; - for (uint32_t i = 0; i < max_len; i++) { - int8_t v = hexValue(**data); - if (v < 0) { break; } - ret = (ret << 4) | v; - *data += 1; - } - return ret; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" -#ifdef USE_ZIGBEE - -#include - -#ifndef ZIGBEE_SAVE_DELAY_SECONDS -#define ZIGBEE_SAVE_DELAY_SECONDS 2; -#endif -const uint16_t kZigbeeSaveDelaySeconds = ZIGBEE_SAVE_DELAY_SECONDS; - - - - - -const size_t endpoints_max = 8; - -typedef struct Z_Device { - uint64_t longaddr; - char * manufacturerId; - char * modelId; - char * friendlyName; - uint8_t endpoints[endpoints_max]; - - DynamicJsonBuffer *json_buffer; - JsonObject *json; - - uint16_t shortaddr; - uint8_t seqNumber; - - int8_t bulbtype; - uint8_t power; - uint8_t colormode; - uint8_t dimmer; - uint8_t sat; - uint16_t ct; - uint16_t hue; - uint16_t x, y; -} Z_Device; - - - - - -typedef int32_t (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); - - -typedef enum Z_Def_Category { - Z_CAT_NONE = 0, - Z_CAT_READ_ATTR, - Z_CAT_VIRTUAL_OCCUPANCY, - Z_CAT_REACHABILITY, - Z_CAT_READ_0006, - Z_CAT_READ_0008, - Z_CAT_READ_0102, - Z_CAT_READ_0300, -} Z_Def_Category; - -const uint32_t Z_CAT_REACHABILITY_TIMEOUT = 1000; - -typedef struct Z_Deferred { - - uint32_t timer; - uint16_t shortaddr; - uint16_t groupaddr; - uint16_t cluster; - uint8_t endpoint; - uint8_t category; - uint32_t value; - Z_DeviceTimer func; -} Z_Deferred; -# 99 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" -class Z_Devices { -public: - Z_Devices() {}; - - - - - - - uint16_t isKnownShortAddr(uint16_t shortaddr) const; - uint16_t isKnownLongAddr(uint64_t longaddr) const; - uint16_t isKnownIndex(uint32_t index) const; - uint16_t isKnownFriendlyName(const char * name) const; - - uint64_t getDeviceLongAddr(uint16_t shortaddr) const; - - uint8_t findFirstEndpoint(uint16_t shortaddr) const; - - - - void updateDevice(uint16_t shortaddr, uint64_t longaddr = 0); - - - void addEndpoint(uint16_t shortaddr, uint8_t endpoint); - void clearEndpoints(uint16_t shortaddr); - - void setManufId(uint16_t shortaddr, const char * str); - void setModelId(uint16_t shortaddr, const char * str); - void setFriendlyName(uint16_t shortaddr, const char * str); - const char * getFriendlyName(uint16_t shortaddr) const; - const char * getModelId(uint16_t shortaddr) const; - void setReachable(uint16_t shortaddr, bool reachable); - - - uint8_t getNextSeqNumber(uint16_t shortaddr); - - - String dumpLightState(uint16_t shortaddr) const; - String dump(uint32_t dump_mode, uint16_t status_shortaddr = 0) const; - int32_t deviceRestore(const JsonObject &json); - - - void setHueBulbtype(uint16_t shortaddr, int8_t bulbtype); - int8_t getHueBulbtype(uint16_t shortaddr) const ; - void updateHueState(uint16_t shortaddr, - const bool *power, const uint8_t *colormode, - const uint8_t *dimmer, const uint8_t *sat, - const uint16_t *ct, const uint16_t *hue, - const uint16_t *x, const uint16_t *y, - const bool *reachable); - bool getHueState(uint16_t shortaddr, - bool *power, uint8_t *colormode, - uint8_t *dimmer, uint8_t *sat, - uint16_t *ct, uint16_t *hue, - uint16_t *x, uint16_t *y, - bool *reachable) const ; - - - void resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uint8_t category); - void setTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func); - void runTimer(void); - - - void jsonClear(uint16_t shortaddr); - void jsonAppend(uint16_t shortaddr, const JsonObject &values); - const JsonObject *jsonGet(uint16_t shortaddr); - void jsonPublishFlush(uint16_t shortaddr); - bool jsonIsConflict(uint16_t shortaddr, const JsonObject &values); - void jsonPublishNow(uint16_t shortaddr, JsonObject &values); - - - size_t devicesSize(void) const { - return _devices.size(); - } - const Z_Device &devicesAt(size_t i) const { - return *(_devices.at(i)); - } - - - bool removeDevice(uint16_t shortaddr); - - - void dirty(void); - void clean(void); - void shrinkToFit(uint16_t shortaddr); - - - uint16_t parseDeviceParam(const char * param, bool short_must_be_known = false) const; - -private: - std::vector _devices = {}; - std::vector _deferred = {}; - uint32_t _saveTimer = 0; - uint8_t _seqNumber = 0; - - template < typename T> - static bool findInVector(const std::vector & vecOfElements, const T & element); - - template < typename T> - static int32_t findEndpointInVector(const std::vector & vecOfElements, uint8_t element); - - Z_Device & getShortAddr(uint16_t shortaddr); - const Z_Device & getShortAddrConst(uint16_t shortaddr) const ; - Z_Device & getLongAddr(uint64_t longaddr); - - int32_t findShortAddr(uint16_t shortaddr) const; - int32_t findLongAddr(uint64_t longaddr) const; - int32_t findFriendlyName(const char * name) const; - - - Z_Device & createDeviceEntry(uint16_t shortaddr, uint64_t longaddr = 0); - void freeDeviceEntry(Z_Device *device); - - void setStringAttribute(char*& attr, const char * str); -}; - - - - -Z_Devices zigbee_devices = Z_Devices(); - - -uint64_t localIEEEAddr = 0; - - - - - - -template < typename T> -bool Z_Devices::findInVector(const std::vector & vecOfElements, const T & element) { - - auto it = std::find(vecOfElements.begin(), vecOfElements.end(), element); - - if (it != vecOfElements.end()) { - return true; - } else { - return false; - } -} - -template < typename T> -int32_t Z_Devices::findEndpointInVector(const std::vector & vecOfElements, uint8_t element) { - - - int32_t found = 0; - for (auto &elem : vecOfElements) { - if (elem == element) { return found; } - found++; - } - - return -1; -} - - - - - -Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) { - if (!shortaddr && !longaddr) { return *(Z_Device*) nullptr; } - - Z_Device* device_alloc = new Z_Device{ - longaddr, - nullptr, - nullptr, - nullptr, - { 0, 0, 0, 0, 0, 0, 0, 0 }, - nullptr, nullptr, - shortaddr, - 0, - - -1, - 0x80, - 0, - 0, - 0, - 200, - 0, - 0, 0, - }; - - device_alloc->json_buffer = new DynamicJsonBuffer(16); - _devices.push_back(device_alloc); - dirty(); - return *(_devices.back()); -} - -void Z_Devices::freeDeviceEntry(Z_Device *device) { - if (device->manufacturerId) { free(device->manufacturerId); } - if (device->modelId) { free(device->modelId); } - if (device->friendlyName) { free(device->friendlyName); } - free(device); -} -# 301 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" -int32_t Z_Devices::findShortAddr(uint16_t shortaddr) const { - if (!shortaddr) { return -1; } - int32_t found = 0; - if (shortaddr) { - for (auto &elem : _devices) { - if (elem->shortaddr == shortaddr) { return found; } - found++; - } - } - return -1; -} -# 320 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" -int32_t Z_Devices::findLongAddr(uint64_t longaddr) const { - if (!longaddr) { return -1; } - int32_t found = 0; - if (longaddr) { - for (auto &elem : _devices) { - if (elem->longaddr == longaddr) { return found; } - found++; - } - } - return -1; -} -# 339 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" -int32_t Z_Devices::findFriendlyName(const char * name) const { - if (!name) { return -1; } - size_t name_len = strlen(name); - int32_t found = 0; - if (name_len) { - for (auto &elem : _devices) { - if (elem->friendlyName) { - if (strcmp(elem->friendlyName, name) == 0) { return found; } - } - found++; - } - } - return -1; -} - - -uint16_t Z_Devices::isKnownShortAddr(uint16_t shortaddr) const { - int32_t found = findShortAddr(shortaddr); - if (found >= 0) { - return shortaddr; - } else { - return 0; - } -} - -uint16_t Z_Devices::isKnownLongAddr(uint64_t longaddr) const { - int32_t found = findLongAddr(longaddr); - if (found >= 0) { - const Z_Device & device = devicesAt(found); - return device.shortaddr; - } else { - return 0; - } -} - -uint16_t Z_Devices::isKnownIndex(uint32_t index) const { - if (index < devicesSize()) { - const Z_Device & device = devicesAt(index); - return device.shortaddr; - } else { - return 0; - } -} - -uint16_t Z_Devices::isKnownFriendlyName(const char * name) const { - if ((!name) || (0 == strlen(name))) { return 0xFFFF; } - int32_t found = findFriendlyName(name); - if (found >= 0) { - const Z_Device & device = devicesAt(found); - return device.shortaddr; - } else { - return 0; - } -} - -uint64_t Z_Devices::getDeviceLongAddr(uint16_t shortaddr) const { - const Z_Device & device = getShortAddrConst(shortaddr); - return device.longaddr; -} - - - - -Z_Device & Z_Devices::getShortAddr(uint16_t shortaddr) { - if (!shortaddr) { return *(Z_Device*) nullptr; } - int32_t found = findShortAddr(shortaddr); - if (found >= 0) { - return *(_devices[found]); - } - - return createDeviceEntry(shortaddr, 0); -} - -const Z_Device & Z_Devices::getShortAddrConst(uint16_t shortaddr) const { - if (!shortaddr) { return *(Z_Device*) nullptr; } - int32_t found = findShortAddr(shortaddr); - if (found >= 0) { - return *(_devices[found]); - } - return *((Z_Device*)nullptr); -} - - -Z_Device & Z_Devices::getLongAddr(uint64_t longaddr) { - if (!longaddr) { return *(Z_Device*) nullptr; } - int32_t found = findLongAddr(longaddr); - if (found > 0) { - return *(_devices[found]); - } - return createDeviceEntry(0, longaddr); -} - - -bool Z_Devices::removeDevice(uint16_t shortaddr) { - int32_t found = findShortAddr(shortaddr); - if (found >= 0) { - freeDeviceEntry(_devices.at(found)); - _devices.erase(_devices.begin() + found); - dirty(); - return true; - } - return false; -} - - - - - - -void Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) { - int32_t s_found = findShortAddr(shortaddr); - int32_t l_found = findLongAddr(longaddr); - - if ((s_found >= 0) && (l_found >= 0)) { - if (s_found == l_found) { - } else { - - _devices[l_found]->shortaddr = shortaddr; - - freeDeviceEntry(_devices.at(s_found)); - _devices.erase(_devices.begin() + s_found); - dirty(); - } - } else if (s_found >= 0) { - - - _devices[s_found]->longaddr = longaddr; - dirty(); - } else if (l_found >= 0) { - - _devices[l_found]->shortaddr = shortaddr; - dirty(); - } else { - - if (shortaddr || longaddr) { - createDeviceEntry(shortaddr, longaddr); - } - } -} - - - - -void Z_Devices::clearEndpoints(uint16_t shortaddr) { - if (!shortaddr) { return; } - Z_Device &device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - - for (uint32_t i = 0; i < endpoints_max; i++) { - device.endpoints[i] = 0; - - } -} - - - - -void Z_Devices::addEndpoint(uint16_t shortaddr, uint8_t endpoint) { - if (!shortaddr) { return; } - if (0x00 == endpoint) { return; } - Z_Device &device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - - for (uint32_t i = 0; i < endpoints_max; i++) { - if (endpoint == device.endpoints[i]) { - return; - } - if (0 == device.endpoints[i]) { - device.endpoints[i] = endpoint; - dirty(); - return; - } - } -} - - -uint8_t Z_Devices::findFirstEndpoint(uint16_t shortaddr) const { - int32_t found = findShortAddr(shortaddr); - if (found < 0) return 0; - const Z_Device &device = devicesAt(found); - - return device.endpoints[0]; -} - -void Z_Devices::setStringAttribute(char*& attr, const char * str) { - size_t str_len = str ? strlen(str) : 0; - - if ((nullptr == attr) && (0 == str_len)) { return; } - if (attr) { - - if (strcmp(attr, str) != 0) { - - free(attr); - attr = nullptr; - } else { - return; - } - } - if (str_len) { - attr = (char*) malloc(str_len + 1); - strlcpy(attr, str, str_len + 1); - } - dirty(); -} -# 553 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" -void Z_Devices::setManufId(uint16_t shortaddr, const char * str) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - - setStringAttribute(device.manufacturerId, str); -} - -void Z_Devices::setModelId(uint16_t shortaddr, const char * str) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - - setStringAttribute(device.modelId, str); -} - -void Z_Devices::setFriendlyName(uint16_t shortaddr, const char * str) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - - setStringAttribute(device.friendlyName, str); -} - -const char * Z_Devices::getFriendlyName(uint16_t shortaddr) const { - int32_t found = findShortAddr(shortaddr); - if (found >= 0) { - const Z_Device & device = devicesAt(found); - return device.friendlyName; - } - return nullptr; -} - -const char * Z_Devices::getModelId(uint16_t shortaddr) const { - int32_t found = findShortAddr(shortaddr); - if (found >= 0) { - const Z_Device & device = devicesAt(found); - return device.modelId; - } - return nullptr; -} - -void Z_Devices::setReachable(uint16_t shortaddr, bool reachable) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - bitWrite(device.power, 7, reachable); -} - - -uint8_t Z_Devices::getNextSeqNumber(uint16_t shortaddr) { - int32_t short_found = findShortAddr(shortaddr); - if (short_found >= 0) { - Z_Device &device = getShortAddr(shortaddr); - device.seqNumber += 1; - return device.seqNumber; - } else { - _seqNumber += 1; - return _seqNumber; - } -} - - - -void Z_Devices::setHueBulbtype(uint16_t shortaddr, int8_t bulbtype) { - Z_Device &device = getShortAddr(shortaddr); - if (bulbtype != device.bulbtype) { - device.bulbtype = bulbtype; - dirty(); - } -} -int8_t Z_Devices::getHueBulbtype(uint16_t shortaddr) const { - int32_t found = findShortAddr(shortaddr); - if (found >= 0) { - return _devices[found]->bulbtype; - } else { - return -1; - } -} - - -void Z_Devices::updateHueState(uint16_t shortaddr, - const bool *power, const uint8_t *colormode, - const uint8_t *dimmer, const uint8_t *sat, - const uint16_t *ct, const uint16_t *hue, - const uint16_t *x, const uint16_t *y, - const bool *reachable) { - Z_Device &device = getShortAddr(shortaddr); - if (power) { bitWrite(device.power, 0, *power); } - if (colormode){ device.colormode = *colormode; } - if (dimmer) { device.dimmer = *dimmer; } - if (sat) { device.sat = *sat; } - if (ct) { device.ct = *ct; } - if (hue) { device.hue = *hue; } - if (x) { device.x = *x; } - if (y) { device.y = *y; } - if (reachable){ bitWrite(device.power, 7, *reachable); } -} - - -bool Z_Devices::getHueState(uint16_t shortaddr, - bool *power, uint8_t *colormode, - uint8_t *dimmer, uint8_t *sat, - uint16_t *ct, uint16_t *hue, - uint16_t *x, uint16_t *y, - bool *reachable) const { - int32_t found = findShortAddr(shortaddr); - if (found >= 0) { - const Z_Device &device = *(_devices[found]); - if (power) { *power = bitRead(device.power, 0); } - if (colormode){ *colormode = device.colormode; } - if (dimmer) { *dimmer = device.dimmer; } - if (sat) { *sat = device.sat; } - if (ct) { *ct = device.ct; } - if (hue) { *hue = device.hue; } - if (x) { *x = device.x; } - if (y) { *y = device.y; } - if (reachable){ *reachable = bitRead(device.power, 7); } - return true; - } else { - return false; - } -} - - - -void Z_Devices::resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uint8_t category) { - - for (auto it = _deferred.begin(); it != _deferred.end(); it++) { - - - - if ((it->shortaddr == shortaddr) && (it->groupaddr == groupaddr)) { - if ((0xFF == category) || (it->category == category)) { - _deferred.erase(it--); - } - } - } -} - - -void Z_Devices::setTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func) { - - if (category) { - resetTimersForDevice(shortaddr, groupaddr, category); - } - - - Z_Deferred deferred = { wait_ms + millis(), - shortaddr, - groupaddr, - cluster, - endpoint, - category, - value, - func }; - _deferred.push_back(deferred); -} - - - -void Z_Devices::runTimer(void) { - - for (auto it = _deferred.begin(); it != _deferred.end(); it++) { - Z_Deferred &defer = *it; - - uint32_t timer = defer.timer; - if (TimeReached(timer)) { - (*defer.func)(defer.shortaddr, defer.groupaddr, defer.cluster, defer.endpoint, defer.value); - _deferred.erase(it--); - } - } - - - if ((_saveTimer) && TimeReached(_saveTimer)) { - saveZigbeeDevices(); - _saveTimer = 0; - } -} - - -void Z_Devices::jsonClear(uint16_t shortaddr) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - - device.json = nullptr; - device.json_buffer->clear(); -} - - -void CopyJsonVariant(JsonObject &to, const String &key, const JsonVariant &val) { - - to.remove(key); - - if (val.is()) { - String sval = val.as(); - to.set(key, sval); - } else if (val.is()) { - JsonArray &nested_arr = to.createNestedArray(key); - CopyJsonArray(nested_arr, val.as()); - } else if (val.is()) { - JsonObject &nested_obj = to.createNestedObject(key); - CopyJsonObject(nested_obj, val.as()); - } else { - to.set(key, val); - } -} - - -void CopyJsonArray(JsonArray &to, const JsonArray &arr) { - for (auto v : arr) { - if (v.is()) { - String sval = v.as(); - to.add(sval); - } else if (v.is()) { - } else if (v.is()) { - } else { - to.add(v); - } - } -} - - -void CopyJsonObject(JsonObject &to, const JsonObject &from) { - for (auto kv : from) { - String key_string = kv.key; - JsonVariant &val = kv.value; - - CopyJsonVariant(to, key_string, val); - } -} - - - - -bool Z_Devices::jsonIsConflict(uint16_t shortaddr, const JsonObject &values) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return false; } - if (&values == nullptr) { return false; } - - if (nullptr == device.json) { - return false; - } -# 800 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" - uint16_t group1 = device.json->get(D_CMND_ZIGBEE_GROUP); - uint16_t group2 = values.get(D_CMND_ZIGBEE_GROUP); - if (group1 != group2) { - return true; - } - - - for (auto kv : values) { - String key_string = kv.key; - - if (0 == strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_GROUP))) { - - } else if (0 == strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_ENDPOINT))) { - - if (device.json->containsKey(kv.key)) { - if (kv.value.as() != device.json->get(kv.key)) { - return true; - } - } - } else if (strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_LINKQUALITY))) { - if (device.json->containsKey(kv.key)) { - return true; - } - } - } - return false; -} - -void Z_Devices::jsonAppend(uint16_t shortaddr, const JsonObject &values) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - if (&values == nullptr) { return; } - - if (nullptr == device.json) { - device.json = &(device.json_buffer->createObject()); - } - - char sa[8]; - snprintf_P(sa, sizeof(sa), PSTR("0x%04X"), shortaddr); - device.json->set(F(D_JSON_ZIGBEE_DEVICE), sa); - - const char * fname = zigbee_devices.getFriendlyName(shortaddr); - if (fname) { - device.json->set(F(D_JSON_ZIGBEE_NAME), (char*) fname); - } - - - CopyJsonObject(*device.json, values); -} - -const JsonObject *Z_Devices::jsonGet(uint16_t shortaddr) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return nullptr; } - return device.json; -} - -void Z_Devices::jsonPublishFlush(uint16_t shortaddr) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - JsonObject * json = device.json; - if (json == nullptr) { return; } - - const char * fname = zigbee_devices.getFriendlyName(shortaddr); - bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); - - - if (use_fname) { - json->remove(F(D_JSON_ZIGBEE_NAME)); - } else { - json->remove(F(D_JSON_ZIGBEE_DEVICE)); - } - - String msg = ""; - json->printTo(msg); - zigbee_devices.jsonClear(shortaddr); - - if (use_fname) { - Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"%s\":%s}}"), fname, msg.c_str()); - } else { - Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str()); - } - if (Settings.flag4.zigbee_distinct_topics) { - char subtopic[16]; - snprintf_P(subtopic, sizeof(subtopic), PSTR("%04X/" D_RSLT_SENSOR), shortaddr); - MqttPublishPrefixTopic_P(TELE, subtopic, Settings.flag.mqtt_sensor_retain); - } else { - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); - } - XdrvRulesProcess(); -} - -void Z_Devices::jsonPublishNow(uint16_t shortaddr, JsonObject & values) { - jsonPublishFlush(shortaddr); - jsonAppend(shortaddr, values); - jsonPublishFlush(shortaddr); -} - -void Z_Devices::dirty(void) { - _saveTimer = kZigbeeSaveDelaySeconds * 1000 + millis(); -} -void Z_Devices::clean(void) { - _saveTimer = 0; -} - - - - - - -uint16_t Z_Devices::parseDeviceParam(const char * param, bool short_must_be_known) const { - if (nullptr == param) { return 0; } - size_t param_len = strlen(param); - char dataBuf[param_len + 1]; - strcpy(dataBuf, param); - RemoveSpace(dataBuf); - uint16_t shortaddr = 0; - - if (strlen(dataBuf) < 4) { - - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 99)) { - shortaddr = zigbee_devices.isKnownIndex(XdrvMailbox.payload - 1); - } - } else if ((dataBuf[0] == '0') && (dataBuf[1] == 'x')) { - - if (strlen(dataBuf) < 18) { - - shortaddr = strtoull(dataBuf, nullptr, 0); - if (short_must_be_known) { - shortaddr = zigbee_devices.isKnownShortAddr(shortaddr); - } - - } else { - - uint64_t longaddr = strtoull(dataBuf, nullptr, 0); - shortaddr = zigbee_devices.isKnownLongAddr(longaddr); - } - } else { - - shortaddr = zigbee_devices.isKnownFriendlyName(dataBuf); - } - - return shortaddr; -} - - -String Z_Devices::dumpLightState(uint16_t shortaddr) const { - DynamicJsonBuffer jsonBuffer; - JsonObject& json = jsonBuffer.createObject(); - char hex[8]; - - int32_t found = findShortAddr(shortaddr); - if (found >= 0) { - const Z_Device & device = devicesAt(found); - const char * fname = getFriendlyName(shortaddr); - - bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); - - snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr); - - JsonObject& dev = use_fname ? json.createNestedObject((char*) fname) - : json.createNestedObject(hex); - if (use_fname) { - dev[F(D_JSON_ZIGBEE_DEVICE)] = hex; - } else if (fname) { - dev[F(D_JSON_ZIGBEE_NAME)] = (char*) fname; - } - - - dev[F(D_JSON_ZIGBEE_LIGHT)] = device.bulbtype; - if (0 <= device.bulbtype) { - - dev[F("Power")] = bitRead(device.power, 0); - dev[F("Reachable")] = bitRead(device.power, 7); - if (1 <= device.bulbtype) { - dev[F("Dimmer")] = device.dimmer; - } - if (2 <= device.bulbtype) { - dev[F("Colormode")] = device.colormode; - } - if ((2 == device.bulbtype) || (5 == device.bulbtype)) { - dev[F("CT")] = device.ct; - } - if (3 <= device.bulbtype) { - dev[F("Sat")] = device.sat; - dev[F("Hue")] = device.hue; - dev[F("X")] = device.x; - dev[F("Y")] = device.y; - } - } - } - - String payload = ""; - payload.reserve(200); - json.printTo(payload); - return payload; -} - - - - - -String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const { - DynamicJsonBuffer jsonBuffer; - JsonArray& json = jsonBuffer.createArray(); - JsonArray& devices = json; - - for (std::vector::const_iterator it = _devices.begin(); it != _devices.end(); ++it) { - const Z_Device &device = **it; - uint16_t shortaddr = device.shortaddr; - char hex[22]; - - - if ((status_shortaddr) && (status_shortaddr != shortaddr)) { continue; } - - JsonObject& dev = devices.createNestedObject(); - - snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr); - dev[F(D_JSON_ZIGBEE_DEVICE)] = hex; - - if (device.friendlyName > 0) { - dev[F(D_JSON_ZIGBEE_NAME)] = (char*) device.friendlyName; - } - - if (2 <= dump_mode) { - hex[0] = '0'; - hex[1] = 'x'; - Uint64toHex(device.longaddr, &hex[2], 64); - dev[F("IEEEAddr")] = hex; - if (device.modelId) { - dev[F(D_JSON_MODEL D_JSON_ID)] = device.modelId; - } - if (device.bulbtype >= 0) { - dev[F(D_JSON_ZIGBEE_LIGHT)] = device.bulbtype; - } - if (device.manufacturerId) { - dev[F("Manufacturer")] = device.manufacturerId; - } - JsonArray& dev_endpoints = dev.createNestedArray(F("Endpoints")); - for (uint32_t i = 0; i < endpoints_max; i++) { - uint8_t endpoint = device.endpoints[i]; - if (0x00 == endpoint) { break; } - - snprintf_P(hex, sizeof(hex), PSTR("0x%02X"), endpoint); - dev_endpoints.add(hex); - } - } - } - String payload = ""; - payload.reserve(200); - json.printTo(payload); - return payload; -} -# 1062 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_2_devices.ino" -int32_t Z_Devices::deviceRestore(const JsonObject &json) { - - - uint16_t device = 0x0000; - uint64_t ieeeaddr = 0x0000000000000000LL; - const char * modelid = nullptr; - const char * manufid = nullptr; - const char * friendlyname = nullptr; - int8_t bulbtype = 0xFF; - size_t endpoints_len = 0; - - - const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); - if (nullptr != &val_device) { - device = strToUInt(val_device); - } else { - return -1; - } - - - const JsonVariant &val_ieeeaddr = getCaseInsensitive(json, PSTR("IEEEAddr")); - if (nullptr != &val_ieeeaddr) { - ieeeaddr = strtoull(val_ieeeaddr.as(), nullptr, 0); - } - - - friendlyname = getCaseInsensitiveConstCharNull(json, PSTR("Name")); - - - modelid = getCaseInsensitiveConstCharNull(json, PSTR("ModelId")); - - - manufid = getCaseInsensitiveConstCharNull(json, PSTR("Manufacturer")); - - - const JsonVariant &val_bulbtype = getCaseInsensitive(json, PSTR(D_JSON_ZIGBEE_LIGHT)); - if (nullptr != &val_bulbtype) { bulbtype = strToUInt(val_bulbtype);; } - - - updateDevice(device, ieeeaddr); - if (modelid) { setModelId(device, modelid); } - if (manufid) { setManufId(device, manufid); } - if (friendlyname) { setFriendlyName(device, friendlyname); } - if (&val_bulbtype) { setHueBulbtype(device, bulbtype); } - - - const JsonVariant &val_endpoints = getCaseInsensitive(json, PSTR("Endpoints")); - if ((nullptr != &val_endpoints) && (val_endpoints.is())) { - const JsonArray &arr_ep = val_endpoints.as(); - endpoints_len = arr_ep.size(); - clearEndpoints(device); - if (endpoints_len) { - for (auto ep_elt : arr_ep) { - uint8_t ep = strToUInt(ep_elt); - if (ep) { - addEndpoint(device, ep); - } - } - } - } - - return 0; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_3_hue.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_3_hue.ino" -#ifdef USE_ZIGBEE -#if defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) - - - - -void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, String *response) { - static const char HUE_LIGHTS_STATUS_JSON1_SUFFIX_ZIGBEE[] PROGMEM = - "%s\"alert\":\"none\"," - "\"effect\":\"none\"," - "\"reachable\":%s}"; - - bool power, reachable; - uint8_t colormode, bri, sat; - uint16_t ct, hue; - uint16_t x, y; - String light_status = ""; - uint32_t echo_gen = findEchoGeneration(); - - zigbee_devices.getHueState(shortaddr, &power, &colormode, &bri, &sat, &ct, &hue, &x, &y, &reachable); - - if (bri > 254) bri = 254; - if (bri < 1) bri = 1; - if (sat > 254) sat = 254; - uint16_t hue16 = changeUIntScale(hue, 0, 360, 0, 65535); - - const size_t buf_size = 256; - char * buf = (char*) malloc(buf_size); - - snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), power ? "true" : "false"); - - if ((1 == echo_gen) || (LST_SINGLE <= local_light_subtype)) { - snprintf_P(buf, buf_size, PSTR("%s\"bri\":%d,"), buf, bri); - } - if (LST_COLDWARM <= local_light_subtype) { - snprintf_P(buf, buf_size, PSTR("%s\"colormode\":\"%s\","), buf, (0 == colormode) ? "hs" : (1 == colormode) ? "xy" : "ct"); - } - if (LST_RGB <= local_light_subtype) { - if (prev_x_str[0] && prev_y_str[0]) { - snprintf_P(buf, buf_size, PSTR("%s\"xy\":[%s,%s],"), buf, prev_x_str, prev_y_str); - } else { - float x_f = x / 65536.0f; - float y_f = y / 65536.0f; - snprintf_P(buf, buf_size, PSTR("%s\"xy\":[%s,%s],"), buf, String(x, 5).c_str(), String(y, 5).c_str()); - } - snprintf_P(buf, buf_size, PSTR("%s\"hue\":%d,\"sat\":%d,"), buf, hue16, sat); - } - if (LST_COLDWARM == local_light_subtype || LST_RGBW <= local_light_subtype) { - snprintf_P(buf, buf_size, PSTR("%s\"ct\":%d,"), buf, ct > 0 ? ct : 284); - } - snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON1_SUFFIX_ZIGBEE, buf, reachable ? "true" : "false"); - - *response += buf; - free(buf); -} - -void HueLightStatus2Zigbee(uint16_t shortaddr, String *response) -{ - const size_t buf_size = 192; - char * buf = (char*) malloc(buf_size); - - const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr); - char shortaddrname[8]; - snprintf_P(shortaddrname, sizeof(shortaddrname), PSTR("0x%04X"), shortaddr); - - snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON2, - (friendlyName) ? friendlyName : shortaddrname, - GetHueDeviceId(shortaddr).c_str()); - *response += buf; - free(buf); -} - -void ZigbeeHueStatus(String * response, uint16_t shortaddr) { - *response += F("{\"state\":"); - HueLightStatus1Zigbee(shortaddr, zigbee_devices.getHueBulbtype(shortaddr), response); - HueLightStatus2Zigbee(shortaddr, response); -} - -void ZigbeeCheckHue(String * response, bool &appending) { - uint32_t zigbee_num = zigbee_devices.devicesSize(); - for (uint32_t i = 0; i < zigbee_num; i++) { - int8_t bulbtype = zigbee_devices.devicesAt(i).bulbtype; - - if (bulbtype >= 0) { - uint16_t shortaddr = zigbee_devices.devicesAt(i).shortaddr; - - if (appending) { *response += ","; } - *response += "\""; - *response += EncodeLightId(0, shortaddr); - *response += F("\":{\"state\":"); - HueLightStatus1Zigbee(shortaddr, bulbtype, response); - HueLightStatus2Zigbee(shortaddr, response); - appending = true; - } - } -} - -void ZigbeeHueGroups(String * lights) { - uint32_t zigbee_num = zigbee_devices.devicesSize(); - for (uint32_t i = 0; i < zigbee_num; i++) { - int8_t bulbtype = zigbee_devices.devicesAt(i).bulbtype; - - if (bulbtype >= 0) { - *lights += ",\""; - *lights += EncodeLightId(i); - *lights += "\""; - } - } -} - - - -void ZigbeeHuePower(uint16_t shortaddr, bool power) { - zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0006, power ? 1 : 0, ""); - zigbee_devices.updateHueState(shortaddr, &power, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); -} - - -void ZigbeeHueDimmer(uint16_t shortaddr, uint8_t dimmer) { - if (dimmer > 0xFE) { dimmer = 0xFE; } - char param[8]; - snprintf_P(param, sizeof(param), PSTR("%02X0A00"), dimmer); - zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0008, 0x04, param); - zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, &dimmer, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); -} - - -void ZigbeeHueCT(uint16_t shortaddr, uint16_t ct) { - if (ct > 0xFEFF) { ct = 0xFEFF; } - AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeHueCT 0x%04X - %d"), shortaddr, ct); - char param[12]; - snprintf_P(param, sizeof(param), PSTR("%02X%02X0A00"), ct & 0xFF, ct >> 8); - uint8_t colormode = 2; - zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x0A, param); - zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, nullptr, &ct, nullptr, nullptr, nullptr, nullptr); -} - - -void ZigbeeHueXY(uint16_t shortaddr, uint16_t x, uint16_t y) { - char param[16]; - if (x > 0xFEFF) { x = 0xFEFF; } - if (y > 0xFEFF) { y = 0xFEFF; } - snprintf_P(param, sizeof(param), PSTR("%02X%02X%02X%02X0A00"), x & 0xFF, x >> 8, y & 0xFF, y >> 8); - uint8_t colormode = 1; - zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x07, param); - zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, nullptr, nullptr, nullptr, &x, &y, nullptr); -} - - -void ZigbeeHueHS(uint16_t shortaddr, uint16_t hue, uint8_t sat) { - char param[16]; - uint8_t hue8 = changeUIntScale(hue, 0, 360, 0, 254); - if (sat > 0xFE) { sat = 0xFE; } - snprintf_P(param, sizeof(param), PSTR("%02X%02X0000"), hue8, sat); - uint8_t colormode = 0; - zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x06, param); - zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, &sat, nullptr, &hue, nullptr, nullptr, nullptr); -} - -void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) { - uint8_t power, colormode, bri, sat; - uint16_t ct, hue; - float x, y; - int code = 200; - - bool resp = false; - bool on = false; - - uint8_t bulbtype = zigbee_devices.getHueBulbtype(shortaddr); - - const size_t buf_size = 100; - char * buf = (char*) malloc(buf_size); - - if (Webserver->args()) { - response = "["; - - StaticJsonBuffer<300> jsonBuffer; - JsonObject &hue_json = jsonBuffer.parseObject(Webserver->arg((Webserver->args())-1)); - if (hue_json.containsKey("on")) { - on = hue_json["on"]; - snprintf_P(buf, buf_size, - PSTR("{\"success\":{\"/lights/%d/state/on\":%s}}"), - device_id, on ? "true" : "false"); - - if (on) { - ZigbeeHuePower(shortaddr, 0x01); - } else { - ZigbeeHuePower(shortaddr, 0x00); - } - response += buf; - resp = true; - } - - if (hue_json.containsKey("bri")) { - bri = hue_json["bri"]; - prev_bri = bri; - if (resp) { response += ","; } - snprintf_P(buf, buf_size, - PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), - device_id, "bri", bri); - response += buf; - if (LST_SINGLE <= bulbtype) { - - if (254 <= bri) { bri = 255; } - ZigbeeHueDimmer(shortaddr, bri); - } - resp = true; - } - - - if (hue_json.containsKey("xy")) { - float x = hue_json["xy"][0]; - float y = hue_json["xy"][1]; - const String &x_str = hue_json["xy"][0]; - const String &y_str = hue_json["xy"][1]; - x_str.toCharArray(prev_x_str, sizeof(prev_x_str)); - y_str.toCharArray(prev_y_str, sizeof(prev_y_str)); - if (resp) { response += ","; } - snprintf_P(buf, buf_size, - PSTR("{\"success\":{\"/lights/%d/state/xy\":[%s,%s]}}"), - device_id, prev_x_str, prev_y_str); - response += buf; - resp = true; - uint16_t xi = x * 65536.0f; - uint16_t yi = y * 65536.0f; - ZigbeeHueXY(shortaddr, xi, yi); - } - bool huesat_changed = false; - if (hue_json.containsKey("hue")) { - hue = hue_json["hue"]; - prev_hue = hue; - if (resp) { response += ","; } - snprintf_P(buf, buf_size, - PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), - device_id, "hue", hue); - response += buf; - if (LST_RGB <= bulbtype) { - - hue = changeUIntScale(hue, 0, 65535, 0, 360); - huesat_changed = true; - } - resp = true; - } - if (hue_json.containsKey("sat")) { - sat = hue_json["sat"]; - prev_sat = sat; - if (resp) { response += ","; } - snprintf_P(buf, buf_size, - PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), - device_id, "sat", sat); - response += buf; - if (LST_RGB <= bulbtype) { - - if (254 <= sat) { sat = 255; } - huesat_changed = true; - } - if (huesat_changed) { - ZigbeeHueHS(shortaddr, hue, sat); - } - resp = true; - } - if (hue_json.containsKey("ct")) { - ct = hue_json["ct"]; - prev_ct = ct; - if (resp) { response += ","; } - snprintf_P(buf, buf_size, - PSTR("{\"success\":{\"/lights/%d/state/%s\":%d}}"), - device_id, "ct", ct); - response += buf; - if ((LST_COLDWARM == bulbtype) || (LST_RGBW <= bulbtype)) { - ZigbeeHueCT(shortaddr, ct); - } - resp = true; - } - - response += "]"; - if (2 == response.length()) { - response = FPSTR(HUE_ERROR_JSON); - } - } - else { - response = FPSTR(HUE_ERROR_JSON); - } - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str()); - WSSend(code, CT_JSON, response); - - free(buf); -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_4_persistence.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_4_persistence.ino" -#ifdef USE_ZIGBEE -# 52 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_4_persistence.ino" -const static uint16_t z_spi_start_sector = 0xFF; -const static uint8_t* z_spi_start = (uint8_t*) 0x402FF000; -const static uint8_t* z_dev_start = z_spi_start + 0x0800; -const static size_t z_spi_len = 0x1000; -const static size_t z_block_offset = 0x0800; -const static size_t z_block_len = 0x0800; - -class z_flashdata_t { -public: - uint32_t name; - uint16_t len; - uint16_t reserved; -}; - -const static uint32_t ZIGB_NAME = 0x3167697A; -const static size_t Z_MAX_FLASH = z_block_len - sizeof(z_flashdata_t); - - -const uint16_t Z_ClusterNumber[] PROGMEM = { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, - 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, - 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, - 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - 0x0100, 0x0101, 0x0102, - 0x0201, 0x0202, 0x0203, 0x0204, - 0x0300, 0x0301, - 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, - 0x0500, 0x0501, 0x0502, - 0x0700, 0x0701, 0x0702, - 0x0B00, 0x0B01, 0x0B02, 0x0B03, 0x0B04, 0x0B05, - 0x1000, - 0xFC0F, -}; - - -uint16_t fromClusterCode(uint8_t c) { - if (c >= sizeof(Z_ClusterNumber)/sizeof(Z_ClusterNumber[0])) { - return 0xFFFF; - } - return pgm_read_word(&Z_ClusterNumber[c]); -} - - -uint8_t toClusterCode(uint16_t c) { - for (uint32_t i = 0; i < sizeof(Z_ClusterNumber)/sizeof(Z_ClusterNumber[0]); i++) { - if (c == pgm_read_word(&Z_ClusterNumber[i])) { - return i; - } - } - return 0xFF; -} - -class SBuffer hibernateDevice(const struct Z_Device &device) { - SBuffer buf(128); - - buf.add8(0x00); - buf.add16(device.shortaddr); - buf.add64(device.longaddr); - - uint32_t endpoints_count = 0; - for (endpoints_count = 0; endpoints_count < endpoints_max; endpoints_count++) { - if (0x00 == device.endpoints[endpoints_count]) { break; } - } - - buf.add8(endpoints_count); - - for (uint32_t i = 0; i < endpoints_max; i++) { - uint8_t endpoint = device.endpoints[i]; - if (0x00 == endpoint) { break; } - - buf.add8(endpoint); - buf.add16(0x0000); - - - buf.add8(0xFF); - - - buf.add8(0xFF); - } - - - if (device.modelId) { - size_t model_len = strlen(device.modelId); - if (model_len > 32) { model_len = 32; } - buf.addBuffer(device.modelId, model_len); - } - buf.add8(0x00); - - - if (device.manufacturerId) { - size_t manuf_len = strlen(device.manufacturerId); - if (manuf_len > 32) { manuf_len = 32; } - buf.addBuffer(device.manufacturerId, manuf_len); - } - buf.add8(0x00); - - - if (device.friendlyName) { - size_t frname_len = strlen(device.friendlyName); - if (frname_len > 32) {frname_len = 32; } - buf.addBuffer(device.friendlyName, frname_len); - } - buf.add8(0x00); - - - buf.add8(device.bulbtype); - - - buf.set8(0, buf.len()); - - return buf; -} - -class SBuffer hibernateDevices(void) { - SBuffer buf(2048); - - size_t devices_size = zigbee_devices.devicesSize(); - if (devices_size > 32) { devices_size = 32; } - buf.add8(devices_size); - - for (uint32_t i = 0; i < devices_size; i++) { - const Z_Device & device = zigbee_devices.devicesAt(i); - const SBuffer buf_device = hibernateDevice(device); - buf.addBuffer(buf_device); - } - - size_t buf_len = buf.len(); - if (buf_len > 2040) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Devices list too big to fit in Flash (%d)"), buf_len); - } - - - char *hex_char = (char*) malloc((buf_len * 2) + 2); - if (hex_char) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "ZbFlashStore %s"), - ToHex_P(buf.getBuffer(), buf_len, hex_char, (buf_len * 2) + 2)); - free(hex_char); - } - - return buf; -} - -void hydrateDevices(const SBuffer &buf) { - uint32_t buf_len = buf.len(); - if (buf_len <= 10) { return; } - - uint32_t k = 0; - uint32_t num_devices = buf.get8(k++); - - for (uint32_t i = 0; (i < num_devices) && (k < buf_len); i++) { - uint32_t dev_record_len = buf.get8(k); - - - - - SBuffer buf_d = buf.subBuffer(k, dev_record_len); -# 217 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_4_persistence.ino" - uint32_t d = 1; - uint16_t shortaddr = buf_d.get16(d); d += 2; - uint64_t longaddr = buf_d.get64(d); d += 8; - zigbee_devices.updateDevice(shortaddr, longaddr); - - uint32_t endpoints = buf_d.get8(d++); - for (uint32_t j = 0; j < endpoints; j++) { - uint8_t ep = buf_d.get8(d++); - uint16_t ep_profile = buf_d.get16(d); d += 2; - zigbee_devices.addEndpoint(shortaddr, ep); - - - while (d < dev_record_len) { - uint8_t ep_cluster = buf_d.get8(d++); - if (0xFF == ep_cluster) { break; } - - } - - while (d < dev_record_len) { - uint8_t ep_cluster = buf_d.get8(d++); - if (0xFF == ep_cluster) { break; } - - } - } - - - - char empty[] = ""; - - - uint32_t s_len = buf_d.strlen_s(d); - char *ptr = s_len ? buf_d.charptr(d) : empty; - zigbee_devices.setModelId(shortaddr, ptr); - d += s_len + 1; - - - s_len = buf_d.strlen_s(d); - ptr = s_len ? buf_d.charptr(d) : empty; - zigbee_devices.setManufId(shortaddr, ptr); - d += s_len + 1; - - - s_len = buf_d.strlen_s(d); - ptr = s_len ? buf_d.charptr(d) : empty; - zigbee_devices.setFriendlyName(shortaddr, ptr); - d += s_len + 1; - - - if (d < dev_record_len) { - zigbee_devices.setHueBulbtype(shortaddr, buf_d.get8(d)); - d++; - } - - - k += dev_record_len; - - } -} - -void loadZigbeeDevices(void) { - z_flashdata_t flashdata; - memcpy_P(&flashdata, z_dev_start, sizeof(z_flashdata_t)); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Zigbee signature in Flash: %08X - %d"), flashdata.name, flashdata.len); - - - if ((flashdata.name == ZIGB_NAME) && (flashdata.len > 0)) { - uint16_t buf_len = flashdata.len; - - SBuffer buf(buf_len); - buf.addBuffer(z_dev_start + sizeof(z_flashdata_t), buf_len); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee devices data in Flash (%d bytes)"), buf_len); - hydrateDevices(buf); - zigbee_devices.clean(); - } else { - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No zigbee devices data in Flash")); - } - -} - -void saveZigbeeDevices(void) { - SBuffer buf = hibernateDevices(); - size_t buf_len = buf.len(); - if (buf_len > Z_MAX_FLASH) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Buffer too big to fit in Flash (%d bytes)"), buf_len); - return; - } - - - uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len); - if (!spi_buffer) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer")); - return; - } - - ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); - - z_flashdata_t *flashdata = (z_flashdata_t*)(spi_buffer + z_block_offset); - flashdata->name = ZIGB_NAME; - flashdata->len = buf_len; - flashdata->reserved = 0; - - memcpy(spi_buffer + z_block_offset + sizeof(z_flashdata_t), buf.getBuffer(), buf_len); - - - if (ESP.flashEraseSector(z_spi_start_sector)) { - ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); - } - - free(spi_buffer); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data store in Flash (0x%08X - %d bytes)"), z_dev_start, buf_len); -} - - -void eraseZigbeeDevices(void) { - zigbee_devices.clean(); - - uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len); - if (!spi_buffer) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer")); - return; - } - - ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); - - - memset(spi_buffer + z_block_offset, 0xFF, z_block_len); - - - if (ESP.flashEraseSector(z_spi_start_sector)) { - ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); - } - - free(spi_buffer); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data erased (0x%08X - %d bytes)"), z_dev_start, z_block_len); -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" -#ifdef USE_ZIGBEE - - - - - - -enum Z_DataTypes { - Znodata = 0x00, - Zdata8 = 0x08, Zdata16, Zdata24, Zdata32, Zdata40, Zdata48, Zdata56, Zdata64, - Zbool = 0x10, - Zmap8 = 0x18, Zmap16, Zmap24, Zmap32, Zmap40, Zmap48, Zmap56, Zmap64, - Zuint8 = 0x20, Zuint16, Zuint24, Zuint32, Zuint40, Zuint48, Zuint56, Zuint64, - Zint8 = 0x28, Zint16, Zint24, Zint32, Zint40, Zint48, Zint56, Zint64, - Zenum8 = 0x30, Zenum16 = 0x31, - Zsemi = 0x38, Zsingle = 0x39, Zdouble = 0x3A, - Zoctstr = 0x41, Zstring = 0x42, Zoctstr16 = 0x43, Zstring16 = 0x44, - Arrray = 0x48, - Zstruct = 0x4C, - Zset = 0x50, Zbag = 0x51, - ZToD = 0xE0, Zdate = 0xE1, ZUTC = 0xE2, - ZclusterId = 0xE8, ZattribId = 0xE9, ZbacOID = 0xEA, - ZEUI64 = 0xF0, Zkey128 = 0xF1, - Zunk = 0xFF -}; - - - - - - -uint8_t Z_getDatatypeLen(uint8_t t) { - if ( ((t >= 0x08) && (t <= 0x0F)) || - ((t >= 0x18) && (t <= 0x2F)) ) { - return (t & 0x07) + 1; - } - switch (t) { - case Zbool: - case Zenum8: - return 1; - case Zenum16: - case Zsemi: - case ZclusterId: - case ZattribId: - return 2; - case Zsingle: - case ZToD: - case Zdate: - case ZUTC: - case ZbacOID: - return 4; - case Zdouble: - case ZEUI64: - return 8; - case Zkey128: - return 16; - case Znodata: - default: - return 0; - } -} -# 92 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" -typedef union ZCLHeaderFrameControl_t { - struct { - uint8_t frame_type : 2; - uint8_t manuf_specific : 1; - uint8_t direction : 1; - uint8_t disable_def_resp : 1; - uint8_t reserved : 3; - } b; - uint32_t d8; -} ZCLHeaderFrameControl_t; - - -class ZCLFrame { -public: - - ZCLFrame(uint8_t frame_control, uint16_t manuf_code, uint8_t transact_seq, uint8_t cmd_id, - const char *buf, size_t buf_len, uint16_t clusterid, uint16_t groupaddr, - uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast, - uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber, - uint32_t timestamp): - _manuf_code(manuf_code), _transact_seq(transact_seq), _cmd_id(cmd_id), - _payload(buf_len ? buf_len : 250), - _cluster_id(clusterid), _groupaddr(groupaddr), - _srcaddr(srcaddr), _srcendpoint(srcendpoint), _dstendpoint(dstendpoint), _wasbroadcast(wasbroadcast), - _linkquality(linkquality), _securityuse(securityuse), _seqnumber(seqnumber), - _timestamp(timestamp) - { - _frame_control.d8 = frame_control; - _payload.addBuffer(buf, buf_len); - }; - - - void log(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_ZIGBEEZCL_RECEIVED "\":{" - "\"groupid\":%d," "\"clusterid\":%d," "\"srcaddr\":\"0x%04X\"," - "\"srcendpoint\":%d," "\"dstendpoint\":%d," "\"wasbroadcast\":%d," - "\"" D_CMND_ZIGBEE_LINKQUALITY "\":%d," "\"securityuse\":%d," "\"seqnumber\":%d," - "\"timestamp\":%d," - "\"fc\":\"0x%02X\",\"manuf\":\"0x%04X\",\"transact\":%d," - "\"cmdid\":\"0x%02X\",\"payload\":\"%s\"}}"), - _groupaddr, _cluster_id, _srcaddr, - _srcendpoint, _dstendpoint, _wasbroadcast, - _linkquality, _securityuse, _seqnumber, - _timestamp, - _frame_control, _manuf_code, _transact_seq, _cmd_id, - hex_char); - if (Settings.flag3.tuya_serial_mqtt_publish) { - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); - XdrvRulesProcess(); - } else { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "%s"), mqtt_data); - } - } - - static ZCLFrame parseRawFrame(const SBuffer &buf, uint8_t offset, uint8_t len, uint16_t clusterid, uint16_t groupid, - uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast, - uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber, - uint32_t timestamp) { - uint32_t i = offset; - ZCLHeaderFrameControl_t frame_control; - uint16_t manuf_code = 0; - uint8_t transact_seq; - uint8_t cmd_id; - - frame_control.d8 = buf.get8(i++); - if (frame_control.b.manuf_specific) { - manuf_code = buf.get16(i); - i += 2; - } - transact_seq = buf.get8(i++); - cmd_id = buf.get8(i++); - ZCLFrame zcl_frame(frame_control.d8, manuf_code, transact_seq, cmd_id, - (const char *)(buf.buf() + i), len + offset - i, - clusterid, groupid, - srcaddr, srcendpoint, dstendpoint, wasbroadcast, - linkquality, securityuse, seqnumber, - timestamp); - return zcl_frame; - } - - bool isClusterSpecificCommand(void) { - return _frame_control.b.frame_type & 1; - } - - static void generateAttributeName(const JsonObject& json, uint16_t cluster, uint16_t attr, char *key, size_t key_len); - void parseRawAttributes(JsonObject& json, uint8_t offset = 0); - void parseReadAttributes(JsonObject& json, uint8_t offset = 0); - void parseResponse(void); - void parseClusterSpecificCommand(JsonObject& json, uint8_t offset = 0); - void postProcessAttributes(uint16_t shortaddr, JsonObject& json); - - inline void setGroupId(uint16_t groupid) { - _groupaddr = groupid; - } - - inline void setClusterId(uint16_t clusterid) { - _cluster_id = clusterid; - } - - inline uint8_t getCmdId(void) const { - return _cmd_id; - } - - inline uint16_t getClusterId(void) const { - return _cluster_id; - } - - inline uint16_t getSrcEndpoint(void) const { - return _srcendpoint; - } - - const SBuffer &getPayload(void) const { - return _payload; - } - - uint16_t getManufCode(void) const { - return _manuf_code; - } - -private: - ZCLHeaderFrameControl_t _frame_control = { .d8 = 0 }; - uint16_t _manuf_code = 0; - uint8_t _transact_seq = 0; - uint8_t _cmd_id = 0; - SBuffer _payload; - uint16_t _cluster_id = 0; - uint16_t _groupaddr = 0; - - uint16_t _srcaddr; - uint8_t _srcendpoint; - uint8_t _dstendpoint; - uint8_t _wasbroadcast; - uint8_t _linkquality; - uint8_t _securityuse; - uint8_t _seqnumber; - uint32_t _timestamp; -}; - - - - - - -uint8_t toPercentageCR2032(uint32_t voltage) { - uint32_t percentage; - if (voltage < 2100) { - percentage = 0; - } else if (voltage < 2440) { - percentage = 6 - ((2440 - voltage) * 6) / 340; - } else if (voltage < 2740) { - percentage = 18 - ((2740 - voltage) * 12) / 300; - } else if (voltage < 2900) { - percentage = 42 - ((2900 - voltage) * 24) / 160; - } else if (voltage < 3000) { - percentage = 100 - ((3000 - voltage) * 58) / 100; - } else if (voltage >= 3000) { - percentage = 100; - } - return percentage; -} - - -uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer &buf, - uint32_t offset, uint32_t buflen) { - - uint32_t i = offset; - uint32_t attrtype = buf.get8(i++); - - - json[attrid_str] = (char*) nullptr; - - uint32_t len = Z_getDatatypeLen(attrtype); - - - switch (attrtype) { - - - - case Zbool: - case Zuint8: - case Zenum8: - { - uint8_t uint8_val = buf.get8(i); - - if (0xFF != uint8_val) { - json[attrid_str] = uint8_val; - } - } - break; - case Zuint16: - case Zenum16: - { - uint16_t uint16_val = buf.get16(i); - - if (0xFFFF != uint16_val) { - json[attrid_str] = uint16_val; - } - } - break; - case Zuint32: - { - uint32_t uint32_val = buf.get32(i); - - if (0xFFFFFFFF != uint32_val) { - json[attrid_str] = uint32_val; - } - } - break; - - - case Zuint40: - case Zuint48: - case Zuint56: - case Zuint64: - case Zint40: - case Zint48: - case Zint56: - case Zint64: - { - - - char hex[2*len+1]; - ToHex_P(buf.buf(i), len, hex, sizeof(hex)); - json[attrid_str] = hex; - - } - break; - case Zint8: - { - int8_t int8_val = buf.get8(i); - - if (0x80 != int8_val) { - json[attrid_str] = int8_val; - } - } - break; - case Zint16: - { - int16_t int16_val = buf.get16(i); - - if (0x8000 != int16_val) { - json[attrid_str] = int16_val; - } - } - break; - case Zint32: - { - int32_t int32_val = buf.get32(i); - - if (0x80000000 != int32_val) { - json[attrid_str] = int32_val; - } - } - break; - - case Zoctstr: - case Zstring: - case Zoctstr16: - case Zstring16: - - { - bool parse_as_string = true; - len = (attrtype <= 0x42) ? buf.get8(i) : buf.get16(i); - i += (attrtype <= 0x42) ? 1 : 2; - if (i + len > buf.len()) { - len = buf.len() - i; - } - - - if ((0x41 == attrtype) || (0x43 == attrtype)) { parse_as_string = false; } - - if (parse_as_string) { - char str[len+1]; - strncpy(str, buf.charptr(i), len); - str[len] = 0x00; - json[attrid_str] = str; - } else { - - char hex[2*len+1]; - ToHex_P(buf.buf(i), len, hex, sizeof(hex)); - json[attrid_str] = hex; - } - - - - } - - break; - - case Zdata8: - case Zmap8: - { - uint8_t uint8_val = buf.get8(i); - - json[attrid_str] = uint8_val; - } - break; - case Zdata16: - case Zmap16: - { - uint16_t uint16_val = buf.get16(i); - - json[attrid_str] = uint16_val; - } - break; - case Zdata32: - case Zmap32: - { - uint32_t uint32_val = buf.get32(i); - - json[attrid_str] = uint32_val; - } - break; - - case Zsingle: - { - uint32_t uint32_val = buf.get32(i); - float * float_val = (float*) &uint32_val; - - json[attrid_str] = *float_val; - } - break; - - - case ZToD: - case Zdate: - case ZUTC: - case ZclusterId: - case ZattribId: - case ZbacOID: - case ZEUI64: - case Zkey128: - case Zsemi: - break; - - - case Zdata24: - case Zdata40: - case Zdata48: - case Zdata56: - case Zdata64: - break; - - case Zmap24: - case Zmap40: - case Zmap48: - case Zmap56: - case Zmap64: - break; - case Zdouble: - { - uint64_t uint64_val = buf.get64(i); - double * double_val = (double*) &uint64_val; - - json[attrid_str] = *double_val; - } - break; - } - i += len; - - - - - - - return i - offset; -} - - -void ZCLFrame::generateAttributeName(const JsonObject& json, uint16_t cluster, uint16_t attr, char *key, size_t key_len) { - uint32_t suffix = 1; - - snprintf_P(key, key_len, PSTR("%04X/%04X"), cluster, attr); - while (json.containsKey(key)) { - suffix++; - snprintf_P(key, key_len, PSTR("%04X/%04X+%d"), cluster, attr, suffix); - } -} - - -void ZCLFrame::parseRawAttributes(JsonObject& json, uint8_t offset) { - uint32_t i = offset; - uint32_t len = _payload.len(); - - while (len >= i + 3) { - uint16_t attrid = _payload.get16(i); - i += 2; - - char key[16]; - generateAttributeName(json, _cluster_id, attrid, key, sizeof(key)); - - - if ((0x0000 == _cluster_id) && (0xFF01 == attrid)) { - if (0x42 == _payload.get8(i)) { - _payload.set8(i, 0x41); - } - } - i += parseSingleAttribute(json, key, _payload, i, len); - } -} - - -void ZCLFrame::parseReadAttributes(JsonObject& json, uint8_t offset) { - uint32_t i = offset; - uint32_t len = _payload.len(); - - while (len - i >= 4) { - uint16_t attrid = _payload.get16(i); - i += 2; - uint8_t status = _payload.get8(i++); - - if (0 == status) { - char key[16]; - generateAttributeName(json, _cluster_id, attrid, key, sizeof(key)); - - i += parseSingleAttribute(json, key, _payload, i, len); - } - } -} - - -void ZCLFrame::parseResponse(void) { - if (_payload.len() < 2) { return; } - uint8_t cmd = _payload.get8(0); - uint8_t status = _payload.get8(1); - - DynamicJsonBuffer jsonBuffer; - JsonObject& json = jsonBuffer.createObject(); - - - char s[12]; - snprintf_P(s, sizeof(s), PSTR("0x%04X"), _srcaddr); - json[F(D_JSON_ZIGBEE_DEVICE)] = s; - - const char * friendlyName = zigbee_devices.getFriendlyName(_srcaddr); - if (friendlyName) { - json[F(D_JSON_ZIGBEE_NAME)] = (char*) friendlyName; - } - - snprintf_P(s, sizeof(s), PSTR("%04X!%02X"), _cluster_id, cmd); - json[F(D_JSON_ZIGBEE_CMD)] = s; - - json[F(D_JSON_ZIGBEE_STATUS)] = status; - - json[F(D_JSON_ZIGBEE_STATUS_MSG)] = getZigbeeStatusMessage(status); - - json[F(D_CMND_ZIGBEE_ENDPOINT)] = _srcendpoint; - - if (_groupaddr) { - json[F(D_CMND_ZIGBEE_GROUP)] = _groupaddr; - } - - json[F(D_CMND_ZIGBEE_LINKQUALITY)] = _linkquality; - - String msg(""); - msg.reserve(100); - json.printTo(msg); - Response_P(PSTR("{\"" D_JSON_ZIGBEE_RESPONSE "\":%s}"), msg.c_str()); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); - XdrvRulesProcess(); -} - - - -void ZCLFrame::parseClusterSpecificCommand(JsonObject& json, uint8_t offset) { - convertClusterSpecific(json, _cluster_id, _cmd_id, _frame_control.b.direction, _payload); - sendHueUpdate(_srcaddr, _groupaddr, _cluster_id, _cmd_id, _frame_control.b.direction); -} - - - - -typedef int32_t (*Z_AttrConverter)(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -typedef struct Z_AttributeConverter { - uint8_t type; - uint8_t cluster_short; - uint16_t attribute; - const char * name; - Z_AttrConverter func; -} Z_AttributeConverter; - -enum Cx_cluster_short { - Cx0000, Cx0001, Cx0002, Cx0003, Cx0004, Cx0005, Cx0006, Cx0007, - Cx0008, Cx0009, Cx000A, Cx000B, Cx000C, Cx000D, Cx000E, Cx000F, - Cx0010, Cx0011, Cx0012, Cx0013, Cx0014, Cx001A, Cx0020, Cx0100, - Cx0101, Cx0102, Cx0300, Cx0400, Cx0401, Cx0402, Cx0403, Cx0404, - Cx0405, Cx0406, Cx0B01, Cx0B05, -}; - -const uint16_t Cx_cluster[] PROGMEM = { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, - 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, - 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x001A, 0x0020, 0x0100, - 0x0101, 0x0102, 0x0300, 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, - 0x0405, 0x0406, 0x0B01, 0x0B05, -}; - -uint16_t CxToCluster(uint8_t cx) { - if (cx < sizeof(Cx_cluster)/sizeof(Cx_cluster[0])) { - return pgm_read_word(&Cx_cluster[cx]); - } - return 0xFFFF; -} - -ZF(ZCLVersion) ZF(AppVersion) ZF(StackVersion) ZF(HWVersion) ZF(Manufacturer) ZF(ModelId) -ZF(DateCode) ZF(PowerSource) ZF(SWBuildID) ZF(Power) ZF(SwitchType) ZF(Dimmer) -ZF(MainsVoltage) ZF(MainsFrequency) ZF(BatteryVoltage) ZF(BatteryPercentage) -ZF(CurrentTemperature) ZF(MinTempExperienced) ZF(MaxTempExperienced) ZF(OverTempTotalDwell) -ZF(SceneCount) ZF(CurrentScene) ZF(CurrentGroup) ZF(SceneValid) -ZF(AlarmCount) ZF(Time) ZF(TimeStatus) ZF(TimeZone) ZF(DstStart) ZF(DstEnd) -ZF(DstShift) ZF(StandardTime) ZF(LocalTime) ZF(LastSetTime) ZF(ValidUntilTime) - -ZF(LocationType) ZF(LocationMethod) ZF(LocationAge) ZF(QualityMeasure) ZF(NumberOfDevices) - -ZF(AnalogInActiveText) ZF(AnalogInDescription) ZF(AnalogInInactiveText) ZF(AnalogInMaxValue) -ZF(AnalogInMinValue) ZF(AnalogInOutOfService) ZF(AqaraRotate) ZF(AnalogInPriorityArray) -ZF(AnalogInReliability) ZF(AnalogInRelinquishDefault) ZF(AnalogInResolution) ZF(AnalogInStatusFlags) -ZF(AnalogInEngineeringUnits) ZF(AnalogInApplicationType) ZF(Aqara_FF05) - -ZF(AnalogOutDescription) ZF(AnalogOutMaxValue) ZF(AnalogOutMinValue) ZF(AnalogOutOutOfService) -ZF(AnalogOutValue) ZF(AnalogOutPriorityArray) ZF(AnalogOutReliability) ZF(AnalogOutRelinquishDefault) -ZF(AnalogOutResolution) ZF(AnalogOutStatusFlags) ZF(AnalogOutEngineeringUnits) ZF(AnalogOutApplicationType) - -ZF(AnalogDescription) ZF(AnalogOutOfService) ZF(AnalogValue) ZF(AnalogPriorityArray) ZF(AnalogReliability) -ZF(AnalogRelinquishDefault) ZF(AnalogStatusFlags) ZF(AnalogEngineeringUnits) ZF(AnalogApplicationType) - -ZF(BinaryInActiveText) ZF(BinaryInDescription) ZF(BinaryInInactiveText) ZF(BinaryInOutOfService) -ZF(BinaryInPolarity) ZF(BinaryInValue) ZF(BinaryInPriorityArray) ZF(BinaryInReliability) -ZF(BinaryInStatusFlags) ZF(BinaryInApplicationType) - -ZF(BinaryOutActiveText) ZF(BinaryOutDescription) ZF(BinaryOutInactiveText) ZF(BinaryOutMinimumOffTime) -ZF(BinaryOutMinimumOnTime) ZF(BinaryOutOutOfService) ZF(BinaryOutPolarity) ZF(BinaryOutValue) -ZF(BinaryOutPriorityArray) ZF(BinaryOutReliability) ZF(BinaryOutRelinquishDefault) ZF(BinaryOutStatusFlags) -ZF(BinaryOutApplicationType) - -ZF(BinaryActiveText) ZF(BinaryDescription) ZF(BinaryInactiveText) ZF(BinaryMinimumOffTime) -ZF(BinaryMinimumOnTime) ZF(BinaryOutOfService) ZF(BinaryValue) ZF(BinaryPriorityArray) ZF(BinaryReliability) -ZF(BinaryRelinquishDefault) ZF(BinaryStatusFlags) ZF(BinaryApplicationType) - -ZF(MultiInStateText) ZF(MultiInDescription) ZF(MultiInNumberOfStates) ZF(MultiInOutOfService) -ZF(MultiInValue) ZF(MultiInReliability) ZF(MultiInStatusFlags) ZF(MultiInApplicationType) - -ZF(MultiOutStateText) ZF(MultiOutDescription) ZF(MultiOutNumberOfStates) ZF(MultiOutOutOfService) -ZF(MultiOutValue) ZF(MultiOutPriorityArray) ZF(MultiOutReliability) ZF(MultiOutRelinquishDefault) -ZF(MultiOutStatusFlags) ZF(MultiOutApplicationType) - -ZF(MultiStateText) ZF(MultiDescription) ZF(MultiNumberOfStates) ZF(MultiOutOfService) ZF(MultiValue) -ZF(MultiReliability) ZF(MultiRelinquishDefault) ZF(MultiStatusFlags) ZF(MultiApplicationType) - -ZF(TotalProfileNum) ZF(MultipleScheduling) ZF(EnergyFormatting) ZF(EnergyRemote) ZF(ScheduleMode) - -ZF(CheckinInterval) ZF(LongPollInterval) ZF(ShortPollInterval) ZF(FastPollTimeout) ZF(CheckinIntervalMin) -ZF(LongPollIntervalMin) ZF(FastPollTimeoutMax) - -ZF(PhysicalClosedLimit) ZF(MotorStepSize) ZF(Status) ZF(ClosedLimit) ZF(Mode) - -ZF(LockState) ZF(LockType) ZF(ActuatorEnabled) ZF(DoorState) ZF(DoorOpenEvents) -ZF(DoorClosedEvents) ZF(OpenPeriod) - -ZF(AqaraVibrationMode) ZF(AqaraVibrationsOrAngle) ZF(AqaraVibration505) ZF(AqaraAccelerometer) - -ZF(WindowCoveringType) ZF(PhysicalClosedLimitLift) ZF(PhysicalClosedLimitTilt) ZF(CurrentPositionLift) -ZF(CurrentPositionTilt) ZF(NumberofActuationsLift) ZF(NumberofActuationsTilt) ZF(ConfigStatus) -ZF(CurrentPositionLiftPercentage) ZF(CurrentPositionTiltPercentage) ZF(InstalledOpenLimitLift) -ZF(InstalledClosedLimitLift) ZF(InstalledOpenLimitTilt) ZF(InstalledClosedLimitTilt) ZF(VelocityLift) -ZF(AccelerationTimeLift) ZF(DecelerationTimeLift) ZF(IntermediateSetpointsLift) -ZF(IntermediateSetpointsTilt) - -ZF(Hue) ZF(Sat) ZF(RemainingTime) ZF(X) ZF(Y) ZF(DriftCompensation) ZF(CompensationText) ZF(CT) -ZF(ColorMode) ZF(NumberOfPrimaries) ZF(Primary1X) ZF(Primary1Y) ZF(Primary1Intensity) ZF(Primary2X) -ZF(Primary2Y) ZF(Primary2Intensity) ZF(Primary3X) ZF(Primary3Y) ZF(Primary3Intensity) ZF(WhitePointX) -ZF(WhitePointY) ZF(ColorPointRX) ZF(ColorPointRY) ZF(ColorPointRIntensity) ZF(ColorPointGX) ZF(ColorPointGY) -ZF(ColorPointGIntensity) ZF(ColorPointBX) ZF(ColorPointBY) ZF(ColorPointBIntensity) - -ZF(Illuminance) ZF(IlluminanceMinMeasuredValue) ZF(IlluminanceMaxMeasuredValue) ZF(IlluminanceTolerance) -ZF(IlluminanceLightSensorType) ZF(IlluminanceLevelStatus) ZF(IlluminanceTargetLevel) - -ZF(Temperature) ZF(TemperatureMinMeasuredValue) ZF(TemperatureMaxMeasuredValue) ZF(TemperatureTolerance) - -ZF(PressureUnit) ZF(Pressure) ZF(PressureMinMeasuredValue) ZF(PressureMaxMeasuredValue) ZF(PressureTolerance) -ZF(PressureScaledValue) ZF(PressureMinScaledValue) ZF(PressureMaxScaledValue) ZF(PressureScaledTolerance) -ZF(PressureScale) - -ZF(FlowRate) ZF(FlowMinMeasuredValue) ZF(FlowMaxMeasuredValue) ZF(FlowTolerance) - -ZF(Humidity) ZF(HumidityMinMeasuredValue) ZF(HumidityMaxMeasuredValue) ZF(HumidityTolerance) - -ZF(Occupancy) ZF(OccupancySensorType) - -ZF(CompanyName) ZF(MeterTypeID) ZF(DataQualityID) ZF(CustomerName) ZF(Model) ZF(PartNumber) -ZF(SoftwareRevision) ZF(POD) ZF(AvailablePower) ZF(PowerThreshold) ZF(ProductRevision) ZF(UtilityName) - -ZF(NumberOfResets) ZF(PersistentMemoryWrites) ZF(LastMessageLQI) ZF(LastMessageRSSI) - -const Z_AttributeConverter Z_PostProcess[] PROGMEM = { - { Zuint8, Cx0000, 0x0000, Z(ZCLVersion), &Z_Copy }, - { Zuint8, Cx0000, 0x0001, Z(AppVersion), &Z_Copy }, - { Zuint8, Cx0000, 0x0002, Z(StackVersion), &Z_Copy }, - { Zuint8, Cx0000, 0x0003, Z(HWVersion), &Z_Copy }, - { Zstring, Cx0000, 0x0004, Z(Manufacturer), &Z_ManufKeep }, - { Zstring, Cx0000, 0x0005, Z(ModelId), &Z_ModelKeep }, - { Zstring, Cx0000, 0x0006, Z(DateCode), &Z_Copy }, - { Zenum8, Cx0000, 0x0007, Z(PowerSource), &Z_Copy }, - { Zstring, Cx0000, 0x4000, Z(SWBuildID), &Z_Copy }, - { Zunk, Cx0000, 0xFFFF, nullptr, &Z_Remove }, - - { Zmap8, Cx0000, 0xFF01, nullptr, &Z_AqaraSensor }, - - - { Zuint16, Cx0001, 0x0000, Z(MainsVoltage), &Z_Copy }, - { Zuint8, Cx0001, 0x0001, Z(MainsFrequency), &Z_Copy }, - { Zuint8, Cx0001, 0x0020, Z(BatteryVoltage), &Z_FloatDiv10 }, - { Zuint8, Cx0001, 0x0021, Z(BatteryPercentage), &Z_Copy }, - - - { Zint16, Cx0002, 0x0000, Z(CurrentTemperature), &Z_Copy }, - { Zint16, Cx0002, 0x0001, Z(MinTempExperienced), &Z_Copy }, - { Zint16, Cx0002, 0x0002, Z(MaxTempExperienced), &Z_Copy }, - { Zuint16, Cx0002, 0x0003, Z(OverTempTotalDwell), &Z_Copy }, - - - { Zuint8, Cx0005, 0x0000, Z(SceneCount), &Z_Copy }, - { Zuint8, Cx0005, 0x0001, Z(CurrentScene), &Z_Copy }, - { Zuint16, Cx0005, 0x0002, Z(CurrentGroup), &Z_Copy }, - { Zbool, Cx0005, 0x0003, Z(SceneValid), &Z_Copy }, - - - - { Zbool, Cx0006, 0x0000, Z(Power), &Z_Copy }, - { Zbool, Cx0006, 0x8000, Z(Power), &Z_Copy }, - - - { Zenum8, Cx0007, 0x0000, Z(SwitchType), &Z_Copy }, - - - { Zuint8, Cx0008, 0x0000, Z(Dimmer), &Z_Copy }, -# 738 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" - { Zuint16, Cx0009, 0x0000, Z(AlarmCount), &Z_Copy }, - - - { ZUTC, Cx000A, 0x0000, Z(Time), &Z_Copy }, - { Zmap8, Cx000A, 0x0001, Z(TimeStatus), &Z_Copy }, - { Zint32, Cx000A, 0x0002, Z(TimeZone), &Z_Copy }, - { Zuint32, Cx000A, 0x0003, Z(DstStart), &Z_Copy }, - { Zuint32, Cx000A, 0x0004, Z(DstEnd), &Z_Copy }, - { Zint32, Cx000A, 0x0005, Z(DstShift), &Z_Copy }, - { Zuint32, Cx000A, 0x0006, Z(StandardTime), &Z_Copy }, - { Zuint32, Cx000A, 0x0007, Z(LocalTime), &Z_Copy }, - { ZUTC, Cx000A, 0x0008, Z(LastSetTime), &Z_Copy }, - { ZUTC, Cx000A, 0x0009, Z(ValidUntilTime), &Z_Copy }, - - - { Zdata8, Cx000B, 0x0000, Z(LocationType), &Z_Copy }, - { Zenum8, Cx000B, 0x0001, Z(LocationMethod), &Z_Copy }, - { Zuint16, Cx000B, 0x0002, Z(LocationAge), &Z_Copy }, - { Zuint8, Cx000B, 0x0003, Z(QualityMeasure), &Z_Copy }, - { Zuint8, Cx000B, 0x0004, Z(NumberOfDevices), &Z_Copy }, - - - - { Zstring, Cx000C, 0x001C, Z(AnalogInDescription), &Z_Copy }, - - { Zsingle, Cx000C, 0x0041, Z(AnalogInMaxValue), &Z_Copy }, - { Zsingle, Cx000C, 0x0045, Z(AnalogInMinValue), &Z_Copy }, - { Zbool, Cx000C, 0x0051, Z(AnalogInOutOfService), &Z_Copy }, - { Zsingle, Cx000C, 0x0055, Z(AqaraRotate), &Z_Copy }, - - { Zenum8, Cx000C, 0x0067, Z(AnalogInReliability), &Z_Copy }, - - { Zsingle, Cx000C, 0x006A, Z(AnalogInResolution), &Z_Copy }, - { Zmap8, Cx000C, 0x006F, Z(AnalogInStatusFlags), &Z_Copy }, - { Zenum16, Cx000C, 0x0075, Z(AnalogInEngineeringUnits),&Z_Copy }, - { Zuint32, Cx000C, 0x0100, Z(AnalogInApplicationType),&Z_Copy }, - { Zuint16, Cx000C, 0xFF05, Z(Aqara_FF05), &Z_Copy }, - - - { Zstring, Cx000D, 0x001C, Z(AnalogOutDescription), &Z_Copy }, - { Zsingle, Cx000D, 0x0041, Z(AnalogOutMaxValue), &Z_Copy }, - { Zsingle, Cx000D, 0x0045, Z(AnalogOutMinValue), &Z_Copy }, - { Zbool, Cx000D, 0x0051, Z(AnalogOutOutOfService),&Z_Copy }, - { Zsingle, Cx000D, 0x0055, Z(AnalogOutValue), &Z_Copy }, - - { Zenum8, Cx000D, 0x0067, Z(AnalogOutReliability), &Z_Copy }, - { Zsingle, Cx000D, 0x0068, Z(AnalogOutRelinquishDefault),&Z_Copy }, - { Zsingle, Cx000D, 0x006A, Z(AnalogOutResolution), &Z_Copy }, - { Zmap8, Cx000D, 0x006F, Z(AnalogOutStatusFlags), &Z_Copy }, - { Zenum16, Cx000D, 0x0075, Z(AnalogOutEngineeringUnits),&Z_Copy }, - { Zuint32, Cx000D, 0x0100, Z(AnalogOutApplicationType),&Z_Copy }, - - - { Zstring, Cx000E, 0x001C, Z(AnalogDescription), &Z_Copy }, - { Zbool, Cx000E, 0x0051, Z(AnalogOutOfService), &Z_Copy }, - { Zsingle, Cx000E, 0x0055, Z(AnalogValue), &Z_Copy }, - { Zunk, Cx000E, 0x0057, Z(AnalogPriorityArray), &Z_Copy }, - { Zenum8, Cx000E, 0x0067, Z(AnalogReliability), &Z_Copy }, - { Zsingle, Cx000E, 0x0068, Z(AnalogRelinquishDefault),&Z_Copy }, - { Zmap8, Cx000E, 0x006F, Z(AnalogStatusFlags), &Z_Copy }, - { Zenum16, Cx000E, 0x0075, Z(AnalogEngineeringUnits),&Z_Copy }, - { Zuint32, Cx000E, 0x0100, Z(AnalogApplicationType),&Z_Copy }, - - - { Zstring, Cx000F, 0x0004, Z(BinaryInActiveText), &Z_Copy }, - { Zstring, Cx000F, 0x001C, Z(BinaryInDescription), &Z_Copy }, - { Zstring, Cx000F, 0x002E, Z(BinaryInInactiveText),&Z_Copy }, - { Zbool, Cx000F, 0x0051, Z(BinaryInOutOfService),&Z_Copy }, - { Zenum8, Cx000F, 0x0054, Z(BinaryInPolarity), &Z_Copy }, - { Zstring, Cx000F, 0x0055, Z(BinaryInValue), &Z_Copy }, - - { Zenum8, Cx000F, 0x0067, Z(BinaryInReliability), &Z_Copy }, - { Zmap8, Cx000F, 0x006F, Z(BinaryInStatusFlags), &Z_Copy }, - { Zuint32, Cx000F, 0x0100, Z(BinaryInApplicationType),&Z_Copy }, - - - { Zstring, Cx0010, 0x0004, Z(BinaryOutActiveText), &Z_Copy }, - { Zstring, Cx0010, 0x001C, Z(BinaryOutDescription), &Z_Copy }, - { Zstring, Cx0010, 0x002E, Z(BinaryOutInactiveText),&Z_Copy }, - { Zuint32, Cx0010, 0x0042, Z(BinaryOutMinimumOffTime),&Z_Copy }, - { Zuint32, Cx0010, 0x0043, Z(BinaryOutMinimumOnTime),&Z_Copy }, - { Zbool, Cx0010, 0x0051, Z(BinaryOutOutOfService),&Z_Copy }, - { Zenum8, Cx0010, 0x0054, Z(BinaryOutPolarity), &Z_Copy }, - { Zbool, Cx0010, 0x0055, Z(BinaryOutValue), &Z_Copy }, - - { Zenum8, Cx0010, 0x0067, Z(BinaryOutReliability), &Z_Copy }, - { Zbool, Cx0010, 0x0068, Z(BinaryOutRelinquishDefault),&Z_Copy }, - { Zmap8, Cx0010, 0x006F, Z(BinaryOutStatusFlags), &Z_Copy }, - { Zuint32, Cx0010, 0x0100, Z(BinaryOutApplicationType),&Z_Copy }, - - - { Zstring, Cx0011, 0x0004, Z(BinaryActiveText), &Z_Copy }, - { Zstring, Cx0011, 0x001C, Z(BinaryDescription), &Z_Copy }, - { Zstring, Cx0011, 0x002E, Z(BinaryInactiveText), &Z_Copy }, - { Zuint32, Cx0011, 0x0042, Z(BinaryMinimumOffTime), &Z_Copy }, - { Zuint32, Cx0011, 0x0043, Z(BinaryMinimumOnTime), &Z_Copy }, - { Zbool, Cx0011, 0x0051, Z(BinaryOutOfService), &Z_Copy }, - { Zbool, Cx0011, 0x0055, Z(BinaryValue), &Z_Copy }, - - { Zenum8, Cx0011, 0x0067, Z(BinaryReliability), &Z_Copy }, - { Zbool, Cx0011, 0x0068, Z(BinaryRelinquishDefault),&Z_Copy }, - { Zmap8, Cx0011, 0x006F, Z(BinaryStatusFlags), &Z_Copy }, - { Zuint32, Cx0011, 0x0100, Z(BinaryApplicationType),&Z_Copy }, - - - - { Zstring, Cx0012, 0x001C, Z(MultiInDescription), &Z_Copy }, - { Zuint16, Cx0012, 0x004A, Z(MultiInNumberOfStates),&Z_Copy }, - { Zbool, Cx0012, 0x0051, Z(MultiInOutOfService), &Z_Copy }, - { Zuint16, Cx0012, 0x0055, Z(MultiInValue), &Z_AqaraCube }, - { Zenum8, Cx0012, 0x0067, Z(MultiInReliability), &Z_Copy }, - { Zmap8, Cx0012, 0x006F, Z(MultiInStatusFlags), &Z_Copy }, - { Zuint32, Cx0012, 0x0100, Z(MultiInApplicationType),&Z_Copy }, - - - - { Zstring, Cx0013, 0x001C, Z(MultiOutDescription), &Z_Copy }, - { Zuint16, Cx0013, 0x004A, Z(MultiOutNumberOfStates),&Z_Copy }, - { Zbool, Cx0013, 0x0051, Z(MultiOutOutOfService), &Z_Copy }, - { Zuint16, Cx0013, 0x0055, Z(MultiOutValue), &Z_Copy }, - - { Zenum8, Cx0013, 0x0067, Z(MultiOutReliability), &Z_Copy }, - { Zuint16, Cx0013, 0x0068, Z(MultiOutRelinquishDefault),&Z_Copy }, - { Zmap8, Cx0013, 0x006F, Z(MultiOutStatusFlags), &Z_Copy }, - { Zuint32, Cx0013, 0x0100, Z(MultiOutApplicationType),&Z_Copy }, - - - - { Zstring, Cx0014, 0x001C, Z(MultiDescription), &Z_Copy }, - { Zuint16, Cx0014, 0x004A, Z(MultiNumberOfStates), &Z_Copy }, - { Zbool, Cx0014, 0x0051, Z(MultiOutOfService), &Z_Copy }, - { Zuint16, Cx0014, 0x0055, Z(MultiValue), &Z_Copy }, - { Zenum8, Cx0014, 0x0067, Z(MultiReliability), &Z_Copy }, - { Zuint16, Cx0014, 0x0068, Z(MultiRelinquishDefault),&Z_Copy }, - { Zmap8, Cx0014, 0x006F, Z(MultiStatusFlags), &Z_Copy }, - { Zuint32, Cx0014, 0x0100, Z(MultiApplicationType), &Z_Copy }, - - - { Zuint8, Cx001A, 0x0000, Z(TotalProfileNum), &Z_Copy }, - { Zbool, Cx001A, 0x0001, Z(MultipleScheduling), &Z_Copy }, - { Zmap8, Cx001A, 0x0002, Z(EnergyFormatting), &Z_Copy }, - { Zbool, Cx001A, 0x0003, Z(EnergyRemote), &Z_Copy }, - { Zmap8, Cx001A, 0x0004, Z(ScheduleMode), &Z_Copy }, - - - { Zuint32, Cx0020, 0x0000, Z(CheckinInterval), &Z_Copy }, - { Zuint32, Cx0020, 0x0001, Z(LongPollInterval), &Z_Copy }, - { Zuint16, Cx0020, 0x0002, Z(ShortPollInterval), &Z_Copy }, - { Zuint16, Cx0020, 0x0003, Z(FastPollTimeout), &Z_Copy }, - { Zuint32, Cx0020, 0x0004, Z(CheckinIntervalMin), &Z_Copy }, - { Zuint32, Cx0020, 0x0005, Z(LongPollIntervalMin), &Z_Copy }, - { Zuint16, Cx0020, 0x0006, Z(FastPollTimeoutMax), &Z_Copy }, - - - { Zuint16, Cx0100, 0x0000, Z(PhysicalClosedLimit), &Z_Copy }, - { Zuint8, Cx0100, 0x0001, Z(MotorStepSize), &Z_Copy }, - { Zmap8, Cx0100, 0x0002, Z(Status), &Z_Copy }, - { Zuint16, Cx0100, 0x0010, Z(ClosedLimit), &Z_Copy }, - { Zenum8, Cx0100, 0x0011, Z(Mode), &Z_Copy }, - - - { Zenum8, Cx0101, 0x0000, Z(LockState), &Z_Copy }, - { Zenum8, Cx0101, 0x0001, Z(LockType), &Z_Copy }, - { Zbool, Cx0101, 0x0002, Z(ActuatorEnabled), &Z_Copy }, - { Zenum8, Cx0101, 0x0003, Z(DoorState), &Z_Copy }, - { Zuint32, Cx0101, 0x0004, Z(DoorOpenEvents), &Z_Copy }, - { Zuint32, Cx0101, 0x0005, Z(DoorClosedEvents), &Z_Copy }, - { Zuint16, Cx0101, 0x0006, Z(OpenPeriod), &Z_Copy }, - - - { Zuint16, Cx0101, 0x0055, Z(AqaraVibrationMode), &Z_AqaraVibration }, - { Zuint16, Cx0101, 0x0503, Z(AqaraVibrationsOrAngle), &Z_Copy }, - { Zuint32, Cx0101, 0x0505, Z(AqaraVibration505), &Z_Copy }, - { Zuint48, Cx0101, 0x0508, Z(AqaraAccelerometer), &Z_AqaraVibration }, - - - { Zenum8, Cx0102, 0x0000, Z(WindowCoveringType), &Z_Copy }, - { Zuint16, Cx0102, 0x0001, Z(PhysicalClosedLimitLift),&Z_Copy }, - { Zuint16, Cx0102, 0x0002, Z(PhysicalClosedLimitTilt),&Z_Copy }, - { Zuint16, Cx0102, 0x0003, Z(CurrentPositionLift), &Z_Copy }, - { Zuint16, Cx0102, 0x0004, Z(CurrentPositionTilt), &Z_Copy }, - { Zuint16, Cx0102, 0x0005, Z(NumberofActuationsLift),&Z_Copy }, - { Zuint16, Cx0102, 0x0006, Z(NumberofActuationsTilt),&Z_Copy }, - { Zmap8, Cx0102, 0x0007, Z(ConfigStatus), &Z_Copy }, - { Zuint8, Cx0102, 0x0008, Z(CurrentPositionLiftPercentage),&Z_Copy }, - { Zuint8, Cx0102, 0x0009, Z(CurrentPositionTiltPercentage),&Z_Copy }, - { Zuint16, Cx0102, 0x0010, Z(InstalledOpenLimitLift),&Z_Copy }, - { Zuint16, Cx0102, 0x0011, Z(InstalledClosedLimitLift),&Z_Copy }, - { Zuint16, Cx0102, 0x0012, Z(InstalledOpenLimitTilt),&Z_Copy }, - { Zuint16, Cx0102, 0x0013, Z(InstalledClosedLimitTilt),&Z_Copy }, - { Zuint16, Cx0102, 0x0014, Z(VelocityLift), &Z_Copy }, - { Zuint16, Cx0102, 0x0015, Z(AccelerationTimeLift),&Z_Copy }, - { Zuint16, Cx0102, 0x0016, Z(DecelerationTimeLift), &Z_Copy }, - { Zmap8, Cx0102, 0x0017, Z(Mode), &Z_Copy }, - { Zoctstr, Cx0102, 0x0018, Z(IntermediateSetpointsLift),&Z_Copy }, - { Zoctstr, Cx0102, 0x0019, Z(IntermediateSetpointsTilt),&Z_Copy }, - - - { Zuint8, Cx0300, 0x0000, Z(Hue), &Z_Copy }, - { Zuint8, Cx0300, 0x0001, Z(Sat), &Z_Copy }, - { Zuint16, Cx0300, 0x0002, Z(RemainingTime), &Z_Copy }, - { Zuint16, Cx0300, 0x0003, Z(X), &Z_Copy }, - { Zuint16, Cx0300, 0x0004, Z(Y), &Z_Copy }, - { Zenum8, Cx0300, 0x0005, Z(DriftCompensation), &Z_Copy }, - { Zstring, Cx0300, 0x0006, Z(CompensationText), &Z_Copy }, - { Zuint16, Cx0300, 0x0007, Z(CT), &Z_Copy }, - { Zenum8, Cx0300, 0x0008, Z(ColorMode), &Z_Copy }, - { Zuint8, Cx0300, 0x0010, Z(NumberOfPrimaries), &Z_Copy }, - { Zuint16, Cx0300, 0x0011, Z(Primary1X), &Z_Copy }, - { Zuint16, Cx0300, 0x0012, Z(Primary1Y), &Z_Copy }, - { Zuint8, Cx0300, 0x0013, Z(Primary1Intensity), &Z_Copy }, - { Zuint16, Cx0300, 0x0015, Z(Primary2X), &Z_Copy }, - { Zuint16, Cx0300, 0x0016, Z(Primary2Y), &Z_Copy }, - { Zuint8, Cx0300, 0x0017, Z(Primary2Intensity), &Z_Copy }, - { Zuint16, Cx0300, 0x0019, Z(Primary3X), &Z_Copy }, - { Zuint16, Cx0300, 0x001A, Z(Primary3Y), &Z_Copy }, - { Zuint8, Cx0300, 0x001B, Z(Primary3Intensity), &Z_Copy }, - { Zuint16, Cx0300, 0x0030, Z(WhitePointX), &Z_Copy }, - { Zuint16, Cx0300, 0x0031, Z(WhitePointY), &Z_Copy }, - { Zuint16, Cx0300, 0x0032, Z(ColorPointRX), &Z_Copy }, - { Zuint16, Cx0300, 0x0033, Z(ColorPointRY), &Z_Copy }, - { Zuint8, Cx0300, 0x0034, Z(ColorPointRIntensity), &Z_Copy }, - { Zuint16, Cx0300, 0x0036, Z(ColorPointGX), &Z_Copy }, - { Zuint16, Cx0300, 0x0037, Z(ColorPointGY), &Z_Copy }, - { Zuint8, Cx0300, 0x0038, Z(ColorPointGIntensity), &Z_Copy }, - { Zuint16, Cx0300, 0x003A, Z(ColorPointBX), &Z_Copy }, - { Zuint16, Cx0300, 0x003B, Z(ColorPointBY), &Z_Copy }, - { Zuint8, Cx0300, 0x003C, Z(ColorPointBIntensity), &Z_Copy }, - - - { Zuint16, Cx0400, 0x0000, Z(Illuminance), &Z_Copy }, - { Zuint16, Cx0400, 0x0001, Z(IlluminanceMinMeasuredValue), &Z_Copy }, - { Zuint16, Cx0400, 0x0002, Z(IlluminanceMaxMeasuredValue), &Z_Copy }, - { Zuint16, Cx0400, 0x0003, Z(IlluminanceTolerance), &Z_Copy }, - { Zenum8, Cx0400, 0x0004, Z(IlluminanceLightSensorType), &Z_Copy }, - { Zunk, Cx0400, 0xFFFF, nullptr, &Z_Remove }, - - - { Zenum8, Cx0401, 0x0000, Z(IlluminanceLevelStatus), &Z_Copy }, - { Zenum8, Cx0401, 0x0001, Z(IlluminanceLightSensorType), &Z_Copy }, - { Zuint16, Cx0401, 0x0010, Z(IlluminanceTargetLevel), &Z_Copy }, - { Zunk, Cx0401, 0xFFFF, nullptr, &Z_Remove }, - - - { Zint16, Cx0402, 0x0000, Z(Temperature), &Z_FloatDiv100 }, - { Zint16, Cx0402, 0x0001, Z(TemperatureMinMeasuredValue), &Z_FloatDiv100 }, - { Zint16, Cx0402, 0x0002, Z(TemperatureMaxMeasuredValue), &Z_FloatDiv100 }, - { Zuint16, Cx0402, 0x0003, Z(TemperatureTolerance), &Z_FloatDiv100 }, - { Zunk, Cx0402, 0xFFFF, nullptr, &Z_Remove }, - - - { Zunk, Cx0403, 0x0000, Z(PressureUnit), &Z_AddPressureUnit }, - { Zint16, Cx0403, 0x0000, Z(Pressure), &Z_Copy }, - { Zint16, Cx0403, 0x0001, Z(PressureMinMeasuredValue), &Z_Copy }, - { Zint16, Cx0403, 0x0002, Z(PressureMaxMeasuredValue), &Z_Copy }, - { Zuint16, Cx0403, 0x0003, Z(PressureTolerance), &Z_Copy }, - { Zint16, Cx0403, 0x0010, Z(PressureScaledValue), &Z_Copy }, - { Zint16, Cx0403, 0x0011, Z(PressureMinScaledValue), &Z_Copy }, - { Zint16, Cx0403, 0x0012, Z(PressureMaxScaledValue), &Z_Copy }, - { Zuint16, Cx0403, 0x0013, Z(PressureScaledTolerance), &Z_Copy }, - { Zint8, Cx0403, 0x0014, Z(PressureScale), &Z_Copy }, - { Zunk, Cx0403, 0xFFFF, nullptr, &Z_Remove }, - - - { Zuint16, Cx0404, 0x0000, Z(FlowRate), &Z_FloatDiv10 }, - { Zuint16, Cx0404, 0x0001, Z(FlowMinMeasuredValue), &Z_Copy }, - { Zuint16, Cx0404, 0x0002, Z(FlowMaxMeasuredValue), &Z_Copy }, - { Zuint16, Cx0404, 0x0003, Z(FlowTolerance), &Z_Copy }, - { Zunk, Cx0404, 0xFFFF, nullptr, &Z_Remove }, - - - { Zuint16, Cx0405, 0x0000, Z(Humidity), &Z_FloatDiv100 }, - { Zuint16, Cx0405, 0x0001, Z(HumidityMinMeasuredValue), &Z_Copy }, - { Zuint16, Cx0405, 0x0002, Z(HumidityMaxMeasuredValue), &Z_Copy }, - { Zuint16, Cx0405, 0x0003, Z(HumidityTolerance), &Z_Copy }, - { Zunk, Cx0405, 0xFFFF, nullptr, &Z_Remove }, - - - { Zmap8, Cx0406, 0x0000, Z(Occupancy), &Z_Copy }, - { Zenum8, Cx0406, 0x0001, Z(OccupancySensorType), &Z_Copy }, - { Zunk, Cx0406, 0xFFFF, nullptr, &Z_Remove }, - - - { Zstring, Cx0B01, 0x0000, Z(CompanyName), &Z_Copy }, - { Zuint16, Cx0B01, 0x0001, Z(MeterTypeID), &Z_Copy }, - { Zuint16, Cx0B01, 0x0004, Z(DataQualityID), &Z_Copy }, - { Zstring, Cx0B01, 0x0005, Z(CustomerName), &Z_Copy }, - { Zoctstr, Cx0B01, 0x0006, Z(Model), &Z_Copy }, - { Zoctstr, Cx0B01, 0x0007, Z(PartNumber), &Z_Copy }, - { Zoctstr, Cx0B01, 0x0008, Z(ProductRevision), &Z_Copy }, - { Zoctstr, Cx0B01, 0x000A, Z(SoftwareRevision), &Z_Copy }, - { Zstring, Cx0B01, 0x000B, Z(UtilityName), &Z_Copy }, - { Zstring, Cx0B01, 0x000C, Z(POD), &Z_Copy }, - { Zint24, Cx0B01, 0x000D, Z(AvailablePower), &Z_Copy }, - { Zint24, Cx0B01, 0x000E, Z(PowerThreshold), &Z_Copy }, - - - { Zuint16, Cx0B05, 0x0000, Z(NumberOfResets), &Z_Copy }, - { Zuint16, Cx0B05, 0x0001, Z(PersistentMemoryWrites),&Z_Copy }, - { Zuint8, Cx0B05, 0x011C, Z(LastMessageLQI), &Z_Copy }, - { Zuint8, Cx0B05, 0x011D, Z(LastMessageRSSI), &Z_Copy }, - -}; - - - -int32_t Z_ManufKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - json[new_name] = value; - zigbee_devices.setManufId(shortaddr, value.as()); - return 1; -} - -int32_t Z_ModelKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - json[new_name] = value; - zigbee_devices.setModelId(shortaddr, value.as()); - return 1; -} - - - -int32_t Z_Remove(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - return 1; -} - - -int32_t Z_Copy(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - json[new_name] = value; - return 1; -} - - -int32_t Z_AddPressureUnit(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - json[new_name] = F(D_UNIT_PRESSURE); - return 0; -} - - -int32_t Z_FloatDiv100(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - json[new_name] = ((float)value) / 100.0f; - return 1; -} - -int32_t Z_FloatDiv10(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - json[new_name] = ((float)value) / 10.0f; - return 1; -} - -int32_t Z_FloatDiv2(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - json[new_name] = ((float)value) / 2.0f; - return 1; -} - - -int32_t Z_OccupancyCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { - DynamicJsonBuffer jsonBuffer; - JsonObject& json = jsonBuffer.createObject(); - json[F(OCCUPANCY)] = 0; - zigbee_devices.jsonPublishNow(shortaddr, json); -} - - -int32_t Z_AqaraCube(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - json[new_name] = value; - int32_t val = value; - const __FlashStringHelper *aqara_cube = F("AqaraCube"); - const __FlashStringHelper *aqara_cube_side = F("AqaraCubeSide"); - const __FlashStringHelper *aqara_cube_from_side = F("AqaraCubeFromSide"); - - switch (val) { - case 0: - json[aqara_cube] = F("shake"); - break; - case 2: - json[aqara_cube] = F("wakeup"); - break; - case 3: - json[aqara_cube] = F("fall"); - break; - case 64 ... 127: - json[aqara_cube] = F("flip90"); - json[aqara_cube_side] = val % 8; - json[aqara_cube_from_side] = (val - 64) / 8; - break; - case 128 ... 132: - json[aqara_cube] = F("flip180"); - json[aqara_cube_side] = val - 128; - break; - case 256 ... 261: - json[aqara_cube] = F("slide"); - json[aqara_cube_side] = val - 256; - break; - case 512 ... 517: - json[aqara_cube] = F("tap"); - json[aqara_cube_side] = val - 512; - break; - } -# 1154 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" - return 1; -} - - -int32_t Z_AqaraVibration(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - - switch (attr) { - case 0x0055: - { - int32_t ivalue = value; - const __FlashStringHelper * svalue; - switch (ivalue) { - case 1: svalue = F("vibrate"); break; - case 2: svalue = F("tilt"); break; - case 3: svalue = F("drop"); break; - default: svalue = F("unknown"); break; - } - json[new_name] = svalue; - } - break; - - - - - case 0x0508: - { - - - String hex = value; - SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); - int16_t x, y, z; - z = buf2.get16(0); - y = buf2.get16(2); - x = buf2.get16(4); - JsonArray& xyz = json.createNestedArray(new_name); - xyz.add(x); - xyz.add(y); - xyz.add(z); - - float X = x; - float Y = y; - float Z = z; - int32_t Angle_X = 0.5f + atanf(X/sqrtf(z*z+y*y)) * f_180pi; - int32_t Angle_Y = 0.5f + atanf(Y/sqrtf(x*x+z*z)) * f_180pi; - int32_t Angle_Z = 0.5f + atanf(Z/sqrtf(x*x+y*y)) * f_180pi; - JsonArray& angles = json.createNestedArray(F("AqaraAngles")); - angles.add(Angle_X); - angles.add(Angle_Y); - angles.add(Angle_Z); - } - break; - } - return 1; -} - -int32_t Z_AqaraSensor(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - String hex = value; - SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); - uint32_t i = 0; - uint32_t len = buf2.len(); - char tmp[] = "tmp"; - - JsonVariant sub_value; - const char * modelId_c = zigbee_devices.getModelId(shortaddr); - String modelId((char*) modelId_c); - - while (len - i >= 2) { - uint8_t attrid = buf2.get8(i++); - - i += parseSingleAttribute(json, tmp, buf2, i, len); - float val = json[tmp]; - json.remove(tmp); - bool translated = false; - if (0x01 == attrid) { - json[F(D_JSON_VOLTAGE)] = val / 1000.0f; - json[F("Battery")] = toPercentageCR2032(val); - } else if ((nullptr != modelId) && (0 == zcl->getManufCode())) { - translated = true; - if (modelId.startsWith(F("lumi.sensor_ht")) || - modelId.startsWith(F("lumi.weather"))) { - - - if (0x64 == attrid) { - json[F(D_JSON_TEMPERATURE)] = val / 100.0f; - } else if (0x65 == attrid) { - json[F(D_JSON_HUMIDITY)] = val / 100.0f; - } else if (0x66 == attrid) { - json[F(D_JSON_PRESSURE)] = val / 100.0f; - json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); - } - } else if (modelId.startsWith(F("lumi.sensor_smoke"))) { - if (0x64 == attrid) { - json[F("SmokeDensity")] = val; - } - } else if (modelId.startsWith(F("lumi.sensor_natgas"))) { - if (0x64 == attrid) { - json[F("GasDensity")] = val; - } - } else { - translated = false; - } - - } - if (!translated) { - if (attrid >= 100) { - char attr_name[12]; - snprintf_P(attr_name, sizeof(attr_name), PSTR("Xiaomi_%02X"), attrid); - json[attr_name] = val; - } - } - } - return 1; -} - - -void ZCLFrame::postProcessAttributes(uint16_t shortaddr, JsonObject& json) { - - for (auto kv : json) { - String key_string = kv.key; - const char * key = key_string.c_str(); - JsonVariant& value = kv.value; - - char * delimiter = strchr(key, '/'); - char * delimiter2 = strchr(key, '+'); - if (delimiter) { - uint16_t attribute; - uint16_t suffix = 1; - uint16_t cluster = strtoul(key, &delimiter, 16); - if (!delimiter2) { - attribute = strtoul(delimiter+1, nullptr, 16); - } else { - attribute = strtoul(delimiter+1, &delimiter2, 16); - suffix = strtoul(delimiter2+1, nullptr, 10); - } - - - if ((cluster == 0x0006) && ((attribute == 0x0000) || (attribute == 0x8000))) { - bool power = value; - zigbee_devices.updateHueState(shortaddr, &power, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr); - } else if ((cluster == 0x0008) && (attribute == 0x0000)) { - uint8_t dimmer = value; - zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, &dimmer, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr); - } else if ((cluster == 0x0300) && (attribute == 0x0000)) { - uint16_t hue8 = value; - uint16_t hue = changeUIntScale(hue8, 0, 254, 0, 360); - zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, nullptr, nullptr, - nullptr, &hue, nullptr, nullptr, nullptr); - } else if ((cluster == 0x0300) && (attribute == 0x0001)) { - uint8_t sat = value; - zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, nullptr, &sat, - nullptr, nullptr, nullptr, nullptr, nullptr); - } else if ((cluster == 0x0300) && (attribute == 0x0003)) { - uint16_t x = value; - zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, &x, nullptr, nullptr); - } else if ((cluster == 0x0300) && (attribute == 0x0004)) { - uint16_t y = value; - zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, &y, nullptr), nullptr; - } else if ((cluster == 0x0300) && (attribute == 0x0007)) { - uint16_t ct = value; - zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, nullptr, nullptr, - &ct, nullptr, nullptr, nullptr, nullptr); - } else if ((cluster == 0x0300) && (attribute == 0x0008)) { - uint8_t colormode = value; - zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr); - } - - - for (uint32_t i = 0; i < sizeof(Z_PostProcess) / sizeof(Z_PostProcess[0]); i++) { - const Z_AttributeConverter *converter = &Z_PostProcess[i]; - uint16_t conv_cluster = CxToCluster(pgm_read_byte(&converter->cluster_short)); - uint16_t conv_attribute = pgm_read_word(&converter->attribute); - - if ((conv_cluster == cluster) && - ((conv_attribute == attribute) || (conv_attribute == 0xFFFF)) ) { - String new_name_str = (const __FlashStringHelper*) converter->name; - if (suffix > 1) { new_name_str += suffix; } - int32_t drop = (*converter->func)(this, shortaddr, json, key, value, new_name_str, conv_cluster, conv_attribute); - if (drop) { - json.remove(key); - } - - } - } - } - } -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_6_commands.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_6_commands.ino" -#ifdef USE_ZIGBEE - - - - - -typedef struct Z_CommandConverter { - const char * tasmota_cmd; - uint16_t cluster; - uint8_t cmd; - uint8_t direction; - const char * param; -} Z_CommandConverter; - -typedef struct Z_XYZ_Var { - uint32_t x = 0; - uint32_t y = 0; - uint32_t z = 0; - uint8_t x_type = 0; - uint8_t y_type = 0; - uint8_t z_type = 0; -} Z_XYZ_Var; - -ZF(AddGroup) ZF(ViewGroup) ZF(GetGroup) ZF(GetAllGroups) ZF(RemoveGroup) ZF(RemoveAllGroups) -ZF(AddScene) ZF(ViewScene) ZF(RemoveScene) ZF(RemoveAllScenes) ZF(RecallScene) ZF(StoreScene) ZF(GetSceneMembership) - -ZF(DimmerUp) ZF(DimmerDown) ZF(DimmerStop) -ZF(ResetAlarm) ZF(ResetAllAlarms) - -ZF(HueSat) ZF(Color) -ZF(ShutterOpen) ZF(ShutterClose) ZF(ShutterStop) ZF(ShutterLift) ZF(ShutterTilt) ZF(Shutter) - -ZF(DimmerMove) ZF(DimmerStep) -ZF(HueMove) ZF(HueStep) ZF(SatMove) ZF(SatStep) ZF(ColorMove) ZF(ColorStep) -ZF(ArrowClick) ZF(ArrowHold) ZF(ArrowRelease) ZF(ZoneStatusChange) - -ZF(xxxx00) ZF(xxxx) ZF(01xxxx) ZF(00) ZF(01) ZF() ZF(xxxxyy) ZF(00190200) ZF(01190200) ZF(xxyyyy) ZF(xx) -ZF(xx000A00) ZF(xx0A00) ZF(xxyy0A00) ZF(xxxxyyyy0A00) ZF(xxxx0A00) ZF(xx0A) -ZF(xx190A00) ZF(xx19) ZF(xx190A) ZF(xxxxyyyy) ZF(xxxxyyzz) ZF(xxyyzzzz) ZF(xxyyyyzz) -# 67 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_6_commands.ino" -const Z_CommandConverter Z_Commands[] PROGMEM = { - - { Z(AddGroup), 0x0004, 0x00, 0x01, Z(xxxx00) }, - { Z(ViewGroup), 0x0004, 0x01, 0x01, Z(xxxx) }, - { Z(GetGroup), 0x0004, 0x02, 0x01, Z(01xxxx) }, - { Z(GetAllGroups), 0x0004, 0x02, 0x01, Z(00) }, - { Z(RemoveGroup), 0x0004, 0x03, 0x01, Z(xxxx) }, - { Z(RemoveAllGroups),0x0004, 0x04, 0x01, Z() }, - - - { Z(ViewScene), 0x0005, 0x01, 0x01, Z(xxxxyy) }, - { Z(RemoveScene), 0x0005, 0x02, 0x01, Z(xxxxyy) }, - { Z(RemoveAllScenes),0x0005, 0x03, 0x01, Z(xxxx) }, - { Z(RecallScene), 0x0005, 0x05, 0x01, Z(xxxxyy) }, - { Z(GetSceneMembership),0x0005, 0x06, 0x01, Z(xxxx) }, - - { Z(Power), 0x0006, 0xFF, 0x01, Z() }, - { Z(Dimmer), 0x0008, 0x04, 0x01, Z(xx0A00) }, - { Z(DimmerUp), 0x0008, 0x06, 0x01, Z(00190200) }, - { Z(DimmerDown), 0x0008, 0x06, 0x01, Z(01190200) }, - { Z(DimmerStop), 0x0008, 0x03, 0x01, Z() }, - { Z(ResetAlarm), 0x0009, 0x00, 0x01, Z(xxyyyy) }, - { Z(ResetAllAlarms), 0x0009, 0x01, 0x01, Z() }, - { Z(Hue), 0x0300, 0x00, 0x01, Z(xx000A00) }, - { Z(Sat), 0x0300, 0x03, 0x01, Z(xx0A00) }, - { Z(HueSat), 0x0300, 0x06, 0x01, Z(xxyy0A00) }, - { Z(Color), 0x0300, 0x07, 0x01, Z(xxxxyyyy0A00) }, - { Z(CT), 0x0300, 0x0A, 0x01, Z(xxxx0A00) }, - { Z(ShutterOpen), 0x0102, 0x00, 0x01, Z() }, - { Z(ShutterClose), 0x0102, 0x01, 0x01, Z() }, - { Z(ShutterStop), 0x0102, 0x02, 0x01, Z() }, - { Z(ShutterLift), 0x0102, 0x05, 0x01, Z(xx) }, - { Z(ShutterTilt), 0x0102, 0x08, 0x01, Z(xx) }, - { Z(Shutter), 0x0102, 0xFF, 0x01, Z() }, - - { Z(Occupancy), 0xEF00, 0x01, 0x82, Z()}, - - { Z(Dimmer), 0x0008, 0x00, 0x01, Z(xx) }, - { Z(DimmerMove), 0x0008, 0x01, 0x01, Z(xx0A) }, - { Z(DimmerStep), 0x0008, 0x02, 0x01, Z(xx190A00) }, - { Z(DimmerMove), 0x0008, 0x05, 0x01, Z(xx0A) }, - { Z(DimmerUp), 0x0008, 0x06, 0x01, Z(00) }, - { Z(DimmerDown), 0x0008, 0x06, 0x01, Z(01) }, - { Z(DimmerStop), 0x0008, 0x07, 0x01, Z() }, - { Z(HueMove), 0x0300, 0x01, 0x01, Z(xx19) }, - { Z(HueStep), 0x0300, 0x02, 0x01, Z(xx190A00) }, - { Z(SatMove), 0x0300, 0x04, 0x01, Z(xx19) }, - { Z(SatStep), 0x0300, 0x05, 0x01, Z(xx190A) }, - { Z(ColorMove), 0x0300, 0x08, 0x01, Z(xxxxyyyy) }, - { Z(ColorStep), 0x0300, 0x09, 0x01, Z(xxxxyyyy0A00) }, - - { Z(ArrowClick), 0x0005, 0x07, 0x01, Z(xx) }, - { Z(ArrowHold), 0x0005, 0x08, 0x01, Z(xx) }, - { Z(ArrowRelease), 0x0005, 0x09, 0x01, Z() }, - - { Z(ZoneStatusChange),0x0500, 0x00, 0x82, Z(xxxxyyzz) }, - - { Z(AddGroup), 0x0004, 0x00, 0x82, Z(xxyyyy) }, - { Z(ViewGroup), 0x0004, 0x01, 0x82, Z(xxyyyy) }, - { Z(GetGroup), 0x0004, 0x02, 0x82, Z(xxyyzzzz) }, - { Z(RemoveGroup), 0x0004, 0x03, 0x82, Z(xxyyyy) }, - - { Z(AddScene), 0x0005, 0x00, 0x82, Z(xxyyyyzz) }, - { Z(ViewScene), 0x0005, 0x01, 0x82, Z(xxyyyyzz) }, - { Z(RemoveScene), 0x0005, 0x02, 0x82, Z(xxyyyyzz) }, - { Z(RemoveAllScenes),0x0005, 0x03, 0x82, Z(xxyyyy) }, - { Z(StoreScene), 0x0005, 0x04, 0x82, Z(xxyyyyzz) }, - { Z(GetSceneMembership),0x0005, 0x06, 0x82,Z(xxyyzzzz) }, -}; - - - - -#define ZLE(x) ((x) & 0xFF), ((x) >> 8) - - -const uint8_t CLUSTER_0006[] = { ZLE(0x0000) }; -const uint8_t CLUSTER_0008[] = { ZLE(0x0000) }; -const uint8_t CLUSTER_0009[] = { ZLE(0x0000) }; -const uint8_t CLUSTER_0300[] = { ZLE(0x0000), ZLE(0x0001), ZLE(0x0003), ZLE(0x0004), ZLE(0x0007), ZLE(0x0008) }; - - -int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { - size_t attrs_len = 0; - const uint8_t* attrs = nullptr; - - switch (cluster) { - case 0x0006: - attrs = CLUSTER_0006; - attrs_len = sizeof(CLUSTER_0006); - break; - case 0x0008: - attrs = CLUSTER_0008; - attrs_len = sizeof(CLUSTER_0008); - break; - case 0x0009: - attrs = CLUSTER_0009; - attrs_len = sizeof(CLUSTER_0009); - break; - case 0x0300: - attrs = CLUSTER_0300; - attrs_len = sizeof(CLUSTER_0300); - break; - } - if (attrs) { - ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, 0, attrs, attrs_len, true , zigbee_devices.getNextSeqNumber(shortaddr)); - } -} - - - -int32_t Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { - if (shortaddr) { - zigbee_devices.setReachable(shortaddr, false); - } -} - - -void zigbeeSetCommandTimer(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint) { - uint32_t wait_ms = 0; - - switch (cluster) { - case 0x0006: - case 0x0009: - wait_ms = 200; - break; - case 0x0008: - case 0x0300: - wait_ms = 1050; - break; - case 0x0102: - wait_ms = 10000; - break; - } - if (wait_ms) { - zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, Z_CAT_NONE, 0 , &Z_ReadAttrCallback); - if (shortaddr) { - zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 , &Z_Unreachable); - } - } -} - - -inline bool isXYZ(char c) { - return (c >= 'x') && (c <= 'z'); -} - - - - -inline int8_t hexValue(char c) { - if ((c >= '0') && (c <= '9')) { - return c - '0'; - } - if ((c >= 'A') && (c <= 'F')) { - return 10 + c - 'A'; - } - if ((c >= 'a') && (c <= 'f')) { - return 10 + c - 'a'; - } - return -1; -} - - -uint32_t parseHex_P(const char **data, size_t max_len = 8) { - uint32_t ret = 0; - for (uint32_t i = 0; i < max_len; i++) { - int8_t v = hexValue(pgm_read_byte(*data)); - if (v < 0) { break; } - ret = (ret << 4) | v; - *data += 1; - } - return ret; -} - - - - - -void parseXYZ(const char *model, const SBuffer &payload, struct Z_XYZ_Var *xyz) { - const char *p = model; - uint32_t v = 0; - char c = pgm_read_byte(p); - while (c) { - char c1 = pgm_read_byte(p+1); - if (!c1) { break; } - if (isXYZ(c) && (c == c1) && (v < payload.len())) { - uint8_t val = payload.get8(v); - switch (c) { - case 'x': - xyz->x = xyz->x | (val << (xyz->x_type * 8)); - xyz->x_type++; - break; - case 'y': - xyz->y = xyz->y | (val << (xyz->y_type * 8)); - xyz->y_type++; - break; - case 'z': - xyz->z = xyz->z | (val << (xyz->z_type * 8)); - xyz->z_type++; - break; - } - } - p += 2; - v++; - c = pgm_read_byte(p); - } -} - - - - - - -void sendHueUpdate(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t cmd, bool direction) { - if (direction) { return; } - - int32_t z_cat = -1; - uint32_t wait_ms = 0; - - switch (cluster) { - case 0x0006: - z_cat = Z_CAT_READ_0006; - wait_ms = 200; - break; - case 0x0008: - z_cat = Z_CAT_READ_0008; - wait_ms = 1050; - break; - case 0x0102: - z_cat = Z_CAT_READ_0102; - wait_ms = 10000; - break; - case 0x0300: - z_cat = Z_CAT_READ_0300; - wait_ms = 1050; - break; - default: - break; - } - if (z_cat >= 0) { - uint8_t endpoint = 0; - if (shortaddr) { - endpoint = zigbee_devices.findFirstEndpoint(shortaddr); - } - if ((!shortaddr) || (endpoint)) { - zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, z_cat, 0 , &Z_ReadAttrCallback); - if (shortaddr) { - zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 , &Z_Unreachable); - } - - } - } -} - - - -void convertClusterSpecific(JsonObject& json, uint16_t cluster, uint8_t cmd, bool direction, const SBuffer &payload) { - size_t hex_char_len = payload.len()*2+2; - char *hex_char = (char*) malloc(hex_char_len); - if (!hex_char) { return; } - ToHex_P((unsigned char*)payload.getBuffer(), payload.len(), hex_char, hex_char_len); - - const __FlashStringHelper* command_name = nullptr; - uint8_t conv_direction; - Z_XYZ_Var xyz; - - - for (uint32_t i = 0; i < sizeof(Z_Commands) / sizeof(Z_Commands[0]); i++) { - const Z_CommandConverter *conv = &Z_Commands[i]; - uint16_t conv_cluster = pgm_read_word(&conv->cluster); - if (conv_cluster == cluster) { - - uint8_t conv_cmd = pgm_read_byte(&conv->cmd); - conv_direction = pgm_read_byte(&conv->direction); - if ((0xFF == conv_cmd) || (cmd == conv_cmd)) { - - if ((direction && (conv_direction & 0x02)) || (!direction && (conv_direction & 0x01))) { - - - - - const char * p = conv->param; - - bool match = true; - for (uint8_t i = 0; i < payload.len(); i++) { - const char c1 = pgm_read_byte(p); - const char c2 = pgm_read_byte(p+1); - - if ((0x00 == c1) || isXYZ(c1)) { - break; - } - const char * p2 = p; - uint32_t nextbyte = parseHex_P(&p2, 2); - - if (nextbyte != payload.get8(i)) { - match = false; - break; - } - p += 2; - } - if (match) { - command_name = (const __FlashStringHelper*) conv->tasmota_cmd; - parseXYZ(conv->param, payload, &xyz); - if (0xFF == conv_cmd) { - - xyz.z = xyz.y; - xyz.z_type = xyz.y_type; - xyz.y = xyz.x; - xyz.y_type = xyz.x_type; - xyz.x = cmd; - xyz.x_type = 1; - } - break; - } - } - } - } - } - - - - - char attrid_str[12]; - snprintf_P(attrid_str, sizeof(attrid_str), PSTR("%04X%c%02X"), cluster, direction ? '<' : '!', cmd); - json[attrid_str] = hex_char; - free(hex_char); - - if (command_name) { - - - if (conv_direction & 0x80) { - - - String command_name2 = String(command_name); - if ((cluster == 0x0500) && (cmd == 0x00)) { - - json[command_name] = xyz.x; - json[command_name2 + F("Ext")] = xyz.y; - json[command_name2 + F("Zone")] = xyz.z; - } else if ((cluster == 0x0004) && ((cmd == 0x00) || (cmd == 0x01) || (cmd == 0x03))) { - - json[command_name] = xyz.y; - json[command_name2 + F("Status")] = xyz.x; - json[command_name2 + F("StatusMsg")] = getZigbeeStatusMessage(xyz.x); - } else if ((cluster == 0x0004) && (cmd == 0x02)) { - - json[command_name2 + F("Capacity")] = xyz.x; - json[command_name2 + F("Count")] = xyz.y; - JsonArray &arr = json.createNestedArray(command_name); - for (uint32_t i = 0; i < xyz.y; i++) { - arr.add(payload.get16(2 + 2*i)); - } - } else if ((cluster == 0x0005) && ((cmd == 0x00) || (cmd == 0x02) || (cmd == 0x03))) { - - json[command_name2 + F("Status")] = xyz.x; - json[command_name2 + F("StatusMsg")] = getZigbeeStatusMessage(xyz.x); - json[F("GroupId")] = xyz.y; - json[F("SceneId")] = xyz.z; - } else if ((cluster == 0x0005) && (cmd == 0x01)) { - - json[command_name2 + F("Status")] = xyz.x; - json[command_name2 + F("StatusMsg")] = getZigbeeStatusMessage(xyz.x); - json[F("GroupId")] = xyz.y; - json[F("SceneId")] = xyz.z; - String scene_payload = json[attrid_str]; - json[F("ScenePayload")] = scene_payload.substring(8); - } else if ((cluster == 0x0005) && (cmd == 0x03)) { - - json[command_name2 + F("Status")] = xyz.x; - json[command_name2 + F("StatusMsg")] = getZigbeeStatusMessage(xyz.x); - json[F("GroupId")] = xyz.y; - } else if ((cluster == 0x0005) && (cmd == 0x06)) { - - json[command_name2 + F("Status")] = xyz.x; - json[command_name2 + F("StatusMsg")] = getZigbeeStatusMessage(xyz.x); - json[F("Capacity")] = xyz.y; - json[F("GroupId")] = xyz.z; - String scene_payload = json[attrid_str]; - json[F("ScenePayload")] = scene_payload.substring(8); - } - } else { - if (0 == xyz.x_type) { - json[command_name] = true; - } else if (0 == xyz.y_type) { - json[command_name] = xyz.x; - } else { - - JsonArray &arr = json.createNestedArray(command_name); - arr.add(xyz.x); - arr.add(xyz.y); - if (xyz.z_type) { - arr.add(xyz.z); - } - } - } - } -} - - - - - -const __FlashStringHelper* zigbeeFindCommand(const char *command, uint16_t *cluster, uint16_t *cmd) { - for (uint32_t i = 0; i < sizeof(Z_Commands) / sizeof(Z_Commands[0]); i++) { - const Z_CommandConverter *conv = &Z_Commands[i]; - uint8_t conv_direction = pgm_read_byte(&conv->direction); - uint8_t conv_cmd = pgm_read_byte(&conv->cmd); - uint16_t conv_cluster = pgm_read_word(&conv->cluster); - if ((conv_direction & 0x01) && (0 == strcasecmp_P(command, conv->tasmota_cmd))) { - *cluster = conv_cluster; - *cmd = conv_cmd; - return (const __FlashStringHelper*) conv->param; - } - } - - return nullptr; -} - - -inline char hexDigit(uint32_t h) { - uint32_t nybble = h & 0x0F; - return (nybble > 9) ? 'A' - 10 + nybble : '0' + nybble; -} - - -String zigbeeCmdAddParams(const char *zcl_cmd_P, uint32_t x, uint32_t y, uint32_t z) { - size_t len = strlen_P(zcl_cmd_P); - char zcl_cmd[len+1]; - strcpy_P(zcl_cmd, zcl_cmd_P); - - char *p = zcl_cmd; - while (*p) { - if (isXYZ(*p) && (*p == *(p+1))) { - uint8_t val; - switch (*p) { - case 'x': - val = x & 0xFF; - x = x >> 8; - break; - case 'y': - val = y & 0xFF; - y = y >> 8; - break; - case 'z': - val = z & 0xFF; - z = z >> 8; - break; - } - *p = hexDigit(val >> 4); - *(p+1) = hexDigit(val); - p++; - } - p++; - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SendZCLCommand_P: zcl_cmd = %s"), zcl_cmd); - - return String(zcl_cmd); -} - -const char kZ_Alias[] PROGMEM = "OFF|" D_OFF "|" D_FALSE "|" D_STOP "|" "OPEN" "|" - "ON|" D_ON "|" D_TRUE "|" D_START "|" "CLOSE" "|" - "TOGGLE|" D_TOGGLE "|" - "ALL" ; - -const uint8_t kZ_Numbers[] PROGMEM = { 0,0,0,0,0, - 1,1,1,1,1, - 2,2, - 255 }; - - -uint32_t ZigbeeAliasOrNumber(const char *state_text) { - char command[16]; - int state_number = GetCommandCode(command, sizeof(command), state_text, kZ_Alias); - if (state_number >= 0) { - - return pgm_read_byte(kZ_Numbers + state_number); - } else { - - return strtoul(state_text, nullptr, 0); - } -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" -#ifdef USE_ZIGBEE - - - -const uint8_t ZIGBEE_STATUS_OK = 0; -const uint8_t ZIGBEE_STATUS_BOOT = 1; -const uint8_t ZIGBEE_STATUS_RESET_CONF = 2; -const uint8_t ZIGBEE_STATUS_STARTING = 3; -const uint8_t ZIGBEE_STATUS_PERMITJOIN_CLOSE = 20; -const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_60 = 21; -const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_XX = 22; -const uint8_t ZIGBEE_STATUS_DEVICE_ANNOUNCE = 30; -const uint8_t ZIGBEE_STATUS_NODE_DESC = 31; -const uint8_t ZIGBEE_STATUS_ACTIVE_EP = 32; -const uint8_t ZIGBEE_STATUS_SIMPLE_DESC = 33; -const uint8_t ZIGBEE_STATUS_DEVICE_INDICATION = 34; -const uint8_t ZIGBEE_STATUS_CC_VERSION = 50; -const uint8_t ZIGBEE_STATUS_CC_INFO = 51; -const uint8_t ZIGBEE_STATUS_UNSUPPORTED_VERSION = 98; -const uint8_t ZIGBEE_STATUS_ABORT = 99; - -typedef int32_t (*ZB_Func)(uint8_t value); -typedef int32_t (*ZB_RecvMsgFunc)(int32_t res, const class SBuffer &buf); - -typedef union Zigbee_Instruction { - struct { - uint8_t i; - uint8_t d8; - uint16_t d16; - } i; - const void *p; -} Zigbee_Instruction; - -typedef struct Zigbee_Instruction_Type { - uint8_t instr; - uint8_t data; -} Zigbee_Instruction_Type; - -enum Zigbee_StateMachine_Instruction_Set { - - ZGB_INSTR_4_BYTES = 0, - ZGB_INSTR_NOOP = 0, - ZGB_INSTR_LABEL, - ZGB_INSTR_GOTO, - ZGB_INSTR_ON_ERROR_GOTO, - ZGB_INSTR_ON_TIMEOUT_GOTO, - ZGB_INSTR_WAIT, - ZGB_INSTR_WAIT_FOREVER, - ZGB_INSTR_STOP, - - - ZGB_INSTR_8_BYTES = 0x80, - ZGB_INSTR_CALL = 0x80, - ZGB_INSTR_LOG, - ZGB_INSTR_MQTT_STATE, - ZGB_INSTR_SEND, - ZGB_INSTR_WAIT_UNTIL, - ZGB_INSTR_WAIT_RECV, - ZGB_ON_RECV_UNEXPECTED, - - - ZGB_INSTR_12_BYTES = 0xF0, - ZGB_INSTR_WAIT_RECV_CALL, -}; - -#define ZI_NOOP() { .i = { ZGB_INSTR_NOOP, 0x00, 0x0000} }, -#define ZI_LABEL(x) { .i = { ZGB_INSTR_LABEL, (x), 0x0000} }, -#define ZI_GOTO(x) { .i = { ZGB_INSTR_GOTO, (x), 0x0000} }, -#define ZI_ON_ERROR_GOTO(x) { .i = { ZGB_INSTR_ON_ERROR_GOTO, (x), 0x0000} }, -#define ZI_ON_TIMEOUT_GOTO(x) { .i = { ZGB_INSTR_ON_TIMEOUT_GOTO, (x), 0x0000} }, -#define ZI_WAIT(x) { .i = { ZGB_INSTR_WAIT, 0x00, (x)} }, -#define ZI_WAIT_FOREVER() { .i = { ZGB_INSTR_WAIT_FOREVER, 0x00, 0x0000} }, -#define ZI_STOP(x) { .i = { ZGB_INSTR_STOP, (x), 0x0000} }, - -#define ZI_CALL(f,x) { .i = { ZGB_INSTR_CALL, (x), 0x0000} }, { .p = (const void*)(f) }, -#define ZI_LOG(x,m) { .i = { ZGB_INSTR_LOG, (x), 0x0000 } }, { .p = ((const void*)(m)) }, -#define ZI_MQTT_STATE(x,m) { .i = { ZGB_INSTR_MQTT_STATE, (x), 0x0000 } }, { .p = ((const void*)(m)) }, -#define ZI_ON_RECV_UNEXPECTED(f) { .i = { ZGB_ON_RECV_UNEXPECTED, 0x00, 0x0000} }, { .p = (const void*)(f) }, -#define ZI_SEND(m) { .i = { ZGB_INSTR_SEND, sizeof(m), 0x0000} }, { .p = (const void*)(m) }, -#define ZI_WAIT_RECV(x,m) { .i = { ZGB_INSTR_WAIT_RECV, sizeof(m), (x)} }, { .p = (const void*)(m) }, -#define ZI_WAIT_UNTIL(x,m) { .i = { ZGB_INSTR_WAIT_UNTIL, sizeof(m), (x)} }, { .p = (const void*)(m) }, -#define ZI_WAIT_RECV_FUNC(x,m,f) { .i = { ZGB_INSTR_WAIT_RECV_CALL, sizeof(m), (x)} }, { .p = (const void*)(m) }, { .p = (const void*)(f) }, - - -const uint8_t ZIGBEE_LABEL_START = 10; -const uint8_t ZIGBEE_LABEL_READY = 20; -const uint8_t ZIGBEE_LABEL_MAIN_LOOP = 21; -const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_CLOSE = 30; -const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60 = 31; -const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX = 32; - -const uint8_t ZIGBEE_LABEL_ABORT = 99; -const uint8_t ZIGBEE_LABEL_UNSUPPORTED_VERSION = 98; - -struct ZigbeeStatus { - bool active = true; - bool state_machine = false; - bool state_waiting = false; - bool state_no_timeout = false; - bool ready = false; - uint8_t on_error_goto = ZIGBEE_LABEL_ABORT; - uint8_t on_timeout_goto = ZIGBEE_LABEL_ABORT; - int16_t pc = 0; - uint32_t next_timeout = 0; - - uint8_t *recv_filter = nullptr; - bool recv_until = false; - size_t recv_filter_len = 0; - ZB_RecvMsgFunc recv_func = nullptr; - ZB_RecvMsgFunc recv_unexpected = nullptr; - - bool init_phase = true; -}; -struct ZigbeeStatus zigbee; - -SBuffer *zigbee_buffer = nullptr; - - - - - -#define Z_B0(a) (uint8_t)( ((a) ) & 0xFF ) -#define Z_B1(a) (uint8_t)( ((a) >> 8) & 0xFF ) -#define Z_B2(a) (uint8_t)( ((a) >> 16) & 0xFF ) -#define Z_B3(a) (uint8_t)( ((a) >> 24) & 0xFF ) -#define Z_B4(a) (uint8_t)( ((a) >> 32) & 0xFF ) -#define Z_B5(a) (uint8_t)( ((a) >> 40) & 0xFF ) -#define Z_B6(a) (uint8_t)( ((a) >> 48) & 0xFF ) -#define Z_B7(a) (uint8_t)( ((a) >> 56) & 0xFF ) - -#define ZBM(n,x...) const uint8_t n[] PROGMEM = { x }; - -#define ZBR(n,x...) uint8_t n[] = { x }; -#define ZBW(n,x...) { const uint8_t n ##t[] = { x }; memcpy(n, n ##t, sizeof(n)); } - -#define USE_ZIGBEE_CHANNEL_MASK (1 << (USE_ZIGBEE_CHANNEL)) - - - -ZBM(ZBS_RESET, Z_AREQ | Z_SYS, SYS_RESET, 0x00 ) -ZBM(ZBR_RESET, Z_AREQ | Z_SYS, SYS_RESET_IND ) - -ZBM(ZBS_VERSION, Z_SREQ | Z_SYS, SYS_VERSION ) -ZBM(ZBR_VERSION, Z_SRSP | Z_SYS, SYS_VERSION ) - - -ZBM(ZBS_ZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_READ, ZNP_HAS_CONFIGURED & 0xFF, ZNP_HAS_CONFIGURED >> 8, 0x00 ) -ZBM(ZBR_ZNPHC, Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_SUCCESS, 0x01 , 0x55) - - -ZBM(ZBS_PAN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PANID ) -ZBR(ZBR_PAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PANID, 0x02 , - Z_B0(USE_ZIGBEE_PANID), Z_B1(USE_ZIGBEE_PANID) ) - -ZBM(ZBS_EXTPAN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_EXTENDED_PAN_ID ) -ZBR(ZBR_EXTPAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_EXTENDED_PAN_ID, - 0x08 , - Z_B0(USE_ZIGBEE_EXTPANID), Z_B1(USE_ZIGBEE_EXTPANID), Z_B2(USE_ZIGBEE_EXTPANID), Z_B3(USE_ZIGBEE_EXTPANID), - Z_B4(USE_ZIGBEE_EXTPANID), Z_B5(USE_ZIGBEE_EXTPANID), Z_B6(USE_ZIGBEE_EXTPANID), Z_B7(USE_ZIGBEE_EXTPANID), - ) - -ZBM(ZBS_CHANN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_CHANLIST ) -ZBR(ZBR_CHANN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_CHANLIST, - 0x04 , - Z_B0(USE_ZIGBEE_CHANNEL_MASK), Z_B1(USE_ZIGBEE_CHANNEL_MASK), Z_B2(USE_ZIGBEE_CHANNEL_MASK), Z_B3(USE_ZIGBEE_CHANNEL_MASK), - ) - -ZBM(ZBS_PFGK, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PRECFGKEY ) -ZBR(ZBR_PFGK, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PRECFGKEY, - 0x10 , - Z_B0(USE_ZIGBEE_PRECFGKEY_L), Z_B1(USE_ZIGBEE_PRECFGKEY_L), Z_B2(USE_ZIGBEE_PRECFGKEY_L), Z_B3(USE_ZIGBEE_PRECFGKEY_L), - Z_B4(USE_ZIGBEE_PRECFGKEY_L), Z_B5(USE_ZIGBEE_PRECFGKEY_L), Z_B6(USE_ZIGBEE_PRECFGKEY_L), Z_B7(USE_ZIGBEE_PRECFGKEY_L), - Z_B0(USE_ZIGBEE_PRECFGKEY_H), Z_B1(USE_ZIGBEE_PRECFGKEY_H), Z_B2(USE_ZIGBEE_PRECFGKEY_H), Z_B3(USE_ZIGBEE_PRECFGKEY_H), - Z_B4(USE_ZIGBEE_PRECFGKEY_H), Z_B5(USE_ZIGBEE_PRECFGKEY_H), Z_B6(USE_ZIGBEE_PRECFGKEY_H), Z_B7(USE_ZIGBEE_PRECFGKEY_H), - - ) - -ZBM(ZBS_PFGKEN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PRECFGKEYS_ENABLE ) -ZBM(ZBR_PFGKEN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PRECFGKEYS_ENABLE, - 0x01 , 0x00 ) - - - -ZBM(ZBR_W_OK, Z_SRSP | Z_SAPI, SAPI_WRITE_CONFIGURATION, Z_SUCCESS ) -ZBM(ZBR_WNV_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_WRITE, Z_SUCCESS ) - - -ZBM(ZBS_FACTRES, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 , 0x02 ) - -ZBR(ZBS_W_PAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PANID, 0x02 , Z_B0(USE_ZIGBEE_PANID), Z_B1(USE_ZIGBEE_PANID) ) - -ZBR(ZBS_W_EXTPAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_EXTENDED_PAN_ID, 0x08 , - Z_B0(USE_ZIGBEE_EXTPANID), Z_B1(USE_ZIGBEE_EXTPANID), Z_B2(USE_ZIGBEE_EXTPANID), Z_B3(USE_ZIGBEE_EXTPANID), - Z_B4(USE_ZIGBEE_EXTPANID), Z_B5(USE_ZIGBEE_EXTPANID), Z_B6(USE_ZIGBEE_EXTPANID), Z_B7(USE_ZIGBEE_EXTPANID) - ) - -ZBR(ZBS_W_CHANN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_CHANLIST, 0x04 , - Z_B0(USE_ZIGBEE_CHANNEL_MASK), Z_B1(USE_ZIGBEE_CHANNEL_MASK), Z_B2(USE_ZIGBEE_CHANNEL_MASK), Z_B3(USE_ZIGBEE_CHANNEL_MASK), - ) - -ZBM(ZBS_W_LOGTYP, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_LOGICAL_TYPE, 0x01 , 0x00 ) - -ZBR(ZBS_W_PFGK, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PRECFGKEY, - 0x10 , - Z_B0(USE_ZIGBEE_PRECFGKEY_L), Z_B1(USE_ZIGBEE_PRECFGKEY_L), Z_B2(USE_ZIGBEE_PRECFGKEY_L), Z_B3(USE_ZIGBEE_PRECFGKEY_L), - Z_B4(USE_ZIGBEE_PRECFGKEY_L), Z_B5(USE_ZIGBEE_PRECFGKEY_L), Z_B6(USE_ZIGBEE_PRECFGKEY_L), Z_B7(USE_ZIGBEE_PRECFGKEY_L), - Z_B0(USE_ZIGBEE_PRECFGKEY_H), Z_B1(USE_ZIGBEE_PRECFGKEY_H), Z_B2(USE_ZIGBEE_PRECFGKEY_H), Z_B3(USE_ZIGBEE_PRECFGKEY_H), - Z_B4(USE_ZIGBEE_PRECFGKEY_H), Z_B5(USE_ZIGBEE_PRECFGKEY_H), Z_B6(USE_ZIGBEE_PRECFGKEY_H), Z_B7(USE_ZIGBEE_PRECFGKEY_H), - - ) - -ZBM(ZBS_W_PFGKEN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PRECFGKEYS_ENABLE, 0x01 , 0x00 ) - -ZBM(ZBS_WNV_SECMODE, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, Z_B0(CONF_TCLK_TABLE_START), Z_B1(CONF_TCLK_TABLE_START), - 0x00 , 0x20 , - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x5a, 0x69, 0x67, 0x42, 0x65, 0x65, 0x41, 0x6c, - 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x39, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) - -ZBM(ZBS_W_ZDODCB, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_ZDO_DIRECT_CB, 0x01 , 0x01 ) - -ZBM(ZBS_WNV_INITZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_ITEM_INIT, ZNP_HAS_CONFIGURED & 0xFF, ZNP_HAS_CONFIGURED >> 8, - 0x01, 0x00 , 0x01 , 0x00 ) - - -ZBM(ZBR_WNV_INIT_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_ITEM_INIT ) - - -ZBM(ZBS_WNV_ZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, Z_B0(ZNP_HAS_CONFIGURED), Z_B1(ZNP_HAS_CONFIGURED), - 0x00 , 0x01 , 0x55 ) - -ZBM(ZBS_STARTUPFROMAPP, Z_SREQ | Z_ZDO, ZDO_STARTUP_FROM_APP, 100, 0 ) -ZBM(ZBR_STARTUPFROMAPP, Z_SRSP | Z_ZDO, ZDO_STARTUP_FROM_APP ) -ZBM(AREQ_STARTUPFROMAPP, Z_AREQ | Z_ZDO, ZDO_STATE_CHANGE_IND, ZDO_DEV_ZB_COORD ) - -ZBM(ZBS_GETDEVICEINFO, Z_SREQ | Z_UTIL, Z_UTIL_GET_DEVICE_INFO ) -ZBM(ZBR_GETDEVICEINFO, Z_SRSP | Z_UTIL, Z_UTIL_GET_DEVICE_INFO, Z_SUCCESS ) -# 268 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" -ZBM(ZBS_ZDO_NODEDESCREQ, Z_SREQ | Z_ZDO, ZDO_NODE_DESC_REQ, 0x00, 0x00 , 0x00, 0x00 ) -ZBM(ZBR_ZDO_NODEDESCREQ, Z_SRSP | Z_ZDO, ZDO_NODE_DESC_REQ, Z_SUCCESS ) - -ZBM(AREQ_ZDO_NODEDESCRSP, Z_AREQ | Z_ZDO, ZDO_NODE_DESC_RSP) -# 286 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" -ZBM(ZBS_ZDO_ACTIVEEPREQ, Z_SREQ | Z_ZDO, ZDO_ACTIVE_EP_REQ, 0x00, 0x00, 0x00, 0x00) -ZBM(ZBR_ZDO_ACTIVEEPREQ, Z_SRSP | Z_ZDO, ZDO_ACTIVE_EP_REQ, Z_SUCCESS) -ZBM(ZBR_ZDO_ACTIVEEPRSP_NONE, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP, 0x00, 0x00 , Z_SUCCESS, - 0x00, 0x00 , 0x00 ) -ZBM(ZBR_ZDO_ACTIVEEPRSP_OK, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP, 0x00, 0x00 , Z_SUCCESS, - 0x00, 0x00 , 0x02 , 0x0B, 0x01 ) - - -ZBM(ZBS_AF_REGISTER01, Z_SREQ | Z_AF, AF_REGISTER, 0x01 , Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), - 0x05, 0x00 , 0x00 , 0x00 , - 0x00 , 0x00 ) -ZBM(ZBR_AF_REGISTER, Z_SRSP | Z_AF, AF_REGISTER, Z_SUCCESS) -ZBM(ZBS_AF_REGISTER0B, Z_SREQ | Z_AF, AF_REGISTER, 0x0B , Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), - 0x05, 0x00 , 0x00 , 0x00 , - 0x00 , 0x00 ) - -ZBM(ZBS_PERMITJOINREQ_CLOSE, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x02 , - 0x00, 0x00 , 0x00 , 0x00 ) -ZBM(ZBR_PERMITJOINREQ, Z_SRSP | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, Z_SUCCESS) -ZBM(ZBR_PERMITJOIN_AREQ_RSP, Z_AREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_RSP, 0x00, 0x00 , Z_SUCCESS ) - - -void Z_UpdateConfig(uint8_t zb_channel, uint16_t zb_pan_id, uint64_t zb_ext_panid, uint64_t zb_precfgkey_l, uint64_t zb_precfgkey_h) { - uint32_t zb_channel_mask = (1 << zb_channel); - - ZBW(ZBR_PAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PANID, 0x02 , - Z_B0(zb_pan_id), Z_B1(zb_pan_id) ) - - ZBW(ZBR_EXTPAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_EXTENDED_PAN_ID, - 0x08 , - Z_B0(zb_ext_panid), Z_B1(zb_ext_panid), Z_B2(zb_ext_panid), Z_B3(zb_ext_panid), - Z_B4(zb_ext_panid), Z_B5(zb_ext_panid), Z_B6(zb_ext_panid), Z_B7(zb_ext_panid), - ) - - ZBW(ZBR_CHANN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_CHANLIST, - 0x04 , - Z_B0(zb_channel_mask), Z_B1(zb_channel_mask), Z_B2(zb_channel_mask), Z_B3(zb_channel_mask), - ) - - ZBW(ZBR_PFGK, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PRECFGKEY, - 0x10 , - Z_B0(zb_precfgkey_l), Z_B1(zb_precfgkey_l), Z_B2(zb_precfgkey_l), Z_B3(zb_precfgkey_l), - Z_B4(zb_precfgkey_l), Z_B5(zb_precfgkey_l), Z_B6(zb_precfgkey_l), Z_B7(zb_precfgkey_l), - Z_B0(zb_precfgkey_h), Z_B1(zb_precfgkey_h), Z_B2(zb_precfgkey_h), Z_B3(zb_precfgkey_h), - Z_B4(zb_precfgkey_h), Z_B5(zb_precfgkey_h), Z_B6(zb_precfgkey_h), Z_B7(zb_precfgkey_h), - - ) - - ZBW(ZBS_W_PAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PANID, 0x02 , Z_B0(zb_pan_id), Z_B1(zb_pan_id) ) - - ZBW(ZBS_W_EXTPAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_EXTENDED_PAN_ID, 0x08 , - Z_B0(zb_ext_panid), Z_B1(zb_ext_panid), Z_B2(zb_ext_panid), Z_B3(zb_ext_panid), - Z_B4(zb_ext_panid), Z_B5(zb_ext_panid), Z_B6(zb_ext_panid), Z_B7(zb_ext_panid) - ) - - ZBW(ZBS_W_CHANN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_CHANLIST, 0x04 , - Z_B0(zb_channel_mask), Z_B1(zb_channel_mask), Z_B2(zb_channel_mask), Z_B3(zb_channel_mask), - ) - - ZBW(ZBS_W_PFGK, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PRECFGKEY, - 0x10 , - Z_B0(zb_precfgkey_l), Z_B1(zb_precfgkey_l), Z_B2(zb_precfgkey_l), Z_B3(zb_precfgkey_l), - Z_B4(zb_precfgkey_l), Z_B5(zb_precfgkey_l), Z_B6(zb_precfgkey_l), Z_B7(zb_precfgkey_l), - Z_B0(zb_precfgkey_h), Z_B1(zb_precfgkey_h), Z_B2(zb_precfgkey_h), Z_B3(zb_precfgkey_h), - Z_B4(zb_precfgkey_h), Z_B5(zb_precfgkey_h), Z_B6(zb_precfgkey_h), Z_B7(zb_precfgkey_h), - ) -} - -const char kCheckingDeviceConfiguration[] PROGMEM = D_LOG_ZIGBEE "checking device configuration"; -const char kConfigured[] PROGMEM = "Configured, starting coordinator"; -const char kStarted[] PROGMEM = "Started"; -const char kZigbeeStarted[] PROGMEM = D_LOG_ZIGBEE "Zigbee started"; -const char kResetting[] PROGMEM = "Resetting configuration"; -const char kZNP12[] PROGMEM = "Only ZNP 1.2 is currently supported"; -const char kAbort[] PROGMEM = "Abort"; -const char kZigbeeAbort[] PROGMEM = D_LOG_ZIGBEE "Abort"; - -static const Zigbee_Instruction zb_prog[] PROGMEM = { - ZI_LABEL(0) - ZI_NOOP() - ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) - ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT) - ZI_ON_RECV_UNEXPECTED(&Z_Recv_Default) - ZI_WAIT(10500) - ZI_ON_ERROR_GOTO(50) - - - - ZI_SEND(ZBS_RESET) - ZI_WAIT_RECV_FUNC(5000, ZBR_RESET, &Z_Reboot) - ZI_WAIT(100) - ZI_LOG(LOG_LEVEL_DEBUG, kCheckingDeviceConfiguration) - ZI_SEND(ZBS_ZNPHC) - ZI_WAIT_RECV(2000, ZBR_ZNPHC) - ZI_SEND(ZBS_VERSION) - ZI_WAIT_RECV_FUNC(2000, ZBR_VERSION, &Z_ReceiveCheckVersion) - ZI_SEND(ZBS_PAN) - ZI_WAIT_RECV(1000, ZBR_PAN) - ZI_SEND(ZBS_EXTPAN) - ZI_WAIT_RECV(1000, ZBR_EXTPAN) - ZI_SEND(ZBS_CHANN) - ZI_WAIT_RECV(1000, ZBR_CHANN) - ZI_SEND(ZBS_PFGK) - ZI_WAIT_RECV(1000, ZBR_PFGK) - ZI_SEND(ZBS_PFGKEN) - ZI_WAIT_RECV(1000, ZBR_PFGKEN) - - - - ZI_LABEL(ZIGBEE_LABEL_START) - ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfigured) - ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) - - -ZI_SEND(ZBS_STARTUPFROMAPP) - ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) - ZI_WAIT_UNTIL(10000, AREQ_STARTUPFROMAPP) - ZI_SEND(ZBS_GETDEVICEINFO) - ZI_WAIT_RECV_FUNC(2000, ZBR_GETDEVICEINFO, &Z_ReceiveDeviceInfo) - - ZI_SEND(ZBS_ZDO_NODEDESCREQ) - ZI_WAIT_RECV(1000, ZBR_ZDO_NODEDESCREQ) - ZI_WAIT_UNTIL(5000, AREQ_ZDO_NODEDESCRSP) - ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) - ZI_WAIT_RECV(1000, ZBR_ZDO_ACTIVEEPREQ) - ZI_WAIT_UNTIL(1000, ZBR_ZDO_ACTIVEEPRSP_NONE) - ZI_SEND(ZBS_AF_REGISTER01) - ZI_WAIT_RECV(1000, ZBR_AF_REGISTER) - ZI_SEND(ZBS_AF_REGISTER0B) - ZI_WAIT_RECV(1000, ZBR_AF_REGISTER) - - ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) - ZI_WAIT_RECV(1000, ZBR_ZDO_ACTIVEEPREQ) - ZI_WAIT_UNTIL(1000, ZBR_ZDO_ACTIVEEPRSP_OK) - ZI_SEND(ZBS_PERMITJOINREQ_CLOSE) - ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) - ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) - - ZI_LABEL(ZIGBEE_LABEL_READY) - ZI_MQTT_STATE(ZIGBEE_STATUS_OK, kStarted) - ZI_LOG(LOG_LEVEL_INFO, kZigbeeStarted) - ZI_CALL(&Z_State_Ready, 1) - ZI_CALL(&Z_Load_Devices, 0) - ZI_CALL(&Z_Query_Bulbs, 0) - ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP) - ZI_WAIT_FOREVER() - ZI_GOTO(ZIGBEE_LABEL_READY) - - ZI_LABEL(50) - ZI_MQTT_STATE(ZIGBEE_STATUS_RESET_CONF, kResetting) - - ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) - ZI_SEND(ZBS_FACTRES) - ZI_WAIT_RECV(1000, ZBR_W_OK) - ZI_SEND(ZBS_RESET) - ZI_WAIT_RECV(5000, ZBR_RESET) - ZI_SEND(ZBS_W_PAN) - ZI_WAIT_RECV(1000, ZBR_W_OK) - ZI_SEND(ZBS_W_EXTPAN) - ZI_WAIT_RECV(1000, ZBR_W_OK) - ZI_SEND(ZBS_W_CHANN) - ZI_WAIT_RECV(1000, ZBR_W_OK) - ZI_SEND(ZBS_W_LOGTYP) - ZI_WAIT_RECV(1000, ZBR_W_OK) - ZI_SEND(ZBS_W_PFGK) - ZI_WAIT_RECV(1000, ZBR_W_OK) - ZI_SEND(ZBS_W_PFGKEN) - ZI_WAIT_RECV(1000, ZBR_W_OK) - ZI_SEND(ZBS_WNV_SECMODE) - ZI_WAIT_RECV(1000, ZBR_WNV_OK) - ZI_SEND(ZBS_W_ZDODCB) - ZI_WAIT_RECV(1000, ZBR_W_OK) - - ZI_SEND(ZBS_WNV_INITZNPHC) - ZI_WAIT_RECV_FUNC(1000, ZBR_WNV_INIT_OK, &Z_CheckNVWrite) - ZI_SEND(ZBS_WNV_ZNPHC) - ZI_WAIT_RECV(1000, ZBR_WNV_OK) - - - ZI_GOTO(ZIGBEE_LABEL_START) - - ZI_LABEL(ZIGBEE_LABEL_UNSUPPORTED_VERSION) - ZI_MQTT_STATE(ZIGBEE_STATUS_UNSUPPORTED_VERSION, kZNP12) - ZI_GOTO(ZIGBEE_LABEL_ABORT) - - ZI_LABEL(ZIGBEE_LABEL_ABORT) - ZI_MQTT_STATE(ZIGBEE_STATUS_ABORT, kAbort) - ZI_LOG(LOG_LEVEL_ERROR, kZigbeeAbort) - ZI_STOP(ZIGBEE_LABEL_ABORT) -}; - -uint8_t ZigbeeGetInstructionSize(uint8_t instr) { - if (instr >= ZGB_INSTR_12_BYTES) { - return 3; - } else if (instr >= ZGB_INSTR_8_BYTES) { - return 2; - } else { - return 1; - } -} - -void ZigbeeGotoLabel(uint8_t label) { - - uint16_t goto_pc = 0xFFFF; - uint8_t cur_instr = 0; - uint8_t cur_d8 = 0; - uint8_t cur_instr_len = 1; - - for (uint32_t i = 0; i < sizeof(zb_prog)/sizeof(zb_prog[0]); i += cur_instr_len) { - const Zigbee_Instruction *cur_instr_line = &zb_prog[i]; - cur_instr = pgm_read_byte(&cur_instr_line->i.i); - cur_d8 = pgm_read_byte(&cur_instr_line->i.d8); - - - if (ZGB_INSTR_LABEL == cur_instr) { - - if (label == cur_d8) { - - zigbee.pc = i; - zigbee.state_machine = true; - zigbee.state_waiting = false; - return; - } - } - - cur_instr_len = ZigbeeGetInstructionSize(cur_instr); - } - - - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Goto label not found, label=%d pc=%d"), label, zigbee.pc); - if (ZIGBEE_LABEL_ABORT != label) { - - ZigbeeGotoLabel(ZIGBEE_LABEL_ABORT); - } else { - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Label Abort (%d) not present, aborting Zigbee"), ZIGBEE_LABEL_ABORT); - zigbee.state_machine = false; - zigbee.active = false; - } -} - -void ZigbeeStateMachine_Run(void) { - uint8_t cur_instr = 0; - uint8_t cur_d8 = 0; - uint16_t cur_d16 = 0; - const void* cur_ptr1 = nullptr; - const void* cur_ptr2 = nullptr; - uint32_t now = millis(); - - if (zigbee.state_waiting) { - - if ((zigbee.next_timeout) && (now > zigbee.next_timeout)) { - if (!zigbee.state_no_timeout) { - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "timeout, goto label %d"), zigbee.on_timeout_goto); - ZigbeeGotoLabel(zigbee.on_timeout_goto); - } else { - zigbee.state_waiting = false; - } - } - } - - while ((zigbee.state_machine) && (!zigbee.state_waiting)) { - - zigbee.recv_filter = nullptr; - zigbee.recv_func = nullptr; - zigbee.recv_until = false; - zigbee.state_no_timeout = false; - - if (zigbee.pc > (sizeof(zb_prog)/sizeof(zb_prog[0]))) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Invalid pc: %d, aborting"), zigbee.pc); - zigbee.pc = -1; - } - if (zigbee.pc < 0) { - zigbee.state_machine = false; - return; - } - - - const Zigbee_Instruction *cur_instr_line = &zb_prog[zigbee.pc]; - cur_instr = pgm_read_byte(&cur_instr_line->i.i); - cur_d8 = pgm_read_byte(&cur_instr_line->i.d8); - cur_d16 = pgm_read_word(&cur_instr_line->i.d16); - if (cur_instr >= ZGB_INSTR_8_BYTES) { - cur_instr_line++; - cur_ptr1 = cur_instr_line->p; - } - if (cur_instr >= ZGB_INSTR_12_BYTES) { - cur_instr_line++; - cur_ptr2 = cur_instr_line->p; - } - - zigbee.pc += ZigbeeGetInstructionSize(cur_instr); - - switch (cur_instr) { - case ZGB_INSTR_NOOP: - case ZGB_INSTR_LABEL: - break; - case ZGB_INSTR_GOTO: - ZigbeeGotoLabel(cur_d8); - break; - case ZGB_INSTR_ON_ERROR_GOTO: - zigbee.on_error_goto = cur_d8; - break; - case ZGB_INSTR_ON_TIMEOUT_GOTO: - zigbee.on_timeout_goto = cur_d8; - break; - case ZGB_INSTR_WAIT: - zigbee.next_timeout = now + cur_d16; - zigbee.state_waiting = true; - zigbee.state_no_timeout = true; - break; - case ZGB_INSTR_WAIT_FOREVER: - zigbee.next_timeout = 0; - zigbee.state_waiting = true; - break; - case ZGB_INSTR_STOP: - zigbee.state_machine = false; - if (cur_d8) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Stopping (%d)"), cur_d8); - } - break; - case ZGB_INSTR_CALL: - if (cur_ptr1) { - uint32_t res; - res = (*((ZB_Func)cur_ptr1))(cur_d8); - if (res > 0) { - ZigbeeGotoLabel(res); - continue; - } else if (res == 0) { - - } else if (res == -1) { - - } else { - ZigbeeGotoLabel(zigbee.on_error_goto); - continue; - } - } - break; - case ZGB_INSTR_LOG: - AddLog_P(cur_d8, (char*) cur_ptr1); - break; - case ZGB_INSTR_MQTT_STATE: - { - const char *f_msg = (const char*) cur_ptr1; - char buf[strlen_P(f_msg) + 1]; - strcpy_P(buf, f_msg); - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{\"Status\":%d,\"Message\":\"%s\"}}"), - cur_d8, buf); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); - XdrvRulesProcess(); - } - break; - case ZGB_INSTR_SEND: - ZigbeeZNPSend((uint8_t*) cur_ptr1, cur_d8 ); - break; - case ZGB_INSTR_WAIT_UNTIL: - zigbee.recv_until = true; - case ZGB_INSTR_WAIT_RECV: - zigbee.recv_filter = (uint8_t *) cur_ptr1; - zigbee.recv_filter_len = cur_d8; - zigbee.next_timeout = now + cur_d16; - zigbee.state_waiting = true; - break; - case ZGB_ON_RECV_UNEXPECTED: - zigbee.recv_unexpected = (ZB_RecvMsgFunc) cur_ptr1; - break; - case ZGB_INSTR_WAIT_RECV_CALL: - zigbee.recv_filter = (uint8_t *) cur_ptr1; - zigbee.recv_filter_len = cur_d8; - zigbee.recv_func = (ZB_RecvMsgFunc) cur_ptr2; - zigbee.next_timeout = now + cur_d16; - zigbee.state_waiting = true; - break; - } - } -} - - - - -int32_t ZigbeeProcessInput(class SBuffer &buf) { - if (!zigbee.state_machine) { return -1; } - - - bool recv_filter_match = true; - bool recv_prefix_match = false; - if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) { - if (zigbee.recv_filter_len >= 2) { - recv_prefix_match = false; - if ( (pgm_read_byte(&zigbee.recv_filter[0]) == buf.get8(0)) && - (pgm_read_byte(&zigbee.recv_filter[1]) == buf.get8(1)) ) { - recv_prefix_match = true; - } - } - - for (uint32_t i = 0; i < zigbee.recv_filter_len; i++) { - if (pgm_read_byte(&zigbee.recv_filter[i]) != buf.get8(i)) { - recv_filter_match = false; - break; - } - } - } - - - int32_t res = -1; - - - - - - if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) { - if (!recv_prefix_match) { - res = -1; - } else { - if (recv_filter_match) { - res = 0; - } else { - if (zigbee.recv_until) { - res = -1; - } else { - res = -2; - } - } - } - } else { - res = -1; - } - - if (recv_prefix_match) { - if (zigbee.recv_func) { - res = (*zigbee.recv_func)(res, buf); - } - } - if (-1 == res) { - - if (zigbee.recv_unexpected) { - res = (*zigbee.recv_unexpected)(res, buf); - } - } - - - if (0 == res) { - - zigbee.state_waiting = false; - } else if (res > 0) { - ZigbeeGotoLabel(res); - } else if (-1 == res) { - - - } else { - - ZigbeeGotoLabel(zigbee.on_error_goto); - } -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" -#ifdef USE_ZIGBEE -# 29 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" -int32_t Z_ReceiveDeviceInfo(int32_t res, class SBuffer &buf) { - - - - - - - - Z_IEEEAddress long_adr = buf.get64(3); - Z_ShortAddress short_adr = buf.get16(11); - uint8_t device_type = buf.get8(13); - uint8_t device_state = buf.get8(14); - uint8_t device_associated = buf.get8(15); - - - localIEEEAddr = long_adr; - - char hex[20]; - Uint64toHex(long_adr, hex, 64); - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\"" - ",\"DeviceType\":%d,\"DeviceState\":%d" - ",\"NumAssocDevices\":%d"), - ZIGBEE_STATUS_CC_INFO, hex, short_adr, device_type, device_state, - device_associated); - - if (device_associated > 0) { - uint idx = 16; - ResponseAppend_P(PSTR(",\"AssocDevicesList\":[")); - for (uint32_t i = 0; i < device_associated; i++) { - if (i > 0) { ResponseAppend_P(PSTR(",")); } - ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(idx)); - idx += 2; - } - ResponseAppend_P(PSTR("]")); - } - - ResponseJsonEnd(); - ResponseJsonEnd(); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); - XdrvRulesProcess(); - - return res; -} - -int32_t Z_CheckNVWrite(int32_t res, class SBuffer &buf) { - - - - uint8_t status = buf.get8(2); - if ((0x00 == status) || (0x09 == status)) { - return 0; - } else { - return -2; - } -} - -int32_t Z_Reboot(int32_t res, class SBuffer &buf) { - - - - static const char Z_RebootReason[] PROGMEM = "Power-up|External|Watchdog"; - - uint8_t reason = buf.get8(2); - uint8_t transport_rev = buf.get8(3); - uint8_t product_id = buf.get8(4); - uint8_t major_rel = buf.get8(5); - uint8_t minor_rel = buf.get8(6); - uint8_t hw_rev = buf.get8(7); - char reason_str[12]; - - if (reason > 3) { reason = 3; } - GetTextIndexed(reason_str, sizeof(reason_str), reason, Z_RebootReason); - - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"Message\":\"CC2530 booted\",\"RestartReason\":\"%s\"" - ",\"MajorRel\":%d,\"MinorRel\":%d}}"), - ZIGBEE_STATUS_BOOT, reason_str, - major_rel, minor_rel); - - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); - XdrvRulesProcess(); - - if ((0x02 == major_rel) && (0x06 == minor_rel)) { - return 0; - } else { - return ZIGBEE_LABEL_UNSUPPORTED_VERSION; - } -} - -int32_t Z_ReceiveCheckVersion(int32_t res, class SBuffer &buf) { -# 129 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" - uint8_t major_rel = buf.get8(4); - uint8_t minor_rel = buf.get8(5); - uint8_t maint_rel = buf.get8(6); - uint32_t revision = buf.get32(7); - - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"MajorRel\":%d,\"MinorRel\":%d" - ",\"MaintRel\":%d,\"Revision\":%d}}"), - ZIGBEE_STATUS_CC_VERSION, major_rel, minor_rel, - maint_rel, revision); - - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); - XdrvRulesProcess(); - - if ((0x02 == major_rel) && (0x06 == minor_rel)) { - return 0; - } else { - return ZIGBEE_LABEL_UNSUPPORTED_VERSION; - } -} - - - - -bool Z_ReceiveMatchPrefix(const class SBuffer &buf, const uint8_t *match) { - if ( (pgm_read_byte(&match[0]) == buf.get8(0)) && - (pgm_read_byte(&match[1]) == buf.get8(1)) ) { - return true; - } else { - return false; - } -} - - - - -int32_t Z_ReceivePermitJoinStatus(int32_t res, const class SBuffer &buf) { - - uint8_t duration = buf.get8(2); - uint8_t status_code; - const char* message; - - if (0xFF == duration) { - status_code = ZIGBEE_STATUS_PERMITJOIN_OPEN_XX; - message = PSTR("Enable Pairing mode until next boot"); - } else if (duration > 0) { - status_code = ZIGBEE_STATUS_PERMITJOIN_OPEN_60; - message = PSTR("Enable Pairing mode for %d seconds"); - } else { - status_code = ZIGBEE_STATUS_PERMITJOIN_CLOSE; - message = PSTR("Disable Pairing mode"); - } - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"Message\":\""), - status_code); - ResponseAppend_P(message, duration); - ResponseAppend_P(PSTR("\"}}")); - - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); - XdrvRulesProcess(); - return -1; -} - -const char* Z_DeviceType[] = { "Coordinator", "Router", "End Device", "Unknown" }; -int32_t Z_ReceiveNodeDesc(int32_t res, const class SBuffer &buf) { - - Z_ShortAddress srcAddr = buf.get16(2); - uint8_t status = buf.get8(4); - Z_ShortAddress nwkAddr = buf.get16(5); - uint8_t logicalType = buf.get8(7); - uint8_t apsFlags = buf.get8(8); - uint8_t MACCapabilityFlags = buf.get8(9); - uint16_t manufacturerCapabilities = buf.get16(10); - uint8_t maxBufferSize = buf.get8(12); - uint16_t maxInTransferSize = buf.get16(13); - uint16_t serverMask = buf.get16(15); - uint16_t maxOutTransferSize = buf.get16(17); - uint8_t descriptorCapabilities = buf.get8(19); - - if (0 == status) { - uint8_t deviceType = logicalType & 0x7; - if (deviceType > 3) { deviceType = 3; } - bool complexDescriptorAvailable = (logicalType & 0x08) ? 1 : 0; - - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"NodeType\":\"%s\",\"ComplexDesc\":%s}}"), - ZIGBEE_STATUS_NODE_DESC, Z_DeviceType[deviceType], - complexDescriptorAvailable ? "true" : "false" - ); - - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); - XdrvRulesProcess(); - } - - return -1; -} - - - - -int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf) { - - Z_ShortAddress srcAddr = buf.get16(2); - uint8_t status = buf.get8(4); - Z_ShortAddress nwkAddr = buf.get16(5); - uint8_t activeEpCount = buf.get8(7); - uint8_t* activeEpList = (uint8_t*) buf.charptr(8); - - for (uint32_t i = 0; i < activeEpCount; i++) { - zigbee_devices.addEndpoint(nwkAddr, activeEpList[i]); - } - - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"ActiveEndpoints\":["), - ZIGBEE_STATUS_ACTIVE_EP); - for (uint32_t i = 0; i < activeEpCount; i++) { - if (i > 0) { ResponseAppend_P(PSTR(",")); } - ResponseAppend_P(PSTR("\"0x%02X\""), activeEpList[i]); - } - ResponseAppend_P(PSTR("]}}")); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); - XdrvRulesProcess(); - - Z_SendAFInfoRequest(nwkAddr); - - return -1; -} - - - - -int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf) { - uint8_t status = buf.get8(2); - Z_IEEEAddress ieeeAddr = buf.get64(3); - Z_ShortAddress nwkAddr = buf.get16(11); - - - - if (0 == status) { - zigbee_devices.updateDevice(nwkAddr, ieeeAddr); - char hex[20]; - Uint64toHex(ieeeAddr, hex, 64); - - const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr); - if (friendlyName) { - Response_P(PSTR("{\"" D_JSON_ZIGBEE_PING "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" - ",\"" D_JSON_ZIGBEE_IEEE "\":\"0x%s\"" - ",\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), nwkAddr, hex, friendlyName); - } else { - Response_P(PSTR("{\"" D_JSON_ZIGBEE_PING "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" - ",\"" D_JSON_ZIGBEE_IEEE "\":\"0x%s\"" - "}}"), nwkAddr, hex); - } - - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); - XdrvRulesProcess(); - } - return -1; -} - - - - -int32_t Z_DataConfirm(int32_t res, const class SBuffer &buf) { - uint8_t status = buf.get8(2); - uint8_t endpoint = buf.get8(3); - - - if (status) { - Response_P(PSTR("{\"" D_JSON_ZIGBEE_CONFIRM "\":{\"" D_CMND_ZIGBEE_ENDPOINT "\":%d" - ",\"" D_JSON_ZIGBEE_STATUS "\":%d" - ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" - "}}"), endpoint, status, getZigbeeStatusMessage(status).c_str()); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); - XdrvRulesProcess(); - } - - return -1; -} - - - - - - -int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) { - Z_ShortAddress srcAddr = buf.get16(2); - Z_ShortAddress nwkAddr = buf.get16(4); - Z_IEEEAddress ieeeAddr = buf.get64(6); - uint8_t capabilities = buf.get8(14); - - zigbee_devices.updateDevice(nwkAddr, ieeeAddr); - - char hex[20]; - Uint64toHex(ieeeAddr, hex, 64); - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\"" - ",\"PowerSource\":%s,\"ReceiveWhenIdle\":%s,\"Security\":%s}}"), - ZIGBEE_STATUS_DEVICE_ANNOUNCE, hex, nwkAddr, - (capabilities & 0x04) ? "true" : "false", - (capabilities & 0x08) ? "true" : "false", - (capabilities & 0x40) ? "true" : "false" - ); - - uint32_t wait_ms = 2000; - Z_Query_Bulb(nwkAddr, wait_ms); - - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); - XdrvRulesProcess(); - Z_SendActiveEpReq(nwkAddr); - return -1; -} - - - - - -int32_t Z_ReceiveTCDevInd(int32_t res, const class SBuffer &buf) { - Z_ShortAddress srcAddr = buf.get16(2); - Z_IEEEAddress ieeeAddr = buf.get64(4); - Z_ShortAddress parentNw = buf.get16(12); - - zigbee_devices.updateDevice(srcAddr, ieeeAddr); - - char hex[20]; - Uint64toHex(ieeeAddr, hex, 64); - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\"" - ",\"ParentNetwork\":\"0x%04X\"}}"), - ZIGBEE_STATUS_DEVICE_INDICATION, hex, srcAddr, parentNw - ); - - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); - XdrvRulesProcess(); - return -1; -} - - - - -int32_t Z_BindRsp(int32_t res, const class SBuffer &buf) { - Z_ShortAddress nwkAddr = buf.get16(2); - uint8_t status = buf.get8(4); - - const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr); - if (friendlyName) { - Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" - ",\"" D_JSON_ZIGBEE_NAME "\":\"%s\"" - ",\"" D_JSON_ZIGBEE_STATUS "\":%d" - ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" - "}}"), nwkAddr, friendlyName, status, getZigbeeStatusMessage(status).c_str()); - } else { - Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" - ",\"" D_JSON_ZIGBEE_STATUS "\":%d" - ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" - "}}"), nwkAddr, status, getZigbeeStatusMessage(status).c_str()); - } - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); - XdrvRulesProcess(); - - return -1; -} - - - - -int32_t Z_UnbindRsp(int32_t res, const class SBuffer &buf) { - Z_ShortAddress nwkAddr = buf.get16(2); - uint8_t status = buf.get8(4); - - const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr); - if (friendlyName) { - Response_P(PSTR("{\"" D_JSON_ZIGBEE_UNBIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" - ",\"" D_JSON_ZIGBEE_NAME "\":\"%s\"" - ",\"" D_JSON_ZIGBEE_STATUS "\":%d" - ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" - "}}"), nwkAddr, friendlyName, status, getZigbeeStatusMessage(status).c_str()); - } else { - Response_P(PSTR("{\"" D_JSON_ZIGBEE_UNBIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" - ",\"" D_JSON_ZIGBEE_STATUS "\":%d" - ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" - "}}"), nwkAddr, status, getZigbeeStatusMessage(status).c_str()); - } - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); - XdrvRulesProcess(); - - return -1; -} - - - -int32_t Z_MgmtBindRsp(int32_t res, const class SBuffer &buf) { - uint16_t shortaddr = buf.get16(2); - uint8_t status = buf.get8(4); - uint8_t bind_total = buf.get8(5); - uint8_t bind_start = buf.get8(6); - uint8_t bind_len = buf.get8(7); - - const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr); - - Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND_STATE "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""), shortaddr); - if (friendlyName) { - ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""), friendlyName); - } - ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_STATUS "\":%d" - ",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\"" - ",\"BindingsTotal\":%d" - - ",\"Bindings\":[" - ), status, getZigbeeStatusMessage(status).c_str(), bind_total); - - uint32_t idx = 8; - for (uint32_t i = 0; i < bind_len; i++) { - if (idx + 14 > buf.len()) { break; } - - - uint8_t srcep = buf.get8(idx + 8); - uint8_t cluster = buf.get16(idx + 9); - uint8_t addrmode = buf.get8(idx + 11); - uint16_t group = 0x0000; - uint64_t dstaddr = 0; - uint8_t dstep = 0x00; - if (Z_Addr_Group == addrmode) { - group = buf.get16(idx + 12); - idx += 14; - } else if (Z_Addr_IEEEAddress == addrmode) { - dstaddr = buf.get64(idx + 12); - dstep = buf.get8(idx + 20); - idx += 21; - } else { - - break; - } - - if (i > 0) { - ResponseAppend_P(PSTR(",")); - } - ResponseAppend_P(PSTR("{\"Cluster\":\"0x%04X\",\"Endpoint\":%d,"), cluster, srcep); - if (Z_Addr_Group == addrmode) { - ResponseAppend_P(PSTR("\"ToGroup\":%d}"), group); - } else if (Z_Addr_IEEEAddress == addrmode) { - char hex[20]; - Uint64toHex(dstaddr, hex, 64); - ResponseAppend_P(PSTR("\"ToDevice\":\"0x%s\",\"ToEndpoint\":%d}"), hex, dstep); - } - } - - ResponseAppend_P(PSTR("]}}")); - - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_BIND_STATE)); - XdrvRulesProcess(); - - return -1; -} -# 491 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" -void Z_SendIEEEAddrReq(uint16_t shortaddr) { - uint8_t IEEEAddrReq[] = { Z_SREQ | Z_ZDO, ZDO_IEEE_ADDR_REQ, Z_B0(shortaddr), Z_B1(shortaddr), 0x00, 0x00 }; - - ZigbeeZNPSend(IEEEAddrReq, sizeof(IEEEAddrReq)); -} - - - - -void Z_SendActiveEpReq(uint16_t shortaddr) { - uint8_t ActiveEpReq[] = { Z_SREQ | Z_ZDO, ZDO_ACTIVE_EP_REQ, Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) }; - - ZigbeeZNPSend(ActiveEpReq, sizeof(ActiveEpReq)); -} - - - - -void Z_SendAFInfoRequest(uint16_t shortaddr) { - uint8_t endpoint = zigbee_devices.findFirstEndpoint(shortaddr); - if (0x00 == endpoint) { endpoint = 0x01; } - uint8_t transacid = zigbee_devices.getNextSeqNumber(shortaddr); - - uint8_t AFInfoReq[] = { Z_SREQ | Z_AF, AF_DATA_REQUEST, Z_B0(shortaddr), Z_B1(shortaddr), endpoint, - 0x01, 0x00, 0x00, transacid, 0x30, 0x1E, 3 + 2*sizeof(uint16_t), - 0x00, transacid, ZCL_READ_ATTRIBUTES, 0x04, 0x00, 0x05, 0x00 - }; - ZigbeeZNPSend(AFInfoReq, sizeof(AFInfoReq)); -} -# 529 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" -void Z_AqaraOccupancy(uint16_t shortaddr, uint16_t cluster, uint8_t endpoint, const JsonObject &json) { - static const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; - - const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR(OCCUPANCY)); - if (nullptr != &val_endpoint) { - uint32_t occupancy = strToUInt(val_endpoint); - - if (occupancy) { - zigbee_devices.setTimer(shortaddr, 0 , OCCUPANCY_TIMEOUT, cluster, endpoint, Z_CAT_VIRTUAL_OCCUPANCY, 0, &Z_OccupancyCallback); - } else { - zigbee_devices.resetTimersForDevice(shortaddr, 0 , Z_CAT_VIRTUAL_OCCUPANCY); - } - } -} - - - -int32_t Z_PublishAttributes(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { - const JsonObject *json = zigbee_devices.jsonGet(shortaddr); - if (json == nullptr) { return 0; } - - zigbee_devices.jsonPublishFlush(shortaddr); - return 1; -} - - - - - -int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) { - uint16_t groupid = buf.get16(2); - uint16_t clusterid = buf.get16(4); - Z_ShortAddress srcaddr = buf.get16(6); - uint8_t srcendpoint = buf.get8(8); - uint8_t dstendpoint = buf.get8(9); - uint8_t wasbroadcast = buf.get8(10); - uint8_t linkquality = buf.get8(11); - uint8_t securityuse = buf.get8(12); - uint32_t timestamp = buf.get32(13); - uint8_t seqnumber = buf.get8(17); - - bool defer_attributes = false; - - ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 19, buf.get8(18), clusterid, groupid, - srcaddr, - srcendpoint, dstendpoint, wasbroadcast, - linkquality, securityuse, seqnumber, - timestamp); - zcl_received.log(); - char shortaddr[8]; - snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr); - - DynamicJsonBuffer jsonBuffer; - JsonObject& json = jsonBuffer.createObject(); - - if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_DEFAULT_RESPONSE == zcl_received.getCmdId())) { - zcl_received.parseResponse(); - } else { - - if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) { - zcl_received.parseRawAttributes(json); - if (clusterid) { defer_attributes = true; } - } else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) { - zcl_received.parseReadAttributes(json); - if (clusterid) { defer_attributes = true; } - } else if (zcl_received.isClusterSpecificCommand()) { - zcl_received.parseClusterSpecificCommand(json); - } - String msg(""); - msg.reserve(100); - json.printTo(msg); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":%s}"), srcaddr, msg.c_str()); - - zcl_received.postProcessAttributes(srcaddr, json); - - json[F(D_CMND_ZIGBEE_ENDPOINT)] = srcendpoint; - - if (groupid) { - json[F(D_CMND_ZIGBEE_GROUP)] = groupid; - } - - json[F(D_CMND_ZIGBEE_LINKQUALITY)] = linkquality; - - - zigbee_devices.resetTimersForDevice(srcaddr, 0 , Z_CAT_REACHABILITY); - zigbee_devices.setReachable(srcaddr, true); - - - Z_AqaraOccupancy(srcaddr, clusterid, srcendpoint, json); - - if (defer_attributes) { - - if (zigbee_devices.jsonIsConflict(srcaddr, json)) { - - zigbee_devices.jsonPublishFlush(srcaddr); - } - zigbee_devices.jsonAppend(srcaddr, json); - zigbee_devices.setTimer(srcaddr, 0 , USE_ZIGBEE_COALESCE_ATTR_TIMER, clusterid, srcendpoint, Z_CAT_READ_ATTR, 0, &Z_PublishAttributes); - } else { - - zigbee_devices.jsonPublishNow(srcaddr, json); - } - } - return -1; -} - - -typedef struct Z_Dispatcher { - const uint8_t* match; - ZB_RecvMsgFunc func; -} Z_Dispatcher; - - -ZBM(AREQ_AF_DATA_CONFIRM, Z_AREQ | Z_AF, AF_DATA_CONFIRM) -ZBM(AREQ_AF_INCOMING_MESSAGE, Z_AREQ | Z_AF, AF_INCOMING_MSG) -ZBM(AREQ_END_DEVICE_ANNCE_IND, Z_AREQ | Z_ZDO, ZDO_END_DEVICE_ANNCE_IND) -ZBM(AREQ_END_DEVICE_TC_DEV_IND, Z_AREQ | Z_ZDO, ZDO_TC_DEV_IND) -ZBM(AREQ_PERMITJOIN_OPEN_XX, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND ) -ZBM(AREQ_ZDO_ACTIVEEPRSP, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP) -ZBM(AREQ_ZDO_SIMPLEDESCRSP, Z_AREQ | Z_ZDO, ZDO_SIMPLE_DESC_RSP) -ZBM(AREQ_ZDO_IEEE_ADDR_RSP, Z_AREQ | Z_ZDO, ZDO_IEEE_ADDR_RSP) -ZBM(AREQ_ZDO_BIND_RSP, Z_AREQ | Z_ZDO, ZDO_BIND_RSP) -ZBM(AREQ_ZDO_UNBIND_RSP, Z_AREQ | Z_ZDO, ZDO_UNBIND_RSP) -ZBM(AREQ_ZDO_MGMT_BIND_RSP, Z_AREQ | Z_ZDO, ZDO_MGMT_BIND_RSP) - - -const Z_Dispatcher Z_DispatchTable[] PROGMEM = { - { AREQ_AF_DATA_CONFIRM, &Z_DataConfirm }, - { AREQ_AF_INCOMING_MESSAGE, &Z_ReceiveAfIncomingMessage }, - { AREQ_END_DEVICE_ANNCE_IND, &Z_ReceiveEndDeviceAnnonce }, - { AREQ_END_DEVICE_TC_DEV_IND, &Z_ReceiveTCDevInd }, - { AREQ_PERMITJOIN_OPEN_XX, &Z_ReceivePermitJoinStatus }, - { AREQ_ZDO_NODEDESCRSP, &Z_ReceiveNodeDesc }, - { AREQ_ZDO_ACTIVEEPRSP, &Z_ReceiveActiveEp }, - { AREQ_ZDO_IEEE_ADDR_RSP, &Z_ReceiveIEEEAddr }, - { AREQ_ZDO_BIND_RSP, &Z_BindRsp }, - { AREQ_ZDO_UNBIND_RSP, &Z_UnbindRsp }, - { AREQ_ZDO_MGMT_BIND_RSP, &Z_MgmtBindRsp }, -}; - - - - - -int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf) { - - if (zigbee.init_phase) { - - return -1; - } else { - for (uint32_t i = 0; i < sizeof(Z_DispatchTable)/sizeof(Z_Dispatcher); i++) { - if (Z_ReceiveMatchPrefix(buf, Z_DispatchTable[i].match)) { - (*Z_DispatchTable[i].func)(res, buf); - } - } - return -1; - } -} -# 695 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" -int32_t Z_Load_Devices(uint8_t value) { - - loadZigbeeDevices(); - return 0; -} - - - - -void Z_Query_Bulb(uint16_t shortaddr, uint32_t &wait_ms) { - const uint32_t inter_message_ms = 100; - - if (0 <= zigbee_devices.getHueBulbtype(shortaddr)) { - uint8_t endpoint = zigbee_devices.findFirstEndpoint(shortaddr); - - if (endpoint) { - zigbee_devices.setTimer(shortaddr, 0 , wait_ms, 0x0006, endpoint, Z_CAT_NONE, 0 , &Z_ReadAttrCallback); - wait_ms += inter_message_ms; - zigbee_devices.setTimer(shortaddr, 0 , wait_ms, 0x0008, endpoint, Z_CAT_NONE, 0 , &Z_ReadAttrCallback); - wait_ms += inter_message_ms; - zigbee_devices.setTimer(shortaddr, 0 , wait_ms, 0x0300, endpoint, Z_CAT_NONE, 0 , &Z_ReadAttrCallback); - wait_ms += inter_message_ms; - zigbee_devices.setTimer(shortaddr, 0, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, 0, endpoint, Z_CAT_REACHABILITY, 0 , &Z_Unreachable); - wait_ms += 1000; - } - } -} - - - - -int32_t Z_Query_Bulbs(uint8_t value) { - - uint32_t wait_ms = 1000; - for (uint32_t i = 0; i < zigbee_devices.devicesSize(); i++) { - const Z_Device &device = zigbee_devices.devicesAt(i); - Z_Query_Bulb(device.shortaddr, wait_ms); - } - return 0; -} - - - - -int32_t Z_State_Ready(uint8_t value) { - zigbee.init_phase = false; - return 0; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" -#ifdef USE_ZIGBEE - -#define XDRV_23 23 - -const uint32_t ZIGBEE_BUFFER_SIZE = 256; -const uint8_t ZIGBEE_SOF = 0xFE; -const uint8_t ZIGBEE_SOF_ALT = 0xFF; - -#include -TasmotaSerial *ZigbeeSerial = nullptr; - - -const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" - D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|" - D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|" - D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|" - D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" - D_CMND_ZIGBEE_BIND "|" D_CMND_ZIGBEE_UNBIND "|" D_CMND_ZIGBEE_PING "|" D_CMND_ZIGBEE_MODELID "|" - D_CMND_ZIGBEE_LIGHT "|" D_CMND_ZIGBEE_RESTORE "|" D_CMND_ZIGBEE_BIND_STATE "|" - D_CMND_ZIGBEE_CONFIG - ; - -void (* const ZigbeeCommand[])(void) PROGMEM = { - &CmndZbZNPSend, &CmndZbPermitJoin, - &CmndZbStatus, &CmndZbReset, &CmndZbSend, - &CmndZbProbe, &CmndZbRead, &CmndZbZNPReceive, - &CmndZbForget, &CmndZbSave, &CmndZbName, - &CmndZbBind, &CmndZbUnbind, &CmndZbPing, &CmndZbModelId, - &CmndZbLight, &CmndZbRestore, &CmndZbBindState, - &CmndZbConfig, - }; - - - - -void ZigbeeInputLoop(void) -{ - static uint32_t zigbee_polling_window = 0; - static uint8_t fcs = ZIGBEE_SOF; - static uint32_t zigbee_frame_len = 5; -# 68 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" - while (ZigbeeSerial->available()) { - yield(); - uint8_t zigbee_in_byte = ZigbeeSerial->read(); - - - if (0 == zigbee_buffer->len()) { - zigbee_frame_len = 5; - fcs = ZIGBEE_SOF; - - - - if (ZIGBEE_SOF_ALT == zigbee_in_byte) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbInput forgiven first byte %02X (only for statistics)"), zigbee_in_byte); - zigbee_in_byte = ZIGBEE_SOF; - } - } - - if ((0 == zigbee_buffer->len()) && (ZIGBEE_SOF != zigbee_in_byte)) { - - AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbInput discarding byte %02X"), zigbee_in_byte); - continue; - } - - if (zigbee_buffer->len() < zigbee_frame_len) { - zigbee_buffer->add8(zigbee_in_byte); - zigbee_polling_window = millis(); - fcs ^= zigbee_in_byte; - } - - if (zigbee_buffer->len() >= zigbee_frame_len) { - zigbee_polling_window = 0; - break; - } - - - if (02 == zigbee_buffer->len()) { - - uint8_t len_byte = zigbee_buffer->get8(1); - if (len_byte > 250) len_byte = 250; - - zigbee_frame_len = len_byte + 5; - } - } - - if (zigbee_buffer->len() && (millis() > (zigbee_polling_window + ZIGBEE_POLLING))) { - char hex_char[(zigbee_buffer->len() * 2) + 2]; - ToHex_P((unsigned char*)zigbee_buffer->getBuffer(), zigbee_buffer->len(), hex_char, sizeof(hex_char)); - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Bytes follow_read_metric = %0d"), ZigbeeSerial->getLoopReadMetric()); - - if (zigbee_buffer->len() != zigbee_frame_len) { - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received frame of wrong size %s, len %d, expected %d"), hex_char, zigbee_buffer->len(), zigbee_frame_len); - } else if (0x00 != fcs) { - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received bad FCS frame %s, %d"), hex_char, fcs); - } else { - - - - SBuffer znp_buffer = zigbee_buffer->subBuffer(2, zigbee_frame_len - 3); - - ToHex_P((unsigned char*)znp_buffer.getBuffer(), znp_buffer.len(), hex_char, sizeof(hex_char)); - Response_P(PSTR("{\"" D_JSON_ZIGBEEZNPRECEIVED "\":\"%s\"}"), hex_char); - if (Settings.flag3.tuya_serial_mqtt_publish) { - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); - XdrvRulesProcess(); - } else { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "%s"), mqtt_data); - } - - ZigbeeProcessInput(znp_buffer); - } - zigbee_buffer->setLen(0); - } -} - - - - -void ZigbeeInit(void) -{ - - if (0 == Settings.zb_channel) { - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Initializing Zigbee parameters from defaults")); - Settings.zb_ext_panid = USE_ZIGBEE_EXTPANID; - Settings.zb_precfgkey_l = USE_ZIGBEE_PRECFGKEY_L; - Settings.zb_precfgkey_h = USE_ZIGBEE_PRECFGKEY_H; - Settings.zb_pan_id = USE_ZIGBEE_PANID; - Settings.zb_channel = USE_ZIGBEE_CHANNEL; - Settings.zb_free_byte = 0; - } - - Z_UpdateConfig(Settings.zb_channel, Settings.zb_pan_id, Settings.zb_ext_panid, Settings.zb_precfgkey_l, Settings.zb_precfgkey_h); - - - zigbee.active = false; - if ((pin[GPIO_ZIGBEE_RX] < 99) && (pin[GPIO_ZIGBEE_TX] < 99)) { - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "GPIOs Rx:%d Tx:%d"), pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX]); - - ZigbeeSerial = new TasmotaSerial(pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX], seriallog_level ? 1 : 2, 0, 256); - ZigbeeSerial->begin(115200); - if (ZigbeeSerial->hardwareSerial()) { - ClaimSerial(); - uint32_t aligned_buffer = ((uint32_t)serial_in_buffer + 3) & ~3; - zigbee_buffer = new PreAllocatedSBuffer(sizeof(serial_in_buffer) - 3, (char*) aligned_buffer); - } else { - - zigbee_buffer = new SBuffer(ZIGBEE_BUFFER_SIZE); - - } - zigbee.active = true; - zigbee.init_phase = true; - zigbee.state_machine = true; - ZigbeeSerial->flush(); - } - -} - - - - - -uint32_t strToUInt(const JsonVariant &val) { - - if (val.is()) { - return val.as(); - } else { - if (val.is()) { - String sval = val.as(); - return strtoull(sval.c_str(), nullptr, 0); - } - } - return 0; -} - - -const unsigned char ZIGBEE_FACTORY_RESET[] PROGMEM = - { Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 , 0x01 }; - -void CmndZbReset(void) { - if (ZigbeeSerial) { - switch (XdrvMailbox.payload) { - case 1: - ZigbeeZNPSend(ZIGBEE_FACTORY_RESET, sizeof(ZIGBEE_FACTORY_RESET)); - eraseZigbeeDevices(); - restart_flag = 2; - ResponseCmndChar_P(PSTR(D_JSON_ZIGBEE_CC2530 " " D_JSON_RESET_AND_RESTARTING)); - break; - default: - ResponseCmndChar_P(PSTR(D_JSON_ONE_TO_RESET)); - } - } -} - - - - - -void CmndZbZNPSendOrReceive(bool send) -{ - if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) { - uint8_t code; - - char *codes = RemoveSpace(XdrvMailbox.data); - int32_t size = strlen(XdrvMailbox.data); - - SBuffer buf((size+1)/2); - - while (size > 1) { - char stemp[3]; - strlcpy(stemp, codes, sizeof(stemp)); - code = strtol(stemp, nullptr, 16); - buf.add8(code); - size -= 2; - codes += 2; - } - if (send) { - - ZigbeeZNPSend(buf.getBuffer(), buf.len()); - } else { - - ZigbeeProcessInput(buf); - } - } - ResponseCmndDone(); -} - - -void CmndZbZNPReceive(void) -{ - CmndZbZNPSendOrReceive(false); -} - -void CmndZbZNPSend(void) -{ - CmndZbZNPSendOrReceive(true); -} - -void ZigbeeZNPSend(const uint8_t *msg, size_t len) { - if ((len < 2) || (len > 252)) { - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_JSON_ZIGBEEZNPSENT ": bad message len %d"), len); - return; - } - uint8_t data_len = len - 2; - - if (ZigbeeSerial) { - uint8_t fcs = data_len; - - ZigbeeSerial->write(ZIGBEE_SOF); - - ZigbeeSerial->write(data_len); - - for (uint32_t i = 0; i < len; i++) { - uint8_t b = pgm_read_byte(msg + i); - ZigbeeSerial->write(b); - fcs ^= b; - - } - ZigbeeSerial->write(fcs); - - } - - char hex_char[(len * 2) + 2]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZNPSENT " %s"), - ToHex_P(msg, len, hex_char, sizeof(hex_char))); -} -# 312 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" -void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, uint16_t manuf, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId) { - - SBuffer buf(32+len); - buf.add8(Z_SREQ | Z_AF); - buf.add8(AF_DATA_REQUEST_EXT); - if (0x0000 == shortaddr) { - buf.add8(Z_Addr_Group); - buf.add64(groupaddr); - buf.add8(0xFF); - } else { - buf.add8(Z_Addr_ShortAddress); - buf.add64(shortaddr); - buf.add8(endpoint); - } - buf.add16(0x0000); - buf.add8(0x01); - buf.add16(clusterId); - buf.add8(transacId); - buf.add8(0x30); - buf.add8(0x1E); - - buf.add16(3 + len + (manuf ? 2 : 0)); - buf.add8((needResponse ? 0x00 : 0x10) | (clusterSpecific ? 0x01 : 0x00) | (manuf ? 0x04 : 0x00)); - if (manuf) { - buf.add16(manuf); - } - buf.add8(transacId); - buf.add8(cmdId); - if (len > 0) { - buf.addBuffer(msg, len); - } - - ZigbeeZNPSend(buf.getBuffer(), buf.len()); -} -# 363 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" -void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint, bool clusterSpecific, uint16_t manuf, - uint16_t cluster, uint8_t cmd, const char *param) { - size_t size = param ? strlen(param) : 0; - SBuffer buf((size+2)/2); - - if (param) { - while (*param) { - uint8_t code = parseHex_P(¶m, 2); - buf.add8(code); - } - } - - if ((0 == endpoint) && (shortaddr)) { - - endpoint = zigbee_devices.findFirstEndpoint(shortaddr); - - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: shortaddr 0x%04X, groupaddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"), - shortaddr, groupaddr, cluster, endpoint, cmd, param); - - if ((0 == endpoint) && (shortaddr)) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbSend: unspecified endpoint")); - return; - } - - - ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, cmd, clusterSpecific, manuf, buf.getBuffer(), buf.len(), true, zigbee_devices.getNextSeqNumber(shortaddr)); - - if (clusterSpecific) { - zigbeeSetCommandTimer(shortaddr, groupaddr, cluster, endpoint); - } -} - - - - -void CmndZbSend(void) { -# 411 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" - if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - DynamicJsonBuffer jsonBuf; - const JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data); - if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } - - - static char delim[] = ", "; - uint16_t device = 0x0000; - uint16_t groupaddr = 0x0000; - uint8_t endpoint = 0x00; - uint16_t manuf = 0x0000; - - uint16_t cluster = 0; - uint8_t cmd = 0; - String cmd_str = ""; - const char *cmd_s; - bool clusterSpecific = true; - - - const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); - if (nullptr != &val_device) { - device = zigbee_devices.parseDeviceParam(val_device.as()); - if (0xFFFF == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } - } - if (0x0000 == device) { - const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group")); - if (nullptr != &val_group) { - groupaddr = strToUInt(val_group); - } else { - ResponseCmndChar_P(PSTR("Unknown device")); - return; - } - } - - const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint")); - if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); } - const JsonVariant &val_manuf = getCaseInsensitive(json, PSTR("Manuf")); - if (nullptr != &val_manuf) { manuf = strToUInt(val_manuf); } - const JsonVariant &val_cmd = getCaseInsensitive(json, PSTR("Send")); - if (nullptr != &val_cmd) { - - - - if (val_cmd.is()) { - - const JsonObject &cmd_obj = val_cmd.as(); - int32_t cmd_size = cmd_obj.size(); - if (cmd_size > 1) { - Response_P(PSTR("Only 1 command allowed (%d)"), cmd_size); - return; - } else if (1 == cmd_size) { - - JsonObject::const_iterator it = cmd_obj.begin(); - String key = it->key; - const JsonVariant& value = it->value; - uint32_t x = 0, y = 0, z = 0; - uint16_t cmd_var; - - const __FlashStringHelper* tasmota_cmd = zigbeeFindCommand(key.c_str(), &cluster, &cmd_var); - if (tasmota_cmd) { - cmd_str = tasmota_cmd; - } else { - Response_P(PSTR("Unrecognized zigbee command: %s"), key.c_str()); - return; - } - - - if (value.is()) { - x = value.as() ? 1 : 0; - } else if (value.is()) { - x = value.as(); - } else { - - const char *s_const = value.as(); - if (s_const != nullptr) { - char s[strlen(s_const)+1]; - strcpy(s, s_const); - if ((nullptr != s) && (0x00 != *s)) { - char *sval = strtok(s, delim); - if (sval) { - x = ZigbeeAliasOrNumber(sval); - sval = strtok(nullptr, delim); - if (sval) { - y = ZigbeeAliasOrNumber(sval); - sval = strtok(nullptr, delim); - if (sval) { - z = ZigbeeAliasOrNumber(sval); - } - } - } - } - } - } - - - if (0xFF == cmd_var) { - cmd = x; - x = y; - y = z; - } else { - cmd = cmd_var; - } - cmd_str = zigbeeCmdAddParams(cmd_str.c_str(), x, y, z); - - cmd_s = cmd_str.c_str(); - } else { - - } - } else if (val_cmd.is()) { - - cmd_str = val_cmd.as(); - - - - - const char * data = cmd_str.c_str(); - cluster = parseHex(&data, 4); - - - if (('_' == *data) || ('!' == *data)) { - if ('_' == *data) { clusterSpecific = false; } - data++; - } else { - ResponseCmndChar_P(PSTR("Wrong delimiter for payload")); - return; - } - - cmd = parseHex(&data, 2); - - - - if ('/' == *data) { data++; } - - cmd_s = data; - } else { - - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeZCLSend device: 0x%04X, group: 0x%04X, endpoint:%d, cluster:0x%04X, cmd:0x%02X, send:\"%s\""), - device, groupaddr, endpoint, cluster, cmd, cmd_s); - zigbeeZCLSendStr(device, groupaddr, endpoint, clusterSpecific, manuf, cluster, cmd, cmd_s); - ResponseCmndDone(); - } else { - Response_P(PSTR("Missing zigbee 'Send'")); - return; - } -} - - - - -void ZbBindUnbind(bool unbind) { - - - - - if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - DynamicJsonBuffer jsonBuf; - const JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data); - if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } - - - - uint16_t srcDevice = 0xFFFF; - uint16_t dstDevice = 0xFFFF; - uint64_t dstLongAddr = 0; - uint8_t endpoint = 0x00; - uint8_t toendpoint = 0x00; - uint16_t toGroup = 0x0000; - uint16_t cluster = 0; - uint32_t group = 0xFFFFFFFF; - - - - const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); - if (nullptr != &val_device) { - srcDevice = zigbee_devices.parseDeviceParam(val_device.as()); - if (0xFFFF == srcDevice) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } - } - if ((nullptr == &val_device) || (0x0000 == srcDevice)) { ResponseCmndChar_P(PSTR("Unknown source device")); return; } - - uint64_t srcLongAddr = zigbee_devices.getDeviceLongAddr(srcDevice); - if (0 == srcLongAddr) { ResponseCmndChar_P(PSTR("Unknown source IEEE address")); return; } - - const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint")); - if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); } - - const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster")); - if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); } - - - - - - const JsonVariant &dst_device = getCaseInsensitive(json, PSTR("ToDevice")); - if (nullptr != &dst_device) { - dstDevice = zigbee_devices.parseDeviceParam(dst_device.as()); - if (0xFFFF == dstDevice) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } - if (0x0000 == dstDevice) { - dstLongAddr = localIEEEAddr; - } else { - dstLongAddr = zigbee_devices.getDeviceLongAddr(dstDevice); - } - if (0 == dstLongAddr) { ResponseCmndChar_P(PSTR("Unknown dest IEEE address")); return; } - - const JsonVariant &val_toendpoint = getCaseInsensitive(json, PSTR("ToEndpoint")); - if (nullptr != &val_toendpoint) { toendpoint = strToUInt(val_endpoint); } else { toendpoint = endpoint; } - } - - - const JsonVariant &to_group = getCaseInsensitive(json, PSTR("ToGroup")); - if (nullptr != &to_group) { toGroup = strToUInt(to_group); } - - - if (toGroup && dstLongAddr) { ResponseCmndChar_P(PSTR("Cannot have both \"ToDevice\" and \"ToGroup\"")); return; } - if (!toGroup && !dstLongAddr) { ResponseCmndChar_P(PSTR("Missing \"ToDevice\" or \"ToGroup\"")); return; } - - SBuffer buf(34); - buf.add8(Z_SREQ | Z_ZDO); - if (unbind) { - buf.add8(ZDO_UNBIND_REQ); - } else { - buf.add8(ZDO_BIND_REQ); - } - buf.add16(srcDevice); - buf.add64(srcLongAddr); - buf.add8(endpoint); - buf.add16(cluster); - if (dstLongAddr) { - buf.add8(Z_Addr_IEEEAddress); - buf.add64(dstLongAddr); - buf.add8(toendpoint); - } else { - buf.add8(Z_Addr_Group); - buf.add16(toGroup); - } - - ZigbeeZNPSend(buf.getBuffer(), buf.len()); - - ResponseCmndDone(); -} - - - - -void CmndZbBind(void) { - ZbBindUnbind(false); -} - - - - -void CmndZbUnbind(void) { - ZbBindUnbind(true); -} - - - - -void CmndZbBindState(void) { - if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); - if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } - if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } - - SBuffer buf(10); - buf.add8(Z_SREQ | Z_ZDO); - buf.add8(ZDO_MGMT_BIND_REQ); - buf.add16(shortaddr); - buf.add8(0); - - ZigbeeZNPSend(buf.getBuffer(), buf.len()); - - ResponseCmndDone(); -} - - -void CmndZbProbe(void) { - CmndZbProbeOrPing(true); -} - - - - -void CmndZbProbeOrPing(boolean probe) { - if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); - if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } - if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } - - - Z_SendIEEEAddrReq(shortaddr); - if (probe) { - Z_SendActiveEpReq(shortaddr); - } - ResponseCmndDone(); -} - - -void CmndZbPing(void) { - CmndZbProbeOrPing(false); -} - - - - - -void CmndZbName(void) { - - - - - - - - if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - - - char *p; - char *str = strtok_r(XdrvMailbox.data, ", ", &p); - - - uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); - if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } - if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } - - if (p == nullptr) { - const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr); - Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, friendlyName ? friendlyName : ""); - } else { - zigbee_devices.setFriendlyName(shortaddr, p); - Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, p); - } -} - - - - - -void CmndZbModelId(void) { - - - - - - - - if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - - - char *p; - char *str = strtok_r(XdrvMailbox.data, ", ", &p); - - - uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); - if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } - if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } - - if (p == nullptr) { - const char * modelId = zigbee_devices.getModelId(shortaddr); - Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), shortaddr, modelId ? modelId : ""); - } else { - zigbee_devices.setModelId(shortaddr, p); - Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), shortaddr, p); - } -} - - - - -void CmndZbLight(void) { - - - - - - - if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - - - char *p; - char *str = strtok_r(XdrvMailbox.data, ", ", &p); - - - uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); - if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } - if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } - - if (p) { - int8_t bulbtype = strtol(p, nullptr, 10); - if (bulbtype > 5) { bulbtype = 5; } - if (bulbtype < -1) { bulbtype = -1; } - zigbee_devices.setHueBulbtype(shortaddr, bulbtype); - } - String dump = zigbee_devices.dumpLightState(shortaddr); - Response_P(PSTR("{\"" D_PRFX_ZB D_CMND_ZIGBEE_LIGHT "\":%s}"), dump.c_str()); - - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_PRFX_ZB D_CMND_ZIGBEE_LIGHT)); - XdrvRulesProcess(); - ResponseCmndDone(); -} - - - - - -void CmndZbForget(void) { - if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); - if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } - if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } - - - if (zigbee_devices.removeDevice(shortaddr)) { - ResponseCmndDone(); - } else { - ResponseCmndChar_P(PSTR("Unknown device")); - } -} - - - - - -void CmndZbSave(void) { - if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - saveZigbeeDevices(); - ResponseCmndDone(); -} -# 849 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" -void CmndZbRestore(void) { - if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - DynamicJsonBuffer jsonBuf; - const JsonVariant json_parsed = jsonBuf.parse((const char*) XdrvMailbox.data); - const JsonVariant * json = &json_parsed; - bool success = false; - - - if (json_parsed.is()) { - success = json_parsed.as().success(); - } else if (json_parsed.is()) { - success = json_parsed.as().success(); - } - if (!success) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } - - - const JsonVariant * zbstatus = &startsWithCaseInsensitive(*json, PSTR("ZbStatus")); - if (nullptr != zbstatus) { - json = zbstatus; - } - - - if (json->is()) { - const JsonArray& arr = json->as(); - for (auto elt : arr) { - - int32_t res = zigbee_devices.deviceRestore(elt); - if (res < 0) { - ResponseCmndChar_P(PSTR("Restore failed")); - return; - } - } - } else if (json->is()) { - int32_t res = zigbee_devices.deviceRestore(*json); - if (res < 0) { - ResponseCmndChar_P(PSTR("Restore failed")); - return; - } - - } else { - ResponseCmndChar_P(PSTR("Missing parameters")); - return; - } - ResponseCmndDone(); -} - - - - - -void CmndZbRead(void) { - - - - if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - DynamicJsonBuffer jsonBuf; - JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data); - if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } - - - uint16_t device = 0xFFFF; - uint16_t groupaddr = 0x0000; - uint16_t cluster = 0x0000; - uint8_t endpoint = 0x00; - uint16_t manuf = 0x0000; - size_t attrs_len = 0; - uint8_t* attrs = nullptr; - - const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); - if (nullptr != &val_device) { - device = zigbee_devices.parseDeviceParam(val_device.as()); - if (0xFFFF == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } - } - if (0x0000 == device) { - const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group")); - if (nullptr != &val_group) { - groupaddr = strToUInt(val_group); - } else { - ResponseCmndChar_P(PSTR("Unknown device")); - return; - } - } - - const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster")); - if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); } - const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint")); - if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); } - const JsonVariant &val_manuf = getCaseInsensitive(json, PSTR("Manuf")); - if (nullptr != &val_manuf) { manuf = strToUInt(val_manuf); } - - const JsonVariant &val_attr = getCaseInsensitive(json, PSTR("Read")); - if (nullptr != &val_attr) { - uint16_t val = strToUInt(val_attr); - if (val_attr.is()) { - const JsonArray& attr_arr = val_attr.as(); - attrs_len = attr_arr.size() * 2; - attrs = new uint8_t[attrs_len]; - - uint32_t i = 0; - for (auto value : attr_arr) { - uint16_t val = strToUInt(value); - attrs[i++] = val & 0xFF; - attrs[i++] = val >> 8; - } - } else { - attrs_len = 2; - attrs = new uint8_t[attrs_len]; - attrs[0] = val & 0xFF; - attrs[1] = val >> 8; - } - } - - if ((0 == endpoint) && (device)) { - endpoint = zigbee_devices.findFirstEndpoint(device); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint); - } - if (0x0000 == device) { - endpoint = 0xFF; - } - - if ((0 != endpoint) && (attrs_len > 0)) { - ZigbeeZCLSend_Raw(device, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, manuf, attrs, attrs_len, true , zigbee_devices.getNextSeqNumber(device)); - ResponseCmndDone(); - } else { - ResponseCmndChar_P(PSTR("Missing parameters")); - } - - if (attrs) { delete[] attrs; } -} - - - - - -void CmndZbPermitJoin(void) { - if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - uint32_t payload = XdrvMailbox.payload; - uint16_t dstAddr = 0xFFFC; - uint8_t duration = 60; - - if (payload <= 0) { - duration = 0; - } else if (99 == payload) { - duration = 0xFF; - } - - SBuffer buf(34); - buf.add8(Z_SREQ | Z_ZDO); - buf.add8(ZDO_MGMT_PERMIT_JOIN_REQ); - buf.add8(0x0F); - buf.add16(0xFFFC); - buf.add8(duration); - buf.add8(0x00); - - ZigbeeZNPSend(buf.getBuffer(), buf.len()); - - ResponseCmndDone(); -} - - - - -void CmndZbStatus(void) { - if (ZigbeeSerial) { - if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); - if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } - if (XdrvMailbox.payload > 0) { - if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } - } - - String dump = zigbee_devices.dump(XdrvMailbox.index, shortaddr); - Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.index, dump.c_str()); - } -} - - - - -void CmndZbConfig(void) { - - - uint8_t zb_channel = Settings.zb_channel; - uint16_t zb_pan_id = Settings.zb_pan_id; - uint64_t zb_ext_panid = Settings.zb_ext_panid; - uint64_t zb_precfgkey_l = Settings.zb_precfgkey_l; - uint64_t zb_precfgkey_h = Settings.zb_precfgkey_h; - - - RemoveAllSpaces(XdrvMailbox.data); - if (strlen(XdrvMailbox.data) > 0) { - DynamicJsonBuffer jsonBuf; - const JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data); - if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } - - - const JsonVariant &val_channel = getCaseInsensitive(json, PSTR("Channel")); - if (nullptr != &val_channel) { zb_channel = strToUInt(val_channel); } - if (zb_channel < 11) { zb_channel = 11; } - if (zb_channel > 26) { zb_channel = 26; } - - const JsonVariant &val_pan_id = getCaseInsensitive(json, PSTR("PanID")); - if (nullptr != &val_pan_id) { zb_pan_id = strToUInt(val_pan_id); } - - const JsonVariant &val_ext_pan_id = getCaseInsensitive(json, PSTR("ExtPanID")); - if (nullptr != &val_ext_pan_id) { zb_ext_panid = strtoull(val_ext_pan_id.as(), nullptr, 0); } - - const JsonVariant &val_key_l = getCaseInsensitive(json, PSTR("KeyL")); - if (nullptr != &val_key_l) { zb_precfgkey_l = strtoull(val_key_l.as(), nullptr, 0); } - - const JsonVariant &val_key_h = getCaseInsensitive(json, PSTR("KeyH")); - if (nullptr != &val_key_h) { zb_precfgkey_h = strtoull(val_key_h.as(), nullptr, 0); } - - - if ( (zb_channel != Settings.zb_channel) || - (zb_pan_id != Settings.zb_pan_id) || - (zb_ext_panid != Settings.zb_ext_panid) || - (zb_precfgkey_l != Settings.zb_precfgkey_l) || - (zb_precfgkey_h != Settings.zb_precfgkey_h) ) { - Settings.zb_channel = zb_channel; - Settings.zb_pan_id = zb_pan_id; - Settings.zb_ext_panid = zb_ext_panid; - Settings.zb_precfgkey_l = zb_precfgkey_l; - Settings.zb_precfgkey_h = zb_precfgkey_h; - restart_flag = 2; - } - } - - - char hex_ext_panid[20] = "0x"; - Uint64toHex(zb_ext_panid, &hex_ext_panid[2], 64); - char hex_precfgkey_l[20] = "0x"; - Uint64toHex(zb_precfgkey_l, &hex_precfgkey_l[2], 64); - char hex_precfgkey_h[20] = "0x"; - Uint64toHex(zb_precfgkey_h, &hex_precfgkey_h[2], 64); - - - Response_P(PSTR("{\"" D_PRFX_ZB D_JSON_ZIGBEE_CONFIG "\":{" - "\"Channel\":%d" - ",\"PanID\":\"0x%04X\"" - ",\"ExtPanID\":\"%s\"" - ",\"KeyL\":\"%s\"" - ",\"KeyH\":\"%s\"" - "}}"), - zb_channel, zb_pan_id, - hex_ext_panid, - hex_precfgkey_l, hex_precfgkey_h); -} - - - - - -bool Xdrv23(uint8_t function) -{ - bool result = false; - - if (zigbee.active) { - switch (function) { - case FUNC_EVERY_50_MSECOND: - if (!zigbee.init_phase) { - zigbee_devices.runTimer(); - } - break; - case FUNC_LOOP: - if (ZigbeeSerial) { ZigbeeInputLoop(); } - if (zigbee.state_machine) { - ZigbeeStateMachine_Run(); - } - break; - case FUNC_PRE_INIT: - ZigbeeInit(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kZbCommands, ZigbeeCommand); - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_24_buzzer.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_24_buzzer.ino" -#ifdef USE_BUZZER - - - - -#define XDRV_24 24 - -struct BUZZER { - uint32_t tune = 0; - uint32_t tune_reload = 0; - bool active = true; - bool enable = false; - uint8_t inverted = 0; - uint8_t count = 0; - uint8_t mode = 0; - uint8_t set[2]; - uint8_t duration; - uint8_t state = 0; -} Buzzer; - - - -void BuzzerOff(void) -{ - DigitalWrite(GPIO_BUZZER, Buzzer.inverted); -} - - -void BuzzerBeep(uint32_t count, uint32_t on, uint32_t off, uint32_t tune, uint32_t mode) -{ - Buzzer.set[0] = off; - Buzzer.set[1] = on; - Buzzer.duration = 1; - Buzzer.tune_reload = 0; - Buzzer.mode = mode; - - if (tune) { - uint32_t tune1 = tune; - uint32_t tune2 = tune; - for (uint32_t i = 0; i < 32; i++) { - if (!(tune2 & 0x80000000)) { - tune2 <<= 1; - } else { - Buzzer.tune_reload <<= 1; - Buzzer.tune_reload |= tune1 & 1; - tune1 >>= 1; - } - } - Buzzer.tune = Buzzer.tune_reload; - } - Buzzer.count = count * 2; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BUZ: %d(%d),%d,%d,0x%08X(0x%08X)"), count, Buzzer.count, on, off, tune, Buzzer.tune); - - Buzzer.enable = (Buzzer.count > 0); - if (!Buzzer.enable) { - BuzzerOff(); - } -} - -void BuzzerSetStateToLed(uint32_t state) -{ - if (Buzzer.enable && (2 == Buzzer.mode)) { - Buzzer.state = (state != 0); - DigitalWrite(GPIO_BUZZER, (Buzzer.inverted) ? !Buzzer.state : Buzzer.state); - } -} - -void BuzzerBeep(uint32_t count) -{ - BuzzerBeep(count, 1, 1, 0, 0); -} - -void BuzzerEnabledBeep(uint32_t count, uint32_t duration) -{ - if (Settings.flag3.buzzer_enable) { - BuzzerBeep(count, duration, 1, 0, 0); - } -} - - - -bool BuzzerPinState(void) -{ - if (XdrvMailbox.index == GPIO_BUZZER_INV) { - Buzzer.inverted = 1; - XdrvMailbox.index -= (GPIO_BUZZER_INV - GPIO_BUZZER); - return true; - } - return false; -} - -void BuzzerInit(void) -{ - if (pin[GPIO_BUZZER] < 99) { - pinMode(pin[GPIO_BUZZER], OUTPUT); - BuzzerOff(); - } else { - Buzzer.active = false; - } -} - -void BuzzerEvery100mSec(void) -{ - if (Buzzer.enable && (Buzzer.mode != 2)) { - if (Buzzer.count) { - if (Buzzer.duration) { - Buzzer.duration--; - if (!Buzzer.duration) { - if (Buzzer.tune) { - Buzzer.state = Buzzer.tune & 1; - Buzzer.tune >>= 1; - } else { - Buzzer.tune = Buzzer.tune_reload; - Buzzer.count -= (Buzzer.tune_reload) ? 2 : 1; - Buzzer.state = Buzzer.count & 1; - if (Buzzer.mode) { - Buzzer.count |= 2; - } - } - Buzzer.duration = Buzzer.set[Buzzer.state]; - } - } - DigitalWrite(GPIO_BUZZER, (Buzzer.inverted) ? !Buzzer.state : Buzzer.state); - } else { - Buzzer.enable = false; - } - } -} - - - - - -const char kBuzzerCommands[] PROGMEM = "|" - "Buzzer" ; - -void (* const BuzzerCommand[])(void) PROGMEM = { - &CmndBuzzer }; - -void CmndBuzzer(void) -{ -# 174 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_24_buzzer.ino" - if (XdrvMailbox.data_len > 0) { - if (XdrvMailbox.payload != 0) { - uint32_t parm[4] = { 0 }; - uint32_t mode = 0; - ParseParameters(4, parm); - if (XdrvMailbox.payload <= 0) { - parm[0] = 1; - mode = -XdrvMailbox.payload; - } - for (uint32_t i = 1; i < 3; i++) { - if (parm[i] < 1) { parm[i] = 1; } - } - BuzzerBeep(parm[0], parm[1], parm[2], parm[3], mode); - } else { - BuzzerBeep(0); - } - } else { - BuzzerBeep(1); - } - ResponseCmndDone(); -} - - - - - -bool Xdrv24(uint8_t function) -{ - bool result = false; - - if (Buzzer.active) { - switch (function) { - case FUNC_EVERY_100_MSECOND: - BuzzerEvery100mSec(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kBuzzerCommands, BuzzerCommand); - break; - case FUNC_PRE_INIT: - BuzzerInit(); - break; - case FUNC_PIN_STATE: - result = BuzzerPinState(); - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_25_A4988_Stepper.ino" -# 21 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_25_A4988_Stepper.ino" -#ifdef USE_A4988_STEPPER - - - - -#define XDRV_25 25 - -#include - -short A4988_dir_pin = pin[GPIO_MAX]; -short A4988_stp_pin = pin[GPIO_MAX]; -short A4988_ms1_pin = pin[GPIO_MAX]; -short A4988_ms2_pin = pin[GPIO_MAX]; -short A4988_ms3_pin = pin[GPIO_MAX]; -short A4988_ena_pin = pin[GPIO_MAX]; -int A4988_spr = 0; -float A4988_rpm = 0; -short A4988_mis = 0; - -A4988_Stepper* myA4988 = nullptr; - -void A4988Init(void) -{ - A4988_dir_pin = pin[GPIO_A4988_DIR]; - A4988_stp_pin = pin[GPIO_A4988_STP]; - A4988_ena_pin = pin[GPIO_A4988_ENA]; - A4988_ms1_pin = pin[GPIO_A4988_MS1]; - A4988_ms2_pin = pin[GPIO_A4988_MS2]; - A4988_ms3_pin = pin[GPIO_A4988_MS3]; - A4988_spr = 200; - A4988_rpm = 30; - A4988_mis = 1; - - myA4988 = new A4988_Stepper( A4988_spr - , A4988_rpm - , A4988_mis - , A4988_dir_pin - , A4988_stp_pin - , A4988_ena_pin - , A4988_ms1_pin - , A4988_ms2_pin - , A4988_ms3_pin ); -} - -const char kA4988Commands[] PROGMEM = "Motor|" - "Move|Rotate|Turn|MIS|SPR|RPM"; - -void (* const A4988Command[])(void) PROGMEM = { - &CmndDoMove,&CmndDoRotate,&CmndDoTurn,&CmndSetMIS,&CmndSetSPR,&CmndSetRPM}; - -void CmndDoMove(void) { - if (XdrvMailbox.data_len > 0) { - long stepsPlease = strtoul(XdrvMailbox.data,nullptr,10); - myA4988->doMove(stepsPlease); - ResponseCmndDone(); - } -} - -void CmndDoRotate(void) { - if (XdrvMailbox.data_len > 0) { - long degrsPlease = strtoul(XdrvMailbox.data,nullptr,10); - myA4988->doRotate(degrsPlease); - ResponseCmndDone(); - } -} - -void CmndDoTurn(void) { - if (XdrvMailbox.data_len > 0) { - float turnsPlease = strtod(XdrvMailbox.data,nullptr); - myA4988->doTurn(turnsPlease); - ResponseCmndDone(); - } -} - -void CmndSetMIS(void) { - if ((pin[GPIO_A4988_MS1] < 99) && (pin[GPIO_A4988_MS2] < 99) && (pin[GPIO_A4988_MS3] < 99) && (XdrvMailbox.data_len > 0)) { - short newMIS = strtoul(XdrvMailbox.data,nullptr,10); - myA4988->setMIS(newMIS); - ResponseCmndDone(); - } -} - -void CmndSetSPR(void) { - if (XdrvMailbox.data_len > 0) { - int newSPR = strtoul(XdrvMailbox.data,nullptr,10); - myA4988->setSPR(newSPR); - ResponseCmndDone(); - } -} - -void CmndSetRPM(void) { - if (XdrvMailbox.data_len > 0) { - short newRPM = strtoul(XdrvMailbox.data,nullptr,10); - myA4988->setRPM(newRPM); - ResponseCmndDone(); - } -} - - - - -bool Xdrv25(uint8_t function) -{ - bool result = false; - if ((pin[GPIO_A4988_DIR] < 99) && (pin[GPIO_A4988_STP] < 99)) { - switch (function) { - case FUNC_INIT: - A4988Init(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kA4988Commands, A4988Command); - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_26_ariluxrf.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_26_ariluxrf.ino" -#ifdef USE_LIGHT -#ifdef USE_ARILUX_RF - - - - -#define XDRV_26 26 - -const uint32_t ARILUX_RF_TIME_AVOID_DUPLICATE = 1000; - -const uint8_t ARILUX_RF_MAX_CHANGES = 51; -const uint32_t ARILUX_RF_SEPARATION_LIMIT = 4300; -const uint32_t ARILUX_RF_RECEIVE_TOLERANCE = 60; - -struct ARILUX { - unsigned int rf_timings[ARILUX_RF_MAX_CHANGES]; - - unsigned long rf_received_value = 0; - unsigned long rf_last_received_value = 0; - unsigned long rf_last_time = 0; - unsigned long rf_lasttime = 0; - - unsigned int rf_change_count = 0; - unsigned int rf_repeat_count = 0; - - uint8_t rf_toggle = 0; -} Arilux; - -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 -#ifndef USE_WS2812_DMA -void AriluxRfInterrupt(void) ICACHE_RAM_ATTR; -#endif -#endif - -void AriluxRfInterrupt(void) -{ - unsigned long time = micros(); - unsigned int duration = time - Arilux.rf_lasttime; - - if (duration > ARILUX_RF_SEPARATION_LIMIT) { - if (abs(duration - Arilux.rf_timings[0]) < 200) { - Arilux.rf_repeat_count++; - if (Arilux.rf_repeat_count == 2) { - unsigned long code = 0; - const unsigned int delay = Arilux.rf_timings[0] / 31; - const unsigned int delayTolerance = delay * ARILUX_RF_RECEIVE_TOLERANCE / 100; - for (unsigned int i = 1; i < Arilux.rf_change_count -1; i += 2) { - code <<= 1; - if (abs(Arilux.rf_timings[i] - (delay *3)) < delayTolerance && abs(Arilux.rf_timings[i +1] - delay) < delayTolerance) { - code |= 1; - } - } - if (Arilux.rf_change_count > 49) { - Arilux.rf_received_value = code; - } - Arilux.rf_repeat_count = 0; - } - } - Arilux.rf_change_count = 0; - } - if (Arilux.rf_change_count >= ARILUX_RF_MAX_CHANGES) { - Arilux.rf_change_count = 0; - Arilux.rf_repeat_count = 0; - } - Arilux.rf_timings[Arilux.rf_change_count++] = duration; - Arilux.rf_lasttime = time; -} - -void AriluxRfHandler(void) -{ - unsigned long now = millis(); - if (Arilux.rf_received_value && !((Arilux.rf_received_value == Arilux.rf_last_received_value) && (now - Arilux.rf_last_time < ARILUX_RF_TIME_AVOID_DUPLICATE))) { - Arilux.rf_last_received_value = Arilux.rf_received_value; - Arilux.rf_last_time = now; - - uint16_t hostcode = Arilux.rf_received_value >> 8 & 0xFFFF; - if (Settings.rf_code[1][6] == Settings.rf_code[1][7]) { - Settings.rf_code[1][6] = hostcode >> 8 & 0xFF; - Settings.rf_code[1][7] = hostcode & 0xFF; - } - uint16_t stored_hostcode = Settings.rf_code[1][6] << 8 | Settings.rf_code[1][7]; - - DEBUG_DRIVER_LOG(PSTR(D_LOG_RFR D_HOST D_CODE " 0x%04X, " D_RECEIVED " 0x%06X"), stored_hostcode, Arilux.rf_received_value); - - if (hostcode == stored_hostcode) { - char command[33]; - char value = '-'; - command[0] = '\0'; - uint8_t keycode = Arilux.rf_received_value & 0xFF; - switch (keycode) { - case 1: - case 3: - snprintf_P(command, sizeof(command), PSTR(D_CMND_POWER " %d"), (1 == keycode) ? 1 : 0); - break; - case 2: - Arilux.rf_toggle++; - Arilux.rf_toggle &= 0x3; - snprintf_P(command, sizeof(command), PSTR(D_CMND_COLOR " %d"), 200 + Arilux.rf_toggle); - break; - case 4: - value = '+'; - case 7: - snprintf_P(command, sizeof(command), PSTR(D_CMND_SPEED " %c"), value); - break; - case 5: - value = '+'; - case 8: - snprintf_P(command, sizeof(command), PSTR(D_CMND_SCHEME " %c"), value); - break; - case 6: - value = '+'; - case 9: - snprintf_P(command, sizeof(command), PSTR(D_CMND_DIMMER " %c"), value); - break; - default: { - if ((keycode >= 10) && (keycode <= 21)) { - snprintf_P(command, sizeof(command), PSTR(D_CMND_COLOR " %d"), keycode -9); - } - } - } - if (strlen(command)) { - ExecuteCommand(command, SRC_LIGHT); - } - } - } - Arilux.rf_received_value = 0; -} - -void AriluxRfInit(void) -{ - if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_ARIRFSEL] < 99)) { - if (Settings.last_module != Settings.module) { - Settings.rf_code[1][6] = 0; - Settings.rf_code[1][7] = 0; - Settings.last_module = Settings.module; - } - Arilux.rf_received_value = 0; - - digitalWrite(pin[GPIO_ARIRFSEL], 0); - attachInterrupt(pin[GPIO_ARIRFRCV], AriluxRfInterrupt, CHANGE); - } -} - -void AriluxRfDisable(void) -{ - if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_ARIRFSEL] < 99)) { - detachInterrupt(pin[GPIO_ARIRFRCV]); - digitalWrite(pin[GPIO_ARIRFSEL], 1); - } -} - - - - - -bool Xdrv26(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_50_MSECOND: - if (pin[GPIO_ARIRFRCV] < 99) { AriluxRfHandler(); } - break; - case FUNC_EVERY_SECOND: - if (10 == uptime) { AriluxRfInit(); } - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_27_shutter.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_27_shutter.ino" -#ifdef USE_SHUTTER - - - - -#define XDRV_27 27 - -#define D_SHUTTER "SHUTTER" - -const uint16_t MOTOR_STOP_TIME = 500; -const uint8_t steps_per_second = 20; - -uint8_t calibrate_pos[6] = {0,30,50,70,90,100}; -uint16_t messwerte[5] = {30,50,70,90,100}; -uint16_t last_execute_step; - -enum ShutterModes { SHT_OFF_OPEN__OFF_CLOSE, SHT_OFF_ON__OPEN_CLOSE, SHT_PULSE_OPEN__PULSE_CLOSE, SHT_OFF_ON__OPEN_CLOSE_STEPPER,}; -enum ShutterButtonStates { SHT_NOT_PRESSED, SHT_PRESSED_MULTI, SHT_PRESSED_HOLD, SHT_PRESSED_IMMEDIATE, SHT_PRESSED_EXT_HOLD, SHT_PRESSED_MULTI_SIMULTANEOUS, SHT_PRESSED_HOLD_SIMULTANEOUS, SHT_PRESSED_EXT_HOLD_SIMULTANEOUS,}; - -const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|" - D_CMND_SHUTTER_OPEN "|" D_CMND_SHUTTER_CLOSE "|" D_CMND_SHUTTER_TOGGLE "|" D_CMND_SHUTTER_STOP "|" D_CMND_SHUTTER_POSITION "|" - D_CMND_SHUTTER_OPENTIME "|" D_CMND_SHUTTER_CLOSETIME "|" D_CMND_SHUTTER_RELAY "|" - D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|" - D_CMND_SHUTTER_MOTORDELAY "|" D_CMND_SHUTTER_FREQUENCY "|" D_CMND_SHUTTER_BUTTON "|" D_CMND_SHUTTER_LOCK "|" D_CMND_SHUTTER_ENABLEENDSTOPTIME "|" D_CMND_SHUTTER_INVERTWEBBUTTONS "|" - D_CMND_SHUTTER_STOPOPEN "|" D_CMND_SHUTTER_STOPCLOSE "|" D_CMND_SHUTTER_STOPTOGGLE "|" D_CMND_SHUTTER_STOPPOSITION; - -void (* const ShutterCommand[])(void) PROGMEM = { - &CmndShutterOpen, &CmndShutterClose, &CmndShutterToggle, &CmndShutterStop, &CmndShutterPosition, - &CmndShutterOpenTime, &CmndShutterCloseTime, &CmndShutterRelay, - &CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay, - &CmndShutterFrequency, &CmndShutterButton, &CmndShutterLock, &CmndShutterEnableEndStopTime, &CmndShutterInvertWebButtons, - &CmndShutterStopOpen, &CmndShutterStopClose, &CmndShutterStopToggle, &CmndShutterStopPosition}; - - const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"Direction\":%d,\"Target\":%d}"; - const char JSON_SHUTTER_BUTTON[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Button%d\":%d}"; - -#include - -Ticker TickerShutter; - -struct SHUTTER { - power_t mask = 0; - power_t old_power = 0; - power_t switched_relay = 0; - uint32_t time[MAX_SHUTTERS]; - int32_t open_max[MAX_SHUTTERS]; - int32_t target_position[MAX_SHUTTERS]; - int32_t start_position[MAX_SHUTTERS]; - int32_t real_position[MAX_SHUTTERS]; - uint16_t open_time[MAX_SHUTTERS]; - uint16_t close_time[MAX_SHUTTERS]; - uint16_t close_velocity[MAX_SHUTTERS]; - int8_t direction[MAX_SHUTTERS]; - uint8_t mode = 0; - int16_t motordelay[MAX_SHUTTERS]; - int16_t pwm_frequency[MAX_SHUTTERS]; - uint16_t max_pwm_frequency = 1000; - uint16_t max_close_pwm_frequency[MAX_SHUTTERS]; - uint8_t skip_relay_change; - int32_t accelerator[MAX_SHUTTERS]; - uint8_t start_reported = 0; -} Shutter; - -void ShutterLogPos(uint32_t i) -{ - char stemp2[10]; - dtostrfd((float)Shutter.time[i] / steps_per_second, 2, stemp2); - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter%d Real %d, Start %d, Stop %d, Dir %d, Delay %d, Rtc %s [s], Freq %d"), - i+1, Shutter.real_position[i], Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i], Shutter.motordelay[i], stemp2, Shutter.pwm_frequency[i]); -} - -void ShutterRtc50mS(void) -{ - for (uint8_t i = 0; i < shutters_present; i++) { - Shutter.time[i]++; - if (Shutter.accelerator[i]) { - - Shutter.pwm_frequency[i] += Shutter.accelerator[i]; - Shutter.pwm_frequency[i] = tmax(0,tmin(Shutter.direction[i]==1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i],Shutter.pwm_frequency[i])); - analogWriteFreq(Shutter.pwm_frequency[i]); - analogWrite(pin[GPIO_PWM1+i], 50); - } - } -} - -#define SHT_DIV_ROUND(__A,__B) (((__A) + (__B)/2) / (__B)) - -int32_t ShutterPercentToRealPosition(uint32_t percent, uint32_t index) -{ - if (Settings.shutter_set50percent[index] != 50) { - return (percent <= 5) ? Settings.shuttercoeff[2][index] * percent : Settings.shuttercoeff[1][index] * percent + Settings.shuttercoeff[0][index]; - } else { - uint32_t realpos; - - for (uint32_t j = 0; j < 5; j++) { - if (0 == Settings.shuttercoeff[j][index]) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SHT: RESET/INIT CALIBRATION MATRIX DIV 0")); - for (uint32_t k = 0; k < 5; k++) { - Settings.shuttercoeff[k][index] = SHT_DIV_ROUND(calibrate_pos[k+1] * 1000, calibrate_pos[5]); - } - } - } - for (uint32_t i = 0; i < 5; i++) { - if ((percent * 10) >= Settings.shuttercoeff[i][index]) { - realpos = SHT_DIV_ROUND(Shutter.open_max[index] * calibrate_pos[i+1], 100); - - } else { - if (0 == i) { - realpos = SHT_DIV_ROUND(SHT_DIV_ROUND(percent * Shutter.open_max[index] * calibrate_pos[i+1], Settings.shuttercoeff[i][index]), 10); - } else { - - - realpos += SHT_DIV_ROUND(SHT_DIV_ROUND((percent*10 - Settings.shuttercoeff[i-1][index] ) * Shutter.open_max[index] * (calibrate_pos[i+1] - calibrate_pos[i]), Settings.shuttercoeff[i][index] - Settings.shuttercoeff[i-1][index]), 100); - } - break; - } - } - return realpos; - } -} - -uint8_t ShutterRealToPercentPosition(int32_t realpos, uint32_t index) -{ - if (Settings.shutter_set50percent[index] != 50) { - return (Settings.shuttercoeff[2][index] * 5 > realpos) ? SHT_DIV_ROUND(realpos, Settings.shuttercoeff[2][index]) : SHT_DIV_ROUND(realpos-Settings.shuttercoeff[0][index], Settings.shuttercoeff[1][index]); - } else { - uint16_t realpercent; - - for (uint32_t i = 0; i < 5; i++) { - if (realpos >= Shutter.open_max[index] * calibrate_pos[i+1] / 100) { - realpercent = SHT_DIV_ROUND(Settings.shuttercoeff[i][index], 10); - - } else { - if (0 == i) { - realpercent = SHT_DIV_ROUND(SHT_DIV_ROUND((realpos - SHT_DIV_ROUND(Shutter.open_max[index] * calibrate_pos[i], 100)) * 10 * Settings.shuttercoeff[i][index], calibrate_pos[i+1]), Shutter.open_max[index]); - } else { - - - - realpercent += SHT_DIV_ROUND(SHT_DIV_ROUND((realpos - SHT_DIV_ROUND(Shutter.open_max[index] * calibrate_pos[i], 100)) * 10 * (Settings.shuttercoeff[i][index] - Settings.shuttercoeff[i-1][index]), (calibrate_pos[i+1] - calibrate_pos[i])), Shutter.open_max[index]) ; - } - break; - } - } - return realpercent; - } -} - -void ShutterInit(void) -{ - shutters_present = 0; - Shutter.mask = 0; - - Shutter.old_power = power; - bool relay_in_interlock = false; - - - if (Settings.shutter_startrelay[MAX_SHUTTERS] == 0) { - Shutter.max_pwm_frequency = Settings.shuttercoeff[4][3] > 0 ? Settings.shuttercoeff[4][3] : Shutter.max_pwm_frequency; - } - for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { - - Settings.shutter_startrelay[i] = (Settings.shutter_startrelay[i] == 0 && i == 0? 1 : Settings.shutter_startrelay[i]); - if (Settings.shutter_startrelay[i] && (Settings.shutter_startrelay[i] < 9)) { - shutters_present++; - - - Shutter.mask |= 3 << (Settings.shutter_startrelay[i] -1) ; - - for (uint32_t j = 0; j < MAX_INTERLOCKS * Settings.flag.interlock; j++) { - - if (Settings.interlock[j] && (Settings.interlock[j] & Shutter.mask)) { - - relay_in_interlock = true; - } - } - if (relay_in_interlock) { - if (Settings.pulse_timer[i] > 0) { - Shutter.mode = SHT_PULSE_OPEN__PULSE_CLOSE; - } else { - Shutter.mode = SHT_OFF_OPEN__OFF_CLOSE; - } - } else { - Shutter.mode = SHT_OFF_ON__OPEN_CLOSE; - if ((pin[GPIO_PWM1+i] < 99) && (pin[GPIO_CNTR1+i] < 99)) { - Shutter.mode = SHT_OFF_ON__OPEN_CLOSE_STEPPER; - Shutter.pwm_frequency[i] = 0; - Shutter.accelerator[i] = 0; - analogWriteFreq(Shutter.pwm_frequency[i]); - analogWrite(pin[GPIO_PWM1+i], 50); - } - } - - TickerShutter.attach_ms(50, ShutterRtc50mS ); - - Settings.shutter_set50percent[i] = (Settings.shutter_set50percent[i] > 0) ? Settings.shutter_set50percent[i] : 50; - - - Shutter.open_time[i] = (Settings.shutter_opentime[i] > 0) ? Settings.shutter_opentime[i] : 100; - Shutter.close_time[i] = (Settings.shutter_closetime[i] > 0) ? Settings.shutter_closetime[i] : 100; - - - Shutter.open_max[i] = 200 * Shutter.open_time[i]; - Shutter.close_velocity[i] = Shutter.open_max[i] / Shutter.close_time[i] / 2 ; - Shutter.max_close_pwm_frequency[i] = Shutter.max_pwm_frequency*Shutter.open_time[i] / Shutter.close_time[i]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d Closefreq: %d"),i, Shutter.max_close_pwm_frequency[i]); - - - if (Settings.shutter_set50percent[i] != 50) { - Settings.shuttercoeff[1][i] = Shutter.open_max[i] * (100 - Settings.shutter_set50percent[i] ) / 5000; - Settings.shuttercoeff[0][i] = Shutter.open_max[i] - (Settings.shuttercoeff[1][i] * 100); - Settings.shuttercoeff[2][i] = (Settings.shuttercoeff[0][i] + 5 * Settings.shuttercoeff[1][i]) / 5; - } - Shutter.mask |= 3 << (Settings.shutter_startrelay[i] -1); - - Shutter.real_position[i] = ShutterPercentToRealPosition(Settings.shutter_position[i], i); - - Shutter.start_position[i] = Shutter.target_position[i] = Shutter.real_position[i]; - Shutter.motordelay[i] = Settings.shutter_motordelay[i]; - - char shutter_open_chr[10]; - dtostrfd((float)Shutter.open_time[i] / 10 , 1, shutter_open_chr); - char shutter_close_chr[10]; - dtostrfd((float)Shutter.close_time[i] / 10, 1, shutter_close_chr); - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d (Relay:%d): Init. Pos: %d [%d %%], Open Vel.: 100, Close Vel.: %d , Max Way: %d, Opentime %s [s], Closetime %s [s], CoeffCalc: c0: %d, c1 %d, c2: %d, c3: %d, c4: %d, binmask %d, is inverted %d, is locked %d, end stop time enabled %d, webButtons inverted %d, shuttermode %d, motordelay %d"), - i+1, Settings.shutter_startrelay[i], Shutter.real_position[i], Settings.shutter_position[i], Shutter.close_velocity[i], Shutter.open_max[i], shutter_open_chr, shutter_close_chr, - Settings.shuttercoeff[0][i], Settings.shuttercoeff[1][i], Settings.shuttercoeff[2][i], Settings.shuttercoeff[3][i], Settings.shuttercoeff[4][i], - Shutter.mask, (Settings.shutter_options[i]&1) ? 1 : 0, (Settings.shutter_options[i]&2) ? 1 : 0, (Settings.shutter_options[i]&4) ? 1 : 0, (Settings.shutter_options[i]&8) ? 1 : 0, Shutter.mode, Shutter.motordelay[i]); - - } else { - - break; - } - ShutterLimitRealAndTargetPositions(i); - Settings.shutter_accuracy = 1; - } -} - -void ShutterReportPosition(bool always) -{ - Response_P(PSTR("{")); - rules_flag.shutter_moving = 0; - for (uint32_t i = 0; i < shutters_present; i++) { - - uint32_t position = ShutterRealToPercentPosition(Shutter.real_position[i], i); - if (Shutter.direction[i] != 0) { - rules_flag.shutter_moving = 1; - ShutterLogPos(i); - } - if (i) { ResponseAppend_P(PSTR(",")); } - uint32_t target = ShutterRealToPercentPosition(Shutter.target_position[i], i); - ResponseAppend_P(JSON_SHUTTER_POS, i+1, (Settings.shutter_options[i] & 1) ? 100-position : position, Shutter.direction[i],(Settings.shutter_options[i] & 1) ? 100-target : target ); - } - ResponseJsonEnd(); - if (always || (rules_flag.shutter_moving)) { - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_PRFX_SHUTTER)); - - } - - - -} - -void ShutterLimitRealAndTargetPositions(uint32_t i) { - if (Shutter.real_position[i]<0) Shutter.real_position[i] = 0; - if (Shutter.real_position[i]>Shutter.open_max[i]) Shutter.real_position[i] = Shutter.open_max[i]; - if (Shutter.target_position[i]<0) Shutter.target_position[i] = 0; - if (Shutter.target_position[i]>Shutter.open_max[i]) Shutter.target_position[i] = Shutter.open_max[i]; -} - -void ShutterUpdatePosition(void) -{ - - char scommand[CMDSZ]; - char stopic[TOPSZ]; - - for (uint32_t i = 0; i < shutters_present; i++) { - if (Shutter.direction[i] != 0) { - int32_t stop_position_delta = 20; - if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) { - - - Shutter.real_position[i] = ShutterCounterBasedPosition(i); - if (!Shutter.start_reported) { - ShutterReportPosition(true); - XdrvRulesProcess(); - Shutter.start_reported = 1; - } - - int32_t max_frequency = Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i]; - int32_t max_freq_change_per_sec = Shutter.max_pwm_frequency*steps_per_second / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); - int32_t min_runtime_ms = Shutter.pwm_frequency[i]*1000 / max_freq_change_per_sec; - int32_t velocity = Shutter.direction[i] == 1 ? 100 : Shutter.close_velocity[i]; - int32_t minstopway = min_runtime_ms * velocity / 100 * Shutter.pwm_frequency[i] / max_frequency * Shutter.direction[i] ; - - int32_t next_possible_stop = Shutter.real_position[i] + minstopway ; - stop_position_delta =200 * Shutter.pwm_frequency[i]/max_frequency + Shutter.direction[i] * (next_possible_stop - Shutter.target_position[i]); - - - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, velocity %d, minstopway %d,cur_freq %d, max_frequency %d, act_freq_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d"),Shutter.time[i],velocity,minstopway, - Shutter.pwm_frequency[i],max_frequency, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i]); - - if (Shutter.accelerator[i] < 0 || next_possible_stop * Shutter.direction[i] > (Shutter.target_position[i]- (100 * Shutter.direction[i])) * Shutter.direction[i] ) { - - Shutter.accelerator[i] = - tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*11/200); - - } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_frequency[i] == max_frequency) { - Shutter.accelerator[i] = 0; - } - } else { - Shutter.real_position[i] = Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); - } - if ( Shutter.real_position[i] * Shutter.direction[i] + stop_position_delta >= Shutter.target_position[i] * Shutter.direction[i] ) { - - - uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter.direction[i] == 1 ? 0 : 1) ; - int16_t missing_steps; - - switch (Shutter.mode) { - case SHT_PULSE_OPEN__PULSE_CLOSE: - - if (SRC_PULSETIMER == last_source || SRC_SHUTTER == last_source || SRC_WEBGUI == last_source) { - ExecuteCommandPower(cur_relay, 1, SRC_SHUTTER); - } else { - last_source = SRC_SHUTTER; - } - break; - case SHT_OFF_ON__OPEN_CLOSE_STEPPER: - missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) - RtcSettings.pulse_counter[i]; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter.pwm_frequency[i]); - Shutter.accelerator[i] = 0; - Shutter.pwm_frequency[i] = Shutter.pwm_frequency[i] > 250 ? 250 : Shutter.pwm_frequency[i]; - analogWriteFreq(Shutter.pwm_frequency[i]); - analogWrite(pin[GPIO_PWM1+i], 50); - Shutter.pwm_frequency[i] = 0; - analogWriteFreq(Shutter.pwm_frequency[i]); - while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) { - delay(1); - } - analogWrite(pin[GPIO_PWM1+i], 0); - Shutter.real_position[i] = ShutterCounterBasedPosition(i); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Real %d, pulsecount %d, start %d"), Shutter.real_position[i],RtcSettings.pulse_counter[i], Shutter.start_position[i]); - - if ((1 << (Settings.shutter_startrelay[i]-1)) & power) { - ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); - ExecuteCommandPower(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER); - } - break; - case SHT_OFF_ON__OPEN_CLOSE: - if ((1 << (Settings.shutter_startrelay[i]-1)) & power) { - ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); - ExecuteCommandPower(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER); - } - break; - case SHT_OFF_OPEN__OFF_CLOSE: - - if ((1 << (cur_relay-1)) & power) { - - ExecuteCommandPower(cur_relay, 0, SRC_SHUTTER); - } - break; - } - ShutterLimitRealAndTargetPositions(i); - Settings.shutter_position[i] = ShutterRealToPercentPosition(Shutter.real_position[i], i); - - ShutterLogPos(i); - - Shutter.start_position[i] = Shutter.real_position[i]; - - - snprintf_P(scommand, sizeof(scommand),PSTR(D_SHUTTER "%d"), i+1); - GetTopic_P(stopic, STAT, mqtt_topic, scommand); - Response_P("%d", (Settings.shutter_options[i] & 1) ? 100 - Settings.shutter_position[i]: Settings.shutter_position[i]); - MqttPublish(stopic, Settings.flag.mqtt_power_retain); - - Shutter.direction[i] = 0; - ShutterReportPosition(true); - rules_flag.shutter_moved = 1; - XdrvRulesProcess(); - } - } - } -} - -bool ShutterState(uint32_t device) -{ - device--; - device &= 3; - return (Settings.flag3.shutter_mode && - (Shutter.mask & (1 << (Settings.shutter_startrelay[device]-1))) ); -} - -void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos) -{ - - if ( ( (1 == direction) && ((Shutter.open_max[i] - Shutter.real_position[i]) / 100 <= 2) ) - || ( (-1 == direction) && (Shutter.real_position[i] / Shutter.close_velocity[i] <= 2)) ) { - Shutter.skip_relay_change = 1; - } else { - if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) { - Shutter.pwm_frequency[i] = 0; - analogWriteFreq(Shutter.pwm_frequency[i]); - analogWrite(pin[GPIO_PWM1+i], 0); - RtcSettings.pulse_counter[i] = 0; - Shutter.accelerator[i] = Shutter.max_pwm_frequency / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Ramp up: %d"), Shutter.accelerator[i]); - } - Shutter.target_position[i] = target_pos; - Shutter.start_position[i] = Shutter.real_position[i]; - Shutter.time[i] = 0; - Shutter.skip_relay_change = 0; - Shutter.direction[i] = direction; - rules_flag.shutter_moving = 1; - rules_flag.shutter_moved = 0; - Shutter.start_reported = 0; - - } - -} - -void ShutterWaitForMotorStop(uint32_t i) -{ - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Wait for Motorstop..")); - if ((SHT_OFF_ON__OPEN_CLOSE == Shutter.mode) || (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode)) { - if (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode) { - - while (Shutter.pwm_frequency[i] > 0) { - - Shutter.pwm_frequency[i] = tmax(Shutter.pwm_frequency[i]-((Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i])/(Shutter.motordelay[i]+1)) , 0); - - analogWriteFreq(Shutter.pwm_frequency[i]); - analogWrite(pin[GPIO_PWM1+i], 50); - delay(50); - } - analogWrite(pin[GPIO_PWM1+i], 0); - Shutter.real_position[i] = ShutterCounterBasedPosition(i); - } else { - ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); - delay(MOTOR_STOP_TIME); - } - } else { - delay(MOTOR_STOP_TIME); - } -} - -int32_t ShutterCounterBasedPosition(uint32_t i) -{ - return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_frequency)+Shutter.start_position[i]; -} - -void ShutterRelayChanged(void) -{ - - - - - char stemp1[10]; - - for (uint32_t i = 0; i < shutters_present; i++) { - power_t powerstate_local = (power >> (Settings.shutter_startrelay[i] -1)) & 3; - - uint8 manual_relays_changed = ((Shutter.switched_relay >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ; - - if (manual_relays_changed) { - - ShutterLimitRealAndTargetPositions(i); - if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE || Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) { - ShutterWaitForMotorStop(i); - switch (powerstate_local) { - case 1: - ShutterStartInit(i, 1, Shutter.open_max[i]); - break; - case 3: - ShutterStartInit(i, -1, 0); - break; - default: - - Shutter.target_position[i] = Shutter.real_position[i]; - } - } else { - if (Shutter.direction[i] != 0 && (!powerstate_local || (powerstate_local && Shutter.mode == SHT_PULSE_OPEN__PULSE_CLOSE))) { - Shutter.target_position[i] = Shutter.real_position[i]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed); - } else { - last_source = SRC_SHUTTER; - if (powerstate_local == 2) { - - ShutterWaitForMotorStop(i); - ShutterStartInit(i, -1, 0); - } else { - - ShutterWaitForMotorStop(i); - ShutterStartInit(i, 1, Shutter.open_max[i]); - } - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Target: %ld, powerstatelocal %d"), i+1, Shutter.target_position[i], powerstate_local); - } - } - } -} - -bool ShutterButtonIsSimultaneousHold(uint32_t button_index, uint32_t shutter_index) { - - uint32 min_shutterbutton_hold_timer = -1; - for (uint32_t i = 0; i < MAX_KEYS; i++) { - if ((button_index != i) && (Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index) && (Button.hold_timer[i] < min_shutterbutton_hold_timer)) - min_shutterbutton_hold_timer = Button.hold_timer[i]; - } - return ((-1 != min_shutterbutton_hold_timer) && (min_shutterbutton_hold_timer > (Button.hold_timer[button_index]>>1))); -} - -void ShutterButtonHandler(void) -{ - uint8_t buttonState = SHT_NOT_PRESSED; - uint8_t button = XdrvMailbox.payload; - uint8_t press_index; - uint32_t button_index = XdrvMailbox.index; - uint8_t shutter_index = Settings.shutter_button[button_index] & 0x03; - uint16_t loops_per_second = 1000 / Settings.button_debounce; - - if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { - if (Settings.flag.button_single) { - buttonState = SHT_PRESSED_MULTI; - press_index = 1; - } else { - if ((Shutter.direction[shutter_index]) && (Button.press_counter[button_index]==0)) { - buttonState = SHT_PRESSED_IMMEDIATE; - press_index = 1; - Button.press_counter[button_index] = 99; - } else { - Button.press_counter[button_index] = (Button.window_timer[button_index]) ? Button.press_counter[button_index] +1 : 1; - - Button.window_timer[button_index] = (loops_per_second >> 2) * 3; - } - } - blinks = 201; - } - - if (NOT_PRESSED == button) { - Button.hold_timer[button_index] = 0; - } else { - Button.hold_timer[button_index]++; - if (!Settings.flag.button_single) { - if (Settings.param[P_HOLD_IGNORE] > 0) { - if (Button.hold_timer[button_index] > loops_per_second * Settings.param[P_HOLD_IGNORE] / 10) { - Button.hold_timer[button_index] = 0; - Button.press_counter[button_index] = 0; - } - } - if ((Button.press_counter[button_index]<99) && (Button.hold_timer[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10)) { - - if (ShutterButtonIsSimultaneousHold(button_index, shutter_index)) { - - for (uint32_t i = 0; i < MAX_KEYS; i++) - if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index)) - Button.press_counter[i] = 99; - press_index = 0; - buttonState = SHT_PRESSED_HOLD_SIMULTANEOUS; - } - if (Button.press_counter[button_index]<99) { - press_index = 0; - buttonState = SHT_PRESSED_HOLD; - } - Button.press_counter[button_index] = 0; - } - if ((Button.press_counter[button_index]==0) && (Button.hold_timer[button_index] == loops_per_second * IMMINENT_RESET_FACTOR * Settings.param[P_HOLD_TIME] / 10)) { - press_index = -1; - - if (ShutterButtonIsSimultaneousHold(button_index, shutter_index)) { - - buttonState = SHT_PRESSED_EXT_HOLD_SIMULTANEOUS; - } else { - buttonState = SHT_PRESSED_EXT_HOLD; - } - } - } - } - - if (!Settings.flag.button_single) { - if (Button.window_timer[button_index]) { - Button.window_timer[button_index]--; - } else { - if (!restart_flag && !Button.hold_timer[button_index] && (Button.press_counter[button_index] > 0)) { - if (Button.press_counter[button_index]<99) { - - uint32 min_shutterbutton_press_counter = -1; - for (uint32_t i = 0; i < MAX_KEYS; i++) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Settings.shutter_button[i] %ld, shutter_index %d, Button.press_counter[i] %d, min_shutterbutton_press_counter %d, i %d"), Settings.shutter_button[i], shutter_index, Button.press_counter[i] , min_shutterbutton_press_counter, i); - if ((button_index != i) && (Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index) && (i != button_index) && (Button.press_counter[i] < min_shutterbutton_press_counter)) { - min_shutterbutton_press_counter = Button.press_counter[i]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: min_shutterbutton_press_counter %d"), min_shutterbutton_press_counter); - } - } - if (min_shutterbutton_press_counter == Button.press_counter[button_index]) { - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: simultanous presss deteced")); - press_index = Button.press_counter[button_index]; - for (uint32_t i = 0; i < MAX_KEYS; i++) - if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) != shutter_index)) - Button.press_counter[i] = 99; - buttonState = SHT_PRESSED_MULTI_SIMULTANEOUS; - } - if ((buttonState != SHT_PRESSED_MULTI_SIMULTANEOUS) && (Button.press_counter[button_index]<99)) { - - press_index = Button.press_counter[button_index]; - buttonState = SHT_PRESSED_MULTI; - } - } - Button.press_counter[button_index] = 0; - } - } - } - - if (buttonState != SHT_NOT_PRESSED) { - if ((!Settings.flag.button_restrict) && (((press_index>=5) && (press_index<=7)) || (buttonState == SHT_PRESSED_EXT_HOLD) || (buttonState == SHT_PRESSED_EXT_HOLD_SIMULTANEOUS))){ - - uint8_t shutter_index_num_buttons = 0; - for (uint32_t i = 0; i < MAX_KEYS; i++) { - if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index)) { - shutter_index_num_buttons++; - } - } - if ((buttonState == SHT_PRESSED_MULTI_SIMULTANEOUS) || ((shutter_index_num_buttons==1) && (buttonState == SHT_PRESSED_MULTI))){ - - - char scmnd[20]; - GetTextIndexed(scmnd, sizeof(scmnd), press_index -3, kCommands); - ExecuteCommand(scmnd, SRC_BUTTON); - return; - } else if ((buttonState == SHT_PRESSED_EXT_HOLD_SIMULTANEOUS) || ((shutter_index_num_buttons==1) && (buttonState == SHT_PRESSED_EXT_HOLD))){ - - - char scmnd[20]; - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); - ExecuteCommand(scmnd, SRC_BUTTON); - return; - } - } - if (buttonState <= SHT_PRESSED_IMMEDIATE) { - if (Settings.shutter_startrelay[shutter_index] && Settings.shutter_startrelay[shutter_index] <9) { - uint8_t pos_press_index = (buttonState == SHT_PRESSED_HOLD) ? 3 : (press_index-1); - if (pos_press_index>3) pos_press_index=3; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: shutter %d, button %d = %d (single=1, double=2, tripple=3, hold=4)"), shutter_index+1, button_index+1, pos_press_index+1); - XdrvMailbox.index = shutter_index +1; - last_source = SRC_BUTTON; - XdrvMailbox.data_len = 0; - char databuf[1] = ""; - XdrvMailbox.data = databuf; - XdrvMailbox.command = NULL; - if (buttonState == SHT_PRESSED_IMMEDIATE) { - XdrvMailbox.payload = XdrvMailbox.index; - CmndShutterStop(); - } else { - uint8_t position = (Settings.shutter_button[button_index]>>(6*pos_press_index + 2)) & 0x03f; - if (position) { - if (Shutter.direction[shutter_index]) { - XdrvMailbox.payload = XdrvMailbox.index; - CmndShutterStop(); - } else { - XdrvMailbox.payload = position = (position-1)<<1; - - if (102 == position) { - XdrvMailbox.payload = XdrvMailbox.index; - CmndShutterToggle(); - } else { - CmndShutterPosition(); - } - if (Settings.shutter_button[button_index] & ((0x01<<26)< 0) && (XdrvMailbox.index <= shutters_present)) { - uint32_t index = XdrvMailbox.index-1; - if (Shutter.direction[index]) { - CmndShutterStop(); - } else { - CmndShutterOpen(); - } - } -} - -void CmndShutterClose(void) -{ - - if ((1 == XdrvMailbox.index) && (XdrvMailbox.payload != -99)) { - XdrvMailbox.index = XdrvMailbox.payload; - } - XdrvMailbox.payload = 0; - XdrvMailbox.data_len = 0; - last_source = SRC_WEBGUI; - CmndShutterPosition(); -} - -void CmndShutterStopClose(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - uint32_t index = XdrvMailbox.index-1; - if (Shutter.direction[index]) { - CmndShutterStop(); - } else { - CmndShutterClose(); - } - } -} - -void CmndShutterToggle(void) -{ - - if ((1 == XdrvMailbox.index) && (XdrvMailbox.payload != -99)) { - XdrvMailbox.index = XdrvMailbox.payload; - } - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - uint32_t index = XdrvMailbox.index-1; - XdrvMailbox.payload = (50 < ShutterRealToPercentPosition(Shutter.real_position[index], index)) ? 0 : 100; - XdrvMailbox.data_len = 0; - last_source = SRC_WEBGUI; - CmndShutterPosition(); - } -} - -void CmndShutterStopToggle(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - uint32_t index = XdrvMailbox.index-1; - if (Shutter.direction[index]) { - CmndShutterStop(); - } else { - CmndShutterToggle(); - } - } -} - -void CmndShutterStop(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (!(Settings.shutter_options[XdrvMailbox.index-1] & 2)) { - if ((1 == XdrvMailbox.index) && (XdrvMailbox.payload != -99)) { - XdrvMailbox.index = XdrvMailbox.payload; - } - uint32_t i = XdrvMailbox.index -1; - if (Shutter.direction[i] != 0) { - - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Stop moving %d: dir: %d"), XdrvMailbox.index, Shutter.direction[i]); - - int32_t temp_realpos = Shutter.start_position[i] + ( (Shutter.time[i]+10) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); - XdrvMailbox.payload = ShutterRealToPercentPosition(temp_realpos, i); - - last_source = SRC_WEBGUI; - CmndShutterPosition(); - } else { - if (XdrvMailbox.command) - ResponseCmndDone(); - } - } else { - if (XdrvMailbox.command) - ResponseCmndIdxChar("Locked"); - } - } -} - -void CmndShutterPosition(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (!(Settings.shutter_options[XdrvMailbox.index-1] & 2)) { - uint32_t index = XdrvMailbox.index-1; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Pos. in: payload %s (%d), payload %d, idx %d, src %d"), XdrvMailbox.data , XdrvMailbox.data_len, XdrvMailbox.payload , XdrvMailbox.index, last_source ); - - - - if ((XdrvMailbox.data_len > 1) && (XdrvMailbox.payload <= 0)) { - - if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_UP) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_OPEN) || ((Shutter.direction[index]==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPOPEN))) { - CmndShutterOpen(); - return; - } - if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_DOWN) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_CLOSE) || ((Shutter.direction[index]==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPCLOSE))) { - CmndShutterClose(); - return; - } - if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_TOGGLE)) { - CmndShutterToggle(); - return; - } - if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOP) || ((Shutter.direction[index]) && (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPOPEN) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPCLOSE)))) { - XdrvMailbox.payload = -99; - CmndShutterStop(); - return; - } - } - - int8_t target_pos_percent = (XdrvMailbox.payload < 0) ? (XdrvMailbox.payload == -99 ? ShutterRealToPercentPosition(Shutter.real_position[index], index) : 0) : ((XdrvMailbox.payload > 100) ? 100 : XdrvMailbox.payload); - - target_pos_percent = ((Settings.shutter_options[index] & 1) && (SRC_WEBGUI != last_source)) ? 100 - target_pos_percent : target_pos_percent; - if (XdrvMailbox.payload != -99) { - - Shutter.target_position[index] = ShutterPercentToRealPosition(target_pos_percent, index); - - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: lastsource %d:, real %d, target %d, payload %d"), last_source, Shutter.real_position[index] ,Shutter.target_position[index],target_pos_percent); - } - if ( (target_pos_percent >= 0) && (target_pos_percent <= 100) && abs(Shutter.target_position[index] - Shutter.real_position[index] ) / Shutter.close_velocity[index] > 2) { - if (Settings.shutter_options[index] & 4) { - if (0 == target_pos_percent) Shutter.target_position[index] -= 1 * 2000; - if (100 == target_pos_percent) Shutter.target_position[index] += 1 * 2000; - } - int8_t new_shutterdirection = Shutter.real_position[index] < Shutter.target_position[index] ? 1 : -1; - if (Shutter.direction[index] == -new_shutterdirection) { - - if (SHT_PULSE_OPEN__PULSE_CLOSE == Shutter.mode) { - - ExecuteCommandPower(Settings.shutter_startrelay[index] + ((new_shutterdirection == 1) ? 0 : 1), 1, SRC_SHUTTER); - delay(100); - } else { - if (SHT_OFF_OPEN__OFF_CLOSE == Shutter.mode) { - ExecuteCommandPower(Settings.shutter_startrelay[index] + ((new_shutterdirection == 1) ? 1 : 0), 0, SRC_SHUTTER); - ShutterWaitForMotorStop(index); - } - } - } - if (Shutter.direction[index] != new_shutterdirection) { - if ((SHT_OFF_ON__OPEN_CLOSE == Shutter.mode) || (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode)) { - - ShutterWaitForMotorStop(index); - ExecuteCommandPower(Settings.shutter_startrelay[index], 0, SRC_SHUTTER); - ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]); - if (Shutter.skip_relay_change == 0) { - - ExecuteCommandPower(Settings.shutter_startrelay[index] +1, new_shutterdirection == 1 ? 0 : 1, SRC_SHUTTER); - - ExecuteCommandPower(Settings.shutter_startrelay[index], 1, SRC_SHUTTER); - } - } else { - - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Start in dir %d"), Shutter.direction[index]); - ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]); - if (Shutter.skip_relay_change == 0) { - ExecuteCommandPower(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER); - } - - } - Shutter.switched_relay = 0; - } - } else { - target_pos_percent = ShutterRealToPercentPosition(Shutter.real_position[index], index); - ShutterReportPosition(true); - } - XdrvMailbox.index = index +1; - if (XdrvMailbox.command) - ResponseCmndIdxNumber((Settings.shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent); - } else { - ShutterReportPosition(true); - if (XdrvMailbox.command) - ResponseCmndIdxChar("Locked"); - } - } -} - -void CmndShutterStopPosition(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - uint32_t index = XdrvMailbox.index-1; - if (Shutter.direction[index]) { - XdrvMailbox.payload = -99; - CmndShutterStop(); - } else { - CmndShutterPosition(); - } - } -} -void CmndShutterOpenTime(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (XdrvMailbox.data_len > 0) { - Settings.shutter_opentime[XdrvMailbox.index -1] = (uint16_t)(10 * CharToFloat(XdrvMailbox.data)); - ShutterInit(); - } - char time_chr[10]; - dtostrfd((float)(Settings.shutter_opentime[XdrvMailbox.index -1]) / 10, 1, time_chr); - ResponseCmndIdxChar(time_chr); - } -} - -void CmndShutterCloseTime(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (XdrvMailbox.data_len > 0) { - Settings.shutter_closetime[XdrvMailbox.index -1] = (uint16_t)(10 * CharToFloat(XdrvMailbox.data)); - ShutterInit(); - } - char time_chr[10]; - dtostrfd((float)(Settings.shutter_closetime[XdrvMailbox.index -1]) / 10, 1, time_chr); - ResponseCmndIdxChar(time_chr); - } -} - -void CmndShutterMotorDelay(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (XdrvMailbox.data_len > 0) { - Settings.shutter_motordelay[XdrvMailbox.index -1] = (uint16_t)(steps_per_second * CharToFloat(XdrvMailbox.data)); - ShutterInit(); - } - char time_chr[10]; - dtostrfd((float)(Settings.shutter_motordelay[XdrvMailbox.index -1]) / steps_per_second, 2, time_chr); - ResponseCmndIdxChar(time_chr); - } -} - -void CmndShutterRelay(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SHUTTERS)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 64)) { - Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload; - if (XdrvMailbox.payload > 0) { - Shutter.mask |= 3 << (XdrvMailbox.payload - 1); - } else { - Shutter.mask ^= 3 << (Settings.shutter_startrelay[XdrvMailbox.index -1] - 1); - } - Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload; - ShutterInit(); - - } - ResponseCmndIdxNumber(Settings.shutter_startrelay[XdrvMailbox.index -1]); - } -} - -void CmndShutterButton(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SHUTTERS)) { - uint32_t setting = 0; -# 1010 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_27_shutter.ino" - if (XdrvMailbox.data_len > 0) { - uint32_t i = 0; - uint32_t button_index = 0; - bool done = false; - bool isShortCommand = false; - char *str_ptr; - - char data_copy[strlen(XdrvMailbox.data) +1]; - strncpy(data_copy, XdrvMailbox.data, sizeof(data_copy)); - - for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < (1+4+4+1); str = strtok_r(nullptr, " ", &str_ptr), i++) { - int field; - switch (str[0]) { - case '-': - field = -1; - break; - case 't': - field = 102; - break; - default: - field = atoi(str); - break; - } - switch (i) { - case 0: - if ((field >= -1) && (field<=4)) { - button_index = (field<=0)?(-1):field; - done = (button_index==-1); - } else - done = true; - break; - case 1: - if (!strcmp_P(str, PSTR("up"))) { - setting |= (((100>>1)+1)<<2) | (((50>>1)+1)<<8) | (((75>>1)+1)<<14) | (((100>>1)+1)<<20); - isShortCommand = true; - break; - } else if (!strcmp_P(str, PSTR("down"))) { - setting |= (((0>>1)+1)<<2) | (((50>>1)+1)<<8) | (((25>>1)+1)<<14) | (((0>>1)+1)<<20); - isShortCommand = true; - break; - } else if (!strcmp_P(str, PSTR("updown"))) { - setting |= (((100>>1)+1)<<2) | (((0>>1)+1)<<8) | (((50>>1)+1)<<14); - isShortCommand = true; - break; - } else if (!strcmp_P(str, PSTR("toggle"))) { - setting |= (((102>>1)+1)<<2) | (((50>>1)+1)<<8); - isShortCommand = true; - break; - } - case 2: - if (isShortCommand) { - if ((field==1) && (setting & (0x3F<<(2+6*3)))) - - setting |= (0x3<<29); - done = true; - break; - } - case 3: - case 4: - if ((field >= -1) && (field<=102)) - setting |= (((field>>1)+1)<<(i*6 + (2-6))); - break; - case 5: - case 6: - case 7: - case 8: - case 9: - if (field==1) - setting |= (1<<(i + (26-5))); - break; - } - if (done) break; - } - - if (button_index) { - if (button_index==-1) { - - for (uint32_t i=0 ; i < MAX_KEYS ; i++) - if ((Settings.shutter_button[i]&0x3) == (XdrvMailbox.index-1)) - Settings.shutter_button[i] = 0; - } else { - if (setting) { - - setting |= (1<<31); - setting |= (XdrvMailbox.index-1) & 0x3; - } - Settings.shutter_button[button_index-1] = setting; - } - } - } - char setting_chr[30*MAX_KEYS] = "-", *setting_chr_ptr = setting_chr; - for (uint32_t i=0 ; i < MAX_KEYS ; i++) { - setting = Settings.shutter_button[i]; - if ((setting&(1<<31)) && ((setting&0x3) == (XdrvMailbox.index-1))) { - if (*setting_chr_ptr == 0) - setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR("|")); - setting_chr_ptr += snprintf_P(setting_chr_ptr, 2, PSTR("%d"), i+1); - - for (uint32_t j=0 ; j < 4 ; j++) { - int8_t pos = (((setting>> (2+6*j))&(0x3f))-1)<<1; - if (0 <= pos) - if (102 == pos) { - setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" t")); - } else { - setting_chr_ptr += snprintf_P(setting_chr_ptr, 5, PSTR(" %d"), pos); - } - else - setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" -")); - } - for (uint32_t j=0 ; j < 5 ; j++) { - bool mqtt = ((setting>>(26+j))&(0x01)!=0); - if (mqtt) - setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" 1")); - else - setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" -")); - } - } - } - ResponseCmndIdxChar(setting_chr); - } -} - -void CmndShutterSetHalfway(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - Settings.shutter_set50percent[XdrvMailbox.index -1] = (Settings.shutter_options[XdrvMailbox.index -1] & 1) ? 100 - XdrvMailbox.payload : XdrvMailbox.payload; - ShutterInit(); - } - ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 1) ? 100 - Settings.shutter_set50percent[XdrvMailbox.index -1] : Settings.shutter_set50percent[XdrvMailbox.index -1]); - } -} - -void CmndShutterFrequency(void) -{ - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 20000)) { - Shutter.max_pwm_frequency = XdrvMailbox.payload; - if (shutters_present < 4) { - Settings.shuttercoeff[4][3] = Shutter.max_pwm_frequency; - } - ShutterInit(); - ResponseCmndNumber(XdrvMailbox.payload); - } else { - ResponseCmndNumber(Shutter.max_pwm_frequency); - } -} - -void CmndShutterSetClose(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - Shutter.real_position[XdrvMailbox.index -1] = 0; - ShutterStartInit(XdrvMailbox.index -1, 0, 0); - Settings.shutter_position[XdrvMailbox.index -1] = 0; - ResponseCmndIdxChar(D_CONFIGURATION_RESET); - } -} - -void CmndShutterInvert(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (XdrvMailbox.payload == 0) { - Settings.shutter_options[XdrvMailbox.index -1] &= ~(1); - } else if (XdrvMailbox.payload == 1) { - Settings.shutter_options[XdrvMailbox.index -1] |= (1); - } - ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 1) ? 1 : 0); - } -} - -void CmndShutterCalibration(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (XdrvMailbox.data_len > 0) { - uint32_t i = 0; - char *str_ptr; - - char data_copy[strlen(XdrvMailbox.data) +1]; - strncpy(data_copy, XdrvMailbox.data, sizeof(data_copy)); - - for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < 5; str = strtok_r(nullptr, " ", &str_ptr), i++) { - int field = atoi(str); - - - if ((field <= 0) || (field > 30000) || ( (i>0) && (field <= messwerte[i-1]) ) ) { - break; - } - messwerte[i] = field; - } - for (i = 0; i < 5; i++) { - Settings.shuttercoeff[i][XdrvMailbox.index -1] = SHT_DIV_ROUND((uint32_t)messwerte[i] * 1000, messwerte[4]); - AddLog_P2(LOG_LEVEL_INFO, PSTR("Settings.shuttercoeff: %d, i: %d, value: %d, messwert %d"), i,XdrvMailbox.index -1,Settings.shuttercoeff[i][XdrvMailbox.index -1], messwerte[i]); - } - ShutterInit(); - ResponseCmndIdxChar(XdrvMailbox.data); - } else { - char setting_chr[30] = "0"; - snprintf_P(setting_chr, sizeof(setting_chr), PSTR("%d %d %d %d %d"), Settings.shuttercoeff[0][XdrvMailbox.index -1], Settings.shuttercoeff[1][XdrvMailbox.index -1], Settings.shuttercoeff[2][XdrvMailbox.index -1], Settings.shuttercoeff[3][XdrvMailbox.index -1], Settings.shuttercoeff[4][XdrvMailbox.index -1]); - ResponseCmndIdxChar(setting_chr); - } - } -} - -void CmndShutterLock(void) { - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (XdrvMailbox.payload == 0) { - Settings.shutter_options[XdrvMailbox.index -1] &= ~(2); - } else if (XdrvMailbox.payload == 1) { - Settings.shutter_options[XdrvMailbox.index -1] |= (2); - } - ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 2) ? 1 : 0); - } -} - -void CmndShutterEnableEndStopTime(void) { - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (XdrvMailbox.payload == 0) { - Settings.shutter_options[XdrvMailbox.index -1] &= ~(4); - } else if (XdrvMailbox.payload == 1) { - Settings.shutter_options[XdrvMailbox.index -1] |= (4); - } - ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 4) ? 1 : 0); - } -} - -void CmndShutterInvertWebButtons(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (XdrvMailbox.payload == 0) { - Settings.shutter_options[XdrvMailbox.index -1] &= ~(8); - } else if (XdrvMailbox.payload == 1) { - Settings.shutter_options[XdrvMailbox.index -1] |= (8); - } - ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 8) ? 1 : 0); - } -} - - - - - -bool Xdrv27(uint8_t function) -{ - bool result = false; - - if (Settings.flag3.shutter_mode) { - switch (function) { - case FUNC_PRE_INIT: - ShutterInit(); - break; - case FUNC_EVERY_50_MSECOND: - ShutterUpdatePosition(); - break; - case FUNC_EVERY_SECOND: - - ShutterReportPosition(false); - break; - - case FUNC_COMMAND: - result = DecodeCommand(kShutterCommands, ShutterCommand); - break; - case FUNC_JSON_APPEND: - for (uint8_t i = 0; i < shutters_present; i++) { - uint8_t position = (Settings.shutter_options[i] & 1) ? 100 - Settings.shutter_position[i] : Settings.shutter_position[i]; - uint8_t target = (Settings.shutter_options[i] & 1) ? 100 - ShutterRealToPercentPosition(Shutter.target_position[i], i) : ShutterRealToPercentPosition(Shutter.target_position[i], i); - - ResponseAppend_P(","); - ResponseAppend_P(JSON_SHUTTER_POS, i+1, position, Shutter.direction[i],target); -#ifdef USE_DOMOTICZ - if ((0 == tele_period) && (0 == i)) { - DomoticzSensor(DZ_SHUTTER, position); - } -#endif - } - break; - case FUNC_SET_POWER: - char stemp1[10]; - - Shutter.switched_relay = XdrvMailbox.index ^ Shutter.old_power; - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Switched relay: %d by %s"), Shutter.switched_relay,GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource)); - ShutterRelayChanged(); - Shutter.old_power = XdrvMailbox.index; - break; - case FUNC_SET_DEVICE_POWER: - if (Shutter.skip_relay_change ) { - uint8_t i; - for (i = 0; i < devices_present; i++) { - if (Shutter.switched_relay &1) { - break; - } - Shutter.switched_relay >>= 1; - } - - result = true; - Shutter.skip_relay_change = 0; - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Skipping switch off relay %d"),i); - ExecuteCommandPower(i+1, 0, SRC_SHUTTER); - } - break; - case FUNC_BUTTON_PRESSED: - if (Settings.shutter_button[XdrvMailbox.index] & (1<<31)) { - ShutterButtonHandler(); - result = true; - } - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_28_pcf8574.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_28_pcf8574.ino" -#ifdef USE_I2C -#ifdef USE_PCF8574 - - - - - - - -#define XDRV_28 28 -#define XI2C_02 2 - -#define PCF8574_ADDR1 0x20 -#define PCF8574_ADDR2 0x38 - -struct PCF8574 { - int error; - uint8_t pin[64]; - uint8_t address[MAX_PCF8574]; - uint8_t pin_mask[MAX_PCF8574] = { 0 }; - uint8_t max_connected_ports = 0; - uint8_t max_devices = 0; - char stype[9]; - bool type = false; -} Pcf8574; - -void Pcf8574SwitchRelay(void) -{ - for (uint32_t i = 0; i < devices_present; i++) { - uint8_t relay_state = bitRead(XdrvMailbox.index, i); - - - - if (Pcf8574.max_devices > 0 && Pcf8574.pin[i] < 99) { - uint8_t board = Pcf8574.pin[i]>>3; - uint8_t oldpinmask = Pcf8574.pin_mask[board]; - uint8_t _val = bitRead(rel_inverted, i) ? !relay_state : relay_state; - - - - if (_val) { - Pcf8574.pin_mask[board] |= _val << (Pcf8574.pin[i]&0x7); - } else { - Pcf8574.pin_mask[board] &= ~(1 << (Pcf8574.pin[i]&0x7)); - } - if (oldpinmask != Pcf8574.pin_mask[board]) { - Wire.beginTransmission(Pcf8574.address[board]); - Wire.write(Pcf8574.pin_mask[board]); - Pcf8574.error = Wire.endTransmission(); - } - - } - } -} - -void Pcf8574Init(void) -{ - uint8_t pcf8574_address = PCF8574_ADDR1; - while ((Pcf8574.max_devices < MAX_PCF8574) && (pcf8574_address < PCF8574_ADDR2 +8)) { - -#ifdef USE_MCP230xx_ADDR - if (USE_MCP230xx_ADDR == pcf8574_address) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("PCF: Address 0x%02x reserved for MCP320xx skipped"), pcf8574_address); - pcf8574_address++; - if ((PCF8574_ADDR1 +7) == pcf8574_address) { - pcf8574_address = PCF8574_ADDR2 +1; - } - } -#endif - - - - if (I2cSetDevice(pcf8574_address)) { - Pcf8574.type = true; - - Pcf8574.address[Pcf8574.max_devices] = pcf8574_address; - Pcf8574.max_devices++; - - strcpy(Pcf8574.stype, "PCF8574"); - if (pcf8574_address >= PCF8574_ADDR2) { - strcpy(Pcf8574.stype, "PCF8574A"); - } - I2cSetActiveFound(pcf8574_address, Pcf8574.stype); - } - - pcf8574_address++; - if ((PCF8574_ADDR1 +7) == pcf8574_address) { - pcf8574_address = PCF8574_ADDR2 +1; - } - } - if (Pcf8574.type) { - for (uint32_t i = 0; i < sizeof(Pcf8574.pin); i++) { - Pcf8574.pin[i] = 99; - } - devices_present = devices_present - Pcf8574.max_connected_ports; - Pcf8574.max_connected_ports = 0; - for (uint32_t idx = 0; idx < Pcf8574.max_devices; idx++) { - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PCF: Device %d config 0x%02x"), idx +1, Settings.pcf8574_config[idx]); - - for (uint32_t i = 0; i < 8; i++) { - uint8_t _result = Settings.pcf8574_config[idx] >> i &1; - - if (_result > 0) { - Pcf8574.pin[devices_present] = i + 8 * idx; - bitWrite(rel_inverted, devices_present, Settings.flag3.pcf8574_ports_inverted); - devices_present++; - Pcf8574.max_connected_ports++; - } - } - } - AddLog_P2(LOG_LEVEL_INFO, PSTR("PCF: Total devices %d, PCF8574 output ports %d"), Pcf8574.max_devices, Pcf8574.max_connected_ports); - } -} - - - - - -#ifdef USE_WEBSERVER - -#define WEB_HANDLE_PCF8574 "pcf" - -const char HTTP_BTN_MENU_PCF8574[] PROGMEM = - "

"; - -const char HTTP_FORM_I2C_PCF8574_1[] PROGMEM = - "
 " D_PCF8574_PARAMETERS " " - "
" - "


"; - -const char HTTP_FORM_I2C_PCF8574_2[] PROGMEM = - "" D_DEVICE " %d " D_PORT " %d"; - -void HandlePcf8574(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_PCF8574)); - - if (Webserver->hasArg("save")) { - Pcf8574SaveSettings(); - WebRestart(1); - return; - } - - WSContentStart_P(D_CONFIGURE_PCF8574); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_I2C_PCF8574_1, (Settings.flag3.pcf8574_ports_inverted) ? " checked" : ""); - WSContentSend_P(HTTP_TABLE100); - for (uint32_t idx = 0; idx < Pcf8574.max_devices; idx++) { - for (uint32_t idx2 = 0; idx2 < 8; idx2++) { - uint8_t helper = 1 << idx2; - WSContentSend_P(HTTP_FORM_I2C_PCF8574_2, - idx +1, idx2, - idx2 + 8*idx, - idx2 + 8*idx, - ((helper & Settings.pcf8574_config[idx]) >> idx2 == 0) ? " selected " : " ", - ((helper & Settings.pcf8574_config[idx]) >> idx2 == 1) ? " selected " : " " - ); - } - } - WSContentSend_P(PSTR("")); - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); -} - -void Pcf8574SaveSettings(void) -{ - char stemp[7]; - char tmp[100]; - - - - Settings.flag3.pcf8574_ports_inverted = Webserver->hasArg("b1"); - for (byte idx = 0; idx < Pcf8574.max_devices; idx++) { - byte count=0; - byte n = Settings.pcf8574_config[idx]; - while(n!=0) { - n = n&(n-1); - count++; - } - if (count <= devices_present) { - devices_present = devices_present - count; - } - for (byte i = 0; i < 8; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("i2cs%d"), i+8*idx); - WebGetArg(stemp, tmp, sizeof(tmp)); - byte _value = (!strlen(tmp)) ? 0 : atoi(tmp); - if (_value) { - Settings.pcf8574_config[idx] = Settings.pcf8574_config[idx] | 1 << i; - devices_present++; - Pcf8574.max_connected_ports++; - } else { - Settings.pcf8574_config[idx] = Settings.pcf8574_config[idx] & ~(1 << i ); - } - } - - - - } -} -#endif - - - - - -bool Xdrv28(uint8_t function) -{ - if (!I2cEnabled(XI2C_02)) { return false; } - - bool result = false; - - if (FUNC_PRE_INIT == function) { - Pcf8574Init(); - } - else if (Pcf8574.type) { - switch (function) { - case FUNC_SET_POWER: - Pcf8574SwitchRelay(); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_ADD_BUTTON: - WSContentSend_P(HTTP_BTN_MENU_PCF8574); - break; - case FUNC_WEB_ADD_HANDLER: - Webserver->on("/" WEB_HANDLE_PCF8574, HandlePcf8574); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_29_deepsleep.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_29_deepsleep.ino" -#ifdef USE_DEEPSLEEP -# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_29_deepsleep.ino" -#define XDRV_29 29 - -#define D_PRFX_DEEPSLEEP "DeepSleep" -#define D_CMND_DEEPSLEEP_TIME "Time" - -const uint32_t DEEPSLEEP_MAX = 10 * 366 * 24 * 60 * 60; -const uint32_t DEEPSLEEP_MAX_CYCLE = 60 * 60; -const uint32_t DEEPSLEEP_MIN_TIME = 5; -const uint32_t DEEPSLEEP_START_COUNTDOWN = 4; - -const char kDeepsleepCommands[] PROGMEM = D_PRFX_DEEPSLEEP "|" - D_CMND_DEEPSLEEP_TIME ; - -void (* const DeepsleepCommand[])(void) PROGMEM = { - &CmndDeepsleepTime }; - -uint32_t deepsleep_sleeptime = 0; -uint8_t deepsleep_flag = 0; - -bool DeepSleepEnabled(void) -{ - if ((Settings.deepsleep < 10) || (Settings.deepsleep > DEEPSLEEP_MAX)) { - Settings.deepsleep = 0; - return false; - } - - if (pin[GPIO_DEEPSLEEP] < 99) { - pinMode(pin[GPIO_DEEPSLEEP], INPUT_PULLUP); - return (digitalRead(pin[GPIO_DEEPSLEEP])); - } - - return true; -} - -void DeepSleepReInit(void) -{ - if ((ResetReason() == REASON_DEEP_SLEEP_AWAKE) && DeepSleepEnabled()) { - if ((RtcSettings.ultradeepsleep > DEEPSLEEP_MAX_CYCLE) && (RtcSettings.ultradeepsleep < 1700000000)) { - - RtcSettings.ultradeepsleep = RtcSettings.ultradeepsleep - DEEPSLEEP_MAX_CYCLE; - AddLog_P2(LOG_LEVEL_ERROR, PSTR("DSL: Remain DeepSleep %d"), RtcSettings.ultradeepsleep); - RtcSettingsSave(); - RtcRebootReset(); - ESP.deepSleep(100 * RtcSettings.deepsleep_slip * (DEEPSLEEP_MAX_CYCLE < RtcSettings.ultradeepsleep ? DEEPSLEEP_MAX_CYCLE : RtcSettings.ultradeepsleep), WAKE_RF_DEFAULT); - yield(); - - } - } - - RtcSettings.ultradeepsleep = 0; -} - -void DeepSleepPrepare(void) -{ - - - - - if ((RtcSettings.nextwakeup == 0) || - (RtcSettings.deepsleep_slip < 9000) || - (RtcSettings.deepsleep_slip > 11000) || - (RtcSettings.nextwakeup > (UtcTime() + Settings.deepsleep))) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("DSL: Reset wrong settings wakeup: %ld, slip %ld"), RtcSettings.nextwakeup, RtcSettings.deepsleep_slip ); - RtcSettings.nextwakeup = 0; - RtcSettings.deepsleep_slip = 10000; - } - - - - int16_t timeslip = (int16_t)(RtcSettings.nextwakeup + millis() / 1000 - UtcTime()) * 10; - - - - timeslip = (timeslip < -(int32_t)Settings.deepsleep) ? 0 : (timeslip > (int32_t)Settings.deepsleep) ? 0 : 1; - if (timeslip) { - RtcSettings.deepsleep_slip = (Settings.deepsleep + RtcSettings.nextwakeup - UtcTime()) * RtcSettings.deepsleep_slip / tmax((Settings.deepsleep - (millis() / 1000)),5); - - RtcSettings.deepsleep_slip = tmin(tmax(RtcSettings.deepsleep_slip, 9000), 11000); - RtcSettings.nextwakeup += Settings.deepsleep; - } - - - - if (RtcSettings.nextwakeup <= (UtcTime() - DEEPSLEEP_MIN_TIME)) { - - RtcSettings.nextwakeup += (((UtcTime() + DEEPSLEEP_MIN_TIME - RtcSettings.nextwakeup) / Settings.deepsleep) + 1) * Settings.deepsleep; - } - - String dt = GetDT(RtcSettings.nextwakeup + LocalTime() - UtcTime()); - - - deepsleep_sleeptime = tmin((uint32_t)DEEPSLEEP_MAX_CYCLE ,RtcSettings.nextwakeup - UtcTime()); - - - Response_P(PSTR("{\"" D_PRFX_DEEPSLEEP "\":{\"" D_JSON_TIME "\":\"%s\",\"Epoch\":%d}}"), (char*)dt.c_str(), RtcSettings.nextwakeup); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_STATUS)); - - - -} - -void DeepSleepStart(void) -{ - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION "Sleeping")); - - WifiShutdown(); - RtcSettings.ultradeepsleep = RtcSettings.nextwakeup - UtcTime(); - RtcSettingsSave(); - - ESP.deepSleep(100 * RtcSettings.deepsleep_slip * deepsleep_sleeptime); - yield(); -} - -void DeepSleepEverySecond(void) -{ - if (!deepsleep_flag) { return; } - - if (DeepSleepEnabled()) { - if (DEEPSLEEP_START_COUNTDOWN == deepsleep_flag) { - SettingsSaveAll(); - DeepSleepPrepare(); - } - deepsleep_flag--; - if (deepsleep_flag <= 0) { - DeepSleepStart(); - } - } else { - deepsleep_flag = 0; - } -} - - - - - -void CmndDeepsleepTime(void) -{ - if ((0 == XdrvMailbox.payload) || - ((XdrvMailbox.payload > 10) && (XdrvMailbox.payload < DEEPSLEEP_MAX))) { - Settings.deepsleep = XdrvMailbox.payload; - RtcSettings.nextwakeup = 0; - deepsleep_flag = (0 == XdrvMailbox.payload) ? 0 : DEEPSLEEP_START_COUNTDOWN; - if (deepsleep_flag) { - if (!Settings.tele_period) { - Settings.tele_period = TELE_PERIOD; - } - } - } - Response_P(S_JSON_COMMAND_NVALUE, XdrvMailbox.command, Settings.deepsleep); -} - - - - - -bool Xdrv29(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_SECOND: - DeepSleepEverySecond(); - break; - case FUNC_AFTER_TELEPERIOD: - if (DeepSleepEnabled() && !deepsleep_flag && (Settings.tele_period == 10 || Settings.tele_period == 300 || UpTime() > Settings.tele_period)) { - deepsleep_flag = DEEPSLEEP_START_COUNTDOWN; - } - break; - case FUNC_COMMAND: - result = DecodeCommand(kDeepsleepCommands, DeepsleepCommand); - break; - case FUNC_PRE_INIT: - DeepSleepReInit(); - break; - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" -#ifdef USE_LIGHT -#ifdef USE_EXS_DIMMER - - - - - - - -#define XDRV_30 30 - -#define EXS_GATE_1_ON 0x20 -#define EXS_GATE_1_OFF 0x21 -#define EXS_DIMM_1_ON 0x22 -#define EXS_DIMM_1_OFF 0x23 -#define EXS_DIMM_1_TBL 0x24 -#define EXS_DIMM_1_VAL 0x25 -#define EXS_GATE_2_ON 0x30 -#define EXS_GATE_2_OFF 0x31 -#define EXS_DIMM_2_ON 0x32 -#define EXS_DIMM_2_OFF 0x33 -#define EXS_DIMM_2_TBL 0x34 -#define EXS_DIMM_2_VAL 0x35 -#define EXS_GATES_ON 0x40 -#define EXS_GATES_OFF 0x41 -#define EXS_DIMMS_ON 0x50 -#define EXS_DIMMS_OFF 0x51 -#define EXS_CH_LOCK 0x60 -#define EXS_GET_VALUES 0xFA -#define EXS_WRITE_EE 0xFC -#define EXS_READ_EE 0xFD -#define EXS_GET_VERSION 0xFE -#define EXS_RESET 0xFF - -#define EXS_BUFFER_SIZE 256 -#define EXS_ACK_TIMEOUT 200 - -#include - -TasmotaSerial *ExsSerial = nullptr; - -typedef struct -{ - uint8_t on = 0; - uint8_t bright_tbl = 0; - uint8_t dimm = 0; - uint8_t impuls_start = 0; - uint32_t impuls_len = 0; -} CHANNEL; - -typedef struct -{ - uint8_t version_major = 0; - uint8_t version_minor = 0; - CHANNEL channel[2]; - uint8_t gate_lock = 0; -} DIMMER; - -struct EXS -{ - uint8_t *buffer = nullptr; - int byte_counter = 0; - int cmd_status = 0; - uint8_t power = 0; - uint8_t dimm[2] = {0, 0}; - DIMMER dimmer; -} Exs; - - - - - -uint8_t crc8(const uint8_t *p, uint8_t len) -{ - const uint8_t table[] = { - 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, - 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D}; - - const uint8_t table_rev[] = { - 0x00, 0x70, 0xE0, 0x90, 0xC1, 0xB1, 0x21, 0x51, - 0x83, 0xF3, 0x63, 0x13, 0x42, 0x32, 0xA2, 0xD2}; - - uint8_t offset; - uint8_t temp, crc8_temp; - uint8_t crc8 = 0; - - for (int i = 0; i < len; i++) - { - temp = *(p + i); - offset = temp ^ crc8; - offset >>= 4; - crc8_temp = crc8 & 0x0f; - crc8 = crc8_temp ^ table_rev[offset]; - offset = crc8 ^ temp; - offset &= 0x0f; - crc8_temp = crc8 & 0xf0; - crc8 = crc8_temp ^ table[offset]; - } - return crc8 ^ 0x55; -} - -void ExsSerialSend(const uint8_t data[] = nullptr, uint16_t len = 0) -{ - int retries = 3; - char rc; - -#ifdef EXS_DEBUG - snprintf_P(log_data, sizeof(log_data), PSTR("EXS: Tx Packet: \"")); - for (uint32_t i = 0; i < len; i++) - { - snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, data[i]); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s\""), log_data); - AddLog(LOG_LEVEL_DEBUG_MORE); -#endif - - while (retries) - { - retries--; - - ExsSerial->write(data, len); - ExsSerial->flush(); - - - uint32_t snd_time = millis(); - while ((TimePassedSince(snd_time) < EXS_ACK_TIMEOUT) && - (!ExsSerial->available())) - ; - - if (!ExsSerial->available()) - { - -#ifdef EXS_DEBUG - AddLog_P(LOG_LEVEL_DEBUG, PSTR("ESX: serial send timeout")); -#endif - continue; - } - - rc = ExsSerial->read(); - if (rc == 0xFF) - break; - } -} - -void ExsSendCmd(uint8_t cmd, uint8_t value) -{ - uint8_t buffer[8]; - uint16_t len; - - buffer[0] = 0x7b; - buffer[3] = cmd; - - switch (cmd) - { - case EXS_GATE_1_ON: - case EXS_GATE_1_OFF: - case EXS_DIMM_1_ON: - case EXS_DIMM_1_OFF: - case EXS_GATE_2_ON: - case EXS_GATE_2_OFF: - case EXS_DIMM_2_ON: - case EXS_DIMM_2_OFF: - case EXS_GATES_ON: - case EXS_GATES_OFF: - case EXS_DIMMS_ON: - case EXS_DIMMS_OFF: - case EXS_GET_VALUES: - case EXS_GET_VERSION: - case EXS_RESET: - buffer[2] = 1; - len = 4; - break; - - case EXS_CH_LOCK: - case EXS_DIMM_1_TBL: - case EXS_DIMM_1_VAL: - case EXS_DIMM_2_TBL: - case EXS_DIMM_2_VAL: - buffer[2] = 2; - buffer[4] = value; - len = 5; - break; - } - buffer[1] = crc8(&buffer[3], buffer[2]); - - ExsSerialSend(buffer, len); -} - -uint8_t ExsSetPower(uint8_t device, uint8_t power) -{ - Exs.dimmer.channel[device].dimm = power; - ExsSendCmd(EXS_DIMM_1_ON + 0x10 * device + power ^ 1, 0); -} - -uint8_t ExsSetBri(uint8_t device, uint8_t bri) -{ - Exs.dimmer.channel[device].bright_tbl = bri; - ExsSendCmd(EXS_DIMM_1_TBL + 0x10 * device, bri); -} - -uint8_t ExsSyncState(uint8_t device) -{ -#ifdef EXS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Channel %d Power Want %d, Is %d"), - device, bitRead(Exs.power, device), Exs.dimmer.channel[device].dimm); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Set Channel %d Brightness Want %d, Is %d"), - device, Exs.dimm[device], Exs.dimmer.channel[device].bright_tbl); -#endif - - if (bitRead(Exs.power, device) && - Exs.dimm[device] != Exs.dimmer.channel[device].bright_tbl) { - ExsSetBri(device, Exs.dimm[device]); - } - - if (!Exs.dimm[device]) { - Exs.dimmer.channel[device].dimm = 0; - } else if (Exs.dimmer.channel[device].dimm != bitRead(Exs.power, device)) { - ExsSetPower(device, bitRead(Exs.power, device)); - } -} - -bool ExsSyncState() -{ -#ifdef EXS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Serial %p, Cmd %d"), ExsSerial, Exs.cmd_status); -#endif - - if (!ExsSerial || Exs.cmd_status != 0) - return false; - - ExsSyncState(0); - ExsSyncState(1); -} - -void ExsDebugState() -{ -#ifdef EXS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: MCU v%d.%d, c0: On:%d,Dim:%d,Tbl:%d(%d%%), c1: On:%d,Dim:%d,Tbl:%d(%d%%), ChLock: %d"), - Exs.dimmer.version_major, Exs.dimmer.version_minor, - Exs.dimmer.channel[0].on, Exs.dimmer.channel[0].dimm, - Exs.dimmer.channel[0].bright_tbl, - changeUIntScale(Exs.dimmer.channel[0].bright_tbl, 0, 255, 0, 100), - Exs.dimmer.channel[1].on, Exs.dimmer.channel[1].dimm, - Exs.dimmer.channel[1].bright_tbl, - changeUIntScale(Exs.dimmer.channel[1].bright_tbl, 0, 255, 0, 100), - Exs.dimmer.gate_lock); -#endif -} - -void ExsPacketProcess(void) -{ - uint8_t len = Exs.buffer[1]; - uint8_t cmd = Exs.buffer[2]; - - switch (cmd) - { - case EXS_GET_VALUES: -# 294 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" - if (len > 9) - { - Exs.dimmer.version_major = Exs.buffer[3]; - Exs.dimmer.version_minor = Exs.buffer[4]; - - - Exs.dimmer.channel[0].on = Exs.buffer[6]; - Exs.dimmer.channel[0].dimm = Exs.buffer[6]; - Exs.dimmer.channel[0].bright_tbl = Exs.buffer[7]; - - - Exs.dimmer.channel[1].on = Exs.buffer[9]; - Exs.dimmer.channel[1].dimm = Exs.buffer[9]; - Exs.dimmer.channel[1].bright_tbl = Exs.buffer[10]; - - Exs.dimmer.gate_lock = Exs.buffer[11]; - } - else -# 327 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" - { - Exs.dimmer.version_major = 1; - Exs.dimmer.version_minor = 0; - - - Exs.dimmer.channel[0].on = Exs.buffer[4] - 48; - Exs.dimmer.channel[0].dimm = Exs.buffer[4] - 48; - Exs.dimmer.channel[0].bright_tbl = Exs.buffer[5] - 48; - - - Exs.dimmer.channel[1].on = Exs.buffer[7] - 48; - Exs.dimmer.channel[1].dimm = Exs.buffer[7] - 48; - Exs.dimmer.channel[1].bright_tbl = Exs.buffer[8] - 48; - - Exs.dimmer.gate_lock = Exs.buffer[9] - 48; - } - - ExsDebugState(); - ExsSyncState(); - ExsDebugState(); - break; - default: - break; - } -} - - - -bool ExsModuleSelected(void) -{ - Settings.light_correction = 0; - Settings.flag.mqtt_serial = 0; - Settings.flag3.pwm_multi_channels = 1; - SetSeriallog(LOG_LEVEL_NONE); - - devices_present = +2; - light_type = LT_SERIAL2; - return true; -} - -bool ExsSetChannels(void) -{ -#ifdef EXS_DEBUG - snprintf_P(log_data, sizeof(log_data), PSTR("EXS: SetChannels: \"")); - for (int i = 0; i < XdrvMailbox.data_len; i++) - { - snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, ((uint8_t *)XdrvMailbox.data)[i]); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s\""), log_data); - AddLog(LOG_LEVEL_DEBUG_MORE); -#endif - - Exs.dimm[0] = ((uint8_t *)XdrvMailbox.data)[0]; - Exs.dimm[1] = ((uint8_t *)XdrvMailbox.data)[1]; - return ExsSyncState(); -} - -bool ExsSetPower(void) -{ - AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Set Power, Device %d, Power 0x%02x"), - active_device, XdrvMailbox.index); - - Exs.power = XdrvMailbox.index; - return ExsSyncState(); -} - -void EsxMcuStart(void) -{ - int retries = 3; - -#ifdef EXS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Request MCU configuration, PIN %d to Low"), pin[GPIO_EXS_ENABLE]); -#endif - - pinMode(pin[GPIO_EXS_ENABLE], OUTPUT); - digitalWrite(pin[GPIO_EXS_ENABLE], LOW); - - delay(1); - - while (ExsSerial->available()) - { - - ExsSerial->read(); - } -} - -void ExsInit(void) -{ -#ifdef EXS_DEBUG - AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Starting Tx %d Rx %d"), pin[GPIO_TXD], pin[GPIO_RXD]); -#endif - - Exs.buffer = (uint8_t *)malloc(EXS_BUFFER_SIZE); - if (Exs.buffer != nullptr) - { - ExsSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); - if (ExsSerial->begin(9600)) - { - if (ExsSerial->hardwareSerial()) - { - ClaimSerial(); - } - ExsSerial->flush(); - EsxMcuStart(); - ExsSendCmd(EXS_CH_LOCK, 0); - ExsSendCmd(EXS_GET_VALUES, 0); - } - } -} - -void ExsSerialInput(void) -{ - while (ExsSerial->available()) - { - yield(); - uint8_t serial_in_byte = ExsSerial->read(); - - AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Serial In Byte 0x%02x"), serial_in_byte); - - if (Exs.cmd_status == 0 && - serial_in_byte == 0x7B) - { - Exs.cmd_status = 1; - Exs.byte_counter = 0; - } - else if (Exs.byte_counter >= EXS_BUFFER_SIZE) - { - Exs.cmd_status = 0; - } - else if (Exs.cmd_status == 1) - { - Exs.buffer[Exs.byte_counter++] = serial_in_byte; - - if (Exs.byte_counter > 2 && Exs.byte_counter == Exs.buffer[1] + 2) - { - uint8_t crc = crc8(&Exs.buffer[2], Exs.buffer[1]); - - - Exs.cmd_status = 0; - -#ifdef EXS_DEBUG - snprintf_P(log_data, sizeof(log_data), PSTR("EXS: RX Packet: \"")); - for (uint32_t i = 0; i < Exs.byte_counter; i++) - { - snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, Exs.buffer[i]); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s\", CRC: 0x%02x"), log_data, crc); - AddLog(LOG_LEVEL_DEBUG_MORE); -#endif - - if (Exs.buffer[0] == crc) - { - ExsSerial->write(0xFF); - ExsPacketProcess(); - } - else - { - ExsSerial->write(0x00); - } - - } - } - } -} - - - - - -#ifdef EXS_MCU_CMNDS - -#define D_PRFX_EXS "Exs" -#define D_CMND_EXS_DIMM "Dimm" -#define D_CMND_EXS_DIMM_TBL "DimmTbl" -#define D_CMND_EXS_DIMM_VAL "DimmVal" -#define D_CMND_EXS_DIMMS "Dimms" -#define D_CMND_EXS_CH_LOCK "ChLock" -#define D_CMND_EXS_STATE "State" - -const char kExsCommands[] PROGMEM = D_PRFX_EXS "|" - D_CMND_EXS_DIMM "|" D_CMND_EXS_DIMM_TBL "|" D_CMND_EXS_DIMM_VAL "|" - D_CMND_EXS_DIMMS "|" D_CMND_EXS_CH_LOCK "|" - D_CMND_EXS_STATE; - -void (* const ExsCommand[])(void) PROGMEM = { - &CmndExsDimm, &CmndExsDimmTbl, &CmndExsDimmVal, - &CmndExsDimms, &CmndExsChLock, - &CmndExsState }; - -void CmndExsDimm(void) -{ - if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) && - (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1)) { - ExsSendCmd(EXS_DIMM_1_ON + 0x10 * (XdrvMailbox.index - 1) + - XdrvMailbox.payload ^ 1, 0); - } - CmndExsState(); -} - -void CmndExsDimmTbl(void) -{ - if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) && - (XdrvMailbox.payload > 0 || XdrvMailbox.payload <= 255)) { - ExsSendCmd(EXS_DIMM_1_TBL + 0x10 * (XdrvMailbox.index - 1), - XdrvMailbox.payload); - } - CmndExsState(); -} - -void CmndExsDimmVal(void) -{ - if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) && - (XdrvMailbox.payload > 0 || XdrvMailbox.payload <= 255)) { - ExsSendCmd(EXS_DIMM_1_VAL + 0x10 * (XdrvMailbox.index - 1), - XdrvMailbox.payload); - } - CmndExsState(); -} - -void CmndExsDimms(void) -{ - if (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1) { - ExsSendCmd(EXS_DIMMS_ON + XdrvMailbox.payload ^ 1, 0); - } - CmndExsState(); -} - -void CmndExsChLock(void) -{ - if (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1) { - ExsSendCmd(EXS_CH_LOCK, XdrvMailbox.payload); - } - CmndExsState(); -} - -void CmndExsState(void) -{ - ExsSendCmd(EXS_GET_VALUES, 0); - - - uint32_t snd_time = millis(); - while ((TimePassedSince(snd_time) < EXS_ACK_TIMEOUT) && - (!ExsSerial->available())) - ; - ExsSerialInput(); - - Response_P(PSTR("{\"" D_CMND_EXS_STATE "\":{")); - ResponseAppend_P(PSTR("\"McuVersion\":\"%d.%d\"," - "\"Channels\":["), - Exs.dimmer.version_major, Exs.dimmer.version_minor); - - for (uint32_t i = 0; i < 2; i++) { - if (i != 0) { - ResponseAppend_P(PSTR(",")); - } - ResponseAppend_P(PSTR("{\"On\":\"%d\"," - "\"BrightProz\":\"%d\"," - "\"BrightTab\":\"%d\"," - "\"Dimm\":\"%d\"}"), - Exs.dimmer.channel[i].on, - changeUIntScale(Exs.dimmer.channel[i].bright_tbl, 0, 255, 0, 100), - Exs.dimmer.channel[i].bright_tbl, - Exs.dimmer.channel[i].dimm); - } - ResponseAppend_P(PSTR("],")); - ResponseAppend_P(PSTR("\"GateLock\":\"%d\""), Exs.dimmer.gate_lock); - ResponseJsonEndEnd(); -} - -#endif - - - - - -bool Xdrv30(uint8_t function) -{ - bool result = false; - - if (EXS_DIMMER == my_module_type) - { - switch (function) - { - case FUNC_LOOP: - if (ExsSerial) - ExsSerialInput(); - break; - case FUNC_MODULE_INIT: - result = ExsModuleSelected(); - break; - case FUNC_INIT: - ExsInit(); - break; - case FUNC_SET_DEVICE_POWER: - result = ExsSetPower(); - break; - case FUNC_SET_CHANNELS: - result = ExsSetChannels(); - break; -#ifdef EXS_MCU_CMNDS - case FUNC_COMMAND: - result = DecodeCommand(kExsCommands, ExsCommand); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_31_tasmota_slave.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_31_tasmota_slave.ino" -#ifdef USE_TASMOTA_SLAVE - - - - -#define XDRV_31 31 - -#define CONST_STK_CRC_EOP 0x20 - -#define CMND_STK_GET_SYNC 0x30 -#define CMND_STK_SET_DEVICE 0x42 -#define CMND_STK_SET_DEVICE_EXT 0x45 -#define CMND_STK_ENTER_PROGMODE 0x50 -#define CMND_STK_LEAVE_PROGMODE 0x51 -#define CMND_STK_LOAD_ADDRESS 0x55 -#define CMND_STK_PROG_PAGE 0x64 - - - - - -#define CMND_START 0xFC -#define CMND_END 0xFD - -#define CMND_FEATURES 0x01 -#define CMND_JSON 0x02 -#define CMND_FUNC_EVERY_SECOND 0x03 -#define CMND_FUNC_EVERY_100_MSECOND 0x04 -#define CMND_SLAVE_SEND 0x05 -#define CMND_PUBLISH_TELE 0x06 -#define CMND_EXECUTE_CMND 0x07 - -#define PARAM_DATA_START 0xFE -#define PARAM_DATA_END 0xFF - -#include - - - - - -class SimpleHexParse { - public: - SimpleHexParse(void); - uint8_t parseLine(char *hexline); - uint8_t ptr_l = 0; - uint8_t ptr_h = 0; - bool PageIsReady = false; - bool firstrun = true; - bool EndOfFile = false; - uint8_t FlashPage[128]; - uint8_t FlashPageIdx = 0; - uint8_t layoverBuffer[16]; - uint8_t layoverIdx = 0; - uint8_t getByte(char *hexline, uint8_t idx); -}; - -SimpleHexParse::SimpleHexParse(void) -{ - -} - -uint8_t SimpleHexParse::parseLine(char *hexline) -{ - if (layoverIdx) { - memcpy(&FlashPage[0], &layoverBuffer[0], layoverIdx); - FlashPageIdx = layoverIdx; - layoverIdx = 0; - } - uint8_t len = getByte(hexline, 1); - uint8_t addr_h = getByte(hexline, 2); - uint8_t addr_l = getByte(hexline, 3); - uint8_t rectype = getByte(hexline, 4); - for (uint8_t idx = 0; idx < len; idx++) { - if (FlashPageIdx < 128) { - FlashPage[FlashPageIdx] = getByte(hexline, idx+5); - FlashPageIdx++; - } else { - layoverBuffer[layoverIdx] = getByte(hexline, idx+5); - layoverIdx++; - } - } - if (1 == rectype) { - EndOfFile = true; - while (FlashPageIdx < 128) { - FlashPage[FlashPageIdx] = 0xFF; - FlashPageIdx++; - } - } - if (FlashPageIdx == 128) { - if (firstrun) { - firstrun = false; - } else { - ptr_l += 0x40; - if (ptr_l == 0) { - ptr_l = 0; - ptr_h++; - } - } - firstrun = false; - PageIsReady = true; - } - return 0; -} - -uint8_t SimpleHexParse::getByte(char* hexline, uint8_t idx) -{ - char buff[3]; - buff[3] = '\0'; - memcpy(&buff, &hexline[(idx*2)-1], 2); - return strtol(buff, 0, 16); -} - - - - - -struct TSLAVE { - uint32_t spi_hex_size = 0; - uint32_t spi_sector_counter = 0; - uint8_t spi_sector_cursor = 0; - uint8_t inverted = LOW; - bool type = false; - bool flashing = false; - bool SerialEnabled = false; - uint8_t waitstate = 0; - bool unsupported = false; -} TSlave; - -typedef union { - uint32_t data; - struct { - uint32_t func_json_append : 1; - uint32_t func_every_second : 1; - uint32_t func_every_100_msecond : 1; - uint32_t func_slave_send : 1; - uint32_t spare4 : 1; - uint32_t spare5 : 1; - uint32_t spare6 : 1; - uint32_t spare7 : 1; - uint32_t spare8 : 1; - uint32_t spare9 : 1; - uint32_t spare10 : 1; - uint32_t spare11 : 1; - uint32_t spare12 : 1; - uint32_t spare13 : 1; - uint32_t spare14 : 1; - uint32_t spare15 : 1; - uint32_t spare16 : 1; - uint32_t spare17 : 1; - uint32_t spare18 : 1; - uint32_t spare19 : 1; - uint32_t spare20 : 1; - uint32_t spare21 : 1; - uint32_t spare22 : 1; - uint32_t spare23 : 1; - uint32_t spare24 : 1; - uint32_t spare25 : 1; - uint32_t spare26 : 1; - uint32_t spare27 : 1; - uint32_t spare28 : 1; - uint32_t spare29 : 1; - uint32_t spare30 : 1; - uint32_t spare31 : 1; - }; -} TSlaveFeatureCfg; - - - - - - -struct TSLAVE_FEATURES { - uint32_t features_version; - TSlaveFeatureCfg features; -} TSlaveSettings; - -struct TSLAVE_COMMAND { - uint8_t command; - uint8_t parameter; - uint8_t unused2; - uint8_t unused3; -} TSlaveCommand; - -TasmotaSerial *TasmotaSlave_Serial; - -uint32_t TasmotaSlave_FlashStart(void) -{ - return (ESP.getSketchSize() / SPI_FLASH_SEC_SIZE) + 2; -} - -uint8_t TasmotaSlave_UpdateInit(void) -{ - TSlave.spi_hex_size = 0; - TSlave.spi_sector_counter = TasmotaSlave_FlashStart(); - TSlave.spi_sector_cursor = 0; - return 0; -} - -void TasmotaSlave_Reset(void) -{ - if (TSlave.SerialEnabled) { - digitalWrite(pin[GPIO_TASMOTASLAVE_RST], !TSlave.inverted); - delay(1); - digitalWrite(pin[GPIO_TASMOTASLAVE_RST], TSlave.inverted); - delay(1); - digitalWrite(pin[GPIO_TASMOTASLAVE_RST], !TSlave.inverted); - delay(5); - } -} - -uint8_t TasmotaSlave_waitForSerialData(int dataCount, int timeout) -{ - int timer = 0; - while (timer < timeout) { - if (TasmotaSlave_Serial->available() >= dataCount) { - return 1; - } - delay(1); - timer++; - } - return 0; -} - -uint8_t TasmotaSlave_sendBytes(uint8_t* bytes, int count) -{ - TasmotaSlave_Serial->write(bytes, count); - TasmotaSlave_waitForSerialData(2, 250); - uint8_t sync = TasmotaSlave_Serial->read(); - uint8_t ok = TasmotaSlave_Serial->read(); - if ((sync == 0x14) && (ok == 0x10)) { - return 1; - } - return 0; -} - -uint8_t TasmotaSlave_execCmd(uint8_t cmd) -{ - uint8_t bytes[] = { cmd, CONST_STK_CRC_EOP }; - return TasmotaSlave_sendBytes(bytes, 2); -} - -uint8_t TasmotaSlave_execParam(uint8_t cmd, uint8_t* params, int count) -{ - uint8_t bytes[32]; - bytes[0] = cmd; - int i = 0; - while (i < count) { - bytes[i + 1] = params[i]; - i++; - } - bytes[i + 1] = CONST_STK_CRC_EOP; - return TasmotaSlave_sendBytes(bytes, i + 2); -} - -uint8_t TasmotaSlave_exitProgMode(void) -{ - return TasmotaSlave_execCmd(CMND_STK_LEAVE_PROGMODE); -} - -uint8_t TasmotaSlave_SetupFlash(void) -{ - uint8_t ProgParams[] = {0x86, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00}; - uint8_t ExtProgParams[] = {0x05, 0x04, 0xd7, 0xc2, 0x00}; - TasmotaSlave_Serial->begin(USE_TASMOTA_SLAVE_FLASH_SPEED); - if (TasmotaSlave_Serial->hardwareSerial()) { - ClaimSerial(); - } - - TasmotaSlave_Reset(); - - uint8_t timeout = 0; - uint8_t no_error = 0; - while (50 > timeout) { - if (TasmotaSlave_execCmd(CMND_STK_GET_SYNC)) { - timeout = 200; - no_error = 1; - } - timeout++; - delay(1); - } - if (no_error) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Found bootloader")); - } else { - no_error = 0; - AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Bootloader could not be found")); - } - if (no_error) { - if (TasmotaSlave_execParam(CMND_STK_SET_DEVICE, ProgParams, sizeof(ProgParams))) { - } else { - no_error = 0; - AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Could not configure device for programming (1)")); - } - } - if (no_error) { - if (TasmotaSlave_execParam(CMND_STK_SET_DEVICE_EXT, ExtProgParams, sizeof(ExtProgParams))) { - } else { - no_error = 0; - AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Could not configure device for programming (2)")); - } - } - if (no_error) { - if (TasmotaSlave_execCmd(CMND_STK_ENTER_PROGMODE)) { - } else { - no_error = 0; - AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Failed to put bootloader into programming mode")); - } - } - return no_error; -} - -uint8_t TasmotaSlave_loadAddress(uint8_t adrHi, uint8_t adrLo) -{ - uint8_t params[] = { adrLo, adrHi }; - return TasmotaSlave_execParam(CMND_STK_LOAD_ADDRESS, params, sizeof(params)); -} - -void TasmotaSlave_FlashPage(uint8_t addr_h, uint8_t addr_l, uint8_t* data) -{ - uint8_t Header[] = {CMND_STK_PROG_PAGE, 0x00, 0x80, 0x46}; - TasmotaSlave_loadAddress(addr_h, addr_l); - TasmotaSlave_Serial->write(Header, 4); - for (int i = 0; i < 128; i++) { - TasmotaSlave_Serial->write(data[i]); - } - TasmotaSlave_Serial->write(CONST_STK_CRC_EOP); - TasmotaSlave_waitForSerialData(2, 250); - TasmotaSlave_Serial->read(); - TasmotaSlave_Serial->read(); -} - -void TasmotaSlave_Flash(void) -{ - bool reading = true; - uint32_t read = 0; - uint32_t processed = 0; - char thishexline[50]; - uint8_t position = 0; - char* flash_buffer; - - SimpleHexParse hexParse = SimpleHexParse(); - - if (!TasmotaSlave_SetupFlash()) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Flashing aborted!")); - TSlave.flashing = false; - restart_flag = 2; - return; - } - - flash_buffer = new char[SPI_FLASH_SEC_SIZE]; - uint32_t flash_start = TasmotaSlave_FlashStart() * SPI_FLASH_SEC_SIZE; - while (reading) { - ESP.flashRead(flash_start + read, (uint32_t*)flash_buffer, SPI_FLASH_SEC_SIZE); - read = read + SPI_FLASH_SEC_SIZE; - if (read >= TSlave.spi_hex_size) { - reading = false; - } - for (uint32_t ca = 0; ca < SPI_FLASH_SEC_SIZE; ca++) { - processed++; - if ((processed <= TSlave.spi_hex_size) && (!hexParse.EndOfFile)) { - if (':' == flash_buffer[ca]) { - position = 0; - } - if (0x0D == flash_buffer[ca]) { - thishexline[position] = 0; - hexParse.parseLine(thishexline); - if (hexParse.PageIsReady) { - TasmotaSlave_FlashPage(hexParse.ptr_h, hexParse.ptr_l, hexParse.FlashPage); - hexParse.PageIsReady = false; - hexParse.FlashPageIdx = 0; - } - } else { - if (0x0A != flash_buffer[ca]) { - thishexline[position] = flash_buffer[ca]; - position++; - } - } - } - } - } - TasmotaSlave_exitProgMode(); - AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Flash done!")); - TSlave.flashing = false; - restart_flag = 2; -} - -void TasmotaSlave_SetFlagFlashing(bool value) -{ - TSlave.flashing = value; -} - -bool TasmotaSlave_GetFlagFlashing(void) -{ - return TSlave.flashing; -} - -void TasmotaSlave_WriteBuffer(uint8_t *buf, size_t size) -{ - if (0 == TSlave.spi_sector_cursor) { - ESP.flashEraseSector(TSlave.spi_sector_counter); - } - TSlave.spi_sector_cursor++; - ESP.flashWrite((TSlave.spi_sector_counter * SPI_FLASH_SEC_SIZE) + ((TSlave.spi_sector_cursor-1)*2048), (uint32_t*)buf, size); - TSlave.spi_hex_size = TSlave.spi_hex_size + size; - if (2 == TSlave.spi_sector_cursor) { - TSlave.spi_sector_cursor = 0; - TSlave.spi_sector_counter++; - } -} - -void TasmotaSlave_Init(void) -{ - if (TSlave.type) { - return; - } - if (10 > TSlave.waitstate) { - TSlave.waitstate++; - return; - } - if (!TSlave.SerialEnabled) { - if ((pin[GPIO_TASMOTASLAVE_RXD] < 99) && (pin[GPIO_TASMOTASLAVE_TXD] < 99) && - ((pin[GPIO_TASMOTASLAVE_RST] < 99) || (pin[GPIO_TASMOTASLAVE_RST_INV] < 99))) { - TasmotaSlave_Serial = new TasmotaSerial(pin[GPIO_TASMOTASLAVE_RXD], pin[GPIO_TASMOTASLAVE_TXD], 1, 0, 200); - if (TasmotaSlave_Serial->begin(USE_TASMOTA_SLAVE_SERIAL_SPEED)) { - if (TasmotaSlave_Serial->hardwareSerial()) { - ClaimSerial(); - } - TasmotaSlave_Serial->setTimeout(50); - if (pin[GPIO_TASMOTASLAVE_RST_INV] < 99) { - pin[GPIO_TASMOTASLAVE_RST] = pin[GPIO_TASMOTASLAVE_RST_INV]; - pin[GPIO_TASMOTASLAVE_RST_INV] = 99; - TSlave.inverted = HIGH; - } - pinMode(pin[GPIO_TASMOTASLAVE_RST], OUTPUT); - TSlave.SerialEnabled = true; - TasmotaSlave_Reset(); - AddLog_P2(LOG_LEVEL_INFO, PSTR("Tasmota Slave Enabled")); - } - } - } - if (TSlave.SerialEnabled) { - TasmotaSlave_sendCmnd(CMND_FEATURES, 0); - char buffer[32]; - TasmotaSlave_Serial->readBytesUntil(char(PARAM_DATA_START), buffer, sizeof(buffer)); - uint8_t len = TasmotaSlave_Serial->readBytesUntil(char(PARAM_DATA_END), buffer, sizeof(buffer)); - memcpy(&TSlaveSettings, &buffer, sizeof(TSlaveSettings)); - if (20191129 == TSlaveSettings.features_version) { - TSlave.type = true; - AddLog_P2(LOG_LEVEL_INFO, PSTR("Tasmota Slave Version %u"), TSlaveSettings.features_version); - } else { - if ((!TSlave.unsupported) && (TSlaveSettings.features_version > 0)) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("Tasmota Slave Version %u not supported!"), TSlaveSettings.features_version); - TSlave.unsupported = true; - } - } - } -} - -void TasmotaSlave_Show(void) -{ - if ((TSlave.type) && (TSlaveSettings.features.func_json_append)) { - char buffer[100]; - TasmotaSlave_sendCmnd(CMND_JSON, 0); - TasmotaSlave_Serial->readBytesUntil(char(PARAM_DATA_START), buffer, sizeof(buffer)-1); - uint8_t len = TasmotaSlave_Serial->readBytesUntil(char(PARAM_DATA_END), buffer, sizeof(buffer)-1); - buffer[len] = '\0'; - ResponseAppend_P(PSTR(",\"TasmotaSlave\":%s"), buffer); - } -} - -void TasmotaSlave_sendCmnd(uint8_t cmnd, uint8_t param) -{ - TSlaveCommand.command = cmnd; - TSlaveCommand.parameter = param; - char buffer[sizeof(TSlaveCommand)+2]; - buffer[0] = CMND_START; - memcpy(&buffer[1], &TSlaveCommand, sizeof(TSlaveCommand)); - buffer[sizeof(TSlaveCommand)+1] = CMND_END; - for (uint8_t ca = 0; ca < sizeof(buffer); ca++) { - TasmotaSlave_Serial->write(buffer[ca]); - } -} - -#define D_PRFX_SLAVE "Slave" -#define D_CMND_SLAVE_RESET "Reset" -#define D_CMND_SLAVE_SEND "Send" - -const char kTasmotaSlaveCommands[] PROGMEM = D_PRFX_SLAVE "|" - D_CMND_SLAVE_RESET "|" D_CMND_SLAVE_SEND; - -void (* const TasmotaSlaveCommand[])(void) PROGMEM = { - &CmndTasmotaSlaveReset, &CmndTasmotaSlaveSend }; - -void CmndTasmotaSlaveReset(void) -{ - TasmotaSlave_Reset(); - TSlave.type = false; - TSlave.waitstate = 7; - TSlave.unsupported = false; - ResponseCmndDone(); -} - -void CmndTasmotaSlaveSend(void) -{ - if (0 < XdrvMailbox.data_len) { - TasmotaSlave_sendCmnd(CMND_SLAVE_SEND, XdrvMailbox.data_len); - TasmotaSlave_Serial->write(char(PARAM_DATA_START)); - for (uint8_t idx = 0; idx < XdrvMailbox.data_len; idx++) { - TasmotaSlave_Serial->write(XdrvMailbox.data[idx]); - } - TasmotaSlave_Serial->write(char(PARAM_DATA_END)); - } - ResponseCmndDone(); -} - -void TasmotaSlave_ProcessIn(void) -{ - uint8_t cmnd = TasmotaSlave_Serial->read(); - switch (cmnd) { - case CMND_START: - TasmotaSlave_waitForSerialData(sizeof(TSlaveCommand),50); - uint8_t buffer[sizeof(TSlaveCommand)]; - for (uint8_t idx = 0; idx < sizeof(TSlaveCommand); idx++) { - buffer[idx] = TasmotaSlave_Serial->read(); - } - TasmotaSlave_Serial->read(); - memcpy(&TSlaveCommand, &buffer, sizeof(TSlaveCommand)); - char inbuf[TSlaveCommand.parameter+1]; - TasmotaSlave_waitForSerialData(TSlaveCommand.parameter, 50); - TasmotaSlave_Serial->read(); - for (uint8_t idx = 0; idx < TSlaveCommand.parameter; idx++) { - inbuf[idx] = TasmotaSlave_Serial->read(); - } - TasmotaSlave_Serial->read(); - inbuf[TSlaveCommand.parameter] = '\0'; - - if (CMND_PUBLISH_TELE == TSlaveCommand.command) { - Response_P(PSTR("{\"TasmotaSlave\":")); - ResponseAppend_P("%s", inbuf); - ResponseJsonEnd(); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); - XdrvRulesProcess(); - } - if (CMND_EXECUTE_CMND == TSlaveCommand.command) { - ExecuteCommand(inbuf, SRC_IGNORE); - } - break; - default: - break; - } -} - - - - - - -bool Xdrv31(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_100_MSECOND: - if (TSlave.type) { - if (TasmotaSlave_Serial->available()) { - TasmotaSlave_ProcessIn(); - } - if (TSlaveSettings.features.func_every_100_msecond) { - TasmotaSlave_sendCmnd(CMND_FUNC_EVERY_100_MSECOND, 0); - } - } - break; - case FUNC_EVERY_SECOND: - if ((TSlave.type) && (TSlaveSettings.features.func_every_second)) { - TasmotaSlave_sendCmnd(CMND_FUNC_EVERY_SECOND, 0); - } - TasmotaSlave_Init(); - break; - case FUNC_JSON_APPEND: - if ((TSlave.type) && (TSlaveSettings.features.func_json_append)) { - TasmotaSlave_Show(); - } - break; - case FUNC_COMMAND: - result = DecodeCommand(kTasmotaSlaveCommands, TasmotaSlaveCommand); - break; - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_32_hotplug.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_32_hotplug.ino" -#ifdef USE_HOTPLUG - - - - - - - -#define XDRV_32 32 - -const uint32_t HOTPLUG_MAX = 254; - -const char kHotPlugCommands[] PROGMEM = "|" - D_CMND_HOTPLUG; - -void (* const HotPlugCommand[])(void) PROGMEM = { - &CmndHotPlugTime }; - -struct { - - bool enabled = false; - uint8_t timeout = 0; -} Hotplug; - -void HotPlugInit(void) -{ - - if (Settings.hotplug_scan == 0xFF) { Settings.hotplug_scan = 0; } - if (Settings.hotplug_scan != 0) { - Hotplug.enabled = true; - Hotplug.timeout = 1; - } else - Hotplug.enabled = false; -} - -void HotPlugEverySecond(void) -{ - if (Hotplug.enabled) { - if (Hotplug.timeout == 0) { - XsnsCall(FUNC_HOTPLUG_SCAN); - Hotplug.timeout = Settings.hotplug_scan; - } - Hotplug.timeout--; - } -} - - - - - -void CmndHotPlugTime(void) -{ - if (XdrvMailbox.payload <= HOTPLUG_MAX) { - Settings.hotplug_scan = XdrvMailbox.payload; - HotPlugInit(); - } - ResponseCmndNumber(Settings.hotplug_scan); -} - - - - - -bool Xdrv32(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_SECOND: - HotPlugEverySecond(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kHotPlugCommands, HotPlugCommand); - break; - case FUNC_PRE_INIT: - HotPlugInit(); - break; - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_33_nrf24l01.ino" -# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_33_nrf24l01.ino" -#ifdef USE_SPI -#ifdef USE_NRF24 - - - - - - - -#define XDRV_33 33 - -#define MOSI 13 -#define MISO 12 -#define SCK 14 - -#include -#include - -const char NRF24type[] PROGMEM = "NRF24"; - -const char HTTP_NRF24[] PROGMEM = - "{s}%sL01%c: " "{m}started{e}"; - -struct { - uint8_t chipType = 0; -} NRF24; - - - -RF24 NRF24radio; - -bool NRF24initRadio() -{ - NRF24radio.begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_DC]); - NRF24radio.powerUp(); - - if(NRF24radio.isChipConnected()){ - DEBUG_DRIVER_LOG(PSTR("NRF24 chip connected")); - return true; - } - DEBUG_DRIVER_LOG(PSTR("NRF24 chip NOT !!!! connected")); - return false; -} - -bool NRF24Detect(void) -{ - if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_DC]<99)){ - SPI.pins(SCK,MOSI,MISO,-1); - if(NRF24initRadio()){ - NRF24.chipType = 32; - AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF24L01 initialized")); - if(NRF24radio.isPVariant()){ - NRF24.chipType = 43; - AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF24L01+ detected")); - } - return true; - } - } - return false; -} - - - - - -bool Xdrv33(uint8_t function) -{ - bool result = false; - - if (FUNC_INIT == function) { - result = NRF24Detect(); - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_34_wemos_motor_v1.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_34_wemos_motor_v1.ino" -#ifdef USE_I2C -#ifdef USE_WEMOS_MOTOR_V1 -# 45 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_34_wemos_motor_v1.ino" -#define XDRV_34 34 -#define XI2C_44 44 - -#ifndef WEMOS_MOTOR_V1_ADDR -#define WEMOS_MOTOR_V1_ADDR 0x30 -#endif -#ifndef WEMOS_MOTOR_V1_FREQ -#define WEMOS_MOTOR_V1_FREQ 1000 -#endif - -#define MOTOR_A 0 -#define MOTOR_B 1 - -#define SHORT_BRAKE 0 -#define CCW 1 -#define CW 2 -#define STOP 3 -#define STANDBY 4 - -struct WMOTORV1 { - bool detected = false; - uint8_t motor; -} WMotorV1; - -void WMotorV1Detect(void) -{ - if (I2cSetDevice(WEMOS_MOTOR_V1_ADDR)) { - WMotorV1.detected = true; - I2cSetActiveFound(WEMOS_MOTOR_V1_ADDR, "WEMOS_MOTOR_V1"); - WMotorV1Reset(); - } -} - -void WMotorV1Reset(void) -{ - - WMotorV1SetFrequency(WEMOS_MOTOR_V1_FREQ); -} - -void WMotorV1SetFrequency(uint32_t freq) -{ - Wire.beginTransmission(WEMOS_MOTOR_V1_ADDR); - Wire.write(((byte)(freq >> 16)) & (byte)0x0f); - Wire.write((byte)(freq >> 16)); - Wire.write((byte)(freq >> 8)); - Wire.write((byte)freq); - Wire.endTransmission(); - -} - -void WMotorV1SetMotor(uint8_t motor, uint8_t dir, float pwm_val) -{ - Wire.beginTransmission(WEMOS_MOTOR_V1_ADDR); - Wire.write(motor | (byte)0x10); - Wire.write(dir); - - uint16_t _pwm_val = uint16_t(pwm_val * 100); - if (_pwm_val > 10000) { - _pwm_val = 10000; - } - - Wire.write((byte)(_pwm_val >> 8)); - Wire.write((byte)_pwm_val); - Wire.endTransmission(); - -} - -bool WMotorV1Command(void) -{ - uint8_t args_count = 0; - - if (XdrvMailbox.data_len > 0) { - args_count = 1; - } else { - return false; - } - - for (uint32_t idx = 0; idx < XdrvMailbox.data_len; idx++) { - if (' ' == XdrvMailbox.data[idx]) { - XdrvMailbox.data[idx] = ','; - } - if (',' == XdrvMailbox.data[idx]) { - args_count++; - } - } - UpperCase(XdrvMailbox.data, XdrvMailbox.data); - - char *command = strtok(XdrvMailbox.data, ","); - - if (strcmp(command, "RESET") == 0) { - WMotorV1Reset(); - Response_P(PSTR("{\"WEMOS_MOTOR_V1\":{\"RESET\":\"OK\"}}")); - return true; - } - - if (strcmp(command, "SETMOTOR") == 0) { - if (args_count >= 3) { - - int motor = atoi(strtok(NULL, ",")); - int dir = atoi(strtok(NULL, ",")); - int duty = 100; - if (args_count == 4) { - duty = atoi(strtok(NULL, ",")); - } - - WMotorV1SetMotor(motor, dir, duty); - Response_P(PSTR("{\"WEMOS_MOTOR_V1\":{\"SETMOTOR\":\"OK\"}}")); - return true; - } - } - return false; -} - - - - - -bool Xdrv34(uint8_t function) -{ - if (!I2cEnabled(XI2C_44)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - WMotorV1Detect(); - } - else if (WMotorV1.detected) { - switch (function) { - case FUNC_COMMAND_DRIVER: - if (XI2C_44 == XdrvMailbox.index) { - result = WMotorV1Command(); - } - break; - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_35_pwm_dimmer.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_35_pwm_dimmer.ino" -#ifdef USE_PWM_DIMMER -# 33 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_35_pwm_dimmer.ino" -#define XDRV_35 35 - -const char kPWMDimmerCommands[] PROGMEM = "|" - D_CMND_BRI_PRESET; - -void (* const PWMDimmerCommand[])(void) PROGMEM = { - &CmndBriPreset }; - -#ifdef USE_PWM_DIMMER_REMOTE -struct remote_pwm_dimmer { - power_t power; - uint8_t bri_power_on; - uint8_t bri_preset_low; - uint8_t bri_preset_high; - uint8_t fixed_color_index; - uint8_t bri; - bool power_button_increases_bri; -}; -#endif - -uint32_t last_button_press_time; -uint32_t button_hold_time[3]; -uint8_t led_timeout_seconds = 0; -uint8_t restore_powered_off_led_counter = 0; -uint8_t power_button_index = 0; -uint8_t down_button_index = 1; -uint8_t buttons_pressed = 0; -uint8_t tap_count = 0; -bool down_button_tapped = false; -bool power_button_increases_bri = true; -bool invert_power_button_bri_direction = false; -bool restore_brightness_leds = false; -bool button_pressed[3] = { false, false, false }; -bool button_hold_sent[3]; -bool button_hold_processed[3]; -#ifdef USE_PWM_DIMMER_REMOTE -struct remote_pwm_dimmer * remote_pwm_dimmers; -struct remote_pwm_dimmer * active_remote_pwm_dimmer; -uint8_t remote_pwm_dimmer_count; -bool active_device_is_local; -#endif - -void PWMModulePreInit(void) -{ - Settings.seriallog_level = 0; - Settings.flag.mqtt_serial = 0; - Settings.ledstate = 0; - - - if (Settings.last_module != Settings.module) { - Settings.flag.pwm_control = true; - Settings.param[P_HOLD_TIME] = 5; - Settings.bri_power_on = Settings.bri_preset_low = Settings.bri_preset_high = 0; - Settings.last_module = Settings.module; - } - - - if (!Settings.bri_power_on) Settings.bri_power_on = 128; - if (!Settings.bri_preset_low) Settings.bri_preset_low = 10; - if (Settings.bri_preset_high < Settings.bri_preset_low) Settings.bri_preset_high = 255; - - PWMDimmerSetPoweredOffLed(); - - - if (!power && pin[GPIO_REL1] < 99) digitalWrite(pin[GPIO_REL1], bitRead(rel_inverted, 0) ? 1 : 0); - -#ifdef USE_PWM_DIMMER_REMOTE - - - if (Settings.flag4.remote_device_mode) { - Settings.flag4.device_groups_enabled = true; - - device_group_count = 0; - for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) { - if (pin[GPIO_KEY1 + button_index] < 99) device_group_count++; - } - - remote_pwm_dimmer_count = device_group_count - 1; - if (remote_pwm_dimmer_count) { - if ((remote_pwm_dimmers = (struct remote_pwm_dimmer *) calloc(remote_pwm_dimmer_count, sizeof(struct remote_pwm_dimmer))) == nullptr) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("PWMDimmer: error allocating PWM dimmer array")); - Settings.flag4.remote_device_mode = false; - } - else { - for (uint8_t i = 0; i < remote_pwm_dimmer_count; i++) { - active_remote_pwm_dimmer = &remote_pwm_dimmers[i]; - active_remote_pwm_dimmer->bri_power_on = 128; - active_remote_pwm_dimmer->bri_preset_low = 10; - active_remote_pwm_dimmer->bri_preset_high = 255; - } - } - } - } - active_device_is_local = true; -#endif -} - - -void PWMDimmerSetBrightnessLeds(int32_t operation) -{ - if (leds_present) { - uint32_t step = (!operation ? 256 / (leds_present + 1) : operation < 0 ? 256 : 0); - uint32_t current_bri = (Light.power ? light_state.getBri() : 0); - uint32_t level = step; - SetLedPowerIdx(0, current_bri >= level); - if (leds_present > 1) { - level += step; - SetLedPowerIdx(1, current_bri >= level); - if (leds_present > 2) { - level += step; - SetLedPowerIdx(2, current_bri >= level); - if (leds_present > 3) { - level += step; - SetLedPowerIdx(3, current_bri >= level); - } - } - } - - - if (!operation) led_timeout_seconds = (current_bri && Settings.flag4.led_timeout ? 5 : 0); - } -} - -void PWMDimmerSetPoweredOffLed(void) -{ - - if (pin[GPIO_LEDLNK] < 99) { - bool power_off_led_on = !power && Settings.flag4.powered_off_led; - if (ledlnk_inverted) power_off_led_on ^= 1; - digitalWrite(pin[GPIO_LEDLNK], power_off_led_on); - } -} - -void PWMDimmerSetPower(void) -{ - DigitalWrite(GPIO_REL1, bitRead(rel_inverted, 0) ? !power : power); - PWMDimmerSetBrightnessLeds(0); - PWMDimmerSetPoweredOffLed(); -} - -#ifdef USE_DEVICE_GROUPS -void PWMDimmerHandleDevGroupItem(void) -{ - uint32_t value = XdrvMailbox.payload; -#ifdef USE_PWM_DIMMER_REMOTE - uint8_t device_group_index = *(uint8_t *)XdrvMailbox.topic; - if (device_group_index > remote_pwm_dimmer_count) return; - bool device_is_local = device_groups[device_group_index].local; - struct remote_pwm_dimmer * remote_pwm_dimmer = &remote_pwm_dimmers[device_group_index]; -#else - if (*(uint8_t *)XdrvMailbox.topic) return; -#endif - - switch (XdrvMailbox.command_code) { -#ifdef USE_PWM_DIMMER_REMOTE - case DGR_ITEM_LIGHT_BRI: - if (!device_is_local) remote_pwm_dimmer->bri = value; - break; - case DGR_ITEM_POWER: - if (!device_is_local) { - remote_pwm_dimmer->power = value; - remote_pwm_dimmer->power_button_increases_bri = (remote_pwm_dimmer->bri < 128); - } - break; - case DGR_ITEM_LIGHT_FIXED_COLOR: - if (!device_is_local) remote_pwm_dimmer->fixed_color_index = value; - break; -#endif - case DGR_ITEM_BRI_POWER_ON: -#ifdef USE_PWM_DIMMER_REMOTE - if (!device_is_local) - remote_pwm_dimmer->bri_power_on = value; - else -#endif - Settings.bri_power_on = value; - break; - case DGR_ITEM_BRI_PRESET_LOW: -#ifdef USE_PWM_DIMMER_REMOTE - if (!device_is_local) - remote_pwm_dimmer->bri_preset_low = value; - else -#endif - Settings.bri_preset_low = value; - break; - case DGR_ITEM_BRI_PRESET_HIGH: -#ifdef USE_PWM_DIMMER_REMOTE - if (!device_is_local) - remote_pwm_dimmer->bri_preset_high = value; - else -#endif - Settings.bri_preset_high = value; - break; - case DGR_ITEM_STATUS: -#ifdef USE_PWM_DIMMER_REMOTE - if (device_is_local) -#endif - SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_BRI_POWER_ON, Settings.bri_power_on, - DGR_ITEM_BRI_PRESET_LOW, Settings.bri_preset_low, DGR_ITEM_BRI_PRESET_HIGH, Settings.bri_preset_high); - break; - } -} -#endif - -void PWMDimmerHandleButton(void) -{ -# 268 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_35_pwm_dimmer.ino" - if (XdrvMailbox.payload && !button_pressed[XdrvMailbox.index]) { - - - if (last_button_press_time && !buttons_pressed && millis() - last_button_press_time > 400) { - last_button_press_time = 0; - tap_count = 0; - } - return; - } - - bool state_updated = false; - int8_t bri_offset = 0; - uint8_t power_on_bri = 0; - uint8_t dgr_item = 0; - uint8_t dgr_value; - uint8_t dgr_more_to_come = false; - uint32_t button_index = XdrvMailbox.index; - uint32_t now = millis(); - - -#ifdef USE_PWM_DIMMER_REMOTE - bool power_is_on = (!active_device_is_local ? active_remote_pwm_dimmer->power : power); - bool is_power_button = (button_index == power_button_index); -#else - bool power_is_on = power; - bool is_power_button = !button_index; -#endif - bool is_down_button = (button_index == down_button_index); - - - if (!XdrvMailbox.payload) { - - - - if (!button_pressed[button_index]) { - last_button_press_time = now; - button_pressed[button_index] = true; - button_hold_time[button_index] = now + Settings.param[P_HOLD_TIME] * 100; - button_hold_sent[button_index] = false; - buttons_pressed++; - -#ifdef USE_PWM_DIMMER_REMOTE - - - if (buttons_pressed == 1 && Settings.flag4.remote_device_mode) { - power_button_index = button_index; - down_button_index = (button_index ? 0 : 1); - active_device_is_local = device_groups[power_button_index].local; - if (!active_device_is_local) active_remote_pwm_dimmer = &remote_pwm_dimmers[power_button_index - 1]; - } -#endif - - return; - } - - - if (button_hold_time[button_index] < now) { - - - - if (!button_pressed[power_button_index] && now - button_hold_time[button_index] > 10000) { - button_hold_time[button_index] = now + 90000; - char scmnd[20]; - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " 2")); - ExecuteCommand(scmnd, SRC_BUTTON); - return; - } - - - - if (!button_hold_sent[button_index]) { - button_hold_sent[button_index] = true; - button_hold_processed[button_index] = (!is_power_button && tap_count ? false : SendKey(KEY_BUTTON, button_index + 1, POWER_HOLD)); - } - if (!button_hold_processed[button_index]) { - - - if (is_power_button) { - - - if (buttons_pressed == 1) { - - - - - if (power_is_on) { -#ifdef USE_PWM_DIMMER_REMOTE - bri_offset = (!active_device_is_local ? (active_remote_pwm_dimmer->power_button_increases_bri ? 1 : -1) : (power_button_increases_bri ? 1 : -1)); -#else - bri_offset = (power_button_increases_bri ? 1 : -1); -#endif - invert_power_button_bri_direction = true; - } - - - - else { -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - power_on_bri = active_remote_pwm_dimmer->bri = active_remote_pwm_dimmer->bri_preset_low; - else -#endif - power_on_bri = Settings.bri_preset_low; - button_hold_time[button_index] = now + 500; - } - } - } - - - else { - - - - - if (power_is_on && !tap_count) { - bri_offset = (is_down_button ? -1 : 1); - } - - else { - uint8_t mqtt_trigger = 0; - - - - if (tap_count) { - - - if (down_button_tapped) { -#ifdef USE_DEVICE_GROUPS - uint8_t uint8_value; -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - uint8_value = active_remote_pwm_dimmer->fixed_color_index; - else -#endif - uint8_value = Light.fixed_color_index; - if (is_down_button) - uint8_value--; - else - uint8_value++; -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - active_remote_pwm_dimmer->fixed_color_index = uint8_value; - else -#endif - Light.fixed_color_index = uint8_value; - dgr_item = DGR_ITEM_LIGHT_FIXED_COLOR; - dgr_value = uint8_value; - dgr_more_to_come = true; -#endif - ; - } - - - else { - mqtt_trigger = (is_down_button ? 3 : 4); - } - } - - - else if (!power_is_on) { - mqtt_trigger = (is_down_button ? 1 : 2); - } - - - if (mqtt_trigger) { - char topic[TOPSZ]; - sprintf_P(mqtt_data, PSTR("Trigger%u"), mqtt_trigger); -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) { - snprintf_P(topic, sizeof(topic), PSTR("cmnd/%s/Event"), device_groups[power_button_index].group_name); - MqttPublish(topic); - } - else -#endif - MqttPublishPrefixTopic_P(CMND, PSTR("Event")); - } - - button_hold_time[button_index] = now + 500; - } - } - } - } - } - - - else { - bool button_was_held = button_hold_sent[button_index]; - - - - if (!(button_hold_sent[button_index] ? button_hold_processed[button_index] : SendKey(KEY_BUTTON, button_index + 1, POWER_TOGGLE))) { - - - if (is_power_button) { - - - if (button_was_held) { - - - - if (invert_power_button_bri_direction) { - invert_power_button_bri_direction = false; -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - active_remote_pwm_dimmer->power_button_increases_bri ^= 1; - else -#endif - power_button_increases_bri ^= 1; -#ifdef USE_PWM_DIMMER_REMOTE - dgr_item = 255; - state_updated = true; -#endif - } - - - else if (tap_count) { - - - - if (!button_was_held) { - -#ifdef USE_PWM_DIMMER_REMOTE - if (active_device_is_local) { -#endif - - - if (down_button_tapped) { - Settings.flag4.led_timeout ^= 1; - if (Light.power) PWMDimmerSetBrightnessLeds(Settings.flag4.led_timeout ? -1 : 0); - } - - - else { - Settings.flag4.powered_off_led ^= 1; - PWMDimmerSetPoweredOffLed(); - } -#ifdef USE_PWM_DIMMER_REMOTE - } -#endif - } - - - - else if (down_button_tapped) { - dgr_item = 255; - } - } - } - - - else { -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - power_on_bri = active_remote_pwm_dimmer->bri_power_on; - else -#endif - power_on_bri = Settings.bri_power_on; - } - } - - - else { - - if (restore_brightness_leds) { - restore_brightness_leds = false; - PWMDimmerSetBrightnessLeds(Settings.flag4.led_timeout ? -1 : 0); - } - - - - if (!button_was_held && button_pressed[power_button_index]) { - down_button_tapped = is_down_button; - tap_count++; - } - - - if (!tap_count) { - - - if (power_is_on) { - - - - if (button_hold_time[button_index] >= now) { - bri_offset = (is_down_button ? -10 : 10); - dgr_item = 255; - } - - - - - else if (!button_hold_processed[button_index]) { - dgr_item = 255; - state_updated = true; - } - } - - - - else { -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - power_on_bri = active_remote_pwm_dimmer->bri = (is_down_button ? active_remote_pwm_dimmer->bri_preset_low : active_remote_pwm_dimmer->bri_preset_high); - else -#endif - power_on_bri = (is_down_button ? Settings.bri_preset_low : Settings.bri_preset_high); - } - } - } - } - - - button_pressed[button_index] = false; - buttons_pressed--; - } - - - if (bri_offset) { - int32_t bri; -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - bri = active_remote_pwm_dimmer->bri; - else -#endif - bri = light_state.getBri(); - int32_t new_bri; - bri_offset *= (Settings.light_correction ? 4 : bri / 16 + 1); - new_bri = bri + bri_offset; - if (bri_offset > 0) { - if (new_bri > 255) new_bri = 255; - } - else { - if (new_bri < 1) new_bri = 1; - } - if (new_bri != bri) { -#ifdef USE_DEVICE_GROUPS - SendDeviceGroupMessage(power_button_index, (dgr_item ? DGR_MSGTYP_UPDATE : DGR_MSGTYP_UPDATE_MORE_TO_COME), DGR_ITEM_LIGHT_BRI, new_bri); -#endif -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - active_remote_pwm_dimmer->bri_power_on = active_remote_pwm_dimmer->bri = new_bri; - else { -#endif - skip_light_fade = true; - light_state.setBri(new_bri); - LightAnimate(); - skip_light_fade = false; - Settings.bri_power_on = new_bri; -#ifdef USE_PWM_DIMMER_REMOTE - } -#endif - } - else { - PWMDimmerSetBrightnessLeds(0); - } - } - - - else if (power_on_bri) { - power_t new_power; -#ifdef USE_DEVICE_GROUPS -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) { - active_remote_pwm_dimmer->power ^= 1; - new_power = active_remote_pwm_dimmer->power; - } - else { -#endif - new_power = power ^ 1; -#ifdef USE_PWM_DIMMER_REMOTE - } -#endif - if (new_power) - SendDeviceGroupMessage(power_button_index, DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_BRI, power_on_bri, DGR_ITEM_POWER, new_power); - else - SendDeviceGroupMessage(power_button_index, DGR_MSGTYP_UPDATE, DGR_ITEM_POWER, new_power); -#endif - -#ifdef USE_PWM_DIMMER_REMOTE - if (!active_device_is_local) - active_remote_pwm_dimmer->power_button_increases_bri = (power_on_bri < 128); - else { -#endif - light_state.setBri(power_on_bri); - ExecuteCommandPower(1, POWER_TOGGLE, SRC_RETRY); -#ifdef USE_PWM_DIMMER_REMOTE - } -#endif - } - - - - else if (dgr_item) { -#ifdef USE_DEVICE_GROUPS - if (dgr_item == 255) dgr_item = 0; - SendDeviceGroupMessage(power_button_index, (dgr_more_to_come ? DGR_MSGTYP_UPDATE_MORE_TO_COME : DGR_MSGTYP_UPDATE_DIRECT), dgr_item, dgr_value); -#endif -#ifdef USE_PWM_DIMMER_REMOTE - if (active_device_is_local) { -#endif - light_controller.saveSettings(); - if (state_updated && Settings.flag3.hass_tele_on_power) { - MqttPublishTeleState(); - } -#ifdef USE_PWM_DIMMER_REMOTE - } -#endif - } -} - - - - - -void CmndBriPreset(void) -{ - if (XdrvMailbox.data_len > 0) { - bool valid = true; - uint32_t value; - uint8_t parm[2]; - parm[0] = Settings.bri_preset_low; - parm[1] = Settings.bri_preset_high; - char * ptr = XdrvMailbox.data; - for (uint32_t i = 0; i < 2; i++) { - while (*ptr == ' ') ptr++; - if (*ptr == '+') { - if (parm[i] < 255) parm[i]++; - } - else if (*ptr == '-') { - if (parm[i] > 1) parm[i]--; - } - else { - value = strtoul(ptr, &ptr, 0); - if (value < 1 || value > 255) { - valid = false; - break; - } - parm[i] = value; - if (*ptr != ',') break; - } - ptr++; - } - if (valid && !*ptr) { - if (parm[0] < parm[1]) { - Settings.bri_preset_low = parm[0]; - Settings.bri_preset_high = parm[1]; - } else - { - Settings.bri_preset_low = parm[1]; - Settings.bri_preset_high = parm[0]; - } -#ifdef USE_DEVICE_GROUPS - SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_BRI_PRESET_LOW, Settings.bri_preset_low, DGR_ITEM_BRI_PRESET_HIGH, Settings.bri_preset_high); -#endif - } - } - Response_P(PSTR("{\"" D_CMND_BRI_PRESET "\":{\"Low\":%d,\"High\":%d}}"), Settings.bri_preset_low, Settings.bri_preset_high); -} - - - - - -bool Xdrv35(uint8_t function) -{ - bool result = false; - - if (PWM_DIMMER != my_module_type) return result; - - switch (function) { - case FUNC_EVERY_SECOND: - - if (led_timeout_seconds && !led_timeout_seconds--) { - PWMDimmerSetBrightnessLeds(-1); - } - - - - - - if (global_state.data) - restore_powered_off_led_counter = 5; - else if (restore_powered_off_led_counter) { - PWMDimmerSetPoweredOffLed(); - restore_powered_off_led_counter--; - } - break; - - case FUNC_BUTTON_PRESSED: - PWMDimmerHandleButton(); - result = true; - break; - -#ifdef USE_DEVICE_GROUPS - case FUNC_DEVICE_GROUP_ITEM: - PWMDimmerHandleDevGroupItem(); - break; -#endif - - case FUNC_COMMAND: - result = DecodeCommand(kPWMDimmerCommands, PWMDimmerCommand); - break; - - case FUNC_SET_DEVICE_POWER: - - - if (XdrvMailbox.index) { - PWMDimmerSetPower(); - - - power_button_increases_bri = (light_state.getBri() < 128); - } - - - - else - result = true; - break; - - case FUNC_PRE_INIT: - PWMModulePreInit(); - break; - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_36_keeloq.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_36_keeloq.ino" -#ifdef USE_KEELOQ -# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_36_keeloq.ino" -#define XDRV_36 36 - -#include "cc1101.h" -#include - -#define SYNC_WORD 199 - -#define Lowpulse 400 -#define Highpulse 800 - -const char kJaroliftCommands[] PROGMEM = "Keeloq|" - "SendRaw|SendButton|Set"; - -void (* const jaroliftCommand[])(void) PROGMEM = { - &CmndSendRaw, &CmdSendButton, &CmdSet}; - -CC1101 cc1101; - -struct JAROLIFT_DEVICE { - int device_key_msb = 0x0; - int device_key_lsb = 0x0; - uint64_t button = 0x0; - int disc = 0x0100; - uint32_t enc = 0x0; - uint64_t pack = 0; - int count = 0; - uint32_t serial = 0x0; - uint8_t port_tx; - uint8_t port_rx; -} jaroliftDevice; - -void CmdSet(void) -{ - if (XdrvMailbox.data_len > 0) { - if (XdrvMailbox.payload > 0) { - char *p; - uint32_t i = 0; - uint32_t param[4] = { 0 }; - for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 4; str = strtok_r(nullptr, ", ", &p)) { - param[i] = strtoul(str, nullptr, 0); - i++; - } - for (uint32_t i = 0; i < 3; i++) { - if (param[i] < 1) { param[i] = 1; } - } - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("params: %08x %08x %08x %08x"), param[0], param[1], param[2], param[3]); - Settings.keeloq_master_msb = param[0]; - Settings.keeloq_master_lsb = param[1]; - Settings.keeloq_serial = param[2]; - Settings.keeloq_count = param[3]; - - jaroliftDevice.serial = param[2]; - jaroliftDevice.count = param[3]; - - GenerateDeviceCryptKey(); - ResponseCmndDone(); - } else { - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("no payload")); - } - } else { - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("no param")); - } -} - -void GenerateDeviceCryptKey() -{ - Keeloq k(Settings.keeloq_master_msb, Settings.keeloq_master_lsb); - jaroliftDevice.device_key_msb = k.decrypt(jaroliftDevice.serial | 0x60000000L); - jaroliftDevice.device_key_lsb = k.decrypt(jaroliftDevice.serial | 0x20000000L); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("generated device keys: %08x %08x"), jaroliftDevice.device_key_msb, jaroliftDevice.device_key_lsb); -} - -void CmdSendButton(void) -{ - noInterrupts(); - entertx(); - - if (XdrvMailbox.data_len > 0) - { - if (XdrvMailbox.payload > 0) - { - jaroliftDevice.button = strtoul(XdrvMailbox.data, nullptr, 0); - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("msb: %08x"), jaroliftDevice.device_key_msb); - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("lsb: %08x"), jaroliftDevice.device_key_lsb); - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("serial: %08x"), jaroliftDevice.serial); - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("disc: %08x"), jaroliftDevice.disc); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("KLQ: count: %08x"), jaroliftDevice.count); - - CreateKeeloqPacket(); - jaroliftDevice.count++; - Settings.keeloq_count = jaroliftDevice.count; - - for(int repeat = 0; repeat <= 1; repeat++) - { - uint64_t bitsToSend = jaroliftDevice.pack; - digitalWrite(jaroliftDevice.port_tx, LOW); - delayMicroseconds(1150); - SendSyncPreamble(13); - delayMicroseconds(3500); - for(int i=72; i>0; i--) - { - SendBit(bitsToSend & 0x0000000000000001); - bitsToSend >>= 1; - } - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("finished sending bits at %d"), micros()); - - delay(16); - } - } - } - - interrupts(); - enterrx(); - - ResponseCmndDone(); -} - -void SendBit(byte bitToSend) -{ - if (bitToSend==1) - { - digitalWrite(jaroliftDevice.port_tx, LOW); - delayMicroseconds(Lowpulse); - digitalWrite(jaroliftDevice.port_tx, HIGH); - delayMicroseconds(Highpulse); - } - else - { - digitalWrite(jaroliftDevice.port_tx, LOW); - delayMicroseconds(Highpulse); - digitalWrite(jaroliftDevice.port_tx, HIGH); - delayMicroseconds(Lowpulse); - } -} - -void CmndSendRaw(void) -{ - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("cmd send called at %d"), micros()); - noInterrupts(); - entertx(); - for(int repeat = 0; repeat <= 1; repeat++) - { - if (XdrvMailbox.data_len > 0) - { - digitalWrite(jaroliftDevice.port_tx, LOW); - delayMicroseconds(1150); - SendSyncPreamble(13); - delayMicroseconds(3500); - - for(int i=XdrvMailbox.data_len-1; i>=0; i--) - { - SendBit(XdrvMailbox.data[i] == '1'); - } - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("finished sending bits at %d"), micros()); - - delay(16); - } - interrupts(); - } - enterrx(); - ResponseCmndDone(); -} - -void enterrx() { - unsigned char marcState = 0; - cc1101.setRxState(); - delay(2); - unsigned long rx_time = micros(); - while (((marcState = cc1101.readStatusReg(CC1101_MARCSTATE)) & 0x1F) != 0x0D ) - { - if (micros() - rx_time > 50000) break; - } -} - -void entertx() { - unsigned char marcState = 0; - cc1101.setTxState(); - delay(2); - unsigned long rx_time = micros(); - while (((marcState = cc1101.readStatusReg(CC1101_MARCSTATE)) & 0x1F) != 0x13 && 0x14 && 0x15) - { - if (micros() - rx_time > 50000) break; - } -} - -void SendSyncPreamble(int l) -{ - for (int i = 0; i < l; ++i) - { - digitalWrite(jaroliftDevice.port_tx, LOW); - delayMicroseconds(400); - digitalWrite(jaroliftDevice.port_tx, HIGH); - delayMicroseconds(380); - } -} - -void CreateKeeloqPacket() -{ - Keeloq k(jaroliftDevice.device_key_msb, jaroliftDevice.device_key_lsb); - unsigned int result = (jaroliftDevice.disc << 16) | jaroliftDevice.count; - jaroliftDevice.pack = (uint64_t)0; - jaroliftDevice.pack |= jaroliftDevice.serial & 0xfffffffL; - jaroliftDevice.pack |= (jaroliftDevice.button & 0xfL) << 28; - jaroliftDevice.pack <<= 32; - - jaroliftDevice.enc = k.encrypt(result); - jaroliftDevice.pack |= jaroliftDevice.enc; - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("pack high: %08x"), jaroliftDevice.pack>>32); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("pack low: %08x"), jaroliftDevice.pack); -} - -void KeeloqInit() -{ - jaroliftDevice.port_tx = pin[GPIO_CC1101_GDO2]; - jaroliftDevice.port_rx = pin[GPIO_CC1101_GDO0]; - - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("cc1101.init()")); - delay(100); - cc1101.init(); - AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("CC1101 done.")); - cc1101.setSyncWord(SYNC_WORD, false); - cc1101.setCarrierFreq(CFREQ_433); - cc1101.disableAddressCheck(); - - pinMode(jaroliftDevice.port_tx, OUTPUT); - pinMode(jaroliftDevice.port_rx, INPUT_PULLUP); - - jaroliftDevice.serial = Settings.keeloq_serial; - jaroliftDevice.count = Settings.keeloq_count; - GenerateDeviceCryptKey(); -} - - - - -bool Xdrv36(uint8_t function) -{ - if ((99 == pin[GPIO_CC1101_GDO0]) || (99 == pin[GPIO_CC1101_GDO2])) { return false; } - - bool result = false; - - switch (function) { - case FUNC_COMMAND: - AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("calling command")); - result = DecodeCommand(kJaroliftCommands, jaroliftCommand); - break; - case FUNC_INIT: - KeeloqInit(); - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("init done.")); - break; - } - - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_37_sonoff_d1.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_37_sonoff_d1.ino" -#ifdef USE_SONOFF_D1 -# 34 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_37_sonoff_d1.ino" -#define XDRV_37 37 - -struct SONOFFD1 { - uint8_t receive_len = 0; - uint8_t power = 255; - uint8_t dimmer = 255; -} SnfD1; - - - -void SonoffD1Received(void) -{ - if (serial_in_byte_counter < 8) { return; } - - uint8_t action = serial_in_buffer[6] & 1; - if (action != SnfD1.power) { - SnfD1.power = action; - - - - ExecuteCommandPower(1, action, SRC_SWITCH); - } - - uint8_t dimmer = serial_in_buffer[7]; - if (dimmer != SnfD1.dimmer) { - SnfD1.dimmer = dimmer; - - - - char scmnd[20]; - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), SnfD1.dimmer); - ExecuteCommand(scmnd, SRC_SWITCH); - } -# 78 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_37_sonoff_d1.ino" -} - -bool SonoffD1SerialInput(void) -{ - if (0xAA == serial_in_byte) { - serial_in_byte_counter = 0; - SnfD1.receive_len = 7; - } - if (SnfD1.receive_len) { - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; - if (6 == serial_in_byte_counter) { - SnfD1.receive_len += serial_in_byte; - } - if (serial_in_byte_counter == SnfD1.receive_len) { -# 101 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_37_sonoff_d1.ino" - AddLogSerial(LOG_LEVEL_DEBUG); - uint8_t crc = 0; - for (uint32_t i = 2; i < SnfD1.receive_len -1; i++) { - crc += serial_in_buffer[i]; - } - if (crc == serial_in_buffer[SnfD1.receive_len -1]) { - SonoffD1Received(); - SnfD1.receive_len = 0; - return true; - } - } - serial_in_byte = 0; - } - return false; -} - - - -void SonoffD1Send() -{ - - uint8_t buffer[17] = { 0xAA,0x55,0x01,0x04,0x00,0x0A,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00 }; - - buffer[6] = SnfD1.power; - buffer[7] = SnfD1.dimmer; - - for (uint32_t i = 0; i < sizeof(buffer); i++) { - if ((i > 1) && (i < sizeof(buffer) -1)) { buffer[16] += buffer[i]; } - Serial.write(buffer[i]); - } -} - -bool SonoffD1SendPower(void) -{ - uint8_t action = XdrvMailbox.index &1; - if (action != SnfD1.power) { - SnfD1.power = action; - - - - SonoffD1Send(); - } - return true; -} - -bool SonoffD1SendDimmer(void) -{ - uint8_t dimmer = LightGetDimmer(1); - dimmer = (dimmer < Settings.dimmer_hw_min) ? Settings.dimmer_hw_min : dimmer; - dimmer = (dimmer > Settings.dimmer_hw_max) ? Settings.dimmer_hw_max : dimmer; - if (dimmer != SnfD1.dimmer) { - SnfD1.dimmer = dimmer; - - - - SonoffD1Send(); - } - return true; -} - -bool SonoffD1ModuleSelected(void) -{ - SetSerial(9600, TS_SERIAL_8N1); - - devices_present++; - light_type = LT_SERIAL1; - - return true; -} - - - - - -bool Xdrv37(uint8_t function) -{ - bool result = false; - - if (SONOFF_D1 == my_module_type) { - switch (function) { - case FUNC_SERIAL: - result = SonoffD1SerialInput(); - break; - case FUNC_SET_DEVICE_POWER: - result = SonoffD1SendPower(); - break; - case FUNC_SET_CHANNELS: - result = SonoffD1SendDimmer(); - break; - case FUNC_MODULE_INIT: - result = SonoffD1ModuleSelected(); - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_38_ping.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_38_ping.ino" -#ifdef USE_PING - -#define XDRV_38 38 - -#include "lwip/icmp.h" -#include "lwip/inet_chksum.h" -#include "lwip/raw.h" -#include "lwip/timeouts.h" - -const char kPingCommands[] PROGMEM = "|" - D_CMND_PING - ; - -void (* const PingCommand[])(void) PROGMEM = { - &CmndPing, - }; - -extern "C" { - - extern uint32 system_relative_time(uint32 time); - extern void ets_bzero(void *s, size_t n); - - const uint16_t Ping_ID = 0xAFAF; - const size_t Ping_data_size = 32; - const uint32_t Ping_timeout_ms = 1000; - const uint32_t Ping_coarse = 1000; - - typedef struct Ping_t { - uint32 ip; - Ping_t *next; - uint16_t seq_num; - uint16_t seqno; - uint8_t success_count; - uint8_t timeout_count; - uint8_t to_send_count; - uint32_t ping_time_sent; - uint32_t min_time; - uint32_t max_time; - uint32_t sum_time; - bool done; - bool fast; - } Ping_t; - - - Ping_t *ping_head = nullptr; - struct raw_pcb *t_ping_pcb = nullptr; - - - - - - - - Ping_t ICACHE_FLASH_ATTR * t_ping_find(uint32_t ip) { - Ping_t *ping = ping_head; - while (ping != nullptr) { - if (ping->ip == ip) { - return ping; - } - ping = ping->next; - } - return nullptr; - } -# 91 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_38_ping.ino" - void ICACHE_FLASH_ATTR t_ping_timeout(void* arg) { - Ping_t *ping = (Ping_t*) arg; - ping->timeout_count++; - } - - - - - - - void ICACHE_FLASH_ATTR t_ping_prepare_echo(struct icmp_echo_hdr *iecho, uint16_t len, Ping_t *ping) { - size_t data_len = len - sizeof(struct icmp_echo_hdr); - - ICMPH_TYPE_SET(iecho, ICMP_ECHO); - ICMPH_CODE_SET(iecho, 0); - iecho->chksum = 0; - iecho->id = Ping_ID; - ping->seq_num++; - if (ping->seq_num == 0x7fff) { ping->seq_num = 0; } - - iecho->seqno = htons(ping->seq_num); - - - for (uint32_t i = 0; i < data_len; i++) { - ((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i; - } - - iecho->chksum = inet_chksum(iecho, len); - } - - - - void ICACHE_FLASH_ATTR t_ping_send(struct raw_pcb *raw, Ping_t *ping) { - struct pbuf *p; - uint16_t ping_size = sizeof(struct icmp_echo_hdr) + Ping_data_size; - - ping->ping_time_sent = system_get_time(); - p = pbuf_alloc(PBUF_IP, ping_size, PBUF_RAM); - if (!p) { return; } - if ((p->len == p->tot_len) && (p->next == nullptr)) { - ip_addr_t ping_target; - struct icmp_echo_hdr *iecho; - - ping_target.addr = ping->ip; - iecho = (struct icmp_echo_hdr *) p->payload; - - t_ping_prepare_echo(iecho, ping_size, ping); - raw_sendto(raw, p, &ping_target); - } - pbuf_free(p); - } - - - - - - static void ICACHE_FLASH_ATTR t_ping_coarse_tmr(void *arg) { - Ping_t *ping = (Ping_t*) arg; - if (ping->to_send_count > 0) { - ping->to_send_count--; - - t_ping_send(t_ping_pcb, ping); - - sys_timeout(Ping_timeout_ms, t_ping_timeout, ping); - sys_timeout(Ping_coarse, t_ping_coarse_tmr, ping); - } else { - sys_untimeout(t_ping_coarse_tmr, ping); - ping->done = true; - } - } - - - - - - - - static uint8_t ICACHE_FLASH_ATTR t_ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *addr) { - Ping_t *ping = t_ping_find(addr->addr); - - if (nullptr == ping) { - return 0; - } - - if (pbuf_header( p, -PBUF_IP_HLEN)==0) { - struct icmp_echo_hdr *iecho; - iecho = (struct icmp_echo_hdr *)p->payload; - - if ((iecho->id == Ping_ID) && (iecho->seqno == htons(ping->seq_num)) && iecho->type == ICMP_ER) { - - if (iecho->seqno != ping->seqno){ - - sys_untimeout(t_ping_timeout, ping); - uint32_t delay = system_relative_time(ping->ping_time_sent); - delay /= 1000; - - ping->sum_time += delay; - if (delay < ping->min_time) { ping->min_time = delay; } - if (delay > ping->max_time) { ping->max_time = delay; } - - ping->success_count++; - ping->seqno = iecho->seqno; - if (ping->fast) { - sys_untimeout(t_ping_coarse_tmr, ping); - ping->done = true; - ping->to_send_count = 0; - } - } - - pbuf_free(p); - return 1; - } - } - - return 0; - } - - - - - - void t_ping_register_pcb(void) { - if (nullptr == t_ping_pcb) { - t_ping_pcb = raw_new(IP_PROTO_ICMP); - - raw_recv(t_ping_pcb, t_ping_recv, nullptr); - raw_bind(t_ping_pcb, IP_ADDR_ANY); - } - } - - - void t_ping_deregister_pcb(void) { - if (nullptr == ping_head) { - raw_remove(t_ping_pcb); - t_ping_pcb = nullptr; - } - } - - - - - bool t_ping_start(uint32_t ip, uint32_t count) { - - if (t_ping_find(ip)) { - return false; - } - - Ping_t *ping = new Ping_t(); - if (0 == count) { - count = 4; - ping->fast = true; - } - ping->min_time = UINT32_MAX; - ping->ip = ip; - ping->to_send_count = count - 1; - - - ping->next = ping_head; - ping_head = ping; - - t_ping_register_pcb(); - t_ping_send(t_ping_pcb, ping); - - - sys_timeout(Ping_timeout_ms, t_ping_timeout, ping); - sys_timeout(Ping_coarse, t_ping_coarse_tmr, ping); - } - -} - - -void PingResponsePoll(void) { - Ping_t *ping = ping_head; - Ping_t **prev_link = &ping_head; - - while (ping != nullptr) { - if (ping->done) { - uint32_t success = ping->success_count; - uint32_t ip = ping->ip; - - Response_P(PSTR("{\"" D_JSON_PING "\":{\"%d.%d.%d.%d\":{" - "\"Reachable\":%s" - ",\"Success\":%d" - ",\"Timeout\":%d" - ",\"MinTime\":%d" - ",\"MaxTime\":%d" - ",\"AvgTime\":%d" - "}}}"), - ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, ip >> 24, - success ? "true" : "false", - success, ping->timeout_count, - success ? ping->min_time : 0, ping->max_time, - success ? ping->sum_time / success : 0 - ); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_PING)); - XdrvRulesProcess(); - - - *prev_link = ping->next; - - Ping_t *ping_to_delete = ping; - ping = ping->next; - delete ping_to_delete; - } else { - prev_link = &ping->next; - ping = ping->next; - } - } -} - - - - - -void CmndPing(void) { - uint32_t count = XdrvMailbox.index; - IPAddress ip; - - RemoveSpace(XdrvMailbox.data); - if (count > 10) { count = 8; } - - if (WiFi.hostByName(XdrvMailbox.data, ip)) { - bool ok = t_ping_start(ip, count); - if (ok) { - ResponseCmndDone(); - } else { - ResponseCmndChar_P(PSTR("Ping already ongoing for this IP")); - } - } else { - ResponseCmndChar_P(PSTR("Unable to resolve IP address")); - } -} - - - - - - -bool Xdrv38(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_250_MSECOND: - PingResponsePoll(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kPingCommands, PingCommand); - break; - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_39_thermostat.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_39_thermostat.ino" -#ifdef USE_THERMOSTAT - -#define XDRV_39 39 - - -#define DEBUG_THERMOSTAT - -#ifdef DEBUG_THERMOSTAT -#define DOMOTICZ_IDX1 791 -#define DOMOTICZ_IDX2 792 -#define DOMOTICZ_IDX3 793 -#endif - - -#define D_CMND_THERMOSTATMODESET "ThermostatModeSet" -#define D_CMND_TEMPFROSTPROTECTSET "TempFrostProtectSet" -#define D_CMND_CONTROLLERMODESET "ControllerModeSet" -#define D_CMND_INPUTSWITCHSET "InputSwitchSet" -#define D_CMND_OUTPUTRELAYSET "OutputRelaySet" -#define D_CMND_TIMEALLOWRAMPUPSET "TimeAllowRampupSet" -#define D_CMND_TEMPMEASUREDSET "TempMeasuredSet" -#define D_CMND_TEMPTARGETSET "TempTargetSet" -#define D_CMND_TEMPTARGETREAD "TempTargetRead" -#define D_CMND_TEMPMEASUREDREAD "TempMeasuredRead" -#define D_CMND_TEMPMEASUREDGRDREAD "TempMeasuredGrdRead" -#define D_CMND_TEMPSENSNUMBERSET "TempSensNumberSet" -#define D_CMND_STATEEMERGENCYSET "StateEmergencySet" -#define D_CMND_POWERMAXSET "PowerMaxSet" -#define D_CMND_TIMEMANUALTOAUTOSET "TimeManualToAutoSet" -#define D_CMND_TIMEONLIMITSET "TimeOnLimitSet" -#define D_CMND_PROPBANDSET "PropBandSet" -#define D_CMND_TIMERESETSET "TimeResetSet" -#define D_CMND_TIMEPICYCLESET "TimePiCycleSet" -#define D_CMND_TEMPANTIWINDUPRESETSET "TempAntiWindupResetSet" -#define D_CMND_TEMPHYSTSET "TempHystSet" -#define D_CMND_TIMEMAXACTIONSET "TimeMaxActionSet" -#define D_CMND_TIMEMINACTIONSET "TimeMinActionSet" -#define D_CMND_TIMEMINTURNOFFACTIONSET "TimeMinTurnoffActionSet" -#define D_CMND_TEMPRUPDELTINSET "TempRupDeltInSet" -#define D_CMND_TEMPRUPDELTOUTSET "TempRupDeltOutSet" -#define D_CMND_TIMERAMPUPMAXSET "TimeRampupMaxSet" -#define D_CMND_TIMERAMPUPCYCLESET "TimeRampupCycleSet" -#define D_CMND_TEMPRAMPUPPIACCERRSET "TempRampupPiAccErrSet" -#define D_CMND_TIMEPIPROPORTREAD "TimePiProportRead" -#define D_CMND_TIMEPIINTEGRREAD "TimePiIntegrRead" -#define D_CMND_TIMESENSLOSTSET "TimeSensLostSet" - -enum ThermostatModes { THERMOSTAT_OFF, THERMOSTAT_AUTOMATIC_OP, THERMOSTAT_MANUAL_OP, THERMOSTAT_MODES_MAX }; -enum ControllerModes { CTR_HYBRID, CTR_PI, CTR_RAMP_UP, CTR_MODES_MAX }; -enum ControllerHybridPhases { CTR_HYBRID_RAMP_UP, CTR_HYBRID_PI }; -enum InterfaceStates { IFACE_OFF, IFACE_ON }; -enum CtrCycleStates { CYCLE_OFF, CYCLE_ON }; -enum EmergencyStates { EMERGENCY_OFF, EMERGENCY_ON }; -enum ThermostatSupportedInputSwitches { - THERMOSTAT_INPUT_NONE, - THERMOSTAT_INPUT_SWT1 = 1, - THERMOSTAT_INPUT_SWT2, - THERMOSTAT_INPUT_SWT3, - THERMOSTAT_INPUT_SWT4 -}; -enum ThermostatSupportedOutputRelays { - THERMOSTAT_OUTPUT_NONE, - THERMOSTAT_OUTPUT_REL1 = 1, - THERMOSTAT_OUTPUT_REL2, - THERMOSTAT_OUTPUT_REL3, - THERMOSTAT_OUTPUT_REL4, - THERMOSTAT_OUTPUT_REL5, - THERMOSTAT_OUTPUT_REL6, - THERMOSTAT_OUTPUT_REL7, - THERMOSTAT_OUTPUT_REL8 -}; - -typedef union { - uint16_t data; - struct { - uint16_t thermostat_mode : 2; - uint16_t controller_mode : 2; - uint16_t sensor_alive : 1; - uint16_t command_output : 1; - uint16_t phase_hybrid_ctr : 1; - uint16_t status_output : 1; - uint16_t status_cycle_active : 1; - uint16_t state_emergency : 1; - uint16_t counter_seconds : 6; - }; -} ThermostatBitfield; - -#ifdef DEBUG_THERMOSTAT -const char DOMOTICZ_MES[] PROGMEM = "{\"idx\":%d,\"nvalue\":%d,\"svalue\":\"%s\"}"; -#endif - -const char kThermostatCommands[] PROGMEM = "|" D_CMND_THERMOSTATMODESET "|" D_CMND_TEMPFROSTPROTECTSET "|" - D_CMND_CONTROLLERMODESET "|" D_CMND_INPUTSWITCHSET "|" D_CMND_OUTPUTRELAYSET "|" D_CMND_TIMEALLOWRAMPUPSET "|" - D_CMND_TEMPMEASUREDSET "|" D_CMND_TEMPTARGETSET "|" D_CMND_TEMPTARGETREAD "|" - D_CMND_TEMPMEASUREDREAD "|" D_CMND_TEMPMEASUREDGRDREAD "|" D_CMND_TEMPSENSNUMBERSET "|" - D_CMND_STATEEMERGENCYSET "|" D_CMND_POWERMAXSET "|" D_CMND_TIMEMANUALTOAUTOSET "|" D_CMND_TIMEONLIMITSET "|" - D_CMND_PROPBANDSET "|" D_CMND_TIMERESETSET "|" D_CMND_TIMEPICYCLESET "|" D_CMND_TEMPANTIWINDUPRESETSET "|" - D_CMND_TEMPHYSTSET "|" D_CMND_TIMEMAXACTIONSET "|" D_CMND_TIMEMINACTIONSET "|" D_CMND_TIMEMINTURNOFFACTIONSET "|" - D_CMND_TEMPRUPDELTINSET "|" D_CMND_TEMPRUPDELTOUTSET "|" D_CMND_TIMERAMPUPMAXSET "|" D_CMND_TIMERAMPUPCYCLESET "|" - D_CMND_TEMPRAMPUPPIACCERRSET "|" D_CMND_TIMEPIPROPORTREAD "|" D_CMND_TIMEPIINTEGRREAD "|" D_CMND_TIMESENSLOSTSET; - -void (* const ThermostatCommand[])(void) PROGMEM = { - &CmndThermostatModeSet, &CmndTempFrostProtectSet, &CmndControllerModeSet, &CmndInputSwitchSet, &CmndOutputRelaySet, - &CmndTimeAllowRampupSet, &CmndTempMeasuredSet, &CmndTempTargetSet, &CmndTempTargetRead, - &CmndTempMeasuredRead, &CmndTempMeasuredGrdRead, &CmndTempSensNumberSet, &CmndStateEmergencySet, - &CmndPowerMaxSet, &CmndTimeManualToAutoSet, &CmndTimeOnLimitSet, &CmndPropBandSet, &CmndTimeResetSet, - &CmndTimePiCycleSet, &CmndTempAntiWindupResetSet, &CmndTempHystSet, &CmndTimeMaxActionSet, - &CmndTimeMinActionSet, &CmndTimeMinTurnoffActionSet, &CmndTempRupDeltInSet, &CmndTempRupDeltOutSet, - &CmndTimeRampupMaxSet, &CmndTimeRampupCycleSet, &CmndTempRampupPiAccErrSet, &CmndTimePiProportRead, - &CmndTimePiIntegrRead, &CmndTimeSensLostSet }; - -struct THERMOSTAT { - uint32_t timestamp_temp_measured_update = 0; - uint32_t timestamp_temp_meas_change_update = 0; - uint32_t timestamp_output_off = 0; - uint32_t timestamp_input_on = 0; - uint32_t time_thermostat_total = 0; - uint32_t time_ctr_checkpoint = 0; - uint32_t time_ctr_changepoint = 0; - int32_t temp_measured_gradient = 0; - int16_t temp_target_level = THERMOSTAT_TEMP_INIT; - int16_t temp_target_level_ctr = THERMOSTAT_TEMP_INIT; - int16_t temp_pi_accum_error = 0; - int16_t temp_pi_error = 0; - int32_t time_proportional_pi; - int32_t time_integral_pi; - int32_t time_total_pi; - uint16_t kP_pi = 0; - uint16_t kI_pi = 0; - int32_t temp_rampup_meas_gradient = 0; - uint32_t timestamp_rampup_start = 0; - uint32_t time_rampup_deadtime = 0; - uint32_t time_rampup_nextcycle = 0; - int16_t temp_measured = 0; - uint8_t time_output_delay = THERMOSTAT_TIME_OUTPUT_DELAY; - uint8_t counter_rampup_cycles = 0; - uint8_t output_relay_number = THERMOSTAT_RELAY_NUMBER; - uint8_t input_switch_number = THERMOSTAT_SWITCH_NUMBER; - uint8_t temp_sens_number = THERMOSTAT_TEMP_SENS_NUMBER; - uint8_t temp_rampup_pi_acc_error = THERMOSTAT_TEMP_PI_RAMPUP_ACC_E; - uint8_t temp_rampup_delta_out = THERMOSTAT_TEMP_RAMPUP_DELTA_OUT; - uint8_t temp_rampup_delta_in = THERMOSTAT_TEMP_RAMPUP_DELTA_IN; - int16_t temp_rampup_output_off = 0; - int16_t temp_rampup_start = 0; - int16_t temp_rampup_cycle = 0; - uint16_t time_rampup_max = THERMOSTAT_TIME_RAMPUP_MAX; - uint16_t time_rampup_cycle = THERMOSTAT_TIME_RAMPUP_CYCLE; - uint16_t time_allow_rampup = THERMOSTAT_TIME_ALLOW_RAMPUP; - uint16_t time_sens_lost = THERMOSTAT_TIME_SENS_LOST; - uint16_t time_manual_to_auto = THERMOSTAT_TIME_MANUAL_TO_AUTO; - uint16_t time_on_limit = THERMOSTAT_TIME_ON_LIMIT; - uint32_t time_reset = THERMOSTAT_TIME_RESET; - uint16_t time_pi_cycle = THERMOSTAT_TIME_PI_CYCLE; - uint16_t time_max_action = THERMOSTAT_TIME_MAX_ACTION; - uint16_t time_min_action = THERMOSTAT_TIME_MIN_ACTION; - uint16_t time_min_turnoff_action = THERMOSTAT_TIME_MIN_TURNOFF_ACTION; - uint8_t val_prop_band = THERMOSTAT_PROP_BAND; - uint8_t temp_reset_anti_windup = THERMOSTAT_TEMP_RESET_ANTI_WINDUP; - int8_t temp_hysteresis = THERMOSTAT_TEMP_HYSTERESIS; - uint8_t temp_frost_protect = THERMOSTAT_TEMP_FROST_PROTECT; - uint16_t power_max = THERMOSTAT_POWER_MAX; - ThermostatBitfield status; -} Thermostat; - - - -void ThermostatInit(void) -{ - ExecuteCommandPower(Thermostat.output_relay_number, POWER_OFF, SRC_THERMOSTAT); - - Thermostat.status.thermostat_mode = THERMOSTAT_OFF; - Thermostat.status.controller_mode = CTR_HYBRID; - Thermostat.status.sensor_alive = IFACE_OFF; - Thermostat.status.command_output = IFACE_OFF; - Thermostat.status.phase_hybrid_ctr = CTR_HYBRID_PI; - Thermostat.status.status_output = IFACE_OFF; - Thermostat.status.status_cycle_active = CYCLE_OFF; - Thermostat.status.state_emergency = EMERGENCY_OFF; - Thermostat.status.counter_seconds = 0; -} - -bool ThermostatMinuteCounter(void) -{ - bool result = false; - Thermostat.status.counter_seconds++; - - if ((Thermostat.status.counter_seconds % 60) == 0) { - result = true; - Thermostat.status.counter_seconds = 0; - } - return result; -} - -inline bool ThermostatSwitchIdValid(uint8_t switchId) -{ - return (switchId >= THERMOSTAT_INPUT_SWT1 && switchId <= THERMOSTAT_INPUT_SWT4); -} - -inline bool ThermostatRelayIdValid(uint8_t relayId) -{ - return (relayId >= THERMOSTAT_OUTPUT_REL1 && relayId <= THERMOSTAT_OUTPUT_REL8); -} - -uint8_t ThermostatSwitchStatus(uint8_t input_switch) -{ - bool ifId = ThermostatSwitchIdValid(input_switch); - if(ifId) { - return(SwitchGetVirtual(ifId - THERMOSTAT_INPUT_SWT1)); - } - else return 255; -} - -void ThermostatSignalProcessingSlow(void) -{ - if ((uptime - Thermostat.timestamp_temp_measured_update) > ((uint32_t)Thermostat.time_sens_lost * 60)) { - Thermostat.status.sensor_alive = IFACE_OFF; - Thermostat.temp_measured_gradient = 0; - Thermostat.temp_measured = 0; - } -} - -void ThermostatSignalProcessingFast(void) -{ - if (ThermostatSwitchStatus(Thermostat.input_switch_number)) { - Thermostat.timestamp_input_on = uptime; - } -} - -void ThermostatCtrState(void) -{ - switch (Thermostat.status.controller_mode) { - case CTR_HYBRID: - ThermostatHybridCtrPhase(); - break; - case CTR_PI: - break; - case CTR_RAMP_UP: - break; - } -} - -void ThermostatHybridCtrPhase(void) -{ - if (Thermostat.status.controller_mode == CTR_HYBRID) { - switch (Thermostat.status.phase_hybrid_ctr) { - case CTR_HYBRID_RAMP_UP: - - - if((Thermostat.time_ctr_checkpoint != 0) - && (uptime >= Thermostat.time_ctr_checkpoint)) { - - Thermostat.time_ctr_checkpoint = 0; - - Thermostat.time_ctr_changepoint = 0; - - Thermostat.status.phase_hybrid_ctr = CTR_HYBRID_PI; - } - break; - case CTR_HYBRID_PI: - - - - - if (((uptime - Thermostat.timestamp_output_off) > (60 * (uint32_t)Thermostat.time_allow_rampup)) - && (Thermostat.temp_target_level != Thermostat.temp_target_level_ctr) - &&((Thermostat.temp_target_level - Thermostat.temp_measured) > Thermostat.temp_rampup_delta_in)) { - Thermostat.timestamp_rampup_start = uptime; - Thermostat.temp_rampup_start = Thermostat.temp_measured; - Thermostat.temp_rampup_meas_gradient = 0; - Thermostat.time_rampup_deadtime = 0; - Thermostat.counter_rampup_cycles = 1; - Thermostat.time_ctr_changepoint = 0; - Thermostat.time_ctr_checkpoint = 0; - Thermostat.status.phase_hybrid_ctr = CTR_HYBRID_RAMP_UP; - } - break; - } - } -#ifdef DEBUG_THERMOSTAT - ThermostatVirtualSwitchCtrState(); -#endif -} - -bool HeatStateAutoToManual(void) -{ - bool change_state = false; - - - - - if ((ThermostatSwitchStatus(Thermostat.input_switch_number) == 1) - || (Thermostat.status.sensor_alive == IFACE_OFF)) { - change_state = true; - } - return change_state; -} - -bool HeatStateManualToAuto(void) -{ - bool change_state; - - - - - - if ((ThermostatSwitchStatus(Thermostat.input_switch_number) == 0) - &&(Thermostat.status.sensor_alive == IFACE_ON) - && ((uptime - Thermostat.timestamp_input_on) > ((uint32_t)Thermostat.time_manual_to_auto * 60))) { - change_state = true; - } - return change_state; -} - -bool HeatStateAllToOff(void) -{ - bool change_state; - - - if (Thermostat.status.state_emergency == EMERGENCY_ON) { - Thermostat.status.thermostat_mode = THERMOSTAT_OFF; - } - return change_state; -} - -void ThermostatState(void) -{ - switch (Thermostat.status.thermostat_mode) { - case THERMOSTAT_OFF: - - break; - case THERMOSTAT_AUTOMATIC_OP: - if (HeatStateAllToOff()) { - Thermostat.status.thermostat_mode = THERMOSTAT_OFF; - } - if (HeatStateAutoToManual()) { - Thermostat.status.thermostat_mode = THERMOSTAT_MANUAL_OP; - } - ThermostatCtrState(); - break; - case THERMOSTAT_MANUAL_OP: - if (HeatStateAllToOff()) { - Thermostat.status.thermostat_mode = THERMOSTAT_OFF; - } - if (HeatStateManualToAuto()) { - Thermostat.status.thermostat_mode = THERMOSTAT_AUTOMATIC_OP; - } - break; - } -} - -void ThermostatOutputRelay(bool active) -{ - - - - - if ((active == true) - && (Thermostat.status.status_output == IFACE_OFF)) { - ExecuteCommandPower(Thermostat.output_relay_number, POWER_ON, SRC_THERMOSTAT); - Thermostat.status.status_output = IFACE_ON; -#ifdef DEBUG_THERMOSTAT - ThermostatVirtualSwitch(); -#endif - } - - - - else if ((active == false) && (Thermostat.status.status_output == IFACE_ON)) { - ExecuteCommandPower(Thermostat.output_relay_number, POWER_OFF, SRC_THERMOSTAT); - Thermostat.timestamp_output_off = uptime; - Thermostat.status.status_output = IFACE_OFF; -#ifdef DEBUG_THERMOSTAT - ThermostatVirtualSwitch(); -#endif - } -} - -void ThermostatCalculatePI(void) -{ - int32_t aux_time_error; - - - aux_time_error = (int32_t)(Thermostat.temp_target_level_ctr - Thermostat.temp_measured) * 10; - - - if (aux_time_error <= (int32_t)(INT16_MIN)) { - Thermostat.temp_pi_error = (int16_t)(INT16_MIN); - } - else if (aux_time_error >= (int32_t)INT16_MAX) { - Thermostat.temp_pi_error = (int16_t)INT16_MAX; - } - else { - Thermostat.temp_pi_error = (int16_t)aux_time_error; - } - - - Thermostat.kP_pi = 100 / (uint16_t)(Thermostat.val_prop_band); - - Thermostat.time_proportional_pi = ((int32_t)(Thermostat.temp_pi_error * (int16_t)Thermostat.kP_pi) * ((int32_t)Thermostat.time_pi_cycle * 60)) / 10000; - - - - - - if ((Thermostat.time_proportional_pi < abs(((int32_t)Thermostat.time_min_action * 60))) - && (Thermostat.time_proportional_pi > 0)) { - Thermostat.time_proportional_pi = ((int32_t)Thermostat.time_min_action * 60); - } - - if (Thermostat.time_proportional_pi < 0) { - Thermostat.time_proportional_pi = 0; - } - else if (Thermostat.time_proportional_pi > ((int32_t)Thermostat.time_pi_cycle * 60)) { - Thermostat.time_proportional_pi = ((int32_t)Thermostat.time_pi_cycle * 60); - } - - - - Thermostat.kI_pi = (uint16_t)((((uint32_t)Thermostat.kP_pi * (uint32_t)Thermostat.time_pi_cycle * 6000)) / (uint32_t)Thermostat.time_reset); - - - - - if (abs((Thermostat.temp_pi_error) / 10) > Thermostat.temp_reset_anti_windup) { - Thermostat.time_integral_pi = 0; - Thermostat.temp_pi_accum_error = 0; - } - - - - else { -# 459 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_39_thermostat.ino" - aux_time_error = (int32_t)Thermostat.temp_pi_accum_error + (int32_t)Thermostat.temp_pi_error; - - - if (aux_time_error <= (int32_t)INT16_MIN) { - Thermostat.temp_pi_accum_error = INT16_MIN; - } - else if (aux_time_error >= (int32_t)INT16_MAX) { - Thermostat.temp_pi_accum_error = INT16_MAX; - } - else { - Thermostat.temp_pi_accum_error = (int16_t)aux_time_error; - } - - - - - if ((Thermostat.temp_pi_error >= 0) - && (abs((Thermostat.temp_pi_error) / 10) <= (int16_t)Thermostat.temp_hysteresis) - && (Thermostat.temp_measured_gradient > 0)) { - - Thermostat.temp_pi_accum_error *= 0.8; - } - - - else if ((Thermostat.temp_pi_error < 0) - && (Thermostat.temp_measured_gradient > 0)) { - - Thermostat.temp_pi_accum_error *= 0.8; - } - - - if (Thermostat.temp_pi_accum_error < 0) { - Thermostat.temp_pi_accum_error = 0; - } - - - Thermostat.time_integral_pi = (((int32_t)Thermostat.temp_pi_accum_error * (int32_t)Thermostat.kI_pi) * (int32_t)((uint32_t)Thermostat.time_pi_cycle * 60)) / 1000000; - - - - - if (Thermostat.time_integral_pi > ((uint32_t)Thermostat.time_pi_cycle * 60)) { - Thermostat.time_integral_pi = ((uint32_t)Thermostat.time_pi_cycle * 60); - } - } - - - Thermostat.time_total_pi = Thermostat.time_proportional_pi + Thermostat.time_integral_pi; - - - - - if (Thermostat.time_total_pi >= ((int32_t)Thermostat.time_pi_cycle * 60)) { - - Thermostat.time_total_pi = ((int32_t)Thermostat.time_pi_cycle * 60); - } - else if (Thermostat.time_total_pi < 0) { - Thermostat.time_total_pi = 0; - } - - - - if (Thermostat.temp_pi_error <= 0) { - - if ((abs((Thermostat.temp_pi_error) / 10) > Thermostat.temp_hysteresis) - || (Thermostat.temp_measured_gradient >= 0)) { - Thermostat.time_total_pi = 0; - } - } - - - - - else if ((Thermostat.temp_pi_error > 0) - && (abs((Thermostat.temp_pi_error) / 10) <= Thermostat.temp_hysteresis) - && (Thermostat.temp_measured_gradient > 0)) { - Thermostat.time_total_pi = 0; - } - - - - if ((Thermostat.time_total_pi <= abs(((uint32_t)Thermostat.time_min_action * 60))) - && (Thermostat.time_total_pi != 0)) { - Thermostat.time_total_pi = ((int32_t)Thermostat.time_min_action * 60); - } - - - else if (Thermostat.time_total_pi > abs(((int32_t)Thermostat.time_max_action * 60))) { - Thermostat.time_total_pi = ((int32_t)Thermostat.time_max_action * 60); - } - - else if (Thermostat.time_total_pi > (((int32_t)Thermostat.time_pi_cycle * 60) - ((int32_t)Thermostat.time_min_turnoff_action * 60))) { - Thermostat.time_total_pi = ((int32_t)Thermostat.time_pi_cycle * 60); - } - - - Thermostat.time_ctr_changepoint = uptime + (uint32_t)Thermostat.time_total_pi; - - Thermostat.time_ctr_checkpoint = uptime + ((uint32_t)Thermostat.time_pi_cycle * 60); -} - -void ThermostatWorkAutomaticPI(void) -{ - char result_chr[FLOATSZ]; - - if ((uptime >= Thermostat.time_ctr_checkpoint) - || (Thermostat.temp_target_level != Thermostat.temp_target_level_ctr) - || ((Thermostat.temp_measured < Thermostat.temp_target_level) - && (Thermostat.temp_measured_gradient < 0) - && (Thermostat.status.status_cycle_active == CYCLE_OFF))) { - Thermostat.temp_target_level_ctr = Thermostat.temp_target_level; - ThermostatCalculatePI(); - - Thermostat.status.status_cycle_active = CYCLE_OFF; - } - if (uptime < Thermostat.time_ctr_changepoint) { - Thermostat.status.status_cycle_active = CYCLE_ON; - Thermostat.status.command_output = IFACE_ON; - } - else { - Thermostat.status.command_output = IFACE_OFF; - } -} - -void ThermostatWorkAutomaticRampUp(void) -{ - int32_t aux_temp_delta; - uint32_t time_in_rampup; - int16_t temp_delta_rampup; - - - if (Thermostat.temp_measured < Thermostat.temp_rampup_start) { - Thermostat.temp_rampup_start = Thermostat.temp_measured; - } - - - time_in_rampup = uptime - Thermostat.timestamp_rampup_start; - temp_delta_rampup = Thermostat.temp_measured - Thermostat.temp_rampup_start; - - Thermostat.status.command_output = IFACE_ON; - - Thermostat.temp_target_level_ctr = Thermostat.temp_target_level; - - - - if ((time_in_rampup <= (60 * (uint32_t)Thermostat.time_rampup_max)) - && (Thermostat.temp_measured < Thermostat.temp_target_level)) { - - - - if ((temp_delta_rampup >= Thermostat.temp_rampup_delta_out) - && (Thermostat.time_rampup_deadtime == 0)) { - - - int32_t time_aux; - time_aux = ((time_in_rampup / 2) - Thermostat.time_output_delay); - if (time_aux >= Thermostat.time_output_delay) { - Thermostat.time_rampup_deadtime = (uint32_t)time_aux; - } - else { - Thermostat.time_rampup_deadtime = Thermostat.time_output_delay; - } - - Thermostat.temp_rampup_meas_gradient = (int32_t)((360000 * (int32_t)temp_delta_rampup) / (int32_t)time_in_rampup); - Thermostat.time_rampup_nextcycle = uptime + (uint32_t)Thermostat.time_rampup_cycle; - - Thermostat.temp_rampup_cycle = Thermostat.temp_measured; - Thermostat.time_ctr_changepoint = uptime + (60 * (uint32_t)Thermostat.time_rampup_max); - Thermostat.temp_rampup_output_off = Thermostat.temp_target_level_ctr; - } - - else if ((Thermostat.time_rampup_deadtime > 0) && (uptime >= Thermostat.time_rampup_nextcycle)) { - - - temp_delta_rampup = Thermostat.temp_measured - Thermostat.temp_rampup_cycle; - uint32_t time_total_rampup = (uint32_t)Thermostat.time_rampup_cycle * Thermostat.counter_rampup_cycles; - - Thermostat.temp_rampup_meas_gradient = int32_t((360000 * (int32_t)temp_delta_rampup) / (int32_t)time_total_rampup); - if (Thermostat.temp_rampup_meas_gradient > 0) { - - - - - - - - aux_temp_delta = (int32_t)(Thermostat.temp_target_level_ctr - Thermostat.temp_rampup_cycle); - - - if ((aux_temp_delta < 0) - ||(temp_delta_rampup <= 0)) { - Thermostat.time_ctr_changepoint = uptime + (uint32_t)(60 * Thermostat.time_rampup_max); - } - else { - Thermostat.time_ctr_changepoint = (uint32_t)(uint32_t)(((uint32_t)(aux_temp_delta) * (uint32_t)(time_total_rampup)) / (uint32_t)temp_delta_rampup) + (uint32_t)Thermostat.time_rampup_nextcycle - (uint32_t)time_total_rampup - (uint32_t)Thermostat.time_rampup_deadtime; - } - - - - - Thermostat.temp_rampup_output_off = (int16_t)(((float)temp_delta_rampup * (float)(Thermostat.time_ctr_changepoint - (uptime - (time_total_rampup)))) / (float)(time_total_rampup * Thermostat.counter_rampup_cycles)) + Thermostat.temp_rampup_cycle; - - Thermostat.time_rampup_nextcycle = uptime + (uint32_t)Thermostat.time_rampup_cycle; - Thermostat.temp_rampup_cycle = Thermostat.temp_measured; - - Thermostat.counter_rampup_cycles = 1; - } - else { - - Thermostat.counter_rampup_cycles++; - - Thermostat.time_rampup_nextcycle = uptime + (uint32_t)Thermostat.time_rampup_cycle; - - Thermostat.time_ctr_changepoint = uptime + (60 * (uint32_t)Thermostat.time_rampup_max) - time_in_rampup; - Thermostat.temp_rampup_output_off = Thermostat.temp_target_level_ctr; - } - - Thermostat.time_ctr_checkpoint = Thermostat.time_ctr_changepoint + Thermostat.time_rampup_deadtime; - } - - - - - - - if ((Thermostat.time_rampup_deadtime == 0) - || (Thermostat.time_ctr_checkpoint == 0) - || (uptime < Thermostat.time_ctr_changepoint) - || (Thermostat.temp_measured < Thermostat.temp_rampup_output_off) - || (Thermostat.temp_rampup_meas_gradient <= 0)) { - Thermostat.status.command_output = IFACE_ON; - } - else { - Thermostat.status.command_output = IFACE_OFF; - } - } - else { - - if (Thermostat.temp_measured < Thermostat.temp_target_level_ctr) { - Thermostat.temp_pi_accum_error = Thermostat.temp_rampup_pi_acc_error; - } - - Thermostat.time_ctr_checkpoint = uptime; - - Thermostat.status.command_output = IFACE_OFF; - } -} - -void ThermostatCtrWork(void) -{ - switch (Thermostat.status.controller_mode) { - case CTR_HYBRID: - switch (Thermostat.status.phase_hybrid_ctr) { - case CTR_HYBRID_RAMP_UP: - ThermostatWorkAutomaticRampUp(); - break; - case CTR_HYBRID_PI: - ThermostatWorkAutomaticPI(); - break; - } - break; - case CTR_PI: - ThermostatWorkAutomaticPI(); - break; - case CTR_RAMP_UP: - ThermostatWorkAutomaticRampUp(); - break; - } -} - -void ThermostatWork(void) -{ - switch (Thermostat.status.thermostat_mode) { - case THERMOSTAT_OFF: - Thermostat.status.command_output = IFACE_OFF; - break; - case THERMOSTAT_AUTOMATIC_OP: - ThermostatCtrWork(); - break; - case THERMOSTAT_MANUAL_OP: - Thermostat.time_ctr_checkpoint = 0; - if (ThermostatSwitchStatus(Thermostat.input_switch_number) == 1) { - Thermostat.status.command_output = IFACE_ON; - } - else { - Thermostat.status.command_output = IFACE_OFF; - } - break; - } - bool output_command; - if (Thermostat.status.command_output == IFACE_OFF) { - output_command = false; - } - else { - output_command = true; - } - ThermostatOutputRelay(output_command); -} - -void ThermostatDiagnostics(void) -{ - - - - -} - -void ThermostatController(void) -{ - ThermostatState(); - ThermostatWork(); -} - -bool ThermostatTimerArm(int16_t tempVal) -{ - bool result = false; - - if ((tempVal >= -1000) - && (tempVal <= 1000) - && (tempVal >= (int16_t)Thermostat.temp_frost_protect)) { - Thermostat.temp_target_level = tempVal; - Thermostat.status.thermostat_mode = THERMOSTAT_AUTOMATIC_OP; - result = true; - } - - return result; -} - -void ThermostatTimerDisarm(void) -{ - Thermostat.temp_target_level = THERMOSTAT_TEMP_INIT; - Thermostat.status.thermostat_mode = THERMOSTAT_OFF; -} - -#ifdef DEBUG_THERMOSTAT -void ThermostatVirtualSwitch(void) -{ - char domoticz_in_topic[] = DOMOTICZ_IN_TOPIC; - Response_P(DOMOTICZ_MES, DOMOTICZ_IDX1, (0 == Thermostat.status.status_output) ? 0 : 1, ""); - MqttPublish(domoticz_in_topic); -} - -void ThermostatVirtualSwitchCtrState(void) -{ - char domoticz_in_topic[] = DOMOTICZ_IN_TOPIC; - Response_P(DOMOTICZ_MES, DOMOTICZ_IDX2, (0 == Thermostat.status.phase_hybrid_ctr) ? 0 : 1, ""); - MqttPublish(domoticz_in_topic); - - - -} -#endif - - - - - -void CmndThermostatModeSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint8_t value = (uint8_t)(CharToFloat(XdrvMailbox.data)); - if ((value >= THERMOSTAT_OFF) && (value < THERMOSTAT_MODES_MAX)) { - Thermostat.status.thermostat_mode = value; - Thermostat.timestamp_input_on = 0; - } - } - ResponseCmndNumber((int)Thermostat.status.thermostat_mode); -} - -void CmndTempFrostProtectSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint8_t value = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10); - if ((value >= 0) && (value <= 255)) { - Thermostat.temp_frost_protect = value; - } - } - ResponseCmndFloat((float)(Thermostat.temp_frost_protect) / 10, 1); -} - -void CmndControllerModeSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint8_t value = (uint8_t)(XdrvMailbox.payload); - if ((value >= 0) && (value < CTR_MODES_MAX)) { - Thermostat.status.controller_mode = value; - } - } - ResponseCmndNumber((int)Thermostat.status.controller_mode); -} - -void CmndInputSwitchSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint8_t value = (uint8_t)(XdrvMailbox.payload); - if (ThermostatSwitchIdValid(value)) { - Thermostat.input_switch_number = value; - Thermostat.timestamp_input_on = uptime; - } - } - ResponseCmndNumber((int)Thermostat.input_switch_number); -} - -void CmndOutputRelaySet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint8_t value = (uint8_t)(XdrvMailbox.payload); - if (ThermostatRelayIdValid(value)) { - Thermostat.output_relay_number = value; - } - } - ResponseCmndNumber((int)Thermostat.output_relay_number); -} - -void CmndTimeAllowRampupSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value < 86400)) { - Thermostat.time_allow_rampup = (uint16_t)(value / 60); - } - } - ResponseCmndNumber((int)((uint32_t)Thermostat.time_allow_rampup * 60)); -} - -void CmndTempMeasuredSet(void) -{ - if (XdrvMailbox.data_len > 0) { - int16_t value = (int16_t)(CharToFloat(XdrvMailbox.data) * 10); - if ((value >= -1000) && (value <= 1000)) { - uint32_t timestamp = uptime; - - if (value != Thermostat.temp_measured) { - int32_t temp_delta = (value - Thermostat.temp_measured); - uint32_t time_delta = (timestamp - Thermostat.timestamp_temp_meas_change_update); - Thermostat.temp_measured_gradient = (int32_t)((360000 * temp_delta) / ((int32_t)time_delta)); - Thermostat.temp_measured = value; - Thermostat.timestamp_temp_meas_change_update = timestamp; - } - Thermostat.timestamp_temp_measured_update = timestamp; - Thermostat.status.sensor_alive = IFACE_ON; - } - } - ResponseCmndFloat(((float)Thermostat.temp_measured) / 10, 1); -} - -void CmndTempTargetSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint16_t value = (uint16_t)(CharToFloat(XdrvMailbox.data) * 10); - if ((value >= -1000) - && (value <= 1000) - && (value >= (int16_t)Thermostat.temp_frost_protect)) { - Thermostat.temp_target_level = value; - } - } - ResponseCmndFloat(((float)Thermostat.temp_target_level) / 10, 1); -} - -void CmndTempTargetRead(void) -{ - ResponseCmndFloat(((float)Thermostat.temp_target_level) / 10, 1); -} - -void CmndTempMeasuredRead(void) -{ - ResponseCmndFloat((float)(Thermostat.temp_measured) / 10, 1); -} - -void CmndTempMeasuredGrdRead(void) -{ - ResponseCmndFloat((float)(Thermostat.temp_measured_gradient) / 1000, 1); -} - -void CmndTempSensNumberSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint8_t value = (uint8_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 255)) { - Thermostat.temp_sens_number = value; - } - } - ResponseCmndNumber((int)Thermostat.temp_sens_number); -} - -void CmndStateEmergencySet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint8_t value = (uint8_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 1)) { - Thermostat.status.state_emergency = (uint16_t)value; - } - } - ResponseCmndNumber((int)Thermostat.status.state_emergency); -} - -void CmndPowerMaxSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint16_t value = (uint16_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 1300)) { - Thermostat.power_max = value; - } - } - ResponseCmndNumber((int)Thermostat.power_max); -} - -void CmndTimeManualToAutoSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 86400)) { - Thermostat.time_manual_to_auto = (uint16_t)(value / 60); - } - } - ResponseCmndNumber((int)((uint32_t)Thermostat.time_manual_to_auto * 60)); -} - -void CmndTimeOnLimitSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 86400)) { - Thermostat.time_on_limit = (uint16_t)(value / 60); - } - } - ResponseCmndNumber((int)((uint32_t)Thermostat.time_on_limit * 60)); -} - -void CmndPropBandSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint8_t value = (uint8_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 20)) { - Thermostat.val_prop_band = value; - } - } - ResponseCmndNumber((int)Thermostat.val_prop_band); -} - -void CmndTimeResetSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 86400)) { - Thermostat.time_reset = value; - } - } - ResponseCmndNumber((int)Thermostat.time_reset); -} - -void CmndTimePiCycleSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 86400)) { - Thermostat.time_pi_cycle = (uint16_t)(value / 60); - } - } - ResponseCmndNumber((int)((uint32_t)Thermostat.time_pi_cycle * 60)); -} - -void CmndTempAntiWindupResetSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint8_t value = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10); - if ((value >= (float)(0)) && (value <= (float)(100.0))) { - Thermostat.temp_reset_anti_windup = value; - } - } - ResponseCmndFloat((float)(Thermostat.temp_reset_anti_windup) / 10, 1); -} - -void CmndTempHystSet(void) -{ - if (XdrvMailbox.data_len > 0) { - int8_t value = (int8_t)(CharToFloat(XdrvMailbox.data) * 10); - if ((value >= -100) && (value <= 100)) { - Thermostat.temp_hysteresis = value; - } - } - ResponseCmndFloat((float)(Thermostat.temp_hysteresis) / 10, 1); -} - -void CmndTimeMaxActionSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 86400)) { - Thermostat.time_max_action = (uint16_t)(value / 60); - } - } - ResponseCmndNumber((int)((uint32_t)Thermostat.time_max_action * 60)); -} - -void CmndTimeMinActionSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 86400)) { - Thermostat.time_min_action = (uint16_t)(value / 60); - } - } - ResponseCmndNumber((int)((uint32_t)Thermostat.time_min_action * 60)); -} - -void CmndTimeSensLostSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 86400)) { - Thermostat.time_sens_lost = (uint16_t)(value / 60); - } - } - ResponseCmndNumber((int)((uint32_t)Thermostat.time_sens_lost * 60)); -} - -void CmndTimeMinTurnoffActionSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 86400)) { - Thermostat.time_min_turnoff_action = (uint16_t)(value / 60); - } - } - ResponseCmndNumber((int)((uint32_t)Thermostat.time_min_turnoff_action * 60)); -} - -void CmndTempRupDeltInSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint8_t value = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10); - if ((value >= 0) && (value <= 100)) { - Thermostat.temp_rampup_delta_in = value; - } - } - ResponseCmndFloat((float)(Thermostat.temp_rampup_delta_in) / 10, 1); -} - -void CmndTempRupDeltOutSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint8_t value = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10); - if ((value >= 0) && (value <= 100)) { - Thermostat.temp_rampup_delta_out = value; - } - } - ResponseCmndFloat((float)(Thermostat.temp_rampup_delta_out) / 10, 1); -} - -void CmndTimeRampupMaxSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 86400)) { - Thermostat.time_rampup_max = (uint16_t)(value / 60); - } - } - ResponseCmndNumber((int)(((uint32_t)Thermostat.time_rampup_max) * 60)); -} - -void CmndTimeRampupCycleSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint32_t value = (uint32_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 54000)) { - Thermostat.time_rampup_cycle = (uint16_t)value; - } - } - ResponseCmndNumber((int)Thermostat.time_rampup_cycle); -} - -void CmndTempRampupPiAccErrSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint16_t value = (uint8_t)(CharToFloat(XdrvMailbox.data) * 100); - if ((value >= 0) && (value <= 2500)) { - Thermostat.temp_rampup_pi_acc_error = value; - } - } - ResponseCmndFloat((float)(Thermostat.temp_rampup_pi_acc_error) / 100, 1); -} - -void CmndTimePiProportRead(void) -{ - ResponseCmndNumber((int)Thermostat.time_proportional_pi); -} - -void CmndTimePiIntegrRead(void) -{ - ResponseCmndNumber((int)Thermostat.time_integral_pi); -} - - - - - -bool Xdrv39(uint8_t function) -{ -#ifdef DEBUG_THERMOSTAT - char result_chr[FLOATSZ]; -#endif - bool result = false; - - switch (function) { - case FUNC_INIT: - ThermostatInit(); - break; - case FUNC_LOOP: - ThermostatSignalProcessingFast(); - ThermostatDiagnostics(); - break; - case FUNC_SERIAL: - break; - case FUNC_EVERY_SECOND: - if (ThermostatMinuteCounter()) { - ThermostatSignalProcessingSlow(); - ThermostatController(); -#ifdef DEBUG_THERMOSTAT - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("")); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("------ Thermostat Start ------")); - dtostrfd(Thermostat.status.counter_seconds, 0, result_chr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.status.counter_seconds: %s"), result_chr); - dtostrfd(Thermostat.status.thermostat_mode, 0, result_chr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.status.thermostat_mode: %s"), result_chr); - dtostrfd(Thermostat.status.controller_mode, 0, result_chr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.status.controller_mode: %s"), result_chr); - dtostrfd(Thermostat.status.phase_hybrid_ctr, 0, result_chr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.status.phase_hybrid_ctr: %s"), result_chr); - dtostrfd(Thermostat.status.sensor_alive, 0, result_chr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.status.sensor_alive: %s"), result_chr); - dtostrfd(Thermostat.status.status_output, 0, result_chr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.status.status_output: %s"), result_chr); - dtostrfd(Thermostat.status.status_cycle_active, 0, result_chr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.status.status_cycle_active: %s"), result_chr); - dtostrfd(Thermostat.temp_pi_error, 0, result_chr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.temp_pi_error: %s"), result_chr); - dtostrfd(Thermostat.temp_pi_accum_error, 0, result_chr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.temp_pi_accum_error: %s"), result_chr); - dtostrfd(Thermostat.time_proportional_pi, 0, result_chr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.time_proportional_pi: %s"), result_chr); - dtostrfd(Thermostat.time_integral_pi, 0, result_chr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.time_integral_pi: %s"), result_chr); - dtostrfd(Thermostat.time_total_pi, 0, result_chr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.time_total_pi: %s"), result_chr); - dtostrfd(Thermostat.temp_measured_gradient, 0, result_chr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.temp_measured_gradient: %s"), result_chr); - dtostrfd(Thermostat.time_rampup_deadtime, 0, result_chr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.time_rampup_deadtime: %s"), result_chr); - dtostrfd(Thermostat.temp_rampup_meas_gradient, 0, result_chr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.temp_rampup_meas_gradient: %s"), result_chr); - dtostrfd(Thermostat.time_ctr_changepoint, 0, result_chr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.time_ctr_changepoint: %s"), result_chr); - dtostrfd(Thermostat.temp_rampup_output_off, 0, result_chr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.temp_rampup_output_off: %s"), result_chr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("------ Thermostat End ------")); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("")); -#endif - } - break; - case FUNC_COMMAND: - result = DecodeCommand(kThermostatCommands, ThermostatCommand); - break; - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_99_debug.ino" -# 22 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_99_debug.ino" -#ifdef DEBUG_THEO -#ifndef USE_DEBUG_DRIVER -#define USE_DEBUG_DRIVER -#endif -#endif - -#ifdef USE_DEBUG_DRIVER - - - - - - -#define XDRV_99 99 - -#ifndef CPU_LOAD_CHECK -#define CPU_LOAD_CHECK 1 -#endif - - - - - -#define D_CMND_CFGDUMP "CfgDump" -#define D_CMND_CFGPEEK "CfgPeek" -#define D_CMND_CFGPOKE "CfgPoke" -#define D_CMND_CFGSHOW "CfgShow" -#define D_CMND_CFGXOR "CfgXor" -#define D_CMND_CPUCHECK "CpuChk" -#define D_CMND_EXCEPTION "Exception" -#define D_CMND_FLASHDUMP "FlashDump" -#define D_CMND_FLASHMODE "FlashMode" -#define D_CMND_FREEMEM "FreeMem" -#define D_CMND_HELP "Help" -#define D_CMND_RTCDUMP "RtcDump" -#define D_CMND_SETSENSOR "SetSensor" -#define D_CMND_I2CWRITE "I2CWrite" -#define D_CMND_I2CREAD "I2CRead" -#define D_CMND_I2CSTRETCH "I2CStretch" -#define D_CMND_I2CCLOCK "I2CClock" - -const char kDebugCommands[] PROGMEM = "|" - D_CMND_CFGDUMP "|" D_CMND_CFGPEEK "|" D_CMND_CFGPOKE "|" -#ifdef USE_WEBSERVER - D_CMND_CFGXOR "|" -#endif - D_CMND_CPUCHECK "|" -#ifdef DEBUG_THEO - D_CMND_EXCEPTION "|" -#endif - D_CMND_FLASHDUMP "|" D_CMND_FLASHMODE "|" D_CMND_FREEMEM"|" D_CMND_HELP "|" D_CMND_RTCDUMP "|" D_CMND_SETSENSOR "|" -#ifdef USE_I2C - D_CMND_I2CWRITE "|" D_CMND_I2CREAD "|" D_CMND_I2CSTRETCH "|" D_CMND_I2CCLOCK -#endif - ; - -void (* const DebugCommand[])(void) PROGMEM = { - &CmndCfgDump, &CmndCfgPeek, &CmndCfgPoke, -#ifdef USE_WEBSERVER - &CmndCfgXor, -#endif - &CmndCpuCheck, -#ifdef DEBUG_THEO - &CmndException, -#endif - &CmndFlashDump, &CmndFlashMode, &CmndFreemem, &CmndHelp, &CmndRtcDump, &CmndSetSensor, -#ifdef USE_I2C - &CmndI2cWrite, &CmndI2cRead, &CmndI2cStretch, &CmndI2cClock -#endif - }; - -uint32_t CPU_loops = 0; -uint32_t CPU_last_millis = 0; -uint32_t CPU_last_loop_time = 0; -uint8_t CPU_load_check = 0; -uint8_t CPU_show_freemem = 0; - - - -#ifdef DEBUG_THEO -void ExceptionTest(uint8_t type) -{ -# 145 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_99_debug.ino" - if (1 == type) { - char svalue[10]; - snprintf_P(svalue, sizeof(svalue), PSTR("%s"), 7); - } -# 159 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_99_debug.ino" - if (2 == type) { - while(1) delay(1000); - } -} - -#endif - - - -void CpuLoadLoop(void) -{ - CPU_last_loop_time = millis(); - if (CPU_load_check && CPU_last_millis) { - CPU_loops ++; - if ((CPU_last_millis + (CPU_load_check *1000)) <= CPU_last_loop_time) { -#if defined(F_CPU) && (F_CPU == 160000000L) - int CPU_load = 100 - ( (CPU_loops*(1 + 30*ssleep)) / (CPU_load_check *800) ); - CPU_loops = CPU_loops / CPU_load_check; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(160MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops); -#else - int CPU_load = 100 - ( (CPU_loops*(1 + 30*ssleep)) / (CPU_load_check *400) ); - CPU_loops = CPU_loops / CPU_load_check; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(80MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops); -#endif - CPU_last_millis = CPU_last_loop_time; - CPU_loops = 0; - } - } -} - - - -#ifdef ESP8266 -#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) - - - -extern "C" { -#include - extern cont_t g_cont; -} - -void DebugFreeMem(void) -{ - register uint32_t *sp asm("a1"); - - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), 4 * (sp - g_cont.stack), XdrvMailbox.data); -} - -#else - - - - -extern "C" { -#include - extern cont_t* g_pcont; -} - -void DebugFreeMem(void) -{ - register uint32_t *sp asm("a1"); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), 4 * (sp - g_pcont->stack), XdrvMailbox.data); -} - -#endif - -#else - -void DebugFreeMem(void) -{ - register uint8_t *sp asm("a1"); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), sp - pxTaskGetStackStart(NULL), XdrvMailbox.data); -} - -#endif - - - -void DebugRtcDump(char* parms) -{ -#ifdef ESP8266 - #define CFG_COLS 16 - - uint16_t idx; - uint16_t maxrow; - uint16_t row; - uint16_t col; - char *p; -# 259 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_99_debug.ino" - uint8_t buffer[768]; - - system_rtc_mem_read(0, (uint32_t*)&buffer, sizeof(buffer)); - - maxrow = ((sizeof(buffer)+CFG_COLS)/CFG_COLS); - - uint16_t srow = strtol(parms, &p, 16) / CFG_COLS; - uint16_t mrow = strtol(p, &p, 10); - - - - if (0 == mrow) { - mrow = 8; - } - if (srow > maxrow) { - srow = maxrow - mrow; - } - if (mrow < (maxrow - srow)) { - maxrow = srow + mrow; - } - - for (row = srow; row < maxrow; row++) { - idx = row * CFG_COLS; - snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx); - for (col = 0; col < CFG_COLS; col++) { - if (!(col%4)) { - snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); - for (col = 0; col < CFG_COLS; col++) { - - - - snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' '); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data); - AddLog(LOG_LEVEL_INFO); - } -#endif -} - - - -void DebugCfgDump(char* parms) -{ - #define CFG_COLS 16 - - uint16_t idx; - uint16_t maxrow; - uint16_t row; - uint16_t col; - char *p; - - uint8_t *buffer = (uint8_t *) &Settings; - maxrow = ((sizeof(SYSCFG)+CFG_COLS)/CFG_COLS); - - uint16_t srow = strtol(parms, &p, 16) / CFG_COLS; - uint16_t mrow = strtol(p, &p, 10); - - - - if (0 == mrow) { - mrow = 8; - } - if (srow > maxrow) { - srow = maxrow - mrow; - } - if (mrow < (maxrow - srow)) { - maxrow = srow + mrow; - } - - for (row = srow; row < maxrow; row++) { - idx = row * CFG_COLS; - snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx); - for (col = 0; col < CFG_COLS; col++) { - if (!(col%4)) { - snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); - for (col = 0; col < CFG_COLS; col++) { - - - - snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' '); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data); - AddLog(LOG_LEVEL_INFO); - delay(1); - } -} - -void DebugCfgPeek(char* parms) -{ - char *p; - - uint16_t address = strtol(parms, &p, 16); - if (address > sizeof(SYSCFG)) address = sizeof(SYSCFG) -4; - address = (address >> 2) << 2; - - uint8_t *buffer = (uint8_t *) &Settings; - uint8_t data8 = buffer[address]; - uint16_t data16 = (buffer[address +1] << 8) + buffer[address]; - uint32_t data32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + data16; - - snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), address); - for (uint32_t i = 0; i < 4; i++) { - snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[address +i]); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); - for (uint32_t i = 0; i < 4; i++) { - snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[address +i] > 0x20) && (buffer[address +i] < 0x7F)) ? (char)buffer[address +i] : ' '); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s| 0x%02X (%d), 0x%04X (%d), 0x%0LX (%lu)"), log_data, data8, data8, data16, data16, data32, data32); - AddLog(LOG_LEVEL_INFO); -} - -void DebugCfgPoke(char* parms) -{ - char *p; - - uint16_t address = strtol(parms, &p, 16); - if (address > sizeof(SYSCFG)) address = sizeof(SYSCFG) -4; - address = (address >> 2) << 2; - - uint32_t data = strtol(p, &p, 16); - - uint8_t *buffer = (uint8_t *) &Settings; - uint32_t data32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + (buffer[address +1] << 8) + buffer[address]; - - uint8_t *nbuffer = (uint8_t *) &data; - for (uint32_t i = 0; i < 4; i++) { buffer[address +i] = nbuffer[+i]; } - - uint32_t ndata32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + (buffer[address +1] << 8) + buffer[address]; - - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: 0x%0LX (%lu) poked to 0x%0LX (%lu)"), address, data32, data32, ndata32, ndata32); -} - -void SetFlashMode(uint8_t mode) -{ -#ifdef ESP8266 - uint8_t *_buffer; - uint32_t address; - - address = 0; - _buffer = new uint8_t[FLASH_SECTOR_SIZE]; - - if (ESP.flashRead(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE)) { - if (_buffer[2] != mode) { - _buffer[2] = mode; - if (ESP.flashEraseSector(address / FLASH_SECTOR_SIZE)) { - ESP.flashWrite(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE); - } - } - } - delete[] _buffer; -#endif -} - - - - - -void CmndHelp(void) -{ - AddLog_P(LOG_LEVEL_INFO, PSTR("HLP: "), kDebugCommands); - ResponseCmndDone(); -} - -void CmndRtcDump(void) -{ - DebugRtcDump(XdrvMailbox.data); - ResponseCmndDone(); -} - -void CmndCfgDump(void) -{ - DebugCfgDump(XdrvMailbox.data); - ResponseCmndDone(); -} - -void CmndCfgPeek(void) -{ - DebugCfgPeek(XdrvMailbox.data); - ResponseCmndDone(); -} - -void CmndCfgPoke(void) -{ - DebugCfgPoke(XdrvMailbox.data); - ResponseCmndDone(); -} - -#ifdef USE_WEBSERVER -void CmndCfgXor(void) -{ - if (XdrvMailbox.data_len > 0) { - Web.config_xor_on_set = XdrvMailbox.payload; - } - ResponseCmndNumber(Web.config_xor_on_set); -} -#endif - -#ifdef DEBUG_THEO -void CmndException(void) -{ - if (XdrvMailbox.data_len > 0) { ExceptionTest(XdrvMailbox.payload); } - ResponseCmndDone(); -} -#endif - -void CmndCpuCheck(void) -{ - if (XdrvMailbox.data_len > 0) { - CPU_load_check = XdrvMailbox.payload; - CPU_last_millis = CPU_last_loop_time; - } - ResponseCmndNumber(CPU_load_check); -} - -void CmndFreemem(void) -{ - if (XdrvMailbox.data_len > 0) { - CPU_show_freemem = XdrvMailbox.payload; - } - ResponseCmndNumber(CPU_show_freemem); -} - -void CmndSetSensor(void) -{ - if (XdrvMailbox.index < MAX_XSNS_DRIVERS) { - if (XdrvMailbox.payload >= 0) { - bitWrite(Settings.sensors[XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1); - if (1 == XdrvMailbox.payload) { - restart_flag = 2; - } - } - Response_P(PSTR("{\"" D_CMND_SETSENSOR "\":")); - XsnsSensorState(); - ResponseJsonEnd(); - } -} - -void CmndFlashMode(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - SetFlashMode(XdrvMailbox.payload); - } - ResponseCmndNumber(ESP.getFlashChipMode()); -} - -uint32_t DebugSwap32(uint32_t x) { - return ((x << 24) & 0xff000000 ) | - ((x << 8) & 0x00ff0000 ) | - ((x >> 8) & 0x0000ff00 ) | - ((x >> 24) & 0x000000ff ); -} - -void CmndFlashDump(void) -{ -#ifdef ESP8266 - - - - const uint32_t flash_start = 0x40200000; - const uint8_t bytes_per_cols = 0x20; - const uint32_t max = (SPIFFS_END + 5) * SPI_FLASH_SEC_SIZE; - - uint32_t start = flash_start; - uint32_t rows = 8; - - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= (max - bytes_per_cols))) { - start += (XdrvMailbox.payload &0x7FFFFFFC); - - char *p; - uint32_t is_payload = strtol(XdrvMailbox.data, &p, 16); - rows = strtol(p, &p, 10); - if (0 == rows) { rows = 8; } - } - uint32_t end = start + (rows * bytes_per_cols); - if ((end - flash_start) > max) { - end = flash_start + max; - } - - for (uint32_t pos = start; pos < end; pos += bytes_per_cols) { - uint32_t* values = (uint32_t*)(pos); - AddLog_P2(LOG_LEVEL_INFO, PSTR("%06X: %08X %08X %08X %08X %08X %08X %08X %08X"), pos - flash_start, - DebugSwap32(values[0]), DebugSwap32(values[1]), DebugSwap32(values[2]), DebugSwap32(values[3]), - DebugSwap32(values[4]), DebugSwap32(values[5]), DebugSwap32(values[6]), DebugSwap32(values[7])); - } - ResponseCmndDone(); -#endif -} - -#ifdef USE_I2C -void CmndI2cWrite(void) -{ - - if (i2c_flg) { - char* parms = XdrvMailbox.data; - uint8_t buffer[100]; - uint32_t index = 0; - - char *p; - char *data = strtok_r(parms, " ,", &p); - while (data != NULL && index < sizeof(buffer)) { - buffer[index++] = strtol(data, nullptr, 16); - data = strtok_r(nullptr, " ,", &p); - } - - if (index > 1) { - AddLogBuffer(LOG_LEVEL_INFO, buffer, index); - - Wire.beginTransmission(buffer[0]); - for (uint32_t i = 1; i < index; i++) { - Wire.write(buffer[i]); - } - int result = Wire.endTransmission(); - AddLog_P2(LOG_LEVEL_INFO, PSTR("I2C: Result %d"), result); - } - } - ResponseCmndDone(); -} - -void CmndI2cRead(void) -{ - - if (i2c_flg) { - char* parms = XdrvMailbox.data; - uint8_t buffer[100]; - uint32_t index = 0; - - char *p; - char *data = strtok_r(parms, " ,", &p); - while (data != NULL && index < sizeof(buffer)) { - buffer[index++] = strtol(data, nullptr, 16); - data = strtok_r(nullptr, " ,", &p); - } - - if (index > 0) { - uint8_t size = 1; - if (index > 1) { - size = buffer[1]; - } - Wire.requestFrom(buffer[0], size); - index = 0; - while (Wire.available() && index < sizeof(buffer)) { - buffer[index++] = Wire.read(); - } - if (index > 0) { - AddLogBuffer(LOG_LEVEL_INFO, buffer, index); - } - } - } - ResponseCmndDone(); -} - -void CmndI2cStretch(void) -{ -#ifdef ESP8266 - if (i2c_flg && (XdrvMailbox.payload > 0)) { - Wire.setClockStretchLimit(XdrvMailbox.payload); - } - ResponseCmndDone(); -#endif -} - -void CmndI2cClock(void) -{ - if (i2c_flg && (XdrvMailbox.payload > 0)) { - Wire.setClock(XdrvMailbox.payload); - } - ResponseCmndDone(); -} -#endif - - - - - -bool Xdrv99(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_LOOP: - CpuLoadLoop(); - break; - case FUNC_FREE_MEM: - if (CPU_show_freemem) { DebugFreeMem(); } - break; - case FUNC_PRE_INIT: - CPU_last_millis = millis(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kDebugCommands, DebugCommand); - break; - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_interface.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdrv_interface.ino" -#ifdef XFUNC_PTR_IN_ROM -bool (* const xdrv_func_ptr[])(uint8_t) PROGMEM = { -#else -bool (* const xdrv_func_ptr[])(uint8_t) = { -#endif - -#ifdef XDRV_01 - &Xdrv01, -#endif - -#ifdef XDRV_02 - &Xdrv02, -#endif - -#ifdef XDRV_03 - &Xdrv03, -#endif - -#ifdef XDRV_04 - &Xdrv04, -#endif - -#ifdef XDRV_05 - &Xdrv05, -#endif - -#ifdef XDRV_06 - &Xdrv06, -#endif - -#ifdef XDRV_07 - &Xdrv07, -#endif - -#ifdef XDRV_08 - &Xdrv08, -#endif - -#ifdef XDRV_09 - &Xdrv09, -#endif - -#ifdef XDRV_10 - &Xdrv10, -#endif - -#ifdef XDRV_11 - &Xdrv11, -#endif - -#ifdef XDRV_12 - &Xdrv12, -#endif - -#ifdef XDRV_13 - &Xdrv13, -#endif - -#ifdef XDRV_14 - &Xdrv14, -#endif - -#ifdef XDRV_15 - &Xdrv15, -#endif - -#ifdef XDRV_16 - &Xdrv16, -#endif - -#ifdef XDRV_17 - &Xdrv17, -#endif - -#ifdef XDRV_18 - &Xdrv18, -#endif - -#ifdef XDRV_19 - &Xdrv19, -#endif - -#ifdef XDRV_20 - &Xdrv20, -#endif - -#ifdef XDRV_21 - &Xdrv21, -#endif - -#ifdef XDRV_22 - &Xdrv22, -#endif - -#ifdef XDRV_23 - &Xdrv23, -#endif - -#ifdef XDRV_24 - &Xdrv24, -#endif - -#ifdef XDRV_25 - &Xdrv25, -#endif - -#ifdef XDRV_26 - &Xdrv26, -#endif - -#ifdef XDRV_27 - &Xdrv27, -#endif - -#ifdef XDRV_28 - &Xdrv28, -#endif - -#ifdef XDRV_29 - &Xdrv29, -#endif - -#ifdef XDRV_30 - &Xdrv30, -#endif - -#ifdef XDRV_31 - &Xdrv31, -#endif - -#ifdef XDRV_32 - &Xdrv32, -#endif - -#ifdef XDRV_33 - &Xdrv33, -#endif - -#ifdef XDRV_34 - &Xdrv34, -#endif - -#ifdef XDRV_35 - &Xdrv35, -#endif - -#ifdef XDRV_36 - &Xdrv36, -#endif - -#ifdef XDRV_37 - &Xdrv37, -#endif - -#ifdef XDRV_38 - &Xdrv38, -#endif - -#ifdef XDRV_39 - &Xdrv39, -#endif - -#ifdef XDRV_40 - &Xdrv40, -#endif - -#ifdef XDRV_41 - &Xdrv41, -#endif - -#ifdef XDRV_42 - &Xdrv42, -#endif - -#ifdef XDRV_43 - &Xdrv43, -#endif - -#ifdef XDRV_44 - &Xdrv44, -#endif - -#ifdef XDRV_45 - &Xdrv45, -#endif - -#ifdef XDRV_46 - &Xdrv46, -#endif - -#ifdef XDRV_47 - &Xdrv47, -#endif - -#ifdef XDRV_48 - &Xdrv48, -#endif - -#ifdef XDRV_49 - &Xdrv49, -#endif - -#ifdef XDRV_50 - &Xdrv50, -#endif - -#ifdef XDRV_51 - &Xdrv51, -#endif - -#ifdef XDRV_52 - &Xdrv52, -#endif - -#ifdef XDRV_53 - &Xdrv53, -#endif - -#ifdef XDRV_54 - &Xdrv54, -#endif - -#ifdef XDRV_55 - &Xdrv55, -#endif - -#ifdef XDRV_56 - &Xdrv56, -#endif - -#ifdef XDRV_57 - &Xdrv57, -#endif - -#ifdef XDRV_58 - &Xdrv58, -#endif - -#ifdef XDRV_59 - &Xdrv59, -#endif - -#ifdef XDRV_60 - &Xdrv60, -#endif - -#ifdef XDRV_61 - &Xdrv61, -#endif - -#ifdef XDRV_62 - &Xdrv62, -#endif - -#ifdef XDRV_63 - &Xdrv63, -#endif - -#ifdef XDRV_64 - &Xdrv64, -#endif - -#ifdef XDRV_65 - &Xdrv65, -#endif - -#ifdef XDRV_66 - &Xdrv66, -#endif - -#ifdef XDRV_67 - &Xdrv67, -#endif - -#ifdef XDRV_68 - &Xdrv68, -#endif - -#ifdef XDRV_69 - &Xdrv69, -#endif - -#ifdef XDRV_70 - &Xdrv70, -#endif - -#ifdef XDRV_71 - &Xdrv71, -#endif - -#ifdef XDRV_72 - &Xdrv72, -#endif - -#ifdef XDRV_73 - &Xdrv73, -#endif - -#ifdef XDRV_74 - &Xdrv74, -#endif - -#ifdef XDRV_75 - &Xdrv75, -#endif - -#ifdef XDRV_76 - &Xdrv76, -#endif - -#ifdef XDRV_77 - &Xdrv77, -#endif - -#ifdef XDRV_78 - &Xdrv78, -#endif - -#ifdef XDRV_79 - &Xdrv79, -#endif - -#ifdef XDRV_80 - &Xdrv80, -#endif - -#ifdef XDRV_81 - &Xdrv81, -#endif - -#ifdef XDRV_82 - &Xdrv82, -#endif - -#ifdef XDRV_83 - &Xdrv83, -#endif - -#ifdef XDRV_84 - &Xdrv84, -#endif - -#ifdef XDRV_85 - &Xdrv85, -#endif - -#ifdef XDRV_86 - &Xdrv86, -#endif - -#ifdef XDRV_87 - &Xdrv87, -#endif - -#ifdef XDRV_88 - &Xdrv88, -#endif - -#ifdef XDRV_89 - &Xdrv89, -#endif - -#ifdef XDRV_90 - &Xdrv90, -#endif - -#ifdef XDRV_91 - &Xdrv91, -#endif - -#ifdef XDRV_92 - &Xdrv92, -#endif - -#ifdef XDRV_93 - &Xdrv93, -#endif - -#ifdef XDRV_94 - &Xdrv94, -#endif - -#ifdef XDRV_95 - &Xdrv95, -#endif - -#ifdef XDRV_96 - &Xdrv96, -#endif - -#ifdef XDRV_97 - &Xdrv97, -#endif - -#ifdef XDRV_98 - &Xdrv98, -#endif - -#ifdef XDRV_99 - &Xdrv99 -#endif -}; - -const uint8_t xdrv_present = sizeof(xdrv_func_ptr) / sizeof(xdrv_func_ptr[0]); - - - - - -#ifdef XFUNC_PTR_IN_ROM -const uint8_t kXdrvList[] PROGMEM = { -#else -const uint8_t kXdrvList[] = { -#endif - -#ifdef XDRV_01 - XDRV_01, -#endif - -#ifdef XDRV_02 - XDRV_02, -#endif - -#ifdef XDRV_03 - XDRV_03, -#endif - -#ifdef XDRV_04 - XDRV_04, -#endif - -#ifdef XDRV_05 - XDRV_05, -#endif - -#ifdef XDRV_06 - XDRV_06, -#endif - -#ifdef XDRV_07 - XDRV_07, -#endif - -#ifdef XDRV_08 - XDRV_08, -#endif - -#ifdef XDRV_09 - XDRV_09, -#endif - -#ifdef XDRV_10 - XDRV_10, -#endif - -#ifdef XDRV_11 - XDRV_11, -#endif - -#ifdef XDRV_12 - XDRV_12, -#endif - -#ifdef XDRV_13 - XDRV_13, -#endif - -#ifdef XDRV_14 - XDRV_14, -#endif - -#ifdef XDRV_15 - XDRV_15, -#endif - -#ifdef XDRV_16 - XDRV_16, -#endif - -#ifdef XDRV_17 - XDRV_17, -#endif - -#ifdef XDRV_18 - XDRV_18, -#endif - -#ifdef XDRV_19 - XDRV_19, -#endif - -#ifdef XDRV_20 - XDRV_20, -#endif - -#ifdef XDRV_21 - XDRV_21, -#endif - -#ifdef XDRV_22 - XDRV_22, -#endif - -#ifdef XDRV_23 - XDRV_23, -#endif - -#ifdef XDRV_24 - XDRV_24, -#endif - -#ifdef XDRV_25 - XDRV_25, -#endif - -#ifdef XDRV_26 - XDRV_26, -#endif - -#ifdef XDRV_27 - XDRV_27, -#endif - -#ifdef XDRV_28 - XDRV_28, -#endif - -#ifdef XDRV_29 - XDRV_29, -#endif - -#ifdef XDRV_30 - XDRV_30, -#endif - -#ifdef XDRV_31 - XDRV_31, -#endif - -#ifdef XDRV_32 - XDRV_32, -#endif - -#ifdef XDRV_33 - XDRV_33, -#endif - -#ifdef XDRV_34 - XDRV_34, -#endif - -#ifdef XDRV_35 - XDRV_35, -#endif - -#ifdef XDRV_36 - XDRV_36, -#endif - -#ifdef XDRV_37 - XDRV_37, -#endif - -#ifdef XDRV_38 - XDRV_38, -#endif - -#ifdef XDRV_39 - XDRV_39, -#endif - -#ifdef XDRV_40 - XDRV_40, -#endif - -#ifdef XDRV_41 - XDRV_41, -#endif - -#ifdef XDRV_42 - XDRV_42, -#endif - -#ifdef XDRV_43 - XDRV_43, -#endif - -#ifdef XDRV_44 - XDRV_44, -#endif - -#ifdef XDRV_45 - XDRV_45, -#endif - -#ifdef XDRV_46 - XDRV_46, -#endif - -#ifdef XDRV_47 - XDRV_47, -#endif - -#ifdef XDRV_48 - XDRV_48, -#endif - -#ifdef XDRV_49 - XDRV_49, -#endif - -#ifdef XDRV_50 - XDRV_50, -#endif - -#ifdef XDRV_51 - XDRV_51, -#endif - -#ifdef XDRV_52 - XDRV_52, -#endif - -#ifdef XDRV_53 - XDRV_53, -#endif - -#ifdef XDRV_54 - XDRV_54, -#endif - -#ifdef XDRV_55 - XDRV_55, -#endif - -#ifdef XDRV_56 - XDRV_56, -#endif - -#ifdef XDRV_57 - XDRV_57, -#endif - -#ifdef XDRV_58 - XDRV_58, -#endif - -#ifdef XDRV_59 - XDRV_59, -#endif - -#ifdef XDRV_60 - XDRV_60, -#endif - -#ifdef XDRV_61 - XDRV_61, -#endif - -#ifdef XDRV_62 - XDRV_62, -#endif - -#ifdef XDRV_63 - XDRV_63, -#endif - -#ifdef XDRV_64 - XDRV_64, -#endif - -#ifdef XDRV_65 - XDRV_65, -#endif - -#ifdef XDRV_66 - XDRV_66, -#endif - -#ifdef XDRV_67 - XDRV_67, -#endif - -#ifdef XDRV_68 - XDRV_68, -#endif - -#ifdef XDRV_69 - XDRV_69, -#endif - -#ifdef XDRV_70 - XDRV_70, -#endif - -#ifdef XDRV_71 - XDRV_71, -#endif - -#ifdef XDRV_72 - XDRV_72, -#endif - -#ifdef XDRV_73 - XDRV_73, -#endif - -#ifdef XDRV_74 - XDRV_74, -#endif - -#ifdef XDRV_75 - XDRV_75, -#endif - -#ifdef XDRV_76 - XDRV_76, -#endif - -#ifdef XDRV_77 - XDRV_77, -#endif - -#ifdef XDRV_78 - XDRV_78, -#endif - -#ifdef XDRV_79 - XDRV_79, -#endif - -#ifdef XDRV_80 - XDRV_80, -#endif - -#ifdef XDRV_81 - XDRV_81, -#endif - -#ifdef XDRV_82 - XDRV_82, -#endif - -#ifdef XDRV_83 - XDRV_83, -#endif - -#ifdef XDRV_84 - XDRV_84, -#endif - -#ifdef XDRV_85 - XDRV_85, -#endif - -#ifdef XDRV_86 - XDRV_86, -#endif - -#ifdef XDRV_87 - XDRV_87, -#endif - -#ifdef XDRV_88 - XDRV_88, -#endif - -#ifdef XDRV_89 - XDRV_89, -#endif - -#ifdef XDRV_90 - XDRV_90, -#endif - -#ifdef XDRV_91 - XDRV_91, -#endif - -#ifdef XDRV_92 - XDRV_92, -#endif - -#ifdef XDRV_93 - XDRV_93, -#endif - -#ifdef XDRV_94 - XDRV_94, -#endif - -#ifdef XDRV_95 - XDRV_95, -#endif - -#ifdef XDRV_96 - XDRV_96, -#endif - -#ifdef XDRV_97 - XDRV_97, -#endif - -#ifdef XDRV_98 - XDRV_98, -#endif - -#ifdef XDRV_99 - XDRV_99 -#endif -}; - - - -void XsnsDriverState(void) -{ - ResponseAppend_P(PSTR(",\"Drivers\":\"")); - for (uint32_t i = 0; i < sizeof(kXdrvList); i++) { -#ifdef XFUNC_PTR_IN_ROM - uint32_t driverid = pgm_read_byte(kXdrvList + i); -#else - uint32_t driverid = kXdrvList[i]; -#endif - ResponseAppend_P(PSTR("%s%d"), (i) ? "," : "", driverid); - } - ResponseAppend_P(PSTR("\"")); -} - - - -bool XdrvRulesProcess(void) -{ - return XdrvCallDriver(10, FUNC_RULES_PROCESS); -} - -#ifdef USE_DEBUG_DRIVER -void ShowFreeMem(const char *where) -{ - char stemp[30]; - snprintf_P(stemp, sizeof(stemp), where); - XdrvMailbox.data = stemp; - XdrvCall(FUNC_FREE_MEM); -} -#endif - - - - - -bool XdrvCallDriver(uint32_t driver, uint8_t Function) -{ - for (uint32_t x = 0; x < xdrv_present; x++) { -#ifdef XFUNC_PTR_IN_ROM - uint32_t listed = pgm_read_byte(kXdrvList + x); -#else - uint32_t listed = kXdrvList[x]; -#endif - if (driver == listed) { - return xdrv_func_ptr[x](Function); - } - } - return false; -} - - - - - -bool XdrvCall(uint8_t Function) -{ - bool result = false; - - DEBUG_TRACE_LOG(PSTR("DRV: %d"), Function); - - for (uint32_t x = 0; x < xdrv_present; x++) { - result = xdrv_func_ptr[x](Function); - - if (result && ((FUNC_COMMAND == Function) || - (FUNC_COMMAND_DRIVER == Function) || - (FUNC_MQTT_DATA == Function) || - (FUNC_RULES_PROCESS == Function) || - (FUNC_BUTTON_PRESSED == Function) || - (FUNC_SERIAL == Function) || - (FUNC_MODULE_INIT == Function) || - (FUNC_SET_CHANNELS == Function) || - (FUNC_PIN_STATE == Function) || - (FUNC_SET_DEVICE_POWER == Function) - )) { - break; - } - } - - return result; -} -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_01_lcd.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_01_lcd.ino" -#ifdef USE_I2C -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_LCD - -#define XDSP_01 1 -#define XI2C_03 3 - -#define LCD_ADDRESS1 0x27 -#define LCD_ADDRESS2 0x3F - -#include -#include - -LiquidCrystal_I2C *lcd; - - - -void LcdInitMode(void) -{ - lcd->init(); - lcd->clear(); -} - -void LcdInit(uint8_t mode) -{ - switch(mode) { - case DISPLAY_INIT_MODE: - LcdInitMode(); -#ifdef USE_DISPLAY_MODES1TO5 - DisplayClearScreenBuffer(); -#endif - break; - case DISPLAY_INIT_PARTIAL: - case DISPLAY_INIT_FULL: - break; - } -} - -void LcdInitDriver(void) -{ - if (!Settings.display_model) { - if (I2cSetDevice(LCD_ADDRESS1)) { - Settings.display_address[0] = LCD_ADDRESS1; - Settings.display_model = XDSP_01; - } - else if (I2cSetDevice(LCD_ADDRESS2)) { - Settings.display_address[0] = LCD_ADDRESS2; - Settings.display_model = XDSP_01; - } - } - - if (XDSP_01 == Settings.display_model) { - I2cSetActiveFound(Settings.display_address[0], "LCD"); - - Settings.display_width = Settings.display_cols[0]; - Settings.display_height = Settings.display_rows; - lcd = new LiquidCrystal_I2C(Settings.display_address[0], Settings.display_cols[0], Settings.display_rows); - -#ifdef USE_DISPLAY_MODES1TO5 - DisplayAllocScreenBuffer(); -#endif - - LcdInitMode(); - } -} - -void LcdDrawStringAt(void) -{ - if (dsp_flag) { - dsp_x--; - dsp_y--; - } - lcd->setCursor(dsp_x, dsp_y); - lcd->print(dsp_str); -} - -void LcdDisplayOnOff(uint8_t on) -{ - if (on) { - lcd->backlight(); - } else { - lcd->noBacklight(); - } -} - - - -#ifdef USE_DISPLAY_MODES1TO5 - -void LcdCenter(uint8_t row, char* txt) -{ - char line[Settings.display_cols[0] +2]; - - int len = strlen(txt); - int offset = 0; - if (len >= Settings.display_cols[0]) { - len = Settings.display_cols[0]; - } else { - offset = (Settings.display_cols[0] - len) / 2; - } - memset(line, 0x20, Settings.display_cols[0]); - line[Settings.display_cols[0]] = 0; - for (uint32_t i = 0; i < len; i++) { - line[offset +i] = txt[i]; - } - lcd->setCursor(0, row); - lcd->print(line); -} - -bool LcdPrintLog(void) -{ - bool result = false; - - disp_refresh--; - if (!disp_refresh) { - disp_refresh = Settings.display_refresh; - if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } - - char* txt = DisplayLogBuffer('\337'); - if (txt != nullptr) { - uint8_t last_row = Settings.display_rows -1; - - for (uint32_t i = 0; i < last_row; i++) { - strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); - lcd->setCursor(0, i); - lcd->print(disp_screen_buffer[i +1]); - } - strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); - DisplayFillScreen(last_row); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); - - lcd->setCursor(0, last_row); - lcd->print(disp_screen_buffer[last_row]); - - result = true; - } - } - return result; -} - -void LcdTime(void) -{ - char line[Settings.display_cols[0] +1]; - - snprintf_P(line, sizeof(line), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); - LcdCenter(0, line); - snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); - LcdCenter(1, line); -} - -void LcdRefresh(void) -{ - if (Settings.display_mode) { - switch (Settings.display_mode) { - case 1: - LcdTime(); - break; - case 2: - case 4: - LcdPrintLog(); - break; - case 3: - case 5: { - if (!LcdPrintLog()) { LcdTime(); } - break; - } - } - } -} - -#endif - - - - - -bool Xdsp01(uint8_t function) -{ - if (!I2cEnabled(XI2C_03)) { return false; } - - bool result = false; - - if (FUNC_DISPLAY_INIT_DRIVER == function) { - LcdInitDriver(); - } - else if (XDSP_01 == Settings.display_model) { - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; - case FUNC_DISPLAY_INIT: - LcdInit(dsp_init); - break; - case FUNC_DISPLAY_POWER: - LcdDisplayOnOff(disp_power); - break; - case FUNC_DISPLAY_CLEAR: - lcd->clear(); - break; -# 238 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_01_lcd.ino" - case FUNC_DISPLAY_DRAW_STRING: - LcdDrawStringAt(); - break; - case FUNC_DISPLAY_ONOFF: - LcdDisplayOnOff(dsp_on); - break; - - -#ifdef USE_DISPLAY_MODES1TO5 - case FUNC_DISPLAY_EVERY_SECOND: - LcdRefresh(); - break; -#endif - } - } - return result; -} - -#endif -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_02_ssd1306.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_02_ssd1306.ino" -#ifdef USE_I2C -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_SSD1306 - -#define XDSP_02 2 -#define XI2C_04 4 - -#define OLED_RESET 4 - -#define SPRINT(A) char str[32];sprintf(str,"val: %d ",A);Serial.println((char*)str); - -#define OLED_ADDRESS1 0x3C -#define OLED_ADDRESS2 0x3D - -#define OLED_BUFFER_COLS 40 -#define OLED_BUFFER_ROWS 16 - -#define OLED_FONT_WIDTH 6 -#define OLED_FONT_HEIGTH 8 - -#include -#include -#include - -Adafruit_SSD1306 *oled1306; - -extern uint8_t *buffer; - - - -void SSD1306InitDriver(void) -{ - if (!Settings.display_model) { - if (I2cSetDevice(OLED_ADDRESS1)) { - Settings.display_address[0] = OLED_ADDRESS1; - Settings.display_model = XDSP_02; - } - else if (I2cSetDevice(OLED_ADDRESS2)) { - Settings.display_address[0] = OLED_ADDRESS2; - Settings.display_model = XDSP_02; - } - } - - if (XDSP_02 == Settings.display_model) { - I2cSetActiveFound(Settings.display_address[0], "SSD1306"); - - if ((Settings.display_width != 64) && (Settings.display_width != 96) && (Settings.display_width != 128)) { - Settings.display_width = 128; - } - if ((Settings.display_height != 16) && (Settings.display_height != 32) && (Settings.display_height != 48) && (Settings.display_height != 64)) { - Settings.display_height = 64; - } - - uint8_t reset_pin = -1; - if (pin[GPIO_OLED_RESET] < 99) { - reset_pin = pin[GPIO_OLED_RESET]; - } - - - if (buffer) { free(buffer); } - buffer = (unsigned char*)calloc((Settings.display_width * Settings.display_height) / 8,1); - if (!buffer) { return; } - - - - oled1306 = new Adafruit_SSD1306(Settings.display_width, Settings.display_height, &Wire, reset_pin); - oled1306->begin(SSD1306_SWITCHCAPVCC, Settings.display_address[0], reset_pin >= 0); - renderer = oled1306; - renderer->DisplayInit(DISPLAY_INIT_MODE, Settings.display_size, Settings.display_rotate, Settings.display_font); - renderer->setTextColor(1,0); - -#ifdef SHOW_SPLASH - renderer->setTextFont(0); - renderer->setTextSize(2); - renderer->setCursor(20,20); - renderer->println(F("SSD1306")); - renderer->Updateframe(); - renderer->DisplayOnff(1); -#endif - - } -} - - -#ifdef USE_DISPLAY_MODES1TO5 - -void Ssd1306PrintLog(void) -{ - disp_refresh--; - if (!disp_refresh) { - disp_refresh = Settings.display_refresh; - if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } - - char* txt = DisplayLogBuffer('\370'); - if (txt != NULL) { - uint8_t last_row = Settings.display_rows -1; - - renderer->clearDisplay(); - renderer->setTextSize(Settings.display_size); - renderer->setCursor(0,0); - for (byte i = 0; i < last_row; i++) { - strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); - renderer->println(disp_screen_buffer[i]); - } - strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); - DisplayFillScreen(last_row); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); - - renderer->println(disp_screen_buffer[last_row]); - renderer->Updateframe(); - } - } -} - -void Ssd1306Time(void) -{ - char line[12]; - - renderer->clearDisplay(); - renderer->setTextSize(Settings.display_size); - renderer->setTextFont(Settings.display_font); - renderer->setCursor(0, 0); - snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); - renderer->println(line); - snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); - renderer->println(line); - renderer->Updateframe(); -} - -void Ssd1306Refresh(void) -{ - if (!renderer) return; - - if (Settings.display_mode) { - switch (Settings.display_mode) { - case 1: - Ssd1306Time(); - break; - case 2: - case 3: - case 4: - case 5: - Ssd1306PrintLog(); - break; - } - } -} - -#endif - - - - - -bool Xdsp02(byte function) -{ - if (!I2cEnabled(XI2C_04)) { return false; } - - bool result = false; - - if (FUNC_DISPLAY_INIT_DRIVER == function) { - SSD1306InitDriver(); - } - else if (XDSP_02 == Settings.display_model) { - switch (function) { -#ifdef USE_DISPLAY_MODES1TO5 - case FUNC_DISPLAY_EVERY_SECOND: - Ssd1306Refresh(); - break; -#endif - case FUNC_DISPLAY_MODEL: - result = true; - break; - } - } - return result; -} - -#endif -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_03_matrix.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_03_matrix.ino" -#ifdef USE_I2C -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_MATRIX - -#define XDSP_03 3 -#define XI2C_05 5 - -#define MTX_MAX_SCREEN_BUFFER 80 - -#include -#include -#include - -Adafruit_8x8matrix *matrix[8]; -uint8_t mtx_matrices = 0; -uint8_t mtx_state = 0; -uint8_t mtx_counter = 0; -int16_t mtx_x = 0; -int16_t mtx_y = 0; - - -char *mtx_buffer = nullptr; - -uint8_t mtx_mode = 0; -uint8_t mtx_loop = 0; -uint8_t mtx_done = 0; - - - -void MatrixWrite(void) -{ - for (uint32_t i = 0; i < mtx_matrices; i++) { - matrix[i]->writeDisplay(); - } -} - -void MatrixClear(void) -{ - for (uint32_t i = 0; i < mtx_matrices; i++) { - matrix[i]->clear(); - } - MatrixWrite(); -} - -void MatrixFixed(char* txt) -{ - for (uint32_t i = 0; i < mtx_matrices; i++) { - matrix[i]->clear(); - matrix[i]->setCursor(-i *8, 0); - matrix[i]->print(txt); - matrix[i]->setBrightness(Settings.display_dimmer); - } - MatrixWrite(); -} - -void MatrixCenter(char* txt) -{ - int offset; - - int len = strlen(txt); - offset = (len < 8) ? offset = ((mtx_matrices *8) - (len *6)) / 2 : 0; - for (uint32_t i = 0; i < mtx_matrices; i++) { - matrix[i]->clear(); - matrix[i]->setCursor(-(i *8)+offset, 0); - matrix[i]->print(txt); - matrix[i]->setBrightness(Settings.display_dimmer); - } - MatrixWrite(); -} - -void MatrixScrollLeft(char* txt, int loop) -{ - switch (mtx_state) { - case 1: - mtx_state = 2; - - mtx_x = 8 * mtx_matrices; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), txt); - - disp_refresh = Settings.display_refresh; - case 2: - disp_refresh--; - if (!disp_refresh) { - disp_refresh = Settings.display_refresh; - for (uint32_t i = 0; i < mtx_matrices; i++) { - matrix[i]->clear(); - matrix[i]->setCursor(mtx_x - i *8, 0); - matrix[i]->print(txt); - matrix[i]->setBrightness(Settings.display_dimmer); - } - MatrixWrite(); - - mtx_x--; - int16_t len = strlen(txt); - if (mtx_x < -(len *6)) { mtx_state = loop; } - } - break; - } -} - -void MatrixScrollUp(char* txt, int loop) -{ - int wordcounter = 0; - char tmpbuf[200]; - char *words[100]; - - - - char separators[] = " /"; - - switch (mtx_state) { - case 1: - mtx_state = 2; - - mtx_y = 8; - mtx_counter = 0; - disp_refresh = Settings.display_refresh; - case 2: - disp_refresh--; - if (!disp_refresh) { - disp_refresh = Settings.display_refresh; - strlcpy(tmpbuf, txt, sizeof(tmpbuf)); - char *p = strtok(tmpbuf, separators); - while (p != nullptr && wordcounter < 40) { - words[wordcounter++] = p; - p = strtok(nullptr, separators); - } - for (uint32_t i = 0; i < mtx_matrices; i++) { - matrix[i]->clear(); - for (uint32_t j = 0; j < wordcounter; j++) { - matrix[i]->setCursor(-i *8, mtx_y + (j *8)); - matrix[i]->println(words[j]); - } - matrix[i]->setBrightness(Settings.display_dimmer); - } - MatrixWrite(); - if (((mtx_y %8) == 0) && mtx_counter) { - mtx_counter--; - } else { - mtx_y--; - mtx_counter = STATES * 1; - } - if (mtx_y < -(wordcounter *8)) { mtx_state = loop; } - } - break; - } -} - - - -void MatrixInitMode(void) -{ - for (uint32_t i = 0; i < mtx_matrices; i++) { - matrix[i]->setRotation(Settings.display_rotate); - matrix[i]->setBrightness(Settings.display_dimmer); - matrix[i]->blinkRate(0); - matrix[i]->setTextWrap(false); - - - matrix[i]->cp437(true); - } - MatrixClear(); -} - -void MatrixInit(uint8_t mode) -{ - switch(mode) { - case DISPLAY_INIT_MODE: - MatrixInitMode(); - break; - case DISPLAY_INIT_PARTIAL: - case DISPLAY_INIT_FULL: - break; - } -} - -void MatrixInitDriver(void) -{ - mtx_buffer = (char*)(malloc(MTX_MAX_SCREEN_BUFFER)); - if (mtx_buffer != nullptr) { - if (!Settings.display_model) { - if (I2cSetDevice(Settings.display_address[1])) { - Settings.display_model = XDSP_03; - } - } - - if (XDSP_03 == Settings.display_model) { - mtx_state = 1; - for (mtx_matrices = 0; mtx_matrices < 8; mtx_matrices++) { - if (Settings.display_address[mtx_matrices]) { - I2cSetActiveFound(Settings.display_address[mtx_matrices], "8x8Matrix"); - matrix[mtx_matrices] = new Adafruit_8x8matrix(); - matrix[mtx_matrices]->begin(Settings.display_address[mtx_matrices]); - } else { - break; - } - } - - Settings.display_width = mtx_matrices * 8; - Settings.display_height = 8; - - MatrixInitMode(); - } - } -} - -void MatrixOnOff(void) -{ - if (!disp_power) { MatrixClear(); } -} - -void MatrixDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) -{ - strlcpy(mtx_buffer, str, MTX_MAX_SCREEN_BUFFER); - mtx_mode = x &1; - mtx_loop = y &1; - if (!mtx_state) { mtx_state = 1; } -} - - - -#ifdef USE_DISPLAY_MODES1TO5 - -void MatrixPrintLog(uint8_t direction) -{ - char* txt = (!mtx_done) ? DisplayLogBuffer('\370') : mtx_buffer; - if (txt != nullptr) { - if (!mtx_state) { mtx_state = 1; } - - if (!mtx_done) { - - uint8_t space = 0; - uint8_t max_cols = (disp_log_buffer_cols < MTX_MAX_SCREEN_BUFFER) ? disp_log_buffer_cols : MTX_MAX_SCREEN_BUFFER; - mtx_buffer[0] = '\0'; - uint8_t i = 0; - while ((txt[i] != '\0') && (i < max_cols)) { - if (txt[i] == ' ') { - space++; - } else { - space = 0; - } - if (space < 2) { - strncat(mtx_buffer, (const char*)txt +i, (strlen(mtx_buffer) < MTX_MAX_SCREEN_BUFFER -1) ? 1 : 0); - } - i++; - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), mtx_buffer); - - mtx_done = 1; - } - - if (direction) { - MatrixScrollUp(mtx_buffer, 0); - } else { - MatrixScrollLeft(mtx_buffer, 0); - } - if (!mtx_state) { mtx_done = 0; } - } else { - char disp_time[9]; - - snprintf_P(disp_time, sizeof(disp_time), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); - MatrixFixed(disp_time); - } -} - -#endif - -void MatrixRefresh(void) -{ - if (disp_power) { - switch (Settings.display_mode) { - case 0: { - switch (mtx_mode) { - case 0: - MatrixScrollLeft(mtx_buffer, mtx_loop); - break; - case 1: - MatrixScrollUp(mtx_buffer, mtx_loop); - break; - } - break; - } -#ifdef USE_DISPLAY_MODES1TO5 - case 2: { - char disp_date[9]; - snprintf_P(disp_date, sizeof(disp_date), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%02d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year -2000); - MatrixFixed(disp_date); - break; - } - case 3: { - char disp_day[10]; - snprintf_P(disp_day, sizeof(disp_day), PSTR("%d %s"), RtcTime.day_of_month, RtcTime.name_of_month); - MatrixCenter(disp_day); - break; - } - case 4: - MatrixPrintLog(0); - break; - case 1: - case 5: - MatrixPrintLog(1); - break; -#endif - } - } -} - - - - - -bool Xdsp03(uint8_t function) -{ - if (!I2cEnabled(XI2C_05)) { return false; } - - bool result = false; - - if (FUNC_DISPLAY_INIT_DRIVER == function) { - MatrixInitDriver(); - } - else if (XDSP_03 == Settings.display_model) { - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; - case FUNC_DISPLAY_INIT: - MatrixInit(dsp_init); - break; - case FUNC_DISPLAY_EVERY_50_MSECOND: - MatrixRefresh(); - break; - case FUNC_DISPLAY_POWER: - MatrixOnOff(); - break; - case FUNC_DISPLAY_DRAW_STRING: - MatrixDrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag); - break; - } - } - return result; -} - -#endif -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_04_ili9341.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_04_ili9341.ino" -#ifdef USE_SPI -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_ILI9341 - -#define XDSP_04 4 - -#define TFT_TOP 16 -#define TFT_BOTTOM 16 -#define TFT_FONT_WIDTH 6 -#define TFT_FONT_HEIGTH 8 - -#include -#include -#include - -Adafruit_ILI9341 *tft; - -uint16_t tft_scroll; - - - -void Ili9341InitMode(void) -{ - tft->setRotation(Settings.display_rotate); - tft->invertDisplay(0); - tft->fillScreen(ILI9341_BLACK); - tft->setTextWrap(false); - tft->cp437(true); - if (!Settings.display_mode) { - tft->setCursor(0, 0); - tft->setTextColor(ILI9341_WHITE, ILI9341_BLACK); - tft->setTextSize(1); - } else { - tft->setScrollMargins(TFT_TOP, TFT_BOTTOM); - tft->setCursor(0, 0); - tft->setTextColor(ILI9341_YELLOW, ILI9341_BLACK); - tft->setTextSize(2); - - - tft_scroll = TFT_TOP; - } -} - -void Ili9341Init(uint8_t mode) -{ - switch(mode) { - case DISPLAY_INIT_MODE: - Ili9341InitMode(); -#ifdef USE_DISPLAY_MODES1TO5 - if (Settings.display_rotate) { - DisplayClearScreenBuffer(); - } -#endif - break; - case DISPLAY_INIT_PARTIAL: - case DISPLAY_INIT_FULL: - break; - } -} - -void Ili9341InitDriver(void) -{ - if (!Settings.display_model) { - Settings.display_model = XDSP_04; - } - - if (XDSP_04 == Settings.display_model) { - if (Settings.display_width != ILI9341_TFTWIDTH) { - Settings.display_width = ILI9341_TFTWIDTH; - } - if (Settings.display_height != ILI9341_TFTHEIGHT) { - Settings.display_height = ILI9341_TFTHEIGHT; - } - tft = new Adafruit_ILI9341(pin[GPIO_SPI_CS], pin[GPIO_SPI_DC]); - tft->begin(); - -#ifdef USE_DISPLAY_MODES1TO5 - if (Settings.display_rotate) { - DisplayAllocScreenBuffer(); - } -#endif - - Ili9341InitMode(); - } -} - -void Ili9341Clear(void) -{ - tft->fillScreen(ILI9341_BLACK); - tft->setCursor(0, 0); -} - -void Ili9341DrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) -{ - uint16_t active_color = ILI9341_WHITE; - - tft->setTextSize(Settings.display_size); - if (!flag) { - tft->setCursor(x, y); - } else { - tft->setCursor((x-1) * TFT_FONT_WIDTH * Settings.display_size, (y-1) * TFT_FONT_HEIGTH * Settings.display_size); - } - if (color) { active_color = color; } - tft->setTextColor(active_color, ILI9341_BLACK); - tft->println(str); -} - -void Ili9341DisplayOnOff(uint8_t on) -{ - - - if (pin[GPIO_BACKLIGHT] < 99) { - pinMode(pin[GPIO_BACKLIGHT], OUTPUT); - digitalWrite(pin[GPIO_BACKLIGHT], on); - } -} - -void Ili9341OnOff(void) -{ - Ili9341DisplayOnOff(disp_power); -} - - - -#ifdef USE_DISPLAY_MODES1TO5 - -void Ili9341PrintLog(void) -{ - disp_refresh--; - if (!disp_refresh) { - disp_refresh = Settings.display_refresh; - if (Settings.display_rotate) { - if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } - } - - char* txt = DisplayLogBuffer('\370'); - if (txt != nullptr) { - uint8_t size = Settings.display_size; - uint16_t theight = size * TFT_FONT_HEIGTH; - - tft->setTextSize(size); - tft->setTextColor(ILI9341_CYAN, ILI9341_BLACK); - if (!Settings.display_rotate) { - tft->setCursor(0, tft_scroll); - tft->fillRect(0, tft_scroll, tft->width(), theight, ILI9341_BLACK); - tft->print(txt); - tft_scroll += theight; - if (tft_scroll >= (tft->height() - TFT_BOTTOM)) { - tft_scroll = TFT_TOP; - } - tft->scrollTo(tft_scroll); - } else { - uint8_t last_row = Settings.display_rows -1; - - tft_scroll = theight; - tft->setCursor(0, tft_scroll); - for (uint32_t i = 0; i < last_row; i++) { - strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); - - tft->print(disp_screen_buffer[i]); - tft_scroll += theight; - tft->setCursor(0, tft_scroll); - delay(1); - } - strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); - DisplayFillScreen(last_row); - tft->print(disp_screen_buffer[last_row]); - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), txt); - } - } -} - -void Ili9341Refresh(void) -{ - if (Settings.display_mode) { - char tftdt[Settings.display_cols[0] +1]; - char date4[11]; - char space[Settings.display_cols[0] - 17]; - char time[9]; - - tft->setTextSize(2); - tft->setTextColor(ILI9341_YELLOW, ILI9341_RED); - tft->setCursor(0, 0); - - snprintf_P(date4, sizeof(date4), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); - memset(space, 0x20, sizeof(space)); - space[sizeof(space) -1] = '\0'; - snprintf_P(time, sizeof(time), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); - snprintf_P(tftdt, sizeof(tftdt), PSTR("%s%s%s"), date4, space, time); - - tft->print(tftdt); - - switch (Settings.display_mode) { - case 1: - case 2: - case 3: - case 4: - case 5: - Ili9341PrintLog(); - break; - } - } -} - -#endif - - - - - -bool Xdsp04(uint8_t function) -{ - bool result = false; - - if (spi_flg) { - if (FUNC_DISPLAY_INIT_DRIVER == function) { - Ili9341InitDriver(); - } - else if (XDSP_04 == Settings.display_model) { - - if (!dsp_color) { dsp_color = ILI9341_WHITE; } - - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; - case FUNC_DISPLAY_INIT: - Ili9341Init(dsp_init); - break; - case FUNC_DISPLAY_POWER: - Ili9341OnOff(); - break; - case FUNC_DISPLAY_CLEAR: - Ili9341Clear(); - break; - case FUNC_DISPLAY_DRAW_HLINE: - tft->writeFastHLine(dsp_x, dsp_y, dsp_len, dsp_color); - break; - case FUNC_DISPLAY_DRAW_VLINE: - tft->writeFastVLine(dsp_x, dsp_y, dsp_len, dsp_color); - break; - case FUNC_DISPLAY_DRAW_LINE: - tft->writeLine(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color); - break; - case FUNC_DISPLAY_DRAW_CIRCLE: - tft->drawCircle(dsp_x, dsp_y, dsp_rad, dsp_color); - break; - case FUNC_DISPLAY_FILL_CIRCLE: - tft->fillCircle(dsp_x, dsp_y, dsp_rad, dsp_color); - break; - case FUNC_DISPLAY_DRAW_RECTANGLE: - tft->drawRect(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color); - break; - case FUNC_DISPLAY_FILL_RECTANGLE: - tft->fillRect(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color); - break; - - - - case FUNC_DISPLAY_TEXT_SIZE: - tft->setTextSize(Settings.display_size); - break; - case FUNC_DISPLAY_FONT_SIZE: - - break; - case FUNC_DISPLAY_DRAW_STRING: - Ili9341DrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag); - break; - case FUNC_DISPLAY_ONOFF: - Ili9341DisplayOnOff(dsp_on); - break; - case FUNC_DISPLAY_ROTATION: - tft->setRotation(Settings.display_rotate); - break; -#ifdef USE_DISPLAY_MODES1TO5 - case FUNC_DISPLAY_EVERY_SECOND: - Ili9341Refresh(); - break; -#endif - } - } - } - return result; -} - -#endif -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_05_epaper_29.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_05_epaper_29.ino" -#ifdef USE_SPI -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_EPAPER_29 - -#define XDSP_05 5 - -#define EPD_TOP 12 -#define EPD_FONT_HEIGTH 12 - -#define COLORED 1 -#define UNCOLORED 0 - - - -#define USE_TINY_FONT - -#include -#include - - -extern uint8_t *buffer; -uint16_t epd_scroll; - -Epd *epd; - - - -void EpdInitDriver29() -{ - if (!Settings.display_model) { - Settings.display_model = XDSP_05; - } - - if (XDSP_05 == Settings.display_model) { - if (Settings.display_width != EPD_WIDTH) { - Settings.display_width = EPD_WIDTH; - } - if (Settings.display_height != EPD_HEIGHT) { - Settings.display_height = EPD_HEIGHT; - } - - - if (buffer) free(buffer); - buffer=(unsigned char*)calloc((EPD_WIDTH * EPD_HEIGHT) / 8,1); - if (!buffer) return; - - - epd = new Epd(EPD_WIDTH,EPD_HEIGHT); - - - if ((pin[GPIO_SPI_CS] < 99) && (pin[GPIO_SPI_CLK] < 99) && (pin[GPIO_SPI_MOSI] < 99)) { - epd->Begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: HardSPI CS %d, CLK %d, MOSI %d"),pin[GPIO_SPI_CS], pin[GPIO_SPI_CLK], pin[GPIO_SPI_MOSI]); - } - else if ((pin[GPIO_SSPI_CS] < 99) && (pin[GPIO_SSPI_SCLK] < 99) && (pin[GPIO_SSPI_MOSI] < 99)) { - epd->Begin(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: SoftSPI CS %d, CLK %d, MOSI %d"),pin[GPIO_SSPI_CS], pin[GPIO_SSPI_SCLK], pin[GPIO_SSPI_MOSI]); - } else { - free(buffer); - return; - } - - renderer = epd; - epd->Init(DISPLAY_INIT_FULL); - epd->Init(DISPLAY_INIT_PARTIAL); - renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); - - renderer->setTextColor(1,0); - -#ifdef SHOW_SPLASH - - renderer->setTextFont(1); - renderer->DrawStringAt(50, 50, "Waveshare E-Paper Display!", COLORED,0); - renderer->Updateframe(); - delay(1000); - renderer->fillScreen(0); -#endif - - } -} - - - - - - - -#ifdef USE_DISPLAY_MODES1TO5 -#define EPD_FONT_HEIGTH 12 -void EpdPrintLog29(void) -{ - - disp_refresh--; - if (!disp_refresh) { - disp_refresh = Settings.display_refresh; - - if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } - - - char* txt = DisplayLogBuffer('\040'); - if (txt != nullptr) { - uint8_t size = Settings.display_size; - uint16_t theight = size * EPD_FONT_HEIGTH; - - renderer->setTextFont(size); - uint8_t last_row = Settings.display_rows -1; - - - epd_scroll = 0; - for (uint32_t i = 0; i < last_row; i++) { - strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); - renderer->DrawStringAt(0, epd_scroll, disp_screen_buffer[i], COLORED, 0); - epd_scroll += theight; - } - strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); - DisplayFillScreen(last_row); - renderer->DrawStringAt(0, epd_scroll, disp_screen_buffer[last_row], COLORED, 0); - - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), txt); - } - } -} - -void EpdRefresh29(void) -{ - if (Settings.display_mode) { - - if (!renderer) return; -# 165 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_05_epaper_29.ino" - switch (Settings.display_mode) { - case 1: - case 2: - case 3: - case 4: - case 5: - EpdPrintLog29(); - renderer->Updateframe(); - break; - } - - - } -} - -#endif - - - - - -bool Xdsp05(uint8_t function) -{ - bool result = false; - if (FUNC_DISPLAY_INIT_DRIVER == function) { - EpdInitDriver29(); - } - else if (XDSP_05 == Settings.display_model) { - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; -#ifdef USE_DISPLAY_MODES1TO5 - case FUNC_DISPLAY_EVERY_SECOND: - EpdRefresh29(); - break; -#endif - } - } - return result; -} - -#endif -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_06_epaper_42.ino" -# 21 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_06_epaper_42.ino" -#ifdef USE_SPI -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_EPAPER_42 - -#define XDSP_06 6 - -#define COLORED42 1 -#define UNCOLORED42 0 - - - -#define USE_TINY_FONT - -#include -#include - -extern uint8_t *buffer; - -Epd42 *epd42; - - - - -void EpdInitDriver42() -{ - if (!Settings.display_model) { - Settings.display_model = XDSP_06; - } - - if (XDSP_06 == Settings.display_model) { - - if (Settings.display_width != EPD_WIDTH42) { - Settings.display_width = EPD_WIDTH42; - } - if (Settings.display_height != EPD_HEIGHT42) { - Settings.display_height = EPD_HEIGHT42; - } - - - if (buffer) free(buffer); - buffer=(unsigned char*)calloc((EPD_WIDTH42 * EPD_HEIGHT42) / 8,1); - if (!buffer) return; - - - epd42 = new Epd42(EPD_WIDTH42,EPD_HEIGHT42); - - #ifdef USE_SPI - if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)) { - epd42->Begin(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); - } else { - free(buffer); - return; - } - #else - if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)) { - epd42->Begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); - } else { - free(buffer); - return; - } - #endif - - renderer = epd42; - - epd42->Init(); - - renderer->fillScreen(0); - - - epd42->Init(DISPLAY_INIT_FULL); - - renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); - - epd42->ClearFrame(); - renderer->Updateframe(); - delay(3000); - renderer->setTextColor(1,0); - -#ifdef SHOW_SPLASH - - renderer->setTextFont(2); - renderer->DrawStringAt(50, 140, "Waveshare E-Paper!", COLORED42,0); - renderer->Updateframe(); - delay(350); - renderer->fillScreen(0); -#endif - - } -} - - - - - - - -#ifdef USE_DISPLAY_MODES1TO5 - -void EpdRefresh42() -{ - if (Settings.display_mode) { - - } -} - -#endif - - - - - - -bool Xdsp06(uint8_t function) -{ - bool result = false; - - if (FUNC_DISPLAY_INIT_DRIVER == function) { - EpdInitDriver42(); - } - else if (XDSP_06 == Settings.display_model) { - - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; - -#ifdef USE_DISPLAY_MODES1TO5 - case FUNC_DISPLAY_EVERY_SECOND: - EpdRefresh42(); - break; -#endif - } - } - return result; -} - - -#endif -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_07_sh1106.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_07_sh1106.ino" -#ifdef USE_I2C -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_SH1106 - -#define OLED_RESET 4 - -#define SPRINT(A) char str[32];sprintf(str,"val: %d ",A);Serial.println((char*)str); - -extern uint8_t *buffer; - -#define XDSP_07 7 -#define XI2C_06 6 - -#define OLED_ADDRESS1 0x3C -#define OLED_ADDRESS2 0x3D - -#define OLED_BUFFER_COLS 40 -#define OLED_BUFFER_ROWS 16 - -#define OLED_FONT_WIDTH 6 -#define OLED_FONT_HEIGTH 8 - -#include -#include -#include - -Adafruit_SH1106 *oled1106; - - - - -void SH1106InitDriver() -{ - if (!Settings.display_model) { - if (I2cSetDevice(OLED_ADDRESS1)) { - Settings.display_address[0] = OLED_ADDRESS1; - Settings.display_model = XDSP_07; - } - else if (I2cSetDevice(OLED_ADDRESS2)) { - Settings.display_address[0] = OLED_ADDRESS2; - Settings.display_model = XDSP_07; - } - } - - if (XDSP_07 == Settings.display_model) { - I2cSetActiveFound(Settings.display_address[0], "SH1106"); - - if (Settings.display_width != SH1106_LCDWIDTH) { - Settings.display_width = SH1106_LCDWIDTH; - } - if (Settings.display_height != SH1106_LCDHEIGHT) { - Settings.display_height = SH1106_LCDHEIGHT; - } - - - if (buffer) free(buffer); - buffer=(unsigned char*)calloc((SH1106_LCDWIDTH * SH1106_LCDHEIGHT) / 8,1); - if (!buffer) return; - - - oled1106 = new Adafruit_SH1106(SH1106_LCDWIDTH,SH1106_LCDHEIGHT); - renderer=oled1106; - renderer->Begin(SH1106_SWITCHCAPVCC, Settings.display_address[0],0); - renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); - renderer->setTextColor(1,0); - -#ifdef SHOW_SPLASH - renderer->setTextFont(0); - renderer->setTextSize(2); - renderer->setCursor(20,20); - renderer->println(F("SH1106")); - renderer->Updateframe(); - renderer->DisplayOnff(1); -#endif - } -} - - - -#ifdef USE_DISPLAY_MODES1TO5 - -void SH1106PrintLog(void) -{ - disp_refresh--; - if (!disp_refresh) { - disp_refresh = Settings.display_refresh; - if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } - - char* txt = DisplayLogBuffer('\370'); - if (txt != NULL) { - uint8_t last_row = Settings.display_rows -1; - - renderer->clearDisplay(); - renderer->setTextSize(Settings.display_size); - renderer->setCursor(0,0); - for (byte i = 0; i < last_row; i++) { - strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); - renderer->println(disp_screen_buffer[i]); - } - strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); - DisplayFillScreen(last_row); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); - - renderer->println(disp_screen_buffer[last_row]); - renderer->Updateframe(); - } - } -} - -void SH1106Time(void) -{ - char line[12]; - - renderer->clearDisplay(); - renderer->setTextSize(Settings.display_size); - renderer->setTextFont(Settings.display_font); - renderer->setCursor(0, 0); - snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); - renderer->println(line); - snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); - renderer->println(line); - renderer->Updateframe(); -} - -void SH1106Refresh(void) -{ - if (!renderer) return; - if (Settings.display_mode) { - switch (Settings.display_mode) { - case 1: - SH1106Time(); - break; - case 2: - case 3: - case 4: - case 5: - SH1106PrintLog(); - break; - } - } -} - -#endif - - - - - -bool Xdsp07(uint8_t function) -{ - if (!I2cEnabled(XI2C_06)) { return false; } - - bool result = false; - - if (FUNC_DISPLAY_INIT_DRIVER == function) { - SH1106InitDriver(); - } - else if (XDSP_07 == Settings.display_model) { - - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; -#ifdef USE_DISPLAY_MODES1TO5 - case FUNC_DISPLAY_EVERY_SECOND: - SH1106Refresh(); - break; -#endif - } - } - return result; -} - -#endif -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_08_ILI9488.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_08_ILI9488.ino" -#ifdef USE_SPI -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_ILI9488 - -#define XDSP_08 8 -#define XI2C_38 38 - -#define COLORED 1 -#define UNCOLORED 0 - - -#define FT6236_address 0x38 - - - -#define USE_TINY_FONT - - -#include -#include - -TouchLocation ili9488_pLoc; -uint8_t ili9488_ctouch_counter = 0; - - -#define BACKPLANE_PIN 2 - -extern uint8_t *buffer; -extern uint8_t color_type; -ILI9488 *ili9488; - -#ifdef USE_TOUCH_BUTTONS -extern VButton *buttons[]; -#endif - -extern const uint16_t picture[]; -uint8_t FT6236_found; - - - -void ILI9488_InitDriver() -{ - if (!Settings.display_model) { - Settings.display_model = XDSP_08; - } - - if (XDSP_08 == Settings.display_model) { - - if (Settings.display_width != ILI9488_TFTWIDTH) { - Settings.display_width = ILI9488_TFTWIDTH; - } - if (Settings.display_height != ILI9488_TFTHEIGHT) { - Settings.display_height = ILI9488_TFTHEIGHT; - } - - - buffer=NULL; - - - fg_color = ILI9488_WHITE; - bg_color = ILI9488_BLACK; - - uint8_t bppin=BACKPLANE_PIN; - if (pin[GPIO_BACKLIGHT]<99) { - bppin=pin[GPIO_BACKLIGHT]; - } - - - if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)){ - ili9488 = new ILI9488(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK],bppin); - } else { - if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)) { - ili9488 = new ILI9488(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK],bppin); - } else { - return; - } - } - - SPI.begin(); - ili9488->begin(); - renderer = ili9488; - renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); - -#ifdef SHOW_SPLASH - - renderer->setTextFont(2); - renderer->setTextColor(ILI9488_WHITE,ILI9488_BLACK); - renderer->DrawStringAt(50, 50, "ILI9488 TFT Display!", ILI9488_WHITE,0); - delay(1000); - - -#endif - - color_type = COLOR_COLOR; - - - if (I2cEnabled(XI2C_38) && I2cSetDevice(FT6236_address)) { - FT6236begin(FT6236_address); - FT6236_found=1; - I2cSetActiveFound(FT6236_address, "FT6236"); - } else { - FT6236_found=0; - } - - } -} - -#ifdef USE_TOUCH_BUTTONS -void ILI9488_MQTT(uint8_t count,const char *cp) { - ResponseTime_P(PSTR(",\"RA8876\":{\"%s%d\":\"%d\"}}"), cp,count+1,(buttons[count]->vpower&0x80)>>7); - MqttPublishTeleSensor(); -} - -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; -} - -void FT6236Check() { -uint16_t temp; -uint8_t rbutt=0,vbutt=0; -ili9488_ctouch_counter++; -if (2 == ili9488_ctouch_counter) { - - ili9488_ctouch_counter=0; - if (FT6236readTouchLocation(&ili9488_pLoc,1)) { - - if (renderer) { - uint8_t rot=renderer->getRotation(); - switch (rot) { - case 0: - temp=ili9488_pLoc.y; - ili9488_pLoc.y=renderer->height()-ili9488_pLoc.x; - ili9488_pLoc.x=temp; - break; - case 1: - break; - case 2: - break; - case 3: - temp=ili9488_pLoc.y; - ili9488_pLoc.y=ili9488_pLoc.x; - ili9488_pLoc.x=renderer->width()-temp; - break; - } - - for (uint8_t count=0; countvpower&0x7f; - if (buttons[count]->contains(ili9488_pLoc.x,ili9488_pLoc.y)) { - - buttons[count]->press(true); - if (buttons[count]->justPressed()) { - if (!bflags) { - 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); - } - } else { - - const char *cp; - if (bflags==1) { - - buttons[count]->vpower^=0x80; - cp="TBT"; - } else { - - buttons[count]->vpower|=0x80; - cp="PBT"; - } - buttons[count]->xdrawButton(buttons[count]->vpower&0x80); - ILI9488_MQTT(count,cp); - } - } - } - if (!bflags) { - rbutt++; - } else { - vbutt++; - } - } - } - } - } else { - - for (uint8_t count=0; countvpower&0x7f; - buttons[count]->press(false); - if (buttons[count]->justReleased()) { - uint8_t bflags=buttons[count]->vpower&0x7f; - if (bflags>0) { - if (bflags>1) { - - buttons[count]->vpower&=0x7f; - ILI9488_MQTT(count,"PBT"); - } - buttons[count]->xdrawButton(buttons[count]->vpower&0x80); - } - } - if (!bflags) { - - uint8_t pwr=bitRead(power,rbutt); - uint8_t vpwr=(buttons[count]->vpower&0x80)>>7; - if (pwr!=vpwr) { - ILI9488_RDW_BUTT(count,pwr); - } - rbutt++; - } - } - } - ili9488_pLoc.x=0; - ili9488_pLoc.y=0; - } -} -} -#endif - - - - -bool Xdsp08(uint8_t function) -{ - bool result = false; - - if (FUNC_DISPLAY_INIT_DRIVER == function) { - ILI9488_InitDriver(); - } - else if (XDSP_08 == Settings.display_model) { - - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; - case FUNC_DISPLAY_EVERY_50_MSECOND: -#ifdef USE_TOUCH_BUTTONS - if (FT6236_found) FT6236Check(); -#endif - break; - } - } - - return result; -} - -#endif -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_09_SSD1351.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_09_SSD1351.ino" -#ifdef USE_SPI -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_SSD1351 - -#define XDSP_09 9 - -#define COLORED 1 -#define UNCOLORED 0 - - - - -#define USE_TINY_FONT - -#include - -extern uint8_t *buffer; -extern uint8_t color_type; -SSD1351 *ssd1351; - - - -void SSD1351_InitDriver() { - if (!Settings.display_model) { - Settings.display_model = XDSP_09; - } - - if (XDSP_09 == Settings.display_model) { - - if (Settings.display_width != SSD1351_WIDTH) { - Settings.display_width = SSD1351_WIDTH; - } - if (Settings.display_height != SSD1351_HEIGHT) { - Settings.display_height = SSD1351_HEIGHT; - } - - buffer=0; - - - fg_color = SSD1351_WHITE; - bg_color = SSD1351_BLACK; - - - if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)){ - ssd1351 = new SSD1351(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); - } else { - if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)){ - ssd1351 = new SSD1351(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); - } else { - return; - } - } - - delay(100); - SPI.begin(); - ssd1351->begin(); - renderer = ssd1351; - renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); - renderer->dim(Settings.display_dimmer); - -#ifdef SHOW_SPLASH - - renderer->setTextFont(2); - renderer->setTextColor(SSD1351_WHITE,SSD1351_BLACK); - renderer->DrawStringAt(10, 60, "SSD1351", SSD1351_RED,0); - delay(1000); - -#endif - color_type = COLOR_COLOR; - } -} - -#ifdef USE_DISPLAY_MODES1TO5 - -void SSD1351PrintLog(void) -{ - disp_refresh--; - if (!disp_refresh) { - disp_refresh = Settings.display_refresh; - if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } - - char* txt = DisplayLogBuffer('\370'); - if (txt != NULL) { - uint8_t last_row = Settings.display_rows -1; - - renderer->clearDisplay(); - renderer->setTextSize(Settings.display_size); - renderer->setCursor(0,0); - for (byte i = 0; i < last_row; i++) { - strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); - renderer->println(disp_screen_buffer[i]); - } - strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); - DisplayFillScreen(last_row); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); - - renderer->println(disp_screen_buffer[last_row]); - renderer->Updateframe(); - } - } -} - -void SSD1351Time(void) -{ - char line[12]; - - renderer->clearDisplay(); - renderer->setTextSize(2); - renderer->setCursor(0, 0); - snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); - renderer->println(line); - snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); - renderer->println(line); - renderer->Updateframe(); -} - -void SSD1351Refresh(void) -{ - if (Settings.display_mode) { - switch (Settings.display_mode) { - case 1: - SSD1351Time(); - break; - case 2: - case 3: - case 4: - case 5: - SSD1351PrintLog(); - break; - } - } -} - -#endif - - - - -bool Xdsp09(uint8_t function) -{ - bool result = false; - - if (FUNC_DISPLAY_INIT_DRIVER == function) { - SSD1351_InitDriver(); - } - else if (XDSP_09 == Settings.display_model) { - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; -#ifdef USE_DISPLAY_MODES1TO5 - case FUNC_DISPLAY_EVERY_SECOND: - SSD1351Refresh(); - break; -#endif - } - } - return result; -} -#endif -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_10_RA8876.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_10_RA8876.ino" -#ifdef USE_SPI -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_RA8876 - -#define XDSP_10 10 -#define XI2C_39 39 - -#define COLORED 1 -#define UNCOLORED 0 - - -#define FT5316_address 0x38 - - - -#define USE_TINY_FONT - -#include -#include - -TouchLocation ra8876_pLoc; -uint8_t ra8876_ctouch_counter = 0; - -#ifdef USE_TOUCH_BUTTONS -extern VButton *buttons[]; -#endif - -extern uint8_t *buffer; -extern uint8_t color_type; -RA8876 *ra8876; - -uint8_t FT5316_found; - - -void RA8876_InitDriver() -{ - if (!Settings.display_model) { - Settings.display_model = XDSP_10; - } - - if (XDSP_10 == Settings.display_model) { - - if (Settings.display_width != RA8876_TFTWIDTH) { - Settings.display_width = RA8876_TFTWIDTH; - } - if (Settings.display_height != RA8876_TFTHEIGHT) { - Settings.display_height = RA8876_TFTHEIGHT; - } - buffer=0; - - - fg_color = RA8876_WHITE; - bg_color = RA8876_BLACK; - - - if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]==13) && (pin[GPIO_SSPI_MISO]==12) && (pin[GPIO_SSPI_SCLK]==14)) { - ra8876 = new RA8876(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_MISO],pin[GPIO_SSPI_SCLK],pin[GPIO_BACKLIGHT]); - } else { - if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]==13) && (pin[GPIO_SPI_MISO]==12) && (pin[GPIO_SPI_CLK]==14)) { - ra8876 = new RA8876(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_MISO],pin[GPIO_SPI_CLK],pin[GPIO_BACKLIGHT]); - } else { - return; - } - } - - ra8876->begin(); - renderer = ra8876; - renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); - renderer->dim(Settings.display_dimmer); - - -#ifdef SHOW_SPLASH - - renderer->setTextFont(2); - renderer->setTextColor(RA8876_WHITE,RA8876_BLACK); - renderer->DrawStringAt(600, 300, "RA8876", RA8876_RED,0); - delay(1000); - -#endif - color_type = COLOR_COLOR; - - if (I2cEnabled(XI2C_39) && I2cSetDevice(FT5316_address)) { - FT6236begin(FT5316_address); - FT5316_found=1; - I2cSetActiveFound(FT5316_address, "FT5316"); - } else { - FT5316_found=0; - } - - } -} - -#ifdef USE_TOUCH_BUTTONS -void RA8876_MQTT(uint8_t count,const char *cp) { - ResponseTime_P(PSTR(",\"RA8876\":{\"%s%d\":\"%d\"}}"), cp,count+1,(buttons[count]->vpower&0x80)>>7); - MqttPublishTeleSensor(); -} - -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; -} - - -void FT5316Check() { -uint16_t temp; -uint8_t rbutt=0,vbutt=0; -ra8876_ctouch_counter++; -if (2 == ra8876_ctouch_counter) { - - ra8876_ctouch_counter=0; - - if (FT6236readTouchLocation(&ra8876_pLoc,1)) { - ra8876_pLoc.x=ra8876_pLoc.x*RA8876_TFTWIDTH/800; - ra8876_pLoc.y=ra8876_pLoc.y*RA8876_TFTHEIGHT/480; - - - if (renderer) { - - - ra8876_pLoc.x=RA8876_TFTWIDTH-ra8876_pLoc.x; - ra8876_pLoc.y=RA8876_TFTHEIGHT-ra8876_pLoc.y; -# 170 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_10_RA8876.ino" - for (uint8_t count=0; countvpower&0x7f; - if (buttons[count]->contains(ra8876_pLoc.x,ra8876_pLoc.y)) { - - buttons[count]->press(true); - if (buttons[count]->justPressed()) { - if (!bflags) { - - 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); - } - } else { - - const char *cp; - if (bflags==1) { - - buttons[count]->vpower^=0x80; - cp="TBT"; - } else { - - buttons[count]->vpower|=0x80; - cp="PBT"; - } - buttons[count]->xdrawButton(buttons[count]->vpower&0x80); - RA8876_MQTT(count,cp); - } - } - } - if (!bflags) { - rbutt++; - } else { - vbutt++; - } - } - } - } - } else { - - for (uint8_t count=0; countvpower&0x7f; - buttons[count]->press(false); - if (buttons[count]->justReleased()) { - if (bflags>0) { - if (bflags>1) { - - buttons[count]->vpower&=0x7f; - RA8876_MQTT(count,"PBT"); - } - buttons[count]->xdrawButton(buttons[count]->vpower&0x80); - } - } - if (!bflags) { - - uint8_t pwr=bitRead(power,rbutt); - uint8_t vpwr=(buttons[count]->vpower&0x80)>>7; - if (pwr!=vpwr) { - RA8876_RDW_BUTT(count,pwr); - } - rbutt++; - } - } - } - ra8876_pLoc.x=0; - ra8876_pLoc.y=0; - } -} -} -#endif -# 426 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_10_RA8876.ino" -bool Xdsp10(uint8_t function) -{ - bool result = false; - - if (FUNC_DISPLAY_INIT_DRIVER == function) { - RA8876_InitDriver(); - } - else if (XDSP_10 == Settings.display_model) { - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; - case FUNC_DISPLAY_EVERY_50_MSECOND: -#ifdef USE_TOUCH_BUTTONS - if (FT5316_found) FT5316Check(); -#endif - break; - } - } - return result; -} -#endif -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_11_sevenseg.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_11_sevenseg.ino" -#ifdef USE_I2C -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_SEVENSEG - -#define XDSP_11 11 -#define XI2C_47 47 - -#include -#include -#include - -Adafruit_7segment sevenseg = Adafruit_7segment(); - -uint8_t sevenseg_state = 0; - - - -void SevensegWrite(void) -{ - sevenseg.writeDisplay(); -} - -void SevensegClear(void) -{ - sevenseg.clear(); - SevensegWrite(); -} - - - - -void SevensegInitMode(void) -{ - sevenseg.setBrightness(Settings.display_dimmer); - sevenseg.blinkRate(0); - SevensegClear(); -} - -void SevensegInit(uint8_t mode) -{ - switch(mode) { - case DISPLAY_INIT_MODE: - case DISPLAY_INIT_PARTIAL: - case DISPLAY_INIT_FULL: - SevensegInitMode(); - break; - } -} - -void SevensegInitDriver(void) -{ - if (!Settings.display_model) { - if (I2cSetDevice(SEVENSEG_ADDRESS1)) { - Settings.display_model = XDSP_11; - } - } - - if (XDSP_11 == Settings.display_model) { - sevenseg_state = 1; - sevenseg.begin(SEVENSEG_ADDRESS1); - - Settings.display_width = 4; - Settings.display_height = 1; - - SevensegInitMode(); - } -} - -void SevensegOnOff(void) -{ - if (!disp_power) { SevensegClear(); } -} - -void SevensegDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) -{ - uint16_t number = 0; - boolean hasnumber= false; - uint8_t dots= 0; - boolean t=false; - boolean T=false; - boolean d=false; - boolean hex=false; - boolean done=false; - boolean s=false; - for (int i=0; (str[i]!='\0') && (!done); i++) { -# 113 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_11_sevenseg.ino" - switch (str[i]) { - case 'x': - hex = true; - break; - case ':': - dots |= 0x02; - break; - case '^': - dots |= 0x08; - break; - case 'v': - dots |= 0x04; - break; - case '.': - dots |= 0x10; - break; - case 'T': - t = true; - break; - case 't': - T = true; - break; - case 's': - s = true; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - hasnumber= true; - number = atoi(str+i); - done = true; - break; - default: - break; - } - } - - if (s) { - - hex = false; - int hour = number/60/60; - int minute = (number/60)%60; - - if (hour) { - - number = hour*100 + minute; - } else { - - number = minute*100 + number%60; - } - } - - if (hasnumber) { - if (hex) { - sevenseg.print(number, HEX); - } else { - sevenseg.print(number, DEC); - } - } - - if (dots) { - sevenseg.writeDigitRaw(2, dots); - } - - sevenseg.writeDisplay(); -} - - - -#ifdef USE_DISPLAY_MODES1TO5 -void SevensegTime(boolean time_24) -{ - - uint hours = RtcTime.hour; - uint minutes = RtcTime.minute; - uint second = RtcTime.second; - uint16_t displayValue = hours * 100 + minutes; - uint16_t dots = 0; - - - if (!time_24) { - - if (hours > 12) { - displayValue -= 1200; - } - - else if (hours == 0) { - displayValue += 1200; - } - } - - - - sevenseg.print(displayValue, DEC); - - - - - if (time_24) { - if (hours == 0) { - - sevenseg.writeDigitNum(1, 0); - - if (minutes < 10) { - sevenseg.writeDigitNum(3, 0); - } - } - if (hours < 10) { - - sevenseg.writeDigitNum(0, 0); - } - } else { - - if (hours >= 12) { - dots |= 0x10; - } - } - - sevenseg.writeDigitRaw(2, dots |= ((second%2) << 1)); - sevenseg.writeDisplay(); -} - -#endif - -void SevensegRefresh(void) -{ - if (disp_power) { - if (Settings.display_mode) { - switch (Settings.display_mode) { - case 1: - SevensegTime(false); - break; - case 2: - SevensegTime(true); - break; - case 4: - case 3: - case 5: { - break; - } - } - } - } -} - - - - - -bool Xdsp11(uint8_t function) -{ - if (!I2cEnabled(XI2C_47)) { return false; } - - bool result = false; - - if (FUNC_DISPLAY_INIT_DRIVER == function) { - SevensegInitDriver(); - } - else if (XDSP_11 == Settings.display_model) { - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; - case FUNC_DISPLAY_INIT: - SevensegInit(dsp_init); - break; - case FUNC_DISPLAY_CLEAR: - SevensegClear(); - break; - case FUNC_DISPLAY_EVERY_SECOND: - SevensegRefresh(); - break; - case FUNC_DISPLAY_ONOFF: - case FUNC_DISPLAY_POWER: - SevensegOnOff(); - break; - case FUNC_DISPLAY_DRAW_STRING: - SevensegDrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag); - break; - } - } - return result; -} - -#endif -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_interface.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_interface.ino" -#if defined(USE_I2C) || defined(USE_SPI) -#ifdef USE_DISPLAY - -#ifdef XFUNC_PTR_IN_ROM -bool (* const xdsp_func_ptr[])(uint8_t) PROGMEM = { -#else -bool (* const xdsp_func_ptr[])(uint8_t) = { -#endif - -#ifdef XDSP_01 - &Xdsp01, -#endif - -#ifdef XDSP_02 - &Xdsp02, -#endif - -#ifdef XDSP_03 - &Xdsp03, -#endif - -#ifdef XDSP_04 - &Xdsp04, -#endif - -#ifdef XDSP_05 - &Xdsp05, -#endif - -#ifdef XDSP_06 - &Xdsp06, -#endif - -#ifdef XDSP_07 - &Xdsp07, -#endif - -#ifdef XDSP_08 - &Xdsp08, -#endif - -#ifdef XDSP_09 - &Xdsp09, -#endif - -#ifdef XDSP_10 - &Xdsp10, -#endif - -#ifdef XDSP_11 - &Xdsp11, -#endif - -#ifdef XDSP_12 - &Xdsp12, -#endif - -#ifdef XDSP_13 - &Xdsp13, -#endif - -#ifdef XDSP_14 - &Xdsp14, -#endif - -#ifdef XDSP_15 - &Xdsp15, -#endif - -#ifdef XDSP_16 - &Xdsp16 -#endif -}; - -const uint8_t xdsp_present = sizeof(xdsp_func_ptr) / sizeof(xdsp_func_ptr[0]); -# 118 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xdsp_interface.ino" -uint8_t XdspPresent(void) -{ - return xdsp_present; -} - -bool XdspCall(uint8_t Function) -{ - bool result = false; - - DEBUG_TRACE_LOG(PSTR("DSP: %d"), Function); - - for (uint32_t x = 0; x < xdsp_present; x++) { - result = xdsp_func_ptr[x](Function); - - if (result && (FUNC_DISPLAY_MODEL == Function)) { - break; - } - } - - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_01_ws2812.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_01_ws2812.ino" -#ifdef USE_LIGHT -#ifdef USE_WS2812 -# 38 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_01_ws2812.ino" -#define XLGT_01 1 - -const uint8_t WS2812_SCHEMES = 8; - -const char kWs2812Commands[] PROGMEM = "|" - D_CMND_LED "|" D_CMND_PIXELS "|" D_CMND_ROTATION "|" D_CMND_WIDTH ; - -void (* const Ws2812Command[])(void) PROGMEM = { - &CmndLed, &CmndPixels, &CmndRotation, &CmndWidth }; - -#include - -#if (USE_WS2812_CTYPE == NEO_GRB) - typedef NeoGrbFeature selectedNeoFeatureType; -#elif (USE_WS2812_CTYPE == NEO_BRG) - typedef NeoBrgFeature selectedNeoFeatureType; -#elif (USE_WS2812_CTYPE == NEO_RBG) - typedef NeoRbgFeature selectedNeoFeatureType; -#elif (USE_WS2812_CTYPE == NEO_RGBW) - typedef NeoRgbwFeature selectedNeoFeatureType; -#elif (USE_WS2812_CTYPE == NEO_GRBW) - typedef NeoGrbwFeature selectedNeoFeatureType; -#else - typedef NeoRgbFeature selectedNeoFeatureType; -#endif - -#ifdef USE_WS2812_DMA - - -#if (USE_WS2812_HARDWARE == NEO_HW_WS2812X) - typedef NeoEsp8266DmaWs2812xMethod selectedNeoSpeedType; -#elif (USE_WS2812_HARDWARE == NEO_HW_SK6812) - typedef NeoEsp8266DmaSk6812Method selectedNeoSpeedType; -#elif (USE_WS2812_HARDWARE == NEO_HW_APA106) - typedef NeoEsp8266DmaApa106Method selectedNeoSpeedType; -#else - typedef NeoEsp8266Dma800KbpsMethod selectedNeoSpeedType; -#endif - -#else - - -#if (USE_WS2812_HARDWARE == NEO_HW_WS2812X) - typedef NeoEsp8266BitBangWs2812xMethod selectedNeoSpeedType; -#elif (USE_WS2812_HARDWARE == NEO_HW_SK6812) - typedef NeoEsp8266BitBangSk6812Method selectedNeoSpeedType; -#else - typedef NeoEsp8266BitBang800KbpsMethod selectedNeoSpeedType; -#endif - -#endif - -NeoPixelBus *strip = nullptr; - -struct WsColor { - uint8_t red, green, blue; -}; - -struct ColorScheme { - WsColor* colors; - uint8_t count; -}; - -WsColor kIncandescent[2] = { 255,140,20, 0,0,0 }; -WsColor kRgb[3] = { 255,0,0, 0,255,0, 0,0,255 }; -WsColor kChristmas[2] = { 255,0,0, 0,255,0 }; -WsColor kHanukkah[2] = { 0,0,255, 255,255,255 }; -WsColor kwanzaa[3] = { 255,0,0, 0,0,0, 0,255,0 }; -WsColor kRainbow[7] = { 255,0,0, 255,128,0, 255,255,0, 0,255,0, 0,0,255, 128,0,255, 255,0,255 }; -WsColor kFire[3] = { 255,0,0, 255,102,0, 255,192,0 }; -ColorScheme kSchemes[WS2812_SCHEMES -1] = { - kIncandescent, 2, - kRgb, 3, - kChristmas, 2, - kHanukkah, 2, - kwanzaa, 3, - kRainbow, 7, - kFire, 3 }; - -uint8_t kWidth[5] = { - 1, - 2, - 4, - 8, - 255 }; -uint8_t kWsRepeat[5] = { - 8, - 6, - 4, - 2, - 1 }; - -struct WS2812 { - uint8_t show_next = 1; - uint8_t scheme_offset = 0; - bool suspend_update = false; -} Ws2812; - - - -void Ws2812StripShow(void) -{ -#if (USE_WS2812_CTYPE > NEO_3LED) - RgbwColor c; -#else - RgbColor c; -#endif - - if (Settings.light_correction) { - for (uint32_t i = 0; i < Settings.light_pixels; i++) { - c = strip->GetPixelColor(i); - c.R = ledGamma(c.R); - c.G = ledGamma(c.G); - c.B = ledGamma(c.B); -#if (USE_WS2812_CTYPE > NEO_3LED) - c.W = ledGamma(c.W); -#endif - strip->SetPixelColor(i, c); - } - } - strip->Show(); -} - -int mod(int a, int b) -{ - int ret = a % b; - if (ret < 0) ret += b; - return ret; -} - -void Ws2812UpdatePixelColor(int position, struct WsColor hand_color, float offset) -{ -#if (USE_WS2812_CTYPE > NEO_3LED) - RgbwColor color; -#else - RgbColor color; -#endif - - uint32_t mod_position = mod(position, (int)Settings.light_pixels); - - color = strip->GetPixelColor(mod_position); - float dimmer = 100 / (float)Settings.light_dimmer; - color.R = tmin(color.R + ((hand_color.red / dimmer) * offset), 255); - color.G = tmin(color.G + ((hand_color.green / dimmer) * offset), 255); - color.B = tmin(color.B + ((hand_color.blue / dimmer) * offset), 255); - strip->SetPixelColor(mod_position, color); -} - -void Ws2812UpdateHand(int position, uint32_t index) -{ - uint32_t width = Settings.light_width; - if (index < WS_MARKER) { width = Settings.ws_width[index]; } - if (!width) { return; } - - position = (position + Settings.light_rotation) % Settings.light_pixels; - - if (Settings.flag.ws_clock_reverse) { - position = Settings.light_pixels -position; - } - WsColor hand_color = { Settings.ws_color[index][WS_RED], Settings.ws_color[index][WS_GREEN], Settings.ws_color[index][WS_BLUE] }; - - Ws2812UpdatePixelColor(position, hand_color, 1); - - uint32_t range = ((width -1) / 2) +1; - for (uint32_t h = 1; h < range; h++) { - float offset = (float)(range - h) / (float)range; - Ws2812UpdatePixelColor(position -h, hand_color, offset); - Ws2812UpdatePixelColor(position +h, hand_color, offset); - } -} - -void Ws2812Clock(void) -{ - strip->ClearTo(0); - int clksize = 60000 / (int)Settings.light_pixels; - - Ws2812UpdateHand((RtcTime.second * 1000) / clksize, WS_SECOND); - Ws2812UpdateHand((RtcTime.minute * 1000) / clksize, WS_MINUTE); - Ws2812UpdateHand((((RtcTime.hour % 12) * 5000) + ((RtcTime.minute * 1000) / 12 )) / clksize, WS_HOUR); - if (Settings.ws_color[WS_MARKER][WS_RED] + Settings.ws_color[WS_MARKER][WS_GREEN] + Settings.ws_color[WS_MARKER][WS_BLUE]) { - for (uint32_t i = 0; i < 12; i++) { - Ws2812UpdateHand((i * 5000) / clksize, WS_MARKER); - } - } - - Ws2812StripShow(); -} - -void Ws2812GradientColor(uint32_t schemenr, struct WsColor* mColor, uint32_t range, uint32_t gradRange, uint32_t i) -{ - - - - - ColorScheme scheme = kSchemes[schemenr]; - uint32_t curRange = i / range; - uint32_t rangeIndex = i % range; - uint32_t colorIndex = rangeIndex / gradRange; - uint32_t start = colorIndex; - uint32_t end = colorIndex +1; - if (curRange % 2 != 0) { - start = (scheme.count -1) - start; - end = (scheme.count -1) - end; - } - float dimmer = 100 / (float)Settings.light_dimmer; - float fmyRed = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].red, scheme.colors[end].red) / dimmer; - float fmyGrn = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].green, scheme.colors[end].green) / dimmer; - float fmyBlu = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].blue, scheme.colors[end].blue) / dimmer; - mColor->red = (uint8_t)fmyRed; - mColor->green = (uint8_t)fmyGrn; - mColor->blue = (uint8_t)fmyBlu; -} - -void Ws2812Gradient(uint32_t schemenr) -{ - - - - - -#if (USE_WS2812_CTYPE > NEO_3LED) - RgbwColor c; - c.W = 0; -#else - RgbColor c; -#endif - - ColorScheme scheme = kSchemes[schemenr]; - if (scheme.count < 2) { return; } - - uint32_t repeat = kWsRepeat[Settings.light_width]; - uint32_t range = (uint32_t)ceil((float)Settings.light_pixels / (float)repeat); - uint32_t gradRange = (uint32_t)ceil((float)range / (float)(scheme.count - 1)); - uint32_t speed = ((Settings.light_speed * 2) -1) * (STATES / 10); - uint32_t offset = speed > 0 ? Light.strip_timer_counter / speed : 0; - - WsColor oldColor, currentColor; - Ws2812GradientColor(schemenr, &oldColor, range, gradRange, offset); - currentColor = oldColor; - for (uint32_t i = 0; i < Settings.light_pixels; i++) { - if (kWsRepeat[Settings.light_width] > 1) { - Ws2812GradientColor(schemenr, ¤tColor, range, gradRange, i +offset); - } - if (Settings.light_speed > 0) { - - c.R = map(Light.strip_timer_counter % speed, 0, speed, oldColor.red, currentColor.red); - c.G = map(Light.strip_timer_counter % speed, 0, speed, oldColor.green, currentColor.green); - c.B = map(Light.strip_timer_counter % speed, 0, speed, oldColor.blue, currentColor.blue); - } - else { - - c.R = currentColor.red; - c.G = currentColor.green; - c.B = currentColor.blue; - } - strip->SetPixelColor(i, c); - oldColor = currentColor; - } - Ws2812StripShow(); -} - -void Ws2812Bars(uint32_t schemenr) -{ - - - - - -#if (USE_WS2812_CTYPE > NEO_3LED) - RgbwColor c; - c.W = 0; -#else - RgbColor c; -#endif - - ColorScheme scheme = kSchemes[schemenr]; - - uint32_t maxSize = Settings.light_pixels / scheme.count; - if (kWidth[Settings.light_width] > maxSize) { maxSize = 0; } - - uint32_t speed = ((Settings.light_speed * 2) -1) * (STATES / 10); - uint32_t offset = (speed > 0) ? Light.strip_timer_counter / speed : 0; - - WsColor mcolor[scheme.count]; - memcpy(mcolor, scheme.colors, sizeof(mcolor)); - float dimmer = 100 / (float)Settings.light_dimmer; - for (uint32_t i = 0; i < scheme.count; i++) { - float fmyRed = (float)mcolor[i].red / dimmer; - float fmyGrn = (float)mcolor[i].green / dimmer; - float fmyBlu = (float)mcolor[i].blue / dimmer; - mcolor[i].red = (uint8_t)fmyRed; - mcolor[i].green = (uint8_t)fmyGrn; - mcolor[i].blue = (uint8_t)fmyBlu; - } - uint32_t colorIndex = offset % scheme.count; - for (uint32_t i = 0; i < Settings.light_pixels; i++) { - if (maxSize) { colorIndex = ((i + offset) % (scheme.count * kWidth[Settings.light_width])) / kWidth[Settings.light_width]; } - c.R = mcolor[colorIndex].red; - c.G = mcolor[colorIndex].green; - c.B = mcolor[colorIndex].blue; - strip->SetPixelColor(i, c); - } - Ws2812StripShow(); -} - -void Ws2812Clear(void) -{ - strip->ClearTo(0); - strip->Show(); - Ws2812.show_next = 1; -} - -void Ws2812SetColor(uint32_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t white) -{ -#if (USE_WS2812_CTYPE > NEO_3LED) - RgbwColor lcolor; - lcolor.W = white; -#else - RgbColor lcolor; -#endif - - lcolor.R = red; - lcolor.G = green; - lcolor.B = blue; - if (led) { - strip->SetPixelColor(led -1, lcolor); - } else { - - for (uint32_t i = 0; i < Settings.light_pixels; i++) { - strip->SetPixelColor(i, lcolor); - } - } - - if (!Ws2812.suspend_update) { - strip->Show(); - Ws2812.show_next = 1; - } -} - -char* Ws2812GetColor(uint32_t led, char* scolor) -{ - uint8_t sl_ledcolor[4]; - - #if (USE_WS2812_CTYPE > NEO_3LED) - RgbwColor lcolor = strip->GetPixelColor(led -1); - sl_ledcolor[3] = lcolor.W; - #else - RgbColor lcolor = strip->GetPixelColor(led -1); - #endif - sl_ledcolor[0] = lcolor.R; - sl_ledcolor[1] = lcolor.G; - sl_ledcolor[2] = lcolor.B; - scolor[0] = '\0'; - for (uint32_t i = 0; i < Light.subtype; i++) { - if (Settings.flag.decimal_text) { - snprintf_P(scolor, 25, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", sl_ledcolor[i]); - } else { - snprintf_P(scolor, 25, PSTR("%s%02X"), scolor, sl_ledcolor[i]); - } - } - return scolor; -} - - - - - -void Ws2812ForceSuspend (void) -{ - Ws2812.suspend_update = true; -} - -void Ws2812ForceUpdate (void) -{ - Ws2812.suspend_update = false; - strip->Show(); - Ws2812.show_next = 1; -} - - - -bool Ws2812SetChannels(void) -{ - uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; - - Ws2812SetColor(0, cur_col[0], cur_col[1], cur_col[2], cur_col[3]); - - return true; -} - -void Ws2812ShowScheme(void) -{ - uint32_t scheme = Settings.light_scheme - Ws2812.scheme_offset; - - switch (scheme) { - case 0: - if ((1 == state_250mS) || (Ws2812.show_next)) { - Ws2812Clock(); - Ws2812.show_next = 0; - } - break; - default: - if (1 == Settings.light_fade) { - Ws2812Gradient(scheme -1); - } else { - Ws2812Bars(scheme -1); - } - Ws2812.show_next = 1; - break; - } -} - -void Ws2812ModuleSelected(void) -{ - if (pin[GPIO_WS2812] < 99) { - - - strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); - strip->Begin(); - - Ws2812Clear(); - - Ws2812.scheme_offset = Light.max_scheme +1; - Light.max_scheme += WS2812_SCHEMES; - -#if (USE_WS2812_CTYPE > NEO_3LED) - light_type = LT_RGBW; -#else - light_type = LT_RGB; -#endif - light_flg = XLGT_01; - } -} - - - -void CmndLed(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Settings.light_pixels)) { - if (XdrvMailbox.data_len > 0) { - char *p; - uint16_t idx = XdrvMailbox.index; - Ws2812ForceSuspend(); - for (char *color = strtok_r(XdrvMailbox.data, " ", &p); color; color = strtok_r(nullptr, " ", &p)) { - if (LightColorEntry(color, strlen(color))) { - Ws2812SetColor(idx, Light.entry_color[0], Light.entry_color[1], Light.entry_color[2], Light.entry_color[3]); - idx++; - if (idx > Settings.light_pixels) { break; } - } else { - break; - } - } - Ws2812ForceUpdate(); - } - char scolor[LIGHT_COLOR_SIZE]; - ResponseCmndIdxChar(Ws2812GetColor(XdrvMailbox.index, scolor)); - } -} - -void CmndPixels(void) -{ - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= WS2812_MAX_LEDS)) { - Settings.light_pixels = XdrvMailbox.payload; - Settings.light_rotation = 0; - Ws2812Clear(); - Light.update = true; - } - ResponseCmndNumber(Settings.light_pixels); -} - -void CmndRotation(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < Settings.light_pixels)) { - Settings.light_rotation = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.light_rotation); -} - -void CmndWidth(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { - if (1 == XdrvMailbox.index) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 4)) { - Settings.light_width = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.light_width); - } else { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32)) { - Settings.ws_width[XdrvMailbox.index -2] = XdrvMailbox.payload; - } - ResponseCmndIdxNumber(Settings.ws_width[XdrvMailbox.index -2]); - } - } -} - - - - - -bool Xlgt01(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_SET_CHANNELS: - result = Ws2812SetChannels(); - break; - case FUNC_SET_SCHEME: - Ws2812ShowScheme(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kWs2812Commands, Ws2812Command); - break; - case FUNC_MODULE_INIT: - Ws2812ModuleSelected(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_02_my92x1.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_02_my92x1.ino" -#ifdef USE_LIGHT -#ifdef USE_MY92X1 - - - - -#define XLGT_02 2 - -struct MY92X1 { - uint8_t pdi_pin = 0; - uint8_t pdcki_pin = 0; - uint8_t model = 0; -} My92x1; - -extern "C" { - void os_delay_us(unsigned int); -} - -void LightDiPulse(uint8_t times) -{ - for (uint32_t i = 0; i < times; i++) { - digitalWrite(My92x1.pdi_pin, HIGH); - digitalWrite(My92x1.pdi_pin, LOW); - } -} - -void LightDckiPulse(uint8_t times) -{ - for (uint32_t i = 0; i < times; i++) { - digitalWrite(My92x1.pdcki_pin, HIGH); - digitalWrite(My92x1.pdcki_pin, LOW); - } -} - -void LightMy92x1Write(uint8_t data) -{ - for (uint32_t i = 0; i < 4; i++) { - digitalWrite(My92x1.pdcki_pin, LOW); - digitalWrite(My92x1.pdi_pin, (data & 0x80)); - digitalWrite(My92x1.pdcki_pin, HIGH); - data = data << 1; - digitalWrite(My92x1.pdi_pin, (data & 0x80)); - digitalWrite(My92x1.pdcki_pin, LOW); - digitalWrite(My92x1.pdi_pin, LOW); - data = data << 1; - } -} - -void LightMy92x1Init(void) -{ - uint8_t chips[3] = { 1, 2, 2 }; - - LightDckiPulse(chips[My92x1.model] * 32); - os_delay_us(12); - - - LightDiPulse(12); - os_delay_us(12); - for (uint32_t n = 0; n < chips[My92x1.model]; n++) { - LightMy92x1Write(0x18); - } - os_delay_us(12); - - - LightDiPulse(16); - os_delay_us(12); -} - -void LightMy92x1Duty(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b, uint8_t duty_w, uint8_t duty_c) -{ - uint8_t channels[3] = { 4, 6, 6 }; - - uint8_t duty[3][6] = {{ duty_r, duty_g, duty_b, duty_w, 0, 0 }, - { duty_w, duty_c, 0, duty_g, duty_r, duty_b }, - { duty_r, duty_g, duty_b, duty_w, duty_w, duty_w }}; - - os_delay_us(12); - for (uint32_t channel = 0; channel < channels[My92x1.model]; channel++) { - LightMy92x1Write(duty[My92x1.model][channel]); - } - os_delay_us(12); - LightDiPulse(8); - os_delay_us(12); -} - - - -bool My92x1SetChannels(void) -{ - uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; - - LightMy92x1Duty(cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]); - - return true; -} - -void My92x1ModuleSelected(void) -{ - if ((pin[GPIO_DCKI] < 99) && (pin[GPIO_DI] < 99)) { - My92x1.pdi_pin = pin[GPIO_DI]; - My92x1.pdcki_pin = pin[GPIO_DCKI]; - - pinMode(My92x1.pdi_pin, OUTPUT); - pinMode(My92x1.pdcki_pin, OUTPUT); - digitalWrite(My92x1.pdi_pin, LOW); - digitalWrite(My92x1.pdcki_pin, LOW); - - My92x1.model = 2; - light_type = LT_RGBW; - if (AILIGHT == my_module_type) { - My92x1.model = 0; - - } - else if (SONOFF_B1 == my_module_type) { - My92x1.model = 1; - light_type = LT_RGBWC; - } - - LightMy92x1Init(); - - light_flg = XLGT_02; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: MY29x1 Found")); - } -} - - - - - -bool Xlgt02(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_SET_CHANNELS: - result = My92x1SetChannels(); - break; - case FUNC_MODULE_INIT: - My92x1ModuleSelected(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_03_sm16716.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_03_sm16716.ino" -#ifdef USE_LIGHT -#ifdef USE_SM16716 - - - - - - - -#define XLGT_03 3 - -#define D_LOG_SM16716 "SM16716: " - -struct SM16716 { - uint8_t pin_clk = 0; - uint8_t pin_dat = 0; - uint8_t pin_sel = 0; - bool enabled = false; -} Sm16716; - -void SM16716_SendBit(uint8_t v) -{ - - - - - - digitalWrite(Sm16716.pin_dat, (v != 0) ? HIGH : LOW); - - digitalWrite(Sm16716.pin_clk, HIGH); - - digitalWrite(Sm16716.pin_clk, LOW); -} - -void SM16716_SendByte(uint8_t v) -{ - uint8_t mask; - - for (mask = 0x80; mask; mask >>= 1) { - SM16716_SendBit(v & mask); - } -} - -void SM16716_Update(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b) -{ - if (Sm16716.pin_sel < 99) { - bool should_enable = (duty_r | duty_g | duty_b); - if (!Sm16716.enabled && should_enable) { - DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color on")); - Sm16716.enabled = true; - digitalWrite(Sm16716.pin_sel, HIGH); - - - delayMicroseconds(1000); - SM16716_Init(); - } - else if (Sm16716.enabled && !should_enable) { - DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color off")); - Sm16716.enabled = false; - digitalWrite(Sm16716.pin_sel, LOW); - } - } - DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "Update; rgb=%02x%02x%02x"), duty_r, duty_g, duty_b); - - - SM16716_SendBit(1); - SM16716_SendByte(duty_r); - SM16716_SendByte(duty_g); - SM16716_SendByte(duty_b); - - - - - - SM16716_SendBit(0); - SM16716_SendByte(0); - SM16716_SendByte(0); - SM16716_SendByte(0); -} -# 111 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_03_sm16716.ino" -void SM16716_Init(void) -{ - for (uint32_t t_init = 0; t_init < 50; ++t_init) { - SM16716_SendBit(0); - } -} - - - -bool Sm16716SetChannels(void) -{ -# 132 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_03_sm16716.ino" - uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; - - SM16716_Update(cur_col[0], cur_col[1], cur_col[2]); - - return true; -} - -void Sm16716ModuleSelected(void) -{ - if ((pin[GPIO_SM16716_CLK] < 99) && (pin[GPIO_SM16716_DAT] < 99)) { - Sm16716.pin_clk = pin[GPIO_SM16716_CLK]; - Sm16716.pin_dat = pin[GPIO_SM16716_DAT]; - Sm16716.pin_sel = pin[GPIO_SM16716_SEL]; -# 157 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_03_sm16716.ino" - pinMode(Sm16716.pin_clk, OUTPUT); - digitalWrite(Sm16716.pin_clk, LOW); - - pinMode(Sm16716.pin_dat, OUTPUT); - digitalWrite(Sm16716.pin_dat, LOW); - - if (Sm16716.pin_sel < 99) { - pinMode(Sm16716.pin_sel, OUTPUT); - digitalWrite(Sm16716.pin_sel, LOW); - - } else { - - SM16716_Init(); - } - - LightPwmOffset(LST_RGB); - light_type += LST_RGB; - light_flg = XLGT_03; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: SM16716 Found")); - } -} - - - - - -bool Xlgt03(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_SET_CHANNELS: - result = Sm16716SetChannels(); - break; - case FUNC_MODULE_INIT: - Sm16716ModuleSelected(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_04_sm2135.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_04_sm2135.ino" -#ifdef USE_LIGHT -#ifdef USE_SM2135 - - - - - - -#define XLGT_04 4 - -#define SM2135_ADDR_MC 0xC0 -#define SM2135_ADDR_CH 0xC1 -#define SM2135_ADDR_R 0xC2 -#define SM2135_ADDR_G 0xC3 -#define SM2135_ADDR_B 0xC4 -#define SM2135_ADDR_C 0xC5 -#define SM2135_ADDR_W 0xC6 - -#define SM2135_RGB 0x00 -#define SM2135_CW 0x80 - -#define SM2135_10MA 0x00 -#define SM2135_15MA 0x01 -#define SM2135_20MA 0x02 -#define SM2135_25MA 0x03 -#define SM2135_30MA 0x04 -#define SM2135_35MA 0x05 -#define SM2135_40MA 0x06 -#define SM2135_45MA 0x07 -#define SM2135_50MA 0x08 -#define SM2135_55MA 0x09 -#define SM2135_60MA 0x0A - - -const uint8_t SM2135_CURRENT = (SM2135_20MA << 4) | SM2135_15MA; - -struct SM2135 { - uint8_t clk = 0; - uint8_t data = 0; -} Sm2135; - -uint8_t Sm2135Write(uint8_t data) -{ - for (uint32_t i = 0; i < 8; i++) { - digitalWrite(Sm2135.clk, LOW); - digitalWrite(Sm2135.data, (data & 0x80)); - digitalWrite(Sm2135.clk, HIGH); - data = data << 1; - } - digitalWrite(Sm2135.clk, LOW); - digitalWrite(Sm2135.data, HIGH); - pinMode(Sm2135.data, INPUT); - digitalWrite(Sm2135.clk, HIGH); - uint8_t ack = digitalRead(Sm2135.data); - pinMode(Sm2135.data, OUTPUT); - return ack; -} - -void Sm2135Send(uint8_t *buffer, uint8_t size) -{ - digitalWrite(Sm2135.data, LOW); - for (uint32_t i = 0; i < size; i++) { - Sm2135Write(buffer[i]); - } - digitalWrite(Sm2135.clk, LOW); - digitalWrite(Sm2135.clk, HIGH); - digitalWrite(Sm2135.data, HIGH); -} - - - -bool Sm2135SetChannels(void) -{ - uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; - uint8_t data[6]; - - if ((0 == cur_col[0]) && (0 == cur_col[1]) && (0 == cur_col[2])) { -# 106 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_04_sm2135.ino" - data[0] = SM2135_ADDR_MC; - data[1] = SM2135_CURRENT; - data[2] = SM2135_CW; - Sm2135Send(data, 3); - delay(1); - data[0] = SM2135_ADDR_C; - data[1] = cur_col[4]; - data[2] = cur_col[3]; - Sm2135Send(data, 3); - } else { - - - - - - - - data[0] = SM2135_ADDR_MC; - data[1] = SM2135_CURRENT; - data[2] = SM2135_RGB; - data[3] = cur_col[1]; - data[4] = cur_col[0]; - data[5] = cur_col[2]; - Sm2135Send(data, 6); - } - - return true; -} - -void Sm2135ModuleSelected(void) -{ - if ((pin[GPIO_SM2135_CLK] < 99) && (pin[GPIO_SM2135_DAT] < 99)) { - Sm2135.clk = pin[GPIO_SM2135_CLK]; - Sm2135.data = pin[GPIO_SM2135_DAT]; - - pinMode(Sm2135.data, OUTPUT); - digitalWrite(Sm2135.data, HIGH); - pinMode(Sm2135.clk, OUTPUT); - digitalWrite(Sm2135.clk, HIGH); - - light_type = LT_RGBWC; - light_flg = XLGT_04; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: SM2135 Found")); - } -} - - - - - -bool Xlgt04(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_SET_CHANNELS: - result = Sm2135SetChannels(); - break; - case FUNC_MODULE_INIT: - Sm2135ModuleSelected(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_05_sonoff_l1.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_05_sonoff_l1.ino" -#ifdef USE_LIGHT -#ifdef USE_SONOFF_L1 - - - - -#define XLGT_05 5 - -#define SONOFF_L1_BUFFER_SIZE 140 - -#define SONOFF_L1_MODE_COLORFUL 1 -#define SONOFF_L1_MODE_COLORFUL_GRADIENT 2 -#define SONOFF_L1_MODE_COLORFUL_BREATH 3 -#define SONOFF_L1_MODE_DIY_GRADIENT 4 -#define SONOFF_L1_MODE_DIY_PULSE 5 -#define SONOFF_L1_MODE_DIY_BREATH 6 -#define SONOFF_L1_MODE_DIY_STROBE 7 -#define SONOFF_L1_MODE_RGB_GRADIENT 8 -#define SONOFF_L1_MODE_RGB_PULSE 9 -#define SONOFF_L1_MODE_RGB_BREATH 10 -#define SONOFF_L1_MODE_RGB_STROBE 11 -#define SONOFF_L1_MODE_SYNC_TO_MUSIC 12 - -struct SNFL1 { - uint32_t unlock = 0; - bool receive_ready = true; -} Snfl1; - - - -void SnfL1Send(const char *buffer) -{ - - - Serial.print(buffer); - Serial.write(0x1B); - Serial.flush(); -} - -void SnfL1SerialSendOk(void) -{ - char buffer[16]; - snprintf_P(buffer, sizeof(buffer), PSTR("AT+SEND=ok")); - - SnfL1Send(buffer); -} - -bool SnfL1SerialInput(void) -{ - if (serial_in_byte != 0x1B) { - if (serial_in_byte_counter >= 140) { - serial_in_byte_counter = 0; - } - if (serial_in_byte_counter || (!serial_in_byte_counter && ('A' == serial_in_byte))) { - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; - } - } else { - serial_in_buffer[serial_in_byte_counter++] = 0x00; - - - - - - - if (!strncmp(serial_in_buffer +3, "RESULT", 6)) { - Snfl1.receive_ready = true; - } - else if (!strncmp(serial_in_buffer +3, "UPDATE", 6)) { - char cmnd_dimmer[20]; - char cmnd_color[20]; - char *end_str; - char *string = serial_in_buffer +10; - char *token = strtok_r(string, ",", &end_str); - - bool color_updated[3] = { false, false, false }; - uint8_t current_color[3]; - memcpy(current_color, Settings.light_color, 3); - - bool switch_state = false; - bool is_power_change = false; - bool is_color_change = false; - bool is_brightness_change = false; - - while (token != nullptr) { - char* end_token; - char* token2 = strtok_r(token, ":", &end_token); - char* token3 = strtok_r(nullptr, ":", &end_token); - - if (!strncmp(token2, "\"sequence\"", 10)) { - - - - token = nullptr; - } - - else if (!strncmp(token2, "\"switch\"", 8)) { - switch_state = !strncmp(token3, "\"on\"", 4) ? true : false; - - - - is_power_change = (switch_state != Light.power); - } - - else if (!strncmp(token2, "\"color", 6)) { - char color_channel_name = token2[6]; - int color_index; - switch(color_channel_name) - { - case 'R': color_index = 0; - break; - case 'G': color_index = 1; - break; - case 'B': color_index = 2; - break; - } - int color_value = atoi(token3); - current_color[color_index] = color_value; - color_updated[color_index] = true; - - bool all_color_channels_updated = color_updated[0] && color_updated[1] && color_updated[2]; - if (all_color_channels_updated) { - - - - - - is_color_change = (Light.power && (memcmp(current_color, Settings.light_color, 3) != 0)); - } - snprintf_P(cmnd_color, sizeof(cmnd_color), PSTR(D_CMND_COLOR "2 %02x%02x%02x"), current_color[0], current_color[1], current_color[2]); - } - - else if (!strncmp(token2, "\"bright\"", 8)) { - uint8_t dimmer = atoi(token3); - - - - is_brightness_change = (Light.power && (dimmer > 0) && (dimmer != Settings.light_dimmer)); - snprintf_P(cmnd_dimmer, sizeof(cmnd_dimmer), PSTR(D_CMND_DIMMER " %d"), dimmer); - } - - token = strtok_r(nullptr, ",", &end_str); - } - - if (is_power_change) { - if (Settings.light_scheme > 0) { - if (!switch_state) { - char cmnd_scheme[20]; - snprintf_P(cmnd_scheme, sizeof(cmnd_scheme), PSTR(D_CMND_SCHEME " 0")); - ExecuteCommand(cmnd_scheme, SRC_SWITCH); - } - } else { - ExecuteCommandPower(1, switch_state, SRC_SWITCH); - } - } - else if (is_brightness_change) { - ExecuteCommand(cmnd_dimmer, SRC_SWITCH); - } - else if (Light.power && is_color_change) { - if (0 == Settings.light_scheme) { - if (Settings.light_fade) { - char cmnd_fade[20]; - snprintf_P(cmnd_fade, sizeof(cmnd_fade), PSTR(D_CMND_FADE " 0")); - ExecuteCommand(cmnd_fade, SRC_SWITCH); - } - ExecuteCommand(cmnd_color, SRC_SWITCH); - } - } - } - - SnfL1SerialSendOk(); - - return true; - } - serial_in_byte = 0; - return false; -} - - - -bool SnfL1SetChannels(void) -{ - if (Snfl1.receive_ready || TimeReached(Snfl1.unlock)) { - - uint8_t *scale_col = (uint8_t*)XdrvMailbox.topic; - - char buffer[140]; - snprintf_P(buffer, sizeof(buffer), PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"light_type\":1,\"colorR\":%d,\"colorG\":%d,\"colorB\":%d,\"bright\":%d,\"mode\":%d"), - LocalTime(), millis()%1000, - Light.power ? "on" : "off", - scale_col[0], scale_col[1], scale_col[2], - light_state.getDimmer(), - SONOFF_L1_MODE_COLORFUL); - - SnfL1Send(buffer); - - Snfl1.unlock = millis() + 500; - Snfl1.receive_ready = false; - } - return true; -} - -void SnfL1ModuleSelected(void) -{ - if (SONOFF_L1 == my_module_type) { - if ((pin[GPIO_RXD] < 99) && (pin[GPIO_TXD] < 99)) { - SetSerial(19200, TS_SERIAL_8N1); - - light_type = LT_RGB; - light_flg = XLGT_05; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("LGT: Sonoff L1 Found")); - } - } -} - - - - - -bool Xlgt05(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_SERIAL: - result = SnfL1SerialInput(); - break; - case FUNC_SET_CHANNELS: - result = SnfL1SetChannels(); - break; - case FUNC_MODULE_INIT: - SnfL1ModuleSelected(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_06_electriq_moodl.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_06_electriq_moodl.ino" -#ifdef USE_LIGHT -#ifdef USE_ELECTRIQ_MOODL -# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_06_electriq_moodl.ino" -#define XLGT_06 6 - - - -bool ElectriqMoodLSetChannels(void) -{ - uint8_t *col = (uint8_t*)XdrvMailbox.data; - uint8_t checksum = (uint8_t)(0x65 + 0xAA + 0x01 + 0x0A); - - Serial.write(0x65); - Serial.write(0xAA); - Serial.write(0x00); - Serial.write(0x01); - Serial.write(0x0A); - - uint8_t payload[5]; - payload[0] = col[0]; - payload[1] = col[1]; - payload[2] = col[2]; - payload[3] = col[3]; - payload[4] = 0x0; - - - for (uint32_t i = 0; i < 5; i++) { - Serial.write(payload[i]); - checksum += payload[i]; - } - - - for (uint32_t i = 0; i < 5; i++) { - Serial.write(payload[i]); - checksum += payload[i]; - } - - Serial.write(checksum); - Serial.flush(); - - return true; -} - -void ElectriqMoodLModuleSelected(void) -{ - if (pin[GPIO_ELECTRIQ_MOODL_TX] < 99) { - SetSerial(9600, TS_SERIAL_8N1); - light_type = LT_RGBW; - light_flg = XLGT_06; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("LGT: ElectriQ Mood Lamp Found")); - } -} - - - - - -bool Xlgt06(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_SET_CHANNELS: - result = ElectriqMoodLSetChannels(); - break; - case FUNC_MODULE_INIT: - ElectriqMoodLModuleSelected(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_interface.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xlgt_interface.ino" -#ifdef USE_LIGHT - -#ifdef XFUNC_PTR_IN_ROM -bool (* const xlgt_func_ptr[])(uint8_t) PROGMEM = { -#else -bool (* const xlgt_func_ptr[])(uint8_t) = { -#endif - -#ifdef XLGT_01 - &Xlgt01, -#endif - -#ifdef XLGT_02 - &Xlgt02, -#endif - -#ifdef XLGT_03 - &Xlgt03, -#endif - -#ifdef XLGT_04 - &Xlgt04, -#endif - -#ifdef XLGT_05 - &Xlgt05, -#endif - -#ifdef XLGT_06 - &Xlgt06, -#endif - -#ifdef XLGT_07 - &Xlgt07, -#endif - -#ifdef XLGT_08 - &Xlgt08, -#endif - -#ifdef XLGT_09 - &Xlgt09, -#endif - -#ifdef XLGT_10 - &Xlgt10, -#endif - -#ifdef XLGT_11 - &Xlgt11, -#endif - -#ifdef XLGT_12 - &Xlgt12, -#endif - -#ifdef XLGT_13 - &Xlgt13, -#endif - -#ifdef XLGT_14 - &Xlgt14, -#endif - -#ifdef XLGT_15 - &Xlgt15, -#endif - -#ifdef XLGT_16 - &Xlgt16 -#endif -}; - -const uint8_t xlgt_present = sizeof(xlgt_func_ptr) / sizeof(xlgt_func_ptr[0]); - -uint8_t xlgt_active = 0; - -bool XlgtCall(uint8_t function) -{ - DEBUG_TRACE_LOG(PSTR("LGT: %d"), function); - - if (FUNC_MODULE_INIT == function) { - for (uint32_t x = 0; x < xlgt_present; x++) { - xlgt_func_ptr[x](function); - if (light_flg) { - xlgt_active = x; - return true; - } - } - } - else if (light_flg) { - return xlgt_func_ptr[xlgt_active](function); - } - return false; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_01_hlw8012.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_01_hlw8012.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_HLW8012 - - - - - - -#define XNRG_01 1 - - -#define HLW_PREF 10000 -#define HLW_UREF 2200 -#define HLW_IREF 4545 - - -#define HJL_PREF 1362 -#define HJL_UREF 822 -#define HJL_IREF 3300 - -#define HLW_POWER_PROBE_TIME 10 -#define HLW_SAMPLE_COUNT 10 - - - -struct HLW { -#ifdef HLW_DEBUG - unsigned long debug[HLW_SAMPLE_COUNT]; -#endif - unsigned long cf_pulse_length = 0; - unsigned long cf_pulse_last_time = 0; - unsigned long cf_power_pulse_length = 0; - - unsigned long cf1_pulse_length = 0; - unsigned long cf1_pulse_last_time = 0; - unsigned long cf1_summed_pulse_length = 0; - unsigned long cf1_pulse_counter = 0; - unsigned long cf1_voltage_pulse_length = 0; - unsigned long cf1_current_pulse_length = 0; - - unsigned long energy_period_counter = 0; - - unsigned long power_ratio = 0; - unsigned long voltage_ratio = 0; - unsigned long current_ratio = 0; - - uint8_t model_type = 0; - uint8_t cf1_timer = 0; - uint8_t power_retry = 0; - bool select_ui_flag = false; - bool ui_flag = true; - bool load_off = true; -} Hlw; - - -#ifndef USE_WS2812_DMA -void HlwCfInterrupt(void) ICACHE_RAM_ATTR; -void HlwCf1Interrupt(void) ICACHE_RAM_ATTR; -#endif - -void HlwCfInterrupt(void) -{ - unsigned long us = micros(); - - if (Hlw.load_off) { - Hlw.cf_pulse_last_time = us; - Hlw.load_off = false; - } else { - Hlw.cf_pulse_length = us - Hlw.cf_pulse_last_time; - Hlw.cf_pulse_last_time = us; - Hlw.energy_period_counter++; - } - Energy.data_valid[0] = 0; -} - -void HlwCf1Interrupt(void) -{ - unsigned long us = micros(); - - Hlw.cf1_pulse_length = us - Hlw.cf1_pulse_last_time; - Hlw.cf1_pulse_last_time = us; - if ((Hlw.cf1_timer > 2) && (Hlw.cf1_timer < 8)) { - Hlw.cf1_summed_pulse_length += Hlw.cf1_pulse_length; -#ifdef HLW_DEBUG - Hlw.debug[Hlw.cf1_pulse_counter] = Hlw.cf1_pulse_length; -#endif - Hlw.cf1_pulse_counter++; - if (HLW_SAMPLE_COUNT == Hlw.cf1_pulse_counter) { - Hlw.cf1_timer = 8; - } - } - Energy.data_valid[0] = 0; -} - - - -void HlwEvery200ms(void) -{ - unsigned long cf1_pulse_length = 0; - unsigned long hlw_w = 0; - unsigned long hlw_u = 0; - unsigned long hlw_i = 0; - - if (micros() - Hlw.cf_pulse_last_time > (HLW_POWER_PROBE_TIME * 1000000)) { - Hlw.cf_pulse_length = 0; - Hlw.load_off = true; - } - Hlw.cf_power_pulse_length = Hlw.cf_pulse_length; - - if (Hlw.cf_power_pulse_length && Energy.power_on && !Hlw.load_off) { - hlw_w = (Hlw.power_ratio * Settings.energy_power_calibration) / Hlw.cf_power_pulse_length ; - Energy.active_power[0] = (float)hlw_w / 10; - Hlw.power_retry = 1; - } else { - if (Hlw.power_retry) { - Hlw.power_retry--; - } else { - Energy.active_power[0] = 0; - } - } - - if (pin[GPIO_NRG_CF1] < 99) { - Hlw.cf1_timer++; - if (Hlw.cf1_timer >= 8) { - Hlw.cf1_timer = 0; - Hlw.select_ui_flag = (Hlw.select_ui_flag) ? false : true; - DigitalWrite(GPIO_NRG_SEL, Hlw.select_ui_flag); - - if (Hlw.cf1_pulse_counter) { - cf1_pulse_length = Hlw.cf1_summed_pulse_length / Hlw.cf1_pulse_counter; - } - -#ifdef HLW_DEBUG - - char stemp[100]; - stemp[0] = '\0'; - for (uint32_t i = 0; i < Hlw.cf1_pulse_counter; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%s %d"), stemp, Hlw.debug[i]); - } - for (uint32_t i = 0; i < Hlw.cf1_pulse_counter; i++) { - for (uint32_t j = i + 1; j < Hlw.cf1_pulse_counter; j++) { - if (Hlw.debug[i] > Hlw.debug[j]) { - std::swap(Hlw.debug[i], Hlw.debug[j]); - } - } - } - unsigned long median = Hlw.debug[(Hlw.cf1_pulse_counter +1) / 2]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NRG: power %d, ui %d, cnt %d, smpl%s, sum %d, mean %d, median %d"), - Hlw.cf_power_pulse_length , Hlw.select_ui_flag, Hlw.cf1_pulse_counter, stemp, Hlw.cf1_summed_pulse_length, cf1_pulse_length, median); -#endif - - if (Hlw.select_ui_flag == Hlw.ui_flag) { - Hlw.cf1_voltage_pulse_length = cf1_pulse_length; - - if (Hlw.cf1_voltage_pulse_length && Energy.power_on) { - hlw_u = (Hlw.voltage_ratio * Settings.energy_voltage_calibration) / Hlw.cf1_voltage_pulse_length ; - Energy.voltage[0] = (float)hlw_u / 10; - } else { - Energy.voltage[0] = 0; - } - - } else { - Hlw.cf1_current_pulse_length = cf1_pulse_length; - - if (Hlw.cf1_current_pulse_length && Energy.active_power[0]) { - hlw_i = (Hlw.current_ratio * Settings.energy_current_calibration) / Hlw.cf1_current_pulse_length; - Energy.current[0] = (float)hlw_i / 1000; - } else { - Energy.current[0] = 0; - } - - } - Hlw.cf1_summed_pulse_length = 0; - Hlw.cf1_pulse_counter = 0; - } - } -} - -void HlwEverySecond(void) -{ - if (Energy.data_valid[0] > ENERGY_WATCHDOG) { - Hlw.cf1_voltage_pulse_length = 0; - Hlw.cf1_current_pulse_length = 0; - Hlw.cf_power_pulse_length = 0; - } else { - unsigned long hlw_len; - - if (Hlw.energy_period_counter) { - hlw_len = 10000 / Hlw.energy_period_counter; - Hlw.energy_period_counter = 0; - if (hlw_len) { - Energy.kWhtoday_delta += ((Hlw.power_ratio * Settings.energy_power_calibration) / hlw_len) / 36; - EnergyUpdateToday(); - } - } - } -} - -void HlwSnsInit(void) -{ - if (!Settings.energy_power_calibration || (4975 == Settings.energy_power_calibration)) { - Settings.energy_power_calibration = HLW_PREF_PULSE; - Settings.energy_voltage_calibration = HLW_UREF_PULSE; - Settings.energy_current_calibration = HLW_IREF_PULSE; - } - - if (Hlw.model_type) { - Hlw.power_ratio = HJL_PREF; - Hlw.voltage_ratio = HJL_UREF; - Hlw.current_ratio = HJL_IREF; - } else { - Hlw.power_ratio = HLW_PREF; - Hlw.voltage_ratio = HLW_UREF; - Hlw.current_ratio = HLW_IREF; - } - - if (pin[GPIO_NRG_SEL] < 99) { - pinMode(pin[GPIO_NRG_SEL], OUTPUT); - digitalWrite(pin[GPIO_NRG_SEL], Hlw.select_ui_flag); - } - if (pin[GPIO_NRG_CF1] < 99) { - pinMode(pin[GPIO_NRG_CF1], INPUT_PULLUP); - attachInterrupt(pin[GPIO_NRG_CF1], HlwCf1Interrupt, FALLING); - } - pinMode(pin[GPIO_HLW_CF], INPUT_PULLUP); - attachInterrupt(pin[GPIO_HLW_CF], HlwCfInterrupt, FALLING); -} - -void HlwDrvInit(void) -{ - Hlw.model_type = 0; - if (pin[GPIO_HJL_CF] < 99) { - pin[GPIO_HLW_CF] = pin[GPIO_HJL_CF]; - pin[GPIO_HJL_CF] = 99; - Hlw.model_type = 1; - } - - if (pin[GPIO_HLW_CF] < 99) { - - Hlw.ui_flag = true; - if (pin[GPIO_NRG_SEL_INV] < 99) { - pin[GPIO_NRG_SEL] = pin[GPIO_NRG_SEL_INV]; - pin[GPIO_NRG_SEL_INV] = 99; - Hlw.ui_flag = false; - } - - if (pin[GPIO_NRG_CF1] < 99) { - if (99 == pin[GPIO_NRG_SEL]) { - Energy.current_available = false; - } - } else { - Energy.current_available = false; - Energy.voltage_available = false; - } - - energy_flg = XNRG_01; - } -} - -bool HlwCommand(void) -{ - bool serviced = true; - - if ((CMND_POWERCAL == Energy.command_code) || (CMND_VOLTAGECAL == Energy.command_code) || (CMND_CURRENTCAL == Energy.command_code)) { - - } - else if (CMND_POWERSET == Energy.command_code) { - if (XdrvMailbox.data_len && Hlw.cf_power_pulse_length ) { - Settings.energy_power_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data) * 10) * Hlw.cf_power_pulse_length ) / Hlw.power_ratio; - } - } - else if (CMND_VOLTAGESET == Energy.command_code) { - if (XdrvMailbox.data_len && Hlw.cf1_voltage_pulse_length ) { - Settings.energy_voltage_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data) * 10) * Hlw.cf1_voltage_pulse_length ) / Hlw.voltage_ratio; - } - } - else if (CMND_CURRENTSET == Energy.command_code) { - if (XdrvMailbox.data_len && Hlw.cf1_current_pulse_length) { - Settings.energy_current_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data)) * Hlw.cf1_current_pulse_length) / Hlw.current_ratio; - } - } - else serviced = false; - - return serviced; -} - - - - - -bool Xnrg01(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_200_MSECOND: - HlwEvery200ms(); - break; - case FUNC_ENERGY_EVERY_SECOND: - HlwEverySecond(); - break; - case FUNC_COMMAND: - result = HlwCommand(); - break; - case FUNC_INIT: - HlwSnsInit(); - break; - case FUNC_PRE_INIT: - HlwDrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_02_cse7766.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_02_cse7766.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_CSE7766 - - - - - - - -#define XNRG_02 2 - -#define CSE_MAX_INVALID_POWER 128 - -#define CSE_NOT_CALIBRATED 0xAA - -#define CSE_PULSES_NOT_INITIALIZED -1 - -#define CSE_PREF 1000 -#define CSE_UREF 100 - -#define CSE_BUFFER_SIZE 25 - -#include - -TasmotaSerial *CseSerial = nullptr; - -struct CSE { - long voltage_cycle = 0; - long current_cycle = 0; - long power_cycle = 0; - long power_cycle_first = 0; - long cf_pulses = 0; - long cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; - - int byte_counter = 0; - uint8_t *rx_buffer = nullptr; - uint8_t power_invalid = 0; - bool received = false; -} Cse; - -void CseReceived(void) -{ - - - - - - - uint8_t header = Cse.rx_buffer[0]; - if ((header & 0xFC) == 0xFC) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: Abnormal hardware")); - return; - } - - - if (HLW_UREF_PULSE == Settings.energy_voltage_calibration) { - long voltage_coefficient = 191200; - if (CSE_NOT_CALIBRATED != header) { - voltage_coefficient = Cse.rx_buffer[2] << 16 | Cse.rx_buffer[3] << 8 | Cse.rx_buffer[4]; - } - Settings.energy_voltage_calibration = voltage_coefficient / CSE_UREF; - } - if (HLW_IREF_PULSE == Settings.energy_current_calibration) { - long current_coefficient = 16140; - if (CSE_NOT_CALIBRATED != header) { - current_coefficient = Cse.rx_buffer[8] << 16 | Cse.rx_buffer[9] << 8 | Cse.rx_buffer[10]; - } - Settings.energy_current_calibration = current_coefficient; - } - if (HLW_PREF_PULSE == Settings.energy_power_calibration) { - long power_coefficient = 5364000; - if (CSE_NOT_CALIBRATED != header) { - power_coefficient = Cse.rx_buffer[14] << 16 | Cse.rx_buffer[15] << 8 | Cse.rx_buffer[16]; - } - Settings.energy_power_calibration = power_coefficient / CSE_PREF; - } - - uint8_t adjustement = Cse.rx_buffer[20]; - Cse.voltage_cycle = Cse.rx_buffer[5] << 16 | Cse.rx_buffer[6] << 8 | Cse.rx_buffer[7]; - Cse.current_cycle = Cse.rx_buffer[11] << 16 | Cse.rx_buffer[12] << 8 | Cse.rx_buffer[13]; - Cse.power_cycle = Cse.rx_buffer[17] << 16 | Cse.rx_buffer[18] << 8 | Cse.rx_buffer[19]; - Cse.cf_pulses = Cse.rx_buffer[21] << 8 | Cse.rx_buffer[22]; - - if (Energy.power_on) { - if (adjustement & 0x40) { - Energy.voltage[0] = (float)(Settings.energy_voltage_calibration * CSE_UREF) / (float)Cse.voltage_cycle; - } - if (adjustement & 0x10) { - Cse.power_invalid = 0; - if ((header & 0xF2) == 0xF2) { - Energy.active_power[0] = 0; - } else { - if (0 == Cse.power_cycle_first) { Cse.power_cycle_first = Cse.power_cycle; } - if (Cse.power_cycle_first != Cse.power_cycle) { - Cse.power_cycle_first = -1; - Energy.active_power[0] = (float)(Settings.energy_power_calibration * CSE_PREF) / (float)Cse.power_cycle; - } else { - Energy.active_power[0] = 0; - } - } - } else { - if (Cse.power_invalid < Settings.param[P_CSE7766_INVALID_POWER]) { - Cse.power_invalid++; - } else { - Cse.power_cycle_first = 0; - Energy.active_power[0] = 0; - } - } - if (adjustement & 0x20) { - if (0 == Energy.active_power[0]) { - Energy.current[0] = 0; - } else { - Energy.current[0] = (float)Settings.energy_current_calibration / (float)Cse.current_cycle; - } - } - } else { - Cse.power_cycle_first = 0; - Energy.voltage[0] = 0; - Energy.active_power[0] = 0; - Energy.current[0] = 0; - } -} - -bool CseSerialInput(void) -{ - while (CseSerial->available()) { - yield(); - uint8_t serial_in_byte = CseSerial->read(); - - if (Cse.received) { - Cse.rx_buffer[Cse.byte_counter++] = serial_in_byte; - if (24 == Cse.byte_counter) { - - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, Cse.rx_buffer, 24); - - uint8_t checksum = 0; - for (uint32_t i = 2; i < 23; i++) { checksum += Cse.rx_buffer[i]; } - if (checksum == Cse.rx_buffer[23]) { - Energy.data_valid[0] = 0; - CseReceived(); - Cse.received = false; - return true; - } else { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: " D_CHECKSUM_FAILURE)); - do { - memmove(Cse.rx_buffer, Cse.rx_buffer +1, 24); - Cse.byte_counter--; - } while ((Cse.byte_counter > 2) && (0x5A != Cse.rx_buffer[1])); - if (0x5A != Cse.rx_buffer[1]) { - Cse.received = false; - Cse.byte_counter = 0; - } - } - } - } else { - if ((0x5A == serial_in_byte) && (1 == Cse.byte_counter)) { - Cse.received = true; - } else { - Cse.byte_counter = 0; - } - Cse.rx_buffer[Cse.byte_counter++] = serial_in_byte; - } - } -} - - - -void CseEverySecond(void) -{ - if (Energy.data_valid[0] > ENERGY_WATCHDOG) { - Cse.voltage_cycle = 0; - Cse.current_cycle = 0; - Cse.power_cycle = 0; - } else { - long cf_frequency = 0; - - if (CSE_PULSES_NOT_INITIALIZED == Cse.cf_pulses_last_time) { - Cse.cf_pulses_last_time = Cse.cf_pulses; - } else { - if (Cse.cf_pulses < Cse.cf_pulses_last_time) { - cf_frequency = (65536 - Cse.cf_pulses_last_time) + Cse.cf_pulses; - } else { - cf_frequency = Cse.cf_pulses - Cse.cf_pulses_last_time; - } - if (cf_frequency && Energy.active_power[0]) { - unsigned long delta = (cf_frequency * Settings.energy_power_calibration) / 36; - - - - if (delta <= (4000*100/36) * 10 ) { - Cse.cf_pulses_last_time = Cse.cf_pulses; - Energy.kWhtoday_delta += delta; - } - else { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: Load overflow")); - Cse.cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; - } - EnergyUpdateToday(); - } - } - } -} - -void CseSnsInit(void) -{ - - - CseSerial = new TasmotaSerial(pin[GPIO_CSE7766_RX], -1, 1); - if (CseSerial->begin(4800, 2)) { - if (CseSerial->hardwareSerial()) { - SetSerial(4800, TS_SERIAL_8E1); - ClaimSerial(); - } - if (0 == Settings.param[P_CSE7766_INVALID_POWER]) { - Settings.param[P_CSE7766_INVALID_POWER] = CSE_MAX_INVALID_POWER; - } - Cse.power_invalid = Settings.param[P_CSE7766_INVALID_POWER]; - } else { - energy_flg = ENERGY_NONE; - } -} - -void CseDrvInit(void) -{ - Cse.rx_buffer = (uint8_t*)(malloc(CSE_BUFFER_SIZE)); - if (Cse.rx_buffer != nullptr) { - - if (pin[GPIO_CSE7766_RX] < 99) { - energy_flg = XNRG_02; - } - } -} - -bool CseCommand(void) -{ - bool serviced = true; - - if (CMND_POWERSET == Energy.command_code) { - if (XdrvMailbox.data_len && Cse.power_cycle) { - Settings.energy_power_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * Cse.power_cycle) / CSE_PREF; - } - } - else if (CMND_VOLTAGESET == Energy.command_code) { - if (XdrvMailbox.data_len && Cse.voltage_cycle) { - Settings.energy_voltage_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * Cse.voltage_cycle) / CSE_UREF; - } - } - else if (CMND_CURRENTSET == Energy.command_code) { - if (XdrvMailbox.data_len && Cse.current_cycle) { - Settings.energy_current_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * Cse.current_cycle) / 1000; - } - } - else serviced = false; - - return serviced; -} - - - - - -bool Xnrg02(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_LOOP: - if (CseSerial) { CseSerialInput(); } - break; - case FUNC_ENERGY_EVERY_SECOND: - CseEverySecond(); - break; - case FUNC_COMMAND: - result = CseCommand(); - break; - case FUNC_INIT: - CseSnsInit(); - break; - case FUNC_PRE_INIT: - CseDrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_03_pzem004t.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_03_pzem004t.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_PZEM004T -# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_03_pzem004t.ino" -#define XNRG_03 3 - -const uint32_t PZEM_STABILIZE = 30; - -#include - -TasmotaSerial *PzemSerial = nullptr; - -#define PZEM_VOLTAGE (uint8_t)0xB0 -#define RESP_VOLTAGE (uint8_t)0xA0 - -#define PZEM_CURRENT (uint8_t)0xB1 -#define RESP_CURRENT (uint8_t)0xA1 - -#define PZEM_POWER (uint8_t)0xB2 -#define RESP_POWER (uint8_t)0xA2 - -#define PZEM_ENERGY (uint8_t)0xB3 -#define RESP_ENERGY (uint8_t)0xA3 - -#define PZEM_SET_ADDRESS (uint8_t)0xB4 -#define RESP_SET_ADDRESS (uint8_t)0xA4 - -#define PZEM_POWER_ALARM (uint8_t)0xB5 -#define RESP_POWER_ALARM (uint8_t)0xA5 - -#define PZEM_DEFAULT_READ_TIMEOUT 500 - - - -struct PZEM { - float energy = 0; - float last_energy = 0; - uint8_t send_retry = 0; - uint8_t read_state = 0; - uint8_t phase = 0; - uint8_t address = 0; -} Pzem; - -struct PZEMCommand { - uint8_t command; - uint8_t addr[4]; - uint8_t data; - uint8_t crc; -}; - -uint8_t PzemCrc(uint8_t *data) -{ - uint16_t crc = 0; - for (uint32_t i = 0; i < sizeof(PZEMCommand) -1; i++) { - crc += *data++; - } - return (uint8_t)(crc & 0xFF); -} - -void PzemSend(uint8_t cmd) -{ - PZEMCommand pzem; - - pzem.command = cmd; - pzem.addr[0] = 192; - pzem.addr[1] = 168; - pzem.addr[2] = 1; - pzem.addr[3] = ((PZEM_SET_ADDRESS == cmd) && Pzem.address) ? Pzem.address : 1 + Pzem.phase; - pzem.data = 0; - - uint8_t *bytes = (uint8_t*)&pzem; - pzem.crc = PzemCrc(bytes); - - PzemSerial->flush(); - PzemSerial->write(bytes, sizeof(pzem)); - - Pzem.address = 0; -} - -bool PzemReceiveReady(void) -{ - return PzemSerial->available() >= (int)sizeof(PZEMCommand); -} - -bool PzemRecieve(uint8_t resp, float *data) -{ -# 124 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_03_pzem004t.ino" - uint8_t buffer[sizeof(PZEMCommand)] = { 0 }; - - unsigned long start = millis(); - uint8_t len = 0; - while ((len < sizeof(PZEMCommand)) && (millis() - start < PZEM_DEFAULT_READ_TIMEOUT)) { - if (PzemSerial->available() > 0) { - uint8_t c = (uint8_t)PzemSerial->read(); - if (!len && ((c & 0xF8) != 0xA0)) { - continue; - } - buffer[len++] = c; - } - } - - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, len); - - if (len != sizeof(PZEMCommand)) { - - return false; - } - if (buffer[6] != PzemCrc(buffer)) { - - return false; - } - if (buffer[0] != resp) { - - return false; - } - - switch (resp) { - case RESP_VOLTAGE: - *data = (float)(buffer[1] << 8) + buffer[2] + (buffer[3] / 10.0); - break; - case RESP_CURRENT: - *data = (float)(buffer[1] << 8) + buffer[2] + (buffer[3] / 100.0); - break; - case RESP_POWER: - *data = (float)(buffer[1] << 8) + buffer[2]; - break; - case RESP_ENERGY: - *data = (float)((uint32_t)buffer[1] << 16) + ((uint16_t)buffer[2] << 8) + buffer[3]; - break; - } - return true; -} - - - -const uint8_t pzem_commands[] { PZEM_SET_ADDRESS, PZEM_VOLTAGE, PZEM_CURRENT, PZEM_POWER, PZEM_ENERGY }; -const uint8_t pzem_responses[] { RESP_SET_ADDRESS, RESP_VOLTAGE, RESP_CURRENT, RESP_POWER, RESP_ENERGY }; - -void PzemEvery250ms(void) -{ - bool data_ready = PzemReceiveReady(); - - if (data_ready) { - float value = 0; - if (PzemRecieve(pzem_responses[Pzem.read_state], &value)) { - Energy.data_valid[Pzem.phase] = 0; - switch (Pzem.read_state) { - case 1: - Energy.voltage[Pzem.phase] = value; - break; - case 2: - Energy.current[Pzem.phase] = value; - break; - case 3: - Energy.active_power[Pzem.phase] = value; - break; - case 4: - Pzem.energy += value; - if (Pzem.phase == Energy.phase_count -1) { - if (Pzem.energy > Pzem.last_energy) { - if (uptime > PZEM_STABILIZE) { - EnergyUpdateTotal(Pzem.energy, false); - } - Pzem.last_energy = Pzem.energy; - } - Pzem.energy = 0; - } - break; - } - Pzem.read_state++; - if (5 == Pzem.read_state) { - Pzem.read_state = 1; - } - - - } - } - - if (0 == Pzem.send_retry || data_ready) { - if (1 == Pzem.read_state) { - if (0 == Pzem.phase) { - Pzem.phase = Energy.phase_count -1; - } else { - Pzem.phase--; - } - - - } - - if (Pzem.address) { - Pzem.read_state = 0; - } - - Pzem.send_retry = 5; - PzemSend(pzem_commands[Pzem.read_state]); - } - else { - Pzem.send_retry--; - if ((Energy.phase_count > 1) && (0 == Pzem.send_retry) && (uptime < PZEM_STABILIZE)) { - Energy.phase_count--; - } - } -} - -void PzemSnsInit(void) -{ - - PzemSerial = new TasmotaSerial(pin[GPIO_PZEM004_RX], pin[GPIO_PZEM0XX_TX], 1); - if (PzemSerial->begin(9600)) { - if (PzemSerial->hardwareSerial()) { - ClaimSerial(); - } - Energy.phase_count = 3; - Pzem.phase = 0; - Pzem.read_state = 1; - } else { - energy_flg = ENERGY_NONE; - } -} - -void PzemDrvInit(void) -{ - if ((pin[GPIO_PZEM004_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { - energy_flg = XNRG_03; - } -} - -bool PzemCommand(void) -{ - bool serviced = true; - - if (CMND_MODULEADDRESS == Energy.command_code) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4)) { - Pzem.address = XdrvMailbox.payload; - } - } - else serviced = false; - - return serviced; -} - - - - - -bool Xnrg03(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_250_MSECOND: - if (PzemSerial && (uptime > 4)) { PzemEvery250ms(); } - break; - case FUNC_COMMAND: - result = PzemCommand(); - break; - case FUNC_INIT: - PzemSnsInit(); - break; - case FUNC_PRE_INIT: - PzemDrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_04_mcp39f501.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_04_mcp39f501.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_MCP39F501 - - - - - - - -#define XNRG_04 4 - -#define MCP_BAUDRATE 4800 -#define MCP_TIMEOUT 4 -#define MCP_CALIBRATION_TIMEOUT 2 - -#define MCP_CALIBRATE_POWER 0x001 -#define MCP_CALIBRATE_VOLTAGE 0x002 -#define MCP_CALIBRATE_CURRENT 0x004 -#define MCP_CALIBRATE_FREQUENCY 0x008 -#define MCP_SINGLE_WIRE_FLAG 0x100 - -#define MCP_START_FRAME 0xA5 -#define MCP_ACK_FRAME 0x06 -#define MCP_ERROR_NAK 0x15 -#define MCP_ERROR_CRC 0x51 - -#define MCP_SINGLE_WIRE 0xAB - -#define MCP_SET_ADDRESS 0x41 - -#define MCP_READ 0x4E -#define MCP_READ_16 0x52 -#define MCP_READ_32 0x44 - -#define MCP_WRITE 0x4D -#define MCP_WRITE_16 0x57 -#define MCP_WRITE_32 0x45 - -#define MCP_SAVE_REGISTERS 0x53 - -#define MCP_CALIBRATION_BASE 0x0028 -#define MCP_CALIBRATION_LEN 52 - -#define MCP_FREQUENCY_REF_BASE 0x0094 -#define MCP_FREQUENCY_GAIN_BASE 0x00AE -#define MCP_FREQUENCY_LEN 4 - -#define MCP_BUFFER_SIZE 60 - -#include -TasmotaSerial *McpSerial = nullptr; - -typedef struct mcp_cal_registers_type { - uint16_t gain_current_rms; - uint16_t gain_voltage_rms; - uint16_t gain_active_power; - uint16_t gain_reactive_power; - sint32_t offset_current_rms; - sint32_t offset_active_power; - sint32_t offset_reactive_power; - sint16_t dc_offset_current; - sint16_t phase_compensation; - uint16_t apparent_power_divisor; - - uint32_t system_configuration; - uint16_t dio_configuration; - uint32_t range; - - uint32_t calibration_current; - uint16_t calibration_voltage; - uint32_t calibration_active_power; - uint32_t calibration_reactive_power; - uint16_t accumulation_interval; -} mcp_cal_registers_type; - -char *mcp_buffer = nullptr; -unsigned long mcp_window = 0; -unsigned long mcp_kWhcounter = 0; -uint32_t mcp_system_configuration = 0x03000000; -uint32_t mcp_active_power; - - -uint32_t mcp_current_rms; -uint16_t mcp_voltage_rms; -uint16_t mcp_line_frequency; - -uint8_t mcp_address = 0; -uint8_t mcp_calibration_active = 0; -uint8_t mcp_init = 0; -uint8_t mcp_timeout = 0; -uint8_t mcp_calibrate = 0; -uint8_t mcp_byte_counter = 0; - - - - - - -uint8_t McpChecksum(uint8_t *data) -{ - uint8_t checksum = 0; - uint8_t offset = 0; - uint8_t len = data[1] -1; - - for (uint32_t i = offset; i < len; i++) { checksum += data[i]; } - return checksum; -} - -unsigned long McpExtractInt(char *data, uint8_t offset, uint8_t size) -{ - unsigned long result = 0; - unsigned long pow = 1; - - for (uint32_t i = 0; i < size; i++) { - result = result + (uint8_t)data[offset + i] * pow; - pow = pow * 256; - } - return result; -} - -void McpSetInt(unsigned long value, uint8_t *data, uint8_t offset, size_t size) -{ - for (uint32_t i = 0; i < size; i++) { - data[offset + i] = ((value >> (i * 8)) & 0xFF); - } -} - -void McpSend(uint8_t *data) -{ - if (mcp_timeout) { return; } - mcp_timeout = MCP_TIMEOUT; - - data[0] = MCP_START_FRAME; - data[data[1] -1] = McpChecksum(data); - - - - for (uint32_t i = 0; i < data[1]; i++) { - McpSerial->write(data[i]); - } -} - - - -void McpGetAddress(void) -{ - uint8_t data[] = { MCP_START_FRAME, 7, MCP_SET_ADDRESS, 0x00, 0x26, MCP_READ_16, 0x00 }; - - McpSend(data); -} - -void McpAddressReceive(void) -{ - - mcp_address = mcp_buffer[3]; -} - - - -void McpGetCalibration(void) -{ - if (mcp_calibration_active) { return; } - mcp_calibration_active = MCP_CALIBRATION_TIMEOUT; - - uint8_t data[] = { MCP_START_FRAME, 8, MCP_SET_ADDRESS, (MCP_CALIBRATION_BASE >> 8) & 0xFF, MCP_CALIBRATION_BASE & 0xFF, MCP_READ, MCP_CALIBRATION_LEN, 0x00 }; - - McpSend(data); -} - -void McpParseCalibration(void) -{ - bool action = false; - mcp_cal_registers_type cal_registers; - - - cal_registers.gain_current_rms = McpExtractInt(mcp_buffer, 2, 2); - cal_registers.gain_voltage_rms = McpExtractInt(mcp_buffer, 4, 2); - cal_registers.gain_active_power = McpExtractInt(mcp_buffer, 6, 2); - cal_registers.gain_reactive_power = McpExtractInt(mcp_buffer, 8, 2); - cal_registers.offset_current_rms = McpExtractInt(mcp_buffer, 10, 4); - cal_registers.offset_active_power = McpExtractInt(mcp_buffer, 14, 4); - cal_registers.offset_reactive_power = McpExtractInt(mcp_buffer, 18, 4); - cal_registers.dc_offset_current = McpExtractInt(mcp_buffer, 22, 2); - cal_registers.phase_compensation = McpExtractInt(mcp_buffer, 24, 2); - cal_registers.apparent_power_divisor = McpExtractInt(mcp_buffer, 26, 2); - - cal_registers.system_configuration = McpExtractInt(mcp_buffer, 28, 4); - cal_registers.dio_configuration = McpExtractInt(mcp_buffer, 32, 2); - cal_registers.range = McpExtractInt(mcp_buffer, 34, 4); - - cal_registers.calibration_current = McpExtractInt(mcp_buffer, 38, 4); - cal_registers.calibration_voltage = McpExtractInt(mcp_buffer, 42, 2); - cal_registers.calibration_active_power = McpExtractInt(mcp_buffer, 44, 4); - cal_registers.calibration_reactive_power = McpExtractInt(mcp_buffer, 48, 4); - cal_registers.accumulation_interval = McpExtractInt(mcp_buffer, 52, 2); - - if (mcp_calibrate & MCP_CALIBRATE_POWER) { - cal_registers.calibration_active_power = Settings.energy_power_calibration; - if (McpCalibrationCalc(&cal_registers, 16)) { action = true; } - } - if (mcp_calibrate & MCP_CALIBRATE_VOLTAGE) { - cal_registers.calibration_voltage = Settings.energy_voltage_calibration; - if (McpCalibrationCalc(&cal_registers, 0)) { action = true; } - } - if (mcp_calibrate & MCP_CALIBRATE_CURRENT) { - cal_registers.calibration_current = Settings.energy_current_calibration; - if (McpCalibrationCalc(&cal_registers, 8)) { action = true; } - } - mcp_timeout = 0; - if (action) { McpSetCalibration(&cal_registers); } - - mcp_calibrate = 0; - - Settings.energy_power_calibration = cal_registers.calibration_active_power; - Settings.energy_voltage_calibration = cal_registers.calibration_voltage; - Settings.energy_current_calibration = cal_registers.calibration_current; - - mcp_system_configuration = cal_registers.system_configuration; - - if (mcp_system_configuration & MCP_SINGLE_WIRE_FLAG) { - mcp_system_configuration &= ~MCP_SINGLE_WIRE_FLAG; - McpSetSystemConfiguration(2); - } -} - -bool McpCalibrationCalc(struct mcp_cal_registers_type *cal_registers, uint8_t range_shift) -{ - uint32_t measured; - uint32_t expected; - uint16_t *gain; - uint32_t new_gain; - - if (range_shift == 0) { - measured = mcp_voltage_rms; - expected = cal_registers->calibration_voltage; - gain = &(cal_registers->gain_voltage_rms); - } else if (range_shift == 8) { - measured = mcp_current_rms; - expected = cal_registers->calibration_current; - gain = &(cal_registers->gain_current_rms); - } else if (range_shift == 16) { - measured = mcp_active_power; - expected = cal_registers->calibration_active_power; - gain = &(cal_registers->gain_active_power); - } else { - return false; - } - - if (measured == 0) { - return false; - } - - uint32_t range = (cal_registers->range >> range_shift) & 0xFF; - -calc: - new_gain = (*gain) * expected / measured; - - if (new_gain < 25000) { - range++; - if (measured > 6) { - measured = measured / 2; - goto calc; - } - } - - if (new_gain > 55000) { - range--; - measured = measured * 2; - goto calc; - } - - *gain = new_gain; - uint32_t old_range = (cal_registers->range >> range_shift) & 0xFF; - cal_registers->range = cal_registers->range ^ (old_range << range_shift); - cal_registers->range = cal_registers->range | (range << range_shift); - - return true; -} - - - - - - -void McpSetCalibration(struct mcp_cal_registers_type *cal_registers) -{ - uint8_t data[7 + MCP_CALIBRATION_LEN + 2 + 1]; - - data[1] = sizeof(data); - data[2] = MCP_SET_ADDRESS; - data[3] = (MCP_CALIBRATION_BASE >> 8) & 0xFF; - data[4] = (MCP_CALIBRATION_BASE >> 0) & 0xFF; - - data[5] = MCP_WRITE; - data[6] = MCP_CALIBRATION_LEN; - - McpSetInt(cal_registers->gain_current_rms, data, 0+7, 2); - McpSetInt(cal_registers->gain_voltage_rms, data, 2+7, 2); - McpSetInt(cal_registers->gain_active_power, data, 4+7, 2); - McpSetInt(cal_registers->gain_reactive_power, data, 6+7, 2); - McpSetInt(cal_registers->offset_current_rms, data, 8+7, 4); - McpSetInt(cal_registers->offset_active_power, data, 12+7, 4); - McpSetInt(cal_registers->offset_reactive_power, data, 16+7, 4); - McpSetInt(cal_registers->dc_offset_current, data, 20+7, 2); - McpSetInt(cal_registers->phase_compensation, data, 22+7, 2); - McpSetInt(cal_registers->apparent_power_divisor, data, 24+7, 2); - - McpSetInt(cal_registers->system_configuration, data, 26+7, 4); - McpSetInt(cal_registers->dio_configuration, data, 30+7, 2); - McpSetInt(cal_registers->range, data, 32+7, 4); - - McpSetInt(cal_registers->calibration_current, data, 36+7, 4); - McpSetInt(cal_registers->calibration_voltage, data, 40+7, 2); - McpSetInt(cal_registers->calibration_active_power, data, 42+7, 4); - McpSetInt(cal_registers->calibration_reactive_power, data, 46+7, 4); - McpSetInt(cal_registers->accumulation_interval, data, 50+7, 2); - - data[MCP_CALIBRATION_LEN+7] = MCP_SAVE_REGISTERS; - data[MCP_CALIBRATION_LEN+8] = mcp_address; - - McpSend(data); -} - - - -void McpSetSystemConfiguration(uint16 interval) -{ - - uint8_t data[17]; - - data[ 1] = sizeof(data); - data[ 2] = MCP_SET_ADDRESS; - data[ 3] = 0x00; - data[ 4] = 0x42; - data[ 5] = MCP_WRITE_32; - data[ 6] = (mcp_system_configuration >> 24) & 0xFF; - data[ 7] = (mcp_system_configuration >> 16) & 0xFF; - data[ 8] = (mcp_system_configuration >> 8) & 0xFF; - data[ 9] = (mcp_system_configuration >> 0) & 0xFF; - data[10] = MCP_SET_ADDRESS; - data[11] = 0x00; - data[12] = 0x5A; - data[13] = MCP_WRITE_16; - data[14] = (interval >> 8) & 0xFF; - data[15] = (interval >> 0) & 0xFF; - - McpSend(data); -} - - - -void McpGetFrequency(void) -{ - if (mcp_calibration_active) { return; } - mcp_calibration_active = MCP_CALIBRATION_TIMEOUT; - - uint8_t data[] = { MCP_START_FRAME, 11, MCP_SET_ADDRESS, (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF, MCP_FREQUENCY_REF_BASE & 0xFF, MCP_READ_16, - MCP_SET_ADDRESS, (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF, MCP_FREQUENCY_GAIN_BASE & 0xFF, MCP_READ_16, 0x00 }; - - McpSend(data); -} - -void McpParseFrequency(void) -{ - - uint16_t line_frequency_ref = mcp_buffer[2] * 256 + mcp_buffer[3]; - uint16_t gain_line_frequency = mcp_buffer[4] * 256 + mcp_buffer[5]; - - if (mcp_calibrate & MCP_CALIBRATE_FREQUENCY) { - line_frequency_ref = Settings.energy_frequency_calibration; - - if ((0xFFFF == mcp_line_frequency) || (0 == gain_line_frequency)) { - mcp_line_frequency = 50000; - gain_line_frequency = 0x8000; - } - gain_line_frequency = gain_line_frequency * line_frequency_ref / mcp_line_frequency; - - mcp_timeout = 0; - McpSetFrequency(line_frequency_ref, gain_line_frequency); - } - - Settings.energy_frequency_calibration = line_frequency_ref; - - mcp_calibrate = 0; -} - -void McpSetFrequency(uint16_t line_frequency_ref, uint16_t gain_line_frequency) -{ - - uint8_t data[17]; - - data[ 1] = sizeof(data); - data[ 2] = MCP_SET_ADDRESS; - data[ 3] = (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF; - data[ 4] = (MCP_FREQUENCY_REF_BASE >> 0) & 0xFF; - - data[ 5] = MCP_WRITE_16; - data[ 6] = (line_frequency_ref >> 8) & 0xFF; - data[ 7] = (line_frequency_ref >> 0) & 0xFF; - - data[ 8] = MCP_SET_ADDRESS; - data[ 9] = (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF; - data[10] = (MCP_FREQUENCY_GAIN_BASE >> 0) & 0xFF; - - data[11] = MCP_WRITE_16; - data[12] = (gain_line_frequency >> 8) & 0xFF; - data[13] = (gain_line_frequency >> 0) & 0xFF; - - data[14] = MCP_SAVE_REGISTERS; - data[15] = mcp_address; - - McpSend(data); -} - - - -void McpGetData(void) -{ - uint8_t data[] = { MCP_START_FRAME, 8, MCP_SET_ADDRESS, 0x00, 0x04, MCP_READ, 22, 0x00 }; - - McpSend(data); -} - -void McpParseData(void) -{ - - - - - - mcp_current_rms = McpExtractInt(mcp_buffer, 2, 4); - mcp_voltage_rms = McpExtractInt(mcp_buffer, 6, 2); - mcp_active_power = McpExtractInt(mcp_buffer, 8, 4); - - - mcp_line_frequency = McpExtractInt(mcp_buffer, 22, 2); - - if (Energy.power_on) { - Energy.data_valid[0] = 0; - Energy.frequency[0] = (float)mcp_line_frequency / 1000; - Energy.voltage[0] = (float)mcp_voltage_rms / 10; - Energy.active_power[0] = (float)mcp_active_power / 100; - if (0 == Energy.active_power[0]) { - Energy.current[0] = 0; - } else { - Energy.current[0] = (float)mcp_current_rms / 10000; - } - } else { - Energy.data_valid[0] = ENERGY_WATCHDOG; - } -} - - - -void McpSerialInput(void) -{ - while ((McpSerial->available()) && (mcp_byte_counter < MCP_BUFFER_SIZE)) { - yield(); - mcp_buffer[mcp_byte_counter++] = McpSerial->read(); - mcp_window = millis(); - } - - - if ((mcp_byte_counter) && (millis() - mcp_window > (24000 / MCP_BAUDRATE) +1)) { - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, (uint8_t*)mcp_buffer, mcp_byte_counter); - - if (MCP_BUFFER_SIZE == mcp_byte_counter) { - - } - else if (1 == mcp_byte_counter) { - if (MCP_ERROR_CRC == mcp_buffer[0]) { - - mcp_timeout = 0; - } - else if (MCP_ERROR_NAK == mcp_buffer[0]) { - - mcp_timeout = 0; - } - } - else if (MCP_ACK_FRAME == mcp_buffer[0]) { - if (mcp_byte_counter == mcp_buffer[1]) { - - if (McpChecksum((uint8_t *)mcp_buffer) != mcp_buffer[mcp_byte_counter -1]) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: " D_CHECKSUM_FAILURE)); - } else { - if (5 == mcp_buffer[1]) { McpAddressReceive(); } - if (25 == mcp_buffer[1]) { McpParseData(); } - if (MCP_CALIBRATION_LEN + 3 == mcp_buffer[1]) { McpParseCalibration(); } - if (MCP_FREQUENCY_LEN + 3 == mcp_buffer[1]) { McpParseFrequency(); } - } - - } - mcp_timeout = 0; - } - else if (MCP_SINGLE_WIRE == mcp_buffer[0]) { - mcp_timeout = 0; - } - - mcp_byte_counter = 0; - McpSerial->flush(); - } -} - - - -void McpEverySecond(void) -{ - if (Energy.data_valid[0] > ENERGY_WATCHDOG) { - mcp_voltage_rms = 0; - mcp_current_rms = 0; - mcp_active_power = 0; - mcp_line_frequency = 0; - } - - if (mcp_active_power) { - Energy.kWhtoday_delta += ((mcp_active_power * 10) / 36); - EnergyUpdateToday(); - } - - if (mcp_timeout) { - mcp_timeout--; - } - else if (mcp_calibration_active) { - mcp_calibration_active--; - } - else if (mcp_init) { - if (2 == mcp_init) { - McpGetCalibration(); - } - else if (1 == mcp_init) { - McpGetFrequency(); - } - mcp_init--; - } - else if (!mcp_address) { - McpGetAddress(); - } - else { - McpGetData(); - } -} - -void McpSnsInit(void) -{ - - McpSerial = new TasmotaSerial(pin[GPIO_MCP39F5_RX], pin[GPIO_MCP39F5_TX], 1); - if (McpSerial->begin(MCP_BAUDRATE)) { - if (McpSerial->hardwareSerial()) { - ClaimSerial(); - mcp_buffer = serial_in_buffer; - } else { - mcp_buffer = (char*)(malloc(MCP_BUFFER_SIZE)); - } - DigitalWrite(GPIO_MCP39F5_RST, 1); - } else { - energy_flg = ENERGY_NONE; - } -} - -void McpDrvInit(void) -{ - if ((pin[GPIO_MCP39F5_RX] < 99) && (pin[GPIO_MCP39F5_TX] < 99)) { - if (pin[GPIO_MCP39F5_RST] < 99) { - pinMode(pin[GPIO_MCP39F5_RST], OUTPUT); - digitalWrite(pin[GPIO_MCP39F5_RST], 0); - } - mcp_calibrate = 0; - mcp_timeout = 2; - mcp_init = 2; - energy_flg = XNRG_04; - } -} - -bool McpCommand(void) -{ - bool serviced = true; - unsigned long value = 0; - - if (CMND_POWERSET == Energy.command_code) { - if (XdrvMailbox.data_len && mcp_active_power) { - value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 100); - if ((value > 100) && (value < 200000)) { - Settings.energy_power_calibration = value; - mcp_calibrate |= MCP_CALIBRATE_POWER; - McpGetCalibration(); - } - } - } - else if (CMND_VOLTAGESET == Energy.command_code) { - if (XdrvMailbox.data_len && mcp_voltage_rms) { - value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 10); - if ((value > 1000) && (value < 2600)) { - Settings.energy_voltage_calibration = value; - mcp_calibrate |= MCP_CALIBRATE_VOLTAGE; - McpGetCalibration(); - } - } - } - else if (CMND_CURRENTSET == Energy.command_code) { - if (XdrvMailbox.data_len && mcp_current_rms) { - value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 10); - if ((value > 100) && (value < 80000)) { - Settings.energy_current_calibration = value; - mcp_calibrate |= MCP_CALIBRATE_CURRENT; - McpGetCalibration(); - } - } - } - else if (CMND_FREQUENCYSET == Energy.command_code) { - if (XdrvMailbox.data_len && mcp_line_frequency) { - value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 1000); - if ((value > 45000) && (value < 65000)) { - Settings.energy_frequency_calibration = value; - mcp_calibrate |= MCP_CALIBRATE_FREQUENCY; - McpGetFrequency(); - } - } - } - else serviced = false; - - return serviced; -} - - - - - -bool Xnrg04(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_LOOP: - if (McpSerial) { McpSerialInput(); } - break; - case FUNC_ENERGY_EVERY_SECOND: - if (McpSerial) { McpEverySecond(); } - break; - case FUNC_COMMAND: - result = McpCommand(); - break; - case FUNC_INIT: - McpSnsInit(); - break; - case FUNC_PRE_INIT: - McpDrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_05_pzem_ac.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_05_pzem_ac.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_PZEM_AC -# 33 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_05_pzem_ac.ino" -#define XNRG_05 5 - -const uint8_t PZEM_AC_DEVICE_ADDRESS = 0x01; -const uint32_t PZEM_AC_STABILIZE = 30; - -#include -TasmotaModbus *PzemAcModbus; - -struct PZEMAC { - float energy = 0; - float last_energy = 0; - uint8_t send_retry = 0; - uint8_t phase = 0; - uint8_t address = 0; - uint8_t address_step = ADDR_IDLE; -} PzemAc; - -void PzemAcEverySecond(void) -{ - bool data_ready = PzemAcModbus->ReceiveReady(); - - if (data_ready) { - uint8_t buffer[30]; - - uint8_t registers = 10; - if (ADDR_RECEIVE == PzemAc.address_step) { - registers = 2; - PzemAc.address_step--; - } - uint8_t error = PzemAcModbus->ReceiveBuffer(buffer, registers); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemAcModbus->ReceiveCount()); - - if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAC: PzemAc %d error %d"), PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, error); - } else { - Energy.data_valid[PzemAc.phase] = 0; - if (10 == registers) { - - - - - - Energy.voltage[PzemAc.phase] = (float)((buffer[3] << 8) + buffer[4]) / 10.0; - Energy.current[PzemAc.phase] = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; - Energy.active_power[PzemAc.phase] = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; - Energy.frequency[PzemAc.phase] = (float)((buffer[17] << 8) + buffer[18]) / 10.0; - Energy.power_factor[PzemAc.phase] = (float)((buffer[19] << 8) + buffer[20]) / 100.0; - - PzemAc.energy += (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); - if (PzemAc.phase == Energy.phase_count -1) { - if (PzemAc.energy > PzemAc.last_energy) { - if (uptime > PZEM_AC_STABILIZE) { - EnergyUpdateTotal(PzemAc.energy, false); - } - PzemAc.last_energy = PzemAc.energy; - } - PzemAc.energy = 0; - } - - } - } - } - - if (0 == PzemAc.send_retry || data_ready) { - if (0 == PzemAc.phase) { - PzemAc.phase = Energy.phase_count -1; - } else { - PzemAc.phase--; - } - PzemAc.send_retry = ENERGY_WATCHDOG; - if (ADDR_SEND == PzemAc.address_step) { - PzemAcModbus->Send(0xF8, 0x06, 0x0002, (uint16_t)PzemAc.address); - PzemAc.address_step--; - } else { - PzemAcModbus->Send(PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, 0x04, 0, 10); - } - } - else { - PzemAc.send_retry--; - if ((Energy.phase_count > 1) && (0 == PzemAc.send_retry) && (uptime < PZEM_AC_STABILIZE)) { - Energy.phase_count--; - } - } -} - -void PzemAcSnsInit(void) -{ - PzemAcModbus = new TasmotaModbus(pin[GPIO_PZEM016_RX], pin[GPIO_PZEM0XX_TX]); - uint8_t result = PzemAcModbus->Begin(9600); - if (result) { - if (2 == result) { ClaimSerial(); } - Energy.phase_count = 3; - PzemAc.phase = 0; - } else { - energy_flg = ENERGY_NONE; - } -} - -void PzemAcDrvInit(void) -{ - if ((pin[GPIO_PZEM016_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { - energy_flg = XNRG_05; - } -} - -bool PzemAcCommand(void) -{ - bool serviced = true; - - if (CMND_MODULEADDRESS == Energy.command_code) { - PzemAc.address = XdrvMailbox.payload; - PzemAc.address_step = ADDR_SEND; - } - else serviced = false; - - return serviced; -} - - - - - -bool Xnrg05(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_ENERGY_EVERY_SECOND: - if (uptime > 4) { PzemAcEverySecond(); } - break; - case FUNC_COMMAND: - result = PzemAcCommand(); - break; - case FUNC_INIT: - PzemAcSnsInit(); - break; - case FUNC_PRE_INIT: - PzemAcDrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_06_pzem_dc.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_06_pzem_dc.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_PZEM_DC -# 32 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_06_pzem_dc.ino" -#define XNRG_06 6 - -const uint8_t PZEM_DC_DEVICE_ADDRESS = 0x01; -const uint32_t PZEM_DC_STABILIZE = 30; - -#include -TasmotaModbus *PzemDcModbus; - -struct PZEMDC { - float energy = 0; - float last_energy = 0; - uint8_t send_retry = 0; - uint8_t channel = 0; - uint8_t address = 0; - uint8_t address_step = ADDR_IDLE; -} PzemDc; - -void PzemDcEverySecond(void) -{ - bool data_ready = PzemDcModbus->ReceiveReady(); - - if (data_ready) { - uint8_t buffer[26]; - - uint8_t registers = 8; - if (ADDR_RECEIVE == PzemDc.address_step) { - registers = 2; - PzemDc.address_step--; - } - uint8_t error = PzemDcModbus->ReceiveBuffer(buffer, registers); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemDcModbus->ReceiveCount()); - - if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PDC: PzemDc %d error %d"), PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, error); - } else { - Energy.data_valid[PzemDc.channel] = 0; - if (8 == registers) { - - - - - - Energy.voltage[PzemDc.channel] = (float)((buffer[3] << 8) + buffer[4]) / 100.0; - Energy.current[PzemDc.channel] = (float)((buffer[5] << 8) + buffer[6]) / 100.0; - Energy.active_power[PzemDc.channel] = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; - - PzemDc.energy += (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); - if (PzemDc.channel == Energy.phase_count -1) { - if (PzemDc.energy > PzemDc.last_energy) { - if (uptime > PZEM_DC_STABILIZE) { - EnergyUpdateTotal(PzemDc.energy, false); - } - PzemDc.last_energy = PzemDc.energy; - } - PzemDc.energy = 0; - } - } - } - } - - if (0 == PzemDc.send_retry || data_ready) { - if (0 == PzemDc.channel) { - PzemDc.channel = Energy.phase_count -1; - } else { - PzemDc.channel--; - } - PzemDc.send_retry = ENERGY_WATCHDOG; - if (ADDR_SEND == PzemDc.address_step) { - PzemDcModbus->Send(0xF8, 0x06, 0x0002, (uint16_t)PzemDc.address); - PzemDc.address_step--; - } else { - PzemDcModbus->Send(PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, 0x04, 0, 8); - } - } - else { - PzemDc.send_retry--; - if ((Energy.phase_count > 1) && (0 == PzemDc.send_retry) && (uptime < PZEM_DC_STABILIZE)) { - Energy.phase_count--; - } - } -} - -void PzemDcSnsInit(void) -{ - PzemDcModbus = new TasmotaModbus(pin[GPIO_PZEM017_RX], pin[GPIO_PZEM0XX_TX]); - uint8_t result = PzemDcModbus->Begin(9600, 2); - if (result) { - if (2 == result) { ClaimSerial(); } - Energy.type_dc = true; - Energy.phase_count = 3; - PzemDc.channel = 0; - } else { - energy_flg = ENERGY_NONE; - } -} - -void PzemDcDrvInit(void) -{ - if ((pin[GPIO_PZEM017_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { - energy_flg = XNRG_06; - } -} - -bool PzemDcCommand(void) -{ - bool serviced = true; - - if (CMND_MODULEADDRESS == Energy.command_code) { - PzemDc.address = XdrvMailbox.payload; - PzemDc.address_step = ADDR_SEND; - } - else serviced = false; - - return serviced; -} - - - - - -bool Xnrg06(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_ENERGY_EVERY_SECOND: - if (uptime > 4) { PzemDcEverySecond(); } - break; - case FUNC_COMMAND: - result = PzemDcCommand(); - break; - case FUNC_INIT: - PzemDcSnsInit(); - break; - case FUNC_PRE_INIT: - PzemDcDrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_07_ade7953.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_07_ade7953.ino" -#ifdef USE_I2C -#ifdef USE_ENERGY_SENSOR -#ifdef USE_ADE7953 -# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_07_ade7953.ino" -#define XNRG_07 7 -#define XI2C_07 7 - -#define ADE7953_PREF 1540 -#define ADE7953_UREF 26000 -#define ADE7953_IREF 10000 - -#define ADE7953_ADDR 0x38 - -const uint16_t Ade7953Registers[] { - 0x31B, - 0x313, - 0x311, - 0x315, - 0x31A, - 0x312, - 0x310, - 0x314, - 0x31C, - 0x10E -}; - -struct Ade7953 { - uint32_t voltage_rms = 0; - uint32_t period = 0; - uint32_t current_rms[2] = { 0, 0 }; - uint32_t active_power[2] = { 0, 0 }; - uint8_t init_step = 0; -} Ade7953; - -int Ade7953RegSize(uint16_t reg) -{ - int size = 0; - switch ((reg >> 8) & 0x0F) { - case 0x03: - size++; - case 0x02: - size++; - case 0x01: - size++; - case 0x00: - case 0x07: - case 0x08: - size++; - } - return size; -} - -void Ade7953Write(uint16_t reg, uint32_t val) -{ - int size = Ade7953RegSize(reg); - if (size) { - Wire.beginTransmission(ADE7953_ADDR); - Wire.write((reg >> 8) & 0xFF); - Wire.write(reg & 0xFF); - while (size--) { - Wire.write((val >> (8 * size)) & 0xFF); - } - Wire.endTransmission(); - delayMicroseconds(5); - } -} - -int32_t Ade7953Read(uint16_t reg) -{ - uint32_t response = 0; - - int size = Ade7953RegSize(reg); - if (size) { - Wire.beginTransmission(ADE7953_ADDR); - Wire.write((reg >> 8) & 0xFF); - Wire.write(reg & 0xFF); - Wire.endTransmission(0); - Wire.requestFrom(ADE7953_ADDR, size); - if (size <= Wire.available()) { - for (uint32_t i = 0; i < size; i++) { - response = response << 8 | Wire.read(); - } - } - } - return response; -} - -void Ade7953Init(void) -{ - Ade7953Write(0x102, 0x0004); - Ade7953Write(0x0FE, 0x00AD); - Ade7953Write(0x120, 0x0030); -} - -void Ade7953GetData(void) -{ - int32_t reg[2][4]; - for (uint32_t i = 0; i < sizeof(Ade7953Registers)/sizeof(uint16_t); i++) { - int32_t value = Ade7953Read(Ade7953Registers[i]); - if (8 == i) { - Ade7953.voltage_rms = value; - } else if (9 == i) { - Ade7953.period = value; - } else { - reg[i >> 2][i &3] = value; - } - } - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ADE: %d, %d, [%d, %d, %d, %d], [%d, %d, %d, %d]"), - Ade7953.voltage_rms, Ade7953.period, - reg[0][0], reg[0][1], reg[0][2], reg[0][3], - reg[1][0], reg[1][1], reg[1][2], reg[1][3]); - - uint32_t apparent_power[2] = { 0, 0 }; - uint32_t reactive_power[2] = { 0, 0 }; - - for (uint32_t channel = 0; channel < 2; channel++) { - Ade7953.current_rms[channel] = reg[channel][0]; - if (Ade7953.current_rms[channel] < 2000) { - Ade7953.current_rms[channel] = 0; - Ade7953.active_power[channel] = 0; - } else { - Ade7953.active_power[channel] = abs(reg[channel][1]); - apparent_power[channel] = abs(reg[channel][2]); - reactive_power[channel] = abs(reg[channel][3]); - } - } - - uint32_t current_rms_sum = Ade7953.current_rms[0] + Ade7953.current_rms[1]; - uint32_t active_power_sum = Ade7953.active_power[0] + Ade7953.active_power[1]; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ADE: U %d, C %d, I %d + %d = %d, P %d + %d = %d"), - Ade7953.voltage_rms, Ade7953.period, - Ade7953.current_rms[0], Ade7953.current_rms[1], current_rms_sum, - Ade7953.active_power[0], Ade7953.active_power[1], active_power_sum); - - if (Energy.power_on) { - Energy.voltage[0] = (float)Ade7953.voltage_rms / Settings.energy_voltage_calibration; - Energy.frequency[0] = 223750.0f / ( (float)Ade7953.period + 1); - - for (uint32_t channel = 0; channel < 2; channel++) { - Energy.data_valid[channel] = 0; - Energy.active_power[channel] = (float)Ade7953.active_power[channel] / (Settings.energy_power_calibration / 10); - Energy.reactive_power[channel] = (float)reactive_power[channel] / (Settings.energy_power_calibration / 10); - Energy.apparent_power[channel] = (float)apparent_power[channel] / (Settings.energy_power_calibration / 10); - if (0 == Energy.active_power[channel]) { - Energy.current[channel] = 0; - } else { - Energy.current[channel] = (float)Ade7953.current_rms[channel] / (Settings.energy_current_calibration * 10); - } - } - } else { - Energy.data_valid[0] = ENERGY_WATCHDOG; - Energy.data_valid[1] = ENERGY_WATCHDOG; - } - - if (active_power_sum) { - Energy.kWhtoday_delta += ((active_power_sum * (100000 / (Settings.energy_power_calibration / 10))) / 3600); - EnergyUpdateToday(); - } -} - -void Ade7953EnergyEverySecond(void) -{ - if (Ade7953.init_step) { - if (1 == Ade7953.init_step) { - Ade7953Init(); - } - Ade7953.init_step--; - } else { - Ade7953GetData(); - } -} - -void Ade7953DrvInit(void) -{ - if (pin[GPIO_ADE7953_IRQ] < 99) { - delay(100); - if (I2cSetDevice(ADE7953_ADDR)) { - if (HLW_PREF_PULSE == Settings.energy_power_calibration) { - Settings.energy_power_calibration = ADE7953_PREF; - Settings.energy_voltage_calibration = ADE7953_UREF; - Settings.energy_current_calibration = ADE7953_IREF; - } - I2cSetActiveFound(ADE7953_ADDR, "ADE7953"); - Ade7953.init_step = 2; - - Energy.phase_count = 2; - Energy.voltage_common = true; - - energy_flg = XNRG_07; - } - } -} - -bool Ade7953Command(void) -{ - bool serviced = true; - - uint32_t channel = (2 == XdrvMailbox.index) ? 1 : 0; - uint32_t value = (uint32_t)(CharToFloat(XdrvMailbox.data) * 100); - - if (CMND_POWERCAL == Energy.command_code) { - if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_PREF; } - - } - else if (CMND_VOLTAGECAL == Energy.command_code) { - if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_UREF; } - - } - else if (CMND_CURRENTCAL == Energy.command_code) { - if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_IREF; } - - } - else if (CMND_POWERSET == Energy.command_code) { - if (XdrvMailbox.data_len && Ade7953.active_power[channel]) { - if ((value > 100) && (value < 200000)) { - Settings.energy_power_calibration = (Ade7953.active_power[channel] * 1000) / value; - } - } - } - else if (CMND_VOLTAGESET == Energy.command_code) { - if (XdrvMailbox.data_len && Ade7953.voltage_rms) { - if ((value > 10000) && (value < 26000)) { - Settings.energy_voltage_calibration = (Ade7953.voltage_rms * 100) / value; - } - } - } - else if (CMND_CURRENTSET == Energy.command_code) { - if (XdrvMailbox.data_len && Ade7953.current_rms[channel]) { - if ((value > 2000) && (value < 1000000)) { - Settings.energy_current_calibration = ((Ade7953.current_rms[channel] * 100) / value) * 100; - } - } - } - else serviced = false; - - return serviced; -} - - - - - -bool Xnrg07(uint8_t function) -{ - if (!I2cEnabled(XI2C_07)) { return false; } - - bool result = false; - - switch (function) { - case FUNC_ENERGY_EVERY_SECOND: - Ade7953EnergyEverySecond(); - break; - case FUNC_COMMAND: - result = Ade7953Command(); - break; - case FUNC_PRE_INIT: - Ade7953DrvInit(); - break; - } - return result; -} - -#endif -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_08_sdm120.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_08_sdm120.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_SDM120 - - - - - - -#define XNRG_08 8 - - -#ifndef SDM120_SPEED - #define SDM120_SPEED 2400 -#endif - -#ifndef SDM120_ADDR - #define SDM120_ADDR 1 -#endif - -#include -TasmotaModbus *Sdm120Modbus; - -const uint8_t sdm120_table = 8; -const uint8_t sdm220_table = 13; - -const uint16_t sdm120_start_addresses[] { - 0x0000, - 0x0006, - 0x000C, - 0x0012, - 0x0018, - 0x001E, - 0x0046, - 0x0156, - - 0X0048, - 0X004A, - 0X004C, - 0X004E, - 0X0024 -}; - -struct SDM120 { - float total_active = 0; - float import_active = NAN; - float import_reactive = 0; - float export_reactive = 0; - float phase_angle = 0; - uint8_t read_state = 0; - uint8_t send_retry = 0; - uint8_t start_address_count = sdm220_table; -} Sdm120; - - - -void SDM120Every250ms(void) -{ - bool data_ready = Sdm120Modbus->ReceiveReady(); - - if (data_ready) { - uint8_t buffer[14]; - - uint32_t error = Sdm120Modbus->ReceiveBuffer(buffer, 2); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Sdm120Modbus->ReceiveCount()); - - if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: SDM120 error %d"), error); - } else { - Energy.data_valid[0] = 0; - - - - - float value; - ((uint8_t*)&value)[3] = buffer[3]; - ((uint8_t*)&value)[2] = buffer[4]; - ((uint8_t*)&value)[1] = buffer[5]; - ((uint8_t*)&value)[0] = buffer[6]; - - switch(Sdm120.read_state) { - case 0: - Energy.voltage[0] = value; - break; - - case 1: - Energy.current[0] = value; - break; - - case 2: - Energy.active_power[0] = value; - break; - - case 3: - Energy.apparent_power[0] = value; - break; - - case 4: - Energy.reactive_power[0] = value; - break; - - case 5: - Energy.power_factor[0] = value; - break; - - case 6: - Energy.frequency[0] = value; - break; - - case 7: - Sdm120.total_active = value; - break; - - case 8: - Sdm120.import_active = value; - break; - - case 9: - Energy.export_active = value; - break; - - case 10: - Sdm120.import_reactive = value; - break; - - case 11: - Sdm120.export_reactive = value; - break; - - case 12: - Sdm120.phase_angle = value; - break; - } - - Sdm120.read_state++; - if (Sdm120.read_state == Sdm120.start_address_count) { - Sdm120.read_state = 0; - - if (Sdm120.start_address_count > sdm120_table) { - if (!isnan(Sdm120.import_active)) { - Sdm120.total_active = Sdm120.import_active; - } else { - Sdm120.start_address_count = sdm120_table; - } - } - EnergyUpdateTotal(Sdm120.total_active, true); - } - } - } - - if (0 == Sdm120.send_retry || data_ready) { - Sdm120.send_retry = 5; - Sdm120Modbus->Send(SDM120_ADDR, 0x04, sdm120_start_addresses[Sdm120.read_state], 2); - } else { - Sdm120.send_retry--; - } -} - -void Sdm120SnsInit(void) -{ - Sdm120Modbus = new TasmotaModbus(pin[GPIO_SDM120_RX], pin[GPIO_SDM120_TX]); - uint8_t result = Sdm120Modbus->Begin(SDM120_SPEED); - if (result) { - if (2 == result) { ClaimSerial(); } - } else { - energy_flg = ENERGY_NONE; - } -} - -void Sdm120DrvInit(void) -{ - if ((pin[GPIO_SDM120_RX] < 99) && (pin[GPIO_SDM120_TX] < 99)) { - energy_flg = XNRG_08; - } -} - -void Sdm220Reset(void) -{ - if (isnan(Sdm120.import_active)) { return; } - - Sdm120.import_active = 0; - Sdm120.import_reactive = 0; - Sdm120.export_reactive = 0; - Sdm120.phase_angle = 0; -} - -#ifdef USE_WEBSERVER -const char HTTP_ENERGY_SDM220[] PROGMEM = - "{s}" D_IMPORT_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" - "{s}" D_EXPORT_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" - "{s}" D_PHASE_ANGLE "{m}%s " D_UNIT_ANGLE "{e}"; -#endif - -void Sdm220Show(bool json) -{ - if (isnan(Sdm120.import_active)) { return; } - - char import_active_chr[FLOATSZ]; - dtostrfd(Sdm120.import_active, Settings.flag2.energy_resolution, import_active_chr); - char import_reactive_chr[FLOATSZ]; - dtostrfd(Sdm120.import_reactive, Settings.flag2.energy_resolution, import_reactive_chr); - char export_reactive_chr[FLOATSZ]; - dtostrfd(Sdm120.export_reactive, Settings.flag2.energy_resolution, export_reactive_chr); - char phase_angle_chr[FLOATSZ]; - dtostrfd(Sdm120.phase_angle, 2, phase_angle_chr); - - if (json) { - ResponseAppend_P(PSTR(",\"" D_JSON_IMPORT_ACTIVE "\":%s,\"" D_JSON_IMPORT_REACTIVE "\":%s,\"" D_JSON_EXPORT_REACTIVE "\":%s,\"" D_JSON_PHASE_ANGLE "\":%s"), - import_active_chr, import_reactive_chr, export_reactive_chr, phase_angle_chr); -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_ENERGY_SDM220, import_reactive_chr, export_reactive_chr, phase_angle_chr); -#endif - } -} - - - - - -bool Xnrg08(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_250_MSECOND: - if (uptime > 4) { SDM120Every250ms(); } - break; - case FUNC_JSON_APPEND: - Sdm220Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Sdm220Show(0); - break; -#endif - case FUNC_ENERGY_RESET: - Sdm220Reset(); - break; - case FUNC_INIT: - Sdm120SnsInit(); - break; - case FUNC_PRE_INIT: - Sdm120DrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_09_dds2382.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_09_dds2382.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_DDS2382 - - - - - - -#define XNRG_09 9 - -#ifndef DDS2382_SPEED -#define DDS2382_SPEED 9600 -#endif -#ifndef DDS2382_ADDR -#define DDS2382_ADDR 1 -#endif - -#include -TasmotaModbus *Dds2382Modbus; - -uint8_t Dds2382_send_retry = 0; - -void Dds2382EverySecond(void) -{ - bool data_ready = Dds2382Modbus->ReceiveReady(); - - if (data_ready) { - uint8_t buffer[46]; - - uint32_t error = Dds2382Modbus->ReceiveBuffer(buffer, 18); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Dds2382Modbus->ReceiveCount()); - - if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "DDS2382 response error %d"), error); - } else { - Energy.data_valid[0] = 0; -# 67 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_09_dds2382.ino" - Energy.voltage[0] = (float)((buffer[27] << 8) + buffer[28]) / 10.0; - Energy.current[0] = (float)((buffer[29] << 8) + buffer[30]) / 100.0; - Energy.active_power[0] = (float)((buffer[31] << 8) + buffer[32]); - Energy.reactive_power[0] = (float)((buffer[33] << 8) + buffer[34]); - Energy.power_factor[0] = (float)((buffer[35] << 8) + buffer[36]) / 1000.0; - Energy.frequency[0] = (float)((buffer[37] << 8) + buffer[38]) / 100.0; - uint8_t offset = 11; - if (Settings.flag3.dds2382_model) { - offset = 19; - } - Energy.export_active = (float)((buffer[offset] << 24) + (buffer[offset +1] << 16) + (buffer[offset +2] << 8) + buffer[offset +3]) / 100.0; - float import_active = (float)((buffer[offset +4] << 24) + (buffer[offset +5] << 16) + (buffer[offset +6] << 8) + buffer[offset +7]) / 100.0; - - EnergyUpdateTotal(import_active, true); - } - } - - if (0 == Dds2382_send_retry || data_ready) { - Dds2382_send_retry = 5; - Dds2382Modbus->Send(DDS2382_ADDR, 0x03, 0, 18); - } else { - Dds2382_send_retry--; - } -} - -void Dds2382SnsInit(void) -{ - Dds2382Modbus = new TasmotaModbus(pin[GPIO_DDS2382_RX], pin[GPIO_DDS2382_TX]); - uint8_t result = Dds2382Modbus->Begin(DDS2382_SPEED); - if (result) { - if (2 == result) { ClaimSerial(); } - } else { - energy_flg = ENERGY_NONE; - } -} - -void Dds2382DrvInit(void) -{ - if ((pin[GPIO_DDS2382_RX] < 99) && (pin[GPIO_DDS2382_TX] < 99)) { - energy_flg = XNRG_09; - } -} - - - - - -bool Xnrg09(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_ENERGY_EVERY_SECOND: - if (uptime > 4) { Dds2382EverySecond(); } - break; - case FUNC_INIT: - Dds2382SnsInit(); - break; - case FUNC_PRE_INIT: - Dds2382DrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_10_sdm630.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_10_sdm630.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_SDM630 - - - - - - -#define XNRG_10 10 - - -#ifndef SDM630_SPEED - #define SDM630_SPEED 9600 -#endif - -#ifndef SDM630_ADDR - #define SDM630_ADDR 1 -#endif - -#include -TasmotaModbus *Sdm630Modbus; - -const uint16_t sdm630_start_addresses[] { - 0x0000, - 0x0002, - 0x0004, - 0x0006, - 0x0008, - 0x000A, - 0x000C, - 0x000E, - 0x0010, - 0x0018, - 0x001A, - 0x001C, - 0x001E, - 0x0020, - 0x0022, - 0x0156 -}; - -struct SDM630 { - uint8_t read_state = 0; - uint8_t send_retry = 0; -} Sdm630; - - - -void SDM630Every250ms(void) -{ - bool data_ready = Sdm630Modbus->ReceiveReady(); - - if (data_ready) { - uint8_t buffer[14]; - - uint32_t error = Sdm630Modbus->ReceiveBuffer(buffer, 2); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Sdm630Modbus->ReceiveCount()); - - if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: SDM630 error %d"), error); - } else { - Energy.data_valid[0] = 0; - Energy.data_valid[1] = 0; - Energy.data_valid[2] = 0; - - - - - float value; - ((uint8_t*)&value)[3] = buffer[3]; - ((uint8_t*)&value)[2] = buffer[4]; - ((uint8_t*)&value)[1] = buffer[5]; - ((uint8_t*)&value)[0] = buffer[6]; - - switch(Sdm630.read_state) { - case 0: - Energy.voltage[0] = value; - break; - - case 1: - Energy.voltage[1] = value; - break; - - case 2: - Energy.voltage[2] = value; - break; - - case 3: - Energy.current[0] = value; - break; - - case 4: - Energy.current[1] = value; - break; - - case 5: - Energy.current[2] = value; - break; - - case 6: - Energy.active_power[0] = value; - break; - - case 7: - Energy.active_power[1] = value; - break; - - case 8: - Energy.active_power[2] = value; - break; - - case 9: - Energy.reactive_power[0] = value; - break; - - case 10: - Energy.reactive_power[1] = value; - break; - - case 11: - Energy.reactive_power[2] = value; - break; - - case 12: - Energy.power_factor[0] = value; - break; - - case 13: - Energy.power_factor[1] = value; - break; - - case 14: - Energy.power_factor[2] = value; - break; - - case 15: - EnergyUpdateTotal(value, true); - break; - } - - Sdm630.read_state++; - if (sizeof(sdm630_start_addresses)/2 == Sdm630.read_state) { - Sdm630.read_state = 0; - } - } - } - - if (0 == Sdm630.send_retry || data_ready) { - Sdm630.send_retry = 5; - Sdm630Modbus->Send(SDM630_ADDR, 0x04, sdm630_start_addresses[Sdm630.read_state], 2); - } else { - Sdm630.send_retry--; - } -} - -void Sdm630SnsInit(void) -{ - Sdm630Modbus = new TasmotaModbus(pin[GPIO_SDM630_RX], pin[GPIO_SDM630_TX]); - uint8_t result = Sdm630Modbus->Begin(SDM630_SPEED); - if (result) { - if (2 == result) { ClaimSerial(); } - Energy.phase_count = 3; - } else { - energy_flg = ENERGY_NONE; - } -} - -void Sdm630DrvInit(void) -{ - if ((pin[GPIO_SDM630_RX] < 99) && (pin[GPIO_SDM630_TX] < 99)) { - energy_flg = XNRG_10; - } -} - - - - - -bool Xnrg10(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_250_MSECOND: - if (uptime > 4) { SDM630Every250ms(); } - break; - case FUNC_INIT: - Sdm630SnsInit(); - break; - case FUNC_PRE_INIT: - Sdm630DrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_11_ddsu666.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_11_ddsu666.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_DDSU666 - - - - -#define XNRG_11 11 - - -#ifndef DDSU666_SPEED - #define DDSU666_SPEED 9600 -#endif - -#ifndef DDSU666_ADDR - #define DDSU666_ADDR 1 -#endif - -#include -TasmotaModbus *Ddsu666Modbus; - -const uint16_t Ddsu666_start_addresses[] { - 0x2000, - 0x2002, - 0x2004, - 0x2006, - 0x200A, - 0x200E, - 0X4000, - 0X400A, -}; - -struct DDSU666 { - float import_active = NAN; - uint8_t read_state = 0; - uint8_t send_retry = 0; -} Ddsu666; - - - -void DDSU666Every250ms(void) -{ - bool data_ready = Ddsu666Modbus->ReceiveReady(); - - if (data_ready) { - uint8_t buffer[14]; - - uint32_t error = Ddsu666Modbus->ReceiveBuffer(buffer, 2); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Ddsu666Modbus->ReceiveCount()); - - if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: Ddsu666 error %d"), error); - } else { - Energy.data_valid[0] = 0; - - - - - float value; - ((uint8_t*)&value)[3] = buffer[3]; - ((uint8_t*)&value)[2] = buffer[4]; - ((uint8_t*)&value)[1] = buffer[5]; - ((uint8_t*)&value)[0] = buffer[6]; - - switch(Ddsu666.read_state) { - case 0: - Energy.voltage[0] = value; - break; - - case 1: - Energy.current[0] = value; - break; - - case 2: - Energy.active_power[0] = value * 1000; - break; - - case 3: - Energy.reactive_power[0] = value * 1000; - break; - - case 4: - Energy.power_factor[0] = value; - break; - - case 5: - Energy.frequency[0] = value; - break; - - case 6: - Ddsu666.import_active = value; - break; - - case 7: - Energy.export_active = value; - break; - } - - Ddsu666.read_state++; - - if (Ddsu666.read_state == 8) { - Ddsu666.read_state = 0; - EnergyUpdateTotal(Ddsu666.import_active, true); - } - } - } - - if (0 == Ddsu666.send_retry || data_ready) { - Ddsu666.send_retry = 5; - Ddsu666Modbus->Send(DDSU666_ADDR, 0x04, Ddsu666_start_addresses[Ddsu666.read_state], 2); - } else { - Ddsu666.send_retry--; - } -} - -void Ddsu666SnsInit(void) -{ - Ddsu666Modbus = new TasmotaModbus(pin[GPIO_DDSU666_RX], pin[GPIO_DDSU666_TX]); - uint8_t result = Ddsu666Modbus->Begin(DDSU666_SPEED); - if (result) { - if (2 == result) { ClaimSerial(); } - } else { - energy_flg = ENERGY_NONE; - } -} - -void Ddsu666DrvInit(void) -{ - if ((pin[GPIO_DDSU666_RX] < 99) && (pin[GPIO_DDSU666_TX] < 99)) { - energy_flg = XNRG_11; - } -} - - - - - -bool Xnrg11(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_250_MSECOND: - if (uptime > 4) { DDSU666Every250ms(); } - break; - case FUNC_INIT: - Ddsu666SnsInit(); - break; - case FUNC_PRE_INIT: - Ddsu666DrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_12_solaxX1.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_12_solaxX1.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_SOLAX_X1 - - - - -#define XNRG_12 12 - -#ifndef SOLAXX1_SPEED -#define SOLAXX1_SPEED 9600 -#endif - -#define INVERTER_ADDRESS 0x0A - -#define D_SOLAX_X1 "SolaxX1" - -#include - -enum solaxX1_Error -{ - solaxX1_ERR_NO_ERROR, - solaxX1_ERR_CRC_ERROR -}; - -union { - uint32_t ErrMessage; - struct { - - uint8_t TzProtectFault:1; - uint8_t MainsLostFault:1; - uint8_t GridVoltFault:1; - uint8_t GridFreqFault:1; - uint8_t PLLLostFault:1; - uint8_t BusVoltFault:1; - uint8_t ErrBit06:1; - uint8_t OciFault:1; - - uint8_t Dci_OCP_Fault:1; - uint8_t ResidualCurrentFault:1; - uint8_t PvVoltFault:1; - uint8_t Ac10Mins_Voltage_Fault:1; - uint8_t IsolationFault:1; - uint8_t TemperatureOverFault:1; - uint8_t FanFault:1; - uint8_t ErrBit15:1; - - uint8_t SpiCommsFault:1; - uint8_t SciCommsFault:1; - uint8_t ErrBit18:1; - uint8_t InputConfigFault:1; - uint8_t EepromFault:1; - uint8_t RelayFault:1; - uint8_t SampleConsistenceFault:1; - uint8_t ResidualCurrent_DeviceFault:1; - - uint8_t ErrBit24:1; - uint8_t ErrBit25:1; - uint8_t ErrBit26:1; - uint8_t ErrBit27:1; - uint8_t ErrBit28:1; - uint8_t DCI_DeviceFault:1; - uint8_t OtherDeviceFault:1; - uint8_t ErrBit31:1; - }; -} ErrCode; - -const char kSolaxMode[] PROGMEM = D_WAITING "|" D_CHECKING "|" D_WORKING "|" D_FAILURE; - -const char kSolaxError[] PROGMEM = - D_SOLAX_ERROR_0 "|" D_SOLAX_ERROR_1 "|" D_SOLAX_ERROR_2 "|" D_SOLAX_ERROR_3 "|" D_SOLAX_ERROR_4 "|" D_SOLAX_ERROR_5 "|" - D_SOLAX_ERROR_6 "|" D_SOLAX_ERROR_7 "|" D_SOLAX_ERROR_8; - - - -TasmotaSerial *solaxX1Serial; - -uint8_t solaxX1_Init = 1; - -struct SOLAXX1 { - float temperature = 0; - float energy_today = 0; - float dc1_voltage = 0; - float dc2_voltage = 0; - float dc1_current = 0; - float dc2_current = 0; - float energy_total = 0; - float runtime_total = 0; - float dc1_power = 0; - float dc2_power = 0; - - uint8_t status = 0; - uint32_t errorCode = 0; -} solaxX1; - -union { - uint8_t status; - struct { - uint8_t freeBit7:1; - uint8_t freeBit6:1; - uint8_t freeBit5:1; - uint8_t queryOffline:1; - uint8_t queryOfflineSend:1; - uint8_t hasAddress:1; - uint8_t inverterAddressSend:1; - uint8_t inverterSnReceived:1; - }; -} protocolStatus; - -uint8_t header[2] = {0xAA, 0x55}; -uint8_t source[2] = {0x00, 0x00}; -uint8_t destination[2] = {0x00, 0x00}; -uint8_t controlCode[1] = {0x00}; -uint8_t functionCode[1] = {0x00}; -uint8_t dataLength[1] = {0x00}; -uint8_t data[16] = {0}; - -uint8_t message[30]; - - - -bool solaxX1_RS485ReceiveReady(void) -{ - return (solaxX1Serial->available() > 1); -} - -void solaxX1_RS485Send(uint16_t msgLen) -{ - memcpy(message, header, 2); - memcpy(message + 2, source, 2); - memcpy(message + 4, destination, 2); - memcpy(message + 6, controlCode, 1); - memcpy(message + 7, functionCode, 1); - memcpy(message + 8, dataLength, 1); - memcpy(message + 9, data, sizeof(data)); - uint16_t crc = solaxX1_calculateCRC(message, msgLen); - - while (solaxX1Serial->available() > 0) - { - solaxX1Serial->read(); - } - - solaxX1Serial->flush(); - solaxX1Serial->write(message, msgLen); - solaxX1Serial->write(highByte(crc)); - solaxX1Serial->write(lowByte(crc)); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, message, msgLen); -} - -uint8_t solaxX1_RS485Receive(uint8_t *value) -{ - uint8_t len = 0; - - while (solaxX1Serial->available() > 0) - { - value[len++] = (uint8_t)solaxX1Serial->read(); - } - - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, value, len); - - uint16_t crc = solaxX1_calculateCRC(value, len - 2); - - if (value[len - 1] == lowByte(crc) && value[len - 2] == highByte(crc)) - { - return solaxX1_ERR_NO_ERROR; - } - else - { - return solaxX1_ERR_CRC_ERROR; - } -} - -uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen) -{ - uint8_t i; - uint16_t wChkSum; - wChkSum = 0; - - for (i = 0; i < bLen; i++) - { - wChkSum = wChkSum + bExternTxPackage[i]; - } - return wChkSum; -} - -void solaxX1_SendInverterAddress(void) -{ - source[0] = 0x00; - destination[0] = 0x00; - destination[1] = 0x00; - controlCode[0] = 0x10; - functionCode[0] = 0x01; - dataLength[0] = 0x0F; - data[14] = INVERTER_ADDRESS; - solaxX1_RS485Send(24); -} - -void solaxX1_QueryLiveData(void) -{ - source[0] = 0x01; - destination[0] = 0x00; - destination[1] = INVERTER_ADDRESS; - controlCode[0] = 0x11; - functionCode[0] = 0x02; - dataLength[0] = 0x00; - solaxX1_RS485Send(9); -} - -uint8_t solaxX1_ParseErrorCode(uint32_t code){ - ErrCode.ErrMessage = code; - - if (code == 0) return 0; - if (ErrCode.MainsLostFault) return 1; - if (ErrCode.GridVoltFault) return 2; - if (ErrCode.GridFreqFault) return 3; - if (ErrCode.PvVoltFault) return 4; - if (ErrCode.IsolationFault) return 5; - if (ErrCode.TemperatureOverFault) return 6; - if (ErrCode.FanFault) return 7; - if (ErrCode.OtherDeviceFault) return 8; -} - - - -uint8_t solaxX1_send_retry = 0; -uint8_t solaxX1_nodata_count = 0; - -void solaxX1250MSecond(void) -{ - uint8_t value[61] = {0}; - bool data_ready = solaxX1_RS485ReceiveReady(); - - if (protocolStatus.hasAddress && (data_ready || solaxX1_send_retry == 0)) - { - if (data_ready) - { - uint8_t error = solaxX1_RS485Receive(value); - if (error) - { - DEBUG_SENSOR_LOG(PSTR("SX1: Data response CRC error")); - } - else - { - solaxX1_nodata_count = 0; - solaxX1_send_retry = 12; - Energy.data_valid[0] = 0; - - solaxX1.temperature = (float)((value[9] << 8) | value[10]); - solaxX1.energy_today = (float)((value[11] << 8) | value[12]) * 0.1f; - solaxX1.dc1_voltage = (float)((value[13] << 8) | value[14]) * 0.1f; - solaxX1.dc2_voltage = (float)((value[15] << 8) | value[16]) * 0.1f; - solaxX1.dc1_current = (float)((value[17] << 8) | value[18]) * 0.1f; - solaxX1.dc2_current = (float)((value[19] << 8) | value[20]) * 0.1f; - Energy.current[0] = (float)((value[21] << 8) | value[22]) * 0.1f; - Energy.voltage[0] = (float)((value[23] << 8) | value[24]) * 0.1f; - Energy.frequency[0] = (float)((value[25] << 8) | value[26]) * 0.01f; - Energy.active_power[0] = (float)((value[27] << 8) | value[28]); - - solaxX1.energy_total = (float)((value[31] << 8) | (value[32] << 8) | (value[33] << 8) | value[34]) * 0.1f; - solaxX1.runtime_total = (float)((value[35] << 8) | (value[36] << 8) | (value[37] << 8) | value[38]); - solaxX1.status = (uint8_t)((value[39] << 8) | value[40]); - - - - - - - - solaxX1.errorCode = (uint32_t)((value[58] << 8) | (value[57] << 8) | (value[56] << 8) | value[55]); - - solaxX1.dc1_power = solaxX1.dc1_voltage * solaxX1.dc1_current; - solaxX1.dc2_power = solaxX1.dc2_voltage * solaxX1.dc2_current; - - solaxX1_QueryLiveData(); - EnergyUpdateTotal(solaxX1.energy_total, true); - } - } - - if (0 == solaxX1_send_retry && 255 != solaxX1_nodata_count) { - solaxX1_send_retry = 12; - solaxX1_QueryLiveData(); - } - - - - if (255 == solaxX1_nodata_count) { - solaxX1_nodata_count = 0; - solaxX1_send_retry = 12; - } - } - else - { - if ((solaxX1_nodata_count % 4) == 0) { DEBUG_SENSOR_LOG(PSTR("SX1: No Data count: %d"), solaxX1_nodata_count); } - if (solaxX1_nodata_count < 10 * 4) - { - solaxX1_nodata_count++; - } - else if (255 != solaxX1_nodata_count) - { - - solaxX1_nodata_count = 255; - solaxX1_send_retry = 12; - protocolStatus.status = 0b00001000; - Energy.data_valid[0] = ENERGY_WATCHDOG; - - solaxX1.temperature = solaxX1.dc1_voltage = solaxX1.dc2_voltage = solaxX1.dc1_current = solaxX1.dc2_current = solaxX1.dc1_power = 0; - solaxX1.dc2_power = solaxX1.status = Energy.current[0] = Energy.voltage[0] = Energy.frequency[0] = Energy.active_power[0] = 0; - - } - } - - if (!protocolStatus.hasAddress && (data_ready || solaxX1_send_retry == 0)) - { - if (data_ready) - { - - if (protocolStatus.inverterAddressSend) - { - uint8_t error = solaxX1_RS485Receive(value); - if (error) - { - DEBUG_SENSOR_LOG(PSTR("SX1: Address confirmation response CRC error")); - } - else - { - if (value[6] == 0x10 && value[7] == 0x81 && value[9] == 0x06) - { - DEBUG_SENSOR_LOG(PSTR("SX1: Set hasAddress")); - protocolStatus.status = 0b00100000; - } - } - } - - - if (protocolStatus.queryOfflineSend) - { - uint8_t error = solaxX1_RS485Receive(value); - if (error) - { - DEBUG_SENSOR_LOG(PSTR("SX1: Query Offline response CRC error")); - } - else - { - - if (value[6] == 0x10 && value[7] == 0x80 && protocolStatus.inverterSnReceived == false) - { - for (uint8_t i = 9; i <= 22; i++) - { - data[i - 9] = value[i]; - } - solaxX1_SendInverterAddress(); - protocolStatus.status = 0b1100000; - DEBUG_SENSOR_LOG(PSTR("SX1: Set inverterSnReceived and inverterAddressSend")); - } - } - } - } - - if (solaxX1_send_retry == 0) - { - if (protocolStatus.queryOfflineSend) - { - protocolStatus.status = 0b00001000; - DEBUG_SENSOR_LOG(PSTR("SX1: Set Query Offline")); - } - solaxX1_send_retry = 12; - } - - - if (protocolStatus.queryOffline) - { - - source[0] = 0x01; - destination[1] = 0x00; - controlCode[0] = 0x10; - functionCode[0] = 0x00; - dataLength[0] = 0x00; - solaxX1_RS485Send(9); - protocolStatus.status = 0b00010000; - DEBUG_SENSOR_LOG(PSTR("SX1: Query Offline Send")); - } - } - - if (!data_ready) - solaxX1_send_retry--; -} - -void solaxX1SnsInit(void) -{ - AddLog_P(LOG_LEVEL_DEBUG, PSTR("SX1: Solax X1 Inverter Init")); - DEBUG_SENSOR_LOG(PSTR("SX1: RX pin: %d, TX pin: %d"), pin[GPIO_SOLAXX1_RX], pin[GPIO_SOLAXX1_TX]); - protocolStatus.status = 0b00100000; - - solaxX1Serial = new TasmotaSerial(pin[GPIO_SOLAXX1_RX], pin[GPIO_SOLAXX1_TX], 1); - if (solaxX1Serial->begin(SOLAXX1_SPEED)) { - if (solaxX1Serial->hardwareSerial()) { ClaimSerial(); } - } else { - energy_flg = ENERGY_NONE; - } -} - -void solaxX1DrvInit(void) -{ - if ((pin[GPIO_SOLAXX1_RX] < 99) && (pin[GPIO_SOLAXX1_TX] < 99)) { - energy_flg = XNRG_12; - } -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_solaxX1_DATA1[] PROGMEM = - "{s}" D_SOLAX_X1 " " D_SOLAR_POWER "{m}%s " D_UNIT_WATT "{e}" - "{s}" D_SOLAX_X1 " " D_PV1_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" - "{s}" D_SOLAX_X1 " " D_PV1_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" - "{s}" D_SOLAX_X1 " " D_PV1_POWER "{m}%s " D_UNIT_WATT "{e}"; -#ifdef SOLAXX1_PV2 -const char HTTP_SNS_solaxX1_DATA2[] PROGMEM = - "{s}" D_SOLAX_X1 " " D_PV2_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" - "{s}" D_SOLAX_X1 " " D_PV2_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" - "{s}" D_SOLAX_X1 " " D_PV2_POWER "{m}%s " D_UNIT_WATT "{e}"; -#endif -const char HTTP_SNS_solaxX1_DATA3[] PROGMEM = - "{s}" D_SOLAX_X1 " " D_UPTIME "{m}%s " D_UNIT_HOUR "{e}" - "{s}" D_SOLAX_X1 " " D_STATUS "{m}%s" - "{s}" D_SOLAX_X1 " " D_ERROR "{m}%s"; -#endif - -void solaxX1Show(bool json) -{ - char solar_power[33]; - dtostrfd(solaxX1.dc1_power + solaxX1.dc2_power, Settings.flag2.wattage_resolution, solar_power); - char pv1_voltage[33]; - dtostrfd(solaxX1.dc1_voltage, Settings.flag2.voltage_resolution, pv1_voltage); - char pv1_current[33]; - dtostrfd(solaxX1.dc1_current, Settings.flag2.current_resolution, pv1_current); - char pv1_power[33]; - dtostrfd(solaxX1.dc1_power, Settings.flag2.wattage_resolution, pv1_power); -#ifdef SOLAXX1_PV2 - char pv2_voltage[33]; - dtostrfd(solaxX1.dc2_voltage, Settings.flag2.voltage_resolution, pv2_voltage); - char pv2_current[33]; - dtostrfd(solaxX1.dc2_current, Settings.flag2.current_resolution, pv2_current); - char pv2_power[33]; - dtostrfd(solaxX1.dc2_power, Settings.flag2.wattage_resolution, pv2_power); -#endif - char temperature[33]; - dtostrfd(solaxX1.temperature, Settings.flag2.temperature_resolution, temperature); - char runtime[33]; - dtostrfd(solaxX1.runtime_total, 0, runtime); - char status[33]; - GetTextIndexed(status, sizeof(status), solaxX1.status, kSolaxMode); - - if (json) - { - ResponseAppend_P(PSTR(",\"" D_JSON_SOLAR_POWER "\":%s,\"" D_JSON_PV1_VOLTAGE "\":%s,\"" D_JSON_PV1_CURRENT "\":%s,\"" D_JSON_PV1_POWER "\":%s"), - solar_power, pv1_voltage, pv1_current, pv1_power); -#ifdef SOLAXX1_PV2 - ResponseAppend_P(PSTR(",\"" D_JSON_PV2_VOLTAGE "\":%s,\"" D_JSON_PV2_CURRENT "\":%s,\"" D_JSON_PV2_POWER "\":%s"), - pv2_voltage, pv2_current, pv2_power); -#endif - ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_RUNTIME "\":%s,\"" D_JSON_STATUS "\":\"%s\",\"" D_JSON_ERROR "\":%d"), - temperature, runtime, status, solaxX1.errorCode); - -#ifdef USE_WEBSERVER - } - else - { - WSContentSend_PD(HTTP_SNS_solaxX1_DATA1, solar_power, pv1_voltage, pv1_current, pv1_power); -#ifdef SOLAXX1_PV2 - WSContentSend_PD(HTTP_SNS_solaxX1_DATA2, pv2_voltage, pv2_current, pv2_power); -#endif - WSContentSend_PD(HTTP_SNS_TEMP, D_SOLAX_X1, temperature, TempUnit()); - char errorCodeString[33]; - WSContentSend_PD(HTTP_SNS_solaxX1_DATA3, runtime, status, - GetTextIndexed(errorCodeString, sizeof(errorCodeString), solaxX1_ParseErrorCode(solaxX1.errorCode), kSolaxError)); -#endif - } -} - - - - - -bool Xnrg12(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_250_MSECOND: - if (uptime > 4) { solaxX1250MSecond(); } - break; - case FUNC_JSON_APPEND: - solaxX1Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - solaxX1Show(0); - break; -#endif - case FUNC_INIT: - solaxX1SnsInit(); - break; - case FUNC_PRE_INIT: - solaxX1DrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_LE01MR -# 71 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" -#define XNRG_13 13 - - -#ifndef LE01MR_SPEED - #define LE01MR_SPEED 2400 -#endif - -#ifndef LE01MR_ADDR - #define LE01MR_ADDR 1 -#endif - -#include -TasmotaModbus *FifLEModbus; - -const uint8_t le01mr_table_sz = 9; - -const uint16_t le01mr_register_addresses[] { - - 0x0130, - 0x0131, - 0x0158, - 0x0139, - 0x0140, - 0x0148, - 0x0150, - 0xA000, - 0xA01E -}; - -struct LE01MR { - float total_active = 0; - float total_reactive = 0; - uint8_t read_state = 0; - uint8_t send_retry = 0; - uint8_t start_address_count = le01mr_table_sz; -} Le01mr; - - - -void FifLEEvery250ms(void) -{ - bool data_ready = FifLEModbus->ReceiveReady(); - - if (data_ready) { - uint8_t buffer[14]; - uint8_t reg_count = 2; - if (Le01mr.read_state < 3) { - reg_count=1; - } - - uint32_t error = FifLEModbus->ReceiveBuffer(buffer, reg_count); - - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, FifLEModbus->ReceiveCount()); - - if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("FiF-LE: LE01MR Modbus error %d"), error); - } else { - Energy.data_valid[0] = 0; -# 146 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" - uint32_t value_buff = 0; - - if (Le01mr.read_state >= 0 && Le01mr.read_state < 3) { - value_buff = ((uint32_t)buffer[3])<<8 | buffer[4]; - } else { - value_buff = ((uint32_t)buffer[3])<<24 | ((uint32_t)buffer[4])<<16 | ((uint32_t)buffer[5])<<8 | buffer[6]; - } - - switch(Le01mr.read_state) { - case 0: - Energy.frequency[0] = value_buff * 0.01f; - break; - - case 1: - Energy.voltage[0] = value_buff * 0.01f; - break; - - case 2: - Energy.power_factor[0] = ((int16_t)value_buff) * 0.001f; - break; - - case 3: - Energy.current[0] = value_buff * 0.001f; - break; - - case 4: - Energy.active_power[0] = value_buff * 1.0f; - break; - - case 5: - Energy.reactive_power[0] = value_buff * 1.0f; - break; - - case 6: - Energy.apparent_power[0] = value_buff * 1.0f; - break; - - case 7: - Le01mr.total_active = value_buff * 0.01f; - break; - - case 8: - Le01mr.total_reactive = value_buff * 0.01f; - break; - } - - Le01mr.read_state++; - if (Le01mr.read_state == Le01mr.start_address_count) { - Le01mr.read_state = 0; - - EnergyUpdateTotal(Le01mr.total_active, true); - } - } - } - - if (0 == Le01mr.send_retry || data_ready) { - uint8_t reg_count = 2; - - Le01mr.send_retry = 5; - - if (Le01mr.read_state < 3) reg_count=1; - - FifLEModbus->Send(LE01MR_ADDR, 0x03, le01mr_register_addresses[Le01mr.read_state], reg_count); - } else { - Le01mr.send_retry--; - } -} - -void FifLESnsInit(void) -{ - FifLEModbus = new TasmotaModbus(pin[GPIO_LE01MR_RX], pin[GPIO_LE01MR_TX]); - uint8_t result = FifLEModbus->Begin(LE01MR_SPEED); - if (result) { - if (2 == result) { ClaimSerial(); } - } else { - energy_flg = ENERGY_NONE; - } -} - -void FifLEDrvInit(void) -{ - if ((pin[GPIO_LE01MR_RX] < 99) && (pin[GPIO_LE01MR_TX] < 99)) { - energy_flg = XNRG_13; - } -} - -void FifLEReset(void) -{ - Le01mr.total_active = 0; - Le01mr.total_reactive = 0; -} - -#ifdef USE_WEBSERVER -const char HTTP_ENERGY_LE01MR[] PROGMEM = - "{s}" D_TOTAL_ACTIVE "{m}%s " D_UNIT_KILOWATTHOUR "{e}" - "{s}" D_TOTAL_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" - ; -#endif - -void FifLEShow(bool json) -{ - char total_reactive_chr[FLOATSZ]; - dtostrfd(Le01mr.total_reactive, Settings.flag2.energy_resolution, total_reactive_chr); - char total_active_chr[FLOATSZ]; - dtostrfd(Le01mr.total_active, Settings.flag2.energy_resolution, total_active_chr); - - if (json) { - ResponseAppend_P(PSTR(",\"" D_JSON_TOTAL_ACTIVE "\":%s,\"" D_JSON_TOTAL_REACTIVE "\":%s"), - total_active_chr, total_reactive_chr); -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_ENERGY_LE01MR, total_active_chr, total_reactive_chr); -#endif - } -} - - - - - -bool Xnrg13(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_250_MSECOND: - if (uptime > 4) { - FifLEEvery250ms(); - } - break; - case FUNC_JSON_APPEND: - FifLEShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - FifLEShow(0); - break; -#endif - case FUNC_ENERGY_RESET: - FifLEReset(); - break; - case FUNC_INIT: - FifLESnsInit(); - break; - case FUNC_PRE_INIT: - FifLEDrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_interface.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xnrg_interface.ino" -#ifdef USE_ENERGY_SENSOR - -#ifdef XFUNC_PTR_IN_ROM -bool (* const xnrg_func_ptr[])(uint8_t) PROGMEM = { -#else -bool (* const xnrg_func_ptr[])(uint8_t) = { -#endif - -#ifdef XNRG_01 - &Xnrg01, -#endif - -#ifdef XNRG_02 - &Xnrg02, -#endif - -#ifdef XNRG_03 - &Xnrg03, -#endif - -#ifdef XNRG_04 - &Xnrg04, -#endif - -#ifdef XNRG_05 - &Xnrg05, -#endif - -#ifdef XNRG_06 - &Xnrg06, -#endif - -#ifdef XNRG_07 - &Xnrg07, -#endif - -#ifdef XNRG_08 - &Xnrg08, -#endif - -#ifdef XNRG_09 - &Xnrg09, -#endif - -#ifdef XNRG_10 - &Xnrg10, -#endif - -#ifdef XNRG_11 - &Xnrg11, -#endif - -#ifdef XNRG_12 - &Xnrg12, -#endif - -#ifdef XNRG_13 - &Xnrg13, -#endif - -#ifdef XNRG_14 - &Xnrg14, -#endif - -#ifdef XNRG_15 - &Xnrg15, -#endif - -#ifdef XNRG_16 - &Xnrg16 -#endif -}; - -const uint8_t xnrg_present = sizeof(xnrg_func_ptr) / sizeof(xnrg_func_ptr[0]); - -uint8_t xnrg_active = 0; - -bool XnrgCall(uint8_t function) -{ - DEBUG_TRACE_LOG(PSTR("NRG: %d"), function); - - if (FUNC_PRE_INIT == function) { - for (uint32_t x = 0; x < xnrg_present; x++) { - xnrg_func_ptr[x](function); - if (energy_flg) { - xnrg_active = x; - return true; - } - } - } - else if (energy_flg) { - return xnrg_func_ptr[xnrg_active](function); - } - return false; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_01_counter.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_01_counter.ino" -#ifdef USE_COUNTER - - - - -#define XSNS_01 1 - -#define D_PRFX_COUNTER "Counter" -#define D_CMND_COUNTERTYPE "Type" -#define D_CMND_COUNTERDEBOUNCE "Debounce" -#define D_CMND_COUNTERDEBOUNCELOW "DebounceLow" -#define D_CMND_COUNTERDEBOUNCEHIGH "DebounceHigh" - -const char kCounterCommands[] PROGMEM = D_PRFX_COUNTER "|" - "|" D_CMND_COUNTERTYPE "|" D_CMND_COUNTERDEBOUNCE "|" D_CMND_COUNTERDEBOUNCELOW "|" D_CMND_COUNTERDEBOUNCEHIGH ; - -void (* const CounterCommand[])(void) PROGMEM = { - &CmndCounter, &CmndCounterType, &CmndCounterDebounce, &CmndCounterDebounceLow, &CmndCounterDebounceHigh }; - -struct COUNTER { - uint32_t timer[MAX_COUNTERS]; - uint32_t timer_low_high[MAX_COUNTERS]; - uint8_t no_pullup = 0; - uint8_t pin_state = 0; - bool any_counter = false; -} Counter; - -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 -void CounterUpdate(uint8_t index) ICACHE_RAM_ATTR; -void CounterUpdate1(void) ICACHE_RAM_ATTR; -void CounterUpdate2(void) ICACHE_RAM_ATTR; -void CounterUpdate3(void) ICACHE_RAM_ATTR; -void CounterUpdate4(void) ICACHE_RAM_ATTR; -#endif - -void CounterUpdate(uint8_t index) -{ - uint32_t time = micros(); - uint32_t debounce_time; - - if (Counter.pin_state) { - - if (digitalRead(pin[GPIO_CNTR1 +index]) == bitRead(Counter.pin_state, index)) { - - return; - } - debounce_time = time - Counter.timer_low_high[index]; - if bitRead(Counter.pin_state, index) { - - if (debounce_time <= Settings.pulse_counter_debounce_high * 1000) return; - } else { - - if (debounce_time <= Settings.pulse_counter_debounce_low * 1000) return; - } - - Counter.timer_low_high[index] = time; - Counter.pin_state ^= (1< Settings.pulse_counter_debounce * 1000) { - Counter.timer[index] = time; - if (bitRead(Settings.pulse_counter_type, index)) { - RtcSettings.pulse_counter[index] = debounce_time; - } else { - RtcSettings.pulse_counter[index]++; - } - } -} - -void CounterUpdate1(void) -{ - CounterUpdate(0); -} - -void CounterUpdate2(void) -{ - CounterUpdate(1); -} - -void CounterUpdate3(void) -{ - CounterUpdate(2); -} - -void CounterUpdate4(void) -{ - CounterUpdate(3); -} - - - -bool CounterPinState(void) -{ - if ((XdrvMailbox.index >= GPIO_CNTR1_NP) && (XdrvMailbox.index < (GPIO_CNTR1_NP + MAX_COUNTERS))) { - bitSet(Counter.no_pullup, XdrvMailbox.index - GPIO_CNTR1_NP); - XdrvMailbox.index -= (GPIO_CNTR1_NP - GPIO_CNTR1); - return true; - } - return false; -} - -void CounterInit(void) -{ - typedef void (*function) () ; - function counter_callbacks[] = { CounterUpdate1, CounterUpdate2, CounterUpdate3, CounterUpdate4 }; - - for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (pin[GPIO_CNTR1 +i] < 99) { - Counter.any_counter = true; - pinMode(pin[GPIO_CNTR1 +i], bitRead(Counter.no_pullup, i) ? INPUT : INPUT_PULLUP); - if ((0 == Settings.pulse_counter_debounce_low) && (0 == Settings.pulse_counter_debounce_high)) { - Counter.pin_state = 0; - attachInterrupt(pin[GPIO_CNTR1 +i], counter_callbacks[i], FALLING); - } else { - Counter.pin_state = 0x8f; - attachInterrupt(pin[GPIO_CNTR1 +i], counter_callbacks[i], CHANGE); - } - } - } -} - -void CounterEverySecond(void) -{ - for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (pin[GPIO_CNTR1 +i] < 99) { - if (bitRead(Settings.pulse_counter_type, i)) { - uint32_t time = micros() - Counter.timer[i]; - if (time > 4200000000) { - RtcSettings.pulse_counter[i] = 4200000000; - } - } - } - } -} - -void CounterSaveState(void) -{ - for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (pin[GPIO_CNTR1 +i] < 99) { - Settings.pulse_counter[i] = RtcSettings.pulse_counter[i]; - } - } -} - -void CounterShow(bool json) -{ - bool header = false; - uint8_t dsxflg = 0; - for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (pin[GPIO_CNTR1 +i] < 99) { - char counter[33]; - if (bitRead(Settings.pulse_counter_type, i)) { - dtostrfd((double)RtcSettings.pulse_counter[i] / 1000000, 6, counter); - } else { - dsxflg++; - snprintf_P(counter, sizeof(counter), PSTR("%lu"), RtcSettings.pulse_counter[i]); - } - - if (json) { - if (!header) { - ResponseAppend_P(PSTR(",\"COUNTER\":{")); - } - ResponseAppend_P(PSTR("%s\"C%d\":%s"), (header)?",":"", i +1, counter); - header = true; -#ifdef USE_DOMOTICZ - if ((0 == tele_period) && (1 == dsxflg)) { - DomoticzSensor(DZ_COUNT, RtcSettings.pulse_counter[i]); - dsxflg++; - } -#endif - if ((0 == tele_period ) && (Settings.flag3.counter_reset_on_tele)) { - RtcSettings.pulse_counter[i] = 0; - } -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(PSTR("{s}" D_COUNTER "%d{m}%s%s{e}"), - i +1, counter, (bitRead(Settings.pulse_counter_type, i)) ? " " D_UNIT_SECOND : ""); -#endif - } - } - } - if (header) { - ResponseJsonEnd(); - } -} - - - - - -void CmndCounter(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) { - if ((XdrvMailbox.data_len > 0) && (pin[GPIO_CNTR1 + XdrvMailbox.index -1] < 99)) { - if ((XdrvMailbox.data[0] == '-') || (XdrvMailbox.data[0] == '+')) { - RtcSettings.pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload; - Settings.pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload; - } else { - RtcSettings.pulse_counter[XdrvMailbox.index -1] = XdrvMailbox.payload; - Settings.pulse_counter[XdrvMailbox.index -1] = XdrvMailbox.payload; - } - } - ResponseCmndIdxNumber(RtcSettings.pulse_counter[XdrvMailbox.index -1]); - } -} - -void CmndCounterType(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1) && (pin[GPIO_CNTR1 + XdrvMailbox.index -1] < 99)) { - bitWrite(Settings.pulse_counter_type, XdrvMailbox.index -1, XdrvMailbox.payload &1); - RtcSettings.pulse_counter[XdrvMailbox.index -1] = 0; - Settings.pulse_counter[XdrvMailbox.index -1] = 0; - } - ResponseCmndIdxNumber(bitRead(Settings.pulse_counter_type, XdrvMailbox.index -1)); - } -} - -void CmndCounterDebounce(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { - Settings.pulse_counter_debounce = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.pulse_counter_debounce); -} - -void CmndCounterDebounceLow(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { - Settings.pulse_counter_debounce_low = XdrvMailbox.payload; - CounterInit(); - } - ResponseCmndNumber(Settings.pulse_counter_debounce_low); -} - -void CmndCounterDebounceHigh(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { - Settings.pulse_counter_debounce_high = XdrvMailbox.payload; - CounterInit(); - } - ResponseCmndNumber(Settings.pulse_counter_debounce_high); -} - - - - - -bool Xsns01(uint8_t function) -{ - bool result = false; - - if (Counter.any_counter) { - switch (function) { - case FUNC_EVERY_SECOND: - CounterEverySecond(); - break; - case FUNC_JSON_APPEND: - CounterShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - CounterShow(0); - break; -#endif - case FUNC_SAVE_BEFORE_RESTART: - case FUNC_SAVE_AT_MIDNIGHT: - CounterSaveState(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kCounterCommands, CounterCommand); - break; - } - } else { - switch (function) { - case FUNC_INIT: - CounterInit(); - break; - case FUNC_PIN_STATE: - result = CounterPinState(); - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_02_analog.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_02_analog.ino" -#ifndef USE_ADC_VCC - - - - -#define XSNS_02 2 - -#define TO_CELSIUS(x) ((x) - 273.15) -#define TO_KELVIN(x) ((x) + 273.15) - - -#define ANALOG_V33 3.3 -#define ANALOG_T0 TO_KELVIN(25.0) - - - - - -#define ANALOG_NTC_BRIDGE_RESISTANCE 32000 -#define ANALOG_NTC_RESISTANCE 10000 -#define ANALOG_NTC_B_COEFFICIENT 3350 - - - - - -#define ANALOG_LDR_BRIDGE_RESISTANCE 10000 -#define ANALOG_LDR_LUX_CALC_SCALAR 12518931 -#define ANALOG_LDR_LUX_CALC_EXPONENT -1.4050 -# 58 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_02_analog.ino" -#define ANALOG_CT_FLAGS 0 -#define ANALOG_CT_MULTIPLIER 2146 -#define ANALOG_CT_VOLTAGE 2300 - -#define CT_FLAG_ENERGY_RESET (1 << 0) - -struct { - float temperature = 0; - float current = 0; - float energy = 0; - uint32_t previous_millis = 0; - uint16_t last_value = 0; -} Adc; - -void AdcInit(void) -{ - if ((Settings.adc_param_type != my_adc0) || (Settings.adc_param1 > 1000000)) { - if (ADC0_TEMP == my_adc0) { - - Settings.adc_param_type = ADC0_TEMP; - Settings.adc_param1 = ANALOG_NTC_BRIDGE_RESISTANCE; - Settings.adc_param2 = ANALOG_NTC_RESISTANCE; - Settings.adc_param3 = ANALOG_NTC_B_COEFFICIENT * 10000; - } - else if (ADC0_LIGHT == my_adc0) { - Settings.adc_param_type = ADC0_LIGHT; - Settings.adc_param1 = ANALOG_LDR_BRIDGE_RESISTANCE; - Settings.adc_param2 = ANALOG_LDR_LUX_CALC_SCALAR; - Settings.adc_param3 = ANALOG_LDR_LUX_CALC_EXPONENT * 10000; - } - else if (ADC0_RANGE == my_adc0) { - Settings.adc_param_type = ADC0_RANGE; - Settings.adc_param1 = 0; - Settings.adc_param2 = 1023; - Settings.adc_param3 = 0; - Settings.adc_param4 = 100; - } - else if (ADC0_CT_POWER == my_adc0) { - Settings.adc_param_type = ADC0_CT_POWER; - Settings.adc_param1 = ANALOG_CT_FLAGS; - Settings.adc_param2 = ANALOG_CT_MULTIPLIER; - Settings.adc_param3 = ANALOG_CT_VOLTAGE; - } - } -} - -uint16_t AdcRead(uint8_t factor) -{ - - - - - - uint8_t samples = 1 << factor; - uint16_t analog = 0; - for (uint32_t i = 0; i < samples; i++) { - analog += analogRead(A0); - delay(1); - } - analog >>= factor; - return analog; -} - -#ifdef USE_RULES -void AdcEvery250ms(void) -{ - if (ADC0_INPUT == my_adc0) { - uint16_t new_value = AdcRead(5); - if ((new_value < Adc.last_value -10) || (new_value > Adc.last_value +10)) { - Adc.last_value = new_value; - uint16_t value = Adc.last_value / 10; - Response_P(PSTR("{\"ANALOG\":{\"A0div10\":%d}}"), (value > 99) ? 100 : value); - XdrvRulesProcess(); - } - } -} -#endif - -uint16_t AdcGetLux(void) -{ - int adc = AdcRead(2); - - double resistorVoltage = ((double)adc / 1023) * ANALOG_V33; - double ldrVoltage = ANALOG_V33 - resistorVoltage; - double ldrResistance = ldrVoltage / resistorVoltage * (double)Settings.adc_param1; - double ldrLux = (double)Settings.adc_param2 * FastPrecisePow(ldrResistance, (double)Settings.adc_param3 / 10000); - - return (uint16_t)ldrLux; -} - -uint16_t AdcGetRange(void) -{ - - - - int adc = AdcRead(2); - double adcrange = ( ((double)Settings.adc_param2 - (double)adc) / ( ((double)Settings.adc_param2 - (double)Settings.adc_param1)) * ((double)Settings.adc_param3 - (double)Settings.adc_param4) + (double)Settings.adc_param4 ); - return (uint16_t)adcrange; -} - -void AdcGetCurrentPower(uint8_t factor) -{ - - - - - - uint8_t samples = 1 << factor; - uint16_t analog = 0; - uint16_t analog_min = 1023; - uint16_t analog_max = 0; - for (uint32_t i = 0; i < samples; i++) { - analog = analogRead(A0); - if (analog < analog_min) { - analog_min = analog; - } - if (analog > analog_max) { - analog_max = analog; - } - delay(1); - } - - Adc.current = (float)(analog_max-analog_min) * ((float)(Settings.adc_param2) / 100000); - float power = Adc.current * (float)(Settings.adc_param3) / 10; - uint32_t current_millis = millis(); - Adc.energy = Adc.energy + ((power * (current_millis - Adc.previous_millis)) / 3600000000); - Adc.previous_millis = current_millis; -} - -void AdcEverySecond(void) -{ - if (ADC0_TEMP == my_adc0) { - int adc = AdcRead(2); - - double Rt = (adc * Settings.adc_param1) / (1024.0 * ANALOG_V33 - (double)adc); - double BC = (double)Settings.adc_param3 / 10000; - double T = BC / (BC / ANALOG_T0 + TaylorLog(Rt / (double)Settings.adc_param2)); - Adc.temperature = ConvertTemp(TO_CELSIUS(T)); - } - else if (ADC0_CT_POWER == my_adc0) { - AdcGetCurrentPower(5); - } -} - -void AdcShow(bool json) -{ - if (ADC0_INPUT == my_adc0) { - uint16_t analog = AdcRead(5); - - if (json) { - ResponseAppend_P(PSTR(",\"ANALOG\":{\"A0\":%d}"), analog); -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_ANALOG, "", 0, analog); -#endif - } - } - - else if (ADC0_TEMP == my_adc0) { - char temperature[33]; - dtostrfd(Adc.temperature, Settings.flag2.temperature_resolution, temperature); - - if (json) { - ResponseAppend_P(JSON_SNS_TEMP, "ANALOG", temperature); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_TEMP, temperature); - } -#endif -#ifdef USE_KNX - if (0 == tele_period) { - KnxSensor(KNX_TEMPERATURE, Adc.temperature); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, "", temperature, TempUnit()); -#endif - } - } - - else if (ADC0_LIGHT == my_adc0) { - uint16_t adc_light = AdcGetLux(); - - if (json) { - ResponseAppend_P(JSON_SNS_ILLUMINANCE, "ANALOG", adc_light); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_ILLUMINANCE, adc_light); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_ILLUMINANCE, "", adc_light); -#endif - } - } - - else if (ADC0_RANGE == my_adc0) { - uint16_t adc_range = AdcGetRange(); - - if (json) { - ResponseAppend_P(JSON_SNS_RANGE, "ANALOG", adc_range); -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_RANGE, "", adc_range); -#endif - } - } - - else if (ADC0_CT_POWER == my_adc0) { - AdcGetCurrentPower(5); - - float voltage = (float)(Settings.adc_param3) / 10; - char voltage_chr[FLOATSZ]; - dtostrfd(voltage, Settings.flag2.voltage_resolution, voltage_chr); - char current_chr[FLOATSZ]; - dtostrfd(Adc.current, Settings.flag2.current_resolution, current_chr); - char power_chr[FLOATSZ]; - dtostrfd(voltage * Adc.current, Settings.flag2.wattage_resolution, power_chr); - char energy_chr[FLOATSZ]; - dtostrfd(Adc.energy, Settings.flag2.energy_resolution, energy_chr); - - if (json) { - ResponseAppend_P(PSTR(",\"ANALOG\":{\"" D_JSON_ENERGY "\":%s,\"" D_JSON_POWERUSAGE "\":%s,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s}"), - energy_chr, power_chr, voltage_chr, current_chr); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_POWER_ENERGY, power_chr); - DomoticzSensor(DZ_VOLTAGE, voltage_chr); - DomoticzSensor(DZ_CURRENT, current_chr); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_VOLTAGE, voltage_chr); - WSContentSend_PD(HTTP_SNS_CURRENT, current_chr); - WSContentSend_PD(HTTP_SNS_POWER, power_chr); - WSContentSend_PD(HTTP_SNS_ENERGY_TOTAL, energy_chr); -#endif - } - } - -} - - - - - -const char kAdcCommands[] PROGMEM = "|" - D_CMND_ADC "|" D_CMND_ADCS "|" D_CMND_ADCPARAM; - -void (* const AdcCommand[])(void) PROGMEM = { - &CmndAdc, &CmndAdcs, &CmndAdcParam }; - -void CmndAdc(void) -{ - if (ValidAdc() && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < ADC0_END)) { - Settings.my_adc0 = XdrvMailbox.payload; - restart_flag = 2; - } - char stemp1[TOPSZ]; - Response_P(PSTR("{\"" D_CMND_ADC "0\":{\"%d\":\"%s\"}}"), Settings.my_adc0, GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_adc0, kAdc0Names)); -} - -void CmndAdcs(void) -{ - Response_P(PSTR("{\"" D_CMND_ADCS "\":{")); - bool jsflg = false; - char stemp1[TOPSZ]; - for (uint32_t i = 0; i < ADC0_END; i++) { - if (jsflg) { - ResponseAppend_P(PSTR(",")); - } - jsflg = true; - ResponseAppend_P(PSTR("\"%d\":\"%s\""), i, GetTextIndexed(stemp1, sizeof(stemp1), i, kAdc0Names)); - } - ResponseJsonEndEnd(); -} - -void CmndAdcParam(void) -{ - if (XdrvMailbox.data_len) { - if ((ADC0_TEMP == XdrvMailbox.payload) || - (ADC0_LIGHT == XdrvMailbox.payload) || - (ADC0_RANGE == XdrvMailbox.payload) || - (ADC0_CT_POWER == XdrvMailbox.payload)) { - if (strstr(XdrvMailbox.data, ",") != nullptr) { - char sub_string[XdrvMailbox.data_len +1]; - - - - Settings.adc_param_type = XdrvMailbox.payload; - Settings.adc_param1 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); - Settings.adc_param2 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 3), nullptr, 10); - if (ADC0_RANGE == XdrvMailbox.payload) { - Settings.adc_param3 = abs(strtol(subStr(sub_string, XdrvMailbox.data, ",", 4), nullptr, 10)); - Settings.adc_param4 = abs(strtol(subStr(sub_string, XdrvMailbox.data, ",", 5), nullptr, 10)); - } else { - Settings.adc_param3 = (int)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)) * 10000); - } - if (ADC0_CT_POWER == XdrvMailbox.payload) { - if ((Settings.adc_param1 & CT_FLAG_ENERGY_RESET) > 0) { - Adc.energy = 0; - Settings.adc_param1 ^= CT_FLAG_ENERGY_RESET; - } - } - } else { - - - - - Settings.adc_param_type = 0; - AdcInit(); - } - } - } - - - Response_P(PSTR("{\"" D_CMND_ADCPARAM "\":[%d,%d,%d"), Settings.adc_param_type, Settings.adc_param1, Settings.adc_param2); - if (ADC0_RANGE == my_adc0) { - ResponseAppend_P(PSTR(",%d,%d"), Settings.adc_param3, Settings.adc_param4); - } else { - int value = Settings.adc_param3; - uint8_t precision; - for (precision = 4; precision > 0; precision--) { - if (value % 10) { break; } - value /= 10; - } - char param3[33]; - dtostrfd(((double)Settings.adc_param3)/10000, precision, param3); - ResponseAppend_P(PSTR(",%s"), param3); - } - ResponseAppend_P(PSTR("]}")); -} - - - - - -bool Xsns02(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_COMMAND: - result = DecodeCommand(kAdcCommands, AdcCommand); - break; - default: - if ((ADC0_INPUT == my_adc0) || - (ADC0_TEMP == my_adc0) || - (ADC0_LIGHT == my_adc0) || - (ADC0_RANGE == my_adc0) || - (ADC0_CT_POWER == my_adc0)) { - switch (function) { -#ifdef USE_RULES - case FUNC_EVERY_250_MSECOND: - AdcEvery250ms(); - break; -#endif - case FUNC_EVERY_SECOND: - AdcEverySecond(); - break; - case FUNC_INIT: - AdcInit(); - break; - case FUNC_JSON_APPEND: - AdcShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - AdcShow(0); - break; -#endif - } - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_04_snfsc.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_04_snfsc.ino" -#ifdef USE_SONOFF_SC -# 57 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_04_snfsc.ino" -#define XSNS_04 4 - -uint16_t sc_value[5] = { 0 }; - -void SonoffScSend(const char *data) -{ - Serial.write(data); - Serial.write('\x1B'); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SERIAL D_TRANSMIT " %s"), data); -} - -void SonoffScInit(void) -{ - - SonoffScSend("AT+START"); - -} - -void SonoffScSerialInput(char *rcvstat) -{ - char *p; - char *str; - uint16_t value[5] = { 0 }; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SERIAL D_RECEIVED " %s"), rcvstat); - - if (!strncasecmp_P(rcvstat, PSTR("AT+UPDATE="), 10)) { - int8_t i = -1; - for (str = strtok_r(rcvstat, ":", &p); str && i < 5; str = strtok_r(nullptr, ":", &p)) { - value[i++] = atoi(str); - } - if (value[0] > 0) { - for (uint32_t i = 0; i < 5; i++) { - sc_value[i] = value[i]; - } - sc_value[2] = (11 - sc_value[2]) * 10; - sc_value[3] *= 10; - sc_value[4] = (11 - sc_value[4]) * 10; - SonoffScSend("AT+SEND=ok"); - } else { - SonoffScSend("AT+SEND=fail"); - } - } - else if (!strcasecmp_P(rcvstat, PSTR("AT+STATUS?"))) { - SonoffScSend("AT+STATUS=4"); - } -} - - - -#ifdef USE_WEBSERVER -const char HTTP_SNS_SCPLUS[] PROGMEM = - "{s}" D_LIGHT "{m}%d%%{e}{s}" D_NOISE "{m}%d%%{e}{s}" D_AIR_QUALITY "{m}%d%%{e}"; -#endif - -void SonoffScShow(bool json) -{ - if (sc_value[0] > 0) { - float t = ConvertTemp(sc_value[1]); - float h = ConvertHumidity(sc_value[0]); - - if (json) { - ResponseAppend_P(PSTR(",\"SonoffSC\":{")); - ResponseAppendTHD(t, h); - ResponseAppend_P(PSTR(",\"" D_JSON_LIGHT "\":%d,\"" D_JSON_NOISE "\":%d,\"" D_JSON_AIRQUALITY "\":%d}"), sc_value[2], sc_value[3], sc_value[4]); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzTempHumPressureSensor(t, h); - DomoticzSensor(DZ_ILLUMINANCE, sc_value[2]); - DomoticzSensor(DZ_COUNT, sc_value[3]); - DomoticzSensor(DZ_AIRQUALITY, 500 + ((100 - sc_value[4]) * 20)); - } -#endif - -#ifdef USE_KNX - if (0 == tele_period) { - KnxSensor(KNX_TEMPERATURE, t); - KnxSensor(KNX_HUMIDITY, h); - } -#endif - -#ifdef USE_WEBSERVER - } else { - WSContentSend_THD("", t, h); - WSContentSend_PD(HTTP_SNS_SCPLUS, sc_value[2], sc_value[3], sc_value[4]); -#endif - } - } -} - - - - - -bool Xsns04(uint8_t function) -{ - bool result = false; - - if (SONOFF_SC == my_module_type) { - switch (function) { - case FUNC_JSON_APPEND: - SonoffScShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - SonoffScShow(0); - break; -#endif - case FUNC_INIT: - SonoffScInit(); - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_05_ds18x20.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_05_ds18x20.ino" -#ifdef USE_DS18x20 - - - - -#define XSNS_05 5 - - - -#define DS18S20_CHIPID 0x10 -#define DS1822_CHIPID 0x22 -#define DS18B20_CHIPID 0x28 -#define MAX31850_CHIPID 0x3B - -#define W1_SKIP_ROM 0xCC -#define W1_CONVERT_TEMP 0x44 -#define W1_WRITE_EEPROM 0x48 -#define W1_WRITE_SCRATCHPAD 0x4E -#define W1_READ_SCRATCHPAD 0xBE - -#define DS18X20_MAX_SENSORS 8 - -const char kDs18x20Types[] PROGMEM = "DS18x20|DS18S20|DS1822|DS18B20|MAX31850"; - -uint8_t ds18x20_chipids[] = { 0, DS18S20_CHIPID, DS1822_CHIPID, DS18B20_CHIPID, MAX31850_CHIPID }; - -struct DS18X20STRUCT { - uint8_t address[8]; - uint8_t index; - uint8_t valid; - float temperature; -} ds18x20_sensor[DS18X20_MAX_SENSORS]; -uint8_t ds18x20_sensors = 0; -uint8_t ds18x20_pin = 0; -uint8_t ds18x20_pin_out = 0; -bool ds18x20_dual_mode = false; -char ds18x20_types[12]; -#ifdef W1_PARASITE_POWER -uint8_t ds18x20_sensor_curr = 0; -unsigned long w1_power_until = 0; -#endif - - - - - -#define W1_MATCH_ROM 0x55 -#define W1_SEARCH_ROM 0xF0 - -uint8_t onewire_last_discrepancy = 0; -uint8_t onewire_last_family_discrepancy = 0; -bool onewire_last_device_flag = false; -unsigned char onewire_rom_id[8] = { 0 }; - - - -uint8_t OneWireReset(void) -{ - uint8_t retries = 125; - - if (!ds18x20_dual_mode) { - pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); - do { - if (--retries == 0) { - return 0; - } - delayMicroseconds(2); - } while (!digitalRead(ds18x20_pin)); - pinMode(ds18x20_pin, OUTPUT); - digitalWrite(ds18x20_pin, LOW); - delayMicroseconds(480); - pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); - delayMicroseconds(70); - uint8_t r = !digitalRead(ds18x20_pin); - delayMicroseconds(410); - return r; - } else { - digitalWrite(ds18x20_pin_out, HIGH); - do { - if (--retries == 0) { - return 0; - } - delayMicroseconds(2); - } while (!digitalRead(ds18x20_pin)); - digitalWrite(ds18x20_pin_out, LOW); - delayMicroseconds(480); - digitalWrite(ds18x20_pin_out, HIGH); - delayMicroseconds(70); - uint8_t r = !digitalRead(ds18x20_pin); - delayMicroseconds(410); - return r; - } -} - -void OneWireWriteBit(uint8_t v) -{ - static const uint8_t delay_low[2] = { 65, 10 }; - static const uint8_t delay_high[2] = { 5, 55 }; - - v &= 1; - if (!ds18x20_dual_mode) { - digitalWrite(ds18x20_pin, LOW); - pinMode(ds18x20_pin, OUTPUT); - delayMicroseconds(delay_low[v]); - digitalWrite(ds18x20_pin, HIGH); - } else { - digitalWrite(ds18x20_pin_out, LOW); - delayMicroseconds(delay_low[v]); - digitalWrite(ds18x20_pin_out, HIGH); - } - delayMicroseconds(delay_high[v]); -} - -uint8_t OneWire1ReadBit(void) -{ - pinMode(ds18x20_pin, OUTPUT); - digitalWrite(ds18x20_pin, LOW); - delayMicroseconds(3); - pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); - delayMicroseconds(10); - uint8_t r = digitalRead(ds18x20_pin); - delayMicroseconds(53); - return r; -} - -uint8_t OneWire2ReadBit(void) -{ - digitalWrite(ds18x20_pin_out, LOW); - delayMicroseconds(3); - digitalWrite(ds18x20_pin_out, HIGH); - delayMicroseconds(10); - uint8_t r = digitalRead(ds18x20_pin); - delayMicroseconds(53); - return r; -} - - - -void OneWireWrite(uint8_t v) -{ - for (uint8_t bit_mask = 0x01; bit_mask; bit_mask <<= 1) { - OneWireWriteBit((bit_mask & v) ? 1 : 0); - } -} - -uint8_t OneWireRead(void) -{ - uint8_t r = 0; - - if (!ds18x20_dual_mode) { - for (uint8_t bit_mask = 0x01; bit_mask; bit_mask <<= 1) { - if (OneWire1ReadBit()) { - r |= bit_mask; - } - } - } else { - for (uint8_t bit_mask = 0x01; bit_mask; bit_mask <<= 1) { - if (OneWire2ReadBit()) { - r |= bit_mask; - } - } - } - return r; -} - -void OneWireSelect(const uint8_t rom[8]) -{ - OneWireWrite(W1_MATCH_ROM); - for (uint32_t i = 0; i < 8; i++) { - OneWireWrite(rom[i]); - } -} - -void OneWireResetSearch(void) -{ - onewire_last_discrepancy = 0; - onewire_last_device_flag = false; - onewire_last_family_discrepancy = 0; - for (uint32_t i = 0; i < 8; i++) { - onewire_rom_id[i] = 0; - } -} - -uint8_t OneWireSearch(uint8_t *newAddr) -{ - uint8_t id_bit_number = 1; - uint8_t last_zero = 0; - uint8_t rom_byte_number = 0; - uint8_t search_result = 0; - uint8_t id_bit; - uint8_t cmp_id_bit; - unsigned char rom_byte_mask = 1; - unsigned char search_direction; - - if (!onewire_last_device_flag) { - if (!OneWireReset()) { - onewire_last_discrepancy = 0; - onewire_last_device_flag = false; - onewire_last_family_discrepancy = 0; - return false; - } - OneWireWrite(W1_SEARCH_ROM); - do { - if (!ds18x20_dual_mode) { - id_bit = OneWire1ReadBit(); - cmp_id_bit = OneWire1ReadBit(); - } else { - id_bit = OneWire2ReadBit(); - cmp_id_bit = OneWire2ReadBit(); - } - if ((id_bit == 1) && (cmp_id_bit == 1)) { - break; - } else { - if (id_bit != cmp_id_bit) { - search_direction = id_bit; - } else { - if (id_bit_number < onewire_last_discrepancy) { - search_direction = ((onewire_rom_id[rom_byte_number] & rom_byte_mask) > 0); - } else { - search_direction = (id_bit_number == onewire_last_discrepancy); - } - if (search_direction == 0) { - last_zero = id_bit_number; - if (last_zero < 9) { - onewire_last_family_discrepancy = last_zero; - } - } - } - if (search_direction == 1) { - onewire_rom_id[rom_byte_number] |= rom_byte_mask; - } else { - onewire_rom_id[rom_byte_number] &= ~rom_byte_mask; - } - OneWireWriteBit(search_direction); - id_bit_number++; - rom_byte_mask <<= 1; - if (rom_byte_mask == 0) { - rom_byte_number++; - rom_byte_mask = 1; - } - } - } while (rom_byte_number < 8); - if (!(id_bit_number < 65)) { - onewire_last_discrepancy = last_zero; - if (onewire_last_discrepancy == 0) { - onewire_last_device_flag = true; - } - search_result = true; - } - } - if (!search_result || !onewire_rom_id[0]) { - onewire_last_discrepancy = 0; - onewire_last_device_flag = false; - onewire_last_family_discrepancy = 0; - search_result = false; - } - for (uint32_t i = 0; i < 8; i++) { - newAddr[i] = onewire_rom_id[i]; - } - return search_result; -} - -bool OneWireCrc8(uint8_t *addr) -{ - uint8_t crc = 0; - uint8_t len = 8; - - while (len--) { - uint8_t inbyte = *addr++; - for (uint32_t i = 8; i; i--) { - uint8_t mix = (crc ^ inbyte) & 0x01; - crc >>= 1; - if (mix) { - crc ^= 0x8C; - } - inbyte >>= 1; - } - } - return (crc == *addr); -} - - - -void Ds18x20Init(void) -{ - uint64_t ids[DS18X20_MAX_SENSORS]; - - ds18x20_pin = pin[GPIO_DSB]; - - if (pin[GPIO_DSB_OUT] < 99) { - ds18x20_pin_out = pin[GPIO_DSB_OUT]; - ds18x20_dual_mode = true; - pinMode(ds18x20_pin_out, OUTPUT); - pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); - } - - OneWireResetSearch(); - - ds18x20_sensors = 0; - while (ds18x20_sensors < DS18X20_MAX_SENSORS) { - if (!OneWireSearch(ds18x20_sensor[ds18x20_sensors].address)) { - break; - } - if (OneWireCrc8(ds18x20_sensor[ds18x20_sensors].address) && - ((ds18x20_sensor[ds18x20_sensors].address[0] == DS18S20_CHIPID) || - (ds18x20_sensor[ds18x20_sensors].address[0] == DS1822_CHIPID) || - (ds18x20_sensor[ds18x20_sensors].address[0] == DS18B20_CHIPID) || - (ds18x20_sensor[ds18x20_sensors].address[0] == MAX31850_CHIPID))) { - ds18x20_sensor[ds18x20_sensors].index = ds18x20_sensors; - ids[ds18x20_sensors] = ds18x20_sensor[ds18x20_sensors].address[0]; - for (uint32_t j = 6; j > 0; j--) { - ids[ds18x20_sensors] = ids[ds18x20_sensors] << 8 | ds18x20_sensor[ds18x20_sensors].address[j]; - } - ds18x20_sensors++; - } - } - for (uint32_t i = 0; i < ds18x20_sensors; i++) { - for (uint32_t j = i + 1; j < ds18x20_sensors; j++) { - if (ids[ds18x20_sensor[i].index] > ids[ds18x20_sensor[j].index]) { - std::swap(ds18x20_sensor[i].index, ds18x20_sensor[j].index); - } - } - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSORS_FOUND " %d"), ds18x20_sensors); -} - -void Ds18x20Convert(void) -{ - OneWireReset(); -#ifdef W1_PARASITE_POWER - - if (++ds18x20_sensor_curr >= ds18x20_sensors) - ds18x20_sensor_curr = 0; - OneWireSelect(ds18x20_sensor[ds18x20_sensor_curr].address); -#else - OneWireWrite(W1_SKIP_ROM); -#endif - OneWireWrite(W1_CONVERT_TEMP); - -} - -bool Ds18x20Read(uint8_t sensor) -{ - uint8_t data[9]; - int8_t sign = 1; - - uint8_t index = ds18x20_sensor[sensor].index; - if (ds18x20_sensor[index].valid) { ds18x20_sensor[index].valid--; } - for (uint32_t retry = 0; retry < 3; retry++) { - OneWireReset(); - OneWireSelect(ds18x20_sensor[index].address); - OneWireWrite(W1_READ_SCRATCHPAD); - for (uint32_t i = 0; i < 9; i++) { - data[i] = OneWireRead(); - } - if (OneWireCrc8(data)) { - switch(ds18x20_sensor[index].address[0]) { - case DS18S20_CHIPID: { - if (data[1] > 0x80) { - data[0] = (~data[0]) +1; - sign = -1; - } - float temp9 = (float)(data[0] >> 1) * sign; - ds18x20_sensor[index].temperature = ConvertTemp((temp9 - 0.25) + ((16.0 - data[6]) / 16.0)); - ds18x20_sensor[index].valid = SENSOR_MAX_MISS; - return true; - } - case DS1822_CHIPID: - case DS18B20_CHIPID: { - if (data[4] != 0x7F) { - data[4] = 0x7F; - OneWireReset(); - OneWireSelect(ds18x20_sensor[index].address); - OneWireWrite(W1_WRITE_SCRATCHPAD); - OneWireWrite(data[2]); - OneWireWrite(data[3]); - OneWireWrite(data[4]); - OneWireSelect(ds18x20_sensor[index].address); - OneWireWrite(W1_WRITE_EEPROM); -#ifdef W1_PARASITE_POWER - w1_power_until = millis() + 10; -#endif - } - uint16_t temp12 = (data[1] << 8) + data[0]; - if (temp12 > 2047) { - temp12 = (~temp12) +1; - sign = -1; - } - ds18x20_sensor[index].temperature = ConvertTemp(sign * temp12 * 0.0625); - ds18x20_sensor[index].valid = SENSOR_MAX_MISS; - return true; - } - case MAX31850_CHIPID: { - int16_t temp14 = (data[1] << 8) + (data[0] & 0xFC); - ds18x20_sensor[index].temperature = ConvertTemp(temp14 * 0.0625); - ds18x20_sensor[index].valid = SENSOR_MAX_MISS; - return true; - } - } - } - } - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSOR_CRC_ERROR)); - return false; -} - -void Ds18x20Name(uint8_t sensor) -{ - uint8_t index = sizeof(ds18x20_chipids); - while (index) { - if (ds18x20_sensor[ds18x20_sensor[sensor].index].address[0] == ds18x20_chipids[index]) { - break; - } - index--; - } - GetTextIndexed(ds18x20_types, sizeof(ds18x20_types), index, kDs18x20Types); - if (ds18x20_sensors > 1) { - snprintf_P(ds18x20_types, sizeof(ds18x20_types), PSTR("%s%c%d"), ds18x20_types, IndexSeparator(), sensor +1); - } -} - - - -void Ds18x20EverySecond(void) -{ - if (!ds18x20_sensors) { return; } - -#ifdef W1_PARASITE_POWER - - unsigned long now = millis(); - if (now < w1_power_until) - return; -#endif - if (uptime & 1 -#ifdef W1_PARASITE_POWER - - || ds18x20_sensors >= 2 -#endif - ) { - - Ds18x20Convert(); - } else { - for (uint32_t i = 0; i < ds18x20_sensors; i++) { - - if (!Ds18x20Read(i)) { - Ds18x20Name(i); - AddLogMissed(ds18x20_types, ds18x20_sensor[ds18x20_sensor[i].index].valid); -#ifdef USE_DS18x20_RECONFIGURE - if (!ds18x20_sensor[ds18x20_sensor[i].index].valid) { - memset(&ds18x20_sensor, 0, sizeof(ds18x20_sensor)); - Ds18x20Init(); - } -#endif - } - } - } -} - -void Ds18x20Show(bool json) -{ - for (uint32_t i = 0; i < ds18x20_sensors; i++) { - uint8_t index = ds18x20_sensor[i].index; - - if (ds18x20_sensor[index].valid) { - char temperature[33]; - dtostrfd(ds18x20_sensor[index].temperature, Settings.flag2.temperature_resolution, temperature); - - Ds18x20Name(i); - - if (json) { - char address[17]; - for (uint32_t j = 0; j < 6; j++) { - sprintf(address+2*j, "%02X", ds18x20_sensor[index].address[6-j]); - } - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_ID "\":\"%s\",\"" D_JSON_TEMPERATURE "\":%s}"), ds18x20_types, address, temperature); -#ifdef USE_DOMOTICZ - if ((0 == tele_period) && (0 == i)) { - DomoticzSensor(DZ_TEMP, temperature); - } -#endif -#ifdef USE_KNX - if ((0 == tele_period) && (0 == i)) { - KnxSensor(KNX_TEMPERATURE, ds18x20_sensor[index].temperature); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, ds18x20_types, temperature, TempUnit()); -#endif - } - } - } -} - - - - - -bool Xsns05(uint8_t function) -{ - bool result = false; - - if (pin[GPIO_DSB] < 99) { - switch (function) { - case FUNC_INIT: - Ds18x20Init(); - break; - case FUNC_EVERY_SECOND: - Ds18x20EverySecond(); - break; - case FUNC_JSON_APPEND: - Ds18x20Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Ds18x20Show(0); - break; -#endif - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_06_dht.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_06_dht.ino" -#ifdef USE_DHT -# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_06_dht.ino" -#define XSNS_06 6 - -#define DHT_MAX_SENSORS 4 -#define DHT_MAX_RETRY 8 - -uint8_t dht_data[5]; -uint8_t dht_sensors = 0; -uint8_t dht_pin_out = 0; -bool dht_active = true; -bool dht_dual_mode = false; - -struct DHTSTRUCT { - uint8_t pin; - uint8_t type; - uint8_t lastresult; - char stype[12]; - float t = NAN; - float h = NAN; -} Dht[DHT_MAX_SENSORS]; - -bool DhtWaitState(uint32_t sensor, uint32_t level) -{ - unsigned long timeout = micros() + 100; - while (digitalRead(Dht[sensor].pin) != level) { - if (TimeReachedUsec(timeout)) { - PrepLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " %s " D_PULSE), - (level) ? D_START_SIGNAL_HIGH : D_START_SIGNAL_LOW); - return false; - } - delayMicroseconds(1); - } - return true; -} - -bool DhtRead(uint32_t sensor) -{ - dht_data[0] = dht_data[1] = dht_data[2] = dht_data[3] = dht_data[4] = 0; - - if (!dht_dual_mode) { - pinMode(Dht[sensor].pin, OUTPUT); - digitalWrite(Dht[sensor].pin, LOW); - } else { - digitalWrite(dht_pin_out, LOW); - } - - switch (Dht[sensor].type) { - case GPIO_DHT11: - delay(19); - break; - case GPIO_DHT22: - delay(2); - break; - case GPIO_SI7021: - delayMicroseconds(500); - break; - } - - if (!dht_dual_mode) { - pinMode(Dht[sensor].pin, INPUT_PULLUP); - } else { - digitalWrite(dht_pin_out, HIGH); - } - - switch (Dht[sensor].type) { - case GPIO_DHT11: - case GPIO_DHT22: - delayMicroseconds(50); - break; - case GPIO_SI7021: - delayMicroseconds(20); - break; - } -# 133 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_06_dht.ino" - uint32_t i = 0; - noInterrupts(); - if (DhtWaitState(sensor, 0) && DhtWaitState(sensor, 1) && DhtWaitState(sensor, 0)) { - for (i = 0; i < 40; i++) { - if (!DhtWaitState(sensor, 1)) { break; } - delayMicroseconds(35); - if (digitalRead(Dht[sensor].pin)) { - dht_data[i / 8] |= (1 << (7 - i % 8)); - } - if (!DhtWaitState(sensor, 0)) { break; } - } - } - interrupts(); - if (i < 40) { return false; } - - uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; - if (dht_data[4] != checksum) { - char hex_char[15]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"), - ToHex_P(dht_data, 5, hex_char, sizeof(hex_char), ' '), checksum); - return false; - } - - float temperature = NAN; - float humidity = NAN; - switch (Dht[sensor].type) { - case GPIO_DHT11: - humidity = dht_data[0]; - temperature = dht_data[2] + ((float)dht_data[3] * 0.1f); - break; - case GPIO_DHT22: - case GPIO_SI7021: - humidity = ((dht_data[0] << 8) | dht_data[1]) * 0.1; - temperature = (((dht_data[2] & 0x7F) << 8 ) | dht_data[3]) * 0.1; - if (dht_data[2] & 0x80) { - temperature *= -1; - } - break; - } - if (isnan(temperature) || isnan(humidity)) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "Invalid NAN reading")); - return false; - } - - if (humidity > 100) { humidity = 100.0; } - if (humidity < 0) { humidity = 0.1; } - Dht[sensor].h = ConvertHumidity(humidity); - Dht[sensor].t = ConvertTemp(temperature); - Dht[sensor].lastresult = 0; - - return true; -} - - - -bool DhtPinState() -{ - if ((XdrvMailbox.index >= GPIO_DHT11) && (XdrvMailbox.index <= GPIO_SI7021)) { - if (dht_sensors < DHT_MAX_SENSORS) { - Dht[dht_sensors].pin = XdrvMailbox.payload; - Dht[dht_sensors].type = XdrvMailbox.index; - dht_sensors++; - XdrvMailbox.index = GPIO_DHT11; - } else { - XdrvMailbox.index = 0; - } - return true; - } - return false; -} - -void DhtInit(void) -{ - if (dht_sensors) { - if (pin[GPIO_DHT11_OUT] < 99) { - dht_pin_out = pin[GPIO_DHT11_OUT]; - dht_dual_mode = true; - dht_sensors = 1; - pinMode(dht_pin_out, OUTPUT); - } - - for (uint32_t i = 0; i < dht_sensors; i++) { - pinMode(Dht[i].pin, INPUT_PULLUP); - Dht[i].lastresult = DHT_MAX_RETRY; - GetTextIndexed(Dht[i].stype, sizeof(Dht[i].stype), Dht[i].type, kSensorNames); - if (dht_sensors > 1) { - snprintf_P(Dht[i].stype, sizeof(Dht[i].stype), PSTR("%s%c%02d"), Dht[i].stype, IndexSeparator(), Dht[i].pin); - } - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "(v5) " D_SENSORS_FOUND " %d"), dht_sensors); - } else { - dht_active = false; - } -} - -void DhtEverySecond(void) -{ - if (uptime &1) { - for (uint32_t sensor = 0; sensor < dht_sensors; sensor++) { - - if (!DhtRead(sensor)) { - Dht[sensor].lastresult++; - if (Dht[sensor].lastresult > DHT_MAX_RETRY) { - Dht[sensor].t = NAN; - Dht[sensor].h = NAN; - } - } - } - } -} - -void DhtShow(bool json) -{ - for (uint32_t i = 0; i < dht_sensors; i++) { - TempHumDewShow(json, ((0 == tele_period) && (0 == i)), Dht[i].stype, Dht[i].t, Dht[i].h); - } -} - - - - - -bool Xsns06(uint8_t function) -{ - bool result = false; - - if (dht_active) { - switch (function) { - case FUNC_EVERY_SECOND: - DhtEverySecond(); - break; - case FUNC_JSON_APPEND: - DhtShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - DhtShow(0); - break; -#endif - case FUNC_INIT: - DhtInit(); - break; - case FUNC_PIN_STATE: - result = DhtPinState(); - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_07_sht1x.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_07_sht1x.ino" -#ifdef USE_I2C -#ifdef USE_SHT -# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_07_sht1x.ino" -#define XSNS_07 7 -#define XI2C_08 8 - -enum { - SHT1X_CMD_MEASURE_TEMP = B00000011, - SHT1X_CMD_MEASURE_RH = B00000101, - SHT1X_CMD_SOFT_RESET = B00011110 -}; - -uint8_t sht_sda_pin; -uint8_t sht_scl_pin; -uint8_t sht_type = 0; -char sht_types[] = "SHT1X"; -uint8_t sht_valid = 0; -float sht_temperature = 0; -float sht_humidity = 0; - -bool ShtReset(void) -{ - pinMode(sht_sda_pin, INPUT_PULLUP); - pinMode(sht_scl_pin, OUTPUT); - delay(11); - for (uint32_t i = 0; i < 9; i++) { - digitalWrite(sht_scl_pin, HIGH); - digitalWrite(sht_scl_pin, LOW); - } - bool success = ShtSendCommand(SHT1X_CMD_SOFT_RESET); - delay(11); - return success; -} - -bool ShtSendCommand(const uint8_t cmd) -{ - pinMode(sht_sda_pin, OUTPUT); - - digitalWrite(sht_sda_pin, HIGH); - digitalWrite(sht_scl_pin, HIGH); - digitalWrite(sht_sda_pin, LOW); - digitalWrite(sht_scl_pin, LOW); - digitalWrite(sht_scl_pin, HIGH); - digitalWrite(sht_sda_pin, HIGH); - digitalWrite(sht_scl_pin, LOW); - - shiftOut(sht_sda_pin, sht_scl_pin, MSBFIRST, cmd); - - bool ackerror = false; - digitalWrite(sht_scl_pin, HIGH); - pinMode(sht_sda_pin, INPUT_PULLUP); - if (digitalRead(sht_sda_pin) != LOW) { - ackerror = true; - } - digitalWrite(sht_scl_pin, LOW); - delayMicroseconds(1); - if (digitalRead(sht_sda_pin) != HIGH) { - ackerror = true; - } - if (ackerror) { - - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_SHT1 D_SENSOR_DID_NOT_ACK_COMMAND)); - } - return (!ackerror); -} - -bool ShtAwaitResult(void) -{ - - for (uint32_t i = 0; i < 16; i++) { - if (LOW == digitalRead(sht_sda_pin)) { - return true; - } - delay(20); - } - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_SHT1 D_SENSOR_BUSY)); - - return false; -} - -int ShtReadData(void) -{ - int val = 0; - - - val = shiftIn(sht_sda_pin, sht_scl_pin, 8); - val <<= 8; - - pinMode(sht_sda_pin, OUTPUT); - digitalWrite(sht_sda_pin, LOW); - digitalWrite(sht_scl_pin, HIGH); - digitalWrite(sht_scl_pin, LOW); - pinMode(sht_sda_pin, INPUT_PULLUP); - - val |= shiftIn(sht_sda_pin, sht_scl_pin, 8); - - digitalWrite(sht_scl_pin, HIGH); - digitalWrite(sht_scl_pin, LOW); - return val; -} - -bool ShtRead(void) -{ - if (sht_valid) { sht_valid--; } - if (!ShtReset()) { return false; } - if (!ShtSendCommand(SHT1X_CMD_MEASURE_TEMP)) { return false; } - if (!ShtAwaitResult()) { return false; } - float tempRaw = ShtReadData(); - if (!ShtSendCommand(SHT1X_CMD_MEASURE_RH)) { return false; } - if (!ShtAwaitResult()) { return false; } - float humRaw = ShtReadData(); - - - const float d1 = -39.7; - const float d2 = 0.01; - sht_temperature = d1 + (tempRaw * d2); - const float c1 = -2.0468; - const float c2 = 0.0367; - const float c3 = -1.5955E-6; - const float t1 = 0.01; - const float t2 = 0.00008; - float rhLinear = c1 + c2 * humRaw + c3 * humRaw * humRaw; - sht_humidity = (sht_temperature - 25) * (t1 + t2 * humRaw) + rhLinear; - sht_temperature = ConvertTemp(sht_temperature); - sht_humidity = ConvertHumidity(sht_humidity); - - sht_valid = SENSOR_MAX_MISS; - return true; -} - - - -void ShtDetect(void) -{ - sht_sda_pin = pin[GPIO_I2C_SDA]; - sht_scl_pin = pin[GPIO_I2C_SCL]; - if (ShtRead()) { - sht_type = 1; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C D_SHT1X_FOUND)); - } else { - Wire.begin(sht_sda_pin, sht_scl_pin); - sht_type = 0; - } -} - -void ShtEverySecond(void) -{ - if (!(uptime %4)) { - - if (!ShtRead()) { - AddLogMissed(sht_types, sht_valid); - } - } -} - -void ShtShow(bool json) -{ - if (sht_valid) { - TempHumDewShow(json, (0 == tele_period), sht_types, sht_temperature, sht_humidity); - } -} - - - - - -bool Xsns07(uint8_t function) -{ - if (!I2cEnabled(XI2C_08)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - ShtDetect(); - } - else if (sht_type) { - switch (function) { - case FUNC_EVERY_SECOND: - ShtEverySecond(); - break; - case FUNC_JSON_APPEND: - ShtShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - ShtShow(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_08_htu21.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_08_htu21.ino" -#ifdef USE_I2C -#ifdef USE_HTU -# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_08_htu21.ino" -#define XSNS_08 8 -#define XI2C_09 9 - -#define HTU21_ADDR 0x40 - -#define SI7013_CHIPID 0x0D -#define SI7020_CHIPID 0x14 -#define SI7021_CHIPID 0x15 -#define HTU21_CHIPID 0x32 - -#define HTU21_READTEMP 0xE3 -#define HTU21_READHUM 0xE5 -#define HTU21_WRITEREG 0xE6 -#define HTU21_READREG 0xE7 -#define HTU21_RESET 0xFE -#define HTU21_HEATER_WRITE 0x51 -#define HTU21_HEATER_READ 0x11 -#define HTU21_SERIAL2_READ1 0xFC -#define HTU21_SERIAL2_READ2 0xC9 - -#define HTU21_HEATER_ON 0x04 -#define HTU21_HEATER_OFF 0xFB - -#define HTU21_RES_RH12_T14 0x00 -#define HTU21_RES_RH8_T12 0x01 -#define HTU21_RES_RH10_T13 0x80 -#define HTU21_RES_RH11_T11 0x81 - -#define HTU21_CRC8_POLYNOM 0x13100 - -const char kHtuTypes[] PROGMEM = "HTU21|SI7013|SI7020|SI7021|T/RH?"; - -uint8_t htu_address; -uint8_t htu_type = 0; -uint8_t htu_delay_temp; -uint8_t htu_delay_humidity = 50; -uint8_t htu_valid = 0; -float htu_temperature = 0; -float htu_humidity = 0; -char htu_types[7]; - -uint8_t HtuCheckCrc8(uint16_t data) -{ - for (uint32_t bit = 0; bit < 16; bit++) { - if (data & 0x8000) { - data = (data << 1) ^ HTU21_CRC8_POLYNOM; - } else { - data <<= 1; - } - } - return data >>= 8; -} - -uint8_t HtuReadDeviceId(void) -{ - uint16_t deviceID = 0; - uint8_t checksum = 0; - - Wire.beginTransmission(HTU21_ADDR); - Wire.write(HTU21_SERIAL2_READ1); - Wire.write(HTU21_SERIAL2_READ2); - Wire.endTransmission(); - - Wire.requestFrom(HTU21_ADDR, 3); - deviceID = Wire.read() << 8; - deviceID |= Wire.read(); - checksum = Wire.read(); - if (HtuCheckCrc8(deviceID) == checksum) { - deviceID = deviceID >> 8; - } else { - deviceID = 0; - } - return (uint8_t)deviceID; -} - -void HtuSetResolution(uint8_t resolution) -{ - uint8_t current = I2cRead8(HTU21_ADDR, HTU21_READREG); - current &= 0x7E; - current |= resolution; - I2cWrite8(HTU21_ADDR, HTU21_WRITEREG, current); -} - -void HtuReset(void) -{ - Wire.beginTransmission(HTU21_ADDR); - Wire.write(HTU21_RESET); - Wire.endTransmission(); - delay(15); -} - -void HtuHeater(uint8_t heater) -{ - uint8_t current = I2cRead8(HTU21_ADDR, HTU21_READREG); - - switch(heater) - { - case HTU21_HEATER_ON : current |= heater; - break; - case HTU21_HEATER_OFF : current &= heater; - break; - default : current &= heater; - break; - } - I2cWrite8(HTU21_ADDR, HTU21_WRITEREG, current); -} - -void HtuInit(void) -{ - HtuReset(); - HtuHeater(HTU21_HEATER_OFF); - HtuSetResolution(HTU21_RES_RH12_T14); -} - -bool HtuRead(void) -{ - uint8_t checksum = 0; - uint16_t sensorval = 0; - - if (htu_valid) { htu_valid--; } - - Wire.beginTransmission(HTU21_ADDR); - Wire.write(HTU21_READTEMP); - if (Wire.endTransmission() != 0) { return false; } - delay(htu_delay_temp); - - Wire.requestFrom(HTU21_ADDR, 3); - if (3 == Wire.available()) { - sensorval = Wire.read() << 8; - sensorval |= Wire.read(); - checksum = Wire.read(); - } - if (HtuCheckCrc8(sensorval) != checksum) { return false; } - - htu_temperature = ConvertTemp(0.002681 * (float)sensorval - 46.85); - - Wire.beginTransmission(HTU21_ADDR); - Wire.write(HTU21_READHUM); - if (Wire.endTransmission() != 0) { return false; } - delay(htu_delay_humidity); - - Wire.requestFrom(HTU21_ADDR, 3); - if (3 <= Wire.available()) { - sensorval = Wire.read() << 8; - sensorval |= Wire.read(); - checksum = Wire.read(); - } - if (HtuCheckCrc8(sensorval) != checksum) { return false; } - - sensorval ^= 0x02; - htu_humidity = 0.001907 * (float)sensorval - 6; - if (htu_humidity > 100) { htu_humidity = 100.0; } - if (htu_humidity < 0) { htu_humidity = 0.01; } - - if ((0.00 == htu_humidity) && (0.00 == htu_temperature)) { - htu_humidity = 0.0; - } - if ((htu_temperature > 0.00) && (htu_temperature < 80.00)) { - htu_humidity = (-0.15) * (25 - htu_temperature) + htu_humidity; - } - htu_humidity = ConvertHumidity(htu_humidity); - - htu_valid = SENSOR_MAX_MISS; - return true; -} - - - -void HtuDetect(void) -{ - htu_address = HTU21_ADDR; - if (I2cActive(htu_address)) { return; } - - htu_type = HtuReadDeviceId(); - if (htu_type) { - uint8_t index = 0; - HtuInit(); - switch (htu_type) { - case HTU21_CHIPID: - htu_delay_temp = 50; - htu_delay_humidity = 16; - break; - case SI7021_CHIPID: - index++; - case SI7020_CHIPID: - index++; - case SI7013_CHIPID: - index++; - htu_delay_temp = 12; - htu_delay_humidity = 23; - break; - default: - index = 4; - htu_delay_temp = 50; - htu_delay_humidity = 23; - } - GetTextIndexed(htu_types, sizeof(htu_types), index, kHtuTypes); - I2cSetActiveFound(htu_address, htu_types); - } -} - -void HtuEverySecond(void) -{ - if (uptime &1) { - - if (!HtuRead()) { - AddLogMissed(htu_types, htu_valid); - } - } -} - -void HtuShow(bool json) -{ - if (htu_valid) { - TempHumDewShow(json, (0 == tele_period), htu_types, htu_temperature, htu_humidity); - } -} - - - - - -bool Xsns08(uint8_t function) -{ - if (!I2cEnabled(XI2C_09)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - HtuDetect(); - } - else if (htu_type) { - switch (function) { - case FUNC_EVERY_SECOND: - HtuEverySecond(); - break; - case FUNC_JSON_APPEND: - HtuShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - HtuShow(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_09_bmp.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_09_bmp.ino" -#ifdef USE_I2C -#ifdef USE_BMP -# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_09_bmp.ino" -#define XSNS_09 9 -#define XI2C_10 10 - -#define BMP_ADDR1 0x76 -#define BMP_ADDR2 0x77 - -#define BMP180_CHIPID 0x55 -#define BMP280_CHIPID 0x58 -#define BME280_CHIPID 0x60 -#define BME680_CHIPID 0x61 - -#define BMP_REGISTER_CHIPID 0xD0 - -#define BMP_REGISTER_RESET 0xE0 - -#define BMP_CMND_RESET 0xB6 - -#define BMP_MAX_SENSORS 2 - -const char kBmpTypes[] PROGMEM = "BMP180|BMP280|BME280|BME680"; - -typedef struct { - uint8_t bmp_address; - char bmp_name[7]; - uint8_t bmp_type; - uint8_t bmp_model; -#ifdef USE_BME680 - uint8_t bme680_state; - float bmp_gas_resistance; -#endif - float bmp_temperature; - float bmp_pressure; - float bmp_humidity; -} bmp_sensors_t; - -uint8_t bmp_addresses[] = { BMP_ADDR1, BMP_ADDR2 }; -uint8_t bmp_count = 0; -uint8_t bmp_once = 1; - -bmp_sensors_t *bmp_sensors = nullptr; - - - - - -#define BMP180_REG_CONTROL 0xF4 -#define BMP180_REG_RESULT 0xF6 -#define BMP180_TEMPERATURE 0x2E -#define BMP180_PRESSURE3 0xF4 - -#define BMP180_AC1 0xAA -#define BMP180_AC2 0xAC -#define BMP180_AC3 0xAE -#define BMP180_AC4 0xB0 -#define BMP180_AC5 0xB2 -#define BMP180_AC6 0xB4 -#define BMP180_VB1 0xB6 -#define BMP180_VB2 0xB8 -#define BMP180_MB 0xBA -#define BMP180_MC 0xBC -#define BMP180_MD 0xBE - -#define BMP180_OSS 3 - -typedef struct { - int16_t cal_ac1; - int16_t cal_ac2; - int16_t cal_ac3; - int16_t cal_b1; - int16_t cal_b2; - int16_t cal_mc; - int16_t cal_md; - uint16_t cal_ac4; - uint16_t cal_ac5; - uint16_t cal_ac6; -} bmp180_cal_data_t; - -bmp180_cal_data_t *bmp180_cal_data = nullptr; - -bool Bmp180Calibration(uint8_t bmp_idx) -{ - if (!bmp180_cal_data) { - bmp180_cal_data = (bmp180_cal_data_t*)malloc(BMP_MAX_SENSORS * sizeof(bmp180_cal_data_t)); - } - if (!bmp180_cal_data) { return false; } - - bmp180_cal_data[bmp_idx].cal_ac1 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC1); - bmp180_cal_data[bmp_idx].cal_ac2 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC2); - bmp180_cal_data[bmp_idx].cal_ac3 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC3); - bmp180_cal_data[bmp_idx].cal_ac4 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC4); - bmp180_cal_data[bmp_idx].cal_ac5 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC5); - bmp180_cal_data[bmp_idx].cal_ac6 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC6); - bmp180_cal_data[bmp_idx].cal_b1 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_VB1); - bmp180_cal_data[bmp_idx].cal_b2 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_VB2); - bmp180_cal_data[bmp_idx].cal_mc = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_MC); - bmp180_cal_data[bmp_idx].cal_md = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_MD); - - - if (!bmp180_cal_data[bmp_idx].cal_ac1 | - !bmp180_cal_data[bmp_idx].cal_ac2 | - !bmp180_cal_data[bmp_idx].cal_ac3 | - !bmp180_cal_data[bmp_idx].cal_ac4 | - !bmp180_cal_data[bmp_idx].cal_ac5 | - !bmp180_cal_data[bmp_idx].cal_ac6 | - !bmp180_cal_data[bmp_idx].cal_b1 | - !bmp180_cal_data[bmp_idx].cal_b2 | - !bmp180_cal_data[bmp_idx].cal_mc | - !bmp180_cal_data[bmp_idx].cal_md) { - return false; - } - - if ((bmp180_cal_data[bmp_idx].cal_ac1 == (int16_t)0xFFFF) | - (bmp180_cal_data[bmp_idx].cal_ac2 == (int16_t)0xFFFF) | - (bmp180_cal_data[bmp_idx].cal_ac3 == (int16_t)0xFFFF) | - (bmp180_cal_data[bmp_idx].cal_ac4 == 0xFFFF) | - (bmp180_cal_data[bmp_idx].cal_ac5 == 0xFFFF) | - (bmp180_cal_data[bmp_idx].cal_ac6 == 0xFFFF) | - (bmp180_cal_data[bmp_idx].cal_b1 == (int16_t)0xFFFF) | - (bmp180_cal_data[bmp_idx].cal_b2 == (int16_t)0xFFFF) | - (bmp180_cal_data[bmp_idx].cal_mc == (int16_t)0xFFFF) | - (bmp180_cal_data[bmp_idx].cal_md == (int16_t)0xFFFF)) { - return false; - } - return true; -} - -void Bmp180Read(uint8_t bmp_idx) -{ - if (!bmp180_cal_data) { return; } - - I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_CONTROL, BMP180_TEMPERATURE); - delay(5); - int ut = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_RESULT); - int32_t xt1 = (ut - (int32_t)bmp180_cal_data[bmp_idx].cal_ac6) * ((int32_t)bmp180_cal_data[bmp_idx].cal_ac5) >> 15; - int32_t xt2 = ((int32_t)bmp180_cal_data[bmp_idx].cal_mc << 11) / (xt1 + (int32_t)bmp180_cal_data[bmp_idx].cal_md); - int32_t bmp180_b5 = xt1 + xt2; - bmp_sensors[bmp_idx].bmp_temperature = ((bmp180_b5 + 8) >> 4) / 10.0; - - I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_CONTROL, BMP180_PRESSURE3); - delay(2 + (4 << BMP180_OSS)); - uint32_t up = I2cRead24(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_RESULT); - up >>= (8 - BMP180_OSS); - - int32_t b6 = bmp180_b5 - 4000; - int32_t x1 = ((int32_t)bmp180_cal_data[bmp_idx].cal_b2 * ((b6 * b6) >> 12)) >> 11; - int32_t x2 = ((int32_t)bmp180_cal_data[bmp_idx].cal_ac2 * b6) >> 11; - int32_t x3 = x1 + x2; - int32_t b3 = ((((int32_t)bmp180_cal_data[bmp_idx].cal_ac1 * 4 + x3) << BMP180_OSS) + 2) >> 2; - - x1 = ((int32_t)bmp180_cal_data[bmp_idx].cal_ac3 * b6) >> 13; - x2 = ((int32_t)bmp180_cal_data[bmp_idx].cal_b1 * ((b6 * b6) >> 12)) >> 16; - x3 = ((x1 + x2) + 2) >> 2; - uint32_t b4 = ((uint32_t)bmp180_cal_data[bmp_idx].cal_ac4 * (uint32_t)(x3 + 32768)) >> 15; - uint32_t b7 = ((uint32_t)up - b3) * (uint32_t)(50000UL >> BMP180_OSS); - - int32_t p; - if (b7 < 0x80000000) { - p = (b7 * 2) / b4; - } - else { - p = (b7 / b4) * 2; - } - x1 = (p >> 8) * (p >> 8); - x1 = (x1 * 3038) >> 16; - x2 = (-7357 * p) >> 16; - p += ((x1 + x2 + (int32_t)3791) >> 4); - bmp_sensors[bmp_idx].bmp_pressure = (float)p / 100.0; -} - - - - - - - -#define BME280_REGISTER_CONTROLHUMID 0xF2 -#define BME280_REGISTER_CONTROL 0xF4 -#define BME280_REGISTER_CONFIG 0xF5 -#define BME280_REGISTER_PRESSUREDATA 0xF7 -#define BME280_REGISTER_TEMPDATA 0xFA -#define BME280_REGISTER_HUMIDDATA 0xFD - -#define BME280_REGISTER_DIG_T1 0x88 -#define BME280_REGISTER_DIG_T2 0x8A -#define BME280_REGISTER_DIG_T3 0x8C -#define BME280_REGISTER_DIG_P1 0x8E -#define BME280_REGISTER_DIG_P2 0x90 -#define BME280_REGISTER_DIG_P3 0x92 -#define BME280_REGISTER_DIG_P4 0x94 -#define BME280_REGISTER_DIG_P5 0x96 -#define BME280_REGISTER_DIG_P6 0x98 -#define BME280_REGISTER_DIG_P7 0x9A -#define BME280_REGISTER_DIG_P8 0x9C -#define BME280_REGISTER_DIG_P9 0x9E -#define BME280_REGISTER_DIG_H1 0xA1 -#define BME280_REGISTER_DIG_H2 0xE1 -#define BME280_REGISTER_DIG_H3 0xE3 -#define BME280_REGISTER_DIG_H4 0xE4 -#define BME280_REGISTER_DIG_H5 0xE5 -#define BME280_REGISTER_DIG_H6 0xE7 - -typedef struct { - uint16_t dig_T1; - int16_t dig_T2; - int16_t dig_T3; - uint16_t dig_P1; - int16_t dig_P2; - int16_t dig_P3; - int16_t dig_P4; - int16_t dig_P5; - int16_t dig_P6; - int16_t dig_P7; - int16_t dig_P8; - int16_t dig_P9; - int16_t dig_H2; - int16_t dig_H4; - int16_t dig_H5; - uint8_t dig_H1; - uint8_t dig_H3; - int8_t dig_H6; -} Bme280CalibrationData_t; - -Bme280CalibrationData_t *Bme280CalibrationData = nullptr; - -bool Bmx280Calibrate(uint8_t bmp_idx) -{ - - - if (!Bme280CalibrationData) { - Bme280CalibrationData = (Bme280CalibrationData_t*)malloc(BMP_MAX_SENSORS * sizeof(Bme280CalibrationData_t)); - } - if (!Bme280CalibrationData) { return false; } - - Bme280CalibrationData[bmp_idx].dig_T1 = I2cRead16LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T1); - Bme280CalibrationData[bmp_idx].dig_T2 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T2); - Bme280CalibrationData[bmp_idx].dig_T3 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T3); - Bme280CalibrationData[bmp_idx].dig_P1 = I2cRead16LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P1); - Bme280CalibrationData[bmp_idx].dig_P2 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P2); - Bme280CalibrationData[bmp_idx].dig_P3 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P3); - Bme280CalibrationData[bmp_idx].dig_P4 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P4); - Bme280CalibrationData[bmp_idx].dig_P5 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P5); - Bme280CalibrationData[bmp_idx].dig_P6 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P6); - Bme280CalibrationData[bmp_idx].dig_P7 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P7); - Bme280CalibrationData[bmp_idx].dig_P8 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P8); - Bme280CalibrationData[bmp_idx].dig_P9 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P9); - if (BME280_CHIPID == bmp_sensors[bmp_idx].bmp_type) { - Bme280CalibrationData[bmp_idx].dig_H1 = I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H1); - Bme280CalibrationData[bmp_idx].dig_H2 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H2); - Bme280CalibrationData[bmp_idx].dig_H3 = I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H3); - Bme280CalibrationData[bmp_idx].dig_H4 = (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H4) << 4) | (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H4 + 1) & 0xF); - Bme280CalibrationData[bmp_idx].dig_H5 = (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H5 + 1) << 4) | (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H5) >> 4); - Bme280CalibrationData[bmp_idx].dig_H6 = (int8_t)I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H6); - I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROL, 0x00); - - I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROLHUMID, 0x01); - I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONFIG, 0xA0); - I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROL, 0x27); - } else { - I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROL, 0xB7); - } - - return true; -} - -void Bme280Read(uint8_t bmp_idx) -{ - if (!Bme280CalibrationData) { return; } - - int32_t adc_T = I2cRead24(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_TEMPDATA); - adc_T >>= 4; - - int32_t vart1 = ((((adc_T >> 3) - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T1 << 1))) * ((int32_t)Bme280CalibrationData[bmp_idx].dig_T2)) >> 11; - int32_t vart2 = (((((adc_T >> 4) - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T1)) * ((adc_T >> 4) - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T1))) >> 12) * - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T3)) >> 14; - int32_t t_fine = vart1 + vart2; - float T = (t_fine * 5 + 128) >> 8; - bmp_sensors[bmp_idx].bmp_temperature = T / 100.0; - - int32_t adc_P = I2cRead24(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_PRESSUREDATA); - adc_P >>= 4; - - int64_t var1 = ((int64_t)t_fine) - 128000; - int64_t var2 = var1 * var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P6; - var2 = var2 + ((var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P5) << 17); - var2 = var2 + (((int64_t)Bme280CalibrationData[bmp_idx].dig_P4) << 35); - var1 = ((var1 * var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P3) >> 8) + ((var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P2) << 12); - var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)Bme280CalibrationData[bmp_idx].dig_P1) >> 33; - if (0 == var1) { - return; - } - int64_t p = 1048576 - adc_P; - p = (((p << 31) - var2) * 3125) / var1; - var1 = (((int64_t)Bme280CalibrationData[bmp_idx].dig_P9) * (p >> 13) * (p >> 13)) >> 25; - var2 = (((int64_t)Bme280CalibrationData[bmp_idx].dig_P8) * p) >> 19; - p = ((p + var1 + var2) >> 8) + (((int64_t)Bme280CalibrationData[bmp_idx].dig_P7) << 4); - bmp_sensors[bmp_idx].bmp_pressure = (float)p / 25600.0; - - if (BMP280_CHIPID == bmp_sensors[bmp_idx].bmp_type) { return; } - - int32_t adc_H = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_HUMIDDATA); - - int32_t v_x1_u32r = (t_fine - ((int32_t)76800)); - v_x1_u32r = (((((adc_H << 14) - (((int32_t)Bme280CalibrationData[bmp_idx].dig_H4) << 20) - - (((int32_t)Bme280CalibrationData[bmp_idx].dig_H5) * v_x1_u32r)) + ((int32_t)16384)) >> 15) * - (((((((v_x1_u32r * ((int32_t)Bme280CalibrationData[bmp_idx].dig_H6)) >> 10) * - (((v_x1_u32r * ((int32_t)Bme280CalibrationData[bmp_idx].dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + - ((int32_t)2097152)) * ((int32_t)Bme280CalibrationData[bmp_idx].dig_H2) + 8192) >> 14)); - v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * - ((int32_t)Bme280CalibrationData[bmp_idx].dig_H1)) >> 4)); - v_x1_u32r = (v_x1_u32r < 0) ? 0 : v_x1_u32r; - v_x1_u32r = (v_x1_u32r > 419430400) ? 419430400 : v_x1_u32r; - float h = (v_x1_u32r >> 12); - bmp_sensors[bmp_idx].bmp_humidity = h / 1024.0; -} - -#ifdef USE_BME680 - - - - -#include - -struct bme680_dev *gas_sensor = nullptr; - -static void BmeDelayMs(uint32_t ms) -{ - delay(ms); -} - -bool Bme680Init(uint8_t bmp_idx) -{ - if (!gas_sensor) { - gas_sensor = (bme680_dev*)malloc(BMP_MAX_SENSORS * sizeof(bme680_dev)); - } - if (!gas_sensor) { return false; } - - gas_sensor[bmp_idx].dev_id = bmp_sensors[bmp_idx].bmp_address; - gas_sensor[bmp_idx].intf = BME680_I2C_INTF; - gas_sensor[bmp_idx].read = &I2cReadBuffer; - gas_sensor[bmp_idx].write = &I2cWriteBuffer; - gas_sensor[bmp_idx].delay_ms = BmeDelayMs; - - - - gas_sensor[bmp_idx].amb_temp = 25; - - int8_t rslt = BME680_OK; - rslt = bme680_init(&gas_sensor[bmp_idx]); - if (rslt != BME680_OK) { return false; } - - - gas_sensor[bmp_idx].tph_sett.os_hum = BME680_OS_2X; - gas_sensor[bmp_idx].tph_sett.os_pres = BME680_OS_4X; - gas_sensor[bmp_idx].tph_sett.os_temp = BME680_OS_8X; - gas_sensor[bmp_idx].tph_sett.filter = BME680_FILTER_SIZE_3; - - - gas_sensor[bmp_idx].gas_sett.run_gas = BME680_ENABLE_GAS_MEAS; - - gas_sensor[bmp_idx].gas_sett.heatr_temp = 320; - gas_sensor[bmp_idx].gas_sett.heatr_dur = 150; - - - - gas_sensor[bmp_idx].power_mode = BME680_FORCED_MODE; - - - uint8_t set_required_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_FILTER_SEL | BME680_GAS_SENSOR_SEL; - - - rslt = bme680_set_sensor_settings(set_required_settings,&gas_sensor[bmp_idx]); - if (rslt != BME680_OK) { return false; } - - bmp_sensors[bmp_idx].bme680_state = 0; - - return true; -} - -void Bme680Read(uint8_t bmp_idx) -{ - if (!gas_sensor) { return; } - - int8_t rslt = BME680_OK; - - if (BME680_CHIPID == bmp_sensors[bmp_idx].bmp_type) { - if (0 == bmp_sensors[bmp_idx].bme680_state) { - - rslt = bme680_set_sensor_mode(&gas_sensor[bmp_idx]); - if (rslt != BME680_OK) { return; } - - - - - - - - bmp_sensors[bmp_idx].bme680_state = 1; - } else { - bmp_sensors[bmp_idx].bme680_state = 0; - - struct bme680_field_data data; - rslt = bme680_get_sensor_data(&data, &gas_sensor[bmp_idx]); - if (rslt != BME680_OK) { return; } - - bmp_sensors[bmp_idx].bmp_temperature = data.temperature / 100.0; - bmp_sensors[bmp_idx].bmp_humidity = data.humidity / 1000.0; - bmp_sensors[bmp_idx].bmp_pressure = data.pressure / 100.0; - - if (data.status & BME680_GASM_VALID_MSK) { - bmp_sensors[bmp_idx].bmp_gas_resistance = data.gas_resistance / 1000.0; - } else { - bmp_sensors[bmp_idx].bmp_gas_resistance = 0; - } - } - } - return; -} - -#endif - - - -void BmpDetect(void) -{ - int bmp_sensor_size = BMP_MAX_SENSORS * sizeof(bmp_sensors_t); - if (!bmp_sensors) { - bmp_sensors = (bmp_sensors_t*)malloc(bmp_sensor_size); - } - if (!bmp_sensors) { return; } - memset(bmp_sensors, 0, bmp_sensor_size); - - for (uint32_t i = 0; i < BMP_MAX_SENSORS; i++) { - if (I2cActive(bmp_addresses[i])) { continue; } - uint8_t bmp_type = I2cRead8(bmp_addresses[i], BMP_REGISTER_CHIPID); - if (bmp_type) { - bmp_sensors[bmp_count].bmp_address = bmp_addresses[i]; - bmp_sensors[bmp_count].bmp_type = bmp_type; - bmp_sensors[bmp_count].bmp_model = 0; - - bool success = false; - switch (bmp_type) { - case BMP180_CHIPID: - success = Bmp180Calibration(bmp_count); - break; - case BME280_CHIPID: - bmp_sensors[bmp_count].bmp_model++; - case BMP280_CHIPID: - bmp_sensors[bmp_count].bmp_model++; - success = Bmx280Calibrate(bmp_count); - break; -#ifdef USE_BME680 - case BME680_CHIPID: - bmp_sensors[bmp_count].bmp_model = 3; - success = Bme680Init(bmp_count); - break; -#endif - } - if (success) { - GetTextIndexed(bmp_sensors[bmp_count].bmp_name, sizeof(bmp_sensors[bmp_count].bmp_name), bmp_sensors[bmp_count].bmp_model, kBmpTypes); - I2cSetActiveFound(bmp_sensors[bmp_count].bmp_address, bmp_sensors[bmp_count].bmp_name); - bmp_count++; - } - } - } -} - -void BmpRead(void) -{ - for (uint32_t bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { - switch (bmp_sensors[bmp_idx].bmp_type) { - case BMP180_CHIPID: - Bmp180Read(bmp_idx); - break; - case BMP280_CHIPID: - case BME280_CHIPID: - Bme280Read(bmp_idx); - break; -#ifdef USE_BME680 - case BME680_CHIPID: - Bme680Read(bmp_idx); - break; -#endif - } - } -} - -void BmpShow(bool json) -{ - for (uint32_t bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { - if (bmp_sensors[bmp_idx].bmp_type) { - float bmp_sealevel = 0.0; - if (bmp_sensors[bmp_idx].bmp_pressure != 0.0) { - bmp_sealevel = (bmp_sensors[bmp_idx].bmp_pressure / FastPrecisePow(1.0 - ((float)Settings.altitude / 44330.0), 5.255)) - 21.6; - bmp_sealevel = ConvertPressure(bmp_sealevel); - } - float bmp_temperature = ConvertTemp(bmp_sensors[bmp_idx].bmp_temperature); - float bmp_pressure = ConvertPressure(bmp_sensors[bmp_idx].bmp_pressure); - - char name[10]; - strlcpy(name, bmp_sensors[bmp_idx].bmp_name, sizeof(name)); - if (bmp_count > 1) { - snprintf_P(name, sizeof(name), PSTR("%s%c%02X"), name, IndexSeparator(), bmp_sensors[bmp_idx].bmp_address); - } - - char temperature[33]; - dtostrfd(bmp_temperature, Settings.flag2.temperature_resolution, temperature); - char pressure[33]; - dtostrfd(bmp_pressure, Settings.flag2.pressure_resolution, pressure); - char sea_pressure[33]; - dtostrfd(bmp_sealevel, Settings.flag2.pressure_resolution, sea_pressure); - - float bmp_humidity = ConvertHumidity(bmp_sensors[bmp_idx].bmp_humidity); - char humidity[33]; - dtostrfd(bmp_humidity, Settings.flag2.humidity_resolution, humidity); - float f_dewpoint = CalcTempHumToDew(bmp_temperature, bmp_humidity); - char dewpoint[33]; - dtostrfd(f_dewpoint, Settings.flag2.temperature_resolution, dewpoint); -#ifdef USE_BME680 - char gas_resistance[33]; - dtostrfd(bmp_sensors[bmp_idx].bmp_gas_resistance, 2, gas_resistance); -#endif - - if (json) { - char json_humidity[80]; - snprintf_P(json_humidity, sizeof(json_humidity), PSTR(",\"" D_JSON_HUMIDITY "\":%s,\"" D_JSON_DEWPOINT "\":%s"), humidity, dewpoint); - char json_sealevel[40]; - snprintf_P(json_sealevel, sizeof(json_sealevel), PSTR(",\"" D_JSON_PRESSUREATSEALEVEL "\":%s"), sea_pressure); -#ifdef USE_BME680 - char json_gas[40]; - snprintf_P(json_gas, sizeof(json_gas), PSTR(",\"" D_JSON_GAS "\":%s"), gas_resistance); - - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s,\"" D_JSON_PRESSURE "\":%s%s%s}"), - name, - temperature, - (bmp_sensors[bmp_idx].bmp_model >= 2) ? json_humidity : "", - pressure, - (Settings.altitude != 0) ? json_sealevel : "", - (bmp_sensors[bmp_idx].bmp_model >= 3) ? json_gas : ""); -#else - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s,\"" D_JSON_PRESSURE "\":%s%s}"), - name, temperature, (bmp_sensors[bmp_idx].bmp_model >= 2) ? json_humidity : "", pressure, (Settings.altitude != 0) ? json_sealevel : ""); -#endif - -#ifdef USE_DOMOTICZ - if ((0 == tele_period) && (0 == bmp_idx)) { - DomoticzTempHumPressureSensor(bmp_temperature, bmp_humidity, bmp_pressure); -#ifdef USE_BME680 - if (bmp_sensors[bmp_idx].bmp_model >= 3) { DomoticzSensor(DZ_AIRQUALITY, (uint32_t)bmp_sensors[bmp_idx].bmp_gas_resistance); } -#endif - } -#endif - -#ifdef USE_KNX - if (0 == tele_period) { - KnxSensor(KNX_TEMPERATURE, bmp_temperature); - KnxSensor(KNX_HUMIDITY, bmp_humidity); - } -#endif - -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, name, temperature, TempUnit()); - if (bmp_sensors[bmp_idx].bmp_model >= 2) { - WSContentSend_PD(HTTP_SNS_HUM, name, humidity); - WSContentSend_PD(HTTP_SNS_DEW, name, dewpoint, TempUnit()); - } - WSContentSend_PD(HTTP_SNS_PRESSURE, name, pressure, PressureUnit().c_str()); - if (Settings.altitude != 0) { - WSContentSend_PD(HTTP_SNS_SEAPRESSURE, name, sea_pressure, PressureUnit().c_str()); - } -#ifdef USE_BME680 - if (bmp_sensors[bmp_idx].bmp_model >= 3) { - WSContentSend_PD(PSTR("{s}%s " D_GAS "{m}%s " D_UNIT_KILOOHM "{e}"), name, gas_resistance); - } -#endif - -#endif - } - } - } -} - -#ifdef USE_DEEPSLEEP - -void BMP_EnterSleep(void) -{ - for (uint32_t bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { - switch (bmp_sensors[bmp_idx].bmp_type) { - case BMP180_CHIPID: - case BMP280_CHIPID: - case BME280_CHIPID: - I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP_REGISTER_RESET, BMP_CMND_RESET); - break; - default: - break; - } - } -} - -#endif - - - - - -bool Xsns09(uint8_t function) -{ - if (!I2cEnabled(XI2C_10)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - BmpDetect(); - } - else if (bmp_count) { - switch (function) { - case FUNC_EVERY_SECOND: - BmpRead(); - break; - case FUNC_JSON_APPEND: - BmpShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - BmpShow(0); - break; -#endif -#ifdef USE_DEEPSLEEP - case FUNC_SAVE_BEFORE_RESTART: - BMP_EnterSleep(); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_10_bh1750.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_10_bh1750.ino" -#ifdef USE_I2C -#ifdef USE_BH1750 - - - - - - -#define XSNS_10 10 -#define XI2C_11 11 - -#define BH1750_ADDR1 0x23 -#define BH1750_ADDR2 0x5C - -#define BH1750_CONTINUOUS_HIGH_RES_MODE2 0x11 -#define BH1750_CONTINUOUS_HIGH_RES_MODE 0x10 -#define BH1750_CONTINUOUS_LOW_RES_MODE 0x13 - -#define BH1750_MEASUREMENT_TIME_HIGH 0x40 -#define BH1750_MEASUREMENT_TIME_LOW 0x60 - -struct BH1750DATA { - uint8_t address; - uint8_t addresses[2] = { BH1750_ADDR1, BH1750_ADDR2 }; - uint8_t resolution[3] = { BH1750_CONTINUOUS_HIGH_RES_MODE, BH1750_CONTINUOUS_HIGH_RES_MODE2, BH1750_CONTINUOUS_LOW_RES_MODE }; - uint8_t type = 0; - uint8_t valid = 0; - uint8_t mtreg = 69; - uint16_t illuminance = 0; - char types[7] = "BH1750"; -} Bh1750; - - - -bool Bh1750SetResolution(void) -{ - Wire.beginTransmission(Bh1750.address); - Wire.write(Bh1750.resolution[Settings.SensorBits1.bh1750_resolution]); - return (!Wire.endTransmission()); -} - -bool Bh1750SetMTreg(void) -{ - Wire.beginTransmission(Bh1750.address); - uint8_t data = BH1750_MEASUREMENT_TIME_HIGH | ((Bh1750.mtreg >> 5) & 0x07); - Wire.write(data); - if (Wire.endTransmission()) { return false; } - Wire.beginTransmission(Bh1750.address); - data = BH1750_MEASUREMENT_TIME_LOW | (Bh1750.mtreg & 0x1F); - Wire.write(data); - if (Wire.endTransmission()) { return false; } - return Bh1750SetResolution(); -} - -bool Bh1750Read(void) -{ - if (Bh1750.valid) { Bh1750.valid--; } - - if (2 != Wire.requestFrom(Bh1750.address, (uint8_t)2)) { return false; } - float illuminance = (Wire.read() << 8) | Wire.read(); - illuminance /= (1.2 * (69 / (float)Bh1750.mtreg)); - if (1 == Settings.SensorBits1.bh1750_resolution) { - illuminance /= 2; - } - Bh1750.illuminance = illuminance; - - Bh1750.valid = SENSOR_MAX_MISS; - return true; -} - - - -void Bh1750Detect(void) -{ - for (uint32_t i = 0; i < sizeof(Bh1750.addresses); i++) { - Bh1750.address = Bh1750.addresses[i]; - if (I2cActive(Bh1750.address)) { continue; } - - if (Bh1750SetMTreg()) { - I2cSetActiveFound(Bh1750.address, Bh1750.types); - Bh1750.type = 1; - break; - } - } -} - -void Bh1750EverySecond(void) -{ - - if (!Bh1750Read()) { - AddLogMissed(Bh1750.types, Bh1750.valid); - } -} -# 123 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_10_bh1750.ino" -bool Bh1750CommandSensor(void) -{ - if (XdrvMailbox.data_len) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { - Settings.SensorBits1.bh1750_resolution = XdrvMailbox.payload; - Bh1750SetResolution(); - } - else if ((XdrvMailbox.payload > 30) && (XdrvMailbox.payload < 255)) { - Bh1750.mtreg = XdrvMailbox.payload; - Bh1750SetMTreg(); - } - } - Response_P(PSTR("{\"" D_CMND_SENSOR "10\":{\"Resolution\":%d,\"MTime\":%d}}"), Settings.SensorBits1.bh1750_resolution, Bh1750.mtreg); - - return true; -} - -void Bh1750Show(bool json) -{ - if (Bh1750.valid) { - if (json) { - ResponseAppend_P(JSON_SNS_ILLUMINANCE, Bh1750.types, Bh1750.illuminance); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_ILLUMINANCE, Bh1750.illuminance); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_ILLUMINANCE, Bh1750.types, Bh1750.illuminance); -#endif - } - } -} - - - - - -bool Xsns10(uint8_t function) -{ - if (!I2cEnabled(XI2C_11)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Bh1750Detect(); - } - else if (Bh1750.type) { - switch (function) { - case FUNC_EVERY_SECOND: - Bh1750EverySecond(); - break; - case FUNC_COMMAND_SENSOR: - if (XSNS_10 == XdrvMailbox.index) { - result = Bh1750CommandSensor(); - } - break; - case FUNC_JSON_APPEND: - Bh1750Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Bh1750Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_11_veml6070.ino" -# 89 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_11_veml6070.ino" -#ifdef USE_I2C -#ifdef USE_VEML6070 - - - - - - -#define XSNS_11 11 -#define XI2C_12 12 - -#define VEML6070_ADDR_H 0x39 -#define VEML6070_ADDR_L 0x38 -#define VEML6070_INTEGRATION_TIME 3 -#define VEML6070_ENABLE 1 -#define VEML6070_DISABLE 0 -#define VEML6070_RSET_DEFAULT 270000 -#define VEML6070_UV_MAX_INDEX 15 -#define VEML6070_UV_MAX_DEFAULT 11 -#define VEML6070_POWER_COEFFCIENT 0.025 -#define VEML6070_TABLE_COEFFCIENT 32.86270591 - - - - - -const char kVemlTypes[] PROGMEM = "VEML6070"; -double uv_risk_map[VEML6070_UV_MAX_INDEX] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; -double uvrisk = 0; -double uvpower = 0; -uint16_t uvlevel = 0; -uint8_t veml6070_addr_low = VEML6070_ADDR_L; -uint8_t veml6070_addr_high = VEML6070_ADDR_H; -uint8_t itime = VEML6070_INTEGRATION_TIME; -uint8_t veml6070_type = 0; -char veml6070_name[9]; -char str_uvrisk_text[10]; - - - -void Veml6070Detect(void) -{ - if (I2cActive(VEML6070_ADDR_L)) { return; } - - - Wire.beginTransmission(VEML6070_ADDR_L); - Wire.write((itime << 2) | 0x02); - uint8_t status = Wire.endTransmission(); - - if (!status) { - veml6070_type = 1; - Veml6070UvTableInit(); - uint8_t veml_model = 0; - GetTextIndexed(veml6070_name, sizeof(veml6070_name), veml_model, kVemlTypes); - I2cSetActiveFound(VEML6070_ADDR_L, veml6070_name); - } -} - - - -void Veml6070UvTableInit(void) -{ - - for (uint32_t i = 0; i < VEML6070_UV_MAX_INDEX; i++) { -#ifdef USE_VEML6070_RSET - if ( (USE_VEML6070_RSET >= 220000) && (USE_VEML6070_RSET <= 1000000) ) { - uv_risk_map[i] = ( (USE_VEML6070_RSET / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); - } else { - uv_risk_map[i] = ( (VEML6070_RSET_DEFAULT / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 resistor error %d"), USE_VEML6070_RSET); - } -#else - uv_risk_map[i] = ( (VEML6070_RSET_DEFAULT / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 resistor default used %d"), VEML6070_RSET_DEFAULT); -#endif - } -} - - - -void Veml6070EverySecond(void) -{ - - Veml6070ModeCmd(1); - uvlevel = Veml6070ReadUv(); - uvrisk = Veml6070UvRiskLevel(uvlevel); - uvpower = Veml6070UvPower(uvrisk); - Veml6070ModeCmd(0); -} - - - -void Veml6070ModeCmd(bool mode_cmd) -{ - - - Wire.beginTransmission(VEML6070_ADDR_L); - Wire.write((mode_cmd << 0) | 0x02 | (itime << 2)); - uint8_t status = Wire.endTransmission(); - - if (!status) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 mode_cmd")); - } -} - - - -uint16_t Veml6070ReadUv(void) -{ - uint16_t uv_raw = 0; - - if (Wire.requestFrom(VEML6070_ADDR_H, 1) != 1) { - return -1; - } - uv_raw = Wire.read(); - uv_raw <<= 8; - - if (Wire.requestFrom(VEML6070_ADDR_L, 1) != 1) { - return -1; - } - uv_raw |= Wire.read(); - - return uv_raw; -} - - - -double Veml6070UvRiskLevel(uint16_t uv_level) -{ - double risk = 0; - if (uv_level < uv_risk_map[VEML6070_UV_MAX_INDEX-1]) { - risk = (double)uv_level / uv_risk_map[0]; - - if ( (risk >= 0) && (risk <= 2.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_1); } - else if ( (risk >= 3.0) && (risk <= 5.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_2); } - else if ( (risk >= 6.0) && (risk <= 7.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_3); } - else if ( (risk >= 8.0) && (risk <= 10.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_4); } - else if ( (risk >= 11.0) && (risk <= 12.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_5); } - else if ( (risk >= 13.0) && (risk <= 25.0) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_6); } - else { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_7); } - return risk; - } else { - - snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_7); - return ( risk = 99 ); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 out of range %d"), risk); - } -} - - - -double Veml6070UvPower(double uvrisk) -{ - - double power = 0; - return ( power = VEML6070_POWER_COEFFCIENT * uvrisk ); -} - - - - -#ifdef USE_WEBSERVER - -#ifdef USE_VEML6070_SHOW_RAW - const char HTTP_SNS_UV_LEVEL[] PROGMEM = "{s}VEML6070 " D_UV_LEVEL "{m}%s " D_UNIT_INCREMENTS "{e}"; -#endif - - const char HTTP_SNS_UV_INDEX[] PROGMEM = "{s}VEML6070 " D_UV_INDEX "{m}%s %s{e}"; - const char HTTP_SNS_UV_POWER[] PROGMEM = "{s}VEML6070 " D_UV_POWER "{m}%s " D_UNIT_WATT_METER_QUADRAT "{e}"; -#endif - - - -void Veml6070Show(bool json) -{ - - char str_uvlevel[33]; - dtostrfd((double)uvlevel, 0, str_uvlevel); - char str_uvrisk[33]; - dtostrfd(uvrisk, 2, str_uvrisk); - char str_uvpower[33]; - dtostrfd(uvpower, 3, str_uvpower); - if (json) { -#ifdef USE_VEML6070_SHOW_RAW - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_UV_LEVEL "\":%s,\"" D_JSON_UV_INDEX "\":%s,\"" D_JSON_UV_INDEX_TEXT "\":\"%s\",\"" D_JSON_UV_POWER "\":%s}"), - veml6070_name, str_uvlevel, str_uvrisk, str_uvrisk_text, str_uvpower); -#else - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_UV_INDEX "\":%s,\"" D_JSON_UV_INDEX_TEXT "\":\"%s\",\"" D_JSON_UV_POWER "\":%s}"), - veml6070_name, str_uvrisk, str_uvrisk_text, str_uvpower); -#endif -#ifdef USE_DOMOTICZ - if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, uvlevel); } -#endif -#ifdef USE_WEBSERVER - } else { -#ifdef USE_VEML6070_SHOW_RAW - WSContentSend_PD(HTTP_SNS_UV_LEVEL, str_uvlevel); -#endif - WSContentSend_PD(HTTP_SNS_UV_INDEX, str_uvrisk, str_uvrisk_text); - WSContentSend_PD(HTTP_SNS_UV_POWER, str_uvpower); -#endif - } -} - - - - - -bool Xsns11(uint8_t function) -{ - if (!I2cEnabled(XI2C_12)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Veml6070Detect(); - } - else if (veml6070_type) { - switch (function) { - case FUNC_EVERY_SECOND: - Veml6070EverySecond(); - break; - case FUNC_JSON_APPEND: - Veml6070Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Veml6070Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_12_ads1115.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_12_ads1115.ino" -#ifdef USE_I2C -#ifdef USE_ADS1115 -# 43 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_12_ads1115.ino" -#define XSNS_12 12 -#define XI2C_13 13 - -#define ADS1115_ADDRESS_ADDR_GND 0x48 -#define ADS1115_ADDRESS_ADDR_VDD 0x49 -#define ADS1115_ADDRESS_ADDR_SDA 0x4A -#define ADS1115_ADDRESS_ADDR_SCL 0x4B - -#define ADS1115_CONVERSIONDELAY (8) - - - - -#define ADS1115_REG_POINTER_MASK (0x03) -#define ADS1115_REG_POINTER_CONVERT (0x00) -#define ADS1115_REG_POINTER_CONFIG (0x01) -#define ADS1115_REG_POINTER_LOWTHRESH (0x02) -#define ADS1115_REG_POINTER_HITHRESH (0x03) - - - - -#define ADS1115_REG_CONFIG_OS_MASK (0x8000) -#define ADS1115_REG_CONFIG_OS_SINGLE (0x8000) -#define ADS1115_REG_CONFIG_OS_BUSY (0x0000) -#define ADS1115_REG_CONFIG_OS_NOTBUSY (0x8000) - -#define ADS1115_REG_CONFIG_MUX_MASK (0x7000) -#define ADS1115_REG_CONFIG_MUX_DIFF_0_1 (0x0000) -#define ADS1115_REG_CONFIG_MUX_DIFF_0_3 (0x1000) -#define ADS1115_REG_CONFIG_MUX_DIFF_1_3 (0x2000) -#define ADS1115_REG_CONFIG_MUX_DIFF_2_3 (0x3000) -#define ADS1115_REG_CONFIG_MUX_SINGLE_0 (0x4000) -#define ADS1115_REG_CONFIG_MUX_SINGLE_1 (0x5000) -#define ADS1115_REG_CONFIG_MUX_SINGLE_2 (0x6000) -#define ADS1115_REG_CONFIG_MUX_SINGLE_3 (0x7000) - -#define ADS1115_REG_CONFIG_PGA_MASK (0x0E00) -#define ADS1115_REG_CONFIG_PGA_6_144V (0x0000) -#define ADS1115_REG_CONFIG_PGA_4_096V (0x0200) -#define ADS1115_REG_CONFIG_PGA_2_048V (0x0400) -#define ADS1115_REG_CONFIG_PGA_1_024V (0x0600) -#define ADS1115_REG_CONFIG_PGA_0_512V (0x0800) -#define ADS1115_REG_CONFIG_PGA_0_256V (0x0A00) - -#define ADS1115_REG_CONFIG_MODE_MASK (0x0100) -#define ADS1115_REG_CONFIG_MODE_CONTIN (0x0000) -#define ADS1115_REG_CONFIG_MODE_SINGLE (0x0100) - -#define ADS1115_REG_CONFIG_DR_MASK (0x00E0) -#define ADS1115_REG_CONFIG_DR_128SPS (0x0000) -#define ADS1115_REG_CONFIG_DR_250SPS (0x0020) -#define ADS1115_REG_CONFIG_DR_490SPS (0x0040) -#define ADS1115_REG_CONFIG_DR_920SPS (0x0060) -#define ADS1115_REG_CONFIG_DR_1600SPS (0x0080) -#define ADS1115_REG_CONFIG_DR_2400SPS (0x00A0) -#define ADS1115_REG_CONFIG_DR_3300SPS (0x00C0) -#define ADS1115_REG_CONFIG_DR_6000SPS (0x00E0) - -#define ADS1115_REG_CONFIG_CMODE_MASK (0x0010) -#define ADS1115_REG_CONFIG_CMODE_TRAD (0x0000) -#define ADS1115_REG_CONFIG_CMODE_WINDOW (0x0010) - -#define ADS1115_REG_CONFIG_CPOL_MASK (0x0008) -#define ADS1115_REG_CONFIG_CPOL_ACTVLOW (0x0000) -#define ADS1115_REG_CONFIG_CPOL_ACTVHI (0x0008) - -#define ADS1115_REG_CONFIG_CLAT_MASK (0x0004) -#define ADS1115_REG_CONFIG_CLAT_NONLAT (0x0000) -#define ADS1115_REG_CONFIG_CLAT_LATCH (0x0004) - -#define ADS1115_REG_CONFIG_CQUE_MASK (0x0003) -#define ADS1115_REG_CONFIG_CQUE_1CONV (0x0000) -#define ADS1115_REG_CONFIG_CQUE_2CONV (0x0001) -#define ADS1115_REG_CONFIG_CQUE_4CONV (0x0002) -#define ADS1115_REG_CONFIG_CQUE_NONE (0x0003) - -struct ADS1115 { - uint8_t count = 0; - uint8_t address; - uint8_t addresses[4] = { ADS1115_ADDRESS_ADDR_GND, ADS1115_ADDRESS_ADDR_VDD, ADS1115_ADDRESS_ADDR_SDA, ADS1115_ADDRESS_ADDR_SCL }; - uint8_t found[4] = {false,false,false,false}; -} Ads1115; - - - -void Ads1115StartComparator(uint8_t channel, uint16_t mode) -{ - - uint16_t config = mode | - ADS1115_REG_CONFIG_CQUE_NONE | - ADS1115_REG_CONFIG_CLAT_NONLAT | - ADS1115_REG_CONFIG_PGA_6_144V | - ADS1115_REG_CONFIG_CPOL_ACTVLOW | - ADS1115_REG_CONFIG_CMODE_TRAD | - ADS1115_REG_CONFIG_DR_6000SPS; - - - config |= (ADS1115_REG_CONFIG_MUX_SINGLE_0 + (0x1000 * channel)); - - - I2cWrite16(Ads1115.address, ADS1115_REG_POINTER_CONFIG, config); -} - -int16_t Ads1115GetConversion(uint8_t channel) -{ - Ads1115StartComparator(channel, ADS1115_REG_CONFIG_MODE_SINGLE); - - delay(ADS1115_CONVERSIONDELAY); - - I2cRead16(Ads1115.address, ADS1115_REG_POINTER_CONVERT); - - Ads1115StartComparator(channel, ADS1115_REG_CONFIG_MODE_CONTIN); - delay(ADS1115_CONVERSIONDELAY); - - uint16_t res = I2cRead16(Ads1115.address, ADS1115_REG_POINTER_CONVERT); - return (int16_t)res; -} - - - -void Ads1115Detect(void) -{ - for (uint32_t i = 0; i < sizeof(Ads1115.addresses); i++) { - if (!Ads1115.found[i]) { - Ads1115.address = Ads1115.addresses[i]; - if (I2cActive(Ads1115.address)) { continue; } - uint16_t buffer; - if (I2cValidRead16(&buffer, Ads1115.address, ADS1115_REG_POINTER_CONVERT) && - I2cValidRead16(&buffer, Ads1115.address, ADS1115_REG_POINTER_CONFIG)) { - Ads1115StartComparator(i, ADS1115_REG_CONFIG_MODE_CONTIN); - I2cSetActiveFound(Ads1115.address, "ADS1115"); - Ads1115.found[i] = 1; - Ads1115.count++; - } - } - } -} - -void Ads1115Show(bool json) -{ - int16_t values[4]; - - for (uint32_t t = 0; t < sizeof(Ads1115.addresses); t++) { - - if (Ads1115.found[t]) { - - uint8_t old_address = Ads1115.address; - Ads1115.address = Ads1115.addresses[t]; - for (uint32_t i = 0; i < 4; i++) { - values[i] = Ads1115GetConversion(i); - - } - Ads1115.address = old_address; - - char label[15]; - if (1 == Ads1115.count) { - - snprintf_P(label, sizeof(label), PSTR("ADS1115")); - } else { - - snprintf_P(label, sizeof(label), PSTR("ADS1115%c%02x"), IndexSeparator(), Ads1115.addresses[t]); - } - - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{"), label); - for (uint32_t i = 0; i < 4; i++) { - ResponseAppend_P(PSTR("%s\"A%d\":%d"), (0 == i) ? "" : ",", i, values[i]); - } - ResponseJsonEnd(); - } -#ifdef USE_WEBSERVER - else { - for (uint32_t i = 0; i < 4; i++) { - WSContentSend_PD(HTTP_SNS_ANALOG, label, i, values[i]); - } - } -#endif - } - } -} - - - - - -bool Xsns12(uint8_t function) -{ - if (!I2cEnabled(XI2C_13)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Ads1115Detect(); - } - else if (Ads1115.count) { - switch (function) { - case FUNC_JSON_APPEND: - Ads1115Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Ads1115Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_13_ina219.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_13_ina219.ino" -#ifdef USE_I2C -#ifdef USE_INA219 -# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_13_ina219.ino" -#define XSNS_13 13 -#define XI2C_14 14 - -#define INA219_ADDRESS1 (0x40) -#define INA219_ADDRESS2 (0x41) -#define INA219_ADDRESS3 (0x44) -#define INA219_ADDRESS4 (0x45) - -#define INA219_READ (0x01) -#define INA219_REG_CONFIG (0x00) - -#define INA219_CONFIG_RESET (0x8000) - -#define INA219_CONFIG_BVOLTAGERANGE_MASK (0x2000) -#define INA219_CONFIG_BVOLTAGERANGE_16V (0x0000) -#define INA219_CONFIG_BVOLTAGERANGE_32V (0x2000) - -#define INA219_CONFIG_GAIN_MASK (0x1800) -#define INA219_CONFIG_GAIN_1_40MV (0x0000) -#define INA219_CONFIG_GAIN_2_80MV (0x0800) -#define INA219_CONFIG_GAIN_4_160MV (0x1000) -#define INA219_CONFIG_GAIN_8_320MV (0x1800) - -#define INA219_CONFIG_BADCRES_MASK (0x0780) -#define INA219_CONFIG_BADCRES_9BIT_1S_84US (0x0<<7) -#define INA219_CONFIG_BADCRES_10BIT_1S_148US (0x1<<7) -#define INA219_CONFIG_BADCRES_11BIT_1S_276US (0x2<<7) -#define INA219_CONFIG_BADCRES_12BIT_1S_532US (0x3<<7) -#define INA219_CONFIG_BADCRES_12BIT_2S_1060US (0x9<<7) -#define INA219_CONFIG_BADCRES_12BIT_4S_2130US (0xA<<7) -#define INA219_CONFIG_BADCRES_12BIT_8S_4260US (0xB<<7) -#define INA219_CONFIG_BADCRES_12BIT_16S_8510US (0xC<<7) -#define INA219_CONFIG_BADCRES_12BIT_32S_17MS (0xD<<7) -#define INA219_CONFIG_BADCRES_12BIT_64S_34MS (0xE<<7) -#define INA219_CONFIG_BADCRES_12BIT_128S_69MS (0xF<<7) - -#define INA219_CONFIG_SADCRES_MASK (0x0078) -#define INA219_CONFIG_SADCRES_9BIT_1S_84US (0x0<<3) -#define INA219_CONFIG_SADCRES_10BIT_1S_148US (0x1<<3) -#define INA219_CONFIG_SADCRES_11BIT_1S_276US (0x2<<3) -#define INA219_CONFIG_SADCRES_12BIT_1S_532US (0x3<<3) -#define INA219_CONFIG_SADCRES_12BIT_2S_1060US (0x9<<3) -#define INA219_CONFIG_SADCRES_12BIT_4S_2130US (0xA<<3) -#define INA219_CONFIG_SADCRES_12BIT_8S_4260US (0xB<<3) -#define INA219_CONFIG_SADCRES_12BIT_16S_8510US (0xC<<3) -#define INA219_CONFIG_SADCRES_12BIT_32S_17MS (0xD<<3) -#define INA219_CONFIG_SADCRES_12BIT_64S_34MS (0xE<<3) -#define INA219_CONFIG_SADCRES_12BIT_128S_69MS (0xF<<3) - -#define INA219_CONFIG_MODE_MASK (0x0007) -#define INA219_CONFIG_MODE_POWERDOWN (0x0000) -#define INA219_CONFIG_MODE_SVOLT_TRIGGERED (0x0001) -#define INA219_CONFIG_MODE_BVOLT_TRIGGERED (0x0002) -#define INA219_CONFIG_MODE_SANDBVOLT_TRIGGERED (0x0003) -#define INA219_CONFIG_MODE_ADCOFF (0x0004) -#define INA219_CONFIG_MODE_SVOLT_CONTINUOUS (0x0005) -#define INA219_CONFIG_MODE_BVOLT_CONTINUOUS (0x0006) -#define INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS (0x0007) - -#define INA219_REG_SHUNTVOLTAGE (0x01) -#define INA219_REG_BUSVOLTAGE (0x02) -#define INA219_REG_POWER (0x03) -#define INA219_REG_CURRENT (0x04) -#define INA219_REG_CALIBRATION (0x05) - -#define INA219_DEFAULT_SHUNT_RESISTOR_MILLIOHMS (100.0) - -uint8_t ina219_type[4] = {0,0,0,0}; -uint8_t ina219_addresses[] = { INA219_ADDRESS1, INA219_ADDRESS2, INA219_ADDRESS3, INA219_ADDRESS4 }; - -#ifdef DEBUG_TASMOTA_SENSOR - -char __ina219_dbg1[10]; -char __ina219_dbg2[10]; -#endif - - - - -float ina219_current_multiplier; - -uint8_t ina219_valid[4] = {0,0,0,0}; -float ina219_voltage[4] = {0,0,0,0}; -float ina219_current[4] = {0,0,0,0}; -char ina219_types[] = "INA219"; -uint8_t ina219_count = 0; -# 132 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_13_ina219.ino" -bool Ina219SetCalibration(uint8_t mode, uint16_t addr) -{ - uint16_t config = 0; - - DEBUG_SENSOR_LOG("Ina219SetCalibration: mode=%d",mode); - if (mode < 5) - { - - ina219_current_multiplier = 1.0 / INA219_DEFAULT_SHUNT_RESISTOR_MILLIOHMS; - #ifdef DEBUG_TASMOTA_SENSOR - dtostrfd(ina219_current_multiplier,5,__ina219_dbg1); - DEBUG_SENSOR_LOG("Ina219SetCalibration: cur_mul=%s",__ina219_dbg1); - #endif - } - else if (mode >= 10) - { - int mult = mode % 10; - int shunt_milliOhms = mode / 10; - for ( ; mult > 0 ; mult-- ) - shunt_milliOhms *= 10; - ina219_current_multiplier = 1.0 / shunt_milliOhms; - #ifdef DEBUG_TASMOTA_SENSOR - dtostrfd(ina219_current_multiplier,5,__ina219_dbg1); - DEBUG_SENSOR_LOG("Ina219SetCalibration: shunt=%dmO => cur_mul=%s",shunt_milliOhms,__ina219_dbg1); - #endif - } - config = INA219_CONFIG_BVOLTAGERANGE_32V - | INA219_CONFIG_GAIN_8_320MV - | INA219_CONFIG_BADCRES_12BIT_16S_8510US - | INA219_CONFIG_SADCRES_12BIT_16S_8510US - | INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS; - - return I2cWrite16(addr, INA219_REG_CONFIG, config); -} - -float Ina219GetShuntVoltage_mV(uint16_t addr) -{ - - int16_t value = I2cReadS16(addr, INA219_REG_SHUNTVOLTAGE); - DEBUG_SENSOR_LOG("Ina219GetShuntVoltage_mV: ShReg = 0x%04X",value); - - return value * 0.01; -} - -float Ina219GetBusVoltage_V(uint16_t addr) -{ - - uint16_t value = I2cRead16(addr, INA219_REG_BUSVOLTAGE) >> 3; - DEBUG_SENSOR_LOG("Ina219GetBusVoltage_V: BusReg = 0x%04X",value); - - return value * 0.004; -} -# 201 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_13_ina219.ino" -bool Ina219Read(void) -{ - for (int i=0; i= 0) && (XdrvMailbox.payload <= 255)) { - Settings.ina219_mode = XdrvMailbox.payload; - restart_flag = 2; - } - Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_13, Settings.ina219_mode); - - return true; -} - - - -void Ina219Detect(void) -{ - for (uint32_t i = 0; i < sizeof(ina219_type); i++) { - uint16_t addr = ina219_addresses[i]; - if (I2cActive(addr)) { continue; } - if (Ina219SetCalibration(Settings.ina219_mode, addr)) { - I2cSetActiveFound(addr, ina219_types); - ina219_type[i] = 1; - ina219_count++; - } - } -} - -void Ina219EverySecond(void) -{ - - Ina219Read(); -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_INA219_DATA[] PROGMEM = - "{s}%s " D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" - "{s}%s " D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" - "{s}%s " D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"; -#endif - -void Ina219Show(bool json) -{ - int num_found=0; - for (int i=0; i1) - snprintf_P(name, sizeof(name), PSTR("%s%c%d"), ina219_types, IndexSeparator(), sensor_num); - else - snprintf_P(name, sizeof(name), PSTR("%s"), ina219_types); - - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"Id\":%02x,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s,\"" D_JSON_POWERUSAGE "\":%s}"), - name, ina219_addresses[i], voltage, current, power); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_VOLTAGE, voltage); - DomoticzSensor(DZ_CURRENT, current); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_INA219_DATA, name, voltage, name, current, name, power); -#endif - } - } -} - - - - - -bool Xsns13(uint8_t function) -{ - if (!I2cEnabled(XI2C_14)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Ina219Detect(); - } - else if (ina219_count) { - switch (function) { - case FUNC_COMMAND_SENSOR: - if (XSNS_13 == XdrvMailbox.index) { - result = Ina219CommandSensor(); - } - break; - case FUNC_EVERY_SECOND: - Ina219EverySecond(); - break; - case FUNC_JSON_APPEND: - Ina219Show(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Ina219Show(0); - break; - #endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_14_sht3x.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_14_sht3x.ino" -#ifdef USE_I2C -#ifdef USE_SHT3X - - - - - - -#define XSNS_14 14 -#define XI2C_15 15 - -#define SHT3X_ADDR_GND 0x44 -#define SHT3X_ADDR_VDD 0x45 -#define SHTC3_ADDR 0x70 - -#define SHT3X_MAX_SENSORS 3 - -const char kShtTypes[] PROGMEM = "SHT3X|SHT3X|SHTC3"; -uint8_t sht3x_addresses[] = { SHT3X_ADDR_GND, SHT3X_ADDR_VDD, SHTC3_ADDR }; - -uint8_t sht3x_count = 0; -struct SHT3XSTRUCT { - uint8_t address; - char types[6]; -} sht3x_sensors[SHT3X_MAX_SENSORS]; - -bool Sht3xRead(float &t, float &h, uint8_t sht3x_address) -{ - unsigned int data[6]; - - t = NAN; - h = NAN; - - Wire.beginTransmission(sht3x_address); - if (SHTC3_ADDR == sht3x_address) { - Wire.write(0x35); - Wire.write(0x17); - Wire.endTransmission(); - Wire.beginTransmission(sht3x_address); - Wire.write(0x78); - Wire.write(0x66); - } else { - Wire.write(0x2C); - Wire.write(0x06); - } - if (Wire.endTransmission() != 0) { - return false; - } - delay(30); - Wire.requestFrom(sht3x_address, (uint8_t)6); - for (uint32_t i = 0; i < 6; i++) { - data[i] = Wire.read(); - }; - t = ConvertTemp((float)((((data[0] << 8) | data[1]) * 175) / 65535.0) - 45); - h = ConvertHumidity((float)((((data[3] << 8) | data[4]) * 100) / 65535.0)); - return (!isnan(t) && !isnan(h) && (h != 0)); -} - - - -void Sht3xDetect(void) -{ - for (uint32_t i = 0; i < SHT3X_MAX_SENSORS; i++) { - if (I2cActive(sht3x_addresses[i])) { continue; } - float t; - float h; - if (Sht3xRead(t, h, sht3x_addresses[i])) { - sht3x_sensors[sht3x_count].address = sht3x_addresses[i]; - GetTextIndexed(sht3x_sensors[sht3x_count].types, sizeof(sht3x_sensors[sht3x_count].types), i, kShtTypes); - I2cSetActiveFound(sht3x_sensors[sht3x_count].address, sht3x_sensors[sht3x_count].types); - sht3x_count++; - } - } -} - -void Sht3xShow(bool json) -{ - for (uint32_t i = 0; i < sht3x_count; i++) { - float t; - float h; - if (Sht3xRead(t, h, sht3x_sensors[i].address)) { - char types[11]; - snprintf_P(types, sizeof(types), PSTR("%s%c0x%02X"), sht3x_sensors[i].types, IndexSeparator(), sht3x_sensors[i].address); - TempHumDewShow(json, ((0 == tele_period) && (0 == i)), types, t, h); - } - } -} - - - - - -bool Xsns14(uint8_t function) -{ - if (!I2cEnabled(XI2C_15)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Sht3xDetect(); - } - else if (sht3x_count) { - switch (function) { - case FUNC_JSON_APPEND: - Sht3xShow(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Sht3xShow(0); - break; - #endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_15_mhz19.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_15_mhz19.ino" -#ifdef USE_MHZ19 -# 33 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_15_mhz19.ino" -#define XSNS_15 15 - -enum MhzFilterOptions {MHZ19_FILTER_OFF, MHZ19_FILTER_OFF_ALLSAMPLES, MHZ19_FILTER_FAST, MHZ19_FILTER_MEDIUM, MHZ19_FILTER_SLOW}; - -#define MHZ19_FILTER_OPTION MHZ19_FILTER_FAST -# 58 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_15_mhz19.ino" -#include - -#ifndef CO2_LOW -#define CO2_LOW 800 -#endif -#ifndef CO2_HIGH -#define CO2_HIGH 1200 -#endif - -#define MHZ19_READ_TIMEOUT 400 -#define MHZ19_RETRY_COUNT 8 - -TasmotaSerial *MhzSerial; - -const char kMhzModels[] PROGMEM = "|B"; - -const char ABC_ENABLED[] = "ABC is Enabled"; -const char ABC_DISABLED[] = "ABC is Disabled"; - -enum MhzCommands { MHZ_CMND_READPPM, MHZ_CMND_ABCENABLE, MHZ_CMND_ABCDISABLE, MHZ_CMND_ZEROPOINT, MHZ_CMND_RESET, MHZ_CMND_RANGE_1000, MHZ_CMND_RANGE_2000, MHZ_CMND_RANGE_3000, MHZ_CMND_RANGE_5000 }; -const uint8_t kMhzCommands[][4] PROGMEM = { - - {0x86,0x00,0x00,0x00}, - {0x79,0xA0,0x00,0x00}, - {0x79,0x00,0x00,0x00}, - {0x87,0x00,0x00,0x00}, - {0x8D,0x00,0x00,0x00}, - {0x99,0x00,0x03,0xE8}, - {0x99,0x00,0x07,0xD0}, - {0x99,0x00,0x0B,0xB8}, - {0x99,0x00,0x13,0x88}}; - -uint8_t mhz_type = 1; -uint16_t mhz_last_ppm = 0; -uint8_t mhz_filter = MHZ19_FILTER_OPTION; -bool mhz_abc_must_apply = false; - -float mhz_temperature = 0; -uint8_t mhz_retry = MHZ19_RETRY_COUNT; -uint8_t mhz_received = 0; -uint8_t mhz_state = 0; - - - -uint8_t MhzCalculateChecksum(uint8_t *array) -{ - uint8_t checksum = 0; - for (uint32_t i = 1; i < 8; i++) { - checksum += array[i]; - } - checksum = 255 - checksum; - return (checksum +1); -} - -size_t MhzSendCmd(uint8_t command_id) -{ - uint8_t mhz_send[9] = { 0 }; - - mhz_send[0] = 0xFF; - mhz_send[1] = 0x01; - memcpy_P(&mhz_send[2], kMhzCommands[command_id], sizeof(uint16_t)); - - - - - memcpy_P(&mhz_send[6], kMhzCommands[command_id] + sizeof(uint16_t), sizeof(uint16_t)); - mhz_send[8] = MhzCalculateChecksum(mhz_send); - - - - return MhzSerial->write(mhz_send, sizeof(mhz_send)); -} - - - -bool MhzCheckAndApplyFilter(uint16_t ppm, uint8_t s) -{ - if (1 == s) { - return false; - } - if (mhz_last_ppm < 400 || mhz_last_ppm > 5000) { - - - mhz_last_ppm = ppm; - return true; - } - int32_t difference = ppm - mhz_last_ppm; - if (s > 0 && s < 64 && mhz_filter != MHZ19_FILTER_OFF) { -# 154 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_15_mhz19.ino" - difference *= s; - difference /= 64; - } - if (MHZ19_FILTER_OFF == mhz_filter) { - if (s != 0 && s != 64) { - return false; - } - } else { - difference >>= (mhz_filter -1); - } - mhz_last_ppm = static_cast(mhz_last_ppm + difference); - return true; -} - -void MhzEverySecond(void) -{ - mhz_state++; - if (8 == mhz_state) { - mhz_state = 0; - - if (mhz_retry) { - mhz_retry--; - if (!mhz_retry) { - mhz_last_ppm = 0; - mhz_temperature = 0; - } - } - - MhzSerial->flush(); - MhzSendCmd(MHZ_CMND_READPPM); - mhz_received = 0; - } - - if ((mhz_state > 2) && !mhz_received) { - uint8_t mhz_response[9]; - - unsigned long start = millis(); - uint8_t counter = 0; - while (((millis() - start) < MHZ19_READ_TIMEOUT) && (counter < 9)) { - if (MhzSerial->available() > 0) { - mhz_response[counter++] = MhzSerial->read(); - } else { - delay(5); - } - } - - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, mhz_response, counter); - - if (counter < 9) { - - return; - } - - uint8_t crc = MhzCalculateChecksum(mhz_response); - if (mhz_response[8] != crc) { - - return; - } - if (0xFF != mhz_response[0] || 0x86 != mhz_response[1]) { - - return; - } - - mhz_received = 1; - - uint16_t u = (mhz_response[6] << 8) | mhz_response[7]; - if (15000 == u) { - if (Settings.SensorBits1.mhz19b_abc_disable) { - - - mhz_abc_must_apply = true; - } - } else { - uint16_t ppm = (mhz_response[2] << 8) | mhz_response[3]; - mhz_temperature = ConvertTemp((float)mhz_response[4] - 40); - uint8_t s = mhz_response[5]; - mhz_type = (s) ? 1 : 2; - if (MhzCheckAndApplyFilter(ppm, s)) { - mhz_retry = MHZ19_RETRY_COUNT; -#ifdef USE_LIGHT - LightSetSignal(CO2_LOW, CO2_HIGH, mhz_last_ppm); -#endif - - if (0 == s || 64 == s) { - if (mhz_abc_must_apply) { - mhz_abc_must_apply = false; - if (!Settings.SensorBits1.mhz19b_abc_disable) { - MhzSendCmd(MHZ_CMND_ABCENABLE); - } else { - MhzSendCmd(MHZ_CMND_ABCDISABLE); - } - } - } - - } - } - - } -} -# 268 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_15_mhz19.ino" -#define D_JSON_RANGE_1000 "1000 ppm range" -#define D_JSON_RANGE_2000 "2000 ppm range" -#define D_JSON_RANGE_3000 "3000 ppm range" -#define D_JSON_RANGE_5000 "5000 ppm range" - -bool MhzCommandSensor(void) -{ - bool serviced = true; - - switch (XdrvMailbox.payload) { - case 0: - Settings.SensorBits1.mhz19b_abc_disable = true; - MhzSendCmd(MHZ_CMND_ABCDISABLE); - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_DISABLED); - break; - case 1: - Settings.SensorBits1.mhz19b_abc_disable = false; - MhzSendCmd(MHZ_CMND_ABCENABLE); - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_ENABLED); - break; - case 2: - MhzSendCmd(MHZ_CMND_ZEROPOINT); - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_ZERO_POINT_CALIBRATION); - break; - case 9: - MhzSendCmd(MHZ_CMND_RESET); - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RESET); - break; - case 1000: - MhzSendCmd(MHZ_CMND_RANGE_1000); - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_1000); - break; - case 2000: - MhzSendCmd(MHZ_CMND_RANGE_2000); - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_2000); - break; - case 3000: - MhzSendCmd(MHZ_CMND_RANGE_3000); - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_3000); - break; - case 5000: - MhzSendCmd(MHZ_CMND_RANGE_5000); - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_5000); - break; - default: - if (!Settings.SensorBits1.mhz19b_abc_disable) { - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_ENABLED); - } else { - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_DISABLED); - } - } - - return serviced; -} - - - -void MhzInit(void) -{ - mhz_type = 0; - if ((pin[GPIO_MHZ_RXD] < 99) && (pin[GPIO_MHZ_TXD] < 99)) { - MhzSerial = new TasmotaSerial(pin[GPIO_MHZ_RXD], pin[GPIO_MHZ_TXD], 1); - if (MhzSerial->begin(9600)) { - if (MhzSerial->hardwareSerial()) { ClaimSerial(); } - mhz_type = 1; - } - - } -} - -void MhzShow(bool json) -{ - char types[7] = "MHZ19B"; - char temperature[33]; - dtostrfd(mhz_temperature, Settings.flag2.temperature_resolution, temperature); - char model[3]; - GetTextIndexed(model, sizeof(model), mhz_type -1, kMhzModels); - - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_MODEL "\":\"%s\",\"" D_JSON_CO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s}"), types, model, mhz_last_ppm, temperature); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_AIRQUALITY, mhz_last_ppm); - DomoticzSensor(DZ_TEMP, temperature); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_CO2, types, mhz_last_ppm); - WSContentSend_PD(HTTP_SNS_TEMP, types, temperature, TempUnit()); -#endif - } -} - - - - - -bool Xsns15(uint8_t function) -{ - bool result = false; - - if (mhz_type) { - switch (function) { - case FUNC_INIT: - MhzInit(); - break; - case FUNC_EVERY_SECOND: - MhzEverySecond(); - break; - case FUNC_COMMAND_SENSOR: - if (XSNS_15 == XdrvMailbox.index) { - result = MhzCommandSensor(); - } - break; - case FUNC_JSON_APPEND: - MhzShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - MhzShow(0); - break; -#endif - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_16_tsl2561.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_16_tsl2561.ino" -#ifdef USE_I2C -#ifdef USE_TSL2561 -# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_16_tsl2561.ino" -#define XSNS_16 16 -#define XI2C_16 16 - -#include - -Tsl2561 Tsl(Wire); - -uint8_t tsl2561_type = 0; -uint8_t tsl2561_valid = 0; -uint32_t tsl2561_milliLux = 0; -char tsl2561_types[] = "TSL2561"; - -bool Tsl2561Read(void) -{ - if (tsl2561_valid) { tsl2561_valid--; } - - uint8_t id; - bool gain; - Tsl2561::exposure_t exposure; - uint16_t scaledFull, scaledIr; - uint32_t full, ir; - - if (Tsl.on()) { - if (Tsl.id(id) - && Tsl2561Util::autoGain(Tsl, gain, exposure, scaledFull, scaledIr) - && Tsl2561Util::normalizedLuminosity(gain, exposure, full = scaledFull, ir = scaledIr) - && Tsl2561Util::milliLux(full, ir, tsl2561_milliLux, Tsl2561::packageCS(id))) { - } else{ - tsl2561_milliLux = 0; - } - } - tsl2561_valid = SENSOR_MAX_MISS; - return true; -} - -void Tsl2561Detect(void) -{ - if (I2cSetDevice(0x29) || I2cSetDevice(0x39) || I2cSetDevice(0x49)) { - uint8_t id; - Tsl.begin(); - if (!Tsl.id(id)) return; - if (Tsl.on()) { - tsl2561_type = 1; - I2cSetActiveFound(Tsl.address(), tsl2561_types); - } - } -} - -void Tsl2561EverySecond(void) -{ - if (!(uptime %2)) { - - if (!Tsl2561Read()) { - AddLogMissed(tsl2561_types, tsl2561_valid); - } - } -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_TSL2561[] PROGMEM = - "{s}TSL2561 " D_ILLUMINANCE "{m}%u.%03u " D_UNIT_LUX "{e}"; -#endif - -void Tsl2561Show(bool json) -{ - if (tsl2561_valid) { - if (json) { - ResponseAppend_P(PSTR(",\"TSL2561\":{\"" D_JSON_ILLUMINANCE "\":%u.%03u}"), - tsl2561_milliLux / 1000, tsl2561_milliLux % 1000); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, (tsl2561_milliLux + 500) / 1000); } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TSL2561, tsl2561_milliLux / 1000, tsl2561_milliLux % 1000); -#endif - } - } -} - - - - - -bool Xsns16(uint8_t function) -{ - if (!I2cEnabled(XI2C_16)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Tsl2561Detect(); - } - else if (tsl2561_type) { - switch (function) { - case FUNC_EVERY_SECOND: - Tsl2561EverySecond(); - break; - case FUNC_JSON_APPEND: - Tsl2561Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Tsl2561Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_17_senseair.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_17_senseair.ino" -#ifdef USE_SENSEAIR -# 29 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_17_senseair.ino" -#define XSNS_17 17 - -#define SENSEAIR_MODBUS_SPEED 9600 -#define SENSEAIR_DEVICE_ADDRESS 0xFE -#define SENSEAIR_READ_REGISTER 0x04 - -#ifndef CO2_LOW -#define CO2_LOW 800 -#endif -#ifndef CO2_HIGH -#define CO2_HIGH 1200 -#endif - -#include -TasmotaModbus *SenseairModbus; - -const char kSenseairTypes[] PROGMEM = "Kx0|S8"; - -uint8_t senseair_type = 1; -char senseair_types[7]; - -uint16_t senseair_co2 = 0; -float senseair_temperature = 0; -float senseair_humidity = 0; - - - -const uint8_t start_addresses[] { 0x1A, 0x00, 0x03, 0x04, 0x05, 0x1C, 0x0A }; - -uint8_t senseair_read_state = 0; -uint8_t senseair_send_retry = 0; - -void Senseair250ms(void) -{ - - - - - uint16_t value = 0; - bool data_ready = SenseairModbus->ReceiveReady(); - - if (data_ready) { - uint8_t error = SenseairModbus->Receive16BitRegister(&value); - if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir response error %d"), error); - } else { - switch(senseair_read_state) { - case 0: - senseair_type = 2; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir type id low %04X"), value); - break; - case 1: - if (value) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir error %04X"), value); - } - break; - case 2: - senseair_co2 = value; -#ifdef USE_LIGHT - LightSetSignal(CO2_LOW, CO2_HIGH, senseair_co2); -#endif - break; - case 3: - senseair_temperature = ConvertTemp((float)value / 100); - break; - case 4: - senseair_humidity = ConvertHumidity((float)value / 100); - break; - case 5: - { - bool relay_state = value >> 8 & 1; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir relay state %d"), relay_state); - break; - } - case 6: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir temp adjustment %d"), value); - break; - } - } - senseair_read_state++; - if (2 == senseair_type) { - if (3 == senseair_read_state) { - senseair_read_state = 1; - } - } else { - if (sizeof(start_addresses) == senseair_read_state) { - senseair_read_state = 1; - } - } - } - - if (0 == senseair_send_retry || data_ready) { - senseair_send_retry = 5; - SenseairModbus->Send(SENSEAIR_DEVICE_ADDRESS, SENSEAIR_READ_REGISTER, (uint16_t)start_addresses[senseair_read_state], 1); - } else { - senseair_send_retry--; - } - - -} - - - -void SenseairInit(void) -{ - senseair_type = 0; - if ((pin[GPIO_SAIR_RX] < 99) && (pin[GPIO_SAIR_TX] < 99)) { - SenseairModbus = new TasmotaModbus(pin[GPIO_SAIR_RX], pin[GPIO_SAIR_TX]); - uint8_t result = SenseairModbus->Begin(SENSEAIR_MODBUS_SPEED); - if (result) { - if (2 == result) { ClaimSerial(); } - senseair_type = 1; - } - } -} - -void SenseairShow(bool json) -{ - GetTextIndexed(senseair_types, sizeof(senseair_types), senseair_type -1, kSenseairTypes); - - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_CO2 "\":%d"), senseair_types, senseair_co2); - if (senseair_type != 2) { - ResponseAppend_P(PSTR(",")); - ResponseAppendTHD(senseair_temperature, senseair_humidity); - } - ResponseJsonEnd(); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_AIRQUALITY, senseair_co2); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_CO2, senseair_types, senseair_co2); - if (senseair_type != 2) { - WSContentSend_THD(senseair_types, senseair_temperature, senseair_humidity); - } -#endif - } -} - - - - - -bool Xsns17(uint8_t function) -{ - bool result = false; - - if (senseair_type) { - switch (function) { - case FUNC_INIT: - SenseairInit(); - break; - case FUNC_EVERY_250_MSECOND: - Senseair250ms(); - break; - case FUNC_JSON_APPEND: - SenseairShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - SenseairShow(0); - break; -#endif - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_18_pms5003.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_18_pms5003.ino" -#ifdef USE_PMS5003 -# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_18_pms5003.ino" -#define XSNS_18 18 - -#include - -#ifndef WARMUP_PERIOD -#define WARMUP_PERIOD 30 -#endif - -#ifndef MIN_INTERVAL_PERIOD -#define MIN_INTERVAL_PERIOD 60 -#endif - -TasmotaSerial *PmsSerial; - -struct PMS5003 { - uint16_t time = 0; - uint8_t type = 1; - uint8_t valid = 0; - uint8_t wake_mode = 1; - uint8_t ready = 1; -} Pms; - -enum PmsCommands -{ - CMD_MODE_ACTIVE, - CMD_SLEEP, - CMD_WAKEUP, - CMD_MODE_PASSIVE, - CMD_READ_DATA -}; - -const uint8_t kPmsCommands[][7] PROGMEM = { - - {0x42, 0x4D, 0xE1, 0x00, 0x01, 0x01, 0x71}, - {0x42, 0x4D, 0xE4, 0x00, 0x00, 0x01, 0x73}, - {0x42, 0x4D, 0xE4, 0x00, 0x01, 0x01, 0x74}, - {0x42, 0x4D, 0xE1, 0x00, 0x00, 0x01, 0x70}, - {0x42, 0x4D, 0xE2, 0x00, 0x00, 0x01, 0x71}}; - -struct pmsX003data { - uint16_t framelen; - uint16_t pm10_standard, pm25_standard, pm100_standard; - uint16_t pm10_env, pm25_env, pm100_env; -#ifdef PMS_MODEL_PMS3003 - uint16_t reserved1, reserved2, reserved3; -#else - uint16_t particles_03um, particles_05um, particles_10um, particles_25um, particles_50um, particles_100um; - uint16_t unused; -#endif - uint16_t checksum; -} pms_data; - - - -size_t PmsSendCmd(uint8_t command_id) -{ - return PmsSerial->write(kPmsCommands[command_id], sizeof(kPmsCommands[command_id])); -} - - - -bool PmsReadData(void) -{ - if (! PmsSerial->available()) { - return false; - } - while ((PmsSerial->peek() != 0x42) && PmsSerial->available()) { - PmsSerial->read(); - } -#ifdef PMS_MODEL_PMS3003 - if (PmsSerial->available() < 22) { -#else - if (PmsSerial->available() < 32) { -#endif - return false; - } - -#ifdef PMS_MODEL_PMS3003 - uint8_t buffer[22]; - PmsSerial->readBytes(buffer, 22); -#else - uint8_t buffer[32]; - PmsSerial->readBytes(buffer, 32); -#endif - uint16_t sum = 0; - PmsSerial->flush(); - -#ifdef PMS_MODEL_PMS3003 - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 22); -#else - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 32); -#endif - - -#ifdef PMS_MODEL_PMS3003 - for (uint32_t i = 0; i < 20; i++) { -#else - for (uint32_t i = 0; i < 30; i++) { -#endif - sum += buffer[i]; - } - -#ifdef PMS_MODEL_PMS3003 - uint16_t buffer_u16[10]; - for (uint32_t i = 0; i < 10; i++) { -#else - uint16_t buffer_u16[15]; - for (uint32_t i = 0; i < 15; i++) { -#endif - buffer_u16[i] = buffer[2 + i*2 + 1]; - buffer_u16[i] += (buffer[2 + i*2] << 8); - } -#ifdef PMS_MODEL_PMS3003 - if (sum != buffer_u16[9]) { -#else - if (sum != buffer_u16[14]) { -#endif - AddLog_P(LOG_LEVEL_DEBUG, PSTR("PMS: " D_CHECKSUM_FAILURE)); - return false; - } - -#ifdef PMS_MODEL_PMS3003 - memcpy((void *)&pms_data, (void *)buffer_u16, 20); -#else - memcpy((void *)&pms_data, (void *)buffer_u16, 30); -#endif - Pms.valid = 10; - - return true; -} -# 172 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_18_pms5003.ino" -bool PmsCommandSensor(void) -{ - if ((pin[GPIO_PMS5003_TX] < 99) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { - if (XdrvMailbox.payload < MIN_INTERVAL_PERIOD) { - - Settings.pms_wake_interval = 0; - Pms.wake_mode = 1; - Pms.ready = 1; - PmsSendCmd(CMD_MODE_ACTIVE); - PmsSendCmd(CMD_WAKEUP); - } else { - - Settings.pms_wake_interval = XdrvMailbox.payload; - PmsSendCmd(CMD_MODE_PASSIVE); - PmsSendCmd(CMD_SLEEP); - Pms.wake_mode = 0; - Pms.ready = 0; - } - } - - Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_18, Settings.pms_wake_interval); - - return true; -} - - - -void PmsSecond(void) -{ - if (Settings.pms_wake_interval >= MIN_INTERVAL_PERIOD) { - - Pms.time++; - if ((Settings.pms_wake_interval - Pms.time <= WARMUP_PERIOD) && !Pms.wake_mode) { - - Pms.wake_mode = 1; - PmsSendCmd(CMD_WAKEUP); - } - if (Pms.time >= Settings.pms_wake_interval) { - - PmsSendCmd(CMD_READ_DATA); - Pms.ready = 1; - Pms.time = 0; - } - } - - if (Pms.ready) { - if (PmsReadData()) { - Pms.valid = 10; - if (Settings.pms_wake_interval >= MIN_INTERVAL_PERIOD) { - PmsSendCmd(CMD_SLEEP); - Pms.wake_mode = 0; - Pms.ready = 0; - } - } else { - if (Pms.valid) { - Pms.valid--; - if (Settings.pms_wake_interval >= MIN_INTERVAL_PERIOD) { - PmsSendCmd(CMD_READ_DATA); - Pms.ready = 1; - } - } - } - } -} - - - -void PmsInit(void) -{ - Pms.type = 0; - if (pin[GPIO_PMS5003_RX] < 99) { - PmsSerial = new TasmotaSerial(pin[GPIO_PMS5003_RX], (pin[GPIO_PMS5003_TX] < 99) ? pin[GPIO_PMS5003_TX] : -1, 1); - if (PmsSerial->begin(9600)) { - if (PmsSerial->hardwareSerial()) { ClaimSerial(); } - - if (99 == pin[GPIO_PMS5003_TX]) { - Settings.pms_wake_interval = 0; - Pms.ready = 1; - } - - Pms.type = 1; - } - } -} - -#ifdef USE_WEBSERVER -#ifdef PMS_MODEL_PMS3003 -const char HTTP_PMS3003_SNS[] PROGMEM = - - - - "{s}PMS3003 " D_ENVIRONMENTAL_CONCENTRATION " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}PMS3003 " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}PMS3003 " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"; -#else -const char HTTP_PMS5003_SNS[] PROGMEM = - - - - "{s}PMS5003 " D_ENVIRONMENTAL_CONCENTRATION " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}PMS5003 " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}PMS5003 " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}PMS5003 " D_PARTICALS_BEYOND " 0.3 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" - "{s}PMS5003 " D_PARTICALS_BEYOND " 0.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" - "{s}PMS5003 " D_PARTICALS_BEYOND " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" - "{s}PMS5003 " D_PARTICALS_BEYOND " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" - "{s}PMS5003 " D_PARTICALS_BEYOND " 5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" - "{s}PMS5003 " D_PARTICALS_BEYOND " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}"; -#endif -#endif - -void PmsShow(bool json) -{ - if (Pms.valid) { - if (json) { -#ifdef PMS_MODEL_PMS3003 - ResponseAppend_P(PSTR(",\"PMS3003\":{\"CF1\":%d,\"CF2.5\":%d,\"CF10\":%d,\"PM1\":%d,\"PM2.5\":%d,\"PM10\":%d}"), - pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard, - pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env); -#else - ResponseAppend_P(PSTR(",\"PMS5003\":{\"CF1\":%d,\"CF2.5\":%d,\"CF10\":%d,\"PM1\":%d,\"PM2.5\":%d,\"PM10\":%d,\"PB0.3\":%d,\"PB0.5\":%d,\"PB1\":%d,\"PB2.5\":%d,\"PB5\":%d,\"PB10\":%d}"), - pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard, - pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env, - pms_data.particles_03um, pms_data.particles_05um, pms_data.particles_10um, pms_data.particles_25um, pms_data.particles_50um, pms_data.particles_100um); -#endif -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_COUNT, pms_data.pm10_env); - DomoticzSensor(DZ_VOLTAGE, pms_data.pm25_env); - DomoticzSensor(DZ_CURRENT, pms_data.pm100_env); - } -#endif -#ifdef USE_WEBSERVER - } else { - -#ifdef PMS_MODEL_PMS3003 - WSContentSend_PD(HTTP_PMS3003_SNS, - - pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env); -#else - WSContentSend_PD(HTTP_PMS5003_SNS, - - pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env, - pms_data.particles_03um, pms_data.particles_05um, pms_data.particles_10um, pms_data.particles_25um, pms_data.particles_50um, pms_data.particles_100um); -#endif -#endif - } - } -} - - - - - -bool Xsns18(uint8_t function) -{ - bool result = false; - - if (Pms.type) { - switch (function) { - case FUNC_INIT: - PmsInit(); - break; - case FUNC_EVERY_SECOND: - PmsSecond(); - break; - case FUNC_COMMAND_SENSOR: - if (XSNS_18 == XdrvMailbox.index) { - result = PmsCommandSensor(); - } - break; - case FUNC_JSON_APPEND: - PmsShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - PmsShow(0); - break; -#endif - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_19_mgs.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_19_mgs.ino" -#ifdef USE_I2C -#ifdef USE_MGS - - - - - - - -#define XSNS_19 19 -#define XI2C_17 17 - -#ifndef MGS_SENSOR_ADDR -#define MGS_SENSOR_ADDR 0x04 -#endif - -#include "MutichannelGasSensor.h" - -bool mgs_detected = false; - -void MGSInit(void) { - gas.begin(MGS_SENSOR_ADDR); -} - -void MGSPrepare(void) -{ - if (I2cActive(MGS_SENSOR_ADDR)) { return; } - - gas.begin(MGS_SENSOR_ADDR); - if (!gas.isError()) { - I2cSetActiveFound(MGS_SENSOR_ADDR, "MultiGas"); - mgs_detected = true; - } -} - -char* measure_gas(int gas_type, char* buffer) -{ - float f = gas.calcGas(gas_type); - dtostrfd(f, 2, buffer); - return buffer; -} - -#ifdef USE_WEBSERVER -const char HTTP_MGS_GAS[] PROGMEM = "{s}MGS %s{m}%s " D_UNIT_PARTS_PER_MILLION "{e}"; -#endif - -void MGSShow(bool json) -{ - char buffer[33]; - if (json) { - ResponseAppend_P(PSTR(",\"MGS\":{\"NH3\":%s"), measure_gas(NH3, buffer)); - ResponseAppend_P(PSTR(",\"CO\":%s"), measure_gas(CO, buffer)); - ResponseAppend_P(PSTR(",\"NO2\":%s"), measure_gas(NO2, buffer)); - ResponseAppend_P(PSTR(",\"C3H8\":%s"), measure_gas(C3H8, buffer)); - ResponseAppend_P(PSTR(",\"C4H10\":%s"), measure_gas(C4H10, buffer)); - ResponseAppend_P(PSTR(",\"CH4\":%s"), measure_gas(GAS_CH4, buffer)); - ResponseAppend_P(PSTR(",\"H2\":%s"), measure_gas(H2, buffer)); - ResponseAppend_P(PSTR(",\"C2H5OH\":%s}"), measure_gas(C2H5OH, buffer)); -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_MGS_GAS, "NH3", measure_gas(NH3, buffer)); - WSContentSend_PD(HTTP_MGS_GAS, "CO", measure_gas(CO, buffer)); - WSContentSend_PD(HTTP_MGS_GAS, "NO2", measure_gas(NO2, buffer)); - WSContentSend_PD(HTTP_MGS_GAS, "C3H8", measure_gas(C3H8, buffer)); - WSContentSend_PD(HTTP_MGS_GAS, "C4H10", measure_gas(C4H10, buffer)); - WSContentSend_PD(HTTP_MGS_GAS, "CH4", measure_gas(GAS_CH4, buffer)); - WSContentSend_PD(HTTP_MGS_GAS, "H2", measure_gas(H2, buffer)); - WSContentSend_PD(HTTP_MGS_GAS, "C2H5OH", measure_gas(C2H5OH, buffer)); -#endif - } -} - - - - - -bool Xsns19(uint8_t function) -{ - if (!I2cEnabled(XI2C_17)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - MGSPrepare(); - } - else if (mgs_detected) { - switch (function) { - case FUNC_JSON_APPEND: - MGSShow(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - MGSShow(0); - break; - #endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_20_novasds.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_20_novasds.ino" -#ifdef USE_NOVA_SDS -# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_20_novasds.ino" -#define XSNS_20 20 - -#include - -#ifndef STARTING_OFFSET -#define STARTING_OFFSET 30 -#endif -#if STARTING_OFFSET < 10 -#error "Please set STARTING_OFFSET >= 10" -#endif -#ifndef NOVA_SDS_RECDATA_TIMEOUT -#define NOVA_SDS_RECDATA_TIMEOUT 150 -#endif -#ifndef NOVA_SDS_DEVICE_ID -#define NOVA_SDS_DEVICE_ID 0xFFFF -#endif - -TasmotaSerial *NovaSdsSerial; - -uint8_t novasds_type = 1; -uint8_t novasds_valid = 0; -uint8_t cont_mode = 1; - -struct sds011data { - uint16_t pm100; - uint16_t pm25; -} novasds_data; -uint16_t pm100_sum; -uint16_t pm25_sum; - - -#define NOVA_SDS_REPORTING_MODE 2 -#define NOVA_SDS_QUERY_DATA 4 -#define NOVA_SDS_SET_DEVICE_ID 5 -#define NOVA_SDS_SLEEP_AND_WORK 6 -#define NOVA_SDS_WORKING_PERIOD 8 -#define NOVA_SDS_CHECK_FIRMWARE_VER 7 - #define NOVA_SDS_QUERY_MODE 0 - #define NOVA_SDS_SET_MODE 1 - #define NOVA_SDS_REPORT_ACTIVE 0 - #define NOVA_SDS_REPORT_QUERY 1 - #define NOVA_SDS_SLEEP 0 - #define NOVA_SDS_WORK 1 - - -bool NovaSdsCommand(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint16_t sensorid, uint8_t *buffer) -{ - uint8_t novasds_cmnd[19] = {0xAA, 0xB4, byte1, byte2, byte3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (uint8_t)(sensorid & 0xFF), (uint8_t)((sensorid>>8) & 0xFF), 0x00, 0xAB}; - - - for (uint32_t i = 2; i < 17; i++) { - novasds_cmnd[17] += novasds_cmnd[i]; - } - - - - - - NovaSdsSerial->write(novasds_cmnd, sizeof(novasds_cmnd)); - NovaSdsSerial->flush(); - - - unsigned long cmndtime = millis(); - while ( (TimePassedSince(cmndtime) < NOVA_SDS_RECDATA_TIMEOUT) && ( ! NovaSdsSerial->available() ) ); - if ( ! NovaSdsSerial->available() ) { - - return false; - } - uint8_t recbuf[10]; - memset(recbuf, 0, sizeof(recbuf)); - - while ( (TimePassedSince(cmndtime) < NOVA_SDS_RECDATA_TIMEOUT) && ( NovaSdsSerial->available() > 0) && (0xAA != (recbuf[0] = NovaSdsSerial->read())) ); - if ( 0xAA != recbuf[0] ) { - - return false; - } - - - NovaSdsSerial->readBytes(&recbuf[1], 9); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, recbuf, sizeof(recbuf)); - - if ( nullptr != buffer ) { - - memcpy(buffer, recbuf, sizeof(recbuf)); - } - - - if ((0xAB != recbuf[9] ) || (recbuf[8] != ((recbuf[2] + recbuf[3] + recbuf[4] + recbuf[5] + recbuf[6] + recbuf[7]) & 0xFF))) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("SDS: " D_CHECKSUM_FAILURE)); - return false; - } - - return true; -} - -void NovaSdsSetWorkPeriod(void) -{ - - NovaSdsCommand(NOVA_SDS_WORKING_PERIOD, NOVA_SDS_SET_MODE, 0, NOVA_SDS_DEVICE_ID, nullptr); - - NovaSdsCommand(NOVA_SDS_REPORTING_MODE, NOVA_SDS_SET_MODE, NOVA_SDS_REPORT_QUERY, NOVA_SDS_DEVICE_ID, nullptr); -} - -bool NovaSdsReadData(void) -{ - uint8_t d[10]; - if ( ! NovaSdsCommand(NOVA_SDS_QUERY_DATA, 0, 0, NOVA_SDS_DEVICE_ID, d) ) { - return false; - } - novasds_data.pm25 = (d[2] + 256 * d[3]); - novasds_data.pm100 = (d[4] + 256 * d[5]); - - return true; -} - - - -void NovaSdsSecond(void) -{ - if (!novasds_valid) - { - NovaSdsSetWorkPeriod(); - novasds_valid=1; - } - if((Settings.tele_period - Settings.novasds_startingoffset <= 0)) - { - if(!cont_mode) - { - cont_mode = 1; - NovaSdsCommand(NOVA_SDS_SLEEP_AND_WORK, NOVA_SDS_SET_MODE, NOVA_SDS_WORK, NOVA_SDS_DEVICE_ID, nullptr); - } - } - else - cont_mode = 0; - - if(tele_period == Settings.tele_period - Settings.novasds_startingoffset && !cont_mode) - { - NovaSdsCommand(NOVA_SDS_SLEEP_AND_WORK, NOVA_SDS_SET_MODE, NOVA_SDS_WORK, NOVA_SDS_DEVICE_ID, nullptr); - } - if(tele_period >= Settings.tele_period-5 && tele_period <= Settings.tele_period-2) - { - if(!(NovaSdsReadData())) novasds_valid=0; - pm100_sum += novasds_data.pm100; - pm25_sum += novasds_data.pm25; - } - if(tele_period == Settings.tele_period-1) - { - novasds_data.pm100 = pm100_sum >> 2; - novasds_data.pm25 = pm25_sum >> 2; - if(!cont_mode) - NovaSdsCommand(NOVA_SDS_SLEEP_AND_WORK, NOVA_SDS_SET_MODE, NOVA_SDS_SLEEP, NOVA_SDS_DEVICE_ID, nullptr); - pm100_sum = pm25_sum = 0; - } -} - - - - - - - -bool NovaSdsCommandSensor(void) -{ - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 256)) { - if( XdrvMailbox.payload < 10 ) Settings.novasds_startingoffset = 10; - else Settings.novasds_startingoffset = XdrvMailbox.payload; - } - Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_20, Settings.novasds_startingoffset); - - return true; -} - -void NovaSdsInit(void) -{ - novasds_type = 0; - if (pin[GPIO_SDS0X1_RX] < 99 && pin[GPIO_SDS0X1_TX] < 99) { - NovaSdsSerial = new TasmotaSerial(pin[GPIO_SDS0X1_RX], pin[GPIO_SDS0X1_TX], 1); - if (NovaSdsSerial->begin(9600)) { - if (NovaSdsSerial->hardwareSerial()) { - ClaimSerial(); - } - novasds_type = 1; - NovaSdsSetWorkPeriod(); - } - } -} - -#ifdef USE_WEBSERVER -const char HTTP_SDS0X1_SNS[] PROGMEM = - "{s}SDS0X1 " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}SDS0X1 " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"; -#endif - -void NovaSdsShow(bool json) -{ - if (novasds_valid) { - float pm10f = (float)(novasds_data.pm100) / 10.0f; - float pm2_5f = (float)(novasds_data.pm25) / 10.0f; - char pm10[33]; - dtostrfd(pm10f, 1, pm10); - char pm2_5[33]; - dtostrfd(pm2_5f, 1, pm2_5); - if (json) { - ResponseAppend_P(PSTR(",\"SDS0X1\":{\"PM2.5\":%s,\"PM10\":%s}"), pm2_5, pm10); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_VOLTAGE, pm2_5); - DomoticzSensor(DZ_CURRENT, pm10); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SDS0X1_SNS, pm2_5, pm10); -#endif - } - } -} - - - - - -bool Xsns20(uint8_t function) -{ - bool result = false; - - if (novasds_type) { - switch (function) { - case FUNC_INIT: - NovaSdsInit(); - break; - case FUNC_EVERY_SECOND: - NovaSdsSecond(); - break; - case FUNC_COMMAND_SENSOR: - if (XSNS_20 == XdrvMailbox.index) { - result = NovaSdsCommandSensor(); - } - break; - case FUNC_JSON_APPEND: - NovaSdsShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - NovaSdsShow(0); - break; -#endif - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_21_sgp30.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_21_sgp30.ino" -#ifdef USE_I2C -#ifdef USE_SGP30 -# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_21_sgp30.ino" -#define XSNS_21 21 -#define XI2C_18 18 - -#define SGP30_ADDRESS 0x58 - -#include "Adafruit_SGP30.h" -Adafruit_SGP30 sgp; - -bool sgp30_type = false; -bool sgp30_ready = false; -float sgp30_abshum; - - - -void sgp30_Init(void) -{ - if (I2cActive(SGP30_ADDRESS)) { return; } - - if (sgp.begin()) { - sgp30_type = true; - - I2cSetActiveFound(SGP30_ADDRESS, "SGP30"); - } -} - - -#define POW_FUNC FastPrecisePow - -float sgp30_AbsoluteHumidity(float temperature, float humidity,char tempUnit) { - - - - - - float temp = NAN; - const float mw = 18.01534; - const float r = 8.31447215; - - if (isnan(temperature) || isnan(humidity) ) { - return NAN; - } - - if (tempUnit != 'C') { - temperature = (temperature - 32.0) * (5.0 / 9.0); - } - - temp = POW_FUNC(2.718281828, (17.67 * temperature) / (temperature + 243.5)); - - - return (6.112 * temp * humidity * mw) / ((273.15 + temperature) * r); -} - -#define SAVE_PERIOD 30 - -void Sgp30Update(void) -{ - sgp30_ready = false; - if (!sgp.IAQmeasure()) { - return; - } - if (global_update && (global_humidity > 0) && (global_temperature != 9999)) { - - sgp30_abshum=sgp30_AbsoluteHumidity(global_temperature,global_humidity,TempUnit()); - sgp.setHumidity(sgp30_abshum*1000); - } - sgp30_ready = true; - - - if (!(uptime%SAVE_PERIOD)) { - - uint16_t TVOC_base; - uint16_t eCO2_base; - - if (!sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) return; - - } -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_SGP30[] PROGMEM = - "{s}SGP30 " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}" - "{s}SGP30 " D_TVOC "{m}%d " D_UNIT_PARTS_PER_BILLION "{e}"; -const char HTTP_SNS_AHUM[] PROGMEM = "{s}SGP30 Abs Humidity{m}%s g/m3{e}"; -#endif - -#define D_JSON_AHUM "aHumidity" - -void Sgp30Show(bool json) -{ - if (sgp30_ready) { - char abs_hum[33]; - - if (json) { - ResponseAppend_P(PSTR(",\"SGP30\":{\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TVOC "\":%d"), sgp.eCO2, sgp.TVOC); - if (global_update && global_humidity>0 && global_temperature!=9999) { - - dtostrfd(sgp30_abshum,4,abs_hum); - ResponseAppend_P(PSTR(",\"" D_JSON_AHUM "\":%s"),abs_hum); - } - ResponseJsonEnd(); -#ifdef USE_DOMOTICZ - if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, sgp.eCO2); -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_SGP30, sgp.eCO2, sgp.TVOC); - if (global_update) { - WSContentSend_PD(HTTP_SNS_AHUM, abs_hum); - } -#endif - } - } -} - - - - - -bool Xsns21(uint8_t function) -{ - if (!I2cEnabled(XI2C_18)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - sgp30_Init(); - } - else if (sgp30_type) { - switch (function) { - case FUNC_EVERY_SECOND: - Sgp30Update(); - break; - case FUNC_JSON_APPEND: - Sgp30Show(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Sgp30Show(0); - break; - #endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_22_sr04.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_22_sr04.ino" -#ifdef USE_SR04 - -#include -#include -# 32 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_22_sr04.ino" -#define XSNS_22 22 - -uint8_t sr04_type = 1; -real64_t distance; - -NewPing* sonar = nullptr; -TasmotaSerial* sonar_serial = nullptr; - -uint8_t Sr04TModeDetect(void) -{ - sr04_type = 0; - if (99 == pin[GPIO_SR04_ECHO]) { return sr04_type; } - - int sr04_echo_pin = pin[GPIO_SR04_ECHO]; - int sr04_trig_pin = (pin[GPIO_SR04_TRIG] < 99) ? pin[GPIO_SR04_TRIG] : -1; - sonar_serial = new TasmotaSerial(sr04_echo_pin, sr04_trig_pin, 1); - - if (sonar_serial->begin(9600,1)) { - DEBUG_SENSOR_LOG(PSTR("SR04: Detect mode")); - - if (sr04_trig_pin != -1) { - sr04_type = (Sr04TMiddleValue(Sr04TMode3Distance(), Sr04TMode3Distance(), Sr04TMode3Distance()) != NO_ECHO) ? 3 : 1; - } else { - sr04_type = 2; - } - } else { - sr04_type = 1; - } - - if (sr04_type < 2) { - delete sonar_serial; - sonar_serial = nullptr; - if (-1 == sr04_trig_pin) { - sr04_trig_pin = pin[GPIO_SR04_ECHO]; - } - sonar = new NewPing(sr04_trig_pin, sr04_echo_pin, 300); - } else { - if (sonar_serial->hardwareSerial()) { - ClaimSerial(); - } - } - - AddLog_P2(LOG_LEVEL_INFO,PSTR("SR04: Mode %d"), sr04_type); - return sr04_type; -} - -uint16_t Sr04TMiddleValue(uint16_t first, uint16_t second, uint16_t third) -{ - uint16_t ret = first; - if (first > second) { - first = second; - second = ret; - } - - if (third < first) { - return first; - } else if (third > second) { - return second; - } else { - return third; - } -} - -uint16_t Sr04TMode3Distance() { - - sonar_serial->write(0x55); - sonar_serial->flush(); - - return Sr04TMode2Distance(); -} - -uint16_t Sr04TMode2Distance(void) -{ - sonar_serial->setTimeout(300); - const char startByte = 0xff; - - if (!sonar_serial->find(startByte)) { - - return NO_ECHO; - } - - delay(5); - - uint8_t crc = sonar_serial->read(); - - uint16_t distance = ((uint16_t)crc) << 8; - - - distance += sonar_serial->read(); - crc += distance & 0x00ff; - crc += 0x00FF; - - - if (crc != sonar_serial->read()) { - AddLog_P2(LOG_LEVEL_ERROR,PSTR("SR04: Reading CRC error.")); - return NO_ECHO; - } - - return distance; -} - -void Sr04TReading(void) { - - if (sonar_serial==nullptr && sonar==nullptr) { - Sr04TModeDetect(); - } - - switch (sr04_type) { - case 3: - distance = (real64_t)(Sr04TMiddleValue(Sr04TMode3Distance(),Sr04TMode3Distance(),Sr04TMode3Distance()))/ 10; - break; - case 2: - - while(sonar_serial->available()) sonar_serial->read(); - distance = (real64_t)(Sr04TMiddleValue(Sr04TMode2Distance(),Sr04TMode2Distance(),Sr04TMode2Distance()))/10; - break; - case 1: - distance = (real64_t)(sonar->ping_median(5))/ US_ROUNDTRIP_CM; - break; - default: - distance = NO_ECHO; - } - - return; -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_DISTANCE[] PROGMEM = - "{s}SR04 " D_DISTANCE "{m}%s" D_UNIT_CENTIMETER "{e}"; -#endif - -void Sr04Show(bool json) -{ - - if (distance != 0) { - char distance_chr[33]; - dtostrfd(distance, 3, distance_chr); - - if(json) { - ResponseAppend_P(PSTR(",\"SR04\":{\"" D_JSON_DISTANCE "\":%s}"), distance_chr); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_COUNT, distance_chr); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_DISTANCE, distance_chr); -#endif - } - } -} - - - - - -bool Xsns22(uint8_t function) -{ - bool result = false; - - if (sr04_type) { - switch (function) { - case FUNC_INIT: - result = (pin[GPIO_SR04_ECHO]<99); - break; - case FUNC_EVERY_SECOND: - Sr04TReading(); - result = true; - break; - case FUNC_JSON_APPEND: - Sr04Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Sr04Show(0); - break; -#endif - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_24_si1145.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_24_si1145.ino" -#ifdef USE_I2C -#ifdef USE_SI1145 -# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_24_si1145.ino" -#define XSNS_24 24 -#define XI2C_19 19 - -#define SI114X_ADDR 0X60 - - - -#define SI114X_QUERY 0X80 -#define SI114X_SET 0XA0 -#define SI114X_NOP 0X00 -#define SI114X_RESET 0X01 -#define SI114X_BUSADDR 0X02 -#define SI114X_PS_FORCE 0X05 -#define SI114X_GET_CAL 0X12 -#define SI114X_ALS_FORCE 0X06 -#define SI114X_PSALS_FORCE 0X07 -#define SI114X_PS_PAUSE 0X09 -#define SI114X_ALS_PAUSE 0X0A -#define SI114X_PSALS_PAUSE 0X0B -#define SI114X_PS_AUTO 0X0D -#define SI114X_ALS_AUTO 0X0E -#define SI114X_PSALS_AUTO 0X0F - - - -#define SI114X_PART_ID 0X00 -#define SI114X_REV_ID 0X01 -#define SI114X_SEQ_ID 0X02 -#define SI114X_INT_CFG 0X03 -#define SI114X_IRQ_ENABLE 0X04 -#define SI114X_IRQ_MODE1 0x05 -#define SI114X_IRQ_MODE2 0x06 -#define SI114X_HW_KEY 0X07 -#define SI114X_MEAS_RATE0 0X08 -#define SI114X_MEAS_RATE1 0X09 -#define SI114X_PS_RATE 0X0A -#define SI114X_PS_LED21 0X0F -#define SI114X_PS_LED3 0X10 -#define SI114X_UCOEFF0 0X13 -#define SI114X_UCOEFF1 0X14 -#define SI114X_UCOEFF2 0X15 -#define SI114X_UCOEFF3 0X16 -#define SI114X_WR 0X17 -#define SI114X_COMMAND 0X18 -#define SI114X_RESPONSE 0X20 -#define SI114X_IRQ_STATUS 0X21 -#define SI114X_ALS_VIS_DATA0 0X22 -#define SI114X_ALS_VIS_DATA1 0X23 -#define SI114X_ALS_IR_DATA0 0X24 -#define SI114X_ALS_IR_DATA1 0X25 -#define SI114X_PS1_DATA0 0X26 -#define SI114X_PS1_DATA1 0X27 -#define SI114X_PS2_DATA0 0X28 -#define SI114X_PS2_DATA1 0X29 -#define SI114X_PS3_DATA0 0X2A -#define SI114X_PS3_DATA1 0X2B -#define SI114X_AUX_DATA0_UVINDEX0 0X2C -#define SI114X_AUX_DATA1_UVINDEX1 0X2D -#define SI114X_RD 0X2E -#define SI114X_CHIP_STAT 0X30 - - - -#define SI114X_CHLIST 0X01 -#define SI114X_CHLIST_ENUV 0x80 -#define SI114X_CHLIST_ENAUX 0x40 -#define SI114X_CHLIST_ENALSIR 0x20 -#define SI114X_CHLIST_ENALSVIS 0x10 -#define SI114X_CHLIST_ENPS1 0x01 -#define SI114X_CHLIST_ENPS2 0x02 -#define SI114X_CHLIST_ENPS3 0x04 - -#define SI114X_PSLED12_SELECT 0X02 -#define SI114X_PSLED3_SELECT 0X03 - -#define SI114X_PS_ENCODE 0X05 -#define SI114X_ALS_ENCODE 0X06 - -#define SI114X_PS1_ADCMUX 0X07 -#define SI114X_PS2_ADCMUX 0X08 -#define SI114X_PS3_ADCMUX 0X09 - -#define SI114X_PS_ADC_COUNTER 0X0A -#define SI114X_PS_ADC_GAIN 0X0B -#define SI114X_PS_ADC_MISC 0X0C - -#define SI114X_ALS_IR_ADC_MUX 0X0E -#define SI114X_AUX_ADC_MUX 0X0F - -#define SI114X_ALS_VIS_ADC_COUNTER 0X10 -#define SI114X_ALS_VIS_ADC_GAIN 0X11 -#define SI114X_ALS_VIS_ADC_MISC 0X12 - -#define SI114X_LED_REC 0X1C - -#define SI114X_ALS_IR_ADC_COUNTER 0X1D -#define SI114X_ALS_IR_ADC_GAIN 0X1E -#define SI114X_ALS_IR_ADC_MISC 0X1F - - - - -#define SI114X_ADCMUX_SMALL_IR 0x00 -#define SI114X_ADCMUX_VISIABLE 0x02 -#define SI114X_ADCMUX_LARGE_IR 0x03 -#define SI114X_ADCMUX_NO 0x06 -#define SI114X_ADCMUX_GND 0x25 -#define SI114X_ADCMUX_TEMPERATURE 0x65 -#define SI114X_ADCMUX_VDD 0x75 - -#define SI114X_PSLED12_SELECT_PS1_NONE 0x00 -#define SI114X_PSLED12_SELECT_PS1_LED1 0x01 -#define SI114X_PSLED12_SELECT_PS1_LED2 0x02 -#define SI114X_PSLED12_SELECT_PS1_LED3 0x04 -#define SI114X_PSLED12_SELECT_PS2_NONE 0x00 -#define SI114X_PSLED12_SELECT_PS2_LED1 0x10 -#define SI114X_PSLED12_SELECT_PS2_LED2 0x20 -#define SI114X_PSLED12_SELECT_PS2_LED3 0x40 -#define SI114X_PSLED3_SELECT_PS2_NONE 0x00 -#define SI114X_PSLED3_SELECT_PS2_LED1 0x10 -#define SI114X_PSLED3_SELECT_PS2_LED2 0x20 -#define SI114X_PSLED3_SELECT_PS2_LED3 0x40 - -#define SI114X_ADC_GAIN_DIV1 0X00 -#define SI114X_ADC_GAIN_DIV2 0X01 -#define SI114X_ADC_GAIN_DIV4 0X02 -#define SI114X_ADC_GAIN_DIV8 0X03 -#define SI114X_ADC_GAIN_DIV16 0X04 -#define SI114X_ADC_GAIN_DIV32 0X05 - -#define SI114X_LED_CURRENT_5MA 0X01 -#define SI114X_LED_CURRENT_11MA 0X02 -#define SI114X_LED_CURRENT_22MA 0X03 -#define SI114X_LED_CURRENT_45MA 0X04 - -#define SI114X_ADC_COUNTER_1ADCCLK 0X00 -#define SI114X_ADC_COUNTER_7ADCCLK 0X01 -#define SI114X_ADC_COUNTER_15ADCCLK 0X02 -#define SI114X_ADC_COUNTER_31ADCCLK 0X03 -#define SI114X_ADC_COUNTER_63ADCCLK 0X04 -#define SI114X_ADC_COUNTER_127ADCCLK 0X05 -#define SI114X_ADC_COUNTER_255ADCCLK 0X06 -#define SI114X_ADC_COUNTER_511ADCCLK 0X07 - -#define SI114X_ADC_MISC_LOWRANGE 0X00 -#define SI114X_ADC_MISC_HIGHRANGE 0X20 -#define SI114X_ADC_MISC_ADC_NORMALPROXIMITY 0X00 -#define SI114X_ADC_MISC_ADC_RAWADC 0X04 - -#define SI114X_INT_CFG_INTOE 0X01 - -#define SI114X_IRQEN_ALS 0x01 -#define SI114X_IRQEN_PS1 0x04 -#define SI114X_IRQEN_PS2 0x08 -#define SI114X_IRQEN_PS3 0x10 - -uint16_t si1145_visible; -uint16_t si1145_infrared; -uint16_t si1145_uvindex; - -bool si1145_type = false; -uint8_t si1145_valid = 0; - - - -uint8_t Si1145ReadByte(uint8_t reg) -{ - return I2cRead8(SI114X_ADDR, reg); -} - -uint16_t Si1145ReadHalfWord(uint8_t reg) -{ - return I2cRead16LE(SI114X_ADDR, reg); -} - -bool Si1145WriteByte(uint8_t reg, uint16_t val) -{ - I2cWrite8(SI114X_ADDR, reg, val); -} - -uint8_t Si1145WriteParamData(uint8_t p, uint8_t v) -{ - Si1145WriteByte(SI114X_WR, v); - Si1145WriteByte(SI114X_COMMAND, p | SI114X_SET); - return Si1145ReadByte(SI114X_RD); -} - - - -bool Si1145Present(void) -{ - return (Si1145ReadByte(SI114X_PART_ID) == 0X45); -} - -void Si1145Reset(void) -{ - Si1145WriteByte(SI114X_MEAS_RATE0, 0); - Si1145WriteByte(SI114X_MEAS_RATE1, 0); - Si1145WriteByte(SI114X_IRQ_ENABLE, 0); - Si1145WriteByte(SI114X_IRQ_MODE1, 0); - Si1145WriteByte(SI114X_IRQ_MODE2, 0); - Si1145WriteByte(SI114X_INT_CFG, 0); - Si1145WriteByte(SI114X_IRQ_STATUS, 0xFF); - - Si1145WriteByte(SI114X_COMMAND, SI114X_RESET); - delay(10); - Si1145WriteByte(SI114X_HW_KEY, 0x17); - delay(10); -} - -void Si1145DeInit(void) -{ - - - Si1145WriteByte(SI114X_UCOEFF0, 0x29); - Si1145WriteByte(SI114X_UCOEFF1, 0x89); - Si1145WriteByte(SI114X_UCOEFF2, 0x02); - Si1145WriteByte(SI114X_UCOEFF3, 0x00); - Si1145WriteParamData(SI114X_CHLIST, SI114X_CHLIST_ENUV | SI114X_CHLIST_ENALSIR | SI114X_CHLIST_ENALSVIS | SI114X_CHLIST_ENPS1); - - - - Si1145WriteParamData(SI114X_PS1_ADCMUX, SI114X_ADCMUX_LARGE_IR); - Si1145WriteByte(SI114X_PS_LED21, SI114X_LED_CURRENT_22MA); - Si1145WriteParamData(SI114X_PSLED12_SELECT, SI114X_PSLED12_SELECT_PS1_LED1); - - - - Si1145WriteParamData(SI114X_PS_ADC_GAIN, SI114X_ADC_GAIN_DIV1); - Si1145WriteParamData(SI114X_PS_ADC_COUNTER, SI114X_ADC_COUNTER_511ADCCLK); - Si1145WriteParamData(SI114X_PS_ADC_MISC, SI114X_ADC_MISC_HIGHRANGE | SI114X_ADC_MISC_ADC_RAWADC); - - - - Si1145WriteParamData(SI114X_ALS_VIS_ADC_GAIN, SI114X_ADC_GAIN_DIV1); - Si1145WriteParamData(SI114X_ALS_VIS_ADC_COUNTER, SI114X_ADC_COUNTER_511ADCCLK); - Si1145WriteParamData(SI114X_ALS_VIS_ADC_MISC, SI114X_ADC_MISC_HIGHRANGE); - - - - Si1145WriteParamData(SI114X_ALS_IR_ADC_GAIN, SI114X_ADC_GAIN_DIV1); - Si1145WriteParamData(SI114X_ALS_IR_ADC_COUNTER, SI114X_ADC_COUNTER_511ADCCLK); - Si1145WriteParamData(SI114X_ALS_IR_ADC_MISC, SI114X_ADC_MISC_HIGHRANGE); - - - - Si1145WriteByte(SI114X_INT_CFG, SI114X_INT_CFG_INTOE); - Si1145WriteByte(SI114X_IRQ_ENABLE, SI114X_IRQEN_ALS); - - - - Si1145WriteByte(SI114X_MEAS_RATE0, 0xFF); - Si1145WriteByte(SI114X_COMMAND, SI114X_PSALS_AUTO); -} - -bool Si1145Begin(void) -{ - if (!Si1145Present()) { return false; } - - Si1145Reset(); - Si1145DeInit(); - return true; -} - - -uint16_t Si1145ReadUV(void) -{ - return Si1145ReadHalfWord(SI114X_AUX_DATA0_UVINDEX0); -} - - -uint16_t Si1145ReadVisible(void) -{ - return Si1145ReadHalfWord(SI114X_ALS_VIS_DATA0); -} - - -uint16_t Si1145ReadIR(void) -{ - return Si1145ReadHalfWord(SI114X_ALS_IR_DATA0); -} - - - -bool Si1145Read(void) -{ - if (si1145_valid) { si1145_valid--; } - - if (!Si1145Present()) { return false; } - - si1145_visible = Si1145ReadVisible(); - si1145_infrared = Si1145ReadIR(); - si1145_uvindex = Si1145ReadUV(); - si1145_valid = SENSOR_MAX_MISS; - return true; -} - -void Si1145Detect(void) -{ - if (I2cActive(SI114X_ADDR)) { return; } - - if (Si1145Begin()) { - si1145_type = true; - I2cSetActiveFound(SI114X_ADDR, "SI1145"); - } -} - -void Si1145Update(void) -{ - if (!Si1145Read()) { - AddLogMissed("SI1145", si1145_valid); - } -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_SI1145[] PROGMEM = - "{s}SI1145 " D_ILLUMINANCE "{m}%d " D_UNIT_LUX "{e}" - "{s}SI1145 " D_INFRARED "{m}%d " D_UNIT_LUX "{e}" - "{s}SI1145 " D_UV_INDEX "{m}%d.%d{e}"; -#endif - -void Si1145Show(bool json) -{ - if (si1145_valid) { - if (json) { - ResponseAppend_P(PSTR(",\"SI1145\":{\"" D_JSON_ILLUMINANCE "\":%d,\"" D_JSON_INFRARED "\":%d,\"" D_JSON_UV_INDEX "\":%d.%d}"), - si1145_visible, si1145_infrared, si1145_uvindex /100, si1145_uvindex %100); -#ifdef USE_DOMOTICZ - if (0 == tele_period) DomoticzSensor(DZ_ILLUMINANCE, si1145_visible); -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_SI1145, si1145_visible, si1145_infrared, si1145_uvindex /100, si1145_uvindex %100); -#endif - } - } -} - - - - - -bool Xsns24(uint8_t function) -{ - if (!I2cEnabled(XI2C_19)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Si1145Detect(); - } - else if (si1145_type) { - switch (function) { - case FUNC_EVERY_SECOND: - Si1145Update(); - break; - case FUNC_JSON_APPEND: - Si1145Show(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Si1145Show(0); - break; - #endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_26_lm75ad.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_26_lm75ad.ino" -#ifdef USE_I2C -#ifdef USE_LM75AD -# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_26_lm75ad.ino" -#define XSNS_26 26 -#define XI2C_20 20 - -#define LM75AD_ADDRESS1 0x48 -#define LM75AD_ADDRESS2 0x49 -#define LM75AD_ADDRESS3 0x4A -#define LM75AD_ADDRESS4 0x4B -#define LM75AD_ADDRESS5 0x4C -#define LM75AD_ADDRESS6 0x4D -#define LM75AD_ADDRESS7 0x4E -#define LM75AD_ADDRESS8 0x4F - -#define LM75_TEMP_REGISTER 0x00 -#define LM75_CONF_REGISTER 0x01 -#define LM75_THYST_REGISTER 0x02 -#define LM75_TOS_REGISTER 0x03 - -bool lm75ad_type = false; -uint8_t lm75ad_address; -uint8_t lm75ad_addresses[] = { LM75AD_ADDRESS1, LM75AD_ADDRESS2, LM75AD_ADDRESS3, LM75AD_ADDRESS4, LM75AD_ADDRESS5, LM75AD_ADDRESS6, LM75AD_ADDRESS7, LM75AD_ADDRESS8 }; - -void LM75ADDetect(void) -{ - for (uint32_t i = 0; i < sizeof(lm75ad_addresses); i++) { - lm75ad_address = lm75ad_addresses[i]; - if (I2cActive(lm75ad_address)) { - continue; } - if (!I2cSetDevice(lm75ad_address)) { - break; - } - uint16_t buffer; - if (I2cValidRead16(&buffer, lm75ad_address, LM75_THYST_REGISTER)) { - if (buffer == 0x4B00) { - lm75ad_type = true; - I2cSetActiveFound(lm75ad_address, "LM75AD"); - break; - } - } - } -} - -float LM75ADGetTemp(void) -{ - int16_t sign = 1; - - uint16_t t = I2cRead16(lm75ad_address, LM75_TEMP_REGISTER); - if (t & 0x8000) { - t = (~t) +0x20; - sign = -1; - } - t = t >> 5; - return ConvertTemp(sign * t * 0.125); -} - -void LM75ADShow(bool json) -{ - float t = LM75ADGetTemp(); - char temperature[33]; - dtostrfd(t, Settings.flag2.temperature_resolution, temperature); - - if (json) { - ResponseAppend_P(PSTR(",\"LM75AD\":{\"" D_JSON_TEMPERATURE "\":%s}"), temperature); -#ifdef USE_DOMOTICZ - if (0 == tele_period) DomoticzSensor(DZ_TEMP, temperature); -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, "LM75AD", temperature, TempUnit()); -#endif - } -} - - - - - -bool Xsns26(uint8_t function) -{ - if (!I2cEnabled(XI2C_20)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - LM75ADDetect(); - } - else if (lm75ad_type) { - switch (function) { - case FUNC_JSON_APPEND: - LM75ADShow(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - LM75ADShow(0); - break; - #endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" -# 28 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" -#ifdef USE_I2C -#ifdef USE_APDS9960 -# 39 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" -#define XSNS_27 27 -#define XI2C_21 21 -# 55 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" -#define APDS9960_I2C_ADDR 0x39 - -#define APDS9960_CHIPID_1 0xAB -#define APDS9960_CHIPID_2 0x9C -#define APDS9960_CHIPID_3 0xA8 - -#define APDS9930_CHIPID_1 0x12 -#define APDS9930_CHIPID_2 0x39 - - -#define GESTURE_THRESHOLD_OUT 10 -#define GESTURE_SENSITIVITY_1 50 -#define GESTURE_SENSITIVITY_2 20 - -uint8_t APDS9960addr; -uint8_t APDS9960type = 0; -char APDS9960stype[] = "APDS9960"; -char currentGesture[6]; -uint8_t gesture_mode = 1; - - -volatile uint8_t recovery_loop_counter = 0; -#define APDS9960_LONG_RECOVERY 50 -#define APDS9960_MAX_GESTURE_CYCLES 50 -bool APDS9960_overload = false; - -#ifdef USE_WEBSERVER -const char HTTP_APDS_9960_SNS[] PROGMEM = - "{s}" "Red" "{m}%s{e}" - "{s}" "Green" "{m}%s{e}" - "{s}" "Blue" "{m}%s{e}" - "{s}" "Ambient" "{m}%s " D_UNIT_LUX "{e}" - "{s}" "CCT" "{m}%s " "K" "{e}" - "{s}" "Proximity" "{m}%s{e}"; -#endif -# 98 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" -#define FIFO_PAUSE_TIME 30 - - -#define APDS9960_ENABLE 0x80 -#define APDS9960_ATIME 0x81 -#define APDS9960_WTIME 0x83 -#define APDS9960_AILTL 0x84 -#define APDS9960_AILTH 0x85 -#define APDS9960_AIHTL 0x86 -#define APDS9960_AIHTH 0x87 -#define APDS9960_PILT 0x89 -#define APDS9960_PIHT 0x8B -#define APDS9960_PERS 0x8C -#define APDS9960_CONFIG1 0x8D -#define APDS9960_PPULSE 0x8E -#define APDS9960_CONTROL 0x8F -#define APDS9960_CONFIG2 0x90 -#define APDS9960_ID 0x92 -#define APDS9960_STATUS 0x93 -#define APDS9960_CDATAL 0x94 -#define APDS9960_CDATAH 0x95 -#define APDS9960_RDATAL 0x96 -#define APDS9960_RDATAH 0x97 -#define APDS9960_GDATAL 0x98 -#define APDS9960_GDATAH 0x99 -#define APDS9960_BDATAL 0x9A -#define APDS9960_BDATAH 0x9B -#define APDS9960_PDATA 0x9C -#define APDS9960_POFFSET_UR 0x9D -#define APDS9960_POFFSET_DL 0x9E -#define APDS9960_CONFIG3 0x9F -#define APDS9960_GPENTH 0xA0 -#define APDS9960_GEXTH 0xA1 -#define APDS9960_GCONF1 0xA2 -#define APDS9960_GCONF2 0xA3 -#define APDS9960_GOFFSET_U 0xA4 -#define APDS9960_GOFFSET_D 0xA5 -#define APDS9960_GOFFSET_L 0xA7 -#define APDS9960_GOFFSET_R 0xA9 -#define APDS9960_GPULSE 0xA6 -#define APDS9960_GCONF3 0xAA -#define APDS9960_GCONF4 0xAB -#define APDS9960_GFLVL 0xAE -#define APDS9960_GSTATUS 0xAF -#define APDS9960_IFORCE 0xE4 -#define APDS9960_PICLEAR 0xE5 -#define APDS9960_CICLEAR 0xE6 -#define APDS9960_AICLEAR 0xE7 -#define APDS9960_GFIFO_U 0xFC -#define APDS9960_GFIFO_D 0xFD -#define APDS9960_GFIFO_L 0xFE -#define APDS9960_GFIFO_R 0xFF - - -#define APDS9960_PON 0b00000001 -#define APDS9960_AEN 0b00000010 -#define APDS9960_PEN 0b00000100 -#define APDS9960_WEN 0b00001000 -#define APSD9960_AIEN 0b00010000 -#define APDS9960_PIEN 0b00100000 -#define APDS9960_GEN 0b01000000 -#define APDS9960_GVALID 0b00000001 - - -#define OFF 0 -#define ON 1 - - -#define POWER 0 -#define AMBIENT_LIGHT 1 -#define PROXIMITY 2 -#define WAIT 3 -#define AMBIENT_LIGHT_INT 4 -#define PROXIMITY_INT 5 -#define GESTURE 6 -#define ALL 7 - - -#define LED_DRIVE_100MA 0 -#define LED_DRIVE_50MA 1 -#define LED_DRIVE_25MA 2 -#define LED_DRIVE_12_5MA 3 - - -#define PGAIN_1X 0 -#define PGAIN_2X 1 -#define PGAIN_4X 2 -#define PGAIN_8X 3 - - -#define AGAIN_1X 0 -#define AGAIN_4X 1 -#define AGAIN_16X 2 -#define AGAIN_64X 3 - - -#define GGAIN_1X 0 -#define GGAIN_2X 1 -#define GGAIN_4X 2 -#define GGAIN_8X 3 - - -#define LED_BOOST_100 0 -#define LED_BOOST_150 1 -#define LED_BOOST_200 2 -#define LED_BOOST_300 3 - - -#define GWTIME_0MS 0 -#define GWTIME_2_8MS 1 -#define GWTIME_5_6MS 2 -#define GWTIME_8_4MS 3 -#define GWTIME_14_0MS 4 -#define GWTIME_22_4MS 5 -#define GWTIME_30_8MS 6 -#define GWTIME_39_2MS 7 - - -#define DEFAULT_ATIME 0xdb -#define DEFAULT_WTIME 246 -#define DEFAULT_PROX_PPULSE 0x87 -#define DEFAULT_GESTURE_PPULSE 0x89 -#define DEFAULT_POFFSET_UR 0 -#define DEFAULT_POFFSET_DL 0 -#define DEFAULT_CONFIG1 0x60 -#define DEFAULT_LDRIVE LED_DRIVE_100MA -#define DEFAULT_PGAIN PGAIN_4X -#define DEFAULT_AGAIN AGAIN_4X -#define DEFAULT_PILT 0 -#define DEFAULT_PIHT 50 -#define DEFAULT_AILT 0xFFFF -#define DEFAULT_AIHT 0 -#define DEFAULT_PERS 0x11 -#define DEFAULT_CONFIG2 0x01 -#define DEFAULT_CONFIG3 0 -#define DEFAULT_GPENTH 40 -#define DEFAULT_GEXTH 30 -#define DEFAULT_GCONF1 0x40 -#define DEFAULT_GGAIN GGAIN_4X -#define DEFAULT_GLDRIVE LED_DRIVE_100MA -#define DEFAULT_GWTIME GWTIME_2_8MS -#define DEFAULT_GOFFSET 0 -#define DEFAULT_GPULSE 0xC9 -#define DEFAULT_GCONF3 0 -#define DEFAULT_GIEN 0 - -#define APDS9960_ERROR 0xFF - - -enum { - DIR_NONE, - DIR_LEFT, - DIR_RIGHT, - DIR_UP, - DIR_DOWN, - DIR_ALL -}; - - -enum { - APDS9960_NA_STATE, - APDS9960_ALL_STATE -}; - - -typedef struct gesture_data_type { - uint8_t u_data[32]; - uint8_t d_data[32]; - uint8_t l_data[32]; - uint8_t r_data[32]; - uint8_t index; - uint8_t total_gestures; - uint8_t in_threshold; - uint8_t out_threshold; -} gesture_data_type; - - - gesture_data_type gesture_data_; - int16_t gesture_ud_delta_ = 0; - int16_t gesture_lr_delta_ = 0; - int16_t gesture_ud_count_ = 0; - int16_t gesture_lr_count_ = 0; - int16_t gesture_state_ = 0; - int16_t gesture_motion_ = DIR_NONE; - - typedef struct color_data_type { - uint16_t a; - uint16_t r; - uint16_t g; - uint16_t b; - uint8_t p; - uint16_t cct; - uint16_t lux; - } color_data_type; - - color_data_type color_data; - uint8_t APDS9960_aTime = DEFAULT_ATIME; -# 307 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" - bool wireWriteByte(uint8_t val) - { - Wire.beginTransmission(APDS9960_I2C_ADDR); - Wire.write(val); - if( Wire.endTransmission() != 0 ) { - return false; - } - - return true; - } -# 326 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" -int8_t wireReadDataBlock( uint8_t reg, - uint8_t *val, - uint16_t len) -{ - unsigned char i = 0; - - - if (!wireWriteByte(reg)) { - return -1; - } - - - Wire.requestFrom(APDS9960_I2C_ADDR, len); - while (Wire.available()) { - if (i >= len) { - return -1; - } - val[i] = Wire.read(); - i++; - } - - return i; -} - - - - - - - -void calculateColorTemperature(void) -{ - float X, Y, Z; - float xc, yc; - float n; - float cct; - - - - - - X = (-0.14282F * color_data.r) + (1.54924F * color_data.g) + (-0.95641F * color_data.b); - Y = (-0.32466F * color_data.r) + (1.57837F * color_data.g) + (-0.73191F * color_data.b); - Z = (-0.68202F * color_data.r) + (0.77073F * color_data.g) + ( 0.56332F * color_data.b); - - - xc = (X) / (X + Y + Z); - yc = (Y) / (X + Y + Z); - - - n = (xc - 0.3320F) / (0.1858F - yc); - - - color_data.cct = (449.0F * FastPrecisePowf(n, 3)) + (3525.0F * FastPrecisePowf(n, 2)) + (6823.3F * n) + 5520.33F; - - return; -} -# 393 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" - uint8_t getProxIntLowThresh(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PILT) ; - return val; - } - - - - - - - void setProxIntLowThresh(uint8_t threshold) - { - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PILT, threshold); - } - - - - - - - uint8_t getProxIntHighThresh(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PIHT) ; - return val; - } - - - - - - - - void setProxIntHighThresh(uint8_t threshold) - { - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PIHT, threshold); - } -# 449 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" - uint8_t getLEDDrive(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL) ; - - val = (val >> 6) & 0b00000011; - - return val; - } -# 472 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" - void setLEDDrive(uint8_t drive) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); - - - drive &= 0b00000011; - drive = drive << 6; - val &= 0b00111111; - val |= drive; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); - } -# 501 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" - uint8_t getProximityGain(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL) ; - - val = (val >> 2) & 0b00000011; - - return val; - } -# 524 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" - void setProximityGain(uint8_t drive) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); - - - drive &= 0b00000011; - drive = drive << 2; - val &= 0b11110011; - val |= drive; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); - } -# 565 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" - void setAmbientLightGain(uint8_t drive) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); - - - drive &= 0b00000011; - val &= 0b11111100; - val |= drive; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); - } -# 592 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" - uint8_t getLEDBoost(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG2) ; - - - val = (val >> 4) & 0b00000011; - - return val; - } -# 616 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" - void setLEDBoost(uint8_t boost) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG2) ; - - boost &= 0b00000011; - boost = boost << 4; - val &= 0b11001111; - val |= boost; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG2, val) ; - } - - - - - - - uint8_t getProxGainCompEnable(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; - - - val = (val >> 5) & 0b00000001; - - return val; - } - - - - - - - void setProxGainCompEnable(uint8_t enable) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; - - - enable &= 0b00000001; - enable = enable << 5; - val &= 0b11011111; - val |= enable; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, val) ; - } -# 684 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" - uint8_t getProxPhotoMask(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; - - - val &= 0b00001111; - - return val; - } -# 709 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" - void setProxPhotoMask(uint8_t mask) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; - - - mask &= 0b00001111; - val &= 0b11110000; - val |= mask; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, val) ; - } - - - - - - - uint8_t getGestureEnterThresh(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GPENTH) ; - - return val; - } - - - - - - - void setGestureEnterThresh(uint8_t threshold) - { - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GPENTH, threshold) ; - - } - - - - - - - uint8_t getGestureExitThresh(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GEXTH) ; - - return val; - } - - - - - - - void setGestureExitThresh(uint8_t threshold) - { - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GEXTH, threshold) ; - } -# 787 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" - uint8_t getGestureGain(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; - - - val = (val >> 5) & 0b00000011; - - return val; - } -# 811 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" - void setGestureGain(uint8_t gain) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; - - - gain &= 0b00000011; - gain = gain << 5; - val &= 0b10011111; - val |= gain; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val) ; - } -# 839 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" - uint8_t getGestureLEDDrive(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; - - - val = (val >> 3) & 0b00000011; - - return val; - } -# 863 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" - void setGestureLEDDrive(uint8_t drive) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; - - - drive &= 0b00000011; - drive = drive << 3; - val &= 0b11100111; - val |= drive; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val) ; - } -# 895 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" - uint8_t getGestureWaitTime(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; - - - val &= 0b00000111; - - return val; - } -# 923 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" - void setGestureWaitTime(uint8_t time) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; - - - time &= 0b00000111; - val &= 0b11111000; - val |= time; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val) ; - } - - - - - - - void getLightIntLowThreshold(uint16_t &threshold) - { - uint8_t val_byte; - threshold = 0; - - - val_byte = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AILTL) ; - threshold = val_byte; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTH, val_byte) ; - threshold = threshold + ((uint16_t)val_byte << 8); - } - - - - - - - - void setLightIntLowThreshold(uint16_t threshold) - { - uint8_t val_low; - uint8_t val_high; - - - val_low = threshold & 0x00FF; - val_high = (threshold & 0xFF00) >> 8; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTL, val_low) ; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTH, val_high) ; - - } - - - - - - - - void getLightIntHighThreshold(uint16_t &threshold) - { - uint8_t val_byte; - threshold = 0; - - - val_byte = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AIHTL); - threshold = val_byte; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTH, val_byte) ; - threshold = threshold + ((uint16_t)val_byte << 8); - } - - - - - - - void setLightIntHighThreshold(uint16_t threshold) - { - uint8_t val_low; - uint8_t val_high; - - - val_low = threshold & 0x00FF; - val_high = (threshold & 0xFF00) >> 8; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTL, val_low); - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTH, val_high) ; - } - - - - - - - - void getProximityIntLowThreshold(uint8_t &threshold) - { - threshold = 0; - - - threshold = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PILT); - - } - - - - - - - void setProximityIntLowThreshold(uint8_t threshold) - { - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PILT, threshold) ; - } -# 1056 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" - void getProximityIntHighThreshold(uint8_t &threshold) - { - threshold = 0; - - - threshold = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PIHT) ; - - } - - - - - - - void setProximityIntHighThreshold(uint8_t threshold) - { - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PIHT, threshold) ; - } - - - - - - - uint8_t getAmbientLightIntEnable(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; - - - val = (val >> 4) & 0b00000001; - - return val; - } - - - - - - - void setAmbientLightIntEnable(uint8_t enable) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE); - - - enable &= 0b00000001; - enable = enable << 4; - val &= 0b11101111; - val |= enable; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, val) ; - } - - - - - - - uint8_t getProximityIntEnable(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; - - - val = (val >> 5) & 0b00000001; - - return val; - } - - - - - - - void setProximityIntEnable(uint8_t enable) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; - - - enable &= 0b00000001; - enable = enable << 5; - val &= 0b11011111; - val |= enable; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, val) ; - } - - - - - - - uint8_t getGestureIntEnable(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; - - - val = (val >> 1) & 0b00000001; - - return val; - } - - - - - - - void setGestureIntEnable(uint8_t enable) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; - - - enable &= 0b00000001; - enable = enable << 1; - val &= 0b11111101; - val |= enable; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF4, val) ; - } - - - - - - void clearAmbientLightInt(void) - { - uint8_t throwaway; - throwaway = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AICLEAR); - } - - - - - - void clearProximityInt(void) - { - uint8_t throwaway; - throwaway = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PICLEAR) ; - - } - - - - - - - uint8_t getGestureMode(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; - - - val &= 0b00000001; - - return val; - } - - - - - - - void setGestureMode(uint8_t mode) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; - - - mode &= 0b00000001; - val &= 0b11111110; - val |= mode; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF4, val) ; - } - - -bool APDS9960_init(void) -{ - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, DEFAULT_ATIME) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_WTIME, DEFAULT_WTIME) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PPULSE, DEFAULT_PROX_PPULSE) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_POFFSET_UR, DEFAULT_POFFSET_UR) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_POFFSET_DL, DEFAULT_POFFSET_DL) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG1, DEFAULT_CONFIG1) ; - - setLEDDrive(DEFAULT_LDRIVE); - - setProximityGain(DEFAULT_PGAIN); - - setAmbientLightGain(DEFAULT_AGAIN); - - setProxIntLowThresh(DEFAULT_PILT) ; - - setProxIntHighThresh(DEFAULT_PIHT); - - setLightIntLowThreshold(DEFAULT_AILT) ; - - setLightIntHighThreshold(DEFAULT_AIHT) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PERS, DEFAULT_PERS) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG2, DEFAULT_CONFIG2) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, DEFAULT_CONFIG3) ; - - - setGestureEnterThresh(DEFAULT_GPENTH); - - setGestureExitThresh(DEFAULT_GEXTH) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF1, DEFAULT_GCONF1) ; - - setGestureGain(DEFAULT_GGAIN) ; - - setGestureLEDDrive(DEFAULT_GLDRIVE) ; - - setGestureWaitTime(DEFAULT_GWTIME) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_U, DEFAULT_GOFFSET) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_D, DEFAULT_GOFFSET) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_L, DEFAULT_GOFFSET) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_R, DEFAULT_GOFFSET) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GPULSE, DEFAULT_GPULSE) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF3, DEFAULT_GCONF3) ; - - setGestureIntEnable(DEFAULT_GIEN); - - disablePower(); - - return true; -} -# 1334 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" -uint8_t getMode(void) -{ - uint8_t enable_value; - - - enable_value = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; - - return enable_value; -} - - - - - - - -void setMode(uint8_t mode, uint8_t enable) -{ - uint8_t reg_val; - - - reg_val = getMode(); - - - - enable = enable & 0x01; - if( mode >= 0 && mode <= 6 ) { - if (enable) { - reg_val |= (1 << mode); - } else { - reg_val &= ~(1 << mode); - } - } else if( mode == ALL ) { - if (enable) { - reg_val = 0x7F; - } else { - reg_val = 0x00; - } - } - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, reg_val) ; -} - - - - - - -void enableLightSensor(void) -{ - - setAmbientLightGain(DEFAULT_AGAIN); - setAmbientLightIntEnable(0); - enablePower() ; - setMode(AMBIENT_LIGHT, 1) ; -} - - - - - -void disableLightSensor(void) -{ - setAmbientLightIntEnable(0) ; - setMode(AMBIENT_LIGHT, 0) ; -} - - - - - - -void enableProximitySensor(void) -{ - - setProximityGain(DEFAULT_PGAIN); - setLEDDrive(DEFAULT_LDRIVE) ; - setProximityIntEnable(0) ; - enablePower(); - setMode(PROXIMITY, 1) ; -} - - - - - -void disableProximitySensor(void) -{ - setProximityIntEnable(0) ; - setMode(PROXIMITY, 0) ; -} - - - - - - -void enableGestureSensor(void) -{ - - - - - - - - resetGestureParameters(); - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_WTIME, 0xFF) ; - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PPULSE, DEFAULT_GESTURE_PPULSE) ; - setLEDBoost(LED_BOOST_100); - setGestureIntEnable(0) ; - setGestureMode(1); - enablePower() ; - setMode(WAIT, 1) ; - setMode(PROXIMITY, 1) ; - setMode(GESTURE, 1); -} - - - - - -void disableGestureSensor(void) -{ - resetGestureParameters(); - setGestureIntEnable(0) ; - setGestureMode(0) ; - setMode(GESTURE, 0) ; -} - - - - - - -bool isGestureAvailable(void) -{ - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GSTATUS) ; - - - val &= APDS9960_GVALID; - - - if( val == 1) { - return true; - } else { - return false; - } -} - - - - - - -int16_t readGesture(void) -{ - uint8_t fifo_level = 0; - uint8_t bytes_read = 0; - uint8_t fifo_data[128]; - uint8_t gstatus; - uint16_t motion; - uint16_t i; - uint8_t gesture_loop_counter = 0; - - - if( !isGestureAvailable() || !(getMode() & 0b01000001) ) { - return DIR_NONE; - } - - - while(1) { - if (gesture_loop_counter == APDS9960_MAX_GESTURE_CYCLES){ - disableGestureSensor(); - APDS9960_overload = true; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("Sensor overload")); - } - gesture_loop_counter += 1; - - delay(FIFO_PAUSE_TIME); - - - gstatus = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GSTATUS); - - - if( (gstatus & APDS9960_GVALID) == APDS9960_GVALID ) { - - - fifo_level = I2cRead8(APDS9960_I2C_ADDR,APDS9960_GFLVL) ; - - - if( fifo_level > 0) { - bytes_read = wireReadDataBlock( APDS9960_GFIFO_U, - (uint8_t*)fifo_data, - (fifo_level * 4) ); - if( bytes_read == -1 ) { - return APDS9960_ERROR; - } - - - if( bytes_read >= 4 ) { - for( i = 0; i < bytes_read; i += 4 ) { - gesture_data_.u_data[gesture_data_.index] = \ - fifo_data[i + 0]; - gesture_data_.d_data[gesture_data_.index] = \ - fifo_data[i + 1]; - gesture_data_.l_data[gesture_data_.index] = \ - fifo_data[i + 2]; - gesture_data_.r_data[gesture_data_.index] = \ - fifo_data[i + 3]; - gesture_data_.index++; - gesture_data_.total_gestures++; - } - - if( processGestureData() ) { - if( decodeGesture() ) { - - } - } - - gesture_data_.index = 0; - gesture_data_.total_gestures = 0; - } - } - } else { - - - delay(FIFO_PAUSE_TIME); - decodeGesture(); - motion = gesture_motion_; - resetGestureParameters(); - return motion; - } - } -} - - - - - -void enablePower(void) -{ - setMode(POWER, 1) ; -} - - - - - -void disablePower(void) -{ - setMode(POWER, 0) ; -} -# 1601 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" -void readAllColorAndProximityData(void) -{ - if (I2cReadBuffer(APDS9960_I2C_ADDR, APDS9960_CDATAL, (uint8_t *) &color_data, (uint16_t)9)) - { - - - } -} -# 1617 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" -void resetGestureParameters(void) -{ - gesture_data_.index = 0; - gesture_data_.total_gestures = 0; - - gesture_ud_delta_ = 0; - gesture_lr_delta_ = 0; - - gesture_ud_count_ = 0; - gesture_lr_count_ = 0; - - gesture_state_ = 0; - gesture_motion_ = DIR_NONE; -} - - - - - - -bool processGestureData(void) -{ - uint8_t u_first = 0; - uint8_t d_first = 0; - uint8_t l_first = 0; - uint8_t r_first = 0; - uint8_t u_last = 0; - uint8_t d_last = 0; - uint8_t l_last = 0; - uint8_t r_last = 0; - uint16_t ud_ratio_first; - uint16_t lr_ratio_first; - uint16_t ud_ratio_last; - uint16_t lr_ratio_last; - uint16_t ud_delta; - uint16_t lr_delta; - uint16_t i; - - - if( gesture_data_.total_gestures <= 4 ) { - return false; - } - - - if( (gesture_data_.total_gestures <= 32) && \ - (gesture_data_.total_gestures > 0) ) { - - - for( i = 0; i < gesture_data_.total_gestures; i++ ) { - if( (gesture_data_.u_data[i] > GESTURE_THRESHOLD_OUT) && - (gesture_data_.d_data[i] > GESTURE_THRESHOLD_OUT) && - (gesture_data_.l_data[i] > GESTURE_THRESHOLD_OUT) && - (gesture_data_.r_data[i] > GESTURE_THRESHOLD_OUT) ) { - - u_first = gesture_data_.u_data[i]; - d_first = gesture_data_.d_data[i]; - l_first = gesture_data_.l_data[i]; - r_first = gesture_data_.r_data[i]; - break; - } - } - - - if( (u_first == 0) || (d_first == 0) || \ - (l_first == 0) || (r_first == 0) ) { - - return false; - } - - for( i = gesture_data_.total_gestures - 1; i >= 0; i-- ) { - - if( (gesture_data_.u_data[i] > GESTURE_THRESHOLD_OUT) && - (gesture_data_.d_data[i] > GESTURE_THRESHOLD_OUT) && - (gesture_data_.l_data[i] > GESTURE_THRESHOLD_OUT) && - (gesture_data_.r_data[i] > GESTURE_THRESHOLD_OUT) ) { - - u_last = gesture_data_.u_data[i]; - d_last = gesture_data_.d_data[i]; - l_last = gesture_data_.l_data[i]; - r_last = gesture_data_.r_data[i]; - break; - } - } - } - - - ud_ratio_first = ((u_first - d_first) * 100) / (u_first + d_first); - lr_ratio_first = ((l_first - r_first) * 100) / (l_first + r_first); - ud_ratio_last = ((u_last - d_last) * 100) / (u_last + d_last); - lr_ratio_last = ((l_last - r_last) * 100) / (l_last + r_last); - - - ud_delta = ud_ratio_last - ud_ratio_first; - lr_delta = lr_ratio_last - lr_ratio_first; - - - gesture_ud_delta_ += ud_delta; - gesture_lr_delta_ += lr_delta; - - - if( gesture_ud_delta_ >= GESTURE_SENSITIVITY_1 ) { - gesture_ud_count_ = 1; - } else if( gesture_ud_delta_ <= -GESTURE_SENSITIVITY_1 ) { - gesture_ud_count_ = -1; - } else { - gesture_ud_count_ = 0; - } - - - if( gesture_lr_delta_ >= GESTURE_SENSITIVITY_1 ) { - gesture_lr_count_ = 1; - } else if( gesture_lr_delta_ <= -GESTURE_SENSITIVITY_1 ) { - gesture_lr_count_ = -1; - } else { - gesture_lr_count_ = 0; - } - return false; -} - - - - - - -bool decodeGesture(void) -{ - - - if( (gesture_ud_count_ == -1) && (gesture_lr_count_ == 0) ) { - gesture_motion_ = DIR_UP; - } else if( (gesture_ud_count_ == 1) && (gesture_lr_count_ == 0) ) { - gesture_motion_ = DIR_DOWN; - } else if( (gesture_ud_count_ == 0) && (gesture_lr_count_ == 1) ) { - gesture_motion_ = DIR_RIGHT; - } else if( (gesture_ud_count_ == 0) && (gesture_lr_count_ == -1) ) { - gesture_motion_ = DIR_LEFT; - } else if( (gesture_ud_count_ == -1) && (gesture_lr_count_ == 1) ) { - if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { - gesture_motion_ = DIR_UP; - } else { - gesture_motion_ = DIR_RIGHT; - } - } else if( (gesture_ud_count_ == 1) && (gesture_lr_count_ == -1) ) { - if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { - gesture_motion_ = DIR_DOWN; - } else { - gesture_motion_ = DIR_LEFT; - } - } else if( (gesture_ud_count_ == -1) && (gesture_lr_count_ == -1) ) { - if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { - gesture_motion_ = DIR_UP; - } else { - gesture_motion_ = DIR_LEFT; - } - } else if( (gesture_ud_count_ == 1) && (gesture_lr_count_ == 1) ) { - if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { - gesture_motion_ = DIR_DOWN; - } else { - gesture_motion_ = DIR_RIGHT; - } - } else { - return false; - } - - return true; -} - -void handleGesture(void) { - if (isGestureAvailable() ) { - switch (readGesture()) { - case DIR_UP: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("UP")); - snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Up")); - break; - case DIR_DOWN: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("DOWN")); - snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Down")); - break; - case DIR_LEFT: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("LEFT")); - snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Left")); - break; - case DIR_RIGHT: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("RIGHT")); - snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Right")); - break; - default: - if(APDS9960_overload) - { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("LONG")); - snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Long")); - } - else{ - AddLog_P(LOG_LEVEL_DEBUG, PSTR("NONE")); - snprintf_P(currentGesture, sizeof(currentGesture), PSTR("None")); - } - } - MqttPublishSensor(); - } -} - -void APDS9960_adjustATime(void) -{ - - I2cValidRead16LE(&color_data.a, APDS9960_I2C_ADDR, APDS9960_CDATAL); - - - if (color_data.a < (uint16_t)20){ - APDS9960_aTime = 0x40; - } - else if (color_data.a < (uint16_t)40){ - APDS9960_aTime = 0x80; - } - else if (color_data.a < (uint16_t)50){ - APDS9960_aTime = DEFAULT_ATIME; - } - else if (color_data.a < (uint16_t)70){ - APDS9960_aTime = 0xc0; - } - if (color_data.a < 200){ - APDS9960_aTime = 0xe9; - } - - - - else{ - APDS9960_aTime = 0xff; - } - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, APDS9960_aTime); - enablePower(); - enableLightSensor(); - delay(20); -} - - -void APDS9960_loop(void) -{ - if (recovery_loop_counter > 0){ - recovery_loop_counter -= 1; - } - if (recovery_loop_counter == 1 && APDS9960_overload){ - enableGestureSensor(); - APDS9960_overload = false; - Response_P(PSTR("{\"Gesture\":\"On\"}")); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); - gesture_mode = 1; - } - - if (gesture_mode) { - if (recovery_loop_counter == 0){ - handleGesture(); - - if (APDS9960_overload) - { - disableGestureSensor(); - recovery_loop_counter = APDS9960_LONG_RECOVERY; - Response_P(PSTR("{\"Gesture\":\"Off\"}")); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); - gesture_mode = 0; - } - } - } -} - -void APDS9960_detect(void) -{ - if (APDS9960type || I2cActive(APDS9960_I2C_ADDR)) { return; } - - APDS9960type = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ID); - if (APDS9960type == APDS9960_CHIPID_1 || APDS9960type == APDS9960_CHIPID_2 || APDS9960type == APDS9960_CHIPID_3) { - if (APDS9960_init()) { - I2cSetActiveFound(APDS9960_I2C_ADDR, APDS9960stype); - - enableProximitySensor(); - enableGestureSensor(); - } else { - APDS9960type = 0; - } - } else { - APDS9960type = 0; - } - currentGesture[0] = '\0'; -} - - - - - -void APDS9960_show(bool json) -{ - if (!APDS9960type) { return; } - - if (!gesture_mode && !APDS9960_overload) { - char red_chr[10]; - char green_chr[10]; - char blue_chr[10]; - char ambient_chr[10]; - char cct_chr[10]; - char prox_chr[10]; - - readAllColorAndProximityData(); - - sprintf (ambient_chr, "%u", color_data.a/4); - sprintf (red_chr, "%u", color_data.r); - sprintf (green_chr, "%u", color_data.g); - sprintf (blue_chr, "%u", color_data.b ); - sprintf (prox_chr, "%u", color_data.p ); - - - - - - calculateColorTemperature(); - sprintf (cct_chr, "%u", color_data.cct); - - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"Red\":%s,\"Green\":%s,\"Blue\":%s,\"Ambient\":%s,\"CCT\":%s,\"Proximity\":%s}"), - APDS9960stype, red_chr, green_chr, blue_chr, ambient_chr, cct_chr, prox_chr); -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_APDS_9960_SNS, red_chr, green_chr, blue_chr, ambient_chr, cct_chr, prox_chr ); -#endif - } - } - else { - if (json && (currentGesture[0] != '\0' )) { - ResponseAppend_P(PSTR(",\"%s\":{\"%s\":1}"), APDS9960stype, currentGesture); - currentGesture[0] = '\0'; - } - } -} -# 1962 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_27_apds9960.ino" -bool APDS9960CommandSensor(void) -{ - bool serviced = true; - - switch (XdrvMailbox.payload) { - case 0: - disableGestureSensor(); - gesture_mode = 0; - enableLightSensor(); - APDS9960_overload = false; - break; - case 1: - if (APDS9960type) { - setGestureGain(DEFAULT_GGAIN); - setProximityGain(DEFAULT_PGAIN); - disableLightSensor(); - enableGestureSensor(); - gesture_mode = 1; - } - break; - case 2: - if (APDS9960type) { - setGestureGain(GGAIN_2X); - setProximityGain(PGAIN_2X); - disableLightSensor(); - enableGestureSensor(); - gesture_mode = 1; - } - break; - default: - int temp_aTime = (uint8_t)XdrvMailbox.payload; - if (temp_aTime > 2 && temp_aTime < 256){ - disablePower(); - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, temp_aTime); - enablePower(); - enableLightSensor(); - } - break; - } - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_27, GetStateText(gesture_mode)); - - return serviced; -} - - - - - -bool Xsns27(uint8_t function) -{ - if (!I2cEnabled(XI2C_21)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - APDS9960_detect(); - } - else if (APDS9960type) { - switch (function) { - case FUNC_EVERY_50_MSECOND: - APDS9960_loop(); - break; - case FUNC_COMMAND_SENSOR: - if (XSNS_27 == XdrvMailbox.index) { - result = APDS9960CommandSensor(); - } - break; - case FUNC_JSON_APPEND: - APDS9960_show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - APDS9960_show(0); - break; -#endif - } - } - return result; -} -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_28_tm1638.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_28_tm1638.ino" -#ifdef USE_TM1638 - - - - - - -#define XSNS_28 28 - -#define TM1638_COLOR_NONE 0 -#define TM1638_COLOR_RED 1 -#define TM1638_COLOR_GREEN 2 - -#define TM1638_CLOCK_DELAY 1 - -uint8_t tm1638_type = 1; -uint8_t tm1638_clock_pin = 0; -uint8_t tm1638_data_pin = 0; -uint8_t tm1638_strobe_pin = 0; -uint8_t tm1638_displays = 8; -uint8_t tm1638_active_display = 1; -uint8_t tm1638_intensity = 0; -uint8_t tm1638_state = 0; - - - - - - -void Tm16XXSend(uint8_t data) -{ - for (uint32_t i = 0; i < 8; i++) { - digitalWrite(tm1638_data_pin, !!(data & (1 << i))); - digitalWrite(tm1638_clock_pin, LOW); - delayMicroseconds(TM1638_CLOCK_DELAY); - digitalWrite(tm1638_clock_pin, HIGH); - } -} - -void Tm16XXSendCommand(uint8_t cmd) -{ - digitalWrite(tm1638_strobe_pin, LOW); - Tm16XXSend(cmd); - digitalWrite(tm1638_strobe_pin, HIGH); -} - -void TM16XXSendData(uint8_t address, uint8_t data) -{ - Tm16XXSendCommand(0x44); - digitalWrite(tm1638_strobe_pin, LOW); - Tm16XXSend(0xC0 | address); - Tm16XXSend(data); - digitalWrite(tm1638_strobe_pin, HIGH); -} - -uint8_t Tm16XXReceive(void) -{ - uint8_t temp = 0; - - - pinMode(tm1638_data_pin, INPUT); - digitalWrite(tm1638_data_pin, HIGH); - - for (uint32_t i = 0; i < 8; ++i) { - digitalWrite(tm1638_clock_pin, LOW); - delayMicroseconds(TM1638_CLOCK_DELAY); - temp |= digitalRead(tm1638_data_pin) << i; - digitalWrite(tm1638_clock_pin, HIGH); - } - - - pinMode(tm1638_data_pin, OUTPUT); - digitalWrite(tm1638_data_pin, LOW); - - return temp; -} - - - -void Tm16XXClearDisplay(void) -{ - for (uint32_t i = 0; i < tm1638_displays; i++) { - TM16XXSendData(i << 1, 0); - } -} - -void Tm1638SetLED(uint8_t color, uint8_t pos) -{ - TM16XXSendData((pos << 1) + 1, color); -} - -void Tm1638SetLEDs(word leds) -{ - for (uint32_t i = 0; i < tm1638_displays; i++) { - uint8_t color = 0; - - if ((leds & (1 << i)) != 0) { - color |= TM1638_COLOR_RED; - } - - if ((leds & (1 << (i + 8))) != 0) { - color |= TM1638_COLOR_GREEN; - } - - Tm1638SetLED(color, i); - } -} - -uint8_t Tm1638GetButtons(void) -{ - uint8_t keys = 0; - - digitalWrite(tm1638_strobe_pin, LOW); - Tm16XXSend(0x42); - for (uint32_t i = 0; i < 4; i++) { - keys |= Tm16XXReceive() << i; - } - digitalWrite(tm1638_strobe_pin, HIGH); - - return keys; -} - - - -void TmInit(void) -{ - tm1638_type = 0; - if ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99)) { - tm1638_clock_pin = pin[GPIO_TM16CLK]; - tm1638_data_pin = pin[GPIO_TM16DIO]; - tm1638_strobe_pin = pin[GPIO_TM16STB]; - - pinMode(tm1638_data_pin, OUTPUT); - pinMode(tm1638_clock_pin, OUTPUT); - pinMode(tm1638_strobe_pin, OUTPUT); - - digitalWrite(tm1638_strobe_pin, HIGH); - digitalWrite(tm1638_clock_pin, HIGH); - - Tm16XXSendCommand(0x40); - Tm16XXSendCommand(0x80 | (tm1638_active_display ? 8 : 0) | tmin(7, tm1638_intensity)); - - digitalWrite(tm1638_strobe_pin, LOW); - Tm16XXSend(0xC0); - for (uint32_t i = 0; i < 16; i++) { - Tm16XXSend(0x00); - } - digitalWrite(tm1638_strobe_pin, HIGH); - - tm1638_type = 1; - tm1638_state = 1; - } -} - -void TmLoop(void) -{ - if (tm1638_state) { - uint8_t buttons = Tm1638GetButtons(); - for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - SwitchSetVirtual(i, (buttons &1) ^1); - uint8_t color = (SwitchGetVirtual(i)) ? TM1638_COLOR_NONE : TM1638_COLOR_RED; - Tm1638SetLED(color, i); - buttons >>= 1; - } - SwitchHandler(1); - } -} -# 201 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_28_tm1638.ino" -bool Xsns28(uint8_t function) -{ - bool result = false; - - if (tm1638_type) { - switch (function) { - case FUNC_INIT: - TmInit(); - break; - case FUNC_EVERY_50_MSECOND: - TmLoop(); - break; -# 223 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_28_tm1638.ino" - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_29_mcp230xx.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_29_mcp230xx.ino" -#ifdef USE_I2C -#ifdef USE_MCP230xx -# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_29_mcp230xx.ino" -#define XSNS_29 29 -#define XI2C_22 22 - - - - - -uint8_t MCP230xx_IODIR = 0x00; -uint8_t MCP230xx_GPINTEN = 0x02; -uint8_t MCP230xx_IOCON = 0x05; -uint8_t MCP230xx_GPPU = 0x06; -uint8_t MCP230xx_INTF = 0x07; -uint8_t MCP230xx_INTCAP = 0x08; -uint8_t MCP230xx_GPIO = 0x09; - -uint8_t mcp230xx_type = 0; -uint8_t mcp230xx_pincount = 0; -uint8_t mcp230xx_int_en = 0; -uint8_t mcp230xx_int_prio_counter = 0; -uint8_t mcp230xx_int_counter_en = 0; -uint8_t mcp230xx_int_retainer_en = 0; -uint8_t mcp230xx_int_sec_counter = 0; - -uint8_t mcp230xx_int_report_defer_counter[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - -uint16_t mcp230xx_int_counter[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - -uint8_t mcp230xx_int_retainer[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - -unsigned long int_millis[16]; - -const char MCP230XX_SENSOR_RESPONSE[] PROGMEM = "{\"Sensor29_D%i\":{\"MODE\":%i,\"PULL_UP\":\"%s\",\"INT_MODE\":\"%s\",\"STATE\":\"%s\"}}"; - -const char MCP230XX_INTCFG_RESPONSE[] PROGMEM = "{\"MCP230xx_INT%s\":{\"D_%i\":%i}}"; - -#ifdef USE_MCP230xx_OUTPUT -const char MCP230XX_CMND_RESPONSE[] PROGMEM = "{\"S29cmnd_D%i\":{\"COMMAND\":\"%s\",\"STATE\":\"%s\"}}"; -#endif - -void MCP230xx_CheckForIntCounter(void) { - uint8_t en = 0; - for (uint32_t ca=0;ca<16;ca++) { - if (Settings.mcp230xx_config[ca].int_count_en) { - en=1; - } - } - if (!Settings.mcp230xx_int_timer) en=0; - mcp230xx_int_counter_en=en; - if (!mcp230xx_int_counter_en) { - for (uint32_t ca=0;ca<16;ca++) { - mcp230xx_int_counter[ca] = 0; - } - } -} - -void MCP230xx_CheckForIntRetainer(void) { - uint8_t en = 0; - for (uint32_t ca=0;ca<16;ca++) { - if (Settings.mcp230xx_config[ca].int_retain_flag) { - en=1; - } - } - mcp230xx_int_retainer_en=en; - if (!mcp230xx_int_retainer_en) { - for (uint32_t ca=0;ca<16;ca++) { - mcp230xx_int_retainer[ca] = 0; - } - } -} - -const char* ConvertNumTxt(uint8_t statu, uint8_t pinmod=0) { -#ifdef USE_MCP230xx_OUTPUT -if ((6 == pinmod) && (statu < 2)) { statu = abs(statu-1); } -#endif - switch (statu) { - case 0: - return "OFF"; - break; - case 1: - return "ON"; - break; -#ifdef USE_MCP230xx_OUTPUT - case 2: - return "TOGGLE"; - break; -#endif - } - return ""; -} - -const char* IntModeTxt(uint8_t intmo) { - switch (intmo) { - case 0: - return "ALL"; - break; - case 1: - return "EVENT"; - break; - case 2: - return "TELE"; - break; - case 3: - return "DISABLED"; - break; - } - return ""; -} - -uint8_t MCP230xx_readGPIO(uint8_t port) { - return I2cRead8(USE_MCP230xx_ADDR, MCP230xx_GPIO + port); -} - -void MCP230xx_ApplySettings(void) -{ - uint8_t int_en = 0; - for (uint32_t mcp230xx_port = 0; mcp230xx_port < mcp230xx_type; mcp230xx_port++) { - uint8_t reg_gppu = 0; - uint8_t reg_gpinten = 0; - uint8_t reg_iodir = 0xFF; -#ifdef USE_MCP230xx_OUTPUT - uint8_t reg_portpins = 0x00; -#endif - for (uint32_t idx = 0; idx < 8; idx++) { - switch (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pinmode) { - case 0 ... 1: - reg_iodir |= (1 << idx); - break; - case 2 ... 4: - reg_iodir |= (1 << idx); - reg_gpinten |= (1 << idx); - int_en = 1; - break; -#ifdef USE_MCP230xx_OUTPUT - case 5 ... 6: - reg_iodir &= ~(1 << idx); - if (Settings.flag.save_state) { - reg_portpins |= (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].saved_state << idx); - } else { - if (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pullup) { - reg_portpins |= (1 << idx); - } - } - break; -#endif - default: - break; - } -#ifdef USE_MCP230xx_OUTPUT - if ((Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pullup) && (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pinmode < 5)) { - reg_gppu |= (1 << idx); - } -#else - if (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pullup) { - reg_gppu |= (1 << idx); - } -#endif - } - I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPPU+mcp230xx_port, reg_gppu); - I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPINTEN+mcp230xx_port, reg_gpinten); - I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_IODIR+mcp230xx_port, reg_iodir); -#ifdef USE_MCP230xx_OUTPUT - I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO+mcp230xx_port, reg_portpins); -#endif - } - for (uint32_t idx=0;idx 0) { - if (I2cValidRead8(&mcp230xx_intcap, USE_MCP230xx_ADDR, MCP230xx_INTCAP+mcp230xx_port)) { - for (uint32_t intp = 0; intp < 8; intp++) { - if ((intf >> intp) & 0x01) { - report_int = 0; - if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].pinmode > 1) { - switch (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].pinmode) { - case 2: - report_int = 1; - break; - case 3: - if (((mcp230xx_intcap >> intp) & 0x01) == 0) report_int = 1; - break; - case 4: - if (((mcp230xx_intcap >> intp) & 0x01) == 1) report_int = 1; - break; - default: - break; - } - - if ((mcp230xx_int_counter_en) && (report_int)) { - if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_count_en) { - mcp230xx_int_counter[intp+(mcp230xx_port*8)]++; - } - } - - if (report_int) { - if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_report_defer) { - mcp230xx_int_report_defer_counter[intp+(mcp230xx_port*8)]++; - if (mcp230xx_int_report_defer_counter[intp+(mcp230xx_port*8)] >= Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_report_defer) { - mcp230xx_int_report_defer_counter[intp+(mcp230xx_port*8)]=0; - } else { - report_int = 0; - } - } - } - - if (report_int) { - if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_retain_flag) { - mcp230xx_int_retainer[intp+(mcp230xx_port*8)] = 1; - report_int = 0; - } - } - if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_count_en) { - report_int = 0; - } - if (report_int) { - bool int_tele = false; - bool int_event = false; - unsigned long millis_now = millis(); - unsigned long millis_since_last_int = millis_now - int_millis[intp+(mcp230xx_port*8)]; - int_millis[intp+(mcp230xx_port*8)]=millis_now; - switch (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_report_mode) { - case 0: - int_tele=true; - int_event=true; - break; - case 1: - int_event=true; - break; - case 2: - int_tele=true; - break; - } - if (int_tele) { - 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")); - } - if (int_event) { - char command[19]; - sprintf(command,"event MCPINT_D%i=%i",intp+(mcp230xx_port*8),((mcp230xx_intcap >> intp) & 0x01)); - ExecuteCommand(command, SRC_RULE); - } - } - } - } - } - } - } - } - } -} - -void MCP230xx_Show(bool json) -{ - if (json) { - uint8_t gpio = MCP230xx_readGPIO(0); - ResponseAppend_P(PSTR(",\"MCP230XX\":{\"D0\":%i,\"D1\":%i,\"D2\":%i,\"D3\":%i,\"D4\":%i,\"D5\":%i,\"D6\":%i,\"D7\":%i"), - (gpio>>0)&1,(gpio>>1)&1,(gpio>>2)&1,(gpio>>3)&1,(gpio>>4)&1,(gpio>>5)&1,(gpio>>6)&1,(gpio>>7)&1); - if (2 == mcp230xx_type) { - gpio = MCP230xx_readGPIO(1); - ResponseAppend_P(PSTR(",\"D8\":%i,\"D9\":%i,\"D10\":%i,\"D11\":%i,\"D12\":%i,\"D13\":%i,\"D14\":%i,\"D15\":%i"), - (gpio>>0)&1,(gpio>>1)&1,(gpio>>2)&1,(gpio>>3)&1,(gpio>>4)&1,(gpio>>5)&1,(gpio>>6)&1,(gpio>>7)&1); - } - ResponseJsonEnd(); - } -} - -#ifdef USE_MCP230xx_OUTPUT - -void MCP230xx_SetOutPin(uint8_t pin,uint8_t pinstate) { - uint8_t portpins; - uint8_t port = 0; - uint8_t pinmo = Settings.mcp230xx_config[pin].pinmode; - uint8_t interlock = Settings.flag.interlock; - int pinadd = (pin % 2)+1-(3*(pin % 2)); - char cmnd[7], stt[4]; - if (pin > 7) { port = 1; } - portpins = MCP230xx_readGPIO(port); - if (interlock && (pinmo == Settings.mcp230xx_config[pin+pinadd].pinmode)) { - if (pinstate < 2) { - if (6 == pinmo) { - if (pinstate) portpins |= (1 << (pin-(port*8))); else portpins |= (1 << (pin+pinadd-(port*8))),portpins &= ~(1 << (pin-(port*8))); - } else { - if (pinstate) portpins &= ~(1 << (pin+pinadd-(port*8))),portpins |= (1 << (pin-(port*8))); else portpins &= ~(1 << (pin-(port*8))); - } - } else { - if (6 == pinmo) { - portpins |= (1 << (pin+pinadd-(port*8))),portpins ^= (1 << (pin-(port*8))); - } else { - portpins &= ~(1 << (pin+pinadd-(port*8))),portpins ^= (1 << (pin-(port*8))); - } - } - } else { - if (pinstate < 2) { - if (pinstate) portpins |= (1 << (pin-(port*8))); else portpins &= ~(1 << (pin-(port*8))); - } else { - portpins ^= (1 << (pin-(port*8))); - } - } - I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO + port, portpins); - if (Settings.flag.save_state) { - Settings.mcp230xx_config[pin].saved_state=portpins>>(pin-(port*8))&1; - Settings.mcp230xx_config[pin+pinadd].saved_state=portpins>>(pin+pinadd-(port*8))&1; - } - sprintf(cmnd,ConvertNumTxt(pinstate, pinmo)); - sprintf(stt,ConvertNumTxt((portpins >> (pin-(port*8))&1), pinmo)); - if (interlock && (pinmo == Settings.mcp230xx_config[pin+pinadd].pinmode)) { - char stt1[4]; - sprintf(stt1,ConvertNumTxt((portpins >> (pin+pinadd-(port*8))&1), pinmo)); - Response_P(PSTR("{\"S29cmnd_D%i\":{\"COMMAND\":\"%s\",\"STATE\":\"%s\"},\"S29cmnd_D%i\":{\"STATE\":\"%s\"}}"),pin, cmnd, stt, pin+pinadd, stt1); - } else { - Response_P(MCP230XX_CMND_RESPONSE, pin, cmnd, stt); - } -} - -#endif - -void MCP230xx_Reset(uint8_t pinmode) { - uint8_t pullup = 0; - if ((pinmode > 1) && (pinmode < 5)) { pullup=1; } - for (uint32_t pinx=0;pinx<16;pinx++) { - Settings.mcp230xx_config[pinx].pinmode=pinmode; - Settings.mcp230xx_config[pinx].pullup=pullup; - Settings.mcp230xx_config[pinx].saved_state=0; - if ((pinmode > 1) && (pinmode < 5)) { - Settings.mcp230xx_config[pinx].int_report_mode=0; - } else { - Settings.mcp230xx_config[pinx].int_report_mode=3; - } - Settings.mcp230xx_config[pinx].int_report_defer=0; - Settings.mcp230xx_config[pinx].int_count_en=0; - Settings.mcp230xx_config[pinx].int_retain_flag=0; - Settings.mcp230xx_config[pinx].spare13=0; - Settings.mcp230xx_config[pinx].spare14=0; - Settings.mcp230xx_config[pinx].spare15=0; - } - Settings.mcp230xx_int_prio = 0; - Settings.mcp230xx_int_timer = 0; - MCP230xx_ApplySettings(); - char pulluptxt[7]; - char intmodetxt[9]; - sprintf(pulluptxt,ConvertNumTxt(pullup)); - uint8_t intmode = 3; - if ((pinmode > 1) && (pinmode < 5)) { intmode = 0; } - sprintf(intmodetxt,IntModeTxt(intmode)); - Response_P(MCP230XX_SENSOR_RESPONSE,99,pinmode,pulluptxt,intmodetxt,""); -} - -bool MCP230xx_Command(void) -{ - bool serviced = true; - bool validpin = false; - uint8_t paramcount = 0; - if (XdrvMailbox.data_len > 0) { - paramcount=1; - } else { - serviced = false; - return serviced; - } - char sub_string[XdrvMailbox.data_len]; - for (uint32_t ca=0;ca 1) { - uint8_t intpri = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - if ((intpri >= 0) && (intpri <= 20)) { - Settings.mcp230xx_int_prio = intpri; - Response_P(MCP230XX_INTCFG_RESPONSE,"PRI",99,Settings.mcp230xx_int_prio); - return serviced; - } - } else { - Response_P(MCP230XX_INTCFG_RESPONSE,"PRI",99,Settings.mcp230xx_int_prio); - return serviced; - } - } - - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTTIMER")) { - if (paramcount > 1) { - uint8_t inttim = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - if ((inttim >= 0) && (inttim <= 3600)) { - Settings.mcp230xx_int_timer = inttim; - MCP230xx_CheckForIntCounter(); - Response_P(MCP230XX_INTCFG_RESPONSE,"TIMER",99,Settings.mcp230xx_int_timer); - return serviced; - } - } else { - Response_P(MCP230XX_INTCFG_RESPONSE,"TIMER",99,Settings.mcp230xx_int_timer); - return serviced; - } - } - - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTDEF")) { - if (paramcount > 1) { - uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - if (pin < mcp230xx_pincount) { - if (pin == 0) { - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0")) validpin=true; - } else { - validpin = true; - } - } - if (validpin) { - if (paramcount > 2) { - uint8_t intdef = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); - if ((intdef >= 0) && (intdef <= 15)) { - Settings.mcp230xx_config[pin].int_report_defer=intdef; - if (Settings.mcp230xx_config[pin].int_count_en) { - Settings.mcp230xx_config[pin].int_count_en=0; - MCP230xx_CheckForIntCounter(); - AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled INTCNT for pin D%i"),pin); - } - Response_P(MCP230XX_INTCFG_RESPONSE,"DEF",pin,Settings.mcp230xx_config[pin].int_report_defer); - return serviced; - } else { - serviced=false; - return serviced; - } - } else { - Response_P(MCP230XX_INTCFG_RESPONSE,"DEF",pin,Settings.mcp230xx_config[pin].int_report_defer); - return serviced; - } - } - serviced = false; - return serviced; - } else { - serviced = false; - return serviced; - } - } - - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTCNT")) { - if (paramcount > 1) { - uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - if (pin < mcp230xx_pincount) { - if (pin == 0) { - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0")) validpin=true; - } else { - validpin = true; - } - } - if (validpin) { - if (paramcount > 2) { - uint8_t intcnt = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); - if ((intcnt >= 0) && (intcnt <= 1)) { - Settings.mcp230xx_config[pin].int_count_en=intcnt; - if (Settings.mcp230xx_config[pin].int_report_defer) { - Settings.mcp230xx_config[pin].int_report_defer=0; - AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled INTDEF for pin D%i"),pin); - } - if (Settings.mcp230xx_config[pin].int_report_mode < 3) { - Settings.mcp230xx_config[pin].int_report_mode=3; - AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled immediate interrupt/telemetry reporting for pin D%i"),pin); - } - if ((Settings.mcp230xx_config[pin].int_count_en) && (!Settings.mcp230xx_int_timer)) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - INTCNT enabled for pin D%i but global INTTIMER is disabled!"),pin); - } - MCP230xx_CheckForIntCounter(); - Response_P(MCP230XX_INTCFG_RESPONSE,"CNT",pin,Settings.mcp230xx_config[pin].int_count_en); - return serviced; - } else { - serviced=false; - return serviced; - } - } else { - Response_P(MCP230XX_INTCFG_RESPONSE,"CNT",pin,Settings.mcp230xx_config[pin].int_count_en); - return serviced; - } - } - serviced = false; - return serviced; - } else { - serviced = false; - return serviced; - } - } - - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTRETAIN")) { - if (paramcount > 1) { - uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - if (pin < mcp230xx_pincount) { - if (pin == 0) { - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0")) validpin=true; - } else { - validpin = true; - } - } - if (validpin) { - if (paramcount > 2) { - uint8_t int_retain = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); - if ((int_retain >= 0) && (int_retain <= 1)) { - Settings.mcp230xx_config[pin].int_retain_flag=int_retain; - Response_P(MCP230XX_INTCFG_RESPONSE,"INT_RETAIN",pin,Settings.mcp230xx_config[pin].int_retain_flag); - MCP230xx_CheckForIntRetainer(); - return serviced; - } else { - serviced=false; - return serviced; - } - } else { - Response_P(MCP230XX_INTCFG_RESPONSE,"INT_RETAIN",pin,Settings.mcp230xx_config[pin].int_retain_flag); - return serviced; - } - } - serviced = false; - return serviced; - } else { - serviced = false; - return serviced; - } - } - - uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1)); - - if (pin < mcp230xx_pincount) { - if (0 == pin) { - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1), "0")) validpin=true; - } else { - validpin=true; - } - } - if (validpin && (paramcount > 1)) { - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "?")) { - uint8_t port = 0; - if (pin > 7) { port = 1; } - uint8_t portdata = MCP230xx_readGPIO(port); - char pulluptxtr[7],pinstatustxtr[7]; - char intmodetxt[9]; - sprintf(intmodetxt,IntModeTxt(Settings.mcp230xx_config[pin].int_report_mode)); - sprintf(pulluptxtr,ConvertNumTxt(Settings.mcp230xx_config[pin].pullup)); -#ifdef USE_MCP230xx_OUTPUT - uint8_t pinmod = Settings.mcp230xx_config[pin].pinmode; - sprintf(pinstatustxtr,ConvertNumTxt(portdata>>(pin-(port*8))&1,pinmod)); - Response_P(MCP230XX_SENSOR_RESPONSE,pin,pinmod,pulluptxtr,intmodetxt,pinstatustxtr); -#else - sprintf(pinstatustxtr,ConvertNumTxt(portdata>>(pin-(port*8))&1)); - Response_P(MCP230XX_SENSOR_RESPONSE,pin,Settings.mcp230xx_config[pin].pinmode,pulluptxtr,intmodetxt,pinstatustxtr); -#endif - return serviced; - } -#ifdef USE_MCP230xx_OUTPUT - if (Settings.mcp230xx_config[pin].pinmode >= 5) { - uint8_t pincmd = Settings.mcp230xx_config[pin].pinmode - 5; - if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "ON")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "1"))) { - MCP230xx_SetOutPin(pin,abs(pincmd-1)); - return serviced; - } - if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "OFF")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0"))) { - MCP230xx_SetOutPin(pin,pincmd); - return serviced; - } - if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "T")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "2"))) { - MCP230xx_SetOutPin(pin,2); - return serviced; - } - } -#endif - uint8_t pinmode = 0; - uint8_t pullup = 0; - uint8_t intmode = 0; - if (paramcount > 1) { - pinmode = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - } - if (paramcount > 2) { - pullup = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); - } - if (paramcount > 3) { - intmode = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4)); - } -#ifdef USE_MCP230xx_OUTPUT - if ((pin < mcp230xx_pincount) && (pinmode > 0) && (pinmode < 7) && (pullup < 2) && (paramcount > 2)) { -#else - if ((pin < mcp230xx_pincount) && (pinmode > 0) && (pinmode < 5) && (pullup < 2) && (paramcount > 2)) { -#endif - Settings.mcp230xx_config[pin].pinmode=pinmode; - Settings.mcp230xx_config[pin].pullup=pullup; - if ((pinmode > 1) && (pinmode < 5)) { - if ((intmode >= 0) && (intmode <= 3)) { - Settings.mcp230xx_config[pin].int_report_mode=intmode; - } - } else { - Settings.mcp230xx_config[pin].int_report_mode=3; - } - MCP230xx_ApplySettings(); - uint8_t port = 0; - if (pin > 7) { port = 1; } - uint8_t portdata = MCP230xx_readGPIO(port); - char pulluptxtc[7], pinstatustxtc[7]; - char intmodetxt[9]; - sprintf(pulluptxtc,ConvertNumTxt(pullup)); - sprintf(intmodetxt,IntModeTxt(Settings.mcp230xx_config[pin].int_report_mode)); -#ifdef USE_MCP230xx_OUTPUT - sprintf(pinstatustxtc,ConvertNumTxt(portdata>>(pin-(port*8))&1,Settings.mcp230xx_config[pin].pinmode)); -#else - sprintf(pinstatustxtc,ConvertNumTxt(portdata>>(pin-(port*8))&1)); -#endif - Response_P(MCP230XX_SENSOR_RESPONSE,pin,pinmode,pulluptxtc,intmodetxt,pinstatustxtc); - return serviced; - } - } else { - serviced=false; - return serviced; - } - return serviced; -} - -#ifdef USE_MCP230xx_DISPLAYOUTPUT - -const char HTTP_SNS_MCP230xx_OUTPUT[] PROGMEM = "{s}MCP230XX D%d{m}%s{e}"; - -void MCP230xx_UpdateWebData(void) -{ - uint8_t gpio1 = MCP230xx_readGPIO(0); - uint8_t gpio2 = 0; - if (2 == mcp230xx_type) { - gpio2 = MCP230xx_readGPIO(1); - } - uint16_t gpio = (gpio2 << 8) + gpio1; - for (uint32_t pin = 0; pin < mcp230xx_pincount; pin++) { - if (Settings.mcp230xx_config[pin].pinmode >= 5) { - char stt[7]; - sprintf(stt,ConvertNumTxt((gpio>>pin)&1,Settings.mcp230xx_config[pin].pinmode)); - WSContentSend_PD(HTTP_SNS_MCP230xx_OUTPUT, pin, stt); - } - } -} - -#endif - -#ifdef USE_MCP230xx_OUTPUT - -void MCP230xx_OutputTelemetry(void) -{ - uint8_t outputcount = 0; - uint16_t gpiototal = 0; - uint8_t gpioa = 0; - uint8_t gpiob = 0; - gpioa=MCP230xx_readGPIO(0); - if (2 == mcp230xx_type) { gpiob=MCP230xx_readGPIO(1); } - gpiototal=((uint16_t)gpiob << 8) | gpioa; - for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { - if (Settings.mcp230xx_config[pinx].pinmode >= 5) outputcount++; - } - if (outputcount) { - char stt[7]; - 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)); - ResponseAppend_P(PSTR("\"OUT_D%i\":\"%s\","),pinx,stt); - } - } - ResponseAppend_P(PSTR("\"END\":1}}")); - MqttPublishTeleSensor(); - } -} - -#endif - -void MCP230xx_Interrupt_Counter_Report(void) { - ResponseTime_P(PSTR(",\"MCP230_INTTIMER\":{")); - for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { - if (Settings.mcp230xx_config[pinx].int_count_en) { - ResponseAppend_P(PSTR("\"INTCNT_D%i\":%i,"),pinx,mcp230xx_int_counter[pinx]); - mcp230xx_int_counter[pinx]=0; - } - } - ResponseAppend_P(PSTR("\"END\":1}}")); - MqttPublishTeleSensor(); - mcp230xx_int_sec_counter = 0; -} - -void MCP230xx_Interrupt_Retain_Report(void) { - uint16_t retainresult = 0; - 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]); - retainresult |= (((mcp230xx_int_retainer[pinx])&1) << pinx); - mcp230xx_int_retainer[pinx]=0; - } - } - ResponseAppend_P(PSTR("\"Value\":%u}}"),retainresult); - MqttPublishTeleSensor(); -} - - - - - -bool Xsns29(uint8_t function) -{ - if (!I2cEnabled(XI2C_22)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - MCP230xx_Detect(); - } - else if (mcp230xx_type) { - switch (function) { - case FUNC_EVERY_50_MSECOND: - if (mcp230xx_int_en) { - mcp230xx_int_prio_counter++; - if ((mcp230xx_int_prio_counter) >= (Settings.mcp230xx_int_prio)) { - MCP230xx_CheckForInterrupt(); - mcp230xx_int_prio_counter=0; - } - } - break; - case FUNC_EVERY_SECOND: - if (mcp230xx_int_counter_en) { - mcp230xx_int_sec_counter++; - if (mcp230xx_int_sec_counter >= Settings.mcp230xx_int_timer) { - MCP230xx_Interrupt_Counter_Report(); - } - } - if (tele_period == 0) { - if (mcp230xx_int_retainer_en) { - MCP230xx_Interrupt_Retain_Report(); - } -#ifdef USE_MCP230xx_OUTPUT - MCP230xx_OutputTelemetry(); -#endif - } - break; - case FUNC_JSON_APPEND: - MCP230xx_Show(1); - break; - case FUNC_COMMAND_SENSOR: - if (XSNS_29 == XdrvMailbox.index) { - result = MCP230xx_Command(); - } - break; -#ifdef USE_WEBSERVER -#ifdef USE_MCP230xx_OUTPUT -#ifdef USE_MCP230xx_DISPLAYOUTPUT - case FUNC_WEB_SENSOR: - MCP230xx_UpdateWebData(); - break; -#endif -#endif -#endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_30_mpr121.ino" -# 46 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_30_mpr121.ino" -#ifdef USE_I2C -#ifdef USE_MPR121 - - - - - -#define XSNS_30 30 -#define XI2C_23 23 - - - - - - - -#define MPR121_ELEX_REG 0x00 - - -#define MPR121_MHDR_REG 0x2B - - -#define MPR121_MHDR_VAL 0x01 - - -#define MPR121_NHDR_REG 0x2C - - -#define MPR121_NHDR_VAL 0x01 - - -#define MPR121_NCLR_REG 0x2D - - -#define MPR121_NCLR_VAL 0x0E - - -#define MPR121_MHDF_REG 0x2F - - -#define MPR121_MHDF_VAL 0x01 - - -#define MPR121_NHDF_REG 0x30 - - -#define MPR121_NHDF_VAL 0x05 - - -#define MPR121_NCLF_REG 0x31 - - -#define MPR121_NCLF_VAL 0x01 - - -#define MPR121_MHDPROXR_REG 0x36 - - -#define MPR121_MHDPROXR_VAL 0x3F - - -#define MPR121_NHDPROXR_REG 0x37 - - -#define MPR121_NHDPROXR_VAL 0x5F - - -#define MPR121_NCLPROXR_REG 0x38 - - -#define MPR121_NCLPROXR_VAL 0x04 - - -#define MPR121_FDLPROXR_REG 0x39 - - -#define MPR121_FDLPROXR_VAL 0x00 - - -#define MPR121_MHDPROXF_REG 0x3A - - -#define MPR121_MHDPROXF_VAL 0x01 - - -#define MPR121_NHDPROXF_REG 0x3B - - -#define MPR121_NHDPROXF_VAL 0x01 - - -#define MPR121_NCLPROXF_REG 0x3C - - -#define MPR121_NCLPROXF_VAL 0x1F - - -#define MPR121_FDLPROXF_REG 0x3D - - -#define MPR121_FDLPROXF_VAL 0x04 - - -#define MPR121_E0TTH_REG 0x41 - - -#define MPR121_E0TTH_VAL 12 - - -#define MPR121_E0RTH_REG 0x42 - - -#define MPR121_E0RTH_VAL 6 - - -#define MPR121_CDT_REG 0x5D - - -#define MPR121_CDT_VAL 0x20 - - -#define MPR121_ECR_REG 0x5E - - -#define MPR121_ECR_VAL 0x8F - - - -#define MPR121_SRST_REG 0x80 - - -#define MPR121_SRST_VAL 0x63 - - -#define BITC(sensor,position) ((pS->current[sensor] >> position) & 1) - - -#define BITP(sensor,position) ((pS->previous[sensor] >> position) & 1) -# 195 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_30_mpr121.ino" -typedef struct mpr121 mpr121; -struct mpr121 { - const uint8_t i2c_addr[4] = { 0x5A, 0x5B, 0x5C, 0x5D }; - const char id[4] = { 'A', 'B', 'C', 'D' }; - bool connected[4] = { false, false, false, false }; - bool running[4] = { false, false, false, false }; - uint16_t current[4] = { 0x0000, 0x0000, 0x0000, 0x0000 }; - uint16_t previous[4] = { 0x0000, 0x0000, 0x0000, 0x0000 }; -}; - -bool mpr21_found = false; -# 217 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_30_mpr121.ino" -void Mpr121Init(struct mpr121 *pS, bool initial) -{ - - for (uint32_t i = 0; i < sizeof(pS->i2c_addr[i]); i++) { - - if (initial && I2cActive(pS->i2c_addr[i])) { continue; } - - - pS->connected[i] = (I2cWrite8(pS->i2c_addr[i], MPR121_SRST_REG, MPR121_SRST_VAL) - && (0x24 == I2cRead8(pS->i2c_addr[i], 0x5D))); - if (pS->connected[i]) { - - - mpr21_found = true; - char device_name[16]; - snprintf_P(device_name, sizeof(device_name), PSTR("MPR121(%c)"), pS->id[i]); - I2cSetActiveFound(pS->i2c_addr[i], device_name); - - - for (uint32_t j = 0; j < 13; j++) { - - - I2cWrite8(pS->i2c_addr[i], MPR121_E0TTH_REG + 2 * j, MPR121_E0TTH_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_E0RTH_REG + 2 * j, MPR121_E0RTH_VAL); - } - - - I2cWrite8(pS->i2c_addr[i], MPR121_MHDR_REG, MPR121_MHDR_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_NHDR_REG, MPR121_NHDR_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_NCLR_REG, MPR121_NCLR_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_MHDF_REG, MPR121_MHDF_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_NHDF_REG, MPR121_NHDF_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_NCLF_REG, MPR121_NCLF_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_MHDPROXR_REG, MPR121_MHDPROXR_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_NHDPROXR_REG, MPR121_NHDPROXR_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_NCLPROXR_REG, MPR121_NCLPROXR_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_FDLPROXR_REG, MPR121_FDLPROXR_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_MHDPROXF_REG, MPR121_MHDPROXF_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_NHDPROXF_REG, MPR121_NHDPROXF_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_NCLPROXF_REG, MPR121_NCLPROXF_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_FDLPROXF_REG, MPR121_FDLPROXF_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_CDT_REG, MPR121_CDT_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_ECR_REG, MPR121_ECR_VAL); - - - pS->running[i] = (0x00 != I2cRead8(pS->i2c_addr[i], MPR121_ECR_REG)); - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_I2C "MPR121%c: %sRunning"), pS->id[i], (pS->running[i]) ? "" : "NOT"); - - } else { - - - pS->running[i] = false; - } - } - - - if (!(pS->connected[0] || pS->connected[1] || pS->connected[2] - || pS->connected[3])) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C "MPR121: No sensors found")); - } -} -# 326 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_30_mpr121.ino" -void Mpr121Show(struct mpr121 *pS, uint8_t function) -{ - - - for (uint32_t i = 0; i < sizeof(pS->i2c_addr[i]); i++) { - - - if (pS->connected[i]) { - - - if (!I2cValidRead16LE(&pS->current[i], pS->i2c_addr[i], MPR121_ELEX_REG)) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_I2C "MPR121%c: ERROR: Cannot read data!"), pS->id[i]); - Mpr121Init(pS, false); - return; - } - - if (BITC(i, 15)) { - - - I2cWrite8(pS->i2c_addr[i], MPR121_ELEX_REG, 0x00); - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_I2C "MPR121%c: ERROR: Excess current detected! Fix circuits if it happens repeatedly! Soft-resetting MPR121 ..."), pS->id[i]); - Mpr121Init(pS, false); - return; - } - } - - if (pS->running[i]) { - - - if (FUNC_JSON_APPEND == function) { - ResponseAppend_P(PSTR(",\"MPR121%c\":{"), pS->id[i]); - } - - for (uint32_t j = 0; j < 13; j++) { - - - if ((FUNC_EVERY_50_MSECOND == function) - && (BITC(i, j) != BITP(i, j))) { - Response_P(PSTR("{\"MPR121%c\":{\"Button%i\":%i}}"), pS->id[i], j, BITC(i, j)); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, mqtt_data); - } - -#ifdef USE_WEBSERVER - if (FUNC_WEB_SENSOR == function) { - WSContentSend_PD(PSTR("{s}MPR121%c Button%d{m}%d{e}"), pS->id[i], j, BITC(i, j)); - } -#endif - - - if (FUNC_JSON_APPEND == function) { - ResponseAppend_P(PSTR("%s\"Button%i\":%i"), (j > 0 ? "," : ""), j, BITC(i, j)); - } - } - - - pS->previous[i] = pS->current[i]; - - - if (FUNC_JSON_APPEND == function) { - ResponseJsonEnd(); - } - } - } -} -# 410 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_30_mpr121.ino" -bool Xsns30(uint8_t function) -{ - if (!I2cEnabled(XI2C_23)) { return false; } - - bool result = false; - - - static struct mpr121 mpr121; - - if (FUNC_INIT == function) { - - Mpr121Init(&mpr121, true); - } - else if (mpr21_found) { - - switch (function) { - - - case FUNC_EVERY_50_MSECOND: - Mpr121Show(&mpr121, FUNC_EVERY_50_MSECOND); - break; - - - case FUNC_JSON_APPEND: - Mpr121Show(&mpr121, FUNC_JSON_APPEND); - break; - -#ifdef USE_WEBSERVER - - case FUNC_WEB_SENSOR: - Mpr121Show(&mpr121, FUNC_WEB_SENSOR); - break; -#endif - } - } - - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_31_ccs811.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_31_ccs811.ino" -#ifdef USE_I2C -#ifdef USE_CCS811 -# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_31_ccs811.ino" -#define XSNS_31 31 -#define XI2C_24 24 - -#define EVERYNSECONDS 5 - -#include "Adafruit_CCS811.h" - -Adafruit_CCS811 ccs; -uint8_t CCS811_ready = 0; -uint8_t CCS811_type = 0;; -uint16_t eCO2; -uint16_t TVOC; -uint8_t tcnt = 0; -uint8_t ecnt = 0; - - - -void CCS811Detect(void) -{ - if (I2cActive(CCS811_ADDRESS)) { return; } - - if (!ccs.begin(CCS811_ADDRESS)) { - CCS811_type = 1; - I2cSetActiveFound(CCS811_ADDRESS, "CCS811"); - } -} - -void CCS811Update(void) -{ - tcnt++; - if (tcnt >= EVERYNSECONDS) { - tcnt = 0; - CCS811_ready = 0; - if (ccs.available()) { - if (!ccs.readData()){ - TVOC = ccs.getTVOC(); - eCO2 = ccs.geteCO2(); - CCS811_ready = 1; - if (global_update && global_humidity>0 && global_temperature!=9999) { ccs.setEnvironmentalData((uint8_t)global_humidity, global_temperature); } - ecnt = 0; - } - } else { - - ecnt++; - if (ecnt > 6) { - - ccs.begin(CCS811_ADDRESS); - } - } - } -} - -const char HTTP_SNS_CCS811[] PROGMEM = - "{s}CCS811 " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}" - "{s}CCS811 " D_TVOC "{m}%d " D_UNIT_PARTS_PER_BILLION "{e}"; - -void CCS811Show(bool json) -{ - if (CCS811_ready) { - if (json) { - ResponseAppend_P(PSTR(",\"CCS811\":{\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TVOC "\":%d}"), eCO2,TVOC); -#ifdef USE_DOMOTICZ - if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, eCO2); -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_CCS811, eCO2, TVOC); -#endif - } - } -} - - - - - -bool Xsns31(uint8_t function) -{ - if (!I2cEnabled(XI2C_24)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - CCS811Detect(); - } - else if (CCS811_type) { - switch (function) { - case FUNC_EVERY_SECOND: - CCS811Update(); - break; - case FUNC_JSON_APPEND: - CCS811Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - CCS811Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_32_mpu6050.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_32_mpu6050.ino" -#ifdef USE_I2C -#ifdef USE_MPU6050 -# 30 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_32_mpu6050.ino" -#define XSNS_32 32 -#define XI2C_25 25 - -#define D_SENSOR_MPU6050 "MPU6050" - -#define MPU_6050_ADDR_AD0_LOW 0x68 -#define MPU_6050_ADDR_AD0_HIGH 0x69 - -uint8_t MPU_6050_address; -uint8_t MPU_6050_addresses[] = { MPU_6050_ADDR_AD0_LOW, MPU_6050_ADDR_AD0_HIGH }; -uint8_t MPU_6050_found; - -int16_t MPU_6050_ax = 0, MPU_6050_ay = 0, MPU_6050_az = 0; -int16_t MPU_6050_gx = 0, MPU_6050_gy = 0, MPU_6050_gz = 0; -int16_t MPU_6050_temperature = 0; - -#ifdef USE_MPU6050_DMP - #include "MPU6050_6Axis_MotionApps20.h" - #include "I2Cdev.h" - #include - typedef struct MPU6050_DMP{ - uint8_t devStatus; - uint16_t packetSize; - uint16_t fifoCount; - uint8_t fifoBuffer[64]; - Quaternion q; - VectorInt16 aa; - VectorInt16 aaReal; - VectorFloat gravity; - float euler[3]; - float yawPitchRoll[3]; - } MPU6050_DMP; - - MPU6050_DMP MPU6050_dmp; -#else - #include -#endif -MPU6050 mpu6050; - -void MPU_6050PerformReading(void) -{ -#ifdef USE_MPU6050_DMP - mpu6050.resetFIFO(); - MPU6050_dmp.fifoCount = mpu6050.getFIFOCount(); - while (MPU6050_dmp.fifoCount < MPU6050_dmp.packetSize) MPU6050_dmp.fifoCount = mpu6050.getFIFOCount(); - mpu6050.getFIFOBytes(MPU6050_dmp.fifoBuffer, MPU6050_dmp.packetSize); - MPU6050_dmp.fifoCount -= MPU6050_dmp.packetSize; - - mpu6050.dmpGetQuaternion(&MPU6050_dmp.q, MPU6050_dmp.fifoBuffer); - mpu6050.dmpGetEuler(MPU6050_dmp.euler, &MPU6050_dmp.q); - mpu6050.dmpGetAccel(&MPU6050_dmp.aa, MPU6050_dmp.fifoBuffer); - mpu6050.dmpGetGravity(&MPU6050_dmp.gravity, &MPU6050_dmp.q); - mpu6050.dmpGetLinearAccel(&MPU6050_dmp.aaReal, &MPU6050_dmp.aa, &MPU6050_dmp.gravity); - mpu6050.dmpGetYawPitchRoll(MPU6050_dmp.yawPitchRoll, &MPU6050_dmp.q, &MPU6050_dmp.gravity); - MPU_6050_gx = MPU6050_dmp.euler[0] * 180/M_PI; - MPU_6050_gy = MPU6050_dmp.euler[1] * 180/M_PI; - MPU_6050_gz = MPU6050_dmp.euler[2] * 180/M_PI; - MPU_6050_ax = MPU6050_dmp.aaReal.x; - MPU_6050_ay = MPU6050_dmp.aaReal.y; - MPU_6050_az = MPU6050_dmp.aaReal.z; -#else - mpu6050.getMotion6( - &MPU_6050_ax, - &MPU_6050_ay, - &MPU_6050_az, - &MPU_6050_gx, - &MPU_6050_gy, - &MPU_6050_gz - ); -#endif - MPU_6050_temperature = mpu6050.getTemperature(); -} -# 119 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_32_mpu6050.ino" -void MPU_6050Detect(void) -{ - for (uint32_t i = 0; i < sizeof(MPU_6050_addresses); i++) - { - MPU_6050_address = MPU_6050_addresses[i]; - if (!I2cSetDevice(MPU_6050_address)) { break; } - mpu6050.setAddr(MPU_6050_addresses[i]); - -#ifdef USE_MPU6050_DMP - MPU6050_dmp.devStatus = mpu6050.dmpInitialize(); - mpu6050.setXGyroOffset(220); - mpu6050.setYGyroOffset(76); - mpu6050.setZGyroOffset(-85); - mpu6050.setZAccelOffset(1788); - if (MPU6050_dmp.devStatus == 0) { - mpu6050.setDMPEnabled(true); - MPU6050_dmp.packetSize = mpu6050.dmpGetFIFOPacketSize(); - MPU_6050_found = true; - } -#else - mpu6050.initialize(); - MPU_6050_found = mpu6050.testConnection(); -#endif - Settings.flag2.axis_resolution = 2; - } - - if (MPU_6050_found) { - I2cSetActiveFound(MPU_6050_address, D_SENSOR_MPU6050); - } -} - -#define D_YAW "Yaw" -#define D_PITCH "Pitch" -#define D_ROLL "Roll" - -#ifdef USE_WEBSERVER -const char HTTP_SNS_AXIS[] PROGMEM = - "{s}" D_SENSOR_MPU6050 " " D_AX_AXIS "{m}%s{e}" - "{s}" D_SENSOR_MPU6050 " " D_AY_AXIS "{m}%s{e}" - "{s}" D_SENSOR_MPU6050 " " D_AZ_AXIS "{m}%s{e}" - "{s}" D_SENSOR_MPU6050 " " D_GX_AXIS "{m}%s{e}" - "{s}" D_SENSOR_MPU6050 " " D_GY_AXIS "{m}%s{e}" - "{s}" D_SENSOR_MPU6050 " " D_GZ_AXIS "{m}%s{e}"; -#ifdef USE_MPU6050_DMP -const char HTTP_SNS_YPR[] PROGMEM = - "{s}" D_SENSOR_MPU6050 " " D_YAW "{m}%s{e}" - "{s}" D_SENSOR_MPU6050 " " D_PITCH "{m}%s{e}" - "{s}" D_SENSOR_MPU6050 " " D_ROLL "{m}%s{e}"; -#endif -#endif - -#define D_JSON_AXIS_AX "AccelXAxis" -#define D_JSON_AXIS_AY "AccelYAxis" -#define D_JSON_AXIS_AZ "AccelZAxis" -#define D_JSON_AXIS_GX "GyroXAxis" -#define D_JSON_AXIS_GY "GyroYAxis" -#define D_JSON_AXIS_GZ "GyroZAxis" -#define D_JSON_YAW "Yaw" -#define D_JSON_PITCH "Pitch" -#define D_JSON_ROLL "Roll" - -void MPU_6050Show(bool json) -{ - MPU_6050PerformReading(); - - double tempConv = (MPU_6050_temperature / 340.0 + 35.53); - char temperature[33]; - dtostrfd(tempConv, Settings.flag2.temperature_resolution, temperature); - char axis_ax[33]; - dtostrfd(MPU_6050_ax, Settings.flag2.axis_resolution, axis_ax); - char axis_ay[33]; - dtostrfd(MPU_6050_ay, Settings.flag2.axis_resolution, axis_ay); - char axis_az[33]; - dtostrfd(MPU_6050_az, Settings.flag2.axis_resolution, axis_az); - char axis_gx[33]; - dtostrfd(MPU_6050_gx, Settings.flag2.axis_resolution, axis_gx); - char axis_gy[33]; - dtostrfd(MPU_6050_gy, Settings.flag2.axis_resolution, axis_gy); - char axis_gz[33]; - dtostrfd(MPU_6050_gz, Settings.flag2.axis_resolution, axis_gz); -#ifdef USE_MPU6050_DMP - char axis_yaw[33]; - dtostrfd(MPU6050_dmp.yawPitchRoll[0] / PI * 180.0, Settings.flag2.axis_resolution, axis_yaw); - char axis_pitch[33]; - dtostrfd(MPU6050_dmp.yawPitchRoll[1] / PI * 180.0, Settings.flag2.axis_resolution, axis_pitch); - char axis_roll[33]; - dtostrfd(MPU6050_dmp.yawPitchRoll[2] / PI * 180.0, Settings.flag2.axis_resolution, axis_roll); -#endif - - if (json) { - char json_axis_ax[25]; - snprintf_P(json_axis_ax, sizeof(json_axis_ax), PSTR(",\"" D_JSON_AXIS_AX "\":%s"), axis_ax); - char json_axis_ay[25]; - snprintf_P(json_axis_ay, sizeof(json_axis_ay), PSTR(",\"" D_JSON_AXIS_AY "\":%s"), axis_ay); - char json_axis_az[25]; - snprintf_P(json_axis_az, sizeof(json_axis_az), PSTR(",\"" D_JSON_AXIS_AZ "\":%s"), axis_az); - char json_axis_gx[25]; - snprintf_P(json_axis_gx, sizeof(json_axis_gx), PSTR(",\"" D_JSON_AXIS_GX "\":%s"), axis_gx); - char json_axis_gy[25]; - snprintf_P(json_axis_gy, sizeof(json_axis_gy), PSTR(",\"" D_JSON_AXIS_GY "\":%s"), axis_gy); - char json_axis_gz[25]; - snprintf_P(json_axis_gz, sizeof(json_axis_gz), PSTR(",\"" D_JSON_AXIS_GZ "\":%s"), axis_gz); -#ifdef USE_MPU6050_DMP - char json_ypr_y[25]; - snprintf_P(json_ypr_y, sizeof(json_ypr_y), PSTR(",\"" D_JSON_YAW "\":%s"), axis_yaw); - char json_ypr_p[25]; - snprintf_P(json_ypr_p, sizeof(json_ypr_p), PSTR(",\"" D_JSON_PITCH "\":%s"), axis_pitch); - char json_ypr_r[25]; - snprintf_P(json_ypr_r, sizeof(json_ypr_r), PSTR(",\"" D_JSON_ROLL "\":%s"), axis_roll); - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s%s%s%s%s%s%s%s%s}"), - D_SENSOR_MPU6050, temperature, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz, - json_ypr_y, json_ypr_p, json_ypr_r); -#else - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s%s%s%s%s%s}"), - D_SENSOR_MPU6050, temperature, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz); -#endif -#ifdef USE_DOMOTICZ - DomoticzSensor(DZ_TEMP, temperature); -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, D_SENSOR_MPU6050, temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_AXIS, axis_ax, axis_ay, axis_az, axis_gx, axis_gy, axis_gz); -#ifdef USE_MPU6050_DMP - WSContentSend_PD(HTTP_SNS_YPR, axis_yaw, axis_pitch, axis_roll); -#endif -#endif - } -} - - - - - -bool Xsns32(uint8_t function) -{ - if (!I2cEnabled(XI2C_25)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - MPU_6050Detect(); - } - else if (MPU_6050_found) { - switch (function) { - case FUNC_EVERY_SECOND: - if (tele_period == Settings.tele_period -3) { - MPU_6050PerformReading(); - } - break; - case FUNC_JSON_APPEND: - MPU_6050Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - MPU_6050Show(0); - MPU_6050PerformReading(); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_33_ds3231.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_33_ds3231.ino" -#ifdef USE_I2C -#ifdef USE_DS3231 -# 35 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_33_ds3231.ino" -#define XSNS_33 33 -#define XI2C_26 26 - - -#ifndef USE_RTC_ADDR -#define USE_RTC_ADDR 0x68 -#endif - - -#define RTC_SECONDS 0x00 -#define RTC_MINUTES 0x01 -#define RTC_HOURS 0x02 -#define RTC_DAY 0x03 -#define RTC_DATE 0x04 -#define RTC_MONTH 0x05 -#define RTC_YEAR 0x06 -#define RTC_CONTROL 0x0E -#define RTC_STATUS 0x0F - -#define OSF 7 -#define EOSC 7 -#define BBSQW 6 -#define CONV 5 -#define RS2 4 -#define RS1 3 -#define INTCN 2 - - -#define HR1224 6 -#define CENTURY 7 -#define DYDT 6 -bool ds3231ReadStatus = false; -bool ds3231WriteStatus = false; -bool DS3231chipDetected = false; - - - - -void DS3231Detect(void) -{ - if (I2cActive(USE_RTC_ADDR)) { return; } - - if (I2cValidRead(USE_RTC_ADDR, RTC_STATUS, 1)) { - I2cSetActiveFound(USE_RTC_ADDR, "DS3231"); - DS3231chipDetected = true; - } -} - - - - -uint8_t bcd2dec(uint8_t n) -{ - return n - 6 * (n >> 4); -} - - - - -uint8_t dec2bcd(uint8_t n) -{ - return n + 6 * (n / 10); -} - - - - -uint32_t ReadFromDS3231(void) -{ - TIME_T tm; - tm.second = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_SECONDS)); - tm.minute = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_MINUTES)); - tm.hour = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_HOURS) & ~_BV(HR1224)); - tm.day_of_week = I2cRead8(USE_RTC_ADDR, RTC_DAY); - tm.day_of_month = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_DATE)); - tm.month = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_MONTH) & ~_BV(CENTURY)); - tm.year = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_YEAR)); - return MakeTime(tm); -} - - - -void SetDS3231Time (uint32_t epoch_time) { - TIME_T tm; - BreakTime(epoch_time, tm); - I2cWrite8(USE_RTC_ADDR, RTC_SECONDS, dec2bcd(tm.second)); - I2cWrite8(USE_RTC_ADDR, RTC_MINUTES, dec2bcd(tm.minute)); - I2cWrite8(USE_RTC_ADDR, RTC_HOURS, dec2bcd(tm.hour)); - I2cWrite8(USE_RTC_ADDR, RTC_DAY, tm.day_of_week); - I2cWrite8(USE_RTC_ADDR, RTC_DATE, dec2bcd(tm.day_of_month)); - I2cWrite8(USE_RTC_ADDR, RTC_MONTH, dec2bcd(tm.month)); - I2cWrite8(USE_RTC_ADDR, RTC_YEAR, dec2bcd(tm.year)); - I2cWrite8(USE_RTC_ADDR, RTC_STATUS, I2cRead8(USE_RTC_ADDR, RTC_STATUS) & ~_BV(OSF)); -} - -void DS3231EverySecond(void) -{ - TIME_T tmpTime; - if (!ds3231ReadStatus && Rtc.utc_time < START_VALID_TIME ) { - ntp_force_sync = true; - Rtc.utc_time = ReadFromDS3231(); - - - BreakTime(Rtc.utc_time, tmpTime); - if (Rtc.utc_time < START_VALID_TIME ) { - ds3231ReadStatus = true; - } - RtcTime.year = tmpTime.year + 1970; - Rtc.daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year); - Rtc.standard_time = RuleToTime(Settings.tflag[0], RtcTime.year); - AddLog_P2(LOG_LEVEL_INFO, PSTR("Set time from DS3231 to RTC (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), - GetDateAndTime(DT_UTC).c_str(), GetDateAndTime(DT_DST).c_str(), GetDateAndTime(DT_STD).c_str()); - if (Rtc.local_time < START_VALID_TIME) { - rules_flag.time_init = 1; - } else { - rules_flag.time_set = 1; - } - } - else if (!ds3231WriteStatus && Rtc.utc_time > START_VALID_TIME && abs(Rtc.utc_time - ReadFromDS3231()) > 60) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("Write Time TO DS3231 from NTP (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), - GetDateAndTime(DT_UTC).c_str(), GetDateAndTime(DT_DST).c_str(), GetDateAndTime(DT_STD).c_str()); - SetDS3231Time (Rtc.utc_time); - ds3231WriteStatus = true; - } -} - - - - - -bool Xsns33(uint8_t function) -{ - if (!I2cEnabled(XI2C_26)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - DS3231Detect(); - } - else if (DS3231chipDetected) { - switch (function) { - case FUNC_EVERY_SECOND: - DS3231EverySecond(); - break; - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_34_hx711.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_34_hx711.ino" -#ifdef USE_HX711 -# 35 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_34_hx711.ino" -#define XSNS_34 34 - -#ifndef HX_MAX_WEIGHT -#define HX_MAX_WEIGHT 20000 -#endif -#ifndef HX_REFERENCE -#define HX_REFERENCE 250 -#endif -#ifndef HX_SCALE -#define HX_SCALE 120 -#endif - -#define HX_TIMEOUT 120 -#define HX_SAMPLES 10 -#define HX_CAL_TIMEOUT 15 - -#define HX_GAIN_128 1 -#define HX_GAIN_32 2 -#define HX_GAIN_64 3 - -#define D_JSON_WEIGHT_REF "WeightRef" -#define D_JSON_WEIGHT_CAL "WeightCal" -#define D_JSON_WEIGHT_MAX "WeightMax" -#define D_JSON_WEIGHT_ITEM "WeightItem" -#define D_JSON_WEIGHT_CHANGE "WeightChange" -#define D_JSON_WEIGHT_RAW "WeightRaw" -#define D_JSON_WEIGHT_DELTA "WeightDelta" - -enum HxCalibrationSteps { HX_CAL_END, HX_CAL_LIMBO, HX_CAL_FINISH, HX_CAL_FAIL, HX_CAL_DONE, HX_CAL_FIRST, HX_CAL_RESET, HX_CAL_START }; - -const char kHxCalibrationStates[] PROGMEM = D_HX_CAL_FAIL "|" D_HX_CAL_DONE "|" D_HX_CAL_REFERENCE "|" D_HX_CAL_REMOVE; - -struct HX { - long weight = 0; - long raw = 0; - long last_weight = 0; - long sum_weight = 0; - long sum_raw = 0; - long offset = 0; - long scale = 1; - long weight_diff = 0; - uint8_t type = 1; - uint8_t sample_count = 0; - uint8_t calibrate_step = HX_CAL_END; - uint8_t calibrate_timer = 0; - uint8_t calibrate_msg = 0; - uint8_t pin_sck; - uint8_t pin_dout; - bool tare_flg = false; - bool weight_changed = false; - uint16_t weight_delta = 4; -} Hx; - - - -bool HxIsReady(uint16_t timeout) -{ - - uint32_t start = millis(); - while ((digitalRead(Hx.pin_dout) == HIGH) && (millis() - start < timeout)) { yield(); } - return (digitalRead(Hx.pin_dout) == LOW); -} - -long HxRead(void) -{ - if (!HxIsReady(HX_TIMEOUT)) { return -1; } - - uint8_t data[3] = { 0 }; - uint8_t filler = 0x00; - - - data[2] = shiftIn(Hx.pin_dout, Hx.pin_sck, MSBFIRST); - data[1] = shiftIn(Hx.pin_dout, Hx.pin_sck, MSBFIRST); - data[0] = shiftIn(Hx.pin_dout, Hx.pin_sck, MSBFIRST); - - - for (unsigned int i = 0; i < HX_GAIN_128; i++) { - digitalWrite(Hx.pin_sck, HIGH); - digitalWrite(Hx.pin_sck, LOW); - } - - - if (data[2] & 0x80) { filler = 0xFF; } - - - unsigned long value = ( static_cast(filler) << 24 - | static_cast(data[2]) << 16 - | static_cast(data[1]) << 8 - | static_cast(data[0]) ); - - return static_cast(value); -} - - - -void HxResetPart(void) -{ - Hx.tare_flg = true; - Hx.sum_weight = 0; - Hx.sample_count = 0; - Hx.last_weight = 0; -} - -void HxReset(void) -{ - HxResetPart(); - Settings.energy_frequency_calibration = 0; -} - -void HxCalibrationStateTextJson(uint8_t msg_id) -{ - char cal_text[30]; - - Hx.calibrate_msg = msg_id; - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, GetTextIndexed(cal_text, sizeof(cal_text), Hx.calibrate_msg, kHxCalibrationStates)); - - if (msg_id < 3) { MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("Sensor34")); } -} - -void SetWeightDelta() -{ - - if (Settings.weight_change == 0) { - Hx.weight_delta = 4; - return; - } - - - if (Settings.weight_change > 100) { - Hx.weight_delta = (Settings.weight_change - 100) * 10 + 100; - return; - } - - - Hx.weight_delta = Settings.weight_change - 1; -} -# 192 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_34_hx711.ino" -bool HxCommand(void) -{ - bool serviced = true; - bool show_parms = false; - char sub_string[XdrvMailbox.data_len +1]; - - for (uint32_t ca = 0; ca < XdrvMailbox.data_len; ca++) { - if ((' ' == XdrvMailbox.data[ca]) || ('=' == XdrvMailbox.data[ca])) { XdrvMailbox.data[ca] = ','; } - } - - switch (XdrvMailbox.payload) { - case 1: - HxReset(); - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, "Reset"); - break; - case 2: - if (strstr(XdrvMailbox.data, ",") != nullptr) { - Settings.weight_reference = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); - } - Hx.scale = 1; - HxReset(); - Hx.calibrate_step = HX_CAL_START; - Hx.calibrate_timer = 1; - HxCalibrationStateTextJson(3); - break; - case 3: - if (strstr(XdrvMailbox.data, ",") != nullptr) { - Settings.weight_reference = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); - } - show_parms = true; - break; - case 4: - if (strstr(XdrvMailbox.data, ",") != nullptr) { - Settings.weight_calibration = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); - Hx.scale = Settings.weight_calibration; - } - show_parms = true; - break; - case 5: - if (strstr(XdrvMailbox.data, ",") != nullptr) { - Settings.weight_max = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10) / 1000; - } - show_parms = true; - break; - case 6: - if (strstr(XdrvMailbox.data, ",") != nullptr) { - Settings.weight_item = (unsigned long)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)) * 10); - } - show_parms = true; - break; - case 7: - Settings.energy_frequency_calibration = Hx.weight; - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, D_JSON_DONE); - break; - case 8: - if (strstr(XdrvMailbox.data, ",") != nullptr) { - Settings.SensorBits1.hx711_json_weight_change = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10) & 1; - } - show_parms = true; - break; - case 9: - if (strstr(XdrvMailbox.data, ",") != nullptr) { - Settings.weight_change = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); - SetWeightDelta(); - } - show_parms = true; - break; - default: - show_parms = true; - } - - if (show_parms) { - char item[33]; - dtostrfd((float)Settings.weight_item / 10, 1, item); - Response_P(PSTR("{\"Sensor34\":{\"" D_JSON_WEIGHT_REF "\":%d,\"" D_JSON_WEIGHT_CAL "\":%d,\"" D_JSON_WEIGHT_MAX "\":%d,\"" - D_JSON_WEIGHT_ITEM "\":%s,\"" D_JSON_WEIGHT_CHANGE "\":%s,\"" D_JSON_WEIGHT_DELTA "\":%d}}"), - Settings.weight_reference, Settings.weight_calibration, Settings.weight_max * 1000, - item, GetStateText(Settings.SensorBits1.hx711_json_weight_change), Settings.weight_change); - } - - return serviced; -} - - - -long HxWeight(void) -{ - return (Hx.calibrate_step < HX_CAL_FAIL) ? Hx.weight : 0; -} - -void HxInit(void) -{ - Hx.type = 0; - if ((pin[GPIO_HX711_DAT] < 99) && (pin[GPIO_HX711_SCK] < 99)) { - Hx.pin_sck = pin[GPIO_HX711_SCK]; - Hx.pin_dout = pin[GPIO_HX711_DAT]; - - pinMode(Hx.pin_sck, OUTPUT); - pinMode(Hx.pin_dout, INPUT); - - digitalWrite(Hx.pin_sck, LOW); - - SetWeightDelta(); - - if (HxIsReady(8 * HX_TIMEOUT)) { - if (!Settings.weight_max) { Settings.weight_max = HX_MAX_WEIGHT / 1000; } - if (!Settings.weight_calibration) { Settings.weight_calibration = HX_SCALE; } - if (!Settings.weight_reference) { Settings.weight_reference = HX_REFERENCE; } - Hx.scale = Settings.weight_calibration; - HxRead(); - HxResetPart(); - Hx.type = 1; - } - } -} - -void HxEvery100mSecond(void) -{ - long raw = HxRead(); - Hx.sum_raw += raw; - Hx.sum_weight += raw; - - Hx.sample_count++; - if (HX_SAMPLES == Hx.sample_count) { - long average = Hx.sum_weight / Hx.sample_count; - long raw_average = Hx.sum_raw / Hx.sample_count; - long value = average - Hx.offset; - Hx.weight = value / Hx.scale; - Hx.raw = raw_average / Hx.scale; - if (Hx.weight < 0) { - if (Settings.energy_frequency_calibration) { - long difference = Settings.energy_frequency_calibration + Hx.weight; - Hx.last_weight = difference; - if (difference < 0) { HxReset(); } - } - Hx.weight = 0; - } else { - Hx.last_weight = Settings.energy_frequency_calibration; - } - - if (Hx.tare_flg) { - Hx.tare_flg = false; - Hx.offset = average; - } - - if (Hx.calibrate_step) { - Hx.calibrate_timer--; - - if (HX_CAL_START == Hx.calibrate_step) { - Hx.calibrate_step--; - Hx.calibrate_timer = HX_CAL_TIMEOUT * (10 / HX_SAMPLES); - } - else if (HX_CAL_RESET == Hx.calibrate_step) { - if (Hx.calibrate_timer) { - if (Hx.weight < (long)Settings.weight_reference) { - Hx.calibrate_step--; - Hx.calibrate_timer = HX_CAL_TIMEOUT * (10 / HX_SAMPLES); - HxCalibrationStateTextJson(2); - } - } else { - Hx.calibrate_step = HX_CAL_FAIL; - } - } - else if (HX_CAL_FIRST == Hx.calibrate_step) { - if (Hx.calibrate_timer) { - if (Hx.weight > (long)Settings.weight_reference) { - Hx.calibrate_step--; - } - } else { - Hx.calibrate_step = HX_CAL_FAIL; - } - } - else if (HX_CAL_DONE == Hx.calibrate_step) { - if (Hx.weight > (long)Settings.weight_reference) { - Hx.calibrate_step = HX_CAL_FINISH; - Settings.weight_calibration = Hx.weight / Settings.weight_reference; - Hx.weight = 0; - HxCalibrationStateTextJson(1); - } else { - Hx.calibrate_step = HX_CAL_FAIL; - } - } - - if (HX_CAL_FAIL == Hx.calibrate_step) { - Hx.calibrate_step--; - Hx.tare_flg = true; - HxCalibrationStateTextJson(0); - } - if (HX_CAL_FINISH == Hx.calibrate_step) { - Hx.calibrate_step--; - Hx.calibrate_timer = 3 * (10 / HX_SAMPLES); - Hx.scale = Settings.weight_calibration; - } - - if (!Hx.calibrate_timer) { - Hx.calibrate_step = HX_CAL_END; - } - } else { - Hx.weight += Hx.last_weight; - - if (Settings.SensorBits1.hx711_json_weight_change) { - if (abs(Hx.weight - Hx.weight_diff) > Hx.weight_delta) { - Hx.weight_diff = Hx.weight; - Hx.weight_changed = true; - } - else if (Hx.weight_changed && (Hx.weight == Hx.weight_diff)) { - mqtt_data[0] = '\0'; - ResponseAppendTime(); - HxShow(true); - ResponseJsonEnd(); - MqttPublishTeleSensor(); - Hx.weight_changed = false; - } - } - } - - Hx.sum_weight = 0; - Hx.sum_raw = 0; - Hx.sample_count = 0; - } -} - -void HxSaveBeforeRestart(void) -{ - Settings.energy_frequency_calibration = Hx.weight; - Hx.sample_count = HX_SAMPLES +1; -} - -#ifdef USE_WEBSERVER -const char HTTP_HX711_WEIGHT[] PROGMEM = - "{s}HX711 " D_WEIGHT "{m}%s " D_UNIT_KILOGRAM "{e}"; -const char HTTP_HX711_COUNT[] PROGMEM = - "{s}HX711 " D_COUNT "{m}%d{e}"; -const char HTTP_HX711_CAL[] PROGMEM = - "{s}HX711 %s{m}{e}"; -#endif - -void HxShow(bool json) -{ - char scount[30] = { 0 }; - - uint16_t count = 0; - float weight = 0; - if (Hx.calibrate_step < HX_CAL_FAIL) { - if (Hx.weight && Settings.weight_item) { - count = (Hx.weight * 10) / Settings.weight_item; - if (count > 1) { - snprintf_P(scount, sizeof(scount), PSTR(",\"" D_JSON_COUNT "\":%d"), count); - } - } - weight = (float)Hx.weight / 1000; - } - char weight_chr[33]; - dtostrfd(weight, Settings.flag2.weight_resolution, weight_chr); - - if (json) { - ResponseAppend_P(PSTR(",\"HX711\":{\"" D_JSON_WEIGHT "\":%s%s, \"" D_JSON_WEIGHT_RAW "\":%d}"), weight_chr, scount, Hx.raw); -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_HX711_WEIGHT, weight_chr); - if (count > 1) { - WSContentSend_PD(HTTP_HX711_COUNT, count); - } - if (Hx.calibrate_step) { - char cal_text[30]; - WSContentSend_PD(HTTP_HX711_CAL, GetTextIndexed(cal_text, sizeof(cal_text), Hx.calibrate_msg, kHxCalibrationStates)); - } -#endif - } -} - -#ifdef USE_WEBSERVER -#ifdef USE_HX711_GUI - - - - -#define WEB_HANDLE_HX711 "s34" - -const char S_CONFIGURE_HX711[] PROGMEM = D_CONFIGURE_HX711; - -const char HTTP_BTN_MENU_MAIN_HX711[] PROGMEM = - "

"; - -const char HTTP_BTN_MENU_HX711[] PROGMEM = - "

"; - -const char HTTP_FORM_HX711[] PROGMEM = - "
 " D_CALIBRATION " " - "
" - "

" D_REFERENCE_WEIGHT " (" D_UNIT_KILOGRAM ")

" - "
" - "
" - "


" - - "
 " D_HX711_PARAMETERS " " - "
" - "

" D_ITEM_WEIGHT " (" D_UNIT_KILOGRAM ")

"; - -void HandleHxAction(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_HX711); - - if (Webserver->hasArg("save")) { - HxSaveSettings(); - HandleConfiguration(); - return; - } - - char stemp1[20]; - - if (Webserver->hasArg("reset")) { - snprintf_P(stemp1, sizeof(stemp1), PSTR("Sensor34 1")); - ExecuteWebCommand(stemp1, SRC_WEBGUI); - - HandleRoot(); - return; - } - - if (Webserver->hasArg("calibrate")) { - WebGetArg("p1", stemp1, sizeof(stemp1)); - Settings.weight_reference = (!strlen(stemp1)) ? 0 : (unsigned long)(CharToFloat(stemp1) * 1000); - - HxLogUpdates(); - - snprintf_P(stemp1, sizeof(stemp1), PSTR("Sensor34 2")); - ExecuteWebCommand(stemp1, SRC_WEBGUI); - - HandleRoot(); - return; - } - - WSContentStart_P(S_CONFIGURE_HX711); - WSContentSendStyle(); - dtostrfd((float)Settings.weight_reference / 1000, 3, stemp1); - char stemp2[20]; - dtostrfd((float)Settings.weight_item / 10000, 4, stemp2); - WSContentSend_P(HTTP_FORM_HX711, stemp1, stemp2); - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); -} - -void HxSaveSettings(void) -{ - char tmp[100]; - - WebGetArg("p2", tmp, sizeof(tmp)); - Settings.weight_item = (!strlen(tmp)) ? 0 : (unsigned long)(CharToFloat(tmp) * 10000); - - HxLogUpdates(); -} - -void HxLogUpdates(void) -{ - char weigth_ref_chr[33]; - dtostrfd((float)Settings.weight_reference / 1000, Settings.flag2.weight_resolution, weigth_ref_chr); - char weigth_item_chr[33]; - dtostrfd((float)Settings.weight_item / 10000, 4, weigth_item_chr); - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_JSON_WEIGHT_REF " %s, " D_JSON_WEIGHT_ITEM " %s"), weigth_ref_chr, weigth_item_chr); -} - -#endif -#endif - - - - - -bool Xsns34(uint8_t function) -{ - bool result = false; - - if (Hx.type) { - switch (function) { - case FUNC_EVERY_100_MSECOND: - HxEvery100mSecond(); - break; - case FUNC_COMMAND_SENSOR: - if (XSNS_34 == XdrvMailbox.index) { - result = HxCommand(); - } - break; - case FUNC_JSON_APPEND: - HxShow(1); - break; - case FUNC_SAVE_BEFORE_RESTART: - HxSaveBeforeRestart(); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - HxShow(0); - break; -#ifdef USE_HX711_GUI - case FUNC_WEB_ADD_MAIN_BUTTON: - WSContentSend_P(HTTP_BTN_MENU_MAIN_HX711); - break; - case FUNC_WEB_ADD_BUTTON: - WSContentSend_P(HTTP_BTN_MENU_HX711); - break; - case FUNC_WEB_ADD_HANDLER: - Webserver->on("/" WEB_HANDLE_HX711, HandleHxAction); - break; -#endif -#endif - case FUNC_INIT: - HxInit(); - break; - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_35_tx20.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_35_tx20.ino" -#if defined(USE_TX20_WIND_SENSOR) || defined(USE_TX23_WIND_SENSOR) -# 51 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_35_tx20.ino" -#define XSNS_35 35 - -#if defined(USE_TX20_WIND_SENSOR) && defined(USE_TX23_WIND_SENSOR) -#undef USE_TX20_WIND_SENSOR -#warning **** use USE_TX20_WIND_SENSOR or USE_TX23_WIND_SENSOR but not both together, TX20 disabled **** -#endif - - -#define TX2X_BIT_TIME 1220 -#define TX2X_WEIGHT_AVG_SAMPLE 150 -#define TX2X_TIMEOUT 10 -#define TX23_READ_INTERVAL 4 - - - -extern "C" { -#include "gpio.h" -} - -#ifdef USE_TX20_WIND_SENSOR -#undef D_TX2x_NAME -#define D_TX2x_NAME "TX20" -#else -#undef D_TX2x_NAME -#define D_TX2x_NAME "TX23" -#endif - -#ifdef USE_WEBSERVER -#define D_TX20_WIND_AVG "∅" -#define D_TX20_WIND_ANGLE "∠" -#define D_TX20_WIND_DEGREE "°" -const char HTTP_SNS_TX2X[] PROGMEM = - "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED "{m}%s %s{e}" -#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS - "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED " " D_TX20_WIND_AVG "{m}%s %s{e}" - "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED_MIN "{m}%s %s{e}" - "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED_MAX "{m}%s %s{e}" -#endif - "{s}" D_TX2x_NAME " " D_TX20_WIND_DIRECTION "{m}%s %s" D_TX20_WIND_DEGREE "{e}" -#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS - "{s}" D_TX2x_NAME " " D_TX20_WIND_DIRECTION " " D_TX20_WIND_AVG "{m}%s %s" D_TX20_WIND_DEGREE "{e}" - "{s}" D_TX2x_NAME " " D_TX20_WIND_DIRECTION " " D_TX20_WIND_ANGLE "{m}%s" D_TX20_WIND_DEGREE " (%s,%s)" D_TX20_WIND_DEGREE; -#endif - ; -#endif - - -float const tx2x_f_pi = 3.1415926535897932384626433; -float const tx2x_f_halfpi = tx2x_f_pi / 2.0; -float const tx2x_f_pi180 = tx2x_f_pi / 180.0; - -#define TX2X_DIRECTIONS_MAXSIZE 3 -const char kTx2xDirections[] PROGMEM = D_TX20_NORTH "|" - D_TX20_NORTH D_TX20_NORTH D_TX20_EAST "|" - D_TX20_NORTH D_TX20_EAST "|" - D_TX20_EAST D_TX20_NORTH D_TX20_EAST "|" - D_TX20_EAST "|" - D_TX20_EAST D_TX20_SOUTH D_TX20_EAST "|" - D_TX20_SOUTH D_TX20_EAST "|" - D_TX20_SOUTH D_TX20_SOUTH D_TX20_EAST "|" - D_TX20_SOUTH "|" - D_TX20_SOUTH D_TX20_SOUTH D_TX20_WEST "|" - D_TX20_SOUTH D_TX20_WEST "|" - D_TX20_WEST D_TX20_SOUTH D_TX20_WEST "|" - D_TX20_WEST "|" - D_TX20_WEST D_TX20_NORTH D_TX20_WEST "|" - D_TX20_NORTH D_TX20_WEST "|" - D_TX20_NORTH D_TX20_NORTH D_TX20_WEST; - -int32_t tx2x_wind_speed = 0; -int32_t tx2x_wind_direction = 0; - -#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS -int32_t tx2x_wind_speed_min = 0xfff; -int32_t tx2x_wind_speed_max = 0; -float tx2x_wind_speed_avg = 0; -float tx2x_wind_direction_avg_x = 0; -float tx2x_wind_direction_avg_y = 0; -float tx2x_wind_direction_avg = 0; -int32_t tx2x_wind_direction_min = 0; -int32_t tx2x_wind_direction_max = 0; - -uint32_t tx2x_count = 0; -uint32_t tx2x_avg_samples; -uint32_t tx2x_last_uptime = 0; -bool tx2x_valuesread = false; -#endif - -#ifdef DEBUG_TASMOTA_SENSOR -uint32_t tx2x_sa = 0; -uint32_t tx2x_sb = 0; -uint32_t tx2x_sc = 0; -uint32_t tx2x_sd = 0; -uint32_t tx2x_se = 0; -uint32_t tx2x_sf = 0; -#endif -uint32_t tx2x_last_available = 0; - -#ifdef USE_TX23_WIND_SENSOR -uint32_t tx23_stage = 0; -#endif - -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 -void TX2xStartRead(void) ICACHE_RAM_ATTR; -#endif - -void TX2xStartRead(void) -{ -# 184 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_35_tx20.ino" -#ifdef USE_TX23_WIND_SENSOR - if (0!=tx23_stage) - { - if ((2==tx23_stage) || (3==tx23_stage)) - { -#endif -#ifdef DEBUG_TASMOTA_SENSOR - tx2x_sa = 0; - tx2x_sb = 0; - tx2x_sc = 0; - tx2x_sd = 0; - tx2x_se = 0; - tx2x_sf = 0; -#else - uint32_t tx2x_sa = 0; - uint32_t tx2x_sb = 0; - uint32_t tx2x_sc = 0; - uint32_t tx2x_sd = 0; - uint32_t tx2x_se = 0; - uint32_t tx2x_sf = 0; -#endif - - delayMicroseconds(TX2X_BIT_TIME / 2); - - for (int32_t bitcount = 41; bitcount > 0; bitcount--) { - uint32_t dpin = (digitalRead(pin[GPIO_TX2X_TXD_BLACK])); -#ifdef USE_TX23_WIND_SENSOR - dpin ^= 1; -#endif - if (bitcount > 41 - 5) { - - tx2x_sa = (tx2x_sa << 1) | (dpin ^ 1); - } else if (bitcount > 41 - 5 - 4) { - - tx2x_sb = tx2x_sb >> 1 | ((dpin ^ 1) << 3); - } else if (bitcount > 41 - 5 - 4 - 12) { - - tx2x_sc = tx2x_sc >> 1 | ((dpin ^ 1) << 11); - } else if (bitcount > 41 - 5 - 4 - 12 - 4) { - - tx2x_sd = tx2x_sd >> 1 | ((dpin ^ 1) << 3); - } else if (bitcount > 41 - 5 - 4 - 12 - 4 - 4) { - - tx2x_se = tx2x_se >> 1 | (dpin << 3); - } else { - - tx2x_sf = tx2x_sf >> 1 | (dpin << 11); - } - delayMicroseconds(TX2X_BIT_TIME); - } - - uint32_t chk = (tx2x_sb + (tx2x_sc & 0xf) + ((tx2x_sc >> 4) & 0xf) + ((tx2x_sc >> 8) & 0xf)); - chk &= 0xf; - - - ; -#ifdef USE_TX23_WIND_SENSOR - if ((chk == tx2x_sd) && (0x1b==tx2x_sa) && (tx2x_sb==tx2x_se) && (tx2x_sc==tx2x_sf) && (tx2x_sc < 511)) { -#else - if ((chk == tx2x_sd) && (tx2x_sb==tx2x_se) && (tx2x_sc==tx2x_sf) && (tx2x_sc < 511)) { -#endif - tx2x_last_available = uptime; - - tx2x_wind_speed = tx2x_sc; - tx2x_wind_direction = tx2x_sb; -#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS - if (!tx2x_valuesread) { - tx2x_wind_direction_min = tx2x_wind_direction; - tx2x_wind_direction_max = tx2x_wind_direction; - tx2x_valuesread = true; - } -#endif - } - -#ifdef USE_TX23_WIND_SENSOR - } - tx23_stage++; - } -#endif - - - - GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << pin[GPIO_TX2X_TXD_BLACK]); -} - -bool Tx2xAvailable(void) -{ - return ((uptime - tx2x_last_available) < TX2X_TIMEOUT); -} - -#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS -float atan2f(float a, float b) -{ - float atan2val; - if (b > 0) { - atan2val = atanf(a/b); - } else if ((b < 0) && (a >= 0)) { - atan2val = atanf(a/b) + tx2x_f_pi; - } else if ((b < 0) && (a < 0)) { - atan2val = atanf(a/b) - tx2x_f_pi; - } else if ((b == 0) && (a > 0)) { - atan2val = tx2x_f_halfpi; - } else if ((b == 0) && (a < 0)) { - atan2val = 0 - (tx2x_f_halfpi); - } else if ((b == 0) && (a == 0)) { - atan2val = 1000; - } - return atan2val; -} - -void Tx2xCheckSampleCount(void) -{ - uint32_t tx2x_prev_avg_samples = tx2x_avg_samples; - if (Settings.tele_period) { - - tx2x_avg_samples = Settings.tele_period; - } else { - - tx2x_avg_samples = TX2X_WEIGHT_AVG_SAMPLE; - } - if (tx2x_prev_avg_samples != tx2x_avg_samples) { - tx2x_wind_speed_avg = tx2x_wind_speed; - tx2x_count = 0; - } -} - -void Tx2xResetStat(void) -{ - DEBUG_SENSOR_LOG(PSTR(D_TX2x_NAME ": reset statistics")); - tx2x_last_uptime = uptime; - Tx2xResetStatData(); -} - -void Tx2xResetStatData(void) -{ - tx2x_wind_speed_min = tx2x_wind_speed; - tx2x_wind_speed_max = tx2x_wind_speed; - - tx2x_wind_direction_min = tx2x_wind_direction; - tx2x_wind_direction_max = tx2x_wind_direction; -} -#endif - -void Tx2xRead(void) -{ -#ifdef USE_TX23_WIND_SENSOR - - - - - - - - if ((uptime % TX23_READ_INTERVAL)==0) { - - - tx23_stage = 0; - pinMode(pin[GPIO_TX2X_TXD_BLACK], OUTPUT); - digitalWrite(pin[GPIO_TX2X_TXD_BLACK], LOW); - } else if ((uptime % TX23_READ_INTERVAL)==1) { - - - tx23_stage = 1; - pinMode(pin[GPIO_TX2X_TXD_BLACK], INPUT_PULLUP); - } -#endif - if (Tx2xAvailable()) { -#ifdef DEBUG_TASMOTA_SENSOR - DEBUG_SENSOR_LOG(PSTR(D_TX2x_NAME ": sa=0x%02lx sb=%ld (0x%02lx), sc=%ld (0x%03lx), sd=0x%02lx, se=%ld, sf=%ld"), tx2x_sa,tx2x_sb,tx2x_sb,tx2x_sc,tx2x_sc,tx2x_sd,tx2x_se,tx2x_sf); -#endif -#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS - if (tx2x_wind_speed < tx2x_wind_speed_min) { - tx2x_wind_speed_min = tx2x_wind_speed; - } - if (tx2x_wind_speed > tx2x_wind_speed_max) { - tx2x_wind_speed_max = tx2x_wind_speed; - } - - - - - if (tx2x_count <= tx2x_avg_samples) { - tx2x_count++; - } - tx2x_wind_speed_avg -= tx2x_wind_speed_avg / tx2x_count; - tx2x_wind_speed_avg += float(tx2x_wind_speed) / tx2x_count; - - tx2x_wind_direction_avg_x -= tx2x_wind_direction_avg_x / tx2x_count; - tx2x_wind_direction_avg_x += cosf((tx2x_wind_direction*22.5) * tx2x_f_pi180) / tx2x_count; - tx2x_wind_direction_avg_y -= tx2x_wind_direction_avg_y / tx2x_count; - tx2x_wind_direction_avg_y += sinf((tx2x_wind_direction*22.5) * tx2x_f_pi180) / tx2x_count; - tx2x_wind_direction_avg = atan2f(tx2x_wind_direction_avg_y, tx2x_wind_direction_avg_x) * 180.0f / tx2x_f_pi; - if (tx2x_wind_direction_avg<0.0) { - tx2x_wind_direction_avg += 360.0; - } - if (tx2x_wind_direction_avg>360.0) { - tx2x_wind_direction_avg -= 360.0; - } - - int32_t tx2x_wind_direction_avg_int = int((tx2x_wind_direction_avg/22.5)+0.5) % 16; - - - if (tx2x_wind_direction > tx2x_wind_direction_avg_int) { - - if ((tx2x_wind_direction-tx2x_wind_direction_avg_int)>8) { - - if ((tx2x_wind_direction - 16) < tx2x_wind_direction_min) { - - tx2x_wind_direction_min = tx2x_wind_direction - 16; - } - } else { - - if (tx2x_wind_direction > tx2x_wind_direction_max) { - - tx2x_wind_direction_max = tx2x_wind_direction; - } - } - } else { - - if ((tx2x_wind_direction_avg_int-tx2x_wind_direction)>8) { - - if ((tx2x_wind_direction + 16) > tx2x_wind_direction_max) { - - tx2x_wind_direction_max = tx2x_wind_direction + 16; - } - } else { - - if (tx2x_wind_direction < tx2x_wind_direction_min) { - - tx2x_wind_direction_min = tx2x_wind_direction; - } - } - } - -#ifdef DEBUG_TASMOTA_SENSOR - char diravg[FLOATSZ]; - dtostrfd(tx2x_wind_direction_avg, 1, diravg); - char cosx[FLOATSZ]; - dtostrfd(tx2x_wind_direction_avg_x, 1, cosx); - char siny[FLOATSZ]; - dtostrfd(tx2x_wind_direction_avg_y, 1, siny); - DEBUG_SENSOR_LOG(PSTR(D_TX2x_NAME ": dir stat - counter=%ld, actint=%ld, avgint=%ld, avg=%s (cosx=%s, siny=%s), min %d, max %d"), - (uptime-tx2x_last_uptime), - tx2x_wind_direction, - tx2x_wind_direction_avg_int, - diravg, - cosx, - siny, - tx2x_wind_direction_min, - tx2x_wind_direction_max - ); -#endif -#endif - } else { - DEBUG_SENSOR_LOG(PSTR(D_TX2x_NAME ": not available")); - tx2x_wind_speed = 0; - tx2x_wind_direction = 0; -#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS - tx2x_wind_speed_avg = 0; - tx2x_wind_direction_avg = 0; - Tx2xResetStatData(); -#endif - } - -#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS - Tx2xCheckSampleCount(); - if (0==Settings.tele_period && (uptime-tx2x_last_uptime)>=tx2x_avg_samples) { - Tx2xResetStat(); - } -#endif -} - -void Tx2xInit(void) -{ - if (!Settings.flag2.speed_conversion) { - Settings.flag2.speed_conversion = 2; - } -#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS - tx2x_valuesread = false; - Tx2xResetStat(); - Tx2xCheckSampleCount(); -#endif -#ifdef USE_TX23_WIND_SENSOR - tx23_stage = 0; - pinMode(pin[GPIO_TX2X_TXD_BLACK], OUTPUT); - digitalWrite(pin[GPIO_TX2X_TXD_BLACK], LOW); -#else - pinMode(pin[GPIO_TX2X_TXD_BLACK], INPUT); -#endif - attachInterrupt(pin[GPIO_TX2X_TXD_BLACK], TX2xStartRead, RISING); -} - -int32_t Tx2xNormalize(int32_t value) -{ - while (value>15) { - value -= 16; - } - while (value<0) { - value += 16; - } - return value; -} - -void Tx2xShow(bool json) -{ - if (!Tx2xAvailable()) { return; } - - char wind_speed_string[FLOATSZ]; - dtostrfd(ConvertSpeed(tx2x_wind_speed)/10, 1, wind_speed_string); - char wind_direction_string[FLOATSZ]; - dtostrfd(tx2x_wind_direction*22.5, 1, wind_direction_string); - char wind_direction_cardinal_string[TX2X_DIRECTIONS_MAXSIZE+1]; - GetTextIndexed(wind_direction_cardinal_string, sizeof(wind_direction_cardinal_string), tx2x_wind_direction, kTx2xDirections); -#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS - char wind_speed_min_string[FLOATSZ]; - dtostrfd(ConvertSpeed(tx2x_wind_speed_min)/10, 1, wind_speed_min_string); - char wind_speed_max_string[FLOATSZ]; - dtostrfd(ConvertSpeed(tx2x_wind_speed_max)/10, 1, wind_speed_max_string); - char wind_speed_avg_string[FLOATSZ]; - dtostrfd(ConvertSpeed(tx2x_wind_speed_avg)/10, 1, wind_speed_avg_string); - char wind_direction_avg_string[FLOATSZ]; - dtostrfd(tx2x_wind_direction_avg, 1, wind_direction_avg_string); - char wind_direction_avg_cardinal_string[4]; - GetTextIndexed(wind_direction_avg_cardinal_string, sizeof(wind_direction_avg_cardinal_string), int((tx2x_wind_direction_avg/22.5f)+0.5f) % 16, kTx2xDirections); - char wind_direction_range_string[FLOATSZ]; - dtostrfd(Tx2xNormalize(tx2x_wind_direction_max-tx2x_wind_direction_min)*22.5, 1, wind_direction_range_string); - char wind_direction_min_string[FLOATSZ]; - dtostrfd(Tx2xNormalize(tx2x_wind_direction_min)*22.5, 1, wind_direction_min_string); - char wind_direction_max_string[FLOATSZ]; - dtostrfd(Tx2xNormalize(tx2x_wind_direction_max)*22.5, 1, wind_direction_max_string); -#endif - - if (json) { -#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS -#ifdef USE_TX2x_LEGACY_JSON - ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":%s,\"SpeedAvg\":%s,\"SpeedMax\":%s,\"Direction\":\"%s\",\"Degree\":%s}"), - wind_speed_string, - wind_speed_avg_string, - wind_speed_max_string, - wind_direction_cardinal_string, - wind_direction_string - ); -#else - ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":{\"Act\":%s,\"Avg\":%s,\"Min\":%s,\"Max\":%s},\"Dir\":{\"Card\":\"%s\",\"Deg\":%s,\"Avg\":%s,\"AvgCard\":\"%s\",\"Min\":%s,\"Max\":%s,\"Range\":%s}}"), - wind_speed_string, - wind_speed_avg_string, - wind_speed_min_string, - wind_speed_max_string, - wind_direction_cardinal_string, - wind_direction_string, - wind_direction_avg_string, - wind_direction_avg_cardinal_string, - wind_direction_min_string, - wind_direction_max_string, - wind_direction_range_string - ); -#endif -#else -#ifdef USE_TX2x_LEGACY_JSON - ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":%s,\"Direction\":\"%s\",\"Degree\":%s}"), - wind_speed_string, wind_direction_cardinal_string, wind_direction_string); -#else - ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":{\"Act\":%s},\"Dir\":{\"Card\":\"%s\",\"Deg\":%s}}"), - wind_speed_string, wind_direction_cardinal_string, wind_direction_string); -#endif -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TX2X, - wind_speed_string, - SpeedUnit().c_str(), -#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS - wind_speed_avg_string, - SpeedUnit().c_str(), - wind_speed_min_string, - SpeedUnit().c_str(), - wind_speed_max_string, - SpeedUnit().c_str(), -#endif - wind_direction_cardinal_string, - wind_direction_string -#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS - ,wind_direction_avg_cardinal_string, - wind_direction_avg_string, - wind_direction_range_string, - wind_direction_min_string, - wind_direction_max_string -#endif - ); -#endif - } -} - - - - - -bool Xsns35(uint8_t function) -{ - bool result = false; - - if (pin[GPIO_TX2X_TXD_BLACK] < 99) { - switch (function) { - case FUNC_INIT: - Tx2xInit(); - break; - case FUNC_EVERY_SECOND: - Tx2xRead(); - break; -#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS - case FUNC_AFTER_TELEPERIOD: - Tx2xResetStat(); - break; -#endif - case FUNC_JSON_APPEND: - Tx2xShow(true); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Tx2xShow(false); - break; -#endif - - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_36_mgc3130.ino" -# 22 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_36_mgc3130.ino" -#ifdef USE_I2C -#ifdef USE_MGC3130 -# 35 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_36_mgc3130.ino" -#define XSNS_36 36 -#define XI2C_27 27 - -#warning **** MGC3130: It is recommended to disable all unneeded I2C-drivers **** - -#define MGC3130_I2C_ADDR 0x42 - -#define MGC3130_xfer pin[GPIO_MGC3130_XFER] -#define MGC3130_reset pin[GPIO_MGC3130_RESET] - - -bool MGC3130_type = false; -char MGC3130stype[] = "MGC3130"; - - -#define MGC3130_SYSTEM_STATUS 0x15 -#define MGC3130_REQUEST_MSG 0x06 -#define MGC3130_FW_VERSION 0x83 -#define MGC3130_SET_RUNTIME 0xA2 -#define MGC3130_SENSOR_DATA 0x91 - - -#define MGC3130_GESTURE_GARBAGE 1 -#define MGC3130_FLICK_WEST_EAST 2 -#define MGC3130_FLICK_EAST_WEST 3 -#define MGC3130_FLICK_SOUTH_NORTH 4 -#define MGC3130_FLICK_NORTH_SOUTH 5 -#define MGC3130_CIRCLE_CLOCKWISE 6 -#define MGC3130_CIRCLE_CCLOCKWISE 7 - -#define MGC3130_MIN_ROTVALUE 0 -#define MGC3130_MAX_ROTVALUE 1023 -#define MGC3130_MIN_ZVALUE 32768 - - -#ifdef USE_WEBSERVER -const char HTTP_MGC_3130_SNS[] PROGMEM = - "{s}" "%s" "{m}%s{e}" - "{s}" "HwRev" "{m}%u.%u{e}" - "{s}" "loaderVer" "{m}%u.%u{e}" - "{s}" "platVer" "{m}%u{e}"; -#endif - - - - - - - -#pragma pack(1) -union MGC3130_Union{ - uint8_t buffer[132]; - struct - { - - uint8_t msgSize; - uint8_t flag; - uint8_t counter; - uint8_t id; - - struct { - uint8_t DSPStatus:1; - uint8_t gestureInfo:1; - uint8_t touchInfo:1; - uint8_t airWheelInfo:1; - uint8_t xyzPosition:1; - uint8_t noisePower:1; - uint8_t reserved:2; - uint8_t electrodeConfiguration:3; - uint8_t CICData:1; - uint8_t SDData:1; - uint16_t reserved2:3; - } outputConfigMask; - uint8_t timestamp; - struct { - uint8_t positionValid:1; - uint8_t airWheelValid:1; - uint8_t rawDataValid:1; - uint8_t noisePowerValid:1; - uint8_t environmentalNoise:1; - uint8_t clipping:1; - uint8_t reserved:1; - uint8_t DSPRunning:1; - } systemInfo; - uint16_t dspInfo; - struct { - uint8_t gestureCode:8; - uint8_t reserved:4; - uint8_t gestureType:4; - uint8_t edgeFlick:1; - uint16_t reserved2:14; - uint8_t gestureInProgress:1; - } gestureInfo; - struct { - uint8_t touchSouth:1; - uint8_t touchWest:1; - uint8_t touchNorth:1; - uint8_t touchEast:1; - uint8_t touchCentre:1; - uint8_t tapSouth:1; - uint8_t tapWest:1; - uint8_t tapNorth:1; - uint8_t tapEast :1; - uint8_t tapCentre:1; - uint8_t doubleTapSouth:1; - uint8_t doubleTapWest:1; - uint8_t doubleTapNorth:1; - uint8_t doubleTapEast:1; - uint8_t doubleTapCentre:1; - uint8_t reserved:1; - uint8_t touchCounter; - uint8_t reserved2; - } touchInfo; - int8_t airWheel; - uint8_t reserved; - uint16_t x; - uint16_t y; - uint16_t z; - float noisePower; - float CICData[4]; - float SDData[4]; - } out; - struct { - uint8_t header[3]; - - uint8_t valid; - uint8_t hwRev[2]; - uint8_t parameterStartAddr; - uint8_t loaderVersion[2]; - uint8_t loaderPlatform; - uint8_t fwStartAddr; - char fwVersion[120]; - } fw; - struct{ - uint8_t id; - uint8_t size; - uint16_t error; - uint32_t reserved; - uint32_t reserved1; - } status; -} MGC_data; -#pragma pack() - -char MGC3130_currentGesture[12]; - -int8_t MGC3130_delta, MGC3130_lastrotation = 0; -int16_t MGC3130_rotValue, MGC3130_lastSentRotValue = 0; - -uint16_t MGC3130_lastSentX, MGC3130_lastSentY, MGC3130_lastSentZ = 0; - -uint8_t hwRev[2], loaderVersion[2], loaderPlatform = 0; -char MGC3130_firmwareInfo[20]; - -uint8_t MGC3130_touchTimeout = 0; -uint16_t MGC3130_touchCounter = 1; -uint32_t MGC3130_touchTimeStamp = millis(); -bool MGC3130_triggeredByTouch = false; - -uint8_t MGC3130_mode = 1; - - - -uint8_t MGC3130autoCal[] = {0x10, 0x00, 0x00, 0xA2, 0x80, 0x00 , 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}; -uint8_t MGC3130disableAirwheel[] = {0x10, 0x00, 0x00, 0xA2, 0x90, 0x00 , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; -uint8_t MGC3130enableAirwheel[] = {0x10, 0x00, 0x00, 0xA2, 0x90, 0x00 , 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; - -void MGC3130_handleSensorData(){ - if ( MGC_data.out.outputConfigMask.touchInfo && MGC3130_touchTimeout == 0){ - if (MGC3130_handleTouch()){ - MGC3130_triggeredByTouch = true; - MqttPublishSensor(); - } - } - - if(MGC3130_mode == 1){ - if( MGC_data.out.outputConfigMask.gestureInfo && MGC_data.out.gestureInfo.gestureCode > 0){ - MGC3130_handleGesture(); - MqttPublishSensor(); - } - } - if(MGC3130_mode == 2){ - if(MGC_data.out.outputConfigMask.airWheelInfo && MGC_data.out.systemInfo.airWheelValid){ - MGC3130_handleAirWheel(); - MqttPublishSensor(); - } - } - if(MGC3130_mode == 3){ - if(MGC_data.out.systemInfo.positionValid && (MGC_data.out.z > MGC3130_MIN_ZVALUE)){ - MqttPublishSensor(); - } - } -} - -void MGC3130_sendMessage(uint8_t data[], uint8_t length){ - Wire.beginTransmission(MGC3130_I2C_ADDR); - Wire.write(data,length); - Wire.endTransmission(); - delay(2); - MGC3130_receiveMessage(); -} - - -void MGC3130_handleGesture(){ - - char edge[5]; - if (MGC_data.out.gestureInfo.edgeFlick){ - snprintf_P(edge, sizeof(edge), PSTR("ED_")); - } - else{ - snprintf_P(edge, sizeof(edge), PSTR("")); - } - switch(MGC_data.out.gestureInfo.gestureCode){ - case MGC3130_GESTURE_GARBAGE: - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("NONE")); - break; - case MGC3130_FLICK_WEST_EAST: - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_WE"), edge); - break; - case MGC3130_FLICK_EAST_WEST: - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_EW"), edge); - break; - case MGC3130_FLICK_SOUTH_NORTH: - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_SN"), edge); - break; - case MGC3130_FLICK_NORTH_SOUTH: - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_NS"), edge); - break; - case MGC3130_CIRCLE_CLOCKWISE: - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("CW")); - break; - case MGC3130_CIRCLE_CCLOCKWISE: - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("CCW")); - break; - } - -} - -bool MGC3130_handleTouch(){ - - bool success = false; - if (MGC_data.out.touchInfo.doubleTapCentre && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_C")); - MGC3130_touchTimeout = 5; - success = true; - MGC3130_touchCounter = 1; - } - else if (MGC_data.out.touchInfo.doubleTapEast && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_E")); - MGC3130_touchTimeout = 5; - success = true; - MGC3130_touchCounter = 1; - } - else if (MGC_data.out.touchInfo.doubleTapNorth && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_N")); - MGC3130_touchTimeout = 5; - success = true; - MGC3130_touchCounter = 1; - } - else if (MGC_data.out.touchInfo.doubleTapWest && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_W")); - MGC3130_touchTimeout = 5; - success = true; - MGC3130_touchCounter = 1; - } - else if (MGC_data.out.touchInfo.doubleTapSouth && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_S")); - MGC3130_touchTimeout = 5; - success = true; - MGC3130_touchCounter = 1; - } - if (MGC_data.out.touchInfo.tapCentre && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_C")); - MGC3130_touchTimeout = 2; - success = true; - MGC3130_touchCounter = 1; - } - else if (MGC_data.out.touchInfo.tapEast && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_E")); - MGC3130_touchTimeout = 2; - success = true; - MGC3130_touchCounter = 1; - } - else if (MGC_data.out.touchInfo.tapNorth && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_N")); - MGC3130_touchTimeout = 2; - success = true; - MGC3130_touchCounter = 1; - } - else if (MGC_data.out.touchInfo.tapWest && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_W")); - MGC3130_touchTimeout = 2; - success = true; - MGC3130_touchCounter = 1; - } - else if (MGC_data.out.touchInfo.tapSouth && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_S")); - MGC3130_touchTimeout = 2; - success = true; - MGC3130_touchCounter = 1; - } - else if (MGC_data.out.touchInfo.touchCentre && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_C")); - success = true; - MGC3130_touchCounter++; - } - else if (MGC_data.out.touchInfo.touchEast && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_E")); - success = true; - MGC3130_touchCounter++; - } - else if (MGC_data.out.touchInfo.touchNorth && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_N")); - success = true; - MGC3130_touchCounter++; - } - else if (MGC_data.out.touchInfo.touchWest && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_W")); - success = true; - MGC3130_touchCounter++; - } - else if (MGC_data.out.touchInfo.touchSouth && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_S")); - success = true; - MGC3130_touchCounter++; - } - - return success; -} - -void MGC3130_handleAirWheel(){ - MGC3130_delta = MGC_data.out.airWheel - MGC3130_lastrotation; - MGC3130_lastrotation = MGC_data.out.airWheel; - - MGC3130_rotValue = MGC3130_rotValue + MGC3130_delta; - if(MGC3130_rotValue < MGC3130_MIN_ROTVALUE){ - MGC3130_rotValue = MGC3130_MIN_ROTVALUE; - } - if(MGC3130_rotValue > MGC3130_MAX_ROTVALUE){ - MGC3130_rotValue = MGC3130_MAX_ROTVALUE; - } -} - -void MGC3130_handleSystemStatus(){ - -} - -bool MGC3130_receiveMessage(){ - if(MGC3130_readData()){ - switch(MGC_data.out.id){ - case MGC3130_SENSOR_DATA: - MGC3130_handleSensorData(); - break; - case MGC3130_SYSTEM_STATUS: - MGC3130_handleSystemStatus(); - break; - case MGC3130_FW_VERSION: - hwRev[0] = MGC_data.fw.hwRev[1]; - hwRev[1] = MGC_data.fw.hwRev[0]; - loaderVersion[0] = MGC_data.fw.loaderVersion[0]; - loaderVersion[1] = MGC_data.fw.loaderVersion[1]; - loaderPlatform = MGC_data.fw.loaderPlatform; - snprintf_P(MGC3130_firmwareInfo, sizeof(MGC3130_firmwareInfo), PSTR("FW: %s"), MGC_data.fw.fwVersion); - MGC3130_firmwareInfo[20] = '\0'; - - break; - } - return true; - } - return false; -} - -bool MGC3130_readData() -{ - bool success = false; - if (!digitalRead(MGC3130_xfer)){ - pinMode(MGC3130_xfer, OUTPUT); - digitalWrite(MGC3130_xfer, LOW); - Wire.requestFrom(MGC3130_I2C_ADDR, (uint16_t)32); - - MGC_data.buffer[0] = 4; - unsigned char i = 0; - while(Wire.available() && (i < MGC_data.buffer[0])){ - MGC_data.buffer[i] = Wire.read(); - i++; - } - digitalWrite(MGC3130_xfer, HIGH); - pinMode(MGC3130_xfer, INPUT); - success = true; - } - return success; -} - -void MGC3130_nextMode(){ - if (MGC3130_mode < 3){ - MGC3130_mode++; - } - else{ - MGC3130_mode = 1; - } - switch(MGC3130_mode){ - case 1: - MGC3130_sendMessage(MGC3130disableAirwheel,16); - break; - case 2: - MGC3130_sendMessage(MGC3130enableAirwheel,16); - break; - case 3: - MGC3130_sendMessage(MGC3130disableAirwheel,16); - break; - } -} - -void MGC3130_loop() -{ - if(MGC3130_touchTimeout > 0){ - MGC3130_touchTimeout--; - } - MGC3130_receiveMessage(); -} - -void MGC3130_detect(void) -{ - if (MGC3130_type || I2cActive(MGC3130_I2C_ADDR)) { return; } - - pinMode(MGC3130_xfer, INPUT_PULLUP); - pinMode(MGC3130_reset, OUTPUT); - digitalWrite(MGC3130_reset, LOW); - delay(10); - digitalWrite(MGC3130_reset, HIGH); - delay(50); - - if (MGC3130_receiveMessage()) { - I2cSetActiveFound(MGC3130_I2C_ADDR, MGC3130stype); - MGC3130_currentGesture[0] = '\0'; - MGC3130_type = true; - } -} - - - - - -void MGC3130_show(bool json) -{ - if (!MGC3130_type) { return; } - - char status_chr[2]; - if (MGC_data.out.systemInfo.DSPRunning) { - sprintf (status_chr, "1"); - } - else{ - sprintf (status_chr, "0"); - } - - if (json) { - if (MGC3130_mode == 3 && !MGC3130_triggeredByTouch) { - if (MGC_data.out.systemInfo.positionValid && !(MGC_data.out.x == MGC3130_lastSentX && MGC_data.out.y == MGC3130_lastSentY && MGC_data.out.z == MGC3130_lastSentZ)) { - ResponseAppend_P(PSTR(",\"%s\":{\"X\":%u,\"Y\":%u,\"Z\":%u}"), - MGC3130stype, MGC_data.out.x/64, MGC_data.out.y/64, (MGC_data.out.z-(uint16_t)MGC3130_MIN_ZVALUE)/64); - MGC3130_lastSentX = MGC_data.out.x; - MGC3130_lastSentY = MGC_data.out.y; - MGC3130_lastSentZ = MGC_data.out.z; - } - } - MGC3130_triggeredByTouch = false; - - if (MGC3130_mode == 2) { - if (MGC_data.out.systemInfo.airWheelValid && (MGC3130_rotValue != MGC3130_lastSentRotValue)) { - ResponseAppend_P(PSTR(",\"%s\":{\"AW\":%i}"), MGC3130stype, MGC3130_rotValue); - MGC3130_lastSentRotValue = MGC3130_rotValue; - } - } - - if (MGC3130_currentGesture[0] != '\0') { - if (millis() - MGC3130_touchTimeStamp > 220 ) { - MGC3130_touchCounter = 1; - } - ResponseAppend_P(PSTR(",\"%s\":{\"%s\":%u}"), MGC3130stype, MGC3130_currentGesture, MGC3130_touchCounter); - MGC3130_currentGesture[0] = '\0'; - MGC3130_touchTimeStamp = millis(); - } -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_MGC_3130_SNS, MGC3130stype, status_chr, hwRev[0], hwRev[1], loaderVersion[0], loaderVersion[1], loaderPlatform ); -#endif - } -} -# 557 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_36_mgc3130.ino" -bool MGC3130CommandSensor() -{ - bool serviced = true; - - switch (XdrvMailbox.payload) { - case 0: - MGC3130_nextMode(); - break; - case 1: - MGC3130_mode = 1; - MGC3130_sendMessage(MGC3130disableAirwheel,16); - break; - case 2: - MGC3130_mode = 2; - MGC3130_sendMessage(MGC3130enableAirwheel,16); - break; - case 3: - MGC3130_mode = 3; - MGC3130_sendMessage(MGC3130disableAirwheel,16); - break; - } - return serviced; -} - - - - - -bool Xsns36(uint8_t function) -{ - if (!I2cEnabled(XI2C_27)) { return false; } - - bool result = false; - - if ((FUNC_INIT == function) && (pin[GPIO_MGC3130_XFER] < 99) && (pin[GPIO_MGC3130_RESET] < 99)) { - MGC3130_detect(); - } - else if (MGC3130_type) { - switch (function) { - case FUNC_EVERY_50_MSECOND: - MGC3130_loop(); - break; - case FUNC_COMMAND_SENSOR: - if (XSNS_36 == XdrvMailbox.index) { - result = MGC3130CommandSensor(); - } - break; - case FUNC_JSON_APPEND: - MGC3130_show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - MGC3130_show(0); - break; -#endif - } - } - return result; -} -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_37_rfsensor.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_37_rfsensor.ino" -#ifdef USE_RF_SENSOR -# 33 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_37_rfsensor.ino" -#define XSNS_37 37 - - - - -#define RFSNS_VALID_WINDOW 1800 - -#define RFSNS_LOOPS_PER_MILLI 1900 -#define RFSNS_RAW_BUFFER_SIZE 180 -#define RFSNS_MIN_RAW_PULSES 112 - -#define RFSNS_MIN_PULSE_LENGTH 300 -#define RFSNS_RAWSIGNAL_SAMPLE 50 -#define RFSNS_SIGNAL_TIMEOUT 10 -#define RFSNS_SIGNAL_REPEAT_TIME 500 - -typedef struct RawSignalStruct -{ - int Number; - uint8_t Repeats; - uint8_t Multiply; - unsigned long Time; - uint8_t Pulses[RFSNS_RAW_BUFFER_SIZE+2]; - -} raw_signal_t; - -raw_signal_t *rfsns_raw_signal = nullptr; -uint8_t rfsns_rf_bit; -uint8_t rfsns_rf_port; -uint8_t rfsns_any_sensor = 0; - - - - - -bool RfSnsFetchSignal(uint8_t DataPin, bool StateSignal) -{ - uint8_t Fbit = digitalPinToBitMask(DataPin); - uint8_t Fport = digitalPinToPort(DataPin); - uint8_t FstateMask = (StateSignal ? Fbit : 0); - - if ((*portInputRegister(Fport) & Fbit) == FstateMask) { - const unsigned long LoopsPerMilli = RFSNS_LOOPS_PER_MILLI; - - - - - - - unsigned long PulseLength = 0; - if (rfsns_raw_signal->Time) { - if (rfsns_raw_signal->Repeats && (rfsns_raw_signal->Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) { - PulseLength = micros() + RFSNS_SIGNAL_TIMEOUT *1000; - while (((rfsns_raw_signal->Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) && (PulseLength > micros())) { - if ((*portInputRegister(Fport) & Fbit) == FstateMask) { - PulseLength = micros() + RFSNS_SIGNAL_TIMEOUT *1000; - } - } - while (((rfsns_raw_signal->Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) && ((*portInputRegister(Fport) & Fbit) != FstateMask)); - } - } - - int RawCodeLength = 1; - bool Ftoggle = false; - unsigned long numloops = 0; - unsigned long maxloops = RFSNS_SIGNAL_TIMEOUT * LoopsPerMilli; - rfsns_raw_signal->Multiply = RFSNS_RAWSIGNAL_SAMPLE; - do { - numloops = 0; - while(((*portInputRegister(Fport) & Fbit) == FstateMask) ^ Ftoggle) { - if (numloops++ == maxloops) { break; } - } - PulseLength = (numloops *1000) / LoopsPerMilli; - if (PulseLength < RFSNS_MIN_PULSE_LENGTH) { break; } - Ftoggle = !Ftoggle; - rfsns_raw_signal->Pulses[RawCodeLength++] = PulseLength / (unsigned long)rfsns_raw_signal->Multiply; - } - while(RawCodeLength < RFSNS_RAW_BUFFER_SIZE && numloops <= maxloops); - - if ((RawCodeLength >= RFSNS_MIN_RAW_PULSES) && (RawCodeLength < RFSNS_RAW_BUFFER_SIZE -1)) { - rfsns_raw_signal->Repeats = 0; - rfsns_raw_signal->Number = RawCodeLength -1; - rfsns_raw_signal->Pulses[rfsns_raw_signal->Number] = 0; - rfsns_raw_signal->Time = millis(); - return true; - } - else - rfsns_raw_signal->Number = 0; - } - - return false; -} - -#ifdef USE_THEO_V2 -# 149 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_37_rfsensor.ino" -#define RFSNS_THEOV2_MAX_CHANNEL 2 - -#define RFSNS_THEOV2_PULSECOUNT 114 -#define RFSNS_THEOV2_RF_PULSE_MID 1000 - -typedef struct { - uint32_t time; - int16_t temp; - uint16_t lux; - uint8_t volt; -} theo_v2_t1_t; - -typedef struct { - uint32_t time; - int16_t temp; - uint16_t hum; - uint8_t volt; -} theo_v2_t2_t; - -theo_v2_t1_t *rfsns_theo_v2_t1 = nullptr; -theo_v2_t2_t *rfsns_theo_v2_t2 = nullptr; - -void RfSnsInitTheoV2(void) -{ - rfsns_theo_v2_t1 = (theo_v2_t1_t*)malloc(RFSNS_THEOV2_MAX_CHANNEL * sizeof(theo_v2_t1_t)); - rfsns_theo_v2_t2 = (theo_v2_t2_t*)malloc(RFSNS_THEOV2_MAX_CHANNEL * sizeof(theo_v2_t2_t)); - rfsns_any_sensor++; -} - -void RfSnsAnalyzeTheov2(void) -{ - if (rfsns_raw_signal->Number != RFSNS_THEOV2_PULSECOUNT) { return; } - - uint8_t Checksum; - uint8_t Channel; - uint8_t Type; - uint8_t Voltage; - int Payload1; - int Payload2; - - uint8_t b, bytes, bits, id; - - uint8_t idx = 3; - uint8_t chksum = 0; - for (bytes = 0; bytes < 7; bytes++) { - b = 0; - for (bits = 0; bits <= 7; bits++) - { - if ((rfsns_raw_signal->Pulses[idx] * rfsns_raw_signal->Multiply) > RFSNS_THEOV2_RF_PULSE_MID) { - b |= 1 << bits; - } - idx += 2; - } - if (bytes > 0) { chksum += b; } - - switch (bytes) { - case 0: - Checksum = b; - break; - case 1: - id = b; - Channel = b & 0x7; - Type = (b >> 3) & 0x1f; - break; - case 2: - Voltage = b; - break; - case 3: - Payload1 = b; - break; - case 4: - Payload1 = (b << 8) | Payload1; - break; - case 5: - Payload2 = b; - break; - case 6: - Payload2 = (b << 8) | Payload2; - break; - } - } - - if (Checksum != chksum) { return; } - if ((Channel == 0) || (Channel > RFSNS_THEOV2_MAX_CHANNEL)) { return; } - Channel--; - - rfsns_raw_signal->Repeats = 1; - - int Payload3 = Voltage & 0x3f; - - switch (Type) { - case 1: - rfsns_theo_v2_t1[Channel].time = LocalTime(); - rfsns_theo_v2_t1[Channel].volt = Payload3; - rfsns_theo_v2_t1[Channel].temp = Payload1; - rfsns_theo_v2_t1[Channel].lux = Payload2; - break; - case 2: - rfsns_theo_v2_t2[Channel].time = LocalTime(); - rfsns_theo_v2_t2[Channel].volt = Payload3; - rfsns_theo_v2_t2[Channel].temp = Payload1; - rfsns_theo_v2_t2[Channel].hum = Payload2; - break; - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFS: TheoV2, ChkCalc %d, Chksum %d, id %d, Type %d, Ch %d, Volt %d, BattLo %d, Pld1 %d, Pld2 %d"), - chksum, Checksum, id, Type, Channel +1, Payload3, (Voltage & 0x80) >> 7, Payload1, Payload2); -} - -void RfSnsTheoV2Show(bool json) -{ - bool sensor_once = false; - - for (uint32_t i = 0; i < RFSNS_THEOV2_MAX_CHANNEL; i++) { - if (rfsns_theo_v2_t1[i].time) { - char sensor[10]; - snprintf_P(sensor, sizeof(sensor), PSTR("TV2T1C%d"), i +1); - char voltage[33]; - dtostrfd((float)rfsns_theo_v2_t1[i].volt / 10, 1, voltage); - - if (rfsns_theo_v2_t1[i].time < LocalTime() - RFSNS_VALID_WINDOW) { - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_RFRECEIVED "\":\"%s\",\"" D_JSON_VOLTAGE "\":%s}"), - sensor, GetDT(rfsns_theo_v2_t1[i].time).c_str(), voltage); - } - } else { - char temperature[33]; - dtostrfd(ConvertTemp((float)rfsns_theo_v2_t1[i].temp / 100), Settings.flag2.temperature_resolution, temperature); - - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_ILLUMINANCE "\":%d,\"" D_JSON_VOLTAGE "\":%s}"), - sensor, temperature, rfsns_theo_v2_t1[i].lux, voltage); -#ifdef USE_DOMOTICZ - if ((0 == tele_period) && !sensor_once) { - DomoticzSensor(DZ_TEMP, temperature); - DomoticzSensor(DZ_ILLUMINANCE, rfsns_theo_v2_t1[i].lux); - sensor_once = true; - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, sensor, temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_ILLUMINANCE, sensor, rfsns_theo_v2_t1[i].lux); -#endif - } - } - } - } - - sensor_once = false; - for (uint32_t i = 0; i < RFSNS_THEOV2_MAX_CHANNEL; i++) { - if (rfsns_theo_v2_t2[i].time) { - char sensor[10]; - snprintf_P(sensor, sizeof(sensor), PSTR("TV2T2C%d"), i +1); - char voltage[33]; - dtostrfd((float)rfsns_theo_v2_t2[i].volt / 10, 1, voltage); - - if (rfsns_theo_v2_t2[i].time < LocalTime() - RFSNS_VALID_WINDOW) { - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_RFRECEIVED" \":\"%s\",\"" D_JSON_VOLTAGE "\":%s}"), - sensor, GetDT(rfsns_theo_v2_t2[i].time).c_str(), voltage); - } - } else { - float temp = ConvertTemp((float)rfsns_theo_v2_t2[i].temp / 100); - float humi = ConvertHumidity((float)rfsns_theo_v2_t2[i].hum / 100); - - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{"), sensor); - ResponseAppendTHD(temp, humi); - ResponseAppend_P(PSTR(",\"" D_JSON_VOLTAGE "\":%s}"), voltage); - - if ((0 == tele_period) && !sensor_once) { -#ifdef USE_DOMOTICZ - DomoticzTempHumPressureSensor(temp, humi); -#endif -#ifdef USE_KNX - KnxSensor(KNX_TEMPERATURE, temp); - KnxSensor(KNX_HUMIDITY, humi); -#endif - sensor_once = true; - } -#ifdef USE_WEBSERVER - } else { - WSContentSend_THD(sensor, temp, humi); -#endif - } - } - } - } -} - -#endif - -#ifdef USE_ALECTO_V2 -# 389 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_37_rfsensor.ino" -#define RFSNS_DKW2012_PULSECOUNT 176 -#define RFSNS_ACH2010_MIN_PULSECOUNT 160 -#define RFSNS_ACH2010_MAX_PULSECOUNT 160 - -#define D_ALECTOV2 "AlectoV2" - -const char kAlectoV2Directions[] PROGMEM = D_TX20_NORTH "|" - D_TX20_NORTH D_TX20_NORTH D_TX20_EAST "|" - D_TX20_NORTH D_TX20_EAST "|" - D_TX20_EAST D_TX20_NORTH D_TX20_EAST "|" - D_TX20_EAST "|" - D_TX20_EAST D_TX20_SOUTH D_TX20_EAST "|" - D_TX20_SOUTH D_TX20_EAST "|" - D_TX20_SOUTH D_TX20_SOUTH D_TX20_EAST "|" - D_TX20_SOUTH "|" - D_TX20_SOUTH D_TX20_SOUTH D_TX20_WEST "|" - D_TX20_SOUTH D_TX20_WEST "|" - D_TX20_WEST D_TX20_SOUTH D_TX20_WEST "|" - D_TX20_WEST "|" - D_TX20_WEST D_TX20_NORTH D_TX20_WEST "|" - D_TX20_NORTH D_TX20_WEST "|" - D_TX20_NORTH D_TX20_NORTH D_TX20_WEST; - -typedef struct { - uint32_t time; - float temp; - float rain; - float wind; - float gust; - uint8_t type; - uint8_t humi; - uint8_t wdir; -} alecto_v2_t; - -alecto_v2_t *rfsns_alecto_v2 = nullptr; -uint16_t rfsns_alecto_rain_base = 0; - -void RfSnsInitAlectoV2(void) -{ - rfsns_alecto_v2 = (alecto_v2_t*)malloc(sizeof(alecto_v2_t)); - rfsns_any_sensor++; -} - -void RfSnsAnalyzeAlectov2() -{ - if (!(((rfsns_raw_signal->Number >= RFSNS_ACH2010_MIN_PULSECOUNT) && - (rfsns_raw_signal->Number <= RFSNS_ACH2010_MAX_PULSECOUNT)) || (rfsns_raw_signal->Number == RFSNS_DKW2012_PULSECOUNT))) { return; } - - uint8_t c = 0; - uint8_t rfbit; - uint8_t data[9] = { 0 }; - uint8_t msgtype = 0; - uint8_t rc = 0; - int temp; - uint8_t checksum = 0; - uint8_t checksumcalc = 0; - uint8_t maxidx = 8; - unsigned long atime; - float factor; - char buf1[16]; - - if (rfsns_raw_signal->Number > RFSNS_ACH2010_MAX_PULSECOUNT) { maxidx = 9; } - - uint8_t idx = maxidx; - for (uint32_t x = rfsns_raw_signal->Number; x > 0; x = x-2) { - if (rfsns_raw_signal->Pulses[x-1] * rfsns_raw_signal->Multiply < 0x300) { - rfbit = 0x80; - } else { - rfbit = 0; - } - data[idx] = (data[idx] >> 1) | rfbit; - c++; - if (c == 8) { - if (idx == 0) { break; } - c = 0; - idx--; - } - } - - checksum = data[maxidx]; - checksumcalc = RfSnsAlectoCRC8(data, maxidx); - - msgtype = (data[0] >> 4) & 0xf; - rc = (data[0] << 4) | (data[1] >> 4); - - if (checksum != checksumcalc) { return; } - if ((msgtype != 10) && (msgtype != 5)) { return; } - - rfsns_raw_signal->Repeats = 1; - - - - - - factor = 1.22; - - - - - - rfsns_alecto_v2->time = LocalTime(); - rfsns_alecto_v2->type = (RFSNS_DKW2012_PULSECOUNT == rfsns_raw_signal->Number); - rfsns_alecto_v2->temp = (float)(((data[1] & 0x3) * 256 + data[2]) - 400) / 10; - rfsns_alecto_v2->humi = data[3]; - uint16_t rain = (data[6] * 256) + data[7]; - - if (rain < rfsns_alecto_rain_base) { rfsns_alecto_rain_base = rain; } - if (rfsns_alecto_rain_base > 0) { - rfsns_alecto_v2->rain += ((float)rain - rfsns_alecto_rain_base) * 0.30; - } - rfsns_alecto_rain_base = rain; - rfsns_alecto_v2->wind = (float)data[4] * factor; - rfsns_alecto_v2->gust = (float)data[5] * factor; - if (rfsns_alecto_v2->type) { - rfsns_alecto_v2->wdir = data[8] & 0xf; - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFS: " D_ALECTOV2 ", ChkCalc %d, Chksum %d, rc %d, Temp %d, Hum %d, Rain %d, Wind %d, Gust %d, Dir %d, Factor %s"), - checksumcalc, checksum, rc, ((data[1] & 0x3) * 256 + data[2]) - 400, data[3], (data[6] * 256) + data[7], data[4], data[5], data[8] & 0xf, dtostrfd(factor, 3, buf1)); -} - -void RfSnsAlectoResetRain(void) -{ - if ((RtcTime.hour == 0) && (RtcTime.minute == 0) && (RtcTime.second == 5)) { - rfsns_alecto_v2->rain = 0; - } -} - - - - - - - -uint8_t RfSnsAlectoCRC8(uint8_t *addr, uint8_t len) -{ - uint8_t crc = 0; - while (len--) { - uint8_t inbyte = *addr++; - for (uint32_t i = 8; i; i--) { - uint8_t mix = (crc ^ inbyte) & 0x80; - crc <<= 1; - if (mix) { crc ^= 0x31; } - inbyte <<= 1; - } - } - return crc; -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_ALECTOV2[] PROGMEM = - "{s}" D_ALECTOV2 " " D_RAIN "{m}%s " D_UNIT_MILLIMETER "{e}" - "{s}" D_ALECTOV2 " " D_TX20_WIND_SPEED "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}" - "{s}" D_ALECTOV2 " " D_TX20_WIND_SPEED_MAX "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}"; -const char HTTP_SNS_ALECTOV2_WDIR[] PROGMEM = - "{s}" D_ALECTOV2 " " D_TX20_WIND_DIRECTION "{m}%s{e}"; -#endif - -void RfSnsAlectoV2Show(bool json) -{ - if (rfsns_alecto_v2->time) { - if (rfsns_alecto_v2->time < LocalTime() - RFSNS_VALID_WINDOW) { - if (json) { - ResponseAppend_P(PSTR(",\"" D_ALECTOV2 "\":{\"" D_JSON_RFRECEIVED "\":\"%s\"}"), GetDT(rfsns_alecto_v2->time).c_str()); - } - } else { - float temp = ConvertTemp(rfsns_alecto_v2->temp); - float humi = ConvertHumidity((float)rfsns_alecto_v2->humi); - - char rain[33]; - dtostrfd(rfsns_alecto_v2->rain, 2, rain); - char wind[33]; - dtostrfd(rfsns_alecto_v2->wind, 2, wind); - char gust[33]; - dtostrfd(rfsns_alecto_v2->gust, 2, gust); - char wdir[4]; - char direction[20]; - if (rfsns_alecto_v2->type) { - GetTextIndexed(wdir, sizeof(wdir), rfsns_alecto_v2->wdir, kAlectoV2Directions); - snprintf_P(direction, sizeof(direction), PSTR(",\"Direction\":\"%s\""), wdir); - } - - if (json) { - ResponseAppend_P(PSTR(",\"" D_ALECTOV2 "\":{")); - ResponseAppendTHD(temp, humi); - ResponseAppend_P(PSTR(",\"Rain\":%s,\"Wind\":%s,\"Gust\":%s%s}"), rain, wind, gust, (rfsns_alecto_v2->type) ? direction : ""); - - if (0 == tele_period) { -#ifdef USE_DOMOTICZ - - - - -#endif - } -#ifdef USE_WEBSERVER - } else { - WSContentSend_THD(D_ALECTOV2, temp, humi); - WSContentSend_PD(HTTP_SNS_ALECTOV2, rain, wind, gust); - if (rfsns_alecto_v2->type) { - WSContentSend_PD(HTTP_SNS_ALECTOV2_WDIR, wdir); - } -#endif - } - } - } -} -#endif - -void RfSnsInit(void) -{ - rfsns_raw_signal = (raw_signal_t*)(malloc(sizeof(raw_signal_t))); - if (rfsns_raw_signal) { - memset(rfsns_raw_signal, 0, sizeof(raw_signal_t)); -#ifdef USE_THEO_V2 - RfSnsInitTheoV2(); -#endif -#ifdef USE_ALECTO_V2 - RfSnsInitAlectoV2(); -#endif - if (rfsns_any_sensor) { - rfsns_rf_bit = digitalPinToBitMask(pin[GPIO_RF_SENSOR]); - rfsns_rf_port = digitalPinToPort(pin[GPIO_RF_SENSOR]); - pinMode(pin[GPIO_RF_SENSOR], INPUT); - } else { - free(rfsns_raw_signal); - rfsns_raw_signal = nullptr; - } - } -} - -void RfSnsAnalyzeRawSignal(void) -{ - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFS: Pulses %d"), (int)rfsns_raw_signal->Number); - -#ifdef USE_THEO_V2 - RfSnsAnalyzeTheov2(); -#endif -#ifdef USE_ALECTO_V2 - RfSnsAnalyzeAlectov2(); -#endif -} - -void RfSnsEverySecond(void) -{ -#ifdef USE_ALECTO_V2 - RfSnsAlectoResetRain(); -#endif -} - -void RfSnsShow(bool json) -{ -#ifdef USE_THEO_V2 - RfSnsTheoV2Show(json); -#endif -#ifdef USE_ALECTO_V2 - RfSnsAlectoV2Show(json); -#endif -} - - - - - -bool Xsns37(uint8_t function) -{ - bool result = false; - - if ((pin[GPIO_RF_SENSOR] < 99) && (FUNC_INIT == function)) { - RfSnsInit(); - } - else if (rfsns_raw_signal) { - switch (function) { - case FUNC_LOOP: - if ((*portInputRegister(rfsns_rf_port) &rfsns_rf_bit) == rfsns_rf_bit) { - if (RfSnsFetchSignal(pin[GPIO_RF_SENSOR], HIGH)) { - RfSnsAnalyzeRawSignal(); - } - } - ssleep = 0; - break; - case FUNC_EVERY_SECOND: - RfSnsEverySecond(); - break; - case FUNC_JSON_APPEND: - RfSnsShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - RfSnsShow(0); - break; -#endif - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_38_az7798.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_38_az7798.ino" -#ifdef USE_AZ7798 - -#define XSNS_38 38 -# 112 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_38_az7798.ino" -#include - -#ifndef CO2_LOW -#define CO2_LOW 800 -#endif -#ifndef CO2_HIGH -#define CO2_HIGH 1200 -#endif - -#define AZ_READ_TIMEOUT 400 - -#define AZ_CLOCK_UPDATE_INTERVAL (24UL * 60 * 60) -#define AZ_EPOCH (946684800UL) - -TasmotaSerial *AzSerial; - -const char ktype[] = "AZ7798"; -uint8_t az_type = 1; -uint16_t az_co2 = 0; -double az_temperature = 0; -double az_humidity = 0; -uint8_t az_received = 0; -uint8_t az_state = 0; -unsigned long az_clock_update = 10; - - - -void AzEverySecond(void) -{ - unsigned long start = millis(); - - az_state++; - if (5 == az_state) { - az_state = 0; - - AzSerial->flush(); - AzSerial->write(":\r", 2); - az_received = 0; - - uint8_t az_response[32]; - uint8_t counter = 0; - uint8_t i, j; - uint8_t response_substr[16]; - - do { - if (AzSerial->available() > 0) { - az_response[counter] = AzSerial->read(); - if(az_response[counter] == 0x0d) { az_received = 1; } - counter++; - } else { - delay(5); - } - } while(((millis() - start) < AZ_READ_TIMEOUT) && (counter < sizeof(az_response)) && !az_received); - - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, az_response, counter); - - if (!az_received) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 comms timeout")); - return; - } - - i = 0; - while((az_response[i] != 'T') && (i < counter)) {i++;} - if(az_response[i] != 'T') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find start of response")); - return; - } - i++; - j = 0; - - while((az_response[i] != 'C') && (az_response[i] != 'F') && (i < counter)) { - response_substr[j++] = az_response[i++]; - } - if((az_response[i] != 'C') && (az_response[i] != 'F')){ - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of temperature")); - return; - } - response_substr[j] = 0; - az_temperature = CharToFloat((char*)response_substr); - if(az_response[i] == 'C') { - az_temperature = ConvertTemp((float)az_temperature); - } else { - az_temperature = ConvertTemp((az_temperature - 32) / 1.8); - } - i++; - if(az_response[i] != ':') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error first delimiter")); - return; - } - i++; - if(az_response[i] != 'C') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error start of CO2")); - return; - } - i++; - j = 0; - - while((az_response[i] != 'p') && (i < counter)) { - response_substr[j++] = az_response[i++]; - } - if(az_response[i] != 'p') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of CO2")); - return; - } - response_substr[j] = 0; - az_co2 = atoi((char*)response_substr); -#ifdef USE_LIGHT - LightSetSignal(CO2_LOW, CO2_HIGH, az_co2); -#endif - i += 3; - if(az_response[i] != ':') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error second delimiter")); - return; - } - i++; - if(az_response[i] != 'H') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error start of humidity")); - return; - } - i++; - j = 0; - - while((az_response[i] != '%') && (i < counter)) { - response_substr[j++] = az_response[i++]; - } - if(az_response[i] != '%') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of humidity")); - return; - } - response_substr[j] = 0; - az_humidity = ConvertHumidity(CharToFloat((char*)response_substr)); - } - - - if ((az_clock_update == 0) && (LocalTime() > AZ_EPOCH)) { - char tmpString[16]; - sprintf(tmpString, "C %d\r", (int)(LocalTime() - AZ_EPOCH)); - AzSerial->write(tmpString); - - do { - if (AzSerial->available() > 0) { - if(AzSerial->read() == 0x0d) { break; } - } else { - delay(5); - } - } while(((millis() - start) < AZ_READ_TIMEOUT)); - az_clock_update = AZ_CLOCK_UPDATE_INTERVAL; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 clock updated")); - } else { - az_clock_update--; - } -} - - - -void AzInit(void) -{ - az_type = 0; - if ((pin[GPIO_AZ_RXD] < 99) && (pin[GPIO_AZ_TXD] < 99)) { - AzSerial = new TasmotaSerial(pin[GPIO_AZ_RXD], pin[GPIO_AZ_TXD], 1); - if (AzSerial->begin(9600)) { - if (AzSerial->hardwareSerial()) { ClaimSerial(); } - az_type = 1; - } - } -} - -void AzShow(bool json) -{ - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_CO2 "\":%d,"), ktype, az_co2); - ResponseAppendTHD(az_temperature, az_humidity); - ResponseJsonEnd(); -#ifdef USE_DOMOTICZ - if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, az_co2); -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_CO2, ktype, az_co2); - WSContentSend_THD(ktype, az_temperature, az_humidity); -#endif - } -} - - - - - -bool Xsns38(uint8_t function) -{ - bool result = false; - - if(az_type){ - switch (function) { - case FUNC_INIT: - AzInit(); - break; - case FUNC_EVERY_SECOND: - AzEverySecond(); - break; - case FUNC_JSON_APPEND: - AzShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - AzShow(0); - break; -#endif - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_39_max31855.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_39_max31855.ino" -#ifdef USE_MAX31855 - -#define XSNS_39 39 - -bool initialized = false; - -struct MAX31855_ResultStruct{ - uint8_t ErrorCode; - float ProbeTemperature; - float ReferenceTemperature; -} MAX31855_Result; - -void MAX31855_Init(void){ - if(initialized) - return; - - - pinMode(pin[GPIO_MAX31855CS], OUTPUT); - pinMode(pin[GPIO_MAX31855CLK], OUTPUT); - pinMode(pin[GPIO_MAX31855DO], INPUT); - - - digitalWrite(pin[GPIO_MAX31855CS], HIGH); - digitalWrite(pin[GPIO_MAX31855CLK], LOW); - - initialized = true; -} - - - - - -void MAX31855_GetResult(void){ - int32_t RawData = MAX31855_ShiftIn(32); - uint8_t probeerror = RawData & 0x7; - - MAX31855_Result.ErrorCode = probeerror; - MAX31855_Result.ReferenceTemperature = MAX31855_GetReferenceTemperature(RawData); - if(probeerror) - MAX31855_Result.ProbeTemperature = NAN; - else - MAX31855_Result.ProbeTemperature = MAX31855_GetProbeTemperature(RawData); -} - - - - - - -float MAX31855_GetProbeTemperature(int32_t RawData){ - if(RawData & 0x80000000) - RawData = (RawData >> 18) | 0xFFFFC000; - else - RawData >>= 18; - - float result = (RawData * 0.25); - - return ConvertTemp(result); -} - - - - - -float MAX31855_GetReferenceTemperature(int32_t RawData){ - if(RawData & 0x8000) - RawData = (RawData >> 4) | 0xFFFFF000; - else - RawData = (RawData >> 4) & 0x00000FFF; - - float result = (RawData * 0.0625); - - return ConvertTemp(result); -} - - - - - -int32_t MAX31855_ShiftIn(uint8_t Length){ - int32_t dataIn = 0; - - digitalWrite(pin[GPIO_MAX31855CS], LOW); - delayMicroseconds(1); - - for (uint32_t i = 0; i < Length; i++) - { - digitalWrite(pin[GPIO_MAX31855CLK], LOW); - delayMicroseconds(1); - dataIn <<= 1; - if(digitalRead(pin[GPIO_MAX31855DO])) - dataIn |= 1; - digitalWrite(pin[GPIO_MAX31855CLK], HIGH); - delayMicroseconds(1); - } - - digitalWrite(pin[GPIO_MAX31855CS], HIGH); - digitalWrite(pin[GPIO_MAX31855CLK], LOW); - return dataIn; -} - -void MAX31855_Show(bool Json){ - char probetemp[33]; - char referencetemp[33]; - dtostrfd(MAX31855_Result.ProbeTemperature, Settings.flag2.temperature_resolution, probetemp); - dtostrfd(MAX31855_Result.ReferenceTemperature, Settings.flag2.temperature_resolution, referencetemp); - - if(Json){ - ResponseAppend_P(PSTR(",\"MAX31855\":{\"" D_JSON_PROBETEMPERATURE "\":%s,\"" D_JSON_REFERENCETEMPERATURE "\":%s,\"" D_JSON_ERROR "\":%d}"), \ - probetemp, referencetemp, MAX31855_Result.ErrorCode); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_TEMP, probetemp); - } -#endif -#ifdef USE_KNX - if (0 == tele_period) { - KnxSensor(KNX_TEMPERATURE, MAX31855_Result.ProbeTemperature); - } -#endif - } else { -#ifdef USE_WEBSERVER - WSContentSend_PD(HTTP_SNS_TEMP, "MAX31855", probetemp, TempUnit()); -#endif - } -} - - - - - -bool Xsns39(uint8_t function) -{ - bool result = false; - if((pin[GPIO_MAX31855CS] < 99) && (pin[GPIO_MAX31855CLK] < 99) && (pin[GPIO_MAX31855DO] < 99)){ - - switch (function) { - case FUNC_INIT: - MAX31855_Init(); - break; - case FUNC_EVERY_SECOND: - MAX31855_GetResult(); - break; - case FUNC_JSON_APPEND: - MAX31855_Show(true); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - MAX31855_Show(false); - break; -#endif - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_40_pn532.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_40_pn532.ino" -#ifdef USE_PN532_HSU - -#define XSNS_40 40 - -#include - -TasmotaSerial *PN532_Serial; - -#define PN532_INVALID_ACK -1 -#define PN532_TIMEOUT -2 -#define PN532_INVALID_FRAME -3 -#define PN532_NO_SPACE -4 - -#define PN532_PREAMBLE 0x00 -#define PN532_STARTCODE1 0x00 -#define PN532_STARTCODE2 0xFF -#define PN532_POSTAMBLE 0x00 - -#define PN532_HOSTTOPN532 0xD4 -#define PN532_PN532TOHOST 0xD5 - -#define PN532_ACK_WAIT_TIME 0x0A - -#define PN532_COMMAND_GETFIRMWAREVERSION 0x02 -#define PN532_COMMAND_SAMCONFIGURATION 0x14 -#define PN532_COMMAND_RFCONFIGURATION 0x32 -#define PN532_COMMAND_INDATAEXCHANGE 0x40 -#define PN532_COMMAND_INLISTPASSIVETARGET 0x4A - -#define PN532_MIFARE_ISO14443A 0x00 -#define MIFARE_CMD_READ 0x30 -#define MIFARE_CMD_AUTH_A 0x60 -#define MIFARE_CMD_AUTH_B 0x61 -#define MIFARE_CMD_WRITE 0xA0 - -uint8_t pn532_model = 0; -uint8_t pn532_command = 0; -uint8_t pn532_scantimer = 0; - -uint8_t pn532_packetbuffer[64]; - -#ifdef USE_PN532_DATA_FUNCTION -uint8_t pn532_function = 0; -uint8_t pn532_newdata[16]; -uint8_t pn532_newdata_len = 0; -#endif - -void PN532_Init(void) -{ - if ((pin[GPIO_PN532_RXD] < 99) && (pin[GPIO_PN532_TXD] < 99)) { - PN532_Serial = new TasmotaSerial(pin[GPIO_PN532_RXD], pin[GPIO_PN532_TXD], 1); - if (PN532_Serial->begin(115200)) { - if (PN532_Serial->hardwareSerial()) { ClaimSerial(); } - PN532_wakeup(); - uint32_t ver = PN532_getFirmwareVersion(); - if (ver) { - PN532_setPassiveActivationRetries(0xFF); - PN532_SAMConfig(); - pn532_model = 1; - AddLog_P2(LOG_LEVEL_INFO,"NFC: PN532 NFC Reader detected (V%u.%u)",(ver>>16) & 0xFF, (ver>>8) & 0xFF); - } - } - } -} - -int8_t PN532_receive(uint8_t *buf, int len, uint16_t timeout) -{ - int read_bytes = 0; - int ret; - unsigned long start_millis; - while (read_bytes < len) { - start_millis = millis(); - do { - ret = PN532_Serial->read(); - if (ret >= 0) { - break; - } - } while((timeout == 0) || ((millis()- start_millis ) < timeout)); - - if (ret < 0) { - if (read_bytes) { - return read_bytes; - } else { - return PN532_TIMEOUT; - } - } - buf[read_bytes] = (uint8_t)ret; - read_bytes++; - } - return read_bytes; -} - -int8_t PN532_readAckFrame(void) -{ - const uint8_t PN532_ACK[] = {0, 0, 0xFF, 0, 0xFF, 0}; - uint8_t ackBuf[sizeof(PN532_ACK)]; - - if (PN532_receive(ackBuf, sizeof(PN532_ACK), PN532_ACK_WAIT_TIME) <= 0) { - return PN532_TIMEOUT; - } - - if (memcmp(&ackBuf, &PN532_ACK, sizeof(PN532_ACK))) { - return PN532_INVALID_ACK; - } - return 0; -} - -int8_t PN532_writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0) -{ - - PN532_Serial->flush(); - - pn532_command = header[0]; - PN532_Serial->write((uint8_t)PN532_PREAMBLE); - PN532_Serial->write((uint8_t)PN532_STARTCODE1); - PN532_Serial->write(PN532_STARTCODE2); - - uint8_t length = hlen + blen + 1; - PN532_Serial->write(length); - PN532_Serial->write(~length + 1); - - PN532_Serial->write(PN532_HOSTTOPN532); - uint8_t sum = PN532_HOSTTOPN532; - - PN532_Serial->write(header, hlen); - for (uint32_t i = 0; i < hlen; i++) { - sum += header[i]; - } - - PN532_Serial->write(body, blen); - for (uint32_t i = 0; i < blen; i++) { - sum += body[i]; - } - - uint8_t checksum = ~sum + 1; - PN532_Serial->write(checksum); - PN532_Serial->write((uint8_t)PN532_POSTAMBLE); - - return PN532_readAckFrame(); -} - -int16_t PN532_readResponse(uint8_t buf[], uint8_t len, uint16_t timeout = 50) -{ - uint8_t tmp[3]; - - - if (PN532_receive(tmp, 3, timeout)<=0) { - return PN532_TIMEOUT; - } - if (0 != tmp[0] || 0!= tmp[1] || 0xFF != tmp[2]) { - return PN532_INVALID_FRAME; - } - - - uint8_t length[2]; - if (PN532_receive(length, 2, timeout) <= 0) { - return PN532_TIMEOUT; - } - - if (0 != (uint8_t)(length[0] + length[1])) { - return PN532_INVALID_FRAME; - } - length[0] -= 2; - if (length[0] > len) { - return PN532_NO_SPACE; - } - - - uint8_t cmd = pn532_command + 1; - if (PN532_receive(tmp, 2, timeout) <= 0) { - return PN532_TIMEOUT; - } - if (PN532_PN532TOHOST != tmp[0] || cmd != tmp[1]) { - return PN532_INVALID_FRAME; - } - - if (PN532_receive(buf, length[0], timeout) != length[0]) { - return PN532_TIMEOUT; - } - - uint8_t sum = PN532_PN532TOHOST + cmd; - for (uint32_t i=0; i status) { - return 0; - } - - response = pn532_packetbuffer[0]; - response <<= 8; - response |= pn532_packetbuffer[1]; - response <<= 8; - response |= pn532_packetbuffer[2]; - response <<= 8; - response |= pn532_packetbuffer[3]; - - return response; -} - -void PN532_wakeup(void) -{ - uint8_t wakeup[5] = {0x55, 0x55, 0, 0, 0 }; - PN532_Serial->write(wakeup,sizeof(wakeup)); - - - PN532_Serial->flush(); -} - -bool PN532_readPassiveTargetID(uint8_t cardbaudrate, uint8_t *uid, uint8_t *uidLength, uint16_t timeout = 50) -{ - pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET; - pn532_packetbuffer[1] = 1; - pn532_packetbuffer[2] = cardbaudrate; - if (PN532_writeCommand(pn532_packetbuffer, 3)) { - return 0x0; - } - - if (PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer), timeout) < 0) { - return 0x0; - } -# 274 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_40_pn532.ino" - if (pn532_packetbuffer[0] != 1) { - return 0; - } - - uint16_t sens_res = pn532_packetbuffer[2]; - sens_res <<= 8; - sens_res |= pn532_packetbuffer[3]; - - - *uidLength = pn532_packetbuffer[5]; - - for (uint32_t i = 0; i < pn532_packetbuffer[5]; i++) { - uid[i] = pn532_packetbuffer[6 + i]; - } - - return 1; -} - -bool PN532_setPassiveActivationRetries(uint8_t maxRetries) -{ - pn532_packetbuffer[0] = PN532_COMMAND_RFCONFIGURATION; - pn532_packetbuffer[1] = 5; - pn532_packetbuffer[2] = 0xFF; - pn532_packetbuffer[3] = 0x01; - pn532_packetbuffer[4] = maxRetries; - if (PN532_writeCommand(pn532_packetbuffer, 5)) { - return 0; - } - return (0 < PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer))); -} - -bool PN532_SAMConfig(void) -{ - pn532_packetbuffer[0] = PN532_COMMAND_SAMCONFIGURATION; - pn532_packetbuffer[1] = 0x01; - pn532_packetbuffer[2] = 0x14; - pn532_packetbuffer[3] = 0x00; - if (PN532_writeCommand(pn532_packetbuffer, 4)) { - return false; - } - return (0 < PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer))); -} - -#ifdef USE_PN532_DATA_FUNCTION - -uint8_t mifareclassic_AuthenticateBlock (uint8_t *uid, uint8_t uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t *keyData) -{ - uint8_t i; - uint8_t _key[6]; - uint8_t _uid[7]; - uint8_t _uidLen; - - - memcpy(&_key, keyData, 6); - memcpy(&_uid, uid, uidLen); - _uidLen = uidLen; - - - pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; - pn532_packetbuffer[1] = 1; - pn532_packetbuffer[2] = (keyNumber) ? MIFARE_CMD_AUTH_B : MIFARE_CMD_AUTH_A; - pn532_packetbuffer[3] = blockNumber; - memcpy(&pn532_packetbuffer[4], &_key, 6); - for (i = 0; i < _uidLen; i++) { - pn532_packetbuffer[10 + i] = _uid[i]; - } - - if (PN532_writeCommand(pn532_packetbuffer, 10 + _uidLen)) { return 0; } - - - PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer)); - - - - - if (pn532_packetbuffer[0] != 0x00) { - - return 0; - } - - return 1; -} - -uint8_t mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t *data) -{ - - pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; - pn532_packetbuffer[1] = 1; - pn532_packetbuffer[2] = MIFARE_CMD_READ; - pn532_packetbuffer[3] = blockNumber; - - - if (PN532_writeCommand(pn532_packetbuffer, 4)) { - return 0; - } - - - PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer)); - - - if (pn532_packetbuffer[0] != 0x00) { - return 0; - } - - - - memcpy (data, &pn532_packetbuffer[1], 16); - - return 1; -} - -uint8_t mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t *data) -{ - - pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; - pn532_packetbuffer[1] = 1; - pn532_packetbuffer[2] = MIFARE_CMD_WRITE; - pn532_packetbuffer[3] = blockNumber; - memcpy(&pn532_packetbuffer[4], data, 16); - - - if (PN532_writeCommand(pn532_packetbuffer, 20)) { - return 0; - } - - - return (0 < PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer))); -} - -#endif - -void PN532_ScanForTag(void) -{ - if (!pn532_model) { return; } - uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; - uint8_t uid_len = 0; - uint8_t card_data[16]; - bool erase_success = false; - bool set_success = false; - if (PN532_readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uid_len)) { - char uids[15]; - -#ifdef USE_PN532_DATA_FUNCTION - char card_datas[34]; -#endif - - ToHex_P((unsigned char*)uid, uid_len, uids, sizeof(uids)); - -#ifdef USE_PN532_DATA_FUNCTION - if (uid_len == 4) { - uint8_t keyuniversal[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - if (mifareclassic_AuthenticateBlock (uid, uid_len, 1, 1, keyuniversal)) { - if (mifareclassic_ReadDataBlock(1, card_data)) { -#ifdef USE_PN532_DATA_RAW - memcpy(&card_datas,&card_data,sizeof(card_data)); -#else - for (uint32_t i = 0;i < sizeof(card_data);i++) { - if ((isalpha(card_data[i])) || ((isdigit(card_data[i])))) { - card_datas[i] = char(card_data[i]); - } else { - card_datas[i] = '\0'; - } - } -#endif - } - if (pn532_function == 1) { - for (uint32_t i = 0;i<16;i++) { - card_data[i] = 0x00; - } - if (mifareclassic_WriteDataBlock(1, card_data)) { - erase_success = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Erase success")); - memcpy(&card_datas,&card_data,sizeof(card_data)); - } - } - if (pn532_function == 2) { -#ifdef USE_PN532_DATA_RAW - memcpy(&card_data,&pn532_newdata,sizeof(card_data)); - if (mifareclassic_WriteDataBlock(1, card_data)) { - set_success = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data write successful")); - memcpy(&card_datas,&card_data,sizeof(card_data)); - } -#else - bool IsAlphaNumeric = true; - for (uint32_t i = 0;i < pn532_newdata_len;i++) { - if ((!isalpha(pn532_newdata[i])) && (!isdigit(pn532_newdata[i]))) { - IsAlphaNumeric = false; - } - } - if (IsAlphaNumeric) { - memcpy(&card_data,&pn532_newdata,pn532_newdata_len); - card_data[pn532_newdata_len] = '\0'; - if (mifareclassic_WriteDataBlock(1, card_data)) { - set_success = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data write successful")); - memcpy(&card_datas,&card_data,sizeof(card_data)); - } - } else { - AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data must be alphanumeric")); - } -#endif - } - } else { - sprintf(card_datas,"AUTHFAIL"); - } - } - switch (pn532_function) { - case 0x01: - if (!erase_success) { - AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Erase fail - exiting erase mode")); - } - break; - case 0x02: - if (!set_success) { - AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Write failed - exiting set mode")); - } - default: - break; - } - pn532_function = 0; -#endif - -#ifdef USE_PN532_DATA_FUNCTION - ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\", \"DATA\":\"%s\"}}"), uids, card_datas); -#else - ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\"}}"), uids); -#endif - - MqttPublishTeleSensor(); - -#ifdef USE_PN532_CAUSE_EVENTS - - char command[71]; -#ifdef USE_PN532_DATA_FUNCTION - sprintf(command,"backlog event PN532_UID=%s;event PN532_DATA=%s",uids,card_datas); -#else - sprintf(command,"event PN532_UID=%s",uids); -#endif - ExecuteCommand(command, SRC_RULE); -#endif - - pn532_scantimer = 7; - } -} - -#ifdef USE_PN532_DATA_FUNCTION - -bool PN532_Command(void) -{ - bool serviced = true; - uint8_t paramcount = 0; - if (XdrvMailbox.data_len > 0) { - paramcount=1; - } else { - serviced = false; - return serviced; - } - char sub_string[XdrvMailbox.data_len]; - char sub_string_tmp[XdrvMailbox.data_len]; - for (uint32_t ca=0;ca 1) { - if (XdrvMailbox.data[XdrvMailbox.data_len-1] == ',') { - serviced = false; - return serviced; - } - sprintf(sub_string_tmp,subStr(sub_string, XdrvMailbox.data, ",", 2)); - pn532_newdata_len = strlen(sub_string_tmp); - if (pn532_newdata_len > 15) { pn532_newdata_len = 15; } - memcpy(&pn532_newdata,&sub_string_tmp,pn532_newdata_len); - pn532_newdata[pn532_newdata_len] = 0x00; - 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); - ResponseTime_P(PSTR(",\"PN532\":{\"COMMAND\":\"S\"}}")); - return serviced; - } - } -} - -#endif - -bool Xsns40(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_INIT: - PN532_Init(); - result = true; - break; - case FUNC_EVERY_50_MSECOND: - break; - case FUNC_EVERY_100_MSECOND: - break; - case FUNC_EVERY_250_MSECOND: - if (pn532_scantimer > 0) { - pn532_scantimer--; - } else { - PN532_ScanForTag(); - } - break; - case FUNC_EVERY_SECOND: - break; -#ifdef USE_PN532_DATA_FUNCTION - case FUNC_COMMAND_SENSOR: - if (XSNS_40 == XdrvMailbox.index) { - result = PN532_Command(); - } - break; -#endif - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_41_max44009.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_41_max44009.ino" -#ifdef USE_I2C -#ifdef USE_MAX44009 - - - - - - -#define XSNS_41 41 -#define XI2C_28 28 - -#define MAX44009_ADDR1 0x4A -#define MAX44009_ADDR2 0x4B -#define MAX44009_NO_REGISTERS 8 -#define REG_CONFIG 0x02 -#define REG_LUMINANCE 0x03 -#define REG_LOWER_THRESHOLD 0x06 -#define REG_THRESHOLD_TIMER 0x07 - -#define MAX44009_CONTINUOUS_AUTO_MODE 0x80 - -uint8_t max44009_address; -uint8_t max44009_addresses[] = { MAX44009_ADDR1, MAX44009_ADDR2, 0 }; -uint8_t max44009_found = 0; -uint8_t max44009_valid = 0; -float max44009_illuminance = 0; -char max44009_types[] = "MAX44009"; - -bool Max4409Read_lum(void) -{ - max44009_valid = 0; - uint8_t regdata[2]; - - - if (I2cValidRead16((uint16_t *)®data, max44009_address, REG_LUMINANCE)) { - int exponent = (regdata[0] & 0xF0) >> 4; - int mantissa = ((regdata[0] & 0x0F) << 4) | (regdata[1] & 0x0F); - max44009_illuminance = (float)(((0x00000001 << exponent) * (float)mantissa) * 0.045); - max44009_valid = 1; - return true; - } else { - return false; - } -} - - - -void Max4409Detect(void) -{ - uint8_t buffer1; - uint8_t buffer2; - for (uint32_t i = 0; 0 != max44009_addresses[i]; i++) { - - max44009_address = max44009_addresses[i]; - if (I2cActive(max44009_address)) { continue; } - - if ((I2cValidRead8(&buffer1, max44009_address, REG_LOWER_THRESHOLD)) && - (I2cValidRead8(&buffer2, max44009_address, REG_THRESHOLD_TIMER))) { - - if ((0x00 == buffer1) && - (0xFF == buffer2)) { - - - - Wire.beginTransmission(max44009_address); - - - Wire.write(REG_CONFIG); - Wire.write(MAX44009_CONTINUOUS_AUTO_MODE); - if (0 == Wire.endTransmission()) { - I2cSetActiveFound(max44009_address, max44009_types); - max44009_found = 1; - break; - } - } - } - } -} - -void Max4409EverySecond(void) -{ - Max4409Read_lum(); -} - -void Max4409Show(bool json) -{ - char illum_str[8]; - - if (max44009_valid) { - - - - uint8_t prec = 0; - if (10 > max44009_illuminance ) { - prec = 3; - } else if (100 > max44009_illuminance) { - prec = 2; - } else if (1000 > max44009_illuminance) { - prec = 1; - } - dtostrf(max44009_illuminance, sizeof(illum_str) -1, prec, illum_str); - - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_ILLUMINANCE "\":%s}"), max44009_types, illum_str); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_ILLUMINANCE, illum_str); - } -#endif -#ifdef USE_WEBSERVER - } else { - - WSContentSend_PD(HTTP_SNS_ILLUMINANCE, max44009_types, (int)max44009_illuminance); -#endif - } - } -} - - - - - -bool Xsns41(uint8_t function) -{ - if (!I2cEnabled(XI2C_28)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Max4409Detect(); - } - else if (max44009_found) { - switch (function) { - case FUNC_EVERY_SECOND: - Max4409EverySecond(); - break; - case FUNC_JSON_APPEND: - Max4409Show(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Max4409Show(0); - break; - #endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_42_scd30.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_42_scd30.ino" -#ifdef USE_I2C -#ifdef USE_SCD30 - -#define XSNS_42 42 -#define XI2C_29 29 - - - -#define SCD30_ADDRESS 0x61 - -#define SCD30_MAX_MISSED_READS 3 -#define SCD30_STATE_NO_ERROR 0 -#define SCD30_STATE_ERROR_DATA_CRC 1 -#define SCD30_STATE_ERROR_READ_MEAS 2 -#define SCD30_STATE_ERROR_SOFT_RESET 3 -#define SCD30_STATE_ERROR_I2C_RESET 4 -#define SCD30_STATE_ERROR_UNKNOWN 5 - -#include "Arduino.h" -#include - -#define D_CMND_SCD30 "SCD30" - -const char S_JSON_SCD30_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_SCD30 "%s\":%d}"; -const char S_JSON_SCD30_COMMAND_NFW_VALUE[] PROGMEM = "{\"" D_CMND_SCD30 "%s\":%d.%d}"; -const char S_JSON_SCD30_COMMAND[] PROGMEM = "{\"" D_CMND_SCD30 "%s\"}"; -const char kSCD30_Commands[] PROGMEM = "Alt|Auto|Cal|FW|Int|Pres|TOff"; - - - - - -enum SCD30_Commands { - CMND_SCD30_ALTITUDE, - CMND_SCD30_AUTOMODE, - CMND_SCD30_CALIBRATE, - CMND_SCD30_FW, - CMND_SCD30_INTERVAL, - CMND_SCD30_PRESSURE, - CMND_SCD30_TEMPOFFSET -}; - -FrogmoreScd30 scd30; - -bool scd30Found = false; -bool scd30IsDataValid = false; -int scd30ErrorState = SCD30_STATE_NO_ERROR; -uint16_t scd30Interval_sec; -int scd30Loop_count = 0; -int scd30DataNotAvailable_count = 0; -int scd30GoodMeas_count = 0; -int scd30Reset_count = 0; -int scd30CrcError_count = 0; -int scd30Co2Zero_count = 0; -int i2cReset_count = 0; -uint16_t scd30_CO2 = 0; -uint16_t scd30_CO2EAvg = 0; -float scd30_Humid = 0.0; -float scd30_Temp = 0.0; - -void Scd30Detect(void) -{ - if (I2cActive(SCD30_ADDRESS)) { return; } - - scd30.begin(); - - uint8_t major = 0; - uint8_t minor = 0; - if (scd30.getFirmwareVersion(&major, &minor)) { return; } - uint16_t interval_sec; - if (scd30.getMeasurementInterval(&scd30Interval_sec)) { return; } - if (scd30.beginMeasuring()) { return; } - - I2cSetActiveFound(SCD30_ADDRESS, "SCD30"); - scd30Found = true; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SCD: FW v%d.%d"), major, minor); -} - - -void Scd30Update(void) -{ - scd30Loop_count++; - if (scd30Loop_count > (scd30Interval_sec - 1)) { - int error = 0; - switch (scd30ErrorState) { - case SCD30_STATE_NO_ERROR: { - error = scd30.readMeasurement(&scd30_CO2, &scd30_CO2EAvg, &scd30_Temp, &scd30_Humid); - switch (error) { - case ERROR_SCD30_NO_ERROR: - scd30Loop_count = 0; - scd30IsDataValid = true; - scd30GoodMeas_count++; - break; - - case ERROR_SCD30_NO_DATA: - scd30DataNotAvailable_count++; - break; - - case ERROR_SCD30_CRC_ERROR: - scd30ErrorState = SCD30_STATE_ERROR_DATA_CRC; - scd30CrcError_count++; -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: CRC error, CRC error: %ld, CO2 zero: %ld, good: %ld, no data: %ld, sc30_reset: %ld, i2c_reset: %ld"), - scd30CrcError_count, scd30Co2Zero_count, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); -#endif - break; - - case ERROR_SCD30_CO2_ZERO: - scd30Co2Zero_count++; -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: CO2 zero, CRC error: %ld, CO2 zero: %ld, good: %ld, no data: %ld, sc30_reset: %ld, i2c_reset: %ld"), - scd30CrcError_count, scd30Co2Zero_count, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); -#endif - break; - - default: { - scd30ErrorState = SCD30_STATE_ERROR_READ_MEAS; -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: Update: ReadMeasurement error: 0x%lX, counter: %ld"), error, scd30Loop_count); -#endif - return; - } - break; - } - } - break; - - case SCD30_STATE_ERROR_DATA_CRC: { - -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld"), - scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: got CRC error, try again, counter: %ld"), scd30Loop_count); -#endif - scd30ErrorState = ERROR_SCD30_NO_ERROR; - } - break; - - case SCD30_STATE_ERROR_READ_MEAS: { - -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld"), - scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: not answering, sending soft reset, counter: %ld"), scd30Loop_count); -#endif - scd30Reset_count++; - error = scd30.softReset(); - if (error) { -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: resetting got error: 0x%lX"), error); -#endif - error >>= 8; - if (error == 4) { - scd30ErrorState = SCD30_STATE_ERROR_SOFT_RESET; - } else { - scd30ErrorState = SCD30_STATE_ERROR_UNKNOWN; - } - } else { - scd30ErrorState = ERROR_SCD30_NO_ERROR; - } - } - break; - - case SCD30_STATE_ERROR_SOFT_RESET: { - -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld"), - scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); - AddLog_P(LOG_LEVEL_ERROR, PSTR("SCD30: clearing i2c bus")); -#endif - i2cReset_count++; - error = scd30.clearI2CBus(); - if (error) { - scd30ErrorState = SCD30_STATE_ERROR_I2C_RESET; -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: error clearing i2c bus: 0x%lX"), error); -#endif - } else { - scd30ErrorState = ERROR_SCD30_NO_ERROR; - } - } - break; - - default: { - -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: unknown error state: 0x%lX"), scd30ErrorState); -#endif - scd30ErrorState = SCD30_STATE_ERROR_SOFT_RESET; - } - } - - if (scd30Loop_count > (SCD30_MAX_MISSED_READS * scd30Interval_sec)) { - scd30IsDataValid = false; - } - } -} - - -int Scd30GetCommand(int command_code, uint16_t *pvalue) -{ - switch (command_code) - { - case CMND_SCD30_ALTITUDE: - return scd30.getAltitudeCompensation(pvalue); - break; - - case CMND_SCD30_AUTOMODE: - return scd30.getCalibrationType(pvalue); - break; - - case CMND_SCD30_CALIBRATE: - return scd30.getForcedRecalibrationFactor(pvalue); - break; - - case CMND_SCD30_INTERVAL: - return scd30.getMeasurementInterval(pvalue); - break; - - case CMND_SCD30_PRESSURE: - return scd30.getAmbientPressure(pvalue); - break; - - case CMND_SCD30_TEMPOFFSET: - return scd30.getTemperatureOffset(pvalue); - break; - - default: - - break; - } -} - -int Scd30SetCommand(int command_code, uint16_t value) -{ - switch (command_code) - { - case CMND_SCD30_ALTITUDE: - return scd30.setAltitudeCompensation(value); - break; - - case CMND_SCD30_AUTOMODE: - return scd30.setCalibrationType(value); - break; - - case CMND_SCD30_CALIBRATE: - return scd30.setForcedRecalibrationFactor(value); - break; - - case CMND_SCD30_INTERVAL: - { - int error = scd30.setMeasurementInterval(value); - if (!error) - { - scd30Interval_sec = value; - } - - return error; - } - break; - - case CMND_SCD30_PRESSURE: - return scd30.setAmbientPressure(value); - break; - - case CMND_SCD30_TEMPOFFSET: - return scd30.setTemperatureOffset(value); - break; - - default: - - break; - } -} - - - - - -bool Scd30CommandSensor() -{ - char command[CMDSZ]; - bool serviced = true; - uint8_t prefix_len = strlen(D_CMND_SCD30); - - if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_SCD30), prefix_len)) { - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + prefix_len, kSCD30_Commands); - - switch (command_code) { - case CMND_SCD30_ALTITUDE: - case CMND_SCD30_AUTOMODE: - case CMND_SCD30_CALIBRATE: - case CMND_SCD30_INTERVAL: - case CMND_SCD30_PRESSURE: - case CMND_SCD30_TEMPOFFSET: - { - uint16_t value = 0; - if (XdrvMailbox.data_len > 0) - { - value = XdrvMailbox.payload; - Scd30SetCommand(command_code, value); - } - else - { - Scd30GetCommand(command_code, &value); - } - - Response_P(S_JSON_SCD30_COMMAND_NVALUE, command, value); - } - break; - - case CMND_SCD30_FW: - { - uint8_t major = 0; - uint8_t minor = 0; - int error; - error = scd30.getFirmwareVersion(&major, &minor); - if (error) - { -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: error getting FW version: 0x%lX"), error); -#endif - serviced = false; - } - else - { - Response_P(S_JSON_SCD30_COMMAND_NFW_VALUE, command, major, minor); - } - } - break; - - default: - - serviced = false; - break; - } - } - return serviced; -} - -void Scd30Show(bool json) -{ - if (scd30IsDataValid) - { - float t = ConvertTemp(scd30_Temp); - float h = ConvertHumidity(scd30_Humid); - - if (json) { - ResponseAppend_P(PSTR(",\"SCD30\":{\"" D_JSON_CO2 "\":%d,\"" D_JSON_ECO2 "\":%d,"), scd30_CO2, scd30_CO2EAvg); - ResponseAppendTHD(t, h); - ResponseJsonEnd(); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_AIRQUALITY, scd30_CO2); - DomoticzTempHumPressureSensor(t, h); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_CO2EAVG, "SCD30", scd30_CO2EAvg); - WSContentSend_PD(HTTP_SNS_CO2, "SCD30", scd30_CO2); - WSContentSend_THD("SCD30", t, h); -#endif - } - } -} - - - - - -bool Xsns42(byte function) -{ - if (!I2cEnabled(XI2C_29)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Scd30Detect(); - } - else if (scd30Found) { - switch (function) { - case FUNC_EVERY_SECOND: - Scd30Update(); - break; - case FUNC_COMMAND: - result = Scd30CommandSensor(); - break; - case FUNC_JSON_APPEND: - Scd30Show(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Scd30Show(0); - break; - #endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_43_hre.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_43_hre.ino" -#ifdef USE_HRE -# 49 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_43_hre.ino" -#define XSNS_43 43 - -enum hre_states { - hre_idle, - hre_sync, - hre_syncing, - hre_read, - hre_reading, - hre_sleep, - hre_sleeping -}; - -hre_states hre_state = hre_idle; - -float hre_usage = 0; -float hre_rate = 0; -uint32_t hre_usage_time = 0; - -int hre_read_errors = 0; -bool hre_good = false; - - - -int hreReadBit() -{ - digitalWrite(pin[GPIO_HRE_CLOCK], HIGH); - delay(1); - int bit = digitalRead(pin[GPIO_HRE_DATA]); - digitalWrite(pin[GPIO_HRE_CLOCK], LOW); - delay(1); - return bit; -} - - - -char hreReadChar(int &parity_errors) -{ - - hreReadBit(); - - unsigned ch=0; - int sum=0; - for (uint32_t i=0; i<7; i++) - { - int b = hreReadBit(); - ch |= b << i; - sum += b; - } - - - if ( (sum & 0x1) != hreReadBit()) - parity_errors++; - - - hreReadBit(); - - return ch; -} - -void hreInit(void) -{ - hre_read_errors = 0; - hre_good = false; - - pinMode(pin[GPIO_HRE_CLOCK], OUTPUT); - pinMode(pin[GPIO_HRE_DATA], INPUT); - - - - digitalWrite(pin[GPIO_HRE_CLOCK], LOW); - - hre_state = hre_sync; -} - - -void hreEvery50ms(void) -{ - static int sync_counter = 0; - static int sync_run = 0; - - static uint32_t curr_start = 0; - static int read_counter = 0; - static int parity_errors = 0; - static char buff[46]; - - static char ch; - static size_t i; - - switch (hre_state) - { - case hre_sync: - if (uptime < 10) - break; - sync_run = 0; - sync_counter = 0; - hre_state = hre_syncing; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_syncing")); - break; - - case hre_syncing: - - - for (uint32_t i=0; i<20; i++) - { - if (hreReadBit()) - sync_run++; - else - sync_run = 0; - if (sync_run == 62) - { - hre_state = hre_read; - break; - } - sync_counter++; - } - - if (sync_counter > 1000) - { - hre_state = hre_sleep; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE D_ERROR)); - } - break; - - - case hre_read: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "sync_run:%d, sync_counter:%d"), sync_run, sync_counter); - read_counter = 0; - parity_errors = 0; - curr_start = uptime; - memset(buff, 0, sizeof(buff)); - hre_state = hre_reading; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_reading")); - - - - - - case hre_reading: - - buff[read_counter++] = hreReadChar(parity_errors); - buff[read_counter++] = hreReadChar(parity_errors); - - if (read_counter == 46) - { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "pe:%d, re:%d, buff:%s"), - parity_errors, hre_read_errors, buff); - if (parity_errors == 0) - { - float curr_usage; - curr_usage = 0.01 * atol(buff+24); - if (hre_usage_time) - { - double dt = 1.666e-2 * (curr_start - hre_usage_time); - hre_rate = (curr_usage - hre_usage)/dt; - } - hre_usage = curr_usage; - hre_usage_time = curr_start; - hre_good = true; - - hre_state = hre_sleep; - } - else - { - hre_read_errors++; - hre_state = hre_sleep; - } - } - break; - - case hre_sleep: - hre_usage_time = curr_start; - hre_state = hre_sleeping; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_sleeping")); - - case hre_sleeping: - - - if (uptime - hre_usage_time >= 27) - hre_state = hre_sync; - } -} - -void hreShow(boolean json) -{ - if (!hre_good) - return; - - const char *id = "HRE"; - - char usage[16]; - char rate[16]; - dtostrfd(hre_usage, 2, usage); - dtostrfd(hre_rate, 3, rate); - - if (json) - { - ResponseAppend_P(JSON_SNS_GNGPM, id, usage, rate); -#ifdef USE_WEBSERVER - } - else - { - WSContentSend_PD(HTTP_SNS_GALLONS, id, usage); - WSContentSend_PD(HTTP_SNS_GPM, id, rate); -#endif - } -} - - - - - -bool Xsns43(byte function) -{ - - if (pin[GPIO_HRE_CLOCK] >= 99 || pin[GPIO_HRE_DATA] >= 99) - return false; - - switch (function) - { - case FUNC_INIT: - hreInit(); - break; - case FUNC_EVERY_50_MSECOND: - hreEvery50ms(); - break; - case FUNC_EVERY_SECOND: - break; - case FUNC_JSON_APPEND: - hreShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - hreShow(0); - break; -#endif - } - return false; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_44_sps30.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_44_sps30.ino" -#ifdef USE_I2C -#ifdef USE_SPS30 - -#define XSNS_44 44 -#define XI2C_30 30 - -#define SPS30_ADDR 0x69 - -#include -#include - -uint8_t sps30_ready = 0; -uint8_t sps30_running; - -struct SPS30 { - float PM1_0; - float PM2_5; - float PM4_0; - float PM10; - float NCPM0_5; - float NCPM1_0; - float NCPM2_5; - float NCPM4_0; - float NCPM10; - float TYPSIZ; -} sps30_result; - -#define SPS_CMD_START_MEASUREMENT 0x0010 -#define SPS_CMD_START_MEASUREMENT_ARG 0x0300 -#define SPS_CMD_STOP_MEASUREMENT 0x0104 -#define SPS_CMD_READ_MEASUREMENT 0x0300 -#define SPS_CMD_GET_DATA_READY 0x0202 -#define SPS_CMD_AUTOCLEAN_INTERVAL 0x8004 -#define SPS_CMD_CLEAN 0x5607 -#define SPS_CMD_GET_ACODE 0xd025 -#define SPS_CMD_GET_SERIAL 0xd033 -#define SPS_CMD_RESET 0xd304 -#define SPS_WRITE_DELAY_US 20000 -#define SPS_MAX_SERIAL_LEN 32 - -uint8_t sps30_calc_CRC(uint8_t *data) { - uint8_t crc = 0xFF; - for (uint32_t i = 0; i < 2; i++) { - crc ^= data[i]; - for (uint32_t bit = 8; bit > 0; --bit) { - if(crc & 0x80) { - crc = (crc << 1) ^ 0x31u; - } else { - crc = (crc << 1); - } - } - } - return crc; -} - -void CmdClean(void); - -unsigned char twi_readFrom(unsigned char address, unsigned char* buf, unsigned int len, unsigned char sendStop); - -void sps30_get_data(uint16_t cmd, uint8_t *data, uint8_t dlen) { -unsigned char cmdb[2]; -uint8_t tmp[3]; -uint8_t index=0; -memset(data,0,dlen); -uint8_t twi_buff[64]; - - Wire.beginTransmission(SPS30_ADDR); - cmdb[0]=cmd>>8; - cmdb[1]=cmd; - Wire.write(cmdb,2); - Wire.endTransmission(); - - - dlen/=2; - dlen*=3; - - twi_readFrom(SPS30_ADDR,twi_buff,dlen,1); - - uint8_t bind=0; - while (bind>8; - cmdb[1]=cmd; - - if (cmd==SPS_CMD_START_MEASUREMENT) { - cmdb[2]=SPS_CMD_START_MEASUREMENT_ARG>>8; - cmdb[3]=SPS_CMD_START_MEASUREMENT_ARG&0xff; - cmdb[4]=sps30_calc_CRC(&cmdb[2]); - Wire.write(cmdb,5); - } else { - Wire.write(cmdb,2); - } - Wire.endTransmission(); -} - -void SPS30_Detect(void) -{ - if (!I2cSetDevice(SPS30_ADDR)) { return; } - I2cSetActiveFound(SPS30_ADDR, "SPS30"); - - uint8_t dcode[32]; - sps30_get_data(SPS_CMD_GET_SERIAL,dcode,sizeof(dcode)); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("sps30 found with serial: %s"),dcode); - sps30_cmd(SPS_CMD_START_MEASUREMENT); - sps30_running = 1; - sps30_ready = 1; -} - -#define D_UNIT_PM "ug/m3" -#define D_UNIT_NCPM "#/m3" - -#ifdef USE_WEBSERVER -const char HTTP_SNS_SPS30_a[] PROGMEM ="{s}SPS30 " "%s" "{m}%s " D_UNIT_PM "{e}"; -const char HTTP_SNS_SPS30_b[] PROGMEM ="{s}SPS30 " "%s" "{m}%s " D_UNIT_NCPM "{e}"; -const char HTTP_SNS_SPS30_c[] PROGMEM ="{s}SPS30 " "TYPSIZ" "{m}%s " "um" "{e}"; -#endif - -#define PMDP 2 - -#define SPS30_HOURS Settings.sps30_inuse_hours - - - -void SPS30_Every_Second() { - if (!sps30_running) return; - - if (uptime%10==0) { - uint8_t vars[sizeof(float)*10]; - sps30_get_data(SPS_CMD_READ_MEASUREMENT,vars,sizeof(vars)); - float *fp=&sps30_result.PM1_0; - - typedef union { - uint8_t array[4]; - float value; - } ByteToFloat; - - ByteToFloat conv; - - for (uint32_t count=0; count<10; count++) { - for (uint32_t i = 0; i < 4; i++){ - conv.array[3-i] = vars[count*sizeof(float)+i]; - } - *fp++=conv.value; - } - } - - if (uptime%3600==0 && uptime>60) { - - - SPS30_HOURS++; - if (SPS30_HOURS>(7*24)) { - CmdClean(); - SPS30_HOURS=0; - } - } - -} - -void SPS30_Show(bool json) -{ - if (!sps30_running) { return; } - - char str[64]; - if (json) { - dtostrfd(sps30_result.PM1_0,PMDP,str); - ResponseAppend_P(PSTR(",\"SPS30\":{\"" "PM1_0" "\":%s"), str); - dtostrfd(sps30_result.PM2_5,PMDP,str); - ResponseAppend_P(PSTR(",\"" "PM2_5" "\":%s"), str); - dtostrfd(sps30_result.PM4_0,PMDP,str); - ResponseAppend_P(PSTR(",\"" "PM4_0" "\":%s"), str); - dtostrfd(sps30_result.PM10,PMDP,str); - ResponseAppend_P(PSTR(",\"" "PM10" "\":%s"), str); - dtostrfd(sps30_result.NCPM0_5,PMDP,str); - ResponseAppend_P(PSTR(",\"" "NCPM0_5" "\":%s"), str); - dtostrfd(sps30_result.NCPM1_0,PMDP,str); - ResponseAppend_P(PSTR(",\"" "NCPM1_0" "\":%s"), str); - dtostrfd(sps30_result.NCPM2_5,PMDP,str); - ResponseAppend_P(PSTR(",\"" "NCPM2_5" "\":%s"), str); - dtostrfd(sps30_result.NCPM4_0,PMDP,str); - ResponseAppend_P(PSTR(",\"" "NCPM4_0" "\":%s"), str); - dtostrfd(sps30_result.NCPM10,PMDP,str); - ResponseAppend_P(PSTR(",\"" "NCPM10" "\":%s"), str); - dtostrfd(sps30_result.TYPSIZ,PMDP,str); - ResponseAppend_P(PSTR(",\"" "TYPSIZ" "\":%s}"), str); - -#ifdef USE_WEBSERVER - } else { - dtostrfd(sps30_result.PM1_0,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 1.0",str); - dtostrfd(sps30_result.PM2_5,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 2.5",str); - dtostrfd(sps30_result.PM4_0,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 4.0",str); - dtostrfd(sps30_result.PM10,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 10",str); - dtostrfd(sps30_result.NCPM0_5,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 0.5",str); - dtostrfd(sps30_result.NCPM1_0,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 1.0",str); - dtostrfd(sps30_result.NCPM2_5,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 2.5",str); - dtostrfd(sps30_result.NCPM4_0,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 4.0",str); - dtostrfd(sps30_result.NCPM10,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 10",str); - dtostrfd(sps30_result.TYPSIZ,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_c,str); -#endif - } -} - -void CmdClean(void) -{ - sps30_cmd(SPS_CMD_CLEAN); - ResponseTime_P(PSTR(",\"SPS30\":{\"CFAN\":\"true\"}}")); - MqttPublishTeleSensor(); -} - -bool SPS30_cmd(void) -{ - bool serviced = true; - if (XdrvMailbox.data_len > 0) { - char *cp=XdrvMailbox.data; - if (*cp=='c') { - - CmdClean(); - } else if (*cp=='0' || *cp=='1') { - sps30_running=*cp&1; - sps30_cmd(sps30_running?SPS_CMD_START_MEASUREMENT:SPS_CMD_STOP_MEASUREMENT); - } else { - serviced=false; - } - } - Response_P(PSTR("{\"SPS30\":\"%s\"}"), sps30_running?"running":"stopped"); - - return serviced; -} - - - - - -bool Xsns44(byte function) -{ - if (!I2cEnabled(XI2C_30)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - SPS30_Detect(); - } - else if (sps30_ready) { - switch (function) { - case FUNC_EVERY_SECOND: - SPS30_Every_Second(); - break; - case FUNC_JSON_APPEND: - SPS30_Show(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - SPS30_Show(0); - break; - #endif - case FUNC_COMMAND_SENSOR: - if (XSNS_44 == XdrvMailbox.index) { - result = SPS30_cmd(); - } - break; - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_45_vl53l0x.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_45_vl53l0x.ino" -#ifdef USE_I2C -#ifdef USE_VL53L0X - -#define XSNS_45 45 -#define XI2C_31 31 - -#include -#include "VL53L0X.h" -VL53L0X sensor; - -uint8_t vl53l0x_ready = 0; -uint16_t vl53l0x_distance; -uint16_t Vl53l0_buffer[5]; -uint8_t Vl53l0_index; - - - -void Vl53l0Detect(void) -{ - if (!I2cSetDevice(0x29)) { return; } - - if (!sensor.init()) { return; } - - I2cSetActiveFound(sensor.getAddress(), "VL53L0X"); - - sensor.setTimeout(500); - - - - - - sensor.startContinuous(); - vl53l0x_ready = 1; - - Vl53l0_index=0; -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_VL53L0X[] PROGMEM = - "{s}VL53L0X " D_DISTANCE "{m}%d" D_UNIT_MILLIMETER "{e}"; -#endif - -#define USE_VL_MEDIAN - -void Vl53l0Every_250MSecond(void) -{ - uint16_t tbuff[5],tmp; - uint8_t flag; - - - uint16_t dist = sensor.readRangeContinuousMillimeters(); - if (dist==0 || dist>2000) { - dist=9999; - } - -#ifdef USE_VL_MEDIAN - - Vl53l0_buffer[Vl53l0_index]=dist; - Vl53l0_index++; - if (Vl53l0_index>=5) Vl53l0_index=0; - - - memmove(tbuff,Vl53l0_buffer,sizeof(tbuff)); - for (byte ocnt=0; ocnt<5; ocnt++) { - flag=0; - for (byte count=0; count<4; count++) { - if (tbuff[count]>tbuff[count+1]) { - tmp=tbuff[count]; - tbuff[count]=tbuff[count+1]; - tbuff[count+1]=tmp; - flag=1; - } - } - if (!flag) break; - } - vl53l0x_distance=tbuff[2]; -#else - vl53l0x_distance=dist; -#endif -} - -void Vl53l0Show(boolean json) -{ - if (json) { - ResponseAppend_P(PSTR(",\"VL53L0X\":{\"" D_JSON_DISTANCE "\":%d}"), vl53l0x_distance); -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_VL53L0X, vl53l0x_distance); -#endif - } -} - - - - - -bool Xsns45(byte function) -{ - if (!I2cEnabled(XI2C_31)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Vl53l0Detect(); - } - else if (vl53l0x_ready) { - switch (function) { - case FUNC_EVERY_250_MSECOND: - Vl53l0Every_250MSecond(); - break; - case FUNC_JSON_APPEND: - Vl53l0Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Vl53l0Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_46_MLX90614.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_46_MLX90614.ino" -#ifdef USE_I2C -#ifdef USE_MLX90614 - -#define XSNS_46 46 -#define XI2C_32 32 - -#define I2_ADR_IRT 0x5a - -#define MLX90614_RAWIR1 0x04 -#define MLX90614_RAWIR2 0x05 -#define MLX90614_TA 0x06 -#define MLX90614_TOBJ1 0x07 -#define MLX90614_TOBJ2 0x08 - -struct { - union { - uint16_t value; - uint32_t i2c_buf; - }; - float obj_temp; - float amb_temp; - bool ready = false; -} mlx90614; - -void MLX90614_Init(void) -{ - if (!I2cSetDevice(I2_ADR_IRT)) { return; } - I2cSetActiveFound(I2_ADR_IRT, "MLX90614"); - mlx90614.ready = true; -} - -void MLX90614_Every_Second(void) -{ - mlx90614.i2c_buf = I2cRead24(I2_ADR_IRT, MLX90614_TOBJ1); - if (mlx90614.value & 0x8000) { - mlx90614.obj_temp = -999; - } else { - mlx90614.obj_temp = ((float)mlx90614.value * 0.02) - 273.15; - } - mlx90614.i2c_buf = I2cRead24(I2_ADR_IRT,MLX90614_TA); - if (mlx90614.value & 0x8000) { - mlx90614.amb_temp = -999; - } else { - mlx90614.amb_temp = ((float)mlx90614.value * 0.02) - 273.15; - } -} - -#ifdef USE_WEBSERVER - const char HTTP_IRTMP[] PROGMEM = - "{s}MXL90614 " "OBJ-" D_TEMPERATURE "{m}%s C" "{e}" - "{s}MXL90614 " "AMB-" D_TEMPERATURE "{m}%s C" "{e}"; -#endif - -void MLX90614_Show(uint8_t json) -{ - char obj_tstr[16]; - dtostrfd(mlx90614.obj_temp, Settings.flag2.temperature_resolution, obj_tstr); - char amb_tstr[16]; - dtostrfd(mlx90614.amb_temp, Settings.flag2.temperature_resolution, amb_tstr); - - if (json) { - ResponseAppend_P(PSTR(",\"MLX90614\":{\"OBJTMP\":%s,\"AMBTMP\":%s}"), obj_tstr, amb_tstr); -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_IRTMP, obj_tstr, amb_tstr); -#endif - } -} - - - - - -bool Xsns46(byte function) -{ - if (!I2cEnabled(XI2C_32)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - MLX90614_Init(); - } - else if (mlx90614.ready) { - switch (function) { - case FUNC_EVERY_SECOND: - MLX90614_Every_Second(); - break; - case FUNC_JSON_APPEND: - MLX90614_Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - MLX90614_Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_47_max31865.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_47_max31865.ino" -#ifdef USE_MAX31865 - -#ifndef USE_SPI -#error "MAX31865 requires USE_SPI enabled" -#endif - -#include "Adafruit_MAX31865.h" - -#define XSNS_47 47 - -#if MAX31865_PTD_WIRES == 4 - #define PTD_WIRES MAX31865_4WIRE -#elif MAX31865_PTD_WIRES == 3 - #define PTD_WIRES MAX31865_3WIRE -#else - #define PTD_WIRES MAX31865_2WIRE -#endif - -int8_t init_status = 0; - -Adafruit_MAX31865 max31865; - -struct MAX31865_Result_Struct { - uint8_t ErrorCode; - uint16_t Rtd; - float PtdResistance; - float PtdTemp; -} MAX31865_Result; - -void MAX31865_Init(void){ - if(init_status) - return; - - max31865.setPins( - pin[GPIO_SSPI_CS], - pin[GPIO_SSPI_MOSI], - pin[GPIO_SSPI_MISO], - pin[GPIO_SSPI_SCLK] - ); - - if(max31865.begin(PTD_WIRES)) - init_status = 1; - else - init_status = -1; -} - - - - - -void MAX31865_GetResult(void){ - uint16_t rtd; - - rtd = max31865.readRTD(); - MAX31865_Result.Rtd = rtd; - MAX31865_Result.PtdResistance = max31865.rtd_to_resistance(rtd, MAX31865_REF_RES); - MAX31865_Result.PtdTemp = max31865.rtd_to_temperature(rtd, MAX31865_PTD_RES, MAX31865_REF_RES) + MAX31865_PTD_BIAS; -} - -void MAX31865_Show(bool Json){ - char temperature[33]; - char resistance[33]; - - dtostrfd(MAX31865_Result.PtdResistance, Settings.flag2.temperature_resolution, resistance); - dtostrfd(MAX31865_Result.PtdTemp, Settings.flag2.temperature_resolution, temperature); - - if(Json){ - ResponseAppend_P(PSTR(",\"MAX31865\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_RESISTANCE "\":%s,\"" D_JSON_ERROR "\":%d}"), \ - temperature, resistance, MAX31865_Result.ErrorCode); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_TEMP, temperature); - } -#endif -#ifdef USE_KNX - if (0 == tele_period) { - KnxSensor(KNX_TEMPERATURE, MAX31865_Result.PtdTemp); - } -#endif - } else { -#ifdef USE_WEBSERVER - WSContentSend_PD(HTTP_SNS_TEMP, "MAX31865", temperature, TempUnit()); -#endif - } -} - - - - - -bool Xsns47(uint8_t function) -{ - bool result = false; - if((pin[GPIO_SSPI_MISO] < 99) && (pin[GPIO_SSPI_MOSI] < 99) && - (pin[GPIO_SSPI_SCLK] < 99) && (pin[GPIO_SSPI_CS] < 99)) { - - switch (function) { - case FUNC_INIT: - MAX31865_Init(); - break; - - case FUNC_EVERY_SECOND: - MAX31865_GetResult(); - break; - - case FUNC_JSON_APPEND: - MAX31865_Show(true); - break; - -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - MAX31865_Show(false); - break; -#endif - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_48_chirp.ino" -# 35 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_48_chirp.ino" -#ifdef USE_I2C -#ifdef USE_CHIRP -# 47 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_48_chirp.ino" -#define XSNS_48 48 -#define XI2C_33 33 - -#define CHIRP_MAX_SENSOR_COUNT 3 - -#define CHIRP_ADDR_STANDARD 0x20 - - - - - -#define D_CMND_CHIRP "CHIRP" - -const char S_JSON_CHIRP_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_CHIRP "%s\":%d}"; -const char S_JSON_CHIRP_COMMAND[] PROGMEM = "{\"" D_CMND_CHIRP "%s\"}"; -const char kCHIRP_Commands[] PROGMEM = "Select|Set|Scan|Reset|Sleep|Wake"; - -const char kChirpTypes[] PROGMEM = "CHIRP"; - - - - - -enum CHIRP_Commands { - CMND_CHIRP_SELECT, - CMND_CHIRP_SET, - CMND_CHIRP_SCAN, - CMND_CHIRP_RESET, - CMND_CHIRP_SLEEP, - CMND_CHIRP_WAKE }; - - - - - - -#define CHIRP_GET_CAPACITANCE 0x00 -#define CHIRP_SET_ADDRESS 0x01 -#define CHIRP_GET_ADDRESS 0x02 -#define CHIRP_MEASURE_LIGHT 0x03 -#define CHIRP_GET_LIGHT 0x04 -#define CHIRP_GET_TEMPERATURE 0x05 -#define CHIRP_RESET 0x06 -#define CHIRP_GET_VERSION 0x07 -#define CHIRP_SLEEP 0x08 -#define CHIRP_GET_BUSY 0x09 - - - - - -void ChirpWriteI2CRegister(uint8_t addr, uint8_t reg) { - Wire.beginTransmission(addr); - Wire.write(reg); - Wire.endTransmission(); -} - -uint16_t ChirpFinishReadI2CRegister16bit(uint8_t addr) { - Wire.requestFrom(addr,(uint8_t)2); - uint16_t t = Wire.read() << 8; - t = t | Wire.read(); - return t; -} - - - - - -uint8_t chirp_current = 0; -uint8_t chirp_found_sensors = 0; - -char chirp_name[7]; -uint8_t chirp_next_job = 0; -uint32_t chirp_timeout_count = 0; - -#pragma pack(1) -struct ChirpSensor_t{ - uint16_t moisture = 0; - uint16_t light = 0; - int16_t temperature = 0; - uint8_t version = 0; - uint8_t address:7; - uint8_t explicitSleep:1; -}; -#pragma pack() - -ChirpSensor_t chirp_sensor[CHIRP_MAX_SENSOR_COUNT]; - - - -void ChirpReset(uint8_t addr) { - ChirpWriteI2CRegister(addr, CHIRP_RESET); -} - - - -void ChirpResetAll(void) { - for (uint32_t i = 0; i < chirp_found_sensors; i++) { - if (chirp_sensor[i].version) { - ChirpReset(chirp_sensor[i].address); - } - } -} - - -void ChirpClockSet() { - Wire.setClockStretchLimit(4000); - Wire.setClock(50000); -} - - - -void ChirpSleep(uint8_t addr) { - ChirpWriteI2CRegister(addr, CHIRP_SLEEP); -} -# 185 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_48_chirp.ino" -void ChirpSelect(uint8_t sensor) { - if(sensor < chirp_found_sensors) { - chirp_current = sensor; - DEBUG_SENSOR_LOG(PSTR("CHIRP: Sensor %u now active."), chirp_current); - } - if (sensor == 255) { - DEBUG_SENSOR_LOG(PSTR("CHIRP: Sensor %u active at address 0x%x."), chirp_current, chirp_sensor[chirp_current].address); - } -} - - - -uint8_t ChirpReadVersion(uint8_t addr) { - return (I2cRead8(addr, CHIRP_GET_VERSION)); -} - - - -bool ChirpSet(uint8_t addr) { - if(addr < 128){ - if (I2cWrite8(chirp_sensor[chirp_current].address, CHIRP_SET_ADDRESS, addr)){ - if(chirp_sensor[chirp_current].version>0x25 && chirp_sensor[chirp_current].version != 255){ - delay(5); - I2cWrite8(chirp_sensor[chirp_current].address, CHIRP_SET_ADDRESS, addr); - - } - DEBUG_SENSOR_LOG(PSTR("CHIRP: Wrote adress %u "), addr); - ChirpReset(chirp_sensor[chirp_current].address); - chirp_sensor[chirp_current].address = addr; - chirp_timeout_count = 10; - chirp_next_job = 0; - if(chirp_sensor[chirp_current].version == 255){ - AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: wrote new address %u, please power off device"), addr); - chirp_sensor[chirp_current].version == 0; - } - return true; - } - } - AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: address %u incorrect and not used"), addr); - return false; -} - - - -bool ChirpScan() -{ - ChirpClockSet(); - chirp_found_sensors = 0; - for (uint8_t address = 1; address <= 127; address++) { - chirp_sensor[chirp_found_sensors].version = 0; - chirp_sensor[chirp_found_sensors].version = ChirpReadVersion(address); - delay(2); - chirp_sensor[chirp_found_sensors].version = ChirpReadVersion(address); - if (chirp_sensor[chirp_found_sensors].version > 0) { - I2cSetActiveFound(address, "CHIRP"); - if (chirp_found_sensors 0); -} - - - -void ChirpDetect(void) -{ - if (chirp_next_job > 0) { return; } - - DEBUG_SENSOR_LOG(PSTR("CHIRP: scan will start ...")); - if (ChirpScan()) { - uint8_t chirp_model = 0; - GetTextIndexed(chirp_name, sizeof(chirp_name), chirp_model, kChirpTypes); - } -} - - -void ChirpServiceAllSensors(uint8_t job){ - for (uint32_t i = 0; i < chirp_found_sensors; i++) { - if (chirp_sensor[i].version && !chirp_sensor[i].explicitSleep) { - DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare for sensor at address 0x%x"), chirp_sensor[i].address); - switch(job){ - case 0: - ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_CAPACITANCE); - break; - case 1: - chirp_sensor[i].moisture = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address); - break; - case 2: - ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_TEMPERATURE); - break; - case 3: - chirp_sensor[i].temperature = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address); - break; - case 4: - ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_MEASURE_LIGHT); - break; - case 5: - ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_LIGHT); - break; - case 6: - chirp_sensor[i].light = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address); - break; - default: - break; - } - } - } -} - - - -void ChirpEvery100MSecond(void) -{ - - if(chirp_timeout_count == 0) { - switch(chirp_next_job) { - case 0: - DEBUG_SENSOR_LOG(PSTR("CHIRP: reset all")); - ChirpResetAll(); - chirp_timeout_count = 10; - chirp_next_job++; - break; - case 1: - - - chirp_next_job++; - break; - case 2: - DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare moisture read")); - ChirpServiceAllSensors(0); - chirp_timeout_count = 11; - chirp_next_job++; - break; - case 3: - DEBUG_SENSOR_LOG(PSTR("CHIRP: finish moisture read")); - ChirpServiceAllSensors(1); - chirp_next_job++; - break; - case 4: - DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare moisture read - 2nd")); - ChirpServiceAllSensors(0); - chirp_timeout_count = 11; - chirp_next_job++; - break; - case 5: - DEBUG_SENSOR_LOG(PSTR("CHIRP: finish moisture read - 2nd")); - ChirpServiceAllSensors(1); - chirp_next_job++; - break; - case 6: - DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare temperature read")); - ChirpServiceAllSensors(2); - chirp_timeout_count = 11; - chirp_next_job++; - break; - case 7: - DEBUG_SENSOR_LOG(PSTR("CHIRP: finish temperature read")); - ChirpServiceAllSensors(3); - chirp_next_job++; - break; - case 8: - DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare temperature read - 2nd")); - ChirpServiceAllSensors(2); - chirp_timeout_count = 11; - chirp_next_job++; - break; - case 9: - DEBUG_SENSOR_LOG(PSTR("CHIRP: finish temperature read - 2nd")); - ChirpServiceAllSensors(3); - chirp_next_job++; - break; - case 10: - DEBUG_SENSOR_LOG(PSTR("CHIRP: start light measure process")); - ChirpServiceAllSensors(4); - chirp_timeout_count = 90; - chirp_next_job++; - break; - case 11: - DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare light read")); - ChirpServiceAllSensors(5); - chirp_timeout_count = 11; - chirp_next_job++; - break; - case 12: - DEBUG_SENSOR_LOG(PSTR("CHIRP: finish light read")); - ChirpServiceAllSensors(6); - chirp_next_job++; - break; - case 13: - DEBUG_SENSOR_LOG(PSTR("CHIRP: paused, waiting for TELE")); - break; - case 14: - if (Settings.tele_period > 16){ - chirp_timeout_count = (Settings.tele_period - 17) * 10; - DEBUG_SENSOR_LOG(PSTR("CHIRP: timeout 1/10 sec: %u, tele: %u"), chirp_timeout_count, Settings.tele_period); - } - else{ - AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: TELEPERIOD must be > 16 seconds !")); - - } - chirp_next_job = 1; - break; - } - } - else { - chirp_timeout_count--; - } -} - - - - -#ifdef USE_WEBSERVER - - -const char HTTP_SNS_DARKNESS[] PROGMEM = "{s} " D_JSON_DARKNESS "{m}%s %%{e}"; -const char HTTP_SNS_CHIRPVER[] PROGMEM = "{s} CHIRP-sensor %u at address{m}0x%x{e}" - "{s} FW-version{m}%s {e}"; ; -const char HTTP_SNS_CHIRPSLEEP[] PROGMEM = "{s} {m} is sleeping ...{e}"; -#endif - - - -void ChirpShow(bool json) -{ - for (uint32_t i = 0; i < chirp_found_sensors; i++) { - if (chirp_sensor[i].version) { - - char str_temperature[33]; - double t_temperature = ((double) chirp_sensor[i].temperature )/10.0; - dtostrfd(t_temperature, Settings.flag2.temperature_resolution, str_temperature); - char str_light[33]; - dtostrfd(chirp_sensor[i].light, 0, str_light); - char str_version[7]; - if(chirp_sensor[i].version == 0xff){ - strncpy_P(str_version, PSTR("Chirp!"), sizeof(str_version)); - } - else{ - sprintf(str_version, "%x", chirp_sensor[i].version); - } - - if (json) { - if(!chirp_sensor[i].explicitSleep) { - ResponseAppend_P(PSTR(",\"%s%u\":{\"" D_JSON_MOISTURE "\":%d"), chirp_name, i, chirp_sensor[i].moisture); - if(chirp_sensor[i].temperature!=-1){ - ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s"),str_temperature); - } - ResponseAppend_P(PSTR(",\"" D_JSON_DARKNESS "\":%s}"),str_light); - } - else { - ResponseAppend_P(PSTR(",\"%s%u\":{\"sleeping\"}"),chirp_name, i); - } - #ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzTempHumPressureSensor(t_temperature, chirp_sensor[i].moisture); - DomoticzSensor(DZ_ILLUMINANCE,chirp_sensor[i].light); - } - #endif - #ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_CHIRPVER, i, chirp_sensor[i].address, str_version); - if (chirp_sensor[i].explicitSleep){ - WSContentSend_PD(HTTP_SNS_CHIRPSLEEP); - } - else { - WSContentSend_PD(HTTP_SNS_MOISTURE, "", chirp_sensor[i].moisture); - WSContentSend_PD(HTTP_SNS_DARKNESS, str_light); - if (chirp_sensor[i].temperature!=-1) { - WSContentSend_PD(HTTP_SNS_TEMP, "", str_temperature, TempUnit()); - } - } - - #endif - } - } - } -} - - - - - -bool ChirpCmd(void) { - char command[CMDSZ]; - bool serviced = true; - uint8_t disp_len = strlen(D_CMND_CHIRP); - - if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_CHIRP), disp_len)) { - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kCHIRP_Commands); - - switch (command_code) { - case CMND_CHIRP_SELECT: - case CMND_CHIRP_SET: - if (XdrvMailbox.data_len > 0) { - if (command_code == CMND_CHIRP_SELECT) { ChirpSelect(XdrvMailbox.payload); } - if (command_code == CMND_CHIRP_SET) { ChirpSet((uint8_t)XdrvMailbox.payload); } - Response_P(S_JSON_CHIRP_COMMAND_NVALUE, command, XdrvMailbox.payload); - } - else { - if (command_code == CMND_CHIRP_SELECT) { ChirpSelect(255); } - Response_P(S_JSON_CHIRP_COMMAND, command, XdrvMailbox.payload); - } - break; - case CMND_CHIRP_SCAN: - case CMND_CHIRP_SLEEP: - case CMND_CHIRP_WAKE: - case CMND_CHIRP_RESET: - if (command_code == CMND_CHIRP_SCAN) { chirp_next_job = 0; - ChirpDetect(); } - if (command_code == CMND_CHIRP_SLEEP) { chirp_sensor[chirp_current].explicitSleep = true; - ChirpSleep(chirp_sensor[chirp_current].address); } - if (command_code == CMND_CHIRP_WAKE) { chirp_sensor[chirp_current].explicitSleep = false; - ChirpReadVersion(chirp_sensor[chirp_current].address); } - if (command_code == CMND_CHIRP_RESET) { ChirpReset(chirp_sensor[chirp_current].address); } - Response_P(S_JSON_CHIRP_COMMAND, command, XdrvMailbox.payload); - break; - default: - - serviced = false; - break; - } - } - return serviced; -} - - - - - -bool Xsns48(uint8_t function) -{ - if (!I2cEnabled(XI2C_33)) { return false; } - - bool result = false; - - switch (function) { - case FUNC_EVERY_100_MSECOND: - if(chirp_found_sensors > 0){ - ChirpEvery100MSecond(); - } - break; - case FUNC_COMMAND: - result = ChirpCmd(); - break; - case FUNC_JSON_APPEND: - ChirpShow(1); - chirp_next_job = 14; - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - ChirpShow(0); - break; -#endif - case FUNC_INIT: - ChirpDetect(); - break; - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_50_paj7620.ino" -# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_50_paj7620.ino" -#ifdef USE_I2C -#ifdef USE_PAJ7620 - - - - - - -#define XSNS_50 50 -#define XI2C_34 34 - -#define PAJ7620_ADDR 0x73 - -#define PAJ7620_BANK_SEL 0xEF - - - -#define PAJ7620_GET_GESTURE 0x43 -#define PAJ7620_PROXIMITY_AVG_Y 0x6c - -#define PAJ7620_OBJECT_CENTER_X 0xad -#define PAJ7620_OBJECT_CENTER_Y 0xaf - -#define PAJ7620_DOWN 1 -#define PAJ7620_UP 2 -#define PAJ7620_RIGHT 4 -#define PAJ7620_LEFT 8 -#define PAJ7620_NEAR 16 -#define PAJ7620_FAR 32 -#define PAJ7620_CW 64 -#define PAJ7620_CCW 128 - - - - -const uint8_t PAJ7620initRegisterArray[][2] PROGMEM = { - {0xEF,0x00}, - {0x32,0x29}, {0x33,0x01}, {0x34,0x00}, {0x35,0x01}, {0x36,0x00}, {0x37,0x07}, {0x38,0x17}, {0x39,0x06}, - {0x3A,0x12}, {0x3F,0x00}, {0x40,0x02}, {0x41,0xFF}, {0x42,0x01}, {0x46,0x2D}, {0x47,0x0F}, {0x48,0x3C}, - {0x49,0x00}, {0x4A,0x1E}, {0x4B,0x00}, {0x4C,0x20}, {0x4D,0x00}, {0x4E,0x1A}, {0x4F,0x14}, {0x50,0x00}, - {0x51,0x10}, {0x52,0x00}, {0x5C,0x02}, {0x5D,0x00}, {0x5E,0x10}, {0x5F,0x3F}, {0x60,0x27}, {0x61,0x28}, - {0x62,0x00}, {0x63,0x03}, {0x64,0xF7}, {0x65,0x03}, {0x66,0xD9}, {0x67,0x03}, {0x68,0x01}, {0x69,0xC8}, - {0x6A,0x40}, {0x6D,0x04}, {0x6E,0x00}, {0x6F,0x00}, {0x70,0x80}, {0x71,0x00}, {0x72,0x00}, {0x73,0x00}, - {0x74,0xF0}, {0x75,0x00}, {0x80,0x42}, {0x81,0x44}, {0x82,0x04}, {0x83,0x20}, {0x84,0x20}, {0x85,0x00}, - {0x86,0x10}, {0x87,0x00}, {0x88,0x05}, {0x89,0x18}, {0x8A,0x10}, {0x8B,0x01}, {0x8C,0x37}, {0x8D,0x00}, - {0x8E,0xF0}, {0x8F,0x81}, {0x90,0x06}, {0x91,0x06}, {0x92,0x1E}, {0x93,0x0D}, {0x94,0x0A}, {0x95,0x0A}, - {0x96,0x0C}, {0x97,0x05}, {0x98,0x0A}, {0x99,0x41}, {0x9A,0x14}, {0x9B,0x0A}, {0x9C,0x3F}, {0x9D,0x33}, - {0x9E,0xAE}, {0x9F,0xF9}, {0xA0,0x48}, {0xA1,0x13}, {0xA2,0x10}, {0xA3,0x08}, {0xA4,0x30}, {0xA5,0x19}, - {0xA6,0x10}, {0xA7,0x08}, {0xA8,0x24}, {0xA9,0x04}, {0xAA,0x1E}, {0xAB,0x1E}, {0xCC,0x19}, {0xCD,0x0B}, - {0xCE,0x13}, {0xCF,0x64}, {0xD0,0x21}, {0xD1,0x0F}, {0xD2,0x88}, {0xE0,0x01}, {0xE1,0x04}, {0xE2,0x41}, - {0xE3,0xD6}, {0xE4,0x00}, {0xE5,0x0C}, {0xE6,0x0A}, {0xE7,0x00}, {0xE8,0x00}, {0xE9,0x00}, {0xEE,0x07}, - {0xEF,0x01}, - {0x00,0x1E}, {0x01,0x1E}, {0x02,0x0F}, {0x03,0x10}, {0x04,0x02}, {0x05,0x00}, {0x06,0xB0}, {0x07,0x04}, - {0x08,0x0D}, {0x09,0x0E}, {0x0A,0x9C}, {0x0B,0x04}, {0x0C,0x05}, {0x0D,0x0F}, {0x0E,0x02}, {0x0F,0x12}, - {0x10,0x02}, {0x11,0x02}, {0x12,0x00}, {0x13,0x01}, {0x14,0x05}, {0x15,0x07}, {0x16,0x05}, {0x17,0x07}, - {0x18,0x01}, {0x19,0x04}, {0x1A,0x05}, {0x1B,0x0C}, {0x1C,0x2A}, {0x1D,0x01}, {0x1E,0x00}, {0x21,0x00}, - {0x22,0x00}, {0x23,0x00}, {0x25,0x01}, {0x26,0x00}, {0x27,0x39}, {0x28,0x7F}, {0x29,0x08}, {0x30,0x03}, - {0x31,0x00}, {0x32,0x1A}, {0x33,0x1A}, {0x34,0x07}, {0x35,0x07}, {0x36,0x01}, {0x37,0xFF}, {0x38,0x36}, - {0x39,0x07}, {0x3A,0x00}, {0x3E,0xFF}, {0x3F,0x00}, {0x40,0x77}, {0x41,0x40}, {0x42,0x00}, {0x43,0x30}, - {0x44,0xA0}, {0x45,0x5C}, {0x46,0x00}, {0x47,0x00}, {0x48,0x58}, {0x4A,0x1E}, {0x4B,0x1E}, {0x4C,0x00}, - {0x4D,0x00}, {0x4E,0xA0}, {0x4F,0x80}, {0x50,0x00}, {0x51,0x00}, {0x52,0x00}, {0x53,0x00}, {0x54,0x00}, - {0x57,0x80}, {0x59,0x10}, {0x5A,0x08}, {0x5B,0x94}, {0x5C,0xE8}, {0x5D,0x08}, {0x5E,0x3D}, {0x5F,0x99}, - {0x60,0x45}, {0x61,0x40}, {0x63,0x2D}, {0x64,0x02}, {0x65,0x96}, {0x66,0x00}, {0x67,0x97}, {0x68,0x01}, - {0x69,0xCD}, {0x6A,0x01}, {0x6B,0xB0}, {0x6C,0x04}, {0x6D,0x2C}, {0x6E,0x01}, {0x6F,0x32}, {0x71,0x00}, - {0x72,0x01}, {0x73,0x35}, {0x74,0x00}, {0x75,0x33}, {0x76,0x31}, {0x77,0x01}, {0x7C,0x84}, {0x7D,0x03}, - {0x7E,0x01}, - {0xEF,0x00} -}; - - - - - -const char kPaj7620Directions[] PROGMEM = "Down|Up|Right|Left|Near|Far|CW|CCW"; - -const uint8_t PAJ7620_PIN[]= {1,2,3,4}; - - - - - -char PAJ7620_name[] = "PAJ7620"; - -uint32_t PAJ7620_timeout_counter = 10; -uint32_t PAJ7620_next_job = 0; -uint32_t PAJ7620_mode = 1; - -struct { - uint8_t current; - uint8_t last; - uint8_t same; - uint8_t unfinished; -} PAJ7620_gesture; - -bool PAJ7620_finished_gesture = false; -char PAJ7620_currentGestureName[6]; - -struct{ - uint8_t x; - uint8_t y; - uint8_t last_x; - uint8_t last_y; - uint8_t proximity; - uint8_t last_proximity; - uint8_t corner; - struct { - uint8_t step:3; - uint8_t countdown:3; - uint8_t valid:1; - } PIN; -} PAJ7620_state; - - - - - -void PAJ7620SelectBank(uint8_t bank) -{ - I2cWrite(PAJ7620_ADDR, PAJ7620_BANK_SEL, bank &1, 1); -} - - - -void PAJ7620DecodeGesture(void) -{ - uint32_t index = 0; - switch (PAJ7620_gesture.current) { - case PAJ7620_LEFT: - index++; - case PAJ7620_RIGHT: - index++; - case PAJ7620_UP: - index++; - case PAJ7620_DOWN: - if (PAJ7620_gesture.unfinished) { - PAJ7620_finished_gesture = true; - break; - } - PAJ7620_gesture.unfinished = PAJ7620_gesture.current; - PAJ7620_timeout_counter = 5; - break; - case PAJ7620_NEAR: - index = 4; - PAJ7620_finished_gesture = true; - PAJ7620_timeout_counter = 25; - break; - case PAJ7620_FAR: - index = 5; - PAJ7620_finished_gesture = true; - PAJ7620_timeout_counter = 25; - break; - case PAJ7620_CW: - index = 6; - PAJ7620_finished_gesture = true; - break; - case PAJ7620_CCW: - index = 7; - PAJ7620_finished_gesture = true; - break; - default: - index = 8; - if (PAJ7620_gesture.unfinished) { - PAJ7620_finished_gesture = true; - } - break; - } - if (index < 8) { - GetTextIndexed(PAJ7620_currentGestureName, sizeof(PAJ7620_currentGestureName), index, kPaj7620Directions); - } - - if (PAJ7620_finished_gesture) { - if (PAJ7620_gesture.unfinished) { - if ((PAJ7620_gesture.current != PAJ7620_NEAR) && (PAJ7620_gesture.current != PAJ7620_FAR)) { - PAJ7620_gesture.current = PAJ7620_gesture.unfinished; - } - } - if (PAJ7620_gesture.current == PAJ7620_gesture.last) { - PAJ7620_gesture.same++; - } else { - PAJ7620_gesture.same = 1; - } - PAJ7620_gesture.last = PAJ7620_gesture.current; - PAJ7620_finished_gesture = false; - PAJ7620_gesture.unfinished = 0; - PAJ7620_timeout_counter += 3; - MqttPublishSensor(); - } -} - - - -void PAJ7620ReadGesture(void) -{ - switch (PAJ7620_mode) { - case 1: - PAJ7620_gesture.current = I2cRead8(PAJ7620_ADDR,PAJ7620_GET_GESTURE); - if ((PAJ7620_gesture.current > 0) || PAJ7620_gesture.unfinished) { - DEBUG_SENSOR_LOG(PSTR("PAJ: gesture: %u"), PAJ7620_gesture.current); - PAJ7620DecodeGesture(); - } - break; - case 2: - PAJ7620_state.proximity = I2cRead8(PAJ7620_ADDR, PAJ7620_PROXIMITY_AVG_Y); - if ((PAJ7620_state.proximity > 0) || (PAJ7620_state.last_proximity > 0)) { - if (PAJ7620_state.proximity != PAJ7620_state.last_proximity) { - PAJ7620_state.last_proximity = PAJ7620_state.proximity; - DEBUG_SENSOR_LOG(PSTR("PAJ: Proximity: %u"), PAJ7620_state.proximity); - MqttPublishSensor(); - } - } - break; - case 3: - case 4: - case 5: - PAJ7620_state.x = I2cRead8(PAJ7620_ADDR, PAJ7620_OBJECT_CENTER_X); - PAJ7620_state.y = I2cRead8(PAJ7620_ADDR, PAJ7620_OBJECT_CENTER_Y); - if ((PAJ7620_state.y > 0) && (PAJ7620_state.x > 0)) { - if ((PAJ7620_state.y != PAJ7620_state.last_y) || (PAJ7620_state.x != PAJ7620_state.last_x)) { - PAJ7620_state.last_y = PAJ7620_state.y; - PAJ7620_state.last_x = PAJ7620_state.x; - DEBUG_SENSOR_LOG(PSTR("PAJ: x: %u y: %u"), PAJ7620_state.x, PAJ7620_state.y); - - PAJ7620_state.corner = 0; - - - - switch (PAJ7620_state.y) { - case 0: case 1: case 2: case 3: case 4: case 5: - PAJ7620_state.corner = 3; - break; - case 9: case 10: case 11: case 12: case 13: case 14: - PAJ7620_state.corner = 1; - break; - } - if (PAJ7620_state.corner != 0) { - switch (PAJ7620_state.x) { - case 0: case 1: case 2: case 3: case 4: case 5: - break; - case 9: case 10: case 11: case 12: case 13: case 14: - PAJ7620_state.corner++; - break; - default: - PAJ7620_state.corner = 0; - break; - } - } - DEBUG_SENSOR_LOG(PSTR("PAJ: corner: %u"), PAJ7620_state.corner); - - if (PAJ7620_state.PIN.countdown == 0) { - PAJ7620_state.PIN.step = 0; - PAJ7620_state.PIN.valid = 0; - } - if (!PAJ7620_state.PIN.step) { - if (PAJ7620_state.corner == PAJ7620_PIN[PAJ7620_state.PIN.step]) { - PAJ7620_state.PIN.step = 1; - PAJ7620_state.PIN.countdown = 7; - } - } else { - if (PAJ7620_state.corner == PAJ7620_PIN[PAJ7620_state.PIN.step]) { - PAJ7620_state.PIN.step += 1; - PAJ7620_state.PIN.countdown = 7; - } else { - PAJ7620_state.PIN.countdown -= 1; - } - } - if (PAJ7620_state.PIN.step == 4) { - PAJ7620_state.PIN.valid = 1; - DEBUG_SENSOR_LOG(PSTR("PAJ: PIN valid!!")); - PAJ7620_state.PIN.countdown = 0; - } - MqttPublishSensor(); - } - } - break; - } -} - - - -void PAJ7620Detect(void) -{ - if (I2cActive(PAJ7620_ADDR)) { return; } - - PAJ7620SelectBank(0); - PAJ7620SelectBank(0); - uint16_t PAJ7620_id = I2cRead16LE(PAJ7620_ADDR,0); - uint8_t PAJ7620_ver = I2cRead8(PAJ7620_ADDR,2); - if (0x7620 == PAJ7620_id) { - I2cSetActiveFound(PAJ7620_ADDR, PAJ7620_name); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAJ: ID: 0x%x and VER: %u"), PAJ7620_id, PAJ7620_ver); - PAJ7620_next_job = 1; - } - else { - DEBUG_SENSOR_LOG(PSTR("PAJ: sensor not found, false ID 0x%x"), PAJ7620_id); - } -} - - - -void PAJ7620Init(void) -{ - DEBUG_SENSOR_LOG(PSTR("PAJ: init sensor start %u"),millis()); - union{ - uint32_t raw; - uint8_t reg_val[4]; - } buf; - - for (uint32_t i = 0; i < (sizeof(PAJ7620initRegisterArray) / 2); i += 2) - { - buf.raw = pgm_read_dword(PAJ7620initRegisterArray + i); - DEBUG_SENSOR_LOG("PAJ: %x %x %x %x",buf.reg_val[0],buf.reg_val[1],buf.reg_val[2],buf.reg_val[3]); - I2cWrite(PAJ7620_ADDR, buf.reg_val[0], buf.reg_val[1], 1); - I2cWrite(PAJ7620_ADDR, buf.reg_val[2], buf.reg_val[3], 1); - } - DEBUG_SENSOR_LOG(PSTR("PAJ: init sensor done %u"),millis()); - PAJ7620_next_job = 2; -} - - - -void PAJ7620Loop(void) -{ - if (0 == PAJ7620_timeout_counter) { - switch (PAJ7620_next_job) { - case 1: - PAJ7620Init(); - break; - case 2: - if (PAJ7620_mode != 0) { - PAJ7620ReadGesture(); - } - break; - } - } else { - PAJ7620_timeout_counter--; - } -} - - - -void PAJ7620Show(bool json) -{ - if (json) { - if (PAJ7620_currentGestureName[0] != '\0' ) { - ResponseAppend_P(PSTR(",\"%s\":{\"%s\":%u}"), PAJ7620_name, PAJ7620_currentGestureName, PAJ7620_gesture.same); - PAJ7620_currentGestureName[0] = '\0'; - return; - } - switch (PAJ7620_mode) { - case 2: - ResponseAppend_P(PSTR(",\"%s\":{\"Proximity\":%u}"), PAJ7620_name, PAJ7620_state.proximity); - break; - case 3: - if (PAJ7620_state.corner > 0) { - ResponseAppend_P(PSTR(",\"%s\":{\"Corner\":%u}"), PAJ7620_name, PAJ7620_state.corner); - } - break; - case 4: - if (PAJ7620_state.PIN.valid) { - ResponseAppend_P(PSTR(",\"%s\":{\"PIN\":%u}"), PAJ7620_name, 1); - PAJ7620_state.PIN.valid = 0; - } - break; - case 5: - ResponseAppend_P(PSTR(",\"%s\":{\"x\":%u,\"y\":%u}"), PAJ7620_name, PAJ7620_state.x, PAJ7620_state.y); - break; - } - } -} -# 411 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_50_paj7620.ino" -bool PAJ7620CommandSensor(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) { - PAJ7620_mode = XdrvMailbox.payload; - } - Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_50, PAJ7620_mode); - - return true; -} - - - - - -bool Xsns50(uint8_t function) -{ - if (!I2cEnabled(XI2C_34)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - PAJ7620Detect(); - } - else if (PAJ7620_next_job) { - switch (function) { - case FUNC_COMMAND_SENSOR: - if (XSNS_50 == XdrvMailbox.index){ - result = PAJ7620CommandSensor(); - } - break; - case FUNC_EVERY_100_MSECOND: - PAJ7620Loop(); - break; - case FUNC_JSON_APPEND: - PAJ7620Show(1); - break; - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_51_rdm6300.ino" -# 21 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_51_rdm6300.ino" -#ifdef USE_RDM6300 - -#define XSNS_51 51 - -#define RDM6300_BAUDRATE 9600 - -#include - -#define RDM_TIMEOUT 100 -char rdm_uid_str[10]; - - -#define RDM6300_BLOCK 2*10 - -uint8_t rdm_blcnt; -TasmotaSerial *RDM6300_Serial = nullptr; - -void RDM6300_Init() { - if (pin[GPIO_RDM6300_RX] < 99) { - RDM6300_Serial = new TasmotaSerial(pin[GPIO_RDM6300_RX],-1,1); - if (RDM6300_Serial->begin(RDM6300_BAUDRATE)) { - if (RDM6300_Serial->hardwareSerial()) { - ClaimSerial(); - } - } - } - rdm_blcnt=0; -} - - -void RDM6300_ScanForTag() { - char rdm_buffer[14]; - uint8_t rdm_index; - uint8_t rdm_array[6]; - - if (!RDM6300_Serial) return; - - if (rdm_blcnt>0) { - rdm_blcnt--; - while (RDM6300_Serial->available()) RDM6300_Serial->read(); - return; - } - - if (RDM6300_Serial->available()) { - - char c=RDM6300_Serial->read(); - if (c!=2) return; - - - rdm_index=0; - uint32_t cmillis=millis(); - while (1) { - if (RDM6300_Serial->available()) { - char c=RDM6300_Serial->read(); - if (c==3) { - - break; - } - rdm_buffer[rdm_index++]=c; - if (rdm_index>13) { - - return; - } - } - if ((millis()-cmillis)>RDM_TIMEOUT) { - - return; - } - } - - - rdm_blcnt=RDM6300_BLOCK; - - - rm6300_hstring_to_array(rdm_array,sizeof(rdm_array),rdm_buffer); - uint8_t accu=0; - for (uint8_t count=0;count<5;count++) { - accu^=rdm_array[count]; - } - if (accu!=rdm_array[5]) { - - return; - } - - - memcpy(rdm_uid_str,&rdm_buffer[2],8); - rdm_uid_str[9]=0; - - ResponseTime_P(PSTR(",\"RDM6300\":{\"UID\":\"%s\"}}"), rdm_uid_str); - MqttPublishTeleSensor(); - - - - - - } - - -} - -uint8_t rm6300_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; -} - - -void rm6300_hstring_to_array(uint8_t array[], uint8_t len, char buffer[]) -{ - char *cp=buffer; - for (uint8_t i = 0; i < len; i++) { - uint8_t val = rm6300_hexnibble(*cp++) << 4; - array[i]= val | rm6300_hexnibble(*cp++); - } -} - -#ifdef USE_WEBSERVER -const char HTTP_RDM6300[] PROGMEM = - "{s}RDM6300 " "UID" "{m}%s" "{e}"; - -void RDM6300_Show(void) { - if (!RDM6300_Serial) return; - if (!rdm_uid_str[0]) strcpy(rdm_uid_str,"????"); - WSContentSend_PD(HTTP_RDM6300,rdm_uid_str); -} -#endif - - - - - -bool Xsns51(byte function) -{ - bool result = false; - - switch (function) { - case FUNC_INIT: - RDM6300_Init(); - break; - case FUNC_EVERY_100_MSECOND: - RDM6300_ScanForTag(); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - RDM6300_Show(); - break; -#endif - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_52_ibeacon.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_52_ibeacon.ino" -#ifdef USE_IBEACON - - - -#define XSNS_52 52 - -#include - -#define HM17_BAUDRATE 9600 - -#define IBEACON_DEBUG - - -#define HM17_V110 - - - -#define IB_TIMEOUT_INTERVAL 30 - -#define IB_UPDATE_TIME_INTERVAL 10 - -TasmotaSerial *IBEACON_Serial = nullptr; - - -uint8_t hm17_found,hm17_cmd,hm17_flag; - -#ifdef IBEACON_DEBUG -uint8_t hm17_debug=0; -#endif - - - -#define HM17_BSIZ 128 -char hm17_sbuffer[HM17_BSIZ]; -uint8_t hm17_sindex,hm17_result,hm17_scanning,hm17_connecting; -uint32_t hm17_lastms; -char ib_mac[14]; - - -#if 1 -uint8_t ib_upd_interval,ib_tout_interval; -#define IB_UPDATE_TIME ib_upd_interval -#define IB_TIMEOUT_TIME ib_tout_interval -#else -#undef IB_UPDATE_TIME -#undef IB_TIMEOUT_TIME -#define IB_UPDATE_TIME Settings.ib_upd_interval -#define IB_TIMEOUT_TIME Settings.ib_tout_interval -#endif - -enum {HM17_TEST,HM17_ROLE,HM17_IMME,HM17_DISI,HM17_IBEA,HM17_SCAN,HM17_DISC,HM17_RESET,HM17_RENEW,HM17_CON}; -#define HM17_SUCESS 99 - -struct IBEACON { - char FACID[8]; - char UID[32]; - char MAJOR[4]; - char MINOR[4]; - char PWR[2]; - char MAC[12]; - char RSSI[4]; -}; - -#define MAX_IBEACONS 16 - -struct IBEACON_UID { - char MAC[12]; - char RSSI[4]; - uint8_t FLAGS; - uint8_t TIME; -} ibeacons[MAX_IBEACONS]; - - -void IBEACON_Init() { - - hm17_found=0; - - - if ((pin[GPIO_IBEACON_RX] < 99) && (pin[GPIO_IBEACON_TX] < 99)) { - IBEACON_Serial = new TasmotaSerial(pin[GPIO_IBEACON_RX], pin[GPIO_IBEACON_TX],1); - if (IBEACON_Serial->begin(HM17_BAUDRATE)) { - if (IBEACON_Serial->hardwareSerial()) { - ClaimSerial(); - } - hm17_sendcmd(HM17_TEST); - hm17_lastms=millis(); - - IB_UPDATE_TIME=IB_UPDATE_TIME_INTERVAL; - IB_TIMEOUT_TIME=IB_TIMEOUT_INTERVAL; - } - } -} - -void hm17_every_second(void) { - if (!IBEACON_Serial) return; - - if (hm17_found) { - if (IB_UPDATE_TIME && (uptime%IB_UPDATE_TIME==0)) { - if (hm17_cmd!=99) { - if (hm17_flag&2) { - ib_sendbeep(); - } else { - if (!hm17_connecting) { - hm17_sendcmd(HM17_DISI); - } - } - } - } - for (uint32_t cnt=0;cntIB_TIMEOUT_TIME) { - ibeacons[cnt].FLAGS=0; - ibeacon_mqtt(ibeacons[cnt].MAC,"0000"); - } - } - } - } else { - if (uptime%20==0) { - hm17_sendcmd(HM17_TEST); - } - } -} - -void hm17_sbclr(void) { - memset(hm17_sbuffer,0,HM17_BSIZ); - hm17_sindex=0; - IBEACON_Serial->flush(); -} - -void hm17_sendcmd(uint8_t cmd) { - hm17_sbclr(); - hm17_cmd=cmd; -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("hm17cmd %d"),cmd); -#endif - switch (cmd) { - case HM17_TEST: - IBEACON_Serial->write("AT"); - break; - case HM17_ROLE: - IBEACON_Serial->write("AT+ROLE1"); - break; - case HM17_IMME: - IBEACON_Serial->write("AT+IMME1"); - break; - case HM17_DISI: - IBEACON_Serial->write("AT+DISI?"); - hm17_scanning=1; - break; - case HM17_IBEA: - IBEACON_Serial->write("AT+IBEA1"); - break; - case HM17_RESET: - IBEACON_Serial->write("AT+RESET"); - break; - case HM17_RENEW: - IBEACON_Serial->write("AT+RENEW"); - break; - case HM17_SCAN: - IBEACON_Serial->write("AT+SCAN5"); - break; - case HM17_DISC: - IBEACON_Serial->write("AT+DISC?"); - hm17_scanning=1; - break; - case HM17_CON: - IBEACON_Serial->write((const uint8_t*)"AT+CON",6); - IBEACON_Serial->write((const uint8_t*)ib_mac,12); - hm17_connecting=1; - break; - } -} - -uint32_t ibeacon_add(struct IBEACON *ib) { - - if (!strncmp(ib->MAC,"FFFF",4) || strncmp(ib->FACID,"00000000",8)) { - for (uint32_t cnt=0;cntMAC,12)) { - - memcpy(ibeacons[cnt].RSSI,ib->RSSI,4); - ibeacons[cnt].TIME=0; - return 1; - } - } - } - for (uint32_t cnt=0;cntMAC,12); - memcpy(ibeacons[cnt].RSSI,ib->RSSI,4); - ibeacons[cnt].FLAGS=1; - ibeacons[cnt].TIME=0; - return 1; - } - } - } - return 0; -} - -void hm17_decode(void) { - struct IBEACON ib; - switch (hm17_cmd) { - case HM17_TEST: - if (!strncmp(hm17_sbuffer,"OK",2)) { -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("AT OK")); -#endif - hm17_sbclr(); - hm17_result=HM17_SUCESS; - hm17_found=1; - } - break; - case HM17_ROLE: - if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("ROLE OK")); -#endif - hm17_sbclr(); - hm17_result=HM17_SUCESS; - } - break; - case HM17_IMME: - if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("IMME OK")); -#endif - hm17_sbclr(); - hm17_result=HM17_SUCESS; - } - break; - case HM17_IBEA: - if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("IBEA OK")); -#endif - hm17_sbclr(); - hm17_result=HM17_SUCESS; - } - break; - case HM17_SCAN: - if (!strncmp(hm17_sbuffer,"OK+Set:5",8)) { -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("SCAN OK")); -#endif - hm17_sbclr(); - hm17_result=HM17_SUCESS; - } - break; - case HM17_RESET: - if (!strncmp(hm17_sbuffer,"OK+RESET",8)) { -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("RESET OK")); -#endif - hm17_sbclr(); - hm17_result=HM17_SUCESS; - } - break; - case HM17_RENEW: - if (!strncmp(hm17_sbuffer,"OK+RENEW",8)) { -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("RENEW OK")); -#endif - hm17_sbclr(); - hm17_result=HM17_SUCESS; - } - break; - case HM17_CON: - if (!strncmp(hm17_sbuffer,"OK+CONNA",8)) { - hm17_sbclr(); -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONNA OK")); -#endif - hm17_connecting=2; - break; - } - if (!strncmp(hm17_sbuffer,"OK+CONNE",8)) { - hm17_sbclr(); -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONNE ERROR")); -#endif - break; - } - if (!strncmp(hm17_sbuffer,"OK+CONNF",8)) { - hm17_sbclr(); -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONNF ERROR")); -#endif - break; - } - if (hm17_connecting==2 && !strncmp(hm17_sbuffer,"OK+CONN",7)) { - hm17_sbclr(); -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONN OK")); -#endif - hm17_connecting=3; - hm17_sendcmd(HM17_TEST); - hm17_connecting=0; - break; - } - break; - - case HM17_DISI: - case HM17_DISC: - if (!strncmp(hm17_sbuffer,"OK+DISCS",8)) { - hm17_sbclr(); - hm17_result=1; -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("DISCS OK")); -#endif - break; - } - if (!strncmp(hm17_sbuffer,"OK+DISIS",8)) { - hm17_sbclr(); - hm17_result=1; -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("DISIS OK")); -#endif - break; - } - if (!strncmp(hm17_sbuffer,"OK+DISCE",8)) { - hm17_sbclr(); - hm17_result=HM17_SUCESS; -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("DISCE OK")); -#endif - hm17_scanning=0; - break; - } - if (!strncmp(hm17_sbuffer,"OK+NAME:",8)) { - if (hm17_sbuffer[hm17_sindex-1]=='\n') { - hm17_result=HM17_SUCESS; -#ifdef IBEACON_DEBUG - if (hm17_debug) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("NAME OK")); - AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); - } -#endif - hm17_sbclr(); - } - break; - } - if (!strncmp(hm17_sbuffer,"OK+DIS0:",8)) { - if (hm17_cmd==HM17_DISI) { -#ifdef HM17_V110 - goto hm17_v110; -#endif - } else { - if (hm17_sindex==20) { - hm17_result=HM17_SUCESS; -#ifdef IBEACON_DEBUG - if (hm17_debug) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("DIS0 OK")); - AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); - } -#endif - hm17_sbclr(); - } - } - break; - } - if (!strncmp(hm17_sbuffer,"OK+DISC:",8)) { -hm17_v110: - if (hm17_cmd==HM17_DISI) { - if (hm17_sindex==78) { -#ifdef IBEACON_DEBUG - if (hm17_debug) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("DISC: OK")); - - AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); - } -#endif - memcpy(ib.FACID,&hm17_sbuffer[8],8); - memcpy(ib.UID,&hm17_sbuffer[8+8+1],32); - memcpy(ib.MAJOR,&hm17_sbuffer[8+8+1+32+1],4); - memcpy(ib.MINOR,&hm17_sbuffer[8+8+1+32+1+4],4); - memcpy(ib.PWR,&hm17_sbuffer[8+8+1+32+1+4+4],2); - memcpy(ib.MAC,&hm17_sbuffer[8+8+1+32+1+4+4+2+1],12); - memcpy(ib.RSSI,&hm17_sbuffer[8+8+1+32+1+4+4+2+1+12+1],4); - - if (ibeacon_add(&ib)) { - ibeacon_mqtt(ib.MAC,ib.RSSI); - } - hm17_sbclr(); - hm17_result=1; - } - } else { -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); -#endif - } - break; - } - } -} - -void IBEACON_loop() { - - if (!IBEACON_Serial) return; - -uint32_t difftime=millis()-hm17_lastms; - - while (IBEACON_Serial->available()) { - hm17_lastms=millis(); - - if (hm17_sindexread(); - hm17_sindex++; - hm17_decode(); - } else { - hm17_sindex=0; - break; - } - } - - if (hm17_cmd==99) { - if (hm17_sindex>=HM17_BSIZ-2 || (hm17_sindex && (difftime>100))) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),hm17_sbuffer); - hm17_sbclr(); - } - } - -} - -#ifdef USE_WEBSERVER -const char HTTP_IBEACON[] PROGMEM = - "{s}IBEACON-UID : %s" " - RSSI : %s" "{m}{e}"; - -void IBEACON_Show(void) { -char mac[14]; -char rssi[6]; - - for (uint32_t cnt=0;cnt 0) { - char *cp=XdrvMailbox.data; - if (*cp>='0' && *cp<='8') { - hm17_sendcmd(*cp&7); - Response_P(S_JSON_IBEACON, XSNS_52,"hm17cmd",*cp&7); - } else if (*cp=='s') { - cp++; - len--; - while (*cp==' ') { - len--; - cp++; - } - IBEACON_Serial->write((uint8_t*)cp,len); - hm17_cmd=99; - Response_P(S_JSON_IBEACON1, XSNS_52,"hm17cmd",cp); - } else if (*cp=='u') { - cp++; - if (*cp) IB_UPDATE_TIME=atoi(cp); - Response_P(S_JSON_IBEACON, XSNS_52,"uintv",IB_UPDATE_TIME); - } else if (*cp=='t') { - cp++; - if (*cp) IB_TIMEOUT_TIME=atoi(cp); - Response_P(S_JSON_IBEACON, XSNS_52,"lintv",IB_TIMEOUT_TIME); - } else if (*cp=='c') { - for (uint32_t cnt=0;cnt - - -#define SPECIAL_SS - - - - - - -#define DJ_TPWRIN "Total_in" -#define DJ_TPWROUT "Total_out" -#define DJ_TPWRCURR "Power_curr" -#define DJ_TPWRCURR1 "Power_p1" -#define DJ_TPWRCURR2 "Power_p2" -#define DJ_TPWRCURR3 "Power_p3" -#define DJ_CURR1 "Curr_p1" -#define DJ_CURR2 "Curr_p2" -#define DJ_CURR3 "Curr_p3" -#define DJ_VOLT1 "Volt_p1" -#define DJ_VOLT2 "Volt_p2" -#define DJ_VOLT3 "Volt_p3" -#define DJ_METERNR "Meter_number" -#define DJ_METERSID "Meter_id" -#define DJ_CSUM "Curr_summ" -#define DJ_VAVG "Volt_avg" -#define DJ_COUNTER "Count" - -struct METER_DESC { - uint8_t srcpin; - uint8_t type; - uint16_t flag; - int32_t params; - char prefix[8]; - int8_t trxpin; - uint8_t tsecs; - char *txmem; - uint8_t index; - uint8_t max_index; -}; - - - - - - -#define EHZ161_0 1 -#define EHZ161_1 2 -#define EHZ363 3 -#define EHZH 4 -#define EDL300 5 -#define Q3B 6 -#define COMBO3 7 -#define COMBO2 8 -#define COMBO3a 9 -#define Q3B_V1 10 -#define EHZ363_2 11 -#define COMBO3b 12 -#define WGS_COMBO 13 -#define EBZD_G 14 - - -#define METER EHZ161_1 - - -#if METER==EHZ161_0 -#undef METERS_USED -#define METERS_USED 1 -struct METER_DESC const meter_desc[METERS_USED]={ - [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|" -"1,1-0:21.7.0*255(@1," D_TPWRCURR1 ",W," DJ_TPWRCURR1 ",0|" -"1,1-0:41.7.0*255(@1," D_TPWRCURR2 ",W," DJ_TPWRCURR2 ",0|" -"1,1-0:61.7.0*255(@1," D_TPWRCURR3 ",W," DJ_TPWRCURR3 ",0|" -"1,=m 3+4+5 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; - -#endif - - - -#if METER==EHZ161_1 -#undef METERS_USED -#define METERS_USED 1 -struct METER_DESC const meter_desc[METERS_USED]={ - [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|" -"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; -#endif - - - -#if METER==EHZ363 -#undef METERS_USED -#define METERS_USED 1 -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; - -const uint8_t meter[]= - -"1,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" - -"1,77070100020800ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" - -"1,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" - -"1,77070100000009ff@#," D_METERNR ",," DJ_METERNR ",0"; -#endif - - - -#if METER==EHZH -#undef METERS_USED -#define METERS_USED 1 -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; - - -const uint8_t meter[]= - -"1,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" - -"1,77070100020800ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" - -"1,770701000f0700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0"; -#endif - - - -#if METER==EDL300 -#undef METERS_USED -#define METERS_USED 1 -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; - - -const uint8_t meter[]= - -"1,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" - -"1,77070100020801ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" - -"1,770701000f0700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0"; -#endif - -#if METER==EBZD_G -#undef METERS_USED -#define METERS_USED 1 -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'s',0,SML_BAUDRATE,"strom",-1,1,0}}; -const uint8_t meter[]= - -"1,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" - -"1,77070100020800ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" - -"1,77070100010801ff@1000," D_TPWRCURR1 ",kWh," DJ_TPWRCURR1 ",4|" - -"1,77070100010802ff@1000," D_TPWRCURR2 ",kWh," DJ_TPWRCURR2 ",4|" - -"1,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" - -"1,77070100600100ff@#," D_METERNR ",," DJ_METERNR ",0"; -#endif - - - - -#if METER==Q3B -#undef METERS_USED -#define METERS_USED 1 -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; -const uint8_t meter[]= - -"1,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" - -"1,77070100020801ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" - -"1,77070100010700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0"; -#endif - -#if METER==COMBO3 - -#undef METERS_USED -#define METERS_USED 3 - -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}, - [1]={14,'s',0,SML_BAUDRATE,"SML",-1,1,0}, - [2]={4,'o',0,SML_BAUDRATE,"OBIS2",-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|" -"1,1-0:21.7.0*255(@1," D_TPWRCURR1 ",W," DJ_TPWRCURR1 ",0|" -"1,1-0:41.7.0*255(@1," D_TPWRCURR2 ",W," DJ_TPWRCURR2 ",0|" -"1,1-0:61.7.0*255(@1," D_TPWRCURR3 ",W," DJ_TPWRCURR3 ",0|" -"1,=m 3+4+5 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" -"2,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" -"2,77070100020800ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" -"2,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"3,1-0:1.8.1*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" -"3,1-0:2.8.1*255(@1," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" -"3,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"3,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; - -#endif - -#if METER==COMBO2 - -#undef METERS_USED -#define METERS_USED 2 - -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'o',0,SML_BAUDRATE,"OBIS1",-1,1,0}, - [1]={14,'o',0,SML_BAUDRATE,"OBIS2",-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|" -"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" - -"2,1-0:1.8.1*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" -"2,1-0:2.8.1*255(@1," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" -"2,=d 6 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"2,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; - -#endif - -#if METER==COMBO3a -#undef METERS_USED -#define METERS_USED 3 - -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'o',0,SML_BAUDRATE,"OBIS1",-1,1,0}, - [1]={14,'o',0,SML_BAUDRATE,"OBIS2",-1,1,0}, - [2]={1,'o',0,SML_BAUDRATE,"OBIS3",-1,1,0}}; - - -const uint8_t meter[]= -"1,=h --- Zähler Nr 1 ---|" -"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|" -"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" -"2,=h --- Zähler Nr 2 ---|" -"2,1-0:1.8.1*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" -"2,1-0:2.8.1*255(@1," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" -"2,=d 6 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"2,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" -"3,=h --- Zähler Nr 3 ---|" -"3,1-0:1.8.1*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" -"3,1-0:2.8.1*255(@1," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" -"3,=d 10 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"3,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; - -#endif - - - -#if METER==Q3B_V1 -#undef METERS_USED -#define METERS_USED 1 -struct METER_DESC const meter_desc[METERS_USED]={ -[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|" -"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; -#endif - - - -#if METER==EHZ363_2 -#undef METERS_USED -#define METERS_USED 1 -struct METER_DESC const meter_desc[METERS_USED]={ -[0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; - -const uint8_t meter[]= - -"1,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",4|" - -"1,77070100020800ff@1000," D_TPWROUT ",kWh," DJ_TPWROUT ",4|" - -"1,77070100010801ff@1000," D_TPWRCURR1 ",kWh," DJ_TPWRCURR1 ",4|" - -"1,77070100010802ff@1000," D_TPWRCURR2 ",kWh," DJ_TPWRCURR2 ",4|" - -"1,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" - -"1,77070100000009ff@#," D_METERNR ",," DJ_METERNR ",0"; -#endif - - -#if METER==COMBO3b -#undef METERS_USED -#define METERS_USED 3 -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}, - [1]={14,'c',0,50,"Gas"}, - [2]={1,'c',0,10,"Wasser"}}; - - -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|" -"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" - - -"2,1-0:1.8.0*255(@100," D_GasIN ",cbm," DJ_COUNTER ",2|" - -"3,1-0:1.8.0*255(@100," D_H2oIN ",cbm," DJ_COUNTER ",2"; -#endif - - -#if METER==WGS_COMBO -#undef METERS_USED -#define METERS_USED 3 - -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={1,'c',0,10,"H20",-1,1,0}, - [1]={4,'c',0,50,"GAS",-1,1,0}, - [2]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; - -const uint8_t meter[]= - - -"1,1-0:1.8.0*255(@10000," D_H2oIN ",cbm," DJ_COUNTER ",4|" - - -"2,=h==================|" -"2,1-0:1.8.0*255(@100," D_GasIN ",cbm," DJ_COUNTER ",3|" - -"3,=h==================|" - -"3,77070100010800ff@1000," D_TPWRIN ",kWh," DJ_TPWRIN ",3|" -"3,=h==================|" - -"3,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",2|" -"3,=h -------------------------------|" -"3,=m 10+11+12 @100," D_StL1L2L3 ",A," DJ_CSUM ",2|" - -"3,=m 13+14+15/#3 @100," D_SpL1L2L3 ",V," DJ_VAVG ",2|" -"3,=h==================|" - -"3,77070100240700ff@1," D_TPWRCURR1 ",W," DJ_TPWRCURR1 ",2|" - -"3,77070100380700ff@1," D_TPWRCURR2 ",W," DJ_TPWRCURR2 ",2|" - -"3,770701004c0700ff@1," D_TPWRCURR3 ",W," DJ_TPWRCURR3 ",2|" -"3,=h -------------------------------|" - -"3,770701001f0700ff@100," D_Strom_L1 ",A," DJ_CURR1 ",2|" - -"3,77070100330700ff@100," D_Strom_L2 ",A," DJ_CURR2 ",2|" - -"3,77070100470700ff@100," D_Strom_L3 ",A," DJ_CURR3 ",2|" -"3,=h -------------------------------|" - -"3,77070100200700ff@100," D_Spannung_L1 ",V," DJ_VOLT1 ",2|" - -"3,77070100340700ff@100," D_Spannung_L2 ",V," DJ_VOLT2 ",2|" - -"3,77070100480700ff@100," D_Spannung_L3 ",V," DJ_VOLT3 ",2|" -"3,=h==================|" - -"3,77070100000009ff@#," D_METERSID ",," DJ_METERSID ",0|" -"3,=h--------------------------------"; -#endif -# 435 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_53_sml.ino" -#define USE_SML_MEDIAN_FILTER - - -#ifndef SML_MAX_VARS -#define SML_MAX_VARS 20 -#endif - - -#define MAX_METERS 5 -double meter_vars[SML_MAX_VARS]; - -#define MAX_DVARS MAX_METERS*2 -double dvalues[MAX_DVARS]; -uint32_t dtimes[MAX_DVARS]; -uint8_t meters_used; - -struct METER_DESC const *meter_desc_p; -const uint8_t *meter_p; -uint8_t meter_spos[MAX_METERS]; - - -TasmotaSerial *meter_ss[MAX_METERS]; - - -#define SML_BSIZ 48 -uint8_t smltbuf[MAX_METERS][SML_BSIZ]; - - -#define METER_ID_SIZE 24 -char meter_id[MAX_METERS][METER_ID_SIZE]; - -#define EBUS_SYNC 0xaa -#define EBUS_ESC 0xa9 - -uint8_t sml_send_blocks; -uint8_t sml_100ms_cnt; -uint8_t sml_desc_cnt; - -#ifdef USE_SML_MEDIAN_FILTER - -#define MEDIAN_SIZE 5 -struct SML_MEDIAN_FILTER { -double buffer[MEDIAN_SIZE]; -int8_t index; -} sml_mf[SML_MAX_VARS]; - -#ifndef FLT_MAX -#define FLT_MAX 99999999 -#endif - -double sml_median_array(double *array,uint8_t len) { - uint8_t ind[len]; - uint8_t mind=0,index=0,flg; - double min=FLT_MAX; - - for (uint8_t hcnt=0; hcntbuffer[mf->index]=in; - mf->index++; - if (mf->index>=MEDIAN_SIZE) mf->index=0; - - return sml_median_array(mf->buffer,MEDIAN_SIZE); -# 539 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_53_sml.ino" -} -#endif - -#ifdef ANALOG_OPTO_SENSOR - -uint8_t ads1115_up; - - -#define SAMPLE_BIT (0x8000) - -#define ADS1115_COMP_QUEUE_SHIFT 0 -#define ADS1115_COMP_LATCH_SHIFT 2 -#define ADS1115_COMP_POLARITY_SHIFT 3 -#define ADS1115_COMP_MODE_SHIFT 4 -#define ADS1115_DATA_RATE_SHIFT 5 -#define ADS1115_MODE_SHIFT 8 -#define ADS1115_PGA_SHIFT 9 -#define ADS1115_MUX_SHIFT 12 - -enum ads1115_comp_queue { - ADS1115_COMP_QUEUE_AFTER_ONE = 0, - ADS1115_COMP_QUEUE_AFTER_TWO = 0x1 << ADS1115_COMP_QUEUE_SHIFT, - ADS1115_COMP_QUEUE_AFTER_FOUR = 0x2 << ADS1115_COMP_QUEUE_SHIFT, - ADS1115_COMP_QUEUE_DISABLE = 0x3 << ADS1115_COMP_QUEUE_SHIFT, - ADS1115_COMP_QUEUE_MASK = 0x3 << ADS1115_COMP_QUEUE_SHIFT, -}; - -enum ads1115_comp_latch { - ADS1115_COMP_LATCH_NO = 0, - ADS1115_COMP_LATCH_YES = 1 << ADS1115_COMP_LATCH_SHIFT, - ADS1115_COMP_LATCH_MASK = 1 << ADS1115_COMP_LATCH_SHIFT, -}; - -enum ads1115_comp_polarity { - ADS1115_COMP_POLARITY_ACTIVE_LOW = 0, - ADS1115_COMP_POLARITY_ACTIVE_HIGH = 1 << ADS1115_COMP_POLARITY_SHIFT, - ADS1115_COMP_POLARITY_MASK = 1 << ADS1115_COMP_POLARITY_SHIFT, -}; - -enum ads1115_comp_mode { - ADS1115_COMP_MODE_WINDOW = 0, - ADS1115_COMP_MODE_HYSTERESIS = 1 << ADS1115_COMP_MODE_SHIFT, - ADS1115_COMP_MODE_MASK = 1 << ADS1115_COMP_MODE_SHIFT, -}; - -enum ads1115_data_rate { - ADS1115_DATA_RATE_8_SPS = 0, - ADS1115_DATA_RATE_16_SPS = 0x1 << ADS1115_DATA_RATE_SHIFT, - ADS1115_DATA_RATE_32_SPS = 0x2 << ADS1115_DATA_RATE_SHIFT, - ADS1115_DATA_RATE_64_SPS = 0x3 << ADS1115_DATA_RATE_SHIFT, - ADS1115_DATA_RATE_128_SPS = 0x4 << ADS1115_DATA_RATE_SHIFT, - ADS1115_DATA_RATE_250_SPS = 0x5 << ADS1115_DATA_RATE_SHIFT, - ADS1115_DATA_RATE_475_SPS = 0x6 << ADS1115_DATA_RATE_SHIFT, - ADS1115_DATA_RATE_860_SPS = 0x7 << ADS1115_DATA_RATE_SHIFT, - ADS1115_DATA_RATE_MASK = 0x7 << ADS1115_DATA_RATE_SHIFT, -}; - -enum ads1115_mode { - ADS1115_MODE_CONTINUOUS = 0, - ADS1115_MODE_SINGLE_SHOT = 1 << ADS1115_MODE_SHIFT, - ADS1115_MODE_MASK = 1 << ADS1115_MODE_SHIFT, -}; - -enum ads1115_pga { - ADS1115_PGA_TWO_THIRDS = 0, - ADS1115_PGA_ONE = 0x1 << ADS1115_PGA_SHIFT, - ADS1115_PGA_TWO = 0x2 << ADS1115_PGA_SHIFT, - ADS1115_PGA_FOUR = 0x3 << ADS1115_PGA_SHIFT, - ADS1115_PGA_EIGHT = 0x4 << ADS1115_PGA_SHIFT, - ADS1115_PGA_SIXTEEN = 0x5 << ADS1115_PGA_SHIFT, - ADS1115_PGA_MASK = 0x7 << ADS1115_PGA_SHIFT, -}; - - -enum ads1115_mux { - ADS1115_MUX_DIFF_AIN0_AIN1 = 0, - ADS1115_MUX_DIFF_AIN0_AIN3 = 0x1 << ADS1115_MUX_SHIFT, - ADS1115_MUX_DIFF_AIN1_AIN3 = 0x2 << ADS1115_MUX_SHIFT, - ADS1115_MUX_DIFF_AIN2_AIN3 = 0x3 << ADS1115_MUX_SHIFT, - ADS1115_MUX_GND_AIN0 = 0x4 << ADS1115_MUX_SHIFT, - ADS1115_MUX_GND_AIN1 = 0x5 << ADS1115_MUX_SHIFT, - ADS1115_MUX_GND_AIN2 = 0x6 << ADS1115_MUX_SHIFT, - ADS1115_MUX_GND_AIN3 = 0x7 << ADS1115_MUX_SHIFT, - ADS1115_MUX_MASK = 0x7 << ADS1115_MUX_SHIFT, -}; - -class ADS1115 { -public: - ADS1115(uint8_t address = 0x48); - - void begin(); - uint8_t trigger_sample(); - uint8_t reset(); - bool is_sample_in_progress(); - int16_t read_sample(); - float sample_to_float(int16_t val); - float read_sample_float(); - - void set_comp_queue(enum ads1115_comp_queue val) { set_config(val, ADS1115_COMP_QUEUE_MASK); } - void set_comp_latching(enum ads1115_comp_latch val) { set_config(val, ADS1115_COMP_LATCH_MASK); } - void set_comp_polarity(enum ads1115_comp_polarity val) { set_config(val, ADS1115_COMP_POLARITY_MASK); } - void set_comp_mode(enum ads1115_comp_mode val) { set_config(val, ADS1115_COMP_MODE_MASK); } - void set_data_rate(enum ads1115_data_rate val) { set_config(val, ADS1115_DATA_RATE_MASK); } - void set_mode(enum ads1115_mode val) { set_config(val, ADS1115_MODE_MASK); } - void set_pga(enum ads1115_pga val) { set_config(val, ADS1115_PGA_MASK); m_voltage_range = val >> ADS1115_PGA_SHIFT; } - void set_mux(enum ads1115_mux val) { set_config(val, ADS1115_MUX_MASK); } - -private: - void set_config(uint16_t val, uint16_t mask) { - m_config = (m_config & ~mask) | val; - } - - uint8_t write_register(uint8_t reg, uint16_t val); - uint16_t read_register(uint8_t reg); - - uint8_t m_address; - uint16_t m_config; - int m_voltage_range; -}; - - -enum ads1115_register { - ADS1115_REGISTER_CONVERSION = 0, - ADS1115_REGISTER_CONFIG = 1, - ADS1115_REGISTER_LOW_THRESH = 2, - ADS1115_REGISTER_HIGH_THRESH = 3, -}; - -#define FACTOR 32768.0 -static float ranges[] = { 6.144 / FACTOR, 4.096 / FACTOR, 2.048 / FACTOR, 1.024 / FACTOR, 0.512 / FACTOR, 0.256 / FACTOR}; - -ADS1115::ADS1115(uint8_t address) -{ - m_address = address; - m_config = ADS1115_COMP_QUEUE_AFTER_ONE | - ADS1115_COMP_LATCH_NO | - ADS1115_COMP_POLARITY_ACTIVE_LOW | - ADS1115_COMP_MODE_WINDOW | - ADS1115_DATA_RATE_128_SPS | - ADS1115_MODE_SINGLE_SHOT | - ADS1115_MUX_GND_AIN0; - set_pga(ADS1115_PGA_ONE); -} - -uint8_t ADS1115::write_register(uint8_t reg, uint16_t val) -{ - Wire.beginTransmission(m_address); - Wire.write(reg); - Wire.write(val>>8); - Wire.write(val & 0xFF); - return Wire.endTransmission(); -} - -uint16_t ADS1115::read_register(uint8_t reg) -{ - Wire.beginTransmission(m_address); - Wire.write(reg); - Wire.endTransmission(); - - uint8_t result = Wire.requestFrom((int)m_address, 2, 1); - if (result != 2) { - return 0; - } - - uint16_t val; - - val = Wire.read() << 8; - val |= Wire.read(); - return val; -} - -void ADS1115::begin() -{ - Wire.begin(); -} - -uint8_t ADS1115::trigger_sample() -{ - return write_register(ADS1115_REGISTER_CONFIG, m_config | SAMPLE_BIT); -} - -uint8_t ADS1115::reset() -{ - Wire.beginTransmission(0); - Wire.write(0x6); - return Wire.endTransmission(); -} - -bool ADS1115::is_sample_in_progress() -{ - uint16_t val = read_register(ADS1115_REGISTER_CONFIG); - return (val & SAMPLE_BIT) == 0; -} - -int16_t ADS1115::read_sample() -{ - return read_register(ADS1115_REGISTER_CONVERSION); -} - -float ADS1115::sample_to_float(int16_t val) -{ - return val * ranges[m_voltage_range]; -} - -float ADS1115::read_sample_float() -{ - return sample_to_float(read_sample()); -} - -ADS1115 adc; - -void ADS1115_init(void) { - - ads1115_up=0; - if (!i2c_flg) return; - - adc.begin(); - adc.set_data_rate(ADS1115_DATA_RATE_128_SPS); - adc.set_mode(ADS1115_MODE_CONTINUOUS); - adc.set_mux(ADS1115_MUX_DIFF_AIN0_AIN3); - adc.set_pga(ADS1115_PGA_TWO); - - int16_t val = adc.read_sample(); - ads1115_up=1; -} - -#endif - -char sml_start; -uint8_t dump2log=0; - -#define SML_SAVAILABLE Serial_available() -#define SML_SREAD Serial_read() -#define SML_SPEAK Serial_peek() - -bool Serial_available() { - uint8_t num=dump2log&7; - 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) num=1; - return meter_ss[num-1]->read(); -} - -uint8_t Serial_peek() { - uint8_t num=dump2log&7; - if (num<1 || num>meters_used) num=1; - return meter_ss[num-1]->peek(); -} - -uint8_t sml_logindex; - -void Dump2log(void) { - -int16_t index=0,hcnt=0; -uint32_t d_lastms; -uint8_t dchars[16]; - - - - if (dump2log&8) { - - while (SML_SAVAILABLE) { - log_data[index]=':'; - index++; - log_data[index]=' '; - index++; - d_lastms=millis(); - while ((millis()-d_lastms)<40) { - if (SML_SAVAILABLE) { - uint8_t c=SML_SREAD; - sprintf(&log_data[index],"%02x ",c); - dchars[hcnt]=c; - index+=3; - hcnt++; - if (hcnt>15) { - - log_data[index]='='; - index++; - log_data[index]='>'; - index++; - log_data[index]=' '; - index++; - for (uint8_t ccnt=0; ccnt<16; ccnt++) { - if (isprint(dchars[ccnt])) { - log_data[index]=dchars[ccnt]; - } else { - log_data[index]=' '; - } - index++; - } - break; - } - } - } - if (index>0) { - log_data[index]=0; - AddLog(LOG_LEVEL_INFO); - index=0; - hcnt=0; - } - } - } else { - if (meter_desc_p[(dump2log&7)-1].type=='o') { - - while (SML_SAVAILABLE) { - char c=SML_SREAD&0x7f; - if (c=='\n' || c=='\r') { - log_data[sml_logindex]=0; - AddLog(LOG_LEVEL_INFO); - sml_logindex=2; - log_data[0]=':'; - log_data[1]=' '; - break; - } - log_data[sml_logindex]=c; - if (sml_logindex2) { - log_data[index]=0; - AddLog(LOG_LEVEL_INFO); - } - } - } -} - -#ifdef ED300L -uint8_t sml_status[MAX_METERS]; -uint8_t g_mindex; -#endif - - -uint8_t *skip_sml(uint8_t *cp,int16_t *res) { - uint8_t len,len1,type; - len=*cp&0xf; - type=*cp&0x70; - if (type==0x70) { - - - cp++; - while (len--) { - len1=*cp&0x0f; - cp+=len1; - } - *res=0; - } else { - - *res=(signed char)*(cp+1); - cp+=len; - } - return cp; -} - - - -double sml_getvalue(unsigned char *cp,uint8_t index) { -uint8_t len,unit,type; -int16_t scaler,result; -int64_t value; -double dval; - - - -#ifdef ED300L - unsigned char *cpx=cp-5; - - if (*cp==0x64 && *cpx==0 && *(cpx+1)==0x01 && *(cpx+2)==0x08 && *(cpx+3)==0) { - sml_status[g_mindex]=*(cp+3); - } -#endif - - cp=skip_sml(cp,&result); - - cp=skip_sml(cp,&result); - - cp=skip_sml(cp,&result); - - cp=skip_sml(cp,&result); - scaler=result; - - - type=*cp&0x70; - len=*cp&0x0f; - cp++; - if (type==0x50 || type==0x60) { - - uint64_t uvalue=0; - uint8_t nlen=len; - while (--nlen) { - uvalue<<=8; - uvalue|=*cp++; - } - if (type==0x50) { - - switch (len-1) { - case 1: - - value=(signed char)uvalue; - break; - case 2: - -#ifdef DWS74_BUG - if (scaler==-2) { - value=(uint32_t)uvalue; - } else { - value=(int16_t)uvalue; - } -#else - value=(int16_t)uvalue; -#endif - break; - case 3: - case 4: - - value=(int32_t)uvalue; - break; - case 5: - case 6: - case 7: - case 8: - - value=(int64_t)uvalue; - break; - } - } else { - - value=uvalue; - } - - } else { - if (!(type&0xf0)) { - - - - if (len==9) { - - cp++; - uint32_t s1,s2; - s1=*cp<<16|*(cp+1)<<8|*(cp+2); - cp+=4; - s2=*cp<<16|*(cp+1)<<8|*(cp+2); - sprintf(&meter_id[index][0],"%u-%u",s1,s2); - } else { - - char *str=&meter_id[index][0]; - for (type=0; type= 'A' && chr <= 'F') rVal = chr + 10 - 'A'; - } - return rVal; -} - -uint8_t sb_counter; - - -double CharToDouble(const char *str) -{ - - char strbuf[24]; - - strlcpy(strbuf, str, sizeof(strbuf)); - char *pt = strbuf; - while ((*pt != '\0') && isblank(*pt)) { pt++; } - - signed char sign = 1; - if (*pt == '-') { sign = -1; } - if (*pt == '-' || *pt=='+') { pt++; } - - double left = 0; - if (*pt != '.') { - left = atoi(pt); - while (isdigit(*pt)) { pt++; } - } - - double right = 0; - if (*pt == '.') { - pt++; - right = atoi(pt); - while (isdigit(*pt)) { - pt++; - right /= 10.0; - } - } - - double result = left + right; - if (sign < 0) { - return -result; - } - return result; -} - - - -void ebus_esc(uint8_t *ebus_buffer, unsigned char len) { - short count,count1; - for (count=0; countavailable()) { - meter_ss[meters]->read(); - } -} - - -void sml_shift_in(uint32_t meters,uint32_t shard) { - uint32_t count; - if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='M' && meter_desc_p[meters].type!='p') { - - for (count=0; countread(); - - if (meter_desc_p[meters].type=='o') { - smltbuf[meters][SML_BSIZ-1]=iob&0x7f; - } else if (meter_desc_p[meters].type=='s') { - 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' || meter_desc_p[meters].type=='M') { - smltbuf[meters][meter_spos[meters]] = iob; - meter_spos[meters]++; - if (meter_spos[meters]>=9) { - SML_Decode(meters); - sml_empty_receiver(meters); - meter_spos[meters]=0; - } - } else if (meter_desc_p[meters].type=='p') { - smltbuf[meters][meter_spos[meters]] = iob; - meter_spos[meters]++; - if (meter_spos[meters]>=7) { - SML_Decode(meters); - sml_empty_receiver(meters); - meter_spos[meters]=0; - } - } else { - if (iob==EBUS_SYNC) { - - - if (meter_spos[meters]>4+5) { - - uint8_t tlen=smltbuf[meters][4]+5; - - if (smltbuf[meters][tlen]=ebus_CalculateCRC(smltbuf[meters],tlen)) { - ebus_esc(smltbuf[meters],tlen); - SML_Decode(meters); - } else { - - - } - } - meter_spos[meters]=0; - return; - } - smltbuf[meters][meter_spos[meters]] = iob; - meter_spos[meters]++; - if (meter_spos[meters]>=SML_BSIZ) { - meter_spos[meters]=0; - } - } - sb_counter++; - if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='M' && meter_desc_p[meters].type!='p') SML_Decode(meters); -} - - - -void SML_Poll(void) { -uint32_t meters; - - for (meters=0; metersavailable()) { - sml_shift_in(meters,0); - } - } - } -} - - -void SML_Decode(uint8_t index) { - const char *mp=(const char*)meter_p; - int8_t mindex; - uint8_t *cp; - uint8_t dindex=0,vindex=0; - delay(0); - while (mp != NULL) { - - - - mindex=((*mp)&7)-1; - - if (mindex<0 || mindex>=meters_used) mindex=0; - mp+=2; - if (*mp=='=' && *(mp+1)=='h') { - mp = strchr(mp, '|'); - if (mp) mp++; - continue; - } - - if (index!=mindex) goto nextsect; - - - cp=&smltbuf[mindex][0]; - - - if (*mp=='=') { - - mp++; - - if (*mp=='m' && !sb_counter) { - - - mp++; - while (*mp==' ') mp++; - - double dvar; - uint8_t opr; - uint32_t ind; - ind=atoi(mp); - while (*mp>='0' && *mp<='9') mp++; - if (ind<1 || ind>SML_MAX_VARS) ind=1; - dvar=meter_vars[ind-1]; - for (uint8_t p=0;p<5;p++) { - if (*mp=='@') { - - meter_vars[vindex]=dvar; - mp++; - SML_Immediate_MQTT((const char*)mp,vindex,mindex); - break; - } - opr=*mp; - mp++; - uint8_t iflg=0; - if (*mp=='#') { - iflg=1; - mp++; - } - ind=atoi(mp); - while (*mp>='0' && *mp<='9') mp++; - if (ind<1 || ind>SML_MAX_VARS) ind=1; - switch (opr) { - case '+': - if (iflg) dvar+=ind; - else dvar+=meter_vars[ind-1]; - break; - case '-': - if (iflg) dvar-=ind; - else dvar-=meter_vars[ind-1]; - break; - case '*': - if (iflg) dvar*=ind; - else dvar*=meter_vars[ind-1]; - break; - case '/': - if (iflg) dvar/=ind; - else dvar/=meter_vars[ind-1]; - break; - } - while (*mp==' ') mp++; - if (*mp=='@') { - - meter_vars[vindex]=dvar; - mp++; - SML_Immediate_MQTT((const char*)mp,vindex,mindex); - break; - } - } - } else if (*mp=='d') { - - if (dindex='0' && *mp<='9') mp++; - if (ind<1 || ind>SML_MAX_VARS) ind=1; - uint32_t delay=atoi(mp)*1000; - uint32_t dtime=millis()-dtimes[dindex]; - if (dtime>delay) { - - dtimes[dindex]=millis(); - double vdiff = meter_vars[ind-1]-dvalues[dindex]; - dvalues[dindex]=meter_vars[ind-1]; - meter_vars[vindex]=(double)360000.0*vdiff/((double)dtime/10000.0); - - mp=strchr(mp,'@'); - if (mp) { - mp++; - SML_Immediate_MQTT((const char*)mp,vindex,mindex); - } - } - dindex++; - } - } else if (*mp=='h') { - - mp = strchr(mp, '|'); - if (mp) mp++; - continue; - } - } else { - - uint8_t found=1; - 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++) { - found=0; - } - } else { - if (meter_desc_p[mindex].type=='s') { - - uint8_t val = hexnibble(*mp++) << 4; - val |= hexnibble(*mp++); - if (val!=*cp++) { - found=0; - } - } else { - - - if (*mp=='x' && *(mp+1)=='x') { - - mp+=2; - cp++; - } else if (!strncmp(mp,"UUuuUUuu",8)) { - uint32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); - ebus_dval=val; - mbus_dval=val; - mp+=8; - cp+=4; - } else if (*mp=='U' && *(mp+1)=='U' && *(mp+2)=='u' && *(mp+3)=='u'){ - uint16_t val = cp[1]|(cp[0]<<8); - mbus_dval=val; - ebus_dval=val; - mp+=4; - cp+=2; - } else if (!strncmp(mp,"SSssSSss",8)) { - int32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); - ebus_dval=val; - mbus_dval=val; - mp+=8; - cp+=4; - } else if (*mp=='u' && *(mp+1)=='u' && *(mp+2)=='U' && *(mp+3)=='U'){ - uint16_t val = cp[0]|(cp[1]<<8); - mbus_dval=val; - ebus_dval=val; - mp+=4; - cp+=2; - } else if (*mp=='u' && *(mp+1)=='u') { - uint8_t val = *cp++; - mbus_dval=val; - ebus_dval=val; - mp+=2; - } else if (*mp=='s' && *(mp+1)=='s' && *(mp+2)=='S' && *(mp+3)=='S') { - int16_t val = *cp|(*(cp+1)<<8); - mbus_dval=val; - ebus_dval=val; - mp+=4; - cp+=2; - } else if (*mp=='S' && *(mp+1)=='S' && *(mp+2)=='s' && *(mp+3)=='s') { - int16_t val = cp[1]|(cp[0]<<8); - mbus_dval=val; - ebus_dval=val; - mp+=4; - cp+=2; - } - else if (*mp=='s' && *(mp+1)=='s') { - int8_t val = *cp++; - mbus_dval=val; - ebus_dval=val; - mp+=2; - } - else if (!strncmp(mp,"ffffffff",8)) { - uint32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); - float *fp=(float*)&val; - ebus_dval=*fp; - mbus_dval=*fp; - mp+=8; - cp+=4; - } - else if (!strncmp(mp,"FFffFFff",8)) { - - uint32_t val= (cp[1]<<0)|(cp[0]<<8)|(cp[3]<<16)|(cp[2]<<24); - float *fp=(float*)&val; - ebus_dval=*fp; - mbus_dval=*fp; - mp+=8; - cp+=4; - } - else if (!strncmp(mp,"eeeeee",6)) { - uint32_t val=(cp[0]<<16)|(cp[1]<<8)|(cp[2]<<0); - mbus_dval=val; - mp+=6; - cp+=3; - } - else if (!strncmp(mp,"vvvvvv",6)) { - mbus_dval=(float)((cp[0]<<8)|(cp[1])) + ((float)cp[2]/10.0); - mp+=6; - cp+=3; - } - else if (!strncmp(mp,"cccccc",6)) { - mbus_dval=(float)((cp[0]<<8)|(cp[1])) + ((float)cp[2]/100.0); - mp+=6; - cp+=3; - } - else if (!strncmp(mp,"pppp",4)) { - mbus_dval=(float)((cp[0]<<8)|cp[1]); - mp+=4; - cp+=2; - } - else { - uint8_t val = hexnibble(*mp++) << 4; - val |= hexnibble(*mp++); - if (val!=*cp++) { - found=0; - } - } - } - } - } - if (found) { - - mp++; -#ifdef ED300L - g_mindex=mindex; -#endif - if (*mp=='#') { - - mp++; - if (meter_desc_p[mindex].type=='o') { - for (uint8_t p=0;p>=shift; - ebus_dval&=1; - mp+=2; - } - if (*mp=='i') { - - 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; - - mp++; - } else { - if (meter_desc_p[mindex].type=='p') { - uint8_t crc = SML_PzemCrc(&smltbuf[mindex][0],6); - if (crc!=smltbuf[mindex][6]) goto nextsect; - dval=mbus_dval; - } else { - dval=ebus_dval; - } - } - - } -#ifdef USE_SML_MEDIAN_FILTER - if (meter_desc_p[mindex].flag&16) { - meter_vars[vindex]=sml_median(&sml_mf[vindex],dval); - } else { - meter_vars[vindex]=dval; - } -#else - meter_vars[vindex]=dval; -#endif - - - double fac=CharToDouble((char*)mp); - meter_vars[vindex]/=fac; - SML_Immediate_MQTT((const char*)mp,vindex,mindex); - } - } - } -nextsect: - - if (vindex=meters_used) lastmind=0; - while (mp != NULL) { - - mindex=((*mp)&7)-1; - if (mindex<0 || mindex>=meters_used) mindex=0; - mp+=2; - if (*mp=='=' && *(mp+1)=='h') { - mp+=2; - - if (json) { - mp = strchr(mp, '|'); - if (mp) mp++; - continue; - } - - uint8_t i; - for (i=0;isml_counters[index].sml_debounce) { - RtcSettings.pulse_counter[index]++; - InjektCounterValue(sml_counters[index].sml_cnt_old_state,RtcSettings.pulse_counter[index]); - } - } else { - - sml_counters[index].sml_counter_ltime=millis(); - } -} - -void SML_CounterUpd1(void) { - SML_CounterUpd(0); -} - -void SML_CounterUpd2(void) { - SML_CounterUpd(1); -} - -void SML_CounterUpd3(void) { - SML_CounterUpd(2); -} - -void SML_CounterUpd4(void) { - SML_CounterUpd(3); -} - -#ifdef USE_SCRIPT -struct METER_DESC script_meter_desc[MAX_METERS]; -uint8_t *script_meter; -#endif - -#ifndef METER_DEF_SIZE -#define METER_DEF_SIZE 3000 -#endif - -bool Gpio_used(uint8_t gpiopin) { - for (uint16_t i=0;iM",-2,0); - if (meter_script==99) { - - if (script_meter) free(script_meter); - script_meter=0; - uint8_t *tp=0; - uint16_t index=0; - uint8_t section=0; - uint8_t srcpin=0; - char *lp=glob_script_mem.scriptptr; - sml_send_blocks=0; - while (lp) { - if (!section) { - if (*lp=='>' && *(lp+1)=='M') { - lp+=2; - meters_used=strtol(lp,0,10); - section=1; - uint32_t mlen=0; - for (uint32_t cnt=0;cnt') { - if (*(tp-1)=='|') *(tp-1)=0; - break; - } - if (*lp=='+') { - - - lp++; - index=*lp&7; - lp+=2; - if (index<1 || index>meters_used) goto next_line; - index--; - srcpin=strtol(lp,&lp,10); - if (Gpio_used(srcpin)) { - AddLog_P(LOG_LEVEL_INFO, PSTR("gpio rx double define!")); -dddef_exit: - if (script_meter) free(script_meter); - script_meter=0; - meters_used=METERS_USED; - goto init10; - } - script_meter_desc[index].srcpin=srcpin; - if (*lp!=',') goto next_line; - lp++; - script_meter_desc[index].type=*lp; - lp+=2; - script_meter_desc[index].flag=strtol(lp,&lp,10); - if (*lp!=',') goto next_line; - lp++; - script_meter_desc[index].params=strtol(lp,&lp,10); - if (*lp!=',') goto next_line; - lp++; - script_meter_desc[index].prefix[7]=0; - for (uint32_t cnt=0; cnt<8; cnt++) { - if (*lp==SCRIPT_EOL || *lp==',') { - script_meter_desc[index].prefix[cnt]=0; - break; - } - script_meter_desc[index].prefix[cnt]=*lp++; - } - if (*lp==',') { - lp++; - script_meter_desc[index].trxpin=strtol(lp,&lp,10); - if (Gpio_used(script_meter_desc[index].trxpin)) { - AddLog_P(LOG_LEVEL_INFO, PSTR("gpio tx double define!")); - goto dddef_exit; - } - 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; cntmeters_used) goto next_line; - while (1) { - if (*lp==SCRIPT_EOL) { - if (*(tp-1)!='|') *tp++='|'; - goto next_line; - } - *tp++=*lp++; - index++; - if (index>=METER_DEF_SIZE) break; - } - } - - } - -next_line: - if (*lp==SCRIPT_EOL) { - lp++; - } else { - lp = strchr(lp, SCRIPT_EOL); - if (!lp) break; - lp++; - } - } - *tp=0; - meter_desc_p=script_meter_desc; - meter_p=script_meter; - } -#endif - -init10: - typedef void (*function)(); - function counter_callbacks[] = {SML_CounterUpd1,SML_CounterUpd2,SML_CounterUpd3,SML_CounterUpd4}; - uint8_t cindex=0; - - for (byte i = 0; i < MAX_COUNTERS; i++) { - RtcSettings.pulse_counter[i]=Settings.pulse_counter[i]; - sml_counters[i].sml_cnt_last_ts=millis(); - } - for (uint8_t meters=0; metersbegin(meter_desc_p[meters].params)) { - meter_ss[meters]->flush(); - } - if (meter_ss[meters]->hardwareSerial()) { - if (meter_desc_p[meters].type=='M') { - Serial.begin(meter_desc_p[meters].params, SERIAL_8E1); - } - ClaimSerial(); - } - - } - } - -} - - -#ifdef USE_SML_SCRIPT_CMD -uint32_t SML_SetBaud(uint32_t meter, uint32_t br) { - if (meter<1 || meter>meters_used) return 0; - meter--; - if (!meter_ss[meter]) return 0; - if (meter_ss[meter]->begin(br)) { - meter_ss[meter]->flush(); - } - if (meter_ss[meter]->hardwareSerial()) { - if (meter_desc_p[meter].type=='M') { - Serial.begin(br, SERIAL_8E1); - } - } - return 1; -} - -uint32_t SML_Status(uint32_t meter) { - if (meter<1 || meter>meters_used) return 0; - meter--; -#ifdef ED300L - return sml_status[meter]; -#else - return 0; -#endif -} - - -uint32_t SML_Write(uint32_t meter,char *hstr) { - if (meter<1 || meter>meters_used) return 0; - meter--; - if (!meter_ss[meter]) return 0; - SML_Send_Seq(meter,hstr); - return 1; -} -#endif - - -void SetDBGLed(uint8_t srcpin, uint8_t ledpin) { - pinMode(ledpin, OUTPUT); - if (digitalRead(srcpin)) { - digitalWrite(ledpin,LOW); - } else { - digitalWrite(ledpin,HIGH); - } -} - - -void SML_Counter_Poll(void) { -uint16_t meters,cindex=0; -uint32_t ctime=millis(); - - for (meters=0; meters0) { - if (ctime-sml_counters[cindex].sml_cnt_last_ts>meter_desc_p[meters].params) { - sml_counters[cindex].sml_cnt_last_ts=ctime; - - if (meter_desc_p[meters].flag&2) { - -#ifdef ANALOG_OPTO_SENSOR - if (ads1115_up) { - int16_t val = adc.read_sample(); - if (val>sml_counters[cindex].ana_max) sml_counters[cindex].ana_max=val; - if (val10) { - sml_counters[cindex].sml_cnt_last_ts=ctime; -#ifdef DEBUG_CNT_LED1 - if (cindex==0) SetDBGLed(meter_desc_p[meters].srcpin,DEBUG_CNT_LED1); -#endif -#ifdef DEBUG_CNT_LED2 - if (cindex==1) SetDBGLed(meter_desc_p[meters].srcpin,DEBUG_CNT_LED2); -#endif - } - } - cindex++; - } - } -} - -#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; - } - } - } -} - -void SML_Check_Send(void) { - sml_100ms_cnt++; - char *cp; - for (uint32_t cnt=sml_desc_cnt; cnt=0 && script_meter_desc[cnt].txmem) { - if ((sml_100ms_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; - sml_desc_cnt++; - } - cp=SML_Get_Sequence(script_meter_desc[cnt].txmem,script_meter_desc[cnt].index); - - } else { - cp=script_meter_desc[cnt].txmem; - - sml_desc_cnt++; - } - - SML_Send_Seq(cnt,cp); - if (sml_desc_cnt>=meters_used) { - sml_desc_cnt=0; - } - break; - } - } else { - sml_desc_cnt++; - } - - if (sml_desc_cnt>=meters_used) { - sml_desc_cnt=0; - } - } -} - -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; -} - - -void SML_Send_Seq(uint32_t meter,char *seq) { - uint8_t sbuff[32]; - uint8_t *ucp=sbuff,slen=0; - 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' || script_meter_desc[meter].type=='M') { - *ucp++=0; - *ucp++=2; - - uint16_t crc = MBUS_calculateCRC(sbuff,6); - *ucp++=lowByte(crc); - *ucp++=highByte(crc); - slen+=4; - } - if (script_meter_desc[meter].type=='o') { - for (uint32_t cnt=0;cntwrite(sbuff,slen); -} -#endif - -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) { - crc >>= 1; - crc ^= 0xA001; - } else { - crc >>= 1; - } - } - } - return crc; -} - -uint8_t SML_PzemCrc(uint8_t *data, uint8_t len) { - uint16_t crc = 0; - for (uint32_t i = 0; i < len; i++) crc += *data++; - return (uint8_t)(crc & 0xFF); -} - - -uint8_t CalcEvenParity(uint8_t data) { -uint8_t parity=0; - - while(data) { - parity^=(data &1); - data>>=1; - } - return parity; -} -# 2334 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_53_sml.ino" -bool XSNS_53_cmd(void) { - bool serviced = true; - if (XdrvMailbox.data_len > 0) { - char *cp=XdrvMailbox.data; - if (*cp=='d') { - - cp++; - uint8_t index=atoi(cp); - if ((index&7)>meters_used) index=1; - if (index>0 && meter_desc_p[(index&7)-1].type=='c') { - index=0; - } - dump2log=index; - ResponseTime_P(PSTR(",\"SML\":{\"CMD\":\"dump: %d\"}}"),dump2log); - } else if (*cp=='c') { - - cp++; - uint8_t index=*cp&7; - if (index<1 || index>MAX_COUNTERS) index=1; - cp++; - while (*cp==' ') cp++; - if (isdigit(*cp)) { - uint32_t cval=atoi(cp); - while (isdigit(*cp)) cp++; - RtcSettings.pulse_counter[index-1]=cval; - uint8_t cindex=0; - for (uint8_t meters=0; metersaddress, INA226_REG_CALIBRATION, si->calibrationValue); - -} - - - - - - -bool Ina226TestPresence(uint8_t device) -{ - - - - uint16_t config = I2cRead16( slaveInfo[device].address, INA226_REG_CONFIG ); - - - if (config != slaveInfo[device].config) - return false; - - return true; - -} - -void Ina226ResetActive(void) -{ - Ina226SlaveInfo_t *p = slaveInfo; - - for (uint32_t i = 0; i < INA226_MAX_ADDRESSES; i++) { - p = &slaveInfo[i]; - - uint8_t addr = p->address; - if (addr) { - I2cResetActive(addr); - } - } -} - - - - - -void Ina226Init() -{ - uint32_t i; - - slavesFound = 0; - - Ina226SlaveInfo_t *p = slaveInfo; -# 215 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_54_ina226.ino" - for (i = 0; i < 4; i++){ - *p = {0}; - } - - - - - - for (i = 0; i < INA226_MAX_ADDRESSES; i++){ - uint8_t addr = pgm_read_byte(probeAddresses + i); - - if (I2cActive(addr)) { continue; } - - - - - if (!Settings.ina226_i_fs[i]) - continue; - - - - - - - if (!I2cWrite16( addr, INA226_REG_CONFIG, INA226_CONFIG_RESET)){ - - AddLog_P2( LOG_LEVEL_DEBUG, "No INA226 at address: %02X", addr); - continue; - } - - - - uint16_t config = I2cRead16( addr, INA226_REG_CONFIG ); - - - if (INA226_RES_CONFIG != config) - continue; - - - config = INA226_DEF_CONFIG; - - - if (!I2cWrite16( addr, INA226_REG_CONFIG, config)) - continue; - - - p = &slaveInfo[i]; - - p->address = addr; - - p->config = config; - - - p->i_lsb = (((float) Settings.ina226_i_fs[i])/10.0f)/32768.0f; - - - - uint32_t r_shunt_uohms = _expand_r_shunt(Settings.ina226_r_shunt[i]); - - - - p->calibrationValue = ((uint16_t) (0.00512/(p->i_lsb * r_shunt_uohms/1000000.0f))); - - p->present = true; - - - Ina226SetCalibration(i); - - I2cSetActiveFound(addr, Ina226Str); - - slavesFound++; - } -} - - - - - -float Ina226ReadBus_v(uint8_t device) -{ - uint8_t addr = slaveInfo[device].address; - int16_t reg_bus_v = I2cReadS16( addr, INA226_REG_BUSVOLTAGE); - - float result = ((float) reg_bus_v) * 0.00125f; - - return result; - -} - - - - - -float Ina226ReadShunt_i(uint8_t device) -{ - uint8_t addr = slaveInfo[device].address; - int16_t reg_shunt_i = I2cReadS16( addr, INA226_REG_CURRENT); - - float result = ((float) reg_shunt_i) * slaveInfo[device].i_lsb; - - return result; -} - - - - - -float Ina226ReadPower_w(uint8_t device) -{ - uint8_t addr = slaveInfo[device].address; - int16_t reg_shunt_i = I2cReadS16( addr, INA226_REG_POWER); - - float result = ((float) reg_shunt_i) * (slaveInfo[device].i_lsb * 25.0); - - return result; -} - - - - - - -void Ina226Read(uint8_t device) -{ - - voltages[device] = Ina226ReadBus_v(device); - currents[device] = Ina226ReadShunt_i(device); - powers[device] = Ina226ReadPower_w(device); - - - - -} - - - - - -void Ina226EverySecond() -{ - - for (uint8_t device = 0; device < INA226_MAX_ADDRESSES; device++){ - - if (slavesFound && slaveInfo[device].present && Ina226TestPresence(device)){ - Ina226Read(device); - } - else { - powers[device] = currents[device] = voltages[device] = 0.0f; - - - - - - - slaveInfo[device].present = false; - } - } -} - - - - - -bool Ina226CommandSensor() -{ - bool serviced = true; - bool show_config = false; - char param_str[64]; - char *cp, *params[4]; - uint8_t i, param_count, device, p1 = XdrvMailbox.payload; - uint32_t r_shunt_uohms; - uint16_t compact_r_shunt_uohms; - - - - - - if (XdrvMailbox.data_len > 62){ - return false; - } - - strncpy(param_str, XdrvMailbox.data, XdrvMailbox.data_len + 1); - param_str[XdrvMailbox.data_len] = 0; - - - for (cp = param_str, i = 0, param_count = 0; *cp && (i < XdrvMailbox.data_len + 1) && (param_count <= 3); i++) - if (param_str[i] == ' ' || param_str[i] == ',' || param_str[i] == 0){ - param_str[i] = 0; - params[param_count] = cp; - - param_count++; - cp = param_str + i + 1; - } - - - if (p1 < 10 || p1 >= 50){ - - switch (p1){ - case 1: - Ina226ResetActive(); - Ina226Init(); - Response_P(PSTR("{\"Sensor54-Command-Result\":{\"SlavesFound\":%d}}"),slavesFound); - break; - - case 2: - restart_flag = 2; - Response_P(PSTR("{\"Sensor54-Command-Result\":{\"Restart_flag\":%d}}"),restart_flag); - break; - - default: - serviced = false; - } - } - else if (p1 < 50){ - - device = (p1 / 10) - 1; - switch (p1 % 10){ - case 0: - show_config = true; - break; - - case 1: - r_shunt_uohms = (uint32_t) ((CharToFloat(params[1])) * 1000000.0f); - - - - if (r_shunt_uohms > 32767){ - uint32_t r_shunt_mohms = r_shunt_uohms/1000UL; - Settings.ina226_r_shunt[device] = (uint16_t) (r_shunt_mohms | 0x8000); - } - else - Settings.ina226_r_shunt[device] = (uint16_t) r_shunt_uohms; - - - show_config = true; - break; - - case 2: - Settings.ina226_i_fs[device] = (uint16_t) ((CharToFloat(params[1])) * 10.0f); - - show_config = true; - break; - - - default: - serviced = false; - break; - } - } - else - serviced = false; - - if (show_config) { - char shunt_r_str[16]; - char fs_i_str[16]; - - - r_shunt_uohms = _expand_r_shunt(Settings.ina226_r_shunt[device]); - dtostrfd(((float)r_shunt_uohms)/1000000.0f, 6, shunt_r_str); - - dtostrfd(((float)Settings.ina226_i_fs[device])/10.0f, 1, fs_i_str); - - Response_P(PSTR("{\"Sensor54-device-settings-%d\":{\"SHUNT_R\":%s,\"FS_I\":%s}}"), - device + 1, shunt_r_str, fs_i_str); - } - - return serviced; -} - - - - - -#ifdef USE_WEBSERVER -const char HTTP_SNS_INA226_DATA[] PROGMEM = - "{s}%s " D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" - "{s}%s " D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" - "{s}%s " D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"; -#endif - -void Ina226Show(bool json) -{ - int i, num_found; - for (num_found = 0, i = 0; i < INA226_MAX_ADDRESSES; i++) { - - if (!slaveInfo[i].present) - continue; - - num_found++; - - char voltage[16]; - dtostrfd(voltages[i], Settings.flag2.voltage_resolution, voltage); - char current[16]; - dtostrfd(currents[i], Settings.flag2.current_resolution, current); - char power[16]; - dtostrfd(powers[i], Settings.flag2.wattage_resolution, power); - char name[16]; - snprintf_P(name, sizeof(name), PSTR("INA226%c%d"),IndexSeparator(), i + 1); - - - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"Id\":%d,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s,\"" D_JSON_POWERUSAGE "\":%s}"), - name, i, voltage, current, power); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_VOLTAGE, voltage); - DomoticzSensor(DZ_CURRENT, current); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_INA226_DATA, name, voltage, name, current, name, power); -#endif - } - - } - -} -# 546 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_54_ina226.ino" -bool Xsns54(byte callback_id) -{ - if (!I2cEnabled(XI2C_35)) { return false; } - - - bool result = false; - - - switch (callback_id) { - case FUNC_EVERY_SECOND: - Ina226EverySecond(); - break; - case FUNC_JSON_APPEND: - Ina226Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Ina226Show(0); - break; -#endif - case FUNC_COMMAND_SENSOR: - if (XSNS_54 == XdrvMailbox.index) { - result = Ina226CommandSensor(); - } - break; - case FUNC_INIT: - Ina226Init(); - break; - } - - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_55_hih_series.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_55_hih_series.ino" -#ifdef USE_I2C -#ifdef USE_HIH6 -# 33 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_55_hih_series.ino" -#define XSNS_55 55 -#define XI2C_36 36 - -#define HIH6_ADDR 0x27 - -struct HIH6 { - float temperature = 0; - float humidity = 0; - uint8_t valid = 0; - uint8_t type = 0; - char types[4] = "HIH"; -} Hih6; - -bool Hih6Read(void) -{ - Wire.beginTransmission(HIH6_ADDR); - if (Wire.endTransmission() != 0) { return false; } - - delay(40); - - uint8_t data[4]; - Wire.requestFrom(HIH6_ADDR, 4); - if (4 == Wire.available()) { - data[0] = Wire.read(); - data[1] = Wire.read(); - data[2] = Wire.read(); - data[3] = Wire.read(); - } else { return false; } - - - - Hih6.humidity = ConvertHumidity(((float)(((data[0] & 0x3F) << 8) | data[1]) * 100.0) / 16383.0); - - int temp = ((data[2] << 8) | (data[3] & 0xFC)) / 4; - Hih6.temperature = ConvertTemp(((float)temp / 16384.0) * 165.0 - 40.0); - - Hih6.valid = SENSOR_MAX_MISS; - return true; -} - - - -void Hih6Detect(void) -{ - if (I2cActive(HIH6_ADDR)) { return; } - - if (uptime < 2) { delay(20); } - Hih6.type = Hih6Read(); - if (Hih6.type) { - I2cSetActiveFound(HIH6_ADDR, Hih6.types); - } -} - -void Hih6EverySecond(void) -{ - if (uptime &1) { - - if (!Hih6Read()) { - AddLogMissed(Hih6.types, Hih6.valid); - } - } -} - -void Hih6Show(bool json) -{ - if (Hih6.valid) { - TempHumDewShow(json, (0 == tele_period), Hih6.types, Hih6.temperature, Hih6.humidity); - } -} - - - - - -bool Xsns55(uint8_t function) -{ - if (!I2cEnabled(XI2C_36)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Hih6Detect(); - } - else if (Hih6.type) { - switch (function) { - case FUNC_EVERY_SECOND: - Hih6EverySecond(); - break; - case FUNC_JSON_APPEND: - Hih6Show(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Hih6Show(0); - break; - #endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_56_hpma.ino" -# 21 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_56_hpma.ino" -#ifdef USE_HPMA -# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_56_hpma.ino" -#define XSNS_56 56 - -#include -#include - -TasmotaSerial *HpmaSerial; -HPMA115S0 *hpma115S0; - -uint8_t hpma_type = 1; -uint8_t hpma_valid = 0; - -struct hpmadata { - unsigned int pm10; - unsigned int pm2_5; -} hpma_data; - - - -void HpmaSecond(void) -{ - unsigned int pm2_5, pm10; - - - - - if (hpma115S0->ReadParticleMeasurement(&pm2_5, &pm10)) { - hpma_data.pm2_5 = pm2_5; - hpma_data.pm10 = pm10; - hpma_valid = 1; - } - -} - -void HpmaInit(void) -{ - hpma_type = 0; - if (pin[GPIO_HPMA_RX] < 99 && pin[GPIO_HPMA_TX] < 99) { - HpmaSerial = new TasmotaSerial(pin[GPIO_HPMA_RX], pin[GPIO_HPMA_TX], 1); - hpma115S0 = new HPMA115S0(*HpmaSerial); - - if (HpmaSerial->begin(9600)) { - if (HpmaSerial->hardwareSerial()) { - ClaimSerial(); - } - hpma_type = 1; - hpma115S0->Init(); - hpma115S0->StartParticleMeasurement(); - } - } -} - -#ifdef USE_WEBSERVER -const char HTTP_HPMA_SNS[] PROGMEM = - "{s}HPMA " D_ENVIRONMENTAL_CONCENTRATION "2.5 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}HPMA " D_ENVIRONMENTAL_CONCENTRATION "10 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"; -#endif - -void HpmaShow(bool json) -{ - if (hpma_valid) { - char pm10[33]; - snprintf_P(pm10, 33, PSTR("%d"), hpma_data.pm10); - char pm2_5[33]; - snprintf_P(pm2_5, 33, PSTR("%d"), hpma_data.pm2_5); - - if (json) { - ResponseAppend_P(PSTR(",\"HPMA\":{\"PM2.5\":%d,\"PM10\":%d}"), hpma_data.pm2_5, hpma_data.pm10); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_VOLTAGE, pm2_5); - DomoticzSensor(DZ_CURRENT, pm10); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_HPMA_SNS, pm2_5, pm10); -#endif - } - } -} - - - - - -bool Xsns56(uint8_t function) -{ - bool result = false; - - if (hpma_type) { - switch (function) { - case FUNC_INIT: - HpmaInit(); - break; - case FUNC_EVERY_SECOND: - HpmaSecond(); - break; - case FUNC_COMMAND_SENSOR: - if (XSNS_56 == XdrvMailbox.index) { - return true; - } - break; - case FUNC_JSON_APPEND: - HpmaShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - HpmaShow(0); - break; -#endif - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_57_tsl2591.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_57_tsl2591.ino" -#ifdef USE_I2C -#ifdef USE_TSL2591 - - - - - - - -#define XSNS_57 57 -#define XI2C_40 40 - -#define TSL2591_ADDRESS 0x29 - -#include - -Adafruit_TSL2591 tsl = Adafruit_TSL2591(); - -uint8_t tsl2591_type = 0; -uint8_t tsl2591_valid = 0; -float tsl2591_lux = 0; - -void Tsl2591Init(void) -{ - - if (I2cSetDevice(0x29)) { - if (tsl.begin()) { - tsl.setGain(TSL2591_GAIN_MED); - tsl.setTiming(TSL2591_INTEGRATIONTIME_300MS); - tsl2591_type = 1; - I2cSetActiveFound(TSL2591_ADDRESS, "TSL2591"); - } - } -} - -bool Tsl2591Read(void) -{ - uint32_t lum = tsl.getFullLuminosity(); - uint16_t ir, full; - ir = lum >> 16; - full = lum & 0xFFFF; - tsl2591_lux = tsl.calculateLux(full, ir); - tsl2591_valid = 1; -} - -void Tsl2591EverySecond(void) -{ - Tsl2591Read(); -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_TSL2591[] PROGMEM = - "{s}TSL2591 " D_ILLUMINANCE "{m}%s " D_UNIT_LUX "{e}"; -#endif - -void Tsl2591Show(bool json) -{ - if (tsl2591_valid) { - char lux_str[10]; - dtostrf(tsl2591_lux, sizeof(lux_str)-1, 3, lux_str); - if (json) { - ResponseAppend_P(PSTR(",\"TSL2591\":{\"" D_JSON_ILLUMINANCE "\":%s}"), lux_str); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, tsl2591_lux); } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TSL2591, lux_str); -#endif - } - } -} - - - - - -bool Xsns57(uint8_t function) -{ - if (!I2cEnabled(XI2C_40)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Tsl2591Init(); - } - else if (tsl2591_type) { - switch (function) { - case FUNC_EVERY_SECOND: - Tsl2591EverySecond(); - break; - case FUNC_JSON_APPEND: - Tsl2591Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Tsl2591Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_58_dht12.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_58_dht12.ino" -#ifdef USE_I2C -#ifdef USE_DHT12 - - - - - - -#define XSNS_58 58 -#define XI2C_41 41 - -#define DHT12_ADDR 0x5C - -struct DHT12 { - float temperature = NAN; - float humidity = NAN; - uint8_t valid = 0; - uint8_t count = 0; - char name[6] = "DHT12"; -} Dht12; - -bool Dht12Read(void) -{ - if (Dht12.valid) { Dht12.valid--; } - - Wire.beginTransmission(DHT12_ADDR); - Wire.write(0); - if (Wire.endTransmission() != 0) { return false; } - - delay(50); - - Wire.requestFrom(DHT12_ADDR, 5); - delay(5); - uint8_t humidity = Wire.read(); - uint8_t humidityTenth = Wire.read(); - uint8_t temp = Wire.read(); - uint8_t tempTenth = Wire.read(); - uint8_t checksum = Wire.read(); - - Dht12.humidity = ConvertHumidity( (float) humidity + (float) humidityTenth/(float) 10.0 ); - Dht12.temperature = ConvertTemp( (float) temp + (float) tempTenth/(float) 10.0 ); - - if (isnan(Dht12.temperature) || isnan(Dht12.humidity)) { return false; } - - Dht12.valid = SENSOR_MAX_MISS; - return true; -} - - - -void Dht12Detect(void) -{ - if (I2cActive(DHT12_ADDR)) { return; } - - if (Dht12Read()) { - I2cSetActiveFound(DHT12_ADDR, Dht12.name); - Dht12.count = 1; - } -} - -void Dht12EverySecond(void) -{ - if (uptime &1) { - - if (!Dht12Read()) { - AddLogMissed(Dht12.name, Dht12.valid); - } - } -} - -void Dht12Show(bool json) -{ - if (Dht12.valid) { - TempHumDewShow(json, (0 == tele_period), Dht12.name, Dht12.temperature, Dht12.humidity); - } -} - - - - - -bool Xsns58(uint8_t function) -{ - if (!I2cEnabled(XI2C_41)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Dht12Detect(); - } - else if (Dht12.count) { - switch (function) { - case FUNC_EVERY_SECOND: - Dht12EverySecond(); - break; - case FUNC_JSON_APPEND: - Dht12Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Dht12Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_59_ds1624.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_59_ds1624.ino" -#ifdef USE_I2C -#ifdef USE_DS1624 - - - - - - - -#define XSNS_59 59 -#define XI2C_42 42 - -#define DS1624_MEM_REGISTER 0x17 -#define DS1624_CONF_REGISTER 0xAC -#define DS1624_TEMP_REGISTER 0xAA -#define DS1624_START_REGISTER 0xEE -#define DS1624_STOP_REGISTER 0x22 - -#define DS1621_COUNTER_REGISTER 0xA8 -#define DS1621_SLOPE_REGISTER 0xA9 - -#define DS1621_CFG_1SHOT (1<<0) -#define DS1621_CFG_DONE (1<<7) - -enum { - DS1624_TYPE_DS1624, - DS1624_TYPE_DS1621 -}; - -#define DS1624_MAX_SENSORS 8 - -bool ds1624_init = false; - -struct { - float value; - uint8_t type; - int errcnt; - int misscnt; - bool valid; - char name[9]; -} ds1624_sns[DS1624_MAX_SENSORS]; - -uint32_t DS1624_Idx2Addr(uint32_t idx) { - return 0x48 + idx; -} - -int DS1624_Restart(uint8_t config, uint32_t idx) { - uint32_t addr = DS1624_Idx2Addr(idx); - if ((config & 1) == 1) { - config &= ~(DS1621_CFG_DONE|DS1621_CFG_1SHOT); - I2cWrite8(addr, DS1624_CONF_REGISTER, config); - delay(10); - AddLog_P2(LOG_LEVEL_ERROR, "%s addr %x is reset, reconfig: %x", ds1624_sns[idx].name, addr, config); - } - I2cValidRead(addr, DS1624_START_REGISTER, 1); -} - -void DS1624_HotPlugUp(uint32_t idx) -{ - uint32_t addr = DS1624_Idx2Addr(idx); - - if (I2cActive(addr)) { return; } - if (!I2cSetDevice(addr)) { return; } - - uint8_t config; - if (I2cValidRead8(&config, addr, DS1624_CONF_REGISTER)) { - uint8_t tmp; - ds1624_sns[idx].type = (I2cValidRead8(&tmp, addr, DS1624_MEM_REGISTER)) ? DS1624_TYPE_DS1624 : DS1624_TYPE_DS1621; - - snprintf_P(ds1624_sns[idx].name, sizeof(ds1624_sns[idx].name), PSTR("DS162%c%c%d"), - (ds1624_sns[idx].type == DS1624_TYPE_DS1621) ? '1' : '4', IndexSeparator(), idx); - I2cSetActiveFound(addr, ds1624_sns[idx].name); - - ds1624_sns[idx].valid = true; - ds1624_sns[idx].errcnt = 0; - ds1624_sns[idx].misscnt = 0; - DS1624_Restart(config,idx); - AddLog_P2(LOG_LEVEL_INFO, "Hot Plug %s addr %x config: %x", ds1624_sns[idx].name, addr, config); - } -} - -void DS1624_HotPlugDown(int idx) -{ - uint32_t addr = DS1624_Idx2Addr(idx); - if (!I2cActive(addr)) { return; } - I2cResetActive(addr); - ds1624_sns[idx].valid = false; - AddLog_P2(LOG_LEVEL_INFO, "Hot UnPlug %s", ds1624_sns[idx].name); -} - -bool DS1624GetTemp(float *value, int idx) -{ - uint32_t addr = DS1624_Idx2Addr(idx); - - uint8_t config; - if (!I2cValidRead8(&config, addr, DS1624_CONF_REGISTER)) { - ds1624_sns[idx].misscnt++; - AddLog_P2(LOG_LEVEL_INFO, "%s device missing (errors: %i)", ds1624_sns[idx].name, ds1624_sns[idx].misscnt); - return false; - } - ds1624_sns[idx].misscnt=0; - if (config & (DS1621_CFG_1SHOT|DS1621_CFG_DONE)) { - ds1624_sns[idx].errcnt++; - AddLog_P2(LOG_LEVEL_INFO, "%s config error, restart... (errors: %i)", ds1624_sns[idx].name, ds1624_sns[idx].errcnt); - DS1624_Restart(config, idx); - return false; - } - - uint16_t t; - if (!I2cValidRead16(&t, DS1624_Idx2Addr(idx), DS1624_TEMP_REGISTER)) { return false; } - if (ds1624_sns[idx].type == DS1624_TYPE_DS1624) { - *value = ((float)(int8_t)(t>>8)) + ((t>>4)&0xf)*0.0625; - } else { - - *value = ((float)(int8_t)(t>>8)); - uint8_t remain; - if (!I2cValidRead8(&remain, addr, DS1621_COUNTER_REGISTER)) { return true; } - uint8_t perc; - if (!I2cValidRead8(&perc, addr, DS1621_SLOPE_REGISTER)) { return true; } - float fix=(float)(perc - remain)/(float)perc; - *value+=fix; - } - ds1624_sns[idx].errcnt=0; - config &= ~(DS1621_CFG_DONE); - I2cWrite8(addr, DS1624_CONF_REGISTER, config); - return true; -} - -void DS1624HotPlugScan(void) -{ - uint16_t t; - - for (uint32_t idx = 0; idx < DS1624_MAX_SENSORS; idx++) { - uint32_t addr = DS1624_Idx2Addr(idx); - if (I2cActive(addr) && !ds1624_sns[idx].valid) { - continue; - } - if (ds1624_sns[idx].valid) { - if ((ds1624_sns[idx].misscnt>2)||(ds1624_sns[idx].errcnt>2)) { - DS1624_HotPlugDown(idx); - continue; - } - } - DS1624_HotPlugUp(idx); - } -} - -void DS1624EverySecond(void) -{ - float t; - for (uint32_t i = 0; i < DS1624_MAX_SENSORS; i++) { - if (!ds1624_sns[i].valid) { continue; } - if (!DS1624GetTemp(&t, i)) { continue; } - ds1624_sns[i].value = ConvertTemp(t); - } -} - -void DS1624Show(bool json) -{ - char temperature[33]; - bool once = true; - - for (uint32_t i = 0; i < DS1624_MAX_SENSORS; i++) { - if (!ds1624_sns[i].valid) { continue; } - - dtostrfd(ds1624_sns[i].value, Settings.flag2.temperature_resolution, temperature); - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s}"), ds1624_sns[i].name, temperature); - if ((0 == tele_period) && once) { -#ifdef USE_DOMOTICZ - DomoticzSensor(DZ_TEMP, temperature); -#endif -#ifdef USE_KNX - KnxSensor(KNX_TEMPERATURE, ds1624_sns[i].value); -#endif - once = false; - } -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, ds1624_sns[i].name, temperature, TempUnit()); -#endif - } - } -} - - - - - -bool Xsns59(uint8_t function) -{ - if (!I2cEnabled(XI2C_42)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - if (!ds1624_init) { - memset(ds1624_sns, 0, sizeof(ds1624_sns)); - ds1624_init = true; - DS1624HotPlugScan(); - } - } - switch (function) { - case FUNC_HOTPLUG_SCAN: - DS1624HotPlugScan(); - break; - case FUNC_EVERY_SECOND: - DS1624EverySecond(); - break; - case FUNC_JSON_APPEND: - DS1624Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - DS1624Show(0); - break; -#endif - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_60_GPS.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_60_GPS.ino" -#ifdef USE_GPS -#if defined(ESP32) && defined(USE_FLOG) - #undef USE_FLOG - #warning FLOG deactivated on ESP32 -#endif -# 117 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_60_GPS.ino" -#define XSNS_60 60 - -#include "NTPServer.h" -#include "NTPPacket.h" - -#ifdef ESP32 -#include -#endif - - - - - -#define D_CMND_UBX "UBX" - -const char S_JSON_UBX_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_UBX "%s\":%d}"; - -const char kUBXTypes[] PROGMEM = "UBX"; - -#define UBX_LAT_LON_THRESHOLD 1000 - -#define UBX_SERIAL_BUFFER_SIZE 256 -#define UBX_TCP_PORT 1234 -#define NTP_MILLIS_OFFSET 50 - - - - - -const char UBLOX_INIT[] PROGMEM = { - - 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x24, - 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x2B, - 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x02,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x32, - 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x39, - 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x04,0x40, - 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x05,0x00,0x00,0x00,0x00,0x00,0x01,0x05,0x47, - - - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0xDC, - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0xB9, - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0xC0, - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0x92, - - - - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x13,0xBE, - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x14,0xC5, - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x21,0x00,0x01,0x00,0x00,0x00,0x00,0x32,0x97, - - - - - - -}; - -char UBX_name[4]; - -struct UBX_t { - const char UBX_HEADER[2] = { 0xB5, 0x62 }; - const char NAV_POSLLH_HEADER[2] = { 0x01, 0x02 }; - const char NAV_STATUS_HEADER[2] = { 0x01, 0x03 }; - const char NAV_TIME_HEADER[2] = { 0x01, 0x21 }; - - struct entry_t { - int32_t lat; - int32_t lon; - uint32_t time; - }; - - union { - entry_t values; - uint8_t bytes[sizeof(entry_t)]; - } rec_buffer; - - struct POLL_MSG { - uint8_t cls; - uint8_t id; - uint16_t zero; - }; - - struct NAV_POSLLH { - uint8_t cls; - uint8_t id; - uint16_t len; - uint32_t iTOW; - int32_t lon; - int32_t lat; - int32_t alt; - int32_t hMSL; - uint32_t hAcc; - uint32_t vAcc; - }; - - struct NAV_STATUS { - uint8_t cls; - uint8_t id; - uint16_t len; - uint32_t iTOW; - uint8_t gpsFix; - uint8_t flags; - uint8_t fixStat; - uint8_t flags2; - uint32_t ttff; - uint32_t msss; - }; - - struct NAV_TIME_UTC { - uint8_t cls; - uint8_t id; - uint16_t len; - uint32_t iTOW; - uint32_t tAcc; - int32_t nano; - uint16_t year; - uint8_t month; - uint8_t day; - uint8_t hour; - uint8_t min; - uint8_t sec; - struct { - uint8_t UTC:1; - uint8_t WKN:1; - uint8_t TOW:1; - uint8_t padding:5; - } valid; - }; - - struct CFG_RATE { - uint8_t cls; - uint8_t id; - uint16_t len; - uint16_t measRate; - uint16_t navRate; - uint16_t timeRef; - char CK[2]; - }; - - struct { - uint32_t last_iTOW; - int32_t last_alt; - uint32_t last_hAcc; - uint32_t last_vAcc; - uint8_t gpsFix; - uint8_t non_empty_loops; - uint16_t log_interval; - int32_t timeOffset; - } state; - - struct { - uint32_t init:1; - uint32_t filter_noise:1; - uint32_t send_when_new:1; - uint32_t send_UI_only:1; - uint32_t runningNTP:1; - - uint32_t forceUTCupdate:1; - uint32_t runningVPort:1; - - } mode; - - union { - NAV_POSLLH navPosllh; - NAV_STATUS navStatus; - NAV_TIME_UTC navTime; - POLL_MSG pollMsg; - CFG_RATE cfgRate; - } Message; - - uint8_t TCPbuf[UBX_SERIAL_BUFFER_SIZE]; - size_t TCPbufSize; -} UBX; - -enum UBXMsgType { - MT_NONE, - MT_NAV_POSLLH, - MT_NAV_STATUS, - MT_NAV_TIME, - MT_POLL -}; - -#ifdef USE_FLOG -FLOG *Flog = nullptr; -#endif -#ifdef ESP8266 -TasmotaSerial *UBXSerial; -#else -HardwareSerial *UBXSerial; -#endif - -NtpServer timeServer(PortUdp); - -WiFiServer vPortServer(UBX_TCP_PORT); -WiFiClient vPortClient; - - - - - -void UBXcalcChecksum(char* CK, size_t msgSize) -{ - memset(CK, 0, 2); - for (int i = 0; i < msgSize; i++) { - CK[0] += ((char*)(&UBX.Message))[i]; - CK[1] += CK[0]; - } -} - -bool UBXcompareMsgHeader(const char* msgHeader) -{ - char* ptr = (char*)(&UBX.Message); - return ptr[0] == msgHeader[0] && ptr[1] == msgHeader[1]; -} - -void UBXinitCFG(void) -{ - for (uint32_t i = 0; i < sizeof(UBLOX_INIT); i++) { - UBXSerial->write( pgm_read_byte(UBLOX_INIT+i) ); - } - DEBUG_SENSOR_LOG(PSTR("UBX: turn off NMEA")); -} - -void UBXsendCFGLine(uint8_t _line) -{ - if (_line>sizeof(UBLOX_INIT)/16) return; - for (uint32_t i = 0; i < 16; i++) { - UBXSerial->write( pgm_read_byte(UBLOX_INIT+i+(_line*16)) ); - } - DEBUG_SENSOR_LOG(PSTR("UBX: send line %u of UBLOX_INIT"), _line); -} - -void UBXTriggerTele(void) -{ - mqtt_data[0] = '\0'; - if (MqttShowSensor()) { - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); -#ifdef USE_RULES - RulesTeleperiod(); -#endif - } -} - - - -void UBXDetect(void) -{ - UBX.mode.init = 0; - if ((pin[GPIO_GPS_RX] < 99) && (pin[GPIO_GPS_TX] < 99)) { -#ifdef ESP8266 - UBXSerial = new TasmotaSerial(pin[GPIO_GPS_RX], pin[GPIO_GPS_TX], 1, 0, UBX_SERIAL_BUFFER_SIZE); - if (UBXSerial->begin(9600)) { -#else - UBXSerial = new HardwareSerial(2); - UBXSerial->begin(9600,SERIAL_8N1,pin[GPIO_GPS_RX], pin[GPIO_GPS_TX]); - { -#endif - DEBUG_SENSOR_LOG(PSTR("UBX: started serial")); -#ifdef ESP8266 - if (UBXSerial->hardwareSerial()) { - ClaimSerial(); - DEBUG_SENSOR_LOG(PSTR("UBX: claim HW")); - } -#endif - } - } - else { - return; - } - - UBXinitCFG(); - UBX.mode.init = 1; - -#ifdef USE_FLOG - if (!Flog) { - Flog = new FLOG; - Flog->init(); - } -#endif - - UBX.state.log_interval = 10; - UBX.mode.send_UI_only = true; - UBXTriggerTele(); -} - -uint32_t UBXprocessGPS() -{ - static uint32_t fpos = 0; - static char checksum[2]; - static uint8_t currentMsgType = MT_NONE; - static size_t payloadSize = sizeof(UBX.Message); - - - uint32_t data_bytes = 0; - while ( UBXSerial->available() ) { - data_bytes++; - byte c = UBXSerial->read(); - if (UBX.mode.runningVPort){ - UBX.TCPbuf[data_bytes-1] = c; - UBX.TCPbufSize = data_bytes; - } - if ( fpos < 2 ) { - - if ( c == UBX.UBX_HEADER[fpos] ) { - fpos++; - } else { - fpos = 0; - } - } else { - - - - - - if ( (fpos-2) < payloadSize ) { - ((char*)(&UBX.Message))[fpos-2] = c; - } - fpos++; - - if ( fpos == 4 ) { - - - if ( UBXcompareMsgHeader(UBX.NAV_POSLLH_HEADER) ) { - currentMsgType = MT_NAV_POSLLH; - payloadSize = sizeof(UBX_t::NAV_POSLLH); - DEBUG_SENSOR_LOG(PSTR("UBX: got NAV_POSLLH")); - } - else if ( UBXcompareMsgHeader(UBX.NAV_STATUS_HEADER) ) { - currentMsgType = MT_NAV_STATUS; - payloadSize = sizeof(UBX_t::NAV_STATUS); - DEBUG_SENSOR_LOG(PSTR("UBX: got NAV_STATUS")); - } - else if ( UBXcompareMsgHeader(UBX.NAV_TIME_HEADER) ) { - currentMsgType = MT_NAV_TIME; - payloadSize = sizeof(UBX_t::NAV_TIME_UTC); - DEBUG_SENSOR_LOG(PSTR("UBX: got NAV_TIME_UTC")); - } - else { - - fpos = 0; - continue; - } - } - - if ( fpos == (payloadSize+2) ) { - - - UBXcalcChecksum(checksum, payloadSize); - } - else if ( fpos == (payloadSize+3) ) { - - - if ( c != checksum[0] ) { - - fpos = 0; - } - } - else if ( fpos == (payloadSize+4) ) { - - - fpos = 0; - if ( c == checksum[1] ) { - - return currentMsgType; - } - } - else if ( fpos > (payloadSize+4) ) { - - - fpos = 0; - } - } - } - - if (data_bytes!=0) { - UBX.state.non_empty_loops++; - DEBUG_SENSOR_LOG(PSTR("UBX: got %u bytes, non-empty-loop: %u"), data_bytes, UBX.state.non_empty_loops); - } else { - UBX.state.non_empty_loops = 0; - } - return MT_NONE; -} - - - - - -#ifdef USE_FLOG -void UBXsendHeader(void) -{ - Webserver->setContentLength(CONTENT_LENGTH_UNKNOWN); - Webserver->sendHeader(F("Content-Disposition"), F("attachment; filename=TASMOTA.gpx")); - WSSend(200, CT_STREAM, F( - "\r\n" - "\r\n" - "\r\n\r\n")); -} - -void UBXsendRecord(uint8_t *buf) -{ - char record[100]; - char stime[32]; - UBX_t::entry_t *entry = (UBX_t::entry_t*)buf; - snprintf_P(stime, sizeof(stime), GetDT(entry->time).c_str()); - char lat[12]; - char lon[12]; - dtostrfd((double)entry->lat/10000000.0f,7,lat); - dtostrfd((double)entry->lon/10000000.0f,7,lon); - snprintf_P(record, sizeof(record),PSTR("\n\t\n\n"),lat ,lon, stime); - - Webserver->sendContent_P(record); -} - -void UBXsendFooter(void) -{ - Webserver->sendContent(F("\n\n")); - Webserver->sendContent(""); - Rtc.user_time_entry = false; -} - - - -void UBXsendFile(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - Flog->startDownload(sizeof(UBX.rec_buffer),UBXsendHeader,UBXsendRecord,UBXsendFooter); -} -#endif - - - -void UBXSetRate(uint16_t interval) -{ - UBX.Message.cfgRate.cls = 0x06; - UBX.Message.cfgRate.id = 0x08; - UBX.Message.cfgRate.len = 6; - uint32_t measRate = (1000*(uint32_t)interval); - if (measRate > 0xffff) { - measRate = 0xffff; - } - UBX.Message.cfgRate.measRate = (uint16_t)measRate; - UBX.Message.cfgRate.navRate = 1; - UBX.Message.cfgRate.timeRef = 1; - UBXcalcChecksum(UBX.Message.cfgRate.CK, sizeof(UBX.Message.cfgRate)-sizeof(UBX.Message.cfgRate.CK)); - DEBUG_SENSOR_LOG(PSTR("UBX: requested interval: %u seconds measRate: %u ms"), interval, UBX.Message.cfgRate.measRate); - UBXSerial->write(UBX.UBX_HEADER[0]); - UBXSerial->write(UBX.UBX_HEADER[1]); - for (uint32_t i =0; iwrite(((uint8_t*)(&UBX.Message.cfgRate))[i]); - DEBUG_SENSOR_LOG(PSTR("UBX: cfgRate byte %u: %x"), i, ((uint8_t*)(&UBX.Message.cfgRate))[i]); - } - UBX.state.log_interval = 10*interval; -} - -void UBXSelectMode(uint16_t mode) -{ - DEBUG_SENSOR_LOG(PSTR("UBX: set mode to %u"),mode); - switch(mode){ -#ifdef USE_FLOG - case 0: - Flog->mode = 0; - break; - case 1: - Flog->mode = 1; - break; - case 2: - UBX.mode.filter_noise = true; - break; - case 3: - UBX.mode.filter_noise = false; - break; - case 4: - Flog->startRecording(true); - AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: start recording - appending")); - break; - case 5: - Flog->startRecording(false); - AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: start recording - new log")); - break; - case 6: - if(Flog->recording == true){ - Flog->stopRecording(); - } - AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: stop recording")); - break; -#endif - case 7: - UBX.mode.send_when_new = 1; - break; - case 8: - UBX.mode.send_when_new = 0; - break; - case 9: - if (timeServer.beginListening()) { - UBX.mode.runningNTP = true; - } - break; - case 10: - UBX.mode.runningNTP = false; - UBXsendCFGLine(10); - UBXsendCFGLine(11); - break; - case 11: - UBX.mode.forceUTCupdate = true; - break; - case 12: - UBX.mode.forceUTCupdate = false; - break; - case 13: - Settings.latitude = UBX.rec_buffer.values.lat/10; - Settings.longitude = UBX.rec_buffer.values.lon/10; - break; - case 14: - vPortServer.begin(); - UBX.mode.runningVPort = 1; - break; - case 15: - - UBX.mode.runningVPort = 0; - break; - default: - if (mode>1000 && mode <1066) { - UBXSetRate(mode-1000); - } - break; - } - UBX.mode.send_UI_only = true; - UBXTriggerTele(); -} - - - -bool UBXHandlePOSLLH() -{ - DEBUG_SENSOR_LOG(PSTR("UBX: iTOW: %u"),UBX.Message.navPosllh.iTOW); - if (UBX.state.gpsFix>1) { - if (UBX.mode.filter_noise) { - if ((UBX.Message.navPosllh.lat-UBX.rec_buffer.values.lat6) { - if(UBX.mode.runningVPort) return; - UBXinitCFG(); - AddLog_P(LOG_LEVEL_ERROR, PSTR("UBX: possible device-reset, will re-init")); - UBXSerial->flush(); - UBX.state.non_empty_loops = 0; - } -} - - - -void UBXLoop50msec(void) -{ - - if (UBX.mode.runningVPort){ - if(!vPortClient.connected()) { - vPortClient = vPortServer.available(); - } - while(vPortClient.available()) { - byte _newByte = vPortClient.read(); - UBXSerial->write(_newByte); - } - - if (UBX.TCPbufSize!=0){ - vPortClient.write((char*)UBX.TCPbuf, UBX.TCPbufSize); - UBX.TCPbufSize = 0; - } - } - - if(UBX.mode.runningNTP){ - timeServer.processOneRequest(UBX.rec_buffer.values.time, UBX.state.timeOffset - NTP_MILLIS_OFFSET); - } -} - -void UBXLoop(void) -{ - static uint16_t counter; - static bool new_position; - - uint32_t msgType = UBXprocessGPS(); - - switch(msgType){ - case MT_NAV_POSLLH: - new_position = UBXHandlePOSLLH(); - break; - case MT_NAV_STATUS: - UBXHandleSTATUS(); - break; - case MT_NAV_TIME: - UBXHandleTIME(); - break; - default: - UBXHandleOther(); - break; - } - -#ifdef USE_FLOG - if (counter>UBX.state.log_interval) { - if (Flog->recording && new_position) { - UBX.rec_buffer.values.time = Rtc.local_time; - Flog->addToBuffer(UBX.rec_buffer.bytes, sizeof(UBX.rec_buffer.bytes)); - counter = 0; - } - } -#endif - - counter++; -} - - - - -#ifdef USE_WEBSERVER - - -#ifdef USE_FLOG -#ifdef DEBUG_TASMOTA_SENSOR - const char HTTP_SNS_FLOGVER[] PROGMEM = "{s}
{m}
{e}{s} FLOG with %u sectors: {m}%u bytes{e}" - "{s} FLOG next sector for REC: {m} %u {e}" - "{s} %u sector(s) with data at sector: {m} %u {e}"; - const char HTTP_SNS_FLOGREC[] PROGMEM = "{s} RECORDING (bytes in buffer) {m}%u{e}"; -#endif - - const char HTTP_SNS_FLOG[] PROGMEM = "{s}
{m}
{e}{s} Flash-Log {m} %s{e}"; - const char kFLOG_STATE0[] PROGMEM = "ready"; - const char kFLOG_STATE1[] PROGMEM = "recording"; - const char * kFLOG_STATE[] ={kFLOG_STATE0, kFLOG_STATE1}; - - const char HTTP_BTN_FLOG_DL[] PROGMEM = ""; - -#endif - const char HTTP_SNS_NTPSERVER[] PROGMEM = "{s} NTP server {m}active{e}"; - - const char HTTP_SNS_GPS[] PROGMEM = "{s} GPS latitude {m}%s{e}" - "{s} GPS longitude {m}%s{e}" - "{s} GPS altitude {m}%s m{e}" - "{s} GPS hor. Accuracy {m}%s m{e}" - "{s} GPS vert. Accuracy {m}%s m{e}" - "{s} GPS sat-fix status {m}%s{e}"; - - const char kGPSFix0[] PROGMEM = "no fix"; - const char kGPSFix1[] PROGMEM = "dead reckoning only"; - const char kGPSFix2[] PROGMEM = "2D-fix"; - const char kGPSFix3[] PROGMEM = "3D-fix"; - const char kGPSFix4[] PROGMEM = "GPS + dead reckoning combined"; - const char kGPSFix5[] PROGMEM = "Time only fix"; - const char * kGPSFix[] PROGMEM ={kGPSFix0, kGPSFix1, kGPSFix2, kGPSFix3, kGPSFix4, kGPSFix5}; - - - - -#endif - - - -void UBXShow(bool json) -{ - char lat[12]; - char lon[12]; - char alt[12]; - char hAcc[12]; - char vAcc[12]; - dtostrfd((double)UBX.rec_buffer.values.lat/10000000.0f,7,lat); - dtostrfd((double)UBX.rec_buffer.values.lon/10000000.0f,7,lon); - dtostrfd((double)UBX.state.last_alt/1000.0f,3,alt); - dtostrfd((double)UBX.state.last_vAcc/1000.0f,3,hAcc); - dtostrfd((double)UBX.state.last_hAcc/1000.0f,3,vAcc); - - if (json) { - ResponseAppend_P(PSTR(",\"GPS\":{")); - if (UBX.mode.send_UI_only) { - uint32_t i = UBX.state.log_interval / 10; - ResponseAppend_P(PSTR("\"fil\":%u,\"int\":%u}"), UBX.mode.filter_noise, i); - } else { - ResponseAppend_P(PSTR("\"lat\":%s,\"lon\":%s,\"alt\":%s,\"hAcc\":%s,\"vAcc\":%s}"), lat, lon, alt, hAcc, vAcc); - } -#ifdef USE_FLOG - ResponseAppend_P(PSTR(",\"FLOG\":{\"rec\":%u,\"mode\":%u,\"sec\":%u}"), Flog->recording, Flog->mode, Flog->sectors_left); -#endif - UBX.mode.send_UI_only = false; -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_GPS, lat, lon, alt, hAcc, vAcc, kGPSFix[UBX.state.gpsFix]); - -#ifdef DEBUG_TASMOTA_SENSOR -#ifdef USE_FLOG - WSContentSend_PD(HTTP_SNS_FLOGVER, Flog->num_sectors, Flog->size, Flog->current_sector, Flog->sectors_left, Flog->sector.header.physical_start_sector); - if (Flog->recording) { - WSContentSend_PD(HTTP_SNS_FLOGREC, Flog->sector.header.buf_pointer - 8); - } -#endif -#endif -#ifdef USE_FLOG - if (Flog->ready) { - WSContentSend_P(HTTP_SNS_FLOG,kFLOG_STATE[Flog->recording]); - } - if (!Flog->recording && Flog->found_saved_data) { - WSContentSend_P(HTTP_BTN_FLOG_DL); - } -#endif - if (UBX.mode.runningNTP) { - WSContentSend_P(HTTP_SNS_NTPSERVER); - } -#endif - } -} - - - - - -bool UBXCmd(void) -{ - bool serviced = true; - if (XdrvMailbox.data_len > 0) { - UBXSelectMode(XdrvMailbox.payload); - Response_P(S_JSON_UBX_COMMAND_NVALUE, XdrvMailbox.command, XdrvMailbox.payload); - } - return serviced; -} - - - - - -bool Xsns60(uint8_t function) -{ - bool result = false; - - if (FUNC_INIT == function) { - UBXDetect(); - } - - if (UBX.mode.init) { - switch (function) { - case FUNC_COMMAND_SENSOR: - if (XSNS_60 == XdrvMailbox.index) { - result = UBXCmd(); - } - break; - case FUNC_EVERY_50_MSECOND: - UBXLoop50msec(); - break; - case FUNC_EVERY_100_MSECOND: -#ifdef USE_FLOG - if (!Flog->running_download) -#endif - { - UBXLoop(); - } - break; -#ifdef USE_FLOG - case FUNC_WEB_ADD_HANDLER: - Webserver->on("/UBX", UBXsendFile); - break; -#endif - case FUNC_JSON_APPEND: - UBXShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: -#ifdef USE_FLOG - if (!Flog->running_download) -#endif - { - UBXShow(0); - } - break; -#endif - } - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_61_MI_NRF24.ino" -# 44 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_61_MI_NRF24.ino" -#ifdef USE_SPI -#ifdef USE_NRF24 -#ifdef USE_MIBLE - -#ifdef DEBUG_TASMOTA_SENSOR - #define MINRF_LOG_BUFFER(x) MINRFshowBuffer(x); -#else - #define MINRF_LOG_BUFFER(x) -#endif -# 62 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_61_MI_NRF24.ino" -#define XSNS_61 61 - -#include - -#define FLORA 1 -#define MJ_HT_V1 2 -#define LYWSD02 3 -#define LYWSD03 4 -#define CGG1 5 -#define CGD1 6 - -#define D_CMND_NRF "NRF" - -const char S_JSON_NRF_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_NRF "%s\":%d}"; -const char S_JSON_NRF_COMMAND[] PROGMEM = "{\"" D_CMND_NRF "%s\":\"%s\"}"; -const char kNRF_Commands[] PROGMEM = "Ignore|Page|Scan|Beacon|Chan"; - -enum NRF_Commands { - CMND_NRF_IGNORE, - CMND_NRF_PAGE, - CMND_NRF_SCAN, - CMND_NRF_BEACON, - CMND_NRF_CHAN - }; - -const uint16_t kMINRFSlaveID[6]={ 0x0098, - 0x01aa, - 0x045b, - 0x055b, - 0x0347, - 0x0576 - }; - -const char kMINRFSlaveType1[] PROGMEM = "Flora"; -const char kMINRFSlaveType2[] PROGMEM = "MJ_HT_V1"; -const char kMINRFSlaveType3[] PROGMEM = "LYWSD02"; -const char kMINRFSlaveType4[] PROGMEM = "LYWSD03"; -const char kMINRFSlaveType5[] PROGMEM = "CGG1"; -const char kMINRFSlaveType6[] PROGMEM = "CGD1"; -const char * kMINRFSlaveType[] PROGMEM = {kMINRFSlaveType1,kMINRFSlaveType2,kMINRFSlaveType3,kMINRFSlaveType4,kMINRFSlaveType5,kMINRFSlaveType6}; - - -const uint32_t kMINRFFloPDU[3] = {0x3eaa857d,0xef3b8730,0x71da7b46}; -const uint32_t kMINRFMJPDU[3] = {0x4760cd66,0xdbcc0cd3,0x33048df5}; -const uint32_t kMINRFL2PDU[3] = {0x3eaa057d,0xef3b0730,0x71dafb46}; - -const uint32_t kMINRFL3PDU[3] = {0x4760cb78,0xdbcc0acd,0x33048beb}; -const uint32_t kMINRFCGGPDU[3] = {0x4760cd6e,0xdbcc0cdb,0x33048dfd}; -const uint32_t kMINRFCGDPDU[3] = {0x5da0d752,0xc10c16e7,0x29c497c1}; - - -const uint8_t kMINRFlsfrList_A[3] = {0x4b,0x17,0x23}; -const uint8_t kMINRFlsfrList_B[3] = {0x21,0x72,0x43}; - - -#pragma pack(1) -struct mi_beacon_t{ - uint16_t productID; - uint8_t counter; - uint8_t Mac[6]; - uint8_t spare; - uint8_t type; - uint8_t ten; - uint8_t size; - union { - struct{ - int16_t temp; - uint16_t hum; - }HT; - uint8_t bat; - uint16_t temp; - uint16_t hum; - uint32_t lux:24; - uint8_t moist; - uint16_t fert; - }; -}; - -struct CGDPacket_t { - uint8_t serial[6]; - uint16_t mode; - union { - struct { - int16_t temp; - uint16_t hum; - }; - uint8_t bat; - }; -}; - -struct bleAdvPacket_t { - uint8_t pduType; - uint8_t payloadSize; - uint8_t mac[6]; -}; - -union FIFO_t{ - bleAdvPacket_t bleAdv; - mi_beacon_t miBeacon; - CGDPacket_t CGDPacket; - uint8_t raw[32]; -}; - -#pragma pack(0) - -struct { - const uint8_t channel[3] = {37,38,39}; - const uint8_t frequency[3] = { 2,26,80}; - - uint16_t timer; - uint8_t currentChan=0; - uint8_t ignore = 0; - uint8_t channelIgnore = 0; - uint8_t confirmedSensors = 0; - uint8_t packetMode; - uint8_t perPage = 4; - uint8_t firstUsedPacketMode = 1; - - FIFO_t buffer; - - struct { - uint8_t mac[6]; - uint32_t time; - uint32_t PDU[3]; - bool active = false; - } beacon; - bool activeScan = false; - bool stopScan = false; - -#ifdef DEBUG_TASMOTA_SENSOR - uint8_t streamBuffer[sizeof(buffer)]; - uint8_t lsfrBuffer[sizeof(buffer)]; -#endif - -} MINRF; - -struct mi_sensor_t{ - uint8_t type; - uint8_t serial[6]; - uint8_t showedUp; - float temp; - union { - struct { - float moisture; - float fertility; - uint32_t lux; - }; - struct { - float hum; - uint8_t bat; - }; - }; -}; - -struct scan_entry_t { - uint8_t mac[6]; - uint16_t cid; - uint16_t svc; - uint16_t uuid; - uint8_t showedUp; -}; - -std::vector MIBLEsensors; -std::vector MINRFscanResult; - -static union{ - scan_entry_t MINRFdummyEntry; - uint8_t MINRFtempBuf[23]; -}; -# 241 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_61_MI_NRF24.ino" -bool MINRFinitBLE(uint8_t _mode) -{ - if (MINRF.timer%1000 == 0){ - NRF24radio.begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_DC]); - NRF24radio.setAutoAck(false); - NRF24radio.setDataRate(RF24_1MBPS); - NRF24radio.disableCRC(); - NRF24radio.setChannel( MINRF.frequency[MINRF.currentChan] ); - NRF24radio.setRetries(0,0); - NRF24radio.setPALevel(RF24_PA_MIN); - NRF24radio.setAddressWidth(4); - - - NRF24radio.powerUp(); - } - if(NRF24radio.isChipConnected()){ - - MINRFchangePacketModeTo(_mode); - return true; - } - - return false; -} - - - - - -void MINRFhopChannel() -{ - for (uint32_t i = 0; i<3;i++){ - MINRF.currentChan++; - if(bitRead(MINRF.channelIgnore,MINRF.currentChan)) continue; - if(MINRF.currentChan >= sizeof(MINRF.channel)) { - MINRF.currentChan = 0; - if(bitRead(MINRF.channelIgnore,MINRF.currentChan)) continue; - } - break; - } - NRF24radio.setChannel( MINRF.frequency[MINRF.currentChan] ); -} - - - - - - - -bool MINRFreceivePacket(void) -{ - if(!NRF24radio.available()) { - return false; - } - while(NRF24radio.available()) { - - - NRF24radio.read( &MINRF.buffer, sizeof(MINRF.buffer) ); -#ifdef DEBUG_TASMOTA_SENSOR - memcpy(&MINRF.streamBuffer, &MINRF.buffer, sizeof(MINRF.buffer)); -#endif - MINRFswapbuf((uint8_t*)&MINRF.buffer, sizeof(MINRF.buffer) ); - - - - switch (MINRF.packetMode) { - case 0: - MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), MINRF.channel[MINRF.currentChan] | 0x40); - break; - case 1: - MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_A[MINRF.currentChan]); - break; - case 2: - MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); - break; - case 3: - MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_A[MINRF.currentChan]); - break; - case 4: - MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); - break; - case 5: - MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); - break; - case 6: - MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); - break; - } - - - } - - return true; -} - -#ifdef DEBUG_TASMOTA_SENSOR -void MINRFshowBuffer(uint8_t (&buf)[32]){ - - - - - DEBUG_SENSOR_LOG(PSTR("MINRF: Buffer: %02x %02x %02x %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x %02x %02x %02x ") - ,buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7],buf[8],buf[9],buf[10],buf[11], - buf[12],buf[13],buf[14],buf[15],buf[16],buf[17],buf[18],buf[19],buf[20],buf[21],buf[22],buf[23], - buf[24],buf[25],buf[26],buf[27],buf[28],buf[29],buf[30],buf[31] - ); -} -#endif - - - - - - -void MINRFswapbuf(uint8_t *buf, uint8_t len) -{ - - while(len--) { - uint8_t a = *buf; - uint8_t v = 0; - if (a & 0x80) v |= 0x01; - if (a & 0x40) v |= 0x02; - if (a & 0x20) v |= 0x04; - if (a & 0x10) v |= 0x08; - if (a & 0x08) v |= 0x10; - if (a & 0x04) v |= 0x20; - if (a & 0x02) v |= 0x40; - if (a & 0x01) v |= 0x80; - *(buf++) = v; - } -} -# 382 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_61_MI_NRF24.ino" -void MINRFwhiten(uint8_t *buf, uint8_t len, uint8_t lfsr) -{ - while(len--) { - uint8_t res = 0; - - for (uint8_t i = 1; i; i <<= 1) { - if (lfsr & 0x01) { - lfsr ^= 0x88; - res |= i; - } - lfsr >>= 1; - } - *(buf++) ^= res; -#ifdef DEBUG_TASMOTA_SENSOR - MINRF.lsfrBuffer[31-len] = lfsr; -#endif - } -} - - - - -bool MINRFhandleBeacon(scan_entry_t * entry, uint32_t offset); - - - - - -void MINRFhandleScan(void){ - if(MINRFscanResult.size()>20 || MINRF.stopScan) { - MINRF.activeScan=false; - MINRFcomputefirstUsedPacketMode(); - uint32_t i = 0; - MINRFscanResult.erase(std::remove_if(MINRFscanResult.begin(), - MINRFscanResult.end(), - [&i](scan_entry_t e) { - if(e.showedUp>2) AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: Beacon %02u: %02X%02X%02X%02X%02X%02X Cid: %04X Svc: %04X UUID: %04X"),i,e.mac[0],e.mac[1],e.mac[2],e.mac[3],e.mac[4],e.mac[5],e.cid,e.svc,e.uuid); - i++; - return ((e.showedUp < 3)); - }), - MINRFscanResult.end()); - MINRF.stopScan=false; - return; - } - - MINRFreverseMAC(MINRF.buffer.bleAdv.mac); - for(uint32_t i=0; i30) break; - uint32_t ADtype = _buf[i+1]; - - if (size+i>32+offset) size=32-i+offset-2; - if (size>30) break; - char _stemp[(size*2)]; - uint32_t backupSize; - switch(ADtype){ - case 0x01: - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Flags: %02x"), _buf[i+2]); - break; - case 0x02: case 0x03: - entry->uuid = _buf[i+3]*256 + _buf[i+2]; - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: UUID: %04x"), entry->uuid); - success = true; - break; - case 0x08: case 0x09: - backupSize = _buf[i+size+1]; - _buf[i+size+1] = 0; - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Name: %s"), (char*)&_buf[i+2]); - success = true; - _buf[i+size+1] = backupSize; - break; - case 0x0a: - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: TxPow: %02u"), _buf[i+2]); - break; - case 0xff: - entry->cid = _buf[i+3]*256 + _buf[i+2]; - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Cid: %04x"), entry->cid); - ToHex_P((unsigned char*)&_buf+i+4,size-3,_stemp,(size*2)); - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); - success = true; - break; - case 0x16: - entry->svc = _buf[i+3]*256 + _buf[i+2]; - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Svc: %04x"), entry->svc); - ToHex_P((unsigned char*)&_buf+i+4,size-3,_stemp,(size*2)); - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); - success = true; - break; - default: - ToHex_P((unsigned char*)&_buf+i+2,size-1,_stemp,(size*2)); - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); - } - i+=size; - } - MINRF.beacon.time = 0; - } - return success; -} - - - - - -void MINRFbeaconCounter(void){ - if(MINRF.beacon.active) { - MINRF.beacon.time++; - char stemp[20]; - snprintf_P(stemp, sizeof(stemp),PSTR("{%s:{\"Beacon\": %u}}"),D_CMND_NRF, MINRF.beacon.time); - AddLog_P2(LOG_LEVEL_DEBUG, stemp); - RulesProcessEvent(stemp); - } -} - - - - - -void MINRFcomputeBeaconPDU(void){ - for (uint32_t i = 0; i<3; i++){ - bleAdvPacket_t packet; - memcpy((uint8_t *)&packet.mac, (uint8_t *)&MINRF.beacon.mac, sizeof(packet.mac)); - MINRFreverseMAC(packet.mac); - MINRFwhiten((uint8_t *)&packet, sizeof(packet), MINRF.channel[i] | 0x40); - MINRFswapbuf((uint8_t*)&packet,sizeof(packet)); - uint32_t pdu = packet.mac[0]<<24 | packet.mac[1]<<16 | packet.mac[2]<<8 | packet.mac[3]; - MINRF.beacon.PDU[i] = pdu; - } -} -# 576 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_61_MI_NRF24.ino" -void MINRFreverseMAC(uint8_t _mac[]){ - uint8_t _reversedMAC[6]; - for (uint8_t i=0; i<6; i++){ - _reversedMAC[5-i] = _mac[i]; - } - memcpy(_mac,_reversedMAC, sizeof(_reversedMAC)); -} - - - - - - - -void MINRFMACStringToBytes(char* _string, uint8_t _mac[]) { - uint32_t index = 0; - while (index < 12) { - char c = _string[index]; - uint8_t value = 0; - if(c >= '0' && c <= '9') - value = (c - '0'); - else if (c >= 'A' && c <= 'F') - value = (10 + (c - 'A')); - _mac[(index/2)] += value << (((index + 1) % 2) * 4); - index++; - } - -} - - - - - -void MINRFcomputefirstUsedPacketMode(void){ - for (uint32_t i = 0; iCGD1) MINRF.firstUsedPacketMode=0; - break; - } - } -} - - - - - - -void MINRFchangePacketModeTo(uint8_t _mode) { - uint32_t (_nextchannel) = MINRF.currentChan+1; - if (_nextchannel>2) _nextchannel=0; - - switch(_mode){ - case 0: - NRF24radio.openReadingPipe(0,0x6B7D9171); - break; - case 1: - NRF24radio.openReadingPipe(0,kMINRFFloPDU[_nextchannel]); - break; - case 2: - NRF24radio.openReadingPipe(0,kMINRFMJPDU[_nextchannel]); - break; - case 3: - NRF24radio.openReadingPipe(0,kMINRFL2PDU[_nextchannel]); - break; - case 4: - NRF24radio.openReadingPipe(0,kMINRFL3PDU[_nextchannel]); - break; - case 5: - NRF24radio.openReadingPipe(0,kMINRFCGGPDU[_nextchannel]); - break; - case 6: - NRF24radio.openReadingPipe(0,kMINRFCGDPDU[_nextchannel]); - break; - } - - MINRF.packetMode = _mode; -} -# 663 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_61_MI_NRF24.ino" -uint32_t MINRFgetSensorSlot(uint8_t (&_serial)[6], uint16_t _type){ - - DEBUG_SENSOR_LOG(PSTR("MINRF: will test ID-type: %x"), _type); - bool _success = false; - for (uint32_t i=0;i<6;i++){ - if(_type == kMINRFSlaveID[i]){ - DEBUG_SENSOR_LOG(PSTR("MINRF: ID is type %u"), i); - _type = i+1; - _success = true; - } - else { - DEBUG_SENSOR_LOG(PSTR("MINRF: ID-type is not: %x"),kMINRFSlaveID[i]); - } - } - if(!_success) return 0xff; - - DEBUG_SENSOR_LOG(PSTR("MINRF: vector size %u"), MIBLEsensors.size()); - for(uint32_t i=0; i 2){ - MINRF.confirmedSensors++; - } - } -} - - - - - -void MINRFhandleMiBeaconPacket(void){ - MINRFreverseMAC(MINRF.buffer.miBeacon.Mac); - uint32_t _slot = MINRFgetSensorSlot(MINRF.buffer.miBeacon.Mac, MINRF.buffer.miBeacon.productID); - if(_slot==0xff) return; - DEBUG_SENSOR_LOG(PSTR("MINRF: slot %u, size vector: %u %u"),_slot,MIBLEsensors.size()); - - mi_sensor_t *_sensorVec = &MIBLEsensors.at(_slot); - float _tempFloat; - - if (_sensorVec->type==MJ_HT_V1 || _sensorVec->type==CGG1){ - memcpy(MINRFtempBuf,(uint8_t*)&MINRF.buffer.miBeacon.spare, 32-9); - memcpy((uint8_t*)&MINRF.buffer.miBeacon.type,MINRFtempBuf, 32-9); - } - - DEBUG_SENSOR_LOG(PSTR("%s at slot %u"), kNRFSlaveType[_sensorVec->type-1],_slot); - switch(MINRF.buffer.miBeacon.type){ - case 0x04: - _tempFloat=(float)(MINRF.buffer.miBeacon.temp)/10.0f; - if(_tempFloat<60){ - _sensorVec->temp=_tempFloat; - DEBUG_SENSOR_LOG(PSTR("Mode 4: temp updated")); - } - DEBUG_SENSOR_LOG(PSTR("Mode 4: U16: %u Temp"), MINRF.buffer.miBeacon.temp ); - break; - case 0x06: - _tempFloat=(float)(MINRF.buffer.miBeacon.hum)/10.0f; - if(_tempFloat<101){ - _sensorVec->hum=_tempFloat; - DEBUG_SENSOR_LOG(PSTR("Mode 6: hum updated")); - } - DEBUG_SENSOR_LOG(PSTR("Mode 6: U16: %u Hum"), MINRF.buffer.miBeacon.hum); - break; - case 0x07: - _sensorVec->lux=MINRF.buffer.miBeacon.lux & 0x00ffffff; - DEBUG_SENSOR_LOG(PSTR("Mode 7: U24: %u Lux"), MINRF.buffer.miBeacon.lux & 0x00ffffff); - break; - case 0x08: - _tempFloat =(float)MINRF.buffer.miBeacon.moist; - if(_tempFloat<100){ - _sensorVec->moisture=_tempFloat; - DEBUG_SENSOR_LOG(PSTR("Mode 8: moisture updated")); - } - DEBUG_SENSOR_LOG(PSTR("Mode 8: U8: %u Moisture"), MINRF.buffer.miBeacon.moist); - break; - case 0x09: - _tempFloat=(float)(MINRF.buffer.miBeacon.fert); - if(_tempFloat<65535){ - _sensorVec->fertility=_tempFloat; - DEBUG_SENSOR_LOG(PSTR("Mode 9: fertility updated")); - } - DEBUG_SENSOR_LOG(PSTR("Mode 9: U16: %u Fertility"), MINRF.buffer.miBeacon.fert); - break; - case 0x0a: - if(MINRF.buffer.miBeacon.bat<101){ - _sensorVec->bat = MINRF.buffer.miBeacon.bat; - DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated")); - } - DEBUG_SENSOR_LOG(PSTR("Mode a: U8: %u %%"), MINRF.buffer.miBeacon.bat); - break; - case 0x0d: - _tempFloat=(float)(MINRF.buffer.miBeacon.HT.temp)/10.0f; - if(_tempFloat<60){ - _sensorVec->temp = _tempFloat; - DEBUG_SENSOR_LOG(PSTR("Mode d: temp updated")); - } - _tempFloat=(float)(MINRF.buffer.miBeacon.HT.hum)/10.0f; - if(_tempFloat<100){ - _sensorVec->hum = _tempFloat; - DEBUG_SENSOR_LOG(PSTR("Mode d: hum updated")); - } - DEBUG_SENSOR_LOG(PSTR("Mode d: U16: %x Temp U16: %x Hum"), MINRF.buffer.miBeacon.HT.temp, MINRF.buffer.miBeacon.HT.hum); - break; - } -} - - - - -void MINRFhandleLYWSD03Packet(void){ - - MINRFreverseMAC(MINRF.buffer.miBeacon.Mac); - uint32_t _slot = MINRFgetSensorSlot(MINRF.buffer.miBeacon.Mac, MINRF.buffer.miBeacon.productID); - DEBUG_SENSOR_LOG(PSTR("MINRF: Sensor slot: %u"), _slot); - if(_slot==0xff) return; - - MINRF_LOG_BUFFER(MINRF.streamBuffer); - MINRF_LOG_BUFFER(MINRF.lsfrBuffer); - MINRF_LOG_BUFFER(MINRF.buffer.raw); -} - - - - - -void MINRFhandleCGD1Packet(void){ - MINRFreverseMAC(MINRF.buffer.CGDPacket.serial); - uint32_t _slot = MINRFgetSensorSlot(MINRF.buffer.CGDPacket.serial, 0x0576); - DEBUG_SENSOR_LOG(PSTR("MINRF: Sensor slot: %u"), _slot); - if(_slot==0xff) return; - - switch (MINRF.buffer.CGDPacket.mode){ - case 0x0401: - float _tempFloat; - _tempFloat=(float)(MINRF.buffer.CGDPacket.temp)/10.0f; - if(_tempFloat<60){ - MIBLEsensors.at(_slot).temp = _tempFloat; - DEBUG_SENSOR_LOG(PSTR("CGD1: temp updated")); - } - _tempFloat=(float)(MINRF.buffer.CGDPacket.hum)/10.0f; - if(_tempFloat<100){ - MIBLEsensors.at(_slot).hum = _tempFloat; - DEBUG_SENSOR_LOG(PSTR("CGD1: hum updated")); - } - DEBUG_SENSOR_LOG(PSTR("CGD1: U16: %x Temp U16: %x Hum"), MINRF.buffer.CGDPacket.temp, MINRF.buffer.CGDPacket.hum); - break; - case 0x0102: - if(MINRF.buffer.CGDPacket.bat<101){ - MIBLEsensors.at(_slot).bat = MINRF.buffer.CGDPacket.bat; - DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated")); - } - break; - default: - DEBUG_SENSOR_LOG(PSTR("MINRF: unexpected CGD1-packet")); - MINRF_LOG_BUFFER(MINRF.buffer.raw); - } -} - - - - - -void MINRF_EVERY_50_MSECOND() { - - if(MINRF.timer>6000){ - DEBUG_SENSOR_LOG(PSTR("MINRF: check for FAKE sensors")); - MINRFpurgeFakeSensors(); - MINRF.timer=0; - } - MINRF.timer++; - - if (!MINRFreceivePacket()){ - - } - - else { - switch (MINRF.packetMode) { - case 0: - if (MINRF.beacon.active){ - MINRFhandleBeacon(&MINRFdummyEntry,6); - } - else MINRFhandleScan(); - break; - case FLORA: case MJ_HT_V1: case LYWSD02: case CGG1: - MINRFhandleMiBeaconPacket(); - break; - case LYWSD03: - MINRFhandleLYWSD03Packet(); - break; - case CGD1: - MINRFhandleCGD1Packet(); - break; - default: - break; - } - } - if (MINRF.beacon.active || MINRF.activeScan) { - MINRF.firstUsedPacketMode=0; - } - - MINRF.packetMode = (MINRF.packetMode+1>CGD1) ? MINRF.firstUsedPacketMode : MINRF.packetMode+1; - for (uint32_t i = MINRF.packetMode; i 0) { - if (XdrvMailbox.payload == 0) XdrvMailbox.payload = MINRF.perPage; - MINRF.perPage = XdrvMailbox.payload; - } - else XdrvMailbox.payload = MINRF.perPage; - Response_P(S_JSON_NRF_COMMAND_NVALUE, command, XdrvMailbox.payload); - break; - case CMND_NRF_IGNORE: - if (XdrvMailbox.data_len > 0) { - if (XdrvMailbox.payload == 0){ - MINRF.ignore = 0; - } - else if (XdrvMailbox.payload < CGD1+1) { - bitSet(MINRF.ignore,XdrvMailbox.payload); - MINRFcomputefirstUsedPacketMode(); - MINRF.timer = 5900; - Response_P(S_JSON_NRF_COMMAND, command, kMINRFSlaveType[XdrvMailbox.payload-1]); - } - else if (XdrvMailbox.payload == 255) { - MINRF.ignore = 255; - } - } - Response_P(S_JSON_NRF_COMMAND_NVALUE, command, MINRF.ignore); - break; - case CMND_NRF_SCAN: - if (XdrvMailbox.data_len > 0) { - MINRF.beacon.active = false; - switch(XdrvMailbox.payload){ - case 0: - MINRF.activeScan = true; - MINRF.stopScan = false; - MINRFscanResult.erase(std::remove_if(MINRFscanResult.begin(), - MINRFscanResult.end(), - [](scan_entry_t&) { return true; }), - MINRFscanResult.end()); - break; - case 1: - MINRF.activeScan = true; - MINRF.stopScan = false; - break; - case 2: - MINRF.stopScan = true; - break; - } - Response_P(S_JSON_NRF_COMMAND_NVALUE, command, XdrvMailbox.payload); - } - break; - case CMND_NRF_BEACON: - if (XdrvMailbox.data_len > 0) { - if(XdrvMailbox.data_len<3){ - if (XdrvMailbox.payload < MINRFscanResult.size()) { - MINRFstartBeacon(XdrvMailbox.payload); - Response_P(S_JSON_NRF_COMMAND_NVALUE, command, XdrvMailbox.payload); - } - } - if (XdrvMailbox.data_len==12){ - memset(MINRF.beacon.mac,0,sizeof(MINRF.beacon.mac)); - MINRFMACStringToBytes(XdrvMailbox.data, MINRF.beacon.mac); - MINRF.beacon.time=0; - MINRF.beacon.active=true; - Response_P(S_JSON_NRF_COMMAND, command, XdrvMailbox.data); - } - MINRFcomputeBeaconPDU(); - } - break; - case CMND_NRF_CHAN: - if (XdrvMailbox.data_len == 1) { - switch(XdrvMailbox.payload){ - case 0: case 1: case 2: - bitRead(MINRF.channelIgnore,XdrvMailbox.payload) == 0 ? bitSet(MINRF.channelIgnore,XdrvMailbox.payload) : bitClear(MINRF.channelIgnore,XdrvMailbox.payload); - break; - } - } - Response_P(S_JSON_NRF_COMMAND_NVALUE, command, MINRF.channelIgnore); - break; - default: - - serviced = false; - break; - } - } else { - return false; - } - return serviced; -} - - - - - -const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u%%{e}"; -const char HTTP_MINRF_MAC[] PROGMEM = "{s}%s %s{m}%02x:%02x:%02x:%02x:%02x:%02x%{e}"; -const char HTTP_MINRF_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%dus/cm{e}"; -const char HTTP_MINRF_HL[] PROGMEM = "{s}
{m}
{e}"; -const char HTTP_NRF24NEW[] PROGMEM = "{s}%sL01%c{m}%u%s / %u{e}"; - -void MINRFShow(bool json) -{ - if (json) { - for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { - if(MIBLEsensors[i].showedUp < 3){ - DEBUG_SENSOR_LOG(PSTR("MINRF: sensor not fully registered yet")); - break; - } - ResponseAppend_P(PSTR(",\"%s-%02x%02x%02x\":{"),kMINRFSlaveType[MIBLEsensors[i].type-1],MIBLEsensors[i].serial[3],MIBLEsensors[i].serial[4],MIBLEsensors[i].serial[5]); - if (MIBLEsensors[i].type==FLORA && !isnan(MIBLEsensors[i].temp)){ - char stemp[FLOATSZ]; - dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, stemp); - ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), stemp); - - if(MIBLEsensors[i].lux!=0xffffffff){ - ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%u"), MIBLEsensors[i].lux); - } - if(!isnan(MIBLEsensors[i].moisture)){ - dtostrfd(MIBLEsensors[i].moisture, 0, stemp); - ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%s"), stemp); - } - if(!isnan(MIBLEsensors[i].fertility)){ - dtostrfd(MIBLEsensors[i].fertility, 0, stemp); - ResponseAppend_P(PSTR(",\"Fertility\":%s"), stemp); - } - ResponseJsonEnd(); - } - if (MIBLEsensors[i].type>FLORA){ - if(!isnan(MIBLEsensors[i].temp) && !isnan(MIBLEsensors[i].hum)){ - ResponseAppendTHD(MIBLEsensors[i].temp,MIBLEsensors[i].hum); - } - if(MIBLEsensors[i].bat!=0x00){ - ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors[i].bat); - } - ResponseJsonEnd(); - } - } - if(MINRF.beacon.active){ - ResponseAppend_P(PSTR(",\"Beacon\":{\"Timer\":%u}"),MINRF.beacon.time); - } - -#ifdef USE_WEBSERVER - } else { - static uint32_t _page = 0; - static uint32_t counter = 0; - int32_t i = _page * MINRF.perPage; - uint32_t j = i + MINRF.perPage; - - if (j+1>MINRF.confirmedSensors){ - j = MINRF.confirmedSensors; - } - char stemp[5] ={0}; - if (MINRF.confirmedSensors-(_page*MINRF.perPage)>1 && MINRF.perPage!=1) { - sprintf_P(stemp,"-%u",j); - } - if (MINRF.confirmedSensors==0) i=-1; - - WSContentSend_PD(HTTP_NRF24NEW, NRF24type, NRF24.chipType, i+1,stemp,MINRF.confirmedSensors); - for (i ; iFLORA){ - WSContentSend_THD(kMINRFSlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].temp, MIBLEsensors[i].hum); - if(MIBLEsensors[i].bat!=0x00){ - WSContentSend_PD(HTTP_BATTERY, kMINRFSlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].bat); - } - } - } - if(MINRF.beacon.active){ - WSContentSend_PD(HTTP_MINRF_HL); - WSContentSend_PD(HTTP_MINRF_HL); - WSContentSend_PD(HTTP_MINRF_MAC, F("Beacon"), D_MAC_ADDRESS, MINRF.beacon.mac[0], MINRF.beacon.mac[1],MINRF.beacon.mac[2],MINRF.beacon.mac[3],MINRF.beacon.mac[4],MINRF.beacon.mac[5]); - WSContentSend_PD(PSTR("{s}Beacon Time{m}%u seconds{e}"),MINRF.beacon.time); - } - if(counter>3) { - _page++; - counter = 0; - } - counter++; - if(MINRF.confirmedSensors%MINRF.perPage==0 && _page==MINRF.confirmedSensors/MINRF.perPage) _page=0; - if(_page>MINRF.confirmedSensors/MINRF.perPage) _page=0; -#endif - } -} - - - - - -bool Xsns61(uint8_t function) -{ - bool result = false; - if (NRF24.chipType) { - switch (function) { - case FUNC_INIT: - MINRFinitBLE(1); - AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: started")); - break; - case FUNC_EVERY_50_MSECOND: - MINRF_EVERY_50_MSECOND(); - break; - case FUNC_EVERY_SECOND: - MINRFbeaconCounter(); - break; - case FUNC_COMMAND: - result = NRFCmd(); - break; - case FUNC_JSON_APPEND: - MINRFShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - MINRFShow(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_62_MI_HM10.ino" -# 37 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_62_MI_HM10.ino" -#ifdef USE_HM10 - -#define XSNS_62 62 - -#include -#include - -TasmotaSerial *HM10Serial; -#define HM10_BAUDRATE 115200 - -#define HM10_MAX_TASK_NUMBER 12 -uint8_t HM10_TASK_LIST[HM10_MAX_TASK_NUMBER+1][2]; - -#define HM10_MAX_RX_BUF 64 - -struct { - uint8_t current_task_delay; - uint8_t last_command; - uint16_t perPage = 4; - uint16_t firmware; - uint32_t period; - uint32_t serialSpeed; - union { - uint32_t time; - uint8_t timebuf[4]; - }; - uint16_t autoScanInterval; - struct { - uint32_t awaiting:8; - uint32_t init:1; - uint32_t pending_task:1; - uint32_t connected:1; - uint32_t subscribed:1; - uint32_t autoScan:1; - - } mode; - struct { - uint8_t sensor; - - } state; -} HM10; - -#pragma pack(1) - - struct { - uint16_t temp; - uint8_t hum; - } LYWSD0x_HT; - struct { - uint8_t spare; - uint16_t temp; - uint16_t hum; - } CGD1_HT; - struct { - uint16_t temp; - uint8_t spare; - uint32_t lux; - uint8_t moist; - uint16_t fert; - } Flora_TLMF; - - -struct mi_beacon_t{ - uint16_t frame; - uint16_t productID; - uint8_t counter; - uint8_t Mac[6]; - uint8_t spare; - uint8_t type; - uint8_t ten; - uint8_t size; - union { - struct{ - uint16_t temp; - uint16_t hum; - }HT; - uint8_t bat; - uint16_t temp; - uint16_t hum; - uint32_t lux; - uint8_t moist; - uint16_t fert; - }; -}; -#pragma pack(0) - -struct mi_sensor_t{ - uint8_t type; - uint8_t serial[6]; - uint8_t showedUp; - float temp; - union { - struct { - float moisture; - float fertility; - uint32_t lux; - }; - struct { - float hum; - }; - }; - uint8_t bat; -}; - -std::vector MIBLEsensors; - - - - - -#define D_CMND_HM10 "HM10" - -const char S_JSON_HM10_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_HM10 "%s\":%d}"; -const char S_JSON_HM10_COMMAND[] PROGMEM = "{\"" D_CMND_HM10 "%s%s\"}"; -const char kHM10_Commands[] PROGMEM = "Scan|AT|Period|Baud|Time|Auto|Page"; - -#define FLORA 1 -#define MJ_HT_V1 2 -#define LYWSD02 3 -#define LYWSD03MMC 4 -#define CGG1 5 -#define CGD1 6 - -const uint16_t kHM10SlaveID[6]={ 0x0098, - 0x01aa, - 0x045b, - 0x055b, - 0x0347, - 0x0576 - }; - -const char kHM10SlaveType1[] PROGMEM = "Flora"; -const char kHM10SlaveType2[] PROGMEM = "MJ_HT_V1"; -const char kHM10SlaveType3[] PROGMEM = "LYWSD02"; -const char kHM10SlaveType4[] PROGMEM = "LYWSD03"; -const char kHM10SlaveType5[] PROGMEM = "CGG1"; -const char kHM10SlaveType6[] PROGMEM = "CGD1"; -const char * kHM10SlaveType[] PROGMEM = {kHM10SlaveType1,kHM10SlaveType2,kHM10SlaveType3,kHM10SlaveType4,kHM10SlaveType5,kHM10SlaveType6}; - - - - - -enum HM10_Commands { - CMND_HM10_DISC_SCAN, - CMND_HM10_AT, - CMND_HM10_PERIOD, - CMND_HM10_BAUD, - CMND_HM10_TIME, - CMND_HM10_AUTO, - CMND_HM10_PAGE - }; - -enum HM10_awaitData: uint8_t { - none = 0, - tempHumLY = 1, - TLMF = 2, - bat = 3, - tempHumCGD1 = 4, - discScan = 5, - tempHumMJ = 6 - }; - - - - - -#define TASK_HM10_NOTASK 0 -#define TASK_HM10_ROLE1 1 -#define TASK_HM10_IMME1 2 -#define TASK_HM10_RENEW 3 -#define TASK_HM10_RESET 4 -#define TASK_HM10_DISC 5 -#define TASK_HM10_CONN 6 -#define TASK_HM10_VERSION 7 -#define TASK_HM10_NAME 8 -#define TASK_HM10_FEEDBACK 9 -#define TASK_HM10_DISCONN 10 -#define TASK_HM10_SUB_L3 11 - -#define TASK_HM10_SCAN9 13 -#define TASK_HM10_UN_L3 14 - -#define TASK_HM10_READ_BT_L3 16 -#define TASK_HM10_SUB_L2 17 -#define TASK_HM10_UN_L2 18 -#define TASK_HM10_READ_BT_L2 19 -#define TASK_HM10_TIME_L2 20 -#define TASK_HM10_SHOW0 21 -#define TASK_HM10_READ_BF_FL 22 -#define TASK_HM10_CALL_TLMF_FL 23 -#define TASK_HM10_READ_TLMF_FL 24 -#define TASK_HM10_SUB_HT_CGD1 25 -#define TASK_HM10_UN_HT_CGD1 26 -#define TASK_HM10_READ_B_CGD1 27 - -#define TASK_HM10_READ_B_MJ 29 -#define TASK_HM10_SUB_HT_MJ 30 - -#define TASK_HM10_STATUS_EVENT 32 - -#define TASK_HM10_DONE 99 - - - - - -void HM10_Launchtask(uint8_t task, uint8_t slot, uint8_t delay){ - HM10_TASK_LIST[slot][0] = task; - HM10_TASK_LIST[slot][1] = delay; - HM10_TASK_LIST[slot+1][0] = TASK_HM10_NOTASK; - HM10.current_task_delay = HM10_TASK_LIST[0][1]; -} - -void HM10_TaskReplaceInSlot(uint8_t task, uint8_t slot){ - HM10.last_command = HM10_TASK_LIST[slot][0]; - HM10_TASK_LIST[slot][0] = task; -} - -void HM10_ReverseMAC(uint8_t _mac[]){ - uint8_t _reversedMAC[6]; - for (uint8_t i=0; i<6; i++){ - _reversedMAC[5-i] = _mac[i]; - } - memcpy(_mac,_reversedMAC, sizeof(_reversedMAC)); -} - - - - - -void HM10_Reset(void) { HM10_Launchtask(TASK_HM10_DISCONN,0,1); - HM10_Launchtask(TASK_HM10_ROLE1,1,1); - HM10_Launchtask(TASK_HM10_IMME1,2,1); - HM10_Launchtask(TASK_HM10_RESET,3,1); - HM10_Launchtask(TASK_HM10_VERSION,4,10); - HM10_Launchtask(TASK_HM10_SCAN9,5,2); - HM10_Launchtask(TASK_HM10_DISC,6,2); - HM10_Launchtask(TASK_HM10_STATUS_EVENT,7,2); - } - -void HM10_Discovery_Scan(void) { - HM10_Launchtask(TASK_HM10_DISCONN,0,1); - HM10_Launchtask(TASK_HM10_DISC,1,5); - HM10_Launchtask(TASK_HM10_STATUS_EVENT,2,2); - } - -void HM10_Read_LYWSD03(void) { - HM10_Launchtask(TASK_HM10_CONN,0,1); - HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); - HM10_Launchtask(TASK_HM10_SUB_L3,2,20); - HM10_Launchtask(TASK_HM10_UN_L3,3,80); - HM10_Launchtask(TASK_HM10_READ_BT_L3,4,5); - HM10_Launchtask(TASK_HM10_DISCONN,5,5); - } - -void HM10_Read_LYWSD02(void) { - HM10_Launchtask(TASK_HM10_CONN,0,1); - HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); - HM10_Launchtask(TASK_HM10_SUB_L2,2,20); - HM10_Launchtask(TASK_HM10_UN_L2,3,80); - HM10_Launchtask(TASK_HM10_READ_BT_L2,4,5); - HM10_Launchtask(TASK_HM10_DISCONN,5,5); - } - -void HM10_Time_LYWSD02(void) { - HM10_Launchtask(TASK_HM10_DISCONN,0,0); - HM10_Launchtask(TASK_HM10_CONN,1,5); - HM10_Launchtask(TASK_HM10_FEEDBACK,2,35); - HM10_Launchtask(TASK_HM10_TIME_L2,3,20); - HM10_Launchtask(TASK_HM10_DISCONN,4,5); - } - -void HM10_Read_Flora(void) { - HM10_Launchtask(TASK_HM10_DISCONN,0,0); - HM10_Launchtask(TASK_HM10_CONN,1,1); - HM10_Launchtask(TASK_HM10_FEEDBACK,2,5); - HM10_Launchtask(TASK_HM10_READ_BF_FL,3,20); - HM10_Launchtask(TASK_HM10_CALL_TLMF_FL,4,30); - HM10_Launchtask(TASK_HM10_DISCONN,5,10); - } - -void HM10_Read_CGD1(void) { - HM10_Launchtask(TASK_HM10_CONN,0,1); - HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); - HM10_Launchtask(TASK_HM10_SUB_HT_CGD1,2,20); - HM10_Launchtask(TASK_HM10_UN_HT_CGD1,3,10); - HM10_Launchtask(TASK_HM10_READ_B_CGD1,4,5); - HM10_Launchtask(TASK_HM10_DISCONN,5,5); - } - -void HM10_Read_MJ_HT_V1(void) { - HM10_Launchtask(TASK_HM10_CONN,0,1); - HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); - HM10_Launchtask(TASK_HM10_READ_B_MJ,2,20); - HM10_Launchtask(TASK_HM10_SUB_HT_MJ,3,10); - HM10_Launchtask(TASK_HM10_DISCONN,4,5); - } -# 343 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_62_MI_HM10.ino" -uint32_t MIBLEgetSensorSlot(uint8_t (&_serial)[6], uint16_t _type){ - - DEBUG_SENSOR_LOG(PSTR("%s: will test ID-type: %x"),D_CMND_HM10, _type); - bool _success = false; - for (uint32_t i=0;i<6;i++){ - if(_type == kHM10SlaveID[i]){ - DEBUG_SENSOR_LOG(PSTR("HM10: ID is type %u"), i); - _type = i+1; - _success = true; - } - else { - DEBUG_SENSOR_LOG(PSTR("%s: ID-type is not: %x"),D_CMND_HM10,kHM10SlaveID[i]); - } - } - if(!_success) return 0xff; - - DEBUG_SENSOR_LOG(PSTR("%s: vector size %u"),D_CMND_HM10, MIBLEsensors.size()); - for(uint32_t i=0; ibegin(HM10.serialSpeed)) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s start serial communication fixed to 115200 baud"),D_CMND_HM10); - if (HM10Serial->hardwareSerial()) { - ClaimSerial(); - DEBUG_SENSOR_LOG(PSTR("%s: claim HW"),D_CMND_HM10); - } - HM10_Reset(); - HM10.mode.pending_task = 1; - HM10.mode.init = 1; - HM10.period = Settings.tele_period; - DEBUG_SENSOR_LOG(PSTR("%s_TASK_LIST initialized, now return to main loop"),D_CMND_HM10); - } - return; -} - - - - - -void HM10parseMiBeacon(char * _buf, uint32_t _slot){ - float _tempFloat; - mi_beacon_t _beacon; - if (MIBLEsensors[_slot].type==MJ_HT_V1 || MIBLEsensors[_slot].type==CGG1){ - memcpy((uint8_t*)&_beacon+1,(uint8_t*)_buf, sizeof(_beacon)); - memcpy((uint8_t*)&_beacon.Mac,(uint8_t*)&_beacon.Mac+1,6); - } - else{ - memcpy((void*)&_beacon,(void*)_buf, sizeof(_beacon)); - } - HM10_ReverseMAC(_beacon.Mac); - if(memcmp(_beacon.Mac,MIBLEsensors[_slot].serial,sizeof(_beacon.Mac))!=0){ - if (MIBLEsensors[_slot].showedUp>3) return; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: remove garbage sensor"),D_CMND_HM10); - DEBUG_SENSOR_LOG(PSTR("%s i: %x %x %x %x %x %x"),D_CMND_HM10, MIBLEsensors[_slot].serial[5], MIBLEsensors[_slot].serial[4],MIBLEsensors[_slot].serial[3],MIBLEsensors[_slot].serial[2],MIBLEsensors[_slot].serial[1],MIBLEsensors[_slot].serial[0]); - DEBUG_SENSOR_LOG(PSTR("%s n: %x %x %x %x %x %x"),D_CMND_HM10, _beacon.Mac[5], _beacon.Mac[4], _beacon.Mac[3],_beacon.Mac[2],_beacon.Mac[1],_beacon.Mac[0]); - MIBLEsensors.erase(MIBLEsensors.begin()+_slot); - return; - } - if (MIBLEsensors[_slot].showedUp<4) MIBLEsensors[_slot].showedUp++; - - DEBUG_SENSOR_LOG(PSTR("MiBeacon type:%02x: %02x %02x %02x %02x %02x %02x %02x %02x"),_beacon.type, (uint8_t)_buf[0],(uint8_t)_buf[1],(uint8_t)_buf[2],(uint8_t)_buf[3],(uint8_t)_buf[4],(uint8_t)_buf[5],(uint8_t)_buf[6],(uint8_t)_buf[7]); - DEBUG_SENSOR_LOG(PSTR(" type:%02x: %02x %02x %02x %02x %02x %02x %02x %02x"),_beacon.type, (uint8_t)_buf[8],(uint8_t)_buf[9],(uint8_t)_buf[10],(uint8_t)_buf[11],(uint8_t)_buf[12],(uint8_t)_buf[13],(uint8_t)_buf[14],(uint8_t)_buf[15]); - - if(MIBLEsensors[_slot].type==4 || MIBLEsensors[_slot].type==6){ - DEBUG_SENSOR_LOG(PSTR("LYWSD03 and CGD1 no support for MiBeacon, type %u"),MIBLEsensors[_slot].type); - return; - } - DEBUG_SENSOR_LOG(PSTR("%s at slot %u"), kHM10SlaveType[MIBLEsensors[_slot].type-1],_slot); - switch(_beacon.type){ - case 0x04: - _tempFloat=(float)(_beacon.temp)/10.0f; - if(_tempFloat<60){ - MIBLEsensors[_slot].temp=_tempFloat; - DEBUG_SENSOR_LOG(PSTR("Mode 4: temp updated")); - } - DEBUG_SENSOR_LOG(PSTR("Mode 4: U16: %u Temp"), _beacon.temp ); - break; - case 0x06: - _tempFloat=(float)(_beacon.hum)/10.0f; - if(_tempFloat<101){ - MIBLEsensors[_slot].hum=_tempFloat; - DEBUG_SENSOR_LOG(PSTR("Mode 6: hum updated")); - } - DEBUG_SENSOR_LOG(PSTR("Mode 6: U16: %u Hum"), _beacon.hum); - break; - case 0x07: - MIBLEsensors[_slot].lux=_beacon.lux & 0x00ffffff; - DEBUG_SENSOR_LOG(PSTR("Mode 7: U24: %u Lux"), _beacon.lux & 0x00ffffff); - break; - case 0x08: - _tempFloat =(float)_beacon.moist; - if(_tempFloat<100){ - MIBLEsensors[_slot].moisture=_tempFloat; - DEBUG_SENSOR_LOG(PSTR("Mode 8: moisture updated")); - } - DEBUG_SENSOR_LOG(PSTR("Mode 8: U8: %u Moisture"), _beacon.moist); - break; - case 0x09: - _tempFloat=(float)(_beacon.fert); - if(_tempFloat<65535){ - MIBLEsensors[_slot].fertility=_tempFloat; - DEBUG_SENSOR_LOG(PSTR("Mode 9: fertility updated")); - } - DEBUG_SENSOR_LOG(PSTR("Mode 9: U16: %u Fertility"), _beacon.fert); - break; - case 0x0a: - if(_beacon.bat<101){ - MIBLEsensors[_slot].bat = _beacon.bat; - DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated")); - } - DEBUG_SENSOR_LOG(PSTR("Mode a: U8: %u %%"), _beacon.bat); - break; - case 0x0d: - _tempFloat=(float)(_beacon.HT.temp)/10.0f; - if(_tempFloat<60){ - MIBLEsensors[_slot].temp = _tempFloat; - DEBUG_SENSOR_LOG(PSTR("Mode d: temp updated")); - } - _tempFloat=(float)(_beacon.HT.hum)/10.0f; - if(_tempFloat<100){ - MIBLEsensors[_slot].hum = _tempFloat; - DEBUG_SENSOR_LOG(PSTR("Mode d: hum updated")); - } - DEBUG_SENSOR_LOG(PSTR("Mode d: U16: %x Temp U16: %x Hum"), _beacon.HT.temp, _beacon.HT.hum); - break; - } -} - -void HM10ParseResponse(char *buf, uint16_t bufsize) { - if (!strncmp(buf,"OK",2)) { - DEBUG_SENSOR_LOG(PSTR("%s: got OK"),D_CMND_HM10); - } - if (!strncmp(buf,"HMSoft",6)) { - const char* _fw = "000"; - memcpy((void *)_fw,(void *)(buf+8),3); - HM10.firmware = atoi(_fw); - DEBUG_SENSOR_LOG(PSTR("%s: Firmware: %d"),D_CMND_HM10, HM10.firmware); - return; - } - char * _pos = strstr(buf, "ISA:"); - if(_pos) { - uint8_t _newMacArray[6] = {0}; - memcpy((void *)_newMacArray,(void *)(_pos+4),6); - HM10_ReverseMAC(_newMacArray); - DEBUG_SENSOR_LOG(PSTR("%s: MAC-array: %02x%02x%02x%02x%02x%02x"),D_CMND_HM10,_newMacArray[0],_newMacArray[1],_newMacArray[2],_newMacArray[3],_newMacArray[4],_newMacArray[5]); - uint16_t _type=0xffff; - - for (uint32_t idx =10;idxavailable()) { - - if(iread(); - } - i++; - success = true; - } - - if(i==0) return success; - - switch (HM10.mode.awaiting){ - case bat: - if (HM10.mode.connected) { - if (HM10readBat(ret)){ - HM10.mode.awaiting = none; - HM10.current_task_delay = 0; - } - } - break; - case tempHumLY: - if (HM10.mode.connected) HM10readHT_LY(ret); - break; - case tempHumCGD1: - if (HM10.mode.connected) HM10readHT_CGD1(ret); - break; - case TLMF: - if (HM10.mode.connected) HM10readTLMF(ret); - break; - case discScan: - if(success) { - HM10ParseResponse(ret,i); - } - break; - case tempHumMJ: - if (HM10.mode.connected) HM10readHT_MJ_HT_V1(ret); - break; - case none: - if(success) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: response: %s"),D_CMND_HM10, (char *)ret); - - - - HM10ParseResponse(ret,i); - } - break; - } - memset(ret,0,i); - return success; -} - - - - - -void HM10_TaskEvery100ms(){ - if (HM10.current_task_delay == 0) { - uint8_t i = 0; - bool runningTaskLoop = true; - while (runningTaskLoop) { - switch(HM10_TASK_LIST[i][0]) { - case TASK_HM10_ROLE1: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: set role to 1"),D_CMND_HM10); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+ROLE1"); - break; - case TASK_HM10_IMME1: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: set imme to 1"),D_CMND_HM10); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+IMME1"); - break; - case TASK_HM10_DISC: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: start discovery"),D_CMND_HM10); - HM10.current_task_delay = 100; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10.mode.awaiting = discScan; - HM10Serial->write("AT+DISA?"); - break; - case TASK_HM10_VERSION: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read version"),D_CMND_HM10); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+VERR?"); - break; - case TASK_HM10_NAME: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read name"),D_CMND_HM10); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+NAME?"); - break; - case TASK_HM10_CONN: - char _con[20]; - sprintf_P(_con,"AT+CON%02x%02x%02x%02x%02x%02x",MIBLEsensors[HM10.state.sensor].serial[0],MIBLEsensors[HM10.state.sensor].serial[1],MIBLEsensors[HM10.state.sensor].serial[2],MIBLEsensors[HM10.state.sensor].serial[3],MIBLEsensors[HM10.state.sensor].serial[4],MIBLEsensors[HM10.state.sensor].serial[5]); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: connect %s"),D_CMND_HM10, _con); - HM10.current_task_delay = 2; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write(_con); - HM10.mode.awaiting = none; - HM10.mode.connected = true; - break; - case TASK_HM10_DISCONN: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: disconnect"),D_CMND_HM10); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT"); - break; - case TASK_HM10_RESET: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: Reset Device"),D_CMND_HM10); - HM10Serial->write("AT+RESET"); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - break; - case TASK_HM10_SUB_L3: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: subscribe"),D_CMND_HM10); - HM10.current_task_delay = 25; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - HM10.mode.awaiting = tempHumLY; - runningTaskLoop = false; - HM10Serial->write("AT+NOTIFY_ON0037"); - break; - case TASK_HM10_UN_L3: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: un-subscribe"),D_CMND_HM10); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10.mode.awaiting = none; - HM10Serial->write("AT+NOTIFYOFF0037"); - break; - case TASK_HM10_SUB_L2: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: subscribe"),D_CMND_HM10); - HM10.current_task_delay = 25; - HM10.mode.awaiting = tempHumLY; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+NOTIFY_ON003C"); - break; - case TASK_HM10_UN_L2: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: un-subscribe"),D_CMND_HM10); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10.mode.awaiting = none; - HM10Serial->write("AT+NOTIFYOFF003C"); - break; - case TASK_HM10_TIME_L2: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: set time"),D_CMND_HM10); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10.time = Rtc.utc_time; - HM10Serial->write("AT+SEND_DATAWR002F"); - HM10Serial->write(HM10.timebuf,4); - HM10Serial->write(Rtc.time_timezone / 60); - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s Time-string: %x%x%x%x%x"),D_CMND_HM10, HM10.timebuf[0],HM10.timebuf[1],HM10.timebuf[2],HM10.timebuf[3],(Rtc.time_timezone /60)); - break; - case TASK_HM10_READ_BT_L3: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 003A"),D_CMND_HM10); - HM10.current_task_delay = 2; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+READDATA003A?"); - HM10.mode.awaiting = bat; - break; - case TASK_HM10_READ_BT_L2: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0043"),D_CMND_HM10); - HM10.current_task_delay = 2; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+READDATA0043?"); - HM10.mode.awaiting = bat; - break; - case TASK_HM10_READ_BF_FL: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0038"),D_CMND_HM10); - HM10.current_task_delay = 2; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+READDATA0038?"); - HM10.mode.awaiting = bat; - break; - case TASK_HM10_CALL_TLMF_FL: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: write to handle 0033"),D_CMND_HM10); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_READ_TLMF_FL,i); - runningTaskLoop = false; - HM10Serial->write("AT+SEND_DATAWR0033"); - HM10Serial->write(0xa0); - HM10Serial->write(0x1f); - HM10.mode.awaiting = none; - break; - case TASK_HM10_READ_TLMF_FL: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0035"),D_CMND_HM10); - HM10.current_task_delay = 2; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+READDATA0035?"); - HM10.mode.awaiting = TLMF; - break; - case TASK_HM10_READ_B_CGD1: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0011"),D_CMND_HM10); - HM10.current_task_delay = 2; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+READDATA0011?"); - HM10.mode.awaiting = bat; - break; - case TASK_HM10_SUB_HT_CGD1: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: subscribe 4b"),D_CMND_HM10); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10.mode.awaiting = tempHumCGD1; - HM10Serial->write("AT+NOTIFY_ON004b"); - break; - case TASK_HM10_UN_HT_CGD1: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: un-subscribe 4b"),D_CMND_HM10); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10.mode.awaiting = none; - HM10Serial->write("AT+NOTIFYOFF004b"); - break; - case TASK_HM10_SCAN9: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: scan time to 9"),D_CMND_HM10); - HM10.current_task_delay = 2; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+SCAN9"); - break; - case TASK_HM10_READ_B_MJ: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0x18"),D_CMND_HM10); - HM10.current_task_delay = 2; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+READDATA0018?"); - HM10.mode.awaiting = bat; - break; - case TASK_HM10_SUB_HT_MJ: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: subscribe to 0x0f"),D_CMND_HM10); - HM10.current_task_delay = 10; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+NOTIFY_ON000F"); - HM10.mode.awaiting = tempHumMJ; - break; - case TASK_HM10_FEEDBACK: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: get response"),D_CMND_HM10); - HM10SerialHandleFeedback(); - HM10.current_task_delay = HM10_TASK_LIST[i+1][1];; - HM10_TASK_LIST[i][0] = TASK_HM10_DONE; - runningTaskLoop = false; - break; - case TASK_HM10_STATUS_EVENT: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: show status"),D_CMND_HM10); - HM10StatusInfo(); - HM10.current_task_delay = HM10_TASK_LIST[i+1][1];; - HM10_TASK_LIST[i][0] = TASK_HM10_DONE; - runningTaskLoop = false; - break; - case TASK_HM10_DONE: - - - if(HM10_TASK_LIST[i+1][0] == TASK_HM10_NOTASK) { - DEBUG_SENSOR_LOG(PSTR("%sno Tasks left"),D_CMND_HM10); - DEBUG_SENSOR_LOG(PSTR("%sHM10_TASK_DONE current slot %u"),D_CMND_HM10, i); - for (uint8_t j = 0; j < HM10_MAX_TASK_NUMBER+1; j++) { - DEBUG_SENSOR_LOG(PSTR("%sHM10_TASK cleanup slot %u"),D_CMND_HM10, j); - HM10_TASK_LIST[j][0] = TASK_HM10_NOTASK; - HM10_TASK_LIST[j][1] = 0; - } - runningTaskLoop = false; - HM10.mode.pending_task = 0; - break; - } - } - i++; - } - } - else { - HM10.current_task_delay--; - } -} - -void HM10StatusInfo(){ - char stemp[20]; - snprintf_P(stemp, sizeof(stemp),PSTR("{%s:{\"found\": %u}}"),D_CMND_HM10, MIBLEsensors.size()); - AddLog_P2(LOG_LEVEL_INFO, stemp); - RulesProcessEvent(stemp); -} - - - - - - -void HM10EverySecond(bool restart){ - static uint32_t _counter = 0; - static uint32_t _nextSensorSlot = 0; - static uint32_t _lastDiscovery = 0; - - if(restart){ - _counter = 0; - _lastDiscovery = 0; - return; - } - - if(HM10.firmware == 0) return; - if(HM10.mode.pending_task == 1) return; - if(MIBLEsensors.size()==0 && !HM10.mode.autoScan) return; - - if((HM10.period-_counter)>15 && HM10.mode.autoScan) { - if(_counter-_lastDiscovery>HM10.autoScanInterval){ - HM10_Discovery_Scan(); - HM10.mode.pending_task = 1; - _counter+=12; - _lastDiscovery = _counter; - return; - } - } - - if(_counter==0) { - HM10.state.sensor = _nextSensorSlot; - _nextSensorSlot++; - HM10.mode.pending_task = 1; - switch(MIBLEsensors[HM10.state.sensor].type){ - case FLORA: - HM10_Read_Flora(); - break; - case MJ_HT_V1: case CGG1: - HM10_Read_MJ_HT_V1(); - break; - case LYWSD02: - HM10_Read_LYWSD02(); - break; - case LYWSD03MMC: - HM10_Read_LYWSD03(); - break; - case CGD1: - HM10_Read_CGD1(); - break; - default: - HM10.mode.pending_task = 0; - } - if (HM10.state.sensor==MIBLEsensors.size()-1) { - _nextSensorSlot= 0; - _counter++; - } - DEBUG_SENSOR_LOG(PSTR("%s: active sensor now: %u"),D_CMND_HM10, HM10.state.sensor); - } - else _counter++; - if (_counter>HM10.period) { - _counter = 0; - _lastDiscovery = 0; - } -} - - - - - -bool HM10Cmd(void) { - char command[CMDSZ]; - bool serviced = true; - uint8_t disp_len = strlen(D_CMND_HM10); - - if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_HM10), disp_len)) { - uint32_t command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kHM10_Commands); - switch (command_code) { - case CMND_HM10_PERIOD: - if (XdrvMailbox.data_len > 0) { - if (XdrvMailbox.payload==1) { - HM10EverySecond(true); - XdrvMailbox.payload = HM10.period; - } - else { - HM10.period = XdrvMailbox.payload; - } - } - else { - XdrvMailbox.payload = HM10.period; - } - Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); - break; - case CMND_HM10_AUTO: - if (XdrvMailbox.data_len > 0) { - if (XdrvMailbox.payload>0) { - HM10.mode.autoScan = 1; - HM10.autoScanInterval = XdrvMailbox.payload; - } - else { - HM10.mode.autoScan = 0; - HM10.autoScanInterval = 0; - } - } - else { - XdrvMailbox.payload = HM10.autoScanInterval; - } - Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); - break; - case CMND_HM10_BAUD: - if (XdrvMailbox.data_len > 0) { - HM10.serialSpeed = XdrvMailbox.payload; - HM10Serial->begin(HM10.serialSpeed); - } - else { - XdrvMailbox.payload = HM10.serialSpeed; - } - Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); - break; - case CMND_HM10_TIME: - if (XdrvMailbox.data_len > 0) { - if(MIBLEsensors.size()>XdrvMailbox.payload){ - if(MIBLEsensors[XdrvMailbox.payload].type == LYWSD02){ - HM10.state.sensor = XdrvMailbox.payload; - HM10_Time_LYWSD02(); - } - } - } - Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); - break; - case CMND_HM10_PAGE: - if (XdrvMailbox.data_len > 0) { - if (XdrvMailbox.payload == 0) XdrvMailbox.payload = HM10.perPage; - HM10.perPage = XdrvMailbox.payload; - } - else XdrvMailbox.payload = HM10.perPage; - Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); - break; - case CMND_HM10_AT: - HM10Serial->write("AT"); - if (strlen(XdrvMailbox.data)!=0) { - HM10Serial->write("+"); - HM10Serial->write(XdrvMailbox.data); - Response_P(S_JSON_HM10_COMMAND, ":AT+",XdrvMailbox.data); - } - else Response_P(S_JSON_HM10_COMMAND, ":AT",XdrvMailbox.data); - break; - case CMND_HM10_DISC_SCAN: - HM10_Discovery_Scan(); - Response_P(S_JSON_HM10_COMMAND, command, ""); - break; - default: - - serviced = false; - break; - } - } else { - return false; - } - return serviced; -} - - - - - - -const char HTTP_HM10[] PROGMEM = "{s}HM10 V%u{m}%u%s / %u{e}"; -const char HTTP_HM10_SERIAL[] PROGMEM = "{s}%s %s{m}%02x:%02x:%02x:%02x:%02x:%02x%{e}"; -const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u%%{e}"; -const char HTTP_HM10_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%uus/cm{e}"; -const char HTTP_HM10_HL[] PROGMEM = "{s}
{m}
{e}"; - -void HM10Show(bool json) -{ - if (json) { - for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { - char slave[33]; - sprintf_P(slave,"%s-%02x%02x%02x",kHM10SlaveType[MIBLEsensors[i].type-1],MIBLEsensors[i].serial[3],MIBLEsensors[i].serial[4],MIBLEsensors[i].serial[5]); - ResponseAppend_P(PSTR(",\"%s\":{"),slave); - if (MIBLEsensors[i].type==FLORA){ - if(!isnan(MIBLEsensors[i].temp)){ - char temperature[FLOATSZ]; - dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, temperature); - ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), temperature); - } - else { - ResponseAppend_P(PSTR("}")); - continue; - } - if(MIBLEsensors[i].lux!=0x0ffffff){ - ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%u"), MIBLEsensors[i].lux); - } - if(!isnan(MIBLEsensors[i].moisture)){ - ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%d"), MIBLEsensors[i].moisture); - } - if(!isnan(MIBLEsensors[i].fertility)){ - ResponseAppend_P(PSTR(",\"Fertility\":%d"), MIBLEsensors[i].fertility); - } - } - if (MIBLEsensors[i].type>FLORA){ - if(!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp)){ - ResponseAppendTHD(MIBLEsensors[i].temp, MIBLEsensors[i].hum); - } - } - if(MIBLEsensors[i].bat!=0x00){ - ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors[i].bat); - } - ResponseAppend_P(PSTR("}")); - } -#ifdef USE_WEBSERVER - } else { - static uint16_t _page = 0; - static uint16_t _counter = 0; - int32_t i = _page * HM10.perPage; - uint32_t j = i + HM10.perPage; - if (j+1>MIBLEsensors.size()){ - j = MIBLEsensors.size(); - } - char stemp[5] ={0}; - if (MIBLEsensors.size()-(_page*HM10.perPage)>1 && HM10.perPage!=1) { - sprintf_P(stemp,"-%u",j); - } - if (MIBLEsensors.size()==0) i=-1; - - WSContentSend_PD(HTTP_HM10, HM10.firmware, i+1,stemp,MIBLEsensors.size()); - for (i; iFLORA){ - if(!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp)){ - WSContentSend_THD(kHM10SlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].temp, MIBLEsensors[i].hum); - } - } - if(MIBLEsensors[i].bat!=0x00){ - WSContentSend_PD(HTTP_BATTERY, kHM10SlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].bat); - } - } - _counter++; - if(_counter>3) { - _page++; - _counter=0; - } - if(MIBLEsensors.size()%HM10.perPage==0 && _page==MIBLEsensors.size()/HM10.perPage) _page=0; - if(_page>MIBLEsensors.size()/HM10.perPage) _page=0; -#endif - } -} - - - - - -bool Xsns62(uint8_t function) -{ - bool result = false; - - if ((pin[GPIO_HM10_RX] < 99) && (pin[GPIO_HM10_TX] < 99)) { - switch (function) { - case FUNC_INIT: - HM10SerialInit(); - break; - case FUNC_EVERY_50_MSECOND: - HM10SerialHandleFeedback(); - break; - case FUNC_EVERY_100_MSECOND: - if (HM10_TASK_LIST[0][0] != TASK_HM10_NOTASK) { - HM10_TaskEvery100ms(); - } - break; - case FUNC_EVERY_SECOND: - HM10EverySecond(false); - break; - case FUNC_COMMAND: - result = HM10Cmd(); - break; - case FUNC_JSON_APPEND: - HM10Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - HM10Show(0); - break; -#endif - } - } - return result; -} -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_63_aht1x.ino" -# 21 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_63_aht1x.ino" -#ifdef USE_I2C -#ifdef USE_AHT1x -# 38 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_63_aht1x.ino" -#define XSNS_63 63 -#define XI2C_43 43 - -#define AHT1X_ADDR1 0x38 -#define AHT1X_ADDR2 0x39 - -#define AHT1X_MAX_SENSORS 2 - -#define AHT_HUMIDITY_CONST 100 -#define AHT_TEMPERATURE_CONST 200 -#define AHT_TEMPERATURE_OFFSET 50 -#define KILOBYTE_CONST 1048576.0f - -#define AHT1X_CMD_DELAY 40 -#define AHT1X_RST_DELAY 30 -#define AHT1X_MEAS_DELAY 80 - -uint8_t AHTSetCalCmd[3] = {0xE1, 0x08, 00}; -uint8_t AHTSetCycleCmd[3] = {0xE1, 0x28, 00}; -uint8_t AHTMeasureCmd[3] = {0xAC, 0x33, 00}; -uint8_t AHTResetCmd = 0xBA; - -const char ahtTypes[] PROGMEM = "AHT1X|AHT1X"; -uint8_t aht1x_addresses[] = { AHT1X_ADDR1, AHT1X_ADDR2 }; -uint8_t aht1x_count = 0; -uint8_t aht1x_Pcount = 0; - -struct AHT1XSTRUCT -{ - float humidity = NAN; - float temperature = NAN; - uint8_t address; - char types[6]; -} aht1x_sensors[AHT1X_MAX_SENSORS]; - -bool AHT1XWrite(uint8_t aht1x_idx) -{ - Wire.beginTransmission(aht1x_sensors[aht1x_idx].address); - Wire.write(AHTMeasureCmd, 3); - if (Wire.endTransmission() != 0) - return false; -} - -bool AHT1XRead(uint8_t aht1x_idx) -{ - uint8_t data[6]; - Wire.requestFrom(aht1x_sensors[aht1x_idx].address, (uint8_t) 6); - for(uint8_t i = 0; Wire.available() > 0; i++){ - data[i] = Wire.read(); - } - if ((data[0] & 0x80) == 0x80) return false; - - aht1x_sensors[aht1x_idx].humidity = (((data[1] << 12)| (data[2] << 4) | data[3] >> 4) * AHT_HUMIDITY_CONST / KILOBYTE_CONST); - aht1x_sensors[aht1x_idx].temperature = ((AHT_TEMPERATURE_CONST * (((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5]) / KILOBYTE_CONST) - AHT_TEMPERATURE_OFFSET); - - return (!isnan(aht1x_sensors[aht1x_idx].temperature) && !isnan(aht1x_sensors[aht1x_idx].humidity) && (aht1x_sensors[aht1x_idx].humidity != 0)); -} - - - - - -void AHT1XPoll(void) -{ - aht1x_Pcount++; - switch (aht1x_Pcount) { - case 10: - AHT1XWrite(0); - break; - case 11: - AHT1XRead(0); - aht1x_Pcount = 0; - break; - } -} - -unsigned char AHT1XReadStatus(uint8_t aht1x_address) -{ - uint8_t result = 0; - Wire.requestFrom(aht1x_address, (uint8_t) 1); - result = Wire.read(); - return result; -} - -void AHT1XReset(uint8_t aht1x_address) -{ - Wire.beginTransmission(aht1x_address); - Wire.write(AHTResetCmd); - Wire.endTransmission(); - delay(AHT1X_RST_DELAY); -} - - -bool AHT1XInit(uint8_t aht1x_address) -{ - Wire.beginTransmission(aht1x_address); - Wire.write(AHTSetCalCmd, 3); - if (Wire.endTransmission() != 0) return false; - delay(AHT1X_CMD_DELAY); - if((AHT1XReadStatus(aht1x_address) & 0x68) == 0x08) - return true; - return false; -} - -void AHT1XDetect(void) -{ - for (uint8_t i = 0; i < AHT1X_MAX_SENSORS; i++) { - if (I2cActive(aht1x_addresses[i])) { continue; } - if (AHT1XInit(aht1x_addresses[i])) - { - aht1x_sensors[aht1x_count].address = aht1x_addresses[i]; - GetTextIndexed(aht1x_sensors[aht1x_count].types, sizeof(aht1x_sensors[aht1x_count].types), i, ahtTypes); - I2cSetActiveFound(aht1x_sensors[aht1x_count].address, aht1x_sensors[aht1x_count].types); - aht1x_count = 1; - break; - } - } -} - -void AHT1XShow(bool json) -{ - for (uint32_t i = 0; i < aht1x_count; i++) { - float tem = ConvertTemp(aht1x_sensors[i].temperature); - float hum = ConvertHumidity(aht1x_sensors[i].humidity); - char types[11]; - snprintf_P(types, sizeof(types), PSTR("%s%c0x%02X"), aht1x_sensors[i].types, IndexSeparator(), aht1x_sensors[i].address); - TempHumDewShow(json, ((0 == tele_period) && (0 == i)), types, tem, hum); - } -} - - - - - -bool Xsns63(uint8_t function) -{ - if (!I2cEnabled(XI2C_43)) { return false; } - bool result = false; - - if (FUNC_INIT == function) { - AHT1XDetect(); - } - else if (aht1x_count){ - switch (function) { - case FUNC_EVERY_100_MSECOND: - AHT1XPoll(); - break; - case FUNC_JSON_APPEND: - AHT1XShow(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - AHT1XShow(0); - break; - #endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_64_hrxl.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_64_hrxl.ino" -#ifdef USE_HRXL - - - - - - - -#define XSNS_64 64 - -#include - -#define HRXL_READ_TIMEOUT 400 - -TasmotaSerial *HRXLSerial = nullptr; - -uint32_t hrxl_distance_mm = 0; -bool hrxl_found = false; - - - -void HRXLInit(void) -{ - hrxl_found = false; - if ((pin[GPIO_HRXL_RX] < 99)) - { - HRXLSerial = new TasmotaSerial(pin[GPIO_HRXL_RX], -1, 1); - if (HRXLSerial->begin(9600)) - { - if (HRXLSerial->hardwareSerial()) - ClaimSerial(); - hrxl_found = true; - HRXLSerial->setTimeout(HRXL_READ_TIMEOUT); - } - } -} - -void HRXLEverySecond(void) -{ - if (!hrxl_found) - return; - - int num_read=0; - int sum=0; - while (HRXLSerial->available()>5) - { - if (HRXLSerial->read() != 'R') - continue; - - int d = HRXLSerial->parseInt(); - if (d >= 30 && d<=5000) - { - sum += d; - num_read++; - } - } - if (num_read>1) - hrxl_distance_mm = int(sum / num_read); - -} - - -void HRXLShow(bool json) -{ - char types[5] = "HRXL"; - if (json) - { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_DISTANCE "\":%d}"), types, hrxl_distance_mm); -#ifdef USE_WEBSERVER - } - else - { - WSContentSend_PD(HTTP_SNS_RANGE, types, hrxl_distance_mm); -#endif - } -} - - - - - -bool Xsns64(uint8_t function) -{ - if (pin[GPIO_HRXL_RX] >= 99) - return false; - - switch (function) - { - case FUNC_INIT: - HRXLInit(); - break; - case FUNC_EVERY_SECOND: - HRXLEverySecond(); - break; - case FUNC_JSON_APPEND: - HRXLShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - HRXLShow(0); - break; -#endif - } - return false; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_65_hdc1080.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_65_hdc1080.ino" -#ifdef USE_I2C -#ifdef USE_HDC1080 -# 31 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_65_hdc1080.ino" -#define XSNS_65 65 -#define XI2C_45 45 - -#define HDC1080_ADDR 0x40 - - - -#define HDC_REG_TEMP 0x00 -#define HDC_REG_RH 0x01 -#define HDC_REG_CONFIG 0x02 -#define HDC_REG_SERIAL1 0xFB -#define HDC_REG_SERIAL2 0xFC -#define HDC_REG_SERIAL3 0xFD -#define HDC_REG_MAN_ID 0xFE -#define HDC_REG_DEV_ID 0xFF - - - -#define HDC1080_MAN_ID 0x5449 -#define HDC1080_DEV_ID 0x1050 - - - -#define HDC1080_RST_ON 0x8000 -#define HDC1080_HEAT_ON 0x2000 -#define HDC1080_MODE_ON 0x1000 -#define HDC1080_TRES_11 0x400 -#define HDC1080_HRES_11 0x100 -#define HDC1080_HRES_8 0x80 - - - -#define HDC1080_CONV_TIME 15 -#define HDC1080_TEMP_MULT 0.0025177 -#define HDC1080_RH_MULT 0.0025177 -#define HDC1080_TEMP_OFFSET 40.0 - -const char* hdc_type_name = "HDC1080"; -uint16_t hdc_manufacturer_id = 0; -uint16_t hdc_device_id = 0; - -float hdc_temperature = 0.0; -float hdc_humidity = 0.0; - -uint8_t hdc_valid = 0; - -bool is_reading = false; -uint32_t hdc_next_read; - - - - - -uint16_t HdcReadDeviceId(void) { - return I2cRead16(HDC1080_ADDR, HDC_REG_DEV_ID); -} - - - - - -uint16_t HdcReadManufacturerId(void) { - return I2cRead16(HDC1080_ADDR, HDC_REG_MAN_ID); -} - - - - -void HdcConfig(uint16_t config) { - I2cWrite16(HDC1080_ADDR, HDC_REG_CONFIG, config); -} - - - - - - - -void HdcReset(void) { - uint16_t current = I2cRead16(HDC1080_ADDR, HDC_REG_CONFIG); - - - - - current |= 0x8000; - - I2cWrite16(HDC1080_ADDR, HDC_REG_CONFIG, current); - - delay(HDC1080_CONV_TIME); -} -# 132 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_65_hdc1080.ino" -int8_t HdcTransactionOpen(uint8_t addr, uint8_t reg) { - Wire.beginTransmission((uint8_t) addr); - Wire.write((uint8_t) reg); - return Wire.endTransmission(); -} -# 147 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_65_hdc1080.ino" -int8_t HdcTransactionClose(uint8_t addr, uint8_t *reg_data, uint16_t len) { - if (len != Wire.requestFrom((uint8_t) addr, (uint8_t) len)) { - return 1; - } - - while (len--) { - *reg_data = (uint8_t)Wire.read(); - reg_data++; - } - - return 0; -} - - - - - -void HdcInit(void) { - HdcReset(); - HdcConfig(HDC1080_MODE_ON); -} - - - - - -bool HdcTriggerRead(void) { - int8_t status = HdcTransactionOpen(HDC1080_ADDR, HDC_REG_TEMP); - - hdc_next_read = millis() + HDC1080_CONV_TIME; - - if(status) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HdcTriggerRead: failed to open the transaction for HDC_REG_TEMP. Status = %d"), status); - - return false; - } - - is_reading = true; - - return true; -} -# 197 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_65_hdc1080.ino" -bool HdcRead(void) { - int8_t status = 0; - uint8_t sensor_data[4]; - uint16_t temp_data = 0; - uint16_t rh_data = 0; - - is_reading = false; - - status = HdcTransactionClose(HDC1080_ADDR, sensor_data, 4); - - if(status) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HdcRead: failed to read HDC_REG_TEMP. Status = %d"), status); - - return false; - } - - temp_data = (uint16_t) ((sensor_data[0] << 8) | sensor_data[1]); - rh_data = (uint16_t) ((sensor_data[2] << 8) | sensor_data[3]); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HdcRead: temperature raw data: 0x%04x; humidity raw data: 0x%04x"), temp_data, rh_data); - - - - hdc_temperature = ConvertTemp(HDC1080_TEMP_MULT * (float) (temp_data) - HDC1080_TEMP_OFFSET); - - hdc_humidity = HDC1080_RH_MULT * (float) (rh_data); - - if (hdc_humidity > 100) { hdc_humidity = 100.0; } - if (hdc_humidity < 0) { hdc_humidity = 0.01; } - hdc_humidity = ConvertHumidity(hdc_humidity); - - hdc_valid = SENSOR_MAX_MISS; - - return true; -} - - - - - - -void HdcDetect(void) { - if (I2cActive(HDC1080_ADDR)) { - - - return; - } - - hdc_manufacturer_id = HdcReadManufacturerId(); - hdc_device_id = HdcReadDeviceId(); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HdcDetect: detected device with manufacturerId = 0x%04X and deviceId = 0x%04X"), hdc_manufacturer_id, hdc_device_id); - - if (hdc_device_id == HDC1080_DEV_ID) { - HdcInit(); - I2cSetActiveFound(HDC1080_ADDR, hdc_type_name); - } -} - - - - - - -void HdcEverySecond(void) { - if (uptime &1) { - if (!HdcTriggerRead()) { - AddLogMissed((char*) hdc_type_name, hdc_valid); - } - } -} - - - - - - -void HdcShow(bool json) { - if (hdc_valid) { - TempHumDewShow(json, (0 == tele_period), hdc_type_name, hdc_temperature, hdc_humidity); - } -} - - - - - -bool Xsns65(uint8_t function) -{ - if (!I2cEnabled(XI2C_45)) { - - - return false; - } - - bool result = false; - - if (FUNC_INIT == function) { - HdcDetect(); - } - else if (hdc_device_id) { - switch (function) { - case FUNC_EVERY_50_MSECOND: - if(is_reading && TimeReached(hdc_next_read)) { - if(!HdcRead()) { - AddLogMissed((char*) hdc_type_name, hdc_valid); - } - } - break; - case FUNC_EVERY_SECOND: - HdcEverySecond(); - break; - case FUNC_JSON_APPEND: - HdcShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - HdcShow(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_66_iAQ.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_66_iAQ.ino" -#ifdef USE_I2C -#ifdef USE_IAQ - -#define XSNS_66 66 -#define XI2C_46 46 - -#define I2_ADR_IAQ 0x5a - -#define IAQ_STATUS_OK 0x00 -#define IAQ_STATUS_BUSY 0x01 -#define IAQ_STATUS_WARM 0x10 -#define IAQ_STATUS_ERR 0x80 -#define IAQ_STATUS_I2C_ERR 0xFF - -struct { - int32_t resistance; - uint16_t pred; - uint16_t Tvoc; - uint8_t status; - bool ready; -} iAQ; - -void IAQ_Init(void) -{ - if (!I2cSetDevice(I2_ADR_IAQ)) { return; } - I2cSetActiveFound(I2_ADR_IAQ, "IAQ"); - iAQ.ready = true; -} - -void IAQ_Read(void) -{ - uint8_t buf[9]; - buf[2] = IAQ_STATUS_I2C_ERR; - Wire.requestFrom((uint8_t)I2_ADR_IAQ,sizeof(buf)); - for( uint32_t i=0; i<9; i++ ) { - buf[i]= Wire.read(); - } - - iAQ.pred = (buf[0]<<8) + buf[1]; - iAQ.status = buf[2]; - iAQ.resistance = ((uint32_t)buf[3]<<24) + ((uint32_t)buf[4]<<16) + ((uint32_t)buf[5]<<8) + (uint32_t)buf[6]; - iAQ.Tvoc = (buf[7]<<8) + buf[8]; -} - - - - - -#ifdef USE_WEBSERVER -const char HTTP_SNS_IAQ[] PROGMEM = - "{s}iAQ-Core " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}" - "{s}iAQ-Core " D_TVOC "{m}%d " D_UNIT_PARTS_PER_BILLION "{e}"; - -const char HTTP_SNS_IAQ_ERROR[] PROGMEM = - "{s}iAQ-Core {m} %s {e}"; -#endif - -void IAQ_Show(uint8_t json) -{ - IAQ_Read(); - - if (json) { - if (iAQ.status!=IAQ_STATUS_OK){ - AddLog_P2(LOG_LEVEL_INFO, PSTR("iAQ: " D_ERROR " %x" ),iAQ.status); - return; - } - else { - ResponseAppend_P(PSTR(",\"IAQ\":{\"" D_JSON_ECO2 "\":%u,\"" D_JSON_TVOC "\":%u,\"" D_JSON_RESISTANCE "\":%u}"), iAQ.pred, iAQ.Tvoc, iAQ.resistance); -#ifdef USE_DOMOTICZ - if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, iAQ.pred); -#endif - } -#ifdef USE_WEBSERVER - } else { - switch(iAQ.status){ - case IAQ_STATUS_OK: - WSContentSend_PD(HTTP_SNS_IAQ, iAQ.pred, iAQ.Tvoc); - break; - case IAQ_STATUS_WARM: - WSContentSend_PD(HTTP_SNS_IAQ_ERROR, D_START); - break; - default: - WSContentSend_PD(HTTP_SNS_IAQ_ERROR, D_ERROR); - } -#endif - } -} - - - - - -bool Xsns66(byte function) -{ - if (!I2cEnabled(XI2C_46)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - IAQ_Init(); - } - else if (iAQ.ready) { - switch (function) { - case FUNC_JSON_APPEND: - IAQ_Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - IAQ_Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_67_as3935.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_67_as3935.ino" -#ifdef USE_I2C -#ifdef USE_AS3935 - - - - - - -#define XSNS_67 67 -#define XI2C_48 48 - -#define D_NAME_AS3935 "AS3935" -#define AS3935_ADDR 0x03 - - -#define IRQ_TBL 0x03, 0x0F, 0 -#define ENERGY_RAW_1 0x04, 0xFF, 0 -#define ENERGY_RAW_2 0x05, 0xFF, 0 -#define ENERGY_RAW_3 0x06, 0x1F, 0 -#define LGHT_DIST 0x07, 0x3F, 0 -#define DISP_TRCO 0x08, 0x20, 5 -#define DISP_LCO 0x08, 0x80, 7 -#define TUNE_CAPS 0x08, 0x0F, 0 -#define AFE_GB 0x00, 0x3E, 0 -#define WDTH 0x01, 0x0F, 0 -#define NF_LEVEL 0x01, 0x70, 4 -#define SPIKE_REJECT 0x02, 0x0F, 0 -#define MIN_NUM_LIGH 0x02, 0x30, 4 -#define DISTURBER 0x03, 0x20, 5 -#define LCO_FDIV 0x03, 0xC0, 6 - -#define INDOORS 0x24 -#define OUTDOORS 0x1C - - - -#define D_AS3935_GAIN "gain:" -#define D_AS3935_ENERGY "energy:" -#define D_AS3935_DISTANCE "distance:" -#define D_AS3935_DISTURBER "disturber:" -#define D_AS3935_VRMS "µVrms:" - -#define D_AS3935_APRX "aprx.:" -#define D_AS3935_AWAY "away" -#define D_AS3935_LIGHT "lightning" -#define D_AS3935_OUT "lightning out of range" -#define D_AS3935_NOT "distance not determined" -#define D_AS3935_ABOVE "lightning overhead" -#define D_AS3935_NOISE "noise detected" -#define D_AS3935_DISTDET "disturber detected" -#define D_AS3935_INTNOEV "Interrupt with no Event!" -#define D_AS3935_NOMESS "listening..." - -#define D_AS3935_ON "On" -#define D_AS3935_OFF "Off" -#define D_AS3935_INDOORS "Indoors" -#define D_AS3935_OUTDOORS "Outdoors" -#define D_AS3935_CAL_FAIL "calibration failed" -#define D_AS3935_CAL_OK "calibration set to:" - - -const char HTTP_SNS_UNIT_KILOMETER[] PROGMEM = D_UNIT_KILOMETER; - -const char HTTP_SNS_AS3935_ENERGY[] PROGMEM = "{s}" D_NAME_AS3935 " " D_AS3935_ENERGY " {m}%d{e}"; -const char HTTP_SNS_AS3935_DISTANZ[] PROGMEM = "{s}" D_NAME_AS3935 " " D_AS3935_DISTANCE " {m}%u " D_UNIT_KILOMETER "{e}"; -const char HTTP_SNS_AS3935_VRMS[] PROGMEM = "{s}" D_NAME_AS3935 " " D_AS3935_VRMS "{m}%#4u (%d){e}"; - -const char HTTP_SNS_AS3935_OUTDOORS[] PROGMEM = "{s}%s " D_AS3935_GAIN " {m}" D_AS3935_OUTDOORS " {e}"; -const char HTTP_SNS_AS3935_INDOORS[] PROGMEM = "{s}%s " D_AS3935_GAIN " {m}" D_AS3935_INDOORS " {e}"; -const char* const HTTP_SNS_AS3935_GAIN[] PROGMEM = {HTTP_SNS_AS3935_INDOORS, HTTP_SNS_AS3935_OUTDOORS}; - -const char HTTP_SNS_AS3935_DIST_ON[] PROGMEM = "{s}%s " D_AS3935_DISTURBER " {m}" D_AS3935_ON " {e}"; -const char HTTP_SNS_AS3935_DIST_OFF[] PROGMEM = "{s}%s " D_AS3935_DISTURBER " {m}" D_AS3935_OFF " {e}"; -const char* const HTTP_SNS_AS3935_DISTURBER[] PROGMEM = {HTTP_SNS_AS3935_DIST_OFF, HTTP_SNS_AS3935_DIST_ON}; - -const char HTTP_SNS_AS3935_EMPTY[] PROGMEM = "{s}%s: " D_AS3935_NOMESS "{e}"; -const char HTTP_SNS_AS3935_OUT[] PROGMEM = "{s}%s: " D_AS3935_OUT "{e}"; -const char HTTP_SNS_AS3935_NOT[] PROGMEM = "{s}%s: " D_AS3935_NOT "{e}"; -const char HTTP_SNS_AS3935_ABOVE[] PROGMEM = "{s}%s: " D_AS3935_ABOVE "{e}"; -const char HTTP_SNS_AS3935_NOISE[] PROGMEM = "{s}%s: " D_AS3935_NOISE "{e}"; -const char HTTP_SNS_AS3935_DISTURB[] PROGMEM = "{s}%s: " D_AS3935_DISTDET "{e}"; -const char HTTP_SNS_AS3935_INTNOEV[] PROGMEM = "{s}%s: " D_AS3935_INTNOEV "{e}"; -const char HTTP_SNS_AS3935_MSG[] PROGMEM = "{s}%s: " D_AS3935_LIGHT " " D_AS3935_APRX " %d " D_UNIT_KILOMETER " " D_AS3935_AWAY "{e}"; -const char* const HTTP_SNS_AS3935_TABLE_1[] PROGMEM = { HTTP_SNS_AS3935_EMPTY, HTTP_SNS_AS3935_MSG, HTTP_SNS_AS3935_OUT, HTTP_SNS_AS3935_NOT, HTTP_SNS_AS3935_ABOVE, HTTP_SNS_AS3935_NOISE, HTTP_SNS_AS3935_DISTURB, HTTP_SNS_AS3935_INTNOEV }; - -const char JSON_SNS_AS3935_EVENTS[] PROGMEM = ",\"%s\":{\"" D_JSON_EVENT "\":%d,\"" D_JSON_DISTANCE "\":%d,\"" D_JSON_ENERGY "\":%u}"; - -const char* const S_JSON_AS3935_COMMAND_ONOFF[] PROGMEM = {"\"" D_AS3935_OFF "\"","\"" D_AS3935_ON"\""}; -const char* const S_JSON_AS3935_COMMAND_GAIN[] PROGMEM = {"\"" D_AS3935_INDOORS "\"", "\"" D_AS3935_OUTDOORS "\""}; -const char* const S_JSON_AS3935_COMMAND_CAL[] PROGMEM = {"" D_AS3935_CAL_FAIL "","" D_AS3935_CAL_OK ""}; - -const char S_JSON_AS3935_COMMAND_STRING[] PROGMEM = "{\"" D_NAME_AS3935 "\":{\"%s\":%s}}"; -const char S_JSON_AS3935_COMMAND_NVALUE[] PROGMEM = "{\"" D_NAME_AS3935 "\":{\"%s\":%d}}"; -const char S_JSON_AS3935_COMMAND_SETTINGS[] PROGMEM = "{\"" D_NAME_AS3935 "\":{\"Gain\":%s,\"NFfloor\":%d,\"uVrms\":%d,\"Tunecaps\":%d,\"MinNumLight\":%d,\"Rejektion\":%d,\"Wdthreshold\":%d,\"MinNFstage\":%d,\"NFAutoTime\":%d,\"DisturberAutoTime\":%d,\"Disturber\":%s,\"NFauto\":%s,\"Disturberauto\":%s,\"NFautomax\":%s,\"Mqttlightevent\":%s}}"; - -const char kAS3935_Commands[] PROGMEM = "setnf|setminstage|setml|default|setgain|settunecaps|setrej|setwdth|disttime|nftime|disturber|autonf|autodisturber|autonfmax|mqttevent|settings|calibrate"; - -enum AS3935_Commands { - CMND_AS3935_SET_NF, - CMND_AS3935_SET_MINNF, - CMND_AS3935_SET_MINLIGHT, - CMND_AS3935_SET_DEF, - CMND_AS3935_SET_GAIN, - CMND_AS3935_SET_TUNE, - CMND_AS3935_SET_REJ, - CMND_AS3935_SET_WDTH, - CMND_AS3935_DISTTIME, - CMND_AS3935_NFTIME, - CMND_AS3935_SET_DISTURBER, - CMND_AS3935_NF_AUTOTUNE, - CMND_AS3935_DIST_AUTOTUNE, - CMND_AS3935_NF_ATUNE_BOTH, - CMND_AS3935_MQTT_LIGHT_EVT, - CMND_AS3935_SETTINGS, - CMND_AS3935_CALIBRATE - }; - -struct AS3935STRUCT -{ - bool autodist_activ = false; - volatile bool detected = false; - volatile bool dispLCO = 0; - uint8_t icount = 0; - uint8_t irq = 0; - uint8_t mqtt_irq = 0; - uint8_t http_irq = 0; - uint8_t http_count_start = 0; - int16_t http_distance = 0; - int16_t distance = 0; - uint16_t http_timer = 0; - uint16_t http_count = 0; - uint16_t nftimer = 0; - uint16_t disttimer = 0; - uint32_t intensity = 0; - uint32_t http_intensity = 0; - volatile uint32_t pulse = 0; -} as3935_sensor; - -uint8_t as3935_active = 0; - -void ICACHE_RAM_ATTR AS3935Isr() { - as3935_sensor.detected = true; -} - -uint8_t AS3935ReadRegister(uint8_t reg, uint8_t mask, uint8_t shift) { - uint8_t data = I2cRead8(AS3935_ADDR, reg); - if (reg == 0x08) Settings.as3935_sensor_cfg[4] = data; - if (reg < 0x04) Settings.as3935_sensor_cfg[reg] = data; - return ((data & mask) >> shift); -} - -void AS3935WriteRegister(uint8_t reg, uint8_t mask, uint8_t shift, uint8_t data) { - uint8_t currentReg = I2cRead8(AS3935_ADDR, reg); - currentReg &= (~mask); - data <<= shift; - data &= mask; - data |= currentReg; - I2cWrite8(AS3935_ADDR, reg, data); - if (reg == 0x08) Settings.as3935_sensor_cfg[4] = I2cRead8(AS3935_ADDR, reg); - if (reg < 0x04) Settings.as3935_sensor_cfg[reg] = I2cRead8(AS3935_ADDR, reg); -} - - - -void ICACHE_RAM_ATTR AS3935CountFreq() { - if (as3935_sensor.dispLCO) - as3935_sensor.pulse++; -} - -bool AS3935AutoTuneCaps(uint8_t irqpin) { - int32_t maxtune = 17500; - uint8_t besttune; - AS3935WriteRegister(LCO_FDIV, 0); - delay(2); - for (uint8_t tune = 0; tune < 16; tune++) { - AS3935WriteRegister(TUNE_CAPS, tune); - delay(2); - AS3935WriteRegister(DISP_LCO,1); - delay(1); - as3935_sensor.dispLCO = true; - as3935_sensor.pulse = 0; - attachInterrupt(digitalPinToInterrupt(irqpin), AS3935CountFreq, RISING); - delay(200); - as3935_sensor.dispLCO = false; - detachInterrupt(irqpin); - AS3935WriteRegister(DISP_LCO,0); - int32_t currentfreq = 500000 - ((as3935_sensor.pulse * 5) * 16); - if(currentfreq < 0) currentfreq = -currentfreq; - if(maxtune > currentfreq) { - maxtune = currentfreq; - besttune = tune; - } - } - if (maxtune >= 17500) - return false; - AS3935SetTuneCaps(besttune); - return true; -} - - - -void AS3935CalibrateRCO() { - I2cWrite8(AS3935_ADDR, 0x3D, 0x96); - AS3935WriteRegister(DISP_TRCO, 1); - delay(2); - AS3935WriteRegister(DISP_TRCO, 0); -} - -uint8_t AS3935TransMinLights(uint8_t min_lights) { - if (5 > min_lights) { - return 0; - } else if (9 > min_lights) { - return 1; - } else if (16 > min_lights) { - return 2; - } else { - return 3; - } -} - -uint8_t AS3935TranslMinLightsInt(uint8_t min_lights) { - switch (min_lights) { - case 0: return 1; - case 1: return 5; - case 2: return 9; - case 3: return 16; - } -} - -uint8_t AS3935TranslIrq(uint8_t irq, uint8_t distance) { - switch(irq) { - case 0: return 7; - case 1: return 5; - case 4: return 6; - case 8: - if (distance == -1) return 2; - else if (distance == 0) return 3; - else if (distance == 1) return 4; - else return 1; - } -} - -void AS3935CalcVrmsLevel(uint16_t &vrms, uint8_t &stage) -{ - uint8_t room = AS3935GetGain(); - uint8_t nflev = AS3935GetNoiseFloor(); - if (room == 0x24) - { - switch (nflev){ - case 0x00: - vrms = 28; - break; - case 0x01: - vrms = 45; - break; - case 0x02: - vrms = 62; - break; - case 0x03: - vrms = 78; - break; - case 0x04: - vrms = 95; - break; - case 0x05: - vrms = 112; - break; - case 0x06: - vrms = 130; - break; - case 0x07: - vrms = 146; - break; - } - stage = nflev; - } - else - { - switch (nflev) - { - case 0x00: - vrms = 390; - break; - case 0x01: - vrms = 630; - break; - case 0x02: - vrms = 860; - break; - case 0x03: - vrms = 1100; - break; - case 0x04: - vrms = 1140; - break; - case 0x05: - vrms = 1570; - break; - case 0x06: - vrms = 1800; - break; - case 0x07: - vrms = 2000; - break; - } - stage = nflev + 8; - } -} - - -uint8_t AS3935GetIRQ() { - delay(2); - return AS3935ReadRegister(IRQ_TBL); -} - -uint8_t AS3935GetDistance() { - return AS3935ReadRegister(LGHT_DIST); -} - -int16_t AS3935CalcDistance() { - uint8_t dist = AS3935GetDistance(); - switch (dist) { - case 0x3F: return -1; - case 0x01: return 1; - case 0x00: return 0; - default: - if (40 < dist){ - return 40; - } - return dist; - } -} - -uint32_t AS3935GetIntensity() { - uint32_t nrgy_raw = (AS3935ReadRegister(ENERGY_RAW_3) << 8); - nrgy_raw |= AS3935ReadRegister(ENERGY_RAW_2); - nrgy_raw <<= 8; - nrgy_raw |= AS3935ReadRegister(ENERGY_RAW_1); - return nrgy_raw; -} - -uint8_t AS3935GetTuneCaps() { - return AS3935ReadRegister(TUNE_CAPS); -} - -void AS3935SetTuneCaps(uint8_t tune) { - AS3935WriteRegister(TUNE_CAPS, tune); - delay(2); - AS3935CalibrateRCO(); -} - -uint8_t AS3935GetDisturber() { - return AS3935ReadRegister(DISTURBER); -} - -uint8_t AS3935SetDisturber(uint8_t stat) { - AS3935WriteRegister(DISTURBER, stat); -} - -uint8_t AS3935GetMinLights() { - return AS3935ReadRegister(MIN_NUM_LIGH); -} - -uint8_t AS3935SetMinLights(uint8_t stat) { - AS3935WriteRegister(MIN_NUM_LIGH, stat); -} - -uint8_t AS3935GetNoiseFloor() { - return AS3935ReadRegister(NF_LEVEL); -} - -uint8_t AS3935SetNoiseFloor(uint8_t noise) { - AS3935WriteRegister(NF_LEVEL , noise); -} - -uint8_t AS3935GetGain() { - if (AS3935ReadRegister(AFE_GB) == OUTDOORS) - return OUTDOORS; - return INDOORS; -} - -uint8_t AS3935SetGain(uint8_t room) { - AS3935WriteRegister(AFE_GB, room); -} - -uint8_t AS3935GetGainInt() { - if (AS3935ReadRegister(AFE_GB) == OUTDOORS) - return 1; -return 0; -} - -uint8_t AS3935GetSpikeRejection() { - return AS3935ReadRegister(SPIKE_REJECT); -} - -void AS3935SetSpikeRejection(uint8_t rej) { - AS3935WriteRegister(SPIKE_REJECT, rej); -} - -uint8_t AS3935GetWdth() { - return AS3935ReadRegister(WDTH); -} - -void AS3935SetWdth(uint8_t wdth) { - AS3935WriteRegister(WDTH, wdth); -} - -bool AS3935AutoTune(){ - detachInterrupt(pin[GPIO_AS3935]); - bool result = AS3935AutoTuneCaps(pin[GPIO_AS3935]); - attachInterrupt(digitalPinToInterrupt(pin[GPIO_AS3935]), AS3935Isr, RISING); - return result; -} - - - -bool AS3935LowerNoiseFloor() { - uint8_t noise = AS3935GetNoiseFloor(); - uint16_t vrms; - uint8_t stage; - AS3935CalcVrmsLevel(vrms, stage); - if (Settings.as3935_functions.nf_autotune_both) { - if (stage == 8 && stage > Settings.as3935_parameter.nf_autotune_min) { - AS3935SetGain(INDOORS); - AS3935SetNoiseFloor(7); - return true; - } - } - if (0 < noise && stage > Settings.as3935_parameter.nf_autotune_min) { - noise--; - AS3935SetNoiseFloor(noise); - return true; - } - return false; -} - -bool AS3935RaiseNoiseFloor() { - uint8_t noise = AS3935GetNoiseFloor(); - uint8_t room = AS3935GetGain(); - if (Settings.as3935_functions.nf_autotune_both) { - if (7 == noise && room == INDOORS) { - AS3935SetGain(OUTDOORS); - AS3935SetNoiseFloor(0); - return true; - } - } - if (7 > noise) { - noise++; - AS3935SetNoiseFloor(noise); - return true; - } - return false; -} - - - -bool AS3935SetDefault() { - I2cWrite8(AS3935_ADDR, 0x3C, 0x96); - delay(2); - Settings.as3935_sensor_cfg[0] = I2cRead8(AS3935_ADDR, 0x00); - Settings.as3935_sensor_cfg[1] = I2cRead8(AS3935_ADDR, 0x01); - Settings.as3935_sensor_cfg[2] = I2cRead8(AS3935_ADDR, 0x02); - Settings.as3935_sensor_cfg[3] = I2cRead8(AS3935_ADDR, 0x03); - Settings.as3935_sensor_cfg[4] = I2cRead8(AS3935_ADDR, 0x08); - Settings.as3935_parameter.nf_autotune_min = 0x00; - Settings.as3935_parameter.nf_autotune_time = 4; - Settings.as3935_parameter.dist_autotune_time = 1; - return true; -} - -void AS3935InitSettings() { - if(Settings.as3935_functions.nf_autotune){ - AS3935SetGain(INDOORS); - AS3935SetNoiseFloor(0); - } - I2cWrite8(AS3935_ADDR, 0x00, Settings.as3935_sensor_cfg[0]); - I2cWrite8(AS3935_ADDR, 0x01, Settings.as3935_sensor_cfg[1]); - I2cWrite8(AS3935_ADDR, 0x02, Settings.as3935_sensor_cfg[2]); - I2cWrite8(AS3935_ADDR, 0x03, Settings.as3935_sensor_cfg[3]); - I2cWrite8(AS3935_ADDR, 0x08, Settings.as3935_sensor_cfg[4]); - delay(2); -} - -void AS3935Setup(void) { - if (Settings.as3935_sensor_cfg[0] == 0x00) { - AS3935SetDefault(); - } else { - AS3935InitSettings(); - } - AS3935CalibrateRCO(); -} - -bool AS3935init() { - uint8_t ret = I2cRead8(AS3935_ADDR, 0x00); - if(INDOORS == ret || OUTDOORS == ret) - return true; - return false; -} - -void AS3935Detect(void) { - if (I2cActive(AS3935_ADDR)) return; - if (AS3935init()) - { - I2cSetActiveFound(AS3935_ADDR, D_NAME_AS3935); - pinMode(pin[GPIO_AS3935], INPUT); - attachInterrupt(digitalPinToInterrupt(pin[GPIO_AS3935]), AS3935Isr, RISING); - AS3935Setup(); - as3935_active = 1; - } -} - -void AS3935EverySecond() { - if (as3935_sensor.detected) { - as3935_sensor.irq = AS3935GetIRQ(); - switch (as3935_sensor.irq) { - case 1: - if (Settings.as3935_functions.nf_autotune) { - if (AS3935RaiseNoiseFloor()) as3935_sensor.nftimer = 0; - } - break; - case 4: - if (Settings.as3935_functions.dist_autotune) { - AS3935SetDisturber(1); - as3935_sensor.autodist_activ = true; - } - break; - case 8: - as3935_sensor.intensity = AS3935GetIntensity(); - as3935_sensor.distance = AS3935CalcDistance(); - as3935_sensor.http_intensity = as3935_sensor.intensity; - as3935_sensor.http_distance = as3935_sensor.distance; - break; - } - - as3935_sensor.http_irq = AS3935TranslIrq(as3935_sensor.irq, as3935_sensor.distance); - - as3935_sensor.mqtt_irq = as3935_sensor.http_irq; - switch (as3935_sensor.mqtt_irq) { - case 5: - case 6: - if (!Settings.as3935_functions.mqtt_only_Light_Event) { - MqttPublishSensor(); - as3935_sensor.http_timer = 10; - } - break; - default: - as3935_sensor.http_timer = 60; - MqttPublishSensor(); - } - - as3935_sensor.intensity = 0; - as3935_sensor.distance = 0; - as3935_sensor.mqtt_irq = 0; - - as3935_sensor.http_count_start = 1; - as3935_sensor.icount++; - as3935_sensor.detected = false; - } - - if (as3935_sensor.http_count_start) as3935_sensor.http_count++; - - if (as3935_sensor.http_count == as3935_sensor.http_timer) { - as3935_sensor.http_count = 0; - as3935_sensor.http_count_start = 0; - as3935_sensor.http_intensity = 0; - as3935_sensor.http_distance = 0; - as3935_sensor.http_irq = 0; - } - - if (Settings.as3935_functions.nf_autotune) { - as3935_sensor.nftimer++; - if (as3935_sensor.nftimer > Settings.as3935_parameter.nf_autotune_time * 60) { - AS3935LowerNoiseFloor(); - as3935_sensor.nftimer = 0; - } - } - - if (Settings.as3935_functions.dist_autotune) { - if (as3935_sensor.autodist_activ) as3935_sensor.disttimer++; - if (as3935_sensor.disttimer >= Settings.as3935_parameter.dist_autotune_time * 60) { - AS3935SetDisturber(0); - as3935_sensor.disttimer = 0; - as3935_sensor.autodist_activ = false; - } - } -} - -bool AS3935Cmd(void) { - char command[CMDSZ]; - uint8_t name_len = strlen(D_NAME_AS3935); - if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_NAME_AS3935), name_len)) { - uint32_t command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + name_len, kAS3935_Commands); - switch (command_code) { - case CMND_AS3935_SET_NF: - if (XdrvMailbox.data_len) { - if (15 >= XdrvMailbox.payload) { - AS3935SetNoiseFloor(XdrvMailbox.payload); - } - } - Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, AS3935GetNoiseFloor()); - break; - case CMND_AS3935_SET_MINNF: - if (XdrvMailbox.data_len) { - if (15 >= XdrvMailbox.payload) { - Settings.as3935_parameter.nf_autotune_min = XdrvMailbox.payload; - } - } - Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, Settings.as3935_parameter.nf_autotune_min); - break; - case CMND_AS3935_SET_MINLIGHT: - if (XdrvMailbox.data_len) { - AS3935SetMinLights(AS3935TransMinLights(XdrvMailbox.payload)); - } - Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, AS3935TranslMinLightsInt(AS3935GetMinLights())); - break; - case CMND_AS3935_SET_DEF: - if (!XdrvMailbox.data_len) { - Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, AS3935SetDefault()); - } - break; - case CMND_AS3935_SET_GAIN: - if (XdrvMailbox.data_len > 6) { - uint8_t data_len = strlen(D_AS3935_OUTDOORS); - if (!strncasecmp_P(XdrvMailbox.data, PSTR(D_AS3935_OUTDOORS), data_len)) { - AS3935SetGain(OUTDOORS); - } else { - AS3935SetGain(INDOORS); - } - } - Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_GAIN[AS3935GetGainInt()]); - break; - case CMND_AS3935_SET_TUNE: - if (XdrvMailbox.data_len) { - if (15 >= XdrvMailbox.payload) { - AS3935SetTuneCaps(XdrvMailbox.payload); - } - } - Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, AS3935GetTuneCaps()); - break; - case CMND_AS3935_SET_REJ: - if (XdrvMailbox.data_len) { - if (15 >= XdrvMailbox.payload) { - AS3935SetSpikeRejection(XdrvMailbox.payload); - } - } - Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, AS3935GetSpikeRejection()); - break; - case CMND_AS3935_SET_WDTH: - if (XdrvMailbox.data_len) { - if (15 >= XdrvMailbox.payload) { - AS3935SetWdth(XdrvMailbox.payload); - } - } - Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, AS3935GetWdth()); - break; - case CMND_AS3935_DISTTIME: - if (XdrvMailbox.data_len) { - if (15 >= XdrvMailbox.payload) { - Settings.as3935_parameter.dist_autotune_time = XdrvMailbox.payload; - } - } - Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, Settings.as3935_parameter.dist_autotune_time); - break; - case CMND_AS3935_NFTIME: - if (XdrvMailbox.data_len) { - if (15 >= XdrvMailbox.payload) { - Settings.as3935_parameter.nf_autotune_time = XdrvMailbox.payload; - } - } - Response_P(S_JSON_AS3935_COMMAND_NVALUE, command, Settings.as3935_parameter.nf_autotune_time); - break; - case CMND_AS3935_SET_DISTURBER: - if (XdrvMailbox.data_len) { - if (2 > XdrvMailbox.payload) { - AS3935SetDisturber(XdrvMailbox.payload); - if (!XdrvMailbox.payload) Settings.as3935_functions.dist_autotune = 0; - } - } - Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[AS3935GetDisturber()]); - break; - case CMND_AS3935_NF_AUTOTUNE: - if (XdrvMailbox.data_len) { - if (2 > XdrvMailbox.payload) { - Settings.as3935_functions.nf_autotune = XdrvMailbox.payload; - } - } - Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[Settings.as3935_functions.nf_autotune]); - break; - case CMND_AS3935_DIST_AUTOTUNE: - if (XdrvMailbox.data_len) { - if (2 > XdrvMailbox.payload) { - Settings.as3935_functions.dist_autotune = XdrvMailbox.payload; - } - } - Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[Settings.as3935_functions.dist_autotune]); - break; - case CMND_AS3935_NF_ATUNE_BOTH: - if (XdrvMailbox.data_len) { - if (2 > XdrvMailbox.payload) { - Settings.as3935_functions.nf_autotune_both = XdrvMailbox.payload; - } - } - Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[Settings.as3935_functions.nf_autotune_both]); - break; - case CMND_AS3935_MQTT_LIGHT_EVT: - if (XdrvMailbox.data_len) { - if (2 > XdrvMailbox.payload) { - Settings.as3935_functions.mqtt_only_Light_Event = XdrvMailbox.payload; - } - } - Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[Settings.as3935_functions.mqtt_only_Light_Event]); - break; - case CMND_AS3935_SETTINGS: { - if (!XdrvMailbox.data_len) { - uint8_t gain = AS3935GetGainInt(); - uint16_t vrms; - uint8_t stage; - AS3935CalcVrmsLevel(vrms, stage); - uint8_t nf_floor = AS3935GetNoiseFloor(); - uint8_t min_nf = Settings.as3935_parameter.nf_autotune_min; - uint8_t tunecaps = AS3935GetTuneCaps(); - uint8_t minnumlight = AS3935TranslMinLightsInt(AS3935GetMinLights()); - uint8_t disturber = AS3935GetDisturber(); - uint8_t reinj = AS3935GetSpikeRejection(); - uint8_t wdth = AS3935GetWdth(); - uint8_t nfauto = Settings.as3935_functions.nf_autotune; - uint8_t distauto = Settings.as3935_functions.dist_autotune; - uint8_t nfautomax = Settings.as3935_functions.nf_autotune_both; - uint8_t jsonlight = Settings.as3935_functions.mqtt_only_Light_Event; - uint8_t nf_time = Settings.as3935_parameter.nf_autotune_time; - uint8_t dist_time =Settings.as3935_parameter.dist_autotune_time; - Response_P(S_JSON_AS3935_COMMAND_SETTINGS, S_JSON_AS3935_COMMAND_GAIN[gain], nf_floor, vrms, tunecaps, minnumlight, reinj, wdth, min_nf, nf_time, dist_time, S_JSON_AS3935_COMMAND_ONOFF[disturber], S_JSON_AS3935_COMMAND_ONOFF[nfauto], S_JSON_AS3935_COMMAND_ONOFF[distauto], S_JSON_AS3935_COMMAND_ONOFF[nfautomax], S_JSON_AS3935_COMMAND_ONOFF[jsonlight]); - } - } - break; - case CMND_AS3935_CALIBRATE: { - bool calreslt; - if (!XdrvMailbox.data_len) calreslt = AS3935AutoTune(); - Response_P(S_JSON_AS3935_COMMAND_NVALUE, S_JSON_AS3935_COMMAND_CAL[calreslt], AS3935GetTuneCaps()); - } - break; - default: - return false; - } - return true; - } else { - return false; - } -} - -void AH3935Show(bool json) -{ - if (json) { - ResponseAppend_P(JSON_SNS_AS3935_EVENTS, D_SENSOR_AS3935, as3935_sensor.mqtt_irq, as3935_sensor.distance, as3935_sensor.intensity ); - -#ifdef USE_WEBSERVER - } else { - uint8_t gain = AS3935GetGainInt(); - uint8_t disturber = AS3935GetDisturber(); - uint16_t vrms; - uint8_t stage; - AS3935CalcVrmsLevel(vrms, stage); - - WSContentSend_PD(HTTP_SNS_AS3935_TABLE_1[as3935_sensor.http_irq], D_NAME_AS3935, as3935_sensor.http_distance); - WSContentSend_PD(HTTP_SNS_AS3935_DISTANZ, as3935_sensor.http_distance); - WSContentSend_PD(HTTP_SNS_AS3935_ENERGY, as3935_sensor.http_intensity); - WSContentSend_PD(HTTP_SNS_AS3935_GAIN[gain], D_NAME_AS3935); - WSContentSend_PD(HTTP_SNS_AS3935_DISTURBER[disturber], D_NAME_AS3935); - WSContentSend_PD(HTTP_SNS_AS3935_VRMS, vrms, stage); -#endif - } -} - - - - - -bool Xsns67(uint8_t function) -{ - if (!I2cEnabled(XI2C_48)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - AS3935Detect(); - } - else if (as3935_active) { - switch (function) { - case FUNC_EVERY_SECOND: - AS3935EverySecond(); - break; - case FUNC_COMMAND: - result = AS3935Cmd(); - break; - case FUNC_JSON_APPEND: - AH3935Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - AH3935Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_91_prometheus.ino" -# 22 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_91_prometheus.ino" -#ifdef USE_PROMETHEUS - - - - -#define XSNS_91 91 - -void HandleMetrics(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR("Prometheus")); - - WSContentBegin(200, CT_PLAIN); - - - char parameter[FLOATSZ]; - - if (global_temperature != 9999) { - dtostrfd(global_temperature, Settings.flag2.temperature_resolution, parameter); - WSContentSend_P(PSTR("# TYPE global_temperature gauge\nglobal_temperature %s\n"), parameter); - } - if (global_humidity != 0) { - dtostrfd(global_humidity, Settings.flag2.humidity_resolution, parameter); - WSContentSend_P(PSTR("# TYPE global_humidity gauge\nglobal_humidity %s\n"), parameter); - } - if (global_pressure != 0) { - dtostrfd(global_pressure, Settings.flag2.pressure_resolution, parameter); - WSContentSend_P(PSTR("# TYPE global_pressure gauge\nglobal_pressure %s\n"), parameter); - } - -#ifdef USE_ENERGY_SENSOR - dtostrfd(Energy.voltage[0], Settings.flag2.voltage_resolution, parameter); - WSContentSend_P(PSTR("# TYPE voltage gauge\nvoltage %s\n"), parameter); - dtostrfd(Energy.current[0], Settings.flag2.current_resolution, parameter); - WSContentSend_P(PSTR("# TYPE current gauge\ncurrent %s\n"), parameter); - dtostrfd(Energy.active_power[0], Settings.flag2.wattage_resolution, parameter); - WSContentSend_P(PSTR("# TYPE active_power gauge\nactive_power %s\n"), parameter); - dtostrfd(Energy.daily, Settings.flag2.energy_resolution, parameter); - WSContentSend_P(PSTR("# TYPE energy_daily gauge\nenergy_daily %s\n"), parameter); - dtostrfd(Energy.total, Settings.flag2.energy_resolution, parameter); - WSContentSend_P(PSTR("# TYPE energy_total counter\nenergy_total %s\n"), parameter); -#endif -# 80 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_91_prometheus.ino" - WSContentEnd(); -} - - - - - -bool Xsns91(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_WEB_ADD_HANDLER: - Webserver->on("/metrics", HandleMetrics); - break; - } - return result; -} - -#endif -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_interface.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xsns_interface.ino" -#ifdef XFUNC_PTR_IN_ROM -bool (* const xsns_func_ptr[])(uint8_t) PROGMEM = { -#else -bool (* const xsns_func_ptr[])(uint8_t) = { -#endif - -#ifdef XSNS_01 - &Xsns01, -#endif - -#ifdef XSNS_02 - &Xsns02, -#endif - -#ifdef XSNS_03 - &Xsns03, -#endif - -#ifdef XSNS_04 - &Xsns04, -#endif - -#ifdef XSNS_05 - &Xsns05, -#endif - -#ifdef XSNS_06 - &Xsns06, -#endif - -#ifdef XSNS_07 - &Xsns07, -#endif - -#ifdef XSNS_08 - &Xsns08, -#endif - -#ifdef XSNS_09 - &Xsns09, -#endif - -#ifdef XSNS_10 - &Xsns10, -#endif - -#ifdef XSNS_11 - &Xsns11, -#endif - -#ifdef XSNS_12 - &Xsns12, -#endif - -#ifdef XSNS_13 - &Xsns13, -#endif - -#ifdef XSNS_14 - &Xsns14, -#endif - -#ifdef XSNS_15 - &Xsns15, -#endif - -#ifdef XSNS_16 - &Xsns16, -#endif - -#ifdef XSNS_17 - &Xsns17, -#endif - -#ifdef XSNS_18 - &Xsns18, -#endif - -#ifdef XSNS_19 - &Xsns19, -#endif - -#ifdef XSNS_20 - &Xsns20, -#endif - -#ifdef XSNS_21 - &Xsns21, -#endif - -#ifdef XSNS_22 - &Xsns22, -#endif - -#ifdef XSNS_23 - &Xsns23, -#endif - -#ifdef XSNS_24 - &Xsns24, -#endif - -#ifdef XSNS_25 - &Xsns25, -#endif - -#ifdef XSNS_26 - &Xsns26, -#endif - -#ifdef XSNS_27 - &Xsns27, -#endif - -#ifdef XSNS_28 - &Xsns28, -#endif - -#ifdef XSNS_29 - &Xsns29, -#endif - -#ifdef XSNS_30 - &Xsns30, -#endif - -#ifdef XSNS_31 - &Xsns31, -#endif - -#ifdef XSNS_32 - &Xsns32, -#endif - -#ifdef XSNS_33 - &Xsns33, -#endif - -#ifdef XSNS_34 - &Xsns34, -#endif - -#ifdef XSNS_35 - &Xsns35, -#endif - -#ifdef XSNS_36 - &Xsns36, -#endif - -#ifdef XSNS_37 - &Xsns37, -#endif - -#ifdef XSNS_38 - &Xsns38, -#endif - -#ifdef XSNS_39 - &Xsns39, -#endif - -#ifdef XSNS_40 - &Xsns40, -#endif - -#ifdef XSNS_41 - &Xsns41, -#endif - -#ifdef XSNS_42 - &Xsns42, -#endif - -#ifdef XSNS_43 - &Xsns43, -#endif - -#ifdef XSNS_44 - &Xsns44, -#endif - -#ifdef XSNS_45 - &Xsns45, -#endif - -#ifdef XSNS_46 - &Xsns46, -#endif - -#ifdef XSNS_47 - &Xsns47, -#endif - -#ifdef XSNS_48 - &Xsns48, -#endif - -#ifdef XSNS_49 - &Xsns49, -#endif - -#ifdef XSNS_50 - &Xsns50, -#endif - -#ifdef XSNS_51 - &Xsns51, -#endif - -#ifdef XSNS_52 - &Xsns52, -#endif - -#ifdef XSNS_53 - &Xsns53, -#endif - -#ifdef XSNS_54 - &Xsns54, -#endif - -#ifdef XSNS_55 - &Xsns55, -#endif - -#ifdef XSNS_56 - &Xsns56, -#endif - -#ifdef XSNS_57 - &Xsns57, -#endif - -#ifdef XSNS_58 - &Xsns58, -#endif - -#ifdef XSNS_59 - &Xsns59, -#endif - -#ifdef XSNS_60 - &Xsns60, -#endif - -#ifdef XSNS_61 - &Xsns61, -#endif - -#ifdef XSNS_62 - &Xsns62, -#endif - -#ifdef XSNS_63 - &Xsns63, -#endif - -#ifdef XSNS_64 - &Xsns64, -#endif - -#ifdef XSNS_65 - &Xsns65, -#endif - -#ifdef XSNS_66 - &Xsns66, -#endif - -#ifdef XSNS_67 - &Xsns67, -#endif - -#ifdef XSNS_68 - &Xsns68, -#endif - -#ifdef XSNS_69 - &Xsns69, -#endif - -#ifdef XSNS_70 - &Xsns70, -#endif - -#ifdef XSNS_71 - &Xsns71, -#endif - -#ifdef XSNS_72 - &Xsns72, -#endif - -#ifdef XSNS_73 - &Xsns73, -#endif - -#ifdef XSNS_74 - &Xsns74, -#endif - -#ifdef XSNS_75 - &Xsns75, -#endif - -#ifdef XSNS_76 - &Xsns76, -#endif - -#ifdef XSNS_77 - &Xsns77, -#endif - -#ifdef XSNS_78 - &Xsns78, -#endif - -#ifdef XSNS_79 - &Xsns79, -#endif - -#ifdef XSNS_80 - &Xsns80, -#endif - -#ifdef XSNS_81 - &Xsns81, -#endif - -#ifdef XSNS_82 - &Xsns82, -#endif - -#ifdef XSNS_83 - &Xsns83, -#endif - -#ifdef XSNS_84 - &Xsns84, -#endif - -#ifdef XSNS_85 - &Xsns85, -#endif - -#ifdef XSNS_86 - &Xsns86, -#endif - -#ifdef XSNS_87 - &Xsns87, -#endif - -#ifdef XSNS_88 - &Xsns88, -#endif - -#ifdef XSNS_89 - &Xsns89, -#endif - -#ifdef XSNS_90 - &Xsns90, -#endif - -#ifdef XSNS_91 - &Xsns91, -#endif - -#ifdef XSNS_92 - &Xsns92, -#endif - -#ifdef XSNS_93 - &Xsns93, -#endif - -#ifdef XSNS_94 - &Xsns94, -#endif - -#ifdef XSNS_95 - &Xsns95, -#endif - -#ifdef XSNS_96 - &Xsns96, -#endif - -#ifdef XSNS_97 - &Xsns97, -#endif - -#ifdef XSNS_98 - &Xsns98, -#endif - -#ifdef XSNS_99 - &Xsns99 -#endif -}; - -const uint8_t xsns_present = sizeof(xsns_func_ptr) / sizeof(xsns_func_ptr[0]); - - - - - -#ifdef XFUNC_PTR_IN_ROM -const uint8_t kXsnsList[] PROGMEM = { -#else -const uint8_t kXsnsList[] = { -#endif - -#ifdef XSNS_01 - XSNS_01, -#endif - -#ifdef XSNS_02 - XSNS_02, -#endif - -#ifdef XSNS_03 - XSNS_03, -#endif - -#ifdef XSNS_04 - XSNS_04, -#endif - -#ifdef XSNS_05 - XSNS_05, -#endif - -#ifdef XSNS_06 - XSNS_06, -#endif - -#ifdef XSNS_07 - XSNS_07, -#endif - -#ifdef XSNS_08 - XSNS_08, -#endif - -#ifdef XSNS_09 - XSNS_09, -#endif - -#ifdef XSNS_10 - XSNS_10, -#endif - -#ifdef XSNS_11 - XSNS_11, -#endif - -#ifdef XSNS_12 - XSNS_12, -#endif - -#ifdef XSNS_13 - XSNS_13, -#endif - -#ifdef XSNS_14 - XSNS_14, -#endif - -#ifdef XSNS_15 - XSNS_15, -#endif - -#ifdef XSNS_16 - XSNS_16, -#endif - -#ifdef XSNS_17 - XSNS_17, -#endif - -#ifdef XSNS_18 - XSNS_18, -#endif - -#ifdef XSNS_19 - XSNS_19, -#endif - -#ifdef XSNS_20 - XSNS_20, -#endif - -#ifdef XSNS_21 - XSNS_21, -#endif - -#ifdef XSNS_22 - XSNS_22, -#endif - -#ifdef XSNS_23 - XSNS_23, -#endif - -#ifdef XSNS_24 - XSNS_24, -#endif - -#ifdef XSNS_25 - XSNS_25, -#endif - -#ifdef XSNS_26 - XSNS_26, -#endif - -#ifdef XSNS_27 - XSNS_27, -#endif - -#ifdef XSNS_28 - XSNS_28, -#endif - -#ifdef XSNS_29 - XSNS_29, -#endif - -#ifdef XSNS_30 - XSNS_30, -#endif - -#ifdef XSNS_31 - XSNS_31, -#endif - -#ifdef XSNS_32 - XSNS_32, -#endif - -#ifdef XSNS_33 - XSNS_33, -#endif - -#ifdef XSNS_34 - XSNS_34, -#endif - -#ifdef XSNS_35 - XSNS_35, -#endif - -#ifdef XSNS_36 - XSNS_36, -#endif - -#ifdef XSNS_37 - XSNS_37, -#endif - -#ifdef XSNS_38 - XSNS_38, -#endif - -#ifdef XSNS_39 - XSNS_39, -#endif - -#ifdef XSNS_40 - XSNS_40, -#endif - -#ifdef XSNS_41 - XSNS_41, -#endif - -#ifdef XSNS_42 - XSNS_42, -#endif - -#ifdef XSNS_43 - XSNS_43, -#endif - -#ifdef XSNS_44 - XSNS_44, -#endif - -#ifdef XSNS_45 - XSNS_45, -#endif - -#ifdef XSNS_46 - XSNS_46, -#endif - -#ifdef XSNS_47 - XSNS_47, -#endif - -#ifdef XSNS_48 - XSNS_48, -#endif - -#ifdef XSNS_49 - XSNS_49, -#endif - -#ifdef XSNS_50 - XSNS_50, -#endif - -#ifdef XSNS_51 - XSNS_51, -#endif - -#ifdef XSNS_52 - XSNS_52, -#endif - -#ifdef XSNS_53 - XSNS_53, -#endif - -#ifdef XSNS_54 - XSNS_54, -#endif - -#ifdef XSNS_55 - XSNS_55, -#endif - -#ifdef XSNS_56 - XSNS_56, -#endif - -#ifdef XSNS_57 - XSNS_57, -#endif - -#ifdef XSNS_58 - XSNS_58, -#endif - -#ifdef XSNS_59 - XSNS_59, -#endif - -#ifdef XSNS_60 - XSNS_60, -#endif - -#ifdef XSNS_61 - XSNS_61, -#endif - -#ifdef XSNS_62 - XSNS_62, -#endif - -#ifdef XSNS_63 - XSNS_63, -#endif - -#ifdef XSNS_64 - XSNS_64, -#endif - -#ifdef XSNS_65 - XSNS_65, -#endif - -#ifdef XSNS_66 - XSNS_66, -#endif - -#ifdef XSNS_67 - XSNS_67, -#endif - -#ifdef XSNS_68 - XSNS_68, -#endif - -#ifdef XSNS_69 - XSNS_69, -#endif - -#ifdef XSNS_70 - XSNS_70, -#endif - -#ifdef XSNS_71 - XSNS_71, -#endif - -#ifdef XSNS_72 - XSNS_72, -#endif - -#ifdef XSNS_73 - XSNS_73, -#endif - -#ifdef XSNS_74 - XSNS_74, -#endif - -#ifdef XSNS_75 - XSNS_75, -#endif - -#ifdef XSNS_76 - XSNS_76, -#endif - -#ifdef XSNS_77 - XSNS_77, -#endif - -#ifdef XSNS_78 - XSNS_78, -#endif - -#ifdef XSNS_79 - XSNS_79, -#endif - -#ifdef XSNS_80 - XSNS_80, -#endif - -#ifdef XSNS_81 - XSNS_81, -#endif - -#ifdef XSNS_82 - XSNS_82, -#endif - -#ifdef XSNS_83 - XSNS_83, -#endif - -#ifdef XSNS_84 - XSNS_84, -#endif - -#ifdef XSNS_85 - XSNS_85, -#endif - -#ifdef XSNS_86 - XSNS_86, -#endif - -#ifdef XSNS_87 - XSNS_87, -#endif - -#ifdef XSNS_88 - XSNS_88, -#endif - -#ifdef XSNS_89 - XSNS_89, -#endif - -#ifdef XSNS_90 - XSNS_90, -#endif - -#ifdef XSNS_91 - XSNS_91, -#endif - -#ifdef XSNS_92 - XSNS_92, -#endif - -#ifdef XSNS_93 - XSNS_93, -#endif - -#ifdef XSNS_94 - XSNS_94, -#endif - -#ifdef XSNS_95 - XSNS_95, -#endif - -#ifdef XSNS_96 - XSNS_96, -#endif - -#ifdef XSNS_97 - XSNS_97, -#endif - -#ifdef XSNS_98 - XSNS_98, -#endif - -#ifdef XSNS_99 - XSNS_99 -#endif -}; - - - -bool XsnsEnabled(uint32_t sns_index) -{ - if (sns_index < sizeof(kXsnsList)) { -#ifdef XFUNC_PTR_IN_ROM - uint32_t index = pgm_read_byte(kXsnsList + sns_index); -#else - uint32_t index = kXsnsList[sns_index]; -#endif - return bitRead(Settings.sensors[index / 32], index % 32); - } - return true; -} - -void XsnsSensorState(void) -{ - ResponseAppend_P(PSTR("\"")); - for (uint32_t i = 0; i < sizeof(kXsnsList); i++) { -#ifdef XFUNC_PTR_IN_ROM - uint32_t sensorid = pgm_read_byte(kXsnsList + i); -#else - uint32_t sensorid = kXsnsList[i]; -#endif - bool disabled = false; - if (sensorid < MAX_XSNS_DRIVERS) { - disabled = !bitRead(Settings.sensors[sensorid / 32], sensorid % 32); - } - ResponseAppend_P(PSTR("%s%s%d"), (i) ? "," : "", (disabled) ? "!" : "", sensorid); - } - ResponseAppend_P(PSTR("\"")); -} - - - - - -bool XsnsNextCall(uint8_t Function, uint8_t &xsns_index) -{ - xsns_index++; - if (xsns_index == xsns_present) { xsns_index = 0; } - -#ifndef USE_DEBUG_DRIVER - if (FUNC_WEB_SENSOR == Function) { -#endif - uint32_t max_disabled = xsns_present; - while (!XsnsEnabled(xsns_index) && max_disabled--) { - xsns_index++; - if (xsns_index == xsns_present) { xsns_index = 0; } - } -#ifndef USE_DEBUG_DRIVER - } -#endif - - return xsns_func_ptr[xsns_index](Function); -} - -bool XsnsCall(uint8_t Function) -{ - bool result = false; - - DEBUG_TRACE_LOG(PSTR("SNS: %d"), Function); - -#ifdef PROFILE_XSNS_EVERY_SECOND - uint32_t profile_start_millis = millis(); -#endif - - for (uint32_t x = 0; x < xsns_present; x++) { -#ifdef USE_DEBUG_DRIVER - if (XsnsEnabled(x)) { -#endif - - if ((FUNC_WEB_SENSOR == Function) && !XsnsEnabled(x)) { continue; } - -#ifdef PROFILE_XSNS_SENSOR_EVERY_SECOND - uint32_t profile_start_millis = millis(); -#endif - result = xsns_func_ptr[x](Function); - -#ifdef PROFILE_XSNS_SENSOR_EVERY_SECOND - uint32_t profile_millis = millis() - profile_start_millis; - if (profile_millis) { - if (FUNC_EVERY_SECOND == Function) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PRF: At %08u XsnsCall %d to Sensor %d took %u mS"), uptime, Function, x, profile_millis); - } - } -#endif - - if (result && ((FUNC_COMMAND == Function) || - (FUNC_PIN_STATE == Function) || - (FUNC_COMMAND_SENSOR == Function) - )) { - break; - } -#ifdef USE_DEBUG_DRIVER - } -#endif - } - -#ifdef PROFILE_XSNS_EVERY_SECOND - uint32_t profile_millis = millis() - profile_start_millis; - if (profile_millis) { - if (FUNC_EVERY_SECOND == Function) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PRF: At %08u XsnsCall %d took %u mS"), uptime, Function, profile_millis); - } - } -#endif - - return result; -} -# 1 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xx2c_interface.ino" -# 20 "/Users/javierarigitalopez/WorkSpace/Github/Tasmota/tasmota/xx2c_interface.ino" -#ifdef USE_I2C - -#ifdef XFUNC_PTR_IN_ROM -const uint8_t kI2cList[] PROGMEM = { -#else -const uint8_t kI2cList[] = { -#endif - -#ifdef XI2C_01 - XI2C_01, -#endif - -#ifdef XI2C_02 - XI2C_02, -#endif - -#ifdef XI2C_03 - XI2C_03, -#endif - -#ifdef XI2C_04 - XI2C_04, -#endif - -#ifdef XI2C_05 - XI2C_05, -#endif - -#ifdef XI2C_06 - XI2C_06, -#endif - -#ifdef XI2C_07 - XI2C_07, -#endif - -#ifdef XI2C_08 - XI2C_08, -#endif - -#ifdef XI2C_09 - XI2C_09, -#endif - -#ifdef XI2C_10 - XI2C_10, -#endif - -#ifdef XI2C_11 - XI2C_11, -#endif - -#ifdef XI2C_12 - XI2C_12, -#endif - -#ifdef XI2C_13 - XI2C_13, -#endif - -#ifdef XI2C_14 - XI2C_14, -#endif - -#ifdef XI2C_15 - XI2C_15, -#endif - -#ifdef XI2C_16 - XI2C_16, -#endif - -#ifdef XI2C_17 - XI2C_17, -#endif - -#ifdef XI2C_18 - XI2C_18, -#endif - -#ifdef XI2C_19 - XI2C_19, -#endif - -#ifdef XI2C_20 - XI2C_20, -#endif - -#ifdef XI2C_21 - XI2C_21, -#endif - -#ifdef XI2C_22 - XI2C_22, -#endif - -#ifdef XI2C_23 - XI2C_23, -#endif - -#ifdef XI2C_24 - XI2C_24, -#endif - -#ifdef XI2C_25 - XI2C_25, -#endif - -#ifdef XI2C_26 - XI2C_26, -#endif - -#ifdef XI2C_27 - XI2C_27, -#endif - -#ifdef XI2C_28 - XI2C_28, -#endif - -#ifdef XI2C_29 - XI2C_29, -#endif - -#ifdef XI2C_30 - XI2C_30, -#endif - -#ifdef XI2C_31 - XI2C_31, -#endif - -#ifdef XI2C_32 - XI2C_32, -#endif - -#ifdef XI2C_33 - XI2C_33, -#endif - -#ifdef XI2C_34 - XI2C_34, -#endif - -#ifdef XI2C_35 - XI2C_35, -#endif - -#ifdef XI2C_36 - XI2C_36, -#endif - -#ifdef XI2C_37 - XI2C_37, -#endif - -#ifdef XI2C_38 - XI2C_38, -#endif - -#ifdef XI2C_39 - XI2C_39, -#endif - -#ifdef XI2C_40 - XI2C_40, -#endif - -#ifdef XI2C_41 - XI2C_41, -#endif - -#ifdef XI2C_42 - XI2C_42, -#endif - -#ifdef XI2C_43 - XI2C_43, -#endif - -#ifdef XI2C_44 - XI2C_44, -#endif - -#ifdef XI2C_45 - XI2C_45, -#endif - -#ifdef XI2C_46 - XI2C_46, -#endif - -#ifdef XI2C_47 - XI2C_47, -#endif - -#ifdef XI2C_48 - XI2C_48, -#endif - -#ifdef XI2C_49 - XI2C_49, -#endif - -#ifdef XI2C_50 - XI2C_50, -#endif - -#ifdef XI2C_51 - XI2C_51, -#endif - -#ifdef XI2C_52 - XI2C_52, -#endif - -#ifdef XI2C_53 - XI2C_53, -#endif - -#ifdef XI2C_54 - XI2C_54, -#endif - -#ifdef XI2C_55 - XI2C_55, -#endif - -#ifdef XI2C_56 - XI2C_56, -#endif - -#ifdef XI2C_57 - XI2C_57, -#endif - -#ifdef XI2C_58 - XI2C_58, -#endif - -#ifdef XI2C_59 - XI2C_59, -#endif - -#ifdef XI2C_60 - XI2C_60, -#endif - -#ifdef XI2C_61 - XI2C_61, -#endif - -#ifdef XI2C_62 - XI2C_62, -#endif - -#ifdef XI2C_63 - XI2C_63, -#endif - -#ifdef XI2C_64 - XI2C_64, -#endif - -#ifdef XI2C_65 - XI2C_65, -#endif - -#ifdef XI2C_66 - XI2C_66, -#endif - -#ifdef XI2C_67 - XI2C_67, -#endif - -#ifdef XI2C_68 - XI2C_68, -#endif - -#ifdef XI2C_69 - XI2C_69, -#endif - -#ifdef XI2C_70 - XI2C_70, -#endif - -#ifdef XI2C_71 - XI2C_71, -#endif - -#ifdef XI2C_72 - XI2C_72, -#endif - -#ifdef XI2C_73 - XI2C_73, -#endif - -#ifdef XI2C_74 - XI2C_74, -#endif - -#ifdef XI2C_75 - XI2C_75, -#endif - -#ifdef XI2C_76 - XI2C_76, -#endif - -#ifdef XI2C_77 - XI2C_77, -#endif - -#ifdef XI2C_78 - XI2C_78, -#endif - -#ifdef XI2C_79 - XI2C_79, -#endif - -#ifdef XI2C_80 - XI2C_80, -#endif - -#ifdef XI2C_81 - XI2C_81, -#endif - -#ifdef XI2C_82 - XI2C_82, -#endif - -#ifdef XI2C_83 - XI2C_83, -#endif - -#ifdef XI2C_84 - XI2C_84, -#endif - -#ifdef XI2C_85 - XI2C_85, -#endif - -#ifdef XI2C_86 - XI2C_86, -#endif - -#ifdef XI2C_87 - XI2C_87, -#endif - -#ifdef XI2C_88 - XI2C_88, -#endif - -#ifdef XI2C_89 - XI2C_89, -#endif - -#ifdef XI2C_90 - XI2C_90, -#endif - -#ifdef XI2C_91 - XI2C_91, -#endif - -#ifdef XI2C_92 - XI2C_92, -#endif - -#ifdef XI2C_93 - XI2C_93, -#endif - -#ifdef XI2C_94 - XI2C_94, -#endif - -#ifdef XI2C_95 - XI2C_95, -#endif - -#ifdef XI2C_96 - XI2C_96 -#endif -}; - - - -bool I2cEnabled(uint32_t i2c_index) -{ - return (i2c_flg && bitRead(Settings.i2c_drivers[i2c_index / 32], i2c_index % 32)); -} - -void I2cDriverState(void) -{ - ResponseAppend_P(PSTR("\"")); - for (uint32_t i = 0; i < sizeof(kI2cList); i++) { -#ifdef XFUNC_PTR_IN_ROM - uint32_t i2c_driver_id = pgm_read_byte(kI2cList + i); -#else - uint32_t i2c_driver_id = kI2cList[i]; -#endif - bool disabled = false; - if (i2c_driver_id < MAX_I2C_DRIVERS) { - disabled = !bitRead(Settings.i2c_drivers[i2c_driver_id / 32], i2c_driver_id % 32); - } - ResponseAppend_P(PSTR("%s%s%d"), (i) ? "," : "", (disabled) ? "!" : "", i2c_driver_id); - } - ResponseAppend_P(PSTR("\"")); -} - -#endif \ No newline at end of file diff --git a/tasmota/xdrv_39_thermostat.ino b/tasmota/xdrv_39_thermostat.ino index 85a1b245b..88f460e8b 100644 --- a/tasmota/xdrv_39_thermostat.ino +++ b/tasmota/xdrv_39_thermostat.ino @@ -22,7 +22,7 @@ #define XDRV_39 39 // Enable/disable debugging -#define DEBUG_THERMOSTAT +//#define DEBUG_THERMOSTAT #ifdef DEBUG_THERMOSTAT #define DOMOTICZ_IDX1 791 From c71b4d34ea4d505a91e98d3420e565b4b69dc218 Mon Sep 17 00:00:00 2001 From: Javier Arigita Date: Sun, 26 Apr 2020 18:04:12 +0200 Subject: [PATCH 366/547] Correct merge --- tasmota/my_user_config.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 1d5ff3944..e37626d7d 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -410,7 +410,6 @@ #define USE_SONOFF_SC // Add support for Sonoff Sc (+1k1 code) #define USE_TUYA_MCU // Add support for Tuya Serial MCU #define TUYA_DIMMER_ID 0 // Default dimmer Id -// #define USE_TUYA_TIME // Add support for Set Time in Tuya MCU #define USE_ARMTRONIX_DIMMERS // Add support for Armtronix Dimmers (+1k4 code) #define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer (+2k code) #define USE_SONOFF_IFAN // Add support for Sonoff iFan02 and iFan03 (+2k code) @@ -421,8 +420,8 @@ #define USE_EXS_DIMMER // Add support for ES-Store WiFi Dimmer (+1k5 code) // #define EXS_MCU_CMNDS // Add command to send MCU commands (+0k8 code) //#define USE_HOTPLUG // Add support for sensor HotPlug -#define USE_DEVICE_GROUPS // Add support for device groups (+5k6 code) - #define USE_DEVICE_GROUPS_SEND // Add support for the DevGroupSend command (+0k5 code) +#define USE_DEVICE_GROUPS // Add support for device groups (+5k code) + #define USE_DEVICE_GROUPS_SEND // Add support for the DevGroupSend command (+0k6 code) #define USE_PWM_DIMMER // Add support for MJ-SD01/acenx/NTONPOWER PWM dimmers (+2k5 code) #define USE_PWM_DIMMER_REMOTE // Add support for remote switches to PWM Dimmer, also adds device groups support (+1k code plus device groups size) //#define USE_KEELOQ // Add support for Jarolift rollers by Keeloq algorithm (+4k5 code) @@ -567,8 +566,7 @@ //#define USE_IBEACON // Add support for bluetooth LE passive scan of ibeacon devices (uses HM17 module) //#define USE_GPS // Add support for GPS and NTP Server for becoming Stratus 1 Time Source (+3k1 code, +132 bytes RAM) // #define USE_FLOG // Add support for GPS logging in OTA's Flash (Experimental) (+2k9 code, +8 bytes RAM) -//#define USE_HM10 // (ESP8266 only) Add support for HM-10 as a BLE-bridge (+9k3 code) -//#define USE_MI_ESP32 // (ESP32 only) Add support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash) +//#define USE_HM10 // Add support for HM-10 as a BLE-bridge for the LYWSD03 (+5k1 code) //#define USE_HRXL // Add support for MaxBotix HRXL-MaxSonar ultrasonic range finders (+0k7) // -- Power monitoring sensors -------------------- From b9aa6fe19df437196d343ded087c4701e427de08 Mon Sep 17 00:00:00 2001 From: Matteo Albinola Date: Sun, 26 Apr 2020 19:21:10 +0200 Subject: [PATCH 367/547] Add configuration persistence fixing variable alignment problem --- tasmota/settings.h | 11 +++++----- tasmota/xsns_68_windmeter.ino | 41 +++++++++++++++-------------------- 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/tasmota/settings.h b/tasmota/settings.h index 481faa2ba..f6a270092 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -525,13 +525,12 @@ struct { uint8_t zb_free_byte; // F33 uint16_t pms_wake_interval; // F34 uint8_t config_version; // F36 -// uint16_t windmeter_radius; // F37 -// uint8_t windmeter_pulses_x_rot; // F39 -// uint16_t windmeter_pulse_debounce; // F3A -// int16_t windmeter_speed_factor; // F3C + uint8_t windmeter_pulses_x_rot; // F37 + uint16_t windmeter_radius; // F38 + uint16_t windmeter_pulse_debounce; // F3A + int16_t windmeter_speed_factor; // F3C -// uint8_t free_f37[122]; // F3E - Decrement if adding new Setting variables just above and below - uint8_t free_f37[129]; // F37 - Decrement if adding new Setting variables just above and below + uint8_t free_f3e[122]; // F3E - Decrement if adding new Setting variables just above and below // Only 32 bit boundary variables below uint16_t pulse_counter_debounce_low; // FB8 diff --git a/tasmota/xsns_68_windmeter.ino b/tasmota/xsns_68_windmeter.ino index f926faf5a..db938a47c 100644 --- a/tasmota/xsns_68_windmeter.ino +++ b/tasmota/xsns_68_windmeter.ino @@ -70,13 +70,6 @@ struct WINDMETER { #endif // USE_WINDMETER_NOSTATISTICS } WindMeter; -struct WINDMETER_SETTINGS { - uint16_t windmeter_radius; - uint8_t windmeter_pulses_x_rot; - uint16_t windmeter_pulse_debounce; - int16_t windmeter_speed_factor; -} WindMeterSettings; - #ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // Fix core 2.5.x ISR not in IRAM Exception void WindMeterUpdateSpeed(void) ICACHE_RAM_ATTR; #endif // ARDUINO_ESP8266_RELEASE_2_3_0 @@ -85,7 +78,7 @@ void WindMeterUpdateSpeed(void) { uint32_t time = micros(); uint32_t time_diff = time - WindMeter.counter_time; - if (time_diff > WindMeterSettings.windmeter_pulse_debounce * 1000) { + if (time_diff > Settings.windmeter_pulse_debounce * 1000) { WindMeter.counter_time = time; WindMeter.counter++; AddLog_P2(LOG_LEVEL_DEBUG, PSTR("WMET: Counter %d"), WindMeter.counter); @@ -99,17 +92,17 @@ void WindMeterInit(void) if (!Settings.flag2.speed_conversion) { Settings.flag2.speed_conversion = 2; // 0 = none, 1 = m/s, 2 = km/h, 3 = kn, 4 = mph, 5 = ft/s, 6 = yd/s } - if (!WindMeterSettings.windmeter_radius) { - WindMeterSettings.windmeter_radius = WINDMETER_DEF_RADIUS; + if (!Settings.windmeter_radius) { + Settings.windmeter_radius = WINDMETER_DEF_RADIUS; } - if (!WindMeterSettings.windmeter_pulses_x_rot) { - WindMeterSettings.windmeter_pulses_x_rot = WINDMETER_DEF_PULSES_X_ROT; + if (!Settings.windmeter_pulses_x_rot) { + Settings.windmeter_pulses_x_rot = WINDMETER_DEF_PULSES_X_ROT; } - if (!WindMeterSettings.windmeter_pulse_debounce) { - WindMeterSettings.windmeter_pulse_debounce = WINDMETER_DEF_PULSE_DEBOUNCE; + if (!Settings.windmeter_pulse_debounce) { + Settings.windmeter_pulse_debounce = WINDMETER_DEF_PULSE_DEBOUNCE; } - if (!WindMeterSettings.windmeter_speed_factor) { - WindMeterSettings.windmeter_speed_factor = (int16_t)(WINDMETER_DEF_COMP_FACTOR * 1000); + if (!Settings.windmeter_speed_factor) { + Settings.windmeter_speed_factor = (int16_t)(WINDMETER_DEF_COMP_FACTOR * 1000); } #ifndef USE_WINDMETER_NOSTATISTICS @@ -128,8 +121,8 @@ void WindMeterEverySecond(void) //AddLog_P2(LOG_LEVEL_INFO, PSTR("delta_time: %d"), delta_time); // speed = ( (pulses / pulses_per_rotation) * (2 * pi * radius) ) / delta_time - WindMeter.speed = ((WindMeter.counter / WindMeterSettings.windmeter_pulses_x_rot) * (windmeter_2pi * ((float)WindMeterSettings.windmeter_radius / 1000))) * ((float)WindMeterSettings.windmeter_speed_factor / 1000); - //WindMeter.speed = (((WindMeter.counter / WindMeterSettings.windmeter_pulses_x_rot) * (windmeter_2pi * ((float)WindMeterSettings.windmeter_radius / 1000))) / ((float)delta_time / 1000000)) * ((float)WindMeterSettings.windmeter_speed_factor / 1000); + WindMeter.speed = ((WindMeter.counter / Settings.windmeter_pulses_x_rot) * (windmeter_2pi * ((float)Settings.windmeter_radius / 1000))) * ((float)Settings.windmeter_speed_factor / 1000); + //WindMeter.speed = (((WindMeter.counter / Settings.windmeter_pulses_x_rot) * (windmeter_2pi * ((float)Settings.windmeter_radius / 1000))) / ((float)delta_time / 1000000)) * ((float)Settings.windmeter_speed_factor / 1000); WindMeter.counter = 0; //WindMeter.speed_time = time; @@ -271,31 +264,31 @@ bool Xsns68Cmnd(void) switch (XdrvMailbox.payload) { case 1: if (strstr(XdrvMailbox.data, ",") != nullptr) { - WindMeterSettings.windmeter_radius = (uint16_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + Settings.windmeter_radius = (uint16_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); } break; case 2: if (strstr(XdrvMailbox.data, ",") != nullptr) { - WindMeterSettings.windmeter_pulses_x_rot = (uint8_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + Settings.windmeter_pulses_x_rot = (uint8_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); } break; case 3: if (strstr(XdrvMailbox.data, ",") != nullptr) { - WindMeterSettings.windmeter_pulse_debounce = (uint16_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + Settings.windmeter_pulse_debounce = (uint16_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); } break; case 4: if (strstr(XdrvMailbox.data, ",") != nullptr) { - WindMeterSettings.windmeter_speed_factor = (int16_t)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)) * 1000); + Settings.windmeter_speed_factor = (int16_t)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)) * 1000); } break; } if (show_parms) { char speed_factor_string[FLOATSZ]; - dtostrfd((float)WindMeterSettings.windmeter_speed_factor / 1000, 3, speed_factor_string); + dtostrfd((float)Settings.windmeter_speed_factor / 1000, 3, speed_factor_string); Response_P(PSTR("{\"" D_WINDMETER_NAME "\":{\"Radius\":%d,\"PulsesPerRot\":%d,\"PulseDebounce\":%d,\"SpeedFactor\":%s}}"), - WindMeterSettings.windmeter_radius, WindMeterSettings.windmeter_pulses_x_rot, WindMeterSettings.windmeter_pulse_debounce, speed_factor_string); + Settings.windmeter_radius, Settings.windmeter_pulses_x_rot, Settings.windmeter_pulse_debounce, speed_factor_string); } return serviced; } From 0464bd364d9fbe0ca599e73459f629178d053a49 Mon Sep 17 00:00:00 2001 From: Mickael Gaillard Date: Sun, 26 Apr 2020 19:35:11 +0200 Subject: [PATCH 368/547] use builtin function for readBuffer Signed-off-by: Mickael Gaillard --- tasmota/xsns_27_apds9960.ino | 52 +++--------------------------------- 1 file changed, 3 insertions(+), 49 deletions(-) diff --git a/tasmota/xsns_27_apds9960.ino b/tasmota/xsns_27_apds9960.ino index e55deb276..1efef4680 100644 --- a/tasmota/xsns_27_apds9960.ino +++ b/tasmota/xsns_27_apds9960.ino @@ -333,53 +333,6 @@ uint8_t gesture_mode = 1; * Helper functions \******************************************************************************/ -/** - * @brief Writes a single byte to the I2C device (no register) - * - * @param[in] val the 1-byte value to write to the I2C device - * @return True if successful write operation. False otherwise. - */ -bool wireWriteByte(uint8_t val) { - Wire.beginTransmission(APDS9960_I2C_ADDR); - Wire.write(val); - if ( Wire.endTransmission() != 0 ) { - return false; - } - - return true; -} - -/** - * @brief Reads a block (array) of bytes from the I2C device and register - * - * @param[in] reg the register to read from - * @param[out] val pointer to the beginning of the data - * @param[in] len number of bytes to read - * @return Number of bytes read. -1 on read error. - */ -int8_t wireReadDataBlock(uint8_t reg, - uint8_t *val, - uint16_t len) { - unsigned char i = 0; - - /* Indicate which register we want to read from */ - if (!wireWriteByte(reg)) { - return -1; - } - - /* Read block data */ - Wire.requestFrom(APDS9960_I2C_ADDR, len); - while (Wire.available()) { - if (i >= len) { - return -1; - } - val[i] = Wire.read(); - i++; - } - - return i; -} - /** * Taken from the Adafruit-library * @brief Converts the raw R/G/B values to color temperature in degrees @@ -1494,8 +1447,9 @@ int16_t readGesture(void) { /* If there's stuff in the FIFO, read it into our data block */ if (fifo_level > 0) { - bytes_read = wireReadDataBlock(APDS9960_GFIFO_U, (uint8_t*)fifo_data, (fifo_level * 4)); - if (bytes_read == -1) { + bytes_read = (fifo_level * 4); + + if (I2cReadBuffer(APDS9960_I2C_ADDR, APDS9960_GFIFO_U, (uint8_t*)fifo_data, bytes_read)) { return APDS9960_ERROR; } From f8fabfe03502989d52a0b065a05e78be55edf82a Mon Sep 17 00:00:00 2001 From: Mickael Gaillard Date: Sun, 26 Apr 2020 19:44:25 +0200 Subject: [PATCH 369/547] Refactor code Signed-off-by: Mickael Gaillard --- tasmota/xsns_27_apds9960.ino | 140 ++++++++++++++++++++--------------- 1 file changed, 82 insertions(+), 58 deletions(-) diff --git a/tasmota/xsns_27_apds9960.ino b/tasmota/xsns_27_apds9960.ino index 1efef4680..141373801 100644 --- a/tasmota/xsns_27_apds9960.ino +++ b/tasmota/xsns_27_apds9960.ino @@ -33,12 +33,9 @@ * Source: Shawn Hymel (SparkFun Electronics) * Adaption for TASMOTA: Christian Baars * - * I2C Address: 0x39 + * I2C Address: 0x39 - standard address \*********************************************************************************************/ -#define XSNS_27 27 -#define XI2C_21 21 // See I2CDEVICES.md - // #if defined(USE_SHT) || defined(USE_VEML6070) || defined(USE_TSL2561) // #warning **** Turned off conflicting drivers SHT and VEML6070 **** // #ifdef USE_SHT @@ -51,6 +48,9 @@ // #undef USE_TSL2561 // possible address conflict on the I2C-bus // #endif // #endif +#define XSNS_27 27 +#define XI2C_21 21 // See I2CDEVICES.md + #define APDS9960_I2C_ADDR 0x39 @@ -61,7 +61,12 @@ #define APDS9930_CHIPID_1 0x12 // we will check, if someone got an incorrect sensor #define APDS9930_CHIPID_2 0x39 // there are case reports about "accidentially bought" 9930's -#define APDS9960_GESTURE // Enable Gesture feature(2k on flash) + +// TODO() : Move to my_user_config.h file +#define USE_APDS9960_GESTURE // Enable Gesture feature (+2k code) +#define USE_APDS9960_PROXIMITY // Enable Proximity feature (Not use) +#define USE_APDS9960_COLOR // Enable Color feature (Not use) + /* Gesture parameters */ #define GESTURE_THRESHOLD_OUT 10 @@ -71,36 +76,44 @@ #define APDS9960_LONG_RECOVERY 50 // long pause after sensor overload in loops #define APDS9960_MAX_GESTURE_CYCLES 50 // how many FIFO-reads are allowed to prevent crash -const char APDS9960_TAG[] = "APDS9960"; - -#ifdef USE_WEBSERVER -#ifdef APDS9960_GESTURE +// TODO() : Move to Translate file #define D_GESTURE "Gesture" - -const char HTTP_SNS_GESTURE[] PROGMEM = "{s}%s " D_GESTURE "{m}%s{e}"; - -#endif // APDS9960_GESTURE - #define D_COLOR_RED "Red" #define D_COLOR_GREEN "Green" #define D_COLOR_BLUE "Blue" #define D_CCT "CCT" #define D_PROXIMITY "Proximity" +#define D_UNIT_KELVIN "°K" + +/******************************************************************************\ + * constants +\******************************************************************************/ + +const char APDS9960_TAG[] PROGMEM = "APDS9960"; // Only one actualy + +#ifdef USE_WEBSERVER + +#ifdef USE_APDS9960_GESTURE + +const char HTTP_SNS_GESTURE[] PROGMEM = "{s}%s " D_GESTURE "{m}%s{e}"; + +#endif // USE_APDS9960_GESTURE + const char HTTP_SNS_COLOR_RED[] PROGMEM = "{s}%s " D_COLOR_RED "{m}%u{e}"; const char HTTP_SNS_COLOR_GREEN[] PROGMEM = "{s}%s " D_COLOR_GREEN "{m}%u{e}"; const char HTTP_SNS_COLOR_BLUE[] PROGMEM = "{s}%s " D_COLOR_BLUE "{m}%u{e}"; -const char HTTP_SNS_CCT[] PROGMEM = "{s}%s " D_CCT "{m}%u " "K" "{e}"; +const char HTTP_SNS_CCT[] PROGMEM = "{s}%s " D_CCT "{m}%u " D_UNIT_KELVIN "{e}"; const char HTTP_SNS_PROXIMITY[] PROGMEM = "{s}%s " D_PROXIMITY "{m}%u{e}"; #endif // USE_WEBSERVER -/*********************************************************************************************\ +/******************************************************************************\ * APDS9960 * * Programmer : APDS9960 Datasheet and Sparkfun -\*********************************************************************************************/ +\******************************************************************************/ /* Misc parameters */ #define FIFO_PAUSE_TIME 30 // Wait period (ms) between FIFO reads @@ -164,6 +177,7 @@ const char HTTP_SNS_PROXIMITY[] PROGMEM = "{s}%s " D_PROXIMITY "{m}%u{e}"; #define APDS9960_AIEN 0b00010000 #define APDS9960_PIEN 0b00100000 #define APDS9960_GEN 0b01000000 + #define APDS9960_GVALID 0b00000001 /* On/Off definitions */ @@ -220,6 +234,8 @@ const char HTTP_SNS_PROXIMITY[] PROGMEM = "{s}%s " D_PROXIMITY "{m}%u{e}"; #define GWTIME_30_8MS 6 #define GWTIME_39_2MS 7 + + /* Default values */ #define DEFAULT_ATIME 0xdb // 103ms = 0xdb = 219 #define DEFAULT_WTIME 246 // 27ms @@ -251,7 +267,8 @@ const char HTTP_SNS_PROXIMITY[] PROGMEM = "{s}%s " D_PROXIMITY "{m}%u{e}"; #define APDS9960_ERROR 0xFF -#ifdef APDS9960_GESTURE +#ifdef USE_APDS9960_GESTURE + /* Direction definitions */ const char GESTURE_UP[] PROGMEM = "Up"; const char GESTURE_DOWN[] PROGMEM = "Down"; @@ -302,7 +319,7 @@ typedef struct gesture_type { int16_t motion_ = DIR_NONE; } gesture_t; -#endif // APDS9960_GESTURE +#endif // USE_APDS9960_GESTURE typedef struct color_data_type { uint16_t a; // measured ambient @@ -310,23 +327,30 @@ typedef struct color_data_type { uint16_t g; // Green uint16_t b; // Blue uint8_t p; // proximity + uint16_t cct; // calculated color temperature uint16_t lux; // calculated illuminance - atm only from rgb } color_data_t; -/*Members*/ -#ifdef APDS9960_GESTURE +/******************************************************************************\ + * Globals +\******************************************************************************/ + +#ifdef USE_APDS9960_GESTURE + gesture_data_t gesture_data; gesture_t gesture; -#endif // APDS9960_GESTURE + +#endif // USE_APDS9960_GESTURE +char currentGesture[6]; + color_data_t color_data; volatile uint8_t recovery_loop_counter = 0; // count number of stateloops to switch the sensor off, if needed bool APDS9960_overload = false; -char currentGesture[6]; uint8_t APDS9960_aTime = DEFAULT_ATIME; -uint8_t APDS9960type = 0; +uint8_t APDS9960_type = 0; uint8_t gesture_mode = 1; /******************************************************************************\ @@ -365,9 +389,9 @@ void calculateColorTemperature(void) { return; } -/******************************************************************************* +/******************************************************************************\ * Getters and setters for register values - ******************************************************************************/ +\******************************************************************************/ /** * @brief Returns the lower threshold for proximity detection @@ -703,7 +727,7 @@ void setProxPhotoMask(uint8_t mask) { I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, val); } -#ifdef APDS9960_GESTURE +#ifdef USE_APDS9960_GESTURE /** * @brief Gets the entry proximity threshold for gesture sensing @@ -908,7 +932,7 @@ void setGestureWaitTime(uint8_t time) { I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val); } -#endif // APDS9960_GESTURE +#endif // USE_APDS9960_GESTURE /** * @brief Gets the low threshold for ambient light interrupts @@ -1224,7 +1248,7 @@ bool APDS9960_init(void) { I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, DEFAULT_CONFIG3); /* Set default values for gesture sense registers */ -#ifdef APDS9960_GESTURE +#ifdef USE_APDS9960_GESTURE setGestureEnterThresh(DEFAULT_GPENTH); setGestureExitThresh(DEFAULT_GEXTH); @@ -1243,8 +1267,8 @@ bool APDS9960_init(void) { I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF3, DEFAULT_GCONF3); setGestureIntEnable(DEFAULT_GIEN); - -#endif // APDS9960_GESTURE + +#endif // USE_APDS9960_GESTURE disablePower(); // go to sleep @@ -1347,7 +1371,7 @@ void disableProximitySensor(void) { setMode(PROXIMITY, OFF); } -#ifdef APDS9960_GESTURE +#ifdef USE_APDS9960_GESTURE /** * @brief Starts the gesture recognition engine on the APDS-9960 * @@ -1496,7 +1520,7 @@ int16_t readGesture(void) { } } -#endif // APDS9960_GESTURE +#endif // USE_APDS9960_GESTURE /** * Turn the APDS-9960 on @@ -1534,7 +1558,7 @@ void readAllColorAndProximityData(void) { * High-level gesture controls \******************************************************************************/ -#ifdef APDS9960_GESTURE +#ifdef USE_APDS9960_GESTURE /** * @brief Resets all the parameters in the gesture data member @@ -1730,7 +1754,7 @@ void handleGesture(void) { } } -#endif // APDS9960_GESTURE +#endif // USE_APDS9960_GESTURE void APDS9960_adjustATime(void) { // not really used atm // readAllColorAndProximityData(); @@ -1764,7 +1788,7 @@ void APDS9960_adjustATime(void) { // not really used atm delay(20); } -#ifdef APDS9960_GESTURE +#ifdef USE_APDS9960_GESTURE void APDS9960_loop(void) { if (recovery_loop_counter > 0) { @@ -1794,31 +1818,31 @@ void APDS9960_loop(void) { } } -#endif // APDS9960_GESTURE +#endif // USE_APDS9960_GESTURE void APDS9960_detect(void) { - if (APDS9960type || I2cActive(APDS9960_I2C_ADDR)) { return; } + if (APDS9960_type || I2cActive(APDS9960_I2C_ADDR)) { return; } - APDS9960type = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ID); + APDS9960_type = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ID); #ifdef USE_DEBUG_DRIVER // Debug new chip - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DRV: %s Chip %X"), APDS9960_TAG, APDS9960type); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DRV: %s Chip %X"), APDS9960_TAG, APDS9960_type); #endif // USE_DEBUG_DRIVER - if (APDS9960type == APDS9960_CHIPID_1 || APDS9960type == APDS9960_CHIPID_2 || APDS9960type == APDS9960_CHIPID_3) { + if (APDS9960_type == APDS9960_CHIPID_1 || APDS9960_type == APDS9960_CHIPID_2 || APDS9960_type == APDS9960_CHIPID_3) { if (APDS9960_init()) { I2cSetActiveFound(APDS9960_I2C_ADDR, APDS9960_TAG); enableProximitySensor(); -#ifdef APDS9960_GESTURE +#ifdef USE_APDS9960_GESTURE enableGestureSensor(); -#endif // APDS9960_GESTURE +#endif // USE_APDS9960_GESTURE } else { - APDS9960type = 0; + APDS9960_type = 0; } } else { - APDS9960type = 0; + APDS9960_type = 0; } currentGesture[0] = '\0'; @@ -1829,7 +1853,7 @@ void APDS9960_detect(void) { \*********************************************************************************************/ void APDS9960_show(bool json) { - if (!APDS9960type) { return; } + if (!APDS9960_type) { return; } if (!gesture_mode && !APDS9960_overload) { uint16_t ambient; @@ -1843,7 +1867,7 @@ void APDS9960_show(bool json) { calculateColorTemperature(); // and calculate Lux if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"Red\":%u,\"Green\":%u,\"Blue\":%u,\"Ambient\":%u,\"CCT\":%u,\"Proximity\":%u}"), + ResponseAppend_P(PSTR(",\"%s\":{\"Red\":%u,\"Green\":%u,\"Blue\":%u,\"" D_JSON_ILLUMINANCE "\":%u,\"CCT\":%u,\"Proximity\":%u}"), APDS9960_TAG, color_data.r, color_data.g, @@ -1861,7 +1885,7 @@ void APDS9960_show(bool json) { WSContentSend_PD(HTTP_SNS_PROXIMITY, APDS9960_TAG, color_data.p); #endif // USE_WEBSERVER } -#ifdef APDS9960_GESTURE +#ifdef USE_APDS9960_GESTURE } else { if (currentGesture[0] != '\0') { if (json) { @@ -1873,7 +1897,7 @@ void APDS9960_show(bool json) { currentGesture[0] = '\0'; } } -#endif // APDS9960_GESTURE +#endif // USE_APDS9960_GESTURE } } @@ -1893,16 +1917,16 @@ bool APDS9960CommandSensor(void) { switch (XdrvMailbox.payload) { case 0: // Off -#ifdef APDS9960_GESTURE +#ifdef USE_APDS9960_GESTURE disableGestureSensor(); -#endif // APDS9960_GESTURE +#endif // USE_APDS9960_GESTURE gesture_mode = 0; enableLightSensor(); APDS9960_overload = false; // prevent unwanted re-enabling break; -#ifdef APDS9960_GESTURE +#ifdef USE_APDS9960_GESTURE case 1: // On with default gain of 4x - if (APDS9960type) { + if (APDS9960_type) { setGestureGain(DEFAULT_GGAIN); setProximityGain(DEFAULT_PGAIN); disableLightSensor(); @@ -1911,7 +1935,7 @@ bool APDS9960CommandSensor(void) { } break; case 2: // gain of 2x , needed for some models - if (APDS9960type) { + if (APDS9960_type) { setGestureGain(GGAIN_2X); setProximityGain(PGAIN_2X); disableLightSensor(); @@ -1919,7 +1943,7 @@ bool APDS9960CommandSensor(void) { gesture_mode = 1; } break; -#endif // APDS9960_GESTURE +#endif // USE_APDS9960_GESTURE default: int temp_aTime = (uint8_t)XdrvMailbox.payload; if (temp_aTime > 2 && temp_aTime < 256) { @@ -1946,13 +1970,13 @@ bool Xsns27(uint8_t function) { if (FUNC_INIT == function) { APDS9960_detect(); - } else if (APDS9960type) { + } else if (APDS9960_type) { switch (function) { -#ifdef APDS9960_GESTURE +#ifdef USE_APDS9960_GESTURE case FUNC_EVERY_50_MSECOND: APDS9960_loop(); break; -#endif // APDS9960_GESTURE +#endif // USE_APDS9960_GESTURE case FUNC_COMMAND_SENSOR: if (XSNS_27 == XdrvMailbox.index) { result = APDS9960CommandSensor(); From 0dc154c8e0b018cacdb6014334c9e4845cf45b75 Mon Sep 17 00:00:00 2001 From: Mickael Gaillard Date: Sun, 26 Apr 2020 19:51:20 +0200 Subject: [PATCH 370/547] Reduce footprint Signed-off-by: Mickael Gaillard --- tasmota/xsns_27_apds9960.ino | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tasmota/xsns_27_apds9960.ino b/tasmota/xsns_27_apds9960.ino index 141373801..4c557c28e 100644 --- a/tasmota/xsns_27_apds9960.ino +++ b/tasmota/xsns_27_apds9960.ino @@ -412,7 +412,7 @@ uint8_t getProxIntLowThresh(void) { * * @param[in] threshold the lower proximity threshold */ -void setProxIntLowThresh(uint8_t threshold) { +inline void setProxIntLowThresh(uint8_t threshold) { I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PILT, threshold); } @@ -435,7 +435,7 @@ uint8_t getProxIntHighThresh(void) { * * @param[in] threshold the high proximity threshold */ -void setProxIntHighThresh(uint8_t threshold) { +inline void setProxIntHighThresh(uint8_t threshold) { I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PIHT, threshold); } @@ -748,7 +748,7 @@ uint8_t getGestureEnterThresh(void) { * * @param[in] threshold proximity value needed to start gesture mode */ -void setGestureEnterThresh(uint8_t threshold) { +inline void setGestureEnterThresh(uint8_t threshold) { I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GPENTH, threshold); } @@ -771,7 +771,7 @@ uint8_t getGestureExitThresh(void) { * * @param[in] threshold proximity value needed to end gesture mode */ -void setGestureExitThresh(uint8_t threshold) { +inline void setGestureExitThresh(uint8_t threshold) { I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GEXTH, threshold); } @@ -1285,7 +1285,7 @@ bool APDS9960_init(void) { * * @return Contents of the ENABLE register. 0xFF if error. */ -uint8_t getMode(void) { +inline uint8_t getMode(void) { uint8_t enable_value; /* Read current ENABLE register */ @@ -1526,7 +1526,7 @@ int16_t readGesture(void) { * Turn the APDS-9960 on * */ -void enablePower(void) { +inline void enablePower(void) { setMode(POWER, ON); } @@ -1534,7 +1534,7 @@ void enablePower(void) { * Turn the APDS-9960 off * */ -void disablePower(void) { +inline void disablePower(void) { setMode(POWER, OFF); } @@ -1546,7 +1546,7 @@ void disablePower(void) { /** * @brief Reads the ARGB-Data and fills color_data */ -void readAllColorAndProximityData(void) { +inline void readAllColorAndProximityData(void) { if (I2cReadBuffer(APDS9960_I2C_ADDR, APDS9960_CDATAL, (uint8_t *) &color_data, (uint16_t)9)) { // not absolutely shure, if this is a correct way to do this, but it is very short // we fill the struct byte by byte From a417df17609a61e429ed0df289d9bc52043f9ed3 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <39969427+ascillato2@users.noreply.github.com> Date: Sun, 26 Apr 2020 20:49:46 -0300 Subject: [PATCH 371/547] Fix Humidity conversion on HDC1080 https://github.com/arendst/Tasmota/issues/8194#issuecomment-615749619 --- tasmota/xsns_65_hdc1080.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xsns_65_hdc1080.ino b/tasmota/xsns_65_hdc1080.ino index 6e834ef90..5b3b159ba 100644 --- a/tasmota/xsns_65_hdc1080.ino +++ b/tasmota/xsns_65_hdc1080.ino @@ -62,7 +62,7 @@ #define HDC1080_CONV_TIME 15 // Assume 6.50 + 6.35 ms + x of conversion delay for this device #define HDC1080_TEMP_MULT 0.0025177 -#define HDC1080_RH_MULT 0.0025177 +#define HDC1080_RH_MULT 0.0015258 #define HDC1080_TEMP_OFFSET 40.0 const char* hdc_type_name = "HDC1080"; From 357e8d71b75f5a1f16bbfe0ca9b6b461f5568927 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Mon, 27 Apr 2020 07:20:17 +0200 Subject: [PATCH 372/547] fix sml transmit timer --- tasmota/xsns_53_sml.ino | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tasmota/xsns_53_sml.ino b/tasmota/xsns_53_sml.ino index c5f2d3cc2..71b205e6e 100755 --- a/tasmota/xsns_53_sml.ino +++ b/tasmota/xsns_53_sml.ino @@ -2206,7 +2206,10 @@ void SML_Check_Send(void) { char *cp; for (uint32_t cnt=sml_desc_cnt; cnt=0 && script_meter_desc[cnt].txmem) { - if ((sml_100ms_cnt%script_meter_desc[cnt].tsecs)==0) { + //AddLog_P2(LOG_LEVEL_INFO, PSTR("100 ms>> %d - %s - %d"),sml_desc_cnt,script_meter_desc[cnt].txmem,script_meter_desc[cnt].tsecs); + if ((sml_100ms_cnt>=script_meter_desc[cnt].tsecs)) { + sml_100ms_cnt=0; + //AddLog_P2(LOG_LEVEL_INFO, PSTR("100 ms>> 2"),cp); 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) { From bdb5d92d85e92a7468e3cb5c5e3b7fedc314ccc4 Mon Sep 17 00:00:00 2001 From: Matteo Albinola Date: Mon, 27 Apr 2020 09:38:39 +0200 Subject: [PATCH 373/547] Add variable for tele updates --- tasmota/settings.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tasmota/settings.h b/tasmota/settings.h index 1eeadca7b..c1efb59d7 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -529,8 +529,9 @@ struct { uint16_t windmeter_radius; // F38 uint16_t windmeter_pulse_debounce; // F3A int16_t windmeter_speed_factor; // F3C + uint8_t windmeter_tele_on_change; // F3E - uint8_t free_f3e[122]; // F3E - Decrement if adding new Setting variables just above and below + uint8_t free_f3f[121]; // F3F - Decrement if adding new Setting variables just above and below // Only 32 bit boundary variables below uint16_t pulse_counter_debounce_low; // FB8 From 5d9be9a4d04131364f0dda1965e183d93d31b9da Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 27 Apr 2020 10:35:38 +0200 Subject: [PATCH 374/547] Fix pin handling part 1 --- tasmota/support.ino | 6 +++--- tasmota/support_tasmota.ino | 6 +++--- tasmota/xdrv_24_buzzer.ino | 6 +++--- tasmota/xdrv_35_pwm_dimmer.ino | 2 +- tasmota/xnrg_01_hlw8012.ino | 2 +- tasmota/xnrg_04_mcp39f501.ino | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tasmota/support.ino b/tasmota/support.ino index ebcca3294..09b37d874 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -1100,10 +1100,10 @@ void SetPin(uint32_t lpin, uint32_t gpio) { */ } -void DigitalWrite(uint32_t gpio_pin, uint32_t state) +void DigitalWrite(uint32_t gpio_pin, uint32_t index, uint32_t state) { - if (Pin(gpio_pin) < 99) { - digitalWrite(Pin(gpio_pin), state &1); + if (Pin(gpio_pin, index) < 99) { + digitalWrite(Pin(gpio_pin, index), state &1); } } diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 1d66b978c..85a3e1a04 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -166,7 +166,7 @@ void SetLatchingRelay(power_t lpower, uint32_t state) for (uint32_t i = 0; i < devices_present; i++) { uint32_t port = (i << 1) + ((latching_power >> i) &1); - DigitalWrite(GPIO_REL1 +port, bitRead(rel_inverted, port) ? !state : state); + DigitalWrite(GPIO_REL1, port, bitRead(rel_inverted, port) ? !state : state); } } @@ -228,7 +228,7 @@ void SetDevicePower(power_t rpower, uint32_t source) for (uint32_t i = 0; i < devices_present; i++) { power_t state = rpower &1; if (i < MAX_RELAYS) { - DigitalWrite(GPIO_REL1 +i, bitRead(rel_inverted, i) ? !state : state); + DigitalWrite(GPIO_REL1, i, bitRead(rel_inverted, i) ? !state : state); } rpower >>= 1; } @@ -351,7 +351,7 @@ void SetLedPowerIdx(uint32_t led, uint32_t state) } else { led_power &= (0xFF ^ mask); } - DigitalWrite(Pin(GPIO_LED1, led), bitRead(led_inverted, led) ? !state : state); + DigitalWrite(GPIO_LED1, led, bitRead(led_inverted, led) ? !state : state); } #ifdef USE_BUZZER if (led == 0) { diff --git a/tasmota/xdrv_24_buzzer.ino b/tasmota/xdrv_24_buzzer.ino index 45b570f85..7c522fa7e 100644 --- a/tasmota/xdrv_24_buzzer.ino +++ b/tasmota/xdrv_24_buzzer.ino @@ -41,7 +41,7 @@ struct BUZZER { void BuzzerOff(void) { - DigitalWrite(GPIO_BUZZER, Buzzer.inverted); // Buzzer Off + DigitalWrite(GPIO_BUZZER, 0, Buzzer.inverted); // Buzzer Off } //void BuzzerBeep(uint32_t count = 1, uint32_t on = 1, uint32_t off = 1, uint32_t tune = 0, uint32_t mode = 0); @@ -81,7 +81,7 @@ void BuzzerSetStateToLed(uint32_t state) { if (Buzzer.enable && (2 == Buzzer.mode)) { Buzzer.state = (state != 0); - DigitalWrite(GPIO_BUZZER, (Buzzer.inverted) ? !Buzzer.state : Buzzer.state); + DigitalWrite(GPIO_BUZZER, 0, (Buzzer.inverted) ? !Buzzer.state : Buzzer.state); } } @@ -140,7 +140,7 @@ void BuzzerEvery100mSec(void) Buzzer.duration = Buzzer.set[Buzzer.state]; } } - DigitalWrite(GPIO_BUZZER, (Buzzer.inverted) ? !Buzzer.state : Buzzer.state); + DigitalWrite(GPIO_BUZZER, 0, (Buzzer.inverted) ? !Buzzer.state : Buzzer.state); } else { Buzzer.enable = false; } diff --git a/tasmota/xdrv_35_pwm_dimmer.ino b/tasmota/xdrv_35_pwm_dimmer.ino index ac2ceaf55..429bc7421 100644 --- a/tasmota/xdrv_35_pwm_dimmer.ino +++ b/tasmota/xdrv_35_pwm_dimmer.ino @@ -165,7 +165,7 @@ void PWMDimmerSetPoweredOffLed(void) void PWMDimmerSetPower(void) { - DigitalWrite(GPIO_REL1, bitRead(rel_inverted, 0) ? !power : power); + DigitalWrite(GPIO_REL1, 0, bitRead(rel_inverted, 0) ? !power : power); PWMDimmerSetBrightnessLeds(0); PWMDimmerSetPoweredOffLed(); } diff --git a/tasmota/xnrg_01_hlw8012.ino b/tasmota/xnrg_01_hlw8012.ino index 4cbe111cd..04cd20847 100644 --- a/tasmota/xnrg_01_hlw8012.ino +++ b/tasmota/xnrg_01_hlw8012.ino @@ -143,7 +143,7 @@ void HlwEvery200ms(void) if (Hlw.cf1_timer >= 8) { Hlw.cf1_timer = 0; Hlw.select_ui_flag = (Hlw.select_ui_flag) ? false : true; - DigitalWrite(GPIO_NRG_SEL, Hlw.select_ui_flag); + DigitalWrite(GPIO_NRG_SEL, 0, Hlw.select_ui_flag); if (Hlw.cf1_pulse_counter) { cf1_pulse_length = Hlw.cf1_summed_pulse_length / Hlw.cf1_pulse_counter; diff --git a/tasmota/xnrg_04_mcp39f501.ino b/tasmota/xnrg_04_mcp39f501.ino index 92eec2013..3adbd0800 100644 --- a/tasmota/xnrg_04_mcp39f501.ino +++ b/tasmota/xnrg_04_mcp39f501.ino @@ -570,7 +570,7 @@ void McpSnsInit(void) } else { mcp_buffer = (char*)(malloc(MCP_BUFFER_SIZE)); } - DigitalWrite(GPIO_MCP39F5_RST, 1); // MCP enable + DigitalWrite(GPIO_MCP39F5_RST, 0, 1); // MCP enable } else { energy_flg = ENERGY_NONE; } From 24280bcdeac2d977f078dc16ae6a8a2a70d55a26 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 27 Apr 2020 11:54:23 +0200 Subject: [PATCH 375/547] Change pin handling part 2 --- tasmota/support.ino | 4 +++ tasmota/xdrv_10_scripter.ino | 6 +++-- tasmota/xdsp_02_ssd1306.ino | 4 +-- tasmota/xdsp_04_ili9341.ino | 8 +++--- tasmota/xdsp_05_epaper_29.ino | 12 ++++----- tasmota/xdsp_06_epaper_42.ino | 8 +++--- tasmota/xdsp_08_ILI9488.ino | 12 ++++----- tasmota/xdsp_09_SSD1351.ino | 8 +++--- tasmota/xdsp_10_RA8876.ino | 8 +++--- tasmota/xlgt_01_ws2812.ino | 4 +-- tasmota/xlgt_02_my92x1.ino | 6 ++--- tasmota/xlgt_03_sm16716.ino | 22 ++++++++-------- tasmota/xlgt_04_sm2135.ino | 6 ++--- tasmota/xlgt_05_sonoff_l1.ino | 2 +- tasmota/xlgt_06_electriq_moodl.ino | 2 +- tasmota/xnrg_01_hlw8012.ino | 40 ++++++++++++++++-------------- tasmota/xnrg_02_cse7766.ino | 10 +++++--- tasmota/xnrg_03_pzem004t.ino | 4 +-- tasmota/xnrg_04_mcp39f501.ino | 10 ++++---- tasmota/xnrg_05_pzem_ac.ino | 4 +-- tasmota/xnrg_06_pzem_dc.ino | 4 +-- tasmota/xnrg_07_ade7953.ino | 2 +- tasmota/xnrg_08_sdm120.ino | 4 +-- tasmota/xnrg_09_dds2382.ino | 4 +-- tasmota/xnrg_10_sdm630.ino | 4 +-- tasmota/xnrg_11_ddsu666.ino | 4 +-- tasmota/xnrg_12_solaxX1.ino | 6 ++--- tasmota/xnrg_13_fif_le01mr.ino | 4 +-- tasmota/xsns_01_counter.ino | 20 +++++++-------- 29 files changed, 122 insertions(+), 110 deletions(-) diff --git a/tasmota/support.ino b/tasmota/support.ino index 09b37d874..03bc8d9e9 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -1073,6 +1073,10 @@ int ResponseJsonEndEnd(void) * GPIO Module and Template management \*********************************************************************************************/ +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // Fix core 2.5.x ISR not in IRAM Exception +uint32_t Pin(uint32_t gpio, uint32_t index) ICACHE_RAM_ATTR; +#endif + uint32_t Pin(uint32_t gpio, uint32_t index = 0); uint32_t Pin(uint32_t gpio, uint32_t index) { //#ifdef ESP8266 diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index 370686848..4f1c7428f 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -1573,7 +1573,8 @@ chknext: } if (!strncmp(vname,"pn[",3)) { GetNumericResult(vname+3,OPER_EQU,&fvar,0); - fvar=pin[(uint8_t)fvar]; +// fvar=pin[(uint8_t)fvar]; + fvar=Pin(fvar); // skip ] bracket len++; goto exit; @@ -1582,7 +1583,8 @@ chknext: GetNumericResult(vname+3,OPER_EQU,&fvar,0); uint8_t gpiopin=fvar; for (uint8_t i=0;ibegin(); #ifdef USE_DISPLAY_MODES1TO5 @@ -128,9 +128,9 @@ void Ili9341DisplayOnOff(uint8_t on) { // tft->showDisplay(on); // tft->invertDisplay(on); - if (pin[GPIO_BACKLIGHT] < 99) { - pinMode(pin[GPIO_BACKLIGHT], OUTPUT); - digitalWrite(pin[GPIO_BACKLIGHT], on); + if (Pin(GPIO_BACKLIGHT) < 99) { + pinMode(Pin(GPIO_BACKLIGHT), OUTPUT); + digitalWrite(Pin(GPIO_BACKLIGHT), on); } } diff --git a/tasmota/xdsp_05_epaper_29.ino b/tasmota/xdsp_05_epaper_29.ino index 946fc6343..c46a13229 100644 --- a/tasmota/xdsp_05_epaper_29.ino +++ b/tasmota/xdsp_05_epaper_29.ino @@ -67,13 +67,13 @@ void EpdInitDriver29() epd = new Epd(EPD_WIDTH,EPD_HEIGHT); // whiten display with full update, takes 3 seconds - if ((pin[GPIO_SPI_CS] < 99) && (pin[GPIO_SPI_CLK] < 99) && (pin[GPIO_SPI_MOSI] < 99)) { - epd->Begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: HardSPI CS %d, CLK %d, MOSI %d"),pin[GPIO_SPI_CS], pin[GPIO_SPI_CLK], pin[GPIO_SPI_MOSI]); + if ((Pin(GPIO_SPI_CS) < 99) && (Pin(GPIO_SPI_CLK) < 99) && (Pin(GPIO_SPI_MOSI) < 99)) { + epd->Begin(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_CLK)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: HardSPI CS %d, CLK %d, MOSI %d"),Pin(GPIO_SPI_CS), Pin(GPIO_SPI_CLK), Pin(GPIO_SPI_MOSI)); } - else if ((pin[GPIO_SSPI_CS] < 99) && (pin[GPIO_SSPI_SCLK] < 99) && (pin[GPIO_SSPI_MOSI] < 99)) { - epd->Begin(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: SoftSPI CS %d, CLK %d, MOSI %d"),pin[GPIO_SSPI_CS], pin[GPIO_SSPI_SCLK], pin[GPIO_SSPI_MOSI]); + else if ((Pin(GPIO_SSPI_CS) < 99) && (Pin(GPIO_SSPI_SCLK) < 99) && (Pin(GPIO_SSPI_MOSI) < 99)) { + epd->Begin(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: SoftSPI CS %d, CLK %d, MOSI %d"),Pin(GPIO_SSPI_CS), Pin(GPIO_SSPI_SCLK), Pin(GPIO_SSPI_MOSI)); } else { free(buffer); return; diff --git a/tasmota/xdsp_06_epaper_42.ino b/tasmota/xdsp_06_epaper_42.ino index 7a19ebb7f..b48f159bb 100644 --- a/tasmota/xdsp_06_epaper_42.ino +++ b/tasmota/xdsp_06_epaper_42.ino @@ -65,15 +65,15 @@ void EpdInitDriver42() epd42 = new Epd42(EPD_WIDTH42,EPD_HEIGHT42); #ifdef USE_SPI - if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)) { - epd42->Begin(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); + if ((Pin(GPIO_SSPI_CS)<99) && (Pin(GPIO_SSPI_MOSI)<99) && (Pin(GPIO_SSPI_SCLK)<99)) { + epd42->Begin(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK)); } else { free(buffer); return; } #else - if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)) { - epd42->Begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); + if ((Pin(GPIO_SPI_CS)<99) && (Pin(GPIO_SPI_MOSI)<99) && (Pin(GPIO_SPI_CLK)<99)) { + epd42->Begin(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_CLK)); } else { free(buffer); return; diff --git a/tasmota/xdsp_08_ILI9488.ino b/tasmota/xdsp_08_ILI9488.ino index 84a493fc6..eeb1328dc 100644 --- a/tasmota/xdsp_08_ILI9488.ino +++ b/tasmota/xdsp_08_ILI9488.ino @@ -80,16 +80,16 @@ void ILI9488_InitDriver() bg_color = ILI9488_BLACK; uint8_t bppin=BACKPLANE_PIN; - if (pin[GPIO_BACKLIGHT]<99) { - bppin=pin[GPIO_BACKLIGHT]; + if (Pin(GPIO_BACKLIGHT)<99) { + bppin=Pin(GPIO_BACKLIGHT); } // init renderer - if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)){ - ili9488 = new ILI9488(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK],bppin); + if ((Pin(GPIO_SSPI_CS)<99) && (Pin(GPIO_SSPI_MOSI)<99) && (Pin(GPIO_SSPI_SCLK)<99)){ + ili9488 = new ILI9488(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK),bppin); } else { - if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)) { - ili9488 = new ILI9488(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK],bppin); + if ((Pin(GPIO_SPI_CS)<99) && (Pin(GPIO_SPI_MOSI)<99) && (Pin(GPIO_SPI_CLK)<99)) { + ili9488 = new ILI9488(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_CLK),bppin); } else { return; } diff --git a/tasmota/xdsp_09_SSD1351.ino b/tasmota/xdsp_09_SSD1351.ino index be7138f65..978973c0d 100644 --- a/tasmota/xdsp_09_SSD1351.ino +++ b/tasmota/xdsp_09_SSD1351.ino @@ -60,11 +60,11 @@ void SSD1351_InitDriver() { bg_color = SSD1351_BLACK; // init renderer - if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)){ - ssd1351 = new SSD1351(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); + if ((Pin(GPIO_SSPI_CS)<99) && (Pin(GPIO_SSPI_MOSI)<99) && (Pin(GPIO_SSPI_SCLK)<99)){ + ssd1351 = new SSD1351(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK)); } else { - if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)){ - ssd1351 = new SSD1351(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); + if ((Pin(GPIO_SPI_CS)<99) && (Pin(GPIO_SPI_MOSI)<99) && (Pin(GPIO_SPI_CLK)<99)) { + ssd1351 = new SSD1351(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_CLK)); } else { return; } diff --git a/tasmota/xdsp_10_RA8876.ino b/tasmota/xdsp_10_RA8876.ino index 88f3ebad2..a91644f3e 100644 --- a/tasmota/xdsp_10_RA8876.ino +++ b/tasmota/xdsp_10_RA8876.ino @@ -72,11 +72,11 @@ void RA8876_InitDriver() bg_color = RA8876_BLACK; // init renderer, must use hardware spi - if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]==13) && (pin[GPIO_SSPI_MISO]==12) && (pin[GPIO_SSPI_SCLK]==14)) { - ra8876 = new RA8876(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_MISO],pin[GPIO_SSPI_SCLK],pin[GPIO_BACKLIGHT]); + if ((Pin(GPIO_SSPI_CS)<99) && (Pin(GPIO_SSPI_MOSI)==13) && (Pin(GPIO_SSPI_MISO)==12) && (Pin(GPIO_SSPI_SCLK)==14)) { + ra8876 = new RA8876(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_MISO),Pin(GPIO_SSPI_SCLK),Pin(GPIO_BACKLIGHT)); } else { - if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]==13) && (pin[GPIO_SPI_MISO]==12) && (pin[GPIO_SPI_CLK]==14)) { - ra8876 = new RA8876(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_MISO],pin[GPIO_SPI_CLK],pin[GPIO_BACKLIGHT]); + if ((Pin(GPIO_SPI_CS)<99) && (Pin(GPIO_SPI_MOSI)==13) && (Pin(GPIO_SPI_MISO)==12) && (Pin(GPIO_SPI_CLK)==14)) { + ra8876 = new RA8876(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_MISO),Pin(GPIO_SPI_CLK),Pin(GPIO_BACKLIGHT)); } else { return; } diff --git a/tasmota/xlgt_01_ws2812.ino b/tasmota/xlgt_01_ws2812.ino index f6d332198..fe31f9779 100644 --- a/tasmota/xlgt_01_ws2812.ino +++ b/tasmota/xlgt_01_ws2812.ino @@ -449,10 +449,10 @@ void Ws2812ShowScheme(void) void Ws2812ModuleSelected(void) { - if (pin[GPIO_WS2812] < 99) { // RGB led + if (Pin(GPIO_WS2812) < 99) { // RGB led // For DMA, the Pin is ignored as it uses GPIO3 due to DMA hardware use. - strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); + strip = new NeoPixelBus(WS2812_MAX_LEDS, Pin(GPIO_WS2812)); strip->Begin(); Ws2812Clear(); diff --git a/tasmota/xlgt_02_my92x1.ino b/tasmota/xlgt_02_my92x1.ino index 69bccaf36..b129a80b0 100644 --- a/tasmota/xlgt_02_my92x1.ino +++ b/tasmota/xlgt_02_my92x1.ino @@ -115,9 +115,9 @@ bool My92x1SetChannels(void) void My92x1ModuleSelected(void) { - if ((pin[GPIO_DCKI] < 99) && (pin[GPIO_DI] < 99)) { - My92x1.pdi_pin = pin[GPIO_DI]; - My92x1.pdcki_pin = pin[GPIO_DCKI]; + if ((Pin(GPIO_DCKI) < 99) && (Pin(GPIO_DI) < 99)) { + My92x1.pdi_pin = Pin(GPIO_DI); + My92x1.pdcki_pin = Pin(GPIO_DCKI); pinMode(My92x1.pdi_pin, OUTPUT); pinMode(My92x1.pdcki_pin, OUTPUT); diff --git a/tasmota/xlgt_03_sm16716.ino b/tasmota/xlgt_03_sm16716.ino index 8332c80d1..5269749fa 100644 --- a/tasmota/xlgt_03_sm16716.ino +++ b/tasmota/xlgt_03_sm16716.ino @@ -100,9 +100,9 @@ void SM16716_Update(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b) /* bool SM16716_ModuleSelected(void) { - Sm16716.pin_clk = pin[GPIO_SM16716_CLK]; - Sm16716.pin_dat = pin[GPIO_SM16716_DAT]; - Sm16716.pin_sel = pin[GPIO_SM16716_SEL]; + Sm16716.pin_clk = Pin(GPIO_SM16716_CLK); + Sm16716.pin_dat = Pin(GPIO_SM16716_DAT); + Sm16716.pin_sel = Pin(GPIO_SM16716_SEL); DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "ModuleSelected; clk_pin=%d, dat_pin=%d)"), Sm16716.pin_clk, Sm16716.pin_dat); return (Sm16716.pin_clk < 99) && (Sm16716.pin_dat < 99); } @@ -122,9 +122,9 @@ bool Sm16716SetChannels(void) /* // handle any PWM pins, skipping the first 3 values for sm16716 for (uint32_t i = 3; i < Light.subtype; i++) { - if (pin[GPIO_PWM1 +i-3] < 99) { + if (Pin(GPIO_PWM1, i-3) < 99) { //AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d, Pwm%d %d"), i, cur_col[i], i+1, curcol); - analogWrite(pin[GPIO_PWM1 +i-3], bitRead(pwm_inverted, i-3) ? Settings.pwm_range - cur_col_10bits[i] : cur_col_10bits[i]); + analogWrite(Pin(GPIO_PWM1, i-3), bitRead(pwm_inverted, i-3) ? Settings.pwm_range - cur_col_10bits[i] : cur_col_10bits[i]); } } */ @@ -138,17 +138,17 @@ bool Sm16716SetChannels(void) void Sm16716ModuleSelected(void) { - if ((pin[GPIO_SM16716_CLK] < 99) && (pin[GPIO_SM16716_DAT] < 99)) { - Sm16716.pin_clk = pin[GPIO_SM16716_CLK]; - Sm16716.pin_dat = pin[GPIO_SM16716_DAT]; - Sm16716.pin_sel = pin[GPIO_SM16716_SEL]; + if ((Pin(GPIO_SM16716_CLK) < 99) && (Pin(GPIO_SM16716_DAT) < 99)) { + Sm16716.pin_clk = Pin(GPIO_SM16716_CLK); + Sm16716.pin_dat = Pin(GPIO_SM16716_DAT); + Sm16716.pin_sel = Pin(GPIO_SM16716_SEL); /* // init PWM for (uint32_t i = 0; i < Light.subtype; i++) { Settings.pwm_value[i] = 0; // Disable direct PWM control - if (pin[GPIO_PWM1 +i] < 99) { - pinMode(pin[GPIO_PWM1 +i], OUTPUT); + if (Pin(GPIO_PWM1, i) < 99) { + pinMode(Pin(GPIO_PWM1, i), OUTPUT); } } */ diff --git a/tasmota/xlgt_04_sm2135.ino b/tasmota/xlgt_04_sm2135.ino index a77108dbb..2c0374123 100644 --- a/tasmota/xlgt_04_sm2135.ino +++ b/tasmota/xlgt_04_sm2135.ino @@ -134,9 +134,9 @@ bool Sm2135SetChannels(void) void Sm2135ModuleSelected(void) { - if ((pin[GPIO_SM2135_CLK] < 99) && (pin[GPIO_SM2135_DAT] < 99)) { - Sm2135.clk = pin[GPIO_SM2135_CLK]; - Sm2135.data = pin[GPIO_SM2135_DAT]; + if ((Pin(GPIO_SM2135_CLK) < 99) && (Pin(GPIO_SM2135_DAT) < 99)) { + Sm2135.clk = Pin(GPIO_SM2135_CLK); + Sm2135.data = Pin(GPIO_SM2135_DAT); pinMode(Sm2135.data, OUTPUT); digitalWrite(Sm2135.data, HIGH); diff --git a/tasmota/xlgt_05_sonoff_l1.ino b/tasmota/xlgt_05_sonoff_l1.ino index 3caf1b06d..1a32fa9ae 100644 --- a/tasmota/xlgt_05_sonoff_l1.ino +++ b/tasmota/xlgt_05_sonoff_l1.ino @@ -221,7 +221,7 @@ bool SnfL1SetChannels(void) void SnfL1ModuleSelected(void) { if (SONOFF_L1 == my_module_type) { - if ((pin[GPIO_RXD] < 99) && (pin[GPIO_TXD] < 99)) { + if ((Pin(GPIO_RXD) < 99) && (Pin(GPIO_TXD) < 99)) { SetSerial(19200, TS_SERIAL_8N1); light_type = LT_RGB; diff --git a/tasmota/xlgt_06_electriq_moodl.ino b/tasmota/xlgt_06_electriq_moodl.ino index 7e972f968..36a1e9e7a 100644 --- a/tasmota/xlgt_06_electriq_moodl.ino +++ b/tasmota/xlgt_06_electriq_moodl.ino @@ -70,7 +70,7 @@ bool ElectriqMoodLSetChannels(void) void ElectriqMoodLModuleSelected(void) { - if (pin[GPIO_ELECTRIQ_MOODL_TX] < 99) { + if (Pin(GPIO_ELECTRIQ_MOODL_TX) < 99) { SetSerial(9600, TS_SERIAL_8N1); light_type = LT_RGBW; light_flg = XLGT_06; diff --git a/tasmota/xnrg_01_hlw8012.ino b/tasmota/xnrg_01_hlw8012.ino index 04cd20847..6a106e985 100644 --- a/tasmota/xnrg_01_hlw8012.ino +++ b/tasmota/xnrg_01_hlw8012.ino @@ -138,7 +138,7 @@ void HlwEvery200ms(void) } } - if (pin[GPIO_NRG_CF1] < 99) { + if (Pin(GPIO_NRG_CF1) < 99) { Hlw.cf1_timer++; if (Hlw.cf1_timer >= 8) { Hlw.cf1_timer = 0; @@ -233,38 +233,42 @@ void HlwSnsInit(void) Hlw.current_ratio = HLW_IREF; } - if (pin[GPIO_NRG_SEL] < 99) { - pinMode(pin[GPIO_NRG_SEL], OUTPUT); - digitalWrite(pin[GPIO_NRG_SEL], Hlw.select_ui_flag); + if (Pin(GPIO_NRG_SEL) < 99) { + pinMode(Pin(GPIO_NRG_SEL), OUTPUT); + digitalWrite(Pin(GPIO_NRG_SEL), Hlw.select_ui_flag); } - if (pin[GPIO_NRG_CF1] < 99) { - pinMode(pin[GPIO_NRG_CF1], INPUT_PULLUP); - attachInterrupt(pin[GPIO_NRG_CF1], HlwCf1Interrupt, FALLING); + if (Pin(GPIO_NRG_CF1) < 99) { + pinMode(Pin(GPIO_NRG_CF1), INPUT_PULLUP); + attachInterrupt(Pin(GPIO_NRG_CF1), HlwCf1Interrupt, FALLING); } - pinMode(pin[GPIO_HLW_CF], INPUT_PULLUP); - attachInterrupt(pin[GPIO_HLW_CF], HlwCfInterrupt, FALLING); + pinMode(Pin(GPIO_HLW_CF), INPUT_PULLUP); + attachInterrupt(Pin(GPIO_HLW_CF), HlwCfInterrupt, FALLING); } void HlwDrvInit(void) { Hlw.model_type = 0; // HLW8012 - if (pin[GPIO_HJL_CF] < 99) { - pin[GPIO_HLW_CF] = pin[GPIO_HJL_CF]; - pin[GPIO_HJL_CF] = 99; + if (Pin(GPIO_HJL_CF) < 99) { +// pin[GPIO_HLW_CF] = pin[GPIO_HJL_CF]; +// pin[GPIO_HJL_CF] = 99; + SetPin(Pin(GPIO_HJL_CF), GPIO_HLW_CF); + SetPin(99, GPIO_HJL_CF); Hlw.model_type = 1; // HJL-01/BL0937 } - if (pin[GPIO_HLW_CF] < 99) { // HLW8012 or HJL-01 based device Power monitor + if (Pin(GPIO_HLW_CF) < 99) { // HLW8012 or HJL-01 based device Power monitor Hlw.ui_flag = true; // Voltage on high - if (pin[GPIO_NRG_SEL_INV] < 99) { - pin[GPIO_NRG_SEL] = pin[GPIO_NRG_SEL_INV]; - pin[GPIO_NRG_SEL_INV] = 99; + if (Pin(GPIO_NRG_SEL_INV) < 99) { +// pin[GPIO_NRG_SEL] = pin[GPIO_NRG_SEL_INV]; +// pin[GPIO_NRG_SEL_INV] = 99; + SetPin(Pin(GPIO_NRG_SEL_INV), GPIO_NRG_SEL); + SetPin(99, GPIO_NRG_SEL_INV); Hlw.ui_flag = false; // Voltage on low } - if (pin[GPIO_NRG_CF1] < 99) { // Voltage and/or Current monitor - if (99 == pin[GPIO_NRG_SEL]) { // Voltage and/or Current selector + if (Pin(GPIO_NRG_CF1) < 99) { // Voltage and/or Current monitor + if (99 == Pin(GPIO_NRG_SEL)) { // Voltage and/or Current selector Energy.current_available = false; // Assume Voltage } } else { diff --git a/tasmota/xnrg_02_cse7766.ino b/tasmota/xnrg_02_cse7766.ino index d7bb93665..13be8d7f9 100644 --- a/tasmota/xnrg_02_cse7766.ino +++ b/tasmota/xnrg_02_cse7766.ino @@ -23,6 +23,8 @@ * CSE7759 and CSE7766 - Energy (Sonoff S31 and Sonoff Pow R2) * HLW8032 - Energy (Blitzwolf SHP5) * + * Needs GPIO_CSE7766_RX only + * * Based on datasheet from http://www.chipsea.com/UploadFiles/2017/08/11144342F01B5662.pdf \*********************************************************************************************/ @@ -223,8 +225,8 @@ void CseEverySecond(void) void CseSnsInit(void) { // Software serial init needs to be done here as earlier (serial) interrupts may lead to Exceptions -// CseSerial = new TasmotaSerial(pin[GPIO_CSE7766_RX], pin[GPIO_CSE7766_TX], 1); - CseSerial = new TasmotaSerial(pin[GPIO_CSE7766_RX], -1, 1); +// CseSerial = new TasmotaSerial(Pin(GPIO_CSE7766_RX), Pin(GPIO_CSE7766_TX), 1); + CseSerial = new TasmotaSerial(Pin(GPIO_CSE7766_RX), -1, 1); if (CseSerial->begin(4800, 2)) { // Fake Software Serial 8E1 by using two stop bits if (CseSerial->hardwareSerial()) { SetSerial(4800, TS_SERIAL_8E1); @@ -243,8 +245,8 @@ void CseDrvInit(void) { Cse.rx_buffer = (uint8_t*)(malloc(CSE_BUFFER_SIZE)); if (Cse.rx_buffer != nullptr) { -// if ((pin[GPIO_CSE7766_RX] < 99) && (pin[GPIO_CSE7766_TX] < 99)) { - if (pin[GPIO_CSE7766_RX] < 99) { +// if ((Pin(GPIO_CSE7766_RX) < 99) && (Pin(GPIO_CSE7766_TX) < 99)) { + if (Pin(GPIO_CSE7766_RX) < 99) { energy_flg = XNRG_02; } } diff --git a/tasmota/xnrg_03_pzem004t.ino b/tasmota/xnrg_03_pzem004t.ino index 466298b0f..d61608aa6 100644 --- a/tasmota/xnrg_03_pzem004t.ino +++ b/tasmota/xnrg_03_pzem004t.ino @@ -241,7 +241,7 @@ void PzemEvery250ms(void) void PzemSnsInit(void) { // Software serial init needs to be done here as earlier (serial) interrupts may lead to Exceptions - PzemSerial = new TasmotaSerial(pin[GPIO_PZEM004_RX], pin[GPIO_PZEM0XX_TX], 1); + PzemSerial = new TasmotaSerial(Pin(GPIO_PZEM004_RX), Pin(GPIO_PZEM0XX_TX), 1); if (PzemSerial->begin(9600)) { if (PzemSerial->hardwareSerial()) { ClaimSerial(); @@ -256,7 +256,7 @@ void PzemSnsInit(void) void PzemDrvInit(void) { - if ((pin[GPIO_PZEM004_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { // Any device with a Pzem004T + if ((Pin(GPIO_PZEM004_RX) < 99) && (Pin(GPIO_PZEM0XX_TX) < 99)) { // Any device with a Pzem004T energy_flg = XNRG_03; } } diff --git a/tasmota/xnrg_04_mcp39f501.ino b/tasmota/xnrg_04_mcp39f501.ino index 3adbd0800..3c1e97d09 100644 --- a/tasmota/xnrg_04_mcp39f501.ino +++ b/tasmota/xnrg_04_mcp39f501.ino @@ -562,7 +562,7 @@ void McpEverySecond(void) void McpSnsInit(void) { // Software serial init needs to be done here as earlier (serial) interrupts may lead to Exceptions - McpSerial = new TasmotaSerial(pin[GPIO_MCP39F5_RX], pin[GPIO_MCP39F5_TX], 1); + McpSerial = new TasmotaSerial(Pin(GPIO_MCP39F5_RX), Pin(GPIO_MCP39F5_TX), 1); if (McpSerial->begin(MCP_BAUDRATE)) { if (McpSerial->hardwareSerial()) { ClaimSerial(); @@ -578,10 +578,10 @@ void McpSnsInit(void) void McpDrvInit(void) { - if ((pin[GPIO_MCP39F5_RX] < 99) && (pin[GPIO_MCP39F5_TX] < 99)) { - if (pin[GPIO_MCP39F5_RST] < 99) { - pinMode(pin[GPIO_MCP39F5_RST], OUTPUT); - digitalWrite(pin[GPIO_MCP39F5_RST], 0); // MCP disable - Reset Delta Sigma ADC's + if ((Pin(GPIO_MCP39F5_RX) < 99) && (Pin(GPIO_MCP39F5_TX) < 99)) { + if (Pin(GPIO_MCP39F5_RST) < 99) { + pinMode(Pin(GPIO_MCP39F5_RST), OUTPUT); + digitalWrite(Pin(GPIO_MCP39F5_RST), 0); // MCP disable - Reset Delta Sigma ADC's } mcp_calibrate = 0; mcp_timeout = 2; // Initial wait diff --git a/tasmota/xnrg_05_pzem_ac.ino b/tasmota/xnrg_05_pzem_ac.ino index 86c4355aa..23b4bf9dd 100644 --- a/tasmota/xnrg_05_pzem_ac.ino +++ b/tasmota/xnrg_05_pzem_ac.ino @@ -117,7 +117,7 @@ void PzemAcEverySecond(void) void PzemAcSnsInit(void) { - PzemAcModbus = new TasmotaModbus(pin[GPIO_PZEM016_RX], pin[GPIO_PZEM0XX_TX]); + PzemAcModbus = new TasmotaModbus(Pin(GPIO_PZEM016_RX), Pin(GPIO_PZEM0XX_TX)); uint8_t result = PzemAcModbus->Begin(9600); if (result) { if (2 == result) { ClaimSerial(); } @@ -130,7 +130,7 @@ void PzemAcSnsInit(void) void PzemAcDrvInit(void) { - if ((pin[GPIO_PZEM016_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { + if ((Pin(GPIO_PZEM016_RX) < 99) && (Pin(GPIO_PZEM0XX_TX) < 99)) { energy_flg = XNRG_05; } } diff --git a/tasmota/xnrg_06_pzem_dc.ino b/tasmota/xnrg_06_pzem_dc.ino index 961e4f854..5ac58a035 100644 --- a/tasmota/xnrg_06_pzem_dc.ino +++ b/tasmota/xnrg_06_pzem_dc.ino @@ -113,7 +113,7 @@ void PzemDcEverySecond(void) void PzemDcSnsInit(void) { - PzemDcModbus = new TasmotaModbus(pin[GPIO_PZEM017_RX], pin[GPIO_PZEM0XX_TX]); + PzemDcModbus = new TasmotaModbus(Pin(GPIO_PZEM017_RX), Pin(GPIO_PZEM0XX_TX)); uint8_t result = PzemDcModbus->Begin(9600, 2); // Uses two stop bits!! if (result) { if (2 == result) { ClaimSerial(); } @@ -127,7 +127,7 @@ void PzemDcSnsInit(void) void PzemDcDrvInit(void) { - if ((pin[GPIO_PZEM017_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { + if ((Pin(GPIO_PZEM017_RX) < 99) && (Pin(GPIO_PZEM0XX_TX) < 99)) { energy_flg = XNRG_06; } } diff --git a/tasmota/xnrg_07_ade7953.ino b/tasmota/xnrg_07_ade7953.ino index 46f2ebd81..1792cc255 100644 --- a/tasmota/xnrg_07_ade7953.ino +++ b/tasmota/xnrg_07_ade7953.ino @@ -199,7 +199,7 @@ void Ade7953EnergyEverySecond(void) void Ade7953DrvInit(void) { - if (pin[GPIO_ADE7953_IRQ] < 99) { // Irq on GPIO16 is not supported... + if (Pin(GPIO_ADE7953_IRQ) < 99) { // Irq on GPIO16 is not supported... delay(100); // Need 100mS to init ADE7953 if (I2cSetDevice(ADE7953_ADDR)) { if (HLW_PREF_PULSE == Settings.energy_power_calibration) { diff --git a/tasmota/xnrg_08_sdm120.ino b/tasmota/xnrg_08_sdm120.ino index e41323a73..2083f348c 100644 --- a/tasmota/xnrg_08_sdm120.ino +++ b/tasmota/xnrg_08_sdm120.ino @@ -176,7 +176,7 @@ void SDM120Every250ms(void) void Sdm120SnsInit(void) { - Sdm120Modbus = new TasmotaModbus(pin[GPIO_SDM120_RX], pin[GPIO_SDM120_TX]); + Sdm120Modbus = new TasmotaModbus(Pin(GPIO_SDM120_RX), Pin(GPIO_SDM120_TX)); uint8_t result = Sdm120Modbus->Begin(SDM120_SPEED); if (result) { if (2 == result) { ClaimSerial(); } @@ -187,7 +187,7 @@ void Sdm120SnsInit(void) void Sdm120DrvInit(void) { - if ((pin[GPIO_SDM120_RX] < 99) && (pin[GPIO_SDM120_TX] < 99)) { + if ((Pin(GPIO_SDM120_RX) < 99) && (Pin(GPIO_SDM120_TX) < 99)) { energy_flg = XNRG_08; } } diff --git a/tasmota/xnrg_09_dds2382.ino b/tasmota/xnrg_09_dds2382.ino index aea1f03ea..a713ded1f 100644 --- a/tasmota/xnrg_09_dds2382.ino +++ b/tasmota/xnrg_09_dds2382.ino @@ -91,7 +91,7 @@ void Dds2382EverySecond(void) void Dds2382SnsInit(void) { - Dds2382Modbus = new TasmotaModbus(pin[GPIO_DDS2382_RX], pin[GPIO_DDS2382_TX]); + Dds2382Modbus = new TasmotaModbus(Pin(GPIO_DDS2382_RX), Pin(GPIO_DDS2382_TX)); uint8_t result = Dds2382Modbus->Begin(DDS2382_SPEED); if (result) { if (2 == result) { ClaimSerial(); } @@ -102,7 +102,7 @@ void Dds2382SnsInit(void) void Dds2382DrvInit(void) { - if ((pin[GPIO_DDS2382_RX] < 99) && (pin[GPIO_DDS2382_TX] < 99)) { + if ((Pin(GPIO_DDS2382_RX) < 99) && (Pin(GPIO_DDS2382_TX) < 99)) { energy_flg = XNRG_09; } } diff --git a/tasmota/xnrg_10_sdm630.ino b/tasmota/xnrg_10_sdm630.ino index 81b8bdcbf..f2c474204 100644 --- a/tasmota/xnrg_10_sdm630.ino +++ b/tasmota/xnrg_10_sdm630.ino @@ -174,7 +174,7 @@ void SDM630Every250ms(void) void Sdm630SnsInit(void) { - Sdm630Modbus = new TasmotaModbus(pin[GPIO_SDM630_RX], pin[GPIO_SDM630_TX]); + Sdm630Modbus = new TasmotaModbus(Pin(GPIO_SDM630_RX), Pin(GPIO_SDM630_TX)); uint8_t result = Sdm630Modbus->Begin(SDM630_SPEED); if (result) { if (2 == result) { ClaimSerial(); } @@ -186,7 +186,7 @@ void Sdm630SnsInit(void) void Sdm630DrvInit(void) { - if ((pin[GPIO_SDM630_RX] < 99) && (pin[GPIO_SDM630_TX] < 99)) { + if ((Pin(GPIO_SDM630_RX) < 99) && (Pin(GPIO_SDM630_TX) < 99)) { energy_flg = XNRG_10; } } diff --git a/tasmota/xnrg_11_ddsu666.ino b/tasmota/xnrg_11_ddsu666.ino index 6299f4f4a..864f8c987 100644 --- a/tasmota/xnrg_11_ddsu666.ino +++ b/tasmota/xnrg_11_ddsu666.ino @@ -133,7 +133,7 @@ void DDSU666Every250ms(void) void Ddsu666SnsInit(void) { - Ddsu666Modbus = new TasmotaModbus(pin[GPIO_DDSU666_RX], pin[GPIO_DDSU666_TX]); + Ddsu666Modbus = new TasmotaModbus(Pin(GPIO_DDSU666_RX), Pin(GPIO_DDSU666_TX)); uint8_t result = Ddsu666Modbus->Begin(DDSU666_SPEED); if (result) { if (2 == result) { ClaimSerial(); } @@ -144,7 +144,7 @@ void Ddsu666SnsInit(void) void Ddsu666DrvInit(void) { - if ((pin[GPIO_DDSU666_RX] < 99) && (pin[GPIO_DDSU666_TX] < 99)) { + if ((Pin(GPIO_DDSU666_RX) < 99) && (Pin(GPIO_DDSU666_TX) < 99)) { energy_flg = XNRG_11; } } diff --git a/tasmota/xnrg_12_solaxX1.ino b/tasmota/xnrg_12_solaxX1.ino index e9da3147d..c04ed8bca 100644 --- a/tasmota/xnrg_12_solaxX1.ino +++ b/tasmota/xnrg_12_solaxX1.ino @@ -406,10 +406,10 @@ void solaxX1250MSecond(void) // Every Second void solaxX1SnsInit(void) { AddLog_P(LOG_LEVEL_DEBUG, PSTR("SX1: Solax X1 Inverter Init")); - DEBUG_SENSOR_LOG(PSTR("SX1: RX pin: %d, TX pin: %d"), pin[GPIO_SOLAXX1_RX], pin[GPIO_SOLAXX1_TX]); + DEBUG_SENSOR_LOG(PSTR("SX1: RX pin: %d, TX pin: %d"), Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX)); protocolStatus.status = 0b00100000; // hasAddress - solaxX1Serial = new TasmotaSerial(pin[GPIO_SOLAXX1_RX], pin[GPIO_SOLAXX1_TX], 1); + solaxX1Serial = new TasmotaSerial(Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX), 1); if (solaxX1Serial->begin(SOLAXX1_SPEED)) { if (solaxX1Serial->hardwareSerial()) { ClaimSerial(); } } else { @@ -419,7 +419,7 @@ void solaxX1SnsInit(void) void solaxX1DrvInit(void) { - if ((pin[GPIO_SOLAXX1_RX] < 99) && (pin[GPIO_SOLAXX1_TX] < 99)) { + if ((Pin(GPIO_SOLAXX1_RX) < 99) && (Pin(GPIO_SOLAXX1_TX) < 99)) { energy_flg = XNRG_12; } } diff --git a/tasmota/xnrg_13_fif_le01mr.ino b/tasmota/xnrg_13_fif_le01mr.ino index 18f45065c..0fcac2658 100644 --- a/tasmota/xnrg_13_fif_le01mr.ino +++ b/tasmota/xnrg_13_fif_le01mr.ino @@ -213,7 +213,7 @@ void FifLEEvery250ms(void) void FifLESnsInit(void) { - FifLEModbus = new TasmotaModbus(pin[GPIO_LE01MR_RX], pin[GPIO_LE01MR_TX]); + FifLEModbus = new TasmotaModbus(Pin(GPIO_LE01MR_RX), Pin(GPIO_LE01MR_TX)); uint8_t result = FifLEModbus->Begin(LE01MR_SPEED); if (result) { if (2 == result) { ClaimSerial(); } @@ -224,7 +224,7 @@ void FifLESnsInit(void) void FifLEDrvInit(void) { - if ((pin[GPIO_LE01MR_RX] < 99) && (pin[GPIO_LE01MR_TX] < 99)) { + if ((Pin(GPIO_LE01MR_RX) < 99) && (Pin(GPIO_LE01MR_TX) < 99)) { energy_flg = XNRG_13; } } diff --git a/tasmota/xsns_01_counter.ino b/tasmota/xsns_01_counter.ino index 663d9413a..4e6db3eb7 100644 --- a/tasmota/xsns_01_counter.ino +++ b/tasmota/xsns_01_counter.ino @@ -59,7 +59,7 @@ void CounterUpdate(uint8_t index) if (Counter.pin_state) { // handle low and high debounce times when configured - if (digitalRead(pin[GPIO_CNTR1 +index]) == bitRead(Counter.pin_state, index)) { + if (digitalRead(Pin(GPIO_CNTR1, index)) == bitRead(Counter.pin_state, index)) { // new pin state to be ignored because debounce time was not met during last IRQ return; } @@ -127,15 +127,15 @@ void CounterInit(void) function counter_callbacks[] = { CounterUpdate1, CounterUpdate2, CounterUpdate3, CounterUpdate4 }; for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (pin[GPIO_CNTR1 +i] < 99) { + if (Pin(GPIO_CNTR1, i) < 99) { Counter.any_counter = true; - pinMode(pin[GPIO_CNTR1 +i], bitRead(Counter.no_pullup, i) ? INPUT : INPUT_PULLUP); + pinMode(Pin(GPIO_CNTR1, i), bitRead(Counter.no_pullup, i) ? INPUT : INPUT_PULLUP); if ((0 == Settings.pulse_counter_debounce_low) && (0 == Settings.pulse_counter_debounce_high)) { Counter.pin_state = 0; - attachInterrupt(pin[GPIO_CNTR1 +i], counter_callbacks[i], FALLING); + attachInterrupt(Pin(GPIO_CNTR1, i), counter_callbacks[i], FALLING); } else { Counter.pin_state = 0x8f; - attachInterrupt(pin[GPIO_CNTR1 +i], counter_callbacks[i], CHANGE); + attachInterrupt(Pin(GPIO_CNTR1, i), counter_callbacks[i], CHANGE); } } } @@ -144,7 +144,7 @@ void CounterInit(void) void CounterEverySecond(void) { for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (pin[GPIO_CNTR1 +i] < 99) { + if (Pin(GPIO_CNTR1, i) < 99) { if (bitRead(Settings.pulse_counter_type, i)) { uint32_t time = micros() - Counter.timer[i]; if (time > 4200000000) { // 70 minutes @@ -158,7 +158,7 @@ void CounterEverySecond(void) void CounterSaveState(void) { for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (pin[GPIO_CNTR1 +i] < 99) { + if (Pin(GPIO_CNTR1, i) < 99) { Settings.pulse_counter[i] = RtcSettings.pulse_counter[i]; } } @@ -169,7 +169,7 @@ void CounterShow(bool json) bool header = false; uint8_t dsxflg = 0; for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (pin[GPIO_CNTR1 +i] < 99) { + if (Pin(GPIO_CNTR1, i) < 99) { char counter[33]; if (bitRead(Settings.pulse_counter_type, i)) { dtostrfd((double)RtcSettings.pulse_counter[i] / 1000000, 6, counter); @@ -213,7 +213,7 @@ void CounterShow(bool json) void CmndCounter(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) { - if ((XdrvMailbox.data_len > 0) && (pin[GPIO_CNTR1 + XdrvMailbox.index -1] < 99)) { + if ((XdrvMailbox.data_len > 0) && (Pin(GPIO_CNTR1, XdrvMailbox.index -1) < 99)) { if ((XdrvMailbox.data[0] == '-') || (XdrvMailbox.data[0] == '+')) { RtcSettings.pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload; Settings.pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload; @@ -229,7 +229,7 @@ void CmndCounter(void) void CmndCounterType(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1) && (pin[GPIO_CNTR1 + XdrvMailbox.index -1] < 99)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1) && (Pin(GPIO_CNTR1, XdrvMailbox.index -1) < 99)) { bitWrite(Settings.pulse_counter_type, XdrvMailbox.index -1, XdrvMailbox.payload &1); RtcSettings.pulse_counter[XdrvMailbox.index -1] = 0; Settings.pulse_counter[XdrvMailbox.index -1] = 0; From c939077514c9efd51de274cf2168c58c45f78d5b Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 27 Apr 2020 12:54:07 +0200 Subject: [PATCH 376/547] Change pin handling part 3 --- tasmota/support.ino | 7 ++++++- tasmota/support_button.ino | 4 ++-- tasmota/support_button_v2.ino | 6 +++--- tasmota/support_command.ino | 2 +- tasmota/support_rotary.ino | 2 +- tasmota/support_switch.ino | 6 +++--- tasmota/support_tasmota.ino | 32 +++++++++++++++--------------- tasmota/xdrv_04_light.ino | 10 +++++----- tasmota/xdrv_05_irremote.ino | 10 +++++----- tasmota/xdrv_05_irremote_full.ino | 10 +++++----- tasmota/xdrv_07_domoticz.ino | 2 +- tasmota/xdrv_08_serial_bridge.ino | 2 +- tasmota/xdrv_10_rules.ino | 4 ++-- tasmota/xdrv_12_home_assistant.ino | 4 ++-- tasmota/xdrv_14_mp3.ino | 2 +- tasmota/xdrv_16_tuyamcu.ino | 2 +- tasmota/xdrv_17_rcswitch.ino | 10 +++++----- tasmota/xdrv_23_zigbee_9_impl.ino | 2 +- tasmota/xdrv_24_buzzer.ino | 2 +- tasmota/xdrv_25_A4988_Stepper.ino | 4 ++-- tasmota/xdrv_26_ariluxrf.ino | 6 +++--- tasmota/xdrv_27_shutter.ino | 2 +- tasmota/xdrv_29_deepsleep.ino | 2 +- tasmota/xdrv_31_tasmota_slave.ino | 6 +++--- tasmota/xdrv_33_nrf24l01.ino | 2 +- tasmota/xdrv_35_pwm_dimmer.ino | 6 +++--- tasmota/xdsp_02_ssd1306.ino | 2 +- tasmota/xdsp_04_ili9341.ino | 2 +- tasmota/xdsp_05_epaper_29.ino | 4 ++-- tasmota/xdsp_06_epaper_42.ino | 4 ++-- tasmota/xdsp_08_ILI9488.ino | 6 +++--- tasmota/xdsp_09_SSD1351.ino | 4 ++-- tasmota/xdsp_10_RA8876.ino | 4 ++-- tasmota/xlgt_01_ws2812.ino | 2 +- tasmota/xlgt_02_my92x1.ino | 2 +- tasmota/xlgt_03_sm16716.ino | 6 +++--- tasmota/xlgt_04_sm2135.ino | 2 +- tasmota/xlgt_05_sonoff_l1.ino | 2 +- tasmota/xlgt_06_electriq_moodl.ino | 2 +- tasmota/xnrg_01_hlw8012.ino | 14 ++++++------- tasmota/xnrg_02_cse7766.ino | 4 ++-- tasmota/xnrg_03_pzem004t.ino | 2 +- tasmota/xnrg_04_mcp39f501.ino | 4 ++-- tasmota/xnrg_05_pzem_ac.ino | 2 +- tasmota/xnrg_06_pzem_dc.ino | 2 +- tasmota/xnrg_07_ade7953.ino | 2 +- tasmota/xnrg_08_sdm120.ino | 2 +- tasmota/xnrg_09_dds2382.ino | 2 +- tasmota/xnrg_10_sdm630.ino | 2 +- tasmota/xnrg_11_ddsu666.ino | 2 +- tasmota/xnrg_12_solaxX1.ino | 2 +- tasmota/xnrg_13_fif_le01mr.ino | 2 +- tasmota/xsns_01_counter.ino | 12 +++++------ tasmota/xsns_05_ds18x20.ino | 8 ++++---- tasmota/xsns_06_dht.ino | 4 ++-- tasmota/xsns_07_sht1x.ino | 4 ++-- tasmota/xsns_15_mhz19.ino | 4 ++-- tasmota/xsns_17_senseair.ino | 4 ++-- tasmota/xsns_18_pms5003.ino | 8 ++++---- tasmota/xsns_20_novasds.ino | 4 ++-- tasmota/xsns_22_sr04.ino | 10 +++++----- tasmota/xsns_28_tm1638.ino | 8 ++++---- 62 files changed, 151 insertions(+), 146 deletions(-) diff --git a/tasmota/support.ino b/tasmota/support.ino index 03bc8d9e9..10e34ada7 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -1094,6 +1094,11 @@ uint32_t Pin(uint32_t gpio, uint32_t index) { */ } +boolean PinUsed(uint32_t gpio, uint32_t index = 0); +boolean PinUsed(uint32_t gpio, uint32_t index) { + return (Pin(gpio, index) < 99); +} + void SetPin(uint32_t lpin, uint32_t gpio) { //#ifdef ESP8266 pin[gpio] = lpin; @@ -1106,7 +1111,7 @@ void SetPin(uint32_t lpin, uint32_t gpio) { void DigitalWrite(uint32_t gpio_pin, uint32_t index, uint32_t state) { - if (Pin(gpio_pin, index) < 99) { + if (PinUsed(gpio_pin, index)) { digitalWrite(Pin(gpio_pin, index), state &1); } } diff --git a/tasmota/support_button.ino b/tasmota/support_button.ino index 59b90e39d..659a02348 100644 --- a/tasmota/support_button.ino +++ b/tasmota/support_button.ino @@ -60,7 +60,7 @@ void ButtonInit(void) { Button.present = 0; for (uint32_t i = 0; i < MAX_KEYS; i++) { - if (Pin(GPIO_KEY1, i) < 99) { + if (PinUsed(GPIO_KEY1, i)) { Button.present++; pinMode(Pin(GPIO_KEY1, i), bitRead(Button.no_pullup_mask, i) ? INPUT : ((16 == Pin(GPIO_KEY1, i)) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); } @@ -135,7 +135,7 @@ void ButtonHandler(void) } else #endif // ESP8266 - if (Pin(GPIO_KEY1, button_index) < 99) { + if (PinUsed(GPIO_KEY1, button_index)) { button_present = 1; button = (digitalRead(Pin(GPIO_KEY1, button_index)) != bitRead(Button.inverted_mask, button_index)); } diff --git a/tasmota/support_button_v2.ino b/tasmota/support_button_v2.ino index 3bb1f9153..4dc94cf13 100644 --- a/tasmota/support_button_v2.ino +++ b/tasmota/support_button_v2.ino @@ -60,7 +60,7 @@ void ButtonInit(void) { Button.present = 0; for (uint32_t i = 0; i < MAX_KEYS; i++) { - if (Pin(GPIO_KEY1, i) < 99) { + if (PinUsed(GPIO_KEY1, i)) { Button.present++; pinMode(Pin(GPIO_KEY1, i), bitRead(Button.no_pullup_mask, i) ? INPUT : ((16 == Pin(GPIO_KEY1, i)) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); } @@ -132,7 +132,7 @@ void ButtonHandler(void) } else #endif // ESP8266 - if (Pin(GPIO_KEY1, button_index) < 99) { + if (PinUsed(GPIO_KEY1, button_index)) { button_present = 1; button = (digitalRead(Pin(GPIO_KEY1, button_index)) != bitRead(Button.inverted_mask, button_index)); } @@ -266,7 +266,7 @@ void ButtonHandler(void) } else { SendKey(KEY_BUTTON, button_index +1, Button.press_counter[button_index] +9); // 2,3,4 and 5 press send just the key value (11,12,13 and 14) for rules if (0 == button_index) { // BUTTON1 can toggle up to 5 relays if present. If a relay is not present will send out the key value (2,11,12,13 and 14) for rules - if ((Button.press_counter[button_index] > 1 && Pin(GPIO_REL1, Button.press_counter[button_index]-1) < 99) && Button.press_counter[button_index] <= MAX_RELAY_BUTTON1) { + if ((Button.press_counter[button_index] > 1 && PinUsed(GPIO_REL1, Button.press_counter[button_index]-1)) && Button.press_counter[button_index] <= MAX_RELAY_BUTTON1) { ExecuteCommandPower(button_index + Button.press_counter[button_index], POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: Relay%d found on GPIO%d"), Button.press_counter[button_index], Pin(GPIO_REL1, Button.press_counter[button_index]-1)); } diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 2066ee56c..cc8bb92ae 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -1167,7 +1167,7 @@ void CmndTemplate(void) void CmndPwm(void) { if (pwm_present && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PWMS)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= Settings.pwm_range) && (Pin(GPIO_PWM1, XdrvMailbox.index -1) < 99)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= Settings.pwm_range) && PinUsed(GPIO_PWM1, XdrvMailbox.index -1)) { Settings.pwm_value[XdrvMailbox.index -1] = XdrvMailbox.payload; analogWrite(Pin(GPIO_PWM1, XdrvMailbox.index -1), bitRead(pwm_inverted, XdrvMailbox.index -1) ? Settings.pwm_range - XdrvMailbox.payload : XdrvMailbox.payload); } diff --git a/tasmota/support_rotary.ino b/tasmota/support_rotary.ino index f93b288c5..25c064a18 100644 --- a/tasmota/support_rotary.ino +++ b/tasmota/support_rotary.ino @@ -80,7 +80,7 @@ bool RotaryButtonPressed(void) void RotaryInit(void) { Rotary.present = 0; - if ((Pin(GPIO_ROT1A) < 99) && (Pin(GPIO_ROT1B) < 99)) { + if (PinUsed(GPIO_ROT1A) && PinUsed(GPIO_ROT1B)) { Rotary.present++; pinMode(Pin(GPIO_ROT1A), INPUT_PULLUP); pinMode(Pin(GPIO_ROT1B), INPUT_PULLUP); diff --git a/tasmota/support_switch.ino b/tasmota/support_switch.ino index 3fba37b3c..64837c74c 100644 --- a/tasmota/support_switch.ino +++ b/tasmota/support_switch.ino @@ -86,7 +86,7 @@ void SwitchProbe(void) uint8_t force_low = (Settings.switch_debounce % 50) &2; // 52, 102, 152 etc for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - if (Pin(GPIO_SWT1, i) < 99) { + if (PinUsed(GPIO_SWT1, i)) { // Olimex user_switch2.c code to fix 50Hz induced pulses if (1 == digitalRead(Pin(GPIO_SWT1, i))) { @@ -127,7 +127,7 @@ void SwitchInit(void) Switch.present = 0; for (uint32_t i = 0; i < MAX_SWITCHES; i++) { Switch.last_state[i] = 1; // Init global to virtual switch state; - if (Pin(GPIO_SWT1, i) < 99) { + if (PinUsed(GPIO_SWT1, i)) { Switch.present++; pinMode(Pin(GPIO_SWT1, i), bitRead(Switch.no_pullup_mask, i) ? INPUT : ((16 == Pin(GPIO_SWT1, i)) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); Switch.last_state[i] = digitalRead(Pin(GPIO_SWT1, i)); // Set global now so doesn't change the saved power state on first switch check @@ -148,7 +148,7 @@ void SwitchHandler(uint8_t mode) uint16_t loops_per_second = 1000 / Settings.switch_debounce; for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - if ((Pin(GPIO_SWT1, i) < 99) || (mode)) { + if (PinUsed(GPIO_SWT1, i) || (mode)) { uint8_t button = Switch.virtual_state[i]; uint8_t switchflag = POWER_TOGGLE +1; diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 85a3e1a04..1f2ad385c 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -325,7 +325,7 @@ void SetPowerOnState(void) // Issue #526 and #909 for (uint32_t i = 0; i < devices_present; i++) { if (!Settings.flag3.no_power_feedback) { // SetOption63 - Don't scan relay power state at restart - #5594 and #5663 - if ((i < MAX_RELAYS) && (Pin(GPIO_REL1, i) < 99)) { + if ((i < MAX_RELAYS) && PinUsed(GPIO_REL1, i)) { bitWrite(power, i, digitalRead(Pin(GPIO_REL1, i)) ^ bitRead(rel_inverted, i)); } } @@ -339,11 +339,11 @@ void SetPowerOnState(void) 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_LED1, 1) < 99) { + if (PinUsed(GPIO_LED1, 1)) { led = 1; } } - if (Pin(GPIO_LED1, led) < 99) { + if (PinUsed(GPIO_LED1, led)) { uint32_t mask = 1 << led; if (state) { state = 1; @@ -608,7 +608,7 @@ void MqttShowPWMState(void) ResponseAppend_P(PSTR("\"" D_CMND_PWM "\":{")); bool first = true; for (uint32_t i = 0; i < MAX_PWMS; i++) { - if (Pin(GPIO_PWM1, i) < 99) { + if (PinUsed(GPIO_PWM1, i)) { ResponseAppend_P(PSTR("%s\"" D_CMND_PWM "%d\":%d"), first ? "" : ",", i+1, Settings.pwm_value[i]); first = false; } @@ -703,9 +703,9 @@ bool MqttShowSensor(void) int json_data_start = strlen(mqtt_data); for (uint32_t i = 0; i < MAX_SWITCHES; i++) { #ifdef USE_TM1638 - if ((Pin(GPIO_SWT1, i) < 99) || ((Pin(GPIO_TM16CLK) < 99) && (Pin(GPIO_TM16DIO) < 99) && (Pin(GPIO_TM16STB) < 99))) { + if (PinUsed(GPIO_SWT1, i) || (PinUsed(GPIO_TM16CLK) && PinUsed(GPIO_TM16DIO) && PinUsed(GPIO_TM16STB))) { #else - if (Pin(GPIO_SWT1, i) < 99) { + if (PinUsed(GPIO_SWT1, i)) { #endif // USE_TM1638 ResponseAppend_P(PSTR(",\"" D_JSON_SWITCH "%d\":\"%s\""), i +1, GetStateText(SwitchState(i))); } @@ -913,7 +913,7 @@ void Every250mSeconds(void) if (200 == blinks) blinks = 0; // Disable blink } } - if (Settings.ledstate &1 && (Pin(GPIO_LEDLNK) < 99 || !(blinks || restart_flag || ota_state_flag)) ) { + if (Settings.ledstate &1 && (PinUsed(GPIO_LEDLNK) || !(blinks || restart_flag || ota_state_flag)) ) { bool tstate = power & Settings.ledmask; #ifdef ESP8266 if ((SONOFF_TOUCH == my_module_type) || (SONOFF_T11 == my_module_type) || (SONOFF_T12 == my_module_type) || (SONOFF_T13 == my_module_type)) { @@ -1320,7 +1320,7 @@ void SerialInput(void) void ResetPwm(void) { for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only - if (Pin(GPIO_PWM1, i) < 99) { + if (PinUsed(GPIO_PWM1, i)) { analogWrite(Pin(GPIO_PWM1, i), bitRead(pwm_inverted, i) ? Settings.pwm_range : 0); // analogWrite(Pin(GPIO_PWM1, i), bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]); } @@ -1446,7 +1446,7 @@ void GpioInit(void) analogWriteFreq(Settings.pwm_frequency); // Default is 1000 (core_esp8266_wiring_pwm.c) #ifdef USE_SPI - spi_flg = ((((Pin(GPIO_SPI_CS) < 99) && (Pin(GPIO_SPI_CS) > 14)) || (Pin(GPIO_SPI_CS) < 12)) || (((Pin(GPIO_SPI_DC) < 99) && (Pin(GPIO_SPI_DC) > 14)) || (Pin(GPIO_SPI_DC) < 12))); + spi_flg = (((PinUsed(GPIO_SPI_CS) && (Pin(GPIO_SPI_CS) > 14)) || (Pin(GPIO_SPI_CS) < 12)) || ((PinUsed(GPIO_SPI_DC) && (Pin(GPIO_SPI_DC) > 14)) || (Pin(GPIO_SPI_DC) < 12))); if (spi_flg) { for (uint32_t i = 0; i < GPIO_MAX; i++) { if ((Pin(i) >= 12) && (Pin(i) <=14)) { SetPin(99, i); } @@ -1458,7 +1458,7 @@ void GpioInit(void) my_module.io[14] = GPIO_SPI_CLK; SetPin(14, GPIO_SPI_CLK); } - soft_spi_flg = ((Pin(GPIO_SSPI_CS) < 99) && (Pin(GPIO_SSPI_SCLK) < 99) && ((Pin(GPIO_SSPI_MOSI) < 99) || (Pin(GPIO_SSPI_MOSI) < 99))); + soft_spi_flg = (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_SCLK) && (PinUsed(GPIO_SSPI_MOSI) || PinUsed(GPIO_SSPI_MOSI))); #endif // USE_SPI // Set any non-used GPIO to INPUT - Related to resetPins() in support_legacy_cores.ino @@ -1474,7 +1474,7 @@ void GpioInit(void) } #ifdef USE_I2C - i2c_flg = ((Pin(GPIO_I2C_SCL) < 99) && (Pin(GPIO_I2C_SDA) < 99)); + i2c_flg = (PinUsed(GPIO_I2C_SCL) && PinUsed(GPIO_I2C_SDA)); if (i2c_flg) { Wire.begin(Pin(GPIO_I2C_SDA), Pin(GPIO_I2C_SCL)); } @@ -1506,7 +1506,7 @@ void GpioInit(void) #endif // ESP8266 for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only - if (Pin(GPIO_PWM1, i) < 99) { + if (PinUsed(GPIO_PWM1, i)) { pinMode(Pin(GPIO_PWM1, i), OUTPUT); if (light_type) { // force PWM GPIOs to low or high mode, see #7165 @@ -1519,7 +1519,7 @@ void GpioInit(void) } for (uint32_t i = 0; i < MAX_RELAYS; i++) { - if (Pin(GPIO_REL1, i) < 99) { + if (PinUsed(GPIO_REL1, i)) { pinMode(Pin(GPIO_REL1, i), OUTPUT); devices_present++; #ifdef ESP8266 @@ -1532,7 +1532,7 @@ void GpioInit(void) } for (uint32_t i = 0; i < MAX_LEDS; i++) { - if (Pin(GPIO_LED1, i) < 99) { + if (PinUsed(GPIO_LED1, i)) { #ifdef USE_ARILUX_RF if ((3 == i) && (leds_present < 2) && (99 == Pin(GPIO_ARIRFSEL))) { SetPin(Pin(GPIO_LED4), GPIO_ARIRFSEL); // Legacy support where LED4 was Arilux RF enable @@ -1547,13 +1547,13 @@ void GpioInit(void) #endif } } - if (Pin(GPIO_LEDLNK) < 99) { + if (PinUsed(GPIO_LEDLNK)) { pinMode(Pin(GPIO_LEDLNK), OUTPUT); digitalWrite(Pin(GPIO_LEDLNK), ledlnk_inverted); } #ifdef USE_PWM_DIMMER - if (PWM_DIMMER == my_module_type && Pin(GPIO_REL1) < 99) { devices_present--; } + if (PWM_DIMMER == my_module_type && PinUsed(GPIO_REL1)) { devices_present--; } #endif // USE_PWM_DIMMER ButtonInit(); diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index 5e3afb9d6..9aaa9c9c0 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -1244,7 +1244,7 @@ bool LightModuleInit(void) if (Settings.flag.pwm_control) { // SetOption15 - Switch between commands PWM or COLOR/DIMMER/CT/CHANNEL for (uint32_t i = 0; i < MAX_PWMS; i++) { - if (Pin(GPIO_PWM1, i) < 99) { light_type++; } // Use Dimmer/Color control for all PWM as SetOption15 = 1 + if (PinUsed(GPIO_PWM1, i)) { light_type++; } // Use Dimmer/Color control for all PWM as SetOption15 = 1 } } @@ -1347,12 +1347,12 @@ void LightInit(void) if (light_type < LT_PWM6) { // PWM for (uint32_t i = 0; i < light_type; i++) { Settings.pwm_value[i] = 0; // Disable direct PWM control - if (Pin(GPIO_PWM1, i) < 99) { + if (PinUsed(GPIO_PWM1, i)) { pinMode(Pin(GPIO_PWM1, i), OUTPUT); } } - if (Pin(GPIO_ARIRFRCV) < 99) { - if (Pin(GPIO_ARIRFSEL) < 99) { + if (PinUsed(GPIO_ARIRFRCV)) { + if (PinUsed(GPIO_ARIRFSEL)) { pinMode(Pin(GPIO_ARIRFSEL), OUTPUT); digitalWrite(Pin(GPIO_ARIRFSEL), 1); // Turn off RF } @@ -2133,7 +2133,7 @@ void LightSetOutputs(const uint16_t *cur_col_10) { // now apply the actual PWM values, adjusted and remapped 10-bits range if (light_type < LT_PWM6) { // only for direct PWM lights, not for Tuya, Armtronix... for (uint32_t i = 0; i < (Light.subtype - Light.pwm_offset); i++) { - if (Pin(GPIO_PWM1, i) < 99) { + if (PinUsed(GPIO_PWM1, i)) { //AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d"), i, cur_col_10[i]); uint16_t cur_col = cur_col_10[i + Light.pwm_offset]; if (!isChannelCT(i)) { // if CT don't use pwm_min and pwm_max diff --git a/tasmota/xdrv_05_irremote.ino b/tasmota/xdrv_05_irremote.ino index 1a961a0f2..87a51de53 100644 --- a/tasmota/xdrv_05_irremote.ino +++ b/tasmota/xdrv_05_irremote.ino @@ -279,28 +279,28 @@ bool Xdrv05(uint8_t function) { bool result = false; - if ((Pin(GPIO_IRSEND) < 99) || (Pin(GPIO_IRRECV) < 99)) { + if (PinUsed(GPIO_IRSEND) || PinUsed(GPIO_IRRECV)) { switch (function) { case FUNC_PRE_INIT: - if (Pin(GPIO_IRSEND) < 99) { + if (PinUsed(GPIO_IRSEND)) { IrSendInit(); } #ifdef USE_IR_RECEIVE - if (Pin(GPIO_IRRECV) < 99) { + if (PinUsed(GPIO_IRRECV)) { IrReceiveInit(); } #endif // USE_IR_RECEIVE break; case FUNC_EVERY_50_MSECOND: #ifdef USE_IR_RECEIVE - if (Pin(GPIO_IRRECV) < 99) { + if (PinUsed(GPIO_IRRECV)) { IrReceiveCheck(); // check if there's anything on IR side } #endif // USE_IR_RECEIVE irsend_active = false; // re-enable IR reception break; case FUNC_COMMAND: - if (Pin(GPIO_IRSEND) < 99) { + if (PinUsed(GPIO_IRSEND)) { result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand); } break; diff --git a/tasmota/xdrv_05_irremote_full.ino b/tasmota/xdrv_05_irremote_full.ino index 83ec56343..60e16b30d 100644 --- a/tasmota/xdrv_05_irremote_full.ino +++ b/tasmota/xdrv_05_irremote_full.ino @@ -635,24 +635,24 @@ bool Xdrv05(uint8_t function) { bool result = false; - if ((Pin(GPIO_IRSEND) < 99) || (Pin(GPIO_IRRECV) < 99)) { + if (PinUsed(GPIO_IRSEND) || PinUsed(GPIO_IRRECV)) { switch (function) { case FUNC_PRE_INIT: - if (Pin(GPIO_IRSEND) < 99) { + if (PinUsed(GPIO_IRSEND)) { IrSendInit(); } - if (Pin(GPIO_IRRECV) < 99) { + if (PinUsed(GPIO_IRRECV)) { IrReceiveInit(); } break; case FUNC_EVERY_50_MSECOND: - if (Pin(GPIO_IRRECV) < 99) { + if (PinUsed(GPIO_IRRECV)) { IrReceiveCheck(); // check if there's anything on IR side } irsend_active = false; // re-enable IR reception break; case FUNC_COMMAND: - if (Pin(GPIO_IRSEND) < 99) { + if (PinUsed(GPIO_IRSEND)) { result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand); } break; diff --git a/tasmota/xdrv_07_domoticz.ino b/tasmota/xdrv_07_domoticz.ino index a5b284fc2..4face9bfb 100644 --- a/tasmota/xdrv_07_domoticz.ino +++ b/tasmota/xdrv_07_domoticz.ino @@ -576,7 +576,7 @@ void HandleDomoticzConfiguration(void) i +1, i, Settings.domoticz_relay_idx[i], i +1, i, Settings.domoticz_key_idx[i]); } - if (Pin(GPIO_SWT1, i) < 99) { + if (PinUsed(GPIO_SWT1, i)) { WSContentSend_P(HTTP_FORM_DOMOTICZ_SWITCH, i +1, i, Settings.domoticz_switch_idx[i]); } diff --git a/tasmota/xdrv_08_serial_bridge.ino b/tasmota/xdrv_08_serial_bridge.ino index 313cc9d6d..a3ae5ae32 100644 --- a/tasmota/xdrv_08_serial_bridge.ino +++ b/tasmota/xdrv_08_serial_bridge.ino @@ -87,7 +87,7 @@ void SerialBridgeInput(void) void SerialBridgeInit(void) { serial_bridge_active = false; - if ((Pin(GPIO_SBR_RX) < 99) && (Pin(GPIO_SBR_TX) < 99)) { + if (PinUsed(GPIO_SBR_RX) && PinUsed(GPIO_SBR_TX)) { SerialBridgeSerial = new TasmotaSerial(Pin(GPIO_SBR_RX), Pin(GPIO_SBR_TX)); if (SerialBridgeSerial->begin(Settings.sbaudrate * 300)) { // Baud rate is stored div 300 so it fits into 16 bits if (SerialBridgeSerial->hardwareSerial()) { diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino index cfd633f38..fa5841564 100644 --- a/tasmota/xdrv_10_rules.ino +++ b/tasmota/xdrv_10_rules.ino @@ -580,9 +580,9 @@ void RulesEvery50ms(void) // Boot time SWITCHES Status for (uint32_t i = 0; i < MAX_SWITCHES; i++) { #ifdef USE_TM1638 - if ((Pin(GPIO_SWT1, i) < 99) || ((Pin(GPIO_TM16CLK) < 99) && (Pin(GPIO_TM16DIO) < 99) && (Pin(GPIO_TM16STB) < 99))) { + if (PinUsed(GPIO_SWT1, i) || (PinUsed(GPIO_TM16CLK) && PinUsed(GPIO_TM16DIO) && PinUsed(GPIO_TM16STB))) { #else - if (Pin(GPIO_SWT1, i) < 99) { + if (PinUsed(GPIO_SWT1, i)) { #endif // USE_TM1638 snprintf_P(json_event, sizeof(json_event), PSTR("{\"" D_JSON_SWITCH "%d\":{\"Boot\":%d}}"), i +1, (SwitchState(i))); RulesProcessEvent(json_event); diff --git a/tasmota/xdrv_12_home_assistant.ino b/tasmota/xdrv_12_home_assistant.ino index 30d17681c..2a95fa591 100644 --- a/tasmota/xdrv_12_home_assistant.ino +++ b/tasmota/xdrv_12_home_assistant.ino @@ -370,7 +370,7 @@ void HAssAnnounceSwitches(void) uint8_t hold = 0; uint8_t pir = 0; - if (Pin(GPIO_SWT1, switch_index) < 99) { switch_present = 1; } + if (PinUsed(GPIO_SWT1, switch_index)) { switch_present = 1; } if (KeyTopicActive(1) && strcmp(SettingsText(SET_MQTT_SWITCH_TOPIC), mqtt_topic)) // Enable Discovery for Switches only if Switchtopic is set to a custom name { @@ -451,7 +451,7 @@ void HAssAnnounceButtons(void) } else #endif { - if (Pin(GPIO_KEY1, button_index) < 99) { + if (PinUsed(GPIO_KEY1, button_index)) { button_present = 1; } } diff --git a/tasmota/xdrv_14_mp3.ino b/tasmota/xdrv_14_mp3.ino index 3bd722119..ed6beee63 100644 --- a/tasmota/xdrv_14_mp3.ino +++ b/tasmota/xdrv_14_mp3.ino @@ -232,7 +232,7 @@ bool Xdrv14(uint8_t function) { bool result = false; - if (Pin(GPIO_MP3_DFR562) < 99) { + if (PinUsed(GPIO_MP3_DFR562)) { switch (function) { case FUNC_PRE_INIT: MP3PlayerInit(); // init and start communication diff --git a/tasmota/xdrv_16_tuyamcu.ino b/tasmota/xdrv_16_tuyamcu.ino index 8b1bd73bd..bada16061 100644 --- a/tasmota/xdrv_16_tuyamcu.ino +++ b/tasmota/xdrv_16_tuyamcu.ino @@ -596,7 +596,7 @@ void TuyaNormalPowerModePacketProcess(void) bool TuyaModuleSelected(void) { - if (!(Pin(GPIO_TUYA_RX) < 99) || !(Pin(GPIO_TUYA_TX) < 99)) { // fallback to hardware-serial if not explicitly selected + if (!PinUsed(GPIO_TUYA_RX) || !PinUsed(GPIO_TUYA_TX)) { // fallback to hardware-serial if not explicitly selected SetPin(1, GPIO_TUYA_TX); SetPin(3, GPIO_TUYA_RX); Settings.my_gp.io[1] = GPIO_TUYA_TX; diff --git a/tasmota/xdrv_17_rcswitch.ino b/tasmota/xdrv_17_rcswitch.ino index 94323db1d..c5013828c 100644 --- a/tasmota/xdrv_17_rcswitch.ino +++ b/tasmota/xdrv_17_rcswitch.ino @@ -81,10 +81,10 @@ void RfReceiveCheck(void) void RfInit(void) { - if (Pin(GPIO_RFSEND) < 99) { + if (PinUsed(GPIO_RFSEND)) { mySwitch.enableTransmit(Pin(GPIO_RFSEND)); } - if (Pin(GPIO_RFRECV) < 99) { + if (PinUsed(GPIO_RFRECV)) { pinMode( Pin(GPIO_RFRECV), INPUT); mySwitch.enableReceive(Pin(GPIO_RFRECV)); } @@ -170,15 +170,15 @@ bool Xdrv17(uint8_t function) { bool result = false; - if ((Pin(GPIO_RFSEND) < 99) || (Pin(GPIO_RFRECV) < 99)) { + if (PinUsed(GPIO_RFSEND) || PinUsed(GPIO_RFRECV)) { switch (function) { case FUNC_EVERY_50_MSECOND: - if (Pin(GPIO_RFRECV) < 99) { + if (PinUsed(GPIO_RFRECV)) { RfReceiveCheck(); } break; case FUNC_COMMAND: - if (Pin(GPIO_RFSEND) < 99) { + if (PinUsed(GPIO_RFSEND)) { result = DecodeCommand(kRfSendCommands, RfSendCommand); } break; diff --git a/tasmota/xdrv_23_zigbee_9_impl.ino b/tasmota/xdrv_23_zigbee_9_impl.ino index e9024299a..983c76cf3 100644 --- a/tasmota/xdrv_23_zigbee_9_impl.ino +++ b/tasmota/xdrv_23_zigbee_9_impl.ino @@ -162,7 +162,7 @@ void ZigbeeInit(void) // AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem1 = %d"), ESP_getFreeHeap()); zigbee.active = false; - if ((Pin(GPIO_ZIGBEE_RX) < 99) && (Pin(GPIO_ZIGBEE_TX) < 99)) { + if (PinUsed(GPIO_ZIGBEE_RX) && PinUsed(GPIO_ZIGBEE_TX)) { AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "GPIOs Rx:%d Tx:%d"), Pin(GPIO_ZIGBEE_RX), Pin(GPIO_ZIGBEE_TX)); // if seriallog_level is 0, we allow GPIO 13/15 to switch to Hardware Serial ZigbeeSerial = new TasmotaSerial(Pin(GPIO_ZIGBEE_RX), Pin(GPIO_ZIGBEE_TX), seriallog_level ? 1 : 2, 0, 256); // set a receive buffer of 256 bytes diff --git a/tasmota/xdrv_24_buzzer.ino b/tasmota/xdrv_24_buzzer.ino index 7c522fa7e..f158f3cd9 100644 --- a/tasmota/xdrv_24_buzzer.ino +++ b/tasmota/xdrv_24_buzzer.ino @@ -111,7 +111,7 @@ bool BuzzerPinState(void) void BuzzerInit(void) { - if (Pin(GPIO_BUZZER) < 99) { + if (PinUsed(GPIO_BUZZER)) { pinMode(Pin(GPIO_BUZZER), OUTPUT); BuzzerOff(); } else { diff --git a/tasmota/xdrv_25_A4988_Stepper.ino b/tasmota/xdrv_25_A4988_Stepper.ino index bf75c413f..48f559314 100644 --- a/tasmota/xdrv_25_A4988_Stepper.ino +++ b/tasmota/xdrv_25_A4988_Stepper.ino @@ -93,7 +93,7 @@ void CmndDoTurn(void) { } void CmndSetMIS(void) { - if ((Pin(GPIO_A4988_MS1) < 99) && (Pin(GPIO_A4988_MS2) < 99) && (Pin(GPIO_A4988_MS3) < 99) && (XdrvMailbox.data_len > 0)) { + if (PinUsed(GPIO_A4988_MS1) && PinUsed(GPIO_A4988_MS2) && PinUsed(GPIO_A4988_MS3) && (XdrvMailbox.data_len > 0)) { short newMIS = strtoul(XdrvMailbox.data,nullptr,10); myA4988->setMIS(newMIS); ResponseCmndDone(); @@ -122,7 +122,7 @@ void CmndSetRPM(void) { bool Xdrv25(uint8_t function) { bool result = false; - if ((Pin(GPIO_A4988_DIR) < 99) && (Pin(GPIO_A4988_STP) < 99)) { + if (PinUsed(GPIO_A4988_DIR) && PinUsed(GPIO_A4988_STP)) { switch (function) { case FUNC_INIT: A4988Init(); diff --git a/tasmota/xdrv_26_ariluxrf.ino b/tasmota/xdrv_26_ariluxrf.ino index af3b80ebc..5027dfdcb 100644 --- a/tasmota/xdrv_26_ariluxrf.ino +++ b/tasmota/xdrv_26_ariluxrf.ino @@ -147,7 +147,7 @@ void AriluxRfHandler(void) void AriluxRfInit(void) { - if ((Pin(GPIO_ARIRFRCV) < 99) && (Pin(GPIO_ARIRFSEL) < 99)) { + if (PinUsed(GPIO_ARIRFRCV) && PinUsed(GPIO_ARIRFSEL)) { if (Settings.last_module != Settings.module) { Settings.rf_code[1][6] = 0; Settings.rf_code[1][7] = 0; @@ -162,7 +162,7 @@ void AriluxRfInit(void) void AriluxRfDisable(void) { - if ((Pin(GPIO_ARIRFRCV) < 99) && (Pin(GPIO_ARIRFSEL) < 99)) { + if (PinUsed(GPIO_ARIRFRCV) && PinUsed(GPIO_ARIRFSEL)) { detachInterrupt(Pin(GPIO_ARIRFRCV)); digitalWrite(Pin(GPIO_ARIRFSEL), 1); // Turn off RF } @@ -178,7 +178,7 @@ bool Xdrv26(uint8_t function) switch (function) { case FUNC_EVERY_50_MSECOND: - if (Pin(GPIO_ARIRFRCV) < 99) { AriluxRfHandler(); } + if (PinUsed(GPIO_ARIRFRCV)) { AriluxRfHandler(); } break; case FUNC_EVERY_SECOND: if (10 == uptime) { AriluxRfInit(); } // Needs rest before enabling RF interrupts diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index b2d0609ed..8e001abb9 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -201,7 +201,7 @@ void ShutterInit(void) } } else { Shutter.mode = SHT_OFF_ON__OPEN_CLOSE; - if ((Pin(GPIO_PWM1, i) < 99) && (Pin(GPIO_CNTR1, i) < 99)) { + if (PinUsed(GPIO_PWM1, i) && PinUsed(GPIO_CNTR1, i)) { Shutter.mode = SHT_OFF_ON__OPEN_CLOSE_STEPPER; Shutter.pwm_frequency[i] = 0; Shutter.accelerator[i] = 0; diff --git a/tasmota/xdrv_29_deepsleep.ino b/tasmota/xdrv_29_deepsleep.ino index c78a5fbbd..4ac0531db 100644 --- a/tasmota/xdrv_29_deepsleep.ino +++ b/tasmota/xdrv_29_deepsleep.ino @@ -54,7 +54,7 @@ bool DeepSleepEnabled(void) return false; // Disabled } - if (Pin(GPIO_DEEPSLEEP) < 99) { + if (PinUsed(GPIO_DEEPSLEEP)) { pinMode(Pin(GPIO_DEEPSLEEP), INPUT_PULLUP); return (digitalRead(Pin(GPIO_DEEPSLEEP))); // Disable DeepSleep if user holds pin GPIO_DEEPSLEEP low } diff --git a/tasmota/xdrv_31_tasmota_slave.ino b/tasmota/xdrv_31_tasmota_slave.ino index 52a43178a..37b98c897 100644 --- a/tasmota/xdrv_31_tasmota_slave.ino +++ b/tasmota/xdrv_31_tasmota_slave.ino @@ -437,15 +437,15 @@ void TasmotaSlave_Init(void) return; } if (!TSlave.SerialEnabled) { - if ((Pin(GPIO_TASMOTASLAVE_RXD) < 99) && (Pin(GPIO_TASMOTASLAVE_TXD) < 99) && - ((Pin(GPIO_TASMOTASLAVE_RST) < 99) || (Pin(GPIO_TASMOTASLAVE_RST_INV) < 99))) { + if (PinUsed(GPIO_TASMOTASLAVE_RXD) && PinUsed(GPIO_TASMOTASLAVE_TXD) && + (PinUsed(GPIO_TASMOTASLAVE_RST) || PinUsed(GPIO_TASMOTASLAVE_RST_INV))) { TasmotaSlave_Serial = new TasmotaSerial(Pin(GPIO_TASMOTASLAVE_RXD), Pin(GPIO_TASMOTASLAVE_TXD), 1, 0, 200); if (TasmotaSlave_Serial->begin(USE_TASMOTA_SLAVE_SERIAL_SPEED)) { if (TasmotaSlave_Serial->hardwareSerial()) { ClaimSerial(); } TasmotaSlave_Serial->setTimeout(50); - if (Pin(GPIO_TASMOTASLAVE_RST_INV) < 99) { + if (PinUsed(GPIO_TASMOTASLAVE_RST_INV)) { SetPin(Pin(GPIO_TASMOTASLAVE_RST_INV), GPIO_TASMOTASLAVE_RST); SetPin(99, GPIO_TASMOTASLAVE_RST_INV); TSlave.inverted = HIGH; diff --git a/tasmota/xdrv_33_nrf24l01.ino b/tasmota/xdrv_33_nrf24l01.ino index 3e7d8c63d..442bcb4db 100644 --- a/tasmota/xdrv_33_nrf24l01.ino +++ b/tasmota/xdrv_33_nrf24l01.ino @@ -73,7 +73,7 @@ bool NRF24initRadio() bool NRF24Detect(void) { - if ((Pin(GPIO_SPI_CS)<99) && (Pin(GPIO_SPI_DC)<99)){ + if (PinUsed(GPIO_SPI_CS) && PinUsed(GPIO_SPI_DC)) { SPI.pins(SCK,MOSI,MISO,-1); if(NRF24initRadio()){ NRF24.chipType = 32; // SPACE diff --git a/tasmota/xdrv_35_pwm_dimmer.ino b/tasmota/xdrv_35_pwm_dimmer.ino index 429bc7421..fd7a9d86b 100644 --- a/tasmota/xdrv_35_pwm_dimmer.ino +++ b/tasmota/xdrv_35_pwm_dimmer.ino @@ -94,7 +94,7 @@ void PWMModulePreInit(void) PWMDimmerSetPoweredOffLed(); // The relay initializes to on. If the power is supposed to be off, turn the relay off. - if (!power && Pin(GPIO_REL1) < 99) digitalWrite(Pin(GPIO_REL1), bitRead(rel_inverted, 0) ? 1 : 0); + if (!power && PinUsed(GPIO_REL1)) digitalWrite(Pin(GPIO_REL1), bitRead(rel_inverted, 0) ? 1 : 0); #ifdef USE_PWM_DIMMER_REMOTE // If remote device mode is enabled, set the device group count to the number of buttons @@ -104,7 +104,7 @@ void PWMModulePreInit(void) device_group_count = 0; for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) { - if (Pin(GPIO_KEY1, button_index) < 99) device_group_count++; + if (PinUsed(GPIO_KEY1, button_index)) device_group_count++; } remote_pwm_dimmer_count = device_group_count - 1; @@ -156,7 +156,7 @@ void PWMDimmerSetBrightnessLeds(int32_t operation) void PWMDimmerSetPoweredOffLed(void) { // Set the powered-off LED state. - if (Pin(GPIO_LEDLNK) < 99) { + if (PinUsed(GPIO_LEDLNK)) { bool power_off_led_on = !power && Settings.flag4.powered_off_led; if (ledlnk_inverted) power_off_led_on ^= 1; digitalWrite(Pin(GPIO_LEDLNK), power_off_led_on); diff --git a/tasmota/xdsp_02_ssd1306.ino b/tasmota/xdsp_02_ssd1306.ino index 64ee72794..24700b0af 100644 --- a/tasmota/xdsp_02_ssd1306.ino +++ b/tasmota/xdsp_02_ssd1306.ino @@ -71,7 +71,7 @@ void SSD1306InitDriver(void) } uint8_t reset_pin = -1; - if (Pin(GPIO_OLED_RESET) < 99) { + if (PinUsed(GPIO_OLED_RESET)) { reset_pin = Pin(GPIO_OLED_RESET); } diff --git a/tasmota/xdsp_04_ili9341.ino b/tasmota/xdsp_04_ili9341.ino index 85432de52..f755314c8 100644 --- a/tasmota/xdsp_04_ili9341.ino +++ b/tasmota/xdsp_04_ili9341.ino @@ -128,7 +128,7 @@ void Ili9341DisplayOnOff(uint8_t on) { // tft->showDisplay(on); // tft->invertDisplay(on); - if (Pin(GPIO_BACKLIGHT) < 99) { + if (PinUsed(GPIO_BACKLIGHT)) { pinMode(Pin(GPIO_BACKLIGHT), OUTPUT); digitalWrite(Pin(GPIO_BACKLIGHT), on); } diff --git a/tasmota/xdsp_05_epaper_29.ino b/tasmota/xdsp_05_epaper_29.ino index c46a13229..dc7397221 100644 --- a/tasmota/xdsp_05_epaper_29.ino +++ b/tasmota/xdsp_05_epaper_29.ino @@ -67,11 +67,11 @@ void EpdInitDriver29() epd = new Epd(EPD_WIDTH,EPD_HEIGHT); // whiten display with full update, takes 3 seconds - if ((Pin(GPIO_SPI_CS) < 99) && (Pin(GPIO_SPI_CLK) < 99) && (Pin(GPIO_SPI_MOSI) < 99)) { + if (PinUsed(GPIO_SPI_CS) && PinUsed(GPIO_SPI_CLK) && PinUsed(GPIO_SPI_MOSI)) { epd->Begin(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_CLK)); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: HardSPI CS %d, CLK %d, MOSI %d"),Pin(GPIO_SPI_CS), Pin(GPIO_SPI_CLK), Pin(GPIO_SPI_MOSI)); } - else if ((Pin(GPIO_SSPI_CS) < 99) && (Pin(GPIO_SSPI_SCLK) < 99) && (Pin(GPIO_SSPI_MOSI) < 99)) { + else if (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_SCLK) && PinUsed(GPIO_SSPI_MOSI)) { epd->Begin(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK)); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: SoftSPI CS %d, CLK %d, MOSI %d"),Pin(GPIO_SSPI_CS), Pin(GPIO_SSPI_SCLK), Pin(GPIO_SSPI_MOSI)); } else { diff --git a/tasmota/xdsp_06_epaper_42.ino b/tasmota/xdsp_06_epaper_42.ino index b48f159bb..fee42db6c 100644 --- a/tasmota/xdsp_06_epaper_42.ino +++ b/tasmota/xdsp_06_epaper_42.ino @@ -65,14 +65,14 @@ void EpdInitDriver42() epd42 = new Epd42(EPD_WIDTH42,EPD_HEIGHT42); #ifdef USE_SPI - if ((Pin(GPIO_SSPI_CS)<99) && (Pin(GPIO_SSPI_MOSI)<99) && (Pin(GPIO_SSPI_SCLK)<99)) { + if (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_MOSI) && PinUsed(GPIO_SSPI_SCLK)) { epd42->Begin(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK)); } else { free(buffer); return; } #else - if ((Pin(GPIO_SPI_CS)<99) && (Pin(GPIO_SPI_MOSI)<99) && (Pin(GPIO_SPI_CLK)<99)) { + if (PinUsed(GPIO_SPI_CS) && PinUsed(GPIO_SPI_MOSI) && PinUsed(GPIO_SPI_CLK)) { epd42->Begin(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_CLK)); } else { free(buffer); diff --git a/tasmota/xdsp_08_ILI9488.ino b/tasmota/xdsp_08_ILI9488.ino index eeb1328dc..1dc877e18 100644 --- a/tasmota/xdsp_08_ILI9488.ino +++ b/tasmota/xdsp_08_ILI9488.ino @@ -80,15 +80,15 @@ void ILI9488_InitDriver() bg_color = ILI9488_BLACK; uint8_t bppin=BACKPLANE_PIN; - if (Pin(GPIO_BACKLIGHT)<99) { + if (PinUsed(GPIO_BACKLIGHT)) { bppin=Pin(GPIO_BACKLIGHT); } // init renderer - if ((Pin(GPIO_SSPI_CS)<99) && (Pin(GPIO_SSPI_MOSI)<99) && (Pin(GPIO_SSPI_SCLK)<99)){ + if (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_MOSI) && PinUsed(GPIO_SSPI_SCLK)) { ili9488 = new ILI9488(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK),bppin); } else { - if ((Pin(GPIO_SPI_CS)<99) && (Pin(GPIO_SPI_MOSI)<99) && (Pin(GPIO_SPI_CLK)<99)) { + if (PinUsed(GPIO_SPI_CS) && PinUsed(GPIO_SPI_MOSI) && PinUsed(GPIO_SPI_CLK)) { ili9488 = new ILI9488(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_CLK),bppin); } else { return; diff --git a/tasmota/xdsp_09_SSD1351.ino b/tasmota/xdsp_09_SSD1351.ino index 978973c0d..5f3148be6 100644 --- a/tasmota/xdsp_09_SSD1351.ino +++ b/tasmota/xdsp_09_SSD1351.ino @@ -60,10 +60,10 @@ void SSD1351_InitDriver() { bg_color = SSD1351_BLACK; // init renderer - if ((Pin(GPIO_SSPI_CS)<99) && (Pin(GPIO_SSPI_MOSI)<99) && (Pin(GPIO_SSPI_SCLK)<99)){ + if (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_MOSI) && PinUsed(GPIO_SSPI_SCLK)){ ssd1351 = new SSD1351(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK)); } else { - if ((Pin(GPIO_SPI_CS)<99) && (Pin(GPIO_SPI_MOSI)<99) && (Pin(GPIO_SPI_CLK)<99)) { + if (PinUsed(GPIO_SPI_CS) && PinUsed(GPIO_SPI_MOSI) && PinUsed(GPIO_SPI_CLK)) { ssd1351 = new SSD1351(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_CLK)); } else { return; diff --git a/tasmota/xdsp_10_RA8876.ino b/tasmota/xdsp_10_RA8876.ino index a91644f3e..9c86ff2d7 100644 --- a/tasmota/xdsp_10_RA8876.ino +++ b/tasmota/xdsp_10_RA8876.ino @@ -72,10 +72,10 @@ void RA8876_InitDriver() bg_color = RA8876_BLACK; // init renderer, must use hardware spi - if ((Pin(GPIO_SSPI_CS)<99) && (Pin(GPIO_SSPI_MOSI)==13) && (Pin(GPIO_SSPI_MISO)==12) && (Pin(GPIO_SSPI_SCLK)==14)) { + if (PinUsed(GPIO_SSPI_CS) && (Pin(GPIO_SSPI_MOSI)==13) && (Pin(GPIO_SSPI_MISO)==12) && (Pin(GPIO_SSPI_SCLK)==14)) { ra8876 = new RA8876(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_MISO),Pin(GPIO_SSPI_SCLK),Pin(GPIO_BACKLIGHT)); } else { - if ((Pin(GPIO_SPI_CS)<99) && (Pin(GPIO_SPI_MOSI)==13) && (Pin(GPIO_SPI_MISO)==12) && (Pin(GPIO_SPI_CLK)==14)) { + if (PinUsed(GPIO_SPI_CS) && (Pin(GPIO_SPI_MOSI)==13) && (Pin(GPIO_SPI_MISO)==12) && (Pin(GPIO_SPI_CLK)==14)) { ra8876 = new RA8876(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_MISO),Pin(GPIO_SPI_CLK),Pin(GPIO_BACKLIGHT)); } else { return; diff --git a/tasmota/xlgt_01_ws2812.ino b/tasmota/xlgt_01_ws2812.ino index fe31f9779..368567181 100644 --- a/tasmota/xlgt_01_ws2812.ino +++ b/tasmota/xlgt_01_ws2812.ino @@ -449,7 +449,7 @@ void Ws2812ShowScheme(void) void Ws2812ModuleSelected(void) { - if (Pin(GPIO_WS2812) < 99) { // RGB led + if (PinUsed(GPIO_WS2812)) { // RGB led // For DMA, the Pin is ignored as it uses GPIO3 due to DMA hardware use. strip = new NeoPixelBus(WS2812_MAX_LEDS, Pin(GPIO_WS2812)); diff --git a/tasmota/xlgt_02_my92x1.ino b/tasmota/xlgt_02_my92x1.ino index b129a80b0..6d12abf84 100644 --- a/tasmota/xlgt_02_my92x1.ino +++ b/tasmota/xlgt_02_my92x1.ino @@ -115,7 +115,7 @@ bool My92x1SetChannels(void) void My92x1ModuleSelected(void) { - if ((Pin(GPIO_DCKI) < 99) && (Pin(GPIO_DI) < 99)) { + if (PinUsed(GPIO_DCKI) && PinUsed(GPIO_DI)) { My92x1.pdi_pin = Pin(GPIO_DI); My92x1.pdcki_pin = Pin(GPIO_DCKI); diff --git a/tasmota/xlgt_03_sm16716.ino b/tasmota/xlgt_03_sm16716.ino index 5269749fa..314122a8b 100644 --- a/tasmota/xlgt_03_sm16716.ino +++ b/tasmota/xlgt_03_sm16716.ino @@ -122,7 +122,7 @@ bool Sm16716SetChannels(void) /* // handle any PWM pins, skipping the first 3 values for sm16716 for (uint32_t i = 3; i < Light.subtype; i++) { - if (Pin(GPIO_PWM1, i-3) < 99) { + if (PinUsed(GPIO_PWM1, i-3)) { //AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d, Pwm%d %d"), i, cur_col[i], i+1, curcol); analogWrite(Pin(GPIO_PWM1, i-3), bitRead(pwm_inverted, i-3) ? Settings.pwm_range - cur_col_10bits[i] : cur_col_10bits[i]); } @@ -138,7 +138,7 @@ bool Sm16716SetChannels(void) void Sm16716ModuleSelected(void) { - if ((Pin(GPIO_SM16716_CLK) < 99) && (Pin(GPIO_SM16716_DAT) < 99)) { + if (PinUsed(GPIO_SM16716_CLK) && PinUsed(GPIO_SM16716_DAT)) { Sm16716.pin_clk = Pin(GPIO_SM16716_CLK); Sm16716.pin_dat = Pin(GPIO_SM16716_DAT); Sm16716.pin_sel = Pin(GPIO_SM16716_SEL); @@ -147,7 +147,7 @@ void Sm16716ModuleSelected(void) // init PWM for (uint32_t i = 0; i < Light.subtype; i++) { Settings.pwm_value[i] = 0; // Disable direct PWM control - if (Pin(GPIO_PWM1, i) < 99) { + if (PinUsed(GPIO_PWM1, i)) { pinMode(Pin(GPIO_PWM1, i), OUTPUT); } } diff --git a/tasmota/xlgt_04_sm2135.ino b/tasmota/xlgt_04_sm2135.ino index 2c0374123..e262395a8 100644 --- a/tasmota/xlgt_04_sm2135.ino +++ b/tasmota/xlgt_04_sm2135.ino @@ -134,7 +134,7 @@ bool Sm2135SetChannels(void) void Sm2135ModuleSelected(void) { - if ((Pin(GPIO_SM2135_CLK) < 99) && (Pin(GPIO_SM2135_DAT) < 99)) { + if (PinUsed(GPIO_SM2135_CLK) && PinUsed(GPIO_SM2135_DAT)) { Sm2135.clk = Pin(GPIO_SM2135_CLK); Sm2135.data = Pin(GPIO_SM2135_DAT); diff --git a/tasmota/xlgt_05_sonoff_l1.ino b/tasmota/xlgt_05_sonoff_l1.ino index 1a32fa9ae..295330142 100644 --- a/tasmota/xlgt_05_sonoff_l1.ino +++ b/tasmota/xlgt_05_sonoff_l1.ino @@ -221,7 +221,7 @@ bool SnfL1SetChannels(void) void SnfL1ModuleSelected(void) { if (SONOFF_L1 == my_module_type) { - if ((Pin(GPIO_RXD) < 99) && (Pin(GPIO_TXD) < 99)) { + if (PinUsed(GPIO_RXD) && PinUsed(GPIO_TXD)) { SetSerial(19200, TS_SERIAL_8N1); light_type = LT_RGB; diff --git a/tasmota/xlgt_06_electriq_moodl.ino b/tasmota/xlgt_06_electriq_moodl.ino index 36a1e9e7a..57c493e64 100644 --- a/tasmota/xlgt_06_electriq_moodl.ino +++ b/tasmota/xlgt_06_electriq_moodl.ino @@ -70,7 +70,7 @@ bool ElectriqMoodLSetChannels(void) void ElectriqMoodLModuleSelected(void) { - if (Pin(GPIO_ELECTRIQ_MOODL_TX) < 99) { + if (PinUsed(GPIO_ELECTRIQ_MOODL_TX)) { SetSerial(9600, TS_SERIAL_8N1); light_type = LT_RGBW; light_flg = XLGT_06; diff --git a/tasmota/xnrg_01_hlw8012.ino b/tasmota/xnrg_01_hlw8012.ino index 6a106e985..9a054422e 100644 --- a/tasmota/xnrg_01_hlw8012.ino +++ b/tasmota/xnrg_01_hlw8012.ino @@ -138,7 +138,7 @@ void HlwEvery200ms(void) } } - if (Pin(GPIO_NRG_CF1) < 99) { + if (PinUsed(GPIO_NRG_CF1)) { Hlw.cf1_timer++; if (Hlw.cf1_timer >= 8) { Hlw.cf1_timer = 0; @@ -233,11 +233,11 @@ void HlwSnsInit(void) Hlw.current_ratio = HLW_IREF; } - if (Pin(GPIO_NRG_SEL) < 99) { + if (PinUsed(GPIO_NRG_SEL)) { pinMode(Pin(GPIO_NRG_SEL), OUTPUT); digitalWrite(Pin(GPIO_NRG_SEL), Hlw.select_ui_flag); } - if (Pin(GPIO_NRG_CF1) < 99) { + if (PinUsed(GPIO_NRG_CF1)) { pinMode(Pin(GPIO_NRG_CF1), INPUT_PULLUP); attachInterrupt(Pin(GPIO_NRG_CF1), HlwCf1Interrupt, FALLING); } @@ -248,7 +248,7 @@ void HlwSnsInit(void) void HlwDrvInit(void) { Hlw.model_type = 0; // HLW8012 - if (Pin(GPIO_HJL_CF) < 99) { + if (PinUsed(GPIO_HJL_CF)) { // pin[GPIO_HLW_CF] = pin[GPIO_HJL_CF]; // pin[GPIO_HJL_CF] = 99; SetPin(Pin(GPIO_HJL_CF), GPIO_HLW_CF); @@ -256,10 +256,10 @@ void HlwDrvInit(void) Hlw.model_type = 1; // HJL-01/BL0937 } - if (Pin(GPIO_HLW_CF) < 99) { // HLW8012 or HJL-01 based device Power monitor + if (PinUsed(GPIO_HLW_CF)) { // HLW8012 or HJL-01 based device Power monitor Hlw.ui_flag = true; // Voltage on high - if (Pin(GPIO_NRG_SEL_INV) < 99) { + if (PinUsed(GPIO_NRG_SEL_INV)) { // pin[GPIO_NRG_SEL] = pin[GPIO_NRG_SEL_INV]; // pin[GPIO_NRG_SEL_INV] = 99; SetPin(Pin(GPIO_NRG_SEL_INV), GPIO_NRG_SEL); @@ -267,7 +267,7 @@ void HlwDrvInit(void) Hlw.ui_flag = false; // Voltage on low } - if (Pin(GPIO_NRG_CF1) < 99) { // Voltage and/or Current monitor + if (PinUsed(GPIO_NRG_CF1)) { // Voltage and/or Current monitor if (99 == Pin(GPIO_NRG_SEL)) { // Voltage and/or Current selector Energy.current_available = false; // Assume Voltage } diff --git a/tasmota/xnrg_02_cse7766.ino b/tasmota/xnrg_02_cse7766.ino index 13be8d7f9..dae622845 100644 --- a/tasmota/xnrg_02_cse7766.ino +++ b/tasmota/xnrg_02_cse7766.ino @@ -245,8 +245,8 @@ void CseDrvInit(void) { Cse.rx_buffer = (uint8_t*)(malloc(CSE_BUFFER_SIZE)); if (Cse.rx_buffer != nullptr) { -// if ((Pin(GPIO_CSE7766_RX) < 99) && (Pin(GPIO_CSE7766_TX) < 99)) { - if (Pin(GPIO_CSE7766_RX) < 99) { +// if (PinUsed(GPIO_CSE7766_RX) && PinUsed(GPIO_CSE7766_TX)) { + if (PinUsed(GPIO_CSE7766_RX)) { energy_flg = XNRG_02; } } diff --git a/tasmota/xnrg_03_pzem004t.ino b/tasmota/xnrg_03_pzem004t.ino index d61608aa6..f06b34b0e 100644 --- a/tasmota/xnrg_03_pzem004t.ino +++ b/tasmota/xnrg_03_pzem004t.ino @@ -256,7 +256,7 @@ void PzemSnsInit(void) void PzemDrvInit(void) { - if ((Pin(GPIO_PZEM004_RX) < 99) && (Pin(GPIO_PZEM0XX_TX) < 99)) { // Any device with a Pzem004T + if (PinUsed(GPIO_PZEM004_RX) && PinUsed(GPIO_PZEM0XX_TX)) { // Any device with a Pzem004T energy_flg = XNRG_03; } } diff --git a/tasmota/xnrg_04_mcp39f501.ino b/tasmota/xnrg_04_mcp39f501.ino index 3c1e97d09..b3f8dbe6c 100644 --- a/tasmota/xnrg_04_mcp39f501.ino +++ b/tasmota/xnrg_04_mcp39f501.ino @@ -578,8 +578,8 @@ void McpSnsInit(void) void McpDrvInit(void) { - if ((Pin(GPIO_MCP39F5_RX) < 99) && (Pin(GPIO_MCP39F5_TX) < 99)) { - if (Pin(GPIO_MCP39F5_RST) < 99) { + if (PinUsed(GPIO_MCP39F5_RX) && PinUsed(GPIO_MCP39F5_TX)) { + if (PinUsed(GPIO_MCP39F5_RST)) { pinMode(Pin(GPIO_MCP39F5_RST), OUTPUT); digitalWrite(Pin(GPIO_MCP39F5_RST), 0); // MCP disable - Reset Delta Sigma ADC's } diff --git a/tasmota/xnrg_05_pzem_ac.ino b/tasmota/xnrg_05_pzem_ac.ino index 23b4bf9dd..bd656a874 100644 --- a/tasmota/xnrg_05_pzem_ac.ino +++ b/tasmota/xnrg_05_pzem_ac.ino @@ -130,7 +130,7 @@ void PzemAcSnsInit(void) void PzemAcDrvInit(void) { - if ((Pin(GPIO_PZEM016_RX) < 99) && (Pin(GPIO_PZEM0XX_TX) < 99)) { + if (PinUsed(GPIO_PZEM016_RX) && PinUsed(GPIO_PZEM0XX_TX)) { energy_flg = XNRG_05; } } diff --git a/tasmota/xnrg_06_pzem_dc.ino b/tasmota/xnrg_06_pzem_dc.ino index 5ac58a035..444861fb0 100644 --- a/tasmota/xnrg_06_pzem_dc.ino +++ b/tasmota/xnrg_06_pzem_dc.ino @@ -127,7 +127,7 @@ void PzemDcSnsInit(void) void PzemDcDrvInit(void) { - if ((Pin(GPIO_PZEM017_RX) < 99) && (Pin(GPIO_PZEM0XX_TX) < 99)) { + if (PinUsed(GPIO_PZEM017_RX) && PinUsed(GPIO_PZEM0XX_TX)) { energy_flg = XNRG_06; } } diff --git a/tasmota/xnrg_07_ade7953.ino b/tasmota/xnrg_07_ade7953.ino index 1792cc255..44bec2e59 100644 --- a/tasmota/xnrg_07_ade7953.ino +++ b/tasmota/xnrg_07_ade7953.ino @@ -199,7 +199,7 @@ void Ade7953EnergyEverySecond(void) void Ade7953DrvInit(void) { - if (Pin(GPIO_ADE7953_IRQ) < 99) { // Irq on GPIO16 is not supported... + if (PinUsed(GPIO_ADE7953_IRQ)) { // Irq on GPIO16 is not supported... delay(100); // Need 100mS to init ADE7953 if (I2cSetDevice(ADE7953_ADDR)) { if (HLW_PREF_PULSE == Settings.energy_power_calibration) { diff --git a/tasmota/xnrg_08_sdm120.ino b/tasmota/xnrg_08_sdm120.ino index 2083f348c..93af326f8 100644 --- a/tasmota/xnrg_08_sdm120.ino +++ b/tasmota/xnrg_08_sdm120.ino @@ -187,7 +187,7 @@ void Sdm120SnsInit(void) void Sdm120DrvInit(void) { - if ((Pin(GPIO_SDM120_RX) < 99) && (Pin(GPIO_SDM120_TX) < 99)) { + if (PinUsed(GPIO_SDM120_RX) && PinUsed(GPIO_SDM120_TX)) { energy_flg = XNRG_08; } } diff --git a/tasmota/xnrg_09_dds2382.ino b/tasmota/xnrg_09_dds2382.ino index a713ded1f..a516ea4a8 100644 --- a/tasmota/xnrg_09_dds2382.ino +++ b/tasmota/xnrg_09_dds2382.ino @@ -102,7 +102,7 @@ void Dds2382SnsInit(void) void Dds2382DrvInit(void) { - if ((Pin(GPIO_DDS2382_RX) < 99) && (Pin(GPIO_DDS2382_TX) < 99)) { + if (PinUsed(GPIO_DDS2382_RX) && PinUsed(GPIO_DDS2382_TX)) { energy_flg = XNRG_09; } } diff --git a/tasmota/xnrg_10_sdm630.ino b/tasmota/xnrg_10_sdm630.ino index f2c474204..4bfeecc96 100644 --- a/tasmota/xnrg_10_sdm630.ino +++ b/tasmota/xnrg_10_sdm630.ino @@ -186,7 +186,7 @@ void Sdm630SnsInit(void) void Sdm630DrvInit(void) { - if ((Pin(GPIO_SDM630_RX) < 99) && (Pin(GPIO_SDM630_TX) < 99)) { + if (PinUsed(GPIO_SDM630_RX) && PinUsed(GPIO_SDM630_TX)) { energy_flg = XNRG_10; } } diff --git a/tasmota/xnrg_11_ddsu666.ino b/tasmota/xnrg_11_ddsu666.ino index 864f8c987..87b585d84 100644 --- a/tasmota/xnrg_11_ddsu666.ino +++ b/tasmota/xnrg_11_ddsu666.ino @@ -144,7 +144,7 @@ void Ddsu666SnsInit(void) void Ddsu666DrvInit(void) { - if ((Pin(GPIO_DDSU666_RX) < 99) && (Pin(GPIO_DDSU666_TX) < 99)) { + if (PinUsed(GPIO_DDSU666_RX) && PinUsed(GPIO_DDSU666_TX)) { energy_flg = XNRG_11; } } diff --git a/tasmota/xnrg_12_solaxX1.ino b/tasmota/xnrg_12_solaxX1.ino index c04ed8bca..418a92acb 100644 --- a/tasmota/xnrg_12_solaxX1.ino +++ b/tasmota/xnrg_12_solaxX1.ino @@ -419,7 +419,7 @@ void solaxX1SnsInit(void) void solaxX1DrvInit(void) { - if ((Pin(GPIO_SOLAXX1_RX) < 99) && (Pin(GPIO_SOLAXX1_TX) < 99)) { + if (PinUsed(GPIO_SOLAXX1_RX) && PinUsed(GPIO_SOLAXX1_TX)) { energy_flg = XNRG_12; } } diff --git a/tasmota/xnrg_13_fif_le01mr.ino b/tasmota/xnrg_13_fif_le01mr.ino index 0fcac2658..71e77793a 100644 --- a/tasmota/xnrg_13_fif_le01mr.ino +++ b/tasmota/xnrg_13_fif_le01mr.ino @@ -224,7 +224,7 @@ void FifLESnsInit(void) void FifLEDrvInit(void) { - if ((Pin(GPIO_LE01MR_RX) < 99) && (Pin(GPIO_LE01MR_TX) < 99)) { + if (PinUsed(GPIO_LE01MR_RX) && PinUsed(GPIO_LE01MR_TX)) { energy_flg = XNRG_13; } } diff --git a/tasmota/xsns_01_counter.ino b/tasmota/xsns_01_counter.ino index 4e6db3eb7..fec5791f7 100644 --- a/tasmota/xsns_01_counter.ino +++ b/tasmota/xsns_01_counter.ino @@ -127,7 +127,7 @@ void CounterInit(void) function counter_callbacks[] = { CounterUpdate1, CounterUpdate2, CounterUpdate3, CounterUpdate4 }; for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (Pin(GPIO_CNTR1, i) < 99) { + if (PinUsed(GPIO_CNTR1, i)) { Counter.any_counter = true; pinMode(Pin(GPIO_CNTR1, i), bitRead(Counter.no_pullup, i) ? INPUT : INPUT_PULLUP); if ((0 == Settings.pulse_counter_debounce_low) && (0 == Settings.pulse_counter_debounce_high)) { @@ -144,7 +144,7 @@ void CounterInit(void) void CounterEverySecond(void) { for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (Pin(GPIO_CNTR1, i) < 99) { + if (PinUsed(GPIO_CNTR1, i)) { if (bitRead(Settings.pulse_counter_type, i)) { uint32_t time = micros() - Counter.timer[i]; if (time > 4200000000) { // 70 minutes @@ -158,7 +158,7 @@ void CounterEverySecond(void) void CounterSaveState(void) { for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (Pin(GPIO_CNTR1, i) < 99) { + if (PinUsed(GPIO_CNTR1, i)) { Settings.pulse_counter[i] = RtcSettings.pulse_counter[i]; } } @@ -169,7 +169,7 @@ void CounterShow(bool json) bool header = false; uint8_t dsxflg = 0; for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (Pin(GPIO_CNTR1, i) < 99) { + if (PinUsed(GPIO_CNTR1, i)) { char counter[33]; if (bitRead(Settings.pulse_counter_type, i)) { dtostrfd((double)RtcSettings.pulse_counter[i] / 1000000, 6, counter); @@ -213,7 +213,7 @@ void CounterShow(bool json) void CmndCounter(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) { - if ((XdrvMailbox.data_len > 0) && (Pin(GPIO_CNTR1, XdrvMailbox.index -1) < 99)) { + if ((XdrvMailbox.data_len > 0) && PinUsed(GPIO_CNTR1, XdrvMailbox.index -1)) { if ((XdrvMailbox.data[0] == '-') || (XdrvMailbox.data[0] == '+')) { RtcSettings.pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload; Settings.pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload; @@ -229,7 +229,7 @@ void CmndCounter(void) void CmndCounterType(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1) && (Pin(GPIO_CNTR1, XdrvMailbox.index -1) < 99)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1) && PinUsed(GPIO_CNTR1, XdrvMailbox.index -1)) { bitWrite(Settings.pulse_counter_type, XdrvMailbox.index -1, XdrvMailbox.payload &1); RtcSettings.pulse_counter[XdrvMailbox.index -1] = 0; Settings.pulse_counter[XdrvMailbox.index -1] = 0; diff --git a/tasmota/xsns_05_ds18x20.ino b/tasmota/xsns_05_ds18x20.ino index 046fb11c4..a762014c7 100644 --- a/tasmota/xsns_05_ds18x20.ino +++ b/tasmota/xsns_05_ds18x20.ino @@ -304,10 +304,10 @@ void Ds18x20Init(void) { uint64_t ids[DS18X20_MAX_SENSORS]; - ds18x20_pin = pin[GPIO_DSB]; + ds18x20_pin = Pin(GPIO_DSB); - if (pin[GPIO_DSB_OUT] < 99) { - ds18x20_pin_out = pin[GPIO_DSB_OUT]; + if (PinUsed(GPIO_DSB_OUT)) { + ds18x20_pin_out = Pin(GPIO_DSB_OUT); ds18x20_dual_mode = true; // Dual pins mode as used by Shelly pinMode(ds18x20_pin_out, OUTPUT); pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); // SetOption74 - Enable internal pullup for single DS18x20 sensor @@ -518,7 +518,7 @@ bool Xsns05(uint8_t function) { bool result = false; - if (pin[GPIO_DSB] < 99) { + if (PinUsed(GPIO_DSB)) { switch (function) { case FUNC_INIT: Ds18x20Init(); diff --git a/tasmota/xsns_06_dht.ino b/tasmota/xsns_06_dht.ino index 2edc4abfa..46fb69fa5 100644 --- a/tasmota/xsns_06_dht.ino +++ b/tasmota/xsns_06_dht.ino @@ -204,8 +204,8 @@ bool DhtPinState() void DhtInit(void) { if (dht_sensors) { - if (pin[GPIO_DHT11_OUT] < 99) { - dht_pin_out = pin[GPIO_DHT11_OUT]; + if (PinUsed(GPIO_DHT11_OUT)) { + dht_pin_out = Pin(GPIO_DHT11_OUT); dht_dual_mode = true; // Dual pins mode as used by Shelly dht_sensors = 1; // We only support one sensor in pseudo mode pinMode(dht_pin_out, OUTPUT); diff --git a/tasmota/xsns_07_sht1x.ino b/tasmota/xsns_07_sht1x.ino index 735802ca2..e58c5553f 100644 --- a/tasmota/xsns_07_sht1x.ino +++ b/tasmota/xsns_07_sht1x.ino @@ -159,8 +159,8 @@ bool ShtRead(void) void ShtDetect(void) { - sht_sda_pin = pin[GPIO_I2C_SDA]; - sht_scl_pin = pin[GPIO_I2C_SCL]; + sht_sda_pin = Pin(GPIO_I2C_SDA); + sht_scl_pin = Pin(GPIO_I2C_SCL); if (ShtRead()) { sht_type = 1; AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C D_SHT1X_FOUND)); diff --git a/tasmota/xsns_15_mhz19.ino b/tasmota/xsns_15_mhz19.ino index 633ca3f59..bc0ee8904 100644 --- a/tasmota/xsns_15_mhz19.ino +++ b/tasmota/xsns_15_mhz19.ino @@ -325,8 +325,8 @@ bool MhzCommandSensor(void) void MhzInit(void) { mhz_type = 0; - if ((pin[GPIO_MHZ_RXD] < 99) && (pin[GPIO_MHZ_TXD] < 99)) { - MhzSerial = new TasmotaSerial(pin[GPIO_MHZ_RXD], pin[GPIO_MHZ_TXD], 1); + if (PinUsed(GPIO_MHZ_RXD) && PinUsed(GPIO_MHZ_TXD)) { + MhzSerial = new TasmotaSerial(Pin(GPIO_MHZ_RXD), Pin(GPIO_MHZ_TXD), 1); if (MhzSerial->begin(9600)) { if (MhzSerial->hardwareSerial()) { ClaimSerial(); } mhz_type = 1; diff --git a/tasmota/xsns_17_senseair.ino b/tasmota/xsns_17_senseair.ino index 60614b4c7..90ba979bb 100644 --- a/tasmota/xsns_17_senseair.ino +++ b/tasmota/xsns_17_senseair.ino @@ -132,8 +132,8 @@ void Senseair250ms(void) // Every 250 mSec void SenseairInit(void) { senseair_type = 0; - if ((pin[GPIO_SAIR_RX] < 99) && (pin[GPIO_SAIR_TX] < 99)) { - SenseairModbus = new TasmotaModbus(pin[GPIO_SAIR_RX], pin[GPIO_SAIR_TX]); + if (PinUsed(GPIO_SAIR_RX) && PinUsed(GPIO_SAIR_TX)) { + SenseairModbus = new TasmotaModbus(Pin(GPIO_SAIR_RX), Pin(GPIO_SAIR_TX)); uint8_t result = SenseairModbus->Begin(SENSEAIR_MODBUS_SPEED); if (result) { if (2 == result) { ClaimSerial(); } diff --git a/tasmota/xsns_18_pms5003.ino b/tasmota/xsns_18_pms5003.ino index 525012f5a..dbac02936 100644 --- a/tasmota/xsns_18_pms5003.ino +++ b/tasmota/xsns_18_pms5003.ino @@ -171,7 +171,7 @@ bool PmsReadData(void) bool PmsCommandSensor(void) { - if ((pin[GPIO_PMS5003_TX] < 99) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { + if (PinUsed(GPIO_PMS5003_TX) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { if (XdrvMailbox.payload < MIN_INTERVAL_PERIOD) { // Set Active Mode if interval is less than 60 seconds Settings.pms_wake_interval = 0; @@ -239,12 +239,12 @@ void PmsSecond(void) // Every second void PmsInit(void) { Pms.type = 0; - if (pin[GPIO_PMS5003_RX] < 99) { - PmsSerial = new TasmotaSerial(pin[GPIO_PMS5003_RX], (pin[GPIO_PMS5003_TX] < 99) ? pin[GPIO_PMS5003_TX] : -1, 1); + if (PinUsed(GPIO_PMS5003_RX)) { + PmsSerial = new TasmotaSerial(Pin(GPIO_PMS5003_RX), (PinUsed(GPIO_PMS5003_TX)) ? Pin(GPIO_PMS5003_TX) : -1, 1); if (PmsSerial->begin(9600)) { if (PmsSerial->hardwareSerial()) { ClaimSerial(); } - if (99 == pin[GPIO_PMS5003_TX]) { // setting interval not supported if TX pin not connected + if (99 == Pin(GPIO_PMS5003_TX)) { // setting interval not supported if TX pin not connected Settings.pms_wake_interval = 0; Pms.ready = 1; } diff --git a/tasmota/xsns_20_novasds.ino b/tasmota/xsns_20_novasds.ino index 0b1580dde..ffaee14bc 100644 --- a/tasmota/xsns_20_novasds.ino +++ b/tasmota/xsns_20_novasds.ino @@ -202,8 +202,8 @@ bool NovaSdsCommandSensor(void) void NovaSdsInit(void) { novasds_type = 0; - if (pin[GPIO_SDS0X1_RX] < 99 && pin[GPIO_SDS0X1_TX] < 99) { - NovaSdsSerial = new TasmotaSerial(pin[GPIO_SDS0X1_RX], pin[GPIO_SDS0X1_TX], 1); + if (PinUsed(GPIO_SDS0X1_RX) && PinUsed(GPIO_SDS0X1_TX)) { + NovaSdsSerial = new TasmotaSerial(Pin(GPIO_SDS0X1_RX), Pin(GPIO_SDS0X1_TX), 1); if (NovaSdsSerial->begin(9600)) { if (NovaSdsSerial->hardwareSerial()) { ClaimSerial(); diff --git a/tasmota/xsns_22_sr04.ino b/tasmota/xsns_22_sr04.ino index dd8d87ab1..116729f66 100644 --- a/tasmota/xsns_22_sr04.ino +++ b/tasmota/xsns_22_sr04.ino @@ -40,10 +40,10 @@ TasmotaSerial* sonar_serial = nullptr; uint8_t Sr04TModeDetect(void) { sr04_type = 0; - if (99 == pin[GPIO_SR04_ECHO]) { return sr04_type; } + if (99 == Pin(GPIO_SR04_ECHO)) { return sr04_type; } - int sr04_echo_pin = pin[GPIO_SR04_ECHO]; - int sr04_trig_pin = (pin[GPIO_SR04_TRIG] < 99) ? pin[GPIO_SR04_TRIG] : pin[GPIO_SR04_ECHO]; // if GPIO_SR04_TRIG is not configured use single PIN mode with GPIO_SR04_ECHO only + int sr04_echo_pin = Pin(GPIO_SR04_ECHO); + int sr04_trig_pin = (PinUsed(GPIO_SR04_TRIG)) ? Pin(GPIO_SR04_TRIG) : Pin(GPIO_SR04_ECHO); // if GPIO_SR04_TRIG is not configured use single PIN mode with GPIO_SR04_ECHO only sonar_serial = new TasmotaSerial(sr04_echo_pin, sr04_trig_pin, 1); if (sonar_serial->begin(9600,1)) { @@ -62,7 +62,7 @@ uint8_t Sr04TModeDetect(void) delete sonar_serial; sonar_serial = nullptr; if (-1 == sr04_trig_pin) { - sr04_trig_pin = pin[GPIO_SR04_ECHO]; // if GPIO_SR04_TRIG is not configured use single PIN mode with GPIO_SR04_ECHO only + sr04_trig_pin = Pin(GPIO_SR04_ECHO); // if GPIO_SR04_TRIG is not configured use single PIN mode with GPIO_SR04_ECHO only } sonar = new NewPing(sr04_trig_pin, sr04_echo_pin, 300); } else { @@ -193,7 +193,7 @@ bool Xsns22(uint8_t function) if (sr04_type) { switch (function) { case FUNC_INIT: - result = (pin[GPIO_SR04_ECHO]<99); + result = (PinUsed(GPIO_SR04_ECHO)); break; case FUNC_EVERY_SECOND: Sr04TReading(); diff --git a/tasmota/xsns_28_tm1638.ino b/tasmota/xsns_28_tm1638.ino index da4de6999..9df220848 100644 --- a/tasmota/xsns_28_tm1638.ino +++ b/tasmota/xsns_28_tm1638.ino @@ -144,10 +144,10 @@ uint8_t Tm1638GetButtons(void) void TmInit(void) { tm1638_type = 0; - if ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99)) { - tm1638_clock_pin = pin[GPIO_TM16CLK]; - tm1638_data_pin = pin[GPIO_TM16DIO]; - tm1638_strobe_pin = pin[GPIO_TM16STB]; + if (PinUsed(GPIO_TM16CLK) && PinUsed(GPIO_TM16DIO) && PinUsed(GPIO_TM16STB)) { + tm1638_clock_pin = Pin(GPIO_TM16CLK); + tm1638_data_pin = Pin(GPIO_TM16DIO); + tm1638_strobe_pin = Pin(GPIO_TM16STB); pinMode(tm1638_data_pin, OUTPUT); pinMode(tm1638_clock_pin, OUTPUT); From 86f801c349f28cbd6f0b6ca7f1209ff1a01ad8a1 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 27 Apr 2020 16:47:29 +0200 Subject: [PATCH 377/547] Change pin handling part 4/4 --- tasmota/xdrv_10_scripter.ino | 2 +- tasmota/xnrg_01_hlw8012.ino | 4 --- tasmota/xsns_34_hx711.ino | 6 ++-- tasmota/xsns_35_tx20.ino | 20 ++++++------ tasmota/xsns_36_mgc3130.ino | 12 ++++---- tasmota/xsns_37_rfsensor.ino | 10 +++--- tasmota/xsns_38_az7798.ino | 4 +-- tasmota/xsns_39_max31855.ino | 24 +++++++-------- tasmota/xsns_40_pn532.ino | 4 +-- tasmota/xsns_43_hre.ino | 15 +++++---- tasmota/xsns_47_max31865.ino | 12 ++++---- tasmota/xsns_51_rdm6300.ino | 4 +-- tasmota/xsns_52_ibeacon.ino | 4 +-- tasmota/xsns_53_sml.ino | 5 +-- tasmota/xsns_56_hpma.ino | 4 +-- tasmota/xsns_60_GPS.ino | 4 +-- tasmota/xsns_61_MI_NRF24.ino | 60 ++++++++++++++++++------------------ tasmota/xsns_62_MI_HM10.ino | 4 +-- tasmota/xsns_64_hrxl.ino | 7 ++--- tasmota/xsns_67_as3935.ino | 10 +++--- 20 files changed, 105 insertions(+), 110 deletions(-) diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index 4f1c7428f..614989ab4 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -1582,7 +1582,7 @@ chknext: if (!strncmp(vname,"pd[",3)) { GetNumericResult(vname+3,OPER_EQU,&fvar,0); uint8_t gpiopin=fvar; - for (uint8_t i=0;i 0; bitcount--) { - uint32_t dpin = (digitalRead(pin[GPIO_TX2X_TXD_BLACK])); + uint32_t dpin = (digitalRead(Pin(GPIO_TX2X_TXD_BLACK))); #ifdef USE_TX23_WIND_SENSOR dpin ^= 1; #endif // USE_TX23_WIND_SENSOR @@ -263,7 +263,7 @@ void TX2xStartRead(void) // Must clear this bit in the interrupt register, // it gets set even when interrupts are disabled - GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << pin[GPIO_TX2X_TXD_BLACK]); + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << Pin(GPIO_TX2X_TXD_BLACK)); } bool Tx2xAvailable(void) @@ -338,13 +338,13 @@ void Tx2xRead(void) // TX23 start transmission by pulling down TxD line for at minimum 500ms // so we pull TxD signal to low every 3 seconds tx23_stage = 0; - pinMode(pin[GPIO_TX2X_TXD_BLACK], OUTPUT); - digitalWrite(pin[GPIO_TX2X_TXD_BLACK], LOW); + pinMode(Pin(GPIO_TX2X_TXD_BLACK), OUTPUT); + digitalWrite(Pin(GPIO_TX2X_TXD_BLACK), LOW); } else if ((uptime % TX23_READ_INTERVAL)==1) { // after pulling down TxD: pull-up TxD every x+1 seconds // to trigger TX23 start transmission tx23_stage = 1; // first rising signal is invalid - pinMode(pin[GPIO_TX2X_TXD_BLACK], INPUT_PULLUP); + pinMode(Pin(GPIO_TX2X_TXD_BLACK), INPUT_PULLUP); } #endif // USE_TX23_WIND_SENSOR if (Tx2xAvailable()) { @@ -465,12 +465,12 @@ void Tx2xInit(void) #endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS #ifdef USE_TX23_WIND_SENSOR tx23_stage = 0; - pinMode(pin[GPIO_TX2X_TXD_BLACK], OUTPUT); - digitalWrite(pin[GPIO_TX2X_TXD_BLACK], LOW); + pinMode(Pin(GPIO_TX2X_TXD_BLACK), OUTPUT); + digitalWrite(Pin(GPIO_TX2X_TXD_BLACK), LOW); #else // USE_TX23_WIND_SENSOR - pinMode(pin[GPIO_TX2X_TXD_BLACK], INPUT); + pinMode(Pin(GPIO_TX2X_TXD_BLACK), INPUT); #endif // USE_TX23_WIND_SENSOR - attachInterrupt(pin[GPIO_TX2X_TXD_BLACK], TX2xStartRead, RISING); + attachInterrupt(Pin(GPIO_TX2X_TXD_BLACK), TX2xStartRead, RISING); } int32_t Tx2xNormalize(int32_t value) @@ -582,7 +582,7 @@ bool Xsns35(uint8_t function) { bool result = false; - if (pin[GPIO_TX2X_TXD_BLACK] < 99) { + if (PinUsed(GPIO_TX2X_TXD_BLACK)) { switch (function) { case FUNC_INIT: Tx2xInit(); diff --git a/tasmota/xsns_36_mgc3130.ino b/tasmota/xsns_36_mgc3130.ino index 4dd76155b..036ecf009 100644 --- a/tasmota/xsns_36_mgc3130.ino +++ b/tasmota/xsns_36_mgc3130.ino @@ -39,14 +39,11 @@ #define MGC3130_I2C_ADDR 0x42 -#define MGC3130_xfer pin[GPIO_MGC3130_XFER] -#define MGC3130_reset pin[GPIO_MGC3130_RESET] - - +uint8_t MGC3130_xfer = 0; +uint8_t MGC3130_reset = 0; bool MGC3130_type = false; char MGC3130stype[] = "MGC3130"; - #define MGC3130_SYSTEM_STATUS 0x15 #define MGC3130_REQUEST_MSG 0x06 #define MGC3130_FW_VERSION 0x83 @@ -478,6 +475,9 @@ void MGC3130_detect(void) { if (MGC3130_type || I2cActive(MGC3130_I2C_ADDR)) { return; } + MGC3130_xfer = Pin(GPIO_MGC3130_XFER); + MGC3130_reset = Pin(GPIO_MGC3130_RESET); + pinMode(MGC3130_xfer, INPUT_PULLUP); pinMode(MGC3130_reset, OUTPUT); digitalWrite(MGC3130_reset, LOW); @@ -588,7 +588,7 @@ bool Xsns36(uint8_t function) bool result = false; - if ((FUNC_INIT == function) && (pin[GPIO_MGC3130_XFER] < 99) && (pin[GPIO_MGC3130_RESET] < 99)) { + if ((FUNC_INIT == function) && PinUsed(GPIO_MGC3130_XFER) && PinUsed(GPIO_MGC3130_RESET)) { MGC3130_detect(); } else if (MGC3130_type) { diff --git a/tasmota/xsns_37_rfsensor.ino b/tasmota/xsns_37_rfsensor.ino index 87ce5c9d7..53945f2bb 100644 --- a/tasmota/xsns_37_rfsensor.ino +++ b/tasmota/xsns_37_rfsensor.ino @@ -607,9 +607,9 @@ void RfSnsInit(void) RfSnsInitAlectoV2(); #endif if (rfsns_any_sensor) { - rfsns_rf_bit = digitalPinToBitMask(pin[GPIO_RF_SENSOR]); - rfsns_rf_port = digitalPinToPort(pin[GPIO_RF_SENSOR]); - pinMode(pin[GPIO_RF_SENSOR], INPUT); + rfsns_rf_bit = digitalPinToBitMask(Pin(GPIO_RF_SENSOR)); + rfsns_rf_port = digitalPinToPort(Pin(GPIO_RF_SENSOR)); + pinMode(Pin(GPIO_RF_SENSOR), INPUT); } else { free(rfsns_raw_signal); rfsns_raw_signal = nullptr; @@ -654,14 +654,14 @@ bool Xsns37(uint8_t function) { bool result = false; - if ((pin[GPIO_RF_SENSOR] < 99) && (FUNC_INIT == function)) { + if (PinUsed(GPIO_RF_SENSOR) && (FUNC_INIT == function)) { RfSnsInit(); } else if (rfsns_raw_signal) { switch (function) { case FUNC_LOOP: if ((*portInputRegister(rfsns_rf_port) &rfsns_rf_bit) == rfsns_rf_bit) { - if (RfSnsFetchSignal(pin[GPIO_RF_SENSOR], HIGH)) { + if (RfSnsFetchSignal(Pin(GPIO_RF_SENSOR), HIGH)) { RfSnsAnalyzeRawSignal(); } } diff --git a/tasmota/xsns_38_az7798.ino b/tasmota/xsns_38_az7798.ino index b6b1dffbe..352f59389 100644 --- a/tasmota/xsns_38_az7798.ino +++ b/tasmota/xsns_38_az7798.ino @@ -267,8 +267,8 @@ void AzEverySecond(void) void AzInit(void) { az_type = 0; - if ((pin[GPIO_AZ_RXD] < 99) && (pin[GPIO_AZ_TXD] < 99)) { - AzSerial = new TasmotaSerial(pin[GPIO_AZ_RXD], pin[GPIO_AZ_TXD], 1); + if (PinUsed(GPIO_AZ_RXD) && PinUsed(GPIO_AZ_TXD)) { + AzSerial = new TasmotaSerial(Pin(GPIO_AZ_RXD), Pin(GPIO_AZ_TXD), 1); if (AzSerial->begin(9600)) { if (AzSerial->hardwareSerial()) { ClaimSerial(); } az_type = 1; diff --git a/tasmota/xsns_39_max31855.ino b/tasmota/xsns_39_max31855.ino index b4a430b54..c8cdfc7aa 100644 --- a/tasmota/xsns_39_max31855.ino +++ b/tasmota/xsns_39_max31855.ino @@ -34,13 +34,13 @@ void MAX31855_Init(void){ return; // Set GPIO modes for SW-SPI - pinMode(pin[GPIO_MAX31855CS], OUTPUT); - pinMode(pin[GPIO_MAX31855CLK], OUTPUT); - pinMode(pin[GPIO_MAX31855DO], INPUT); + pinMode(Pin(GPIO_MAX31855CS), OUTPUT); + pinMode(Pin(GPIO_MAX31855CLK), OUTPUT); + pinMode(Pin(GPIO_MAX31855DO), INPUT); // Chip not selected / Clock low - digitalWrite(pin[GPIO_MAX31855CS], HIGH); - digitalWrite(pin[GPIO_MAX31855CLK], LOW); + digitalWrite(Pin(GPIO_MAX31855CS), HIGH); + digitalWrite(Pin(GPIO_MAX31855CLK), LOW); initialized = true; } @@ -99,22 +99,22 @@ float MAX31855_GetReferenceTemperature(int32_t RawData){ int32_t MAX31855_ShiftIn(uint8_t Length){ int32_t dataIn = 0; - digitalWrite(pin[GPIO_MAX31855CS], LOW); // CS = LOW -> Start SPI communication + digitalWrite(Pin(GPIO_MAX31855CS), LOW); // CS = LOW -> Start SPI communication delayMicroseconds(1); // CS fall to output enable = max. 100ns for (uint32_t i = 0; i < Length; i++) { - digitalWrite(pin[GPIO_MAX31855CLK], LOW); + digitalWrite(Pin(GPIO_MAX31855CLK), LOW); delayMicroseconds(1); // CLK pulse width low = min. 100ns / CLK fall to output valid = max. 40ns dataIn <<= 1; - if(digitalRead(pin[GPIO_MAX31855DO])) + if(digitalRead(Pin(GPIO_MAX31855DO))) dataIn |= 1; - digitalWrite(pin[GPIO_MAX31855CLK], HIGH); + digitalWrite(Pin(GPIO_MAX31855CLK), HIGH); delayMicroseconds(1); // CLK pulse width high = min. 100ns } - digitalWrite(pin[GPIO_MAX31855CS], HIGH); // CS = HIGH -> End SPI communication - digitalWrite(pin[GPIO_MAX31855CLK], LOW); + digitalWrite(Pin(GPIO_MAX31855CS), HIGH); // CS = HIGH -> End SPI communication + digitalWrite(Pin(GPIO_MAX31855CLK), LOW); return dataIn; } @@ -151,7 +151,7 @@ void MAX31855_Show(bool Json){ bool Xsns39(uint8_t function) { bool result = false; - if((pin[GPIO_MAX31855CS] < 99) && (pin[GPIO_MAX31855CLK] < 99) && (pin[GPIO_MAX31855DO] < 99)){ + if(PinUsed(GPIO_MAX31855CS) && PinUsed(GPIO_MAX31855CLK) && PinUsed(GPIO_MAX31855DO)){ switch (function) { case FUNC_INIT: diff --git a/tasmota/xsns_40_pn532.ino b/tasmota/xsns_40_pn532.ino index b241eb320..15257abef 100644 --- a/tasmota/xsns_40_pn532.ino +++ b/tasmota/xsns_40_pn532.ino @@ -66,8 +66,8 @@ uint8_t pn532_newdata_len = 0; void PN532_Init(void) { - if ((pin[GPIO_PN532_RXD] < 99) && (pin[GPIO_PN532_TXD] < 99)) { - PN532_Serial = new TasmotaSerial(pin[GPIO_PN532_RXD], pin[GPIO_PN532_TXD], 1); + if (PinUsed(GPIO_PN532_RXD9) && PinUsed(GPIO_PN532_TXD)) { + PN532_Serial = new TasmotaSerial(Pin(GPIO_PN532_RXD), Pin(GPIO_PN532_TXD), 1); if (PN532_Serial->begin(115200)) { if (PN532_Serial->hardwareSerial()) { ClaimSerial(); } PN532_wakeup(); diff --git a/tasmota/xsns_43_hre.ino b/tasmota/xsns_43_hre.ino index fa03d0301..19fffe957 100644 --- a/tasmota/xsns_43_hre.ino +++ b/tasmota/xsns_43_hre.ino @@ -71,10 +71,10 @@ bool hre_good = false; // The settling times here were determined using a single unit hooked to a scope int hreReadBit() { - digitalWrite(pin[GPIO_HRE_CLOCK], HIGH); + digitalWrite(Pin(GPIO_HRE_CLOCK), HIGH); delay(1); - int bit = digitalRead(pin[GPIO_HRE_DATA]); - digitalWrite(pin[GPIO_HRE_CLOCK], LOW); + int bit = digitalRead(Pin(GPIO_HRE_DATA)); + digitalWrite(Pin(GPIO_HRE_CLOCK), LOW); delay(1); return bit; } @@ -110,12 +110,12 @@ void hreInit(void) hre_read_errors = 0; hre_good = false; - pinMode(pin[GPIO_HRE_CLOCK], OUTPUT); - pinMode(pin[GPIO_HRE_DATA], INPUT); + pinMode(Pin(GPIO_HRE_CLOCK), OUTPUT); + pinMode(Pin(GPIO_HRE_DATA), INPUT); // Note that the level shifter inverts this line and we want to leave it // high when not being read. - digitalWrite(pin[GPIO_HRE_CLOCK], LOW); + digitalWrite(Pin(GPIO_HRE_CLOCK), LOW); hre_state = hre_sync; } @@ -260,8 +260,7 @@ void hreShow(boolean json) bool Xsns43(byte function) { // If we don't have pins assigned give up quickly. - if (pin[GPIO_HRE_CLOCK] >= 99 || pin[GPIO_HRE_DATA] >= 99) - return false; + if (!PinUsed(GPIO_HRE_CLOCK) || !PinUsed(GPIO_HRE_DATA)) { return false; } switch (function) { diff --git a/tasmota/xsns_47_max31865.ino b/tasmota/xsns_47_max31865.ino index 3b8a11706..e29054392 100644 --- a/tasmota/xsns_47_max31865.ino +++ b/tasmota/xsns_47_max31865.ino @@ -51,10 +51,10 @@ void MAX31865_Init(void){ return; max31865.setPins( - pin[GPIO_SSPI_CS], - pin[GPIO_SSPI_MOSI], - pin[GPIO_SSPI_MISO], - pin[GPIO_SSPI_SCLK] + Pin(GPIO_SSPI_CS), + Pin(GPIO_SSPI_MOSI), + Pin(GPIO_SSPI_MISO), + Pin(GPIO_SSPI_SCLK) ); if(max31865.begin(PTD_WIRES)) @@ -110,8 +110,8 @@ void MAX31865_Show(bool Json){ bool Xsns47(uint8_t function) { bool result = false; - if((pin[GPIO_SSPI_MISO] < 99) && (pin[GPIO_SSPI_MOSI] < 99) && - (pin[GPIO_SSPI_SCLK] < 99) && (pin[GPIO_SSPI_CS] < 99)) { + if (PinUsed(GPIO_SSPI_MISO) && PinUsed(GPIO_SSPI_MOSI) && + PinUsed(GPIO_SSPI_SCLK) && PinUsed(GPIO_SSPI_CS)) { switch (function) { case FUNC_INIT: diff --git a/tasmota/xsns_51_rdm6300.ino b/tasmota/xsns_51_rdm6300.ino index 7fd8bc5b5..de2714cb2 100644 --- a/tasmota/xsns_51_rdm6300.ino +++ b/tasmota/xsns_51_rdm6300.ino @@ -36,8 +36,8 @@ uint8_t rdm_blcnt; TasmotaSerial *RDM6300_Serial = nullptr; void RDM6300_Init() { - if (pin[GPIO_RDM6300_RX] < 99) { - RDM6300_Serial = new TasmotaSerial(pin[GPIO_RDM6300_RX],-1,1); + if (PinUsed(GPIO_RDM6300_RX)) { + RDM6300_Serial = new TasmotaSerial(Pin(GPIO_RDM6300_RX),-1,1); if (RDM6300_Serial->begin(RDM6300_BAUDRATE)) { if (RDM6300_Serial->hardwareSerial()) { ClaimSerial(); diff --git a/tasmota/xsns_52_ibeacon.ino b/tasmota/xsns_52_ibeacon.ino index 97130df2a..88b040e76 100644 --- a/tasmota/xsns_52_ibeacon.ino +++ b/tasmota/xsns_52_ibeacon.ino @@ -95,8 +95,8 @@ void IBEACON_Init() { hm17_found=0; // actually doesnt work reliably with software serial - if ((pin[GPIO_IBEACON_RX] < 99) && (pin[GPIO_IBEACON_TX] < 99)) { - IBEACON_Serial = new TasmotaSerial(pin[GPIO_IBEACON_RX], pin[GPIO_IBEACON_TX],1); + if (PinUsed(GPIO_IBEACON_RX) && PinUsed(GPIO_IBEACON_TX)) { + IBEACON_Serial = new TasmotaSerial(Pin(GPIO_IBEACON_RX), Pin(GPIO_IBEACON_TX),1); if (IBEACON_Serial->begin(HM17_BAUDRATE)) { if (IBEACON_Serial->hardwareSerial()) { ClaimSerial(); diff --git a/tasmota/xsns_53_sml.ino b/tasmota/xsns_53_sml.ino index 71b205e6e..c61bfa20d 100755 --- a/tasmota/xsns_53_sml.ino +++ b/tasmota/xsns_53_sml.ino @@ -1831,8 +1831,9 @@ uint8_t *script_meter; #endif bool Gpio_used(uint8_t gpiopin) { - for (uint16_t i=0;ibegin(9600)) { diff --git a/tasmota/xsns_60_GPS.ino b/tasmota/xsns_60_GPS.ino index b789222fe..e7094887b 100644 --- a/tasmota/xsns_60_GPS.ino +++ b/tasmota/xsns_60_GPS.ino @@ -354,8 +354,8 @@ void UBXTriggerTele(void) void UBXDetect(void) { UBX.mode.init = 0; - if ((pin[GPIO_GPS_RX] < 99) && (pin[GPIO_GPS_TX] < 99)) { - UBXSerial = new TasmotaSerial(pin[GPIO_GPS_RX], pin[GPIO_GPS_TX], 1, 0, UBX_SERIAL_BUFFER_SIZE); // 64 byte buffer is NOT enough + if (PinUsed(GPIO_GPS_RX) && PinUsed(GPIO_GPS_TX)) { + UBXSerial = new TasmotaSerial(Pin(GPIO_GPS_RX), Pin(GPIO_GPS_TX), 1, 0, UBX_SERIAL_BUFFER_SIZE); // 64 byte buffer is NOT enough if (UBXSerial->begin(9600)) { DEBUG_SENSOR_LOG(PSTR("UBX: started serial")); if (UBXSerial->hardwareSerial()) { diff --git a/tasmota/xsns_61_MI_NRF24.ino b/tasmota/xsns_61_MI_NRF24.ino index d21f9e7e1..5dd82f3a5 100644 --- a/tasmota/xsns_61_MI_NRF24.ino +++ b/tasmota/xsns_61_MI_NRF24.ino @@ -27,10 +27,10 @@ 0.9.4.0 20200304 integrate - sensor types can be ignored (default for LYWSD03), add CGD1 (Alarm clock), correct PDU-types for LYWSD02 --- - 0.9.3.0 20200222 integrate - use now the correct id-word instead of MAC-OUI, + 0.9.3.0 20200222 integrate - use now the correct id-word instead of MAC-OUI, add CGG1 --- - 0.9.2.0 20200212 integrate - "backports" from MI-HM10, change reading pattern, + 0.9.2.0 20200212 integrate - "backports" from MI-HM10, change reading pattern, add missing PDU-types, renaming driver --- 0.9.1.0 20200117 integrate - Added support for the LYWSD02 @@ -176,7 +176,7 @@ struct { uint8_t packetMode; // 0 - normal BLE-advertisements, 1 - 6 "special" sensor packets uint8_t perPage = 4; uint8_t firstUsedPacketMode = 1; - + FIFO_t buffer; struct { @@ -232,16 +232,16 @@ static union{ /********************************************************************************************/ /** - * @brief - * + * @brief + * * @param _mode Packet mode 0-6 * @return true If no error occured - * @return false If NRF24L01 is not connected + * @return false If NRF24L01 is not connected */ bool MINRFinitBLE(uint8_t _mode) { if (MINRF.timer%1000 == 0){ // only re-init every 20 seconds - NRF24radio.begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_DC]); + NRF24radio.begin(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_DC)); NRF24radio.setAutoAck(false); NRF24radio.setDataRate(RF24_1MBPS); NRF24radio.disableCRC(); @@ -264,7 +264,7 @@ bool MINRFinitBLE(uint8_t _mode) /** * @brief cycle through the channels 37-39, skip ignored channel - * + * */ void MINRFhopChannel() { @@ -405,19 +405,19 @@ bool MINRFhandleBeacon(scan_entry_t * entry, uint32_t offset); /** * @brief handle a generic BLE-packet in the scan process - * + * */ void MINRFhandleScan(void){ if(MINRFscanResult.size()>20 || MINRF.stopScan) { MINRF.activeScan=false; MINRFcomputefirstUsedPacketMode(); uint32_t i = 0; // pass counter as reference to lambda - MINRFscanResult.erase(std::remove_if(MINRFscanResult.begin(), + MINRFscanResult.erase(std::remove_if(MINRFscanResult.begin(), MINRFscanResult.end(), [&i](scan_entry_t e) { if(e.showedUp>2) AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: Beacon %02u: %02X%02X%02X%02X%02X%02X Cid: %04X Svc: %04X UUID: %04X"),i,e.mac[0],e.mac[1],e.mac[2],e.mac[3],e.mac[4],e.mac[5],e.cid,e.svc,e.uuid); i++; - return ((e.showedUp < 3)); + return ((e.showedUp < 3)); }), MINRFscanResult.end()); MINRF.stopScan=false; @@ -447,7 +447,7 @@ void MINRFhandleScan(void){ /** * @brief start beacon mode, can co-exist with Mijia-sniffing - * + * * @param entry number of entry in scan list */ void MINRFstartBeacon(uint16_t entry){ @@ -459,7 +459,7 @@ void MINRFstartBeacon(uint16_t entry){ /** * @brief semi-generic BLE-ADV-parser - * + * * @param entry Entry of scan list * @param offset Depends on the reading mode: 0->regular BLE-ADV, 6->"cutted" BLE-ADV with MAC as PDU * @return true - when name, cid, uuid or svc is found with any value @@ -536,7 +536,7 @@ bool MINRFhandleBeacon(scan_entry_t * entry, uint32_t offset){ /** * @brief increase beacon timer every second and process the result - * + * */ void MINRFbeaconCounter(void){ if(MINRF.beacon.active) { @@ -550,7 +550,7 @@ void MINRFbeaconCounter(void){ /** * @brief compute "PDU" from MAC for each possible channel and store it globally - * + * */ void MINRFcomputeBeaconPDU(void){ for (uint32_t i = 0; i<3; i++){ @@ -570,7 +570,7 @@ void MINRFcomputeBeaconPDU(void){ /** * @brief reverse 6-byte-array, hard-coded size of 6 - * + * * @param _mac pass an uint_t[6] */ void MINRFreverseMAC(uint8_t _mac[]){ @@ -582,8 +582,8 @@ void MINRFreverseMAC(uint8_t _mac[]){ } /** - * @brief - * + * @brief + * * @param _string input string in format: AABBCCDDEEFF (upper case!) * @param _mac target byte array with fixed size of 6 */ @@ -594,7 +594,7 @@ void MINRFMACStringToBytes(char* _string, uint8_t _mac[]) { //uppercase uint8_t value = 0; if(c >= '0' && c <= '9') value = (c - '0'); - else if (c >= 'A' && c <= 'F') + else if (c >= 'A' && c <= 'F') value = (10 + (c - 'A')); _mac[(index/2)] += value << (((index + 1) % 2) * 4); index++; @@ -604,7 +604,7 @@ void MINRFMACStringToBytes(char* _string, uint8_t _mac[]) { //uppercase /** * @brief helper function, to avoid to start with an ignored sensor type - * + * */ void MINRFcomputefirstUsedPacketMode(void){ for (uint32_t i = 0; ilux=MINRF.buffer.miBeacon.lux & 0x00ffffff; DEBUG_SENSOR_LOG(PSTR("Mode 7: U24: %u Lux"), MINRF.buffer.miBeacon.lux & 0x00ffffff); - break; + break; case 0x08: _tempFloat =(float)MINRF.buffer.miBeacon.moist; if(_tempFloat<100){ @@ -881,7 +881,7 @@ void MINRFhandleCGD1Packet(void){ // no MiBeacon \*********************************************************************************************/ void MINRF_EVERY_50_MSECOND() { // Every 50mseconds - + if(MINRF.timer>6000){ // happens every 6000/20 = 300 seconds DEBUG_SENSOR_LOG(PSTR("MINRF: check for FAKE sensors")); MINRFpurgeFakeSensors(); @@ -890,7 +890,7 @@ void MINRF_EVERY_50_MSECOND() { // Every 50mseconds MINRF.timer++; if (!MINRFreceivePacket()){ - // DEBUG_SENSOR_LOG(PSTR("MINRF: nothing received")); + // DEBUG_SENSOR_LOG(PSTR("MINRF: nothing received")); } else { @@ -1057,7 +1057,7 @@ void MINRFShow(bool json) } ResponseAppend_P(PSTR(",\"%s-%02x%02x%02x\":{"),kMINRFSlaveType[MIBLEsensors[i].type-1],MIBLEsensors[i].serial[3],MIBLEsensors[i].serial[4],MIBLEsensors[i].serial[5]); if (MIBLEsensors[i].type==FLORA && !isnan(MIBLEsensors[i].temp)){ - char stemp[FLOATSZ]; + char stemp[FLOATSZ]; dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, stemp); ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), stemp); @@ -1074,7 +1074,7 @@ void MINRFShow(bool json) } ResponseJsonEnd(); } - if (MIBLEsensors[i].type>FLORA){ + if (MIBLEsensors[i].type>FLORA){ if(!isnan(MIBLEsensors[i].temp) && !isnan(MIBLEsensors[i].hum)){ ResponseAppendTHD(MIBLEsensors[i].temp,MIBLEsensors[i].hum); } @@ -1112,7 +1112,7 @@ void MINRFShow(bool json) continue; } WSContentSend_PD(HTTP_MINRF_HL); - WSContentSend_PD(HTTP_MINRF_MAC, kMINRFSlaveType[MIBLEsensors[i].type-1], D_MAC_ADDRESS, MIBLEsensors[i].serial[0], MIBLEsensors[i].serial[1],MIBLEsensors[i].serial[2],MIBLEsensors[i].serial[3],MIBLEsensors[i].serial[4],MIBLEsensors[i].serial[5]); + WSContentSend_PD(HTTP_MINRF_MAC, kMINRFSlaveType[MIBLEsensors[i].type-1], D_MAC_ADDRESS, MIBLEsensors[i].serial[0], MIBLEsensors[i].serial[1],MIBLEsensors[i].serial[2],MIBLEsensors[i].serial[3],MIBLEsensors[i].serial[4],MIBLEsensors[i].serial[5]); if (MIBLEsensors[i].type==FLORA){ if(!isnan(MIBLEsensors[i].temp)){ char temperature[FLOATSZ]; @@ -1139,7 +1139,7 @@ void MINRFShow(bool json) if(MINRF.beacon.active){ WSContentSend_PD(HTTP_MINRF_HL); WSContentSend_PD(HTTP_MINRF_HL); - WSContentSend_PD(HTTP_MINRF_MAC, F("Beacon"), D_MAC_ADDRESS, MINRF.beacon.mac[0], MINRF.beacon.mac[1],MINRF.beacon.mac[2],MINRF.beacon.mac[3],MINRF.beacon.mac[4],MINRF.beacon.mac[5]); + WSContentSend_PD(HTTP_MINRF_MAC, F("Beacon"), D_MAC_ADDRESS, MINRF.beacon.mac[0], MINRF.beacon.mac[1],MINRF.beacon.mac[2],MINRF.beacon.mac[3],MINRF.beacon.mac[4],MINRF.beacon.mac[5]); WSContentSend_PD(PSTR("{s}Beacon Time{m}%u seconds{e}"),MINRF.beacon.time); } if(counter>3) { diff --git a/tasmota/xsns_62_MI_HM10.ino b/tasmota/xsns_62_MI_HM10.ino index 01fa338e8..6655a0c87 100644 --- a/tasmota/xsns_62_MI_HM10.ino +++ b/tasmota/xsns_62_MI_HM10.ino @@ -403,7 +403,7 @@ uint32_t MIBLEgetSensorSlot(uint8_t (&_serial)[6], uint16_t _type){ void HM10SerialInit(void) { HM10.mode.init = false; HM10.serialSpeed = HM10_BAUDRATE; - HM10Serial = new TasmotaSerial(pin[GPIO_HM10_RX], pin[GPIO_HM10_TX], 1, 0, HM10_MAX_RX_BUF); + HM10Serial = new TasmotaSerial(Pin(GPIO_HM10_RX), Pin(GPIO_HM10_TX), 1, 0, HM10_MAX_RX_BUF); if (HM10Serial->begin(HM10.serialSpeed)) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s start serial communication fixed to 115200 baud"),D_CMND_HM10); if (HM10Serial->hardwareSerial()) { @@ -1257,7 +1257,7 @@ bool Xsns62(uint8_t function) { bool result = false; - if ((pin[GPIO_HM10_RX] < 99) && (pin[GPIO_HM10_TX] < 99)) { + if (PinUsed(GPIO_HM10_RX) && PinUsed(GPIO_HM10_TX)) { switch (function) { case FUNC_INIT: HM10SerialInit(); // init and start communication diff --git a/tasmota/xsns_64_hrxl.ino b/tasmota/xsns_64_hrxl.ino index a82134c94..389197e1a 100644 --- a/tasmota/xsns_64_hrxl.ino +++ b/tasmota/xsns_64_hrxl.ino @@ -41,9 +41,9 @@ bool hrxl_found = false; void HRXLInit(void) { hrxl_found = false; - if ((pin[GPIO_HRXL_RX] < 99)) + if (PinUsed(GPIO_HRXL_RX)) { - HRXLSerial = new TasmotaSerial(pin[GPIO_HRXL_RX], -1, 1); + HRXLSerial = new TasmotaSerial(Pin(GPIO_HRXL_RX), -1, 1); if (HRXLSerial->begin(9600)) { if (HRXLSerial->hardwareSerial()) @@ -100,8 +100,7 @@ void HRXLShow(bool json) bool Xsns64(uint8_t function) { - if (pin[GPIO_HRXL_RX] >= 99) - return false; + if (!PinUsed(GPIO_HRXL_RX)) { return false; } switch (function) { diff --git a/tasmota/xsns_67_as3935.ino b/tasmota/xsns_67_as3935.ino index 61042228f..27b97f9a2 100644 --- a/tasmota/xsns_67_as3935.ino +++ b/tasmota/xsns_67_as3935.ino @@ -401,9 +401,9 @@ void AS3935SetWdth(uint8_t wdth) { } bool AS3935AutoTune(){ - detachInterrupt(pin[GPIO_AS3935]); - bool result = AS3935AutoTuneCaps(pin[GPIO_AS3935]); - attachInterrupt(digitalPinToInterrupt(pin[GPIO_AS3935]), AS3935Isr, RISING); + detachInterrupt(Pin(GPIO_AS3935)); + bool result = AS3935AutoTuneCaps(Pin(GPIO_AS3935)); + attachInterrupt(digitalPinToInterrupt(Pin(GPIO_AS3935)), AS3935Isr, RISING); return result; } @@ -497,8 +497,8 @@ void AS3935Detect(void) { if (AS3935init()) { I2cSetActiveFound(AS3935_ADDR, D_NAME_AS3935); - pinMode(pin[GPIO_AS3935], INPUT); - attachInterrupt(digitalPinToInterrupt(pin[GPIO_AS3935]), AS3935Isr, RISING); + pinMode(Pin(GPIO_AS3935), INPUT); + attachInterrupt(digitalPinToInterrupt(Pin(GPIO_AS3935)), AS3935Isr, RISING); AS3935Setup(); as3935_active = 1; } From 1dda2ac6633e0118ed75927bcfb249e1bce21a95 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 27 Apr 2020 16:55:56 +0200 Subject: [PATCH 378/547] Fix pin handling part 4/4 --- tasmota/xsns_40_pn532.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xsns_40_pn532.ino b/tasmota/xsns_40_pn532.ino index 15257abef..8218d0b35 100644 --- a/tasmota/xsns_40_pn532.ino +++ b/tasmota/xsns_40_pn532.ino @@ -66,7 +66,7 @@ uint8_t pn532_newdata_len = 0; void PN532_Init(void) { - if (PinUsed(GPIO_PN532_RXD9) && PinUsed(GPIO_PN532_TXD)) { + if (PinUsed(GPIO_PN532_RXD) && PinUsed(GPIO_PN532_TXD)) { PN532_Serial = new TasmotaSerial(Pin(GPIO_PN532_RXD), Pin(GPIO_PN532_TXD), 1); if (PN532_Serial->begin(115200)) { if (PN532_Serial->hardwareSerial()) { ClaimSerial(); } From ae4ec4325d05c1c926f79395cb91c3b625404d31 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 27 Apr 2020 17:16:52 +0200 Subject: [PATCH 379/547] Change pin handling part 5 --- tasmota/support_command.ino | 6 +++--- tasmota/support_tasmota.ino | 6 +++--- tasmota/xdrv_36_keeloq.ino | 2 +- tasmota/xnrg_01_hlw8012.ino | 6 +++--- tasmota/xsns_18_pms5003.ino | 2 +- tasmota/xsns_22_sr04.ino | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index cc8bb92ae..01c227f36 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -1694,7 +1694,7 @@ void CmndAltitude(void) void CmndLedPower(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_LEDS)) { - if (99 == Pin(GPIO_LEDLNK)) { XdrvMailbox.index = 1; } + if (!PinUsed(GPIO_LEDLNK)) { XdrvMailbox.index = 1; } if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { Settings.ledstate &= 8; // Disable power control uint32_t mask = 1 << (XdrvMailbox.index -1); // Led to control @@ -1713,14 +1713,14 @@ void CmndLedPower(void) break; } blinks = 0; - if (99 == Pin(GPIO_LEDLNK)) { + if (!PinUsed(GPIO_LEDLNK)) { SetLedPower(Settings.ledstate &8); } else { SetLedPowerIdx(XdrvMailbox.index -1, (led_power & mask)); } } bool state = bitRead(led_power, XdrvMailbox.index -1); - if (99 == Pin(GPIO_LEDLNK)) { + if (!PinUsed(GPIO_LEDLNK)) { state = bitRead(Settings.ledstate, 3); } ResponseCmndIdxChar(GetStateText(state)); diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 1f2ad385c..a6a293ae3 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -338,7 +338,7 @@ void SetPowerOnState(void) 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 (!PinUsed(GPIO_LEDLNK) && (0 == led)) { // Legacy - LED1 is link led only if LED2 is present if (PinUsed(GPIO_LED1, 1)) { led = 1; } @@ -362,7 +362,7 @@ void SetLedPowerIdx(uint32_t led, uint32_t state) void SetLedPower(uint32_t state) { - if (99 == Pin(GPIO_LEDLNK)) { // Legacy - Only use LED1 and/or LED2 + if (!PinUsed(GPIO_LEDLNK)) { // Legacy - Only use LED1 and/or LED2 SetLedPowerIdx(0, state); } else { power_t mask = 1; @@ -1534,7 +1534,7 @@ void GpioInit(void) for (uint32_t i = 0; i < MAX_LEDS; i++) { if (PinUsed(GPIO_LED1, i)) { #ifdef USE_ARILUX_RF - if ((3 == i) && (leds_present < 2) && (99 == Pin(GPIO_ARIRFSEL))) { + if ((3 == i) && (leds_present < 2) && !PinUsed(GPIO_ARIRFSEL)) { SetPin(Pin(GPIO_LED4), GPIO_ARIRFSEL); // Legacy support where LED4 was Arilux RF enable SetPin(99, GPIO_LED4); } else { diff --git a/tasmota/xdrv_36_keeloq.ino b/tasmota/xdrv_36_keeloq.ino index 75e3e6167..c271cf3f7 100644 --- a/tasmota/xdrv_36_keeloq.ino +++ b/tasmota/xdrv_36_keeloq.ino @@ -266,7 +266,7 @@ void KeeloqInit() \*********************************************************************************************/ bool Xdrv36(uint8_t function) { - if ((99 == Pin(GPIO_CC1101_GDO0)) || (99 == Pin(GPIO_CC1101_GDO2))) { return false; } + if (!PinUsed(GPIO_CC1101_GDO0) || !PinUsed(GPIO_CC1101_GDO2)) { return false; } bool result = false; diff --git a/tasmota/xnrg_01_hlw8012.ino b/tasmota/xnrg_01_hlw8012.ino index a1e67ecfc..12a10abb6 100644 --- a/tasmota/xnrg_01_hlw8012.ino +++ b/tasmota/xnrg_01_hlw8012.ino @@ -254,7 +254,7 @@ void HlwDrvInit(void) Hlw.model_type = 1; // HJL-01/BL0937 } - if (PinUsed(GPIO_HLW_CF)) { // HLW8012 or HJL-01 based device Power monitor + if (PinUsed(GPIO_HLW_CF)) { // HLW8012 or HJL-01 based device Power monitor Hlw.ui_flag = true; // Voltage on high if (PinUsed(GPIO_NRG_SEL_INV)) { @@ -263,8 +263,8 @@ void HlwDrvInit(void) Hlw.ui_flag = false; // Voltage on low } - if (PinUsed(GPIO_NRG_CF1)) { // Voltage and/or Current monitor - if (99 == Pin(GPIO_NRG_SEL)) { // Voltage and/or Current selector + if (PinUsed(GPIO_NRG_CF1)) { // Voltage and/or Current monitor + if (!PinUsed(GPIO_NRG_SEL)) { // Voltage and/or Current selector Energy.current_available = false; // Assume Voltage } } else { diff --git a/tasmota/xsns_18_pms5003.ino b/tasmota/xsns_18_pms5003.ino index dbac02936..d54527ddc 100644 --- a/tasmota/xsns_18_pms5003.ino +++ b/tasmota/xsns_18_pms5003.ino @@ -244,7 +244,7 @@ void PmsInit(void) if (PmsSerial->begin(9600)) { if (PmsSerial->hardwareSerial()) { ClaimSerial(); } - if (99 == Pin(GPIO_PMS5003_TX)) { // setting interval not supported if TX pin not connected + if (!PinUsed(GPIO_PMS5003_TX)) { // setting interval not supported if TX pin not connected Settings.pms_wake_interval = 0; Pms.ready = 1; } diff --git a/tasmota/xsns_22_sr04.ino b/tasmota/xsns_22_sr04.ino index 116729f66..187a67c3f 100644 --- a/tasmota/xsns_22_sr04.ino +++ b/tasmota/xsns_22_sr04.ino @@ -40,7 +40,7 @@ TasmotaSerial* sonar_serial = nullptr; uint8_t Sr04TModeDetect(void) { sr04_type = 0; - if (99 == Pin(GPIO_SR04_ECHO)) { return sr04_type; } + if (!PinUsed(GPIO_SR04_ECHO)) { return sr04_type; } int sr04_echo_pin = Pin(GPIO_SR04_ECHO); int sr04_trig_pin = (PinUsed(GPIO_SR04_TRIG)) ? Pin(GPIO_SR04_TRIG) : Pin(GPIO_SR04_ECHO); // if GPIO_SR04_TRIG is not configured use single PIN mode with GPIO_SR04_ECHO only From 3fef91d6ca7b666882bd81c69734755d099eec29 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 27 Apr 2020 17:28:05 +0200 Subject: [PATCH 380/547] Change pin handling part 6 --- tasmota/support.ino | 6 ++++++ tasmota/support_tasmota.ino | 4 +--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tasmota/support.ino b/tasmota/support.ino index 10e34ada7..0986a007e 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -1109,6 +1109,12 @@ void SetPin(uint32_t lpin, uint32_t gpio) { */ } +void InitAllPins(void) { + for (uint32_t i = 0; i < ARRAY_SIZE(pin); i++) { + SetPin(99, i); + } +} + void DigitalWrite(uint32_t gpio_pin, uint32_t index, uint32_t state) { if (PinUsed(gpio_pin, index)) { diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index a6a293ae3..3db5ea2f7 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -1383,9 +1383,7 @@ void GpioInit(void) my_adc0 = template_adc0; // Force Template override } - for (uint32_t i = 0; i < GPIO_MAX; i++) { - SetPin(99, i); - } + InitAllPins(); for (uint32_t i = 0; i < ARRAY_SIZE(my_module.io); i++) { uint32_t mpin = ValidPin(i, my_module.io[i]); From 4cd21644d69dfd53f4094a75bbce15679bf45282 Mon Sep 17 00:00:00 2001 From: kugelkopf123 <45996965+kugelkopf123@users.noreply.github.com> Date: Mon, 27 Apr 2020 20:44:26 +0200 Subject: [PATCH 381/547] Update xsns_02_analog.ino --- tasmota/xsns_02_analog.ino | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tasmota/xsns_02_analog.ino b/tasmota/xsns_02_analog.ino index 038da9fa5..41be46ff8 100644 --- a/tasmota/xsns_02_analog.ino +++ b/tasmota/xsns_02_analog.ino @@ -166,6 +166,8 @@ void AdcGetCurrentPower(uint8_t factor) uint16_t analog = 0; uint16_t analog_min = 1023; uint16_t analog_max = 0; + + if(Settings.adc_param1==0){ for (uint32_t i = 0; i < samples; i++) { analog = analogRead(A0); if (analog < analog_min) { @@ -176,8 +178,19 @@ void AdcGetCurrentPower(uint8_t factor) } delay(1); } - Adc.current = (float)(analog_max-analog_min) * ((float)(Settings.adc_param2) / 100000); + } + else{ + + analog = AdcRead(5); + if(analog>Settings.adc_param1){ + Adc.current = ((float)(analog) - (float)Settings.adc_param1) * ((float)(Settings.adc_param2) / 100000); + } + else{ + Adc.current = 0; + } + } + float power = Adc.current * (float)(Settings.adc_param3) / 10; uint32_t current_millis = millis(); Adc.energy = Adc.energy + ((power * (current_millis - Adc.previous_millis)) / 3600000000); @@ -357,7 +370,7 @@ void CmndAdcParam(void) Settings.adc_param3 = (int)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)) * 10000); } if (ADC0_CT_POWER == XdrvMailbox.payload) { - if ((Settings.adc_param1 & CT_FLAG_ENERGY_RESET) > 0) { + if ((Settings.adc_param1 == 1 & CT_FLAG_ENERGY_RESET) > 0) { Adc.energy = 0; Settings.adc_param1 ^= CT_FLAG_ENERGY_RESET; // Cancel energy reset flag } @@ -435,4 +448,4 @@ bool Xsns02(uint8_t function) return result; } -#endif // USE_ADC_VCC +#endif // USE_ADC_VCC \ No newline at end of file From b6e62bf71583124994e91d0c1ebb0bb746d14b6c Mon Sep 17 00:00:00 2001 From: Paul C Diem Date: Mon, 27 Apr 2020 22:26:32 -0500 Subject: [PATCH 382/547] Only advance to next palette color when fade is down --- tasmota/xdrv_04_light.ino | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index 9aaa9c9c0..971c0b2b2 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -1702,17 +1702,19 @@ void LightCycleColor(int8_t direction) #ifdef USE_LIGHT_PALETTE if (Light.palette_count) { - if (0 == direction) { - Light.wheel = random(Light.palette_count); - } - else { - Light.wheel += direction; - if (Light.wheel >= Light.palette_count) { - Light.wheel = 0; - if (direction < 0) Light.wheel = Light.palette_count - 1; + if (!Light.fade_running) { + if (0 == direction) { + Light.wheel = random(Light.palette_count); } + else { + Light.wheel += direction; + if (Light.wheel >= Light.palette_count) { + Light.wheel = 0; + if (direction < 0) Light.wheel = Light.palette_count - 1; + } + } + LightSetPaletteEntry(); } - LightSetPaletteEntry(); return; } #endif // USE_LIGHT_PALETTE From 966d3522ad830920a26dfa3bfcfe9db279d0b4d9 Mon Sep 17 00:00:00 2001 From: Paul C Diem Date: Tue, 28 Apr 2020 00:18:40 -0500 Subject: [PATCH 383/547] Rework DGR channel update --- tasmota/xdrv_04_light.ino | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index 971c0b2b2..345951696 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -2234,27 +2234,21 @@ void calcGammaBulbs(uint16_t cur_col_10[5]) { } #ifdef USE_DEVICE_GROUPS -void LightSendDeviceGroupStatus(bool force) +void LightSendDeviceGroupStatus(bool status) { - static uint8_t last_channels[LST_MAX]; - static uint8_t channels_sequence = 0; static uint8_t last_bri; - uint8_t bri = light_state.getBri(); - bool send_bri_update = (force || bri != last_bri); - + bool send_bri_update = (status || bri != last_bri); if (Light.subtype > LST_SINGLE && !Light.devgrp_no_channels_out) { - uint8_t channels[LST_MAX + 1]; - light_state.getChannels(channels); - if (force || memcmp(last_channels, channels, LST_MAX) -#ifdef USE_LIGHT_PALETTE - || (Settings.light_scheme && Light.palette_count) -#endif // USE_LIGHT_PALETTE - ) { - memcpy(last_channels, channels, LST_MAX); - channels[LST_MAX] = ++channels_sequence; - SendLocalDeviceGroupMessage((send_bri_update ? DGR_MSGTYP_PARTIAL_UPDATE : DGR_MSGTYP_UPDATE), DGR_ITEM_LIGHT_CHANNELS, channels); + static uint8_t channels[LST_MAX + 1] = { 0, 0, 0, 0, 0, 0 }; + if (status) { + light_state.getChannels(channels); } + else { + memcpy(channels, Light.new_color, LST_MAX); + channels[LST_MAX]++; + } + SendLocalDeviceGroupMessage((send_bri_update ? DGR_MSGTYP_PARTIAL_UPDATE : DGR_MSGTYP_UPDATE), DGR_ITEM_LIGHT_CHANNELS, channels); } if (send_bri_update) { last_bri = bri; From 4536746d032b35f80cb73649764f65cbf311ae2b Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 28 Apr 2020 11:09:56 +0200 Subject: [PATCH 384/547] Update Changelog and Release notes --- RELEASENOTES.md | 3 ++- tasmota/CHANGELOG.md | 1 + tasmota/settings.h | 2 +- tasmota/xsns_02_analog.ino | 31 +++++++++++++++---------------- tools/decode-status.py | 6 +++--- 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d7e8d2bf1..62a21c61f 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -61,7 +61,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Change light scheme 2,3,4 cycle time speed from 24,48,72,... seconds to 4,6,12,24,36,48,... seconds (#8034) - Change remove floating point libs from IRAM - Change remove MQTT Info messages on restart for DeepSleep Wake (#8044) -- Change IRremoteESP8266 library updated to v2.7.5 +- Change IRremoteESP8266 library updated to v2.7.6 - Fix possible Relay toggle on (OTA) restart - Fix PWM flickering during wifi connection (#8046) - Fix Zigbee sending wrong Sat value with Hue emulation @@ -78,6 +78,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Add command ``SetOption73 1`` for button decoupling and send multi-press and hold MQTT messages by Federico Leoni (#8235) - Add command ``SetOption90 1`` to disable non-json MQTT messages (#8044) - Add command ``SetOption91 1`` to enable fading at startup / power on +- Add command ``SetOption92 1`` to set PWM Mode from regular PWM to ColorTemp control (Xiaomi Philips ...) - Add command ``Sensor10 0/1/2`` to control BH1750 resolution - 0 = High (default), 1 = High2, 2 = Low (#8016) - Add command ``Sensor10 31..254`` to control BH1750 measurement time which defaults to 69 (#8016) - Add command ``Sensor18 0..32000`` to control PMS5003 sensor interval to extend lifetime by Gene Ruebsamen (#8128) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 9d6b6c8e0..f3b36fcf5 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -12,6 +12,7 @@ - Fix Zigbee DimmerUp/DimmerDown malformed - Add config version tag - Add command ``SetOption73 1`` for button decoupling and send multi-press and hold MQTT messages by Federico Leoni (#8235) +- Add command ``SetOption92 1`` to set PWM Mode from regular PWM to ColorTemp control (Xiaomi Philips ...) - Add command ``SO`` as shortcut for command ``SetOption`` ### 8.2.0.3 20200329 diff --git a/tasmota/settings.h b/tasmota/settings.h index ae9243c70..ccf3e44ed 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -111,7 +111,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t zigbee_distinct_topics : 1; // bit 7 (v8.1.0.10) - SetOption89 - Distinct MQTT topics per device for Zigbee (#7835) uint32_t only_json_message : 1; // bit 8 (v8.2.0.3) - SetOption90 - Disable non-json MQTT response uint32_t fade_at_startup : 1; // bit 9 (v8.2.0.3) - SetOption91 - Enable light fading at start/power on - uint32_t pwm_ct_mode : 1; // bit 10 () - SetOption92 - Set PWM Mode from regular PWM to ColorTemp control (Xiaomi Philips ...) + uint32_t pwm_ct_mode : 1; // bit 10 (v8.2.0.4) - SetOption92 - Set PWM Mode from regular PWM to ColorTemp control (Xiaomi Philips ...) uint32_t spare11 : 1; uint32_t spare12 : 1; uint32_t spare13 : 1; diff --git a/tasmota/xsns_02_analog.ino b/tasmota/xsns_02_analog.ino index 41be46ff8..019eed86a 100644 --- a/tasmota/xsns_02_analog.ino +++ b/tasmota/xsns_02_analog.ino @@ -167,26 +167,25 @@ void AdcGetCurrentPower(uint8_t factor) uint16_t analog_min = 1023; uint16_t analog_max = 0; - if(Settings.adc_param1==0){ - for (uint32_t i = 0; i < samples; i++) { - analog = analogRead(A0); - if (analog < analog_min) { - analog_min = analog; + if (0 == Settings.adc_param1) { + for (uint32_t i = 0; i < samples; i++) { + analog = analogRead(A0); + if (analog < analog_min) { + analog_min = analog; + } + if (analog > analog_max) { + analog_max = analog; + } + delay(1); } - if (analog > analog_max) { - analog_max = analog; - } - delay(1); + Adc.current = (float)(analog_max-analog_min) * ((float)(Settings.adc_param2) / 100000); } - Adc.current = (float)(analog_max-analog_min) * ((float)(Settings.adc_param2) / 100000); - } - else{ - + else { analog = AdcRead(5); - if(analog>Settings.adc_param1){ + if (analog > Settings.adc_param1) { Adc.current = ((float)(analog) - (float)Settings.adc_param1) * ((float)(Settings.adc_param2) / 100000); } - else{ + else { Adc.current = 0; } } @@ -370,7 +369,7 @@ void CmndAdcParam(void) Settings.adc_param3 = (int)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)) * 10000); } if (ADC0_CT_POWER == XdrvMailbox.payload) { - if ((Settings.adc_param1 == 1 & CT_FLAG_ENERGY_RESET) > 0) { + if (((1 == Settings.adc_param1) & CT_FLAG_ENERGY_RESET) > 0) { Adc.energy = 0; Settings.adc_param1 ^= CT_FLAG_ENERGY_RESET; // Cancel energy reset flag } diff --git a/tools/decode-status.py b/tools/decode-status.py index 2450d1fb5..d2d7e2b83 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -126,7 +126,7 @@ a_setoption = [[ "Enable Weekend Energy Tariff", "Select different Modbus registers for Active Energy", "Enable hardware energy total counter as reference", - "Enable HTTP CORS", + "Detach buttons from relays and enable MQTT action state for multipress", "Enable internal pullup for single DS18x20 sensor", "GroupTopic replaces %topic% (0) or fixed topic cmnd/grouptopic (1)", "Enable incrementing bootcount when deepsleep is enabled", @@ -146,7 +146,7 @@ a_setoption = [[ "Distinct MQTT topics per device for Zigbee", "Disable non-json MQTT response", "Enable light fading at start/power on", - "","", + "Set PWM Mode from regular PWM to ColorTemp control","", "","","","", "","","","", "","","","", @@ -241,7 +241,7 @@ else: obj = json.load(fp) def StartDecode(): - print ("\n*** decode-status.py v20200411 by Theo Arends and Jacek Ziolkowski ***") + print ("\n*** decode-status.py v20200428 by Theo Arends and Jacek Ziolkowski ***") # print("Decoding\n{}".format(obj)) From e23b43f9798cf484df850d2365163bc5e7fdcc2e Mon Sep 17 00:00:00 2001 From: Matteo Albinola Date: Tue, 28 Apr 2020 14:02:05 +0200 Subject: [PATCH 385/547] Add option for triggering a tele update under conditions --- tasmota/settings.h | 2 +- tasmota/xsns_68_windmeter.ino | 46 +++++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/tasmota/settings.h b/tasmota/settings.h index c1efb59d7..0cf2bd9cf 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -529,7 +529,7 @@ struct { uint16_t windmeter_radius; // F38 uint16_t windmeter_pulse_debounce; // F3A int16_t windmeter_speed_factor; // F3C - uint8_t windmeter_tele_on_change; // F3E + uint8_t windmeter_tele_pchange; // F3E uint8_t free_f3f[121]; // F3F - Decrement if adding new Setting variables just above and below diff --git a/tasmota/xsns_68_windmeter.ino b/tasmota/xsns_68_windmeter.ino index db938a47c..b0342984e 100644 --- a/tasmota/xsns_68_windmeter.ino +++ b/tasmota/xsns_68_windmeter.ino @@ -31,6 +31,7 @@ #define WINDMETER_DEF_PULSES_X_ROT 1 // Number of pulses for a complete rotation #define WINDMETER_DEF_PULSE_DEBOUNCE 10 // Pulse counter debounce time (milliseconds) #define WINDMETER_DEF_COMP_FACTOR 1.18 // Compensation factor +#define WINDMETER_DEF_TELE_PCHANGE 255 // Minimum percentage change between current and last reported speed in order to trigger a new tele message (0...100, 255 means off) #define WINDMETER_WEIGHT_AVG_SAMPLE 150 // No of samples to take #ifdef USE_WEBSERVER @@ -61,6 +62,7 @@ struct WINDMETER { unsigned long counter = 0; //uint32_t speed_time; float speed = 0; + float last_tele_speed = 0; #ifndef USE_WINDMETER_NOSTATISTICS float speed_min = 0; float speed_max = 0; @@ -104,6 +106,9 @@ void WindMeterInit(void) if (!Settings.windmeter_speed_factor) { Settings.windmeter_speed_factor = (int16_t)(WINDMETER_DEF_COMP_FACTOR * 1000); } + if (!Settings.windmeter_tele_pchange) { + Settings.windmeter_tele_pchange = WINDMETER_DEF_TELE_PCHANGE; + } #ifndef USE_WINDMETER_NOSTATISTICS WindMeterResetStatData(); @@ -154,6 +159,22 @@ void WindMeterEverySecond(void) WindMeterResetStatData(); } #endif // USE_WINDMETER_NOSTATISTICS + + if (WindMeterShouldTriggerTele()) { + WindMeterTriggerTele(); + } +} + +bool WindMeterShouldTriggerTele() +{ + if (Settings.windmeter_tele_pchange > 100) { + return false; + } else if (WindMeter.last_tele_speed == 0) { + return WindMeter.speed > 0; + } else { + float perc_change = (WindMeter.speed / WindMeter.last_tele_speed) -1; + return (perc_change * ((perc_change < 0) ? -100 : 100)) >= Settings.windmeter_tele_pchange; + } } void WindMeterResetStatData(void) @@ -204,6 +225,7 @@ void WindMeterShow(bool json) #endif // USE_WINDMETER_NOSTATISTICS if (json) { + WindMeter.last_tele_speed = WindMeter.speed; #ifndef USE_WINDMETER_NOSTATISTICS ResponseAppend_P(PSTR(",\"" D_WINDMETER_NAME "\":{\"" D_JSON_SPEED "\":{\"Act\":%s,\"Avg\":%s,\"Min\":%s,\"Max\":%s},\"Dir\":{\"Card\":\"%s\",\"Deg\":%s,\"Avg\":%s,\"AvgCard\":\"%s\",\"Min\":%s,\"Max\":%s,\"Range\":%s}}"), speed_string, @@ -252,6 +274,17 @@ void WindMeterShow(bool json) } } +void WindMeterTriggerTele(void) +{ + mqtt_data[0] = '\0'; + if (MqttShowSensor()) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); +#ifdef USE_RULES + RulesTeleperiod(); // Allow rule based HA messages +#endif // USE_RULES + } +} + /*********************************************************************************************\ * Commands \*********************************************************************************************/ @@ -282,13 +315,22 @@ bool Xsns68Cmnd(void) Settings.windmeter_speed_factor = (int16_t)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)) * 1000); } break; + case 5: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.windmeter_tele_pchange = (uint8_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + } + break; } if (show_parms) { char speed_factor_string[FLOATSZ]; dtostrfd((float)Settings.windmeter_speed_factor / 1000, 3, speed_factor_string); - Response_P(PSTR("{\"" D_WINDMETER_NAME "\":{\"Radius\":%d,\"PulsesPerRot\":%d,\"PulseDebounce\":%d,\"SpeedFactor\":%s}}"), - Settings.windmeter_radius, Settings.windmeter_pulses_x_rot, Settings.windmeter_pulse_debounce, speed_factor_string); + char tele_pchange_string[4] = "off"; + if (Settings.windmeter_tele_pchange <= 100) { + itoa(Settings.windmeter_tele_pchange, tele_pchange_string, 10); + } + Response_P(PSTR("{\"" D_WINDMETER_NAME "\":{\"Radius\":%d,\"PulsesPerRot\":%d,\"PulseDebounce\":%d,\"SpeedFactor\":%s,\"TeleTriggerMin%Change\":%s}}"), + Settings.windmeter_radius, Settings.windmeter_pulses_x_rot, Settings.windmeter_pulse_debounce, speed_factor_string, tele_pchange_string); } return serviced; } From 60e7a73b6066733c948993076df47f48cdecfaa8 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 28 Apr 2020 14:42:47 +0200 Subject: [PATCH 386/547] Switch from GPIO array to Pin array --- tasmota/support.ino | 16 +++++++++------- tasmota/support_tasmota.ino | 24 +++++++++++++++++------- tasmota/tasmota.ino | 6 +++++- tasmota/tasmota_globals.h | 2 ++ tasmota/xdrv_10_scripter.ino | 9 +++++++++ tasmota/xdrv_31_tasmota_slave.ino | 2 ++ tasmota/xnrg_01_hlw8012.ino | 4 ++++ tasmota/xsns_53_sml.ino | 6 ++++++ 8 files changed, 54 insertions(+), 15 deletions(-) diff --git a/tasmota/support.ino b/tasmota/support.ino index 0986a007e..1bcdb9015 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -1079,11 +1079,14 @@ uint32_t Pin(uint32_t gpio, uint32_t index) ICACHE_RAM_ATTR; uint32_t Pin(uint32_t gpio, uint32_t index = 0); uint32_t Pin(uint32_t gpio, uint32_t index) { -//#ifdef ESP8266 +#ifdef LEGACY_GPIO_ARRAY return pin[gpio + index]; // Pin number configured for gpio or 99 if not used -/* #else - uint16_t real_gpio = (gpio << 5) + index; +//#ifdef ESP8266 + uint16_t real_gpio = gpio + index; +//#else +// uint16_t real_gpio = (gpio << 5) + index; +//endif for (uint32_t i = 0; i < ARRAY_SIZE(pin); i++) { if (pin[i] == real_gpio) { return i; // Pin number configured for gpio @@ -1091,7 +1094,6 @@ uint32_t Pin(uint32_t gpio, uint32_t index) { } return 99; // No pin used for gpio #endif -*/ } boolean PinUsed(uint32_t gpio, uint32_t index = 0); @@ -1100,20 +1102,20 @@ boolean PinUsed(uint32_t gpio, uint32_t index) { } void SetPin(uint32_t lpin, uint32_t gpio) { -//#ifdef ESP8266 +#ifdef LEGACY_GPIO_ARRAY pin[gpio] = lpin; -/* #else pin[lpin] = gpio; #endif -*/ } +#ifdef LEGACY_GPIO_ARRAY void InitAllPins(void) { for (uint32_t i = 0; i < ARRAY_SIZE(pin); i++) { SetPin(99, i); } } +#endif void DigitalWrite(uint32_t gpio_pin, uint32_t index, uint32_t state) { diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 3db5ea2f7..72137edab 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -1383,13 +1383,15 @@ void GpioInit(void) my_adc0 = template_adc0; // Force Template override } +#ifdef LEGACY_GPIO_ARRAY InitAllPins(); +#endif for (uint32_t i = 0; i < ARRAY_SIZE(my_module.io); i++) { uint32_t mpin = ValidPin(i, my_module.io[i]); DEBUG_CORE_LOG(PSTR("INI: gpio pin %d, mpin %d"), i, mpin); - if (mpin) { + if (mpin) { // Above GPIO_NONE XdrvMailbox.index = mpin; XdrvMailbox.payload = i; @@ -1398,16 +1400,16 @@ void GpioInit(void) mpin -= (GPIO_SWT1_NP - GPIO_SWT1); } else if ((mpin >= GPIO_KEY1_NP) && (mpin < (GPIO_KEY1_NP + MAX_KEYS))) { - ButtonPullupFlag(mpin - GPIO_KEY1_NP); // 0 .. 3 + ButtonPullupFlag(mpin - GPIO_KEY1_NP); // 0 .. 3 mpin -= (GPIO_KEY1_NP - GPIO_KEY1); } else if ((mpin >= GPIO_KEY1_INV) && (mpin < (GPIO_KEY1_INV + MAX_KEYS))) { - ButtonInvertFlag(mpin - GPIO_KEY1_INV); // 0 .. 3 + ButtonInvertFlag(mpin - GPIO_KEY1_INV); // 0 .. 3 mpin -= (GPIO_KEY1_INV - GPIO_KEY1); } else if ((mpin >= GPIO_KEY1_INV_NP) && (mpin < (GPIO_KEY1_INV_NP + MAX_KEYS))) { - ButtonPullupFlag(mpin - GPIO_KEY1_INV_NP); // 0 .. 3 - ButtonInvertFlag(mpin - GPIO_KEY1_INV_NP); // 0 .. 3 + ButtonPullupFlag(mpin - GPIO_KEY1_INV_NP); // 0 .. 3 + ButtonInvertFlag(mpin - GPIO_KEY1_INV_NP); // 0 .. 3 mpin -= (GPIO_KEY1_INV_NP - GPIO_KEY1); } else if ((mpin >= GPIO_REL1_INV) && (mpin < (GPIO_REL1_INV + MAX_RELAYS))) { @@ -1433,9 +1435,13 @@ void GpioInit(void) mpin = XdrvMailbox.index; }; } - if (mpin) { SetPin(i, mpin); } + if (mpin) { SetPin(i, mpin); } // Anything above GPIO_NONE and below GPIO_SENSOR_END } +#ifndef LEGACY_GPIO_ARRAY + AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t*)pin, ARRAY_SIZE(pin)); +#endif + #ifdef ESP8266 if ((2 == Pin(GPIO_TXD)) || (H801 == my_module_type)) { Serial.set_tx(2); } #endif // ESP8266 @@ -1446,9 +1452,11 @@ void GpioInit(void) #ifdef USE_SPI spi_flg = (((PinUsed(GPIO_SPI_CS) && (Pin(GPIO_SPI_CS) > 14)) || (Pin(GPIO_SPI_CS) < 12)) || ((PinUsed(GPIO_SPI_DC) && (Pin(GPIO_SPI_DC) > 14)) || (Pin(GPIO_SPI_DC) < 12))); if (spi_flg) { +#ifdef LEGACY_GPIO_ARRAY for (uint32_t i = 0; i < GPIO_MAX; i++) { if ((Pin(i) >= 12) && (Pin(i) <=14)) { SetPin(99, i); } } +#endif my_module.io[12] = GPIO_SPI_MISO; SetPin(12, GPIO_SPI_MISO); my_module.io[13] = GPIO_SPI_MOSI; @@ -1456,7 +1464,7 @@ void GpioInit(void) my_module.io[14] = GPIO_SPI_CLK; SetPin(14, GPIO_SPI_CLK); } - soft_spi_flg = (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_SCLK) && (PinUsed(GPIO_SSPI_MOSI) || PinUsed(GPIO_SSPI_MOSI))); + soft_spi_flg = (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_SCLK) && (PinUsed(GPIO_SSPI_MOSI) || PinUsed(GPIO_SSPI_MISO))); #endif // USE_SPI // Set any non-used GPIO to INPUT - Related to resetPins() in support_legacy_cores.ino @@ -1534,7 +1542,9 @@ void GpioInit(void) #ifdef USE_ARILUX_RF if ((3 == i) && (leds_present < 2) && !PinUsed(GPIO_ARIRFSEL)) { SetPin(Pin(GPIO_LED4), GPIO_ARIRFSEL); // Legacy support where LED4 was Arilux RF enable +#ifdef LEGACY_GPIO_ARRAY SetPin(99, GPIO_LED4); +#endif } else { #endif pinMode(Pin(GPIO_LED1, i), OUTPUT); diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 69be1bbe5..4f84832ac 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -119,7 +119,7 @@ uint16_t blink_counter = 0; // Number of blink cycles uint16_t seriallog_timer = 0; // Timer to disable Seriallog uint16_t syslog_timer = 0; // Timer to re-enable syslog_level //#ifdef ESP32 -//uint16_t pin[MAX_GPIO_PIN]; // Possible pin configurations +//uint16_t pin[MAX_GPIO_PIN] = { 0 }; // Possible pin configurations //#endif int16_t save_data_counter; // Counter and flag for config save to Flash RulesBitfield rules_flag; // Rule state flags (16 bits) @@ -130,7 +130,11 @@ uint8_t latching_relay_pulse = 0; // Latching relay pulse timer uint8_t ssleep; // Current copy of Settings.sleep uint8_t blinkspeed = 1; // LED blink rate //#ifdef ESP8266 +#ifdef LEGACY_GPIO_ARRAY uint8_t pin[GPIO_MAX]; // Possible pin configurations +#else +uint8_t pin[MAX_GPIO_PIN] = { 0 }; // Possible pin configurations +#endif //#endif uint8_t active_device = 1; // Active device in ExecuteCommandPower uint8_t leds_present = 0; // Max number of LED supported diff --git a/tasmota/tasmota_globals.h b/tasmota/tasmota_globals.h index 9b72ba954..8881f8334 100644 --- a/tasmota/tasmota_globals.h +++ b/tasmota/tasmota_globals.h @@ -53,6 +53,8 @@ extern "C" void resetPins(); * Mandatory defines satisfying disabled defines \*********************************************************************************************/ +//#define LEGACY_GPIO_ARRAY + #ifndef MODULE #define MODULE SONOFF_BASIC // [Module] Select default model #endif diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index 614989ab4..0c2ad48cf 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -1582,6 +1582,7 @@ chknext: if (!strncmp(vname,"pd[",3)) { GetNumericResult(vname+3,OPER_EQU,&fvar,0); uint8_t gpiopin=fvar; +#ifdef LEGACY_GPIO_ARRAY for (uint8_t i=0;i 0)) { + fvar = pin[gpiopin]; + // skip ] bracket + len++; + goto exit; + } +#endif fvar=999; goto exit; } diff --git a/tasmota/xdrv_31_tasmota_slave.ino b/tasmota/xdrv_31_tasmota_slave.ino index 37b98c897..370a3f6d6 100644 --- a/tasmota/xdrv_31_tasmota_slave.ino +++ b/tasmota/xdrv_31_tasmota_slave.ino @@ -447,7 +447,9 @@ void TasmotaSlave_Init(void) TasmotaSlave_Serial->setTimeout(50); if (PinUsed(GPIO_TASMOTASLAVE_RST_INV)) { SetPin(Pin(GPIO_TASMOTASLAVE_RST_INV), GPIO_TASMOTASLAVE_RST); +#ifdef LEGACY_GPIO_ARRAY SetPin(99, GPIO_TASMOTASLAVE_RST_INV); +#endif TSlave.inverted = HIGH; } pinMode(Pin(GPIO_TASMOTASLAVE_RST), OUTPUT); diff --git a/tasmota/xnrg_01_hlw8012.ino b/tasmota/xnrg_01_hlw8012.ino index 12a10abb6..7a3becf0a 100644 --- a/tasmota/xnrg_01_hlw8012.ino +++ b/tasmota/xnrg_01_hlw8012.ino @@ -250,7 +250,9 @@ void HlwDrvInit(void) Hlw.model_type = 0; // HLW8012 if (PinUsed(GPIO_HJL_CF)) { SetPin(Pin(GPIO_HJL_CF), GPIO_HLW_CF); +#ifdef LEGACY_GPIO_ARRAY SetPin(99, GPIO_HJL_CF); +#endif Hlw.model_type = 1; // HJL-01/BL0937 } @@ -259,7 +261,9 @@ void HlwDrvInit(void) Hlw.ui_flag = true; // Voltage on high if (PinUsed(GPIO_NRG_SEL_INV)) { SetPin(Pin(GPIO_NRG_SEL_INV), GPIO_NRG_SEL); +#ifdef LEGACY_GPIO_ARRAY SetPin(99, GPIO_NRG_SEL_INV); +#endif Hlw.ui_flag = false; // Voltage on low } diff --git a/tasmota/xsns_53_sml.ino b/tasmota/xsns_53_sml.ino index c61bfa20d..632f64f50 100755 --- a/tasmota/xsns_53_sml.ino +++ b/tasmota/xsns_53_sml.ino @@ -1831,12 +1831,18 @@ uint8_t *script_meter; #endif bool Gpio_used(uint8_t gpiopin) { +#ifdef LEGACY_GPIO_ARRAY for (uint16_t i=0;i 0)) { + return true; + } +#endif return false; } From 8975bfb310787e97a241d236723424408bbc6c12 Mon Sep 17 00:00:00 2001 From: Matteo Albinola Date: Tue, 28 Apr 2020 18:09:10 +0200 Subject: [PATCH 387/547] Uncomment feature lines --- tasmota/support_features.ino | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino index 454e13bac..fcaedf423 100644 --- a/tasmota/support_features.ino +++ b/tasmota/support_features.ino @@ -554,9 +554,9 @@ void GetFeatures(void) #ifdef USE_PING feature6 |= 0x00000080; // xdrv_38_ping.ino #endif -//#ifdef USE_WINDMETER -// feature6 |= 0x00000100; // xsns_68_windmeter.ino -//#endif +#ifdef USE_WINDMETER + feature6 |= 0x00000100; // xsns_68_windmeter.ino +#endif // feature6 |= 0x00000100; // feature6 |= 0x00000200; From c24de18278222e970935c9ba8a268c2406a418b7 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 28 Apr 2020 18:27:07 +0200 Subject: [PATCH 388/547] Change ESP32 pin allocation part 1 --- tasmota/settings.h | 11 + tasmota/support.ino | 43 +- tasmota/tasmota.h | 6 - tasmota/tasmota.ino | 28 +- tasmota/tasmota_globals.h | 13 +- tasmota/tasmota_template.h | 25 +- tasmota/tasmota_template_ESP32.h | 731 ++++++++++++++++++++++++ tasmota/tasmota_template_ESP32_final.h | 742 +++++++++++++++++++++++++ 8 files changed, 1548 insertions(+), 51 deletions(-) create mode 100644 tasmota/tasmota_template_ESP32_final.h diff --git a/tasmota/settings.h b/tasmota/settings.h index ccf3e44ed..e5c9800fd 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -358,8 +358,17 @@ struct { SysBitfield3 flag3; // 3A0 uint8_t switchmode[MAX_SWITCHES]; // 3A4 (6.0.0b - moved from 0x4CA) +#ifdef ESP8266 char ex_friendlyname[4][33]; // 3AC char ex_switch_topic[33]; // 430 +#else // ESP32 +#ifdef FINAL_ESP32 + myio my_gp; // 3AC - 2 x 40 bytes (ESP32) + mytmplt user_template; // 3FC - 2 x 37 bytes (ESP32) + + uint8_t free_esp32_446[11]; // 446 +#endif +#endif // ESP8266 - ESP32 char serial_delimiter; // 451 uint8_t seriallog_level; // 452 @@ -406,10 +415,12 @@ struct { #ifdef ESP8266 char ex_mqtt_fulltopic[100]; // 558 #else // ESP32 +#ifndef FINAL_ESP32 myio my_gp; // 558 - 40 bytes (ESP32) mytmplt user_template; // 580 - 37 bytes (ESP32) uint8_t free_esp32_5a5[23]; // 5A5 +#endif #endif // ESP8266 - ESP32 SysBitfield2 flag2; // 5BC diff --git a/tasmota/support.ino b/tasmota/support.ino index 1bcdb9015..02d17af98 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -1081,19 +1081,23 @@ uint32_t Pin(uint32_t gpio, uint32_t index = 0); uint32_t Pin(uint32_t gpio, uint32_t index) { #ifdef LEGACY_GPIO_ARRAY return pin[gpio + index]; // Pin number configured for gpio or 99 if not used -#else -//#ifdef ESP8266 +#else // No LEGACY_GPIO_ARRAY +#ifdef ESP8266 uint16_t real_gpio = gpio + index; -//#else -// uint16_t real_gpio = (gpio << 5) + index; -//endif +#else // ESP32 +#ifndef FINAL_ESP32 + uint16_t real_gpio = gpio + index; +#else // FINAL_ESP32 + uint16_t real_gpio = (gpio << 5) + index; +#endif // FINAL_ESP32 +#endif // ESP8266 - ESP32 for (uint32_t i = 0; i < ARRAY_SIZE(pin); i++) { if (pin[i] == real_gpio) { return i; // Pin number configured for gpio } } return 99; // No pin used for gpio -#endif +#endif // No LEGACY_GPIO_ARRAY } boolean PinUsed(uint32_t gpio, uint32_t index = 0); @@ -1164,13 +1168,18 @@ String ModuleName(void) void ModuleGpios(myio *gp) { -//#ifdef ESP8266 +#ifdef ESP8266 uint8_t *dest = (uint8_t *)gp; uint8_t src[ARRAY_SIZE(Settings.user_template.gp.io)]; -//#else -// uint16_t *dest = (uint16_t *)gp; -// uint16_t src[ARRAY_SIZE(Settings.user_template.gp.io)]; -//#endif +#else // ESP32 +#ifndef FINAL_ESP32 + uint8_t *dest = (uint8_t *)gp; + uint8_t src[ARRAY_SIZE(Settings.user_template.gp.io)]; +#else // FINAL_ESP32 + uint16_t *dest = (uint16_t *)gp; + uint16_t src[ARRAY_SIZE(Settings.user_template.gp.io)]; +#endif +#endif // ESP8266 - ESP32 memset(dest, GPIO_NONE, sizeof(myio)); if (USER_MODULE == Settings.module) { @@ -1275,11 +1284,15 @@ bool ValidAdc(void) return (ADC0_USER == template_adc0); } -//#ifdef ESP8266 +#ifdef ESP8266 bool GetUsedInModule(uint32_t val, uint8_t *arr) -//#else -//bool GetUsedInModule(uint32_t val, uint16_t *arr) -//#endif +#else // ESP32 +#ifndef FINAL_ESP32 +bool GetUsedInModule(uint32_t val, uint8_t *arr) +#else // FINAL_ESP32 +bool GetUsedInModule(uint32_t val, uint16_t *arr) +#endif // FINAL_ESP32 +#endif // ESP8266 - ESP32 { int offset = 0; diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index aae4df24c..a1a3cb473 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -41,14 +41,8 @@ * Power Type \*********************************************************************************************/ -//#ifdef ESP8266 typedef unsigned long power_t; // Power (Relay) type const uint32_t POWER_MASK = 0xffffffffUL; // Power (Relay) full mask -//#endif // ESP8266 -//#ifdef ESP32 -//typedef uint64_t power_t; // Power (Relay) type -//const uint64_t POWER_MASK = 0xffffffffffffffffull; // Power (Relay) full mask -//#endif // ESP32 /*********************************************************************************************\ * Constants diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 4f84832ac..8af7285ad 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -118,9 +118,13 @@ uint16_t tele_period = 9999; // Tele period timer uint16_t blink_counter = 0; // Number of blink cycles uint16_t seriallog_timer = 0; // Timer to disable Seriallog uint16_t syslog_timer = 0; // Timer to re-enable syslog_level -//#ifdef ESP32 -//uint16_t pin[MAX_GPIO_PIN] = { 0 }; // Possible pin configurations -//#endif + +#ifdef ESP32 +#ifdef FINAL_ESP32 +uint16_t pin[MAX_GPIO_PIN] = { 0 }; // Possible pin configurations +#endif // FINAL_ESP32 +#endif // ESP32 + int16_t save_data_counter; // Counter and flag for config save to Flash RulesBitfield rules_flag; // Rule state flags (16 bits) uint8_t mqtt_cmnd_blocked = 0; // Ignore flag for publish command @@ -129,13 +133,23 @@ uint8_t state_250mS = 0; // State 250msecond per second flag uint8_t latching_relay_pulse = 0; // Latching relay pulse timer uint8_t ssleep; // Current copy of Settings.sleep uint8_t blinkspeed = 1; // LED blink rate -//#ifdef ESP8266 + +#ifdef ESP8266 #ifdef LEGACY_GPIO_ARRAY uint8_t pin[GPIO_MAX]; // Possible pin configurations -#else +#else // No LEGACY_GPIO_ARRAY uint8_t pin[MAX_GPIO_PIN] = { 0 }; // Possible pin configurations -#endif -//#endif +#endif // LEGACY_GPIO_ARRAY +#else // ESP32 +#ifndef FINAL_ESP32 +#ifdef LEGACY_GPIO_ARRAY +uint8_t pin[GPIO_MAX]; // Possible pin configurations +#else // No LEGACY_GPIO_ARRAY +uint8_t pin[MAX_GPIO_PIN] = { 0 }; // Possible pin configurations +#endif // LEGACY_GPIO_ARRAY +#endif // No FINAL_ESP32 +#endif // ESP8266 - ESP32 + uint8_t active_device = 1; // Active device in ExecuteCommandPower uint8_t leds_present = 0; // Max number of LED supported uint8_t led_inverted = 0; // LED inverted flag (1 = (0 = On, 1 = Off)) diff --git a/tasmota/tasmota_globals.h b/tasmota/tasmota_globals.h index 8881f8334..178cf3c24 100644 --- a/tasmota/tasmota_globals.h +++ b/tasmota/tasmota_globals.h @@ -50,10 +50,19 @@ extern "C" void resetPins(); #include "tasmota_configurations.h" // Preconfigured configurations /*********************************************************************************************\ - * Mandatory defines satisfying disabled defines + * Theo transition defines - DO NOT TOUCH \*********************************************************************************************/ -//#define LEGACY_GPIO_ARRAY +//#define LEGACY_GPIO_ARRAY // Uncomment to use legacy GPIO array instead of new PIN array + +//#define FINAL_ESP32 // Uncomment for ESP32 16-bits PIN array +#ifdef FINAL_ESP32 +#undef LEGACY_GPIO_ARRAY +#endif + +/*********************************************************************************************\ + * Mandatory defines satisfying disabled defines +\*********************************************************************************************/ #ifndef MODULE #define MODULE SONOFF_BASIC // [Module] Select default model diff --git a/tasmota/tasmota_template.h b/tasmota/tasmota_template.h index ae95705c7..5f9c23b7c 100644 --- a/tasmota/tasmota_template.h +++ b/tasmota/tasmota_template.h @@ -20,6 +20,8 @@ #ifndef _TASMOTA_TEMPLATE_H_ #define _TASMOTA_TEMPLATE_H_ +#ifdef ESP8266 + // User selectable GPIO functionality // ATTENTION: Only add at the end of this list just before GPIO_SENSOR_END // Then add the same name(s) in a nice location in array kGpioNiceList @@ -699,31 +701,14 @@ const char kAdc0Names[] PROGMEM = /********************************************************************************************/ -#ifdef ESP8266 - #define MAX_GPIO_PIN 17 // Number of supported GPIO #define MIN_FLASH_PINS 4 // Number of flash chip pins unusable for configuration (GPIO6, 7, 8 and 11) +#define MAX_USER_PINS 13 // MAX_GPIO_PIN - MIN_FLASH_PINS #define ADC0_PIN 17 // Pin number of ADC0 #define WEMOS_MODULE 17 // Wemos module const char PINS_WEMOS[] PROGMEM = "D3TXD4RXD2D1flashcFLFLolD6D7D5D8D0A0"; -#else // ESP32 - -// esp32 has more pins -#define USER_MODULE 255 -#define MAX_GPIO_PIN 40 // Number of supported GPIO -#define MIN_FLASH_PINS 4 // Number of flash chip pins unusable for configuration (GPIO6, 7, 8 and 11) -#define ADC0_PIN 33 // Pin number of ADC0 -#define WEMOS_MODULE 0 // Wemos module - -// 0 1 2 3 4 5 6 7 8 9101112131415161718192021222324252627282930313233343536373839 -const char PINS_WEMOS[] PROGMEM = "IOTXIORXIOIOflashcFLFLolIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOA6A7A0IoIoA3"; - -#endif // ESP8266 - -#define MAX_USER_PINS MAX_GPIO_PIN-MIN_FLASH_PINS - /********************************************************************************************/ typedef struct MYIO { @@ -758,10 +743,8 @@ typedef struct MYTMPLT { } mytmplt; /********************************************************************************************/ - -#ifdef ESP8266 - // Supported hardware modules + enum SupportedModules { SONOFF_BASIC, SONOFF_RF, SONOFF_SV, SONOFF_TH, SONOFF_DUAL, SONOFF_POW, SONOFF_4CH, SONOFF_S2X, SLAMPHER, SONOFF_TOUCH, SONOFF_LED, CH1, CH4, MOTOR, ELECTRODRAGON, EXS_RELAY, WION, WEMOS, SONOFF_DEV, H801, diff --git a/tasmota/tasmota_template_ESP32.h b/tasmota/tasmota_template_ESP32.h index 831532a59..4dfd9dd90 100644 --- a/tasmota/tasmota_template_ESP32.h +++ b/tasmota/tasmota_template_ESP32.h @@ -21,6 +21,7 @@ #define _TASMOTA_TEMPLATE_ESP32_H_ #ifdef ESP32 +#ifndef FINAL_ESP32 // Hardware has no ESP32 #undef USE_TUYA_DIMMER @@ -42,12 +43,738 @@ #undef USE_TUYA_MCU #undef USE_PS_16_DZ +// User selectable GPIO functionality +// ATTENTION: Only add at the end of this list just before GPIO_SENSOR_END +// Then add the same name(s) in a nice location in array kGpioNiceList +enum UserSelectablePins { + GPIO_NONE, // Not used + GPIO_DHT11, // DHT11 + GPIO_DHT22, // DHT21, DHT22, AM2301, AM2302, AM2321 + GPIO_SI7021, // iTead SI7021 + GPIO_DSB, // Single wire DS18B20 or DS18S20 + GPIO_I2C_SCL, // I2C SCL + GPIO_I2C_SDA, // I2C SDA + GPIO_WS2812, // WS2812 Led string + GPIO_IRSEND, // IR remote + GPIO_SWT1, // User connected external switches + GPIO_SWT2, + GPIO_SWT3, + GPIO_SWT4, + GPIO_SWT5, + GPIO_SWT6, + GPIO_SWT7, + GPIO_SWT8, + GPIO_KEY1, // Button usually connected to GPIO0 + GPIO_KEY2, + GPIO_KEY3, + GPIO_KEY4, + GPIO_REL1, // Relays + GPIO_REL2, + GPIO_REL3, + GPIO_REL4, + GPIO_REL5, + GPIO_REL6, + GPIO_REL7, + GPIO_REL8, + GPIO_REL1_INV, + GPIO_REL2_INV, + GPIO_REL3_INV, + GPIO_REL4_INV, + GPIO_REL5_INV, + GPIO_REL6_INV, + GPIO_REL7_INV, + GPIO_REL8_INV, + GPIO_PWM1, // RGB Red or C Cold White + GPIO_PWM2, // RGB Green or CW Warm White + GPIO_PWM3, // RGB Blue + GPIO_PWM4, // RGBW (Cold) White + GPIO_PWM5, // RGBCW Warm White + GPIO_CNTR1, + GPIO_CNTR2, + GPIO_CNTR3, + GPIO_CNTR4, + GPIO_PWM1_INV, // RGB Red or C Cold White + GPIO_PWM2_INV, // RGB Green or CW Warm White + GPIO_PWM3_INV, // RGB Blue + GPIO_PWM4_INV, // RGBW (Cold) White + GPIO_PWM5_INV, // RGBCW Warm White + GPIO_IRRECV, // IR receiver + GPIO_LED1, // Leds + GPIO_LED2, + GPIO_LED3, + GPIO_LED4, + GPIO_LED1_INV, + GPIO_LED2_INV, + GPIO_LED3_INV, + GPIO_LED4_INV, + GPIO_MHZ_TXD, // MH-Z19 Serial interface + GPIO_MHZ_RXD, // MH-Z19 Serial interface + GPIO_PZEM0XX_TX, // PZEM0XX Serial interface + GPIO_PZEM004_RX, // PZEM004T Serial interface + GPIO_SAIR_TX, // SenseAir Serial interface + GPIO_SAIR_RX, // SenseAir Serial interface + GPIO_SPI_CS, // SPI Chip Select + GPIO_SPI_DC, // SPI Data Direction + GPIO_BACKLIGHT, // Display backlight control + GPIO_PMS5003_RX, // Plantower PMS5003 Serial interface + GPIO_SDS0X1_RX, // Nova Fitness SDS011 Serial interface + GPIO_SBR_TX, // Serial Bridge Serial interface + GPIO_SBR_RX, // Serial Bridge Serial interface + GPIO_SR04_TRIG, // SR04 Trigger/TX pin + GPIO_SR04_ECHO, // SR04 Echo/RX pin + GPIO_SDM120_TX, // SDM120 Serial interface + GPIO_SDM120_RX, // SDM120 Serial interface + GPIO_SDM630_TX, // SDM630 Serial interface + GPIO_SDM630_RX, // SDM630 Serial interface + GPIO_TM16CLK, // TM1638 Clock + GPIO_TM16DIO, // TM1638 Data I/O + GPIO_TM16STB, // TM1638 Strobe + GPIO_SWT1_NP, // User connected external switches + GPIO_SWT2_NP, + GPIO_SWT3_NP, + GPIO_SWT4_NP, + GPIO_SWT5_NP, + GPIO_SWT6_NP, + GPIO_SWT7_NP, + GPIO_SWT8_NP, + GPIO_KEY1_NP, // Button usually connected to GPIO0 + GPIO_KEY2_NP, + GPIO_KEY3_NP, + GPIO_KEY4_NP, + GPIO_CNTR1_NP, + GPIO_CNTR2_NP, + GPIO_CNTR3_NP, + GPIO_CNTR4_NP, + GPIO_PZEM016_RX, // PZEM-014,016 Serial Modbus interface + GPIO_PZEM017_RX, // PZEM-003,017 Serial Modbus interface + GPIO_MP3_DFR562, // RB-DFR-562, DFPlayer Mini MP3 Player + GPIO_SDS0X1_TX, // Nova Fitness SDS011 Serial interface + GPIO_HX711_SCK, // HX711 Load Cell clock + GPIO_HX711_DAT, // HX711 Load Cell data + GPIO_TX2X_TXD_BLACK, // TX20/TX23 Transmission Pin + GPIO_RFSEND, // RF transmitter + GPIO_RFRECV, // RF receiver + GPIO_TUYA_TX, // Tuya Serial interface + GPIO_TUYA_RX, // Tuya Serial interface + GPIO_MGC3130_XFER, // MGC3130 Transfer + GPIO_MGC3130_RESET, // MGC3130 Reset + GPIO_SSPI_MISO, // Software SPI Master Input Slave Output + GPIO_SSPI_MOSI, // Software SPI Master Output Slave Input + GPIO_SSPI_SCLK, // Software SPI Serial Clock + GPIO_SSPI_CS, // Software SPI Chip Select + GPIO_SSPI_DC, // Software SPI Data or Command + GPIO_RF_SENSOR, // Rf receiver with sensor decoding + GPIO_AZ_TXD, // AZ-Instrument 7798 Serial interface + GPIO_AZ_RXD, // AZ-Instrument 7798 Serial interface + GPIO_MAX31855CS, // MAX31855 Serial interface + GPIO_MAX31855CLK, // MAX31855 Serial interface + GPIO_MAX31855DO, // MAX31855 Serial interface + GPIO_KEY1_INV, // Inverted buttons + GPIO_KEY2_INV, + GPIO_KEY3_INV, + GPIO_KEY4_INV, + GPIO_KEY1_INV_NP, // Inverted buttons without pull-up + GPIO_KEY2_INV_NP, + GPIO_KEY3_INV_NP, + GPIO_KEY4_INV_NP, + GPIO_NRG_SEL, // HLW8012/HLJ-01 Sel output (1 = Voltage) + GPIO_NRG_SEL_INV, // HLW8012/HLJ-01 Sel output (0 = Voltage) + GPIO_NRG_CF1, // HLW8012/HLJ-01 CF1 voltage / current + GPIO_HLW_CF, // HLW8012 CF power + GPIO_HJL_CF, // HJL-01/BL0937 CF power + GPIO_MCP39F5_TX, // MCP39F501 Serial interface (Shelly2) + GPIO_MCP39F5_RX, // MCP39F501 Serial interface (Shelly2) + GPIO_MCP39F5_RST, // MCP39F501 Reset (Shelly2) + GPIO_PN532_TXD, // PN532 NFC Serial Tx + GPIO_PN532_RXD, // PN532 NFC Serial Rx + GPIO_SM16716_CLK, // SM16716 CLOCK + GPIO_SM16716_DAT, // SM16716 DATA + GPIO_SM16716_SEL, // SM16716 SELECT + GPIO_DI, // my92x1 PWM input + GPIO_DCKI, // my92x1 CLK input + GPIO_CSE7766_TX, // CSE7766 Serial interface (S31 and Pow R2) - Not used anymore 20200121 + GPIO_CSE7766_RX, // CSE7766 Serial interface (S31 and Pow R2) + GPIO_ARIRFRCV, // AriLux RF Receive input + GPIO_TXD, // Serial interface + GPIO_RXD, // Serial interface + GPIO_ROT1A, // Rotary switch1 A Pin + GPIO_ROT1B, // Rotary switch1 B Pin + GPIO_ROT2A, // Rotary switch2 A Pin + GPIO_ROT2B, // Rotary switch2 B Pin + GPIO_HRE_CLOCK, // Clock/Power line for HR-E Water Meter + GPIO_HRE_DATA, // Data line for HR-E Water Meter + GPIO_ADE7953_IRQ, // ADE7953 IRQ + GPIO_LEDLNK, // Link led + GPIO_LEDLNK_INV, // Inverted link led + GPIO_ARIRFSEL, // Arilux RF Receive input selected + GPIO_BUZZER, // Buzzer + GPIO_BUZZER_INV, // Inverted buzzer + GPIO_OLED_RESET, // OLED Display Reset + GPIO_SOLAXX1_TX, // Solax Inverter tx pin + GPIO_SOLAXX1_RX, // Solax Inverter rx pin + GPIO_ZIGBEE_TX, // Zigbee Serial interface + GPIO_ZIGBEE_RX, // Zigbee Serial interface + GPIO_RDM6300_RX, // RDM6300 RX + GPIO_IBEACON_TX, // HM17 IBEACON TX + GPIO_IBEACON_RX, // HM17 IBEACON RX + GPIO_A4988_DIR, // A4988 direction pin + GPIO_A4988_STP, // A4988 step pin + GPIO_A4988_ENA, // A4988 enabled pin + GPIO_A4988_MS1, // A4988 microstep pin1 + GPIO_A4988_MS2, // A4988 microstep pin2 + GPIO_A4988_MS3, // A4988 microstep pin3 + GPIO_DDS2382_TX, // DDS2382 Serial interface + GPIO_DDS2382_RX, // DDS2382 Serial interface + GPIO_DDSU666_TX, // DDSU666 Serial interface + GPIO_DDSU666_RX, // DDSU666 Serial interface + GPIO_SM2135_CLK, // SM2135 Clk + GPIO_SM2135_DAT, // SM2135 Dat + GPIO_DEEPSLEEP, // Kill switch for deepsleep + GPIO_EXS_ENABLE, // EXS MCU Enable + GPIO_TASMOTASLAVE_TXD, // Slave TX + GPIO_TASMOTASLAVE_RXD, // Slave RX + GPIO_TASMOTASLAVE_RST, // Slave Reset Pin + GPIO_TASMOTASLAVE_RST_INV, // Slave Reset Inverted + GPIO_HPMA_RX, // Honeywell HPMA115S0 Serial interface + GPIO_HPMA_TX, // Honeywell HPMA115S0 Serial interface + GPIO_GPS_RX, // GPS serial interface + GPIO_GPS_TX, // GPS serial interface + GPIO_DSB_OUT, // Pseudo Single wire DS18B20 or DS18S20 + GPIO_DHT11_OUT, // Pseudo Single wire DHT11, DHT21, DHT22, AM2301, AM2302, AM2321 + GPIO_HM10_RX, // HM10-BLE-Mijia-bridge serial interface + GPIO_HM10_TX, // HM10-BLE-Mijia-bridge serial interface + GPIO_LE01MR_RX, // F&F LE-01MR energy meter + GPIO_LE01MR_TX, // F&F LE-01MR energy meter + GPIO_CC1101_GDO0, // CC1101 pin for RX + GPIO_CC1101_GDO2, // CC1101 pin for RX + GPIO_HRXL_RX, // Data from MaxBotix HRXL sonar range sensor + GPIO_ELECTRIQ_MOODL_TX, // ElectriQ iQ-wifiMOODL Serial TX + GPIO_AS3935, + GPIO_PMS5003_TX, // Plantower PMS5003 Serial interface + GPIO_SENSOR_END }; + +// Programmer selectable GPIO functionality +enum ProgramSelectablePins { + GPIO_FIX_START = 251, + GPIO_SPI_MISO, // SPI MISO library fixed pin GPIO12 + GPIO_SPI_MOSI, // SPI MOSI library fixed pin GPIO13 + GPIO_SPI_CLK, // SPI Clk library fixed pin GPIO14 + GPIO_USER, // User configurable needs to be 255 + GPIO_MAX }; + +// Text in webpage Module Parameters and commands GPIOS and GPIO +const char kSensorNames[] PROGMEM = + D_SENSOR_NONE "|" + D_SENSOR_DHT11 "|" D_SENSOR_AM2301 "|" D_SENSOR_SI7021 "|" + D_SENSOR_DS18X20 "|" + D_SENSOR_I2C_SCL "|" D_SENSOR_I2C_SDA "|" + D_SENSOR_WS2812 "|" + D_SENSOR_IRSEND "|" + D_SENSOR_SWITCH "1|" D_SENSOR_SWITCH "2|" D_SENSOR_SWITCH "3|" D_SENSOR_SWITCH "4|" D_SENSOR_SWITCH "5|" D_SENSOR_SWITCH "6|" D_SENSOR_SWITCH "7|" D_SENSOR_SWITCH "8|" + D_SENSOR_BUTTON "1|" D_SENSOR_BUTTON "2|" D_SENSOR_BUTTON "3|" D_SENSOR_BUTTON "4|" + D_SENSOR_RELAY "1|" D_SENSOR_RELAY "2|" D_SENSOR_RELAY "3|" D_SENSOR_RELAY "4|" D_SENSOR_RELAY "5|" D_SENSOR_RELAY "6|" D_SENSOR_RELAY "7|" D_SENSOR_RELAY "8|" + D_SENSOR_RELAY "1i|" D_SENSOR_RELAY "2i|" D_SENSOR_RELAY "3i|" D_SENSOR_RELAY "4i|" D_SENSOR_RELAY "5i|" D_SENSOR_RELAY "6i|" D_SENSOR_RELAY "7i|" D_SENSOR_RELAY "8i|" + D_SENSOR_PWM "1|" D_SENSOR_PWM "2|" D_SENSOR_PWM "3|" D_SENSOR_PWM "4|" D_SENSOR_PWM "5|" + D_SENSOR_COUNTER "1|" D_SENSOR_COUNTER "2|" D_SENSOR_COUNTER "3|" D_SENSOR_COUNTER "4|" + D_SENSOR_PWM "1i|" D_SENSOR_PWM "2i|" D_SENSOR_PWM "3i|" D_SENSOR_PWM "4i|" D_SENSOR_PWM "5i|" + D_SENSOR_IRRECV "|" + D_SENSOR_LED "1|" D_SENSOR_LED "2|" D_SENSOR_LED "3|" D_SENSOR_LED "4|" + D_SENSOR_LED "1i|" D_SENSOR_LED "2i|" D_SENSOR_LED "3i|" D_SENSOR_LED "4i|" + D_SENSOR_MHZ_TX "|" D_SENSOR_MHZ_RX "|" + D_SENSOR_PZEM0XX_TX "|" D_SENSOR_PZEM004_RX "|" + D_SENSOR_SAIR_TX "|" D_SENSOR_SAIR_RX "|" + D_SENSOR_SPI_CS "|" D_SENSOR_SPI_DC "|" D_SENSOR_BACKLIGHT "|" + D_SENSOR_PMS5003_RX "|" D_SENSOR_SDS0X1_RX "|" + D_SENSOR_SBR_TX "|" D_SENSOR_SBR_RX "|" + D_SENSOR_SR04_TRIG "|" D_SENSOR_SR04_ECHO "|" + D_SENSOR_SDM120_TX "|" D_SENSOR_SDM120_RX "|" + D_SENSOR_SDM630_TX "|" D_SENSOR_SDM630_RX "|" + D_SENSOR_TM1638_CLK "|" D_SENSOR_TM1638_DIO "|" D_SENSOR_TM1638_STB "|" + D_SENSOR_SWITCH "1n|" D_SENSOR_SWITCH "2n|" D_SENSOR_SWITCH "3n|" D_SENSOR_SWITCH "4n|" D_SENSOR_SWITCH "5n|" D_SENSOR_SWITCH "6n|" D_SENSOR_SWITCH "7n|" D_SENSOR_SWITCH "8n|" + D_SENSOR_BUTTON "1n|" D_SENSOR_BUTTON "2n|" D_SENSOR_BUTTON "3n|" D_SENSOR_BUTTON "4n|" + D_SENSOR_COUNTER "1n|" D_SENSOR_COUNTER "2n|" D_SENSOR_COUNTER "3n|" D_SENSOR_COUNTER "4n|" + D_SENSOR_PZEM016_RX "|" D_SENSOR_PZEM017_RX "|" + D_SENSOR_DFR562 "|" D_SENSOR_SDS0X1_TX "|" + D_SENSOR_HX711_SCK "|" D_SENSOR_HX711_DAT "|" + D_SENSOR_TX2X_TX "|" + D_SENSOR_RFSEND "|" D_SENSOR_RFRECV "|" + D_SENSOR_TUYA_TX "|" D_SENSOR_TUYA_RX "|" + D_SENSOR_MGC3130_XFER "|" D_SENSOR_MGC3130_RESET "|" + D_SENSOR_SSPI_MISO "|" D_SENSOR_SSPI_MOSI "|" D_SENSOR_SSPI_SCLK "|" D_SENSOR_SSPI_CS "|" D_SENSOR_SSPI_DC "|" + D_SENSOR_RF_SENSOR "|" + D_SENSOR_AZ_TX "|" D_SENSOR_AZ_RX "|" + D_SENSOR_MAX31855_CS "|" D_SENSOR_MAX31855_CLK "|" D_SENSOR_MAX31855_DO "|" + D_SENSOR_BUTTON "1i|" D_SENSOR_BUTTON "2i|" D_SENSOR_BUTTON "3i|" D_SENSOR_BUTTON "4i|" + D_SENSOR_BUTTON "1in|" D_SENSOR_BUTTON "2in|" D_SENSOR_BUTTON "3in|" D_SENSOR_BUTTON "4in|" + D_SENSOR_NRG_SEL "|" D_SENSOR_NRG_SEL "i|" D_SENSOR_NRG_CF1 "|" D_SENSOR_HLW_CF "|" D_SENSOR_HJL_CF "|" + D_SENSOR_MCP39F5_TX "|" D_SENSOR_MCP39F5_RX "|" D_SENSOR_MCP39F5_RST "|" + D_SENSOR_PN532_TX "|" D_SENSOR_PN532_RX "|" + D_SENSOR_SM16716_CLK "|" D_SENSOR_SM16716_DAT "|" D_SENSOR_SM16716_POWER "|" + D_SENSOR_MY92X1_DI "|" D_SENSOR_MY92X1_DCKI "|" + D_SENSOR_CSE7766_TX "|" D_SENSOR_CSE7766_RX "|" + D_SENSOR_ARIRFRCV "|" D_SENSOR_TXD "|" D_SENSOR_RXD "|" + D_SENSOR_ROTARY "1a|" D_SENSOR_ROTARY "1b|" D_SENSOR_ROTARY "2a|" D_SENSOR_ROTARY "2b|" + D_SENSOR_HRE_CLOCK "|" D_SENSOR_HRE_DATA "|" + D_SENSOR_ADE7953_IRQ "|" + D_SENSOR_LED_LINK "|" D_SENSOR_LED_LINK "i|" + D_SENSOR_ARIRFSEL "|" + D_SENSOR_BUZZER "|" D_SENSOR_BUZZER "i|" + D_SENSOR_OLED_RESET "|" + D_SENSOR_SOLAXX1_TX "|" D_SENSOR_SOLAXX1_RX "|" + D_SENSOR_ZIGBEE_TXD "|" D_SENSOR_ZIGBEE_RXD "|" + D_SENSOR_RDM6300_RX "|" + D_SENSOR_IBEACON_TX "|" D_SENSOR_IBEACON_RX "|" + D_SENSOR_A4988_DIR "|" D_SENSOR_A4988_STP "|" D_SENSOR_A4988_ENA "|" D_SENSOR_A4988_MS1 "|" D_SENSOR_A4988_MS2 "|" D_SENSOR_A4988_MS3 "|" + D_SENSOR_DDS2382_TX "|" D_SENSOR_DDS2382_RX "|" + D_SENSOR_DDSU666_TX "|" D_SENSOR_DDSU666_RX "|" + D_SENSOR_SM2135_CLK "|" D_SENSOR_SM2135_DAT "|" + D_SENSOR_DEEPSLEEP "|" D_SENSOR_EXS_ENABLE "|" + D_SENSOR_SLAVE_TX "|" D_SENSOR_SLAVE_RX "|" D_SENSOR_SLAVE_RESET "|" D_SENSOR_SLAVE_RESET "i|" + D_SENSOR_HPMA_RX "|" D_SENSOR_HPMA_TX "|" + D_SENSOR_GPS_RX "|" D_SENSOR_GPS_TX "|" + D_SENSOR_DS18X20 "o|" D_SENSOR_DHT11 "o|" + D_SENSOR_HM10_RX "|" D_SENSOR_HM10_TX "|" + D_SENSOR_LE01MR_RX "|" D_SENSOR_LE01MR_TX "|" + D_SENSOR_CC1101_GDO0 "|" D_SENSOR_CC1101_GDO2 "|" + D_SENSOR_HRXL_RX "|" + D_SENSOR_ELECTRIQ_MOODL "|" + D_SENSOR_AS3935 "|" D_SENSOR_PMS5003_TX + ; + +const char kSensorNamesFixed[] PROGMEM = + D_SENSOR_SPI_MISO "|" D_SENSOR_SPI_MOSI "|" D_SENSOR_SPI_CLK "|" + D_SENSOR_USER; + +const uint8_t kGpioNiceList[] PROGMEM = { + GPIO_NONE, // Not used + GPIO_KEY1, // Buttons + GPIO_KEY1_NP, + GPIO_KEY1_INV, + GPIO_KEY1_INV_NP, + GPIO_KEY2, + GPIO_KEY2_NP, + GPIO_KEY2_INV, + GPIO_KEY2_INV_NP, + GPIO_KEY3, + GPIO_KEY3_NP, + GPIO_KEY3_INV, + GPIO_KEY3_INV_NP, + GPIO_KEY4, + GPIO_KEY4_NP, + GPIO_KEY4_INV, + GPIO_KEY4_INV_NP, + GPIO_SWT1, // User connected external switches + GPIO_SWT1_NP, + GPIO_SWT2, + GPIO_SWT2_NP, + GPIO_SWT3, + GPIO_SWT3_NP, + GPIO_SWT4, + GPIO_SWT4_NP, + GPIO_SWT5, + GPIO_SWT5_NP, + GPIO_SWT6, + GPIO_SWT6_NP, + GPIO_SWT7, + GPIO_SWT7_NP, + GPIO_SWT8, + GPIO_SWT8_NP, + GPIO_REL1, // Relays + GPIO_REL1_INV, + GPIO_REL2, + GPIO_REL2_INV, + GPIO_REL3, + GPIO_REL3_INV, + GPIO_REL4, + GPIO_REL4_INV, + GPIO_REL5, + GPIO_REL5_INV, + GPIO_REL6, + GPIO_REL6_INV, + GPIO_REL7, + GPIO_REL7_INV, + GPIO_REL8, + GPIO_REL8_INV, + GPIO_LED1, // Leds + GPIO_LED1_INV, + GPIO_LED2, + GPIO_LED2_INV, + GPIO_LED3, + GPIO_LED3_INV, + GPIO_LED4, + GPIO_LED4_INV, + GPIO_LEDLNK, // Link led + GPIO_LEDLNK_INV, // Inverted link led + GPIO_PWM1, // RGB Red or C Cold White + GPIO_PWM1_INV, + GPIO_PWM2, // RGB Green or CW Warm White + GPIO_PWM2_INV, + GPIO_PWM3, // RGB Blue + GPIO_PWM3_INV, + GPIO_PWM4, // RGBW (Cold) White + GPIO_PWM4_INV, + GPIO_PWM5, // RGBCW Warm White + GPIO_PWM5_INV, +#ifdef USE_COUNTER + GPIO_CNTR1, // Counters + GPIO_CNTR1_NP, + GPIO_CNTR2, + GPIO_CNTR2_NP, + GPIO_CNTR3, + GPIO_CNTR3_NP, + GPIO_CNTR4, + GPIO_CNTR4_NP, +#endif +#ifdef USE_BUZZER + GPIO_BUZZER, // Buzzer + GPIO_BUZZER_INV, // Inverted buzzer +#endif + GPIO_TXD, // Serial interface + GPIO_RXD, // Serial interface +#ifdef USE_I2C + GPIO_I2C_SCL, // I2C SCL + GPIO_I2C_SDA, // I2C SDA +#endif +#ifdef USE_SPI + GPIO_SPI_CS, // SPI Chip Select + GPIO_SPI_DC, // SPI Data Direction + GPIO_SSPI_MISO, // Software SPI Master Input Slave Output + GPIO_SSPI_MOSI, // Software SPI Master Output Slave Input + GPIO_SSPI_SCLK, // Software SPI Serial Clock + GPIO_SSPI_CS, // Software SPI Chip Select + GPIO_SSPI_DC, // Software SPI Data or Command +#endif +#ifdef USE_DISPLAY + GPIO_BACKLIGHT, // Display backlight control + GPIO_OLED_RESET, // OLED Display Reset +#endif +#ifdef USE_DHT + GPIO_DHT11, // DHT11 + GPIO_DHT22, // DHT21, DHT22, AM2301, AM2302, AM2321 + GPIO_SI7021, // iTead SI7021 + GPIO_DHT11_OUT, // Pseudo Single wire DHT11, DHT21, DHT22, AM2301, AM2302, AM2321 +#endif +#ifdef USE_DS18x20 + GPIO_DSB, // Single wire DS18B20 or DS18S20 + GPIO_DSB_OUT, // Pseudo Single wire DS18B20 or DS18S20 +#endif + +// Light +#ifdef USE_LIGHT +#ifdef USE_WS2812 + GPIO_WS2812, // WS2812 Led string +#endif +#ifdef USE_ARILUX_RF + GPIO_ARIRFRCV, // AriLux RF Receive input + GPIO_ARIRFSEL, // Arilux RF Receive input selected +#endif +#ifdef USE_MY92X1 + GPIO_DI, // my92x1 PWM input + GPIO_DCKI, // my92x1 CLK input +#endif // USE_MY92X1 +#ifdef USE_SM16716 + GPIO_SM16716_CLK, // SM16716 CLOCK + GPIO_SM16716_DAT, // SM16716 DATA + GPIO_SM16716_SEL, // SM16716 SELECT +#endif // USE_SM16716 +#ifdef USE_SM2135 + GPIO_SM2135_CLK, // SM2135 CLOCK + GPIO_SM2135_DAT, // SM2135 DATA +#endif // USE_SM2135 +#ifdef USE_TUYA_MCU + GPIO_TUYA_TX, // Tuya Serial interface + GPIO_TUYA_RX, // Tuya Serial interface +#endif +#ifdef USE_EXS_DIMMER + GPIO_EXS_ENABLE, // EXS MCU Enable +#endif +#ifdef USE_ELECTRIQ_MOODL + GPIO_ELECTRIQ_MOODL_TX, +#endif +#endif // USE_LIGHT + +#if defined(USE_IR_REMOTE) || defined(USE_IR_REMOTE_FULL) + GPIO_IRSEND, // IR remote +#if defined(USE_IR_RECEIVE) || defined(USE_IR_REMOTE_FULL) + GPIO_IRRECV, // IR receiver +#endif +#endif + +#ifdef USE_RC_SWITCH + GPIO_RFSEND, // RF transmitter + GPIO_RFRECV, // RF receiver +#endif +#ifdef USE_RF_SENSOR + GPIO_RF_SENSOR, // Rf receiver with sensor decoding +#endif +#ifdef USE_SR04 + GPIO_SR04_TRIG, // SR04 Tri/TXgger pin + GPIO_SR04_ECHO, // SR04 Ech/RXo pin +#endif +#ifdef USE_TM1638 + GPIO_TM16CLK, // TM1638 Clock + GPIO_TM16DIO, // TM1638 Data I/O + GPIO_TM16STB, // TM1638 Strobe +#endif +#ifdef USE_HX711 + GPIO_HX711_SCK, // HX711 Load Cell clock + GPIO_HX711_DAT, // HX711 Load Cell data +#endif + +// Energy sensors +#ifdef USE_ENERGY_SENSOR +#ifdef USE_HLW8012 + GPIO_NRG_SEL, // HLW8012/HLJ-01 Sel output (1 = Voltage) + GPIO_NRG_SEL_INV, // HLW8012/HLJ-01 Sel output (0 = Voltage) + GPIO_NRG_CF1, // HLW8012/HLJ-01 CF1 voltage / current + GPIO_HLW_CF, // HLW8012 CF power + GPIO_HJL_CF, // HJL-01/BL0937 CF power +#endif +#if defined(USE_I2C) && defined(USE_ADE7953) + GPIO_ADE7953_IRQ, // ADE7953 IRQ +#endif +#ifdef USE_CSE7766 + GPIO_CSE7766_TX, // CSE7766 Serial interface (S31 and Pow R2) + GPIO_CSE7766_RX, // CSE7766 Serial interface (S31 and Pow R2) +#endif +#ifdef USE_MCP39F501 + GPIO_MCP39F5_TX, // MCP39F501 Serial interface (Shelly2) + GPIO_MCP39F5_RX, // MCP39F501 Serial interface (Shelly2) + GPIO_MCP39F5_RST, // MCP39F501 Reset (Shelly2) +#endif +#if defined(USE_PZEM004T) || defined(USE_PZEM_AC) || defined(USE_PZEM_DC) + GPIO_PZEM0XX_TX, // PZEM0XX Serial interface +#endif +#ifdef USE_PZEM004T + GPIO_PZEM004_RX, // PZEM004T Serial interface +#endif +#ifdef USE_PZEM_AC + GPIO_PZEM016_RX, // PZEM-014,016 Serial Modbus interface +#endif +#ifdef USE_PZEM_DC + GPIO_PZEM017_RX, // PZEM-003,017 Serial Modbus interface +#endif +#ifdef USE_SDM120 + GPIO_SDM120_TX, // SDM120 Serial interface + GPIO_SDM120_RX, // SDM120 Serial interface +#endif +#ifdef USE_SDM630 + GPIO_SDM630_TX, // SDM630 Serial interface + GPIO_SDM630_RX, // SDM630 Serial interface +#endif +#ifdef USE_DDS2382 + GPIO_DDS2382_TX, // DDS2382 Serial interface + GPIO_DDS2382_RX, // DDS2382 Serial interface +#endif +#ifdef USE_DDSU666 + GPIO_DDSU666_TX, // DDSU666 Serial interface + GPIO_DDSU666_RX, // DDSU666 Serial interface +#endif // USE_DDSU666 +#ifdef USE_SOLAX_X1 + GPIO_SOLAXX1_TX, // Solax Inverter tx pin + GPIO_SOLAXX1_RX, // Solax Inverter rx pin +#endif // USE_SOLAX_X1 +#ifdef USE_LE01MR + GPIO_LE01MR_RX, // F7F LE-01MR energy meter rx pin + GPIO_LE01MR_TX, // F7F LE-01MR energy meter tx pin +#endif // IFDEF:USE_LE01MR +#endif // USE_ENERGY_SENSOR + +// Serial +#ifdef USE_SERIAL_BRIDGE + GPIO_SBR_TX, // Serial Bridge Serial interface + GPIO_SBR_RX, // Serial Bridge Serial interface +#endif +#ifdef USE_ZIGBEE + GPIO_ZIGBEE_TX, // Zigbee Serial interface + GPIO_ZIGBEE_RX, // Zigbee Serial interface +#endif +#ifdef USE_MHZ19 + GPIO_MHZ_TXD, // MH-Z19 Serial interface + GPIO_MHZ_RXD, // MH-Z19 Serial interface +#endif +#ifdef USE_SENSEAIR + GPIO_SAIR_TX, // SenseAir Serial interface + GPIO_SAIR_RX, // SenseAir Serial interface +#endif +#ifdef USE_NOVA_SDS + GPIO_SDS0X1_TX, // Nova Fitness SDS011 Serial interface + GPIO_SDS0X1_RX, // Nova Fitness SDS011 Serial interface +#endif +#ifdef USE_HPMA + GPIO_HPMA_TX, // Honeywell HPMA115S0 Serial interface + GPIO_HPMA_RX, // Honeywell HPMA115S0 Serial interface +#endif +#ifdef USE_PMS5003 + GPIO_PMS5003_TX, // Plantower PMS5003 Serial interface + GPIO_PMS5003_RX, // Plantower PMS5003 Serial interface +#endif +#if defined(USE_TX20_WIND_SENSOR) || defined(USE_TX23_WIND_SENSOR) + GPIO_TX2X_TXD_BLACK, // TX20/TX23 Transmission Pin +#endif +#ifdef USE_MP3_PLAYER + GPIO_MP3_DFR562, // RB-DFR-562, DFPlayer Mini MP3 Player Serial interface +#endif +#ifdef USE_AZ7798 + GPIO_AZ_TXD, // AZ-Instrument 7798 CO2 datalogger Serial interface + GPIO_AZ_RXD, // AZ-Instrument 7798 CO2 datalogger Serial interface +#endif +#ifdef USE_PN532_HSU + GPIO_PN532_TXD, // PN532 HSU Tx + GPIO_PN532_RXD, // PN532 HSU Rx +#endif +#ifdef USE_TASMOTA_SLAVE + GPIO_TASMOTASLAVE_TXD, // Tasmota Slave TX + GPIO_TASMOTASLAVE_RXD, // Tasmota Slave RX + GPIO_TASMOTASLAVE_RST, // Tasmota Slave Reset + GPIO_TASMOTASLAVE_RST_INV, // Tasmota Slave Reset Inverted +#endif +#ifdef USE_RDM6300 + GPIO_RDM6300_RX, +#endif +#ifdef USE_IBEACON + GPIO_IBEACON_RX, + GPIO_IBEACON_TX, +#endif +#ifdef USE_GPS + GPIO_GPS_RX, // GPS serial interface + GPIO_GPS_TX, // GPS serial interface +#endif +#ifdef USE_HM10 + GPIO_HM10_RX, // GPS serial interface + GPIO_HM10_TX, // GPS serial interface +#endif + +#ifdef USE_MGC3130 + GPIO_MGC3130_XFER, + GPIO_MGC3130_RESET, +#endif +#ifdef USE_MAX31855 + GPIO_MAX31855CS, // MAX31855 Serial interface + GPIO_MAX31855CLK, // MAX31855 Serial interface + GPIO_MAX31855DO, // MAX31855 Serial interface +#endif +#ifdef ROTARY_V1 + GPIO_ROT1A, // Rotary switch1 A Pin + GPIO_ROT1B, // Rotary switch1 B Pin + GPIO_ROT2A, // Rotary switch2 A Pin + GPIO_ROT2B, // Rotary switch2 B Pin +#endif +#ifdef USE_HRE + GPIO_HRE_CLOCK, + GPIO_HRE_DATA, +#endif +#ifdef USE_A4988_STEPPER + GPIO_A4988_DIR, // A4988 direction pin + GPIO_A4988_STP, // A4988 step pin + // folowing are not mandatory + GPIO_A4988_ENA, // A4988 enabled pin + GPIO_A4988_MS1, // A4988 microstep pin1 + GPIO_A4988_MS2, // A4988 microstep pin2 + GPIO_A4988_MS3, // A4988 microstep pin3 +#endif +#ifdef USE_DEEPSLEEP + GPIO_DEEPSLEEP, +#endif +#ifdef USE_KEELOQ + GPIO_CC1101_GDO0, // CC1101 pin for RX + GPIO_CC1101_GDO2, // CC1101 pin for RX +#endif +#ifdef USE_HRXL + GPIO_HRXL_RX, +#endif +#ifdef USE_AS3935 + GPIO_AS3935, +#endif +}; + +/********************************************************************************************/ + +// User selectable ADC0 functionality +enum UserSelectableAdc0 { + ADC0_NONE, // Not used + ADC0_INPUT, // Analog input + ADC0_TEMP, // Thermistor + ADC0_LIGHT, // Light sensor + ADC0_BUTTON, // Button + ADC0_BUTTON_INV, + ADC0_RANGE, // Range + ADC0_CT_POWER, // Current +// ADC0_SWITCH, // Switch +// ADC0_SWITCH_INV, + ADC0_END }; + +// Programmer selectable ADC0 functionality +enum ProgramSelectableAdc0 { + ADC0_FIX_START = 14, + ADC0_USER, // User configurable needs to be 15 + ADC0_MAX }; + +// Text in webpage Module Parameters and commands ADC +const char kAdc0Names[] PROGMEM = + D_SENSOR_NONE "|" D_ANALOG_INPUT "|" + D_TEMPERATURE "|" D_LIGHT "|" + D_SENSOR_BUTTON "|" D_SENSOR_BUTTON "i|" + D_RANGE "|" + D_CT_POWER "|" +// D_SENSOR_SWITCH "|" D_SENSOR_SWITCH "i|" + ; + +/********************************************************************************************/ + +// esp32 has more pins +#define USER_MODULE 255 +#define MAX_GPIO_PIN 40 // Number of supported GPIO +#define MIN_FLASH_PINS 4 // Number of flash chip pins unusable for configuration (GPIO6, 7, 8 and 11) +#define MAX_USER_PINS 36 // MAX_GPIO_PIN - MIN_FLASH_PINS +#define ADC0_PIN 33 // Pin number of ADC0 +#define WEMOS_MODULE 0 // Wemos module + +// 0 1 2 3 4 5 6 7 8 9101112131415161718192021222324252627282930313233343536373839 +const char PINS_WEMOS[] PROGMEM = "IOTXIORXIOIOflashcFLFLolIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOA6A7A0IoIoA3"; + +/********************************************************************************************/ + +typedef struct MYIO { + uint8_t io[MAX_GPIO_PIN]; +} myio; + +typedef struct MYCFGIO { + uint8_t io[MAX_GPIO_PIN - MIN_FLASH_PINS]; +} mycfgio; + +#define GPIO_FLAG_USED 0 // Currently two flags used + +#define GPIO_FLAG_SPARE04 16 +#define GPIO_FLAG_SPARE05 32 +#define GPIO_FLAG_SPARE06 64 +#define GPIO_FLAG_SPARE07 128 + +typedef union { + uint8_t data; + struct { + uint8_t adc0 : 4; // Allow ADC0 when define USE_ADC_VCC is disabled + uint8_t spare04 : 1; + uint8_t spare05 : 1; + uint8_t spare06 : 1; + uint8_t spare07 : 1; + }; +} gpio_flag; + +typedef struct MYTMPLT { + mycfgio gp; + gpio_flag flag; +} mytmplt; + /********************************************************************************************/ // Supported hardware modules + enum SupportedModules { WEMOS, ESP32_CAM_AITHINKER, MAXMODULE}; +#define USER_MODULE 255 + const char kModuleNames[] PROGMEM = "ESP32-DevKit|ESP32 Cam AiThinker"; @@ -102,6 +829,10 @@ const mytmplt kModules PROGMEM = 0 // Flag }; +#else // FINAL_ESP32 +#include "tasmota_template_ESP32_final.h" +#endif // FINAL_ESP32 + #endif // ESP32 #endif // _TASMOTA_TEMPLATE_ESP32_H_ diff --git a/tasmota/tasmota_template_ESP32_final.h b/tasmota/tasmota_template_ESP32_final.h new file mode 100644 index 000000000..e13dbec24 --- /dev/null +++ b/tasmota/tasmota_template_ESP32_final.h @@ -0,0 +1,742 @@ +/* + tasmota_template_ESP32_final.h - template settings for Tasmota + + Copyright (C) 2020 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _TASMOTA_TEMPLATE_ESP32_FINAL_H_ +#define _TASMOTA_TEMPLATE_ESP32_FINAL_H_ + +#ifdef ESP32 + +// Hardware has no ESP32 +#undef USE_TUYA_DIMMER +#undef USE_PWM_DIMMER +#undef USE_EXS_DIMMER +#undef USE_ARMTRONIX_DIMMERS +#undef USE_SONOFF_RF +#undef USE_SONOFF_SC +#undef USE_SONOFF_IFAN +#undef USE_SONOFF_L1 +#undef USE_SONOFF_D1 +#undef USE_RF_FLASH + +// Not ported (yet) +#undef USE_DISCOVERY +#undef USE_ADC_VCC // Needs to be ported +#undef USE_DEEPSLEEP +#undef USE_MY92X1 +#undef USE_TUYA_MCU +#undef USE_PS_16_DZ + +enum UserSelectablePins { + GPIO_NONE, // Not used + GPIO_KEY1, // 4 x Button usually connected to GPIO0 + GPIO_KEY1_NP, + GPIO_KEY1_INV, + GPIO_KEY1_INV_NP, + GPIO_SWT1, // 8 x User connected external switches + GPIO_SWT1_NP, + GPIO_REL1, // 8 x Relays + GPIO_REL1_INV, + GPIO_LED1, // 4 x Leds + GPIO_LED1_INV, + GPIO_CNTR1, // 4 x Counter + GPIO_CNTR1_NP, + GPIO_PWM1, // 5 x PWM + GPIO_PWM1_INV, + GPIO_BUZZER, // Buzzer + GPIO_BUZZER_INV, // Inverted buzzer + GPIO_LEDLNK, // Link led + GPIO_LEDLNK_INV, // Inverted link led + GPIO_I2C_SCL, // I2C SCL + GPIO_I2C_SDA, // I2C SDA + GPIO_SPI_MISO, // SPI MISO + GPIO_SPI_MOSI, // SPI MOSI + GPIO_SPI_CLK, // SPI Clk + GPIO_SPI_CS, // SPI Chip Select + GPIO_SPI_DC, // SPI Data Direction + GPIO_SSPI_MISO, // Software SPI Master Input Slave Output + GPIO_SSPI_MOSI, // Software SPI Master Output Slave Input + GPIO_SSPI_SCLK, // Software SPI Serial Clock + GPIO_SSPI_CS, // Software SPI Chip Select + GPIO_SSPI_DC, // Software SPI Data or Command + GPIO_BACKLIGHT, // Display backlight control + GPIO_OLED_RESET, // OLED Display Reset + GPIO_IRSEND, // IR remote + GPIO_IRRECV, // IR receiver + GPIO_RFSEND, // RF transmitter + GPIO_RFRECV, // RF receiver + GPIO_DHT11, // DHT11 + GPIO_DHT22, // DHT21, DHT22, AM2301, AM2302, AM2321 + GPIO_SI7021, // iTead SI7021 + GPIO_DHT11_OUT, // Pseudo Single wire DHT11, DHT21, DHT22, AM2301, AM2302, AM2321 + GPIO_DSB, // Single wire DS18B20 or DS18S20 + GPIO_DSB_OUT, // Pseudo Single wire DS18B20 or DS18S20 + GPIO_WS2812, // WS2812 Led string + GPIO_MHZ_TXD, // MH-Z19 Serial interface + GPIO_MHZ_RXD, // MH-Z19 Serial interface + GPIO_PZEM0XX_TX, // PZEM0XX Serial interface + GPIO_PZEM004_RX, // PZEM004T Serial interface + GPIO_PZEM016_RX, // PZEM-014,016 Serial Modbus interface + GPIO_PZEM017_RX, // PZEM-003,017 Serial Modbus interface + GPIO_SAIR_TX, // SenseAir Serial interface + GPIO_SAIR_RX, // SenseAir Serial interface + GPIO_PMS5003_TX, // Plantower PMS5003 Serial interface + GPIO_PMS5003_RX, // Plantower PMS5003 Serial interface + GPIO_SDS0X1_TX, // Nova Fitness SDS011 Serial interface + GPIO_SDS0X1_RX, // Nova Fitness SDS011 Serial interface + GPIO_SBR_TX, // Serial Bridge Serial interface + GPIO_SBR_RX, // Serial Bridge Serial interface + GPIO_SR04_TRIG, // SR04 Trigger/TX pin + GPIO_SR04_ECHO, // SR04 Echo/RX pin + GPIO_SDM120_TX, // SDM120 Serial interface + GPIO_SDM120_RX, // SDM120 Serial interface + GPIO_SDM630_TX, // SDM630 Serial interface + GPIO_SDM630_RX, // SDM630 Serial interface + GPIO_TM16CLK, // TM1638 Clock + GPIO_TM16DIO, // TM1638 Data I/O + GPIO_TM16STB, // TM1638 Strobe + GPIO_MP3_DFR562, // RB-DFR-562, DFPlayer Mini MP3 Player + GPIO_HX711_SCK, // HX711 Load Cell clock + GPIO_HX711_DAT, // HX711 Load Cell data + GPIO_TX2X_TXD_BLACK, // TX20/TX23 Transmission Pin + GPIO_TUYA_TX, // Tuya Serial interface + GPIO_TUYA_RX, // Tuya Serial interface + GPIO_MGC3130_XFER, // MGC3130 Transfer + GPIO_MGC3130_RESET, // MGC3130 Reset + GPIO_RF_SENSOR, // Rf receiver with sensor decoding + GPIO_AZ_TXD, // AZ-Instrument 7798 Serial interface + GPIO_AZ_RXD, // AZ-Instrument 7798 Serial interface + GPIO_MAX31855CS, // MAX31855 Serial interface + GPIO_MAX31855CLK, // MAX31855 Serial interface + GPIO_MAX31855DO, // MAX31855 Serial interface + GPIO_NRG_SEL, // HLW8012/HLJ-01 Sel output (1 = Voltage) + GPIO_NRG_SEL_INV, // HLW8012/HLJ-01 Sel output (0 = Voltage) + GPIO_NRG_CF1, // HLW8012/HLJ-01 CF1 voltage / current + GPIO_HLW_CF, // HLW8012 CF power + GPIO_HJL_CF, // HJL-01/BL0937 CF power + GPIO_MCP39F5_TX, // MCP39F501 Serial interface (Shelly2) + GPIO_MCP39F5_RX, // MCP39F501 Serial interface (Shelly2) + GPIO_MCP39F5_RST, // MCP39F501 Reset (Shelly2) + GPIO_PN532_TXD, // PN532 NFC Serial Tx + GPIO_PN532_RXD, // PN532 NFC Serial Rx + GPIO_SM16716_CLK, // SM16716 CLOCK + GPIO_SM16716_DAT, // SM16716 DATA + GPIO_SM16716_SEL, // SM16716 SELECT + GPIO_DI, // my92x1 PWM input + GPIO_DCKI, // my92x1 CLK input + GPIO_CSE7766_TX, // CSE7766 Serial interface (S31 and Pow R2) - Not used anymore 20200121 + GPIO_CSE7766_RX, // CSE7766 Serial interface (S31 and Pow R2) + GPIO_ARIRFRCV, // AriLux RF Receive input + GPIO_TXD, // Serial interface + GPIO_RXD, // Serial interface + GPIO_ROT1A, // Rotary switch1 A Pin + GPIO_ROT1B, // Rotary switch1 B Pin + GPIO_ROT2A, // Rotary switch2 A Pin + GPIO_ROT2B, // Rotary switch2 B Pin + GPIO_HRE_CLOCK, // Clock/Power line for HR-E Water Meter + GPIO_HRE_DATA, // Data line for HR-E Water Meter + GPIO_ADE7953_IRQ, // ADE7953 IRQ + GPIO_ARIRFSEL, // Arilux RF Receive input selected + GPIO_SOLAXX1_TX, // Solax Inverter tx pin + GPIO_SOLAXX1_RX, // Solax Inverter rx pin + GPIO_ZIGBEE_TX, // Zigbee Serial interface + GPIO_ZIGBEE_RX, // Zigbee Serial interface + GPIO_RDM6300_RX, // RDM6300 RX + GPIO_IBEACON_TX, // HM17 IBEACON TX + GPIO_IBEACON_RX, // HM17 IBEACON RX + GPIO_A4988_DIR, // A4988 direction pin + GPIO_A4988_STP, // A4988 step pin + GPIO_A4988_ENA, // A4988 enabled pin + GPIO_A4988_MS1, // A4988 microstep pin1 + GPIO_A4988_MS2, // A4988 microstep pin2 + GPIO_A4988_MS3, // A4988 microstep pin3 + GPIO_DDS2382_TX, // DDS2382 Serial interface + GPIO_DDS2382_RX, // DDS2382 Serial interface + GPIO_DDSU666_TX, // DDSU666 Serial interface + GPIO_DDSU666_RX, // DDSU666 Serial interface + GPIO_SM2135_CLK, // SM2135 Clk + GPIO_SM2135_DAT, // SM2135 Dat + GPIO_DEEPSLEEP, // Kill switch for deepsleep + GPIO_EXS_ENABLE, // EXS MCU Enable + GPIO_TASMOTASLAVE_TXD, // Slave TX + GPIO_TASMOTASLAVE_RXD, // Slave RX + GPIO_TASMOTASLAVE_RST, // Slave Reset Pin + GPIO_TASMOTASLAVE_RST_INV, // Slave Reset Inverted + GPIO_HPMA_RX, // Honeywell HPMA115S0 Serial interface + GPIO_HPMA_TX, // Honeywell HPMA115S0 Serial interface + GPIO_GPS_RX, // GPS serial interface + GPIO_GPS_TX, // GPS serial interface + GPIO_HM10_RX, // HM10-BLE-Mijia-bridge serial interface + GPIO_HM10_TX, // HM10-BLE-Mijia-bridge serial interface + GPIO_LE01MR_RX, // F&F LE-01MR energy meter + GPIO_LE01MR_TX, // F&F LE-01MR energy meter + GPIO_CC1101_GDO0, // CC1101 pin for RX + GPIO_CC1101_GDO2, // CC1101 pin for RX + GPIO_HRXL_RX, // Data from MaxBotix HRXL sonar range sensor + GPIO_ELECTRIQ_MOODL_TX, // ElectriQ iQ-wifiMOODL Serial TX + GPIO_AS3935, +/* + ADC0_INPUT, // Analog input + ADC0_TEMP, // Thermistor + ADC0_LIGHT, // Light sensor + ADC0_BUTTON, // Button + ADC0_BUTTON_INV, + ADC0_RANGE, // Range + ADC0_CT_POWER, // Current +*/ + GPIO_SENSOR_END }; + +enum ProgramSelectablePins { +// GPIO_FIX_START = 254, + GPIO_FIX_START = 2046, + GPIO_USER, // User configurable needs to be 2047 + GPIO_MAX }; + +// Text in webpage Module Parameters and commands GPIOS and GPIO +const char kSensorNames[] PROGMEM = + D_SENSOR_NONE "|" + D_SENSOR_BUTTON "|" + D_SENSOR_BUTTON "n|" + D_SENSOR_BUTTON "i|" + D_SENSOR_BUTTON "in|" + D_SENSOR_SWITCH "|" + D_SENSOR_SWITCH "n|" + D_SENSOR_RELAY "|" + D_SENSOR_RELAY "i|" + D_SENSOR_LED "|" + D_SENSOR_LED "i|" + D_SENSOR_COUNTER "|" + D_SENSOR_COUNTER "n|" + D_SENSOR_PWM "|" + D_SENSOR_PWM "i|" + D_SENSOR_BUZZER "|" + D_SENSOR_BUZZER "i|" + D_SENSOR_LED_LINK "|" D_SENSOR_LED_LINK "i|" + D_SENSOR_I2C_SCL "|" D_SENSOR_I2C_SDA "|" + D_SENSOR_SPI_MISO "|" D_SENSOR_SPI_MOSI "|" D_SENSOR_SPI_CLK "|" D_SENSOR_SPI_CS "|" D_SENSOR_SPI_DC "|" + D_SENSOR_SSPI_MISO "|" D_SENSOR_SSPI_MOSI "|" D_SENSOR_SSPI_SCLK "|" D_SENSOR_SSPI_CS "|" D_SENSOR_SSPI_DC "|" + D_SENSOR_BACKLIGHT "|" D_SENSOR_OLED_RESET "|" + D_SENSOR_IRSEND "|" D_SENSOR_IRRECV "|" + D_SENSOR_RFSEND "|" D_SENSOR_RFRECV "|" + D_SENSOR_DHT11 "|" D_SENSOR_AM2301 "|" D_SENSOR_SI7021 "|" D_SENSOR_DHT11 "o|" + D_SENSOR_DS18X20 "|" D_SENSOR_DS18X20 "o|" + D_SENSOR_WS2812 "|" + D_SENSOR_MHZ_TX "|" D_SENSOR_MHZ_RX "|" + D_SENSOR_PZEM0XX_TX "|" D_SENSOR_PZEM004_RX "|" D_SENSOR_PZEM016_RX "|" D_SENSOR_PZEM017_RX "|" + D_SENSOR_SAIR_TX "|" D_SENSOR_SAIR_RX "|" + D_SENSOR_PMS5003_TX "|" D_SENSOR_PMS5003_RX "|" + D_SENSOR_SDS0X1_TX "|" D_SENSOR_SDS0X1_RX "|" + D_SENSOR_SBR_TX "|" D_SENSOR_SBR_RX "|" + D_SENSOR_SR04_TRIG "|" D_SENSOR_SR04_ECHO "|" + D_SENSOR_SDM120_TX "|" D_SENSOR_SDM120_RX "|" + D_SENSOR_SDM630_TX "|" D_SENSOR_SDM630_RX "|" + D_SENSOR_TM1638_CLK "|" D_SENSOR_TM1638_DIO "|" D_SENSOR_TM1638_STB "|" + D_SENSOR_DFR562 "|" + D_SENSOR_HX711_SCK "|" D_SENSOR_HX711_DAT "|" + D_SENSOR_TX2X_TX "|" + D_SENSOR_TUYA_TX "|" D_SENSOR_TUYA_RX "|" + D_SENSOR_MGC3130_XFER "|" D_SENSOR_MGC3130_RESET "|" + D_SENSOR_RF_SENSOR "|" + D_SENSOR_AZ_TX "|" D_SENSOR_AZ_RX "|" + D_SENSOR_MAX31855_CS "|" D_SENSOR_MAX31855_CLK "|" D_SENSOR_MAX31855_DO "|" + D_SENSOR_NRG_SEL "|" D_SENSOR_NRG_SEL "i|" D_SENSOR_NRG_CF1 "|" D_SENSOR_HLW_CF "|" D_SENSOR_HJL_CF "|" + D_SENSOR_MCP39F5_TX "|" D_SENSOR_MCP39F5_RX "|" D_SENSOR_MCP39F5_RST "|" + D_SENSOR_PN532_TX "|" D_SENSOR_PN532_RX "|" + D_SENSOR_SM16716_CLK "|" D_SENSOR_SM16716_DAT "|" D_SENSOR_SM16716_POWER "|" + D_SENSOR_MY92X1_DI "|" D_SENSOR_MY92X1_DCKI "|" + D_SENSOR_CSE7766_TX "|" D_SENSOR_CSE7766_RX "|" + D_SENSOR_ARIRFRCV "|" + D_SENSOR_TXD "|" D_SENSOR_RXD "|" + D_SENSOR_ROTARY "1a|" D_SENSOR_ROTARY "1b|" D_SENSOR_ROTARY "2a|" D_SENSOR_ROTARY "2b|" + D_SENSOR_HRE_CLOCK "|" D_SENSOR_HRE_DATA "|" + D_SENSOR_ADE7953_IRQ "|" + D_SENSOR_ARIRFSEL "|" + D_SENSOR_SOLAXX1_TX "|" D_SENSOR_SOLAXX1_RX "|" + D_SENSOR_ZIGBEE_TXD "|" D_SENSOR_ZIGBEE_RXD "|" + D_SENSOR_RDM6300_RX "|" + D_SENSOR_IBEACON_TX "|" D_SENSOR_IBEACON_RX "|" + D_SENSOR_A4988_DIR "|" D_SENSOR_A4988_STP "|" D_SENSOR_A4988_ENA "|" D_SENSOR_A4988_MS1 "|" D_SENSOR_A4988_MS2 "|" D_SENSOR_A4988_MS3 "|" + D_SENSOR_DDS2382_TX "|" D_SENSOR_DDS2382_RX "|" + D_SENSOR_DDSU666_TX "|" D_SENSOR_DDSU666_RX "|" + D_SENSOR_SM2135_CLK "|" D_SENSOR_SM2135_DAT "|" + D_SENSOR_DEEPSLEEP "|" D_SENSOR_EXS_ENABLE "|" + D_SENSOR_SLAVE_TX "|" D_SENSOR_SLAVE_RX "|" D_SENSOR_SLAVE_RESET "|" D_SENSOR_SLAVE_RESET "i|" + D_SENSOR_HPMA_RX "|" D_SENSOR_HPMA_TX "|" + D_SENSOR_GPS_RX "|" D_SENSOR_GPS_TX "|" + D_SENSOR_HM10_RX "|" D_SENSOR_HM10_TX "|" + D_SENSOR_LE01MR_RX "|" D_SENSOR_LE01MR_TX "|" + D_SENSOR_CC1101_GDO0 "|" D_SENSOR_CC1101_GDO2 "|" + D_SENSOR_HRXL_RX "|" + D_SENSOR_ELECTRIQ_MOODL "|" + D_SENSOR_AS3935 "|" +/* + D_ANALOG_INPUT "|" + D_TEMPERATURE "|" D_LIGHT "|" + D_SENSOR_BUTTON "|" D_SENSOR_BUTTON "i|" + D_RANGE "|" + D_CT_POWER "|" +*/ + ; + +const char kSensorNamesFixed[] PROGMEM = + D_SENSOR_USER; + +const uint16_t kGpioNiceList[] PROGMEM = { + GPIO_NONE, // Not used + GPIO_KEY1, // Buttons + GPIO_KEY1_NP, + GPIO_KEY1_INV, + GPIO_KEY1_INV_NP, + GPIO_SWT1, // User connected external switches + GPIO_SWT1_NP, + GPIO_REL1, // Relays + GPIO_REL1_INV, + GPIO_LED1, // Leds + GPIO_LED1_INV, +#ifdef USE_COUNTER + GPIO_CNTR1, // Counters + GPIO_CNTR1_NP, +#endif + GPIO_PWM1, // RGB Red or C Cold White + GPIO_PWM1_INV, +#ifdef USE_BUZZER + GPIO_BUZZER, // Buzzer + GPIO_BUZZER_INV, // Inverted buzzer +#endif + GPIO_LEDLNK, // Link led + GPIO_LEDLNK_INV, // Inverted link led +#ifdef USE_I2C + GPIO_I2C_SCL, // I2C SCL + GPIO_I2C_SDA, // I2C SDA +#endif +#ifdef USE_SPI + GPIO_SPI_MISO, // SPI MISO + GPIO_SPI_MOSI, // SPI MOSI + GPIO_SPI_CLK, // SPI Clk + GPIO_SPI_CS, // SPI Chip Select + GPIO_SPI_DC, // SPI Data Direction + GPIO_SSPI_MISO, // Software SPI Master Input Slave Output + GPIO_SSPI_MOSI, // Software SPI Master Output Slave Input + GPIO_SSPI_SCLK, // Software SPI Serial Clock + GPIO_SSPI_CS, // Software SPI Chip Select + GPIO_SSPI_DC, // Software SPI Data or Command +#endif +#ifdef USE_DISPLAY + GPIO_BACKLIGHT, // Display backlight control + GPIO_OLED_RESET, // OLED Display Reset +#endif + + GPIO_TXD, // Serial interface + GPIO_RXD, // Serial interface + +#ifdef USE_DHT + GPIO_DHT11, // DHT11 + GPIO_DHT22, // DHT21, DHT22, AM2301, AM2302, AM2321 + GPIO_SI7021, // iTead SI7021 + GPIO_DHT11_OUT, // Pseudo Single wire DHT11, DHT21, DHT22, AM2301, AM2302, AM2321 +#endif +#ifdef USE_DS18x20 + GPIO_DSB, // Single wire DS18B20 or DS18S20 + GPIO_DSB_OUT, // Pseudo Single wire DS18B20 or DS18S20 +#endif + +// Light +#ifdef USE_LIGHT +#ifdef USE_WS2812 + GPIO_WS2812, // WS2812 Led string +#endif +#ifdef USE_ARILUX_RF + GPIO_ARIRFRCV, // AriLux RF Receive input + GPIO_ARIRFSEL, // Arilux RF Receive input selected +#endif +#ifdef USE_MY92X1 + GPIO_DI, // my92x1 PWM input + GPIO_DCKI, // my92x1 CLK input +#endif // USE_MY92X1 +#ifdef USE_SM16716 + GPIO_SM16716_CLK, // SM16716 CLOCK + GPIO_SM16716_DAT, // SM16716 DATA + GPIO_SM16716_SEL, // SM16716 SELECT +#endif // USE_SM16716 +#ifdef USE_SM2135 + GPIO_SM2135_CLK, // SM2135 CLOCK + GPIO_SM2135_DAT, // SM2135 DATA +#endif // USE_SM2135 +#ifdef USE_TUYA_MCU + GPIO_TUYA_TX, // Tuya Serial interface + GPIO_TUYA_RX, // Tuya Serial interface +#endif +#ifdef USE_EXS_DIMMER + GPIO_EXS_ENABLE, // EXS MCU Enable +#endif +#ifdef USE_ELECTRIQ_MOODL + GPIO_ELECTRIQ_MOODL_TX, +#endif +#endif // USE_LIGHT + +#if defined(USE_IR_REMOTE) || defined(USE_IR_REMOTE_FULL) + GPIO_IRSEND, // IR remote +#if defined(USE_IR_RECEIVE) || defined(USE_IR_REMOTE_FULL) + GPIO_IRRECV, // IR receiver +#endif +#endif + +#ifdef USE_RC_SWITCH + GPIO_RFSEND, // RF transmitter + GPIO_RFRECV, // RF receiver +#endif +#ifdef USE_RF_SENSOR + GPIO_RF_SENSOR, // Rf receiver with sensor decoding +#endif +#ifdef USE_SR04 + GPIO_SR04_TRIG, // SR04 Tri/TXgger pin + GPIO_SR04_ECHO, // SR04 Ech/RXo pin +#endif +#ifdef USE_TM1638 + GPIO_TM16CLK, // TM1638 Clock + GPIO_TM16DIO, // TM1638 Data I/O + GPIO_TM16STB, // TM1638 Strobe +#endif +#ifdef USE_HX711 + GPIO_HX711_SCK, // HX711 Load Cell clock + GPIO_HX711_DAT, // HX711 Load Cell data +#endif + +// Energy sensors +#ifdef USE_ENERGY_SENSOR +#ifdef USE_HLW8012 + GPIO_NRG_SEL, // HLW8012/HLJ-01 Sel output (1 = Voltage) + GPIO_NRG_SEL_INV, // HLW8012/HLJ-01 Sel output (0 = Voltage) + GPIO_NRG_CF1, // HLW8012/HLJ-01 CF1 voltage / current + GPIO_HLW_CF, // HLW8012 CF power + GPIO_HJL_CF, // HJL-01/BL0937 CF power +#endif +#if defined(USE_I2C) && defined(USE_ADE7953) + GPIO_ADE7953_IRQ, // ADE7953 IRQ +#endif +#ifdef USE_CSE7766 + GPIO_CSE7766_TX, // CSE7766 Serial interface (S31 and Pow R2) + GPIO_CSE7766_RX, // CSE7766 Serial interface (S31 and Pow R2) +#endif +#ifdef USE_MCP39F501 + GPIO_MCP39F5_TX, // MCP39F501 Serial interface (Shelly2) + GPIO_MCP39F5_RX, // MCP39F501 Serial interface (Shelly2) + GPIO_MCP39F5_RST, // MCP39F501 Reset (Shelly2) +#endif +#if defined(USE_PZEM004T) || defined(USE_PZEM_AC) || defined(USE_PZEM_DC) + GPIO_PZEM0XX_TX, // PZEM0XX Serial interface +#endif +#ifdef USE_PZEM004T + GPIO_PZEM004_RX, // PZEM004T Serial interface +#endif +#ifdef USE_PZEM_AC + GPIO_PZEM016_RX, // PZEM-014,016 Serial Modbus interface +#endif +#ifdef USE_PZEM_DC + GPIO_PZEM017_RX, // PZEM-003,017 Serial Modbus interface +#endif +#ifdef USE_SDM120 + GPIO_SDM120_TX, // SDM120 Serial interface + GPIO_SDM120_RX, // SDM120 Serial interface +#endif +#ifdef USE_SDM630 + GPIO_SDM630_TX, // SDM630 Serial interface + GPIO_SDM630_RX, // SDM630 Serial interface +#endif +#ifdef USE_DDS2382 + GPIO_DDS2382_TX, // DDS2382 Serial interface + GPIO_DDS2382_RX, // DDS2382 Serial interface +#endif +#ifdef USE_DDSU666 + GPIO_DDSU666_TX, // DDSU666 Serial interface + GPIO_DDSU666_RX, // DDSU666 Serial interface +#endif // USE_DDSU666 +#ifdef USE_SOLAX_X1 + GPIO_SOLAXX1_TX, // Solax Inverter tx pin + GPIO_SOLAXX1_RX, // Solax Inverter rx pin +#endif // USE_SOLAX_X1 +#ifdef USE_LE01MR + GPIO_LE01MR_RX, // F7F LE-01MR energy meter rx pin + GPIO_LE01MR_TX, // F7F LE-01MR energy meter tx pin +#endif // IFDEF:USE_LE01MR +#endif // USE_ENERGY_SENSOR + +// Serial +#ifdef USE_SERIAL_BRIDGE + GPIO_SBR_TX, // Serial Bridge Serial interface + GPIO_SBR_RX, // Serial Bridge Serial interface +#endif +#ifdef USE_ZIGBEE + GPIO_ZIGBEE_TX, // Zigbee Serial interface + GPIO_ZIGBEE_RX, // Zigbee Serial interface +#endif +#ifdef USE_MHZ19 + GPIO_MHZ_TXD, // MH-Z19 Serial interface + GPIO_MHZ_RXD, // MH-Z19 Serial interface +#endif +#ifdef USE_SENSEAIR + GPIO_SAIR_TX, // SenseAir Serial interface + GPIO_SAIR_RX, // SenseAir Serial interface +#endif +#ifdef USE_NOVA_SDS + GPIO_SDS0X1_TX, // Nova Fitness SDS011 Serial interface + GPIO_SDS0X1_RX, // Nova Fitness SDS011 Serial interface +#endif +#ifdef USE_HPMA + GPIO_HPMA_TX, // Honeywell HPMA115S0 Serial interface + GPIO_HPMA_RX, // Honeywell HPMA115S0 Serial interface +#endif +#ifdef USE_PMS5003 + GPIO_PMS5003_TX, // Plantower PMS5003 Serial interface + GPIO_PMS5003_RX, // Plantower PMS5003 Serial interface +#endif +#if defined(USE_TX20_WIND_SENSOR) || defined(USE_TX23_WIND_SENSOR) + GPIO_TX2X_TXD_BLACK, // TX20/TX23 Transmission Pin +#endif +#ifdef USE_MP3_PLAYER + GPIO_MP3_DFR562, // RB-DFR-562, DFPlayer Mini MP3 Player Serial interface +#endif +#ifdef USE_AZ7798 + GPIO_AZ_TXD, // AZ-Instrument 7798 CO2 datalogger Serial interface + GPIO_AZ_RXD, // AZ-Instrument 7798 CO2 datalogger Serial interface +#endif +#ifdef USE_PN532_HSU + GPIO_PN532_TXD, // PN532 HSU Tx + GPIO_PN532_RXD, // PN532 HSU Rx +#endif +#ifdef USE_TASMOTA_SLAVE + GPIO_TASMOTASLAVE_TXD, // Tasmota Slave TX + GPIO_TASMOTASLAVE_RXD, // Tasmota Slave RX + GPIO_TASMOTASLAVE_RST, // Tasmota Slave Reset + GPIO_TASMOTASLAVE_RST_INV, // Tasmota Slave Reset Inverted +#endif +#ifdef USE_RDM6300 + GPIO_RDM6300_RX, +#endif +#ifdef USE_IBEACON + GPIO_IBEACON_RX, + GPIO_IBEACON_TX, +#endif +#ifdef USE_GPS + GPIO_GPS_RX, // GPS serial interface + GPIO_GPS_TX, // GPS serial interface +#endif +#ifdef USE_HM10 + GPIO_HM10_RX, // GPS serial interface + GPIO_HM10_TX, // GPS serial interface +#endif + +#ifdef USE_MGC3130 + GPIO_MGC3130_XFER, + GPIO_MGC3130_RESET, +#endif +#ifdef USE_MAX31855 + GPIO_MAX31855CS, // MAX31855 Serial interface + GPIO_MAX31855CLK, // MAX31855 Serial interface + GPIO_MAX31855DO, // MAX31855 Serial interface +#endif +#ifdef ROTARY_V1 + GPIO_ROT1A, // Rotary switch1 A Pin + GPIO_ROT1B, // Rotary switch1 B Pin + GPIO_ROT2A, // Rotary switch2 A Pin + GPIO_ROT2B, // Rotary switch2 B Pin +#endif +#ifdef USE_HRE + GPIO_HRE_CLOCK, + GPIO_HRE_DATA, +#endif +#ifdef USE_A4988_STEPPER + GPIO_A4988_DIR, // A4988 direction pin + GPIO_A4988_STP, // A4988 step pin + // folowing are not mandatory + GPIO_A4988_ENA, // A4988 enabled pin + GPIO_A4988_MS1, // A4988 microstep pin1 + GPIO_A4988_MS2, // A4988 microstep pin2 + GPIO_A4988_MS3, // A4988 microstep pin3 +#endif +#ifdef USE_DEEPSLEEP + GPIO_DEEPSLEEP, +#endif +#ifdef USE_KEELOQ + GPIO_CC1101_GDO0, // CC1101 pin for RX + GPIO_CC1101_GDO2, // CC1101 pin for RX +#endif +#ifdef USE_HRXL + GPIO_HRXL_RX, +#endif +#ifdef USE_AS3935 + GPIO_AS3935, +#endif +/* + ADC0_INPUT, // Analog input + ADC0_TEMP, // Thermistor + ADC0_LIGHT, // Light sensor + ADC0_BUTTON, // Button + ADC0_BUTTON_INV, + ADC0_RANGE, // Range + ADC0_CT_POWER, // Current +*/ +}; + +//******************************************************************************************** + +// User selectable ADC0 functionality +enum UserSelectableAdc0 { + ADC0_NONE, // Not used + ADC0_INPUT, // Analog input + ADC0_TEMP, // Thermistor + ADC0_LIGHT, // Light sensor + ADC0_BUTTON, // Button + ADC0_BUTTON_INV, + ADC0_RANGE, // Range + ADC0_CT_POWER, // Current +// ADC0_SWITCH, // Switch +// ADC0_SWITCH_INV, + ADC0_END }; + +// Programmer selectable ADC0 functionality +enum ProgramSelectableAdc0 { + ADC0_FIX_START = 14, + ADC0_USER, // User configurable needs to be 15 + ADC0_MAX }; + +// Text in webpage Module Parameters and commands ADC +const char kAdc0Names[] PROGMEM = + D_SENSOR_NONE "|" D_ANALOG_INPUT "|" + D_TEMPERATURE "|" D_LIGHT "|" + D_SENSOR_BUTTON "|" D_SENSOR_BUTTON "i|" + D_RANGE "|" + D_CT_POWER "|" +// D_SENSOR_SWITCH "|" D_SENSOR_SWITCH "i|" + ; + +//******************************************************************************************** + +#define MAX_GPIO_PIN 40 // Number of supported GPIO +#define MIN_FLASH_PINS 4 // Number of flash chip pins unusable for configuration (GPIO6, 7, 8 and 11) +#define MAX_USER_PINS 36 // MAX_GPIO_PIN - MIN_FLASH_PINS +#define ADC0_PIN 33 // Pin number of ADC0 +#define WEMOS_MODULE 0 // Wemos module + +// 0 1 2 3 4 5 6 7 8 9101112131415161718192021222324252627282930313233343536373839 +const char PINS_WEMOS[] PROGMEM = "IOTXIORXIOIOflashcFLFLolIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOA6A7A0IoIoA3"; + +//******************************************************************************************** + +typedef struct MYIO { + uint16_t io[MAX_GPIO_PIN]; +} myio; // 40 * 2 = 80 bytes + +typedef struct MYCFGIO { + uint16_t io[MAX_USER_PINS]; +} mycfgio; // 36 * 2 = 72 bytes + +#define GPIO_FLAG_USED 0 // Currently no flags used + +typedef union { + uint16_t data; + struct { + uint16_t spare00 : 1; + uint16_t spare01 : 1; + uint16_t spare02 : 1; + uint16_t spare03 : 1; + uint16_t spare04 : 1; + uint16_t spare05 : 1; + uint16_t spare06 : 1; + uint16_t spare07 : 1; + uint16_t spare08 : 1; + uint16_t spare09 : 1; + uint16_t spare10 : 1; + uint16_t spare11 : 1; + uint16_t spare12 : 1; + uint16_t spare13 : 1; + uint16_t spare14 : 1; + uint16_t spare15 : 1; + }; +} gpio_flag; // 2 bytes + +typedef struct MYTMPLT { + mycfgio gp; // 72 bytes + gpio_flag flag; // 2 bytes +} mytmplt; // 74 bytes + +/********************************************************************************************/ +// Supported hardware modules +enum SupportedModules { + WEMOS, ESP32_CAM_AITHINKER, + MAXMODULE}; + +#define USER_MODULE 255 + +const char kModuleNames[] PROGMEM = + "ESP32-DevKit|ESP32 Cam AiThinker"; + +// Default module settings +const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { + WEMOS, + ESP32_CAM_AITHINKER +}; + +const mytmplt kModules PROGMEM = +{ // WEMOS - Espressif ESP32-DevKitC - Any ESP32 device like WeMos and NodeMCU hardware (ESP32) + GPIO_USER << 5, // 0 (I)O GPIO0, ADC2_CH1, TOUCH1, RTC_GPIO11, CLK_OUT1, EMAC_TX_CLK + GPIO_USER << 5, // 1 IO TXD0 GPIO1, U0TXD, CLK_OUT3, EMAC_RXD2 + GPIO_USER << 5, // 2 IO GPIO2, ADC2_CH2, TOUCH2, RTC_GPIO12, HSPIWP, HS2_DATA0, SD_DATA0 + GPIO_USER << 5, // 3 IO RXD0 GPIO3, U0RXD, CLK_OUT2 + GPIO_USER << 5, // 4 IO GPIO4, ADC2_CH0, TOUCH0, RTC_GPIO10, HSPIHD, HS2_DATA1, SD_DATA1, EMAC_TX_ER + GPIO_USER << 5, // 5 IO GPIO5, VSPICS0, HS1_DATA6, EMAC_RX_CLK + // 6 IO GPIO6, Flash CLK + // 7 IO GPIO7, Flash D0 + // 8 IO GPIO8, Flash D1 + GPIO_USER << 5, // 9 IO GPIO9, Flash D2, U1RXD + GPIO_USER << 5, // 10 IO GPIO10, Flash D3, U1TXD + // 11 IO GPIO11, Flash CMD + GPIO_USER << 5, // 12 (I)O GPIO12, ADC2_CH5, TOUCH5, RTC_GPIO15, MTDI, HSPIQ, HS2_DATA2, SD_DATA2, EMAC_TXD3 (If driven High, flash voltage (VDD_SDIO) is 1.8V not default 3.3V. Has internal pull-down, so unconnected = Low = 3.3V. May prevent flashing and/or booting if 3.3V flash is connected and pulled high. See ESP32 datasheet for more details.) + GPIO_USER << 5, // 13 IO GPIO13, ADC2_CH4, TOUCH4, RTC_GPIO14, MTCK, HSPID, HS2_DATA3, SD_DATA3, EMAC_RX_ER + GPIO_USER << 5, // 14 IO GPIO14, ADC2_CH6, TOUCH6, RTC_GPIO16, MTMS, HSPICLK, HS2_CLK, SD_CLK, EMAC_TXD2 + GPIO_USER << 5, // 15 (I)O GPIO15, ADC2_CH3, TOUCH3, MTDO, HSPICS0, RTC_GPIO13, HS2_CMD, SD_CMD, EMAC_RXD3 (If driven Low, silences boot messages from normal boot. Has internal pull-up, so unconnected = High = normal output.) + GPIO_USER << 5, // 16 IO GPIO16, HS1_DATA4, U2RXD, EMAC_CLK_OUT + GPIO_USER << 5, // 17 IO GPIO17, HS1_DATA5, U2TXD, EMAC_CLK_OUT_180 + GPIO_USER << 5, // 18 IO GPIO18, VSPICLK, HS1_DATA7 + GPIO_USER << 5, // 19 IO GPIO19, VSPIQ, U0CTS, EMAC_TXD0 + 0, // 20 + GPIO_USER << 5, // 21 IO GPIO21, VSPIHD, EMAC_TX_EN + GPIO_USER << 5, // 22 IO LED GPIO22, VSPIWP, U0RTS, EMAC_TXD1 + GPIO_USER << 5, // 23 IO GPIO23, VSPID, HS1_STROBE + 0, // 24 + GPIO_USER << 5, // 25 IO GPIO25, DAC_1, ADC2_CH8, RTC_GPIO6, EMAC_RXD0 + GPIO_USER << 5, // 26 IO GPIO26, DAC_2, ADC2_CH9, RTC_GPIO7, EMAC_RXD1 + GPIO_USER << 5, // 27 IO GPIO27, ADC2_CH7, TOUCH7, RTC_GPIO17, EMAC_RX_DV + 0, // 28 + 0, // 29 + 0, // 30 + 0, // 31 + GPIO_USER << 5, // 32 IO GPIO32, XTAL_32K_P (32.768 kHz crystal oscillator input), ADC1_CH4, TOUCH9, RTC_GPIO9 + GPIO_USER << 5, // 33 IO GPIO33, XTAL_32K_N (32.768 kHz crystal oscillator output), ADC1_CH5, TOUCH8, RTC_GPIO8 + GPIO_USER << 5, // 34 I NO PULLUP GPIO34, ADC1_CH6, RTC_GPIO4 + GPIO_USER << 5, // 35 I NO PULLUP GPIO35, ADC1_CH7, RTC_GPIO5 + GPIO_USER << 5, // 36 I NO PULLUP GPIO36, SENSOR_VP, ADC_H, ADC1_CH0, RTC_GPIO0 + 0, // 37 NO PULLUP + 0, // 38 NO PULLUP + GPIO_USER << 5, // 39 I NO PULLUP GPIO39, SENSOR_VN, ADC1_CH3, ADC_H, RTC_GPIO3 + 0 // Flag +}; + +#endif // ESP32 + +#endif // _TASMOTA_TEMPLATE_ESP32_FINAL_H_ From d7f81899a7cb426aacd0d1cee0f66e27c0ef285c Mon Sep 17 00:00:00 2001 From: Javier Arigita Date: Wed, 29 Apr 2020 13:46:22 +0200 Subject: [PATCH 389/547] Bugfix manual to auto corrected, reduction of floats and rampup ctr. improvement --- tasmota/xdrv_39_thermostat.ino | 36 +++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/tasmota/xdrv_39_thermostat.ino b/tasmota/xdrv_39_thermostat.ino index 88f460e8b..7f5d031cf 100644 --- a/tasmota/xdrv_39_thermostat.ino +++ b/tasmota/xdrv_39_thermostat.ino @@ -316,7 +316,7 @@ bool HeatStateAutoToManual(void) bool HeatStateManualToAuto(void) { - bool change_state; + bool change_state = false; // If switch input inactive // AND sensor alive @@ -332,11 +332,12 @@ bool HeatStateManualToAuto(void) bool HeatStateAllToOff(void) { - bool change_state; + bool change_state = false; // If emergency mode then switch OFF the output inmediately if (Thermostat.status.state_emergency == EMERGENCY_ON) { Thermostat.status.thermostat_mode = THERMOSTAT_OFF; // Emergency switch to THERMOSTAT_OFF + change_state = true; } return change_state; } @@ -375,7 +376,9 @@ void ThermostatOutputRelay(bool active) // then switch output to ON if ((active == true) && (Thermostat.status.status_output == IFACE_OFF)) { +#ifndef DEBUG_THERMOSTAT ExecuteCommandPower(Thermostat.output_relay_number, POWER_ON, SRC_THERMOSTAT); +#endif Thermostat.status.status_output = IFACE_ON; #ifdef DEBUG_THERMOSTAT ThermostatVirtualSwitch(); @@ -385,7 +388,9 @@ void ThermostatOutputRelay(bool active) // AND current output status is ON // then switch output to OFF else if ((active == false) && (Thermostat.status.status_output == IFACE_ON)) { +#ifndef DEBUG_THERMOSTAT ExecuteCommandPower(Thermostat.output_relay_number, POWER_OFF, SRC_THERMOSTAT); +#endif Thermostat.timestamp_output_off = uptime; Thermostat.status.status_output = IFACE_OFF; #ifdef DEBUG_THERMOSTAT @@ -638,10 +643,7 @@ void ThermostatWorkAutomaticRampUp(void) // Calculate time to switch Off and come out of ramp-up // y-y1 = m(x-x1) -> x = ((y-y1) / m) + x1 -> y1 = temp_rampup_cycle, x1 = (time_rampup_nextcycle - time_rampup_cycle), m = gradient in º/sec // Better Alternative -> (y-y1)/(x-x1) = ((y2-y1)/(x2-x1)) -> where y = temp (target) and x = time (to switch off, what its needed) - // x = ((y-y1)/(y2-y1))*(x2-x1) + x1 - deadtime - // Thermostat.time_ctr_changepoint = (uint32_t)(((float)(Thermostat.temp_target_level_ctr - Thermostat.temp_rampup_cycle) / (float)temp_delta_rampup) * (float)(time_total_rampup)) + (uint32_t)(Thermostat.time_rampup_nextcycle - (time_total_rampup)) - Thermostat.time_rampup_deadtime; - //Thermostat.time_ctr_changepoint = (uint32_t)(((float)(Thermostat.temp_target_level_ctr - Thermostat.temp_rampup_cycle) * (float)(time_total_rampup)) / (float)temp_delta_rampup) + (uint32_t)(Thermostat.time_rampup_nextcycle - (time_total_rampup)) - Thermostat.time_rampup_deadtime; - + // x = ((y-y1)/(y2-y1))*(x2-x1) + x1 - deadtime aux_temp_delta = (int32_t)(Thermostat.temp_target_level_ctr - Thermostat.temp_rampup_cycle); // Protect overflow, if temperature goes down set max @@ -656,7 +658,8 @@ void ThermostatWorkAutomaticRampUp(void) // Calculate temperature for switching off the output // y = (((y2-y1)/(x2-x1))*(x-x1)) + y1 // Thermostat.temp_rampup_output_off = (int16_t)(((float)(temp_delta_rampup) / (float)(time_total_rampup * Thermostat.counter_rampup_cycles)) * (float)(Thermostat.time_ctr_changepoint - (uptime - (time_total_rampup)))) + Thermostat.temp_rampup_cycle; - Thermostat.temp_rampup_output_off = (int16_t)(((float)temp_delta_rampup * (float)(Thermostat.time_ctr_changepoint - (uptime - (time_total_rampup)))) / (float)(time_total_rampup * Thermostat.counter_rampup_cycles)) + Thermostat.temp_rampup_cycle; + //Thermostat.temp_rampup_output_off = (int16_t)(((float)temp_delta_rampup * (float)(Thermostat.time_ctr_changepoint - (uptime - (time_total_rampup)))) / (float)(time_total_rampup * Thermostat.counter_rampup_cycles)) + Thermostat.temp_rampup_cycle; + Thermostat.temp_rampup_output_off = (int16_t)(((int32_t)temp_delta_rampup * (int32_t)(Thermostat.time_ctr_changepoint - (uptime - (time_total_rampup)))) / (int32_t)(time_total_rampup * Thermostat.counter_rampup_cycles)) + Thermostat.temp_rampup_cycle; // Set auxiliary variables Thermostat.time_rampup_nextcycle = uptime + (uint32_t)Thermostat.time_rampup_cycle; Thermostat.temp_rampup_cycle = Thermostat.temp_measured; @@ -1211,6 +1214,25 @@ bool Xdrv39(uint8_t function) AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.time_ctr_changepoint: %s"), result_chr); dtostrfd(Thermostat.temp_rampup_output_off, 0, result_chr); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.temp_rampup_output_off: %s"), result_chr); + dtostrfd(uptime, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("uptime: %s"), result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("------ Special debug section for time_ctr_changepoint ------")); + dtostrfd(Thermostat.temp_target_level_ctr, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.temp_target_level_ctr: %s"), result_chr); + dtostrfd(Thermostat.temp_rampup_cycle, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.temp_rampup_cycle: %s"), result_chr); + dtostrfd(Thermostat.time_rampup_cycle, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.time_rampup_cycle: %s"), result_chr); + dtostrfd(Thermostat.counter_rampup_cycles, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.counter_rampup_cycles: %s"), result_chr); + dtostrfd(Thermostat.temp_measured, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.temp_measured: %s"), result_chr); + dtostrfd(Thermostat.temp_rampup_start, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.temp_rampup_start: %s"), result_chr); + dtostrfd(Thermostat.time_rampup_nextcycle, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.time_rampup_nextcycle: %s"), result_chr); + dtostrfd(Thermostat.time_rampup_deadtime, 0, result_chr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.time_rampup_deadtime: %s"), result_chr); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("------ Thermostat End ------")); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("")); #endif From d805803daa91be6eba1e8f13e1359735961e0f42 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 29 Apr 2020 14:01:02 +0200 Subject: [PATCH 390/547] Change pin array names Change pin array names to block unwanted use of direct access --- tasmota/support.ino | 12 ++++++------ tasmota/support_tasmota.ino | 2 +- tasmota/tasmota.ino | 10 +++++----- tasmota/xdrv_10_scripter.ino | 10 +++++----- tasmota/xsns_53_sml.ino | 4 ++-- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/tasmota/support.ino b/tasmota/support.ino index 02d17af98..e90d01c85 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -1080,7 +1080,7 @@ uint32_t Pin(uint32_t gpio, uint32_t index) ICACHE_RAM_ATTR; uint32_t Pin(uint32_t gpio, uint32_t index = 0); uint32_t Pin(uint32_t gpio, uint32_t index) { #ifdef LEGACY_GPIO_ARRAY - return pin[gpio + index]; // Pin number configured for gpio or 99 if not used + return pin_gpio[gpio + index]; // Pin number configured for gpio or 99 if not used #else // No LEGACY_GPIO_ARRAY #ifdef ESP8266 uint16_t real_gpio = gpio + index; @@ -1091,8 +1091,8 @@ uint32_t Pin(uint32_t gpio, uint32_t index) { uint16_t real_gpio = (gpio << 5) + index; #endif // FINAL_ESP32 #endif // ESP8266 - ESP32 - for (uint32_t i = 0; i < ARRAY_SIZE(pin); i++) { - if (pin[i] == real_gpio) { + for (uint32_t i = 0; i < ARRAY_SIZE(gpio_pin); i++) { + if (gpio_pin[i] == real_gpio) { return i; // Pin number configured for gpio } } @@ -1107,15 +1107,15 @@ boolean PinUsed(uint32_t gpio, uint32_t index) { void SetPin(uint32_t lpin, uint32_t gpio) { #ifdef LEGACY_GPIO_ARRAY - pin[gpio] = lpin; + pin_gpio[gpio] = lpin; #else - pin[lpin] = gpio; + gpio_pin[lpin] = gpio; #endif } #ifdef LEGACY_GPIO_ARRAY void InitAllPins(void) { - for (uint32_t i = 0; i < ARRAY_SIZE(pin); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(pin_gpio); i++) { SetPin(99, i); } } diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 72137edab..91fda344d 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -1439,7 +1439,7 @@ void GpioInit(void) } #ifndef LEGACY_GPIO_ARRAY - AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t*)pin, ARRAY_SIZE(pin)); + AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t*)gpio_pin, ARRAY_SIZE(gpio_pin)); #endif #ifdef ESP8266 diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 8af7285ad..b552c55c6 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -121,7 +121,7 @@ uint16_t syslog_timer = 0; // Timer to re-enable syslog_level #ifdef ESP32 #ifdef FINAL_ESP32 -uint16_t pin[MAX_GPIO_PIN] = { 0 }; // Possible pin configurations +uint16_t gpio_pin[MAX_GPIO_PIN] = { 0 }; // GPIO functions indexed by pin number #endif // FINAL_ESP32 #endif // ESP32 @@ -136,16 +136,16 @@ uint8_t blinkspeed = 1; // LED blink rate #ifdef ESP8266 #ifdef LEGACY_GPIO_ARRAY -uint8_t pin[GPIO_MAX]; // Possible pin configurations +uint8_t pin_gpio[GPIO_MAX]; // Pin numbers indexed by GPIO function #else // No LEGACY_GPIO_ARRAY -uint8_t pin[MAX_GPIO_PIN] = { 0 }; // Possible pin configurations +uint8_t gpio_pin[MAX_GPIO_PIN] = { 0 }; // GPIO functions indexed by pin number #endif // LEGACY_GPIO_ARRAY #else // ESP32 #ifndef FINAL_ESP32 #ifdef LEGACY_GPIO_ARRAY -uint8_t pin[GPIO_MAX]; // Possible pin configurations +uint8_t pin_gpio[GPIO_MAX]; // Pin numbers indexed by GPIO function #else // No LEGACY_GPIO_ARRAY -uint8_t pin[MAX_GPIO_PIN] = { 0 }; // Possible pin configurations +uint8_t gpio_pin[MAX_GPIO_PIN] = { 0 }; // GPIO functions indexed by pin number #endif // LEGACY_GPIO_ARRAY #endif // No FINAL_ESP32 #endif // ESP8266 - ESP32 diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index 0c2ad48cf..8078c7bce 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -1573,7 +1573,7 @@ chknext: } if (!strncmp(vname,"pn[",3)) { GetNumericResult(vname+3,OPER_EQU,&fvar,0); -// fvar=pin[(uint8_t)fvar]; +// fvar=pin_gpio[(uint8_t)fvar]; fvar=Pin(fvar); // skip ] bracket len++; @@ -1583,8 +1583,8 @@ chknext: GetNumericResult(vname+3,OPER_EQU,&fvar,0); uint8_t gpiopin=fvar; #ifdef LEGACY_GPIO_ARRAY - for (uint8_t i=0;i 0)) { - fvar = pin[gpiopin]; + if ((gpiopin < ARRAY_SIZE(gpio_pin)) && (gpio_pin[gpiopin] > 0)) { + fvar = gpio_pin[gpiopin]; // skip ] bracket len++; goto exit; diff --git a/tasmota/xsns_53_sml.ino b/tasmota/xsns_53_sml.ino index 632f64f50..efe3b89bc 100755 --- a/tasmota/xsns_53_sml.ino +++ b/tasmota/xsns_53_sml.ino @@ -1833,13 +1833,13 @@ uint8_t *script_meter; bool Gpio_used(uint8_t gpiopin) { #ifdef LEGACY_GPIO_ARRAY for (uint16_t i=0;i 0)) { + if ((gpiopin < ARRAY_SIZE(gpio_pin)) && (gpio_pin[gpiopin] > 0)) { return true; } #endif From 7dc7b631d46348fd902eaa5943281afc7c7b1983 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 29 Apr 2020 14:18:38 +0200 Subject: [PATCH 391/547] Fix ESP32 settings size Fix ESP32 settings size as regression from yesterday --- tasmota/settings.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tasmota/settings.h b/tasmota/settings.h index e5c9800fd..f4139a748 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -362,12 +362,15 @@ struct { char ex_friendlyname[4][33]; // 3AC char ex_switch_topic[33]; // 430 #else // ESP32 -#ifdef FINAL_ESP32 +#ifndef FINAL_ESP32 + char ex_friendlyname[4][33]; // 3AC + char ex_switch_topic[33]; // 430 +#else // FINAL_ESP32 myio my_gp; // 3AC - 2 x 40 bytes (ESP32) mytmplt user_template; // 3FC - 2 x 37 bytes (ESP32) uint8_t free_esp32_446[11]; // 446 -#endif +#endif // FINAL_ESP32 #endif // ESP8266 - ESP32 char serial_delimiter; // 451 @@ -420,7 +423,9 @@ struct { mytmplt user_template; // 580 - 37 bytes (ESP32) uint8_t free_esp32_5a5[23]; // 5A5 -#endif +#else // FINAL_ESP32 + char ex_mqtt_fulltopic[100]; // 558 +#endif // FINAL_ESP32 #endif // ESP8266 - ESP32 SysBitfield2 flag2; // 5BC From ef61668037ed8667ec438a874339d52f3632c14d Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 29 Apr 2020 17:44:03 +0200 Subject: [PATCH 392/547] Change ESP32 pin allocation part 2 --- tasmota/support.ino | 25 +++++++- tasmota/support_command.ino | 56 ++++++++++++++--- tasmota/support_tasmota.ino | 58 +++++++++--------- tasmota/tasmota_globals.h | 14 ++++- tasmota/tasmota_template_ESP32_final.h | 84 +++++++++++++------------- tasmota/xdrv_01_webserver.ino | 36 ++++++++--- 6 files changed, 182 insertions(+), 91 deletions(-) diff --git a/tasmota/support.ino b/tasmota/support.ino index e90d01c85..7523a65a3 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -1256,7 +1256,7 @@ bool FlashPin(uint32_t pin) return (((pin > 5) && (pin < 9)) || (11 == pin)); } -uint8_t ValidPin(uint32_t pin, uint32_t gpio) +uint32_t ValidPin(uint32_t pin, uint32_t gpio) { if (FlashPin(pin)) { return GPIO_NONE; // Disable flash pins GPIO6, GPIO7, GPIO8 and GPIO11 @@ -1274,7 +1274,15 @@ uint8_t ValidPin(uint32_t pin, uint32_t gpio) bool ValidGPIO(uint32_t pin, uint32_t gpio) { +#ifdef ESP8266 return (GPIO_USER == ValidPin(pin, gpio)); // Only allow GPIO_USER pins +#else // ESP32 +#ifndef FINAL_ESP32 + return (GPIO_USER == ValidPin(pin, gpio)); // Only allow GPIO_USER pins +#else // FINAL_ESP32 + return (GPIO_USER == ValidPin(pin, gpio >> 5)); // Only allow GPIO_USER pins +#endif // FINAL_ESP32 +#endif // ESP8266 - ESP32 } bool ValidAdc(void) @@ -1362,7 +1370,7 @@ bool JsonTemplate(const char* dataBuf) #ifdef ESP8266 StaticJsonBuffer<400> jb; // 331 from https://arduinojson.org/v5/assistant/ #else - StaticJsonBuffer<800> jb; // 654 from https://arduinojson.org/v5/assistant/ + StaticJsonBuffer<999> jb; // 654 from https://arduinojson.org/v5/assistant/ #endif JsonObject& obj = jb.parseObject(dataBuf); if (!obj.success()) { return false; } @@ -1881,3 +1889,16 @@ void AddLogMissed(const char *sensor, uint32_t misses) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SNS: %s missed %d"), sensor, SENSOR_MAX_MISS - misses); } + +void AddLogBufferSize(uint32_t loglevel, uint8_t *buffer, uint32_t count, uint32_t size) { + snprintf_P(log_data, sizeof(log_data), PSTR("DMP:")); + for (uint32_t i = 0; i < count; i++) { + if (1 == size) { // uint8_t + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, *(buffer)); + } else { // uint16_t + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X%02X"), log_data, *(buffer +1), *(buffer)); + } + buffer += size; + } + AddLog(loglevel); +} diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 01c227f36..7bd3fcde5 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -1045,11 +1045,30 @@ void CmndGpio(void) if (XdrvMailbox.index < ARRAY_SIZE(Settings.my_gp.io)) { myio cmodule; ModuleGpios(&cmodule); - if (ValidGPIO(XdrvMailbox.index, cmodule.io[XdrvMailbox.index]) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < GPIO_SENSOR_END)) { + if (ValidGPIO(XdrvMailbox.index, cmodule.io[XdrvMailbox.index]) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < AGPIO(GPIO_SENSOR_END))) { bool present = false; - for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(kGpioNiceList); i++) { +#ifdef ESP8266 uint32_t midx = pgm_read_byte(kGpioNiceList + i); - if (midx == XdrvMailbox.payload) { present = true; } + if (midx == XdrvMailbox.payload) { + present = true; + break; + } +#else // ESP32 +#ifndef FINAL_ESP32 + uint32_t midx = pgm_read_byte(kGpioNiceList + i); + if (midx == XdrvMailbox.payload) { + present = true; + break; + } +#else // FINAL_ESP32 + uint32_t midx = pgm_read_word(kGpioNiceList + i) << 5; + if (midx == (XdrvMailbox.payload & 0xFFE0)) { + present = true; + break; + } +#endif // FINAL_ESP32 +#endif // ESP8266 - ESP32 } if (present) { for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { @@ -1067,17 +1086,25 @@ void CmndGpio(void) if (ValidGPIO(i, cmodule.io[i]) || ((GPIO_USER == XdrvMailbox.payload) && !FlashPin(i))) { if (jsflg) { ResponseAppend_P(PSTR(",")); } jsflg = true; - uint8_t sensor_type = Settings.my_gp.io[i]; + uint32_t sensor_type = Settings.my_gp.io[i]; if (!ValidGPIO(i, cmodule.io[i])) { sensor_type = cmodule.io[i]; if (GPIO_USER == sensor_type) { // A user GPIO equals a not connected (=GPIO_NONE) GPIO here sensor_type = GPIO_NONE; } } - uint8_t sensor_name_idx = sensor_type; +#ifdef ESP8266 + uint32_t sensor_name_idx = sensor_type; +#else // ESP32 +#ifndef FINAL_ESP32 + uint32_t sensor_name_idx = sensor_type; +#else // FINAL_ESP32 + uint32_t sensor_name_idx = sensor_type >> 5; +#endif // FINAL_ESP32 +#endif // ESP8266 - ESP32 const char *sensor_names = kSensorNames; - if (sensor_type > GPIO_FIX_START) { - sensor_name_idx = sensor_type - GPIO_FIX_START -1; + if (sensor_name_idx > GPIO_FIX_START) { + sensor_name_idx = sensor_name_idx - GPIO_FIX_START -1; sensor_names = kSensorNamesFixed; } char stemp1[TOPSZ]; @@ -1099,8 +1126,19 @@ void CmndGpios(void) ModuleGpios(&cmodule); uint32_t lines = 1; bool jsflg = false; - for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(kGpioNiceList); i++) { +#ifdef ESP8266 uint32_t midx = pgm_read_byte(kGpioNiceList + i); + uint32_t ridx = midx; +#else // ESP32 +#ifndef FINAL_ESP32 + uint32_t midx = pgm_read_byte(kGpioNiceList + i); + uint32_t ridx = midx; +#else // FINAL_ESP32 + uint32_t midx = pgm_read_word(kGpioNiceList + i); + uint32_t ridx = midx << 5; +#endif // FINAL_ESP32 +#endif // ESP8266 - ESP32 if ((XdrvMailbox.payload != 255) && GetUsedInModule(midx, cmodule.io)) { continue; } if (!jsflg) { Response_P(PSTR("{\"" D_CMND_GPIOS "%d\":{"), lines); @@ -1109,7 +1147,7 @@ void CmndGpios(void) } jsflg = true; char stemp1[TOPSZ]; - if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), midx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (LOGSZ - TOPSZ)) || (i == sizeof(kGpioNiceList) -1)) { + if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), ridx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (LOGSZ - TOPSZ)) || (i == ARRAY_SIZE(kGpioNiceList) -1)) { ResponseJsonEndEnd(); MqttPublishPrefixTopic_P(RESULT_OR_STAT, UpperCase(XdrvMailbox.command, XdrvMailbox.command)); jsflg = false; diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 91fda344d..c1f59cc95 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -1353,21 +1353,21 @@ void GpioInit(void) } for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) { - if ((Settings.user_template.gp.io[i] >= GPIO_SENSOR_END) && (Settings.user_template.gp.io[i] < GPIO_USER)) { - Settings.user_template.gp.io[i] = GPIO_USER; // Fix not supported sensor ids in template + if ((Settings.user_template.gp.io[i] >= AGPIO(GPIO_SENSOR_END)) && (Settings.user_template.gp.io[i] < AGPIO(GPIO_USER))) { + Settings.user_template.gp.io[i] = AGPIO(GPIO_USER); // Fix not supported sensor ids in template } } myio def_gp; ModuleGpios(&def_gp); for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { - if ((Settings.my_gp.io[i] >= GPIO_SENSOR_END) && (Settings.my_gp.io[i] < GPIO_USER)) { + if ((Settings.my_gp.io[i] >= AGPIO(GPIO_SENSOR_END)) && (Settings.my_gp.io[i] < AGPIO(GPIO_USER))) { Settings.my_gp.io[i] = GPIO_NONE; // Fix not supported sensor ids in module } else if (Settings.my_gp.io[i] > GPIO_NONE) { my_module.io[i] = Settings.my_gp.io[i]; // Set User selected Module sensors } - if ((def_gp.io[i] > GPIO_NONE) && (def_gp.io[i] < GPIO_USER)) { + if ((def_gp.io[i] > GPIO_NONE) && (def_gp.io[i] < AGPIO(GPIO_USER))) { my_module.io[i] = def_gp.io[i]; // Force Template override } } @@ -1395,38 +1395,38 @@ void GpioInit(void) XdrvMailbox.index = mpin; XdrvMailbox.payload = i; - if ((mpin >= GPIO_SWT1_NP) && (mpin < (GPIO_SWT1_NP + MAX_SWITCHES))) { - SwitchPullupFlag(mpin - GPIO_SWT1_NP); - mpin -= (GPIO_SWT1_NP - GPIO_SWT1); + if ((mpin >= AGPIO(GPIO_SWT1_NP)) && (mpin < (AGPIO(GPIO_SWT1_NP) + MAX_SWITCHES))) { + SwitchPullupFlag(mpin - AGPIO(GPIO_SWT1_NP)); + mpin -= (AGPIO(GPIO_SWT1_NP) - AGPIO(GPIO_SWT1)); } - else if ((mpin >= GPIO_KEY1_NP) && (mpin < (GPIO_KEY1_NP + MAX_KEYS))) { - ButtonPullupFlag(mpin - GPIO_KEY1_NP); // 0 .. 3 - mpin -= (GPIO_KEY1_NP - GPIO_KEY1); + else if ((mpin >= AGPIO(GPIO_KEY1_NP)) && (mpin < (AGPIO(GPIO_KEY1_NP) + MAX_KEYS))) { + ButtonPullupFlag(mpin - AGPIO(GPIO_KEY1_NP)); // 0 .. 3 + mpin -= (AGPIO(GPIO_KEY1_NP) - AGPIO(GPIO_KEY1)); } - else if ((mpin >= GPIO_KEY1_INV) && (mpin < (GPIO_KEY1_INV + MAX_KEYS))) { - ButtonInvertFlag(mpin - GPIO_KEY1_INV); // 0 .. 3 - mpin -= (GPIO_KEY1_INV - GPIO_KEY1); + else if ((mpin >= AGPIO(GPIO_KEY1_INV)) && (mpin < (AGPIO(GPIO_KEY1_INV) + MAX_KEYS))) { + ButtonInvertFlag(mpin - AGPIO(GPIO_KEY1_INV)); // 0 .. 3 + mpin -= (AGPIO(GPIO_KEY1_INV) - AGPIO(GPIO_KEY1)); } - else if ((mpin >= GPIO_KEY1_INV_NP) && (mpin < (GPIO_KEY1_INV_NP + MAX_KEYS))) { - ButtonPullupFlag(mpin - GPIO_KEY1_INV_NP); // 0 .. 3 - ButtonInvertFlag(mpin - GPIO_KEY1_INV_NP); // 0 .. 3 - mpin -= (GPIO_KEY1_INV_NP - GPIO_KEY1); + else if ((mpin >= AGPIO(GPIO_KEY1_INV_NP)) && (mpin < (AGPIO(GPIO_KEY1_INV_NP) + MAX_KEYS))) { + ButtonPullupFlag(mpin - AGPIO(GPIO_KEY1_INV_NP)); // 0 .. 3 + ButtonInvertFlag(mpin - AGPIO(GPIO_KEY1_INV_NP)); // 0 .. 3 + mpin -= (AGPIO(GPIO_KEY1_INV_NP) - AGPIO(GPIO_KEY1)); } - else if ((mpin >= GPIO_REL1_INV) && (mpin < (GPIO_REL1_INV + MAX_RELAYS))) { - bitSet(rel_inverted, mpin - GPIO_REL1_INV); - mpin -= (GPIO_REL1_INV - GPIO_REL1); + else if ((mpin >= AGPIO(GPIO_REL1_INV)) && (mpin < (AGPIO(GPIO_REL1_INV) + MAX_RELAYS))) { + bitSet(rel_inverted, mpin - AGPIO(GPIO_REL1_INV)); + mpin -= (AGPIO(GPIO_REL1_INV) - AGPIO(GPIO_REL1)); } - else if ((mpin >= GPIO_LED1_INV) && (mpin < (GPIO_LED1_INV + MAX_LEDS))) { - bitSet(led_inverted, mpin - GPIO_LED1_INV); - mpin -= (GPIO_LED1_INV - GPIO_LED1); + else if ((mpin >= AGPIO(GPIO_LED1_INV)) && (mpin < (AGPIO(GPIO_LED1_INV) + MAX_LEDS))) { + bitSet(led_inverted, mpin - AGPIO(GPIO_LED1_INV)); + mpin -= (AGPIO(GPIO_LED1_INV) - AGPIO(GPIO_LED1)); } - else if (mpin == GPIO_LEDLNK_INV) { + else if (mpin == AGPIO(GPIO_LEDLNK_INV)) { ledlnk_inverted = 1; - mpin -= (GPIO_LEDLNK_INV - GPIO_LEDLNK); + mpin -= (AGPIO(GPIO_LEDLNK_INV) - AGPIO(GPIO_LEDLNK)); } - else if ((mpin >= GPIO_PWM1_INV) && (mpin < (GPIO_PWM1_INV + MAX_PWMS))) { - bitSet(pwm_inverted, mpin - GPIO_PWM1_INV); - mpin -= (GPIO_PWM1_INV - GPIO_PWM1); + else if ((mpin >= AGPIO(GPIO_PWM1_INV)) && (mpin < (AGPIO(GPIO_PWM1_INV) + MAX_PWMS))) { + bitSet(pwm_inverted, mpin - AGPIO(GPIO_PWM1_INV)); + mpin -= (AGPIO(GPIO_PWM1_INV) - AGPIO(GPIO_PWM1)); } else if (XdrvCall(FUNC_PIN_STATE)) { mpin = XdrvMailbox.index; @@ -1439,7 +1439,7 @@ void GpioInit(void) } #ifndef LEGACY_GPIO_ARRAY - AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t*)gpio_pin, ARRAY_SIZE(gpio_pin)); +// AddLogBufferSize(LOG_LEVEL_DEBUG, (uint8_t*)gpio_pin, ARRAY_SIZE(gpio_pin), sizeof(gpio_pin[0])); #endif #ifdef ESP8266 diff --git a/tasmota/tasmota_globals.h b/tasmota/tasmota_globals.h index 178cf3c24..1cff43fd8 100644 --- a/tasmota/tasmota_globals.h +++ b/tasmota/tasmota_globals.h @@ -53,9 +53,9 @@ extern "C" void resetPins(); * Theo transition defines - DO NOT TOUCH \*********************************************************************************************/ -//#define LEGACY_GPIO_ARRAY // Uncomment to use legacy GPIO array instead of new PIN array +//#define LEGACY_GPIO_ARRAY // Uncomment to use legacy GPIO array instead of new PIN array -//#define FINAL_ESP32 // Uncomment for ESP32 16-bits PIN array +//#define FINAL_ESP32 // Uncomment for ESP32 16-bits PIN array #ifdef FINAL_ESP32 #undef LEGACY_GPIO_ARRAY #endif @@ -333,6 +333,16 @@ const char kWebColors[] PROGMEM = #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #endif +#ifdef ESP8266 +#define AGPIO(x) (x) +#else // ESP32 +#ifndef FINAL_ESP32 +#define AGPIO(x) (x) +#else // FINAL_ESP32 +#define AGPIO(x) (x<<5) +#endif // FINAL_ESP32 +#endif // ESP8266 - ESP32 + #ifdef USE_DEVICE_GROUPS #define SendDeviceGroupMessage(DEVICE_INDEX, REQUEST_TYPE, ...) _SendDeviceGroupMessage(DEVICE_INDEX, REQUEST_TYPE, __VA_ARGS__, 0) #define SendLocalDeviceGroupMessage(REQUEST_TYPE, ...) _SendDeviceGroupMessage(0, REQUEST_TYPE, __VA_ARGS__, 0) diff --git a/tasmota/tasmota_template_ESP32_final.h b/tasmota/tasmota_template_ESP32_final.h index e13dbec24..b48897094 100644 --- a/tasmota/tasmota_template_ESP32_final.h +++ b/tasmota/tasmota_template_ESP32_final.h @@ -693,48 +693,48 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { }; const mytmplt kModules PROGMEM = -{ // WEMOS - Espressif ESP32-DevKitC - Any ESP32 device like WeMos and NodeMCU hardware (ESP32) - GPIO_USER << 5, // 0 (I)O GPIO0, ADC2_CH1, TOUCH1, RTC_GPIO11, CLK_OUT1, EMAC_TX_CLK - GPIO_USER << 5, // 1 IO TXD0 GPIO1, U0TXD, CLK_OUT3, EMAC_RXD2 - GPIO_USER << 5, // 2 IO GPIO2, ADC2_CH2, TOUCH2, RTC_GPIO12, HSPIWP, HS2_DATA0, SD_DATA0 - GPIO_USER << 5, // 3 IO RXD0 GPIO3, U0RXD, CLK_OUT2 - GPIO_USER << 5, // 4 IO GPIO4, ADC2_CH0, TOUCH0, RTC_GPIO10, HSPIHD, HS2_DATA1, SD_DATA1, EMAC_TX_ER - GPIO_USER << 5, // 5 IO GPIO5, VSPICS0, HS1_DATA6, EMAC_RX_CLK - // 6 IO GPIO6, Flash CLK - // 7 IO GPIO7, Flash D0 - // 8 IO GPIO8, Flash D1 - GPIO_USER << 5, // 9 IO GPIO9, Flash D2, U1RXD - GPIO_USER << 5, // 10 IO GPIO10, Flash D3, U1TXD - // 11 IO GPIO11, Flash CMD - GPIO_USER << 5, // 12 (I)O GPIO12, ADC2_CH5, TOUCH5, RTC_GPIO15, MTDI, HSPIQ, HS2_DATA2, SD_DATA2, EMAC_TXD3 (If driven High, flash voltage (VDD_SDIO) is 1.8V not default 3.3V. Has internal pull-down, so unconnected = Low = 3.3V. May prevent flashing and/or booting if 3.3V flash is connected and pulled high. See ESP32 datasheet for more details.) - GPIO_USER << 5, // 13 IO GPIO13, ADC2_CH4, TOUCH4, RTC_GPIO14, MTCK, HSPID, HS2_DATA3, SD_DATA3, EMAC_RX_ER - GPIO_USER << 5, // 14 IO GPIO14, ADC2_CH6, TOUCH6, RTC_GPIO16, MTMS, HSPICLK, HS2_CLK, SD_CLK, EMAC_TXD2 - GPIO_USER << 5, // 15 (I)O GPIO15, ADC2_CH3, TOUCH3, MTDO, HSPICS0, RTC_GPIO13, HS2_CMD, SD_CMD, EMAC_RXD3 (If driven Low, silences boot messages from normal boot. Has internal pull-up, so unconnected = High = normal output.) - GPIO_USER << 5, // 16 IO GPIO16, HS1_DATA4, U2RXD, EMAC_CLK_OUT - GPIO_USER << 5, // 17 IO GPIO17, HS1_DATA5, U2TXD, EMAC_CLK_OUT_180 - GPIO_USER << 5, // 18 IO GPIO18, VSPICLK, HS1_DATA7 - GPIO_USER << 5, // 19 IO GPIO19, VSPIQ, U0CTS, EMAC_TXD0 - 0, // 20 - GPIO_USER << 5, // 21 IO GPIO21, VSPIHD, EMAC_TX_EN - GPIO_USER << 5, // 22 IO LED GPIO22, VSPIWP, U0RTS, EMAC_TXD1 - GPIO_USER << 5, // 23 IO GPIO23, VSPID, HS1_STROBE - 0, // 24 - GPIO_USER << 5, // 25 IO GPIO25, DAC_1, ADC2_CH8, RTC_GPIO6, EMAC_RXD0 - GPIO_USER << 5, // 26 IO GPIO26, DAC_2, ADC2_CH9, RTC_GPIO7, EMAC_RXD1 - GPIO_USER << 5, // 27 IO GPIO27, ADC2_CH7, TOUCH7, RTC_GPIO17, EMAC_RX_DV - 0, // 28 - 0, // 29 - 0, // 30 - 0, // 31 - GPIO_USER << 5, // 32 IO GPIO32, XTAL_32K_P (32.768 kHz crystal oscillator input), ADC1_CH4, TOUCH9, RTC_GPIO9 - GPIO_USER << 5, // 33 IO GPIO33, XTAL_32K_N (32.768 kHz crystal oscillator output), ADC1_CH5, TOUCH8, RTC_GPIO8 - GPIO_USER << 5, // 34 I NO PULLUP GPIO34, ADC1_CH6, RTC_GPIO4 - GPIO_USER << 5, // 35 I NO PULLUP GPIO35, ADC1_CH7, RTC_GPIO5 - GPIO_USER << 5, // 36 I NO PULLUP GPIO36, SENSOR_VP, ADC_H, ADC1_CH0, RTC_GPIO0 - 0, // 37 NO PULLUP - 0, // 38 NO PULLUP - GPIO_USER << 5, // 39 I NO PULLUP GPIO39, SENSOR_VN, ADC1_CH3, ADC_H, RTC_GPIO3 - 0 // Flag +{ // WEMOS - Espressif ESP32-DevKitC - Any ESP32 device like WeMos and NodeMCU hardware (ESP32) + AGPIO(GPIO_USER), // 0 (I)O GPIO0, ADC2_CH1, TOUCH1, RTC_GPIO11, CLK_OUT1, EMAC_TX_CLK + AGPIO(GPIO_USER), // 1 IO TXD0 GPIO1, U0TXD, CLK_OUT3, EMAC_RXD2 + AGPIO(GPIO_USER), // 2 IO GPIO2, ADC2_CH2, TOUCH2, RTC_GPIO12, HSPIWP, HS2_DATA0, SD_DATA0 + AGPIO(GPIO_USER), // 3 IO RXD0 GPIO3, U0RXD, CLK_OUT2 + AGPIO(GPIO_USER), // 4 IO GPIO4, ADC2_CH0, TOUCH0, RTC_GPIO10, HSPIHD, HS2_DATA1, SD_DATA1, EMAC_TX_ER + AGPIO(GPIO_USER), // 5 IO GPIO5, VSPICS0, HS1_DATA6, EMAC_RX_CLK + // 6 IO GPIO6, Flash CLK + // 7 IO GPIO7, Flash D0 + // 8 IO GPIO8, Flash D1 + AGPIO(GPIO_USER), // 9 IO GPIO9, Flash D2, U1RXD + AGPIO(GPIO_USER), // 10 IO GPIO10, Flash D3, U1TXD + // 11 IO GPIO11, Flash CMD + AGPIO(GPIO_USER), // 12 (I)O GPIO12, ADC2_CH5, TOUCH5, RTC_GPIO15, MTDI, HSPIQ, HS2_DATA2, SD_DATA2, EMAC_TXD3 (If driven High, flash voltage (VDD_SDIO) is 1.8V not default 3.3V. Has internal pull-down, so unconnected = Low = 3.3V. May prevent flashing and/or booting if 3.3V flash is connected and pulled high. See ESP32 datasheet for more details.) + AGPIO(GPIO_USER), // 13 IO GPIO13, ADC2_CH4, TOUCH4, RTC_GPIO14, MTCK, HSPID, HS2_DATA3, SD_DATA3, EMAC_RX_ER + AGPIO(GPIO_USER), // 14 IO GPIO14, ADC2_CH6, TOUCH6, RTC_GPIO16, MTMS, HSPICLK, HS2_CLK, SD_CLK, EMAC_TXD2 + AGPIO(GPIO_USER), // 15 (I)O GPIO15, ADC2_CH3, TOUCH3, MTDO, HSPICS0, RTC_GPIO13, HS2_CMD, SD_CMD, EMAC_RXD3 (If driven Low, silences boot messages from normal boot. Has internal pull-up, so unconnected = High = normal output.) + AGPIO(GPIO_USER), // 16 IO GPIO16, HS1_DATA4, U2RXD, EMAC_CLK_OUT + AGPIO(GPIO_USER), // 17 IO GPIO17, HS1_DATA5, U2TXD, EMAC_CLK_OUT_180 + AGPIO(GPIO_USER), // 18 IO GPIO18, VSPICLK, HS1_DATA7 + AGPIO(GPIO_USER), // 19 IO GPIO19, VSPIQ, U0CTS, EMAC_TXD0 + 0, // 20 + AGPIO(GPIO_USER), // 21 IO GPIO21, VSPIHD, EMAC_TX_EN + AGPIO(GPIO_USER), // 22 IO LED GPIO22, VSPIWP, U0RTS, EMAC_TXD1 + AGPIO(GPIO_USER), // 23 IO GPIO23, VSPID, HS1_STROBE + 0, // 24 + AGPIO(GPIO_USER), // 25 IO GPIO25, DAC_1, ADC2_CH8, RTC_GPIO6, EMAC_RXD0 + AGPIO(GPIO_USER), // 26 IO GPIO26, DAC_2, ADC2_CH9, RTC_GPIO7, EMAC_RXD1 + AGPIO(GPIO_USER), // 27 IO GPIO27, ADC2_CH7, TOUCH7, RTC_GPIO17, EMAC_RX_DV + 0, // 28 + 0, // 29 + 0, // 30 + 0, // 31 + AGPIO(GPIO_USER), // 32 IO GPIO32, XTAL_32K_P (32.768 kHz crystal oscillator input), ADC1_CH4, TOUCH9, RTC_GPIO9 + AGPIO(GPIO_USER), // 33 IO GPIO33, XTAL_32K_N (32.768 kHz crystal oscillator output), ADC1_CH5, TOUCH8, RTC_GPIO8 + AGPIO(GPIO_USER), // 34 I NO PULLUP GPIO34, ADC1_CH6, RTC_GPIO4 + AGPIO(GPIO_USER), // 35 I NO PULLUP GPIO35, ADC1_CH7, RTC_GPIO5 + AGPIO(GPIO_USER), // 36 I NO PULLUP GPIO36, SENSOR_VP, ADC_H, ADC1_CH0, RTC_GPIO0 + 0, // 37 NO PULLUP + 0, // 38 NO PULLUP + AGPIO(GPIO_USER), // 39 I NO PULLUP GPIO39, SENSOR_VN, ADC1_CH3, ADC_H, RTC_GPIO3 + 0 // Flag }; #endif // ESP32 diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 61f98e649..a4f43b6c6 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -1429,12 +1429,23 @@ void HandleTemplateConfiguration(void) WSContentSend_P(HTTP_SCRIPT_MODULE_TEMPLATE); WSContentSend_P(HTTP_SCRIPT_TEMPLATE); - for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { // GPIO: }2'0'>None (0)}3}2'17'>Button1 (17)}3... + for (uint32_t i = 0; i < ARRAY_SIZE(kGpioNiceList); i++) { // GPIO: }2'0'>None (0)}3}2'17'>Button1 (17)}3... if (1 == i) { - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, GPIO_USER, D_SENSOR_USER, GPIO_USER); // }2'255'>User (255)}3 + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, AGPIO(GPIO_USER), D_SENSOR_USER, AGPIO(GPIO_USER)); // }2'255'>User (255)}3 } +#ifdef ESP8266 uint32_t midx = pgm_read_byte(kGpioNiceList + i); - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); + uint32_t ridx = midx; +#else // ESP32 +#ifndef FINAL_ESP32 + uint32_t midx = pgm_read_byte(kGpioNiceList + i); + uint32_t ridx = midx; +#else // FINAL_ESP32 + uint32_t midx = pgm_read_word(kGpioNiceList + i); + uint32_t ridx = midx << 5; +#endif // FINAL_ESP32 +#endif // ESP8266 - ESP32 + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, ridx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), ridx); } WSContentSend_P(HTTP_SCRIPT_TEMPLATE2); for (uint32_t i = 0; i < ADC0_END; i++) { // FLAG: }2'0'>None (0)}3}2'17'>Analog (17)}3... @@ -1492,7 +1503,7 @@ void TemplateSaveSettings(void) if (8 == i) { j = 12; } snprintf_P(webindex, sizeof(webindex), PSTR("g%d"), j); WebGetArg(webindex, tmp, sizeof(tmp)); // GPIO - uint8_t gpio = atoi(tmp); + uint32_t gpio = atoi(tmp); snprintf_P(svalue, sizeof(svalue), PSTR("%s%s%d"), svalue, (i>0)?",":"", gpio); j++; } @@ -1546,10 +1557,21 @@ void HandleModuleConfiguration(void) WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), vidx); } WSContentSend_P(PSTR("\";sk(%d,99);os=\""), Settings.module); - for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(kGpioNiceList); i++) { +#ifdef ESP8266 midx = pgm_read_byte(kGpioNiceList + i); + uint32_t ridx = midx; +#else // ESP32 +#ifndef FINAL_ESP32 + midx = pgm_read_byte(kGpioNiceList + i); + uint32_t ridx = midx; +#else // FINAL_ESP32 + midx = pgm_read_word(kGpioNiceList + i); + uint32_t ridx = midx << 5; +#endif // FINAL_ESP32 +#endif // ESP8266 - ESP32 if (!GetUsedInModule(midx, cmodule.io)) { - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, ridx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), ridx); } } WSContentSend_P(PSTR("\";")); @@ -1616,7 +1638,7 @@ void ModuleSaveSettings(void) if (ValidGPIO(i, cmodule.io[i])) { snprintf_P(webindex, sizeof(webindex), PSTR("g%d"), i); WebGetArg(webindex, tmp, sizeof(tmp)); - uint8_t value = (!strlen(tmp)) ? 0 : atoi(tmp); + uint32_t value = (!strlen(tmp)) ? 0 : atoi(tmp); #ifdef ESP8266 Settings.my_gp.io[i] = value; #else // ESP32 From a83ea5290ee921850a937aa29655025db6edcf3d Mon Sep 17 00:00:00 2001 From: device111 <48546979+device111@users.noreply.github.com> Date: Wed, 29 Apr 2020 20:33:46 +0200 Subject: [PATCH 393/547] Fix http message time --- tasmota/xsns_67_as3935.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasmota/xsns_67_as3935.ino b/tasmota/xsns_67_as3935.ino index 27b97f9a2..59618c32a 100644 --- a/tasmota/xsns_67_as3935.ino +++ b/tasmota/xsns_67_as3935.ino @@ -548,14 +548,14 @@ void AS3935EverySecond() { as3935_sensor.mqtt_irq = 0; // start http times as3935_sensor.http_count_start = 1; + as3935_sensor.http_count = 0; as3935_sensor.icount++; // Int counter as3935_sensor.detected = false; } if (as3935_sensor.http_count_start) as3935_sensor.http_count++; // clear Http - if (as3935_sensor.http_count == as3935_sensor.http_timer) { - as3935_sensor.http_count = 0; + if (as3935_sensor.http_count > as3935_sensor.http_timer) { as3935_sensor.http_count_start = 0; as3935_sensor.http_intensity = 0; as3935_sensor.http_distance = 0; From 98dc4d8c4daa72ed50a169fb2180af459d864318 Mon Sep 17 00:00:00 2001 From: Javier Arigita Date: Wed, 29 Apr 2020 21:42:20 +0200 Subject: [PATCH 394/547] Support added for DS18B20 temperature sensor and reduction of variables in heating structure --- tasmota/xdrv_39_thermostat.ino | 163 +++++++++++++++++++++------------ 1 file changed, 104 insertions(+), 59 deletions(-) diff --git a/tasmota/xdrv_39_thermostat.ino b/tasmota/xdrv_39_thermostat.ino index 7f5d031cf..37a9378ff 100644 --- a/tasmota/xdrv_39_thermostat.ino +++ b/tasmota/xdrv_39_thermostat.ino @@ -22,13 +22,16 @@ #define XDRV_39 39 // Enable/disable debugging -//#define DEBUG_THERMOSTAT +#define DEBUG_THERMOSTAT + +// Use attached temperature sensor +#define THERMOSTAT_USE_LOCAL_SENSOR #ifdef DEBUG_THERMOSTAT #define DOMOTICZ_IDX1 791 #define DOMOTICZ_IDX2 792 #define DOMOTICZ_IDX3 793 -#endif +#endif // DEBUG_THERMOSTAT // Commands #define D_CMND_THERMOSTATMODESET "ThermostatModeSet" @@ -43,6 +46,7 @@ #define D_CMND_TEMPMEASUREDREAD "TempMeasuredRead" #define D_CMND_TEMPMEASUREDGRDREAD "TempMeasuredGrdRead" #define D_CMND_TEMPSENSNUMBERSET "TempSensNumberSet" +#define D_CMND_SENSORINPUTSET "SensorInputSet" #define D_CMND_STATEEMERGENCYSET "StateEmergencySet" #define D_CMND_POWERMAXSET "PowerMaxSet" #define D_CMND_TIMEMANUALTOAUTOSET "TimeManualToAutoSet" @@ -70,6 +74,7 @@ enum ControllerHybridPhases { CTR_HYBRID_RAMP_UP, CTR_HYBRID_PI }; enum InterfaceStates { IFACE_OFF, IFACE_ON }; enum CtrCycleStates { CYCLE_OFF, CYCLE_ON }; enum EmergencyStates { EMERGENCY_OFF, EMERGENCY_ON }; +enum SensorType { SENSOR_MQTT, SENSOR_DS18B20, SENSOR_MAX }; enum ThermostatSupportedInputSwitches { THERMOSTAT_INPUT_NONE, THERMOSTAT_INPUT_SWT1 = 1, // Buttons @@ -90,28 +95,32 @@ enum ThermostatSupportedOutputRelays { }; typedef union { - uint16_t data; + uint32_t data; struct { - uint16_t thermostat_mode : 2; // Operation mode of the thermostat system - uint16_t controller_mode : 2; // Operation mode of the thermostat controller - uint16_t sensor_alive : 1; // Flag stating if temperature sensor is alive (0 = inactive, 1 = active) - uint16_t command_output : 1; // Flag stating state to save the command to the output (0 = inactive, 1 = active) - uint16_t phase_hybrid_ctr : 1; // Phase of the hybrid controller (Ramp-up or PI) - uint16_t status_output : 1; // Status of the output switch - uint16_t status_cycle_active : 1; // Status showing if cycle is active (Output ON) or not (Output OFF) - uint16_t state_emergency : 1; // State for thermostat emergency - uint16_t counter_seconds : 6; // Second counter used to track minutes + uint32_t thermostat_mode : 2; // Operation mode of the thermostat system + uint32_t controller_mode : 2; // Operation mode of the thermostat controller + uint32_t sensor_alive : 1; // Flag stating if temperature sensor is alive (0 = inactive, 1 = active) + uint32_t sensor_type : 1; // Sensor type: MQTT/local + uint32_t command_output : 1; // Flag stating state to save the command to the output (0 = inactive, 1 = active) + uint32_t phase_hybrid_ctr : 1; // Phase of the hybrid controller (Ramp-up or PI) + uint32_t status_output : 1; // Status of the output switch + uint32_t status_cycle_active : 1; // Status showing if cycle is active (Output ON) or not (Output OFF) + uint32_t state_emergency : 1; // State for thermostat emergency + uint32_t counter_seconds : 6; // Second counter used to track minutes + uint32_t output_relay_number : 4; // Output relay number + uint32_t input_switch_number : 3; // Input switch number + uint32_t free : 8; // Free bits in Bitfield }; } ThermostatBitfield; #ifdef DEBUG_THERMOSTAT const char DOMOTICZ_MES[] PROGMEM = "{\"idx\":%d,\"nvalue\":%d,\"svalue\":\"%s\"}"; -#endif +#endif // DEBUG_THERMOSTAT const char kThermostatCommands[] PROGMEM = "|" D_CMND_THERMOSTATMODESET "|" D_CMND_TEMPFROSTPROTECTSET "|" D_CMND_CONTROLLERMODESET "|" D_CMND_INPUTSWITCHSET "|" D_CMND_OUTPUTRELAYSET "|" D_CMND_TIMEALLOWRAMPUPSET "|" D_CMND_TEMPMEASUREDSET "|" D_CMND_TEMPTARGETSET "|" D_CMND_TEMPTARGETREAD "|" - D_CMND_TEMPMEASUREDREAD "|" D_CMND_TEMPMEASUREDGRDREAD "|" D_CMND_TEMPSENSNUMBERSET "|" + D_CMND_TEMPMEASUREDREAD "|" D_CMND_TEMPMEASUREDGRDREAD "|" D_CMND_SENSORINPUTSET "|" D_CMND_STATEEMERGENCYSET "|" D_CMND_POWERMAXSET "|" D_CMND_TIMEMANUALTOAUTOSET "|" D_CMND_TIMEONLIMITSET "|" D_CMND_PROPBANDSET "|" D_CMND_TIMERESETSET "|" D_CMND_TIMEPICYCLESET "|" D_CMND_TEMPANTIWINDUPRESETSET "|" D_CMND_TEMPHYSTSET "|" D_CMND_TIMEMAXACTIONSET "|" D_CMND_TIMEMINACTIONSET "|" D_CMND_TIMEMINTURNOFFACTIONSET "|" @@ -121,7 +130,7 @@ const char kThermostatCommands[] PROGMEM = "|" D_CMND_THERMOSTATMODESET "|" D_CM void (* const ThermostatCommand[])(void) PROGMEM = { &CmndThermostatModeSet, &CmndTempFrostProtectSet, &CmndControllerModeSet, &CmndInputSwitchSet, &CmndOutputRelaySet, &CmndTimeAllowRampupSet, &CmndTempMeasuredSet, &CmndTempTargetSet, &CmndTempTargetRead, - &CmndTempMeasuredRead, &CmndTempMeasuredGrdRead, &CmndTempSensNumberSet, &CmndStateEmergencySet, + &CmndTempMeasuredRead, &CmndTempMeasuredGrdRead, &CmndSensorInputSet, &CmndStateEmergencySet, &CmndPowerMaxSet, &CmndTimeManualToAutoSet, &CmndTimeOnLimitSet, &CmndPropBandSet, &CmndTimeResetSet, &CmndTimePiCycleSet, &CmndTempAntiWindupResetSet, &CmndTempHystSet, &CmndTimeMaxActionSet, &CmndTimeMinActionSet, &CmndTimeMinTurnoffActionSet, &CmndTempRupDeltInSet, &CmndTempRupDeltOutSet, @@ -129,6 +138,7 @@ void (* const ThermostatCommand[])(void) PROGMEM = { &CmndTimePiIntegrRead, &CmndTimeSensLostSet }; struct THERMOSTAT { + ThermostatBitfield status; // Bittfield including states as well as several flags uint32_t timestamp_temp_measured_update = 0; // Timestamp of latest measurement update uint32_t timestamp_temp_meas_change_update = 0; // Timestamp of latest measurement value change (> or < to previous) uint32_t timestamp_output_off = 0; // Timestamp of latest thermostat output Off state @@ -137,8 +147,8 @@ struct THERMOSTAT { uint32_t time_ctr_checkpoint = 0; // Time to finalize the control cycle within the PI strategy or to switch to PI from Rampup uint32_t time_ctr_changepoint = 0; // Time until switching off output within the controller in seconds int32_t temp_measured_gradient = 0; // Temperature measured gradient from sensor in thousandths of degrees per hour - int16_t temp_target_level = THERMOSTAT_TEMP_INIT; // Target level of the thermostat in tenths of degrees - int16_t temp_target_level_ctr = THERMOSTAT_TEMP_INIT; // Target level set for the controller + int16_t temp_target_level = THERMOSTAT_TEMP_INIT; // Target level of the thermostat in tenths of degrees + int16_t temp_target_level_ctr = THERMOSTAT_TEMP_INIT; // Target level set for the controller int16_t temp_pi_accum_error = 0; // Temperature accumulated error for the PI controller in hundredths of degrees int16_t temp_pi_error = 0; // Temperature error for the PI controller in hundredths of degrees int32_t time_proportional_pi; // Time proportional part of the PI controller @@ -151,15 +161,13 @@ struct THERMOSTAT { uint32_t time_rampup_deadtime = 0; // Time constant of the thermostat system (step response time) uint32_t time_rampup_nextcycle = 0; // Time where the ramp-up controller shall start the next cycle int16_t temp_measured = 0; // Temperature measurement received from sensor in tenths of degrees + int16_t temp_rampup_output_off = 0; // Temperature to swith off relay output within the ramp-up controller in tenths of degrees uint8_t time_output_delay = THERMOSTAT_TIME_OUTPUT_DELAY; // Output delay between state change and real actuation event (f.i. valve open/closed) - uint8_t counter_rampup_cycles = 0; // Counter of ramp-up cycles - uint8_t output_relay_number = THERMOSTAT_RELAY_NUMBER; // Output relay number - uint8_t input_switch_number = THERMOSTAT_SWITCH_NUMBER; // Input switch number - uint8_t temp_sens_number = THERMOSTAT_TEMP_SENS_NUMBER; // Temperature sensor number + uint8_t counter_rampup_cycles = 0; // Counter of ramp-up cycles uint8_t temp_rampup_pi_acc_error = THERMOSTAT_TEMP_PI_RAMPUP_ACC_E; // Accumulated error when switching from ramp-up controller to PI uint8_t temp_rampup_delta_out = THERMOSTAT_TEMP_RAMPUP_DELTA_OUT; // Minimum delta temperature to target to get out of the rampup mode, in tenths of degrees celsius uint8_t temp_rampup_delta_in = THERMOSTAT_TEMP_RAMPUP_DELTA_IN; // Minimum delta temperature to target to get into rampup mode, in tenths of degrees celsius - int16_t temp_rampup_output_off = 0; // Temperature to swith off relay output within the ramp-up controller in tenths of degrees + uint8_t val_prop_band = THERMOSTAT_PROP_BAND; // Proportional band of the PI controller in degrees celsius int16_t temp_rampup_start = 0; // Temperature at start of ramp-up controller in tenths of degrees celsius int16_t temp_rampup_cycle = 0; // Temperature set at the beginning of each ramp-up cycle in tenths of degrees uint16_t time_rampup_max = THERMOSTAT_TIME_RAMPUP_MAX; // Time maximum ramp-up controller duration in minutes @@ -168,34 +176,36 @@ struct THERMOSTAT { uint16_t time_sens_lost = THERMOSTAT_TIME_SENS_LOST; // Maximum time w/o sensor update to set it as lost uint16_t time_manual_to_auto = THERMOSTAT_TIME_MANUAL_TO_AUTO; // Time without input switch active to change from manual to automatic in minutes uint16_t time_on_limit = THERMOSTAT_TIME_ON_LIMIT; // Maximum time with output active in minutes - uint32_t time_reset = THERMOSTAT_TIME_RESET; // Reset time of the PI controller in seconds uint16_t time_pi_cycle = THERMOSTAT_TIME_PI_CYCLE; // Cycle time for the thermostat controller in seconds + uint32_t time_reset = THERMOSTAT_TIME_RESET; // Reset time of the PI controller in seconds uint16_t time_max_action = THERMOSTAT_TIME_MAX_ACTION; // Maximum thermostat time per cycle in minutes uint16_t time_min_action = THERMOSTAT_TIME_MIN_ACTION; // Minimum thermostat time per cycle in minutes uint16_t time_min_turnoff_action = THERMOSTAT_TIME_MIN_TURNOFF_ACTION; // Minimum turnoff time in minutes, below it the thermostat will be held on - uint8_t val_prop_band = THERMOSTAT_PROP_BAND; // Proportional band of the PI controller in degrees celsius uint8_t temp_reset_anti_windup = THERMOSTAT_TEMP_RESET_ANTI_WINDUP; // Range where reset antiwindup is disabled, in tenths of degrees celsius int8_t temp_hysteresis = THERMOSTAT_TEMP_HYSTERESIS; // Range hysteresis for temperature PI controller, in tenths of degrees celsius - uint8_t temp_frost_protect = THERMOSTAT_TEMP_FROST_PROTECT; // Minimum temperature for frost protection, in tenths of degrees celsius uint16_t power_max = THERMOSTAT_POWER_MAX; // Maximum output power in Watt - ThermostatBitfield status; // Bittfield including states as well as several flags + uint8_t temp_frost_protect = THERMOSTAT_TEMP_FROST_PROTECT; // Minimum temperature for frost protection, in tenths of degrees celsius } Thermostat; /*********************************************************************************************/ void ThermostatInit(void) { - ExecuteCommandPower(Thermostat.output_relay_number, POWER_OFF, SRC_THERMOSTAT); // Make sure the Output is OFF // Init Thermostat.status bitfield: Thermostat.status.thermostat_mode = THERMOSTAT_OFF; Thermostat.status.controller_mode = CTR_HYBRID; Thermostat.status.sensor_alive = IFACE_OFF; + Thermostat.status.sensor_type = SENSOR_MQTT; Thermostat.status.command_output = IFACE_OFF; Thermostat.status.phase_hybrid_ctr = CTR_HYBRID_PI; Thermostat.status.status_output = IFACE_OFF; Thermostat.status.status_cycle_active = CYCLE_OFF; Thermostat.status.state_emergency = EMERGENCY_OFF; Thermostat.status.counter_seconds = 0; + Thermostat.status.output_relay_number = THERMOSTAT_RELAY_NUMBER; + Thermostat.status.input_switch_number = THERMOSTAT_SWITCH_NUMBER; + // Make sure the Output is OFF + ExecuteCommandPower(Thermostat.status.output_relay_number, POWER_OFF, SRC_THERMOSTAT); } bool ThermostatMinuteCounter(void) @@ -240,7 +250,7 @@ void ThermostatSignalProcessingSlow(void) void ThermostatSignalProcessingFast(void) { - if (ThermostatSwitchStatus(Thermostat.input_switch_number)) { // Check if input switch active and register last update + if (ThermostatSwitchStatus(Thermostat.status.input_switch_number)) { // Check if input switch active and register last update Thermostat.timestamp_input_on = uptime; } } @@ -297,7 +307,7 @@ void ThermostatHybridCtrPhase(void) } #ifdef DEBUG_THERMOSTAT ThermostatVirtualSwitchCtrState(); -#endif +#endif // DEBUG_THERMOSTAT } bool HeatStateAutoToManual(void) @@ -307,7 +317,7 @@ bool HeatStateAutoToManual(void) // If switch input is active // OR temperature sensor is not alive // then go to manual - if ((ThermostatSwitchStatus(Thermostat.input_switch_number) == 1) + if ((ThermostatSwitchStatus(Thermostat.status.input_switch_number) == 1) || (Thermostat.status.sensor_alive == IFACE_OFF)) { change_state = true; } @@ -322,7 +332,7 @@ bool HeatStateManualToAuto(void) // AND sensor alive // AND no switch input action (time in current state) bigger than a pre-defined time // then go to automatic - if ((ThermostatSwitchStatus(Thermostat.input_switch_number) == 0) + if ((ThermostatSwitchStatus(Thermostat.status.input_switch_number) == 0) &&(Thermostat.status.sensor_alive == IFACE_ON) && ((uptime - Thermostat.timestamp_input_on) > ((uint32_t)Thermostat.time_manual_to_auto * 60))) { change_state = true; @@ -370,32 +380,32 @@ void ThermostatState(void) void ThermostatOutputRelay(bool active) { - // TODO: See if the real output state can be read by f.i. bitRead(power, Thermostat.output_relay_number)) + // TODO: See if the real output state can be read by f.i. bitRead(power, Thermostat.status.output_relay_number)) // If command received to enable output // AND current output status is OFF // then switch output to ON if ((active == true) && (Thermostat.status.status_output == IFACE_OFF)) { #ifndef DEBUG_THERMOSTAT - ExecuteCommandPower(Thermostat.output_relay_number, POWER_ON, SRC_THERMOSTAT); -#endif + ExecuteCommandPower(Thermostat.status.output_relay_number, POWER_ON, SRC_THERMOSTAT); +#endif // DEBUG_THERMOSTAT Thermostat.status.status_output = IFACE_ON; #ifdef DEBUG_THERMOSTAT ThermostatVirtualSwitch(); -#endif +#endif // DEBUG_THERMOSTAT } // If command received to disable output // AND current output status is ON // then switch output to OFF else if ((active == false) && (Thermostat.status.status_output == IFACE_ON)) { #ifndef DEBUG_THERMOSTAT - ExecuteCommandPower(Thermostat.output_relay_number, POWER_OFF, SRC_THERMOSTAT); -#endif + ExecuteCommandPower(Thermostat.status.output_relay_number, POWER_OFF, SRC_THERMOSTAT); +#endif // DEBUG_THERMOSTAT Thermostat.timestamp_output_off = uptime; Thermostat.status.status_output = IFACE_OFF; #ifdef DEBUG_THERMOSTAT ThermostatVirtualSwitch(); -#endif +#endif // DEBUG_THERMOSTAT } } @@ -740,7 +750,7 @@ void ThermostatWork(void) break; case THERMOSTAT_MANUAL_OP: // State manual operation following input switch Thermostat.time_ctr_checkpoint = 0; - if (ThermostatSwitchStatus(Thermostat.input_switch_number) == 1) { + if (ThermostatSwitchStatus(Thermostat.status.input_switch_number) == 1) { Thermostat.status.command_output = IFACE_ON; } else { @@ -810,7 +820,35 @@ void ThermostatVirtualSwitchCtrState(void) //Response_P(DOMOTICZ_MES, DOMOTICZ_IDX3, (0 == Thermostat.time_ctr_changepoint) ? 0 : 1, ""); //MqttPublish(domoticz_in_topic); } -#endif +#endif // DEBUG_THERMOSTAT + +#ifdef THERMOSTAT_USE_LOCAL_SENSOR +void ThermostatGetLocalSensor(void) { + DynamicJsonBuffer jsonBuffer; + JsonObject& root = jsonBuffer.parseObject((const char*)mqtt_data); + if (root.success()) { + const char* value_c = root["DS18B20"]["Temperature"]; + if (value_c != NULL && strlen(value_c) > 0 && (isdigit(value_c[0]) || (value_c[0] == '-' && isdigit(value_c[1])) ) ) { + int16_t value = (int16_t)(CharToFloat(value_c) * 10); + if ( (value >= -1000) + && (value <= 1000) + && (Thermostat.status.sensor_type == SENSOR_DS18B20)) { + uint32_t timestamp = uptime; + // Calculate temperature gradient if temperature value has changed + if (value != Thermostat.temp_measured) { + int32_t temp_delta = (value - Thermostat.temp_measured); // in tenths of degrees + uint32_t time_delta = (timestamp - Thermostat.timestamp_temp_meas_change_update); // in seconds + Thermostat.temp_measured_gradient = (int32_t)((360000 * temp_delta) / ((int32_t)time_delta)); // hundreths of degrees per hour + Thermostat.temp_measured = value; + Thermostat.timestamp_temp_meas_change_update = timestamp; + } + Thermostat.timestamp_temp_measured_update = timestamp; + Thermostat.status.sensor_alive = IFACE_ON; + } + } + } +} +#endif // THERMOSTAT_USE_LOCAL_SENSOR /*********************************************************************************************\ * Commands @@ -855,11 +893,22 @@ void CmndInputSwitchSet(void) if (XdrvMailbox.data_len > 0) { uint8_t value = (uint8_t)(XdrvMailbox.payload); if (ThermostatSwitchIdValid(value)) { - Thermostat.input_switch_number = value; + Thermostat.status.input_switch_number = value; Thermostat.timestamp_input_on = uptime; } } - ResponseCmndNumber((int)Thermostat.input_switch_number); + ResponseCmndNumber((int)Thermostat.status.input_switch_number); +} + +void CmndSensorInputSet(void) +{ + if (XdrvMailbox.data_len > 0) { + uint8_t value = (uint8_t)(XdrvMailbox.payload); + if ((value >= 0) && (value < SENSOR_MAX)) { + Thermostat.status.sensor_type = value; + } + } + ResponseCmndNumber((int)Thermostat.status.sensor_type); } void CmndOutputRelaySet(void) @@ -867,10 +916,10 @@ void CmndOutputRelaySet(void) if (XdrvMailbox.data_len > 0) { uint8_t value = (uint8_t)(XdrvMailbox.payload); if (ThermostatRelayIdValid(value)) { - Thermostat.output_relay_number = value; + Thermostat.status.output_relay_number = value; } } - ResponseCmndNumber((int)Thermostat.output_relay_number); + ResponseCmndNumber((int)Thermostat.status.output_relay_number); } void CmndTimeAllowRampupSet(void) @@ -888,7 +937,9 @@ void CmndTempMeasuredSet(void) { if (XdrvMailbox.data_len > 0) { int16_t value = (int16_t)(CharToFloat(XdrvMailbox.data) * 10); - if ((value >= -1000) && (value <= 1000)) { + if ( (value >= -1000) + && (value <= 1000) + && (Thermostat.status.sensor_type == SENSOR_MQTT)) { uint32_t timestamp = uptime; // Calculate temperature gradient if temperature value has changed if (value != Thermostat.temp_measured) { @@ -909,7 +960,7 @@ void CmndTempTargetSet(void) { if (XdrvMailbox.data_len > 0) { uint16_t value = (uint16_t)(CharToFloat(XdrvMailbox.data) * 10); - if ((value >= -1000) + if ( (value >= -1000) && (value <= 1000) && (value >= (int16_t)Thermostat.temp_frost_protect)) { Thermostat.temp_target_level = value; @@ -933,17 +984,6 @@ void CmndTempMeasuredGrdRead(void) ResponseCmndFloat((float)(Thermostat.temp_measured_gradient) / 1000, 1); } -void CmndTempSensNumberSet(void) -{ - if (XdrvMailbox.data_len > 0) { - uint8_t value = (uint8_t)(XdrvMailbox.payload); - if ((value >= 0) && (value <= 255)) { - Thermostat.temp_sens_number = value; - } - } - ResponseCmndNumber((int)Thermostat.temp_sens_number); -} - void CmndStateEmergencySet(void) { if (XdrvMailbox.data_len > 0) { @@ -1160,7 +1200,7 @@ bool Xdrv39(uint8_t function) { #ifdef DEBUG_THERMOSTAT char result_chr[FLOATSZ]; -#endif +#endif // DEBUG_THERMOSTAT bool result = false; switch (function) { @@ -1235,9 +1275,14 @@ bool Xdrv39(uint8_t function) AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat.time_rampup_deadtime: %s"), result_chr); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("------ Thermostat End ------")); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("")); -#endif +#endif // DEBUG_THERMOSTAT } break; + case FUNC_SHOW_SENSOR: +#ifdef THERMOSTAT_USE_LOCAL_SENSOR + ThermostatGetLocalSensor(); +#endif // THERMOSTAT_USE_LOCAL_SENSOR + break; case FUNC_COMMAND: result = DecodeCommand(kThermostatCommands, ThermostatCommand); break; From e233c3830de638635a03a228d388b27473396de7 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 30 Apr 2020 14:38:32 +0200 Subject: [PATCH 395/547] Create CI_github.yml --- .github/workflows/CI_github.yml | 441 ++++++++++++++++++++++++++++++++ 1 file changed, 441 insertions(+) create mode 100644 .github/workflows/CI_github.yml diff --git a/.github/workflows/CI_github.yml b/.github/workflows/CI_github.yml new file mode 100644 index 000000000..685af3b24 --- /dev/null +++ b/.github/workflows/CI_github.yml @@ -0,0 +1,441 @@ +name: PlatformIO CI + +on: [push] + +jobs: + tasmota: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota + + tasmota-minimal: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-minimal + + tasmota-lite: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-lite + + tasmota-knx: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-knx + + tasmota-sensors: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-sensors + + + tasmota-display: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-display + + tasmota-ir: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-ir + + tasmota-BG: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-BG + + tasmota-BR: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-BR + + tasmota-CN: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-CN + + tasmota-CZ: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-CZ + + tasmota-DE: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-DE + + tasmota-ES: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-ES + + + tasmota-FR: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-FR + + tasmota-GR: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-GR + + tasmota-HE: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-HE + + tasmota-HU: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-HU + + tasmota-IT: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-IT + + tasmota-KO: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-KO + + tasmota-NL: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-NL + + tasmota-PL: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-PL + + tasmota-PT: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-PT + + tasmota-RO: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-RO + + tasmota-RU: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-RU + + tasmota-SE: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-SE + + tasmota-SK: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-SK + + tasmota-TR: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-TR + + tasmota-TW: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-TW + + tasmota-UK: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-UK From f6389c73852f5997df069df698e14d1a85a563b8 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 30 Apr 2020 14:41:43 +0200 Subject: [PATCH 396/547] CI on Push and Pull --- .github/workflows/CI_github.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI_github.yml b/.github/workflows/CI_github.yml index 685af3b24..6fb55d608 100644 --- a/.github/workflows/CI_github.yml +++ b/.github/workflows/CI_github.yml @@ -1,6 +1,6 @@ name: PlatformIO CI -on: [push] +on: [push, pull_request] jobs: tasmota: From c0a05e9586762020a4945bfb9179758d6065ad26 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 30 Apr 2020 18:47:34 +0200 Subject: [PATCH 397/547] Change ESP32 pin allocation part 3 --- tasmota/support_command.ino | 25 ++- tasmota/tasmota_template_ESP32_final.h | 294 ++++++++++++------------- tasmota/xdrv_01_webserver.ino | 80 ++++++- 3 files changed, 239 insertions(+), 160 deletions(-) diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 7bd3fcde5..a446fec30 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -1062,8 +1062,8 @@ void CmndGpio(void) break; } #else // FINAL_ESP32 - uint32_t midx = pgm_read_word(kGpioNiceList + i) << 5; - if (midx == (XdrvMailbox.payload & 0xFFE0)) { + uint32_t midx = pgm_read_word(kGpioNiceList + i); + if ((XdrvMailbox.payload >= (midx & 0xFFE0)) && (XdrvMailbox.payload < midx)) { present = true; break; } @@ -1083,16 +1083,17 @@ void CmndGpio(void) Response_P(PSTR("{")); bool jsflg = false; for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { - if (ValidGPIO(i, cmodule.io[i]) || ((GPIO_USER == XdrvMailbox.payload) && !FlashPin(i))) { + if (ValidGPIO(i, cmodule.io[i]) || ((AGPIO(GPIO_USER) == XdrvMailbox.payload) && !FlashPin(i))) { if (jsflg) { ResponseAppend_P(PSTR(",")); } jsflg = true; uint32_t sensor_type = Settings.my_gp.io[i]; if (!ValidGPIO(i, cmodule.io[i])) { sensor_type = cmodule.io[i]; - if (GPIO_USER == sensor_type) { // A user GPIO equals a not connected (=GPIO_NONE) GPIO here + if (AGPIO(GPIO_USER) == sensor_type) { // A user GPIO equals a not connected (=GPIO_NONE) GPIO here sensor_type = GPIO_NONE; } } + char sindex[4] = { 0 }; #ifdef ESP8266 uint32_t sensor_name_idx = sensor_type; #else // ESP32 @@ -1100,6 +1101,14 @@ void CmndGpio(void) uint32_t sensor_name_idx = sensor_type; #else // FINAL_ESP32 uint32_t sensor_name_idx = sensor_type >> 5; + uint32_t nice_list_search = sensor_type & 0xFFE0; + for (uint32_t j = 0; j < ARRAY_SIZE(kGpioNiceList); j++) { + uint32_t nls_idx = pgm_read_word(kGpioNiceList + j); + if (((nls_idx & 0xFFE0) == nice_list_search) && ((nls_idx & 0x001F) > 0)) { + snprintf_P(sindex, sizeof(sindex), PSTR("%d"), (sensor_type & 0x001F) +1); + break; + } + } #endif // FINAL_ESP32 #endif // ESP8266 - ESP32 const char *sensor_names = kSensorNames; @@ -1108,8 +1117,8 @@ void CmndGpio(void) sensor_names = kSensorNamesFixed; } char stemp1[TOPSZ]; - ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":{\"%d\":\"%s\"}"), - i, sensor_type, GetTextIndexed(stemp1, sizeof(stemp1), sensor_name_idx, sensor_names)); + ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":{\"%d\":\"%s%s\"}"), + i, sensor_type, GetTextIndexed(stemp1, sizeof(stemp1), sensor_name_idx, sensor_names), sindex); } } if (jsflg) { @@ -1135,8 +1144,8 @@ void CmndGpios(void) uint32_t midx = pgm_read_byte(kGpioNiceList + i); uint32_t ridx = midx; #else // FINAL_ESP32 - uint32_t midx = pgm_read_word(kGpioNiceList + i); - uint32_t ridx = midx << 5; + uint32_t ridx = pgm_read_word(kGpioNiceList + i) & 0xFFE0; + uint32_t midx = ridx >> 5; #endif // FINAL_ESP32 #endif // ESP8266 - ESP32 if ((XdrvMailbox.payload != 255) && GetUsedInModule(midx, cmodule.io)) { continue; } diff --git a/tasmota/tasmota_template_ESP32_final.h b/tasmota/tasmota_template_ESP32_final.h index b48897094..bd8063d33 100644 --- a/tasmota/tasmota_template_ESP32_final.h +++ b/tasmota/tasmota_template_ESP32_final.h @@ -297,290 +297,290 @@ const char kSensorNamesFixed[] PROGMEM = D_SENSOR_USER; const uint16_t kGpioNiceList[] PROGMEM = { - GPIO_NONE, // Not used - GPIO_KEY1, // Buttons - GPIO_KEY1_NP, - GPIO_KEY1_INV, - GPIO_KEY1_INV_NP, - GPIO_SWT1, // User connected external switches - GPIO_SWT1_NP, - GPIO_REL1, // Relays - GPIO_REL1_INV, - GPIO_LED1, // Leds - GPIO_LED1_INV, + GPIO_NONE, // Not used + AGPIO(GPIO_KEY1) + MAX_KEYS, // Buttons + AGPIO(GPIO_KEY1_NP) + MAX_KEYS, + AGPIO(GPIO_KEY1_INV) + MAX_KEYS, + AGPIO(GPIO_KEY1_INV_NP) + MAX_KEYS, + AGPIO(GPIO_SWT1) + MAX_SWITCHES, // User connected external switches + AGPIO(GPIO_SWT1_NP) + MAX_SWITCHES, + AGPIO(GPIO_REL1) + MAX_RELAYS, // Relays + AGPIO(GPIO_REL1_INV) + MAX_RELAYS, + AGPIO(GPIO_LED1) + MAX_LEDS, // Leds + AGPIO(GPIO_LED1_INV) + MAX_LEDS, #ifdef USE_COUNTER - GPIO_CNTR1, // Counters - GPIO_CNTR1_NP, + AGPIO(GPIO_CNTR1) + MAX_COUNTERS, // Counters + AGPIO(GPIO_CNTR1_NP) + MAX_COUNTERS, #endif - GPIO_PWM1, // RGB Red or C Cold White - GPIO_PWM1_INV, + AGPIO(GPIO_PWM1) + MAX_PWMS, // RGB Red or C Cold White + AGPIO(GPIO_PWM1_INV) + MAX_PWMS, #ifdef USE_BUZZER - GPIO_BUZZER, // Buzzer - GPIO_BUZZER_INV, // Inverted buzzer + AGPIO(GPIO_BUZZER), // Buzzer + AGPIO(GPIO_BUZZER_INV), // Inverted buzzer #endif - GPIO_LEDLNK, // Link led - GPIO_LEDLNK_INV, // Inverted link led + AGPIO(GPIO_LEDLNK), // Link led + AGPIO(GPIO_LEDLNK_INV), // Inverted link led #ifdef USE_I2C - GPIO_I2C_SCL, // I2C SCL - GPIO_I2C_SDA, // I2C SDA + AGPIO(GPIO_I2C_SCL), // I2C SCL + AGPIO(GPIO_I2C_SDA), // I2C SDA #endif #ifdef USE_SPI - GPIO_SPI_MISO, // SPI MISO - GPIO_SPI_MOSI, // SPI MOSI - GPIO_SPI_CLK, // SPI Clk - GPIO_SPI_CS, // SPI Chip Select - GPIO_SPI_DC, // SPI Data Direction - GPIO_SSPI_MISO, // Software SPI Master Input Slave Output - GPIO_SSPI_MOSI, // Software SPI Master Output Slave Input - GPIO_SSPI_SCLK, // Software SPI Serial Clock - GPIO_SSPI_CS, // Software SPI Chip Select - GPIO_SSPI_DC, // Software SPI Data or Command + AGPIO(GPIO_SPI_MISO), // SPI MISO + AGPIO(GPIO_SPI_MOSI), // SPI MOSI + AGPIO(GPIO_SPI_CLK), // SPI Clk + AGPIO(GPIO_SPI_CS), // SPI Chip Select + AGPIO(GPIO_SPI_DC), // SPI Data Direction + AGPIO(GPIO_SSPI_MISO), // Software SPI Master Input Slave Output + AGPIO(GPIO_SSPI_MOSI), // Software SPI Master Output Slave Input + AGPIO(GPIO_SSPI_SCLK), // Software SPI Serial Clock + AGPIO(GPIO_SSPI_CS), // Software SPI Chip Select + AGPIO(GPIO_SSPI_DC), // Software SPI Data or Command #endif #ifdef USE_DISPLAY - GPIO_BACKLIGHT, // Display backlight control - GPIO_OLED_RESET, // OLED Display Reset + AGPIO(GPIO_BACKLIGHT), // Display backlight control + AGPIO(GPIO_OLED_RESET), // OLED Display Reset #endif - GPIO_TXD, // Serial interface - GPIO_RXD, // Serial interface + AGPIO(GPIO_TXD), // Serial interface + AGPIO(GPIO_RXD), // Serial interface #ifdef USE_DHT - GPIO_DHT11, // DHT11 - GPIO_DHT22, // DHT21, DHT22, AM2301, AM2302, AM2321 - GPIO_SI7021, // iTead SI7021 - GPIO_DHT11_OUT, // Pseudo Single wire DHT11, DHT21, DHT22, AM2301, AM2302, AM2321 + AGPIO(GPIO_DHT11), // DHT11 + AGPIO(GPIO_DHT22), // DHT21, DHT22, AM2301, AM2302, AM2321 + AGPIO(GPIO_SI7021), // iTead SI7021 + AGPIO(GPIO_DHT11_OUT), // Pseudo Single wire DHT11, DHT21, DHT22, AM2301, AM2302, AM2321 #endif #ifdef USE_DS18x20 - GPIO_DSB, // Single wire DS18B20 or DS18S20 - GPIO_DSB_OUT, // Pseudo Single wire DS18B20 or DS18S20 + AGPIO(GPIO_DSB), // Single wire DS18B20 or DS18S20 + AGPIO(GPIO_DSB_OUT), // Pseudo Single wire DS18B20 or DS18S20 #endif // Light #ifdef USE_LIGHT #ifdef USE_WS2812 - GPIO_WS2812, // WS2812 Led string + AGPIO(GPIO_WS2812), // WS2812 Led string #endif #ifdef USE_ARILUX_RF - GPIO_ARIRFRCV, // AriLux RF Receive input - GPIO_ARIRFSEL, // Arilux RF Receive input selected + AGPIO(GPIO_ARIRFRCV), // AriLux RF Receive input + AGPIO(GPIO_ARIRFSEL), // Arilux RF Receive input selected #endif #ifdef USE_MY92X1 - GPIO_DI, // my92x1 PWM input - GPIO_DCKI, // my92x1 CLK input + AGPIO(GPIO_DI), // my92x1 PWM input + AGPIO(GPIO_DCKI), // my92x1 CLK input #endif // USE_MY92X1 #ifdef USE_SM16716 - GPIO_SM16716_CLK, // SM16716 CLOCK - GPIO_SM16716_DAT, // SM16716 DATA - GPIO_SM16716_SEL, // SM16716 SELECT + AGPIO(GPIO_SM16716_CLK), // SM16716 CLOCK + AGPIO(GPIO_SM16716_DAT), // SM16716 DATA + AGPIO(GPIO_SM16716_SEL), // SM16716 SELECT #endif // USE_SM16716 #ifdef USE_SM2135 - GPIO_SM2135_CLK, // SM2135 CLOCK - GPIO_SM2135_DAT, // SM2135 DATA + AGPIO(GPIO_SM2135_CLK), // SM2135 CLOCK + AGPIO(GPIO_SM2135_DAT), // SM2135 DATA #endif // USE_SM2135 #ifdef USE_TUYA_MCU - GPIO_TUYA_TX, // Tuya Serial interface - GPIO_TUYA_RX, // Tuya Serial interface + AGPIO(GPIO_TUYA_TX), // Tuya Serial interface + AGPIO(GPIO_TUYA_RX), // Tuya Serial interface #endif #ifdef USE_EXS_DIMMER - GPIO_EXS_ENABLE, // EXS MCU Enable + AGPIO(GPIO_EXS_ENABLE), // EXS MCU Enable #endif #ifdef USE_ELECTRIQ_MOODL - GPIO_ELECTRIQ_MOODL_TX, + AGPIO(GPIO_ELECTRIQ_MOODL_TX), #endif #endif // USE_LIGHT #if defined(USE_IR_REMOTE) || defined(USE_IR_REMOTE_FULL) - GPIO_IRSEND, // IR remote + AGPIO(GPIO_IRSEND), // IR remote #if defined(USE_IR_RECEIVE) || defined(USE_IR_REMOTE_FULL) - GPIO_IRRECV, // IR receiver + AGPIO(GPIO_IRRECV), // IR receiver #endif #endif #ifdef USE_RC_SWITCH - GPIO_RFSEND, // RF transmitter - GPIO_RFRECV, // RF receiver + AGPIO(GPIO_RFSEND), // RF transmitter + AGPIO(GPIO_RFRECV), // RF receiver #endif #ifdef USE_RF_SENSOR - GPIO_RF_SENSOR, // Rf receiver with sensor decoding + AGPIO(GPIO_RF_SENSOR), // Rf receiver with sensor decoding #endif #ifdef USE_SR04 - GPIO_SR04_TRIG, // SR04 Tri/TXgger pin - GPIO_SR04_ECHO, // SR04 Ech/RXo pin + AGPIO(GPIO_SR04_TRIG), // SR04 Tri/TXgger pin + AGPIO(GPIO_SR04_ECHO), // SR04 Ech/RXo pin #endif #ifdef USE_TM1638 - GPIO_TM16CLK, // TM1638 Clock - GPIO_TM16DIO, // TM1638 Data I/O - GPIO_TM16STB, // TM1638 Strobe + AGPIO(GPIO_TM16CLK), // TM1638 Clock + AGPIO(GPIO_TM16DIO), // TM1638 Data I/O + AGPIO(GPIO_TM16STB), // TM1638 Strobe #endif #ifdef USE_HX711 - GPIO_HX711_SCK, // HX711 Load Cell clock - GPIO_HX711_DAT, // HX711 Load Cell data + AGPIO(GPIO_HX711_SCK), // HX711 Load Cell clock + AGPIO(GPIO_HX711_DAT), // HX711 Load Cell data #endif // Energy sensors #ifdef USE_ENERGY_SENSOR #ifdef USE_HLW8012 - GPIO_NRG_SEL, // HLW8012/HLJ-01 Sel output (1 = Voltage) - GPIO_NRG_SEL_INV, // HLW8012/HLJ-01 Sel output (0 = Voltage) - GPIO_NRG_CF1, // HLW8012/HLJ-01 CF1 voltage / current - GPIO_HLW_CF, // HLW8012 CF power - GPIO_HJL_CF, // HJL-01/BL0937 CF power + AGPIO(GPIO_NRG_SEL), // HLW8012/HLJ-01 Sel output (1 = Voltage) + AGPIO(GPIO_NRG_SEL_INV), // HLW8012/HLJ-01 Sel output (0 = Voltage) + AGPIO(GPIO_NRG_CF1), // HLW8012/HLJ-01 CF1 voltage / current + AGPIO(GPIO_HLW_CF), // HLW8012 CF power + AGPIO(GPIO_HJL_CF), // HJL-01/BL0937 CF power #endif #if defined(USE_I2C) && defined(USE_ADE7953) - GPIO_ADE7953_IRQ, // ADE7953 IRQ + AGPIO(GPIO_ADE7953_IRQ), // ADE7953 IRQ #endif #ifdef USE_CSE7766 - GPIO_CSE7766_TX, // CSE7766 Serial interface (S31 and Pow R2) - GPIO_CSE7766_RX, // CSE7766 Serial interface (S31 and Pow R2) + AGPIO(GPIO_CSE7766_TX), // CSE7766 Serial interface (S31 and Pow R2) + AGPIO(GPIO_CSE7766_RX), // CSE7766 Serial interface (S31 and Pow R2) #endif #ifdef USE_MCP39F501 - GPIO_MCP39F5_TX, // MCP39F501 Serial interface (Shelly2) - GPIO_MCP39F5_RX, // MCP39F501 Serial interface (Shelly2) - GPIO_MCP39F5_RST, // MCP39F501 Reset (Shelly2) + AGPIO(GPIO_MCP39F5_TX), // MCP39F501 Serial interface (Shelly2) + AGPIO(GPIO_MCP39F5_RX), // MCP39F501 Serial interface (Shelly2) + AGPIO(GPIO_MCP39F5_RST), // MCP39F501 Reset (Shelly2) #endif #if defined(USE_PZEM004T) || defined(USE_PZEM_AC) || defined(USE_PZEM_DC) - GPIO_PZEM0XX_TX, // PZEM0XX Serial interface + AGPIO(GPIO_PZEM0XX_TX), // PZEM0XX Serial interface #endif #ifdef USE_PZEM004T - GPIO_PZEM004_RX, // PZEM004T Serial interface + AGPIO(GPIO_PZEM004_RX), // PZEM004T Serial interface #endif #ifdef USE_PZEM_AC - GPIO_PZEM016_RX, // PZEM-014,016 Serial Modbus interface + AGPIO(GPIO_PZEM016_RX), // PZEM-014,016 Serial Modbus interface #endif #ifdef USE_PZEM_DC - GPIO_PZEM017_RX, // PZEM-003,017 Serial Modbus interface + AGPIO(GPIO_PZEM017_RX), // PZEM-003,017 Serial Modbus interface #endif #ifdef USE_SDM120 - GPIO_SDM120_TX, // SDM120 Serial interface - GPIO_SDM120_RX, // SDM120 Serial interface + AGPIO(GPIO_SDM120_TX), // SDM120 Serial interface + AGPIO(GPIO_SDM120_RX), // SDM120 Serial interface #endif #ifdef USE_SDM630 - GPIO_SDM630_TX, // SDM630 Serial interface - GPIO_SDM630_RX, // SDM630 Serial interface + AGPIO(GPIO_SDM630_TX), // SDM630 Serial interface + AGPIO(GPIO_SDM630_RX), // SDM630 Serial interface #endif #ifdef USE_DDS2382 - GPIO_DDS2382_TX, // DDS2382 Serial interface - GPIO_DDS2382_RX, // DDS2382 Serial interface + AGPIO(GPIO_DDS2382_TX), // DDS2382 Serial interface + AGPIO(GPIO_DDS2382_RX), // DDS2382 Serial interface #endif #ifdef USE_DDSU666 - GPIO_DDSU666_TX, // DDSU666 Serial interface - GPIO_DDSU666_RX, // DDSU666 Serial interface + AGPIO(GPIO_DDSU666_TX), // DDSU666 Serial interface + AGPIO(GPIO_DDSU666_RX), // DDSU666 Serial interface #endif // USE_DDSU666 #ifdef USE_SOLAX_X1 - GPIO_SOLAXX1_TX, // Solax Inverter tx pin - GPIO_SOLAXX1_RX, // Solax Inverter rx pin + AGPIO(GPIO_SOLAXX1_TX), // Solax Inverter tx pin + AGPIO(GPIO_SOLAXX1_RX), // Solax Inverter rx pin #endif // USE_SOLAX_X1 #ifdef USE_LE01MR - GPIO_LE01MR_RX, // F7F LE-01MR energy meter rx pin - GPIO_LE01MR_TX, // F7F LE-01MR energy meter tx pin + AGPIO(GPIO_LE01MR_RX), // F7F LE-01MR energy meter rx pin + AGPIO(GPIO_LE01MR_TX), // F7F LE-01MR energy meter tx pin #endif // IFDEF:USE_LE01MR #endif // USE_ENERGY_SENSOR // Serial #ifdef USE_SERIAL_BRIDGE - GPIO_SBR_TX, // Serial Bridge Serial interface - GPIO_SBR_RX, // Serial Bridge Serial interface + AGPIO(GPIO_SBR_TX), // Serial Bridge Serial interface + AGPIO(GPIO_SBR_RX), // Serial Bridge Serial interface #endif #ifdef USE_ZIGBEE - GPIO_ZIGBEE_TX, // Zigbee Serial interface - GPIO_ZIGBEE_RX, // Zigbee Serial interface + AGPIO(GPIO_ZIGBEE_TX), // Zigbee Serial interface + AGPIO(GPIO_ZIGBEE_RX), // Zigbee Serial interface #endif #ifdef USE_MHZ19 - GPIO_MHZ_TXD, // MH-Z19 Serial interface - GPIO_MHZ_RXD, // MH-Z19 Serial interface + AGPIO(GPIO_MHZ_TXD), // MH-Z19 Serial interface + AGPIO(GPIO_MHZ_RXD), // MH-Z19 Serial interface #endif #ifdef USE_SENSEAIR - GPIO_SAIR_TX, // SenseAir Serial interface - GPIO_SAIR_RX, // SenseAir Serial interface + AGPIO(GPIO_SAIR_TX), // SenseAir Serial interface + AGPIO(GPIO_SAIR_RX), // SenseAir Serial interface #endif #ifdef USE_NOVA_SDS - GPIO_SDS0X1_TX, // Nova Fitness SDS011 Serial interface - GPIO_SDS0X1_RX, // Nova Fitness SDS011 Serial interface + AGPIO(GPIO_SDS0X1_TX), // Nova Fitness SDS011 Serial interface + AGPIO(GPIO_SDS0X1_RX), // Nova Fitness SDS011 Serial interface #endif #ifdef USE_HPMA - GPIO_HPMA_TX, // Honeywell HPMA115S0 Serial interface - GPIO_HPMA_RX, // Honeywell HPMA115S0 Serial interface + AGPIO(GPIO_HPMA_TX), // Honeywell HPMA115S0 Serial interface + AGPIO(GPIO_HPMA_RX), // Honeywell HPMA115S0 Serial interface #endif #ifdef USE_PMS5003 - GPIO_PMS5003_TX, // Plantower PMS5003 Serial interface - GPIO_PMS5003_RX, // Plantower PMS5003 Serial interface + AGPIO(GPIO_PMS5003_TX), // Plantower PMS5003 Serial interface + AGPIO(GPIO_PMS5003_RX), // Plantower PMS5003 Serial interface #endif #if defined(USE_TX20_WIND_SENSOR) || defined(USE_TX23_WIND_SENSOR) - GPIO_TX2X_TXD_BLACK, // TX20/TX23 Transmission Pin + AGPIO(GPIO_TX2X_TXD_BLACK), // TX20/TX23 Transmission Pin #endif #ifdef USE_MP3_PLAYER - GPIO_MP3_DFR562, // RB-DFR-562, DFPlayer Mini MP3 Player Serial interface + AGPIO(GPIO_MP3_DFR562), // RB-DFR-562, DFPlayer Mini MP3 Player Serial interface #endif #ifdef USE_AZ7798 - GPIO_AZ_TXD, // AZ-Instrument 7798 CO2 datalogger Serial interface - GPIO_AZ_RXD, // AZ-Instrument 7798 CO2 datalogger Serial interface + AGPIO(GPIO_AZ_TXD), // AZ-Instrument 7798 CO2 datalogger Serial interface + AGPIO(GPIO_AZ_RXD), // AZ-Instrument 7798 CO2 datalogger Serial interface #endif #ifdef USE_PN532_HSU - GPIO_PN532_TXD, // PN532 HSU Tx - GPIO_PN532_RXD, // PN532 HSU Rx + AGPIO(GPIO_PN532_TXD), // PN532 HSU Tx + AGPIO(GPIO_PN532_RXD), // PN532 HSU Rx #endif #ifdef USE_TASMOTA_SLAVE - GPIO_TASMOTASLAVE_TXD, // Tasmota Slave TX - GPIO_TASMOTASLAVE_RXD, // Tasmota Slave RX - GPIO_TASMOTASLAVE_RST, // Tasmota Slave Reset - GPIO_TASMOTASLAVE_RST_INV, // Tasmota Slave Reset Inverted + AGPIO(GPIO_TASMOTASLAVE_TXD), // Tasmota Slave TX + AGPIO(GPIO_TASMOTASLAVE_RXD), // Tasmota Slave RX + AGPIO(GPIO_TASMOTASLAVE_RST), // Tasmota Slave Reset + AGPIO(GPIO_TASMOTASLAVE_RST_INV), // Tasmota Slave Reset Inverted #endif #ifdef USE_RDM6300 - GPIO_RDM6300_RX, + AGPIO(GPIO_RDM6300_RX), #endif #ifdef USE_IBEACON - GPIO_IBEACON_RX, - GPIO_IBEACON_TX, + AGPIO(GPIO_IBEACON_RX), + AGPIO(GPIO_IBEACON_TX), #endif #ifdef USE_GPS - GPIO_GPS_RX, // GPS serial interface - GPIO_GPS_TX, // GPS serial interface + AGPIO(GPIO_GPS_RX), // GPS serial interface + AGPIO(GPIO_GPS_TX), // GPS serial interface #endif #ifdef USE_HM10 - GPIO_HM10_RX, // GPS serial interface - GPIO_HM10_TX, // GPS serial interface + AGPIO(GPIO_HM10_RX), // GPS serial interface + AGPIO(GPIO_HM10_TX), // GPS serial interface #endif #ifdef USE_MGC3130 - GPIO_MGC3130_XFER, - GPIO_MGC3130_RESET, + AGPIO(GPIO_MGC3130_XFER), + AGPIO(GPIO_MGC3130_RESET), #endif #ifdef USE_MAX31855 - GPIO_MAX31855CS, // MAX31855 Serial interface - GPIO_MAX31855CLK, // MAX31855 Serial interface - GPIO_MAX31855DO, // MAX31855 Serial interface + AGPIO(GPIO_MAX31855CS), // MAX31855 Serial interface + AGPIO(GPIO_MAX31855CLK), // MAX31855 Serial interface + AGPIO(GPIO_MAX31855DO), // MAX31855 Serial interface #endif #ifdef ROTARY_V1 - GPIO_ROT1A, // Rotary switch1 A Pin - GPIO_ROT1B, // Rotary switch1 B Pin - GPIO_ROT2A, // Rotary switch2 A Pin - GPIO_ROT2B, // Rotary switch2 B Pin + AGPIO(GPIO_ROT1A), // Rotary switch1 A Pin + AGPIO(GPIO_ROT1B), // Rotary switch1 B Pin + AGPIO(GPIO_ROT2A), // Rotary switch2 A Pin + AGPIO(GPIO_ROT2B), // Rotary switch2 B Pin #endif #ifdef USE_HRE - GPIO_HRE_CLOCK, - GPIO_HRE_DATA, + AGPIO(GPIO_HRE_CLOCK), + AGPIO(GPIO_HRE_DATA), #endif #ifdef USE_A4988_STEPPER - GPIO_A4988_DIR, // A4988 direction pin - GPIO_A4988_STP, // A4988 step pin + AGPIO(GPIO_A4988_DIR), // A4988 direction pin + AGPIO(GPIO_A4988_STP), // A4988 step pin // folowing are not mandatory - GPIO_A4988_ENA, // A4988 enabled pin - GPIO_A4988_MS1, // A4988 microstep pin1 - GPIO_A4988_MS2, // A4988 microstep pin2 - GPIO_A4988_MS3, // A4988 microstep pin3 + AGPIO(GPIO_A4988_ENA), // A4988 enabled pin + AGPIO(GPIO_A4988_MS1), // A4988 microstep pin1 + AGPIO(GPIO_A4988_MS2), // A4988 microstep pin2 + AGPIO(GPIO_A4988_MS3), // A4988 microstep pin3 #endif #ifdef USE_DEEPSLEEP - GPIO_DEEPSLEEP, + AGPIO(GPIO_DEEPSLEEP), #endif #ifdef USE_KEELOQ - GPIO_CC1101_GDO0, // CC1101 pin for RX - GPIO_CC1101_GDO2, // CC1101 pin for RX + AGPIO(GPIO_CC1101_GDO0), // CC1101 pin for RX + AGPIO(GPIO_CC1101_GDO2), // CC1101 pin for RX #endif #ifdef USE_HRXL - GPIO_HRXL_RX, + AGPIO(GPIO_HRXL_RX), #endif #ifdef USE_AS3935 - GPIO_AS3935, + AGPIO(GPIO_AS3935), #endif /* ADC0_INPUT, // Analog input diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index a4f43b6c6..beeaa31a3 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -231,12 +231,46 @@ const char HTTP_MODULE_TEMPLATE_REPLACE[] PROGMEM = "}2%d'>%s (%d}3"; // }2 and }3 are used in below os.replace const char HTTP_SCRIPT_MODULE_TEMPLATE[] PROGMEM = +#ifdef ESP8266 "var os;" "function sk(s,g){" // s = value, g = id and name "var o=os.replace(/}2/g,\"