Просмотр исходного кода

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 лет назад
Родитель
Сommit
58cbd680e8

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

@@ -27,6 +27,7 @@
 #include "esp_camera.h"
 
 #include "driver/ledc.h"
+#include "server_tflite.h"
 
 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 {
         httpd_req_t *req;
@@ -76,6 +79,21 @@ typedef struct {
 } 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)
 {
 #ifdef USE_PWM_LEDFLASH
@@ -219,6 +237,7 @@ void CCamera::EnableAutoExposure(int flash_duration)
 esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
 {
     string ftype;
+    int _size;
 
     uint8_t *zwischenspeicher = NULL;
 
@@ -255,7 +274,12 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
         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);
     if (!zwischenspeicher)
     {
@@ -454,14 +478,23 @@ esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay)
     }
 
     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);
-        } 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);
@@ -640,3 +673,84 @@ bool CCamera::getCameraInitSuccessful()
 {
     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);
         bool CameraInitSuccessful = false;
+        bool demoMode = false;
+
+        bool loadNextDemoImage(camera_fb_t *fb);
+        long GetFileSize(std::string filename);
 
     public:
         int image_height, image_width;
@@ -40,8 +44,10 @@ class CCamera {
         bool SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation);
         void GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol);
         void SetLEDIntensity(float _intrel);
+        bool testCamera(void);
         void EnableAutoExposure(int flash_duration);
         bool getCameraInitSuccessful();
+        void useDemoMode(void);
        
 
         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);
             Camera.SetLEDIntensity(ledintensity);
         }
+
+        if ((toUpper(splitted[0]) == "DEMO") && (splitted.size() > 1))
+        {
+            if (toUpper(splitted[1]) == "TRUE")
+                Camera.useDemoMode();
+        }
     }
 
     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!");
                 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!");
                 /* 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.
@@ -282,7 +281,6 @@ extern "C" void app_main(void)
                    setSystemStatusFlag(SYSTEM_STATUS_CAM_FB_BAD);
             }
             else {
-                esp_camera_fb_return(fb);   
                 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/img_tmp");
     MakeDir("/sdcard/log");
+    MakeDir("/sdcard/demo");
     printf("Nach Start des Post Handlers\n");
 
     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
 ImageSize = VGA
 FixedExposure = false
+;Demo = false
 
 [Alignment]
 InitialRotate = 179

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

@@ -121,6 +121,23 @@ textarea {
 			<td class="description">
 				Time to keep the raw image (in days, resp. "0" = forever)
 			</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 class="expert" id="ex1">
 			
@@ -1778,6 +1795,7 @@ function UpdateInput() {
 
 	WriteParameter(param, category, "MakeImage", "LogImageLocation", true);
 	WriteParameter(param, category, "MakeImage", "LogfileRetentionInDays", true);
+	WriteParameter(param, category, "MakeImage", "Demo", true);
 	WriteParameter(param, category, "MakeImage", "WaitBeforeTakingPicture", false);		
 	WriteParameter(param, category, "MakeImage", "ImageQuality", false);		
 	WriteParameter(param, category, "MakeImage", "Brightness", false);		
@@ -1897,7 +1915,8 @@ function ReadParameterAll()
 	category["GPIO"]["enabled"] = document.getElementById("Category_GPIO_enabled").checked;
 	
 	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", "ImageQuality", 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, "WaitBeforeTakingPicture");
      ParamAddValue(param, catname, "LogfileRetentionInDays");
+     ParamAddValue(param, catname, "Demo");
      ParamAddValue(param, catname, "Brightness");
      ParamAddValue(param, catname, "Contrast");
      ParamAddValue(param, catname, "Saturation");