From 4aa1c18d822944f60bda4c27391211b333135846 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 11 May 2020 16:45:22 +0200 Subject: [PATCH 1/9] Revert uart buffer size increase Revert uart buffer size increase but visit uart buffer reads more often to solve possible uart buffer overrun caused by sleep --- tasmota/support_tasmota.ino | 2 +- tasmota/tasmota.ino | 38 ++++++++++++++++++++++--------------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 728d20f7f..b1cc56a68 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -1232,7 +1232,7 @@ void SerialInput(void) } else if ((serial_in_byte_counter == INPUT_BUFFER_SIZE) #ifdef ESP8266 -// || Serial.hasOverrun() // Default ESP8266 Serial buffer size is 256. Tasmota increases to INPUT_BUFFER_SIZE + || Serial.hasOverrun() // Default ESP8266 Serial buffer size is 256. Tasmota increases to INPUT_BUFFER_SIZE #endif ) { serial_buffer_overrun = true; diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index d1d841810..091b6eed3 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -198,8 +198,7 @@ char web_log[WEB_LOG_SIZE] = {'\0'}; // Web log buffer * Main \*********************************************************************************************/ -void setup(void) -{ +void setup(void) { #ifdef ESP32 #ifdef DISABLE_ESP32_BROWNOUT DisableBrownout(); // Workaround possible weak LDO resulting in brownout detection during Wifi connection @@ -216,7 +215,7 @@ void setup(void) RtcRebootSave(); Serial.begin(APP_BAUDRATE); - Serial.setRxBufferSize(INPUT_BUFFER_SIZE); // Default is 256 chars +// Serial.setRxBufferSize(INPUT_BUFFER_SIZE); // Default is 256 chars seriallog_level = LOG_LEVEL_INFO; // Allow specific serial messages until config loaded snprintf_P(my_version, sizeof(my_version), PSTR("%d.%d.%d"), VERSION >> 24 & 0xff, VERSION >> 16 & 0xff, VERSION >> 8 & 0xff); // Release version 6.3.0 @@ -325,8 +324,7 @@ void setup(void) XsnsCall(FUNC_INIT); } -void BacklogLoop(void) -{ +void BacklogLoop(void) { if (TimeReached(backlog_delay)) { if (!BACKLOG_EMPTY && !backlog_mutex) { #ifdef SUPPORT_IF_STATEMENT @@ -345,8 +343,18 @@ void BacklogLoop(void) } } -void loop(void) -{ +void SleepDelay(uint32_t mseconds) { + if (mseconds) { + for (uint32_t wait = 0; wait < mseconds; wait++) { + delay(1); + if (Serial.available()) { break; } // We need to service serial buffer ASAP as otherwise we get uart buffer overrun + } + } else { + delay(0); + } +} + +void loop(void) { uint32_t my_sleep = millis(); XdrvCall(FUNC_LOOP); @@ -396,23 +404,23 @@ void loop(void) uint32_t my_activity = millis() - my_sleep; - if (Settings.flag3.sleep_normal) { // SetOption60 - Enable normal sleep instead of dynamic sleep - // yield(); // yield == delay(0), delay contains yield, auto yield in loop - delay(ssleep); // https://github.com/esp8266/Arduino/issues/2021 + if (Settings.flag3.sleep_normal) { // SetOption60 - Enable normal sleep instead of dynamic sleep + // yield(); // yield == delay(0), delay contains yield, auto yield in loop + SleepDelay(ssleep); // https://github.com/esp8266/Arduino/issues/2021 } else { if (my_activity < (uint32_t)ssleep) { - delay((uint32_t)ssleep - my_activity); // Provide time for background tasks like wifi + SleepDelay((uint32_t)ssleep - my_activity); // Provide time for background tasks like wifi } else { if (global_state.wifi_down) { - delay(my_activity /2); // If wifi down and my_activity > setoption36 then force loop delay to 1/3 of my_activity period + SleepDelay(my_activity /2); // If wifi down and my_activity > setoption36 then force loop delay to 1/3 of my_activity period } } } - if (!my_activity) { my_activity++; } // We cannot divide by 0 + if (!my_activity) { my_activity++; } // We cannot divide by 0 uint32_t loop_delay = ssleep; - if (!loop_delay) { loop_delay++; } // We cannot divide by 0 - uint32_t loops_per_second = 1000 / loop_delay; // We need to keep track of this many loops per second + if (!loop_delay) { loop_delay++; } // We cannot divide by 0 + uint32_t loops_per_second = 1000 / loop_delay; // We need to keep track of this many loops per second uint32_t this_cycle_ratio = 100 * my_activity / loop_delay; loop_load_avg = loop_load_avg - (loop_load_avg / loops_per_second) + (this_cycle_ratio / loops_per_second); // Take away one loop average away and add the new one } From 6d7f3843ce226884eb935d76c311446952c41948 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Mon, 11 May 2020 19:19:59 +0200 Subject: [PATCH 2/9] scripter bug fix --- tasmota/xdrv_10_scripter.ino | 52 +++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index d16c0bd31..e8d46e246 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -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; } From 2ff5a1ccb6b762b26e112e22c462d4ee06bdd31d Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 11 May 2020 21:16:17 +0200 Subject: [PATCH 3/9] Add Zigbee support for router and end-device mode --- tasmota/CHANGELOG.md | 1 + tasmota/i18n.h | 2 +- tasmota/my_user_config.h | 3 + tasmota/xdrv_23_zigbee_7_statemachine.ino | 199 +++++++++++++++++++--- tasmota/xdrv_23_zigbee_8_parsers.ino | 33 ++++ 5 files changed, 218 insertions(+), 20 deletions(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index dcdcf2840..d059823d0 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -15,6 +15,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..0d00692be 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -484,7 +484,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/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/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 }, From a6aad08a5e031927a45bbe5d8dbcff083845177c Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Tue, 12 May 2020 08:14:58 +0200 Subject: [PATCH 4/9] webcam bugfix and some cleanup --- tasmota/xdrv_81_webcam.ino | 129 ++++++++++++------------------------- 1 file changed, 42 insertions(+), 87 deletions(-) diff --git a/tasmota/xdrv_81_webcam.ino b/tasmota/xdrv_81_webcam.ino index 95ec03cc4..4c988f57e 100644 --- a/tasmota/xdrv_81_webcam.ino +++ b/tasmota/xdrv_81_webcam.ino @@ -25,8 +25,10 @@ * 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 + * Command: prefix = WC + * Stream = 1,0 => start, stop streaming + * Resolution = 0 .. 10 => set resolution + * 0 = FRAMESIZE_96x96, (96x96) * 1 = FRAMESIZE_QQVGA2 (128x160) * 2 = FRAMESIZE_QCIF (176x144) * 3 = FRAMESIZE_HQVGA (240x176) @@ -37,18 +39,27 @@ * 8 = FRAMESIZE_XGA (1024x768) * 9 = FRAMESIZE_SXGA (1280x1024) * 10 = FRAMESIZE_UXGA (1600x1200) + * Mirror = 1,0 => mirror picture on,off + * Flip = 1,0 => Flip picture on,off + * Saturation = -2 ... +2 => set picture Saturation + * Brightness = -2 ... +2 => set picture Brightness + * Contrast = -2 ... +2 => set picture Contrast * * 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 @@ -56,6 +67,8 @@ #include "fd_forward.h" #include "fr_forward.h" + +// CAMERA_MODEL_AI_THINKER default template pins #define PWDN_GPIO_NUM 32 #define RESET_GPIO_NUM -1 #define XCLK_GPIO_NUM 0 @@ -134,24 +147,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; @@ -191,7 +186,6 @@ uint32_t wc_setup(int32_t fsiz) { config.pin_reset = RESET_GPIO_NUM; AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Default template")); } -#endif //ESP.getPsramSize() @@ -229,14 +223,7 @@ uint32_t wc_setup(int32_t fsiz) { } 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 @@ -338,27 +325,6 @@ uint32_t get_picstore(int32_t num, uint8_t **buff) { return picstore[num].len; } -uint32_t wc_get_jpeg(uint8_t **buff) { - size_t _jpg_buf_len = 0; - uint8_t * _jpg_buf = NULL; - 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; - } - } 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; @@ -442,13 +408,25 @@ void HandleImage(void) { 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 + 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) { @@ -457,6 +435,7 @@ void HandleImage(void) { } client.write((char *)picstore[bnum].buff, picstore[bnum].len); } + client.stop(); AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Sending image #: %d"), bnum+1); } @@ -468,11 +447,12 @@ WiFiClient client; void handleMjpeg(void) { AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Handle camserver")); - if (!wc_stream_active) { +// if (!wc_stream_active) { +// always restart stream wc_stream_active = 1; client = CamServer->client(); AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Create client")); - } +// } } void HandleImageTheo(void) { @@ -860,32 +840,7 @@ void wc_pic_setup(void) { 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) { From d182817892297d0935fb8f6bad4e3d9734eab406 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Tue, 12 May 2020 14:19:10 +0200 Subject: [PATCH 5/9] add psram to info page ESP32 --- tasmota/language/bg_BG.h | 2 ++ tasmota/language/cs_CZ.h | 2 ++ tasmota/language/de_DE.h | 2 ++ tasmota/language/el_GR.h | 2 ++ tasmota/language/en_GB.h | 2 ++ tasmota/language/es_ES.h | 2 ++ tasmota/language/fr_FR.h | 2 ++ tasmota/language/he_HE.h | 2 ++ tasmota/language/hu_HU.h | 2 ++ tasmota/language/it_IT.h | 2 ++ tasmota/language/ko_KO.h | 2 ++ tasmota/language/nl_NL.h | 2 ++ tasmota/language/pl_PL.h | 2 ++ tasmota/language/pt_BR.h | 2 ++ tasmota/language/pt_PT.h | 2 ++ tasmota/language/ro_RO.h | 2 ++ tasmota/language/ru_RU.h | 2 ++ tasmota/language/sk_SK.h | 2 ++ tasmota/language/sv_SE.h | 2 ++ tasmota/language/tr_TR.h | 2 ++ tasmota/language/uk_UA.h | 2 ++ tasmota/language/zh_CN.h | 2 ++ tasmota/language/zh_TW.h | 2 ++ tasmota/xdrv_01_webserver.ino | 6 ++++++ 24 files changed, 52 insertions(+) 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/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); From 7078466e7c6434591ecf4bb6b2c6028828934160 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 12 May 2020 14:30:07 +0200 Subject: [PATCH 6/9] Refactor webcam --- tasmota/sendemail.ino | 4 +- tasmota/xdrv_10_scripter.ino | 20 +- tasmota/xdrv_81_webcam.ino | 678 ++++++++++++++++++----------------- 3 files changed, 354 insertions(+), 348 deletions(-) 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/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index e8d46e246..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 diff --git a/tasmota/xdrv_81_webcam.ino b/tasmota/xdrv_81_webcam.ino index 4c988f57e..efc5fa32c 100644 --- a/tasmota/xdrv_81_webcam.ino +++ b/tasmota/xdrv_81_webcam.ino @@ -25,25 +25,25 @@ * 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: prefix = WC - * Stream = 1,0 => start, stop streaming - * Resolution = 0 .. 10 => 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) - * Mirror = 1,0 => mirror picture on,off - * Flip = 1,0 => Flip picture on,off - * Saturation = -2 ... +2 => set picture Saturation - * Brightness = -2 ... +2 => set picture Brightness - * Contrast = -2 ... +2 => set picture Contrast + * 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 @@ -60,13 +60,20 @@ #define XDRV_81 81 - -#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 @@ -87,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; @@ -100,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++) { @@ -120,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; @@ -133,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; } @@ -165,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; @@ -184,7 +189,7 @@ 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")); } //ESP.getPsramSize() @@ -200,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 @@ -218,7 +223,7 @@ 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; } @@ -242,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; } @@ -250,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; } @@ -289,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; @@ -297,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; @@ -305,199 +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_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; +// optional motion detector +void WcDetectMotion(void) { + camera_fb_t *wc_fb; + uint8_t *out_buf = 0; - 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) { - size_t _jpg_buf_len = 0; - uint8_t * _jpg_buf = NULL; - camera_fb_t *wc_fb = 0; + if ((millis()-motion_ltime) > motion_detect) { + motion_ltime = millis(); 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; + + 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; } - 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(WC_LOGLEVEL, PSTR("CAM: No image #: %d"), bnum); - return; - } - client.write((char *)picstore[bnum].buff, picstore[bnum].len); + esp_camera_fb_return(wc_fb); } - client.stop(); - - 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) { -// always restart stream - 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 @@ -569,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(); @@ -594,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; } @@ -608,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; } @@ -634,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, PSTR("CAM: Sending image #: %d"), bnum+1); +} + +void HandleImageBasic(void) { + if (!HttpCheckPriviledgedAccess()) { 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(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, 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; @@ -644,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"); @@ -659,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; } } @@ -667,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; } @@ -684,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"); @@ -701,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; @@ -785,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 { @@ -797,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) { @@ -822,26 +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); -} - - - void WcInit(void) { if (!Settings.webcam_config.data) { Settings.webcam_config.stream = 1; @@ -897,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); } @@ -905,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); } @@ -913,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); } @@ -921,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); } @@ -929,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); } @@ -937,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); } @@ -951,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(); From d0daea1037df56b712d656d515035f47c117d87e Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 12 May 2020 14:54:18 +0200 Subject: [PATCH 7/9] Change ESP32 from Wemos to ESP32dev - Change ESP32 from Wemos to ESP32dev - Add environment tasmota32-webcam using esp32cam --- platformio_override_sample.ini | 5 +++-- platformio_tasmota_env32.ini | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) 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..b5b2dcb78 100644 --- a/platformio_tasmota_env32.ini +++ b/platformio_tasmota_env32.ini @@ -17,6 +17,11 @@ 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 + [env:tasmota32-minimal] extends = env:tasmota32 build_flags = ${common32.build_flags} -DFIRMWARE_MINIMAL From 640c834bad86421a573e7908b2a31cc0cd63ff7e Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 12 May 2020 14:59:41 +0200 Subject: [PATCH 8/9] Add ESP32webcam to CI --- .github/workflows/CI_github_ESP32.yml | 17 +++++++++++++++++ platformio_tasmota_env32.ini | 1 + 2 files changed, 18 insertions(+) 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_tasmota_env32.ini b/platformio_tasmota_env32.ini index b5b2dcb78..e7f321464 100644 --- a/platformio_tasmota_env32.ini +++ b/platformio_tasmota_env32.ini @@ -21,6 +21,7 @@ build_flags = ${common32.build_flags} extends = env:tasmota32 board = esp32cam board_build.f_cpu = 240000000L +build_flags = ${common32.build_flags} -DUSE_WEBCAM [env:tasmota32-minimal] extends = env:tasmota32 From af32b3f5e58b4af00124ed6a8d8bf413e36561c0 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 12 May 2020 15:42:16 +0200 Subject: [PATCH 9/9] Add ESP32 configuration webcam --- platformio_tasmota_env32.ini | 2 +- tasmota/i18n.h | 2 ++ tasmota/support_command.ino | 6 ++++ tasmota/tasmota_configurations.h | 4 +++ tasmota/tasmota_configurations_ESP32.h | 40 ++++++++++++++++++++++++++ tasmota/xdrv_81_webcam.ino | 6 ++-- 6 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 tasmota/tasmota_configurations_ESP32.h diff --git a/platformio_tasmota_env32.ini b/platformio_tasmota_env32.ini index e7f321464..f4f99282c 100644 --- a/platformio_tasmota_env32.ini +++ b/platformio_tasmota_env32.ini @@ -21,7 +21,7 @@ build_flags = ${common32.build_flags} extends = env:tasmota32 board = esp32cam board_build.f_cpu = 240000000L -build_flags = ${common32.build_flags} -DUSE_WEBCAM +build_flags = ${common32.build_flags} -DFIRMWARE_WEBCAM [env:tasmota32-minimal] extends = env:tasmota32 diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 0d00692be..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" 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_81_webcam.ino b/tasmota/xdrv_81_webcam.ino index efc5fa32c..fb2c86161 100644 --- a/tasmota/xdrv_81_webcam.ino +++ b/tasmota/xdrv_81_webcam.ino @@ -642,13 +642,13 @@ void HandleImage(void) { } client.stop(); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Sending image #: %d"), bnum+1); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("CAM: Sending image #: %d"), bnum+1); } void HandleImageBasic(void) { if (!HttpCheckPriviledgedAccess()) { return; } - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Capture image")); + AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP "Capture image")); if (Settings.webcam_config.stream) { if (!CamServer) { @@ -686,7 +686,7 @@ void HandleImageBasic(void) { esp_camera_fb_return(wc_fb); // Free frame buffer - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: Image sent")); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("CAM: Image sent")); } void HandleWebcamMjpeg(void) {