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 "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,6 +478,14 @@ esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay)
|
||||
}
|
||||
|
||||
if(res == ESP_OK){
|
||||
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 {
|
||||
if(fb->format == PIXFORMAT_JPEG){
|
||||
fb_len = fb->len;
|
||||
res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
|
||||
@@ -464,6 +496,7 @@ esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay)
|
||||
fb_len = jchunk.len;
|
||||
}
|
||||
}
|
||||
}
|
||||
esp_camera_fb_return(fb);
|
||||
int64_t fr_end = esp_timer_get_time();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -273,8 +273,7 @@ extern "C" void app_main(void)
|
||||
setSystemStatusFlag(SYSTEM_STATUS_CAM_BAD);
|
||||
}
|
||||
} else { // Test Camera
|
||||
camera_fb_t * fb = esp_camera_fb_get();
|
||||
if (!fb) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
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
|
||||
ImageSize = VGA
|
||||
FixedExposure = false
|
||||
;Demo = false
|
||||
|
||||
[Alignment]
|
||||
InitialRotate = 179
|
||||
|
||||
@@ -122,6 +122,23 @@ textarea {
|
||||
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">
|
||||
|
||||
<td class="indent1">
|
||||
@@ -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);
|
||||
@@ -1898,6 +1916,7 @@ function ReadParameterAll()
|
||||
|
||||
ReadParameter(param, "MakeImage", "LogImageLocation", 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);
|
||||
|
||||
@@ -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");
|
||||
|
||||