Преглед изворни кода

Merge branch 'jomjol:master' into master

CaCO3 пре 3 година
родитељ
комит
18be9afbe1
66 измењених фајлова са 1813 додато и 461 уклоњено
  1. 106 0
      .github/workflows/build.yaml
  2. 21 0
      README.md
  3. 95 21
      code/components/jomjol_fileserver_ota/server_file.cpp
  4. 2 0
      code/components/jomjol_fileserver_ota/server_file.h
  5. 3 0
      code/components/jomjol_fileserver_ota/server_help.cpp
  6. 97 22
      code/components/jomjol_fileserver_ota/server_ota.cpp
  7. 31 52
      code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp
  8. 3 7
      code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.h
  9. 4 4
      code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h
  10. 5 0
      code/components/jomjol_flowcontroll/ClassFlowImage.cpp
  11. 93 10
      code/components/jomjol_flowcontroll/ClassFlowMQTT.cpp
  12. 1 0
      code/components/jomjol_flowcontroll/ClassFlowMQTT.h
  13. 61 20
      code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp
  14. 4 4
      code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h
  15. 47 2
      code/components/jomjol_helper/Helper.cpp
  16. 3 0
      code/components/jomjol_helper/Helper.h
  17. 1 0
      code/components/jomjol_logfile/ClassLogFile.cpp
  18. 104 15
      code/components/jomjol_mqtt/interface_mqtt.cpp
  19. 2 2
      code/components/jomjol_mqtt/interface_mqtt.h
  20. 66 1
      code/components/jomjol_tfliteclass/server_tflite.cpp
  21. 2 2
      code/main/version.cpp
  22. 1 1
      code/main/version.h
  23. 4 1
      code/platformio.ini
  24. 1 1
      code/sdkconfig
  25. 1 1
      code/sdkconfig.esp32cam
  26. 178 8
      code/test/components/jomjol-flowcontroll/test_flowpostprocessing.cpp
  27. 2 2
      code/version.cpp
  28. BIN
      firmware/bootloader.bin
  29. BIN
      firmware/firmware.bin
  30. BIN
      firmware/html.zip
  31. BIN
      sd-card/config/ana-class100_0120_s1_q.tflite
  32. BIN
      sd-card/config/ana-class100_0130_s1_q.tflite
  33. 7 7
      sd-card/config/config.ini
  34. BIN
      sd-card/config/dig-class100-0130_s2_q.tflite
  35. BIN
      sd-card/config/dig-class100_0120_s2_q.tflite
  36. 0 0
      sd-card/html/FileSaver.min.js
  37. 0 0
      sd-card/html/FileSaver.min.js.map
  38. 140 0
      sd-card/html/backup.html
  39. 31 0
      sd-card/html/common.js
  40. 15 2
      sd-card/html/edit_alignment.html
  41. 36 7
      sd-card/html/edit_analog.html
  42. 6 2
      sd-card/html/edit_config_param.html
  43. 14 2
      sd-card/html/edit_digits.html
  44. 1 4
      sd-card/html/edit_explain_0.html
  45. 1 4
      sd-card/html/edit_explain_6.html
  46. 14 0
      sd-card/html/edit_reference.html
  47. 1 4
      sd-card/html/explain_0.html
  48. 1 3
      sd-card/html/explain_1.html
  49. 1 4
      sd-card/html/explain_2.html
  50. 1 3
      sd-card/html/explain_3.html
  51. 1 4
      sd-card/html/explain_4.html
  52. 1 4
      sd-card/html/explain_5.html
  53. 1 3
      sd-card/html/explain_6.html
  54. 1 1
      sd-card/html/gethost.js
  55. 103 94
      sd-card/html/index.html
  56. 113 90
      sd-card/html/index_configure.html
  57. 3 1
      sd-card/html/info.html
  58. 12 0
      sd-card/html/jszip.min.js
  59. 18 35
      sd-card/html/ota_page.html
  60. 207 0
      sd-card/html/ota_page_new.html
  61. 1 1
      sd-card/html/ota_page_v2.html
  62. 9 6
      sd-card/html/readconfigparam.js
  63. 1 1
      sd-card/html/setup.html
  64. 100 0
      sd-card/html/style.css
  65. 1 1
      sd-card/html/version.txt
  66. 34 2
      sd-card/html/wasserzaehler_roi.html

+ 106 - 0
.github/workflows/build.yaml

@@ -0,0 +1,106 @@
+name: Build and Pack
+
+on: [push]
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v2
+
+    - name: Cache PlatformIO
+      uses: actions/cache@v2
+      with:
+        path: ~/.platformio
+        key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
+
+    - name: Set up Python
+      uses: actions/setup-python@v2
+    - name: Install PlatformIO
+      run: |
+        python -m pip install --upgrade pip
+        pip install --upgrade platformio
+
+    - name: Set Variables
+      id: vars
+      run: |
+        echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
+        echo "::set-output name=date_time::$(git log -1 --format="%at" | xargs -I{} date -d @{} '+%Y-%m-%d %H:%M:%S')"
+        echo "::set-output name=date_time_filename::$(git log -1 --format="%at" | xargs -I{} date -d @{} '+%Y-%m-%d_%H-%M-%S')"
+        #echo "::set-output name=version_string::${{ steps.vars.outputs.date_time_filename }}__${{ github.ref_name }}_(${{ steps.vars.outputs.sha_short }})"
+        #echo "Version String: ${{ steps.vars.outputs.version_string }}"
+
+
+    - name: Set Version used in HTML Info page
+      run: echo "${{ steps.vars.outputs.date_time }}, ${{ github.ref_name }} (${{ steps.vars.outputs.sha_short }})" > "sd-card/html/version.txt"
+
+
+    - name: Build Firmware
+#      run: mkdir -p ./code/.pio/build/esp32cam/; touch ./code/.pio/build/esp32cam/firmware.bin # Testing
+      run: cd code; platformio run --environment esp32cam
+
+
+
+    # Old OTA concept
+    # firmware__*.zip needs to be unpacked before attaching to the release!
+    # The bin filename can contain versioning.
+    - name: Rename firmware file to contain versioning (old ota)
+      run: |
+        mkdir -p ./dist_old_ota
+        cp "./code/.pio/build/esp32cam/firmware.bin" "./dist_old_ota/firmware__${{ steps.vars.outputs.date_time_filename }}__${{ github.ref_name }}_(${{ steps.vars.outputs.sha_short }}).bin"
+        ls -l ./dist_old_ota
+
+    - name: Upload Firmware artifact (old OTA concept)
+      uses: actions/upload-artifact@v3
+      with:
+        name: "firmware__extract_before_upload__only_needed_for_migration_from_11.2.0"
+        path: ./dist_old_ota/*
+
+    - name: Upload Web interface artifact (old OTA concept)
+      uses: actions/upload-artifact@v3
+      with:
+        name: "html__only_needed_for_migration_from_11.2.0__${{ steps.vars.outputs.date_time_filename }}__${{ github.ref_name }}_(${{ steps.vars.outputs.sha_short }})"
+        path: ./sd-card/html/*
+
+
+
+    # New OTA concept
+    # update__version.zip file with following content:
+    #  - /firmware.bin
+    #  - (optional) /html/*
+    #  - (optional) /config/*.tfl        
+    - name: Prepare update.zip artifact
+      run: |
+        mkdir -p ./dist
+        cp "./code/.pio/build/esp32cam/firmware.bin" "dist/firmware.bin"
+
+    - name: Upload update.zip Artifact (Firmware only)
+      uses: actions/upload-artifact@v3
+      with:
+        name: "update_firmware_only__${{ steps.vars.outputs.date_time_filename }}__${{ github.ref_name }}_(${{ steps.vars.outputs.sha_short }})"
+        path: ./dist/*
+        
+
+    - name: Prepare update.zip artifact (Firmware + Web UI)
+      run: cp -r ./sd-card/html ./dist/
+
+    - name: Upload update.zip artifact (Firmware + Web UI)
+      uses: actions/upload-artifact@v3
+      with:
+        name: "update_firmware+web_ui__${{ steps.vars.outputs.date_time_filename }}__${{ github.ref_name }}_(${{ steps.vars.outputs.sha_short }})"
+        path: ./dist/*
+        
+
+    - name: Prepare update.zip artifact (Firmware + Web UI + CNN)
+      run: |
+        mkdir ./dist/config/
+        cp ./sd-card/config/*.tfl ./dist/config/ 2>/dev/null || true
+        cp ./sd-card/config/*.tflite ./dist/config/ 2>/dev/null || true
+
+    - name: Upload update.zip artifact (Firmware + Web UI + CNN)
+      uses: actions/upload-artifact@v3
+      with:
+        name: "update_firmware+web_ui+cnn__${{ steps.vars.outputs.date_time_filename }}__${{ github.ref_name }}_(${{ steps.vars.outputs.sha_short }})"
+        path: ./dist/*

+ 21 - 0
README.md

@@ -40,6 +40,27 @@ In other cases you can contact the developer via email: <img src="https://raw.gi
 
 ------
 
+##### 11.3.0 - Intermediate Digits (2022-09-17)
+
+- **ATTENTION**: 
+  - first update the 'firmware.bin' and ensure that the new version is running
+
+  - Only afterwards update the 'html.zip'
+  
+  - Otherwise the downwards compatibility of the new counter clockwise feature is not given and you end in a reboot loop, that needs manual flashing!
+  
+
+
+- Increased precision (more than 6-7 digits)
+- Implements Counter Clockwise Analog Pointers
+- Improved post processing algorithm
+- Debugging: intensive use of testcases
+- MQTT: improved handling, extended logging, automated reconnect
+- HTML: Backup Option for Configuration
+- HTML: Improved Reboot
+- HTML: Update WebUI (Reboot, Infos, CPU Temp, RSSI)
+- This version is largely also based on the work of **[caco3](https://github.com/caco3)**,  **[adellafave](https://github.com/adellafave)**,  **[haverland](https://github.com/haverland)**,  **[stefanbode](https://github.com/stefanbode)**, **[PLCHome](https://github.com/PLCHome)**
+
 ##### 11.2.0 - Intermediate Digits (2022-08-28)
 
 - Updated Tensorflow / TFlite to newest tflite (version as of 2022-07-27)

+ 95 - 21
code/components/jomjol_fileserver_ota/server_file.cpp

@@ -120,16 +120,6 @@ esp_err_t get_tflite_file_handler(httpd_req_t *req)
 }
 
 
-/* Handler to redirect incoming GET request for /index.html to /
- * This can be overridden by uploading file with same name */
-// static esp_err_t index_html_get_handler(httpd_req_t *req)
-// {
-//     httpd_resp_set_status(req, "307 Temporary Redirect");
-//     httpd_resp_set_hdr(req, "Location", "/");
-//     httpd_resp_send(req, NULL, 0);  // Response body can be empty
-//     return ESP_OK;
-// }
-
 /* Send HTTP response with a run-time generated html consisting of
  * a list of all files and folders under the requested path.
  * In case of SPIFFS this returns empty list when path is any
@@ -716,6 +706,101 @@ void delete_all_in_directory(std::string _directory)
     closedir(dir);
 }
 
+std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::string _target_bin, std::string _main)
+{
+    int i, sort_iter;
+    mz_bool status;
+    size_t uncomp_size;
+    mz_zip_archive zip_archive;
+    void* p;
+    char archive_filename[64];
+    std::string zw, ret = "";
+//    static const char* s_Test_archive_filename = "testhtml.zip";
+
+    printf("miniz.c version: %s\n", MZ_VERSION);
+    printf("Zipfile: %s\n", _in_zip_file.c_str());
+    printf("Target Dir ZIP: %s\n", _target_zip.c_str());
+    printf("Target Dir BIN: %s\n", _target_bin.c_str());
+
+    // Now try to open the archive.
+    memset(&zip_archive, 0, sizeof(zip_archive));
+    status = mz_zip_reader_init_file(&zip_archive, _in_zip_file.c_str(), 0);
+    if (!status)
+    {
+        printf("mz_zip_reader_init_file() failed!\n");
+        return ret;
+    }
+
+    // Get and print information about each file in the archive.
+    int numberoffiles = (int)mz_zip_reader_get_num_files(&zip_archive);
+    for (sort_iter = 0; sort_iter < 2; sort_iter++)
+    {
+        memset(&zip_archive, 0, sizeof(zip_archive));
+        status = mz_zip_reader_init_file(&zip_archive, _in_zip_file.c_str(), sort_iter ? MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY : 0);
+        if (!status)
+        {
+            printf("mz_zip_reader_init_file() failed!\n");
+            return ret;
+        }
+
+        for (i = 0; i < numberoffiles; i++)
+        {
+            mz_zip_archive_file_stat file_stat;
+            mz_zip_reader_file_stat(&zip_archive, i, &file_stat);
+            sprintf(archive_filename, file_stat.m_filename);
+ 
+            // Try to extract all the files to the heap.
+            p = mz_zip_reader_extract_file_to_heap(&zip_archive, archive_filename, &uncomp_size, 0);
+            if (!p)
+            {
+                printf("mz_zip_reader_extract_file_to_heap() failed!\n");
+                mz_zip_reader_end(&zip_archive);
+                return ret;
+            }
+
+            // Save to File.
+            zw = std::string(archive_filename);
+            if (toUpper(zw) == "FIRMWARE.BIN")
+            {
+                zw = _target_bin + zw;
+                ret = zw;
+            }
+            else
+            {
+                std::string _dir = getDirectory(zw);
+
+                if (_dir.length() > 0)
+                {
+                    zw = _main + zw;
+                }
+                else
+                {
+                    zw = _target_zip + zw;
+                }
+
+            }
+
+            printf("Filename to extract: %s", zw.c_str());
+            DeleteFile(zw);
+            FILE* fpTargetFile = OpenFileAndWait(zw.c_str(), "wb");
+            fwrite(p, 1, (uint)uncomp_size, fpTargetFile);
+            fclose(fpTargetFile);
+
+            printf("Successfully extracted file \"%s\", size %u\n", archive_filename, (uint)uncomp_size);
+            //            printf("File data: \"%s\"\n", (const char*)p);
+
+            // We're done.
+            mz_free(p);
+        }
+
+        // Close the archive, freeing any resources it was using
+        mz_zip_reader_end(&zip_archive);
+    }
+
+    printf("Success.\n");
+    return ret;
+}
+
 void unzip(std::string _in_zip_file, std::string _target_directory){
     int i, sort_iter;
     mz_bool status;
@@ -860,15 +945,4 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
     };
     httpd_register_uri_handler(server, &file_delete);
 
-
-    /* URI handler for getting tflite files from server */
-/*
-    httpd_uri_t file_tflite = {
-        .uri       = "/tflite",   // Match all URIs of type /delete/path/to/file
-        .method    = HTTP_GET,
-        .handler   = get_tflite_file_handler,
-        .user_ctx  = server_data    // Pass server data as context
-    };
-    httpd_register_uri_handler(server, &file_tflite);
-*/
 }

+ 2 - 0
code/components/jomjol_fileserver_ota/server_file.h

@@ -4,6 +4,8 @@
 void register_server_file_uri(httpd_handle_t server, const char *base_path);
 
 void unzip(std::string _in_zip_file, std::string _target_directory);
+std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::string _target_bin, std::string _main = "/sdcard/");
+
 
 void delete_all_in_directory(std::string _directory);
 

+ 3 - 0
code/components/jomjol_fileserver_ota/server_help.cpp

@@ -43,6 +43,7 @@ esp_err_t send_file(httpd_req_t *req, std::string filename)
     }
 
     ESP_LOGI(TAG, "Sending file : %s ...", filename.c_str());
+//    httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
     set_content_type_from_file(req, filename.c_str());
 
     /* Retrieve the pointer to scratch buffer for temporary storage */
@@ -120,6 +121,8 @@ esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filename)
         return httpd_resp_set_type(req, "image/x-icon");
     } else if (IS_FILE_EXT(filename, ".js")) {
         return httpd_resp_set_type(req, "text/javascript");
+    } else if (IS_FILE_EXT(filename, ".css")) {
+        return httpd_resp_set_type(req, "text/css");
     }
     /* This is a limited set only */
     /* For any other type always set as plain text */

+ 97 - 22
code/components/jomjol_fileserver_ota/server_ota.cpp

@@ -50,6 +50,8 @@ static char ota_write_data[BUFFSIZE + 1] = { 0 };
 #define OTA_URL_SIZE 256
 static const char *TAGPARTOTA = "server_ota";
 
+esp_err_t handler_reboot(httpd_req_t *req);
+
 
 static void infinite_loop(void)
 {
@@ -207,24 +209,6 @@ static void print_sha256 (const uint8_t *image_hash, const char *label)
 
 static bool diagnostic(void)
 {
-/*
-    gpio_config_t io_conf;
-    io_conf.intr_type    = (gpio_int_type_t) GPIO_PIN_INTR_DISABLE;
-    io_conf.mode         = GPIO_MODE_INPUT;
-    io_conf.pin_bit_mask = (1ULL << CONFIG_EXAMPLE_GPIO_DIAGNOSTIC);
-    io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
-    io_conf.pull_up_en   = GPIO_PULLUP_ENABLE;
-    gpio_config(&io_conf);
-
-    ESP_LOGI(TAGPARTOTA, "Diagnostics (5 sec)...");
-    vTaskDelay(5000 / portTICK_PERIOD_MS);
-
-    bool diagnostic_is_ok = gpio_get_level(CONFIG_EXAMPLE_GPIO_DIAGNOSTIC);
-
-    gpio_reset_pin(CONFIG_EXAMPLE_GPIO_DIAGNOSTIC);
-
-    return diagnostic_is_ok;
-*/
     return true;
 }
 
@@ -326,7 +310,7 @@ esp_err_t handler_ota_update(httpd_req_t *req)
         
         if (httpd_query_key_value(_query, "task", _valuechar, 30) == ESP_OK)
         {
-            printf("task is found"); printf(_valuechar); printf("\n"); 
+            printf("task is found: "); printf(_valuechar); printf("\n"); 
             _task = std::string(_valuechar);
         }
 
@@ -344,16 +328,105 @@ esp_err_t handler_ota_update(httpd_req_t *req)
 
     };
 
+    if (_task.compare("update") == 0)
+    {
+        std::string filetype = toUpper(getFileType(fn));
+        if (filetype.length() == 0)
+        {
+            std::string zw = "Update failed - no file specified (zip, bin, tfl, tlite)";
+            httpd_resp_sendstr_chunk(req, zw.c_str());
+            httpd_resp_sendstr_chunk(req, NULL);  
+            return ESP_OK;        
+        }
+
+
+        if ((filetype == "TFLITE") || (filetype == "TFL"))
+        {
+            std::string out = "/sdcard/config/" + getFileFullFileName(fn);
+            DeleteFile(out);
+            CopyFile(fn, out);
+            DeleteFile(fn);
+
+            const char*  resp_str = "Neural Network File copied.";
+            httpd_resp_sendstr_chunk(req, resp_str);
+            httpd_resp_sendstr_chunk(req, NULL);  
+            return ESP_OK;
+        }
+
+
+        if (filetype == "ZIP")
+        {
+            std::string in, out, outbin, zw, retfirmware;
+
+//            in = "/sdcard/firmware/html.zip";
+            out = "/sdcard/html";
+            outbin = "/sdcard/firmware";
+
+//            delete_all_in_directory(out);
+
+            retfirmware = unzip_new(fn, out+"/", outbin+"/");
+
+            if (retfirmware.length() > 0)
+            {
+                filetype = "BIN";
+                fn = retfirmware;
+                zw = "HTML Update Successfull!<br><br>Additioal firmware found in ZIP file.\n";
+                httpd_resp_sendstr_chunk(req, zw.c_str());
+            }
+            else
+            {
+                zw = "HTML Update Successfull!<br><br>No reboot necessary.\n";
+                httpd_resp_sendstr_chunk(req, zw.c_str());
+                httpd_resp_sendstr_chunk(req, NULL);  
+                return ESP_OK;        
+            }
+        }
+
+
+        if (filetype == "BIN")
+        {
+            const char* resp_str;    
+            KillTFliteTasks();
+            gpio_handler_deinit();
+            if (ota_update_task(fn))
+            {
+//                resp_str = "rebooting - Firmware Update Successfull!<br><br>You can restart now.";
+//                httpd_resp_send(req, resp_str, strlen(resp_str));  
+//                httpd_resp_sendstr_chunk(req, NULL);  
+                return handler_reboot(req);                
+            }
+            else
+            {
+                resp_str = "Error during Firmware Update!!!<br><br>Please check output of console.";
+            }
+
+            httpd_resp_send(req, resp_str, strlen(resp_str));  
+
+            #ifdef DEBUG_DETAIL_ON 
+                LogFile.WriteHeapInfo("handler_ota_update - Done");    
+            #endif
+
+            return ESP_OK;
+        }
+
+
+        std::string zw = "Update failed - no valid file specified (zip, bin, tfl, tlite)";
+        httpd_resp_sendstr_chunk(req, zw.c_str());
+        httpd_resp_sendstr_chunk(req, NULL);  
+        return ESP_OK;        
+    }
+
+
     if (_task.compare("unziphtml") == 0)
     {
         std::string in, out, zw;
 
         in = "/sdcard/firmware/html.zip";
-        out = "/sdcard/html/";
+        out = "/sdcard/html";
 
         delete_all_in_directory(out);
 
-        unzip(in, out);
+        unzip(in, out+"/");
         zw = "HTML Update Successfull!<br><br>No reboot necessary";
         httpd_resp_sendstr_chunk(req, zw.c_str());
         httpd_resp_sendstr_chunk(req, NULL);  
@@ -371,6 +444,8 @@ esp_err_t handler_ota_update(httpd_req_t *req)
             unlink(fn.c_str());
         }
         /* Respond with an empty chunk to signal HTTP response completion */
+        std::string zw = "file deleted!\n";
+        httpd_resp_sendstr_chunk(req, zw.c_str());
         httpd_resp_send_chunk(req, NULL, 0);
         return ESP_OK;
     }
@@ -437,7 +512,7 @@ esp_err_t handler_reboot(httpd_req_t *req)
 
     LogFile.WriteToFile("handler_reboot");
     ESP_LOGI(TAGPARTOTA, "!!! System will restart within 5 sec!!!");
-    const char* resp_str = "<body style='font-family: arial'> <h3 id=t></h3></body><script>var h='Rebooting!<br>The page will automatically reload.<br>'; document.getElementById('t').innerHTML=h; setInterval(function (){h +='.'; document.getElementById('t').innerHTML=h; fetch(window.location.hostname,{mode: 'no-cors'}).then(r=>{window.location.replace('/wasserzaehler_roi.html');})}, 1000);</script>";
+    const char* resp_str = "<body style='font-family: arial'> <h3 id=t></h3></body><script>var h='Rebooting!<br>The page will automatically reload after around 25s.<br>'; document.getElementById('t').innerHTML=h; setInterval(function (){h +='.'; document.getElementById('t').innerHTML=h; fetch(window.location.hostname,{mode: 'no-cors'}).then(r=>{parent.location.href=('/index.html');})}, 1000);</script>";
     httpd_resp_send(req, resp_str, strlen(resp_str)); 
     
     doReboot();

+ 31 - 52
code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp

@@ -10,7 +10,7 @@
 
 static const char* TAG = "flow_analog";
 
-bool debugdetailgeneral = false;
+bool debugdetailgeneral = true;
 
 ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype) : ClassFlowImage(NULL, TAG)
 {
@@ -201,7 +201,7 @@ int ClassFlowCNNGeneral::ZeigerEvalHybridNeu(float zahl, float zahl_vorgaenger,
     if (AnalogerVorgaenger)
     {
 //        result = ZeigerEvalAnalogToDigitNeu(zahl, eval_vorgaenger);
-        result = ZeigerEvalAnalogToDigitNeu(zahl, zahl_vorgaenger);
+        result = ZeigerEvalAnalogToDigitNeu(zahl, zahl_vorgaenger, eval_vorgaenger);
         if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEvalHybridNeu - Analoger Vorgänger, Bewertung über ZeigerEvalAnalogNeu = " + std::to_string(result) +
                                                     " zahl: " + std::to_string(zahl) + " zahl_vorgaenger = " + std::to_string(zahl_vorgaenger)+ " eval_vorgaenger = " + std::to_string(eval_vorgaenger) + " DigitalUnschaerfe = " +  std::to_string(DigitalUnschaerfe));
         return result;
@@ -232,20 +232,20 @@ int ClassFlowCNNGeneral::ZeigerEvalHybridNeu(float zahl, float zahl_vorgaenger,
     }
 
     // bleibt nur >= 9.5 --> noch kein Nulldurchgang --> 2.8 --> 2, und 3.1 --> 2
-    // hier auf 4 reduziert, da erst ab Vorgänder 9 anfängt umzustellen. Bei 9.5 Vorgänger kann die aktuelle
-    // Zahl noch x.4 - x.5 sein.
+    // alles >=x.4 kann als aktuelle Zahl gelten im Übergang. Bei 9.5 Vorgänger kann die aktuelle
+    // Zahl noch x.6 - x.7 sein. 
     if (ergebnis_nachkomma >= 4)
         result =  ergebnis_vorkomma;
     else
         result =  (ergebnis_vorkomma - 1 + 10) % 10;
 
     if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEvalHybridNeu - KEIN Analoger Vorgänger, >= 9.5 --> noch kein Nulldurchgang = " + std::to_string(result) +
-                                                " zahl: " + std::to_string(zahl) + " zahl_vorgaenger = " + std::to_string(zahl_vorgaenger)+ " eval_vorgaenger = " + std::to_string(eval_vorgaenger) + " DigitalUnschaerfe = " +  std::to_string(DigitalUnschaerfe));
+                                                " zahl: " + std::to_string(zahl) + " zahl_vorgaenger = " + std::to_string(zahl_vorgaenger)+ " eval_vorgaenger = " + std::to_string(eval_vorgaenger) + " DigitalUnschaerfe = " +  std::to_string(DigitalUnschaerfe) + " ergebnis_nachkomma = " + std::to_string(ergebnis_nachkomma));
     return result;
 }
 
 
-int ClassFlowCNNGeneral::ZeigerEvalAnalogToDigitNeu(float zahl, float ziffer_vorgaenger)
+int ClassFlowCNNGeneral::ZeigerEvalAnalogToDigitNeu(float zahl, float ziffer_vorgaenger,  int eval_vorgaenger)
 {
     int result;
     int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
@@ -272,7 +272,8 @@ int ClassFlowCNNGeneral::ZeigerEvalAnalogToDigitNeu(float zahl, float ziffer_vor
         return result;
     }  
 
-    if (ziffer_vorgaenger <= 1)  // Nulldurchgang hat stattgefunden (!Bewertung über Prev_value und nicht Zahl!) --> hier aufrunden (2.8 --> 3, aber auch 3.1 --> 3)
+    if (ziffer_vorgaenger <= 1 && eval_vorgaenger<9)  // Nulldurchgang hat stattgefunden (!Bewertung über Prev_value und nicht Zahl!) --> hier aufrunden (2.8 --> 3, aber auch 3.1 --> 3)
+        // aber Sonderfall ziffer_vorgaeger = 0.1 vor_vorgaenger 9.9 => eval_vorgaenger ist 9, damit hat Nulldurchgang nicht stattgefunden.
     {
         if (ergebnis_nachkomma > 5)
             result =  (ergebnis_vorkomma + 1) % 10;
@@ -339,38 +340,6 @@ int ClassFlowCNNGeneral::ZeigerEvalAnalogNeu(float zahl, int ziffer_vorgaenger)
 
 }
 
-/*
-int ClassFlowCNNGeneral::ZeigerEval(float zahl, int ziffer_vorgaenger)
-{   
-    int ergebnis_nachkomma = ((int) floor(zahl * 10) + 10) % 10;
-    int ergebnis_vorkomma = ((int) floor(zahl) + 10) % 10;
-    int ergebnis;
-    float ergebnis_rating;
-    if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEval erg_v=" + std::to_string(ergebnis_vorkomma) + ", erg_n=" + std::to_string(ergebnis_nachkomma) + ", ziff_v=" + std::to_string(ziffer_vorgaenger));
-
-    if (ziffer_vorgaenger == -1)
-        return ergebnis_vorkomma % 10;
-
-    // Ist die aktuelle Stelle schon umgesprungen und die Vorstelle noch nicht?
-    // Akt.: 2.1, Vorstelle = 0.9 => 1.9
-    // Problem sind mehrere Rundungen 
-    // Bsp. zahl=4.5, Vorgänger= 9.6 (ziffer_vorgaenger=0)
-    // Tritt nur auf bei Übergang von analog auf digit
-    ergebnis_rating = ergebnis_nachkomma - ziffer_vorgaenger;
-    if (ergebnis_nachkomma >= 5)
-        ergebnis_rating-=5.1;
-    else
-        ergebnis_rating+=5;
-    ergebnis = (int) round(zahl);
-    if (ergebnis_rating < 0)
-        ergebnis-=1;
-    if (ergebnis == -1)
-        ergebnis+=10;
-    
-    ergebnis = (ergebnis + 10) % 10;
-    return ergebnis;
-}
-*/
 
 bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
 {
@@ -416,11 +385,6 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
         {
             this->logfileRetentionInDays = std::stoi(zerlegt[1]);
         }
-//        if ((toUpper(zerlegt[0]) == "MODELTYPE") && (zerlegt.size() > 1))
-//        {
-//            if (toUpper(zerlegt[1]) == "DIGITHYPRID")
-//                CNNType = DigitalHyprid;
-//        }
 
         if ((toUpper(zerlegt[0]) == "MODEL") && (zerlegt.size() > 1))
         {
@@ -439,6 +403,11 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
             neuroi->posy = std::stoi(zerlegt[2]);
             neuroi->deltax = std::stoi(zerlegt[3]);
             neuroi->deltay = std::stoi(zerlegt[4]);
+            neuroi->CCW = false;
+            if (zerlegt.size() >= 6)
+            {
+                neuroi->CCW = toUpper(zerlegt[5]) == "TRUE";
+            }
             neuroi->result_float = -1;
             neuroi->image = NULL;
             neuroi->image_org = NULL;
@@ -511,7 +480,7 @@ general* ClassFlowCNNGeneral::GetGENERAL(string _name, bool _create = true)
 
     _ret->ROI.push_back(neuroi);
 
-    printf("GetGENERAL - GENERAL %s - roi %s\n", _analog.c_str(), _roi.c_str());
+    printf("GetGENERAL - GENERAL %s - roi %s - CCW: %d\n", _analog.c_str(), _roi.c_str(), neuroi->CCW);
 
     return _ret;
 }
@@ -658,10 +627,11 @@ bool ClassFlowCNNGeneral::getNetworkParameter()
                 CNNType = Digital;
                 printf("TFlite-Type set to Digital\n");
                 break;
-            case 20:
+/*            case 20:
                 CNNType = DigitalHyprid10;
                 printf("TFlite-Type set to DigitalHyprid10\n");
                 break;
+*/
 //            case 22:
 //                CNNType = DigitalHyprid;
 //                printf("TFlite-Type set to DigitalHyprid\n");
@@ -724,8 +694,13 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
                         f1 = tflite->GetOutputValue(0);
                         f2 = tflite->GetOutputValue(1);
                         float result = fmod(atan2(f1, f2) / (M_PI * 2) + 2, 1);
-                        GENERAL[_ana]->ROI[i]->result_float = result * 10;
-                        printf("Result General(Analog)%i: %f\n", i, GENERAL[_ana]->ROI[i]->result_float); 
+                              
+                        if(GENERAL[_ana]->ROI[i]->CCW)
+                            GENERAL[_ana]->ROI[i]->result_float = 10 - (result * 10);
+                        else
+                            GENERAL[_ana]->ROI[i]->result_float = result * 10;
+                              
+                        printf("Result General(Analog)%i - CCW: %d -  %f\n", i, GENERAL[_ana]->ROI[i]->CCW, GENERAL[_ana]->ROI[i]->result_float); 
                         if (isLogImage)
                             LogImage(logPath, GENERAL[_ana]->ROI[i]->name, &GENERAL[_ana]->ROI[i]->result_float, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
                     } break;
@@ -790,6 +765,7 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
                         }
                     } break;
 */
+/*
                 case DigitalHyprid10:
                     {
                         int _num, _nachkomma;
@@ -825,6 +801,7 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
                             }
                         }
                     } break;
+*/
 
                 case DoubleHyprid10:
                     {
@@ -858,7 +835,7 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
                             _fit = _val + _valminus;
 
                         }
-                        if (result > 10)
+                        if (result >= 10)
                             result = result - 10;
                         if (result < 0)
                             result = result + 10;
@@ -916,15 +893,17 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
     
                         _num = tflite->GetOutClassification();
                         
-                        GENERAL[_ana]->ROI[i]->result_float = (float)_num / 10.0;
+                        if(GENERAL[_ana]->ROI[i]->CCW)
+                            GENERAL[_ana]->ROI[i]->result_float = 10 - ((float)_num / 10.0);                              
+                        else
+                            GENERAL[_ana]->ROI[i]->result_float = (float)_num / 10.0;
 
- 
                         _result_save_file = GENERAL[_ana]->ROI[i]->result_float;
 
                         
                         GENERAL[_ana]->ROI[i]->isReject = false;
                         
-                        printf("Result General(Analog)%i: %f\n", i, GENERAL[_ana]->ROI[i]->result_float); 
+                        printf("Result General(Analog)%i - CCW: %d -  %f\n", i, GENERAL[_ana]->ROI[i]->CCW, GENERAL[_ana]->ROI[i]->result_float); 
 
                         if (isLogImage)
                         {

+ 3 - 7
code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.h

@@ -10,7 +10,6 @@ enum t_CNNType {
     Analogue,
     Analogue100,
     Digital,
-//    DigitalHyprid,
     DigitalHyprid10,
     DoubleHyprid10,
     Digital100,
@@ -30,21 +29,18 @@ protected:
     int DigitalBand = 3;
     float DigitalAnalogerVorgaengerUebergangsbereich = 2;
     float DigitalUebergangsbereichVorgaengerAnalogToDigit = 1; // war vorher 2
-    float DigitalUebergangsbereichVorgaenger = 0.9;
+    float DigitalUebergangsbereichVorgaenger = 0.7; // 9.3 - 0.7
 
     string cnnmodelfile;
     int modelxsize, modelysize, modelchannel;
     bool isLogImageSelect;
     string LogImageSelect;
     ClassFlowAlignment* flowpostalignment;
-//    ClassFlowPostProcessing *flowpostprocessing = NULL;
+
     bool SaveAllFiles;   
-//    bool extendedResolution;
 
-//    int ZeigerEval(float zahl, int ziffer_vorgaenger);
-//    int ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int eval_vorgaenger);
     int ZeigerEvalAnalogNeu(float zahl, int ziffer_vorgaenger);
-    int ZeigerEvalAnalogToDigitNeu(float zahl, float ziffer_vorgaenger);
+    int ZeigerEvalAnalogToDigitNeu(float zahl, float ziffer_vorgaenger,  int eval_vorgaenger);
     int ZeigerEvalHybridNeu(float zahl, float zahl_vorgaenger, int eval_vorgaenger, bool AnalogerVorgaenger = false);
 
 

+ 4 - 4
code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h

@@ -7,7 +7,7 @@ struct roi {
     int posx, posy, deltax, deltay;
     float result_float;
     int result_klasse;
-    bool isReject;
+    bool isReject, CCW;
     string name;
     CImageBasis *image, *image_org;
 };
@@ -33,9 +33,9 @@ struct NumberPost {
     bool checkDigitIncreaseConsistency;
     time_t lastvalue;
     string timeStamp;
-    float FlowRateAct;          // m3 / min
-    float PreValue;             // letzter Wert, der gut ausgelesen wurde
-    float Value;                // letzer ausgelesener Wert, inkl. Korrekturen
+    double FlowRateAct;          // m3 / min
+    double PreValue;             // letzter Wert, der gut ausgelesen wurde
+    double Value;                // letzer ausgelesener Wert, inkl. Korrekturen
     string ReturnRateValue;      // RückgabewertRate
     string ReturnChangeAbsolute;      // RückgabewertRate
     string ReturnRawValue;      // Rohwert (mit N & führenden 0)    

+ 5 - 0
code/components/jomjol_flowcontroll/ClassFlowImage.cpp

@@ -63,7 +63,12 @@ void ClassFlowImage::LogImage(string logPath, string name, float *resultFloat, i
         if (*resultFloat < 0)
             sprintf(buf, "N.N_");
         else
+        {
             sprintf(buf, "%.1f_", *resultFloat);
+            if (strcmp(buf, "10.0_"))
+                sprintf(buf, "0.0_");
+        }
+            
 	} else if (resultInt != NULL) {
 		sprintf(buf, "%d_", *resultInt);
 	} else {

+ 93 - 10
code/components/jomjol_flowcontroll/ClassFlowMQTT.cpp

@@ -6,6 +6,7 @@
 #include "time_sntp.h"
 #include "interface_mqtt.h"
 #include "ClassFlowPostProcessing.h"
+#include "ClassLogFile.h"
 
 #include <time.h>
 
@@ -31,9 +32,7 @@ void ClassFlowMQTT::SetInitialParameter(void)
     ListFlowControll = NULL; 
     disabled = false;
     MQTTenable = false;
-    
-    
-
+    keepAlive = 600; // TODO This must be greater than the Flow Interval!
 }       
 
 ClassFlowMQTT::ClassFlowMQTT()
@@ -124,11 +123,50 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
         printf("InitMQTTInit\n");
         mainerrortopic = maintopic + "/connection";
         printf("Init MQTT with uri: %s, clientname: %s, user: %s, password: %s, maintopic: %s\n", uri.c_str(), clientname.c_str(), user.c_str(), password.c_str(), mainerrortopic.c_str());
-        MQTTInit(uri, clientname, user, password, mainerrortopic, 60); 
-        MQTTPublish(mainerrortopic, "connected", SetRetainFlag);
-        MQTTenable = true;
+        if (!MQTTInit(uri, clientname, user, password, mainerrortopic, keepAlive))
+        { // Failed
+            MQTTenable = false;
+            return true; // We need to return true despite we failed, else it will retry 5x and then reboot!
+        }
+    }
+
+    // Try sending mainerrortopic. If it fails, re-run init
+    if (!MQTTPublish(mainerrortopic, "connected", SetRetainFlag))
+    { // Failed
+        LogFile.WriteToFile("MQTT - Re-running init...!");
+        if (!MQTTInit(this->uri, this->clientname, this->user, this->password, this->mainerrortopic, keepAlive))
+        { // Failed
+            MQTTenable = false;
+            return false;
+        } 
+    }
+
+    // Try again and quit if it fails
+    if (!MQTTPublish(mainerrortopic, "connected", SetRetainFlag))
+    { // Failed
+        MQTTenable = false;
+        return false;
     }
+
+
+
    
+ /*   if (!MQTTPublish(mainerrortopic, "connected", SetRetainFlag))
+    { // Failed
+        LogFile.WriteToFile("MQTT - Could not publish connection status!");
+        MQTTenable = false;
+        return true; // We need to return true despite we failed, else it will retry 5x and then reboot!
+    }*/
+
+ /*   if(!MQTTPublish(_LWTContext, "", 1))
+    {
+        LogFile.WriteToFile("MQTT - Could not publish LWT!");
+        MQTTenable = false;
+        return true; // We need to return true despite we failed, else it will retry 5x and then reboot!
+    }*/
+
+
+    MQTTenable = true;
     return true;
 }
 
@@ -141,8 +179,44 @@ string ClassFlowMQTT::GetMQTTMainTopic()
 
 bool ClassFlowMQTT::doFlow(string zwtime)
 {
-    if (!MQTTenable)
-        return true;
+  //  if (!MQTTenable) {
+  //      LogFile.WriteToFile("MQTT not enabled!");
+  //
+  //      // Try again to init it
+  //   if (!MQTTInit(this->uri, this->clientname, this->user, this->password, this->mainerrortopic, keepAlive))
+  //      { // Failed
+  //          MQTTenable = false;
+  //          return true; // We need to return true despite we failed, else it will retry 5x and then reboot!
+  //      } 
+  //
+  //     if (!MQTTPublish(mainerrortopic, "connected", SetRetainFlag))
+  //      { // Failed
+  //          MQTTenable = false;
+  //          return true; // We need to return true despite we failed, else it will retry 5x and then reboot!
+  //      }
+  //      
+  //      LogFile.WriteToFile("MQTT is now enabled");
+  //      MQTTenable = true;
+  //  }
+
+
+    // Try sending mainerrortopic. If it fails, re-run init
+    if (!MQTTPublish(mainerrortopic, "connected", SetRetainFlag))
+    { // Failed
+        LogFile.WriteToFile("MQTT - Re-running init...!");
+        if (!MQTTInit(this->uri, this->clientname, this->user, this->password, this->mainerrortopic, keepAlive))
+        { // Failed
+            MQTTenable = false;
+            return true; // We need to return true despite we failed, else it will retry 5x and then reboot!
+        } 
+    }
+
+    // Try again and quit if it fails
+    if (!MQTTPublish(mainerrortopic, "connected", SetRetainFlag))
+    { // Failed
+        MQTTenable = false;
+        return true; // We need to return true despite we failed, else it will retry 5x and then reboot!
+    }
 
     std::string result;
     std::string resulterror = "";
@@ -153,7 +227,10 @@ bool ClassFlowMQTT::doFlow(string zwtime)
     string zw = "";
     string namenumber = "";
 
-    MQTTPublish(mainerrortopic, "connected");
+    // if (!MQTTPublish(mainerrortopic, "connected", SetRetainFlag))
+    //{ // Failed, skip other topics
+    //    return true; // We need to return true despite we failed, else it will retry 5x and then reboot!
+    //}
     
     zw = maintopic + "/" + "uptime";
     char uptimeStr[11];
@@ -163,13 +240,19 @@ bool ClassFlowMQTT::doFlow(string zwtime)
     zw = maintopic + "/" + "freeMem";
     char freeheapmem[11];
     sprintf(freeheapmem, "%zu", esp_get_free_heap_size());
-    MQTTPublish(zw, freeheapmem, SetRetainFlag);
+    if (!MQTTPublish(zw, freeheapmem, SetRetainFlag))
+    { // Failed, skip other topics
+        return true; // We need to return true despite we failed, else it will retry 5x and then reboot!
+    }
 
     zw = maintopic + "/" + "wifiRSSI";
     char rssi[11];
     sprintf(rssi, "%d", get_WIFI_RSSI());
     MQTTPublish(zw, rssi, SetRetainFlag);
 
+    zw = maintopic + "/" + "CPUtemp";
+    std::string cputemp = std::to_string(temperatureRead());
+    MQTTPublish(zw, cputemp, SetRetainFlag);
 
     if (flowpostprocessing)
     {

+ 1 - 0
code/components/jomjol_flowcontroll/ClassFlowMQTT.h

@@ -15,6 +15,7 @@ protected:
     std::string user, password; 
     int SetRetainFlag;
     bool MQTTenable;
+    int keepAlive;
 
     std::string maintopic, mainerrortopic; 
 	void SetInitialParameter(void);        

+ 61 - 20
code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp

@@ -9,6 +9,7 @@
 #include <time.h>
 
 #include "time_sntp.h"
+//#define SERIAL_DEBUG // testing debug on serial enabled
 
 
 #define PREVALUE_TIME_FORMAT_OUTPUT "%Y-%m-%dT%H:%M:%S"
@@ -68,7 +69,7 @@ string ClassFlowPostProcessing::GetPreValue(std::string _number)
     return result;
 }
 
-void ClassFlowPostProcessing::SetPreValue(float zw, string _numbers, bool _extern)
+void ClassFlowPostProcessing::SetPreValue(double zw, string _numbers, bool _extern)
 {
     printf("SetPrevalue: %f, %s\n", zw, _numbers.c_str());
     for (int j = 0; j < NUMBERS.size(); ++j)
@@ -126,7 +127,7 @@ bool ClassFlowPostProcessing::LoadPreValue(void)
             {
                 if (NUMBERS[j]->name == name)
                 {
-                    NUMBERS[j]->PreValue = stof(zwvalue.c_str());
+                    NUMBERS[j]->PreValue = stod(zwvalue.c_str());
                     NUMBERS[j]->ReturnPreValue = RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma + 1);      // SIcherheitshalber 1 Stelle mehr, da ggf. Exgtended Resolution an ist (wird erst beim ersten Durchlauf gesetzt)
 
                     time_t tStart;
@@ -177,7 +178,7 @@ bool ClassFlowPostProcessing::LoadPreValue(void)
         fclose(pFile);
         printf("%s", zw);
         zwvalue = trim(std::string(zw));
-        NUMBERS[0]->PreValue = stof(zwvalue.c_str());
+        NUMBERS[0]->PreValue = stod(zwvalue.c_str());
 
         time_t tStart;
         int yy, month, dd, hh, mm, ss;
@@ -663,7 +664,9 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
                     previous_value = zw - 48;
             }
         }
-
+        #ifdef SERIAL_DEBUG
+            printf("After analog->getReadout: ReturnRaw %s\n", NUMBERS[j]->ReturnRawValue.c_str());  
+        #endif
         if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi)
             NUMBERS[j]->ReturnRawValue = "." + NUMBERS[j]->ReturnRawValue;
 
@@ -674,16 +677,22 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
             else
                 NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, NUMBERS[j]->isExtendedResolution, previous_value);        // Extended Resolution nur falls es keine analogen Ziffern gibt
         }
-
+        #ifdef SERIAL_DEBUG
+            printf("After digital->getReadout: ReturnRaw %s\n", NUMBERS[j]->ReturnRawValue.c_str());  
+        #endif
         NUMBERS[j]->ReturnRawValue = ShiftDecimal(NUMBERS[j]->ReturnRawValue, NUMBERS[j]->DecimalShift);
 
-        printf("ReturnRaw %s", NUMBERS[j]->ReturnRawValue.c_str());  
-
+        #ifdef SERIAL_DEBUG
+            printf("After ShiftDecimal: ReturnRaw %s\n", NUMBERS[j]->ReturnRawValue.c_str());  
+        #endif
 
         if (IgnoreLeadingNaN)               
             while ((NUMBERS[j]->ReturnRawValue.length() > 1) && (NUMBERS[j]->ReturnRawValue[0] == 'N'))
                 NUMBERS[j]->ReturnRawValue.erase(0, 1);
 
+        #ifdef SERIAL_DEBUG
+            printf("After IgnoreLeadingNaN: ReturnRaw %s\n", NUMBERS[j]->ReturnRawValue.c_str());  
+        #endif
         NUMBERS[j]->ReturnValue = NUMBERS[j]->ReturnRawValue;
 
         if (findDelimiterPos(NUMBERS[j]->ReturnValue, "N") != std::string::npos)
@@ -693,18 +702,38 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
             else
                 continue; // es gibt keinen Zahl, da noch ein N vorhanden ist.
         }
-
+        #ifdef SERIAL_DEBUG
+            printf("After findDelimiterPos: ReturnValue %s\n", NUMBERS[j]->ReturnRawValue.c_str());  
+        #endif
         // Lösche führende Nullen (außer es ist nur noch einen 0)
         while ((NUMBERS[j]->ReturnValue.length() > 1) && (NUMBERS[j]->ReturnValue[0] == '0'))
             NUMBERS[j]->ReturnValue.erase(0, 1);
-
-        NUMBERS[j]->Value = std::stof(NUMBERS[j]->ReturnValue);
+        #ifdef SERIAL_DEBUG
+            printf("After removeLeadingZeros: ReturnValue %s\n", NUMBERS[j]->ReturnRawValue.c_str());  
+        #endif
+        NUMBERS[j]->Value = std::stod(NUMBERS[j]->ReturnValue);
+        #ifdef SERIAL_DEBUG
+            printf("After setting the Value: Value %f and as double is %f\n", NUMBERS[j]->Value, std::stod(NUMBERS[j]->ReturnValue));  
+        #endif
 
         if (NUMBERS[j]->checkDigitIncreaseConsistency)
         {
-            NUMBERS[j]->Value = checkDigitConsistency(NUMBERS[j]->Value, NUMBERS[j]->DecimalShift, NUMBERS[j]->analog_roi != NULL, NUMBERS[j]->PreValue);
+            if (flowDigit)
+            {
+                if (flowDigit->getCNNType() != Digital)
+                    printf("checkDigitIncreaseConsistency = true - ignored due to wrong CNN-Type (not Digital Classification)\n"); 
+                else 
+                    NUMBERS[j]->Value = checkDigitConsistency(NUMBERS[j]->Value, NUMBERS[j]->DecimalShift, NUMBERS[j]->analog_roi != NULL, NUMBERS[j]->PreValue);
+            }
+            else
+            {
+                printf("checkDigitIncreaseConsistency = true - no digital numbers defined!\n"); 
+            }
         }
 
+        #ifdef SERIAL_DEBUG
+            printf("After checkDigitIncreaseConsistency: Value %f\n", NUMBERS[j]->Value);  
+        #endif
 
 
         if (!NUMBERS[j]->AllowNegativeRates)
@@ -717,7 +746,9 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
                 continue;
             }
         }
-
+        #ifdef SERIAL_DEBUG
+            printf("After AllowNegativeRates: Value %f\n", NUMBERS[j]->Value);  
+        #endif
         double difference = difftime(imagetime, NUMBERS[j]->lastvalue);      // in Sekunden
         difference /= 60;  
         NUMBERS[j]->FlowRateAct = (NUMBERS[j]->Value - NUMBERS[j]->PreValue) / difference;
@@ -725,7 +756,7 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
 
         if (NUMBERS[j]->useMaxRateValue && PreValueUse && NUMBERS[j]->PreValueOkay)
         {
-            float _ratedifference;  
+            double _ratedifference;  
             if (NUMBERS[j]->RateType == RateChange)
                 _ratedifference = NUMBERS[j]->FlowRateAct;
             else
@@ -740,7 +771,9 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
                 continue;
             }
         }
-
+        #ifdef SERIAL_DEBUG
+           printf("After MaxRateCheck: Value %f\n", NUMBERS[j]->Value);  
+        #endif
         NUMBERS[j]->ReturnChangeAbsolute = RundeOutput(NUMBERS[j]->Value - NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma);                                                
         NUMBERS[j]->lastvalue = imagetime;
         NUMBERS[j]->PreValue = NUMBERS[j]->Value;
@@ -818,7 +851,7 @@ string ClassFlowPostProcessing::getReadoutParam(bool _rawValue, bool _noerror, i
     return NUMBERS[_number]->ReturnValue;
 }
 
-string ClassFlowPostProcessing::RundeOutput(float _in, int _anzNachkomma){
+string ClassFlowPostProcessing::RundeOutput(double _in, int _anzNachkomma){
     std::stringstream stream;
     int _zw = _in;    
 //    printf("AnzNachkomma: %d\n", _anzNachkomma);
@@ -842,7 +875,7 @@ string ClassFlowPostProcessing::RundeOutput(float _in, int _anzNachkomma){
 }
 
 
-string ClassFlowPostProcessing::ErsetzteN(string input, float _prevalue)
+string ClassFlowPostProcessing::ErsetzteN(string input, double _prevalue)
 {
     int posN, posPunkt;
     int pot, ziffer;
@@ -873,7 +906,7 @@ string ClassFlowPostProcessing::ErsetzteN(string input, float _prevalue)
     return input;
 }
 
-float ClassFlowPostProcessing::checkDigitConsistency(float input, int _decilamshift, bool _isanalog, float _preValue){
+float ClassFlowPostProcessing::checkDigitConsistency(double input, int _decilamshift, bool _isanalog, double _preValue){
     int aktdigit, olddigit;
     int aktdigit_before, olddigit_before;
     int pot, pot_max;
@@ -885,8 +918,14 @@ float ClassFlowPostProcessing::checkDigitConsistency(float input, int _decilamsh
     {
         pot++;
     }
+    #ifdef SERIAL_DEBUG
+        printf("checkDigitConsistency: pot=%d, decimalshift=%d\n", pot, _decilamshift);
+    #endif
     pot_max = ((int) log10(input)) + 1;
-
+    float not_checked_input = floorf(input * pow(10, pot)) / pow(10, pot);
+    #ifdef SERIAL_DEBUG
+        printf("checkDigitConsistency: not_checked_input=%f\n", not_checked_input);
+    #endif
     while (pot <= pot_max)
     {
         zw = input / pow(10, pot-1);
@@ -915,11 +954,13 @@ float ClassFlowPostProcessing::checkDigitConsistency(float input, int _decilamsh
                 input = input + ((float) (1)) * pow(10, pot);   // addiere 1 an der Stelle
             }
         }
-
+        #ifdef SERIAL_DEBUG
+            printf("checkDigitConsistency: input=%f", input);
+        #endif
         pot++;
     }
 
-    return input;
+    return not_checked_input + input;
 }
 
 string ClassFlowPostProcessing::getReadoutRate(int _number)

+ 4 - 4
code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h

@@ -32,9 +32,9 @@ protected:
     bool LoadPreValue(void);
     string ShiftDecimal(string in, int _decShift);
 
-    string ErsetzteN(string, float _prevalue);
-    float checkDigitConsistency(float input, int _decilamshift, bool _isanalog, float _preValue);
-    string RundeOutput(float _in, int _anzNachkomma);
+    string ErsetzteN(string, double _prevalue);
+    float checkDigitConsistency(double input, int _decilamshift, bool _isanalog, double _preValue);
+    string RundeOutput(double _in, int _anzNachkomma);
 
     void InitNUMBERS();
     void handleDecimalSeparator(string _decsep, string _value);
@@ -58,7 +58,7 @@ public:
     string getReadoutTimeStamp(int _number = 0);
     void SavePreValue();
     string GetPreValue(std::string _number = "");
-    void SetPreValue(float zw, string _numbers, bool _extern = false);
+    void SetPreValue(double zw, string _numbers, bool _extern = false);
 
     std::string GetJSON(std::string _id = "", std::string _mac = "", std::string _lineend = "\n");
 

+ 47 - 2
code/components/jomjol_helper/Helper.cpp

@@ -209,6 +209,21 @@ size_t findDelimiterPos(string input, string delimiter)
 	return pos;
 }
 
+void DeleteFile(string fn)
+{
+//	ESP_LOGI(logTag, "Deleting file : %s", fn.c_str());
+	/* Delete file */
+	FILE* fpSourceFile = OpenFileAndWait(fn.c_str(), "rb");
+	if (!fpSourceFile)	// Sourcefile existiert nicht sonst gibt es einen Fehler beim Kopierversuch!
+	{
+		printf("DeleteFile: File %s existiert nicht!\n", fn.c_str());
+		return;
+	}
+	fclose(fpSourceFile);
+
+	unlink(fn.c_str());    
+}
+
 
 void CopyFile(string input, string output)
 {
@@ -243,18 +258,48 @@ void CopyFile(string input, string output)
 	// Close The Files
 	fclose(fpSourceFile);
 	fclose(fpTargetFile);
+	printf("File copied: %s to %s", input.c_str(), output.c_str());
 }
 
+string getFileFullFileName(string filename)
+{
+	size_t lastpos = filename.find_last_of('/');
+
+	if (lastpos == string::npos)
+		return "";
+
+//	printf("Last position: %d\n", lastpos);
+
+	string zw = filename.substr(lastpos + 1, filename.size() - lastpos);
+
+	return zw;
+}
+
+string getDirectory(string filename)
+{
+	size_t lastpos = filename.find('/');
+
+	if (lastpos == string::npos)
+		return "";
+
+//	printf("Directory: %d\n", lastpos);
+
+	string zw = filename.substr(0, lastpos - 1);
+	return zw;
+}
 
 string getFileType(string filename)
 {
-	int lastpos = filename.find(".", 0);
-	int neu_pos;
+	size_t lastpos = filename.find(".", 0);
+	size_t neu_pos;
 	while ((neu_pos = filename.find(".", lastpos + 1)) > -1)
 	{
 		lastpos = neu_pos;
 	}
 
+	if (lastpos == string::npos)
+		return "";
+
 	string zw = filename.substr(lastpos + 1, filename.size() - lastpos);
 	zw = toUpper(zw);
 

+ 3 - 0
code/components/jomjol_helper/Helper.h

@@ -10,6 +10,7 @@ std::string FormatFileName(std::string input);
 void FindReplace(std::string& line, std::string& oldString, std::string& newString);
 
 void CopyFile(string input, string output);
+void DeleteFile(string fn);
 
 FILE* OpenFileAndWait(const char* nm, const char* _mode, int _waitsec = 1);
 
@@ -19,6 +20,8 @@ string trim(string istring, string adddelimiter = "");
 bool ctype_space(const char c, string adddelimiter);
 
 string getFileType(string filename);
+string getFileFullFileName(string filename);
+string getDirectory(string filename);
 
 int mkdir_r(const char *dir, const mode_t mode);
 int removeFolder(const char* folderPath, const char* logTag);

+ 1 - 0
code/components/jomjol_logfile/ClassLogFile.cpp

@@ -129,6 +129,7 @@ void ClassLogFile::WriteToFile(std::string info, bool _time)
     std::string logpath = logroot + "/" + buffer; 
     
     WriteToDedicatedFile(logpath, info, _time);
+    printf((info + "\n").c_str());
 }
 
 std::string ClassLogFile::GetCurrentFileName()

+ 104 - 15
code/components/jomjol_mqtt/interface_mqtt.cpp

@@ -19,18 +19,43 @@ esp_mqtt_event_id_t esp_mmqtt_ID = MQTT_EVENT_ANY;
 bool mqtt_connected = false;
 esp_mqtt_client_handle_t client = NULL;
 
-void MQTTPublish(std::string _key, std::string _content, int retained_flag){
-    if (client && mqtt_connected) {
-        int msg_id;
-        std::string zw;
-        msg_id = esp_mqtt_client_publish(client, _key.c_str(), _content.c_str(), 0, 1, retained_flag);
-        zw = "sent publish successful in MQTTPublish, msg_id=" + std::to_string(msg_id) + ", " + _key + ", " + _content;
-        if (debugdetail) LogFile.WriteToFile(zw);
-        ESP_LOGD(TAG_INTERFACEMQTT, "sent publish successful in MQTTPublish, msg_id=%d, %s, %s", msg_id, _key.c_str(), _content.c_str());
-    }
-    else {
-        ESP_LOGW(TAG_INTERFACEMQTT, "Problem with Publish, client=%d, mqtt_connected %d", (int) client, (int) mqtt_connected);
+bool MQTTPublish(std::string _key, std::string _content, int retained_flag){
+  
+  //  if (!client) {
+  //      LogFile.WriteToFile("MQTT - client not initialized!");  
+  //      return false;      
+  //  }
+  //  LogFile.WriteToFile("MQTT - client initialized!");  // Debug
+  //
+  //  if (!mqtt_connected) {
+  //      LogFile.WriteToFile("MQTT - Can not publish, not connected!");
+  //      ESP_LOGW(TAG_INTERFACEMQTT, "Problem with Publish, client=%d, mqtt_connected %d", (int) client, (int) mqtt_connected);
+  //      return false;            
+  //  }
+  //  LogFile.WriteToFile("MQTT - connected!");  // Debug
+
+ /*   if (client && mqtt_connected) {
+        LogFile.WriteToFile("MQTT - connected!");  // Debug
     }
+    else { // init needed
+        if (!MQTTInit(this->uri, this->clientname, this->user, password, mainerrortopic, keepAlive)) // validate{
+        { // Failed
+            return false;
+        }
+    }*/
+
+
+    int msg_id;
+    std::string zw;
+    msg_id = esp_mqtt_client_publish(client, _key.c_str(), _content.c_str(), 0, 1, retained_flag);
+    if (msg_id < 0) {
+        LogFile.WriteToFile("MQTT - Failed to publish '" + _key + "'!");
+        return false;
+    }
+    zw = "MQTT - sent publish successful in MQTTPublish, msg_id=" + std::to_string(msg_id) + ", " + _key + ", " + _content;
+    if (debugdetail) LogFile.WriteToFile(zw);
+    ESP_LOGD(TAG_INTERFACEMQTT, "sent publish successful in MQTTPublish, msg_id=%d, %s, %s", msg_id, _key.c_str(), _content.c_str());
+    return true;
 }
 
 
@@ -90,11 +115,23 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
     mqtt_event_handler_cb((esp_mqtt_event_handle_t) event_data);
 }
 
-void MQTTInit(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password, std::string _LWTContext, int _keepalive){
+
+bool MQTTInit(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password, std::string _LWTContext, int _keepalive){
     std::string _zwmessage = "connection lost";
 
     int _lzw = _zwmessage.length();
 
+/*    LWTContext = _LWTContext;
+
+    mqtt_cfg.uri = _mqttURI.c_str();
+    mqtt_cfg.client_id = _clientid.c_str();
+    mqtt_cfg.lwt_topic = _LWTContext.c_str();
+    mqtt_cfg.lwt_msg = _zwmessage.c_str();
+    mqtt_cfg.lwt_retain = 1;
+    mqtt_cfg.lwt_msg_len = _lzw;
+    mqtt_cfg.keepalive = _keepalive;
+*/
+
     esp_mqtt_client_config_t mqtt_cfg = {
         .uri = _mqttURI.c_str(),
         .client_id = _clientid.c_str(),
@@ -105,28 +142,78 @@ void MQTTInit(std::string _mqttURI, std::string _clientid, std::string _user, st
         .keepalive = _keepalive
     };
 
+    LogFile.WriteToFile("MQTT - Init");
+
     if (_user.length() && _password.length()){
         mqtt_cfg.username = _user.c_str();
         mqtt_cfg.password = _password.c_str();
         ESP_LOGI(TAG_INTERFACEMQTT, "Connect to MQTT: %s, %s", mqtt_cfg.username, mqtt_cfg.password);
     };
 
+    MQTTdestroy();
     client = esp_mqtt_client_init(&mqtt_cfg);
     if (client)
     {
         if (esp_mqtt_client_register_event(client, esp_mmqtt_ID, mqtt_event_handler, client) != ESP_OK)
+        {
             LogFile.WriteToFile("MQTT - Could not register event!");
+            return false;
+        }
         if (esp_mqtt_client_start(client) != ESP_OK)
+        {
             LogFile.WriteToFile("MQTT - Could not start client!");
+            return false;
+        }
+
+       /* if(!MQTTPublish(_LWTContext, "", 1))
+        {
+            LogFile.WriteToFile("MQTT - Could not publish LWT!");
+            return false;
+        }*/
+    }
+    else
+    {
+        LogFile.WriteToFile("MQTT - Could not Init client!");
+        return false;
+    }
+
+    LogFile.WriteToFile("MQTT - Init successful");
+    return true;
+}
+
+/*
+void MQTTReConnect(){
+    std::string _zwmessage = "connection lost";
+    int _lzw = _zwmessage.length();
+
+>>>>>>> Stashed changes
+    client = esp_mqtt_client_init(&mqtt_cfg);
+    if (client)
+    {
+        if (esp_mqtt_client_register_event(client, esp_mmqtt_ID, mqtt_event_handler, client) != ESP_OK)
+            LogFile.WriteToFile("MQTT - Could not register event!");
+        if (esp_mqtt_client_start(client) != ESP_OK)
+            LogFile.WriteToFile("MQTT - Could not start client!");
+
+<<<<<<< Updated upstream
+        if(MQTTPublish(_LWTContext, "", 1)) {
+            LogFile.WriteToFile("MQTT - Client init successful");
+        }
+=======
+        if (mqtt_connected)
+            MQTTPublish(LWTContext, "", 1);
+        else
+            LogFile.WriteToFile("Problem with (Re)Connection not successful!");
 
-        MQTTPublish(_LWTContext, "", 1);
+>>>>>>> Stashed changes
     }
     else
     {
-        LogFile.WriteToFile("MQTT - Could not Init MQTT Client!");
+        LogFile.WriteToFile("MQTT - Could not Init client!");
     }
 
 }
+*/
 
 void MQTTdestroy() {
     if (client != NULL) {
@@ -185,6 +272,7 @@ void MQTTregisterSubscribeFunction(std::string topic, std::function<bool(std::st
 
 void MQTTconnected(){
     if (mqtt_connected) {
+        LogFile.WriteToFile("MQTT - Connected");
         if (connectFunktionMap != NULL) {
             for(std::map<std::string, std::function<void()>>::iterator it = connectFunktionMap->begin(); it != connectFunktionMap->end(); ++it) {
                 it->second();
@@ -192,10 +280,11 @@ void MQTTconnected(){
             }
         }
 
-        if (subscribeFunktionMap != NULL) {
+       if (subscribeFunktionMap != NULL) {
             for(std::map<std::string, std::function<bool(std::string, char*, int)>>::iterator it = subscribeFunktionMap->begin(); it != subscribeFunktionMap->end(); ++it) {
                 int msg_id = esp_mqtt_client_subscribe(client, it->first.c_str(), 0);
                 ESP_LOGD(TAG_INTERFACEMQTT, "topic %s subscribe successful, msg_id=%d", it->first.c_str(), msg_id);
+                LogFile.WriteToFile("MQTT - topic " + it->first + " subscribe successful, msg_id=" + std::to_string(msg_id));
             }
         }
     }

+ 2 - 2
code/components/jomjol_mqtt/interface_mqtt.h

@@ -5,12 +5,12 @@
 #include <map>
 #include <functional>
 
-void MQTTInit(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password, std::string _LWTContext, int _keepalive);
+bool MQTTInit(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password, std::string _LWTContext, int _keepalive);
 void MQTTdestroy();
 
 //void MQTTInit(std::string _mqttURI, std::string _clientid, std::string _user = "", std::string _password = "");
 
-void MQTTPublish(std::string _key, std::string _content, int retained_flag = 1);            // retained Flag as Standart
+bool MQTTPublish(std::string _key, std::string _content, int retained_flag = 1);            // retained Flag as Standart
 
 bool MQTTisConnected();
 

+ 66 - 1
code/components/jomjol_tfliteclass/server_tflite.cpp

@@ -21,6 +21,7 @@
 #include "server_GPIO.h"
 
 #include "server_file.h"
+#include "connect_wlan.h"
 
 #define DEBUG_DETAIL_ON       
 
@@ -590,6 +591,55 @@ esp_err_t handler_statusflow(httpd_req_t *req)
     return ESP_OK;
 };
 
+esp_err_t handler_cputemp(httpd_req_t *req)
+{
+#ifdef DEBUG_DETAIL_ON       
+    LogFile.WriteHeapInfo("handler_cputemp - Start");       
+#endif
+
+    const char* resp_str;
+    char cputemp[20];
+    
+    sprintf(cputemp, "CPU Temp: %4.1f°C", temperatureRead());
+
+    resp_str = cputemp;
+
+    httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
+    httpd_resp_send(req, resp_str, strlen(resp_str));   
+    /* Respond with an empty chunk to signal HTTP response completion */
+    httpd_resp_send_chunk(req, NULL, 0);      
+
+#ifdef DEBUG_DETAIL_ON       
+    LogFile.WriteHeapInfo("handler_cputemp - End");       
+#endif
+
+    return ESP_OK;
+};
+
+esp_err_t handler_rssi(httpd_req_t *req)
+{
+#ifdef DEBUG_DETAIL_ON       
+    LogFile.WriteHeapInfo("handler_rssi - Start");       
+#endif
+
+    const char* resp_str;
+    char rssi[20];
+
+    sprintf(rssi, "RSSI: %idBm", get_WIFI_RSSI());
+
+    resp_str = rssi;
+
+    httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
+    httpd_resp_send(req, resp_str, strlen(resp_str));   
+    /* Respond with an empty chunk to signal HTTP response completion */
+    httpd_resp_send_chunk(req, NULL, 0);      
+
+#ifdef DEBUG_DETAIL_ON       
+    LogFile.WriteHeapInfo("handler_rssi - End");       
+#endif
+
+    return ESP_OK;
+};
 
 esp_err_t handler_prevalue(httpd_req_t *req)
 {
@@ -643,7 +693,7 @@ esp_err_t handler_prevalue(httpd_req_t *req)
     httpd_resp_send_chunk(req, NULL, 0);      
 
 #ifdef DEBUG_DETAIL_ON       
-    LogFile.WriteHeapInfo("handler_prevalue - Start");       
+    LogFile.WriteHeapInfo("handler_prevalue - End");       
 #endif
 
     return ESP_OK;
@@ -766,11 +816,26 @@ void register_server_tflite_uri(httpd_handle_t server)
     camuri.user_ctx  = (void*) "Light Off"; 
     httpd_register_uri_handler(server, &camuri);  
     
+    camuri.uri       = "/cputemp.html";
+    camuri.handler   = handler_cputemp;
+    camuri.user_ctx  = (void*) "Light Off"; 
+    httpd_register_uri_handler(server, &camuri);  
+
+    camuri.uri       = "/rssi.html";
+    camuri.handler   = handler_rssi;
+    camuri.user_ctx  = (void*) "Light Off"; 
+    httpd_register_uri_handler(server, &camuri);  
+
     camuri.uri       = "/editflow.html";
     camuri.handler   = handler_editflow;
     camuri.user_ctx  = (void*) "EditFlow"; 
     httpd_register_uri_handler(server, &camuri);     
 
+    camuri.uri       = "/value.html";
+    camuri.handler   = handler_wasserzaehler;
+    camuri.user_ctx  = (void*) "Value"; 
+    httpd_register_uri_handler(server, &camuri);  
+
     camuri.uri       = "/wasserzaehler.html";
     camuri.handler   = handler_wasserzaehler;
     camuri.user_ctx  = (void*) "Wasserzaehler"; 

+ 2 - 2
code/main/version.cpp

@@ -1,4 +1,4 @@
-const char* GIT_REV="234925c";
+const char* GIT_REV="36a02d1";
 const char* GIT_TAG="";
 const char* GIT_BRANCH="master";
-const char* BUILD_TIME="2022-08-28 19:59";
+const char* BUILD_TIME="2022-09-17 10:08";

+ 1 - 1
code/main/version.h

@@ -13,7 +13,7 @@ extern "C"
 #include "Helper.h"
 #include <fstream>
 
-const char* GIT_BASE_BRANCH = "master - v11.2.0 - 2022-08-28";
+const char* GIT_BASE_BRANCH = "master - v11.3.0 - 2022-09-16";
 
 
 const char* git_base_branch(void)

+ 4 - 1
code/platformio.ini

@@ -12,12 +12,12 @@
 [platformio]
 src_dir = main
 
-
 [env:esp32cam]
 platform = espressif32@4.4.0
 ;platform = espressif32@5.1.0
 ;platform = espressif32
 board = esp32cam
+;board = m5stack-core-esp32
 framework = espidf
 
 ;board_build.partitions = partitions_singleapp.csv
@@ -45,3 +45,6 @@ monitor_rts = 0
 monitor_dtr = 0
 
 debug_tool = esp-prog
+
+; Enable and adapt for logging over USB
+;upload_port = /dev/ttyUSB0

+ 1 - 1
code/sdkconfig

@@ -1028,7 +1028,7 @@ CONFIG_MQTT_TRANSPORT_SSL=y
 CONFIG_MQTT_TRANSPORT_WEBSOCKET=y
 CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y
 # CONFIG_MQTT_MSG_ID_INCREMENTAL is not set
-# CONFIG_MQTT_SKIP_PUBLISH_IF_DISCONNECTED is not set
+CONFIG_MQTT_SKIP_PUBLISH_IF_DISCONNECTED is not set
 # CONFIG_MQTT_REPORT_DELETED_MESSAGES is not set
 # CONFIG_MQTT_USE_CUSTOM_CONFIG is not set
 # CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED is not set

+ 1 - 1
code/sdkconfig.esp32cam

@@ -1036,7 +1036,7 @@ CONFIG_MQTT_TRANSPORT_SSL=y
 CONFIG_MQTT_TRANSPORT_WEBSOCKET=y
 CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y
 # CONFIG_MQTT_MSG_ID_INCREMENTAL is not set
-# CONFIG_MQTT_SKIP_PUBLISH_IF_DISCONNECTED is not set
+CONFIG_MQTT_SKIP_PUBLISH_IF_DISCONNECTED=y
 # CONFIG_MQTT_REPORT_DELETED_MESSAGES is not set
 # CONFIG_MQTT_USE_CUSTOM_CONFIG is not set
 # CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED is not set

+ 178 - 8
code/test/components/jomjol-flowcontroll/test_flowpostprocessing.cpp

@@ -5,7 +5,8 @@
 #include <ClassFlowMakeImage.h>
 
 void setUpClassFlowPostprocessing(void);
-string process_doFlow(std::vector<float> analog, std::vector<float> digits);
+string process_doFlow(std::vector<float> analog, std::vector<float> digits, t_CNNType digType = Digital100, 
+                bool checkConsistency=false,  bool extendedResolution=false, int decimal_shift=0);
 
 ClassFlowCNNGeneral* _analog;
 ClassFlowCNNGeneral* _digit;
@@ -43,13 +44,14 @@ void test_doFlow() {
         std::vector<float> digits = { 1.2, 6.7};
         std::vector<float> analogs = { 9.5, 8.4};
         const char* expected = "16.98";
+        const char* expected_extended = "16.984";
         std::string result = process_doFlow(analogs, digits);
         TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
 
         /*
          * https://github.com/jomjol/AI-on-the-edge-device/issues/921
          * 
-         * Das Ergebnis sollte "376529.6" sein. Bzw. 16.98 ohne Extended true
+         * Das Ergebnis sollte "376529.6" sein. 
          */
         digits = { 3.0, 7.0, 6.0, 5.0, 2.5, 9.6};
         analogs = { 6.4};
@@ -158,11 +160,154 @@ void test_doFlow() {
         expected = "194.9089";
         result = process_doFlow(analogs, digits);
         TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
+
+        // Fehler bei V11.2.0
+        // https://github.com/jomjol/AI-on-the-edge-device/issues/921#issuecomment-1229552041
+        digits = { 2.9, 7.0, 7.0, 9.1, 8.1, 8.5};  // 376.9884(1) als falsches Ergebnis
+        analogs = { 4.1 };
+        expected = "377988.4";
+        result = process_doFlow(analogs, digits);
+        TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
+
+        // Fehler bei V11.2.0
+        // https://github.com/jomjol/AI-on-the-edge-device/issues/921#issuecomment-1233149877
+        digits = { 0.0, 0.0, 7.0, 8.9};  // 79.9999(6) als falsches Ergebnis
+        analogs = { 0.1, 0.1, 0.1, 9.6};
+        expected = "78.9999";
+        result = process_doFlow(analogs, digits);
+        TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
+
+        // Fehler bei V11.2.0 
+        // https://github.com/jomjol/AI-on-the-edge-device/issues/921#issuecomment-1236119370
+        digits = { 3.1, 9.1, 5.7};  // 9.1 führt zu falscher Erkennung eines unvollständigen Übergangs
+        analogs = { 8.8, 6.1, 3.0, 2.0};
+        expected = "395.8632";
+        result = process_doFlow(analogs, digits);
+        TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
+
+        // Fehler bei V11.2.0 
+        // https://github.com/jomjol/AI-on-the-edge-device/discussions/950#discussion-4338615
+        digits = { 1.0, 9.0, 9.0};  // Übergang wurde um 1 erhöht (200, statt 199)
+        analogs = { 7.1, 4.8, 8.3};
+        expected = "199.748";
+        result = process_doFlow(analogs, digits, Digital);
+        TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
+
+        // Fehler bei Rolling (2002-09-09)
+        // https://github.com/jomjol/AI-on-the-edge-device/issues/921#issuecomment-1242730397
+        digits = { 3.0, 2.0, 2.0, 8.0, 9.0, 4.0, 1.7, 9.8};  // falscher Wert 32290.420
+        analogs = { };
+        expected = "32289.420";
+        expected_extended= "32289.4198";
+        // FALSCH! wegen ungenügender Präzision von NUMBERS->Value
+        // expected_extended= "32289.4198";
+
+        // extendResolution=false, checkConsistency=false
+        result = process_doFlow(analogs, digits, Digital100, false, false, -3);
+        TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
+
+        // extendResolution=true
+        result = process_doFlow(analogs, digits, Digital100, false, true, -3);
+        TEST_ASSERT_EQUAL_STRING(expected_extended, result.c_str());
+
+        // checkConsistency=true und extendResolution=true
+        result = process_doFlow(analogs, digits, Digital100, false, true, -3);
+        TEST_ASSERT_EQUAL_STRING(expected_extended, result.c_str());
+
+        // Fehler Rolling (2022-09-10)
+        // not documented as issue
+        digits = { 0.0, 0.0, 7.9, 3.8};  // 84.99401 als falsches Ergebnis
+        analogs = { 0.0, 9.4, 4.1, 0.1};
+        expected = "83.9940";
+        expected_extended= "83.99401";
+
+        // checkConsistency=false
+        result = process_doFlow(analogs, digits, Digital100, false);
+        TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
+
+
+        // checkConsistency=true
+        result = process_doFlow(analogs, digits, Digital100, true);
+        TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
+
+        // extendResolution=true
+        result = process_doFlow(analogs, digits, Digital100, false, true);
+        TEST_ASSERT_EQUAL_STRING(expected_extended, result.c_str());
+
+        // checkConsistency=true und extendResolution=true
+        result = process_doFlow(analogs, digits, Digital100, false, true);
+        TEST_ASSERT_EQUAL_STRING(expected_extended, result.c_str());
+
+        // Fehler Rolling (2022-09-10)
+        // https://github.com/jomjol/AI-on-the-edge-device/issues/994#issue-1368570945
+        digits = { 0.0, 0.0, 1.0, 2.0, 2.8, 1.9, 2.8, 5.6};  // 123245.6 als falsches Ergebnis
+        analogs = { };
+        expected = "123236";
+        expected_extended= "123235.6";
+        
+        // checkConsistency=true
+        result = process_doFlow(analogs, digits, Digital100, false, false);
+        TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
+
+
+        // checkConsistency=true
+        result = process_doFlow(analogs, digits, Digital100, true, false);
+        TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
+
+        // extendResolution=true
+        result = process_doFlow(analogs, digits, Digital100, false, true);
+        TEST_ASSERT_EQUAL_STRING(expected_extended, result.c_str());
+
+       // Fehler bei V11.2.0 
+        // https://github.com/jomjol/AI-on-the-edge-device/discussions/950#discussioncomment-3661982
+        digits = { 3.0, 2.0, 4.1, 9.0, 4.0, 6.3, 9.2};  // 3249.459 als falsches Ergebnis
+        analogs = { };
+        expected = "3249.469";
+        expected_extended= "3249.4692";
+
+        // checkConsistency=true
+        result = process_doFlow(analogs, digits, Digital100, false, false, -3);
+        TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
+
+
+        // checkConsistency=true
+        // checkConsistency NOT working correctly
+        //result = process_doFlow(analogs, digits, Digital100, true, false, -3);
+        //TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
+
+        // extendResolution=true
+        result = process_doFlow(analogs, digits, Digital100, false, true, -3);
+        TEST_ASSERT_EQUAL_STRING(expected_extended, result.c_str());
+
+       // Fehler bei V11.2.0 
+        // https://github.com/jomjol/AI-on-the-edge-device/issues/1020#issue-1375648891
+        digits = { 0.0, 2.0, 6.1, 9.2};  // 259.9227 als falsches Ergebnis
+        analogs = { 9.0, 2.5, 2.9, 7.2};
+        expected = "269.9227";
+        expected_extended= "269.92272";
+        // Float Value reduziert die Genauigkeit hier. Korrekt wäre
+        // expected_extended= "269.92272";
+
+        // checkConsistency=true
+        result = process_doFlow(analogs, digits, Digital100, false, false);
+        TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
+
+
+        // checkConsistency=true
+        // checkConsistency NOT working correctly
+        //result = process_doFlow(analogs, digits, Digital100, true, false, -3);
+        //TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
+
+
+        // checkConsistency=true und extendResolution=true
+        result = process_doFlow(analogs, digits, Digital100, false, true);
+        TEST_ASSERT_EQUAL_STRING(expected_extended, result.c_str());
+
 }
 
 
 
-void setUpClassFlowPostprocessing(void)
+void setUpClassFlowPostprocessing(t_CNNType digType, t_CNNType anaType)
 {
     
     // wird im doFlow verwendet
@@ -170,18 +315,19 @@ void setUpClassFlowPostprocessing(void)
     FlowControll.push_back(flowmakeimage);
 
     // Die Modeltypen werden gesetzt, da keine Modelle verwendet werden.
-    _analog = new ClassFlowCNNGeneral(nullptr, Analogue100);
+    _analog = new ClassFlowCNNGeneral(nullptr, anaType);
     
-    _digit =  new ClassFlowCNNGeneral(nullptr, Digital100);
+    _digit =  new ClassFlowCNNGeneral(nullptr, digType);
 
     undertestPost = new UnderTestPost(&FlowControll, _analog, _digit);
   
 }
 
 
-std::string process_doFlow(std::vector<float> analog, std::vector<float> digits) {
+std::string process_doFlow(std::vector<float> analog, std::vector<float> digits, t_CNNType digType, 
+            bool checkConsistency, bool extendedResolution, int decimal_shift) {
     // setup the classundertest
-    setUpClassFlowPostprocessing();
+    setUpClassFlowPostprocessing(digType, Analogue100);
 
     printf("SetupClassFlowPostprocessing completed.\n");
 
@@ -189,11 +335,11 @@ std::string process_doFlow(std::vector<float> analog, std::vector<float> digits)
     if (digits.size()>0) {
         general* gen_digit = _digit->GetGENERAL("default", true);
         gen_digit->ROI.clear();
-
         for (int i = 0; i<digits.size(); i++) {
             roi* digitROI = new roi();
             string name = "digit_" + std::to_string(i);
             digitROI->name = name;
+            digitROI->result_klasse = (int) digits[i];
             digitROI->result_float = digits[i];
             gen_digit->ROI.push_back(digitROI);
         }
@@ -215,6 +361,30 @@ std::string process_doFlow(std::vector<float> analog, std::vector<float> digits)
     printf("Setup ROIs completed.\n");
 
     undertestPost->InitNUMBERS();
+    if (checkConsistency) {
+        printf("checkConsistency=true\n");
+        std::vector<NumberPost*>* NUMBERS = undertestPost->GetNumbers();    
+        for (int _n = 0; _n < (*NUMBERS).size(); ++_n) {
+            printf("Set checkConsistency on number: %d\n", _n);
+            (*NUMBERS)[_n]->checkDigitIncreaseConsistency = true;
+        }
+    }
+    if (extendedResolution ) {
+       std::vector<NumberPost*>* NUMBERS = undertestPost->GetNumbers();    
+        for (int _n = 0; _n < (*NUMBERS).size(); ++_n) {
+            printf("Set extendedResolution on number: %d\n", _n);
+            (*NUMBERS)[_n]->isExtendedResolution = true;
+        }
+
+    }
+    if (decimal_shift!=0) {
+        std::vector<NumberPost*>* NUMBERS = undertestPost->GetNumbers();    
+        for (int _n = 0; _n < (*NUMBERS).size(); ++_n) {
+            printf("Set decimalshif on number: %d to %d\n", _n, decimal_shift);
+            (*NUMBERS)[_n]->DecimalShift = decimal_shift;
+            (*NUMBERS)[_n]->DecimalShiftInitial = decimal_shift;   
+        }       
+    }
 
     string time;
     // run test

+ 2 - 2
code/version.cpp

@@ -1,4 +1,4 @@
-const char* GIT_REV="234925c";
+const char* GIT_REV="36a02d1";
 const char* GIT_TAG="";
 const char* GIT_BRANCH="master";
-const char* BUILD_TIME="2022-08-28 19:59";
+const char* BUILD_TIME="2022-09-17 10:08";

BIN
firmware/bootloader.bin


BIN
firmware/firmware.bin


BIN
firmware/html.zip


BIN
sd-card/config/ana-class100_0120_s1_q.tflite


BIN
sd-card/config/ana-class100_0130_s1_q.tflite


+ 7 - 7
sd-card/config/config.ini

@@ -25,9 +25,9 @@ Model = /config/dig-cont_0570_s3.tflite
 CNNGoodThreshold = 0.5
 ;LogImageLocation = /log/digit
 ;LogfileRetentionInDays = 3
-main.dig1 294 126 30 54
-main.dig2 343 126 30 54
-main.dig3 391 126 30 54
+main.dig1 294 126 30 54 false
+main.dig2 343 126 30 54 false
+main.dig3 391 126 30 54 false
 
 [Analog]
 Model = /config/ana-cont_11.3.0_s2.tflite
@@ -35,10 +35,10 @@ CNNGoodThreshold = 0.5
 ;LogImageLocation = /log/analog
 ;LogfileRetentionInDays = 3
 ExtendedResolution = true
-main.ana1 432 230 92 92
-main.ana2 379 332 92 92
-main.ana3 283 374 92 92
-main.ana4 155 328 92 92
+main.ana1 432 230 92 92 false
+main.ana2 379 332 92 92 false
+main.ana3 283 374 92 92 false
+main.ana4 155 328 92 92 false
 
 [PostProcessing]
 main.DecimalShift = 0

BIN
sd-card/config/dig-class100-0130_s2_q.tflite


BIN
sd-card/config/dig-class100_0120_s2_q.tflite


Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
sd-card/html/FileSaver.min.js


Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
sd-card/html/FileSaver.min.js.map


+ 140 - 0
sd-card/html/backup.html

@@ -0,0 +1,140 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="icon" href="favicon.ico" type="image/x-icon">
+<title>Backup/Restore Configuration</title>
+<meta charset="utf-8">
+
+<style>
+h1 {font-size: 2em;}
+h2 {font-size: 1.5em;}
+h3 {font-size: 1.2em;} 
+p {font-size: 1em;}
+
+input[type=number] {
+	width: 138px;
+	padding: 10px 5px;
+	display: inline-block;
+	border: 1px solid #ccc;
+	font-size: 16px; 
+}
+
+.button {
+	padding: 10px 20px;
+    width: 211px;
+	font-size: 16px;
+}
+</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>
+
+<table border="0">
+    </tr>
+        <td>
+            <button class="button" id="doBackup" type="button" onclick="doBackup()">Create Config backup</button>
+        </td>
+        <td>
+            <p id=progress></p>
+        </td>
+    </tr>
+</table>
+<hr>
+<h2>Restore Configuration</h2>
+<p>Use the <a href="/fileserver/config/" target="_self">File Server</a> to upload individual files.</p>
+</body>
+
+
+<script src="jszip.min.js"></script>
+<script src="FileSaver.min.js"></script>
+<script>
+
+function doBackup() {  
+    document.getElementById("progress").innerHTML = "Creating backup...";
+    
+    // Get hostname
+    try {
+        var xhttp = new XMLHttpRequest();
+        xhttp.open("GET", "/version?type=Hostname", false);
+        xhttp.send();
+        hostname = xhttp.responseText;
+    }
+    catch(err) {
+        alert("Failed to fetch hostname: " + err.message);
+        return;
+    }
+    
+    // get date/time
+    var dateTime = new Date().toJSON().slice(0,10) + "_" + new Date().toJSON().slice(11,19).replaceAll(":", "-");
+    
+    zipFilename = hostname + "_" + dateTime + ".zip";
+    console.log(zipFilename);
+
+    // Get files list
+    try {
+        var xhttp = new XMLHttpRequest();
+        xhttp.open("GET", "/fileserver/config/", false);
+        xhttp.send();
+        
+        var parser = new DOMParser();
+        var content = parser.parseFromString(xhttp.responseText, 'text/html');    }
+    catch(err) {
+        alert("Failed to fetch files list: " + err.message);
+        return;
+    }
+    
+    const list = content.querySelectorAll("a");
+    
+    var urls = [];
+    
+    for (a of list) {
+        url = a.getAttribute("href");
+        urls.push(url);
+    }
+    
+    // Pack as zip and download
+    try {
+        saveZip(zipFilename, urls);
+        }
+    catch(err) {
+        alert("Failed to zip files: " + err.message);
+        return;
+    }
+}
+
+
+const saveZip = (filename, urls) => {
+    if(!urls) return;
+
+    const zip = new JSZip();
+    const folder = zip.folder("");
+    
+    var i = 0;
+    urls.forEach((url) => {        
+        const blobPromise = fetch(url).then((r) => {
+            if (r.status === 200) return r.blob();
+            return Promise.reject(new Error(r.statusText));
+        });
+        const name = url.substring(url.lastIndexOf("/") + 1);
+        folder.file(name, blobPromise);
+    });
+
+    zip.generateAsync({ type: "blob" },
+        function updateCallback(metadata) {
+            var msg = "Progress: " + metadata.percent.toFixed(0) + "%";
+            if(metadata.currentFile) {
+                msg += ", " + metadata.currentFile;
+            }
+
+            console.log(msg);
+            document.getElementById("progress").innerHTML = msg;
+        }
+    ).then((blob) => saveAs(blob, filename));
+};
+
+</script>
+
+</html>

+ 31 - 0
sd-card/html/common.js

@@ -0,0 +1,31 @@
+ 
+var basepath = "http://192.168.178.22";
+        
+function LoadHostname() {
+    _basepath = getbasepath(); 
+
+
+    var xhttp = new XMLHttpRequest();
+    xhttp.addEventListener('load', function(event) {
+        if (xhttp.status >= 200 && xhttp.status < 300) {
+            hostname = xhttp.responseText;
+                document.title = hostname + " - AI on the edge";
+                document.getElementById("id_title").innerHTML  = "Digitizer - AI on the edge - " + hostname;
+        } 
+        else {
+                console.warn(request.statusText, request.responseText);
+        }
+    });
+
+//     var xhttp = new XMLHttpRequest();
+    try {
+            url = _basepath + '/version?type=Hostname';     
+            xhttp.open("GET", url, true);
+            xhttp.send();
+
+    }
+    catch (error)
+    {
+//               alert("Loading Hostname failed");
+    }
+}

+ 15 - 2
sd-card/html/edit_alignment.html

@@ -86,7 +86,10 @@ select {
 
 	<table>
 	  <tr>
-		<td colspan="2"><input class="button" type="submit" name="saveroi" onclick="SaveToConfig()" value="Save all to Config.ini"></td>
+		<td><input class="button" type="submit" name="saveroi" onclick="SaveToConfig()" value="Save all to Config.ini"></td>
+	  </tr> 
+	  <tr>
+                <td><button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate saved config</button></td>
 	  </tr>  
 	</table>
 
@@ -107,7 +110,17 @@ select {
             basepath = "http://192.168.178.26";
             basepath = "",
             param;
-
+    
+function doReboot() {
+    if (confirm("Are you sure you want to reboot? Did you save the config?")) {
+            var stringota = "/reboot";
+            window.location = stringota;
+            window.location.href = stringota;
+            window.location.assign(stringota);
+            window.location.replace(stringota);
+    }
+}
+    
 function ChangeSelection(){
     aktindex = parseInt(document.getElementById("index").value);
     UpdateReference();

+ 36 - 7
sd-card/html/edit_analog.html

@@ -116,18 +116,22 @@ th, td {
 	  <tr>
         <td>x: <input type="number" name="refx" id="refx" step=1 onchange="valuemanualchanged()" tabindex=2></td>	  
 		<td>dx: <input type="number" name="refdx" id="refdx" step=1 onchange="valuemanualchangeddx()" tabindex=4></td>
-		<td rowspan="2"><label for="lockAR"> Lock aspect ratio: </label><input type="checkbox" id="lockAR" name="lockAR" value="1" onclick="changelockAR()" checked tabindex=6></td>
+		<td rowspan="1"><label for="lockAR"> Lock aspect ratio: </label><input type="checkbox" id="lockAR" name="lockAR" value="1" onclick="changelockAR()" checked tabindex=6></td>
 	  </tr>
 	  <tr>
 		<td>y: <input type="number" name="refy" id="refy" step=1 onchange="valuemanualchanged()" tabindex=3></td>	
 		<td>dy: <input type="number" name="refdy" id="refdy" step=1 onchange="valuemanualchanged()" tabindex=5></td>
+		<td rowspan="1"><label for="CCW"> Counter-Clockwise Rotation: </label><input type="checkbox" id="CCW" name="CCW" value="0" onclick="changeCCW()" unchecked tabindex=7></td>
 	  </tr>
 	</table>			 
 </div>	 
 
 	<table>
 	  <tr>
-		<td><input class="button" type="submit" id="saveroi" name="saveroi" onclick="SaveToConfig()" value="Save all to Config.ini" tabindex=7></td>
+		<td><input class="button" type="submit" id="saveroi" name="saveroi" onclick="SaveToConfig()" value="Save all to Config.ini" tabindex=8></td>
+	  </tr>  
+	  <tr>
+                <td><button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate saved config</button></td>
 	  </tr>  
     </table>	
 
@@ -147,8 +151,17 @@ th, td {
             enhanceCon = false;
             lockAR = true;
             basepath = "http://192.168.178.26";
-
-
+    
+function doReboot() {
+    if (confirm("Are you sure you want to reboot? Did you save the config?")) {
+            var stringota = "/reboot";
+            window.location = stringota;
+            window.location.href = stringota;
+            window.location.assign(stringota);
+            window.location.replace(stringota);
+    }
+}
+    
 function EnDisableAnalog() {
         isEnabled = document.getElementById("Category_Analog_enabled").checked;
 
@@ -204,9 +217,9 @@ function newROI(){
     var _roinew = prompt("Please enter name of new ROI", "name");
 
     if (ROIInfo.length > 0)
-        erg = CreateROI(_number, "analog", sel.selectedIndex, _roinew, 1, 1, ROIInfo[aktindex]["dx"], ROIInfo[aktindex]["dy"]);
+        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, 1, 1, 30, 30);
+        erg = CreateROI(_number, "analog", sel.selectedIndex, _roinew, 1, 1, 30, 30, false);
 
     if (erg != "")
         alert(erg);
@@ -234,6 +247,20 @@ function changelockAR(){
     lockAR = document.getElementById("lockAR").checked;
 }
 
+function changeCCW(){
+    var sel = document.getElementById("Numbers_value1");
+    var _number = sel.options[sel.selectedIndex].text;
+
+    ROIInfo = getROIInfo("analog", _number);
+    aktindex = parseInt(document.getElementById("index").value);
+
+    if (document.getElementById("CCW").checked)
+        ROIInfo[aktindex]["CCW"] = "true";
+    else
+        ROIInfo[aktindex]["CCW"] = "false";
+	UpdateROIs();
+}
+
 function ChangeSelection(){
     aktindex = parseInt(document.getElementById("index").value);
 //    lockAR = true;
@@ -249,7 +276,7 @@ function SaveToConfig(){
 }
 
 
-function UpdateROIs(){
+function UpdateROIs(_sel){
     document.getElementById("Category_Analog_enabled").checked = true;
     var sel = document.getElementById("Numbers_value1");
     var _number = sel.options[sel.selectedIndex].text;
@@ -320,6 +347,8 @@ function UpdateROIs(){
     document.getElementById("refy").value = ROIInfo[aktindex]["y"];  
     document.getElementById("refdx").value = ROIInfo[aktindex]["dx"];  
     document.getElementById("refdy").value = ROIInfo[aktindex]["dy"];  
+    document.getElementById("CCW").checked = ROIInfo[aktindex]["CCW"] == "true";
+
     rect.startX = ROIInfo[aktindex]["x"];
     rect.startY = ROIInfo[aktindex]["y"];
     rect.w = ROIInfo[aktindex]["dx"];

+ 6 - 2
sd-card/html/edit_config_param.html

@@ -611,8 +611,12 @@ textarea {
 				<input type="text" id="MQTT_MainTopic_value1">
 			</td>
 			<td style="font-size: 80%;">
-				MQTT main topic, under which the counters are published. The single value will be published with the following key: MAINTOPIC/VALUE_NAME/PARAMETER <br>
-				where parameters are: value, rate, timestamp, error<br>
+				MQTT main topic, under which the counters are published. <br>
+				The single value will be published with the following key: MAINTOPIC/VALUE_NAME/PARAMETER where
+				<ul>
+					<li> VALUE_NAME is the name of the value (a meter might have more than one value) as defined during analog and digital ROI configuration (defaults to "main")</li>
+					<li> and PARAMETERS are: value, rate, timestamp, error</li>
+				</ul>
 				The general connection status can be found in MAINTOPIC/CONNECTION
 			</td>
 		</tr>

+ 14 - 2
sd-card/html/edit_digits.html

@@ -119,6 +119,9 @@ th, td {
 	<table>
 	  <tr>
 		<td><input class="button" type="submit" id="saveroi" name="saveroi" onclick="SaveToConfig()" value="Save all to Config.ini" tabindex=7></td>
+	  </tr> 
+	  <tr>
+                <td><button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate saved config</button></td>
 	  </tr>  
 	</table>
 
@@ -140,6 +143,15 @@ th, td {
             lockAR = true;
             basepath = "http://192.168.178.26";
 
+    function doReboot() {
+        if (confirm("Are you sure you want to reboot? Did you save the config?")) {
+                var stringota = "/reboot";
+                window.location = stringota;
+                window.location.href = stringota;
+                window.location.assign(stringota);
+                window.location.replace(stringota);
+        }
+    }
 
 function EnDisableDigits() {
         isEnabled = document.getElementById("Category_Digits_enabled").checked;
@@ -197,9 +209,9 @@ function newROI() {
     var _roinew = prompt("Please enter name of new ROI", "name");
 
     if (ROIInfo.length > 0)
-        erg = CreateROI(_number, "digit", sel.selectedIndex, _roinew, 1, 1, ROIInfo[aktindex]["dx"], ROIInfo[aktindex]["dy"]);
+        erg = CreateROI(_number, "digit", sel.selectedIndex, _roinew, 1, 1, ROIInfo[aktindex]["dx"], ROIInfo[aktindex]["dy"], 0);
     else
-        erg = CreateROI(_number, "digit", sel.selectedIndex, _roinew, 1, 1, 30, 51);
+        erg = CreateROI(_number, "digit", sel.selectedIndex, _roinew, 1, 1, 30, 51, 0);
 
     if (erg != "")
         alert(erg);

+ 1 - 4
sd-card/html/edit_explain_0.html

@@ -2,14 +2,11 @@
 <html style="width: fit-content">
 <head>
 <link rel="icon" href="favicon.ico" type="image/x-icon">
-<title>jomjol - AI on the edge</title>
+<title>AI on the edge</title>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
 <style>
-.h_iframe iframe {width:995px;height:605px;}
-.h_iframe {width:995px;height:605px;}
-
 h1 {font-size: 2em; margin-block-end: 0.3em;}
 h2 {font-size: 1.5em;margin-block-start: 0.3em;}
 h3 {font-size: 1.2em;}

+ 1 - 4
sd-card/html/edit_explain_6.html

@@ -2,14 +2,11 @@
 <html style="width: fit-content">
 <head>
 <link rel="icon" href="favicon.ico" type="image/x-icon">
-<title>jomjol - AI on the edge</title>
+<title>AI on the edge</title>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
 <style>
-.h_iframe iframe {width:995px;height:605px;}
-.h_iframe {width:995px;height:605px;}
-
 h1 {font-size: 2em; margin-block-end: 0.3em;}
 h2 {font-size: 1.5em;margin-block-start: 0.3em;}
 h3 {font-size: 1.2em;}

+ 14 - 0
sd-card/html/edit_reference.html

@@ -35,6 +35,7 @@ table {
 
 <body style="font-family: arial; padding: 0px 10px;">
     <h2>Create Reference out of Raw Image</h2>
+    <p><b>Note: After saving a new Reference Image, make sure to update the Alignment Marks and the ROI's and reboot once!</b></p>
 
 	<table>
 	  <tr>
@@ -85,6 +86,9 @@ table {
 		</tr>			
 		<tr>		
 			<td><input class="button" type="button" id="updatereferenceimage" value="Update Reference Image" onclick="SaveReference()"></td>
+		</tr>		
+		<tr>		
+			<td><button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate saved Reference</button></td>
 		</tr>
 	</table>
 
@@ -102,6 +106,16 @@ table {
             basepath = "http://192.168.178.26";   
             isActReference = false;
             param;
+            
+        function doReboot() {
+            if (confirm("Are you sure you want to reboot? Did you save the config?")) {
+                    var stringota = "/reboot";
+                    window.location = stringota;
+                    window.location.href = stringota;
+                    window.location.assign(stringota);
+                    window.location.replace(stringota);
+            }
+        }
 
         function doTake(){ 
             var xhttp = new XMLHttpRequest();

+ 1 - 4
sd-card/html/explain_0.html

@@ -2,14 +2,11 @@
 <html style="width: fit-content">
 <head>
 <link rel="icon" href="favicon.ico" type="image/x-icon">
-<title>jomjol - AI on the edge</title>
+<title>AI on the edge</title>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
 <style>
-.h_iframe iframe {width:995px;height:605px;}
-.h_iframe {width:995px;height:605px;}
-
 h1 {font-size: 2em; margin-block-end: 0.3em;}
 h2 {font-size: 1.5em;margin-block-start: 0.3em;}
 h3 {font-size: 1.2em;}

+ 1 - 3
sd-card/html/explain_1.html

@@ -2,13 +2,11 @@
 <html style="width: fit-content">
 <head>
 <link rel="icon" href="favicon.ico" type="image/x-icon">
-<title>jomjol - AI on the edge</title>
+<title>AI on the edge</title>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
 <style>
-.h_iframe iframe {width:995px;height:605px;}
-.h_iframe {width:995px;height:605px;}
 
 h1 {font-size: 2em; margin-block-end: 0.3em;}
 h2 {font-size: 1.5em;margin-block-start: 0.3em;}

+ 1 - 4
sd-card/html/explain_2.html

@@ -2,14 +2,11 @@
 <html style="width: fit-content">
 <head>
 <link rel="icon" href="favicon.ico" type="image/x-icon">
-<title>jomjol - AI on the edge</title>
+<title>AI on the edge</title>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
 <style>
-.h_iframe iframe {width:995px;height:605px;}
-.h_iframe {width:995px;height:605px;}
-
 h1 {font-size: 2em; margin-block-end: 0.3em;}
 h2 {font-size: 1.5em;margin-block-start: 0.3em;}
 h3 {font-size: 1.2em;}

+ 1 - 3
sd-card/html/explain_3.html

@@ -2,13 +2,11 @@
 <html style="width: fit-content">
 <head>
 <link rel="icon" href="favicon.ico" type="image/x-icon">
-<title>jomjol - AI on the edge</title>
+<title>AI on the edge</title>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
 <style>
-.h_iframe iframe {width:995px;height:605px;}
-.h_iframe {width:995px;height:605px;}
 
 h1 {font-size: 2em; margin-block-end: 0.3em;}
 h2 {font-size: 1.5em;margin-block-start: 0.3em;}

+ 1 - 4
sd-card/html/explain_4.html

@@ -2,14 +2,11 @@
 <html style="width: fit-content">
 <head>
 <link rel="icon" href="favicon.ico" type="image/x-icon">
-<title>jomjol - AI on the edge</title>
+<title>AI on the edge</title>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
 <style>
-.h_iframe iframe {width:995px;height:605px;}
-.h_iframe {width:995px;height:605px;}
-
 h1 {font-size: 2em; margin-block-end: 0.3em;}
 h2 {font-size: 1.5em;margin-block-start: 0.3em;}
 h3 {font-size: 1.2em;}

+ 1 - 4
sd-card/html/explain_5.html

@@ -2,14 +2,11 @@
 <html style="width: fit-content">
 <head>
 <link rel="icon" href="favicon.ico" type="image/x-icon">
-<title>jomjol - AI on the edge</title>
+<title>AI on the edge</title>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
 <style>
-.h_iframe iframe {width:995px;height:605px;}
-.h_iframe {width:995px;height:605px;}
-
 h1 {font-size: 2em; margin-block-end: 0.3em;}
 h2 {font-size: 1.5em;margin-block-start: 0.3em;}
 h3 {font-size: 1.2em;}

+ 1 - 3
sd-card/html/explain_6.html

@@ -2,13 +2,11 @@
 <html style="width: fit-content">
 <head>
 <link rel="icon" href="favicon.ico" type="image/x-icon">
-<title>jomjol - AI on the edge</title>
+<title>AI on the edge</title>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 
 <style>
-.h_iframe iframe {width:995px;height:605px;}
-.h_iframe {width:995px;height:605px;}
 
 h1 {font-size: 2em; margin-block-end: 0.3em;}
 h2 {font-size: 1.5em;margin-block-start: 0.3em;}

+ 1 - 1
sd-card/html/gethost.js

@@ -20,7 +20,7 @@ function getbasepath(){
     }
     else
     {
-        host = "http://" + host;
+        host = window.location.protocol + "//" + host;
         if (window.location.port != "") {
             host = host + ":" + window.location.port;
         }

+ 103 - 94
sd-card/html/index.html

@@ -1,78 +1,123 @@
 <!DOCTYPE html>
-<html style="width: fit-content">
+<html>
 <head>
 <link rel="icon" href="favicon.ico" type="image/x-icon">
-<title>jomjol - AI on the edge</title>
+<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" type="text/css" > -->
 <style>
-.h_iframe iframe {width:995px;height:605px;}
-.h_iframe {width:995px;height:605px;}
+/* Older Firmware versions (11.2.0 and older) do not support css files, therefore we need to keep it integrated for now! */
+body, html {
+    width: 100%; 
+    height: 100%; 
+    min-height: 800px;
+    margin: 0px 0px 0px 2px; 
+    padding: 0; 
+    font-family: arial;
+    width: fit-content;
+}
+
+.main {
+    display: flex; 
+    width: 100%; 
+    height: 100%; 
+    flex-direction: column; 
+    overflow: hidden;
+}
 
-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;}
+.iframe {
+    flex-grow: 1;
+    margin: 5px 7px 4px 0px; 
+    padding: 0; 
+    border: 2px solid black;
+}
+
+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;
+}
 
 ul {
-  list-style-type: none;
-  margin: 0;
-  padding: 0;
-  overflow: hidden;
-  background-color: #333;
-  width:1000px;
+    list-style-type: none;
+    margin: 0;
+    padding: 0;
+    overflow: hidden;
+    background-color: #333;
+    width:1000px;
 }
 
 li {
-  float: left;
-  font-family: arial;
-  font-size: 18px;
+    float: left;
+    font-family: arial;
+    font-size: 18px;
 }
 
 li a, .dropbtn {
-  display: inline-block;
-  color: white;
-  text-align: center;
-  padding: 14px 16px;
-  text-decoration: none;
+    display: inline-block;
+    color: white;
+    text-align: center;
+    padding: 14px 16px;
+    text-decoration: none;
 }
 
 li a:hover, .dropdown:hover .dropbtn {
-  background-color: red;
+    background-color: red;
 }
 
 li.dropdown {
-  display: inline-block;
+    display: inline-block;
 }
 
 .dropdown-content {
-  display: none;
-  position: absolute;
-  background-color: #f9f9f9;
-  min-width: 160px;
-  box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
-  z-index: 1;
-  font-family: arial;
+    display: none;
+    position: absolute;
+    background-color: #f9f9f9;
+    min-width: 160px;
+    box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
+    z-index: 1;
+    font-family: arial;
 }
 
 .dropdown-content a {
-  color: black;
-  padding: 12px 16px;
-  text-decoration: none;
-  display: block;
-  text-align: left;
+    color: black;
+    padding: 12px 16px;
+    text-decoration: none;
+    display: block;
+    text-align: left;
 }
 
-.dropdown-content a:hover {background-color: #f1f1f1;}
+.dropdown-content a:hover {
+    color: white;
+    background-color: red;
+}
 
 .dropdown:hover .dropdown-content {
-  display: block;
-}
+    display: block;
+} 
 </style>
+
+<script type="text/javascript" src="common.js"></script>
+<script type="text/javascript" src="gethost.js"></script>
+<script type="text/javascript" src="readconfigcommon.js"></script>
+<script type="text/javascript" src="readconfigparam.js"></script>
 </head>
 
-<body style="font-family: arial">
+<body>
+<div class="main">
 
 <table style="border: none">
   <tr><td style="padding-right: 10px;"><img src="favicon.ico"></td>
@@ -82,71 +127,35 @@ li.dropdown {
 </table>
 
 <ul>
-  <li><a href="#"onclick="document.getElementById('maincontent').src = '/wasserzaehler_roi.html';">Overview</a></li>
-	<li class="dropdown">
+    <li><a href="#"onclick="document.getElementById('maincontent').src = '/wasserzaehler_roi.html';">Overview</a></li>
+    <li class="dropdown">
 	<a href="javascript:void(0)" class="dropbtn">Configuration</a>
 	<div class="dropdown-content">
-	  <a href="#"onclick="document.getElementById('maincontent').src = '/prevalue_set.html';">Set preValue</a>
-	  <a href="index_configure.html">Edit Configuration</a>
+	    <a href="#"onclick="document.getElementById('maincontent').src = '/prevalue_set.html';">Set Previous Value</a>
+	    <a href="index_configure.html">Edit Configuration</a>
 	</div>
-  </li>
-  <li><a href="#"onclick="document.getElementById('maincontent').src = '/wasserzaehler.html?full';">Recognition</a></li>
-  <li><a href="#"onclick="document.getElementById('maincontent').src = '/fileserver/';">File Server</a></li>
-	<li class="dropdown">
+    </li>
+    <li><a href="#"onclick="document.getElementById('maincontent').src = '/wasserzaehler.html?full';">Recognition</a></li>
+    <li><a href="#"onclick="document.getElementById('maincontent').src = '/fileserver/';">File Server</a></li>
+    <li class="dropdown">
 	<a href="javascript:void(0)" class="dropbtn">System</a>
 	<div class="dropdown-content">
-	  <a href="#"onclick="document.getElementById('maincontent').src = '/ota_page.html';">OTA Update</a>
-	  <a href="#"onclick="document.getElementById('maincontent').src = '/fileserver/log/message/?readonly=true';">Log Viewer</a>      
-	  <a href="#"onclick="document.getElementById('maincontent').src = '/reboot_page.html';">Reboot</a>
-	  <a href="#"onclick="document.getElementById('maincontent').src = '/info.html';">Info</a>      
+  	  <a href="#"onclick="document.getElementById('maincontent').src = '/backup.html';">Backup/Restore</a>
+	    <a href="#"onclick="document.getElementById('maincontent').src = '/ota_page.html';">OTA Update</a>
+	    <a href="#"onclick="document.getElementById('maincontent').src = '/fileserver/log/message/?readonly=true';">Log Viewer</a>      
+	    <a href="#"onclick="document.getElementById('maincontent').src = '/reboot_page.html';">Reboot</a>
+	    <a href="#"onclick="document.getElementById('maincontent').src = '/info.html';">Info</a>      
 	</div> 
+    </li>
 </ul>
-<p>
-
-<div class="h_iframe">
-	 <iframe name="maincontent" id ="maincontent" src="/wasserzaehler_roi.html" title="fileserver" allowfullscreen></iframe> 
-</div>
-
-<script type="text/javascript" src="./gethost.js"></script> 
-<script type="text/javascript" src="./readconfigcommon.js"></script>  
-<script type="text/javascript" src="./readconfigparam.js"></script>  
 
+<iframe name="maincontent" class="iframe" id="maincontent" src="wasserzaehler_roi.html"></iframe>
 
 <script type="text/javascript">
-	var basepath = "http://192.168.178.22";
-
-
-function LoadHostname() {
-	_basepath = getbasepath(); 
-
-
-	var xhttp = new XMLHttpRequest();
-	xhttp.addEventListener('load', function(event) {
-	  if (xhttp.status >= 200 && xhttp.status < 300) {
-		hostname = xhttp.responseText;
-		  document.title = hostname + " - jomjol - AI on the edge";
-		  document.getElementById("id_title").innerHTML  = "Digitizer - AI on the edge - " + hostname;
-	  } else {
-		 console.warn(request.statusText, request.responseText);
-	  }
-	 });
-
-//     var xhttp = new XMLHttpRequest();
-	 try {
-		  url = _basepath + '/version?type=Hostname';     
-		  xhttp.open("GET", url, true);
-		  xhttp.send();
-
-	 }
-	 catch (error)
-	 {
-//               alert("Loading Hostname failed");
-	 }
-  }
-
   LoadHostname();
-
 </script>
 
+</div>
+
 </body>
 </html>

+ 113 - 90
sd-card/html/index_configure.html

@@ -2,76 +2,117 @@
 <html>
 <head>
 <link rel="icon" href="favicon.ico" type="image/x-icon">
-<title>jomjol - AI on the edge</title>
+<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" type="text/css" > -->
 <style>
-.h_iframe iframe {width:995px;height:765px;}
-.h_iframe {width:995px;height:765px;}
+/* Older Firmware versions (11.2.0 and older) do not support css files, therefore we need to keep it integrated for now! */
+body, html {
+    width: 100%; 
+    height: 100%; 
+    min-height: 800px;
+    margin: 0px 0px 0px 2px; 
+    padding: 0; 
+    font-family: arial;
+    width: fit-content;
+}
+
+.main {
+    display: flex; 
+    width: 100%; 
+    height: 100%; 
+    flex-direction: column; 
+    overflow: hidden;
+}
+
+.iframe {
+    flex-grow: 1;
+    margin: 5px 7px 4px 0px; 
+    padding: 0; 
+    border: 2px solid black;
+}
+
+h1 {
+    font-size: 2em; 
+    margin-block-end: 0.3em;
+}
+
+h2 {
+    font-size: 1.5em;
+    margin-block-start: 0.3em;
+}
 
-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;}
+h3 {
+    font-size: 1.2em;
+}
+
+p {
+    font-size: 1em;
+}
 
 ul {
-  list-style-type: none;
-  margin: 0;
-  padding: 0;
-  overflow: hidden;
-  background-color: #333;
-  width:1000px;
+    list-style-type: none;
+    margin: 0;
+    padding: 0;
+    overflow: hidden;
+    background-color: #333;
+    width:1000px;
 }
 
 li {
-  float: left;
-  font-family: arial;
-  font-size: 18px;
+    float: left;
+    font-family: arial;
+    font-size: 18px;
 }
 
 li a, .dropbtn {
-  display: inline-block;
-  color: white;
-  text-align: center;
-  padding: 14px 16px;
-  text-decoration: none;
+    display: inline-block;
+    color: white;
+    text-align: center;
+    padding: 14px 16px;
+    text-decoration: none;
 }
 
 li a:hover, .dropdown:hover .dropbtn {
-  background-color: red;
+    background-color: red;
 }
 
 li.dropdown {
-  display: inline-block;
+    display: inline-block;
 }
 
 .dropdown-content {
-  display: none;
-  position: absolute;
-  background-color: #f9f9f9;
-  min-width: 160px;
-  box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
-  z-index: 1;
-  font-family: arial;
+    display: none;
+    position: absolute;
+    background-color: #f9f9f9;
+    min-width: 160px;
+    box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
+    z-index: 1;
+    font-family: arial;
 }
 
 .dropdown-content a {
-  color: black;
-  padding: 12px 16px;
-  text-decoration: none;
-  display: block;
-  text-align: left;
+    color: black;
+    padding: 12px 16px;
+    text-decoration: none;
+    display: block;
+    text-align: left;
 }
 
-.dropdown-content a:hover {background-color: #f1f1f1;}
+.dropdown-content a:hover {
+    color: white;
+    background-color: red;
+}
 
 .dropdown:hover .dropdown-content {
-  display: block;
-}
+    display: block;
+} 
 </style>
+</head>
 
-<body style="font-family: arial">
+<body>
+<div class="main">
 
 <table style="border: none">
   <tr><td style="padding-right: 10px;"><img src="favicon.ico"></td>
@@ -81,62 +122,44 @@ li.dropdown {
 </table>
 
 <ul>
-	<li aria-current="page"><a href="index.html">Main Page</a>	
-	<li><a href="#"onclick="document.getElementById('maincontent').src = 'edit_config_param.html';">Configuration</a></li>
+    <li aria-current="page"><a href="index.html">Main Page</a>	
+    <li><a href="#"onclick="document.getElementById('maincontent').src = 'edit_config_param.html';">Configuration</a></li>
+    <li class="dropdown">
+	<a href="javascript:void(0)" class="dropbtn">Alignment</a>
+	<div class="dropdown-content">
+		<a href="#"onclick="document.getElementById('maincontent').src = 'edit_reference.html';">Reference Image</a>
+		<a href="#"onclick="document.getElementById('maincontent').src = 'edit_alignment.html';">Alignment Marks</a>
+	</div>
+    </li>
     <li class="dropdown">
-		<a href="javascript:void(0)" class="dropbtn">Alignment</a>
-		<div class="dropdown-content">
-			<a href="#"onclick="document.getElementById('maincontent').src = 'edit_reference.html';">Reference Image</a>
-			<a href="#"onclick="document.getElementById('maincontent').src = 'edit_alignment.html';">Alignment Marks</a>
-		</div>
-	</li>
+	<a href="javascript:void(0)" class="dropbtn">Regions Of Interest (ROI)</a>
+	<div class="dropdown-content">
+		<a href="#"onclick="document.getElementById('maincontent').src = 'edit_digits.html';">Digital ROIs</a>
+		<a href="#"onclick="document.getElementById('maincontent').src = 'edit_analog.html';">Analog ROIs</a>
+	</div> 
+    </li>
     <li class="dropdown">
-		<a href="javascript:void(0)" class="dropbtn">Regions Of Interest (ROI)</a>
-		<div class="dropdown-content">
-			<a href="#"onclick="document.getElementById('maincontent').src = 'edit_digits.html';">Digital ROIs</a>
-			<a href="#"onclick="document.getElementById('maincontent').src = 'edit_analog.html';">Analog ROIs</a>
-		</div> 
-	</li>		
+	<a href="javascript:void(0)" class="dropbtn">System</a>
+	<div class="dropdown-content">
+	    <a href="#"onclick="document.getElementById('maincontent').src = '/backup.html';">Backup/Restore</a>
+	    <a href="#"onclick="document.getElementById('maincontent').src = '/ota_page.html';">OTA Update</a>
+	    <a href="#"onclick="document.getElementById('maincontent').src = '/fileserver/log/message/?readonly=true';">Log Viewer</a>      
+	    <a href="#"onclick="document.getElementById('maincontent').src = '/reboot_page.html';">Reboot</a>
+	    <a href="#"onclick="document.getElementById('maincontent').src = '/info.html';">Info</a>      
+	</div> 
+    </li>
 </ul>
-<p>
-<div class="h_iframe">
-	 <iframe width="1020px" height="650px" name="maincontent" id ="maincontent" src="edit_config_param.html" title="fileserver"></iframe> 
-</div>
-<script type="text/javascript" src="./gethost.js"></script> 
 
-<script type="text/javascript">
-	var basepath = "http://192.168.178.22";
-
-
-function LoadHostname() {
-	_basepath = getbasepath(); 
-  
-	var xhttp = new XMLHttpRequest();
-	xhttp.addEventListener('load', function(event) {
-	  if (xhttp.status >= 200 && xhttp.status < 300) {
-		  hostname = xhttp.responseText;
-      document.title = hostname + " - Configure";
-		  document.getElementById("id_title").innerHTML  = "Configure - " + hostname;
-	  } else {
-		 console.warn(request.statusText, request.responseText);
-	  }
-	 });
-
-//     var xhttp = new XMLHttpRequest();
-	 try {
-		  url = _basepath + '/version?type=Hostname';     
-		  xhttp.open("GET", url, true);
-		  xhttp.send();
-
-	 }
-	 catch (error)
-	 {
-//               alert("Loading Hostname failed");
-	 }
-  }
+<iframe name="maincontent" class="iframe" id="maincontent" src="edit_config_param.html"></iframe>
 
-  LoadHostname();
+<script type="text/javascript" src="common.js"></script>
+<script type="text/javascript" src="gethost.js"></script>
 
+<script type="text/javascript">
+  LoadHostname();
 </script>
+
+</div>
+
 </body>
 </html>

+ 3 - 1
sd-card/html/info.html

@@ -2,7 +2,7 @@
 <html>
 <head>
 <link rel="icon" href="favicon.ico" type="image/x-icon">
-<title>Set PreValue</title>
+<title>Info</title>
 <meta charset="utf-8">
 
 <style>
@@ -136,6 +136,8 @@ div {
   </tr>
 </table>
 
+<h3>Copyright</h3>
+Copyright &copy; 2020 - 2022 by <a href="https://github.com/jomjol/AI-on-the-edge-device" target=_blank>Jomjol</a> and others.
 
 </body>
 </html>

Разлика између датотеке није приказан због своје велике величине
+ 12 - 0
sd-card/html/jszip.min.js


+ 18 - 35
sd-card/html/ota_page.html

@@ -29,7 +29,9 @@ input[type=number] {
 </head>
 
 <body style="font-family: arial; padding: 0px 10px;">
-<h3>It is strongly recommended to update firmware and content of /html directory on SD-card at the same time!</h3>
+Check at <a href="https://github.com/jomjol/AI-on-the-edge-device/releases" target=_blank>https://github.com/jomjol/AI-on-the-edge-device/releases</a> to see if there is an update available.
+<h3>It is strongly recommended to update firmware and web interface (stored separately in the html directory on SD-card) at the same time!</h3>
+<hr>
 <h2>1. Firmware Update</h2>
 <table class="fixed" border="0">
     <tr>
@@ -37,18 +39,21 @@ input[type=number] {
             <table border="0">
                 <tr>
                     <td style="width: 230px">
-                        <label for="newfile">Select the firmware file:</label>
+                        <label for="newfile">Select the firmware file (firmware.bin):</label>
                     </td>
                     <td colspan="2">
                         <input id="newfile" type="file" onchange="setpath()" style="width:100%;">
                     </td>
+                    <td rowspan="2" style="padding-left:50px">
+                        <button class="button" id="doUpdate" type="button" onclick="doUpdate()">Flash the firmware<br>(Takes about 60s)</button>
+                    </td>
                 </tr>
                 <tr>
                     <td>
                         <label for="filepath">Set path on server:</label>
                     </td>
                     <td>
-                        <input id="filepath" type="text" style="width:100%;" readonly>
+                        <input id="filepath" type="text" style="width:100%; border: 0" readonly>
                     </td>
                     <td>
                         <button id="upload" type="button" onclick="upload()">Upload</button>
@@ -57,41 +62,29 @@ input[type=number] {
             </table>
         </td>
     </tr>
-	<tr>
-		<td>
-            <table border="0">
-                <tr>
-                    <td style="width: 230px">
-                        <button class="button" id="doUpdate" type="button" onclick="doUpdate()">Flash the firmware</button>
-                    </td>
-					<td>
-						(Takes about 60s)
-					</td>
-                </tr>
-            </table>		
-		</td>
-	</tr>
 </table>
-
-<h2>2. Update "/html" directory</h2>
+<h2>2. Web Interface Update</h2>
 <table class="fixed" border="0">
     <tr>
         <td>
             <table border="0">
                 <tr>
                     <td style="width: 230px">
-                        <label for="newfilehtml">Select the zipped /html content:</label>
+                        <label for="newfilehtml">Select the Web Interface file (html.zip):</label>
                     </td>
                     <td colspan="2">
                         <input id="newfilehtml" type="file" onchange="setpathhtml()" style="width:100%;">
                     </td>
+                    <td rowspan="2" style="padding-left:50px">
+			<button class="button" id="doUpdatehtml" type="button" onclick="doUpdatehtml()">Update Web Interface</button>
+                    </td>
                 </tr>
                 <tr>
                     <td>
                         <label for="filepathhtml">Set path on server:</label>
                     </td>
                     <td>
-                        <input id="filepathhtml" type="text" style="width:100%;" readonly>
+                        <input id="filepathhtml" type="text" style="width:100%; border: 0" readonly>
                     </td>
                     <td>
                         <button id="uploadhtml" type="button" onclick="uploadhtml()">Upload</button>
@@ -100,21 +93,12 @@ input[type=number] {
             </table>
         </td>
     </tr>
-	<tr>
-		<td>
-			<button class="button" id="doUpdatehtml" type="button" onclick="doUpdatehtml()">Update "/html" directory</button>
-		</td>
-	</tr>
 </table>
 <h2>3. Reboot</h2>
-<table class="fixed" border="0">
-	<tr>
-		<td>
 			<button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate updates</button>
-		</td>
-	</tr>
-</table>
-<h2>4. Upload neural network definition (tfl/tflite file)</h2>
+<hr>
+<h2>Upload Neural Network Definition (tfl/tflite file)</h2>
+<b>The file must be set active afterwards on the <a href="index_configure.html" target="_parent">Config</a> page</b>!
 <table class="fixed" border="0">
     <tr>
         <td>
@@ -132,7 +116,7 @@ input[type=number] {
                         <label for="filepathtfl">Set path on server</label>
                     </td>
                     <td>
-                        <input id="filepathtfl" type="text" style="width:100%;" readonly>
+                        <input id="filepathtfl" type="text" style="width:100%; border: 0" readonly>
                     </td>
                     <td>
                         <button id="uploadtfl" type="button" onclick="uploadtfl()" disabled>Upload</button>
@@ -141,7 +125,6 @@ input[type=number] {
             </table>
         </td>
     </tr>
-    The file must be activated in the config.ini file.
 </table>
 
 

+ 207 - 0
sd-card/html/ota_page_new.html

@@ -0,0 +1,207 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="icon" href="favicon.ico" type="image/x-icon">
+<title>OTA Update</title>
+<meta charset="utf-8">
+
+<style>
+h1 {font-size: 2em;}
+h2 {font-size: 1.5em;}
+h3 {font-size: 1.2em;} 
+p {font-size: 1em;}
+
+input[type=number] {
+	width: 138px;
+	padding: 10px 5px;
+	display: inline-block;
+	border: 1px solid #ccc;
+	font-size: 16px; 
+}
+
+.button {
+	padding: 10px 20px;
+    width: 211px;
+	font-size: 16px;
+}
+</style>
+
+</head>
+
+<body style="font-family: arial; padding: 0px 10px;">
+Check at <a href="https://github.com/jomjol/AI-on-the-edge-device/releases" target=_blank>https://github.com/jomjol/AI-on-the-edge-device/releases</a> to see if there is an update available.
+<h3>It is strongly recommended to update firmware and web interface (stored separately in the html directory on SD-card) at the same time!</h3>
+<hr>
+<h2>Update</h2>
+<table class="fixed" border="0">
+    <tr>
+        <td>
+            <table border="0">
+                <tr>
+                    <td style="width: 230px">
+                        <label for="newfile">Select the update file (update.zip, firmware.bin, html.zip, *.tfl/tflite):</label>
+                    </td>
+                    <td colspan="2">
+                        <input id="newfile" type="file" onchange="setpath()" style="width:100%;">
+                    </td>
+                    <td rowspan="2" style="padding-left:50px">
+                        <button class="button" id="doUpdate" type="button" onclick="doUpdate()">Flash the firmware<br>(Takes about 60s)</button>
+                    </td>
+                </tr>
+                <tr>
+                    <td>
+                        <label for="filepath">Selected upload file:</label>
+                    </td>
+                    <td>
+                        <input id="filepath" type="text" style="width:100%; border: 0" readonly>
+                    </td>
+                    <td>
+                        <button id="upload" type="button" onclick="upload()">Upload</button>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+</table>
+<h2>Reboot</h2>
+			<button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate updates</button>
+<hr>
+
+<script type="text/javascript" src="./gethost.js"></script> 
+
+
+<script language="JavaScript">
+
+var basepath = "http://192.168.178.26";
+
+
+function init(){
+    basepath = getbasepath();
+
+    document.getElementById("reboot").disabled = true;
+    document.getElementById("upload").disabled = true;
+    document.getElementById("doUpdate").disabled = true;
+}
+
+function doUpdate() {
+	if (confirm("Are you sure to update the firmware?")) {
+		var stringota = "/ota?file=firmware.bin";
+        document.getElementById("doUpdate").disabled = true;
+        
+        var xhttp = new XMLHttpRequest();
+	
+        xhttp.onreadystatechange = function() {
+            if (xhttp.readyState == 4) {
+                if (xhttp.status == 200) {
+                    document.getElementById("reboot").disabled = false;
+                    alert("Flash successfull - Reboot necessary to make changes active.");
+                    /* keine Reaktion, damit sich das Dokument nicht ändert */
+                } else if (xhttp.status == 0) {
+                    alert("Server closed the connection abruptly!");
+                    UpdatePage();
+                } else {
+                    alert(xhttp.status + " Error!\n" + xhttp.responseText);
+                    UpdatePage();
+                }
+            }
+        };
+        xhttp.open("GET", stringota, true);
+        xhttp.send();        
+	}
+}
+
+
+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);
+	}
+}
+
+function setpath() {
+    var nameneu = document.getElementById("newfile").value;
+    nameneu = nameneu.split(/[\\\/]/).pop();
+    document.getElementById("filepath").value = nameneu;
+    document.getElementById("upload").disabled = false;
+}
+
+
+function upload() {
+	var xhttp = new XMLHttpRequest();
+
+    var filePath = document.getElementById("filepath").value;
+    var upload_path = "/upload/firmware/" + filePath;
+    var fileInput = document.getElementById("newfile").files;
+
+	/* first delete the old firmware */	
+	xhttp.onreadystatechange = function() {
+		if (xhttp.readyState == 4) {
+			if (xhttp.status == 200) {
+				/* keine Reaktion, damit sich das Dokument nicht ändert */
+			} else if (xhttp.status == 0) {
+				alert("Server closed the connection abruptly!");
+				UpdatePage();
+			} else {
+				alert(xhttp.status + " Error!\n" + xhttp.responseText);
+				UpdatePage();
+			}
+		}
+	};
+    var _toDo = basepath + "/ota?delete=" + filePath;
+	xhttp.open("GET", _toDo, false);
+	xhttp.send();
+	/* ----------------------------- */
+	
+
+    /* Max size of an individual file. Make sure this
+     * value is same as that set in file_server.c */
+    var MAX_FILE_SIZE = 6000*1024;
+    var MAX_FILE_SIZE_STR = "6MB";
+
+    if (fileInput.length == 0) {
+        alert("No file selected!");
+    } else if (filePath.length == 0) {
+        alert("File path on server is not set!");
+    } else if (filePath.indexOf(' ') >= 0) {
+        alert("File path on server cannot have spaces!");
+    } else if (filePath[filePath.length-1] == '/') {
+        alert("File name not specified after path!");
+    } else if (fileInput[0].size > MAX_FILE_SIZE) {
+        alert("File size must be less than " + MAX_FILE_SIZE_STR + "!");
+    } else {
+        document.getElementById("newfile").disabled = true;
+        document.getElementById("filepath").disabled = true;
+        document.getElementById("upload").disabled = true;
+		
+        xhttp.onreadystatechange = function() {
+            if (xhttp.readyState == 4) {
+                if (xhttp.status == 200) {
+					alert("Upload successfull!")
+//                    document.reload();
+                    document.getElementById("reboot").disabled = false;
+                    document.getElementById("doUpdate").disabled = false;
+                } else if (xhttp.status == 0) {
+                    alert("Server closed the connection abruptly!");
+                    UpdatePage();
+                } else {
+                    alert(xhttp.status + " Error!\n" + xhttp.responseText);
+                    UpdatePage();
+                }
+            }
+        };
+		
+
+        var file = fileInput[0];
+        xhttp.open("POST", upload_path, true);
+        xhttp.send(file);
+    }
+}
+
+init();
+
+</script>
+</body>
+</html>

+ 1 - 1
sd-card/html/ota_page_v2.html

@@ -1,7 +1,7 @@
 <html>
 <head>
 <link rel="icon" href="data:,">
-	<title>jomjol - AI on the edge</title>
+	<title>AI on the edge</title>
 	<meta charset="utf-8">
 	<meta name="viewport" content="width=device-width, initial-scale=1">
 	<script type="text/javascript">

+ 9 - 6
sd-card/html/readconfigparam.js

@@ -86,8 +86,6 @@ function ParseConfig() {
      ParamAddValue(param, catname, "CNNGoodThreshold", 1); 
      ParamAddValue(param, catname, "LogImageLocation");
      ParamAddValue(param, catname, "LogfileRetentionInDays");
-//     ParamAddValue(param, catname, "ModelInputSize", 2); 
-         
 
      var catname = "Analog";
      category[catname] = new Object(); 
@@ -97,7 +95,6 @@ function ParseConfig() {
      ParamAddValue(param, catname, "Model");
      ParamAddValue(param, catname, "LogImageLocation");
      ParamAddValue(param, catname, "LogfileRetentionInDays");
-//     ParamAddValue(param, catname, "ModelInputSize", 2);
 
      var catname = "PostProcessing";
      category[catname] = new Object(); 
@@ -236,9 +233,9 @@ function ParseConfigParamAll(_aktline, _catname){
           let [isCom, input] = isCommented(_input);
           var linesplit = ZerlegeZeile(input);
           ParamExtractValueAll(param, linesplit, _catname, _aktline, isCom);
-          if (!isCom && (linesplit.length == 5) && (_catname == 'Digits'))
+          if (!isCom && (linesplit.length >= 5) && (_catname == 'Digits'))
                ExtractROIs(input, "digit");
-          if (!isCom && (linesplit.length == 5) && (_catname == 'Analog'))
+          if (!isCom && (linesplit.length >= 5) && (_catname == 'Analog'))
                ExtractROIs(input, "analog");
           if (!isCom && (linesplit.length == 3) && (_catname == 'Alignment'))
           {
@@ -398,6 +395,7 @@ function WriteConfigININew()
                               text = text + " " + NUMBERS[_roi]["digit"][_roiddet]["y"];
                               text = text + " " + NUMBERS[_roi]["digit"][_roiddet]["dx"];
                               text = text + " " + NUMBERS[_roi]["digit"][_roiddet]["dy"];
+                              text = text + " " + NUMBERS[_roi]["digit"][_roiddet]["CCW"];
                               config_split.push(text);
                          }
                     }
@@ -416,6 +414,7 @@ function WriteConfigININew()
                               text = text + " " + NUMBERS[_roi]["analog"][_roiddet]["y"];
                               text = text + " " + NUMBERS[_roi]["analog"][_roiddet]["dx"];
                               text = text + " " + NUMBERS[_roi]["analog"][_roiddet]["dy"];
+                              text = text + " " + NUMBERS[_roi]["analog"][_roiddet]["CCW"];
                               config_split.push(text);
                          }
                     }
@@ -484,6 +483,9 @@ function ExtractROIs(_aktline, _type){
      abc["dx"] = linesplit[3];
      abc["dy"] = linesplit[4];
      abc["ar"] = parseFloat(linesplit[3]) / parseFloat(linesplit[4]);
+     abc["CCW"] = "false";
+     if (linesplit.length >= 6)
+          abc["CCW"] = linesplit[5];
 }
 
 
@@ -712,7 +714,7 @@ function DeleteNUMBER(_delte){
      return "";
 }
 
-function CreateROI(_number, _type, _pos, _roinew, _x, _y, _dx, _dy){
+function CreateROI(_number, _type, _pos, _roinew, _x, _y, _dx, _dy, _CCW){
      _indexnumber = -1;
      for (j = 0; j < NUMBERS.length; ++j)
           if (NUMBERS[j]["name"] == _number)
@@ -735,6 +737,7 @@ function CreateROI(_number, _type, _pos, _roinew, _x, _y, _dx, _dy){
      _ret["dx"] = _dx;
      _ret["dy"] = _dy;
      _ret["ar"] = _dx / _dy;
+     _ret["CCW"] = _CCW;
 
      NUMBERS[_indexnumber][_type].splice(_pos+1, 0, _ret);
 

+ 1 - 1
sd-card/html/setup.html

@@ -2,7 +2,7 @@
 <html style="width: fit-content">
 <head>
 <link rel="icon" href="favicon.ico" type="image/x-icon">
-<title>jomjol - AI on the edge</title>
+<title>AI on the edge</title>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 

+ 100 - 0
sd-card/html/style.css

@@ -0,0 +1,100 @@
+body, html {
+    width: 100%; 
+    height: 100%; 
+    min-height: 800px;
+    margin: 0px 0px 0px 2px; 
+    padding: 0; 
+    font-family: arial;
+    width: fit-content;
+}
+
+.main {
+    display: flex; 
+    width: 100%; 
+    height: 100%; 
+    flex-direction: column; 
+    overflow: hidden;
+}
+
+.iframe {
+    flex-grow: 1;
+    margin: 5px 7px 4px 0px; 
+    padding: 0; 
+    border: 2px solid black;
+}
+
+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;
+}
+
+ul {
+    list-style-type: none;
+    margin: 0;
+    padding: 0;
+    overflow: hidden;
+    background-color: #333;
+    width:1000px;
+}
+
+li {
+    float: left;
+    font-family: arial;
+    font-size: 18px;
+}
+
+li a, .dropbtn {
+    display: inline-block;
+    color: white;
+    text-align: center;
+    padding: 14px 16px;
+    text-decoration: none;
+}
+
+li a:hover, .dropdown:hover .dropbtn {
+    background-color: red;
+}
+
+li.dropdown {
+    display: inline-block;
+}
+
+.dropdown-content {
+    display: none;
+    position: absolute;
+    background-color: #f9f9f9;
+    min-width: 160px;
+    box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
+    z-index: 1;
+    font-family: arial;
+}
+
+.dropdown-content a {
+    color: black;
+    padding: 12px 16px;
+    text-decoration: none;
+    display: block;
+    text-align: left;
+}
+
+.dropdown-content a:hover {
+    color: white;
+    background-color: red;
+}
+
+.dropdown:hover .dropdown-content {
+    display: block;
+} 

+ 1 - 1
sd-card/html/version.txt

@@ -1 +1 @@
-15.1.0
+16.4.1

+ 34 - 2
sd-card/html/wasserzaehler_roi.html

@@ -20,7 +20,7 @@
 
 <table class="tg">
   <tr>
-    <td class="tg-1" rowspan="9"><div id="img"></div></td>
+    <td class="tg-1" rowspan="9" style="vertical-align: top"><div id="img"></div></td>
     <th class="th">Value:</th>
   </tr>	
   <tr>	
@@ -57,6 +57,8 @@
     <td class="tg-3">
 	<div id="timestamp" ></div>	
 	<div id="statusflow" ></div>	
+	<div id="cputemp" ></div>	
+	<div id="rssi" ></div>	
 	</td>	
   </tr>    
 </table>
@@ -83,6 +85,8 @@ function addZero(i) {
 	$('#img').html('<img src="/img_tmp/alg_roi.jpg" style="max-height:555px; display:block; margin-left:auto;  margin-right:auto;"></img>');
 	$('#timestamp').html("Last Page Refresh:" + (h + ":" + m + ":" + s));
 	loadStatus();
+	loadCPUTemp();
+	loadRSSI();
 	refresh();
 	});
 
@@ -94,10 +98,10 @@ function refresh() {
 	var h = addZero(d.getHours());
 	var m = addZero(d.getMinutes());
 	var s = addZero(d.getSeconds());	
+	
 	// reassign the url to be like alg_roi.jpg?timestamp=456784512 based on timestamp
 	$('#img').html('<img src="/img_tmp/alg_roi.jpg?timestamp='+ timestamp +'"max-height:555px; display:block; margin-left:auto;  margin-right:auto;"></img>');
 	$('#timestamp').html("Last Page Refresh:" + (h + ":" + m + ":" + s));
-	loadStatus();
 	init();
 	  refresh();
 	}, 300000);
@@ -120,6 +124,32 @@ function refresh() {
 		xhttp.send();		
 	}
 
+	function loadCPUTemp() {
+		url = basepath + '/cputemp.html';     
+		var xhttp = new XMLHttpRequest();
+		xhttp.onreadystatechange = function() {
+			if (this.readyState == 4 && this.status == 200) {
+				var _rsp = xhttp.responseText;
+				$('#cputemp').html(_rsp);
+			}
+		}
+		xhttp.open("GET", url, true);
+		xhttp.send();		
+	}
+
+	function loadRSSI() {
+		url = basepath + '/rssi.html';     
+		var xhttp = new XMLHttpRequest();
+		xhttp.onreadystatechange = function() {
+			if (this.readyState == 4 && this.status == 200) {
+				var _rsp = xhttp.responseText;
+				$('#rssi').html(_rsp);
+			}
+		}
+		xhttp.open("GET", url, true);
+		xhttp.send();		
+	}
+		
 	function loadValue(_type, _div, _style) {
 		url = basepath + '/wasserzaehler.html?all=true&type=' + _type;     
 		var xhttp = new XMLHttpRequest();
@@ -167,6 +197,8 @@ function refresh() {
 		loadValue("prevalue", "prevalue");
 		loadValue("error", "error", "font-size:8px");
 		loadStatus();
+		loadCPUTemp();
+		loadRSSI();
 	}
 
 	init();

Неке датотеке нису приказане због велике количине промена