Compare commits

..

6 Commits

Author SHA1 Message Date
CaCO3
59ccd601fc . 2023-05-03 22:36:07 +02:00
CaCO3
becbfc463f Merge branch 'add-websocket2' of https://github.com/jomjol/AI-on-the-edge-device into add-websocket2 2023-05-03 21:47:17 +02:00
CaCO3
a5d2086159 add example UI 2023-05-03 21:46:59 +02:00
CaCO3
716dcb5a84 add websocket support 2023-05-03 21:46:59 +02:00
CaCO3
a5c66eb014 add example UI 2023-04-30 22:47:09 +02:00
CaCO3
6c86317d52 add websocket support 2023-04-30 21:37:35 +02:00
113 changed files with 3084 additions and 5298 deletions

View File

@@ -4,31 +4,6 @@
# Due to the way it works, you have to add each response twice, once for the issue, once for the discussions!
labels:
#######################################################################
# Bot Response: Documentation
#######################################################################
- name: bot-reply Documentation
labeled:
issue:
body: |
Please have a look on our documentation: https://jomjol.github.io/AI-on-the-edge-device-docs
discussion:
body: |
Please have a look on our documentation: https://jomjol.github.io/AI-on-the-edge-device-docs
#######################################################################
# Bot Response: ROI setup
#######################################################################
- name: bot-reply ROI Setup
labeled:
issue:
body: |
Make sure to setup your ROIs properly. Have a look on our documentation: https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/#how-to-setup-the-digit-rois-perfectly
discussion:
body: |
Make sure to setup your ROIs properly. Have a look on our documentation: https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/#how-to-setup-the-digit-rois-perfectly
#######################################################################
# Bot Response: Logfile
#######################################################################

View File

@@ -380,7 +380,7 @@ jobs:
#########################################################################################
## Update the Web Installer on a release
#########################################################################################
# Make sure to also update update-webinstaller.yml!
# This is the same as in the update-webinstaller.yml
update-web-installer:
needs: [release]
environment:
@@ -408,14 +408,12 @@ jobs:
- name: Add binary to Web Installer and update manifest
run: |
echo "Updating Web installer to use firmware from ${{ steps.last_release.outputs.tag_name }}..."
rm -f docs/binary/firmware.bin
wget https://github.com/jomjol/AI-on-the-edge-device/releases/download/${{ steps.last_release.outputs.tag_name }}/AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
unzip AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
cp -f firmware.bin docs/binary/firmware.bin
echo "Updating index and manifest file..."
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/index.html
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
cp -f docs/manifest_template.json docs/manifest.json
sed -i 's/VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
- name: Setup Pages
uses: actions/configure-pages@v2

View File

@@ -1,7 +1,7 @@
# This updates the Web Installer with the files from the docs folder and the binary of the latest release
# it only gets run on:
# - Manually triggered
# Make sure to also update the lower part of build.yml!
# - Changes to the docs folder in the `rolling` branch
# - On a release
name: Manual Web Installer Update
@@ -40,14 +40,12 @@ jobs:
- name: Add binary to Web Installer and update manifest
run: |
echo "Updating Web installer to use firmware from ${{ steps.last_release.outputs.tag_name }}..."
rm -f docs/binary/firmware.bin
wget https://github.com/jomjol/AI-on-the-edge-device/releases/download/${{ steps.last_release.outputs.tag_name }}/AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
unzip AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
cp -f firmware.bin docs/binary/firmware.bin
echo "Updating index and manifest file..."
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/index.html
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
cp -f docs/manifest_template.json docs/manifest.json
sed -i 's/VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
- name: Setup Pages
uses: actions/configure-pages@v2

View File

@@ -1,51 +1,3 @@
## [15.4.0] - 2023-12-22
### Changes
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/15.4.0...v15.3.0)
#### Changed
- Updates submodules (esp-nn, tflite-micro-example, esp-camera)
- Explicitly included needed tflite network layers (instead of all) , resulting in much smaller firmware size
- Added shortcut icon
- Rename in InfluxDB 'Database' to 'Bucket'
- Updated analog tflite files
- dig-class100-0167_s2_q.tflite
- dig-class11_1700_s2.tflite
- ana-cont_1208_s2_q.tflite
#### Fixed
* InfluxDB: consider DST setting for UTC time conversion
* Minor html response bugfix
- Memory leakage (MQTT)
## [15.3.0] - 2023-07-22
### Changes
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.3.0...v15.2.4)
#### Changed
- Updated PlatformIO to `6.3.2`
- Updated analog tflite files
- ana-cont_1207_s2_q.tflite
- dig-cont_0620_s3_q.tflite
## [15.2.4] - 2023-05-02
### Changes

View File

@@ -2,41 +2,15 @@
**There are a lot of ideas for further improvements, but only limited capacity on side of the developer.** Therefore I have created this page as a collection of ideas.
1. Whoever has a new idea can put it here, so that it is not forgotten.
1. Who ever has a new idea can put it here, so it that it is not forgotten.
2. Whoever has the time, capacity and passion to support the project can take any of the ideas and implement them. I will provide support and help wherever I can!
2. Who ever has time, capacity and passion to support, can take any of the ideas and implement them.
I will support and help where ever I can!
____
#### #40 Trigger with cron like exact time slot
* https://github.com/jomjol/AI-on-the-edge-device/issues/2470
#### #39 upnp implementation to auto detect the device
* https://github.com/jomjol/AI-on-the-edge-device/issues/2481
#### #38 Energy Saving
* Deep sleep between recognition
* https://github.com/jomjol/AI-on-the-edge-device/issues/2486
#### #37 Auto init SD card
* Fully implement the SD card handling (including formatting) into the firmware
* https://github.com/jomjol/AI-on-the-edge-device/issues/2488Demo
#### #36 Run demo without camera
Demo mode requires a working camera (if not, one receives a 'Cam bad' error). Would be nice to demo or play around on other ESP32 boards (or on ESP32-CAM boards when you broke the camera cable...).
#### #35 Use the same model, but provide the image from a Smartphone Camera
@@ -77,7 +51,7 @@ haveing this state in the mqtt broker can trigger functions like closing the ate
#### ~~#29 Add favicon and use the hostname for the website~~- implemented v11.3.1
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/927~~
~~* https://github.com/jomjol/AI-on-the-edge-device/issues/927~~
#### #28 Improved error handling for ROIs
@@ -115,7 +89,7 @@ haveing this state in the mqtt broker can trigger functions like closing the ate
#### ~~#22 Direct hint to the different neural network files in the other repositories~~- implemented >v11.3.1
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/644~~
~~* https://github.com/jomjol/AI-on-the-edge-device/issues/644~~

View File

@@ -1,31 +1,31 @@
# Welcome to the AI-on-the-edge-device
<img src="images/icon/watermeter.svg" width="100px">
Artificial intelligence based systems have become established in our everyday lives. Just think of speech or image recognition. Most of the systems rely on either powerful processors or a direct connection to the cloud for doing the calculations there. With the increasing power of modern processors, the AI systems are coming closer to the end user which is usually called **edge computing**.
Here, this edge computing is put into a practically oriented example, where an AI network is implemented on an ESP32 device so: **AI on the edge**.
Artificial intelligence based systems have been established in our every days live. Just think of speech or image recognition. Most of the systems relay on either powerful processors or a direct connection to the cloud for doing the calculations up there. With the increasing power of modern processors the AI systems are coming closer to the end user - which is usually called **edge computing**.
Here this edge computing is brought into a practical oriented example, where a AI network is implemented on a ESP32 device so: **AI on the edge**.
This project allows you to digitize your **analog** water, gas, power and other meters using cheap and easily available hardware.
This projects allows you to digitalize your **analoge** water, gas, power and other meters using cheap and easily available hardware.
All you need is an [ESP32 board with a supported camera](https://jomjol.github.io/AI-on-the-edge-device-docs/Hardware-Compatibility/) and something of a practical hand.
All you need is an [ESP32 board with a supported camera](https://jomjol.github.io/AI-on-the-edge-device-docs/Hardware-Compatibility/) and a bit of a practical hand.
<img src="images/esp32-cam.png" width="200px">
## Key features
- Tensorflow Lite (TFlite) integration including easy-to-use wrapper
- Inline image processing (feature detection, alignment, ROI extraction)
- **Small** and **cheap** device (3 x 4.5 x 2 cm³, < 10 EUR)
- Integrated camera and illumination
- Web interface for administration and control
- OTA interface for updating directly via the web interface
- Tensorflow Lite (TFlite) integration - including easy to use wrapper
- Inline Image processing (feature detection, alignment, ROI extraction)
- **Small** and **cheap** device (3x4.5x2 cm³, < 10 EUR)
- camera and illumination integrated
- Web surface to administrate and control
- OTA-Interface to update directly through the web interface
- Full integration into Homeassistant
- Support for Influx DB 1 and 2
- Support for Influx DB 1
- MQTT
- REST API
## Workflow
The device takes a photo of your meter at a defined interval. It then extracts the Regions of Interest (ROIs) from the image and runs them through artificial intelligence. As a result, you get the digitized value of your meter.
The device takes a photo of your meter at a defined interval. It then extracts the Regions of Interest (ROI's) out of it and runs them through an artificial inteligence. As a result, you get the digitalized value of your meter.
There are several options for what to do with that value. Either send it to an MQTT broker, write it to an InfluxDb or simply provide access to it via a REST API.
There are several options what to do with that value. Either send it to a MQTT broker, write it to an InfluxDb or simply provide it throug a REST API.
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/idea.jpg" width="600">
@@ -41,68 +41,62 @@ There are several options for what to do with that value. Either send it to an M
## Setup
There is growing [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/) which provides you with a lot of information. Head there to get a start, set it up and configure it.
There is a growing [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/) which provides you with a lot of information.
Head there to get a start, set it up and configure it.
There are also articles in the German Heise magazine "make:" about the setup and technical background (behind a paywall): [DIY - Setup](https://www.heise.de/select/make/2021/2/2103513300897420296)
There are also a articles in the German Heise magazine "make:" about the setup and the technical background (behind a paywall) : [DIY - Setup](https://www.heise.de/select/make/2021/2/2103513300897420296)
A lot of people created useful Youtube videos which might help you getting started.
Here a small selection:
- [youtube.com/watch?v=HKBofb1cnNc](https://www.youtube.com/watch?v=HKBofb1cnNc)
- [youtube.com/watch?v=yyf0ORNLCk4](https://www.youtube.com/watch?v=yyf0ORNLCk4)
- [youtube.com/watch?v=XxmTubGek6M](https://www.youtube.com/watch?v=XxmTubGek6M)
- [youtube.com/watch?v=mDIJEyElkAU](https://www.youtube.com/watch?v=mDIJEyElkAU)
- [youtube.com/watch?v=SssiPkyKVVs](https://www.youtube.com/watch?v=SssiPkyKVVs)
- [youtube.com/watch?v=MAHE_QyHZFQ](https://www.youtube.com/watch?v=MAHE_QyHZFQ)
- [youtube.com/watch?v=Uap_6bwtILQ](https://www.youtube.com/watch?v=Uap_6bwtILQ)
For further background information, head to [Neural Networks](https://www.heise.de/select/make/2021/6/2126410443385102621), [Training Neural Networks](https://www.heise.de/select/make/2022/1/2134114065999161585) and [Programming on the ESP32](https://www.heise.de/select/make/2022/2/2204010051597422030).
For further background information, head to [Neural Networks](https://www.heise.de/select/make/2021/6/2126410443385102621), [Training Neural Networks](https://www.heise.de/select/make/2022/1/2134114065999161585) and [Programming on the ESP32](https://www.heise.de/select/make/2022/2/2204010051597422030)
### Download
The latest available version can be found on the [Releases page](https://github.com/jomjol/AI-on-the-edge-device/releases).
The latest available version is available on the [Releases page](https://github.com/jomjol/AI-on-the-edge-device/releases).
### Flashing the ESP32
Initially you will have to flash the ESP32 via a USB connection. Later updates are possible directly over the air (OTA using WIFI).
### Flashing of the ESP32
Initially you will have to flash the ESP32 through an USB connection. Later an update is possible directly over the Air (OTA).
There are different ways to flash your ESP32:
- The prefered way is the [Web Installer and Console](https://jomjol.github.io/AI-on-the-edge-device/index.html) which is a browser-based tool to flash the ESP32 and extract the log over USB:
![](images/web-installer.png)
- [Web Installer and Console](https://jomjol.github.io/AI-on-the-edge-device/index.html) (Webbrowser based tool to flash the ESP32 and extract the Log over USB)
- Flash Tool from Espressif
- ESPtool (command-line tool)
- ESPtool (Command Line Tool)
See the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/) for more information.
See the [Docu](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/) for more information.
### Flashing the SD Card
The SD card can be setup automatically after the firmware got installed. See the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#remote-setup-using-the-built-in-access-point) for details. For this to work, the SD card must be FAT formated (which is the default on a new SD card).
Alternatively the SD card still can be setup manually, see the [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#3-sd-card) for details!
### Flashing the SD-Card
The SD-Card must be flashed separately, see the [Docu](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/) for details.
## Casing
Various 3D-printable housing can be found here:
A 3d-printable housing can be found here:
- https://www.thingiverse.com/thing:4573481 (Water Meter)
- https://www.thingiverse.com/thing:5028229 (Power Meter)
- https://www.thingiverse.com/thing:5224101 (Gas Meter)
- https://www.thingiverse.com/thing:4571627 (ESP32-cam housing only)
- https://www.thingiverse.com/thing:4571627 (ESP32-Cam housing only)
## Build it yourself
See [Build Instructions](code/README.md).
## Donate
If you would like to support the developer with a cup of coffee, you can do that via [PayPal](https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL).
If you would like to support the developer with a cup of coffee you can do that via [Paypal](https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL).
<a href="https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL"><img border="0" src="images/paypal.png" width="200px" target="_blank"></a>
## Support
If you have any technical problems please search the [discussions](https://github.com/jomjol/AI-on-the-edge-device/discussions). In case you found a ug or have a feature request, please open an [issue](https://github.com/jomjol/AI-on-the-edge-device/issues).
<form action="https://www.paypal.com/donate" method="post" target="_top">
<input type="hidden" name="hosted_button_id" value="8TRSVYNYKDSWL" />
<input type="image" src="https://www.paypalobjects.com/en_US/DK/i/btn/btn_donateCC_LG.gif" border="0" name="submit" title="PayPal - The safer, easier way to pay online!" alt="Donate with PayPal button" />
<img alt="" border="0" src="https://www.paypal.com/en_DE/i/scr/pixel.gif" width="1" height="1" />
</form>
If you have any technical topics, you can create an [Issue](https://github.com/jomjol/AI-on-the-edge-device/issues).
In other cases you can contact the developer via email: <img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/mail.jpg" height="25">
## Changes and History
See [Changelog](Changelog.md).
## Build It Yourself
See [Build Instructions](code/README.md).
See [Changelog](Changelog.md)
## Tools
* Logfile downloader and combiner (Thx to [reserve85](https://github.com/reserve85))
* Files see ['/tools/logfile-tool'](tbd), how-to see [documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/outdated--Gasmeter-Log-Downloader/)
* Files see ['/tools/logfile-tool'](tbd), How-to see [Docu](https://jomjol.github.io/AI-on-the-edge-device-docs/outdated--Gasmeter-Log-Downloader/)
## Additional Ideas
There are some ideas and feature requests which are not currently being pursued mainly due to capacity reasons on the part of the developers.
They features are collected in the [issues](https://github.com/jomjol/AI-on-the-edge-device/issues) and in [FeatureRequest.md](FeatureRequest.md).
There are some ideas and feature requests which are not followed currently - mainly due to capacity reasons on side of the developer. They are collected here: [FeatureRequest.md](FeatureRequest.md)
------

1
code/.gitignore vendored
View File

@@ -1,5 +1,4 @@
.pio
.idea
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json

View File

@@ -8,15 +8,6 @@ git checkout rolling
git submodule update --init
```
## Update Submodules
```
cd /components/submodule-name (e.g. tflite-micro-example)
git checkout VERSION (e.g. HASH of latest tflite-micro-example build)
cd ../../ (auf Ebene von code)
git submodule update --init
```
Evt. muss man vorher noch einige Verzeichnisse in compenents von Hand löschen, da sie beim checkout nicht gelöscht wurden (vor update -- init)
## Build and Flash within terminal
See further down to build it within an IDE.
### Compile

View File

@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
idf_component_register(SRCS ${app_sources}
INCLUDE_DIRS "." "../../include" "miniz"
REQUIRES vfs esp_http_server app_update esp_http_client nvs_flash jomjol_tfliteclass jomjol_flowcontroll spiffs jomjol_helper jomjol_controlGPIO)
REQUIRES vfs tflite-lib esp_http_server app_update esp_http_client nvs_flash jomjol_tfliteclass jomjol_flowcontroll spiffs jomjol_helper jomjol_controlGPIO)

View File

@@ -588,9 +588,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
/* Read file in chunks into the scratch buffer */
chunksize = fread(chunk, 1, SERVER_FILER_SCRATCH_BUFSIZE, fd);
/* Send buffer contents as HTTP chunk. If empty this functions as a
* last-chunk message, signaling end-of-response, to the HTTP client.
* See RFC 2616, section 3.6.1 for details on Chunked Transfer Encoding. */
/* Send the buffer contents as HTTP response chunk */
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
fclose(fd);
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!");
@@ -608,6 +606,8 @@ static esp_err_t download_get_handler(httpd_req_t *req)
fclose(fd);
ESP_LOGD(TAG, "File successfully sent");
/* Respond with an empty chunk to signal HTTP response completion */
httpd_resp_send_chunk(req, NULL, 0);
return ESP_OK;
}
@@ -717,8 +717,7 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
/* Close file upon upload completion */
fclose(fd);
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "File saved: " + string(filename));
ESP_LOGI(TAG, "File reception completed");
ESP_LOGI(TAG, "File reception complete");
std::string directory = std::string(filepath);
size_t zw = directory.find("/");
@@ -737,27 +736,21 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
// ESP_LOGD(TAG, "Directory danach 2: %s", directory.c_str());
/* Redirect onto root to see the updated file list */
if (strcmp(filename, "/config/config.ini") == 0 ||
strcmp(filename, "/config/ref0.jpg") == 0 ||
strcmp(filename, "/config/ref0_org.jpg") == 0 ||
strcmp(filename, "/config/ref1.jpg") == 0 ||
strcmp(filename, "/config/ref1_org.jpg") == 0 ||
strcmp(filename, "/config/reference.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref0.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref0_org.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref1.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref1_org.jpg") == 0 ||
strcmp(filename, "/img_tmp/reference.jpg") == 0 )
{
httpd_resp_set_status(req, HTTPD_200); // Avoid reloading of folder content
}
else {
httpd_resp_set_status(req, "303 See Other"); // Reload folder content after upload
}
httpd_resp_set_status(req, "303 See Other");
httpd_resp_set_hdr(req, "Location", directory.c_str());
/* Redirect onto root to see the updated file list */
httpd_resp_set_status(req, "303 See Other");
httpd_resp_set_hdr(req, "Location", directory.c_str());
httpd_resp_sendstr(req, "File uploaded successfully");
/*
if (strcmp(filepath, CONFIG_FILE) == 0) {
ESP_LOGD(TAG, "New config found. Reload handler.");
gpio_handler_deinit();
MQTTdestroy();
}
*/
return ESP_OK;
}
@@ -844,15 +837,16 @@ static esp_err_t delete_post_handler(httpd_req_t *req)
return ESP_FAIL;
}
if (stat(filepath, &file_stat) == -1) { // File does not exist
/* This is ok, we would delete it anyway */
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "File does not exist: " + string(filename));
if (stat(filepath, &file_stat) == -1) {
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File does not exist: " + string(filename));
/* Respond with 400 Bad Request */
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File does not exist");
return ESP_FAIL;
}
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Deleting file: " + string(filename));
/* Delete file */
unlink(filepath);
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "File deleted: " + string(filename));
ESP_LOGI(TAG, "File deletion completed");
directory = std::string(filepath);
size_t zw = directory.find("/");
@@ -869,30 +863,16 @@ static esp_err_t delete_post_handler(httpd_req_t *req)
directory = directory.substr(start_fn, found - start_fn + 1);
directory = "/fileserver" + directory;
ESP_LOGD(TAG, "Directory danach 4: %s", directory.c_str());
}
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
/* Redirect onto root to see the updated file list */
if (strcmp(filename, "/config/config.ini") == 0 ||
strcmp(filename, "/config/ref0.jpg") == 0 ||
strcmp(filename, "/config/ref0_org.jpg") == 0 ||
strcmp(filename, "/config/ref1.jpg") == 0 ||
strcmp(filename, "/config/ref1_org.jpg") == 0 ||
strcmp(filename, "/config/reference.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref0.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref0_org.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref1.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref1_org.jpg") == 0 ||
strcmp(filename, "/img_tmp/reference.jpg") == 0 )
{
httpd_resp_set_status(req, HTTPD_200); // Avoid reloading of folder content
}
else {
httpd_resp_set_status(req, "303 See Other"); // Reload folder content after upload
}
}
httpd_resp_set_status(req, "303 See Other");
httpd_resp_set_hdr(req, "Location", directory.c_str());
httpd_resp_sendstr(req, "File successfully deleted");
return ESP_OK;
@@ -949,7 +929,7 @@ std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::st
// Get and print information about each file in the archive.
int numberoffiles = (int)mz_zip_reader_get_num_files(&zip_archive);
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Files to be extracted: " + to_string(numberoffiles));
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Numbers of files to be extracted: " + to_string(numberoffiles));
sort_iter = 0;
{
@@ -1013,7 +993,7 @@ std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::st
string filename_zw = zw + SUFFIX_ZW;
ESP_LOGI(TAG, "File to extract: %s, Temp. Filename: %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);
@@ -1117,7 +1097,7 @@ void unzip(std::string _in_zip_file, std::string _target_directory){
// Save to File.
zw = std::string(archive_filename);
zw = _target_directory + zw;
ESP_LOGD(TAG, "File to extract: %s", zw.c_str());
ESP_LOGD(TAG, "Filename to extract: %s", zw.c_str());
FILE* fpTargetFile = fopen(zw.c_str(), "wb");
fwrite(p, 1, (uint)uncomp_size, fpTargetFile);
fclose(fpTargetFile);

View File

@@ -61,14 +61,8 @@ esp_err_t send_file(httpd_req_t *req, std::string filename)
endsWith(filename, ".jpeg") ||
endsWith(filename, ".ico") ||
endsWith(filename, ".png")) {
if (filename == "/sdcard/html/setup.html") {
httpd_resp_set_hdr(req, "Clear-Site-Data", "\"*\"");
}
else {
httpd_resp_set_hdr(req, "Cache-Control", "max-age=86400");
}
}
set_content_type_from_file(req, filename.c_str());

View File

@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
idf_component_register(SRCS ${app_sources}
INCLUDE_DIRS "."
REQUIRES esp_timer esp_wifi jomjol_tfliteclass jomjol_helper jomjol_controlcamera jomjol_mqtt jomjol_influxdb jomjol_fileserver_ota jomjol_image_proc jomjol_wlan)
REQUIRES esp_timer esp_wifi jomjol_tfliteclass jomjol_helper jomjol_controlcamera jomjol_mqtt jomjol_influxdb jomjol_fileserver_ota jomjol_image_proc jomjol_wlan jomjol_helper)

View File

@@ -37,6 +37,23 @@ ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNTy
imagesRetention = 5;
}
ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, std::string _cnn_name) : ClassFlowImage(NULL, TAG)
{
string cnnmodelfile = "";
modelxsize = 1;
modelysize = 1;
CNNGoodThreshold = 0.0;
ListFlowControll = NULL;
previousElement = NULL;
SaveAllFiles = false;
disabled = false;
isLogImageSelect = false;
CNNType = AutoDetect;
flowpostalignment = _flowalign;
imagesRetention = 5;
cnn_name = _cnn_name;
}
string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution, int prev, float _before_narrow_Analog, float analogDigitalTransitionStart)
{
@@ -55,7 +72,7 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
// LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(analog) number=" + std::to_string(number) + ", result_after_decimal_point=" + std::to_string(result_after_decimal_point) + ", prev=" + std::to_string(prev));
result = std::to_string(prev);
if (_extendedResolution)
if (_extendedResolution && (CNNType != Digital))
result = result + std::to_string(result_after_decimal_point);
for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i)
@@ -143,10 +160,10 @@ int ClassFlowCNNGeneral::PointerEvalHybridNew(float number, float number_of_pred
if (eval_predecessors < 0)
{
// on first digit is no spezial logic for transition needed
// we use the recognition as given. The result is the int value of the recognition
// add precisition of 2 digits and round before trunc
result = (int) ((int) trunc(round((number+10 % 10)*100)) ) / 100;
if ((result_after_decimal_point <= Digital_Uncertainty * 10) || (result_after_decimal_point >= Digital_Uncertainty * 10)) // Band around the digit --> Rounding, as digit reaches inaccuracy in the frame
result = (int) (round(number) + 10) % 10;
else
result = (int) ((int) trunc(number) + 10) % 10;
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - No predecessor - Result = " + std::to_string(result) +
" number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors)+ " eval_predecessors = " + std::to_string(eval_predecessors) + " Digital_Uncertainty = " + std::to_string(Digital_Uncertainty));
@@ -485,7 +502,7 @@ bool ClassFlowCNNGeneral::doFlow(string time)
if (!doAlignAndCut(time)){
return false;
}
};
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "doFlow after alignment");
@@ -853,9 +870,10 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
bool ClassFlowCNNGeneral::isExtendedResolution(int _number)
{
if (CNNType == Digital)
return false;
if (!(CNNType == Digital))
return true;
return false;
}

View File

@@ -25,6 +25,7 @@ protected:
t_CNNType CNNType;
std::vector<general*> GENERAL;
float CNNGoodThreshold;
std::string cnn_name = "ClassFlowCNNGeneral";
//moved to define.h
//float Analog_error = 3.0;
@@ -56,6 +57,7 @@ protected:
public:
ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype = AutoDetect);
ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, std::string _cnn_name);
bool ReadParameter(FILE* pfile, string& aktparamgraph);
bool doFlow(string time);
@@ -81,7 +83,7 @@ public:
t_CNNType getCNNType(){return CNNType;};
string name(){return "ClassFlowCNNGeneral";};
string name(){return "ClassFlowCNNGeneral " + cnn_name;};
};
#endif

View File

@@ -24,6 +24,7 @@ extern "C" {
#include "server_mqtt.h"
#endif //ENABLE_MQTT
#include "websocket.h"
#include "server_help.h"
#include "MainFlowControl.h"
#include "../../include/defines.h"
@@ -85,8 +86,10 @@ std::string ClassFlowControll::TranslateAktstatus(std::string _input)
return ("Take Image");
if (_input.compare("ClassFlowAlignment") == 0)
return ("Aligning");
if (_input.compare("ClassFlowCNNGeneral") == 0)
return ("Digitalization of ROIs");
if (_input.compare("ClassFlowCNNGeneral Digital") == 0)
return ("Digitalization of Digital ROIs");
if (_input.compare("ClassFlowCNNGeneral Analog") == 0)
return ("Digitalization of Analog ROIs");
#ifdef ENABLE_MQTT
if (_input.compare("ClassFlowMQTT") == 0)
return ("Sending MQTT");
@@ -100,7 +103,7 @@ std::string ClassFlowControll::TranslateAktstatus(std::string _input)
if (_input.compare("ClassFlowPostProcessing") == 0)
return ("Post-Processing");
return "Unkown Status";
return "Unkown Status: " + _input +"";
}
@@ -187,6 +190,8 @@ void ClassFlowControll::SetInitialParameter(void)
aktRunNr = 0;
aktstatus = "Flow task not yet created";
aktstatusWithTime = aktstatus;
schedule_websocket_message("{\"state\": \"" + aktstatus + "\"}");
}
@@ -220,12 +225,12 @@ ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
}
if (toUpper(_type).compare("[ANALOG]") == 0)
{
cfc = new ClassFlowCNNGeneral(flowalignment);
cfc = new ClassFlowCNNGeneral(flowalignment, std::string("Analog"));
flowanalog = (ClassFlowCNNGeneral*) cfc;
}
if (toUpper(_type).compare(0, 7, "[DIGITS") == 0)
{
cfc = new ClassFlowCNNGeneral(flowalignment);
cfc = new ClassFlowCNNGeneral(flowalignment, std::string("Digit"));
flowdigit = (ClassFlowCNNGeneral*) cfc;
}
#ifdef ENABLE_MQTT
@@ -269,6 +274,8 @@ void ClassFlowControll::InitFlow(std::string config)
aktstatus = "Initialization";
aktstatusWithTime = aktstatus;
schedule_websocket_message("{\"state\": \"" + aktstatus + "\"}");
//#ifdef ENABLE_MQTT
//MQTTPublish(mqttServer_getMainTopic() + "/" + "status", "Initialization", 1, false); // Right now, not possible -> MQTT Service is going to be started later
//#endif //ENABLE_MQTT
@@ -331,6 +338,8 @@ void ClassFlowControll::setActStatus(std::string _aktstatus)
{
aktstatus = _aktstatus;
aktstatusWithTime = aktstatus;
schedule_websocket_message("{\"state\": \"" + aktstatus + "\"}");
}
@@ -348,6 +357,8 @@ void ClassFlowControll::doFlowTakeImageOnly(string time)
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, 1, false);
#endif //ENABLE_MQTT
schedule_websocket_message("{\"state\": \"" + aktstatus + "\"}");
FlowControll[i]->doFlow(time);
}
}
@@ -379,6 +390,7 @@ bool ClassFlowControll::doFlow(string time)
aktstatus = TranslateAktstatus(FlowControll[i]->name());
aktstatusWithTime = aktstatus + " (" + zw_time + ")";
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Status: " + aktstatusWithTime);
schedule_websocket_message("{\"state\": \"" + aktstatus + "\"}");
#ifdef ENABLE_MQTT
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, qos, false);
#endif //ENABLE_MQTT
@@ -413,6 +425,7 @@ bool ClassFlowControll::doFlow(string time)
aktstatus = "Flow finished";
aktstatusWithTime = aktstatus + " (" + zw_time + ")";
//LogFile.WriteToFile(ESP_LOG_INFO, TAG, aktstatusWithTime);
schedule_websocket_message("{\"state\": \"" + aktstatus + "\"}");
#ifdef ENABLE_MQTT
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, qos, false);
#endif //ENABLE_MQTT

View File

@@ -34,14 +34,13 @@ protected:
bool AutoStart;
float AutoInterval;
bool SetupModeActive;
void SetInitialParameter(void);
std::string aktstatusWithTime;
std::string aktstatus;
int aktRunNr;
public:
bool SetupModeActive;
void InitFlow(std::string config);
bool doFlow(string time);
void doFlowTakeImageOnly(string time);

View File

@@ -20,7 +20,7 @@ static const char* TAG = "INFLUXDBV2";
void ClassFlowInfluxDBv2::SetInitialParameter(void)
{
uri = "";
bucket = "";
database = "";
dborg = "";
dbtoken = "";
// dbfield = "";
@@ -109,9 +109,9 @@ bool ClassFlowInfluxDBv2::ReadParameter(FILE* pfile, string& aktparamgraph)
{
handleMeasurement(splitted[0], splitted[1]);
}
if (((toUpper(splitted[0]) == "BUCKET")) && (splitted.size() > 1))
if (((toUpper(splitted[0]) == "DATABASE")) && (splitted.size() > 1))
{
this->bucket = splitted[1];
this->database = splitted[1];
}
}
@@ -119,11 +119,11 @@ bool ClassFlowInfluxDBv2::ReadParameter(FILE* pfile, string& aktparamgraph)
printf("org: %s\n", dborg.c_str());
printf("token: %s\n", dbtoken.c_str());
if ((uri.length() > 0) && (bucket.length() > 0) && (dbtoken.length() > 0) && (dborg.length() > 0))
if ((uri.length() > 0) && (database.length() > 0) && (dbtoken.length() > 0) && (dborg.length() > 0))
{
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDB with uri: " + uri + ", org: " + dborg + ", token: *****");
// printf("vor V2 Init\n");
InfluxDB_V2_Init(uri, bucket, dborg, dbtoken);
InfluxDB_V2_Init(uri, database, dborg, dbtoken);
// printf("nach V2 Init\n");
InfluxDBenable = true;
} else {

View File

@@ -15,7 +15,7 @@ class ClassFlowInfluxDBv2 :
public ClassFlow
{
protected:
std::string uri, bucket;
std::string uri, database;
std::string dborg, dbtoken, dbfield;
std::string OldValue;
ClassFlowPostProcessing* flowpostprocessing;

View File

@@ -9,6 +9,7 @@
#include "ClassLogFile.h"
#include "time_sntp.h"
#include "websocket.h"
#include "interface_mqtt.h"
#include "ClassFlowPostProcessing.h"
#include "ClassFlowControll.h"
@@ -37,9 +38,6 @@ void ClassFlowMQTT::SetInitialParameter(void)
topicUptime = "";
topicFreeMem = "";
caCertFilename = "";
clientCertFilename = "";
clientKeyFilename = "";
clientname = wlan_config.hostname;
OldValue = "";
@@ -105,18 +103,6 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
{
splitted = ZerlegeZeile(aktparamgraph);
if ((toUpper(splitted[0]) == "CACERT") && (splitted.size() > 1))
{
this->caCertFilename = splitted[1];
}
if ((toUpper(splitted[0]) == "CLIENTCERT") && (splitted.size() > 1))
{
this->clientCertFilename = splitted[1];
}
if ((toUpper(splitted[0]) == "CLIENTKEY") && (splitted.size() > 1))
{
this->clientKeyFilename = splitted[1];
}
if ((toUpper(splitted[0]) == "USER") && (splitted.size() > 1))
{
this->user = splitted[1];
@@ -211,8 +197,7 @@ bool ClassFlowMQTT::Start(float AutoInterval)
mqttServer_setParameter(flowpostprocessing->GetNumbers(), keepAlive, roundInterval);
bool MQTTConfigCheck = MQTT_Configure(uri, clientname, user, password, maintopic, LWT_TOPIC, LWT_CONNECTED,
LWT_DISCONNECTED, caCertFilename, clientCertFilename, clientKeyFilename,
keepAlive, SetRetainFlag, (void *)&GotConnected);
LWT_DISCONNECTED, keepAlive, SetRetainFlag, (void *)&GotConnected);
if (!MQTTConfigCheck) {
return false;
@@ -265,11 +250,15 @@ bool ClassFlowMQTT::doFlow(string zwtime)
namenumber = maintopic + "/" + namenumber + "/";
if (result.length() > 0)
if (result.length() > 0) {
success |= MQTTPublish(namenumber + "value", result, qos, SetRetainFlag);
schedule_websocket_message("{\"value\": \"" + result + "\", \"number\": \"" + (*NUMBERS)[i]->name + "\"}");
}
if (resulterror.length() > 0)
if (resulterror.length() > 0) {
success |= MQTTPublish(namenumber + "error", resulterror, qos, SetRetainFlag);
schedule_websocket_message("{\"error\": \"" + resulterror + "\", \"number\": \"" + (*NUMBERS)[i]->name + "\"}");
}
if (resultrate.length() > 0) {
success |= MQTTPublish(namenumber + "rate", resultrate, qos, SetRetainFlag);
@@ -289,8 +278,10 @@ bool ClassFlowMQTT::doFlow(string zwtime)
success |= MQTTPublish(namenumber + "rate_per_digitalization_round", resultchangabs, qos, SetRetainFlag);
}
if (resultraw.length() > 0)
if (resultraw.length() > 0) {
success |= MQTTPublish(namenumber + "raw", resultraw, qos, SetRetainFlag);
schedule_websocket_message("{\"raw\": \"" + resultraw + "\", \"number\": \"" + (*NUMBERS)[i]->name + "\"}");
}
if (resulttimestamp.length() > 0)
success |= MQTTPublish(namenumber + "timestamp", resulttimestamp, qos, SetRetainFlag);

View File

@@ -19,7 +19,6 @@ protected:
std::string OldValue;
ClassFlowPostProcessing* flowpostprocessing;
std::string user, password;
std::string caCertFilename, clientCertFilename, clientKeyFilename;
bool SetRetainFlag;
int keepAlive; // Seconds
float roundInterval; // Minutes

View File

@@ -8,6 +8,7 @@
#include <time.h>
#include "websocket.h"
#include "time_sntp.h"
#include "esp_log.h"
@@ -866,12 +867,12 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handleAllowNegativeRate for device: " + NUMBERS[j]->name);
if ((NUMBERS[j]->Value < NUMBERS[j]->PreValue))
{
// more debug if extended resolution is on, see #2447
if (NUMBERS[j]->isExtendedResolution) {
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Neg: value=" + std::to_string(NUMBERS[j]->Value)
+ ", preValue=" + std::to_string(NUMBERS[j]->PreValue)
+ ", preToll=" + std::to_string(NUMBERS[j]->PreValue-(2/pow(10, NUMBERS[j]->Nachkomma))));
}
#ifdef SERIAL_DEBUG
ESP_LOGD(TAG, "Neg: value=%f, preValue=%f, preToll%f", NUMBERS[j]->Value, NUMBERS[j]->PreValue,
NUMBERS[j]->PreValue-(2/pow(10, NUMBERS[j]->Nachkomma))
) ;
#endif
// Include inaccuracy of 0.2 for isExtendedResolution.
if (NUMBERS[j]->Value >= (NUMBERS[j]->PreValue-(2/pow(10, NUMBERS[j]->Nachkomma))) && NUMBERS[j]->isExtendedResolution) {
NUMBERS[j]->Value = NUMBERS[j]->PreValue;
@@ -882,6 +883,8 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
NUMBERS[j]->ReturnValue = "";
NUMBERS[j]->lastvalue = imagetime;
schedule_websocket_message("{\"status\": \"" + NUMBERS[j]->ErrorMessageText + "\", \"number\": \"" + NUMBERS[j]->name + "\"}");
string _zw = NUMBERS[j]->name + ": Raw: " + NUMBERS[j]->ReturnRawValue + ", Value: " + NUMBERS[j]->ReturnValue + ", Status: " + NUMBERS[j]->ErrorMessageText;
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, _zw);
WriteDataLog(j);
@@ -915,6 +918,8 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
NUMBERS[j]->ReturnRateValue = "";
NUMBERS[j]->lastvalue = imagetime;
schedule_websocket_message("{\"status\": \"" + NUMBERS[j]->ErrorMessageText + "\", \"number\": \"" + NUMBERS[j]->name + "\"}");
string _zw = NUMBERS[j]->name + ": Raw: " + NUMBERS[j]->ReturnRawValue + ", Value: " + NUMBERS[j]->ReturnValue + ", Status: " + NUMBERS[j]->ErrorMessageText;
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, _zw);
WriteDataLog(j);

View File

@@ -13,6 +13,7 @@
#include "Helper.h"
#include "statusled.h"
#include "websocket.h"
#include "esp_camera.h"
#include "time_sntp.h"
#include "ClassControllCamera.h"
@@ -230,7 +231,7 @@ esp_err_t handler_stream(httpd_req_t *req)
if (httpd_query_key_value(_query, "flashlight", _value, 10) == ESP_OK)
{
#ifdef DEBUG_DETAIL_ON
ESP_LOGD(TAG, "flashlight is found%s", _value);
ESP_LOGD(TAG, "flashlight is found%s", _size);
#endif
if (strlen(_value) > 0)
flashlightOn = true;
@@ -453,7 +454,7 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req)
else {
/* Digital ROIs */
txt = "<body style=\"font-family: arial\">";
txt += "<hr><h3>Recognized Digit ROIs (previous round)</h3>\n";
txt += "<h3>Recognized Digit ROIs (previous round)</h3>\n";
txt += "<table style=\"border-spacing: 5px\"><tr style=\"text-align: center; vertical-align: top;\">\n";
std::vector<HTMLInfo*> htmlinfodig;
@@ -488,7 +489,7 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req)
/* Analog ROIs */
txt = "<hr><h3>Recognized Analog ROIs (previous round)</h3>\n";
txt = "<h3>Recognized Analog ROIs (previous round)</h3>\n";
txt += "<table style=\"border-spacing: 5px\"><tr style=\"text-align: center; vertical-align: top;\">\n";
std::vector<HTMLInfo*> htmlinfoana;
@@ -510,7 +511,7 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req)
/* Full Image
* Only show it after the image got taken and aligned */
txt = "<hr><h3>Aligned Image (current round)</h3>\n";
txt = "<h3>Aligned Image (current round)</h3>\n";
if ((*status == std::string("Initialization")) ||
(*status == std::string("Initialization (delayed)")) ||
(*status == std::string("Take Image"))) {
@@ -657,7 +658,14 @@ esp_err_t handler_editflow(httpd_req_t *req)
string out2 = out.substr(0, out.length() - 4) + "_org.jpg";
if ((flowctrl.SetupModeActive || (*flowctrl.getActStatus() == "Flow finished")) && psram_init_shared_memory_for_take_image_step()) {
std::string state = *flowctrl.getActStatus();
/* To be able to provide the image, several conditions must be met due to the shared PSRAM usage:
- Ether the round most be completed or not started yet
- Or we must be in Setup Mode
- Additionally, the initialization of the shared PSRAM must be successful */
if (((state == "Flow finished") || (state == "Initialization") || (state == "Initialization (delayed)") || isSetupModusActive()) &&
psram_init_shared_memory_for_take_image_step()) {
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Taking image for Alignment Mark Update...");
CAlignAndCutImage *caic = new CAlignAndCutImage("cutref", in);
@@ -680,7 +688,7 @@ esp_err_t handler_editflow(httpd_req_t *req)
}
else {
LogFile.WriteToFile(ESP_LOG_WARN, TAG, std::string("Taking image for Alignment Mark not possible while device") +
" is busy with a round (Current State: '" + *flowctrl.getActStatus() + "')!");
" is busy with a round (Current State: '" + state + "')!");
zw = "Device Busy";
}
@@ -890,7 +898,7 @@ esp_err_t handler_prevalue(httpd_req_t *req)
if (httpd_query_key_value(_query, "value", _value, 20) == ESP_OK) {
#ifdef DEBUG_DETAIL_ON
ESP_LOGD(TAG, "Value: %s", _value);
ESP_LOGD(TAG, "Value: %s", _size);
#endif
}
}
@@ -978,6 +986,10 @@ void task_autodoFlow(void *pvParameter)
{
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "----------------------------------------------------------------"); // Clear separation between runs
std::string _zw = "Round #" + std::to_string(++countRounds) + " started";
schedule_websocket_message("{\"round\": \"" + std::to_string(countRounds) + "\"}");
schedule_websocket_message("{\"uptime\": \"" + std::to_string(getUpTime()) + "\"}");
time_t roundStartTime = getUpTime();
LogFile.WriteToFile(ESP_LOG_INFO, TAG, _zw);
fr_start = esp_timer_get_time();
@@ -1005,12 +1017,15 @@ void task_autodoFlow(void *pvParameter)
// Round finished -> Logfile
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Round #" + std::to_string(countRounds) +
" completed (" + std::to_string(getUpTime() - roundStartTime) + " seconds)");
schedule_websocket_message("{\"round duration\": \"" + std::to_string(getUpTime() - roundStartTime) + "\"}");
// CPU Temp -> Logfile
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CPU Temperature: " + std::to_string((int)temperatureRead()) + "°C");
schedule_websocket_message("{\"cpu temperature\": \"" + std::to_string(temperatureRead()) + "\"}");
// WIFI Signal Strength (RSSI) -> Logfile
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "WIFI Signal (RSSI): " + std::to_string(get_WIFI_RSSI()) + "dBm");
schedule_websocket_message("{\"wifi rssi\": \"" + std::to_string(get_WIFI_RSSI()) + "\"}");
// Check if time is synchronized (if NTP is configured)
if (getUseNtp() && !getTimeIsSet()) {

View File

@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
idf_component_register(SRCS ${app_sources}
INCLUDE_DIRS "."
REQUIRES esp_timer tflite-lib jomjol_logfile fatfs sdmmc)
REQUIRES esp_timer tflite-lib jomjol_logfile fatfs sdmmc esp_http_server)

View File

@@ -0,0 +1,129 @@
#include "esp_log.h"
#include <esp_http_server.h>
#include "../../include/defines.h"
#include "ClassLogFile.h"
#include "freertos/ringbuf.h"
#include "psram.h"
#include "websocket.h"
#define MAX_MESSAGE_LENGTH 100
static const char *TAG = "WEBSOCKET";
static httpd_handle_t server = NULL;
static httpd_handle_t websocket_handle = NULL;
/*
* Structure holding server handle and message
* in order to use out of request send */
struct async_resp_arg {
httpd_handle_t hd;
char message[MAX_MESSAGE_LENGTH];
};
/*
* async send function, which we put into the httpd work queue
*/
static void websocket_send_pending_message(void *arg) {
esp_err_t ret;
struct async_resp_arg *resp_arg = (struct async_resp_arg *)arg;
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Sending Websocket message: '" + std::string(resp_arg->message) + "'");
httpd_ws_frame_t ws_pkt;
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
ws_pkt.payload = (uint8_t *)resp_arg->message;
ws_pkt.len = strlen(resp_arg->message);
ws_pkt.type = HTTPD_WS_TYPE_TEXT;
static size_t max_clients = CONFIG_LWIP_MAX_LISTENING_TCP;
size_t fds = max_clients;
int client_fds[max_clients];
ret = httpd_get_client_list(server, &fds, client_fds);
if (ret != ESP_OK) {
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to get Websocket client ist: " + std::to_string(ret) + "!");
free_psram_heap("websocket msg", resp_arg);
return;
}
/* Send it to all websocket clients */
for (int i = 0; i < fds; i++) {
int client_info = httpd_ws_get_fd_info(server, client_fds[i]);
if (client_info == HTTPD_WS_CLIENT_WEBSOCKET) {
httpd_ws_send_frame_async(websocket_handle, client_fds[i], &ws_pkt);
}
}
free_psram_heap("websocket msg", resp_arg);
}
esp_err_t schedule_websocket_message(std::string message) {
// return 0;
esp_err_t ret;
if (websocket_handle == NULL) { // No websocket connecten open
return ESP_OK;
}
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Scheduled websocket message: '" + message + "'");
struct async_resp_arg *resp_arg = (struct async_resp_arg *)malloc_psram_heap("websocket msg",
sizeof(struct async_resp_arg), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
if (resp_arg == NULL) {
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to malloc memory for scheduled websocket message!");
return ESP_ERR_NO_MEM;
}
strncpy(resp_arg->message, message.c_str(), MAX_MESSAGE_LENGTH);
ret = httpd_queue_work(websocket_handle, websocket_send_pending_message, resp_arg);
if (ret != ESP_OK) {
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Websocket Scheduling failed: " + std::to_string(ret) + "!");
free_psram_heap("websocket msg", resp_arg);
}
return ret;
}
static esp_err_t ws_handler(httpd_req_t *req) {
if (req->method == HTTP_GET) {
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Handshake done, the new websocket connection was opened");
websocket_handle = req->handle;
}
return ESP_OK;
}
static const httpd_uri_t ws_uri = {
.uri = "/ws",
.method = HTTP_GET,
.handler = ws_handler,
.user_ctx = NULL,
.is_websocket = true
};
esp_err_t start_websocket_server(httpd_handle_t _server) {
esp_err_t ret;
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init Websocket Server");
server = _server;
// Registering the ws handler
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Registering URI handler");
ret = httpd_register_uri_handler(server, &ws_uri);
if (ret != ESP_OK) {
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Registering Websocket URI handler failed: " + std::to_string(ret));
}
return ret;
}

View File

@@ -0,0 +1,10 @@
#ifndef WEBSOCKET_H
#define WEBSOCKET_H
#include <esp_http_server.h>
esp_err_t start_websocket_server(httpd_handle_t server);
esp_err_t schedule_websocket_message(std::string message);
#endif // WEBSOCKET_H

View File

@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
idf_component_register(SRCS ${app_sources}
INCLUDE_DIRS "."
REQUIRES esp_http_client jomjol_logfile)
REQUIRES tflite-lib esp_http_client jomjol_logfile)

View File

@@ -16,16 +16,16 @@ std::string _influxDBUser;
std::string _influxDBPassword;
std::string _influxDB_V2_URI;
std::string _influxDB_V2_Bucket;
std::string _influxDB_V2_Database;
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 _bucket, std::string _org, std::string _token)
void InfluxDB_V2_Init(std::string _uri, std::string _database, std::string _org, std::string _token)
{
_influxDB_V2_URI = _uri;
_influxDB_V2_Bucket = _bucket;
_influxDB_V2_Database = _database;
_influxDB_V2_Org = _org;
_influxDB_V2_Token = _token;
}
@@ -49,16 +49,20 @@ void InfluxDB_V2_Publish(std::string _measurement, std::string _key, std::string
if (_timestamp.length() > 0)
{
struct tm tm;
time_t t;
time(&t);
localtime_r(&t, &tm); // Extract DST setting from actual time to consider it for timestamp evaluation
strptime(_timestamp.c_str(), PREVALUE_TIME_FORMAT_OUTPUT, &tm);
t = mktime(&tm);
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Timestamp: " + _timestamp + ", Timestamp (UTC): " + std::to_string(t));
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);
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Use handover timestamp: " + _timestamp + " converted GMT timestamp: " + std::to_string(t));
// utc = 2*t - utc; // Take care of timezone (looks difficult, but is easy: t = t + (t - utc), weil t-utc = timezone)
// LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "time conversion utc after: " + std::to_string(utc));
sprintf(nowTimestamp,"%ld000000000", (long) t); // UTC
payload = _measurement + " " + _key + "=" + _content + " " + nowTimestamp;
}
else
@@ -70,7 +74,7 @@ void InfluxDB_V2_Publish(std::string _measurement, std::string _key, std::string
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_Bucket;
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
@@ -112,7 +116,7 @@ static esp_err_t http_event_handler(esp_http_client_event_t *evt)
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Error encountered");
break;
case HTTP_EVENT_ON_CONNECTED:
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client connected");
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Error encountered");
ESP_LOGI(TAG, "HTTP Client Connected");
break;
case HTTP_EVENT_HEADERS_SENT:
@@ -161,16 +165,20 @@ void InfluxDBPublish(std::string _measurement, std::string _key, std::string _co
if (_timestamp.length() > 0)
{
struct tm tm;
time_t t;
time(&t);
localtime_r(&t, &tm); // Extract DST setting from actual time to consider it for timestamp evaluation
strptime(_timestamp.c_str(), PREVALUE_TIME_FORMAT_OUTPUT, &tm);
t = mktime(&tm);
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Timestamp: " + _timestamp + ", Timestamp (UTC): " + std::to_string(t));
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);
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Use handover timestamp: " + _timestamp + " converted GMT timestamp: " + std::to_string(t));
// utc = 2*t - utc; // Take care of timezone (looks difficult, but is easy: t = t + (t - utc), weil t-utc = timezone)
// LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "time conversion utc after: " + std::to_string(utc));
sprintf(nowTimestamp,"%ld000000000", (long) t); // UTC
payload = _measurement + " " + _key + "=" + _content + " " + nowTimestamp;
}
else
@@ -183,7 +191,7 @@ void InfluxDBPublish(std::string _measurement, std::string _key, std::string _co
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload);
// use the default retention policy of the bucket
// use the default retention policy of the database
std::string apiURI = _influxDBURI + "/write?db=" + _influxDBDatabase;
// std::string apiURI = _influxDBURI + "/api/v2/write?bucket=" + _influxDBDatabase + "/";

View File

@@ -13,7 +13,7 @@ void InfluxDBInit(std::string _influxDBURI, std::string _database, std::string _
void InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, std::string _timestamp);
// Interface to InfluxDB v2.x
void InfluxDB_V2_Init(std::string _uri, std::string _bucket, std::string _org, std::string _token);
void InfluxDB_V2_Init(std::string _uri, std::string _database, std::string _org, std::string _token);
void InfluxDB_V2_Publish(std::string _measurement, std::string _key, std::string _content, std::string _timestamp);

View File

@@ -2,9 +2,6 @@
#include "interface_mqtt.h"
#include "esp_log.h"
#if DEBUG_DETAIL_ON
#include "esp_timer.h"
#endif
#include "connect_wlan.h"
#include "mqtt_client.h"
#include "ClassLogFile.h"
@@ -12,11 +9,6 @@
#include "cJSON.h"
#include "../../include/defines.h"
#if DEBUG_DETAIL_ON
#include "esp_timer.h"
#endif
static const char *TAG = "MQTT IF";
std::map<std::string, std::function<void()>>* connectFunktionMap = NULL;
@@ -35,7 +27,6 @@ bool mqtt_connected = false;
esp_mqtt_client_handle_t client = NULL;
std::string uri, client_id, lwt_topic, lwt_connected, lwt_disconnected, user, password, maintopic;
std::string caCert, clientCert, clientKey;
int keepalive;
bool SetRetainFlag;
void (*callbackOnConnected)(std::string, bool) = NULL;
@@ -173,10 +164,6 @@ static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) {
else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_NOT_AUTHORIZED) {
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, not authorized. Check username/password (0x05)");
}
else {
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Other event id:" + event->error_handle->connect_return_code);
ESP_LOGE(TAG, "Other event id:%d", event->error_handle->connect_return_code);
}
#ifdef DEBUG_DETAIL_ON
ESP_LOGD(TAG, "MQTT_EVENT_ERROR - esp_mqtt_error_codes:");
@@ -206,7 +193,6 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password,
std::string _maintopic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected,
std::string _cacertfilename, std::string _clientcertfilename, std::string _clientkeyfilename,
int _keepalive, bool _SetRetainFlag, void *_callbackOnConnected) {
if ((_mqttURI.length() == 0) || (_maintopic.length() == 0) || (_clientid.length() == 0))
{
@@ -224,25 +210,6 @@ bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _us
maintopic = _maintopic;
callbackOnConnected = ( void (*)(std::string, bool) )(_callbackOnConnected);
if (_clientcertfilename.length() && _clientkeyfilename.length()){
std::ifstream cert_ifs(_clientcertfilename);
std::string cert_content((std::istreambuf_iterator<char>(cert_ifs)), (std::istreambuf_iterator<char>()));
clientCert = cert_content;
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using clientCert: " + _clientcertfilename);
std::ifstream key_ifs(_clientkeyfilename);
std::string key_content((std::istreambuf_iterator<char>(key_ifs)), (std::istreambuf_iterator<char>()));
clientKey = key_content;
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using clientKey: " + _clientkeyfilename);
}
if (_cacertfilename.length() ){
std::ifstream ifs(_cacertfilename);
std::string content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
caCert = content;
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using caCert: " + _cacertfilename);
}
if (_user.length() && _password.length()){
user = _user;
password = _password;
@@ -296,20 +263,6 @@ int MQTT_Init() {
mqtt_cfg.session.keepalive = keepalive;
mqtt_cfg.buffer.size = 1536; // size of MQTT send/receive buffer (Default: 1024)
if (caCert.length()){
mqtt_cfg.broker.verification.certificate = caCert.c_str();
mqtt_cfg.broker.verification.certificate_len = caCert.length() + 1;
mqtt_cfg.broker.verification.skip_cert_common_name_check = true;
}
if (clientCert.length() && clientKey.length()){
mqtt_cfg.credentials.authentication.certificate = clientCert.c_str();
mqtt_cfg.credentials.authentication.certificate_len = clientCert.length() + 1;
mqtt_cfg.credentials.authentication.key = clientKey.c_str();
mqtt_cfg.credentials.authentication.key_len = clientKey.length() + 1;
}
if (user.length() && password.length()){
mqtt_cfg.credentials.username = user.c_str();
mqtt_cfg.credentials.authentication.password = password.c_str();
@@ -413,11 +366,9 @@ bool mqtt_handler_set_prevalue(std::string _topic, char* _data, int _data_len)
if (cJSON_IsNumber(value)) { // Check if value is a number
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_set_prevalue called: numbersname: " + std::string(numbersname->valuestring) +
", value: " + std::to_string(value->valuedouble));
if (flowctrl.UpdatePrevalue(std::to_string(value->valuedouble), std::string(numbersname->valuestring), true)) {
cJSON_Delete(jsonData);
if (flowctrl.UpdatePrevalue(std::to_string(value->valuedouble), std::string(numbersname->valuestring), true))
return ESP_OK;
}
}
else {
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: value not a valid number (\"value\": 12345.12345)");
}
@@ -425,7 +376,6 @@ bool mqtt_handler_set_prevalue(std::string _topic, char* _data, int _data_len)
else {
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: numbersname not a valid string (\"numbersname\": \"main\")");
}
cJSON_Delete(jsonData);
}
else {
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: handler called, but no data received");

View File

@@ -11,7 +11,6 @@
bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password,
std::string _maintopic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected,
std::string _cacertfilename, std::string _clientcertfilename, std::string _clientkeyfilename,
int _keepalive, bool SetRetainFlag, void *callbackOnConnected);
int MQTT_Init();
void MQTTdestroy_client(bool _disable);

View File

@@ -12,31 +12,6 @@
static const char *TAG = "TFLITE";
/// Static Resolver muss mit allen Operatoren geladen Werden, die benöägit werden - ABER nur 1x --> gesonderte Funktion /////////////////////////////
static bool MakeStaticResolverDone = false;
static tflite::MicroMutableOpResolver<15> resolver;
void MakeStaticResolver()
{
if (MakeStaticResolverDone)
return;
MakeStaticResolverDone = true;
resolver.AddFullyConnected();
resolver.AddReshape();
resolver.AddSoftmax();
resolver.AddConv2D();
resolver.AddMaxPool2D();
resolver.AddQuantize();
resolver.AddMul();
resolver.AddAdd();
resolver.AddLeakyRelu();
resolver.AddDequantize();
}
////////////////////////////////////////////////////////////////////////////////////////
float CTfLiteClass::GetOutputValue(int nr)
{
TfLiteTensor* output2 = this->interpreter->output(0);
@@ -204,20 +179,16 @@ bool CTfLiteClass::LoadInputImageBasis(CImageBasis *rs)
}
bool CTfLiteClass::MakeAllocate()
{
MakeStaticResolver();
static tflite::AllOpsResolver resolver;
#ifdef DEBUG_DETAIL_ON
LogFile.WriteHeapInfo("CTLiteClass::Alloc start");
#endif
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CTfLiteClass::MakeAllocate");
this->interpreter = new tflite::MicroInterpreter(this->model, resolver, this->tensor_arena, this->kTensorArenaSize);
// this->interpreter = new tflite::MicroInterpreter(this->model, resolver, this->tensor_arena, this->kTensorArenaSize, this->error_reporter);
this->interpreter = new tflite::MicroInterpreter(this->model, resolver, this->tensor_arena, this->kTensorArenaSize, this->error_reporter);
if (this->interpreter)
{
@@ -314,7 +285,6 @@ bool CTfLiteClass::ReadFileToModel(std::string _fn)
bool CTfLiteClass::LoadModel(std::string _fn)
{
#ifdef SUPRESS_TFLITE_ERRORS
// this->error_reporter = new tflite::ErrorReporter;
this->error_reporter = new tflite::OwnMicroErrorReporter;
#else
this->error_reporter = new tflite::MicroErrorReporter;
@@ -350,21 +320,16 @@ CTfLiteClass::CTfLiteClass()
CTfLiteClass::~CTfLiteClass()
{
delete this->interpreter;
// delete this->error_reporter;
delete this->error_reporter;
psram_free_shared_tensor_arena_and_model_memory();
}
#ifdef SUPRESS_TFLITE_ERRORS
namespace tflite
{
//tflite::ErrorReporter
// int OwnMicroErrorReporter::Report(const char* format, va_list args)
int OwnMicroErrorReporter::Report(const char* format, va_list args)
{
return 0;
}
}
#endif

View File

@@ -3,12 +3,8 @@
#ifndef CTFLITECLASS_H
#define CTFLITECLASS_H
#include "tensorflow/lite/micro/micro_mutable_op_resolver.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/micro/micro_mutable_op_resolver.h"
#include "tensorflow/lite/micro/kernels/micro_ops.h"
#include "tensorflow/lite/micro/tflite_bridge/micro_error_reporter.h"
#include "tensorflow/lite/micro/all_ops_resolver.h"
#include "tensorflow/lite/micro/micro_error_reporter.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/micro/kernels/micro_ops.h"
@@ -17,8 +13,6 @@
#include "CImageBasis.h"
#ifdef SUPRESS_TFLITE_ERRORS
#include "tensorflow/lite/core/api/error_reporter.h"
#include "tensorflow/lite/micro/compatibility.h"
@@ -33,7 +27,6 @@ namespace tflite {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#endif
class CTfLiteClass
{
protected:
@@ -41,6 +34,7 @@ class CTfLiteClass
const tflite::Model* model;
tflite::MicroInterpreter* interpreter;
TfLiteTensor* output = nullptr;
static tflite::AllOpsResolver resolver;
int kTensorArenaSize;
uint8_t *tensor_arena;
@@ -74,6 +68,4 @@ class CTfLiteClass
int ReadInputDimenstion(int _dim);
};
void MakeStaticResolver();
#endif //CTFLITECLASS_H

View File

@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
idf_component_register(SRCS ${app_sources}
INCLUDE_DIRS "."
REQUIRES tflite-lib jomjol_logfile jomjol_configfile)
REQUIRES tflite-lib jomjol_logfile jomjol_configfile jomjol_helper)

View File

@@ -13,6 +13,7 @@
#include "esp_sntp.h"
#include "../../include/defines.h"
#include "websocket.h"
#include "ClassLogFile.h"
#include "configFile.h"
@@ -68,6 +69,8 @@ void time_sync_notification_cb(struct timeval *tv)
}
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Time is synced with NTP Server " +
getServerName() + ": " + getCurrentTimeString("%Y-%m-%d %H:%M:%S"));
schedule_websocket_message("{\"ntp\": \"synchronized\"}");
}

View File

@@ -174,12 +174,10 @@ int LoadWlanFromFile(std::string fn)
}
/* Check if password is empty (mandatory parameter) */
/* Disabled see issue #2393
if (wlan_config.password.empty()) {
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Password empty. Device init aborted!");
return -2;
}
*/
return 0;
}

View File

@@ -1,9 +1,3 @@
dependencies:
idf:
component_hash: null
source:
type: idf
version: 5.0.2
manifest_hash: f880feca80f04921fc95fd31e9c2936b9896764c15a62f6e2d312c57a62a36db
manifest_hash: 63f5c6c9f0bcebc7b9ca12d2aa8b26b2c5f5218d377dc4b2375d9b9ca1df7815
target: esp32
version: 1.0.0

View File

@@ -173,7 +173,7 @@
fprintf(stderr, "Error at %s:%d\n", __FILE__, __LINE__); \
exit(1); \
}
// #define SUPRESS_TFLITE_ERRORS // use, to avoid error messages from TFLITE
#define SUPRESS_TFLITE_ERRORS // use, to avoid error messages from TFLITE
// connect_wlan.cpp

View File

@@ -36,6 +36,7 @@
#include "MainFlowControl.h"
#include "server_file.h"
#include "server_ota.h"
#include "websocket.h"
#include "time_sntp.h"
#include "configFile.h"
//#include "ClassControllCamera.h"
@@ -492,6 +493,7 @@ extern "C" void app_main(void)
ESP_LOGD(TAG, "starting servers");
server = start_webserver();
start_websocket_server(server);
register_server_camera_uri(server);
register_server_main_flow_task_uri(server);
register_server_file_uri(server, "/sdcard");
@@ -642,6 +644,7 @@ void migrateConfiguration(void) {
/* Fieldname has a <NUMBER> as prefix! */
if (isInString(configLines[i], "Fieldname")) { // It is the parameter "Fieldname"
migrated = migrated | replaceString(configLines[i], "Fieldname", "Field"); // Rename it to Field
migrated = migrated | replaceString(configLines[i], ";", ""); // Enable it
}
}
@@ -649,10 +652,7 @@ void migrateConfiguration(void) {
/* Fieldname has a <NUMBER> as prefix! */
if (isInString(configLines[i], "Fieldname")) { // It is the parameter "Fieldname"
migrated = migrated | replaceString(configLines[i], "Fieldname", "Field"); // Rename it to Field
}
/* Database got renamed to Bucket! */
if (isInString(configLines[i], "Database")) { // It is the parameter "Database"
migrated = migrated | replaceString(configLines[i], "Database", "Bucket"); // Rename it to Bucket
migrated = migrated | replaceString(configLines[i], ";", ""); // Enable it
}
}
@@ -723,7 +723,6 @@ std::vector<std::string> splitString(const std::string& str) {
}
/*bool replace_all(std::string& s, std::string const& toReplace, std::string const& replaceWith) {
std::string buf;
std::size_t pos = 0;

View File

@@ -39,20 +39,16 @@ esp_err_t info_get_handler(httpd_req_t *req)
char _valuechar[30];
std::string _task;
if (httpd_req_get_url_query_str(req, _query, 200) != ESP_OK)
if (httpd_req_get_url_query_str(req, _query, 200) == ESP_OK)
{
return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "invalid query string");
}
ESP_LOGD(TAG, "Query: %s", _query);
if (httpd_query_key_value(_query, "type", _valuechar, 30) != ESP_OK)
if (httpd_query_key_value(_query, "type", _valuechar, 30) == ESP_OK)
{
return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "missing or invalid 'type' query parameter (too long value?)");
}
ESP_LOGD(TAG, "type is found: %s", _valuechar);
_task = std::string(_valuechar);
}
}
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
@@ -170,12 +166,6 @@ esp_err_t info_get_handler(httpd_req_t *req)
httpd_resp_sendstr(req, zw.c_str());
return ESP_OK;
}
else
{
char formatted[256];
snprintf(formatted, sizeof(formatted), "Unknown value for parameter info 'type': '%s'\n", _task.c_str());
return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, formatted);
}
return ESP_OK;
}
@@ -437,7 +427,7 @@ httpd_handle_t start_webserver(void)
config.server_port = 80;
config.ctrl_port = 32768;
config.max_open_sockets = 5; //20210921 --> previously 7
config.max_uri_handlers = 39; // previously 24, 20220511: 35, 20221220: 37, 2023-01-02:38
config.max_uri_handlers = 45; // previously 24, 20220511: 35, 20221220: 37, 2023-01-02:38, 20230430 (adding websocket): 45
config.max_resp_headers = 8;
config.backlog_conn = 5;
config.lru_purge_enable = true; // this cuts old connections if new ones are needed.

View File

@@ -19,7 +19,7 @@
[common:esp32-idf]
extends = common:idf
platform = platformio/espressif32 @ 6.3.2
platform = platformio/espressif32 @ 6.2.0
framework = espidf
lib_deps =
${common:idf.lib_deps}

View File

@@ -109,11 +109,7 @@ CONFIG_ESP_INT_WDT_TIMEOUT_MS=300
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
CONFIG_HTTPD_PURGE_BUF_LEN=16
<<<<<<< Updated upstream
=======
CONFIG_HTTPD_WS_SUPPORT=y
CONFIG_LWIP_MAX_SOCKETS=12
>>>>>>> Stashed changes
CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=16
CONFIG_ESP32_WIFI_CACHE_TX_BUFFER_NUM=16

BIN
docs/binary/firmware.bin Normal file

Binary file not shown.

View File

@@ -18,18 +18,15 @@
For further information about AI-on-the-edge-device please go to <a href=https://github.com/jomjol/AI-on-the-edge-device target=_blank>https://github.com/jomjol/AI-on-the-edge-device</a>.</p>
<h2>Notes:</h2>
<p>Notes:</p>
<ul>
<li>The Webinstall will install the latest firmware (Version <b>$VERSION</b>).</li>
<li>For the installation, make sure to switch the ESP32 to Bootloader mode by keeping the <b>FLASH</b> button pressed while the <b>RESET</b> button gets relesed. if there is no <b>FLASH</b> button, you need to pull <b>GPIO0</b> low!</li>
<li>For the installation, make sure to switch the ESP32 to Bootloader mode!</li>
<li>After the installation, a manual reset might be required!</li>
<li>Please note that not all web browsers and operating systems support the necessary USB access needed for this Webinstaller!</li>
<li>Please note that not all webbrowsers and operating systems support the needed access to USB!</li>
<li>Check the <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Installation target=_blank>documentation</a> for additional information.</li>
<li>The SD card can be setup automatically after the firmware got installed. See the <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#remote-setup-using-the-built-in-access-point target=_blank>documentation</a> for details. For this to work, the SD card must be FAT formated (which is the default on a new SD card).
Alternatively the SD card still can be setup manually, see the <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#3-sd-card target=_blank>documentation</a> for details!</li>
</ul>
<li>The SD-Card still must be setup separately. This can be done manually or using the new <b>Remote Setup</b>. See the <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#3-sd-card target=_blank>documentation</a> for further instructions!</li>
<hr>
</ul>
<p><esp-web-install-button manifest="manifest.json"></esp-web-install-button></p>
<hr>

View File

@@ -1,6 +1,6 @@
{
"name": "AI-on-the-edge",
"version": "$VERSION",
"version": "13.0.8",
"funding_url": "https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL",
"new_install_prompt_erase": false,
"builds": [

View File

@@ -0,0 +1,25 @@
{
"name": "AI-on-the-edge",
"version": "VERSION",
"funding_url": "https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL",
"new_install_prompt_erase": false,
"builds": [
{
"chipFamily": "ESP32",
"parts": [
{
"path": "binary/bootloader.bin",
"offset": 4096
},
{
"path": "binary/partitions.bin",
"offset": 32768
},
{
"path": "binary/firmware.bin",
"offset": 65536
}
]
}
]
}

6
firmware/README.md Normal file
View File

@@ -0,0 +1,6 @@
# Firmware
The firmware got moved to the [Release page](https://github.com/jomjol/AI-on-the-edge-device/releases).
# Installation Guide
You find the complete installation guide at https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -12,7 +12,7 @@ FixedExposure = false
Demo = false
[Alignment]
InitialRotate = 0.0
InitialRotate = 179
InitialMirror = false
SearchFieldX = 20
SearchFieldY = 20
@@ -22,7 +22,7 @@ FlipImageSize = false
/config/ref1.jpg 442 142
[Digits]
Model = /config/dig-cont_0620_s3_q.tflite
Model = /config/dig-cont_0611_s3_q.tflite
CNNGoodThreshold = 0.5
;ROIImagesLocation = /log/digit
;ROIImagesRetention = 3
@@ -31,7 +31,7 @@ main.dig2 343 126 30 54 false
main.dig3 391 126 30 54 false
[Analog]
Model = /config/ana-cont_1208_s2_q.tflite
Model = /config/ana-cont_1105_s2_q.tflite
CNNGoodThreshold = 0.5
;ROIImagesLocation = /log/analog
;ROIImagesRetention = 3
@@ -62,9 +62,6 @@ CheckDigitIncreaseConsistency = false
RetainMessages = false
HomeassistantDiscovery = false
;MeterType = other
;CACert = /config/certs/RootCA.pem
;ClientCert = /config/certs/client.pem.crt
;ClientKey = /config/certs/client.pem.key
;[InfluxDB]
;Uri = undefined
@@ -75,7 +72,7 @@ HomeassistantDiscovery = false
;[InfluxDBv2]
;Uri = undefined
;Bucket = undefined
;Database = undefined
;Measurement = undefined
;Org = undefined
;Token = undefined
@@ -109,6 +106,6 @@ LogfilesRetention = 3
TimeZone = CET-1CEST,M3.5.0,M10.5.0/3
;TimeServer = pool.ntp.org
;Hostname = undefined
RSSIThreshold = -75
;RSSIThreshold = 0
CPUFrequency = 160
SetupMode = true

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -1,41 +1,42 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<html>
<head>
<title>Backup/Restore Configuration</title>
<meta charset="UTF-8" />
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
<title>Backup/Restore Configuration</title>
<meta charset="utf-8">
<style>
h1 {font-size: 2em;}
h2 {font-size: 1.5em; margin-block-start: 0.0em; margin-block-end: 0.2em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
<style>
h1 {font-size: 2em;}
h2 {font-size: 1.5em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
input[type=number] {
input[type=number] {
width: 138px;
padding: 10px 5px;
display: inline-block;
border: 1px solid #ccc;
font-size: 16px;
}
}
.button {
padding: 5px 10px;
width: 205px;
.button {
padding: 10px 20px;
width: 211px;
font-size: 16px;
}
</style>
}
</style>
</head>
<body style="font-family: arial; padding: 0px 10px;">
<h2>Backup Configuration</h2>
<p>With the following action the <a href="/fileserver/config/" target="_self">config</a> folder on the SD-card gets zipped and provided as a download.</p>
<h2>Backup Configuration</h2>
<p>With the following action the <a href="/fileserver/config/" target="_self">config</a> folder on the SD-card gets zipped and provided as a download.</p>
<button class="button" id="startBackup" type="button" onclick="startBackup()">Create Backup</button>
<p id=progress></p>
<hr>
<h2>Restore Configuration</h2>
<p>Use the <a href="/fileserver/config/" target="_self">File Server</a> to upload individual files.</p>
<button class="button" id="startBackup" type="button" onclick="startBackup()">Create Config backup</button>
<p id=progress></p>
<hr>
<h2>Restore Configuration</h2>
<p>Use the <a href="/fileserver/config/" target="_self">File Server</a> to upload individual files.</p>
</body>

View File

@@ -1,17 +1,18 @@
/* 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!
* Keep empty to disable using it. Enabling it will break access through a forwared port, see
* https://github.com/jomjol/AI-on-the-edge-device/issues/2681 */
var domainname_for_testing = "";
//var domainname_for_testing = "192.168.1.151";
* And you also might have to disable CORS in your webbrowser! */
var domainname_for_testing = "192.168.178.23";
/* Returns the domainname with prepended protocol.
Eg. http://watermeter.fritz.box or http://192.168.1.5 */
function getDomainname(){
var host = window.location.hostname;
if (domainname_for_testing != "") {
if (((host == "127.0.0.1") || (host == "localhost") || (host == ""))
// && ((window.location.port == "80") || (window.location.port == ""))
)
{
console.log("Using pre-defined domainname for testing: " + domainname_for_testing);
domainname = "http://" + domainname_for_testing
}
@@ -138,8 +139,8 @@ function compareVersions() {
console.log("FW Hash: " + fWGitHash + ", Web UI Hash: " + webUiHash);
if (fWGitHash != webUiHash) {
firework.launch("The version of the web interface (" + webUiHash +
") does not match the firmware version (" +
firework.launch("The Version of the Web Interface (" + webUiHash +
") does not match the Firmware Version (" +
fWGitHash + ")! It is suggested to keep them on the same version!", 'warning', 30000);
}
}

View File

@@ -1,26 +1,17 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<html>
<head>
<title>Data Viewer</title>
<meta charset="UTF-8" />
<style>
h1 {font-size: 2em;}
h2 {font-size: 1.5em; margin-block-start: 0.0em; margin-block-end: 0.2em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
html,
body {
height: 100%;
margin: 1px;
margin: 2px;
font-family: Arial, Helvetica, sans-serif;
}
.box {
display: flex;
flex-flow: column;
height: 99.75%;
height: 100%;
}
.box .row.header {
@@ -39,31 +30,26 @@
font-family: 'Courier New', Courier, monospace;
font-size: small;
}
.button {
padding: 5px 10px;
width: 160px;
font-size: 16px;
}
</style>
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
</head>
<body>
<h2>Data Viewer</h2>
<h4>Today's latest data</h4>
<h3>Todays Data</h3>
<h4>Last part of Todays Data</h4>
<div class="box">
<div class="row header">
<button class="button" onClick="reload();">Refresh</button>
<button class="button" onClick="window.open(getDomainname() + '/datafileact');">Show Full File</button>
<button class="button" onClick="window.location.href = getDomainname() + '/fileserver/log/data/'">Show Data Files</button>
<button class="button" onClick="window.location.href = 'graph.html?v=$COMMIT_HASH'">Show Graph</button>
<button onClick="reload();">Refresh</button>
<button onClick="window.open(getDomainname() + '/datafileact');">Show Full File</button>
<button onClick="window.location.href = getDomainname() + '/fileserver/log/data/'">Show Data Files</button>
<button onClick="window.location.href = 'graph.html?v=$COMMIT_HASH'">Show Graph</button>
</div>
<div class="row content" id="data"><br><br><br><b>Loading data file, please wait...</b></div>
<div class="row content" id="data"><br><br><br><b>Loading Data file, please wait...</b></div>
<div class="row footer">
<button class="button" onClick="reload();">Refresh</button>
<button class="button" onClick="window.open(getDomainname() + '/datafileact');">Show Full File</button>
<button class="button" onClick="window.location.href = getDomainname() + '/fileserver/log/data/'">Show Data Files</button>
<button class="button" onClick="window.location.href = 'graph.html?v=$COMMIT_HASH'">Show Graph</button>
<button onClick="reload();">Refresh</button>
<button onClick="window.open(getDomainname() + '/datafileact');">Show Full File</button>
<button onClick="window.location.href = getDomainname() + '/fileserver/log/data/'">Show Data Files</button>
<button onClick="window.location.href = 'graph.html?v=$COMMIT_HASH'">Show Graph</button>
<p></p>
</div>
</div>
</body>

View File

@@ -1,135 +1,101 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<html>
<head>
<title>Alignment marker</title>
<meta charset="UTF-8" />
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
<title>Alignment Marks</title>
<meta charset="utf-8"/>
<style>
h1 {font-size: 2em;}
h2 {font-size: 1.5em; margin-block-start: 0.0em; margin-block-end: 0.2em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
<style>
h1 {font-size: 2em;}
h2 {font-size: 1.5em; margin-block-start: 0.0em; margin-block-end: 0.2em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
input[type=number] {
width: 60px;
input[type=number] {
width: 100px;
margin-right: 10px;
padding: 3px 5px;
display: inline-block;
border: 1px solid #ccc;
font-size: 16px;
}
}
input[type=text] {
input[type=text] {
padding: 3px 5px;
display: inline-block;
border: 1px solid #ccc;
font-size: 16px;
}
}
input:out-of-range {
background-color: rgba(255, 0, 0, 0.25);
border: 1px solid red;
}
select {
select {
padding: 3px 5px;
display: inline-block;
border: 1px solid #ccc;
font-size: 16px;
margin-right: 10px;
min-width: 100px;
vertical-align: middle;
}
}
.button {
.button {
padding: 5px 10px;
width: 205px;
width: 210px;
font-size: 16px;
}
th, td {
padding: 5px 5px 5px 0px;
}
table {
width: 660px;
padding: 5px;
}
</style>
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
<script type="text/javascript" src="jquery-3.6.0.min.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="firework.js?v=$COMMIT_HASH"></script>
}
</style>
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
<script type="text/javascript" src="jquery-3.6.0.min.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="firework.js?v=$COMMIT_HASH"></script>
</head>
<body style="font-family: arial; padding: 0px 10px;">
<h2>Alignment Marker</h2>
<details id="desc_details" style="font-size:16px">
<summary><b>CLICK HERE</b> for usage description. More infos in documentation:
<a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Alignment/ target=_blank>Alignment</a>
</summary>
<p>
Two alignment marker with clear contour and proper contrast are needed to identify unique "fix points" on the image.
The marker area should be not be part of the variable area of ROI evaluation. Please find more information in documenation:
<a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Alignment/ target=_blank>Alignment</a>
</p>
<p>
Select an alignment marker area using drag and dop feature by mouse operation or by manually entering the coordinates and sizes in the fields below the image.
After you selected a suitable first alignment marker area, push the <b>"Update Marker"</b> button. Switch to second alignment marker with <b>"Marker"</b>
and repeat the procedure.
</p>
<p>
After definition of both alignment marker is completed don't forget to save with the <b>"Save New Marker"</b> button!<br>
<b>NOTE:</b> There is no need to perform a reboot after every saving or step. It's sufficient to reboot after all configuration steps
(reference image, alignment, ROI configuration) are completed to activate new configuration.
</p>
</details>
<hr />
<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>
<colgroup>
<col span="1" style="width: 33.3%;">
<col span="1" style="width: 33.3%;">
<col span="1" style="width: 33.3%;">
</colgroup>
<tr>
<td>Marker:
<td><canvas id="canvas" crossorigin></canvas></td>
</tr>
</table>
<table>
<tr>
<td>Select Reference:
<select id="index" name="reference" onchange="ChangeSelection()">
<option value="0" selected>Marker 1</option>
<option value="1" >Marker 2</option>
<option value="0" selected>Reference 0</option>
<option value="1" >Reference 1</option>
</select>
</td>
<td colspan="2" style="padding-left: 22px; color: grey;">Filename: <output type="text" name="name" id="name" onchange="namechanged()"></td>
<td colspan="2">Storage Path/Name: <input type="text" name="name" id="name" onchange="namechanged()"></td>
</tr>
<tr>
<td style="padding-top: 10px">x: <input type="number" name="refx" id="refx" step=1 onchange="valuemanualchanged()"></td>
<td style="padding-top: 10px">dx: <input type="number" name="refdx" id="refdx" step=1 onchange="valuemanualchanged()"></td>
<td rowspan="2" style="padding-top: 10px;"><input class="button" type="button" value="Update Marker" onclick="CutOutReference()"></td>
<td rowspan="2" style="padding-top: 10px"><input class="button" type="button" value="Update Reference Image" onclick="CutOutReference()"></td>
</tr>
<tr>
<td>y: <input type="number" name="refy" id="refy" step=1 onchange="valuemanualchanged()"></td>
<td>dy: <input type="number" name="refdy" id="refdy" step=1 onchange="valuemanualchanged()"></td>
</tr>
<tr>
<td style="vertical-align: bottom;">Selected Image Area:</td>
<td style="vertical-align: bottom;">Resulting Marker:</td>
<td><input class="button" type="button" id="enhancecontrast" value="Enhance Image Contrast" onclick="EnhanceContrast()"></td>
<td style="padding-top: 10px">Original Image:</td>
<td style="padding-top: 10px">Reference Image:</td>
<td rowspan="2"><input class="button" type="button" id="enhancecontrast" value="Enhance Contrast" onclick="EnhanceContrast()"></td>
</tr>
<tr>
<td style="height:70px; vertical-align: top;"><img id="img_ref_org" src = ""></td> <!--/img_tmp/ref_zw_org.jpg-->
<td style="height:70px; vertical-align: top;"><img id="img_ref" src = ""></td> <!--/img_tmp/ref_zw.jpg-->
</tr>
<tr>
<td style="vertical-align:bottom;"><b>Reference Image:</b></td>
<td></td>
<td><input style="font-weight:bold;" class="button" type="submit" name="saveroi" id="savemarker" onclick="SaveToConfig()" value="Save New Marker">
</td>
</tr>
<tr>
<td colspan="3"><canvas id="canvas" crossorigin></canvas></td>
<td><img id="img_ref_org" src = "/img_tmp/ref_zw_org.jpg"></td>
<td><img id="img_ref" src = "/img_tmp/ref_zw.jpg"></td>
</tr>
</table>
<table>
<tr>
<td><input class="button" type="submit" name="saveroi" onclick="SaveToConfig()" value="Save">
<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>
</table>
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="readconfigcommon.js?v=$COMMIT_HASH"></script>
@@ -164,15 +130,10 @@ function ChangeSelection(){
}
function SaveToConfig(){
if (confirm("Are you sure you want to save the new alignment marker configuration?")) {
WriteConfigININew();
UpdateConfigReference(domainname)
SaveConfigToServer(domainname);
document.getElementById("savemarker").disabled = true;
document.getElementById("enhancecontrast").disabled = true;
firework.launch('Alignment marker saved. They will get applied after next reboot', 'success', 5000);
}
firework.launch('Reference Marks got updated. The change will get applied after the next reboot!', 'success', 5000);
}
function EnhanceContrast(){
@@ -185,7 +146,6 @@ function EnhanceContrast(){
enhanceCon = true;
if (MakeContrastImageZW(refInfo[aktindex], enhanceCon, domainname)) {
UpdateReference();
document.getElementById("enhancecontrast").disabled = true;
}
}
@@ -214,6 +174,7 @@ function UpdateReference(){
document.getElementById("refy").value = refInfo[aktindex]["y"];
rect.startX = document.getElementById("refx").value;
rect.startY = document.getElementById("refy").value;
document.getElementById("enhancecontrast").disabled = true;
draw();
}
@@ -255,26 +216,13 @@ function dataURLtoBlob(dataurl) {
}
/* hash #description open the details part of the page */
function openDescription() {
if(window.location.hash) {
var hash = window.location.hash.substring(1); //Puts hash in variable, and removes the # character
if(hash == 'description')
document.getElementById("desc_details").open = true;
}
}
function init() {
openDescription();
domainname = getDomainname();
loadConfig(domainname);
ParseConfig();
param = getConfigParameters();
document.getElementById("savemarker").disabled = true;
document.getElementById("enhancecontrast").disabled = true;
canvas.addEventListener('mousedown', mouseDown, false);
canvas.addEventListener('mouseup', mouseUp, false);
canvas.addEventListener('mousemove', mouseMove, false);
@@ -287,8 +235,6 @@ function dataURLtoBlob(dataurl) {
drawImage();
}
function drawImage(){
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
@@ -308,7 +254,6 @@ function dataURLtoBlob(dataurl) {
if (MakeRefZW(refInfo[aktindex], domainname)) {
UpdateReference();
document.getElementById("enhancecontrast").disabled = false;
document.getElementById("savemarker").disabled = false;
}
}

View File

@@ -1,219 +1,140 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<html>
<head>
<meta charset="UTF-8" />
<title>Analog ROI</title>
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
<meta charset="utf-8"/>
<title>Analog ROI's</title>
<style>
h1 {font-size: 2em;}
h2 {font-size: 1.5em; margin-block-start: 0.0em; margin-block-end: 0.2em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
<style>
h1 {font-size: 2em;}
h2 {font-size: 1.5em; margin-block-start: 0.0em; margin-block-end: 0.2em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
input[type=number] {
width: 60px;
input[type=number] {
width: 100px;
margin-right: 10px;
padding: 3px 5px;
display: inline-block;
border: 1px solid #ccc;
font-size: 16px;
}
}
input[type=text] {
input[type=text] {
padding: 3px 5px;
display: inline-block;
border: 1px solid #ccc;
font-size: 16px;
}
}
input:out-of-range {
background-color: rgba(255, 0, 0, 0.25);
border: 1px solid red;
}
select {
select {
padding: 3px 5px;
display: inline-block;
border: 1px solid #ccc;
font-size: 16px;
margin-right: 10px;
min-width: 100px;
max-width: 100%;
vertical-align: middle;
overflow: hidden;
}
}
.button {
.button {
padding: 5px 10px;
width: 160px;
width: 210px;
font-size: 16px;
}
}
.multiplier {
padding: 0px 0px;
.move {
padding: 4px 4px;
width: 100px;
font-size: 12px;
}
}
th, td {
th, td {
padding: 5px 5px 5px 0px;
}
}
#div2{
#div2{
background-color:#777;
margin-bottom:20px;
}
.disabledDiv {
}
.disabledDiv {
pointer-events: none;
opacity: 0.4;
}
table {
width: 660px;
padding: 5px;
table-layout: fixed;
}
</style>
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
<script type="text/javascript" src="jquery-3.6.0.min.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="firework.js?v=$COMMIT_HASH"></script>
}
</style>
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
<script type="text/javascript" src="jquery-3.6.0.min.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="firework.js?v=$COMMIT_HASH"></script>
</head>
<body style="font-family: arial; padding: 0px 10px;">
<h2>Analog ROI</h2>
<details id="desc_details" style="font-size:16px">
<summary><b>CLICK HERE</b> for usage description. More infos in documentation:
<a href=https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/ target=_blank>ROI Configuration</a>
</summary>
<p>
<b>R</b>egion <b>O</b>f <b>I</b>nterest (ROI) for analog pointer counter can be defined on this page. If no analog pointer counter need to be
processed, disable analog pointer counter processing by deselecting <b>"Analog ROI Processing"</b>.
</p>
<p>
By default one number sequence (a number seqence contains of 1-x digit ROIs + 1-x analog counter ROIs which are processed together) is
predefined and already selected in the drop down <b>"Number Sequence"</b>. If you need more than one number sequence additional
one's can be added with the buttons next to the drop down. Each number sequence will be processed separately.
</p>
<p>
Using drag and drop by mouse of by manually entering the parameters into the given fields the analog ROIs can be positined to the analog pointer
counters on the reference image. To have proper ROI definition please check the documentation:
<a href=https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/ target=_blank>ROI Configuration</a>. It's very important to be
really precise to have reliable processing. With the drop down <b>"ROI"</b> you can change between the different ROIs in the selected
number sequence. To create new ROIs use <b>"New ROI"</b>.
</p>
<p>
The order of the ROIs defines the position (and therefore the multiplication factor) within the reading sequence. The position
in the number sequence can be changed with the buttons <b>"Move ROI Lower"</b> and <b>"Move ROI Higher"</b>. The multiplication factor which is
shown below the ROI drop down is the multiplication factor of pure position/order in number sequence and the factor right-hand side to this is
the additionally corrected by decimal shift setting (configuration, expert parameter, default: 0).
</p>
<p>
After definition of digit ROIs is completed don't forget to save with the <b>"Save Config"</b> button!<br>
<b>NOTE:</b> There is no need to perform a reboot after every saving or step. It's sufficient to reboot after all configuration steps
(reference image, alignment, ROI configuration) are completed to activate new configuration.
</p>
</details>
<hr />
<p>
<input type="checkbox" id="Category_Analog_enabled" value="1" onclick = 'EnDisableAnalog()' checked >
<label style="font-weight: bold; font-size: larger;" for="Category_Analog_enabled">Analog ROI Processing</label>
</p>
<h2>Analog ROI's</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">
<table>
<tr>
<td><canvas id="canvas" crossorigin></canvas></td>
</tr>
</table>
<p>
<table>
<tr>
<class id="Numbers_text" style="color:black;"><b>Number: </b></class>
<select id="Numbers_value1" onchange="numberChanged()">
</select>
<input class="move" type="submit" id="renameNumber" name="renameNumber" onclick="renameNumber()" value="Rename">
<input class="move" type="submit" id="newNumber" name="newNumber" onclick="newNumber()" value="New">
<input class="move" type="submit" id="removeNumber" name="removeNumber" onclick="removeNumber()" value="Remove">
</tr>
</table>
</p>
<table>
<colgroup>
<col span="1" style="width: 22%;">
<col span="1" style="width: 26%;">
<col span="1" style="width: 26%;">
<col span="1" style="width: 26%;">
</colgroup>
<tr>
<td colspan="4" style="padding: 0px"><class id="Numbers_text" style="color:black;">Number Sequence:</class></td>
<td><input class="button" type="submit" id= "newROI" name="newROI" onclick="newROI()" value="New ROI (after current)"></td>
<td><input class="button" type="submit" id= "deleteROI" name="deleteROI" onclick="deleteROI()" value="Delete ROI"></td>
<td></td>
</tr>
<tr>
<td>
<select id="Numbers_value1" onchange="numberChanged()">
<select id="index" name="index" onchange="ChangeSelection()" tabindex=1>
</select>
</td>
<td><input class="button" type="submit" id="newNumber" name="newNumber" onclick="newNumber()" value="New Sequence"></td>
<td><input class="button" type="submit" id="renameNumber" name="renameNumber" onclick="renameNumber()" value="Rename Sequence"></td>
<td><input class="button" type="submit" id="removeNumber" name="removeNumber" onclick="removeNumber()" value="Delete Sequence"></td>
</tr>
</table>
<hr />
<table>
<colgroup>
<col span="1" style="width: 22%;">
<col span="1" style="width: 26%;">
<col span="1" style="width: 26%;">
<col span="1" style="width: 26%;">
</colgroup>
<tr>
<td style="padding: 0px">ROI:</td>
</tr>
<tr>
<td><select id="index" name="index" onchange="ChangeSelection()" tabindex=1></select></td>
<td><input class="button" type="submit" id="newROI" name="newROI" onclick="newROI()" value="New ROI"></td>
<td><input class="button" type="submit" id="renameROI" name="renameROI" onclick="renameROI()" value="Rename ROI"></td>
<td><input class="button" type="submit" id="deleteROI" name="deleteROI" onclick="deleteROI()" value="Delete ROI"></td>
</tr>
<tr>
<td class="multiplier">Multiplier: <output type="text" id="multiplier" name="multiplier"></output><br>
(only based on order)
<td>
<input class="button" type="submit" id="renameROI" name="renameROI" onclick="renameROI()" value="Rename">
</td>
<td class="multiplier">Multiplier: <output type="text" id="multiplier_decshift" name="multiplier_decshift"></output><br>
(order + decimal shift: <output type="text" id="decimalShift" name="decimalShift"></output>)
<td>
<input class="move" type="submit" id="moveNext" onclick="moveNext()" value="move Next">
<input class="move" type="submit" id="movePrevious" onclick="movePrevious()" value="move Previous">
</td>
<td><input class="button" type="submit" id="movePrevious" onclick="movePrevious()" value="Move ROI Higher"></td>
<td><input class="button" type="submit" id="moveNext" onclick="moveNext()" value="Move ROI Lower"></td>
</tr>
</table>
<table>
<colgroup>
<col span="1" style="width: 18%;">
<col span="1" style="width: 18%;">
<col span="1" style="width: 64%;">
</colgroup>
<tr>
<td>x: <input type="number" name="refx" id="refx" step=1 onchange="valuemanualchanged()" tabindex=2></td>
<td>Δx: <input type="number" name="refdx" id="refdx" step=1 onchange="valuemanualchangeddx()" tabindex=4></td>
<td><input type="checkbox" id="lockAspectRatio" name="lockAspectRatio" value="1" onclick="changelockAspectRatio()" checked tabindex=6><label for="lockAspectRatio"> Lock aspect ratio </label></td>
<td><input type="checkbox" id="lockAspectRatio" name="lockAspectRatio" value="1" onclick="changelockAspectRatio()" checked tabindex=6><label for="lockAspectRatio"> Lock aspect ratio</label></td>
</tr>
<tr>
<td>y: <input type="number" name="refy" id="refy" step=1 onchange="valuemanualchanged()" tabindex=3></td>
<td>Δy: <input type="number" name="refdy" id="refdy" step=1 onchange="valuemanualchanged()" tabindex=5></td>
<td><input type="checkbox" id="lockSizes" name="lockSizes" value="1" onclick="changelockSizes()" checked tabindex=7><label for="lockSizes"> Synchronize y, Δx and Δy between ROIs</label></td>
<td><input type="checkbox" id="lockSizes" name="lockSizes" value="1" onclick="changelockSizes()" checked tabindex=7><label for="lockSizes"> Synchronize Δx and Δy between ROIs</label></td>
</tr>
<tr>
<td colspan="2"></td>
<td><input type="checkbox" id="CCW" name="CCW" value="0" onclick="changeCCW()" unchecked tabindex=8><label for="CCW">Counter clockwise rotation (CCW)</label></td>
<td><input type="checkbox" id="CCW" name="CCW" value="0" onclick="changeCCW()" unchecked tabindex=8><label for="CCW"> Counter-Clockwise Rotation: </label></td>
</tr>
</table>
</div>
<table>
<colgroup>
<col span="1" style="width: 22%;">
<col span="1" style="width: 26%;">
<col span="1" style="width: 26%;">
<col span="1" style="width: 26%;">
</colgroup>
<tr>
<td colspan="3" style="vertical-align: bottom;"><b>Reference Image:</b></td>
<!---<td><button class="button" id="reboot" type="button" onclick="doReboot()">Reboot device</button></td>-->
<td><input style="font-weight:bold;" class="button" type="submit" id="saveroi" name="saveroi" onclick="SaveToConfig()" value="Save Config" tabindex=10></td>
<td><input class="button" type="submit" id="saveroi" name="saveroi" onclick="SaveToConfig()" value="Save" tabindex=9>
</tr>
<tr>
<td colspan="4"><canvas id="canvas" crossorigin></canvas></td>
<td><button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate the changes</button></td>
</tr>
</table>
@@ -237,7 +158,8 @@
lockSizes = false;
domainname = getDomainname();
function doReboot() {
function doReboot() {
if (confirm("Are you sure you want to reboot? Did you save your changes?")) {
var stringota = getDomainname() + "/reboot";
window.location = stringota;
@@ -245,7 +167,7 @@
window.location.assign(stringota);
window.location.replace(stringota);
}
}
}
function EnDisableAnalog() {
isEnabled = document.getElementById("Category_Analog_enabled").checked;
@@ -264,8 +186,8 @@ function EnDisableAnalog() {
sah1(document.getElementById("div1"), !isEnabled);
cofcat["Analog"]["enabled"] = isEnabled;
document.getElementById("saveroi").disabled = false;
if (isEnabled)
{
@@ -290,10 +212,6 @@ function onNameChange(){
}
function deleteROI(){
if (!confirm("Delete the selected ROI?")) {
return; //break out of the function early because prompt was aborted
}
ROIInfo.splice(aktindex, 1);
if (aktindex > ROIInfo.length - 1){
aktindex = ROIInfo.length - 1;
@@ -302,21 +220,18 @@ function deleteROI(){
draw();
}
function newROI() {
function newROI(){
var sel = document.getElementById("Numbers_value1");
var _number= sel.options[sel.selectedIndex].text;
sel = document.getElementById("index");
var _roialt= sel.options[sel.selectedIndex].text;
var _roinew = prompt("Please enter a name for the new ROI", "name");
if (_roinew === null) {
return; //break out of the function early because prompt was aborted
}
var _roinew = prompt("Please enter name of new ROI", "name");
if (ROIInfo.length > 0)
erg = CreateROI(_number, "analog", sel.selectedIndex, _roinew, 15, 30, ROIInfo[aktindex]["dx"], ROIInfo[aktindex]["dy"], ROIInfo[aktindex]["CCW"]=="true");
erg = CreateROI(_number, "analog", sel.selectedIndex, _roinew, 1, 1, ROIInfo[aktindex]["dx"], ROIInfo[aktindex]["dy"], ROIInfo[aktindex]["CCW"]=="true");
else
erg = CreateROI(_number, "analog", sel.selectedIndex, _roinew, 15, 30, 30, 30, false);
erg = CreateROI(_number, "analog", sel.selectedIndex, _roinew, 1, 1, 30, 30, false);
if (erg != "")
firework.launch(erg, 'danger', 30000);
@@ -360,9 +275,7 @@ function changeCCW(){
ROIInfo[aktindex]["CCW"] = "true";
else
ROIInfo[aktindex]["CCW"] = "false";
UpdateROIs();
}
function ChangeSelection(){
@@ -372,68 +285,11 @@ function ChangeSelection(){
}
function SaveToConfig(){
if (confirm("Are you sure you want to save the new analog ROI configuration?")) {
//_zwcat = getConfigCategory();
// _zwcat = getConfigCategory();
cofcat["Analog"]["enabled"] = document.getElementById("Category_Analog_enabled").checked;
WriteConfigININew();
SaveConfigToServer(domainname);
UpdateROIs();
document.getElementById("saveroi").disabled = true;
firework.launch('Configuration saved. It will get applied after next reboot', 'success', 5000);
}
}
function ShowMultiplier()
{
var decimalShift = 0;
var multiplier;
var multiplier_decshift;
var fixedDecimals;
var fixedDecimals_decshift;
var NumberInfo = getNUMBERInfo();
var CategoryInfo = getConfigCategory();
var sel = document.getElementById("Numbers_value1");
var _number= sel.options[sel.selectedIndex].text;
document.getElementById("decimalShift").value = 0;
for (var i = 0; i < NumberInfo.length; ++i) {
if (NumberInfo[i]["name"] == _number) {
if (NumberInfo[i]["PostProcessing"]["DecimalShift"]["enabled"]) {
decimalShift = NumberInfo[i]["PostProcessing"]["DecimalShift"]["value1"];
document.getElementById("decimalShift").value=decimalShift;
}
}
}
if (CategoryInfo["Analog"]["enabled"] == true && CategoryInfo["Digits"]["enabled"] == true) { // Digit + Analog
multiplier = fixedDecimals = aktindex + 1;
multiplier_decshift = fixedDecimals_decshift = multiplier - Number(decimalShift);
if (multiplier < 0)
fixedDecimals = 0;
if (multiplier_decshift < 0)
fixedDecimals_decshift = 0;
document.getElementById("multiplier").value="x" + Number(10 ** (-1*multiplier)).toFixed(fixedDecimals);
document.getElementById("multiplier_decshift").value="x" + Number(10 ** (-1*multiplier_decshift)).toFixed(fixedDecimals_decshift);
}
else if (CategoryInfo["Analog"]["enabled"] == true && CategoryInfo["Digits"]["enabled"] == false) { // Only Analog
multiplier = ROIInfo.length - 1 - aktindex;
multiplier_decshift = fixedDecimals_decshift = multiplier + Number(decimalShift);
if (multiplier_decshift > 0)
fixedDecimals_decshift = 0;
if (fixedDecimals_decshift < 0) {
fixedDecimals_decshift = -1 * fixedDecimals_decshift;
}
document.getElementById("multiplier").value="x" + Number(10 ** multiplier).toFixed(0);
document.getElementById("multiplier_decshift").value="x" + Number(10 ** multiplier_decshift).toFixed(fixedDecimals_decshift);
}
firework.launch('Configuration got updated. It will get applied after the next reboot!', 'success', 5000);
}
@@ -449,28 +305,19 @@ function UpdateROIs(_sel){
{
document.getElementById("Category_Analog_enabled").checked = false;
EnDisableAnalog();
firework.launch('Analog ROI processing is disabled. Activate with checkbox if needed', 'warning', 10000);
firework.launch('Analog ROIs are disabled - please enable first (Check box top left)', 'warning', 10000);
return;
}
if (ROIInfo.length == 0){
firework.launch('No analog ROIs defined in selected number sequence', 'warning', 10000);
firework.launch('There are no ROIs defined. Please first create a new ROI ("New ROIs ...")', 'danger', 10000);
document.getElementById("newROI").disabled = false;
document.getElementById("deleteROI").disabled = true;
document.getElementById("renameROI").disabled = true;
document.getElementById("index").disabled = true;
document.getElementById("multiplier").style.display = "none";
document.getElementById("multiplier_decshift").style.display = "none";
document.getElementById("refx").disabled = true;
document.getElementById("refdx").disabled = true;
document.getElementById("refy").disabled = true;
document.getElementById("refdy").disabled = true;
document.getElementById("lockSizes").disabled = true;
document.getElementById("lockAspectRatio").disabled = true;
document.getElementById("CCW").disabled = true;
document.getElementById("saveroi").disabled = true;
document.getElementById("renameROI").disabled = true;
document.getElementById("moveNext").disabled = true;
document.getElementById("movePrevious").disabled = true;
document.getElementById("saveroi").disabled = false;
return;
}
else
@@ -479,15 +326,6 @@ function UpdateROIs(_sel){
document.getElementById("deleteROI").disabled = false;
document.getElementById("renameROI").disabled = false;
document.getElementById("index").disabled = false;
document.getElementById("multiplier").style.display = "";
document.getElementById("multiplier_decshift").style.display = "";
document.getElementById("refx").disabled = false;
document.getElementById("refdx").disabled = false;
document.getElementById("refy").disabled = false;
document.getElementById("refdy").disabled = false;
document.getElementById("lockSizes").disabled = false;
document.getElementById("lockAspectRatio").disabled = false;
document.getElementById("CCW").disabled = false;
document.getElementById("saveroi").disabled = false;
}
@@ -497,7 +335,7 @@ function UpdateROIs(_sel){
}
if (aktindex > ROIInfo.length)
aktindex = ROIInfo.length-1;
aktindex = ROIInfo.length;
for (var i = 0; i < ROIInfo.length; ++i){
var option = document.createElement("option");
@@ -522,8 +360,6 @@ function UpdateROIs(_sel){
document.getElementById("moveNext").disabled = true;
}
ShowMultiplier();
document.getElementById("lockAspectRatio").checked = lockAspectRatio;
document.getElementById("lockSizes").checked = lockSizes;
@@ -538,6 +374,7 @@ function UpdateROIs(_sel){
rect.w = ROIInfo[aktindex]["dx"];
rect.h = ROIInfo[aktindex]["dy"];
draw();
}
function loadCanvas(dataURL) {
@@ -569,18 +406,8 @@ function UpdateROIs(_sel){
}
/* hash #description open the details part of the page */
function openDescription() {
if(window.location.hash) {
var hash = window.location.hash.substring(1); //Puts hash in variable, and removes the # character
if(hash == 'description')
document.getElementById("desc_details").open = true;
}
}
function init() {
openDescription();
domainname = getDomainname();
canvas.addEventListener('mousedown', mouseDown, false);
canvas.addEventListener('mouseup', mouseUp, false);
@@ -613,7 +440,6 @@ function UpdateROIs(_sel){
console.log("Not all ROI have the same dX and dY, unticking the sync checkbox!");
}
document.getElementById("saveroi").disabled = true;
drawImage();
draw();
@@ -657,13 +483,10 @@ function UpdateNUMBERS(_sel){
function renameNumber(){
var sel = document.getElementById("Numbers_value1");
var _delete= sel.options[sel.selectedIndex].text;
var _numbernew = prompt("Please enter a new name for the selected number sequence", _delete);
if (_numbernew === null) {
return; //break out of the function early because prompt was aborted
}
var _delte= sel.options[sel.selectedIndex].text;
var _numbernew = prompt("Please enter new name", _delte);
erg = RenameNUMBER(_delete, _numbernew);
erg = RenameNUMBER(_delte, _numbernew);
if (erg != "")
firework.launch(erg, 'danger', 30000);
else
@@ -671,10 +494,7 @@ function renameNumber(){
}
function newNumber(){
var _numbernew = prompt("Please enter a name for the new number sequence", "name");
if (_numbernew === null) {
return; //break out of the function early because prompt was aborted
}
var _numbernew = prompt("Please enter name of new number", "name");
erg = CreateNUMBER(_numbernew);
if (erg != "")
@@ -685,13 +505,10 @@ function newNumber(){
function removeNumber(){
if (confirm("The entire number sequence will be removed (digit + analog parts). " +
"To remove single ROI of the number sequence, use \"Delete ROI\" instead.\n" +
"Do you really want to proceed?"))
{
if (confirm("This will remove the number complete (analog and digital).\nIf you only want to remove the digital ROIs, please use \"Delete ROIs\".\nDo you want to proceed?")) {
var sel = document.getElementById("Numbers_value1");
var _delete= sel.options[sel.selectedIndex].text;
erg = DeleteNUMBER(_delete);
var _delte= sel.options[sel.selectedIndex].text;
erg = DeleteNUMBER(_delte);
if (erg != "")
firework.launch(erg, 'danger', 30000);
UpdateNUMBERS();
@@ -759,11 +576,7 @@ function drawTextBG(context, txt, x, y, padding) {
var dx = parseInt(ROIInfo[_nb].dx) + parseInt(lw);
var dy = parseInt(ROIInfo[_nb].dy) + parseInt(lw);
context.strokeRect(x0, y0, dx, dy);
if (ROIInfo[_nb]["CCW"] != "true")
drawTextBG(context, ROIInfo[_nb]["name"], x0+dx/2-0.5, y0-13, 5);
else
drawTextBG(context, ROIInfo[_nb]["name"]+" (CCW)", x0+dx/2-0.5, y0-13, 5);
lw = 1;
var x0 = parseInt(ROIInfo[_nb].x) - parseInt(lw/2);
@@ -773,8 +586,7 @@ function drawTextBG(context, txt, x, y, padding) {
context.strokeRect(x0, y0, dx, dy);
context.lineWidth = lw;
context.beginPath();
// context.arc (x0+dx/2, y0+dy/2, dx/2, 0, 2 * Math.PI);
context.ellipse(x0+dx/2, y0+dy/2, dx/2, dy/2, 0, 0, 2 * Math.PI);
context.arc(x0+dx/2, y0+dy/2, dx/2, 0, 2 * Math.PI);
context.moveTo(x0+dx/2, y0);
context.lineTo(x0+dx/2, y0+dy);
context.moveTo(x0, y0+dy/2);
@@ -791,16 +603,10 @@ function drawTextBG(context, txt, x, y, padding) {
var dx = parseInt(rect.w) + parseInt(lw);
var dy = parseInt(rect.h) + parseInt(lw);
context.strokeRect(x0, y0, dx, dy);
if (ROIInfo[aktindex]["CCW"] != "true")
drawTextBG(context, ROIInfo[aktindex]["name"], x0+dx/2, y0-11, 5);
else
drawTextBG(context, ROIInfo[aktindex]["name"] + " (CCW)", x0+dx/2, y0-11, 5);
context.lineWidth = 1;
context.beginPath();
// context.arc(x0+dx/2, y0+dy/2, dx/2, 0, 2 * Math.PI);
context.ellipse(x0+dx/2, y0+dy/2, dx/2, dy/2, 0, 0, 2 * Math.PI);
context.arc(x0+dx/2, y0+dy/2, dx/2, 0, 2 * Math.PI);
context.moveTo(x0+dx/2, y0);
context.lineTo(x0+dx/2, y0+dy);
context.moveTo(x0, y0+dy/2);
@@ -900,7 +706,6 @@ function drawTextBG(context, txt, x, y, padding) {
rect.startY = document.getElementById("refy").value;
draw();
}
document.getElementById("saveroi").disabled = false;
}
function valuemanualchangeddx(){
@@ -916,7 +721,6 @@ function drawTextBG(context, txt, x, y, padding) {
rect.startY = document.getElementById("refy").value;
draw();
}
document.getElementById("saveroi").disabled = false;
}
@@ -926,10 +730,8 @@ function drawTextBG(context, txt, x, y, padding) {
sel = document.getElementById("index");
var _roialt= sel.options[sel.selectedIndex].text;
var _roinew = prompt("Please enter a new name for the selected ROI", _roialt);
if (_roinew === null) {
return; //break out of the function early because prompt was aborted
}
var _roinew = prompt("Please enter new name", _roialt);
erg = RenameROI(_number, "analog", _roialt, _roinew);
if (erg != "")
@@ -940,15 +742,13 @@ function drawTextBG(context, txt, x, y, padding) {
function numberChanged()
{
aktindex = 0;
UpdateROIs();
}
init();
</script>
</body>
</html>

View File

@@ -0,0 +1,136 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
<meta charset="utf-8"/>
<title>Check</title>
<style>
h1 {font-size: 2em;}
h2 {font-size: 1.5em; margin-block-start: 0.0em; margin-block-end: 0.2em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
.button {
padding: 5px 10px;
width: 210px;
font-size: 16px;
}
</style>
</head>
<body style="font-family: arial">
<table>
<tr><td colspan="2">Result:</td></tr>
<tr>
<td>
<iframe name="maincontent" id ="maincontent" width="700px" height="700px"></iframe>
</td>
<td style="padding-left: 15px;">
<p>
<input class="button" type="submit" id="take" onclick="doTake()" value="1. Take Picture">
</p>
<p>
<input class="button" type="submit" id="align" onclick="doAlign()" value="2. Align Image"><br>
</p>
<p>
Takes up to 2 Minutes!
</p>
Digits and Analog recognition not yet implemented.
<p>
<input class="button" type="submit" id="digits" onclick="doDigits()" value="3a. Analyse Digits">
</p>
<p>
<input class="button" type="submit" id="analog" onclick="doAnalog()" value="3b Analyse Analog">
</p>
</td>
</tr>
</table>
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="readconfig.js?v=$COMMIT_HASH"></script>
<script type="text/javascript">
var domainname = getDomainname();
function doAnalog(){
var xhttp = new XMLHttpRequest();
url = domainname + "/editflow?task=test_analog";
if (domainname.length > 0){
url = url + "&host=" + domainname;
}
xhttp.open("GET", url, false);
xhttp.send();
var html = xhttp.responseText;
html = html.replace("src=\"/", "src=\"" + domainname + "/");
document.getElementById("maincontent").src = 'data:text/html,' + encodeURIComponent(html);
}
function doDigits(){
var xhttp = new XMLHttpRequest();
url = domainname + "/editflow?task=test_digits";
if (domainname.length > 0){
url = url + "&host=" + domainname;
}
xhttp.open("GET", url, false);
xhttp.send();
var html = xhttp.responseText;
html = html.replace("src=\"/", "src=\"" + domainname + "/");
document.getElementById("maincontent").src = 'data:text/html,' + encodeURIComponent(html);
}
function doAlign(){
var xhttp = new XMLHttpRequest();
url = domainname + "/editflow?task=test_align";
if (domainname.length > 0){
url = url + "&host=" + domainname;
}
xhttp.open("GET", url, false);
xhttp.send();
var html = xhttp.responseText;
html = html.replace("src=\"/", "src=\"" + domainname + "/");
document.getElementById("maincontent").src = 'data:text/html,' + encodeURIComponent(html);
document.getElementById("align").disabled = false;
// document.getElementById("digits").disabled = false;
// document.getElementById("analog").disabled = false;
}
function doTake(){
var xhttp = new XMLHttpRequest();
url = domainname + "/editflow?task=test_take";
if (domainname.length > 0){
url = url + "&host=" + domainname;
}
xhttp.open("GET", url, false);
xhttp.send();
var html = xhttp.responseText;
document.getElementById("maincontent").src = 'data:text/html,' + encodeURIComponent(html);
document.getElementById("align").disabled = false;
document.getElementById("digits").disabled = true;
document.getElementById("analog").disabled = true;
}
function Init(){
domainname = getDomainname();
document.getElementById("align").disabled = true;
document.getElementById("digits").disabled = true;
document.getElementById("analog").disabled = true;
}
Init();
</script>
</body>
</html>

View File

@@ -1,24 +1,25 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<html>
<head>
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
<title>Edit Config</title>
<meta charset="UTF-8" />
<meta charset="utf-8">
<style>
h1 {font-size: 2em;}
h2 {font-size: 1.5em; margin-block-start: 0.0em; margin-block-end: 0.2em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
h1 {font-size: 2em;}
h2 {font-size: 1.5em; margin-block-start: 0.0em; margin-block-end: 0.2em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
.button {
padding: 5px 10px;
width: 220px;
.button {
padding: 5px 20px;
width: 211px;
font-size: 16px;
}
}
textarea {
font-size: 15px;
}
textarea {
font-size: 14px;
}
</style>
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
<script type="text/javascript" src="jquery-3.6.0.min.js?v=$COMMIT_HASH"></script>
@@ -27,60 +28,59 @@
<body style="font-family: arial; padding: 0px 10px;">
<table style="width:660px">
<h2>Configuration - "Config.ini" Editor</h2>
<td>
<textarea id="inputTextToSave" rows="30" style="width:100%"></textarea>
<table>
<tr><td><h2>Config.ini:</h2></td></tr>
<tr>
<td colspan="3">
<textarea id="inputTextToSave" cols="100" rows="33"></textarea>
</td>
</table>
</tr>
<tr>
<td><button class="button" onclick="saveTextAsFile()">Save</button></td>
</tr>
<tr>
<td><button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate changes</button></td>
</tr>
</table>
<table>
<td>
<button class="button" onclick="saveTextAsFile()">Save Config</button>
</td>
<td>
<button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate changes</button>
</td>
</table>
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="readconfigcommon.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="readconfigcommon.js?v=$COMMIT_HASH"></script>
<script type="text/javascript">
<script type="text/javascript">
var canvas = document.getElementById('canvas'),
domainname = getDomainname();
function LoadConfigNeu() {
function LoadConfigNeu() {
domainname = getDomainname();
loadConfig(domainname);
document.getElementById("inputTextToSave").value = getConfig();
}
function saveTextAsFile()
{
if (confirm("Are you sure you want to save the configuration?")) {
function saveTextAsFile()
{
if (confirm("Are you sure you want to update \"config.ini\"?")) {
FileDeleteOnServer("/config/config.ini", domainname);
var textToSave = document.getElementById("inputTextToSave").value;
FileSendContent(textToSave, "/config/config.ini", domainname);
firework.launch('Configuration saved. It will get applied after next reboot', 'success', 5000);
}
firework.launch('Configuration got updated. It will get applied after the next reboot!', 'success', 5000);
}
}
function doReboot() {
if (confirm("Are you sure you want to reboot?")) {
function doReboot() {
if (confirm("Are you sure you want to reboot the ESP32?")) {
var stringota = "/reboot";
window.location = stringota;
window.location.href = stringota;
window.location.assign(stringota);
window.location.replace(stringota);
}
}
}
LoadConfigNeu();
</script>
LoadConfigNeu();
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -1,178 +1,112 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<html>
<head>
<meta charset="UTF-8" />
<title>Digit ROI</title>
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
<meta charset="utf-8"/>
<title>Digit ROI's</title>
<style>
h1 {font-size: 2em;}
h2 {font-size: 1.5em; margin-block-start: 0.0em; margin-block-end: 0.2em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
<style>
h1 {font-size: 2em;}
h2 {font-size: 1.5em; margin-block-start: 0.0em; margin-block-end: 0.2em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
input[type=number] {
width: 60px;
input[type=number] {
width: 100px;
margin-right: 10px;
padding: 3px 5px;
display: inline-block;
border: 1px solid #ccc;
font-size: 16px;
}
}
input[type=text] {
input[type=text] {
padding: 3px 5px;
display: inline-block;
border: 1px solid #ccc;
font-size: 16px;
}
}
input:out-of-range {
background-color: rgba(255, 0, 0, 0.25);
border: 1px solid red;
}
select {
select {
padding: 3px 5px;
display: inline-block;
border: 1px solid #ccc;
font-size: 16px;
margin-right: 10px;
min-width: 100px;
max-width: 100%;
vertical-align: middle;
overflow: hidden;
}
}
.button {
.button {
padding: 5px 10px;
width: 160px;
width: 210px;
font-size: 16px;
}
}
.multiplier {
padding: 0px 0px;
.move {
padding: 4px 4px;
width: 100px;
font-size: 12px;
}
}
th, td {
th, td {
padding: 5px 5px 5px 0px;
}
table {
width: 660px;
padding: 5px;
table-layout: fixed;
}
</style>
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
<script type="text/javascript" src="jquery-3.6.0.min.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="firework.js?v=$COMMIT_HASH"></script>
}
</style>
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
<script type="text/javascript" src="jquery-3.6.0.min.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="firework.js?v=$COMMIT_HASH"></script>
</head>
<body style="font-family: arial; padding: 0px 10px;">
<h2>Digit ROI</h2>
<details id="desc_details" style="font-size:16px">
<summary><b>CLICK HERE</b> for usage description. More infos in documentation:
<a href=https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/ target=_blank>ROI Configuration</a>
</summary>
<p>
<b>R</b>egion <b>O</b>f <b>I</b>nterest (ROI) for digit numbers can be defined on this page. If no digit numbers need to be processed,
disable digit processing by deselecting <b>"Digit ROI Processing"</b>.
</p>
<p>
By default one number sequence (a number seqence contains of 1-x digit ROIs + 1-x analog counter ROIs which are processed together) is
predefined and already selected in the drop down <b>"Number sequence"</b>. If you need more than one number sequence additional
one's can be added with the buttons next to the drop down. Each number sequence will be processed separately.
</p>
<p>
Using drag and drop by mouse of by manually entering the parameters into the given fields the digit ROIs can be positined to the digit numbers
on the reference image. To have proper ROI definition please check the documentation:
<a href=https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/ target=_blank>ROI Configuration</a>. It's very important to be
really precise to have reliable processing. With the drop down <b>"ROI"</b> you can change between the different ROIs in the selected
number sequence. To create new ROIs use <b>"New ROI"</b>.
</p>
<p>
The order of the ROIs defines the position (and therefore the multiplication factor) within the reading sequence. The position
in the number sequence can be changed with the buttons <b>"Move ROI Lower"</b> and <b>"Move ROI Higher"</b>. The multiplication factor which is
shown below the ROI drop down is the multiplication factor of pure position/order in number sequence and the factor right-hand side to this is
the additionally corrected by decimal shift setting (configuration, expert parameter, default: 0).
</p>
<p>
After definition of digit ROIs is completed don't forget to save with the <b>"Save Config"</b> button!<br>
<b>NOTE:</b> There is no need to perform a reboot after every saving or step. It's sufficient to reboot after all configuration steps
(reference image, alignment, ROI configuration) are completed to activate new configuration.
</p>
</details>
<hr />
<h2>Digit ROI's</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 style="font-weight: bold; font-size: larger;" for="Category_Digits_enabled">Digit ROI Processing</label>
</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">
<table>
<colgroup>
<col span="1" style="width: 22%;">
<col span="1" style="width: 26%;">
<col span="1" style="width: 26%;">
<col span="1" style="width: 26%;">
</colgroup>
<tr>
<td colspan="4" style="padding: 0px"><class id="Numbers_text" style="color:black;">Number Sequence:</class></td>
<canvas id="canvas" crossorigin></canvas>
</tr>
</table>
<p>
<table>
<tr>
<class id="Numbers_text" style="color:black;"><b>Number:</b> </class>
<select id="Numbers_value1" onchange="numberChanged()">
</select>
<input class="move" type="submit" id="renameNumber" name="renameNumber" onclick="renameNumber()" value="Rename">
<input class="move" type="submit" id="newNumber" name="newNumber" onclick="newNumber()" value="New">
<input class="move" type="submit" id="removeNumber" name="removeNumber" onclick="removeNumber()" value="Remove">
</tr>
</table>
</p>
<table>
<tr>
<td><input class="button" type="submit" id="newROI" name="newROI" onclick="newROI()" value="New ROI (after current)"></td>
<td><input class="button" type="submit" id="deleteROI" name="deleteROI" onclick="deleteROI()" value="Delete ROI"></td>
<td></td>
</tr>
<tr>
<td>
<select id="Numbers_value1" onchange="numberChanged()">
<select id="index" name="index" onchange="ChangeSelection()" tabindex=1>
</select>
</td>
<td><input class="button" type="submit" id="newNumber" name="newNumber" onclick="newNumber()" value="New Sequence"></td>
<td><input class="button" type="submit" id="renameNumber" name="renameNumber" onclick="renameNumber()" value="Rename Sequence"></td>
<td><input class="button" type="submit" id="removeNumber" name="removeNumber" onclick="removeNumber()" value="Delete Sequence"></td>
</tr>
</table>
<hr />
<table>
<colgroup>
<col span="1" style="width: 22%;">
<col span="1" style="width: 26%;">
<col span="1" style="width: 26%;">
<col span="1" style="width: 26%;">
</colgroup>
<tr>
<td style="padding: 0px">ROI:</td>
</tr>
<tr>
<td><select id="index" name="index" onchange="ChangeSelection()" tabindex=1></select></td>
<td><input class="button" type="submit" id="newROI" name="newROI" onclick="newROI()" value="New ROI"></td>
<td><input class="button" type="submit" id="renameROI" name="renameROI" onclick="renameROI()" value="Rename ROI"></td>
<td><input class="button" type="submit" id="deleteROI" name="deleteROI" onclick="deleteROI()" value="Delete ROI"></td>
</tr>
<tr>
<td class="multiplier">Multiplier: <output type="text" id="multiplier" name="multiplier"></output><br>
(only based on order)
<td>
<input class="button" type="submit" id="renameROI" name="renameROI" onclick="renameROI()" value="Rename">
<td>
<input class="move" type="submit" id="moveNext" onclick="moveNext()" value="move Next">
<input class="move" type="submit" id="movePrevious" onclick="movePrevious()" value="move Previous">
</td>
<td class="multiplier">Multiplier: <output type="text" id="multiplier_decshift" name="multiplier_decshift"></output><br>
(order + decimal shift: <output type="text" id="decimalShift" name="decimalShift"></output>)
</td>
<td><input class="button" type="submit" id="movePrevious" onclick="movePrevious()" value="Move ROI Higher"></td>
<td><input class="button" type="submit" id="moveNext" onclick="moveNext()" value="Move ROI Lower"></td>
</tr>
</table>
<table>
<colgroup>
<col span="1" style="width: 18%;">
<col span="1" style="width: 18%;">
<col span="1" style="width: 64%;">
</colgroup>
<tr>
<td>x: <input type="number" name="refx" id="refx" step=1 onchange="valuemanualchanged()" tabindex=2></td>
<td>Δx: <input type="number" name="refdx" id="refdx" step=1 onchange="valuemanualchangeddx()" tabindex=4></td>
@@ -185,26 +119,17 @@
</tr>
<tr>
<td colspan="2"></td>
<td><input type="checkbox" id="lockSpaceEquidistant" name="lockSpaceEquidistant" value="1" onclick="changeLockSpaceEquidistant()" checked tabindex=8>
<label for="lockSpaceEquidistant">Keep equidistance of <input type="number" name="space" id="space" step=1 onchange="valuemanualchangedspace()" tabindex=9> between all ROIs</label>
</td>
<td ><input type="checkbox" id="lockSpaceEquidistant" name="lockSpaceEquidistant" value="1" onclick="changeLockSpaceEquidistant()" checked tabindex=9>
<label for="lockSpaceEquidistant">Keep equidistance of <input type="number" name="space" id="space" step=1 onchange="valuemanualchangedspace()" tabindex=8> between all ROIs</label></td>
</tr>
</table>
</div>
<table>
<colgroup>
<col span="1" style="width: 22%;">
<col span="1" style="width: 26%;">
<col span="1" style="width: 26%;">
<col span="1" style="width: 26%;">
</colgroup>
<tr>
<td colspan="3" style="vertical-align: bottom;"><b>Reference Image:</b></td>
<td><input style="font-weight:bold;" class="button" type="submit" id="saveroi" name="saveroi" onclick="SaveToConfig()" value="Save Config" tabindex=10></td>
</tr>
<tr>
<td colspan="4"><canvas id="canvas" crossorigin></canvas></td>
<td><input class="button" type="submit" id="saveroi" name="saveroi" onclick="SaveToConfig()" value="Save" tabindex=10>
<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>
</table>
@@ -258,7 +183,6 @@ function EnDisableDigits() {
sah1(document.getElementById("div1"), !isEnabled);
cofcat["Digits"]["enabled"] = isEnabled;
document.getElementById("saveroi").disabled = false;
if (isEnabled)
{
@@ -284,10 +208,6 @@ function onNameChange(){
}
function deleteROI(){
if (!confirm("Delete the selected ROI?")) {
return; //break out of the function early because prompt was aborted
}
ROIInfo.splice(aktindex, 1);
if (aktindex > ROIInfo.length - 1){
aktindex = ROIInfo.length - 1;
@@ -302,10 +222,7 @@ function newROI() {
sel = document.getElementById("index");
var _roialt= sel.options[sel.selectedIndex].text;
var _roinew = prompt("Please enter a name for the new ROI", "name");
if (_roinew === null) {
return; //break out of the function early because prompt was aborted
}
var _roinew = prompt("Please enter name of new ROI", "name");
if (ROIInfo.length > 0) {
if (ROIInfo.length > 1) {
@@ -315,7 +232,7 @@ function newROI() {
parseInt(ROIInfo[sel.selectedIndex].y), ROIInfo[aktindex]["dx"], ROIInfo[aktindex]["dy"], 0);
}
else
erg = CreateROI(_number, "digit", sel.selectedIndex, _roinew, 15, 30, 30, 51, 0);
erg = CreateROI(_number, "digit", sel.selectedIndex, _roinew, 1, 1, 30, 51, 0);
if (erg != "")
firework.launch(erg, 'danger', 30000);
@@ -357,7 +274,7 @@ function changelockSizes(){
valuemanualchangedspace();
if (!lockSizes) {
firework.launch("In most cases it's advised to keep the y, Δx and Δy identical!", 'warning', 10000);
firework.launch("For best results it is in most cases advised to keep the y, Δx and Δy identical!", 'warning', 10000);
}
}
@@ -379,51 +296,11 @@ function ChangeSelection(){
}
function SaveToConfig(){
if (confirm("Are you sure you want to save the new digit ROI configuration?")) {
// _zwcat = getConfigCategory();
// _zwcat = getConfigCategory();
cofcat["Digits"]["enabled"] = document.getElementById("Category_Digits_enabled").checked;
WriteConfigININew();
SaveConfigToServer(domainname);
UpdateROIs();
document.getElementById("saveroi").disabled = true;
firework.launch('Configuration saved. It will get applied after next reboot', 'success', 5000);
}
}
function ShowMultiplier()
{
var decimalShift = 0;
var multiplier;
var multiplier_decshift;
var fixedDecimals_decshift;
var NumberInfo = getNUMBERInfo();
var sel = document.getElementById("Numbers_value1");
var _number= sel.options[sel.selectedIndex].text;
document.getElementById("decimalShift").value = 0;
for (var i = 0; i < NumberInfo.length; ++i) {
if (NumberInfo[i]["name"] == _number) {
if (NumberInfo[i]["PostProcessing"]["DecimalShift"]["enabled"]) {
decimalShift = NumberInfo[i]["PostProcessing"]["DecimalShift"]["value1"];
document.getElementById("decimalShift").value = decimalShift;
}
}
}
multiplier = ROIInfo.length - 1 - aktindex;
multiplier_decshift = fixedDecimals_decshift = multiplier + Number(decimalShift);
if (multiplier_decshift > 0)
fixedDecimals_decshift = 0;
if (fixedDecimals_decshift < 0) {
fixedDecimals_decshift = -1*fixedDecimals_decshift;
}
document.getElementById("multiplier").value="x" + Number(10 ** multiplier).toFixed(0);
document.getElementById("multiplier_decshift").value="x" + Number(10 ** multiplier_decshift).toFixed(fixedDecimals_decshift);
firework.launch('Configuration got updated. It will get applied after the next reboot!', 'success', 5000);
}
@@ -439,28 +316,19 @@ function UpdateROIs(_sel){
{
document.getElementById("Category_Digits_enabled").checked = false;
EnDisableDigits();
firework.launch('Digit ROI processing is disabled. Activate with checkbox if needed', 'warning', 10000);
firework.launch('Digital ROIs are disabled - please enable first (Check box top left)', 'warning', 10000);
return;
}
if (ROIInfo.length == 0){
firework.launch('No digit ROIs defined in selected number sequence', 'warning', 10000);
firework.launch('There are no ROIs defined. Please first create a new ROI ("New ROIs ...")', 'danger', 10000);
document.getElementById("newROI").disabled = false;
document.getElementById("deleteROI").disabled = true;
document.getElementById("renameROI").disabled = true;
document.getElementById("index").disabled = true;
document.getElementById("multiplier").style.display = "none";
document.getElementById("multiplier_decshift").style.display = "none";
document.getElementById("refx").disabled = true;
document.getElementById("refdx").disabled = true;
document.getElementById("refy").disabled = true;
document.getElementById("refdy").disabled = true;
document.getElementById("lockSizes").disabled = true;
document.getElementById("lockAspectRatio").disabled = true;
document.getElementById("lockSpaceEquidistant").disabled = true;
document.getElementById("saveroi").disabled = true;
document.getElementById("renameROI").disabled = true;
document.getElementById("moveNext").disabled = true;
document.getElementById("movePrevious").disabled = true;
document.getElementById("saveroi").disabled = false;
return;
}
else
@@ -469,15 +337,6 @@ function UpdateROIs(_sel){
document.getElementById("deleteROI").disabled = false;
document.getElementById("renameROI").disabled = false;
document.getElementById("index").disabled = false;
document.getElementById("multiplier").style.display = "";
document.getElementById("multiplier_decshift").style.display = "";
document.getElementById("refx").disabled = false;
document.getElementById("refdx").disabled = false;
document.getElementById("refy").disabled = false;
document.getElementById("refdy").disabled = false;
document.getElementById("lockSizes").disabled = false;
document.getElementById("lockAspectRatio").disabled = false;
document.getElementById("lockSpaceEquidistant").disabled = false;
document.getElementById("saveroi").disabled = false;
}
@@ -487,7 +346,7 @@ function UpdateROIs(_sel){
}
if (aktindex > ROIInfo.length)
aktindex = ROIInfo.length-1;
aktindex = ROIInfo.length;
for (var i = 0; i < ROIInfo.length; ++i){
var option = document.createElement("option");
@@ -512,8 +371,6 @@ function UpdateROIs(_sel){
document.getElementById("moveNext").disabled = true;
}
ShowMultiplier();
document.getElementById("lockAspectRatio").checked = lockAspectRatio;
document.getElementById("lockSizes").checked = lockSizes;
document.getElementById("lockSpaceEquidistant").checked = lockSpaceEquidistant;
@@ -564,19 +421,7 @@ function UpdateROIs(_sel){
return { top: Math.round(top), left: Math.round(left) };
}
/* hash #description open the details part of the page */
function openDescription() {
if(window.location.hash) {
var hash = window.location.hash.substring(1); //Puts hash in variable, and removes the # character
if(hash == 'description')
document.getElementById("desc_details").open = true;
}
}
function init() {
openDescription();
domainname = getDomainname();
canvas.addEventListener('mousedown', mouseDown, false);
canvas.addEventListener('mouseup', mouseUp, false);
@@ -636,8 +481,6 @@ function UpdateROIs(_sel){
document.getElementById("space").value = space;
}
document.getElementById("saveroi").disabled = true;
drawImage();
draw();
}
@@ -680,13 +523,10 @@ function UpdateNUMBERS(_sel){
function renameNumber(){
var sel = document.getElementById("Numbers_value1");
var _delete= sel.options[sel.selectedIndex].text;
var _numbernew = prompt("Please enter a new name for the selected number sequence", _delete);
if (_numbernew === null) {
return; //break out of the function early because prompt was aborted
}
var _delte= sel.options[sel.selectedIndex].text;
var _numbernew = prompt("Please enter new name", _delte);
erg = RenameNUMBER(_delete, _numbernew);
erg = RenameNUMBER(_delte, _numbernew);
if (erg != "")
firework.launch(erg, 'danger', 30000);
else
@@ -694,10 +534,7 @@ function renameNumber(){
}
function newNumber(){
var _numbernew = prompt("Please enter a name for the new number sequence", "name");
if (_numbernew === null) {
return; //break out of the function early because prompt was aborted
}
var _numbernew = prompt("Please enter name of new number", "name");
erg = CreateNUMBER(_numbernew);
if (erg != "")
@@ -708,13 +545,10 @@ function newNumber(){
function removeNumber(){
if (confirm("The entire number sequence will be removed (digit + analog parts). " +
"To remove single ROI of the number sequence, use \"Delete ROI\" instead.\n" +
"Do you really want to proceed?"))
{
if (confirm("This will remove the number complete (analog and digital).\nIf you only want to remove the digital ROIs, please use \"Delete ROIs\".\nDo you want to proceed?")) {
var sel = document.getElementById("Numbers_value1");
var _delete= sel.options[sel.selectedIndex].text;
erg = DeleteNUMBER(_delete);
var _delte= sel.options[sel.selectedIndex].text;
erg = DeleteNUMBER(_delte);
if (erg != "")
firework.launch(erg, 'danger', 30000);
UpdateNUMBERS();
@@ -921,7 +755,6 @@ function draw() {
}
draw();
}
document.getElementById("saveroi").disabled = false;
}
function valuemanualchangeddx(){
@@ -947,7 +780,6 @@ function draw() {
draw();
}
document.getElementById("saveroi").disabled = false;
}
function valuemanualchangedspace(){
@@ -989,10 +821,8 @@ function draw() {
sel = document.getElementById("index");
var _roialt= sel.options[sel.selectedIndex].text;
var _roinew = prompt("Please enter a new name for the selected ROI", _roialt);
if (_roinew === null) {
return; //break out of the function early because prompt was aborted
}
var _roinew = prompt("Please enter new name", _roialt);
erg = RenameROI(_number, "digit", _roialt, _roinew);
if (erg != "")
@@ -1003,7 +833,6 @@ function draw() {
function numberChanged()
{
aktindex = 0;
UpdateROIs();
}

View File

@@ -1,8 +1,10 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<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 charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
h1 {font-size: 2em; margin-block-end: 0.3em;}
@@ -17,7 +19,7 @@ p {font-size: 1em;}
<body style="font-family: arial">
<h2>Welcome to the setup of the AI-on-the-edge-device</h2>
<h2>Welcome to the Setup of the Digitizer</h2>
<p>
@@ -25,48 +27,41 @@ p {font-size: 1em;}
</p>
<p>
This is the first time you started the device after the initial installation. You have been automatically routed to the <b>initial setup procedure</b>.
With the prodecure the basic setup of your device within seven steps will be performed. After completion of all steps the setup mode will be completed
and the device restarts automatically to the regular web interface.<br>
Note: All settings of the initial setup will be also accessible using regular web interface.
See documentation: <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/initial-setup target=_blank>Initial setup procedure</a> for additional explanations.</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>.
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. See <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/initial-setup target=_blank>
https://jomjol.github.io/AI-on-the-edge-device-docs/initial-setup</a> for additional explanations.</p>
</p>
<p> You can navigate forward and backward during the setup with the buttons "Next Step" and "Previous Step".<br>
With the button "Abort Setup" the setup will be skipped and abort screen will be presented.<br>
To restart the setup process, push the button "Restart Setup".
<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 before heading to another step!</span><br>
</p>
<p>
This is an overview over the seven steps:
This is an overview over the five steps:
</p>
<p>
<ol>
<li><p>Adjust <b>lens focus</b> and check for <b>reflections of flashlight</b>.<br>
Ensure you camera lens has proper focus to object and flashlight do not create any distoring reflections.</p></li>
<li><p>Create the <b>reference image</b>.<br>
<li><p>Create the <b>Reference Image</b>.<br>
It is the base for the position referencing and the identification of the digits and counters.</p></li>
<li><p>Define two unique <b>alignment marker</b>.<br>
They are used to perform an orientation alignment of the taken camera images before further processing</p></li>
<li></p>Define <b>ROI's</b> for the <b>digits</b>.<br>
<li><p>Define two unique <b>Reference Marks</b>.<br>
They is used to align the individual camera images and identify the absolut positions.</p></li>
<li></p>Define <b>ROI's</b> for the <b>Digits</b>.<br>
They will be used to digitize the digit part of your meter.<br>
NOTE: If your meter has no digits, this step can be skipped.</p></li>
<li>Define <b>ROI's</b> for the <b>analog counters</b>.<br>
If your meter has no digits, this step can be skipped.</p></li>
<li>Define <b>ROI's</b>> for the <b>Analog Counters</b>.<br>
They will be used to digitize the analog part of your meter.<br>
NOTE: If your meter has no analog counters, this step can be skipped.</p></li>
<li><p><b>Additional configuration:</b> List of all parameters<br>
Further configuration of your device can be done here.<br>
NOTE: This can also be performed later with regular web interface, e.g. to setup any publishing service like MQTT</p>
</li>
<li><p><b>Setup Completion:</b> End/Abort setup mode<br>
In the final step the setup mode needs to be properly terminated by pushing the button in this page.<br>
NOTE: This is important, otherwise the setup mode is recalled again after reboot.</p>
</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>
<p>Please be patient when switching to another step. The device takes some time to load all needed information!</p>
<p>After step 5 the setup is completed, you then can reboot and starts into the normal operation mode.</p>
<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 AI-on-the-edge-device!</b></p>
<p><b>Have fun with your digitizer!</b></p>
</body>
</html>

View File

@@ -1,8 +1,10 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<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 charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
h1 {font-size: 2em; margin-block-end: 0.3em;}
@@ -12,8 +14,8 @@ p {font-size: 1em;}
.button {
padding: 5px 10px;
width: 205px;
padding: 5px 20px;
width: 211px;
font-size: 16px;
}
@@ -26,16 +28,15 @@ p {font-size: 1em;}
<body style="font-family: arial">
<h4>Initial setup aborted!</h4>
<h4>Step 6: Setup Completed!</h4>
<p>You have <b>aborted</b> the initial setup!</p>
<p>Once you push the button below, the setup mode will be ended and the device will be automatically restart to regular web interface.
Please be aware: The configuration of the device is not or only parly adapted to your needs!<br>
Configuration can still be adapted / completed using regular web interface.
<p>Congratulations, you completed the setup and are now ready to reboot to the normal mode!</p>
<p>Once you have pushed the button below, 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 some minutes until you get the first reading.
</p>
<p>
<button class="button" onclick="reboot()">Abort Setup + Reboot</button>
<button class="button" onclick="reboot()">Leave Setup Modus and Reboot to Normal modus</button>
</p>
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
@@ -43,12 +44,15 @@ p {font-size: 1em;}
<script type="text/javascript" src="readconfigcommon.js?v=$COMMIT_HASH"></script>
<script type="text/javascript">
var canvas = document.getElementById('canvas'),
domainname = getDomainname();
aktstatu = 0;
function reboot() {
if (confirm("Do you want to abort the setup mode and switch to regular web interface?")) {
if (confirm("Do you want to leave the configuration mode and restart the ESP32?")) {
domainname = getDomainname();
if (!loadConfig(domainname)) {
firework.launch('Setup mode could not be deactivated! Please try again!', 'danger', 30000);
firework.launch('Setup Modus could not be deactivated! Please try again!', 'danger', 30000);
return;
}
ParseConfig();
@@ -59,11 +63,12 @@ p {font-size: 1em;}
WriteConfigININew();
SaveConfigToServer(domainname);
var stringota = getDomainname() + "/reboot";
parent.location = stringota;
parent.location.href = stringota;
parent.location.assign(stringota);
parent.location.replace(stringota);
var stringota = "/reboot";
window.location = stringota;
window.location.href = stringota;
window.location.assign(stringota);
window.location.replace(stringota);
}
}

View File

@@ -1,74 +0,0 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<head>
<title>AI on the edge</title>
<meta charset="UTF-8" />
<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;}
.button {
padding: 5px 10px;
width: 205px;
font-size: 16px;
}
</style>
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
<script type="text/javascript" src="jquery-3.6.0.min.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="firework.js?v=$COMMIT_HASH"></script>
</head>
<body style="font-family: arial">
<h4>Step 7 / 7: Initial setup completed!</h4>
<p>
Congratulations! You have completed the initial setup and you are now ready to restart to the regular operation mode!
</p>
<p>
Once you push the button below, the setup mode will be completed and the device will be automatically restart to regular web interface.
If configuration is error free, the device will automatically start with first processing. It will take some time until you get the first reading.
</p>
<p>
<button class="button" onclick="reboot()">Complete Setup + Reboot</button>
</p>
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="readconfigparam.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="readconfigcommon.js?v=$COMMIT_HASH"></script>
<script type="text/javascript">
function reboot() {
if (confirm("Do you want to complete the setup mode and switch to regular web interface?")) {
domainname = getDomainname();
if (!loadConfig(domainname)) {
firework.launch('Setup mode could not be deactivated! Please try again!', 'danger', 30000);
return;
}
ParseConfig();
param = getConfigParameters();
param["System"]["SetupMode"]["enabled"] = true;
param["System"]["SetupMode"]["value1"] = "false";
WriteConfigININew();
SaveConfigToServer(domainname);
var stringota = getDomainname() + "/reboot";
parent.location = stringota;
parent.location.href = stringota;
parent.location.assign(stringota);
parent.location.replace(stringota);
}
}
</script>
</body>
</html>

View File

@@ -1,173 +1,98 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<html>
<head>
<title>Reference Image</title>
<meta charset="utf-8"/>
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
<title>Reference Image</title>
<meta charset="utf-8"/>
<style>
h1 {font-size: 2em;}
h2 {font-size: 1.5em; margin-block-start: 0.0em; margin-block-end: 0.2em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
<style>
h1 {font-size: 2em;}
h2 {font-size: 1.5em; margin-block-start: 0.0em; margin-block-end: 0.2em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
input[type=number] {
width: 60px;
input[type=number] {
width: 100px;
margin-right: 10px;
padding: 3px 5px;
display: inline-block;
border: 1px solid #ccc;
font-size: 16px;
vertical-align: middle;
}
}
input:out-of-range {
background-color: rgba(255, 0, 0, 0.25);
border: 1px solid red;
}
input:invalid {
background-color: rgba(255, 0, 0, 0.25);
border: 1px solid red;
}
.button {
.button {
padding: 5px 10px;
width: 205px;
width: 210px;
font-size: 16px;
}
}
th, td {
padding: 5px 5px 5px 0px;
}
table {
width: 660px;
table {
padding: 5px;
}
</style>
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
<script type="text/javascript" src="jquery-3.6.0.min.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="firework.js?v=$COMMIT_HASH"></script>
}
</style>
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
<script type="text/javascript" src="jquery-3.6.0.min.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="firework.js?v=$COMMIT_HASH"></script>
</head>
<body style="font-family: arial; padding: 0px 10px;">
<h2>Reference Image</h2>
<details id="desc_details" style="font-size:16px">
<summary><b>CLICK HERE</b> for usage description. More infos in documentation:
<a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Reference-Image/ target=_blank>Reference Image</a>
</summary>
<p>
The reference image is the base image on which the alignment markers, digit ROIs and anlog ROIs will be defined.
</p>
<p>
Firstly the actual saved reference image is shown. If you start with the setup from scratch a default image is shown as placeholder.
Use the button <b>"Create New Reference"</b> to start creation of your own reference image. After selecting the button a new image will be taken
all configured parameter will be applied to the shown image. With the button <b>"Update Image"</b> you can update the image again (still all parameter
get applied to the new image).
</p>
<p>
To have reliable evaluation processing a properly horizontal aligned evaluation area is mandatory. Using the parameters "Rotation angle" and
"Rotation angle (Fine-tune)" the image can be rotated in both directions. The resulting rotation anlge is used to prerotate the image before
the alignment algorithm is processed to compensate only small misalignments. Further information can be found in documenation:
<a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Reference-Image/ target=_blank>Reference Image</a>
</p>
<p>
After setting up your reference image don't forget to save with the <b>"Save New Reference"</b> button!<br>
<b>NOTE:</b> There is no need to perform a reboot after every saving or step. It's sufficient to reboot after all configuration steps
(reference image, alignment, ROI configuration) are completed to activate new configuration.
</p>
</details>
<hr />
<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>
<colgroup>
<col span="1" style="width: 33.3%;">
<col span="1" style="width: 33.3%;">
<col span="1" style="width: 33.3%;">
</colgroup>
<tr>
<td><input class="button" type="button" value="Show Actual Reference" onclick="showReference(param)"></td>
<td><input class="button" type="button" id="startreference" value="Create New Reference" onclick="loadRawImage(false)"></td>
<td><input class="button" type="submit" id="take" onclick="doTake()" value="Update Image">
<td><input class="button" type="button" value="Create New Reference" onclick="loadRawImage()"></td>
<td><input class="button" type="submit" id="take" onclick="doTake()" value="Take Image"></td>
</tr>
</table>
<table>
<colgroup>
<col span="1" style="width: 32%;">
<col span="1" style="width: 28%;">
<col span="1" style="width: 18%;">
<col span="1" style="width: 22%;">
</colgroup>
<tr>
<td><label for="mirror" id="labelmirror">Mirror image:</label></td>
<td><input type="checkbox" id="mirror" name="mirror" value="1" onchange="drawRotated()"></td>
<td style="padding-top: 10px"><label for="mirror" id="labelmirror">Mirror Image:</label></td>
<td style="padding-top: 10px"><input type="checkbox" id="mirror" name="mirror" value="1" onchange="drawRotated()"></td>
<td>
<class id="TakeImage_LEDIntensity_text" style="color:black;">LED intensity:</class>
</td>
<td>
<input required style="clear: both" type="number" id="TakeImage_LEDIntensity_value1" size="13" value="0" min="0" max="100"
oninput="(!validity.rangeOverflow||(value=100)) && (!validity.rangeUnderflow||(value=0)) &&
(!validity.stepMismatch||(value=parseInt(this.value)));">
<class id="TakeImage_LEDIntensity_text" style="color:black;">LEDIntensity: </class>
<input type="number" id="TakeImage_LEDIntensity_value1" size="13" value=0 min="0" max="100" style="float: right; clear: both;">
</td>
</tr>
<tr>
<td><label for="flip" id="labelflip">Flip image size:</label></td>
<td><label for="flip" id="labelflip">Flip Image Size:</label></td>
<td><input type="checkbox" id="flip" name="flip" value="1" onchange="drawRotated()"></td>
<td>
<class id="TakeImage_Brightness_text" style="color:black;">Brightness:</class>
<class id="TakeImage_Brightness_text" style="color:black;">Brightness: </class>
<input type="number" id="TakeImage_Brightness_value1" size="13" value=0 min="-2" max="2" style="float: right; clear: both;">
</td>
</tr>
<tr>
<td><label for="mirror">Pre-rotate Angle:</label></td>
<td><input type="number" id="prerotateangle" name="prerotateangle" value="0" min="-360" max="360" onchange="drawRotated()">Degrees</td>
<td>
<input style="clear: both; width: 80%;vertical-align:middle" type="range" id="TakeImage_Brightness_value1" size="13" value=0 min="-2" max="2" oninput="this.nextElementSibling.value = this.value">
<output id="TakeImage_Brightness_value1_output" style="vertical-align:middle; min-width:15px; padding-right:5px; text-align:right; float:left">0</output>
<class id="TakeImage_Contrast_text" style="color:black;">Contrast</class>
<input type="number" id="TakeImage_Contrast_value1" size="13" value=0 min="-2" max="2" style="float: right; clear: both;">
</td>
</tr>
<tr>
<td><label for="prerotateangle">Rotation angle:</label></td>
<td><label for="mirror">Fine Alignment:</label></td>
<td><input type="number" id="finerotate" name="finerotate" value=0.0 min="-1" max="1" step="0.2" onchange="drawRotated()">Degrees</td>
<td>
<input required type="number" id="prerotateangle" name="prerotateangle" value="0" min="-360" max="360" onchange="drawRotated()"
oninput="(!validity.rangeOverflow||(value=360)) && (!validity.rangeUnderflow||(value=-360)) &&
(!validity.stepMismatch||(value=parseInt(this.value)));">degree
</td>
<td>
<class id="TakeImage_Contrast_text" style="color:black;">Contrast:</class>
</td>
<td>
<input style="clear: both; width: 80%;vertical-align:middle" type="range" id="TakeImage_Contrast_value1" size="13" value=0 min="-2" max="2" oninput="this.nextElementSibling.value = this.value">
<output id="TakeImage_Contrast_value1_output" style="vertical-align:middle; min-width:15px; padding-right:5px; text-align:right; float:left">0</output>
</td>
</tr>
<tr>
<td><label for="finerotate">Rotation angle (Fine-tune):</label></td>
<td>
<input required type="number" id="finerotate" name="finerotate" value=0.0 min="-1" max="1" step="0.1" onchange="drawRotated()"
oninput="(!validity.rangeOverflow||(value=1)) && (!validity.rangeUnderflow||(value=-1)) &&
(!validity.stepMismatch||(value=parseInt(this.value)));">degree
</td>
<td>
<class id="TakeImage_Saturation_text" style="color:black;">Saturation:</class>
</td>
<td>
<input style="clear: both; width: 80%;vertical-align:middle" type="range" id="TakeImage_Saturation_value1" size="13" value=0 min="-2" max="2" oninput="this.nextElementSibling.value = this.value">
<output id="TakeImage_Saturation_value1_output" style="vertical-align:middle; min-width:15px; padding-right:5px; text-align:right; float:left">0</output>
<class id="TakeImage_Saturation_text" style="color:black;">Saturation</class>
<input type="number" id="TakeImage_Saturation_value1" size="13" value=0 min="-2" max="2" style="float: right; clear: both;">
</td>
</tr>
</table>
<table>
<colgroup>
<col span="1" style="width: 33.3%;">
<col span="1" style="width: 33.3%;">
<col span="1" style="width: 33.3%;">
</colgroup>
<tr>
<td style="vertical-align: bottom;"><b>Reference Image:</b></td>
<td></td>
<td>
<input style="font-weight:bold;" class="button" type="button" id="updatereferenceimage" value="Save New Reference" onclick="SaveReference()">
</td>
<td><canvas id="canvas"></canvas></td>
</tr>
<tr>
<td colspan="3"><canvas id="canvas"></canvas></td>
<td><input class="button" type="button" id="updatereferenceimage" value="Save" onclick="SaveReference()">
<p>Proceed to update the <a href=edit_alignment.html>Alignment Marks</a> when you are done.</p></td>
</tr>
</table>
@@ -177,6 +102,7 @@
<script type="text/javascript" src="readconfigparam.js?v=$COMMIT_HASH"></script>
<script language="JavaScript">
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
@@ -185,7 +111,6 @@
isActReference = false;
param;
function doReboot() {
if (confirm("Are you sure you want to reboot? Did you save the config?")) {
var stringota = getDomainname() + "/reboot";
@@ -196,7 +121,6 @@
}
}
function doTake(){
var xhttp = new XMLHttpRequest();
if (param["TakeImage"]["Brightness"].found && param["TakeImage"]["Brightness"].enabled)
@@ -220,24 +144,15 @@
xhttp.open("GET", url, false);
xhttp.send();
firework.launch('Taking updated image...', 'success', 5000);
loadRawImage(true);
loadRawImage();
}
function loadRawImage(new_image) {
if (new_image) {
function loadRawImage(){
url = getDomainname() + "/img_tmp/raw.jpg" + "?session=" + Math.floor((Math.random() * 1000000) + 1);
document.getElementById("updatereferenceimage").disabled = false;
}
else {
document.getElementById("updatereferenceimage").disabled = true;
doTake();
}
document.getElementById("finerotate").disabled = false;
document.getElementById("prerotateangle").disabled = false;
document.getElementById("startreference").disabled = true;
document.getElementById("updatereferenceimage").disabled = false;
document.getElementById("take").disabled = false;
if (param["Alignment"]["InitialMirror"].found)
document.getElementById("mirror").disabled = false;
@@ -273,22 +188,14 @@
// document.getElementById("TakeImage_Contrast_value1").disabled = false;
isActReference = false;
loadCanvas(url, true);
loadCanvas(url);
drawRotated();
}
function showReference(_param){
url = getDomainname() + "/fileserver/config/reference.jpg" + "?session=" + Math.floor((Math.random() * 1000000) + 1);
if (_param["Alignment"]["InitialRotate"].value1 < 0) {
document.getElementById("prerotateangle").value = Math.ceil(_param["Alignment"]["InitialRotate"].value1);
}
else {
document.getElementById("prerotateangle").value = Math.floor(_param["Alignment"]["InitialRotate"].value1);
}
document.getElementById("finerotate").value = (Number(_param["Alignment"]["InitialRotate"].value1) -
Number(document.getElementById("prerotateangle").value)).toFixed(1);
url = getDomainname() + "/fileserver/config/reference.jpg" + "?session=" + Math.floor((Math.random() * 1000000) + 1);;
document.getElementById("finerotate").value = 0;
document.getElementById("prerotateangle").value = _param["Alignment"]["InitialRotate"].value1;
if (_param["Alignment"]["InitialMirror"].found && (_param["Alignment"]["InitialMirror"].value1 == "true"))
document.getElementById("mirror").checked = true;
@@ -299,7 +206,6 @@
document.getElementById("finerotate").disabled = true;
document.getElementById("prerotateangle").disabled = true;
document.getElementById("updatereferenceimage").disabled = true;
document.getElementById("startreference").disabled = false;
document.getElementById("take").disabled = true;
document.getElementById("TakeImage_Brightness_value1").disabled = true;
document.getElementById("TakeImage_Saturation_value1").disabled = true;
@@ -318,10 +224,10 @@
document.getElementById("flip").disabled = true;
isActReference = true;
loadCanvas(url, false);
loadCanvas(url);
drawRotated(false, true);
}
function dataURLtoBlob(dataurl) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
@@ -331,11 +237,9 @@
return new Blob([u8arr], {type:mime});
}
function SaveReference(){
if (confirm("Are you sure you want to save the new reference image configuration?")) {
param["Alignment"]["InitialRotate"].value1 = (Number(document.getElementById("prerotateangle").value) +
Number(document.getElementById("finerotate").value)).toFixed(1);
if (confirm("Are you sure you want to update the Reference Image?")) {
param["Alignment"]["InitialRotate"].value1 = document.getElementById("prerotateangle").value;
if ((param["Alignment"]["InitialMirror"].found == true) && (document.getElementById("mirror").checked))
{
@@ -364,28 +268,22 @@
WriteConfigININew();
SaveConfigToServer(getDomainname());
document.getElementById("updatereferenceimage").disabled = true;
SaveCanvasToImage(canvas, "/config/reference.jpg", true, getDomainname());
showReference(param);
//UpdatePage();
firework.launch('Reference image configuration saved. It will get applied after next reboot', 'success', 5000);
UpdatePage();
firework.launch('Reference got saved. It will get applied after the next reboot!', 'success', 5000);
}
}
function loadCanvas(dataURL, grid) {
function loadCanvas(dataURL) {
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
imageObj.onload = function() {
canvas.width = this.width;
canvas.height = this.height;
if (grid)
drawRotated(true);
else
drawRotated(false);
drawRotated();
};
imageObj.src = dataURL;
@@ -405,19 +303,7 @@
return { top: Math.round(top), left: Math.round(left) };
}
/* hash #description open the details part of the page */
function openDescription() {
if(window.location.hash) {
var hash = window.location.hash.substring(1); //Puts hash in variable, and removes the # character
if(hash == 'description')
document.getElementById("desc_details").open = true;
}
}
function init() {
openDescription();
canvas.addEventListener('mousemove', mouseMove, false);
loadConfig(getDomainname());
ParseConfig();
@@ -453,11 +339,10 @@
showReference(param);
}
function UpdateInput() {
WriteParameter(param, category, "TakeImage", "Brightness", false, true);
WriteParameter(param, category, "TakeImage", "Contrast", false, true);
WriteParameter(param, category, "TakeImage", "Saturation", false, true);
WriteParameter(param, category, "TakeImage", "Brightness", false);
WriteParameter(param, category, "TakeImage", "Contrast", false);
WriteParameter(param, category, "TakeImage", "Saturation", false);
WriteParameter(param, category, "TakeImage", "LEDIntensity", false);
}
@@ -480,7 +365,7 @@
}
function WriteParameter(_param, _category, _cat, _name, _optional, outval = false, _select = false, _anzpara = 1){
function WriteParameter(_param, _category, _cat, _name, _optional, _select = false, _anzpara = 1){
if (_param[_cat][_name]["found"]){
if (_optional) {
document.getElementById(_cat+"_"+_name+"_enabled").checked = _param[_cat][_name]["enabled"];
@@ -504,8 +389,7 @@
document.getElementById(_cat+"_"+_name+"_value"+j).value = _param[_cat][_name]["value"+j];
}
}
if (outval)
document.getElementById(_cat+"_"+_name+"_value1_output").value = document.getElementById(_cat+"_"+_name+"_value1").value;
}
else {
if (_optional) {
@@ -519,7 +403,7 @@
}
function drawRotated(_grid = true) {
function drawRotated(_grid = true, _isreference = false){
finerot= parseFloat(document.getElementById("finerotate").value);
prerot = parseFloat(document.getElementById("prerotateangle").value);
mirror = document.getElementById("mirror").checked;
@@ -571,11 +455,11 @@
context.restore();
if (_grid)
if (_grid == true && !isActReference){
drawGrid();
}
}
function drawGrid(){
var canvas = document.getElementById('canvas');
@@ -605,13 +489,8 @@
ctx.restore();
}
function mouseMove(e) {
if (isActReference)
drawRotated(false);
else
drawRotated(true);
drawRotated();
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
@@ -629,8 +508,8 @@
context.stroke();
}
init();
init();
</script>
</body>

View File

@@ -1,33 +1,34 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<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 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>Step 1 / 7: Adjust Focus And Verify Flashlight</h4>
Firstly you have find a proper mounting position and potentially have to adjust the focus of the camera lens to get a sharp and crisp image.
This <b>live stream with flashlight on</b> could be helpful for this task. More details about adjusting the camera lens can be found here:
<a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Reference-Image/#focus target=_blank>Focus Adjustment</a><br>
Additionally it should be verfied that the flashlight is not creating any distrubing reflection in the processing relevant areas.
Beside using the built-in internal flash LED it's also possible to attach additional external LEDs to the device to have more possiblities
to get proper light condition. Please read the documentation if you'd like to use extenal LEDs:
<a href=https://jomjol.github.io/AI-on-the-edge-device-docs/External-LED/ target=_blank>Installation Of External LEDs</a>
<p>
NOTE: The flashlight indensity is set to default (50%) for initial verfication in this step and can be modified in next step. After modification
you can come back to this step if you'd like to test with adjusted light intensity.<br>
The live stream can also be called at any time also after setup mode is completed on regular web interface.
</p>
<h4>Step 1: Define a Reference Image</h4>
The reference image is the base to define the digits, counters and references positions.
<p>
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.
</p>
<p>
Don't forget to save your changes with the <b>"Save"</b> button!
</p>
</body>
</html>

View File

@@ -1,21 +1,34 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<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 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>Step 2 / 7: Create A Reference Image</h4>
<h4>Step 2: Define two Alignment Marks</h4>
Two opposite alignment marks are needed to identify unique fix points on the image.
<p>
Mark the reference by drag and dop with the mouse or with the coordinates and push <b>"Update Reference"</b>.
<br>
You can switch between the two reference with <b>"Select Reference"</b>.
</p>
<p>
Don't forget to save your changes with the <b>"Save"</b> button!
</p>
</body>
</html>

View File

@@ -1,21 +1,36 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<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 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>Step 3 / 7: Definement Of Alignment Marker</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.
<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.
<br>
To create new ROIs use <b>"New ROIs"</b>. The order of the ROIs defines the positon within the reading. <br>
You can change it with <b>"move Next" / "move Previous"</b>.
</p>
<p>
Don't forget to save your changes with the <b>"Save"</b> button!
</p>
</body>
</html>

View File

@@ -1,21 +1,35 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<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 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>Step 4 / 7: Configuration Of ROIs For Digit Numbers</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.
<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.
<br>
To create new ROIs use <b>"New ROIs"</b>. The order of the ROIs defines the positon within the reading. <br>
You can change it with <b>"move Next" / "move Previous"</b>.
</p>
<p>
Don't forget to save your changes with the <b>"Save"</b> button!
</p>
</body>
</html>

View File

@@ -1,21 +1,31 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<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 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>Step 5 / 7: Configuration Of ROIs For Analog Counters</h4>
<h4>Step 5: General Configuration Settings</h4>
<p>Here you can define additional settings. The default settings should fit for a normal/initial setup.</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>
Don't forget to save your changes with the <b>"Save"</b> button!
</p>
</body>
</html>

View File

@@ -1,23 +1 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<head>
<title>AI on the edge</title>
<meta charset="UTF-8" />
<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>Step 6 / 7: Configuration Page: List Of All Parameters</h4>
<p>
NOTE: All parameter can be edited later in the regular web interface. So don't worry if you do not understand the parameters yet!
</p>
</body>
</html>
<!-- This page is never shown -->

View File

@@ -1 +0,0 @@
<!-- This page is never shown -->

View File

@@ -1,15 +1,9 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<html>
<head>
<link href="/firework.css?v=$COMMIT_HASH" rel="stylesheet">
<script type="text/javascript" src="/jquery-3.6.0.min.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="/firework.js?v=$COMMIT_HASH"></script>
<style>
h1 {font-size: 2em;}
h2 {font-size: 1.5em; margin-block-start: 0.0em; margin-block-end: 0.2em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
#files_table {
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
@@ -33,82 +27,57 @@
padding-top: 12px;
padding-bottom: 12px;
text-align: left;
background-color:lightgrey;
color: black;
}
input[type=file] {
padding: 5px 0px;
display: inline-block;
font-size: 16px;
}
input[type=text] {
padding: 5px 10px;
display: inline-block;
border: 1px solid #ccc;
font-size: 16px;
}
.button {
padding: 4px 10px;
width: 100px;
font-size: 16px;
background-color: #0011ff;
color: white;
}
</style>
</head>
</body>
<table class="fixed" border="0" width=100% style="font-family: arial">
<tr>
<td style="vertical-align: top;width: 300px;">
<h2>Fileserver</h2>
</body>
<table class="fixed" border="0" width=100% style="font-family: arial">
<tr><td>
<h2>Content on SD-Card</h2>
</td>
<td rowspan="2">
<table border="0" style="width:100%">
<td rowspan="2" width="500px">
<table border="0">
<tr>
<td style="width:80px">
<label for="newfile">Source</label>
<td>
<label for="newfile">Upload a file</label>
</td>
<td colspan="2">
<input id="newfile" type="file" onchange="setpath()" style="width:100%;">
</td>
</tr>
<tr>
<td>
<label for="filepath">Destination</label>
<label for="filepath">Set path on server</label>
</td>
<td>
<input id="filepath" type="text" style="width:94%;">
<input id="filepath" type="text" style="width:100%;">
</td>
<td>
<button id="upload" type="button" class="button" onclick="upload()">Upload</button>
<button id="upload" type="button" onclick="upload()">Upload</button>
</td>
</tr>
</table>
</td>
</tr>
<tr></tr>
</td></tr>
<tr>
<td colspan="2">
<button style="font-size:16px; padding: 5px 10px" id="dirup" type="button" onclick="dirup()" disabled>&#129145; Directory up</button>
<span style="padding-left:15px" id="currentpath"></span>
<td>
<span id="currentpath"></span> &nbsp;&nbsp;&nbsp;&nbsp;<button id="dirup" type="button" onclick="dirup()" disabled>&#129041; Directory up</button>
</td>
</tr>
</table>
</table>
<script type="text/javascript" src="/common.js?v=$COMMIT_HASH">
</script>
<script language="JavaScript">
function setpath() {
<script type="text/javascript" src="/common.js?v=$COMMIT_HASH">
</script>
<script language="JavaScript">
function setpath() {
var fileserverpraefix = "/fileserver";
var anz_zeichen_fileserver = fileserverpraefix.length;
var default_path = window.location.pathname.substring(anz_zeichen_fileserver) + document.getElementById("newfile").files[0].name;
document.getElementById("filepath").value = default_path;
}
}
function dirup() {
function dirup() {
var str = window.location.href;
str = str.substring(0, str.length-1);
var zw = str.indexOf("/");
@@ -122,10 +91,10 @@
var res = str.substring(0, found+1);
window.location.href = res;
}
}
function upload() {
function upload() {
var filePath = document.getElementById("filepath").value;
var upload_path = "/upload/" + filePath;
var fileInput = document.getElementById("newfile").files;
@@ -160,7 +129,6 @@
document.open();
document.write(xhttp.responseText);
document.close();
firework.launch('File upload completed', 'success', 5000);
} else if (xhttp.status == 0) {
firework.launch('Server closed the connection abruptly!', 'danger', 30000);
UpdatePage(false);
@@ -173,10 +141,10 @@
xhttp.open("POST", upload_path, true);
xhttp.send(file);
}
}
}
function checkAtRootLevel(res) {
function checkAtRootLevel(res) {
if (getPath() == "/fileserver/") { // Already at root level
document.getElementById("dirup").disabled = true;
console.log("Already on sd-card root level!");
@@ -185,20 +153,21 @@
document.getElementById("dirup").disabled = false;
return false;
}
}
function getPath() {
function getPath() {
return window.location.pathname.replace(/\/+$/, '') + "/"
}
}
checkAtRootLevel();
checkAtRootLevel();
console.log("Current path: " + getPath().replace("/fileserver", ""));
document.getElementById("currentpath").innerHTML = "Current path: <b>" + getPath().replace("/fileserver", "") + "</b>";
console.log("Current path: " + getPath().replace("/fileserver", ""));
document.getElementById("currentpath").innerHTML = "Current path: <b>" + getPath().replace("/fileserver", "") + "</b>";
document.cookie = "page=" + getPath() + "; path=/";
document.cookie = "page=" + getPath() + "; path=/";
</script>
</body>
</script>
</body>
</html>

View File

@@ -4,15 +4,13 @@
border-color: #888;
border-radius: 6px;
color: #fff;
left: 10%;
left: 200px;
padding: 5px;
position: fixed;
opacity: 0;
text-align: center;
top: 0px;
width: 80%;
min-width:30%;
max-width:580px;
width: 600px;
z-index: 99;
font-size:120%;
}

View File

@@ -29,7 +29,7 @@
p += parseInt($(this).height()) + 30
});
$('<div id="'+ fid +'" class="'+ c +'">'+ m +'<a onclick="firework.remove(\'#'+ fid +'\')"><img style="height:28px;" src=close.png></a></div>')
$('<div id="'+ fid +'" class="'+ c +'">'+ m +'<a onclick="firework.remove(\'#'+ fid +'\')"><img src=close.png></a></div>')
.appendTo('body')
.animate({
opacity: 1,

View File

@@ -1,38 +1,14 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<head>
<title>Data Graph</title>
<html>
<head>
<script type="text/javascript" src='plotly-basic-2.18.2.min.js?v=$COMMIT_HASH'></script>
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="readconfigcommon.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="readconfigparam.js?v=$COMMIT_HASH"></script>
<style>
h1 {font-size: 2em;}
h2 {font-size: 1.5em; margin-block-start: 0.0em; margin-block-end: 0.2em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
body {
font-family: Arial, Helvetica, sans-serif;
}
select {
padding: 3px 5px;
display: inline-block;
border: 1px solid #ccc;
font-size: 16px;
margin-right: 10px;
min-width: 100px;
vertical-align: middle;
}
.button {
padding: 5px 10px;
width: 160px;
font-size: 16px;
}
</style>
<script>
@@ -47,7 +23,7 @@
.then(response => {
// handle the response
if (response.status == 404) {
firework.launch("No data available for " + dateString, 'warning', 10000);
firework.launch("No log data available for " + dateString, 'warning', 10000);
}
response.text()
.then( result => {
@@ -156,17 +132,17 @@
},
margin: {
l: 70,
r: 70,
l: 50,
r: 50,
b: 50,
t: 40,
t: 50,
pad: 4
},
legend: {
x: 0.02,
y: 0.97,
xanchor: 'left'
x: 0.2,
y: 0.9,
xanchor: 'right'
}
};
@@ -182,19 +158,19 @@
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
<script type="text/javascript" src="jquery-3.6.0.min.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="firework.js?v=$COMMIT_HASH"></script>
</head>
<body>
<h2>Data Graph</h2>
</head>
<body>
<h3>Data Graph</h3>
<div id='chart'><p>Loading...<br></p></div>
Number sequence:
<select id="numbers" onchange="run();"></select>
Day:
<select id="datafiles" onchange="run();"></select>
<input type="checkbox" id="showRrelativeValues" onclick = 'run();' unchecked ><label for="showRrelativeValues">Show relative values</label><br><br>
<button class="button" onclick="run();">Refresh</button>
<button class="button" onClick="window.location.href = 'data.html?v=$COMMIT_HASH'">Show Data Viewer</button>
<button class="button" onClick="window.location.href = getDomainname() + '/fileserver/log/data/'">Show Data Files</button>
<select id="numbers" onchange="run();"></select>
<input type="checkbox" id="showRrelativeValues" onclick = 'run();' unchecked ><label for="showRrelativeValues">Show relative values</label>
<button onclick="run();">Refresh</button>
&nbsp;&nbsp;|&nbsp;&nbsp;
<button onClick="window.location.href = 'data.html?v=$COMMIT_HASH'">Show data</button>
<button onClick="window.location.href = getDomainname() + '/fileserver/log/data/'">Show data files</button>
</div>
<script>
function WriteModelFiles()
@@ -238,15 +214,7 @@
WriteModelFiles();
WriteNumbers();
function Refresh() {
setTimeout (function() {
run();
Refresh();
}, 300000);
}
run();
Refresh();
</script>
</body>
</html>

View File

@@ -1,43 +1,24 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<html>
<head>
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
<link rel="apple-touch-icon" href="watermeter.svg" />
<link rel="shortcut icon" href="watermeter.svg" sizes="196x196">
<title>AI on the edge</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="style.css?v=$COMMIT_HASH" type="text/css" >
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
<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">
<link rel="stylesheet" href="style.css?v=$COMMIT_HASH" type="text/css" >
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="readconfigcommon.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="readconfigparam.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="readconfigcommon.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="readconfigparam.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="jquery-3.6.0.min.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="firework.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="jquery-3.6.0.min.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="firework.js?v=$COMMIT_HASH"></script>
<script>
var streamPopup;
var streamFlashlight = false;
var streamWindowFeatures =
'channelmode=no,directories=no,fullscreen=no,' +
'location=no,dependent=yes,menubar=no,resizable=no,scrollbars=no,' +
'status=no,toolbar=no,titlebar=no,' +
'left=10,top=250,width=640px,height=480px';
function loadPage(page) {
console.log("loadPage( " + page + " )");
if (streamPopup) // Ensure that stream popup is closed because it's blocking web interface
streamPopup.close();
asyncPageLoad(page);
}
async function asyncPageLoad(page ) {
console.log(" loading " + page + " ...");
<script>
async function loadPage(page) {
console.log("loadPage(" + page + ")");
document.cookie = "page="+page + "; path=/";
document.getElementById('maincontent').src = page;
@@ -46,7 +27,6 @@
});
}
function resetMenu() {
[].forEach.call(document.querySelectorAll('.submenu'), function (el) {
el.style.visibility = 'visible';
@@ -68,55 +48,47 @@
}
return "";
}
</script>
</script>
</head>
<body>
<div class="main">
<table style="border: none; width:100%">
<tr>
<td style="padding-right: 10px;"><img style="width:64px; height:64px" src="favicon.ico?v=$COMMIT_HASH"></td>
<table style="border: none">
<tr><td style="padding-right: 10px;"><img src="favicon.ico?v=$COMMIT_HASH"></td>
<td><h1 id="id_title"> Digitizer - AI on the edge</h1>
<h2>An ESP32 all inclusive neural network recognition system for meter digitalization</h2>
</td>
</tr>
</td></tr>
</table>
<div class="menu" onmouseover="resetMenu()">
<ul>
<li><a href="#" onclick="loadPage('overview.html?v=$COMMIT_HASH');">Overview</a></li>
<li><a>Settings <i class="arrow down"></i></a>
<ul class="submenu">
<li><a href="#" onclick="loadPage('prevalue_set.html?v=$COMMIT_HASH');">Set "Previous Value"</a></li>
<li><a href="#" onclick="loadPage('prevalue_set.html?v=$COMMIT_HASH');">Set Previous Value</a></li>
<li><a href="#" onclick="loadPage('edit_config_param.html?v=$COMMIT_HASH');">Configuration</a></li>
<li><a>Alignment <i class="arrow right"></i></a>
<ul>
<li><a href="#" onclick="loadPage('edit_reference.html?v=$COMMIT_HASH');">Reference Image</a></li>
<li><a href="#" onclick="loadPage('edit_alignment.html?v=$COMMIT_HASH');">Alignment Marker</a></li>
<li><a href="#" onclick="loadPage('edit_alignment.html?v=$COMMIT_HASH');">Alignment Marks</a></li>
</ul>
</li>
<li><a><strong>R</strong>egions <strong>O</strong>f <strong>I</strong>nterest <i class="arrow right"></i></a>
<ul>
<li><a href="#" onclick="loadPage('edit_digits.html?v=$COMMIT_HASH');">Digit ROI</a></li>
<li><a href="#" onclick="loadPage('edit_analog.html?v=$COMMIT_HASH');">Analog ROI</a></li>
<li><a href="#" onclick="loadPage('edit_digits.html?v=$COMMIT_HASH');">Digital ROIs</a></li>
<li><a href="#" onclick="loadPage('edit_analog.html?v=$COMMIT_HASH');">Analog ROIs</a></li>
</ul>
</li>
</ul>
<li><a>Data<i class="arrow down"></i></a>
<li><a>Data <i class="arrow down"></i></a>
<ul class="submenu">
<li><a href="#" onclick="loadPage(getDomainname() + '/value?full');">Recognition</a></li>
<li><a>Livestream <i class="arrow right"></i></a>
<ul>
<li><a href="#" onclick="start_livestream(false);">Live Stream (Light off)</a></li>
<li><a href="#" onclick="start_livestream(true);">Live Stream (Light on)</a></li>
</ul>
</li>
<li><a href="#" onclick="loadPage('graph.html?v=$COMMIT_HASH');">Data Graph</a></li>
<li><a href="#" onclick="loadPage('data.html?v=$COMMIT_HASH');">Data Table</a></li>
<li><a href="#" onclick="loadPage('data.html?v=$COMMIT_HASH');">Data Viewer</a></li>
<li><a href="#" onclick="loadPage(getDomainname() + '/fileserver/log/data/');">Data Files</a></li>
</ul>
</li>
@@ -130,96 +102,28 @@
<li><a href="#" onclick="loadPage(getDomainname() + '/fileserver/');">File Server</a></li>
<li><a href="#" onclick="loadPage('reboot_page.html?v=$COMMIT_HASH');">Reboot</a></li>
<li><a href="#" onclick="loadPage('info.html?v=$COMMIT_HASH');">Info</a></li>
<li><a href="https://jomjol.github.io/AI-on-the-edge-device-docs/" target="_blank">Help</a></li>
</ul>
</li>
<li id="ManualControl" style="display:none;"><a>Manual Control <i class="arrow down"></i></a> <!-- Workaround: Hide menu if no entry is available -->
<ul class="submenu">
<!--<li><a href="#" onclick="flow_start()">Start Round</a></li>--> <!-- Needs to be adapted on code side first to ensure proper user feedback -->
<li id="HASendDiscovery" style="display:none;"><a href="#" onclick="HA_send_discovery()">Resend HA Discovery</a></li>
</ul>
</li>
</ul>
</div>
<iframe title="maincontent" name="maincontent" class="iframe" id="maincontent"></iframe>
<iframe name="maincontent" class="iframe" id="maincontent"></iframe>
<span id="Version" style="font-size: 10px; margin-top: -5px;padding-left: 10px;">Loading version...</span>
<span id="Version" style="font-size: 10px; margin-top: -5px">Loading version...</span>
<script type="text/javascript">
LoadHostname();
LoadFwVersion();
LoadWebUiVersion();
HA_send_discovery_visibility();
if (getCookie("page") == "" || getCookie("page") == "reboot_page.html?v=$COMMIT_HASH") {
document.cookie = "page=overview.html?v=$COMMIT_HASH" + "; path=/";
}
console.log("Loading page: " + getCookie("page"));
document.getElementById('maincontent').src = getCookie("page");
/*
function flow_start() {
var url = getDomainname() + '/flow_start';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
if (xhttp.responseText.substring(0,3) == "001") {
firework.launch('Flow start triggered', 'success', 5000);
window.location.reload();
}
else if (xhttp.responseText.substring(0,3) == "002") {
firework.launch('Flow start scheduled. Start after round is completed', 'success', 5000);
}
else if (xhttp.responseText.substring(0,3) == "099") {
firework.launch('Flow start triggered, but start not possible (no flow task available)', 'danger', 5000);
}
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
*/
function HA_send_discovery_visibility() {
loadConfig(domainname);
ParseConfig();
category = getConfigCategory();
param = getConfigParameters();
if (category["MQTT"]["enabled"] && param["MQTT"]["HomeassistantDiscovery"]["value1"] == "true") {
document.getElementById("ManualControl").style.display="";
document.getElementById("HASendDiscovery").style.display="";
}
}
function HA_send_discovery() {
console.log("HA Discovery scheduled");
var url = getDomainname() + '/mqtt_publish_discovery';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
firework.launch('Sending HA discovery topics scheduled. The sending will be processed in state "Publish to MQTT"', 'success', 5000);
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
function start_livestream(streamFlashlight) {
if (streamPopup)
streamPopup.close();
if (streamFlashlight)
streamPopup = window.open(getDomainname() + '/stream?flashlight=true','LivestreamWithlight',streamWindowFeatures);
else
streamPopup = window.open(getDomainname() + '/stream','Livestream',streamWindowFeatures);
streamPopup.focus();
}
</script>
</div>
</body>
</html>

View File

@@ -1,8 +1,9 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<html>
<head>
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
<title>Info</title>
<meta charset="UTF-8" />
<meta charset="utf-8">
<style>
h1 {font-size: 2em;}
@@ -10,490 +11,190 @@ h2 {font-size: 1.5em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
th, td {
padding: 5px 5px 5px 5px;
border-width: 1px;
border-style: solid;
border-color: rgb(240, 240, 240);
div {
width: 350px;
padding: 10px 5px;
border: 1px solid #ccc;
font-family: arial;
font-size: 16px;
max-height: 35px;
}
output {
padding-left:5px;
}
table {
width: 660px;
padding: 5px;
border-collapse:collapse;
}
</style>
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
</head>
<body style="font-family: arial; padding: 0px 10px;">
<table>
<colgroup>
<col span="1" style="width: 35%;">
<col span="1" style="width: 65%;">
</colgroup>
<h3>Runtime Information</h3>
<table style="font-family: arial">
<tr>
<h3>Runtime Information</h3>
</tr>
<tr>
<td>Start time:</td>
<td><output id="starttime"></output></td>
</tr>
<tr>
<td>Uptime:</td>
<td><output id="uptime"></output></td>
</tr>
</table>
<table>
<colgroup>
<col span="1" style="width: 35%;">
<col span="1" style="width: 65%;">
</colgroup>
<tr>
<h3>Build Info</h3>
</tr>
<tr>
<td>Firmware version:</td>
<td>
<output id="firmware"></output>
Last restart:
</td>
<td>
<div id="starttime">
<object data="/starttime"></object>
</div>
</td>
</tr>
<tr>
<td>Firmware build time:</td>
<td>
<output id="build-time"></output>
Uptime:
</td>
<td>
<div id="uptime">
<object data="/uptime"></object>
</div>
</td>
</tr>
</table>
<h3>Build Info</h3>
<table style="font-family: arial">
<tr>
<td>
Firmware Version:
</td>
<td>
<div id="firmware" style="width: 700px">
<object data="/info?type=FirmwareVersion" style="width: 700px"></object>
</div>
</td>
</tr>
<tr>
<td>
Firmware Build Time:
</td>
<td>
<div id="build-time">
<object data="/info?type=BuildTime"></object>
</div>
</td>
</tr>
<tr>
<td>Web interface version:</td>
<td>
<output id="web-ui"></output>
Web Interface Version:
</td>
<td>
<div id="web-ui" style="width: 700px">
<object data="/info?type=HTMLVersion" style="width: 700px"></object>
</div>
</td>
</tr>
</table>
</table>
<table>
<colgroup>
<col span="1" style="width: 35%;">
<col span="1" style="width: 65%;">
</colgroup>
<h3>Host Info</h3>
<table style="font-family: arial">
<tr>
<h3>Host Info</h3>
</tr>
<tr>
<td>Hostname:</td>
<td>
<output id="hostname"></output>
Hostname:
</td>
<td>
<div id="Hostname">
<object data="/info?type=Hostname"></object>
</div>
</td>
</tr>
<tr>
<td>IP Address:</td>
<td>
<output id="IP-address"></output>
IP-Address:
</td>
<td>
<div id="IP">
<object data="/info?type=IP"></object>
</div>
</td>
</tr>
<tr>
<td>WLAN SSID:</td>
<td>
<output id="wlan-ssid"></output>
WLan-SSID:
</td>
<td>
<div id="SSID">
<object data="/info?type=SSID"></object>
</div>
</td>
</tr>
</table>
</table>
<table>
<colgroup>
<col span="1" style="width: 35%;">
<col span="1" style="width: 65%;">
</colgroup>
<h3>SD Card Info</h3>
<table style="font-family: arial">
<tr>
<h3>SD Card Info</h3>
</tr>
<tr>
<td>SD card manufacturer:</td>
<td>
<output id="SDCardManufacturer"></output>
SD Card Manufacturer:
</td>
<td>
<div id="SDCardManufacturer">
<object data="/info?type=SDCardManufacturer"></object>
</div>
</td>
</tr>
<tr>
<td>SD card name:</td>
<td>
<output id="SDCardName"></output>
SD Card Name:
</td>
<td>
<div id="SDCardName">
<object data="/info?type=SDCardName"></object>
</div>
</td>
</tr>
<tr>
<td>SD card size:</td>
<td>
<output id="SDCardCapacity"></output>
SD Card Size [MB]:
</td>
<td>
<div id="SDCardCapacity">
<object data="/info?type=SDCardCapacity"></object>
</div>
</td>
</tr>
<tr>
<td>SD card sector size:</td>
<td>
<output id="SDCardSectorSize"></output>
SD Card Sector Size [byte]:
</td>
<td>
<div id="SDCardSectorSize">
<object data="/info?type=SDCardSectorSize"></object>
</div>
</td>
</tr>
<tr>
<td>Partition size:</td>
<td>
<output id="SDCardPartitionSize"></output>
Partition Size [MB]:
</td>
<td>
<div id="SDPartitionSize">
<object data="/info?type=SDCardPartitionSize"></object>
</div>
</td>
</tr>
<tr>
<td>Partition free space:</td>
<td>
<output id="SDCardFreePartitionSpace"></output>
Partition Free Space [MB]:
</td>
<td>
<div id="SDFreePartitionSpace">
<object data="/info?type=SDCardFreePartitionSpace"></object>
</div>
</td>
</tr>
<tr>
<td>Partition allocation size:</td>
<td>
<output id="SDCardPartitionAllocationSize"></output>
Partition Allocation Size [byte]:
</td>
<td>
<div id="SDCardPartitionAllocationSize">
<object data="/info?type=SDCardPartitionAllocationSize"></object>
</div>
</td>
</tr>
</table>
<table>
<colgroup>
<col span="1" style="width: 35%;">
<col span="1" style="width: 65%;">
</colgroup>
<tr>
<h3>Memory Info</h3>
</tr>
<tr>
<td>Total Free (Int + Ext):</td>
<td>
<output id="RAMTotalFree"></output>
</td>
</tr>
<tr>
<td>Ext. RAM - Free:</td>
<td>
<output id="ExtRAMFree"></output>
</td>
</tr>
<tr>
<td>Ext. RAM - Largest Free Block:</td>
<td>
<output id="ExtRAMLargestFree"></output>
</td>
</tr>
<tr>
<td>Ext. RAM - Min Free:</td>
<td>
<output id="ExtRAMMinFree"></output>
</td>
</tr>
<tr>
<td>Int. RAM - Free:</td>
<td>
<output id="IntRAMFree"></output>
</td>
</tr>
<tr>
<td>Int. RAM - Largest Free Block:</td>
<td>
<output id="IntRAMLargestFree"></output>
</td>
</tr>
<tr>
<td>Int. RAM - Min Free:</td>
<td>
<output id="IntRAMMinFree"></output>
</td>
</tr>
</table>
</table>
<h3>Copyright</h3>
Copyright &copy; 2020 - 2023 by <a href="https://github.com/jomjol/AI-on-the-edge-device" target=_blank>Jomjol</a> and others.
</body>
</html>
<script type="text/javascript">
function loadLastRestart()
{
url = getDomainname() + '/starttime';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
//Input format: 19700101-010019
var timestamp = xhttp.response.substr(6,2) + "." +
xhttp.response.substr(4,2) + "." +
xhttp.response.substr(0,4) + " " +
xhttp.response.substr(9,2) + ":" +
xhttp.response.substr(11,2) + ":" +
xhttp.response.substr(13,2);
document.getElementById("starttime").value = timestamp;
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
function loadUptime()
{
url = getDomainname() + '/uptime';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("uptime").value = xhttp.response;
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
function loadFWVersion()
{
url = getDomainname() + '/info?type=FirmwareVersion';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("firmware").value = xhttp.response;
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
function loadFWBuildTime()
{
url = getDomainname() + '/info?type=BuildTime';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
// Input format: 2023-04-02 10:56
var timestamp = xhttp.response.substr(8,2) + "." +
xhttp.response.substr(5,2) + "." +
xhttp.response.substr(0,4) + " " +
xhttp.response.substr(11,2) + ":" +
xhttp.response.substr(14,2);
document.getElementById("build-time").value = timestamp;
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
function loadHTMLVersion()
{
url = getDomainname() + '/info?type=HTMLVersion';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("web-ui").value = xhttp.response;
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
function loadHostname()
{
url = getDomainname() + '/info?type=Hostname';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("hostname").value = xhttp.response;
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
function loadIPAddress()
{
url = getDomainname() + '/info?type=IP';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("IP-address").value = xhttp.response;
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
function loadWLANSSID()
{
url = getDomainname() + '/info?type=SSID';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("WLAN-SSID").value = xhttp.response;
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
function loadWLANSSID()
{
url = getDomainname() + '/info?type=SSID';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("wlan-ssid").value = xhttp.response;
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
function loadWLANSSID()
{
url = getDomainname() + '/info?type=SSID';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("wlan-ssid").value = xhttp.response;
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
function loadSDCardManufacturer()
{
url = getDomainname() + '/info?type=SDCardManufacturer';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("SDCardManufacturer").value = xhttp.response;
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
function loadSDCardName()
{
url = getDomainname() + '/info?type=SDCardName';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("SDCardName").value = xhttp.response;
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
function loadSDCardCapacity()
{
url = getDomainname() + '/info?type=SDCardCapacity';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("SDCardCapacity").value = xhttp.response + " MB";
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
function loadSDCardSectorSize()
{
url = getDomainname() + '/info?type=SDCardSectorSize';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("SDCardSectorSize").value = xhttp.response + " byte";
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
function loadSDCardPartitionSize()
{
url = getDomainname() + '/info?type=SDCardPartitionSize';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("SDCardPartitionSize").value = xhttp.response + " MB";
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
function loadSDCardFreePartitionSpace()
{
url = getDomainname() + '/info?type=SDCardFreePartitionSpace';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("SDCardFreePartitionSpace").value = xhttp.response + " MB";
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
function loadSDCardPartitionAllocationSize()
{
url = getDomainname() + '/info?type=SDCardPartitionAllocationSize';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("SDCardPartitionAllocationSize").value = xhttp.response + " byte";
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
function loadMemoryInfo()
{
url = getDomainname() + '/heap';
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
data = xhttp.response.split("|");
document.getElementById("RAMTotalFree").value = data[0].split("l: ")[1] + " byte";
document.getElementById("IntRAMFree").value = data[4].split(":")[1] + " byte";
document.getElementById("IntRAMLargestFree").value = data[5].split(":")[1] + " byte";
document.getElementById("IntRAMMinFree").value = data[6].split(":")[1] + " byte";
document.getElementById("ExtRAMFree").value = data[1].split(":")[1] + " byte";
document.getElementById("ExtRAMLargestFree").value = data[2].split(":")[1] + " byte";
document.getElementById("ExtRAMMinFree").value = data[3].split(":")[1] + " byte";
}
}
xhttp.open("GET", url, true);
xhttp.send();
}
function init()
{
loadMemoryInfo();
loadLastRestart();
loadUptime();
loadFWVersion();
loadFWBuildTime();
loadHTMLVersion();
loadHostname();
loadIPAddress();
loadWLANSSID();
loadSDCardManufacturer();
loadSDCardName();
loadSDCardCapacity();
loadSDCardSectorSize();
loadSDCardPartitionSize();
loadSDCardFreePartitionSpace();
loadSDCardPartitionAllocationSize();
}
init();
</script>

View File

@@ -1,19 +1,16 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<html>
<head>
<title>Log Viewer</title>
<style>
html,
body {
height: 100%;
margin: 1px;
margin: 2px;
}
.box {
display: flex;
flex-flow: column;
height: 99.75%;
height: 100%;
}
.box .row.header {
@@ -32,27 +29,21 @@
font-family: 'Courier New', Courier, monospace;
font-size: small;
}
.button {
padding: 5px 10px;
width: 190px;
font-size: 16px;
}
</style>
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
</head>
<body>
<div class="box">
<div class="row header">
<button class="button" onClick="reload();">Reload</button>
<button class="button" onClick="window.open(getDomainname() + '/logfileact');">Show Full Log</button>
<button class="button" onClick="window.location.href = getDomainname() + '/fileserver/log/message/'">Show Older Log Files</button>
<button onClick="reload();">Reload</button>
<button onClick="window.open(getDomainname() + '/logfileact');">Show full log</button>
<button onClick="window.location.href = getDomainname() + '/fileserver/log/message/'">Show older log files</button>
</div>
<div class="row content" id="log"><br><br><br><b>Loading logfile, please wait...</b></div>
<div class="row content" id="log"><br><br><br><b>Loading Logfile, please wait...</b></div>
<div class="row footer">
<button class="button" onClick="reload();">Reload</button>
<button class="button" onClick="window.open(getDomainname() + '/logfileact');">Show Full Log</button>
<button class="button" onClick="window.location.href = getDomainname() + '/fileserver/log/message/'">Show Older Log Files</button>
<button onClick="reload();">Reload</button>
<button onClick="window.open(getDomainname() + '/logfileact');">Show full log</button>
<button onClick="window.location.href = getDomainname() + '/fileserver/log/message/'">Show older log files</button>
</div>
</div>
</body>

View File

@@ -1,25 +1,27 @@
<!DOCTYPE html>
<html lang="en" xml:lang="en">
<html>
<head>
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
<title>OTA Update</title>
<meta charset="UTF-8" />
<meta charset="utf-8">
<style>
h1 {font-size: 2em;}
h2 {font-size: 1.5em; margin-block-start: 0.0em; margin-block-end: 0.2em;}
h2 {font-size: 1.5em;}
h3 {font-size: 1.2em;}
p {font-size: 1em;}
input[type=file] {
width: 660px;
padding: 5px 0px;
input[type=number] {
width: 138px;
padding: 10px 5px;
display: inline-block;
border: 1px solid #ccc;
font-size: 16px;
}
.button {
padding: 5px 10px;
width: 205px;
padding: 10px 20px;
width: 211px;
font-size: 16px;
}
</style>
@@ -49,11 +51,11 @@
<form id="upload_form" enctype="multipart/form-data" method="post">
<input type="file" accept=".bin,.zip,.tfl,.tflite" name="file_selector" id="file_selector" onchange="validate_file()"><br><br>
<button class="button" id="start_OTA_button" type="button" onclick="start_OTA()" disabled>Upload And Install</button>
<button class="button" style="width:300px" id="start_OTA_button" type="button" onclick="start_OTA()" disabled>Upload and install</button>
<br><br>
<progress id="progressBar" value="0" max="100" style="width:600px;"></progress>
<h3><span id="status">Status: Idle</span></h3>
<p id="loaded_n_total"></p>
<h3><span id="status">Status: idle</span></h3>
</form>
@@ -260,10 +262,10 @@
function progressHandler(event) {
_("loaded_n_total").innerHTML = "Uploaded " + (event.loaded / 1024 / 1024).toFixed(2) +
" MB of " + (event.total / 1024/ 1024).toFixed(2) + " MB";
" MBytes of " + (event.total / 1024/ 1024).toFixed(2) + " MBytes.";
var percent = (event.loaded / event.total) * 100;
_("progressBar").value = Math.round(percent);
_("status").innerHTML = "Status: " + Math.round(percent) + "% uploaded. Please wait...";
_("status").innerHTML = "Status: " + Math.round(percent) + "% uploaded... please wait";
}

Some files were not shown because too many files have changed in this diff Show More