Merge pull request #8 from arendst/development

Update
This commit is contained in:
Jason2866 2019-09-18 13:12:02 +02:00 committed by GitHub
commit 3f7b4999c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
84 changed files with 3046 additions and 1353 deletions

View File

@ -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:_

View File

@ -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)**

View File

@ -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)**

View File

@ -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).

1
.gitignore vendored
View File

@ -10,6 +10,7 @@
sonoff/user_config_override.h
build
firmware.map
firmware.asm
## Visual Studio Code specific ######
.vscode

View File

@ -1,6 +1,6 @@
language: python
python:
- '2.7'
- '3.7'
sudo: false
install:
- pip install -U platformio

View File

@ -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 );

View File

@ -1,6 +1,6 @@
{
"name": "TasmotaModbus",
"version": "1.1.1",
"version": "1.2.0",
"keywords": [
"serial", "io", "TasmotaModbus"
],

View File

@ -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.

View File

@ -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)

View File

@ -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

9
pio/obj-dump.py Normal file
View File

@ -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])

View File

@ -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

View File

@ -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)

View File

@ -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&deg;%c{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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_

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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 "А"

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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 "А"

View File

@ -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

View File

@ -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 "安"

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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";

View File

@ -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++) {

View File

@ -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

View File

@ -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_

View File

@ -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_

View File

@ -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
\*********************************************************************************************/

View File

@ -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';

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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();
}
/********************************************************************************************/

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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
\*********************************************************************************************/

View File

@ -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();
}
}
/*********************************************************************************************\

View File

@ -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);

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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
}
}
}

View File

@ -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:

123
sonoff/xnrg_09_dds2382.ino Normal file
View File

@ -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

215
sonoff/xnrg_10_sdm630.ino Normal file
View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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","",
"","","","",
"","","","",
"","","","",