diff --git a/tasmota/sendemail.ino b/tasmota/sendemail.ino index a93c51358..a02babb63 100644 --- a/tasmota/sendemail.ino +++ b/tasmota/sendemail.ino @@ -462,6 +462,11 @@ void attach_Array(char *aname) { char buff[64]; sprintf_P(buff,PSTR("Content-Disposition: attachment; filename=\"%s.txt\"\r\n\r\n"), aname); g_client->write(buff); + // send timestamp + strcpy(buff, GetDateAndTime(DT_LOCAL).c_str()); + strcat(buff,"\t"); + g_client->write(buff); + float *fp=array; for (uint32_t cnt = 0; cnt Ticker Script_ticker1; @@ -991,6 +995,11 @@ void form1000(uint32_t number, char *dp, char sc) { #define SCRIPT_UDP_PORT 1999 IPAddress script_udp_remote_ip; +void Restart_globvars(void) { + Script_Stop_UDP(); + Script_Init_UDP(); +} + void Script_Stop_UDP(void) { if (!glob_script_mem.udp_flags.udp_used) return; if (glob_script_mem.udp_flags.udp_connected) { @@ -1862,8 +1871,12 @@ chknext: while (*lp==' ') lp++; float fvar2; lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + float prio = STASK_PRIO; + if (*lp!=')') { + lp = GetNumericArgument(lp, OPER_EQU, &prio, 0); + } lp++; - fvar = scripter_create_task(fvar, fvar1, fvar2); + fvar = scripter_create_task(fvar, fvar1, fvar2, prio); len = 0; goto exit; } @@ -4042,12 +4055,20 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonParserObject *jo) { int8_t mode = fvar; digitalWrite(pinnr, mode & 1); goto next_line; - } else if (!strncmp(lp, "svars(", 5)) { + } else if (!strncmp(lp, "svars", 5)) { lp += 5; // save vars Scripter_save_pvars(); goto next_line; } +#ifdef USE_SCRIPT_GLOBVARS + else if (!strncmp(lp, "gvr", 3)) { + lp += 3; + // reset global vars udp server + Restart_globvars(); + goto next_line; + } +#endif #ifdef USE_LIGHT #ifdef USE_WS2812 else if (!strncmp(lp, "ws2812(", 7)) { @@ -4712,7 +4733,6 @@ void script_upload_start(void) { //if (upload_file) upload_file.write(upload.buf,upload.currentSize); } else if(upload.status == UPLOAD_FILE_END) { - AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload close")); //if (upload_file) upload_file.close(); if (Web.upload_error) { AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload error")); @@ -4721,6 +4741,7 @@ void script_upload_start(void) { bitWrite(Settings.rule_enabled, 0, sc_state); SaveScript(); SaveScriptEnd(); + //AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload success")); } } else { Web.upload_error = 1; @@ -6476,6 +6497,10 @@ void ScriptWebShow(char mc) { lp++; } } + char *cv_ptr; + float cv_max=0; + float cv_inc=0; + float *cv_count=0; while (lp) { while (*lp==SCRIPT_EOL) { lp++; @@ -6485,6 +6510,41 @@ void ScriptWebShow(char mc) { } if (*lp!=';') { // send this line to web + SCRIPT_SKIP_SPACES + if (!strncmp(lp, "%for ", 5)) { + // for next loop + struct T_INDEX ind; + uint8_t vtype; + lp = isvar(lp + 5, &vtype, &ind, 0, 0, 0); + if ((vtype!=VAR_NV) && (vtype&STYPE)==0) { + uint16_t index = glob_script_mem.type[ind.index].index; + cv_count = &glob_script_mem.fvars[index]; + SCRIPT_SKIP_SPACES + lp = GetNumericArgument(lp , OPER_EQU, cv_count, 0); + SCRIPT_SKIP_SPACES + lp = GetNumericArgument(lp , OPER_EQU, &cv_max, 0); + SCRIPT_SKIP_SPACES + lp = GetNumericArgument(lp , OPER_EQU, &cv_inc, 0); + cv_ptr = lp; + goto nextwebline; + } else { + continue; + } + } else if (!strncmp(lp, "%next", 5)) { + if (cv_count) { + // for next loop + *cv_count += cv_inc; + if (*cv_count<=cv_max) { + lp = cv_ptr; + } else { + cv_count = 0; + goto nextwebline; + } + } else { + goto nextwebline; + } + } + Replace_Cmd_Vars(lp, 1, tmp, sizeof(tmp)); char *lin = tmp; if ((!mc && (*lin!='$')) || (mc=='w' && (*lin!='$'))) { @@ -7030,10 +7090,6 @@ bool RulesProcessEvent(char *json_event) { #define STASK_STACK 8192 #endif -#ifndef STASK_PRIO -#define STASK_PRIO 1 -#endif //ESP32 - #if 1 struct ESP32_Task { @@ -7073,17 +7129,17 @@ void script_task2(void *arg) { } } } -uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core) { +uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core, uint32_t prio) { //return 0; BaseType_t res = 0; if (core > 1) { core = 1; } if (num == 1) { if (esp32_tasks[0].task_t) { vTaskDelete(esp32_tasks[0].task_t); } - res = xTaskCreatePinnedToCore(script_task1, "T1", STASK_STACK, NULL, STASK_PRIO, &esp32_tasks[0].task_t, core); + res = xTaskCreatePinnedToCore(script_task1, "T1", STASK_STACK, NULL, prio, &esp32_tasks[0].task_t, core); esp32_tasks[0].task_timer = time; } else { if (esp32_tasks[1].task_t) { vTaskDelete(esp32_tasks[1].task_t); } - res = xTaskCreatePinnedToCore(script_task2, "T2", STASK_STACK, NULL, STASK_PRIO, &esp32_tasks[1].task_t, core); + res = xTaskCreatePinnedToCore(script_task2, "T2", STASK_STACK, NULL, prio, &esp32_tasks[1].task_t, core); esp32_tasks[1].task_timer = time; } return res; @@ -7109,17 +7165,17 @@ void script_task2(void *arg) { } } -uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core) { +uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core, uint32_t prio) { //return 0; BaseType_t res = 0; if (core > 1) { core = 1; } if (num == 1) { if (task_t1) { vTaskDelete(task_t1); } - res = xTaskCreatePinnedToCore(script_task1, "T1", STASK_STACK, NULL, STASK_PRIO, &task_t1, core); + res = xTaskCreatePinnedToCore(script_task1, "T1", STASK_STACK, NULL, prio, &task_t1, core); task_timer1 = time; } else { if (task_t2) { vTaskDelete(task_t2); } - res = xTaskCreatePinnedToCore(script_task2, "T2", STASK_STACK, NULL, STASK_PRIO, &task_t2, core); + res = xTaskCreatePinnedToCore(script_task2, "T2", STASK_STACK, NULL, prio, &task_t2, core); task_timer2 = time; } return res; diff --git a/tasmota/xdrv_81_webcam.ino b/tasmota/xdrv_81_webcam.ino old mode 100644 new mode 100755 index b0dab47b7..f56e4c7ff --- a/tasmota/xdrv_81_webcam.ino +++ b/tasmota/xdrv_81_webcam.ino @@ -105,6 +105,19 @@ struct { #endif } Wc; +#ifdef ENABLE_RTSPSERVER +#include +#include +#include +#include +WiFiServer rtspServer(8554); +CStreamer *rtsp_streamer; +CRtspSession *rtsp_session; +WiFiClient rtsp_client; +uint8_t rtsp_start; +OV2640 cam; +#endif + /*********************************************************************************************/ bool WcPinUsed(void) { @@ -199,7 +212,6 @@ uint32_t WcSetup(int32_t fsiz) { //esp_log_level_set("*", ESP_LOG_INFO); - // if PSRAM IC present, init with UXGA resolution and higher JPEG quality // for larger pre-allocated frame buffer. @@ -216,11 +228,12 @@ uint32_t WcSetup(int32_t fsiz) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CAM: PSRAM not found")); } +// AddLog_P2(LOG_LEVEL_INFO, PSTR("CAM: heap check 1: %d"),ESP_getFreeHeap()); + // stupid workaround camera diver eats up static ram should prefer PSRAM // so we steal static ram to force driver to alloc PSRAM - //ESP.getMaxAllocHeap() - -// void *x=malloc(70000); +// uint32_t maxfram = ESP.getMaxAllocHeap(); +// void *x=malloc(maxfram-4096); void *x = 0; esp_err_t err = esp_camera_init(&config); if (x) { free(x); } @@ -230,6 +243,8 @@ uint32_t WcSetup(int32_t fsiz) { return 0; } +// AddLog_P2(LOG_LEVEL_INFO, PSTR("CAM: heap check 2: %d"),ESP_getFreeHeap()); + sensor_t * wc_s = esp_camera_sensor_get(); wc_s->set_vflip(wc_s, Settings.webcam_config.flip); @@ -819,11 +834,16 @@ uint32_t WcSetStreamserver(uint32_t flag) { void WcStreamControl() { WcSetStreamserver(Settings.webcam_config.stream); - int resolution = (!Settings.webcam_config.stream) ? -1 : Settings.webcam_config.resolution; - WcSetup(resolution); + WcSetup(Settings.webcam_config.resolution); } /*********************************************************************************************/ +#ifdef ENABLE_RTSPSERVER +static uint32_t rtsp_lastframe_time; +#ifndef RTSP_FRAME_TIME +#define RTSP_FRAME_TIME 100 +#endif +#endif void WcLoop(void) { if (CamServer) { @@ -834,6 +854,45 @@ void WcLoop(void) { #ifdef USE_FACE_DETECT if (Wc.face_detect_time) { WcDetectFace(); } #endif + +#ifdef ENABLE_RTSPSERVER + + if (!rtsp_start && !global_state.wifi_down && Wc.up) { + rtspServer.begin(); + rtsp_start = 1; + AddLog_P2(LOG_LEVEL_INFO, PSTR("CAM: RTSP init")); + rtsp_lastframe_time = millis(); + } + + // If we have an active client connection, just service that until gone + if (rtsp_session) { + rtsp_session->handleRequests(0); // we don't use a timeout here, + // instead we send only if we have new enough frames + + uint32_t now = millis(); + if ((now-rtsp_lastframe_time) > RTSP_FRAME_TIME) { + rtsp_session->broadcastCurrentFrame(now); + rtsp_lastframe_time = now; + // AddLog_P2(LOG_LEVEL_INFO, PSTR("CAM: RTSP session frame")); + } + + if (rtsp_session->m_stopped) { + delete rtsp_session; + delete rtsp_streamer; + rtsp_session = NULL; + rtsp_streamer = NULL; + AddLog_P2(LOG_LEVEL_INFO, PSTR("CAM: RTSP stopped")); + } + } + else { + rtsp_client = rtspServer.accept(); + if (rtsp_client) { + rtsp_streamer = new OV2640Streamer(&rtsp_client, cam); // our streamer for UDP/TCP based RTP transport + rtsp_session = new CRtspSession(&rtsp_client, rtsp_streamer); // our threads RTSP session and state + AddLog_P2(LOG_LEVEL_INFO, PSTR("CAM: RTSP stream created")); + } + } +#endif } void WcPicSetup(void) { @@ -880,15 +939,16 @@ void WcInit(void) { #define D_CMND_WC_SATURATION "Saturation" #define D_CMND_WC_BRIGHTNESS "Brightness" #define D_CMND_WC_CONTRAST "Contrast" +#define D_CMND_WC_INIT "Init" const char kWCCommands[] PROGMEM = D_PRFX_WEBCAM "|" // Prefix "|" D_CMND_WC_STREAM "|" D_CMND_WC_RESOLUTION "|" D_CMND_WC_MIRROR "|" D_CMND_WC_FLIP "|" - D_CMND_WC_SATURATION "|" D_CMND_WC_BRIGHTNESS "|" D_CMND_WC_CONTRAST + D_CMND_WC_SATURATION "|" D_CMND_WC_BRIGHTNESS "|" D_CMND_WC_CONTRAST "|" D_CMND_WC_INIT ; void (* const WCCommand[])(void) PROGMEM = { &CmndWebcam, &CmndWebcamStream, &CmndWebcamResolution, &CmndWebcamMirror, &CmndWebcamFlip, - &CmndWebcamSaturation, &CmndWebcamBrightness, &CmndWebcamContrast + &CmndWebcamSaturation, &CmndWebcamBrightness, &CmndWebcamContrast, &CmndWebcamInit }; void CmndWebcam(void) { @@ -956,6 +1016,11 @@ void CmndWebcamContrast(void) { ResponseCmndNumber(Settings.webcam_config.contrast -2); } +void CmndWebcamInit(void) { + WcStreamControl(); + ResponseCmndDone(); +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ diff --git a/tasmota/xdsp_12_ST7789.ino b/tasmota/xdsp_12_ST7789.ino index d31c55fa4..703bd1d05 100644 --- a/tasmota/xdsp_12_ST7789.ino +++ b/tasmota/xdsp_12_ST7789.ino @@ -108,7 +108,8 @@ void ST7789_InitDriver() #endif // init renderer, may use hardware spi - if (PinUsed(GPIO_SPI_CS) && (Pin(GPIO_SPI_MOSI)==HW_SPI_MOSI) && (Pin(GPIO_SPI_CLK)==HW_SPI_CLK) && PinUsed(GPIO_SPI_DC)) { + //if (PinUsed(GPIO_SPI_CS) && (Pin(GPIO_SPI_MOSI)==HW_SPI_MOSI) && (Pin(GPIO_SPI_CLK)==HW_SPI_CLK) && PinUsed(GPIO_SPI_DC)) { + if ((Pin(GPIO_SPI_MOSI)==HW_SPI_MOSI) && (Pin(GPIO_SPI_CLK)==HW_SPI_CLK) && PinUsed(GPIO_SPI_DC)) { st7789 = new Arduino_ST7789(Pin(GPIO_SPI_DC), reset, cs, bppin); } else { if ((PinUsed(GPIO_SSPI_CS) || PinUsed(GPIO_OLED_RESET)) && PinUsed(GPIO_SSPI_MOSI) && PinUsed(GPIO_SSPI_SCLK) && PinUsed(GPIO_SSPI_DC)) {