mirror of https://github.com/arendst/Tasmota.git
commit
3f7b4999c7
|
@ -1,28 +1,27 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
<GUIDE>
|
||||
|
||||
<This BUG issue template is meant to REPORT Tasmota software BUGS ONLY>
|
||||
|
||||
<Please DO NOT OPEN AN ISSUE:>
|
||||
<If your Tasmota version is not the latest, please update before posting. Your issue might be already solved. Latest precompiled bins of Tasmota can be downloaded from http://thehackbox.org/tasmota/>
|
||||
<If your issue is a flashing issue, please address that to the Tasmota Support Chat>
|
||||
<If your issue is compilation problem, please address that to the Tasmota Support Chat>
|
||||
<If your issue has been addresed before (duplicated issue), please ask in the original issue>
|
||||
<If your issue is wifi problem or mqtt problem, please try first the steps provided in troubleshooting of the wiki>
|
||||
|
||||
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.
|
||||
> **GUIDE**
|
||||
>
|
||||
> This BUG issue template is meant to REPORT Tasmota software BUGS ONLY>
|
||||
>
|
||||
> Please DO NOT OPEN AN ISSUE:
|
||||
> - If your Tasmota version is not the latest from the development branch, please update your device before submitting your issue. Your problem might already be solved. The latest precompiled binaries of Tasmota can be downloaded from http://thehackbox.org/tasmota/>
|
||||
> - If your issue is a flashing issue, please address it to the Tasmota Support Chat>
|
||||
> - If your issue is compilation problem, please address it to the Tasmota Support Chat>
|
||||
> - If your issue has been addresed before (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 and troubleshooting wiki articles>
|
||||
>
|
||||
> 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.
|
||||
|
||||
### BUG DESCRIPTION
|
||||
_A clear and concise description of what the bug is._
|
||||
|
||||
|
||||
### REQUESTED INFORMATION
|
||||
_Make sure these boxes are checked before submitting your issue. Thank you_
|
||||
_Make sure your have performed every step and checked the applicable boxes before submitting your issue. Thank you!_
|
||||
|
||||
**FAILURE TO COMPLETE THE REQUESTED INFORMATION WILL RESULT IN YOUR ISSUE BEING CLOSED**
|
||||
|
||||
|
@ -31,22 +30,35 @@ _Make sure these boxes are checked before submitting your issue. Thank you_
|
|||
- [ ] Searched the problem in the wiki (https://github.com/arendst/Sonoff-Tasmota/wiki/Troubleshooting)
|
||||
- [ ] 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 (i.e. Sonoff Basic) : _____
|
||||
- [ ] Tasmota binary firmware version number used : ____ / (pre-compiled or self-compiled ?)
|
||||
- [ ] Development IDE - Compiler / Upload tools used : ____ / ____
|
||||
- [ ] Provide the output of command ``status 0`` :
|
||||
```
|
||||
STATUS 0 OUTPUT HERE:
|
||||
|
||||
|
||||
```
|
||||
- [ ] Provide the output of console when you experience your issue if apply :
|
||||
_(Please use_ ``weblog 4`` _for more debug information)_
|
||||
```
|
||||
CONSOLE OUTPUT HERE:
|
||||
|
||||
|
||||
```
|
||||
- [ ] Device used (e.g., Sonoff Basic): _____
|
||||
- [ ] Tasmota binary firmware version number used: _____
|
||||
- [ ] Pre-compiled
|
||||
- [ ] Self-compiled
|
||||
- [ ] IDE / Compiler
|
||||
- [ ] Flashing tools used: _____
|
||||
- [ ] Provide the output of command ``Backlog Template; Module; GPIO``:
|
||||
```
|
||||
Configuration output here:
|
||||
|
||||
```
|
||||
- [ ] If using rules, provide the output of command ``Backlog Rule1; Rule2; Rule3``:
|
||||
```
|
||||
Rules output here:
|
||||
|
||||
```
|
||||
- [ ] Provide the output of command ``Status 0``:
|
||||
```
|
||||
STATUS 0 output here:
|
||||
|
||||
|
||||
```
|
||||
- [ ] Provide the output of console when you experience your issue if applicable:
|
||||
_(Please use_ ``weblog 4`` _for more debug information)_
|
||||
```
|
||||
Console output here:
|
||||
|
||||
|
||||
```
|
||||
|
||||
### TO REPRODUCE
|
||||
_Steps to reproduce the behavior:_
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
---
|
||||
name: Troubleshooting
|
||||
about: Users Troubleshooting Help
|
||||
|
||||
---
|
||||
|
||||
<GUIDE>
|
||||
|
||||
<This troubleshooting issue template is meant to help Tasmota users with difficult problems. It is aimed to be opened if using the wiki and the support chat could not solve the issue. The Github Issue tracker is NOT a general discussion forum>
|
||||
|
||||
<Please DO NOT OPEN AN ISSUE:>
|
||||
<If you have general questions or you need help on Tasmota usage, go to the Tasmota support chat>
|
||||
<If your Tasmota version is not the latest, please update before posting. Your issue might be already solved. Latest precompiled bins of Tasmota can be downloaded from http://thehackbox.org/tasmota/>
|
||||
<If your issue is a new device, please use the Tasmota Template Feature. See wiki for that>
|
||||
<If your issue is a flashing issue, please address that to the Tasmota Support Chat>
|
||||
<If your issue is compilation problem, please address that to the Tasmota Support Chat>
|
||||
<If your issue has been addresed before (duplicated issue), please ask in the original issue>
|
||||
<If your issue is wifi problem or mqtt problem, please try first the steps provided in troubleshooting of the wiki>
|
||||
|
||||
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.
|
||||
> **GUIDE**
|
||||
>
|
||||
> This troubleshooting issue template is meant to help Tasmota users with difficult problems. It is aimed to be opened if using the wiki and the support chat could not solve the issue. The Github Issue tracker is NOT a general discussion forum!
|
||||
>
|
||||
> Please DO NOT OPEN AN ISSUE:
|
||||
> - If you have general questions or you need help on Tasmota usage, go to the Tasmota support chat
|
||||
> - If your Tasmota version is not the latest from the development branch, please update your device before submitting your issue. Your problem might already be solved. The latest precompiled binaries of Tasmota can be downloaded from http://thehackbox.org/tasmota/
|
||||
> - If your issue is about a new device, please use the Tasmota [Template](../wiki/Templates) feature.
|
||||
> - If your issue is a flashing issue, please address it to the Tasmota Support Chat
|
||||
> - If your issue is compilation problem, please address it to the Tasmota Support Chat
|
||||
> - If your issue has been addresed before (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 and troubleshooting wiki articles
|
||||
>
|
||||
> 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.
|
||||
|
||||
### ISSUE DESCRIPTION - TROUBLESHOOTING
|
||||
_A clear description of what the issue is and be as extensive as possible_
|
||||
|
@ -33,20 +32,34 @@ _Make sure these boxes are checked before submitting your issue. Thank you_
|
|||
- [ ] Searched the problem in the wiki (https://github.com/arendst/Sonoff-Tasmota/wiki/Troubleshooting)
|
||||
- [ ] 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 (i.e. Sonoff Basic) : _____
|
||||
- [ ] Tasmota binary firmware version number used : ____ / (pre-compiled or self-compiled ?)
|
||||
- [ ] Development IDE - Compiler / Upload tools used : ____ / ____
|
||||
- [ ] Provide the output of command ``status 0`` :
|
||||
```
|
||||
STATUS 0 OUTPUT HERE:
|
||||
- [ ] Device used (e.g., Sonoff Basic): _____
|
||||
- [ ] Tasmota binary firmware version number used: _____
|
||||
- [ ] Pre-compiled
|
||||
- [ ] Self-compiled
|
||||
- [ ] IDE / Compiler
|
||||
- [ ] Flashing tools used: _____
|
||||
- [ ] Provide the output of command ``Backlog Template; Module; GPIO``:
|
||||
```
|
||||
Configuration output here:
|
||||
|
||||
```
|
||||
- [ ] If using rules, provide the output of command ``Backlog Rule1; Rule2; Rule3``:
|
||||
```
|
||||
Rules output here:
|
||||
|
||||
```
|
||||
- [ ] Provide the output of command ``Status 0``:
|
||||
```
|
||||
STATUS 0 output here:
|
||||
|
||||
|
||||
```
|
||||
- [ ] Provide the output of console when you experience your issue if applicable:
|
||||
_(Please use_ ``weblog 4`` _for more debug information)_
|
||||
```
|
||||
Console output here:
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
- [ ] Provide the output of console when you experience your issue if apply :
|
||||
_(Please use_ ``weblog 4`` _for more debug information)_
|
||||
```
|
||||
CONSOLE OUTPUT HERE:
|
||||
|
||||
|
||||
```
|
||||
**(Please, remember to close the issue when the problem has been addressed)**
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
|
||||
---
|
||||
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.
|
||||
|
||||
**Have you looked for this feature in other issues and in the wiki?**
|
||||
> 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.
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
**Have you looked for this feature in other issues and in the wiki?**
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
_A clear and concise description of what the problem is._
|
||||
|
||||
**Describe the solution you'd like**
|
||||
**Describe the solution you'd like**
|
||||
_A clear and concise description of what you want to happen._
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
**Describe alternatives you've considered**
|
||||
_A clear and concise description of any alternative solutions or features you've considered._
|
||||
|
||||
**Additional context**
|
||||
**Additional context**
|
||||
_Add any other context or screenshots about the feature request here._
|
||||
|
||||
**(Please, remember to close the issue when the problem has been addressed)**
|
||||
**(Please, remember to close the issue when the problem has been addressed)**
|
||||
|
|
|
@ -6,6 +6,6 @@
|
|||
- [ ] The pull request is done against the latest dev branch
|
||||
- [ ] Only relevant files were touched
|
||||
- [ ] Only one feature/fix was added per PR.
|
||||
- [ ] The code change is tested and works on core 2.3.0, 2.4.2 and 2.5.2
|
||||
- [ ] The code change is tested and works on core 2.3.0, 2.4.2, 2.5.2, and pre-2.6
|
||||
- [ ] The code change pass travis tests. **Your PR cannot be merged unless tests pass**
|
||||
- [ ] I accept the [CLA](https://github.com/arendst/Sonoff-Tasmota/blob/development/CONTRIBUTING.md#contributor-license-agreement-cla).
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
sonoff/user_config_override.h
|
||||
build
|
||||
firmware.map
|
||||
firmware.asm
|
||||
|
||||
## Visual Studio Code specific ######
|
||||
.vscode
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
language: python
|
||||
python:
|
||||
- '2.7'
|
||||
- '3.7'
|
||||
sudo: false
|
||||
install:
|
||||
- pip install -U platformio
|
||||
|
|
|
@ -69,7 +69,7 @@ uint16_t FT6236GetButtonMask(void) {
|
|||
|
||||
void FT6236begin(uint8_t i2c_addr) {
|
||||
FT6236_i2c_addr=i2c_addr;
|
||||
Wire.begin(FT6236_i2c_addr);
|
||||
Wire.begin();
|
||||
FT6236writeTouchRegister(0,FT6236_MODE_NORMAL);
|
||||
lenLibVersion = FT6236readTouchAddr(0x0a1, FT6236buf, 2 );
|
||||
firmwareId = FT6236readTouchRegister( 0xa6 );
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "TasmotaModbus",
|
||||
"version": "1.1.1",
|
||||
"version": "1.2.0",
|
||||
"keywords": [
|
||||
"serial", "io", "TasmotaModbus"
|
||||
],
|
|
@ -1,5 +1,5 @@
|
|||
name=TasmotaModbus
|
||||
version=1.1.1
|
||||
version=1.2.0
|
||||
author=Theo Arends
|
||||
maintainer=Theo Arends <theo@arends.com>
|
||||
sentence=Basic modbus wrapper for TasmotaSerial for ESP8266.
|
|
@ -61,10 +61,10 @@ void TasmotaModbus::Send(uint8_t device_address, uint8_t function_code, uint16_t
|
|||
|
||||
frame[0] = mb_address; // 0xFE default device address or dedicated like 0x01
|
||||
frame[1] = function_code;
|
||||
frame[2] = (uint8_t)(start_address >> 8);
|
||||
frame[3] = (uint8_t)(start_address);
|
||||
frame[4] = (uint8_t)(register_count >> 8);
|
||||
frame[5] = (uint8_t)(register_count);
|
||||
frame[2] = (uint8_t)(start_address >> 8); // MSB
|
||||
frame[3] = (uint8_t)(start_address); // LSB
|
||||
frame[4] = (uint8_t)(register_count >> 8); // MSB
|
||||
frame[5] = (uint8_t)(register_count); // LSB
|
||||
uint16_t crc = CalculateCRC(frame, 6);
|
||||
frame[6] = (uint8_t)(crc);
|
||||
frame[7] = (uint8_t)(crc >> 8);
|
||||
|
@ -80,35 +80,48 @@ bool TasmotaModbus::ReceiveReady()
|
|||
|
||||
uint8_t TasmotaModbus::ReceiveBuffer(uint8_t *buffer, uint8_t register_count)
|
||||
{
|
||||
uint8_t len = 0;
|
||||
mb_len = 0;
|
||||
uint32_t last = millis();
|
||||
while ((available() > 0) && (len < (register_count *2) + 5) && (millis() - last < 10)) {
|
||||
while ((available() > 0) && (mb_len < (register_count *2) + 5) && (millis() - last < 10)) {
|
||||
uint8_t data = (uint8_t)read();
|
||||
if (!len) { // Skip leading data as provided by hardware serial
|
||||
if (!mb_len) { // Skip leading data as provided by hardware serial
|
||||
if (mb_address == data) {
|
||||
buffer[len++] = data;
|
||||
buffer[mb_len++] = data;
|
||||
}
|
||||
} else {
|
||||
buffer[len++] = data;
|
||||
if (3 == len) {
|
||||
buffer[mb_len++] = data;
|
||||
if (3 == mb_len) {
|
||||
if (buffer[1] & 0x80) { // 01 84 02 f2 f1
|
||||
return buffer[2]; // 1 = Illegal Function, 2 = Illegal Address, 3 = Illegal Data, 4 = Slave Error
|
||||
return buffer[2]; // 1 = Illegal Function,
|
||||
// 2 = Illegal Data Address,
|
||||
// 3 = Illegal Data Value,
|
||||
// 4 = Slave Error
|
||||
// 5 = Acknowledge but not finished (no error)
|
||||
// 6 = Slave Busy
|
||||
// 8 = Memory Parity error
|
||||
// 10 = Gateway Path Unavailable
|
||||
// 11 = Gateway Target device failed to respond
|
||||
}
|
||||
}
|
||||
}
|
||||
last = millis();
|
||||
}
|
||||
|
||||
if (len < 7) { return 7; } // 7 = Not enough data
|
||||
if (len != buffer[2] + 5) {
|
||||
buffer[2] = len - 5; // As it's wrong anyway let's store actual number received in here (5 will be added by client)
|
||||
return 8; // 8 = Unexpected result
|
||||
if (mb_len < 7) { return 7; } // 7 = Not enough data
|
||||
|
||||
/*
|
||||
if (mb_len != buffer[2] + 5) {
|
||||
buffer[2] = mb_len - 5; // As it's wrong anyway let's store actual number received in here (5 will be added by client)
|
||||
return 3; // 3 = Unexpected result
|
||||
}
|
||||
*/
|
||||
|
||||
uint16_t crc = (buffer[mb_len -1] << 8) | buffer[mb_len -2];
|
||||
if (CalculateCRC(buffer, mb_len -2) != crc) {
|
||||
return 9; // 9 = crc error
|
||||
}
|
||||
|
||||
uint16_t crc = (buffer[len -1] << 8) | buffer[len -2];
|
||||
if (CalculateCRC(buffer, len -2) != crc) { return 9; } // 9 = crc error
|
||||
|
||||
return 0; // 0 = No error
|
||||
return 0; // 0 = No error
|
||||
}
|
||||
|
||||
uint8_t TasmotaModbus::Receive16BitRegister(uint16_t *value)
|
|
@ -37,21 +37,28 @@ class TasmotaModbus : public TasmotaSerial {
|
|||
bool ReceiveReady();
|
||||
|
||||
/* Return codes:
|
||||
* 0 - No error
|
||||
* 1 - Illegal function
|
||||
* 2 - Illegal address
|
||||
* 3 - Illegal data
|
||||
* 4 - Slave error
|
||||
* 7 - Not enough minimal data received
|
||||
* 8 - Not enough data receieved
|
||||
* 9 - Crc error
|
||||
* 0 = No error
|
||||
* 1 = Illegal Function,
|
||||
* 2 = Illegal Data Address,
|
||||
* 3 = Illegal Data Value,
|
||||
* 4 = Slave Error
|
||||
* 5 = Acknowledge but not finished (no error)
|
||||
* 6 = Slave Busy
|
||||
* 7 = Not enough minimal data received
|
||||
* 8 = Memory Parity error
|
||||
* 9 = Crc error
|
||||
* 10 = Gateway Path Unavailable
|
||||
* 11 = Gateway Target device failed to respond
|
||||
*/
|
||||
uint8_t ReceiveBuffer(uint8_t *buffer, uint8_t register_count);
|
||||
uint8_t Receive16BitRegister(uint16_t *value);
|
||||
uint8_t Receive32BitRegister(float *value);
|
||||
|
||||
uint8_t ReceiveCount(void) { return mb_len; }
|
||||
|
||||
private:
|
||||
uint8_t mb_address;
|
||||
uint8_t mb_len;
|
||||
};
|
||||
|
||||
#endif // TasmotaModbus_h
|
|
@ -0,0 +1,9 @@
|
|||
# Little convenience script to get an object dump
|
||||
|
||||
Import('env')
|
||||
|
||||
def obj_dump_after_elf(source, target, env):
|
||||
print("Create firmware.asm")
|
||||
env.Execute("xtensa-lx106-elf-objdump "+ "-D " + str(target[0]) + " > "+ "${PROGNAME}.asm")
|
||||
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", [obj_dump_after_elf])
|
|
@ -220,6 +220,7 @@ upload_resetmethod = nodemcu
|
|||
; *** Upload Serial reset method for Wemos and NodeMCU
|
||||
upload_port = COM5
|
||||
extra_scripts = pio/strip-floats.py
|
||||
pio/obj-dump.py
|
||||
|
||||
; *** Upload file to OTA server using SCP
|
||||
;upload_port = user@host:/path
|
||||
|
|
|
@ -1,9 +1,25 @@
|
|||
/*********************************************************************************************\
|
||||
* 6.6.0.12 20190910
|
||||
* Redesign command Tariff to now default to 0 (=disabled) and allowing to set both Standard Time (ST) and Daylight Savings Time (DST) start hour
|
||||
* Commands Tariff1 22,23 = Tariff1 (Off-Peak) ST,DST Tariff2 (Standard) 6,7 = Tariff2 ST,DST Tariff9 0/1 = Weekend toggle (1 = Off-Peak during weekend)
|
||||
* Change rename "Data" to "Hash" and limit to 32 bits when receiving UNKNOWN IR protocol (see DECODE_HASH from IRremoteESP8266)
|
||||
* Add command Gpios 255/All to show all available GPIO components (#6407)
|
||||
* Change JSON output format for commands Adc, Adcs, Modules, Gpio and Gpios from list to dictionary (#6407)
|
||||
* Add Zigbee support phase 3 - support for Xiaomi lumi.weather air quality sensor, Osram mini-switch
|
||||
* Change energy sensors for three phase/channel support
|
||||
* Add support for Shelly 2.5 dual energy (#6160)
|
||||
* Add initial support for up to three PZEM-014/-016 on serial modbus connection with addresses 1 (default), 2 and 3 (#2315)
|
||||
* Add initial support for up to three PZEM-004T on serial connection with addresses x.x.x.1 (default), 2 and 3 (#2315)
|
||||
* Add initial support for up to three PZEM-003/-017 on serial modbus connection with addresses 1 (default), 2 and 3 (#2315)
|
||||
* Add driver USE_SDM630_2 as future replacement for USE_SDM630 - Pls test and report
|
||||
* Add command ModuleAddress 1/2/3 to set Pzem module address when a single module is connected (#2315)
|
||||
*
|
||||
* 6.6.0.11 20190907
|
||||
* Change Settings crc calculation allowing short term backward compatibility
|
||||
* Add support for up to 4 INA226 Voltage and Current sensors by Steve Rogers (#6342)
|
||||
* Change Improve reliability of TasmotaSerial at 115200 bauds and reduce IRAM usage for Stage/pre-2.6
|
||||
* Add support for A4988 stepper-motor-driver-circuit by Tim Leuschner (#6370)
|
||||
* Add support for Hiking DDS238-2 Modbus energy meter by Matteo Campanella (#6384)
|
||||
*
|
||||
* 6.6.0.10 20190905
|
||||
* Redesign Tuya support by Shantur Rathore (#6353)
|
||||
|
|
|
@ -385,6 +385,7 @@
|
|||
#define D_JSON_IR_BITS "Bits"
|
||||
#define D_JSON_IR_DATA "Data"
|
||||
#define D_JSON_IR_DATALSB "DataLSB"
|
||||
#define D_JSON_IR_HASH "Hash"
|
||||
#define D_JSON_IR_RAWDATA "RawData"
|
||||
#define D_JSON_IR_REPEAT "Repeat"
|
||||
#define D_CMND_IRHVAC "IRHVAC"
|
||||
|
@ -451,7 +452,9 @@
|
|||
#define D_CMND_TUYA_MCU "TuyaMCU"
|
||||
|
||||
// Commands xdrv_23_zigbee.ino
|
||||
#define D_CMND_ZIGBEE_PERMITJOIN "ZigbeePermitJoin"
|
||||
#define D_CMND_ZIGBEEZNPSEND "ZigbeeZNPSend"
|
||||
#define D_JSON_ZIGBEE_STATUS "ZigbeeStatus"
|
||||
#define D_JSON_ZIGBEEZNPRECEIVED "ZigbeeZNPReceived"
|
||||
#define D_JSON_ZIGBEEZNPSENT "ZigbeeZNPSent"
|
||||
#define D_JSON_ZIGBEEZCLRECEIVED "ZigbeeZCLReceived"
|
||||
|
@ -536,8 +539,8 @@ const char S_JSON_COMMAND_SVALUE_SPACE_UNIT[] PROGMEM = "{\"%s\":\"%s %s\"
|
|||
const char S_JSON_COMMAND_NVALUE_UNIT[] PROGMEM = "{\"%s\":\"%d%s\"}";
|
||||
const char S_JSON_COMMAND_NVALUE_UNIT_NVALUE_UNIT[] PROGMEM = "{\"%s\":\"%d%s (%d%s)\"}";
|
||||
|
||||
const char S_JSON_COMMAND_NVALUE_SVALUE[] PROGMEM = "{\"%s\":\"%d (%s)\"}";
|
||||
const char S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE[] PROGMEM = "{\"%s\":\"%d (" D_JSON_ACTIVE " %d)\"}";
|
||||
const char S_JSON_COMMAND_NVALUE_SVALUE[] PROGMEM = "{\"%s\":{\"%d\":\"%s\"}}";
|
||||
const char S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE[] PROGMEM = "{\"%s\":{\"%d\":{\"" D_JSON_ACTIVE "\":\"%d\"}}}";
|
||||
|
||||
const char S_JSON_COMMAND_NVALUE[] PROGMEM = "{\"%s\":%d}";
|
||||
const char S_JSON_COMMAND_LVALUE[] PROGMEM = "{\"%s\":%lu}";
|
||||
|
@ -550,7 +553,7 @@ const char S_JSON_COMMAND_INDEX_LVALUE[] PROGMEM = "{\"%s%d\":%lu}";
|
|||
const char S_JSON_COMMAND_INDEX_SVALUE[] PROGMEM = "{\"%s%d\":\"%s\"}";
|
||||
const char S_JSON_COMMAND_INDEX_ASTERISK[] PROGMEM = "{\"%s%d\":\"" D_ASTERISK_PWD "\"}";
|
||||
const char S_JSON_COMMAND_INDEX_SVALUE_SVALUE[] PROGMEM = "{\"%s%d\":\"%s%s\"}";
|
||||
const char S_JSON_COMMAND_INDEX_NVALUE_ACTIVE_NVALUE[] PROGMEM = "{\"%s%d\":\"%d (" D_JSON_ACTIVE " %d)\"}";
|
||||
const char S_JSON_COMMAND_INDEX_NVALUE_ACTIVE_NVALUE[] PROGMEM = "{\"%s%d\":{\"%d\":{\"" D_JSON_ACTIVE "\":\"%d\"}}}";
|
||||
|
||||
const char S_JSON_SENSOR_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":%d}";
|
||||
const char S_JSON_SENSOR_INDEX_SVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":\"%s\"}";
|
||||
|
@ -603,12 +606,6 @@ const char kCodeImage[] PROGMEM = "sonoff|minimal|classic|sensors|knx|basic|disp
|
|||
// support.ino
|
||||
static const char kMonthNames[] = D_MONTH3LIST;
|
||||
|
||||
const char kOptionOff[] PROGMEM = "OFF|" D_OFF "|" D_FALSE "|" D_STOP "|" D_CELSIUS ;
|
||||
const char kOptionOn[] PROGMEM = "ON|" D_ON "|" D_TRUE "|" D_START "|" D_FAHRENHEIT "|" D_USER ;
|
||||
const char kOptionToggle[] PROGMEM = "TOGGLE|" D_TOGGLE "|" D_ADMIN ;
|
||||
const char kOptionBlink[] PROGMEM = "BLINK|" D_BLINK ;
|
||||
const char kOptionBlinkOff[] PROGMEM = "BLINKOFF|" D_BLINKOFF ;
|
||||
|
||||
// xdrv_02_webserver.ino
|
||||
#ifdef USE_WEBSERVER
|
||||
const char HTTP_SNS_TEMP[] PROGMEM = "{s}%s " D_TEMPERATURE "{m}%s°%c{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
|
||||
|
|
|
@ -596,13 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -596,12 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -596,12 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -596,12 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -596,12 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -596,12 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -596,12 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -596,12 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -596,12 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
#define D_BY "da" // Written by me
|
||||
#define D_BYTES "Bytes"
|
||||
#define D_CELSIUS "Celsius"
|
||||
#define D_CHANNEL "Channel"
|
||||
#define D_CHANNEL "Canale"
|
||||
#define D_CO2 "CO2"
|
||||
#define D_CODE "codice" // Button code
|
||||
#define D_COLDLIGHT "Fredda"
|
||||
|
@ -106,7 +106,7 @@
|
|||
#define D_IMMEDIATE "immediato" // Button immediate
|
||||
#define D_INDEX "Indice"
|
||||
#define D_INFO "Info"
|
||||
#define D_INFRARED "Infrared"
|
||||
#define D_INFRARED "Infrarosso"
|
||||
#define D_INITIALIZED "Inizializzato"
|
||||
#define D_IP_ADDRESS "Indirizzo IP"
|
||||
#define D_LIGHT "Luce"
|
||||
|
@ -199,7 +199,7 @@
|
|||
#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_FAILED_TO_START "Partenza fallita"
|
||||
#define D_PATCH_ISSUE_2186 "Patch issue 2186"
|
||||
#define D_CONNECTING_TO_AP "Connessione ad AP"
|
||||
#define D_IN_MODE "In modalità"
|
||||
|
@ -305,7 +305,7 @@
|
|||
#define D_CONFIGURE_TEMPLATE "Configurare Modello"
|
||||
#define D_TEMPLATE_PARAMETERS "Parametri Modello"
|
||||
#define D_TEMPLATE_NAME "Nome"
|
||||
#define D_BASE_TYPE "Baseto nel"
|
||||
#define D_BASE_TYPE "Basato nel"
|
||||
#define D_TEMPLATE_FLAGS "Opzioni"
|
||||
|
||||
#define D_SAVE_CONFIGURATION "Salva configurazione"
|
||||
|
@ -492,7 +492,7 @@
|
|||
#define D_TX20_NORTH "N"
|
||||
#define D_TX20_EAST "E"
|
||||
#define D_TX20_SOUTH "S"
|
||||
#define D_TX20_WEST "W"
|
||||
#define D_TX20_WEST "O"
|
||||
|
||||
//xsns_43_hre.ino
|
||||
#define D_LOG_HRE "HRE: "
|
||||
|
@ -596,12 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
@ -673,27 +675,27 @@
|
|||
#define D_UNIT_ANGLE "°"
|
||||
|
||||
//SOLAXX1
|
||||
#define D_PV1_VOLTAGE "PV1 Voltage"
|
||||
#define D_PV1_CURRENT "PV1 Current"
|
||||
#define D_PV1_POWER "PV1 Power"
|
||||
#define D_PV2_VOLTAGE "PV2 Voltage"
|
||||
#define D_PV2_CURRENT "PV2 Current"
|
||||
#define D_PV2_POWER "PV2 Power"
|
||||
#define D_SOLAR_POWER "Solar Power"
|
||||
#define D_INVERTER_POWER "Inverter Power"
|
||||
#define D_STATUS "Status"
|
||||
#define D_WAITING "Waiting"
|
||||
#define D_CHECKING "Checking"
|
||||
#define D_WORKING "Working"
|
||||
#define D_FAILURE "Failure"
|
||||
#define D_SOLAX_ERROR_0 "No Error Code"
|
||||
#define D_SOLAX_ERROR_1 "Grid Lost Fault"
|
||||
#define D_SOLAX_ERROR_2 "Grid Voltage Fault"
|
||||
#define D_SOLAX_ERROR_3 "Grid Frequency Fault"
|
||||
#define D_SOLAX_ERROR_4 "Pv Voltage Fault"
|
||||
#define D_SOLAX_ERROR_5 "Isolation Fault"
|
||||
#define D_SOLAX_ERROR_6 "Over Temperature Fault"
|
||||
#define D_SOLAX_ERROR_7 "Fan Fault"
|
||||
#define D_SOLAX_ERROR_8 "Other Device Fault"
|
||||
#define D_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_STATUS "Stato"
|
||||
#define D_WAITING "In attesa"
|
||||
#define D_CHECKING "Controllando"
|
||||
#define D_WORKING "Lavorando"
|
||||
#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"
|
||||
|
||||
#endif // _LANGUAGE_IT_IT_H_
|
||||
|
|
|
@ -596,12 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -596,12 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -596,12 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -596,12 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -596,12 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -596,12 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "А"
|
||||
|
|
|
@ -596,12 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -596,12 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -596,16 +596,19 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "Hr"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
|
@ -635,7 +638,6 @@
|
|||
#define D_UNIT_WATT "W"
|
||||
#define D_UNIT_WATTHOUR "Wh"
|
||||
#define D_UNIT_WATT_METER_QUADRAT "W/m²"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
|
||||
// Log message prefix
|
||||
#define D_LOG_APPLICATION "APP: " // Application
|
||||
|
|
|
@ -596,12 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "А"
|
||||
|
|
|
@ -596,16 +596,19 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "安"
|
||||
#define D_UNIT_CENTIMETER "厘米"
|
||||
#define D_UNIT_HERTZ "赫兹"
|
||||
#define D_UNIT_HOUR "时"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
|
@ -634,7 +637,6 @@
|
|||
#define D_UNIT_VOLT "伏"
|
||||
#define D_UNIT_WATT "瓦"
|
||||
#define D_UNIT_WATTHOUR "瓦时"
|
||||
#define D_UNIT_HERTZ "赫兹"
|
||||
#define D_UNIT_WATT_METER_QUADRAT "瓦/平米"
|
||||
|
||||
// Log message prefix
|
||||
|
|
|
@ -596,12 +596,14 @@
|
|||
#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_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"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "安"
|
||||
|
|
|
@ -431,13 +431,18 @@
|
|||
#define USE_PZEM_AC // Add support for PZEM014,016 Energy monitor (+1k1 code)
|
||||
#define USE_PZEM_DC // Add support for PZEM003,017 Energy monitor (+1k1 code)
|
||||
#define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code)
|
||||
//#define USE_SDM120_2 // Add support for Eastron SDM120-Modbus energy meter (+1k4 code)
|
||||
//#define USE_SDM120_2 // Add support for Eastron SDM120-Modbus energy monitor (+1k1 code)
|
||||
#define SDM120_SPEED 2400 // SDM120-Modbus RS485 serial speed (default: 2400 baud)
|
||||
//#define USE_SDM630_2 // Add support for Eastron SDM630-Modbus energy monitor (+0k6 code)
|
||||
#define SDM630_SPEED 9600 // SDM630-Modbus RS485 serial speed (default: 9600 baud)
|
||||
//#define USE_DDS2382 // Add support for Hiking DDS2382 Modbus energy monitor (+0k6 code)
|
||||
#define DDS2382_SPEED 9600 // Hiking DDS2382 Modbus RS485 serial speed (default: 9600 baud)
|
||||
|
||||
//#define USE_SDM120 // Add support for Eastron SDM120-Modbus energy meter (+2k4 code)
|
||||
#define SDM120_SPEED 2400 // SDM120-Modbus RS485 serial speed (default: 2400 baud)
|
||||
// #define SDM120_SPEED 2400 // SDM120-Modbus RS485 serial speed (default: 2400 baud)
|
||||
#define USE_SDM220 // Add extra parameters for SDM220 (+0k1 code)
|
||||
//#define USE_SDM630 // Add support for Eastron SDM630-Modbus energy meter (+2k code)
|
||||
#define SDM630_SPEED 9600 // SDM630-Modbus RS485 serial speed (default: 9600 baud)
|
||||
// #define SDM630_SPEED 9600 // SDM630-Modbus RS485 serial speed (default: 9600 baud)
|
||||
//#define USE_SOLAX_X1 // Add support for Solax X1 series Modbus log info (+4k1 code)
|
||||
#define SOLAXX1_SPEED 9600 // Solax X1 Modbus RS485 serial speed (default: 9600 baud)
|
||||
#define SOLAXX1_PV2 // Solax X1 using second PV
|
||||
|
|
|
@ -227,9 +227,7 @@ struct SYSCFG {
|
|||
uint8_t weblog_level; // 1AC
|
||||
uint8_t mqtt_fingerprint[2][20]; // 1AD
|
||||
uint8_t adc_param_type; // 1D5
|
||||
|
||||
uint8_t free_1D6[18]; // 1D6 Free since 5.12.0e
|
||||
|
||||
uint8_t register8[18]; // 1D6 - 18 x 8-bit registers indexed by enum SettingsRegister8
|
||||
uint8_t sps30_inuse_hours; // 1E8
|
||||
char mqtt_host[33]; // 1E9 - Keep together with below as being copied as one chunck with reset 6
|
||||
uint16_t mqtt_port; // 20A - Keep together
|
||||
|
|
|
@ -128,15 +128,6 @@
|
|||
#ifndef TUYA_DIMMER_MAX
|
||||
#define TUYA_DIMMER_MAX 100
|
||||
#endif
|
||||
#ifndef ENERGY_TARIFF1_HOUR
|
||||
#define ENERGY_TARIFF1_HOUR 23 // Start hour "nighttime" or "off-peak" tariff
|
||||
#endif
|
||||
#ifndef ENERGY_TARIFF2_HOUR
|
||||
#define ENERGY_TARIFF2_HOUR 7 // Start hour "daytime" or "standard" tariff
|
||||
#endif
|
||||
#ifndef ENERGY_TARIFF_WEEKEND
|
||||
#define ENERGY_TARIFF_WEEKEND 1 // 0 = No difference in weekend, 1 = off-peak during weekend
|
||||
#endif
|
||||
|
||||
enum WebColors {
|
||||
COL_TEXT, COL_BACKGROUND, COL_FORM,
|
||||
|
@ -1097,9 +1088,6 @@ void SettingsDelta(void)
|
|||
} else {
|
||||
Settings.param[P_TUYA_DIMMER_MAX] = 255;
|
||||
}
|
||||
Settings.param[P_ENERGY_TARIFF1] = ENERGY_TARIFF1_HOUR;
|
||||
Settings.param[P_ENERGY_TARIFF2] = ENERGY_TARIFF2_HOUR;
|
||||
Settings.flag3.energy_weekend = ENERGY_TARIFF_WEEKEND;
|
||||
}
|
||||
if (Settings.version < 0x06060009) {
|
||||
Settings.baudrate = Settings.ex_baudrate * 4;
|
||||
|
@ -1139,8 +1127,11 @@ void SettingsDelta(void)
|
|||
Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_ex_TUYA_CURRENT_ID];
|
||||
tuyaindex++;
|
||||
}
|
||||
|
||||
}
|
||||
if (Settings.version < 0x0606000C) {
|
||||
memset(&Settings.register8, 0x00, sizeof(Settings.register8));
|
||||
}
|
||||
|
||||
Settings.version = VERSION;
|
||||
SettingsSave(1);
|
||||
}
|
||||
|
|
|
@ -245,11 +245,18 @@ enum ButtonStates { PRESSED, NOT_PRESSED };
|
|||
|
||||
enum Shortcuts { SC_CLEAR, SC_DEFAULT, SC_USER };
|
||||
|
||||
enum SettingsParamIndex {P_HOLD_TIME, P_MAX_POWER_RETRY, P_ex_TUYA_DIMMER_ID, P_MDNS_DELAYED_START, P_BOOT_LOOP_OFFSET, P_RGB_REMAP, P_IR_UNKNOW_THRESHOLD, // SetOption32 .. SetOption38
|
||||
P_CSE7766_INVALID_POWER, P_HOLD_IGNORE, P_ex_TUYA_RELAYS, P_OVER_TEMP, // SetOption39 .. SetOption42
|
||||
P_TUYA_DIMMER_MAX, P_ex_TUYA_VOLTAGE_ID, P_ex_TUYA_CURRENT_ID, P_ex_TUYA_POWER_ID, // SetOption43 .. SetOption46
|
||||
P_ENERGY_TARIFF1, P_ENERGY_TARIFF2, // SetOption47 .. SetOption48
|
||||
P_MAX_PARAM8}; // Max is PARAM8_SIZE (18) - SetOption32 until SetOption49
|
||||
enum SettingsParamIndex { P_HOLD_TIME, P_MAX_POWER_RETRY, P_ex_TUYA_DIMMER_ID, P_MDNS_DELAYED_START, P_BOOT_LOOP_OFFSET, P_RGB_REMAP, P_IR_UNKNOW_THRESHOLD, // SetOption32 .. SetOption38
|
||||
P_CSE7766_INVALID_POWER, P_HOLD_IGNORE, P_ex_TUYA_RELAYS, P_OVER_TEMP, // SetOption39 .. SetOption42
|
||||
P_TUYA_DIMMER_MAX,
|
||||
P_ex_TUYA_VOLTAGE_ID, P_ex_TUYA_CURRENT_ID, P_ex_TUYA_POWER_ID, // SetOption43 .. SetOption46
|
||||
P_ex_ENERGY_TARIFF1, P_ex_ENERGY_TARIFF2, // SetOption47 .. SetOption48
|
||||
P_MAX_PARAM8 }; // Max is PARAM8_SIZE (18) - SetOption32 until SetOption49
|
||||
|
||||
enum SettingsRegister8 { R8_ENERGY_TARIFF1_ST, R8_ENERGY_TARIFF2_ST, R8_ENERGY_TARIFF1_DS, R8_ENERGY_TARIFF2_DS,
|
||||
R8_SPARE04, R8_SPARE05, R8_SPARE06, R8_SPARE07,
|
||||
R8_SPARE08, R8_SPARE09, R8_SPARE10, R8_SPARE11,
|
||||
R8_SPARE12, R8_SPARE13, R8_SPARE14, R8_SPARE15,
|
||||
R8_SPARE16, R8_SPARE17 }; // Max size is 18 (Settings.register8[])
|
||||
|
||||
enum DomoticzSensors {DZ_TEMP, DZ_TEMP_HUM, DZ_TEMP_HUM_BARO, DZ_POWER_ENERGY, DZ_ILLUMINANCE, DZ_COUNT, DZ_VOLTAGE, DZ_CURRENT, DZ_AIRQUALITY, DZ_P1_SMART_METER, DZ_MAX_SENSORS};
|
||||
|
||||
|
@ -272,6 +279,8 @@ enum XsnsFunctions {FUNC_SETTINGS_OVERRIDE, FUNC_PIN_STATE, FUNC_MODULE_INIT, FU
|
|||
FUNC_RULES_PROCESS, FUNC_SERIAL, FUNC_FREE_MEM, FUNC_BUTTON_PRESSED,
|
||||
FUNC_WEB_ADD_BUTTON, FUNC_WEB_ADD_MAIN_BUTTON, FUNC_WEB_ADD_HANDLER, FUNC_SET_CHANNELS};
|
||||
|
||||
enum AddressConfigSteps { ADDR_IDLE, ADDR_RECEIVE, ADDR_SEND };
|
||||
|
||||
enum CommandSource { SRC_IGNORE, SRC_MQTT, SRC_RESTART, SRC_BUTTON, SRC_SWITCH, SRC_BACKLOG, SRC_SERIAL, SRC_WEBGUI, SRC_WEBCOMMAND, SRC_WEBCONSOLE, SRC_PULSETIMER,
|
||||
SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_OVERTEMP, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_REMOTE, SRC_MAX };
|
||||
const char kCommandSource[] PROGMEM = "I|MQTT|Restart|Button|Switch|Backlog|Serial|WebGui|WebCommand|WebConsole|PulseTimer|Timer|Rule|MaxPower|MaxEnergy|Overtemp|Light|Knx|Display|Wemo|Hue|Retry|Remote";
|
||||
|
|
|
@ -581,8 +581,11 @@ void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source)
|
|||
MqttPublishPowerBlinkState(device);
|
||||
}
|
||||
|
||||
if (Settings.flag.interlock && !interlock_mutex) { // Clear all but masked relay in interlock group
|
||||
interlock_mutex = true;
|
||||
if (Settings.flag.interlock &&
|
||||
!interlock_mutex &&
|
||||
((POWER_ON == state) || ((POWER_TOGGLE == state) && !(power & mask)))
|
||||
) {
|
||||
interlock_mutex = true; // Clear all but masked relay in interlock group if new set requested
|
||||
for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) {
|
||||
if (Settings.interlock[i] & mask) { // Find interlock group
|
||||
for (uint32_t j = 0; j < devices_present; j++) {
|
||||
|
|
|
@ -148,8 +148,8 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c
|
|||
#define USE_PMS5003 // Add support for PMS5003 and PMS7003 particle concentration sensor (+1k3 code)
|
||||
#define USE_NOVA_SDS // Add support for SDS011 and SDS021 particle concentration sensor (+0k7 code)
|
||||
#define USE_SERIAL_BRIDGE // Add support for software Serial Bridge (+0k8 code)
|
||||
#define USE_SDM120 // Add support for Eastron SDM120-Modbus energy meter (+1k7 code)
|
||||
#define USE_SDM630 // Add support for Eastron SDM630-Modbus energy meter (+2k code)
|
||||
//#define USE_SDM120 // Add support for Eastron SDM120-Modbus energy meter (+1k7 code)
|
||||
//#define USE_SDM630 // Add support for Eastron SDM630-Modbus energy meter (+2k code)
|
||||
#define USE_MP3_PLAYER // Use of the DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop
|
||||
#define MP3_VOLUME 10 // Set the startup volume on init, the range can be 0..30(max)
|
||||
//#define USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger
|
||||
|
@ -161,6 +161,10 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c
|
|||
#define USE_PZEM_AC // Add support for PZEM014,016 Energy monitor (+1k1 code)
|
||||
#define USE_PZEM_DC // Add support for PZEM003,017 Energy monitor (+1k1 code)
|
||||
#define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code)
|
||||
#define USE_SDM120_2 // Add support for Eastron SDM120-Modbus energy monitor (+1k1 code)
|
||||
#define USE_SDM630_2 // Add support for Eastron SDM630-Modbus energy monitor (+0k6 code)
|
||||
#define USE_DDS2382 // Add support for Hiking DDS2382 Modbus energy monitor (+0k6 code)
|
||||
|
||||
#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor
|
||||
#define USE_MAX31855 // Add support for MAX31855 K-Type thermocouple sensor using softSPI
|
||||
#define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram)
|
||||
|
@ -244,6 +248,9 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c
|
|||
#undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor
|
||||
#undef USE_MCP39F501 // Disable support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code)
|
||||
#undef USE_SDM120_2 // Disable support for Eastron SDM120-Modbus energy meter
|
||||
#undef USE_SDM630_2 // Disable support for Eastron SDM630-Modbus energy monitor (+0k6 code)
|
||||
#undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code)
|
||||
|
||||
#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor
|
||||
#undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI
|
||||
#undef USE_IR_REMOTE // Disable IR remote commands using library IRremoteESP8266 and ArduinoJson
|
||||
|
@ -294,6 +301,9 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c
|
|||
#undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor
|
||||
#undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor
|
||||
#undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2
|
||||
#undef USE_SDM120_2 // Disable support for Eastron SDM120-Modbus energy meter
|
||||
#undef USE_SDM630_2 // Disable support for Eastron SDM630-Modbus energy monitor (+0k6 code)
|
||||
#undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code)
|
||||
#undef USE_EMULATION // Disable Belkin WeMo and Hue Bridge emulation for Alexa (-16k code, -2k mem)
|
||||
#undef USE_DOMOTICZ // Disable Domoticz
|
||||
#undef USE_HOME_ASSISTANT // Disable Home Assistant
|
||||
|
@ -375,6 +385,9 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c
|
|||
#undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor
|
||||
#undef USE_MCP39F501 // Disable support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code)
|
||||
#undef USE_SDM120_2 // Disable support for Eastron SDM120-Modbus energy meter
|
||||
#undef USE_SDM630_2 // Disable support for Eastron SDM630-Modbus energy monitor (+0k6 code)
|
||||
#undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code)
|
||||
|
||||
//#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor
|
||||
#undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI
|
||||
#undef USE_WS2812 // Disable WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by //
|
||||
|
@ -468,9 +481,12 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c
|
|||
#undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor
|
||||
#undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor
|
||||
//#undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2
|
||||
#undef USE_SDM120_2 // Disable support for Eastron SDM120-Modbus energy meter
|
||||
#undef USE_SDM630_2 // Disable support for Eastron SDM630-Modbus energy monitor (+0k6 code)
|
||||
#undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code)
|
||||
|
||||
#undef USE_DHT // Disable support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor
|
||||
#undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI
|
||||
#undef USE_SDM120_2 // Disable support for Eastron SDM120-Modbus energy meter
|
||||
#undef USE_IR_REMOTE // Disable IR driver
|
||||
#undef USE_WS2812 // Disable WS2812 Led string
|
||||
#undef USE_ARILUX_RF // Disable support for Arilux RF remote controller
|
||||
|
@ -551,6 +567,9 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c
|
|||
#undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor
|
||||
#undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2
|
||||
#undef USE_SDM120_2 // Disable support for Eastron SDM120-Modbus energy meter
|
||||
#undef USE_SDM630_2 // Disable support for Eastron SDM630-Modbus energy monitor (+0k6 code)
|
||||
#undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code)
|
||||
|
||||
#undef USE_DHT // Disable support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor
|
||||
#undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI
|
||||
#undef USE_IR_REMOTE // Disable IR driver
|
||||
|
|
|
@ -200,6 +200,8 @@ enum UserSelectablePins {
|
|||
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_SENSOR_END };
|
||||
|
||||
// Programmer selectable GPIO functionality
|
||||
|
@ -274,6 +276,7 @@ const char kSensorNames[] PROGMEM =
|
|||
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 "|"
|
||||
;
|
||||
|
||||
// User selectable ADC0 functionality
|
||||
|
@ -617,6 +620,14 @@ const uint8_t kGpioNiceList[] PROGMEM = {
|
|||
GPIO_SDM120_TX, // SDM120 Serial interface
|
||||
GPIO_SDM120_RX, // SDM120 Serial interface
|
||||
#endif
|
||||
#ifdef USE_SDM630_2
|
||||
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
|
||||
#endif // USE_ENERGY_SENSOR
|
||||
#ifndef USE_SDM120_2
|
||||
#ifdef USE_SDM120
|
||||
|
@ -624,10 +635,12 @@ const uint8_t kGpioNiceList[] PROGMEM = {
|
|||
GPIO_SDM120_RX, // SDM120 Serial interface
|
||||
#endif
|
||||
#endif // USE_SDM120_2
|
||||
#ifndef USE_SDM630_2
|
||||
#ifdef USE_SDM630
|
||||
GPIO_SDM630_TX, // SDM630 Serial interface
|
||||
GPIO_SDM630_RX, // SDM630 Serial interface
|
||||
#endif
|
||||
#endif // USE_SDM630_2
|
||||
#ifdef USE_SOLAX_X1
|
||||
GPIO_SOLAXX1_TX, // Solax Inverter tx pin
|
||||
GPIO_SOLAXX1_RX, // Solax Inverter rx pin
|
||||
|
@ -795,7 +808,7 @@ const uint8_t kModuleNiceList[] PROGMEM = {
|
|||
|
||||
// Default module settings
|
||||
const mytmplt kModules[MAXMODULE] PROGMEM = {
|
||||
{ "Sonoff Basic", // Sonoff Basic (ESP8266)
|
||||
{ "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
|
||||
|
@ -815,7 +828,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
0, // GPIO16
|
||||
0 // ADC0 Analog input
|
||||
},
|
||||
{ "Sonoff RF", // Sonoff RF (ESP8266)
|
||||
{ "Sonoff RF", // SONOFF_RF - Sonoff RF (ESP8266)
|
||||
GPIO_KEY1, // GPIO00 Button
|
||||
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
|
||||
GPIO_USER, // GPIO02 Optional sensor
|
||||
|
@ -833,7 +846,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_USER, // GPIO14 Optional sensor
|
||||
0, 0, 0
|
||||
},
|
||||
{ "Sonoff SV", // Sonoff SV (ESP8266)
|
||||
{ "Sonoff SV", // SONOFF_SV - Sonoff SV (ESP8266)
|
||||
GPIO_KEY1, // GPIO00 Button
|
||||
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
|
||||
0,
|
||||
|
@ -852,7 +865,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
0, 0,
|
||||
ADC0_USER // ADC0 Analog input
|
||||
},
|
||||
{ "Sonoff TH", // Sonoff TH10/16 (ESP8266)
|
||||
{ "Sonoff TH", // SONOFF_TH - Sonoff TH10/16 (ESP8266)
|
||||
GPIO_KEY1, // GPIO00 Button
|
||||
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
|
||||
0,
|
||||
|
@ -870,7 +883,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_USER, // GPIO14 Optional sensor
|
||||
0, 0, 0
|
||||
},
|
||||
{ "Sonoff Dual", // Sonoff Dual (ESP8266)
|
||||
{ "Sonoff Dual", // SONOFF_DUAL - Sonoff Dual (ESP8266)
|
||||
0,
|
||||
GPIO_TXD, // GPIO01 Relay control
|
||||
0,
|
||||
|
@ -888,7 +901,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_USER, // GPIO14 Optional sensor
|
||||
0, 0, 0
|
||||
},
|
||||
{ "Sonoff Pow", // Sonoff Pow (ESP8266 - HLW8012)
|
||||
{ "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)
|
||||
|
@ -904,7 +917,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LED1, // GPIO15 Blue Led (0 = On, 1 = Off) - Link and Power status
|
||||
0, 0
|
||||
},
|
||||
{ "Sonoff 4CH", // Sonoff 4CH (ESP8285)
|
||||
{ "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
|
||||
|
@ -923,7 +936,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On)
|
||||
0, 0
|
||||
},
|
||||
{ "Sonoff S2X", // Sonoff S20, S22 and S26 Smart Socket (ESP8266)
|
||||
{ "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
|
||||
|
@ -939,7 +952,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LED1_INV, // GPIO13 Green/Blue Led (0 = On, 1 = Off)
|
||||
0, 0, 0, 0
|
||||
},
|
||||
{ "Slampher", // Slampher (ESP8266)
|
||||
{ "Slampher", // SLAMPHER - Slampher (ESP8266)
|
||||
GPIO_KEY1, // GPIO00 Button
|
||||
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
|
||||
0,
|
||||
|
@ -955,7 +968,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
|
||||
0, 0, 0, 0
|
||||
},
|
||||
{ "Sonoff Touch", // Sonoff Touch (ESP8285)
|
||||
{ "Sonoff Touch", // SONOFF_TOUCH - Sonoff Touch (ESP8285)
|
||||
GPIO_KEY1, // GPIO00 Button
|
||||
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
|
||||
0,
|
||||
|
@ -971,7 +984,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off)
|
||||
0, 0, 0, 0
|
||||
},
|
||||
{ "Sonoff LED", // Sonoff LED (ESP8266)
|
||||
{ "Sonoff LED", // SONOFF_LED - Sonoff LED (ESP8266)
|
||||
GPIO_KEY1, // GPIO00 Button
|
||||
0, 0, 0,
|
||||
GPIO_USER, // GPIO04 Optional sensor (PWM3 Green)
|
||||
|
@ -988,7 +1001,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_USER, // GPIO15 Optional sensor (PWM4 Blue)
|
||||
0, 0
|
||||
},
|
||||
{ "1 Channel", // 1 Channel Inching/Latching Relay using (PSA-B01 - ESP8266 and PSF-B01 - ESP8285)
|
||||
{ "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)
|
||||
|
@ -1001,7 +1014,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status
|
||||
0, 0, 0, 0
|
||||
},
|
||||
{ "4 Channel", // 4 Channel Inching/Latching Relays (ESP8266)
|
||||
{ "4 Channel", // CH4 - 4 Channel Inching/Latching Relays (ESP8266)
|
||||
0,
|
||||
GPIO_TXD, // GPIO01 Relay control
|
||||
0,
|
||||
|
@ -1017,7 +1030,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
|
||||
0, 0, 0, 0
|
||||
},
|
||||
{ "Motor C/AC", // Motor Clockwise / Anti clockwise (PSA-B01 - ESP8266)
|
||||
{ "Motor C/AC", // MOTOR - Motor Clockwise / Anti clockwise (PSA-B01 - ESP8266)
|
||||
GPIO_KEY1, // GPIO00 Button
|
||||
0, 0, 0, 0, 0,
|
||||
// GPIO06 (SD_CLK Flash)
|
||||
|
@ -1030,7 +1043,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status
|
||||
0, 0, 0, 0
|
||||
},
|
||||
{ "ElectroDragon", // ElectroDragon IoT Relay Board (ESP8266)
|
||||
{ "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
|
||||
|
@ -1050,7 +1063,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LED1, // GPIO16 Green/Blue Led (1 = On, 0 = Off) - Link and Power status
|
||||
ADC0_USER // ADC0 A0 Analog input
|
||||
},
|
||||
{ "EXS Relay(s)", // ES-Store Latching relay(s) (ESP8266)
|
||||
{ "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
|
||||
|
@ -1073,7 +1086,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_USER, // GPIO16 V3.1 Module Pin 4 - V5.0 GPIO_REL4_INV Relay2 ( 1 = On)
|
||||
0
|
||||
},
|
||||
{ "WiOn", // Indoor Tap (ESP8266)
|
||||
{ "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,
|
||||
|
@ -1091,7 +1104,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On)
|
||||
0, 0
|
||||
},
|
||||
{ "Generic", // Any ESP8266/ESP8285 device like WeMos and NodeMCU hardware (ESP8266)
|
||||
{ "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
|
||||
|
@ -1111,7 +1124,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_USER, // GPIO16 D0 Wemos Wake
|
||||
ADC0_USER // ADC0 A0 Analog input
|
||||
},
|
||||
{ "Sonoff Dev", // Sonoff Dev (ESP8266)
|
||||
{ "Sonoff Dev", // SONOFF_DEV - Sonoff Dev (ESP8266)
|
||||
GPIO_KEY1, // GPIO00 E-FW Button
|
||||
GPIO_USER, // GPIO01 TX Serial RXD and Optional sensor
|
||||
0, // GPIO02
|
||||
|
@ -1131,7 +1144,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
0, // GPIO16
|
||||
ADC0_USER // ADC0 A0 Analog input
|
||||
},
|
||||
{ "H801", // Lixada H801 Wifi (ESP8266)
|
||||
{ "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
|
||||
|
@ -1150,7 +1163,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_PWM1, // GPIO15 Red
|
||||
0, 0
|
||||
},
|
||||
{ "Sonoff SC", // Sonoff SC (ESP8266)
|
||||
{ "Sonoff SC", // SONOFF_SC - onoff SC (ESP8266)
|
||||
GPIO_KEY1, // GPIO00 Button
|
||||
GPIO_TXD, // GPIO01 RXD to ATMEGA328P
|
||||
GPIO_USER, // GPIO02 Optional sensor
|
||||
|
@ -1166,7 +1179,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status
|
||||
0, 0, 0, 0
|
||||
},
|
||||
{ "Sonoff BN-SZ", // Sonoff BN-SZ01 Ceiling led (ESP8285)
|
||||
{ "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)
|
||||
|
@ -1178,7 +1191,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off) - Link and Power status
|
||||
0, 0, 0, 0
|
||||
},
|
||||
{ "Sonoff 4CH Pro", // Sonoff 4CH Pro (ESP8285)
|
||||
{ "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
|
||||
|
@ -1197,7 +1210,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On)
|
||||
0, 0
|
||||
},
|
||||
{ "Huafan SS", // Hua Fan Smart Socket (ESP8266) - like Sonoff Pow
|
||||
{ "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
|
||||
|
@ -1214,7 +1227,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_HLW_CF, // GPIO14 HLW8012 CF power
|
||||
0, 0, 0
|
||||
},
|
||||
{ "Sonoff Bridge", // Sonoff RF Bridge 433 (ESP8285)
|
||||
{ "Sonoff Bridge", // SONOFF_BRIDGE - Sonoff RF Bridge 433 (ESP8285)
|
||||
GPIO_KEY1, // GPIO00 Button
|
||||
GPIO_TXD, // GPIO01 RF bridge control
|
||||
GPIO_USER, // GPIO02 Optional sensor
|
||||
|
@ -1232,7 +1245,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_USER, // GPIO14 Optional sensor
|
||||
0, 0, 0
|
||||
},
|
||||
{ "Sonoff B1", // Sonoff B1 (ESP8285 - my9231)
|
||||
{ "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
|
||||
|
@ -1249,7 +1262,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_DCKI, // GPIO14 my9231 DCKI
|
||||
0, 0, 0
|
||||
},
|
||||
{ "AiLight", // Ai-Thinker RGBW led (ESP8266 - my9291)
|
||||
{ "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
|
||||
|
@ -1267,7 +1280,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_DCKI, // GPIO15 my9291 DCKI
|
||||
0, 0
|
||||
},
|
||||
{ "Sonoff T1 1CH", // Sonoff T1 1CH (ESP8285)
|
||||
{ "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)
|
||||
|
@ -1283,7 +1296,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
|
||||
0, 0, 0, 0
|
||||
},
|
||||
{ "Sonoff T1 2CH", // Sonoff T1 2CH (ESP8285)
|
||||
{ "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)
|
||||
|
@ -1300,7 +1313,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
|
||||
0, 0, 0, 0
|
||||
},
|
||||
{ "Sonoff T1 3CH", // Sonoff T1 3CH (ESP8285)
|
||||
{ "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)
|
||||
|
@ -1317,7 +1330,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
|
||||
0, 0, 0, 0
|
||||
},
|
||||
{ "Supla Espablo", // Supla Espablo (ESP8266)
|
||||
{ "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
|
||||
|
@ -1338,7 +1351,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LED1, // GPIO16 Led (1 = On, 0 = Off) - Link and Power status
|
||||
ADC0_USER // ADC0 A0 Analog input
|
||||
},
|
||||
{ "Witty Cloud", // Witty Cloud Dev Board (ESP8266)
|
||||
{ "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
|
||||
|
@ -1359,7 +1372,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
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 Wifi Relay (ESP8266)
|
||||
{ "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
|
||||
|
@ -1376,7 +1389,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
// GPIO11 (SD_CMD Flash)
|
||||
0, 0, 0, 0, 0, 0
|
||||
},
|
||||
{ "MagicHome", // Magic Home (aka Flux-light) (ESP8266) and Arilux LC10 (ESP8285)
|
||||
{ "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
|
||||
|
@ -1396,7 +1409,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_ARIRFSEL, // GPIO15 RF receiver control (Arilux LC10)
|
||||
0, 0
|
||||
},
|
||||
{ "Luani HVIO", // ESP8266_HVIO
|
||||
{ "Luani HVIO", // LUANIHVIO - ESP8266_HVIO
|
||||
// https://luani.de/projekte/esp8266-hvio/
|
||||
0, // GPIO00 Flash jumper
|
||||
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
|
||||
|
@ -1417,7 +1430,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
0,
|
||||
ADC0_USER // ADC0 A0 Analog input
|
||||
},
|
||||
{ "KMC 70011", // KMC 70011
|
||||
{ "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,
|
||||
|
@ -1434,7 +1447,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_REL1, // GPIO14 Relay
|
||||
0, 0, 0
|
||||
},
|
||||
{ "Arilux LC01", // Arilux AL-LC01 (ESP8285)
|
||||
{ "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
|
||||
|
@ -1454,7 +1467,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_USER, // GPIO14 RGBW LED White (optional - set to PWM4 for Cold White or Warm White)
|
||||
0, 0, 0
|
||||
},
|
||||
{ "Arilux LC11", // Arilux AL-LC11 (ESP8266)
|
||||
{ "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
|
||||
|
@ -1475,7 +1488,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_ARIRFRCV, // GPIO15 RF receiver input
|
||||
0, 0
|
||||
},
|
||||
{ "Sonoff Dual R2", // Sonoff Dual R2 (ESP8285)
|
||||
{ "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,
|
||||
|
@ -1492,7 +1505,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
|
||||
0, 0, 0, 0
|
||||
},
|
||||
{ "Arilux LC06", // Arilux AL-LC06 (ESP8285)
|
||||
{ "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
|
||||
|
@ -1512,7 +1525,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_USER, // GPIO15 RGBW LED White
|
||||
0, 0
|
||||
},
|
||||
{ "Sonoff S31", // Sonoff S31 (ESP8266 - CSE7766)
|
||||
{ "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,
|
||||
|
@ -1528,7 +1541,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status
|
||||
0, 0, 0, 0
|
||||
},
|
||||
{ "Zengge WF017", // Zenggee ZJ-WF017-A (ESP12S))
|
||||
{ "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,
|
||||
|
@ -1547,7 +1560,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_PWM3, // GPIO14 RGB LED Blue
|
||||
0, 0, 0
|
||||
},
|
||||
{ "Sonoff Pow R2", // Sonoff Pow R2 (ESP8285 - CSE7766)
|
||||
{ "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,
|
||||
|
@ -1563,7 +1576,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
|
||||
0, 0, 0, 0
|
||||
},
|
||||
{ "Sonoff iFan02", // Sonoff iFan02 (ESP8285)
|
||||
{ "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
|
||||
|
@ -1582,7 +1595,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_REL4, // GPIO15 WIFI_O3 Relay 4 (0 = Off, 1 = On) controlling the fan
|
||||
0, 0
|
||||
},
|
||||
{ "BlitzWolf SHP", // BlitzWolf BW-SHP2 and BW-SHP6 (ESP8285 - BL0937 or HJL-01 Energy Monitoring)
|
||||
{ "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
|
||||
|
@ -1605,7 +1618,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On)
|
||||
0, 0
|
||||
},
|
||||
{ "Shelly 1", // Shelly1 Open Source (ESP8266 - 2MB) - https://shelly.cloud/shelly1-open-source/
|
||||
{ "Shelly 1", // SHELLY1 - Shelly1 Open Source (ESP8266 - 2MB) - https://shelly.cloud/shelly1-open-source/
|
||||
0, // GPIO00 - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC
|
||||
0, // GPIO01 Serial RXD - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC
|
||||
0,
|
||||
|
@ -1620,7 +1633,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
// GPIO11 (SD_CMD Flash)
|
||||
0, 0, 0, 0, 0, 0
|
||||
},
|
||||
{ "Shelly 2", // Shelly2 (ESP8266 - 2MB) - https://shelly.cloud/shelly2/
|
||||
{ "Shelly 2", // SHELLY2 - Shelly2 (ESP8266 - 2MB) - https://shelly.cloud/shelly2/
|
||||
0,
|
||||
GPIO_MCP39F5_TX, // GPIO01 MCP39F501 Serial input
|
||||
0,
|
||||
|
@ -1640,7 +1653,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
0,
|
||||
0
|
||||
},
|
||||
{ "Xiaomi Philips", // Xiaomi Philips bulb (ESP8266)
|
||||
{ "Xiaomi Philips", // PHILIPS - Xiaomi Philips bulb (ESP8266)
|
||||
0, 0, 0, 0, 0, 0,
|
||||
// GPIO06 (SD_CLK Flash)
|
||||
// GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
|
||||
|
@ -1653,7 +1666,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_PWM1, // GPIO15 light intensity
|
||||
0, 0
|
||||
},
|
||||
{ "Neo Coolcam", // Neo Coolcam (ESP8266)
|
||||
{ "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
|
||||
|
@ -1668,7 +1681,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_KEY1, // GPIO13 Button
|
||||
0, 0, 0, 0
|
||||
},
|
||||
{ "ESP Switch", // Michael Haustein 4 channel wall switch (ESP07 = ESP8266)
|
||||
{ "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
|
||||
|
@ -1689,7 +1702,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_REL1_INV, // GPIO16 Green Led 1 (0 = On, 1 = Off)
|
||||
0
|
||||
},
|
||||
{ "OBI Socket", // OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko/p/2291706
|
||||
{ "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,
|
||||
|
@ -1709,7 +1722,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_USER, // GPIO16
|
||||
ADC0_USER // ADC0 A0 Analog input
|
||||
},
|
||||
{ "Teckin", // https://www.amazon.de/gp/product/B07D5V139R
|
||||
{ "Teckin", // TECKIN - https://www.amazon.de/gp/product/B07D5V139R
|
||||
0,
|
||||
GPIO_KEY1, // GPIO01 Serial TXD and Button
|
||||
0,
|
||||
|
@ -1727,7 +1740,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On)
|
||||
0, 0, 0
|
||||
},
|
||||
{ "AplicWDP303075", // Aplic WDP 303075 (ESP8285 - HLW8012 Energy Monitoring)
|
||||
{ "AplicWDP303075", // APLIC_WDP303075 - Aplic WDP 303075 (ESP8285 - HLW8012 Energy Monitoring)
|
||||
// https://www.amazon.de/dp/B07CNWVNJ2
|
||||
0, 0, 0,
|
||||
GPIO_KEY1, // GPIO03 Button
|
||||
|
@ -1744,7 +1757,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_REL1, // GPIO14 Relay SRU 5VDC SDA (0 = Off, 1 = On )
|
||||
0, 0, 0
|
||||
},
|
||||
{ "Tuya MCU", // Tuya MCU device (ESP8266 w/ separate MCU)
|
||||
{ "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
|
||||
|
@ -1765,7 +1778,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_USER,
|
||||
0
|
||||
},
|
||||
{ "Gosund SP1 v23", // https://www.amazon.de/gp/product/B0777BWS1P
|
||||
{ "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,
|
||||
|
@ -1783,7 +1796,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On)
|
||||
0, 0, 0
|
||||
},
|
||||
{ "ARMTR Dimmer", // ARMTRONIX Dimmer, one or two channel (ESP8266 w/ separate MCU dimmer)
|
||||
{ "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,
|
||||
|
@ -1805,7 +1818,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_USER,
|
||||
0
|
||||
},
|
||||
{ "SK03 Outdoor", // Outdoor smart plug with power monitoring HLW8012 chip - https://www.amazon.com/gp/product/B07CG7MBPV
|
||||
{ "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
|
||||
|
@ -1822,7 +1835,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On)
|
||||
0, 0
|
||||
},
|
||||
{ "PS-16-DZ", // PS-16-DZ Dimmer (ESP8266 w/ separate Nuvoton MCU dimmer)
|
||||
{ "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
|
||||
|
@ -1843,7 +1856,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_USER,
|
||||
0
|
||||
},
|
||||
{ "Teckin US", // Teckin SP20 US with Energy Monitoring
|
||||
{ "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
|
||||
|
@ -1863,7 +1876,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_NRG_CF1, // GPIO14 BL0937 or HJL-01 CF1 current / voltage
|
||||
0, 0, 0
|
||||
},
|
||||
{ "Manzoku strip", // "MANZOKU" labeled power strip, EU version
|
||||
{ "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
|
||||
|
@ -1885,7 +1898,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_USER, // GPIO16
|
||||
0
|
||||
},
|
||||
{ "OBI Socket 2", // OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko-2-stueck-weiss/p/4077673
|
||||
{ "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,
|
||||
|
@ -1902,7 +1915,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LED1, // GPIO13 Red LED - Power status
|
||||
0, 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
|
||||
{ "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
|
||||
|
@ -1920,7 +1933,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_IRSEND, // GPIO14 IR Transmitter
|
||||
0, 0, 0
|
||||
},
|
||||
{ "Digoo DG-SP202", // Digoo DG-SP202
|
||||
{ "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
|
||||
|
@ -1941,7 +1954,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_KEY2_NP, // GPIO16 Button2, externally pulled up
|
||||
0
|
||||
},
|
||||
{ "KA10", // SMANERGY KA10 (ESP8285 - BL0937 Energy Monitoring) - https://www.amazon.es/dp/B07MBTCH2Y
|
||||
{ "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
|
||||
|
@ -1959,7 +1972,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_REL1, // GPIO14 Relay 1
|
||||
0, 0, 0
|
||||
},
|
||||
{ "Luminea ZX2820",
|
||||
{ "Luminea ZX2820", // ZX2820
|
||||
GPIO_KEY1, // GPIO00 Button
|
||||
0, 0, 0,
|
||||
GPIO_HLW_CF, // GPIO04 HLW8012 CF power
|
||||
|
@ -1975,7 +1988,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_REL1, // GPIO14 Relay
|
||||
0, 0, 0
|
||||
},
|
||||
{ "Mi Desk Lamp", // Mi LED Desk Lamp - https://www.mi.com/global/smartlamp/
|
||||
{ "Mi Desk Lamp", // MI_DESK_LAMP - Mi LED Desk Lamp - https://www.mi.com/global/smartlamp/
|
||||
0, 0,
|
||||
GPIO_KEY1, // GPIO02 Button
|
||||
0,
|
||||
|
@ -1991,7 +2004,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_ROT1B, // GPIO13 Rotary switch B pin
|
||||
0, 0, 0, 0
|
||||
},
|
||||
{ "SP10", // Tuya SP10 (BL0937 Energy Monitoring)
|
||||
{ "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
|
||||
|
@ -2010,7 +2023,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_REL1, // GPIO14 Relay and red LED
|
||||
0, 0, 0
|
||||
},
|
||||
{ "WAGA CHCZ02MB", // WAGA life CHCZ02MB (HJL-01 Energy Monitoring)
|
||||
{ "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
|
||||
|
@ -2030,7 +2043,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LEDLNK_INV, // GPIO15 Blue LED - Link status
|
||||
0, 0
|
||||
},
|
||||
{ "SYF05", // Sunyesmart SYF05 (a.k.a. Fcmila) = TYWE3S + SM16726
|
||||
{ "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
|
||||
|
@ -2054,7 +2067,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_USER, // GPIO16 N.C.
|
||||
ADC0_USER // ADC0 A0 Analog input
|
||||
},
|
||||
{ "Sonoff L1", // Sonoff L1 RGB LED controller (ESP8266 w/ separate Nuvoton MCU)
|
||||
{ "Sonoff L1", // SONOFF_L1 - Sonoff L1 RGB LED controller (ESP8266 w/ separate Nuvoton MCU)
|
||||
0,
|
||||
GPIO_TXD, // GPIO01 MCU serial control
|
||||
0,
|
||||
|
@ -2070,7 +2083,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
GPIO_LED1_INV, // GPIO13 WiFi Blue Led - Link and Power status
|
||||
0, 0, 0, 0
|
||||
},
|
||||
{ "Sonoff iFan03", // Sonoff iFan03 (ESP8285)
|
||||
{ "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
|
||||
|
@ -2091,235 +2104,4 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
|
|||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Optionals
|
||||
|
||||
{ "RGB Smart Plug", // Tuya based smart plug with power monitoring and RGB light
|
||||
// https://www.aliexpress.com/item/ET-Smart-Plug-Wifi-Socket-With-Switch-Phone-APP-Voice-Remote-Control-Monitor-Smart-Timing-Switch/32964036349.html?spm=a2g0s.9042311.0.0.439c4c4d4N8N2Q
|
||||
// https://xiangshangcn.en.alibaba.com/product/60844251239-807590977/RGB_wifi_smart_plug_smart_socket_smart_outlet_EU_works_with_Amazon_alexa_google_home_mobile_app_tuya_solution_smart_life.html?spm=a2700.icbuShop.41413.24.4e996767oFAAmO
|
||||
GPIO_PWM1, // GPIO00 Red
|
||||
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
|
||||
GPIO_PWM3, // GPIO02 Blue
|
||||
GPIO_USER, // GPIO03 Serial TXD and Optional sensor
|
||||
GPIO_PWM2, // GPIO04 Green
|
||||
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
|
||||
}
|
||||
|
||||
{ "ESP RGBWWC", // esp rgbww controller https://github.com/pljakobs/esp_rgbww_controller/tree/v2.3
|
||||
GPIO_KEY1, // GPIO00 Button
|
||||
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
|
||||
0, // GPIO02
|
||||
GPIO_USER, // GPIO03 Serial TXD and Optional sensor
|
||||
GPIO_PWM5, // GPIO04 LED Warm White
|
||||
GPIO_PWM4, // GPIO05 LED Cold 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_PWM2, // GPIO12 LED Green
|
||||
GPIO_PWM1, // GPIO13 LED Red
|
||||
GPIO_PWM3, // GPIO14 LED Blue
|
||||
0, // GPIO15
|
||||
GPIO_KEY2, // GPIO16 Button
|
||||
0
|
||||
}
|
||||
|
||||
{ "N0DY Relay", // N0DY Wifi Dual Relay (ESP-07)
|
||||
// https://www.n0dy.com/product/web-controlled-dual-relay/
|
||||
// https://www.amazon.com/dp/B072MKV8ZM
|
||||
GPIO_KEY1, // GPIO00 PROG Button
|
||||
GPIO_USER, // GPIO01 Serial RXD or Optional sensor on J2 RXD (if not using serial io)
|
||||
0, // GPIO02
|
||||
GPIO_USER, // GPIO03 Serial TXD or Optional sensor on J2 TXD (if not using serial io)
|
||||
GPIO_REL2_INV, // GPIO04 Relay 2 (active low)
|
||||
GPIO_REL1_INV, // GPIO05 Relay 1 (active low)
|
||||
// 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", // Magic Home (aka Flux-light) (ESP8266)
|
||||
// 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_USER, // GPIO04 IR receiver (optional)
|
||||
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)
|
||||
GPIO_PWM1, // GPIO14 RGB LED Red
|
||||
0, 0, 0
|
||||
}
|
||||
|
||||
{ "Arilux LC10", // Arilux LC10 (ESP8285), RGBW + RF
|
||||
// https://github.com/arendst/Sonoff-Tasmota/wiki/MagicHome-with-ESP8285
|
||||
// https://www.aliexpress.com/item/DC5-24V-Wireless-WIFI-LED-RGB-Controller-RGBW-Controller-IR-RF-Remote-Control-IOS-Android-for/32827253255.html
|
||||
// https://www.aliexpress.com/item/Wifi-LED-RGB-Controler-DC12V-MIni-Wifi-RGB-RGBW-LED-Controller-for-RGB-RGBW-LED-Strip/32673444047.html
|
||||
GPIO_USER, // GPIO00 Optional Button
|
||||
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
|
||||
0,
|
||||
GPIO_USER, // GPIO03 Serial TXD and Optional sensor0
|
||||
GPIO_ARIRFRCV, // GPIO04 RF receiver input
|
||||
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_PWM4, // GPIO13 RGBW LED White
|
||||
GPIO_PWM1, // GPIO14 RGB LED Red
|
||||
GPIO_ARIRFSEL, // GPIO15 RF receiver control
|
||||
0, 0
|
||||
}
|
||||
|
||||
{ "Xenon 3CH", // Xenon 3CH (ESP8266) - (#1128)
|
||||
0, 0, 0,
|
||||
GPIO_KEY2, // GPIO03 Serial TXD and Optional sensor
|
||||
GPIO_REL2, // GPIO04 Relay 2
|
||||
GPIO_KEY3, // GPIO05 Input 2
|
||||
// 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_KEY1, // GPIO12 Key input
|
||||
GPIO_REL1, // GPIO13 Relay 1
|
||||
0,
|
||||
GPIO_REL3, // GPIO15 Relay 3
|
||||
0, 0
|
||||
}
|
||||
|
||||
{ "PowStro Basic", // PowStro (ESP8266) - (#1419)
|
||||
0, 0, 0, 0,
|
||||
GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On)
|
||||
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_KEY1, // GPIO12 Button
|
||||
0, 0,
|
||||
GPIO_LED1, // GPIO15 Led (1 = On, 0 = Off) - Link and Power status
|
||||
0, 0
|
||||
}
|
||||
|
||||
{ "SMPW701E", // SM-PW701E WLAN Socket (#1190)
|
||||
0, 0, 0, 0,
|
||||
GPIO_LED1_INV, // GPIO04 Blue Led (0 = On, 1 = Off) - Link and Power status
|
||||
0, // GPIO05 IR or RF receiver (optional)
|
||||
// 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 and Red Led (0 = Off, 1 = On)
|
||||
GPIO_KEY1, // GPIO13 Button
|
||||
0, 0, 0, 0
|
||||
}
|
||||
|
||||
{ "SWA1", // Smart Plugs (ESP8266)
|
||||
0,
|
||||
GPIO_USER, // GPIO01
|
||||
0,
|
||||
GPIO_USER, // GPIO03
|
||||
GPIO_LED1_INV, // GPIO04 Blue LED - Link and Power status
|
||||
GPIO_REL1, // GPIO05 Red LED and relay
|
||||
// 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_KEY1, // GPIO13 Button (normally GPIO00)
|
||||
GPIO_USER, // GPIO14
|
||||
0, 0, 0
|
||||
}
|
||||
|
||||
{ "MagicHome v2.3", // Magic Home (aka Flux-light) (ESP8266) (#1353)
|
||||
0, 0,
|
||||
GPIO_LED1_INV, // GPIO02 Blue onboard LED - Link and Power status
|
||||
0,
|
||||
GPIO_USER, // GPIO04 IR receiver (optional)
|
||||
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_PWM1, // GPIO12 RGB LED Red
|
||||
GPIO_PWM3, // GPIO13 RGB LED Blue
|
||||
0,
|
||||
GPIO_PWM4, // GPIO15 RGBW LED White
|
||||
0, 0
|
||||
}
|
||||
|
||||
{ "Ledunia", // Ledunia (ESP8266 - 32MB) - http://ledunia.de/
|
||||
GPIO_USER, // GPIO00 (D0)
|
||||
GPIO_USER, // GPIO01 (D7) Serial RXD
|
||||
GPIO_USER, // GPIO02 (D2)
|
||||
GPIO_USER, // GPIO03 (D8) Serial TXD
|
||||
GPIO_USER, // GPIO04 (D4) 4 x WS2812 Leds, (DOUT) Extents WS2812 string
|
||||
GPIO_USER, // GPIO05 (D5) Blue 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_USER, // GPIO12 (D12)
|
||||
GPIO_USER, // GPIO13 (D13)
|
||||
GPIO_USER, // GPIO14 (D14)
|
||||
GPIO_USER, // GPIO15 (D15)
|
||||
GPIO_USER, // GPIO16 (D16)
|
||||
0 // ADC0 Analog input (A0)
|
||||
}
|
||||
|
||||
{ "Delock 11826", // Delock 11826 (ESP8285) = Sonoff Basic
|
||||
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
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
#endif // _SONOFF_TEMPLATE_H_
|
||||
|
|
|
@ -20,6 +20,6 @@
|
|||
#ifndef _SONOFF_VERSION_H_
|
||||
#define _SONOFF_VERSION_H_
|
||||
|
||||
const uint32_t VERSION = 0x0606000B;
|
||||
const uint32_t VERSION = 0x0606000C;
|
||||
|
||||
#endif // _SONOFF_VERSION_H_
|
||||
|
|
|
@ -325,6 +325,24 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c
|
|||
return out;
|
||||
}
|
||||
|
||||
char* Uint64toHex(uint64_t value, char *str, uint16_t bits)
|
||||
{
|
||||
ulltoa(value, str, 16); // Get 64bit value
|
||||
|
||||
int fill = 8;
|
||||
if ((bits > 3) && (bits < 65)) {
|
||||
fill = bits / 4; // Max 16
|
||||
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))) { // Fix for JSON output (https://stackoverflow.com/questions/1423081/json-left-out-infinity-and-nan-json-status-in-ecmascript)
|
||||
|
@ -761,25 +779,26 @@ bool DecodeCommand(const char* haystack, void (* const MyCommand[])(void))
|
|||
return false;
|
||||
}
|
||||
|
||||
const char kOptions[] PROGMEM = "OFF|" D_OFF "|" D_FALSE "|" D_STOP "|" D_CELSIUS "|" // 0
|
||||
"ON|" D_ON "|" D_TRUE "|" D_START "|" D_FAHRENHEIT "|" D_USER "|" // 1
|
||||
"TOGGLE|" D_TOGGLE "|" D_ADMIN "|" // 2
|
||||
"BLINK|" D_BLINK "|" // 3
|
||||
"BLINKOFF|" D_BLINKOFF "|" // 4
|
||||
"ALL" ; // 255
|
||||
|
||||
const uint8_t sNumbers[] PROGMEM = { 0,0,0,0,0,
|
||||
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 = -1;
|
||||
|
||||
if (GetCommandCode(command, sizeof(command), state_text, kOptionOff) >= 0) {
|
||||
state_number = 0;
|
||||
}
|
||||
else if (GetCommandCode(command, sizeof(command), state_text, kOptionOn) >= 0) {
|
||||
state_number = 1;
|
||||
}
|
||||
else if (GetCommandCode(command, sizeof(command), state_text, kOptionToggle) >= 0) {
|
||||
state_number = 2;
|
||||
}
|
||||
else if (GetCommandCode(command, sizeof(command), state_text, kOptionBlink) >= 0) {
|
||||
state_number = 3;
|
||||
}
|
||||
else if (GetCommandCode(command, sizeof(command), state_text, kOptionBlinkOff) >= 0) {
|
||||
state_number = 4;
|
||||
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;
|
||||
}
|
||||
|
@ -950,6 +969,11 @@ int ResponseJsonEnd(void)
|
|||
return ResponseAppend_P(PSTR("}"));
|
||||
}
|
||||
|
||||
int ResponseJsonEndEnd(void)
|
||||
{
|
||||
return ResponseAppend_P(PSTR("}}"));
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* GPIO Module and Template management
|
||||
\*********************************************************************************************/
|
||||
|
|
|
@ -366,7 +366,7 @@ void CmndStatus(void)
|
|||
XsnsDriverState();
|
||||
ResponseAppend_P(PSTR(",\"Sensors\":"));
|
||||
XsnsSensorState();
|
||||
ResponseAppend_P(PSTR("}}"));
|
||||
ResponseJsonEndEnd();
|
||||
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "4"));
|
||||
}
|
||||
|
||||
|
@ -795,14 +795,14 @@ void CmndModules(void)
|
|||
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);
|
||||
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))) {
|
||||
ResponseAppend_P(PSTR("]}"));
|
||||
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++;
|
||||
|
@ -839,7 +839,7 @@ void CmndGpio(void)
|
|||
if (jsflg) { ResponseAppend_P(PSTR(",")); }
|
||||
jsflg = true;
|
||||
char stemp1[TOPSZ];
|
||||
ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":\"%d (%s)\""), i, Settings.my_gp.io[i], GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_gp.io[i], kSensorNames));
|
||||
ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":{\"%d\":\"%s\"}"), i, Settings.my_gp.io[i], GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_gp.io[i], kSensorNames));
|
||||
}
|
||||
}
|
||||
if (jsflg) {
|
||||
|
@ -859,20 +859,19 @@ void CmndGpios(void)
|
|||
bool jsflg = false;
|
||||
for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) {
|
||||
midx = pgm_read_byte(kGpioNiceList + i);
|
||||
if (!GetUsedInModule(midx, cmodule.io)) {
|
||||
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)) {
|
||||
ResponseAppend_P(PSTR("]}"));
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_STAT, UpperCase(XdrvMailbox.command, XdrvMailbox.command));
|
||||
jsflg = false;
|
||||
lines++;
|
||||
}
|
||||
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';
|
||||
|
|
|
@ -447,7 +447,9 @@ void GetFeatures(void)
|
|||
#ifdef USE_A4988_Stepper
|
||||
feature5 |= 0x00000020; // xdrv_25_A4988.ino
|
||||
#endif
|
||||
// feature5 |= 0x00000040;
|
||||
#ifdef USE_DDS2382
|
||||
feature5 |= 0x00000040; // Xnrg_09_dds2382.ino
|
||||
#endif
|
||||
// feature5 |= 0x00000080;
|
||||
|
||||
// feature5 |= 0x00000100;
|
||||
|
|
|
@ -299,7 +299,7 @@ void BreakTime(uint32_t time_input, TIME_T &tm)
|
|||
strlcpy(tm.name_of_month, kMonthNames + (month *3), 4);
|
||||
tm.month = month + 1; // jan is month 1
|
||||
tm.day_of_month = time + 1; // day of month
|
||||
tm.valid = (time_input > 1451602800); // 2016-01-01
|
||||
tm.valid = (time_input > START_VALID_TIME); // 2016-01-01
|
||||
}
|
||||
|
||||
uint32_t MakeTime(TIME_T &tm)
|
||||
|
@ -374,9 +374,9 @@ void RtcSecond(void)
|
|||
uint8_t offset = (uptime < 30) ? RtcTime.second : (((ESP.getChipId() & 0xF) * 3) + 3) ; // First try ASAP to sync. If fails try once every 60 seconds based on chip id
|
||||
if (!global_state.wifi_down && (((offset == RtcTime.second) && ((RtcTime.year < 2016) || (Rtc.ntp_sync_minute == RtcTime.minute))) || ntp_force_sync)) {
|
||||
Rtc.ntp_time = sntp_get_current_timestamp();
|
||||
if (Rtc.ntp_time > 1451602800) { // Fix NTP bug in core 2.4.1/SDK 2.2.1 (returns Thu Jan 01 08:00:10 1970 after power on)
|
||||
if (Rtc.ntp_time > START_VALID_TIME) { // Fix NTP bug in core 2.4.1/SDK 2.2.1 (returns Thu Jan 01 08:00:10 1970 after power on)
|
||||
ntp_force_sync = false;
|
||||
if (Rtc.utc_time > 1451602800) { Rtc.drift_time = Rtc.ntp_time - Rtc.utc_time; }
|
||||
if (Rtc.utc_time > START_VALID_TIME) { Rtc.drift_time = Rtc.ntp_time - Rtc.utc_time; }
|
||||
Rtc.utc_time = Rtc.ntp_time;
|
||||
Rtc.ntp_sync_minute = 60; // Sync so block further requests
|
||||
if (Rtc.restart_time == 0) {
|
||||
|
@ -391,7 +391,7 @@ void RtcSecond(void)
|
|||
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "(" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str());
|
||||
ntp_synced_message = true;
|
||||
|
||||
if (Rtc.local_time < 1451602800) { // 2016-01-01
|
||||
if (Rtc.local_time < START_VALID_TIME) { // 2016-01-01
|
||||
rules_flag.time_init = 1;
|
||||
} else {
|
||||
rules_flag.time_set = 1;
|
||||
|
@ -403,7 +403,7 @@ void RtcSecond(void)
|
|||
}
|
||||
Rtc.utc_time++;
|
||||
Rtc.local_time = Rtc.utc_time;
|
||||
if (Rtc.local_time > 1451602800) { // 2016-01-01
|
||||
if (Rtc.local_time > START_VALID_TIME) { // 2016-01-01
|
||||
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);
|
||||
|
@ -447,7 +447,7 @@ void RtcSecond(void)
|
|||
|
||||
void RtcSetTime(uint32_t epoch)
|
||||
{
|
||||
if (epoch < 1451602800) { // 2016-01-01
|
||||
if (epoch < START_VALID_TIME) { // 2016-01-01
|
||||
Rtc.user_time_entry = false;
|
||||
ntp_force_sync = true;
|
||||
} else {
|
||||
|
|
|
@ -23,6 +23,8 @@ typedef struct SBuffer_impl {
|
|||
uint8_t buf[]; // the actual data
|
||||
} SBuffer_impl;
|
||||
|
||||
|
||||
|
||||
typedef class SBuffer {
|
||||
|
||||
protected:
|
||||
|
@ -43,7 +45,8 @@ public:
|
|||
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(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;
|
||||
|
@ -57,6 +60,12 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
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) { // append 8 bits value
|
||||
if (_buf->len < _buf->size) { // do we have room for 1 byte
|
||||
_buf->buf[_buf->len++] = data;
|
||||
|
@ -124,6 +133,15 @@ public:
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
||||
SBuffer subBuffer(const size_t start, size_t len) const {
|
||||
if (start >= _buf->len) {
|
||||
|
@ -138,6 +156,32 @@ public:
|
|||
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;
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@ const char HTTP_SCRIPT_COUNTER[] PROGMEM =
|
|||
"wl(u);";
|
||||
|
||||
const char HTTP_SCRIPT_ROOT[] PROGMEM =
|
||||
|
||||
"function la(p){"
|
||||
"var a='';"
|
||||
"if(la.arguments.length==1){"
|
||||
|
@ -110,6 +111,12 @@ const char HTTP_SCRIPT_ROOT[] PROGMEM =
|
|||
"x.send();"
|
||||
"lt=setTimeout(la,%d);" // Settings.web_refresh
|
||||
"}"
|
||||
#ifdef USE_SCRIPT_WEB_DISPLAY
|
||||
"function seva(par,ivar){"
|
||||
"la('&sv='+ivar+'_'+par);"
|
||||
"}"
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef USE_JAVASCRIPT_ES6
|
||||
"lb=p=>la('&d='+p);" // Dark - Bright &d related to lb(value) and WebGetArg("d", tmp, sizeof(tmp));
|
||||
|
@ -1009,6 +1016,10 @@ bool HandleRootStatusRefresh(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
#ifdef USE_SCRIPT_WEB_DISPLAY
|
||||
Script_Check_HTML_Setvars();
|
||||
#endif
|
||||
|
||||
char tmp[8]; // WebGetArg numbers only
|
||||
char svalue[32]; // Command and number parameter
|
||||
|
||||
|
|
|
@ -37,14 +37,15 @@
|
|||
#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_POWERSET, CMND_VOLTAGESET, CMND_CURRENTSET, CMND_FREQUENCYSET, CMND_MODULEADDRESS };
|
||||
|
||||
const char kEnergyCommands[] PROGMEM = "|" // No prefix
|
||||
D_CMND_POWERCAL "|" D_CMND_VOLTAGECAL "|" D_CMND_CURRENTCAL "|"
|
||||
D_CMND_POWERSET "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTSET "|" D_CMND_FREQUENCYSET "|"
|
||||
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
|
||||
|
@ -57,7 +58,7 @@ const char kEnergyCommands[] PROGMEM = "|" // No prefix
|
|||
|
||||
void (* const EnergyCommand[])(void) PROGMEM = {
|
||||
&CmndPowerCal, &CmndVoltageCal, &CmndCurrentCal,
|
||||
&CmndPowerSet, &CmndVoltageSet, &CmndCurrentSet, &CmndFrequencySet,
|
||||
&CmndPowerSet, &CmndVoltageSet, &CmndCurrentSet, &CmndFrequencySet, &CmndModuleAddress,
|
||||
#ifdef USE_ENERGY_MARGIN_DETECTION
|
||||
&CmndPowerDelta, &CmndPowerLow, &CmndPowerHigh, &CmndVoltageLow, &CmndVoltageHigh, &CmndCurrentLow, &CmndCurrentHigh,
|
||||
#ifdef USE_ENERGY_POWER_LIMIT
|
||||
|
@ -68,33 +69,37 @@ void (* const EnergyCommand[])(void) PROGMEM = {
|
|||
#endif // USE_ENERGY_MARGIN_DETECTION
|
||||
&CmndEnergyReset, &CmndTariff };
|
||||
|
||||
const char kEnergyPhases[] PROGMEM = "|%s / %s|%s / %s / %s||[%s,%s]|[%s,%s,%s]";
|
||||
|
||||
struct ENERGY {
|
||||
float voltage = 0; // 123.1 V
|
||||
float current = 0; // 123.123 A
|
||||
float active_power = 0; // 123.1 W
|
||||
float apparent_power = NAN; // 123.1 VA
|
||||
float reactive_power = NAN; // 123.1 VAr
|
||||
float power_factor = NAN; // 0.12
|
||||
float frequency = NAN; // 123.1 Hz
|
||||
float voltage[3] = { 0, 0, 0 }; // 123.1 V
|
||||
float current[3] = { 0, 0, 0 }; // 123.123 A
|
||||
float active_power[3] = { 0, 0, 0 }; // 123.1 W
|
||||
float apparent_power[3] = { NAN, NAN, NAN }; // 123.1 VA
|
||||
float reactive_power[3] = { NAN, NAN, NAN }; // 123.1 VAr
|
||||
float power_factor[3] = { NAN, NAN, NAN }; // 0.12
|
||||
float frequency[3] = { NAN, NAN, NAN }; // 123.1 Hz
|
||||
|
||||
float start_energy = 0; // 12345.12345 kWh total previous
|
||||
float start_energy = 0; // 12345.12345 kWh total previous
|
||||
float daily = 0; // 123.123 kWh
|
||||
float total = 0; // 12345.12345 kWh tariff 1 + 2
|
||||
float total1 = 0; // 12345.12345 kWh tariff 1 - off-peak
|
||||
float export_active = NAN; // 123.123 KWh
|
||||
|
||||
float daily = 0; // 123.123 kWh
|
||||
float total = 0; // 12345.12345 kWh tariff 1 + 2
|
||||
float total1 = 0; // 12345.12345 kWh tariff 1 - off-peak
|
||||
float export_active = NAN; // 123.123 KWh
|
||||
|
||||
unsigned long kWhtoday_delta = 0; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to Energy.kWhtoday (HLW and CSE only)
|
||||
unsigned long kWhtoday_offset = 0; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily
|
||||
unsigned long kWhtoday; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily
|
||||
unsigned long period = 0; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily
|
||||
unsigned long kWhtoday_delta = 0; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to Energy.kWhtoday (HLW and CSE only)
|
||||
unsigned long kWhtoday_offset = 0; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily
|
||||
unsigned long kWhtoday; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily
|
||||
unsigned long period = 0; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily
|
||||
|
||||
uint8_t fifth_second = 0;
|
||||
uint8_t command_code = 0;
|
||||
uint8_t data_valid = 0;
|
||||
|
||||
bool voltage_available = true; // Enable if voltage is measured
|
||||
bool current_available = true; // Enable if current is measured
|
||||
uint8_t phase_count = 1; // Number of phases active
|
||||
bool voltage_common = false; // Use single voltage
|
||||
|
||||
bool voltage_available = true; // Enable if voltage is measured
|
||||
bool current_available = true; // Enable if current is measured
|
||||
|
||||
bool type_dc = false;
|
||||
bool power_on = true;
|
||||
|
@ -123,6 +128,25 @@ Ticker ticker_energy;
|
|||
|
||||
/********************************************************************************************/
|
||||
|
||||
bool EnergyTariff1Active() // Off-Peak hours
|
||||
{
|
||||
uint8_t tariff1 = Settings.register8[R8_ENERGY_TARIFF1_ST];
|
||||
uint8_t tariff2 = Settings.register8[R8_ENERGY_TARIFF2_ST];
|
||||
if (IsDst() && (Settings.register8[R8_ENERGY_TARIFF1_DS] != Settings.register8[R8_ENERGY_TARIFF2_DS])) {
|
||||
tariff1 = Settings.register8[R8_ENERGY_TARIFF1_DS];
|
||||
tariff2 = Settings.register8[R8_ENERGY_TARIFF2_DS];
|
||||
}
|
||||
if (tariff1 != tariff2) {
|
||||
return ((RtcTime.hour < tariff2) || // Tarrif1 = Off-Peak
|
||||
(RtcTime.hour >= tariff1) ||
|
||||
(Settings.flag3.energy_weekend && ((RtcTime.day_of_week == 1) ||
|
||||
(RtcTime.day_of_week == 7)))
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void EnergyUpdateToday(void)
|
||||
{
|
||||
if (Energy.kWhtoday_delta > 1000) {
|
||||
|
@ -143,11 +167,7 @@ void EnergyUpdateToday(void)
|
|||
Energy.daily = (float)(RtcSettings.energy_kWhtoday) / 100000;
|
||||
Energy.total = (float)(RtcSettings.energy_kWhtotal + RtcSettings.energy_kWhtoday) / 100000;
|
||||
|
||||
if ((RtcTime.hour < Settings.param[P_ENERGY_TARIFF2]) || // Tarrif1 = Off-Peak
|
||||
(RtcTime.hour >= Settings.param[P_ENERGY_TARIFF1]) ||
|
||||
(Settings.flag3.energy_weekend && ((RtcTime.day_of_week == 1) ||
|
||||
(RtcTime.day_of_week == 7)))
|
||||
) {
|
||||
if (EnergyTariff1Active()) { // Tarrif1 = Off-Peak
|
||||
RtcSettings.energy_usage.usage1_kWhtoday += energy_diff;
|
||||
RtcSettings.energy_usage.return1_kWhtotal += return_diff;
|
||||
Energy.total1 = (float)(RtcSettings.energy_usage.usage1_kWhtotal + RtcSettings.energy_usage.usage1_kWhtoday) / 100000;
|
||||
|
@ -260,23 +280,23 @@ void EnergyMarginCheck(void)
|
|||
}
|
||||
|
||||
if (Settings.energy_power_delta) {
|
||||
float delta = abs(Energy.power_history[0] - Energy.active_power);
|
||||
float delta = abs(Energy.power_history[0] - Energy.active_power[0]);
|
||||
// Any delta compared to minimal delta
|
||||
float min_power = (Energy.power_history[0] > Energy.active_power) ? Energy.active_power : Energy.power_history[0];
|
||||
float min_power = (Energy.power_history[0] > Energy.active_power[0]) ? Energy.active_power[0] : Energy.power_history[0];
|
||||
if (((delta / min_power) * 100) > Settings.energy_power_delta) {
|
||||
Energy.power_delta = 1;
|
||||
Energy.power_history[1] = Energy.active_power; // We only want one report so reset history
|
||||
Energy.power_history[2] = Energy.active_power;
|
||||
Energy.power_history[1] = Energy.active_power[0]; // We only want one report so reset history
|
||||
Energy.power_history[2] = Energy.active_power[0];
|
||||
}
|
||||
}
|
||||
Energy.power_history[0] = Energy.power_history[1]; // Shift in history every second allowing power changes to settle for up to three seconds
|
||||
Energy.power_history[1] = Energy.power_history[2];
|
||||
Energy.power_history[2] = Energy.active_power;
|
||||
Energy.power_history[2] = Energy.active_power[0];
|
||||
|
||||
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)) {
|
||||
energy_power_u = (uint16_t)(Energy.active_power);
|
||||
energy_voltage_u = (uint16_t)(Energy.voltage);
|
||||
energy_current_u = (uint16_t)(Energy.current * 1000);
|
||||
energy_power_u = (uint16_t)(Energy.active_power[0]);
|
||||
energy_voltage_u = (uint16_t)(Energy.voltage[0]);
|
||||
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);
|
||||
|
||||
|
@ -316,7 +336,7 @@ void EnergyMarginCheck(void)
|
|||
#ifdef USE_ENERGY_POWER_LIMIT
|
||||
// Max Power
|
||||
if (Settings.energy_max_power_limit) {
|
||||
if (Energy.active_power > 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 {
|
||||
|
@ -407,15 +427,17 @@ void EnergyOverTempCheck()
|
|||
Energy.data_valid++;
|
||||
if (Energy.data_valid > ENERGY_WATCHDOG) {
|
||||
// Reset energy registers
|
||||
Energy.voltage = 0;
|
||||
Energy.current = 0;
|
||||
Energy.active_power = 0;
|
||||
if (!isnan(Energy.apparent_power)) { Energy.apparent_power = 0; }
|
||||
if (!isnan(Energy.reactive_power)) { Energy.reactive_power = 0; }
|
||||
if (!isnan(Energy.frequency)) { Energy.frequency = 0; }
|
||||
if (!isnan(Energy.power_factor)) { Energy.power_factor = 0; }
|
||||
Energy.start_energy = 0;
|
||||
for (uint32_t i = 0; i < Energy.phase_count; i++) {
|
||||
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; }
|
||||
}
|
||||
if (!isnan(Energy.export_active)) { Energy.export_active = 0; }
|
||||
Energy.start_energy = 0;
|
||||
|
||||
XnrgCall(FUNC_ENERGY_RESET);
|
||||
}
|
||||
|
@ -501,19 +523,31 @@ void CmndEnergyReset(void)
|
|||
|
||||
void CmndTariff(void)
|
||||
{
|
||||
// Tariff1 23
|
||||
// Tariff2 7
|
||||
// Tariff3 0/1
|
||||
// Tariff1 22,23 - Tariff1 start hour for Standard Time and Daylight Savings Time
|
||||
// Tariff2 6,7 - Tariff2 start hour for Standard Time and Daylight Savings Time
|
||||
// Tariff9 0/1
|
||||
|
||||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) {
|
||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 24)) {
|
||||
Settings.param[P_ENERGY_TARIFF1 + XdrvMailbox.index -1] = XdrvMailbox.payload;
|
||||
char *p;
|
||||
char *str = strtok_r(XdrvMailbox.data, ", ", &p);
|
||||
uint32_t time_type = 0;
|
||||
while ((str != nullptr) && (time_type <= 2)) {
|
||||
uint8_t value = strtol(str, nullptr, 10);
|
||||
if ((value >= 0) && (value < 24)) {
|
||||
Settings.register8[R8_ENERGY_TARIFF1_ST + (XdrvMailbox.index -1) + time_type] = value;
|
||||
}
|
||||
str = strtok_r(nullptr, ", ", &p);
|
||||
time_type += 2;
|
||||
}
|
||||
}
|
||||
else if (XdrvMailbox.index == 3) {
|
||||
else if (XdrvMailbox.index == 9) {
|
||||
Settings.flag3.energy_weekend = XdrvMailbox.payload & 1;
|
||||
}
|
||||
Response_P(PSTR("{\"%s\":{\"Off-Peak\":%d,\"Standard\":%d,\"Weekend\":\"%s\"}}"),
|
||||
XdrvMailbox.command, Settings.param[P_ENERGY_TARIFF1], Settings.param[P_ENERGY_TARIFF2], GetStateText(Settings.flag3.energy_weekend));
|
||||
Response_P(PSTR("{\"%s\":{\"Off-Peak\":[%d,%d],\"Standard\":[%d,%d],\"Weekend\":\"%s\"}}"),
|
||||
XdrvMailbox.command,
|
||||
Settings.register8[R8_ENERGY_TARIFF1_ST], Settings.register8[R8_ENERGY_TARIFF1_DS],
|
||||
Settings.register8[R8_ENERGY_TARIFF2_ST], Settings.register8[R8_ENERGY_TARIFF2_DS],
|
||||
GetStateText(Settings.flag3.energy_weekend));
|
||||
}
|
||||
|
||||
void CmndPowerCal(void)
|
||||
|
@ -581,6 +615,16 @@ void CmndFrequencySet(void)
|
|||
}
|
||||
}
|
||||
|
||||
void CmndModuleAddress(void)
|
||||
{
|
||||
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4) && (1 == Energy.phase_count)) {
|
||||
Energy.command_code = CMND_MODULEADDRESS;
|
||||
if (XnrgCall(FUNC_COMMAND)) { // Module address
|
||||
ResponseCmndDone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_ENERGY_MARGIN_DETECTION
|
||||
void CmndPowerDelta(void)
|
||||
{
|
||||
|
@ -751,72 +795,129 @@ const char HTTP_ENERGY_SNS3[] PROGMEM =
|
|||
"{s}" D_EXPORT_ACTIVE "{m}%s " D_UNIT_KILOWATTHOUR "{e}";
|
||||
#endif // USE_WEBSERVER
|
||||
|
||||
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); // Dirty
|
||||
break;
|
||||
case 3:
|
||||
snprintf_P(result, FLOATSZ *3, layout, input, input + FLOATSZ, input + FLOATSZ + FLOATSZ); // Even dirtier
|
||||
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; // 1,2,3
|
||||
return EnergyFormatIndex(result, input, json, index, single);
|
||||
}
|
||||
|
||||
void EnergyShow(bool json)
|
||||
{
|
||||
float power_factor = Energy.power_factor;
|
||||
|
||||
char apparent_power_chr[FLOATSZ];
|
||||
char reactive_power_chr[FLOATSZ];
|
||||
char power_factor_chr[FLOATSZ];
|
||||
char frequency_chr[FLOATSZ];
|
||||
if (!Energy.type_dc) {
|
||||
if (Energy.current_available && Energy.voltage_available) {
|
||||
float apparent_power = Energy.apparent_power;
|
||||
if (isnan(apparent_power)) {
|
||||
apparent_power = Energy.voltage * Energy.current;
|
||||
}
|
||||
if (apparent_power < Energy.active_power) { // Should be impossible
|
||||
Energy.active_power = apparent_power;
|
||||
}
|
||||
|
||||
if (isnan(power_factor)) {
|
||||
power_factor = (Energy.active_power && apparent_power) ? Energy.active_power / apparent_power : 0;
|
||||
if (power_factor > 1) power_factor = 1;
|
||||
}
|
||||
|
||||
float reactive_power = Energy.reactive_power;
|
||||
if (isnan(reactive_power)) {
|
||||
reactive_power = 0;
|
||||
uint32_t difference = ((uint32_t)(apparent_power * 100) - (uint32_t)(Energy.active_power * 100)) / 10;
|
||||
if ((Energy.current > 0.005) && ((difference > 15) || (difference > (uint32_t)(apparent_power * 100 / 1000)))) {
|
||||
// calculating reactive power only if current is greater than 0.005A and
|
||||
// difference between active and apparent power is greater than 1.5W or 1%
|
||||
reactive_power = (float)(RoundSqrtInt((uint32_t)(apparent_power * apparent_power * 100) - (uint32_t)(Energy.active_power * Energy.active_power * 100))) / 10;
|
||||
}
|
||||
}
|
||||
|
||||
dtostrfd(apparent_power, Settings.flag2.wattage_resolution, apparent_power_chr);
|
||||
dtostrfd(reactive_power, Settings.flag2.wattage_resolution, reactive_power_chr);
|
||||
dtostrfd(power_factor, 2, power_factor_chr);
|
||||
}
|
||||
if (!isnan(Energy.frequency)) {
|
||||
dtostrfd(Energy.frequency, Settings.flag2.frequency_resolution, frequency_chr);
|
||||
for (uint32_t i = 0; i < Energy.phase_count; i++) {
|
||||
if (Energy.voltage_common) {
|
||||
Energy.voltage[i] = Energy.voltage[0];
|
||||
}
|
||||
}
|
||||
|
||||
char voltage_chr[FLOATSZ];
|
||||
dtostrfd(Energy.voltage, Settings.flag2.voltage_resolution, voltage_chr);
|
||||
char current_chr[FLOATSZ];
|
||||
dtostrfd(Energy.current, Settings.flag2.current_resolution, current_chr);
|
||||
char active_power_chr[FLOATSZ];
|
||||
dtostrfd(Energy.active_power, Settings.flag2.wattage_resolution, active_power_chr);
|
||||
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]) { // Should be impossible
|
||||
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)))) {
|
||||
// calculating reactive power only if current is greater than 0.005A and
|
||||
// difference between active and apparent power is greater than 1.5W or 1%
|
||||
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[FLOATSZ];
|
||||
dtostrfd(Energy.total, Settings.flag2.energy_resolution, energy_total_chr);
|
||||
char energy_total_chr[3][FLOATSZ];
|
||||
dtostrfd(Energy.total, Settings.flag2.energy_resolution, energy_total_chr[0]);
|
||||
uint8_t energy_total_fields = 1;
|
||||
if (Settings.register8[R8_ENERGY_TARIFF1_ST] != Settings.register8[R8_ENERGY_TARIFF2_ST]) {
|
||||
dtostrfd(Energy.total1, Settings.flag2.energy_resolution, energy_total_chr[1]); // Tariff1
|
||||
dtostrfd(Energy.total - Energy.total1, Settings.flag2.energy_resolution, energy_total_chr[2]); // Tariff2
|
||||
energy_total_fields = 3;
|
||||
}
|
||||
char export_active_chr[FLOATSZ];
|
||||
dtostrfd(Energy.export_active, Settings.flag2.energy_resolution, export_active_chr);
|
||||
|
||||
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(), energy_total_chr, energy_yesterday_chr, energy_daily_chr);
|
||||
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"), export_active_chr);
|
||||
}
|
||||
|
||||
if (show_energy_period) {
|
||||
float energy = 0;
|
||||
if (Energy.period) {
|
||||
|
@ -827,57 +928,64 @@ void EnergyShow(bool json)
|
|||
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"), active_power_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"),
|
||||
apparent_power_chr, reactive_power_chr, power_factor_chr);
|
||||
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)) {
|
||||
ResponseAppend_P(PSTR(",\"" D_JSON_FREQUENCY "\":%s"), frequency_chr);
|
||||
if (!isnan(Energy.frequency[0])) {
|
||||
ResponseAppend_P(PSTR(",\"" D_JSON_FREQUENCY "\":%s"),
|
||||
EnergyFormat(value_chr, frequency_chr[0], json));
|
||||
}
|
||||
}
|
||||
if (Energy.voltage_available) {
|
||||
ResponseAppend_P(PSTR(",\"" D_JSON_VOLTAGE "\":%s"), voltage_chr);
|
||||
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"), current_chr);
|
||||
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) { // Only send if telemetry
|
||||
dtostrfd(Energy.total * 1000, 1, energy_total_chr);
|
||||
DomoticzSensorPowerEnergy((int)Energy.active_power, energy_total_chr); // PowerUsage, EnergyToday
|
||||
dtostrfd(Energy.total * 1000, 1, energy_total_chr[0]);
|
||||
DomoticzSensorPowerEnergy((int)Energy.active_power[0], energy_total_chr[0]); // PowerUsage, EnergyToday
|
||||
|
||||
dtostrfd((Energy.total - Energy.total1) * 1000, 1, energy_total_chr); // Tariff2
|
||||
char energy_total1_chr[FLOATSZ];
|
||||
dtostrfd(Energy.total1 * 1000, 1, energy_total1_chr); // Tariff1
|
||||
dtostrfd(Energy.total1 * 1000, 1, energy_total_chr[1]); // Tariff1
|
||||
dtostrfd((Energy.total - Energy.total1) * 1000, 1, energy_total_chr[2]); // Tariff2
|
||||
char return1_total_chr[FLOATSZ];
|
||||
dtostrfd(RtcSettings.energy_usage.return1_kWhtotal, 1, return1_total_chr);
|
||||
char return2_total_chr[FLOATSZ];
|
||||
dtostrfd(RtcSettings.energy_usage.return2_kWhtotal, 1, return2_total_chr);
|
||||
DomoticzSensorP1SmartMeter(energy_total1_chr, energy_total_chr, return1_total_chr, return2_total_chr, (int)Energy.active_power);
|
||||
DomoticzSensorP1SmartMeter(energy_total_chr[1], energy_total_chr[2], return1_total_chr, return2_total_chr, (int)Energy.active_power[0]);
|
||||
|
||||
if (Energy.voltage_available) {
|
||||
DomoticzSensor(DZ_VOLTAGE, voltage_chr); // Voltage
|
||||
DomoticzSensor(DZ_VOLTAGE, voltage_chr[0]); // Voltage
|
||||
}
|
||||
if (Energy.current_available) {
|
||||
DomoticzSensor(DZ_CURRENT, current_chr); // Current
|
||||
DomoticzSensor(DZ_CURRENT, current_chr[0]); // Current
|
||||
}
|
||||
}
|
||||
#endif // USE_DOMOTICZ
|
||||
#ifdef USE_KNX
|
||||
if (show_energy_period) {
|
||||
if (Energy.voltage_available) {
|
||||
KnxSensor(KNX_ENERGY_VOLTAGE, Energy.voltage);
|
||||
KnxSensor(KNX_ENERGY_VOLTAGE, Energy.voltage[0]);
|
||||
}
|
||||
if (Energy.current_available) {
|
||||
KnxSensor(KNX_ENERGY_CURRENT, Energy.current);
|
||||
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_POWER, Energy.active_power);
|
||||
if (!Energy.type_dc) { KnxSensor(KNX_ENERGY_POWERFACTOR, power_factor); }
|
||||
KnxSensor(KNX_ENERGY_DAILY, Energy.daily);
|
||||
KnxSensor(KNX_ENERGY_TOTAL, Energy.total);
|
||||
KnxSensor(KNX_ENERGY_START, Energy.start_energy);
|
||||
|
@ -886,21 +994,27 @@ void EnergyShow(bool json)
|
|||
#ifdef USE_WEBSERVER
|
||||
} else {
|
||||
if (Energy.voltage_available) {
|
||||
WSContentSend_PD(PSTR("{s}" D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}"), voltage_chr);
|
||||
WSContentSend_PD(PSTR("{s}" D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}"),
|
||||
EnergyFormat(value_chr, voltage_chr[0], json, Energy.voltage_common));
|
||||
}
|
||||
if (Energy.current_available) {
|
||||
WSContentSend_PD(PSTR("{s}" D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}"), current_chr);
|
||||
WSContentSend_PD(PSTR("{s}" D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}"),
|
||||
EnergyFormat(value_chr, current_chr[0], json));
|
||||
}
|
||||
WSContentSend_PD(PSTR("{s}" D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"), active_power_chr);
|
||||
WSContentSend_PD(PSTR("{s}" D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"),
|
||||
EnergyFormat(value_chr, active_power_chr[0], json));
|
||||
if (!Energy.type_dc) {
|
||||
if (Energy.current_available && Energy.voltage_available) {
|
||||
WSContentSend_PD(HTTP_ENERGY_SNS1, apparent_power_chr, reactive_power_chr, power_factor_chr);
|
||||
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)) {
|
||||
WSContentSend_PD(PSTR("{s}" D_FREQUENCY "{m}%s " D_UNIT_HERTZ "{e}"), frequency_chr);
|
||||
if (!isnan(Energy.frequency[0])) {
|
||||
WSContentSend_PD(PSTR("{s}" D_FREQUENCY "{m}%s " D_UNIT_HERTZ "{e}"),
|
||||
EnergyFormat(value_chr, frequency_chr[0], json));
|
||||
}
|
||||
}
|
||||
WSContentSend_PD(HTTP_ENERGY_SNS2, energy_daily_chr, energy_yesterday_chr, energy_total_chr);
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -70,24 +70,6 @@ void IrSendInit(void)
|
|||
irsend->begin();
|
||||
}
|
||||
|
||||
char* IrUint64toHex(uint64_t value, char *str, uint16_t bits)
|
||||
{
|
||||
ulltoa(value, str, 16); // Get 64bit value
|
||||
|
||||
int fill = 8;
|
||||
if ((bits > 3) && (bits < 65)) {
|
||||
fill = bits / 4; // Max 16
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef USE_IR_RECEIVE
|
||||
/*********************************************************************************************\
|
||||
* IR Receive
|
||||
|
@ -129,13 +111,21 @@ void IrReceiveCheck(void)
|
|||
|
||||
if (irrecv->decode(&results)) {
|
||||
char hvalue[65]; // Max 256 bits
|
||||
if (results.bits > 64) {
|
||||
// This emulates IRutils resultToHexidecimal and may needs a larger IR_RCV_BUFFER_SIZE
|
||||
uint32_t digits2 = results.bits / 8;
|
||||
if (results.bits % 8) { digits2++; }
|
||||
ToHex_P((unsigned char*)results.state, digits2, hvalue, sizeof(hvalue)); // Get n-bit value as hex 56341200
|
||||
|
||||
iridx = results.decode_type;
|
||||
if ((iridx < 0) || (iridx > 14)) { iridx = 0; } // UNKNOWN
|
||||
|
||||
if (iridx) {
|
||||
if (results.bits > 64) {
|
||||
// This emulates IRutils resultToHexidecimal and may needs a larger IR_RCV_BUFFER_SIZE
|
||||
uint32_t digits2 = results.bits / 8;
|
||||
if (results.bits % 8) { digits2++; }
|
||||
ToHex_P((unsigned char*)results.state, digits2, hvalue, sizeof(hvalue)); // Get n-bit value as hex 56341200
|
||||
} else {
|
||||
Uint64toHex(results.value, hvalue, results.bits); // Get 64bit value as hex 00123456
|
||||
}
|
||||
} else {
|
||||
IrUint64toHex(results.value, hvalue, results.bits); // Get 64bit value as hex 00123456
|
||||
Uint64toHex(results.value, hvalue, 32); // UNKNOWN is always 32 bits hash
|
||||
}
|
||||
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_IRR "Echo %d, RawLen %d, Overflow %d, Bits %d, Value 0x%s, Decode %d"),
|
||||
|
@ -146,16 +136,19 @@ void IrReceiveCheck(void)
|
|||
if (!irsend_active && (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE)) {
|
||||
ir_lasttime = now;
|
||||
|
||||
iridx = results.decode_type;
|
||||
if ((iridx < 0) || (iridx > 14)) { iridx = 0; } // UNKNOWN
|
||||
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,\"" D_JSON_IR_DATA "\":%s"),
|
||||
GetTextIndexed(sirtype, sizeof(sirtype), iridx, kIrRemoteProtocols), results.bits, svalue);
|
||||
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 "\":["));
|
||||
|
@ -178,7 +171,7 @@ void IrReceiveCheck(void)
|
|||
ResponseAppend_P(PSTR("],\"" D_JSON_IR_RAWDATA "Info\":[%d,%d,%d]"), extended_length, i -1, results.overflow);
|
||||
}
|
||||
|
||||
ResponseAppend_P(PSTR("}}"));
|
||||
ResponseJsonEndEnd();
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED));
|
||||
|
||||
if (iridx) {
|
||||
|
@ -927,7 +920,7 @@ uint32_t IrRemoteCmndIrSendJson(void)
|
|||
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), IrUint64toHex(data, hvalue, bits), repeat, protocol_code);
|
||||
protocol_text, protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat, protocol_code);
|
||||
|
||||
irsend_active = true;
|
||||
switch (protocol_code) { // Equals IRremoteESP8266.h enum decode_type_t
|
||||
|
|
|
@ -72,27 +72,6 @@ uint64_t reverseBitsInBytes64(uint64_t b) {
|
|||
return a.i;
|
||||
}
|
||||
|
||||
char* IrUint64toHex(uint64_t value, char *str, uint16_t bits)
|
||||
{
|
||||
ulltoa(value, str, 16); // Get 64bit value
|
||||
|
||||
int fill = 8;
|
||||
if ((bits > 3) && (bits < 65)) {
|
||||
fill = bits / 4; // Max 16
|
||||
if (bits % 4) { fill++; }
|
||||
}
|
||||
int len = strlen(str);
|
||||
fill -= len;
|
||||
if (fill > 0) {
|
||||
memmove(str + fill, str, len +1);
|
||||
memset(str, '0', fill);
|
||||
}
|
||||
memmove(str + 2, str, strlen(str) +1);
|
||||
str[0] = '0';
|
||||
str[1] = 'x';
|
||||
return str;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* IR Receive
|
||||
\*********************************************************************************************/
|
||||
|
@ -177,20 +156,31 @@ String sendIRJsonState(const struct decode_results &results) {
|
|||
json += resultToHexidecimal(&results);
|
||||
json += "\"";
|
||||
} else {
|
||||
json += ",\"" D_JSON_IR_DATA "\":";
|
||||
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];
|
||||
IrUint64toHex(results.value, hvalue, results.bits); // Get 64bit value as hex 0x00123456
|
||||
json += "\"";
|
||||
json += hvalue;
|
||||
json += "\",\"" D_JSON_IR_DATALSB "\":\"";
|
||||
IrUint64toHex(reverseBitsInBytes64(results.value), hvalue, results.bits); // Get 64bit value as hex 0x00123456, LSB
|
||||
json += hvalue;
|
||||
json += "\"";
|
||||
if (UNKNOWN != results.decode_type) {
|
||||
Uint64toHex(results.value, hvalue, results.bits); // Get 64bit value as hex 0x00123456
|
||||
json += "\"";
|
||||
json += hvalue;
|
||||
json += "\",\"" D_JSON_IR_DATALSB "\":\"";
|
||||
Uint64toHex(reverseBitsInBytes64(results.value), hvalue, results.bits); // Get 64bit value as hex 0x00123456, LSB
|
||||
json += hvalue;
|
||||
json += "\"";
|
||||
} else { // UNKNOWN
|
||||
Uint64toHex(results.value, hvalue, 32); // Unknown is always 32 bits
|
||||
json += "\"";
|
||||
json += hvalue;
|
||||
json += "\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
json += ",\"" D_JSON_IR_REPEAT "\":";
|
||||
|
@ -243,7 +233,7 @@ void IrReceiveCheck(void)
|
|||
ResponseAppend_P(PSTR("],\"" D_JSON_IR_RAWDATA "Info\":[%d,%d,%d]"), extended_length, i -1, results.overflow);
|
||||
}
|
||||
|
||||
ResponseAppend_P(PSTR("}}"));
|
||||
ResponseJsonEndEnd();
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED));
|
||||
|
||||
if (iridx) {
|
||||
|
@ -449,7 +439,7 @@ uint32_t IrRemoteCmndIrSendJson(void)
|
|||
char dvalue[32];
|
||||
char hvalue[32];
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol %d, bits %d, data %s (%s), repeat %d"),
|
||||
protocol, bits, ulltoa(data, dvalue, 10), IrUint64toHex(data, hvalue, bits), repeat);
|
||||
protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat);
|
||||
|
||||
irsend_active = true; // deactivate receive
|
||||
bool success = irsend->send(protocol, data, bits, repeat);
|
||||
|
|
|
@ -482,7 +482,7 @@ void CmndTimers(void)
|
|||
jsflg++;
|
||||
PrepShowTimer(i +1);
|
||||
if (jsflg > 3) {
|
||||
ResponseAppend_P(PSTR("}}"));
|
||||
ResponseJsonEndEnd();
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_TIMERS));
|
||||
jsflg = 0;
|
||||
}
|
||||
|
|
|
@ -339,6 +339,7 @@ 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));
|
||||
|
@ -684,7 +685,7 @@ void RulesTeleperiod(void)
|
|||
bool RulesMqttData(void)
|
||||
{
|
||||
bool serviced = false;
|
||||
if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > 128) {
|
||||
if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > 256) {
|
||||
return false;
|
||||
}
|
||||
String sTopic = XdrvMailbox.topic;
|
||||
|
@ -703,7 +704,7 @@ bool RulesMqttData(void)
|
|||
if (event_item.Key.length() == 0) { //If did not specify Key
|
||||
value = sData;
|
||||
} else { //If specified Key, need to parse Key/Value from JSON data
|
||||
StaticJsonBuffer<400> jsonBuf;
|
||||
StaticJsonBuffer<500> jsonBuf;
|
||||
JsonObject& jsonData = jsonBuf.parseObject(sData);
|
||||
String key1 = event_item.Key;
|
||||
String key2;
|
||||
|
@ -1185,6 +1186,7 @@ void CmndIf()
|
|||
parameters[XdrvMailbox.data_len] = '\0';
|
||||
ProcessIfStatement(parameters);
|
||||
}
|
||||
ResponseCmndDone();
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
|
|
@ -2045,6 +2045,8 @@ void Replace_Cmd_Vars(char *srcbuf,char *dstbuf,uint16_t dstsize) {
|
|||
if (isdigit(*cp)) {
|
||||
dprec=*cp&0xf;
|
||||
cp++;
|
||||
} else {
|
||||
dprec=glob_script_mem.script_dprec;
|
||||
}
|
||||
cp=isvar(cp,&vtype,&ind,&fvar,string,0);
|
||||
if (vtype!=VAR_NV) {
|
||||
|
@ -3204,6 +3206,7 @@ void HandleScriptTextareaConfiguration(void) {
|
|||
}
|
||||
|
||||
void HandleScriptConfiguration(void) {
|
||||
|
||||
if (!HttpCheckPriviledgedAccess()) { return; }
|
||||
|
||||
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_SCRIPT);
|
||||
|
@ -3343,7 +3346,6 @@ bool ScriptCommand(void) {
|
|||
if (XdrvMailbox.data[count]==';') XdrvMailbox.data[count]='\n';
|
||||
}
|
||||
execute_script(XdrvMailbox.data);
|
||||
Scripter_save_pvars();
|
||||
}
|
||||
}
|
||||
return serviced;
|
||||
|
@ -3578,6 +3580,70 @@ String ScriptUnsubscribe(const char * data, int data_len)
|
|||
#endif // SUPPORT_MQTT_EVENT
|
||||
|
||||
#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) {
|
||||
// string type must insert quotes
|
||||
uint8_t tlen=strlen(cp1);
|
||||
memmove(cp1+1,cp1,tlen);
|
||||
*cp1='\"';
|
||||
*(cp1+tlen+1)='\"';
|
||||
}
|
||||
|
||||
//toLog(cmdbuf);
|
||||
execute_script(cmdbuf);
|
||||
}
|
||||
}
|
||||
|
||||
const char SCRIPT_MSG_SLIDER[] PROGMEM =
|
||||
"<div><span class='p'>%s</span><center><b>%s</b><span class='q'>%s</span></div>"
|
||||
"<div><input type='range' min='%d' max='%d' value='%d' onchange='seva(value,\"%s\")'></div>";
|
||||
|
||||
const char SCRIPT_MSG_BUTTON[] PROGMEM =
|
||||
"<div><button type='submit' onclick='seva(%d,\"%s\")'>%s</button></div>";
|
||||
|
||||
const char SCRIPT_MSG_CHKBOX[] PROGMEM =
|
||||
"<div><center><label><b>%s</b><input type='checkbox' %s onchange='seva(%d,\"%s\")'></label></div>";
|
||||
|
||||
const char SCRIPT_MSG_TEXTINP[] PROGMEM =
|
||||
"<div><center><label><b>%s</b><input type='text' value='%s' style='width:200px' onchange='seva(value,\"%s\")'></label></div>";
|
||||
|
||||
//<input onkeypress="if(event.key == 'Enter') {console.log('Test')}">
|
||||
//<input onBlur="if (this.value == '') { var field = this; setTimeout(function() { field.focus(); }, 0); }" type="text">
|
||||
|
||||
void ScriptGetVarname(char *nbuf,char *sp, uint32_t blen) {
|
||||
uint32_t cnt;
|
||||
for (cnt=0;cnt<blen-1;cnt++) {
|
||||
if (*sp==' ' || *sp==')') {
|
||||
break;
|
||||
}
|
||||
nbuf[cnt]=*sp++;
|
||||
}
|
||||
nbuf[cnt]=0;
|
||||
}
|
||||
|
||||
void ScriptWebShow(void) {
|
||||
uint8_t web_script=Run_Scripter(">W",-2,0);
|
||||
if (web_script==99) {
|
||||
|
@ -3603,8 +3669,112 @@ void ScriptWebShow(void) {
|
|||
}
|
||||
cp++;
|
||||
}
|
||||
Replace_Cmd_Vars(line,tmp,sizeof(tmp));
|
||||
WSContentSend_PD(PSTR("{s}%s{e}"),tmp);
|
||||
// check for input elements
|
||||
if (!strncmp(line,"sl(",3)) {
|
||||
// insert slider sl(min max var left mid right)
|
||||
char *lp=line;
|
||||
float min;
|
||||
lp=GetNumericResult(lp+3,OPER_EQU,&min,0);
|
||||
SCRIPT_SKIP_SPACES
|
||||
// arg2
|
||||
float max;
|
||||
lp=GetNumericResult(lp,OPER_EQU,&max,0);
|
||||
SCRIPT_SKIP_SPACES
|
||||
float val;
|
||||
char *slp=lp;
|
||||
lp=GetNumericResult(lp,OPER_EQU,&val,0);
|
||||
SCRIPT_SKIP_SPACES
|
||||
|
||||
char vname[16];
|
||||
ScriptGetVarname(vname,slp,sizeof(vname));
|
||||
|
||||
char left[SCRIPT_MAXSSIZE];
|
||||
lp=GetStringResult(lp,OPER_EQU,left,0);
|
||||
SCRIPT_SKIP_SPACES
|
||||
char mid[SCRIPT_MAXSSIZE];
|
||||
lp=GetStringResult(lp,OPER_EQU,mid,0);
|
||||
SCRIPT_SKIP_SPACES
|
||||
char right[SCRIPT_MAXSSIZE];
|
||||
lp=GetStringResult(lp,OPER_EQU,right,0);
|
||||
SCRIPT_SKIP_SPACES
|
||||
|
||||
WSContentSend_PD(SCRIPT_MSG_SLIDER,left,mid,right,(uint32_t)min,(uint32_t)max,(uint32_t)val,vname);
|
||||
|
||||
|
||||
} else if (!strncmp(line,"ck(",3)) {
|
||||
char *lp=line+3;
|
||||
char *slp=lp;
|
||||
float val;
|
||||
lp=GetNumericResult(lp,OPER_EQU,&val,0);
|
||||
SCRIPT_SKIP_SPACES
|
||||
|
||||
char vname[16];
|
||||
ScriptGetVarname(vname,slp,sizeof(vname));
|
||||
|
||||
char label[SCRIPT_MAXSSIZE];
|
||||
lp=GetStringResult(lp,OPER_EQU,label,0);
|
||||
char *cp;
|
||||
uint8_t uval;
|
||||
if (val>0) {
|
||||
cp="checked='checked'";
|
||||
uval=0;
|
||||
} else {
|
||||
cp="";
|
||||
uval=1;
|
||||
}
|
||||
WSContentSend_PD(SCRIPT_MSG_CHKBOX,label,cp,uval,vname);
|
||||
|
||||
} else if (!strncmp(line,"bu(",3)) {
|
||||
char *lp=line+3;
|
||||
char *slp=lp;
|
||||
float val;
|
||||
lp=GetNumericResult(lp,OPER_EQU,&val,0);
|
||||
SCRIPT_SKIP_SPACES
|
||||
|
||||
char vname[16];
|
||||
ScriptGetVarname(vname,slp,sizeof(vname));
|
||||
|
||||
SCRIPT_SKIP_SPACES
|
||||
char ontxt[SCRIPT_MAXSSIZE];
|
||||
lp=GetStringResult(lp,OPER_EQU,ontxt,0);
|
||||
SCRIPT_SKIP_SPACES
|
||||
char offtxt[SCRIPT_MAXSSIZE];
|
||||
lp=GetStringResult(lp,OPER_EQU,offtxt,0);
|
||||
|
||||
char *cp;
|
||||
uint8_t uval;
|
||||
if (val>0) {
|
||||
cp=ontxt;
|
||||
uval=0;
|
||||
} else {
|
||||
cp=offtxt;
|
||||
uval=1;
|
||||
}
|
||||
WSContentSend_PD(SCRIPT_MSG_BUTTON,uval,vname,cp);
|
||||
|
||||
} else if (!strncmp(line,"tx(",3)) {
|
||||
char *lp=line+3;
|
||||
char *slp=lp;
|
||||
char str[SCRIPT_MAXSSIZE];
|
||||
lp=ForceStringVar(lp,str);
|
||||
SCRIPT_SKIP_SPACES
|
||||
char label[SCRIPT_MAXSSIZE];
|
||||
lp=GetStringResult(lp,OPER_EQU,label,0);
|
||||
|
||||
char vname[16];
|
||||
ScriptGetVarname(vname,slp,sizeof(vname));
|
||||
|
||||
WSContentSend_PD(SCRIPT_MSG_TEXTINP,label,str,vname);
|
||||
|
||||
}
|
||||
else {
|
||||
Replace_Cmd_Vars(line,tmp,sizeof(tmp));
|
||||
if (tmp[0]=='@') {
|
||||
WSContentSend_PD(PSTR("<div>%s</div>"),&tmp[1]);
|
||||
} else {
|
||||
WSContentSend_PD(PSTR("{s}%s{e}"),tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (*lp==SCRIPT_EOL) {
|
||||
lp++;
|
||||
|
@ -3735,6 +3905,7 @@ bool Xdrv10(uint8_t function)
|
|||
// assure permanent memory is 4 byte aligned
|
||||
{ uint32_t ptr=(uint32_t)glob_script_mem.script_pram;
|
||||
ptr&=0xfffffffc;
|
||||
ptr+=4;
|
||||
glob_script_mem.script_pram=(uint8_t*)ptr;
|
||||
glob_script_mem.script_pram_size-=4;
|
||||
}
|
||||
|
@ -3767,6 +3938,7 @@ bool Xdrv10(uint8_t function)
|
|||
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);
|
||||
|
|
|
@ -88,7 +88,8 @@ const char HASS_DISCOVER_SENSOR[] PROGMEM =
|
|||
|
||||
const char HASS_DISCOVER_SENSOR_TEMP[] PROGMEM =
|
||||
",\"unit_of_meas\":\"°%c\"," // °C / °F
|
||||
"\"val_tpl\":\"{{value_json['%s'].Temperature}}\""; // "SI7021-14":{"Temperature":null,"Humidity":null} -> {{ value_json['SI7021-14'].Temperature }}
|
||||
"\"val_tpl\":\"{{value_json['%s'].Temperature}}\"," // "SI7021-14":{"Temperature":null,"Humidity":null} -> {{ value_json['SI7021-14'].Temperature }}
|
||||
"\"dev_cla\":\"temperature\""; // temperature
|
||||
|
||||
const char HASS_DISCOVER_SENSOR_HUM[] PROGMEM =
|
||||
",\"unit_of_meas\":\"%%\"," // %
|
||||
|
@ -103,19 +104,27 @@ const char HASS_DISCOVER_SENSOR_PRESS[] PROGMEM =
|
|||
//ENERGY
|
||||
const char HASS_DISCOVER_SENSOR_KWH[] PROGMEM =
|
||||
",\"unit_of_meas\":\"kWh\"," // kWh
|
||||
"\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Total/Yesterday/Today }}
|
||||
"\"val_tpl\":\"{{value_json['%s'].%s}}\"," // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Total/Yesterday/Today }}
|
||||
"\"dev_cla\":\"power\""; // power
|
||||
|
||||
const char HASS_DISCOVER_SENSOR_WATT[] PROGMEM =
|
||||
",\"unit_of_meas\":\"W\"," // W
|
||||
"\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].POWER }}
|
||||
|
||||
"\"val_tpl\":\"{{value_json['%s'].%s}}\"," // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].POWER }}
|
||||
"\"dev_cla\":\"power\"";
|
||||
const char HASS_DISCOVER_SENSOR_VOLTAGE[] PROGMEM =
|
||||
",\"unit_of_meas\":\"V\"," // V
|
||||
"\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Voltage }}
|
||||
|
||||
"\"val_tpl\":\"{{value_json['%s'].%s}}\"," // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Voltage }}
|
||||
"\"dev_cla\":\"power\"";
|
||||
const char HASS_DISCOVER_SENSOR_AMPERE[] PROGMEM =
|
||||
",\"unit_of_meas\":\"A\"," // A
|
||||
"\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Current }}
|
||||
"\"val_tpl\":\"{{value_json['%s'].%s}}\"," // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Current }}
|
||||
"\"dev_cla\":\"power\"";
|
||||
|
||||
//ILLUMINANCE
|
||||
const char HASS_DISCOVER_SENSOR_ILLUMINANCE[] PROGMEM =
|
||||
",\"unit_of_meas\":\"LX\"," // LX by default
|
||||
"\"val_tpl\":\"{{value_json['%s'].Illuminance}}\"," // "ANALOG":{"Illuminance":34}}
|
||||
"\"dev_cla\":\"illuminance\""; // illuminance
|
||||
|
||||
const char HASS_DISCOVER_SENSOR_ANY[] PROGMEM =
|
||||
",\"unit_of_meas\":\" \"," // " " As unit of measurement to get a value graph in Hass
|
||||
|
@ -124,7 +133,7 @@ const char HASS_DISCOVER_SENSOR_ANY[] PROGMEM =
|
|||
const char HASS_DISCOVER_SENSOR_HASS_STATUS[] PROGMEM =
|
||||
",\"json_attributes_topic\":\"%s\","
|
||||
"\"unit_of_meas\":\" \"," // " " As unit of measurement to get a value graph in Hass
|
||||
"\"val_tpl\":\"{{value_json['" D_JSON_RSSI "']}}\"";// "COUNTER":{"C1":0} -> {{ value_json['COUNTER'].C1 }}
|
||||
"\"val_tpl\":\"{{value_json['" D_JSON_RSSI "']}}\""; // "COUNTER":{"C1":0} -> {{ value_json['COUNTER'].C1 }}
|
||||
|
||||
const char HASS_DISCOVER_DEVICE_INFO[] PROGMEM =
|
||||
",\"uniq_id\":\"%s\","
|
||||
|
@ -438,6 +447,8 @@ void HAssAnnounceSensor(const char* sensorname, const char* subsensortype)
|
|||
TryResponseAppend_P(HASS_DISCOVER_SENSOR_VOLTAGE, sensorname, subsensortype);
|
||||
} else if (!strcmp_P(subsensortype, PSTR(D_JSON_CURRENT))){
|
||||
TryResponseAppend_P(HASS_DISCOVER_SENSOR_AMPERE, sensorname, subsensortype);
|
||||
} else if (!strcmp_P(subsensortype, PSTR(D_JSON_ILLUMINANCE))){
|
||||
TryResponseAppend_P(HASS_DISCOVER_SENSOR_ILLUMINANCE, sensorname, subsensortype);
|
||||
}
|
||||
else {
|
||||
TryResponseAppend_P(HASS_DISCOVER_SENSOR_ANY, sensorname, subsensortype);
|
||||
|
|
|
@ -386,17 +386,17 @@ void TuyaPacketProcess(void)
|
|||
|
||||
#ifdef USE_ENERGY_SENSOR
|
||||
else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_VOLTAGE) {
|
||||
Energy.voltage = (float)(Tuya.buffer[12] << 8 | Tuya.buffer[13]) / 10;
|
||||
Energy.voltage[0] = (float)(Tuya.buffer[12] << 8 | Tuya.buffer[13]) / 10;
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Voltage=%d"), Tuya.buffer[6], (Tuya.buffer[12] << 8 | Tuya.buffer[13]));
|
||||
} else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_CURRENT) {
|
||||
Energy.current = (float)(Tuya.buffer[12] << 8 | Tuya.buffer[13]) / 1000;
|
||||
Energy.current[0] = (float)(Tuya.buffer[12] << 8 | Tuya.buffer[13]) / 1000;
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Current=%d"), Tuya.buffer[6], (Tuya.buffer[12] << 8 | Tuya.buffer[13]));
|
||||
} else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_POWER) {
|
||||
Energy.active_power = (float)(Tuya.buffer[12] << 8 | Tuya.buffer[13]) / 10;
|
||||
Energy.active_power[0] = (float)(Tuya.buffer[12] << 8 | Tuya.buffer[13]) / 10;
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Active_Power=%d"), Tuya.buffer[6], (Tuya.buffer[12] << 8 | Tuya.buffer[13]));
|
||||
|
||||
if (Tuya.lastPowerCheckTime != 0 && Energy.active_power > 0) {
|
||||
Energy.kWhtoday += (float)Energy.active_power * (Rtc.utc_time - Tuya.lastPowerCheckTime) / 36;
|
||||
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;
|
||||
|
|
|
@ -404,4 +404,25 @@ enum Z_Util {
|
|||
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
|
||||
|
||||
};
|
||||
|
||||
enum class ZclGlobalCommandId : uint8_t {
|
||||
};
|
||||
|
||||
#endif // USE_ZIGBEE
|
|
@ -0,0 +1,619 @@
|
|||
/*
|
||||
xdrv_23_zigbee_converters.ino - zigbee support for Sonoff-Tasmota
|
||||
|
||||
Copyright (C) 2019 Theo Arends and Stephan Hadinger
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_ZIGBEE
|
||||
|
||||
/*********************************************************************************************\
|
||||
* ZCL
|
||||
\*********************************************************************************************/
|
||||
|
||||
typedef union ZCLHeaderFrameControl_t {
|
||||
struct {
|
||||
uint8_t frame_type : 2; // 00 = across entire profile, 01 = cluster specific
|
||||
uint8_t manuf_specific : 1; // Manufacturer Specific Sub-field
|
||||
uint8_t direction : 1; // 0 = tasmota to zigbee, 1 = zigbee to tasmota
|
||||
uint8_t disable_def_resp : 1; // don't send back default response
|
||||
uint8_t reserved : 3;
|
||||
} b;
|
||||
uint32_t d8; // raw 8 bits field
|
||||
} 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 = 0, uint16_t groupid = 0):
|
||||
_cmd_id(cmd_id), _manuf_code(manuf_code), _transact_seq(transact_seq),
|
||||
_payload(buf_len ? buf_len : 250), // allocate the data frame from source or preallocate big enough
|
||||
_cluster_id(clusterid), _group_id(groupid)
|
||||
{
|
||||
_frame_control.d8 = frame_control;
|
||||
_payload.addBuffer(buf, buf_len);
|
||||
};
|
||||
|
||||
|
||||
void publishMQTTReceived(uint16_t groupid, uint16_t clusterid, Z_ShortAddress srcaddr,
|
||||
uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast,
|
||||
uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber,
|
||||
uint32_t timestamp) {
|
||||
char hex_char[_payload.len()*2+2];
|
||||
ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char));
|
||||
Response_P(PSTR("{\"" D_JSON_ZIGBEEZCLRECEIVED "\":{"
|
||||
"\"groupid\":%d," "\"clusterid\":%d," "\"srcaddr\":\"0x%04X\","
|
||||
"\"srcendpoint\":%d," "\"dstendpoint\":%d," "\"wasbroadcast\":%d,"
|
||||
"\"linkquality\":%d," "\"securityuse\":%d," "\"seqnumber\":%d,"
|
||||
"\"timestamp\":%d,"
|
||||
"\"fc\":\"0x%02X\",\"manuf\":\"0x%04X\",\"transact\":%d,"
|
||||
"\"cmdid\":\"0x%02X\",\"payload\":\"%s\""),
|
||||
groupid, clusterid, srcaddr,
|
||||
srcendpoint, dstendpoint, wasbroadcast,
|
||||
linkquality, securityuse, seqnumber,
|
||||
timestamp,
|
||||
_frame_control, _manuf_code, _transact_seq, _cmd_id,
|
||||
hex_char);
|
||||
|
||||
ResponseJsonEnd(); // append '}'
|
||||
ResponseJsonEnd(); // append '}'
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCLSENT));
|
||||
XdrvRulesProcess();
|
||||
}
|
||||
|
||||
static ZCLFrame parseRawFrame(SBuffer &buf, uint8_t offset, uint8_t len, uint16_t clusterid, uint16_t groupid) { // parse a raw frame and build the ZCL frame object
|
||||
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);
|
||||
return zcl_frame;
|
||||
}
|
||||
|
||||
bool isClusterSpecificCommand(void) {
|
||||
return _frame_control.b.frame_type & 1;
|
||||
}
|
||||
|
||||
void parseRawAttributes(JsonObject& json, uint8_t offset = 0);
|
||||
void parseClusterSpecificCommand(JsonObject& json, uint8_t offset = 0);
|
||||
void postProcessAttributes(JsonObject& json);
|
||||
|
||||
inline void setGroupId(uint16_t groupid) {
|
||||
_group_id = 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;
|
||||
}
|
||||
|
||||
const SBuffer &getPayload(void) const {
|
||||
return _payload;
|
||||
}
|
||||
|
||||
private:
|
||||
ZCLHeaderFrameControl_t _frame_control = { .d8 = 0 };
|
||||
uint16_t _manuf_code = 0; // optional
|
||||
uint8_t _transact_seq = 0; // transaction sequence number
|
||||
uint8_t _cmd_id = 0;
|
||||
uint16_t _cluster_id = 0;
|
||||
uint16_t _group_id = 0;
|
||||
SBuffer _payload;
|
||||
};
|
||||
|
||||
|
||||
// Zigbee ZCL converters
|
||||
|
||||
// from https://github.com/Koenkk/zigbee-shepherd-converters/blob/638d29f0cace6343052b9a4e7fd60980fa785479/converters/fromZigbee.js#L55
|
||||
// Input voltage in mV, i.e. 3000 = 3.000V
|
||||
// Output percentage from 0 to 100 as int
|
||||
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 len) {
|
||||
|
||||
uint32_t i = offset;
|
||||
uint32_t attrtype = buf.get8(i++);
|
||||
|
||||
// fallback - enter a null value
|
||||
json[attrid_str] = (char*) nullptr;
|
||||
|
||||
// now parse accordingly to attr type
|
||||
switch (attrtype) {
|
||||
case 0x00: // nodata
|
||||
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
|
||||
{
|
||||
uint8_t uint8_val = buf.get8(i);
|
||||
i += 1;
|
||||
if (0xFF != uint8_val) {
|
||||
json[attrid_str] = uint8_val;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x21: // uint16
|
||||
{
|
||||
uint16_t uint16_val = buf.get16(i);
|
||||
i += 2;
|
||||
if (0xFFFF != uint16_val) {
|
||||
json[attrid_str] = uint16_val;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x23: // uint16
|
||||
{
|
||||
uint32_t uint32_val = buf.get32(i);
|
||||
i += 4;
|
||||
if (0xFFFFFFFF != uint32_val) {
|
||||
json[attrid_str] = uint32_val;
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Note: uint40, uint48, uint56, uint64 are not used in ZCL, so they are not implemented (yet)
|
||||
case 0x24: // int40
|
||||
case 0x25: // int48
|
||||
case 0x26: // int56
|
||||
case 0x27: // int64
|
||||
i += attrtype - 0x1F; // 5 - 8;
|
||||
break;
|
||||
case 0x28: // uint8
|
||||
{
|
||||
int8_t int8_val = buf.get8(i);
|
||||
i += 1;
|
||||
if (0x80 != int8_val) {
|
||||
json[attrid_str] = int8_val;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x29: // uint16
|
||||
{
|
||||
int16_t int16_val = buf.get16(i);
|
||||
i += 2;
|
||||
if (0x8000 != int16_val) {
|
||||
json[attrid_str] = int16_val;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x2B: // uint16
|
||||
{
|
||||
int32_t int32_val = buf.get32(i);
|
||||
i += 4;
|
||||
if (0x80000000 != int32_val) {
|
||||
json[attrid_str] = int32_val;
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Note: int40, int48, int56, int64 are not used in ZCL, so they are not implemented (yet)
|
||||
case 0x2C: // int40
|
||||
case 0x2D: // int48
|
||||
case 0x2E: // int56
|
||||
case 0x2F: // int64
|
||||
i += attrtype - 0x27; // 5 - 8;
|
||||
break;
|
||||
|
||||
case 0x41: // octet string, 1 byte len
|
||||
case 0x42: // char string, 1 byte len
|
||||
case 0x43: // octet string, 2 bytes len
|
||||
case 0x44: // char string, 2 bytes len
|
||||
// For strings, default is to try to do a real string, but reverts to octet stream if null char is present or on some exceptions
|
||||
{
|
||||
bool parse_as_string = true;
|
||||
uint32_t len = (attrtype <= 0x42) ? buf.get8(i) : buf.get16(i); // len is 8 or 16 bits
|
||||
i += (attrtype <= 0x42) ? 1 : 2; // increment pointer
|
||||
|
||||
// check if we can safely use a string
|
||||
if ((0x41 == attrtype) || (0x43 == attrtype)) { parse_as_string = false; }
|
||||
else {
|
||||
for (uint32_t j = 0; j < len; j++) {
|
||||
if (0x00 == buf.get8(i+j)) {
|
||||
parse_as_string = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parse_as_string) {
|
||||
char str[len+1];
|
||||
strncpy(str, buf.charptr(i), len);
|
||||
str[len] = 0x00;
|
||||
json[attrid_str] = str;
|
||||
} else {
|
||||
// print as HEX
|
||||
char hex[2*len+1];
|
||||
ToHex_P(buf.buf(i), len, hex, sizeof(hex));
|
||||
json[attrid_str] = hex;
|
||||
}
|
||||
|
||||
i += len;
|
||||
break;
|
||||
}
|
||||
i += buf.get8(i) + 1;
|
||||
break;
|
||||
|
||||
|
||||
// TODO
|
||||
case 0x08: // data8
|
||||
i++;
|
||||
break;
|
||||
case 0x18: // map8
|
||||
i++;
|
||||
break;
|
||||
case 0x19: // map16
|
||||
i += 2;
|
||||
break;
|
||||
case 0x1B: // map32
|
||||
i += 4;
|
||||
break;
|
||||
// enum
|
||||
case 0x30: // enum8
|
||||
case 0x31: // enum16
|
||||
i += attrtype - 0x2F;
|
||||
break;
|
||||
|
||||
case 0x39: // float
|
||||
i += 4;
|
||||
break;
|
||||
|
||||
case 0xE0: // ToD
|
||||
case 0xE1: // date
|
||||
case 0xE2: // UTC
|
||||
i += 4;
|
||||
break;
|
||||
|
||||
case 0xE8: // clusterId
|
||||
case 0xE9: // attribId
|
||||
i += 2;
|
||||
break;
|
||||
case 0xEA: // bacOID
|
||||
i += 4;
|
||||
break;
|
||||
|
||||
case 0xF0: // EUI64
|
||||
i += 8;
|
||||
break;
|
||||
case 0xF1: // key128
|
||||
i += 16;
|
||||
break;
|
||||
|
||||
// Other un-implemented data types
|
||||
case 0x09: // data16
|
||||
case 0x0A: // data24
|
||||
case 0x0B: // data32
|
||||
case 0x0C: // data40
|
||||
case 0x0D: // data48
|
||||
case 0x0E: // data56
|
||||
case 0x0F: // data64
|
||||
i += attrtype - 0x07; // 2-8
|
||||
break;
|
||||
// map<x>
|
||||
case 0x1A: // map24
|
||||
case 0x1C: // map40
|
||||
case 0x1D: // map48
|
||||
case 0x1E: // map56
|
||||
case 0x1F: // map64
|
||||
i += attrtype - 0x17;
|
||||
break;
|
||||
// semi
|
||||
case 0x38: // semi (float on 2 bytes)
|
||||
i += 2;
|
||||
break;
|
||||
case 0x3A: // double precision
|
||||
i += 8;
|
||||
break;
|
||||
}
|
||||
|
||||
// String pp; // pretty print
|
||||
// json[attrid_str].prettyPrintTo(pp);
|
||||
// // now store the attribute
|
||||
// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: ZCL attribute decoded, id %s, type 0x%02X, val=%s"),
|
||||
// attrid_str, attrtype, pp.c_str());
|
||||
return i - offset; // how much have we increased the index
|
||||
}
|
||||
|
||||
|
||||
// First pass, parse all attributes in their native format
|
||||
// The key is 32 bits, high 16 bits is cluserid, low 16 bits is attribute id
|
||||
void ZCLFrame::parseRawAttributes(JsonObject& json, uint8_t offset) {
|
||||
uint32_t i = offset;
|
||||
uint32_t len = _payload.len();
|
||||
uint32_t attrid = _cluster_id << 16; // set high 16 bits with cluster id
|
||||
|
||||
while (len + offset - i >= 3) {
|
||||
attrid = (attrid & 0xFFFF0000) | _payload.get16(i); // get lower 16 bits
|
||||
i += 2;
|
||||
|
||||
char shortaddr[12];
|
||||
snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%08X"), attrid);
|
||||
|
||||
// exception for Xiaomi lumi.weather - specific field to be treated as octet and not char
|
||||
if (0x0000FF01 == attrid) {
|
||||
if (0x42 == _payload.get8(i)) {
|
||||
_payload.set8(i, 0x41); // change type from 0x42 to 0x41
|
||||
}
|
||||
}
|
||||
i += parseSingleAttribute(json, shortaddr, _payload, i, len);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse non-normalized attributes
|
||||
// The key is 24 bits, high 16 bits is cluserid, low 8 bits is command id
|
||||
void ZCLFrame::parseClusterSpecificCommand(JsonObject& json, uint8_t offset) {
|
||||
uint32_t i = offset;
|
||||
uint32_t len = _payload.len();
|
||||
uint32_t attrid = _cluster_id << 8 | _cmd_id;
|
||||
|
||||
char attrid_str[12];
|
||||
snprintf_P(attrid_str, sizeof(attrid_str), PSTR("0x%06X"), attrid); // 24 bits
|
||||
|
||||
char hex_char[_payload.len()*2+2];
|
||||
ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char));
|
||||
|
||||
json[attrid_str] = hex_char;
|
||||
}
|
||||
|
||||
#define ZCL_MODELID "0x00000005" // Cluster 0x0000, attribute 0x05
|
||||
#define ZCL_TEMPERATURE "0x04020000" // Cluster 0x0402, attribute 0x00
|
||||
#define ZCL_PRESSURE "0x04030000" // Cluster 0x0403, attribute 0x00
|
||||
#define ZCL_PRESSURE_SCALED "0x04030010" // Cluster 0x0403, attribute 0x10
|
||||
#define ZCL_PRESSURE_SCALE "0x04030014" // Cluster 0x0403, attribute 0x14
|
||||
#define ZCL_HUMIDITY "0x04050000" // Cluster 0x0403, attribute 0x00
|
||||
#define ZCL_LUMI_WEATHER "0x0000FF01" // Cluster 0x0000, attribute 0xFF01 - proprietary
|
||||
|
||||
#define ZCL_OO_OFF "0x000600" // Cluster 0x0006, cmd 0x00 - On/Off - Off
|
||||
#define ZCL_OO_ON "0x000601" // Cluster 0x0006, cmd 0x01 - On/Off - On
|
||||
#define ZCL_COLORTEMP_MOVE "0x03000A" // Cluster 0x0300, cmd 0x0A, Move to Color Temp
|
||||
#define ZCL_LC_MOVE "0x000800" // Cluster 0x0008, cmd 0x00, Level Control Move to Level
|
||||
#define ZCL_LC_MOVE_1 "0x000801" // Cluster 0x0008, cmd 0x01, Level Control Move
|
||||
#define ZCL_LC_STEP "0x000802" // Cluster 0x0008, cmd 0x02, Level Control Step
|
||||
#define ZCL_LC_STOP "0x000803" // Cluster 0x0008, cmd 0x03, Level Control Stop
|
||||
#define ZCL_LC_MOVE_WOO "0x000804" // Cluster 0x0008, cmd 0x04, Level Control Move to Level, with On/Off
|
||||
#define ZCL_LC_MOVE_1_WOO "0x000805" // Cluster 0x0008, cmd 0x05, Level Control Move, with On/Off
|
||||
#define ZCL_LC_STEP_WOO "0x000806" // Cluster 0x0008, cmd 0x05, Level Control Step, with On/Off
|
||||
#define ZCL_LC_STOP_WOO "0x000807" // Cluster 0x0008, cmd 0x07, Level Control Stop
|
||||
|
||||
void ZCLFrame::postProcessAttributes(JsonObject& json) {
|
||||
const __FlashStringHelper *key;
|
||||
|
||||
// ModelID ZCL 3.2
|
||||
key = F(ZCL_MODELID);
|
||||
if (json.containsKey(key)) {
|
||||
json[F(D_JSON_MODEL D_JSON_ID)] = json[key];
|
||||
json.remove(key);
|
||||
}
|
||||
|
||||
// Temperature ZCL 4.4
|
||||
key = F(ZCL_TEMPERATURE);
|
||||
if (json.containsKey(key)) {
|
||||
// parse temperature
|
||||
int32_t temperature = json[key];
|
||||
json.remove(key);
|
||||
json[F(D_JSON_TEMPERATURE)] = temperature / 100.0f;
|
||||
}
|
||||
|
||||
// Pressure ZCL 4.5
|
||||
key = F(ZCL_PRESSURE);
|
||||
if (json.containsKey(key)) {
|
||||
json[F(D_JSON_PRESSURE)] = json[key];
|
||||
json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); // hPa
|
||||
json.remove(key);
|
||||
}
|
||||
json.remove(F(ZCL_PRESSURE_SCALE));
|
||||
json.remove(F(ZCL_PRESSURE_SCALED));
|
||||
|
||||
// Humidity ZCL 4.7
|
||||
key = F(ZCL_HUMIDITY);
|
||||
if (json.containsKey(key)) {
|
||||
// parse temperature
|
||||
uint32_t humidity = json[key];
|
||||
json.remove(key);
|
||||
json[F(D_JSON_HUMIDITY)] = humidity / 100.0f;
|
||||
}
|
||||
|
||||
// Osram Mini Switch
|
||||
key = F(ZCL_OO_OFF);
|
||||
if (json.containsKey(key)) {
|
||||
json.remove(key);
|
||||
json[F(D_CMND_POWER)] = F("Off");
|
||||
}
|
||||
key = F(ZCL_OO_ON);
|
||||
if (json.containsKey(key)) {
|
||||
json.remove(key);
|
||||
json[F(D_CMND_POWER)] = F("On");
|
||||
}
|
||||
key = F(ZCL_COLORTEMP_MOVE);
|
||||
if (json.containsKey(key)) {
|
||||
String hex = json[key];
|
||||
SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
|
||||
uint16_t color_temp = buf2.get16(0);
|
||||
uint16_t transition_time = buf2.get16(2);
|
||||
json.remove(key);
|
||||
json[F("ColorTemp")] = color_temp;
|
||||
json[F("TransitionTime")] = transition_time / 10.0f;
|
||||
}
|
||||
key = F(ZCL_LC_MOVE_WOO);
|
||||
if (json.containsKey(key)) {
|
||||
String hex = json[key];
|
||||
SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
|
||||
uint8_t level = buf2.get8(0);
|
||||
uint16_t transition_time = buf2.get16(1);
|
||||
json.remove(key);
|
||||
json[F("Dimmer")] = changeUIntScale(level, 0, 255, 0, 100); // change to percentage
|
||||
json[F("TransitionTime")] = transition_time / 10.0f;
|
||||
if (0 == level) {
|
||||
json[F(D_CMND_POWER)] = F("Off");
|
||||
} else {
|
||||
json[F(D_CMND_POWER)] = F("On");
|
||||
}
|
||||
}
|
||||
key = F(ZCL_LC_MOVE);
|
||||
if (json.containsKey(key)) {
|
||||
String hex = json[key];
|
||||
SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
|
||||
uint8_t level = buf2.get8(0);
|
||||
uint16_t transition_time = buf2.get16(1);
|
||||
json.remove(key);
|
||||
json[F("Dimmer")] = changeUIntScale(level, 0, 255, 0, 100); // change to percentage
|
||||
json[F("TransitionTime")] = transition_time / 10.0f;
|
||||
}
|
||||
key = F(ZCL_LC_MOVE_1);
|
||||
if (json.containsKey(key)) {
|
||||
String hex = json[key];
|
||||
SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
|
||||
uint8_t move_mode = buf2.get8(0);
|
||||
uint8_t move_rate = buf2.get8(1);
|
||||
json.remove(key);
|
||||
json[F("Move")] = move_mode ? F("Down") : F("Up");
|
||||
json[F("Rate")] = move_rate;
|
||||
}
|
||||
key = F(ZCL_LC_MOVE_1_WOO);
|
||||
if (json.containsKey(key)) {
|
||||
String hex = json[key];
|
||||
SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
|
||||
uint8_t move_mode = buf2.get8(0);
|
||||
uint8_t move_rate = buf2.get8(1);
|
||||
json.remove(key);
|
||||
json[F("Move")] = move_mode ? F("Down") : F("Up");
|
||||
json[F("Rate")] = move_rate;
|
||||
if (0 == move_mode) {
|
||||
json[F(D_CMND_POWER)] = F("On");
|
||||
}
|
||||
}
|
||||
key = F(ZCL_LC_STEP);
|
||||
if (json.containsKey(key)) {
|
||||
String hex = json[key];
|
||||
SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
|
||||
uint8_t step_mode = buf2.get8(0);
|
||||
uint8_t step_size = buf2.get8(1);
|
||||
uint16_t transition_time = buf2.get16(2);
|
||||
json.remove(key);
|
||||
json[F("Step")] = step_mode ? F("Down") : F("Up");
|
||||
json[F("StepSize")] = step_size;
|
||||
json[F("TransitionTime")] = transition_time / 10.0f;
|
||||
}
|
||||
key = F(ZCL_LC_STEP_WOO);
|
||||
if (json.containsKey(key)) {
|
||||
String hex = json[key];
|
||||
SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
|
||||
uint8_t step_mode = buf2.get8(0);
|
||||
uint8_t step_size = buf2.get8(1);
|
||||
uint16_t transition_time = buf2.get16(2);
|
||||
json.remove(key);
|
||||
json[F("Step")] = step_mode ? F("Down") : F("Up");
|
||||
json[F("StepSize")] = step_size;
|
||||
json[F("TransitionTime")] = transition_time / 10.0f;
|
||||
if (0 == step_mode) {
|
||||
json[F(D_CMND_POWER)] = F("On");
|
||||
}
|
||||
}
|
||||
key = F(ZCL_LC_STOP);
|
||||
if (json.containsKey(key)) {
|
||||
json.remove(key);
|
||||
json[F("Stop")] = 1;
|
||||
}
|
||||
key = F(ZCL_LC_STOP_WOO);
|
||||
if (json.containsKey(key)) {
|
||||
json.remove(key);
|
||||
json[F("Stop")] = 1;
|
||||
}
|
||||
|
||||
// Lumi.weather proprietary field
|
||||
key = F(ZCL_LUMI_WEATHER);
|
||||
if (json.containsKey(key)) {
|
||||
String hex = json[key];
|
||||
SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonObject& json_lumi = jsonBuffer.createObject();
|
||||
uint32_t i = 0;
|
||||
uint32_t len = buf2.len();
|
||||
char shortaddr[8];
|
||||
|
||||
while (len - i >= 2) {
|
||||
uint8_t attrid = buf2.get8(i++);
|
||||
|
||||
snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%02X"), attrid);
|
||||
|
||||
i += parseSingleAttribute(json_lumi, shortaddr, buf2, i, len);
|
||||
}
|
||||
// parse output
|
||||
if (json_lumi.containsKey("0x64")) { // Temperature
|
||||
int32_t temperature = json_lumi["0x64"];
|
||||
json[F(D_JSON_TEMPERATURE)] = temperature / 100.0f;
|
||||
}
|
||||
if (json_lumi.containsKey("0x65")) { // Humidity
|
||||
uint32_t humidity = json_lumi["0x65"];
|
||||
json[F(D_JSON_HUMIDITY)] = humidity / 100.0f;
|
||||
}
|
||||
if (json_lumi.containsKey("0x66")) { // Pressure
|
||||
int32_t pressure = json_lumi["0x66"];
|
||||
json[F(D_JSON_PRESSURE)] = pressure / 100.0f;
|
||||
json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); // hPa
|
||||
}
|
||||
if (json_lumi.containsKey("0x01")) { // Battery Voltage
|
||||
uint32_t voltage = json_lumi["0x01"];
|
||||
json[F(D_JSON_VOLTAGE)] = voltage / 1000.0f;
|
||||
json[F("Battery")] = toPercentageCR2032(voltage);
|
||||
}
|
||||
json.remove(key);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // USE_ZIGBEE
|
|
@ -23,17 +23,35 @@
|
|||
|
||||
const uint32_t ZIGBEE_BUFFER_SIZE = 256; // Max ZNP frame is SOF+LEN+CMD1+CMD2+250+FCS = 255
|
||||
const uint8_t ZIGBEE_SOF = 0xFE;
|
||||
const uint8_t ZIGBEE_LABEL_ABORT = 99; // goto label 99 in case of fatal error
|
||||
const uint8_t ZIGBEE_LABEL_READY = 20; // goto label 99 in case of fatal error
|
||||
|
||||
// Status code used for ZigbeeStatus MQTT message
|
||||
// Ex: {"ZigbeeStatus":{"code": 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
|
||||
const uint8_t ZIGBEE_STATUS_STARTING = 3; // Starting CC2530 as coordinator
|
||||
const uint8_t ZIGBEE_STATUS_PERMITJOIN_CLOSE = 20; // Disable PermitJoin
|
||||
const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_60 = 21; // Enable PermitJoin for 60 seconds
|
||||
const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_XX = 22; // Enable PermitJoin until next boot
|
||||
const uint8_t ZIGBEE_STATUS_DEVICE_VERSION = 50; // Status: CC2530 ZNP Version
|
||||
const uint8_t ZIGBEE_STATUS_DEVICE_INFO = 51; // Status: CC2530 Device Configuration
|
||||
const uint8_t ZIGBEE_STATUS_UNSUPPORTED_VERSION = 98; // Unsupported ZNP version
|
||||
const uint8_t ZIGBEE_STATUS_ABORT = 99; // Fatal error, Zigbee not working
|
||||
|
||||
//#define Z_USE_SOFTWARE_SERIAL
|
||||
|
||||
#ifdef Z_USE_SOFTWARE_SERIAL
|
||||
#include <SoftwareSerial.h>
|
||||
SoftwareSerial *ZigbeeSerial = nullptr;
|
||||
#else
|
||||
#include <TasmotaSerial.h>
|
||||
|
||||
TasmotaSerial *ZigbeeSerial = nullptr;
|
||||
#endif
|
||||
|
||||
const char kZigbeeCommands[] PROGMEM = "|" D_CMND_ZIGBEEZNPSEND;
|
||||
|
||||
void (* const ZigbeeCommand[])(void) PROGMEM = { &CmndZigbeeZNPSend };
|
||||
const char kZigbeeCommands[] PROGMEM = "|" D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN;
|
||||
|
||||
void (* const ZigbeeCommand[])(void) PROGMEM = { &CmndZigbeeZNPSend, &CmndZigbeePermitJoin };
|
||||
|
||||
typedef int32_t (*ZB_Func)(uint8_t value);
|
||||
typedef int32_t (*ZB_RecvMsgFunc)(int32_t res, class SBuffer &buf);
|
||||
|
@ -74,6 +92,7 @@ enum Zigbee_StateMachine_Instruction_Set {
|
|||
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
|
||||
ZGB_INSTR_MQTT_STATUS, // send MQTT status string with code
|
||||
ZGB_INSTR_SEND, // send a ZNP message
|
||||
ZGB_INSTR_WAIT_UNTIL, // wait until the specified message is received, ignore all others
|
||||
ZGB_INSTR_WAIT_RECV, // wait for a message according to the filter
|
||||
|
@ -95,12 +114,24 @@ enum Zigbee_StateMachine_Instruction_Set {
|
|||
|
||||
#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_STATUS(x, m) { .i = { ZGB_INSTR_MQTT_STATUS, (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) },
|
||||
|
||||
// Labels used in the State Machine -- internal only
|
||||
const uint8_t ZIGBEE_LABEL_START = 10; // Start ZNP
|
||||
const uint8_t ZIGBEE_LABEL_READY = 20; // goto label 20 for main loop
|
||||
const uint8_t ZIGBEE_LABEL_MAIN_LOOP = 21; // main loop
|
||||
const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_CLOSE = 30; // disable permit join
|
||||
const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60 = 31; // enable permit join for 60 seconds
|
||||
const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX = 32; // enable permit join for 60 seconds
|
||||
// errors
|
||||
const uint8_t ZIGBEE_LABEL_ABORT = 99; // goto label 99 in case of fatal error
|
||||
const uint8_t ZIGBEE_LABEL_UNSUPPORTED_VERSION = 98; // Unsupported ZNP version
|
||||
|
||||
struct ZigbeeStatus {
|
||||
bool active = true; // is Zigbee active for this device, i.e. GPIOs configured
|
||||
bool state_machine = false; // the state machine is running
|
||||
|
@ -124,74 +155,6 @@ struct ZigbeeStatus zigbee;
|
|||
|
||||
SBuffer *zigbee_buffer = nullptr;
|
||||
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
* ZCL
|
||||
\*********************************************************************************************/
|
||||
|
||||
typedef union ZCLHeaderFrameControl_t {
|
||||
struct {
|
||||
uint8_t frame_type : 2; // 00 = across entire profile, 01 = cluster specific
|
||||
uint8_t manuf_specific : 1; // Manufacturer Specific Sub-field
|
||||
uint8_t direction : 1; // 0 = tasmota to zigbee, 1 = zigbee to tasmota
|
||||
uint8_t disable_def_resp : 1; // don't send back default response
|
||||
uint8_t reserved : 3;
|
||||
} b;
|
||||
uint8_t d8; // raw 8 bits field
|
||||
} 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 ):
|
||||
_cmd_id(cmd_id), _manuf_code(manuf_code), _transact_seq(transact_seq),
|
||||
_payload(buf_len ? buf_len : 250) // allocate the data frame from source or preallocate big enough
|
||||
{
|
||||
_frame_control.d8 = frame_control;
|
||||
_payload.addBuffer(buf, buf_len);
|
||||
};
|
||||
|
||||
void publishMQTTReceived(void) {
|
||||
char hex_char[_payload.len()*2+2];
|
||||
ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char));
|
||||
ResponseTime_P(PSTR(",\"" D_JSON_ZIGBEEZCLRECEIVED "\":{\"fc\":\"0x%02X\",\"manuf\":\"0x%04X\",\"transact\":%d,"
|
||||
"\"cmdid\":\"0x%02X\",\"payload\":\"%s\"}}"),
|
||||
_frame_control, _manuf_code, _transact_seq, _cmd_id,
|
||||
hex_char);
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCLSENT));
|
||||
XdrvRulesProcess();
|
||||
}
|
||||
|
||||
static ZCLFrame parseRawFrame(SBuffer &buf, uint8_t offset, uint8_t len) { // parse a raw frame and build the ZCL frame object
|
||||
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);
|
||||
return zcl_frame;
|
||||
}
|
||||
|
||||
private:
|
||||
ZCLHeaderFrameControl_t _frame_control = { .d8 = 0 };
|
||||
uint16_t _manuf_code = 0; // optional
|
||||
uint8_t _transact_seq = 0; // transaction sequence number
|
||||
uint8_t _cmd_id = 0;
|
||||
SBuffer _payload;
|
||||
};
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
* State Machine
|
||||
\*********************************************************************************************/
|
||||
|
@ -209,8 +172,8 @@ private:
|
|||
|
||||
// ZBS_* Zigbee Send
|
||||
// ZBR_* Zigbee Recv
|
||||
ZBM(ZBS_RESET, Z_AREQ | Z_SYS, SYS_RESET, 0x01 ) // 410001 SYS_RESET_REQ Software reset
|
||||
ZBM(ZBR_RESET, Z_AREQ | Z_SYS, SYS_RESET_IND ) // 4180 SYS_RESET_REQ Software reset response
|
||||
ZBM(ZBS_RESET, Z_AREQ | Z_SYS, SYS_RESET, 0x00 ) // 410001 SYS_RESET_REQ Hardware reset
|
||||
ZBM(ZBR_RESET, Z_AREQ | Z_SYS, SYS_RESET_IND ) // 4180 SYS_RESET_REQ Hardware reset response
|
||||
|
||||
ZBM(ZBS_VERSION, Z_SREQ | Z_SYS, SYS_VERSION ) // 2102 Z_SYS:version
|
||||
ZBM(ZBR_VERSION, Z_SRSP | Z_SYS, SYS_VERSION ) // 6102 Z_SYS:version
|
||||
|
@ -295,7 +258,7 @@ ZBM(ZBS_W_ZDODCB, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_ZDO_DIRECT_CB,
|
|||
ZBM(ZBS_WNV_INITZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_ITEM_INIT, ZNP_HAS_CONFIGURED & 0xFF, ZNP_HAS_CONFIGURED >> 8,
|
||||
0x01, 0x00 /* InitLen 16 bits */, 0x01 /* len */, 0x00 ) // 2107000F01000100 - 610709
|
||||
// Init succeeded
|
||||
ZBM(ZBR_WNV_INIT_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_WRITE, Z_Created ) // 610709 - NV Write
|
||||
ZBM(ZBR_WNV_INIT_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_ITEM_INIT, Z_Created ) // 610709 - NV Write
|
||||
// Write ZNP Has Configured
|
||||
ZBM(ZBS_WNV_ZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, Z_B0(ZNP_HAS_CONFIGURED), Z_B1(ZNP_HAS_CONFIGURED),
|
||||
0x00 /* offset */, 0x01 /* len */, 0x55 ) // 2109000F000155 - 610900
|
||||
|
@ -352,11 +315,14 @@ ZBM(ZBS_AF_REGISTER0B, Z_SREQ | Z_AF, AF_REGISTER, 0x0B /* endpoint */, Z_B0(Z_P
|
|||
// Z_ZDO:mgmtPermitJoinReq
|
||||
ZBM(ZBS_PERMITJOINREQ_CLOSE, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x02 /* AddrMode */, // 25360200000000
|
||||
0x00, 0x00 /* DstAddr */, 0x00 /* Duration */, 0x00 /* TCSignificance */)
|
||||
ZBM(ZBS_PERMITJOINREQ_OPEN, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x0F /* AddrMode */, // 25360FFFFCFF00
|
||||
ZBM(ZBS_PERMITJOINREQ_OPEN_60, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x0F /* AddrMode */, // 25360FFFFC3C00
|
||||
0xFC, 0xFF /* DstAddr */, 60 /* Duration */, 0x00 /* TCSignificance */)
|
||||
ZBM(ZBS_PERMITJOINREQ_OPEN_XX, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x0F /* AddrMode */, // 25360FFFFCFF00
|
||||
0xFC, 0xFF /* DstAddr */, 0xFF /* Duration */, 0x00 /* TCSignificance */)
|
||||
ZBM(ZBR_PERMITJOINREQ, Z_SRSP | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, Z_Success) // 653600
|
||||
ZBM(ZBR_PERMITJOIN_AREQ_CLOSE, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND, 0x00 /* Duration */) // 45CB00
|
||||
ZBM(ZBR_PERMITJOIN_AREQ_OPEN, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND, 0xFF /* Duration */) // 45CBFF
|
||||
ZBM(ZBR_PERMITJOIN_AREQ_CLOSE, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND, 0x00 /* Duration */) // 45CB00
|
||||
ZBM(ZBR_PERMITJOIN_AREQ_OPEN_60, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND, 60 /* Duration */) // 45CB3C
|
||||
ZBM(ZBR_PERMITJOIN_AREQ_OPEN_XX, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND, 0xFF /* Duration */) // 45CBFF
|
||||
ZBM(ZBR_PERMITJOIN_AREQ_RSP, Z_AREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_RSP, 0x00, 0x00 /* srcAddr*/, Z_Success ) // 45B6000000
|
||||
|
||||
// Filters for ZCL frames
|
||||
|
@ -371,119 +337,209 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
|
|||
ZI_WAIT(15000) // wait for 15 seconds for Tasmota to stabilize
|
||||
ZI_ON_ERROR_GOTO(50)
|
||||
|
||||
ZI_LOG(LOG_LEVEL_INFO, "ZIG: rebooting device")
|
||||
ZI_MQTT_STATUS(ZIGBEE_STATUS_BOOT, "Booting")
|
||||
//ZI_LOG(LOG_LEVEL_INFO, "ZIG: rebooting device")
|
||||
ZI_SEND(ZBS_RESET) // reboot cc2530 just in case we rebooted ESP8266 but not cc2530
|
||||
ZI_WAIT_RECV(5000, ZBR_RESET) // timeout 5s
|
||||
ZI_LOG(LOG_LEVEL_INFO, "ZIG: checking device configuration")
|
||||
ZI_SEND(ZBS_ZNPHC) // check value of ZNP Has Configured
|
||||
ZI_WAIT_RECV(2000, ZBR_ZNPHC)
|
||||
ZI_SEND(ZBS_VERSION) // check ZNP software version
|
||||
ZI_WAIT_RECV(500, ZBR_VERSION)
|
||||
ZI_WAIT_RECV_FUNC(1000, ZBR_VERSION, &Z_ReceiveCheckVersion) // Check version
|
||||
ZI_SEND(ZBS_PAN) // check PAN ID
|
||||
ZI_WAIT_RECV(500, ZBR_PAN)
|
||||
ZI_WAIT_RECV(1000, ZBR_PAN)
|
||||
ZI_SEND(ZBS_EXTPAN) // check EXT PAN ID
|
||||
ZI_WAIT_RECV(500, ZBR_EXTPAN)
|
||||
ZI_WAIT_RECV(1000, ZBR_EXTPAN)
|
||||
ZI_SEND(ZBS_CHANN) // check CHANNEL
|
||||
ZI_WAIT_RECV(500, ZBR_CHANN)
|
||||
ZI_WAIT_RECV(1000, ZBR_CHANN)
|
||||
ZI_SEND(ZBS_PFGK) // check PFGK
|
||||
ZI_WAIT_RECV(500, ZBR_PFGK)
|
||||
ZI_WAIT_RECV(1000, ZBR_PFGK)
|
||||
ZI_SEND(ZBS_PFGKEN) // check PFGKEN
|
||||
ZI_WAIT_RECV(500, ZBR_PFGKEN)
|
||||
ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee configuration ok")
|
||||
ZI_WAIT_RECV(1000, ZBR_PFGKEN)
|
||||
//ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee configuration ok")
|
||||
// all is good, we can start
|
||||
|
||||
ZI_LABEL(10) // START ZNP App
|
||||
ZI_CALL(&Z_State_Ready, 1)
|
||||
ZI_LABEL(ZIGBEE_LABEL_START) // START ZNP App
|
||||
ZI_MQTT_STATUS(ZIGBEE_STATUS_STARTING, "Configured, starting coordinator")
|
||||
//ZI_CALL(&Z_State_Ready, 1) // Now accept incoming messages
|
||||
ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
|
||||
// Z_ZDO:startupFromApp
|
||||
ZI_LOG(LOG_LEVEL_INFO, "ZIG: starting zigbee coordinator")
|
||||
ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator
|
||||
ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) // wait for sync ack of command
|
||||
ZI_WAIT_UNTIL(5000, AREQ_STARTUPFROMAPP) // wait for async message that coordinator started
|
||||
ZI_SEND(ZBS_GETDEVICEINFO) // GetDeviceInfo
|
||||
ZI_WAIT_RECV(500, ZBR_GETDEVICEINFO) // TODO memorize info
|
||||
ZI_SEND(ZBS_ZDO_NODEDESCREQ) // Z_ZDO:nodeDescReq
|
||||
ZI_WAIT_RECV(500, ZBR_ZDO_NODEDESCREQ)
|
||||
//ZI_LOG(LOG_LEVEL_INFO, "ZIG: starting zigbee coordinator")
|
||||
ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator
|
||||
ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) // wait for sync ack of command
|
||||
ZI_WAIT_UNTIL(5000, AREQ_STARTUPFROMAPP) // wait for async message that coordinator started
|
||||
ZI_SEND(ZBS_GETDEVICEINFO) // GetDeviceInfo
|
||||
ZI_WAIT_RECV_FUNC(2000, ZBR_GETDEVICEINFO, &Z_ReceiveDeviceInfo)
|
||||
//ZI_WAIT_RECV(2000, ZBR_GETDEVICEINFO) // TODO memorize info
|
||||
ZI_SEND(ZBS_ZDO_NODEDESCREQ) // Z_ZDO:nodeDescReq
|
||||
ZI_WAIT_RECV(1000, ZBR_ZDO_NODEDESCREQ)
|
||||
ZI_WAIT_UNTIL(5000, AREQ_ZDO_NODEDESCREQ)
|
||||
ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) // Z_ZDO:activeEpReq
|
||||
ZI_WAIT_RECV(500, ZBR_ZDO_ACTIVEEPREQ)
|
||||
ZI_WAIT_UNTIL(500, ZBR_ZDO_ACTIVEEPRSP_NONE)
|
||||
ZI_SEND(ZBS_AF_REGISTER01) // Z_AF register for endpoint 01, profile 0x0104 Home Automation
|
||||
ZI_WAIT_RECV(500, ZBR_AF_REGISTER)
|
||||
ZI_SEND(ZBS_AF_REGISTER0B) // Z_AF register for endpoint 0B, profile 0x0104 Home Automation
|
||||
ZI_WAIT_RECV(500, ZBR_AF_REGISTER)
|
||||
ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) // Z_ZDO:activeEpReq
|
||||
ZI_WAIT_RECV(1000, ZBR_ZDO_ACTIVEEPREQ)
|
||||
ZI_WAIT_UNTIL(1000, ZBR_ZDO_ACTIVEEPRSP_NONE)
|
||||
ZI_SEND(ZBS_AF_REGISTER01) // Z_AF register for endpoint 01, profile 0x0104 Home Automation
|
||||
ZI_WAIT_RECV(1000, ZBR_AF_REGISTER)
|
||||
ZI_SEND(ZBS_AF_REGISTER0B) // Z_AF register for endpoint 0B, profile 0x0104 Home Automation
|
||||
ZI_WAIT_RECV(1000, ZBR_AF_REGISTER)
|
||||
// Z_ZDO:nodeDescReq ?? Is is useful to redo it? TODO
|
||||
// redo Z_ZDO:activeEpReq to check that Ep are available
|
||||
ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) // Z_ZDO:activeEpReq
|
||||
ZI_WAIT_RECV(500, ZBR_ZDO_ACTIVEEPREQ)
|
||||
ZI_WAIT_UNTIL(500, ZBR_ZDO_ACTIVEEPRSP_OK)
|
||||
ZI_SEND(ZBS_PERMITJOINREQ_CLOSE) // Closing the Permit Join
|
||||
ZI_WAIT_RECV(500, ZBR_PERMITJOINREQ)
|
||||
ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful
|
||||
ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) // Z_ZDO:activeEpReq
|
||||
ZI_WAIT_RECV(1000, ZBR_ZDO_ACTIVEEPREQ)
|
||||
ZI_WAIT_UNTIL(1000, ZBR_ZDO_ACTIVEEPRSP_OK)
|
||||
ZI_SEND(ZBS_PERMITJOINREQ_CLOSE) // Closing the Permit Join
|
||||
ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ)
|
||||
ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful
|
||||
//ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_CLOSE)
|
||||
ZI_SEND(ZBS_PERMITJOINREQ_OPEN) // Opening Permit Join, normally through command TODO
|
||||
ZI_WAIT_RECV(500, ZBR_PERMITJOINREQ)
|
||||
ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful
|
||||
//ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_OPEN)
|
||||
//ZI_SEND(ZBS_PERMITJOINREQ_OPEN_XX) // Opening Permit Join, normally through command
|
||||
//ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ)
|
||||
//ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful
|
||||
//ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_OPEN_XX)
|
||||
|
||||
ZI_LABEL(ZIGBEE_LABEL_READY)
|
||||
ZI_MQTT_STATUS(ZIGBEE_STATUS_OK, "Started")
|
||||
ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee device ready, listening...")
|
||||
ZI_CALL(&Z_State_Ready, 1)
|
||||
ZI_CALL(&Z_State_Ready, 1) // Now accept incoming messages
|
||||
ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP)
|
||||
ZI_WAIT_FOREVER()
|
||||
ZI_GOTO(ZIGBEE_LABEL_READY)
|
||||
|
||||
ZI_LABEL(50) // reformat device
|
||||
ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee bad configuration of device, doing a factory reset")
|
||||
ZI_LABEL(ZIGBEE_LABEL_PERMIT_JOIN_CLOSE)
|
||||
ZI_MQTT_STATUS(ZIGBEE_STATUS_PERMITJOIN_CLOSE, "Disable Pairing mode")
|
||||
ZI_SEND(ZBS_PERMITJOINREQ_CLOSE) // Closing the Permit Join
|
||||
ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ)
|
||||
//ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful
|
||||
//ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_CLOSE)
|
||||
ZI_GOTO(ZIGBEE_LABEL_MAIN_LOOP)
|
||||
|
||||
ZI_LABEL(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60)
|
||||
ZI_MQTT_STATUS(ZIGBEE_STATUS_PERMITJOIN_OPEN_60, "Enable Pairing mode for 60 seconds")
|
||||
ZI_SEND(ZBS_PERMITJOINREQ_OPEN_60)
|
||||
ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ)
|
||||
//ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful
|
||||
//ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_OPEN_60)
|
||||
ZI_GOTO(ZIGBEE_LABEL_MAIN_LOOP)
|
||||
|
||||
ZI_LABEL(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX)
|
||||
ZI_MQTT_STATUS(ZIGBEE_STATUS_PERMITJOIN_OPEN_XX, "Enable Pairing mode until next boot")
|
||||
ZI_SEND(ZBS_PERMITJOINREQ_OPEN_XX)
|
||||
ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ)
|
||||
//ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful
|
||||
//ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_OPEN_XX)
|
||||
ZI_GOTO(ZIGBEE_LABEL_MAIN_LOOP)
|
||||
|
||||
ZI_LABEL(50) // reformat device
|
||||
ZI_MQTT_STATUS(ZIGBEE_STATUS_RESET_CONF, "Reseting configuration")
|
||||
//ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee bad configuration of device, doing a factory reset")
|
||||
ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
|
||||
ZI_SEND(ZBS_FACTRES) // factory reset
|
||||
ZI_WAIT_RECV(500, ZBR_W_OK)
|
||||
ZI_SEND(ZBS_RESET) // reset device
|
||||
ZI_SEND(ZBS_FACTRES) // factory reset
|
||||
ZI_WAIT_RECV(1000, ZBR_W_OK)
|
||||
ZI_SEND(ZBS_RESET) // reset device
|
||||
ZI_WAIT_RECV(5000, ZBR_RESET)
|
||||
ZI_SEND(ZBS_W_PAN) // write PAN ID
|
||||
ZI_WAIT_RECV(500, ZBR_W_OK)
|
||||
ZI_SEND(ZBS_W_EXTPAN) // write EXT PAN ID
|
||||
ZI_WAIT_RECV(500, ZBR_W_OK)
|
||||
ZI_SEND(ZBS_W_CHANN) // write CHANNEL
|
||||
ZI_WAIT_RECV(500, ZBR_W_OK)
|
||||
ZI_SEND(ZBS_W_LOGTYP) // write Logical Type = coordinator
|
||||
ZI_WAIT_RECV(500, ZBR_W_OK)
|
||||
ZI_SEND(ZBS_W_PFGK) // write PRECFGKEY
|
||||
ZI_WAIT_RECV(500, ZBR_W_OK)
|
||||
ZI_SEND(ZBS_W_PFGKEN) // write PRECFGKEY Enable
|
||||
ZI_WAIT_RECV(500, ZBR_W_OK)
|
||||
ZI_SEND(ZBS_WNV_SECMODE) // write Security Mode
|
||||
ZI_WAIT_RECV(500, ZBR_WNV_OK)
|
||||
ZI_SEND(ZBS_W_ZDODCB) // write Z_ZDO Direct CB
|
||||
ZI_WAIT_RECV(500, ZBR_W_OK)
|
||||
ZI_SEND(ZBS_W_PAN) // write PAN ID
|
||||
ZI_WAIT_RECV(1000, ZBR_W_OK)
|
||||
ZI_SEND(ZBS_W_EXTPAN) // write EXT PAN ID
|
||||
ZI_WAIT_RECV(1000, ZBR_W_OK)
|
||||
ZI_SEND(ZBS_W_CHANN) // write CHANNEL
|
||||
ZI_WAIT_RECV(1000, ZBR_W_OK)
|
||||
ZI_SEND(ZBS_W_LOGTYP) // write Logical Type = coordinator
|
||||
ZI_WAIT_RECV(1000, ZBR_W_OK)
|
||||
ZI_SEND(ZBS_W_PFGK) // write PRECFGKEY
|
||||
ZI_WAIT_RECV(1000, ZBR_W_OK)
|
||||
ZI_SEND(ZBS_W_PFGKEN) // write PRECFGKEY Enable
|
||||
ZI_WAIT_RECV(1000, ZBR_W_OK)
|
||||
ZI_SEND(ZBS_WNV_SECMODE) // write Security Mode
|
||||
ZI_WAIT_RECV(1000, ZBR_WNV_OK)
|
||||
ZI_SEND(ZBS_W_ZDODCB) // write Z_ZDO Direct CB
|
||||
ZI_WAIT_RECV(1000, ZBR_W_OK)
|
||||
// Now mark the device as ready, writing 0x55 in memory slot 0x0F00
|
||||
ZI_SEND(ZBS_WNV_INITZNPHC) // Init NV ZNP Has Configured
|
||||
ZI_WAIT_RECV(500, ZBR_WNV_INIT_OK)
|
||||
ZI_SEND(ZBS_WNV_ZNPHC) // Write NV ZNP Has Configured
|
||||
ZI_WAIT_RECV(500, ZBR_WNV_OK)
|
||||
ZI_SEND(ZBS_WNV_INITZNPHC) // Init NV ZNP Has Configured
|
||||
ZI_WAIT_RECV(1000, ZBR_WNV_INIT_OK)
|
||||
ZI_SEND(ZBS_WNV_ZNPHC) // Write NV ZNP Has Configured
|
||||
ZI_WAIT_RECV(1000, ZBR_WNV_OK)
|
||||
|
||||
ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee device reconfigured")
|
||||
ZI_GOTO(10)
|
||||
//ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee device reconfigured")
|
||||
ZI_GOTO(ZIGBEE_LABEL_START)
|
||||
|
||||
ZI_LABEL(ZIGBEE_LABEL_ABORT) // Label 99: abort
|
||||
ZI_LABEL(ZIGBEE_LABEL_UNSUPPORTED_VERSION)
|
||||
ZI_MQTT_STATUS(ZIGBEE_STATUS_UNSUPPORTED_VERSION, "Only ZNP 1.2 is currently supported")
|
||||
ZI_GOTO(ZIGBEE_LABEL_ABORT)
|
||||
|
||||
ZI_LABEL(ZIGBEE_LABEL_ABORT) // Label 99: abort
|
||||
ZI_MQTT_STATUS(ZIGBEE_STATUS_ABORT, "Abort")
|
||||
ZI_LOG(LOG_LEVEL_ERROR, "ZIG: Abort")
|
||||
ZI_STOP(ZIGBEE_LABEL_ABORT)
|
||||
};
|
||||
|
||||
int32_t Z_ReceiveDeviceInfo(int32_t res, class SBuffer &buf) {
|
||||
// Ex= 6700.00.6263151D004B1200.0000.07.09.02.83869991
|
||||
// IEEE Adr (8 bytes) = 0x00124B001D156362
|
||||
// Short Addr (2 bytes) = 0x0000
|
||||
// Device Type (1 byte) = 0x07 (coord?)
|
||||
// Device State (1 byte) = 0x09 (coordinator started)
|
||||
// NumAssocDevices (1 byte) = 0x02
|
||||
// List of devices: 0x8683, 0x9199
|
||||
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);
|
||||
|
||||
int32_t Z_Recv_Vers(int32_t res, class SBuffer &buf) {
|
||||
char hex[20];
|
||||
Uint64toHex(long_adr, hex, 64);
|
||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATUS "\":{"
|
||||
"\"code\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\""
|
||||
",\"DeviceType\":%d,\"DeviceState\":%d"
|
||||
",\"NumAssocDevices\":%d"),
|
||||
ZIGBEE_STATUS_DEVICE_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(); // append '}'
|
||||
ResponseJsonEnd(); // append '}'
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATUS));
|
||||
XdrvRulesProcess();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int32_t Z_ReceiveCheckVersion(int32_t res, class SBuffer &buf) {
|
||||
// check that the version is supported
|
||||
// typical version for ZNP 1.2
|
||||
// 61020200-020603D91434010200000000
|
||||
// TranportRev = 02
|
||||
// Product = 00
|
||||
// MajorRel = 2
|
||||
// MinorRel = 6
|
||||
// MaintRel = 3
|
||||
// Revision = 20190425 d (0x013414D9)
|
||||
if ((0x02 == buf.get8(4)) && (0x06 == buf.get8(5))) {
|
||||
// 61020200-02.06.03.D9143401.0200000000
|
||||
// TranportRev = 02
|
||||
// Product = 00
|
||||
// MajorRel = 2
|
||||
// MinorRel = 6
|
||||
// MaintRel = 3
|
||||
// Revision = 20190425 d (0x013414D9)
|
||||
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_STATUS "\":{"
|
||||
"\"code\":%d,\"MajorRel\":%d,\"MinorRel\":%d"
|
||||
",\"MaintRel\":%d,\"Revision\":%d}}"),
|
||||
ZIGBEE_STATUS_DEVICE_VERSION, major_rel, minor_rel,
|
||||
maint_rel, revision);
|
||||
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATUS));
|
||||
XdrvRulesProcess();
|
||||
|
||||
if ((0x02 == major_rel) && (0x06 == minor_rel)) {
|
||||
return 0; // version 2.6.x is ok
|
||||
} else {
|
||||
return -2; // abort
|
||||
return ZIGBEE_LABEL_UNSUPPORTED_VERSION; // abort
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -496,10 +552,44 @@ int32_t Z_Recv_Default(int32_t res, class SBuffer &buf) {
|
|||
} else {
|
||||
if ( (pgm_read_byte(&ZBR_AF_INCOMING_MESSAGE[0]) == buf.get8(0)) &&
|
||||
(pgm_read_byte(&ZBR_AF_INCOMING_MESSAGE[1]) == buf.get8(1)) ) {
|
||||
// AF_INCOMING_MSG, extract ZCL part TODO
|
||||
// skip first 19 bytes
|
||||
ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 19, buf.get8(18));
|
||||
zcl_received.publishMQTTReceived();
|
||||
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);
|
||||
|
||||
ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 19, buf.get8(18), clusterid, groupid);
|
||||
|
||||
zcl_received.publishMQTTReceived(groupid, clusterid, srcaddr,
|
||||
srcendpoint, dstendpoint, wasbroadcast,
|
||||
linkquality, securityuse, seqnumber,
|
||||
timestamp);
|
||||
|
||||
char shortaddr[8];
|
||||
snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr);
|
||||
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonObject& json_root = jsonBuffer.createObject();
|
||||
JsonObject& json = json_root.createNestedObject(shortaddr);
|
||||
if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) {
|
||||
zcl_received.parseRawAttributes(json);
|
||||
} else if (zcl_received.isClusterSpecificCommand()) {
|
||||
zcl_received.parseClusterSpecificCommand(json);
|
||||
}
|
||||
zcl_received.postProcessAttributes(json);
|
||||
|
||||
String msg("");
|
||||
msg.reserve(100);
|
||||
json_root.printTo(msg);
|
||||
|
||||
Response_P(PSTR("%s"), msg.c_str());
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCLRECEIVED));
|
||||
XdrvRulesProcess();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
@ -658,11 +748,16 @@ void ZigbeeStateMachine_Run(void) {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
// TODO
|
||||
break;
|
||||
case ZGB_INSTR_LOG:
|
||||
AddLog_P(cur_d8, (char*) cur_ptr1);
|
||||
break;
|
||||
case ZGB_INSTR_MQTT_STATUS:
|
||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATUS "\":{\"code\":%d,\"message\":\"%s\"}}"),
|
||||
cur_d8, (char*) cur_ptr1);
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATUS));
|
||||
XdrvRulesProcess();
|
||||
break;
|
||||
case ZGB_INSTR_SEND:
|
||||
ZigbeeZNPSend((uint8_t*) cur_ptr1, cur_d8 /* len */);
|
||||
break;
|
||||
|
@ -791,7 +886,7 @@ void ZigbeeInput(void)
|
|||
|
||||
if ((0 == zigbee_buffer->len()) && (ZIGBEE_SOF != zigbee_in_byte)) {
|
||||
// waiting for SOF (Start Of Frame) byte, discard anything else
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZigbeeInput discarding byte %02X"), zigbee_in_byte);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInput discarding byte %02X"), zigbee_in_byte);
|
||||
continue; // discard
|
||||
}
|
||||
|
||||
|
@ -820,6 +915,9 @@ void ZigbeeInput(void)
|
|||
char hex_char[(zigbee_buffer->len() * 2) + 2];
|
||||
ToHex_P((unsigned char*)zigbee_buffer->getBuffer(), zigbee_buffer->len(), hex_char, sizeof(hex_char));
|
||||
|
||||
#ifndef Z_USE_SOFTWARE_SERIAL
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZIG: Bytes follor_read_metric = %0d"), ZigbeeSerial->getLoopReadMetric());
|
||||
#endif
|
||||
// buffer received, now check integrity
|
||||
if (zigbee_buffer->len() != zigbee_frame_len) {
|
||||
// Len is not correct, log and reject frame
|
||||
|
@ -852,19 +950,25 @@ void ZigbeeInit(void)
|
|||
zigbee.active = false;
|
||||
if ((pin[GPIO_ZIGBEE_RX] < 99) && (pin[GPIO_ZIGBEE_TX] < 99)) {
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("Zigbee: GPIOs Rx:%d Tx:%d"), pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX]);
|
||||
#ifdef Z_USE_SOFTWARE_SERIAL
|
||||
ZigbeeSerial = new SoftwareSerial();
|
||||
ZigbeeSerial->begin(115200, pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX], SWSERIAL_8N1, false, 256); // ZNP is 115200, RTS/CTS (ignored), 8N1
|
||||
ZigbeeSerial->enableIntTx(false);
|
||||
zigbee_buffer = new SBuffer(ZIGBEE_BUFFER_SIZE);
|
||||
#else
|
||||
ZigbeeSerial = new TasmotaSerial(pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX], 0, 0, 256); // set a receive buffer of 256 bytes
|
||||
if (ZigbeeSerial->begin(115200)) { // ZNP is 115200, RTS/CTS (ignored), 8N1
|
||||
if (ZigbeeSerial->hardwareSerial()) {
|
||||
ClaimSerial();
|
||||
zigbee_buffer = new PreAllocatedSBuffer(sizeof(serial_in_buffer), serial_in_buffer);
|
||||
} else {
|
||||
zigbee_buffer = new SBuffer(ZIGBEE_BUFFER_SIZE);
|
||||
}
|
||||
zigbee.active = true;
|
||||
zigbee.init_phase = true; // start the state machine
|
||||
zigbee.state_machine = true; // start the state machine
|
||||
ZigbeeSerial->flush();
|
||||
}
|
||||
ZigbeeSerial->begin(115200);
|
||||
if (ZigbeeSerial->hardwareSerial()) {
|
||||
ClaimSerial();
|
||||
zigbee_buffer = new PreAllocatedSBuffer(sizeof(serial_in_buffer), serial_in_buffer);
|
||||
} else {
|
||||
zigbee_buffer = new SBuffer(ZIGBEE_BUFFER_SIZE);
|
||||
}
|
||||
#endif
|
||||
zigbee.active = true;
|
||||
zigbee.init_phase = true; // start the state machine
|
||||
zigbee.state_machine = true; // start the state machine
|
||||
ZigbeeSerial->flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -874,7 +978,6 @@ void ZigbeeInit(void)
|
|||
|
||||
void CmndZigbeeZNPSend(void)
|
||||
{
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("CmndZigbeeZNPSend: entering, data_len = %d"), XdrvMailbox.data_len); // TODO
|
||||
if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) {
|
||||
uint8_t code;
|
||||
|
||||
|
@ -928,6 +1031,23 @@ void ZigbeeZNPSend(const uint8_t *msg, size_t len) {
|
|||
XdrvRulesProcess();
|
||||
}
|
||||
|
||||
|
||||
void CmndZigbeePermitJoin(void)
|
||||
{
|
||||
uint32_t payload = XdrvMailbox.payload;
|
||||
if (payload < 0) { payload = 0; }
|
||||
if ((99 != payload) && (payload > 1)) { payload = 1; }
|
||||
|
||||
if (1 == payload) {
|
||||
ZigbeeGotoLabel(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60);
|
||||
} else if (99 == payload){
|
||||
ZigbeeGotoLabel(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX);
|
||||
} else {
|
||||
ZigbeeGotoLabel(ZIGBEE_LABEL_PERMIT_JOIN_CLOSE);
|
||||
}
|
||||
ResponseCmndDone();
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
|
@ -22,8 +22,6 @@
|
|||
#include <A4988_Stepper.h>
|
||||
#define XDRV_25 25
|
||||
|
||||
enum A4988Errors { A4988_NO_ERROR, A4988_NO_JSON_COMMAND, A4988_INVALID_JSON, A4988_MOVE, A4988_ROTATE, A4988_TURN};
|
||||
|
||||
short A4988_dir_pin = pin[GPIO_MAX];
|
||||
short A4988_stp_pin = pin[GPIO_MAX];
|
||||
short A4988_ms1_pin = pin[GPIO_MAX];
|
||||
|
@ -59,97 +57,58 @@ void A4988Init(void)
|
|||
, A4988_ms3_pin );
|
||||
}
|
||||
|
||||
const char kA4988Commands[] PROGMEM = "|"
|
||||
"MOTOR";
|
||||
const char kA4988Commands[] PROGMEM = "Motor|" // prefix
|
||||
"Move|Rotate|Turn|MIS|SPR|RPM";
|
||||
|
||||
void (* const A4988Command[])(void) PROGMEM = { &CmndMOTOR};
|
||||
void (* const A4988Command[])(void) PROGMEM = {
|
||||
&CmndDoMove,&CmndDoRotate,&CmndDoTurn,&CmndSetMIS,&CmndSetSPR,&CmndSetRPM};
|
||||
|
||||
uint32_t MOTORCmndJson(void)
|
||||
{
|
||||
// MOTOR {"doMove":200}
|
||||
// MOTOR {"doRotate":360}
|
||||
// MOTOR {"doTurn":1.0}
|
||||
uint32_t returnValue =A4988_NO_JSON_COMMAND;
|
||||
|
||||
char parm_uc[12];
|
||||
char dataBufUc[XdrvMailbox.data_len];
|
||||
UpperCase(dataBufUc, XdrvMailbox.data);
|
||||
RemoveSpace(dataBufUc);
|
||||
if (strlen(dataBufUc) < 8) { returnValue =A4988_INVALID_JSON; }
|
||||
|
||||
DynamicJsonBuffer jsonBuf;
|
||||
JsonObject &json = jsonBuf.parseObject(dataBufUc);
|
||||
if (json.success()) {
|
||||
UpperCase_P(parm_uc, PSTR(D_JSON_MOTOR_SPR));
|
||||
if (json.containsKey(parm_uc)){
|
||||
int howManySteps =strtoul(json[parm_uc],nullptr,10);
|
||||
myA4988->setSPR(howManySteps);
|
||||
returnValue = A4988_NO_ERROR;
|
||||
}
|
||||
UpperCase_P(parm_uc, PSTR(D_JSON_MOTOR_RPM));
|
||||
if (json.containsKey(parm_uc)){
|
||||
int howManyRounds =strtoul(json[parm_uc],nullptr,10);
|
||||
myA4988->setRPM(howManyRounds);
|
||||
returnValue = A4988_NO_ERROR;
|
||||
}
|
||||
UpperCase_P(parm_uc, PSTR(D_JSON_MOTOR_MIS));
|
||||
if (json.containsKey(parm_uc)){
|
||||
short oneToSixteen =strtoul(json[parm_uc],nullptr,10);
|
||||
myA4988->setMIS(oneToSixteen);
|
||||
returnValue = A4988_NO_ERROR;
|
||||
}
|
||||
UpperCase_P(parm_uc, PSTR(D_JSON_MOTOR_MOVE));
|
||||
if (json.containsKey(parm_uc)){
|
||||
long stepsPlease = strtoul(json[parm_uc],nullptr,10);
|
||||
myA4988->doMove(stepsPlease);
|
||||
returnValue = A4988_MOVE;
|
||||
}
|
||||
UpperCase_P(parm_uc, PSTR(D_JSON_MOTOR_ROTATE));
|
||||
if (json.containsKey(parm_uc)){
|
||||
long degrsPlease = strtoul(json[parm_uc],nullptr,10);
|
||||
myA4988->doRotate(degrsPlease);
|
||||
returnValue = A4988_ROTATE;
|
||||
}
|
||||
UpperCase_P(parm_uc, PSTR(D_JSON_MOTOR_TURN));
|
||||
if (json.containsKey(parm_uc)){
|
||||
float turnsPlease = strtod(json[parm_uc],nullptr);
|
||||
myA4988->doTurn(turnsPlease);
|
||||
returnValue = A4988_TURN;
|
||||
}
|
||||
} else returnValue =A4988_INVALID_JSON;
|
||||
return returnValue;
|
||||
void CmndDoMove(void) {
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
long stepsPlease = strtoul(XdrvMailbox.data,nullptr,10);
|
||||
myA4988->doMove(stepsPlease);
|
||||
ResponseCmndDone();
|
||||
}
|
||||
}
|
||||
|
||||
void CmndMOTOR(void){
|
||||
uint32_t error;
|
||||
if (XdrvMailbox.data_len) {
|
||||
if (strstr(XdrvMailbox.data, "}") == nullptr) {
|
||||
error = A4988_NO_JSON_COMMAND;
|
||||
} else {
|
||||
error = MOTORCmndJson();
|
||||
}
|
||||
void CmndDoRotate(void) {
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
long degrsPlease = strtoul(XdrvMailbox.data,nullptr,10);
|
||||
myA4988->doRotate(degrsPlease);
|
||||
ResponseCmndDone();
|
||||
}
|
||||
A4988CmndResponse(error);
|
||||
}
|
||||
|
||||
void A4988CmndResponse(uint32_t error){
|
||||
switch (error) {
|
||||
case A4988_NO_JSON_COMMAND:
|
||||
ResponseCmndChar(PSTR("No command!"));
|
||||
break;
|
||||
case A4988_MOVE:
|
||||
ResponseCmndChar(PSTR("Stepping!"));
|
||||
break;
|
||||
case A4988_ROTATE:
|
||||
ResponseCmndChar(PSTR("Rotating!"));
|
||||
break;
|
||||
case A4988_TURN:
|
||||
ResponseCmndChar(PSTR("Turning!"));
|
||||
break;
|
||||
default: // A4988_NO_ERROR
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
|
|
|
@ -137,7 +137,8 @@ void Ssd1306Time(void)
|
|||
char line[12];
|
||||
|
||||
renderer->clearDisplay();
|
||||
renderer->setTextSize(2);
|
||||
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); // [ 12:34:56 ]
|
||||
renderer->println(line);
|
||||
|
|
|
@ -131,7 +131,8 @@ void SH1106Time(void)
|
|||
char line[12];
|
||||
|
||||
renderer->clearDisplay();
|
||||
renderer->setTextSize(2);
|
||||
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); // [ 12:34:56 ]
|
||||
renderer->println(line);
|
||||
|
|
|
@ -128,13 +128,13 @@ void HlwEvery200ms(void)
|
|||
|
||||
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 ; // W *10
|
||||
Energy.active_power = (float)hlw_w / 10;
|
||||
Energy.active_power[0] = (float)hlw_w / 10;
|
||||
Hlw.power_retry = 1; // Workaround issue #5161
|
||||
} else {
|
||||
if (Hlw.power_retry) {
|
||||
Hlw.power_retry--;
|
||||
} else {
|
||||
Energy.active_power = 0;
|
||||
Energy.active_power[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,19 +175,19 @@ void HlwEvery200ms(void)
|
|||
|
||||
if (Hlw.cf1_voltage_pulse_length && Energy.power_on) { // If powered on always provide voltage
|
||||
hlw_u = (Hlw.voltage_ratio * Settings.energy_voltage_calibration) / Hlw.cf1_voltage_pulse_length ; // V *10
|
||||
Energy.voltage = (float)hlw_u / 10;
|
||||
Energy.voltage[0] = (float)hlw_u / 10;
|
||||
} else {
|
||||
Energy.voltage = 0;
|
||||
Energy.voltage[0] = 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
Hlw.cf1_current_pulse_length = cf1_pulse_length;
|
||||
|
||||
if (Hlw.cf1_current_pulse_length && Energy.active_power) { // No current if no power being consumed
|
||||
if (Hlw.cf1_current_pulse_length && Energy.active_power[0]) { // No current if no power being consumed
|
||||
hlw_i = (Hlw.current_ratio * Settings.energy_current_calibration) / Hlw.cf1_current_pulse_length; // mA
|
||||
Energy.current = (float)hlw_i / 1000;
|
||||
Energy.current[0] = (float)hlw_i / 1000;
|
||||
} else {
|
||||
Energy.current = 0;
|
||||
Energy.current[0] = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -52,8 +52,9 @@ struct CSE {
|
|||
void CseReceived(void)
|
||||
{
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
||||
// 55 5A 02 F7 60 00 03 5A 00 40 10 04 8B 9F 51 A6 58 18 72 75 61 AC A1 30 - Power not valid (load below 5W)
|
||||
// 55 5A 02 F7 60 00 03 AB 00 40 10 02 60 5D 51 A6 58 03 E9 EF 71 0B 7A 36
|
||||
// F2 5A 02 F7 60 00 03 61 00 40 10 05 72 40 51 A6 58 63 10 1B E1 7F 4D 4E - F2 = Power cycle exceeds range - takes too long - No load
|
||||
// 55 5A 02 F7 60 00 03 5A 00 40 10 04 8B 9F 51 A6 58 18 72 75 61 AC A1 30 - 55 = Ok, 61 = Power not valid (load below 5W)
|
||||
// 55 5A 02 F7 60 00 03 AB 00 40 10 02 60 5D 51 A6 58 03 E9 EF 71 0B 7A 36 - 55 = Ok, 71 = Ok
|
||||
// Hd Id VCal---- Voltage- ICal---- Current- PCal---- Power--- Ad CF--- Ck
|
||||
|
||||
uint8_t header = serial_in_buffer[0];
|
||||
|
@ -93,19 +94,19 @@ void CseReceived(void)
|
|||
|
||||
if (Energy.power_on) { // Powered on
|
||||
if (adjustement & 0x40) { // Voltage valid
|
||||
Energy.voltage = (float)(Settings.energy_voltage_calibration * CSE_UREF) / (float)Cse.voltage_cycle;
|
||||
Energy.voltage[0] = (float)(Settings.energy_voltage_calibration * CSE_UREF) / (float)Cse.voltage_cycle;
|
||||
}
|
||||
if (adjustement & 0x10) { // Power valid
|
||||
Cse.power_invalid = 0;
|
||||
if ((header & 0xF2) == 0xF2) { // Power cycle exceeds range
|
||||
Energy.active_power = 0;
|
||||
Energy.active_power[0] = 0;
|
||||
} else {
|
||||
if (0 == Cse.power_cycle_first) { Cse.power_cycle_first = Cse.power_cycle; } // Skip first incomplete Cse.power_cycle
|
||||
if (Cse.power_cycle_first != Cse.power_cycle) {
|
||||
Cse.power_cycle_first = -1;
|
||||
Energy.active_power = (float)(Settings.energy_power_calibration * CSE_PREF) / (float)Cse.power_cycle;
|
||||
Energy.active_power[0] = (float)(Settings.energy_power_calibration * CSE_PREF) / (float)Cse.power_cycle;
|
||||
} else {
|
||||
Energy.active_power = 0;
|
||||
Energy.active_power[0] = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -113,21 +114,21 @@ void CseReceived(void)
|
|||
Cse.power_invalid++;
|
||||
} else {
|
||||
Cse.power_cycle_first = 0;
|
||||
Energy.active_power = 0; // Powered on but no load
|
||||
Energy.active_power[0] = 0; // Powered on but no load
|
||||
}
|
||||
}
|
||||
if (adjustement & 0x20) { // Current valid
|
||||
if (0 == Energy.active_power) {
|
||||
Energy.current = 0;
|
||||
if (0 == Energy.active_power[0]) {
|
||||
Energy.current[0] = 0;
|
||||
} else {
|
||||
Energy.current = (float)Settings.energy_current_calibration / (float)Cse.current_cycle;
|
||||
Energy.current[0] = (float)Settings.energy_current_calibration / (float)Cse.current_cycle;
|
||||
}
|
||||
}
|
||||
} else { // Powered off
|
||||
Cse.power_cycle_first = 0;
|
||||
Energy.voltage = 0;
|
||||
Energy.active_power = 0;
|
||||
Energy.current = 0;
|
||||
Energy.voltage[0] = 0;
|
||||
Energy.active_power[0] = 0;
|
||||
Energy.current[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -189,7 +190,7 @@ void CseEverySecond(void)
|
|||
} else {
|
||||
cf_frequency = Cse.cf_pulses - Cse.cf_pulses_last_time;
|
||||
}
|
||||
if (cf_frequency && Energy.active_power) {
|
||||
if (cf_frequency && Energy.active_power[0]) {
|
||||
unsigned long delta = (cf_frequency * Settings.energy_power_calibration) / 36;
|
||||
// prevent invalid load delta steps even checksum is valid (issue #5789):
|
||||
if (delta <= (3680*100/36) * 10 ) { // max load for S31/Pow R2: 3.68kW
|
||||
|
|
|
@ -56,6 +56,14 @@ TasmotaSerial *PzemSerial = nullptr;
|
|||
|
||||
/*********************************************************************************************/
|
||||
|
||||
struct PZEM {
|
||||
float energy = 0;
|
||||
uint8_t send_retry = 0;
|
||||
uint8_t read_state = 0; // Set address
|
||||
uint8_t phase = 0;
|
||||
uint8_t address = 0;
|
||||
} Pzem;
|
||||
|
||||
struct PZEMCommand {
|
||||
uint8_t command;
|
||||
uint8_t addr[4];
|
||||
|
@ -63,12 +71,12 @@ struct PZEMCommand {
|
|||
uint8_t crc;
|
||||
};
|
||||
|
||||
IPAddress pzem_ip(192, 168, 1, 1);
|
||||
|
||||
uint8_t PzemCrc(uint8_t *data)
|
||||
{
|
||||
uint16_t crc = 0;
|
||||
for (uint32_t i = 0; i < sizeof(PZEMCommand) -1; i++) crc += *data++;
|
||||
for (uint32_t i = 0; i < sizeof(PZEMCommand) -1; i++) {
|
||||
crc += *data++;
|
||||
}
|
||||
return (uint8_t)(crc & 0xFF);
|
||||
}
|
||||
|
||||
|
@ -77,7 +85,10 @@ void PzemSend(uint8_t cmd)
|
|||
PZEMCommand pzem;
|
||||
|
||||
pzem.command = cmd;
|
||||
for (uint32_t i = 0; i < sizeof(pzem.addr); i++) pzem.addr[i] = pzem_ip[i];
|
||||
pzem.addr[0] = 0; // Address 0.0.0.1
|
||||
pzem.addr[1] = 0;
|
||||
pzem.addr[2] = 0;
|
||||
pzem.addr[3] = ((PZEM_SET_ADDRESS == cmd) && Pzem.address) ? Pzem.address : 1 + Pzem.phase;
|
||||
pzem.data = 0;
|
||||
|
||||
uint8_t *bytes = (uint8_t*)&pzem;
|
||||
|
@ -85,6 +96,8 @@ void PzemSend(uint8_t cmd)
|
|||
|
||||
PzemSerial->flush();
|
||||
PzemSerial->write(bytes, sizeof(pzem));
|
||||
|
||||
Pzem.address = 0;
|
||||
}
|
||||
|
||||
bool PzemReceiveReady(void)
|
||||
|
@ -159,42 +172,55 @@ bool PzemRecieve(uint8_t resp, float *data)
|
|||
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 };
|
||||
|
||||
uint8_t pzem_read_state = 0;
|
||||
uint8_t pzem_sendRetry = 0;
|
||||
|
||||
void PzemEvery200ms(void)
|
||||
{
|
||||
bool data_ready = PzemReceiveReady();
|
||||
|
||||
if (data_ready) {
|
||||
float value = 0;
|
||||
if (PzemRecieve(pzem_responses[pzem_read_state], &value)) {
|
||||
if (PzemRecieve(pzem_responses[Pzem.read_state], &value)) {
|
||||
Energy.data_valid = 0;
|
||||
switch (pzem_read_state) {
|
||||
switch (Pzem.read_state) {
|
||||
case 1: // Voltage as 230.2V
|
||||
Energy.voltage = value;
|
||||
Energy.voltage[Pzem.phase] = value;
|
||||
break;
|
||||
case 2: // Current as 17.32A
|
||||
Energy.current = value;
|
||||
Energy.current[Pzem.phase] = value;
|
||||
break;
|
||||
case 3: // Power as 20W
|
||||
Energy.active_power = value;
|
||||
Energy.active_power[Pzem.phase] = value;
|
||||
break;
|
||||
case 4: // Total energy as 99999Wh
|
||||
EnergyUpdateTotal(value, false);
|
||||
Pzem.energy += value;
|
||||
if (Pzem.phase == Energy.phase_count -1) {
|
||||
EnergyUpdateTotal(Pzem.energy, false);
|
||||
Pzem.energy = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
pzem_read_state++;
|
||||
if (5 == pzem_read_state) pzem_read_state = 1;
|
||||
Pzem.read_state++;
|
||||
if (5 == Pzem.read_state) {
|
||||
Pzem.read_state = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == pzem_sendRetry || data_ready) {
|
||||
pzem_sendRetry = 5;
|
||||
PzemSend(pzem_commands[pzem_read_state]);
|
||||
if (0 == Pzem.send_retry || data_ready) {
|
||||
Pzem.phase++;
|
||||
if (Pzem.phase >= Energy.phase_count) {
|
||||
Pzem.phase = 0;
|
||||
}
|
||||
if (Pzem.address) {
|
||||
Pzem.read_state = 0; // Set address
|
||||
}
|
||||
Pzem.send_retry = 5;
|
||||
PzemSend(pzem_commands[Pzem.read_state]);
|
||||
}
|
||||
else {
|
||||
pzem_sendRetry--;
|
||||
Pzem.send_retry--;
|
||||
if ((Energy.phase_count > 1) && (0 == Pzem.send_retry) && (uptime < 30)) {
|
||||
Energy.phase_count--; // Decrement phases if no response after retry within 30 seconds after restart
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,7 +229,12 @@ 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);
|
||||
if (PzemSerial->begin(9600)) {
|
||||
if (PzemSerial->hardwareSerial()) { ClaimSerial(); }
|
||||
if (PzemSerial->hardwareSerial()) {
|
||||
ClaimSerial();
|
||||
}
|
||||
Energy.phase_count = 3; // Start off with three phases
|
||||
Pzem.phase = 2;
|
||||
Pzem.read_state = 1;
|
||||
} else {
|
||||
energy_flg = ENERGY_NONE;
|
||||
}
|
||||
|
@ -216,6 +247,18 @@ void PzemDrvInit(void)
|
|||
}
|
||||
}
|
||||
|
||||
bool PzemCommand(void)
|
||||
{
|
||||
bool serviced = true;
|
||||
|
||||
if (CMND_MODULEADDRESS == Energy.command_code) {
|
||||
Pzem.address = XdrvMailbox.payload; // Valid addresses are 1, 2 and 3
|
||||
}
|
||||
else serviced = false; // Unknown command
|
||||
|
||||
return serviced;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
@ -228,6 +271,9 @@ bool Xnrg03(uint8_t function)
|
|||
case FUNC_EVERY_200_MSECOND:
|
||||
if (PzemSerial) { PzemEvery200ms(); }
|
||||
break;
|
||||
case FUNC_COMMAND:
|
||||
result = PzemCommand();
|
||||
break;
|
||||
case FUNC_INIT:
|
||||
PzemSnsInit();
|
||||
break;
|
||||
|
|
|
@ -455,19 +455,19 @@ void McpParseData(void)
|
|||
mcp_line_frequency = McpExtractInt(mcp_buffer, 22, 2);
|
||||
|
||||
if (Energy.power_on) { // Powered on
|
||||
Energy.frequency = (float)mcp_line_frequency / 1000;
|
||||
Energy.voltage = (float)mcp_voltage_rms / 10;
|
||||
Energy.active_power = (float)mcp_active_power / 100;
|
||||
if (0 == Energy.active_power) {
|
||||
Energy.current = 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 = (float)mcp_current_rms / 10000;
|
||||
Energy.current[0] = (float)mcp_current_rms / 10000;
|
||||
}
|
||||
} else { // Powered off
|
||||
Energy.frequency = 0;
|
||||
Energy.voltage = 0;
|
||||
Energy.active_power = 0;
|
||||
Energy.current = 0;
|
||||
Energy.frequency[0] = 0;
|
||||
Energy.voltage[0] = 0;
|
||||
Energy.active_power[0] = 0;
|
||||
Energy.current[0] = 0;
|
||||
}
|
||||
Energy.data_valid = 0;
|
||||
}
|
||||
|
|
|
@ -36,63 +36,71 @@
|
|||
#include <TasmotaModbus.h>
|
||||
TasmotaModbus *PzemAcModbus;
|
||||
|
||||
/*
|
||||
uint16_t PzemCalculateCRC(uint8_t *buffer, uint8_t num)
|
||||
{
|
||||
uint16_t crc = 0xFFFF;
|
||||
for (uint32_t i = 0; i < num; i++) {
|
||||
crc ^= buffer[i];
|
||||
for (uint32_t j = 8; j; j--) {
|
||||
if ((crc & 0x0001) != 0) { // If the LSB is set
|
||||
crc >>= 1; // Shift right and XOR 0xA001
|
||||
crc ^= 0xA001;
|
||||
} else { // Else LSB is not set
|
||||
crc >>= 1; // Just shift right
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
*/
|
||||
struct PZEMAC {
|
||||
float 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)
|
||||
{
|
||||
static uint8_t send_retry = 0;
|
||||
|
||||
bool data_ready = PzemAcModbus->ReceiveReady();
|
||||
|
||||
if (data_ready) {
|
||||
uint8_t buffer[26];
|
||||
uint8_t buffer[30]; // At least 5 + (2 * 10) = 25
|
||||
|
||||
uint8_t error = PzemAcModbus->ReceiveBuffer(buffer, 10);
|
||||
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, sizeof(buffer));
|
||||
uint8_t registers = 10;
|
||||
if (ADDR_RECEIVE == PzemAc.address_step) {
|
||||
registers = 2; // Need 1 byte extra as response is F8 06 00 02 00 01 FD A3
|
||||
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(D_LOG_DEBUG "PzemAc response error %d"), error);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAC: PzemAc %d error %d"), PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, error);
|
||||
} else {
|
||||
// if ((PzemCalculateCRC(buffer, 23)) == ((buffer[24] << 8) | buffer[23])) {
|
||||
Energy.data_valid = 0;
|
||||
Energy.data_valid = 0;
|
||||
if (10 == registers) {
|
||||
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
||||
// 01 04 14 08 D1 00 6C 00 00 00 F4 00 00 00 26 00 00 01 F4 00 64 00 00 51 34
|
||||
// Id Cc Sz Volt- Current---- Power------ Energy----- Frequ PFact Alarm Crc--
|
||||
Energy.voltage = (float)((buffer[3] << 8) + buffer[4]) / 10.0; // 6553.0 V
|
||||
Energy.current = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; // 4294967.000 A
|
||||
Energy.active_power = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; // 429496729.0 W
|
||||
Energy.frequency = (float)((buffer[17] << 8) + buffer[18]) / 10.0; // 50.0 Hz
|
||||
Energy.power_factor = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00
|
||||
float energy = (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); // 4294967295 Wh
|
||||
Energy.voltage[PzemAc.phase] = (float)((buffer[3] << 8) + buffer[4]) / 10.0; // 6553.0 V
|
||||
Energy.current[PzemAc.phase] = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; // 4294967.000 A
|
||||
Energy.active_power[PzemAc.phase] = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; // 429496729.0 W
|
||||
Energy.frequency[PzemAc.phase] = (float)((buffer[17] << 8) + buffer[18]) / 10.0; // 50.0 Hz
|
||||
Energy.power_factor[PzemAc.phase] = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00
|
||||
|
||||
EnergyUpdateTotal(energy, false);
|
||||
// }
|
||||
PzemAc.energy += (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); // 4294967295 Wh
|
||||
if (PzemAc.phase == Energy.phase_count -1) {
|
||||
EnergyUpdateTotal(PzemAc.energy, false);
|
||||
PzemAc.energy = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == send_retry || data_ready) {
|
||||
send_retry = 5;
|
||||
PzemAcModbus->Send(PZEM_AC_DEVICE_ADDRESS, 0x04, 0, 10);
|
||||
if (0 == PzemAc.send_retry || data_ready) {
|
||||
PzemAc.phase++;
|
||||
if (PzemAc.phase >= Energy.phase_count) {
|
||||
PzemAc.phase = 0;
|
||||
}
|
||||
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 {
|
||||
send_retry--;
|
||||
PzemAc.send_retry--;
|
||||
if ((Energy.phase_count > 1) && (0 == PzemAc.send_retry) && (uptime < 30)) {
|
||||
Energy.phase_count--; // Decrement phases if no response after retry within 30 seconds after restart
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,6 +110,8 @@ void PzemAcSnsInit(void)
|
|||
uint8_t result = PzemAcModbus->Begin(9600);
|
||||
if (result) {
|
||||
if (2 == result) { ClaimSerial(); }
|
||||
Energy.phase_count = 3; // Start off with three phases
|
||||
PzemAc.phase = 2;
|
||||
} else {
|
||||
energy_flg = ENERGY_NONE;
|
||||
}
|
||||
|
@ -114,6 +124,19 @@ void PzemAcDrvInit(void)
|
|||
}
|
||||
}
|
||||
|
||||
bool PzemAcCommand(void)
|
||||
{
|
||||
bool serviced = true;
|
||||
|
||||
if (CMND_MODULEADDRESS == Energy.command_code) {
|
||||
PzemAc.address = XdrvMailbox.payload; // Valid addresses are 1, 2 and 3
|
||||
PzemAc.address_step = ADDR_SEND;
|
||||
}
|
||||
else serviced = false; // Unknown command
|
||||
|
||||
return serviced;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
@ -126,6 +149,9 @@ bool Xnrg05(uint8_t function)
|
|||
case FUNC_ENERGY_EVERY_SECOND:
|
||||
if (uptime > 4) { PzemAcEverySecond(); } // Fix start up issue #5875
|
||||
break;
|
||||
case FUNC_COMMAND:
|
||||
result = PzemAcCommand();
|
||||
break;
|
||||
case FUNC_INIT:
|
||||
PzemAcSnsInit();
|
||||
break;
|
||||
|
|
|
@ -36,41 +36,69 @@
|
|||
#include <TasmotaModbus.h>
|
||||
TasmotaModbus *PzemDcModbus;
|
||||
|
||||
struct PZEMDC {
|
||||
float 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)
|
||||
{
|
||||
static uint8_t send_retry = 0;
|
||||
|
||||
bool data_ready = PzemDcModbus->ReceiveReady();
|
||||
|
||||
if (data_ready) {
|
||||
uint8_t buffer[22];
|
||||
uint8_t buffer[26]; // At least 5 + (2 * 8) = 21
|
||||
|
||||
uint8_t error = PzemDcModbus->ReceiveBuffer(buffer, 8);
|
||||
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, sizeof(buffer));
|
||||
uint8_t registers = 8;
|
||||
if (ADDR_RECEIVE == PzemDc.address_step) {
|
||||
registers = 2; // Need 1 byte extra as response is F8 06 00 02 00 01 FD A3
|
||||
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(D_LOG_DEBUG "PzemDc response error %d"), error);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PDC: PzemDc %d error %d"), PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, error);
|
||||
} else {
|
||||
Energy.data_valid = 0;
|
||||
if (8 == registers) {
|
||||
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
||||
// 01 04 10 05 40 00 0A 00 0D 00 00 00 02 00 00 00 00 00 00 D6 29
|
||||
// Id Cc Sz Volt- Curre Power------ Energy----- HiAlm LoAlm Crc--
|
||||
Energy.voltage = (float)((buffer[3] << 8) + buffer[4]) / 100.0; // 655.00 V
|
||||
Energy.current = (float)((buffer[5] << 8) + buffer[6]) / 100.0; // 655.00 A
|
||||
Energy.active_power = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; // 429496729.0 W
|
||||
float energy = (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); // 4294967295 Wh
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
||||
// 01 04 10 05 40 00 0A 00 0D 00 00 00 02 00 00 00 00 00 00 D6 29
|
||||
// Id Cc Sz Volt- Curre Power------ Energy----- HiAlm LoAlm Crc--
|
||||
Energy.voltage[PzemDc.channel] = (float)((buffer[3] << 8) + buffer[4]) / 100.0; // 655.00 V
|
||||
Energy.current[PzemDc.channel] = (float)((buffer[5] << 8) + buffer[6]) / 100.0; // 655.00 A
|
||||
Energy.active_power[PzemDc.channel] = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; // 429496729.0 W
|
||||
|
||||
EnergyUpdateTotal(energy, false);
|
||||
PzemDc.energy += (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); // 4294967295 Wh
|
||||
if (PzemDc.channel == Energy.phase_count -1) {
|
||||
EnergyUpdateTotal(PzemDc.energy, false);
|
||||
PzemDc.energy = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == send_retry || data_ready) {
|
||||
send_retry = 5;
|
||||
PzemDcModbus->Send(PZEM_DC_DEVICE_ADDRESS, 0x04, 0, 8);
|
||||
if (0 == PzemDc.send_retry || data_ready) {
|
||||
PzemDc.channel++;
|
||||
if (PzemDc.channel >= Energy.phase_count) {
|
||||
PzemDc.channel = 0;
|
||||
}
|
||||
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 {
|
||||
send_retry--;
|
||||
PzemDc.send_retry--;
|
||||
if ((Energy.phase_count > 1) && (0 == PzemDc.send_retry) && (uptime < 30)) {
|
||||
Energy.phase_count--; // Decrement channels if no response after retry within 30 seconds after restart
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,6 +109,8 @@ void PzemDcSnsInit(void)
|
|||
if (result) {
|
||||
if (2 == result) { ClaimSerial(); }
|
||||
Energy.type_dc = true;
|
||||
Energy.phase_count = 3; // Start off with three channels
|
||||
PzemDc.channel = 2;
|
||||
} else {
|
||||
energy_flg = ENERGY_NONE;
|
||||
}
|
||||
|
@ -93,6 +123,19 @@ void PzemDcDrvInit(void)
|
|||
}
|
||||
}
|
||||
|
||||
bool PzemDcCommand(void)
|
||||
{
|
||||
bool serviced = true;
|
||||
|
||||
if (CMND_MODULEADDRESS == Energy.command_code) {
|
||||
PzemDc.address = XdrvMailbox.payload; // Valid addresses are 1, 2 and 3
|
||||
PzemDc.address_step = ADDR_SEND;
|
||||
}
|
||||
else serviced = false; // Unknown command
|
||||
|
||||
return serviced;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
@ -105,6 +148,9 @@ bool Xnrg06(uint8_t function)
|
|||
case FUNC_ENERGY_EVERY_SECOND:
|
||||
if (uptime > 4) { PzemDcEverySecond(); } // Fix start up issue #5875
|
||||
break;
|
||||
case FUNC_COMMAND:
|
||||
result = PzemDcCommand();
|
||||
break;
|
||||
case FUNC_INIT:
|
||||
PzemDcSnsInit();
|
||||
break;
|
||||
|
|
|
@ -36,14 +36,22 @@
|
|||
|
||||
#define ADE7953_ADDR 0x38
|
||||
|
||||
const uint8_t Ade7953Registers[] {
|
||||
0x1B, // RMS current channel B (Relay 1)
|
||||
0x13, // Active power channel B
|
||||
0x11, // Apparent power channel B
|
||||
0x15, // Reactive power channel B
|
||||
0x1A, // RMS current channel A (Relay 2)
|
||||
0x12, // Active power channel A
|
||||
0x10, // Apparent power channel A
|
||||
0x14, // Reactive power channel A
|
||||
0x1C // RMS voltage (Both relays)
|
||||
};
|
||||
|
||||
struct Ade7953 {
|
||||
uint32_t active_power = 0;
|
||||
uint32_t active_power1 = 0;
|
||||
uint32_t active_power2 = 0;
|
||||
uint32_t current_rms = 0;
|
||||
uint32_t current_rms1 = 0;
|
||||
uint32_t current_rms2 = 0;
|
||||
uint32_t voltage_rms = 0;
|
||||
uint32_t current_rms[2] = { 0, 0 };
|
||||
uint32_t active_power[2] = { 0, 0 };
|
||||
uint8_t init_step = 0;
|
||||
} Ade7953;
|
||||
|
||||
|
@ -80,7 +88,7 @@ void Ade7953Write(uint16_t reg, uint32_t val)
|
|||
}
|
||||
}
|
||||
|
||||
uint32_t Ade7953Read(uint16_t reg)
|
||||
int32_t Ade7953Read(uint16_t reg)
|
||||
{
|
||||
uint32_t response = 0;
|
||||
|
||||
|
@ -109,48 +117,65 @@ void Ade7953Init(void)
|
|||
|
||||
void Ade7953GetData(void)
|
||||
{
|
||||
int32_t active_power;
|
||||
int32_t reg[2][4];
|
||||
for (uint32_t i = 0; i < sizeof(Ade7953Registers); i++) {
|
||||
int32_t value = Ade7953Read(0x300 + Ade7953Registers[i]);
|
||||
if (8 == i) {
|
||||
Ade7953.voltage_rms = value; // RMS voltage (Both relays)
|
||||
} else {
|
||||
reg[i >> 2][i &3] = value;
|
||||
}
|
||||
}
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ADE: %d, [%d, %d, %d, %d], [%d, %d, %d, %d]"),
|
||||
Ade7953.voltage_rms, reg[0][0], reg[0][1], reg[0][2], reg[0][3],
|
||||
reg[1][0], reg[1][1], reg[1][2], reg[1][3]);
|
||||
|
||||
Ade7953.voltage_rms = Ade7953Read(0x31C); // Both relays
|
||||
Ade7953.current_rms1 = Ade7953Read(0x31B); // Relay 1
|
||||
if (Ade7953.current_rms1 < 2000) { // No load threshold (20mA)
|
||||
Ade7953.current_rms1 = 0;
|
||||
Ade7953.active_power1 = 0;
|
||||
} else {
|
||||
active_power = (int32_t)Ade7953Read(0x313) * -1; // Relay 1
|
||||
Ade7953.active_power1 = (active_power > 0) ? active_power : 0;
|
||||
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) { // No load threshold (20mA)
|
||||
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]);
|
||||
}
|
||||
}
|
||||
Ade7953.current_rms2 = Ade7953Read(0x31A); // Relay 2
|
||||
if (Ade7953.current_rms2 < 2000) { // No load threshold (20mA)
|
||||
Ade7953.current_rms2 = 0;
|
||||
Ade7953.active_power2 = 0;
|
||||
} else {
|
||||
active_power = (int32_t)Ade7953Read(0x312); // Relay 2
|
||||
Ade7953.active_power2 = (active_power > 0) ? active_power : 0;
|
||||
}
|
||||
// First phase only supports accumulated Current and Power
|
||||
Ade7953.current_rms = Ade7953.current_rms1 + Ade7953.current_rms2;
|
||||
Ade7953.active_power = Ade7953.active_power1 + Ade7953.active_power2;
|
||||
|
||||
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, I %d + %d = %d, P %d + %d = %d"),
|
||||
Ade7953.voltage_rms, Ade7953.current_rms1, Ade7953.current_rms2, Ade7953.current_rms, Ade7953.active_power1, Ade7953.active_power2, Ade7953.active_power);
|
||||
Ade7953.voltage_rms, 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) { // Powered on
|
||||
Energy.voltage = (float)Ade7953.voltage_rms / Settings.energy_voltage_calibration;
|
||||
Energy.active_power = (float)Ade7953.active_power / (Settings.energy_power_calibration / 10);
|
||||
if (0 == Energy.active_power) {
|
||||
Energy.current = 0;
|
||||
} else {
|
||||
Energy.current = (float)Ade7953.current_rms / (Settings.energy_current_calibration * 10);
|
||||
Energy.voltage[0] = (float)Ade7953.voltage_rms / Settings.energy_voltage_calibration;
|
||||
|
||||
for (uint32_t channel = 0; channel < 2; channel++) {
|
||||
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 { // Powered off
|
||||
Energy.voltage = 0;
|
||||
Energy.active_power = 0;
|
||||
Energy.current = 0;
|
||||
Energy.voltage[0] = 0;
|
||||
for (uint32_t channel = 0; channel < 2; channel++) {
|
||||
Energy.current[channel] = 0;
|
||||
Energy.active_power[channel] = 0;
|
||||
Energy.reactive_power[channel] = 0;
|
||||
Energy.apparent_power[channel] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (Ade7953.active_power) {
|
||||
Energy.kWhtoday_delta += ((Ade7953.active_power * (100000 / (Settings.energy_power_calibration / 10))) / 3600);
|
||||
if (active_power_sum) {
|
||||
Energy.kWhtoday_delta += ((active_power_sum * (100000 / (Settings.energy_power_calibration / 10))) / 3600);
|
||||
EnergyUpdateToday();
|
||||
}
|
||||
}
|
||||
|
@ -179,6 +204,10 @@ void Ade7953DrvInit(void)
|
|||
}
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "ADE7953", ADE7953_ADDR);
|
||||
Ade7953.init_step = 2;
|
||||
|
||||
Energy.phase_count = 2; // Handle two channels as two phases
|
||||
Energy.voltage_common = true; // Use common voltage
|
||||
|
||||
energy_flg = XNRG_07;
|
||||
}
|
||||
}
|
||||
|
@ -188,6 +217,7 @@ bool Ade7953Command(void)
|
|||
{
|
||||
bool serviced = true;
|
||||
|
||||
uint32_t channel = (2 == XdrvMailbox.index) ? 1 : 0;
|
||||
uint32_t value = (uint32_t)(CharToFloat(XdrvMailbox.data) * 100); // 1.23 = 123
|
||||
|
||||
if (CMND_POWERCAL == Energy.command_code) {
|
||||
|
@ -203,9 +233,9 @@ bool Ade7953Command(void)
|
|||
// Service in xdrv_03_energy.ino
|
||||
}
|
||||
else if (CMND_POWERSET == Energy.command_code) {
|
||||
if (XdrvMailbox.data_len && Ade7953.active_power) {
|
||||
if (XdrvMailbox.data_len && Ade7953.active_power[channel]) {
|
||||
if ((value > 100) && (value < 200000)) { // Between 1W and 2000W
|
||||
Settings.energy_power_calibration = (Ade7953.active_power * 1000) / value; // 0.00 W
|
||||
Settings.energy_power_calibration = (Ade7953.active_power[channel] * 1000) / value; // 0.00 W
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -217,9 +247,9 @@ bool Ade7953Command(void)
|
|||
}
|
||||
}
|
||||
else if (CMND_CURRENTSET == Energy.command_code) {
|
||||
if (XdrvMailbox.data_len && Ade7953.current_rms) {
|
||||
if (XdrvMailbox.data_len && Ade7953.current_rms[channel]) {
|
||||
if ((value > 2000) && (value < 1000000)) { // Between 20mA and 10A
|
||||
Settings.energy_current_calibration = ((Ade7953.current_rms * 100) / value) * 100; // 0.00 mA
|
||||
Settings.energy_current_calibration = ((Ade7953.current_rms[channel] * 100) / value) * 100; // 0.00 mA
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,15 +77,14 @@ void SDM120Every250ms(void)
|
|||
bool data_ready = Sdm120Modbus->ReceiveReady();
|
||||
|
||||
if (data_ready) {
|
||||
uint8_t buffer[9];
|
||||
uint8_t buffer[14]; // At least 5 + (2 * 2) = 9
|
||||
|
||||
uint32_t error = Sdm120Modbus->ReceiveBuffer(buffer, 2);
|
||||
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, sizeof(buffer));
|
||||
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Sdm120Modbus->ReceiveCount());
|
||||
|
||||
if (error) {
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SDM120 response error %d"), error);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: SDM120 error %d"), error);
|
||||
} else {
|
||||
uint32_t max_table =
|
||||
Energy.data_valid = 0;
|
||||
|
||||
// 0 1 2 3 4 5 6 7 8
|
||||
|
@ -99,31 +98,31 @@ void SDM120Every250ms(void)
|
|||
|
||||
switch(Sdm120.read_state) {
|
||||
case 0:
|
||||
Energy.voltage = value; // 230.2 V
|
||||
Energy.voltage[0] = value; // 230.2 V
|
||||
break;
|
||||
|
||||
case 1:
|
||||
Energy.current = value; // 1.260 A
|
||||
Energy.current[0] = value; // 1.260 A
|
||||
break;
|
||||
|
||||
case 2:
|
||||
Energy.active_power = value; // -196.3 W
|
||||
Energy.active_power[0] = value; // -196.3 W
|
||||
break;
|
||||
|
||||
case 3:
|
||||
Energy.apparent_power = value; // 223.4 VA
|
||||
Energy.apparent_power[0] = value; // 223.4 VA
|
||||
break;
|
||||
|
||||
case 4:
|
||||
Energy.reactive_power = value; // 92.2
|
||||
Energy.reactive_power[0] = value; // 92.2
|
||||
break;
|
||||
|
||||
case 5:
|
||||
Energy.power_factor = value; // -0.91
|
||||
Energy.power_factor[0] = value; // -0.91
|
||||
break;
|
||||
|
||||
case 6:
|
||||
Energy.frequency = value; // 50.0 Hz
|
||||
Energy.frequency[0] = value; // 50.0 Hz
|
||||
break;
|
||||
|
||||
case 7:
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
xnrg_09_dds2382.ino - Hiking DDS238-2 Modbus energy meter support for Sonoff-Tasmota
|
||||
|
||||
Copyright (C) 2019 Matteo Campanella - based on the work of Gennaro Tortone
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_ENERGY_SENSOR
|
||||
#ifdef USE_DDS2382
|
||||
/*********************************************************************************************\
|
||||
* Hiking DDS238-2 Modbus energy meter
|
||||
*
|
||||
* Based on: https://github.com/reaper7/SDM_Energy_Meter
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XNRG_09 9
|
||||
|
||||
#ifndef DDS2382_SPEED
|
||||
#define DDS2382_SPEED 9600 // default dds2382 Modbus address
|
||||
#endif
|
||||
#ifndef DDS2382_ADDR
|
||||
#define DDS2382_ADDR 1 // default dds2382 Modbus address
|
||||
#endif
|
||||
|
||||
#include <TasmotaModbus.h>
|
||||
TasmotaModbus *Dds2382Modbus;
|
||||
|
||||
uint8_t Dds2382_send_retry = 0;
|
||||
|
||||
void Dds2382EverySecond(void)
|
||||
{
|
||||
bool data_ready = Dds2382Modbus->ReceiveReady();
|
||||
|
||||
if (data_ready) {
|
||||
uint8_t buffer[46]; // At least 5 + (2 * 18) = 41
|
||||
|
||||
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 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
||||
// SA FC BC EnergyTotal ExportActiv ImportActiv Volta Curre APowe RPowe PFact Frequ Crc--
|
||||
|
||||
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; // 1.00
|
||||
Energy.frequency[0] = (float)((buffer[37] << 8) + buffer[38]) / 100.0; // 50.0 Hz
|
||||
Energy.export_active = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[13] << 8) + buffer[14]) / 100.0; // 429496729.0 W
|
||||
// float energy_total = (float)((buffer[3] << 24) + (buffer[4] << 16) + (buffer[5] << 8) + buffer[6]) / 100.0; // 429496729.0 W
|
||||
float import_active = (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[17] << 8) + buffer[18]) / 100.0; // 429496729.0 W
|
||||
|
||||
EnergyUpdateTotal(import_active, false); // 484.708 kWh
|
||||
}
|
||||
} // end data ready
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
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 // USE_DDS2382
|
||||
#endif // USE_ENERGY_SENSOR
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
xnrg_10_sdm630.ino - Eastron SDM630-Modbus energy meter support for Sonoff-Tasmota
|
||||
|
||||
Copyright (C) 2019 Gennaro Tortone and 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_ENERGY_SENSOR
|
||||
#ifdef USE_SDM630_2
|
||||
/*********************************************************************************************\
|
||||
* Eastron SDM630-Modbus energy meter
|
||||
*
|
||||
* Based on: https://github.com/reaper7/SDM_Energy_Meter
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XNRG_10 10
|
||||
|
||||
// can be user defined in my_user_config.h
|
||||
#ifndef SDM630_SPEED
|
||||
#define SDM630_SPEED 9600 // default SDM630 Modbus address
|
||||
#endif
|
||||
// can be user defined in my_user_config.h
|
||||
#ifndef SDM630_ADDR
|
||||
#define SDM630_ADDR 1 // default SDM630 Modbus address
|
||||
#endif
|
||||
|
||||
#include <TasmotaModbus.h>
|
||||
TasmotaModbus *Sdm630Modbus;
|
||||
|
||||
const uint16_t sdm630_start_addresses[] {
|
||||
0x0000, // L1 - SDM630_VOLTAGE [V]
|
||||
0x0002, // L2 - SDM630_VOLTAGE [V]
|
||||
0x0004, // L3 - SDM630_VOLTAGE [V]
|
||||
0x0006, // L1 - SDM630_CURRENT [A]
|
||||
0x0008, // L2 - SDM630_CURRENT [A]
|
||||
0x000A, // L3 - SDM630_CURRENT [A]
|
||||
0x000C, // L1 - SDM630_POWER [W]
|
||||
0x000E, // L2 - SDM630_POWER [W]
|
||||
0x0010, // L3 - SDM630_POWER [W]
|
||||
0x0018, // L1 - SDM630_REACTIVE_POWER [VAR]
|
||||
0x001A, // L2 - SDM630_REACTIVE_POWER [VAR]
|
||||
0x001C, // L3 - SDM630_REACTIVE_POWER [VAR]
|
||||
0x001E, // L1 - SDM630_POWER_FACTOR
|
||||
0x0020, // L2 - SDM630_POWER_FACTOR
|
||||
0x0022, // L3 - SDM630_POWER_FACTOR
|
||||
0x0156 // Total - SDM630_TOTAL_ACTIVE_ENERGY [Wh]
|
||||
};
|
||||
|
||||
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]; // At least 5 + (2 * 2) = 9
|
||||
|
||||
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 1 2 3 4 5 6 7 8
|
||||
// SA FC BC Fh Fl Sh Sl Cl Ch
|
||||
// 01 04 04 43 66 33 34 1B 38 = 230.2 Volt
|
||||
float value;
|
||||
((uint8_t*)&value)[3] = buffer[3]; // Get float values
|
||||
((uint8_t*)&value)[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;
|
||||
}
|
||||
}
|
||||
} // end data ready
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
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 // USE_SDM630_2
|
||||
#endif // USE_ENERGY_SENSOR
|
|
@ -196,12 +196,12 @@ void CmndAdc(void)
|
|||
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));
|
||||
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 "\":["));
|
||||
Response_P(PSTR("{\"" D_CMND_ADCS "\":{"));
|
||||
bool jsflg = false;
|
||||
char stemp1[TOPSZ];
|
||||
for (uint32_t i = 0; i < ADC0_END; i++) {
|
||||
|
@ -209,9 +209,9 @@ void CmndAdcs(void)
|
|||
ResponseAppend_P(PSTR(","));
|
||||
}
|
||||
jsflg = true;
|
||||
ResponseAppend_P(PSTR("\"%d (%s)\""), i, GetTextIndexed(stemp1, sizeof(stemp1), i, kAdc0Names));
|
||||
ResponseAppend_P(PSTR("\"%d\":\"%s\""), i, GetTextIndexed(stemp1, sizeof(stemp1), i, kAdc0Names));
|
||||
}
|
||||
ResponseAppend_P(PSTR("]}"));
|
||||
ResponseJsonEndEnd();
|
||||
}
|
||||
|
||||
void CmndAdcParam(void)
|
||||
|
|
|
@ -134,7 +134,7 @@ void Sgp30Show(bool json)
|
|||
if (global_update) {
|
||||
ResponseAppend_P(PSTR(",\"" D_JSON_AHUM "\":%s"),abs_hum);
|
||||
}
|
||||
ResponseAppend_P(PSTR("}"));
|
||||
ResponseJsonEnd();
|
||||
#ifdef USE_DOMOTICZ
|
||||
if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, sgp.eCO2);
|
||||
#endif // USE_DOMOTICZ
|
||||
|
|
|
@ -142,13 +142,13 @@ bool Xsns33(uint8_t function)
|
|||
break;
|
||||
case FUNC_EVERY_SECOND:
|
||||
TIME_T tmpTime;
|
||||
if (!ds3231ReadStatus && DS3231chipDetected && Rtc.utc_time < 1451602800 ) { // We still did not sync with NTP (time not valid) , so, read time from DS3231
|
||||
if (!ds3231ReadStatus && DS3231chipDetected && Rtc.utc_time < START_VALID_TIME ) { // We still did not sync with NTP (time not valid) , so, read time from DS3231
|
||||
ntp_force_sync = true; //force to sync with ntp
|
||||
Rtc.utc_time = ReadFromDS3231(); //we read UTC TIME from DS3231
|
||||
// from this line, we just copy the function from "void RtcSecond()" at the support.ino ,line 2143 and above
|
||||
// We need it to set rules etc.
|
||||
BreakTime(Rtc.utc_time, tmpTime);
|
||||
if (Rtc.utc_time < 1451602800 ) {
|
||||
if (Rtc.utc_time < START_VALID_TIME ) {
|
||||
ds3231ReadStatus = true; //if time in DS3231 is valid, do not update again
|
||||
}
|
||||
RtcTime.year = tmpTime.year + 1970;
|
||||
|
@ -156,13 +156,13 @@ bool Xsns33(uint8_t function)
|
|||
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"),
|
||||
GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str());
|
||||
if (Rtc.local_time < 1451602800) { // 2016-01-01
|
||||
if (Rtc.local_time < START_VALID_TIME) { // 2016-01-01
|
||||
rules_flag.time_init = 1;
|
||||
} else {
|
||||
rules_flag.time_set = 1;
|
||||
}
|
||||
}
|
||||
else if (!ds3231WriteStatus && DS3231chipDetected && Rtc.utc_time > 1451602800 && abs(Rtc.utc_time - ReadFromDS3231()) > 60) {//if time is valid and is drift from RTC in more that 60 second
|
||||
else if (!ds3231WriteStatus && DS3231chipDetected && Rtc.utc_time > START_VALID_TIME && abs(Rtc.utc_time - ReadFromDS3231()) > 60) {//if time is valid and is drift from RTC in more that 60 second
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("Write Time TO DS3231 from NTP (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"),
|
||||
GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str());
|
||||
SetDS3231Time (Rtc.utc_time); //update the DS3231 time
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
*/
|
||||
|
||||
#ifdef USE_SML_M
|
||||
//
|
||||
|
||||
#define XSNS_53 53
|
||||
|
||||
|
@ -30,13 +29,14 @@
|
|||
#define SML_BAUDRATE 9600
|
||||
|
||||
// send this every N seconds (for meters that only send data on demand)
|
||||
// not longer supported, use scripting instead
|
||||
//#define SML_SEND_SEQ
|
||||
|
||||
// debug counter input to led for counter1 and 2
|
||||
//#define DEBUG_CNT_LED1 2
|
||||
//#define DEBUG_CNT_LED1 2
|
||||
|
||||
// use analog optical counter sensor with AD Converter ADS1115 (not yet)
|
||||
// use analog optical counter sensor with AD Converter ADS1115 (not yet functional)
|
||||
//#define ANALOG_OPTO_SENSOR
|
||||
// fototransistor with pullup at A0, A1 of ADS1115 A3 and +3.3V
|
||||
// level and amplification are automatically set
|
||||
|
@ -44,7 +44,7 @@
|
|||
|
||||
#include <TasmotaSerial.h>
|
||||
|
||||
// use special no wait serial driver
|
||||
// use special no wait serial driver, should be always on
|
||||
#define SPECIAL_SS
|
||||
|
||||
// addresses a bug in meter DWS74
|
||||
|
@ -147,6 +147,9 @@ struct METER_DESC {
|
|||
uint8_t max_index;
|
||||
};
|
||||
|
||||
// this descriptor method is no longer supported
|
||||
// but still functional for simple meters
|
||||
// use scripting method instead
|
||||
// meter list , enter new meters here
|
||||
//=====================================================
|
||||
#define EHZ161_0 1
|
||||
|
@ -391,7 +394,7 @@ const uint8_t meter[]=
|
|||
#define METERS_USED 1
|
||||
struct METER_DESC const meter_desc[METERS_USED]={
|
||||
[0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}};
|
||||
// 2 Richtungszähler EHZ SML 8 bit 9600 baud, binär
|
||||
// 2 direction meter EHZ SML 8 bit 9600 baud, binary
|
||||
const uint8_t meter[]=
|
||||
//0x77,0x07,0x01,0x00,0x01,0x08,0x00,0xff
|
||||
"1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|"
|
||||
|
@ -407,7 +410,7 @@ const uint8_t meter[]=
|
|||
"1,77070100000009ff@#," D_METERNR ",," DJ_METERNR ",0";
|
||||
#endif
|
||||
|
||||
// Beispiel für einen OBIS Stromzähler und einen Gaszähler + Wasserzähler
|
||||
// example OBIS power meter + gas and water counter
|
||||
#if METER==COMBO3b
|
||||
#undef METERS_USED
|
||||
#define METERS_USED 3
|
||||
|
@ -416,14 +419,14 @@ struct METER_DESC const meter_desc[METERS_USED]={
|
|||
[1]={14,'c',0,50,"Gas"}, // GPIO14 gas counter
|
||||
[2]={1,'c',0,10,"Wasser"}}; // water counter
|
||||
|
||||
// 3 Zähler definiert
|
||||
// 3 meters defined
|
||||
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|"
|
||||
|
||||
// bei gaszählern (countern) muss der Vergleichsstring so aussehen wie hier
|
||||
// with counters the comparison string must be exactly this string
|
||||
"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";
|
||||
|
@ -435,8 +438,8 @@ const uint8_t meter[]=
|
|||
#define METERS_USED 3
|
||||
|
||||
struct METER_DESC const meter_desc[METERS_USED]={
|
||||
[0]={1,'c',0,10,"H20",-1,1,0}, // GPIO1 Wasser Zähler
|
||||
[1]={4,'c',0,50,"GAS",-1,1,0}, // GPIO4 gas Zähler
|
||||
[0]={1,'c',0,10,"H20",-1,1,0}, // GPIO1 water counter
|
||||
[1]={4,'c',0,50,"GAS",-1,1,0}, // GPIO4 gas counter
|
||||
[2]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; // SML harware serial RX pin
|
||||
|
||||
const uint8_t meter[]=
|
||||
|
@ -491,10 +494,12 @@ const uint8_t meter[]=
|
|||
|
||||
// median filter eliminates outliers, but uses much RAM and CPU cycles
|
||||
// 672 bytes extra RAM with MAX_VARS = 16
|
||||
//#define USE_MEDIAN_FILTER
|
||||
// default compile on, but must be enabled by descriptor flag 16
|
||||
// may be undefined if RAM must be saved
|
||||
#define USE_SML_MEDIAN_FILTER
|
||||
|
||||
// max number of vars , may be adjusted
|
||||
#define MAX_VARS 16
|
||||
#define MAX_VARS 20
|
||||
// max number of meters , may be adjusted
|
||||
#define MAX_METERS 5
|
||||
double meter_vars[MAX_VARS];
|
||||
|
@ -506,6 +511,7 @@ uint8_t meters_used;
|
|||
|
||||
struct METER_DESC const *meter_desc_p;
|
||||
const uint8_t *meter_p;
|
||||
uint8_t meter_spos[MAX_METERS];
|
||||
|
||||
// software serial pointers
|
||||
TasmotaSerial *meter_ss[MAX_METERS];
|
||||
|
@ -520,24 +526,58 @@ char meter_id[MAX_METERS][METER_ID_SIZE];
|
|||
|
||||
#define EBUS_SYNC 0xaa
|
||||
#define EBUS_ESC 0xa9
|
||||
uint8_t ebus_pos;
|
||||
uint8_t mbus_pos;
|
||||
|
||||
#ifdef USE_MEDIAN_FILTER
|
||||
uint8_t sml_send_blocks;
|
||||
uint8_t sml_100ms_cnt;
|
||||
uint8_t sml_desc_cnt;
|
||||
|
||||
#ifdef USE_SML_MEDIAN_FILTER
|
||||
// median filter, should be odd size
|
||||
#define MEDIAN_SIZE 5
|
||||
struct MEDIAN_FILTER {
|
||||
struct SML_MEDIAN_FILTER {
|
||||
double buffer[MEDIAN_SIZE];
|
||||
int8_t index;
|
||||
} sml_mf[MAX_VARS];
|
||||
|
||||
|
||||
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; hcnt<len/2+1; hcnt++) {
|
||||
for (uint8_t mcnt=0; mcnt<len; mcnt++) {
|
||||
flg=0;
|
||||
for (uint8_t icnt=0; icnt<index; icnt++) {
|
||||
if (ind[icnt]==mcnt) {
|
||||
flg=1;
|
||||
}
|
||||
}
|
||||
if (!flg) {
|
||||
if (array[mcnt]<min) {
|
||||
min=array[mcnt];
|
||||
mind=mcnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
ind[index]=mind;
|
||||
index++;
|
||||
min=FLT_MAX;
|
||||
}
|
||||
return array[ind[len/2]];
|
||||
}
|
||||
|
||||
|
||||
// calc median
|
||||
double median(struct MEDIAN_FILTER* mf, double in) {
|
||||
double tbuff[MEDIAN_SIZE],tmp;
|
||||
uint8_t flag;
|
||||
double sml_median(struct SML_MEDIAN_FILTER* mf, double in) {
|
||||
//double tbuff[MEDIAN_SIZE],tmp;
|
||||
//uint8_t flag;
|
||||
mf->buffer[mf->index]=in;
|
||||
mf->index++;
|
||||
if (mf->index>=MEDIAN_SIZE) mf->index=0;
|
||||
|
||||
return sml_median_array(mf->buffer,MEDIAN_SIZE);
|
||||
/*
|
||||
// sort list and take median
|
||||
memmove(tbuff,mf->buffer,sizeof(tbuff));
|
||||
for (byte ocnt=0; ocnt<MEDIAN_SIZE; ocnt++) {
|
||||
|
@ -553,6 +593,7 @@ double median(struct MEDIAN_FILTER* mf, double in) {
|
|||
if (!flag) break;
|
||||
}
|
||||
return tbuff[MEDIAN_SIZE/2];
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -714,8 +755,7 @@ uint16_t ADS1115::read_register(uint8_t reg)
|
|||
|
||||
uint8_t result = Wire.requestFrom((int)m_address, 2, 1);
|
||||
if (result != 2) {
|
||||
Wire.flush();
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t val;
|
||||
|
@ -1056,9 +1096,8 @@ uint8_t hexnibble(char chr) {
|
|||
|
||||
uint8_t sb_counter;
|
||||
|
||||
// because orig CharToDouble was defective
|
||||
// fixed in Tasmota 6.4.1.19 20190222
|
||||
double xCharToDouble(const char *str)
|
||||
// need double precision in this driver
|
||||
double CharToDouble(const char *str)
|
||||
{
|
||||
// simple ascii to double, because atof or strtod are too large
|
||||
char strbuf[24];
|
||||
|
@ -1145,9 +1184,16 @@ uint8_t ebus_CalculateCRC( uint8_t *Data, uint16_t DataLen ) {
|
|||
return Crc;
|
||||
}
|
||||
|
||||
void sml_empty_receiver(uint32_t meters) {
|
||||
while (meter_ss[meters]->available()) {
|
||||
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') {
|
||||
if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='p') {
|
||||
// shift in
|
||||
for (count=0; count<SML_BSIZ-1; count++) {
|
||||
smltbuf[meters][count]=smltbuf[meters][count+1];
|
||||
|
@ -1162,43 +1208,48 @@ void sml_shift_in(uint32_t meters,uint32_t shard) {
|
|||
} else if (meter_desc_p[meters].type=='r') {
|
||||
smltbuf[meters][SML_BSIZ-1]=iob;
|
||||
} else if (meter_desc_p[meters].type=='m') {
|
||||
smltbuf[meters][mbus_pos] = iob;
|
||||
mbus_pos++;
|
||||
if (mbus_pos>=9) {
|
||||
smltbuf[meters][meter_spos[meters]] = iob;
|
||||
meter_spos[meters]++;
|
||||
if (meter_spos[meters]>=9) {
|
||||
SML_Decode(meters);
|
||||
mbus_pos=0;
|
||||
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) {
|
||||
// should be end of telegramm
|
||||
// QQ,ZZ,PB,SB,NN ..... CRC, ACK SYNC
|
||||
if (ebus_pos>4+5) {
|
||||
if (meter_spos[meters]>4+5) {
|
||||
// get telegramm lenght
|
||||
uint8_t tlen=smltbuf[meters][4]+5;
|
||||
// test crc
|
||||
if (smltbuf[meters][tlen]=ebus_CalculateCRC(smltbuf[meters],tlen)) {
|
||||
ebus_esc(smltbuf[meters],tlen);
|
||||
//eBus_analyze();
|
||||
// XX0204UUSS@
|
||||
SML_Decode(meters);
|
||||
//AddLog_P(LOG_LEVEL_INFO, PSTR("ebus block found"));
|
||||
//ebus_set_timeout();
|
||||
} else {
|
||||
// crc error
|
||||
//AddLog_P(LOG_LEVEL_INFO, PSTR("ebus crc error"));
|
||||
}
|
||||
}
|
||||
ebus_pos=0;
|
||||
meter_spos[meters]=0;
|
||||
return;
|
||||
}
|
||||
smltbuf[meters][ebus_pos] = iob;
|
||||
ebus_pos++;
|
||||
if (ebus_pos>=SML_BSIZ) {
|
||||
ebus_pos=0;
|
||||
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') SML_Decode(meters);
|
||||
if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='p') SML_Decode(meters);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1356,14 +1407,22 @@ void SML_Decode(uint8_t index) {
|
|||
found=0;
|
||||
}
|
||||
} else {
|
||||
// ebus mbus or raw
|
||||
// ebus mbus pzem or raw
|
||||
// XXHHHHSSUU
|
||||
if (*mp=='x' && *(mp+1)=='x') {
|
||||
//ignore
|
||||
mp+=2;
|
||||
cp++;
|
||||
} else if (*mp=='u' && *(mp+1)=='u' && *(mp+2)=='u' && *(mp+3)=='u'){
|
||||
uint16_t val = *cp|(*(cp+1)<<8);
|
||||
} 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[0]|(cp[1]<<8);
|
||||
mbus_dval=val;
|
||||
ebus_dval=val;
|
||||
mp+=4;
|
||||
cp+=2;
|
||||
|
@ -1383,13 +1442,44 @@ void SML_Decode(uint8_t index) {
|
|||
ebus_dval=val;
|
||||
mp+=2;
|
||||
}
|
||||
else if (*mp=='f' && *(mp+1)=='f' && *(mp+2)=='f' && *(mp+3)=='f' && *(mp+4)=='f' && *(mp+5)=='f' && *(mp+6)=='f' && *(mp+7)=='f') {
|
||||
uint32_t val= (*(cp+0)<<24)|(*(cp+1)<<16)|(*(cp+2)<<8)|(*(cp+3)<<0);
|
||||
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)) {
|
||||
// reverse word float
|
||||
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++);
|
||||
|
@ -1419,15 +1509,15 @@ void SML_Decode(uint8_t index) {
|
|||
}
|
||||
} else {
|
||||
double dval;
|
||||
if (meter_desc_p[mindex].type!='e' && meter_desc_p[mindex].type!='r' && meter_desc_p[mindex].type!='m') {
|
||||
if (meter_desc_p[mindex].type!='e' && meter_desc_p[mindex].type!='r' && meter_desc_p[mindex].type!='m' && meter_desc_p[mindex].type!='p') {
|
||||
// get numeric values
|
||||
if (meter_desc_p[mindex].type=='o' || meter_desc_p[mindex].type=='c') {
|
||||
dval=xCharToDouble((char*)cp);
|
||||
dval=CharToDouble((char*)cp);
|
||||
} else {
|
||||
dval=sml_getvalue(cp,mindex);
|
||||
}
|
||||
} else {
|
||||
// ebus or mbus
|
||||
// ebus pzem or mbus or raw
|
||||
if (*mp=='b') {
|
||||
mp++;
|
||||
uint8_t shift=*mp&7;
|
||||
|
@ -1449,17 +1539,28 @@ void SML_Decode(uint8_t index) {
|
|||
//AddLog_P2(LOG_LEVEL_INFO, PSTR(">> %s"),mp);
|
||||
mp++;
|
||||
} else {
|
||||
dval=ebus_dval;
|
||||
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_MEDIAN_FILTER
|
||||
meter_vars[vindex]=median(&sml_mf[vindex],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
|
||||
//AddLog_P2(LOG_LEVEL_INFO, PSTR(">> %s"),mp);
|
||||
// get scaling factor
|
||||
double fac=xCharToDouble((char*)mp);
|
||||
double fac=CharToDouble((char*)mp);
|
||||
meter_vars[vindex]/=fac;
|
||||
SML_Immediate_MQTT((const char*)mp,vindex,mindex);
|
||||
}
|
||||
|
@ -1733,10 +1834,15 @@ void SML_Init(void) {
|
|||
meter_desc_p=meter_desc;
|
||||
meter_p=meter;
|
||||
|
||||
sml_desc_cnt=0;
|
||||
|
||||
for (uint32_t cnt=0;cnt<MAX_VARS;cnt++) {
|
||||
meter_vars[cnt]=0;
|
||||
}
|
||||
|
||||
for (uint32_t cnt=0;cnt<MAX_METERS;cnt++) {
|
||||
meter_spos[cnt]=0;
|
||||
}
|
||||
|
||||
#ifdef USE_SCRIPT
|
||||
|
||||
|
@ -1744,17 +1850,20 @@ void SML_Init(void) {
|
|||
if (script_meter_desc[cnt].txmem) {
|
||||
free(script_meter_desc[cnt].txmem);
|
||||
script_meter_desc[cnt].txmem=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t meter_script=Run_Scripter(">M",-2,0);
|
||||
if (meter_script==99) {
|
||||
// use script definition
|
||||
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') {
|
||||
|
@ -1770,7 +1879,9 @@ void SML_Init(void) {
|
|||
}
|
||||
if (mlen==0) return; // missing end #
|
||||
script_meter=(uint8_t*)calloc(mlen,1);
|
||||
if (!script_meter) return;
|
||||
if (!script_meter) {
|
||||
goto dddef_exit;
|
||||
}
|
||||
tp=script_meter;
|
||||
goto next_line;
|
||||
}
|
||||
|
@ -1784,14 +1895,18 @@ void SML_Init(void) {
|
|||
// add descriptor +1,1,c,0,10,H20
|
||||
//toLogEOL(">>",lp);
|
||||
lp++;
|
||||
uint8_t index=*lp&7;
|
||||
index=*lp&7;
|
||||
lp+=2;
|
||||
if (index<1 || index>meters_used) goto next_line;
|
||||
index--;
|
||||
uint8_t srcpin=strtol(lp,&lp,10);
|
||||
srcpin=strtol(lp,&lp,10);
|
||||
if (Gpio_used(srcpin)) {
|
||||
AddLog_P(LOG_LEVEL_INFO, PSTR("gpio double define!"));
|
||||
return;
|
||||
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;
|
||||
|
@ -1815,6 +1930,10 @@ void SML_Init(void) {
|
|||
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);
|
||||
|
@ -1838,6 +1957,7 @@ void SML_Init(void) {
|
|||
}
|
||||
script_meter_desc[index].index=0;
|
||||
script_meter_desc[index].max_index=tx_entries;
|
||||
sml_send_blocks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1879,6 +1999,7 @@ next_line:
|
|||
}
|
||||
#endif
|
||||
|
||||
init10:
|
||||
typedef void (*function)();
|
||||
function counter_callbacks[] = {SML_CounterUpd1,SML_CounterUpd2,SML_CounterUpd3,SML_CounterUpd4};
|
||||
uint8_t cindex=0;
|
||||
|
@ -1916,7 +2037,7 @@ next_line:
|
|||
} else {
|
||||
// serial input, init
|
||||
#ifdef SPECIAL_SS
|
||||
if (meter_desc_p[meters].type=='m') {
|
||||
if (meter_desc_p[meters].type=='m' || meter_desc_p[meters].type=='p') {
|
||||
meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1);
|
||||
} else {
|
||||
meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1,1);
|
||||
|
@ -2028,26 +2149,38 @@ char *SML_Get_Sequence(char *cp,uint32_t index) {
|
|||
}
|
||||
}
|
||||
|
||||
uint8_t sml_250ms_cnt;
|
||||
|
||||
|
||||
void SML_Check_Send(void) {
|
||||
sml_250ms_cnt++;
|
||||
for (uint32_t cnt=0; cnt<meters_used; cnt++) {
|
||||
sml_100ms_cnt++;
|
||||
char *cp;
|
||||
for (uint32_t cnt=sml_desc_cnt; cnt<meters_used; cnt++) {
|
||||
if (script_meter_desc[cnt].trxpin>=0 && script_meter_desc[cnt].txmem) {
|
||||
if ((sml_250ms_cnt%script_meter_desc[cnt].tsecs)==0) {
|
||||
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++;
|
||||
}
|
||||
char *cp=SML_Get_Sequence(script_meter_desc[cnt].txmem,script_meter_desc[cnt].index);
|
||||
SML_Send_Seq(cnt,cp);
|
||||
//AddLog_P2(LOG_LEVEL_INFO, PSTR(">> %s"),cp);
|
||||
cp=SML_Get_Sequence(script_meter_desc[cnt].txmem,script_meter_desc[cnt].index);
|
||||
//SML_Send_Seq(cnt,cp);
|
||||
} else {
|
||||
SML_Send_Seq(cnt,script_meter_desc[cnt].txmem);
|
||||
cp=script_meter_desc[cnt].txmem;
|
||||
//SML_Send_Seq(cnt,cp);
|
||||
sml_desc_cnt++;
|
||||
}
|
||||
//AddLog_P2(LOG_LEVEL_INFO, PSTR(">> %s"),cp);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2066,7 +2199,7 @@ uint8_t sml_hexnibble(char chr) {
|
|||
// send sequence every N Seconds
|
||||
void SML_Send_Seq(uint32_t meter,char *seq) {
|
||||
uint8_t sbuff[32];
|
||||
uint8_t *ucp=sbuff,slen;
|
||||
uint8_t *ucp=sbuff,slen=0;
|
||||
char *cp=seq;
|
||||
while (*cp) {
|
||||
if (!*cp || !*(cp+1)) break;
|
||||
|
@ -2086,6 +2219,15 @@ void SML_Send_Seq(uint32_t meter,char *seq) {
|
|||
*ucp++=highByte(crc);
|
||||
slen+=4;
|
||||
}
|
||||
if (script_meter_desc[meter].type=='p') {
|
||||
*ucp++=0xc0;
|
||||
*ucp++=0xa8;
|
||||
*ucp++=1;
|
||||
*ucp++=1;
|
||||
*ucp++=0;
|
||||
*ucp++=SML_PzemCrc(sbuff,6);
|
||||
slen+=6;
|
||||
}
|
||||
meter_ss[meter]->write(sbuff,slen);
|
||||
}
|
||||
#endif // USE_SCRIPT
|
||||
|
@ -2107,6 +2249,11 @@ uint16_t MBUS_calculateCRC(uint8_t *frame, uint8_t num) {
|
|||
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);
|
||||
}
|
||||
/*
|
||||
// for odd parity init with 1
|
||||
uint8_t CalcEvenParity(uint8_t data) {
|
||||
|
@ -2200,7 +2347,7 @@ bool Xsns53(byte function) {
|
|||
else SML_Poll();
|
||||
break;
|
||||
#ifdef USE_SCRIPT
|
||||
case FUNC_EVERY_250_MSECOND:
|
||||
case FUNC_EVERY_100_MSECOND:
|
||||
SML_Check_Send();
|
||||
break;
|
||||
#endif // USE_SCRIPT
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
VER = '2.3.0033'
|
||||
VER = '2.3.0034'
|
||||
|
||||
"""
|
||||
decode-config.py - Backup/Restore Sonoff-Tasmota configuration data
|
||||
|
@ -998,7 +998,26 @@ Setting_6_6_0_10['flag2'][0].update ({
|
|||
})
|
||||
Setting_6_6_0_10['flag3'][0].pop('tuya_show_dimmer',None)
|
||||
# ======================================================================
|
||||
Setting_6_6_0_11 = copy.deepcopy(Setting_6_6_0_10)
|
||||
Setting_6_6_0_11.update ({
|
||||
'ina226_r_shunt': ('<H', 0xE20, ([4], None, ('Power', '"Sensor54 {}1 {}".format(#,$)')) ),
|
||||
'ina226_i_fs': ('<H', 0xE28, ([4], None, ('Power', '"Sensor54 {}2 {}".format(#,$)')) ),
|
||||
})
|
||||
# ======================================================================
|
||||
Setting_6_6_0_12 = copy.deepcopy(Setting_6_6_0_11)
|
||||
Setting_6_6_0_12.update ({
|
||||
'register8_ENERGY_TARIFF1_ST': ('B', 0x1D6, (None, None, ('Power', '"Tariff1 {},{}".format($,@["register8_ENERGY_TARIFF1_DS"])')) ),
|
||||
'register8_ENERGY_TARIFF2_ST': ('B', 0x1D7, (None, None, ('Power', '"Tariff2 {},{}".format($,@["register8_ENERGY_TARIFF2_DS"])')) ),
|
||||
'register8_ENERGY_TARIFF1_DS': ('B', 0x1D8, (None, None, ('Power', None)) ),
|
||||
'register8_ENERGY_TARIFF2_DS': ('B', 0x1D9, (None, None, ('Power', None)) ),
|
||||
})
|
||||
Setting_6_6_0_12['flag3'][0].update ({
|
||||
'energy_weekend': ('<L', (0x3A0,1,20), (None, None, ('Power', '"Tariff9 {}".format($)')) ),
|
||||
})
|
||||
# ======================================================================
|
||||
Settings = [
|
||||
(0x606000C,0x1000, Setting_6_6_0_12),
|
||||
(0x606000B,0x1000, Setting_6_6_0_11),
|
||||
(0x606000A,0x1000, Setting_6_6_0_10),
|
||||
(0x6060009,0x1000, Setting_6_6_0_9),
|
||||
(0x6060008,0x1000, Setting_6_6_0_8),
|
||||
|
@ -1696,8 +1715,11 @@ def GetSettingsCrc(dobj):
|
|||
"""
|
||||
if isinstance(dobj, bytearray):
|
||||
dobj = str(dobj)
|
||||
version, size, setting = GetTemplateSetting(dobj)
|
||||
if version < 0x06060007 or version > 0x0606000A:
|
||||
size = 3584
|
||||
crc = 0
|
||||
for i in range(0, len(dobj)):
|
||||
for i in range(0, size):
|
||||
if not i in [14,15]: # Skip crc
|
||||
byte = ord(dobj[i])
|
||||
crc += byte * (i+1)
|
||||
|
@ -1705,6 +1727,28 @@ def GetSettingsCrc(dobj):
|
|||
return crc & 0xffff
|
||||
|
||||
|
||||
def GetSettingsCrc32(dobj):
|
||||
"""
|
||||
Return binary config data calclulated crc32
|
||||
|
||||
@param dobj:
|
||||
decrypted binary config data
|
||||
|
||||
@return:
|
||||
4 byte unsigned integer crc value
|
||||
|
||||
"""
|
||||
if isinstance(dobj, bytearray):
|
||||
dobj = str(dobj)
|
||||
crc = 0
|
||||
for i in range(0, len(dobj)-4):
|
||||
crc ^= ord(dobj[i])
|
||||
for j in range(0, 8):
|
||||
crc = (crc >> 1) ^ (-int(crc & 1) & 0xEDB88320);
|
||||
|
||||
return ~crc & 0xffffffff
|
||||
|
||||
|
||||
def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, datadef, arraydef, validate, cmd, group, tasmotacmnd, converter, readconverter, writeconverter"):
|
||||
|
||||
"""
|
||||
|
@ -2583,8 +2627,16 @@ def Bin2Mapping(decode_cfg):
|
|||
cfg_crc = GetField(decode_cfg, 'cfg_crc', setting['cfg_crc'], raw=True)
|
||||
else:
|
||||
cfg_crc = GetSettingsCrc(decode_cfg)
|
||||
if cfg_crc != GetSettingsCrc(decode_cfg):
|
||||
exit(ExitCode.DATA_CRC_ERROR, 'Data CRC error, read 0x{:x} should be 0x{:x}'.format(cfg_crc, GetSettingsCrc(decode_cfg)), type_=LogType.WARNING, doexit=not args.ignorewarning,line=inspect.getlineno(inspect.currentframe()))
|
||||
if 'cfg_crc32' in setting:
|
||||
cfg_crc32 = GetField(decode_cfg, 'cfg_crc32', setting['cfg_crc32'], raw=True)
|
||||
else:
|
||||
cfg_crc32 = GetSettingsCrc32(decode_cfg)
|
||||
if version < 0x0606000B:
|
||||
if cfg_crc != GetSettingsCrc(decode_cfg):
|
||||
exit(ExitCode.DATA_CRC_ERROR, 'Data CRC error, read 0x{:4x} should be 0x{:4x}'.format(cfg_crc, GetSettingsCrc(decode_cfg)), type_=LogType.WARNING, doexit=not args.ignorewarning,line=inspect.getlineno(inspect.currentframe()))
|
||||
else:
|
||||
if cfg_crc32 != GetSettingsCrc32(decode_cfg):
|
||||
exit(ExitCode.DATA_CRC_ERROR, 'Data CRC32 error, read 0x{:8x} should be 0x{:8x}'.format(cfg_crc32, GetSettingsCrc32(decode_cfg)), type_=LogType.WARNING, doexit=not args.ignorewarning,line=inspect.getlineno(inspect.currentframe()))
|
||||
|
||||
# get valuemapping
|
||||
valuemapping = GetField(decode_cfg, None, (setting,0,(None, None, (INTERNAL, None))))
|
||||
|
@ -2615,6 +2667,9 @@ def Bin2Mapping(decode_cfg):
|
|||
}
|
||||
if 'cfg_crc' in setting:
|
||||
valuemapping['header']['template'].update({'size': cfg_size})
|
||||
if 'cfg_crc32' in setting:
|
||||
valuemapping['header']['template'].update({'crc32': cfg_crc32})
|
||||
valuemapping['header']['data'].update({'crc32': hex(GetSettingsCrc32(decode_cfg))})
|
||||
if 'version' in setting:
|
||||
valuemapping['header']['data'].update({'version': hex(cfg_version)})
|
||||
|
||||
|
@ -2660,6 +2715,9 @@ def Mapping2Bin(decode_cfg, jsonconfig, filename=""):
|
|||
if 'cfg_crc' in setting:
|
||||
crc = GetSettingsCrc(_buffer)
|
||||
struct.pack_into(setting['cfg_crc'][0], _buffer, setting['cfg_crc'][1], crc)
|
||||
if 'cfg_crc32' in setting:
|
||||
crc32 = GetSettingsCrc32(_buffer)
|
||||
struct.pack_into(setting['cfg_crc32'][0], _buffer, setting['cfg_crc32'][1], crc32)
|
||||
return _buffer
|
||||
|
||||
else:
|
||||
|
|
|
@ -167,7 +167,7 @@ a_features = [[
|
|||
"USE_MAX31865","USE_CHIRP","USE_SOLAX_X1","USE_PAJ7620"
|
||||
],[
|
||||
"USE_BUZZER","USE_RDM6300","USE_IBEACON","USE_SML_M",
|
||||
"USE_INA226","USE_A4988_Stepper","","",
|
||||
"USE_INA226","USE_A4988_Stepper","USE_DDS2382","",
|
||||
"","","","",
|
||||
"","","","",
|
||||
"","","","",
|
||||
|
|
Loading…
Reference in New Issue