Compare commits

..

37 Commits

Author SHA1 Message Date
jomjol
4c407499d2 Update Influxdb 2023-02-27 22:19:10 +01:00
jomjol
b97d808b54 Merge branch 'bugfix-influxdb-v15.0.1' 2023-02-27 21:55:38 +01:00
jomjol
9f2e91a9df Bugfix #1933 (again :-)) 2023-02-27 21:54:50 +01:00
CaCO3
10e0435383 Merge branch 'rolling'
# Conflicts:
#	Changelog.md
#	code/components/jomjol_helper/Helper.cpp
#	code/components/jomjol_helper/Helper.h
#	sd-card/html/edit_config_param.html
#	sd-card/html/readconfigparam.js
2023-02-24 20:19:45 +01:00
CaCO3
2c1a7f4c9e DataLogActive is true by default 2023-02-23 23:39:02 +01:00
CaCO3
3d711f495e updated save message 2023-02-22 23:20:52 +01:00
jomjol
b21e3c6c9d Remove ";Topic = ", Delete Checkbox "AllowNegativeRate" 2023-02-22 21:38:31 +01:00
CaCO3
23d2ae627d Update config.ini 2023-02-22 11:33:44 +01:00
CaCO3
3f62abf878 add missing config.ini entries for InfluxDBv2 2023-02-22 09:42:25 +01:00
CaCO3
025f4af9f2 V14.1 backport to rolling (#2058)
* Migrate parameters to v14.1 branch (#2023)

* Migrated parameters

* -

* .

* .

* .

* .

* .

* Remove unneeded checkboxes for true/false

* Remove ";"

* Correct MaintTopic

* Added missing parameters to UI: FlipImageSize, InitialMirror
Removed checkbox in UI for ErrorMessage
Added migration of pboolean parameters: enable them if they where disabled, set them to their default value, then enable them
Switch SetRetainFlag internally to a boolean

* .

* CamImages -> RawImages

* CamImages -> RawImages

* catch error on unknown parameter

* fix missing case insensitivity

* fix typo

* fixmissing rename

* fix migration of ExtendedResolution

* Delete ClassFlowMakeImage.cpp

* Delete ClassFlowMakeImage.h

---------

Co-authored-by: CaCO3 <caco@ruinelli.ch>
Co-authored-by: jomjol <30766535+jomjol@users.noreply.github.com>

* Update Changelog.md

---------

Co-authored-by: CaCO3 <caco@ruinelli.ch>
Co-authored-by: jomjol <30766535+jomjol@users.noreply.github.com>
2023-02-21 23:18:24 +01:00
CaCO3
3d92860c5e Updated web pages (#2055)
* allow longer file paths

* updated web pages

* updated the setup pages

* .

* Update server_file.cpp

---------

Co-authored-by: CaCO3 <caco@ruinelli.ch>
2023-02-21 22:35:50 +01:00
CaCO3
2ed9fb8eb5 Create folders as needed (#2056)
* allow longer file paths

* create folders as needed

---------

Co-authored-by: CaCO3 <caco@ruinelli.ch>
2023-02-21 22:14:09 +01:00
jomjol
598db004ae Update Changelog.md 2023-02-20 20:16:39 +01:00
jomjol
70332fe142 Update tflite 2023-02-20 20:03:39 +01:00
Slider0007
10da8c4f94 Support saturation & contrast (#2048)
* reference image: use correct value for contrast

* Update quality, contrast & saturation parameter

* fix for saturation,contrast+error handling
2023-02-19 21:15:16 +01:00
jomjol
5bac1c68d9 Correct BugFix in InfluxDB 2023-02-18 12:46:14 +01:00
jomjol
008dba7e11 fix for #2036 2023-02-18 11:44:56 +01:00
Yonz
7283bfd506 Update FeatureRequest.md (#2033)
Added:  #35 Use the same model, but provide the image from a Smartphone Camera
2023-02-16 21:05:34 +01:00
CaCO3
44e186e65b Update backup.html (#2015) 2023-02-11 21:45:08 +01:00
jomjol
55be652dc1 Update Changelog.md 2023-02-09 20:16:18 +01:00
jomjol
1acd72d33e Implement InfluxDB v2 (#2004)
* Implement InfluxDBv2 Innitial

* Update incl. UI

* Correct UI

* Update UI - Indiv. Param.

* Update edit_config_param.html

* Correct Timeshift

* Update Fieldname
2023-02-09 20:13:08 +01:00
jomjol
795bcd0d21 Merge branch 'InfluxDB-Fix-Timeshift' into rolling 2023-02-08 20:31:17 +01:00
jomjol
0b2e38935b Update Changelog.md 2023-02-08 20:29:01 +01:00
jomjol
a9c5bebb45 Update interface_influxdb.cpp 2023-02-08 19:56:28 +01:00
Joerg Rosenkranz
876adc51af Fix small typo (#1995) 2023-02-08 19:54:10 +01:00
CaCO3
bf090f3762 Update README.md 2023-02-07 23:13:11 +01:00
CaCO3
3fa16c5624 Use the preprocessed Web UI for the manual.zip (#1983)
* Update build.yaml

* Update build.yaml

* Update build.yaml
2023-02-05 15:31:04 +01:00
Slider0007
b9134f923e ota_page: Add missing quotation mark (#1977) 2023-02-05 08:10:59 +01:00
CaCO3
06f4d417b5 Cleanup config (#1972)
* Update config.ini

* Update config.ini

* Update config.ini
2023-02-05 08:10:29 +01:00
Slider0007
55efc3b3f4 wifi disconnect before deinit (#1978) 2023-02-05 08:09:53 +01:00
jomjol
800e231301 Update Changelog.md 2023-02-04 21:45:02 +01:00
jomjol
34a3d6d6e3 Merge branch 'Increase-max-JPG-size' into rolling 2023-02-04 21:38:37 +01:00
CaCO3
4bfe5422c5 fix broken webinstaller update 2023-02-03 21:46:08 +01:00
CaCO3
d63dc08f33 Update manual-update-webinstaller.yml 2023-02-03 21:43:40 +01:00
Slider0007
2ee85001eb Cleanup defines.h (#1967)
* Update defines.h

* Update: Disable USE_HIMEM_IF_AVAILABLE
2023-02-03 21:02:32 +01:00
Nicolas Liaudat
1d2f920819 Option alignment algo off (#1924)
* Update edit_config_param.html

* Update ClassFlowAlignment.cpp
2023-02-03 21:01:43 +01:00
jomjol
69583db99e Correct influx utc (#1964)
* Update interface_influxdb.cpp

* Correct to UTC
2023-02-02 19:14:55 +01:00
45 changed files with 1154 additions and 305 deletions

View File

@@ -1,4 +1,4 @@
## [15.0.1] - 2023-02-23 ## [Unreleased]
**Parameter Migration** **Parameter Migration**
@@ -27,7 +27,8 @@ If you want to revert back to `v14` or earlier, you will have to revert the migr
#### Fixed #### Fixed
- [2036](https://github.com/jomjol/AI-on-the-edge-device/issues/2036) Fix wrong url-encoding - [#2036](https://github.com/jomjol/AI-on-the-edge-device/issues/2036) Fix wrong url-encoding
- **NEW v15.0.2:** [#1933](https://github.com/jomjol/AI-on-the-edge-device/issues/1933) Bugfix InfluxDB Timestamp
#### Removed #### Removed

View File

@@ -10,6 +10,11 @@
____ ____
#### #35 Use the same model, but provide the image from a Smartphone Camera
as reading the Electricity or Water meter every few minutues only delivers apparent accuracy (DE: "Scheingenauigkeit") you could just as well take a picture with your Smartphone evey so often (e.g. once a week when you are in the Basement anyway), then with some "semi clever" tricks pass this image to the model developed here, and the values then on to who ever needs them e.g. via MQTT.
IMO: It is not needed to have that many readings (datapoints) as our behaviour (Use of electricity or water) doesn't vary that much, say, over a weeks time. The interpolation between weekly readings will give sufficient information on the power and/or water usage.
#### #34 implement state and Roi for water leak detection #### #34 implement state and Roi for water leak detection
for example see Roi on the next picture.. for example see Roi on the next picture..
![grafik](https://user-images.githubusercontent.com/38385805/207858812-2a6ba41d-1a8c-4fa1-9b6a-53cdd113c106.png) ![grafik](https://user-images.githubusercontent.com/38385805/207858812-2a6ba41d-1a8c-4fa1-9b6a-53cdd113c106.png)

View File

@@ -63,7 +63,6 @@ static camera_config_t camera_config = {
.fb_count = 1, //if more than one, i2s runs in continuous mode. Use only with JPEG .fb_count = 1, //if more than one, i2s runs in continuous mode. Use only with JPEG
.fb_location = CAMERA_FB_IN_PSRAM, /*!< The location where the frame buffer will be allocated */ .fb_location = CAMERA_FB_IN_PSRAM, /*!< The location where the frame buffer will be allocated */
.grab_mode = CAMERA_GRAB_LATEST, // only from new esp32cam version .grab_mode = CAMERA_GRAB_LATEST, // only from new esp32cam version
}; };
@@ -126,63 +125,97 @@ void CCamera::ledc_init(void)
} }
static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len){ static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len)
{
jpg_chunking_t *j = (jpg_chunking_t *)arg; jpg_chunking_t *j = (jpg_chunking_t *)arg;
if(!index){
if(!index) {
j->len = 0; j->len = 0;
} }
if(httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK){
if(httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK) {
return 0; return 0;
} }
j->len += len; j->len += len;
return len; return len;
} }
bool CCamera::SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation) bool CCamera::SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation)
{ {
bool result = false; _brightness = min(2, max(-2, _brightness));
sensor_t * s = esp_camera_sensor_get(); _contrast = min(2, max(-2, _contrast));
if (_brightness > -100) _saturation = min(2, max(-2, _saturation));
_brightness = min(2, max(-2, _brightness));
if (_contrast > -100)
_contrast = min(2, max(-2, _contrast));
if (_saturation > -100)
_saturation = min(2, max(-2, _saturation));
if (_saturation > -100) sensor_t * s = esp_camera_sensor_get();
if (s) {
s->set_saturation(s, _saturation); s->set_saturation(s, _saturation);
if (_contrast > -100)
s->set_contrast(s, _contrast); s->set_contrast(s, _contrast);
if (_brightness > -100)
s->set_brightness(s, _brightness); s->set_brightness(s, _brightness);
if ((_brightness != brightness) && (_brightness > -100)) /* Workaround - bug in cam library - enable bits are set without using bitwise OR logic -> only latest enable setting is used */
result = true; /* Library version: https://github.com/espressif/esp32-camera/commit/5c8349f4cf169c8a61283e0da9b8cff10994d3f3 */
if ((_contrast != contrast) && (_contrast > -100)) /* Reference: https://esp32.com/viewtopic.php?f=19&t=14376#p93178 */
result = true; /* The memory structure is as follows for
if ((_saturation != saturation) && (_saturation > -100)) byte_0 = enable_bits
result = true; byte_0->bit0 = enable saturation and hue --> OK
byte_0->bit1 = enable saturation --> OK
byte_0->bit2 = enable brightness and contrast --> OK
byte_0->bit3 = enable green -> blue spitial effect (Antique and blunish and greenish and readdish and b&w) enable
byte_0->bit4 = anable gray -> read spitial effect (Antique and blunish and greenish and readdish and b&w) enable
byte_0->bit5 = remove (UV) in YUV color system
byte_0->bit6 = enable negative
byte_0->bit7 = remove (Y) in YUV color system
byte_1 = saturation1 0-255 --> ?
byte_2 = hue 0-255 --> OK
byte_3 = saturation2 0-255 --> OK
byte_4 = reenter saturation2 in documents --> ?
byte_5 = spital effect green -> blue 0-255 --> ?
byte_6 = spital effect gray -> read 0-255 --> ?
byte_7 = contrast lower byte 0-255 --> OK
byte_8 = contrast higher byte 0-255 --> OK
byte_9 = brightness 0-255 --> OK
byte_10= if byte_10==4 contrast effective --> ?
*/
if (_brightness > -100) //s->set_reg(s, 0x7C, 0xFF, 2); // Optional feature - hue setting: Select byte 2 in register 0x7C to set hue value
brightness = _brightness; //s->set_reg(s, 0x7D, 0xFF, 0); // Optional feature - hue setting: Hue value 0 - 255
if (_contrast > -100) s->set_reg(s, 0xFF, 0x01, 0); // Select DSP bank
contrast = _contrast; s->set_reg(s, 0x7C, 0xFF, 0); // Select byte 0 in register 0x7C
if (_saturation > -100) s->set_reg(s, 0x7D, 7, 7); // Set bit 0, 1, 2 in register 0x7D to enable saturation, contrast, brightness and hue control
saturation = _saturation; }
else {
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SetBrightnessContrastSaturation: Failed to get control structure");
}
if (result && isFixedExposure) if (((_brightness != brightness) || (_contrast != contrast) || (_saturation != saturation)) && isFixedExposure)
EnableAutoExposure(waitbeforepicture_org); EnableAutoExposure(waitbeforepicture_org);
return result; brightness = _brightness;
contrast = _contrast;
saturation = _saturation;
ESP_LOGD(TAG, "brightness %d, contrast: %d, saturation %d", brightness, contrast, saturation);
return true;
} }
void CCamera::SetQualitySize(int qual, framesize_t resol) void CCamera::SetQualitySize(int qual, framesize_t resol)
{ {
qual = min(63, max(8, qual)); // Limit quality from 8..63 (values lower than 8 tent to be unstable)
sensor_t * s = esp_camera_sensor_get(); sensor_t * s = esp_camera_sensor_get();
s->set_quality(s, qual); if (s) {
s->set_framesize(s, resol); s->set_quality(s, qual);
s->set_framesize(s, resol);
}
else {
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SetQualitySize: Failed to get control structure");
}
ActualResolution = resol; ActualResolution = resol;
ActualQuality = qual; ActualQuality = qual;
@@ -191,41 +224,45 @@ void CCamera::SetQualitySize(int qual, framesize_t resol)
image_height = 240; image_height = 240;
image_width = 320; image_width = 320;
} }
if (resol == FRAMESIZE_VGA) else if (resol == FRAMESIZE_VGA)
{ {
image_height = 480; image_height = 480;
image_width = 640; image_width = 640;
} }
} }
void CCamera::EnableAutoExposure(int flash_duration) void CCamera::EnableAutoExposure(int flash_duration)
{ {
ESP_LOGD(TAG, "EnableAutoExposure"); ESP_LOGD(TAG, "EnableAutoExposure");
LEDOnOff(true); LEDOnOff(true);
if (flash_duration > 0) if (flash_duration > 0) {
LightOnOff(true); LightOnOff(true);
const TickType_t xDelay = flash_duration / portTICK_PERIOD_MS; const TickType_t xDelay = flash_duration / portTICK_PERIOD_MS;
vTaskDelay( xDelay ); vTaskDelay( xDelay );
}
camera_fb_t * fb = esp_camera_fb_get(); camera_fb_t * fb = esp_camera_fb_get();
esp_camera_fb_return(fb); esp_camera_fb_return(fb);
fb = esp_camera_fb_get(); fb = esp_camera_fb_get();
if (!fb) { if (!fb) {
ESP_LOGE(TAG, "Camera Capture Failed");
LEDOnOff(false); LEDOnOff(false);
LightOnOff(false); LightOnOff(false);
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Capture Failed (Procedure 'EnableAutoExposure') --> Reboot! " LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "EnableAutoExposure: Capture Failed. "
"Check that your camera module is working and connected properly."); "Check camera module and/or proper electrical connection");
//doReboot(); //doReboot();
} }
esp_camera_fb_return(fb); esp_camera_fb_return(fb);
sensor_t * s = esp_camera_sensor_get(); sensor_t * s = esp_camera_sensor_get();
s->set_gain_ctrl(s, 0); if (s) {
s->set_exposure_ctrl(s, 0); s->set_gain_ctrl(s, 0);
s->set_exposure_ctrl(s, 0);
}
else {
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "EnableAutoExposure: Failed to get control structure to set gain+exposure");
}
LEDOnOff(false); LEDOnOff(false);
LightOnOff(false); LightOnOff(false);
@@ -237,22 +274,21 @@ void CCamera::EnableAutoExposure(int flash_duration)
esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay) esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
{ {
#ifdef DEBUG_DETAIL_ON #ifdef DEBUG_DETAIL_ON
LogFile.WriteHeapInfo("CCamera::CaptureToBasisImage - Start"); LogFile.WriteHeapInfo("CaptureToBasisImage - Start");
#endif #endif
_Image->EmptyImage(); //Delete previous stored raw image -> black image _Image->EmptyImage(); //Delete previous stored raw image -> black image
LEDOnOff(true); LEDOnOff(true);
if (delay > 0) if (delay > 0) {
{
LightOnOff(true); LightOnOff(true);
const TickType_t xDelay = delay / portTICK_PERIOD_MS; const TickType_t xDelay = delay / portTICK_PERIOD_MS;
vTaskDelay( xDelay ); vTaskDelay( xDelay );
} }
#ifdef DEBUG_DETAIL_ON #ifdef DEBUG_DETAIL_ON
LogFile.WriteHeapInfo("CCamera::CaptureToBasisImage - After LightOn"); LogFile.WriteHeapInfo("CaptureToBasisImage - After LightOn");
#endif #endif
camera_fb_t * fb = esp_camera_fb_get(); camera_fb_t * fb = esp_camera_fb_get();
@@ -262,9 +298,8 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
LEDOnOff(false); LEDOnOff(false);
LightOnOff(false); LightOnOff(false);
ESP_LOGE(TAG, "CaptureToBasisImage: Capture Failed"); LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "is not working anymore (CaptureToBasisImage) - most probably caused "
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "is not working anymore (CCamera::CaptureToBasisImage) - most probably caused by a hardware problem (instablility, ...). " "by a hardware problem (instablility, ...). System will reboot.");
"System will reboot.");
doReboot(); doReboot();
return ESP_FAIL; return ESP_FAIL;
@@ -276,11 +311,16 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
} }
CImageBasis* _zwImage = new CImageBasis(); CImageBasis* _zwImage = new CImageBasis();
_zwImage->LoadFromMemory(fb->buf, fb->len); if (_zwImage) {
_zwImage->LoadFromMemory(fb->buf, fb->len);
}
else {
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToBasisImage: Can't allocate _zwImage");
}
esp_camera_fb_return(fb); esp_camera_fb_return(fb);
#ifdef DEBUG_DETAIL_ON #ifdef DEBUG_DETAIL_ON
LogFile.WriteHeapInfo("CCamera::CaptureToBasisImage - After fb_get"); LogFile.WriteHeapInfo("CaptureToBasisImage - After fb_get");
#endif #endif
LEDOnOff(false); LEDOnOff(false);
@@ -292,7 +332,7 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
// vTaskDelay( xDelay ); // wait for power to recover // vTaskDelay( xDelay ); // wait for power to recover
#ifdef DEBUG_DETAIL_ON #ifdef DEBUG_DETAIL_ON
LogFile.WriteHeapInfo("CCamera::CaptureToBasisImage - After LoadFromMemory"); LogFile.WriteHeapInfo("CaptureToBasisImage - After LoadFromMemory");
#endif #endif
stbi_uc* p_target; stbi_uc* p_target;
@@ -320,7 +360,7 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
delete _zwImage; delete _zwImage;
#ifdef DEBUG_DETAIL_ON #ifdef DEBUG_DETAIL_ON
LogFile.WriteHeapInfo("CCamera::CaptureToBasisImage - Done"); LogFile.WriteHeapInfo("CaptureToBasisImage - Done");
#endif #endif
return ESP_OK; return ESP_OK;
@@ -333,8 +373,7 @@ esp_err_t CCamera::CaptureToFile(std::string nm, int delay)
LEDOnOff(true); // Switched off to save power ! LEDOnOff(true); // Switched off to save power !
if (delay > 0) if (delay > 0) {
{
LightOnOff(true); LightOnOff(true);
const TickType_t xDelay = delay / portTICK_PERIOD_MS; const TickType_t xDelay = delay / portTICK_PERIOD_MS;
vTaskDelay( xDelay ); vTaskDelay( xDelay );
@@ -344,11 +383,10 @@ esp_err_t CCamera::CaptureToFile(std::string nm, int delay)
esp_camera_fb_return(fb); esp_camera_fb_return(fb);
fb = esp_camera_fb_get(); fb = esp_camera_fb_get();
if (!fb) { if (!fb) {
ESP_LOGE(TAG, "CaptureToFile: Camera Capture Failed");
LEDOnOff(false); LEDOnOff(false);
LightOnOff(false); LightOnOff(false);
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Capture Failed (CCamera::CaptureToFile) --> Reboot! " LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Capture Failed. "
"Check that your camera module is working and connected properly."); "Check camera module and/or proper electrical connection");
//doReboot(); //doReboot();
return ESP_FAIL; return ESP_FAIL;
@@ -395,24 +433,21 @@ esp_err_t CCamera::CaptureToFile(std::string nm, int delay)
} }
FILE * fp = fopen(nm.c_str(), "wb"); FILE * fp = fopen(nm.c_str(), "wb");
if (fp == NULL) /* If an error occurs during the file creation */ if (fp == NULL) { // If an error occurs during the file creation
{ LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Failed to open file " + nm);
fprintf(stderr, "fopen() failed for '%s'\n", nm.c_str());
} }
else else {
{
fwrite(buf, sizeof(uint8_t), buf_len, fp); fwrite(buf, sizeof(uint8_t), buf_len, fp);
fclose(fp); fclose(fp);
} }
if (converted) if (converted)
free(buf); free(buf);
esp_camera_fb_return(fb); esp_camera_fb_return(fb);
if (delay > 0) if (delay > 0)
{
LightOnOff(false); LightOnOff(false);
}
return ESP_OK; return ESP_OK;
} }
@@ -420,29 +455,26 @@ esp_err_t CCamera::CaptureToFile(std::string nm, int delay)
esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay) esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay)
{ {
camera_fb_t * fb = NULL;
esp_err_t res = ESP_OK; esp_err_t res = ESP_OK;
size_t fb_len = 0; size_t fb_len = 0;
int64_t fr_start = esp_timer_get_time(); int64_t fr_start = esp_timer_get_time();
LEDOnOff(true); LEDOnOff(true);
if (delay > 0) if (delay > 0) {
{
LightOnOff(true); LightOnOff(true);
const TickType_t xDelay = delay / portTICK_PERIOD_MS; const TickType_t xDelay = delay / portTICK_PERIOD_MS;
vTaskDelay( xDelay ); vTaskDelay( xDelay );
} }
camera_fb_t *fb = esp_camera_fb_get();
fb = esp_camera_fb_get();
esp_camera_fb_return(fb); esp_camera_fb_return(fb);
fb = esp_camera_fb_get(); fb = esp_camera_fb_get();
if (!fb) { if (!fb) {
ESP_LOGE(TAG, "Camera capture failed");
LEDOnOff(false); LEDOnOff(false);
LightOnOff(false); LightOnOff(false);
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Capture Failed. "
"Check camera module and/or proper electrical connection");
httpd_resp_send_500(req); httpd_resp_send_500(req);
// doReboot(); // doReboot();
@@ -482,9 +514,7 @@ esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay)
ESP_LOGI(TAG, "JPG: %uKB %ums", (uint32_t)(fb_len/1024), (uint32_t)((fr_end - fr_start)/1000)); ESP_LOGI(TAG, "JPG: %uKB %ums", (uint32_t)(fb_len/1024), (uint32_t)((fr_end - fr_start)/1000));
if (delay > 0) if (delay > 0)
{
LightOnOff(false); LightOnOff(false);
}
return res; return res;
} }
@@ -494,19 +524,18 @@ void CCamera::LightOnOff(bool status)
{ {
GpioHandler* gpioHandler = gpio_handler_get(); GpioHandler* gpioHandler = gpio_handler_get();
if ((gpioHandler != NULL) && (gpioHandler->isEnabled())) { if ((gpioHandler != NULL) && (gpioHandler->isEnabled())) {
ESP_LOGD(TAG, "Use gpioHandler flashLigh"); ESP_LOGD(TAG, "Use gpioHandler to trigger flashlight");
gpioHandler->flashLightEnable(status); gpioHandler->flashLightEnable(status);
} else { }
else {
#ifdef USE_PWM_LEDFLASH #ifdef USE_PWM_LEDFLASH
if (status) if (status) {
{
ESP_LOGD(TAG, "Internal Flash-LED turn on with PWM %d", led_intensity); ESP_LOGD(TAG, "Internal Flash-LED turn on with PWM %d", led_intensity);
ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, led_intensity)); ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, led_intensity));
// Update duty to apply the new value // Update duty to apply the new value
ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL)); ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
} }
else else {
{
ESP_LOGD(TAG, "Internal Flash-LED turn off PWM"); ESP_LOGD(TAG, "Internal Flash-LED turn off PWM");
ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, 0)); ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, 0));
ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL)); ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
@@ -555,33 +584,33 @@ void CCamera::GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol
ESP_LOGD(TAG, "Query: %s", _query); ESP_LOGD(TAG, "Query: %s", _query);
if (httpd_query_key_value(_query, "size", _size, 10) == ESP_OK) if (httpd_query_key_value(_query, "size", _size, 10) == ESP_OK)
{ {
#ifdef DEBUG_DETAIL_ON #ifdef DEBUG_DETAIL_ON
ESP_LOGD(TAG, "Size: %s", _size); ESP_LOGD(TAG, "Size: %s", _size);
#endif #endif
if (strcmp(_size, "QVGA") == 0) if (strcmp(_size, "QVGA") == 0)
resol = FRAMESIZE_QVGA; // 320x240 resol = FRAMESIZE_QVGA; // 320x240
if (strcmp(_size, "VGA") == 0) else if (strcmp(_size, "VGA") == 0)
resol = FRAMESIZE_VGA; // 640x480 resol = FRAMESIZE_VGA; // 640x480
if (strcmp(_size, "SVGA") == 0) else if (strcmp(_size, "SVGA") == 0)
resol = FRAMESIZE_SVGA; // 800x600 resol = FRAMESIZE_SVGA; // 800x600
if (strcmp(_size, "XGA") == 0) else if (strcmp(_size, "XGA") == 0)
resol = FRAMESIZE_XGA; // 1024x768 resol = FRAMESIZE_XGA; // 1024x768
if (strcmp(_size, "SXGA") == 0) else if (strcmp(_size, "SXGA") == 0)
resol = FRAMESIZE_SXGA; // 1280x1024 resol = FRAMESIZE_SXGA; // 1280x1024
if (strcmp(_size, "UXGA") == 0) else if (strcmp(_size, "UXGA") == 0)
resol = FRAMESIZE_UXGA; // 1600x1200 resol = FRAMESIZE_UXGA; // 1600x1200
} }
if (httpd_query_key_value(_query, "quality", _qual, 10) == ESP_OK) if (httpd_query_key_value(_query, "quality", _qual, 10) == ESP_OK)
{ {
#ifdef DEBUG_DETAIL_ON #ifdef DEBUG_DETAIL_ON
ESP_LOGD(TAG, "Quality: %s", _qual); ESP_LOGD(TAG, "Quality: %s", _qual);
#endif #endif
qual = atoi(_qual); qual = atoi(_qual);
if (qual > 63) if (qual > 63) // Limit to max. 63
qual = 63; qual = 63;
if (qual < 0) else if (qual < 8) // Limit to min. 8
qual = 0; qual = 8;
} }
} }
} }
@@ -591,28 +620,29 @@ framesize_t CCamera::TextToFramesize(const char * _size)
{ {
if (strcmp(_size, "QVGA") == 0) if (strcmp(_size, "QVGA") == 0)
return FRAMESIZE_QVGA; // 320x240 return FRAMESIZE_QVGA; // 320x240
if (strcmp(_size, "VGA") == 0) else if (strcmp(_size, "VGA") == 0)
return FRAMESIZE_VGA; // 640x480 return FRAMESIZE_VGA; // 640x480
if (strcmp(_size, "SVGA") == 0) else if (strcmp(_size, "SVGA") == 0)
return FRAMESIZE_SVGA; // 800x600 return FRAMESIZE_SVGA; // 800x600
if (strcmp(_size, "XGA") == 0) else if (strcmp(_size, "XGA") == 0)
return FRAMESIZE_XGA; // 1024x768 return FRAMESIZE_XGA; // 1024x768
if (strcmp(_size, "SXGA") == 0) else if (strcmp(_size, "SXGA") == 0)
return FRAMESIZE_SXGA; // 1280x1024 return FRAMESIZE_SXGA; // 1280x1024
if (strcmp(_size, "UXGA") == 0) else if (strcmp(_size, "UXGA") == 0)
return FRAMESIZE_UXGA; // 1600x1200 return FRAMESIZE_UXGA; // 1600x1200
return ActualResolution; return ActualResolution;
} }
CCamera::CCamera() CCamera::CCamera()
{ {
#ifdef DEBUG_DETAIL_ON #ifdef DEBUG_DETAIL_ON
ESP_LOGD(TAG, "CreateClassCamera"); ESP_LOGD(TAG, "CreateClassCamera");
#endif #endif
brightness = -5; brightness = 0;
contrast = -5; contrast = 0;
saturation = -5; saturation = 0;
isFixedExposure = false; isFixedExposure = false;
ledc_init(); ledc_init();

View File

@@ -996,8 +996,12 @@ std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::st
ESP_LOGI(TAG, "Filename to extract: %s, Zwischenfilename: %s", zw.c_str(), filename_zw.c_str()); ESP_LOGI(TAG, "Filename to extract: %s, Zwischenfilename: %s", zw.c_str(), filename_zw.c_str());
std::string folder = filename_zw.substr(0, filename_zw.find_last_of('/'));
MakeDir(folder);
// extrahieren in zwischendatei // extrahieren in zwischendatei
DeleteFile(filename_zw); DeleteFile(filename_zw);
FILE* fpTargetFile = fopen(filename_zw.c_str(), "wb"); FILE* fpTargetFile = fopen(filename_zw.c_str(), "wb");
uint writtenbytes = fwrite(p, 1, (uint)uncomp_size, fpTargetFile); uint writtenbytes = fwrite(p, 1, (uint)uncomp_size, fpTargetFile);
fclose(fpTargetFile); fclose(fpTargetFile);

View File

@@ -65,7 +65,7 @@ bool ClassFlowAlignment::ReadParameter(FILE* pfile, string& aktparamgraph)
std::vector<string> splitted; std::vector<string> splitted;
int suchex = 40; int suchex = 40;
int suchey = 40; int suchey = 40;
int alg_algo = 0; int alg_algo = 0; //default=0; 1 =HIGHACCURACY; 2= FAST; 3= OFF //add disable aligment algo |01.2023
aktparamgraph = trim(aktparamgraph); aktparamgraph = trim(aktparamgraph);
@@ -130,6 +130,8 @@ bool ClassFlowAlignment::ReadParameter(FILE* pfile, string& aktparamgraph)
alg_algo = 1; alg_algo = 1;
if (toUpper(splitted[1]) == "FAST") if (toUpper(splitted[1]) == "FAST")
alg_algo = 2; alg_algo = 2;
if (toUpper(splitted[1]) == "OFF") //no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
alg_algo = 3;
} }
} }
@@ -145,7 +147,10 @@ bool ClassFlowAlignment::ReadParameter(FILE* pfile, string& aktparamgraph)
#endif #endif
} }
LoadReferenceAlignmentValues(); //no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
if(References[0].alignment_algo != 3){
LoadReferenceAlignmentValues();
}
return true; return true;
@@ -234,14 +239,22 @@ bool ClassFlowAlignment::doFlow(string time)
AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/rot.jpg")); AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/rot.jpg"));
} }
if (!AlignAndCutImage->Align(&References[0], &References[1]))
{ //no align algo if set to 3 = off //add disable aligment algo |01.2023
SaveReferenceAlignmentValues(); if(References[0].alignment_algo != 3){
} if (!AlignAndCutImage->Align(&References[0], &References[1]))
{
SaveReferenceAlignmentValues();
}
}// no align
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG #ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
if (AlgROI) { if (AlgROI) {
DrawRef(ImageTMP); //no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
if(References[0].alignment_algo != 3){
DrawRef(ImageTMP);
}
tfliteflow.DigitalDrawROI(ImageTMP); tfliteflow.DigitalDrawROI(ImageTMP);
tfliteflow.AnalogDrawROI(ImageTMP); tfliteflow.AnalogDrawROI(ImageTMP);
ImageTMP->writeToMemoryAsJPG((ImageData*)AlgROI, 90); ImageTMP->writeToMemoryAsJPG((ImageData*)AlgROI, 90);
@@ -258,7 +271,10 @@ bool ClassFlowAlignment::doFlow(string time)
delete ImageTMP; delete ImageTMP;
ImageTMP = NULL; ImageTMP = NULL;
LoadReferenceAlignmentValues(); //no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
if(References[0].alignment_algo != 3){
LoadReferenceAlignmentValues();
}
return true; return true;
} }

View File

@@ -60,6 +60,9 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
if ((_stepname.compare("[InfluxDB]") == 0) || (_stepname.compare(";[InfluxDB]") == 0)){ if ((_stepname.compare("[InfluxDB]") == 0) || (_stepname.compare(";[InfluxDB]") == 0)){
_classname = "ClassFlowInfluxDB"; _classname = "ClassFlowInfluxDB";
} }
if ((_stepname.compare("[InfluxDBv2]") == 0) || (_stepname.compare(";[InfluxDBv2]") == 0)){
_classname = "ClassFlowInfluxDBv2";
}
#endif //ENABLE_INFLUXDB #endif //ENABLE_INFLUXDB
for (int i = 0; i < FlowControll.size(); ++i) for (int i = 0; i < FlowControll.size(); ++i)
@@ -90,6 +93,8 @@ std::string ClassFlowControll::TranslateAktstatus(std::string _input)
#ifdef ENABLE_INFLUXDB #ifdef ENABLE_INFLUXDB
if (_input.compare("ClassFlowInfluxDB") == 0) if (_input.compare("ClassFlowInfluxDB") == 0)
return ("Sending InfluxDB"); return ("Sending InfluxDB");
if (_input.compare("ClassFlowInfluxDBv2") == 0)
return ("Sending InfluxDBv2");
#endif //ENABLE_INFLUXDB #endif //ENABLE_INFLUXDB
if (_input.compare("ClassFlowPostProcessing") == 0) if (_input.compare("ClassFlowPostProcessing") == 0)
return ("Post-Processing"); return ("Post-Processing");
@@ -233,6 +238,8 @@ ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
#ifdef ENABLE_INFLUXDB #ifdef ENABLE_INFLUXDB
if (toUpper(_type).compare("[INFLUXDB]") == 0) if (toUpper(_type).compare("[INFLUXDB]") == 0)
cfc = new ClassFlowInfluxDB(&FlowControll); cfc = new ClassFlowInfluxDB(&FlowControll);
if (toUpper(_type).compare("[INFLUXDBV2]") == 0)
cfc = new ClassFlowInfluxDBv2(&FlowControll);
#endif //ENABLE_INFLUXDB #endif //ENABLE_INFLUXDB
if (toUpper(_type).compare("[WRITELIST]") == 0) if (toUpper(_type).compare("[WRITELIST]") == 0)
cfc = new ClassFlowWriteList(&FlowControll); cfc = new ClassFlowWriteList(&FlowControll);
@@ -290,6 +297,7 @@ void ClassFlowControll::InitFlow(std::string config)
while ((line.size() > 0) && !(feof(pFile))) while ((line.size() > 0) && !(feof(pFile)))
{ {
cfc = CreateClassFlow(line); cfc = CreateClassFlow(line);
// printf("Name: %s\n", cfc->name().c_str());
if (cfc) if (cfc)
{ {
ESP_LOGD(TAG, "Start ReadParameter (%s)", line.c_str()); ESP_LOGD(TAG, "Start ReadParameter (%s)", line.c_str());

View File

@@ -15,6 +15,7 @@
#endif //ENABLE_MQTT #endif //ENABLE_MQTT
#ifdef ENABLE_INFLUXDB #ifdef ENABLE_INFLUXDB
#include "ClassFlowInfluxDB.h" #include "ClassFlowInfluxDB.h"
#include "ClassFlowInfluxDBv2.h"
#endif //ENABLE_INFLUXDB #endif //ENABLE_INFLUXDB
#include "ClassFlowCNNGeneral.h" #include "ClassFlowCNNGeneral.h"
#include "ClassFlowWriteList.h" #include "ClassFlowWriteList.h"

View File

@@ -50,6 +50,7 @@ struct NumberPost {
int DecimalShiftInitial; int DecimalShiftInitial;
float AnalogDigitalTransitionStart; // When is the digit > x.1, i.e. when does it start to tilt? float AnalogDigitalTransitionStart; // When is the digit > x.1, i.e. when does it start to tilt?
int Nachkomma; int Nachkomma;
string Fieldname; // Fieldname in InfluxDB2
bool isExtendedResolution; bool isExtendedResolution;

View File

@@ -0,0 +1,219 @@
#ifdef ENABLE_INFLUXDB
#include <sstream>
#include "ClassFlowInfluxDBv2.h"
#include "Helper.h"
#include "connect_wlan.h"
#include "time_sntp.h"
#include "interface_influxdb.h"
#include "ClassFlowPostProcessing.h"
#include "esp_log.h"
#include "../../include/defines.h"
#include "ClassLogFile.h"
#include <time.h>
static const char* TAG = "class_flow_influxDbv2";
void ClassFlowInfluxDBv2::SetInitialParameter(void)
{
uri = "";
database = "";
measurement = "";
dborg = "";
dbtoken = "";
// dbfield = "";
OldValue = "";
flowpostprocessing = NULL;
previousElement = NULL;
ListFlowControll = NULL;
disabled = false;
InfluxDBenable = false;
}
ClassFlowInfluxDBv2::ClassFlowInfluxDBv2()
{
SetInitialParameter();
}
ClassFlowInfluxDBv2::ClassFlowInfluxDBv2(std::vector<ClassFlow*>* lfc)
{
SetInitialParameter();
ListFlowControll = lfc;
for (int i = 0; i < ListFlowControll->size(); ++i)
{
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
{
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
}
}
}
ClassFlowInfluxDBv2::ClassFlowInfluxDBv2(std::vector<ClassFlow*>* lfc, ClassFlow *_prev)
{
SetInitialParameter();
previousElement = _prev;
ListFlowControll = lfc;
for (int i = 0; i < ListFlowControll->size(); ++i)
{
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
{
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
}
}
}
bool ClassFlowInfluxDBv2::ReadParameter(FILE* pfile, string& aktparamgraph)
{
std::vector<string> splitted;
aktparamgraph = trim(aktparamgraph);
printf("akt param: %s\n", aktparamgraph.c_str());
if (aktparamgraph.size() == 0)
if (!this->GetNextParagraph(pfile, aktparamgraph))
return false;
if (toUpper(aktparamgraph).compare("[INFLUXDBV2]") != 0)
return false;
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
{
// ESP_LOGD(TAG, "while loop reading line: %s", aktparamgraph.c_str());
splitted = ZerlegeZeile(aktparamgraph);
std::string _param = GetParameterName(splitted[0]);
if ((toUpper(_param) == "ORG") && (splitted.size() > 1))
{
this->dborg = splitted[1];
}
if ((toUpper(_param) == "TOKEN") && (splitted.size() > 1))
{
this->dbtoken = splitted[1];
}
if ((toUpper(_param) == "URI") && (splitted.size() > 1))
{
this->uri = splitted[1];
}
if (((toUpper(_param) == "MEASUREMENT")) && (splitted.size() > 1))
{
this->measurement = splitted[1];
}
if (((toUpper(_param) == "FIELDNAME")) && (splitted.size() > 1))
{
handleFieldname(splitted[0], splitted[1]);
}
if (((toUpper(splitted[0]) == "DATABASE")) && (splitted.size() > 1))
{
this->database = splitted[1];
}
}
printf("uri: %s\n", uri.c_str());
printf("measurement: %s\n", measurement.c_str());
printf("org: %s\n", dborg.c_str());
printf("token: %s\n", dbtoken.c_str());
if ((uri.length() > 0) && (database.length() > 0) && (measurement.length() > 0) && (dbtoken.length() > 0) && (dborg.length() > 0))
{
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDB with uri: " + uri + ", measurement: " + measurement + ", org: " + dborg + ", token: *****");
// printf("vor V2 Init\n");
InfluxDB_V2_Init(uri, database, measurement, dborg, dbtoken);
// printf("nach V2 Init\n");
InfluxDBenable = true;
} else {
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBv2 (Verion2 !!!) init skipped as we are missing some parameters");
}
return true;
}
string ClassFlowInfluxDBv2::GetInfluxDBMeasurement()
{
return measurement;
}
void ClassFlowInfluxDBv2::handleFieldname(string _decsep, string _value)
{
string _digit, _decpos;
int _pospunkt = _decsep.find_first_of(".");
// ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt);
if (_pospunkt > -1)
_digit = _decsep.substr(0, _pospunkt);
else
_digit = "default";
for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j)
{
if (_digit == "default") // Set to default first (if nothing else is set)
{
flowpostprocessing->NUMBERS[j]->Fieldname = _value;
}
if (flowpostprocessing->NUMBERS[j]->name == _digit)
{
flowpostprocessing->NUMBERS[j]->Fieldname = _value;
}
}
}
bool ClassFlowInfluxDBv2::doFlow(string zwtime)
{
if (!InfluxDBenable)
return true;
std::string result;
std::string resulterror = "";
std::string resultraw = "";
std::string resultrate = "";
std::string resulttimestamp = "";
string zw = "";
string namenumber = "";
if (flowpostprocessing)
{
std::vector<NumberPost*>* NUMBERS = flowpostprocessing->GetNumbers();
for (int i = 0; i < (*NUMBERS).size(); ++i)
{
result = (*NUMBERS)[i]->ReturnValue;
resultraw = (*NUMBERS)[i]->ReturnRawValue;
resulterror = (*NUMBERS)[i]->ErrorMessageText;
resultrate = (*NUMBERS)[i]->ReturnRateValue;
resulttimestamp = (*NUMBERS)[i]->timeStamp;
if ((*NUMBERS)[i]->Fieldname.length() > 0)
{
namenumber = (*NUMBERS)[i]->Fieldname;
}
else
{
namenumber = (*NUMBERS)[i]->name;
if (namenumber == "default")
namenumber = "value";
else
namenumber = namenumber + "/value";
}
printf("vor sende Influx_DB_V2 - namenumber. %s, result: %s, timestampt: %s", namenumber.c_str(), result.c_str(), resulttimestamp.c_str());
if (result.length() > 0)
InfluxDB_V2_Publish(namenumber, result, resulttimestamp);
// InfluxDB_V2_Publish(namenumber, result, resulttimestamp);
}
}
OldValue = result;
return true;
}
#endif //ENABLE_INFLUXDB

View File

@@ -0,0 +1,41 @@
#ifdef ENABLE_INFLUXDB
#pragma once
#ifndef CLASSFINFLUXDBv2_H
#define CLASSFINFLUXDBv2_H
#include "ClassFlow.h"
#include "ClassFlowPostProcessing.h"
#include <string>
class ClassFlowInfluxDBv2 :
public ClassFlow
{
protected:
std::string uri, database, measurement;
std::string dborg, dbtoken, dbfield;
std::string OldValue;
ClassFlowPostProcessing* flowpostprocessing;
bool InfluxDBenable;
void SetInitialParameter(void);
void handleFieldname(string _decsep, string _value);
public:
ClassFlowInfluxDBv2();
ClassFlowInfluxDBv2(std::vector<ClassFlow*>* lfc);
ClassFlowInfluxDBv2(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
string GetInfluxDBMeasurement();
bool ReadParameter(FILE* pfile, string& aktparamgraph);
bool doFlow(string time);
string name(){return "ClassFlowInfluxDBv2";};
};
#endif //CLASSFINFLUXDBv2_H
#endif //ENABLE_INFLUXDB

View File

@@ -15,7 +15,6 @@ class ClassFlowPostProcessing :
public ClassFlow public ClassFlow
{ {
protected: protected:
std::vector<NumberPost*> NUMBERS;
bool UpdatePreValueINI; bool UpdatePreValueINI;
int PreValueAgeStartup; int PreValueAgeStartup;
@@ -54,6 +53,8 @@ protected:
public: public:
bool PreValueUse; bool PreValueUse;
std::vector<NumberPost*> NUMBERS;
ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit); ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit);
virtual ~ClassFlowPostProcessing(){}; virtual ~ClassFlowPostProcessing(){};

View File

@@ -224,15 +224,50 @@ void FindReplace(std::string& line, std::string& oldString, std::string& newStri
} }
bool MakeDir(std::string _what) /**
* Create a folder and its parent folders as needed
*/
bool MakeDir(std::string path)
{ {
int mk_ret = mkdir(_what.c_str(), 0775); std::string parent;
if (mk_ret)
{ LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Creating folder " + path + "...");
ESP_LOGD(TAG, "error with mkdir %s ret %d", _what.c_str(), mk_ret);
return false; bool bSuccess = false;
int nRC = ::mkdir( path.c_str(), 0775 );
if( nRC == -1 )
{
switch( errno ) {
case ENOENT:
//parent didn't exist, try to create it
parent = path.substr(0, path.find_last_of('/'));
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Need to create parent folder first: " + parent);
if(MakeDir(parent)) {
//Now, try to create again.
bSuccess = 0 == ::mkdir( path.c_str(), 0775 );
}
else {
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create parent folder: " + parent);
bSuccess = false;
}
break;
case EEXIST:
//Done!
bSuccess = true;
break;
default:
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create folder: " + path);
bSuccess = false;
break;
}
}
else {
bSuccess = true;
} }
return true;
return bSuccess;
} }
@@ -576,9 +611,6 @@ std::vector<string> HelperZerlegeZeile(std::string input, std::string _delimiter
std::vector<string> ZerlegeZeile(std::string input, std::string delimiter) std::vector<string> ZerlegeZeile(std::string input, std::string delimiter)
{ {
std::vector<string> Output; std::vector<string> Output;
input = trim(input, delimiter);
/* The input can have multiple formats: /* The input can have multiple formats:
* - key = value * - key = value
* - key = value1 value2 value3 ... * - key = value1 value2 value3 ...
@@ -593,12 +625,13 @@ std::vector<string> ZerlegeZeile(std::string input, std::string delimiter)
* As a workaround and to not break any legacy usage, we enforce to only use the * As a workaround and to not break any legacy usage, we enforce to only use the
* equal sign, if the key is "password" * equal sign, if the key is "password"
*/ */
if (input.find("password") != string::npos) { // Line contains a password, use the equal sign as the only delimiter and only split on first occurrence if ((input.find("password") != string::npos) || (input.find("Token") != string::npos)) { // Line contains a password, use the equal sign as the only delimiter and only split on first occurrence
size_t pos = input.find("="); size_t pos = input.find("=");
Output.push_back(trim(input.substr(0, pos), "")); Output.push_back(trim(input.substr(0, pos), ""));
Output.push_back(trim(input.substr(pos +1, string::npos), "")); Output.push_back(trim(input.substr(pos +1, string::npos), ""));
} }
else { // Legacy Mode else { // Legacy Mode
input = trim(input, delimiter); // sonst werden delimiter am Ende (z.B. == im Token) gelöscht)
size_t pos = findDelimiterPos(input, delimiter); size_t pos = findDelimiterPos(input, delimiter);
std::string token; std::string token;
while (pos != std::string::npos) { while (pos != std::string::npos) {

View File

@@ -0,0 +1,214 @@
#ifdef ENABLE_INFLUXDB
#include "interface_influxdb.h"
#include "esp_log.h"
#include <time.h>
#include "ClassLogFile.h"
#include "esp_http_client.h"
#include "../../include/defines.h"
static const char *TAG = "INFLUXDB";
std::string _influxDBURI;
std::string _influxDBDatabase;
std::string _influxDBMeasurement;
std::string _influxDBUser;
std::string _influxDBPassword;
static esp_err_t http_event_handler(esp_http_client_event_t *evt)
{
switch(evt->event_id)
{
case HTTP_EVENT_ERROR:
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Error encountered");
break;
case HTTP_EVENT_ON_CONNECTED:
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Error encountered");
ESP_LOGI(TAG, "HTTP Client Connected");
break;
case HTTP_EVENT_HEADERS_SENT:
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client sent all request headers");
break;
case HTTP_EVENT_ON_HEADER:
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Header: key=" + std::string(evt->header_key) + ", value=" + std::string(evt->header_value));
break;
case HTTP_EVENT_ON_DATA:
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client data recevied: len=" + std::to_string(evt->data_len));
break;
case HTTP_EVENT_ON_FINISH:
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client finished");
break;
case HTTP_EVENT_DISCONNECTED:
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Disconnected");
break;
}
return ESP_OK;
}
void InfluxDBPublish(std::string _key, std::string _content, std::string _timestamp) {
char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
esp_http_client_config_t http_config = {
.user_agent = "ESP32 Meter reader",
.method = HTTP_METHOD_POST,
.event_handler = http_event_handler,
.buffer_size = MAX_HTTP_OUTPUT_BUFFER,
.user_data = response_buffer
};
if (_influxDBUser.length() && _influxDBPassword.length()){
http_config.username = _influxDBUser.c_str();
http_config.password = _influxDBPassword.c_str();
http_config.auth_type = HTTP_AUTH_TYPE_BASIC;
}
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", Timestamp: " + _timestamp);
char nowTimestamp[21];
std::string payload;
if (_timestamp.length() > 0)
{
struct tm tm;
strptime(_timestamp.c_str(), PREVALUE_TIME_FORMAT_OUTPUT, &tm);
time_t t = mktime(&tm); // Time in Localtime (looks like timezone is not used by strptime)
struct tm * ptm;
ptm = gmtime ( &t );
time_t utc = mktime(ptm);
utc = 2*t - utc; // Take care of timezone (looks difficult, but is easy: t = t + (t - utc), weil t-utc = timezone)
sprintf(nowTimestamp,"%ld000000000", (long) utc); // UTC
payload = _influxDBMeasurement + " " + _key + "=" + _content + " " + nowTimestamp;
// payload = _influxDBMeasurement + " " + _key + "=774 " + nowTimestamp;
}
else
{
payload = _influxDBMeasurement + " " + _key + "=" + _content;
}
payload.shrink_to_fit();
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload);
// use the default retention policy of the database
std::string apiURI = _influxDBURI + "/api/v2/write?bucket=" + _influxDBDatabase + "/";
apiURI.shrink_to_fit();
http_config.url = apiURI.c_str();
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "API URI: " + apiURI);
esp_http_client_handle_t http_client = esp_http_client_init(&http_config);
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "client is initialized");
esp_http_client_set_header(http_client, "Content-Type", "text/plain");
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "header is set");
ESP_ERROR_CHECK(esp_http_client_set_post_field(http_client, payload.c_str(), payload.length()));
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "post payload is set");
esp_err_t err = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_http_client_perform(http_client));
if( err == ESP_OK ) {
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request was performed");
int status_code = esp_http_client_get_status_code(http_client);
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP status code" + std::to_string(status_code));
} else {
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request failed");
}
esp_http_client_cleanup(http_client);
}
/*
void InfluxDBPublish(std::string _key, std::string _content, std::string _timestamp) {
char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
esp_http_client_config_t http_config = {
.user_agent = "ESP32 Meter reader",
.method = HTTP_METHOD_POST,
.event_handler = http_event_handler,
.buffer_size = MAX_HTTP_OUTPUT_BUFFER,
.user_data = response_buffer
};
if (_influxDBUser.length() && _influxDBPassword.length()){
http_config.username = _influxDBUser.c_str();
http_config.password = _influxDBPassword.c_str();
http_config.auth_type = HTTP_AUTH_TYPE_BASIC;
}
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", Timestamp: " + _timestamp);
// Format: #define PREVALUE_TIME_FORMAT_OUTPUT "%Y-%m-%dT%H:%M:%S%z"
struct tm tm;
strptime(_timestamp.c_str(), PREVALUE_TIME_FORMAT_OUTPUT, &tm);
time_t t = mktime(&tm); // t is now your desired time_t
struct tm * ptm;
ptm = gmtime ( &t );
time_t utc = mktime(ptm);
// time_t now;
// time(&now);
char nowTimestamp[21];
// pad with zeroes to get nanoseconds
// sprintf(nowTimestamp,"%ld000000000", (long) now);
// sprintf(nowTimestamp,"%ld000000000", (long) t); // Localtime
sprintf(nowTimestamp,"%ld000000000", (long) utc); // UTC
// LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Test Time Conversion - t: " + std::to_string(t) + ", utc: " + std::to_string(utc));
// LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Test Time Conversion - now: " + std::to_string(now) + ", timestamp: " + std::to_string(t) + "(correct time not used yet)");
std::string payload = _influxDBMeasurement + " " + _key + "=" + _content + " " + nowTimestamp;
payload.shrink_to_fit();
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload);
// use the default retention policy of the database
std::string apiURI = _influxDBURI + "/api/v2/write?bucket=" + _influxDBDatabase + "/";
apiURI.shrink_to_fit();
http_config.url = apiURI.c_str();
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "API URI: " + apiURI);
esp_http_client_handle_t http_client = esp_http_client_init(&http_config);
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "client is initialized");
esp_http_client_set_header(http_client, "Content-Type", "text/plain");
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "header is set");
ESP_ERROR_CHECK(esp_http_client_set_post_field(http_client, payload.c_str(), payload.length()));
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "post payload is set");
esp_err_t err = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_http_client_perform(http_client));
if( err == ESP_OK ) {
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request was performed");
int status_code = esp_http_client_get_status_code(http_client);
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP status code" + std::to_string(status_code));
} else {
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request failed");
}
esp_http_client_cleanup(http_client);
}
*/
void InfluxDBInit(std::string _uri, std::string _database, std::string _measurement, std::string _user, std::string _password){
_influxDBURI = _uri;
_influxDBDatabase = _database;
_influxDBMeasurement = _measurement;
_influxDBUser = _user;
_influxDBPassword = _password;
}
void InfluxDBdestroy() {
}
#endif //ENABLE_INFLUXDB

View File

@@ -0,0 +1,24 @@
#ifdef ENABLE_INFLUXDB
#pragma once
#ifndef INTERFACE_INFLUXDB_H
#define INTERFACE_INFLUXDB_H
#include <string>
#include <map>
#include <functional>
// Interface to InfluxDB v1.x
void InfluxDBInit(std::string _influxDBURI, std::string _database, std::string _measurement, std::string _user, std::string _password);
void InfluxDBPublish(std::string _key, std::string _content, std::string _timestamp);
// Interface to InfluxDB v2.x
void InfluxDB_V2_Init(std::string _uri, std::string _database, std::string _measurement, std::string _org, std::string _token);
void InfluxDB_V2_Publish(std::string _key, std::string _content, std::string _timestamp);
void InfluxDBdestroy();
#endif //INTERFACE_INFLUXDB_H
#endif //ENABLE_INFLUXDB

View File

@@ -16,6 +16,101 @@ std::string _influxDBMeasurement;
std::string _influxDBUser; std::string _influxDBUser;
std::string _influxDBPassword; std::string _influxDBPassword;
std::string _influxDB_V2_URI;
std::string _influxDB_V2_Database;
std::string _influxDB_V2_Measurement;
std::string _influxDB_V2_Token;
std::string _influxDB_V2_Org;
static esp_err_t http_event_handler(esp_http_client_event_t *evt);
void InfluxDB_V2_Init(std::string _uri, std::string _database, std::string _measurement, std::string _org, std::string _token)
{
_influxDB_V2_URI = _uri;
_influxDB_V2_Database = _database;
_influxDB_V2_Measurement = _measurement;
_influxDB_V2_Org = _org;
_influxDB_V2_Token = _token;
}
void InfluxDB_V2_Publish(std::string _key, std::string _content, std::string _timestamp)
{
char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
esp_http_client_config_t http_config = {
.user_agent = "ESP32 Meter reader",
.method = HTTP_METHOD_POST,
.event_handler = http_event_handler,
.buffer_size = MAX_HTTP_OUTPUT_BUFFER,
.user_data = response_buffer
};
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDB_V2_Publish - Key: " + _key + ", Content: " + _content + ", Timestamp: " + _timestamp);
// Format: #define PREVALUE_TIME_FORMAT_OUTPUT "%Y-%m-%dT%H:%M:%S%z"
char nowTimestamp[21];
std::string payload;
if (_timestamp.length() > 0)
{
struct tm tm;
strptime(_timestamp.c_str(), PREVALUE_TIME_FORMAT_OUTPUT, &tm);
time_t t = mktime(&tm); // Time in Localtime (looks like timezone is not used by strptime)
struct tm * ptm;
ptm = gmtime ( &t );
time_t utc = mktime(ptm);
utc = 2*t - utc; // Take care of timezone (looks difficult, but is easy: t = t + (t - utc), weil t-utc = timezone)
sprintf(nowTimestamp,"%ld000000000", (long) utc); // UTC
payload = _influxDB_V2_Measurement + " " + _key + "=" + _content + " " + nowTimestamp;
// payload = _influxDB_V2_Measurement + " " + _key + "=774 " + nowTimestamp;
}
else
{
payload = _influxDB_V2_Measurement + " " + _key + "=" + _content;
// payload = _influxDB_V2_Measurement + " " + _key + "=774";
}
payload.shrink_to_fit();
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload);
std::string apiURI = _influxDB_V2_URI + "/api/v2/write?org=" + _influxDB_V2_Org + "&bucket=" + _influxDB_V2_Database;
apiURI.shrink_to_fit();
http_config.url = apiURI.c_str();
ESP_LOGI(TAG, "http_config: %s", http_config.url); // Add mark on log to see when it restarted
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "API URI: " + apiURI);
esp_http_client_handle_t http_client = esp_http_client_init(&http_config);
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "client is initialized");
esp_http_client_set_header(http_client, "Content-Type", "text/plain");
std::string _zw = "Token " + _influxDB_V2_Token;
// LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Tokenheader: %s\n", _zw.c_str());
esp_http_client_set_header(http_client, "Authorization", _zw.c_str());
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "header is set");
ESP_ERROR_CHECK(esp_http_client_set_post_field(http_client, payload.c_str(), payload.length()));
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "post payload is set");
esp_err_t err = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_http_client_perform(http_client));
if( err == ESP_OK ) {
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request was performed");
int status_code = esp_http_client_get_status_code(http_client);
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP status code" + std::to_string(status_code));
} else {
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request failed");
}
esp_http_client_cleanup(http_client);
}
static esp_err_t http_event_handler(esp_http_client_event_t *evt) static esp_err_t http_event_handler(esp_http_client_event_t *evt)
{ {
switch(evt->event_id) switch(evt->event_id)
@@ -64,28 +159,30 @@ void InfluxDBPublish(std::string _key, std::string _content, std::string _timest
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", Timestamp: " + _timestamp); LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", Timestamp: " + _timestamp);
// Format: #define PREVALUE_TIME_FORMAT_OUTPUT "%Y-%m-%dT%H:%M:%S%z"
struct tm tm;
strptime(_timestamp.c_str(), PREVALUE_TIME_FORMAT_OUTPUT, &tm);
time_t t = mktime(&tm); // t is now your desired time_t
struct tm * ptm;
ptm = gmtime ( &t );
time_t utc = mktime(ptm);
// time_t now;
// time(&now);
char nowTimestamp[21]; char nowTimestamp[21];
// pad with zeroes to get nanoseconds std::string payload;
// sprintf(nowTimestamp,"%ld000000000", (long) now);
// sprintf(nowTimestamp,"%ld000000000", (long) t); // Localtime
sprintf(nowTimestamp,"%ld000000000", (long) utc); // UTC
if (_timestamp.length() > 0)
{
struct tm tm;
strptime(_timestamp.c_str(), PREVALUE_TIME_FORMAT_OUTPUT, &tm);
time_t t = mktime(&tm); // Time in Localtime (looks like timezone is not used by strptime)
// LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Test Time Conversion - t: " + std::to_string(t) + ", utc: " + std::to_string(utc)); struct tm * ptm;
// LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Test Time Conversion - now: " + std::to_string(now) + ", timestamp: " + std::to_string(t) + "(correct time not used yet)"); ptm = gmtime ( &t );
time_t utc = mktime(ptm);
utc = 2*t - utc; // Take care of timezone (looks difficult, but is easy: t = t + (t - utc), weil t-utc = timezone)
sprintf(nowTimestamp,"%ld000000000", (long) utc); // UTC
payload = _influxDBMeasurement + " " + _key + "=" + _content + " " + nowTimestamp;
// payload = _influxDBMeasurement + " " + _key + "=774 " + nowTimestamp;
}
else
{
payload = _influxDBMeasurement + " " + _key + "=" + _content;
}
std::string payload = _influxDBMeasurement + " " + _key + "=" + _content + " " + nowTimestamp;
payload.shrink_to_fit(); payload.shrink_to_fit();
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload);

View File

@@ -8,10 +8,17 @@
#include <map> #include <map>
#include <functional> #include <functional>
// Interface to InfluxDB v1.x
void InfluxDBInit(std::string _influxDBURI, std::string _database, std::string _measurement, std::string _user, std::string _password); void InfluxDBInit(std::string _influxDBURI, std::string _database, std::string _measurement, std::string _user, std::string _password);
void InfluxDBdestroy();
void InfluxDBPublish(std::string _key, std::string _content, std::string _timestamp); void InfluxDBPublish(std::string _key, std::string _content, std::string _timestamp);
// Interface to InfluxDB v2.x
void InfluxDB_V2_Init(std::string _uri, std::string _database, std::string _measurement, std::string _org, std::string _token);
void InfluxDB_V2_Publish(std::string _key, std::string _content, std::string _timestamp);
void InfluxDBdestroy();
#endif //INTERFACE_INFLUXDB_H #endif //INTERFACE_INFLUXDB_H
#endif //ENABLE_INFLUXDB #endif //ENABLE_INFLUXDB

View File

@@ -556,7 +556,9 @@ bool getWIFIisConnected()
void WIFIDestroy() void WIFIDestroy()
{ {
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, event_handler); esp_wifi_disconnect();
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, event_handler);
esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, event_handler); esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, event_handler);
#ifdef WLAN_USE_MESH_ROAMING #ifdef WLAN_USE_MESH_ROAMING
esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW, esp_bss_rssi_low_handler); esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW, esp_bss_rssi_low_handler);

View File

@@ -21,7 +21,9 @@
// use himem //https://github.com/jomjol/AI-on-the-edge-device/issues/1842 // use himem //https://github.com/jomjol/AI-on-the-edge-device/issues/1842
#define USE_HIMEM_IF_AVAILABLE #if (CONFIG_SPIRAM_BANKSWITCH_ENABLE)
#define USE_HIMEM_IF_AVAILABLE 1
#endif
/* Uncomment this to generate task list with stack sizes using the /heap handler /* Uncomment this to generate task list with stack sizes using the /heap handler
PLEASE BE AWARE: The following CONFIG parameters have to to be set in PLEASE BE AWARE: The following CONFIG parameters have to to be set in
@@ -117,7 +119,6 @@
//ClassFlowControll: Serve alg_roi.jpg from memory as JPG //ClassFlowControll: Serve alg_roi.jpg from memory as JPG
#define ALGROI_LOAD_FROM_MEM_AS_JPG // Load ALG_ROI.JPG as rendered JPG from RAM #define ALGROI_LOAD_FROM_MEM_AS_JPG // Load ALG_ROI.JPG as rendered JPG from RAM
#define ALGROI_LOAD_FROM_MEM_AS_JPG__SHOW_TAKE_IMAGE_PROCESS // Show take image image processing on webinterface (overview.html)
//ClassFlowMQTT //ClassFlowMQTT
#define LWT_TOPIC "connection" #define LWT_TOPIC "connection"

View File

@@ -3,4 +3,4 @@ The firmware got moved to the [Release page](https://github.com/jomjol/AI-on-the
# Installation Guide # Installation Guide
You find the complete installation guide at <https://github.com/jomjol/AI-on-the-edge-device/wiki/Installation> You find the complete installation guide at https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/

View File

@@ -65,11 +65,19 @@ HomeassistantDiscovery = false
;[InfluxDB] ;[InfluxDB]
;Uri = undefined ;Uri = undefined
;Database = ;Database = undefined
;Measurement = undefined ;Measurement = undefined
;user = undefined ;user = undefined
;password = undefined ;password = undefined
;[InfluxDBv2]
;Uri = undefined
;Database = undefined
;Measurement = undefined
;Org = undefined
;Token = undefined
;main.Fieldname = undefined
;[GPIO] ;[GPIO]
;MainTopicMQTT = wasserzaehler/GPIO ;MainTopicMQTT = wasserzaehler/GPIO
;IO0 = input disabled 10 false false ;IO0 = input disabled 10 false false

Binary file not shown.

Binary file not shown.

View File

@@ -121,9 +121,15 @@ function fetchFiles(urls, filesData, index, retry, zipFilename) {
else if (retry == 1) { // longer timeout else if (retry == 1) { // longer timeout
xhr.timeout = 5000; // time in milliseconds xhr.timeout = 5000; // time in milliseconds
} }
else { // very long timeout else if (retry == 2) { // longer timeout
xhr.timeout = 20000; // time in milliseconds xhr.timeout = 20000; // time in milliseconds
} }
else if (retry == 3) { // longer timeout
xhr.timeout = 30000; // time in milliseconds
}
else { // very long timeout
xhr.timeout = 60000; // time in milliseconds
}
xhr.onload = () => { // Request finished xhr.onload = () => { // Request finished
//console.log(url + " done"); //console.log(url + " done");

View File

@@ -1,7 +1,7 @@
/* The UI can also be run locally, but you have to set the IP of your devide accordingly. /* The UI can also be run locally, but you have to set the IP of your devide accordingly.
* And you also might have to disable CORS in your webbrowser! */ * And you also might have to disable CORS in your webbrowser! */
var domainname_for_testing = "192.168.178.44"; var domainname_for_testing = "192.168.178.62";

View File

@@ -2,7 +2,7 @@
<html> <html>
<head> <head>
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon"> <link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
<title>Make Alignment</title> <title>Alignment Marks</title>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<style> <style>
@@ -48,7 +48,11 @@ select {
<body style="font-family: arial; padding: 0px 10px;"> <body style="font-family: arial; padding: 0px 10px;">
<h2>Define Alignment Structure in Reference Image</h2> <h2>Alignment Marks</h2>
<p>On this page you define two Reference Marks.
See <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Alignment/ target=_blank>https://jomjol.github.io/AI-on-the-edge-device-docs/Alignment/</a> for explanations.</p>
<p>After saving the Reference Marks, you can define the <a href=edit_digits.html>digit</a> resp. <a href=edit_analog.html>analog</a> ROI's.<br>
Only after those steps a reboot is required.</p>
<table> <table>
<tr> <tr>
@@ -88,10 +92,8 @@ select {
<table> <table>
<tr> <tr>
<td><input class="button" type="submit" name="saveroi" onclick="SaveToConfig()" value="Save"></td> <td><input class="button" type="submit" name="saveroi" onclick="SaveToConfig()" value="Save">
</tr> <p>Proceed to update the <a href=edit_digits.html>digit</a> resp. <a href=edit_analog.html>analog</a> ROI's when you are done.</p></td>
<tr>
<td><button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate changes</button></td>
</tr> </tr>
</table> </table>
@@ -131,7 +133,7 @@ function SaveToConfig(){
WriteConfigININew(); WriteConfigININew();
UpdateConfigReference(domainname) UpdateConfigReference(domainname)
SaveConfigToServer(domainname); SaveConfigToServer(domainname);
firework.launch('Configuration got updated. Please reboot to activate changes!', 'success', 5000); firework.launch('Configuration got updated. It will get applied after the next reboot!', 'success', 5000);
} }
function EnhanceContrast(){ function EnhanceContrast(){

View File

@@ -3,7 +3,7 @@
<head> <head>
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon"> <link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
<meta charset="utf-8"/> <meta charset="utf-8"/>
<title>Make Analog Alignment</title> <title>Analog ROI's</title>
<style> <style>
h1 {font-size: 2em;} h1 {font-size: 2em;}
@@ -67,9 +67,11 @@ th, td {
<body style="font-family: arial; padding: 0px 10px;"> <body style="font-family: arial; padding: 0px 10px;">
<h2><input type="checkbox" id="Category_Analog_enabled" value="1" onclick = 'EnDisableAnalog()' checked > <h2>Analog ROI's</h2>
Edit Analog</h2> <p>On this page you define ROI's for the analog counters.
See <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/ target=_blank>https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/</a> for explanations.</p>
<input type="checkbox" id="Category_Analog_enabled" value="1" onclick = 'EnDisableAnalog()' checked ><label for="Category_Analog_enabled">Enable Analog ROI's</label></p>
<div id="div1"> <div id="div1">
<table> <table>
@@ -134,10 +136,10 @@ th, td {
<table> <table>
<tr> <tr>
<td><input class="button" type="submit" id="saveroi" name="saveroi" onclick="SaveToConfig()" value="Save" tabindex=9></td> <td><input class="button" type="submit" id="saveroi" name="saveroi" onclick="SaveToConfig()" value="Save" tabindex=9>
</tr> </tr>
<tr> <tr>
<td><button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate changes</button></td> <td><button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate the changes</button></td>
</tr> </tr>
</table> </table>
@@ -290,7 +292,7 @@ function SaveToConfig(){
cofcat["Analog"]["enabled"] = document.getElementById("Category_Analog_enabled").checked; cofcat["Analog"]["enabled"] = document.getElementById("Category_Analog_enabled").checked;
WriteConfigININew(); WriteConfigININew();
SaveConfigToServer(domainname); SaveConfigToServer(domainname);
firework.launch('Configuration got updated. Please reboot to activate changes!', 'success', 5000); firework.launch('Configuration got updated. It will get applied after the next reboot!', 'success', 5000);
} }

View File

@@ -63,7 +63,7 @@ function saveTextAsFile()
FileDeleteOnServer("/config/config.ini", domainname); FileDeleteOnServer("/config/config.ini", domainname);
var textToSave = document.getElementById("inputTextToSave").value; var textToSave = document.getElementById("inputTextToSave").value;
FileSendContent(textToSave, "/config/config.ini", domainname); FileSendContent(textToSave, "/config/config.ini", domainname);
firework.launch('Configuration got updated. Please reboot to activate changes!', 'success', 5000); firework.launch('Configuration got updated. It will get applied after the next reboot!', 'success', 5000);
} }
} }

View File

@@ -160,8 +160,9 @@ textarea {
<input type="number" id="TakeImage_ImageQuality_value1" size="13" min="0" max="63"> <input type="number" id="TakeImage_ImageQuality_value1" size="13" min="0" max="63">
</td> </td>
<td class="description"> <td class="description">
Quality index for picture (default = "12" - "0" high ... "63" low) <br> Image quality index (default = 12) <br>
Remark: values smaller than 12 can result in a reboot, as the bigger sized JPEG might not fit in the available RAM! Input range: 8 [highest quality] ... 63 [lowest quality] <br>
Remark: Value < 12 could result in system instabilities!
</td> </td>
</tr> </tr>
<tr class="expert" id="ex3"> <tr class="expert" id="ex3">
@@ -200,7 +201,8 @@ textarea {
<input type="number" id="TakeImage_Brightness_value1" size="13" min="-2" max="2"> <input type="number" id="TakeImage_Brightness_value1" size="13" min="-2" max="2">
</td> </td>
<td style="font-size: 80%;"> <td style="font-size: 80%;">
Image Brightness (-2 .. 2 - default = "0") Image brightness (default = 0) <br>
Input range: -2 ... 2
</td> </td>
</tr> </tr>
@@ -212,8 +214,8 @@ textarea {
<input type="number" id="TakeImage_Contrast_value1" size="13" min="-2" max="2"> <input type="number" id="TakeImage_Contrast_value1" size="13" min="-2" max="2">
</td> </td>
<td style="font-size: 80%;"> <td style="font-size: 80%;">
Image Contrast (-2 .. 2 - default = "0") <br> Image contrast (default = 0) <br>
Remark: camera driver is not fully supporting this setting yet (no impact on image) Input range: -2 ... 2
</td> </td>
</tr> </tr>
@@ -225,8 +227,8 @@ textarea {
<input type="number" id="TakeImage_Saturation_value1" size="13" min="-2" max="2"> <input type="number" id="TakeImage_Saturation_value1" size="13" min="-2" max="2">
</td> </td>
<td style="font-size: 80%;"> <td style="font-size: 80%;">
Image Saturation (-2 .. 2 - default = "0") <br> Image saturation (default = 0) <br>
Remark: camera driver is not fully supporting this setting yet (no impact on image) Input range: -2 ... 2
</td> </td>
</tr> </tr>
@@ -281,6 +283,7 @@ textarea {
<option value="default" selected>Default</option> <option value="default" selected>Default</option>
<option value="highAccuracy" >HighAccuracy</option> <option value="highAccuracy" >HighAccuracy</option>
<option value="fast" >Fast</option> <option value="fast" >Fast</option>
<option value="off" >Off</option><!-- add disable aligment algo |01.2023 -->
</select> </select>
</td> </td>
<td style="font-size: 80%;"> <td style="font-size: 80%;">
@@ -788,6 +791,97 @@ textarea {
<tr>
<td colspan="3" style="padding-left: 20px;">
<h4>
<input type="checkbox" id="Category_InfluxDBv2_enabled" value="1" onclick = 'UpdateAfterCategoryCheck()' unchecked >
<label for=Category_InfluxDBv2_enabled>InfluxDBv2</h4><h5>Only InfluxDB v2.x is configured here, v1.x see above [InfluxDB]</h5></label>
</td>
</tr>
<tr>
<td class="indent1">
<input type="checkbox" id="InfluxDBv2_Uri_enabled" value="1" onclick = 'InvertEnableItem("InfluxDBv2", "Uri")' unchecked >
<label for=InfluxDBv2_Uri_enabled><class id="InfluxDBv2_Uri_text" style="color:black;">Uri</class></label>
</td>
<td>
<input type="text" id="InfluxDBv2_Uri_value1">
</td>
<td style="font-size: 80%;">
URI of the HTTP interface to InfluxDB (Version2), without traililing slash, e.g. http://IP-Address:Port
</td>
</tr>
<tr>
<td class="indent1">
<input type="checkbox" id="InfluxDBv2_Database_enabled" value="1" onclick = 'InvertEnableItem("InfluxDBv2", "Database")' unchecked >
<label for=InfluxDBv2_Database_enabled><class id="InfluxDBv2_Database_text" style="color:black;">Database</class></label>
</td>
<td>
<input type="text" id="InfluxDBv2_Database_value1">
</td>
<td style="font-size: 80%;">
Database name in which to publish the read value.
</td>
</tr>
<tr>
<td class="indent1">
<input type="checkbox" id="InfluxDBv2_Measurement_enabled" value="1" onclick = 'InvertEnableItem("InfluxDBv2", "Measurement")' unchecked >
<label for=InfluxDBv2_Measurement_enabled><class id="InfluxDBv2_Measurement_text" style="color:black;">Measurement</class></label>
</td>
<td>
<input type="text" id="InfluxDBv2_Measurement_value1">
</td>
<td style="font-size: 80%;">
Measurement name to use to publish the read value.
</td>
</tr>
<tr>
<td class="indent1">
<input type="checkbox" id="InfluxDBv2_Org_enabled" value="1" onclick = 'InvertEnableItem("InfluxDBv2", "Org")' unchecked >
<label for=InfluxDBv2_Org_enabled><class id="InfluxDBv2_Org_text" style="color:black;">Org</class></label>
</td>
<td>
<input type="text" id="InfluxDBv2_Org_value1">
</td>
<td style="font-size: 80%;">
Organisation (Org) for InfluxDBv2 authentication
</td>
</tr>
<tr>
<td class="indent1">
<input type="checkbox" id="InfluxDBv2_Token_enabled" value="1" onclick = 'InvertEnableItem("InfluxDBv2", "Token")' unchecked >
<label for=InfluxDBv2_Token_enabled><class id="InfluxDBv2_Token_text" style="color:black;">Token</class></label>
</td>
<td>
<input type="text" id="InfluxDBv2_Token_value1">
</td>
<td style="font-size: 80%;">
Token for InfluxDB authentication
</td>
</tr>
<tr>
<td class="indent1" colspan="3">
<br>
<b>Postprocessing Individual Parameters:
<select id="NumbersInfluxDB_value1" onchange="numberInfluxDBChanged()">
<option value="0" selected>default</option>
<option value="1" >NT</option>
<option value="2" >HT</option>
</select></b>
</td>
</tr>
<tr>
<td class="indent1" style="padding-left: 75px;">
<input type="checkbox" id="InfluxDBv2_Fieldname_enabled" value="1" onclick = 'InvertEnableItem("InfluxDBv2", "Fieldname")' unchecked >
<label for=InfluxDBv2_Fieldname_enabled><class id="InfluxDBv2_Fieldname_text" style="color:black;">Fieldname</class></label>
</td>
<td>
<input type="text" id="InfluxDBv2_Fieldname_value1">
</td>
<td style="font-size: 80%;">
Fieldname to use for saving.
</td>
</tr>
<tr> <tr>
<td colspan="3" style="padding-left: 20px;"> <td colspan="3" style="padding-left: 20px;">
<h4><input type="checkbox" id="Category_GPIO_enabled" value="1" onclick='UpdateAfterCategoryCheck()' unchecked > <h4><input type="checkbox" id="Category_GPIO_enabled" value="1" onclick='UpdateAfterCategoryCheck()' unchecked >
@@ -1487,7 +1581,8 @@ function LoadConfigNeu() {
InitIndivParameter(); InitIndivParameter();
UpdateInput(); UpdateInput();
UpdateInputIndividual(); var sel = document.getElementById("Numbers_value1");
UpdateInputIndividual(sel);
UpdateExpertModus(); UpdateExpertModus();
document.getElementById("divall").style.display = ''; document.getElementById("divall").style.display = '';
} }
@@ -1499,15 +1594,18 @@ function InitIndivParameter()
var _index = document.getElementById("Numbers_value1"); var _index = document.getElementById("Numbers_value1");
while (_index.length) while (_index.length)
_index.remove(0); _index.remove(0);
var _indexInflux = document.getElementById("NumbersInfluxDB_value1");
while (_indexInflux.length)
_indexInflux.remove(0);
for (var i = 0; i < NUMBERS.length; ++i){ for (var i = 0; i < NUMBERS.length; ++i){
var option = document.createElement("option"); var optionInflux = document.createElement("option");
option.text = NUMBERS[i]["name"]; optionInflux.text = NUMBERS[i]["name"];
option.value = i; optionInflux.value = i;
_index.add(option); _indexInflux.add(optionInflux);
} }
_index.selectedIndex = 0; _index.selectedIndex = 0;
_indexInflux.selectedIndex = 0;
} }
@@ -1798,7 +1896,7 @@ function ReadParameter(_param, _cat, _name, _optional, _number = -1){
} }
} }
function UpdateInputIndividual() function UpdateInputIndividual(sel)
{ {
if (NUNBERSAkt != -1) if (NUNBERSAkt != -1)
{ {
@@ -1806,22 +1904,22 @@ function UpdateInputIndividual()
ReadParameter(param, "PostProcessing", "AnalogDigitalTransitionStart", true, NUNBERSAkt) ReadParameter(param, "PostProcessing", "AnalogDigitalTransitionStart", true, NUNBERSAkt)
ReadParameter(param, "PostProcessing", "MaxRateValue", true, NUNBERSAkt) ReadParameter(param, "PostProcessing", "MaxRateValue", true, NUNBERSAkt)
ReadParameter(param, "PostProcessing", "MaxRateType", true, NUNBERSAkt) ReadParameter(param, "PostProcessing", "MaxRateType", true, NUNBERSAkt)
ReadParameter(param, "PostProcessing", "ExtendedResolution", false, NUNBERSAkt) ReadParameter(param, "PostProcessing", "ExtendedResolution", false, NUNBERSAkt)
ReadParameter(param, "PostProcessing", "IgnoreLeadingNaN", false, NUNBERSAkt) ReadParameter(param, "PostProcessing", "IgnoreLeadingNaN", false, NUNBERSAkt)
ReadParameter(param, "PostProcessing", "AllowNegativeRates", false, NUNBERSAkt) ReadParameter(param, "PostProcessing", "AllowNegativeRates", false, NUNBERSAkt)
ReadParameter(param, "InfluxDBv2", "Fieldname", true, NUNBERSAkt)
} }
var sel = document.getElementById("Numbers_value1"); // var sel = document.getElementById("Numbers_value1");
NUNBERSAkt = sel.selectedIndex; NUNBERSAkt = sel.selectedIndex;
WriteParameter(param, category, "PostProcessing", "DecimalShift", true, NUNBERSAkt); WriteParameter(param, category, "PostProcessing", "DecimalShift", true, NUNBERSAkt);
WriteParameter(param, category, "PostProcessing", "AnalogDigitalTransitionStart", true, NUNBERSAkt); WriteParameter(param, category, "PostProcessing", "AnalogDigitalTransitionStart", true, NUNBERSAkt);
WriteParameter(param, category, "PostProcessing", "MaxRateValue", true, NUNBERSAkt); WriteParameter(param, category, "PostProcessing", "MaxRateValue", true, NUNBERSAkt);
WriteParameter(param, category, "PostProcessing", "MaxRateType", true, NUNBERSAkt); WriteParameter(param, category, "PostProcessing", "MaxRateType", true, NUNBERSAkt);
WriteParameter(param, category, "PostProcessing", "ExtendedResolution", false, NUNBERSAkt); WriteParameter(param, category, "PostProcessing", "ExtendedResolution", false, NUNBERSAkt);
WriteParameter(param, category, "PostProcessing", "IgnoreLeadingNaN", false, NUNBERSAkt); WriteParameter(param, category, "PostProcessing", "IgnoreLeadingNaN", false, NUNBERSAkt);
WriteParameter(param, category, "PostProcessing", "AllowNegativeRates", false, NUNBERSAkt); WriteParameter(param, category, "PostProcessing", "AllowNegativeRates", false, NUNBERSAkt);
WriteParameter(param, category, "InfluxDBv2", "Fieldname", true, NUNBERSAkt);
} }
function UpdateInput() { function UpdateInput() {
@@ -1830,6 +1928,7 @@ function UpdateInput() {
document.getElementById("Category_MQTT_enabled").checked = category["MQTT"]["enabled"]; document.getElementById("Category_MQTT_enabled").checked = category["MQTT"]["enabled"];
document.getElementById("Category_GPIO_enabled").checked = category["GPIO"]["enabled"]; document.getElementById("Category_GPIO_enabled").checked = category["GPIO"]["enabled"];
document.getElementById("Category_InfluxDB_enabled").checked = category["InfluxDB"]["enabled"]; document.getElementById("Category_InfluxDB_enabled").checked = category["InfluxDB"]["enabled"];
document.getElementById("Category_InfluxDBv2_enabled").checked = category["InfluxDBv2"]["enabled"];
setVisible("GPIO_item", category["GPIO"]["enabled"]); setVisible("GPIO_item", category["GPIO"]["enabled"]);
WriteParameter(param, category, "TakeImage", "RawImagesLocation", true); WriteParameter(param, category, "TakeImage", "RawImagesLocation", true);
@@ -1876,6 +1975,13 @@ function UpdateInput() {
WriteParameter(param, category, "InfluxDB", "user", true); WriteParameter(param, category, "InfluxDB", "user", true);
WriteParameter(param, category, "InfluxDB", "password", true); WriteParameter(param, category, "InfluxDB", "password", true);
WriteParameter(param, category, "InfluxDBv2", "Uri", true);
WriteParameter(param, category, "InfluxDBv2", "Database", true);
WriteParameter(param, category, "InfluxDBv2", "Measurement", true);
WriteParameter(param, category, "InfluxDBv2", "Org", true);
WriteParameter(param, category, "InfluxDBv2", "Token", true);
WriteParameter(param, category, "InfluxDBv2", "Fieldname", true);
WriteParameter(param, category, "GPIO", "IO0", true); WriteParameter(param, category, "GPIO", "IO0", true);
WriteParameter(param, category, "GPIO", "IO1", true); WriteParameter(param, category, "GPIO", "IO1", true);
WriteParameter(param, category, "GPIO", "IO3", true); WriteParameter(param, category, "GPIO", "IO3", true);
@@ -1951,6 +2057,7 @@ function ReadParameterAll()
category["Digits"]["enabled"] = document.getElementById("Category_Digits_enabled").checked; category["Digits"]["enabled"] = document.getElementById("Category_Digits_enabled").checked;
category["MQTT"]["enabled"] = document.getElementById("Category_MQTT_enabled").checked; category["MQTT"]["enabled"] = document.getElementById("Category_MQTT_enabled").checked;
category["InfluxDB"]["enabled"] = document.getElementById("Category_InfluxDB_enabled").checked; category["InfluxDB"]["enabled"] = document.getElementById("Category_InfluxDB_enabled").checked;
category["InfluxDBv2"]["enabled"] = document.getElementById("Category_InfluxDBv2_enabled").checked;
category["GPIO"]["enabled"] = document.getElementById("Category_GPIO_enabled").checked; category["GPIO"]["enabled"] = document.getElementById("Category_GPIO_enabled").checked;
ReadParameter(param, "TakeImage", "RawImagesLocation", true); ReadParameter(param, "TakeImage", "RawImagesLocation", true);
@@ -1998,6 +2105,13 @@ function ReadParameterAll()
ReadParameter(param, "InfluxDB", "user", true); ReadParameter(param, "InfluxDB", "user", true);
ReadParameter(param, "InfluxDB", "password", true); ReadParameter(param, "InfluxDB", "password", true);
ReadParameter(param, "InfluxDBv2", "Uri", true);
ReadParameter(param, "InfluxDBv2", "Database", true);
ReadParameter(param, "InfluxDBv2", "Measurement", true);
ReadParameter(param, "InfluxDBv2", "Org", true);
ReadParameter(param, "InfluxDBv2", "Token", true);
// ReadParameter(param, "InfluxDB", "Fieldname", true);
ReadParameter(param, "GPIO", "IO0", true); ReadParameter(param, "GPIO", "IO0", true);
ReadParameter(param, "GPIO", "IO1", true); ReadParameter(param, "GPIO", "IO1", true);
ReadParameter(param, "GPIO", "IO3", true); ReadParameter(param, "GPIO", "IO3", true);
@@ -2029,7 +2143,8 @@ function ReadParameterAll()
ReadParameter(param, "System", "TimeServer", true); ReadParameter(param, "System", "TimeServer", true);
ReadParameter(param, "System", "RSSIThreshold", true); ReadParameter(param, "System", "RSSIThreshold", true);
UpdateInputIndividual(); var sel = document.getElementById("Numbers_value1");
UpdateInputIndividual(sel);
// FormatDecimalValue(param, "PostProcessing", "MaxRateValue"); // FormatDecimalValue(param, "PostProcessing", "MaxRateValue");
} }
@@ -2049,10 +2164,12 @@ function UpdateAfterCategoryCheck() {
category["Digits"]["enabled"] = document.getElementById("Category_Digits_enabled").checked; category["Digits"]["enabled"] = document.getElementById("Category_Digits_enabled").checked;
category["MQTT"]["enabled"] = document.getElementById("Category_MQTT_enabled").checked; category["MQTT"]["enabled"] = document.getElementById("Category_MQTT_enabled").checked;
category["InfluxDB"]["enabled"] = document.getElementById("Category_InfluxDB_enabled").checked; category["InfluxDB"]["enabled"] = document.getElementById("Category_InfluxDB_enabled").checked;
category["InfluxDBv2"]["enabled"] = document.getElementById("Category_InfluxDBv2_enabled").checked;
category["GPIO"]["enabled"] = document.getElementById("Category_GPIO_enabled").checked; category["GPIO"]["enabled"] = document.getElementById("Category_GPIO_enabled").checked;
UpdateInput(); UpdateInput();
UpdateInputIndividual(); var sel = document.getElementById("Numbers_value1");
UpdateInputIndividual(sel);
} }
function UpdateExpertModus() function UpdateExpertModus()
@@ -2103,7 +2220,7 @@ function saveTextAsFile()
ReadParameterAll(); ReadParameterAll();
WriteConfigININew(); WriteConfigININew();
SaveConfigToServer(domainname); SaveConfigToServer(domainname);
firework.launch('Configuration got updated. Please reboot to activate changes!', 'success', 5000); firework.launch('Configuration got updated. It will get applied after the next reboot!', 'success', 5000);
} }
} }
@@ -2129,7 +2246,24 @@ function editConfigDirect() {
function numberChanged() function numberChanged()
{ {
UpdateInputIndividual(); var sel = document.getElementById("Numbers_value1");
_neu = sel.selectedIndex;
UpdateInputIndividual(sel);
var _selInflux = document.getElementById("NumbersInfluxDB_value1");
if (_selInflux.selectedIndex != _neu)
_selInflux.selectedIndex = _neu
}
function numberInfluxDBChanged()
{
var sel = document.getElementById("NumbersInfluxDB_value1");
_neu = sel.selectedIndex;
UpdateInputIndividual(sel);
var _sel2 = document.getElementById("Numbers_value1");
if (_sel2.selectedIndex != _neu)
_sel2.selectedIndex = _neu
} }
LoadConfigNeu(); LoadConfigNeu();

View File

@@ -3,7 +3,7 @@
<head> <head>
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon"> <link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
<meta charset="utf-8"/> <meta charset="utf-8"/>
<title>Make Digital Alignment</title> <title>Digit ROI's</title>
<style> <style>
h1 {font-size: 2em;} h1 {font-size: 2em;}
@@ -58,8 +58,15 @@ th, td {
<body style="font-family: arial; padding: 0px 10px;"> <body style="font-family: arial; padding: 0px 10px;">
<h2><input type="checkbox" id="Category_Digits_enabled" value="1" onclick = 'EnDisableDigits()' checked > <h2>Digit ROI's</h2>
Edit Digits</h2> <p>On this page you define ROI's for the digits.
See <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/ target=_blank>https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/</a> for explanations.</p>
<p><input type="checkbox" id="Category_Digits_enabled" value="1" onclick = 'EnDisableDigits()' checked><label for="Category_Digits_enabled">Enable Digit ROI's</label></p>
<p>After saving the digit ROI's, you can define the <a href=edit_analog.html>analog</a> ROI's if your meter has analog counters.<br>
Only after those steps a reboot is required.</p>
<div id="div1"> <div id="div1">
@@ -126,10 +133,8 @@ th, td {
<table> <table>
<tr> <tr>
<td><input class="button" type="submit" id="saveroi" name="saveroi" onclick="SaveToConfig()" value="Save" tabindex=10></td> <td><input class="button" type="submit" id="saveroi" name="saveroi" onclick="SaveToConfig()" value="Save" tabindex=10>
</tr> <p>Proceed to update the <a href=edit_analog.html>analog</a> ROI's when you are done or <a href=reboot_page.html>reboot</a> if there are no analogue counters.</p></td>
<tr>
<td><button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate changes</button></td>
</tr> </tr>
</table> </table>
@@ -291,7 +296,7 @@ function SaveToConfig(){
cofcat["Digits"]["enabled"] = document.getElementById("Category_Digits_enabled").checked; cofcat["Digits"]["enabled"] = document.getElementById("Category_Digits_enabled").checked;
WriteConfigININew(); WriteConfigININew();
SaveConfigToServer(domainname); SaveConfigToServer(domainname);
firework.launch('Configuration got updated. Please reboot to activate changes!', 'success', 5000); firework.launch('Configuration got updated. It will get applied after the next reboot!', 'success', 5000);
} }

View File

@@ -28,12 +28,12 @@ p {font-size: 1em;}
<p> <p>
This is the first time you started the digitizer after the initial installation. You have been automatically routed to the <b>initial setup procedure</b>. This is the first time you started the digitizer after the initial installation. You have been automatically routed to the <b>initial setup procedure</b>.
Here you adjust the settings for your counter within five steps. In the final step the inital setup will be disabled and it will restart to the normal mode. Here you adjust the settings for your meter within five steps. In the final step the inital setup will be disabled and it will restart to the normal mode.
All settings will also be accessible there. All settings will also be accessible there. See <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/initial-setup target=_blank>
<br> https://jomjol.github.io/AI-on-the-edge-device-docs/initial-setup</a> for additional explanations.</p>
<br> </p>
You can navigate forward and backward during the setup with the buttons "Next" and "Previous".<br><br> <p>You can navigate forward and backward during the setup with the buttons "Next" and "Previous".<br><br>
<span color=red>Do not forget to save in each step!</span><br> <span color=red>Do not forget to save in each step before heading to another step!</span><br>
</p> </p>
<p> <p>
This is an overview over the five steps: This is an overview over the five steps:
@@ -41,26 +41,27 @@ p {font-size: 1em;}
<p> <p>
<ol> <ol>
<li>Create reference image <br> <li><p>Create the <b>Reference Image</b>.<br>
Base for the position referencing and the identification of the digits and counters.</li> It is the base for the position referencing and the identification of the digits and counters.</p></li>
<li>Define two unique references <br> <li><p>Define two unique <b>Reference Marks</b>.<br>
Used to align the individual camera shot and identify the absolut positions</li> They is used to align the individual camera images and identify the absolut positions.</p></li>
<li>Define the digits <br> <li></p>Define <b>ROI's</b> for the <b>Digits</b>.<br>
Digital digits to be recognized</li> They will be used to digitize the digit part of your meter.<br>
<li>Define the analog counters <br> If your meter has no digits, this step can be skipped.</p></li>
Analog counters to be identified</li> <li>Define <b>ROI's</b>> for the <b>Analog Counters</b>.<br>
<li>General settings <br> They will be used to digitize the analog part of your meter.<br>
Most settings can be used with default value at the beginning. MQTT connection can be specified here</li> If your meter has no analog counters, this step can be skipped.</p></li>
<li><p><b>General Settings</b><br>
Further configuration of your device.</p></li>
</ol> </ol>
<p>
After step 5 setup mode is completed, it then reboots and starts into the normal mode. <p>Please be patient when switching to another step. The device takes some time to load all needed information!</p>
<br><br>
If you need support, have a look to the <a href=https://jomjol.github.io/AI-on-the-edge-device-docs target=_blank>documenation</a> or the <a href=https://github.com/jomjol/AI-on-the-edge-device/discussions target=_blank>discussion</a> pages.<br> <p>After step 5 the setup is completed, you then can reboot and starts into the normal operation mode.</p>
<h4>Have fun with the digitizer!</h4> <p>If you need support, have a look to the <a href=https://jomjol.github.io/AI-on-the-edge-device-docs target=_blank>documentation</a> or the <a href=https://github.com/jomjol/AI-on-the-edge-device/discussions target=_blank>discussion</a> pages.</p>
<p><b>Have fun with your digitizer!</b></p>
</body> </body>
</html> </html>

View File

@@ -28,16 +28,11 @@ p {font-size: 1em;}
<body style="font-family: arial"> <body style="font-family: arial">
<h4>Finished!</h4> <h4>Step 6: Setup Completed!</h4>
<p> <p>Congratulations, you completed the setup and are now ready to reboot to the normal mode!</p>
Now you are finished with the setup and ready to reboot to the normal mode. <p>Once you have pushed the button below, the setup modus will be left and the digitizer will restart to normal operation mode.<br>
<br> The Web Interface will automatically reload. It will take some minutes until you get the first reading.
Once you have pushed below button, the setup modus will be left and the digitizer will restart to normal operation mode.
<br>
The Web Interface will automatically reload. It will take two to four minutes, until you get the first reading.
<br>
All settings can be changed as well in the configuration menu in the Normal modus.
</p> </p>
<p> <p>

View File

@@ -2,7 +2,7 @@
<html> <html>
<head> <head>
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon"> <link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
<title>Make Reference</title> <title>Reference Image</title>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<style> <style>
@@ -36,8 +36,12 @@ table {
</head> </head>
<body style="font-family: arial; padding: 0px 10px;"> <body style="font-family: arial; padding: 0px 10px;">
<h2>Create Reference out of Raw Image</h2> <h2>Reference Image</h2>
<p><b>Note: After saving a new Reference Image, make sure to update the Alignment Marks and the ROI's and reboot once!</b></p> <p>On this page you define the Reference Image.
See <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Reference-Image/ target=_blank>https://jomjol.github.io/AI-on-the-edge-device-docs/Reference-Image/</a> for explanations.</p>
<p>After saving a new Reference Image, make sure to update the <a href=edit_alignment.html>Alignment Marks</a> and then
the <a href=edit_digits.html>digit</a> resp. <a href=edit_analog.html>analog</a> ROI's.<br>
Only after those steps a reboot is required.</p>
<table> <table>
<tr> <tr>
@@ -87,10 +91,8 @@ table {
<td><canvas id="canvas"></canvas></td> <td><canvas id="canvas"></canvas></td>
</tr> </tr>
<tr> <tr>
<td><input class="button" type="button" id="updatereferenceimage" value="Save" onclick="SaveReference()"></td> <td><input class="button" type="button" id="updatereferenceimage" value="Save" onclick="SaveReference()">
</tr> <p>Proceed to update the <a href=edit_alignment.html>Alignment Marks</a> when you are done.</p></td>
<tr>
<td><button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate saved Reference</button></td>
</tr> </tr>
</table> </table>
@@ -129,7 +131,7 @@ table {
_contrast = document.getElementById("TakeImage_Contrast_value1").value; _contrast = document.getElementById("TakeImage_Contrast_value1").value;
_saturation = document.getElementById("TakeImage_Saturation_value1").value; _saturation = document.getElementById("TakeImage_Saturation_value1").value;
url = getDomainname() + "/editflow?task=test_take&bri=" + _brightness; url = getDomainname() + "/editflow?task=test_take&bri=" + _brightness;
url = url + "&con=" + _saturation + "&sat=" + _saturation + "&int=" + _intensity; url = url + "&con=" + _contrast + "&sat=" + _saturation + "&int=" + _intensity;
} }
else else
{ {
@@ -270,7 +272,7 @@ table {
SaveCanvasToImage(canvas, "/config/reference.jpg", true, getDomainname()); SaveCanvasToImage(canvas, "/config/reference.jpg", true, getDomainname());
showReference(param); showReference(param);
UpdatePage(); UpdatePage();
firework.launch('Reference got updated. Please reboot to activate changes!', 'success', 5000); firework.launch('Reference got updated. It will get applied after the next reboot!', 'success', 5000);
} }
} }

View File

@@ -20,12 +20,14 @@ p {font-size: 1em;}
<body style="font-family: arial"> <body style="font-family: arial">
<h4>Reference Image</h4> <h4>Step 1: Define a Reference Image</h4>
The reference image is the base to define the digits, counters and references positions. The reference image is the base to define the digits, counters and references positions.
<p> <p>
Firstly you see the default image. Use the Button "Create New Reference" to start to create your own reference image.<br> Firstly you see the default image. Use the Button "Create New Reference" to start to create your own reference image.<br>
Most important feature is a straight alignment of the image. Use the Pre-rotate angle and the fine alignment to adjust the rotation of the image<br> Most important feature is a straight alignment of the image. Use the Pre-rotate angle and the fine alignment to adjust the rotation of the image.
Store the reference image by pushing <b>"Update Reference Image"</b>. A reboot is not yet required. </p>
<p>
Don't forget to save your changes with the <b>"Save"</b> button!
</p> </p>
</body> </body>

View File

@@ -19,15 +19,15 @@ p {font-size: 1em;}
<body style="font-family: arial"> <body style="font-family: arial">
<h4>Alignment References</h4> <h4>Step 2: Define two Alignment Marks</h4>
Two opposite alignment references are needed to identify unique fix points on the image. Two opposite alignment marks are needed to identify unique fix points on the image.
<p> <p>
Mark the reference by drag and dop with the mouse or with the coordinates and push <b>"Update Reference"</b>. Mark the reference by drag and dop with the mouse or with the coordinates and push <b>"Update Reference"</b>.
<br> <br>
You can switch between the two reference with <b>"Select Reference"</b>. You can switch between the two reference with <b>"Select Reference"</b>.
</p> </p>
<p> <p>
Don't forget to save your changes! A reboot is not yet required. Don't forget to save your changes with the <b>"Save"</b> button!
</p> </p>
</body> </body>

View File

@@ -20,7 +20,7 @@ p {font-size: 1em;}
<body style="font-family: arial"> <body style="font-family: arial">
<h4>Define Digits</h4> <h4>Step 3: Define ROI's for your Digits</h4>
Here you define your digits you want to read. If you have more than one number on the reading you can define several numbers with the <b>"Number"</b> selector. There you can also define new numbers. Here you define your digits you want to read. If you have more than one number on the reading you can define several numbers with the <b>"Number"</b> selector. There you can also define new numbers.
<p> <p>
With the drop down menue <b>"ROI x"</b> you can change between the different digits. Mark them with the mouse or the coordinates. With the drop down menue <b>"ROI x"</b> you can change between the different digits. Mark them with the mouse or the coordinates.
@@ -29,7 +29,7 @@ Here you define your digits you want to read. If you have more than one number o
You can change it with <b>"move Next" / "move Previous"</b>. You can change it with <b>"move Next" / "move Previous"</b>.
</p> </p>
<p> <p>
Don't forget to save your changes! A reboot is not yet required. Don't forget to save your changes with the <b>"Save"</b> button!
</p> </p>
</body> </body>

View File

@@ -19,7 +19,7 @@ p {font-size: 1em;}
<body style="font-family: arial"> <body style="font-family: arial">
<h4>Define Digits</h4> <h4>Step 4: Define ROI's for your Analog Counters</h4>
Here you define your analog counters you want to read. If you have more than one number on the reading you can define several numbers with the <b>"Number"</b> selector. There you can also define new numbers. If you do not have analog counters delete all ROIs. Here you define your analog counters you want to read. If you have more than one number on the reading you can define several numbers with the <b>"Number"</b> selector. There you can also define new numbers. If you do not have analog counters delete all ROIs.
<p> <p>
With the drop down menue <b>"ROI x"</b> you can change between the different counters. Mark them with the mouse or the coordinates. With the drop down menue <b>"ROI x"</b> you can change between the different counters. Mark them with the mouse or the coordinates.
@@ -28,7 +28,7 @@ Here you define your analog counters you want to read. If you have more than one
You can change it with <b>"move Next" / "move Previous"</b>. You can change it with <b>"move Next" / "move Previous"</b>.
</p> </p>
<p> <p>
Don't forget to save your changes with <b>"Save all to Config.ini"</b>! A reboot is not yet required. Don't forget to save your changes with the <b>"Save"</b> button!
</p> </p>
</body> </body>

View File

@@ -19,12 +19,12 @@ p {font-size: 1em;}
<body style="font-family: arial"> <body style="font-family: arial">
<h4>General configuration parameters</h4> <h4>Step 5: General Configuration Settings</h4>
<p>Here you can define additional settings. The settings should fit for a normal setup.</p> <p>Here you can define additional settings. The default settings should fit for a normal/initial setup.</p>
<p>You will also be availabl</p> <p>You will also be able to change them later, so don't worry if you do not understand the parameters yet!</p>
</p>
<p> <p>
Don't forget to save your changes! Don't forget to save your changes with the <b>"Save"</b> button!
<br><span color=red>You should not reboot here, but leave the setup modus on the next page!</span>
</p> </p>
</body> </body>

View File

@@ -1,26 +1 @@
<!DOCTYPE html> <!-- This page is never shown -->
<html style="width: fit-content">
<head>
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
<title>AI on the edge</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
h1 {font-size: 2em; margin-block-end: 0.3em;}
h2 {font-size: 1.5em;margin-block-start: 0.3em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
</style>
</head>
<body style="font-family: arial">
<h4>Finished!</h4>
Read below!
</body>
</html>

View File

@@ -13,7 +13,7 @@ function getbasepath(){
{ {
// host = "http://192.168.2.219"; // jomjol interner test // host = "http://192.168.2.219"; // jomjol interner test
// host = "http://192.168.178.46"; // jomjol interner test // host = "http://192.168.178.46"; // jomjol interner test
host = "http://192.168.178.62"; // jomjol interner Real host = "http://192.168.178.44"; // jomjol interner Real
// host = "http://192.168.43.191"; // host = "http://192.168.43.191";
// host = "."; // jomjol interner localhost // host = "."; // jomjol interner localhost

View File

@@ -118,7 +118,7 @@
* - *.bin * - *.bin
* - *.zip */ * - *.zip */
else if (filename.endsWith(".zip") || filename.endsWith(".bin")) { // Warning but still accepted else if (filename.endsWith(".zip") || filename.endsWith(".bin")) { // Warning but still accepted
firework.launch('The filename does not match the suggested filename pattern, but is nevertheless accepted. You can now press "Upload and install', 'warning', 10000); firework.launch('The filename does not match the suggested filename pattern, but is nevertheless accepted. You can now press "Upload and install".', 'warning', 10000);
} }
/* Any other file name format is not accepted */ /* Any other file name format is not accepted */
else { // invalid else { // invalid

View File

@@ -45,7 +45,6 @@ function ZerlegeZeile(input, delimiter = " =\t\r")
var Output = Array(0); var Output = Array(0);
// delimiter = " =,\t"; // delimiter = " =,\t";
input = trim(input, delimiter);
/* The input can have multiple formats: /* The input can have multiple formats:
* - key = value * - key = value
@@ -61,12 +60,14 @@ function ZerlegeZeile(input, delimiter = " =\t\r")
* As a workaround and to not break any legacy usage, we enforce to only use the * As a workaround and to not break any legacy usage, we enforce to only use the
* equal sign, if the key is "password" * equal sign, if the key is "password"
*/ */
if (input.includes("password")) { // Line contains a password, use the equal sign as the only delimiter and only split on first occurrence if (input.includes("password") || input.includes("Token")) { // Line contains a password, use the equal sign as the only delimiter and only split on first occurrence
var pos = input.indexOf("="); var pos = input.indexOf("=");
delimiter = " \t\r"
Output.push(trim(input.substr(0, pos), delimiter)); Output.push(trim(input.substr(0, pos), delimiter));
Output.push(trim(input.substr(pos +1, input.length), delimiter)); Output.push(trim(input.substr(pos +1, input.length), delimiter));
} }
else { // Legacy Mode else { // Legacy Mode
input = trim(input, delimiter);
var pos = findDelimiterPos(input, delimiter); var pos = findDelimiterPos(input, delimiter);
var token; var token;
while (pos > -1) { while (pos > -1) {

View File

@@ -195,6 +195,17 @@ function ParseConfig() {
ParamAddValue(param, catname, "user"); ParamAddValue(param, catname, "user");
ParamAddValue(param, catname, "password"); ParamAddValue(param, catname, "password");
var catname = "InfluxDBv2";
category[catname] = new Object();
category[catname]["enabled"] = false;
category[catname]["found"] = false;
param[catname] = new Object();
ParamAddValue(param, catname, "Uri");
ParamAddValue(param, catname, "Database");
ParamAddValue(param, catname, "Measurement");
ParamAddValue(param, catname, "Org");
ParamAddValue(param, catname, "Token");
ParamAddValue(param, catname, "Fieldname", 1, true);
var catname = "GPIO"; var catname = "GPIO";
category[catname] = new Object(); category[catname] = new Object();

View File

@@ -40,7 +40,7 @@ p {font-size: 1em;}
<td> <td>
<button class="button" id="previous" name="previous" onclick="clickPrevious()">Previous</button> <button class="button" id="previous" name="previous" onclick="clickPrevious()">Previous</button>
<button class="button" id="next" name="next" onclick="clickNext()">Next</button> <button class="button" id="next" name="next" onclick="clickNext()">Next</button>
If you need support, have a look to the <a href=https://jomjol.github.io/AI-on-the-edge-device-docs target=_blank>documenation</a> or the <a href=https://github.com/jomjol/AI-on-the-edge-device/discussions target=_blank>discussion</a> pages. If you need support, have a look to the <a href=https://jomjol.github.io/AI-on-the-edge-device-docs target=_blank>documentation</a> or the <a href=https://github.com/jomjol/AI-on-the-edge-device/discussions target=_blank>discussion</a> pages.
</td> </td>
</tr> </tr>
</table> </table>
@@ -83,53 +83,53 @@ function clickPrevious() {
function LoadStep(){ function LoadStep(){
switch (aktstatu) { switch (aktstatu) {
case 0: case 0:
document.getElementById('maincontent').src = '/edit_explain_0.html?v=$COMMIT_HASH'; document.getElementById('maincontent').src = 'edit_explain_0.html?v=$COMMIT_HASH';
document.getElementById('h_iframe_explain').style.display = "none"; document.getElementById('h_iframe_explain').style.display = "none";
document.getElementById("previous").disabled = true; document.getElementById("previous").disabled = true;
document.getElementById("next").disabled = false; document.getElementById("next").disabled = false;
break; break;
case 1: case 1:
document.getElementById('maincontent').src = '/edit_reference.html?v=$COMMIT_HASH'; document.getElementById('maincontent').src = 'edit_reference.html?v=$COMMIT_HASH';
document.getElementById('explaincontent').src = '/explain_1.html?v=$COMMIT_HASH'; document.getElementById('explaincontent').src = 'explain_1.html?v=$COMMIT_HASH';
document.getElementById('h_iframe_explain').style.display = ""; document.getElementById('h_iframe_explain').style.display = "";
document.getElementById("previous").disabled = false; document.getElementById("previous").disabled = false;
document.getElementById("next").disabled = false; document.getElementById("next").disabled = false;
break; break;
case 2: case 2:
document.getElementById('maincontent').src = '/edit_alignment.html?v=$COMMIT_HASH'; document.getElementById('maincontent').src = 'edit_alignment.html?v=$COMMIT_HASH';
document.getElementById('explaincontent').src = '/explain_2.html?v=$COMMIT_HASH'; document.getElementById('explaincontent').src = 'explain_2.html?v=$COMMIT_HASH';
document.getElementById('h_iframe_explain').style.display = ""; document.getElementById('h_iframe_explain').style.display = "";
document.getElementById("previous").disabled = false; document.getElementById("previous").disabled = false;
document.getElementById("next").disabled = false; document.getElementById("next").disabled = false;
break; break;
case 3: case 3:
document.getElementById('maincontent').src = '/edit_digits.html?v=$COMMIT_HASH'; document.getElementById('maincontent').src = 'edit_digits.html?v=$COMMIT_HASH';
document.getElementById('explaincontent').src = '/explain_3.html?v=$COMMIT_HASH'; document.getElementById('explaincontent').src = 'explain_3.html?v=$COMMIT_HASH';
document.getElementById('h_iframe_explain').style.display = ""; document.getElementById('h_iframe_explain').style.display = "";
document.getElementById("previous").disabled = false; document.getElementById("previous").disabled = false;
document.getElementById("next").disabled = false; document.getElementById("next").disabled = false;
break; break;
case 4: case 4:
document.getElementById('maincontent').src = '/edit_analog.html?v=$COMMIT_HASH'; document.getElementById('maincontent').src = 'edit_analog.html?v=$COMMIT_HASH';
document.getElementById('explaincontent').src = '/explain_4.html?v=$COMMIT_HASH'; document.getElementById('explaincontent').src = 'explain_4.html?v=$COMMIT_HASH';
document.getElementById('h_iframe_explain').style.display = ""; document.getElementById('h_iframe_explain').style.display = "";
document.getElementById("previous").disabled = false; document.getElementById("previous").disabled = false;
document.getElementById("next").disabled = false; document.getElementById("next").disabled = false;
break; break;
case 5: case 5:
document.getElementById('maincontent').src = '/edit_config_param.html?v=$COMMIT_HASH?InitialSetup=true'; document.getElementById('maincontent').src = 'edit_config_param.html?v=$COMMIT_HASH?InitialSetup=true';
document.getElementById('explaincontent').src = '/explain_5.html?v=$COMMIT_HASH'; document.getElementById('explaincontent').src = 'explain_5.html?v=$COMMIT_HASH';
document.getElementById('h_iframe_explain').style.display = ""; document.getElementById('h_iframe_explain').style.display = "";
document.getElementById("previous").disabled = false; document.getElementById("previous").disabled = false;
document.getElementById("next").disabled = false; document.getElementById("next").disabled = false;
break; break;
case 6: case 6:
document.getElementById('maincontent').src = '/edit_explain_6.html?v=$COMMIT_HASH'; document.getElementById('maincontent').src = 'edit_explain_6.html?v=$COMMIT_HASH';
document.getElementById('explaincontent').src = '/explain_6.html?v=$COMMIT_HASH'; document.getElementById('explaincontent').src = 'explain_6.html?v=$COMMIT_HASH'; // Note: The page never gets shown!
document.getElementById('h_iframe_explain').style.display = "none"; document.getElementById('h_iframe_explain').style.display = "none";
document.getElementById("previous").disabled = false; document.getElementById("previous").disabled = false;
document.getElementById("next").disabled = true; document.getElementById("next").disabled = true;