diff --git a/code/components/jomjol_controlcamera/ClassControllCamera.cpp b/code/components/jomjol_controlcamera/ClassControllCamera.cpp index 659a0251..fb5e071b 100644 --- a/code/components/jomjol_controlcamera/ClassControllCamera.cpp +++ b/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,16 +67,33 @@ static camera_config_t camera_config = { }; - - CCamera Camera; +uint8_t *demoImage = NULL; // Buffer holding the demo image in bytes + +#define DEMO_IMAGE_SIZE 30000 // Max size of demo image in bytes + typedef struct { httpd_req_t *req; size_t len; } 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 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; +} diff --git a/code/components/jomjol_controlcamera/ClassControllCamera.h b/code/components/jomjol_controlcamera/ClassControllCamera.h index fe375a0d..d9d32135 100644 --- a/code/components/jomjol_controlcamera/ClassControllCamera.h +++ b/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); diff --git a/code/components/jomjol_flowcontroll/ClassFlowMakeImage.cpp b/code/components/jomjol_flowcontroll/ClassFlowMakeImage.cpp index 235681ae..ea5a45cb 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowMakeImage.cpp +++ b/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); diff --git a/code/main/main.cpp b/code/main/main.cpp index d66c1297..c37512a8 100644 --- a/code/main/main.cpp +++ b/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); } } diff --git a/code/main/softAP.cpp b/code/main/softAP.cpp index 7462f3b1..bca2290f 100644 --- a/code/main/softAP.cpp +++ b/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"); diff --git a/demo-images/readme.md b/demo-images/readme.md new file mode 100644 index 00000000..eda5bb48 --- /dev/null +++ b/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 diff --git a/demo-images/watermeter/520.8983.jpg b/demo-images/watermeter/520.8983.jpg new file mode 100644 index 00000000..3189ce85 Binary files /dev/null and b/demo-images/watermeter/520.8983.jpg differ diff --git a/demo-images/watermeter/520.9086.jpg b/demo-images/watermeter/520.9086.jpg new file mode 100644 index 00000000..1931f4b7 Binary files /dev/null and b/demo-images/watermeter/520.9086.jpg differ diff --git a/demo-images/watermeter/520.9351.jpg b/demo-images/watermeter/520.9351.jpg new file mode 100644 index 00000000..3c440251 Binary files /dev/null and b/demo-images/watermeter/520.9351.jpg differ diff --git a/demo-images/watermeter/520.9787.jpg b/demo-images/watermeter/520.9787.jpg new file mode 100644 index 00000000..9454ad9b Binary files /dev/null and b/demo-images/watermeter/520.9787.jpg differ diff --git a/demo-images/watermeter/521.0213.jpg b/demo-images/watermeter/521.0213.jpg new file mode 100644 index 00000000..3414129f Binary files /dev/null and b/demo-images/watermeter/521.0213.jpg differ diff --git a/demo-images/watermeter/521.0465.jpg b/demo-images/watermeter/521.0465.jpg new file mode 100644 index 00000000..adb1073d Binary files /dev/null and b/demo-images/watermeter/521.0465.jpg differ diff --git a/demo-images/watermeter/521.0929.jpg b/demo-images/watermeter/521.0929.jpg new file mode 100644 index 00000000..2119fade Binary files /dev/null and b/demo-images/watermeter/521.0929.jpg differ diff --git a/demo-images/watermeter/521.1232.jpg b/demo-images/watermeter/521.1232.jpg new file mode 100644 index 00000000..fbfd9e20 Binary files /dev/null and b/demo-images/watermeter/521.1232.jpg differ diff --git a/demo-images/watermeter/521.1708.jpg b/demo-images/watermeter/521.1708.jpg new file mode 100644 index 00000000..8684d9a4 Binary files /dev/null and b/demo-images/watermeter/521.1708.jpg differ diff --git a/demo-images/watermeter/521.2043.jpg b/demo-images/watermeter/521.2043.jpg new file mode 100644 index 00000000..98a58e68 Binary files /dev/null and b/demo-images/watermeter/521.2043.jpg differ diff --git a/demo-images/watermeter/521.2170.jpg b/demo-images/watermeter/521.2170.jpg new file mode 100644 index 00000000..ac04a1b3 Binary files /dev/null and b/demo-images/watermeter/521.2170.jpg differ diff --git a/demo-images/watermeter/521.2413.jpg b/demo-images/watermeter/521.2413.jpg new file mode 100644 index 00000000..26635ff0 Binary files /dev/null and b/demo-images/watermeter/521.2413.jpg differ diff --git a/demo-images/watermeter/521.2575.jpg b/demo-images/watermeter/521.2575.jpg new file mode 100644 index 00000000..5ecf3497 Binary files /dev/null and b/demo-images/watermeter/521.2575.jpg differ diff --git a/demo-images/watermeter/521.2853.jpg b/demo-images/watermeter/521.2853.jpg new file mode 100644 index 00000000..f4ae0a50 Binary files /dev/null and b/demo-images/watermeter/521.2853.jpg differ diff --git a/demo-images/watermeter/521.3027.jpg b/demo-images/watermeter/521.3027.jpg new file mode 100644 index 00000000..c759fefc Binary files /dev/null and b/demo-images/watermeter/521.3027.jpg differ diff --git a/demo-images/watermeter/files.txt b/demo-images/watermeter/files.txt new file mode 100644 index 00000000..1ef504a2 --- /dev/null +++ b/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 diff --git a/demo-images/watermeter/overview.png b/demo-images/watermeter/overview.png new file mode 100644 index 00000000..4d4ac34e Binary files /dev/null and b/demo-images/watermeter/overview.png differ diff --git a/sd-card/config/config.ini b/sd-card/config/config.ini index 8670fd0c..9a309d69 100644 --- a/sd-card/config/config.ini +++ b/sd-card/config/config.ini @@ -9,6 +9,7 @@ LEDIntensity = 50 ImageQuality = 12 ImageSize = VGA FixedExposure = false +;Demo = false [Alignment] InitialRotate = 179 diff --git a/sd-card/html/edit_config_param.html b/sd-card/html/edit_config_param.html index 33cda42e..554ca5e9 100644 --- a/sd-card/html/edit_config_param.html +++ b/sd-card/html/edit_config_param.html @@ -121,6 +121,23 @@ textarea { Time to keep the raw image (in days, resp. "0" = forever) + + + + + + + + + + + Enable to use demo images instead of the real camera images.
+ Make sore to have a demo folder on your SD-Card! + See Details on Demo Mode. + @@ -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); diff --git a/sd-card/html/readconfigparam.js b/sd-card/html/readconfigparam.js index 5cba11b9..0a721fa5 100644 --- a/sd-card/html/readconfigparam.js +++ b/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");