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>
@@ -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,16 +67,33 @@ static camera_config_t camera_config = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CCamera Camera;
|
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 {
|
typedef struct {
|
||||||
httpd_req_t *req;
|
httpd_req_t *req;
|
||||||
size_t len;
|
size_t len;
|
||||||
} 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){
|
if (demoMode) { // Use images stored on SD-Card instead of camera image
|
||||||
fb_len = fb->len;
|
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};
|
else {
|
||||||
res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk)?ESP_OK:ESP_FAIL;
|
if(fb->format == PIXFORMAT_JPEG){
|
||||||
httpd_resp_send_chunk(req, NULL, 0);
|
fb_len = fb->len;
|
||||||
fb_len = jchunk.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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
} else { // Test Camera
|
||||||
camera_fb_t * fb = esp_camera_fb_get();
|
if (!Camera.testCamera()) {
|
||||||
if (!fb) {
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
demo-images/readme.md
Normal file
@@ -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
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
demo-images/watermeter/520.9086.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
demo-images/watermeter/520.9351.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
demo-images/watermeter/520.9787.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
demo-images/watermeter/521.0213.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
demo-images/watermeter/521.0465.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
demo-images/watermeter/521.0929.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
demo-images/watermeter/521.1232.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
demo-images/watermeter/521.1708.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
demo-images/watermeter/521.2043.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
demo-images/watermeter/521.2170.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
demo-images/watermeter/521.2413.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
demo-images/watermeter/521.2575.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
demo-images/watermeter/521.2853.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
demo-images/watermeter/521.3027.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
15
demo-images/watermeter/files.txt
Normal file
@@ -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
Normal file
|
After Width: | Height: | Size: 25 KiB |
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||