Merge branch 'arendst/development' into development

This commit is contained in:
reloxx13 2018-09-28 21:06:21 +02:00
commit 17c71055e3
53 changed files with 3987 additions and 480 deletions

View File

@ -4,6 +4,9 @@ about: Create a report to help us improve
---
**IMPORTANT NOTICE**
If you do not complete the template below it is likely that your issue will not be addressed. When providing information about your issue please be as extensive as possible so that it can be solved by as little as possible responses.
**Describe the bug**
_A clear and concise description of what the bug is._
@ -11,6 +14,7 @@ _A clear and concise description of what the bug is._
_Also, make sure these boxes are checked [x] before submitting your issue - Thank you!_
- [ ] _Searched the problem in issues and in the wiki_
- [ ] _Hardware used_ :
- [ ] _Development/Compiler/Upload tools used_ :
- [ ] _Provide the output of command_``status 0`` :
```
STATUS 0 OUTPUT HERE

View File

@ -4,12 +4,16 @@ about: Users Troubleshooting Help
---
**IMPORTANT NOTICE**
If you do not complete the template below it is likely that your issue will not be addressed. When providing information about your issue please be as extensive as possible so that it can be solved by as little as possible responses.
Make sure these boxes are checked [x] before submitting your issue - Thank you!
- [ ] Searched the problem in issues (https://github.com/arendst/Sonoff-Tasmota/issues)
- [ ] 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)
- [ ] Development/Compiler/Upload tools used :
- [ ] Hardware used :
- [ ] Provide the output of command ``status 0`` :
```

View File

@ -59,7 +59,7 @@ See [Community](https://groups.google.com/d/forum/sonoffusers) for forum.<br />
See [Chat](https://discord.gg/Ks2Kzd4) for more user experience.
The following devices are supported:
- [iTead Sonoff Basic](https://www.itead.cc/smart-home/sonoff-wifi-wireless-switch-1.html)
- [iTead Sonoff Basic (R2)](https://www.itead.cc/smart-home/sonoff-wifi-wireless-switch-1.html)
- [iTead Sonoff RF](https://www.itead.cc/smart-home/sonoff-rf.html)
- [iTead Sonoff SV](https://www.itead.cc/smart-home/sonoff-sv.html)<img src="https://github.com/arendst/arendst.github.io/blob/master/media/sonoff_th.jpg" width="250" align="right" />
- [iTead Sonoff TH10/TH16 with temperature sensor](https://www.itead.cc/smart-home/sonoff-th.html)
@ -90,10 +90,15 @@ The following devices are supported:
- [MagicHome PWM LED controller](https://github.com/arendst/Sonoff-Tasmota/wiki/MagicHome-LED-strip-controller)
- AriLux AL-LC01, AL-LC06 and AL-LC11 PWM LED controller
- [Supla device - Espablo-inCan mod. for electrical Installation box](https://forum.supla.org/viewtopic.php?f=33&t=2188)
- [BlitzWolf BW-SHP2 Smart Socket with 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)
- [BlitzWolf BW-SHP2 Smart Socket with 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)<img src="https://github.com/arendst/arendst.github.io/blob/master/media/shelly2_small_250a.png" width="250" align="right" />
- [Luani HVIO board](https://luani.de/projekte/esp8266-hvio/)
- Xiaomi-Phillips Bulbs
- Wemos D1 mini, NodeMcu and Ledunia
- [Wemos D1 mini](https://wiki.wemos.cc/products:d1:d1_mini)
- [HuaFan Smart Socket](HuaFan-Smart-Socket)
- [Hyleton-313 Smart Plug](Hyleton-313-Smart-Plug)
- [Allterco Shelly 1](https://shelly.cloud/shelly1-open-source/)
- [Allterco Shelly 2 with Energy Monitoring](https://shelly.cloud/shelly2/)
- NodeMcu and Ledunia
- [KS-602 based switches like GresaTek, Jesiya, NewRice, Lyasi etc](https://ucexperiment.wordpress.com/2017/11/14/reprogramming-a-lyasi-wifi-wall-switch-with-esp8285/)
### Contribute
You can contribute to Sonoff-Tasmota by

View File

@ -16,6 +16,7 @@ env_default = sonoff
;env_default = sonoff-classic
;env_default = sonoff-knx
;env_default = sonoff-sensors
;env_default = sonoff-display
;env_default = sonoff-BG
;env_default = sonoff-BR
;env_default = sonoff-CN
@ -167,6 +168,20 @@ upload_resetmethod = ${common.upload_resetmethod}
upload_speed = ${common.upload_speed}
extra_scripts = ${common.extra_scripts}
[env:sonoff-display]
platform = ${common.platform}
framework = ${common.framework}
board = ${common.board}
board_build.flash_mode = ${common.board_build.flash_mode}
board_build.f_cpu = ${common.board_build.f_cpu}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} -DUSE_DISPLAYS
monitor_speed = ${common.monitor_speed}
upload_port = ${common.upload_port}
upload_resetmethod = ${common.upload_resetmethod}
upload_speed = ${common.upload_speed}
extra_scripts = ${common.extra_scripts}
[env:sonoff-BG]
platform = ${common.platform}
framework = ${common.framework}

View File

@ -1,10 +1,53 @@
/* 6.2.1.3 20180907
/* 6.2.1.9 20180928
* Add Apparent Power and Reactive Power to Energy Monitoring devices (#251)
* Add RF Receiver control to module MagicHome to be used on Arilux LC10 (#3792)
*
* 6.2.1.8 20180926
* Change status JSON message providing more switch and retain information
* Change pinmode for no-pullup defined switches to pullup when configured as switchmode PUSHBUTTON (=3 and up) (#3896)
* Add delay after restart before processing rule sensor data (#3811)
* Fix Home Assistant forced light discovery (#3908)
* Add rule triggers SWITCH1#BOOT and POWER1#BOOT (#3904, #3910)
* Add support for Neo Coolcam Wifi Smart Power Plug
*
* 6.2.1.7 20180925
* Remove restart after ntpserver change and force NTP re-sync (#3890)
* Release full Shelly2 support
* Released tools/decode-config.py by Norbert Richter to decode configuration data. See file for information
* Add define USE_DISPLAYS for selecting image sonoff-display
*
* 6.2.1.6 20180922
* Removed commands PowerCal, VoltageCal and CurrentCal as more functionality is provided by commands PowerSet, VoltageSet and CurrentSet
* Allow decimals as input to commands PowerSet, VoltageSet and CurrentSet
* Add support for PCA9685 12bit 16pin hardware PWM driver (#3866)
* Add power value below 5W to Sonoff Pow R2 and S31 (#3745)
* Add force_update to Home Assistant discovery (#3873)
* Fix rule trigger POWER1#STATE execution after restart and SetOption0 is 0 (#3856)
* Disable serial logging on Shelly2 as serial is being used by energy monitoring (#3878)
* Fix Shelly2 wrong FrequencySet calculation and add input range checks (#3882)
*
* 6.2.1.5 20180921
* Add authentication to HTTP web pages
* Add energy monitoring to Shelly2 (#2789)
* Rewrite TSL2561 driver to fix some issues (#3681)
*
* 6.2.1.4 20180916
* Add command SerialSend5 to send raw serial data like "A5074100545293"
* Update MCP230xx driver
* Update Czech translation
* Update MP3 driver (#3800)
* Add userid/password option to decode-status.py (#3796)
* Fix syslog when emulation is selected (#2109, #3784)
* Fix Pzem2 compilation error (#3766, #3767)
* Add uncalibrated energy monitoring to Shelly2 (#2789)
*
* 6.2.1.3 20180907
* Change web Configure Module GPIO drop down list order for better readability
* Fix showing Period Power in energy threshold messages
* Fix ButtonRetain to not use default topic for clearing retain messages (#3737)
* Add sleep to Nova Fitness SDS01X sensor (#2841, #3724, #3749)
* Add Analog input AD0 enabled to sonoff-sensors.bin (#3756, #3757)
* Add Support to Xiaomi-Phillips Bulbs
* Add Support for Xiaomi-Philips Bulbs (#3787)
*
* 6.2.1.2 20180906
* Fix KNX PA exception. Regression from 6.2.1 buffer overflow caused by subStr() (#3700, #3710)

View File

@ -130,8 +130,10 @@
#define D_JSON_TYPE "Type"
#define D_JSON_UPTIME "Uptime"
#define D_JSON_UTC_TIME "UTC"
#define D_JSON_UVINDEX "UvIndex"
#define D_JSON_UV_INDEX "UvIndex"
#define D_JSON_UV_INDEX_TEXT "UvIndexText"
#define D_JSON_UV_LEVEL "UvLevel"
#define D_JSON_UV_POWER "UvPower"
#define D_JSON_VCC "Vcc"
#define D_JSON_VERSION "Version"
#define D_JSON_VOLTAGE "Voltage"
@ -174,6 +176,7 @@
#define D_CMND_BLINKTIME "BlinkTime"
#define D_CMND_BLINKCOUNT "BlinkCount"
#define D_CMND_SENSOR "Sensor"
#define D_CMND_DRIVER "Driver"
#define D_CMND_SAVEDATA "SaveData"
#define D_CMND_SETOPTION "SetOption"
#define D_CMND_TEMPERATURE_RESOLUTION "TempRes"
@ -286,12 +289,10 @@
#define D_CMND_CURRENTLOW "CurrentLow"
#define D_CMND_CURRENTHIGH "CurrentHigh"
#define D_CMND_ENERGYRESET "EnergyReset"
#define D_CMND_POWERCAL "PowerCal"
#define D_CMND_POWERSET "PowerSet"
#define D_CMND_VOLTAGECAL "VoltageCal"
#define D_CMND_VOLTAGESET "VoltageSet"
#define D_CMND_CURRENTCAL "CurrentCal"
#define D_CMND_CURRENTSET "CurrentSet"
#define D_CMND_FREQUENCYSET "FrequencySet"
#define D_CMND_MAXPOWER "MaxPower"
#define D_CMND_MAXPOWERHOLD "MaxPowerHold"
#define D_CMND_MAXPOWERWINDOW "MaxPowerWindow"
@ -419,7 +420,8 @@ enum UnitNames {
UNIT_SECTORS,
UNIT_VOLT,
UNIT_WATT,
UNIT_WATTHOUR };
UNIT_WATTHOUR,
UNIT_HERTZ };
const char kUnitNames[] PROGMEM =
D_UNIT_AMPERE "|"
D_UNIT_HOUR "|"
@ -439,7 +441,8 @@ const char kUnitNames[] PROGMEM =
D_UNIT_SECTORS "|"
D_UNIT_VOLT "|"
D_UNIT_WATT "|"
D_UNIT_WATTHOUR ;
D_UNIT_WATTHOUR "|"
D_UNIT_HERTZ ;
const char S_JSON_COMMAND_NVALUE_SPACE_UNIT[] PROGMEM = "{\"%s\":\"%d %s\"}";
const char S_JSON_COMMAND_LVALUE_SPACE_UNIT[] PROGMEM = "{\"%s\":\"%lu %s\"}";
@ -467,6 +470,9 @@ const char S_JSON_COMMAND_INDEX_NVALUE_ACTIVE_NVALUE[] PROGMEM = "{\"%s%d\":\"%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\"}";
const char S_JSON_DRIVER_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_DRIVER "%d\":%d}";
const char S_JSON_DRIVER_INDEX_SVALUE[] PROGMEM = "{\"" D_CMND_DRIVER "%d\":\"%s\"}";
const char JSON_SNS_TEMP[] PROGMEM = "%s,\"%s\":{\"" D_JSON_TEMPERATURE "\":%s}";
const char JSON_SNS_TEMPHUM[] PROGMEM = "%s,\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s}";

View File

@ -28,7 +28,7 @@
* Use online command StateText to translate ON, OFF, HOLD and TOGGLE.
* Use online command Prefix to translate cmnd, stat and tele.
*
* Updated until v6.2.0.1
* Updated until v6.2.1.8
\*********************************************************************/
//#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English)
@ -82,7 +82,7 @@
#define D_DNS_SERVER "DNS Сървър"
#define D_DONE "Изпълнено"
#define D_DST_TIME "DST"
#define D_ECO2 "eCO2"
#define D_ECO2 "eCO"
#define D_EMULATION "Емулация"
#define D_ENABLED "Активиран"
#define D_ERASE "Изтриване"
@ -163,7 +163,15 @@
#define D_USER "Потребител"
#define D_UTC_TIME "UTC"
#define D_UV_INDEX "UV индекс"
#define D_UV_LEVEL "Ниво на ултравиолетово излъчване"
#define D_UV_INDEX_1 "Нисък"
#define D_UV_INDEX_2 "Среден"
#define D_UV_INDEX_3 "Висок"
#define D_UV_INDEX_4 "Много висок"
#define D_UV_INDEX_5 "Изгаряне 1/2 степен"
#define D_UV_INDEX_6 "Изгаряне 3-та степен"
#define D_UV_INDEX_7 "Извън обхват"
#define D_UV_LEVEL "UV ниво"
#define D_UV_POWER "UV мощност"
#define D_VERSION "Версия"
#define D_VOLTAGE "Напрежение"
#define D_WARMLIGHT "Топла"
@ -173,8 +181,8 @@
#define D_WARNING_MINIMAL_VERSION "ПРЕДУПРЕЖДЕНИЕ Тази версия не поддържа постоянни настройки"
#define D_LEVEL_10 "ниво 1-0"
#define D_LEVEL_01 "ниво 0-1"
#define D_SERIAL_LOGGING_DISABLED "Серийния логинг изключен"
#define D_SYSLOG_LOGGING_REENABLED "Системния логинг активиран"
#define D_SERIAL_LOGGING_DISABLED "Серийният лог изключен"
#define D_SYSLOG_LOGGING_REENABLED "Системният лог активиран"
#define D_SET_BAUDRATE_TO "Задаване скорост на предаване (Baudrate)"
#define D_RECEIVED_TOPIC "Получен топик"
@ -186,7 +194,7 @@
#define D_BLOCKED_LOOP "Блокиран цикъл"
#define D_WPS_FAILED_WITH_STATUS "WPS конфигурацията е НЕУСПЕШНА със статус"
#define D_ACTIVE_FOR_3_MINUTES "активно в течение на 3 минути"
#define D_FAILED_TO_START "неуспешно стартиране"
#define D_FAILED_TO_START "Неуспешно стартиране"
#define D_PATCH_ISSUE_2186 "Проблем с патч 2186"
#define D_CONNECTING_TO_AP "Свързване към точка за достъп"
#define D_IN_MODE "в режим"
@ -233,7 +241,7 @@
#define D_CONFIGURE_WIFI "Конфигурация на WiFi"
#define D_CONFIGURE_MQTT "Конфигурация на MQTT"
#define D_CONFIGURE_DOMOTICZ "Конфигурация на Domoticz"
#define D_CONFIGURE_LOGGING "Конфигурация на логинга"
#define D_CONFIGURE_LOGGING "Конфигурация на лога"
#define D_CONFIGURE_OTHER "Драги конфигурации"
#define D_CONFIRM_RESET_CONFIGURATION "Потвърдете изчистването"
#define D_RESET_CONFIGURATION "Изчистване на конфигурацията"
@ -267,7 +275,7 @@
#define D_CLIENT "Клиент"
#define D_FULL_TOPIC "Пълен топик"
#define D_LOGGING_PARAMETERS "Параметри на логинга"
#define D_LOGGING_PARAMETERS "Параметри на лога"
#define D_SERIAL_LOG_LEVEL "Степен на серийния лог"
#define D_WEB_LOG_LEVEL "Степен на Уеб лога"
#define D_SYS_LOG_LEVEL "Степен на системния лог"
@ -371,13 +379,13 @@
#define D_DOMOTICZ_TEMP "Temp"
#define D_DOMOTICZ_TEMP_HUM "Temp,Hum"
#define D_DOMOTICZ_TEMP_HUM_BARO "Temp,Hum,Baro"
#define D_DOMOTICZ_POWER_ENERGY "Power,Energy"
#define D_DOMOTICZ_ILLUMINANCE "Illuminance"
#define D_DOMOTICZ_COUNT "Count/PM1"
#define D_DOMOTICZ_VOLTAGE "Voltage/PM2,5"
#define D_DOMOTICZ_CURRENT "Current/PM10"
#define D_DOMOTICZ_AIRQUALITY "AirQuality"
#define D_DOMOTICZ_UPDATE_TIMER "Update timer"
#define D_DOMOTICZ_POWER_ENERGY "Мощност,Енергия"
#define D_DOMOTICZ_ILLUMINANCE "Осветеност"
#define D_DOMOTICZ_COUNT "Брояч/PM1"
#define D_DOMOTICZ_VOLTAGE "Напрежение/PM2,5"
#define D_DOMOTICZ_CURRENT "Ток/PM10"
#define D_DOMOTICZ_AIRQUALITY "Качество на въздуха"
#define D_DOMOTICZ_UPDATE_TIMER "Период на опресняване"
// xdrv_09_timers.ino
#define D_CONFIGURE_TIMER "Конфигуриране на таймер"
@ -456,7 +464,7 @@
#define D_SENSOR_I2C_SCL "I2C SCL"
#define D_SENSOR_I2C_SDA "I2C SDA"
#define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Player"
#define D_SENSOR_DFR562 "MP3 плейър"
#define D_SENSOR_IRSEND "IRsend"
#define D_SENSOR_SWITCH "Ключ" // Suffix "1"
#define D_SENSOR_BUTTON "Бутон" // Suffix "1"
@ -494,10 +502,11 @@
#define D_UNIT_CENTIMETER "cm"
#define D_UNIT_HERTZ "Hz"
#define D_UNIT_HOUR "h"
#define D_UNIT_INCREMENTS "inc"
#define D_UNIT_KILOOHM "kΩ"
#define D_UNIT_KILOWATTHOUR "kWh"
#define D_UNIT_LUX "lx"
#define D_UNIT_MICROGRAM_PER_CUBIC_METER "µg/m3"
#define D_UNIT_MICROGRAM_PER_CUBIC_METER "µg/m³"
#define D_UNIT_MICROMETER "µm"
#define D_UNIT_MICROSECOND "µs"
#define D_UNIT_MILLIAMPERE "mA"
@ -514,6 +523,7 @@
#define D_UNIT_VOLT "V"
#define D_UNIT_WATT "W"
#define D_UNIT_WATTHOUR "Wh"
#define D_UNIT_WATT_METER_QUADRAT "W/m²"
// Log message prefix
#define D_LOG_APPLICATION "APP: " // Application

View File

@ -163,7 +163,15 @@
#define D_USER "Uživatel"
#define D_UTC_TIME "UTC"
#define D_UV_INDEX "UV Index"
#define D_UV_INDEX_1 "Low"
#define D_UV_INDEX_2 "Mid"
#define D_UV_INDEX_3 "High"
#define D_UV_INDEX_4 "Danger"
#define D_UV_INDEX_5 "BurnL1/2"
#define D_UV_INDEX_6 "BurnL3"
#define D_UV_INDEX_7 "OoR"
#define D_UV_LEVEL "úroveň UV"
#define D_UV_POWER "UV Power"
#define D_VERSION "Verze"
#define D_VOLTAGE "Napětí"
#define D_WARMLIGHT "Teplé světlo"
@ -494,6 +502,7 @@
#define D_UNIT_CENTIMETER "cm"
#define D_UNIT_HERTZ "Hz"
#define D_UNIT_HOUR "hod"
#define D_UNIT_INCREMENTS "inc"
#define D_UNIT_KILOOHM "kOhm"
#define D_UNIT_KILOWATTHOUR "kWh"
#define D_UNIT_LUX "lx"
@ -514,6 +523,7 @@
#define D_UNIT_VOLT "V"
#define D_UNIT_WATT "W"
#define D_UNIT_WATTHOUR "Wh"
#define D_UNIT_WATT_METER_QUADRAT "W/m²"
// Log message prefix
#define D_LOG_APPLICATION "APP: " // Application

View File

@ -163,7 +163,15 @@
#define D_USER "Benutzer"
#define D_UTC_TIME "UTC"
#define D_UV_INDEX "UV-Index"
#define D_UV_INDEX_1 "Niedrig"
#define D_UV_INDEX_2 "Mittel"
#define D_UV_INDEX_3 "Hoch"
#define D_UV_INDEX_4 "Intensiv"
#define D_UV_INDEX_5 "Gefährlich"
#define D_UV_INDEX_6 "Schädlich"
#define D_UV_INDEX_7 "Messwert!"
#define D_UV_LEVEL "UV-Level"
#define D_UV_POWER "UV Intensität"
#define D_VERSION "Version"
#define D_VOLTAGE "Spannung"
#define D_WARMLIGHT "warm"
@ -494,6 +502,7 @@
#define D_UNIT_CENTIMETER "cm"
#define D_UNIT_HERTZ "Hz"
#define D_UNIT_HOUR "h"
#define D_UNIT_INCREMENTS "inc"
#define D_UNIT_KILOOHM "kOhm"
#define D_UNIT_KILOWATTHOUR "kWh"
#define D_UNIT_LUX "lx"
@ -514,6 +523,7 @@
#define D_UNIT_VOLT "V"
#define D_UNIT_WATT "W"
#define D_UNIT_WATTHOUR "Wh"
#define D_UNIT_WATT_METER_QUADRAT "W/m²"
// Log message prefix
#define D_LOG_APPLICATION "APP: " // Application

View File

@ -163,7 +163,15 @@
#define D_USER "Χρήστης"
#define D_UTC_TIME "UTC"
#define D_UV_INDEX "UV Index"
#define D_UV_INDEX_1 "Low"
#define D_UV_INDEX_2 "Mid"
#define D_UV_INDEX_3 "High"
#define D_UV_INDEX_4 "Danger"
#define D_UV_INDEX_5 "BurnL1/2"
#define D_UV_INDEX_6 "BurnL3"
#define D_UV_INDEX_7 "OoR"
#define D_UV_LEVEL "Επίπεδο UV"
#define D_UV_POWER "UV Power"
#define D_VERSION "Έκδοση"
#define D_VOLTAGE "Τάση"
#define D_WARMLIGHT "Ζεστό"
@ -494,6 +502,7 @@
#define D_UNIT_CENTIMETER "cm"
#define D_UNIT_HERTZ "Hz"
#define D_UNIT_HOUR "Hr"
#define D_UNIT_INCREMENTS "inc"
#define D_UNIT_KILOOHM "kOhm"
#define D_UNIT_KILOWATTHOUR "kWh"
#define D_UNIT_LUX "lx"
@ -514,6 +523,7 @@
#define D_UNIT_VOLT "V"
#define D_UNIT_WATT "W"
#define D_UNIT_WATTHOUR "Wh"
#define D_UNIT_WATT_METER_QUADRAT "W/m²"
// Log message prefix
#define D_LOG_APPLICATION "APP: " // Application

View File

@ -163,7 +163,15 @@
#define D_USER "User"
#define D_UTC_TIME "UTC"
#define D_UV_INDEX "UV Index"
#define D_UV_INDEX_1 "Low"
#define D_UV_INDEX_2 "Mid"
#define D_UV_INDEX_3 "High"
#define D_UV_INDEX_4 "Danger"
#define D_UV_INDEX_5 "BurnL1/2"
#define D_UV_INDEX_6 "BurnL3"
#define D_UV_INDEX_7 "OoR"
#define D_UV_LEVEL "UV Level"
#define D_UV_POWER "UV Power"
#define D_VERSION "Version"
#define D_VOLTAGE "Voltage"
#define D_WARMLIGHT "Warm"
@ -493,6 +501,7 @@
#define D_UNIT_AMPERE "A"
#define D_UNIT_CENTIMETER "cm"
#define D_UNIT_HOUR "Hr"
#define D_UNIT_INCREMENTS "inc"
#define D_UNIT_KILOOHM "kOhm"
#define D_UNIT_KILOWATTHOUR "kWh"
#define D_UNIT_LUX "lx"
@ -514,6 +523,7 @@
#define D_UNIT_WATT "W"
#define D_UNIT_WATTHOUR "Wh"
#define D_UNIT_HERTZ "Hz"
#define D_UNIT_WATT_METER_QUADRAT "W/m²"
// Log message prefix
#define D_LOG_APPLICATION "APP: " // Application

View File

@ -163,7 +163,15 @@
#define D_USER "Usuario"
#define D_UTC_TIME "UTC"
#define D_UV_INDEX "Índice UV"
#define D_UV_INDEX_1 "Bajo"
#define D_UV_INDEX_2 "Medio"
#define D_UV_INDEX_3 "Alto"
#define D_UV_INDEX_4 "Peligroso"
#define D_UV_INDEX_5 "Quemaduras 1 a 2 grad"
#define D_UV_INDEX_6 "Quemaduras 3 grad"
#define D_UV_INDEX_7 "Fuera de Rango"
#define D_UV_LEVEL "Nivel UV"
#define D_UV_POWER "UV Power"
#define D_VERSION "Versión"
#define D_VOLTAGE "Tensión"
#define D_WARMLIGHT "Cálida"
@ -494,6 +502,7 @@
#define D_UNIT_CENTIMETER "cm"
#define D_UNIT_HERTZ "Hz"
#define D_UNIT_HOUR "Hr"
#define D_UNIT_INCREMENTS "inc"
#define D_UNIT_KILOOHM "kOhm"
#define D_UNIT_KILOWATTHOUR "kWh"
#define D_UNIT_LUX "lx"
@ -514,6 +523,7 @@
#define D_UNIT_VOLT "V"
#define D_UNIT_WATT "W"
#define D_UNIT_WATTHOUR "Wh"
#define D_UNIT_WATT_METER_QUADRAT "W/m²"
// Log message prefix
#define D_LOG_APPLICATION "APP: " // Application

View File

@ -28,7 +28,7 @@
* Use online command StateText to translate ON, OFF, HOLD and TOGGLE.
* Use online command Prefix to translate cmnd, stat and tele.
*
* Updated until v6.1.1.7
* Updated until v6.2.1.7
\*********************************************************************/
#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English)
@ -65,7 +65,7 @@
#define D_BY "par" // Written by me
#define D_BYTES "Bytes"
#define D_CELSIUS "Celsius"
#define D_CHANNEL "Channel"
#define D_CHANNEL "Canal"
#define D_CO2 "Dioxyde de carbone"
#define D_CODE "code" // Button code
#define D_COLDLIGHT "Froid"
@ -163,7 +163,15 @@
#define D_USER "Utilisateur"
#define D_UTC_TIME "UTC"
#define D_UV_INDEX "Indice UV"
#define D_UV_INDEX_1 "Faible"
#define D_UV_INDEX_2 "Modéré"
#define D_UV_INDEX_3 "Élevé"
#define D_UV_INDEX_4 "Très élevé"
#define D_UV_INDEX_5 "Brûlure niv.1/2"
#define D_UV_INDEX_6 "Brûlure niv.3"
#define D_UV_INDEX_7 "Hors échelle"
#define D_UV_LEVEL "Niveau UV"
#define D_UV_POWER "Puissance UV"
#define D_VERSION "Version"
#define D_VOLTAGE "Tension"
#define D_WARMLIGHT "Chaud"
@ -176,7 +184,7 @@
#define D_SERIAL_LOGGING_DISABLED "Journalisation série désactivée"
#define D_SYSLOG_LOGGING_REENABLED "Jounalisation syslog réactivée"
#define D_SET_BAUDRATE_TO "Définir baudrate à"
#define D_SET_BAUDRATE_TO "Définir le débit à"
#define D_RECEIVED_TOPIC "Topic reçu" // Terme MQTT
#define D_DATA_SIZE "Taille données"
#define D_ANALOG_INPUT "Analogique"
@ -494,6 +502,7 @@
#define D_UNIT_CENTIMETER "cm"
#define D_UNIT_HERTZ "Hz"
#define D_UNIT_HOUR "h"
#define D_UNIT_INCREMENTS "inc"
#define D_UNIT_KILOOHM "kΩ"
#define D_UNIT_KILOWATTHOUR "kWh"
#define D_UNIT_LUX "lx"
@ -514,6 +523,7 @@
#define D_UNIT_VOLT "V"
#define D_UNIT_WATT "W"
#define D_UNIT_WATTHOUR "Wh"
#define D_UNIT_WATT_METER_QUADRAT "W/m²"
// Log message prefix
#define D_LOG_APPLICATION "APP: " // Application

View File

@ -163,7 +163,15 @@
#define D_USER "Felhasználó"
#define D_UTC_TIME "UTC"
#define D_UV_INDEX "UV Index"
#define D_UV_INDEX_1 "Low"
#define D_UV_INDEX_2 "Mid"
#define D_UV_INDEX_3 "High"
#define D_UV_INDEX_4 "Danger"
#define D_UV_INDEX_5 "BurnL1/2"
#define D_UV_INDEX_6 "BurnL3"
#define D_UV_INDEX_7 "OoR"
#define D_UV_LEVEL "UV Szint"
#define D_UV_POWER "UV Power"
#define D_VERSION "Verzió"
#define D_VOLTAGE "Feszültség"
#define D_WARMLIGHT "Meleg"
@ -494,6 +502,7 @@
#define D_UNIT_CENTIMETER "cm"
#define D_UNIT_HERTZ "Hz"
#define D_UNIT_HOUR "ó"
#define D_UNIT_INCREMENTS "inc"
#define D_UNIT_KILOOHM "kOhm"
#define D_UNIT_KILOWATTHOUR "kWh"
#define D_UNIT_LUX "lx"
@ -514,6 +523,7 @@
#define D_UNIT_VOLT "V"
#define D_UNIT_WATT "W"
#define D_UNIT_WATTHOUR "Wh"
#define D_UNIT_WATT_METER_QUADRAT "W/m²"
// Log message prefix
#define D_LOG_APPLICATION "APP: " // Application

View File

@ -163,7 +163,15 @@
#define D_USER "Utente"
#define D_UTC_TIME "UTC"
#define D_UV_INDEX "Indice UV"
#define D_UV_INDEX_1 "Low"
#define D_UV_INDEX_2 "Mid"
#define D_UV_INDEX_3 "High"
#define D_UV_INDEX_4 "Danger"
#define D_UV_INDEX_5 "BurnL1/2"
#define D_UV_INDEX_6 "BurnL3"
#define D_UV_INDEX_7 "OoR"
#define D_UV_LEVEL "Livello UV"
#define D_UV_POWER "UV Power"
#define D_VERSION "Versione"
#define D_VOLTAGE "Tensione"
#define D_WARMLIGHT "Calda"
@ -494,6 +502,7 @@
#define D_UNIT_CENTIMETER "cm"
#define D_UNIT_HERTZ "Hz"
#define D_UNIT_HOUR "Hr"
#define D_UNIT_INCREMENTS "inc"
#define D_UNIT_KILOOHM "kOhm"
#define D_UNIT_KILOWATTHOUR "kWh"
#define D_UNIT_LUX "lx"
@ -514,6 +523,7 @@
#define D_UNIT_VOLT "V"
#define D_UNIT_WATT "W"
#define D_UNIT_WATTHOUR "Wh"
#define D_UNIT_WATT_METER_QUADRAT "W/m²"
// Log message prefix
#define D_LOG_APPLICATION "APP: " // Application

View File

@ -163,7 +163,15 @@
#define D_USER "Gebruiker"
#define D_UTC_TIME "UTC"
#define D_UV_INDEX "UV-index"
#define D_UV_INDEX_1 "Low"
#define D_UV_INDEX_2 "Mid"
#define D_UV_INDEX_3 "High"
#define D_UV_INDEX_4 "Danger"
#define D_UV_INDEX_5 "BurnL1/2"
#define D_UV_INDEX_6 "BurnL3"
#define D_UV_INDEX_7 "OoR"
#define D_UV_LEVEL "UV niveau"
#define D_UV_POWER "UV Power"
#define D_VERSION "Versie"
#define D_VOLTAGE "Spanning"
#define D_WARMLIGHT "Warm"
@ -494,6 +502,7 @@
#define D_UNIT_CENTIMETER "cm"
#define D_UNIT_HERTZ "Hz"
#define D_UNIT_HOUR "h"
#define D_UNIT_INCREMENTS "inc"
#define D_UNIT_KILOOHM "kOhm"
#define D_UNIT_KILOWATTHOUR "kWh"
#define D_UNIT_LUX "lx"
@ -514,6 +523,7 @@
#define D_UNIT_VOLT "V"
#define D_UNIT_WATT "W"
#define D_UNIT_WATTHOUR "Wh"
#define D_UNIT_WATT_METER_QUADRAT "W/m²"
// Log message prefix
#define D_LOG_APPLICATION "APP: " // Application

View File

@ -163,7 +163,15 @@
#define D_USER "Użytkownik"
#define D_UTC_TIME "UTC"
#define D_UV_INDEX "UV Index"
#define D_UV_INDEX_1 "Low"
#define D_UV_INDEX_2 "Mid"
#define D_UV_INDEX_3 "High"
#define D_UV_INDEX_4 "Danger"
#define D_UV_INDEX_5 "BurnL1/2"
#define D_UV_INDEX_6 "BurnL3"
#define D_UV_INDEX_7 "OoR"
#define D_UV_LEVEL "Poziom UV"
#define D_UV_POWER "UV Power"
#define D_VERSION "Wersja"
#define D_VOLTAGE "Napięcie"
#define D_WARMLIGHT "Nagrzanie"
@ -494,6 +502,7 @@
#define D_UNIT_CENTIMETER "cm"
#define D_UNIT_HERTZ "Hz"
#define D_UNIT_HOUR "Godz"
#define D_UNIT_INCREMENTS "inc"
#define D_UNIT_KILOOHM "kOhm"
#define D_UNIT_KILOWATTHOUR "kWh"
#define D_UNIT_LUX "lx"
@ -514,6 +523,7 @@
#define D_UNIT_VOLT "V"
#define D_UNIT_WATT "W"
#define D_UNIT_WATTHOUR "Wh"
#define D_UNIT_WATT_METER_QUADRAT "W/m²"
// Log message prefix
#define D_LOG_APPLICATION "APP: " // Application

View File

@ -163,7 +163,15 @@
#define D_USER "Usuário"
#define D_UTC_TIME "UTC"
#define D_UV_INDEX "Índice UV"
#define D_UV_INDEX_1 "Low"
#define D_UV_INDEX_2 "Mid"
#define D_UV_INDEX_3 "High"
#define D_UV_INDEX_4 "Danger"
#define D_UV_INDEX_5 "BurnL1/2"
#define D_UV_INDEX_6 "BurnL3"
#define D_UV_INDEX_7 "OoR"
#define D_UV_LEVEL "Nível UV"
#define D_UV_POWER "UV Power"
#define D_VERSION "Versão"
#define D_VOLTAGE "Voltagem"
#define D_WARMLIGHT "Luz quente"
@ -494,6 +502,7 @@
#define D_UNIT_CENTIMETER "cm"
#define D_UNIT_HERTZ "Hz"
#define D_UNIT_HOUR "H"
#define D_UNIT_INCREMENTS "inc"
#define D_UNIT_KILOOHM "kOhm"
#define D_UNIT_KILOWATTHOUR "kWh"
#define D_UNIT_LUX "lx"
@ -514,6 +523,7 @@
#define D_UNIT_VOLT "V"
#define D_UNIT_WATT "W"
#define D_UNIT_WATTHOUR "W/h"
#define D_UNIT_WATT_METER_QUADRAT "W/m²"
// Log message prefix
#define D_LOG_APPLICATION "APP: " // Application

View File

@ -163,7 +163,15 @@
#define D_USER "Utilizador"
#define D_UTC_TIME "UTC"
#define D_UV_INDEX "Indíce UV"
#define D_UV_INDEX_1 "Low"
#define D_UV_INDEX_2 "Mid"
#define D_UV_INDEX_3 "High"
#define D_UV_INDEX_4 "Danger"
#define D_UV_INDEX_5 "BurnL1/2"
#define D_UV_INDEX_6 "BurnL3"
#define D_UV_INDEX_7 "OoR"
#define D_UV_LEVEL "Nível UV"
#define D_UV_POWER "UV Power"
#define D_VERSION "Versão"
#define D_VOLTAGE "Voltagem"
#define D_WARMLIGHT "Luz Quente"
@ -494,6 +502,7 @@
#define D_UNIT_CENTIMETER "cm"
#define D_UNIT_HERTZ "Hz"
#define D_UNIT_HOUR "Hr"
#define D_UNIT_INCREMENTS "inc"
#define D_UNIT_KILOOHM "kOhm"
#define D_UNIT_KILOWATTHOUR "kWh"
#define D_UNIT_LUX "lx"
@ -514,6 +523,7 @@
#define D_UNIT_VOLT "V"
#define D_UNIT_WATT "W"
#define D_UNIT_WATTHOUR "Wh"
#define D_UNIT_WATT_METER_QUADRAT "W/m²"
// Log message prefix
#define D_LOG_APPLICATION "APP: " // Application

View File

@ -163,7 +163,15 @@
#define D_USER "Пользователь"
#define D_UTC_TIME "UTC"
#define D_UV_INDEX "UV Index"
#define D_UV_INDEX_1 "Low"
#define D_UV_INDEX_2 "Mid"
#define D_UV_INDEX_3 "High"
#define D_UV_INDEX_4 "Danger"
#define D_UV_INDEX_5 "BurnL1/2"
#define D_UV_INDEX_6 "BurnL3"
#define D_UV_INDEX_7 "OoR"
#define D_UV_LEVEL "УФ уровень"
#define D_UV_POWER "UV Power"
#define D_VERSION "Версия"
#define D_VOLTAGE "Напряжение"
#define D_WARMLIGHT "Тепло"
@ -494,6 +502,7 @@
#define D_UNIT_CENTIMETER "cm"
#define D_UNIT_HERTZ "Hz"
#define D_UNIT_HOUR "Ч"
#define D_UNIT_INCREMENTS "inc"
#define D_UNIT_KILOOHM "кОм"
#define D_UNIT_KILOWATTHOUR "кВт"
#define D_UNIT_LUX "лк"
@ -514,6 +523,7 @@
#define D_UNIT_VOLT "В"
#define D_UNIT_WATT "Вт"
#define D_UNIT_WATTHOUR "ВтЧ"
#define D_UNIT_WATT_METER_QUADRAT "W/m²"
// Log message prefix
#define D_LOG_APPLICATION "APP: " // Application

View File

@ -163,7 +163,15 @@
#define D_USER "Kullanıcı"
#define D_UTC_TIME "UTC"
#define D_UV_INDEX "UV Indeksi"
#define D_UV_INDEX_1 "Low"
#define D_UV_INDEX_2 "Mid"
#define D_UV_INDEX_3 "High"
#define D_UV_INDEX_4 "Danger"
#define D_UV_INDEX_5 "BurnL1/2"
#define D_UV_INDEX_6 "BurnL3"
#define D_UV_INDEX_7 "OoR"
#define D_UV_LEVEL "UV Seviyesi"
#define D_UV_POWER "UV Power"
#define D_VERSION "Versiyon"
#define D_VOLTAGE "Voltaj"
#define D_WARMLIGHT "Sıcak"
@ -494,6 +502,7 @@
#define D_UNIT_AMPERE "A"
#define D_UNIT_CENTIMETER "cm"
#define D_UNIT_HOUR "Hr"
#define D_UNIT_INCREMENTS "inc"
#define D_UNIT_KILOOHM "kOhm"
#define D_UNIT_KILOWATTHOUR "kWh"
#define D_UNIT_LUX "lx"
@ -514,6 +523,7 @@
#define D_UNIT_VOLT "V"
#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

View File

@ -163,7 +163,15 @@
#define D_USER "Користувач"
#define D_UTC_TIME "UTC"
#define D_UV_INDEX "УФ індекс"
#define D_UV_INDEX_1 "Low"
#define D_UV_INDEX_2 "Mid"
#define D_UV_INDEX_3 "High"
#define D_UV_INDEX_4 "Danger"
#define D_UV_INDEX_5 "BurnL1/2"
#define D_UV_INDEX_6 "BurnL3"
#define D_UV_INDEX_7 "OoR"
#define D_UV_LEVEL "УФ рівень"
#define D_UV_POWER "UV Power"
#define D_VERSION "Версія"
#define D_VOLTAGE "Напруга"
#define D_WARMLIGHT "Тепло"
@ -495,6 +503,7 @@
#define D_UNIT_CENTIMETER "cм"
#define D_UNIT_HERTZ "Гц"
#define D_UNIT_HOUR "Г"
#define D_UNIT_INCREMENTS "inc"
#define D_UNIT_KILOOHM "кОм"
#define D_UNIT_KILOWATTHOUR "кВт"
#define D_UNIT_LUX "лк"
@ -515,6 +524,7 @@
#define D_UNIT_VOLT "В"
#define D_UNIT_WATT "Вт"
#define D_UNIT_WATTHOUR "ВтГод"
#define D_UNIT_WATT_METER_QUADRAT "W/m²"
// Log message prefix
#define D_LOG_APPLICATION "APP: " // Application

View File

@ -163,7 +163,15 @@
#define D_USER "用户名"
#define D_UTC_TIME "UTC"
#define D_UV_INDEX "紫外线指数"
#define D_UV_INDEX_1 "Low"
#define D_UV_INDEX_2 "Mid"
#define D_UV_INDEX_3 "High"
#define D_UV_INDEX_4 "Danger"
#define D_UV_INDEX_5 "BurnL1/2"
#define D_UV_INDEX_6 "BurnL3"
#define D_UV_INDEX_7 "OoR"
#define D_UV_LEVEL "紫外线水平"
#define D_UV_POWER "UV Power"
#define D_VERSION "版本"
#define D_VOLTAGE "电压"
#define D_WARMLIGHT "暖"
@ -493,6 +501,7 @@
#define D_UNIT_AMPERE "安"
#define D_UNIT_CENTIMETER "厘米"
#define D_UNIT_HOUR "时"
#define D_UNIT_INCREMENTS "inc"
#define D_UNIT_KILOOHM "千欧"
#define D_UNIT_KILOWATTHOUR "千瓦时"
#define D_UNIT_LUX "勒克斯"
@ -514,6 +523,7 @@
#define D_UNIT_WATT "瓦"
#define D_UNIT_WATTHOUR "瓦时"
#define D_UNIT_HERTZ "赫兹"
#define D_UNIT_WATT_METER_QUADRAT "W/m²"
// Log message prefix
#define D_LOG_APPLICATION "APP: " // Application

View File

@ -163,7 +163,15 @@
#define D_USER "用戶名"
#define D_UTC_TIME "UTC"
#define D_UV_INDEX "UV Index"
#define D_UV_INDEX_1 "Low"
#define D_UV_INDEX_2 "Mid"
#define D_UV_INDEX_3 "High"
#define D_UV_INDEX_4 "Danger"
#define D_UV_INDEX_5 "BurnL1/2"
#define D_UV_INDEX_6 "BurnL3"
#define D_UV_INDEX_7 "OoR"
#define D_UV_LEVEL "紫外線等級"
#define D_UV_POWER "UV Power"
#define D_VERSION "版本"
#define D_VOLTAGE "電壓"
#define D_WARMLIGHT "暖"
@ -493,6 +501,7 @@
#define D_UNIT_AMPERE "安"
#define D_UNIT_CENTIMETER "cm"
#define D_UNIT_HERTZ "Hz"
#define D_UNIT_INCREMENTS "inc"
#define D_UNIT_HOUR "時"
#define D_UNIT_KILOOHM "千歐"
#define D_UNIT_KILOWATTHOUR "千瓦時"
@ -514,6 +523,7 @@
#define D_UNIT_VOLT "伏"
#define D_UNIT_WATT "瓦"
#define D_UNIT_WATTHOUR "瓦時"
#define D_UNIT_WATT_METER_QUADRAT "W/m²"
// Log message prefix
#define D_LOG_APPLICATION "APP: " // Application

View File

@ -322,9 +322,13 @@ struct SYSCFG {
uint16_t mcp230xx_int_timer; // 718
byte free_71A[180]; // 71A
byte free_71A[174]; // 71A
char mems[MAX_RULE_MEMS][10]; // 7CE
unsigned long energy_frequency_calibration; // 7C8
byte free_7CC[2]; // 7CC
char mems[MAX_RULE_MEMS][10]; // 7CE
// 800 Full - no more free locations
char rules[MAX_RULE_SETS][MAX_RULE_SIZE]; // 800 uses 512 bytes in v5.12.0m, 3 x 512 bytes in v5.14.0b
@ -333,21 +337,21 @@ struct SYSCFG {
} Settings;
struct RTCRBT {
uint16_t valid; // 000
uint8_t fast_reboot_count; // 002
uint8_t free_003[1]; // 003
uint16_t valid; // 280 (RTC memory offset 100 - sizeof(RTCRBT))
uint8_t fast_reboot_count; // 282
uint8_t free_003[1]; // 283
} RtcReboot;
struct RTCMEM {
uint16_t valid; // 000
byte oswatch_blocked_loop; // 002
uint8_t ota_loader; // 003
unsigned long energy_kWhtoday; // 004
unsigned long energy_kWhtotal; // 008
unsigned long pulse_counter[MAX_COUNTERS]; // 00C
power_t power; // 01C
uint8_t free_020[60]; // 020
// 05C next free location (64 (=core) + 100 (=tasmota offset) + 92 (=0x5C RTCMEM struct) = 256 bytes (max = 512))
uint16_t valid; // 290 (RTC memory offset 100)
byte oswatch_blocked_loop; // 292
uint8_t ota_loader; // 293
unsigned long energy_kWhtoday; // 294
unsigned long energy_kWhtotal; // 298
unsigned long pulse_counter[MAX_COUNTERS]; // 29C
power_t power; // 2AC
uint8_t free_020[60]; // 2B0
// 2EC - 2FF free locations
} RtcSettings;
struct TIME_T {

View File

@ -86,20 +86,12 @@ void RtcSettingsSave()
RtcSettings.valid = RTC_MEM_VALID;
ESP.rtcUserMemoryWrite(100, (uint32_t*)&RtcSettings, sizeof(RTCMEM));
rtc_settings_crc = GetRtcSettingsCrc();
#ifdef DEBUG_THEO
AddLog_P(LOG_LEVEL_DEBUG, PSTR("Dump: Save"));
RtcSettingsDump();
#endif // DEBUG_THEO
}
}
void RtcSettingsLoad()
{
ESP.rtcUserMemoryRead(100, (uint32_t*)&RtcSettings, sizeof(RTCMEM));
#ifdef DEBUG_THEO
AddLog_P(LOG_LEVEL_DEBUG, PSTR("Dump: Load"));
RtcSettingsDump();
#endif // DEBUG_THEO
ESP.rtcUserMemoryRead(100, (uint32_t*)&RtcSettings, sizeof(RTCMEM)); // 0x290
if (RtcSettings.valid != RTC_MEM_VALID) {
memset(&RtcSettings, 0, sizeof(RTCMEM));
RtcSettings.valid = RTC_MEM_VALID;
@ -145,7 +137,7 @@ void RtcRebootSave()
void RtcRebootLoad()
{
ESP.rtcUserMemoryRead(100 - sizeof(RTCRBT), (uint32_t*)&RtcReboot, sizeof(RTCRBT));
ESP.rtcUserMemoryRead(100 - sizeof(RTCRBT), (uint32_t*)&RtcReboot, sizeof(RTCRBT)); // 0x280
if (RtcReboot.valid != RTC_MEM_VALID) {
memset(&RtcReboot, 0, sizeof(RTCRBT));
RtcReboot.valid = RTC_MEM_VALID;

View File

@ -83,7 +83,7 @@ enum TasmotaCommands {
CMND_LOGHOST, CMND_LOGPORT, CMND_IPADDRESS, CMND_NTPSERVER, CMND_AP, CMND_SSID, CMND_PASSWORD, CMND_HOSTNAME,
CMND_WIFICONFIG, CMND_FRIENDLYNAME, CMND_SWITCHMODE,
CMND_TELEPERIOD, CMND_RESTART, CMND_RESET, CMND_TIMEZONE, CMND_TIMESTD, CMND_TIMEDST, CMND_ALTITUDE, CMND_LEDPOWER, CMND_LEDSTATE,
CMND_I2CSCAN, CMND_SERIALSEND, CMND_BAUDRATE, CMND_SERIALDELIMITER };
CMND_I2CSCAN, CMND_SERIALSEND, CMND_BAUDRATE, CMND_SERIALDELIMITER, CMND_DRIVER };
const char kTasmotaCommands[] PROGMEM =
D_CMND_BACKLOG "|" D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_FANSPEED "|" D_CMND_STATUS "|" D_CMND_STATE "|" D_CMND_POWERONSTATE "|" D_CMND_PULSETIME "|"
D_CMND_BLINKTIME "|" D_CMND_BLINKCOUNT "|" D_CMND_SENSOR "|" D_CMND_SAVEDATA "|" D_CMND_SETOPTION "|" D_CMND_TEMPERATURE_RESOLUTION "|" D_CMND_HUMIDITY_RESOLUTION "|"
@ -93,7 +93,7 @@ const char kTasmotaCommands[] PROGMEM =
D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|"
D_CMND_WIFICONFIG "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|"
D_CMND_TELEPERIOD "|" D_CMND_RESTART "|" D_CMND_RESET "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|"
D_CMND_I2CSCAN "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALDELIMITER;
D_CMND_I2CSCAN "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALDELIMITER "|" D_CMND_DRIVER;
const uint8_t kIFan02Speed[4][3] = {{6,6,6}, {7,6,6}, {7,7,6}, {7,6,7}};
@ -147,6 +147,7 @@ uint16_t blink_counter = 0; // Number of blink cycles
uint16_t seriallog_timer = 0; // Timer to disable Seriallog
uint16_t syslog_timer = 0; // Timer to re-enable syslog_level
uint16_t holdbutton[MAX_KEYS] = { 0 }; // Timer for button hold
uint16_t switch_no_pullup = 0; // Switch pull-up bitmask flags
int16_t save_data_counter; // Counter and flag for config save to Flash
RulesBitfield rules_flag; // Rule state flags (16 bits)
uint8_t serial_local = 0; // Handle serial locally;
@ -335,7 +336,8 @@ void SetDevicePower(power_t rpower, int source)
}
}
XdrvSetPower(rpower);
XdrvMailbox.index = rpower;
XdrvCall(FUNC_SET_POWER);
if ((SONOFF_DUAL == Settings.module) || (CH4 == Settings.module)) {
Serial.write(0xA0);
@ -461,10 +463,11 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
grpflg = (strstr(topicBuf, Settings.mqtt_grptopic) != NULL);
fallback_topic_flag = (strstr(topicBuf, mqtt_client) != NULL);
type = strrchr(topicBuf, '/') +1; // Last part of received topic is always the command (type)
type = strrchr(topicBuf, '/'); // Last part of received topic is always the command (type)
index = 1;
if (type != NULL) {
type++;
for (i = 0; i < strlen(type); i++) {
type[i] = toupper(type[i]);
}
@ -679,7 +682,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, (Settings.save_data > 1) ? stemp1 : GetStateText(Settings.save_data));
}
else if (CMND_SENSOR == command_code) {
else if ((CMND_SENSOR == command_code) || (CMND_DRIVER == command_code)) {
XdrvMailbox.index = index;
XdrvMailbox.data_len = data_len;
XdrvMailbox.payload16 = payload16;
@ -687,8 +690,11 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
XdrvMailbox.grpflg = grpflg;
XdrvMailbox.topic = command;
XdrvMailbox.data = dataBuf;
XsnsCall(FUNC_COMMAND);
// if (!XsnsCall(FUNC_COMMAND)) type = NULL;
if (CMND_SENSOR == command_code) {
XsnsCall(FUNC_COMMAND);
} else {
XdrvCall(FUNC_COMMAND);
}
}
else if ((CMND_SETOPTION == command_code) && (index < 82)) {
byte ptype;
@ -969,22 +975,25 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.baudrate * 1200);
}
else if ((CMND_SERIALSEND == command_code) && (index > 0) && (index <= 4)) {
else if ((CMND_SERIALSEND == command_code) && (index > 0) && (index <= 5)) {
SetSeriallog(LOG_LEVEL_NONE);
Settings.flag.mqtt_serial = 1;
Settings.flag.mqtt_serial_raw = (4 == index) ? 1 : 0;
Settings.flag.mqtt_serial_raw = (index > 3) ? 1 : 0;
if (data_len > 0) {
if (1 == index) {
Serial.printf("%s\n", dataBuf);
Serial.printf("%s\n", dataBuf); // "Hello Tiger\n"
}
else if (2 == index || 4 == index) {
for (int i = 0; i < data_len; i++) {
Serial.write(dataBuf[i]);
Serial.write(dataBuf[i]); // "Hello Tiger" or "A0"
}
}
else if (3 == index) {
uint16_t dat_len = data_len;
Serial.printf("%s", Unescape(dataBuf, &dat_len));
Serial.printf("%s", Unescape(dataBuf, &dat_len)); // "Hello\f"
}
else if (5 == index) {
SerialSendRaw(dataBuf, data_len); // "AA004566"
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
}
@ -1035,7 +1044,8 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
for (i = 0; i < strlen(Settings.ntp_server[index -1]); i++) {
if (Settings.ntp_server[index -1][i] == ',') Settings.ntp_server[index -1][i] = '.';
}
restart_flag = 2;
// restart_flag = 2; // Issue #3890
ntp_force_sync = 1;
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.ntp_server[index -1]);
}
@ -1110,6 +1120,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
else if ((CMND_SWITCHMODE == command_code) && (index > 0) && (index <= MAX_SWITCHES)) {
if ((payload >= 0) && (payload < MAX_SWITCH_OPTION)) {
Settings.switchmode[index -1] = payload;
GpioSwitchPinMode(index -1);
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_NVALUE, command, index, Settings.switchmode[index-1]);
}
@ -1403,6 +1414,7 @@ void PublishStatus(uint8_t payload)
{
uint8_t option = STAT;
char stemp[MAX_FRIENDLYNAMES * (sizeof(Settings.friendlyname[0]) +MAX_FRIENDLYNAMES)];
char stemp2[MAX_SWITCHES * 3];
// Workaround MQTT - TCP/IP stack queueing when SUB_PREFIX = PUB_PREFIX
if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1]) && (!payload)) option++; // TELE
@ -1417,8 +1429,12 @@ void PublishStatus(uint8_t payload)
for (byte i = 0; i < maxfn; i++) {
snprintf_P(stemp, sizeof(stemp), PSTR("%s%s\"%s\"" ), stemp, (i > 0 ? "," : ""), Settings.friendlyname[i]);
}
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS "\":{\"" D_CMND_MODULE "\":%d,\"" D_CMND_FRIENDLYNAME "\":[%s],\"" D_CMND_TOPIC "\":\"%s\",\"" D_CMND_BUTTONTOPIC "\":\"%s\",\"" D_CMND_POWER "\":%d,\"" D_CMND_POWERONSTATE "\":%d,\"" D_CMND_LEDSTATE "\":%d,\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"),
Settings.module +1, stemp, mqtt_topic, Settings.button_topic, power, Settings.poweronstate, Settings.ledstate, Settings.save_data, Settings.flag.save_state, Settings.flag.mqtt_button_retain, Settings.flag.mqtt_power_retain);
stemp2[0] = '\0';
for (byte i = 0; i < MAX_SWITCHES; i++) {
snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%d" ), stemp2, (i > 0 ? "," : ""), Settings.switchmode[i]);
}
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS "\":{\"" D_CMND_MODULE "\":%d,\"" D_CMND_FRIENDLYNAME "\":[%s],\"" D_CMND_TOPIC "\":\"%s\",\"" D_CMND_BUTTONTOPIC "\":\"%s\",\"" D_CMND_POWER "\":%d,\"" D_CMND_POWERONSTATE "\":%d,\"" D_CMND_LEDSTATE "\":%d,\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_SWITCHTOPIC "\":\"%s\",\"" D_CMND_SWITCHMODE "\":[%s],\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_SWITCHRETAIN "\":%d,\"" D_CMND_SENSORRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"),
Settings.module +1, stemp, mqtt_topic, Settings.button_topic, power, Settings.poweronstate, Settings.ledstate, Settings.save_data, Settings.flag.save_state, Settings.switch_topic, stemp2, Settings.flag.mqtt_button_retain, Settings.flag.mqtt_switch_retain, Settings.flag.mqtt_sensor_retain, Settings.flag.mqtt_power_retain);
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS));
}
@ -2282,11 +2298,23 @@ void SerialInput()
/********************************************************************************************/
void GpioSwitchPinMode(uint8_t index)
{
if (pin[GPIO_SWT1 +index] < 99) {
// pinMode(pin[GPIO_SWT1 +index], (16 == pin[GPIO_SWT1 +index]) ? INPUT_PULLDOWN_16 : bitRead(switch_no_pullup, index) ? INPUT : INPUT_PULLUP);
uint8_t no_pullup = 0;
if (bitRead(switch_no_pullup, index)) {
no_pullup = (Settings.switchmode[index] < PUSHBUTTON);
}
pinMode(pin[GPIO_SWT1 +index], (16 == pin[GPIO_SWT1 +index]) ? INPUT_PULLDOWN_16 : (no_pullup) ? INPUT : INPUT_PULLUP);
}
}
void GpioInit()
{
uint8_t mpin;
uint8_t key_no_pullup = 0;
uint16_t switch_no_pullup = 0;
mytmplt def_module;
if (!Settings.module || (Settings.module >= MAXMODULE)) {
@ -2443,7 +2471,7 @@ void GpioInit()
for (byte i = 0; i < MAX_SWITCHES; i++) {
lastwallswitch[i] = 1; // Init global to virtual switch state;
if (pin[GPIO_SWT1 +i] < 99) {
pinMode(pin[GPIO_SWT1 +i], (16 == pin[GPIO_SWT1 +i]) ? INPUT_PULLDOWN_16 : bitRead(switch_no_pullup, i) ? INPUT : INPUT_PULLUP);
GpioSwitchPinMode(i);
lastwallswitch[i] = digitalRead(pin[GPIO_SWT1 +i]); // Set global now so doesn't change the saved power state on first switch check
}
virtualswitch[i] = lastwallswitch[i];
@ -2606,7 +2634,6 @@ void setup()
SetPulseTimer(i, Settings.pulse_timer[i]);
}
}
blink_powersave = power;
snprintf_P(log_data, sizeof(log_data), PSTR(D_PROJECT " %s %s (" D_CMND_TOPIC " %s, " D_FALLBACK " %s, " D_CMND_GROUPTOPIC " %s) " D_VERSION " %s-" ARDUINO_ESP8266_RELEASE),

View File

@ -51,7 +51,7 @@ void KNX_CB_Action(message_t const &msg, void *arg);
#endif
#define USE_DHT // Default DHT11 sensor needs no external library
#define USE_ENERGY_SENSOR // Use energy sensors
#define USE_ENERGY_SENSOR // Use energy sensors (+14k code)
#define USE_HLW8012 // Use energy sensor for Sonoff Pow and WolfBlitz
#define USE_CSE7766 // Use energy sensor for Sonoff S31 and Pow R2
@ -167,6 +167,30 @@ void KNX_CB_Action(message_t const &msg, void *arg);
#undef USE_EMULATION // Disable Belkin WeMo and Hue Bridge emulation for Alexa (-16k code, -2k mem)
#endif // USE_KNX_NO_EMULATION
/*********************************************************************************************\
* [sonoff-display.bin]
* Provide an image with display drivers enabled
\*********************************************************************************************/
#ifdef USE_DISPLAYS
#undef USE_ENERGY_SENSOR // Disable energy sensors (-14k code)
#undef USE_EMULATION // Disable Belkin WeMo and Hue Bridge emulation for Alexa (-16k code, -2k mem)
#define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram)
#define USE_DISPLAY // Add I2C Display Support (+2k code)
#define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0
#define USE_DISPLAY_LCD // [DisplayModel 1] Enable Lcd display (I2C addresses 0x27 and 0x3F) (+6k code)
#define USE_DISPLAY_SSD1306 // [DisplayModel 2] Enable SSD1306 Oled 128x64 display (I2C addresses 0x3C and 0x3D) (+16k code)
#define USE_DISPLAY_MATRIX // [DisplayModel 3] Enable 8x8 Matrix display (I2C adresseses see below) (+11k code)
#define USE_SPI // Hardware SPI using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) in addition to two user selectable GPIOs(CS and DC)
#define USE_DISPLAY_ILI9341 // [DisplayModel 4] Enable ILI9341 Tft 480x320 display (+19k code)
#undef USE_ARILUX_RF // Remove support for Arilux RF remote controller (-0k8 code, 252 iram (non 2.3.0))
#undef USE_RF_FLASH // Remove support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB (-3k code)
#endif // USE_DISPLAYS
/*********************************************************************************************\
* Mandatory define for DS18x20 if changed by above image selections
\*********************************************************************************************/

View File

@ -231,6 +231,7 @@ enum SupportedModules {
SHELLY1,
SHELLY2,
PHILIPS,
NEO_COOLCAM,
MAXMODULE };
/********************************************************************************************/
@ -390,6 +391,7 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = {
SHELLY1,
SHELLY2,
BLITZWOLF_BWSHP2,
NEO_COOLCAM,
H801,
MAGICHOME,
ARILUX_LC01,
@ -399,9 +401,9 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = {
HUAFAN_SS,
KMC_70011,
AILIGHT,
WEMOS,
PHILIPS,
WITTY,
PHILIPS
WEMOS
};
// Default module settings
@ -853,6 +855,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
0, 0, 0, 0, 0, 0, // Flash connection
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,
@ -867,6 +870,22 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
GPIO_PWM1, // GPIO14 RGB LED Red
0, 0, 0
},
*/
{ "MagicHome", // Magic Home (aka Flux-light) (ESP8266) and Arilux LC10 (ESP8285)
// https://www.aliexpress.com/item/Magic-Home-Mini-RGB-RGBW-Wifi-Controller-For-Led-Strip-Panel-light-Timing-Function-16million-colors/32686853650.html
0,
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
GPIO_LED1_INV, // GPIO02 Blue onboard LED
GPIO_USER, // GPIO03 Serial TXD and Optional sensor
GPIO_ARIRFRCV, // GPIO04 IR or RF receiver (optional)
GPIO_PWM2, // GPIO05 RGB LED Green
0, 0, 0, 0, 0, 0, // Flash connection
GPIO_PWM3, // GPIO12 RGB LED Blue
GPIO_USER, // GPIO13 RGBW LED White (optional - set to PWM4 for Cold White or Warm White as used on Arilux LC10)
GPIO_PWM1, // GPIO14 RGB LED Red
GPIO_LED2_INV, // GPIO15 RF receiver control
0, 0
},
{ "Luani HVIO", // ESP8266_HVIO
// https://luani.de/projekte/esp8266-hvio/
0, // GPIO00 Flash jumper
@ -1054,12 +1073,40 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
0, 0,
GPIO_PWM1, // GPIO15 light intensity
0, 0
},
{ "Neo Coolcam", // Neo Coolcam (ESP8266)
// https://www.banggood.com/NEO-COOLCAM-WiFi-Mini-Smart-Plug-APP-Remote-Control-Timing-Smart-Socket-EU-Plug-p-1288562.html?cur_warehouse=CN
0, 0, 0, 0,
GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off)
0,
0, 0, 0, 0, 0, 0, // Flash connection
GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
GPIO_KEY1, // GPIO13 Button
0, 0, 0, 0
}
};
/*
Optionals
{ "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
0, 0, 0, 0, 0, 0, // Flash connection
GPIO_PWM3, // GPIO12 RGB LED Blue
GPIO_PWM4, // GPIO13 RGBW LED White
GPIO_PWM1, // GPIO14 RGB LED Red
GPIO_LED2_INV, // GPIO15 RF receiver control
0, 0
}
{ "Xenon 3CH", // Xenon 3CH (ESP8266) - (#1128)
0, 0, 0,
GPIO_KEY2, // GPIO03 Serial TXD and Optional sensor

View File

@ -20,7 +20,7 @@
#ifndef _SONOFF_VERSION_H_
#define _SONOFF_VERSION_H_
#define VERSION 0x06020103
#define VERSION 0x06020109
#define D_PROGRAMNAME "Sonoff-Tasmota"
#define D_AUTHOR "Theo Arends"

View File

@ -494,6 +494,32 @@ double FastPrecisePow(double a, double b)
return r * u.d;
}
uint32_t SqrtInt(uint32_t num)
{
if (num <= 1) {
return num;
}
uint32_t x = num / 2;
uint32_t y;
do {
y = (x + num / x) / 2;
if (y >= x) {
return x;
}
x = y;
} while (true);
}
uint32_t RoundSqrtInt(uint32_t num)
{
uint32_t s = SqrtInt(4 * num);
if (s & 1) {
s++;
}
return s / 2;
}
char* GetTextIndexed(char* destination, size_t destination_size, uint16_t index, const char* haystack)
{
// Returns empty string if not found
@ -704,6 +730,21 @@ void ClaimSerial()
Settings.baudrate = baudrate / 1200;
}
void SerialSendRaw(char *codes, int size)
{
char *p;
char stemp[3];
uint8_t code;
while (size > 0) {
snprintf(stemp, sizeof(stemp), codes);
code = strtol(stemp, &p, 16);
Serial.write(code);
size -= 2;
codes += 2;
}
}
uint32_t GetHash(const char *buffer, size_t size)
{
uint32_t hash = 0;

View File

@ -282,7 +282,9 @@
#define USE_BMP // Enable BMP085/BMP180/BMP280/BME280 sensor (I2C address 0x76 or 0x77) (+4k code)
// #define USE_BME680 // Enable support for BME680 sensor using Bosch BME680 library (+4k code)
#define USE_BH1750 // Enable BH1750 sensor (I2C address 0x23 or 0x5C) (+0k5 code)
// #define USE_VEML6070 // Enable VEML6070 sensor (I2C addresses 0x38 and 0x39) (+0k5 code)
// #define USE_VEML6070 // Enable VEML6070 sensor (I2C addresses 0x38 and 0x39) (+1k5 code)
#define USE_VEML6070_RSET 270000 // VEML6070, Rset in Ohm used on PCB board, default 270K = 270000ohm, range for this sensor: 220K ... 1Meg
#define USE_VEML6070_SHOW_RAW // VEML6070, shows the raw value of UV-A
// #define USE_ADS1115 // Enable ADS1115 16 bit A/D converter (I2C address 0x48, 0x49, 0x4A or 0x4B) based on Adafruit ADS1x15 library (no library needed) (+0k7 code)
// #define USE_ADS1115_I2CDEV // Enable ADS1115 16 bit A/D converter (I2C address 0x48, 0x49, 0x4A or 0x4B) using library i2cdevlib-Core and i2cdevlib-ADS1115 (+2k code)
// #define USE_INA219 // Enable INA219 (I2C address 0x40, 0x41 0x44 or 0x45) Low voltage and current sensor (+1k code)
@ -294,9 +296,12 @@
// #define USE_SI1145 // Enable SI1145/46/47 sensor (I2C address 0x60) (+1k code)
#define USE_LM75AD // Enable LM75AD sensor (I2C addresses 0x48 - 0x4F) (+0k5 code)
// #define USE_APDS9960 // Enable APDS9960 Proximity Sensor (I2C address 0x39). Disables SHT and VEML6070 (+4k7 code)
// #define USE_MCP230xx // Enable MCP23008/MCP23017 for GP INPUT ONLY (I2C addresses 0x20 - 0x27) providing command Sensor29 for configuration (+4k7 code)
// #define USE_MCP230xx // Enable MCP23008/MCP23017 - Must define I2C Address in #define USE_MCP230xx_ADDR below - range 0x20 - 0x27 (+4k7 code)
// #define USE_MCP230xx_ADDR 0x20 // Enable MCP23008/MCP23017 I2C Address to use (Must be within range 0x20 through 0x27 - set according to your wired setup)
// #define USE_MCP230xx_OUTPUT // Enable MCP23008/MCP23017 OUTPUT support through sensor29 commands (+1k5 code)
// #define USE_MCP230xx_DISPLAYOUTPUT // Enable MCP23008/MCP23017 to display state of OUTPUT pins on Web UI (+0k2 code)
// #define USE_PCA9685 // Enable PCA9685 I2C HW PWM Driver - Must define I2C Address in #define USE_PCA9685_ADDR below - range 0x40 - 0x47 (+1k4 code)
// #define USE_PCA9685_ADDR 0x40 // Enable PCA9685 I2C Address to use (Must be within range 0x40 through 0x47 - set according to your wired setup)
// #define USE_MPR121 // Enable MPR121 controller (I2C addresses 0x5A, 0x5B, 0x5C and 0x5D) in input mode for touch buttons (+1k3 code)
// #define USE_CCS811 // Enable CCS811 sensor (I2C address 0x5A) (+2k2 code)
// #define USE_MPU6050 // Enable MPU6050 sensor (I2C address 0x68 AD0 low or 0x69 AD0 high) (+2k6 code)
@ -318,7 +323,7 @@
#endif // USE_I2C
// -- SPI sensors ---------------------------------
//#define USE_SPI // SPI using library TasmotaTFT
//#define USE_SPI // Hardware SPI using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) in addition to two user selectable GPIOs(CS and DC)
#ifdef USE_SPI
#ifndef USE_DISPLAY
@ -342,11 +347,12 @@
//#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 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 MP3_VOLUME 10 // Set the startup volume on init, the range can be 0..30(max)
// Power monitoring sensors -----------------------
#define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code)
#define USE_PZEM2 // Add support for PZEM003,014,016,017 Energy monitor (+1k1 code)
#define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code)
// -- Low level interface devices -----------------
#define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram)
@ -380,6 +386,7 @@
//#define USE_CLASSIC // Create sonoff-classic with initial configuration tools WPS, SmartConfig and WifiManager
//#define USE_SENSORS // Create sonoff-sensors with useful sensors enabled
//#define USE_KNX_NO_EMULATION // Create sonoff-knx with KNX but without Emulation
//#define USE_DISPLAYS // Create sonoff-display with display drivers enabled
//#define BE_MINIMAL // Create sonoff-minimal as intermediate firmware for OTA-MAGIC
/*********************************************************************************************\

View File

@ -29,8 +29,6 @@
uint8_t *efm8bb1_update = NULL;
#endif // USE_RF_FLASH
#define D_TASMOTA_TOKEN "Tasmota-Token"
enum UploadTypes { UPL_TASMOTA, UPL_SETTINGS, UPL_EFM8BB1 };
const char HTTP_HEAD[] PROGMEM =
@ -58,17 +56,13 @@ const char HTTP_HEAD[] PROGMEM =
"eb('s1').value=l.innerText||l.textContent;"
"eb('p1').focus();"
"}"
"function lx(){"
"if(to==1){"
"if(tp<30){"
"tp++;"
"lt=setTimeout(lx,33);" // Wait for token from server
"}else{"
"lt=setTimeout(la,1355);" // Discard action and retry
"}"
"return;"
"function la(p){"
"var a='';"
"if(la.arguments.length==1){"
"a=p;"
"clearTimeout(lt);"
"}"
"if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1)
"if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1)
"x=new XMLHttpRequest();"
"x.onreadystatechange=function(){"
"if(x.readyState==4&&x.status==200){"
@ -76,32 +70,15 @@ const char HTTP_HEAD[] PROGMEM =
"eb('l1').innerHTML=s;"
"}"
"};"
"x.open('GET','ay'+pc,true);" // Async request
"x.setRequestHeader('" D_TASMOTA_TOKEN "',to);"
"x.send();" // Perform command if available and get updated information
"pc='';"
"lt=setTimeout(la,2345-(tp*33));"
"}"
"function la(p){"
"if(la.arguments.length==1){"
"pc='?'+p;"
"clearTimeout(lt);"
"}else{pc='';}"
"to=1;tp=0;"
"if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1)
"x=new XMLHttpRequest();"
"x.onreadystatechange=function(){"
"if(x.readyState==4&&x.status==200){to=x.getResponseHeader('" D_TASMOTA_TOKEN "');}else{to=1;}"
"};"
"x.open('GET','az',true);" // Async request
"x.send();" // Get token from server
"lx();"
"x.open('GET','ay'+a,true);"
"x.send();"
"lt=setTimeout(la,2345);"
"}"
"function lb(p){"
"la('d='+p);"
"la('?d='+p);"
"}"
"function lc(p){"
"la('c='+p);"
"la('?t='+p);"
"}";
const char HTTP_HEAD_STYLE[] PROGMEM =
@ -340,7 +317,7 @@ const char HTTP_END[] PROGMEM =
"</body>"
"</html>";
const char HTTP_DEVICE_CONTROL[] PROGMEM = "<td style='width:%d%%'><button onclick='la(\"o=%d\");'>%s%s</button></td>";
const char HTTP_DEVICE_CONTROL[] PROGMEM = "<td style='width:%d%%'><button onclick='la(\"?o=%d\");'>%s%s</button></td>";
const char HTTP_DEVICE_STATE[] PROGMEM = "%s<td style='width:%d{c}%s;font-size:%dpx'>%s</div></td>"; // {c} = %'><div style='text-align:center;font-weight:
const char HDR_CTYPE_PLAIN[] PROGMEM = "text/plain";
@ -349,8 +326,6 @@ const char HDR_CTYPE_XML[] PROGMEM = "text/xml";
const char HDR_CTYPE_JSON[] PROGMEM = "application/json";
const char HDR_CTYPE_STREAM[] PROGMEM = "application/octet-stream";
const char HDR_TASMOTA_TOKEN[] PROGMEM = D_TASMOTA_TOKEN;
#define DNS_PORT 53
enum HttpOptions {HTTP_OFF, HTTP_USER, HTTP_ADMIN, HTTP_MANAGER};
@ -366,7 +341,6 @@ uint8_t upload_progress_dot_count;
uint8_t config_block_count = 0;
uint8_t config_xor_on = 0;
uint8_t config_xor_on_set = CONFIG_FILE_XOR;
long ajax_token = 1;
// Helper function to avoid code duplication (saves 4k Flash)
static void WebGetArg(const char* arg, char* out, size_t max)
@ -400,11 +374,10 @@ void StartWebserver(int type, IPAddress ipweb)
WebServer->on("/up", HandleUpgradeFirmware);
WebServer->on("/u1", HandleUpgradeFirmwareStart); // OTA
WebServer->on("/u2", HTTP_POST, HandleUploadDone, HandleUploadLoop);
WebServer->on("/u2", HTTP_OPTIONS, HandlePreflightRequest);
WebServer->on("/cs", HandleConsole);
WebServer->on("/ax", HandleAjaxConsoleRefresh);
WebServer->on("/ay", HandleAjaxStatusRefresh);
WebServer->on("/az", HandleToken);
WebServer->on("/u2", HTTP_OPTIONS, HandlePreflightRequest);
WebServer->on("/cm", HandleHttpCommand);
WebServer->on("/rb", HandleRestart);
#ifndef BE_MINIMAL
@ -509,6 +482,15 @@ void SetHeader()
#endif
}
bool WebAuthenticate(void)
{
if (Settings.web_password[0] != 0) {
return WebServer->authenticate(WEB_USERNAME, Settings.web_password);
} else {
return true;
}
}
void ShowPage(String &page, bool auth)
{
if (auth && (Settings.web_password[0] != 0) && !WebServer->authenticate(WEB_USERNAME, Settings.web_password)) {
@ -619,7 +601,7 @@ void HandleRoot()
if (idx > 0) { page += F("</tr><tr>"); }
for (byte j = 0; j < 4; j++) {
idx++;
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("<td style='width:25%'><button onclick='la(\"k=%d\");'>%d</button></td>"), idx, idx);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("<td style='width:25%'><button onclick='la(\"?k=%d\");'>%d</button></td>"), idx, idx);
page += mqtt_data;
}
}
@ -634,34 +616,13 @@ void HandleRoot()
}
}
void HandleToken()
{
char token[11];
ajax_token = random(2, 0x7FFFFFFF);
snprintf_P(token, sizeof(token), PSTR("%u"), ajax_token);
SetHeader();
WebServer->sendHeader(FPSTR(HDR_TASMOTA_TOKEN), token);
snprintf_P(token, sizeof(token), PSTR("%u"), random(0x7FFFFFFF));
WebServer->send(200, FPSTR(HDR_CTYPE_HTML), token);
const char* header_key[] = { D_TASMOTA_TOKEN };
WebServer->collectHeaders(header_key, 1);
}
void HandleAjaxStatusRefresh()
{
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
char svalue[80];
char tmp[100];
if (WebServer->header(FPSTR(HDR_TASMOTA_TOKEN)).toInt() != ajax_token) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(D_FILE_NOT_FOUND));
SetHeader();
WebServer->send(404, FPSTR(HDR_CTYPE_PLAIN), mqtt_data);
return;
}
ajax_token = 1;
WebGetArg("o", tmp, sizeof(tmp));
if (strlen(tmp)) {
ShowWebSource(SRC_WEBGUI);
@ -736,6 +697,7 @@ boolean HttpUser()
void HandleConfiguration()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURATION);
String page = FPSTR(HTTP_HEAD);
@ -759,6 +721,7 @@ void HandleConfiguration()
void HandleModuleConfiguration()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
char stemp[20];
uint8_t midx;
@ -829,6 +792,7 @@ void HandleWifiConfiguration()
void HandleWifi(boolean scan)
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_WIFI);
@ -925,6 +889,7 @@ void HandleWifi(boolean scan)
void HandleMqttConfiguration()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MQTT);
String page = FPSTR(HTTP_HEAD);
@ -948,6 +913,7 @@ void HandleMqttConfiguration()
void HandleLoggingConfiguration()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_LOGGING);
String page = FPSTR(HTTP_HEAD);
@ -995,6 +961,7 @@ void HandleLoggingConfiguration()
void HandleOtherConfiguration()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_OTHER);
char stemp[40];
@ -1032,6 +999,7 @@ void HandleOtherConfiguration()
void HandleBackupConfiguration()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_BACKUP_CONFIGURATION));
if (!SettingsBufferAlloc()) { return; }
@ -1067,6 +1035,7 @@ void HandleBackupConfiguration()
void HandleSaveSettings()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
char stemp[TOPSZ];
char stemp2[TOPSZ];
@ -1232,6 +1201,7 @@ void HandleSaveSettings()
void HandleResetConfiguration()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
char svalue[33];
@ -1252,6 +1222,7 @@ void HandleResetConfiguration()
void HandleRestoreConfiguration()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTORE_CONFIGURATION);
String page = FPSTR(HTTP_HEAD);
@ -1270,6 +1241,7 @@ void HandleRestoreConfiguration()
void HandleInformation()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_INFORMATION);
char stopic[TOPSZ];
@ -1386,6 +1358,7 @@ void HandleInformation()
void HandleUpgradeFirmware()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_FIRMWARE_UPGRADE);
String page = FPSTR(HTTP_HEAD);
@ -1405,6 +1378,7 @@ void HandleUpgradeFirmware()
void HandleUpgradeFirmwareStart()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
char svalue[100];
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPGRADE_STARTED));
@ -1432,6 +1406,7 @@ void HandleUpgradeFirmwareStart()
void HandleUploadDone()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPLOAD_DONE));
char error[100];
@ -1684,6 +1659,7 @@ void HandlePreflightRequest()
void HandleHttpCommand()
{
if (HttpUser()) { return; }
// if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
char svalue[INPUT_BUFFER_SIZE]; // Large to serve Backlog
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_COMMAND));
@ -1741,6 +1717,7 @@ void HandleHttpCommand()
void HandleConsole()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONSOLE);
String page = FPSTR(HTTP_HEAD);
@ -1756,6 +1733,7 @@ void HandleConsole()
void HandleAjaxConsoleRefresh()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
char svalue[INPUT_BUFFER_SIZE]; // Large to serve Backlog
byte cflg = 1;
byte counter = 0; // Initial start, should never be 0 again
@ -1812,6 +1790,7 @@ void HandleAjaxConsoleRefresh()
void HandleRestart()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTART);
String page = FPSTR(HTTP_HEAD);

View File

@ -29,35 +29,37 @@
enum EnergyCommands {
CMND_POWERDELTA,
CMND_POWERLOW, CMND_POWERHIGH, CMND_VOLTAGELOW, CMND_VOLTAGEHIGH, CMND_CURRENTLOW, CMND_CURRENTHIGH,
CMND_POWERCAL, CMND_POWERSET, CMND_VOLTAGECAL, CMND_VOLTAGESET, CMND_CURRENTCAL, CMND_CURRENTSET,
CMND_POWERSET, CMND_VOLTAGESET, CMND_CURRENTSET, CMND_FREQUENCYSET,
CMND_ENERGYRESET, CMND_MAXENERGY, CMND_MAXENERGYSTART,
CMND_MAXPOWER, CMND_MAXPOWERHOLD, CMND_MAXPOWERWINDOW,
CMND_SAFEPOWER, CMND_SAFEPOWERHOLD, CMND_SAFEPOWERWINDOW };
const char kEnergyCommands[] PROGMEM =
D_CMND_POWERDELTA "|"
D_CMND_POWERLOW "|" D_CMND_POWERHIGH "|" D_CMND_VOLTAGELOW "|" D_CMND_VOLTAGEHIGH "|" D_CMND_CURRENTLOW "|" D_CMND_CURRENTHIGH "|"
D_CMND_POWERCAL "|" D_CMND_POWERSET "|" D_CMND_VOLTAGECAL "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTCAL "|" D_CMND_CURRENTSET "|"
D_CMND_POWERSET "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTSET "|" D_CMND_FREQUENCYSET "|"
D_CMND_ENERGYRESET "|" D_CMND_MAXENERGY "|" D_CMND_MAXENERGYSTART "|"
D_CMND_MAXPOWER "|" D_CMND_MAXPOWERHOLD "|" D_CMND_MAXPOWERWINDOW "|"
D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW ;
float energy_voltage = 0; // 123.1 V
float energy_current = 0; // 123.123 A
float energy_power = 0; // 123.1 W
float energy_power_factor = NAN; // 0.12
int energy_calc_power_factor = 0; // Do not calculate power factor from data
float energy_frequency = NAN; // 123.1 Hz
float energy_start = 0; // 12345.12345 kWh total previous
float energy_voltage = 0; // 123.1 V
float energy_current = 0; // 123.123 A
float energy_active_power = 0; // 123.1 W
float energy_apparent_power = NAN; // 123.1 VA
float energy_reactive_power = NAN; // 123.1 VAr
float energy_power_factor = NAN; // 0.12
float energy_frequency = NAN; // 123.1 Hz
float energy_start = 0; // 12345.12345 kWh total previous
float energy_daily = 0; // 123.123 kWh
float energy_total = 0; // 12345.12345 kWh
float energy_daily = 0; // 123.123 kWh
float energy_total = 0; // 12345.12345 kWh
unsigned long energy_kWhtoday_delta = 0; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to energy_kWhtoday (HLW and CSE only)
unsigned long energy_kWhtoday; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = energy_daily
unsigned long energy_period = 0; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = energy_daily
unsigned long energy_kWhtoday; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = energy_daily
unsigned long energy_period = 0; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = energy_daily
float energy_power_last[3] = { 0 };
uint8_t energy_power_delta = 0;
bool energy_type_dc = false;
bool energy_power_on = true;
byte energy_min_power_flag = 0;
@ -98,6 +100,8 @@ void EnergyUpdateToday()
void Energy200ms()
{
energy_power_on = (power != 0) | Settings.flag.no_power_on_check;
energy_fifth_second++;
if (5 == energy_fifth_second) {
energy_fifth_second = 0;
@ -121,18 +125,7 @@ void Energy200ms()
}
}
energy_power_on = (power &1) | Settings.flag.no_power_on_check;
XnrgCall(FUNC_EVERY_200_MSECOND);
if (energy_calc_power_factor) {
float power_factor = 0;
if (energy_voltage && energy_current && energy_power) {
power_factor = energy_power / (energy_voltage * energy_current);
if (power_factor > 1) power_factor = 1;
}
energy_power_factor = power_factor;
}
}
void EnergySaveState()
@ -178,21 +171,21 @@ void EnergyMarginCheck()
}
if (Settings.energy_power_delta) {
float delta = abs(energy_power_last[0] - energy_power);
float delta = abs(energy_power_last[0] - energy_active_power);
// Any delta compared to minimal delta
float min_power = (energy_power_last[0] > energy_power) ? energy_power : energy_power_last[0];
float min_power = (energy_power_last[0] > energy_active_power) ? energy_active_power : energy_power_last[0];
if (((delta / min_power) * 100) > Settings.energy_power_delta) {
energy_power_delta = 1;
energy_power_last[1] = energy_power; // We only want one report so reset history
energy_power_last[2] = energy_power;
energy_power_last[1] = energy_active_power; // We only want one report so reset history
energy_power_last[2] = energy_active_power;
}
}
energy_power_last[0] = energy_power_last[1]; // Shift in history every second allowing power changes to settle for up to three seconds
energy_power_last[1] = energy_power_last[2];
energy_power_last[2] = energy_power;
energy_power_last[2] = energy_active_power;
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_power);
energy_power_u = (uint16_t)(energy_active_power);
energy_voltage_u = (uint16_t)(energy_voltage);
energy_current_u = (uint16_t)(energy_current * 1000);
@ -235,7 +228,7 @@ void EnergyMarginCheck()
#if FEATURE_POWER_LIMIT
// Max Power
if (Settings.energy_max_power_limit) {
if (energy_power > Settings.energy_max_power_limit) {
if (energy_active_power > Settings.energy_max_power_limit) {
if (!energy_mplh_counter) {
energy_mplh_counter = Settings.energy_max_power_limit_hold;
} else {
@ -415,42 +408,22 @@ boolean EnergyCommand()
command, energy_total_chr, energy_yesterday_chr, energy_daily_chr);
status_flag = 1;
}
else if ((CMND_POWERCAL == command_code) && XnrgCall(FUNC_COMMAND)) {
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 32001)) {
Settings.energy_power_calibration = (XdrvMailbox.payload > 4000) ? XdrvMailbox.payload : HLW_PREF_PULSE; // HLW = 12530, CSE = 5364
}
nvalue = Settings.energy_power_calibration;
unit = UNIT_MICROSECOND;
}
else if ((CMND_VOLTAGECAL == command_code) && XnrgCall(FUNC_COMMAND)) {
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 32001)) {
Settings.energy_voltage_calibration = (XdrvMailbox.payload > 999) ? XdrvMailbox.payload : HLW_UREF_PULSE; // HLW = 1950, CSE = 1912
}
nvalue = Settings.energy_voltage_calibration;
unit = UNIT_MICROSECOND;
}
else if ((CMND_CURRENTCAL == command_code) && XnrgCall(FUNC_COMMAND)) {
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 32001)) {
Settings.energy_current_calibration = (XdrvMailbox.payload > 1100) ? XdrvMailbox.payload : HLW_IREF_PULSE; // HLW = 3500, CSE = 16140
}
nvalue = Settings.energy_current_calibration;
unit = UNIT_MICROSECOND;
}
else if ((CMND_POWERSET == command_code) && XnrgCall(FUNC_COMMAND)) { // Watt
snprintf_P(command, sizeof(command), PSTR(D_CMND_POWERCAL));
nvalue = Settings.energy_power_calibration;
unit = UNIT_MICROSECOND;
}
else if ((CMND_VOLTAGESET == command_code) && XnrgCall(FUNC_COMMAND)) { // Volt
snprintf_P(command, sizeof(command), PSTR(D_CMND_VOLTAGECAL));
nvalue = Settings.energy_voltage_calibration;
unit = UNIT_MICROSECOND;
}
else if ((CMND_CURRENTSET == command_code) && XnrgCall(FUNC_COMMAND)) { // milliAmpere
snprintf_P(command, sizeof(command), PSTR(D_CMND_CURRENTCAL));
nvalue = Settings.energy_current_calibration;
unit = UNIT_MICROSECOND;
}
else if ((CMND_FREQUENCYSET == command_code) && XnrgCall(FUNC_COMMAND)) { // Hz
nvalue = Settings.energy_frequency_calibration;
unit = UNIT_MICROSECOND;
}
#if FEATURE_POWER_LIMIT
else if (CMND_MAXPOWER == command_code) {
@ -514,6 +487,11 @@ boolean EnergyCommand()
else serviced = false; // Unknown command
if (serviced && !status_flag) {
if (UNIT_MICROSECOND == unit) {
snprintf_P(command, sizeof(command), PSTR("%sCal"), command);
}
if (Settings.flag.value_units) {
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_LVALUE_SPACE_UNIT, command, nvalue, GetTextIndexed(sunit, sizeof(sunit), unit, kUnitNames));
} else {
@ -550,6 +528,8 @@ const char HTTP_ENERGY_SNS1[] PROGMEM = "%s"
"{s}" D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}";
const char HTTP_ENERGY_SNS2[] PROGMEM = "%s"
"{s}" D_POWERUSAGE_APPARENT "{m}%s " D_UNIT_VA "{e}"
"{s}" D_POWERUSAGE_REACTIVE "{m}%s " D_UNIT_VAR "{e}"
"{s}" D_POWER_FACTOR "{m}%s{e}";
const char HTTP_ENERGY_SNS3[] PROGMEM = "%s"
@ -563,27 +543,65 @@ const char HTTP_ENERGY_SNS4[] PROGMEM = "%s"
void EnergyShow(boolean json)
{
char energy_total_chr[10];
char voltage_chr[10];
char current_chr[10];
char active_power_chr[10];
char apparent_power_chr[10];
char reactive_power_chr[10];
char power_factor_chr[10];
char frequency_chr[10];
char energy_daily_chr[10];
char energy_period_chr[10];
char energy_power_chr[10];
char energy_voltage_chr[10];
char energy_current_chr[10];
char energy_frequency_chr[10];
char energy_power_factor_chr[10];
char energy_yesterday_chr[10];
char energy_total_chr[10];
char speriod[20];
char spfactor[20];
char sfrequency[20];
bool show_energy_period = (0 == tele_period);
dtostrfd(energy_power, Settings.flag2.wattage_resolution, energy_power_chr);
dtostrfd(energy_voltage, Settings.flag2.voltage_resolution, energy_voltage_chr);
dtostrfd(energy_current, Settings.flag2.current_resolution, energy_current_chr);
dtostrfd(energy_total, Settings.flag2.energy_resolution, energy_total_chr);
float power_factor = energy_power_factor;
if (!energy_type_dc) {
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);
snprintf_P(sfrequency, sizeof(sfrequency), PSTR(",\"" D_JSON_FREQUENCY "\":%s"), frequency_chr);
}
}
dtostrfd(energy_voltage, Settings.flag2.voltage_resolution, voltage_chr);
dtostrfd(energy_current, Settings.flag2.current_resolution, current_chr);
dtostrfd(energy_active_power, Settings.flag2.wattage_resolution, active_power_chr);
dtostrfd(energy_daily, Settings.flag2.energy_resolution, energy_daily_chr);
dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr);
dtostrfd(energy_total, Settings.flag2.energy_resolution, energy_total_chr);
float energy = 0;
if (show_energy_period) {
@ -592,34 +610,30 @@ void EnergyShow(boolean json)
dtostrfd(energy, Settings.flag2.wattage_resolution, energy_period_chr);
snprintf_P(speriod, sizeof(speriod), PSTR(",\"" D_JSON_PERIOD "\":%s"), energy_period_chr);
}
if (!isnan(energy_frequency)) {
dtostrfd(energy_frequency, Settings.flag2.frequency_resolution, energy_frequency_chr);
snprintf_P(sfrequency, sizeof(sfrequency), PSTR(",\"" D_JSON_FREQUENCY "\":%s"), energy_frequency_chr);
}
if (!isnan(energy_power_factor)) {
dtostrfd(energy_power_factor, 2, energy_power_factor_chr);
snprintf_P(spfactor, sizeof(spfactor), PSTR(",\"" D_JSON_POWERFACTOR "\":%s"), energy_power_factor_chr);
}
if (json) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s%s,\""
D_JSON_POWERUSAGE "\":%s%s,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s%s}"),
mqtt_data, energy_total_chr, energy_yesterday_chr, energy_daily_chr, (show_energy_period) ? speriod : "",
energy_power_chr, (!isnan(energy_power_factor)) ? spfactor : "", energy_voltage_chr, energy_current_chr, (!isnan(energy_frequency)) ? sfrequency : "");
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s%s,\"" D_JSON_POWERUSAGE "\":%s"),
mqtt_data, energy_total_chr, energy_yesterday_chr, energy_daily_chr, (show_energy_period) ? speriod : "", active_power_chr);
if (!energy_type_dc) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_APPARENT_POWERUSAGE "\":%s,\"" D_JSON_REACTIVE_POWERUSAGE "\":%s,\"" D_JSON_POWERFACTOR "\":%s%s"),
mqtt_data, apparent_power_chr, reactive_power_chr, power_factor_chr, (!isnan(energy_frequency)) ? sfrequency : "");
}
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s}"), mqtt_data, voltage_chr, current_chr);
#ifdef USE_DOMOTICZ
if (show_energy_period) { // Only send if telemetry
dtostrfd(energy_total * 1000, 1, energy_total_chr);
DomoticzSensorPowerEnergy((int)energy_power, energy_total_chr); // PowerUsage, EnergyToday
DomoticzSensor(DZ_VOLTAGE, energy_voltage_chr); // Voltage
DomoticzSensor(DZ_CURRENT, energy_current_chr); // Current
DomoticzSensorPowerEnergy((int)energy_active_power, energy_total_chr); // PowerUsage, EnergyToday
DomoticzSensor(DZ_VOLTAGE, voltage_chr); // Voltage
DomoticzSensor(DZ_CURRENT, current_chr); // Current
}
#endif // USE_DOMOTICZ
#ifdef USE_KNX
if (show_energy_period) {
KnxSensor(KNX_ENERGY_VOLTAGE, energy_voltage);
KnxSensor(KNX_ENERGY_CURRENT, energy_current);
KnxSensor(KNX_ENERGY_POWER, energy_power);
if (!isnan(energy_power_factor)) { KnxSensor(KNX_ENERGY_POWERFACTOR, energy_power_factor); }
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);
@ -627,9 +641,11 @@ void EnergyShow(boolean json)
#endif // USE_KNX
#ifdef USE_WEBSERVER
} else {
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS1, mqtt_data, energy_voltage_chr, energy_current_chr, energy_power_chr);
if (!isnan(energy_power_factor)) { snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS2, mqtt_data, energy_power_factor_chr); }
if (!isnan(energy_frequency)) { snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS3, mqtt_data, energy_frequency_chr); }
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS1, mqtt_data, voltage_chr, current_chr, active_power_chr);
if (!energy_type_dc) {
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS2, mqtt_data, apparent_power_chr, reactive_power_chr, power_factor_chr);
if (!isnan(energy_frequency)) { snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS3, mqtt_data, frequency_chr); }
}
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS4, mqtt_data, energy_daily_chr, energy_yesterday_chr, energy_total_chr);
#endif // USE_WEBSERVER
}

View File

@ -199,21 +199,6 @@ uint8_t SnfBrUpdateInit()
/********************************************************************************************/
void SonoffBridgeSendRaw(char *codes, int size)
{
char *p;
char stemp[3];
uint8_t code;
while (size > 0) {
snprintf(stemp, sizeof(stemp), codes);
code = strtol(stemp, &p, 16);
Serial.write(code);
size -= 2;
codes += 2;
}
}
void SonoffBridgeReceivedRaw()
{
// Decoding according to https://github.com/Portisch/RF-Bridge-EFM8BB1
@ -552,11 +537,11 @@ boolean SonoffBridgeCommand()
break;
case 192: // 0xC0 - Beep
char beep[] = "AAC000C055";
SonoffBridgeSendRaw(beep, sizeof(beep));
SerialSendRaw(beep, sizeof(beep));
break;
}
} else {
SonoffBridgeSendRaw(XdrvMailbox.data, XdrvMailbox.data_len);
SerialSendRaw(XdrvMailbox.data, XdrvMailbox.data_len);
sonoff_bridge_receive_raw_flag = 1;
}
}

View File

@ -375,10 +375,8 @@ const char S_CONFIGURE_DOMOTICZ[] PROGMEM = D_CONFIGURE_DOMOTICZ;
void HandleDomoticzConfiguration()
{
if (HTTP_USER == webserver_state) {
HandleRoot();
return;
}
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_DOMOTICZ);
char stemp[32];

View File

@ -676,10 +676,8 @@ const char S_CONFIGURE_TIMER[] PROGMEM = D_CONFIGURE_TIMER;
void HandleTimerConfiguration()
{
if (HTTP_USER == webserver_state) {
HandleRoot();
return;
}
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_TIMER);
String page = FPSTR(HTTP_HEAD);

View File

@ -366,6 +366,7 @@ void RulesEvery50ms()
if (Settings.rule_enabled) { // Any rule enabled
char json_event[120];
if (-1 == rules_new_power) { rules_new_power = power; }
if (rules_new_power != rules_old_power) {
if (rules_old_power != -1) {
for (byte i = 0; i < devices_present; i++) {
@ -375,6 +376,25 @@ void RulesEvery50ms()
RulesProcessEvent(json_event);
}
}
} else {
// Boot time POWER OUTPUTS (Relays) Status
for (byte i = 0; i < devices_present; i++) {
uint8_t new_state = (rules_new_power >> i) &1;
snprintf_P(json_event, sizeof(json_event), PSTR("{\"Power%d\":{\"Boot\":%d}}"), i +1, new_state);
RulesProcessEvent(json_event);
}
// Boot time SWITCHES Status
for (byte i = 0; i < MAX_SWITCHES; i++) {
#ifdef USE_TM1638
if ((pin[GPIO_SWT1 +i] < 99) || ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99))) {
#else
if (pin[GPIO_SWT1 +i] < 99) {
#endif // USE_TM1638
boolean swm = ((FOLLOW_INV == Settings.switchmode[i]) || (PUSHBUTTON_INV == Settings.switchmode[i]) || (PUSHBUTTONHOLD_INV == Settings.switchmode[i]));
snprintf_P(json_event, sizeof(json_event), PSTR("{\"" D_JSON_SWITCH "%d\":{\"Boot\":%d}}"), i +1, (swm ^ lastwallswitch[i]));
RulesProcessEvent(json_event);
}
}
}
rules_old_power = rules_new_power;
}
@ -424,7 +444,7 @@ void RulesEvery50ms()
void RulesEvery100ms()
{
if (Settings.rule_enabled) { // Any rule enabled
if (Settings.rule_enabled && (uptime > 4)) { // Any rule enabled and allow 4 seconds start-up time for sensors (#3811)
mqtt_data[0] = '\0';
int tele_period_save = tele_period;
tele_period = 2; // Do not allow HA updates during next function call

View File

@ -803,15 +803,13 @@ const char HTTP_FORM_KNX_ADD_TABLE_ROW2[] PROGMEM =
void HandleKNXConfiguration()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_KNX);
char tmp[100];
String stmp;
if (HTTP_USER == webserver_state) {
HandleRoot();
return;
}
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_KNX);
if ( WebServer->hasArg("save") ) {
KNX_Save_Settings();
HandleConfiguration();

View File

@ -19,7 +19,7 @@
#ifdef USE_HOME_ASSISTANT
const char HASS_DISCOVER_SWITCH[] PROGMEM =
const char HASS_DISCOVER_RELAY[] PROGMEM =
"{\"name\":\"%s\"," // dualr2 1
"\"command_topic\":\"%s\"," // cmnd/dualr2/POWER2
"\"state_topic\":\"%s\"," // stat/dualr2/RESULT (implies "\"optimistic\":\"false\",")
@ -39,7 +39,8 @@ const char HASS_DISCOVER_BUTTON[] PROGMEM =
// "\"optimistic\":\"false\"," // false is Hass default when state_topic is set
"\"availability_topic\":\"%s\"," // tele/dualr2/LWT
"\"payload_available\":\"" D_ONLINE "\"," // Online
"\"payload_not_available\":\"" D_OFFLINE "\""; // Offline
"\"payload_not_available\":\"" D_OFFLINE "\"," // Offline
"\"force_update\":true";
const char HASS_DISCOVER_LIGHT_DIMMER[] PROGMEM =
"%s,\"brightness_command_topic\":\"%s\"," // cmnd/led2/Dimmer
@ -75,7 +76,7 @@ void HAssDiscoverRelay()
for (int i = 1; i <= MAX_RELAYS; i++) {
is_light = ((i == devices_present) && (light_type));
is_topic_light = Settings.flag.hass_light;
is_topic_light = Settings.flag.hass_light || is_light;
mqtt_data[0] = '\0'; // Clear retained message
@ -102,7 +103,7 @@ void HAssDiscoverRelay()
GetTopic_P(command_topic, CMND, mqtt_topic, value_template);
GetTopic_P(state_topic, STAT, mqtt_topic, S_RSLT_RESULT);
GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT);
snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SWITCH, name, command_topic, state_topic, value_template, Settings.state_text[0], Settings.state_text[1], availability_topic);
snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_RELAY, name, command_topic, state_topic, value_template, Settings.state_text[0], Settings.state_text[1], availability_topic);
if (is_light) {
char brightness_command_topic[TOPSZ];

184
sonoff/xdrv_15_pca9685.ino Normal file
View File

@ -0,0 +1,184 @@
/*
xdrv_15_pca9685.ino - Support for I2C PCA9685 12bit 16 pin hardware PWM driver
Copyright (C) 2018 Andre Thomas 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_I2C
#ifdef USE_PCA9685
#define XDRV_15 15
#define PCA9685_REG_MODE1 0x00
#define PCA9685_REG_LED0_ON_L 0x06
#define PCA9685_REG_PRE_SCALE 0xFE
uint8_t pca9685_detected = 0;
uint16_t pca9685_freq = 50;
void PCA9685_Detect(void)
{
if (pca9685_detected) { return; }
uint8_t buffer;
if (I2cValidRead8(&buffer, USE_PCA9685_ADDR, PCA9685_REG_MODE1)) {
I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, 0x20);
if (I2cValidRead8(&buffer, USE_PCA9685_ADDR, PCA9685_REG_MODE1)) {
if (0x20 == buffer) {
pca9685_detected = 1;
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "PCA9685", USE_PCA9685_ADDR);
AddLog(LOG_LEVEL_DEBUG);
PCA9685_Reset(); // Reset the controller
}
}
}
}
void PCA9685_Reset(void)
{
I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, 0x80);
PCA9685_SetPWMfreq(50);
for (uint8_t pin=0;pin<16;pin++) {
PCA9685_SetPWM(pin,0,false);
}
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"RESET\":\"OK\"}}"));
}
void PCA9685_SetPWMfreq(double freq) {
/*
7.3.5 from datasheet
prescale value = round(25000000/(4096*freq))-1;
*/
pca9685_freq=freq;
uint8_t pre_scale_osc = round(25000000/(4096*freq))-1;
if (1526 == freq) pre_scale_osc=0xFF; // force setting for 24hz because rounding causes 1526 to be 254
uint8_t current_mode1 = I2cRead8(USE_PCA9685_ADDR, PCA9685_REG_MODE1); // read current value of MODE1 register
uint8_t sleep_mode1 = (current_mode1&0x7F) | 0x10; // Determine register value to put PCA to sleep
I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, sleep_mode1); // Let's sleep a little
I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_PRE_SCALE, pre_scale_osc); // Set the pre-scaler
I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, current_mode1 | 0xA0); // Reset MODE1 register to original state and enable auto increment
}
void PCA9685_SetPWM_Reg(uint8_t pin, uint16_t on, uint16_t off) {
uint8_t led_reg = PCA9685_REG_LED0_ON_L + 4 * pin;
uint32_t led_data = 0;
I2cWrite8(USE_PCA9685_ADDR, led_reg, on);
I2cWrite8(USE_PCA9685_ADDR, led_reg+1, (on >> 8));
I2cWrite8(USE_PCA9685_ADDR, led_reg+2, off);
I2cWrite8(USE_PCA9685_ADDR, led_reg+3, (off >> 8));
}
void PCA9685_SetPWM(uint8_t pin, uint16_t pwm, bool inverted) {
if (4096 == pwm) {
PCA9685_SetPWM_Reg(pin, 4096, 0); // Special use additional bit causes channel to turn on completely without PWM
} else {
PCA9685_SetPWM_Reg(pin, 0, pwm);
}
}
bool PCA9685_Command(void)
{
boolean serviced = true;
boolean validpin = false;
uint8_t paramcount = 0;
if (XdrvMailbox.data_len > 0) {
paramcount=1;
} else {
serviced = false;
return serviced;
}
char sub_string[XdrvMailbox.data_len];
for (uint8_t ca=0;ca<XdrvMailbox.data_len;ca++) {
if ((' ' == XdrvMailbox.data[ca]) || ('=' == XdrvMailbox.data[ca])) { XdrvMailbox.data[ca] = ','; }
if (',' == XdrvMailbox.data[ca]) { paramcount++; }
}
UpperCase(XdrvMailbox.data,XdrvMailbox.data);
if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"RESET")) { PCA9685_Reset(); return serviced; }
if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"PWMF")) {
if (paramcount > 1) {
uint16_t new_freq = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2));
if ((new_freq >= 24) && (new_freq <= 1526)) {
PCA9685_SetPWMfreq(new_freq);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"PWMF\":%i, \"Result\":\"OK\"}}"));
return serviced;
}
} else { // No parameter was given for setfreq, so we return current setting
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"PWMF\":%i}}"),pca9685_freq);
return serviced;
}
}
if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"PWM")) {
if (paramcount > 1) {
uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2));
if (paramcount > 2) {
if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 3), "ON")) {
PCA9685_SetPWM(pin, 4096, false);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,4096);
serviced = true;
return serviced;
}
if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 3), "OFF")) {
PCA9685_SetPWM(pin, 0, false);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,0);
serviced = true;
return serviced;
}
uint16_t pwm = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3));
if ((pin >= 0 && pin <= 15) && (pwm >= 0 && pwm <= 4096)) {
PCA9685_SetPWM(pin, pwm, false);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,pwm);
serviced = true;
return serviced;
}
}
}
}
return serviced;
}
boolean Xdrv15(byte function)
{
boolean result = false;
if (i2c_flg) {
switch (function) {
case FUNC_MQTT_DATA:
break;
case FUNC_EVERY_SECOND:
PCA9685_Detect();
break;
case FUNC_EVERY_50_MSECOND:
break;
case FUNC_JSON_APPEND:
break;
case FUNC_COMMAND:
if (XDRV_15 == XdrvMailbox.index) {
PCA9685_Command();
}
break;
case FUNC_WEB_APPEND:
break;
default:
break;
}
}
return result;
}
#endif // USE_PCA9685
#endif // USE_IC2

View File

@ -35,20 +35,25 @@
* Debug commands
\*********************************************************************************************/
#define D_CMND_CFGDUMP "CfgDump"
#define D_CMND_CFGPOKE "CfgPoke"
#define D_CMND_CFGPEEK "CfgPeek"
#define D_CMND_CFGXOR "CfgXor"
#define D_CMND_CFGDUMP "CfgDump"
#define D_CMND_CFGPOKE "CfgPoke"
#define D_CMND_CFGPEEK "CfgPeek"
#define D_CMND_CFGSHOW "CfgShow"
#define D_CMND_CFGXOR "CfgXor"
#define D_CMND_CPUCHECK "CpuChk"
#define D_CMND_EXCEPTION "Exception"
#define D_CMND_CPUCHECK "CpuChk"
#define D_CMND_FREEMEM "FreeMem"
#define D_CMND_RTCDUMP "RtcDump"
#define D_CMND_HELP "Help"
enum DebugCommands { CMND_CFGDUMP, CMND_CFGPEEK, CMND_CFGPOKE, CMND_CFGXOR, CMND_EXCEPTION, CMND_CPUCHECK };
const char kDebugCommands[] PROGMEM = D_CMND_CFGDUMP "|" D_CMND_CFGPEEK "|" D_CMND_CFGPOKE "|" D_CMND_CFGXOR "|" D_CMND_EXCEPTION "|" D_CMND_CPUCHECK;
enum DebugCommands { CMND_CFGDUMP, CMND_CFGPEEK, CMND_CFGPOKE, CMND_CFGSHOW, CMND_CFGXOR, CMND_CPUCHECK, CMND_EXCEPTION, CMND_FREEMEM, CMND_RTCDUMP, CMND_HELP };
const char kDebugCommands[] PROGMEM = D_CMND_CFGDUMP "|" D_CMND_CFGPEEK "|" D_CMND_CFGPOKE "|" D_CMND_CFGSHOW "|" D_CMND_CFGXOR "|" D_CMND_CPUCHECK "|" D_CMND_EXCEPTION "|" D_CMND_FREEMEM "|" D_CMND_RTCDUMP "|" D_CMND_HELP;
uint32_t CPU_loops = 0;
uint32_t CPU_last_millis = 0;
uint32_t CPU_last_loop_time = 0;
uint8_t CPU_load_check = CPU_LOAD_CHECK;
uint8_t CPU_load_check = 0;
uint8_t CPU_show_freemem = 0;
/*******************************************************************************************/
@ -117,41 +122,6 @@ Decoding 14 results
}
}
/*******************************************************************************************/
void RtcSettingsDump()
{
#define CFG_COLS 16
uint16_t idx;
uint16_t maxrow;
uint16_t row;
uint16_t col;
uint8_t *buffer = (uint8_t *) &RtcSettings;
maxrow = ((sizeof(RTCMEM)+CFG_COLS)/CFG_COLS);
for (row = 0; row < maxrow; row++) {
idx = row * CFG_COLS;
snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx);
for (col = 0; col < CFG_COLS; col++) {
if (!(col%4)) {
snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data);
}
snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]);
}
snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data);
for (col = 0; col < CFG_COLS; col++) {
// if (!(col%4)) {
// snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data);
// }
snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' ');
}
snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data);
AddLog(LOG_LEVEL_INFO);
}
}
#endif // DEBUG_THEO
/*******************************************************************************************/
@ -224,6 +194,68 @@ void DebugFreeMem()
/*******************************************************************************************/
void DebugRtcDump(char* parms)
{
#define CFG_COLS 16
uint16_t idx;
uint16_t maxrow;
uint16_t row;
uint16_t col;
char *p;
// |<--SDK data (256 bytes)-->|<--User data (512 bytes)-->|
// 000 - 0FF: SDK
// 000 - 01B: SDK rst_info
// 100 - 2FF: User
// 280 - 283: Tasmota RtcReboot (Offset 100 (x 4bytes) - sizeof(RTCRBT) (x 4bytes))
// 290 - 2EB: Tasmota RtcSettings (Offset 100 (x 4bytes))
uint8_t buffer[768];
// ESP.rtcUserMemoryRead(0, (uint32_t*)&buffer, sizeof(buffer));
system_rtc_mem_read(0, (uint32_t*)&buffer, sizeof(buffer));
maxrow = ((sizeof(buffer)+CFG_COLS)/CFG_COLS);
uint16_t srow = strtol(parms, &p, 16) / CFG_COLS;
uint16_t mrow = strtol(p, &p, 10);
// snprintf_P(log_data, sizeof(log_data), PSTR("Cnfg: Parms %s, Start row %d, rows %d"), parms, srow, mrow);
// AddLog(LOG_LEVEL_DEBUG);
if (0 == mrow) { // Default only 8 lines
mrow = 8;
}
if (srow > maxrow) {
srow = maxrow - mrow;
}
if (mrow < (maxrow - srow)) {
maxrow = srow + mrow;
}
for (row = srow; row < maxrow; row++) {
idx = row * CFG_COLS;
snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx);
for (col = 0; col < CFG_COLS; col++) {
if (!(col%4)) {
snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data);
}
snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]);
}
snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data);
for (col = 0; col < CFG_COLS; col++) {
// if (!(col%4)) {
// snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data);
// }
snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' ');
}
snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data);
AddLog(LOG_LEVEL_INFO);
}
}
/*******************************************************************************************/
void DebugCfgDump(char* parms)
{
#define CFG_COLS 16
@ -322,6 +354,53 @@ void DebugCfgPoke(char* parms)
AddLog(LOG_LEVEL_INFO);
}
void DebugCfgShow(uint8_t more)
{
uint8_t *SetAddr;
SetAddr = (uint8_t *)&Settings;
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: Hostname (%d) [%s]"), (uint8_t *)&Settings.hostname - SetAddr, sizeof(Settings.hostname)-1, Settings.hostname);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: SSids (%d) [%s], [%s]"), (uint8_t *)&Settings.sta_ssid - SetAddr, sizeof(Settings.sta_ssid[0])-1, Settings.sta_ssid[0], Settings.sta_ssid[1]);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: Friendlynames (%d) [%s], [%s], [%s], [%s]"), (uint8_t *)&Settings.friendlyname - SetAddr, sizeof(Settings.friendlyname[0])-1, Settings.friendlyname[0], Settings.friendlyname[1], Settings.friendlyname[2], Settings.friendlyname[3]);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: OTA Url (%d) [%s]"), (uint8_t *)&Settings.ota_url - SetAddr, sizeof(Settings.ota_url)-1, Settings.ota_url);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: StateText (%d) [%s], [%s], [%s], [%s]"), (uint8_t *)&Settings.state_text - SetAddr, sizeof(Settings.state_text[0])-1, Settings.state_text[0], Settings.state_text[1], Settings.state_text[2], Settings.state_text[3]);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: Syslog Host (%d) [%s]"), (uint8_t *)&Settings.syslog_host - SetAddr, sizeof(Settings.syslog_host)-1, Settings.syslog_host);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: NTP Servers (%d) [%s], [%s], [%s]"), (uint8_t *)&Settings.ntp_server - SetAddr, sizeof(Settings.ntp_server[0])-1, Settings.ntp_server[0], Settings.ntp_server[1], Settings.ntp_server[2]);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT Host (%d) [%s]"), (uint8_t *)&Settings.mqtt_host - SetAddr, sizeof(Settings.mqtt_host)-1, Settings.mqtt_host);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT Client (%d) [%s]"), (uint8_t *)&Settings.mqtt_client - SetAddr, sizeof(Settings.mqtt_client)-1, Settings.mqtt_client);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT User (%d) [%s]"), (uint8_t *)&Settings.mqtt_user - SetAddr, sizeof(Settings.mqtt_user)-1, Settings.mqtt_user);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT FullTopic (%d) [%s]"), (uint8_t *)&Settings.mqtt_fulltopic - SetAddr, sizeof(Settings.mqtt_fulltopic)-1, Settings.mqtt_fulltopic);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT Topic (%d) [%s]"), (uint8_t *)&Settings.mqtt_topic - SetAddr, sizeof(Settings.mqtt_topic)-1, Settings.mqtt_topic);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT GroupTopic (%d) [%s]"), (uint8_t *)&Settings.mqtt_grptopic - SetAddr, sizeof(Settings.mqtt_grptopic)-1, Settings.mqtt_grptopic);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT ButtonTopic (%d) [%s]"), (uint8_t *)&Settings.button_topic - SetAddr, sizeof(Settings.button_topic)-1, Settings.button_topic);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT SwitchTopic (%d) [%s]"), (uint8_t *)&Settings.switch_topic - SetAddr, sizeof(Settings.switch_topic)-1, Settings.switch_topic);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT Prefixes (%d) [%s], [%s], [%s]"), (uint8_t *)&Settings.mqtt_prefix - SetAddr, sizeof(Settings.mqtt_prefix[0])-1, Settings.mqtt_prefix[0], Settings.mqtt_prefix[1], Settings.mqtt_prefix[2]);
AddLog(LOG_LEVEL_INFO);
if (17 == more) {
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: AP Passwords (%d) [%s], [%s]"), (uint8_t *)&Settings.sta_pwd - SetAddr, sizeof(Settings.sta_pwd[0])-1, Settings.sta_pwd[0], Settings.sta_pwd[1]);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT Password (%d) [%s]"), (uint8_t *)&Settings.mqtt_pwd - SetAddr, sizeof(Settings.mqtt_pwd)-1, Settings.mqtt_pwd);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: Web Password (%d) [%s]"), (uint8_t *)&Settings.web_password - SetAddr, sizeof(Settings.web_password)-1, Settings.web_password);
AddLog(LOG_LEVEL_INFO);
}
}
/*******************************************************************************************/
boolean DebugCommand()
@ -333,6 +412,15 @@ boolean DebugCommand()
if (-1 == command_code) {
serviced = false; // Unknown command
}
else if (CMND_HELP == command_code) {
snprintf_P(log_data, sizeof(log_data), kDebugCommands);
AddLog(LOG_LEVEL_INFO);
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
}
else if (CMND_RTCDUMP == command_code) {
DebugRtcDump(XdrvMailbox.data);
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
}
else if (CMND_CFGDUMP == command_code) {
DebugCfgDump(XdrvMailbox.data);
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
@ -345,6 +433,10 @@ boolean DebugCommand()
DebugCfgPoke(XdrvMailbox.data);
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
}
else if (CMND_CFGSHOW == command_code) {
DebugCfgShow(XdrvMailbox.payload);
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
}
#ifdef USE_WEBSERVER
else if (CMND_CFGXOR == command_code) {
if (XdrvMailbox.data_len > 0) {
@ -366,6 +458,12 @@ boolean DebugCommand()
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, CPU_load_check);
}
else if (CMND_FREEMEM == command_code) {
if (XdrvMailbox.data_len > 0) {
CPU_show_freemem = XdrvMailbox.payload;
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, CPU_show_freemem);
}
else serviced = false; // Unknown command
return serviced;
@ -392,7 +490,7 @@ boolean Xdrv99(byte function)
result = DebugCommand();
break;
case FUNC_FREE_MEM:
DebugFreeMem();
if (CPU_show_freemem) { DebugFreeMem(); }
break;
}
return result;

View File

@ -153,14 +153,6 @@ boolean XdrvCommand(uint8_t grpflg, char *type, uint16_t index, char *dataBuf, u
return XdrvCall(FUNC_COMMAND);
}
void XdrvSetPower(power_t mpower)
{
// XdrvMailbox.valid = 1;
XdrvMailbox.index = mpower;
XdrvCall(FUNC_SET_POWER);
}
boolean XdrvMqttData(char *topicBuf, uint16_t stopicBuf, char *dataBuf, uint16_t sdataBuf)
{
XdrvMailbox.index = stopicBuf;

View File

@ -111,9 +111,9 @@ void HlwEvery200ms()
if (hlw_cf_pulse_length && energy_power_on && !hlw_load_off) {
hlw_w = (hlw_power_ratio * Settings.energy_power_calibration) / hlw_cf_pulse_length;
energy_power = (float)hlw_w / 10;
energy_active_power = (float)hlw_w / 10;
} else {
energy_power = 0;
energy_active_power = 0;
}
hlw_cf1_timer++;
@ -142,7 +142,7 @@ void HlwEvery200ms()
hlw_cf1_current_pulse_length = hlw_cf1_pulse_length;
hlw_cf1_current_max_pulse_counter = hlw_cf1_pulse_counter;
if (hlw_cf1_current_pulse_length && energy_power) { // No current if no power being consumed
if (hlw_cf1_current_pulse_length && energy_active_power) { // No current if no power being consumed
hlw_i = (hlw_current_ratio * Settings.energy_current_calibration) / hlw_cf1_current_pulse_length;
energy_current = (float)hlw_i / 1000;
} else {
@ -217,7 +217,6 @@ void HlwDrvInit()
{
if (!energy_flg) {
if ((pin[GPIO_HLW_SEL] < 99) && (pin[GPIO_HLW_CF1] < 99) && (pin[GPIO_HLW_CF] < 99)) { // Sonoff Pow or any HLW8012 based device
energy_calc_power_factor = 1; // Calculate power factor from data
energy_flg = XNRG_01;
}
}
@ -227,22 +226,19 @@ boolean HlwCommand()
{
boolean serviced = true;
if ((CMND_POWERCAL == energy_command_code) || (CMND_VOLTAGECAL == energy_command_code) || (CMND_CURRENTCAL == energy_command_code)) {
}
else if (CMND_POWERSET == energy_command_code) {
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 3601) && hlw_cf_pulse_length) {
Settings.energy_power_calibration = (XdrvMailbox.payload * 10 * hlw_cf_pulse_length) / hlw_power_ratio;
if (CMND_POWERSET == energy_command_code) {
if (XdrvMailbox.data_len && hlw_cf_pulse_length) {
Settings.energy_power_calibration = ((unsigned long)(CharToDouble(XdrvMailbox.data) * 10) * hlw_cf_pulse_length) / hlw_power_ratio;
}
}
else if (CMND_VOLTAGESET == energy_command_code) {
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 501) && hlw_cf1_voltage_pulse_length) {
Settings.energy_voltage_calibration = (XdrvMailbox.payload * 10 * hlw_cf1_voltage_pulse_length) / hlw_voltage_ratio;
if (XdrvMailbox.data_len && hlw_cf1_voltage_pulse_length) {
Settings.energy_voltage_calibration = ((unsigned long)(CharToDouble(XdrvMailbox.data) * 10) * hlw_cf1_voltage_pulse_length) / hlw_voltage_ratio;
}
}
else if (CMND_CURRENTSET == energy_command_code) {
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 16001) && hlw_cf1_current_pulse_length) {
Settings.energy_current_calibration = (XdrvMailbox.payload * hlw_cf1_current_pulse_length) / hlw_current_ratio;
if (XdrvMailbox.data_len && hlw_cf1_current_pulse_length) {
Settings.energy_current_calibration = ((unsigned long)(CharToDouble(XdrvMailbox.data)) * hlw_cf1_current_pulse_length) / hlw_current_ratio;
}
}
else serviced = false; // Unknown command

View File

@ -27,6 +27,8 @@
#define XNRG_02 2
#define CSE_MAX_INVALID_POWER 128 // Number of invalid power receipts before deciding active power is zero
#define CSE_NOT_CALIBRATED 0xAA
#define CSE_PULSES_NOT_INITIALIZED -1
@ -42,10 +44,12 @@ long power_cycle = 0;
unsigned long power_cycle_first = 0;
long cf_pulses = 0;
long cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED;
uint8_t cse_power_invalid = CSE_MAX_INVALID_POWER;
void CseReceived()
{
// 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
// Hd Id VCal---- Voltage- ICal---- Current- PCal---- Power--- Ad CF--- Ck
@ -89,23 +93,28 @@ void CseReceived()
energy_voltage = (float)(Settings.energy_voltage_calibration * CSE_UREF) / (float)voltage_cycle;
}
if (adjustement & 0x10) { // Power valid
cse_power_invalid = 0;
if ((header & 0xF2) == 0xF2) { // Power cycle exceeds range
energy_power = 0;
energy_active_power = 0;
} else {
if (0 == power_cycle_first) power_cycle_first = power_cycle; // Skip first incomplete power_cycle
if (0 == power_cycle_first) { power_cycle_first = power_cycle; } // Skip first incomplete power_cycle
if (power_cycle_first != power_cycle) {
power_cycle_first = -1;
energy_power = (float)(Settings.energy_power_calibration * CSE_PREF) / (float)power_cycle;
energy_active_power = (float)(Settings.energy_power_calibration * CSE_PREF) / (float)power_cycle;
} else {
energy_power = 0;
energy_active_power = 0;
}
}
} else {
power_cycle_first = 0;
energy_power = 0; // Powered on but no load
if (cse_power_invalid < CSE_MAX_INVALID_POWER) { // Allow measurements down to about 1W
cse_power_invalid++;
} else {
power_cycle_first = 0;
energy_active_power = 0; // Powered on but no load
}
}
if (adjustement & 0x20) { // Current valid
if (0 == energy_power) {
if (0 == energy_active_power) {
energy_current = 0;
} else {
energy_current = (float)Settings.energy_current_calibration / (float)current_cycle;
@ -114,7 +123,7 @@ void CseReceived()
} else { // Powered off
power_cycle_first = 0;
energy_voltage = 0;
energy_power = 0;
energy_active_power = 0;
energy_current = 0;
}
}
@ -171,7 +180,7 @@ void CseEverySecond()
} else {
cf_frequency = cf_pulses - cf_pulses_last_time;
}
if (cf_frequency && energy_power) {
if (cf_frequency && energy_active_power) {
cf_pulses_last_time = cf_pulses;
energy_kWhtoday_delta += (cf_frequency * Settings.energy_power_calibration) / 36;
EnergyUpdateToday();
@ -185,7 +194,6 @@ void CseDrvInit()
if ((SONOFF_S31 == Settings.module) || (SONOFF_POW_R2 == Settings.module)) { // Sonoff S31 or Sonoff Pow R2
baudrate = 4800;
serial_config = SERIAL_8E1;
energy_calc_power_factor = 1; // Calculate power factor from data
energy_flg = XNRG_02;
}
}
@ -195,22 +203,19 @@ boolean CseCommand()
{
boolean serviced = true;
if ((CMND_POWERCAL == energy_command_code) || (CMND_VOLTAGECAL == energy_command_code) || (CMND_CURRENTCAL == energy_command_code)) {
}
else if (CMND_POWERSET == energy_command_code) {
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 3601) && power_cycle) {
Settings.energy_power_calibration = (XdrvMailbox.payload * power_cycle) / CSE_PREF;
if (CMND_POWERSET == energy_command_code) {
if (XdrvMailbox.data_len && power_cycle) {
Settings.energy_power_calibration = ((unsigned long)CharToDouble(XdrvMailbox.data) * power_cycle) / CSE_PREF;
}
}
else if (CMND_VOLTAGESET == energy_command_code) {
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 501) && voltage_cycle) {
Settings.energy_voltage_calibration = (XdrvMailbox.payload * voltage_cycle) / CSE_UREF;
if (XdrvMailbox.data_len && voltage_cycle) {
Settings.energy_voltage_calibration = ((unsigned long)CharToDouble(XdrvMailbox.data) * voltage_cycle) / CSE_UREF;
}
}
else if (CMND_CURRENTSET == energy_command_code) {
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 16001) && current_cycle) {
Settings.energy_current_calibration = (XdrvMailbox.payload * current_cycle) / 1000;
if (XdrvMailbox.data_len && current_cycle) {
Settings.energy_current_calibration = ((unsigned long)CharToDouble(XdrvMailbox.data) * current_cycle) / 1000;
}
}
else serviced = false; // Unknown command

View File

@ -177,7 +177,7 @@ void PzemEvery200ms()
energy_current = value;
break;
case 3: // Power as 20W
energy_power = value;
energy_active_power = value;
break;
case 4: // Total energy as 99999Wh
if (!energy_start || (value < energy_start)) energy_start = value; // Init after restart and hanlde roll-over if any
@ -215,7 +215,6 @@ void PzemDrvInit()
{
if (!energy_flg) {
if ((pin[GPIO_PZEM_RX] < 99) && (pin[GPIO_PZEM_TX] < 99)) { // Any device with a Pzem004T
energy_calc_power_factor = 1; // Calculate power factor from data
energy_flg = XNRG_03;
}
}

View File

@ -0,0 +1,646 @@
/*
xnrg_04_mcp39f501.ino - MCP39F501 energy sensor support for Sonoff-Tasmota
Copyright (C) 2018 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_MCP39F501
/*********************************************************************************************\
* MCP39F501 - Energy (Shelly 2)
*
* Based on datasheet from https://www.microchip.com/wwwproducts/en/MCP39F501
* and https://github.com/OLIMEX/olimex-iot-firmware-esp8266/blob/7a7f9bb56d4b72770dba8d0f18eaa9d956dd0baf/olimex/user/modules/mod_emtr.c
\*********************************************************************************************/
#define XNRG_04 4
#define MCP_TIMEOUT 4
#define MCP_CALIBRATION_TIMEOUT 2
#define MCP_CALIBRATE_POWER 0x001
#define MCP_CALIBRATE_VOLTAGE 0x002
#define MCP_CALIBRATE_CURRENT 0x004
#define MCP_CALIBRATE_FREQUENCY 0x008
#define MCP_SINGLE_WIRE_FLAG 0x100
#define MCP_START_FRAME 0xA5
#define MCP_ACK_FRAME 0x06
#define MCP_ERROR_NAK 0x15
#define MCP_ERROR_CRC 0x51
#define MCP_SINGLE_WIRE 0xAB
#define MCP_SET_ADDRESS 0x41
#define MCP_READ 0x4E
#define MCP_READ_16 0x52
#define MCP_READ_32 0x44
#define MCP_WRITE 0x4D
#define MCP_WRITE_16 0x57
#define MCP_WRITE_32 0x45
#define MCP_SAVE_REGISTERS 0x53
#define MCP_CALIBRATION_BASE 0x0028
#define MCP_CALIBRATION_LEN 52
#define MCP_FREQUENCY_REF_BASE 0x0094
#define MCP_FREQUENCY_GAIN_BASE 0x00AE
#define MCP_FREQUENCY_LEN 4
typedef struct mcp_cal_registers_type {
uint16_t gain_current_rms;
uint16_t gain_voltage_rms;
uint16_t gain_active_power;
uint16_t gain_reactive_power;
sint32_t offset_current_rms;
sint32_t offset_active_power;
sint32_t offset_reactive_power;
sint16_t dc_offset_current;
sint16_t phase_compensation;
uint16_t apparent_power_divisor;
uint32_t system_configuration;
uint16_t dio_configuration;
uint32_t range;
uint32_t calibration_current;
uint16_t calibration_voltage;
uint32_t calibration_active_power;
uint32_t calibration_reactive_power;
uint16_t accumulation_interval;
} mcp_cal_registers_type;
unsigned long mcp_kWhcounter = 0;
uint32_t mcp_system_configuration = 0x03000000;
uint32_t mcp_active_power;
//uint32_t mcp_reactive_power;
//uint32_t mcp_apparent_power;
uint32_t mcp_current_rms;
uint16_t mcp_voltage_rms;
uint16_t mcp_line_frequency;
//sint16_t mcp_power_factor;
uint8_t mcp_address = 0;
uint8_t mcp_calibration_active = 0;
uint8_t mcp_init = 0;
uint8_t mcp_timeout = 0;
uint8_t mcp_calibrate = 0;
/*********************************************************************************************\
* Olimex tools
* https://github.com/OLIMEX/olimex-iot-firmware-esp8266/blob/7a7f9bb56d4b72770dba8d0f18eaa9d956dd0baf/olimex/user/modules/mod_emtr.c
\*********************************************************************************************/
uint8_t McpChecksum(uint8_t *data)
{
uint8_t checksum = 0;
uint8_t offset = 0;
uint8_t len = data[1] -1;
for (byte i = offset; i < len; i++) { checksum += data[i]; }
return checksum;
}
unsigned long McpExtractInt(char *data, uint8_t offset, uint8_t size)
{
unsigned long result = 0;
unsigned long pow = 1;
for (byte i = 0; i < size; i++) {
result = result + (uint8_t)data[offset + i] * pow;
pow = pow * 256;
}
return result;
}
void McpSetInt(unsigned long value, uint8_t *data, uint8_t offset, size_t size)
{
for (byte i = 0; i < size; i++) {
data[offset + i] = ((value >> (i * 8)) & 0xFF);
}
}
void McpSend(uint8_t *data)
{
if (mcp_timeout) { return; }
mcp_timeout = MCP_TIMEOUT;
data[0] = MCP_START_FRAME;
data[data[1] -1] = McpChecksum(data);
// AddLogSerial(LOG_LEVEL_DEBUG_MORE, data, data[1]);
for (byte i = 0; i < data[1]; i++) {
Serial.write(data[i]);
}
}
/********************************************************************************************/
void McpGetAddress(void)
{
uint8_t data[] = { MCP_START_FRAME, 7, MCP_SET_ADDRESS, 0x00, 0x26, MCP_READ_16, 0x00 };
McpSend(data);
}
void McpAddressReceive(void)
{
// 06 05 004D 58
mcp_address = serial_in_buffer[3];
}
/********************************************************************************************/
void McpGetCalibration(void)
{
if (mcp_calibration_active) { return; }
mcp_calibration_active = MCP_CALIBRATION_TIMEOUT;
uint8_t data[] = { MCP_START_FRAME, 8, MCP_SET_ADDRESS, (MCP_CALIBRATION_BASE >> 8) & 0xFF, MCP_CALIBRATION_BASE & 0xFF, MCP_READ, MCP_CALIBRATION_LEN, 0x00 };
McpSend(data);
}
void McpParseCalibration(void)
{
bool action = false;
mcp_cal_registers_type cal_registers;
// 06 37 C882 B6AD 0781 9273 06000000 00000000 00000000 0000 D3FF 0300 00000003 9204 120C1300 204E0000 9808 E0AB0000 D9940000 0200 24
cal_registers.gain_current_rms = McpExtractInt(serial_in_buffer, 2, 2);
cal_registers.gain_voltage_rms = McpExtractInt(serial_in_buffer, 4, 2);
cal_registers.gain_active_power = McpExtractInt(serial_in_buffer, 6, 2);
cal_registers.gain_reactive_power = McpExtractInt(serial_in_buffer, 8, 2);
cal_registers.offset_current_rms = McpExtractInt(serial_in_buffer, 10, 4);
cal_registers.offset_active_power = McpExtractInt(serial_in_buffer, 14, 4);
cal_registers.offset_reactive_power = McpExtractInt(serial_in_buffer, 18, 4);
cal_registers.dc_offset_current = McpExtractInt(serial_in_buffer, 22, 2);
cal_registers.phase_compensation = McpExtractInt(serial_in_buffer, 24, 2);
cal_registers.apparent_power_divisor = McpExtractInt(serial_in_buffer, 26, 2);
cal_registers.system_configuration = McpExtractInt(serial_in_buffer, 28, 4);
cal_registers.dio_configuration = McpExtractInt(serial_in_buffer, 32, 2);
cal_registers.range = McpExtractInt(serial_in_buffer, 34, 4);
cal_registers.calibration_current = McpExtractInt(serial_in_buffer, 38, 4);
cal_registers.calibration_voltage = McpExtractInt(serial_in_buffer, 42, 2);
cal_registers.calibration_active_power = McpExtractInt(serial_in_buffer, 44, 4);
cal_registers.calibration_reactive_power = McpExtractInt(serial_in_buffer, 48, 4);
cal_registers.accumulation_interval = McpExtractInt(serial_in_buffer, 52, 2);
if (mcp_calibrate & MCP_CALIBRATE_POWER) {
cal_registers.calibration_active_power = Settings.energy_power_calibration;
if (McpCalibrationCalc(&cal_registers, 16)) { action = true; }
}
if (mcp_calibrate & MCP_CALIBRATE_VOLTAGE) {
cal_registers.calibration_voltage = Settings.energy_voltage_calibration;
if (McpCalibrationCalc(&cal_registers, 0)) { action = true; }
}
if (mcp_calibrate & MCP_CALIBRATE_CURRENT) {
cal_registers.calibration_current = Settings.energy_current_calibration;
if (McpCalibrationCalc(&cal_registers, 8)) { action = true; }
}
mcp_timeout = 0;
if (action) { McpSetCalibration(&cal_registers); }
mcp_calibrate = 0;
Settings.energy_power_calibration = cal_registers.calibration_active_power;
Settings.energy_voltage_calibration = cal_registers.calibration_voltage;
Settings.energy_current_calibration = cal_registers.calibration_current;
mcp_system_configuration = cal_registers.system_configuration;
if (mcp_system_configuration & MCP_SINGLE_WIRE_FLAG) {
mcp_system_configuration &= ~MCP_SINGLE_WIRE_FLAG; // Reset SingleWire flag
McpSetSystemConfiguration(2);
}
}
bool McpCalibrationCalc(struct mcp_cal_registers_type *cal_registers, uint8_t range_shift)
{
uint32_t measured;
uint32_t expected;
uint16_t *gain;
uint32_t new_gain;
if (range_shift == 0) {
measured = mcp_voltage_rms;
expected = cal_registers->calibration_voltage;
gain = &(cal_registers->gain_voltage_rms);
} else if (range_shift == 8) {
measured = mcp_current_rms;
expected = cal_registers->calibration_current;
gain = &(cal_registers->gain_current_rms);
} else if (range_shift == 16) {
measured = mcp_active_power;
expected = cal_registers->calibration_active_power;
gain = &(cal_registers->gain_active_power);
} else {
return false;
}
if (measured == 0) {
return false;
}
uint32_t range = (cal_registers->range >> range_shift) & 0xFF;
calc:
new_gain = (*gain) * expected / measured;
if (new_gain < 25000) {
range++;
if (measured > 6) {
measured = measured / 2;
goto calc;
}
}
if (new_gain > 55000) {
range--;
measured = measured * 2;
goto calc;
}
*gain = new_gain;
uint32_t old_range = (cal_registers->range >> range_shift) & 0xFF;
cal_registers->range = cal_registers->range ^ (old_range << range_shift);
cal_registers->range = cal_registers->range | (range << range_shift);
return true;
}
/*
void McpCalibrationReactivePower(void)
{
cal_registers.gain_reactive_power = cal_registers.gain_reactive_power * cal_registers.calibration_reactive_power / mcp_reactive_power;
}
*/
void McpSetCalibration(struct mcp_cal_registers_type *cal_registers)
{
uint8_t data[7 + MCP_CALIBRATION_LEN + 2 + 1];
data[1] = sizeof(data);
data[2] = MCP_SET_ADDRESS; // Set address pointer
data[3] = (MCP_CALIBRATION_BASE >> 8) & 0xFF; // address
data[4] = (MCP_CALIBRATION_BASE >> 0) & 0xFF; // address
data[5] = MCP_WRITE; // Write N bytes
data[6] = MCP_CALIBRATION_LEN;
McpSetInt(cal_registers->gain_current_rms, data, 0+7, 2);
McpSetInt(cal_registers->gain_voltage_rms, data, 2+7, 2);
McpSetInt(cal_registers->gain_active_power, data, 4+7, 2);
McpSetInt(cal_registers->gain_reactive_power, data, 6+7, 2);
McpSetInt(cal_registers->offset_current_rms, data, 8+7, 4);
McpSetInt(cal_registers->offset_active_power, data, 12+7, 4);
McpSetInt(cal_registers->offset_reactive_power, data, 16+7, 4);
McpSetInt(cal_registers->dc_offset_current, data, 20+7, 2);
McpSetInt(cal_registers->phase_compensation, data, 22+7, 2);
McpSetInt(cal_registers->apparent_power_divisor, data, 24+7, 2);
McpSetInt(cal_registers->system_configuration, data, 26+7, 4);
McpSetInt(cal_registers->dio_configuration, data, 30+7, 2);
McpSetInt(cal_registers->range, data, 32+7, 4);
McpSetInt(cal_registers->calibration_current, data, 36+7, 4);
McpSetInt(cal_registers->calibration_voltage, data, 40+7, 2);
McpSetInt(cal_registers->calibration_active_power, data, 42+7, 4);
McpSetInt(cal_registers->calibration_reactive_power, data, 46+7, 4);
McpSetInt(cal_registers->accumulation_interval, data, 50+7, 2);
data[MCP_CALIBRATION_LEN+7] = MCP_SAVE_REGISTERS; // Save registers to flash
data[MCP_CALIBRATION_LEN+8] = mcp_address; // Device address
McpSend(data);
}
/********************************************************************************************/
void McpSetSystemConfiguration(uint16 interval)
{
// A5 11 41 00 42 45 03 00 01 00 41 00 5A 57 00 06 7A
uint8_t data[17];
data[ 1] = sizeof(data);
data[ 2] = MCP_SET_ADDRESS; // Set address pointer
data[ 3] = 0x00; // address
data[ 4] = 0x42; // address
data[ 5] = MCP_WRITE_32; // Write 4 bytes
data[ 6] = (mcp_system_configuration >> 24) & 0xFF; // system_configuration
data[ 7] = (mcp_system_configuration >> 16) & 0xFF; // system_configuration
data[ 8] = (mcp_system_configuration >> 8) & 0xFF; // system_configuration
data[ 9] = (mcp_system_configuration >> 0) & 0xFF; // system_configuration
data[10] = MCP_SET_ADDRESS; // Set address pointer
data[11] = 0x00; // address
data[12] = 0x5A; // address
data[13] = MCP_WRITE_16; // Write 2 bytes
data[14] = (interval >> 8) & 0xFF; // interval
data[15] = (interval >> 0) & 0xFF; // interval
McpSend(data);
}
/********************************************************************************************/
void McpGetFrequency(void)
{
if (mcp_calibration_active) { return; }
mcp_calibration_active = MCP_CALIBRATION_TIMEOUT;
uint8_t data[] = { MCP_START_FRAME, 11, MCP_SET_ADDRESS, (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF, MCP_FREQUENCY_REF_BASE & 0xFF, MCP_READ_16,
MCP_SET_ADDRESS, (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF, MCP_FREQUENCY_GAIN_BASE & 0xFF, MCP_READ_16, 0x00 };
McpSend(data);
}
void McpParseFrequency(void)
{
// 06 07 C350 8000 A0
uint16_t line_frequency_ref = serial_in_buffer[2] * 256 + serial_in_buffer[3];
uint16_t gain_line_frequency = serial_in_buffer[4] * 256 + serial_in_buffer[5];
if (mcp_calibrate & MCP_CALIBRATE_FREQUENCY) {
line_frequency_ref = Settings.energy_frequency_calibration;
if ((0xFFFF == mcp_line_frequency) || (0 == gain_line_frequency)) { // Reset values to 50Hz
mcp_line_frequency = 50000;
gain_line_frequency = 0x8000;
}
gain_line_frequency = gain_line_frequency * line_frequency_ref / mcp_line_frequency;
mcp_timeout = 0;
McpSetFrequency(line_frequency_ref, gain_line_frequency);
}
Settings.energy_frequency_calibration = line_frequency_ref;
mcp_calibrate = 0;
}
void McpSetFrequency(uint16_t line_frequency_ref, uint16_t gain_line_frequency)
{
// A5 11 41 00 94 57 C3 B4 41 00 AE 57 7E 46 53 4D 03
uint8_t data[17];
data[ 1] = sizeof(data);
data[ 2] = MCP_SET_ADDRESS; // Set address pointer
data[ 3] = (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF; // address
data[ 4] = (MCP_FREQUENCY_REF_BASE >> 0) & 0xFF; // address
data[ 5] = MCP_WRITE_16; // Write register
data[ 6] = (line_frequency_ref >> 8) & 0xFF; // line_frequency_ref high
data[ 7] = (line_frequency_ref >> 0) & 0xFF; // line_frequency_ref low
data[ 8] = MCP_SET_ADDRESS; // Set address pointer
data[ 9] = (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF; // address
data[10] = (MCP_FREQUENCY_GAIN_BASE >> 0) & 0xFF; // address
data[11] = MCP_WRITE_16; // Write register
data[12] = (gain_line_frequency >> 8) & 0xFF; // gain_line_frequency high
data[13] = (gain_line_frequency >> 0) & 0xFF; // gain_line_frequency low
data[14] = MCP_SAVE_REGISTERS; // Save registers to flash
data[15] = mcp_address; // Device address
McpSend(data);
}
/********************************************************************************************/
void McpGetData(void)
{
uint8_t data[] = { MCP_START_FRAME, 8, MCP_SET_ADDRESS, 0x00, 0x04, MCP_READ, 22, 0x00 };
McpSend(data);
}
void McpParseData(void)
{
// 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
// 06 19 61 06 00 00 FE 08 9B 0E 00 00 0B 00 00 00 97 0E 00 00 FF 7F 0C C6 35
// 06 19 CE 18 00 00 F2 08 3A 38 00 00 66 00 00 00 93 38 00 00 36 7F 9A C6 B7
// Ak Ln Current---- Volt- ActivePower ReActivePow ApparentPow Factr Frequ Ck
mcp_current_rms = McpExtractInt(serial_in_buffer, 2, 4);
mcp_voltage_rms = McpExtractInt(serial_in_buffer, 6, 2);
mcp_active_power = McpExtractInt(serial_in_buffer, 8, 4);
// mcp_reactive_power = McpExtractInt(serial_in_buffer, 12, 4);
// mcp_power_factor = McpExtractInt(serial_in_buffer, 20, 2);
mcp_line_frequency = McpExtractInt(serial_in_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;
} else {
energy_current = (float)mcp_current_rms / 10000;
}
} else { // Powered off
energy_frequency = 0;
energy_voltage = 0;
energy_active_power = 0;
energy_current = 0;
}
}
/********************************************************************************************/
bool McpSerialInput(void)
{
serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
unsigned long start = millis();
while (millis() - start < 20) {
yield();
if (Serial.available()) {
serial_in_buffer[serial_in_byte_counter++] = Serial.read();
start = millis();
}
}
AddLogSerial(LOG_LEVEL_DEBUG_MORE);
if (1 == serial_in_byte_counter) {
if (MCP_ERROR_CRC == serial_in_buffer[0]) {
// AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: Send " D_CHECKSUM_FAILURE));
mcp_timeout = 0;
}
else if (MCP_ERROR_NAK == serial_in_buffer[0]) {
// AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: NAck"));
mcp_timeout = 0;
}
}
else if (MCP_ACK_FRAME == serial_in_buffer[0]) {
if (serial_in_byte_counter == serial_in_buffer[1]) {
if (McpChecksum((uint8_t *)serial_in_buffer) != serial_in_buffer[serial_in_byte_counter -1]) {
AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: " D_CHECKSUM_FAILURE));
} else {
if (5 == serial_in_buffer[1]) { McpAddressReceive(); }
if (25 == serial_in_buffer[1]) { McpParseData(); }
if (MCP_CALIBRATION_LEN + 3 == serial_in_buffer[1]) { McpParseCalibration(); }
if (MCP_FREQUENCY_LEN + 3 == serial_in_buffer[1]) { McpParseFrequency(); }
}
}
mcp_timeout = 0;
}
else if (MCP_SINGLE_WIRE == serial_in_buffer[0]) {
mcp_timeout = 0;
}
return 1;
}
/********************************************************************************************/
void McpEverySecond(void)
{
if (mcp_active_power) {
energy_kWhtoday_delta += ((mcp_active_power * 10) / 36);
EnergyUpdateToday();
}
if (mcp_timeout) {
mcp_timeout--;
}
else if (mcp_calibration_active) {
mcp_calibration_active--;
}
else if (mcp_init) {
if (2 == mcp_init) {
McpGetCalibration(); // Get calibration parameters and disable SingleWire mode if enabled
}
else if (1 == mcp_init) {
McpGetFrequency(); // Get calibration parameter
}
mcp_init--;
}
else if (!mcp_address) {
McpGetAddress(); // Get device address for future calibration changes
}
else {
McpGetData(); // Get energy data
}
}
void McpSnsInit(void)
{
SetSeriallog(LOG_LEVEL_NONE); // Free serial interface from logging interference
digitalWrite(15, 1); // GPIO15 - MCP enable
}
void McpDrvInit(void)
{
if (!energy_flg) {
if (SHELLY2 == Settings.module) {
pinMode(15, OUTPUT);
digitalWrite(15, 0); // GPIO15 - MCP disable - Reset Delta Sigma ADC's
baudrate = 4800;
mcp_calibrate = 0;
mcp_timeout = 2; // Initial wait
mcp_init = 2; // Initial setup steps
energy_flg = XNRG_04;
}
}
}
boolean McpCommand(void)
{
boolean serviced = true;
unsigned long value = 0;
if (CMND_POWERSET == energy_command_code) {
if (XdrvMailbox.data_len && mcp_active_power) {
value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 100);
if ((value > 100) && (value < 200000)) { // Between 1W and 2000W
Settings.energy_power_calibration = value;
mcp_calibrate |= MCP_CALIBRATE_POWER;
McpGetCalibration();
}
}
}
else if (CMND_VOLTAGESET == energy_command_code) {
if (XdrvMailbox.data_len && mcp_voltage_rms) {
value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 10);
if ((value > 1000) && (value < 2600)) { // Between 100V and 260V
Settings.energy_voltage_calibration = value;
mcp_calibrate |= MCP_CALIBRATE_VOLTAGE;
McpGetCalibration();
}
}
}
else if (CMND_CURRENTSET == energy_command_code) {
if (XdrvMailbox.data_len && mcp_current_rms) {
value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 10);
if ((value > 100) && (value < 80000)) { // Between 10mA and 8A
Settings.energy_current_calibration = value;
mcp_calibrate |= MCP_CALIBRATE_CURRENT;
McpGetCalibration();
}
}
}
else if (CMND_FREQUENCYSET == energy_command_code) {
if (XdrvMailbox.data_len && mcp_line_frequency) {
value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 1000);
if ((value > 45000) && (value < 65000)) { // Between 45Hz and 65Hz
Settings.energy_frequency_calibration = value;
mcp_calibrate |= MCP_CALIBRATE_FREQUENCY;
McpGetFrequency();
}
}
}
else serviced = false; // Unknown command
return serviced;
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
int Xnrg04(byte function)
{
int result = 0;
if (FUNC_PRE_INIT == function) {
McpDrvInit();
}
else if (XNRG_04 == energy_flg) {
switch (function) {
case FUNC_INIT:
McpSnsInit();
break;
case FUNC_EVERY_SECOND:
McpEverySecond();
break;
case FUNC_COMMAND:
result = McpCommand();
break;
case FUNC_SERIAL:
result = McpSerialInput();
break;
}
}
return result;
}
#endif // USE_MCP39F501
#endif // USE_ENERGY_SENSOR

View File

@ -141,12 +141,13 @@ void Pzem2Every200ms()
float energy = 0;
if (PZEM2_TYPES_003_017 == pzem2_type) {
energy_type_dc = true;
// 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
// FE 04 10 27 10 00 64 03 E8 00 00 00 00 00 00 00 00 00 00 HH LL = PZEM-017
// 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_power = (float)((uint32_t)buffer[9] << 24 + (uint32_t)buffer[10] << 16 + (uint32_t)buffer[7] << 8 + buffer[8]) / 10.0; // 429496729.0 W
energy_active_power = (float)((uint32_t)buffer[9] << 24 + (uint32_t)buffer[10] << 16 + (uint32_t)buffer[7] << 8 + buffer[8]) / 10.0; // 429496729.0 W
energy = (float)((uint32_t)buffer[13] << 24 + (uint32_t)buffer[14] << 16 + (uint32_t)buffer[11] << 8 + buffer[12]); // 4294967295 Wh
if (!energy_start || (energy < energy_start)) { energy_start = energy; } // Init after restart and hanlde roll-over if any
energy_kWhtoday += (energy - energy_start) * 100;
@ -154,12 +155,13 @@ void Pzem2Every200ms()
EnergyUpdateToday();
}
else if (PZEM2_TYPES_014_016 == pzem2_type) { // PZEM-014,016
energy_type_dc = false;
// 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
// FE 04 14 08 98 03 E8 00 00 08 98 00 00 00 00 00 00 01 F4 00 64 00 00 HH LL = PZEM-014
// 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)((uint32_t)buffer[7] << 24 + (uint32_t)buffer[8] << 16 + (uint32_t)buffer[5] << 8 + buffer[6]) / 1000.0; // 4294967.000 A
energy_power = (float)((uint32_t)buffer[11] << 24 + (uint32_t)buffer[12] << 16 + (uint32_t)buffer[9] << 8 + buffer[10]) / 10.0; // 429496729.0 W
energy_active_power = (float)((uint32_t)buffer[11] << 24 + (uint32_t)buffer[12] << 16 + (uint32_t)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
energy = (float)((uint32_t)buffer[15] << 24 + (uint32_t)buffer[16] << 16 + (uint32_t)buffer[13] << 8 + buffer[14]); // 4294967295 Wh

View File

@ -15,78 +15,284 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-----------------------------------------------------
Some words to the meaning of the UV Risk Level:
-----------------------------------------------------
D_UV_INDEX_1 = "Low" = sun->fun
D_UV_INDEX_2 = "Mid" = sun->glases advised
D_UV_INDEX_3 = "High" = sun->glases a must
D_UV_INDEX_4 = "Danger" = sun->skin burns Level 1
D_UV_INDEX_5 = "BurnL1/2" = sun->skin burns level 1..2
D_UV_INDEX_6 = "BurnL3" = sun->skin burns with level 3
D_UV_INDEX_7 = "OoR" = out of range or unknown
--------------------------------------------------------------------------------------------
Version Date Action Description
--------------------------------------------------------------------------------------------
1.0.0.2 20180928 tests - same as in version 1.0.0.1
cleaned - source code
changed - snprintf_P for json and web server output
- much more compressed and more professional code
added - uv_risk_text to json and web server output
changed - switch (function) to be 100% compatible
- added Veml6070EverySecond in thought of compatibile
added - Veml6070UvTableInit to do this only once to spare time
debugging - @Adrian helped me out in case of a %s%s in mqtt_data. Thank You Adrian
next - possible i will add the calculation for LAT and LONG coordinates for much more precission (TBD)
- show not only the UV Power value in W/m2, possible a @define value to show it as joule value (TBD)
- add a #define to select how many characters are shown benhind the decimal point for the UV Index (TBD)
---
1.0.0.1 20180925 tests - all tests are done with 1x sonoff sv, 2x Wemos D1 (not the mini)
- 3 different VEMl6070 sensors from 3 different online shops
- all the last three test where good and all looks working so far
- all tests are done at high noon with blue sky and a leaned UV light source
sience - a special Thank You to my friend the professor. He works in the aerospace industrie. Thank You R.G.T.
- all calculations are based on the very good work of Karel Vanicek. Thank You Karel
- more information about UV Index and the irradiation power calculation can be found on the internet
info - all calculations are based on the effective irradiation from Karel Vanicek
- all this was not possible without the work of @arendst. He has done really a lot of basic work/code. Thank You Theo
cleaned - source code a little bit
added - missing void in function calls: void name(void)
added - UV Risk level now defined as UV Index, 0.00 based on NASA standard with text behind the value
added - UV Power level now named as UV Power, used W/m2 because official standards
added - automatic fill of the uv-risk compare table based on the coefficient calculation
added - suspend and wakeup mode for the uv seonsor
- current drain in wake-up-ed mode was around 180uA incl. I2C bus
- current drain in suspend mode was around 70..80uA incl. I2C bus
changed - 2x the power calculation about some incorrent data sheet values
changed - float to double calculation because a rare effect on uv compare map filling
- in that case @andrethomas was a big help too (while(work){output=lot_of_fun};)
added - USE_VEML6070_RSET
- in user_config as possible input, different resistor values depending on PCB types
added - USE_VEML6070_SHOW_RAW
- in user_config, show or show-NOT the uv raw value
added - lots of #defines for automatic calulations to get the best possible values
added - error messages for LOG_LEVEL_DEBUG
added - lots of information in one of the last postings in: https://github.com/arendst/Sonoff-Tasmota/issues/3844
debugging - without the softly hit ;-) from @andrethomas about Serial.print i would never done it. Thank You Andre
safety - personal, please read this: http://www.segurancaetrabalho.com.br/download/uv_index_karel_vanicek.pdf
next - possible i will add the calculation for LAT and LONG coordinates for much more precission
- show not only the UV Power value in W/m2, possible a @define value to show it as joule value
- add a #define to select how many characters are shown benhind the decimal point for the UV Index
---
1.0.0.0 20180912 started - further development by mike2nl - https://github.com/mike2nl/Sonoff-Tasmota
forked - from arendst/tasmota - https://github.com/arendst/Sonoff-Tasmota
base - code base from arendst too
*/
#ifdef USE_I2C
#ifdef USE_VEML6070
/*********************************************************************************************\
* VEML6070 - Ultra Violet Light Intensity
* VEML6070 - Ultra Violet Light Intensity (UV-A, 100% output by 255nm)
*
* I2C Address: 0x38 and 0x39
\*********************************************************************************************/
#define VEML6070_ADDR_H 0x39
#define VEML6070_ADDR_L 0x38
#define VEML6070_INTEGRATION_TIME 3 // 500msec integration time
uint8_t veml6070_address;
uint8_t veml6070_type = 0;
uint16_t Veml6070ReadUv()
{
if (Wire.requestFrom(VEML6070_ADDR_H, 1) != 1) {
return -1;
}
uint16_t uvi = Wire.read();
uvi <<= 8;
if (Wire.requestFrom(VEML6070_ADDR_L, 1) != 1) {
return -1;
}
uvi |= Wire.read();
return uvi;
}
#define VEML6070_ADDR_H 0x39 // on some PCB boards the address can be changed by a solder point,
#define VEML6070_ADDR_L 0x38 // to have no address conflicts with other I2C sensors and/or hardware
#define VEML6070_INTEGRATION_TIME 3 // IT_4 = 500msec integration time, because the precission is 4 times higher then IT_0.5
#define VEML6070_ENABLE 1 //
#define VEML6070_DISABLE 0 //
#define VEML6070_RSET_DEFAULT 270000 // 270K default resistor value 270000 ohm, range from 220K..1Meg
#define VEML6070_UV_MAX_INDEX 15 // normal 11, internal on weather laboratories and NASA it's 15 so far the sensor is linear
#define VEML6070_UV_MAX_DEFAULT 11 // 11 = public default table values
#define VEML6070_POWER_COEFFCIENT 0.025 // based on calculations from Karel Vanicek and reorder by hand
#define VEML6070_TABLE_COEFFCIENT 32.86270591 // calculated by hand with help from a friend of mine, a professor which works in aero space things
// (resistor, differences, power coefficients and official UV index calculations (LAT & LONG will be added later)
/********************************************************************************************/
void Veml6070Detect()
// globals
const char kVemlTypes[] PROGMEM = "VEML6070"; // in preperation of veml6075
double uv_risk_map[VEML6070_UV_MAX_INDEX] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
double uvrisk = 0;
double uvpower = 0;
uint16_t uvlevel = 0;
uint8_t veml6070_addr_low = VEML6070_ADDR_L;
uint8_t veml6070_addr_high = VEML6070_ADDR_H;
uint8_t itime = VEML6070_INTEGRATION_TIME;
uint8_t veml6070_type = 0;
uint8_t veml6070_valid = 0;
char veml6070_name[9];
char str_uvrisk_text[10];
/********************************************************************************************/
void Veml6070Detect(void)
{
if (veml6070_type) {
return;
}
uint8_t itime = VEML6070_INTEGRATION_TIME;
veml6070_address = VEML6070_ADDR_L;
Wire.beginTransmission(veml6070_address);
// init the UV sensor
Wire.beginTransmission(VEML6070_ADDR_L);
Wire.write((itime << 2) | 0x02);
uint8_t status = Wire.endTransmission();
uint8_t status = Wire.endTransmission();
// action on status
if (!status) {
veml6070_type = 1;
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "VEML6070", veml6070_address);
veml6070_type = 1;
uint8_t veml_model = 0;
GetTextIndexed(veml6070_name, sizeof(veml6070_name), veml_model, kVemlTypes);
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "VEML6070", VEML6070_ADDR_L);
AddLog(LOG_LEVEL_DEBUG);
}
}
/********************************************************************************************/
void Veml6070UvTableInit(void)
{
// fill the uv-risk compare table once, based on the coefficient calculation
for (uint8_t i = 0; i < VEML6070_UV_MAX_INDEX; i++) {
#ifdef USE_VEML6070_RSET
if ( (USE_VEML6070_RSET >= 220000) && (USE_VEML6070_RSET <= 1000000) ) {
uv_risk_map[i] = ( (USE_VEML6070_RSET / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1);
} else {
uv_risk_map[i] = ( (VEML6070_RSET_DEFAULT / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1);
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "VEML6070 resistor error %d"), USE_VEML6070_RSET);
AddLog(LOG_LEVEL_DEBUG);
}
#else
uv_risk_map[i] = ( (VEML6070_RSET_DEFAULT / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1);
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "VEML6070 resistor default used %d"), VEML6070_RSET_DEFAULT);
AddLog(LOG_LEVEL_DEBUG);
#endif
}
}
/********************************************************************************************/
void Veml6070EverySecond(void)
{
// all = 10..15[ms]
if (11 == (uptime %100)) {
Veml6070ModeCmd(1); // on = 1[ms], wakeup the UV sensor
Veml6070Detect(); // 1[ms], check for sensor and init with IT time
Veml6070ModeCmd(0); // off = 5[ms], suspend the UV sensor
} else {
Veml6070ModeCmd(1); // 1[ms], wakeup the UV sensor
uvlevel = Veml6070ReadUv(); // 1..2[ms], get UV raw values
uvrisk = Veml6070UvRiskLevel(uvlevel); // 0..1[ms], get UV risk level
uvpower = Veml6070UvPower(uvrisk); // 2[ms], get UV power in W/m2
Veml6070ModeCmd(0); // off = 5[ms], suspend the UV sensor
}
}
/********************************************************************************************/
void Veml6070ModeCmd(boolean mode_cmd)
{
// mode_cmd 1 = on = 1[ms]
// mode_cmd 0 = off = 2[ms]
Wire.beginTransmission(VEML6070_ADDR_L);
Wire.write((mode_cmd << 0) | 0x02 | (itime << 2));
uint8_t status = Wire.endTransmission();
// action on status
if (!status) {
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "VEML6070 mode_cmd", VEML6070_ADDR_L);
AddLog(LOG_LEVEL_DEBUG);
}
}
/********************************************************************************************/
uint16_t Veml6070ReadUv(void)
{
uint16_t uv_raw = 0;
// read high byte
if (Wire.requestFrom(VEML6070_ADDR_H, 1) != 1) {
return -1;
}
uv_raw = Wire.read();
uv_raw <<= 8;
// read low byte
if (Wire.requestFrom(VEML6070_ADDR_L, 1) != 1) {
return -1;
}
uv_raw |= Wire.read();
// high and low done
return uv_raw;
}
/********************************************************************************************/
double Veml6070UvRiskLevel(uint16_t uv_level)
{
double risk = 0;
if (uv_level < uv_risk_map[VEML6070_UV_MAX_INDEX-1]) {
risk = (double)uv_level / uv_risk_map[0];
// generate uv-risk string
if ( (risk >= 0) && (risk <= 2.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_1); }
else if ( (risk >= 3.0) && (risk <= 5.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_2); }
else if ( (risk >= 6.0) && (risk <= 7.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_3); }
else if ( (risk >= 8.0) && (risk <= 10.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_4); }
else if ( (risk >= 11.0) && (risk <= 12.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_5); }
else if ( (risk >= 13.0) && (risk <= 25.0) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_6); }
else { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_7); }
return risk;
} else {
// out of range and much to high - it must be outerspace or sensor damaged
snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_7);
return ( risk = 99 );
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "VEML6070 out of range %d"), risk);
AddLog(LOG_LEVEL_DEBUG);
}
}
/********************************************************************************************/
double Veml6070UvPower(double uvrisk)
{
// based on calculations for effective irradiation from Karel Vanicek
double power = 0;
return ( power = VEML6070_POWER_COEFFCIENT * uvrisk );
}
/********************************************************************************************/
// normaly in i18n.h, Line 520 .. 525
#ifdef USE_WEBSERVER
const char HTTP_SNS_ULTRAVIOLET[] PROGMEM =
"%s{s}VEML6070 " D_UV_LEVEL "{m}%d{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
// {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
#ifdef USE_VEML6070_SHOW_RAW
const char HTTP_SNS_UV_LEVEL[] PROGMEM = "%s{s}VEML6070 " D_UV_LEVEL "{m}%s " D_UNIT_INCREMENTS "{e}";
#endif // USE_VEML6070_SHOW_RAW
// different uv index level texts
const char HTTP_SNS_UV_INDEX[] PROGMEM = "%s{s}VEML6070 " D_UV_INDEX " {m}%s %s{e}";
const char HTTP_SNS_UV_POWER[] PROGMEM = "%s{s}VEML6070 " D_UV_POWER "{m}%s " D_UNIT_WATT_METER_QUADRAT "{e}";
#endif // USE_WEBSERVER
/********************************************************************************************/
void Veml6070Show(boolean json)
{
if (veml6070_type) {
uint16_t uvlevel = Veml6070ReadUv();
char str_uvlevel[6]; // e.g. 99999 inc = UVLevel
char str_uvrisk[6]; // e.g. 25.99 text = UvIndex
char str_uvpower[6]; // e.g. 0.399 W/m² = UvPower
// convert double values to string
dtostrfd((double)uvlevel, 0, str_uvlevel);
dtostrfd(uvrisk, 2, str_uvrisk);
dtostrfd(uvpower, 3, str_uvpower);
if (json) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"VEML6070\":{\"" D_JSON_UV_LEVEL "\":%d}"), mqtt_data, uvlevel);
#ifdef USE_VEML6070_SHOW_RAW
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_JSON_UV_LEVEL "\":%s,\"" D_JSON_UV_INDEX "\":%s,\"" D_JSON_UV_INDEX_TEXT "\":%s,\"" D_JSON_UV_POWER "\":%s}"),
mqtt_data, veml6070_name, str_uvlevel, str_uvrisk, str_uvrisk_text, str_uvpower);
#else
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_JSON_UV_INDEX "\":%s,\"" D_JSON_UV_INDEX_TEXT "\":%s,\"" D_JSON_UV_POWER "\":%s}"),
mqtt_data, veml6070_name, str_uvrisk, str_uvrisk_text, str_uvpower);
#endif // USE_VEML6070_SHOW_RAW
#ifdef USE_DOMOTICZ
if (0 == tele_period) DomoticzSensor(DZ_ILLUMINANCE, uvlevel);
if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, uvlevel); }
#endif // USE_DOMOTICZ
#ifdef USE_WEBSERVER
} else {
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_ULTRAVIOLET, mqtt_data, uvlevel);
#ifdef USE_VEML6070_SHOW_RAW
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_UV_LEVEL, mqtt_data, str_uvlevel);
#endif // USE_VEML6070_SHOW_RAW
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_UV_INDEX, mqtt_data, str_uvrisk, str_uvrisk_text);
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_UV_POWER, mqtt_data, str_uvpower);
#endif // USE_WEBSERVER
}
}
@ -104,8 +310,12 @@ boolean Xsns11(byte function)
if (i2c_flg) {
switch (function) {
case FUNC_PREP_BEFORE_TELEPERIOD:
Veml6070Detect();
case FUNC_INIT:
Veml6070Detect(); // 1[ms], detect and init the sensor
Veml6070UvTableInit(); // 1[ms], initalize the UV compare table only once
break;
case FUNC_EVERY_SECOND:
Veml6070EverySecond(); // 10..15[ms], tested with OLED display, do all the actions needed to get all sensor values
break;
case FUNC_JSON_APPEND:
Veml6070Show(1);
@ -122,4 +332,3 @@ boolean Xsns11(byte function)
#endif // USE_VEML6070
#endif // USE_I2C

View File

@ -31,17 +31,67 @@
Tsl2561 Tsl(Wire);
uint8_t tsl2561_type = 0;
uint8_t tsl2561_valid = 0;
uint32_t tsl2561_milliLux = 0;
char tsl2561_types[] = "TSL2561";
bool Tsl2561Read()
{
if (tsl2561_valid) { tsl2561_valid--; }
uint8_t id;
bool gain;
Tsl2561::exposure_t exposure;
uint16_t scaledFull, scaledIr;
uint32_t full, ir;
if (Tsl.available()) {
if (Tsl.on()) {
if (Tsl.id(id)
&& Tsl2561Util::autoGain(Tsl, gain, exposure, scaledFull, scaledIr)
&& Tsl2561Util::normalizedLuminosity(gain, exposure, full = scaledFull, ir = scaledIr)
&& Tsl2561Util::milliLux(full, ir, tsl2561_milliLux, Tsl2561::packageCS(id))) {
} else{
tsl2561_milliLux = 0;
}
}
}
tsl2561_valid = SENSOR_MAX_MISS;
return true;
}
void Tsl2561Detect()
{
if (tsl2561_type) { return; }
if (!Tsl.available()) {
Tsl.begin();
if (Tsl.available()) {
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "TSL2561", Tsl.address());
tsl2561_type = 1;
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, tsl2561_types, Tsl.address());
AddLog(LOG_LEVEL_DEBUG);
}
}
}
void Tsl2561EverySecond()
{
if (90 == (uptime %100)) {
// 1mS
Tsl2561Detect();
}
else if (!(uptime %2)) { // Update every 2 seconds
// ?mS - 4Sec
if (tsl2561_type) {
if (!Tsl2561Read()) {
AddLogMissed(tsl2561_types, tsl2561_valid);
// if (!tsl2561_valid) { tsl2561_type = 0; }
}
}
}
}
#ifdef USE_WEBSERVER
const char HTTP_SNS_TSL2561[] PROGMEM =
"%s{s}TSL2561 " D_ILLUMINANCE "{m}%u.%03u " D_UNIT_LUX "{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
@ -49,37 +99,17 @@ const char HTTP_SNS_TSL2561[] PROGMEM =
void Tsl2561Show(boolean json)
{
uint8_t id;
bool gain;
Tsl2561::exposure_t exposure;
uint16_t scaledFull, scaledIr;
uint32_t full, ir;
uint32_t milliLux;
if (Tsl.available()) {
if (Tsl.on()) {
if( Tsl.id(id)
&& Tsl2561Util::autoGain(Tsl, gain, exposure, scaledFull, scaledIr)
&& Tsl2561Util::normalizedLuminosity(gain, exposure, full = scaledFull, ir = scaledIr)
&& Tsl2561Util::milliLux(full, ir, milliLux, Tsl2561::packageCS(id))) {
// snprintf_P(log_data, sizeof(log_data), PSTR(D_ILLUMINANCE " g:%d, e:%d, f:%u, i:%u -> %u.%03u " D_UNIT_LUX),
// gain, exposure, full, ir, milliLux/1000, milliLux%1000);
// AddLog(LOG_LEVEL_DEBUG);
if (json) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"TSL2561\":{\"" D_JSON_ILLUMINANCE "\":%u.%03u}"),
mqtt_data, milliLux/1000, milliLux%1000);
if (tsl2561_valid) {
if (json) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"TSL2561\":{\"" D_JSON_ILLUMINANCE "\":%u.%03u}"),
mqtt_data, tsl2561_milliLux / 1000, tsl2561_milliLux % 1000);
#ifdef USE_DOMOTICZ
if (0 == tele_period) DomoticzSensor(DZ_ILLUMINANCE, (milliLux+500)/1000);
if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, (tsl2561_milliLux + 500) / 1000); }
#endif // USE_DOMOTICZ
#ifdef USE_WEBSERVER
} else {
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TSL2561, mqtt_data, milliLux/1000, milliLux%1000);
} else {
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TSL2561, mqtt_data, tsl2561_milliLux / 1000, tsl2561_milliLux % 1000);
#endif // USE_WEBSERVER
}
}
Tsl.off();
}
}
}
@ -96,9 +126,12 @@ boolean Xsns16(byte function)
if (i2c_flg) {
switch (function) {
case FUNC_PREP_BEFORE_TELEPERIOD:
case FUNC_INIT:
Tsl2561Detect();
break;
case FUNC_EVERY_SECOND:
Tsl2561EverySecond();
break;
case FUNC_JSON_APPEND:
Tsl2561Show(1);
break;

View File

@ -46,7 +46,7 @@ uint8_t lm75ad_type = 0;
uint8_t lm75ad_address;
uint8_t lm75ad_addresses[] = { LM75AD_ADDRESS1, LM75AD_ADDRESS2, LM75AD_ADDRESS3, LM75AD_ADDRESS4, LM75AD_ADDRESS5, LM75AD_ADDRESS6, LM75AD_ADDRESS7, LM75AD_ADDRESS8 };
void LM75ADDetect()
void LM75ADDetect(void)
{
if (lm75ad_type) { return; }
@ -64,7 +64,7 @@ void LM75ADDetect()
}
}
float LM75ADGetTemp() {
float LM75ADGetTemp(void) {
int16_t sign = 1;
uint16_t t = I2cRead16(lm75ad_address, LM75_TEMP_REGISTER);

View File

@ -31,15 +31,6 @@
#define XSNS_29 29
#define MCP230xx_ADDRESS1 0x20
#define MCP230xx_ADDRESS2 0x21
#define MCP230xx_ADDRESS3 0x22
#define MCP230xx_ADDRESS4 0x23
#define MCP230xx_ADDRESS5 0x24
#define MCP230xx_ADDRESS6 0x25
#define MCP230xx_ADDRESS7 0x26
#define MCP230xx_ADDRESS8 0x27
/*
Default register locations for MCP23008 - They change for MCP23017 in default bank mode
*/
@ -53,8 +44,6 @@ uint8_t MCP230xx_INTCAP = 0x08;
uint8_t MCP230xx_GPIO = 0x09;
uint8_t mcp230xx_type = 0;
uint8_t mcp230xx_address;
uint8_t mcp230xx_addresses[] = { MCP230xx_ADDRESS1, MCP230xx_ADDRESS2, MCP230xx_ADDRESS3, MCP230xx_ADDRESS4, MCP230xx_ADDRESS5, MCP230xx_ADDRESS6, MCP230xx_ADDRESS7, MCP230xx_ADDRESS8 };
uint8_t mcp230xx_pincount = 0;
uint8_t mcp230xx_int_en = 0;
uint8_t mcp230xx_int_prio_counter = 0;
@ -131,7 +120,7 @@ const char* IntModeTxt(uint8_t intmo) {
}
uint8_t MCP230xx_readGPIO(uint8_t port) {
return I2cRead8(mcp230xx_address, MCP230xx_GPIO + port);
return I2cRead8(USE_MCP230xx_ADDR, MCP230xx_GPIO + port);
}
void MCP230xx_ApplySettings(void) {
@ -178,11 +167,11 @@ void MCP230xx_ApplySettings(void) {
}
#endif // USE_MCP230xx_OUTPUT
}
I2cWrite8(mcp230xx_address, MCP230xx_GPPU+mcp230xx_port, reg_gppu);
I2cWrite8(mcp230xx_address, MCP230xx_GPINTEN+mcp230xx_port, reg_gpinten);
I2cWrite8(mcp230xx_address, MCP230xx_IODIR+mcp230xx_port, reg_iodir);
I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPPU+mcp230xx_port, reg_gppu);
I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPINTEN+mcp230xx_port, reg_gpinten);
I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_IODIR+mcp230xx_port, reg_iodir);
#ifdef USE_MCP230xx_OUTPUT
I2cWrite8(mcp230xx_address, MCP230xx_GPIO+mcp230xx_port, reg_portpins);
I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO+mcp230xx_port, reg_portpins);
#endif // USE_MCP230xx_OUTPUT
}
for (uint8_t idx=0;idx<mcp230xx_pincount;idx++) {
@ -192,7 +181,7 @@ void MCP230xx_ApplySettings(void) {
MCP230xx_CheckForIntCounter(); // update register on whether or not we should be counting interrupts
}
void MCP230xx_Detect()
void MCP230xx_Detect(void)
{
if (mcp230xx_type) {
return;
@ -200,34 +189,30 @@ void MCP230xx_Detect()
uint8_t buffer;
for (byte i = 0; i < sizeof(mcp230xx_addresses); i++) {
mcp230xx_address = mcp230xx_addresses[i];
I2cWrite8(mcp230xx_address, MCP230xx_IOCON, 0x80); // attempt to set bank mode - this will only work on MCP23017, so its the best way to detect the different chips 23008 vs 23017
if (I2cValidRead8(&buffer, mcp230xx_address, MCP230xx_IOCON)) {
if (0x00 == buffer) {
mcp230xx_type = 1; // We have a MCP23008
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "MCP23008", mcp230xx_address);
I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_IOCON, 0x80); // attempt to set bank mode - this will only work on MCP23017, so its the best way to detect the different chips 23008 vs 23017
if (I2cValidRead8(&buffer, USE_MCP230xx_ADDR, MCP230xx_IOCON)) {
if (0x00 == buffer) {
mcp230xx_type = 1; // We have a MCP23008
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "MCP23008", USE_MCP230xx_ADDR);
AddLog(LOG_LEVEL_DEBUG);
mcp230xx_pincount = 8;
MCP230xx_ApplySettings();
} else {
if (0x80 == buffer) {
mcp230xx_type = 2; // We have a MCP23017
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "MCP23017", USE_MCP230xx_ADDR);
AddLog(LOG_LEVEL_DEBUG);
mcp230xx_pincount = 8;
mcp230xx_pincount = 16;
// Reset bank mode to 0
I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_IOCON, 0x00);
// Update register locations for MCP23017
MCP230xx_GPINTEN = 0x04;
MCP230xx_GPPU = 0x0C;
MCP230xx_INTF = 0x0E;
MCP230xx_INTCAP = 0x10;
MCP230xx_GPIO = 0x12;
MCP230xx_ApplySettings();
} else {
if (0x80 == buffer) {
mcp230xx_type = 2; // We have a MCP23017
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "MCP23017", mcp230xx_address);
AddLog(LOG_LEVEL_DEBUG);
mcp230xx_pincount = 16;
// Reset bank mode to 0
I2cWrite8(mcp230xx_address, MCP230xx_IOCON, 0x00);
// Update register locations for MCP23017
MCP230xx_GPINTEN = 0x04;
MCP230xx_GPPU = 0x0C;
MCP230xx_INTF = 0x0E;
MCP230xx_INTCAP = 0x10;
MCP230xx_GPIO = 0x12;
MCP230xx_ApplySettings();
}
}
break;
}
}
}
@ -237,9 +222,9 @@ void MCP230xx_CheckForInterrupt(void) {
uint8_t mcp230xx_intcap = 0;
uint8_t report_int;
for (uint8_t mcp230xx_port=0;mcp230xx_port<mcp230xx_type;mcp230xx_port++) {
if (I2cValidRead8(&intf,mcp230xx_address,MCP230xx_INTF+mcp230xx_port)) {
if (I2cValidRead8(&intf,USE_MCP230xx_ADDR,MCP230xx_INTF+mcp230xx_port)) {
if (intf > 0) {
if (I2cValidRead8(&mcp230xx_intcap, mcp230xx_address, MCP230xx_INTCAP+mcp230xx_port)) {
if (I2cValidRead8(&mcp230xx_intcap, USE_MCP230xx_ADDR, MCP230xx_INTCAP+mcp230xx_port)) {
for (uint8_t intp = 0; intp < 8; intp++) {
if ((intf >> intp) & 0x01) { // we know which pin caused interrupt
report_int = 0;
@ -280,7 +265,9 @@ void MCP230xx_CheckForInterrupt(void) {
if (report_int) {
bool int_tele = false;
bool int_event = false;
unsigned long millis_since_last_int = millis() - int_millis[intp+(mcp230xx_port*8)];
unsigned long millis_now = millis();
unsigned long millis_since_last_int = millis_now - int_millis[intp+(mcp230xx_port*8)];
int_millis[intp+(mcp230xx_port*8)]=millis_now;
switch (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_report_mode) {
case 0:
int_tele=true;
@ -297,14 +284,13 @@ void MCP230xx_CheckForInterrupt(void) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str());
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"MCP230XX_INT\":{\"D%i\":%i,\"MS\":%lu}"), mqtt_data, intp+(mcp230xx_port*8), ((mcp230xx_intcap >> intp) & 0x01),millis_since_last_int);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data);
MqttPublishPrefixTopic_P(RESULT_OR_STAT, mqtt_data);
MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("MCP230XX_INT"));
}
if (int_event) {
char command[19]; // Theoretical max = 'event MCPINT_D16=1' so 18 + 1 (for the \n)
sprintf(command,"event MCPINT_D%i=%i",intp+(mcp230xx_port*8),((mcp230xx_intcap >> intp) & 0x01));
ExecuteCommand(command, SRC_RULE);
}
int_millis[intp+(mcp230xx_port*8)]=millis();
}
}
}
@ -366,7 +352,7 @@ void MCP230xx_SetOutPin(uint8_t pin,uint8_t pinstate) {
portpins ^= (1 << (pin-(port*8)));
}
}
I2cWrite8(mcp230xx_address, MCP230xx_GPIO + port, portpins);
I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO + port, portpins);
if (Settings.flag.save_state) { // Firmware configured to save last known state in settings
Settings.mcp230xx_config[pin].saved_state=portpins>>(pin-(port*8))&1;
Settings.mcp230xx_config[pin+pinadd].saved_state=portpins>>(pin+pinadd-(port*8))&1;
@ -705,7 +691,7 @@ void MCP230xx_OutputTelemetry(void) {
#endif // USE_MCP230xx_OUTPUT
void MCP230xx_Interrupt_Counter_Report() {
void MCP230xx_Interrupt_Counter_Report(void) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\",\"MCP230_INTTIMER\": {"), GetDateAndTime(DT_LOCAL).c_str());
for (uint8_t pinx = 0;pinx < mcp230xx_pincount;pinx++) {
if (Settings.mcp230xx_config[pinx].int_count_en) { // Counting is enabled for this pin so we add to report

1963
tools/decode-config.py Normal file

File diff suppressed because it is too large Load Diff