Переглянути джерело

Add demo mode (#1720)

* move main part to cam file

* added demo mode

* .

* add a define to configure the logfile handling (#1709)

Co-authored-by: CaCO3 <caco@ruinelli.ch>

* Move Logfile Switch to define.h

* Update Reboot Algo

* Update server_ota.cpp

* Avoid loading of status infos twice (#1711)

* Force a reboot even reboot task cannot be created due to lack of heap (#1713)

* Deinit all components before reboot

* Update

* Update

* Force reboot when reboot task cannot be created

* Improve log message when web UI is incomplete (#1716)

* improve warning if version.txt is missing

* typo

* show round duration in log

Co-authored-by: CaCO3 <caco@ruinelli.ch>

* .

* .

* .

* creade demo dir

* fix static IP in UP, improve explanation for HA (#1719)

* fix static IP in UP, improve explanation for HA

* Update edit_config_param.html

Co-authored-by: CaCO3 <caco@ruinelli.ch>

* Create demo folder at startup (if not present)

* move demo files

* Update defines.h (#1726)

* updated description

* moved to expert section

* fixed broken enabled state

Co-authored-by: CaCO3 <caco@ruinelli.ch>
Co-authored-by: jomjol <30766535+jomjol@users.noreply.github.com>
Co-authored-by: Slider0007 <115730895+Slider0007@users.noreply.github.com>
CaCO3 3 роки тому
батько
коміт
58cbd680e8

+ 123 - 9
code/components/jomjol_controlcamera/ClassControllCamera.cpp

@@ -27,6 +27,7 @@
 #include "esp_camera.h"
 #include "esp_camera.h"
 
 
 #include "driver/ledc.h"
 #include "driver/ledc.h"
+#include "server_tflite.h"
 
 
 static const char *TAG = "CAM"; 
 static const char *TAG = "CAM"; 
 
 
@@ -66,9 +67,11 @@ static camera_config_t camera_config = {
 };
 };
 
 
 
 
+CCamera Camera;
 
 
+uint8_t *demoImage = NULL; // Buffer holding the demo image in bytes
 
 
-CCamera Camera;
+#define DEMO_IMAGE_SIZE 30000 // Max size of demo image in bytes
 
 
 typedef struct {
 typedef struct {
         httpd_req_t *req;
         httpd_req_t *req;
@@ -76,6 +79,21 @@ typedef struct {
 } jpg_chunking_t;
 } jpg_chunking_t;
 
 
 
 
+bool CCamera::testCamera(void) {
+    bool success;
+    camera_fb_t *fb = esp_camera_fb_get();
+    if (fb)  {
+        success = true;
+    }
+    else {
+        success = false;
+    }
+    
+    esp_camera_fb_return(fb);
+    return success;
+}
+
+
 void CCamera::ledc_init(void)
 void CCamera::ledc_init(void)
 {
 {
 #ifdef USE_PWM_LEDFLASH
 #ifdef USE_PWM_LEDFLASH
@@ -219,6 +237,7 @@ void CCamera::EnableAutoExposure(int flash_duration)
 esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
 esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
 {
 {
     string ftype;
     string ftype;
+    int _size;
 
 
     uint8_t *zwischenspeicher = NULL;
     uint8_t *zwischenspeicher = NULL;
 
 
@@ -255,7 +274,12 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
         return ESP_FAIL;
         return ESP_FAIL;
     }
     }
 
 
-    int _size = fb->len;
+    if (demoMode) { // Use images stored on SD-Card instead of camera image
+        /* Replace Framebuffer with image from SD-Card */
+        loadNextDemoImage(fb);
+    }
+
+    _size = fb->len;
     zwischenspeicher = (uint8_t*) malloc(_size);
     zwischenspeicher = (uint8_t*) malloc(_size);
     if (!zwischenspeicher)
     if (!zwischenspeicher)
     {
     {
@@ -454,14 +478,23 @@ esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay)
     }
     }
 
 
     if(res == ESP_OK){
     if(res == ESP_OK){
-        if(fb->format == PIXFORMAT_JPEG){
-            fb_len = fb->len;
+        if (demoMode) { // Use images stored on SD-Card instead of camera image
+            LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Using Demo image!");
+            /* Replace Framebuffer with image from SD-Card */
+            loadNextDemoImage(fb);
+
             res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
             res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
-        } else {
-            jpg_chunking_t jchunk = {req, 0};
-            res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk)?ESP_OK:ESP_FAIL;
-            httpd_resp_send_chunk(req, NULL, 0);
-            fb_len = jchunk.len;
+        }
+        else {
+            if(fb->format == PIXFORMAT_JPEG){
+                fb_len = fb->len;
+                res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
+            } else {
+                jpg_chunking_t jchunk = {req, 0};
+                res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk)?ESP_OK:ESP_FAIL;
+                httpd_resp_send_chunk(req, NULL, 0);
+                fb_len = jchunk.len;
+            }
         }
         }
     }
     }
     esp_camera_fb_return(fb);
     esp_camera_fb_return(fb);
@@ -640,3 +673,84 @@ bool CCamera::getCameraInitSuccessful()
 {
 {
     return CameraInitSuccessful;
     return CameraInitSuccessful;
 }
 }
+
+std::vector<std::string> demoFiles;
+
+void CCamera::useDemoMode()
+{
+    char line[50];
+
+    FILE *fd = fopen("/sdcard/demo/files.txt", "r");
+    if (!fd) {
+        LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Please provide the demo files first!");
+        return;
+    }
+
+    demoImage = (uint8_t*)malloc(DEMO_IMAGE_SIZE);
+    if (demoImage == NULL) {
+        LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Unable to acquire required memory for demo image!");
+        return;
+    }
+
+    while (fgets(line, sizeof(line), fd) != NULL) {
+        line[strlen(line) - 1] = '\0';
+        demoFiles.push_back(line);
+    }
+    
+    fclose(fd);
+
+    LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Using Demo mode (" + std::to_string(demoFiles.size()) + 
+            " files) instead of real camera image!");
+
+    for (auto file : demoFiles) {
+        LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, file);
+    }
+
+    demoMode = true;
+}
+
+
+bool CCamera::loadNextDemoImage(camera_fb_t *fb) {
+    char filename[50];
+    int readBytes;
+    long fileSize;
+
+    snprintf(filename, sizeof(filename), "/sdcard/demo/%s", demoFiles[getCountFlowRounds() % demoFiles.size()].c_str());
+
+    LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Using " + std::string(filename) + " as demo image");
+
+    /* Inject saved image */
+
+    FILE * fp = fopen(filename, "rb");
+    if (!fp) {
+        LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + std::string(filename) +"!");
+        return false;
+    }
+
+    fileSize = GetFileSize(filename);
+    if (fileSize > DEMO_IMAGE_SIZE) {
+        char buf[100];
+        snprintf(buf, sizeof(buf), "Demo Image (%d bytes) is larger than provided buffer (%d bytes)!",
+                (int)fileSize, DEMO_IMAGE_SIZE);
+        LogFile.WriteToFile(ESP_LOG_ERROR, TAG, std::string(buf));
+        return false;
+    }
+
+    readBytes = fread(demoImage, 1, DEMO_IMAGE_SIZE, fp);
+    LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "read " + std::to_string(readBytes) + " bytes");
+    fclose(fp);
+
+    fb->buf = demoImage; // Update pointer
+    fb->len = readBytes;
+    // ToDo do we also need to set height, width, format and timestamp?
+
+    return true;
+}
+
+
+long CCamera::GetFileSize(std::string filename)
+{
+    struct stat stat_buf;
+    long rc = stat(filename.c_str(), &stat_buf);
+    return rc == 0 ? stat_buf.st_size : -1;
+}

+ 6 - 0
code/components/jomjol_controlcamera/ClassControllCamera.h

@@ -26,6 +26,10 @@ class CCamera {
 
 
         void ledc_init(void);
         void ledc_init(void);
         bool CameraInitSuccessful = false;
         bool CameraInitSuccessful = false;
+        bool demoMode = false;
+
+        bool loadNextDemoImage(camera_fb_t *fb);
+        long GetFileSize(std::string filename);
 
 
     public:
     public:
         int image_height, image_width;
         int image_height, image_width;
@@ -40,8 +44,10 @@ class CCamera {
         bool SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation);
         bool SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation);
         void GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol);
         void GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol);
         void SetLEDIntensity(float _intrel);
         void SetLEDIntensity(float _intrel);
+        bool testCamera(void);
         void EnableAutoExposure(int flash_duration);
         void EnableAutoExposure(int flash_duration);
         bool getCameraInitSuccessful();
         bool getCameraInitSuccessful();
+        void useDemoMode(void);
        
        
 
 
         framesize_t TextToFramesize(const char * text);
         framesize_t TextToFramesize(const char * text);

+ 6 - 0
code/components/jomjol_flowcontroll/ClassFlowMakeImage.cpp

@@ -140,6 +140,12 @@ bool ClassFlowMakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
             ledintensity = max((float) 0, ledintensity);
             ledintensity = max((float) 0, ledintensity);
             Camera.SetLEDIntensity(ledintensity);
             Camera.SetLEDIntensity(ledintensity);
         }
         }
+
+        if ((toUpper(splitted[0]) == "DEMO") && (splitted.size() > 1))
+        {
+            if (toUpper(splitted[1]) == "TRUE")
+                Camera.useDemoMode();
+        }
     }
     }
 
 
     Camera.SetBrightnessContrastSaturation(_brightness, _contrast, _saturation);
     Camera.SetBrightnessContrastSaturation(_brightness, _contrast, _saturation);

+ 2 - 4
code/main/main.cpp

@@ -272,9 +272,8 @@ extern "C" void app_main(void)
                 LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Check that your camera module is working and connected properly!");
                 LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Check that your camera module is working and connected properly!");
                 setSystemStatusFlag(SYSTEM_STATUS_CAM_BAD);
                 setSystemStatusFlag(SYSTEM_STATUS_CAM_BAD);
             }
             }
-        } else { // Test Camera            
-            camera_fb_t * fb = esp_camera_fb_get();
-            if (!fb) {
+        } else { // Test Camera    
+            if (!Camera.testCamera()) {
                 LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Camera Framebuffer cannot be initialized!");
                 LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Camera Framebuffer cannot be initialized!");
                 /* Easiest would be to simply restart here and try again,
                 /* Easiest would be to simply restart here and try again,
                    how ever there seem to be systems where it fails at startup but still work corectly later.
                    how ever there seem to be systems where it fails at startup but still work corectly later.
@@ -282,7 +281,6 @@ extern "C" void app_main(void)
                    setSystemStatusFlag(SYSTEM_STATUS_CAM_FB_BAD);
                    setSystemStatusFlag(SYSTEM_STATUS_CAM_FB_BAD);
             }
             }
             else {
             else {
-                esp_camera_fb_return(fb);   
                 Camera.LightOnOff(false);
                 Camera.LightOnOff(false);
             }
             }
         }
         }

+ 1 - 0
code/main/softAP.cpp

@@ -339,6 +339,7 @@ esp_err_t upload_post_handlerAP(httpd_req_t *req)
     MakeDir("/sdcard/html");
     MakeDir("/sdcard/html");
     MakeDir("/sdcard/img_tmp");
     MakeDir("/sdcard/img_tmp");
     MakeDir("/sdcard/log");
     MakeDir("/sdcard/log");
+    MakeDir("/sdcard/demo");
     printf("Nach Start des Post Handlers\n");
     printf("Nach Start des Post Handlers\n");
 
 
     LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "upload_post_handlerAP");
     LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "upload_post_handlerAP");

+ 2 - 0
demo-images/readme.md

@@ -0,0 +1,2 @@
+# Images for Demo Mode
+See https://github.com/jomjol/AI-on-the-edge-device/wiki/Demo-Mode for details

BIN
demo-images/watermeter/520.8983.jpg


BIN
demo-images/watermeter/520.9086.jpg


BIN
demo-images/watermeter/520.9351.jpg


BIN
demo-images/watermeter/520.9787.jpg


BIN
demo-images/watermeter/521.0213.jpg


BIN
demo-images/watermeter/521.0465.jpg


BIN
demo-images/watermeter/521.0929.jpg


BIN
demo-images/watermeter/521.1232.jpg


BIN
demo-images/watermeter/521.1708.jpg


BIN
demo-images/watermeter/521.2043.jpg


BIN
demo-images/watermeter/521.2170.jpg


BIN
demo-images/watermeter/521.2413.jpg


BIN
demo-images/watermeter/521.2575.jpg


BIN
demo-images/watermeter/521.2853.jpg


BIN
demo-images/watermeter/521.3027.jpg


+ 15 - 0
demo-images/watermeter/files.txt

@@ -0,0 +1,15 @@
+520.8983.jpg
+520.9086.jpg
+520.9351.jpg
+520.9787.jpg
+521.0213.jpg
+521.0465.jpg
+521.0929.jpg
+521.1232.jpg
+521.1708.jpg
+521.2043.jpg
+521.2170.jpg
+521.2413.jpg
+521.2575.jpg
+521.2853.jpg
+521.3027.jpg

BIN
demo-images/watermeter/overview.png


+ 1 - 0
sd-card/config/config.ini

@@ -9,6 +9,7 @@ LEDIntensity = 50
 ImageQuality = 12
 ImageQuality = 12
 ImageSize = VGA
 ImageSize = VGA
 FixedExposure = false
 FixedExposure = false
+;Demo = false
 
 
 [Alignment]
 [Alignment]
 InitialRotate = 179
 InitialRotate = 179

+ 20 - 1
sd-card/html/edit_config_param.html

@@ -121,6 +121,23 @@ textarea {
 			<td class="description">
 			<td class="description">
 				Time to keep the raw image (in days, resp. "0" = forever)
 				Time to keep the raw image (in days, resp. "0" = forever)
 			</td>
 			</td>
+		</tr>		
+		<tr class="expert" id="ex1">
+			<td class="indent1">
+				<input type="checkbox" id="MakeImage_Demo_enabled" value="1"  onclick = 'InvertEnableItem("MakeImage", "Demo")' unchecked >
+				<label for=MakeImage_Demo_enabled><class id="MakeImage_Demo_text" style="color:black;">Demo Mode</class></label>
+			</td>
+			<td>
+				<select id="MakeImage_Demo_value1">
+					<option value="true">true</option>
+					<option value="false" selected>false</option>
+				</select>
+			</td>
+			<td style="font-size: 80%;">
+				Enable to use demo images instead of the real camera images.<br>
+				Make sore to have a demo folder on your SD-Card!
+				See Details on <a href="https://github.com/jomjol/AI-on-the-edge-device/wiki/Demo-Mode" target="_blank">Demo Mode</a>.
+			</td>
 		</tr>
 		</tr>
 		<tr class="expert" id="ex1">
 		<tr class="expert" id="ex1">
 			
 			
@@ -1778,6 +1795,7 @@ function UpdateInput() {
 
 
 	WriteParameter(param, category, "MakeImage", "LogImageLocation", true);
 	WriteParameter(param, category, "MakeImage", "LogImageLocation", true);
 	WriteParameter(param, category, "MakeImage", "LogfileRetentionInDays", true);
 	WriteParameter(param, category, "MakeImage", "LogfileRetentionInDays", true);
+	WriteParameter(param, category, "MakeImage", "Demo", true);
 	WriteParameter(param, category, "MakeImage", "WaitBeforeTakingPicture", false);		
 	WriteParameter(param, category, "MakeImage", "WaitBeforeTakingPicture", false);		
 	WriteParameter(param, category, "MakeImage", "ImageQuality", false);		
 	WriteParameter(param, category, "MakeImage", "ImageQuality", false);		
 	WriteParameter(param, category, "MakeImage", "Brightness", false);		
 	WriteParameter(param, category, "MakeImage", "Brightness", false);		
@@ -1897,7 +1915,8 @@ function ReadParameterAll()
 	category["GPIO"]["enabled"] = document.getElementById("Category_GPIO_enabled").checked;
 	category["GPIO"]["enabled"] = document.getElementById("Category_GPIO_enabled").checked;
 	
 	
 	ReadParameter(param, "MakeImage", "LogImageLocation", true);
 	ReadParameter(param, "MakeImage", "LogImageLocation", true);
-	ReadParameter(param, "MakeImage", "LogfileRetentionInDays", true);
+	ReadParameter(param, "MakeImage", "LogfileRetentionInDays", true);	
+	ReadParameter(param, "MakeImage", "Demo", true);
 	ReadParameter(param, "MakeImage", "WaitBeforeTakingPicture", false);		
 	ReadParameter(param, "MakeImage", "WaitBeforeTakingPicture", false);		
 	ReadParameter(param, "MakeImage", "ImageQuality", false);		
 	ReadParameter(param, "MakeImage", "ImageQuality", false);		
 	ReadParameter(param, "MakeImage", "Brightness", false);		
 	ReadParameter(param, "MakeImage", "Brightness", false);		

+ 1 - 0
sd-card/html/readconfigparam.js

@@ -119,6 +119,7 @@ function ParseConfig() {
      ParamAddValue(param, catname, "LogImageLocation");
      ParamAddValue(param, catname, "LogImageLocation");
      ParamAddValue(param, catname, "WaitBeforeTakingPicture");
      ParamAddValue(param, catname, "WaitBeforeTakingPicture");
      ParamAddValue(param, catname, "LogfileRetentionInDays");
      ParamAddValue(param, catname, "LogfileRetentionInDays");
+     ParamAddValue(param, catname, "Demo");
      ParamAddValue(param, catname, "Brightness");
      ParamAddValue(param, catname, "Brightness");
      ParamAddValue(param, catname, "Contrast");
      ParamAddValue(param, catname, "Contrast");
      ParamAddValue(param, catname, "Saturation");
      ParamAddValue(param, catname, "Saturation");