diff --git a/.github/workflows/CI_github_ESP32.yml b/.github/workflows/CI_github_ESP32.yml
index 9e8312b51..28221fb20 100644
--- a/.github/workflows/CI_github_ESP32.yml
+++ b/.github/workflows/CI_github_ESP32.yml
@@ -21,6 +21,23 @@ jobs:
platformio run -e tasmota32
+ tasmota32-webcam:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: Set up Python
+ uses: actions/setup-python@v1
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -U platformio
+ platformio upgrade --dev
+ platformio update
+ - name: Run PlatformIO
+ run: |
+ cp platformio_override_sample.ini platformio_override.ini
+ platformio run -e tasmota32-webcam
+
tasmota32-minimal:
runs-on: ubuntu-latest
steps:
diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini
index e14e570c8..5a66a3efb 100644
--- a/platformio_override_sample.ini
+++ b/platformio_override_sample.ini
@@ -27,6 +27,7 @@ default_envs =
; tasmota-display
; tasmota-ir
; tasmota32
+; tasmota32-webcam
; tasmota32-minimal
; tasmota32-lite
; tasmota32-knx
@@ -160,13 +161,13 @@ build_type = debug
[common32]
platform = espressif32@1.12.0
platform_packages = tool-esptoolpy@1.20800.0
-board = wemos_d1_mini32
+board = esp32dev
board_build.ldscript = esp32_out.ld
board_build.partitions = esp32_partition_app1984k_spiffs64k.csv
board_build.flash_mode = ${common.board_build.flash_mode}
board_build.f_cpu = ${common.board_build.f_cpu}
build_unflags = ${common.build_unflags}
- -Wpointer-arith
+ -Wpointer-arith
monitor_speed = ${common.monitor_speed}
upload_port = ${common.upload_port}
upload_resetmethod = ${common.upload_resetmethod}
diff --git a/platformio_tasmota_env32.ini b/platformio_tasmota_env32.ini
index 0c2e40fe3..f4f99282c 100644
--- a/platformio_tasmota_env32.ini
+++ b/platformio_tasmota_env32.ini
@@ -17,6 +17,12 @@ lib_ignore = ${common32.lib_ignore}
build_unflags = ${common32.build_unflags}
build_flags = ${common32.build_flags}
+[env:tasmota32-webcam]
+extends = env:tasmota32
+board = esp32cam
+board_build.f_cpu = 240000000L
+build_flags = ${common32.build_flags} -DFIRMWARE_WEBCAM
+
[env:tasmota32-minimal]
extends = env:tasmota32
build_flags = ${common32.build_flags} -DFIRMWARE_MINIMAL
diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md
index 46f92021c..60c631833 100644
--- a/tasmota/CHANGELOG.md
+++ b/tasmota/CHANGELOG.md
@@ -21,6 +21,7 @@
- Change minimum PWM Frequency from 100 Hz to 40 Hz
- Change PWM updated to the latest version of Arduino PR #7231
- Change Philips Hue emulation now exposes modelId and manufacturerId
+- Add Zigbee support for router and end-device mode
### 8.2.0.5 20200425
diff --git a/tasmota/i18n.h b/tasmota/i18n.h
index 4fd7fe7d3..089a9b55f 100644
--- a/tasmota/i18n.h
+++ b/tasmota/i18n.h
@@ -123,6 +123,8 @@
#define D_JSON_PROBETEMPERATURE "ProbeTemperature"
#define D_JSON_PROGRAMFLASHSIZE "ProgramFlashSize"
#define D_JSON_PROGRAMSIZE "ProgramSize"
+#define D_JSON_PSRMAXMEMORY "PsrMax"
+#define D_JSON_PSRFREEMEMORY "PsrFree"
#define D_JSON_REFERENCETEMPERATURE "ReferenceTemperature"
#define D_JSON_REMAINING "Remaining"
#define D_JSON_RESET "Reset"
@@ -484,7 +486,7 @@
// Commands xdrv_23_zigbee.ino
#define D_PRFX_ZB "Zb"
-#define D_ZIGBEE_NOT_STARTED "Zigbee not started (yet)"
+#define D_ZIGBEE_NOT_STARTED "Zigbee not started"
#define D_CMND_ZIGBEE_PERMITJOIN "PermitJoin"
#define D_CMND_ZIGBEE_STATUS "Status"
#define D_CMND_ZIGBEE_RESET "Reset"
diff --git a/tasmota/language/bg_BG.h b/tasmota/language/bg_BG.h
index a27b150f9..03ae5bd6c 100644
--- a/tasmota/language/bg_BG.h
+++ b/tasmota/language/bg_BG.h
@@ -98,6 +98,8 @@
#define D_FILE "Файл"
#define D_FLOW_RATE "Дебит"
#define D_FREE_MEMORY "Свободна памет"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Честота"
#define D_GAS "Газ"
#define D_GATEWAY "Шлюз"
diff --git a/tasmota/language/cs_CZ.h b/tasmota/language/cs_CZ.h
index a048c890a..71a907a81 100644
--- a/tasmota/language/cs_CZ.h
+++ b/tasmota/language/cs_CZ.h
@@ -98,6 +98,8 @@
#define D_FILE "Soubor"
#define D_FLOW_RATE "Flow rate"
#define D_FREE_MEMORY "Volná paměť"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Kmitočet"
#define D_GAS "Plyn"
#define D_GATEWAY "Výchozí brána"
diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h
index f1150405e..fdd1ec18d 100644
--- a/tasmota/language/de_DE.h
+++ b/tasmota/language/de_DE.h
@@ -98,6 +98,8 @@
#define D_FILE "Datei"
#define D_FLOW_RATE "Durchflussmenge"
#define D_FREE_MEMORY "Freier Arbeitsspeicher"
+#define D_PSR_MAX_MEMORY "PS-RAM Speicher"
+#define D_PSR_FREE_MEMORY "PS-RAM freier Speicher"
#define D_FREQUENCY "Frequenz"
#define D_GAS "Gas"
#define D_GATEWAY "Gateway"
diff --git a/tasmota/language/el_GR.h b/tasmota/language/el_GR.h
index 4d9424b5d..ddd1d5b0b 100644
--- a/tasmota/language/el_GR.h
+++ b/tasmota/language/el_GR.h
@@ -98,6 +98,8 @@
#define D_FILE "Αρχείο"
#define D_FLOW_RATE "Flow rate"
#define D_FREE_MEMORY "Ελεύθερη μνήμη"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Συχνότητα"
#define D_GAS "Αέριο"
#define D_GATEWAY "Πύλη"
diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h
index 072148a73..0396ca3e6 100644
--- a/tasmota/language/en_GB.h
+++ b/tasmota/language/en_GB.h
@@ -98,6 +98,8 @@
#define D_FILE "File"
#define D_FLOW_RATE "Flow rate"
#define D_FREE_MEMORY "Free Memory"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Frequency"
#define D_GAS "Gas"
#define D_GATEWAY "Gateway"
diff --git a/tasmota/language/es_ES.h b/tasmota/language/es_ES.h
index 2d82250ec..0602c3634 100644
--- a/tasmota/language/es_ES.h
+++ b/tasmota/language/es_ES.h
@@ -98,6 +98,8 @@
#define D_FILE "Archivo"
#define D_FLOW_RATE "Caudal"
#define D_FREE_MEMORY "Memoria Libre"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Frecuencia"
#define D_GAS "Gas"
#define D_GATEWAY "Gateway"
diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h
index 342dcef80..c620572ca 100644
--- a/tasmota/language/fr_FR.h
+++ b/tasmota/language/fr_FR.h
@@ -98,6 +98,8 @@
#define D_FILE "Fichier"
#define D_FLOW_RATE "Débit"
#define D_FREE_MEMORY "Mémoire libre"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Fréquence"
#define D_GAS "Gaz"
#define D_GATEWAY "Passerelle"
diff --git a/tasmota/language/he_HE.h b/tasmota/language/he_HE.h
index 9402c722b..4802b529e 100644
--- a/tasmota/language/he_HE.h
+++ b/tasmota/language/he_HE.h
@@ -98,6 +98,8 @@
#define D_FILE "קובץ"
#define D_FLOW_RATE "Flow rate"
#define D_FREE_MEMORY "זכרון פנוי"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "תדר"
#define D_GAS "גז"
#define D_GATEWAY "שער"
diff --git a/tasmota/language/hu_HU.h b/tasmota/language/hu_HU.h
index 9ef7d57c2..ee45ae083 100644
--- a/tasmota/language/hu_HU.h
+++ b/tasmota/language/hu_HU.h
@@ -98,6 +98,8 @@
#define D_FILE "Fájl"
#define D_FLOW_RATE "Flow rate"
#define D_FREE_MEMORY "Szabad memória"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Frekvencia"
#define D_GAS "Gáz"
#define D_GATEWAY "Átjáró"
diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h
index c9710ef01..88acefc92 100644
--- a/tasmota/language/it_IT.h
+++ b/tasmota/language/it_IT.h
@@ -98,6 +98,8 @@
#define D_FILE "File"
#define D_FLOW_RATE "Flusso dati"
#define D_FREE_MEMORY "Memoria Libera"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Frequenza"
#define D_GAS "Gas"
#define D_GATEWAY "Gateway"
diff --git a/tasmota/language/ko_KO.h b/tasmota/language/ko_KO.h
index 18633f373..3e3bd17e0 100644
--- a/tasmota/language/ko_KO.h
+++ b/tasmota/language/ko_KO.h
@@ -98,6 +98,8 @@
#define D_FILE "파일"
#define D_FLOW_RATE "Flow rate"
#define D_FREE_MEMORY "남은 메모리"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Frequency"
#define D_GAS "가스"
#define D_GATEWAY "게이트웨이"
diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h
index b0ae25e7b..49efcb622 100644
--- a/tasmota/language/nl_NL.h
+++ b/tasmota/language/nl_NL.h
@@ -98,6 +98,8 @@
#define D_FILE "Bestand"
#define D_FLOW_RATE "Debiet"
#define D_FREE_MEMORY "Vrij geheugen"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Frequentie"
#define D_GAS "Gas"
#define D_GATEWAY "Gateway"
diff --git a/tasmota/language/pl_PL.h b/tasmota/language/pl_PL.h
index 2c6e38e7c..d9d05fbeb 100644
--- a/tasmota/language/pl_PL.h
+++ b/tasmota/language/pl_PL.h
@@ -98,6 +98,8 @@
#define D_FILE "Plik"
#define D_FLOW_RATE "Przepływ"
#define D_FREE_MEMORY "Wolna pamięć"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Częstotliwość"
#define D_GAS "Gas"
#define D_GATEWAY "Brama"
diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h
index d1e51a077..4fb48c04d 100644
--- a/tasmota/language/pt_BR.h
+++ b/tasmota/language/pt_BR.h
@@ -98,6 +98,8 @@
#define D_FILE "Arquivo"
#define D_FLOW_RATE "Quociente de vazão"
#define D_FREE_MEMORY "Memória livre"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Frequência"
#define D_GAS "Gás"
#define D_GATEWAY "Gateway"
diff --git a/tasmota/language/pt_PT.h b/tasmota/language/pt_PT.h
index 1227a738a..232b7494d 100644
--- a/tasmota/language/pt_PT.h
+++ b/tasmota/language/pt_PT.h
@@ -98,6 +98,8 @@
#define D_FILE "Ficheiro"
#define D_FLOW_RATE "Taxa de Fluxo"
#define D_FREE_MEMORY "Memoria Livre"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Frequência"
#define D_GAS "Gás"
#define D_GATEWAY "Gateway"
diff --git a/tasmota/language/ro_RO.h b/tasmota/language/ro_RO.h
index 616e1d9b1..79c66d779 100644
--- a/tasmota/language/ro_RO.h
+++ b/tasmota/language/ro_RO.h
@@ -98,6 +98,8 @@
#define D_FILE "Fișier"
#define D_FLOW_RATE "Debit"
#define D_FREE_MEMORY "Memorie Liberă"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Frecvență"
#define D_GAS "Gaz"
#define D_GATEWAY "Gateway"
diff --git a/tasmota/language/ru_RU.h b/tasmota/language/ru_RU.h
index 7a46a56db..b7d8adcb6 100644
--- a/tasmota/language/ru_RU.h
+++ b/tasmota/language/ru_RU.h
@@ -98,6 +98,8 @@
#define D_FILE "Файл"
#define D_FLOW_RATE "Flow rate"
#define D_FREE_MEMORY "Свободная память"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Frequency"
#define D_GAS "Газ"
#define D_GATEWAY "Шлюз"
diff --git a/tasmota/language/sk_SK.h b/tasmota/language/sk_SK.h
index 3bd5e2ff8..9db574bf8 100644
--- a/tasmota/language/sk_SK.h
+++ b/tasmota/language/sk_SK.h
@@ -98,6 +98,8 @@
#define D_FALSE "Nepravda"
#define D_FILE "Súbor"
#define D_FREE_MEMORY "Voľná pamäť"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Frekvencia"
#define D_GAS "Plyn"
#define D_GATEWAY "Predvolená brána"
diff --git a/tasmota/language/sv_SE.h b/tasmota/language/sv_SE.h
index d5ad82e6f..da8355215 100644
--- a/tasmota/language/sv_SE.h
+++ b/tasmota/language/sv_SE.h
@@ -98,6 +98,8 @@
#define D_FILE "Fil"
#define D_FLOW_RATE "Flow rate"
#define D_FREE_MEMORY "Ledigt minne"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Frekvens"
#define D_GAS "Gas"
#define D_GATEWAY "Gateway"
diff --git a/tasmota/language/tr_TR.h b/tasmota/language/tr_TR.h
index abbc68dbf..9b205954b 100644
--- a/tasmota/language/tr_TR.h
+++ b/tasmota/language/tr_TR.h
@@ -98,6 +98,8 @@
#define D_FILE "Dosya"
#define D_FLOW_RATE "Flow rate"
#define D_FREE_MEMORY "Boş Hafıza"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Frekans"
#define D_GAS "Gas"
#define D_GATEWAY "Geçit"
diff --git a/tasmota/language/uk_UA.h b/tasmota/language/uk_UA.h
index 915925d1d..8c566eac3 100644
--- a/tasmota/language/uk_UA.h
+++ b/tasmota/language/uk_UA.h
@@ -98,6 +98,8 @@
#define D_FILE "Файл"
#define D_FLOW_RATE "Потік"
#define D_FREE_MEMORY "Вільна память"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Частота"
#define D_GAS "Газ"
#define D_GATEWAY "Шлюз"
diff --git a/tasmota/language/zh_CN.h b/tasmota/language/zh_CN.h
index 5e76e6069..143e49aec 100644
--- a/tasmota/language/zh_CN.h
+++ b/tasmota/language/zh_CN.h
@@ -98,6 +98,8 @@
#define D_FILE "文件:"
#define D_FLOW_RATE "Flow rate"
#define D_FREE_MEMORY "空闲内存"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "频率"
#define D_GAS "气体"
#define D_GATEWAY "网关"
diff --git a/tasmota/language/zh_TW.h b/tasmota/language/zh_TW.h
index e04296c5a..5c96ec688 100644
--- a/tasmota/language/zh_TW.h
+++ b/tasmota/language/zh_TW.h
@@ -98,6 +98,8 @@
#define D_FILE "文件:"
#define D_FLOW_RATE "Flow rate"
#define D_FREE_MEMORY "可用記憶體"
+#define D_PSR_MAX_MEMORY "PS-RAM Memory"
+#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
#define D_FREQUENCY "Frequency"
#define D_GAS "氣體"
#define D_GATEWAY "網關"
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index 2e3eb4a5f..150a200e0 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -640,10 +640,13 @@
// -- Zigbee interface ----------------------------
//#define USE_ZIGBEE // Enable serial communication with Zigbee CC2530 flashed with ZNP (+49k code, +3k mem)
#define USE_ZIGBEE_PANID 0x1A63 // arbitrary PAN ID for Zigbee network, must be unique in the home
+ // if PANID == 0xFFFF, then the device will act as a Zigbee router, the parameters below are ignored
+ // if PANID == 0xFFFE, then the device will act as a Zigbee end-device (non-router), the parameters below are ignored
#define USE_ZIGBEE_EXTPANID 0xCCCCCCCCCCCCCCCCL // arbitrary extended PAN ID
#define USE_ZIGBEE_CHANNEL 11 // Zigbee Channel (11-26)
#define USE_ZIGBEE_PRECFGKEY_L 0x0F0D0B0907050301L // note: changing requires to re-pair all devices
#define USE_ZIGBEE_PRECFGKEY_H 0x0D0C0A0806040200L // note: changing requires to re-pair all devices
+
#define USE_ZIGBEE_COALESCE_ATTR_TIMER 350 // timer to coalesce attribute values (in ms)
// -- Other sensors/drivers -----------------------
diff --git a/tasmota/sendemail.ino b/tasmota/sendemail.ino
index f04cbf668..0a4c4de0e 100644
--- a/tasmota/sendemail.ino
+++ b/tasmota/sendemail.ino
@@ -630,10 +630,10 @@ void send_message_txt(char *txt) {
uint32_t cnt;
uint8_t *buff;
uint32_t len,picmax;
- picmax=get_picstore(-1,0);
+ picmax=WcGetPicstore(-1,0);
cnt=*txt&7;
if (cnt<1 || cnt>picmax) cnt=1;
- len=get_picstore(cnt-1,&buff);
+ len=WcGetPicstore(cnt-1,&buff);
if (len) {
char str[12];
sprintf(str,"img_%1d.jpg",cnt+1);
diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino
index 1bd243754..cee21783e 100644
--- a/tasmota/support_command.ino
+++ b/tasmota/support_command.ino
@@ -461,6 +461,9 @@ void CmndStatus(void)
if ((0 == payload) || (4 == payload)) {
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS4_MEMORY "\":{\"" D_JSON_PROGRAMSIZE "\":%d,\"" D_JSON_FREEMEMORY "\":%d,\"" D_JSON_HEAPSIZE "\":%d,\""
+#ifdef ESP32
+ D_JSON_PSRMAXMEMORY "\":%d,\"" D_JSON_PSRFREEMEMORY "\":%d,"
+#endif
D_JSON_PROGRAMFLASHSIZE "\":%d,\"" D_JSON_FLASHSIZE "\":%d"
#ifdef ESP8266
",\"" D_JSON_FLASHCHIPID "\":\"%06X\""
@@ -468,6 +471,9 @@ void CmndStatus(void)
",\"" D_JSON_FLASHMODE "\":%d,\""
D_JSON_FEATURES "\":[\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\"]"),
ESP_getSketchSize()/1024, ESP.getFreeSketchSpace()/1024, ESP_getFreeHeap()/1024,
+#ifdef ESP32
+ ESP.getPsramSize()/1024, ESP.getFreePsram()/1024,
+#endif
ESP.getFlashChipSize()/1024, ESP.getFlashChipRealSize()/1024
#ifdef ESP8266
, ESP.getFlashChipId()
diff --git a/tasmota/tasmota_configurations.h b/tasmota/tasmota_configurations.h
index f0444efbb..37ac20237 100644
--- a/tasmota/tasmota_configurations.h
+++ b/tasmota/tasmota_configurations.h
@@ -638,4 +638,8 @@
#undef USE_DEBUG_DRIVER // Disable debug code
#endif // FIRMWARE_MINIMAL
+#ifdef ESP32
+#include "tasmota_configurations_ESP32.h"
+#endif // ESP32
+
#endif // _TASMOTA_CONFIGURATIONS_H_
diff --git a/tasmota/tasmota_configurations_ESP32.h b/tasmota/tasmota_configurations_ESP32.h
new file mode 100644
index 000000000..b551c78ef
--- /dev/null
+++ b/tasmota/tasmota_configurations_ESP32.h
@@ -0,0 +1,40 @@
+/*
+ tasmota_configurations_ESP32.h - ESP32 only Configurations for Tasmota
+
+ Copyright (C) 2020 Theo Arends
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifndef _TASMOTA_CONFIGURATIONS_ESP32_H_
+#define _TASMOTA_CONFIGURATIONS_ESP32_H_
+
+#ifdef ESP32
+
+/*********************************************************************************************\
+ * [tasmota32-webcam.bin]
+ * Provide an image with useful supported sensors enabled
+\*********************************************************************************************/
+
+#ifdef FIRMWARE_WEBCAM
+
+#undef CODE_IMAGE_STR
+#define CODE_IMAGE_STR "webcam"
+
+#define USE_WEBCAM
+#endif // FIRMWARE_WEBCAM
+
+#endif // ESP32
+
+#endif // _TASMOTA_CONFIGURATIONS_ESP32_H_
diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino
index e7a94893d..75f309569 100644
--- a/tasmota/xdrv_01_webserver.ino
+++ b/tasmota/xdrv_01_webserver.ino
@@ -2284,6 +2284,12 @@ void HandleInformation(void)
WSContentSend_P(PSTR("}1" D_PROGRAM_SIZE "}2%dkB"), ESP_getSketchSize() / 1024);
WSContentSend_P(PSTR("}1" D_FREE_PROGRAM_SPACE "}2%dkB"), ESP.getFreeSketchSpace() / 1024);
WSContentSend_P(PSTR("}1" D_FREE_MEMORY "}2%dkB"), freeMem / 1024);
+#ifdef ESP32
+ if (psramFound()) {
+ WSContentSend_P(PSTR("}1" D_PSR_MAX_MEMORY "}2%dkB"), ESP.getPsramSize() / 1024);
+ WSContentSend_P(PSTR("}1" D_PSR_FREE_MEMORY "}2%dkB"), ESP.getFreePsram() / 1024);
+ }
+#endif
WSContentSend_P(PSTR(""));
WSContentSend_P(HTTP_SCRIPT_INFO_END);
diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino
index d16c0bd31..44207609d 100755
--- a/tasmota/xdrv_10_scripter.ino
+++ b/tasmota/xdrv_10_scripter.ino
@@ -1407,9 +1407,9 @@ chknext:
if (ind>=SFS_MAX) ind=SFS_MAX-1;
if (glob_script_mem.file_flags[ind].is_open) {
uint8_t *buff;
- float maxps=get_picstore(-1,0);
+ float maxps=WcGetPicstore(-1,0);
if (fvar<1 || fvar>maxps) fvar=1;
- uint32_t len=get_picstore(fvar-1, &buff);
+ uint32_t len=WcGetPicstore(fvar-1, &buff);
if (len) {
//glob_script_mem.files[ind].seek(0,SeekEnd);
fvar=glob_script_mem.files[ind].write(buff,len);
@@ -2056,13 +2056,13 @@ chknext:
case 0:
{ float fvar2;
lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
- fvar=wc_setup(fvar2);
+ fvar=WcSetup(fvar2);
}
break;
case 1:
{ float fvar2;
lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
- fvar=wc_get_frame(fvar2);
+ fvar=WcGetFrame(fvar2);
}
break;
case 2:
@@ -2070,32 +2070,32 @@ chknext:
lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
SCRIPT_SKIP_SPACES
lp=GetNumericResult(lp,OPER_EQU,&fvar3,0);
- fvar=wc_set_options(fvar2,fvar3);
+ fvar=WcSetOptions(fvar2,fvar3);
}
break;
case 3:
- fvar=wc_get_width();
+ fvar=WcGetWidth();
break;
case 4:
- fvar=wc_get_height();
+ fvar=WcGetHeight();
break;
case 5:
{ float fvar2;
lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
- fvar=wc_set_streamserver(fvar2);
+ fvar=WcSetStreamserver(fvar2);
}
break;
case 6:
{ float fvar2;
lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
- fvar=wc_set_motion_detect(fvar2);
+ fvar=WcSetMotionDetect(fvar2);
}
break;
#ifdef USE_FACE_DETECT
case 7:
{ float fvar2;
lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
- fvar=wc_set_face_detect(fvar2);
+ fvar=WcSetFaceDetect(fvar2);
}
break;
#endif
@@ -2674,6 +2674,40 @@ exit10:
return lp;
}
+#ifdef ESP32
+
+TimerHandle_t beep_th;
+void StopBeep( TimerHandle_t xTimer );
+
+void StopBeep( TimerHandle_t xTimer ) {
+ ledcWriteTone(7,0);
+ xTimerStop(xTimer, 0);
+}
+
+void esp32_beep(int32_t freq ,uint32_t len) {
+ if (freq<0) {
+ ledcSetup(7,500,10);
+ ledcAttachPin(-freq,7);
+ ledcWriteTone(7,0);
+ if (!beep_th) {
+ beep_th = xTimerCreate("beep",100,pdFALSE,( void * ) 0,StopBeep);
+ }
+ } else {
+ if (!beep_th) return;
+ if (!freq) {
+ ledcWriteTone(7,0);
+ xTimerStop(beep_th, 10);
+ return;
+ }
+ if (len < 10) return;
+ if (xTimerIsTimerActive(beep_th)) return;
+ ledcWriteTone(7,freq);
+ uint32_t ticks = pdMS_TO_TICKS(len);
+ xTimerChangePeriod( beep_th, ticks, 10);
+ }
+}
+#endif // ESP32
+
//#define IFTHEN_DEBUG
#define IF_NEST 8
@@ -2706,11 +2740,12 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) {
}
JsonObject *jo=0;
-
+ DynamicJsonBuffer jsonBuffer; // on heap
+ JsonObject &jobj=jsonBuffer.parseObject(js);
if (js) {
- DynamicJsonBuffer jsonBuffer; // on heap
- JsonObject &jobj=jsonBuffer.parseObject(js);
jo=&jobj;
+ } else {
+ jo=0;
}
char *lp=glob_script_mem.scriptptr;
@@ -2995,13 +3030,10 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) {
else if (!strncmp(lp,"beep(",5)) {
lp+=5;
lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
- if (fvar<0) {
- ledcSetup(7,500,10);
- ledcAttachPin(-fvar,7);
- ledcWriteTone(7,0);
- } else {
- ledcWriteTone(7,fvar);
- }
+ SCRIPT_SKIP_SPACES
+ float fvar1;
+ lp=GetNumericResult(lp,OPER_EQU,&fvar1,0);
+ esp32_beep(fvar,fvar1);
lp++;
goto next_line;
}
diff --git a/tasmota/xdrv_23_zigbee_7_statemachine.ino b/tasmota/xdrv_23_zigbee_7_statemachine.ino
index 51a5af99a..e2f0f039d 100644
--- a/tasmota/xdrv_23_zigbee_7_statemachine.ino
+++ b/tasmota/xdrv_23_zigbee_7_statemachine.ino
@@ -33,6 +33,7 @@ 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_SCANNING = 40; // State change
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
@@ -79,6 +80,7 @@ enum Zigbee_StateMachine_Instruction_Set {
// 12 bytes instructions
ZGB_INSTR_12_BYTES = 0xF0,
+ ZGB_INSTR_WAIT_UNTIL_CALL, // wait until the specified message is received and call function upon receive, ignore all others
ZGB_INSTR_WAIT_RECV_CALL, // wait for a filtered message and call function upon receive
};
@@ -98,15 +100,25 @@ enum Zigbee_StateMachine_Instruction_Set {
#define ZI_SEND(m) { .i = { ZGB_INSTR_SEND, sizeof(m), 0x0000} }, { .p = (const void*)(m) },
#define ZI_WAIT_RECV(x, m) { .i = { ZGB_INSTR_WAIT_RECV, sizeof(m), (x)} }, { .p = (const void*)(m) },
#define ZI_WAIT_UNTIL(x, m) { .i = { ZGB_INSTR_WAIT_UNTIL, sizeof(m), (x)} }, { .p = (const void*)(m) },
-#define ZI_WAIT_RECV_FUNC(x, m, f) { .i = { ZGB_INSTR_WAIT_RECV_CALL, sizeof(m), (x)} }, { .p = (const void*)(m) }, { .p = (const void*)(f) },
+#define ZI_WAIT_UNTIL_FUNC(x, m, f) { .i = { ZGB_INSTR_WAIT_UNTIL_CALL, sizeof(m), (x)} }, { .p = (const void*)(m) }, { .p = (const void*)(f) },
+#define ZI_WAIT_RECV_FUNC(x, m, f) { .i = { ZGB_INSTR_WAIT_RECV_CALL, sizeof(m), (x)} }, { .p = (const void*)(m) }, { .p = (const void*)(f) },
// Labels used in the State Machine -- internal only
-const uint8_t ZIGBEE_LABEL_START = 10; // Start ZNP
+const uint8_t ZIGBEE_LABEL_INIT_COORD = 10; // Start ZNP as coordinator
+const uint8_t ZIGBEE_LABEL_START_COORD = 11; // Start ZNP as coordinator
+const uint8_t ZIGBEE_LABEL_INIT_ROUTER = 12; // Start ZNP as router
+const uint8_t ZIGBEE_LABEL_START_ROUTER = 13; // Start ZNP as router
+const uint8_t ZIGBEE_LABEL_INIT_DEVICE = 14; // Start ZNP as end-device
+const uint8_t ZIGBEE_LABEL_START_DEVICE = 15; // Start ZNP as end-device
const uint8_t ZIGBEE_LABEL_READY = 20; // goto label 20 for main loop
const uint8_t ZIGBEE_LABEL_MAIN_LOOP = 21; // main loop
const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_CLOSE = 30; // disable permit join
const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60 = 31; // enable permit join for 60 seconds
const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX = 32; // enable permit join for 60 seconds
+// factory reset
+const uint8_t ZIGBEE_LABEL_FACT_RESET_COORD = 50; // main loop
+const uint8_t ZIGBEE_LABEL_FACT_RESET_ROUTER = 51; // main loop
+const uint8_t ZIGBEE_LABEL_FACT_RESET_DEVICE = 52; // main loop
// errors
const uint8_t ZIGBEE_LABEL_ABORT = 99; // goto label 99 in case of fatal error
const uint8_t ZIGBEE_LABEL_UNSUPPORTED_VERSION = 98; // Unsupported ZNP version
@@ -198,15 +210,26 @@ ZBM(ZBS_PFGKEN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PRECFGKEYS_ENABLE
ZBM(ZBR_PFGKEN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_PRECFGKEYS_ENABLE,
0x01 /* len */, 0x00 ) // 660400630100
+ZBM(ZBS_LOGTYPE, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_LOGICAL_TYPE ) // 260487
+ZBM(ZBS_LOGTYPE_COORD, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_LOGICAL_TYPE,
+ 0x01 /* len */, 0x00 ) // 660400870100 - coordinator
+ZBM(ZBS_LOGTYPE_ROUTER, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_LOGICAL_TYPE,
+ 0x01 /* len */, 0x01 ) // 660400870101 - router
+ZBM(ZBS_LOGTYPE_DEVICE, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CONF_LOGICAL_TYPE,
+ 0x01 /* len */, 0x02 ) // 660400870102 - device
+
+
// commands to "format" the device
// Write configuration - write success
ZBM(ZBR_W_OK, Z_SRSP | Z_SAPI, SAPI_WRITE_CONFIGURATION, Z_SUCCESS ) // 660500 - Write Configuration
ZBM(ZBR_WNV_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_WRITE, Z_SUCCESS ) // 610900 - NV Write
// Factory reset
-ZBM(ZBS_FACTRES, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 /* len */, 0x02 ) // 2605030102
+ZBM(ZBS_FACTRES, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 /* len */, 0x03 ) // 2605030103
// Write PAN ID
ZBR(ZBS_W_PAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PANID, 0x02 /* len */, Z_B0(USE_ZIGBEE_PANID), Z_B1(USE_ZIGBEE_PANID) ) // 26058302xxxx
+// Write Universal PAN ID
+ZBR(ZBS_W_ALL_PAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PANID, 0x02 /* len */, Z_B0(0xFFFF), Z_B1(0xFFFF) ) // 26058302FFFF
// Write EXT PAN ID
ZBR(ZBS_W_EXTPAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_EXTENDED_PAN_ID, 0x08 /* len */,
Z_B0(USE_ZIGBEE_EXTPANID), Z_B1(USE_ZIGBEE_EXTPANID), Z_B2(USE_ZIGBEE_EXTPANID), Z_B3(USE_ZIGBEE_EXTPANID),
@@ -216,8 +239,17 @@ ZBR(ZBS_W_EXTPAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_EXTENDED_PAN_I
ZBR(ZBS_W_CHANN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_CHANLIST, 0x04 /* len */,
Z_B0(USE_ZIGBEE_CHANNEL_MASK), Z_B1(USE_ZIGBEE_CHANNEL_MASK), Z_B2(USE_ZIGBEE_CHANNEL_MASK), Z_B3(USE_ZIGBEE_CHANNEL_MASK),
/*0x00, 0x08, 0x00, 0x00*/ ) // 26058404xxxxxxxx
+// Write All Channels
+const uint32_t ZB_ALL_CHANNELS = 0x07FFF800;
+ZBR(ZBS_W_ALL_CHANN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_CHANLIST, 0x04 /* len */,
+ Z_B0(ZB_ALL_CHANNELS), Z_B1(ZB_ALL_CHANNELS), Z_B2(ZB_ALL_CHANNELS), Z_B3(ZB_ALL_CHANNELS),
+ /*0x00, 0x08, 0x00, 0x00*/ ) // 2605840400F8FF7F
// Write Logical Type = 00 = coordinator
-ZBM(ZBS_W_LOGTYP, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_LOGICAL_TYPE, 0x01 /* len */, 0x00 ) // 2605870100
+ZBM(ZBS_W_LOGTYP_COORD, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_LOGICAL_TYPE, 0x01 /* len */, 0x00 ) // 2605870100
+// Write Logical Type = 01 = router
+ZBM(ZBS_W_LOGTYP_ROUTER, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_LOGICAL_TYPE, 0x01 /* len */, 0x01 ) // 2605870101
+// Write Logical Type = 02 = device
+ZBM(ZBS_W_LOGTYP_DEVICE, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_LOGICAL_TYPE, 0x01 /* len */, 0x02 ) // 2605870102
// Write precfgkey
ZBR(ZBS_W_PFGK, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PRECFGKEY,
0x10 /* len */,
@@ -251,7 +283,9 @@ ZBM(ZBS_WNV_ZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, Z_B0(ZNP_HAS_CONFIGURED),
// Z_ZDO:startupFromApp
ZBM(ZBS_STARTUPFROMAPP, Z_SREQ | Z_ZDO, ZDO_STARTUP_FROM_APP, 100, 0 /* delay */) // 25406400
ZBM(ZBR_STARTUPFROMAPP, Z_SRSP | Z_ZDO, ZDO_STARTUP_FROM_APP ) // 6540 + 01 for new network, 00 for exisitng network, 02 for error
-ZBM(AREQ_STARTUPFROMAPP, Z_AREQ | Z_ZDO, ZDO_STATE_CHANGE_IND, ZDO_DEV_ZB_COORD ) // 45C009 + 08 = starting, 09 = started
+ZBM(AREQ_STARTUPFROMAPP_COORD, Z_AREQ | Z_ZDO, ZDO_STATE_CHANGE_IND, ZDO_DEV_ZB_COORD ) // 45C009 + 08 = starting, 09 = started
+ZBM(AREQ_STARTUPFROMAPP_ROUTER, Z_AREQ | Z_ZDO, ZDO_STATE_CHANGE_IND, ZDO_DEV_ROUTER ) // 45C009 + 02 = looking PanID, 07 = started
+ZBM(AREQ_STARTUPFROMAPP_DEVICE, Z_AREQ | Z_ZDO, ZDO_STATE_CHANGE_IND, ZDO_DEV_END_DEVICE ) // 45C009 + 02 = looking PanID, 06 = started
// GetDeviceInfo
ZBM(ZBS_GETDEVICEINFO, Z_SREQ | Z_UTIL, Z_UTIL_GET_DEVICE_INFO ) // 2700
ZBM(ZBR_GETDEVICEINFO, Z_SRSP | Z_UTIL, Z_UTIL_GET_DEVICE_INFO, Z_SUCCESS ) // Ex= 6700.00.6263151D004B1200.0000.07.09.00
@@ -298,6 +332,16 @@ ZBM(ZBR_AF_REGISTER, Z_SRSP | Z_AF, AF_REGISTER, Z_SUCCESS) // 640000
ZBM(ZBS_AF_REGISTER0B, Z_SREQ | Z_AF, AF_REGISTER, 0x0B /* endpoint */, Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), // 2400040B050000000000
0x05, 0x00 /* AppDeviceId */, 0x00 /* AppDevVer */, 0x00 /* LatencyReq */,
0x00 /* AppNumInClusters */, 0x00 /* AppNumInClusters */)
+// Z_AF:register profile:104, ep:01 - main clusters for router or device
+ZBM(ZBS_AF_REGISTER_ALL, Z_SREQ | Z_AF, AF_REGISTER, 0x01 /* endpoint */, Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), // 24000401050000000000
+ 0x05, 0x00 /* AppDeviceId */, 0x00 /* AppDevVer */, 0x00 /* LatencyReq */,
+ 0x0E /* AppNumInClusters */, // actually all clusters will be received
+ 0x00,0x00, 0x04,0x00, 0x05,0x00, 0x06,0x00, // 0x0000, 0x0004, 0x0005, 0x0006
+ 0x07,0x00, 0x08,0x00, 0x0A,0x00, 0x02,0x01, // 0x0007, 0x0008, 0x000A, 0X0102
+ 0x00,0x03, 0x00,0x04, 0x02,0x04, 0x03,0x04, // 0x0300, 0x0400, 0x0402, 0x0403
+ 0x05,0x04, 0x06,0x04, // 0x0405, 0x0406
+ 0x00 /* AppNumInClusters */)
+
// Z_ZDO:mgmtPermitJoinReq
ZBM(ZBS_PERMITJOINREQ_CLOSE, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x02 /* AddrMode */, // 25360200000000
0x00, 0x00 /* DstAddr */, 0x00 /* Duration */, 0x00 /* TCSignificance */)
@@ -352,7 +396,9 @@ void Z_UpdateConfig(uint8_t zb_channel, uint16_t zb_pan_id, uint64_t zb_ext_pani
}
const char kCheckingDeviceConfiguration[] PROGMEM = D_LOG_ZIGBEE "checking device configuration";
-const char kConfigured[] PROGMEM = "Configured, starting coordinator";
+const char kConfiguredCoord[] PROGMEM = "Configured, starting coordinator";
+const char kConfiguredRouter[] PROGMEM = "Configured, starting router";
+const char kConfiguredDevice[] PROGMEM = "Configured, starting end-device";
const char kStarted[] PROGMEM = "Started";
const char kZigbeeStarted[] PROGMEM = D_LOG_ZIGBEE "Zigbee started";
const char kResetting[] PROGMEM = "Resetting configuration";
@@ -367,18 +413,29 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT)
ZI_ON_RECV_UNEXPECTED(&Z_Recv_Default)
ZI_WAIT(10500) // wait for 10 seconds for Tasmota to stabilize
- ZI_ON_ERROR_GOTO(50)
//ZI_MQTT_STATE(ZIGBEE_STATUS_BOOT, "Booting")
//ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "rebooting device")
ZI_SEND(ZBS_RESET) // reboot cc2530 just in case we rebooted ESP8266 but not cc2530
ZI_WAIT_RECV_FUNC(5000, ZBR_RESET, &Z_Reboot) // timeout 5s
ZI_WAIT(100)
- ZI_LOG(LOG_LEVEL_DEBUG, kCheckingDeviceConfiguration)
+ ZI_LOG(LOG_LEVEL_DEBUG, kCheckingDeviceConfiguration) // Log Debug: checking device configuration
+ ZI_SEND(ZBS_VERSION) // check ZNP software version
+ ZI_WAIT_RECV_FUNC(2000, ZBR_VERSION, &Z_ReceiveCheckVersion) // Check if version is valid
+
+ // Dispatching whether coordinator, router or end-device
+ ZI_CALL(&Z_SwitchDeviceType, 0) // goto ZIGBEE_LABEL_START_ROUTER, ZIGBEE_LABEL_START_DEVICE or continue if coordinator
+
+ // ======================================================================
+ // Start as Zigbee Coordinator
+ // ======================================================================
+ // Check the configuration as Coordinator
+ ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_FACT_RESET_COORD)
ZI_SEND(ZBS_ZNPHC) // check value of ZNP Has Configured
ZI_WAIT_RECV(2000, ZBR_ZNPHC)
- ZI_SEND(ZBS_VERSION) // check ZNP software version
- ZI_WAIT_RECV_FUNC(2000, ZBR_VERSION, &Z_ReceiveCheckVersion) // Check version
+
+ ZI_SEND(ZBS_LOGTYPE) // check the logical type
+ ZI_WAIT_RECV(1000, ZBS_LOGTYPE_COORD) // it should be coordinator
ZI_SEND(ZBS_PAN) // check PAN ID
ZI_WAIT_RECV(1000, ZBR_PAN)
ZI_SEND(ZBS_EXTPAN) // check EXT PAN ID
@@ -392,14 +449,14 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
//ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "zigbee configuration ok")
// all is good, we can start
- ZI_LABEL(ZIGBEE_LABEL_START) // START ZNP App
- ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfigured)
+ ZI_LABEL(ZIGBEE_LABEL_START_COORD) // START ZNP App
+ ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfiguredCoord)
ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
// Z_ZDO:startupFromApp
//ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "starting zigbee coordinator")
-ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator
+ ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator
ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) // wait for sync ack of command
- ZI_WAIT_UNTIL(10000, AREQ_STARTUPFROMAPP) // wait for async message that coordinator started
+ ZI_WAIT_UNTIL(10000, AREQ_STARTUPFROMAPP_COORD) // 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
@@ -421,6 +478,9 @@ ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator
ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ)
ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP)
+ // ======================================================================
+ // Correctly configured and running, enable all Tasmota features
+ // ======================================================================
ZI_LABEL(ZIGBEE_LABEL_READY)
ZI_MQTT_STATE(ZIGBEE_STATUS_OK, kStarted)
ZI_LOG(LOG_LEVEL_INFO, kZigbeeStarted)
@@ -431,7 +491,7 @@ ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator
ZI_WAIT_FOREVER()
ZI_GOTO(ZIGBEE_LABEL_READY)
- ZI_LABEL(50) // reformat device
+ ZI_LABEL(ZIGBEE_LABEL_FACT_RESET_COORD) // reformat device
ZI_MQTT_STATE(ZIGBEE_STATUS_RESET_CONF, kResetting)
//ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "zigbee bad configuration of device, doing a factory reset")
ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
@@ -445,7 +505,7 @@ ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator
ZI_WAIT_RECV(1000, ZBR_W_OK)
ZI_SEND(ZBS_W_CHANN) // write CHANNEL
ZI_WAIT_RECV(1000, ZBR_W_OK)
- ZI_SEND(ZBS_W_LOGTYP) // write Logical Type = coordinator
+ ZI_SEND(ZBS_W_LOGTYP_COORD) // write Logical Type = coordinator
ZI_WAIT_RECV(1000, ZBR_W_OK)
ZI_SEND(ZBS_W_PFGK) // write PRECFGKEY
ZI_WAIT_RECV(1000, ZBR_W_OK)
@@ -462,7 +522,98 @@ ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator
ZI_WAIT_RECV(1000, ZBR_WNV_OK)
//ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "zigbee device reconfigured")
- ZI_GOTO(ZIGBEE_LABEL_START)
+ ZI_GOTO(ZIGBEE_LABEL_START_COORD)
+
+ // ======================================================================
+ // Start as Zigbee Router
+ // ======================================================================
+ ZI_LABEL(ZIGBEE_LABEL_INIT_ROUTER) // Init as a router
+ // Check the configuration as Router
+ ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_FACT_RESET_ROUTER)
+ ZI_SEND(ZBS_ZNPHC) // check value of ZNP Has Configured
+ ZI_WAIT_RECV(2000, ZBR_ZNPHC)
+ ZI_SEND(ZBS_LOGTYPE) // check the logical type
+ ZI_WAIT_RECV(1000, ZBS_LOGTYPE_ROUTER) // it should be coordinator
+
+ ZI_LABEL(ZIGBEE_LABEL_START_ROUTER) // Init as a router
+ ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfiguredRouter)
+ ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
+ ZI_SEND(ZBS_AF_REGISTER_ALL) // Z_AF register for endpoint 01, profile 0x0104 Home Automation
+ ZI_WAIT_RECV(1000, ZBR_AF_REGISTER)
+ ZI_SEND(ZBS_STARTUPFROMAPP) // start router
+ ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) // wait for sync ack of command
+ ZI_WAIT_UNTIL_FUNC(0xFFFF, AREQ_STARTUPFROMAPP_ROUTER, &Z_ReceiveStateChange) // wait for async message that coordinator started
+ ZI_SEND(ZBS_GETDEVICEINFO) // GetDeviceInfo
+ ZI_WAIT_RECV_FUNC(2000, ZBR_GETDEVICEINFO, &Z_ReceiveDeviceInfo)
+ ZI_GOTO(ZIGBEE_LABEL_READY)
+
+ ZI_LABEL(ZIGBEE_LABEL_FACT_RESET_ROUTER) // Factory reset for router
+ ZI_MQTT_STATE(ZIGBEE_STATUS_RESET_CONF, kResetting)
+ ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
+ ZI_SEND(ZBS_FACTRES) // factory reset
+ ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_SEND(ZBS_RESET) // reset device
+ ZI_WAIT_RECV(5000, ZBR_RESET)
+ ZI_SEND(ZBS_W_LOGTYP_ROUTER) // write Logical Type = router
+ ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_SEND(ZBS_W_ALL_PAN) // write universal PAN ID = 0xFFFF
+ ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_SEND(ZBS_W_ALL_CHANN) // write Allows all CHANNELS = 0x07FFF800, 11-26
+ ZI_WAIT_RECV(1000, ZBR_W_OK)
+
+ // Now mark the device as ready, writing 0x55 in memory slot 0x0F00
+ ZI_SEND(ZBS_WNV_INITZNPHC) // Init NV ZNP Has Configured
+ ZI_WAIT_RECV_FUNC(1000, ZBR_WNV_INIT_OK, &Z_CheckNVWrite)
+ ZI_SEND(ZBS_WNV_ZNPHC) // Write NV ZNP Has Configured
+ ZI_WAIT_RECV(1000, ZBR_WNV_OK)
+
+ ZI_GOTO(ZIGBEE_LABEL_START_ROUTER)
+
+ // ======================================================================
+ // Start as Zigbee Device
+ // ======================================================================
+ ZI_LABEL(ZIGBEE_LABEL_INIT_DEVICE) // Init as a router
+ // Check the configuration as Router
+ ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_FACT_RESET_DEVICE)
+ ZI_SEND(ZBS_ZNPHC) // check value of ZNP Has Configured
+ ZI_WAIT_RECV(2000, ZBR_ZNPHC)
+ ZI_SEND(ZBS_LOGTYPE) // check the logical type
+ ZI_WAIT_RECV(1000, ZBS_LOGTYPE_DEVICE) // it should be coordinator
+
+ ZI_LABEL(ZIGBEE_LABEL_START_DEVICE) // Init as a router
+ ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfiguredDevice)
+ ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
+ ZI_SEND(ZBS_AF_REGISTER_ALL) // Z_AF register for endpoint 01, profile 0x0104 Home Automation
+ ZI_WAIT_RECV(1000, ZBR_AF_REGISTER)
+ ZI_SEND(ZBS_STARTUPFROMAPP) // start router
+ ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) // wait for sync ack of command
+ ZI_WAIT_UNTIL_FUNC(0xFFFF, AREQ_STARTUPFROMAPP_ROUTER, &Z_ReceiveStateChange) // wait forever for async message that coordinator started
+ ZI_SEND(ZBS_GETDEVICEINFO) // GetDeviceInfo
+ ZI_WAIT_RECV_FUNC(2000, ZBR_GETDEVICEINFO, &Z_ReceiveDeviceInfo)
+ ZI_GOTO(ZIGBEE_LABEL_READY)
+
+ ZI_LABEL(ZIGBEE_LABEL_FACT_RESET_DEVICE) // Factory reset for router
+ ZI_MQTT_STATE(ZIGBEE_STATUS_RESET_CONF, kResetting)
+ ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
+ ZI_SEND(ZBS_FACTRES) // factory reset
+ ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_SEND(ZBS_RESET) // reset device
+ ZI_WAIT_RECV(5000, ZBR_RESET)
+ ZI_SEND(ZBS_W_LOGTYP_DEVICE) // write Logical Type = router
+ ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_SEND(ZBS_W_ALL_PAN) // write universal PAN ID = 0xFFFF
+ ZI_WAIT_RECV(1000, ZBR_W_OK)
+ ZI_SEND(ZBS_W_ALL_CHANN) // write Allows all CHANNELS = 0x07FFF800, 11-26
+ ZI_WAIT_RECV(1000, ZBR_W_OK)
+
+ // Now mark the device as ready, writing 0x55 in memory slot 0x0F00
+ ZI_SEND(ZBS_WNV_INITZNPHC) // Init NV ZNP Has Configured
+ ZI_WAIT_RECV_FUNC(1000, ZBR_WNV_INIT_OK, &Z_CheckNVWrite)
+ ZI_SEND(ZBS_WNV_ZNPHC) // Write NV ZNP Has Configured
+ ZI_WAIT_RECV(1000, ZBR_WNV_OK)
+
+ ZI_GOTO(ZIGBEE_LABEL_START_DEVICE)
+
ZI_LABEL(ZIGBEE_LABEL_UNSUPPORTED_VERSION)
ZI_MQTT_STATE(ZIGBEE_STATUS_UNSUPPORTED_VERSION, kZNP12)
@@ -640,17 +791,27 @@ void ZigbeeStateMachine_Run(void) {
case ZGB_INSTR_WAIT_RECV:
zigbee.recv_filter = (uint8_t *) cur_ptr1;
zigbee.recv_filter_len = cur_d8; // len
- zigbee.next_timeout = now + cur_d16;
+ if (0xFFFF == cur_d16) {
+ zigbee.next_timeout = 0; // forever
+ } else {
+ zigbee.next_timeout = now + cur_d16;
+ }
zigbee.state_waiting = true;
break;
case ZGB_ON_RECV_UNEXPECTED:
zigbee.recv_unexpected = (ZB_RecvMsgFunc) cur_ptr1;
break;
+ case ZGB_INSTR_WAIT_UNTIL_CALL:
+ zigbee.recv_until = true; // and reuse ZGB_INSTR_WAIT_RECV
case ZGB_INSTR_WAIT_RECV_CALL:
zigbee.recv_filter = (uint8_t *) cur_ptr1;
zigbee.recv_filter_len = cur_d8; // len
zigbee.recv_func = (ZB_RecvMsgFunc) cur_ptr2;
- zigbee.next_timeout = now + cur_d16;
+ if (0xFFFF == cur_d16) {
+ zigbee.next_timeout = 0; // forever
+ } else {
+ zigbee.next_timeout = now + cur_d16;
+ }
zigbee.state_waiting = true;
break;
}
diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino
index acccbb8fe..96f6ecd9d 100644
--- a/tasmota/xdrv_23_zigbee_8_parsers.ino
+++ b/tasmota/xdrv_23_zigbee_8_parsers.ino
@@ -147,6 +147,18 @@ int32_t Z_ReceiveCheckVersion(int32_t res, class SBuffer &buf) {
}
}
+// checks the device type (coordinator, router, end-device)
+// If coordinator continue
+// If router goto ZIGBEE_LABEL_START_ROUTER
+// If device goto ZIGBEE_LABEL_START_DEVICE
+int32_t Z_SwitchDeviceType(int32_t res, class SBuffer &buf) {
+ switch (Settings.zb_pan_id) {
+ case 0xFFFF: return ZIGBEE_LABEL_INIT_ROUTER;
+ case 0xFFFE: return ZIGBEE_LABEL_INIT_DEVICE;
+ default: return 0; // continue
+ }
+}
+
//
// Helper function, checks if the incoming buffer matches the 2-bytes prefix, i.e. message type in PMEM
//
@@ -306,6 +318,25 @@ int32_t Z_DataConfirm(int32_t res, const class SBuffer &buf) {
return -1;
}
+//
+// Handle State Change Indication incoming message
+//
+int32_t Z_ReceiveStateChange(int32_t res, const class SBuffer &buf) {
+ uint8_t state = buf.get8(2);
+
+ if (ZDO_DEV_NWK_DISC == state) {
+ Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
+ "\"Status\":%d,\"Message\":\"%s\"}}"),
+ ZIGBEE_STATUS_SCANNING, PSTR("Scanning Zigbee network")
+ );
+
+ MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
+ XdrvRulesProcess();
+ }
+
+ return res;
+}
+
//
// Handle Receive End Device Announce incoming message
// This message is also received when a previously paired device is powered up
@@ -641,6 +672,7 @@ typedef struct Z_Dispatcher {
// Ffilters based on ZNP frames
ZBM(AREQ_AF_DATA_CONFIRM, Z_AREQ | Z_AF, AF_DATA_CONFIRM) // 4480
ZBM(AREQ_AF_INCOMING_MESSAGE, Z_AREQ | Z_AF, AF_INCOMING_MSG) // 4481
+// ZBM(AREQ_STATE_CHANGE_IND, Z_AREQ | Z_ZDO, ZDO_STATE_CHANGE_IND) // 45C0
ZBM(AREQ_END_DEVICE_ANNCE_IND, Z_AREQ | Z_ZDO, ZDO_END_DEVICE_ANNCE_IND) // 45C1
ZBM(AREQ_END_DEVICE_TC_DEV_IND, Z_AREQ | Z_ZDO, ZDO_TC_DEV_IND) // 45CA
ZBM(AREQ_PERMITJOIN_OPEN_XX, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND ) // 45CB
@@ -655,6 +687,7 @@ ZBM(AREQ_ZDO_MGMT_BIND_RSP, Z_AREQ | Z_ZDO, ZDO_MGMT_BIND_RSP) // 4
const Z_Dispatcher Z_DispatchTable[] PROGMEM = {
{ AREQ_AF_DATA_CONFIRM, &Z_DataConfirm },
{ AREQ_AF_INCOMING_MESSAGE, &Z_ReceiveAfIncomingMessage },
+ // { AREQ_STATE_CHANGE_IND, &Z_ReceiveStateChange },
{ AREQ_END_DEVICE_ANNCE_IND, &Z_ReceiveEndDeviceAnnonce },
{ AREQ_END_DEVICE_TC_DEV_IND, &Z_ReceiveTCDevInd },
{ AREQ_PERMITJOIN_OPEN_XX, &Z_ReceivePermitJoinStatus },
diff --git a/tasmota/xdrv_81_webcam.ino b/tasmota/xdrv_81_webcam.ino
index 95ec03cc4..fb2c86161 100644
--- a/tasmota/xdrv_81_webcam.ino
+++ b/tasmota/xdrv_81_webcam.ino
@@ -25,37 +25,57 @@
* Template as used on ESP32-CAM WiFi + bluetooth Camera Module Development Board ESP32 With Camera Module OV2640 Geekcreit for Arduino
* {"NAME":"AITHINKER CAM No SPI","GPIO":[4992,65504,65504,65504,65504,5088,65504,65504,65504,65504,65504,65504,65504,65504,5089,5090,0,5091,5184,5152,0,5120,5024,5056,0,0,0,0,4928,65504,5094,5095,5092,0,0,5093],"FLAG":0,"BASE":1}
*
- * Command: Webcam
- * 0 = Stop streaming
- * 1 = FRAMESIZE_QQVGA2 (128x160)
- * 2 = FRAMESIZE_QCIF (176x144)
- * 3 = FRAMESIZE_HQVGA (240x176)
- * 4 = FRAMESIZE_QVGA (320x240)
- * 5 = FRAMESIZE_CIF (400x296)
- * 6 = FRAMESIZE_VGA (640x480)
- * 7 = FRAMESIZE_SVGA (800x600)
- * 8 = FRAMESIZE_XGA (1024x768)
- * 9 = FRAMESIZE_SXGA (1280x1024)
- * 10 = FRAMESIZE_UXGA (1600x1200)
+ * Supported commands:
+ * WcStream = Control streaming, 0 = stop, 1 = start
+ * WcResolution = Set resolution
+ * 0 = FRAMESIZE_96x96, (96x96)
+ * 1 = FRAMESIZE_QQVGA2 (128x160)
+ * 2 = FRAMESIZE_QCIF (176x144)
+ * 3 = FRAMESIZE_HQVGA (240x176)
+ * 4 = FRAMESIZE_QVGA (320x240)
+ * 5 = FRAMESIZE_CIF (400x296)
+ * 6 = FRAMESIZE_VGA (640x480)
+ * 7 = FRAMESIZE_SVGA (800x600)
+ * 8 = FRAMESIZE_XGA (1024x768)
+ * 9 = FRAMESIZE_SXGA (1280x1024)
+ * 10 = FRAMESIZE_UXGA (1600x1200)
+ * WcMirror = Mirror picture, 0 = no, 1 = yes
+ * WcFlip = Flip picture, 0 = no, 1 = yes
+ * WcSaturation = Set picture Saturation -2 ... +2
+ * WcBrightness = Set picture Brightness -2 ... +2
+ * WcContrast = Set picture Contrast -2 ... +2
*
* Only boards with PSRAM should be used. To enable PSRAM board should be se set to esp32cam in common32 of platform_override.ini
* board = esp32cam
* To speed up cam processing cpu frequency should be better set to 240Mhz in common32 of platform_override.ini
* board_build.f_cpu = 240000000L
-\*********************************************************************************************/
+ * remarks for AI-THINKER
+ * GPIO0 zero must be disconnected from any wire after programming because this pin drives the cam clock and does
+ * not tolerate any capictive load
+ * flash led = gpio 4
+ * red led = gpio 33
+ */
+
+/*********************************************************************************************/
#define XDRV_81 81
-#define CAMERA_MODEL_AI_THINKER
-
-#define USE_TEMPLATE
-
-#define WC_LOGLEVEL LOG_LEVEL_INFO
-
+#include "esp_camera.h"
+#include "sensor.h"
#include "fb_gfx.h"
#include "fd_forward.h"
#include "fr_forward.h"
+bool HttpCheckPriviledgedAccess(bool);
+extern ESP8266WebServer *Webserver;
+
+ESP8266WebServer *CamServer;
+#define BOUNDARY "e8b8c539-047d-4777-a985-fbba6edff11e"
+
+WiFiClient client;
+
+
+// CAMERA_MODEL_AI_THINKER default template pins
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
@@ -74,10 +94,6 @@
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
-
-#include "esp_camera.h"
-#include "sensor.h"
-
uint8_t wc_up;
uint16_t wc_width;
uint16_t wc_height;
@@ -87,6 +103,8 @@ uint8_t faces;
uint16_t face_detect_time;
#endif
+/*********************************************************************************************/
+
bool WcPinUsed(void) {
bool pin_used = true;
for (uint32_t i = 0; i < MAX_WEBCAM_DATA; i++) {
@@ -107,7 +125,7 @@ bool WcPinUsed(void) {
return pin_used;
}
-uint32_t wc_setup(int32_t fsiz) {
+uint32_t WcSetup(int32_t fsiz) {
if (fsiz > 10) { fsiz = 10; }
wc_stream_active = 0;
@@ -120,7 +138,7 @@ uint32_t wc_setup(int32_t fsiz) {
if (wc_up) {
esp_camera_deinit();
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: deinit"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Deinit"));
//return wc_up;
}
@@ -134,24 +152,6 @@ uint32_t wc_setup(int32_t fsiz) {
// config.pixel_format = PIXFORMAT_GRAYSCALE;
// config.pixel_format = PIXFORMAT_RGB565;
-#ifndef USE_TEMPLATE
- config.pin_d0 = Y2_GPIO_NUM;
- config.pin_d1 = Y3_GPIO_NUM;
- config.pin_d2 = Y4_GPIO_NUM;
- config.pin_d3 = Y5_GPIO_NUM;
- config.pin_d4 = Y6_GPIO_NUM;
- config.pin_d5 = Y7_GPIO_NUM;
- config.pin_d6 = Y8_GPIO_NUM;
- config.pin_d7 = Y9_GPIO_NUM;
- config.pin_xclk = XCLK_GPIO_NUM;
- config.pin_pclk = PCLK_GPIO_NUM;
- config.pin_vsync = VSYNC_GPIO_NUM;
- config.pin_href = HREF_GPIO_NUM;
- config.pin_sscb_sda = SIOD_GPIO_NUM;
- config.pin_sscb_scl = SIOC_GPIO_NUM;
- config.pin_pwdn = PWDN_GPIO_NUM;
- config.pin_reset = RESET_GPIO_NUM;
-#else
if (WcPinUsed()) {
config.pin_d0 = Pin(GPIO_WEBCAM_DATA); // Y2_GPIO_NUM;
config.pin_d1 = Pin(GPIO_WEBCAM_DATA, 1); // Y3_GPIO_NUM;
@@ -170,7 +170,7 @@ uint32_t wc_setup(int32_t fsiz) {
config.pin_pwdn = (PinUsed(GPIO_WEBCAM_PWDN)) ? Pin(GPIO_WEBCAM_PWDN) : -1; // PWDN_GPIO_NUM;
config.pin_reset = (PinUsed(GPIO_WEBCAM_RESET)) ? Pin(GPIO_WEBCAM_RESET) : -1; // RESET_GPIO_NUM;
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: User template"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: User template"));
} else {
// defaults to AI THINKER
config.pin_d0 = Y2_GPIO_NUM;
@@ -189,9 +189,8 @@ uint32_t wc_setup(int32_t fsiz) {
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Default template"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Default template"));
}
-#endif
//ESP.getPsramSize()
@@ -206,12 +205,12 @@ uint32_t wc_setup(int32_t fsiz) {
config.frame_size = FRAMESIZE_UXGA;
config.jpeg_quality = 10;
config.fb_count = 2;
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: PSRAM found"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: PSRAM found"));
} else {
config.frame_size = FRAMESIZE_VGA;
config.jpeg_quality = 12;
config.fb_count = 1;
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: PSRAM not found"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: PSRAM not found"));
}
// stupid workaround camera diver eats up static ram should prefer PSRAM
@@ -224,19 +223,12 @@ uint32_t wc_setup(int32_t fsiz) {
if (x) { free(x); }
if (err != ESP_OK) {
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Init failed with error 0x%x"), err);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Init failed with error 0x%x"), err);
return 0;
}
sensor_t * wc_s = esp_camera_sensor_get();
- // initial sensors are flipped vertically and colors are a bit saturated
-/*
- if (OV3660_PID == wc_s->id.PID) {
- wc_s->set_vflip(wc_s, 1); // flip it back
- wc_s->set_brightness(wc_s, 1); // up the brightness just a bit
- wc_s->set_saturation(wc_s, -2); // lower the saturation
- }
-*/
+
wc_s->set_vflip(wc_s, Settings.webcam_config.flip);
wc_s->set_hmirror(wc_s, Settings.webcam_config.mirror);
wc_s->set_brightness(wc_s, Settings.webcam_config.brightness -2); // up the brightness just a bit
@@ -255,7 +247,7 @@ uint32_t wc_setup(int32_t fsiz) {
fd_init();
#endif
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Initialized"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Initialized"));
wc_up = 1;
if (psram) { wc_up=2; }
@@ -263,7 +255,9 @@ uint32_t wc_setup(int32_t fsiz) {
return wc_up;
}
-int32_t wc_set_options(uint32_t sel, int32_t value) {
+/*********************************************************************************************/
+
+int32_t WcSetOptions(uint32_t sel, int32_t value) {
int32_t res = 0;
sensor_t *s = esp_camera_sensor_get();
if (!s) { return -99; }
@@ -302,7 +296,7 @@ int32_t wc_set_options(uint32_t sel, int32_t value) {
return res;
}
-uint32_t wc_get_width(void) {
+uint32_t WcGetWidth(void) {
camera_fb_t *wc_fb = esp_camera_fb_get();
if (!wc_fb) { return 0; }
wc_width = wc_fb->width;
@@ -310,7 +304,7 @@ uint32_t wc_get_width(void) {
return wc_width;
}
-uint32_t wc_get_height(void) {
+uint32_t WcGetHeight(void) {
camera_fb_t *wc_fb = esp_camera_fb_get();
if (!wc_fb) { return 0; }
wc_height = wc_fb->height;
@@ -318,206 +312,69 @@ uint32_t wc_get_height(void) {
return wc_height;
}
-#ifndef MAX_PICSTORE
-#define MAX_PICSTORE 4
-#endif
-struct PICSTORE {
- uint8_t *buff;
- uint32_t len;
-};
+/*********************************************************************************************/
-struct PICSTORE picstore[MAX_PICSTORE];
+uint16_t motion_detect;
+uint32_t motion_ltime;
+uint32_t motion_trigger;
+uint32_t motion_brightness;
+uint8_t *last_motion_buffer;
-#ifdef COPYFRAME
-struct PICSTORE tmp_picstore;
-#endif
-
-uint32_t get_picstore(int32_t num, uint8_t **buff) {
- if (num<0) { return MAX_PICSTORE; }
- *buff = picstore[num].buff;
- return picstore[num].len;
+uint32_t WcSetMotionDetect(int32_t value) {
+ if (value >= 0) { motion_detect = value; }
+ if (-1 == value) {
+ return motion_trigger;
+ } else {
+ return motion_brightness;
+ }
}
-uint32_t wc_get_jpeg(uint8_t **buff) {
- size_t _jpg_buf_len = 0;
- uint8_t * _jpg_buf = NULL;
+// optional motion detector
+void WcDetectMotion(void) {
camera_fb_t *wc_fb;
- wc_fb = esp_camera_fb_get();
- if (!wc_fb) { return 0; }
- if (wc_fb->format != PIXFORMAT_JPEG) {
- bool jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len);
- if (!jpeg_converted) {
- _jpg_buf_len = wc_fb->len;
- _jpg_buf = wc_fb->buf;
+ uint8_t *out_buf = 0;
+
+ if ((millis()-motion_ltime) > motion_detect) {
+ motion_ltime = millis();
+ wc_fb = esp_camera_fb_get();
+ if (!wc_fb) { return; }
+
+ if (!last_motion_buffer) {
+ last_motion_buffer=(uint8_t *)heap_caps_malloc((wc_fb->width*wc_fb->height)+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ }
+ if (last_motion_buffer) {
+ if (PIXFORMAT_JPEG == wc_fb->format) {
+ out_buf = (uint8_t *)heap_caps_malloc((wc_fb->width*wc_fb->height*3)+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ if (out_buf) {
+ fmt2rgb888(wc_fb->buf, wc_fb->len, wc_fb->format, out_buf);
+ uint32_t x, y;
+ uint8_t *pxi = out_buf;
+ uint8_t *pxr = last_motion_buffer;
+ // convert to bw
+ uint64_t accu = 0;
+ uint64_t bright = 0;
+ for (y = 0; y < wc_fb->height; y++) {
+ for (x = 0; x < wc_fb->width; x++) {
+ int32_t gray = (pxi[0] + pxi[1] + pxi[2]) / 3;
+ int32_t lgray = pxr[0];
+ pxr[0] = gray;
+ pxi += 3;
+ pxr++;
+ accu += abs(gray - lgray);
+ bright += gray;
+ }
+ }
+ motion_trigger = accu / ((wc_fb->height * wc_fb->width) / 100);
+ motion_brightness = bright / ((wc_fb->height * wc_fb->width) / 100);
+ free(out_buf);
+ }
+ }
}
- } else {
- _jpg_buf_len = wc_fb->len;
- _jpg_buf = wc_fb->buf;
- }
- esp_camera_fb_return(wc_fb); // This frees the buffer
- *buff = _jpg_buf; // Buffer has been freed so this will cause exceptions
- return _jpg_buf_len;
-}
-
-uint32_t wc_get_frame(int32_t bnum) {
- size_t _jpg_buf_len = 0;
- uint8_t * _jpg_buf = NULL;
- camera_fb_t *wc_fb = 0;
- bool jpeg_converted = false;
-
- if (bnum < 0) {
- if (bnum < -MAX_PICSTORE) { bnum=-1; }
- bnum = -bnum;
- bnum--;
- if (picstore[bnum].buff) { free(picstore[bnum].buff); }
- picstore[bnum].len = 0;
- return 0;
- }
-
-#ifdef COPYFRAME
- if (bnum & 0x10) {
- bnum &= 0xf;
- _jpg_buf = tmp_picstore.buff;
- _jpg_buf_len = tmp_picstore.len;
- if (!_jpg_buf_len) { return 0; }
- goto pcopy;
- }
-#endif
-
- wc_fb = esp_camera_fb_get();
- if (!wc_fb) {
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Can't get frame"));
- return 0;
- }
- if (!bnum) {
- wc_width = wc_fb->width;
- wc_height = wc_fb->height;
esp_camera_fb_return(wc_fb);
- return 0;
- }
-
- if (wc_fb->format != PIXFORMAT_JPEG) {
- jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len);
- if (!jpeg_converted){
- //Serial.println("JPEG compression failed");
- _jpg_buf_len = wc_fb->len;
- _jpg_buf = wc_fb->buf;
- }
- } else {
- _jpg_buf_len = wc_fb->len;
- _jpg_buf = wc_fb->buf;
- }
-
-pcopy:
- if ((bnum < 1) || (bnum > MAX_PICSTORE)) { bnum = 1; }
- bnum--;
- if (picstore[bnum].buff) { free(picstore[bnum].buff); }
- picstore[bnum].buff = (uint8_t *)heap_caps_malloc(_jpg_buf_len+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
- if (picstore[bnum].buff) {
- memcpy(picstore[bnum].buff, _jpg_buf, _jpg_buf_len);
- picstore[bnum].len = _jpg_buf_len;
- } else {
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Can't allocate picstore"));
- picstore[bnum].len = 0;
- }
- if (wc_fb) { esp_camera_fb_return(wc_fb); }
- if (jpeg_converted) { free(_jpg_buf); }
- if (!picstore[bnum].buff) { return 0; }
-
- return _jpg_buf_len;
-}
-
-bool HttpCheckPriviledgedAccess(bool);
-extern ESP8266WebServer *Webserver;
-
-void HandleImage(void) {
- if (!HttpCheckPriviledgedAccess(true)) { return; }
-
- uint32_t bnum = Webserver->arg(F("p")).toInt();
- if ((bnum < 0) || (bnum > MAX_PICSTORE)) { bnum= 1; }
- WiFiClient client = Webserver->client();
- String response = "HTTP/1.1 200 OK\r\n";
- response += "Content-disposition: inline; filename=cap.jpg\r\n";
- response += "Content-type: image/jpeg\r\n\r\n";
- Webserver->sendContent(response);
-
- if (!bnum) {
- uint8_t *buff;
- uint32_t len;
- len = wc_get_jpeg(&buff);
- if (len) {
- client.write(buff,len);
- free(buff); // Buffer has been freed already in wc_get_jpeg so this will cause exceptions
- }
- } else {
- bnum--;
- if (!picstore[bnum].len) {
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: No image #: %d"), bnum);
- return;
- }
- client.write((char *)picstore[bnum].buff, picstore[bnum].len);
- }
-
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Sending image #: %d"), bnum+1);
-}
-
-ESP8266WebServer *CamServer;
-#define BOUNDARY "e8b8c539-047d-4777-a985-fbba6edff11e"
-
-WiFiClient client;
-
-void handleMjpeg(void) {
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Handle camserver"));
- if (!wc_stream_active) {
- wc_stream_active = 1;
- client = CamServer->client();
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Create client"));
}
}
-void HandleImageTheo(void) {
- if (!HttpCheckPriviledgedAccess(true)) { return; }
-
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Capture image"));
-
- if (Settings.webcam_config.stream) {
- if (!CamServer) {
- WcStreamControl();
- }
- }
-
- camera_fb_t *wc_fb;
- wc_fb = esp_camera_fb_get(); // Acquire frame
- if (!wc_fb) {
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Frame buffer could not be acquired"));
- return;
- }
-
- size_t _jpg_buf_len = 0;
- uint8_t * _jpg_buf = NULL;
- if (wc_fb->format != PIXFORMAT_JPEG) {
- bool jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len);
- if (!jpeg_converted) {
- _jpg_buf_len = wc_fb->len;
- _jpg_buf = wc_fb->buf;
- }
- } else {
- _jpg_buf_len = wc_fb->len;
- _jpg_buf = wc_fb->buf;
- }
-
- if (_jpg_buf_len) {
- Webserver->client().flush();
- WSHeaderSend();
- Webserver->sendHeader(F("Content-disposition"), F("inline; filename=cap.jpg"));
- Webserver->send_P(200, "image/jpeg", (char *)_jpg_buf, _jpg_buf_len);
- Webserver->client().stop();
- }
-
- esp_camera_fb_return(wc_fb); // Free frame buffer
-
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Image sent"));
-}
+/*********************************************************************************************/
#ifdef USE_FACE_DETECT
@@ -589,23 +446,23 @@ void draw_face_boxes(dl_matrix3du_t *image_matrix, box_array_t *boxes, int face_
#define DL_SPIRAM_SUPPORT
-uint32_t wc_set_face_detect(int32_t value) {
+uint32_t WcSetFaceDetect(int32_t value) {
if (value >= 0) { face_detect_time = value; }
return faces;
}
uint32_t face_ltime;
-uint32_t detect_face(void);
+uint32_t WcDetectFace(void);
-uint32_t detect_face(void) {
-dl_matrix3du_t *image_matrix;
-size_t out_len, out_width, out_height;
-uint8_t * out_buf;
-bool s;
-bool detected = false;
-int face_id = 0;
-camera_fb_t *fb;
+uint32_t WcDetectFace(void) {
+ dl_matrix3du_t *image_matrix;
+ size_t out_len, out_width, out_height;
+ uint8_t * out_buf;
+ bool s;
+ bool detected = false;
+ int face_id = 0;
+ camera_fb_t *fb;
if ((millis() - face_ltime) > face_detect_time) {
face_ltime = millis();
@@ -614,7 +471,7 @@ camera_fb_t *fb;
image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3);
if (!image_matrix) {
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: dl_matrix3du_alloc failed"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: dl_matrix3du_alloc failed"));
esp_camera_fb_return(fb);
return ESP_FAIL;
}
@@ -628,7 +485,7 @@ camera_fb_t *fb;
esp_camera_fb_return(fb);
if (!s){
dl_matrix3du_free(image_matrix);
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: to rgb888 failed"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: to rgb888 failed"));
return ESP_FAIL;
}
@@ -654,7 +511,195 @@ camera_fb_t *fb;
}
#endif
-void handleMjpeg_task(void) {
+/*********************************************************************************************/
+
+#ifndef MAX_PICSTORE
+#define MAX_PICSTORE 4
+#endif
+struct PICSTORE {
+ uint8_t *buff;
+ uint32_t len;
+};
+
+struct PICSTORE picstore[MAX_PICSTORE];
+
+#ifdef COPYFRAME
+struct PICSTORE tmp_picstore;
+#endif
+
+uint32_t WcGetPicstore(int32_t num, uint8_t **buff) {
+ if (num<0) { return MAX_PICSTORE; }
+ *buff = picstore[num].buff;
+ return picstore[num].len;
+}
+
+uint32_t WcGetFrame(int32_t bnum) {
+ size_t _jpg_buf_len = 0;
+ uint8_t * _jpg_buf = NULL;
+ camera_fb_t *wc_fb = 0;
+ bool jpeg_converted = false;
+
+ if (bnum < 0) {
+ if (bnum < -MAX_PICSTORE) { bnum=-1; }
+ bnum = -bnum;
+ bnum--;
+ if (picstore[bnum].buff) { free(picstore[bnum].buff); }
+ picstore[bnum].len = 0;
+ return 0;
+ }
+
+#ifdef COPYFRAME
+ if (bnum & 0x10) {
+ bnum &= 0xf;
+ _jpg_buf = tmp_picstore.buff;
+ _jpg_buf_len = tmp_picstore.len;
+ if (!_jpg_buf_len) { return 0; }
+ goto pcopy;
+ }
+#endif
+
+ wc_fb = esp_camera_fb_get();
+ if (!wc_fb) {
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Can't get frame"));
+ return 0;
+ }
+ if (!bnum) {
+ wc_width = wc_fb->width;
+ wc_height = wc_fb->height;
+ esp_camera_fb_return(wc_fb);
+ return 0;
+ }
+
+ if (wc_fb->format != PIXFORMAT_JPEG) {
+ jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len);
+ if (!jpeg_converted){
+ //Serial.println("JPEG compression failed");
+ _jpg_buf_len = wc_fb->len;
+ _jpg_buf = wc_fb->buf;
+ }
+ } else {
+ _jpg_buf_len = wc_fb->len;
+ _jpg_buf = wc_fb->buf;
+ }
+
+pcopy:
+ if ((bnum < 1) || (bnum > MAX_PICSTORE)) { bnum = 1; }
+ bnum--;
+ if (picstore[bnum].buff) { free(picstore[bnum].buff); }
+ picstore[bnum].buff = (uint8_t *)heap_caps_malloc(_jpg_buf_len+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ if (picstore[bnum].buff) {
+ memcpy(picstore[bnum].buff, _jpg_buf, _jpg_buf_len);
+ picstore[bnum].len = _jpg_buf_len;
+ } else {
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Can't allocate picstore"));
+ picstore[bnum].len = 0;
+ }
+ if (wc_fb) { esp_camera_fb_return(wc_fb); }
+ if (jpeg_converted) { free(_jpg_buf); }
+ if (!picstore[bnum].buff) { return 0; }
+
+ return _jpg_buf_len;
+}
+
+void HandleImage(void) {
+ if (!HttpCheckPriviledgedAccess()) { return; }
+
+ uint32_t bnum = Webserver->arg(F("p")).toInt();
+ if ((bnum < 0) || (bnum > MAX_PICSTORE)) { bnum= 1; }
+ WiFiClient client = Webserver->client();
+ String response = "HTTP/1.1 200 OK\r\n";
+ response += "Content-disposition: inline; filename=cap.jpg\r\n";
+ response += "Content-type: image/jpeg\r\n\r\n";
+ Webserver->sendContent(response);
+
+ if (!bnum) {
+ size_t _jpg_buf_len = 0;
+ uint8_t * _jpg_buf = NULL;
+ camera_fb_t *wc_fb = 0;
+ wc_fb = esp_camera_fb_get();
+ if (!wc_fb) { return; }
+ if (wc_fb->format != PIXFORMAT_JPEG) {
+ bool jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len);
+ if (!jpeg_converted) {
+ _jpg_buf_len = wc_fb->len;
+ _jpg_buf = wc_fb->buf;
+ }
+ } else {
+ _jpg_buf_len = wc_fb->len;
+ _jpg_buf = wc_fb->buf;
+ }
+ if (_jpg_buf_len) {
+ client.write((char *)_jpg_buf, _jpg_buf_len);
+ }
+ if (wc_fb) { esp_camera_fb_return(wc_fb); }
+ } else {
+ bnum--;
+ if (!picstore[bnum].len) {
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: No image #: %d"), bnum);
+ return;
+ }
+ client.write((char *)picstore[bnum].buff, picstore[bnum].len);
+ }
+ client.stop();
+
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("CAM: Sending image #: %d"), bnum+1);
+}
+
+void HandleImageBasic(void) {
+ if (!HttpCheckPriviledgedAccess()) { return; }
+
+ AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP "Capture image"));
+
+ if (Settings.webcam_config.stream) {
+ if (!CamServer) {
+ WcStreamControl();
+ }
+ }
+
+ camera_fb_t *wc_fb;
+ wc_fb = esp_camera_fb_get(); // Acquire frame
+ if (!wc_fb) {
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Frame buffer could not be acquired"));
+ return;
+ }
+
+ size_t _jpg_buf_len = 0;
+ uint8_t * _jpg_buf = NULL;
+ if (wc_fb->format != PIXFORMAT_JPEG) {
+ bool jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len);
+ if (!jpeg_converted) {
+ _jpg_buf_len = wc_fb->len;
+ _jpg_buf = wc_fb->buf;
+ }
+ } else {
+ _jpg_buf_len = wc_fb->len;
+ _jpg_buf = wc_fb->buf;
+ }
+
+ if (_jpg_buf_len) {
+ Webserver->client().flush();
+ WSHeaderSend();
+ Webserver->sendHeader(F("Content-disposition"), F("inline; filename=snapshot.jpg"));
+ Webserver->send_P(200, "image/jpeg", (char *)_jpg_buf, _jpg_buf_len);
+ Webserver->client().stop();
+ }
+
+ esp_camera_fb_return(wc_fb); // Free frame buffer
+
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("CAM: Image sent"));
+}
+
+void HandleWebcamMjpeg(void) {
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Handle camserver"));
+// if (!wc_stream_active) {
+// always restart stream
+ wc_stream_active = 1;
+ client = CamServer->client();
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Create client"));
+// }
+}
+
+void HandleWebcamMjpegTask(void) {
camera_fb_t *wc_fb;
size_t _jpg_buf_len = 0;
uint8_t * _jpg_buf = NULL;
@@ -664,13 +709,13 @@ void handleMjpeg_task(void) {
bool jpeg_converted = false;
if (!client.connected()) {
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Client fail"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Client fail"));
wc_stream_active = 0;
}
if (1 == wc_stream_active) {
client.flush();
client.setTimeout(3);
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Start stream"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Start stream"));
client.print("HTTP/1.1 200 OK\r\n"
"Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n"
"\r\n");
@@ -679,7 +724,7 @@ void handleMjpeg_task(void) {
if (2 == wc_stream_active) {
wc_fb = esp_camera_fb_get();
if (!wc_fb) {
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Frame fail"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Frame fail"));
wc_stream_active = 0;
}
}
@@ -687,7 +732,7 @@ void handleMjpeg_task(void) {
if (wc_fb->format != PIXFORMAT_JPEG) {
jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len);
if (!jpeg_converted){
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: JPEG compression failed"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: JPEG compression failed"));
_jpg_buf_len = wc_fb->len;
_jpg_buf = wc_fb->buf;
}
@@ -704,7 +749,7 @@ void handleMjpeg_task(void) {
if (tlen!=_jpg_buf_len) {
esp_camera_fb_return(wc_fb);
wc_stream_active=0;
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Send fail"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Send fail"));
}*/
client.print("\r\n--" BOUNDARY "\r\n");
@@ -721,83 +766,25 @@ void handleMjpeg_task(void) {
if (jpeg_converted) { free(_jpg_buf); }
esp_camera_fb_return(wc_fb);
- //AddLog_P2(WC_LOGLEVEL, PSTR("CAM: send frame"));
+ //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: send frame"));
}
if (0 == wc_stream_active) {
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Stream exit"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Stream exit"));
client.flush();
client.stop();
}
}
-void CamHandleRoot(void) {
+void HandleWebcamRoot(void) {
//CamServer->redirect("http://" + String(ip) + ":81/cam.mjpeg");
CamServer->sendHeader("Location", WiFi.localIP().toString() + ":81/cam.mjpeg");
CamServer->send(302, "", "");
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: root called"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Root called"));
}
-uint16_t motion_detect;
-uint32_t motion_ltime;
-uint32_t motion_trigger;
-uint32_t motion_brightness;
-uint8_t *last_motion_buffer;
+/*********************************************************************************************/
-uint32_t wc_set_motion_detect(int32_t value) {
- if (value >= 0) { motion_detect = value; }
- if (-1 == value) {
- return motion_trigger;
- } else {
- return motion_brightness;
- }
-}
-
-// optional motion detector
-void detect_motion(void) {
- camera_fb_t *wc_fb;
- uint8_t *out_buf = 0;
-
- if ((millis()-motion_ltime) > motion_detect) {
- motion_ltime = millis();
- wc_fb = esp_camera_fb_get();
- if (!wc_fb) { return; }
-
- if (!last_motion_buffer) {
- last_motion_buffer=(uint8_t *)heap_caps_malloc((wc_fb->width*wc_fb->height)+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
- }
- if (last_motion_buffer) {
- if (PIXFORMAT_JPEG == wc_fb->format) {
- out_buf = (uint8_t *)heap_caps_malloc((wc_fb->width*wc_fb->height*3)+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
- if (out_buf) {
- fmt2rgb888(wc_fb->buf, wc_fb->len, wc_fb->format, out_buf);
- uint32_t x, y;
- uint8_t *pxi = out_buf;
- uint8_t *pxr = last_motion_buffer;
- // convert to bw
- uint64_t accu = 0;
- uint64_t bright = 0;
- for (y = 0; y < wc_fb->height; y++) {
- for (x = 0; x < wc_fb->width; x++) {
- int32_t gray = (pxi[0] + pxi[1] + pxi[2]) / 3;
- int32_t lgray = pxr[0];
- pxr[0] = gray;
- pxi += 3;
- pxr++;
- accu += abs(gray - lgray);
- bright += gray;
- }
- }
- motion_trigger = accu / ((wc_fb->height * wc_fb->width) / 100);
- motion_brightness = bright / ((wc_fb->height * wc_fb->width) / 100);
- free(out_buf);
- }
- }
- }
- esp_camera_fb_return(wc_fb);
- }
-}
-
-uint32_t wc_set_streamserver(uint32_t flag) {
+uint32_t WcSetStreamserver(uint32_t flag) {
if (global_state.wifi_down) { return 0; }
wc_stream_active = 0;
@@ -805,11 +792,11 @@ uint32_t wc_set_streamserver(uint32_t flag) {
if (flag) {
if (!CamServer) {
CamServer = new ESP8266WebServer(81);
- CamServer->on("/", CamHandleRoot);
- CamServer->on("/cam.mjpeg", handleMjpeg);
- CamServer->on("/cam.jpg", handleMjpeg);
- CamServer->on("/stream", handleMjpeg);
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Stream init"));
+ CamServer->on("/", HandleWebcamRoot);
+ CamServer->on("/cam.mjpeg", HandleWebcamMjpeg);
+ CamServer->on("/cam.jpg", HandleWebcamMjpeg);
+ CamServer->on("/stream", HandleWebcamMjpeg);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Stream init"));
CamServer->begin();
}
} else {
@@ -817,16 +804,35 @@ uint32_t wc_set_streamserver(uint32_t flag) {
CamServer->stop();
delete CamServer;
CamServer = NULL;
- AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Stream exit"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Stream exit"));
}
}
return 0;
}
void WcStreamControl() {
- wc_set_streamserver(Settings.webcam_config.stream);
+ WcSetStreamserver(Settings.webcam_config.stream);
int resolution = (!Settings.webcam_config.stream) ? -1 : Settings.webcam_config.resolution;
- wc_setup(resolution);
+ WcSetup(resolution);
+}
+
+/*********************************************************************************************/
+
+void WcLoop(void) {
+ if (CamServer) {
+ CamServer->handleClient();
+ if (wc_stream_active) { HandleWebcamMjpegTask(); }
+ }
+ if (motion_detect) { WcDetectMotion(); }
+#ifdef USE_FACE_DETECT
+ if (face_detect_time) { WcDetectFace(); }
+#endif
+}
+
+void WcPicSetup(void) {
+ Webserver->on("/wc.jpg", HandleImage);
+ Webserver->on("/wc.mjpeg", HandleImage);
+ Webserver->on("/snapshot.jpg", HandleImageBasic);
}
void WcShowStream(void) {
@@ -842,51 +848,6 @@ void WcShowStream(void) {
}
}
-void wc_loop(void) {
- if (CamServer) {
- CamServer->handleClient();
- if (wc_stream_active) { handleMjpeg_task(); }
- }
- if (motion_detect) { detect_motion(); }
-#ifdef USE_FACE_DETECT
- if (face_detect_time) { detect_face(); }
-#endif
-}
-
-void wc_pic_setup(void) {
- Webserver->on("/wc.jpg", HandleImage);
- Webserver->on("/wc.mjpeg", HandleImage);
-
- Webserver->on("/snapshot.jpg", HandleImageTheo);
-}
-
-/*
-typedef enum {
- // FRAMESIZE_96x96, // 96x96
- FRAMESIZE_QQVGA, // 160x120 0
- FRAMESIZE_QQVGA2, // 128x160 1
- FRAMESIZE_QCIF, // 176x144 2
- FRAMESIZE_HQVGA, // 240x176 3
-
- // FRAMESIZE_240x240, // 240x240 3
-
- FRAMESIZE_QVGA, // 320x240 4
- FRAMESIZE_CIF, // 400x296 5
- FRAMESIZE_VGA, // 640x480 6
- FRAMESIZE_SVGA, // 800x600 7
- FRAMESIZE_XGA, // 1024x768 8
- FRAMESIZE_SXGA, // 1280x1024 9
- FRAMESIZE_UXGA, // 1600x1200 10
-
-
- FRAMESIZE_QXGA, // 2048*1536
- FRAMESIZE_INVALID
-} framesize_t;
-
-flash led = gpio4
-red led = gpio 33
-*/
-
void WcInit(void) {
if (!Settings.webcam_config.data) {
Settings.webcam_config.stream = 1;
@@ -942,7 +903,7 @@ void CmndWebcamStream(void) {
void CmndWebcamResolution(void) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10)) {
Settings.webcam_config.resolution = XdrvMailbox.payload;
- wc_set_options(0, Settings.webcam_config.resolution);
+ WcSetOptions(0, Settings.webcam_config.resolution);
}
ResponseCmndNumber(Settings.webcam_config.resolution);
}
@@ -950,7 +911,7 @@ void CmndWebcamResolution(void) {
void CmndWebcamMirror(void) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) {
Settings.webcam_config.mirror = XdrvMailbox.payload;
- wc_set_options(3, Settings.webcam_config.mirror);
+ WcSetOptions(3, Settings.webcam_config.mirror);
}
ResponseCmndStateText(Settings.webcam_config.mirror);
}
@@ -958,7 +919,7 @@ void CmndWebcamMirror(void) {
void CmndWebcamFlip(void) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) {
Settings.webcam_config.flip = XdrvMailbox.payload;
- wc_set_options(2, Settings.webcam_config.flip);
+ WcSetOptions(2, Settings.webcam_config.flip);
}
ResponseCmndStateText(Settings.webcam_config.flip);
}
@@ -966,7 +927,7 @@ void CmndWebcamFlip(void) {
void CmndWebcamSaturation(void) {
if ((XdrvMailbox.payload >= -2) && (XdrvMailbox.payload <= 2)) {
Settings.webcam_config.saturation = XdrvMailbox.payload +2;
- wc_set_options(6, Settings.webcam_config.saturation -2);
+ WcSetOptions(6, Settings.webcam_config.saturation -2);
}
ResponseCmndNumber(Settings.webcam_config.saturation -2);
}
@@ -974,7 +935,7 @@ void CmndWebcamSaturation(void) {
void CmndWebcamBrightness(void) {
if ((XdrvMailbox.payload >= -2) && (XdrvMailbox.payload <= 2)) {
Settings.webcam_config.brightness = XdrvMailbox.payload +2;
- wc_set_options(5, Settings.webcam_config.brightness -2);
+ WcSetOptions(5, Settings.webcam_config.brightness -2);
}
ResponseCmndNumber(Settings.webcam_config.brightness -2);
}
@@ -982,7 +943,7 @@ void CmndWebcamBrightness(void) {
void CmndWebcamContrast(void) {
if ((XdrvMailbox.payload >= -2) && (XdrvMailbox.payload <= 2)) {
Settings.webcam_config.contrast = XdrvMailbox.payload +2;
- wc_set_options(4, Settings.webcam_config.contrast -2);
+ WcSetOptions(4, Settings.webcam_config.contrast -2);
}
ResponseCmndNumber(Settings.webcam_config.contrast -2);
}
@@ -996,10 +957,10 @@ bool Xdrv81(uint8_t function) {
switch (function) {
case FUNC_LOOP:
- wc_loop();
+ WcLoop();
break;
case FUNC_WEB_ADD_HANDLER:
- wc_pic_setup();
+ WcPicSetup();
break;
case FUNC_WEB_ADD_MAIN_BUTTON:
WcShowStream();