Merge branch 'development' of https://github.com/arendst/Sonoff-Tasmota into hm10

This commit is contained in:
Staars 2020-01-31 07:39:35 +01:00
commit 3e43c4c551
58 changed files with 1591 additions and 552 deletions

View File

@ -128,6 +128,7 @@
| | | | | | | | |
| USE_IR_REMOTE | - | - | x | x | x | x | x |
| USE_IR_RECEIVE | - | - | x | x | x | x | x |
| USE_IR_REMOTE_FULL | - | - | - | - | - | x | - | Enable ALL protocols
| | | | | | | | |
| USE_SR04 | - | - | x | x | x | - | x |
| USE_TM1638 | - | - | - | - | x | - | - |

View File

@ -52,11 +52,13 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
## Changelog
### Version 8.1.0.4
### Version 8.1.0.5
- Change Lights: simplified gamma correction and 10 bits internal computation
- Change commands ``Prefix``, ``Ssid``, ``StateText``, ``NTPServer``, and ``FriendlyName`` displaying all items
- Change IRremoteESP8266 library updated to v2.7.2
- Change Zigbee command prefix from ``Zigbee*`` to ``Zb*``
- Change wifi connectivity stability (#7602)
- Fix Sonoff Bridge, Sc, L1, iFan03 and CSE7766 serial interface to forced speed, config and disable logging
- Fix commands ``Display`` and ``Counter`` from overruling command processing (#7322)
- Fix ``White`` added to light status (#7142)
@ -66,6 +68,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
- Fix ``WakeUp <x>`` ignores provided value (#7473)
- Fix exception 9 restart on log message in Ticker interrupt service routines NTP, Wemos and Hue emulation (#7496)
- Fix ``PowerDelta`` zero power detection (#7515)
- Fix ``RGBWWTable`` ignored (#7572)
- Add command ``SetOption79 0/1`` to enable reset of counters at teleperiod time by Andre Thomas (#7355)
- Add command ``SetOption82 0/1`` to limit the CT range for Alexa to 200..380
- Add command ``ShutterButton <parameters>`` to control shutter(s) by to-scho (#7403)
@ -80,3 +83,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
- Add support to BMP driver to enter reset state (sleep enable) when deep sleep is used in Tasmota
- Add support for gzipped binaries
- Add web page sliders when ``SetOption37 128`` is active allowing control of white(s)
- Add most SetOptions as defines to my_user_config.h
- Add SoftwareSerial to CSE7766 driver allowing different GPIOs (#7563)
- Add optional parameter <startcolor> to command ``Scheme <scheme>, <startcolor>`` to control initial start color
- Add rule trigger on one level deeper using syntax with two ``#`` like ``on zigbeereceived#vibration_sensor#aqaracubeside=0 do ...``

View File

@ -1,10 +1,22 @@
## Unreleased (development)
### 8.1.0.5 20200126
- Change wifi connectivity stability (#7602)
- Add ``SetOption84 1`` sends AWS IoT device shadow updates (alternative to retained)
### 8.1.0.4 20200116
- Change Zigbee command prefix from ``Zigbee*`` to ``Zb*``
- Fix ``PowerDelta`` zero power detection (#7515)
- Fix OTA minimal gzipped detection regression from 8.1.0.3
- Fix ``RGBWWTable`` ignored (#7572)
- Add web page sliders when ``SetOption37 128`` is active allowing control of white(s)
- Add Zigbee persistence and friendly names
- Add most SetOptions as defines to my_user_config.h
- Add SoftwareSerial to CSE7766 driver allowing different GPIOs (#7563)
- Add optional parameter <startcolor> to command ``Scheme <scheme>, <startcolor>`` to control initial start color
- Add rule trigger on one level deeper using syntax with two ``#`` like ``on zigbeereceived#vibration_sensor#aqaracubeside=0 do ...``
### 8.1.0.3 20200106

View File

@ -114,6 +114,7 @@
#define D_JSON_ACTIVE_POWERUSAGE "ActivePower"
#define D_JSON_APPARENT_POWERUSAGE "ApparentPower"
#define D_JSON_REACTIVE_POWERUSAGE "ReactivePower"
#define D_JSON_RANGE "Range"
#define D_JSON_PRESSURE "Pressure"
#define D_JSON_PRESSUREATSEALEVEL "SeaPressure"
#define D_JSON_PRESSURE_UNIT "PressureUnit"
@ -466,26 +467,32 @@
#define D_JSON_TUYA_MCU_RECEIVED "TuyaReceived"
// Commands xdrv_23_zigbee.ino
#define D_PRFX_ZB "Zb"
#define D_PRFX_ZIGBEE "Zigbee"
#define D_ZIGBEE_NOT_STARTED "Zigbee not started (yet)"
#define D_CMND_ZIGBEE_PERMITJOIN "ZigbeePermitJoin"
#define D_CMND_ZIGBEE_STATUS "ZigbeeStatus"
#define D_CMND_ZIGBEE_RESET "ZigbeeReset"
#define D_CMND_ZIGBEE_PERMITJOIN "PermitJoin"
#define D_CMND_ZIGBEE_STATUS "Status"
#define D_CMND_ZIGBEE_RESET "Reset"
#define D_JSON_ZIGBEE_CC2530 "CC2530"
#define D_CMND_ZIGBEEZNPRECEIVE "ZigbeeZNPReceive" // only for debug
#define D_CMND_ZIGBEEZNPSEND "ZigbeeZNPSend"
#define D_JSON_ZIGBEE_STATE "ZigbeeState"
#define D_JSON_ZIGBEEZNPRECEIVED "ZigbeeZNPReceived"
#define D_JSON_ZIGBEEZNPSENT "ZigbeeZNPSent"
#define D_JSON_ZIGBEEZCL_RECEIVED "ZigbeeZCLReceived"
#define D_JSON_ZIGBEEZCL_RAW_RECEIVED "ZigbeeZCLRawReceived"
#define D_CMND_ZIGBEEZNPRECEIVE "ZNPReceive" // only for debug
#define D_CMND_ZIGBEEZNPSEND "ZNPSend"
#define D_JSON_ZIGBEE_STATE "ZbState"
#define D_JSON_ZIGBEEZNPRECEIVED "ZbZNPReceived"
#define D_JSON_ZIGBEEZNPSENT "ZbZNPSent"
#define D_JSON_ZIGBEEZCL_RECEIVED "ZbZCLReceived"
#define D_JSON_ZIGBEEZCL_RAW_RECEIVED "ZbZCLRawReceived"
#define D_JSON_ZIGBEE_DEVICE "Device"
#define D_JSON_ZIGBEE_NAME "Name"
#define D_CMND_ZIGBEE_PROBE "ZigbeeProbe"
#define D_CMND_ZIGBEE_RECEIVED "ZigbeeReceived"
#define D_CMND_ZIGBEE_NAME "Name"
#define D_CMND_ZIGBEE_PROBE "Probe"
#define D_CMND_ZIGBEE_FORGET "Forget"
#define D_CMND_ZIGBEE_SAVE "Save"
#define D_CMND_ZIGBEE_LINKQUALITY "LinkQuality"
#define D_CMND_ZIGBEE_READ "ZigbeeRead"
#define D_CMND_ZIGBEE_SEND "ZigbeeSend"
#define D_JSON_ZIGBEE_ZCL_SENT "ZigbeeZCLSent"
#define D_CMND_ZIGBEE_READ "Read"
#define D_CMND_ZIGBEE_SEND "Send"
#define D_JSON_ZIGBEE_ZCL_SENT "ZbZCLSent"
#define D_JSON_ZIGBEE_RECEIVED "ZbReceived"
#define D_JSON_ZIGBEE_RECEIVED_LEGACY "ZigbeeReceived"
// Commands xdrv_25_A4988_Stepper.ino
#define D_CMND_MOTOR "MOTOR"
@ -602,6 +609,7 @@ const char JSON_SNS_TEMPHUM[] PROGMEM = ",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,
const char JSON_SNS_ILLUMINANCE[] PROGMEM = ",\"%s\":{\"" D_JSON_ILLUMINANCE "\":%d}";
const char JSON_SNS_MOISTURE[] PROGMEM = ",\"%s\":{\"" D_JSON_MOISTURE "\":%d}";
const char JSON_SNS_RANGE[] PROGMEM = ",\"%s\":{\"" D_JSON_RANGE "\":%d}";
const char JSON_SNS_GNGPM[] PROGMEM = ",\"%s\":{\"" D_JSON_TOTAL_USAGE "\":%s,\"" D_JSON_FLOWRATE "\":%s}";
@ -633,6 +641,7 @@ const char HTTP_SNS_CO2EAVG[] PROGMEM = "{s}%s " D_ECO2 "{m}%d " D_UNIT_PARTS_PE
const char HTTP_SNS_GALLONS[] PROGMEM = "{s}%s " D_TOTAL_USAGE "{m}%s " D_UNIT_GALLONS " {e}";
const char HTTP_SNS_GPM[] PROGMEM = "{s}%s " D_FLOW_RATE "{m}%s " D_UNIT_GALLONS_PER_MIN" {e}";
const char HTTP_SNS_MOISTURE[] PROGMEM = "{s}%s " D_MOISTURE "{m}%d %%{e}";
const char HTTP_SNS_RANGE[] PROGMEM = "{s}%s " D_RANGE "{m}%d{e}";
const char HTTP_SNS_VOLTAGE[] PROGMEM = "{s}" D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}";
const char HTTP_SNS_CURRENT[] PROGMEM = "{s}" D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}";

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "Размер на програмата"
#define D_PROJECT "Проект"
#define D_RAIN "Дъжд"
#define D_RANGE "Range"
#define D_RECEIVED "Получено"
#define D_RESTART "Рестарт"
#define D_RESTARTING "Рестартиране"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "Velikost programu"
#define D_PROJECT "Projekt"
#define D_RAIN "Rain"
#define D_RANGE "Range"
#define D_RECEIVED "Přijatý"
#define D_RESTART "Restart"
#define D_RESTARTING "Restartování"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "Ben. Flash Speicher"
#define D_PROJECT "Projekt"
#define D_RAIN "Regen"
#define D_RANGE "Range"
#define D_RECEIVED "erhalten"
#define D_RESTART "Neustart"
#define D_RESTARTING "starte neu"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "Μέγεθος προγράμματος"
#define D_PROJECT "Έργο"
#define D_RAIN "Rain"
#define D_RANGE "Range"
#define D_RECEIVED "Ελήφθη"
#define D_RESTART "Επανεκκίνηση"
#define D_RESTARTING "Επανεκκινεί"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "Program Size"
#define D_PROJECT "Project"
#define D_RAIN "Rain"
#define D_RANGE "Range"
#define D_RECEIVED "Received"
#define D_RESTART "Restart"
#define D_RESTARTING "Restarting"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "Tamaño Programa"
#define D_PROJECT "Proyecto"
#define D_RAIN "Lluvia"
#define D_RANGE "Range"
#define D_RECEIVED "Recibido"
#define D_RESTART "Reiniciar"
#define D_RESTARTING "Reiniciando"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "Taille programme"
#define D_PROJECT "Projet"
#define D_RAIN "Pluie"
#define D_RANGE "Range"
#define D_RECEIVED "Reçu"
#define D_RESTART "Redémarrage"
#define D_RESTARTING "Redémarre"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "גודל תוכנית"
#define D_PROJECT "פרויקט"
#define D_RAIN "גשם"
#define D_RANGE "Range"
#define D_RECEIVED "התקבל"
#define D_RESTART "איתחול"
#define D_RESTARTING "הפעלה מחדש"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "Program méret"
#define D_PROJECT "Projekt"
#define D_RAIN "Eső"
#define D_RANGE "Range"
#define D_RECEIVED "Érkezett"
#define D_RESTART "Újraindítás"
#define D_RESTARTING "Újraindítás"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "Dimensione Programma"
#define D_PROJECT "Progetto"
#define D_RAIN "Rain"
#define D_RANGE "Range"
#define D_RECEIVED "Ricevuto"
#define D_RESTART "Riavvio"
#define D_RESTARTING "Riavviando"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "프로그램 용량"
#define D_PROJECT "프로젝트"
#define D_RAIN "비"
#define D_RANGE "Range"
#define D_RECEIVED "받음"
#define D_RESTART "재시작"
#define D_RESTARTING "재시작 중.."

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "Programma Grootte"
#define D_PROJECT "Project"
#define D_RAIN "Regen"
#define D_RANGE "Range"
#define D_RECEIVED "Ontvangen"
#define D_RESTART "Herstart"
#define D_RESTARTING "Herstarten"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "Rozmiar programu"
#define D_PROJECT "Projekt"
#define D_RAIN "Deszcz"
#define D_RANGE "Range"
#define D_RECEIVED "Otrzymany"
#define D_RESTART "Restart"
#define D_RESTARTING "Restartowanie"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "Tamanho do programa"
#define D_PROJECT "Projeto"
#define D_RAIN "Rain"
#define D_RANGE "Range"
#define D_RECEIVED "Recebido"
#define D_RESTART "Reiniciar"
#define D_RESTARTING "Reiniciando"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "Tamanho do Programa"
#define D_PROJECT "Projeto"
#define D_RAIN "Chuva"
#define D_RANGE "Range"
#define D_RECEIVED "Recebido"
#define D_RESTART "Reiniciar"
#define D_RESTARTING "A reiniciar"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "Размер программы "
#define D_PROJECT "Проект"
#define D_RAIN "Rain"
#define D_RANGE "Range"
#define D_RECEIVED "Получено"
#define D_RESTART "Перезапуск"
#define D_RESTARTING "Перезапуск"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "Veľkosť programu"
#define D_PROJECT "Projekt"
#define D_RAIN "Dážď"
#define D_RANGE "Range"
#define D_RECEIVED "Prijatý"
#define D_RESTART "Reštart"
#define D_RESTARTING "Reštartuje sa"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "Programstorlek"
#define D_PROJECT "Projekt"
#define D_RAIN "Regn"
#define D_RANGE "Range"
#define D_RECEIVED "Mottagen"
#define D_RESTART "Omstart"
#define D_RESTARTING "Startar om"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "Yazılım Boyutu"
#define D_PROJECT "Proje"
#define D_RAIN "Rain"
#define D_RANGE "Range"
#define D_RECEIVED "Alınan"
#define D_RESTART "Yeniden Başlat"
#define D_RESTARTING "Yeniden Başlatılıyor"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "Розмір програми"
#define D_PROJECT "Проект"
#define D_RAIN "Дощ"
#define D_RANGE "Range"
#define D_RECEIVED "Отримано"
#define D_RESTART "Перезавантаження"
#define D_RESTARTING "Перезавантаження"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "固件大小"
#define D_PROJECT "项目:"
#define D_RAIN "降水量"
#define D_RANGE "Range"
#define D_RECEIVED "已接收"
#define D_RESTART "重启"
#define D_RESTARTING "正在重启"

View File

@ -137,6 +137,7 @@
#define D_PROGRAM_SIZE "韌體大小"
#define D_PROJECT "項目:"
#define D_RAIN "Rain"
#define D_RANGE "Range"
#define D_RECEIVED "已接收"
#define D_RESTART "重啟"
#define D_RESTARTING "正在重啟"

View File

@ -53,7 +53,7 @@
//#define MODULE SONOFF_BASIC // [Module] Select default model from tasmota_template.h
#define SAVE_DATA 1 // [SaveData] Save changed parameters to Flash (0 = disable, 1 - 3600 seconds)
#define SAVE_STATE 1 // [SetOption0] Save changed power state to Flash (0 = disable, 1 = enable)
#define SAVE_STATE true // [SetOption0] Save changed power state to Flash (false = disable, true = enable)
#define BOOT_LOOP_OFFSET 1 // [SetOption36] Number of boot loops before starting restoring defaults (0 = disable, 1..200 = boot loops offset)
// -- Wifi ----------------------------------------
@ -69,6 +69,8 @@
#define WIFI_CONFIG_TOOL WIFI_RETRY // [WifiConfig] Default tool if wifi fails to connect (default option: 4 - WIFI_RETRY)
// (WIFI_RESTART, WIFI_MANAGER, WIFI_RETRY, WIFI_WAIT, WIFI_SERIAL, WIFI_MANAGER_RESET_ONLY)
// The configuration can be changed after first setup using WifiConfig 0, 2, 4, 5, 6 and 7.
#define WIFI_SCAN_AT_RESTART false // [SetOption56] Scan wifi network at restart for configured AP's
#define WIFI_SCAN_REGULARLY false // [SetOption57] Scan wifi network every 44 minutes for configured AP's
// -- Syslog --------------------------------------
#define SYS_LOG_HOST "" // [LogHost] (Linux) syslog host
@ -80,9 +82,10 @@
// -- Ota -----------------------------------------
#define OTA_URL "http://thehackbox.org/tasmota/release/tasmota.bin" // [OtaUrl]
#define OTA_COMPATIBILITY false // [SetOption78] Disable OTA compatibility check
// -- MQTT ----------------------------------------
#define MQTT_USE 1 // [SetOption3] Select default MQTT use (0 = Off, 1 = On)
#define MQTT_USE true // [SetOption3] Select default MQTT use (false = Off, true = On)
#define MQTT_HOST "" // [MqttHost]
#define MQTT_FINGERPRINT1 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" // [MqttFingerprint1]
@ -91,10 +94,11 @@
#define MQTT_USER "DVES_USER" // [MqttUser] MQTT user
#define MQTT_PASS "DVES_PASS" // [MqttPassword] MQTT password
#define MQTT_BUTTON_RETAIN 0 // [ButtonRetain] Button may send retain flag (0 = off, 1 = on)
#define MQTT_POWER_RETAIN 0 // [PowerRetain] Power status message may send retain flag (0 = off, 1 = on)
#define MQTT_SWITCH_RETAIN 0 // [SwitchRetain] Switch may send retain flag (0 = off, 1 = on)
#define MQTT_BUTTON_SWITCH_FORCE_LOCAL 0 // [SetOption61] Force local operation when button/switch topic is set (0 = off, 1 = on)
#define MQTT_BUTTON_RETAIN false // [ButtonRetain] Button may send retain flag (false = off, true = on)
#define MQTT_POWER_RETAIN false // [PowerRetain] Power status message may send retain flag (false = off, true = on)
#define MQTT_SWITCH_RETAIN false // [SwitchRetain] Switch may send retain flag (false = off, true = on)
#define MQTT_SENSOR_RETAIN false // [SensorRetain] Sensor may send retain flag (false = off, true = on)
#define MQTT_BUTTON_SWITCH_FORCE_LOCAL false // [SetOption61] Force local operation when button/switch topic is set (false = off, true = on)
#define MQTT_STATUS_OFF "OFF" // [StateText1] Command or Status result when turned off (needs to be a string like "0" or "Off")
#define MQTT_STATUS_ON "ON" // [StateText2] Command or Status result when turned on (needs to be a string like "1" or "On")
@ -113,19 +117,30 @@
// %topic% token options (also ButtonTopic and SwitchTopic)
#define MQTT_TOPIC PROJECT // [Topic] (unique) MQTT device topic, set to 'PROJECT "_%06X"' for unique topic including device MAC address
#define MQTT_GRPTOPIC "tasmotas" // [GroupTopic] MQTT Group topic
#define MQTT_GROUPTOPIC_FORMAT false // [SetOption75] GroupTopic replaces %topic% (false) or fixed topic cmnd/grouptopic (true)
#define MQTT_BUTTON_TOPIC "0" // [ButtonTopic] MQTT button topic, "0" = same as MQTT_TOPIC, set to 'PROJECT "_BTN_%06X"' for unique topic including device MAC address
#define MQTT_SWITCH_TOPIC "0" // [SwitchTopic] MQTT button topic, "0" = same as MQTT_TOPIC, set to 'PROJECT "_SW_%06X"' for unique topic including device MAC address
#define MQTT_CLIENT_ID "DVES_%06X" // [MqttClient] Also fall back topic using Chip Id = last 6 characters of MAC address
// -- MQTT - Telemetry ----------------------------
#define TELE_PERIOD 300 // [TelePeriod] Telemetry (0 = disable, 10 - 3600 seconds)
#define TELE_ON_POWER 0 // [SetOption59] send tele/STATE together with stat/RESULT (0 = Disable, 1 = Enable)
#define TELE_ON_POWER false // [SetOption59] send tele/STATE together with stat/RESULT (false = Disable, true = Enable)
// -- MQTT - Domoticz -----------------------------
#define DOMOTICZ_UPDATE_TIMER 0 // [DomoticzUpdateTimer] Send relay status (0 = disable, 1 - 3600 seconds)
// -- MQTT - Home Assistant Discovery -------------
#define HOME_ASSISTANT_DISCOVERY_ENABLE 0 // [SetOption19] Home Assistant Discovery (0 = Disable, 1 = Enable)
#define HOME_ASSISTANT_DISCOVERY_ENABLE false // [SetOption19] Home Assistant Discovery (false = Disable, true = Enable)
#define HASS_AS_LIGHT false // [SetOption30] Enforce HAss autodiscovery as light
// -- MQTT - Options ------------------------------
#define MQTT_RESULT_COMMAND false // [SetOption4] Switch between MQTT RESULT or COMMAND
#define MQTT_LWT_MESSAGE false // [SetOption10] Switch between MQTT LWT OFFLINE or empty message
#define MQTT_POWER_FORMAT false // [SetOption26] Switch between POWER or POWER1 for single power devices
#define MQTT_APPEND_TIMEZONE false // [SetOption52] Append timezone to JSON time
#define MQTT_NO_HOLD_RETAIN false // [SetOption62] Disable retain flag on HOLD messages
#define MQTT_INDEX_SEPARATOR false // [SetOption64] Enable "_" instead of "-" as sensor index separator
#define MQTT_TUYA_RECEIVED false // [SetOption66] Enable TuyaMcuReceived messages over Mqtt
// -- HTTP ----------------------------------------
#define WEB_SERVER 2 // [WebServer] Web server (0 = Off, 1 = Start as User, 2 = Start as Admin)
@ -134,6 +149,9 @@
#define EMULATION EMUL_NONE // [Emulation] Select Belkin WeMo (single relay/light) or Hue Bridge emulation (multi relay/light) (EMUL_NONE, EMUL_WEMO or EMUL_HUE)
#define CORS_DOMAIN "" // [Cors] CORS Domain for preflight requests
// -- HTTP Options --------------------------------
#define GUI_SHOW_HOSTNAME false // [SetOption53] Show hostname and IP address in GUI main menu
// -- HTTP GUI Colors -----------------------------
// HTML hex color codes. Only 3 and 6 digit hex string values are supported!! See https://www.w3schools.com/colors/colors_hex.asp
// Light theme - pre v7
@ -181,8 +199,12 @@
#define COLOR_TIMER_TAB_BACKGROUND "#999" // [WebColor18] Config timer tab background color - Dark gray
#define COLOR_TITLE_TEXT "#eaeaea" // [WebColor19] Title text color - Very light gray
// -- KNX -----------------------------------------
#define KNX_ENABLED false // [Knx_Enabled] Enable KNX protocol
#define KNX_ENHANCED false // [Knx_Enhanced] Enable KNX Enhanced Mode
// -- mDNS ----------------------------------------
#define MDNS_ENABLED 0 // [SetOption55] Use mDNS (0 = Disable, 1 = Enable)
#define MDNS_ENABLED false // [SetOption55] Use mDNS (false = Disable, true = Enable)
// -- Time - Up to three NTP servers in your region
#define NTP_SERVER1 "pool.ntp.org" // [NtpServer1] Select first NTP server by name or IP address (129.250.35.250)
@ -214,28 +236,69 @@
#define APP_LEDSTATE LED_POWER // [LedState] Function of led
// (LED_OFF, LED_POWER, LED_MQTTSUB, LED_POWER_MQTTSUB, LED_MQTTPUB, LED_POWER_MQTTPUB, LED_MQTT, LED_POWER_MQTT)
#define APP_LEDMASK 0xFFFF // [LedMask] Assign Relay to Power led (0xFFFF is default)
#define APP_ENABLE_LEDLINK false // [SetOption31] Enable link led blinking
#define APP_PULSETIME 0 // [PulseTime] Time in 0.1 Sec to turn off power for relay 1 (0 = disabled)
#define APP_POWERON_STATE POWER_ALL_SAVED // [PowerOnState] Power On Relay state
// (POWER_ALL_OFF, POWER_ALL_ON, POWER_ALL_SAVED_TOGGLE, POWER_ALL_SAVED, POWER_ALL_ALWAYS_ON, POWER_ALL_OFF_PULSETIME_ON)
#define APP_BLINKTIME 10 // [BlinkTime] Time in 0.1 Sec to blink/toggle power for relay 1
#define APP_BLINKCOUNT 10 // [BlinkCount] Number of blinks (0 = 32000)
#define APP_NORMAL_SLEEP false // [SetOption60] Enable normal sleep instead of dynamic sleep
#define APP_SLEEP 0 // [Sleep] Sleep time to lower energy consumption (0 = Off, 1 - 250 mSec),
#define PWM_MAX_SLEEP 10 // Sleep will be lowered to this value when light is on, to avoid flickering
#define KEY_DEBOUNCE_TIME 50 // [ButtonDebounce] Number of mSeconds button press debounce time
#define KEY_HOLD_TIME 40 // [SetOption32] Number of 0.1 seconds to hold Button or external Pushbutton before sending HOLD message
#define KEY_DISABLE_MULTIPRESS false // [SetOption1] Disable button multipress
#define KEY_SWAP_DOUBLE_PRESS false // [SetOption11] Swap button single and double press functionality
#define KEY_ONLY_SINGLE_PRESS false // [SetOption13] Enable only single press to speed up button press recognition
#define SWITCH_DEBOUNCE_TIME 50 // [SwitchDebounce] Number of mSeconds switch press debounce time
#define SWITCH_MODE TOGGLE // [SwitchMode] TOGGLE, FOLLOW, FOLLOW_INV, PUSHBUTTON, PUSHBUTTON_INV, PUSHBUTTONHOLD, PUSHBUTTONHOLD_INV, PUSHBUTTON_TOGGLE, TOGGLEMULTI, FOLLOWMULTI, FOLLOWMULTI_INV (the wall switch state)
#define WS2812_LEDS 30 // [Pixels] Number of WS2812 LEDs to start with (max is 512)
#define TEMP_CONVERSION 0 // [SetOption8] Return temperature in (0 = Celsius or 1 = Fahrenheit)
#define PRESSURE_CONVERSION 0 // [SetOption24] Return pressure in (0 = hPa or 1 = mmHg)
#define TEMP_CONVERSION false // [SetOption8] Return temperature in (false = Celsius or true = Fahrenheit)
#define PRESSURE_CONVERSION false // [SetOption24] Return pressure in (false = hPa or true = mmHg)
#define TEMP_RESOLUTION 1 // [TempRes] Maximum number of decimals (0 - 3) showing sensor Temperature
#define HUMIDITY_RESOLUTION 1 // [HumRes] Maximum number of decimals (0 - 3) showing sensor Humidity
#define PRESSURE_RESOLUTION 1 // [PressRes] Maximum number of decimals (0 - 3) showing sensor Pressure
#define ENERGY_RESOLUTION 3 // [EnergyRes] Maximum number of decimals (0 - 5) showing energy usage in kWh
#define CALC_RESOLUTION 3 // [CalcRes] Maximum number of decimals (0 - 7) used in commands ADD, SUB, MULT and SCALE
#define APP_FLASH_CYCLE false // [SetOption12] Switch between dynamic or fixed slot flash save location
#define APP_NO_RELAY_SCAN false // [SetOption63] Don't scan relay power state at restart
#define APP_DISABLE_POWERCYCLE false // [SetOption65] Disable fast power cycle detection for device reset
#define DEEPSLEEP_BOOTCOUNT false // [SetOption76] Enable incrementing bootcount when deepsleep is enabled
// -- Lights --------------------------------------
#define WS2812_LEDS 30 // [Pixels] Number of WS2812 LEDs to start with (max is 512)
#define LIGHT_MODE true // [SetOption15] Switch between commands PWM or COLOR/DIMMER/CT/CHANNEL
#define LIGHT_CLOCK_DIRECTION false // [SetOption16] Switch WS2812 clock between clockwise or counter-clockwise
#define LIGHT_COLOR_RADIX false // [SetOption17] Switch between decimal or hexadecimal color output (false = hexadecimal, true = decimal)
#define LIGHT_PAIRS_CO2 false // [SetOption18] Enable Pair light signal with CO2 sensor
#define LIGHT_POWER_CONTROL false // [SetOption20] Enable power control in relation to Dimmer/Color/Ct changes
#define LIGHT_CHANNEL_MODE false // [SetOption68] Enable multi-channels PWM instead of Color PWM
#define LIGHT_SLIDER_POWER false // [SetOption77] Do not power off if slider moved to far left
#define LIGHT_ALEXA_CT_RANGE false // [SetOption82] Reduced CT range for Alexa
// -- Energy --------------------------------------
#define ENERGY_VOLTAGE_ALWAYS false // [SetOption21] Enable show voltage even if powered off
#define ENERGY_DDS2382_MODE false // [SetOption71] Enable DDS2382 different Modbus registers for Active Energy (#6531)
#define ENERGY_HARDWARE_TOTALS false // [SetOption72] Enable hardware energy total counter as reference (#6561)
// -- Other Options -------------------------------
#define TIMERS_ENABLED false // [Timers] Enable Timers
#define RF_DATA_RADIX false // [SetOption28] RF receive data format (false = hexadecimal, true = decimal)
#define IR_DATA_RADIX false // [SetOption29] IR receive data format (false = hexadecimal, true = decimal)
#define TUYA_SETOPTION_20 false // [SetOption54] Apply SetOption20 settings to Tuya device
#define IR_ADD_RAW_DATA false // [SetOption58] Add IR Raw data to JSON message
#define BUZZER_ENABLE false // [SetOption67] Enable buzzer when available
#define DS18X20_PULL_UP false // [SetOption74] Enable internal pullup for single DS18x20 sensor
#define COUNTER_RESET false // [SetOption79] Enable resetting of counters after telemetry was sent
#define SHUTTER_SUPPORT false // [SetOption80] Enable shutter support
#define PCF8574_INVERT_PORTS false // [SetOption81] Invert all ports on PCF8574 devices
#define ZIGBEE_FRIENDLY_NAMES false // [SetOption83] Enable Zigbee FriendlyNames instead of ShortAddresses when possible
/*********************************************************************************************\
* END OF SECTION 1
*
@ -531,7 +594,7 @@
#define IR_RCV_MIN_UNKNOWN_SIZE 6 // Set the smallest sized "UNKNOWN" message packets we actually care about (default 6, max 255)
// -- Zigbee interface ----------------------------
//#define USE_ZIGBEE // Enable serial communication with Zigbee CC2530 flashed with ZNP
//#define USE_ZIGBEE // Enable serial communication with Zigbee CC2530 flashed with ZNP (+35k code, +3.2k mem)
#define USE_ZIGBEE_PANID 0x1A63 // arbitrary PAN ID for Zigbee network, must be unique in the home
#define USE_ZIGBEE_EXTPANID 0xCCCCCCCCCCCCCCCCL // arbitrary extended PAN ID
#define USE_ZIGBEE_CHANNEL 11 // Zigbee Channel (11-26)

View File

@ -101,9 +101,9 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu
typedef union { // Restricted by MISRA-C Rule 18.4 but so useful...
uint32_t data; // Allow bit manipulation using SetOption
struct { // SetOption82 .. SetOption113
uint32_t alexa_ct_range : 1; // bit 0 (v8.1.0.2) - SetOption82 - Reduced CT range for Alexa
uint32_t spare01 : 1;
uint32_t spare02 : 1;
uint32_t alexa_ct_range : 1; // bit 0 (v8.1.0.2) - SetOption82 - Reduced CT range for Alexa
uint32_t zigbee_use_names : 1; // bit 1 (v8.1.0.4) - SetOption83 - Use FriendlyNames instead of ShortAddresses when possible
uint32_t awsiot_shadow : 1; // bit 2 (v8.1.0.5) - SetOption84 - (AWS IoT) publish MQTT state to a device shadow
uint32_t spare03 : 1;
uint32_t spare04 : 1;
uint32_t spare05 : 1;
@ -467,8 +467,9 @@ struct SYSCFG {
uint8_t hotplug_scan; // F03
uint8_t reserved1; // F04 - reserved for s-hadinger
uint8_t free_f05[215]; // F05
uint8_t free_f05[211]; // F05
int adc_param4; // FD8
uint32_t shutter_button[MAX_KEYS]; // FDC
uint32_t i2c_drivers[3]; // FEC I2cDriver
uint32_t cfg_timestamp; // FF8

View File

@ -700,7 +700,11 @@ void EspErase(uint32_t start_sector, uint32_t end_sector)
// bool result = EsptoolEraseSector(sector); // Esptool - erases flash completely (slow)
if (serial_output) {
#ifdef ARDUINO_ESP8266_RELEASE_2_3_0
Serial.printf(D_LOG_APPLICATION D_ERASED_SECTOR " %d %s\n", sector, (result) ? D_OK : D_ERROR);
#else
Serial.printf_P(PSTR(D_LOG_APPLICATION D_ERASED_SECTOR " %d %s\n"), sector, (result) ? D_OK : D_ERROR);
#endif
delay(10);
} else {
yield();
@ -782,8 +786,13 @@ void SettingsDefaultSet2(void)
{
memset((char*)&Settings +16, 0x00, sizeof(SYSCFG) -16);
// Settings.flag.value_units = 0;
// Settings.flag.stop_flash_rotate = 0;
Settings.flag.stop_flash_rotate = APP_FLASH_CYCLE;
Settings.flag.global_state = APP_ENABLE_LEDLINK;
Settings.flag3.sleep_normal = APP_NORMAL_SLEEP;
Settings.flag3.no_power_feedback = APP_NO_RELAY_SCAN;
Settings.flag3.fast_power_cycle_disable = APP_DISABLE_POWERCYCLE;
Settings.flag3.bootcount_update = DEEPSLEEP_BOOTCOUNT;
Settings.flag3.compatibility_check = OTA_COMPATIBILITY;
Settings.save_data = SAVE_DATA;
Settings.param[P_BACKLOG_DELAY] = MIN_BACKLOG_DELAY;
Settings.param[P_BOOT_LOOP_OFFSET] = BOOT_LOOP_OFFSET; // SetOption36
@ -824,6 +833,8 @@ void SettingsDefaultSet2(void)
Settings.seriallog_level = SERIAL_LOG_LEVEL;
// Wifi
Settings.flag3.use_wifi_scan = WIFI_SCAN_AT_RESTART;
Settings.flag3.use_wifi_rescan = WIFI_SCAN_REGULARLY;
Settings.wifi_output_power = 170;
ParseIp(&Settings.ip_address[0], WIFI_IP_ADDRESS);
ParseIp(&Settings.ip_address[1], WIFI_GATEWAY);
@ -844,16 +855,17 @@ void SettingsDefaultSet2(void)
// Webserver
Settings.flag2.emulation = EMULATION;
Settings.flag3.gui_hostname_ip = GUI_SHOW_HOSTNAME;
Settings.flag3.mdns_enabled = MDNS_ENABLED;
Settings.webserver = WEB_SERVER;
Settings.weblog_level = WEB_LOG_LEVEL;
SettingsUpdateText(SET_WEBPWD, WEB_PASSWORD);
Settings.flag3.mdns_enabled = MDNS_ENABLED;
SettingsUpdateText(SET_CORS, CORS_DOMAIN);
// Button
// Settings.flag.button_restrict = 0;
// Settings.flag.button_swap = 0;
// Settings.flag.button_single = 0;
Settings.flag.button_restrict = KEY_DISABLE_MULTIPRESS;
Settings.flag.button_swap = KEY_SWAP_DOUBLE_PRESS;
Settings.flag.button_single = KEY_ONLY_SINGLE_PRESS;
Settings.param[P_HOLD_TIME] = KEY_HOLD_TIME; // Default 4 seconds hold time
// Switch
@ -861,16 +873,19 @@ void SettingsDefaultSet2(void)
// MQTT
Settings.flag.mqtt_enabled = MQTT_USE;
// Settings.flag.mqtt_response = 0;
Settings.flag.mqtt_response = MQTT_RESULT_COMMAND;
Settings.flag.mqtt_offline = MQTT_LWT_MESSAGE;
Settings.flag.mqtt_power_retain = MQTT_POWER_RETAIN;
Settings.flag.mqtt_button_retain = MQTT_BUTTON_RETAIN;
Settings.flag.mqtt_switch_retain = MQTT_SWITCH_RETAIN;
Settings.flag3.button_switch_force_local = MQTT_BUTTON_SWITCH_FORCE_LOCAL;
Settings.flag3.hass_tele_on_power = TELE_ON_POWER;
// Settings.flag.mqtt_sensor_retain = 0;
// Settings.flag.mqtt_offline = 0;
Settings.flag.mqtt_sensor_retain = MQTT_SENSOR_RETAIN;
// Settings.flag.mqtt_serial = 0;
// Settings.flag.device_index_enable = 0;
Settings.flag.device_index_enable = MQTT_POWER_FORMAT;
Settings.flag3.time_append_timezone = MQTT_APPEND_TIMEZONE;
Settings.flag3.button_switch_force_local = MQTT_BUTTON_SWITCH_FORCE_LOCAL;
Settings.flag3.no_hold_retain = MQTT_NO_HOLD_RETAIN;
Settings.flag3.use_underscore = MQTT_INDEX_SEPARATOR;
Settings.flag3.grouptopic_mode = MQTT_GROUPTOPIC_FORMAT;
SettingsUpdateText(SET_MQTT_HOST, MQTT_HOST);
Settings.mqtt_port = MQTT_PORT;
SettingsUpdateText(SET_MQTT_CLIENT, MQTT_CLIENT_ID);
@ -904,10 +919,13 @@ void SettingsDefaultSet2(void)
Settings.mqttlog_level = MQTT_LOG_LEVEL;
// Energy
Settings.flag.no_power_on_check = ENERGY_VOLTAGE_ALWAYS;
Settings.flag2.current_resolution = 3;
// Settings.flag2.voltage_resolution = 0;
// Settings.flag2.wattage_resolution = 0;
Settings.flag2.energy_resolution = ENERGY_RESOLUTION;
Settings.flag3.dds2382_model = ENERGY_DDS2382_MODE;
Settings.flag3.hardware_energy_total = ENERGY_HARDWARE_TOTALS;
Settings.param[P_MAX_POWER_RETRY] = MAX_POWER_RETRY;
// Settings.energy_power_delta = 0;
Settings.energy_power_calibration = HLW_PREF_PULSE;
@ -937,9 +955,12 @@ void SettingsDefaultSet2(void)
Settings.param[P_OVER_TEMP] = ENERGY_OVERTEMP;
// IRRemote
Settings.flag.ir_receive_decimal = IR_DATA_RADIX;
Settings.flag3.receive_raw = IR_ADD_RAW_DATA;
Settings.param[P_IR_UNKNOW_THRESHOLD] = IR_RCV_MIN_UNKNOWN_SIZE;
// RF Bridge
Settings.flag.rf_receive_decimal = RF_DATA_RADIX;
// for (uint32_t i = 0; i < 17; i++) { Settings.rf_code[i][0] = 0; }
memcpy_P(Settings.rf_code[0], kDefaultRfCode, 9);
@ -960,6 +981,8 @@ void SettingsDefaultSet2(void)
Settings.flag2.pressure_resolution = PRESSURE_RESOLUTION;
Settings.flag2.humidity_resolution = HUMIDITY_RESOLUTION;
Settings.flag2.temperature_resolution = TEMP_RESOLUTION;
Settings.flag3.ds18x20_internal_pullup = DS18X20_PULL_UP;
Settings.flag3.counter_reset_on_tele = COUNTER_RESET;
// Settings.altitude = 0;
// Rules
@ -968,19 +991,28 @@ void SettingsDefaultSet2(void)
// for (uint32_t i = 1; i < MAX_RULE_SETS; i++) { Settings.rules[i][0] = '\0'; }
Settings.flag2.calc_resolution = CALC_RESOLUTION;
// Timer
Settings.flag3.timers_enable = TIMERS_ENABLED;
// Home Assistant
Settings.flag.hass_light = HASS_AS_LIGHT;
Settings.flag.hass_discovery = HOME_ASSISTANT_DISCOVERY_ENABLE;
Settings.flag3.hass_tele_on_power = TELE_ON_POWER;
// Knx
// Settings.flag.knx_enabled = 0;
// Settings.flag.knx_enable_enhancement = 0;
Settings.flag.knx_enabled = KNX_ENABLED;
Settings.flag.knx_enable_enhancement = KNX_ENHANCED;
// Light
Settings.flag.pwm_control = 1;
//Settings.flag.ws_clock_reverse = 0;
//Settings.flag.light_signal = 0;
//Settings.flag.not_power_linked = 0;
//Settings.flag.decimal_text = 0;
Settings.flag.pwm_control = LIGHT_MODE;
Settings.flag.ws_clock_reverse = LIGHT_CLOCK_DIRECTION;
Settings.flag.light_signal = LIGHT_PAIRS_CO2;
Settings.flag.not_power_linked = LIGHT_POWER_CONTROL;
Settings.flag.decimal_text = LIGHT_COLOR_RADIX;
Settings.flag3.pwm_multi_channels = LIGHT_CHANNEL_MODE;
Settings.flag3.slider_dimmer_stay_on = LIGHT_SLIDER_POWER;
Settings.flag4.alexa_ct_range = LIGHT_ALEXA_CT_RANGE;
Settings.pwm_frequency = PWM_FREQ;
Settings.pwm_range = PWM_RANGE;
for (uint32_t i = 0; i < MAX_PWMS; i++) {
@ -1064,6 +1096,15 @@ void SettingsDefaultSet2(void)
memset(&Settings.monitors, 0xFF, 20); // Enable all possible monitors, displays and sensors
SettingsEnableAllI2cDrivers();
// Tuya
Settings.flag3.tuya_apply_o20 = TUYA_SETOPTION_20;
Settings.flag3.tuya_serial_mqtt_publish = MQTT_TUYA_RECEIVED;
Settings.flag3.buzzer_enable = BUZZER_ENABLE;
Settings.flag3.shutter_mode = SHUTTER_SUPPORT;
Settings.flag3.pcf8574_ports_inverted = PCF8574_INVERT_PORTS;
Settings.flag4.zigbee_use_names = ZIGBEE_FRIENDLY_NAMES;
}
/********************************************************************************************/

View File

@ -493,6 +493,17 @@ bool ParseIp(uint32_t* addr, const char* str)
return (3 == i);
}
uint32_t ParseParameters(uint32_t count, uint32_t *params)
{
char *p;
uint32_t i = 0;
for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < count; str = strtok_r(nullptr, ", ", &p)) {
params[i] = strtoul(str, nullptr, 0);
i++;
}
return i;
}
// Function to parse & check if version_str is newer than our currently installed version.
bool NewerVersion(char* version_str)
{
@ -1521,11 +1532,7 @@ bool I2cSetDevice(uint32_t addr)
return false; // If already active report as not present;
}
Wire.beginTransmission((uint8_t)addr);
bool result = (0 == Wire.endTransmission());
if (result) {
I2cSetActive(addr, 1);
}
return result;
return (0 == Wire.endTransmission());
}
#endif // USE_I2C
@ -1631,8 +1638,12 @@ void AddLog(uint32_t loglevel)
if (!web_log_index) web_log_index++; // Index 0 is not allowed as it is the end of char string
}
#endif // USE_WEBSERVER
if (!global_state.mqtt_down && (loglevel <= Settings.mqttlog_level)) { MqttPublishLogging(mxtime); }
if (!global_state.wifi_down && (loglevel <= syslog_level)) { Syslog(); }
if (Settings.flag.mqtt_enabled && // SetOption3 - Enable MQTT
!global_state.mqtt_down &&
(loglevel <= Settings.mqttlog_level)) { MqttPublishLogging(mxtime); }
if (!global_state.wifi_down &&
(loglevel <= syslog_level)) { Syslog(); }
}
void AddLog_P(uint32_t loglevel, const char *formatP)

View File

@ -338,7 +338,8 @@ void CmndStatus(void)
char stemp2[TOPSZ];
// Workaround MQTT - TCP/IP stack queueing when SUB_PREFIX = PUB_PREFIX
if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2)) && (!payload)) { option++; } // TELE
// Commented on 20200118 as it seems to be no longer needed
// if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2)) && (!payload)) { option++; } // TELE
if ((!Settings.flag.mqtt_enabled) && (6 == payload)) { payload = 99; } // SetOption3 - Enable MQTT
if (!energy_flg && (9 == payload)) { payload = 99; }

View File

@ -80,7 +80,7 @@ public:
return _buf->len;
}
size_t add32(const uint32_t data) { // append 32 bits value
if (_buf->len < _buf->size - 3) { // do we have room for 2 bytes
if (_buf->len < _buf->size - 3) { // do we have room for 4 bytes
_buf->buf[_buf->len++] = data;
_buf->buf[_buf->len++] = data >> 8;
_buf->buf[_buf->len++] = data >> 16;
@ -88,6 +88,19 @@ public:
}
return _buf->len;
}
size_t add64(const uint64_t data) { // append 64 bits value
if (_buf->len < _buf->size - 7) { // do we have room for 8 bytes
_buf->buf[_buf->len++] = data;
_buf->buf[_buf->len++] = data >> 8;
_buf->buf[_buf->len++] = data >> 16;
_buf->buf[_buf->len++] = data >> 24;
_buf->buf[_buf->len++] = data >> 32;
_buf->buf[_buf->len++] = data >> 40;
_buf->buf[_buf->len++] = data >> 48;
_buf->buf[_buf->len++] = data >> 56;
}
return _buf->len;
}
size_t addBuffer(const SBuffer &buf2) {
if (len() + buf2.len() <= size()) {
@ -152,6 +165,20 @@ public:
return 0;
}
// if no NULL is found, returns length until the end of the buffer
inline size_t strlen(const size_t offset) const {
return strnlen((const char*) &_buf->buf[offset], len() - offset);
}
size_t strlen_s(const size_t offset) const {
size_t slen = this->strlen(offset);
if (slen == len() - offset) {
return 0; // we didn't find a NULL char
} else {
return slen;
}
}
SBuffer subBuffer(const size_t start, size_t len) const {
if (start >= _buf->len) {
len = 0;

View File

@ -438,9 +438,9 @@ bool SendKey(uint32_t key, uint32_t device, uint32_t state)
#ifdef USE_DOMOTICZ
if (!(DomoticzSendKey(key, device, state, strlen(mqtt_data)))) {
#endif // USE_DOMOTICZ
MqttPublishDirect(stopic, ((key) ? Settings.flag.mqtt_switch_retain // CMND_SWITCHRETAIN
: Settings.flag.mqtt_button_retain) && // CMND_BUTTONRETAIN
(state != POWER_HOLD || !Settings.flag3.no_hold_retain)); // SetOption62 - Don't use retain flag on HOLD messages
MqttPublish(stopic, ((key) ? Settings.flag.mqtt_switch_retain // CMND_SWITCHRETAIN
: Settings.flag.mqtt_button_retain) && // CMND_BUTTONRETAIN
(state != POWER_HOLD || !Settings.flag3.no_hold_retain)); // SetOption62 - Don't use retain flag on HOLD messages
#ifdef USE_DOMOTICZ
}
#endif // USE_DOMOTICZ
@ -692,7 +692,12 @@ void MqttPublishSensor(void)
}
}
/********************************************************************************************/
/*********************************************************************************************\
* State loops
\*********************************************************************************************/
/*-------------------------------------------------------------------------------------------*\
* Every second
\*-------------------------------------------------------------------------------------------*/
void PerformEverySecond(void)
{
@ -715,6 +720,13 @@ void PerformEverySecond(void)
#endif
}
if (mqtt_cmnd_blocked_reset) {
mqtt_cmnd_blocked_reset--;
if (!mqtt_cmnd_blocked_reset) {
mqtt_cmnd_blocked = 0; // Clean up MQTT cmnd loop block
}
}
if (seriallog_timer) {
seriallog_timer--;
if (!seriallog_timer) {
@ -763,9 +775,6 @@ void PerformEverySecond(void)
}
}
/*********************************************************************************************\
* State loops
\*********************************************************************************************/
/*-------------------------------------------------------------------------------------------*\
* Every 0.1 second
\*-------------------------------------------------------------------------------------------*/
@ -822,8 +831,6 @@ void Every250mSeconds(void)
state_250mS++;
state_250mS &= 0x3;
if (mqtt_cmnd_publish) mqtt_cmnd_publish--; // Clean up
if (!Settings.flag.global_state) { // Problem blinkyblinky enabled - SetOption31 - Control link led blinking
if (global_state.data) { // Any problem
if (global_state.mqtt_down) { blinkinterval = 7; } // MQTT problem so blink every 2 seconds (slowest)

View File

@ -22,14 +22,17 @@
\*********************************************************************************************/
#ifndef WIFI_RSSI_THRESHOLD
#define WIFI_RSSI_THRESHOLD 10 // Difference in dB between current network and scanned network
// Decrease the roam threshold from 10 to 5 to address devices connecting at very low RSSI and being close to inoperative
#define WIFI_RSSI_THRESHOLD 5 // Difference in dB between current network and scanned network
#endif
#ifndef WIFI_RESCAN_MINUTES
#define WIFI_RESCAN_MINUTES 44 // Number of minutes between wifi network rescan
// Increase rescan interval from 44 to 5 minutes to improve ability for devices to reach network harmony
#define WIFI_RESCAN_MINUTES 5 // Number of minutes between wifi network rescan
#endif
const uint8_t WIFI_CONFIG_SEC = 180; // seconds before restart
const uint8_t WIFI_CHECK_SEC = 20; // seconds
// Drop from 20 seconds to 5 seconds since we control the reconnections, not the Arduino SDK
const uint8_t WIFI_CHECK_SEC = 5; // seconds
const uint8_t WIFI_RETRY_OFFSET_SEC = 20; // seconds
#include <ESP8266WiFi.h> // Wifi, MQTT, Ota, WifiManager
@ -49,7 +52,9 @@ struct WIFI {
uint8_t config_counter = 0;
uint8_t mdns_begun = 0; // mDNS active
uint8_t scan_state;
uint8_t bssid[6];
uint8_t bssid[6] = {0};
uint8_t bssid_last[6] = {0}; // store the last connect bssid
int8_t best_network_db;
} Wifi;
int WifiGetRssiAsQuality(int rssi)
@ -182,7 +187,9 @@ void WifiBegin(uint8_t flag, uint8_t channel)
// if (WiFi.getPhyMode() != WIFI_PHY_MODE_11N) { WiFi.setPhyMode(WIFI_PHY_MODE_11N); } // B/G/N
// if (WiFi.getPhyMode() != WIFI_PHY_MODE_11G) { WiFi.setPhyMode(WIFI_PHY_MODE_11G); } // B/G
if (!WiFi.getAutoConnect()) { WiFi.setAutoConnect(true); }
// WiFi.setAutoReconnect(true);
// Handle the reconnection in WifiCheckIp() since the autoreconnect keeps sending deauthentication messages which causes the AP to block traffic as it looks like an DoS attack
// This needs to be explicitly called as "false" otherwise the default is enabled
WiFi.setAutoReconnect(false);
switch (flag) {
case 0: // AP1
case 1: // AP2
@ -198,13 +205,18 @@ void WifiBegin(uint8_t flag, uint8_t channel)
WiFi.config(Settings.ip_address[0], Settings.ip_address[1], Settings.ip_address[2], Settings.ip_address[3]); // Set static IP
}
WiFi.hostname(my_hostname);
char stemp[40] = { 0 };
if (channel) {
WiFi.begin(SettingsText(SET_STASSID1 + Settings.sta_active), SettingsText(SET_STAPWD1 + Settings.sta_active), channel, Wifi.bssid);
// Add connected BSSID and channel for multi-AP installations
char hex_char[18];
snprintf_P(stemp, sizeof(stemp), PSTR(" Channel %d BSSId %s"), channel, ToHex_P((unsigned char*)Wifi.bssid, 6, hex_char, sizeof(hex_char), ':'));
} else {
WiFi.begin(SettingsText(SET_STASSID1 + Settings.sta_active), SettingsText(SET_STAPWD1 + Settings.sta_active));
}
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTING_TO_AP "%d %s " D_IN_MODE " 11%c " D_AS " %s..."),
Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), kWifiPhyMode[WiFi.getPhyMode() & 0x3], my_hostname);
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTING_TO_AP "%d %s%s " D_IN_MODE " 11%c " D_AS " %s..."),
Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), stemp, kWifiPhyMode[WiFi.getPhyMode() & 0x3], my_hostname);
#if LWIP_IPV6
for (bool configured = false; !configured;) {
@ -223,22 +235,22 @@ void WifiBegin(uint8_t flag, uint8_t channel)
void WifiBeginAfterScan(void)
{
static int8_t best_network_db;
// Not active
if (0 == Wifi.scan_state) { return; }
// Init scan when not connected
if (1 == Wifi.scan_state) {
memset((void*) &Wifi.bssid, 0, sizeof(Wifi.bssid));
best_network_db = -127;
Wifi.best_network_db = -127;
Wifi.scan_state = 3;
}
// Init scan when connected
if (2 == Wifi.scan_state) {
uint8_t* bssid = WiFi.BSSID(); // Get current bssid
memcpy((void*) &Wifi.bssid, (void*) bssid, sizeof(Wifi.bssid));
best_network_db = WiFi.RSSI(); // Get current rssi and add threshold
if (best_network_db < -WIFI_RSSI_THRESHOLD) { best_network_db += WIFI_RSSI_THRESHOLD; }
Wifi.best_network_db = WiFi.RSSI(); // Get current rssi and add threshold
if (Wifi.best_network_db < -WIFI_RSSI_THRESHOLD) {
Wifi.best_network_db += WIFI_RSSI_THRESHOLD;
}
Wifi.scan_state = 3;
}
// Init scan
@ -259,6 +271,12 @@ void WifiBeginAfterScan(void)
}
// Scan done
if (5 == Wifi.scan_state) {
uint32_t number_known = 0; // count the number of known AP's so we can clear the Wifi.bssid_last if there is only one
int32_t channel_max = 0; // No scan result
int8_t ap_max = 3; // AP default if not found
uint8_t bssid_max[6]; // Save last bssid
memcpy((void*) &bssid_max, (void*) &Wifi.bssid, sizeof(bssid_max)); // store the strongest bssid
int32_t channel = 0; // No scan result
int8_t ap = 3; // AP default if not found
uint8_t last_bssid[6]; // Save last bssid
@ -282,12 +300,25 @@ void WifiBeginAfterScan(void)
for (j = 0; j < MAX_SSIDS; j++) {
if (ssid_scan == SettingsText(SET_STASSID1 + j)) { // SSID match
known = true;
if (rssi_scan > best_network_db) { // Best network
number_known++;
if (rssi_scan > Wifi.best_network_db) { // Best network
if (sec_scan == ENC_TYPE_NONE || SettingsText(SET_STAPWD1 + j)) { // Check for passphrase if not open wlan
best_network_db = (int8_t)rssi_scan;
channel = chan_scan;
ap = j; // AP1 or AP2
memcpy((void*) &Wifi.bssid, (void*) bssid_scan, sizeof(Wifi.bssid));
// store the max values in case there is only one AP and we need to try to reconnect
memcpy((void*) &bssid_max, (void*) bssid_scan, sizeof(bssid_max));
channel_max = chan_scan;
ap_max = j;
// if the bssid is not the same as the last failed attempt, force picking the next strongest AP to prevent getting stuck on a strong RSSI, but poor channel health
for (uint32_t i = 0; i < sizeof(Wifi.bssid_last); i++) {
if (bssid_scan[i] != Wifi.bssid_last[i]) {
Wifi.best_network_db = (int8_t)rssi_scan;
channel = chan_scan;
ap = j; // AP1 or AP2
memcpy((void*) &Wifi.bssid, (void*) bssid_scan, sizeof(Wifi.bssid));
// save the last bssid used
memcpy((void*) &Wifi.bssid_last, (void*) bssid_scan, sizeof(Wifi.bssid_last));
break;
}
}
}
}
break;
@ -307,6 +338,16 @@ void WifiBeginAfterScan(void)
WiFi.scanDelete(); // Clean up Ram
delay(0);
}
// reset the last bssid if there is only one AP to allow the reconnect of the same AP on the next cycle
if (number_known == 1) {
// clear the last value
memset((void*) &Wifi.bssid_last, 0, sizeof(Wifi.bssid_last));
memcpy((void*) &Wifi.bssid, (void*) bssid_max, sizeof(Wifi.bssid));
channel = channel_max;
ap = ap_max;
}
Wifi.scan_state = 0;
// If bssid changed then (re)connect wifi
for (uint32_t i = 0; i < sizeof(Wifi.bssid); i++) {
@ -381,17 +422,11 @@ void WifiCheckIp(void)
#else
if ((WL_CONNECTED == WiFi.status()) && (static_cast<uint32_t>(WiFi.localIP()) != 0)) {
#endif // LWIP_IPV6=1
// initialize the last connect bssid since we had a successful connection
memset((void*) &Wifi.bssid_last, 0, sizeof(Wifi.bssid_last));
WifiSetState(1);
Wifi.counter = WIFI_CHECK_SEC;
Wifi.retry = Wifi.retry_init;
AddLog_P((Wifi.status != WL_CONNECTED) ? LOG_LEVEL_INFO : LOG_LEVEL_DEBUG_MORE, S_LOG_WIFI, PSTR(D_CONNECTED));
if (Wifi.status != WL_CONNECTED) {
// AddLog_P(LOG_LEVEL_INFO, PSTR("Wifi: Set IP addresses"));
Settings.ip_address[1] = (uint32_t)WiFi.gatewayIP();
Settings.ip_address[2] = (uint32_t)WiFi.subnetMask();
Settings.ip_address[3] = (uint32_t)WiFi.dnsIP();
}
Wifi.status = WL_CONNECTED;
#ifdef USE_DISCOVERY
#ifdef WEBSERVER_ADVERTISE
if (2 == Wifi.mdns_begun) {
@ -407,32 +442,18 @@ void WifiCheckIp(void)
switch (Wifi.status) {
case WL_CONNECTED:
AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_NO_IP_ADDRESS));
Wifi.status = 0;
Wifi.retry = Wifi.retry_init;
// if poor channel health prevents DHCP broadcast from succeeding, restart the request
// The code will eventually do a recoonect when the 1/2 interval is hit to try another access point if this remains unsuccessful
wifi_station_dhcpc_start();
break;
case WL_NO_SSID_AVAIL:
AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_NOT_REACHED));
if (WIFI_WAIT == Settings.sta_config) {
Wifi.retry = Wifi.retry_init;
} else {
if (Wifi.retry > (Wifi.retry_init / 2)) {
Wifi.retry = Wifi.retry_init / 2;
}
else if (Wifi.retry) {
Wifi.retry = 0;
}
}
break;
case WL_CONNECT_FAILED:
AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_WRONG_PASSWORD));
if (Wifi.retry > (Wifi.retry_init / 2)) {
Wifi.retry = Wifi.retry_init / 2;
}
else if (Wifi.retry) {
Wifi.retry = 0;
}
break;
default: // WL_IDLE_STATUS and WL_DISCONNECTED
// log on the 1/2 or full interval
if (!Wifi.retry || ((Wifi.retry_init / 2) == Wifi.retry)) {
AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_TIMEOUT));
} else {
@ -444,9 +465,11 @@ void WifiCheckIp(void)
}
}
}
if (Wifi.retry) {
if (Settings.flag3.use_wifi_scan) { // SetOption56 - Scan wifi network at restart for configured AP's
if (Wifi.retry_init == Wifi.retry) {
// check the 1/2 interval as well when rescanning - scan state machine takes 4 seconds
if ((Wifi.retry_init == Wifi.retry) || ((Wifi.retry_init / 2) == Wifi.retry)){
Wifi.scan_state = 1; // Select scanned SSID
}
} else {
@ -593,7 +616,6 @@ String WifiGetOutputPower(void)
dtostrfd((float)(Settings.wifi_output_power) / 10, 1, stemp1);
return String(stemp1);
}
void WifiSetOutputPower(void)
{
WiFi.setOutputPower((float)(Settings.wifi_output_power) / 10);
@ -605,7 +627,9 @@ void WifiConnect(void)
WifiSetOutputPower();
WiFi.persistent(false); // Solve possible wifi init errors
Wifi.status = 0;
Wifi.retry_init = WIFI_RETRY_OFFSET_SEC + ((ESP.getChipId() & 0xF) * 2);
// lower the rety times now Tasmota control the reconnections, not the Arduino SDK
// Wifi.retry_init = WIFI_RETRY_OFFSET_SEC + ((ESP.getChipId() & 0xF) * 2);
Wifi.retry_init = WIFI_RETRY_OFFSET_SEC + (ESP.getChipId() & 0xF);
Wifi.retry = Wifi.retry_init;
Wifi.counter = 1;
}

View File

@ -31,6 +31,7 @@
\*********************************************************************************************/
#define CODE_IMAGE 0
#define CODE_IMAGE_STR "tasmota"
#define USE_LIGHT // Enable light control
#define USE_ENERGY_SENSOR // Use energy sensors (+14k code)

View File

@ -68,8 +68,6 @@
// Structs
#include "settings.h"
const char kCodeImage[] PROGMEM = "tasmota|minimal|sensors|knx|lite|display|ir";
/*********************************************************************************************\
* Global variables
\*********************************************************************************************/
@ -110,12 +108,13 @@ float global_temperature = 9999; // Provide a global temperature to b
float global_humidity = 0; // Provide a global humidity to be used by some sensors
float global_pressure = 0; // Provide a global pressure to be used by some sensors
uint16_t tele_period = 9999; // Tele period timer
uint16_t mqtt_cmnd_publish = 0; // ignore flag for publish command
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
int16_t save_data_counter; // Counter and flag for config save to Flash
RulesBitfield rules_flag; // Rule state flags (16 bits)
uint8_t mqtt_cmnd_blocked = 0; // Ignore flag for publish command
uint8_t mqtt_cmnd_blocked_reset = 0; // Count down to reset if needed
uint8_t state_250mS = 0; // State 250msecond per second flag
uint8_t latching_relay_pulse = 0; // Latching relay pulse timer
uint8_t sleep; // Current copy of Settings.sleep
@ -199,8 +198,8 @@ void setup(void)
if (VERSION & 0xff) { // Development or patched version 6.3.0.10
snprintf_P(my_version, sizeof(my_version), PSTR("%s.%d"), my_version, VERSION & 0xff);
}
char code_image[20];
snprintf_P(my_image, sizeof(my_image), PSTR("(%s)"), GetTextIndexed(code_image, sizeof(code_image), CODE_IMAGE, kCodeImage));
// Thehackbox inserts "release" or "commit number" before compiling using sed -i -e 's/PSTR("(%s)")/PSTR("(85cff52-%s)")/g' tasmota.ino
snprintf_P(my_image, sizeof(my_image), PSTR("(%s)"), CODE_IMAGE_STR); // Results in (85cff52-tasmota) or (release-tasmota)
SettingsLoad();
SettingsDelta();

View File

@ -79,6 +79,8 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
#undef CODE_IMAGE
#define CODE_IMAGE 2
#undef CODE_IMAGE_STR
#define CODE_IMAGE_STR "sensors"
#undef USE_DISCOVERY // Disable mDNS (+8k code or +23.5k code with core 2_5_x, +0.3k mem)
@ -228,6 +230,8 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
#undef CODE_IMAGE
#define CODE_IMAGE 3
#undef CODE_IMAGE_STR
#define CODE_IMAGE_STR "knx"
#ifndef USE_KNX
#define USE_KNX // Enable KNX IP Protocol Support (+23k code, +3k3 mem)
@ -248,6 +252,8 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
#undef CODE_IMAGE
#define CODE_IMAGE 5
#undef CODE_IMAGE_STR
#define CODE_IMAGE_STR "display"
#undef USE_EMULATION // Disable Belkin WeMo and Hue Bridge emulation for Alexa (-16k code, -2k mem)
#undef USE_EMULATION_HUE // Disable Hue Bridge emulation for Alexa (+14k code, +2k mem common)
@ -313,6 +319,8 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
#undef CODE_IMAGE
#define CODE_IMAGE 6
#undef CODE_IMAGE_STR
#define CODE_IMAGE_STR "ir"
#undef USE_EMULATION
#undef USE_EMULATION_HUE // Disable Hue emulation - only for lights and relays
@ -406,6 +414,8 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
#undef CODE_IMAGE
#define CODE_IMAGE 4
#undef CODE_IMAGE_STR
#define CODE_IMAGE_STR "lite"
#undef APP_SLEEP
#define APP_SLEEP 1 // Default to sleep = 1 for FIRMWARE_LITE
@ -511,6 +521,8 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
#undef CODE_IMAGE
#define CODE_IMAGE 1
#undef CODE_IMAGE_STR
#define CODE_IMAGE_STR "minimal"
#undef FIRMWARE_LITE // Disable tasmota-lite with no sensors
#undef FIRMWARE_SENSORS // Disable tasmota-sensors with useful sensors enabled

View File

@ -169,7 +169,7 @@ enum UserSelectablePins {
GPIO_SM16716_SEL, // SM16716 SELECT
GPIO_DI, // my92x1 PWM input
GPIO_DCKI, // my92x1 CLK input
GPIO_CSE7766_TX, // CSE7766 Serial interface (S31 and Pow R2)
GPIO_CSE7766_TX, // CSE7766 Serial interface (S31 and Pow R2) - Not used anymore 20200121
GPIO_CSE7766_RX, // CSE7766 Serial interface (S31 and Pow R2)
GPIO_ARIRFRCV, // AriLux RF Receive input
GPIO_TXD, // Serial interface
@ -311,7 +311,7 @@ enum UserSelectableAdc0 {
ADC0_LIGHT, // Light sensor
ADC0_BUTTON, // Button
ADC0_BUTTON_INV,
ADC0_MOIST, // Moisture
ADC0_RANGE, // Range
ADC0_CT_POWER, // Current
// ADC0_SWITCH, // Switch
// ADC0_SWITCH_INV,
@ -328,7 +328,7 @@ const char kAdc0Names[] PROGMEM =
D_SENSOR_NONE "|" D_ANALOG_INPUT "|"
D_TEMPERATURE "|" D_LIGHT "|"
D_SENSOR_BUTTON "|" D_SENSOR_BUTTON "i|"
D_MOISTURE "|"
D_RANGE "|"
D_CT_POWER "|"
// D_SENSOR_SWITCH "|" D_SENSOR_SWITCH "i|"
;

View File

@ -20,7 +20,7 @@
#ifndef _TASMOTA_VERSION_H_
#define _TASMOTA_VERSION_H_
const uint32_t VERSION = 0x08010004;
const uint32_t VERSION = 0x08010005;
// Lowest compatible version
const uint32_t VERSION_COMPATIBLE = 0x07010006;

View File

@ -221,6 +221,15 @@ void MqttUnsubscribeLib(const char *topic)
bool MqttPublishLib(const char* topic, bool retained)
{
// If Prefix1 equals Prefix2 disable next MQTT subscription to prevent loop
if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) {
char *str = strstr(topic, SettingsText(SET_MQTTPREFIX1));
if (str == topic) {
mqtt_cmnd_blocked_reset = 4; // Allow up to four seconds before resetting residual cmnd blocks
mqtt_cmnd_blocked++;
}
}
bool result = MqttClient.publish(topic, mqtt_data, retained);
yield(); // #3313
return result;
@ -238,12 +247,8 @@ void MqttDataHandler(char* mqtt_topic, uint8_t* mqtt_data, unsigned int data_len
// Do not execute multiple times if Prefix1 equals Prefix2
if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) {
char *str = strstr(mqtt_topic, SettingsText(SET_MQTTPREFIX1));
if ((str == mqtt_topic) && mqtt_cmnd_publish) {
if (mqtt_cmnd_publish > 3) {
mqtt_cmnd_publish -= 3;
} else {
mqtt_cmnd_publish = 0;
}
if ((str == mqtt_topic) && mqtt_cmnd_blocked) {
mqtt_cmnd_blocked--;
return;
}
}
@ -291,52 +296,41 @@ void MqttUnsubscribe(const char *topic)
void MqttPublishLogging(const char *mxtime)
{
if (Settings.flag.mqtt_enabled) { // SetOption3 - Enable MQTT
if (MqttIsConnected()) {
char saved_mqtt_data[strlen(mqtt_data) +1];
memcpy(saved_mqtt_data, mqtt_data, sizeof(saved_mqtt_data));
char saved_mqtt_data[MESSZ];
memcpy(saved_mqtt_data, mqtt_data, sizeof(saved_mqtt_data));
// ResponseTime_P(PSTR(",\"Log\":{\"%s\"}}"), log_data); // Will fail as some messages contain JSON
Response_P(PSTR("%s%s"), mxtime, log_data); // No JSON and ugly!!
// ResponseTime_P(PSTR(",\"Log\":{\"%s\"}}"), log_data); // Will fail as some messages contain JSON
Response_P(PSTR("%s%s"), mxtime, log_data); // No JSON and ugly!!
char stopic[TOPSZ];
GetTopic_P(stopic, STAT, mqtt_topic, PSTR("LOGGING"));
MqttPublishLib(stopic, false);
char romram[33];
char stopic[TOPSZ];
snprintf_P(romram, sizeof(romram), PSTR("LOGGING"));
GetTopic_P(stopic, STAT, mqtt_topic, romram);
char *me;
if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) {
me = strstr(stopic, SettingsText(SET_MQTTPREFIX1));
if (me == stopic) {
mqtt_cmnd_publish += 3;
}
}
MqttPublishLib(stopic, false);
memcpy(mqtt_data, saved_mqtt_data, sizeof(saved_mqtt_data));
}
}
memcpy(mqtt_data, saved_mqtt_data, sizeof(saved_mqtt_data));
}
void MqttPublishDirect(const char* topic, bool retained)
void MqttPublish(const char* topic, bool retained)
{
char sretained[CMDSZ];
char slog_type[20];
#ifdef USE_DEBUG_DRIVER
ShowFreeMem(PSTR("MqttPublishDirect"));
ShowFreeMem(PSTR("MqttPublish"));
#endif
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
// if (retained) {
// AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR("Retained are not supported by AWS IoT, using retained = false."));
// }
retained = false; // AWS IoT does not support retained, it will disconnect if received
#endif
char sretained[CMDSZ];
sretained[0] = '\0';
char slog_type[20];
snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_RESULT));
if (Settings.flag.mqtt_enabled) { // SetOption3 - Enable MQTT
if (MqttIsConnected()) {
if (MqttPublishLib(topic, retained)) {
snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_MQTT));
if (retained) {
snprintf_P(sretained, sizeof(sretained), PSTR(" (" D_RETAINED ")"));
}
if (MqttPublishLib(topic, retained)) {
snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_MQTT));
if (retained) {
snprintf_P(sretained, sizeof(sretained), PSTR(" (" D_RETAINED ")"));
}
}
}
@ -354,25 +348,6 @@ void MqttPublishDirect(const char* topic, bool retained)
}
}
void MqttPublish(const char* topic, bool retained)
{
char *me;
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
if (retained) {
AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR("Retained are not supported by AWS IoT, using retained = false."));
}
retained = false; // AWS IoT does not support retained, it will disconnect if received
#endif
if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) {
me = strstr(topic, SettingsText(SET_MQTTPREFIX1));
if (me == topic) {
mqtt_cmnd_publish += 3;
}
}
MqttPublishDirect(topic, retained);
}
void MqttPublish(const char* topic)
{
MqttPublish(topic, false);
@ -387,7 +362,7 @@ void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retain
* prefix 5 = stat using subtopic or RESULT
* prefix 6 = tele using subtopic or RESULT
*/
char romram[33];
char romram[64];
char stopic[TOPSZ];
snprintf_P(romram, sizeof(romram), ((prefix > 3) && !Settings.flag.mqtt_response) ? S_RSLT_RESULT : subtopic); // SetOption4 - Switch between MQTT RESULT or COMMAND
@ -397,6 +372,36 @@ void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retain
prefix &= 3;
GetTopic_P(stopic, prefix, mqtt_topic, romram);
MqttPublish(stopic, retained);
#ifdef USE_MQTT_AWS_IOT
if ((prefix > 0) && (Settings.flag4.awsiot_shadow)) { // placeholder for SetOptionXX
// compute the target topic
char *topic = SettingsText(SET_MQTT_TOPIC);
char topic2[strlen(topic)+1]; // save buffer onto stack
strcpy(topic2, topic);
// replace any '/' with '_'
char *s = topic2;
while (*s) {
if ('/' == *s) {
*s = '_';
}
s++;
}
// update topic is "$aws/things/<topic>/shadow/update"
snprintf_P(romram, sizeof(romram), PSTR("$aws/things/%s/shadow/update"), topic2);
// copy buffer
char *mqtt_save = (char*) malloc(strlen(mqtt_data)+1);
if (!mqtt_save) { return; } // abort
strcpy(mqtt_save, mqtt_data);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"state\":{\"reported\":%s}}"), mqtt_save);
free(mqtt_save);
bool result = MqttClient.publish(romram, mqtt_data, false);
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "Updated shadow: %s"), romram);
yield(); // #3313
}
#endif // USE_MQTT_AWS_IOT
}
void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic)
@ -692,11 +697,6 @@ void MqttCheck(void)
if (!MqttIsConnected()) {
global_state.mqtt_down = 1;
if (!Mqtt.retry_counter) {
#ifdef USE_DISCOVERY
#ifdef MQTT_HOST_DISCOVERY
if (!strlen(SettingsText(SET_MQTT_HOST)) && !Wifi.mdns_begun) { return; }
#endif // MQTT_HOST_DISCOVERY
#endif // USE_DISCOVERY
MqttReconnect();
} else {
Mqtt.retry_counter--;
@ -706,7 +706,9 @@ void MqttCheck(void)
}
} else {
global_state.mqtt_down = 0;
if (Mqtt.initial_connection_state) MqttReconnect();
if (Mqtt.initial_connection_state) {
MqttReconnect();
}
}
}
@ -850,17 +852,17 @@ void CmndPrefix(void)
void CmndPublish(void)
{
if (XdrvMailbox.data_len > 0) {
char *mqtt_part = strtok(XdrvMailbox.data, " ");
char *payload_part;
char *mqtt_part = strtok_r(XdrvMailbox.data, " ", &payload_part);
if (mqtt_part) {
char stemp1[TOPSZ];
strlcpy(stemp1, mqtt_part, sizeof(stemp1));
mqtt_part = strtok(nullptr, " ");
if (mqtt_part) {
strlcpy(mqtt_data, mqtt_part, sizeof(mqtt_data));
if ((payload_part != nullptr) && strlen(payload_part)) {
strlcpy(mqtt_data, payload_part, sizeof(mqtt_data));
} else {
mqtt_data[0] = '\0';
}
MqttPublishDirect(stemp1, (XdrvMailbox.index == 2));
MqttPublish(stemp1, (XdrvMailbox.index == 2));
// ResponseCmndDone();
mqtt_data[0] = '\0';
}
@ -986,8 +988,10 @@ void CmndSensorRetain(void)
\*********************************************************************************************/
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
const static uint16_t tls_spi_start_sector = SPIFFS_END + 4; // 0xXXFF
const static uint8_t* tls_spi_start = (uint8_t*) ((tls_spi_start_sector * SPI_FLASH_SEC_SIZE) + 0x40200000); // 0x40XFF000
// const static uint16_t tls_spi_start_sector = SPIFFS_END + 4; // 0xXXFF
// const static uint8_t* tls_spi_start = (uint8_t*) ((tls_spi_start_sector * SPI_FLASH_SEC_SIZE) + 0x40200000); // 0x40XFF000
const static uint16_t tls_spi_start_sector = 0xFF; // Force last bank of first MB
const static uint8_t* tls_spi_start = (uint8_t*) 0x402FF000; // 0x402FF000
const static size_t tls_spi_len = 0x1000; // 4kb blocs
const static size_t tls_block_offset = 0x0400;
const static size_t tls_block_len = 0x0400; // 1kb
@ -1144,8 +1148,12 @@ void CmndTlsDump(void) {
uint32_t start = (uint32_t)tls_spi_start + tls_block_offset;
uint32_t end = start + tls_block_len -1;
for (uint32_t pos = start; pos < end; pos += 0x10) {
uint32_t* values = (uint32_t*)(pos);
Serial.printf_P(PSTR("%08x: %08x %08x %08x %08x\n"), pos, bswap32(values[0]), bswap32(values[1]), bswap32(values[2]), bswap32(values[3]));
uint32_t* values = (uint32_t*)(pos);
#ifdef ARDUINO_ESP8266_RELEASE_2_3_0
Serial.printf("%08x: %08x %08x %08x %08x\n", pos, bswap32(values[0]), bswap32(values[1]), bswap32(values[2]), bswap32(values[3]));
#else
Serial.printf_P(PSTR("%08x: %08x %08x %08x %08x\n"), pos, bswap32(values[0]), bswap32(values[1]), bswap32(values[2]), bswap32(values[3]));
#endif
}
}
#endif // DEBUG_DUMP_TLS

View File

@ -522,26 +522,19 @@ void CmndEnergyReset(void)
}
}
else if ((XdrvMailbox.index > 3) && (XdrvMailbox.index <= 5)) {
char *p;
char *str = strtok_r(XdrvMailbox.data, ", ", &p);
int32_t position = -1;
uint32_t values[2];
while ((str != nullptr) && (position < 1)) {
uint32_t value = strtoul(str, nullptr, 10);
position++;
values[position] = value *100;
str = strtok_r(nullptr, ", ", &p);
}
uint32_t values[2] = { 0 };
uint32_t position = ParseParameters(2, values);
values[0] *= 100;
values[1] *= 100;
switch (XdrvMailbox.index)
{
case 4:
// Reset energy_usage.usage totals
if (position > -1) {
if (position > 0) {
RtcSettings.energy_usage.usage1_kWhtotal = values[0];
}
if (position > 0) {
if (position > 1) {
RtcSettings.energy_usage.usage2_kWhtotal = values[1];
}
Settings.energy_usage.usage1_kWhtotal = RtcSettings.energy_usage.usage1_kWhtotal;
@ -549,10 +542,10 @@ void CmndEnergyReset(void)
break;
case 5:
// Reset energy_usage.return totals
if (position > -1) {
if (position > 0) {
RtcSettings.energy_usage.return1_kWhtotal = values[0];
}
if (position > 0) {
if (position > 1) {
RtcSettings.energy_usage.return2_kWhtotal = values[1];
}
Settings.energy_usage.return1_kWhtotal = RtcSettings.energy_usage.return1_kWhtotal;

View File

@ -1723,8 +1723,16 @@ void LightAnimate(void)
Light.new_color[i] = Light.current_color[i];
}
} else {
/*
Response_P(PSTR("{\"" D_CMND_WAKEUP "\":\"" D_JSON_DONE "\"}"));
MqttPublishPrefixTopic_P(TELE, PSTR(D_CMND_WAKEUP));
*/
Response_P(PSTR("{\"" D_CMND_WAKEUP "\":\"" D_JSON_DONE "\""));
LightState(1);
ResponseJsonEnd();
MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_WAKEUP));
XdrvRulesProcess();
Light.wakeup_active = 0;
Settings.light_scheme = LS_POWER;
}
@ -1799,6 +1807,14 @@ void LightAnimate(void)
}
}
// Apply RGBWWTable only if Settings.rgbwwTable[4] != 0
if (0 != Settings.rgbwwTable[4]) {
for (uint32_t i = 0; i<Light.subtype; i++) {
uint32_t adjust = change8to10(Settings.rgbwwTable[i]);
cur_col_10[i] = changeUIntScale(cur_col_10[i], 0, 1023, 0, adjust);
}
}
// final adjusments for PMW, post-gamma correction
for (uint32_t i = 0; i < LST_MAX; i++) {
// scale from 0..1023 to 0..pwm_range, but keep any non-zero value to at least 1
@ -2185,6 +2201,13 @@ void CmndSupportColor(void)
void CmndColor(void)
{
// Color - Show current RGBWW color state
// Color1 - Change color to RGBWW
// Color2 - Change color to RGBWW but retain brightness (=dimmer)
// Color3 - Change color to RGB of WS2812 Clock Second
// Color4 - Change color to RGB of WS2812 Clock Minute
// Color5 - Change color to RGB of WS2812 Clock Hour
// Color6 - Change color to RGB of WS2812 Clock Marker
if ((Light.subtype > LST_SINGLE) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 6)) {
CmndSupportColor();
}
@ -2192,6 +2215,8 @@ void CmndColor(void)
void CmndWhite(void)
{
// White - Show current White (=Dimmer2) state
// White 0..100 - Set White colors dimmer state
if (Light.pwm_multi_channels) { return; }
if ( ((Light.subtype >= LST_RGBW) || (LST_COLDWARM == Light.subtype)) && (XdrvMailbox.index == 1)) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) {
@ -2205,6 +2230,10 @@ void CmndWhite(void)
void CmndChannel(void)
{
// Channel<x> - Show current Channel state
// Channel<x> 0..100 - Set Channel dimmer state
// Channel<x> + - Incerement Channel in steps of 10
// Channel<x> - - Decrement Channel in steps of 10
if ((XdrvMailbox.index >= Light.device) && (XdrvMailbox.index < Light.device + Light.subtype )) {
uint32_t light_index = XdrvMailbox.index - Light.device;
power_t coldim = 0; // bit flag to update
@ -2249,48 +2278,35 @@ void CmndChannel(void)
void CmndHsbColor(void)
{
// HsbColor - Show current HSB
// HsbColor 360,100,100 - Set Hue, Saturation and Brighthness
// HsbColor 360,100 - Set Hue and Saturation
// HsbColor 360 - Set Hue
// HsbColor1 360 - Set Hue
// HsbColor2 100 - Set Saturation
// HsbColor3 100 - Set Brightness
if (Light.subtype >= LST_RGB) {
bool validHSB = (XdrvMailbox.data_len > 0);
if (validHSB) {
uint16_t HSB[3];
if (XdrvMailbox.data_len > 0) {
uint16_t c_hue;
uint8_t c_sat;
light_state.getHSB(&c_hue, &c_sat, nullptr);
uint32_t HSB[3];
HSB[0] = c_hue;
HSB[1] = c_sat;
HSB[2] = light_state.getBriRGB();
char *substr = strstr(XdrvMailbox.data, ",");
if (substr != nullptr) { // Command with comma separated parameters, Hue (0<H<360), Saturation (0<S<100) AND optional Brightness (0<B<100)
HSB[0] = atoi(XdrvMailbox.data);
for (uint32_t i = 1; i < 3; i++) {
substr++;
HSB[i] = atoi(substr);
if ((2 == XdrvMailbox.index) || (3 == XdrvMailbox.index)) {
if ((uint32_t)XdrvMailbox.payload > 100) { XdrvMailbox.payload = 100; }
HSB[XdrvMailbox.index-1] = changeUIntScale(XdrvMailbox.payload, 0, 100, 0, 255);
} else {
uint32_t paramcount = ParseParameters(3, HSB);
if (HSB[0] > 360) { HSB[0] = 360; }
for (uint32_t i = 1; i < paramcount; i++) {
if (HSB[i] > 100) { HSB[i] == 100; }
HSB[i] = changeUIntScale(HSB[i], 0, 100, 0, 255); // change sat and bri to 0..255
substr = strstr(substr, ",");
if (substr == nullptr) {
break;
}
}
if (substr != nullptr) {
validHSB = false;
}
} else { // Command with only 1 parameter, Hue (0<H<360), Saturation (0<S<100) OR Brightness (0<B<100)
if (1 == XdrvMailbox.index) {
HSB[0] = XdrvMailbox.payload;
} else if ((XdrvMailbox.index > 1) && (XdrvMailbox.index < 4)) {
HSB[XdrvMailbox.index-1] = changeUIntScale(XdrvMailbox.payload,0,100,0,255);
} else {
validHSB = false;
}
}
if (validHSB) {
light_controller.changeHSB(HSB[0], HSB[1], HSB[2]);
LightPreparePower(1);
}
light_controller.changeHSB(HSB[0], HSB[1], HSB[2]);
LightPreparePower(1);
} else {
LightState(0);
}
@ -2299,6 +2315,11 @@ void CmndHsbColor(void)
void CmndScheme(void)
{
// Scheme 0..12 - Select one of schemes 0 to 12
// Scheme 2 - Select scheme 2
// Scheme 2,0 - Select scheme 2 with color wheel set to 0 (HSB Red)
// Scheme + - Select next scheme
// Scheme - - Select previous scheme
if (Light.subtype >= LST_RGB) {
uint32_t max_scheme = Light.max_scheme;
@ -2311,6 +2332,10 @@ void CmndScheme(void)
}
}
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= max_scheme)) {
uint32_t parm[2];
if (ParseParameters(2, parm) > 1) {
Light.wheel = parm[1];
}
Settings.light_scheme = XdrvMailbox.payload;
if (LS_WAKEUP == Settings.light_scheme) {
Light.wakeup_active = 3;
@ -2328,6 +2353,8 @@ void CmndScheme(void)
void CmndWakeup(void)
{
// Wakeup - Start wakeup light
// Wakeup 0..100 - Start wakeup light to dimmer value 0..100
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) {
light_controller.changeDimmer(XdrvMailbox.payload);
}
@ -2339,6 +2366,10 @@ void CmndWakeup(void)
void CmndColorTemperature(void)
{
// CT - Show current color temperature
// CT 153..500 - Set color temperature
// CT + - Incerement color temperature in steps of 34
// CT - - Decrement color temperature in steps of 34
if (Light.pwm_multi_channels) { return; }
if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { // ColorTemp
uint32_t ct = light_state.getCT();
@ -2361,6 +2392,12 @@ void CmndColorTemperature(void)
void CmndDimmer(void)
{
// Dimmer - Show current Dimmer state
// Dimmer0 0..100 - Change both RGB and W(W) Dimmers
// Dimmer1 0..100 - Change RGB Dimmer
// Dimmer2 0..100 - Change W(W) Dimmer
// Dimmer<x> + - Incerement Dimmer in steps of 10
// Dimmer<x> - - Decrement Dimmer in steps of 10
uint32_t dimmer;
if (XdrvMailbox.index > 2) { XdrvMailbox.index = 1; }
@ -2402,17 +2439,13 @@ void CmndDimmer(void)
void CmndDimmerRange(void)
{
// DimmerRange - Show current dimmer range as used by Tuya and PS16DZ Dimmers
// DimmerRange 0,100 - Set dimmer hardware range from 0 to 100 and restart
if (XdrvMailbox.data_len > 0) {
char *p;
uint8_t i = 0;
uint16_t parm[2];
uint32_t parm[2];
parm[0] = Settings.dimmer_hw_min;
parm[1] = Settings.dimmer_hw_max;
for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 2; str = strtok_r(nullptr, ", ", &p)) {
parm[i] = strtoul(str, nullptr, 0);
i++;
}
ParseParameters(2, parm);
if (parm[0] < parm[1]) {
Settings.dimmer_hw_min = parm[0];
Settings.dimmer_hw_max = parm[1];
@ -2427,6 +2460,10 @@ void CmndDimmerRange(void)
void CmndLedTable(void)
{
// LedTable - Show current LedTable state
// LedTable 0 - Turn LedTable Off
// LedTable On - Turn LedTable On
// LedTable Toggle - Toggle LedTable state
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) {
switch (XdrvMailbox.payload) {
case 0: // Off
@ -2444,20 +2481,13 @@ void CmndLedTable(void)
void CmndRgbwwTable(void)
{
// RgbWwTable - Show current RGBWW State
// RgbWwTable 255,255,255,255,255 - Set RGBWW state to maximum
if ((XdrvMailbox.data_len > 0)) {
if (strstr(XdrvMailbox.data, ",") != nullptr) { // Command with up to 5 comma separated parameters
for (uint32_t i = 0; i < LST_RGBCW; i++) {
char *substr;
if (0 == i) {
substr = strtok(XdrvMailbox.data, ",");
} else {
substr = strtok(nullptr, ",");
}
if (substr != nullptr) {
Settings.rgbwwTable[i] = atoi(substr);
}
}
uint32_t parm[LST_RGBCW -1];
uint32_t parmcount = ParseParameters(LST_RGBCW, parm);
for (uint32_t i = 0; i < parmcount; i++) {
Settings.rgbwwTable[i] = parm[i];
}
Light.update = true;
}
@ -2466,11 +2496,15 @@ void CmndRgbwwTable(void)
for (uint32_t i = 0; i < LST_RGBCW; i++) {
snprintf_P(scolor, sizeof(scolor), PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Settings.rgbwwTable[i]);
}
ResponseCmndIdxChar(scolor);
ResponseCmndChar(scolor);
}
void CmndFade(void)
{
// Fade - Show current Fade state
// Fade 0 - Turn Fade Off
// Fade On - Turn Fade On
// Fade Toggle - Toggle Fade state
switch (XdrvMailbox.payload) {
case 0: // Off
case 1: // On
@ -2485,7 +2519,11 @@ void CmndFade(void)
}
void CmndSpeed(void)
{ // 1 - fast, 40 - very slow
{
// Speed 1 - Fast
// Speed 40 - Very slow
// Speed + - Increment Speed
// Speed - - Decrement Speed
if (1 == XdrvMailbox.data_len) {
if (('+' == XdrvMailbox.data[0]) && (Settings.light_speed > 1)) {
XdrvMailbox.payload = Settings.light_speed - 1;
@ -2502,6 +2540,8 @@ void CmndSpeed(void)
void CmndWakeupDuration(void)
{
// WakeUpDuration - Show current Wake Up duration in seconds
// WakeUpDuration 60 - Set Wake Up duration to 60 seconds
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 3001)) {
Settings.light_wakeup = XdrvMailbox.payload;
Light.wakeup_active = 0;
@ -2510,7 +2550,8 @@ void CmndWakeupDuration(void)
}
void CmndUndocA(void)
{ // Theos legacy status
{
// Theos legacy status
char scolor[LIGHT_COLOR_SIZE];
LightGetColor(scolor, true); // force hex whatever Option 17
scolor[6] = '\0'; // RGB only

View File

@ -255,27 +255,42 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule)
}
// Step2: Search rule_task and rule_name
StaticJsonBuffer<1024> jsonBuf;
JsonObject &root = jsonBuf.parseObject(event);
if (!root.success()) { return false; } // No valid JSON data
const char* str_value;
if ((pos = rule_name.indexOf("[")) > 0) { // "CURRENT[1]"
int rule_name_idx = rule_name.substring(pos +1).toInt();
int rule_name_idx = 0;
if ((pos = rule_name.indexOf("[")) > 0) { // "SUBTYPE1#CURRENT[1]"
rule_name_idx = rule_name.substring(pos +1).toInt();
if ((rule_name_idx < 1) || (rule_name_idx > 6)) { // Allow indexes 1 to 6
rule_name_idx = 1;
}
rule_name = rule_name.substring(0, pos); // "CURRENT"
str_value = root[rule_task][rule_name][rule_name_idx -1]; // "ENERGY" and "CURRENT" and 0
} else {
str_value = root[rule_task][rule_name]; // "INA219" and "CURRENT"
rule_name = rule_name.substring(0, pos); // "SUBTYPE1#CURRENT"
}
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Task %s, Name %s, Value |%s|, TrigCnt %d, TrigSt %d, Source %s, Json %s"),
// rule_task.c_str(), rule_name.c_str(), rule_svalue, Rules.trigger_count[rule_set], bitRead(Rules.triggers[rule_set], Rules.trigger_count[rule_set]), event.c_str(), (str_value) ? str_value : "none");
StaticJsonBuffer<1024> jsonBuf;
JsonObject &root = jsonBuf.parseObject(event);
if (!root.success()) { return false; } // No valid JSON data
if (!root[rule_task].success()) { return false; } // No rule_task in JSON data
if (!root[rule_task][rule_name].success()) { return false; }
// No value but rule_name is ok
JsonObject &obj1 = root[rule_task];
JsonObject *obj = &obj1;
String subtype;
uint32_t i = 0;
while ((pos = rule_name.indexOf("#")) > 0) { // "SUBTYPE1#SUBTYPE2#CURRENT"
subtype = rule_name.substring(0, pos);
if (!(*obj)[subtype].success()) { return false; } // No subtype in JSON data
JsonObject &obj2 = (*obj)[subtype];
obj = &obj2;
rule_name = rule_name.substring(pos +1);
if (i++ > 10) { return false; } // Abandon possible loop
}
if (!(*obj)[rule_name].success()) { return false; } // No name in JSON data
const char* str_value;
if (rule_name_idx) {
str_value = (*obj)[rule_name][rule_name_idx -1]; // "CURRENT[1]"
} else {
str_value = (*obj)[rule_name]; // "CURRENT"
}
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Task %s, Name %s, Value |%s|, TrigCnt %d, TrigSt %d, Source %s, Json %s"),
// rule_task.c_str(), rule_name.c_str(), rule_svalue, Rules.trigger_count[rule_set], bitRead(Rules.triggers[rule_set], Rules.trigger_count[rule_set]), event.c_str(), (str_value) ? str_value : "none");
Rules.event_value = str_value; // Prepare %value%

View File

@ -4775,6 +4775,7 @@ bool Xdrv10(uint8_t function)
glob_script_mem.script_pram_size=MAX_SCRIPT_SIZE;
glob_script_mem.flags=1;
I2cSetActiveFound(EEPROM_ADDRESS, "EEPROM");
}
}
#endif

View File

@ -22,6 +22,10 @@
#include <vector>
#include <map>
#ifndef ZIGBEE_SAVE_DELAY_SECONDS
#define ZIGBEE_SAVE_DELAY_SECONDS 10; // wait for 10s before saving Zigbee info
#endif
const uint16_t kZigbeeSaveDelaySeconds = ZIGBEE_SAVE_DELAY_SECONDS; // wait for x seconds
typedef int32_t (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value);
@ -57,6 +61,16 @@ class Z_Devices {
public:
Z_Devices() {};
// Probe the existence of device keys
// Results:
// - 0x0000 = not found
// - 0xFFFF = bad parameter
// - 0x<shortaddr> = the device's short address
uint16_t isKnownShortAddr(uint16_t shortaddr) const;
uint16_t isKnownLongAddr(uint64_t longaddr) const;
uint16_t isKnownIndex(uint32_t index) const;
uint16_t isKnownFriendlyName(const char * name) const;
// Add new device, provide ShortAddr and optional longAddr
// If it is already registered, update information, otherwise create the entry
void updateDevice(uint16_t shortaddr, uint64_t longaddr = 0);
@ -74,13 +88,14 @@ public:
void setManufId(uint16_t shortaddr, const char * str);
void setModelId(uint16_t shortaddr, const char * str);
void setFriendlyNameId(uint16_t shortaddr, const char * str);
void setFriendlyName(uint16_t shortaddr, const char * str);
const String * getFriendlyName(uint16_t) const;
// device just seen on the network, update the lastSeen field
void updateLastSeen(uint16_t shortaddr);
// Dump json
String dump(uint32_t dump_mode, int32_t device_num = 0) const;
String dump(uint32_t dump_mode, uint16_t status_shortaddr = 0) const;
// Timers
void resetTimer(uint32_t shortaddr);
@ -89,13 +104,33 @@ public:
// Append or clear attributes Json structure
void jsonClear(uint16_t shortaddr);
void jsonAppend(uint16_t shortaddr, JsonObject &values);
void jsonAppend(uint16_t shortaddr, const JsonObject &values);
const JsonObject *jsonGet(uint16_t shortaddr);
const void jsonPublish(uint16_t shortaddr); // publish the json message and clear buffer
void jsonPublishFlush(uint16_t shortaddr); // publish the json message and clear buffer
bool jsonIsConflict(uint16_t shortaddr, const JsonObject &values);
void jsonPublishNow(uint16_t shortaddr, JsonObject &values);
// Iterator
size_t devicesSize(void) const {
return _devices.size();
}
const Z_Device &devicesAt(size_t i) const {
return _devices.at(i);
}
// Remove device from list
bool removeDevice(uint16_t shortaddr);
// Mark data as 'dirty' and requiring to save in Flash
void dirty(void);
void clean(void); // avoid writing to flash the last changes
// Find device by name, can be short_addr, long_addr, number_in_array or name
uint16_t parseDeviceParam(const char * param, bool short_must_be_known = false) const;
private:
std::vector<Z_Device> _devices = {};
uint32_t _saveTimer = 0;
template < typename T>
static bool findInVector(const std::vector<T> & vecOfElements, const T & element);
@ -109,8 +144,9 @@ private:
Z_Device & getShortAddr(uint16_t shortaddr); // find Device from shortAddr, creates it if does not exist
Z_Device & getLongAddr(uint64_t longaddr); // find Device from shortAddr, creates it if does not exist
int32_t findShortAddr(uint16_t shortaddr);
int32_t findLongAddr(uint64_t longaddr);
int32_t findShortAddr(uint16_t shortaddr) const;
int32_t findLongAddr(uint64_t longaddr) const;
int32_t findFriendlyName(const char * name) const;
void _updateLastSeen(Z_Device &device) {
if (&device != nullptr) {
@ -187,6 +223,7 @@ Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) {
nullptr, nullptr };
device.json_buffer = new DynamicJsonBuffer();
_devices.push_back(device);
dirty();
return _devices.back();
}
@ -198,7 +235,7 @@ Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) {
// Out:
// index in _devices of entry, -1 if not found
//
int32_t Z_Devices::findShortAddr(uint16_t shortaddr) {
int32_t Z_Devices::findShortAddr(uint16_t shortaddr) const {
if (!shortaddr) { return -1; } // does not make sense to look for 0x0000 shortaddr (localhost)
int32_t found = 0;
if (shortaddr) {
@ -217,7 +254,7 @@ int32_t Z_Devices::findShortAddr(uint16_t shortaddr) {
// Out:
// index in _devices of entry, -1 if not found
//
int32_t Z_Devices::findLongAddr(uint64_t longaddr) {
int32_t Z_Devices::findLongAddr(uint64_t longaddr) const {
if (!longaddr) { return -1; }
int32_t found = 0;
if (longaddr) {
@ -228,6 +265,66 @@ int32_t Z_Devices::findLongAddr(uint64_t longaddr) {
}
return -1;
}
//
// Scan all devices to find a corresponding friendlyNme
// Looks info device.friendlyName entry
// In:
// friendlyName (null terminated, should not be empty)
// Out:
// index in _devices of entry, -1 if not found
//
int32_t Z_Devices::findFriendlyName(const char * name) const {
if (!name) { return -1; } // if pointer is null
size_t name_len = strlen(name);
int32_t found = 0;
if (name_len) {
for (auto &elem : _devices) {
if (elem.friendlyName == name) { return found; }
found++;
}
}
return -1;
}
// Probe if device is already known but don't create any entry
uint16_t Z_Devices::isKnownShortAddr(uint16_t shortaddr) const {
int32_t found = findShortAddr(shortaddr);
if (found >= 0) {
return shortaddr;
} else {
return 0; // unknown
}
}
uint16_t Z_Devices::isKnownLongAddr(uint64_t longaddr) const {
int32_t found = findLongAddr(longaddr);
if (found >= 0) {
const Z_Device & device = devicesAt(found);
return device.shortaddr; // can be zero, if not yet registered
} else {
return 0;
}
}
uint16_t Z_Devices::isKnownIndex(uint32_t index) const {
if (index < devicesSize()) {
const Z_Device & device = devicesAt(index);
return device.shortaddr;
} else {
return 0;
}
}
uint16_t Z_Devices::isKnownFriendlyName(const char * name) const {
if ((!name) || (0 == strlen(name))) { return 0xFFFF; } // Error
int32_t found = findFriendlyName(name);
if (found >= 0) {
const Z_Device & device = devicesAt(found);
return device.shortaddr; // can be zero, if not yet registered
} else {
return 0;
}
}
//
// We have a seen a shortaddr on the network, get the corresponding
@ -252,6 +349,17 @@ Z_Device & Z_Devices::getLongAddr(uint64_t longaddr) {
return createDeviceEntry(0, longaddr);
}
// Remove device from list, return true if it was known, false if it was not recorded
bool Z_Devices::removeDevice(uint16_t shortaddr) {
int32_t found = findShortAddr(shortaddr);
if (found >= 0) {
_devices.erase(_devices.begin() + found);
dirty();
return true;
}
return false;
}
//
// We have just seen a device on the network, update the info based on short/long addr
// In:
@ -270,15 +378,18 @@ void Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) {
// erase the previous shortaddr
_devices.erase(_devices.begin() + s_found);
updateLastSeen(shortaddr);
dirty();
}
} else if (s_found >= 0) {
// shortaddr already exists but longaddr not
// add the longaddr to the entry
_devices[s_found].longaddr = longaddr;
updateLastSeen(shortaddr);
dirty();
} else if (l_found >= 0) {
// longaddr entry exists, update shortaddr
_devices[l_found].shortaddr = shortaddr;
dirty();
} else {
// neither short/lonf addr are found.
if (shortaddr || longaddr) {
@ -298,6 +409,7 @@ void Z_Devices::addEndoint(uint16_t shortaddr, uint8_t endpoint) {
_updateLastSeen(device);
if (findEndpointInVector(device.endpoints, ep_profile) < 0) {
device.endpoints.push_back(ep_profile);
dirty();
}
}
@ -310,8 +422,12 @@ void Z_Devices::addEndointProfile(uint16_t shortaddr, uint8_t endpoint, uint16_t
int32_t found = findEndpointInVector(device.endpoints, ep_profile);
if (found < 0) {
device.endpoints.push_back(ep_profile);
dirty();
} else {
device.endpoints[found] = ep_profile;
if (device.endpoints[found] != ep_profile) {
device.endpoints[found] = ep_profile;
dirty();
}
}
}
@ -324,10 +440,12 @@ void Z_Devices::addCluster(uint16_t shortaddr, uint8_t endpoint, uint16_t cluste
if (!out) {
if (!findInVector(device.clusters_in, ep_cluster)) {
device.clusters_in.push_back(ep_cluster);
dirty();
}
} else { // out
if (!findInVector(device.clusters_out, ep_cluster)) {
device.clusters_out.push_back(ep_cluster);
dirty();
}
}
}
@ -352,21 +470,41 @@ void Z_Devices::setManufId(uint16_t shortaddr, const char * str) {
Z_Device & device = getShortAddr(shortaddr);
if (&device == nullptr) { return; } // don't crash if not found
_updateLastSeen(device);
if (!device.manufacturerId.equals(str)) {
dirty();
}
device.manufacturerId = str;
}
void Z_Devices::setModelId(uint16_t shortaddr, const char * str) {
Z_Device & device = getShortAddr(shortaddr);
if (&device == nullptr) { return; } // don't crash if not found
_updateLastSeen(device);
if (!device.modelId.equals(str)) {
dirty();
}
device.modelId = str;
}
void Z_Devices::setFriendlyNameId(uint16_t shortaddr, const char * str) {
void Z_Devices::setFriendlyName(uint16_t shortaddr, const char * str) {
Z_Device & device = getShortAddr(shortaddr);
if (&device == nullptr) { return; } // don't crash if not found
_updateLastSeen(device);
if (!device.friendlyName.equals(str)) {
dirty();
}
device.friendlyName = str;
}
const String * Z_Devices::getFriendlyName(uint16_t shortaddr) const {
int32_t found = findShortAddr(shortaddr);
if (found >= 0) {
const Z_Device & device = devicesAt(found);
if (device.friendlyName.length() > 0) {
return &device.friendlyName;
}
}
return nullptr;
}
// device just seen on the network, update the lastSeen field
void Z_Devices::updateLastSeen(uint16_t shortaddr) {
Z_Device & device = getShortAddr(shortaddr);
@ -398,19 +536,22 @@ void Z_Devices::setTimer(uint32_t shortaddr, uint32_t wait_ms, uint16_t cluster,
// Run timer at each tick
void Z_Devices::runTimer(void) {
uint32_t now = millis();
for (std::vector<Z_Device>::iterator it = _devices.begin(); it != _devices.end(); ++it) {
Z_Device &device = *it;
uint16_t shortaddr = device.shortaddr;
uint32_t timer = device.timer;
if ((timer) && (timer <= now)) {
if ((timer) && TimeReached(timer)) {
device.timer = 0; // cancel the timer before calling, so the callback can set another timer
// trigger the timer
(*device.func)(device.shortaddr, device.cluster, device.endpoint, device.value);
}
}
// save timer
if ((_saveTimer) && TimeReached(_saveTimer)) {
saveZigbeeDevices();
_saveTimer = 0;
}
}
void Z_Devices::jsonClear(uint16_t shortaddr) {
@ -482,7 +623,7 @@ bool Z_Devices::jsonIsConflict(uint16_t shortaddr, const JsonObject &values) {
return false;
}
void Z_Devices::jsonAppend(uint16_t shortaddr, JsonObject &values) {
void Z_Devices::jsonAppend(uint16_t shortaddr, const JsonObject &values) {
Z_Device & device = getShortAddr(shortaddr);
if (&device == nullptr) { return; } // don't crash if not found
if (&values == nullptr) { return; }
@ -490,6 +631,16 @@ void Z_Devices::jsonAppend(uint16_t shortaddr, JsonObject &values) {
if (nullptr == device.json) {
device.json = &(device.json_buffer->createObject());
}
// Prepend Device, will be removed later if redundant
char sa[8];
snprintf_P(sa, sizeof(sa), PSTR("0x%04X"), shortaddr);
device.json->set(F(D_JSON_ZIGBEE_DEVICE), sa);
// Prepend Friendly Name if it has one
const String * fname = zigbee_devices.getFriendlyName(shortaddr);
if (fname) {
device.json->set(F(D_JSON_ZIGBEE_NAME), (char*)fname->c_str()); // (char*) forces ArduinoJson to make a copy of the cstring
}
// copy all values from 'values' to 'json'
CopyJsonObject(*device.json, values);
}
@ -500,40 +651,125 @@ const JsonObject *Z_Devices::jsonGet(uint16_t shortaddr) {
return device.json;
}
const void Z_Devices::jsonPublish(uint16_t shortaddr) {
const JsonObject *json = zigbee_devices.jsonGet(shortaddr);
if (json == nullptr) { return; } // don't crash if not found
void Z_Devices::jsonPublishFlush(uint16_t shortaddr) {
Z_Device & device = getShortAddr(shortaddr);
if (&device == nullptr) { return; } // don't crash if not found
JsonObject * json = device.json;
if (json == nullptr) { return; } // abort if nothing in buffer
const String * fname = zigbee_devices.getFriendlyName(shortaddr);
bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); // should we replace shortaddr with friendlyname?
// if (use_fname) {
// // we need to add the Device short_addr inside the JSON
// char sa[8];
// snprintf_P(sa, sizeof(sa), PSTR("0x%04X"), shortaddr);
// json->set(F(D_JSON_ZIGBEE_DEVICE), sa);
// } else if (fname) {
// json->set(F(D_JSON_NAME), (char*) fname);
// }
// Remove redundant "Name" or "Device"
if (use_fname) {
json->remove(F(D_JSON_ZIGBEE_NAME));
} else {
json->remove(F(D_JSON_ZIGBEE_DEVICE));
}
String msg = "";
json->printTo(msg);
zigbee_devices.jsonClear(shortaddr);
Response_P(PSTR("{\"" D_CMND_ZIGBEE_RECEIVED "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str());
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
XdrvRulesProcess();
if (use_fname) {
Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"%s\":%s}}"), fname->c_str(), msg.c_str());
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
XdrvRulesProcess();
// DEPRECATED TODO
Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED_LEGACY "\":{\"%s\":%s}}"), fname->c_str(), msg.c_str());
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
XdrvRulesProcess();
} else {
Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str());
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
XdrvRulesProcess();
// DEPRECATED TODO
Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED_LEGACY "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str());
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
XdrvRulesProcess();
}
// MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
// XdrvRulesProcess();
}
void Z_Devices::jsonPublishNow(uint16_t shortaddr, JsonObject & values) {
jsonPublishFlush(shortaddr); // flush any previous buffer
jsonAppend(shortaddr, values);
jsonPublishFlush(shortaddr); // publish now
}
void Z_Devices::dirty(void) {
_saveTimer = kZigbeeSaveDelaySeconds * 1000 + millis();
}
void Z_Devices::clean(void) {
_saveTimer = 0;
}
// Parse the command parameters for either:
// - a short address starting with "0x", example: 0x1234
// - a long address starting with "0x", example: 0x7CB03EBB0A0292DD
// - a number 0..99, the index number in ZigbeeStatus
// - a friendly name, between quotes, example: "Room_Temp"
uint16_t Z_Devices::parseDeviceParam(const char * param, bool short_must_be_known) const {
if (nullptr == param) { return 0; }
size_t param_len = strlen(param);
char dataBuf[param_len + 1];
strcpy(dataBuf, param);
RemoveSpace(dataBuf);
uint16_t shortaddr = 0;
if (strlen(dataBuf) < 4) {
// simple number 0..99
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 99)) {
shortaddr = zigbee_devices.isKnownIndex(XdrvMailbox.payload - 1);
}
} else if ((dataBuf[0] == '0') && (dataBuf[1] == 'x')) {
// starts with 0x
if (strlen(dataBuf) < 18) {
// expect a short address
shortaddr = strtoull(dataBuf, nullptr, 0);
if (short_must_be_known) {
shortaddr = zigbee_devices.isKnownShortAddr(shortaddr);
}
// else we don't check if it's already registered to force unregistered devices
} else {
// expect a long address
uint64_t longaddr = strtoull(dataBuf, nullptr, 0);
shortaddr = zigbee_devices.isKnownLongAddr(longaddr);
}
} else {
// expect a Friendly Name
shortaddr = zigbee_devices.isKnownFriendlyName(dataBuf);
}
return shortaddr;
}
// Dump the internal memory of Zigbee devices
// Mode = 1: simple dump of devices addresses and names
// Mode = 2: Mode 1 + also dump the endpoints, profiles and clusters
String Z_Devices::dump(uint32_t dump_mode, int32_t device_num) const {
// Mode = 1: simple dump of devices addresses
// Mode = 2: simple dump of devices addresses and names
// Mode = 3: Mode 2 + also dump the endpoints, profiles and clusters
String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const {
DynamicJsonBuffer jsonBuffer;
JsonArray& json = jsonBuffer.createArray();
JsonArray& devices = json;
//JsonArray& devices = json.createNestedArray(F("ZigbeeDevices"));
// if device_num == 0, then we show all devices.
// When no payload, the default is -99. In this case change it to 0.
if (device_num < 0) { device_num = 0; }
uint32_t device_current = 1;
for (std::vector<Z_Device>::const_iterator it = _devices.begin(); it != _devices.end(); ++it, ++device_current) {
// ignore non-current device, if specified device is non-zero
if ((device_num > 0) && (device_num != device_current)) { continue; }
for (std::vector<Z_Device>::const_iterator it = _devices.begin(); it != _devices.end(); ++it) {
const Z_Device& device = *it;
uint16_t shortaddr = device.shortaddr;
char hex[20];
char hex[22];
// ignore non-current device, if specified device is non-zero
if ((status_shortaddr) && (status_shortaddr != shortaddr)) { continue; }
JsonObject& dev = devices.createNestedObject();
@ -545,7 +781,9 @@ String Z_Devices::dump(uint32_t dump_mode, int32_t device_num) const {
}
if (2 <= dump_mode) {
Uint64toHex(device.longaddr, hex, 64);
hex[0] = '0'; // prefix with '0x'
hex[1] = 'x';
Uint64toHex(device.longaddr, &hex[2], 64);
dev[F("IEEEAddr")] = hex;
if (device.modelId.length() > 0) {
dev[F(D_JSON_MODEL D_JSON_ID)] = device.modelId;

View File

@ -0,0 +1,334 @@
/*
xdrv_23_zigbee.ino - zigbee support for Tasmota
Copyright (C) 2020 Theo Arends and Stephan Hadinger
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef USE_ZIGBEE
// Ensure persistence of devices into Flash
//
// Structure:
// (from file info):
// uint16 - start address in Flash (offset)
// uint16 - length in bytes (makes sure parsing stops)
//
// File structure:
// uint8 - number of devices, 0=none, 0xFF=invalid entry (probably Flash was erased)
//
// [Array of devices]
// [Offset = 2]
// uint8 - length of revice record
// uint16 - short address
// uint64 - long IEEE address
// uint8 - number of endpoints
// [Array of endpoints]
// uint8 - endpoint number
// uint16 - profileID of the endpoint
// Array of uint8 - clusters In codes, 0xFF end marker
// Array of uint8 - clusters Out codes, 0xFF end marker
//
// str - ModelID (null terminated C string, 32 chars max)
// str - Manuf (null terminated C string, 32 chars max)
// reserved for extensions
// Memory footprint
const static uint16_t z_spi_start_sector = 0xFF; // Force last bank of first MB
const static uint8_t* z_spi_start = (uint8_t*) 0x402FF000; // 0x402FF000
const static uint8_t* z_dev_start = z_spi_start + 0x0800; // 0x402FF800 - 2KB
const static size_t z_spi_len = 0x1000; // 4kb blocs
const static size_t z_block_offset = 0x0800;
const static size_t z_block_len = 0x0800; // 2kb
class z_flashdata_t {
public:
uint32_t name; // simple 4 letters name. Currently 'skey', 'crt ', 'crt1', 'crt2'
uint16_t len; // len of object
uint16_t reserved; // align on 4 bytes boundary
};
const static uint32_t ZIGB_NAME = 0x3167697A; // 'zig1' little endian
const static size_t Z_MAX_FLASH = z_block_len - sizeof(z_flashdata_t); // 2040
// encoding for the most commonly 32 clusters, used for binary encoding
const uint16_t Z_ClusterNumber[] PROGMEM = {
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
0x0100, 0x0101, 0x0102,
0x0201, 0x0202, 0x0203, 0x0204,
0x0300, 0x0301,
0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406,
0x0500, 0x0501, 0x0502,
0x0700, 0x0701, 0x0702,
0x0B00, 0x0B01, 0x0B02, 0x0B03, 0x0B04, 0x0B05,
0x1000,
0xFC0F,
};
// convert a 1 byte cluster code to the actual cluster number
uint16_t fromClusterCode(uint8_t c) {
if (c >= sizeof(Z_ClusterNumber)/sizeof(Z_ClusterNumber[0])) {
return 0xFFFF; // invalid
}
return pgm_read_word(&Z_ClusterNumber[c]);
}
// convert a cluster number to 1 byte, or 0xFF if not in table
uint8_t toClusterCode(uint16_t c) {
for (uint32_t i = 0; i < sizeof(Z_ClusterNumber)/sizeof(Z_ClusterNumber[0]); i++) {
if (c == pgm_read_word(&Z_ClusterNumber[i])) {
return i;
}
}
return 0xFF; // not found
}
class SBuffer hibernateDevice(const struct Z_Device &device) {
SBuffer buf(128);
buf.add8(0x00); // overall length, will be updated later
buf.add16(device.shortaddr);
buf.add64(device.longaddr);
uint32_t endpoints = device.endpoints.size();
if (endpoints > 254) { endpoints = 254; }
buf.add8(endpoints);
// iterate on endpoints
for (std::vector<uint32_t>::const_iterator ite = device.endpoints.begin() ; ite != device.endpoints.end(); ++ite) {
uint32_t ep_profile = *ite;
uint8_t endpoint = (ep_profile >> 16) & 0xFF;
uint16_t profileId = ep_profile & 0xFFFF;
buf.add8(endpoint);
buf.add16(profileId);
for (std::vector<uint32_t>::const_iterator itc = device.clusters_in.begin() ; itc != device.clusters_in.end(); ++itc) {
uint16_t cluster = *itc & 0xFFFF;
uint8_t c_endpoint = (*itc >> 16) & 0xFF;
if (endpoint == c_endpoint) {
uint8_t clusterCode = toClusterCode(cluster);
if (0xFF != clusterCode) { buf.add8(clusterCode); }
}
}
buf.add8(0xFF); // end of endpoint marker
for (std::vector<uint32_t>::const_iterator itc = device.clusters_out.begin() ; itc != device.clusters_out.end(); ++itc) {
uint16_t cluster = *itc & 0xFFFF;
uint8_t c_endpoint = (*itc >> 16) & 0xFF;
if (endpoint == c_endpoint) {
uint8_t clusterCode = toClusterCode(cluster);
if (0xFF != clusterCode) { buf.add8(clusterCode); }
}
}
buf.add8(0xFF); // end of endpoint marker
}
// ModelID
size_t model_len = device.modelId.length();
if (model_len > 32) { model_len = 32; } // max 32 chars
buf.addBuffer(device.modelId.c_str(), model_len);
buf.add8(0x00); // end of string marker
// ManufID
size_t manuf_len = device.manufacturerId.length();
if (manuf_len > 32) {manuf_len = 32; } // max 32 chars
buf.addBuffer(device.manufacturerId.c_str(), manuf_len);
buf.add8(0x00); // end of string marker
// FriendlyName
size_t frname_len = device.friendlyName.length();
if (frname_len > 32) {frname_len = 32; } // max 32 chars
buf.addBuffer(device.friendlyName.c_str(), frname_len);
buf.add8(0x00); // end of string marker
// update overall length
buf.set8(0, buf.len());
return buf;
}
class SBuffer hibernateDevices(void) {
SBuffer buf(2048);
size_t devices_size = zigbee_devices.devicesSize();
if (devices_size > 32) { devices_size = 32; } // arbitrarily limit to 32 devices, for now
buf.add8(devices_size); // number of devices
for (uint32_t i = 0; i < devices_size; i++) {
const Z_Device & device = zigbee_devices.devicesAt(i);
const SBuffer buf_device = hibernateDevice(device);
buf.addBuffer(buf_device);
}
size_t buf_len = buf.len();
if (buf_len > 2040) {
AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Devices list too big to fit in Flash (%d)"), buf_len);
}
// Log
char *hex_char = (char*) malloc((buf_len * 2) + 2);
if (hex_char) {
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "ZbFlashStore %s"),
ToHex_P(buf.getBuffer(), buf_len, hex_char, (buf_len * 2) + 2));
free(hex_char);
}
return buf;
}
void hidrateDevices(const SBuffer &buf) {
uint32_t buf_len = buf.len();
if (buf_len <= 10) { return; }
uint32_t k = 0;
uint32_t num_devices = buf.get8(k++);
for (uint32_t i = 0; (i < num_devices) && (k < buf_len); i++) {
uint32_t dev_record_len = buf.get8(k);
SBuffer buf_d = buf.subBuffer(k, dev_record_len);
uint32_t d = 1; // index in device buffer
uint16_t shortaddr = buf_d.get16(d); d += 2;
uint64_t longaddr = buf_d.get64(d); d += 8;
zigbee_devices.updateDevice(shortaddr, longaddr); // update device's addresses
uint32_t endpoints = buf_d.get8(d++);
for (uint32_t j = 0; j < endpoints; j++) {
uint8_t ep = buf_d.get8(d++);
uint16_t ep_profile = buf_d.get16(d); d += 2;
zigbee_devices.addEndointProfile(shortaddr, ep, ep_profile);
// in clusters
while (d < dev_record_len) { // safe guard against overflow
uint8_t ep_cluster = buf_d.get8(d++);
if (0xFF == ep_cluster) { break; } // end of block
zigbee_devices.addCluster(shortaddr, ep, fromClusterCode(ep_cluster), false);
}
// out clusters
while (d < dev_record_len) { // safe guard against overflow
uint8_t ep_cluster = buf_d.get8(d++);
if (0xFF == ep_cluster) { break; } // end of block
zigbee_devices.addCluster(shortaddr, ep, fromClusterCode(ep_cluster), true);
}
}
// parse 3 strings
char empty[] = "";
// ManufID
uint32_t s_len = buf_d.strlen_s(d);
char *ptr = s_len ? buf_d.charptr(d) : empty;
zigbee_devices.setModelId(shortaddr, ptr);
d += s_len + 1;
// ManufID
s_len = buf_d.strlen_s(d);
ptr = s_len ? buf_d.charptr(d) : empty;
zigbee_devices.setManufId(shortaddr, ptr);
d += s_len + 1;
// FriendlyName
s_len = buf_d.strlen_s(d);
ptr = s_len ? buf_d.charptr(d) : empty;
zigbee_devices.setFriendlyName(shortaddr, ptr);
d += s_len + 1;
// next iteration
k += dev_record_len;
}
}
void loadZigbeeDevices(void) {
z_flashdata_t flashdata;
memcpy_P(&flashdata, z_dev_start, sizeof(z_flashdata_t));
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Zigbee signature in Flash: %08X - %d"), flashdata.name, flashdata.len);
// Check the signature
if ((flashdata.name == ZIGB_NAME) && (flashdata.len > 0)) {
uint16_t buf_len = flashdata.len;
// parse what seems to be a valid entry
SBuffer buf(buf_len);
buf.addBuffer(z_dev_start + sizeof(z_flashdata_t), buf_len);
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee devices data in Flash (%d bytes)"), buf_len);
hidrateDevices(buf);
zigbee_devices.clean(); // don't write back to Flash what we just loaded
} else {
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No zigbee devices data in Flash"));
}
}
void saveZigbeeDevices(void) {
SBuffer buf = hibernateDevices();
size_t buf_len = buf.len();
if (buf_len > Z_MAX_FLASH) {
AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Buffer too big to fit in Flash (%d bytes)"), buf_len);
return;
}
// first copy SPI buffer into ram
uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len);
if (!spi_buffer) {
AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer"));
return;
}
// copy the flash into RAM to make local change, and write back the whole buffer
ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE);
z_flashdata_t *flashdata = (z_flashdata_t*)(spi_buffer + z_block_offset);
flashdata->name = ZIGB_NAME;
flashdata->len = buf_len;
flashdata->reserved = 0;
memcpy(spi_buffer + z_block_offset + sizeof(z_flashdata_t), buf.getBuffer(), buf_len);
// buffer is now ready, write it back
if (ESP.flashEraseSector(z_spi_start_sector)) {
ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE);
}
free(spi_buffer);
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data store in Flash (0x%08X - %d bytes)"), z_dev_start, buf_len);
}
// Erase the flash area containing the ZigbeeData
void eraseZigbeeDevices(void) {
zigbee_devices.clean(); // avoid writing data to flash after erase
// first copy SPI buffer into ram
uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len);
if (!spi_buffer) {
AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer"));
return;
}
// copy the flash into RAM to make local change, and write back the whole buffer
ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE);
// Fill the Zigbee area with 0xFF
memset(spi_buffer + z_block_offset, 0xFF, z_block_len);
// buffer is now ready, write it back
if (ESP.flashEraseSector(z_spi_start_sector)) {
ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE);
}
free(spi_buffer);
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data erased (0x%08X - %d bytes)"), z_dev_start, z_block_len);
}
#endif // USE_ZIGBEE

View File

@ -356,7 +356,12 @@ uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer
// TODO
case 0x39: // float
i += 4;
{
uint32_t uint32_val = buf.get32(i);
float * float_val = (float*) &uint32_val;
i += 4;
json[attrid_str] = *float_val;
}
break;
case 0xE0: // ToD
@ -401,7 +406,12 @@ uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer
i += 2;
break;
case 0x3A: // double precision
i += 8;
{
uint64_t uint64_val = buf.get64(i);
double * double_val = (double*) &uint64_val;
i += 8;
json[attrid_str] = *double_val;
}
break;
}
@ -473,7 +483,7 @@ void ZCLFrame::parseClusterSpecificCommand(JsonObject& json, uint8_t offset) {
uint32_t len = _payload.len();
char attrid_str[12];
snprintf_P(attrid_str, sizeof(attrid_str), PSTR("%04X!%02X"), _cmd_id, _cluster_id);
snprintf_P(attrid_str, sizeof(attrid_str), PSTR("%04X!%02X"), _cluster_id, _cmd_id);
char hex_char[_payload.len()*2+2];
ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char));
@ -561,7 +571,7 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
{ 0x000C, 0x0041, "MaxPresentValue", &Z_Copy },
{ 0x000C, 0x0045, "MinPresentValue", &Z_Copy },
{ 0x000C, 0x0051, "OutOfService", &Z_Copy },
{ 0x000C, 0x0055, "PresentValue", &Z_Copy },
{ 0x000C, 0x0055, "AqaraRotate", &Z_Copy },
{ 0x000C, 0x0057, "PriorityArray", &Z_Copy },
{ 0x000C, 0x0067, "Reliability", &Z_Copy },
{ 0x000C, 0x0068, "RelinquishDefault", &Z_Copy },
@ -569,6 +579,7 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
{ 0x000C, 0x006F, "StatusFlags", &Z_Copy },
{ 0x000C, 0x0075, "EngineeringUnits", &Z_Copy },
{ 0x000C, 0x0100, "ApplicationType", &Z_Copy },
{ 0x000C, 0xFF05, "Aqara_FF05", &Z_Copy },
// Binary Output cluster
{ 0x0010, 0x0004, "ActiveText", &Z_Copy },
{ 0x0010, 0x001C, "Description", &Z_Copy },
@ -830,10 +841,10 @@ int32_t Z_FloatDiv10(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject&
// Publish a message for `"Occupancy":0` when the timer expired
int32_t Z_OccupancyCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value) {
// send Occupancy:false message
Response_P(PSTR("{\"" D_CMND_ZIGBEE_RECEIVED "\":{\"0x%04X\":{\"" OCCUPANCY "\":0}}}"), shortaddr);
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
XdrvRulesProcess();
DynamicJsonBuffer jsonBuffer;
JsonObject& json = jsonBuffer.createObject();
json[F(OCCUPANCY)] = 0;
zigbee_devices.jsonPublishNow(shortaddr, json);
}
// Aqara Cube

View File

@ -40,11 +40,11 @@ const Z_CommandConverter Z_Commands[] = {
{ "Color", "0300!07/xxxxyyyy0A00" }, // x, y (uint16)
{ "CT", "0300!0A/xxxx0A00" }, // Color Temperature Mireds (uint16)
{ "Shutter", "0102!xx" },
{ "ShutterOpen", "0102!00"},
{ "ShutterClose", "0102!01"},
{ "ShutterStop", "0102!02"},
{ "ShutterLift", "0102!05xx"}, // Lift percentage, 0%=open, 100%=closed
{ "ShutterTilt", "0102!08xx"}, // Tilt percentage
{ "ShutterOpen", "0102!00" },
{ "ShutterClose", "0102!01" },
{ "ShutterStop", "0102!02" },
{ "ShutterLift", "0102!05xx" }, // Lift percentage, 0%=open, 100%=closed
{ "ShutterTilt", "0102!08xx" }, // Tilt percentage
};
#define ZLE(x) ((x) & 0xFF), ((x) >> 8) // Little Endian

View File

@ -32,7 +32,7 @@ const uint8_t ZIGBEE_STATUS_DEVICE_ANNOUNCE = 30; // Device announces its
const uint8_t ZIGBEE_STATUS_NODE_DESC = 31; // Node descriptor
const uint8_t ZIGBEE_STATUS_ACTIVE_EP = 32; // Endpoints descriptor
const uint8_t ZIGBEE_STATUS_SIMPLE_DESC = 33; // Simple Descriptor (clusters)
const uint8_t ZIGBEE_STATUS_DEVICE_INDICATION = 34; // Device announces its address
const uint8_t ZIGBEE_STATUS_DEVICE_INDICATION = 34; // Device announces its address
const uint8_t ZIGBEE_STATUS_CC_VERSION = 50; // Status: CC2530 ZNP Version
const uint8_t ZIGBEE_STATUS_CC_INFO = 51; // Status: CC2530 Device Configuration
const uint8_t ZIGBEE_STATUS_UNSUPPORTED_VERSION = 98; // Unsupported ZNP version
@ -354,7 +354,7 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
//ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "starting zigbee coordinator")
ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator
ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) // wait for sync ack of command
ZI_WAIT_UNTIL(5000, AREQ_STARTUPFROMAPP) // wait for async message that coordinator started
ZI_WAIT_UNTIL(10000, AREQ_STARTUPFROMAPP) // wait for async message that coordinator started
ZI_SEND(ZBS_GETDEVICEINFO) // GetDeviceInfo
ZI_WAIT_RECV_FUNC(2000, ZBR_GETDEVICEINFO, &Z_ReceiveDeviceInfo)
//ZI_WAIT_RECV(2000, ZBR_GETDEVICEINFO) // memorize info
@ -386,6 +386,7 @@ ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator
ZI_MQTT_STATE(ZIGBEE_STATUS_OK, "Started")
ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "Zigbee started")
ZI_CALL(&Z_State_Ready, 1) // Now accept incoming messages
ZI_CALL(&Z_Load_Devices, 0)
ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP)
ZI_WAIT_FOREVER()
ZI_GOTO(ZIGBEE_LABEL_READY)
@ -544,7 +545,7 @@ void ZigbeeStateMachine_Run(void) {
}
// load current instruction details
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Executing instruction pc=%d"), zigbee.pc);
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Executing instruction pc=%d"), zigbee.pc);
const Zigbee_Instruction *cur_instr_line = &zb_prog[zigbee.pc];
cur_instr = pgm_read_byte(&cur_instr_line->i.i);
cur_d8 = pgm_read_byte(&cur_instr_line->i.d8);

View File

@ -403,7 +403,7 @@ int32_t Z_PublishAttributes(uint16_t shortaddr, uint16_t cluster, uint16_t endpo
// Post-provess for Aqara Presence Senson
Z_AqaraOccupancy(shortaddr, cluster, endpoint, json);
zigbee_devices.jsonPublish(shortaddr);
zigbee_devices.jsonPublishFlush(shortaddr);
return 1;
}
@ -432,9 +432,7 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr);
DynamicJsonBuffer jsonBuffer;
JsonObject& json_root = jsonBuffer.createObject();
JsonObject& json1 = json_root.createNestedObject(F(D_CMND_ZIGBEE_RECEIVED));
JsonObject& json = json1.createNestedObject(shortaddr);
JsonObject& json = jsonBuffer.createObject();
if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) {
zcl_received.parseRawAttributes(json);
@ -446,8 +444,8 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
}
String msg("");
msg.reserve(100);
json_root.printTo(msg);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeZCLRawReceived: %s"), msg.c_str());
json.printTo(msg);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":%s}"), srcaddr, msg.c_str());
zcl_received.postProcessAttributes(srcaddr, json);
// Add linkquality
@ -457,18 +455,14 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
// Prepare for publish
if (zigbee_devices.jsonIsConflict(srcaddr, json)) {
// there is conflicting values, force a publish of the previous message now and don't coalesce
zigbee_devices.jsonPublish(srcaddr);
zigbee_devices.jsonPublishFlush(srcaddr);
} else {
zigbee_devices.jsonAppend(srcaddr, json);
zigbee_devices.setTimer(srcaddr, USE_ZIGBEE_COALESCE_ATTR_TIMER, clusterid, srcendpoint, 0, &Z_PublishAttributes);
}
} else {
// Publish immediately
msg = "";
json_root.printTo(msg);
Response_P(PSTR("%s"), msg.c_str());
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
XdrvRulesProcess();
zigbee_devices.jsonPublishNow(srcaddr, json);
}
return -1;
}
@ -511,6 +505,12 @@ int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf) {
}
}
int32_t Z_Load_Devices(uint8_t value) {
// try to hidrate from known devices
loadZigbeeDevices();
return 0; // continue
}
int32_t Z_State_Ready(uint8_t value) {
zigbee.init_phase = false; // initialization phase complete
return 0; // continue

View File

@ -28,16 +28,24 @@ const uint8_t ZIGBEE_SOF_ALT = 0xFF;
#include <TasmotaSerial.h>
TasmotaSerial *ZigbeeSerial = nullptr;
const char kZigbeeCommands[] PROGMEM = "|"
const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|"
D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|"
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE
;
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|"
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME ;
const char kZigbeeCommands[] PROGMEM = D_PRFX_ZIGBEE "|" // legacy prefix -- deprecated
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|"
D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|"
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|"
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME ;
void (* const ZigbeeCommand[])(void) PROGMEM = {
&CmndZigbeeZNPSend, &CmndZigbeePermitJoin,
&CmndZigbeeStatus, &CmndZigbeeReset, &CmndZigbeeSend,
&CmndZigbeeProbe, &CmndZigbeeRead, &CmndZigbeeZNPReceive
&CmndZigbeeProbe, &CmndZigbeeRead, &CmndZigbeeZNPReceive,
&CmndZigbeeForget, &CmndZigbeeSave, &CmndZigbeeName
};
int32_t ZigbeeProcessInput(class SBuffer &buf) {
@ -62,7 +70,7 @@ int32_t ZigbeeProcessInput(class SBuffer &buf) {
}
}
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "ZigbeeProcessInput: recv_prefix_match = %d, recv_filter_match = %d"), recv_prefix_match, recv_filter_match);
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "ZbProcessInput: recv_prefix_match = %d, recv_filter_match = %d"), recv_prefix_match, recv_filter_match);
}
// if there is a recv_callback, call it now
@ -101,7 +109,7 @@ int32_t ZigbeeProcessInput(class SBuffer &buf) {
res = (*zigbee.recv_unexpected)(res, buf);
}
}
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "ZigbeeProcessInput: res = %d"), res);
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "ZbProcessInput: res = %d"), res);
// change state accordingly
if (0 == res) {
@ -134,7 +142,7 @@ void ZigbeeInput(void)
while (ZigbeeSerial->available()) {
yield();
uint8_t zigbee_in_byte = ZigbeeSerial->read();
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZigbeeInput byte=%d len=%d"), zigbee_in_byte, zigbee_buffer->len());
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZbInput byte=%d len=%d"), zigbee_in_byte, zigbee_buffer->len());
if (0 == zigbee_buffer->len()) { // make sure all variables are correctly initialized
zigbee_frame_len = 5;
@ -143,14 +151,14 @@ void ZigbeeInput(void)
// in this case the first bit (lsb) is missed and Tasmota receives 0xFF instead of 0xFE
// We forgive this mistake, and next bytes are automatically resynchronized
if (ZIGBEE_SOF_ALT == zigbee_in_byte) {
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInput forgiven first byte %02X (only for statistics)"), zigbee_in_byte);
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbInput forgiven first byte %02X (only for statistics)"), zigbee_in_byte);
zigbee_in_byte = ZIGBEE_SOF;
}
}
if ((0 == zigbee_buffer->len()) && (ZIGBEE_SOF != zigbee_in_byte)) {
// waiting for SOF (Start Of Frame) byte, discard anything else
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInput discarding byte %02X"), zigbee_in_byte);
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbInput discarding byte %02X"), zigbee_in_byte);
continue; // discard
}
@ -210,7 +218,7 @@ void ZigbeeInit(void)
{
zigbee.active = false;
if ((pin[GPIO_ZIGBEE_RX] < 99) && (pin[GPIO_ZIGBEE_TX] < 99)) {
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("Zigbee: GPIOs Rx:%d Tx:%d"), pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX]);
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "GPIOs Rx:%d Tx:%d"), pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX]);
// if seriallog_level is 0, we allow GPIO 13/15 to switch to Hardware Serial
ZigbeeSerial = new TasmotaSerial(pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX], seriallog_level ? 1 : 2, 0, 256); // set a receive buffer of 256 bytes
ZigbeeSerial->begin(115200);
@ -254,6 +262,7 @@ void CmndZigbeeReset(void) {
switch (XdrvMailbox.payload) {
case 1:
ZigbeeZNPSend(ZIGBEE_FACTORY_RESET, sizeof(ZIGBEE_FACTORY_RESET));
eraseZigbeeDevices();
restart_flag = 2;
ResponseCmndChar(D_JSON_ZIGBEE_CC2530 " " D_JSON_RESET_AND_RESTARTING);
break;
@ -263,13 +272,6 @@ void CmndZigbeeReset(void) {
}
}
void CmndZigbeeStatus(void) {
if (ZigbeeSerial) {
String dump = zigbee_devices.dump(XdrvMailbox.index, XdrvMailbox.payload);
Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.index, dump.c_str());
}
}
void CmndZigbeeZNPSendOrReceive(bool send)
{
if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) {
@ -320,17 +322,17 @@ void ZigbeeZNPSend(const uint8_t *msg, size_t len) {
uint8_t fcs = data_len;
ZigbeeSerial->write(ZIGBEE_SOF); // 0xFE
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend SOF %02X"), ZIGBEE_SOF);
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend SOF %02X"), ZIGBEE_SOF);
ZigbeeSerial->write(data_len);
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend LEN %02X"), data_len);
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend LEN %02X"), data_len);
for (uint32_t i = 0; i < len; i++) {
uint8_t b = pgm_read_byte(msg + i);
ZigbeeSerial->write(b);
fcs ^= b;
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend byt %02X"), b);
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend byt %02X"), b);
}
ZigbeeSerial->write(fcs); // finally send fcs checksum byte
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend FCS %02X"), fcs);
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend FCS %02X"), fcs);
}
// Now send a MQTT message to report the sent message
char hex_char[(len * 2) + 2];
@ -421,13 +423,13 @@ void zigbeeZCLSendStr(uint16_t dstAddr, uint8_t endpoint, const char *data) {
if (0 == endpoint) {
// endpoint is not specified, let's try to find it from shortAddr
endpoint = zigbee_devices.findClusterEndpointIn(dstAddr, cluster);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeSend: guessing endpoint 0x%02X"), endpoint);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint);
}
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeSend: dstAddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"),
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: dstAddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"),
dstAddr, cluster, endpoint, cmd, data);
if (0 == endpoint) {
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeSend: unspecified endpoint"));
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbSend: unspecified endpoint"));
return;
}
@ -467,7 +469,7 @@ void CmndZigbeeSend(void) {
if (nullptr != &val_device) { device = strToUInt(val_device); }
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("endpoint"));
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
const JsonVariant val_cmd = getCaseInsensitive(json, PSTR("Send"));
const JsonVariant &val_cmd = getCaseInsensitive(json, PSTR("Send"));
if (nullptr != &val_cmd) {
// probe the type of the argument
// If JSON object, it's high level commands
@ -522,9 +524,9 @@ void CmndZigbeeSend(void) {
}
}
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeSend: command_template = %s"), cmd_str.c_str());
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: command_template = %s"), cmd_str.c_str());
cmd_str = zigbeeCmdAddParams(cmd_str.c_str(), x, y, z); // fill in parameters
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeSend: command_final = %s"), cmd_str.c_str());
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: command_final = %s"), cmd_str.c_str());
} else {
// we have zero command, pass through until last error for missing command
}
@ -535,7 +537,7 @@ void CmndZigbeeSend(void) {
// we have an unsupported command type, just ignore it and fallback to missing command
}
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeCmd_actual: ZigbeeZCLSend {\"device\":\"0x%04X\",\"endpoint\":%d,\"send\":\"%s\"}"),
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbCmd_actual: ZigbeeZCLSend {\"device\":\"0x%04X\",\"endpoint\":%d,\"send\":\"%s\"}"),
device, endpoint, cmd_str.c_str());
zigbeeZCLSendStr(device, endpoint, cmd_str.c_str());
} else {
@ -548,20 +550,68 @@ void CmndZigbeeSend(void) {
// Probe a specific device to get its endpoints and supported clusters
void CmndZigbeeProbe(void) {
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
char dataBufUc[XdrvMailbox.data_len + 1];
UpperCase(dataBufUc, XdrvMailbox.data);
RemoveSpace(dataBufUc);
if (strlen(dataBufUc) < 3) { ResponseCmndChar("Invalid destination"); return; }
// TODO, for now ignore friendly names
uint16_t shortaddr = strtoull(dataBufUc, nullptr, 0);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CmndZigbeeScan: short addr 0x%04X"), shortaddr);
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; }
if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; }
// everything is good, we can send the command
Z_SendActiveEpReq(shortaddr);
ResponseCmndDone();
}
// Specify, read or erase a Friendly Name
void CmndZigbeeName(void) {
// Syntax is:
// ZigbeeName <device_id>,<friendlyname> - assign a friendly name
// ZigbeeName <device_id> - display the current friendly name
// ZigbeeName <device_id>, - remove friendly name
//
// Where <device_id> can be: short_addr, long_addr, device_index, friendly_name
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
// check if parameters contain a comma ','
char *p;
char *str = strtok_r(XdrvMailbox.data, ", ", &p);
// parse first part, <device_id>
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; }
if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; }
if (p == nullptr) {
const String * friendlyName = zigbee_devices.getFriendlyName(shortaddr);
Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, friendlyName ? friendlyName->c_str() : "");
} else {
zigbee_devices.setFriendlyName(shortaddr, p);
Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, p);
}
}
// Remove an old Zigbee device from the list of known devices, use ZigbeeStatus to know all registered devices
void CmndZigbeeForget(void) {
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; }
if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; }
// everything is good, we can send the command
if (zigbee_devices.removeDevice(shortaddr)) {
ResponseCmndDone();
} else {
ResponseCmndChar("Unknown device");
}
}
// Save Zigbee information to flash
void CmndZigbeeSave(void) {
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
saveZigbeeDevices();
ResponseCmndDone();
}
// Send an attribute read command to a device, specifying cluster and list of attributes
void CmndZigbeeRead(void) {
// ZigbeeRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":5}
@ -633,6 +683,20 @@ void CmndZigbeePermitJoin(void)
ResponseCmndDone();
}
void CmndZigbeeStatus(void) {
if (ZigbeeSerial) {
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; }
if (XdrvMailbox.payload > 0) {
if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; }
}
String dump = zigbee_devices.dump(XdrvMailbox.index, shortaddr);
Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.index, dump.c_str());
}
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
@ -659,7 +723,8 @@ bool Xdrv23(uint8_t function)
ZigbeeInit();
break;
case FUNC_COMMAND:
result = DecodeCommand(kZigbeeCommands, ZigbeeCommand);
result = DecodeCommand(kZbCommands, ZigbeeCommand);
result = result || DecodeCommand(kZigbeeCommands, ZigbeeCommand); // deprecated
break;
}
}

View File

@ -155,13 +155,8 @@ void CmndBuzzer(void)
if (XdrvMailbox.data_len > 0) {
if (XdrvMailbox.payload > 0) {
char *p;
uint32_t i = 0;
uint32_t parm[4] = { 0 };
for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 4; str = strtok_r(nullptr, ", ", &p)) {
parm[i] = strtoul(str, nullptr, 0);
i++;
}
ParseParameters(4, parm);
for (uint32_t i = 0; i < 3; i++) {
if (parm[i] < 1) { parm[i] = 1; } // Default Count, On time, Off time
}

View File

@ -34,7 +34,7 @@ uint16_t messwerte[5] = {30,50,70,90,100};
uint16_t last_execute_step;
enum ShutterModes { SHT_OFF_OPEN__OFF_CLOSE, SHT_OFF_ON__OPEN_CLOSE, SHT_PULSE_OPEN__PULSE_CLOSE, SHT_OFF_ON__OPEN_CLOSE_STEPPER,};
enum ShutterButtonStates { SHT_NOT_PRESSED, SHT_PRESSED_MULTI, SHT_PRESSED_HOLD, SHT_PRESSED_IMMEDIATE, SHT_SHT_PRESSED_MULTI_SIMULTANEOUS, SHT_PRESSED_EXT_HOLD_SIMULTANEOUS,};
enum ShutterButtonStates { SHT_NOT_PRESSED, SHT_PRESSED_MULTI, SHT_PRESSED_HOLD, SHT_PRESSED_IMMEDIATE, SHT_PRESSED_MULTI_SIMULTANEOUS, SHT_PRESSED_HOLD_SIMULTANEOUS, SHT_PRESSED_EXT_HOLD_SIMULTANEOUS,};
const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|"
D_CMND_SHUTTER_OPEN "|" D_CMND_SHUTTER_CLOSE "|" D_CMND_SHUTTER_STOP "|" D_CMND_SHUTTER_POSITION "|"
@ -48,7 +48,8 @@ void (* const ShutterCommand[])(void) PROGMEM = {
&CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay,
&CmndShutterFrequency, &CmndShutterButton, &CmndShutterLock, &CmndShutterEnableEndStopTime};
const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"Direction\":%d}";
const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"Direction\":%d}";
const char JSON_SHUTTER_BUTTON[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Button%d\":%d}";
#include <Ticker.h>
@ -101,6 +102,8 @@ void ShutterRtc50mS(void)
int32_t ShutterPercentToRealPosition(uint32_t percent, uint32_t index)
{
if (0 == percent) return 0;
if (100 == percent) return Shutter.open_max[index];
if (Settings.shutter_set50percent[index] != 50) {
return (percent <= 5) ? Settings.shuttercoeff[2][index] * percent : Settings.shuttercoeff[1][index] * percent + Settings.shuttercoeff[0][index];
} else {
@ -135,6 +138,8 @@ int32_t ShutterPercentToRealPosition(uint32_t percent, uint32_t index)
uint8_t ShutterRealToPercentPosition(int32_t realpos, uint32_t index)
{
if (0 >= realpos) return 0;
if (Shutter.open_max[index] <= realpos) return 100;
if (Settings.shutter_set50percent[index] != 50) {
return (Settings.shuttercoeff[2][index] * 5 > realpos) ? SHT_DIV_ROUND(realpos, Settings.shuttercoeff[2][index]) : SHT_DIV_ROUND(realpos-Settings.shuttercoeff[0][index], Settings.shuttercoeff[1][index]);
} else {
@ -228,7 +233,7 @@ void ShutterInit(void)
Shutter.real_position[i] = ShutterPercentToRealPosition(Settings.shutter_position[i], i);
//Shutter.real_position[i] = Settings.shutter_position[i] <= 5 ? Settings.shuttercoeff[2][i] * Settings.shutter_position[i] : Settings.shuttercoeff[1][i] * Settings.shutter_position[i] + Settings.shuttercoeff[0,i];
Shutter.start_position[i] = Shutter.real_position[i];
Shutter.start_position[i] = Shutter.target_position[i] = Shutter.real_position[i];
Shutter.motordelay[i] = Settings.shutter_motordelay[i];
char shutter_open_chr[10];
@ -244,7 +249,7 @@ void ShutterInit(void)
// terminate loop at first INVALID shutter.
break;
}
ShutterLimitRealAndTargetPositions(i);
Settings.shutter_accuracy = 1;
}
}
@ -556,28 +561,27 @@ void ShutterButtonHandler(void)
}
}
if ((Button.press_counter[button_index]<99) && (Button.hold_timer[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10)) { // press still valid && SetOption32 (40) - Button hold
if (!Settings.flag.button_restrict) { // no SetOption1 (0)
// check for simultaneous shutter button hold
if (ShutterButtonIsSimultaneousHold(button_index)) {
// simultaneous shutter button hold detected
for (uint32_t i = 0; i < MAX_KEYS; i++)
if (Settings.shutter_button[i] & (1<<31))
Button.press_counter[i] = 99; // Remember to discard further action for press & hold within button timings
}
// check for simultaneous shutter button hold
if (ShutterButtonIsSimultaneousHold(button_index)) {
// simultaneous shutter button hold detected
for (uint32_t i = 0; i < MAX_KEYS; i++)
if (Settings.shutter_button[i] & (1<<31))
Button.press_counter[i] = 99; // Remember to discard further action for press & hold within button timings
press_index = 0;
buttonState = SHT_PRESSED_HOLD_SIMULTANEOUS;
}
if (Button.press_counter[button_index]<99)
if (Button.press_counter[button_index]<99) {
press_index = 0;
buttonState = SHT_PRESSED_HOLD;
}
Button.press_counter[button_index] = 0;
}
if ((!Settings.flag.button_restrict) && (Button.press_counter[button_index]==0) && (Button.hold_timer[button_index] == loops_per_second * IMMINENT_RESET_FACTOR * Settings.param[P_HOLD_TIME] / 10)) { // no SetOption1 (0) && SetOption32 (40) - Button held for factor times longer
if ((Button.press_counter[button_index]==0) && (Button.hold_timer[button_index] == loops_per_second * IMMINENT_RESET_FACTOR * Settings.param[P_HOLD_TIME] / 10)) { // SetOption32 (40) - Button held for factor times longer
// check for simultaneous shutter button extend hold
if (ShutterButtonIsSimultaneousHold(button_index)) {
// simultaneous shutter button extend hold detected
char scmnd[20];
press_index = 0;
buttonState = SHT_PRESSED_EXT_HOLD_SIMULTANEOUS;
snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1"));
ExecuteCommand(scmnd, SRC_BUTTON);
return;
}
}
}
@ -589,29 +593,23 @@ void ShutterButtonHandler(void)
} else {
if (!restart_flag && !Button.hold_timer[button_index] && (Button.press_counter[button_index] > 0)) {
if (Button.press_counter[button_index]<99) {
if ((!Settings.flag.button_restrict) && (Button.press_counter[button_index]>=5)) { // no SetOption1 (0) && 5x or more presses
// check for simultaneous shutter button press >3
uint32 min_shutterbutton_press_counter = -1;
for (uint32_t i = 0; i < MAX_KEYS; i++) {
if ((Settings.shutter_button[i] & (1<<31)) && (Button.press_counter[i] < min_shutterbutton_press_counter))
min_shutterbutton_press_counter = Button.press_counter[i];
}
if (min_shutterbutton_press_counter >= Button.press_counter[button_index]-2) {
char scmnd[20];
// simultaneous shutter button press >3 detected
press_index = Button.press_counter[button_index];
for (uint32_t i = 0; i < MAX_KEYS; i++)
if (Settings.shutter_button[i] & (1<<31))
Button.press_counter[i] = 99; // Remember to discard further action for press & hold within button timings
buttonState = SHT_SHT_PRESSED_MULTI_SIMULTANEOUS;
GetTextIndexed(scmnd, sizeof(scmnd), press_index -3, kCommands);
ExecuteCommand(scmnd, SRC_BUTTON);
return;
}
// check for simultaneous shutter button press
uint32 min_shutterbutton_press_counter = -1;
for (uint32_t i = 0; i < MAX_KEYS; i++) {
if ((Settings.shutter_button[i] & (1<<31)) && (Button.press_counter[i] < min_shutterbutton_press_counter))
min_shutterbutton_press_counter = Button.press_counter[i];
}
press_index = Button.press_counter[button_index];
if ((buttonState == SHT_NOT_PRESSED) && (Button.press_counter[button_index]<99)) {
if (min_shutterbutton_press_counter == Button.press_counter[button_index]) {
// simultaneous shutter button press detected
press_index = Button.press_counter[button_index];
for (uint32_t i = 0; i < MAX_KEYS; i++)
if (Settings.shutter_button[i] & (1<<31))
Button.press_counter[i] = 99; // Remember to discard further action for press & hold within button timings
buttonState = SHT_PRESSED_MULTI_SIMULTANEOUS;
}
if ((buttonState != SHT_PRESSED_MULTI_SIMULTANEOUS) && (Button.press_counter[button_index]<99)) {
// no simultaneous shutter button press >3 detected
press_index = Button.press_counter[button_index];
buttonState = SHT_PRESSED_MULTI;
}
}
@ -620,40 +618,58 @@ void ShutterButtonHandler(void)
}
}
if ((buttonState != SHT_NOT_PRESSED) && (buttonState != SHT_SHT_PRESSED_MULTI_SIMULTANEOUS) && (buttonState != SHT_PRESSED_EXT_HOLD_SIMULTANEOUS)) {
if (Settings.shutter_startrelay[shutter_index] && Settings.shutter_startrelay[shutter_index] <9) {
if (press_index>3) press_index=3;
press_index = (buttonState == SHT_PRESSED_HOLD) ? 3 : (press_index-1);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: shutter %d, button %d = %d (single=1, double=2, tripple=3, hold=4)"), shutter_index+1, button_index+1, press_index+1);
XdrvMailbox.index = shutter_index +1;
last_source = SRC_BUTTON;
XdrvMailbox.data_len = 0;
char databuf[1] = "";
XdrvMailbox.data = databuf;
XdrvMailbox.command = NULL;
if (buttonState == SHT_PRESSED_IMMEDIATE) {
XdrvMailbox.payload = XdrvMailbox.index;
CmndShutterStop();
}
else {
uint8_t position = (Settings.shutter_button[button_index]>>(6*press_index + 2)) & 0x03f;
if (position) {
if (Shutter.direction[shutter_index]) {
XdrvMailbox.payload = XdrvMailbox.index;
CmndShutterStop();
} else {
XdrvMailbox.payload = position = (position-1)<<1;
CmndShutterPosition();
if (Settings.shutter_button[button_index] & ((0x01<<26)<<press_index)) {
// MQTT broadcast to grouptopic
char scommand[CMDSZ];
char stopic[TOPSZ];
for (uint32_t i = 0; i < MAX_SHUTTERS; i++) {
if ((i==shutter_index) || (Settings.shutter_button[button_index] & (0x01<<30))) {
snprintf_P(scommand, sizeof(scommand),PSTR("ShutterPosition%d"), i+1);
GetGroupTopic_P(stopic, scommand);
Response_P("%d", position);
MqttPublish(stopic, false);
if (buttonState != SHT_NOT_PRESSED) {
if (buttonState == SHT_PRESSED_MULTI_SIMULTANEOUS) {
if ((press_index>=5) && (press_index<=7) && (!Settings.flag.button_restrict)) { // 5x..7x && no SetOption1 (0)
// simultaneous shutter button press 5x, 6x, 7x detected
char scmnd[20];
GetTextIndexed(scmnd, sizeof(scmnd), press_index -3, kCommands);
ExecuteCommand(scmnd, SRC_BUTTON);
return;
}
} else if (buttonState == SHT_PRESSED_EXT_HOLD_SIMULTANEOUS) {
// simultaneous shutter button extend hold detected
if (!Settings.flag.button_restrict) { // no SetOption1 (0)
char scmnd[20];
snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1"));
ExecuteCommand(scmnd, SRC_BUTTON);
return;
}
} else if (buttonState <= SHT_PRESSED_IMMEDIATE) {
if (Settings.shutter_startrelay[shutter_index] && Settings.shutter_startrelay[shutter_index] <9) {
uint8_t pos_press_index = (buttonState == SHT_PRESSED_HOLD) ? 3 : (press_index-1);
if (pos_press_index>3) pos_press_index=3;
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: shutter %d, button %d = %d (single=1, double=2, tripple=3, hold=4)"), shutter_index+1, button_index+1, pos_press_index+1);
XdrvMailbox.index = shutter_index +1;
last_source = SRC_BUTTON;
XdrvMailbox.data_len = 0;
char databuf[1] = "";
XdrvMailbox.data = databuf;
XdrvMailbox.command = NULL;
if (buttonState == SHT_PRESSED_IMMEDIATE) {
XdrvMailbox.payload = XdrvMailbox.index;
CmndShutterStop();
}
else {
uint8_t position = (Settings.shutter_button[button_index]>>(6*pos_press_index + 2)) & 0x03f;
if (position) {
if (Shutter.direction[shutter_index]) {
XdrvMailbox.payload = XdrvMailbox.index;
CmndShutterStop();
} else {
XdrvMailbox.payload = position = (position-1)<<1;
CmndShutterPosition();
if (Settings.shutter_button[button_index] & ((0x01<<26)<<pos_press_index)) {
// MQTT broadcast to grouptopic
char scommand[CMDSZ];
char stopic[TOPSZ];
for (uint32_t i = 0; i < MAX_SHUTTERS; i++) {
if ((i==shutter_index) || (Settings.shutter_button[button_index] & (0x01<<30))) {
snprintf_P(scommand, sizeof(scommand),PSTR("ShutterPosition%d"), i+1);
GetGroupTopic_P(stopic, scommand);
Response_P("%d", position);
MqttPublish(stopic, false);
}
}
}
}
@ -661,6 +677,11 @@ void ShutterButtonHandler(void)
}
}
}
Response_P(PSTR("{"));
ResponseAppend_P(JSON_SHUTTER_BUTTON, shutter_index+1, (buttonState <= SHT_PRESSED_IMMEDIATE) ? (button_index+1) : 0, press_index);
ResponseJsonEnd();
MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_PRFX_SHUTTER));
XdrvRulesProcess();
}
}
@ -757,18 +778,16 @@ void CmndShutterPosition(void)
target_pos_percent = ((Settings.shutter_options[index] & 1) && (SRC_WEBGUI != last_source)) ? 100 - target_pos_percent : target_pos_percent;
if (XdrvMailbox.payload != -99) {
//target_pos_percent = (Settings.shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent;
if (0 == target_pos_percent) {
Shutter.target_position[index] = (Settings.shutter_options[index] & 4) ? (-1 * 2000) : 0;
} else if (100 == target_pos_percent) {
Shutter.target_position[index] = (Settings.shutter_options[index] & 4) ? (Shutter.open_max[index] + 1 * 2000) : Shutter.open_max[index];
} else {
Shutter.target_position[index] = ShutterPercentToRealPosition(target_pos_percent, index);
}
Shutter.target_position[index] = ShutterPercentToRealPosition(target_pos_percent, index);
Shutter.accelerator[index] = Shutter.max_pwm_frequency / ((Shutter.motordelay[index] > 0) ? Shutter.motordelay[index] : 1);
//Shutter.target_position[index] = XdrvMailbox.payload < 5 ? Settings.shuttercoeff[2][index] * XdrvMailbox.payload : Settings.shuttercoeff[1][index] * XdrvMailbox.payload + Settings.shuttercoeff[0,index];
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: lastsource %d:, real %d, target %d, payload %d"), last_source, Shutter.real_position[index] ,Shutter.target_position[index],target_pos_percent);
}
if ( (target_pos_percent >= 0) && (target_pos_percent <= 100) && abs(Shutter.target_position[index] - Shutter.real_position[index] ) / Shutter.close_velocity[index] > 2) {
if (Settings.shutter_options[index] & 4) {
if (0 == target_pos_percent) Shutter.target_position[index] -= 1 * 2000;
if (100 == target_pos_percent) Shutter.target_position[index] += 1 * 2000;
}
int8_t new_shutterdirection = Shutter.real_position[index] < Shutter.target_position[index] ? 1 : -1;
if (Shutter.direction[index] == -new_shutterdirection) {
// direction need to be changed. on momentary switches first stop the Shutter
@ -789,26 +808,32 @@ void CmndShutterPosition(void)
ShutterWaitForMotorStop(index);
ExecuteCommandPower(Settings.shutter_startrelay[index], 0, SRC_SHUTTER);
ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]);
// Code for shutters with circuit safe configuration, switch the direction Relay
ExecuteCommandPower(Settings.shutter_startrelay[index] +1, new_shutterdirection == 1 ? 0 : 1, SRC_SHUTTER);
// power on
ExecuteCommandPower(Settings.shutter_startrelay[index], 1, SRC_SHUTTER);
if (Shutter.skip_relay_change == 0) {
// Code for shutters with circuit safe configuration, switch the direction Relay
ExecuteCommandPower(Settings.shutter_startrelay[index] +1, new_shutterdirection == 1 ? 0 : 1, SRC_SHUTTER);
// power on
ExecuteCommandPower(Settings.shutter_startrelay[index], 1, SRC_SHUTTER);
}
} else {
// now start the motor for the right direction, work for momentary and normal shutters.
AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Start in dir %d"), Shutter.direction[index]);
ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]);
ExecuteCommandPower(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER);
if (Shutter.skip_relay_change == 0) {
ExecuteCommandPower(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER);
}
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Delay6 5s, xdrv %d"), XdrvMailbox.payload);
}
Shutter.switched_relay = 0;
}
} else {
target_pos_percent = ShutterRealToPercentPosition(Shutter.real_position[index], index);
ShutterReportPosition(true);
}
XdrvMailbox.index = index +1; // Fix random index for ShutterClose
if (XdrvMailbox.command)
ResponseCmndIdxNumber((Settings.shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent);
} else {
ShutterReportPosition(true);
if (XdrvMailbox.command)
ResponseCmndIdxChar("Locked");
}

View File

@ -116,6 +116,7 @@ void ILI9488_InitDriver()
if (I2cEnabled(XI2C_38) && I2cSetDevice(FT6236_address)) {
FT6236begin(FT6236_address);
FT6236_found=1;
I2cSetActiveFound(FT6236_address, "FT6236");
} else {
FT6236_found=0;
}

View File

@ -101,6 +101,7 @@ void RA8876_InitDriver()
if (I2cEnabled(XI2C_39) && I2cSetDevice(FT5316_address)) {
FT6236begin(FT5316_address);
FT5316_found=1;
I2cSetActiveFound(FT5316_address, "FT5316");
} else {
FT5316_found=0;
}

View File

@ -20,7 +20,7 @@
#ifdef USE_ENERGY_SENSOR
#ifdef USE_CSE7766
/*********************************************************************************************\
* CSE7766 - Energy (Sonoff S31 and Sonoff Pow R2)
* CSE7759 and CSE7766 - Energy (Sonoff S31 and Sonoff Pow R2)
* HLW8032 - Energy (Blitzwolf SHP5)
*
* Based on datasheet from http://www.chipsea.com/UploadFiles/2017/08/11144342F01B5662.pdf
@ -37,6 +37,12 @@
#define CSE_PREF 1000
#define CSE_UREF 100
#define CSE_BUFFER_SIZE 25
#include <TasmotaSerial.h>
TasmotaSerial *CseSerial = nullptr;
struct CSE {
long voltage_cycle = 0;
long current_cycle = 0;
@ -45,6 +51,8 @@ struct CSE {
long cf_pulses = 0;
long cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED;
int byte_counter = 0;
uint8_t *rx_buffer = nullptr;
uint8_t power_invalid = 0;
bool received = false;
} Cse;
@ -57,7 +65,7 @@ void CseReceived(void)
// 55 5A 02 F7 60 00 03 AB 00 40 10 02 60 5D 51 A6 58 03 E9 EF 71 0B 7A 36 - 55 = Ok, 71 = Ok
// Hd Id VCal---- Voltage- ICal---- Current- PCal---- Power--- Ad CF--- Ck
uint8_t header = serial_in_buffer[0];
uint8_t header = Cse.rx_buffer[0];
if ((header & 0xFC) == 0xFC) {
AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: Abnormal hardware"));
return;
@ -67,30 +75,30 @@ void CseReceived(void)
if (HLW_UREF_PULSE == Settings.energy_voltage_calibration) {
long voltage_coefficient = 191200; // uSec
if (CSE_NOT_CALIBRATED != header) {
voltage_coefficient = serial_in_buffer[2] << 16 | serial_in_buffer[3] << 8 | serial_in_buffer[4];
voltage_coefficient = Cse.rx_buffer[2] << 16 | Cse.rx_buffer[3] << 8 | Cse.rx_buffer[4];
}
Settings.energy_voltage_calibration = voltage_coefficient / CSE_UREF;
}
if (HLW_IREF_PULSE == Settings.energy_current_calibration) {
long current_coefficient = 16140; // uSec
if (CSE_NOT_CALIBRATED != header) {
current_coefficient = serial_in_buffer[8] << 16 | serial_in_buffer[9] << 8 | serial_in_buffer[10];
current_coefficient = Cse.rx_buffer[8] << 16 | Cse.rx_buffer[9] << 8 | Cse.rx_buffer[10];
}
Settings.energy_current_calibration = current_coefficient;
}
if (HLW_PREF_PULSE == Settings.energy_power_calibration) {
long power_coefficient = 5364000; // uSec
if (CSE_NOT_CALIBRATED != header) {
power_coefficient = serial_in_buffer[14] << 16 | serial_in_buffer[15] << 8 | serial_in_buffer[16];
power_coefficient = Cse.rx_buffer[14] << 16 | Cse.rx_buffer[15] << 8 | Cse.rx_buffer[16];
}
Settings.energy_power_calibration = power_coefficient / CSE_PREF;
}
uint8_t adjustement = serial_in_buffer[20];
Cse.voltage_cycle = serial_in_buffer[5] << 16 | serial_in_buffer[6] << 8 | serial_in_buffer[7];
Cse.current_cycle = serial_in_buffer[11] << 16 | serial_in_buffer[12] << 8 | serial_in_buffer[13];
Cse.power_cycle = serial_in_buffer[17] << 16 | serial_in_buffer[18] << 8 | serial_in_buffer[19];
Cse.cf_pulses = serial_in_buffer[21] << 8 | serial_in_buffer[22];
uint8_t adjustement = Cse.rx_buffer[20];
Cse.voltage_cycle = Cse.rx_buffer[5] << 16 | Cse.rx_buffer[6] << 8 | Cse.rx_buffer[7];
Cse.current_cycle = Cse.rx_buffer[11] << 16 | Cse.rx_buffer[12] << 8 | Cse.rx_buffer[13];
Cse.power_cycle = Cse.rx_buffer[17] << 16 | Cse.rx_buffer[18] << 8 | Cse.rx_buffer[19];
Cse.cf_pulses = Cse.rx_buffer[21] << 8 | Cse.rx_buffer[22];
if (Energy.power_on) { // Powered on
if (adjustement & 0x40) { // Voltage valid
@ -134,41 +142,44 @@ void CseReceived(void)
bool CseSerialInput(void)
{
if (Cse.received) {
serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
if (24 == serial_in_byte_counter) {
while (CseSerial->available()) {
yield();
uint8_t serial_in_byte = CseSerial->read();
AddLogSerial(LOG_LEVEL_DEBUG_MORE);
if (Cse.received) {
Cse.rx_buffer[Cse.byte_counter++] = serial_in_byte;
if (24 == Cse.byte_counter) {
uint8_t checksum = 0;
for (uint32_t i = 2; i < 23; i++) { checksum += serial_in_buffer[i]; }
if (checksum == serial_in_buffer[23]) {
Energy.data_valid[0] = 0;
CseReceived();
Cse.received = false;
return true;
} else {
AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: " D_CHECKSUM_FAILURE));
do { // Sync buffer with data (issue #1907 and #3425)
memmove(serial_in_buffer, serial_in_buffer +1, 24);
serial_in_byte_counter--;
} while ((serial_in_byte_counter > 2) && (0x5A != serial_in_buffer[1]));
if (0x5A != serial_in_buffer[1]) {
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, Cse.rx_buffer, 24);
uint8_t checksum = 0;
for (uint32_t i = 2; i < 23; i++) { checksum += Cse.rx_buffer[i]; }
if (checksum == Cse.rx_buffer[23]) {
Energy.data_valid[0] = 0;
CseReceived();
Cse.received = false;
serial_in_byte_counter = 0;
return true;
} else {
AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: " D_CHECKSUM_FAILURE));
do { // Sync buffer with data (issue #1907 and #3425)
memmove(Cse.rx_buffer, Cse.rx_buffer +1, 24);
Cse.byte_counter--;
} while ((Cse.byte_counter > 2) && (0x5A != Cse.rx_buffer[1]));
if (0x5A != Cse.rx_buffer[1]) {
Cse.received = false;
Cse.byte_counter = 0;
}
}
}
}
} else {
if ((0x5A == serial_in_byte) && (1 == serial_in_byte_counter)) { // 0x5A - Packet header 2
Cse.received = true;
} else {
serial_in_byte_counter = 0;
if ((0x5A == serial_in_byte) && (1 == Cse.byte_counter)) { // 0x5A - Packet header 2
Cse.received = true;
} else {
Cse.byte_counter = 0;
}
Cse.rx_buffer[Cse.byte_counter++] = serial_in_byte;
}
serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
}
serial_in_byte = 0; // Discard
return false;
}
/********************************************************************************************/
@ -209,15 +220,33 @@ void CseEverySecond(void)
}
}
void CseDrvInit(void)
void CseSnsInit(void)
{
if ((3 == pin[GPIO_CSE7766_RX]) && (1 == pin[GPIO_CSE7766_TX])) { // As it uses 8E1 currently only hardware serial is supported
SetSerial(4800, TS_SERIAL_8E1);
// Software serial init needs to be done here as earlier (serial) interrupts may lead to Exceptions
// CseSerial = new TasmotaSerial(pin[GPIO_CSE7766_RX], pin[GPIO_CSE7766_TX], 1);
CseSerial = new TasmotaSerial(pin[GPIO_CSE7766_RX], -1, 1);
if (CseSerial->begin(4800, 2)) { // Fake Software Serial 8E1 by using two stop bits
if (CseSerial->hardwareSerial()) {
SetSerial(4800, TS_SERIAL_8E1);
ClaimSerial();
}
if (0 == Settings.param[P_CSE7766_INVALID_POWER]) {
Settings.param[P_CSE7766_INVALID_POWER] = CSE_MAX_INVALID_POWER; // SetOption39 1..255
}
Cse.power_invalid = Settings.param[P_CSE7766_INVALID_POWER];
energy_flg = XNRG_02;
} else {
energy_flg = ENERGY_NONE;
}
}
void CseDrvInit(void)
{
Cse.rx_buffer = (uint8_t*)(malloc(CSE_BUFFER_SIZE));
if (Cse.rx_buffer != nullptr) {
// if ((pin[GPIO_CSE7766_RX] < 99) && (pin[GPIO_CSE7766_TX] < 99)) {
if (pin[GPIO_CSE7766_RX] < 99) {
energy_flg = XNRG_02;
}
}
}
@ -254,8 +283,8 @@ bool Xnrg02(uint8_t function)
bool result = false;
switch (function) {
case FUNC_SERIAL:
result = CseSerialInput();
case FUNC_LOOP:
if (CseSerial) { CseSerialInput(); }
break;
case FUNC_ENERGY_EVERY_SECOND:
CseEverySecond();
@ -263,6 +292,9 @@ bool Xnrg02(uint8_t function)
case FUNC_COMMAND:
result = CseCommand();
break;
case FUNC_INIT:
CseSnsInit();
break;
case FUNC_PRE_INIT:
CseDrvInit();
break;

View File

@ -71,7 +71,7 @@ struct {
void AdcInit(void)
{
if ((Settings.adc_param_type != my_adc0) || (Settings.adc_param1 > 1000000) || (Settings.adc_param1 < 100)) {
if ((Settings.adc_param_type != my_adc0) || (Settings.adc_param1 > 1000000)) {
if (ADC0_TEMP == my_adc0) {
// Default Shelly 2.5 and 1PM parameters
Settings.adc_param_type = ADC0_TEMP;
@ -85,11 +85,12 @@ void AdcInit(void)
Settings.adc_param2 = ANALOG_LDR_LUX_CALC_SCALAR;
Settings.adc_param3 = ANALOG_LDR_LUX_CALC_EXPONENT * 10000;
}
else if (ADC0_MOIST == my_adc0) {
Settings.adc_param_type = ADC0_MOIST;
else if (ADC0_RANGE == my_adc0) {
Settings.adc_param_type = ADC0_RANGE;
Settings.adc_param1 = 0;
Settings.adc_param2 = 1023;
Settings.adc_param3 = 0;
Settings.adc_param4 = 100;
}
else if (ADC0_CT_POWER == my_adc0) {
Settings.adc_param_type = ADC0_CT_POWER;
@ -144,17 +145,14 @@ uint16_t AdcGetLux(void)
return (uint16_t)ldrLux;
}
uint16_t AdcGetMoist(void)
uint16_t AdcGetRange(void)
{
// formula for calibration: value, fromLow, fromHigh, toHigh, toLow
// Example: 632, 0, 1023, 100, 0
// int( ( ( (<param2> - <analogue-value>) / ( <param2> - <param1> ) ) * ( <param3> - <param4> ) ) + <param4> )
// double amoist = ((Settings.adc_param2 - (double)adc) / (Settings.adc_param2 - Settings.adc_param1) * 100;
// int((((1023 - <analog-reading>) / ( 1023 - 0 )) * ( 100 - 0 )) + 0 )
// formula for calibration: value, fromLow, fromHigh, toLow, toHigh
// Example: 514, 632, 236, 0, 100
// int( ((<param2> - <analog-value>) / (<param2> - <param1>) ) * (<param3> - <param4>) ) + <param4> )
int adc = AdcRead(2);
double amoist = ((double)Settings.adc_param2 - (double)adc) / ((double)Settings.adc_param2 - (double)Settings.adc_param1) * 100;
//double amoist = ((1023 - (double)adc) / 1023) * 100;
return (uint16_t)amoist;
double adcrange = ( ((double)Settings.adc_param2 - (double)adc) / ( ((double)Settings.adc_param2 - (double)Settings.adc_param1)) * ((double)Settings.adc_param3 - (double)Settings.adc_param4) + (double)Settings.adc_param4 );
return (uint16_t)adcrange;
}
void AdcGetCurrentPower(uint8_t factor)
@ -255,14 +253,14 @@ void AdcShow(bool json)
}
}
else if (ADC0_MOIST == my_adc0) {
uint16_t adc_moist = AdcGetMoist();
else if (ADC0_RANGE == my_adc0) {
uint16_t adc_range = AdcGetRange();
if (json) {
ResponseAppend_P(JSON_SNS_MOISTURE, "ANALOG", adc_moist);
ResponseAppend_P(JSON_SNS_RANGE, "ANALOG", adc_range);
#ifdef USE_WEBSERVER
} else {
WSContentSend_PD(HTTP_SNS_MOISTURE, "", adc_moist);
WSContentSend_PD(HTTP_SNS_RANGE, "", adc_range);
#endif // USE_WEBSERVER
}
}
@ -342,16 +340,20 @@ void CmndAdcParam(void)
if (XdrvMailbox.data_len) {
if ((ADC0_TEMP == XdrvMailbox.payload) ||
(ADC0_LIGHT == XdrvMailbox.payload) ||
(ADC0_MOIST == XdrvMailbox.payload) ||
(ADC0_RANGE == XdrvMailbox.payload) ||
(ADC0_CT_POWER == XdrvMailbox.payload)) {
if (strstr(XdrvMailbox.data, ",") != nullptr) { // Process parameter entry
char sub_string[XdrvMailbox.data_len +1];
// AdcParam 2, 32000, 10000, 3350
// AdcParam 3, 10000, 12518931, -1.405
// AdcParam 6, 0, 1023, 0, 100
Settings.adc_param_type = XdrvMailbox.payload;
Settings.adc_param1 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10);
Settings.adc_param2 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 3), nullptr, 10);
if (!ADC0_MOIST == XdrvMailbox.payload) {
if (ADC0_RANGE == XdrvMailbox.payload) {
Settings.adc_param3 = abs(strtol(subStr(sub_string, XdrvMailbox.data, ",", 4), nullptr, 10));
Settings.adc_param4 = abs(strtol(subStr(sub_string, XdrvMailbox.data, ",", 5), nullptr, 10));
} else {
Settings.adc_param3 = (int)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)) * 10000);
}
if (ADC0_CT_POWER == XdrvMailbox.payload) {
@ -373,7 +375,9 @@ void CmndAdcParam(void)
// AdcParam
Response_P(PSTR("{\"" D_CMND_ADCPARAM "\":[%d,%d,%d"), Settings.adc_param_type, Settings.adc_param1, Settings.adc_param2);
if (ADC0_MOIST != my_adc0) {
if (ADC0_RANGE == my_adc0) {
ResponseAppend_P(PSTR(",%d,%d"), Settings.adc_param3, Settings.adc_param4);
} else {
int value = Settings.adc_param3;
uint8_t precision;
for (precision = 4; precision > 0; precision--) {
@ -403,7 +407,7 @@ bool Xsns02(uint8_t function)
if ((ADC0_INPUT == my_adc0) ||
(ADC0_TEMP == my_adc0) ||
(ADC0_LIGHT == my_adc0) ||
(ADC0_MOIST == my_adc0) ||
(ADC0_RANGE == my_adc0) ||
(ADC0_CT_POWER == my_adc0)) {
switch (function) {
#ifdef USE_RULES

View File

@ -38,6 +38,9 @@
#define DS1621_COUNTER_REGISTER 0xA8 //exists on 1621 and 1624(undocumented)
#define DS1621_SLOPE_REGISTER 0xA9 //exists on 1624 and 1624(undocumented)
#define DS1621_CFG_1SHOT (1<<0)
#define DS1621_CFG_DONE (1<<7)
enum {
DS1624_TYPE_DS1624,
DS1624_TYPE_DS1621
@ -50,6 +53,8 @@ bool ds1624_init = false;
struct {
float value;
uint8_t type;
int errcnt;
int misscnt;
bool valid;
char name[9];
} ds1624_sns[DS1624_MAX_SENSORS];
@ -58,6 +63,17 @@ uint32_t DS1624_Idx2Addr(uint32_t idx) {
return 0x48 + idx;
}
int DS1624_Restart(uint8_t config, uint32_t idx) {
uint32_t addr = DS1624_Idx2Addr(idx);
if ((config & 1) == 1) {
config &= ~(DS1621_CFG_DONE|DS1621_CFG_1SHOT);
I2cWrite8(addr, DS1624_CONF_REGISTER, config); // 1shot off
delay(10); // by spec after writing
AddLog_P2(LOG_LEVEL_ERROR, "%s addr %x is reset, reconfig: %x", ds1624_sns[idx].name, addr, config);
}
I2cValidRead(addr, DS1624_START_REGISTER, 1);
}
void DS1624_HotPlugUp(uint32_t idx)
{
uint32_t addr = DS1624_Idx2Addr(idx);
@ -75,12 +91,9 @@ void DS1624_HotPlugUp(uint32_t idx)
I2cSetActiveFound(addr, ds1624_sns[idx].name);
ds1624_sns[idx].valid = true;
if ((config & 1) == 1) {
config &= 0xfe;
I2cWrite8(addr, DS1624_CONF_REGISTER, config); // 1show off
delay(10); // by spec after writing
}
I2cValidRead(addr, DS1624_START_REGISTER, 1); // FIXME 0 must read, but 0 isn't work for tasmota
ds1624_sns[idx].errcnt = 0;
ds1624_sns[idx].misscnt = 0;
DS1624_Restart(config,idx);
AddLog_P2(LOG_LEVEL_INFO, "Hot Plug %s addr %x config: %x", ds1624_sns[idx].name, addr, config);
}
}
@ -98,16 +111,37 @@ bool DS1624GetTemp(float *value, int idx)
{
uint32_t addr = DS1624_Idx2Addr(idx);
uint8_t config;
if (!I2cValidRead8(&config, addr, DS1624_CONF_REGISTER)) {
ds1624_sns[idx].misscnt++;
AddLog_P2(LOG_LEVEL_INFO, "%s device missing (errors: %i)", ds1624_sns[idx].name, ds1624_sns[idx].misscnt);
return false;
}
ds1624_sns[idx].misscnt=0;
if (config & (DS1621_CFG_1SHOT|DS1621_CFG_DONE)) {
ds1624_sns[idx].errcnt++;
AddLog_P2(LOG_LEVEL_INFO, "%s config error, restart... (errors: %i)", ds1624_sns[idx].name, ds1624_sns[idx].errcnt);
DS1624_Restart(config, idx);
return false;
}
uint16_t t;
if (!I2cValidRead16(&t, DS1624_Idx2Addr(idx), DS1624_TEMP_REGISTER)) { return false; }
*value = ((float)(int8_t)(t>>8)) + ((t>>4)&0xf)*0.0625;
if (ds1624_sns[idx].type == DS1624_TYPE_DS1621) { // Higher resolution
if (ds1624_sns[idx].type == DS1624_TYPE_DS1624) {
*value = ((float)(int8_t)(t>>8)) + ((t>>4)&0xf)*0.0625;
} else { //type == DS1624_TYPE_DS1621
// Datasheet for ds1621 is wrong for high resolution, real is:
*value = ((float)(int8_t)(t>>8));
uint8_t remain;
if (!I2cValidRead8(&remain, addr, DS1621_COUNTER_REGISTER)) { return true; }
uint8_t perc;
if (!I2cValidRead8(&perc, addr, DS1621_SLOPE_REGISTER)) { return true; }
*value += ((float)perc - (float)remain)/((float)perc) - 0.25;
float fix=(float)(perc - remain)/(float)perc;
*value+=fix;
}
ds1624_sns[idx].errcnt=0;
config &= ~(DS1621_CFG_DONE);
I2cWrite8(addr, DS1624_CONF_REGISTER, config);
return true;
}
@ -120,9 +154,11 @@ void DS1624HotPlugScan(void)
if (I2cActive(addr) && !ds1624_sns[idx].valid) {
continue; // is busy by another driver
}
if (!I2cValidRead16(&t, DS1624_Idx2Addr(idx), DS1624_TEMP_REGISTER)) {
DS1624_HotPlugDown(idx);
continue;
if (ds1624_sns[idx].valid) {
if ((ds1624_sns[idx].misscnt>2)||(ds1624_sns[idx].errcnt>2)) {
DS1624_HotPlugDown(idx);
continue;
}
}
DS1624_HotPlugUp(idx);
}