Compare commits

..

11 Commits

Author SHA1 Message Date
CaCO3
1ceb47fbc7 . 2025-02-25 00:22:47 +01:00
CaCO3
a413d0228e . 2025-02-25 00:10:29 +01:00
CaCO3
e22eaa2736 . 2025-02-25 00:01:17 +01:00
CaCO3
154d159595 . 2025-02-24 23:58:27 +01:00
CaCO3
23a5458a2b . 2025-02-24 23:53:57 +01:00
CaCO3
89097fa5a2 . 2025-02-24 23:46:38 +01:00
CaCO3
6952db0298 swap HTML folders after extracting 2025-02-24 23:26:55 +01:00
CaCO3
550889c373 rename html folder 2025-02-24 23:10:03 +01:00
CaCO3
1200ee1289 Merge branch 'fix-gz' of https://github.com/jomjol/AI-on-the-edge-device into fix-gz 2025-02-24 21:57:09 +01:00
CaCO3
fceccb90b0 delete HTML directory on an update 2025-02-24 21:56:49 +01:00
CaCO3
d68c95d626 delete HTML directory on an update 2025-02-24 21:30:14 +01:00
60 changed files with 2420 additions and 2895 deletions

View File

@@ -1,10 +1,6 @@
name: Build and Pack
on:
push:
pull_request:
release:
types: [released] # Only trigger on published releases (not drafts or pre-released)
on: [push, pull_request]
jobs:
#########################################################################################
@@ -88,8 +84,7 @@ jobs:
cd ../..
cp -r ./sd-card/html/* ./html/
rm -f ./html/edit_config_template.html # Remove the config page template, it is no longer needed
echo "Replacing variables..."
cd html
find . -type f -exec sed -i 's/$COMMIT_HASH/${{ steps.vars.outputs.sha_short }}/g' {} \;
@@ -290,9 +285,8 @@ jobs:
rm -rf ./sd-card/html
rm -rf ./sd-card/demo
cp -r ./html ./sd-card/ # Overwrite the Web UI with the preprocessed files
rm -f ./sd-card/Readme.md
cp -r ./demo ./sd-card/
cd sd-card; rm -rf html/param-tooltips; zip -r ../manual_setup/sd-card.zip *; cd ..
cd sd-card; zip -r ../manual_setup/sd-card.zip *; cd ..
cd ./manual_setup
- name: Upload manual_setup.zip artifact (Firmware + Bootloader + Partitions + Web UI)
@@ -308,7 +302,7 @@ jobs:
prepare-release:
runs-on: ubuntu-latest
needs: [pack-for-update, pack-for-manual_setup, pack-for-remote_setup]
if: github.event_name == 'release' # Only run when the trigger is a release
if: startsWith(github.ref, 'refs/tags/')
# Sets permissions of the GITHUB_TOKEN to allow updating the branches
permissions:
@@ -410,7 +404,7 @@ jobs:
#########################################################################################
# Make sure to also update update-webinstaller.yml!
update-web-installer:
if: github.event_name == 'release' # Only run when the trigger is a release
if: github.event_name == 'release' && github.event.action == 'published' # Only run on release but not on prerelease
needs: [prepare-release]
environment:
name: github-pages
@@ -439,22 +433,22 @@ jobs:
- name: Add binary to Web Installer and update manifest
run: |
echo "Updating Web installer to use firmware from ${{ steps.last_release.outputs.tag_name }}..."
rm -f webinstaller/binary/firmware.bin
rm -f docs/binary/firmware.bin
wget ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.last_release.outputs.tag_name }}/AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
unzip AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
cp -f firmware.bin webinstaller/binary/firmware.bin
cp -f firmware.bin docs/binary/firmware.bin
echo "Updating index and manifest file..."
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' webinstaller/index.html
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' webinstaller/manifest.json
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/index.html
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
- name: Setup Pages
uses: actions/configure-pages@v5
uses: actions/configure-pages@v4
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
uses: actions/upload-pages-artifact@v2
with:
path: 'webinstaller'
path: 'docs'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4.0.5 # Note: v4 does not work!
uses: actions/deploy-pages@v3 # Note: v4 does not work!

View File

@@ -1,4 +1,4 @@
# This updates the Web Installer with the files from the webinstaller folder and the binary of the latest release
# This updates the Web Installer with the files from the docs folder and the binary of the latest release
# it only gets run on:
# - Manually triggered
# Make sure to also update the lower part of build.yml!
@@ -11,7 +11,7 @@ on:
# branches:
# - rolling
# paths:
# - webinstaller # The path filter somehow does not work, so lets run it on every change to rolling
# - docs # The path filter somehow does not work, so lets run it on every change to rolling
jobs:
manually-update-web-installer:
@@ -42,13 +42,13 @@ jobs:
- name: Add binary to Web Installer and update manifest
run: |
echo "Updating Web installer to use firmware from ${{ steps.last_release.outputs.tag_name }}..."
rm -f webinstaller/binary/firmware.bin
wget ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.last_release.outputs.tag_name }}/AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
rm -f docs/binary/firmware.bin
wget https://github.com/jomjol/AI-on-the-edge-device/releases/download/${{ steps.last_release.outputs.tag_name }}/AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
unzip AI-on-the-edge-device__update__${{ steps.last_release.outputs.tag_name }}.zip
cp -f firmware.bin webinstaller/binary/firmware.bin
cp -f firmware.bin docs/binary/firmware.bin
echo "Updating index and manifest file..."
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' webinstaller/index.html
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' webinstaller/manifest.json
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/index.html
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
- name: Setup Pages
uses: actions/configure-pages@v5
@@ -56,7 +56,7 @@ jobs:
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: 'webinstaller'
path: 'docs'
- name: Deploy to GitHub Pages
id: deployment

View File

@@ -18,7 +18,7 @@ permissions:
jobs:
comment:
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4

File diff suppressed because it is too large Load Diff

View File

@@ -250,13 +250,6 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>Frank Haverland</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/SybexX">
<img src="https://avatars.githubusercontent.com/u/587201?v=4" width="100;" alt="SybexX"/>
<br />
<sub><b>michael</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Slider0007">
<img src="https://avatars.githubusercontent.com/u/115730895?v=4" width="100;" alt="Slider0007"/>
@@ -264,6 +257,13 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>Slider0007</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/SybexX">
<img src="https://avatars.githubusercontent.com/u/587201?v=4" width="100;" alt="SybexX"/>
<br />
<sub><b>michael</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/nliaudat">
<img src="https://avatars.githubusercontent.com/u/6782613?v=4" width="100;" alt="nliaudat"/>
@@ -368,13 +368,6 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>parhedberg</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/fsck-block">
<img src="https://avatars.githubusercontent.com/u/58307481?v=4" width="100;" alt="fsck-block"/>
<br />
<sub><b>fsck-block</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/slovdahl">
<img src="https://avatars.githubusercontent.com/u/1417619?v=4" width="100;" alt="slovdahl"/>
@@ -396,6 +389,13 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>LordGuilly</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/bilalmirza74">
<img src="https://avatars.githubusercontent.com/u/84387676?v=4" width="100;" alt="bilalmirza74"/>
<br />
<sub><b>Bilal Mirza</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/muggenhor">
<img src="https://avatars.githubusercontent.com/u/484066?v=4" width="100;" alt="muggenhor"/>
@@ -406,17 +406,10 @@ There are some ideas and feature requests which are not currently being pursued
</tr>
<tr>
<td align="center">
<a href="https://github.com/bilalmirza74">
<img src="https://avatars.githubusercontent.com/u/84387676?v=4" width="100;" alt="bilalmirza74"/>
<a href="https://github.com/ppisljar">
<img src="https://avatars.githubusercontent.com/u/13629809?v=4" width="100;" alt="ppisljar"/>
<br />
<sub><b>Bilal Mirza</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/AngryApostrophe">
<img src="https://avatars.githubusercontent.com/u/89547888?v=4" width="100;" alt="AngryApostrophe"/>
<br />
<sub><b>AngryApostrophe</b></sub>
<sub><b>Peter Pisljar</b></sub>
</a>
</td>
<td align="center">
@@ -433,13 +426,6 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>Ranjana761</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/SURYANSH-RAI">
<img src="https://avatars.githubusercontent.com/u/79277130?v=4" width="100;" alt="SURYANSH-RAI"/>
<br />
<sub><b>SURYANSH RAI</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/SkylightXD">
<img src="https://avatars.githubusercontent.com/u/16561545?v=4" width="100;" alt="SkylightXD"/>
@@ -447,8 +433,6 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>SkylightXD</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/ottk3">
<img src="https://avatars.githubusercontent.com/u/5236802?v=4" width="100;" alt="ottk3"/>
@@ -463,6 +447,8 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>Tobias Bieniek</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/tkopczuk">
<img src="https://avatars.githubusercontent.com/u/101632?v=4" width="100;" alt="tkopczuk"/>
@@ -491,8 +477,6 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>flox_x</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/gneluka">
<img src="https://avatars.githubusercontent.com/u/32097881?v=4" width="100;" alt="gneluka"/>
@@ -507,6 +491,8 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>kalwados</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/kub3let">
<img src="https://avatars.githubusercontent.com/u/95883234?v=4" width="100;" alt="kub3let"/>
@@ -535,8 +521,13 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>smartboart</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/AngryApostrophe">
<img src="https://avatars.githubusercontent.com/u/89547888?v=4" width="100;" alt="AngryApostrophe"/>
<br />
<sub><b>AngryApostrophe</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/wetneb">
<img src="https://avatars.githubusercontent.com/u/309908?v=4" width="100;" alt="wetneb"/>
@@ -544,6 +535,8 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>Antonin Delpeuch</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/adarazs">
<img src="https://avatars.githubusercontent.com/u/6269603?v=4" width="100;" alt="adarazs"/>
@@ -579,8 +572,6 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>Dave</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/FarukhS52">
<img src="https://avatars.githubusercontent.com/u/129654632?v=4" width="100;" alt="FarukhS52"/>
@@ -588,6 +579,8 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>Farookh Zaheer Siddiqui</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/hex7c0">
<img src="https://avatars.githubusercontent.com/u/4419146?v=4" width="100;" alt="hex7c0"/>
@@ -623,8 +616,6 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>Joerg Rosenkranz</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/Innovatorcloudy">
<img src="https://avatars.githubusercontent.com/u/183274513?v=4" width="100;" alt="Innovatorcloudy"/>
@@ -632,6 +623,8 @@ There are some ideas and feature requests which are not currently being pursued
<sub><b>KrishCode</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/myxor">
<img src="https://avatars.githubusercontent.com/u/1397377?v=4" width="100;" alt="myxor"/>
@@ -659,13 +652,6 @@ There are some ideas and feature requests which are not currently being pursued
<br />
<sub><b>Michael Geissler</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/ppisljar">
<img src="https://avatars.githubusercontent.com/u/13629809?v=4" width="100;" alt="ppisljar"/>
<br />
<sub><b>Peter Pisljar</b></sub>
</a>
</td>
</tr>
<tbody>

View File

@@ -4,7 +4,7 @@
```
git clone https://github.com/jomjol/AI-on-the-edge-device.git
cd AI-on-the-edge-device
git checkout main
git checkout rolling
git submodule update --init
```
@@ -12,10 +12,10 @@ git submodule update --init
```
cd /components/submodule-name (e.g. tflite-micro-example)
git checkout VERSION (e.g. HASH of latest tflite-micro-example build)
cd ../../ (at the code level)
cd ../../ (auf Ebene von code)
git submodule update --init
```
You may need to manually delete some directories in the 'components' folder beforehand, as they were not deleted during checkout (before update -- init)
Evt. muss man vorher noch einige Verzeichnisse in compenents von Hand löschen, da sie beim checkout nicht gelöscht wurden (vor update -- init)
## Build and Flash within terminal
See further down to build it within an IDE.
@@ -51,7 +51,7 @@ pio device monitor -p /dev/ttyUSB0
```
git clone https://github.com/jomjol/AI-on-the-edge-device.git
cd AI-on-the-edge-device
git checkout main
git checkout rolling
git submodule update --init
```

View File

@@ -1,226 +0,0 @@
/* Src: https://github.com/Zunawe/md5-c, commit: f3529b6
* License: Unlicense */
/*
* Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm
* and modified slightly to be functionally identical but condensed into control structures.
*/
#include "md5.h"
/*
* Constants defined by the MD5 algorithm
*/
#define A 0x67452301
#define B 0xefcdab89
#define C 0x98badcfe
#define D 0x10325476
static uint32_t S[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
static uint32_t K[] = {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};
/*
* Padding used to make the size (in bits) of the input congruent to 448 mod 512
*/
static uint8_t PADDING[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
/*
* Bit-manipulation functions defined by the MD5 algorithm
*/
#define F(X, Y, Z) ((X & Y) | (~X & Z))
#define G(X, Y, Z) ((X & Z) | (Y & ~Z))
#define H(X, Y, Z) (X ^ Y ^ Z)
#define I(X, Y, Z) (Y ^ (X | ~Z))
/*
* Rotates a 32-bit word left by n bits
*/
uint32_t rotateLeft(uint32_t x, uint32_t n){
return (x << n) | (x >> (32 - n));
}
/*
* Initialize a context
*/
void md5Init(MD5Context *ctx){
ctx->size = (uint64_t)0;
ctx->buffer[0] = (uint32_t)A;
ctx->buffer[1] = (uint32_t)B;
ctx->buffer[2] = (uint32_t)C;
ctx->buffer[3] = (uint32_t)D;
}
/*
* Add some amount of input to the context
*
* If the input fills out a block of 512 bits, apply the algorithm (md5Step)
* and save the result in the buffer. Also updates the overall size.
*/
void md5Update(MD5Context *ctx, uint8_t *input_buffer, size_t input_len){
uint32_t input[16];
unsigned int offset = ctx->size % 64;
ctx->size += (uint64_t)input_len;
// Copy each byte in input_buffer into the next space in our context input
for(unsigned int i = 0; i < input_len; ++i){
ctx->input[offset++] = (uint8_t)*(input_buffer + i);
// If we've filled our context input, copy it into our local array input
// then reset the offset to 0 and fill in a new buffer.
// Every time we fill out a chunk, we run it through the algorithm
// to enable some back and forth between cpu and i/o
if(offset % 64 == 0){
for(unsigned int j = 0; j < 16; ++j){
// Convert to little-endian
// The local variable `input` our 512-bit chunk separated into 32-bit words
// we can use in calculations
input[j] = (uint32_t)(ctx->input[(j * 4) + 3]) << 24 |
(uint32_t)(ctx->input[(j * 4) + 2]) << 16 |
(uint32_t)(ctx->input[(j * 4) + 1]) << 8 |
(uint32_t)(ctx->input[(j * 4)]);
}
md5Step(ctx->buffer, input);
offset = 0;
}
}
}
/*
* Pad the current input to get to 448 bytes, append the size in bits to the very end,
* and save the result of the final iteration into digest.
*/
void md5Finalize(MD5Context *ctx){
uint32_t input[16];
unsigned int offset = ctx->size % 64;
unsigned int padding_length = offset < 56 ? 56 - offset : (56 + 64) - offset;
// Fill in the padding and undo the changes to size that resulted from the update
md5Update(ctx, PADDING, padding_length);
ctx->size -= (uint64_t)padding_length;
// Do a final update (internal to this function)
// Last two 32-bit words are the two halves of the size (converted from bytes to bits)
for(unsigned int j = 0; j < 14; ++j){
input[j] = (uint32_t)(ctx->input[(j * 4) + 3]) << 24 |
(uint32_t)(ctx->input[(j * 4) + 2]) << 16 |
(uint32_t)(ctx->input[(j * 4) + 1]) << 8 |
(uint32_t)(ctx->input[(j * 4)]);
}
input[14] = (uint32_t)(ctx->size * 8);
input[15] = (uint32_t)((ctx->size * 8) >> 32);
md5Step(ctx->buffer, input);
// Move the result into digest (convert from little-endian)
for(unsigned int i = 0; i < 4; ++i){
ctx->digest[(i * 4) + 0] = (uint8_t)((ctx->buffer[i] & 0x000000FF));
ctx->digest[(i * 4) + 1] = (uint8_t)((ctx->buffer[i] & 0x0000FF00) >> 8);
ctx->digest[(i * 4) + 2] = (uint8_t)((ctx->buffer[i] & 0x00FF0000) >> 16);
ctx->digest[(i * 4) + 3] = (uint8_t)((ctx->buffer[i] & 0xFF000000) >> 24);
}
}
/*
* Step on 512 bits of input with the main MD5 algorithm.
*/
void md5Step(uint32_t *buffer, uint32_t *input){
uint32_t AA = buffer[0];
uint32_t BB = buffer[1];
uint32_t CC = buffer[2];
uint32_t DD = buffer[3];
uint32_t E;
unsigned int j;
for(unsigned int i = 0; i < 64; ++i){
switch(i / 16){
case 0:
E = F(BB, CC, DD);
j = i;
break;
case 1:
E = G(BB, CC, DD);
j = ((i * 5) + 1) % 16;
break;
case 2:
E = H(BB, CC, DD);
j = ((i * 3) + 5) % 16;
break;
default:
E = I(BB, CC, DD);
j = (i * 7) % 16;
break;
}
uint32_t temp = DD;
DD = CC;
CC = BB;
BB = BB + rotateLeft(AA + E + K[i] + input[j], S[i]);
AA = temp;
}
buffer[0] += AA;
buffer[1] += BB;
buffer[2] += CC;
buffer[3] += DD;
}
/*
* Functions that run the algorithm on the provided input and put the digest into result.
* result should be able to store 16 bytes.
*/
void md5String(char *input, uint8_t *result){
MD5Context ctx;
md5Init(&ctx);
md5Update(&ctx, (uint8_t *)input, strlen(input));
md5Finalize(&ctx);
memcpy(result, ctx.digest, 16);
}
void md5File(FILE *file, uint8_t *result){
void *input_buffer = malloc(1024);
size_t input_size = 0;
MD5Context ctx;
md5Init(&ctx);
while((input_size = fread(input_buffer, 1, 1024, file)) > 0){
md5Update(&ctx, (uint8_t *)input_buffer, input_size);
}
md5Finalize(&ctx);
free(input_buffer);
memcpy(result, ctx.digest, 16);
}

View File

@@ -1,28 +0,0 @@
/* Src: https://github.com/Zunawe/md5-c, commit: f3529b6
* License: Unlicense */
#pragma once
#ifndef MD5_H
#define MD5_H
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
typedef struct{
uint64_t size; // Size of input in bytes
uint32_t buffer[4]; // Current accumulation of hash
uint8_t input[64]; // Input to be used in the next step
uint8_t digest[16]; // Result of algorithm
}MD5Context;
void md5Init(MD5Context *ctx);
void md5Update(MD5Context *ctx, uint8_t *input, size_t input_len);
void md5Finalize(MD5Context *ctx);
void md5Step(uint32_t *buffer, uint32_t *input);
void md5String(char *input, uint8_t *result);
void md5File(FILE *file, uint8_t *result);
#endif // MD5_H

View File

@@ -36,7 +36,6 @@ extern "C" {
#include "MainFlowControl.h"
#include "server_help.h"
#include "md5.h"
#ifdef ENABLE_MQTT
#include "interface_mqtt.h"
#endif //ENABLE_MQTT
@@ -611,8 +610,6 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
FILE *fd = NULL;
struct stat file_stat;
ESP_LOGI(TAG, "uri: %s", req->uri);
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
/* Skip leading "/upload" from URI to get filename */
@@ -714,77 +711,44 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "File saved: " + string(filename));
ESP_LOGI(TAG, "File reception completed");
string s = req->uri;
if (isInString(s, "?md5")) {
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Calculate and return MD5 sum...");
fd = fopen(filepath, "r");
if (!fd) {
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to open file for reading: " + string(filepath));
/* Respond with 500 Internal Server Error */
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to open file for reading");
return ESP_FAIL;
}
std::string directory = std::string(filepath);
size_t zw = directory.find("/");
size_t found = zw;
while (zw != std::string::npos)
{
zw = directory.find("/", found+1);
if (zw != std::string::npos)
found = zw;
}
uint8_t result[16];
string md5hex = "";
string response = "{\"md5\":";
char hex[3];
int start_fn = strlen(((struct file_server_data *)req->user_ctx)->base_path);
ESP_LOGD(TAG, "Directory: %s, start_fn: %d, found: %d", directory.c_str(), start_fn, found);
directory = directory.substr(start_fn, found - start_fn + 1);
directory = "/fileserver" + directory;
// ESP_LOGD(TAG, "Directory danach 2: %s", directory.c_str());
md5File(fd, result);
fclose(fd);
for (int i = 0; i < sizeof(result); i++) {
snprintf(hex, sizeof(hex), "%02x", result[i]);
md5hex.append(hex);
}
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "MD5 of " + string(filepath) + ": " + md5hex);
response.append("\"" + md5hex + "\"");
response.append("}");
httpd_resp_sendstr(req, response.c_str());
/* Redirect onto root to see the updated file list */
if (strcmp(filename, "/config/config.ini") == 0 ||
strcmp(filename, "/config/ref0.jpg") == 0 ||
strcmp(filename, "/config/ref0_org.jpg") == 0 ||
strcmp(filename, "/config/ref1.jpg") == 0 ||
strcmp(filename, "/config/ref1_org.jpg") == 0 ||
strcmp(filename, "/config/reference.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref0.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref0_org.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref1.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref1_org.jpg") == 0 ||
strcmp(filename, "/img_tmp/reference.jpg") == 0 )
{
httpd_resp_set_status(req, HTTPD_200); // Avoid reloading of folder content
}
else { // Return file server page
std::string directory = std::string(filepath);
size_t zw = directory.find("/");
size_t found = zw;
while (zw != std::string::npos)
{
zw = directory.find("/", found+1);
if (zw != std::string::npos)
found = zw;
}
int start_fn = strlen(((struct file_server_data *)req->user_ctx)->base_path);
ESP_LOGD(TAG, "Directory: %s, start_fn: %d, found: %d", directory.c_str(), start_fn, found);
directory = directory.substr(start_fn, found - start_fn + 1);
directory = "/fileserver" + directory;
// ESP_LOGD(TAG, "Directory danach 2: %s", directory.c_str());
/* Redirect onto root to see the updated file list */
if (strcmp(filename, "/config/config.ini") == 0 ||
strcmp(filename, "/config/ref0.jpg") == 0 ||
strcmp(filename, "/config/ref0_org.jpg") == 0 ||
strcmp(filename, "/config/ref1.jpg") == 0 ||
strcmp(filename, "/config/ref1_org.jpg") == 0 ||
strcmp(filename, "/config/reference.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref0.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref0_org.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref1.jpg") == 0 ||
strcmp(filename, "/img_tmp/ref1_org.jpg") == 0 ||
strcmp(filename, "/img_tmp/reference.jpg") == 0 )
{
httpd_resp_set_status(req, HTTPD_200); // Avoid reloading of folder content
}
else {
httpd_resp_set_status(req, "303 See Other"); // Reload folder content after upload
}
httpd_resp_set_hdr(req, "Location", directory.c_str());
httpd_resp_sendstr(req, "File uploaded successfully");
else {
httpd_resp_set_status(req, "303 See Other"); // Reload folder content after upload
}
httpd_resp_set_hdr(req, "Location", directory.c_str());
httpd_resp_sendstr(req, "File uploaded successfully");
return ESP_OK;
}

View File

@@ -96,15 +96,15 @@ void task_do_Update_ZIP(void *pvParameter)
/* ZIP file got extracted, replace the old html folder with the new one */
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Renaming folder " + outHtml + " to " + outHtmlOld + "...");
RenameFolder(outHtml, outHtmlOld);
::rename(outHtml.c_str(), outHtmlOld.c_str());
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Renaming folder " + outHtmlTmp + " to " + outHtml + "...");
RenameFolder(outHtmlTmp, outHtml);
::rename(outHtmlTmp.c_str(), outHtml.c_str());
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Deleting folder " + outHtmlOld + "...");
removeFolder(outHtmlOld.c_str(), TAG);
if (retfirmware.length() > 0)
{
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Found firmware.bin");
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Found firmware.bin");
ota_update_task(retfirmware);
}

View File

@@ -31,6 +31,7 @@ enum t_RateType {
RateChange // time difference is considered and a normalized rate is used for comparison with NumberPost.maxRate
};
/**
* Holds all properties and settings of a sequence. A sequence is a set of digit and/or analog ROIs that are combined to
* provide one meter reading (value).
@@ -44,7 +45,6 @@ struct NumberPost {
int ChangeRateThreshold; // threshold parameter for negative rate detection
bool PreValueOkay; // previousValueValid; indicates that the reading of the previous round has no errors
bool AllowNegativeRates; // allowNegativeRate; defines if the consistency checks allow negative rates between consecutive meter readings.
bool IgnoreLeadingNaN;
bool checkDigitIncreaseConsistency; // extendedConsistencyCheck; performs an additional consistency check to avoid wrong readings
time_t timeStampLastValue; // Timestamp for the last read value; is used for the log
time_t timeStampLastPreValue; // Timestamp for the last PreValue set; is used for useMaxRateValue
@@ -66,7 +66,7 @@ struct NumberPost {
float AnalogToDigitTransitionStart; // AnalogToDigitTransitionStartValue; FIXME: need a better description; When is the digit > x.1, i.e. when does it start to tilt?
int Nachkomma; // decimalPlaces; usually defined by the number of analog ROIs; affected by DecimalShift
string DomoticzIdx; // Domoticz counter Idx
string DomoticzIdx; // Domoticz counter Idx
string FieldV1; // influxdbFieldName_v1; Name of the Field in InfluxDBv1
string MeasurementV1; // influxdbMeasurementName_v1; Name of the Measurement in InfluxDBv1
@@ -83,3 +83,4 @@ struct NumberPost {
};
#endif

View File

@@ -156,7 +156,7 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
mqttServer_setMeterType("water", "L", "h", "L/h");
}
else if (toUpper(splitted[1]) == "WATER_FT3") {
mqttServer_setMeterType("water", "ft³", "min", "ft³/min"); // min = Minutes
mqttServer_setMeterType("water", "ft³", "m", "ft³/m"); // m = Minutes
}
else if (toUpper(splitted[1]) == "WATER_GAL") {
mqttServer_setMeterType("water", "gal", "h", "gal/h");
@@ -165,7 +165,7 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
mqttServer_setMeterType("gas", "", "h", "m³/h");
}
else if (toUpper(splitted[1]) == "GAS_FT3") {
mqttServer_setMeterType("gas", "ft³", "min", "ft³/min"); // min = Minutes
mqttServer_setMeterType("gas", "ft³", "m", "ft³/m"); // m = Minutes
}
else if (toUpper(splitted[1]) == "ENERGY_WH") {
mqttServer_setMeterType("energy", "Wh", "h", "W");
@@ -180,13 +180,13 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
mqttServer_setMeterType("energy", "GJ", "h", "GJ/h");
}
else if (toUpper(splitted[1]) == "TEMPERATURE_C") {
mqttServer_setMeterType("temperature", "°C", "min", "°C/min"); // min = Minutes
mqttServer_setMeterType("temperature", "°C", "m", "°C/m"); // m = Minutes
}
else if (toUpper(splitted[1]) == "TEMPERATURE_F") {
mqttServer_setMeterType("temperature", "°F", "min", "°F/min"); // min = Minutes
mqttServer_setMeterType("temperature", "°F", "m", "°F/m"); // m = Minutes
}
else if (toUpper(splitted[1]) == "TEMPERATURE_K") {
mqttServer_setMeterType("temperature", "K", "min", "K/m"); // min = Minutes
mqttServer_setMeterType("temperature", "K", "m", "K/m"); // m = Minutes
}
}

View File

@@ -320,6 +320,7 @@ ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc, C
ListFlowControll = lfc;
flowTakeImage = NULL;
UpdatePreValueINI = false;
IgnoreLeadingNaN = false;
flowAnalog = _analog;
flowDigit = _digit;
@@ -430,27 +431,6 @@ void ClassFlowPostProcessing::handleAllowNegativeRate(string _decsep, string _va
}
}
void ClassFlowPostProcessing::handleIgnoreLeadingNaN(string _decsep, string _value) {
string _digit, _decpos;
int _pospunkt = _decsep.find_first_of(".");
if (_pospunkt > -1) {
_digit = _decsep.substr(0, _pospunkt);
}
else {
_digit = "default";
}
for (int j = 0; j < NUMBERS.size(); ++j) {
bool _zwdc = alphanumericToBoolean(_value);
// Set to default first (if nothing else is set)
if ((_digit == "default") || (NUMBERS[j]->name == _digit)) {
NUMBERS[j]->IgnoreLeadingNaN = _zwdc;
}
}
}
void ClassFlowPostProcessing::handleMaxRateType(string _decsep, string _value) {
string _digit, _decpos;
int _pospunkt = _decsep.find_first_of(".");
@@ -529,7 +509,7 @@ void ClassFlowPostProcessing::handleChangeRateThreshold(string _decsep, string _
}
}
}
/*
void ClassFlowPostProcessing::handlecheckDigitIncreaseConsistency(std::string _decsep, std::string _value)
{
std::string _digit;
@@ -552,7 +532,7 @@ void ClassFlowPostProcessing::handlecheckDigitIncreaseConsistency(std::string _d
}
}
}
*/
bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph) {
std::vector<string> splitted;
int _n;
@@ -605,7 +585,12 @@ bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph)
}
if ((toUpper(_param) == "CHECKDIGITINCREASECONSISTENCY") && (splitted.size() > 1)) {
handlecheckDigitIncreaseConsistency(splitted[0], splitted[1]);
// handlecheckDigitIncreaseConsistency(splitted[0], splitted[1]);
if (alphanumericToBoolean(splitted[1])) {
for (_n = 0; _n < NUMBERS.size(); ++_n) {
NUMBERS[_n]->checkDigitIncreaseConsistency = true;
}
}
}
if ((toUpper(_param) == "ALLOWNEGATIVERATES") && (splitted.size() > 1)) {
@@ -617,7 +602,7 @@ bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph)
}
if ((toUpper(_param) == "IGNORELEADINGNAN") && (splitted.size() > 1)) {
handleIgnoreLeadingNaN(splitted[0], splitted[1]);
IgnoreLeadingNaN = alphanumericToBoolean(splitted[1]);
}
if ((toUpper(_param) == "PREVALUEAGESTARTUP") && (splitted.size() > 1)) {
@@ -685,7 +670,6 @@ void ClassFlowPostProcessing::InitNUMBERS() {
_number->FlowRateAct = 0; // m3 / min
_number->PreValueOkay = false;
_number->AllowNegativeRates = false;
_number->IgnoreLeadingNaN = false;
_number->MaxRateValue = 0.1;
_number->MaxRateType = AbsoluteChange;
_number->useMaxRateValue = false;
@@ -837,7 +821,7 @@ bool ClassFlowPostProcessing::doFlow(string zwtime) {
ESP_LOGD(TAG, "After ShiftDecimal: ReturnRaw %s", NUMBERS[j]->ReturnRawValue.c_str());
#endif
if (NUMBERS[j]->IgnoreLeadingNaN) {
if (IgnoreLeadingNaN) {
while ((NUMBERS[j]->ReturnRawValue.length() > 1) && (NUMBERS[j]->ReturnRawValue[0] == 'N')) {
NUMBERS[j]->ReturnRawValue.erase(0, 1);
}
@@ -884,7 +868,12 @@ bool ClassFlowPostProcessing::doFlow(string zwtime) {
if (NUMBERS[j]->checkDigitIncreaseConsistency) {
if (flowDigit) {
NUMBERS[j]->Value = checkDigitConsistency(NUMBERS[j]->Value, NUMBERS[j]->DecimalShift, NUMBERS[j]->analog_roi != NULL, NUMBERS[j]->PreValue);
if (flowDigit->getCNNType() != Digit) {
ESP_LOGD(TAG, "checkDigitIncreaseConsistency = true - ignored due to wrong CNN-Type (not Digit Classification)");
}
else {
NUMBERS[j]->Value = checkDigitConsistency(NUMBERS[j]->Value, NUMBERS[j]->DecimalShift, NUMBERS[j]->analog_roi != NULL, NUMBERS[j]->PreValue);
}
}
else {
#ifdef SERIAL_DEBUG
@@ -898,7 +887,7 @@ bool ClassFlowPostProcessing::doFlow(string zwtime) {
#endif
if (PreValueUse && NUMBERS[j]->PreValueOkay) {
if ((NUMBERS[j]->Nachkomma > 0) && (NUMBERS[j]->ChangeRateThreshold > 0)) {
if (NUMBERS[j]->Nachkomma > 0) {
double _difference1 = (NUMBERS[j]->PreValue - (NUMBERS[j]->ChangeRateThreshold / pow(10, NUMBERS[j]->Nachkomma)));
double _difference2 = (NUMBERS[j]->PreValue + (NUMBERS[j]->ChangeRateThreshold / pow(10, NUMBERS[j]->Nachkomma)));

View File

@@ -10,6 +10,7 @@
#include <string>
class ClassFlowPostProcessing :
public ClassFlow
{
@@ -18,7 +19,8 @@ protected:
int PreValueAgeStartup;
bool ErrorMessage;
bool IgnoreLeadingNaN; // SPECIAL CASE for User Gustl ???
ClassFlowCNNGeneral* flowAnalog;
ClassFlowCNNGeneral* flowDigit;
@@ -33,16 +35,15 @@ protected:
float checkDigitConsistency(double input, int _decilamshift, bool _isanalog, double _preValue);
void InitNUMBERS();
void handleDecimalSeparator(string _decsep, string _value);
void handleMaxRateValue(string _decsep, string _value);
void handleDecimalExtendedResolution(string _decsep, string _value);
void handleMaxRateType(string _decsep, string _value);
void handleAnalogToDigitTransitionStart(string _decsep, string _value);
void handleAllowNegativeRate(string _decsep, string _value);
void handleIgnoreLeadingNaN(string _decsep, string _value);
void handleChangeRateThreshold(string _decsep, string _value);
void handlecheckDigitIncreaseConsistency(std::string _decsep, std::string _value);
std::string GetStringReadouts(general);
void WriteDataLog(int _index);
@@ -74,4 +75,5 @@ public:
string name(){return "ClassFlowPostProcessing";};
};
#endif //CLASSFFLOWPOSTPROCESSING_H

View File

@@ -365,35 +365,19 @@ size_t findDelimiterPos(string input, string delimiter)
bool RenameFile(string from, string to)
{
// ESP_LOGI(logTag, "Renaming File: %s", from.c_str());
// ESP_LOGI(logTag, "Deleting file: %s", fn.c_str());
/* Delete file */
FILE *fpSourceFile = fopen(from.c_str(), "rb");
// Sourcefile does not exist otherwise there is a mistake when renaming!
// Sourcefile existiert nicht sonst gibt es einen Fehler beim Kopierversuch!
if (!fpSourceFile)
{
ESP_LOGE(TAG, "RenameFile: File %s does not exist!", from.c_str());
ESP_LOGE(TAG, "DeleteFile: File %s existiert nicht!", from.c_str());
return false;
}
fclose(fpSourceFile);
rename(from.c_str(), to.c_str());
return true;
}
bool RenameFolder(string from, string to)
{
// ESP_LOGI(logTag, "Renaming Folder: %s", from.c_str());
DIR *fpSourceFolder = opendir(from.c_str());
// Sourcefolder does not exist otherwise there is a mistake when renaming!
if (!fpSourceFolder)
{
ESP_LOGE(TAG, "RenameFolder: Folder %s does not exist!", from.c_str());
return false;
}
closedir(fpSourceFolder);
rename(from.c_str(), to.c_str());
return true;
@@ -403,7 +387,7 @@ bool FileExists(string filename)
{
FILE *fpSourceFile = fopen(filename.c_str(), "rb");
// Sourcefile does not exist
// Sourcefile existiert nicht sonst gibt es einen Fehler beim Kopierversuch!
if (!fpSourceFile)
{
return false;
@@ -414,36 +398,22 @@ bool FileExists(string filename)
return true;
}
bool FolderExists(string foldername)
bool DeleteFile(string fn)
{
DIR *fpSourceFolder = opendir(foldername.c_str());
// Sourcefolder does not exist
if (!fpSourceFolder)
{
return false;
}
closedir(fpSourceFolder);
return true;
}
bool DeleteFile(string filename)
{
// ESP_LOGI(logTag, "Deleting file: %s", filename.c_str());
// ESP_LOGI(logTag, "Deleting file: %s", fn.c_str());
/* Delete file */
FILE *fpSourceFile = fopen(filename.c_str(), "rb");
FILE *fpSourceFile = fopen(fn.c_str(), "rb");
// Sourcefile does not exist otherwise there is a mistake in copying!
// Sourcefile existiert nicht sonst gibt es einen Fehler beim Kopierversuch!
if (!fpSourceFile)
{
ESP_LOGD(TAG, "DeleteFile: File %s existiert nicht!", filename.c_str());
ESP_LOGD(TAG, "DeleteFile: File %s existiert nicht!", fn.c_str());
return false;
}
fclose(fpSourceFile);
unlink(filename.c_str());
unlink(fn.c_str());
return true;
}

View File

@@ -16,12 +16,10 @@ std::size_t file_size(const std::string& file_name);
void FindReplace(std::string& line, std::string& oldString, std::string& newString);
bool CopyFile(string input, string output);
bool DeleteFile(string filename);
bool DeleteFile(string fn);
bool RenameFile(string from, string to);
bool RenameFolder(string from, string to);
bool MakeDir(std::string _what);
bool FileExists(string filename);
bool FolderExists(string foldername);
string RundeOutput(double _in, int _anzNachkomma);

View File

@@ -85,7 +85,7 @@ bool MQTTPublish(std::string _key, std::string _content, int qos, bool retained_
_content.append("..");
}
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Published topic: " + _key + ", content: " + _content);
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Published topic: " + _key + ", content: " + _content + " (msg_id=" + std::to_string(msg_id) + ")");
return true;
}
else {
@@ -465,7 +465,7 @@ void MQTTconnected(){
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);
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "topic " + it->first + " subscribe successful");
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "topic " + it->first + " subscribe successful, msg_id=" + std::to_string(msg_id));
}
}

View File

@@ -174,14 +174,14 @@ bool MQTThomeassistantDiscovery(int qos) {
int aFreeInternalHeapSizeBefore = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
// Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "uptime", "Uptime", "progress-clock", "s", "duration", "measurement", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "uptime", "Uptime", "clock-time-eight-outline", "s", "", "", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "MAC", "MAC Address", "network-outline", "", "", "", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "fwVersion", "Firmware Version", "application-outline", "", "", "", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "hostname", "Hostname", "network-outline", "", "", "", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "freeMem", "Free Memory", "memory", "B", "", "measurement", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "wifiRSSI", "Wi-Fi RSSI", "wifi", "dBm", "signal_strength", "", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "CPUtemp", "CPU Temperature", "thermometer", "°C", "temperature", "measurement", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "interval", "Interval", "clock-time-eight-outline", "min", "", "measurement", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "interval", "Interval", "clock-time-eight-outline", "min", "" , "measurement", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "IP", "IP", "network-outline", "", "", "", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "status", "Status", "list-status", "", "", "", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "flowstart", "Manual Flow Start", "timer-play-outline", "", "", "", "", qos);
@@ -195,12 +195,9 @@ bool MQTThomeassistantDiscovery(int qos) {
/* If "Allow neg. rate" is true, use "measurement" instead of "total_increasing" for the State Class, see https://github.com/jomjol/AI-on-the-edge-device/issues/3331 */
std::string value_state_class = "total_increasing";
if (meterType == "temperature") {
if ((*NUMBERS)[i]->AllowNegativeRates) {
value_state_class = "measurement";
}
else if ((*NUMBERS)[i]->AllowNegativeRates) {
value_state_class = "total";
}
/* Energy meters need a different Device Class, see https://github.com/jomjol/AI-on-the-edge-device/issues/3333 */
std::string rate_device_class = "volume_flow_rate";
@@ -208,17 +205,17 @@ bool MQTThomeassistantDiscovery(int qos) {
rate_device_class = "power";
}
// Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category | QoS
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "value", "Value", "gauge", valueUnit, meterType, value_state_class, "", qos); // State Class = "total_increasing" if <NUMBERS>.AllowNegativeRates = false, "measurement" in case of a thermometer, else use "total".
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "raw", "Raw Value", "raw", valueUnit, meterType, value_state_class, "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "error", "Error", "alert-circle-outline", "", "", "", "diagnostic", qos);
// Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "value", "Value", "gauge", valueUnit, meterType, value_state_class, "", qos); // State Class = "total_increasing" if <NUMBERS>.AllowNegativeRates = false, else use "measurement"
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "raw", "Raw Value", "raw", valueUnit, meterType, "measurement", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "error", "Error", "alert-circle-outline", "", "", "", "diagnostic", qos);
/* Not announcing "rate" as it is better to use rate_per_time_unit resp. rate_per_digitization_round */
// allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate", "Rate (Unit/Minute)", "swap-vertical", "", "", "", "", qos); // Legacy, always Unit per Minute
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_time_unit", "Rate (" + rateUnit + ")", "swap-vertical", rateUnit, rate_device_class, "measurement", "", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_digitization_round","Change since last Digitization round", "arrow-expand-vertical", valueUnit, "", "measurement", "", qos); // correctly the Unit is Unit/Interval!
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "timestamp", "Timestamp", "clock-time-eight-outline", "", "timestamp", "", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "json", "JSON", "code-json", "", "", "", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "problem", "Problem", "alert-outline", "", "problem", "", "", qos); // Special binary sensor which is based on error topic
// allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate", "Rate (Unit/Minute)", "swap-vertical", "", "", "", ""); // Legacy, always Unit per Minute
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_time_unit", "Rate (" + rateUnit + ")", "swap-vertical", rateUnit, rate_device_class, "measurement", "", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_digitization_round", "Change since last Digitization round", "arrow-expand-vertical", valueUnit, "", "measurement", "", qos); // correctly the Unit is Unit/Interval!
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "timestamp", "Timestamp", "clock-time-eight-outline", "", "timestamp", "", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "json", "JSON", "code-json", "", "", "", "diagnostic", qos);
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "problem", "Problem", "alert-outline", "", "problem", "", "", qos); // Special binary sensor which is based on error topic
}
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Successfully published all Homeassistant Discovery MQTT topics");

View File

@@ -1,6 +1,4 @@
#include "openmetrics.h"
#include "functional"
#include "esp_log.h"
/**
* create a singe metric from the given input
@@ -12,66 +10,10 @@ std::string createMetric(const std::string &metricName, const std::string &help,
metricName + " " + value + "\n";
}
typedef struct sequence_metric {
const char *name;
const char *help;
const char *type;
std::function<std::string(NumberPost *number)> valueFunc;
} sequence_metric_t;
sequence_metric_t sequenceMetrics[4] = {
{ "flow_value", "current value of meter readout", "gauge", [](NumberPost *number)-> std::string {return number->ReturnValue;} },
{ "flow_raw_value", "current raw value of meter readout", "gauge", [](NumberPost *number)-> std::string {return number->ReturnRawValue;} },
{ "flow_pre_value", "previous value of meter readout", "gauge", [](NumberPost *number)-> std::string {return number->ReturnPreValue;} },
{ "flow_error", "Error message text != 'no error'", "gauge", [](NumberPost *number)-> std::string {return number->ErrorMessageText.compare("no error") == 0 ? "0" : "1";} },
};
std::string createSequenceMetrics(std::string prefix, const std::vector<NumberPost *> &numbers)
{
std::string result;
for (int i = 0; i<sizeof(sequenceMetrics)/sizeof(sequence_metric_t);i++)
{
std::string res;
for (const auto &number : numbers)
{
std::string value = sequenceMetrics[i].valueFunc(number);
if (value.find("N") != std::string::npos) {
value = "NaN";
}
ESP_LOGD("METRICS", "metric=%s, name=%s, value = %s ",sequenceMetrics[i].name,number->name.c_str(), value.c_str());
// only valid data is reported (https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#missing-data)
if (value.length() > 0)
{
auto label = number->name;
// except newline, double quote, and backslash (https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#abnf)
// to keep it simple, these characters are just removed from the label
replaceAll(label, "\\", "");
replaceAll(label, "\"", "");
replaceAll(label, "\n", "");
res += prefix + "_" + sequenceMetrics[i].name + "{sequence=\"" + label + "\"} " + value + "\n";
}
}
// prepend metadata if a valid metric was created
if (res.length() > 0)
{
res = "# HELP " + prefix + "_" + sequenceMetrics[i].name + " " + sequenceMetrics[i].help + "\n"
+ "# TYPE " + prefix + "_" + sequenceMetrics[i].name + " " + sequenceMetrics[i].type + "\n"
+ res;
}
result += res;
}
return result;
}
/**
* Generate the MetricFamily from all available sequences
* @returns the string containing the text wire format of the MetricFamily
**/
/*
std::string createSequenceMetrics(std::string prefix, const std::vector<NumberPost *> &numbers)
{
std::string res;
@@ -99,4 +41,3 @@ std::string createSequenceMetrics(std::string prefix, const std::vector<NumberPo
}
return res;
}
*/

View File

@@ -254,35 +254,6 @@ extern "C" void app_main(void)
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "==================== Start ======================");
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "=================================================");
// SD card: basic R/W check
// ********************************************
int iSDCardStatus = SDCardCheckRW();
if (iSDCardStatus < 0) {
if (iSDCardStatus <= -1 && iSDCardStatus >= -2) { // write error
StatusLED(SDCARD_CHECK, 1, true);
}
else if (iSDCardStatus <= -3 && iSDCardStatus >= -5) { // read error
StatusLED(SDCARD_CHECK, 2, true);
}
else if (iSDCardStatus == -6) { // delete error
StatusLED(SDCARD_CHECK, 3, true);
}
setSystemStatusFlag(SYSTEM_STATUS_SDCARD_CHECK_BAD); // reduced web interface going to be loaded
}
// SD card: Create further mandatory directories (if not already existing)
// Correct creation of these folders will be checked with function "SDCardCheckFolderFilePresence"
// ********************************************
MakeDir("/sdcard/firmware"); // mandatory for OTA firmware update
MakeDir("/sdcard/img_tmp"); // mandatory for setting up alignment marks
MakeDir("/sdcard/demo"); // mandatory for demo mode
MakeDir("/sdcard/config/certs"); // mandatory for mqtt certificates
// Check for updates
// ********************************************
CheckOTAUpdate();
CheckUpdate();
// Init external PSRAM
// ********************************************
esp_err_t PSRAMStatus = esp_psram_init();
@@ -381,6 +352,22 @@ extern "C" void app_main(void)
}
}
// SD card: basic R/W check
// ********************************************
int iSDCardStatus = SDCardCheckRW();
if (iSDCardStatus < 0) {
if (iSDCardStatus <= -1 && iSDCardStatus >= -2) { // write error
StatusLED(SDCARD_CHECK, 1, true);
}
else if (iSDCardStatus <= -3 && iSDCardStatus >= -5) { // read error
StatusLED(SDCARD_CHECK, 2, true);
}
else if (iSDCardStatus == -6) { // delete error
StatusLED(SDCARD_CHECK, 3, true);
}
setSystemStatusFlag(SYSTEM_STATUS_SDCARD_CHECK_BAD); // reduced web interface going to be loaded
}
// Migrate parameter in config.ini to new naming (firmware 15.0 and newer)
// ********************************************
migrateConfiguration();
@@ -393,6 +380,19 @@ extern "C" void app_main(void)
// ********************************************
setCpuFrequency();
// SD card: Create further mandatory directories (if not already existing)
// Correct creation of these folders will be checked with function "SDCardCheckFolderFilePresence"
// ********************************************
MakeDir("/sdcard/firmware"); // mandatory for OTA firmware update
MakeDir("/sdcard/img_tmp"); // mandatory for setting up alignment marks
MakeDir("/sdcard/demo"); // mandatory for demo mode
MakeDir("/sdcard/config/certs"); // mandatory for mqtt certificates
// Check for updates
// ********************************************
CheckOTAUpdate();
CheckUpdate();
// Start SoftAP for initial remote setup
// Note: Start AP if no wlan.ini and/or config.ini available, e.g. SD card empty; function does not exit anymore until reboot
// ********************************************

View File

@@ -37,58 +37,23 @@ void test_createSequenceMetrics()
NumberPost *number_1 = new NumberPost;
number_1->name = "main";
number_1->ReturnValue = "123.456";
number_1->ReturnRawValue = "N23.456";
number_1->ReturnPreValue = "986.543";
number_1->ErrorMessageText = "";
NUMBERS.push_back(number_1);
const std::string metricNamePrefix = "ai_on_the_edge_device";
const std::string metricName1 = metricNamePrefix + "_flow_value";
const std::string metricName2 = metricNamePrefix + "_flow_raw_value";
const std::string metricName3 = metricNamePrefix + "_flow_pre_value";
const std::string metricName4 = metricNamePrefix + "_flow_error";
std::string expected1 ;
expected1 = "# HELP " + metricName1 + " current value of meter readout\n# TYPE " + metricName1 + " gauge\n" +
metricName1 + "{sequence=\"" + number_1->name + "\"} " + number_1->ReturnValue + "\n";
expected1 += "# HELP " + metricName2 + " current raw value of meter readout\n# TYPE " + metricName2 + " gauge\n" +
metricName2 + "{sequence=\"" + number_1->name + "\"} " + "NaN" + "\n";
expected1 += "# HELP " + metricName3 + " previous value of meter readout\n# TYPE " + metricName3 + " gauge\n" +
metricName3 + "{sequence=\"" + number_1->name + "\"} " + number_1->ReturnPreValue + "\n";
expected1 += "# HELP " + metricName4 + " Error message text != 'no error'\n# TYPE " + metricName4 + " gauge\n" +
metricName4 + "{sequence=\"" + number_1->name + "\"} " + "1" + "\n";
const std::string metricName = metricNamePrefix + "_flow_value";
std::string expected1 = "# HELP " + metricName + " current value of meter readout\n# TYPE " + metricName + " gauge\n" +
metricName + "{sequence=\"" + number_1->name + "\"} " + number_1->ReturnValue + "\n";
TEST_ASSERT_EQUAL_STRING(expected1.c_str(), createSequenceMetrics(metricNamePrefix, NUMBERS).c_str());
NumberPost *number_2 = new NumberPost;
number_2->name = "secondary";
number_2->ReturnValue = "1.0";
number_2->ReturnRawValue = "01.000";
number_2->ReturnPreValue = "0.987";
number_2->ErrorMessageText = "no error";
NUMBERS.push_back(number_2);
std::string expected2 ;
expected2 = "# HELP " + metricName1 + " current value of meter readout\n# TYPE " + metricName1 + " gauge\n" +
metricName1 + "{sequence=\"" + number_1->name + "\"} " + number_1->ReturnValue + "\n" +
metricName1 + "{sequence=\"" + number_2->name + "\"} " + number_2->ReturnValue + "\n";
expected2 += "# HELP " + metricName2 + " current raw value of meter readout\n# TYPE " + metricName2 + " gauge\n" +
metricName2 + "{sequence=\"" + number_1->name + "\"} " + "NaN" + "\n" +
metricName2 + "{sequence=\"" + number_2->name + "\"} " + number_2->ReturnRawValue + "\n";
expected2 += "# HELP " + metricName3 + " previous value of meter readout\n# TYPE " + metricName3 + " gauge\n" +
metricName3 + "{sequence=\"" + number_1->name + "\"} " + number_1->ReturnPreValue + "\n" +
metricName3 + "{sequence=\"" + number_2->name + "\"} " + number_2->ReturnPreValue + "\n";
expected2 += "# HELP " + metricName4 + " Error message text != 'no error'\n# TYPE " + metricName4 + " gauge\n" +
metricName4 + "{sequence=\"" + number_1->name + "\"} " + "1" + "\n" +
metricName4 + "{sequence=\"" + number_2->name + "\"} " + "0" + "\n";
std::string expected2 = "# HELP " + metricName + " current value of meter readout\n# TYPE " + metricName + " gauge\n" +
metricName + "{sequence=\"" + number_1->name + "\"} " + number_1->ReturnValue + "\n" +
metricName + "{sequence=\"" + number_2->name + "\"} " + number_2->ReturnValue + "\n";
TEST_ASSERT_EQUAL_STRING(expected2.c_str(), createSequenceMetrics(metricNamePrefix, NUMBERS).c_str());
}

View File

@@ -67,19 +67,21 @@
<div class="footer">
<div class="footer-section">
<span>Support & Contact</span>
<span>Support & Contact Us</span>
<a href="https://github.com/jomjol/AI-on-the-edge-device" target="_blank" title="GitHub">
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/refs/heads/main/images/github-logo.png" alt="GitHub">
<img src="https://github.com/jomjol/AI-on-the-edge-device/images/github-logo.png" alt="GitHub">
</a>
<img src="https://github.com/jomjol/AI-on-the-edge-device/images/gmail-logo.png" alt="Email">
</a>
<a href="https://github.com/jomjol/AI-on-the-edge-device/discussions" target="_blank" title="GitHub">
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/refs/heads/main/images/discussion-logo.png" alt="GitHub">
<img src="https://github.com/jomjol/AI-on-the-edge-device/images/discussion-logo" alt="GitHub">
</a>
</div>
<div class="footer-section">
<span>Donations</span>
<a href="https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL" target="_blank" title="Donate via PayPal">
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/refs/heads/main/images/paypal.png" alt="PayPal" style="width: 60px; height: auto;">
<img src="https://github.com/jomjol/AI-on-the-edge-device/images/paypal.png" alt="PayPal" style="width: 60px; height: auto;">
</a>
</div>
</div>

View File

@@ -31,6 +31,7 @@ AlignmentAlgo
CNNGoodThreshold
PreValueAgeStartup
ErrorMessage
CheckDigitIncreaseConsistency
IO0
IO1
IO3

View File

@@ -4,4 +4,4 @@ Default Value: `undefined`
Dedicated definition of the field for InfluxDB use for saving in the Influx database (e.g.: "watermeter/value").
!!! Note
If you edit the config file manually, you must prefix this parameter with `<NUMBER>` followed by a dot (eg. `main.Field`). The reason is that this parameter is specific for each `<NUMBER>` (`<NUMBER>` is the name of the number sequence defined in the ROI's).
This parameter must be prefixed with `<NUMBER>` followed by a dot (eg. `main.Field`). `<NUMBER>` is the name of the number sequence defined in the ROI's.

View File

@@ -4,4 +4,4 @@ Default Value: `undefined`
Field for InfluxDB v2 to use for saving.
!!! Note
If you edit the config file manually, you must prefix this parameter with `<NUMBER>` followed by a dot (eg. `main.Field`). The reason is that this parameter is specific for each `<NUMBER>` (`<NUMBER>` is the name of the number sequence defined in the ROI's).
This parameter must be prefixed with `<NUMBER>` followed by a dot (eg. `main.Field`). `<NUMBER>` is the name of the number sequence defined in the ROI's.

View File

@@ -3,26 +3,9 @@ Default Value: `other`
Select the Meter Type so the sensors have the right units in Homeassistant.
!!! Note
For `Watermeter` you need to have Homeassistant 2022.11 or newer!
Please also make sure that the selected Meter Type matches the dimension of the value provided by the meter!
Eg. if your meter provides `m³`, you need to also set it to `m³`.
Alternatively you can set the parameter `DecimalShift` to `3` so the value is converted to `liters`!
List of supported options:
- `other`
- `water_m3` (uses `m^3/h` as rate)
- `water_l` (uses `l/h` as rate, not officially supported by Homeassistant!)
- `water_gal` (uses `gal/h` as rate, not officially supported by Homeassistant!)
- `water_ft3` (uses `ft^3/min` as rate)
- `gas_m3` (uses `m^3/h` as rate)
- `gas_ft3` (uses `ft^3/min` as rate)
- `energy_wh` (uses `W` as rate)
- `energy_kwh` (uses `KW` as rate)
- `energy_mwh` (uses `MW` as rate)
- `energy_gj` (uses `GJ/h` as rate, not officially supported by Homeassistant!)
- `temperature_c` (uses `+C/min` as rate)
- `temperature_f` (uses `°F/min` as rate)
- `temperature_k` (uses `K/min` as rate)
!!! Note
Not all options are supported by Homeassistant, see `SensorDeviceClass.VOLUME_FLOW_RATE` in [https://developers.home-assistant.io/docs/core/entity/sensor/#available-device-classes](https://developers.home-assistant.io/docs/core/entity/sensor/#available-device-classes)!

View File

@@ -4,4 +4,4 @@ Default Value: `0`
The Idx number for the counter device. Can be obtained from the devices setup page on the Domoticz system.
!!! Note
If you edit the config file manually, you must prefix this parameter with `<NUMBER>` followed by a dot (eg. `main.DomoticzIDX`). The reason is that this parameter is specific for each `<NUMBER>` (`<NUMBER>` is the name of the number sequence defined in the ROI's).
This parameter must be prefixed with `<NUMBER>` followed by a dot (eg. `main.DomoticzIDX`). `<NUMBER>` is the name of the number sequence defined in the ROI's.

View File

@@ -0,0 +1,8 @@
# Parameter `CheckDigitIncreaseConsistency`
Default Value: `false`
!!! Warning
This is an **Expert Parameter**! Only change it if you understand what it does!
An additional consistency check.
It especially improves the zero crossing check between digits.

View File

@@ -7,4 +7,4 @@ Allow a meter to count backwards (decreasing values).
This is unusual (it means there is a negative rate) and not wanted in most cases!
!!! Note
If you edit the config file manually, you must prefix this parameter with `<NUMBER>` followed by a dot (eg. `main.AllowNegativeRates`). The reason is that this parameter is specific for each `<NUMBER>` (`<NUMBER>` is the name of the number sequence defined in the ROI's).
This parameter must be prefixed with `<NUMBER>` followed by a dot (eg. `main.AllowNegativeRates`). `<NUMBER>` is the name of the number sequence defined in the ROI's.

View File

@@ -9,4 +9,4 @@ See [here](../Watermeter-specific-analog---digit-transition) for details.
Range: `6.0` .. `9.9`.
!!! Note
If you edit the config file manually, you must prefix this parameter with `<NUMBER>` followed by a dot (eg. `main.AnalogToDigitTransitionStart`). The reason is that this parameter is specific for each `<NUMBER>` (`<NUMBER>` is the name of the number sequence defined in the ROI's).
This parameter must be prefixed with `<NUMBER>` followed by a dot (eg. `main.AnalogToDigitTransitionStart`). `<NUMBER>` is the name of the number sequence defined in the ROI's.

View File

@@ -1,7 +1,7 @@
# Parameter `ChangeRateThreshold`
Default Value: `2`
Range: `0` .. `9`.
Range: `1` .. `9`.
Threshold parameter for change rate detection.<br>
This parameter is intended to compensate for small reading fluctuations that occur when the meter does not change its value for a long time (e.g. at night) or slightly turns backwards. This can eg. happen on watermeters.
@@ -10,18 +10,18 @@ It is only applied to the last digit of the read value (See example below).
If the read value is within PreValue +/- Threshold, no further calculation is carried out and the Value/Prevalue remains at the old value.
!!! Note
If you edit the config file manually, you must prefix this parameter with `<NUMBER>` followed by a dot (eg. `main.ChangeRateThreshold`). The reason is that this parameter is specific for each `<NUMBER>` (`<NUMBER>` is the name of the number sequence defined in the ROI's).
This parameter must be prefixed with `<NUMBER>` followed by a dot (eg. `main.ChangeRateThreshold`). `<NUMBER>` is the name of the number sequence defined in the ROI's.
## Example
- Smallest ROI provides value for `0.000'x` (Eg. a water meter with 4 pointers behind the decimal point)
- ChangeRateThreshold = 2
#### With `ExtendedResolution` **disabled**
#### With `Extended Resolution` **disabled**
PreValue: `123.456'7` -> Threshold = `+/-0.000'2`.<br>
All changes between `123.456'5` and `123.456'9` get ignored
#### With `ExtendedResolution` **enabled**
#### With `Extended Resolution` **enabled**
PreValue: `123.456'78` -> Threshold = `+/-0.000'02`.<br>
All changes between `123.456'76` and `123.456'80` get ignored.

View File

@@ -1,11 +0,0 @@
# Parameter `CheckDigitIncreaseConsistency`
Default Value: `false`
!!! Warning
This is an **Expert Parameter**! Only change it if you understand what it does!
An additional consistency check.
It especially improves the zero crossing check between digits.
!!! Note
If you edit the config file manually, you must prefix this parameter with `<NUMBER>` followed by a dot (eg. `main.CheckDigitIncreaseConsistency`). The reason is that this parameter is specific for each `<NUMBER>` (`<NUMBER>` is the name of the number sequence defined in the ROI's).

View File

@@ -5,4 +5,4 @@ Shift the decimal separator (positiv or negativ).
Eg. to move from `m³` to `liter` (`1 m³` equals `1000 liters`), you need to set it to `+3`.
!!! Note
If you edit the config file manually, you must prefix this parameter with `<NUMBER>` followed by a dot (eg. `main.DecimalShift`). The reason is that this parameter is specific for each `<NUMBER>` (`<NUMBER>` is the name of the number sequence defined in the ROI's).
This parameter must be prefixed with `<NUMBER>` followed by a dot (eg. `main.DecimalShift`). `<NUMBER>` is the name of the number sequence defined in the ROI's.

View File

@@ -7,4 +7,4 @@ Use the decimal place of the last analog counter for increased accuracy.
This parameter is only supported on the `*-class*` and `*-const` models! See [Choosing-the-Model](../Choosing-the-Model) for details.
!!! Note
If you edit the config file manually, you must prefix this parameter with `<NUMBER>` followed by a dot (eg. `main.ExtendedResolution`). The reason is that this parameter is specific for each `<NUMBER>` (`<NUMBER>` is the name of the number sequence defined in the ROI's).
This parameter must be prefixed with `<NUMBER>` followed by a dot (eg. `main.ExtendedResolution`). `<NUMBER>` is the name of the number sequence defined in the ROI's.

View File

@@ -6,4 +6,4 @@ This is only relevant for models which use `N`!
See [here](../Choosing-the-Model) for details.
!!! Note
If you edit the config file manually, you must prefix this parameter with `<NUMBER>` followed by a dot (eg. `main.IgnoreLeadingNaN`). The reason is that this parameter is specific for each `<NUMBER>` (`<NUMBER>` is the name of the number sequence defined in the ROI's).
This parameter must be prefixed with `<NUMBER>` followed by a dot (eg. `main.IgnoreLeadingNaN`). `<NUMBER>` is the name of the number sequence defined in the ROI's.

View File

@@ -5,4 +5,4 @@ Defines if the **Change Rate** is calculated as the difference between the last
as the difference normalized to the interval (`RateChange` = difference per minute).
!!! Note
If you edit the config file manually, you must prefix this parameter with `<NUMBER>` followed by a dot (eg. `main.MaxRateType`). The reason is that this parameter is specific for each `<NUMBER>` (`<NUMBER>` is the name of the number sequence defined in the ROI's).
This parameter must be prefixed with `<NUMBER>` followed by a dot (eg. `main.MaxRateType`). `<NUMBER>` is the name of the number sequence defined in the ROI's.

View File

@@ -6,4 +6,4 @@ Maximum allowed change between two readings, if exceeded the last reading will b
If negative rate is disallowed and no maximum rate value is set, one false high reading will lead to a period of missing measurements until the measurement reaches the previous false high reading. E.g. if the counter is at `600,00` and it's read incorrectly as` 610,00`, all measurements will be skipped until the counter reaches `610,00`. Setting the MaxRateValue to `0,05` leads to a rejection of all readings with a difference `> 0,05`, in this case `610,00`. The rejection also applies to correct readings with a difference `> 0,05`!
!!! Note
If you edit the config file manually, you must prefix this parameter with `<NUMBER>` followed by a dot (eg. `main.MaxRateValue`). The reason is that this parameter is specific for each `<NUMBER>` (`<NUMBER>` is the name of the number sequence defined in the ROI's).
This parameter must be prefixed with `<NUMBER>` followed by a dot (eg. `main.MaxRateValue`). `<NUMBER>` is the name of the number sequence defined in the ROI's.

View File

@@ -1,5 +0,0 @@
# SD Card content
This folder contains the files which are required to setup the SD card.
❗ Do not directly copy this folder onto your SD card, **it will not work!** Instead, you can use any of the artifacts generaded in any of the Pipeline runs of the [Build-Pipeline](https://github.com/jomjol/AI-on-the-edge-device/actions/workflows/build.yaml).
The files in the `html` folder here only serve as templates. The real `html` folder get generated using the Github actions.

View File

@@ -43,7 +43,7 @@ AlignmentAlgo = default
/config/ref1.jpg 442 142
[Digits]
Model = /config/dig-cont_0900_s3_q.tflite
Model = /config/dig-cont_0712_s3_q.tflite
CNNGoodThreshold = 0.5
;ROIImagesLocation = /log/digit
;ROIImagesRetention = 3
@@ -52,7 +52,7 @@ main.dig2 343 126 30 54 false
main.dig3 391 126 30 54 false
[Analog]
Model = /config/ana-cont_1500_s2_q.tflite
Model = /config/ana-cont_1300_s2.tflite
CNNGoodThreshold = 0.5
;ROIImagesLocation = /log/analog
;ROIImagesRetention = 3
@@ -73,7 +73,7 @@ main.MaxRateValue = 0.05
main.ExtendedResolution = false
main.IgnoreLeadingNaN = false
ErrorMessage = true
main.CheckDigitIncreaseConsistency = false
CheckDigitIncreaseConsistency = false
;[MQTT]
;Uri = mqtt://IP-ADRESS:1883

View File

@@ -43,14 +43,14 @@ AlignmentAlgo = default
/config/ref1.jpg 536 113
[Digits]
Model = /config/dig-cont_0810_s3_q.tflite
Model = /config/dig-cont_0710_s3_q.tflite
CNNGoodThreshold = 0.5
;ROIImagesLocation = /log/digit
;ROIImagesRetention = 3
main.dig1 438 62 49 71 false
[Analog]
Model = /config/ana-cont_1400_s2_q.tflite
Model = /config/ana-cont_1300_s2.tflite
;ROIImagesLocation = /log/analog
;ROIImagesRetention = 3
main.ana1 452 199 120 120 false
@@ -67,7 +67,7 @@ main.AllowNegativeRates = true
main.ExtendedResolution = true
main.IgnoreLeadingNaN = false
ErrorMessage = true
main.CheckDigitIncreaseConsistency = false
CheckDigitIncreaseConsistency = false
;[MQTT]
;Uri = mqtt://IP-ADRESS:1883
@@ -76,14 +76,11 @@ main.CheckDigitIncreaseConsistency = false
;user = USERNAME
;password = PASSWORD
RetainMessages = false
;DomoticzTopicIn = undefined
;main.DomoticzIDX = undefined
HomeassistantDiscovery = false
;MeterType = other
;CACert = /config/certs/RootCA.pem
;ClientCert = /config/certs/client.pem.crt
;ClientKey = /config/certs/client.pem.key
;ValidateServerCert = true
;[InfluxDB]
;Uri = undefined
@@ -136,3 +133,4 @@ TimeZone = CET-1CEST,M3.5.0,M10.5.0/3
RSSIThreshold = -75
CPUFrequency = 160
SetupMode = false

View File

@@ -1,7 +1,6 @@
/* The UI can also be run locally, but you have to set the IP of your device accordingly.
/* The UI can also be run locally, but you have to set the IP of your devide accordingly.
* And you also might have to disable CORS in your webbrowser!
* Eg using https://chromewebstore.google.com/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf?utm_source=ext_app_menu on chrome
* Keep empty to disable using it. Enabling it will break access through a forwared port, see
* https://github.com/jomjol/AI-on-the-edge-device/issues/2681 */
var domainname_for_testing = "";

View File

@@ -79,18 +79,6 @@
transform: translate(-50%,-50%);
-ms-transform: translate(-50%,-50%);
}
#reboot_button {
float: none;
background-color: #f44336;
color: white;
padding: 5px;
border-radius:
5px; font-weight: bold;
text-align: center;
text-decoration: none;
display: inline-block;
}
</style>
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
@@ -200,11 +188,13 @@
param;
function doReboot() {
var stringota = domainname + "/reboot";
window.location = stringota;
window.location.href = stringota;
window.location.assign(stringota);
window.location.replace(stringota);
if (confirm("Are you sure you want to reboot? Did you save your changes?")) {
var stringota = domainname + "/reboot";
window.location = stringota;
window.location.href = stringota;
window.location.assign(stringota);
window.location.replace(stringota);
}
}
function ChangeSelection(){
@@ -225,45 +215,50 @@
document.getElementById("overlay").style.display = "block";
document.getElementById("overlaytext").innerHTML = "Save Alignment Marker...";
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function task() {
while (true) {
WriteConfigININew();
if (neueref1 == 1 && neueref2 == 1) {
UpdateConfigReferences(domainname);
}
else if (neueref1 == 1) {
var anzneueref = 1;
UpdateConfigReference(anzneueref, domainname);
}
else if (neueref2 == 1) {
var anzneueref = 2;
UpdateConfigReference(anzneueref, domainname);
}
SaveConfigToServer(domainname);
document.getElementById("updatemarker").disabled = false;
// document.getElementById("savemarker").disabled = true;
// document.getElementById("enhancecontrast").disabled = true;
EnDisableItem(false, "savemarker", true);
EnDisableItem(false, "enhancecontrast", true);
document.getElementById("overlay").style.display = "none";
firework.launch('Alignment marker saved. They will get applied after the next reboot!<br><br>\n<a id="reboot_button" onclick="doReboot()">reboot now</a>', 'success', 5000);
return;
if (confirm("Are you sure you want to save the new alignment marker configuration?")) {
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
setTimeout(function () {
// Delay so the overlay gets shown
task();
}, 1);
async function task() {
while (true) {
WriteConfigININew();
if (neueref1 == 1 && neueref2 == 1) {
UpdateConfigReferences(domainname);
}
else if (neueref1 == 1) {
var anzneueref = 1;
UpdateConfigReference(anzneueref, domainname);
}
else if (neueref2 == 1) {
var anzneueref = 2;
UpdateConfigReference(anzneueref, domainname);
}
SaveConfigToServer(domainname);
document.getElementById("updatemarker").disabled = false;
// document.getElementById("savemarker").disabled = true;
// document.getElementById("enhancecontrast").disabled = true;
EnDisableItem(false, "savemarker", true);
EnDisableItem(false, "enhancecontrast", true);
document.getElementById("overlay").style.display = "none";
firework.launch('Alignment marker saved. They will get applied after next reboot', 'success', 5000);
return;
}
}
setTimeout(function () {
// Delay so the overlay gets shown
task();
}, 1);
}
else {
document.getElementById("overlay").style.display = "none";
}
}
function EnhanceContrast() {

View File

@@ -5,20 +5,6 @@
<meta charset="UTF-8" />
<title>Analog ROI</title>
<style>
#reboot_button {
float: none;
background-color: #f44336;
color: white;
padding: 5px;
border-radius:
5px; font-weight: bold;
text-align: center;
text-decoration: none;
display: inline-block;
}
</style>
<link href="edit_style.css" rel="stylesheet">
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
@@ -199,11 +185,13 @@ The following settings are only used for easier setup, they are <b>not</b> persi
domainname = getDomainname();
function doReboot() {
var stringota = getDomainname() + "/reboot";
window.location = stringota;
window.location.href = stringota;
window.location.assign(stringota);
window.location.replace(stringota);
if (confirm("Are you sure you want to reboot? Did you save your changes?")) {
var stringota = getDomainname() + "/reboot";
window.location = stringota;
window.location.href = stringota;
window.location.assign(stringota);
window.location.replace(stringota);
}
}
function EnDisableAnalog() {
@@ -343,14 +331,16 @@ The following settings are only used for easier setup, they are <b>not</b> persi
}
function SaveToConfig() {
//_zwcat = getConfigCategory();
cofcat["Analog"]["enabled"] = document.getElementById("Category_Analog_enabled").checked;
WriteConfigININew();
SaveConfigToServer(domainname);
UpdateROIs();
document.getElementById("saveroi").disabled = true;
if (confirm("Are you sure you want to save the new analog ROI configuration?")) {
//_zwcat = getConfigCategory();
cofcat["Analog"]["enabled"] = document.getElementById("Category_Analog_enabled").checked;
WriteConfigININew();
SaveConfigToServer(domainname);
UpdateROIs();
document.getElementById("saveroi").disabled = true;
firework.launch('Configuration saved. It will get applied after the next reboot!<br><br>\n<a id="reboot_button" onclick="doReboot()">reboot now</a>', 'success', 5000);
firework.launch('Configuration saved. It will get applied after next reboot', 'success', 5000);
}
}
function ShowMultiplier() {

View File

@@ -20,18 +20,6 @@
textarea {
font-size: 15px;
}
#reboot_button {
float: none;
background-color: #f44336;
color: white;
padding: 5px;
border-radius:
5px; font-weight: bold;
text-align: center;
text-decoration: none;
display: inline-block;
}
</style>
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
@@ -50,8 +38,15 @@
</td>
</table>
<hr>
<button class="button" onclick="saveTextAsFile()">Save Config</button>
<table>
<td>
<button class="button" onclick="saveTextAsFile()">Save Config</button>
</td>
<td>
<button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate changes</button>
</td>
</table>
<script type="text/javascript" src="readconfigparam.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="readconfigcommon.js?v=$COMMIT_HASH"></script>
@@ -69,19 +64,23 @@
function saveTextAsFile()
{
FileDeleteOnServer("/config/config.ini", domainname);
var textToSave = document.getElementById("inputTextToSave").value;
FileSendContent(textToSave, "/config/config.ini", domainname);
if (confirm("Are you sure you want to save the configuration?")) {
FileDeleteOnServer("/config/config.ini", domainname);
var textToSave = document.getElementById("inputTextToSave").value;
FileSendContent(textToSave, "/config/config.ini", domainname);
firework.launch('Configuration saved. It will get applied after the next reboot!<br><br>\n<a id="reboot_button" onclick="doReboot()">reboot now</a>', 'success', 5000);
firework.launch('Configuration saved. It will get applied after next reboot', 'success', 5000);
}
}
function doReboot() {
var stringota = "/reboot";
window.location = stringota;
window.location.href = stringota;
window.location.assign(stringota);
window.location.replace(stringota);
if (confirm("Are you sure you want to reboot?")) {
var stringota = "/reboot";
window.location = stringota;
window.location.href = stringota;
window.location.assign(stringota);
window.location.replace(stringota);
}
}
LoadConfigNeu();

File diff suppressed because it is too large Load Diff

View File

@@ -4,19 +4,6 @@
<head>
<meta charset="UTF-8" />
<title>Digit ROI</title>
<style>
#reboot_button {
float: none;
background-color: #f44336;
color: white;
padding: 5px;
border-radius:
5px; font-weight: bold;
text-align: center;
text-decoration: none;
display: inline-block;
}
</style>
<link href="edit_style.css" rel="stylesheet">
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
@@ -217,11 +204,13 @@
domainname = getDomainname();
function doReboot() {
var stringota = getDomainname() + "/reboot";
window.location = stringota;
window.location.href = stringota;
window.location.assign(stringota);
window.location.replace(stringota);
if (confirm("Are you sure you want to reboot? Did you save your changes?")) {
var stringota = getDomainname() + "/reboot";
window.location = stringota;
window.location.href = stringota;
window.location.assign(stringota);
window.location.replace(stringota);
}
}
function EnDisableDigits() {
@@ -370,14 +359,16 @@
}
function SaveToConfig() {
// _zwcat = getConfigCategory();
cofcat["Digits"]["enabled"] = document.getElementById("Category_Digits_enabled").checked;
WriteConfigININew();
SaveConfigToServer(domainname);
UpdateROIs();
document.getElementById("saveroi").disabled = true;
if (confirm("Are you sure you want to save the new digit ROI configuration?")) {
// _zwcat = getConfigCategory();
cofcat["Digits"]["enabled"] = document.getElementById("Category_Digits_enabled").checked;
WriteConfigININew();
SaveConfigToServer(domainname);
UpdateROIs();
document.getElementById("saveroi").disabled = true;
firework.launch('Configuration saved. It will get applied after the next reboot!<br><br>\n<a id="reboot_button" onclick="doReboot()">reboot now</a>', 'success', 5000);
firework.launch('Configuration saved. It will get applied after next reboot', 'success', 5000);
}
}
function ShowMultiplier() {

View File

@@ -395,12 +395,22 @@
<script type="text/javascript">
var canvas = document.getElementById('canvas'),
domainname = getDomainname(),
context = canvas.getContext('2d'),
imageObj = new Image(),
isActReference = false,
param,
category;
domainname = getDomainname(),
context = canvas.getContext('2d'),
imageObj = new Image(),
isActReference = false,
param,
category;
function doReboot() {
if (confirm("Are you sure you want to reboot? Did you save the config?")) {
var stringota = domainname + "/reboot";
window.location = stringota;
window.location.href = stringota;
window.location.assign(stringota);
window.location.replace(stringota);
}
}
function cameraParameterChanged() {
document.getElementById("savereferenceimage").disabled = true;
@@ -728,7 +738,7 @@
if (xhttp.responseText == "CamSettingsSet") {
document.getElementById("overlay").style.display = "none";
firework.launch('Cam Settings saved', 'success', 5000);
firework.launch('Cam Settings saved', 'success', 2000);
return;
}
else {

File diff suppressed because one or more lines are too long

View File

@@ -25,7 +25,6 @@
</style>
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
<script src="md5.min.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="jquery-3.6.0.min.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
<script type="text/javascript" src="firework.js?v=$COMMIT_HASH"></script>
@@ -143,7 +142,7 @@
function doRebootAfterUpdate() {
var xhttp = new XMLHttpRequest();
xhttp.open("GET", domainname + "/reboot", true);
xhttp.open("GET", "/reboot", true);
xhttp.send();
}
@@ -170,35 +169,15 @@
}
};
var url = domainname + "/ota?task=emptyfirmwaredir";
xhttp.open("GET", url, true);
var _toDo = domainname + "/ota?task=emptyfirmwaredir";
xhttp.open("GET", _toDo, true);
xhttp.send();
}
function validateMd5(md5_on_device, callback) {
const reader = new FileReader();
reader.onload = (event) => {
const fileContent = event.target.result;
md5_on_webbrowser = md5(fileContent);
console.log("MD5 on device: " + md5_on_device + ", MD5 on web browser: " + md5_on_webbrowser);
if (md5_on_device == md5_on_webbrowser) {
console.log("MD5 values are equal");
callback(true);
}
else {
console.log("MD5 values are NOT equal!");
callback(false);
}
}
var fileInput = document.getElementById("file_selector").files;
reader.readAsArrayBuffer(fileInput[0]);
}
function extract() {
document.getElementById("status").innerText = "Status: Processing on device...";
var xhttp = new XMLHttpRequest();
/* first delete the old firmware */
xhttp.onreadystatechange = function() {
@@ -207,9 +186,9 @@
document.cookie = "page=overview.html?v=$COMMIT_HASH" + "; path=/"; // Make sure after the reboot we go to the overview page
if (xhttp.responseText.startsWith("reboot")) { // Reboot required
console.log("The device will now reboot and install the update!");
console.log("Upload completed, the device will now restart and install the update!");
document.getElementById("status").innerText = "Status: Installing...";
firework.launch('Upload completed and validated. The device will now reboot and install the update', 'success', 5000);
firework.launch('Upload completed, the device will now restart and install the update', 'success', 5000);
/* Tell it to reboot */
doRebootAfterUpdate();
@@ -249,8 +228,8 @@
var file_name = document.getElementById("file_selector").value;
filePath = file_name.split(/[\\\/]/).pop();
var url = domainname + "/ota?task=update&file=" + filePath;
xhttp.open("GET", url, true);
var _toDo = domainname + "/ota?task=update&file=" + filePath;
xhttp.open("GET", _toDo, true);
xhttp.send();
}
@@ -263,7 +242,7 @@
function upload() {
document.getElementById("status").innerText = "Status: Uploading...";
var url = domainname + "/upload/firmware/" + filePath + "?md5";
var upload_path = "/upload/firmware/" + filePath;
var file = _("file_selector").files[0];
var formdata = new FormData();
@@ -274,7 +253,7 @@
ajax.addEventListener("error", errorHandler, false);
ajax.addEventListener("abort", abortHandler, false);
ajax.open("POST", url);
ajax.open("POST", upload_path);
ajax.send(file);
}
@@ -284,43 +263,16 @@
" MB of " + (event.total / 1024/ 1024).toFixed(2) + " MB";
var percent = (event.loaded / event.total) * 100;
_("progressBar").value = Math.round(percent);
if (Math.round(percent) == 100) {
_("progressBar").value = 0; //will clear progress bar after successful upload
_("loaded_n_total").innerHTML = "";
_("status").innerHTML = "Status: Upload completed. Validating file...";
}
else {
_("status").innerHTML = "Status: " + Math.round(percent) + "% uploaded...";
}
_("status").innerHTML = "Status: " + Math.round(percent) + "% uploaded. Please wait...";
}
function completeHandler(event) {
console.log("Upload completed");
console.log("Response: " + event.target.responseText);
try {
md5_on_device = JSON.parse(event.target.responseText).md5;
validateMd5(md5_on_device, (result) => {
if (result == true) {
_("status").innerHTML = "Status: The uploaded file is valid, installing it...";
extract();
}
else {
_("status").innerHTML = "Status: The file got corrupted! Please upload it again!";
firework.launch('Upload failed, the file got corrupted! Please upload it again!', 'danger', 30000);
document.getElementById("start_OTA_button").disabled = false;
}
});
}
catch (e) {
// If the firmware is to old, it will return the file sever page instead of the JSON object with the MD5 sum.
// In juch case just proceed to keep legacy support.
console.log("It seems to be a legacy firmware, installing the update without validation!");
_("status").innerHTML = "Status: It seems to be a legacy firmware, installing the update without validation...";
extract();
}
_("status").innerHTML = "Status: " + event.target.responseText;
_("progressBar").value = 0; //will clear progress bar after successful upload
_("loaded_n_total").innerHTML = "";
extract();
}

View File

@@ -6,6 +6,7 @@ var ref = new Array(2);
var NUMBERS = new Array(0);
var REFERENCES = new Array(0);
function getNUMBERSList() {
_domainname = getDomainname();
var namenumberslist = "";
@@ -32,6 +33,7 @@ function getNUMBERSList() {
return namenumberslist;
}
function getDATAList() {
_domainname = getDomainname();
datalist = "";
@@ -60,6 +62,7 @@ function getDATAList() {
return datalist;
}
function getTFLITEList() {
_domainname = getDomainname();
tflitelist = "";
@@ -87,6 +90,7 @@ function getTFLITEList() {
return tflitelist;
}
function ParseConfig() {
config_split = config_gesamt.split("\n");
var aktline = 0;
@@ -168,7 +172,7 @@ function ParseConfig() {
category[catname]["enabled"] = false;
category[catname]["found"] = false;
param[catname] = new Object();
ParamAddValue(param, catname, "DecimalShift", 1, true, "0");
ParamAddValue(param, catname, "DecimalShift", 1, true);
ParamAddValue(param, catname, "AnalogToDigitTransitionStart", 1, true, "9.2");
ParamAddValue(param, catname, "ChangeRateThreshold", 1, true, "2");
// ParamAddValue(param, catname, "PreValueUse", 1, true, "true");
@@ -181,7 +185,7 @@ function ParseConfig() {
ParamAddValue(param, catname, "IgnoreLeadingNaN", 1, true, "false");
// ParamAddValue(param, catname, "IgnoreAllNaN", 1, true, "false");
ParamAddValue(param, catname, "ErrorMessage");
ParamAddValue(param, catname, "CheckDigitIncreaseConsistency", 1, true, "false");
ParamAddValue(param, catname, "CheckDigitIncreaseConsistency");
var catname = "MQTT";
category[catname] = new Object();
@@ -355,6 +359,7 @@ function ParseConfig() {
}
}
function ParamAddValue(param, _cat, _param, _anzParam = 1, _isNUMBER = false, _defaultValue = "", _checkRegExList = null) {
param[_cat][_param] = new Object();
param[_cat][_param]["found"] = false;
@@ -366,6 +371,7 @@ function ParamAddValue(param, _cat, _param, _anzParam = 1, _isNUMBER = false, _d
param[_cat][_param].checkRegExList = _checkRegExList;
};
function ParseConfigParamAll(_aktline, _catname) {
++_aktline;
@@ -397,6 +403,7 @@ function ParseConfigParamAll(_aktline, _catname) {
return _aktline;
}
function ParamExtractValue(_param, _linesplit, _catname, _paramname, _aktline, _iscom, _anzvalue = 1) {
if ((_linesplit[0].toUpperCase() == _paramname.toUpperCase()) && (_linesplit.length > _anzvalue)) {
_param[_catname][_paramname]["found"] = true;
@@ -410,6 +417,7 @@ function ParamExtractValue(_param, _linesplit, _catname, _paramname, _aktline, _
}
}
function ParamExtractValueAll(_param, _linesplit, _catname, _aktline, _iscom) {
for (var paramname in _param[_catname]) {
_AktROI = "default";
@@ -467,6 +475,7 @@ function ParamExtractValueAll(_param, _linesplit, _catname, _aktline, _iscom) {
}
}
function getCamConfig() {
ParseConfig();
@@ -644,10 +653,12 @@ function getCamConfig() {
return param;
}
function getConfigParameters() {
return param;
}
function WriteConfigININew() {
// Cleanup empty NUMBERS
for (var j = 0; j < NUMBERS.length; ++j) {
@@ -749,6 +760,7 @@ function WriteConfigININew() {
}
}
function isCommented(input) {
let isComment = false;
@@ -760,6 +772,7 @@ function isCommented(input) {
return [isComment, input];
}
function SaveConfigToServer(_domainname){
// leere Zeilen am Ende löschen
var zw = config_split.length - 1;
@@ -779,14 +792,17 @@ function SaveConfigToServer(_domainname){
FileSendContent(config_gesamt, "/config/config.ini", _domainname);
}
function getConfig() {
return config_gesamt;
}
function getConfigCategory() {
return category;
}
function ExtractROIs(_aktline, _type){
var linesplit = ZerlegeZeile(_aktline);
abc = getNUMBERS(linesplit[0], _type);
@@ -803,6 +819,7 @@ function ExtractROIs(_aktline, _type){
}
}
function getNUMBERS(_name, _type, _create = true) {
_pospunkt = _name.indexOf (".");
@@ -862,6 +879,7 @@ function getNUMBERS(_name, _type, _create = true) {
return neuroi;
}
function CopyReferenceToImgTmp(_domainname) {
for (index = 0; index < 2; ++index) {
_filenamevon = REFERENCES[index]["name"];
@@ -876,10 +894,12 @@ function CopyReferenceToImgTmp(_domainname) {
}
}
function GetReferencesInfo(){
return REFERENCES;
}
function UpdateConfigReferences(_domainname){
for (var index = 0; index < 2; ++index) {
_filenamenach = REFERENCES[index]["name"];
@@ -894,6 +914,7 @@ function UpdateConfigReferences(_domainname){
}
}
function UpdateConfigReference(_anzneueref, _domainname){
var index = 0;
@@ -918,10 +939,12 @@ function UpdateConfigReference(_anzneueref, _domainname){
FileCopyOnServer(_filenamevon, _filenamenach, _domainname);
}
function getNUMBERInfo(){
return NUMBERS;
}
function RenameNUMBER(_alt, _neu){
if ((_neu.indexOf(".") >= 0) || (_neu.indexOf(",") >= 0) || (_neu.indexOf(" ") >= 0) || (_neu.indexOf("\"") >= 0)) {
return "Number sequence name must not contain , . \" or a space";
@@ -949,6 +972,7 @@ function RenameNUMBER(_alt, _neu){
return "";
}
function DeleteNUMBER(_delete){
if (NUMBERS.length == 1) {
return "One number sequence is mandatory. Therefore this cannot be deleted"
@@ -969,6 +993,7 @@ function DeleteNUMBER(_delete){
return "";
}
function CreateNUMBER(_numbernew){
found = false;
@@ -1016,6 +1041,7 @@ function CreateNUMBER(_numbernew){
return "";
}
function getROIInfo(_typeROI, _number){
index = -1;
@@ -1033,6 +1059,7 @@ function getROIInfo(_typeROI, _number){
}
}
function RenameROI(_number, _type, _alt, _neu){
if ((_neu.includes("=")) || (_neu.includes(".")) || (_neu.includes(":")) || (_neu.includes(",")) || (_neu.includes(";")) || (_neu.includes(" ")) || (_neu.includes("\""))) {
return "ROI name must not contain . : , ; = \" or space";
@@ -1071,6 +1098,7 @@ function RenameROI(_number, _type, _alt, _neu){
return "";
}
function DeleteNUMBER(_delte) {
if (NUMBERS.length == 1) {
return "The last number cannot be deleted"
@@ -1091,6 +1119,7 @@ function DeleteNUMBER(_delte) {
return "";
}
function CreateROI(_number, _type, _pos, _roinew, _x, _y, _dx, _dy, _CCW){
_indexnumber = -1;

View File

@@ -34,11 +34,13 @@ p {font-size: 1em;}
<script>
function doReboot() {
var stringota = getDomainname() + "/reboot";
window.location = stringota;
window.location.href = stringota;
window.location.assign(stringota);
window.location.replace(stringota);
// if (confirm("Are you sure you want to reboot the ESP32?")) {
var stringota = getDomainname() + "/reboot";
window.location = stringota;
window.location.href = stringota;
window.location.assign(stringota);
window.location.replace(stringota);
// }
}
</script>

View File

@@ -29,7 +29,7 @@
// var xhttp = new XMLHttpRequest();
// xhttp.onreadystatechange = function() {if (xhttp.readyState == 4) {if (xhttp.status == 200) {document.reload();}}};
if (!file.name.includes("remote-setup")){
if (!confirm("The zip file name should contain \"...remote-setup...\". Are you sure you have downloaded the correct file?"))
if (!confirm("The zip file name should contain \"...remote-setup...\". Are you sure that you have downloaded the correct file?"))
return;
}
@@ -41,7 +41,7 @@ if (!file.name.includes("remote-setup")){
var file = document.getElementById("newfile").files[0];
if (!file.name.includes("remote-setup"))
{
if (!confirm("The zip file name should contain \"...remote-setup...\". Are you sure you have downloaded the correct file?"))
if (!confirm("The zip file name should contain \"...remote-setup...\". Are you sure that you have downloaded the correct file?"))
return;
}

View File

@@ -1,3 +0,0 @@
# Webinstaller
This folder is used to provide the required files to generate the [Web-Installer](https://jomjol.github.io/AI-on-the-edge-device/).
The Webinstaller gets automatically updated on a release using the Github actions.

View File

@@ -1,2 +0,0 @@
# Binary folder of the webinstaller
The firmware itself (`firmware.bin`) gets copied to this folder through the Github action.