mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2025-12-09 13:06:54 +03:00
Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03c84a1ff3 | ||
|
|
b5e0d6ee66 | ||
|
|
7d33c3ee5f | ||
|
|
53cee961f4 | ||
|
|
c81f901a19 | ||
|
|
6642f6f995 | ||
|
|
431df73e52 | ||
|
|
1f5d4de5f3 | ||
|
|
ce00192684 | ||
|
|
28f3ad0242 | ||
|
|
2dfd55e1c3 | ||
|
|
f84f20b2e8 | ||
|
|
806adcb4d0 | ||
|
|
4dc4752823 | ||
|
|
827423023c | ||
|
|
6ca7897fa0 | ||
|
|
39b8b5b07c | ||
|
|
5b98acaa32 | ||
|
|
d3d241c7b9 | ||
|
|
8c7e86fea4 | ||
|
|
98d85b2c6c | ||
|
|
f42e9c71f2 | ||
|
|
00be21f9e1 | ||
|
|
c694d9f363 | ||
|
|
a7dc37761b | ||
|
|
2dd2d03f6c | ||
|
|
4c407499d2 | ||
|
|
b97d808b54 | ||
|
|
9f2e91a9df | ||
|
|
18e96d62a6 | ||
|
|
a1a77ae5d9 | ||
|
|
c5b20f3680 | ||
|
|
add6cf5c33 | ||
|
|
493bd4df2f | ||
|
|
53ff190860 | ||
|
|
9a9aa68a65 | ||
|
|
a92cb69067 | ||
|
|
90fa44380c | ||
|
|
7a9f61a8d8 | ||
|
|
a8f8189543 | ||
|
|
2c4bda9e66 | ||
|
|
7b2a80a13d | ||
|
|
95d312b920 | ||
|
|
886cd4ffa5 | ||
|
|
10e0435383 | ||
|
|
b0de37b762 | ||
|
|
22e4b39f77 | ||
|
|
2c1a7f4c9e | ||
|
|
3960823439 | ||
|
|
d5ef08546a | ||
|
|
3d711f495e | ||
|
|
b21e3c6c9d | ||
|
|
23d2ae627d | ||
|
|
3f62abf878 | ||
|
|
025f4af9f2 | ||
|
|
3d92860c5e | ||
|
|
2ed9fb8eb5 | ||
|
|
598db004ae | ||
|
|
70332fe142 | ||
|
|
10da8c4f94 | ||
|
|
5bac1c68d9 | ||
|
|
008dba7e11 | ||
|
|
7283bfd506 | ||
|
|
56788652ae | ||
|
|
44e186e65b | ||
|
|
55be652dc1 | ||
|
|
1acd72d33e | ||
|
|
795bcd0d21 | ||
|
|
0b2e38935b | ||
|
|
a9c5bebb45 | ||
|
|
876adc51af | ||
|
|
bf090f3762 | ||
|
|
30a50720e5 | ||
|
|
6c891ab7fe | ||
|
|
3fa16c5624 | ||
|
|
b9134f923e | ||
|
|
06f4d417b5 | ||
|
|
55efc3b3f4 | ||
|
|
800e231301 | ||
|
|
34a3d6d6e3 | ||
|
|
4bfe5422c5 | ||
|
|
d63dc08f33 | ||
|
|
2ee85001eb | ||
|
|
1d2f920819 | ||
|
|
69583db99e |
52
.github/workflows/build.yaml
vendored
52
.github/workflows/build.yaml
vendored
@@ -70,15 +70,24 @@ jobs:
|
||||
#run: echo "Testing... ${{ github.ref_name }}, ${{ steps.vars.outputs.sha_short }}" > ./sd-card/html/version.txt; mkdir -p ./code/.pio/build/esp32cam/; cd ./code/.pio/build/esp32cam/; echo "${{ steps.vars.outputs.sha_short }}" > firmware.bin; cp firmware.bin partitions.bin; cp firmware.bin bootloader.bin # Testing
|
||||
run: cd code; platformio run --environment esp32cam
|
||||
|
||||
- name: Prepare Web UI (copy data from repo and update hashes in all files)
|
||||
- name: Prepare Web UI (copy data from repo, generate tooltip pages and update hashes in all files)
|
||||
run: |
|
||||
rm -rf ./html
|
||||
mkdir html
|
||||
cp ./sd-card/html/* ./html/
|
||||
cp -r ./sd-card/html/* ./html/
|
||||
|
||||
python -m pip install markdown
|
||||
mkdir html/param-tooltips
|
||||
cd tools/parameter-tooltip-generator
|
||||
bash generate-param-doc-tooltips.sh
|
||||
cd ../..
|
||||
|
||||
cp -r ./sd-card/html/* ./html/
|
||||
|
||||
echo "Replacing variables..."
|
||||
cd html; find . -type f -exec sed -i 's/$COMMIT_HASH/${{ steps.vars.outputs.sha_short }}/g' {} \;
|
||||
|
||||
|
||||
|
||||
#########################################################################################
|
||||
## Pack for Update
|
||||
#########################################################################################
|
||||
@@ -86,7 +95,7 @@ jobs:
|
||||
# New OTA concept
|
||||
# update__version.zip file with following content:
|
||||
# - /firmware.bin
|
||||
# - (optional) /html/*
|
||||
# - (optional) /html/* (inkl. subfolders)
|
||||
# - (optional) /config/*.tfl
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
@@ -149,7 +158,7 @@ jobs:
|
||||
# New Remote Setup concept
|
||||
# remote_setup__version.zip file with following content:
|
||||
# - /firmware.bin
|
||||
# - /html/*
|
||||
# - /html/* (inkl. subfolders)
|
||||
# - /config/*
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
@@ -324,24 +333,23 @@ jobs:
|
||||
# extract the version used in next step
|
||||
- id: get_version
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: battila7/get-version-action@v2
|
||||
uses: Simply007/get-version-action@v2
|
||||
|
||||
# the changelog [unreleased] will now be changed to the release version
|
||||
- name: Update changelog
|
||||
uses: thomaseizinger/keep-a-changelog-new-release@v1
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
changelogPath: Changelog.md
|
||||
version: ${{ steps.get_version.outputs.version-without-v }}
|
||||
# # the changelog [unreleased] will now be changed to the release version
|
||||
# - name: Update changelog
|
||||
# uses: thomaseizinger/keep-a-changelog-new-release@v1
|
||||
# if: startsWith(github.ref, 'refs/tags/')
|
||||
# with:
|
||||
# changelogPath: Changelog.md
|
||||
# version: ${{ steps.get_version.outputs.version-without-v }}
|
||||
|
||||
# the release notes will be extracted from changelog
|
||||
- name: Extract release notes
|
||||
id: extract-release-notes
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: ffurrer2/extract-release-notes@v1
|
||||
with:
|
||||
changelog_file: Changelog.md
|
||||
|
||||
# # the release notes will be extracted from changelog
|
||||
# - name: Extract release notes
|
||||
# id: extract-release-notes
|
||||
# if: startsWith(github.ref, 'refs/tags/')
|
||||
# uses: ffurrer2/extract-release-notes@v1
|
||||
# with:
|
||||
# changelog_file: Changelog.md
|
||||
|
||||
# Releases should only be created on master by tagging the last commit.
|
||||
# all artifacts in firmware folder pushed to the release
|
||||
@@ -392,7 +400,7 @@ jobs:
|
||||
|
||||
- name: Get version of last release
|
||||
id: last_release
|
||||
uses: InsonusK/get-latest-release@v1.0.1
|
||||
uses: mindojo/get-latest-release@0b8ef1434d7468d6bffcc8263baff5c777f72321
|
||||
with:
|
||||
myToken: ${{ github.token }}
|
||||
exclude_types: "draft|prerelease"
|
||||
|
||||
@@ -32,10 +32,10 @@ jobs:
|
||||
|
||||
- name: Get version of last release
|
||||
id: last_release
|
||||
uses: InsonusK/get-latest-release@v1.0.1
|
||||
uses: mindojo/get-latest-release@0b8ef1434d7468d6bffcc8263baff5c777f72321
|
||||
with:
|
||||
myToken: ${{ github.token }}
|
||||
exclude_types: "release"
|
||||
exclude_types: "draft|prerelease"
|
||||
view_top: 1
|
||||
|
||||
- name: Add binary to Web Installer and update manifest
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -23,3 +23,5 @@ CTestTestfile.cmake
|
||||
_deps
|
||||
code/edgeAI.code-workspace
|
||||
.DS_Store
|
||||
tools/parameter-tooltip-generator/html
|
||||
tools/parameter-tooltip-generator/AI-on-the-edge-device-docs
|
||||
|
||||
144
Changelog.md
144
Changelog.md
@@ -1,6 +1,137 @@
|
||||
## [14.0.2] - 2023-02-05
|
||||
## [15.1.1] - 2023-03-23
|
||||
|
||||
**Stabilization and Improved User Experience**
|
||||
### Update Procedure
|
||||
|
||||
Update Procedure see [online documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#update-ota-over-the-air)
|
||||
|
||||
### Changes
|
||||
|
||||
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.0...v15.1.1)
|
||||
|
||||
#### Added
|
||||
|
||||
- [#2206](https://github.com/jomjol/AI-on-the-edge-device/pull/2206) Log PSRAM usage
|
||||
- [#2216](https://github.com/jomjol/AI-on-the-edge-device/pull/2216) Log MQTT connection refused reasons
|
||||
|
||||
#### Changed
|
||||
|
||||
- n.a.
|
||||
|
||||
#### Fixed
|
||||
|
||||
- [#2224](https://github.com/jomjol/AI-on-the-edge-device/pull/2224), [#2213](https://github.com/jomjol/AI-on-the-edge-device/pull/2213) Reverted some of the PSRAM usage changes due to negative sideffects
|
||||
- [#2203](https://github.com/jomjol/AI-on-the-edge-device/issues/2203) Correct API for pure InfluxDB v1
|
||||
- [#2180](https://github.com/jomjol/AI-on-the-edge-device/pull/2180) Fixed links in Parameter Documentation
|
||||
- Various minor fixes
|
||||
|
||||
#### Removed
|
||||
|
||||
- n.a.
|
||||
|
||||
## [15.1.0] - 2023-03-12
|
||||
|
||||
### Update Procedure
|
||||
|
||||
Update Procedure see [online documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#update-ota-over-the-air)
|
||||
|
||||
:bangbang: Afterwards you should force-reload the Web Interface (usually Ctrl-F5 will do it)!
|
||||
|
||||
:bangbang: Afterwards you should check your configuration for errors!
|
||||
|
||||
### Changes
|
||||
|
||||
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.0.3...v15.1.0)
|
||||
|
||||
#### Added
|
||||
- The Configuration page has now tooltips with enhanced documentation
|
||||
- MQTT:
|
||||
- Added `GJ` (`gigajoule`) as an energy meter unit
|
||||
- Removed State Class and unit from `raw` topic
|
||||
- Various Improvements (Only send Homeassistant Discovery the first time we connect, ...) (https://github.com/jomjol/AI-on-the-edge-device/pull/2091
|
||||
- Added Expert Parameter to change CPU Clock from `160` to `240 Mhz`
|
||||
- SD card basic read/write check and a folder/file presence check at boot to indicate SD card issues or missing folders / files ([#2085](https://github.com/jomjol/AI-on-the-edge-device/pull/2085))
|
||||
- Simplified "WIFI roaming" by client triggered channel scan (AP switching at low RSSI) -> using expert parameter "RSSIThreshold" ([#2120](https://github.com/jomjol/AI-on-the-edge-device/pull/2120))
|
||||
- Log WLAN disconnect reason codes (see [WLAN disconnect reasons](https://jomjol.github.io/AI-on-the-edge-device-docs/WLAN-disconnect-reason))
|
||||
- Support of InfluxDB v2 ([#2004](https://github.com/jomjol/AI-on-the-edge-device/pull/2004))
|
||||
|
||||
|
||||
#### Changed
|
||||
- Updated models (tflite files), removed old versions (https://github.com/jomjol/AI-on-the-edge-device/pull/2089, https://github.com/jomjol/AI-on-the-edge-device/pull/2133)
|
||||
:bangbang: **Attention:** Update your configuration!
|
||||
- Hybrid CNN network to `dig-cont_0611_s3`
|
||||
- Analog CNN network to `ana-cont-11.0.5` and `ana-clas100-1.5.7`
|
||||
- Digital CNN network to `dig-class100-1.6.0`
|
||||
- Various Web interface Improvements/Enhancements:
|
||||
- Restructured Menu (Needs cache clearing to be applied)
|
||||
- Enhanced `Previous Value` page
|
||||
- Improved/faster Graph page
|
||||
- Various minor improvements
|
||||
- ROI config pages improvements
|
||||
- Improved Backup Functionality
|
||||
- Added log file logs for Firmware Update
|
||||
- Improved memory management (moved various stuff to external PSRAM, https://github.com/jomjol/AI-on-the-edge-device/pull/2117)
|
||||
- Camera driver update: Support of contrast and saturation ([#2048](https://github.com/jomjol/AI-on-the-edge-device/pull/2048))
|
||||
:bangbang: **Attention**: This could have impact to old configurations. Please check your configuration and potentially adapt parametrization, if detection is negativly affected.
|
||||
- Improved error handling and provide more verbose output in error cases during boot phase ([#2020](https://github.com/jomjol/AI-on-the-edge-device/pull/2020))
|
||||
- Red board LED is indicating more different errors and states (see [Status LED Blink Codes](https://jomjol.github.io/AI-on-the-edge-device-docs/StatusLED-BlinkCodes))
|
||||
- Logfile: Print start indication block after time is synced to indicate start in logfile after a cold boot
|
||||
- `Image Quality Index`: Limit lower input range to 8 to avoid system instabilities
|
||||
|
||||
#### Fixed
|
||||
- Various minor fixes
|
||||
- Added State Class "measurement" to rate_per_time_unit
|
||||
- GPIO: Avoid MQTT publishing to empty topic when "MQTT enable" flag is not set
|
||||
- Fix timezone config parser
|
||||
- Remote Setup truncated long passwords (https://github.com/jomjol/AI-on-the-edge-device/issues/2167)
|
||||
- Problem with timestamp in InfluxDB interface
|
||||
|
||||
#### Removed
|
||||
- n.a.
|
||||
|
||||
|
||||
## [15.0.3] - 2023-02-28
|
||||
|
||||
**Name: Parameter Migration**
|
||||
|
||||
### Update Procedure
|
||||
|
||||
Update Procedure see [online documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#update-ota-over-the-air)
|
||||
|
||||
:bangbang: Afterwards you should force-reload the Web Interface (usually Ctrl-F5 will do it).
|
||||
|
||||
### Changes
|
||||
|
||||
This release only migrates some parameters, see #2023 for details and a list of all parameter changes.
|
||||
The parameter migration happens automatically on the next startup. No user interaction is required.
|
||||
A backup of the config is stored on the SD-card as `config.bak`.
|
||||
|
||||
Beside of the parameter change and the bugfix listed below, no changes are contained in this release!
|
||||
|
||||
If you want to revert back to `v14` or earlier, you will have to revert the migration changes in `config.ini` manually!
|
||||
|
||||
#### Added
|
||||
|
||||
- n.a.
|
||||
|
||||
#### Changed
|
||||
|
||||
- [#2023](https://github.com/jomjol/AI-on-the-edge-device/pull/2023) Migrated Parameters
|
||||
- Removed old `Topic` parameter, it is not used anymore
|
||||
|
||||
#### Fixed
|
||||
|
||||
- [#2036](https://github.com/jomjol/AI-on-the-edge-device/issues/2036) Fix wrong url-encoding
|
||||
- **NEW v15.0.2:** [#1933](https://github.com/jomjol/AI-on-the-edge-device/issues/1933) Bugfix InfluxDB Timestamp
|
||||
- **NEW v15.0.3:** Re-added lost dropdownbox filling for Postprocessing Individual Parameters
|
||||
|
||||
#### Removed
|
||||
|
||||
- n.a.
|
||||
|
||||
|
||||
## [14.0.3] -2023-02-05
|
||||
|
||||
**Name: Stabilization and Improved User Experience**
|
||||
|
||||
Thanks to over 80 Pull Requests from 6 contributors, we can anounce another great release with many many improvements and new features:
|
||||
|
||||
@@ -54,7 +185,7 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/
|
||||
|
||||
## [13.0.8] - 2022-12-19
|
||||
|
||||
**Home Assistant MQTT Discovery Support**
|
||||
**Name: Home Assistant MQTT Discovery Support**
|
||||
|
||||
### Update Procedure see [online documentation](https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/#update-ota-over-the-air)
|
||||
|
||||
@@ -134,7 +265,7 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/
|
||||
|
||||
## [12.0.1] 2022-09-29
|
||||
|
||||
Improve **u**ser e**x**perience
|
||||
Name: Improve **u**ser e**x**perience
|
||||
|
||||
:bangbang: The release breaks a few things in ota update :bangbang:
|
||||
|
||||
@@ -802,7 +933,10 @@ External Illumination
|
||||
- Initial Version
|
||||
|
||||
|
||||
[14.0.0]: https://github.com/jomjol/AI-on-the-edge-device/compare/v13.0.8...v14.0.2
|
||||
[15.1.1]: https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.0...v15.1.1
|
||||
[15.1.0]: https://github.com/jomjol/AI-on-the-edge-device/compare/v15.0.3...v15.1.0
|
||||
[15.0.3]: https://github.com/jomjol/AI-on-the-edge-device/compare/v14.0.3...v15.0.3
|
||||
[14.0.3]: https://github.com/jomjol/AI-on-the-edge-device/compare/v13.0.8...v14.0.3
|
||||
[13.0.8]: https://github.com/jomjol/AI-on-the-edge-device/compare/v12.0.1...v13.0.8
|
||||
[13.0.7]: https://github.com/jomjol/AI-on-the-edge-device/compare/v12.0.1...v13.0.7
|
||||
[13.0.5]: https://github.com/jomjol/AI-on-the-edge-device/compare/v12.0.1...v13.0.5
|
||||
|
||||
@@ -10,6 +10,14 @@
|
||||
|
||||
|
||||
____
|
||||
#### #36 Run demo without camera
|
||||
Demo mode requires a working camera (if not, one receives a 'Cam bad' error). Would be nice to demo or play around on other ESP32 boards (or on ESP32-CAM boards when you broke the camera cable...).
|
||||
|
||||
#### #35 Use the same model, but provide the image from a Smartphone Camera
|
||||
as reading the Electricity or Water meter every few minutues only delivers apparent accuracy (DE: "Scheingenauigkeit") you could just as well take a picture with your Smartphone evey so often (e.g. once a week when you are in the Basement anyway), then with some "semi clever" tricks pass this image to the model developed here, and the values then on to who ever needs them e.g. via MQTT.
|
||||
IMO: It is not needed to have that many readings (datapoints) as our behaviour (Use of electricity or water) doesn't vary that much, say, over a weeks time. The interpolation between weekly readings will give sufficient information on the power and/or water usage.
|
||||
|
||||
|
||||
#### #34 implement state and Roi for water leak detection
|
||||
for example see Roi on the next picture..
|
||||

|
||||
|
||||
@@ -82,10 +82,10 @@ static void gpioHandlerTask(void *arg) {
|
||||
|
||||
void GpioPin::gpioInterrupt(int value) {
|
||||
#ifdef ENABLE_MQTT
|
||||
if (_mqttTopic != "") {
|
||||
if (_mqttTopic.compare("") != 0) {
|
||||
ESP_LOGD(TAG, "gpioInterrupt %s %d", _mqttTopic.c_str(), value);
|
||||
|
||||
MQTTPublish(_mqttTopic, value ? "true" : "false");
|
||||
MQTTPublish(_mqttTopic, value ? "true" : "false", 1);
|
||||
}
|
||||
#endif //ENABLE_MQTT
|
||||
currentState = value;
|
||||
@@ -115,7 +115,7 @@ void GpioPin::init()
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
if ((_mqttTopic != "") && ((_mode == GPIO_PIN_MODE_OUTPUT) || (_mode == GPIO_PIN_MODE_OUTPUT_PWM) || (_mode == GPIO_PIN_MODE_BUILT_IN_FLASH_LED))) {
|
||||
if ((_mqttTopic.compare("") != 0) && ((_mode == GPIO_PIN_MODE_OUTPUT) || (_mode == GPIO_PIN_MODE_OUTPUT_PWM) || (_mode == GPIO_PIN_MODE_BUILT_IN_FLASH_LED))) {
|
||||
std::function<bool(std::string, char*, int)> f = std::bind(&GpioPin::handleMQTT, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
MQTTregisterSubscribeFunction(_mqttTopic, f);
|
||||
}
|
||||
@@ -141,8 +141,8 @@ void GpioPin::setValue(bool value, gpio_set_source setSource, std::string* error
|
||||
gpio_set_level(_gpio, value);
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
if ((_mqttTopic != "") && (setSource != GPIO_SET_SOURCE_MQTT)) {
|
||||
MQTTPublish(_mqttTopic, value ? "true" : "false");
|
||||
if ((_mqttTopic.compare("") != 0) && (setSource != GPIO_SET_SOURCE_MQTT)) {
|
||||
MQTTPublish(_mqttTopic, value ? "true" : "false", 1);
|
||||
}
|
||||
#endif //ENABLE_MQTT
|
||||
}
|
||||
@@ -153,7 +153,8 @@ void GpioPin::publishState() {
|
||||
if (newState != currentState) {
|
||||
ESP_LOGD(TAG,"publish state of GPIO %d new state %d", _gpio, newState);
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTPublish(_mqttTopic, newState ? "true" : "false");
|
||||
if (_mqttTopic.compare("") != 0)
|
||||
MQTTPublish(_mqttTopic, newState ? "true" : "false", 1);
|
||||
#endif //ENABLE_MQTT
|
||||
currentState = newState;
|
||||
}
|
||||
@@ -359,9 +360,9 @@ bool GpioHandler::readConfig()
|
||||
gpio_int_type_t intType = resolveIntType(toLower(splitted[2]));
|
||||
uint16_t dutyResolution = (uint8_t)atoi(splitted[3].c_str());
|
||||
#ifdef ENABLE_MQTT
|
||||
bool mqttEnabled = toLower(splitted[4]) == "true";
|
||||
bool mqttEnabled = (toLower(splitted[4]) == "true");
|
||||
#endif // ENABLE_MQTT
|
||||
bool httpEnabled = toLower(splitted[5]) == "true";
|
||||
bool httpEnabled = (toLower(splitted[5]) == "true");
|
||||
char gpioName[100];
|
||||
if (splitted.size() >= 7) {
|
||||
strcpy(gpioName, trim(splitted[6]).c_str());
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "Helper.h"
|
||||
#include "statusled.h"
|
||||
#include "CImageBasis.h"
|
||||
|
||||
#include "server_ota.h"
|
||||
@@ -63,7 +64,6 @@ static camera_config_t camera_config = {
|
||||
.fb_count = 1, //if more than one, i2s runs in continuous mode. Use only with JPEG
|
||||
.fb_location = CAMERA_FB_IN_PSRAM, /*!< The location where the frame buffer will be allocated */
|
||||
.grab_mode = CAMERA_GRAB_LATEST, // only from new esp32cam version
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -126,63 +126,97 @@ void CCamera::ledc_init(void)
|
||||
}
|
||||
|
||||
|
||||
static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len){
|
||||
static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len)
|
||||
{
|
||||
jpg_chunking_t *j = (jpg_chunking_t *)arg;
|
||||
if(!index){
|
||||
|
||||
if(!index) {
|
||||
j->len = 0;
|
||||
}
|
||||
if(httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK){
|
||||
|
||||
if(httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
j->len += len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
bool CCamera::SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation)
|
||||
{
|
||||
bool result = false;
|
||||
sensor_t * s = esp_camera_sensor_get();
|
||||
if (_brightness > -100)
|
||||
_brightness = min(2, max(-2, _brightness));
|
||||
if (_contrast > -100)
|
||||
_contrast = min(2, max(-2, _contrast));
|
||||
if (_saturation > -100)
|
||||
_saturation = min(2, max(-2, _saturation));
|
||||
_brightness = min(2, max(-2, _brightness));
|
||||
_contrast = min(2, max(-2, _contrast));
|
||||
_saturation = min(2, max(-2, _saturation));
|
||||
|
||||
if (_saturation > -100)
|
||||
sensor_t * s = esp_camera_sensor_get();
|
||||
if (s) {
|
||||
s->set_saturation(s, _saturation);
|
||||
if (_contrast > -100)
|
||||
s->set_contrast(s, _contrast);
|
||||
if (_brightness > -100)
|
||||
s->set_brightness(s, _brightness);
|
||||
|
||||
if ((_brightness != brightness) && (_brightness > -100))
|
||||
result = true;
|
||||
if ((_contrast != contrast) && (_contrast > -100))
|
||||
result = true;
|
||||
if ((_saturation != saturation) && (_saturation > -100))
|
||||
result = true;
|
||||
|
||||
if (_brightness > -100)
|
||||
brightness = _brightness;
|
||||
if (_contrast > -100)
|
||||
contrast = _contrast;
|
||||
if (_saturation > -100)
|
||||
saturation = _saturation;
|
||||
/* Workaround - bug in cam library - enable bits are set without using bitwise OR logic -> only latest enable setting is used */
|
||||
/* Library version: https://github.com/espressif/esp32-camera/commit/5c8349f4cf169c8a61283e0da9b8cff10994d3f3 */
|
||||
/* Reference: https://esp32.com/viewtopic.php?f=19&t=14376#p93178 */
|
||||
/* The memory structure is as follows for
|
||||
byte_0 = enable_bits
|
||||
byte_0->bit0 = enable saturation and hue --> OK
|
||||
byte_0->bit1 = enable saturation --> OK
|
||||
byte_0->bit2 = enable brightness and contrast --> OK
|
||||
byte_0->bit3 = enable green -> blue spitial effect (Antique and blunish and greenish and readdish and b&w) enable
|
||||
byte_0->bit4 = anable gray -> read spitial effect (Antique and blunish and greenish and readdish and b&w) enable
|
||||
byte_0->bit5 = remove (UV) in YUV color system
|
||||
byte_0->bit6 = enable negative
|
||||
byte_0->bit7 = remove (Y) in YUV color system
|
||||
byte_1 = saturation1 0-255 --> ?
|
||||
byte_2 = hue 0-255 --> OK
|
||||
byte_3 = saturation2 0-255 --> OK
|
||||
byte_4 = reenter saturation2 in documents --> ?
|
||||
byte_5 = spital effect green -> blue 0-255 --> ?
|
||||
byte_6 = spital effect gray -> read 0-255 --> ?
|
||||
byte_7 = contrast lower byte 0-255 --> OK
|
||||
byte_8 = contrast higher byte 0-255 --> OK
|
||||
byte_9 = brightness 0-255 --> OK
|
||||
byte_10= if byte_10==4 contrast effective --> ?
|
||||
*/
|
||||
|
||||
if (result && isFixedExposure)
|
||||
//s->set_reg(s, 0x7C, 0xFF, 2); // Optional feature - hue setting: Select byte 2 in register 0x7C to set hue value
|
||||
//s->set_reg(s, 0x7D, 0xFF, 0); // Optional feature - hue setting: Hue value 0 - 255
|
||||
s->set_reg(s, 0xFF, 0x01, 0); // Select DSP bank
|
||||
s->set_reg(s, 0x7C, 0xFF, 0); // Select byte 0 in register 0x7C
|
||||
s->set_reg(s, 0x7D, 7, 7); // Set bit 0, 1, 2 in register 0x7D to enable saturation, contrast, brightness and hue control
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SetBrightnessContrastSaturation: Failed to get control structure");
|
||||
}
|
||||
|
||||
if (((_brightness != brightness) || (_contrast != contrast) || (_saturation != saturation)) && isFixedExposure)
|
||||
EnableAutoExposure(waitbeforepicture_org);
|
||||
|
||||
return result;
|
||||
brightness = _brightness;
|
||||
contrast = _contrast;
|
||||
saturation = _saturation;
|
||||
|
||||
ESP_LOGD(TAG, "brightness %d, contrast: %d, saturation %d", brightness, contrast, saturation);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CCamera::SetQualitySize(int qual, framesize_t resol)
|
||||
{
|
||||
sensor_t * s = esp_camera_sensor_get();
|
||||
s->set_quality(s, qual);
|
||||
s->set_framesize(s, resol);
|
||||
qual = min(63, max(8, qual)); // Limit quality from 8..63 (values lower than 8 tent to be unstable)
|
||||
|
||||
sensor_t * s = esp_camera_sensor_get();
|
||||
if (s) {
|
||||
s->set_quality(s, qual);
|
||||
s->set_framesize(s, resol);
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SetQualitySize: Failed to get control structure");
|
||||
}
|
||||
|
||||
ActualResolution = resol;
|
||||
ActualQuality = qual;
|
||||
|
||||
@@ -191,41 +225,45 @@ void CCamera::SetQualitySize(int qual, framesize_t resol)
|
||||
image_height = 240;
|
||||
image_width = 320;
|
||||
}
|
||||
if (resol == FRAMESIZE_VGA)
|
||||
else if (resol == FRAMESIZE_VGA)
|
||||
{
|
||||
image_height = 480;
|
||||
image_width = 640;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void CCamera::EnableAutoExposure(int flash_duration)
|
||||
{
|
||||
ESP_LOGD(TAG, "EnableAutoExposure");
|
||||
|
||||
LEDOnOff(true);
|
||||
if (flash_duration > 0)
|
||||
if (flash_duration > 0) {
|
||||
LightOnOff(true);
|
||||
const TickType_t xDelay = flash_duration / portTICK_PERIOD_MS;
|
||||
vTaskDelay( xDelay );
|
||||
const TickType_t xDelay = flash_duration / portTICK_PERIOD_MS;
|
||||
vTaskDelay( xDelay );
|
||||
}
|
||||
|
||||
camera_fb_t * fb = esp_camera_fb_get();
|
||||
esp_camera_fb_return(fb);
|
||||
fb = esp_camera_fb_get();
|
||||
if (!fb) {
|
||||
ESP_LOGE(TAG, "Camera Capture Failed");
|
||||
LEDOnOff(false);
|
||||
LightOnOff(false);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Capture Failed (Procedure 'EnableAutoExposure') --> Reboot! "
|
||||
"Check that your camera module is working and connected properly.");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "EnableAutoExposure: Capture Failed. "
|
||||
"Check camera module and/or proper electrical connection");
|
||||
//doReboot();
|
||||
}
|
||||
esp_camera_fb_return(fb);
|
||||
|
||||
sensor_t * s = esp_camera_sensor_get();
|
||||
s->set_gain_ctrl(s, 0);
|
||||
s->set_exposure_ctrl(s, 0);
|
||||
|
||||
if (s) {
|
||||
s->set_gain_ctrl(s, 0);
|
||||
s->set_exposure_ctrl(s, 0);
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "EnableAutoExposure: Failed to get control structure to set gain+exposure");
|
||||
}
|
||||
|
||||
LEDOnOff(false);
|
||||
LightOnOff(false);
|
||||
@@ -237,22 +275,21 @@ void CCamera::EnableAutoExposure(int flash_duration)
|
||||
esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CCamera::CaptureToBasisImage - Start");
|
||||
LogFile.WriteHeapInfo("CaptureToBasisImage - Start");
|
||||
#endif
|
||||
|
||||
_Image->EmptyImage(); //Delete previous stored raw image -> black image
|
||||
|
||||
LEDOnOff(true);
|
||||
|
||||
if (delay > 0)
|
||||
{
|
||||
if (delay > 0) {
|
||||
LightOnOff(true);
|
||||
const TickType_t xDelay = delay / portTICK_PERIOD_MS;
|
||||
vTaskDelay( xDelay );
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CCamera::CaptureToBasisImage - After LightOn");
|
||||
LogFile.WriteHeapInfo("CaptureToBasisImage - After LightOn");
|
||||
#endif
|
||||
|
||||
camera_fb_t * fb = esp_camera_fb_get();
|
||||
@@ -262,9 +299,8 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
|
||||
LEDOnOff(false);
|
||||
LightOnOff(false);
|
||||
|
||||
ESP_LOGE(TAG, "CaptureToBasisImage: Capture Failed");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "is not working anymore (CCamera::CaptureToBasisImage) - most probably caused by a hardware problem (instablility, ...). "
|
||||
"System will reboot.");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "is not working anymore (CaptureToBasisImage) - most probably caused "
|
||||
"by a hardware problem (instablility, ...). System will reboot.");
|
||||
doReboot();
|
||||
|
||||
return ESP_FAIL;
|
||||
@@ -275,12 +311,17 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
|
||||
loadNextDemoImage(fb);
|
||||
}
|
||||
|
||||
CImageBasis* _zwImage = new CImageBasis();
|
||||
_zwImage->LoadFromMemory(fb->buf, fb->len);
|
||||
CImageBasis* _zwImage = new CImageBasis("zwImage");
|
||||
if (_zwImage) {
|
||||
_zwImage->LoadFromMemory(fb->buf, fb->len);
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToBasisImage: Can't allocate _zwImage");
|
||||
}
|
||||
esp_camera_fb_return(fb);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CCamera::CaptureToBasisImage - After fb_get");
|
||||
LogFile.WriteHeapInfo("CaptureToBasisImage - After fb_get");
|
||||
#endif
|
||||
|
||||
LEDOnOff(false);
|
||||
@@ -292,7 +333,7 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
|
||||
// vTaskDelay( xDelay ); // wait for power to recover
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CCamera::CaptureToBasisImage - After LoadFromMemory");
|
||||
LogFile.WriteHeapInfo("CaptureToBasisImage - After LoadFromMemory");
|
||||
#endif
|
||||
|
||||
stbi_uc* p_target;
|
||||
@@ -320,7 +361,7 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
|
||||
delete _zwImage;
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CCamera::CaptureToBasisImage - Done");
|
||||
LogFile.WriteHeapInfo("CaptureToBasisImage - Done");
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
@@ -333,8 +374,7 @@ esp_err_t CCamera::CaptureToFile(std::string nm, int delay)
|
||||
|
||||
LEDOnOff(true); // Switched off to save power !
|
||||
|
||||
if (delay > 0)
|
||||
{
|
||||
if (delay > 0) {
|
||||
LightOnOff(true);
|
||||
const TickType_t xDelay = delay / portTICK_PERIOD_MS;
|
||||
vTaskDelay( xDelay );
|
||||
@@ -344,11 +384,10 @@ esp_err_t CCamera::CaptureToFile(std::string nm, int delay)
|
||||
esp_camera_fb_return(fb);
|
||||
fb = esp_camera_fb_get();
|
||||
if (!fb) {
|
||||
ESP_LOGE(TAG, "CaptureToFile: Camera Capture Failed");
|
||||
LEDOnOff(false);
|
||||
LightOnOff(false);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Capture Failed (CCamera::CaptureToFile) --> Reboot! "
|
||||
"Check that your camera module is working and connected properly.");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Capture Failed. "
|
||||
"Check camera module and/or proper electrical connection");
|
||||
//doReboot();
|
||||
|
||||
return ESP_FAIL;
|
||||
@@ -395,24 +434,21 @@ esp_err_t CCamera::CaptureToFile(std::string nm, int delay)
|
||||
}
|
||||
|
||||
FILE * fp = fopen(nm.c_str(), "wb");
|
||||
if (fp == NULL) /* If an error occurs during the file creation */
|
||||
{
|
||||
fprintf(stderr, "fopen() failed for '%s'\n", nm.c_str());
|
||||
if (fp == NULL) { // If an error occurs during the file creation
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Failed to open file " + nm);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
fwrite(buf, sizeof(uint8_t), buf_len, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
if (converted)
|
||||
free(buf);
|
||||
|
||||
esp_camera_fb_return(fb);
|
||||
|
||||
if (delay > 0)
|
||||
{
|
||||
LightOnOff(false);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -420,29 +456,26 @@ esp_err_t CCamera::CaptureToFile(std::string nm, int delay)
|
||||
|
||||
esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay)
|
||||
{
|
||||
camera_fb_t * fb = NULL;
|
||||
esp_err_t res = ESP_OK;
|
||||
size_t fb_len = 0;
|
||||
int64_t fr_start = esp_timer_get_time();
|
||||
|
||||
|
||||
LEDOnOff(true);
|
||||
|
||||
if (delay > 0)
|
||||
{
|
||||
if (delay > 0) {
|
||||
LightOnOff(true);
|
||||
const TickType_t xDelay = delay / portTICK_PERIOD_MS;
|
||||
vTaskDelay( xDelay );
|
||||
}
|
||||
|
||||
|
||||
fb = esp_camera_fb_get();
|
||||
camera_fb_t *fb = esp_camera_fb_get();
|
||||
esp_camera_fb_return(fb);
|
||||
fb = esp_camera_fb_get();
|
||||
if (!fb) {
|
||||
ESP_LOGE(TAG, "Camera capture failed");
|
||||
LEDOnOff(false);
|
||||
LightOnOff(false);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Capture Failed. "
|
||||
"Check camera module and/or proper electrical connection");
|
||||
httpd_resp_send_500(req);
|
||||
// doReboot();
|
||||
|
||||
@@ -482,9 +515,7 @@ esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay)
|
||||
ESP_LOGI(TAG, "JPG: %uKB %ums", (uint32_t)(fb_len/1024), (uint32_t)((fr_end - fr_start)/1000));
|
||||
|
||||
if (delay > 0)
|
||||
{
|
||||
LightOnOff(false);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -494,19 +525,18 @@ void CCamera::LightOnOff(bool status)
|
||||
{
|
||||
GpioHandler* gpioHandler = gpio_handler_get();
|
||||
if ((gpioHandler != NULL) && (gpioHandler->isEnabled())) {
|
||||
ESP_LOGD(TAG, "Use gpioHandler flashLigh");
|
||||
ESP_LOGD(TAG, "Use gpioHandler to trigger flashlight");
|
||||
gpioHandler->flashLightEnable(status);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
#ifdef USE_PWM_LEDFLASH
|
||||
if (status)
|
||||
{
|
||||
if (status) {
|
||||
ESP_LOGD(TAG, "Internal Flash-LED turn on with PWM %d", led_intensity);
|
||||
ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, led_intensity));
|
||||
// Update duty to apply the new value
|
||||
ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
ESP_LOGD(TAG, "Internal Flash-LED turn off PWM");
|
||||
ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, 0));
|
||||
ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
|
||||
@@ -528,15 +558,17 @@ void CCamera::LightOnOff(bool status)
|
||||
|
||||
void CCamera::LEDOnOff(bool status)
|
||||
{
|
||||
// Init the GPIO
|
||||
gpio_pad_select_gpio(BLINK_GPIO);
|
||||
/* Set the GPIO as a push/pull output */
|
||||
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
|
||||
if (xHandle_task_StatusLED == NULL) {
|
||||
// Init the GPIO
|
||||
gpio_pad_select_gpio(BLINK_GPIO);
|
||||
/* Set the GPIO as a push/pull output */
|
||||
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
|
||||
|
||||
if (!status)
|
||||
gpio_set_level(BLINK_GPIO, 1);
|
||||
else
|
||||
gpio_set_level(BLINK_GPIO, 0);
|
||||
if (!status)
|
||||
gpio_set_level(BLINK_GPIO, 1);
|
||||
else
|
||||
gpio_set_level(BLINK_GPIO, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -555,33 +587,33 @@ void CCamera::GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol
|
||||
ESP_LOGD(TAG, "Query: %s", _query);
|
||||
if (httpd_query_key_value(_query, "size", _size, 10) == ESP_OK)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Size: %s", _size);
|
||||
#endif
|
||||
#endif
|
||||
if (strcmp(_size, "QVGA") == 0)
|
||||
resol = FRAMESIZE_QVGA; // 320x240
|
||||
if (strcmp(_size, "VGA") == 0)
|
||||
else if (strcmp(_size, "VGA") == 0)
|
||||
resol = FRAMESIZE_VGA; // 640x480
|
||||
if (strcmp(_size, "SVGA") == 0)
|
||||
else if (strcmp(_size, "SVGA") == 0)
|
||||
resol = FRAMESIZE_SVGA; // 800x600
|
||||
if (strcmp(_size, "XGA") == 0)
|
||||
else if (strcmp(_size, "XGA") == 0)
|
||||
resol = FRAMESIZE_XGA; // 1024x768
|
||||
if (strcmp(_size, "SXGA") == 0)
|
||||
else if (strcmp(_size, "SXGA") == 0)
|
||||
resol = FRAMESIZE_SXGA; // 1280x1024
|
||||
if (strcmp(_size, "UXGA") == 0)
|
||||
else if (strcmp(_size, "UXGA") == 0)
|
||||
resol = FRAMESIZE_UXGA; // 1600x1200
|
||||
}
|
||||
if (httpd_query_key_value(_query, "quality", _qual, 10) == ESP_OK)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Quality: %s", _qual);
|
||||
#endif
|
||||
#endif
|
||||
qual = atoi(_qual);
|
||||
|
||||
if (qual > 63)
|
||||
if (qual > 63) // Limit to max. 63
|
||||
qual = 63;
|
||||
if (qual < 0)
|
||||
qual = 0;
|
||||
else if (qual < 8) // Limit to min. 8
|
||||
qual = 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -591,28 +623,29 @@ framesize_t CCamera::TextToFramesize(const char * _size)
|
||||
{
|
||||
if (strcmp(_size, "QVGA") == 0)
|
||||
return FRAMESIZE_QVGA; // 320x240
|
||||
if (strcmp(_size, "VGA") == 0)
|
||||
else if (strcmp(_size, "VGA") == 0)
|
||||
return FRAMESIZE_VGA; // 640x480
|
||||
if (strcmp(_size, "SVGA") == 0)
|
||||
else if (strcmp(_size, "SVGA") == 0)
|
||||
return FRAMESIZE_SVGA; // 800x600
|
||||
if (strcmp(_size, "XGA") == 0)
|
||||
else if (strcmp(_size, "XGA") == 0)
|
||||
return FRAMESIZE_XGA; // 1024x768
|
||||
if (strcmp(_size, "SXGA") == 0)
|
||||
else if (strcmp(_size, "SXGA") == 0)
|
||||
return FRAMESIZE_SXGA; // 1280x1024
|
||||
if (strcmp(_size, "UXGA") == 0)
|
||||
return FRAMESIZE_UXGA; // 1600x1200
|
||||
else if (strcmp(_size, "UXGA") == 0)
|
||||
return FRAMESIZE_UXGA; // 1600x1200
|
||||
|
||||
return ActualResolution;
|
||||
}
|
||||
|
||||
|
||||
CCamera::CCamera()
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "CreateClassCamera");
|
||||
#endif
|
||||
brightness = -5;
|
||||
contrast = -5;
|
||||
saturation = -5;
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "CreateClassCamera");
|
||||
#endif
|
||||
brightness = 0;
|
||||
contrast = 0;
|
||||
saturation = 0;
|
||||
isFixedExposure = false;
|
||||
|
||||
ledc_init();
|
||||
|
||||
@@ -228,7 +228,7 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath, const
|
||||
if (chunksize > 0){
|
||||
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||
fclose(fd);
|
||||
ESP_LOGE(TAG, "File sending failed!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
@@ -267,7 +267,7 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath, const
|
||||
strlcpy(entrypath + dirpath_len, entry->d_name, sizeof(entrypath) - dirpath_len);
|
||||
ESP_LOGD(TAG, "Entrypath: %s", entrypath);
|
||||
if (stat(entrypath, &entry_stat) == -1) {
|
||||
ESP_LOGE(TAG, "Failed to stat %s: %s", entrytype, entry->d_name);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to stat " + string(entrytype) + ": " + string(entry->d_name));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -357,7 +357,7 @@ static esp_err_t send_datafile(httpd_req_t *req, bool send_full_file)
|
||||
|
||||
fd = fopen(currentfilename.c_str(), "r");
|
||||
if (!fd) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + std::string(currentfilename) +"!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + currentfilename + "!");
|
||||
/* Respond with 404 Error */
|
||||
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404());
|
||||
return ESP_FAIL;
|
||||
@@ -373,7 +373,7 @@ static esp_err_t send_datafile(httpd_req_t *req, bool send_full_file)
|
||||
|
||||
/* Adapted from https://www.geeksforgeeks.org/implement-your-own-tail-read-last-n-lines-of-a-huge-file/ */
|
||||
if (fseek(fd, 0, SEEK_END)) {
|
||||
ESP_LOGE(TAG, "Failed to get to end of file!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to get to end of file!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
else {
|
||||
@@ -381,7 +381,7 @@ static esp_err_t send_datafile(httpd_req_t *req, bool send_full_file)
|
||||
ESP_LOGI(TAG, "File contains %ld bytes", pos);
|
||||
|
||||
if (fseek(fd, pos - std::min((long)LOGFILE_LAST_PART_BYTES, pos), SEEK_SET)) { // Go LOGFILE_LAST_PART_BYTES bytes back from EOF
|
||||
ESP_LOGE(TAG, "Failed to go back %ld bytes within the file!", std::min((long)LOGFILE_LAST_PART_BYTES, pos));
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to go back " + to_string(std::min((long)LOGFILE_LAST_PART_BYTES, pos)) + " bytes within the file!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
@@ -404,7 +404,7 @@ static esp_err_t send_datafile(httpd_req_t *req, bool send_full_file)
|
||||
/* Send the buffer contents as HTTP response chunk */
|
||||
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||
fclose(fd);
|
||||
ESP_LOGE(TAG, "File sending failed!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!");
|
||||
/* Abort sending file */
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
/* Respond with 500 Internal Server Error */
|
||||
@@ -443,7 +443,7 @@ static esp_err_t send_logfile(httpd_req_t *req, bool send_full_file)
|
||||
|
||||
fd = fopen(currentfilename.c_str(), "r");
|
||||
if (!fd) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + std::string(currentfilename.c_str()) +"!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + currentfilename + "!");
|
||||
/* Respond with 404 Error */
|
||||
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404());
|
||||
return ESP_FAIL;
|
||||
@@ -459,7 +459,7 @@ static esp_err_t send_logfile(httpd_req_t *req, bool send_full_file)
|
||||
|
||||
/* Adapted from https://www.geeksforgeeks.org/implement-your-own-tail-read-last-n-lines-of-a-huge-file/ */
|
||||
if (fseek(fd, 0, SEEK_END)) {
|
||||
ESP_LOGE(TAG, "Failed to get to end of file!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to get to end of file!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
else {
|
||||
@@ -467,7 +467,7 @@ static esp_err_t send_logfile(httpd_req_t *req, bool send_full_file)
|
||||
ESP_LOGI(TAG, "File contains %ld bytes", pos);
|
||||
|
||||
if (fseek(fd, pos - std::min((long)LOGFILE_LAST_PART_BYTES, pos), SEEK_SET)) { // Go LOGFILE_LAST_PART_BYTES bytes back from EOF
|
||||
ESP_LOGE(TAG, "Failed to go back %ld bytes within the file!", std::min((long)LOGFILE_LAST_PART_BYTES, pos));
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to go back " + to_string(std::min((long)LOGFILE_LAST_PART_BYTES, pos)) + " bytes within the file!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
@@ -490,7 +490,7 @@ static esp_err_t send_logfile(httpd_req_t *req, bool send_full_file)
|
||||
/* Send the buffer contents as HTTP response chunk */
|
||||
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||
fclose(fd);
|
||||
ESP_LOGE(TAG, "File sending failed!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!");
|
||||
/* Abort sending file */
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
/* Respond with 500 Internal Server Error */
|
||||
@@ -530,7 +530,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
||||
|
||||
|
||||
if (!filename) {
|
||||
ESP_LOGE(TAG, "Filename is too long");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Filename is too long");
|
||||
/* Respond with 414 Error */
|
||||
httpd_resp_send_err(req, HTTPD_414_URI_TOO_LONG, "Filename too long");
|
||||
return ESP_FAIL;
|
||||
@@ -571,7 +571,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
||||
|
||||
fd = fopen(filepath, "r");
|
||||
if (!fd) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + std::string(filepath) +"!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + std::string(filepath) + "!");
|
||||
/* Respond with 404 Error */
|
||||
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404());
|
||||
return ESP_FAIL;
|
||||
@@ -592,7 +592,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
||||
/* Send the buffer contents as HTTP response chunk */
|
||||
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||
fclose(fd);
|
||||
ESP_LOGE(TAG, "File sending failed!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!");
|
||||
/* Abort sending file */
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
/* Respond with 500 Internal Server Error */
|
||||
@@ -634,14 +634,14 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
||||
|
||||
/* Filename cannot have a trailing '/' */
|
||||
if (filename[strlen(filename) - 1] == '/') {
|
||||
ESP_LOGE(TAG, "Invalid filename: %s", filename);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Invalid filename: " + string(filename));
|
||||
/* Respond with 400 Bad Request */
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid filename");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (stat(filepath, &file_stat) == 0) {
|
||||
ESP_LOGE(TAG, "File already exists: %s", filepath);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File already exists: " + string(filepath));
|
||||
/* Respond with 400 Bad Request */
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File already exists");
|
||||
return ESP_FAIL;
|
||||
@@ -649,7 +649,7 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
||||
|
||||
/* File cannot be larger than a limit */
|
||||
if (req->content_len > MAX_FILE_SIZE) {
|
||||
ESP_LOGE(TAG, "File too large: %d bytes", req->content_len);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File too large: " + to_string(req->content_len) + " bytes");
|
||||
/* Respond with 400 Bad Request */
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
|
||||
"File size must be less than "
|
||||
@@ -661,7 +661,7 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
||||
|
||||
fd = fopen(filepath, "w");
|
||||
if (!fd) {
|
||||
ESP_LOGE(TAG, "Failed to create file: %s", filepath);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create file: " + string(filepath));
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create file");
|
||||
return ESP_FAIL;
|
||||
@@ -692,7 +692,7 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
||||
fclose(fd);
|
||||
unlink(filepath);
|
||||
|
||||
ESP_LOGE(TAG, "File reception failed!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File reception failed!");
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file");
|
||||
return ESP_FAIL;
|
||||
@@ -705,7 +705,7 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
||||
fclose(fd);
|
||||
unlink(filepath);
|
||||
|
||||
ESP_LOGE(TAG, "File write failed!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File write failed!");
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to write file to storage");
|
||||
return ESP_FAIL;
|
||||
@@ -780,7 +780,7 @@ static esp_err_t delete_post_handler(httpd_req_t *req)
|
||||
|
||||
if (httpd_query_key_value(_query, "task", _valuechar, 30) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "task is found: %s", _valuechar);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "task is found: " + string(_valuechar));
|
||||
_task = std::string(_valuechar);
|
||||
}
|
||||
}
|
||||
@@ -826,26 +826,26 @@ static esp_err_t delete_post_handler(httpd_req_t *req)
|
||||
|
||||
/* Filename cannot have a trailing '/' */
|
||||
if (filename[strlen(filename) - 1] == '/') {
|
||||
ESP_LOGE(TAG, "Invalid filename: %s", filename);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Invalid filename: " + string(filename));
|
||||
/* Respond with 400 Bad Request */
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid filename");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (strcmp(filename, "wlan.ini") == 0) {
|
||||
ESP_LOGE(TAG, "Trying to delete protected file : %s", filename);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to delete protected file : " + string(filename));
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Not allowed to delete wlan.ini");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (stat(filepath, &file_stat) == -1) {
|
||||
ESP_LOGE(TAG, "File does not exist: %s", filename);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File does not exist: " + string(filename));
|
||||
/* Respond with 400 Bad Request */
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File does not exist");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Deleting file: %s", filename);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Deleting file: " + string(filename));
|
||||
/* Delete file */
|
||||
unlink(filepath);
|
||||
|
||||
@@ -887,7 +887,7 @@ void delete_all_in_directory(std::string _directory)
|
||||
std::string filename;
|
||||
|
||||
if (!dir) {
|
||||
ESP_LOGE(TAG, "Failed to stat dir: %s", _directory.c_str());
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to stat dir: " + _directory);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -896,7 +896,7 @@ void delete_all_in_directory(std::string _directory)
|
||||
if (!(entry->d_type == DT_DIR)){
|
||||
if (strcmp("wlan.ini", entry->d_name) != 0){ // auf wlan.ini soll nicht zugegriffen werden !!!
|
||||
filename = _directory + "/" + std::string(entry->d_name);
|
||||
ESP_LOGI(TAG, "Deleting file: %s", filename.c_str());
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Deleting file: " + filename);
|
||||
/* Delete file */
|
||||
unlink(filename.c_str());
|
||||
}
|
||||
@@ -930,7 +930,7 @@ std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::st
|
||||
|
||||
// Get and print information about each file in the archive.
|
||||
int numberoffiles = (int)mz_zip_reader_get_num_files(&zip_archive);
|
||||
ESP_LOGI(TAG, "Numbers of files to be extracted: %d", numberoffiles);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Numbers of files to be extracted: " + to_string(numberoffiles));
|
||||
|
||||
sort_iter = 0;
|
||||
{
|
||||
@@ -953,7 +953,7 @@ std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::st
|
||||
p = mz_zip_reader_extract_file_to_heap(&zip_archive, archive_filename, &uncomp_size, 0);
|
||||
if (!p)
|
||||
{
|
||||
ESP_LOGE(TAG, "mz_zip_reader_extract_file_to_heap() failed on file %s", archive_filename);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mz_zip_reader_extract_file_to_heap() failed on file " + string(archive_filename));
|
||||
mz_zip_reader_end(&zip_archive);
|
||||
return ret;
|
||||
}
|
||||
@@ -996,8 +996,12 @@ std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::st
|
||||
|
||||
ESP_LOGI(TAG, "Filename to extract: %s, Zwischenfilename: %s", zw.c_str(), filename_zw.c_str());
|
||||
|
||||
std::string folder = filename_zw.substr(0, filename_zw.find_last_of('/'));
|
||||
MakeDir(folder);
|
||||
|
||||
// extrahieren in zwischendatei
|
||||
DeleteFile(filename_zw);
|
||||
|
||||
FILE* fpTargetFile = fopen(filename_zw.c_str(), "wb");
|
||||
uint writtenbytes = fwrite(p, 1, (uint)uncomp_size, fpTargetFile);
|
||||
fclose(fpTargetFile);
|
||||
@@ -1011,21 +1015,22 @@ std::string unzip_new(std::string _in_zip_file, std::string _target_zip, std::st
|
||||
else
|
||||
{
|
||||
isokay = false;
|
||||
ESP_LOGD(TAG, "ERROR in writting extracted file (function fwrite) extracted file \"%s\", size %u", archive_filename, (uint)uncomp_size);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ERROR in writting extracted file (function fwrite) extracted file \"" +
|
||||
string(archive_filename) + "\", size " + to_string(uncomp_size));
|
||||
}
|
||||
|
||||
DeleteFile(zw);
|
||||
if (!isokay)
|
||||
ESP_LOGE(TAG, "ERROR in fwrite \"%s\", size %u", archive_filename, (uint)uncomp_size);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ERROR in fwrite \"" + string(archive_filename) + "\", size " + to_string(uncomp_size));
|
||||
isokay = isokay && RenameFile(filename_zw, zw);
|
||||
if (!isokay)
|
||||
ESP_LOGE(TAG, "ERROR in Rename \"%s\" to \"%s\"", filename_zw.c_str(), zw.c_str());
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ERROR in Rename \"" + filename_zw + "\" to \"" + zw);
|
||||
|
||||
if (isokay)
|
||||
ESP_LOGI(TAG, "Successfully extracted file \"%s\", size %u", archive_filename, (uint)uncomp_size);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Successfully extracted file \"" + string(archive_filename) + "\", size " + to_string(uncomp_size));
|
||||
else
|
||||
{
|
||||
ESP_LOGE(TAG, "ERROR in extracting file \"%s\", size %u", archive_filename, (uint)uncomp_size);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ERROR in extracting file \"" + string(archive_filename) + "\", size " + to_string(uncomp_size));
|
||||
ret = "ERROR";
|
||||
}
|
||||
mz_free(p);
|
||||
@@ -1059,7 +1064,7 @@ void unzip(std::string _in_zip_file, std::string _target_directory){
|
||||
status = mz_zip_reader_init_file(&zip_archive, _in_zip_file.c_str(), 0);
|
||||
if (!status)
|
||||
{
|
||||
ESP_LOGD(TAG, "mz_zip_reader_init_file() failed!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mz_zip_reader_init_file() failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1071,7 +1076,7 @@ void unzip(std::string _in_zip_file, std::string _target_directory){
|
||||
status = mz_zip_reader_init_file(&zip_archive, _in_zip_file.c_str(), sort_iter ? MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY : 0);
|
||||
if (!status)
|
||||
{
|
||||
ESP_LOGD(TAG, "mz_zip_reader_init_file() failed!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mz_zip_reader_init_file() failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1085,7 +1090,7 @@ void unzip(std::string _in_zip_file, std::string _target_directory){
|
||||
p = mz_zip_reader_extract_file_to_heap(&zip_archive, archive_filename, &uncomp_size, 0);
|
||||
if (!p)
|
||||
{
|
||||
ESP_LOGD(TAG, "mz_zip_reader_extract_file_to_heap() failed!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mz_zip_reader_extract_file_to_heap() failed!");
|
||||
mz_zip_reader_end(&zip_archive);
|
||||
return;
|
||||
}
|
||||
@@ -1121,19 +1126,19 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
|
||||
/* Validate file storage base path */
|
||||
if (!base_path) {
|
||||
// if (!base_path || strcmp(base_path, "/spiffs") != 0) {
|
||||
ESP_LOGE(TAG, "File server base_path not set");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File server base_path not set");
|
||||
// return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (server_data) {
|
||||
ESP_LOGE(TAG, "File server already started");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File server already started");
|
||||
// return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
/* Allocate memory for server data */
|
||||
server_data = (file_server_data *) calloc(1, sizeof(struct file_server_data));
|
||||
if (!server_data) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for server data");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to allocate memory for server data");
|
||||
// return ESP_ERR_NO_MEM;
|
||||
}
|
||||
strlcpy(server_data->base_path, base_path,
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
#include "Helper.h"
|
||||
#include "statusled.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
/*an ota data write buffer ready to write to the flash*/
|
||||
@@ -56,9 +57,9 @@ bool initial_setup = false;
|
||||
static void infinite_loop(void)
|
||||
{
|
||||
int i = 0;
|
||||
ESP_LOGI(TAG, "When a new firmware is available on the server, press the reset button to download it");
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "When a new firmware is available on the server, press the reset button to download it");
|
||||
while(1) {
|
||||
ESP_LOGI(TAG, "Waiting for a new firmware... %d", ++i);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Waiting for a new firmware... (" + to_string(++i) + ")");
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
@@ -66,6 +67,8 @@ static void infinite_loop(void)
|
||||
|
||||
void task_do_Update_ZIP(void *pvParameter)
|
||||
{
|
||||
StatusLED(AP_OR_OTA, 1, true); // Signaling an OTA update
|
||||
|
||||
std::string filetype = toUpper(getFileType(_file_name_update));
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "File: " + _file_name_update + " Filetype: " + filetype);
|
||||
@@ -87,13 +90,13 @@ void task_do_Update_ZIP(void *pvParameter)
|
||||
ota_update_task(retfirmware);
|
||||
}
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trigger reboot due to firmware update.");
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trigger reboot due to firmware update");
|
||||
doRebootOTA();
|
||||
} else if (filetype == "BIN")
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Do firmware update - file: " + _file_name_update);
|
||||
ota_update_task(_file_name_update);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trigger reboot due to firmware update.");
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trigger reboot due to firmware update");
|
||||
doRebootOTA();
|
||||
}
|
||||
else
|
||||
@@ -108,7 +111,7 @@ void CheckUpdate()
|
||||
FILE *pfile;
|
||||
if ((pfile = fopen("/sdcard/update.txt", "r")) == NULL)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "No update triggered.");
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "No pending update");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -120,13 +123,13 @@ void CheckUpdate()
|
||||
std::string _szw = std::string(zw);
|
||||
if (_szw == "init")
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Inital Setup triggered.");
|
||||
initial_setup = true; }
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Inital Setup triggered");
|
||||
}
|
||||
}
|
||||
|
||||
fclose(pfile);
|
||||
DeleteFile("/sdcard/update.txt"); // Prevent Boot Loop!!!
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Update during boot triggered - Update File: " + _file_name_update);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Start update process (" + _file_name_update + ")");
|
||||
|
||||
|
||||
xTaskCreate(&task_do_Update_ZIP, "task_do_Update_ZIP", configMINIMAL_STACK_SIZE * 35, NULL, tskIDLE_PRIORITY+1, NULL);
|
||||
@@ -148,10 +151,10 @@ static bool ota_update_task(std::string fn)
|
||||
const esp_partition_t *configured = esp_ota_get_boot_partition();
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
|
||||
if (configured != running) {
|
||||
ESP_LOGW(TAG, "Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x",
|
||||
configured->address, running->address);
|
||||
ESP_LOGW(TAG, "(This can happen if either the OTA boot data or preferred boot image become somehow corrupted.)");
|
||||
if (configured != running) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Configured OTA boot partition at offset " + to_string(configured->address) +
|
||||
", but running from offset " + to_string(running->address));
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "(This can happen if either the OTA boot data or preferred boot image become somehow corrupted.)");
|
||||
}
|
||||
ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)",
|
||||
running->type, running->subtype, running->address);
|
||||
@@ -179,7 +182,7 @@ static bool ota_update_task(std::string fn)
|
||||
|
||||
while (data_read > 0) {
|
||||
if (data_read < 0) {
|
||||
ESP_LOGE(TAG, "Error: SSL data read error");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Error: SSL data read error");
|
||||
return false;
|
||||
} else if (data_read > 0) {
|
||||
if (image_header_was_checked == false) {
|
||||
@@ -203,16 +206,17 @@ static bool ota_update_task(std::string fn)
|
||||
// check current version with last invalid partition
|
||||
if (last_invalid_app != NULL) {
|
||||
if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0) {
|
||||
ESP_LOGW(TAG, "New version is the same as invalid version.");
|
||||
ESP_LOGW(TAG, "Previously, there was an attempt to launch the firmware with %s version, but it failed.", invalid_app_info.version);
|
||||
ESP_LOGW(TAG, "The firmware has been rolled back to the previous version.");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "New version is the same as invalid version");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Previously, there was an attempt to launch the firmware with " +
|
||||
string(invalid_app_info.version) + " version, but it failed");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "The firmware has been rolled back to the previous version");
|
||||
infinite_loop();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (memcmp(new_app_info.version, running_app_info.version, sizeof(new_app_info.version)) == 0) {
|
||||
ESP_LOGW(TAG, "Current running version is the same as a new. We will not continue the update.");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Current running version is the same as a new. We will not continue the update");
|
||||
infinite_loop();
|
||||
}
|
||||
*/
|
||||
@@ -220,12 +224,12 @@ static bool ota_update_task(std::string fn)
|
||||
|
||||
err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_ota_begin failed (" + string(esp_err_to_name(err)) + ")");
|
||||
return false;
|
||||
}
|
||||
ESP_LOGI(TAG, "esp_ota_begin succeeded");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "received package is not fit len");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "received package is not fit len");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -241,7 +245,7 @@ static bool ota_update_task(std::string fn)
|
||||
// * `errno` to check for underlying transport connectivity closure if any
|
||||
//
|
||||
if (errno == ECONNRESET || errno == ENOTCONN) {
|
||||
ESP_LOGE(TAG, "Connection closed, errno = %d", errno);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection closed, errno = " + to_string(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -254,15 +258,15 @@ static bool ota_update_task(std::string fn)
|
||||
err = esp_ota_end(update_handle);
|
||||
if (err != ESP_OK) {
|
||||
if (err == ESP_ERR_OTA_VALIDATE_FAILED) {
|
||||
ESP_LOGE(TAG, "Image validation failed, image is corrupted");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Image validation failed, image is corrupted");
|
||||
}
|
||||
ESP_LOGE(TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err));
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_ota_end failed (" + string(esp_err_to_name(err)) + ")!");
|
||||
return false;
|
||||
}
|
||||
|
||||
err = esp_ota_set_boot_partition(update_partition);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err));
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_ota_set_boot_partition failed (" + string(esp_err_to_name(err)) + ")!");
|
||||
|
||||
}
|
||||
// ESP_LOGI(TAG, "Prepare to restart system!");
|
||||
@@ -329,7 +333,7 @@ void CheckOTAUpdate(void)
|
||||
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution...");
|
||||
esp_ota_mark_app_valid_cancel_rollback();
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Diagnostics failed! Start rollback to the previous version...");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Diagnostics failed! Start rollback to the previous version...");
|
||||
esp_ota_mark_app_invalid_rollback_and_reboot();
|
||||
}
|
||||
}
|
||||
@@ -353,7 +357,7 @@ void CheckOTAUpdate(void)
|
||||
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution...");
|
||||
esp_ota_mark_app_valid_cancel_rollback();
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Diagnostics failed! Start rollback to the previous version...");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Diagnostics failed! Start rollback to the previous version...");
|
||||
esp_ota_mark_app_invalid_rollback_and_reboot();
|
||||
}
|
||||
}
|
||||
@@ -445,7 +449,7 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
||||
if ((filetype == "ZIP") || (filetype == "BIN"))
|
||||
{
|
||||
FILE *pfile;
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Update for reboot.");
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Update for reboot");
|
||||
pfile = fopen("/sdcard/update.txt", "w");
|
||||
fwrite(fn.c_str(), fn.length(), 1, pfile);
|
||||
fclose(pfile);
|
||||
@@ -522,7 +526,7 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGD(TAG, "File does not exist: %s", fn.c_str());
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File does not exist: " + fn);
|
||||
}
|
||||
/* Respond with an empty chunk to signal HTTP response completion */
|
||||
std::string zw = "file deleted\n";
|
||||
@@ -537,7 +541,7 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
||||
httpd_resp_send(req, zw.c_str(), strlen(zw.c_str()));
|
||||
httpd_resp_send_chunk(req, NULL, 0);
|
||||
|
||||
ESP_LOGE(TAG, "ota without parameter - should not be the case!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ota without parameter - should not be the case!");
|
||||
|
||||
/*
|
||||
const char* resp_str;
|
||||
@@ -586,6 +590,9 @@ void task_reboot(void *KillAutoFlow)
|
||||
KillTFliteTasks(); // Kill autoflow task if executed in extra task, if not don't kill parent task
|
||||
}
|
||||
|
||||
Camera.LightOnOff(false);
|
||||
StatusLEDOff();
|
||||
|
||||
/* Stop service tasks */
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTdestroy_client(true);
|
||||
@@ -600,20 +607,20 @@ void task_reboot(void *KillAutoFlow)
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
hard_restart(); // Reset type: System reset (Triggered by watchdog), if esp_restart stalls (WDT needs to be activated)
|
||||
|
||||
ESP_LOGE(TAG, "Reboot failed!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Reboot failed!");
|
||||
vTaskDelete(NULL); //Delete this task if it comes to this point
|
||||
}
|
||||
|
||||
|
||||
void doReboot()
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Reboot triggered by Software (5s).");
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Reboot triggered by Software (5s)");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Reboot in 5sec");
|
||||
|
||||
BaseType_t xReturned = xTaskCreate(&task_reboot, "task_reboot", configMINIMAL_STACK_SIZE * 3, (void*) true, 10, NULL);
|
||||
BaseType_t xReturned = xTaskCreate(&task_reboot, "task_reboot", configMINIMAL_STACK_SIZE * 4, (void*) true, 10, NULL);
|
||||
if( xReturned != pdPASS )
|
||||
{
|
||||
ESP_LOGE(TAG, "task_reboot not created -> force reboot without killing flow");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "task_reboot not created -> force reboot without killing flow");
|
||||
task_reboot((void*) false);
|
||||
}
|
||||
vTaskDelay(10000 / portTICK_PERIOD_MS); // Prevent serving web client fetch response until system is shuting down
|
||||
@@ -624,6 +631,8 @@ void doRebootOTA()
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Reboot in 5sec");
|
||||
|
||||
Camera.LightOnOff(false);
|
||||
StatusLEDOff();
|
||||
esp_camera_deinit();
|
||||
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
@@ -641,7 +650,7 @@ esp_err_t handler_reboot(httpd_req_t *req)
|
||||
#endif
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_reboot");
|
||||
ESP_LOGI(TAG, "!!! System will restart within 5 sec!!!");
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "!!! System will restart within 5 sec!!!");
|
||||
|
||||
std::string response =
|
||||
"<html><head><script>"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "ClassFlowAlignment.h"
|
||||
#include "ClassFlowMakeImage.h"
|
||||
#include "ClassFlowTakeImage.h"
|
||||
#include "ClassFlow.h"
|
||||
#include "server_tflite.h"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
#include "psram.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
|
||||
@@ -31,7 +32,7 @@ void ClassFlowAlignment::SetInitialParameter(void)
|
||||
ImageBasis = NULL;
|
||||
ImageTMP = NULL;
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
AlgROI = (ImageData*)heap_caps_malloc(sizeof(ImageData), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
||||
AlgROI = (ImageData*)malloc_psram_heap(std::string(TAG) + "->AlgROI", sizeof(ImageData), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
||||
#endif
|
||||
previousElement = NULL;
|
||||
disabled = false;
|
||||
@@ -46,16 +47,16 @@ ClassFlowAlignment::ClassFlowAlignment(std::vector<ClassFlow*>* lfc)
|
||||
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowMakeImage") == 0)
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowTakeImage") == 0)
|
||||
{
|
||||
ImageBasis = ((ClassFlowMakeImage*) (*ListFlowControll)[i])->rawImage;
|
||||
ImageBasis = ((ClassFlowTakeImage*) (*ListFlowControll)[i])->rawImage;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ImageBasis) // the function take pictures does not exist --> must be created first ONLY FOR TEST PURPOSES
|
||||
{
|
||||
ESP_LOGD(TAG, "CImageBasis had to be created");
|
||||
ImageBasis = new CImageBasis(namerawimage);
|
||||
ImageBasis = new CImageBasis("ImageBasis", namerawimage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +66,7 @@ bool ClassFlowAlignment::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
std::vector<string> splitted;
|
||||
int suchex = 40;
|
||||
int suchey = 40;
|
||||
int alg_algo = 0;
|
||||
int alg_algo = 0; //default=0; 1 =HIGHACCURACY; 2= FAST; 3= OFF //add disable aligment algo |01.2023
|
||||
|
||||
|
||||
aktparamgraph = trim(aktparamgraph);
|
||||
@@ -74,7 +75,7 @@ bool ClassFlowAlignment::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||
return false;
|
||||
|
||||
if (aktparamgraph.compare("[Alignment]") != 0) //Paragraph does not fit MakeImage
|
||||
if (aktparamgraph.compare("[Alignment]") != 0) //Paragraph does not fit Alignment
|
||||
return false;
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
@@ -130,6 +131,8 @@ bool ClassFlowAlignment::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
alg_algo = 1;
|
||||
if (toUpper(splitted[1]) == "FAST")
|
||||
alg_algo = 2;
|
||||
if (toUpper(splitted[1]) == "OFF") //no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||
alg_algo = 3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +148,10 @@ bool ClassFlowAlignment::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
#endif
|
||||
}
|
||||
|
||||
LoadReferenceAlignmentValues();
|
||||
//no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||
if(References[0].alignment_algo != 3){
|
||||
LoadReferenceAlignmentValues();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -184,7 +190,7 @@ bool ClassFlowAlignment::doFlow(string time)
|
||||
|
||||
if (!ImageTMP)
|
||||
{
|
||||
ImageTMP = new CImageBasis(ImageBasis);
|
||||
ImageTMP = new CImageBasis("ImageTMP", ImageBasis);
|
||||
if (!ImageTMP)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate ImageTMP -> Exec this round aborted!");
|
||||
@@ -194,7 +200,7 @@ bool ClassFlowAlignment::doFlow(string time)
|
||||
}
|
||||
|
||||
delete AlignAndCutImage;
|
||||
AlignAndCutImage = new CAlignAndCutImage(ImageBasis, ImageTMP);
|
||||
AlignAndCutImage = new CAlignAndCutImage("AlignAndCutImage", ImageBasis, ImageTMP);
|
||||
if (!AlignAndCutImage)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlignAndCutImage -> Exec this round aborted!");
|
||||
@@ -202,7 +208,7 @@ bool ClassFlowAlignment::doFlow(string time)
|
||||
return false;
|
||||
}
|
||||
|
||||
CRotateImage rt(AlignAndCutImage, ImageTMP, initialflip);
|
||||
CRotateImage rt("rawImage", AlignAndCutImage, ImageTMP, initialflip);
|
||||
if (initialflip)
|
||||
{
|
||||
int _zw = ImageBasis->height;
|
||||
@@ -234,14 +240,22 @@ bool ClassFlowAlignment::doFlow(string time)
|
||||
AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/rot.jpg"));
|
||||
}
|
||||
|
||||
if (!AlignAndCutImage->Align(&References[0], &References[1]))
|
||||
{
|
||||
SaveReferenceAlignmentValues();
|
||||
}
|
||||
|
||||
//no align algo if set to 3 = off //add disable aligment algo |01.2023
|
||||
if(References[0].alignment_algo != 3){
|
||||
if (!AlignAndCutImage->Align(&References[0], &References[1]))
|
||||
{
|
||||
SaveReferenceAlignmentValues();
|
||||
}
|
||||
}// no align
|
||||
|
||||
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
if (AlgROI) {
|
||||
DrawRef(ImageTMP);
|
||||
//no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||
if(References[0].alignment_algo != 3){
|
||||
DrawRef(ImageTMP);
|
||||
}
|
||||
tfliteflow.DigitalDrawROI(ImageTMP);
|
||||
tfliteflow.AnalogDrawROI(ImageTMP);
|
||||
ImageTMP->writeToMemoryAsJPG((ImageData*)AlgROI, 90);
|
||||
@@ -258,7 +272,10 @@ bool ClassFlowAlignment::doFlow(string time)
|
||||
delete ImageTMP;
|
||||
ImageTMP = NULL;
|
||||
|
||||
LoadReferenceAlignmentValues();
|
||||
//no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||
if(References[0].alignment_algo != 3){
|
||||
LoadReferenceAlignmentValues();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNTy
|
||||
CNNType = AutoDetect;
|
||||
CNNType = _cnntype;
|
||||
flowpostalignment = _flowalign;
|
||||
logfileRetentionInDays = 5;
|
||||
imagesRetention = 5;
|
||||
}
|
||||
|
||||
|
||||
@@ -324,9 +324,9 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
splitted = ZerlegeZeile(aktparamgraph);
|
||||
if ((toUpper(splitted[0]) == "LOGIMAGELOCATION") && (splitted.size() > 1))
|
||||
if ((toUpper(splitted[0]) == "ROIIMAGESLOCATION") && (splitted.size() > 1))
|
||||
{
|
||||
this->LogImageLocation = "/sdcard" + splitted[1];
|
||||
this->imagesLocation = "/sdcard" + splitted[1];
|
||||
this->isLogImage = true;
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "LOGIMAGESELECT") && (splitted.size() > 1))
|
||||
@@ -335,9 +335,9 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
isLogImageSelect = true;
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "LOGFILERETENTIONINDAYS") && (splitted.size() > 1))
|
||||
if ((toUpper(splitted[0]) == "ROIIMAGESRETENTION") && (splitted.size() > 1))
|
||||
{
|
||||
this->logfileRetentionInDays = std::stoi(splitted[1]);
|
||||
this->imagesRetention = std::stoi(splitted[1]);
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "MODEL") && (splitted.size() > 1))
|
||||
@@ -381,8 +381,10 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
GENERAL[_ana]->ROI[i]->image = new CImageBasis(modelxsize, modelysize, modelchannel);
|
||||
GENERAL[_ana]->ROI[i]->image_org = new CImageBasis(GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, 3);
|
||||
GENERAL[_ana]->ROI[i]->image = new CImageBasis("ROI " + GENERAL[_ana]->ROI[i]->name,
|
||||
modelxsize, modelysize, modelchannel);
|
||||
GENERAL[_ana]->ROI[i]->image_org = new CImageBasis("ROI " + GENERAL[_ana]->ROI[i]->name + " original",
|
||||
GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, 3);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -25,6 +25,7 @@ extern "C" {
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
#include "server_help.h"
|
||||
#include "server_tflite.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char* TAG = "CTRL";
|
||||
@@ -38,8 +39,8 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
|
||||
|
||||
ESP_LOGD(TAG, "Step %s start", _stepname.c_str());
|
||||
|
||||
if ((_stepname.compare("[MakeImage]") == 0) || (_stepname.compare(";[MakeImage]") == 0)){
|
||||
_classname = "ClassFlowMakeImage";
|
||||
if ((_stepname.compare("[TakeImage]") == 0) || (_stepname.compare(";[TakeImage]") == 0)){
|
||||
_classname = "ClassFlowTakeImage";
|
||||
}
|
||||
if ((_stepname.compare("[Alignment]") == 0) || (_stepname.compare(";[Alignment]") == 0)){
|
||||
_classname = "ClassFlowAlignment";
|
||||
@@ -60,11 +61,14 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
|
||||
if ((_stepname.compare("[InfluxDB]") == 0) || (_stepname.compare(";[InfluxDB]") == 0)){
|
||||
_classname = "ClassFlowInfluxDB";
|
||||
}
|
||||
if ((_stepname.compare("[InfluxDBv2]") == 0) || (_stepname.compare(";[InfluxDBv2]") == 0)){
|
||||
_classname = "ClassFlowInfluxDBv2";
|
||||
}
|
||||
#endif //ENABLE_INFLUXDB
|
||||
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
if (FlowControll[i]->name().compare(_classname) == 0){
|
||||
if (!(FlowControll[i]->name().compare("ClassFlowMakeImage") == 0)) // if it is a MakeImage, the image does not need to be included, this happens automatically with the html query.
|
||||
if (!(FlowControll[i]->name().compare("ClassFlowTakeImage") == 0)) // if it is a TakeImage, the image does not need to be included, this happens automatically with the html query.
|
||||
FlowControll[i]->doFlow("");
|
||||
result = FlowControll[i]->getHTMLSingleStep(_host);
|
||||
}
|
||||
@@ -77,7 +81,7 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
|
||||
|
||||
std::string ClassFlowControll::TranslateAktstatus(std::string _input)
|
||||
{
|
||||
if (_input.compare("ClassFlowMakeImage") == 0)
|
||||
if (_input.compare("ClassFlowTakeImage") == 0)
|
||||
return ("Take Image");
|
||||
if (_input.compare("ClassFlowAlignment") == 0)
|
||||
return ("Aligning");
|
||||
@@ -90,6 +94,8 @@ std::string ClassFlowControll::TranslateAktstatus(std::string _input)
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
if (_input.compare("ClassFlowInfluxDB") == 0)
|
||||
return ("Sending InfluxDB");
|
||||
if (_input.compare("ClassFlowInfluxDBv2") == 0)
|
||||
return ("Sending InfluxDBv2");
|
||||
#endif //ENABLE_INFLUXDB
|
||||
if (_input.compare("ClassFlowPostProcessing") == 0)
|
||||
return ("Post-Processing");
|
||||
@@ -171,7 +177,7 @@ bool ClassFlowControll::StartMQTTService() {
|
||||
/* Start the MQTT service */
|
||||
for (int i = 0; i < FlowControll.size(); ++i) {
|
||||
if (FlowControll[i]->name().compare("ClassFlowMQTT") == 0) {
|
||||
return ((ClassFlowMQTT*) (FlowControll[i]))->Start(AutoIntervall);
|
||||
return ((ClassFlowMQTT*) (FlowControll[i]))->Start(AutoInterval);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -183,19 +189,20 @@ void ClassFlowControll::SetInitialParameter(void)
|
||||
{
|
||||
AutoStart = false;
|
||||
SetupModeActive = false;
|
||||
AutoIntervall = 10; // Minutes
|
||||
AutoInterval = 10; // Minutes
|
||||
flowdigit = NULL;
|
||||
flowanalog = NULL;
|
||||
flowpostprocessing = NULL;
|
||||
disabled = false;
|
||||
aktRunNr = 0;
|
||||
aktstatus = "Flow task not yet created";
|
||||
aktstatusWithTime = aktstatus;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowControll::isAutoStart(long &_intervall)
|
||||
bool ClassFlowControll::isAutoStart(long &_interval)
|
||||
{
|
||||
_intervall = AutoIntervall * 60 * 1000; // AutoInterval: minutes -> ms
|
||||
_interval = AutoInterval * 60 * 1000; // AutoInterval: minutes -> ms
|
||||
return AutoStart;
|
||||
}
|
||||
|
||||
@@ -206,10 +213,10 @@ ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
|
||||
|
||||
_type = trim(_type);
|
||||
|
||||
if (toUpper(_type).compare("[MAKEIMAGE]") == 0)
|
||||
if (toUpper(_type).compare("[TAKEIMAGE]") == 0)
|
||||
{
|
||||
cfc = new ClassFlowMakeImage(&FlowControll);
|
||||
flowmakeimage = (ClassFlowMakeImage*) cfc;
|
||||
cfc = new ClassFlowTakeImage(&FlowControll);
|
||||
flowtakeimage = (ClassFlowTakeImage*) cfc;
|
||||
}
|
||||
if (toUpper(_type).compare("[ALIGNMENT]") == 0)
|
||||
{
|
||||
@@ -233,6 +240,8 @@ ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
if (toUpper(_type).compare("[INFLUXDB]") == 0)
|
||||
cfc = new ClassFlowInfluxDB(&FlowControll);
|
||||
if (toUpper(_type).compare("[INFLUXDBV2]") == 0)
|
||||
cfc = new ClassFlowInfluxDBv2(&FlowControll);
|
||||
#endif //ENABLE_INFLUXDB
|
||||
if (toUpper(_type).compare("[WRITELIST]") == 0)
|
||||
cfc = new ClassFlowWriteList(&FlowControll);
|
||||
@@ -265,8 +274,10 @@ ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
|
||||
void ClassFlowControll::InitFlow(std::string config)
|
||||
{
|
||||
aktstatus = "Initialization";
|
||||
aktstatusWithTime = aktstatus;
|
||||
|
||||
//#ifdef ENABLE_MQTT
|
||||
//MQTTPublish(mqttServer_getMainTopic() + "/" + "status", "Initialization", false); // Right now, not possible -> MQTT Service is going to be started later
|
||||
//MQTTPublish(mqttServer_getMainTopic() + "/" + "status", "Initialization", 1, false); // Right now, not possible -> MQTT Service is going to be started later
|
||||
//#endif //ENABLE_MQTT
|
||||
|
||||
string line;
|
||||
@@ -290,6 +301,7 @@ void ClassFlowControll::InitFlow(std::string config)
|
||||
while ((line.size() > 0) && !(feof(pFile)))
|
||||
{
|
||||
cfc = CreateClassFlow(line);
|
||||
// printf("Name: %s\n", cfc->name().c_str());
|
||||
if (cfc)
|
||||
{
|
||||
ESP_LOGD(TAG, "Start ReadParameter (%s)", line.c_str());
|
||||
@@ -310,6 +322,12 @@ void ClassFlowControll::InitFlow(std::string config)
|
||||
}
|
||||
|
||||
|
||||
std::string* ClassFlowControll::getActStatusWithTime()
|
||||
{
|
||||
return &aktstatusWithTime;
|
||||
}
|
||||
|
||||
|
||||
std::string* ClassFlowControll::getActStatus()
|
||||
{
|
||||
return &aktstatus;
|
||||
@@ -319,21 +337,22 @@ std::string* ClassFlowControll::getActStatus()
|
||||
void ClassFlowControll::setActStatus(std::string _aktstatus)
|
||||
{
|
||||
aktstatus = _aktstatus;
|
||||
aktstatusWithTime = aktstatus;
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowControll::doFlowMakeImageOnly(string time)
|
||||
void ClassFlowControll::doFlowTakeImageOnly(string time)
|
||||
{
|
||||
std::string zw_time;
|
||||
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
{
|
||||
if (FlowControll[i]->name() == "ClassFlowMakeImage") {
|
||||
if (FlowControll[i]->name() == "ClassFlowTakeImage") {
|
||||
zw_time = getCurrentTimeString("%H:%M:%S");
|
||||
std::string flowStatus = TranslateAktstatus(FlowControll[i]->name());
|
||||
aktstatus = flowStatus + " (" + zw_time + ")";
|
||||
aktstatus = TranslateAktstatus(FlowControll[i]->name());
|
||||
aktstatusWithTime = aktstatus + " (" + zw_time + ")";
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", flowStatus, false);
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, 1, false);
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
FlowControll[i]->doFlow(time);
|
||||
@@ -347,6 +366,7 @@ bool ClassFlowControll::doFlow(string time)
|
||||
bool result = true;
|
||||
std::string zw_time;
|
||||
int repeat = 0;
|
||||
int qos = 1;
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowControll::doFlow - Start");
|
||||
@@ -363,11 +383,11 @@ bool ClassFlowControll::doFlow(string time)
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
{
|
||||
zw_time = getCurrentTimeString("%H:%M:%S");
|
||||
std::string flowStatus = TranslateAktstatus(FlowControll[i]->name());
|
||||
aktstatus = flowStatus + " (" + zw_time + ")";
|
||||
//LogFile.WriteToFile(ESP_LOG_INFO, TAG, aktstatus);
|
||||
aktstatus = TranslateAktstatus(FlowControll[i]->name());
|
||||
aktstatusWithTime = aktstatus + " (" + zw_time + ")";
|
||||
//LogFile.WriteToFile(ESP_LOG_INFO, TAG, aktstatusWithTime);
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", flowStatus, false);
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, qos, false);
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
@@ -397,11 +417,11 @@ bool ClassFlowControll::doFlow(string time)
|
||||
}
|
||||
|
||||
zw_time = getCurrentTimeString("%H:%M:%S");
|
||||
std::string flowStatus = "Flow finished";
|
||||
aktstatus = flowStatus + " (" + zw_time + ")";
|
||||
//LogFile.WriteToFile(ESP_LOG_INFO, TAG, aktstatus);
|
||||
aktstatus = "Flow finished";
|
||||
aktstatusWithTime = aktstatus + " (" + zw_time + ")";
|
||||
//LogFile.WriteToFile(ESP_LOG_INFO, TAG, aktstatusWithTime);
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", flowStatus, false);
|
||||
MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, qos, false);
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
return result;
|
||||
@@ -527,7 +547,7 @@ bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
|
||||
|
||||
if ((toUpper(aktparamgraph).compare("[AUTOTIMER]") != 0) && (toUpper(aktparamgraph).compare("[DEBUG]") != 0) &&
|
||||
(toUpper(aktparamgraph).compare("[SYSTEM]") != 0 && (toUpper(aktparamgraph).compare("[DATALOGGING]") != 0))) // Paragraph passt nicht zu MakeImage
|
||||
(toUpper(aktparamgraph).compare("[SYSTEM]") != 0 && (toUpper(aktparamgraph).compare("[DATALOGGING]") != 0))) // Paragraph passt nicht zu Debug oder DataLogging
|
||||
return false;
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
@@ -541,9 +561,9 @@ bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
}
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "INTERVALL") && (splitted.size() > 1))
|
||||
if ((toUpper(splitted[0]) == "INTERVAL") && (splitted.size() > 1))
|
||||
{
|
||||
AutoIntervall = std::stof(splitted[1]);
|
||||
AutoInterval = std::stof(splitted[1]);
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "DATALOGACTIVE") && (splitted.size() > 1))
|
||||
@@ -557,12 +577,12 @@ bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
}
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "DATALOGRETENTIONINDAYS") && (splitted.size() > 1))
|
||||
if ((toUpper(splitted[0]) == "DATAFILESRETENTION") && (splitted.size() > 1))
|
||||
{
|
||||
LogFile.SetDataLogRetention(std::stoi(splitted[1]));
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "LOGFILE") && (splitted.size() > 1))
|
||||
if ((toUpper(splitted[0]) == "LOGLEVEL") && (splitted.size() > 1))
|
||||
{
|
||||
/* matches esp_log_level_t */
|
||||
if ((toUpper(splitted[1]) == "TRUE") || (toUpper(splitted[1]) == "2"))
|
||||
@@ -581,26 +601,33 @@ bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
LogFile.setLogLevel(ESP_LOG_DEBUG);
|
||||
}
|
||||
|
||||
/* If system reboot was not triggered by user and reboot was caused by execption -> keep log level to DEBUG */
|
||||
if (!getIsPlannedReboot() && (esp_reset_reason() == ESP_RST_PANIC))
|
||||
LogFile.setLogLevel(ESP_LOG_DEBUG);
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "LOGFILERETENTIONINDAYS") && (splitted.size() > 1))
|
||||
if ((toUpper(splitted[0]) == "LOGFILESRETENTION") && (splitted.size() > 1))
|
||||
{
|
||||
LogFile.SetLogFileRetention(std::stoi(splitted[1]));
|
||||
}
|
||||
|
||||
/* TimeServer and TimeZone got already read from the config, see setupTime () */
|
||||
|
||||
if ((toUpper(splitted[0]) == "RSSITHREASHOLD") && (splitted.size() > 1))
|
||||
|
||||
#if (defined WLAN_USE_ROAMING_BY_SCANNING || (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES))
|
||||
if ((toUpper(splitted[0]) == "RSSITHRESHOLD") && (splitted.size() > 1))
|
||||
{
|
||||
if (ChangeRSSIThreashold(WLAN_CONFIG_FILE, atoi(splitted[1].c_str())))
|
||||
int RSSIThresholdTMP = atoi(splitted[1].c_str());
|
||||
RSSIThresholdTMP = min(0, max(-100, RSSIThresholdTMP)); // Verify input limits (-100 - 0)
|
||||
|
||||
if (ChangeRSSIThreshold(WLAN_CONFIG_FILE, RSSIThresholdTMP))
|
||||
{
|
||||
// reboot necessary so that the new wlan.ini is also used !!!
|
||||
fclose(pfile);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Rebooting to activate new RSSITHREASHOLD ...");
|
||||
esp_restart();
|
||||
hard_restart();
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Rebooting to activate new RSSITHRESHOLD ...");
|
||||
doReboot();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((toUpper(splitted[0]) == "HOSTNAME") && (splitted.size() > 1))
|
||||
{
|
||||
@@ -608,9 +635,7 @@ bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
// reboot necessary so that the new wlan.ini is also used !!!
|
||||
fclose(pfile);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Rebooting to activate new HOSTNAME...");
|
||||
esp_restart();
|
||||
hard_restart();
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Rebooting to activate new HOSTNAME...");
|
||||
doReboot();
|
||||
}
|
||||
}
|
||||
@@ -660,7 +685,7 @@ int ClassFlowControll::CleanTempFolder() {
|
||||
|
||||
esp_err_t ClassFlowControll::SendRawJPG(httpd_req_t *req)
|
||||
{
|
||||
return flowmakeimage != NULL ? flowmakeimage->SendRawJPG(req) : ESP_FAIL;
|
||||
return flowtakeimage != NULL ? flowtakeimage->SendRawJPG(req) : ESP_FAIL;
|
||||
}
|
||||
|
||||
|
||||
@@ -801,7 +826,7 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
_send = new CImageBasis(flowalignment->ImageBasis);
|
||||
_send = new CImageBasis("alg_roi", flowalignment->ImageBasis);
|
||||
|
||||
if (_send->ImageOkay()) {
|
||||
if (flowalignment) flowalignment->DrawRef(_send);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "ClassFlow.h"
|
||||
#include "ClassFlowMakeImage.h"
|
||||
#include "ClassFlowTakeImage.h"
|
||||
#include "ClassFlowAlignment.h"
|
||||
#include "ClassFlowCNNGeneral.h"
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
@@ -15,6 +15,7 @@
|
||||
#endif //ENABLE_MQTT
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
#include "ClassFlowInfluxDB.h"
|
||||
#include "ClassFlowInfluxDBv2.h"
|
||||
#endif //ENABLE_INFLUXDB
|
||||
#include "ClassFlowCNNGeneral.h"
|
||||
#include "ClassFlowWriteList.h"
|
||||
@@ -29,20 +30,21 @@ protected:
|
||||
ClassFlowCNNGeneral* flowanalog;
|
||||
ClassFlowCNNGeneral* flowdigit;
|
||||
// ClassFlowDigit* flowdigit;
|
||||
ClassFlowMakeImage* flowmakeimage;
|
||||
ClassFlowTakeImage* flowtakeimage;
|
||||
ClassFlow* CreateClassFlow(std::string _type);
|
||||
|
||||
bool AutoStart;
|
||||
float AutoIntervall;
|
||||
float AutoInterval;
|
||||
bool SetupModeActive;
|
||||
void SetInitialParameter(void);
|
||||
std::string aktstatusWithTime;
|
||||
std::string aktstatus;
|
||||
int aktRunNr;
|
||||
|
||||
public:
|
||||
void InitFlow(std::string config);
|
||||
bool doFlow(string time);
|
||||
void doFlowMakeImageOnly(string time);
|
||||
void doFlowTakeImageOnly(string time);
|
||||
bool getStatusSetupModus(){return SetupModeActive;};
|
||||
string getReadout(bool _rawvalue, bool _noerror);
|
||||
string getReadoutAll(int _type);
|
||||
@@ -68,8 +70,9 @@ public:
|
||||
|
||||
std::string doSingleStep(std::string _stepname, std::string _host);
|
||||
|
||||
bool isAutoStart(long &_intervall);
|
||||
bool isAutoStart(long &_interval);
|
||||
|
||||
std::string* getActStatusWithTime();
|
||||
std::string* getActStatus();
|
||||
void setActStatus(std::string _aktstatus);
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ struct NumberPost {
|
||||
int DecimalShiftInitial;
|
||||
float AnalogDigitalTransitionStart; // When is the digit > x.1, i.e. when does it start to tilt?
|
||||
int Nachkomma;
|
||||
string Fieldname; // Fieldname in InfluxDB2
|
||||
|
||||
bool isExtendedResolution;
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ ClassFlowImage::ClassFlowImage(const char* logTag)
|
||||
this->logTag = logTag;
|
||||
isLogImage = false;
|
||||
disabled = false;
|
||||
this->logfileRetentionInDays = 5;
|
||||
this->imagesRetention = 5;
|
||||
}
|
||||
|
||||
ClassFlowImage::ClassFlowImage(std::vector<ClassFlow*> * lfc, const char* logTag) : ClassFlow(lfc)
|
||||
@@ -32,7 +32,7 @@ ClassFlowImage::ClassFlowImage(std::vector<ClassFlow*> * lfc, const char* logTag
|
||||
this->logTag = logTag;
|
||||
isLogImage = false;
|
||||
disabled = false;
|
||||
this->logfileRetentionInDays = 5;
|
||||
this->imagesRetention = 5;
|
||||
}
|
||||
|
||||
ClassFlowImage::ClassFlowImage(std::vector<ClassFlow*> * lfc, ClassFlow *_prev, const char* logTag) : ClassFlow(lfc, _prev)
|
||||
@@ -40,7 +40,7 @@ ClassFlowImage::ClassFlowImage(std::vector<ClassFlow*> * lfc, ClassFlow *_prev,
|
||||
this->logTag = logTag;
|
||||
isLogImage = false;
|
||||
disabled = false;
|
||||
this->logfileRetentionInDays = 5;
|
||||
this->imagesRetention = 5;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ string ClassFlowImage::CreateLogFolder(string time) {
|
||||
if (!isLogImage)
|
||||
return "";
|
||||
|
||||
string logPath = LogImageLocation + "/" + time.LOGFILE_TIME_FORMAT_DATE_EXTR + "/" + time.LOGFILE_TIME_FORMAT_HOUR_EXTR;
|
||||
string logPath = imagesLocation + "/" + time.LOGFILE_TIME_FORMAT_DATE_EXTR + "/" + time.LOGFILE_TIME_FORMAT_HOUR_EXTR;
|
||||
isLogImage = mkdir_r(logPath.c_str(), S_IRWXU) == 0;
|
||||
if (!isLogImage) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't create log folder for analog images. Path " + logPath);
|
||||
@@ -95,7 +95,7 @@ void ClassFlowImage::RemoveOldLogs()
|
||||
return;
|
||||
|
||||
ESP_LOGD(TAG, "remove old images");
|
||||
if (logfileRetentionInDays == 0) {
|
||||
if (imagesRetention == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -104,17 +104,17 @@ void ClassFlowImage::RemoveOldLogs()
|
||||
char cmpfilename[30];
|
||||
|
||||
time(&rawtime);
|
||||
rawtime = addDays(rawtime, -logfileRetentionInDays + 1);
|
||||
rawtime = addDays(rawtime, -1 * imagesRetention + 1);
|
||||
timeinfo = localtime(&rawtime);
|
||||
//ESP_LOGD(TAG, "ImagefileRetentionInDays: %d", logfileRetentionInDays);
|
||||
//ESP_LOGD(TAG, "ImagefileRetentionInDays: %d", imagesRetention);
|
||||
|
||||
strftime(cmpfilename, 30, LOGFILE_TIME_FORMAT, timeinfo);
|
||||
//ESP_LOGD(TAG, "file name to compare: %s", cmpfilename);
|
||||
string folderName = string(cmpfilename).LOGFILE_TIME_FORMAT_DATE_EXTR;
|
||||
|
||||
DIR *dir = opendir(LogImageLocation.c_str());
|
||||
DIR *dir = opendir(imagesLocation.c_str());
|
||||
if (!dir) {
|
||||
ESP_LOGE(TAG, "Failed to stat dir: %s", LogImageLocation.c_str());
|
||||
ESP_LOGE(TAG, "Failed to stat dir: %s", imagesLocation.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ void ClassFlowImage::RemoveOldLogs()
|
||||
int deleted = 0;
|
||||
int notDeleted = 0;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
string folderPath = LogImageLocation + "/" + entry->d_name;
|
||||
string folderPath = imagesLocation + "/" + entry->d_name;
|
||||
if (entry->d_type == DT_DIR) {
|
||||
//ESP_LOGD(TAG, "Compare %s to %s", entry->d_name, folderName.c_str());
|
||||
if ((strlen(entry->d_name) == folderName.length()) && (strcmp(entry->d_name, folderName.c_str()) < 0)) {
|
||||
|
||||
@@ -10,9 +10,9 @@ using namespace std;
|
||||
class ClassFlowImage : public ClassFlow
|
||||
{
|
||||
protected:
|
||||
string LogImageLocation;
|
||||
string imagesLocation;
|
||||
bool isLogImage;
|
||||
unsigned short logfileRetentionInDays;
|
||||
unsigned short imagesRetention;
|
||||
const char* logTag;
|
||||
|
||||
string CreateLogFolder(string time);
|
||||
|
||||
219
code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.cpp
Normal file
219
code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.cpp
Normal file
@@ -0,0 +1,219 @@
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
#include <sstream>
|
||||
#include "ClassFlowInfluxDBv2.h"
|
||||
#include "Helper.h"
|
||||
#include "connect_wlan.h"
|
||||
|
||||
#include "time_sntp.h"
|
||||
#include "interface_influxdb.h"
|
||||
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
#include "esp_log.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
static const char* TAG = "class_flow_influxDbv2";
|
||||
|
||||
void ClassFlowInfluxDBv2::SetInitialParameter(void)
|
||||
{
|
||||
uri = "";
|
||||
database = "";
|
||||
measurement = "";
|
||||
dborg = "";
|
||||
dbtoken = "";
|
||||
// dbfield = "";
|
||||
|
||||
OldValue = "";
|
||||
flowpostprocessing = NULL;
|
||||
previousElement = NULL;
|
||||
ListFlowControll = NULL;
|
||||
disabled = false;
|
||||
InfluxDBenable = false;
|
||||
}
|
||||
|
||||
ClassFlowInfluxDBv2::ClassFlowInfluxDBv2()
|
||||
{
|
||||
SetInitialParameter();
|
||||
}
|
||||
|
||||
ClassFlowInfluxDBv2::ClassFlowInfluxDBv2(std::vector<ClassFlow*>* lfc)
|
||||
{
|
||||
SetInitialParameter();
|
||||
|
||||
ListFlowControll = lfc;
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
|
||||
{
|
||||
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClassFlowInfluxDBv2::ClassFlowInfluxDBv2(std::vector<ClassFlow*>* lfc, ClassFlow *_prev)
|
||||
{
|
||||
SetInitialParameter();
|
||||
|
||||
previousElement = _prev;
|
||||
ListFlowControll = lfc;
|
||||
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
|
||||
{
|
||||
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowInfluxDBv2::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
std::vector<string> splitted;
|
||||
|
||||
aktparamgraph = trim(aktparamgraph);
|
||||
printf("akt param: %s\n", aktparamgraph.c_str());
|
||||
|
||||
if (aktparamgraph.size() == 0)
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||
return false;
|
||||
|
||||
if (toUpper(aktparamgraph).compare("[INFLUXDBV2]") != 0)
|
||||
return false;
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
// ESP_LOGD(TAG, "while loop reading line: %s", aktparamgraph.c_str());
|
||||
splitted = ZerlegeZeile(aktparamgraph);
|
||||
std::string _param = GetParameterName(splitted[0]);
|
||||
|
||||
if ((toUpper(_param) == "ORG") && (splitted.size() > 1))
|
||||
{
|
||||
this->dborg = splitted[1];
|
||||
}
|
||||
if ((toUpper(_param) == "TOKEN") && (splitted.size() > 1))
|
||||
{
|
||||
this->dbtoken = splitted[1];
|
||||
}
|
||||
if ((toUpper(_param) == "URI") && (splitted.size() > 1))
|
||||
{
|
||||
this->uri = splitted[1];
|
||||
}
|
||||
if (((toUpper(_param) == "MEASUREMENT")) && (splitted.size() > 1))
|
||||
{
|
||||
this->measurement = splitted[1];
|
||||
}
|
||||
if (((toUpper(_param) == "FIELDNAME")) && (splitted.size() > 1))
|
||||
{
|
||||
handleFieldname(splitted[0], splitted[1]);
|
||||
}
|
||||
if (((toUpper(splitted[0]) == "DATABASE")) && (splitted.size() > 1))
|
||||
{
|
||||
this->database = splitted[1];
|
||||
}
|
||||
}
|
||||
|
||||
printf("uri: %s\n", uri.c_str());
|
||||
printf("measurement: %s\n", measurement.c_str());
|
||||
printf("org: %s\n", dborg.c_str());
|
||||
printf("token: %s\n", dbtoken.c_str());
|
||||
|
||||
if ((uri.length() > 0) && (database.length() > 0) && (measurement.length() > 0) && (dbtoken.length() > 0) && (dborg.length() > 0))
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDB with uri: " + uri + ", measurement: " + measurement + ", org: " + dborg + ", token: *****");
|
||||
// printf("vor V2 Init\n");
|
||||
InfluxDB_V2_Init(uri, database, measurement, dborg, dbtoken);
|
||||
// printf("nach V2 Init\n");
|
||||
InfluxDBenable = true;
|
||||
} else {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBv2 (Verion2 !!!) init skipped as we are missing some parameters");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowInfluxDBv2::GetInfluxDBMeasurement()
|
||||
{
|
||||
return measurement;
|
||||
}
|
||||
|
||||
void ClassFlowInfluxDBv2::handleFieldname(string _decsep, string _value)
|
||||
{
|
||||
string _digit, _decpos;
|
||||
int _pospunkt = _decsep.find_first_of(".");
|
||||
// ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt);
|
||||
if (_pospunkt > -1)
|
||||
_digit = _decsep.substr(0, _pospunkt);
|
||||
else
|
||||
_digit = "default";
|
||||
for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j)
|
||||
{
|
||||
if (_digit == "default") // Set to default first (if nothing else is set)
|
||||
{
|
||||
flowpostprocessing->NUMBERS[j]->Fieldname = _value;
|
||||
}
|
||||
if (flowpostprocessing->NUMBERS[j]->name == _digit)
|
||||
{
|
||||
flowpostprocessing->NUMBERS[j]->Fieldname = _value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ClassFlowInfluxDBv2::doFlow(string zwtime)
|
||||
{
|
||||
if (!InfluxDBenable)
|
||||
return true;
|
||||
|
||||
std::string result;
|
||||
std::string resulterror = "";
|
||||
std::string resultraw = "";
|
||||
std::string resultrate = "";
|
||||
std::string resulttimestamp = "";
|
||||
string zw = "";
|
||||
string namenumber = "";
|
||||
|
||||
if (flowpostprocessing)
|
||||
{
|
||||
std::vector<NumberPost*>* NUMBERS = flowpostprocessing->GetNumbers();
|
||||
|
||||
for (int i = 0; i < (*NUMBERS).size(); ++i)
|
||||
{
|
||||
result = (*NUMBERS)[i]->ReturnValue;
|
||||
resultraw = (*NUMBERS)[i]->ReturnRawValue;
|
||||
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
||||
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
||||
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
||||
|
||||
if ((*NUMBERS)[i]->Fieldname.length() > 0)
|
||||
{
|
||||
namenumber = (*NUMBERS)[i]->Fieldname;
|
||||
}
|
||||
else
|
||||
{
|
||||
namenumber = (*NUMBERS)[i]->name;
|
||||
if (namenumber == "default")
|
||||
namenumber = "value";
|
||||
else
|
||||
namenumber = namenumber + "/value";
|
||||
}
|
||||
|
||||
printf("vor sende Influx_DB_V2 - namenumber. %s, result: %s, timestampt: %s", namenumber.c_str(), result.c_str(), resulttimestamp.c_str());
|
||||
|
||||
if (result.length() > 0)
|
||||
InfluxDB_V2_Publish(namenumber, result, resulttimestamp);
|
||||
// InfluxDB_V2_Publish(namenumber, result, resulttimestamp);
|
||||
}
|
||||
}
|
||||
|
||||
OldValue = result;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif //ENABLE_INFLUXDB
|
||||
41
code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.h
Normal file
41
code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef CLASSFINFLUXDBv2_H
|
||||
#define CLASSFINFLUXDBv2_H
|
||||
|
||||
#include "ClassFlow.h"
|
||||
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
class ClassFlowInfluxDBv2 :
|
||||
public ClassFlow
|
||||
{
|
||||
protected:
|
||||
std::string uri, database, measurement;
|
||||
std::string dborg, dbtoken, dbfield;
|
||||
std::string OldValue;
|
||||
ClassFlowPostProcessing* flowpostprocessing;
|
||||
bool InfluxDBenable;
|
||||
|
||||
void SetInitialParameter(void);
|
||||
|
||||
void handleFieldname(string _decsep, string _value);
|
||||
|
||||
public:
|
||||
ClassFlowInfluxDBv2();
|
||||
ClassFlowInfluxDBv2(std::vector<ClassFlow*>* lfc);
|
||||
ClassFlowInfluxDBv2(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
||||
|
||||
string GetInfluxDBMeasurement();
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string name(){return "ClassFlowInfluxDBv2";};
|
||||
};
|
||||
|
||||
#endif //CLASSFINFLUXDBv2_H
|
||||
#endif //ENABLE_INFLUXDB
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "ClassFlowMQTT.h"
|
||||
#include "Helper.h"
|
||||
#include "connect_wlan.h"
|
||||
#include "read_wlanini.h"
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
#include "time_sntp.h"
|
||||
@@ -31,18 +32,18 @@ void ClassFlowMQTT::SetInitialParameter(void)
|
||||
topicError = "";
|
||||
topicRate = "";
|
||||
topicTimeStamp = "";
|
||||
maintopic = hostname;
|
||||
maintopic = wlan_config.hostname;
|
||||
|
||||
topicUptime = "";
|
||||
topicFreeMem = "";
|
||||
|
||||
clientname = "AIOTED-" + getMac();
|
||||
clientname = wlan_config.hostname;
|
||||
|
||||
OldValue = "";
|
||||
flowpostprocessing = NULL;
|
||||
user = "";
|
||||
password = "";
|
||||
SetRetainFlag = 0;
|
||||
SetRetainFlag = false;
|
||||
previousElement = NULL;
|
||||
ListFlowControll = NULL;
|
||||
disabled = false;
|
||||
@@ -95,7 +96,7 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||
return false;
|
||||
|
||||
if (toUpper(aktparamgraph).compare("[MQTT]") != 0) // Paragraph does not fit MakeImage
|
||||
if (toUpper(aktparamgraph).compare("[MQTT]") != 0) // Paragraph does not fit MQTT
|
||||
return false;
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
@@ -113,10 +114,10 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
this->uri = splitted[1];
|
||||
}
|
||||
if ((toUpper(splitted[0]) == "SETRETAINFLAG") && (splitted.size() > 1))
|
||||
if ((toUpper(splitted[0]) == "RETAINMESSAGES") && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "TRUE") {
|
||||
SetRetainFlag = 1;
|
||||
SetRetainFlag = true;
|
||||
setMqtt_Server_Retain(SetRetainFlag);
|
||||
}
|
||||
}
|
||||
@@ -155,6 +156,9 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
else if (toUpper(splitted[1]) == "ENERGY_MWH") {
|
||||
mqttServer_setMeterType("energy", "MWh", "h", "MW");
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "ENERGY_GJ") {
|
||||
mqttServer_setMeterType("energy", "GJ", "h", "GJ/h");
|
||||
}
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "CLIENTID") && (splitted.size() > 1))
|
||||
@@ -165,7 +169,6 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
if (((toUpper(splitted[0]) == "TOPIC") || (toUpper(splitted[0]) == "MAINTOPIC")) && (splitted.size() > 1))
|
||||
{
|
||||
maintopic = splitted[1];
|
||||
mqttServer_setMainTopic(maintopic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,6 +177,8 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
* How ever we need the interval parameter from the ClassFlowControll, but that only gets started later.
|
||||
* To work around this, we delay the start and trigger it from ClassFlowControll::ReadParameter() */
|
||||
|
||||
mqttServer_setMainTopic(maintopic);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -184,9 +189,9 @@ string ClassFlowMQTT::GetMQTTMainTopic()
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowMQTT::Start(float AutoIntervall)
|
||||
bool ClassFlowMQTT::Start(float AutoInterval)
|
||||
{
|
||||
roundInterval = AutoIntervall; // Minutes
|
||||
roundInterval = AutoInterval; // Minutes
|
||||
keepAlive = roundInterval * 60 * 2.5; // Seconds, make sure it is greater thatn 2 rounds!
|
||||
|
||||
std::stringstream stream;
|
||||
@@ -209,6 +214,7 @@ bool ClassFlowMQTT::Start(float AutoIntervall)
|
||||
|
||||
bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
{
|
||||
bool success;
|
||||
std::string result;
|
||||
std::string resulterror = "";
|
||||
std::string resultraw = "";
|
||||
@@ -219,8 +225,12 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
std::string resultchangabs = "";
|
||||
string zw = "";
|
||||
string namenumber = "";
|
||||
int qos = 1;
|
||||
|
||||
publishSystemData();
|
||||
/* Send the the Homeassistant Discovery and the Static Topics in case they where scheduled */
|
||||
sendDiscovery_and_static_Topics();
|
||||
|
||||
success = publishSystemData(qos);
|
||||
|
||||
if (flowpostprocessing && getMQTTisConnected())
|
||||
{
|
||||
@@ -246,13 +256,13 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
|
||||
|
||||
if (result.length() > 0)
|
||||
MQTTPublish(namenumber + "value", result, SetRetainFlag);
|
||||
success |= MQTTPublish(namenumber + "value", result, qos, SetRetainFlag);
|
||||
|
||||
if (resulterror.length() > 0)
|
||||
MQTTPublish(namenumber + "error", resulterror, SetRetainFlag);
|
||||
success |= MQTTPublish(namenumber + "error", resulterror, qos, SetRetainFlag);
|
||||
|
||||
if (resultrate.length() > 0) {
|
||||
MQTTPublish(namenumber + "rate", resultrate, SetRetainFlag);
|
||||
success |= MQTTPublish(namenumber + "rate", resultrate, qos, SetRetainFlag);
|
||||
|
||||
std::string resultRatePerTimeUnit;
|
||||
if (getTimeUnit() == "h") { // Need conversion to be per hour
|
||||
@@ -261,22 +271,22 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
else { // Keep per minute
|
||||
resultRatePerTimeUnit = resultrate;
|
||||
}
|
||||
MQTTPublish(namenumber + "rate_per_time_unit", resultRatePerTimeUnit, SetRetainFlag);
|
||||
success |= MQTTPublish(namenumber + "rate_per_time_unit", resultRatePerTimeUnit, qos, SetRetainFlag);
|
||||
}
|
||||
|
||||
if (resultchangabs.length() > 0) {
|
||||
MQTTPublish(namenumber + "changeabsolut", resultchangabs, SetRetainFlag); // Legacy API
|
||||
MQTTPublish(namenumber + "rate_per_digitalization_round", resultchangabs, SetRetainFlag);
|
||||
success |= MQTTPublish(namenumber + "changeabsolut", resultchangabs, qos, SetRetainFlag); // Legacy API
|
||||
success |= MQTTPublish(namenumber + "rate_per_digitalization_round", resultchangabs, qos, SetRetainFlag);
|
||||
}
|
||||
|
||||
if (resultraw.length() > 0)
|
||||
MQTTPublish(namenumber + "raw", resultraw, SetRetainFlag);
|
||||
success |= MQTTPublish(namenumber + "raw", resultraw, qos, SetRetainFlag);
|
||||
|
||||
if (resulttimestamp.length() > 0)
|
||||
MQTTPublish(namenumber + "timestamp", resulttimestamp, SetRetainFlag);
|
||||
success |= MQTTPublish(namenumber + "timestamp", resulttimestamp, qos, SetRetainFlag);
|
||||
|
||||
std::string json = flowpostprocessing->getJsonFromNumber(i, "\n");
|
||||
MQTTPublish(namenumber + "json", json, SetRetainFlag);
|
||||
success |= MQTTPublish(namenumber + "json", json, qos, SetRetainFlag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,10 +304,14 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
// result = result + "\t" + zw;
|
||||
// }
|
||||
// }
|
||||
// MQTTPublish(topic, result, SetRetainFlag);
|
||||
// success |= MQTTPublish(topic, result, qos, SetRetainFlag);
|
||||
// }
|
||||
|
||||
OldValue = result;
|
||||
|
||||
if (!success) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "One or more MQTT topics failed to be published!");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ protected:
|
||||
std::string OldValue;
|
||||
ClassFlowPostProcessing* flowpostprocessing;
|
||||
std::string user, password;
|
||||
int SetRetainFlag;
|
||||
bool SetRetainFlag;
|
||||
int keepAlive; // Seconds
|
||||
float roundInterval; // Minutes
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
ClassFlowMQTT(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
||||
|
||||
string GetMQTTMainTopic();
|
||||
bool Start(float AutoIntervall);
|
||||
bool Start(float AutoInterval);
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
#include "Helper.h"
|
||||
#include "ClassFlowMakeImage.h"
|
||||
#include "ClassFlowTakeImage.h"
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
#include <iomanip>
|
||||
@@ -285,7 +285,7 @@ ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc, C
|
||||
ListFlowControll = NULL;
|
||||
FilePreValue = FormatFileName("/sdcard/config/prevalue.ini");
|
||||
ListFlowControll = lfc;
|
||||
flowMakeImage = NULL;
|
||||
flowTakeImage = NULL;
|
||||
UpdatePreValueINI = false;
|
||||
IgnoreLeadingNaN = false;
|
||||
flowAnalog = _analog;
|
||||
@@ -293,9 +293,9 @@ ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc, C
|
||||
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowMakeImage") == 0)
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowTakeImage") == 0)
|
||||
{
|
||||
flowMakeImage = (ClassFlowMakeImage*) (*ListFlowControll)[i];
|
||||
flowTakeImage = (ClassFlowTakeImage*) (*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -402,7 +402,7 @@ void ClassFlowPostProcessing::handleAllowNegativeRate(string _decsep, string _va
|
||||
_digit = _decsep.substr(0, _pospunkt);
|
||||
else
|
||||
_digit = "default";
|
||||
|
||||
|
||||
for (int j = 0; j < NUMBERS.size(); ++j)
|
||||
{
|
||||
bool _rt = false;
|
||||
@@ -503,7 +503,7 @@ bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
return false;
|
||||
|
||||
|
||||
if (aktparamgraph.compare("[PostProcessing]") != 0) // Paragraph does not fit MakeImage
|
||||
if (aktparamgraph.compare("[PostProcessing]") != 0) // Paragraph does not fit PostProcessing
|
||||
return false;
|
||||
|
||||
InitNUMBERS();
|
||||
@@ -715,7 +715,7 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
|
||||
|
||||
// Update decimal point, as the decimal places can also change when changing from CNNType Auto --> xyz:
|
||||
|
||||
imagetime = flowMakeImage->getTimeImageTaken();
|
||||
imagetime = flowTakeImage->getTimeImageTaken();
|
||||
if (imagetime == 0)
|
||||
time(&imagetime);
|
||||
|
||||
@@ -841,6 +841,7 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
|
||||
|
||||
if (!NUMBERS[j]->AllowNegativeRates)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handleAllowNegativeRate for device: " + NUMBERS[j]->name);
|
||||
if ((NUMBERS[j]->Value < NUMBERS[j]->PreValue))
|
||||
{
|
||||
#ifdef SERIAL_DEBUG
|
||||
@@ -848,6 +849,7 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
|
||||
NUMBERS[j]->PreValue-(2/pow(10, NUMBERS[j]->Nachkomma))
|
||||
) ;
|
||||
#endif
|
||||
|
||||
// Include inaccuracy of 0.2 for isExtendedResolution.
|
||||
if (NUMBERS[j]->Value >= (NUMBERS[j]->PreValue-(2/pow(10, NUMBERS[j]->Nachkomma))) && NUMBERS[j]->isExtendedResolution) {
|
||||
NUMBERS[j]->Value = NUMBERS[j]->PreValue;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#define CLASSFFLOWPOSTPROCESSING_H
|
||||
|
||||
#include "ClassFlow.h"
|
||||
#include "ClassFlowMakeImage.h"
|
||||
#include "ClassFlowTakeImage.h"
|
||||
#include "ClassFlowCNNGeneral.h"
|
||||
#include "ClassFlowDefineTypes.h"
|
||||
|
||||
@@ -15,7 +15,6 @@ class ClassFlowPostProcessing :
|
||||
public ClassFlow
|
||||
{
|
||||
protected:
|
||||
std::vector<NumberPost*> NUMBERS;
|
||||
bool UpdatePreValueINI;
|
||||
|
||||
int PreValueAgeStartup;
|
||||
@@ -29,7 +28,7 @@ protected:
|
||||
|
||||
string FilePreValue;
|
||||
|
||||
ClassFlowMakeImage *flowMakeImage;
|
||||
ClassFlowTakeImage *flowTakeImage;
|
||||
|
||||
bool LoadPreValue(void);
|
||||
string ShiftDecimal(string in, int _decShift);
|
||||
@@ -54,6 +53,8 @@ protected:
|
||||
|
||||
public:
|
||||
bool PreValueUse;
|
||||
std::vector<NumberPost*> NUMBERS;
|
||||
|
||||
|
||||
ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit);
|
||||
virtual ~ClassFlowPostProcessing(){};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "ClassFlowMakeImage.h"
|
||||
#include "ClassFlowTakeImage.h"
|
||||
#include "Helper.h"
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
static const char* TAG = "flow_make_image";
|
||||
|
||||
esp_err_t ClassFlowMakeImage::camera_capture(){
|
||||
esp_err_t ClassFlowTakeImage::camera_capture(){
|
||||
string nm = namerawimage;
|
||||
Camera.CaptureToFile(nm);
|
||||
time(&TimeImageTaken);
|
||||
@@ -26,7 +26,7 @@ esp_err_t ClassFlowMakeImage::camera_capture(){
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void ClassFlowMakeImage::takePictureWithFlash(int flash_duration)
|
||||
void ClassFlowTakeImage::takePictureWithFlash(int flash_duration)
|
||||
{
|
||||
// in case the image is flipped, it must be reset here //
|
||||
rawImage->width = image_width;
|
||||
@@ -40,7 +40,7 @@ void ClassFlowMakeImage::takePictureWithFlash(int flash_duration)
|
||||
if (SaveAllFiles) rawImage->SaveToFile(namerawimage);
|
||||
}
|
||||
|
||||
void ClassFlowMakeImage::SetInitialParameter(void)
|
||||
void ClassFlowTakeImage::SetInitialParameter(void)
|
||||
{
|
||||
waitbeforepicture = 5;
|
||||
isImageSize = false;
|
||||
@@ -56,15 +56,15 @@ void ClassFlowMakeImage::SetInitialParameter(void)
|
||||
}
|
||||
|
||||
|
||||
ClassFlowMakeImage::ClassFlowMakeImage(std::vector<ClassFlow*>* lfc) : ClassFlowImage(lfc, TAG)
|
||||
ClassFlowTakeImage::ClassFlowTakeImage(std::vector<ClassFlow*>* lfc) : ClassFlowImage(lfc, TAG)
|
||||
{
|
||||
LogImageLocation = "/log/source";
|
||||
logfileRetentionInDays = 5;
|
||||
imagesLocation = "/log/source";
|
||||
imagesRetention = 5;
|
||||
SetInitialParameter();
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowMakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
bool ClassFlowTakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
std::vector<string> splitted;
|
||||
|
||||
@@ -77,21 +77,21 @@ bool ClassFlowMakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||
return false;
|
||||
|
||||
if (aktparamgraph.compare("[MakeImage]") != 0) // Paragraph does not fit MakeImage
|
||||
if (aktparamgraph.compare("[TakeImage]") != 0) // Paragraph does not fit TakeImage
|
||||
return false;
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
splitted = ZerlegeZeile(aktparamgraph);
|
||||
if ((splitted[0] == "LogImageLocation") && (splitted.size() > 1))
|
||||
if ((toUpper(splitted[0]) == "RAWIMAGESLOCATION") && (splitted.size() > 1))
|
||||
{
|
||||
LogImageLocation = "/sdcard" + splitted[1];
|
||||
imagesLocation = "/sdcard" + splitted[1];
|
||||
isLogImage = true;
|
||||
}
|
||||
if ((splitted[0] == "ImageQuality") && (splitted.size() > 1))
|
||||
if ((toUpper(splitted[0]) == "IMAGEQUALITY") && (splitted.size() > 1))
|
||||
ImageQuality = std::stod(splitted[1]);
|
||||
|
||||
if ((splitted[0] == "ImageSize") && (splitted.size() > 1))
|
||||
if ((toUpper(splitted[0]) == "IMAGESIZE") && (splitted.size() > 1))
|
||||
{
|
||||
ImageSize = Camera.TextToFramesize(splitted[1].c_str());
|
||||
isImageSize = true;
|
||||
@@ -108,9 +108,9 @@ bool ClassFlowMakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
waitbeforepicture = stoi(splitted[1]);
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "LOGFILERETENTIONINDAYS") && (splitted.size() > 1))
|
||||
if ((toUpper(splitted[0]) == "RAWIMAGESRETENTION") && (splitted.size() > 1))
|
||||
{
|
||||
this->logfileRetentionInDays = std::stoi(splitted[1]);
|
||||
this->imagesRetention = std::stoi(splitted[1]);
|
||||
}
|
||||
|
||||
if ((toUpper(splitted[0]) == "BRIGHTNESS") && (splitted.size() > 1))
|
||||
@@ -154,7 +154,7 @@ bool ClassFlowMakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
|
||||
image_width = Camera.image_width;
|
||||
image_height = Camera.image_height;
|
||||
rawImage = new CImageBasis();
|
||||
rawImage = new CImageBasis("rawImage");
|
||||
rawImage->CreateEmptyImage(image_width, image_height, 3);
|
||||
|
||||
waitbeforepicture_store = waitbeforepicture;
|
||||
@@ -173,7 +173,7 @@ bool ClassFlowMakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowMakeImage::getHTMLSingleStep(string host)
|
||||
string ClassFlowTakeImage::getHTMLSingleStep(string host)
|
||||
{
|
||||
string result;
|
||||
result = "Raw Image: <br>\n<img src=\"" + host + "/img_tmp/raw.jpg\">\n";
|
||||
@@ -181,14 +181,14 @@ string ClassFlowMakeImage::getHTMLSingleStep(string host)
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowMakeImage::doFlow(string zwtime)
|
||||
bool ClassFlowTakeImage::doFlow(string zwtime)
|
||||
{
|
||||
string logPath = CreateLogFolder(zwtime);
|
||||
|
||||
int flash_duration = (int) (waitbeforepicture * 1000);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowMakeImage::doFlow - Before takePictureWithFlash");
|
||||
LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - Before takePictureWithFlash");
|
||||
#endif
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ bool ClassFlowMakeImage::doFlow(string zwtime)
|
||||
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowMakeImage::doFlow - After takePictureWithFlash");
|
||||
LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - After takePictureWithFlash");
|
||||
#endif
|
||||
|
||||
LogImage(logPath, "raw", NULL, NULL, zwtime, rawImage);
|
||||
@@ -212,14 +212,14 @@ bool ClassFlowMakeImage::doFlow(string zwtime)
|
||||
RemoveOldLogs();
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowMakeImage::doFlow - After RemoveOldLogs");
|
||||
LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - After RemoveOldLogs");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t ClassFlowMakeImage::SendRawJPG(httpd_req_t *req)
|
||||
esp_err_t ClassFlowTakeImage::SendRawJPG(httpd_req_t *req)
|
||||
{
|
||||
int flash_duration = (int) (waitbeforepicture * 1000);
|
||||
time(&TimeImageTaken);
|
||||
@@ -229,9 +229,9 @@ esp_err_t ClassFlowMakeImage::SendRawJPG(httpd_req_t *req)
|
||||
}
|
||||
|
||||
|
||||
ImageData* ClassFlowMakeImage::SendRawImage()
|
||||
ImageData* ClassFlowTakeImage::SendRawImage()
|
||||
{
|
||||
CImageBasis *zw = new CImageBasis(rawImage);
|
||||
CImageBasis *zw = new CImageBasis("SendRawImage", rawImage);
|
||||
ImageData *id;
|
||||
int flash_duration = (int) (waitbeforepicture * 1000);
|
||||
Camera.CaptureToBasisImage(zw, flash_duration);
|
||||
@@ -243,12 +243,12 @@ ImageData* ClassFlowMakeImage::SendRawImage()
|
||||
return id;
|
||||
}
|
||||
|
||||
time_t ClassFlowMakeImage::getTimeImageTaken()
|
||||
time_t ClassFlowTakeImage::getTimeImageTaken()
|
||||
{
|
||||
return TimeImageTaken;
|
||||
}
|
||||
|
||||
ClassFlowMakeImage::~ClassFlowMakeImage(void)
|
||||
ClassFlowTakeImage::~ClassFlowTakeImage(void)
|
||||
{
|
||||
delete rawImage;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef CLASSFFLOWMAKEIMAGE_H
|
||||
#define CLASSFFLOWMAKEIMAGE_H
|
||||
#ifndef CLASSFFLOWTAKEIMAGE_H
|
||||
#define CLASSFFLOWTAKEIMAGE_H
|
||||
|
||||
#include "ClassFlowImage.h"
|
||||
#include "ClassControllCamera.h"
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
class ClassFlowMakeImage :
|
||||
class ClassFlowTakeImage :
|
||||
public ClassFlowImage
|
||||
{
|
||||
protected:
|
||||
@@ -37,19 +37,19 @@ protected:
|
||||
public:
|
||||
CImageBasis *rawImage;
|
||||
|
||||
ClassFlowMakeImage(std::vector<ClassFlow*>* lfc);
|
||||
ClassFlowTakeImage(std::vector<ClassFlow*>* lfc);
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string getHTMLSingleStep(string host);
|
||||
time_t getTimeImageTaken();
|
||||
string name(){return "ClassFlowMakeImage";};
|
||||
string name(){return "ClassFlowTakeImage";};
|
||||
|
||||
ImageData* SendRawImage();
|
||||
esp_err_t SendRawJPG(httpd_req_t *req);
|
||||
|
||||
~ClassFlowMakeImage(void);
|
||||
~ClassFlowTakeImage(void);
|
||||
};
|
||||
|
||||
|
||||
#endif //CLASSFFLOWMAKEIMAGE_H
|
||||
#endif //CLASSFFLOWTAKEIMAGE_H
|
||||
@@ -64,14 +64,14 @@ string getESPHeapInfo(){
|
||||
|
||||
sprintf(aMsgBuf," | SPI Free: %ld", (long) aFreeSPIHeapSize);
|
||||
espInfoResultStr += string(aMsgBuf);
|
||||
sprintf(aMsgBuf," | SPI Larg Block: %ld", (long) aHeapLargestFreeBlockSize);
|
||||
sprintf(aMsgBuf," | SPI Large Block: %ld", (long) aHeapLargestFreeBlockSize);
|
||||
espInfoResultStr += string(aMsgBuf);
|
||||
sprintf(aMsgBuf," | SPI Min Free: %ld", (long) aMinFreeHeapSize);
|
||||
espInfoResultStr += string(aMsgBuf);
|
||||
|
||||
sprintf(aMsgBuf," | Int Free: %ld", (long) (aFreeInternalHeapSize));
|
||||
espInfoResultStr += string(aMsgBuf);
|
||||
sprintf(aMsgBuf," | Int Larg Block: %ld", (long) aHeapIntLargestFreeBlockSize);
|
||||
sprintf(aMsgBuf," | Int Large Block: %ld", (long) aHeapIntLargestFreeBlockSize);
|
||||
espInfoResultStr += string(aMsgBuf);
|
||||
sprintf(aMsgBuf," | Int Min Free: %ld", (long) (aMinFreeInternalHeapSize));
|
||||
espInfoResultStr += string(aMsgBuf);
|
||||
@@ -224,15 +224,50 @@ void FindReplace(std::string& line, std::string& oldString, std::string& newStri
|
||||
}
|
||||
|
||||
|
||||
bool MakeDir(std::string _what)
|
||||
/**
|
||||
* Create a folder and its parent folders as needed
|
||||
*/
|
||||
bool MakeDir(std::string path)
|
||||
{
|
||||
int mk_ret = mkdir(_what.c_str(), 0775);
|
||||
if (mk_ret)
|
||||
{
|
||||
ESP_LOGD(TAG, "error with mkdir %s ret %d", _what.c_str(), mk_ret);
|
||||
return false;
|
||||
std::string parent;
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Creating folder " + path + "...");
|
||||
|
||||
bool bSuccess = false;
|
||||
int nRC = ::mkdir( path.c_str(), 0775 );
|
||||
if( nRC == -1 )
|
||||
{
|
||||
switch( errno ) {
|
||||
case ENOENT:
|
||||
//parent didn't exist, try to create it
|
||||
parent = path.substr(0, path.find_last_of('/'));
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Need to create parent folder first: " + parent);
|
||||
if(MakeDir(parent)) {
|
||||
//Now, try to create again.
|
||||
bSuccess = 0 == ::mkdir( path.c_str(), 0775 );
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create parent folder: " + parent);
|
||||
bSuccess = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case EEXIST:
|
||||
//Done!
|
||||
bSuccess = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create folder: " + path + " (errno: " + std::to_string(errno) + ")");
|
||||
bSuccess = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
bSuccess = true;
|
||||
}
|
||||
return true;
|
||||
|
||||
return bSuccess;
|
||||
}
|
||||
|
||||
|
||||
@@ -576,9 +611,6 @@ std::vector<string> HelperZerlegeZeile(std::string input, std::string _delimiter
|
||||
std::vector<string> ZerlegeZeile(std::string input, std::string delimiter)
|
||||
{
|
||||
std::vector<string> Output;
|
||||
|
||||
input = trim(input, delimiter);
|
||||
|
||||
/* The input can have multiple formats:
|
||||
* - key = value
|
||||
* - key = value1 value2 value3 ...
|
||||
@@ -593,12 +625,13 @@ std::vector<string> ZerlegeZeile(std::string input, std::string delimiter)
|
||||
* As a workaround and to not break any legacy usage, we enforce to only use the
|
||||
* equal sign, if the key is "password"
|
||||
*/
|
||||
if (input.find("password") != string::npos) { // Line contains a password, use the equal sign as the only delimiter and only split on first occurrence
|
||||
if ((input.find("password") != string::npos) || (input.find("Token") != string::npos)) { // Line contains a password, use the equal sign as the only delimiter and only split on first occurrence
|
||||
size_t pos = input.find("=");
|
||||
Output.push_back(trim(input.substr(0, pos), ""));
|
||||
Output.push_back(trim(input.substr(pos +1, string::npos), ""));
|
||||
}
|
||||
else { // Legacy Mode
|
||||
input = trim(input, delimiter); // sonst werden delimiter am Ende (z.B. == im Token) gelöscht)
|
||||
size_t pos = findDelimiterPos(input, delimiter);
|
||||
std::string token;
|
||||
while (pos != std::string::npos) {
|
||||
@@ -900,3 +933,64 @@ const char* get404(void) {
|
||||
" You could try your <a href=index.html target=_parent>luck</a> here!</pre>\n"
|
||||
"<script>document.cookie = \"page=overview.html\"</script>"; // Make sure we load the overview page
|
||||
}
|
||||
|
||||
|
||||
std::string UrlDecode(const std::string& value)
|
||||
{
|
||||
std::string result;
|
||||
result.reserve(value.size());
|
||||
|
||||
for (std::size_t i = 0; i < value.size(); ++i)
|
||||
{
|
||||
auto ch = value[i];
|
||||
|
||||
if (ch == '%' && (i + 2) < value.size())
|
||||
{
|
||||
auto hex = value.substr(i + 1, 2);
|
||||
auto dec = static_cast<char>(std::strtol(hex.c_str(), nullptr, 16));
|
||||
result.push_back(dec);
|
||||
i += 2;
|
||||
}
|
||||
else if (ch == '+')
|
||||
{
|
||||
result.push_back(' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back(ch);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool replaceString(std::string& s, std::string const& toReplace, std::string const& replaceWith) {
|
||||
return replaceString(s, toReplace, replaceWith, true);
|
||||
}
|
||||
|
||||
|
||||
bool replaceString(std::string& s, std::string const& toReplace, std::string const& replaceWith, bool logIt) {
|
||||
std::size_t pos = s.find(toReplace);
|
||||
|
||||
if (pos == std::string::npos) { // Not found
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string old = s;
|
||||
s.replace(pos, toReplace.length(), replaceWith);
|
||||
if (logIt) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Migrated Configfile line '" + old + "' to '" + s + "'");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool isInString(std::string& s, std::string const& toFind) {
|
||||
std::size_t pos = s.find(toFind);
|
||||
|
||||
if (pos == std::string::npos) { // Not found
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -76,6 +76,8 @@ enum SystemStatusFlag_t { // One bit per error
|
||||
SYSTEM_STATUS_PSRAM_BAD = 1 << 0, // 1, Critical Error
|
||||
SYSTEM_STATUS_HEAP_TOO_SMALL = 1 << 1, // 2, Critical Error
|
||||
SYSTEM_STATUS_CAM_BAD = 1 << 2, // 4, Critical Error
|
||||
SYSTEM_STATUS_SDCARD_CHECK_BAD = 1 << 3, // 8, Critical Error
|
||||
SYSTEM_STATUS_FOLDER_CHECK_BAD = 1 << 4, // 16, Critical Error
|
||||
|
||||
// Second Byte
|
||||
SYSTEM_STATUS_CAM_FB_BAD = 1 << (0+8), // 8, Flow still might work
|
||||
@@ -93,4 +95,10 @@ std::string getFormatedUptime(bool compact);
|
||||
|
||||
const char* get404(void);
|
||||
|
||||
std::string UrlDecode(const std::string& value);
|
||||
|
||||
bool replaceString(std::string& s, std::string const& toReplace, std::string const& replaceWith);
|
||||
bool replaceString(std::string& s, std::string const& toReplace, std::string const& replaceWith, bool logIt);
|
||||
bool isInString(std::string& s, std::string const& toFind);
|
||||
|
||||
#endif //HELPER_H
|
||||
|
||||
42
code/components/jomjol_helper/psram.cpp
Normal file
42
code/components/jomjol_helper/psram.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "ClassLogFile.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
static const char* TAG = "PSRAM";
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
void *malloc_psram_heap(std::string name, size_t size, uint32_t caps) {
|
||||
void *ptr;
|
||||
|
||||
ptr = heap_caps_malloc(size, caps);
|
||||
if (ptr != NULL) {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocated " + to_string(size) + " bytes in PSRAM for '" + name + "'");
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to allocate " + to_string(size) + " bytes in PSRAM for '" + name + "'!");
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
void *calloc_psram_heap(std::string name, size_t n, size_t size, uint32_t caps) {
|
||||
void *ptr;
|
||||
|
||||
ptr = heap_caps_calloc(n, size, caps);
|
||||
if (ptr != NULL) {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocated " + to_string(size) + " bytes in PSRAM for '" + name + "'");
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to allocate " + to_string(size) + " bytes in PSRAM for '" + name + "'!");
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
void free_psram_heap(std::string name, void *ptr) {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Freeing memory in PSRAM used for '" + name + "'...");
|
||||
heap_caps_free(ptr);
|
||||
}
|
||||
7
code/components/jomjol_helper/psram.h
Normal file
7
code/components/jomjol_helper/psram.h
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
void *malloc_psram_heap(std::string name, size_t size, uint32_t caps);
|
||||
void *calloc_psram_heap(std::string name, size_t n, size_t size, uint32_t caps);
|
||||
|
||||
void free_psram_heap(std::string name, void *ptr);
|
||||
166
code/components/jomjol_helper/sdcard_check.cpp
Normal file
166
code/components/jomjol_helper/sdcard_check.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
#include "sdcard_check.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "esp_rom_crc.h"
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
static const char *TAG = "SDCARD";
|
||||
|
||||
int SDCardCheckRW(void)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Basic R/W check started...");
|
||||
FILE* pFile = NULL;
|
||||
int iCRCMessage = 0;
|
||||
|
||||
pFile = fopen("/sdcard/sdcheck.txt","w");
|
||||
if (pFile == NULL) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Basic R/W check: (E1) No able to open file to write");
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
std::string sMessage = "This message is used for a SD-Card basic check!";
|
||||
iCRCMessage = esp_rom_crc16_le(0, (uint8_t*)sMessage.c_str(), sMessage.length());
|
||||
if (fwrite(sMessage.c_str(), sMessage.length(), 1, pFile) == 0 ) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Basic R/W check: (E2) Not able to write file");
|
||||
fclose(pFile);
|
||||
unlink("/sdcard/sdcheck.txt");
|
||||
return -2;
|
||||
}
|
||||
fclose(pFile);
|
||||
}
|
||||
|
||||
pFile = fopen("/sdcard/sdcheck.txt","r");
|
||||
if (pFile == NULL) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Basic R/W check: (E3) Not able to open file to read back");
|
||||
unlink("/sdcard/sdcheck.txt");
|
||||
return -3;
|
||||
}
|
||||
else {
|
||||
char cReadBuf[50];
|
||||
if (fgets(cReadBuf, sizeof(cReadBuf), pFile) == 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Basic R/W check: (E4) Not able to read file back");
|
||||
fclose(pFile);
|
||||
unlink("/sdcard/sdcheck.txt");
|
||||
return -4;
|
||||
}
|
||||
else {
|
||||
if (esp_rom_crc16_le(0, (uint8_t*)cReadBuf, strlen(cReadBuf)) != iCRCMessage) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Basic R/W check: (E5) Read back, but wrong CRC");
|
||||
fclose(pFile);
|
||||
unlink("/sdcard/sdcheck.txt");
|
||||
return -5;
|
||||
}
|
||||
}
|
||||
fclose(pFile);
|
||||
}
|
||||
|
||||
if (unlink("/sdcard/sdcheck.txt") != 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Basic R/W check: (E6) Unable to delete the file");
|
||||
return -6;
|
||||
}
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Basic R/W check successful");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool SDCardCheckFolderFilePresence()
|
||||
{
|
||||
struct stat sb;
|
||||
bool bRetval = true;
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Folder/file presence check started...");
|
||||
/* check if folder exists: config */
|
||||
if (stat("/sdcard/config", &sb) != 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: Folder /config not found");
|
||||
bRetval = false;
|
||||
}
|
||||
|
||||
/* check if folder exists: html */
|
||||
if (stat("/sdcard/html", &sb) != 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: Folder /html not found");
|
||||
bRetval = false;
|
||||
}
|
||||
|
||||
/* check if folder exists: firmware */
|
||||
if (stat("/sdcard/firmware", &sb) != 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: Folder /firmware not found");
|
||||
bRetval = false;
|
||||
}
|
||||
|
||||
/* check if folder exists: img_tmp */
|
||||
if (stat("/sdcard/img_tmp", &sb) != 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: Folder /img_tmp not found");
|
||||
bRetval = false;
|
||||
}
|
||||
|
||||
/* check if folder exists: log */
|
||||
if (stat("/sdcard/log", &sb) != 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: Folder /log not found");
|
||||
bRetval = false;
|
||||
}
|
||||
|
||||
/* check if folder exists: demo */
|
||||
if (stat("/sdcard/demo", &sb) != 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: Folder /demo not found");
|
||||
bRetval = false;
|
||||
}
|
||||
|
||||
/* check if file exists: wlan.ini */
|
||||
if (stat("/sdcard/wlan.ini", &sb) != 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: File /wlan.ini not found");
|
||||
bRetval = false;
|
||||
}
|
||||
|
||||
/* check if file exists: config.ini */
|
||||
if (stat("/sdcard/config/config.ini", &sb) != 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: File /config/config.ini not found");
|
||||
bRetval = false;
|
||||
}
|
||||
|
||||
/* check if file exists: index.html */
|
||||
if (stat("/sdcard/html/index.html", &sb) != 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: File /html/index.html not found");
|
||||
bRetval = false;
|
||||
}
|
||||
|
||||
/* check if file exists: ota.html */
|
||||
if (stat("/sdcard/html/ota_page.html", &sb) != 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: File /html/ota.html not found");
|
||||
bRetval = false;
|
||||
}
|
||||
|
||||
/* check if file exists: log.html */
|
||||
if (stat("/sdcard/html/log.html", &sb) != 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: File /html/log.html not found");
|
||||
bRetval = false;
|
||||
}
|
||||
|
||||
/* check if file exists: common.js */
|
||||
if (stat("/sdcard/html/common.js", &sb) != 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: File /html/common.js not found");
|
||||
bRetval = false;
|
||||
}
|
||||
|
||||
/* check if file exists: gethost.js */
|
||||
if (stat("/sdcard/html/gethost.js", &sb) != 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: File /html/gethost.js not found");
|
||||
bRetval = false;
|
||||
}
|
||||
|
||||
/* check if file exists: version.txt */
|
||||
if (stat("/sdcard/html/version.txt", &sb) != 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Folder/file check: File /html/version.txt not found");
|
||||
bRetval = false;
|
||||
}
|
||||
|
||||
if (bRetval)
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Folder/file presence check successful");
|
||||
|
||||
return bRetval;
|
||||
}
|
||||
11
code/components/jomjol_helper/sdcard_check.h
Normal file
11
code/components/jomjol_helper/sdcard_check.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef COMPONENTS_HELPER_SDCARD_CHECK_H
|
||||
#define COMPONENTS_HELPER_SDCARD_CHECK_H
|
||||
|
||||
#include "../../include/defines.h"
|
||||
|
||||
int SDCardCheckRW(void);
|
||||
bool SDCardCheckFolderFilePresence(void);
|
||||
|
||||
#endif /* COMPONENTS_HELPER_SDCARD_CHECK_H */
|
||||
148
code/components/jomjol_helper/statusled.cpp
Normal file
148
code/components/jomjol_helper/statusled.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
#include "statusled.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
|
||||
static const char* TAG = "STATUSLED";
|
||||
|
||||
TaskHandle_t xHandle_task_StatusLED = NULL;
|
||||
struct StatusLEDData StatusLEDData = {};
|
||||
|
||||
|
||||
void task_StatusLED(void *pvParameter)
|
||||
{
|
||||
//ESP_LOGD(TAG, "task_StatusLED - create");
|
||||
while (StatusLEDData.bProcessingRequest)
|
||||
{
|
||||
//ESP_LOGD(TAG, "task_StatusLED - start");
|
||||
struct StatusLEDData StatusLEDDataInt = StatusLEDData;
|
||||
|
||||
gpio_pad_select_gpio(BLINK_GPIO); // Init the GPIO
|
||||
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT); // Set the GPIO as a push/pull output
|
||||
gpio_set_level(BLINK_GPIO, 1);// LED off
|
||||
|
||||
for (int i=0; i<2; ) // Default: repeat 2 times
|
||||
{
|
||||
if (!StatusLEDDataInt.bInfinite)
|
||||
++i;
|
||||
|
||||
for (int j = 0; j < StatusLEDDataInt.iSourceBlinkCnt; ++j)
|
||||
{
|
||||
gpio_set_level(BLINK_GPIO, 0);
|
||||
vTaskDelay(StatusLEDDataInt.iBlinkTime / portTICK_PERIOD_MS);
|
||||
gpio_set_level(BLINK_GPIO, 1);
|
||||
vTaskDelay(StatusLEDDataInt.iBlinkTime / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
vTaskDelay(500 / portTICK_PERIOD_MS); // Delay between module code and error code
|
||||
|
||||
for (int j = 0; j < StatusLEDDataInt.iCodeBlinkCnt; ++j)
|
||||
{
|
||||
gpio_set_level(BLINK_GPIO, 0);
|
||||
vTaskDelay(StatusLEDDataInt.iBlinkTime / portTICK_PERIOD_MS);
|
||||
gpio_set_level(BLINK_GPIO, 1);
|
||||
vTaskDelay(StatusLEDDataInt.iBlinkTime / portTICK_PERIOD_MS);
|
||||
}
|
||||
vTaskDelay(1500 / portTICK_PERIOD_MS); // Delay to signal new round
|
||||
}
|
||||
|
||||
StatusLEDData.bProcessingRequest = false;
|
||||
//ESP_LOGD(TAG, "task_StatusLED - done/wait");
|
||||
vTaskDelay(10000 / portTICK_PERIOD_MS); // Wait for an upcoming request otherwise continue and delete task to save memory
|
||||
}
|
||||
//ESP_LOGD(TAG, "task_StatusLED - delete");
|
||||
xHandle_task_StatusLED = NULL;
|
||||
vTaskDelete(NULL); // Delete this task due to no request
|
||||
}
|
||||
|
||||
|
||||
void StatusLED(StatusLedSource _eSource, int _iCode, bool _bInfinite)
|
||||
{
|
||||
//ESP_LOGD(TAG, "StatusLED - start");
|
||||
|
||||
if (_eSource == WLAN_CONN) {
|
||||
StatusLEDData.iSourceBlinkCnt = WLAN_CONN;
|
||||
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||
StatusLEDData.iBlinkTime = 250;
|
||||
StatusLEDData.bInfinite = _bInfinite;
|
||||
}
|
||||
else if (_eSource == WLAN_INIT) {
|
||||
StatusLEDData.iSourceBlinkCnt = WLAN_INIT;
|
||||
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||
StatusLEDData.iBlinkTime = 250;
|
||||
StatusLEDData.bInfinite = _bInfinite;
|
||||
}
|
||||
else if (_eSource == SDCARD_INIT) {
|
||||
StatusLEDData.iSourceBlinkCnt = SDCARD_INIT;
|
||||
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||
StatusLEDData.iBlinkTime = 250;
|
||||
StatusLEDData.bInfinite = _bInfinite;
|
||||
}
|
||||
else if (_eSource == SDCARD_CHECK) {
|
||||
StatusLEDData.iSourceBlinkCnt = SDCARD_CHECK;
|
||||
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||
StatusLEDData.iBlinkTime = 250;
|
||||
StatusLEDData.bInfinite = _bInfinite;
|
||||
}
|
||||
else if (_eSource == CAM_INIT) {
|
||||
StatusLEDData.iSourceBlinkCnt = CAM_INIT;
|
||||
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||
StatusLEDData.iBlinkTime = 250;
|
||||
StatusLEDData.bInfinite = _bInfinite;
|
||||
}
|
||||
else if (_eSource == PSRAM_INIT) {
|
||||
StatusLEDData.iSourceBlinkCnt = PSRAM_INIT;
|
||||
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||
StatusLEDData.iBlinkTime = 250;
|
||||
StatusLEDData.bInfinite = _bInfinite;
|
||||
}
|
||||
else if (_eSource == TIME_CHECK) {
|
||||
StatusLEDData.iSourceBlinkCnt = TIME_CHECK;
|
||||
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||
StatusLEDData.iBlinkTime = 250;
|
||||
StatusLEDData.bInfinite = _bInfinite;
|
||||
}
|
||||
else if (_eSource == AP_OR_OTA) {
|
||||
StatusLEDData.iSourceBlinkCnt = AP_OR_OTA;
|
||||
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||
StatusLEDData.iBlinkTime = 350;
|
||||
StatusLEDData.bInfinite = _bInfinite;
|
||||
}
|
||||
|
||||
if (xHandle_task_StatusLED && !StatusLEDData.bProcessingRequest) {
|
||||
StatusLEDData.bProcessingRequest = true;
|
||||
BaseType_t xReturned = xTaskAbortDelay(xHandle_task_StatusLED); // Reuse still running status LED task
|
||||
/*if (xReturned == pdPASS)
|
||||
ESP_LOGD(TAG, "task_StatusLED - abort waiting delay");*/
|
||||
}
|
||||
else if (xHandle_task_StatusLED == NULL) {
|
||||
StatusLEDData.bProcessingRequest = true;
|
||||
BaseType_t xReturned = xTaskCreate(&task_StatusLED, "task_StatusLED", 1280, NULL, tskIDLE_PRIORITY+1, &xHandle_task_StatusLED);
|
||||
if(xReturned != pdPASS)
|
||||
{
|
||||
xHandle_task_StatusLED = NULL;
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "task_StatusLED failed to create");
|
||||
LogFile.WriteHeapInfo("task_StatusLED failed");
|
||||
}
|
||||
}
|
||||
else {
|
||||
ESP_LOGD(TAG, "task_StatusLED still processing, request skipped"); // Requests with high frequency could be skipped, but LED is only helpful for static states
|
||||
}
|
||||
//ESP_LOGD(TAG, "StatusLED - done");
|
||||
}
|
||||
|
||||
|
||||
void StatusLEDOff(void)
|
||||
{
|
||||
if (xHandle_task_StatusLED)
|
||||
vTaskDelete(xHandle_task_StatusLED); // Delete task for StatusLED to force stop of blinking
|
||||
|
||||
gpio_pad_select_gpio(BLINK_GPIO); // Init the GPIO
|
||||
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT); // Set the GPIO as a push/pull output
|
||||
gpio_set_level(BLINK_GPIO, 1);// LED off
|
||||
}
|
||||
34
code/components/jomjol_helper/statusled.h
Normal file
34
code/components/jomjol_helper/statusled.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef STATUSLED_H
|
||||
#define STATUSLED_H
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
|
||||
extern TaskHandle_t xHandle_task_StatusLED;
|
||||
|
||||
enum StatusLedSource {
|
||||
WLAN_CONN = 1,
|
||||
WLAN_INIT = 2,
|
||||
SDCARD_INIT = 3,
|
||||
SDCARD_CHECK = 4,
|
||||
CAM_INIT = 5,
|
||||
PSRAM_INIT = 6,
|
||||
TIME_CHECK = 7,
|
||||
AP_OR_OTA = 8
|
||||
};
|
||||
|
||||
struct StatusLEDData {
|
||||
int iSourceBlinkCnt = 1;
|
||||
int iCodeBlinkCnt = 1;
|
||||
int iBlinkTime = 250;
|
||||
bool bInfinite = false;
|
||||
bool bProcessingRequest = false;
|
||||
};
|
||||
|
||||
void StatusLED(StatusLedSource _eSource, int _iCode, bool _bInfinite);
|
||||
void StatusLEDOff(void);
|
||||
|
||||
#endif //STATUSLED_H
|
||||
@@ -5,12 +5,14 @@
|
||||
#include <math.h>
|
||||
#include <algorithm>
|
||||
#include <esp_log.h>
|
||||
#include "psram.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char* TAG = "c_align_and_cut_image";
|
||||
|
||||
CAlignAndCutImage::CAlignAndCutImage(CImageBasis *_org, CImageBasis *_temp)
|
||||
CAlignAndCutImage::CAlignAndCutImage(std::string _name, CImageBasis *_org, CImageBasis *_temp) : CImageBasis(_name)
|
||||
{
|
||||
name = _name;
|
||||
rgb_image = _org->rgb_image;
|
||||
channels = _org->channels;
|
||||
width = _org->width;
|
||||
@@ -37,7 +39,7 @@ bool CAlignAndCutImage::Align(RefInfo *_temp1, RefInfo *_temp2)
|
||||
int r0_x, r0_y, r1_x, r1_y;
|
||||
bool isSimilar1, isSimilar2;
|
||||
|
||||
CFindTemplate* ft = new CFindTemplate(rgb_image, channels, width, height, bpp);
|
||||
CFindTemplate* ft = new CFindTemplate("align", rgb_image, channels, width, height, bpp);
|
||||
|
||||
r0_x = _temp1->target_x;
|
||||
r0_y = _temp1->target_y;
|
||||
@@ -81,7 +83,7 @@ bool CAlignAndCutImage::Align(RefInfo *_temp1, RefInfo *_temp2)
|
||||
LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", zw);
|
||||
#endif*/
|
||||
|
||||
CRotateImage rt(this, ImageTMP);
|
||||
CRotateImage rt("Align", this, ImageTMP);
|
||||
rt.Translate(dx, dy);
|
||||
rt.Rotate(d_winkel, _temp1->target_x, _temp1->target_y);
|
||||
ESP_LOGD(TAG, "Alignment: dx %d - dy %d - rot %f", dx, dy, d_winkel);
|
||||
@@ -107,7 +109,7 @@ void CAlignAndCutImage::CutAndSave(std::string _template1, int x1, int y1, int d
|
||||
dy = y2 - y1;
|
||||
|
||||
int memsize = dx * dy * channels;
|
||||
uint8_t* odata = (unsigned char*) GET_MEMORY(memsize);
|
||||
uint8_t* odata = (unsigned char*) malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||
|
||||
stbi_uc* p_target;
|
||||
stbi_uc* p_source;
|
||||
@@ -186,7 +188,7 @@ CImageBasis* CAlignAndCutImage::CutAndSave(int x1, int y1, int dx, int dy)
|
||||
dy = y2 - y1;
|
||||
|
||||
int memsize = dx * dy * channels;
|
||||
uint8_t* odata = (unsigned char*)GET_MEMORY(memsize);
|
||||
uint8_t* odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||
|
||||
stbi_uc* p_target;
|
||||
stbi_uc* p_source;
|
||||
@@ -202,7 +204,7 @@ CImageBasis* CAlignAndCutImage::CutAndSave(int x1, int y1, int dx, int dy)
|
||||
p_target[_channels] = p_source[_channels];
|
||||
}
|
||||
|
||||
CImageBasis* rs = new CImageBasis(odata, channels, dx, dy, bpp);
|
||||
CImageBasis* rs = new CImageBasis("CutAndSave", odata, channels, dx, dy, bpp);
|
||||
RGBImageRelease();
|
||||
rs->SetIndepended();
|
||||
return rs;
|
||||
|
||||
@@ -12,9 +12,9 @@ class CAlignAndCutImage : public CImageBasis
|
||||
public:
|
||||
int t0_dx, t0_dy, t1_dx, t1_dy;
|
||||
CImageBasis *ImageTMP;
|
||||
CAlignAndCutImage(std::string _image) : CImageBasis(_image) {ImageTMP = NULL;};
|
||||
CAlignAndCutImage(uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp) : CImageBasis(_rgb_image, _channels, _width, _height, _bpp) {ImageTMP = NULL;};
|
||||
CAlignAndCutImage(CImageBasis *_org, CImageBasis *_temp);
|
||||
CAlignAndCutImage(std::string name, std::string _image) : CImageBasis(name, _image) {ImageTMP = NULL;};
|
||||
CAlignAndCutImage(std::string name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp) {ImageTMP = NULL;};
|
||||
CAlignAndCutImage(std::string name, CImageBasis *_org, CImageBasis *_temp);
|
||||
|
||||
bool Align(RefInfo *_temp1, RefInfo *_temp2);
|
||||
// void Align(std::string _template1, int x1, int y1, std::string _template2, int x2, int y2, int deltax = 40, int deltay = 40, std::string imageROI = "");
|
||||
|
||||
@@ -32,7 +32,7 @@ class CFindTemplate : public CImageBasis
|
||||
{
|
||||
public:
|
||||
int tpl_width, tpl_height, tpl_bpp;
|
||||
CFindTemplate(uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp) : CImageBasis(_rgb_image, _channels, _width, _height, _bpp) {};
|
||||
CFindTemplate(std::string name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp) {};
|
||||
|
||||
bool FindTemplate(RefInfo *_ref);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "CImageBasis.h"
|
||||
#include "Helper.h"
|
||||
#include "psram.h"
|
||||
#include "ClassLogFile.h"
|
||||
#include "server_ota.h"
|
||||
|
||||
@@ -360,8 +361,9 @@ void CImageBasis::drawCircle(int x1, int y1, int rad, int r, int g, int b, int t
|
||||
}
|
||||
|
||||
|
||||
CImageBasis::CImageBasis()
|
||||
CImageBasis::CImageBasis(string _name)
|
||||
{
|
||||
name = _name;
|
||||
externalImage = false;
|
||||
rgb_image = NULL;
|
||||
width = 0;
|
||||
@@ -384,8 +386,9 @@ void CImageBasis::CreateEmptyImage(int _width, int _height, int _channels)
|
||||
LogFile.WriteHeapInfo("CreateEmptyImage");
|
||||
#endif
|
||||
|
||||
int memsize = width * height * channels;
|
||||
rgb_image = (unsigned char*)GET_MEMORY(memsize);
|
||||
memsize = width * height * channels;
|
||||
|
||||
rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
|
||||
|
||||
if (rgb_image == NULL)
|
||||
{
|
||||
@@ -435,8 +438,10 @@ void CImageBasis::LoadFromMemory(stbi_uc *_buffer, int len)
|
||||
{
|
||||
RGBImageLock();
|
||||
|
||||
if (rgb_image)
|
||||
if (rgb_image != NULL) {
|
||||
stbi_image_free(rgb_image);
|
||||
//free_psram_heap(std::string(TAG) + "->rgb_image (LoadFromMemory)", rgb_image);
|
||||
}
|
||||
|
||||
rgb_image = stbi_load_from_memory(_buffer, len, &width, &height, &channels, 3);
|
||||
bpp = channels;
|
||||
@@ -454,8 +459,9 @@ void CImageBasis::LoadFromMemory(stbi_uc *_buffer, int len)
|
||||
}
|
||||
|
||||
|
||||
CImageBasis::CImageBasis(CImageBasis *_copyfrom)
|
||||
CImageBasis::CImageBasis(string _name, CImageBasis *_copyfrom)
|
||||
{
|
||||
name = _name;
|
||||
islocked = false;
|
||||
externalImage = false;
|
||||
channels = _copyfrom->channels;
|
||||
@@ -469,8 +475,9 @@ CImageBasis::CImageBasis(CImageBasis *_copyfrom)
|
||||
LogFile.WriteHeapInfo("CImageBasis_copyfrom - Start");
|
||||
#endif
|
||||
|
||||
int memsize = width * height * channels;
|
||||
rgb_image = (unsigned char*)GET_MEMORY(memsize);
|
||||
memsize = width * height * channels;
|
||||
|
||||
rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
|
||||
|
||||
if (rgb_image == NULL)
|
||||
{
|
||||
@@ -489,8 +496,9 @@ CImageBasis::CImageBasis(CImageBasis *_copyfrom)
|
||||
}
|
||||
|
||||
|
||||
CImageBasis::CImageBasis(int _width, int _height, int _channels)
|
||||
CImageBasis::CImageBasis(string _name, int _width, int _height, int _channels)
|
||||
{
|
||||
name = _name;
|
||||
islocked = false;
|
||||
externalImage = false;
|
||||
channels = _channels;
|
||||
@@ -504,8 +512,9 @@ CImageBasis::CImageBasis(int _width, int _height, int _channels)
|
||||
LogFile.WriteHeapInfo("CImageBasis_width,height,ch - Start");
|
||||
#endif
|
||||
|
||||
int memsize = width * height * channels;
|
||||
rgb_image = (unsigned char*)GET_MEMORY(memsize);
|
||||
memsize = width * height * channels;
|
||||
|
||||
rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
|
||||
|
||||
if (rgb_image == NULL)
|
||||
{
|
||||
@@ -523,8 +532,9 @@ CImageBasis::CImageBasis(int _width, int _height, int _channels)
|
||||
}
|
||||
|
||||
|
||||
CImageBasis::CImageBasis(std::string _image)
|
||||
CImageBasis::CImageBasis(string _name, std::string _image)
|
||||
{
|
||||
name = _name;
|
||||
islocked = false;
|
||||
channels = 3;
|
||||
externalImage = false;
|
||||
@@ -569,8 +579,9 @@ bool CImageBasis::ImageOkay(){
|
||||
}
|
||||
|
||||
|
||||
CImageBasis::CImageBasis(uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp)
|
||||
CImageBasis::CImageBasis(string _name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp)
|
||||
{
|
||||
name = _name;
|
||||
islocked = false;
|
||||
rgb_image = _rgb_image;
|
||||
channels = _channels;
|
||||
@@ -607,8 +618,10 @@ CImageBasis::~CImageBasis()
|
||||
{
|
||||
RGBImageLock();
|
||||
|
||||
if (!externalImage)
|
||||
stbi_image_free(rgb_image);
|
||||
if (!externalImage) {
|
||||
//stbi_image_free(rgb_image);
|
||||
free_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ", " + to_string(memsize) + ")", rgb_image);
|
||||
}
|
||||
|
||||
RGBImageRelease();
|
||||
}
|
||||
@@ -637,19 +650,19 @@ void CImageBasis::SaveToFile(std::string _imageout)
|
||||
|
||||
void CImageBasis::Resize(int _new_dx, int _new_dy)
|
||||
{
|
||||
int memsize = _new_dx * _new_dy * channels;
|
||||
uint8_t* odata = (unsigned char*)GET_MEMORY(memsize);
|
||||
memsize = _new_dx * _new_dy * channels;
|
||||
uint8_t* odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
stbir_resize_uint8(rgb_image, width, height, 0, odata, _new_dx, _new_dy, 0, channels);
|
||||
stbi_image_free(rgb_image);
|
||||
|
||||
rgb_image = (unsigned char*)GET_MEMORY(memsize);
|
||||
rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis Resize (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
|
||||
memCopy(odata, rgb_image, memsize);
|
||||
width = _new_dx;
|
||||
height = _new_dy;
|
||||
stbi_image_free(odata);
|
||||
|
||||
free_psram_heap(std::string(TAG) + "->odata", odata);
|
||||
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ class CImageBasis
|
||||
protected:
|
||||
bool externalImage;
|
||||
std::string filename;
|
||||
std::string name; // Just used for diagnostics
|
||||
int memsize = 0;
|
||||
|
||||
void memCopy(uint8_t* _source, uint8_t* _target, int _size);
|
||||
bool isInImage(int x, int y);
|
||||
@@ -37,7 +39,7 @@ class CImageBasis
|
||||
bool islocked;
|
||||
|
||||
public:
|
||||
uint8_t* rgb_image;
|
||||
uint8_t* rgb_image = NULL;
|
||||
int channels;
|
||||
int width, height, bpp;
|
||||
|
||||
@@ -64,11 +66,11 @@ class CImageBasis
|
||||
void EmptyImage();
|
||||
|
||||
|
||||
CImageBasis();
|
||||
CImageBasis(std::string _image);
|
||||
CImageBasis(uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp);
|
||||
CImageBasis(int _width, int _height, int _channels);
|
||||
CImageBasis(CImageBasis *_copyfrom);
|
||||
CImageBasis(std::string name);
|
||||
CImageBasis(std::string name, std::string _image);
|
||||
CImageBasis(std::string name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp);
|
||||
CImageBasis(std::string name, int _width, int _height, int _channels);
|
||||
CImageBasis(std::string name, CImageBasis *_copyfrom);
|
||||
|
||||
void Resize(int _new_dx, int _new_dy);
|
||||
void Resize(int _new_dx, int _new_dy, CImageBasis *_target);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include <string>
|
||||
#include "CRotateImage.h"
|
||||
#include "psram.h"
|
||||
|
||||
static const char *TAG = "C ROTATE IMG";
|
||||
|
||||
CRotateImage::CRotateImage(CImageBasis *_org, CImageBasis *_temp, bool _flip)
|
||||
CRotateImage::CRotateImage(std::string _name, CImageBasis *_org, CImageBasis *_temp, bool _flip) : CImageBasis(_name)
|
||||
{
|
||||
rgb_image = _org->rgb_image;
|
||||
channels = _org->channels;
|
||||
@@ -15,6 +18,7 @@ CRotateImage::CRotateImage(CImageBasis *_org, CImageBasis *_temp, bool _flip)
|
||||
doflip = _flip;
|
||||
}
|
||||
|
||||
|
||||
void CRotateImage::Mirror(){
|
||||
int memsize = width * height * channels;
|
||||
uint8_t* odata;
|
||||
@@ -24,7 +28,7 @@ void CRotateImage::Mirror(){
|
||||
}
|
||||
else
|
||||
{
|
||||
odata = (unsigned char*)GET_MEMORY(memsize);
|
||||
odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +54,7 @@ void CRotateImage::Mirror(){
|
||||
// memcpy(rgb_image, odata, memsize);
|
||||
memCopy(odata, rgb_image, memsize);
|
||||
if (!ImageTMP)
|
||||
stbi_image_free(odata);
|
||||
free_psram_heap(std::string(TAG) + "->odata", odata);
|
||||
|
||||
if (ImageTMP)
|
||||
ImageTMP->RGBImageRelease();
|
||||
@@ -109,7 +113,7 @@ void CRotateImage::Rotate(float _angle, int _centerx, int _centery)
|
||||
}
|
||||
else
|
||||
{
|
||||
odata = (unsigned char*)GET_MEMORY(memsize);
|
||||
odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
|
||||
@@ -148,7 +152,7 @@ void CRotateImage::Rotate(float _angle, int _centerx, int _centery)
|
||||
|
||||
if (!ImageTMP)
|
||||
{
|
||||
stbi_image_free(odata);
|
||||
free_psram_heap(std::string(TAG) + "->odata", odata);
|
||||
}
|
||||
if (ImageTMP)
|
||||
ImageTMP->RGBImageRelease();
|
||||
@@ -209,7 +213,7 @@ void CRotateImage::RotateAntiAliasing(float _angle, int _centerx, int _centery)
|
||||
}
|
||||
else
|
||||
{
|
||||
odata = (unsigned char*)GET_MEMORY(memsize);
|
||||
odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
|
||||
@@ -269,7 +273,7 @@ void CRotateImage::RotateAntiAliasing(float _angle, int _centerx, int _centery)
|
||||
|
||||
if (!ImageTMP)
|
||||
{
|
||||
stbi_image_free(odata);
|
||||
free_psram_heap(std::string(TAG) + "->odata", odata);
|
||||
}
|
||||
if (ImageTMP)
|
||||
ImageTMP->RGBImageRelease();
|
||||
@@ -300,7 +304,7 @@ void CRotateImage::Translate(int _dx, int _dy)
|
||||
}
|
||||
else
|
||||
{
|
||||
odata = (unsigned char*)GET_MEMORY(memsize);
|
||||
odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
|
||||
@@ -336,7 +340,7 @@ void CRotateImage::Translate(int _dx, int _dy)
|
||||
memCopy(odata, rgb_image, memsize);
|
||||
if (!ImageTMP)
|
||||
{
|
||||
stbi_image_free(odata);
|
||||
free_psram_heap(std::string(TAG) + "->odata", odata);
|
||||
}
|
||||
|
||||
if (ImageTMP)
|
||||
|
||||
@@ -8,12 +8,13 @@
|
||||
|
||||
class CRotateImage: public CImageBasis
|
||||
{
|
||||
|
||||
public:
|
||||
CImageBasis *ImageTMP, *ImageOrg;
|
||||
bool doflip;
|
||||
CRotateImage(std::string _image, bool _flip = false) : CImageBasis(_image) {ImageTMP = NULL; ImageOrg = NULL; doflip = _flip;};
|
||||
CRotateImage(uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp, bool _flip = false) : CImageBasis(_rgb_image, _channels, _width, _height, _bpp) {ImageTMP = NULL; ImageOrg = NULL; doflip = _flip;};
|
||||
CRotateImage(CImageBasis *_org, CImageBasis *_temp, bool _flip = false);
|
||||
CRotateImage(std::string name, std::string _image, bool _flip = false) : CImageBasis(name, _image) {ImageTMP = NULL; ImageOrg = NULL; doflip = _flip;};
|
||||
CRotateImage(std::string name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp, bool _flip = false) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp) {ImageTMP = NULL; ImageOrg = NULL; doflip = _flip;};
|
||||
CRotateImage(std::string name, CImageBasis *_org, CImageBasis *_temp, bool _flip = false);
|
||||
|
||||
void Rotate(float _angle);
|
||||
void RotateAntiAliasing(float _angle);
|
||||
|
||||
214
code/components/jomjol_influxdb/interface_influxdb._c_pp_old
Normal file
214
code/components/jomjol_influxdb/interface_influxdb._c_pp_old
Normal file
@@ -0,0 +1,214 @@
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
#include "interface_influxdb.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include <time.h>
|
||||
#include "ClassLogFile.h"
|
||||
#include "esp_http_client.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
|
||||
static const char *TAG = "INFLUXDB";
|
||||
|
||||
std::string _influxDBURI;
|
||||
std::string _influxDBDatabase;
|
||||
std::string _influxDBMeasurement;
|
||||
std::string _influxDBUser;
|
||||
std::string _influxDBPassword;
|
||||
|
||||
static esp_err_t http_event_handler(esp_http_client_event_t *evt)
|
||||
{
|
||||
switch(evt->event_id)
|
||||
{
|
||||
case HTTP_EVENT_ERROR:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Error encountered");
|
||||
break;
|
||||
case HTTP_EVENT_ON_CONNECTED:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Error encountered");
|
||||
ESP_LOGI(TAG, "HTTP Client Connected");
|
||||
break;
|
||||
case HTTP_EVENT_HEADERS_SENT:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client sent all request headers");
|
||||
break;
|
||||
case HTTP_EVENT_ON_HEADER:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Header: key=" + std::string(evt->header_key) + ", value=" + std::string(evt->header_value));
|
||||
break;
|
||||
case HTTP_EVENT_ON_DATA:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client data recevied: len=" + std::to_string(evt->data_len));
|
||||
break;
|
||||
case HTTP_EVENT_ON_FINISH:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client finished");
|
||||
break;
|
||||
case HTTP_EVENT_DISCONNECTED:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Disconnected");
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void InfluxDBPublish(std::string _key, std::string _content, std::string _timestamp) {
|
||||
char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
|
||||
esp_http_client_config_t http_config = {
|
||||
.user_agent = "ESP32 Meter reader",
|
||||
.method = HTTP_METHOD_POST,
|
||||
.event_handler = http_event_handler,
|
||||
.buffer_size = MAX_HTTP_OUTPUT_BUFFER,
|
||||
.user_data = response_buffer
|
||||
};
|
||||
|
||||
if (_influxDBUser.length() && _influxDBPassword.length()){
|
||||
http_config.username = _influxDBUser.c_str();
|
||||
http_config.password = _influxDBPassword.c_str();
|
||||
http_config.auth_type = HTTP_AUTH_TYPE_BASIC;
|
||||
}
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", Timestamp: " + _timestamp);
|
||||
|
||||
char nowTimestamp[21];
|
||||
std::string payload;
|
||||
|
||||
if (_timestamp.length() > 0)
|
||||
{
|
||||
struct tm tm;
|
||||
strptime(_timestamp.c_str(), PREVALUE_TIME_FORMAT_OUTPUT, &tm);
|
||||
time_t t = mktime(&tm); // Time in Localtime (looks like timezone is not used by strptime)
|
||||
|
||||
struct tm * ptm;
|
||||
ptm = gmtime ( &t );
|
||||
time_t utc = mktime(ptm);
|
||||
utc = 2*t - utc; // Take care of timezone (looks difficult, but is easy: t = t + (t - utc), weil t-utc = timezone)
|
||||
|
||||
sprintf(nowTimestamp,"%ld000000000", (long) utc); // UTC
|
||||
|
||||
payload = _influxDBMeasurement + " " + _key + "=" + _content + " " + nowTimestamp;
|
||||
// payload = _influxDBMeasurement + " " + _key + "=774 " + nowTimestamp;
|
||||
}
|
||||
else
|
||||
{
|
||||
payload = _influxDBMeasurement + " " + _key + "=" + _content;
|
||||
}
|
||||
|
||||
payload.shrink_to_fit();
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload);
|
||||
|
||||
|
||||
// use the default retention policy of the database
|
||||
std::string apiURI = _influxDBURI + "/api/v2/write?bucket=" + _influxDBDatabase + "/";
|
||||
apiURI.shrink_to_fit();
|
||||
http_config.url = apiURI.c_str();
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "API URI: " + apiURI);
|
||||
|
||||
esp_http_client_handle_t http_client = esp_http_client_init(&http_config);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "client is initialized");
|
||||
|
||||
esp_http_client_set_header(http_client, "Content-Type", "text/plain");
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "header is set");
|
||||
|
||||
ESP_ERROR_CHECK(esp_http_client_set_post_field(http_client, payload.c_str(), payload.length()));
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "post payload is set");
|
||||
|
||||
esp_err_t err = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_http_client_perform(http_client));
|
||||
|
||||
if( err == ESP_OK ) {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request was performed");
|
||||
int status_code = esp_http_client_get_status_code(http_client);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP status code" + std::to_string(status_code));
|
||||
} else {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request failed");
|
||||
}
|
||||
esp_http_client_cleanup(http_client);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
void InfluxDBPublish(std::string _key, std::string _content, std::string _timestamp) {
|
||||
char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
|
||||
esp_http_client_config_t http_config = {
|
||||
.user_agent = "ESP32 Meter reader",
|
||||
.method = HTTP_METHOD_POST,
|
||||
.event_handler = http_event_handler,
|
||||
.buffer_size = MAX_HTTP_OUTPUT_BUFFER,
|
||||
.user_data = response_buffer
|
||||
};
|
||||
|
||||
if (_influxDBUser.length() && _influxDBPassword.length()){
|
||||
http_config.username = _influxDBUser.c_str();
|
||||
http_config.password = _influxDBPassword.c_str();
|
||||
http_config.auth_type = HTTP_AUTH_TYPE_BASIC;
|
||||
}
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", Timestamp: " + _timestamp);
|
||||
|
||||
// Format: #define PREVALUE_TIME_FORMAT_OUTPUT "%Y-%m-%dT%H:%M:%S%z"
|
||||
struct tm tm;
|
||||
strptime(_timestamp.c_str(), PREVALUE_TIME_FORMAT_OUTPUT, &tm);
|
||||
time_t t = mktime(&tm); // t is now your desired time_t
|
||||
|
||||
struct tm * ptm;
|
||||
ptm = gmtime ( &t );
|
||||
time_t utc = mktime(ptm);
|
||||
|
||||
// time_t now;
|
||||
// time(&now);
|
||||
char nowTimestamp[21];
|
||||
// pad with zeroes to get nanoseconds
|
||||
// sprintf(nowTimestamp,"%ld000000000", (long) now);
|
||||
// sprintf(nowTimestamp,"%ld000000000", (long) t); // Localtime
|
||||
sprintf(nowTimestamp,"%ld000000000", (long) utc); // UTC
|
||||
|
||||
|
||||
// LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Test Time Conversion - t: " + std::to_string(t) + ", utc: " + std::to_string(utc));
|
||||
// LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Test Time Conversion - now: " + std::to_string(now) + ", timestamp: " + std::to_string(t) + "(correct time not used yet)");
|
||||
|
||||
std::string payload = _influxDBMeasurement + " " + _key + "=" + _content + " " + nowTimestamp;
|
||||
payload.shrink_to_fit();
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload);
|
||||
|
||||
|
||||
// use the default retention policy of the database
|
||||
std::string apiURI = _influxDBURI + "/api/v2/write?bucket=" + _influxDBDatabase + "/";
|
||||
apiURI.shrink_to_fit();
|
||||
http_config.url = apiURI.c_str();
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "API URI: " + apiURI);
|
||||
|
||||
esp_http_client_handle_t http_client = esp_http_client_init(&http_config);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "client is initialized");
|
||||
|
||||
esp_http_client_set_header(http_client, "Content-Type", "text/plain");
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "header is set");
|
||||
|
||||
ESP_ERROR_CHECK(esp_http_client_set_post_field(http_client, payload.c_str(), payload.length()));
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "post payload is set");
|
||||
|
||||
esp_err_t err = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_http_client_perform(http_client));
|
||||
|
||||
if( err == ESP_OK ) {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request was performed");
|
||||
int status_code = esp_http_client_get_status_code(http_client);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP status code" + std::to_string(status_code));
|
||||
} else {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request failed");
|
||||
}
|
||||
esp_http_client_cleanup(http_client);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
void InfluxDBInit(std::string _uri, std::string _database, std::string _measurement, std::string _user, std::string _password){
|
||||
_influxDBURI = _uri;
|
||||
_influxDBDatabase = _database;
|
||||
_influxDBMeasurement = _measurement;
|
||||
_influxDBUser = _user;
|
||||
_influxDBPassword = _password;
|
||||
|
||||
}
|
||||
|
||||
void InfluxDBdestroy() {
|
||||
}
|
||||
|
||||
#endif //ENABLE_INFLUXDB
|
||||
24
code/components/jomjol_influxdb/interface_influxdb._h_old
Normal file
24
code/components/jomjol_influxdb/interface_influxdb._h_old
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
|
||||
#pragma once
|
||||
#ifndef INTERFACE_INFLUXDB_H
|
||||
#define INTERFACE_INFLUXDB_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
|
||||
// Interface to InfluxDB v1.x
|
||||
void InfluxDBInit(std::string _influxDBURI, std::string _database, std::string _measurement, std::string _user, std::string _password);
|
||||
void InfluxDBPublish(std::string _key, std::string _content, std::string _timestamp);
|
||||
|
||||
// Interface to InfluxDB v2.x
|
||||
void InfluxDB_V2_Init(std::string _uri, std::string _database, std::string _measurement, std::string _org, std::string _token);
|
||||
void InfluxDB_V2_Publish(std::string _key, std::string _content, std::string _timestamp);
|
||||
|
||||
|
||||
|
||||
void InfluxDBdestroy();
|
||||
|
||||
#endif //INTERFACE_INFLUXDB_H
|
||||
#endif //ENABLE_INFLUXDB
|
||||
@@ -16,6 +16,101 @@ std::string _influxDBMeasurement;
|
||||
std::string _influxDBUser;
|
||||
std::string _influxDBPassword;
|
||||
|
||||
std::string _influxDB_V2_URI;
|
||||
std::string _influxDB_V2_Database;
|
||||
std::string _influxDB_V2_Measurement;
|
||||
std::string _influxDB_V2_Token;
|
||||
std::string _influxDB_V2_Org;
|
||||
|
||||
static esp_err_t http_event_handler(esp_http_client_event_t *evt);
|
||||
|
||||
void InfluxDB_V2_Init(std::string _uri, std::string _database, std::string _measurement, std::string _org, std::string _token)
|
||||
{
|
||||
_influxDB_V2_URI = _uri;
|
||||
_influxDB_V2_Database = _database;
|
||||
_influxDB_V2_Measurement = _measurement;
|
||||
_influxDB_V2_Org = _org;
|
||||
_influxDB_V2_Token = _token;
|
||||
}
|
||||
|
||||
void InfluxDB_V2_Publish(std::string _key, std::string _content, std::string _timestamp)
|
||||
{
|
||||
char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
|
||||
esp_http_client_config_t http_config = {
|
||||
.user_agent = "ESP32 Meter reader",
|
||||
.method = HTTP_METHOD_POST,
|
||||
.event_handler = http_event_handler,
|
||||
.buffer_size = MAX_HTTP_OUTPUT_BUFFER,
|
||||
.user_data = response_buffer
|
||||
};
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDB_V2_Publish - Key: " + _key + ", Content: " + _content + ", Timestamp: " + _timestamp);
|
||||
|
||||
// Format: #define PREVALUE_TIME_FORMAT_OUTPUT "%Y-%m-%dT%H:%M:%S%z"
|
||||
|
||||
char nowTimestamp[21];
|
||||
std::string payload;
|
||||
|
||||
if (_timestamp.length() > 0)
|
||||
{
|
||||
struct tm tm;
|
||||
strptime(_timestamp.c_str(), PREVALUE_TIME_FORMAT_OUTPUT, &tm);
|
||||
time_t t = mktime(&tm); // Time in Localtime (looks like timezone is not used by strptime)
|
||||
|
||||
struct tm * ptm;
|
||||
ptm = gmtime ( &t );
|
||||
time_t utc = mktime(ptm);
|
||||
utc = 2*t - utc; // Take care of timezone (looks difficult, but is easy: t = t + (t - utc), weil t-utc = timezone)
|
||||
|
||||
sprintf(nowTimestamp,"%ld000000000", (long) utc); // UTC
|
||||
|
||||
payload = _influxDB_V2_Measurement + " " + _key + "=" + _content + " " + nowTimestamp;
|
||||
// payload = _influxDB_V2_Measurement + " " + _key + "=774 " + nowTimestamp;
|
||||
}
|
||||
else
|
||||
{
|
||||
payload = _influxDB_V2_Measurement + " " + _key + "=" + _content;
|
||||
// payload = _influxDB_V2_Measurement + " " + _key + "=774";
|
||||
}
|
||||
|
||||
payload.shrink_to_fit();
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload);
|
||||
|
||||
std::string apiURI = _influxDB_V2_URI + "/api/v2/write?org=" + _influxDB_V2_Org + "&bucket=" + _influxDB_V2_Database;
|
||||
apiURI.shrink_to_fit();
|
||||
http_config.url = apiURI.c_str();
|
||||
ESP_LOGI(TAG, "http_config: %s", http_config.url); // Add mark on log to see when it restarted
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "API URI: " + apiURI);
|
||||
|
||||
esp_http_client_handle_t http_client = esp_http_client_init(&http_config);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "client is initialized");
|
||||
|
||||
esp_http_client_set_header(http_client, "Content-Type", "text/plain");
|
||||
std::string _zw = "Token " + _influxDB_V2_Token;
|
||||
// LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Tokenheader: %s\n", _zw.c_str());
|
||||
esp_http_client_set_header(http_client, "Authorization", _zw.c_str());
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "header is set");
|
||||
|
||||
ESP_ERROR_CHECK(esp_http_client_set_post_field(http_client, payload.c_str(), payload.length()));
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "post payload is set");
|
||||
|
||||
esp_err_t err = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_http_client_perform(http_client));
|
||||
|
||||
if( err == ESP_OK ) {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request was performed");
|
||||
int status_code = esp_http_client_get_status_code(http_client);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP status code" + std::to_string(status_code));
|
||||
} else {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request failed");
|
||||
}
|
||||
esp_http_client_cleanup(http_client);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static esp_err_t http_event_handler(esp_http_client_event_t *evt)
|
||||
{
|
||||
switch(evt->event_id)
|
||||
@@ -64,35 +159,37 @@ void InfluxDBPublish(std::string _key, std::string _content, std::string _timest
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", Timestamp: " + _timestamp);
|
||||
|
||||
// Format: #define PREVALUE_TIME_FORMAT_OUTPUT "%Y-%m-%dT%H:%M:%S%z"
|
||||
struct tm tm;
|
||||
strptime(_timestamp.c_str(), PREVALUE_TIME_FORMAT_OUTPUT, &tm);
|
||||
time_t t = mktime(&tm); // t is now your desired time_t
|
||||
|
||||
struct tm * ptm;
|
||||
ptm = gmtime ( &t );
|
||||
time_t utc = mktime(ptm);
|
||||
|
||||
// time_t now;
|
||||
// time(&now);
|
||||
char nowTimestamp[21];
|
||||
// pad with zeroes to get nanoseconds
|
||||
// sprintf(nowTimestamp,"%ld000000000", (long) now);
|
||||
// sprintf(nowTimestamp,"%ld000000000", (long) t); // Localtime
|
||||
sprintf(nowTimestamp,"%ld000000000", (long) utc); // UTC
|
||||
|
||||
std::string payload;
|
||||
|
||||
// LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Test Time Conversion - t: " + std::to_string(t) + ", utc: " + std::to_string(utc));
|
||||
// LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Test Time Conversion - now: " + std::to_string(now) + ", timestamp: " + std::to_string(t) + "(correct time not used yet)");
|
||||
if (_timestamp.length() > 0)
|
||||
{
|
||||
struct tm tm;
|
||||
strptime(_timestamp.c_str(), PREVALUE_TIME_FORMAT_OUTPUT, &tm);
|
||||
time_t t = mktime(&tm); // Time in Localtime (looks like timezone is not used by strptime)
|
||||
|
||||
struct tm * ptm;
|
||||
ptm = gmtime ( &t );
|
||||
time_t utc = mktime(ptm);
|
||||
utc = 2*t - utc; // Take care of timezone (looks difficult, but is easy: t = t + (t - utc), weil t-utc = timezone)
|
||||
|
||||
sprintf(nowTimestamp,"%ld000000000", (long) utc); // UTC
|
||||
|
||||
payload = _influxDBMeasurement + " " + _key + "=" + _content + " " + nowTimestamp;
|
||||
// payload = _influxDBMeasurement + " " + _key + "=774 " + nowTimestamp;
|
||||
}
|
||||
else
|
||||
{
|
||||
payload = _influxDBMeasurement + " " + _key + "=" + _content;
|
||||
}
|
||||
|
||||
std::string payload = _influxDBMeasurement + " " + _key + "=" + _content + " " + nowTimestamp;
|
||||
payload.shrink_to_fit();
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "sending line to influxdb:" + payload);
|
||||
|
||||
|
||||
// use the default retention policy of the database
|
||||
std::string apiURI = _influxDBURI + "/api/v2/write?bucket=" + _influxDBDatabase + "/";
|
||||
std::string apiURI = _influxDBURI + "/write?db=" + _influxDBDatabase + "/";
|
||||
apiURI.shrink_to_fit();
|
||||
http_config.url = apiURI.c_str();
|
||||
|
||||
|
||||
@@ -8,10 +8,17 @@
|
||||
#include <map>
|
||||
#include <functional>
|
||||
|
||||
// Interface to InfluxDB v1.x
|
||||
void InfluxDBInit(std::string _influxDBURI, std::string _database, std::string _measurement, std::string _user, std::string _password);
|
||||
void InfluxDBdestroy();
|
||||
|
||||
void InfluxDBPublish(std::string _key, std::string _content, std::string _timestamp);
|
||||
|
||||
// Interface to InfluxDB v2.x
|
||||
void InfluxDB_V2_Init(std::string _uri, std::string _database, std::string _measurement, std::string _org, std::string _token);
|
||||
void InfluxDB_V2_Publish(std::string _key, std::string _content, std::string _timestamp);
|
||||
|
||||
|
||||
|
||||
void InfluxDBdestroy();
|
||||
|
||||
#endif //INTERFACE_INFLUXDB_H
|
||||
#endif //ENABLE_INFLUXDB
|
||||
@@ -15,6 +15,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include "Helper.h"
|
||||
#include "time_sntp.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char *TAG = "LOGFILE";
|
||||
@@ -78,11 +79,11 @@ void ClassLogFile::WriteToData(std::string _timestamp, std::string _name, std::s
|
||||
}
|
||||
|
||||
|
||||
void ClassLogFile::setLogLevel(esp_log_level_t _logLevel){
|
||||
loglevel = _logLevel;
|
||||
|
||||
void ClassLogFile::setLogLevel(esp_log_level_t _logLevel)
|
||||
{
|
||||
std::string levelText;
|
||||
|
||||
// Print log level to log file
|
||||
switch(_logLevel) {
|
||||
case ESP_LOG_WARN:
|
||||
levelText = "WARNING";
|
||||
@@ -95,13 +96,16 @@ void ClassLogFile::setLogLevel(esp_log_level_t _logLevel){
|
||||
case ESP_LOG_DEBUG:
|
||||
levelText = "DEBUG";
|
||||
break;
|
||||
|
||||
case ESP_LOG_ERROR:
|
||||
default:
|
||||
levelText = "ERROR";
|
||||
break;
|
||||
}
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Set log level to " + levelText);
|
||||
|
||||
ESP_LOGI(TAG, "Log Level set to %s", levelText.c_str());
|
||||
// set new log level
|
||||
loglevel = _logLevel;
|
||||
|
||||
/*
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Test");
|
||||
@@ -318,15 +322,23 @@ void ClassLogFile::RemoveOldLogFile()
|
||||
//ESP_LOGD(TAG, "compare log file: %s to %s", entry->d_name, cmpfilename);
|
||||
if ((strlen(entry->d_name) == strlen(cmpfilename)) && (strcmp(entry->d_name, cmpfilename) < 0)) {
|
||||
//ESP_LOGD(TAG, "delete log file: %s", entry->d_name);
|
||||
std::string filepath = logroot + "/" + entry->d_name;
|
||||
if (unlink(filepath.c_str()) == 0) {
|
||||
deleted ++;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "can't delete file: %s", entry->d_name);
|
||||
notDeleted ++;
|
||||
std::string filepath = logroot + "/" + entry->d_name;
|
||||
if ((strcmp(entry->d_name, "log_1970-01-01.txt") == 0) && getTimeWasNotSetAtBoot()) { // keep logfile log_1970-01-01.txt if time was not set at boot (some boot logs are in there)
|
||||
//ESP_LOGD(TAG, "Skip deleting this file: %s", entry->d_name);
|
||||
notDeleted++;
|
||||
}
|
||||
} else {
|
||||
notDeleted ++;
|
||||
else {
|
||||
if (unlink(filepath.c_str()) == 0) {
|
||||
deleted++;
|
||||
}
|
||||
else {
|
||||
ESP_LOGE(TAG, "can't delete file: %s", entry->d_name);
|
||||
notDeleted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
notDeleted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -386,14 +398,17 @@ void ClassLogFile::RemoveOldDataLog()
|
||||
}
|
||||
|
||||
|
||||
void ClassLogFile::CreateLogDirectories()
|
||||
bool ClassLogFile::CreateLogDirectories()
|
||||
{
|
||||
MakeDir("/sdcard/log");
|
||||
MakeDir("/sdcard/log/data");
|
||||
MakeDir("/sdcard/log/analog");
|
||||
MakeDir("/sdcard/log/digit");
|
||||
MakeDir("/sdcard/log/message");
|
||||
MakeDir("/sdcard/log/source");
|
||||
bool bRetval = false;
|
||||
bRetval = MakeDir("/sdcard/log");
|
||||
bRetval = MakeDir("/sdcard/log/data");
|
||||
bRetval = MakeDir("/sdcard/log/analog");
|
||||
bRetval = MakeDir("/sdcard/log/digit");
|
||||
bRetval = MakeDir("/sdcard/log/message");
|
||||
bRetval = MakeDir("/sdcard/log/source");
|
||||
|
||||
return bRetval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
|
||||
void CloseLogFileAppendHandle();
|
||||
|
||||
void CreateLogDirectories();
|
||||
bool CreateLogDirectories();
|
||||
void RemoveOldLogFile();
|
||||
void RemoveOldDataLog();
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ std::map<std::string, std::function<void()>>* connectFunktionMap = NULL;
|
||||
std::map<std::string, std::function<bool(std::string, char*, int)>>* subscribeFunktionMap = NULL;
|
||||
|
||||
int failedOnRound = -1;
|
||||
int MQTTReconnectCnt = 0;
|
||||
|
||||
esp_mqtt_event_id_t esp_mqtt_ID = MQTT_EVENT_ANY;
|
||||
// ESP_EVENT_ANY_ID
|
||||
@@ -25,11 +26,12 @@ bool mqtt_connected = false;
|
||||
|
||||
esp_mqtt_client_handle_t client = NULL;
|
||||
std::string uri, client_id, lwt_topic, lwt_connected, lwt_disconnected, user, password, maintopic;
|
||||
int keepalive, SetRetainFlag;
|
||||
void (*callbackOnConnected)(std::string, int) = NULL;
|
||||
int keepalive;
|
||||
bool SetRetainFlag;
|
||||
void (*callbackOnConnected)(std::string, bool) = NULL;
|
||||
|
||||
|
||||
bool MQTTPublish(std::string _key, std::string _content, int retained_flag)
|
||||
bool MQTTPublish(std::string _key, std::string _content, int qos, bool retained_flag)
|
||||
{
|
||||
if (!mqtt_enabled) { // MQTT sevice not started / configured (MQTT_Init not called before)
|
||||
return false;
|
||||
@@ -49,7 +51,7 @@ bool MQTTPublish(std::string _key, std::string _content, int retained_flag)
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
long long int starttime = esp_timer_get_time();
|
||||
#endif
|
||||
int msg_id = esp_mqtt_client_publish(client, _key.c_str(), _content.c_str(), 0, 1, retained_flag);
|
||||
int msg_id = esp_mqtt_client_publish(client, _key.c_str(), _content.c_str(), 0, qos, retained_flag);
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Publish msg_id %d in %lld ms", msg_id, (esp_timer_get_time() - starttime)/1000);
|
||||
#endif
|
||||
@@ -58,7 +60,7 @@ bool MQTTPublish(std::string _key, std::string _content, int retained_flag)
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
starttime = esp_timer_get_time();
|
||||
#endif
|
||||
msg_id = esp_mqtt_client_publish(client, _key.c_str(), _content.c_str(), 0, 1, retained_flag);
|
||||
msg_id = esp_mqtt_client_publish(client, _key.c_str(), _content.c_str(), 0, qos, retained_flag);
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Publish msg_id %d in %lld ms", msg_id, (esp_timer_get_time() - starttime)/1000);
|
||||
#endif
|
||||
@@ -88,29 +90,39 @@ static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) {
|
||||
std::string topic = "";
|
||||
switch (event->event_id) {
|
||||
case MQTT_EVENT_BEFORE_CONNECT:
|
||||
ESP_LOGD(TAG, "MQTT_EVENT_BEFORE_CONNECT");
|
||||
mqtt_initialized = true;
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_CONNECTED:
|
||||
ESP_LOGD(TAG, "MQTT_EVENT_CONNECTED");
|
||||
MQTTReconnectCnt = 0;
|
||||
mqtt_initialized = true;
|
||||
mqtt_connected = true;
|
||||
MQTTconnected();
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DISCONNECTED:
|
||||
ESP_LOGD(TAG, "MQTT_EVENT_DISCONNECTED");
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Disconnected from broker");
|
||||
mqtt_connected = false;
|
||||
MQTTReconnectCnt++;
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected, trying to reconnect");
|
||||
|
||||
if (MQTTReconnectCnt >= 5) {
|
||||
MQTTReconnectCnt = 0;
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Disconnected, multiple reconnect attempts failed, still retrying...");
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBED:
|
||||
ESP_LOGD(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBED:
|
||||
ESP_LOGD(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISHED:
|
||||
ESP_LOGD(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DATA:
|
||||
ESP_LOGD(TAG, "MQTT_EVENT_DATA");
|
||||
ESP_LOGD(TAG, "TOPIC=%.*s", event->topic_len, event->topic);
|
||||
@@ -125,7 +137,33 @@ static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) {
|
||||
ESP_LOGW(TAG, "no handler available\r\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_ERROR:
|
||||
// http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718033 --> chapter 3.2.2.3
|
||||
|
||||
// The server does not support the level of the MQTT protocol requested by the client
|
||||
// NOTE: Only protocol 3.1.1 is supported (refer to setting in sdkconfig)
|
||||
if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_PROTOCOL) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, unacceptable protocol version (0x01)");
|
||||
}
|
||||
// The client identifier is correct UTF-8 but not allowed by the server
|
||||
// e.g. clientID empty (cannot be the case -> default set in firmware)
|
||||
else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_ID_REJECTED) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, identifier rejected (0x02)");
|
||||
}
|
||||
// The network connection has been made but the MQTT service is unavailable
|
||||
else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_SERVER_UNAVAILABLE) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, Server unavailable (0x03)");
|
||||
}
|
||||
// The data in the user name or password is malformed
|
||||
else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_BAD_USERNAME) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, malformed data in username or password (0x04)");
|
||||
}
|
||||
// The client is not authorized to connect
|
||||
else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_NOT_AUTHORIZED) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, not authorized. Check username/password (0x05)");
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "MQTT_EVENT_ERROR - esp_mqtt_error_codes:");
|
||||
ESP_LOGD(TAG, "error_type:%d", event->error_handle->error_type);
|
||||
@@ -135,8 +173,9 @@ static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) {
|
||||
ESP_LOGD(TAG, "esp_tls_stack_err:%d", event->error_handle->esp_tls_stack_err);
|
||||
ESP_LOGD(TAG, "esp_tls_cert_verify_flags:%d", event->error_handle->esp_tls_cert_verify_flags);
|
||||
#endif
|
||||
mqtt_connected = false;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
ESP_LOGD(TAG, "Other event id:%d", event->event_id);
|
||||
break;
|
||||
@@ -153,7 +192,7 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
|
||||
|
||||
bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password,
|
||||
std::string _maintopic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected,
|
||||
int _keepalive, int _SetRetainFlag, void *_callbackOnConnected) {
|
||||
int _keepalive, bool _SetRetainFlag, void *_callbackOnConnected) {
|
||||
if ((_mqttURI.length() == 0) || (_maintopic.length() == 0) || (_clientid.length() == 0))
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Init aborted! Config error (URI, MainTopic or ClientID missing)");
|
||||
@@ -168,7 +207,7 @@ bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _us
|
||||
keepalive = _keepalive;
|
||||
SetRetainFlag = _SetRetainFlag;
|
||||
maintopic = _maintopic;
|
||||
callbackOnConnected = ( void (*)(std::string, int) )(_callbackOnConnected);
|
||||
callbackOnConnected = ( void (*)(std::string, bool) )(_callbackOnConnected);
|
||||
|
||||
if (_user.length() && _password.length()){
|
||||
user = _user;
|
||||
@@ -220,8 +259,7 @@ int MQTT_Init() {
|
||||
.buffer_size = 1536, // size of MQTT send/receive buffer (Default: 1024)
|
||||
.reconnect_timeout_ms = 15000, // Try to reconnect to broker (Default: 10000ms)
|
||||
.network_timeout_ms = 20000, // Network Timeout (Default: 10000ms)
|
||||
.message_retransmit_timeout = 3000 // Tiem after message resent when broker not acknowledged (QoS1, QoS2)
|
||||
|
||||
.message_retransmit_timeout = 3000 // Time after message resent when broker not acknowledged (QoS1, QoS2)
|
||||
};
|
||||
|
||||
if (user.length() && password.length()){
|
||||
@@ -312,8 +350,7 @@ bool mqtt_handler_flow_start(std::string _topic, char* _data, int _data_len) {
|
||||
void MQTTconnected(){
|
||||
if (mqtt_connected) {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Connected to broker");
|
||||
MQTTPublish(lwt_topic, lwt_connected, true); // Publish "connected" to maintopic/connection
|
||||
|
||||
|
||||
if (connectFunktionMap != NULL) {
|
||||
for(std::map<std::string, std::function<void()>>::iterator it = connectFunktionMap->begin(); it != connectFunktionMap->end(); ++it) {
|
||||
it->second();
|
||||
@@ -332,7 +369,7 @@ void MQTTconnected(){
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(10000 / portTICK_PERIOD_MS); // Delay execution of callback routine after connection got established
|
||||
/* Send Static Topics and Homeassistant Discovery */
|
||||
if (callbackOnConnected) { // Call onConnected callback routine --> mqtt_server
|
||||
callbackOnConnected(maintopic, SetRetainFlag);
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@
|
||||
|
||||
bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password,
|
||||
std::string _maintopic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected,
|
||||
int _keepalive, int SetRetainFlag, void *callbackOnConnected);
|
||||
int _keepalive, bool SetRetainFlag, void *callbackOnConnected);
|
||||
int MQTT_Init();
|
||||
void MQTTdestroy_client(bool _disable);
|
||||
|
||||
bool MQTTPublish(std::string _key, std::string _content, int retained_flag = 1); // retained Flag as Standart
|
||||
bool MQTTPublish(std::string _key, std::string _content, int qos, bool retained_flag = 1); // retained Flag as Standart
|
||||
|
||||
bool getMQTTisEnabled();
|
||||
bool getMQTTisConnected();
|
||||
|
||||
305
code/components/jomjol_mqtt/mqtt_outbox.c
Normal file
305
code/components/jomjol_mqtt/mqtt_outbox.c
Normal file
@@ -0,0 +1,305 @@
|
||||
/* This is a modification of https://github.com/espressif/esp-mqtt/blob/master/lib/mqtt_outbox.c
|
||||
* to use the PSRAM instead of the internal heap.
|
||||
*/
|
||||
#include "mqtt_outbox.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "sys/queue.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
/* Enable this to use the PSRAM for MQTT Publishing.
|
||||
* This saves 10 kBytes of RAM, see https://github.com/jomjol/AI-on-the-edge-device/pull/2113
|
||||
* However we can run into PSRAM fragmentation issues, leading to insufficient large blocks to load the model.
|
||||
* See https://github.com/jomjol/AI-on-the-edge-device/issues/2200 */
|
||||
#define USE_PSRAM
|
||||
|
||||
#ifdef CONFIG_MQTT_CUSTOM_OUTBOX
|
||||
static const char *TAG = "outbox";
|
||||
|
||||
typedef struct outbox_item {
|
||||
char *buffer;
|
||||
int len;
|
||||
int msg_id;
|
||||
int msg_type;
|
||||
int msg_qos;
|
||||
outbox_tick_t tick;
|
||||
pending_state_t pending;
|
||||
STAILQ_ENTRY(outbox_item) next;
|
||||
} outbox_item_t;
|
||||
|
||||
STAILQ_HEAD(outbox_list_t, outbox_item);
|
||||
|
||||
|
||||
outbox_handle_t outbox_init(void)
|
||||
{
|
||||
#ifdef USE_PSRAM
|
||||
outbox_handle_t outbox = heap_caps_calloc(1, sizeof(struct outbox_list_t), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
||||
#else
|
||||
outbox_handle_t outbox = calloc(1, sizeof(struct outbox_list_t));
|
||||
#endif
|
||||
//ESP_MEM_CHECK(TAG, outbox, return NULL);
|
||||
STAILQ_INIT(outbox);
|
||||
return outbox;
|
||||
}
|
||||
|
||||
outbox_item_handle_t outbox_enqueue(outbox_handle_t outbox, outbox_message_handle_t message, outbox_tick_t tick)
|
||||
{
|
||||
#ifdef USE_PSRAM
|
||||
outbox_item_handle_t item = heap_caps_calloc(1, sizeof(outbox_item_t), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
||||
#else
|
||||
outbox_item_handle_t item = calloc(1, sizeof(outbox_item_t));
|
||||
#endif
|
||||
//ESP_MEM_CHECK(TAG, item, return NULL);
|
||||
item->msg_id = message->msg_id;
|
||||
item->msg_type = message->msg_type;
|
||||
item->msg_qos = message->msg_qos;
|
||||
item->tick = tick;
|
||||
item->len = message->len + message->remaining_len;
|
||||
item->pending = QUEUED;
|
||||
#ifdef USE_PSRAM
|
||||
item->buffer = heap_caps_malloc(message->len + message->remaining_len, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
||||
#else
|
||||
item->buffer = malloc(message->len + message->remaining_len);
|
||||
#endif
|
||||
/*ESP_MEM_CHECK(TAG, item->buffer, {
|
||||
free(item);
|
||||
return NULL;
|
||||
});*/
|
||||
memcpy(item->buffer, message->data, message->len);
|
||||
if (message->remaining_data) {
|
||||
memcpy(item->buffer + message->len, message->remaining_data, message->remaining_len);
|
||||
}
|
||||
STAILQ_INSERT_TAIL(outbox, item, next);
|
||||
ESP_LOGD(TAG, "ENQUEUE msgid=%d, msg_type=%d, len=%d, size=%d", message->msg_id, message->msg_type, message->len + message->remaining_len, outbox_get_size(outbox));
|
||||
return item;
|
||||
}
|
||||
|
||||
outbox_item_handle_t outbox_get(outbox_handle_t outbox, int msg_id)
|
||||
{
|
||||
outbox_item_handle_t item;
|
||||
STAILQ_FOREACH(item, outbox, next) {
|
||||
if (item->msg_id == msg_id) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
outbox_item_handle_t outbox_dequeue(outbox_handle_t outbox, pending_state_t pending, outbox_tick_t *tick)
|
||||
{
|
||||
outbox_item_handle_t item;
|
||||
STAILQ_FOREACH(item, outbox, next) {
|
||||
if (item->pending == pending) {
|
||||
if (tick) {
|
||||
*tick = item->tick;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t outbox_delete_item(outbox_handle_t outbox, outbox_item_handle_t item_to_delete)
|
||||
{
|
||||
outbox_item_handle_t item;
|
||||
STAILQ_FOREACH(item, outbox, next) {
|
||||
if (item == item_to_delete) {
|
||||
STAILQ_REMOVE(outbox, item, outbox_item, next);
|
||||
#ifdef USE_PSRAM
|
||||
heap_caps_free(item->buffer);
|
||||
heap_caps_free(item);
|
||||
#else
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
#endif
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint8_t *outbox_item_get_data(outbox_item_handle_t item, size_t *len, uint16_t *msg_id, int *msg_type, int *qos)
|
||||
{
|
||||
if (item) {
|
||||
*len = item->len;
|
||||
*msg_id = item->msg_id;
|
||||
*msg_type = item->msg_type;
|
||||
*qos = item->msg_qos;
|
||||
return (uint8_t *)item->buffer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t outbox_delete(outbox_handle_t outbox, int msg_id, int msg_type)
|
||||
{
|
||||
outbox_item_handle_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, outbox, next, tmp) {
|
||||
if (item->msg_id == msg_id && (0xFF & (item->msg_type)) == msg_type) {
|
||||
STAILQ_REMOVE(outbox, item, outbox_item, next);
|
||||
#ifdef USE_PSRAM
|
||||
heap_caps_free(item->buffer);
|
||||
heap_caps_free(item);
|
||||
#else
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
#endif
|
||||
ESP_LOGD(TAG, "DELETED msgid=%d, msg_type=%d, remain size=%d", msg_id, msg_type, outbox_get_size(outbox));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_err_t outbox_delete_msgid(outbox_handle_t outbox, int msg_id)
|
||||
{
|
||||
outbox_item_handle_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, outbox, next, tmp) {
|
||||
if (item->msg_id == msg_id) {
|
||||
STAILQ_REMOVE(outbox, item, outbox_item, next);
|
||||
#ifdef USE_PSRAM
|
||||
heap_caps_free(item->buffer);
|
||||
heap_caps_free(item);
|
||||
#else
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
esp_err_t outbox_set_pending(outbox_handle_t outbox, int msg_id, pending_state_t pending)
|
||||
{
|
||||
outbox_item_handle_t item = outbox_get(outbox, msg_id);
|
||||
if (item) {
|
||||
item->pending = pending;
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
pending_state_t outbox_item_get_pending(outbox_item_handle_t item)
|
||||
{
|
||||
if (item) {
|
||||
return item->pending;
|
||||
}
|
||||
return QUEUED;
|
||||
}
|
||||
|
||||
esp_err_t outbox_set_tick(outbox_handle_t outbox, int msg_id, outbox_tick_t tick)
|
||||
{
|
||||
outbox_item_handle_t item = outbox_get(outbox, msg_id);
|
||||
if (item) {
|
||||
item->tick = tick;
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t outbox_delete_msgtype(outbox_handle_t outbox, int msg_type)
|
||||
{
|
||||
outbox_item_handle_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, outbox, next, tmp) {
|
||||
if (item->msg_type == msg_type) {
|
||||
STAILQ_REMOVE(outbox, item, outbox_item, next);
|
||||
#ifdef USE_PSRAM
|
||||
heap_caps_free(item->buffer);
|
||||
heap_caps_free(item);
|
||||
#else
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
int outbox_delete_single_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout)
|
||||
{
|
||||
int msg_id = -1;
|
||||
outbox_item_handle_t item;
|
||||
STAILQ_FOREACH(item, outbox, next) {
|
||||
if (current_tick - item->tick > timeout) {
|
||||
STAILQ_REMOVE(outbox, item, outbox_item, next);
|
||||
|
||||
#ifdef USE_PSRAM
|
||||
heap_caps_free(item->buffer);
|
||||
#else
|
||||
free(item->buffer);
|
||||
#endif
|
||||
|
||||
msg_id = item->msg_id;
|
||||
|
||||
#ifdef USE_PSRAM
|
||||
heap_caps_free(item);
|
||||
#else
|
||||
free(item);
|
||||
#endif
|
||||
|
||||
return msg_id;
|
||||
}
|
||||
|
||||
}
|
||||
return msg_id;
|
||||
}
|
||||
|
||||
int outbox_delete_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout)
|
||||
{
|
||||
int deleted_items = 0;
|
||||
outbox_item_handle_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, outbox, next, tmp) {
|
||||
if (current_tick - item->tick > timeout) {
|
||||
STAILQ_REMOVE(outbox, item, outbox_item, next);
|
||||
#ifdef USE_PSRAM
|
||||
heap_caps_free(item->buffer);
|
||||
heap_caps_free(item);
|
||||
#else
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
#endif
|
||||
deleted_items ++;
|
||||
}
|
||||
|
||||
}
|
||||
return deleted_items;
|
||||
}
|
||||
|
||||
int outbox_get_size(outbox_handle_t outbox)
|
||||
{
|
||||
int siz = 0;
|
||||
outbox_item_handle_t item;
|
||||
STAILQ_FOREACH(item, outbox, next) {
|
||||
// Suppressing "use after free" warning as this could happen only if queue is in inconsistent state
|
||||
// which never happens if STAILQ interface used
|
||||
siz += item->len; // NOLINT(clang-analyzer-unix.Malloc)
|
||||
}
|
||||
return siz;
|
||||
}
|
||||
|
||||
void outbox_delete_all_items(outbox_handle_t outbox)
|
||||
{
|
||||
outbox_item_handle_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, outbox, next, tmp) {
|
||||
STAILQ_REMOVE(outbox, item, outbox_item, next);
|
||||
#ifdef USE_PSRAM
|
||||
heap_caps_free(item->buffer);
|
||||
heap_caps_free(item);
|
||||
#else
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
void outbox_destroy(outbox_handle_t outbox)
|
||||
{
|
||||
outbox_delete_all_items(outbox);
|
||||
|
||||
#ifdef USE_PSRAM
|
||||
heap_caps_free(outbox);
|
||||
#else
|
||||
free(outbox);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MQTT_CUSTOM_OUTBOX */
|
||||
66
code/components/jomjol_mqtt/mqtt_outbox.h
Normal file
66
code/components/jomjol_mqtt/mqtt_outbox.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/* This is an adaption of https://github.com/espressif/esp-mqtt/blob/master/lib/include/mqtt_outbox.h
|
||||
* This file is subject to the terms and conditions defined in
|
||||
* file 'LICENSE', which is part of this source code package.
|
||||
* Tuan PM <tuanpm at live dot com>
|
||||
*/
|
||||
#ifndef _MQTT_OUTOBX_H_
|
||||
#define _MQTT_OUTOBX_H_
|
||||
//#include "platform.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct outbox_item;
|
||||
|
||||
typedef struct outbox_list_t *outbox_handle_t;
|
||||
typedef struct outbox_item *outbox_item_handle_t;
|
||||
typedef struct outbox_message *outbox_message_handle_t;
|
||||
typedef long long outbox_tick_t;
|
||||
|
||||
typedef struct outbox_message {
|
||||
uint8_t *data;
|
||||
int len;
|
||||
int msg_id;
|
||||
int msg_qos;
|
||||
int msg_type;
|
||||
uint8_t *remaining_data;
|
||||
int remaining_len;
|
||||
} outbox_message_t;
|
||||
|
||||
typedef enum pending_state {
|
||||
QUEUED,
|
||||
TRANSMITTED,
|
||||
ACKNOWLEDGED,
|
||||
CONFIRMED
|
||||
} pending_state_t;
|
||||
|
||||
outbox_handle_t outbox_init(void);
|
||||
outbox_item_handle_t outbox_enqueue(outbox_handle_t outbox, outbox_message_handle_t message, outbox_tick_t tick);
|
||||
outbox_item_handle_t outbox_dequeue(outbox_handle_t outbox, pending_state_t pending, outbox_tick_t *tick);
|
||||
outbox_item_handle_t outbox_get(outbox_handle_t outbox, int msg_id);
|
||||
uint8_t *outbox_item_get_data(outbox_item_handle_t item, size_t *len, uint16_t *msg_id, int *msg_type, int *qos);
|
||||
esp_err_t outbox_delete(outbox_handle_t outbox, int msg_id, int msg_type);
|
||||
esp_err_t outbox_delete_msgid(outbox_handle_t outbox, int msg_id);
|
||||
esp_err_t outbox_delete_msgtype(outbox_handle_t outbox, int msg_type);
|
||||
esp_err_t outbox_delete_item(outbox_handle_t outbox, outbox_item_handle_t item);
|
||||
int outbox_delete_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout);
|
||||
/**
|
||||
* @brief Deletes single expired message returning it's message id
|
||||
*
|
||||
* @return msg id of the deleted message, -1 if no expired message in the outbox
|
||||
*/
|
||||
int outbox_delete_single_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout);
|
||||
|
||||
esp_err_t outbox_set_pending(outbox_handle_t outbox, int msg_id, pending_state_t pending);
|
||||
pending_state_t outbox_item_get_pending(outbox_item_handle_t item);
|
||||
esp_err_t outbox_set_tick(outbox_handle_t outbox, int msg_id, outbox_tick_t tick);
|
||||
int outbox_get_size(outbox_handle_t outbox);
|
||||
void outbox_destroy(outbox_handle_t outbox);
|
||||
void outbox_delete_all_items(outbox_handle_t outbox);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "esp_log.h"
|
||||
#include "ClassLogFile.h"
|
||||
#include "connect_wlan.h"
|
||||
#include "read_wlanini.h"
|
||||
#include "server_mqtt.h"
|
||||
#include "interface_mqtt.h"
|
||||
#include "time_sntp.h"
|
||||
@@ -29,8 +30,9 @@ std::string timeUnit = "";
|
||||
std::string rateUnit = "Unit/Minute";
|
||||
float roundInterval; // Minutes
|
||||
int keepAlive = 0; // Seconds
|
||||
int retainFlag;
|
||||
bool retainFlag;
|
||||
static std::string maintopic;
|
||||
bool sendingOf_DiscoveryAndStaticTopics_scheduled = true; // Set it to true to make sure it gets sent at least once after startup
|
||||
|
||||
|
||||
void mqttServer_setParameter(std::vector<NumberPost*>* _NUMBERS, int _keepAlive, float _roundInterval) {
|
||||
@@ -46,8 +48,9 @@ void mqttServer_setMeterType(std::string _meterType, std::string _valueUnit, std
|
||||
rateUnit = _rateUnit;
|
||||
}
|
||||
|
||||
void sendHomeAssistantDiscoveryTopic(std::string group, std::string field,
|
||||
std::string name, std::string icon, std::string unit, std::string deviceClass, std::string stateClass, std::string entityCategory) {
|
||||
bool sendHomeAssistantDiscoveryTopic(std::string group, std::string field,
|
||||
std::string name, std::string icon, std::string unit, std::string deviceClass, std::string stateClass, std::string entityCategory,
|
||||
int qos) {
|
||||
std::string version = std::string(libfive_git_version());
|
||||
|
||||
if (version == "") {
|
||||
@@ -130,26 +133,31 @@ void sendHomeAssistantDiscoveryTopic(std::string group, std::string field,
|
||||
"}" +
|
||||
"}";
|
||||
|
||||
MQTTPublish(topicFull, payload, true);
|
||||
return MQTTPublish(topicFull, payload, qos, true);
|
||||
}
|
||||
|
||||
void MQTThomeassistantDiscovery() {
|
||||
if (!getMQTTisConnected())
|
||||
return;
|
||||
bool MQTThomeassistantDiscovery(int qos) {
|
||||
bool allSendsSuccessed = false;
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "MQTT - Sending Homeassistant Discovery Topics (Meter Type: " + meterType + ", Value Unit: " + valueUnit + " , Rate Unit: " + rateUnit + ")...");
|
||||
if (!getMQTTisConnected()) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Unable to send Homeassistant Discovery Topics, we are not connected to the MQTT broker!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category
|
||||
sendHomeAssistantDiscoveryTopic("", "uptime", "Uptime", "clock-time-eight-outline", "s", "", "", "diagnostic");
|
||||
sendHomeAssistantDiscoveryTopic("", "MAC", "MAC Address", "network-outline", "", "", "", "diagnostic");
|
||||
sendHomeAssistantDiscoveryTopic("", "hostname", "Hostname", "network-outline", "", "", "", "diagnostic");
|
||||
sendHomeAssistantDiscoveryTopic("", "freeMem", "Free Memory", "memory", "B", "", "measurement", "diagnostic");
|
||||
sendHomeAssistantDiscoveryTopic("", "wifiRSSI", "Wi-Fi RSSI", "wifi", "dBm", "signal_strength", "", "diagnostic");
|
||||
sendHomeAssistantDiscoveryTopic("", "CPUtemp", "CPU Temperature", "thermometer", "°C", "temperature", "measurement", "diagnostic");
|
||||
sendHomeAssistantDiscoveryTopic("", "interval", "Interval", "clock-time-eight-outline", "min", "" , "measurement", "diagnostic");
|
||||
sendHomeAssistantDiscoveryTopic("", "IP", "IP", "network-outline", "", "", "", "diagnostic");
|
||||
sendHomeAssistantDiscoveryTopic("", "status", "Status", "list-status", "", "", "", "diagnostic");
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Publishing Homeassistant Discovery topics (Meter Type: '" + meterType + "', Value Unit: '" + valueUnit + "' , Rate Unit: '" + rateUnit + "') ...");
|
||||
|
||||
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", "clock-time-eight-outline", "s", "", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "MAC", "MAC Address", "network-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("", "IP", "IP", "network-outline", "", "", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "status", "Status", "list-status", "", "", "", "diagnostic", qos);
|
||||
|
||||
|
||||
for (int i = 0; i < (*NUMBERS).size(); ++i) {
|
||||
@@ -158,76 +166,141 @@ void MQTThomeassistantDiscovery() {
|
||||
group = "";
|
||||
}
|
||||
|
||||
// Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category
|
||||
sendHomeAssistantDiscoveryTopic(group, "value", "Value", "gauge", valueUnit, meterType, "total_increasing", "");
|
||||
sendHomeAssistantDiscoveryTopic(group, "raw", "Raw Value", "raw", valueUnit, "", "total_increasing", "diagnostic");
|
||||
sendHomeAssistantDiscoveryTopic(group, "error", "Error", "alert-circle-outline", "", "", "", "diagnostic");
|
||||
// Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "value", "Value", "gauge", valueUnit, meterType, "total_increasing", "", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "raw", "Raw Value", "raw", "", "", "", "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_digitalization_round */
|
||||
// sendHomeAssistantDiscoveryTopic(group, "rate", "Rate (Unit/Minute)", "swap-vertical", "", "", "", ""); // Legacy, always Unit per Minute
|
||||
sendHomeAssistantDiscoveryTopic(group, "rate_per_time_unit", "Rate (" + rateUnit + ")", "swap-vertical", rateUnit, "", "", "");
|
||||
sendHomeAssistantDiscoveryTopic(group, "rate_per_digitalization_round", "Change since last digitalization round", "arrow-expand-vertical", valueUnit, "", "measurement", ""); // correctly the Unit is Uint/Interval!
|
||||
sendHomeAssistantDiscoveryTopic(group, "timestamp", "Timestamp", "clock-time-eight-outline", "", "timestamp", "", "diagnostic");
|
||||
sendHomeAssistantDiscoveryTopic(group, "json", "JSON", "code-json", "", "", "", "diagnostic");
|
||||
sendHomeAssistantDiscoveryTopic(group, "problem", "Problem", "alert-outline", "", "problem", "", ""); // 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, "", "measurement", "", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_digitalization_round", "Change since last digitalization 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");
|
||||
|
||||
int aFreeInternalHeapSizeAfter = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
int aMinFreeInternalHeapSize = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Int. Heap Usage before Publishing Homeassistand Discovery Topics: " +
|
||||
to_string(aFreeInternalHeapSizeBefore) + ", after: " + to_string(aFreeInternalHeapSizeAfter) + ", delta: " +
|
||||
to_string(aFreeInternalHeapSizeBefore - aFreeInternalHeapSizeAfter) + ", lowest free: " + to_string(aMinFreeInternalHeapSize));
|
||||
|
||||
return allSendsSuccessed;
|
||||
}
|
||||
|
||||
void publishSystemData() {
|
||||
if (!getMQTTisConnected())
|
||||
return;
|
||||
bool publishSystemData(int qos) {
|
||||
bool allSendsSuccessed = false;
|
||||
|
||||
if (!getMQTTisConnected()) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Unable to send System Topics, we are not connected to the MQTT broker!");
|
||||
return false;
|
||||
}
|
||||
|
||||
char tmp_char[50];
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Publishing system MQTT topics...");
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Publishing System MQTT topics...");
|
||||
|
||||
int aFreeInternalHeapSizeBefore = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
|
||||
allSendsSuccessed |= MQTTPublish(maintopic + "/" + std::string(LWT_TOPIC), LWT_CONNECTED, qos, retainFlag); // Publish "connected" to maintopic/connection
|
||||
|
||||
sprintf(tmp_char, "%ld", (long)getUpTime());
|
||||
MQTTPublish(maintopic + "/" + "uptime", std::string(tmp_char), retainFlag);
|
||||
allSendsSuccessed |= MQTTPublish(maintopic + "/" + "uptime", std::string(tmp_char), qos, retainFlag);
|
||||
|
||||
sprintf(tmp_char, "%lu", (long) getESPHeapSize());
|
||||
MQTTPublish(maintopic + "/" + "freeMem", std::string(tmp_char), retainFlag);
|
||||
allSendsSuccessed |= MQTTPublish(maintopic + "/" + "freeMem", std::string(tmp_char), qos, retainFlag);
|
||||
|
||||
sprintf(tmp_char, "%d", get_WIFI_RSSI());
|
||||
MQTTPublish(maintopic + "/" + "wifiRSSI", std::string(tmp_char), retainFlag);
|
||||
allSendsSuccessed |= MQTTPublish(maintopic + "/" + "wifiRSSI", std::string(tmp_char), qos, retainFlag);
|
||||
|
||||
sprintf(tmp_char, "%d", (int)temperatureRead());
|
||||
MQTTPublish(maintopic + "/" + "CPUtemp", std::string(tmp_char), retainFlag);
|
||||
allSendsSuccessed |= MQTTPublish(maintopic + "/" + "CPUtemp", std::string(tmp_char), qos, retainFlag);
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Successfully published all System MQTT topics");
|
||||
|
||||
int aFreeInternalHeapSizeAfter = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
int aMinFreeInternalHeapSize = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Int. Heap Usage before publishing System Topics: " +
|
||||
to_string(aFreeInternalHeapSizeBefore) + ", after: " + to_string(aFreeInternalHeapSizeAfter) + ", delta: " +
|
||||
to_string(aFreeInternalHeapSizeBefore - aFreeInternalHeapSizeAfter) + ", lowest free: " + to_string(aMinFreeInternalHeapSize));
|
||||
|
||||
return allSendsSuccessed;
|
||||
}
|
||||
|
||||
|
||||
void publishStaticData() {
|
||||
if (!getMQTTisConnected())
|
||||
return;
|
||||
bool publishStaticData(int qos) {
|
||||
bool allSendsSuccessed = false;
|
||||
|
||||
if (!getMQTTisConnected()) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Unable to send Static Topics, we are not connected to the MQTT broker!");
|
||||
return false;
|
||||
}
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Publishing static MQTT topics...");
|
||||
MQTTPublish(maintopic + "/" + "MAC", getMac(), retainFlag);
|
||||
MQTTPublish(maintopic + "/" + "IP", *getIPAddress(), retainFlag);
|
||||
MQTTPublish(maintopic + "/" + "hostname", hostname, retainFlag);
|
||||
|
||||
int aFreeInternalHeapSizeBefore = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
|
||||
allSendsSuccessed |= MQTTPublish(maintopic + "/" + "MAC", getMac(), qos, retainFlag);
|
||||
allSendsSuccessed |= MQTTPublish(maintopic + "/" + "IP", *getIPAddress(), qos, retainFlag);
|
||||
allSendsSuccessed |= MQTTPublish(maintopic + "/" + "hostname", wlan_config.hostname, qos, retainFlag);
|
||||
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(1) << roundInterval; // minutes
|
||||
MQTTPublish(maintopic + "/" + "interval", stream.str(), retainFlag);
|
||||
allSendsSuccessed |= MQTTPublish(maintopic + "/" + "interval", stream.str(), qos, retainFlag);
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Successfully published all Static MQTT topics");
|
||||
|
||||
int aFreeInternalHeapSizeAfter = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
int aMinFreeInternalHeapSize = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Int. Heap Usage before Publishing Static Topics: " +
|
||||
to_string(aFreeInternalHeapSizeBefore) + ", after: " + to_string(aFreeInternalHeapSizeAfter) + ", delta: " +
|
||||
to_string(aFreeInternalHeapSizeBefore - aFreeInternalHeapSizeAfter) + ", lowest free: " + to_string(aMinFreeInternalHeapSize));
|
||||
|
||||
return allSendsSuccessed;
|
||||
}
|
||||
|
||||
esp_err_t sendDiscovery_and_static_Topics(httpd_req_t *req) {
|
||||
if (HomeassistantDiscovery) {
|
||||
MQTThomeassistantDiscovery();
|
||||
}
|
||||
|
||||
publishStaticData();
|
||||
|
||||
const char* resp_str = (const char*) req->user_ctx;
|
||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||
|
||||
esp_err_t scheduleSendingDiscovery_and_static_Topics(httpd_req_t *req) {
|
||||
sendingOf_DiscoveryAndStaticTopics_scheduled = true;
|
||||
char msg[] = "MQTT Homeassistant Discovery and Static Topics scheduled";
|
||||
httpd_resp_send(req, msg, strlen(msg));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void GotConnected(std::string maintopic, int retainFlag) {
|
||||
if (HomeassistantDiscovery) {
|
||||
MQTThomeassistantDiscovery();
|
||||
|
||||
esp_err_t sendDiscovery_and_static_Topics(void) {
|
||||
bool success = false;
|
||||
|
||||
if (!sendingOf_DiscoveryAndStaticTopics_scheduled) {
|
||||
// Flag not set, nothing to do
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
publishStaticData();
|
||||
publishSystemData();
|
||||
if (HomeassistantDiscovery) {
|
||||
success = MQTThomeassistantDiscovery(1);
|
||||
}
|
||||
|
||||
success |= publishStaticData(1);
|
||||
|
||||
if (success) { // Success, clear the flag
|
||||
sendingOf_DiscoveryAndStaticTopics_scheduled = false;
|
||||
return ESP_OK;
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "One or more MQTT topics failed to be published, will try sending them in the next round!");
|
||||
/* Keep sendingOf_DiscoveryAndStaticTopics_scheduled set so we can retry after the next round */
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GotConnected(std::string maintopic, bool retainFlag) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
void register_server_mqtt_uri(httpd_handle_t server) {
|
||||
@@ -235,8 +308,8 @@ void register_server_mqtt_uri(httpd_handle_t server) {
|
||||
uri.method = HTTP_GET;
|
||||
|
||||
uri.uri = "/mqtt_publish_discovery";
|
||||
uri.handler = sendDiscovery_and_static_Topics;
|
||||
uri.user_ctx = (void*) "MQTT Discovery and Static Topics sent";
|
||||
uri.handler = scheduleSendingDiscovery_and_static_Topics;
|
||||
uri.user_ctx = (void*) "";
|
||||
httpd_register_uri_handler(server, &uri);
|
||||
}
|
||||
|
||||
@@ -251,7 +324,7 @@ void SetHomeassistantDiscoveryEnabled(bool enabled) {
|
||||
}
|
||||
|
||||
|
||||
void setMqtt_Server_Retain(int _retainFlag) {
|
||||
void setMqtt_Server_Retain(bool _retainFlag) {
|
||||
retainFlag = _retainFlag;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,16 +10,17 @@
|
||||
void SetHomeassistantDiscoveryEnabled(bool enabled);
|
||||
void mqttServer_setParameter(std::vector<NumberPost*>* _NUMBERS, int interval, float roundInterval);
|
||||
void mqttServer_setMeterType(std::string meterType, std::string valueUnit, std::string timeUnit,std::string rateUnit);
|
||||
void setMqtt_Server_Retain(int SetRetainFlag);
|
||||
void setMqtt_Server_Retain(bool SetRetainFlag);
|
||||
void mqttServer_setMainTopic( std::string maintopic);
|
||||
std::string mqttServer_getMainTopic();
|
||||
|
||||
void register_server_mqtt_uri(httpd_handle_t server);
|
||||
|
||||
void publishSystemData();
|
||||
bool publishSystemData(int qos);
|
||||
|
||||
std::string getTimeUnit(void);
|
||||
void GotConnected(std::string maintopic, int SetRetainFlag);
|
||||
void GotConnected(std::string maintopic, bool SetRetainFlag);
|
||||
esp_err_t sendDiscovery_and_static_Topics(void);
|
||||
|
||||
|
||||
#endif //SERVERMQTT_H
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "CTfLiteClass.h"
|
||||
#include "ClassLogFile.h"
|
||||
#include "Helper.h"
|
||||
#include "psram.h"
|
||||
#include "esp_log.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
@@ -250,7 +251,7 @@ bool CTfLiteClass::ReadFileToModel(std::string _fn)
|
||||
LogFile.WriteHeapInfo("CTLiteClass::Alloc modelfile start");
|
||||
#endif
|
||||
|
||||
modelfile = (unsigned char*)GET_MEMORY(size);
|
||||
modelfile = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->modelfile", size, MALLOC_CAP_SPIRAM);
|
||||
|
||||
if(modelfile != NULL)
|
||||
{
|
||||
@@ -305,17 +306,17 @@ CTfLiteClass::CTfLiteClass()
|
||||
this->input = nullptr;
|
||||
this->output = nullptr;
|
||||
this->kTensorArenaSize = 800 * 1024; /// according to testfile: 108000 - so far 600;; 2021-09-11: 200 * 1024
|
||||
this->tensor_arena = (uint8_t*)GET_MEMORY(kTensorArenaSize);
|
||||
this->tensor_arena = (uint8_t*)malloc_psram_heap(std::string(TAG) + "->tensor_arena", kTensorArenaSize, MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
|
||||
CTfLiteClass::~CTfLiteClass()
|
||||
{
|
||||
free(modelfile);
|
||||
|
||||
free(this->tensor_arena);
|
||||
delete this->interpreter;
|
||||
delete this->error_reporter;
|
||||
|
||||
free_psram_heap(std::string(TAG) + "->modelfile", modelfile);
|
||||
free_psram_heap(std::string(TAG) + "->tensor_arena", this->tensor_arena);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "../../include/defines.h"
|
||||
#include "Helper.h"
|
||||
#include "statusled.h"
|
||||
|
||||
#include "esp_camera.h"
|
||||
#include "time_sntp.h"
|
||||
@@ -21,7 +22,11 @@
|
||||
#include "server_GPIO.h"
|
||||
|
||||
#include "server_file.h"
|
||||
|
||||
#include "read_wlanini.h"
|
||||
#include "connect_wlan.h"
|
||||
#include "psram.h"
|
||||
|
||||
|
||||
ClassFlowControll tfliteflow;
|
||||
|
||||
@@ -30,7 +35,7 @@ TaskHandle_t xHandletask_autodoFlow = NULL;
|
||||
bool bTaskAutoFlowCreated = false;
|
||||
bool flowisrunning = false;
|
||||
|
||||
long auto_intervall = 0;
|
||||
long auto_interval = 0;
|
||||
bool auto_isrunning = false;
|
||||
|
||||
int countRounds = 0;
|
||||
@@ -44,20 +49,24 @@ static const char *TAG = "TFLITE SERVER";
|
||||
void CheckIsPlannedReboot()
|
||||
{
|
||||
FILE *pfile;
|
||||
if ((pfile = fopen("/sdcard/reboot.txt", "r")) == NULL)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Not a planned reboot.");
|
||||
if ((pfile = fopen("/sdcard/reboot.txt", "r")) == NULL) {
|
||||
//LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Initial boot or not a planned reboot");
|
||||
isPlannedReboot = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Planned reboot.");
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Planned reboot");
|
||||
DeleteFile("/sdcard/reboot.txt"); // Prevent Boot Loop!!!
|
||||
isPlannedReboot = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool getIsPlannedReboot()
|
||||
{
|
||||
return isPlannedReboot;
|
||||
}
|
||||
|
||||
|
||||
int getCountFlowRounds()
|
||||
{
|
||||
return countRounds;
|
||||
@@ -143,12 +152,12 @@ esp_err_t handler_get_heap(httpd_req_t *req)
|
||||
std::string zw = "Heap info:<br>" + getESPHeapInfo();
|
||||
|
||||
#ifdef TASK_ANALYSIS_ON
|
||||
char* pcTaskList = (char*) heap_caps_calloc(1, sizeof(char) * 768, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
||||
char* pcTaskList = (char*) calloc_psram_heap(std::string(TAG) + "->pcTaskList", 1, sizeof(char) * 768, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
||||
if (pcTaskList) {
|
||||
vTaskList(pcTaskList);
|
||||
zw = zw + "<br><br>Task info:<br><pre>Name | State | Prio | Lowest stacksize | Creation order | CPU (-1=NoAffinity)<br>"
|
||||
+ std::string(pcTaskList) + "</pre>";
|
||||
heap_caps_free(pcTaskList);
|
||||
free_psram_heap(std::string(TAG) + "->pcTaskList", pcTaskList);
|
||||
}
|
||||
else {
|
||||
zw = zw + "<br><br>Task info:<br>ERROR - Allocation of TaskList buffer in PSRAM failed";
|
||||
@@ -370,64 +379,109 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
zw = tfliteflow.getReadout(_rawValue, _noerror);
|
||||
if (zw.length() > 0)
|
||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||
|
||||
std::string *status = tfliteflow.getActStatus();
|
||||
string query = std::string(_query);
|
||||
// ESP_LOGD(TAG, "Query: %s, query.c_str());
|
||||
if (query.find("full") != std::string::npos)
|
||||
{
|
||||
string txt;
|
||||
txt = "<body style=\"font-family: arial\">";
|
||||
|
||||
if ((countRounds <= 1) && (*status != std::string("Flow finished"))) { // First round not completed yet
|
||||
txt += "<h3>Please wait for the first round to complete!</h3><h3>Current state: " + *status + "</h3>\n";
|
||||
}
|
||||
else {
|
||||
txt += "<h3>Value</h3>";
|
||||
}
|
||||
|
||||
httpd_resp_sendstr_chunk(req, txt.c_str());
|
||||
}
|
||||
|
||||
|
||||
zw = tfliteflow.getReadout(_rawValue, _noerror);
|
||||
if (zw.length() > 0)
|
||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||
|
||||
|
||||
if (query.find("full") != std::string::npos)
|
||||
{
|
||||
string txt, zw;
|
||||
|
||||
txt = "<p>Aligned Image: <p><img src=\"/img_tmp/alg_roi.jpg\"> <p>\n";
|
||||
txt = txt + "Digital Counter: <p> ";
|
||||
httpd_resp_sendstr_chunk(req, txt.c_str());
|
||||
|
||||
std::vector<HTMLInfo*> htmlinfodig;
|
||||
htmlinfodig = tfliteflow.GetAllDigital();
|
||||
|
||||
for (int i = 0; i < htmlinfodig.size(); ++i)
|
||||
{
|
||||
if (tfliteflow.GetTypeDigital() == Digital)
|
||||
if ((countRounds <= 1) && (*status != std::string("Flow finished"))) { // First round not completed yet
|
||||
// Nothing to do
|
||||
}
|
||||
else {
|
||||
/* Digital ROIs */
|
||||
txt = "<body style=\"font-family: arial\">";
|
||||
txt += "<h3>Recognized Digit ROIs (previous round)</h3>\n";
|
||||
txt += "<table style=\"border-spacing: 5px\"><tr style=\"text-align: center; vertical-align: top;\">\n";
|
||||
|
||||
std::vector<HTMLInfo*> htmlinfodig;
|
||||
htmlinfodig = tfliteflow.GetAllDigital();
|
||||
|
||||
for (int i = 0; i < htmlinfodig.size(); ++i)
|
||||
{
|
||||
if (htmlinfodig[i]->val == 10)
|
||||
zw = "NaN";
|
||||
else
|
||||
zw = to_string((int) htmlinfodig[i]->val);
|
||||
if (tfliteflow.GetTypeDigital() == Digital)
|
||||
{
|
||||
if (htmlinfodig[i]->val == 10)
|
||||
zw = "NaN";
|
||||
else
|
||||
zw = to_string((int) htmlinfodig[i]->val);
|
||||
|
||||
txt = "<img src=\"/img_tmp/" + htmlinfodig[i]->filename + "\"> " + zw;
|
||||
txt += "<td style=\"width: 100px\"><h4>" + zw + "</h4><p><img src=\"/img_tmp/" + htmlinfodig[i]->filename + "\"></p></td>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(1) << htmlinfodig[i]->val;
|
||||
zw = stream.str();
|
||||
|
||||
txt += "<td style=\"width: 100px\"><h4>" + zw + "</h4><p><img src=\"/img_tmp/" + htmlinfodig[i]->filename + "\"></p></td>\n";
|
||||
}
|
||||
delete htmlinfodig[i];
|
||||
}
|
||||
else
|
||||
|
||||
htmlinfodig.clear();
|
||||
|
||||
txt += "</tr></table>\n";
|
||||
httpd_resp_sendstr_chunk(req, txt.c_str());
|
||||
|
||||
|
||||
/* Analog ROIs */
|
||||
txt = "<h3>Recognized Analog ROIs (previous round)</h3>\n";
|
||||
txt += "<table style=\"border-spacing: 5px\"><tr style=\"text-align: center; vertical-align: top;\">\n";
|
||||
|
||||
std::vector<HTMLInfo*> htmlinfoana;
|
||||
htmlinfoana = tfliteflow.GetAllAnalog();
|
||||
for (int i = 0; i < htmlinfoana.size(); ++i)
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(1) << htmlinfodig[i]->val;
|
||||
stream << std::fixed << std::setprecision(1) << htmlinfoana[i]->val;
|
||||
zw = stream.str();
|
||||
|
||||
txt = "<img src=\"/img_tmp/" + htmlinfodig[i]->filename + "\"> " + zw;
|
||||
txt += "<td style=\"width: 150px;\"><h4>" + zw + "</h4><p><img src=\"/img_tmp/" + htmlinfoana[i]->filename + "\"></p></td>\n";
|
||||
delete htmlinfoana[i];
|
||||
}
|
||||
htmlinfoana.clear();
|
||||
|
||||
txt += "</tr>\n</table>\n";
|
||||
httpd_resp_sendstr_chunk(req, txt.c_str());
|
||||
|
||||
|
||||
/* Full Image
|
||||
* Only show it after the image got taken and aligned */
|
||||
txt = "<h3>Aligned Image (current round)</h3>\n";
|
||||
if ((*status == std::string("Initialization")) ||
|
||||
(*status == std::string("Initialization (delayed)")) ||
|
||||
(*status == std::string("Take Image"))) {
|
||||
txt += "<p>Current state: " + *status + "</p>\n";
|
||||
}
|
||||
else {
|
||||
txt += "<img src=\"/img_tmp/alg_roi.jpg\">\n";
|
||||
}
|
||||
httpd_resp_sendstr_chunk(req, txt.c_str());
|
||||
delete htmlinfodig[i];
|
||||
}
|
||||
htmlinfodig.clear();
|
||||
|
||||
txt = " <p> Analog Meter: <p> ";
|
||||
httpd_resp_sendstr_chunk(req, txt.c_str());
|
||||
|
||||
std::vector<HTMLInfo*> htmlinfoana;
|
||||
htmlinfoana = tfliteflow.GetAllAnalog();
|
||||
for (int i = 0; i < htmlinfoana.size(); ++i)
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(1) << htmlinfoana[i]->val;
|
||||
zw = stream.str();
|
||||
|
||||
txt = "<img src=\"/img_tmp/" + htmlinfoana[i]->filename + "\"> " + zw;
|
||||
httpd_resp_sendstr_chunk(req, txt.c_str());
|
||||
delete htmlinfoana[i];
|
||||
}
|
||||
htmlinfoana.clear();
|
||||
|
||||
}
|
||||
|
||||
/* Respond with an empty chunk to signal HTTP response completion */
|
||||
@@ -564,11 +618,11 @@ esp_err_t handler_editflow(httpd_req_t *req)
|
||||
|
||||
string out2 = out.substr(0, out.length() - 4) + "_org.jpg";
|
||||
|
||||
CAlignAndCutImage *caic = new CAlignAndCutImage(in);
|
||||
CAlignAndCutImage *caic = new CAlignAndCutImage("cutref", in);
|
||||
caic->CutAndSave(out2, x, y, dx, dy);
|
||||
delete caic;
|
||||
|
||||
CImageBasis *cim = new CImageBasis(out2);
|
||||
CImageBasis *cim = new CImageBasis("cutref", out2);
|
||||
if (enhance)
|
||||
{
|
||||
cim->Contrast(90);
|
||||
@@ -620,8 +674,8 @@ esp_err_t handler_editflow(httpd_req_t *req)
|
||||
// string zwzw = "Do " + _task + " start\n"; ESP_LOGD(TAG, zwzw.c_str());
|
||||
Camera.SetBrightnessContrastSaturation(bri, con, sat);
|
||||
Camera.SetLEDIntensity(intens);
|
||||
ESP_LOGD(TAG, "test_take - vor MakeImage");
|
||||
std::string zw = tfliteflow.doSingleStep("[MakeImage]", _host);
|
||||
ESP_LOGD(TAG, "test_take - vor TakeImage");
|
||||
std::string zw = tfliteflow.doSingleStep("[TakeImage]", _host);
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
httpd_resp_send(req, zw.c_str(), zw.length());
|
||||
}
|
||||
@@ -664,7 +718,7 @@ esp_err_t handler_statusflow(httpd_req_t *req)
|
||||
ESP_LOGD(TAG, "handler_prevalue: %s", req->uri);
|
||||
#endif
|
||||
|
||||
string* zw = tfliteflow.getActStatus();
|
||||
string* zw = tfliteflow.getActStatusWithTime();
|
||||
resp_str = zw->c_str();
|
||||
|
||||
httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN);
|
||||
@@ -807,31 +861,25 @@ void task_autodoFlow(void *pvParameter)
|
||||
|
||||
bTaskAutoFlowCreated = true;
|
||||
|
||||
if (!isPlannedReboot)
|
||||
if (!isPlannedReboot && (esp_reset_reason() == ESP_RST_PANIC))
|
||||
{
|
||||
if (esp_reset_reason() == ESP_RST_PANIC) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Restarted due to an Exception/panic! Postponing first round start by 5 minutes to allow for an OTA Update or to fetch the log!");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Setting logfile level to DEBUG until the next reboot!");
|
||||
LogFile.setLogLevel(ESP_LOG_DEBUG);
|
||||
tfliteflow.setActStatus("Initialization (delayed)");
|
||||
//#ifdef ENABLE_MQTT
|
||||
//MQTTPublish(mqttServer_getMainTopic() + "/" + "status", "Initialization (delayed)", false); // Right now, not possible -> MQTT Service is going to be started later
|
||||
//#endif //ENABLE_MQTT
|
||||
vTaskDelay(60*5000 / portTICK_RATE_MS); // Wait 5 minutes to give time to do an OTA Update or fetch the log
|
||||
}
|
||||
tfliteflow.setActStatus("Initialization (delayed)");
|
||||
//#ifdef ENABLE_MQTT
|
||||
//MQTTPublish(mqttServer_getMainTopic() + "/" + "status", "Initialization (delayed)", false); // Right now, not possible -> MQTT Service is going to be started later
|
||||
//#endif //ENABLE_MQTT
|
||||
vTaskDelay(60*5000 / portTICK_PERIOD_MS); // Wait 5 minutes to give time to do an OTA update or fetch the log
|
||||
}
|
||||
|
||||
|
||||
ESP_LOGD(TAG, "task_autodoFlow: start");
|
||||
doInit();
|
||||
|
||||
auto_isrunning = tfliteflow.isAutoStart(auto_intervall);
|
||||
auto_isrunning = tfliteflow.isAutoStart(auto_interval);
|
||||
|
||||
if (isSetupModusActive())
|
||||
{
|
||||
auto_isrunning = false;
|
||||
std::string zw_time = getCurrentTimeString(LOGFILE_TIME_FORMAT);
|
||||
tfliteflow.doFlowMakeImageOnly(zw_time);
|
||||
tfliteflow.doFlowTakeImageOnly(zw_time);
|
||||
}
|
||||
|
||||
while (auto_isrunning)
|
||||
@@ -861,21 +909,37 @@ void task_autodoFlow(void *pvParameter)
|
||||
LogFile.RemoveOldLogFile();
|
||||
LogFile.RemoveOldDataLog();
|
||||
}
|
||||
|
||||
// Round finished -> Logfile
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Round #" + std::to_string(countRounds) +
|
||||
" completed (" + std::to_string(getUpTime() - roundStartTime) + " seconds)");
|
||||
|
||||
//CPU Temp -> Logfile
|
||||
// CPU Temp -> Logfile
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CPU Temperature: " + std::to_string((int)temperatureRead()) + "°C");
|
||||
|
||||
// WIFI Signal Strength (RSSI) -> Logfile
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "WIFI Signal (RSSI): " + std::to_string(get_WIFI_RSSI()) + "dBm");
|
||||
|
||||
//Round finished -> Logfile
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Round #" + std::to_string(countRounds) +
|
||||
" completed (" + std::to_string(getUpTime() - roundStartTime) + " seconds)");
|
||||
|
||||
// Check if time is synchronized (if NTP is configured)
|
||||
if (getUseNtp() && !getTimeIsSet()) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Time server is configured, but time is not yet set!");
|
||||
StatusLED(TIME_CHECK, 1, false);
|
||||
}
|
||||
|
||||
#if (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES)
|
||||
wifiRoamingQuery();
|
||||
#endif
|
||||
|
||||
// Scan channels and check if an AP with better RSSI is available, then disconnect and try to reconnect to AP with better RSSI
|
||||
// NOTE: Keep this direct before the following task delay, because scan is done in blocking mode and this takes ca. 1,5 - 2s.
|
||||
#ifdef WLAN_USE_ROAMING_BY_SCANNING
|
||||
wifiRoamByScanning();
|
||||
#endif
|
||||
|
||||
fr_delta_ms = (esp_timer_get_time() - fr_start) / 1000;
|
||||
if (auto_intervall > fr_delta_ms)
|
||||
if (auto_interval > fr_delta_ms)
|
||||
{
|
||||
const TickType_t xDelay = (auto_intervall - fr_delta_ms) / portTICK_PERIOD_MS;
|
||||
const TickType_t xDelay = (auto_interval - fr_delta_ms) / portTICK_PERIOD_MS;
|
||||
ESP_LOGD(TAG, "Autoflow: sleep for: %ldms", (long) xDelay);
|
||||
vTaskDelay( xDelay );
|
||||
}
|
||||
@@ -892,11 +956,12 @@ void TFliteDoAutoStart()
|
||||
|
||||
ESP_LOGD(TAG, "getESPHeapInfo: %s", getESPHeapInfo().c_str());
|
||||
|
||||
xReturned = xTaskCreatePinnedToCore(&task_autodoFlow, "task_autodoFlow", 16 * 1024, NULL, tskIDLE_PRIORITY+2, &xHandletask_autodoFlow, 0);
|
||||
//xReturned = xTaskCreate(&task_autodoFlow, "task_autodoFlow", 16 * 1024, NULL, tskIDLE_PRIORITY+2, &xHandletask_autodoFlow);
|
||||
uint32_t stackSize = 16 * 1024;
|
||||
xReturned = xTaskCreatePinnedToCore(&task_autodoFlow, "task_autodoFlow", stackSize, NULL, tskIDLE_PRIORITY+2, &xHandletask_autodoFlow, 0);
|
||||
if( xReturned != pdPASS )
|
||||
{
|
||||
ESP_LOGD(TAG, "ERROR task_autodoFlow konnte nicht erzeugt werden!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Creation task_autodoFlow failed. Requested stack size:" + std::to_string(stackSize));
|
||||
LogFile.WriteHeapInfo("Creation task_autodoFlow failed");
|
||||
}
|
||||
ESP_LOGD(TAG, "getESPHeapInfo: %s", getESPHeapInfo().c_str());
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ bool isSetupModusActive();
|
||||
int getCountFlowRounds();
|
||||
|
||||
void CheckIsPlannedReboot();
|
||||
bool getIsPlannedReboot();
|
||||
|
||||
esp_err_t GetJPG(std::string _filename, httpd_req_t *req);
|
||||
esp_err_t GetRawJPG(httpd_req_t *req);
|
||||
|
||||
@@ -25,6 +25,8 @@ static const char *TAG = "SNTP";
|
||||
static std::string timeZone = "";
|
||||
static std::string timeServer = "undefined";
|
||||
static bool useNtp = true;
|
||||
static bool timeWasNotSetAtBoot = false;
|
||||
static bool timeWasNotSetAtBoot_PrintStartBlock = false;
|
||||
|
||||
std::string getNtpStatusText(sntp_sync_status_t status);
|
||||
static void setTimeZone(std::string _tzstring);
|
||||
@@ -59,7 +61,13 @@ std::string getCurrentTimeString(const char * frm)
|
||||
|
||||
void time_sync_notification_cb(struct timeval *tv)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Time is now successfully synced with NTP Server " +
|
||||
if (timeWasNotSetAtBoot_PrintStartBlock) {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "=================================================");
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "==================== Start ======================");
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "== Logs before time sync -> log_1970-01-01.txt ==");
|
||||
timeWasNotSetAtBoot_PrintStartBlock = false;
|
||||
}
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Time is synced with NTP Server " +
|
||||
getServerName() + ": " + getCurrentTimeString("%Y-%m-%d %H:%M:%S"));
|
||||
}
|
||||
|
||||
@@ -111,6 +119,11 @@ bool getUseNtp(void) {
|
||||
return useNtp;
|
||||
}
|
||||
|
||||
bool getTimeWasNotSetAtBoot(void)
|
||||
{
|
||||
return timeWasNotSetAtBoot;
|
||||
}
|
||||
|
||||
|
||||
std::string getServerName(void) {
|
||||
char buf[100];
|
||||
@@ -140,7 +153,7 @@ bool setupTime() {
|
||||
ConfigFile configFile = ConfigFile(CONFIG_FILE);
|
||||
|
||||
if (!configFile.ConfigFileExists()){
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "No ConfigFile defined - exit setupTime() ");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "No ConfigFile defined - exit setupTime()!");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -162,10 +175,15 @@ bool setupTime() {
|
||||
|
||||
while (configFile.getNextLine(&line, disabledLine, eof) &&
|
||||
!configFile.isNewParagraph(line)) {
|
||||
splitted = ZerlegeZeile(line);
|
||||
splitted = ZerlegeZeile(line, "=");
|
||||
|
||||
if (toUpper(splitted[0]) == "TIMEZONE") {
|
||||
timeZone = splitted[1];
|
||||
if (splitted.size() <= 1) { // parameter part is empty
|
||||
timeZone = "";
|
||||
}
|
||||
else {
|
||||
timeZone = splitted[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (toUpper(splitted[0]) == "TIMESERVER") {
|
||||
@@ -225,6 +243,8 @@ bool setupTime() {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "The local time is unknown, starting with " + std::string(strftime_buf));
|
||||
if (useNtp) {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Once the NTP server provides a time, we will switch to that one");
|
||||
timeWasNotSetAtBoot = true;
|
||||
timeWasNotSetAtBoot_PrintStartBlock = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ std::string ConvertTimeToString(time_t _time, const char * frm);
|
||||
|
||||
|
||||
bool getTimeIsSet(void);
|
||||
bool getTimeWasNotSetAtBoot(void);
|
||||
|
||||
bool getUseNtp(void);
|
||||
bool setupTime();
|
||||
|
||||
@@ -1,12 +1,24 @@
|
||||
#include "connect_wlan.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_wnm.h"
|
||||
#include "esp_rrm.h"
|
||||
#include "esp_mbo.h"
|
||||
#include "esp_mac.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
@@ -17,72 +29,41 @@
|
||||
#include "interface_mqtt.h"
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include "ClassLogFile.h"
|
||||
#include "read_wlanini.h"
|
||||
#include "Helper.h"
|
||||
#include "statusled.h"
|
||||
|
||||
//////////////////////
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_wnm.h"
|
||||
#include "esp_rrm.h"
|
||||
#include "esp_mbo.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_mac.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_netif.h"
|
||||
|
||||
|
||||
/////////////////////
|
||||
#include "../../include/defines.h"
|
||||
|
||||
|
||||
/* FreeRTOS event group to signal when we are connected*/
|
||||
static EventGroupHandle_t s_wifi_event_group;
|
||||
|
||||
static const char *TAG = "WIFI";
|
||||
|
||||
static int s_retry_num = 0;
|
||||
bool WIFIConnected = false;
|
||||
static bool APWithBetterRSSI = false;
|
||||
static bool WIFIConnected = false;
|
||||
static int WIFIReconnectCnt = 0;
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
int BlinkDauer;
|
||||
int BlinkAnzahl;
|
||||
bool BlinkOff;
|
||||
bool BlinkIsRunning = false;
|
||||
|
||||
std::string hostname = "";
|
||||
std::string std_hostname = "watermeter";
|
||||
std::string ipadress = "";
|
||||
std::string ssid = "";
|
||||
int RSSIThreashold;
|
||||
void strinttoip4(const char *ip, int &a, int &b, int &c, int &d) {
|
||||
std::string zw = std::string(ip);
|
||||
std::stringstream s(zw);
|
||||
char ch; //to temporarily store the '.'
|
||||
s >> a >> ch >> b >> ch >> c >> ch >> d;
|
||||
}
|
||||
|
||||
|
||||
std::string BssidToString(const char* c) {
|
||||
char cBssid[25];
|
||||
sprintf(cBssid, "%02x:%02x:%02x:%02x:%02x:%02x", c[0], c[1], c[2], c[3], c[4], c[5]);
|
||||
return std::string(cBssid);
|
||||
}
|
||||
|
||||
/////////////////////////////////
|
||||
/////////////////////////////////
|
||||
|
||||
#ifdef WLAN_USE_MESH_ROAMING
|
||||
|
||||
int RSSI_Threshold = WLAN_WIFI_RSSI_THRESHOLD;
|
||||
|
||||
/* rrm ctx */
|
||||
int rrm_ctx = 0;
|
||||
|
||||
/* FreeRTOS event group to signal when we are connected & ready to make a request */
|
||||
static EventGroupHandle_t wifi_event_group;
|
||||
|
||||
/* esp netif object representing the WIFI station */
|
||||
static esp_netif_t *sta_netif = NULL;
|
||||
|
||||
//static const char *TAG = "roaming_example";
|
||||
|
||||
static inline uint32_t WPA_GET_LE32(const uint8_t *a)
|
||||
{
|
||||
return ((uint32_t) a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
|
||||
@@ -105,6 +86,7 @@ static inline uint32_t WPA_GET_LE32(const uint8_t *a)
|
||||
#define ETH_ALEN 6
|
||||
#endif
|
||||
|
||||
|
||||
#define MAX_NEIGHBOR_LEN 512
|
||||
static char * get_btm_neighbor_list(uint8_t *report, size_t report_len)
|
||||
{
|
||||
@@ -121,10 +103,10 @@ static char * get_btm_neighbor_list(uint8_t *report, size_t report_len)
|
||||
* PHY Type[1]
|
||||
* Optional Subelements[variable]
|
||||
*/
|
||||
#define NR_IE_MIN_LEN (ETH_ALEN + 4 + 1 + 1 + 1)
|
||||
#define NR_IE_MIN_LEN (ETH_ALEN + 4 + 1 + 1 + 1)
|
||||
|
||||
if (!report || report_len == 0) {
|
||||
ESP_LOGI(TAG, "RRM neighbor report is not valid");
|
||||
ESP_LOGD(TAG, "Roaming: RRM neighbor report is not valid");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -140,14 +122,14 @@ static char * get_btm_neighbor_list(uint8_t *report, size_t report_len)
|
||||
|
||||
if (pos[0] != WLAN_EID_NEIGHBOR_REPORT ||
|
||||
nr_len < NR_IE_MIN_LEN) {
|
||||
ESP_LOGI(TAG, "CTRL: Invalid Neighbor Report element: id=%u len=%u",
|
||||
ESP_LOGD(TAG, "Roaming CTRL: Invalid Neighbor Report element: id=%u len=%u",
|
||||
data[0], nr_len);
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (2U + nr_len > report_len) {
|
||||
ESP_LOGI(TAG, "CTRL: Invalid Neighbor Report element: id=%u len=%zu nr_len=%u",
|
||||
ESP_LOGD(TAG, "Roaming CTRL: Invalid Neighbor Report element: id=%u len=%zu nr_len=%u",
|
||||
data[0], report_len, nr_len);
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
@@ -190,8 +172,8 @@ static char * get_btm_neighbor_list(uint8_t *report, size_t report_len)
|
||||
|
||||
pos += s_len;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "RMM neigbor report bssid=" MACSTR
|
||||
|
||||
ESP_LOGI(TAG, "Roaming: RMM neigbor report bssid=" MACSTR
|
||||
" info=0x%x op_class=%u chan=%u phy_type=%u%s%s%s%s",
|
||||
MAC2STR(nr), WPA_GET_LE32(nr + ETH_ALEN),
|
||||
nr[ETH_ALEN + 4], nr[ETH_ALEN + 5],
|
||||
@@ -199,6 +181,10 @@ static char * get_btm_neighbor_list(uint8_t *report, size_t report_len)
|
||||
lci[0] ? " lci=" : "", lci,
|
||||
civic[0] ? " civic=" : "", civic);
|
||||
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: RMM neigbor report BSSID: " + BssidToString((char*)nr) +
|
||||
", Channel: " + std::to_string(nr[ETH_ALEN + 5]));
|
||||
|
||||
/* neighbor start */
|
||||
len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, " neighbor=");
|
||||
/* bssid */
|
||||
@@ -236,18 +222,19 @@ void neighbor_report_recv_cb(void *ctx, const uint8_t *report, size_t report_len
|
||||
int *val = (int*) ctx;
|
||||
uint8_t *pos = (uint8_t *)report;
|
||||
int cand_list = 0;
|
||||
int ret;
|
||||
|
||||
if (!report) {
|
||||
ESP_LOGE(TAG, "report is null");
|
||||
ESP_LOGD(TAG, "Roaming: Neighbor report is null");
|
||||
return;
|
||||
}
|
||||
if (*val != rrm_ctx) {
|
||||
ESP_LOGE(TAG, "rrm_ctx value didn't match, not initiated by us");
|
||||
ESP_LOGE(TAG, "Roaming: rrm_ctx value didn't match, not initiated by us");
|
||||
return;
|
||||
}
|
||||
/* dump report info */
|
||||
ESP_LOGI(TAG, "rrm: neighbor report len=%d", report_len);
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, pos, report_len, ESP_LOG_INFO);
|
||||
ESP_LOGD(TAG, "Roaming: RRM neighbor report len=%d", report_len);
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, pos, report_len, ESP_LOG_DEBUG);
|
||||
|
||||
/* create neighbor list */
|
||||
char *neighbor_list = get_btm_neighbor_list(pos + 1, report_len - 1);
|
||||
@@ -266,8 +253,10 @@ void neighbor_report_recv_cb(void *ctx, const uint8_t *report, size_t report_len
|
||||
esp_wifi_scan_get_ap_records(&number, &ap_records);
|
||||
cand_list = 1;
|
||||
}
|
||||
/* send AP btm query, this will cause STA to roam as well */
|
||||
esp_wnm_send_bss_transition_mgmt_query(REASON_FRAME_LOSS, neighbor_list, cand_list);
|
||||
/* send AP btm query requesting to roam depending on candidate list of AP */
|
||||
// btm_query_reasons: https://github.com/espressif/esp-idf/blob/release/v4.4/components/wpa_supplicant/esp_supplicant/include/esp_wnm.h
|
||||
ret = esp_wnm_send_bss_transition_mgmt_query(REASON_FRAME_LOSS, neighbor_list, cand_list); // query reason 16 -> LOW RSSI --> (btm_query_reason)16
|
||||
ESP_LOGD(TAG, "neighbor_report_recv_cb retval - bss_transisition_query: %d", ret);
|
||||
|
||||
cleanup:
|
||||
if (neighbor_list)
|
||||
@@ -275,276 +264,399 @@ cleanup:
|
||||
}
|
||||
|
||||
|
||||
static void esp_bss_rssi_low_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
static void esp_bss_rssi_low_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
|
||||
{
|
||||
int retval = -1;
|
||||
wifi_event_bss_rssi_low_t *event = (wifi_event_bss_rssi_low_t*) event_data;
|
||||
|
||||
ESP_LOGI(TAG, "%s:bss rssi is=%d", __func__, event->rssi);
|
||||
/* Lets check channel conditions */
|
||||
rrm_ctx++;
|
||||
if (esp_rrm_send_neighbor_rep_request(neighbor_report_recv_cb, &rrm_ctx) < 0) {
|
||||
/* failed to send neighbor report request */
|
||||
ESP_LOGI(TAG, "failed to send neighbor report request");
|
||||
if (esp_wnm_send_bss_transition_mgmt_query(REASON_FRAME_LOSS, NULL, 0) < 0) {
|
||||
ESP_LOGI(TAG, "failed to send btm query");
|
||||
}
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming Event: RSSI " + std::to_string(event->rssi) +
|
||||
" < RSSI_Threshold " + std::to_string(wlan_config.rssi_threshold));
|
||||
|
||||
/* If RRM is supported, call RRM and then send BTM query to AP */
|
||||
if (esp_rrm_is_rrm_supported_connection() && esp_wnm_is_btm_supported_connection())
|
||||
{
|
||||
/* Lets check channel conditions */
|
||||
rrm_ctx++;
|
||||
|
||||
retval = esp_rrm_send_neighbor_rep_request(neighbor_report_recv_cb, &rrm_ctx);
|
||||
ESP_LOGD(TAG, "esp_rrm_send_neighbor_rep_request retval: %d", retval);
|
||||
if (retval == 0)
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: RRM + BTM query sent");
|
||||
else
|
||||
ESP_LOGD(TAG, "esp_rrm_send_neighbor_rep_request retval: %d", retval);
|
||||
}
|
||||
|
||||
/* If RRM is not supported or RRM request failed, send directly BTM query to AP */
|
||||
if (retval < 0 && esp_wnm_is_btm_supported_connection())
|
||||
{
|
||||
// btm_query_reasons: https://github.com/espressif/esp-idf/blob/release/v4.4/components/wpa_supplicant/esp_supplicant/include/esp_wnm.h
|
||||
retval = esp_wnm_send_bss_transition_mgmt_query(REASON_FRAME_LOSS, NULL, 0); // query reason 16 -> LOW RSSI --> (btm_query_reason)16
|
||||
ESP_LOGD(TAG, "esp_wnm_send_bss_transition_mgmt_query retval: %d", retval);
|
||||
if (retval == 0)
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: BTM query sent");
|
||||
else
|
||||
ESP_LOGD(TAG, "esp_wnm_send_bss_transition_mgmt_query retval: %d", retval);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
void printRoamingFeatureSupport(void)
|
||||
{
|
||||
if (esp_rrm_is_rrm_supported_connection())
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Roaming: RRM (802.11k) supported by AP");
|
||||
else
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Roaming: RRM (802.11k) NOT supported by AP");
|
||||
|
||||
//////////////////////////////////
|
||||
//////////////////////////////////
|
||||
if (esp_wnm_is_btm_supported_connection())
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Roaming: BTM (802.11v) supported by AP");
|
||||
else
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Roaming: BTM (802.11v) NOT supported by AP");
|
||||
}
|
||||
|
||||
|
||||
#ifdef WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES
|
||||
void wifiRoamingQuery(void)
|
||||
{
|
||||
/* Query only if WIFI is connected and feature is supported by AP */
|
||||
if (WIFIConnected && (esp_rrm_is_rrm_supported_connection() || esp_wnm_is_btm_supported_connection())) {
|
||||
/* Client is allowed to send query to AP for roaming request if RSSI is lower than threshold */
|
||||
/* Note 1: Set RSSI threshold funtion needs to be called to trigger WIFI_EVENT_STA_BSS_RSSI_LOW */
|
||||
/* Note 2: Additional querys will be sent after flow round is finshed --> server_tflite.cpp - function "task_autodoFlow" */
|
||||
/* Note 3: RSSI_Threshold = 0 --> Disable client query by application (WebUI parameter) */
|
||||
|
||||
if (wlan_config.rssi_threshold != 0 && get_WIFI_RSSI() != -127 && (get_WIFI_RSSI() < wlan_config.rssi_threshold))
|
||||
esp_wifi_set_rssi_threshold(wlan_config.rssi_threshold);
|
||||
}
|
||||
}
|
||||
#endif // WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES
|
||||
#endif // WLAN_USE_MESH_ROAMING
|
||||
|
||||
|
||||
#ifdef WLAN_USE_ROAMING_BY_SCANNING
|
||||
std::string getAuthModeName(const wifi_auth_mode_t auth_mode)
|
||||
{
|
||||
std::string AuthModeNames[] = {"OPEN", "WEP", "WPA PSK", "WPA2 PSK", "WPA WPA2 PSK", "WPA2 ENTERPRISE",
|
||||
"WPA3 PSK", "WPA2 WPA3 PSK", "WAPI_PSK", "MAX"};
|
||||
return AuthModeNames[auth_mode];
|
||||
}
|
||||
|
||||
|
||||
void wifi_scan(void)
|
||||
{
|
||||
wifi_scan_config_t wifi_scan_config;
|
||||
memset(&wifi_scan_config, 0, sizeof(wifi_scan_config));
|
||||
|
||||
wifi_scan_config.ssid = (uint8_t*)wlan_config.ssid.c_str(); // only scan for configured SSID
|
||||
wifi_scan_config.show_hidden = true; // scan also hidden SSIDs
|
||||
wifi_scan_config.channel = 0; // scan all channels
|
||||
|
||||
esp_wifi_scan_start(&wifi_scan_config, true); // not using event handler SCAN_DONE by purpose to keep SYS_EVENT heap smaller
|
||||
// and the calling task task_autodoFlow is after scan is finish in wait state anyway
|
||||
// Scan duration: ca. (120ms + 30ms) * Number of channels -> ca. 1,5 - 2s
|
||||
|
||||
uint16_t max_number_of_ap_found = 10; // max. number of APs, value will be updated by function "esp_wifi_scan_get_ap_num"
|
||||
esp_wifi_scan_get_ap_num(&max_number_of_ap_found); // get actual found APs
|
||||
wifi_ap_record_t* wifi_ap_records = new wifi_ap_record_t[max_number_of_ap_found]; // Allocate necessary record datasets
|
||||
if (wifi_ap_records == NULL) {
|
||||
esp_wifi_scan_get_ap_records(0, NULL); // free internal heap
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "wifi_scan: Failed to allocate heap for wifi_ap_records");
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (esp_wifi_scan_get_ap_records(&max_number_of_ap_found, wifi_ap_records) != ESP_OK) { // Retrieve results (and free internal heap)
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "wifi_scan: esp_wifi_scan_get_ap_records: Error retrieving datasets");
|
||||
free(wifi_ap_records);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
wifi_ap_record_t currentAP;
|
||||
esp_wifi_sta_get_ap_info(¤tAP);
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: Current AP BSSID=" + BssidToString((char*)currentAP.bssid));
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: Scan completed, APs found with configured SSID: " + std::to_string(max_number_of_ap_found));
|
||||
for (int i = 0; i < max_number_of_ap_found; i++) {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: " + std::to_string(i+1) +
|
||||
": SSID=" + std::string((char*)wifi_ap_records[i].ssid) +
|
||||
", BSSID=" + BssidToString((char*)wifi_ap_records[i].bssid) +
|
||||
", RSSI=" + std::to_string(wifi_ap_records[i].rssi) +
|
||||
", CH=" + std::to_string(wifi_ap_records[i].primary) +
|
||||
", AUTH=" + getAuthModeName(wifi_ap_records[i].authmode));
|
||||
if (wifi_ap_records[i].rssi > (currentAP.rssi + 5) && // RSSI is better than actual RSSI + 5 --> Avoid switching to AP with roughly same RSSI
|
||||
(strcmp(BssidToString((char*)wifi_ap_records[i].bssid).c_str(), BssidToString((char*)currentAP.bssid).c_str()) != 0))
|
||||
{
|
||||
APWithBetterRSSI = true;
|
||||
}
|
||||
}
|
||||
free(wifi_ap_records);
|
||||
}
|
||||
|
||||
|
||||
void wifiRoamByScanning(void)
|
||||
{
|
||||
if (wlan_config.rssi_threshold != 0 && get_WIFI_RSSI() != -127 && (get_WIFI_RSSI() < wlan_config.rssi_threshold)) {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: Start scan of all channels for SSID " + wlan_config.ssid);
|
||||
wifi_scan();
|
||||
|
||||
if (APWithBetterRSSI) {
|
||||
APWithBetterRSSI = false;
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Roaming: AP with better RSSI in range, disconnecting to switch AP...");
|
||||
esp_wifi_disconnect();
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: Scan completed, stay on current AP");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // WLAN_USE_ROAMING_BY_SCANNING
|
||||
|
||||
|
||||
std::string* getIPAddress()
|
||||
{
|
||||
return &ipadress;
|
||||
return &wlan_config.ipaddress;
|
||||
}
|
||||
|
||||
|
||||
std::string* getSSID()
|
||||
{
|
||||
return &ssid;
|
||||
return &wlan_config.ssid;
|
||||
}
|
||||
|
||||
|
||||
void task_doBlink(void *pvParameter)
|
||||
static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
|
||||
{
|
||||
ESP_LOGI("BLINK", "Flash - start");
|
||||
while (BlinkIsRunning)
|
||||
{
|
||||
// ESP_LOGI("BLINK", "Blinken - wait");
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
BlinkIsRunning = true;
|
||||
|
||||
// Init the GPIO
|
||||
gpio_pad_select_gpio(BLINK_GPIO);
|
||||
/* Set the GPIO as a push/pull output */
|
||||
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
|
||||
|
||||
for (int i = 0; i < BlinkAnzahl; ++i)
|
||||
{
|
||||
if (BlinkAnzahl > 1)
|
||||
{
|
||||
gpio_set_level(BLINK_GPIO, 1);
|
||||
vTaskDelay(BlinkDauer / portTICK_PERIOD_MS);
|
||||
}
|
||||
gpio_set_level(BLINK_GPIO, 0);
|
||||
vTaskDelay(BlinkDauer / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
if (BlinkOff)
|
||||
gpio_set_level(BLINK_GPIO, 1);
|
||||
|
||||
ESP_LOGI("BLINK", "Flash - done");
|
||||
BlinkIsRunning = false;
|
||||
|
||||
vTaskDelete(NULL); //Delete this task if it exits from the loop above
|
||||
}
|
||||
|
||||
|
||||
void LEDBlinkTask(int _dauer, int _anz, bool _off)
|
||||
{
|
||||
BlinkDauer = _dauer;
|
||||
BlinkAnzahl = _anz;
|
||||
BlinkOff = _off;
|
||||
|
||||
xTaskCreate(&task_doBlink, "task_doBlink", 4 * 1024, NULL, tskIDLE_PRIORITY+1, NULL);
|
||||
}
|
||||
/////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
static void event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
|
||||
{
|
||||
WIFIConnected = false;
|
||||
LEDBlinkTask(200, 1, true);
|
||||
esp_wifi_connect();
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
WIFIConnected = false;
|
||||
// if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
|
||||
esp_wifi_connect();
|
||||
s_retry_num++;
|
||||
ESP_LOGI(TAG, "retrying connection to the AP");
|
||||
// } else {
|
||||
// xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||
// }
|
||||
ESP_LOGI(TAG,"connection to the AP failed");
|
||||
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
|
||||
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
|
||||
ipadress = std::string(ip4addr_ntoa((const ip4_addr*) &event->ip_info.ip));
|
||||
s_retry_num = 0;
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||
LEDBlinkTask(1000, 5, true);
|
||||
}
|
||||
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
|
||||
{
|
||||
/* Disconnect reason: https://github.com/espressif/esp-idf/blob/d825753387c1a64463779bbd2369e177e5d59a79/components/esp_wifi/include/esp_wifi_types.h */
|
||||
wifi_event_sta_disconnected_t *disconn = (wifi_event_sta_disconnected_t *)event_data;
|
||||
if (disconn->reason == WIFI_REASON_ROAMING) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected (" + std::to_string(disconn->reason) + ", Roaming 802.11kv)");
|
||||
// --> no reconnect neccessary, it should automatically reconnect to new AP
|
||||
}
|
||||
else {
|
||||
WIFIConnected = false;
|
||||
if (disconn->reason == WIFI_REASON_NO_AP_FOUND) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected (" + std::to_string(disconn->reason) + ", No AP)");
|
||||
StatusLED(WLAN_CONN, 1, false);
|
||||
}
|
||||
else if (disconn->reason == WIFI_REASON_AUTH_EXPIRE ||
|
||||
disconn->reason == WIFI_REASON_AUTH_FAIL ||
|
||||
disconn->reason == WIFI_REASON_NOT_AUTHED ||
|
||||
disconn->reason == WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT ||
|
||||
disconn->reason == WIFI_REASON_HANDSHAKE_TIMEOUT) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected (" + std::to_string(disconn->reason) + ", Auth fail)");
|
||||
StatusLED(WLAN_CONN, 2, false);
|
||||
}
|
||||
else if (disconn->reason == WIFI_REASON_BEACON_TIMEOUT) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected (" + std::to_string(disconn->reason) + ", Timeout)");
|
||||
StatusLED(WLAN_CONN, 3, false);
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected (" + std::to_string(disconn->reason) + ")");
|
||||
StatusLED(WLAN_CONN, 4, false);
|
||||
}
|
||||
WIFIReconnectCnt++;
|
||||
esp_wifi_connect(); // Try to connect again
|
||||
}
|
||||
|
||||
if (WIFIReconnectCnt >= 10) {
|
||||
WIFIReconnectCnt = 0;
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Disconnected, multiple reconnect attempts failed (" +
|
||||
std::to_string(disconn->reason) + "), still retrying...");
|
||||
}
|
||||
}
|
||||
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Connected to: " + wlan_config.ssid + ", RSSI: " +
|
||||
std::to_string(get_WIFI_RSSI()));
|
||||
|
||||
#ifdef WLAN_USE_MESH_ROAMING
|
||||
printRoamingFeatureSupport();
|
||||
|
||||
#ifdef WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES
|
||||
// wifiRoamingQuery(); // Avoid client triggered query during processing flow (reduce risk of heap shortage). Request will be triggered at the end of every round anyway
|
||||
#endif //WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES
|
||||
|
||||
#endif //WLAN_USE_MESH_ROAMING
|
||||
}
|
||||
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
|
||||
{
|
||||
WIFIConnected = true;
|
||||
#ifdef ENABLE_MQTT
|
||||
WIFIReconnectCnt = 0;
|
||||
|
||||
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
|
||||
wlan_config.ipaddress = std::string(ip4addr_ntoa((const ip4_addr*) &event->ip_info.ip));
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Assigned IP: " + wlan_config.ipaddress);
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
if (getMQTTisEnabled()) {
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
MQTT_Init(); // Init when WIFI is getting connected
|
||||
}
|
||||
#endif //ENABLE_MQTT
|
||||
}
|
||||
#endif //ENABLE_MQTT
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void strinttoip4(const char *ip, int &a, int &b, int &c, int &d) {
|
||||
std::string zw = std::string(ip);
|
||||
std::stringstream s(zw);
|
||||
char ch; //to temporarily store the '.'
|
||||
s >> a >> ch >> b >> ch >> c >> ch >> d;
|
||||
}
|
||||
|
||||
|
||||
void wifi_init_sta(const char *_ssid, const char *_password, const char *_hostname, const char *_ipadr, const char *_gw, const char *_netmask, const char *_dns, int _rssithreashold)
|
||||
esp_err_t wifi_init_sta(void)
|
||||
{
|
||||
RSSI_Threshold = _rssithreashold;
|
||||
s_wifi_event_group = xEventGroupCreate();
|
||||
esp_err_t retval = esp_netif_init();
|
||||
if (retval != ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_netif_init: Error: " + std::to_string(retval));
|
||||
return retval;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
retval = esp_event_loop_create_default();
|
||||
if (retval != ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_event_loop_create_default: Error: " + std::to_string(retval));
|
||||
return retval;
|
||||
}
|
||||
|
||||
esp_netif_t *my_sta = esp_netif_create_default_wifi_sta();
|
||||
|
||||
if ((_ipadr != NULL) && (_gw != NULL) && (_netmask != NULL))
|
||||
if (!wlan_config.ipaddress.empty() && !wlan_config.gateway.empty() && !wlan_config.netmask.empty())
|
||||
{
|
||||
|
||||
ESP_LOGI(TAG, "set IP %s, GW %s, Netmask %s manual", _ipadr, _gw, _netmask);
|
||||
esp_netif_dhcpc_stop(my_sta);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Manual interface config -> IP: " + wlan_config.ipaddress + ", Gateway: " +
|
||||
std::string(wlan_config.gateway) + ", Netmask: " + std::string(wlan_config.netmask));
|
||||
esp_netif_dhcpc_stop(my_sta); // Stop DHCP service
|
||||
|
||||
esp_netif_ip_info_t ip_info;
|
||||
int a, b, c, d;
|
||||
strinttoip4(_ipadr, a, b, c, d);
|
||||
IP4_ADDR(&ip_info.ip, a, b, c, d);
|
||||
strinttoip4(_gw, a, b, c, d);
|
||||
IP4_ADDR(&ip_info.gw, a, b, c, d);
|
||||
strinttoip4(_netmask, a, b, c, d);
|
||||
IP4_ADDR(&ip_info.netmask, a, b, c, d);
|
||||
strinttoip4(wlan_config.ipaddress.c_str(), a, b, c, d);
|
||||
IP4_ADDR(&ip_info.ip, a, b, c, d); // Set static IP address
|
||||
|
||||
esp_netif_set_ip_info(my_sta, &ip_info);
|
||||
strinttoip4(wlan_config.gateway.c_str(), a, b, c, d);
|
||||
IP4_ADDR(&ip_info.gw, a, b, c, d); // Set gateway
|
||||
|
||||
strinttoip4(wlan_config.netmask.c_str(), a, b, c, d);
|
||||
IP4_ADDR(&ip_info.netmask, a, b, c, d); // Set netmask
|
||||
|
||||
esp_netif_set_ip_info(my_sta, &ip_info); // Set static IP configuration
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Automatic interface config --> Use DHCP service");
|
||||
}
|
||||
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
retval = esp_wifi_init(&cfg);
|
||||
if (retval != ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_init: Error: " + std::to_string(retval));
|
||||
return retval;
|
||||
}
|
||||
|
||||
if ((_ipadr != NULL) && (_gw != NULL) && (_netmask != NULL))
|
||||
if (!wlan_config.ipaddress.empty() && !wlan_config.gateway.empty() && !wlan_config.netmask.empty())
|
||||
{
|
||||
if (_dns == NULL)
|
||||
_dns = _gw;
|
||||
|
||||
ESP_LOGI(TAG, "set DNS manual");
|
||||
if (wlan_config.dns.empty()) {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "No DNS server, use gateway");
|
||||
wlan_config.dns = wlan_config.gateway;
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Manual interface config -> DNS: " + wlan_config.dns);
|
||||
}
|
||||
|
||||
esp_netif_dns_info_t dns_info;
|
||||
ip4_addr_t ip;
|
||||
ip.addr = esp_ip4addr_aton(_dns);
|
||||
ip.addr = esp_ip4addr_aton(wlan_config.dns.c_str());
|
||||
ip_addr_set_ip4_u32(&dns_info.ip, ip.addr);
|
||||
ESP_ERROR_CHECK(esp_netif_set_dns_info(my_sta, ESP_NETIF_DNS_MAIN, &dns_info));
|
||||
}
|
||||
|
||||
esp_event_handler_instance_t instance_any_id;
|
||||
esp_event_handler_instance_t instance_got_ip;
|
||||
esp_event_handler_instance_t instance_bss_rssi_low;
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
|
||||
ESP_EVENT_ANY_ID,
|
||||
&event_handler,
|
||||
NULL,
|
||||
&instance_any_id));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
|
||||
IP_EVENT_STA_GOT_IP,
|
||||
&event_handler,
|
||||
NULL,
|
||||
&instance_got_ip));
|
||||
retval = esp_netif_set_dns_info(my_sta, ESP_NETIF_DNS_MAIN, &dns_info);
|
||||
if (retval != ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_netif_set_dns_info: Error: " + std::to_string(retval));
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
retval = esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
|
||||
&event_handler, NULL, NULL);
|
||||
if (retval != ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_event_handler_instance_register - WIFI_ANY: Error: " + std::to_string(retval));
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
|
||||
&event_handler, NULL, NULL);
|
||||
if (retval != ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_event_handler_instance_register - GOT_IP: Error: " + std::to_string(retval));
|
||||
return retval;
|
||||
}
|
||||
|
||||
#ifdef WLAN_USE_MESH_ROAMING
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
|
||||
WIFI_EVENT_STA_BSS_RSSI_LOW,
|
||||
&esp_bss_rssi_low_handler,
|
||||
NULL,
|
||||
&instance_bss_rssi_low));
|
||||
retval = esp_event_handler_instance_register(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW,
|
||||
&esp_bss_rssi_low_handler, NULL, NULL);
|
||||
if (retval != ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_event_handler_instance_register - BSS_RSSI_LOW: Error: " + std::to_string(retval));
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
wifi_config_t wifi_config = { };
|
||||
|
||||
strcpy((char*)wifi_config.sta.ssid, (const char*)_ssid);
|
||||
strcpy((char*)wifi_config.sta.password, (const char*)_password);
|
||||
wifi_config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; // Scan all channels instead of stopping after first match
|
||||
wifi_config.sta.sort_method = WIFI_CONNECT_AP_BY_SIGNAL; // Sort by signal strength and keep up to 4 best APs
|
||||
//wifi_config.sta.failure_retry_cnt = 3; // IDF version 5.0 will support this
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
|
||||
ESP_ERROR_CHECK(esp_wifi_start() );
|
||||
#ifdef WLAN_USE_MESH_ROAMING
|
||||
wifi_config.sta.rm_enabled = 1; // 802.11k (Radio Resource Management)
|
||||
wifi_config.sta.btm_enabled = 1; // 802.11v (BSS Transition Management)
|
||||
//wifi_config.sta.mbo_enabled = 1; // Multiband Operation (better use of Wi-Fi network resources in roaming decisions) -> not activated to save heap
|
||||
wifi_config.sta.pmf_cfg.capable = 1; // 802.11w (Protected Management Frame, activated by default if other device also advertizes PMF capability)
|
||||
//wifi_config.sta.ft_enabled = 1; // 802.11r (BSS Fast Transition) -> Upcoming IDF version 5.0 will support 11r
|
||||
#endif
|
||||
|
||||
if (_hostname != NULL)
|
||||
strcpy((char*)wifi_config.sta.ssid, (const char*)wlan_config.ssid.c_str());
|
||||
strcpy((char*)wifi_config.sta.password, (const char*)wlan_config.password.c_str());
|
||||
|
||||
retval = esp_wifi_set_mode(WIFI_MODE_STA);
|
||||
if (retval != ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_set_mode: Error: " + std::to_string(retval));
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
|
||||
if (retval != ESP_OK) {
|
||||
if (retval == ESP_ERR_WIFI_PASSWORD) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_set_config: SSID password invalid! Error: " + std::to_string(retval));
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_set_config: Error: " + std::to_string(retval));
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = esp_wifi_start();
|
||||
if (retval != ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_start: Error: " + std::to_string(retval));
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (!wlan_config.hostname.empty())
|
||||
{
|
||||
esp_err_t ret = tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA , _hostname);
|
||||
hostname = std::string(_hostname);
|
||||
if(ret != ESP_OK ){
|
||||
ESP_LOGE(TAG,"Failed to set hostname: %d",ret);
|
||||
retval = tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA , wlan_config.hostname.c_str());
|
||||
if(retval != ESP_OK ) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to set hostname! Error: " + std::to_string(retval));
|
||||
}
|
||||
else {
|
||||
ESP_LOGI(TAG,"Set hostname to: %s", _hostname);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Set hostname to: " + wlan_config.hostname);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "wifi_init_sta finished.");
|
||||
|
||||
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
|
||||
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
|
||||
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
|
||||
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
|
||||
pdFALSE,
|
||||
pdFALSE,
|
||||
portMAX_DELAY);
|
||||
|
||||
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
|
||||
* happened. */
|
||||
if (bits & WIFI_CONNECTED_BIT) {
|
||||
#ifdef __HIDE_PASSWORD
|
||||
ESP_LOGI(TAG, "Connected with AP: %s, password: XXXXXXX", _ssid);
|
||||
#else
|
||||
ESP_LOGI(TAG, "Connected with AP: %s, password: %s", _ssid, _password);
|
||||
#endif
|
||||
} else if (bits & WIFI_FAIL_BIT) {
|
||||
#ifdef __HIDE_PASSWORD
|
||||
ESP_LOGI(TAG, "Failed to connect with AP: %s, password: XXXXXXXX", _ssid);
|
||||
#else
|
||||
ESP_LOGI(TAG, "Failed to connect with AP: %s, password: %s", _ssid, _password);
|
||||
#endif
|
||||
} else {
|
||||
ESP_LOGE(TAG, "UNEXPECTED EVENT");
|
||||
}
|
||||
ssid = std::string(_ssid);
|
||||
|
||||
|
||||
/* The event will not be processed after unregister */
|
||||
// ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
|
||||
// ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
|
||||
// vEventGroupDelete(s_wifi_event_group);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Init successful");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
int get_WIFI_RSSI()
|
||||
{
|
||||
wifi_ap_record_t ap;
|
||||
esp_wifi_sta_get_ap_info(&ap);
|
||||
return ap.rssi;
|
||||
}
|
||||
|
||||
|
||||
void wifi_init_sta(const char *_ssid, const char *_password, const char *_hostname)
|
||||
{
|
||||
wifi_init_sta(_ssid, _password, _hostname, NULL, NULL, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
void wifi_init_sta(const char *_ssid, const char *_password)
|
||||
{
|
||||
wifi_init_sta(_ssid, _password, NULL, NULL, NULL, NULL, NULL, 0);
|
||||
wifi_ap_record_t ap;
|
||||
if (esp_wifi_sta_get_ap_info(&ap) == ESP_OK)
|
||||
return ap.rssi;
|
||||
else
|
||||
return -127; // Return -127 if no info available e.g. not connected
|
||||
}
|
||||
|
||||
|
||||
@@ -556,12 +668,13 @@ bool getWIFIisConnected()
|
||||
|
||||
void WIFIDestroy()
|
||||
{
|
||||
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, event_handler);
|
||||
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, event_handler);
|
||||
esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, event_handler);
|
||||
#ifdef WLAN_USE_MESH_ROAMING
|
||||
esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW, esp_bss_rssi_low_handler);
|
||||
#endif
|
||||
|
||||
|
||||
esp_wifi_disconnect();
|
||||
esp_wifi_stop();
|
||||
esp_wifi_deinit();
|
||||
}
|
||||
|
||||
@@ -5,18 +5,19 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
void wifi_init_sta(const char *_ssid, const char *_password, const char *_hostname, const char *_ipadr, const char *_gw, const char *_netmask, const char *_dns, int _rssithreashold);
|
||||
void wifi_init_sta(const char *_ssid, const char *_password, const char *_hostname);
|
||||
void wifi_init_sta(const char *_ssid, const char *_password);
|
||||
|
||||
int wifi_init_sta(void);
|
||||
std::string* getIPAddress();
|
||||
std::string* getSSID();
|
||||
int get_WIFI_RSSI();
|
||||
bool getWIFIisConnected();
|
||||
void WIFIDestroy();
|
||||
|
||||
extern std::string hostname;
|
||||
extern std::string std_hostname;
|
||||
extern int RSSIThreashold;
|
||||
#if (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES)
|
||||
void wifiRoamingQuery(void);
|
||||
#endif
|
||||
|
||||
#ifdef WLAN_USE_ROAMING_BY_SCANNING
|
||||
void wifiRoamByScanning(void);
|
||||
#endif
|
||||
|
||||
#endif //CONNECT_WLAN_H
|
||||
@@ -11,9 +11,14 @@
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "ClassLogFile.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char *TAG = "WLAN.INI";
|
||||
static const char *TAG = "WLANINI";
|
||||
|
||||
|
||||
struct wlan_config wlan_config = {};
|
||||
|
||||
|
||||
std::vector<string> ZerlegeZeileWLAN(std::string input, std::string _delimiter = "")
|
||||
{
|
||||
@@ -40,207 +45,189 @@ std::vector<string> ZerlegeZeileWLAN(std::string input, std::string _delimiter =
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool LoadWlanFromFile(std::string fn, char *&_ssid, char *&_password, char *&_hostname, char *&_ipadr, char *&_gw, char *&_netmask, char *&_dns, int &_rssithreashold)
|
||||
int LoadWlanFromFile(std::string fn)
|
||||
{
|
||||
std::string ssid = "";
|
||||
std::string passphrase = "";
|
||||
std::string ipaddress = "";
|
||||
std::string gw = "";
|
||||
std::string netmask = "";
|
||||
std::string dns = "";
|
||||
int rssithreshold = 0;
|
||||
|
||||
std::string line = "";
|
||||
std::string tmp = "";
|
||||
std::vector<string> splitted;
|
||||
hostname = std_hostname;
|
||||
|
||||
FILE* pFile;
|
||||
fn = FormatFileName(fn);
|
||||
FILE* pFile = fopen(fn.c_str(), "r");
|
||||
if (pFile == NULL) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Unable to open file (read). Device init aborted!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pFile = fopen(fn.c_str(), "r");
|
||||
if (!pFile)
|
||||
return false;
|
||||
ESP_LOGD(TAG, "LoadWlanFromFile: wlan.ini opened");
|
||||
|
||||
ESP_LOGD(TAG, "file loaded");
|
||||
|
||||
if (pFile == NULL)
|
||||
return false;
|
||||
|
||||
char zw[1024];
|
||||
fgets(zw, 1024, pFile);
|
||||
line = std::string(zw);
|
||||
char zw[256];
|
||||
if (fgets(zw, sizeof(zw), pFile) == NULL) {
|
||||
line = "";
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "file opened, but empty or content not readable. Device init aborted!");
|
||||
fclose(pFile);
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
line = std::string(zw);
|
||||
}
|
||||
|
||||
while ((line.size() > 0) || !(feof(pFile)))
|
||||
{
|
||||
// ESP_LOGD(TAG, "%s", line.c_str());
|
||||
splitted = ZerlegeZeileWLAN(line, "=");
|
||||
splitted[0] = trim(splitted[0], " ");
|
||||
//ESP_LOGD(TAG, "line: %s", line.c_str());
|
||||
if (line[0] != ';') { // Skip lines which starts with ';'
|
||||
|
||||
if ((splitted.size() > 1) && (toUpper(splitted[0]) == "HOSTNAME")){
|
||||
hostname = trim(splitted[1]);
|
||||
if ((hostname[0] == '"') && (hostname[hostname.length()-1] == '"')){
|
||||
hostname = hostname.substr(1, hostname.length()-2);
|
||||
splitted = ZerlegeZeileWLAN(line, "=");
|
||||
splitted[0] = trim(splitted[0], " ");
|
||||
|
||||
if ((splitted.size() > 1) && (toUpper(splitted[0]) == "SSID")){
|
||||
tmp = trim(splitted[1]);
|
||||
if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){
|
||||
tmp = tmp.substr(1, tmp.length()-2);
|
||||
}
|
||||
wlan_config.ssid = tmp;
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "SSID: " + wlan_config.ssid);
|
||||
}
|
||||
|
||||
else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "PASSWORD")){
|
||||
tmp = splitted[1];
|
||||
if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){
|
||||
tmp = tmp.substr(1, tmp.length()-2);
|
||||
}
|
||||
wlan_config.password = tmp;
|
||||
#ifndef __HIDE_PASSWORD
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Password: " + wlan_config.password);
|
||||
#else
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Password: XXXXXXXX");
|
||||
#endif
|
||||
}
|
||||
|
||||
else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "HOSTNAME")){
|
||||
tmp = trim(splitted[1]);
|
||||
if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){
|
||||
tmp = tmp.substr(1, tmp.length()-2);
|
||||
}
|
||||
wlan_config.hostname = tmp;
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Hostname: " + wlan_config.hostname);
|
||||
}
|
||||
|
||||
else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "IP")){
|
||||
tmp = splitted[1];
|
||||
if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){
|
||||
tmp = tmp.substr(1, tmp.length()-2);
|
||||
}
|
||||
wlan_config.ipaddress = tmp;
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "IP-Address: " + wlan_config.ipaddress);
|
||||
}
|
||||
|
||||
else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "GATEWAY")){
|
||||
tmp = splitted[1];
|
||||
if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){
|
||||
tmp = tmp.substr(1, tmp.length()-2);
|
||||
}
|
||||
wlan_config.gateway = tmp;
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Gateway: " + wlan_config.gateway);
|
||||
}
|
||||
|
||||
else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "NETMASK")){
|
||||
tmp = splitted[1];
|
||||
if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){
|
||||
tmp = tmp.substr(1, tmp.length()-2);
|
||||
}
|
||||
wlan_config.netmask = tmp;
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Netmask: " + wlan_config.netmask);
|
||||
}
|
||||
|
||||
else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "DNS")){
|
||||
tmp = splitted[1];
|
||||
if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){
|
||||
tmp = tmp.substr(1, tmp.length()-2);
|
||||
}
|
||||
wlan_config.dns = tmp;
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "DNS: " + wlan_config.dns);
|
||||
}
|
||||
#if (defined WLAN_USE_ROAMING_BY_SCANNING || (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES))
|
||||
else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "RSSITHRESHOLD")){
|
||||
tmp = trim(splitted[1]);
|
||||
if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){
|
||||
tmp = tmp.substr(1, tmp.length()-2);
|
||||
}
|
||||
wlan_config.rssi_threshold = atoi(tmp.c_str());
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "RSSIThreshold: " + std::to_string(wlan_config.rssi_threshold));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((splitted.size() > 1) && (toUpper(splitted[0]) == "SSID")){
|
||||
ssid = trim(splitted[1]);
|
||||
if ((ssid[0] == '"') && (ssid[ssid.length()-1] == '"')){
|
||||
ssid = ssid.substr(1, ssid.length()-2);
|
||||
}
|
||||
}
|
||||
|
||||
if ((splitted.size() > 1) && (toUpper(splitted[0]) == "RSSITHREASHOLD")){
|
||||
string _s = trim(splitted[1]);
|
||||
if ((_s[0] == '"') && (_s[_s.length()-1] == '"')){
|
||||
_s = _s.substr(1, ssid.length()-2);
|
||||
}
|
||||
rssithreshold = atoi(_s.c_str());
|
||||
}
|
||||
|
||||
|
||||
if ((splitted.size() > 1) && (toUpper(splitted[0]) == "PASSWORD")){
|
||||
passphrase = splitted[1];
|
||||
if ((passphrase[0] == '"') && (passphrase[passphrase.length()-1] == '"')){
|
||||
passphrase = passphrase.substr(1, passphrase.length()-2);
|
||||
}
|
||||
}
|
||||
|
||||
if ((splitted.size() > 1) && (toUpper(splitted[0]) == "IP")){
|
||||
ipaddress = splitted[1];
|
||||
if ((ipaddress[0] == '"') && (ipaddress[ipaddress.length()-1] == '"')){
|
||||
ipaddress = ipaddress.substr(1, ipaddress.length()-2);
|
||||
}
|
||||
}
|
||||
|
||||
if ((splitted.size() > 1) && (toUpper(splitted[0]) == "GATEWAY")){
|
||||
gw = splitted[1];
|
||||
if ((gw[0] == '"') && (gw[gw.length()-1] == '"')){
|
||||
gw = gw.substr(1, gw.length()-2);
|
||||
}
|
||||
}
|
||||
|
||||
if ((splitted.size() > 1) && (toUpper(splitted[0]) == "NETMASK")){
|
||||
netmask = splitted[1];
|
||||
if ((netmask[0] == '"') && (netmask[netmask.length()-1] == '"')){
|
||||
netmask = netmask.substr(1, netmask.length()-2);
|
||||
}
|
||||
}
|
||||
|
||||
if ((splitted.size() > 1) && (toUpper(splitted[0]) == "DNS")){
|
||||
dns = splitted[1];
|
||||
if ((dns[0] == '"') && (dns[dns.length()-1] == '"')){
|
||||
dns = dns.substr(1, dns.length()-2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (fgets(zw, 1024, pFile) == NULL)
|
||||
{
|
||||
/* read next line */
|
||||
if (fgets(zw, sizeof(zw), pFile) == NULL) {
|
||||
line = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
line = std::string(zw);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(pFile);
|
||||
|
||||
// Check if Hostname was empty in .ini if yes set to std_hostname
|
||||
if(hostname.length() == 0){
|
||||
hostname = std_hostname;
|
||||
/* Check if SSID is empty (mandatory parameter) */
|
||||
if (wlan_config.ssid.empty()) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SSID empty. Device init aborted!");
|
||||
return -2;
|
||||
}
|
||||
|
||||
_hostname = new char[hostname.length() + 1];
|
||||
strcpy(_hostname, hostname.c_str());
|
||||
|
||||
_ssid = new char[ssid.length() + 1];
|
||||
strcpy(_ssid, ssid.c_str());
|
||||
|
||||
_password = new char[passphrase.length() + 1];
|
||||
strcpy(_password, passphrase.c_str());
|
||||
|
||||
if (ipaddress.length() > 0)
|
||||
{
|
||||
_ipadr = new char[ipaddress.length() + 1];
|
||||
strcpy(_ipadr, ipaddress.c_str());
|
||||
/* Check if password is empty (mandatory parameter) */
|
||||
if (wlan_config.password.empty()) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Password empty. Device init aborted!");
|
||||
return -2;
|
||||
}
|
||||
else
|
||||
_ipadr = NULL;
|
||||
|
||||
if (gw.length() > 0)
|
||||
{
|
||||
_gw = new char[gw.length() + 1];
|
||||
strcpy(_gw, gw.c_str());
|
||||
}
|
||||
else
|
||||
_gw = NULL;
|
||||
|
||||
if (netmask.length() > 0)
|
||||
{
|
||||
_netmask = new char[netmask.length() + 1];
|
||||
strcpy(_netmask, netmask.c_str());
|
||||
}
|
||||
else
|
||||
_netmask = NULL;
|
||||
|
||||
if (dns.length() > 0)
|
||||
{
|
||||
_dns = new char[dns.length() + 1];
|
||||
strcpy(_dns, dns.c_str());
|
||||
}
|
||||
else
|
||||
_dns = NULL;
|
||||
|
||||
_rssithreashold = rssithreshold;
|
||||
RSSIThreashold = rssithreshold;
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool ChangeHostName(std::string fn, std::string _newhostname)
|
||||
{
|
||||
if (_newhostname == hostname)
|
||||
if (_newhostname == wlan_config.hostname)
|
||||
return false;
|
||||
|
||||
string line = "";
|
||||
std::string line = "";
|
||||
std::vector<string> splitted;
|
||||
|
||||
std::vector<string> neuesfile;
|
||||
bool found = false;
|
||||
|
||||
std::vector<string> neuesfile;
|
||||
FILE* pFile = NULL;
|
||||
|
||||
FILE* pFile;
|
||||
fn = FormatFileName(fn);
|
||||
pFile = fopen(fn.c_str(), "r");
|
||||
|
||||
ESP_LOGD(TAG, "file loaded\n");
|
||||
|
||||
if (pFile == NULL)
|
||||
if (pFile == NULL) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ChangeHostName: Unable to open file wlan.ini (read)");
|
||||
return false;
|
||||
}
|
||||
|
||||
char zw[1024];
|
||||
fgets(zw, 1024, pFile);
|
||||
line = std::string(zw);
|
||||
ESP_LOGD(TAG, "ChangeHostName: wlan.ini opened");
|
||||
|
||||
char zw[256];
|
||||
if (fgets(zw, sizeof(zw), pFile) == NULL) {
|
||||
line = "";
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ChangeHostName: File opened, but empty or content not readable");
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
line = std::string(zw);
|
||||
}
|
||||
|
||||
while ((line.size() > 0) || !(feof(pFile)))
|
||||
{
|
||||
ESP_LOGD(TAG, "%s", line.c_str());
|
||||
//ESP_LOGD(TAG, "ChangeHostName: line: %s", line.c_str());
|
||||
splitted = ZerlegeZeileWLAN(line, "=");
|
||||
splitted[0] = trim(splitted[0], " ");
|
||||
|
||||
if ((splitted.size() > 1) && (toUpper(splitted[0]) == "HOSTNAME")){
|
||||
if ((splitted.size() > 1) && ((toUpper(splitted[0]) == "HOSTNAME") || (toUpper(splitted[0]) == ";HOSTNAME"))){
|
||||
line = "hostname = \"" + _newhostname + "\"\n";
|
||||
found = true;
|
||||
}
|
||||
|
||||
neuesfile.push_back(line);
|
||||
|
||||
if (fgets(zw, 1024, pFile) == NULL)
|
||||
if (fgets(zw, sizeof(zw), pFile) == NULL)
|
||||
{
|
||||
line = "";
|
||||
}
|
||||
@@ -252,52 +239,64 @@ bool ChangeHostName(std::string fn, std::string _newhostname)
|
||||
|
||||
if (!found)
|
||||
{
|
||||
line = "\nhostname = \"" + _newhostname + "\"\n";
|
||||
line = "\n;++++++++++++++++++++++++++++++++++\n";
|
||||
line += "; Hostname: Name of device in network\n";
|
||||
line += "; This parameter can be configured via WebUI configuration\n";
|
||||
line += "; Default: \"watermeter\", if nothing is configured\n\n";
|
||||
line = "hostname = \"" + _newhostname + "\"\n";
|
||||
neuesfile.push_back(line);
|
||||
}
|
||||
|
||||
fclose(pFile);
|
||||
|
||||
pFile = fopen(fn.c_str(), "w+");
|
||||
if (pFile == NULL) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ChangeHostName: Unable to open file wlan.ini (write)");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < neuesfile.size(); ++i)
|
||||
{
|
||||
ESP_LOGD(TAG, "%s", neuesfile[i].c_str());
|
||||
//ESP_LOGD(TAG, "%s", neuesfile[i].c_str());
|
||||
fputs(neuesfile[i].c_str(), pFile);
|
||||
}
|
||||
|
||||
fclose(pFile);
|
||||
|
||||
ESP_LOGD(TAG, "*** Hostname update done ***");
|
||||
ESP_LOGD(TAG, "ChangeHostName done");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ChangeRSSIThreashold(std::string fn, int _newrssithreashold)
|
||||
#if (defined WLAN_USE_ROAMING_BY_SCANNING || (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES))
|
||||
bool ChangeRSSIThreshold(std::string fn, int _newrssithreshold)
|
||||
{
|
||||
if (RSSIThreashold == _newrssithreashold)
|
||||
if (wlan_config.rssi_threshold == _newrssithreshold)
|
||||
return false;
|
||||
|
||||
string line = "";
|
||||
std::string line = "";
|
||||
std::vector<string> splitted;
|
||||
|
||||
std::vector<string> neuesfile;
|
||||
bool found = false;
|
||||
|
||||
std::vector<string> neuesfile;
|
||||
FILE* pFile = NULL;
|
||||
|
||||
FILE* pFile;
|
||||
fn = FormatFileName(fn);
|
||||
pFile = fopen(fn.c_str(), "r");
|
||||
|
||||
ESP_LOGD(TAG, "file loaded\n");
|
||||
|
||||
if (pFile == NULL)
|
||||
if (pFile == NULL) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ChangeRSSIThreshold: Unable to open file wlan.ini (read)");
|
||||
return false;
|
||||
}
|
||||
|
||||
char zw[1024];
|
||||
fgets(zw, 1024, pFile);
|
||||
line = std::string(zw);
|
||||
ESP_LOGD(TAG, "ChangeRSSIThreshold: wlan.ini opened");
|
||||
|
||||
char zw[256];
|
||||
if (fgets(zw, sizeof(zw), pFile) == NULL) {
|
||||
line = "";
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ChangeRSSIThreshold: File opened, but empty or content not readable");
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
line = std::string(zw);
|
||||
}
|
||||
|
||||
while ((line.size() > 0) || !(feof(pFile)))
|
||||
{
|
||||
@@ -305,43 +304,68 @@ bool ChangeRSSIThreashold(std::string fn, int _newrssithreashold)
|
||||
splitted = ZerlegeZeileWLAN(line, "=");
|
||||
splitted[0] = trim(splitted[0], " ");
|
||||
|
||||
if ((splitted.size() > 1) && (toUpper(splitted[0]) == "RSSITHREASHOLD")){
|
||||
line = "RSSIThreashold = " + to_string(_newrssithreashold) + "\n";
|
||||
/* Workaround to eliminate line with typo "RSSIThreashold" or "rssi" if existing */
|
||||
if (((splitted.size() > 1) && (toUpper(splitted[0]) == "RSSITHREASHOLD")) ||
|
||||
((splitted.size() > 1) && (toUpper(splitted[0]) == ";RSSITHREASHOLD")) ||
|
||||
((splitted.size() > 1) && (toUpper(splitted[0]) == "RSSI")) ||
|
||||
((splitted.size() > 1) && (toUpper(splitted[0]) == ";RSSI"))) {
|
||||
if (fgets(zw, sizeof(zw), pFile) == NULL) {
|
||||
line = "";
|
||||
}
|
||||
else {
|
||||
line = std::string(zw);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((splitted.size() > 1) && ((toUpper(splitted[0]) == "RSSITHRESHOLD") || (toUpper(splitted[0]) == ";RSSITHRESHOLD"))) {
|
||||
line = "RSSIThreshold = " + to_string(_newrssithreshold) + "\n";
|
||||
found = true;
|
||||
}
|
||||
|
||||
|
||||
neuesfile.push_back(line);
|
||||
|
||||
if (fgets(zw, 1024, pFile) == NULL)
|
||||
{
|
||||
|
||||
if (fgets(zw, sizeof(zw), pFile) == NULL) {
|
||||
line = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
line = std::string(zw);
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
line = "RSSIThreashold = " + to_string(_newrssithreashold) + "\n";
|
||||
line = "\n;++++++++++++++++++++++++++++++++++\n";
|
||||
line += "; WIFI Roaming:\n";
|
||||
line += "; Network assisted roaming protocol is activated by default\n";
|
||||
line += "; AP / mesh system needs to support roaming protocol 802.11k/v\n";
|
||||
line += ";\n";
|
||||
line += "; Optional feature (usually not neccessary):\n";
|
||||
line += "; RSSI Threshold for client requested roaming query (RSSI < RSSIThreshold)\n";
|
||||
line += "; Note: This parameter can be configured via WebUI configuration\n";
|
||||
line += "; Default: 0 = Disable client requested roaming query\n\n";
|
||||
line += "RSSIThreshold = " + to_string(_newrssithreshold) + "\n";
|
||||
neuesfile.push_back(line);
|
||||
}
|
||||
|
||||
fclose(pFile);
|
||||
|
||||
pFile = fopen(fn.c_str(), "w+");
|
||||
if (pFile == NULL) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ChangeRSSIThreshold: Unable to open file wlan.ini (write)");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < neuesfile.size(); ++i)
|
||||
{
|
||||
ESP_LOGD(TAG, "%s", neuesfile[i].c_str());
|
||||
//ESP_LOGD(TAG, "%s", neuesfile[i].c_str());
|
||||
fputs(neuesfile[i].c_str(), pFile);
|
||||
}
|
||||
|
||||
fclose(pFile);
|
||||
|
||||
ESP_LOGD(TAG, "*** RSSIThreashold update done ***");
|
||||
ESP_LOGD(TAG, "ChangeRSSIThreshold done");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,10 +5,22 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
bool LoadWlanFromFile(std::string fn, char *&_ssid, char *&_password, char *&_hostname, char *&_ipadr, char *&_gw, char *&_netmask, char *&_dns, int &_rssithreashold);
|
||||
struct wlan_config {
|
||||
std::string ssid = "";
|
||||
std::string password = "";
|
||||
std::string hostname = "watermeter"; // Default: watermeter
|
||||
std::string ipaddress = "";
|
||||
std::string gateway = "";
|
||||
std::string netmask = "";
|
||||
std::string dns = "";
|
||||
int rssi_threshold = 0; // Default: 0 -> ROAMING disabled
|
||||
};
|
||||
extern struct wlan_config wlan_config;
|
||||
|
||||
|
||||
int LoadWlanFromFile(std::string fn);
|
||||
bool ChangeHostName(std::string fn, std::string _newhostname);
|
||||
bool ChangeRSSIThreashold(std::string fn, int _newrssithreashold);
|
||||
bool ChangeRSSIThreshold(std::string fn, int _newrssithreshold);
|
||||
|
||||
|
||||
#endif //READ_WLANINI_H
|
||||
@@ -21,7 +21,9 @@
|
||||
|
||||
|
||||
// use himem //https://github.com/jomjol/AI-on-the-edge-device/issues/1842
|
||||
#define USE_HIMEM_IF_AVAILABLE
|
||||
#if (CONFIG_SPIRAM_BANKSWITCH_ENABLE)
|
||||
#define USE_HIMEM_IF_AVAILABLE 1
|
||||
#endif
|
||||
|
||||
/* Uncomment this to generate task list with stack sizes using the /heap handler
|
||||
PLEASE BE AWARE: The following CONFIG parameters have to to be set in
|
||||
@@ -56,11 +58,11 @@
|
||||
//#define CONFIG_IDF_TARGET_ARCH_XTENSA //not needed with platformio/espressif32 @ 5.2.0
|
||||
|
||||
|
||||
//ClassControllCamera + ClassFlowMakeImage + connect_wlan + main
|
||||
//ClassControllCamera + ClassFlowTakeImage + connect_wlan + main
|
||||
#define FLASH_GPIO GPIO_NUM_4
|
||||
#define BLINK_GPIO GPIO_NUM_33
|
||||
|
||||
//ClassFlowMQTT + interface_mqtt + connect_wlan + main
|
||||
//interface_mqtt + read_wlanini
|
||||
#define __HIDE_PASSWORD
|
||||
|
||||
//ClassControllCamera
|
||||
@@ -69,12 +71,13 @@
|
||||
//server_GPIO
|
||||
#define __LEDGLOBAL
|
||||
|
||||
//ClassControllCamera + ClassFlowMakeImage
|
||||
//ClassControllCamera + ClassFlowTakeImage
|
||||
#define CAMERA_MODEL_AI_THINKER
|
||||
#define BOARD_ESP32CAM_AITHINKER
|
||||
|
||||
//server_GPIO + server_file + SoftAP
|
||||
#define CONFIG_FILE "/sdcard/config/config.ini"
|
||||
#define CONFIG_FILE_BACKUP "/sdcard/config/config.bak"
|
||||
|
||||
//ClassFlowControll + Main + SoftAP
|
||||
#define WLAN_CONFIG_FILE "/sdcard/wlan.ini"
|
||||
@@ -116,7 +119,6 @@
|
||||
|
||||
//ClassFlowControll: Serve alg_roi.jpg from memory as JPG
|
||||
#define ALGROI_LOAD_FROM_MEM_AS_JPG // Load ALG_ROI.JPG as rendered JPG from RAM
|
||||
#define ALGROI_LOAD_FROM_MEM_AS_JPG__SHOW_TAKE_IMAGE_PROCESS // Show take image image processing on webinterface (overview.html)
|
||||
|
||||
//ClassFlowMQTT
|
||||
#define LWT_TOPIC "connection"
|
||||
@@ -129,14 +131,8 @@
|
||||
|
||||
//CImageBasis
|
||||
#define HTTP_BUFFER_SENT 1024
|
||||
#define GET_MEMORY(X) heap_caps_malloc(X, MALLOC_CAP_SPIRAM)
|
||||
#define MAX_JPG_SIZE 128000
|
||||
|
||||
|
||||
//CAlignAndCutImage + CImageBasis
|
||||
#define _USE_MATH_DEFINES
|
||||
#define GET_MEMORY(X) heap_caps_malloc(X, MALLOC_CAP_SPIRAM)
|
||||
|
||||
//make_stb + stb_image_resize + stb_image_write + stb_image //do not work if not in make_stb.cpp
|
||||
//#define STB_IMAGE_IMPLEMENTATION
|
||||
//#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
@@ -159,15 +155,23 @@
|
||||
}
|
||||
#define SUPRESS_TFLITE_ERRORS // use, to avoid error messages from TFLITE
|
||||
|
||||
//connect_wlan
|
||||
#define WLAN_USE_MESH_ROAMING
|
||||
#define WLAN_WIFI_RSSI_THRESHOLD -50
|
||||
#define EXAMPLE_ESP_MAXIMUM_RETRY 1000
|
||||
/* The event group allows multiple bits for each event, but we only care about two events:
|
||||
* - we are connected to the AP with an IP
|
||||
* - we failed to connect after the maximum amount of retries */
|
||||
#define WIFI_CONNECTED_BIT BIT0
|
||||
#define WIFI_FAIL_BIT BIT1
|
||||
|
||||
// connect_wlan.cpp
|
||||
//******************************
|
||||
/* WIFI roaming functionalities 802.11k+v (uses ca. 6kB - 8kB internal RAM; if SCAN CACHE activated: + 1kB / beacon)
|
||||
PLEASE BE AWARE: The following CONFIG parameters have to to be set in
|
||||
sdkconfig.defaults before use of this function is possible!!
|
||||
CONFIG_WPA_11KV_SUPPORT=y
|
||||
CONFIG_WPA_SCAN_CACHE=n
|
||||
CONFIG_WPA_MBO_SUPPORT=n
|
||||
CONFIG_WPA_11R_SUPPORT=n
|
||||
*/
|
||||
//#define WLAN_USE_MESH_ROAMING // 802.11v (BSS Transition Management) + 802.11k (Radio Resource Management) (ca. 6kB - 8kB internal RAM neccessary)
|
||||
//#define WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES // Client can send query to AP requesting to roam (if RSSI lower than RSSI threshold)
|
||||
|
||||
/* WIFI roaming only client triggered by scanning the channels after each round (only if RSSI < RSSIThreshold) and trigger a disconnect to switch AP */
|
||||
#define WLAN_USE_ROAMING_BY_SCANNING
|
||||
|
||||
|
||||
//ClassFlowCNNGeneral
|
||||
#define Analog_error 3
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
//#include <string>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <regex>
|
||||
|
||||
//#include "freertos/FreeRTOS.h"
|
||||
//#include "freertos/task.h"
|
||||
//#include "freertos/event_groups.h"
|
||||
@@ -8,6 +12,7 @@
|
||||
//#include "esp_psram.h" // Comming in IDF 5.0, see https://docs.espressif.com/projects/esp-idf/en/v5.0-beta1/esp32/migration-guides/release-5.x/system.html?highlight=esp_psram_get_size
|
||||
//#include "spiram.h"
|
||||
#include "esp32/spiram.h"
|
||||
#include "esp_pm.h"
|
||||
|
||||
|
||||
// SD-Card ////////////////////
|
||||
@@ -29,13 +34,17 @@
|
||||
#include "server_file.h"
|
||||
#include "server_ota.h"
|
||||
#include "time_sntp.h"
|
||||
#include "configFile.h"
|
||||
//#include "ClassControllCamera.h"
|
||||
#include "server_main.h"
|
||||
#include "server_camera.h"
|
||||
#ifdef ENABLE_MQTT
|
||||
#include "server_mqtt.h"
|
||||
#endif //ENABLE_MQTT
|
||||
//#include "Helper.h"
|
||||
#include "Helper.h"
|
||||
#include "statusled.h"
|
||||
#include "sdcard_check.h"
|
||||
|
||||
#include "../../include/defines.h"
|
||||
//#include "server_GPIO.h"
|
||||
|
||||
@@ -78,8 +87,13 @@ extern std::string getFwVersion(void);
|
||||
extern std::string getHTMLversion(void);
|
||||
extern std::string getHTMLcommit(void);
|
||||
|
||||
std::vector<std::string> splitString(const std::string& str);
|
||||
void migrateConfiguration(void);
|
||||
bool setCpuFrequency(void);
|
||||
|
||||
static const char *TAG = "MAIN";
|
||||
|
||||
|
||||
bool Init_NVS_SDCard()
|
||||
{
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
@@ -87,9 +101,8 @@ bool Init_NVS_SDCard()
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
////////////////////////////////////////////////
|
||||
|
||||
ESP_LOGI(TAG, "Using SDMMC peripheral");
|
||||
ESP_LOGD(TAG, "Using SDMMC peripheral");
|
||||
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||
|
||||
// This initializes the slot without card detect (CD) and write protect (WP) signals.
|
||||
@@ -97,20 +110,19 @@ bool Init_NVS_SDCard()
|
||||
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||
|
||||
// To use 1-line SD mode, uncomment the following line:
|
||||
|
||||
#ifdef __SD_USE_ONE_LINE_MODE__
|
||||
slot_config.width = 1;
|
||||
#endif
|
||||
#ifdef __SD_USE_ONE_LINE_MODE__
|
||||
slot_config.width = 1;
|
||||
#endif
|
||||
|
||||
// GPIOs 15, 2, 4, 12, 13 should have external 10k pull-ups.
|
||||
// Internal pull-ups are not sufficient. However, enabling internal pull-ups
|
||||
// does make a difference some boards, so we do that here.
|
||||
gpio_set_pull_mode(GPIO_NUM_15, GPIO_PULLUP_ONLY); // CMD, needed in 4- and 1- line modes
|
||||
gpio_set_pull_mode(GPIO_NUM_2, GPIO_PULLUP_ONLY); // D0, needed in 4- and 1-line modes
|
||||
#ifndef __SD_USE_ONE_LINE_MODE__
|
||||
gpio_set_pull_mode(GPIO_NUM_4, GPIO_PULLUP_ONLY); // D1, needed in 4-line mode only
|
||||
gpio_set_pull_mode(GPIO_NUM_12, GPIO_PULLUP_ONLY); // D2, needed in 4-line mode only
|
||||
#endif
|
||||
#ifndef __SD_USE_ONE_LINE_MODE__
|
||||
gpio_set_pull_mode(GPIO_NUM_4, GPIO_PULLUP_ONLY); // D1, needed in 4-line mode only
|
||||
gpio_set_pull_mode(GPIO_NUM_12, GPIO_PULLUP_ONLY); // D2, needed in 4-line mode only
|
||||
#endif
|
||||
gpio_set_pull_mode(GPIO_NUM_13, GPIO_PULLUP_ONLY); // D3, needed in 4- and 1-line modes
|
||||
|
||||
// Options for mounting the filesystem.
|
||||
@@ -122,244 +134,330 @@ bool Init_NVS_SDCard()
|
||||
.allocation_unit_size = 16 * 1024
|
||||
};
|
||||
|
||||
sdmmc_card_t* card;
|
||||
// Use settings defined above to initialize SD card and mount FAT filesystem.
|
||||
// Note: esp_vfs_fat_sdmmc_mount is an all-in-one convenience function.
|
||||
// Please check its source code and implement error recovery when developing
|
||||
// production applications.
|
||||
sdmmc_card_t* card;
|
||||
ret = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
if (ret == ESP_FAIL) {
|
||||
ESP_LOGE(TAG, "Failed to mount filesystem. "
|
||||
"If you want the card to be formatted, set format_if_mount_failed = true.");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to initialize the card (%s). "
|
||||
"Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
|
||||
ESP_LOGE(TAG, "Failed to mount FAT filesystem on SD card. Check SD card filesystem (only FAT supported) or try another card");
|
||||
StatusLED(SDCARD_INIT, 1, true);
|
||||
}
|
||||
else if (ret == 263) { // Error code: 0x107 --> usually: SD not found
|
||||
ESP_LOGE(TAG, "SD card init failed. Check if SD card is properly inserted into SD card slot or try another card");
|
||||
StatusLED(SDCARD_INIT, 2, true);
|
||||
}
|
||||
else {
|
||||
ESP_LOGE(TAG, "SD card init failed. Check error code or try another card");
|
||||
StatusLED(SDCARD_INIT, 3, true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
sdmmc_card_print_info(stdout, card);
|
||||
//sdmmc_card_print_info(stdout, card); // With activated CONFIG_NEWLIB_NANO_FORMAT --> capacity not printed correctly anymore
|
||||
SaveSDCardInfo(card);
|
||||
return true;
|
||||
}
|
||||
|
||||
void task_MainInitError_blink(void *pvParameter)
|
||||
{
|
||||
gpio_pad_select_gpio(BLINK_GPIO);
|
||||
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
|
||||
|
||||
|
||||
TickType_t xDelay;
|
||||
xDelay = 100 / portTICK_PERIOD_MS;
|
||||
ESP_LOGD(TAG, "SD-Card could not be inialized - STOP THE PROGRAMM HERE");
|
||||
|
||||
while (1)
|
||||
{
|
||||
gpio_set_level(BLINK_GPIO, 1);
|
||||
vTaskDelay( xDelay );
|
||||
gpio_set_level(BLINK_GPIO, 0);
|
||||
vTaskDelay( xDelay );
|
||||
|
||||
}
|
||||
vTaskDelete(NULL); //Delete this task if it exits from the loop above
|
||||
}
|
||||
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
|
||||
//#ifdef CONFIG_HEAP_TRACING_STANDALONE
|
||||
#if defined HEAP_TRACING_MAIN_WIFI || defined HEAP_TRACING_MAIN_START
|
||||
//register a buffer to record the memory trace
|
||||
ESP_ERROR_CHECK( heap_trace_init_standalone(trace_record, NUM_RECORDS) );
|
||||
#endif
|
||||
|
||||
//#ifdef CONFIG_HEAP_TRACING_STANDALONE
|
||||
#if defined HEAP_TRACING_MAIN_WIFI || defined HEAP_TRACING_MAIN_START
|
||||
//register a buffer to record the memory trace
|
||||
ESP_ERROR_CHECK( heap_trace_init_standalone(trace_record, NUM_RECORDS) );
|
||||
#endif
|
||||
|
||||
TickType_t xDelay;
|
||||
|
||||
#ifdef DISABLE_BROWNOUT_DETECTOR
|
||||
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef DISABLE_BROWNOUT_DETECTOR
|
||||
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "\n\n\n\n\n"); // Add mark on log to see when it restarted
|
||||
|
||||
#ifdef HEAP_TRACING_MAIN_START
|
||||
ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
|
||||
#endif
|
||||
|
||||
// ********************************************
|
||||
// Highlight start of app_main
|
||||
// ********************************************
|
||||
ESP_LOGI(TAG, "\n\n\n\n================ Start app_main =================");
|
||||
|
||||
// Init camera
|
||||
// ********************************************
|
||||
PowerResetCamera();
|
||||
esp_err_t camStatus = Camera.InitCam();
|
||||
Camera.LightOnOff(false);
|
||||
xDelay = 2000 / portTICK_PERIOD_MS;
|
||||
ESP_LOGD(TAG, "After camera initialization: sleep for: %ldms", (long) xDelay);
|
||||
vTaskDelay( xDelay );
|
||||
|
||||
xDelay = 2000 / portTICK_PERIOD_MS;
|
||||
ESP_LOGD(TAG, "After camera initialization: sleep for: %ldms", (long) xDelay * CONFIG_FREERTOS_HZ/portTICK_PERIOD_MS);
|
||||
vTaskDelay( xDelay );
|
||||
|
||||
// Init SD card
|
||||
// ********************************************
|
||||
if (!Init_NVS_SDCard())
|
||||
{
|
||||
xTaskCreate(&task_MainInitError_blink, "task_MainInitError_blink", configMINIMAL_STACK_SIZE * 64, NULL, tskIDLE_PRIORITY+1, NULL);
|
||||
return; // No way to continue without SD-Card!
|
||||
ESP_LOGE(TAG, "Device init aborted!");
|
||||
return; // No way to continue without working SD card!
|
||||
}
|
||||
|
||||
setupTime();
|
||||
// SD card: Create log directories (if not already existing)
|
||||
// ********************************************
|
||||
LogFile.CreateLogDirectories(); // mandatory for logging + image saving
|
||||
|
||||
string versionFormated = getFwVersion() + ", Date/Time: " + std::string(BUILD_TIME) + \
|
||||
// ********************************************
|
||||
// Highlight start of logfile logging
|
||||
// Default Log Level: INFO -> Everything which needs to be logged during boot should be have level INFO, WARN OR ERROR
|
||||
// ********************************************
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "=================================================");
|
||||
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
|
||||
}
|
||||
|
||||
// Migrate parameter in config.ini to new naming (firmware 15.0 and newer)
|
||||
// ********************************************
|
||||
migrateConfiguration();
|
||||
|
||||
// Init time (as early as possible, but SD card needs to be initialized)
|
||||
// ********************************************
|
||||
setupTime(); // NTP time service: Status of time synchronization will be checked after every round (server_tflite.cpp)
|
||||
|
||||
|
||||
// Set CPU Frequency
|
||||
// ********************************************
|
||||
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
|
||||
|
||||
// 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
|
||||
// ********************************************
|
||||
#ifdef ENABLE_SOFTAP
|
||||
CheckStartAPMode();
|
||||
#endif
|
||||
|
||||
// SD card: Check presence of some mandatory folders / files
|
||||
// ********************************************
|
||||
if (!SDCardCheckFolderFilePresence()) {
|
||||
StatusLED(SDCARD_CHECK, 4, true);
|
||||
setSystemStatusFlag(SYSTEM_STATUS_FOLDER_CHECK_BAD); // reduced web interface going to be loaded
|
||||
}
|
||||
|
||||
// Check version information
|
||||
// ********************************************
|
||||
std::string versionFormated = getFwVersion() + ", Date/Time: " + std::string(BUILD_TIME) + \
|
||||
", Web UI: " + getHTMLversion();
|
||||
|
||||
if (std::string(GIT_TAG) != "") { // We are on a tag, add it as prefix
|
||||
string versionFormated = "Tag: '" + std::string(GIT_TAG) + "', " + versionFormated;
|
||||
versionFormated = "Tag: '" + std::string(GIT_TAG) + "', " + versionFormated;
|
||||
}
|
||||
|
||||
LogFile.CreateLogDirectories();
|
||||
MakeDir("/sdcard/demo"); // needed for demo mode
|
||||
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "=================================================");
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "==================== Startup ====================");
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "=================================================");
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, versionFormated);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Reset reason: " + getResetReason());
|
||||
|
||||
#ifdef DEBUG_ENABLE_SYSINFO
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL( 4, 0, 0 )
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Device Info : " + get_device_info() );
|
||||
ESP_LOGD(TAG, "Device infos %s", get_device_info().c_str());
|
||||
#endif
|
||||
#endif //DEBUG_ENABLE_SYSINFO
|
||||
|
||||
#ifdef USE_HIMEM_IF_AVAILABLE
|
||||
#ifdef DEBUG_HIMEM_MEMORY_CHECK
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Himem mem check : " + himem_memory_check() );
|
||||
ESP_LOGD(TAG, "Himem mem check %s", himem_memory_check().c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
CheckIsPlannedReboot();
|
||||
CheckOTAUpdate();
|
||||
CheckUpdate();
|
||||
#ifdef ENABLE_SOFTAP
|
||||
CheckStartAPMode(); // if no wlan.ini and/or config.ini --> AP ist startet and this function does not exit anymore until reboot
|
||||
#endif
|
||||
|
||||
#ifdef HEAP_TRACING_MAIN_WIFI
|
||||
ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
|
||||
#endif
|
||||
|
||||
char *ssid = NULL, *passwd = NULL, *hostname = NULL, *ip = NULL, *gateway = NULL, *netmask = NULL, *dns = NULL; int rssithreashold = 0;
|
||||
LoadWlanFromFile(WLAN_CONFIG_FILE, ssid, passwd, hostname, ip, gateway, netmask, dns, rssithreashold);
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "WLAN-Settings - RSSI-Threashold: " + to_string(rssithreashold));
|
||||
|
||||
if (ssid != NULL && passwd != NULL)
|
||||
#ifdef __HIDE_PASSWORD
|
||||
ESP_LOGD(TAG, "WLan: %s, XXXXXX", ssid);
|
||||
#else
|
||||
ESP_LOGD(TAG, "WLan: %s, %s", ssid, passwd);
|
||||
#endif
|
||||
|
||||
else
|
||||
ESP_LOGD(TAG, "No SSID and PASSWORD set!!!");
|
||||
|
||||
if (hostname != NULL)
|
||||
ESP_LOGD(TAG, "Hostname: %s", hostname);
|
||||
else
|
||||
ESP_LOGD(TAG, "Hostname not set");
|
||||
|
||||
if (ip != NULL && gateway != NULL && netmask != NULL)
|
||||
ESP_LOGD(TAG, "Fixed IP: %s, Gateway %s, Netmask %s", ip, gateway, netmask);
|
||||
if (dns != NULL)
|
||||
ESP_LOGD(TAG, "DNS IP: %s", dns);
|
||||
|
||||
|
||||
wifi_init_sta(ssid, passwd, hostname, ip, gateway, netmask, dns, rssithreashold);
|
||||
|
||||
|
||||
xDelay = 2000 / portTICK_PERIOD_MS;
|
||||
ESP_LOGD(TAG, "main: sleep for: %ldms", (long) xDelay);
|
||||
vTaskDelay( xDelay );
|
||||
|
||||
#ifdef HEAP_TRACING_MAIN_WIFI
|
||||
ESP_ERROR_CHECK( heap_trace_stop() );
|
||||
heap_trace_dump();
|
||||
#endif
|
||||
|
||||
#ifdef HEAP_TRACING_MAIN_START
|
||||
ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
|
||||
#endif
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "=================================================");
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "================== Main Started =================");
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "=================================================");
|
||||
|
||||
if (getHTMLcommit().substr(0, 7) == "?")
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, std::string("Failed to read file html/version.txt to parse Web UI version"));
|
||||
|
||||
if (getHTMLcommit().substr(0, 7) != std::string(GIT_REV).substr(0, 7)) { // Compare the first 7 characters of both hashes
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, std::string("Web UI version (") + getHTMLcommit() + ") does not match firmware version (" + std::string(GIT_REV) + ") !");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Please make sure to setup the SD-Card properly (check the documentation) or re-install using the AI-on-the-edge-device__update__*.zip!");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Web UI version (" + getHTMLcommit() + ") does not match firmware version (" + std::string(GIT_REV) + ")");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Recommendation: Repeat installation using AI-on-the-edge-device__update__*.zip");
|
||||
}
|
||||
|
||||
std::string zw = getCurrentTimeString("%Y%m%d-%H%M%S");
|
||||
ESP_LOGD(TAG, "time %s", zw.c_str());
|
||||
// Check reboot reason
|
||||
// ********************************************
|
||||
CheckIsPlannedReboot();
|
||||
if (!getIsPlannedReboot() && (esp_reset_reason() == ESP_RST_PANIC)) { // If system reboot was not triggered by user and reboot was caused by execption
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Reset reason: " + getResetReason());
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Device was rebooted due to a software exception! Log level is set to DEBUG until the next reboot. "
|
||||
"Flow init is delayed by 5 minutes to check the logs or do an OTA update");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Keep device running until crash occurs again and check logs after device is up again");
|
||||
LogFile.setLogLevel(ESP_LOG_DEBUG);
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Reset reason: " + getResetReason());
|
||||
}
|
||||
|
||||
#ifdef HEAP_TRACING_MAIN_START
|
||||
ESP_ERROR_CHECK( heap_trace_stop() );
|
||||
heap_trace_dump();
|
||||
#endif
|
||||
|
||||
#ifdef HEAP_TRACING_MAIN_START
|
||||
ESP_ERROR_CHECK( heap_trace_stop() );
|
||||
heap_trace_dump();
|
||||
#endif
|
||||
#ifdef HEAP_TRACING_MAIN_WIFI
|
||||
ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
|
||||
#endif
|
||||
|
||||
/* Check if PSRAM can be initalized */
|
||||
esp_err_t ret;
|
||||
ret = esp_spiram_init();
|
||||
if (ret == ESP_FAIL) { // Failed to init PSRAM, most likely not available or broken
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to initialize PSRAM (" + std::to_string(ret) + ")!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Either your device misses the PSRAM chip or it is broken!");
|
||||
setSystemStatusFlag(SYSTEM_STATUS_PSRAM_BAD);
|
||||
}
|
||||
else { // PSRAM init ok
|
||||
/* Check if PSRAM provides at least 4 MB */
|
||||
size_t psram_size = esp_spiram_get_size();
|
||||
// size_t psram_size = esp_psram_get_size(); // comming in IDF 5.0
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "The device has " + std::to_string(psram_size/1024/1024) + " MBytes of PSRAM");
|
||||
if (psram_size < (4*1024*1024)) { // PSRAM is below 4 MBytes
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "At least 4 MBytes are required!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Does the device really have a 4 Mbytes PSRAM?");
|
||||
setSystemStatusFlag(SYSTEM_STATUS_PSRAM_BAD);
|
||||
// Read WLAN parameter and start WIFI
|
||||
// ********************************************
|
||||
int iWLANStatus = LoadWlanFromFile(WLAN_CONFIG_FILE);
|
||||
if (iWLANStatus == 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "WLAN config loaded, init WIFI...");
|
||||
if (wifi_init_sta() != ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "WIFI init failed. Device init aborted!");
|
||||
StatusLED(WLAN_INIT, 3, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check available Heap memory */
|
||||
size_t _hsize = getESPHeapSize();
|
||||
if (_hsize < 4000000) { // Check available Heap memory for a bit less than 4 MB (a test on a good device showed 4187558 bytes to be available)
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Not enough Heap memory available. Expected around 4 MBytes, but only " + std::to_string(_hsize) + " Bytes are available! That is not enough for this firmware!");
|
||||
setSystemStatusFlag(SYSTEM_STATUS_HEAP_TOO_SMALL);
|
||||
} else { // Heap memory is ok
|
||||
if (camStatus != ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Failed to initialize camera module, retrying...");
|
||||
|
||||
PowerResetCamera();
|
||||
esp_err_t camStatus = Camera.InitCam();
|
||||
Camera.LightOnOff(false);
|
||||
xDelay = 2000 / portTICK_PERIOD_MS;
|
||||
ESP_LOGD(TAG, "After camera initialization: sleep for: %ldms", (long) xDelay);
|
||||
vTaskDelay( xDelay );
|
||||
|
||||
if (camStatus != ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to initialize camera module!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Check that your camera module is working and connected properly!");
|
||||
setSystemStatusFlag(SYSTEM_STATUS_CAM_BAD);
|
||||
}
|
||||
} else { // Test Camera
|
||||
if (!Camera.testCamera()) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Camera Framebuffer cannot be initialized!");
|
||||
/* Easiest would be to simply restart here and try again,
|
||||
how ever there seem to be systems where it fails at startup but still work corectly later.
|
||||
Therefore we treat it still as successed! */
|
||||
setSystemStatusFlag(SYSTEM_STATUS_CAM_FB_BAD);
|
||||
}
|
||||
else {
|
||||
Camera.LightOnOff(false);
|
||||
}
|
||||
}
|
||||
else if (iWLANStatus == -1) { // wlan.ini not available, potentially empty or content not readable
|
||||
StatusLED(WLAN_INIT, 1, true);
|
||||
return; // No way to continue without reading the wlan.ini
|
||||
}
|
||||
else if (iWLANStatus == -2) { // SSID or password not configured
|
||||
StatusLED(WLAN_INIT, 2, true);
|
||||
return; // No way to continue with empty SSID or password!
|
||||
}
|
||||
|
||||
xDelay = 2000 / portTICK_PERIOD_MS;
|
||||
ESP_LOGD(TAG, "main: sleep for: %ldms", (long) xDelay*10);
|
||||
ESP_LOGD(TAG, "main: sleep for: %ldms", (long) xDelay * CONFIG_FREERTOS_HZ/portTICK_PERIOD_MS);
|
||||
vTaskDelay( xDelay );
|
||||
|
||||
// Set log level for wifi component to WARN level (default: INFO; only relevant for serial console)
|
||||
// ********************************************
|
||||
esp_log_level_set("wifi", ESP_LOG_WARN);
|
||||
|
||||
#ifdef HEAP_TRACING_MAIN_WIFI
|
||||
ESP_ERROR_CHECK( heap_trace_stop() );
|
||||
heap_trace_dump();
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_ENABLE_SYSINFO
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL( 4, 0, 0 )
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Device Info : " + get_device_info() );
|
||||
ESP_LOGD(TAG, "Device infos %s", get_device_info().c_str());
|
||||
#endif
|
||||
#endif //DEBUG_ENABLE_SYSINFO
|
||||
|
||||
#ifdef USE_HIMEM_IF_AVAILABLE
|
||||
#ifdef DEBUG_HIMEM_MEMORY_CHECK
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Himem mem check : " + himem_memory_check() );
|
||||
ESP_LOGD(TAG, "Himem mem check %s", himem_memory_check().c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Init external PSRAM
|
||||
// ********************************************
|
||||
esp_err_t PSRAMStatus = esp_spiram_init();
|
||||
if (PSRAMStatus != ESP_OK) { // ESP_FAIL -> Failed to init PSRAM
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "PSRAM init failed (" + std::to_string(PSRAMStatus) + ")! PSRAM not found or defective");
|
||||
setSystemStatusFlag(SYSTEM_STATUS_PSRAM_BAD);
|
||||
StatusLED(PSRAM_INIT, 1, true);
|
||||
}
|
||||
else { // ESP_OK -> PSRAM init OK --> continue to check PSRAM size
|
||||
size_t psram_size = esp_spiram_get_size(); // size_t psram_size = esp_psram_get_size(); // comming in IDF 5.0
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "PSRAM size: " + std::to_string(psram_size) + " byte (" + std::to_string(psram_size/1024/1024) +
|
||||
"MB / " + std::to_string(psram_size/1024/1024*8) + "MBit)");
|
||||
|
||||
// Check PSRAM size
|
||||
// ********************************************
|
||||
if (psram_size < (4*1024*1024)) { // PSRAM is below 4 MBytes (32Mbit)
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "PSRAM size >= 4MB (32Mbit) is mandatory to run this application");
|
||||
setSystemStatusFlag(SYSTEM_STATUS_PSRAM_BAD);
|
||||
StatusLED(PSRAM_INIT, 2, true);
|
||||
}
|
||||
else { // PSRAM size OK --> continue to check heap size
|
||||
size_t _hsize = getESPHeapSize();
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Total heap: " + std::to_string(_hsize) + " byte");
|
||||
|
||||
// Check heap memory
|
||||
// ********************************************
|
||||
if (_hsize < 4000000) { // Check available Heap memory for a bit less than 4 MB (a test on a good device showed 4187558 bytes to be available)
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Total heap >= 4000000 byte is mandatory to run this application");
|
||||
setSystemStatusFlag(SYSTEM_STATUS_HEAP_TOO_SMALL);
|
||||
StatusLED(PSRAM_INIT, 3, true);
|
||||
}
|
||||
else { // HEAP size OK --> continue to check camera init
|
||||
// Check camera init
|
||||
// ********************************************
|
||||
if (camStatus != ESP_OK) { // Camera init failed, retry to init
|
||||
char camStatusHex[33];
|
||||
sprintf(camStatusHex,"0x%02x", camStatus);
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Camera init failed (" + std::string(camStatusHex) + "), retrying...");
|
||||
|
||||
PowerResetCamera();
|
||||
camStatus = Camera.InitCam();
|
||||
Camera.LightOnOff(false);
|
||||
|
||||
xDelay = 2000 / portTICK_PERIOD_MS;
|
||||
ESP_LOGD(TAG, "After camera initialization: sleep for: %ldms", (long) xDelay * CONFIG_FREERTOS_HZ/portTICK_PERIOD_MS);
|
||||
vTaskDelay( xDelay );
|
||||
|
||||
if (camStatus != ESP_OK) { // Camera init failed again
|
||||
sprintf(camStatusHex,"0x%02x", camStatus);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Camera init failed (" + std::string(camStatusHex) +
|
||||
")! Check camera module and/or proper electrical connection");
|
||||
setSystemStatusFlag(SYSTEM_STATUS_CAM_BAD);
|
||||
StatusLED(CAM_INIT, 1, true);
|
||||
}
|
||||
}
|
||||
else { // ESP_OK -> Camera init OK --> continue to perform camera framebuffer check
|
||||
// Camera framebuffer check
|
||||
// ********************************************
|
||||
if (!Camera.testCamera()) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Camera framebuffer check failed");
|
||||
// Easiest would be to simply restart here and try again,
|
||||
// how ever there seem to be systems where it fails at startup but still work correctly later.
|
||||
// Therefore we treat it still as successed! */
|
||||
setSystemStatusFlag(SYSTEM_STATUS_CAM_FB_BAD);
|
||||
StatusLED(CAM_INIT, 2, false);
|
||||
}
|
||||
Camera.LightOnOff(false); // make sure flashlight is off before start of flow
|
||||
|
||||
// Print camera infos
|
||||
// ********************************************
|
||||
char caminfo[50];
|
||||
sensor_t * s = esp_camera_sensor_get();
|
||||
sprintf(caminfo, "PID: 0x%02x, VER: 0x%02x, MIDL: 0x%02x, MIDH: 0x%02x", s->id.PID, s->id.VER, s->id.MIDH, s->id.MIDL);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Camera info: " + std::string(caminfo));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print Device info
|
||||
// ********************************************
|
||||
esp_chip_info_t chipInfo;
|
||||
esp_chip_info(&chipInfo);
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Device info: CPU cores: " + std::to_string(chipInfo.cores) +
|
||||
", Chip revision: " + std::to_string(chipInfo.revision));
|
||||
|
||||
// Print SD-Card info
|
||||
// ********************************************
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "SD card info: Name: " + getSDCardName() + ", Capacity: " +
|
||||
getSDCardCapacity() + "MB, Free: " + getSDCardFreePartitionSpace() + "MB");
|
||||
|
||||
xDelay = 2000 / portTICK_PERIOD_MS;
|
||||
ESP_LOGD(TAG, "main: sleep for: %ldms", (long) xDelay * CONFIG_FREERTOS_HZ/portTICK_PERIOD_MS);
|
||||
vTaskDelay( xDelay );
|
||||
|
||||
// Start webserver + register handler
|
||||
// ********************************************
|
||||
ESP_LOGD(TAG, "starting servers");
|
||||
|
||||
server = start_webserver();
|
||||
@@ -376,25 +474,300 @@ extern "C" void app_main(void)
|
||||
ESP_LOGD(TAG, "Before reg server main");
|
||||
register_server_main_uri(server, "/sdcard");
|
||||
|
||||
|
||||
/* Testing */
|
||||
// Only for testing purpose
|
||||
//setSystemStatusFlag(SYSTEM_STATUS_CAM_FB_BAD);
|
||||
//setSystemStatusFlag(SYSTEM_STATUS_PSRAM_BAD);
|
||||
|
||||
/* Main Init has successed or only an error which allows to continue operation */
|
||||
// Check main init + start TFlite task
|
||||
// ********************************************
|
||||
if (getSystemStatus() == 0) { // No error flag is set
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Initialization completed successfully!");
|
||||
ESP_LOGD(TAG, "Before do autostart");
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Initialization completed successfully! Starting flow task ...");
|
||||
TFliteDoAutoStart();
|
||||
}
|
||||
else if (isSetSystemStatusFlag(SYSTEM_STATUS_CAM_FB_BAD) || // Non critical errors occured, we try to continue...
|
||||
isSetSystemStatusFlag(SYSTEM_STATUS_NTP_BAD)) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Initialization completed with errors, but trying to continue...");
|
||||
ESP_LOGD(TAG, "Before do autostart");
|
||||
isSetSystemStatusFlag(SYSTEM_STATUS_NTP_BAD)) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Initialization completed with errors! Starting flow task ...");
|
||||
TFliteDoAutoStart();
|
||||
}
|
||||
else { // Any other error is critical and makes running the flow impossible.
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Initialization failed. Not starting flows!");
|
||||
else { // Any other error is critical and makes running the flow impossible. Init is going to abort.
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Initialization failed. Flow task start aborted. Loading reduced web interface...");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void migrateConfiguration(void) {
|
||||
bool migrated = false;
|
||||
|
||||
if (!FileExists(CONFIG_FILE)) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Config file seems to be missing!");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string section = "";
|
||||
std::ifstream ifs(CONFIG_FILE);
|
||||
std::string content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
|
||||
|
||||
/* Split config file it array of lines */
|
||||
std::vector<std::string> configLines = splitString(content);
|
||||
|
||||
/* Process each line */
|
||||
for (int i = 0; i < configLines.size(); i++) {
|
||||
//ESP_LOGI(TAG, "Line %d: %s", i, configLines[i].c_str());
|
||||
|
||||
if (configLines[i].find("[") != std::string::npos) { // Start of new section
|
||||
section = configLines[i];
|
||||
replaceString(section, ";", "", false); // Remove possible semicolon (just for the string comparison)
|
||||
//ESP_LOGI(TAG, "New section: %s", section.c_str());
|
||||
}
|
||||
|
||||
/* Migrate parameters as needed
|
||||
* For the boolean parameters, we make them enabled all the time now:
|
||||
* 1. If they where disabled, set them to their default value
|
||||
* 2. Enable them
|
||||
* Notes:
|
||||
* The migration has some simplifications:
|
||||
* - Case Sensitiveness must be like in the initial config.ini
|
||||
* - No Whitespace after a semicollon
|
||||
* - Only one whitespace before/after the equal sign
|
||||
*/
|
||||
if (section == "[MakeImage]") {
|
||||
migrated = migrated | replaceString(configLines[i], "[MakeImage]", "[TakeImage]"); // Rename the section itself
|
||||
}
|
||||
|
||||
if (section == "[MakeImage]" || section == "[TakeImage]") {
|
||||
migrated = migrated | replaceString(configLines[i], "LogImageLocation", "RawImagesLocation");
|
||||
migrated = migrated | replaceString(configLines[i], "LogfileRetentionInDays", "RawImagesRetention");
|
||||
|
||||
migrated = migrated | replaceString(configLines[i], ";Demo = true", ";Demo = false"); // Set it to its default value
|
||||
migrated = migrated | replaceString(configLines[i], ";Demo", "Demo"); // Enable it
|
||||
|
||||
migrated = migrated | replaceString(configLines[i], ";FixedExposure = true", ";FixedExposure = false"); // Set it to its default value
|
||||
migrated = migrated | replaceString(configLines[i], ";FixedExposure", "FixedExposure"); // Enable it
|
||||
}
|
||||
|
||||
if (section == "[Alignment]") {
|
||||
migrated = migrated | replaceString(configLines[i], ";InitialMirror = true", ";InitialMirror = false"); // Set it to its default value
|
||||
migrated = migrated | replaceString(configLines[i], ";InitialMirror", "InitialMirror"); // Enable it
|
||||
|
||||
migrated = migrated | replaceString(configLines[i], ";FlipImageSize = true", ";FlipImageSize = false"); // Set it to its default value
|
||||
migrated = migrated | replaceString(configLines[i], ";FlipImageSize", "FlipImageSize"); // Enable it
|
||||
}
|
||||
|
||||
if (section == "[Digits]") {
|
||||
migrated = migrated | replaceString(configLines[i], "LogImageLocation", "ROIImagesLocation");
|
||||
migrated = migrated | replaceString(configLines[i], "LogfileRetentionInDays", "ROIImagesRetention");
|
||||
}
|
||||
|
||||
if (section == "[Analog]") {
|
||||
migrated = migrated | replaceString(configLines[i], "LogImageLocation", "ROIImagesLocation");
|
||||
migrated = migrated | replaceString(configLines[i], "LogfileRetentionInDays", "ROIImagesRetention");
|
||||
migrated = migrated | replaceString(configLines[i], "ExtendedResolution", ";UNUSED_PARAMETER"); // This parameter is no longer used
|
||||
}
|
||||
|
||||
if (section == "[PostProcessing]") {
|
||||
migrated = migrated | replaceString(configLines[i], ";PreValueUse = true", ";PreValueUse = false"); // Set it to its default value
|
||||
migrated = migrated | replaceString(configLines[i], ";PreValueUse", "PreValueUse"); // Enable it
|
||||
|
||||
/* AllowNegativeRates has a <NUMBER> as prefix! */
|
||||
if (isInString(configLines[i], "AllowNegativeRates") && isInString(configLines[i], ";")) { // It is the parameter "AllowNegativeRates" and it is commented out
|
||||
migrated = migrated | replaceString(configLines[i], "true", "false"); // Set it to its default value
|
||||
migrated = migrated | replaceString(configLines[i], ";", ""); // Enable it
|
||||
}
|
||||
|
||||
/* IgnoreLeadingNaN has a <NUMBER> as prefix! */
|
||||
if (isInString(configLines[i], "IgnoreLeadingNaN") && isInString(configLines[i], ";")) { // It is the parameter "IgnoreLeadingNaN" and it is commented out
|
||||
migrated = migrated | replaceString(configLines[i], "true", "false"); // Set it to its default value
|
||||
migrated = migrated | replaceString(configLines[i], ";", ""); // Enable it
|
||||
}
|
||||
|
||||
/* ExtendedResolution has a <NUMBER> as prefix! */
|
||||
if (isInString(configLines[i], "ExtendedResolution") && isInString(configLines[i], ";")) { // It is the parameter "ExtendedResolution" and it is commented out
|
||||
migrated = migrated | replaceString(configLines[i], "true", "false"); // Set it to its default value
|
||||
migrated = migrated | replaceString(configLines[i], ";", ""); // Enable it
|
||||
}
|
||||
|
||||
migrated = migrated | replaceString(configLines[i], ";ErrorMessage = true", ";ErrorMessage = false"); // Set it to its default value
|
||||
migrated = migrated | replaceString(configLines[i], ";ErrorMessage", "ErrorMessage"); // Enable it
|
||||
|
||||
migrated = migrated | replaceString(configLines[i], ";CheckDigitIncreaseConsistency = true", ";CheckDigitIncreaseConsistency = false"); // Set it to its default value
|
||||
migrated = migrated | replaceString(configLines[i], ";CheckDigitIncreaseConsistency", "CheckDigitIncreaseConsistency"); // Enable it
|
||||
}
|
||||
|
||||
if (section == "[MQTT]") {
|
||||
migrated = migrated | replaceString(configLines[i], "SetRetainFlag", "RetainMessages"); // First rename it, enable it with its default value
|
||||
migrated = migrated | replaceString(configLines[i], ";RetainMessages = true", ";RetainMessages = false"); // Set it to its default value
|
||||
migrated = migrated | replaceString(configLines[i], ";RetainMessages", "RetainMessages"); // Enable it
|
||||
|
||||
migrated = migrated | replaceString(configLines[i], ";HomeassistantDiscovery = true", ";HomeassistantDiscovery = false"); // Set it to its default value
|
||||
migrated = migrated | replaceString(configLines[i], ";HomeassistantDiscovery", "HomeassistantDiscovery"); // Enable it
|
||||
|
||||
if (configLines[i].rfind("Topic", 0) != std::string::npos) // only if string starts with "Topic" (Was the naming in very old version)
|
||||
{
|
||||
migrated = migrated | replaceString(configLines[i], "Topic", "MainTopic");
|
||||
}
|
||||
}
|
||||
|
||||
if (section == "[InfluxDB]") {
|
||||
|
||||
}
|
||||
|
||||
if (section == "[GPIO]") {
|
||||
|
||||
}
|
||||
|
||||
if (section == "[DataLogging]") {
|
||||
migrated = migrated | replaceString(configLines[i], "DataLogRetentionInDays", "DataFilesRetention");
|
||||
/* DataLogActive is true by default! */
|
||||
migrated = migrated | replaceString(configLines[i], ";DataLogActive = false", ";DataLogActive = true"); // Set it to its default value
|
||||
migrated = migrated | replaceString(configLines[i], ";DataLogActive", "DataLogActive"); // Enable it
|
||||
}
|
||||
|
||||
if (section == "[AutoTimer]") {
|
||||
migrated = migrated | replaceString(configLines[i], "Intervall", "Interval");
|
||||
migrated = migrated | replaceString(configLines[i], ";AutoStart = true", ";AutoStart = false"); // Set it to its default value
|
||||
migrated = migrated | replaceString(configLines[i], ";AutoStart", "AutoStart"); // Enable it
|
||||
|
||||
}
|
||||
|
||||
if (section == "[Debug]") {
|
||||
migrated = migrated | replaceString(configLines[i], "Logfile ", "LogLevel "); // Whitespace needed so it does not match `LogfileRetentionInDays`
|
||||
/* LogLevel (resp. LogFile) was originally a boolean, but we switched it to an int
|
||||
* For both cases (true/false), we set it to level 2 (WARNING) */
|
||||
migrated = migrated | replaceString(configLines[i], "LogLevel = true", "LogLevel = 2");
|
||||
migrated = migrated | replaceString(configLines[i], "LogLevel = false", "LogLevel = 2");
|
||||
migrated = migrated | replaceString(configLines[i], "LogfileRetentionInDays", "LogfilesRetention");
|
||||
}
|
||||
|
||||
if (section == "[System]") {
|
||||
migrated = migrated | replaceString(configLines[i], "RSSIThreashold", "RSSIThreshold");
|
||||
migrated = migrated | replaceString(configLines[i], "AutoAdjustSummertime", ";UNUSED_PARAMETER"); // This parameter is no longer used
|
||||
|
||||
migrated = migrated | replaceString(configLines[i], ";SetupMode = true", ";SetupMode = false"); // Set it to its default value
|
||||
migrated = migrated | replaceString(configLines[i], ";SetupMode", "SetupMode"); // Enable it
|
||||
}
|
||||
}
|
||||
|
||||
if (migrated) { // At least one replacement happened
|
||||
if (! RenameFile(CONFIG_FILE, CONFIG_FILE_BACKUP)) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create backup of Config file!");
|
||||
return;
|
||||
}
|
||||
|
||||
FILE* pfile = fopen(CONFIG_FILE, "w");
|
||||
for (int i = 0; i < configLines.size(); i++) {
|
||||
fwrite(configLines[i].c_str() , configLines[i].length(), 1, pfile);
|
||||
fwrite("\n" , 1, 1, pfile);
|
||||
}
|
||||
fclose(pfile);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Config file migrated. Saved backup to " + string(CONFIG_FILE_BACKUP));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::string> splitString(const std::string& str) {
|
||||
std::vector<std::string> tokens;
|
||||
|
||||
std::stringstream ss(str);
|
||||
std::string token;
|
||||
|
||||
while (std::getline(ss, token, '\n')) {
|
||||
tokens.push_back(token);
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
|
||||
/*bool replace_all(std::string& s, std::string const& toReplace, std::string const& replaceWith) {
|
||||
std::string buf;
|
||||
std::size_t pos = 0;
|
||||
std::size_t prevPos;
|
||||
bool found = false;
|
||||
|
||||
// Reserves rough estimate of final size of string.
|
||||
buf.reserve(s.size());
|
||||
|
||||
while (true) {
|
||||
prevPos = pos;
|
||||
pos = s.find(toReplace, pos);
|
||||
if (pos == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
found = true;
|
||||
buf.append(s, prevPos, pos - prevPos);
|
||||
buf += replaceWith;
|
||||
pos += toReplace.size();
|
||||
}
|
||||
|
||||
buf.append(s, prevPos, s.size() - prevPos);
|
||||
s.swap(buf);
|
||||
|
||||
return found;
|
||||
}*/
|
||||
|
||||
|
||||
bool setCpuFrequency(void) {
|
||||
ConfigFile configFile = ConfigFile(CONFIG_FILE);
|
||||
string cpuFrequency = "160";
|
||||
esp_pm_config_esp32_t pm_config;
|
||||
|
||||
if (!configFile.ConfigFileExists()){
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "No ConfigFile defined - exit setCpuFrequency()!");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> splitted;
|
||||
std::string line = "";
|
||||
bool disabledLine = false;
|
||||
bool eof = false;
|
||||
|
||||
|
||||
/* Load config from config file */
|
||||
while ((!configFile.GetNextParagraph(line, disabledLine, eof) ||
|
||||
(line.compare("[System]") != 0)) && !eof) {}
|
||||
if (eof) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (disabledLine) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (configFile.getNextLine(&line, disabledLine, eof) &&
|
||||
!configFile.isNewParagraph(line)) {
|
||||
splitted = ZerlegeZeile(line);
|
||||
|
||||
if (toUpper(splitted[0]) == "CPUFREQUENCY") {
|
||||
cpuFrequency = splitted[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (esp_pm_get_configuration(&pm_config) != ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read CPU Frequency!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cpuFrequency == "160") { // 160 is the default
|
||||
// No change needed
|
||||
}
|
||||
else if (cpuFrequency == "240") {
|
||||
pm_config.max_freq_mhz = 240;
|
||||
pm_config.min_freq_mhz = pm_config.max_freq_mhz;
|
||||
if (esp_pm_configure(&pm_config) != ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to set new CPU frequency!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Unknown CPU frequency: " + cpuFrequency + "! "
|
||||
"It must be 160 or 240!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_pm_get_configuration(&pm_config) == ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, string("CPU frequency: ") + to_string(pm_config.max_freq_mhz) + " MHz");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "time_sntp.h"
|
||||
|
||||
#include "connect_wlan.h"
|
||||
#include "read_wlanini.h"
|
||||
|
||||
#include "version.h"
|
||||
|
||||
@@ -83,7 +84,7 @@ esp_err_t info_get_handler(httpd_req_t *req)
|
||||
else if (_task.compare("Hostname") == 0)
|
||||
{
|
||||
std::string zw;
|
||||
zw = std::string(hostname);
|
||||
zw = std::string(wlan_config.hostname);
|
||||
httpd_resp_sendstr(req, zw.c_str());
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -214,7 +215,10 @@ esp_err_t hello_main_handler(httpd_req_t *req)
|
||||
|
||||
if (filetosend == "/sdcard/html/index.html") {
|
||||
if (isSetSystemStatusFlag(SYSTEM_STATUS_PSRAM_BAD) || // Initialization failed with crritical errors!
|
||||
isSetSystemStatusFlag(SYSTEM_STATUS_CAM_BAD)) {
|
||||
isSetSystemStatusFlag(SYSTEM_STATUS_CAM_BAD) ||
|
||||
isSetSystemStatusFlag(SYSTEM_STATUS_SDCARD_CHECK_BAD) ||
|
||||
isSetSystemStatusFlag(SYSTEM_STATUS_FOLDER_CHECK_BAD))
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "We have a critical error, not serving main page!");
|
||||
|
||||
char buf[20];
|
||||
@@ -227,13 +231,13 @@ esp_err_t hello_main_handler(httpd_req_t *req)
|
||||
}
|
||||
}
|
||||
|
||||
message += "<br>Please check <a href=\"https://jomjol.github.io/AI-on-the-edge-device-docs/Error-Codes\" target=_blank>jomjol.github.io/AI-on-the-edge-device-docs/Error-Codes</a> for more information!";
|
||||
message += "<br>Please check logs with log viewer and/or <a href=\"https://jomjol.github.io/AI-on-the-edge-device-docs/Error-Codes\" target=_blank>jomjol.github.io/AI-on-the-edge-device-docs/Error-Codes</a> for more information!";
|
||||
message += "<br><br><button onclick=\"window.location.href='/reboot';\">Reboot</button>";
|
||||
message += " <button onclick=\"window.open('/ota_page.html');\">OTA Update</button>";
|
||||
message += " <button onclick=\"window.open('/log.html');\">Log Viewer</button>";
|
||||
message += " <button onclick=\"window.open('/info.html');\">Show System Info</button>";
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, message.c_str());
|
||||
return ESP_FAIL;
|
||||
httpd_resp_send(req, message.c_str(), message.length());
|
||||
return ESP_OK;
|
||||
}
|
||||
else if (isSetupModusActive()) {
|
||||
ESP_LOGD(TAG, "System is in setup mode --> index.html --> setup.html");
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "server_help.h"
|
||||
#include "defines.h"
|
||||
#include "Helper.h"
|
||||
#include "statusled.h"
|
||||
#include "server_ota.h"
|
||||
|
||||
#include "lwip/err.h"
|
||||
@@ -40,22 +41,24 @@
|
||||
bool isConfigINI = false;
|
||||
bool isWlanINI = false;
|
||||
|
||||
static const char *TAG = "wifi softAP";
|
||||
static const char *TAG = "WIFI AP";
|
||||
|
||||
|
||||
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
if (event_id == WIFI_EVENT_AP_STACONNECTED) {
|
||||
wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
|
||||
ESP_LOGI(TAG, "station "MACSTR" join, AID=%d",
|
||||
ESP_LOGI(TAG, "station " MACSTR " join, AID=%d",
|
||||
MAC2STR(event->mac), event->aid);
|
||||
} else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
|
||||
wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
|
||||
ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d",
|
||||
ESP_LOGI(TAG, "station " MACSTR " leave, AID=%d",
|
||||
MAC2STR(event->mac), event->aid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void wifi_init_softAP(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
@@ -87,7 +90,7 @@ void wifi_init_softAP(void)
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
|
||||
ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
|
||||
ESP_LOGI(TAG, "started with SSID \"%s\", password: \"%s\", channel: %d. Connect to AP and open http://192.168.4.1",
|
||||
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);
|
||||
}
|
||||
|
||||
@@ -136,18 +139,18 @@ void SendHTTPResponse(httpd_req_t *req)
|
||||
httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str()));
|
||||
|
||||
// message = "</tr><tr><td> Hostname</td><td><input type=\"text\" name=\"hostname\" id=\"hostname\"></td><td></td>";
|
||||
// message += "</tr><tr><td>Fixed IP</td><td><input type=\"text\" name=\"ip\" id=\"ip\"></td><td>Leave emtpy if set by router</td></tr>";
|
||||
// message += "<tr><td>gateway</td><td><input type=\"text\" name=\"gateway\" id=\"gateway\"></td><td>Leave emtpy if set by router</td></tr>";
|
||||
// message += "<tr><td>netmask</td><td><input type=\"text\" name=\"netmask\" id=\"netmask\"></td><td>Leave emtpy if set by router</td>";
|
||||
// message += "</tr><tr><td>DNS</td><td><input type=\"text\" name=\"dns\" id=\"dns\"></td><td>Leave emtpy if set by router</td></tr>";
|
||||
// message += "<tr><td>RSSI Threashold</td><td><input type=\"number\" name=\"name\" id=\"threashold\" min=\"-100\" max=\"0\" step=\"1\" value = \"0\"></td><td>WLAN Mesh Parameter: Threashold for RSSI value to check for start switching access point in a mesh system.Possible values: -100 to 0, 0 = disabled - Value will be transfered to wlan.ini at next startup)</td></tr>";
|
||||
// message += "</tr><tr><td>Fixed IP</td><td><input type=\"text\" name=\"ip\" id=\"ip\"></td><td>Leave emtpy if set by router (DHCP)</td></tr>";
|
||||
// message += "<tr><td>Gateway</td><td><input type=\"text\" name=\"gateway\" id=\"gateway\"></td><td>Leave emtpy if set by router (DHCP)</td></tr>";
|
||||
// message += "<tr><td>Netmask</td><td><input type=\"text\" name=\"netmask\" id=\"netmask\"></td><td>Leave emtpy if set by router (DHCP)</td>";
|
||||
// message += "</tr><tr><td>DNS</td><td><input type=\"text\" name=\"dns\" id=\"dns\"></td><td>Leave emtpy if set by router (DHCP)</td></tr>";
|
||||
// message += "<tr><td>RSSI Threshold</td><td><input type=\"number\" name=\"name\" id=\"threshold\" min=\"-100\" max=\"0\" step=\"1\" value = \"0\"></td><td>WLAN Mesh Parameter: Threshold for RSSI value to check for start switching access point in a mesh system (if actual RSSI is lower). Possible values: -100 to 0, 0 = disabled - Value will be transfered to wlan.ini at next startup)</td></tr>";
|
||||
// httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str()));
|
||||
|
||||
|
||||
message = "<button class=\"button\" type=\"button\" onclick=\"wr()\">Write wlan.ini</button>";
|
||||
message += "<script language=\"JavaScript\">async function wr(){";
|
||||
message += "api = \"/config?\"+\"ssid=\"+document.getElementById(\"ssid\").value+\"&pwd=\"+document.getElementById(\"password\").value;";
|
||||
// message += "api = \"/config?\"+\"ssid=\"+document.getElementById(\"ssid\").value+\"&pwd=\"+document.getElementById(\"password\").value+\"&hn=\"+document.getElementById(\"hostname\").value+\"&ip=\"+document.getElementById(\"ip\").value+\"&gw=\"+document.getElementById(\"gateway\").value+\"&nm=\"+document.getElementById(\"netmask\").value+\"&dns=\"+document.getElementById(\"dns\").value+\"&rssi=\"+document.getElementById(\"threashold\").value;";
|
||||
// message += "api = \"/config?\"+\"ssid=\"+document.getElementById(\"ssid\").value+\"&pwd=\"+document.getElementById(\"password\").value+\"&hn=\"+document.getElementById(\"hostname\").value+\"&ip=\"+document.getElementById(\"ip\").value+\"&gw=\"+document.getElementById(\"gateway\").value+\"&nm=\"+document.getElementById(\"netmask\").value+\"&dns=\"+document.getElementById(\"dns\").value+\"&rssithreshold=\"+document.getElementById(\"threshold\").value;";
|
||||
message += "fetch(api);await new Promise(resolve => setTimeout(resolve, 1000));location.reload();}</script>";
|
||||
httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str()));
|
||||
return;
|
||||
@@ -164,8 +167,6 @@ void SendHTTPResponse(httpd_req_t *req)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
esp_err_t test_handler(httpd_req_t *req)
|
||||
{
|
||||
SendHTTPResponse(req);
|
||||
@@ -182,7 +183,7 @@ esp_err_t reboot_handlerAP(httpd_req_t *req)
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trigger reboot due to firmware update.");
|
||||
doRebootOTA();
|
||||
return ESP_OK;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
esp_err_t config_ini_handler(httpd_req_t *req)
|
||||
@@ -201,134 +202,164 @@ esp_err_t config_ini_handler(httpd_req_t *req)
|
||||
std::string hn = ""; // hostname
|
||||
std::string ip = "";
|
||||
std::string gw = ""; // gateway
|
||||
std::string nm = ""; // nm
|
||||
std::string nm = ""; // netmask
|
||||
std::string dns = "";
|
||||
std::string rssi = "";
|
||||
std::string rssithreshold = ""; //rssi threshold for WIFI roaming
|
||||
std::string text = "";
|
||||
|
||||
|
||||
if (httpd_req_get_url_query_str(req, _query, 400) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "Query: %s", _query);
|
||||
|
||||
if (httpd_query_key_value(_query, "ssid", _valuechar, 30) == ESP_OK)
|
||||
if (httpd_query_key_value(_query, "ssid", _valuechar, 100) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "ssid is found: %s", _valuechar);
|
||||
ssid = std::string(_valuechar);
|
||||
ssid = UrlDecode(std::string(_valuechar));
|
||||
}
|
||||
|
||||
if (httpd_query_key_value(_query, "pwd", _valuechar, 30) == ESP_OK)
|
||||
if (httpd_query_key_value(_query, "pwd", _valuechar, 100) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "pwd is found: %s", _valuechar);
|
||||
pwd = std::string(_valuechar);
|
||||
pwd = UrlDecode(std::string(_valuechar));
|
||||
}
|
||||
|
||||
if (httpd_query_key_value(_query, "ssid", _valuechar, 30) == ESP_OK)
|
||||
if (httpd_query_key_value(_query, "ssid", _valuechar, 100) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "ssid is found: %s", _valuechar);
|
||||
ssid = std::string(_valuechar);
|
||||
ssid = UrlDecode(std::string(_valuechar));
|
||||
}
|
||||
|
||||
if (httpd_query_key_value(_query, "hn", _valuechar, 30) == ESP_OK)
|
||||
if (httpd_query_key_value(_query, "hn", _valuechar, 100) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "hostname is found: %s", _valuechar);
|
||||
hn = std::string(_valuechar);
|
||||
hn = UrlDecode(std::string(_valuechar));
|
||||
}
|
||||
|
||||
if (httpd_query_key_value(_query, "ip", _valuechar, 30) == ESP_OK)
|
||||
if (httpd_query_key_value(_query, "ip", _valuechar, 100) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "ip is found: %s", _valuechar);
|
||||
ip = std::string(_valuechar);
|
||||
ip = UrlDecode(std::string(_valuechar));
|
||||
}
|
||||
|
||||
if (httpd_query_key_value(_query, "gw", _valuechar, 30) == ESP_OK)
|
||||
if (httpd_query_key_value(_query, "gw", _valuechar, 100) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "gateway is found: %s", _valuechar);
|
||||
gw = std::string(_valuechar);
|
||||
gw = UrlDecode(std::string(_valuechar));
|
||||
}
|
||||
|
||||
if (httpd_query_key_value(_query, "nm", _valuechar, 30) == ESP_OK)
|
||||
if (httpd_query_key_value(_query, "nm", _valuechar, 100) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "netmask is found: %s", _valuechar);
|
||||
nm = std::string(_valuechar);
|
||||
nm = UrlDecode(std::string(_valuechar));
|
||||
}
|
||||
|
||||
if (httpd_query_key_value(_query, "dns", _valuechar, 30) == ESP_OK)
|
||||
if (httpd_query_key_value(_query, "dns", _valuechar, 100) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "dns is found: %s", _valuechar);
|
||||
dns = std::string(_valuechar);
|
||||
dns = UrlDecode(std::string(_valuechar));
|
||||
}
|
||||
|
||||
if (httpd_query_key_value(_query, "rssi", _valuechar, 30) == ESP_OK)
|
||||
if (httpd_query_key_value(_query, "rssithreshold", _valuechar, 100) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "rssi is found: %s", _valuechar);
|
||||
rssi = std::string(_valuechar);
|
||||
ESP_LOGD(TAG, "rssithreshold is found: %s", _valuechar);
|
||||
rssithreshold = UrlDecode(std::string(_valuechar));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
FILE* configfilehandle = fopen(WLAN_CONFIG_FILE, "w");
|
||||
|
||||
text = ";++++++++++++++++++++++++++++++++++\n";
|
||||
text += "; AI on the edge - WLAN configuration\n";
|
||||
text += "; ssid: Name of WLAN network (mandatory), e.g. \"WLAN-SSID\"\n";
|
||||
text += "; password: Password of WLAN network (mandatory), e.g. \"PASSWORD\"\n\n";
|
||||
fputs(text.c_str(), configfilehandle);
|
||||
|
||||
if (ssid.length())
|
||||
ssid = "ssid = \"" + ssid + "\"\n";
|
||||
else
|
||||
ssid = ";ssid = \"\"\n";
|
||||
|
||||
ssid = "ssid = \"\"\n";
|
||||
fputs(ssid.c_str(), configfilehandle);
|
||||
|
||||
if (pwd.length())
|
||||
pwd = "password = \"" + pwd + "\"\n";
|
||||
else
|
||||
pwd = ";password = \"\"\n";
|
||||
pwd = "password = \"\"\n";
|
||||
fputs(pwd.c_str(), configfilehandle);
|
||||
|
||||
text = "\n;++++++++++++++++++++++++++++++++++\n";
|
||||
text += "; Hostname: Name of device in network\n";
|
||||
text += "; This parameter can be configured via WebUI configuration\n";
|
||||
text += "; Default: \"watermeter\", if nothing is configured\n\n";
|
||||
fputs(text.c_str(), configfilehandle);
|
||||
|
||||
if (hn.length())
|
||||
hn = "hostname = \"" + hn + "\"\n";
|
||||
else
|
||||
hn = ";hostname = \"\"\n";
|
||||
hn = ";hostname = \"watermeter\"\n";
|
||||
fputs(hn.c_str(), configfilehandle);
|
||||
|
||||
text = "\n;++++++++++++++++++++++++++++++++++\n";
|
||||
text += "; Fixed IP: If you like to use fixed IP instead of DHCP (default), the following\n";
|
||||
text += "; parameters needs to be configured: ip, gateway, netmask are mandatory, dns optional\n\n";
|
||||
fputs(text.c_str(), configfilehandle);
|
||||
|
||||
if (ip.length())
|
||||
ip = "ip = \"" + ip + "\"\n";
|
||||
else
|
||||
ip = ";ip = \"\"\n";
|
||||
ip = ";ip = \"xxx.xxx.xxx.xxx\"\n";
|
||||
fputs(ip.c_str(), configfilehandle);
|
||||
|
||||
if (gw.length())
|
||||
gw = "gateway = \"" + gw + "\"\n";
|
||||
else
|
||||
gw = ";gateway = \"\"\n";
|
||||
gw = ";gateway = \"xxx.xxx.xxx.xxx\"\n";
|
||||
fputs(gw.c_str(), configfilehandle);
|
||||
|
||||
if (nm.length())
|
||||
nm = "netmask = \"" + nm + "\"\n";
|
||||
else
|
||||
nm = ";netmask = \"\"\n";
|
||||
nm = ";netmask = \"xxx.xxx.xxx.xxx\"\n";
|
||||
fputs(nm.c_str(), configfilehandle);
|
||||
|
||||
text = "\n;++++++++++++++++++++++++++++++++++\n";
|
||||
text += "; DNS server (optional, if no DNS is configured, gateway address will be used)\n\n";
|
||||
fputs(text.c_str(), configfilehandle);
|
||||
|
||||
if (dns.length())
|
||||
dns = "dns = \"" + dns + "\"\n";
|
||||
else
|
||||
dns = ";dns = \"\"\n";
|
||||
dns = ";dns = \"xxx.xxx.xxx.xxx\"\n";
|
||||
fputs(dns.c_str(), configfilehandle);
|
||||
|
||||
if (rssi.length())
|
||||
rssi = "RSSIThreashold = \"" + rssi + "\"\n";
|
||||
text = "\n;++++++++++++++++++++++++++++++++++\n";
|
||||
text += "; WIFI Roaming:\n";
|
||||
text += "; Network assisted roaming protocol is activated by default\n";
|
||||
text += "; AP / mesh system needs to support roaming protocol 802.11k/v\n";
|
||||
text += ";\n";
|
||||
text += "; Optional feature (usually not neccessary):\n";
|
||||
text += "; RSSI Threshold for client requested roaming query (RSSI < RSSIThreshold)\n";
|
||||
text += "; Note: This parameter can be configured via WebUI configuration\n";
|
||||
text += "; Default: 0 = Disable client requested roaming query\n\n";
|
||||
fputs(text.c_str(), configfilehandle);
|
||||
|
||||
if (rssithreshold.length())
|
||||
rssithreshold = "RSSIThreshold = " + rssithreshold + "\n";
|
||||
else
|
||||
rssi = ";rssi = \"\"\n";
|
||||
fputs(rssi.c_str(), configfilehandle);
|
||||
rssithreshold = "RSSIThreshold = 0\n";
|
||||
fputs(rssithreshold.c_str(), configfilehandle);
|
||||
|
||||
fflush(configfilehandle);
|
||||
fclose(configfilehandle);
|
||||
|
||||
std::string zw = "ota without parameter - should not be the case!";
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
httpd_resp_send(req, zw.c_str(), strlen(zw.c_str()));
|
||||
httpd_resp_send_chunk(req, NULL, 0);
|
||||
|
||||
ESP_LOGE(TAG, "end config.ini");
|
||||
httpd_resp_send(req, zw.c_str(), zw.length());
|
||||
|
||||
ESP_LOGD(TAG, "end config.ini");
|
||||
|
||||
return ESP_OK;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
esp_err_t upload_post_handlerAP(httpd_req_t *req)
|
||||
@@ -470,21 +501,28 @@ httpd_handle_t start_webserverAP(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void CheckStartAPMode()
|
||||
{
|
||||
isConfigINI = FileExists(CONFIG_FILE);
|
||||
isWlanINI = FileExists(WLAN_CONFIG_FILE);
|
||||
|
||||
if (!isConfigINI or !isWlanINI)
|
||||
if (!isConfigINI)
|
||||
ESP_LOGW(TAG, "config.ini not found!");
|
||||
|
||||
if (!isWlanINI)
|
||||
ESP_LOGW(TAG, "wlan.ini not found!");
|
||||
|
||||
if (!isConfigINI || !isWlanINI)
|
||||
{
|
||||
ESP_LOGI(TAG, "Starting access point for remote configuration");
|
||||
StatusLED(AP_OR_OTA, 2, true);
|
||||
wifi_init_softAP();
|
||||
start_webserverAP();
|
||||
while(1) { // wait until reboot within task_do_Update_ZIP
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif //#ifdef ENABLE_SOFTAP
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
##################################################
|
||||
# Application specific configuration
|
||||
# Edit this file instead of sdkconfig.esp32cam!
|
||||
# After editting, make sure to explicitly delete
|
||||
# After editing make sure to explicitly delete
|
||||
# sdkconfig.esp32cam to apply your changes!
|
||||
##################################################
|
||||
|
||||
@@ -94,11 +94,14 @@ CONFIG_SPIRAM_SPEED_40M=y
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_BOOT_INIT=y
|
||||
CONFIG_SPIRAM_USE_MALLOC=y
|
||||
#CONFIG_SPIRAM_USE_MEMMAP=y => Does not work: "cam_dma_config(306): frame buffer malloc failed"
|
||||
CONFIG_SPIRAM_MEMTEST=y
|
||||
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384
|
||||
CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=40960
|
||||
CONFIG_SPIRAM_CACHE_WORKAROUND=y
|
||||
CONFIG_SPIRAM_IGNORE_NOTFOUND=y
|
||||
#CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y => Leads to memory fragmentation, see https://github.com/jomjol/AI-on-the-edge-device/issues/2200
|
||||
#CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y => Leads to memory fragmentation, see https://github.com/jomjol/AI-on-the-edge-device/issues/2200
|
||||
|
||||
CONFIG_ESP_INT_WDT_TIMEOUT_MS=300
|
||||
|
||||
@@ -118,6 +121,9 @@ CONFIG_MQTT_MSG_ID_INCREMENTAL=y
|
||||
CONFIG_MQTT_SKIP_PUBLISH_IF_DISCONNECTED=y
|
||||
CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED=y
|
||||
CONFIG_MQTT_USE_CORE_0=y
|
||||
CONFIG_MQTT_USE_CUSTOM_CONFIG=y
|
||||
#CONFIG_MQTT_OUTBOX_EXPIRED_TIMEOUT_MS=5000
|
||||
#CONFIG_MQTT_CUSTOM_OUTBOX=y # -> Use custom outbox in components/jomjol_mqtt/mqtt_outbox.h/cpp. If USE_PSRAM is enabled in there, it will save 10 kBytes of internal RAM. How ever it also leads to memory fragmentation, see https://github.com/jomjol/AI-on-the-edge-device/issues/2200
|
||||
|
||||
CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=n
|
||||
|
||||
@@ -133,7 +139,16 @@ CONFIG_GC032A_SUPPORT=n
|
||||
CONFIG_GC0308_SUPPORT=n
|
||||
CONFIG_BF3005_SUPPORT=n
|
||||
|
||||
#only necessary for task analysis (include/defines -> TASK_ANALYSIS_ON)
|
||||
CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=4864
|
||||
|
||||
#only necessary for WIFI mesh roaming (include/defines.h -> WLAN_USE_MESH_ROAMING)
|
||||
#CONFIG_WPA_11KV_SUPPORT=y
|
||||
#CONFIG_WPA_SCAN_CACHE=n
|
||||
#CONFIG_WPA_MBO_SUPPORT=n
|
||||
#CONFIG_WPA_11R_SUPPORT=n // Will be supported with ESP-IDF v5.0
|
||||
#CONFIG_WPA_DEBUG_PRINT=n
|
||||
|
||||
#only necessary for task analysis (include/defines.h -> TASK_ANALYSIS_ON)
|
||||
#set in [env:esp32cam-dev-task-analysis]
|
||||
#CONFIG_FREERTOS_USE_TRACE_FACILITY=1
|
||||
#CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
|
||||
@@ -144,3 +159,5 @@ CONFIG_BF3005_SUPPORT=n
|
||||
#I (2112) esp_himem: Initialized. Using last 8 32KB address blocks for bank switching on 4352 KB of physical memory.
|
||||
CONFIG_SPIRAM_BANKSWITCH_ENABLE=n
|
||||
#CONFIG_SPIRAM_BANKSWITCH_RESERVE is not set
|
||||
|
||||
CONFIG_PM_ENABLE=y
|
||||
|
||||
@@ -9,11 +9,11 @@ UnderTestPost* setUpClassFlowPostprocessing(t_CNNType digType, t_CNNType anaType
|
||||
ClassFlowCNNGeneral* _analog;
|
||||
ClassFlowCNNGeneral* _digit;
|
||||
std::vector<ClassFlow*> FlowControll;
|
||||
ClassFlowMakeImage* flowmakeimage;
|
||||
ClassFlowTakeImage* flowtakeimage;
|
||||
|
||||
// wird im doFlow verwendet
|
||||
flowmakeimage = new ClassFlowMakeImage(&FlowControll);
|
||||
FlowControll.push_back(flowmakeimage);
|
||||
flowtakeimage = new ClassFlowTakeImage(&FlowControll);
|
||||
FlowControll.push_back(flowtakeimage);
|
||||
|
||||
// Die Modeltypen werden gesetzt, da keine Modelle verwendet werden.
|
||||
_analog = new ClassFlowCNNGeneral(nullptr, anaType);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <unity.h>
|
||||
#include <ClassFlowPostProcessing.h>
|
||||
#include <ClassFlowCNNGeneral.h>
|
||||
#include <ClassFlowMakeImage.h>
|
||||
#include <ClassFlowTakeImage.h>
|
||||
#include <Helper.h>
|
||||
|
||||
|
||||
|
||||
@@ -76,3 +76,30 @@ void testNegative() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fehlerberichte aus Issues
|
||||
*
|
||||
*/
|
||||
void testNegative_Issues() {
|
||||
// Ohne decimal_shift
|
||||
std::vector<float> digits = { 2.0, 2.0, 0.0, 1.0, 7.2, 9.0, 8.0};
|
||||
std::vector<float> analogs = { };
|
||||
double preValue_extended = 22018.080;
|
||||
double preValue = 22018.08;
|
||||
|
||||
const char* expected = "22017.98";
|
||||
|
||||
// https://github.com/jomjol/AI-on-the-edge-device/issues/2145#issuecomment-1461899094
|
||||
// extendResolution=false
|
||||
// value < preValue
|
||||
// Prüfung eingeschaltet => Fehler
|
||||
preValue = 22018.08; // zu groß
|
||||
UnderTestPost* underTestPost = init_do_flow(analogs, digits, Digital100, false, false, -2);
|
||||
setAllowNegatives(underTestPost, false);
|
||||
setPreValue(underTestPost, preValue_extended);
|
||||
std::string result = process_doFlow(underTestPost);
|
||||
TEST_ASSERT_EQUAL_STRING("Neg. Rate - Read: - Raw: 22017.98 - Pre: 22018.08 ", underTestPost->getReadoutError().c_str());
|
||||
TEST_ASSERT_EQUAL_STRING(expected, result.c_str());
|
||||
delete underTestPost;
|
||||
|
||||
}
|
||||
@@ -108,9 +108,9 @@ extern "C" void app_main()
|
||||
esp_log_level_set("*", ESP_LOG_DEBUG); // set all components to ERROR level
|
||||
|
||||
UNITY_BEGIN();
|
||||
|
||||
RUN_TEST(testNegative);
|
||||
|
||||
RUN_TEST(testNegative_Issues);
|
||||
/* RUN_TEST(testNegative);
|
||||
|
||||
RUN_TEST(test_analogToDigit_Standard);
|
||||
RUN_TEST(test_analogToDigit_Transition);
|
||||
RUN_TEST(test_doFlowPP);
|
||||
@@ -121,6 +121,6 @@ extern "C" void app_main()
|
||||
|
||||
// getReadoutRawString test
|
||||
RUN_TEST(test_getReadoutRawString);
|
||||
|
||||
*/
|
||||
UNITY_END();
|
||||
}
|
||||
|
||||
@@ -3,4 +3,4 @@ The firmware got moved to the [Release page](https://github.com/jomjol/AI-on-the
|
||||
|
||||
# Installation Guide
|
||||
|
||||
You find the complete installation guide at <https://github.com/jomjol/AI-on-the-edge-device/wiki/Installation>
|
||||
You find the complete installation guide at https://jomjol.github.io/AI-on-the-edge-device-docs/Installation/
|
||||
|
||||
Binary file not shown.
BIN
sd-card/config/ana-class100_0157_s1_q.tflite
Normal file
BIN
sd-card/config/ana-class100_0157_s1_q.tflite
Normal file
Binary file not shown.
Binary file not shown.
BIN
sd-card/config/ana-cont_1105_s2_q.tflite
Normal file
BIN
sd-card/config/ana-cont_1105_s2_q.tflite
Normal file
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
[MakeImage]
|
||||
;LogImageLocation = /log/source
|
||||
[TakeImage]
|
||||
;RawImagesLocation = /log/source
|
||||
WaitBeforeTakingPicture = 5
|
||||
;LogfileRetentionInDays = 15
|
||||
;RawImagesRetention = 15
|
||||
Brightness = 0
|
||||
Contrast = 0
|
||||
Saturation = 0
|
||||
@@ -9,7 +9,7 @@ LEDIntensity = 50
|
||||
ImageQuality = 12
|
||||
ImageSize = VGA
|
||||
FixedExposure = false
|
||||
;Demo = true
|
||||
Demo = false
|
||||
|
||||
[Alignment]
|
||||
InitialRotate = 179
|
||||
@@ -22,20 +22,19 @@ FlipImageSize = false
|
||||
/config/ref1.jpg 442 142
|
||||
|
||||
[Digits]
|
||||
Model = /config/dig-cont_0600_s3.tflite
|
||||
Model = /config/dig-cont_0611_s3_q.tflite
|
||||
CNNGoodThreshold = 0.5
|
||||
;LogImageLocation = /log/digit
|
||||
;LogfileRetentionInDays = 3
|
||||
;ROIImagesLocation = /log/digit
|
||||
;ROIImagesRetention = 3
|
||||
main.dig1 294 126 30 54 false
|
||||
main.dig2 343 126 30 54 false
|
||||
main.dig3 391 126 30 54 false
|
||||
|
||||
[Analog]
|
||||
Model = /config/ana-cont_11.3.1_s2.tflite
|
||||
Model = /config/ana-cont_1105_s2_q.tflite
|
||||
CNNGoodThreshold = 0.5
|
||||
;LogImageLocation = /log/analog
|
||||
;LogfileRetentionInDays = 3
|
||||
ExtendedResolution = true
|
||||
;ROIImagesLocation = /log/analog
|
||||
;ROIImagesRetention = 3
|
||||
main.ana1 432 230 92 92 false
|
||||
main.ana2 379 332 92 92 false
|
||||
main.ana3 283 374 92 92 false
|
||||
@@ -46,11 +45,11 @@ main.DecimalShift = 0
|
||||
main.AnalogDigitalTransitionStart = 9.2
|
||||
PreValueUse = true
|
||||
PreValueAgeStartup = 720
|
||||
AllowNegativeRates = false
|
||||
main.AllowNegativeRates = false
|
||||
main.MaxRateValue = 0.05
|
||||
;main.MaxRateType = AbsoluteChange
|
||||
;main.ExtendedResolution = false
|
||||
;main.IgnoreLeadingNaN = true
|
||||
main.ExtendedResolution = false
|
||||
main.IgnoreLeadingNaN = false
|
||||
ErrorMessage = true
|
||||
CheckDigitIncreaseConsistency = false
|
||||
|
||||
@@ -60,17 +59,25 @@ CheckDigitIncreaseConsistency = false
|
||||
;ClientID = watermeter
|
||||
;user = USERNAME
|
||||
;password = PASSWORD
|
||||
;SetRetainFlag = true
|
||||
;HomeassistantDiscovery = true
|
||||
RetainMessages = false
|
||||
HomeassistantDiscovery = false
|
||||
;MeterType = other
|
||||
|
||||
;[InfluxDB]
|
||||
;Uri = undefined
|
||||
;Database =
|
||||
;Database = undefined
|
||||
;Measurement = undefined
|
||||
;user = undefined
|
||||
;password = undefined
|
||||
|
||||
;[InfluxDBv2]
|
||||
;Uri = undefined
|
||||
;Database = undefined
|
||||
;Measurement = undefined
|
||||
;Org = undefined
|
||||
;Token = undefined
|
||||
;main.Fieldname = undefined
|
||||
|
||||
;[GPIO]
|
||||
;MainTopicMQTT = wasserzaehler/GPIO
|
||||
;IO0 = input disabled 10 false false
|
||||
@@ -85,19 +92,20 @@ LEDColor = 150 150 150
|
||||
|
||||
[AutoTimer]
|
||||
AutoStart = true
|
||||
Intervall = 5
|
||||
Interval = 5
|
||||
|
||||
[DataLogging]
|
||||
DataLogActive = true
|
||||
DataLogRetentionInDays = 3
|
||||
DataFilesRetention = 3
|
||||
|
||||
[Debug]
|
||||
Logfile = 1
|
||||
LogfileRetentionInDays = 3
|
||||
LogLevel = 1
|
||||
LogfilesRetention = 3
|
||||
|
||||
[System]
|
||||
TimeZone = CET-1CEST,M3.5.0,M10.5.0/3
|
||||
;TimeServer = pool.ntp.org
|
||||
;AutoAdjustSummertime = false
|
||||
;Hostname = undefined
|
||||
;RSSIThreshold = 0
|
||||
CPUFrequency = 160
|
||||
SetupMode = true
|
||||
|
||||
Binary file not shown.
BIN
sd-card/config/dig-class100_0160_s2_q.tflite
Normal file
BIN
sd-card/config/dig-class100_0160_s2_q.tflite
Normal file
Binary file not shown.
Binary file not shown.
BIN
sd-card/config/dig-cont_0611_s3.tflite
Normal file
BIN
sd-card/config/dig-cont_0611_s3.tflite
Normal file
Binary file not shown.
BIN
sd-card/config/dig-cont_0611_s3_q.tflite
Normal file
BIN
sd-card/config/dig-cont_0611_s3_q.tflite
Normal file
Binary file not shown.
@@ -121,9 +121,15 @@ function fetchFiles(urls, filesData, index, retry, zipFilename) {
|
||||
else if (retry == 1) { // longer timeout
|
||||
xhr.timeout = 5000; // time in milliseconds
|
||||
}
|
||||
else { // very long timeout
|
||||
else if (retry == 2) { // longer timeout
|
||||
xhr.timeout = 10000; // time in milliseconds
|
||||
}
|
||||
else if (retry == 3) { // longer timeout
|
||||
xhr.timeout = 20000; // time in milliseconds
|
||||
}
|
||||
else { // very long timeout
|
||||
xhr.timeout = 30000; // time in milliseconds
|
||||
}
|
||||
|
||||
xhr.onload = () => { // Request finished
|
||||
//console.log(url + " done");
|
||||
@@ -140,6 +146,20 @@ function fetchFiles(urls, filesData, index, retry, zipFilename) {
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onprogress = (e) => { // XMLHttpRequest progress ... extend timeout
|
||||
xhr.timeout = xhr.timeout + 500;
|
||||
};
|
||||
|
||||
xhr.onerror = (e) => { // XMLHttpRequest error loading
|
||||
console.log("Error on fetching " + url + "!");
|
||||
if (retry > 5) {
|
||||
setStatus("<span style=\"color: red\">Backup failed, please restart the device and try again!</span>");
|
||||
}
|
||||
else {
|
||||
fetchFiles(urls, filesData, index, retry+1, zipFilename);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.ontimeout = (e) => { // XMLHttpRequest timed out
|
||||
console.log("Timeout on fetching " + url + "!");
|
||||
if (retry > 5) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
/* 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! */
|
||||
var domainname_for_testing = "192.168.178.44";
|
||||
var domainname_for_testing = "192.168.1.153";
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 2px;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.box {
|
||||
@@ -33,19 +34,22 @@
|
||||
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h3>Todays Data</h3>
|
||||
<h4>Last part of Todays Data</h4>
|
||||
<div class="box">
|
||||
<div class="row header">
|
||||
<button onClick="reload();">Reload</button>
|
||||
<button onClick="window.open(getDomainname() + '/datafileact');">Show full data</button>
|
||||
<button onClick="window.location.href = getDomainname() + '/fileserver/log/data/'">Show older data files</button>
|
||||
<button onClick="window.location.href = 'graph.html?v=$COMMIT_HASH'">Show graph</button>
|
||||
<button onClick="reload();">Refresh</button>
|
||||
<button onClick="window.open(getDomainname() + '/datafileact');">Show Full File</button>
|
||||
<button onClick="window.location.href = getDomainname() + '/fileserver/log/data/'">Show Data Files</button>
|
||||
<button onClick="window.location.href = 'graph.html?v=$COMMIT_HASH'">Show Graph</button>
|
||||
</div>
|
||||
<div class="row content" id="data"><br><br><br><b>Loading Data file, please wait...</b></div>
|
||||
<div class="row footer">
|
||||
<button onClick="reload();">Reload</button>
|
||||
<button onClick="window.open(getDomainname() + '/datafileact');">Show full data</button>
|
||||
<button onClick="window.location.href = getDomainname() + '/fileserver/log/data/'">Show older data files</button>
|
||||
<button onClick="window.location.href = 'graph.html?v=$COMMIT_HASH'">Show graph</button>
|
||||
<button onClick="reload();">Refresh</button>
|
||||
<button onClick="window.open(getDomainname() + '/datafileact');">Show Full File</button>
|
||||
<button onClick="window.location.href = getDomainname() + '/fileserver/log/data/'">Show Data Files</button>
|
||||
<button onClick="window.location.href = 'graph.html?v=$COMMIT_HASH'">Show Graph</button>
|
||||
<p></p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
|
||||
<title>Make Alignment</title>
|
||||
<title>Alignment Marks</title>
|
||||
<meta charset="utf-8"/>
|
||||
|
||||
<style>
|
||||
@@ -48,7 +48,11 @@ select {
|
||||
|
||||
<body style="font-family: arial; padding: 0px 10px;">
|
||||
|
||||
<h2>Define Alignment Structure in Reference Image</h2>
|
||||
<h2>Alignment Marks</h2>
|
||||
<p>On this page you define two Reference Marks.
|
||||
See <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Alignment/ target=_blank>https://jomjol.github.io/AI-on-the-edge-device-docs/Alignment/</a> for explanations.</p>
|
||||
<p>After saving the Reference Marks, you can define the <a href=edit_digits.html>digit</a> resp. <a href=edit_analog.html>analog</a> ROI's.<br>
|
||||
Only after those steps a reboot is required.</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
@@ -88,11 +92,9 @@ select {
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><input class="button" type="submit" name="saveroi" onclick="SaveToConfig()" value="Save"></td>
|
||||
<td><input class="button" type="submit" name="saveroi" onclick="SaveToConfig()" value="Save">
|
||||
<p>Proceed to update the <a href=edit_digits.html>digit</a> resp. <a href=edit_analog.html>analog</a> ROI's when you are done.</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate changes</button></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
|
||||
@@ -113,7 +115,7 @@ select {
|
||||
param;
|
||||
|
||||
function doReboot() {
|
||||
if (confirm("Are you sure you want to reboot? Did you save the config?")) {
|
||||
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;
|
||||
@@ -131,7 +133,7 @@ function SaveToConfig(){
|
||||
WriteConfigININew();
|
||||
UpdateConfigReference(domainname)
|
||||
SaveConfigToServer(domainname);
|
||||
firework.launch('Configuration got updated. Please reboot to activate changes!', 'success', 5000);
|
||||
firework.launch('Configuration got updated. It will get applied after the next reboot!', 'success', 5000);
|
||||
}
|
||||
|
||||
function EnhanceContrast(){
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
|
||||
<meta charset="utf-8"/>
|
||||
<title>Make Analog Alignment</title>
|
||||
<title>Analog ROI's</title>
|
||||
|
||||
<style>
|
||||
h1 {font-size: 2em;}
|
||||
@@ -67,10 +67,12 @@ th, td {
|
||||
|
||||
<body style="font-family: arial; padding: 0px 10px;">
|
||||
|
||||
<h2><input type="checkbox" id="Category_Analog_enabled" value="1" onclick = 'EnDisableAnalog()' checked >
|
||||
Edit Analog</h2>
|
||||
<h2>Analog ROI's</h2>
|
||||
<p>On this page you define ROI's for the analog counters.
|
||||
See <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/ target=_blank>https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/</a> for explanations.</p>
|
||||
|
||||
<input type="checkbox" id="Category_Analog_enabled" value="1" onclick = 'EnDisableAnalog()' checked ><label for="Category_Analog_enabled">Enable Analog ROI's</label></p>
|
||||
|
||||
|
||||
<div id="div1">
|
||||
<table>
|
||||
<tr>
|
||||
@@ -83,9 +85,6 @@ th, td {
|
||||
<tr>
|
||||
<class id="Numbers_text" style="color:black;"><b>Number: </b></class>
|
||||
<select id="Numbers_value1" onchange="numberChanged()">
|
||||
<option value="0" selected>default</option>
|
||||
<option value="1" >NT</option>
|
||||
<option value="2" >HT</option>
|
||||
</select>
|
||||
<input class="move" type="submit" id="renameNumber" name="renameNumber" onclick="renameNumber()" value="Rename">
|
||||
<input class="move" type="submit" id="newNumber" name="newNumber" onclick="newNumber()" value="New">
|
||||
@@ -103,8 +102,6 @@ th, td {
|
||||
<tr>
|
||||
<td>
|
||||
<select id="index" name="index" onchange="ChangeSelection()" tabindex=1>
|
||||
<option value="0" selected>ROI 0</option>
|
||||
<option value="1" >ROI 1</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
@@ -118,26 +115,26 @@ th, td {
|
||||
<tr>
|
||||
<td>x: <input type="number" name="refx" id="refx" step=1 onchange="valuemanualchanged()" tabindex=2></td>
|
||||
<td>Δx: <input type="number" name="refdx" id="refdx" step=1 onchange="valuemanualchangeddx()" tabindex=4></td>
|
||||
<td><label for="lockAR"> Lock aspect ratio: </label><input type="checkbox" id="lockAR" name="lockAR" value="1" onclick="changelockAR()" checked tabindex=6></td>
|
||||
<td><input type="checkbox" id="lockAspectRatio" name="lockAspectRatio" value="1" onclick="changelockAspectRatio()" checked tabindex=6><label for="lockAspectRatio"> Lock aspect ratio</label></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>y: <input type="number" name="refy" id="refy" step=1 onchange="valuemanualchanged()" tabindex=3></td>
|
||||
<td>Δy: <input type="number" name="refdy" id="refdy" step=1 onchange="valuemanualchanged()" tabindex=5></td>
|
||||
<td><label for="lockSizes"> Synchronize Δx and Δy between ROIs</label><input type="checkbox" id="lockSizes" name="lockSizes" value="1" onclick="changelockSizes()" checked tabindex=7></td>
|
||||
<td><input type="checkbox" id="lockSizes" name="lockSizes" value="1" onclick="changelockSizes()" checked tabindex=7><label for="lockSizes"> Synchronize Δx and Δy between ROIs</label></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"></td>
|
||||
<td><label for="CCW"> Counter-Clockwise Rotation: </label><input type="checkbox" id="CCW" name="CCW" value="0" onclick="changeCCW()" unchecked tabindex=8></td>
|
||||
<td><input type="checkbox" id="CCW" name="CCW" value="0" onclick="changeCCW()" unchecked tabindex=8><label for="CCW"> Counter-Clockwise Rotation: </label></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><input class="button" type="submit" id="saveroi" name="saveroi" onclick="SaveToConfig()" value="Save" tabindex=9></td>
|
||||
<td><input class="button" type="submit" id="saveroi" name="saveroi" onclick="SaveToConfig()" value="Save" tabindex=9>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate changes</button></td>
|
||||
<td><button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate the changes</button></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -157,13 +154,13 @@ th, td {
|
||||
cofcat,
|
||||
param,
|
||||
enhanceCon = false;
|
||||
lockAR = true;
|
||||
lockSizes = true;
|
||||
lockAspectRatio = true;
|
||||
lockSizes = false;
|
||||
domainname = getDomainname();
|
||||
|
||||
|
||||
function doReboot() {
|
||||
if (confirm("Are you sure you want to reboot? Did you save the config?")) {
|
||||
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;
|
||||
@@ -220,6 +217,7 @@ function deleteROI(){
|
||||
aktindex = ROIInfo.length - 1;
|
||||
}
|
||||
UpdateROIs();
|
||||
draw();
|
||||
}
|
||||
|
||||
function newROI(){
|
||||
@@ -257,12 +255,13 @@ function moveNext(){
|
||||
UpdateROIs();
|
||||
}
|
||||
|
||||
function changelockAR(){
|
||||
lockAR = document.getElementById("lockAR").checked;
|
||||
function changelockAspectRatio(){
|
||||
lockAspectRatio = document.getElementById("lockAspectRatio").checked;
|
||||
}
|
||||
|
||||
function changelockSizes(){
|
||||
lockSizes = document.getElementById("lockSizes").checked;
|
||||
UpdateROIs();
|
||||
}
|
||||
|
||||
function changeCCW(){
|
||||
@@ -281,7 +280,7 @@ function changeCCW(){
|
||||
|
||||
function ChangeSelection(){
|
||||
aktindex = parseInt(document.getElementById("index").value);
|
||||
// lockAR = true;
|
||||
// lockAspectRatio = true;
|
||||
UpdateROIs();
|
||||
}
|
||||
|
||||
@@ -290,7 +289,7 @@ function SaveToConfig(){
|
||||
cofcat["Analog"]["enabled"] = document.getElementById("Category_Analog_enabled").checked;
|
||||
WriteConfigININew();
|
||||
SaveConfigToServer(domainname);
|
||||
firework.launch('Configuration got updated. Please reboot to activate changes!', 'success', 5000);
|
||||
firework.launch('Configuration got updated. It will get applied after the next reboot!', 'success', 5000);
|
||||
}
|
||||
|
||||
|
||||
@@ -315,6 +314,7 @@ function UpdateROIs(_sel){
|
||||
document.getElementById("newROI").disabled = false;
|
||||
document.getElementById("deleteROI").disabled = true;
|
||||
document.getElementById("index").disabled = true;
|
||||
document.getElementById("saveroi").disabled = true;
|
||||
document.getElementById("renameROI").disabled = true;
|
||||
document.getElementById("moveNext").disabled = true;
|
||||
document.getElementById("movePrevious").disabled = true;
|
||||
@@ -359,7 +359,7 @@ function UpdateROIs(_sel){
|
||||
document.getElementById("moveNext").disabled = true;
|
||||
}
|
||||
|
||||
document.getElementById("lockAR").checked = lockAR;
|
||||
document.getElementById("lockAspectRatio").checked = lockAspectRatio;
|
||||
document.getElementById("lockSizes").checked = lockSizes;
|
||||
|
||||
document.getElementById("refx").value = ROIInfo[aktindex]["x"];
|
||||
@@ -417,6 +417,29 @@ function UpdateROIs(_sel){
|
||||
param = getConfigParameters();
|
||||
cofcat = getConfigCategory();
|
||||
UpdateNUMBERS();
|
||||
|
||||
/* Check if the ROIs have same dy and dx. If so, tick the sync checkbox */
|
||||
var all_dx_dy_Identical = true;
|
||||
if (ROIInfo.length > 1) {
|
||||
for (var i = 1; i < (ROIInfo.length); ++i) { // 2nd .. last ROI
|
||||
if (parseInt(ROIInfo[i].dx) != parseInt(ROIInfo[0].dx) ||
|
||||
parseInt(ROIInfo[i].dy) != parseInt(ROIInfo[0].dy)) {
|
||||
all_dx_dy_Identical = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (all_dx_dy_Identical) {
|
||||
lockSizes = true;
|
||||
console.log("All ROI have the same dX and dY, ticking the sync checkbox!");
|
||||
document.getElementById("lockSizes").checked = lockSizes;
|
||||
}
|
||||
else {
|
||||
console.log("Not all ROI have the same dX and dY, unticking the sync checkbox!");
|
||||
}
|
||||
|
||||
|
||||
drawImage();
|
||||
draw();
|
||||
}
|
||||
@@ -508,11 +531,17 @@ function drawTextBG(context, txt, x, y, padding) {
|
||||
if (typeof ROIInfo === 'undefined') { // During init, ROIInfo is not defined yet
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
var canvas = document.getElementById('canvas');
|
||||
var context = canvas.getContext('2d');
|
||||
context.drawImage(imageObj, 0, 0);
|
||||
|
||||
if (ROIInfo.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.font = "15px Arial";
|
||||
context.fillStyle = "red";
|
||||
context.textAlign = "center";
|
||||
@@ -538,7 +567,7 @@ function drawTextBG(context, txt, x, y, padding) {
|
||||
{
|
||||
if (_nb != _number)
|
||||
{
|
||||
lw = 1;
|
||||
lw = 2;
|
||||
context.lineWidth = lw;
|
||||
context.strokeStyle = "#990000";
|
||||
var x0 = parseInt(ROIInfo[_nb].x) - parseInt(lw/2);
|
||||
@@ -547,12 +576,24 @@ function drawTextBG(context, txt, x, y, padding) {
|
||||
var dy = parseInt(ROIInfo[_nb].dy) + parseInt(lw);
|
||||
context.strokeRect(x0, y0, dx, dy);
|
||||
drawTextBG(context, ROIInfo[_nb]["name"], x0+dx/2-0.5, y0-13, 5);
|
||||
|
||||
lw = 1;
|
||||
var x0 = parseInt(ROIInfo[_nb].x) - parseInt(lw/2);
|
||||
var y0 = parseInt(ROIInfo[_nb].y) - parseInt(lw/2);
|
||||
var dx = parseInt(ROIInfo[_nb].dx) + parseInt(lw);
|
||||
var dy = parseInt(ROIInfo[_nb].dy) + parseInt(lw);
|
||||
context.strokeRect(x0, y0, dx, dy);
|
||||
context.lineWidth = lw;
|
||||
context.beginPath();
|
||||
context.arc(x0+dx/2, y0+dy/2, dx/2, 0, 2 * Math.PI);
|
||||
context.moveTo(x0+dx/2, y0);
|
||||
context.lineTo(x0+dx/2, y0+dy);
|
||||
context.moveTo(x0, y0+dy/2);
|
||||
context.lineTo(x0+dx, y0+dy/2);
|
||||
context.stroke();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
lw = 4
|
||||
context.lineWidth = lw;
|
||||
context.strokeStyle = "#FF0000";
|
||||
@@ -620,7 +661,7 @@ function drawTextBG(context, txt, x, y, padding) {
|
||||
zw = getCoords(this)
|
||||
|
||||
|
||||
if (lockAR) {
|
||||
if (lockAspectRatio) {
|
||||
rect.h = (e.pageY - zw.top) - rect.startY;
|
||||
rect.w = Math.round(rect.h * ROIInfo[aktindex]["ar"]); }
|
||||
else {
|
||||
@@ -655,7 +696,7 @@ function drawTextBG(context, txt, x, y, padding) {
|
||||
if (!drag) {
|
||||
rect.w = document.getElementById("refdx").value;
|
||||
rect.h = document.getElementById("refdy").value;
|
||||
if (lockAR) {
|
||||
if (lockAspectRatio) {
|
||||
rect.w = Math.round(rect.h * ROIInfo[aktindex]["ar"]);
|
||||
document.getElementById("refdx").value = rect.w;
|
||||
}
|
||||
@@ -670,7 +711,7 @@ function drawTextBG(context, txt, x, y, padding) {
|
||||
if (!drag) {
|
||||
rect.w = document.getElementById("refdx").value;
|
||||
rect.h = document.getElementById("refdy").value;
|
||||
if (lockAR) {
|
||||
if (lockAspectRatio) {
|
||||
rect.h = Math.round(rect.w / ROIInfo[aktindex]["ar"]);
|
||||
document.getElementById("refdy").value = rect.h;
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ function saveTextAsFile()
|
||||
FileDeleteOnServer("/config/config.ini", domainname);
|
||||
var textToSave = document.getElementById("inputTextToSave").value;
|
||||
FileSendContent(textToSave, "/config/config.ini", domainname);
|
||||
firework.launch('Configuration got updated. Please reboot to activate changes!', 'success', 5000);
|
||||
firework.launch('Configuration got updated. It will get applied after the next reboot!', 'success', 5000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
|
||||
<meta charset="utf-8"/>
|
||||
<title>Make Digital Alignment</title>
|
||||
<title>Digit ROI's</title>
|
||||
|
||||
<style>
|
||||
h1 {font-size: 2em;}
|
||||
@@ -58,8 +58,15 @@ th, td {
|
||||
|
||||
<body style="font-family: arial; padding: 0px 10px;">
|
||||
|
||||
<h2><input type="checkbox" id="Category_Digits_enabled" value="1" onclick = 'EnDisableDigits()' checked >
|
||||
Edit Digits</h2>
|
||||
<h2>Digit ROI's</h2>
|
||||
<p>On this page you define ROI's for the digits.
|
||||
See <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/ target=_blank>https://jomjol.github.io/AI-on-the-edge-device-docs/ROI-Configuration/</a> for explanations.</p>
|
||||
|
||||
<p><input type="checkbox" id="Category_Digits_enabled" value="1" onclick = 'EnDisableDigits()' checked><label for="Category_Digits_enabled">Enable Digit ROI's</label></p>
|
||||
|
||||
<p>After saving the digit ROI's, you can define the <a href=edit_analog.html>analog</a> ROI's if your meter has analog counters.<br>
|
||||
Only after those steps a reboot is required.</p>
|
||||
|
||||
|
||||
<div id="div1">
|
||||
|
||||
@@ -74,9 +81,6 @@ th, td {
|
||||
<tr>
|
||||
<class id="Numbers_text" style="color:black;"><b>Number:</b> </class>
|
||||
<select id="Numbers_value1" onchange="numberChanged()">
|
||||
<option value="0" selected>default</option>
|
||||
<option value="1" >NT</option>
|
||||
<option value="2" >HT</option>
|
||||
</select>
|
||||
<input class="move" type="submit" id="renameNumber" name="renameNumber" onclick="renameNumber()" value="Rename">
|
||||
<input class="move" type="submit" id="newNumber" name="newNumber" onclick="newNumber()" value="New">
|
||||
@@ -94,8 +98,6 @@ th, td {
|
||||
<tr>
|
||||
<td>
|
||||
<select id="index" name="index" onchange="ChangeSelection()" tabindex=1>
|
||||
<option value="0" selected>ROI 0</option>
|
||||
<option value="1" >ROI 1</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
@@ -108,7 +110,7 @@ th, td {
|
||||
<tr>
|
||||
<td>x: <input type="number" name="refx" id="refx" step=1 onchange="valuemanualchanged()" tabindex=2></td>
|
||||
<td>Δx: <input type="number" name="refdx" id="refdx" step=1 onchange="valuemanualchangeddx()" tabindex=4></td>
|
||||
<td><input type="checkbox" id="lockAR" name="lockAR" value="1" onclick="changelockAR()" checked tabindex=6><label for="lockAR"> Lock aspect ratio </label></td>
|
||||
<td><input type="checkbox" id="lockAspectRatio" name="lockAspectRatio" value="1" onclick="changelockAspectRatio()" checked tabindex=6><label for="lockAspectRatio"> Lock aspect ratio </label></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>y: <input type="number" name="refy" id="refy" step=1 onchange="valuemanualchanged()" tabindex=3></td>
|
||||
@@ -126,11 +128,9 @@ th, td {
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><input class="button" type="submit" id="saveroi" name="saveroi" onclick="SaveToConfig()" value="Save" tabindex=10></td>
|
||||
<td><input class="button" type="submit" id="saveroi" name="saveroi" onclick="SaveToConfig()" value="Save" tabindex=10>
|
||||
<p>Proceed to update the <a href=edit_analog.html>analog</a> ROI's when you are done or <a href=reboot_page.html>reboot</a> if there are no analogue counters.</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate changes</button></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
|
||||
@@ -149,14 +149,14 @@ th, td {
|
||||
cofcat,
|
||||
param,
|
||||
enhanceCon = false;
|
||||
lockAR = true;
|
||||
lockSizes = true;
|
||||
lockAspectRatio = true;
|
||||
lockSizes = false;
|
||||
lockSpaceEquidistant = true;
|
||||
space = 3;
|
||||
domainname = getDomainname();
|
||||
|
||||
function doReboot() {
|
||||
if (confirm("Are you sure you want to reboot? Did you save the config?")) {
|
||||
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;
|
||||
@@ -264,12 +264,14 @@ function moveNext(){
|
||||
valuemanualchanged();
|
||||
}
|
||||
|
||||
function changelockAR(){
|
||||
lockAR = document.getElementById("lockAR").checked;
|
||||
function changelockAspectRatio(){
|
||||
lockAspectRatio = document.getElementById("lockAspectRatio").checked;
|
||||
}
|
||||
|
||||
function changelockSizes(){
|
||||
lockSizes = document.getElementById("lockSizes").checked;
|
||||
UpdateROIs();
|
||||
valuemanualchangedspace();
|
||||
|
||||
if (!lockSizes) {
|
||||
firework.launch("For best results it is in most cases advised to keep the y, Δx and Δy identical!", 'warning', 10000);
|
||||
@@ -278,11 +280,18 @@ function changelockSizes(){
|
||||
|
||||
function changeLockSpaceEquidistant(){
|
||||
lockSpaceEquidistant = document.getElementById("lockSpaceEquidistant").checked;
|
||||
if (!lockSpaceEquidistant) {
|
||||
document.getElementById("space").disabled = true;
|
||||
}
|
||||
else {
|
||||
document.getElementById("space").disabled = false;
|
||||
}
|
||||
UpdateROIs();
|
||||
}
|
||||
|
||||
function ChangeSelection(){
|
||||
aktindex = parseInt(document.getElementById("index").value);
|
||||
// lockAR = true;
|
||||
// lockAspectRatio = true;
|
||||
UpdateROIs();
|
||||
}
|
||||
|
||||
@@ -291,7 +300,7 @@ function SaveToConfig(){
|
||||
cofcat["Digits"]["enabled"] = document.getElementById("Category_Digits_enabled").checked;
|
||||
WriteConfigININew();
|
||||
SaveConfigToServer(domainname);
|
||||
firework.launch('Configuration got updated. Please reboot to activate changes!', 'success', 5000);
|
||||
firework.launch('Configuration got updated. It will get applied after the next reboot!', 'success', 5000);
|
||||
}
|
||||
|
||||
|
||||
@@ -362,10 +371,16 @@ function UpdateROIs(_sel){
|
||||
document.getElementById("moveNext").disabled = true;
|
||||
}
|
||||
|
||||
document.getElementById("lockAR").checked = lockAR;
|
||||
document.getElementById("lockAspectRatio").checked = lockAspectRatio;
|
||||
document.getElementById("lockSizes").checked = lockSizes;
|
||||
document.getElementById("lockSpaceEquidistant").checked = lockSpaceEquidistant;
|
||||
document.getElementById("space").value = space;
|
||||
if (!lockSpaceEquidistant) {
|
||||
document.getElementById("space").disabled = true;
|
||||
}
|
||||
else {
|
||||
document.getElementById("space").disabled = false;
|
||||
}
|
||||
|
||||
document.getElementById("refx").value = ROIInfo[aktindex]["x"];
|
||||
document.getElementById("refy").value = ROIInfo[aktindex]["y"];
|
||||
@@ -418,8 +433,53 @@ function UpdateROIs(_sel){
|
||||
cofcat = getConfigCategory();
|
||||
UpdateNUMBERS();
|
||||
|
||||
space = ROIInfo[1].x - parseInt(ROIInfo[0].x) - parseInt(ROIInfo[0].dx);
|
||||
document.getElementById("space").value = space;
|
||||
|
||||
/* Check if the ROIs are equidistant. Only if not, untick the checkbox */
|
||||
if (ROIInfo.length > 1) {
|
||||
var distanceROI0_to_ROI1 = parseInt(ROIInfo[1].x) - (parseInt(ROIInfo[0].x) + parseInt(ROIInfo[0].dx)); // Distance between 1st and 2nd ROI
|
||||
//console.log("0->1: " + distanceROI0_to_ROI1);
|
||||
for (var i = 1; i < (ROIInfo.length - 1); ++i) { // 2nd .. 2nd-last ROI
|
||||
//console.log(i + "->" + i+1 + ": " + (parseInt(ROIInfo[i+1].x) - (parseInt(ROIInfo[i].x) + parseInt(ROIInfo[i].dx))));
|
||||
if (distanceROI0_to_ROI1 != (parseInt(ROIInfo[i+1].x) - (parseInt(ROIInfo[i].x) + parseInt(ROIInfo[i].dx)))) {
|
||||
console.log("Not equidistant, unticking the checkbox!");
|
||||
lockSpaceEquidistant = false;
|
||||
document.getElementById("lockSpaceEquidistant").checked = lockSpaceEquidistant;
|
||||
if (!lockSpaceEquidistant) {
|
||||
document.getElementById("space").disabled = true;
|
||||
}
|
||||
else {
|
||||
document.getElementById("space").disabled = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Check if the ROIs have same y, dy and dx. If so, tick the sync checkbox */
|
||||
var all_y_dx_dy_Identical = true;
|
||||
if (ROIInfo.length > 1) {
|
||||
for (var i = 1; i < (ROIInfo.length); ++i) { // 2nd .. last ROI
|
||||
if (parseInt(ROIInfo[i].y) != parseInt(ROIInfo[0].y) ||
|
||||
parseInt(ROIInfo[i].dx) != parseInt(ROIInfo[0].dx) ||
|
||||
parseInt(ROIInfo[i].dy) != parseInt(ROIInfo[0].dy)) {
|
||||
all_y_dx_dy_Identical = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (all_y_dx_dy_Identical) {
|
||||
lockSizes = true;
|
||||
console.log("All ROI have the same Y, dX and dY, ticking the sync checkbox!");
|
||||
document.getElementById("lockSizes").checked = lockSizes;
|
||||
}
|
||||
else {
|
||||
console.log("Not all ROI have the same Y, dX and dY, unticking the sync checkbox!");
|
||||
}
|
||||
|
||||
if (ROIInfo.length > 1) {
|
||||
space = ROIInfo[1].x - parseInt(ROIInfo[0].x) - parseInt(ROIInfo[0].dx);
|
||||
document.getElementById("space").value = space;
|
||||
}
|
||||
|
||||
drawImage();
|
||||
draw();
|
||||
@@ -553,8 +613,19 @@ function draw() {
|
||||
var dy = parseInt(ROIInfo[_nb].dy) + parseInt(lw);
|
||||
context.strokeRect(x0, y0, dx, dy);
|
||||
drawTextBG(context, ROIInfo[_nb]["name"], x0+dx/2, y0-12, 5);
|
||||
}
|
||||
|
||||
lw = 1
|
||||
var x0 = parseInt(ROIInfo[_nb].x) - parseInt(lw/2);
|
||||
var y0 = parseInt(ROIInfo[_nb].y) - parseInt(lw/2);
|
||||
var dx = parseInt(ROIInfo[_nb].dx) + parseInt(lw);
|
||||
var dy = parseInt(ROIInfo[_nb].dy) + parseInt(lw);
|
||||
context.lineWidth = lw;
|
||||
context.beginPath();
|
||||
context.moveTo(x0, y0+dy/2);
|
||||
context.lineTo(x0+dx, y0+dy/2);
|
||||
context.stroke();
|
||||
context.strokeRect(x0+dx*0.2, y0+dy*0.2, dx*0.6, dy*0.6);
|
||||
}
|
||||
}
|
||||
|
||||
lw = 4
|
||||
@@ -630,7 +701,7 @@ function draw() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (lockAR) {
|
||||
if (lockAspectRatio) {
|
||||
rect.h = (e.pageY - zw.top) - rect.startY;
|
||||
rect.w = Math.round(rect.h * ROIInfo[aktindex]["ar"]); }
|
||||
else {
|
||||
@@ -670,7 +741,7 @@ function draw() {
|
||||
rect.w = document.getElementById("refdx").value;
|
||||
rect.h = document.getElementById("refdy").value;
|
||||
|
||||
if (lockAR) {
|
||||
if (lockAspectRatio) {
|
||||
rect.w = Math.round(rect.h * ROIInfo[aktindex]["ar"]);
|
||||
document.getElementById("refdx").value = rect.w;
|
||||
}
|
||||
@@ -695,7 +766,7 @@ function draw() {
|
||||
rect.w = document.getElementById("refdx").value;
|
||||
rect.h = document.getElementById("refdy").value;
|
||||
|
||||
if (lockAR) {
|
||||
if (lockAspectRatio) {
|
||||
rect.h = Math.round(rect.w / ROIInfo[aktindex]["ar"]);
|
||||
document.getElementById("refdy").value = rect.h;
|
||||
}
|
||||
|
||||
@@ -28,12 +28,12 @@ p {font-size: 1em;}
|
||||
|
||||
<p>
|
||||
This is the first time you started the digitizer after the initial installation. You have been automatically routed to the <b>initial setup procedure</b>.
|
||||
Here you adjust the settings for your counter within five steps. In the final step the inital setup will be disabled and it will restart to the normal mode.
|
||||
All settings will also be accessible there.
|
||||
<br>
|
||||
<br>
|
||||
You can navigate forward and backward during the setup with the buttons "Next" and "Previous".<br><br>
|
||||
<span color=red>Do not forget to save in each step!</span><br>
|
||||
Here you adjust the settings for your meter within five steps. In the final step the inital setup will be disabled and it will restart to the normal mode.
|
||||
All settings will also be accessible there. See <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/initial-setup target=_blank>
|
||||
https://jomjol.github.io/AI-on-the-edge-device-docs/initial-setup</a> for additional explanations.</p>
|
||||
</p>
|
||||
<p>You can navigate forward and backward during the setup with the buttons "Next" and "Previous".<br><br>
|
||||
<span color=red>Do not forget to save in each step before heading to another step!</span><br>
|
||||
</p>
|
||||
<p>
|
||||
This is an overview over the five steps:
|
||||
@@ -41,26 +41,27 @@ p {font-size: 1em;}
|
||||
|
||||
<p>
|
||||
<ol>
|
||||
<li>Create reference image <br>
|
||||
Base for the position referencing and the identification of the digits and counters.</li>
|
||||
<li>Define two unique references <br>
|
||||
Used to align the individual camera shot and identify the absolut positions</li>
|
||||
<li>Define the digits <br>
|
||||
Digital digits to be recognized</li>
|
||||
<li>Define the analog counters <br>
|
||||
Analog counters to be identified</li>
|
||||
<li>General settings <br>
|
||||
Most settings can be used with default value at the beginning. MQTT connection can be specified here</li>
|
||||
<li><p>Create the <b>Reference Image</b>.<br>
|
||||
It is the base for the position referencing and the identification of the digits and counters.</p></li>
|
||||
<li><p>Define two unique <b>Reference Marks</b>.<br>
|
||||
They is used to align the individual camera images and identify the absolut positions.</p></li>
|
||||
<li></p>Define <b>ROI's</b> for the <b>Digits</b>.<br>
|
||||
They will be used to digitize the digit part of your meter.<br>
|
||||
If your meter has no digits, this step can be skipped.</p></li>
|
||||
<li>Define <b>ROI's</b>> for the <b>Analog Counters</b>.<br>
|
||||
They will be used to digitize the analog part of your meter.<br>
|
||||
If your meter has no analog counters, this step can be skipped.</p></li>
|
||||
<li><p><b>General Settings</b><br>
|
||||
Further configuration of your device.</p></li>
|
||||
</ol>
|
||||
<p>
|
||||
|
||||
After step 5 setup mode is completed, it then reboots and starts into the normal mode.
|
||||
<br><br>
|
||||
<p>Please be patient when switching to another step. The device takes some time to load all needed information!</p>
|
||||
|
||||
If you need support, have a look to the <a href=https://jomjol.github.io/AI-on-the-edge-device-docs target=_blank>documenation</a> or the <a href=https://github.com/jomjol/AI-on-the-edge-device/discussions target=_blank>discussion</a> pages.<br>
|
||||
<p>After step 5 the setup is completed, you then can reboot and starts into the normal operation mode.</p>
|
||||
|
||||
<h4>Have fun with the digitizer!</h4>
|
||||
<p>If you need support, have a look to the <a href=https://jomjol.github.io/AI-on-the-edge-device-docs target=_blank>documentation</a> or the <a href=https://github.com/jomjol/AI-on-the-edge-device/discussions target=_blank>discussion</a> pages.</p>
|
||||
|
||||
<p><b>Have fun with your digitizer!</b></p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -28,16 +28,11 @@ p {font-size: 1em;}
|
||||
|
||||
<body style="font-family: arial">
|
||||
|
||||
<h4>Finished!</h4>
|
||||
<h4>Step 6: Setup Completed!</h4>
|
||||
|
||||
<p>
|
||||
Now you are finished with the setup and ready to reboot to the normal mode.
|
||||
<br>
|
||||
Once you have pushed below button, the setup modus will be left and the digitizer will restart to normal operation mode.
|
||||
<br>
|
||||
The Web Interface will automatically reload. It will take two to four minutes, until you get the first reading.
|
||||
<br>
|
||||
All settings can be changed as well in the configuration menu in the Normal modus.
|
||||
<p>Congratulations, you completed the setup and are now ready to reboot to the normal mode!</p>
|
||||
<p>Once you have pushed the button below, the setup modus will be left and the digitizer will restart to normal operation mode.<br>
|
||||
The Web Interface will automatically reload. It will take some minutes until you get the first reading.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
|
||||
<title>Make Reference</title>
|
||||
<title>Reference Image</title>
|
||||
<meta charset="utf-8"/>
|
||||
|
||||
<style>
|
||||
@@ -36,8 +36,12 @@ table {
|
||||
</head>
|
||||
|
||||
<body style="font-family: arial; padding: 0px 10px;">
|
||||
<h2>Create Reference out of Raw Image</h2>
|
||||
<p><b>Note: After saving a new Reference Image, make sure to update the Alignment Marks and the ROI's and reboot once!</b></p>
|
||||
<h2>Reference Image</h2>
|
||||
<p>On this page you define the Reference Image.
|
||||
See <a href=https://jomjol.github.io/AI-on-the-edge-device-docs/Reference-Image/ target=_blank>https://jomjol.github.io/AI-on-the-edge-device-docs/Reference-Image/</a> for explanations.</p>
|
||||
<p>After saving a new Reference Image, make sure to update the <a href=edit_alignment.html>Alignment Marks</a> and then
|
||||
the <a href=edit_digits.html>digit</a> resp. <a href=edit_analog.html>analog</a> ROI's.<br>
|
||||
Only after those steps a reboot is required.</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
@@ -49,8 +53,8 @@ table {
|
||||
<td style="padding-top: 10px"><label for="mirror" id="labelmirror">Mirror Image:</label></td>
|
||||
<td style="padding-top: 10px"><input type="checkbox" id="mirror" name="mirror" value="1" onchange="drawRotated()"></td>
|
||||
<td>
|
||||
<class id="MakeImage_LEDIntensity_text" style="color:black;">LEDIntensity: </class>
|
||||
<input type="number" id="MakeImage_LEDIntensity_value1" size="13" value=0 min="0" max="100" style="float: right; clear: both;">
|
||||
<class id="TakeImage_LEDIntensity_text" style="color:black;">LEDIntensity: </class>
|
||||
<input type="number" id="TakeImage_LEDIntensity_value1" size="13" value=0 min="0" max="100" style="float: right; clear: both;">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -58,8 +62,8 @@ table {
|
||||
<td><label for="flip" id="labelflip">Flip Image Size:</label></td>
|
||||
<td><input type="checkbox" id="flip" name="flip" value="1" onchange="drawRotated()"></td>
|
||||
<td>
|
||||
<class id="MakeImage_Brightness_text" style="color:black;">Brightness: </class>
|
||||
<input type="number" id="MakeImage_Brightness_value1" size="13" value=0 min="-2" max="2" style="float: right; clear: both;">
|
||||
<class id="TakeImage_Brightness_text" style="color:black;">Brightness: </class>
|
||||
<input type="number" id="TakeImage_Brightness_value1" size="13" value=0 min="-2" max="2" style="float: right; clear: both;">
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
@@ -67,16 +71,16 @@ table {
|
||||
<td><label for="mirror">Pre-rotate Angle:</label></td>
|
||||
<td><input type="number" id="prerotateangle" name="prerotateangle" value="0" min="-360" max="360" onchange="drawRotated()">Degrees</td>
|
||||
<td>
|
||||
<class id="MakeImage_Contrast_text" style="color:black;">Contrast</class>
|
||||
<input type="number" id="MakeImage_Contrast_value1" size="13" value=0 min="-2" max="2" style="float: right; clear: both;">
|
||||
<class id="TakeImage_Contrast_text" style="color:black;">Contrast</class>
|
||||
<input type="number" id="TakeImage_Contrast_value1" size="13" value=0 min="-2" max="2" style="float: right; clear: both;">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="mirror">Fine Alignment:</label></td>
|
||||
<td><input type="number" id="finerotate" name="finerotate" value=0.0 min="-1" max="1" step="0.2" onchange="drawRotated()">Degrees</td>
|
||||
<td>
|
||||
<class id="MakeImage_Saturation_text" style="color:black;">Saturation</class>
|
||||
<input type="number" id="MakeImage_Saturation_value1" size="13" value=0 min="-2" max="2" style="float: right; clear: both;">
|
||||
<class id="TakeImage_Saturation_text" style="color:black;">Saturation</class>
|
||||
<input type="number" id="TakeImage_Saturation_value1" size="13" value=0 min="-2" max="2" style="float: right; clear: both;">
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
@@ -87,10 +91,8 @@ table {
|
||||
<td><canvas id="canvas"></canvas></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input class="button" type="button" id="updatereferenceimage" value="Save" onclick="SaveReference()"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><button class="button" id="reboot" type="button" onclick="doReboot()">Reboot to activate saved Reference</button></td>
|
||||
<td><input class="button" type="button" id="updatereferenceimage" value="Save" onclick="SaveReference()">
|
||||
<p>Proceed to update the <a href=edit_alignment.html>Alignment Marks</a> when you are done.</p></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -121,15 +123,15 @@ table {
|
||||
|
||||
function doTake(){
|
||||
var xhttp = new XMLHttpRequest();
|
||||
if (param["MakeImage"]["Brightness"].found && param["MakeImage"]["Brightness"].enabled)
|
||||
if (param["TakeImage"]["Brightness"].found && param["TakeImage"]["Brightness"].enabled)
|
||||
{
|
||||
_intensity = document.getElementById("MakeImage_LEDIntensity_value1").value;
|
||||
_intensity = document.getElementById("TakeImage_LEDIntensity_value1").value;
|
||||
if (_intensity == "") _intensity = "50";
|
||||
_brightness = document.getElementById("MakeImage_Brightness_value1").value;
|
||||
_contrast = document.getElementById("MakeImage_Contrast_value1").value;
|
||||
_saturation = document.getElementById("MakeImage_Saturation_value1").value;
|
||||
_brightness = document.getElementById("TakeImage_Brightness_value1").value;
|
||||
_contrast = document.getElementById("TakeImage_Contrast_value1").value;
|
||||
_saturation = document.getElementById("TakeImage_Saturation_value1").value;
|
||||
url = getDomainname() + "/editflow?task=test_take&bri=" + _brightness;
|
||||
url = url + "&con=" + _saturation + "&sat=" + _saturation + "&int=" + _intensity;
|
||||
url = url + "&con=" + _contrast + "&sat=" + _saturation + "&int=" + _intensity;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -166,24 +168,24 @@ table {
|
||||
document.getElementById("labelflip").style = "color:lightgrey;";
|
||||
}
|
||||
|
||||
if (param["MakeImage"]["Brightness"].found && param["MakeImage"]["Brightness"].enabled)
|
||||
if (param["TakeImage"]["Brightness"].found && param["TakeImage"]["Brightness"].enabled)
|
||||
{
|
||||
document.getElementById("MakeImage_Brightness_value1").disabled = false;
|
||||
document.getElementById("MakeImage_Contrast_value1").disabled = false;
|
||||
document.getElementById("MakeImage_Saturation_value1").disabled = false;
|
||||
document.getElementById("MakeImage_LEDIntensity_value1").disabled = false;
|
||||
document.getElementById("TakeImage_Brightness_value1").disabled = false;
|
||||
document.getElementById("TakeImage_Contrast_value1").disabled = false;
|
||||
document.getElementById("TakeImage_Saturation_value1").disabled = false;
|
||||
document.getElementById("TakeImage_LEDIntensity_value1").disabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
document.getElementById("MakeImage_Brightness_value1").type = "hidden";
|
||||
document.getElementById("MakeImage_Brightness_text").style.visibility = "hidden";
|
||||
document.getElementById("TakeImage_Brightness_value1").type = "hidden";
|
||||
document.getElementById("TakeImage_Brightness_text").style.visibility = "hidden";
|
||||
|
||||
}
|
||||
|
||||
// if (param["MakeImage"]["Saturation"].found)
|
||||
// document.getElementById("MakeImage_Saturation_value1").disabled = false;
|
||||
// if (param["MakeImage"]["Contrast"].found)
|
||||
// document.getElementById("MakeImage_Contrast_value1").disabled = false;
|
||||
// if (param["TakeImage"]["Saturation"].found)
|
||||
// document.getElementById("TakeImage_Saturation_value1").disabled = false;
|
||||
// if (param["TakeImage"]["Contrast"].found)
|
||||
// document.getElementById("TakeImage_Contrast_value1").disabled = false;
|
||||
|
||||
isActReference = false;
|
||||
loadCanvas(url);
|
||||
@@ -205,16 +207,16 @@ table {
|
||||
document.getElementById("prerotateangle").disabled = true;
|
||||
document.getElementById("updatereferenceimage").disabled = true;
|
||||
document.getElementById("take").disabled = true;
|
||||
document.getElementById("MakeImage_Brightness_value1").disabled = true;
|
||||
document.getElementById("MakeImage_Saturation_value1").disabled = true;
|
||||
document.getElementById("MakeImage_Contrast_value1").disabled = true;
|
||||
document.getElementById("MakeImage_LEDIntensity_value1").disabled = true;
|
||||
document.getElementById("TakeImage_Brightness_value1").disabled = true;
|
||||
document.getElementById("TakeImage_Saturation_value1").disabled = true;
|
||||
document.getElementById("TakeImage_Contrast_value1").disabled = true;
|
||||
document.getElementById("TakeImage_LEDIntensity_value1").disabled = true;
|
||||
document.getElementById("mirror").disabled = false;
|
||||
document.getElementById("flip").disabled = false;
|
||||
if (!(param["MakeImage"]["Brightness"].found))
|
||||
if (!(param["TakeImage"]["Brightness"].found))
|
||||
{
|
||||
document.getElementById("MakeImage_Brightness_value1").type = "hidden";
|
||||
document.getElementById("MakeImage_Brightness_text").style.visibility = "hidden";
|
||||
document.getElementById("TakeImage_Brightness_value1").type = "hidden";
|
||||
document.getElementById("TakeImage_Brightness_text").style.visibility = "hidden";
|
||||
}
|
||||
|
||||
|
||||
@@ -236,7 +238,7 @@ table {
|
||||
}
|
||||
|
||||
function SaveReference(){
|
||||
if (confirm("Are you sure you want to update the reference image?")) {
|
||||
if (confirm("Are you sure you want to update the Reference Image?")) {
|
||||
param["Alignment"]["InitialRotate"].value1 = document.getElementById("prerotateangle").value;
|
||||
|
||||
if ((param["Alignment"]["InitialMirror"].found == true) && (document.getElementById("mirror").checked))
|
||||
@@ -253,12 +255,12 @@ table {
|
||||
else
|
||||
param["Alignment"]["FlipImageSize"].value1 = "false";
|
||||
|
||||
if (param["MakeImage"]["Brightness"].found && param["MakeImage"]["Brightness"].enabled)
|
||||
if (param["TakeImage"]["Brightness"].found && param["TakeImage"]["Brightness"].enabled)
|
||||
{
|
||||
ReadParameter(param, "MakeImage", "Brightness", false);
|
||||
ReadParameter(param, "MakeImage", "Contrast", false);
|
||||
ReadParameter(param, "MakeImage", "Saturation", false);
|
||||
ReadParameter(param, "MakeImage", "LEDIntensity", false);
|
||||
ReadParameter(param, "TakeImage", "Brightness", false);
|
||||
ReadParameter(param, "TakeImage", "Contrast", false);
|
||||
ReadParameter(param, "TakeImage", "Saturation", false);
|
||||
ReadParameter(param, "TakeImage", "LEDIntensity", false);
|
||||
}
|
||||
|
||||
var canvas = document.getElementById("canvas");
|
||||
@@ -270,7 +272,7 @@ table {
|
||||
SaveCanvasToImage(canvas, "/config/reference.jpg", true, getDomainname());
|
||||
showReference(param);
|
||||
UpdatePage();
|
||||
firework.launch('Reference got updated. Please reboot to activate changes!', 'success', 5000);
|
||||
firework.launch('Reference got updated. It will get applied after the next reboot!', 'success', 5000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,30 +309,30 @@ table {
|
||||
ParseConfig();
|
||||
param = getConfigParameters();
|
||||
|
||||
param["MakeImage"]["LEDIntensity"]["enabled"] = true;
|
||||
param["MakeImage"]["Brightness"]["enabled"] = true;
|
||||
param["MakeImage"]["Contrast"]["enabled"] = true;
|
||||
param["MakeImage"]["Saturation"]["enabled"] = true;
|
||||
param["TakeImage"]["LEDIntensity"]["enabled"] = true;
|
||||
param["TakeImage"]["Brightness"]["enabled"] = true;
|
||||
param["TakeImage"]["Contrast"]["enabled"] = true;
|
||||
param["TakeImage"]["Saturation"]["enabled"] = true;
|
||||
|
||||
if (!param["MakeImage"]["LEDIntensity"]["found"])
|
||||
if (!param["TakeImage"]["LEDIntensity"]["found"])
|
||||
{
|
||||
param["MakeImage"]["LEDIntensity"]["found"] = true;
|
||||
param["MakeImage"]["LEDIntensity"]["value1"] = "50";
|
||||
param["TakeImage"]["LEDIntensity"]["found"] = true;
|
||||
param["TakeImage"]["LEDIntensity"]["value1"] = "50";
|
||||
}
|
||||
if (!param["MakeImage"]["Brightness"]["found"])
|
||||
if (!param["TakeImage"]["Brightness"]["found"])
|
||||
{
|
||||
param["MakeImage"]["Brightness"]["found"] = true;
|
||||
param["MakeImage"]["Brightness"]["value1"] = "0";
|
||||
param["TakeImage"]["Brightness"]["found"] = true;
|
||||
param["TakeImage"]["Brightness"]["value1"] = "0";
|
||||
}
|
||||
if (!param["MakeImage"]["Contrast"]["found"])
|
||||
if (!param["TakeImage"]["Contrast"]["found"])
|
||||
{
|
||||
param["MakeImage"]["Contrast"]["found"] = true;
|
||||
param["MakeImage"]["Contrast"]["value1"] = "0";
|
||||
param["TakeImage"]["Contrast"]["found"] = true;
|
||||
param["TakeImage"]["Contrast"]["value1"] = "0";
|
||||
}
|
||||
if (!param["MakeImage"]["Saturation"]["found"])
|
||||
if (!param["TakeImage"]["Saturation"]["found"])
|
||||
{
|
||||
param["MakeImage"]["Saturation"]["found"] = true;
|
||||
param["MakeImage"]["Saturation"]["value1"] = "0";
|
||||
param["TakeImage"]["Saturation"]["found"] = true;
|
||||
param["TakeImage"]["Saturation"]["value1"] = "0";
|
||||
}
|
||||
|
||||
UpdateInput();
|
||||
@@ -338,10 +340,10 @@ table {
|
||||
}
|
||||
|
||||
function UpdateInput() {
|
||||
WriteParameter(param, category, "MakeImage", "Brightness", false);
|
||||
WriteParameter(param, category, "MakeImage", "Contrast", false);
|
||||
WriteParameter(param, category, "MakeImage", "Saturation", false);
|
||||
WriteParameter(param, category, "MakeImage", "LEDIntensity", false);
|
||||
WriteParameter(param, category, "TakeImage", "Brightness", false);
|
||||
WriteParameter(param, category, "TakeImage", "Contrast", false);
|
||||
WriteParameter(param, category, "TakeImage", "Saturation", false);
|
||||
WriteParameter(param, category, "TakeImage", "LEDIntensity", false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -20,12 +20,14 @@ p {font-size: 1em;}
|
||||
|
||||
<body style="font-family: arial">
|
||||
|
||||
<h4>Reference Image</h4>
|
||||
<h4>Step 1: Define a Reference Image</h4>
|
||||
The reference image is the base to define the digits, counters and references positions.
|
||||
<p>
|
||||
Firstly you see the default image. Use the Button "Create New Reference" to start to create your own reference image.<br>
|
||||
Most important feature is a straight alignment of the image. Use the Pre-rotate angle and the fine alignment to adjust the rotation of the image<br>
|
||||
Store the reference image by pushing <b>"Update Reference Image"</b>. A reboot is not yet required.
|
||||
Most important feature is a straight alignment of the image. Use the Pre-rotate angle and the fine alignment to adjust the rotation of the image.
|
||||
</p>
|
||||
<p>
|
||||
Don't forget to save your changes with the <b>"Save"</b> button!
|
||||
</p>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -19,15 +19,15 @@ p {font-size: 1em;}
|
||||
|
||||
<body style="font-family: arial">
|
||||
|
||||
<h4>Alignment References</h4>
|
||||
Two opposite alignment references are needed to identify unique fix points on the image.
|
||||
<h4>Step 2: Define two Alignment Marks</h4>
|
||||
Two opposite alignment marks are needed to identify unique fix points on the image.
|
||||
<p>
|
||||
Mark the reference by drag and dop with the mouse or with the coordinates and push <b>"Update Reference"</b>.
|
||||
<br>
|
||||
You can switch between the two reference with <b>"Select Reference"</b>.
|
||||
</p>
|
||||
<p>
|
||||
Don't forget to save your changes! A reboot is not yet required.
|
||||
Don't forget to save your changes with the <b>"Save"</b> button!
|
||||
</p>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -20,7 +20,7 @@ p {font-size: 1em;}
|
||||
|
||||
<body style="font-family: arial">
|
||||
|
||||
<h4>Define Digits</h4>
|
||||
<h4>Step 3: Define ROI's for your Digits</h4>
|
||||
Here you define your digits you want to read. If you have more than one number on the reading you can define several numbers with the <b>"Number"</b> selector. There you can also define new numbers.
|
||||
<p>
|
||||
With the drop down menue <b>"ROI x"</b> you can change between the different digits. Mark them with the mouse or the coordinates.
|
||||
@@ -29,7 +29,7 @@ Here you define your digits you want to read. If you have more than one number o
|
||||
You can change it with <b>"move Next" / "move Previous"</b>.
|
||||
</p>
|
||||
<p>
|
||||
Don't forget to save your changes! A reboot is not yet required.
|
||||
Don't forget to save your changes with the <b>"Save"</b> button!
|
||||
</p>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -19,7 +19,7 @@ p {font-size: 1em;}
|
||||
|
||||
<body style="font-family: arial">
|
||||
|
||||
<h4>Define Digits</h4>
|
||||
<h4>Step 4: Define ROI's for your Analog Counters</h4>
|
||||
Here you define your analog counters you want to read. If you have more than one number on the reading you can define several numbers with the <b>"Number"</b> selector. There you can also define new numbers. If you do not have analog counters delete all ROIs.
|
||||
<p>
|
||||
With the drop down menue <b>"ROI x"</b> you can change between the different counters. Mark them with the mouse or the coordinates.
|
||||
@@ -28,7 +28,7 @@ Here you define your analog counters you want to read. If you have more than one
|
||||
You can change it with <b>"move Next" / "move Previous"</b>.
|
||||
</p>
|
||||
<p>
|
||||
Don't forget to save your changes with <b>"Save all to Config.ini"</b>! A reboot is not yet required.
|
||||
Don't forget to save your changes with the <b>"Save"</b> button!
|
||||
</p>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -19,12 +19,12 @@ p {font-size: 1em;}
|
||||
|
||||
<body style="font-family: arial">
|
||||
|
||||
<h4>General configuration parameters</h4>
|
||||
<p>Here you can define additional settings. The settings should fit for a normal setup.</p>
|
||||
<p>You will also be availabl</p>
|
||||
<h4>Step 5: General Configuration Settings</h4>
|
||||
<p>Here you can define additional settings. The default settings should fit for a normal/initial setup.</p>
|
||||
<p>You will also be able to change them later, so don't worry if you do not understand the parameters yet!</p>
|
||||
</p>
|
||||
<p>
|
||||
Don't forget to save your changes!
|
||||
<br><span color=red>You should not reboot here, but leave the setup modus on the next page!</span>
|
||||
Don't forget to save your changes with the <b>"Save"</b> button!
|
||||
</p>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -1,26 +1 @@
|
||||
<!DOCTYPE html>
|
||||
<html style="width: fit-content">
|
||||
<head>
|
||||
<link rel="icon" href="favicon.ico?v=$COMMIT_HASH" type="image/x-icon">
|
||||
<title>AI on the edge</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<style>
|
||||
|
||||
h1 {font-size: 2em; margin-block-end: 0.3em;}
|
||||
h2 {font-size: 1.5em;margin-block-start: 0.3em;}
|
||||
h3 {font-size: 1.2em;}
|
||||
p {font-size: 1em;}
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body style="font-family: arial">
|
||||
|
||||
<h4>Finished!</h4>
|
||||
Read below!
|
||||
</body>
|
||||
</html>
|
||||
<!-- This page is never shown -->
|
||||
@@ -13,7 +13,7 @@ function getbasepath(){
|
||||
{
|
||||
// host = "http://192.168.2.219"; // jomjol interner test
|
||||
// host = "http://192.168.178.46"; // jomjol interner test
|
||||
host = "http://192.168.178.62"; // jomjol interner Real
|
||||
host = "http://192.168.178.44"; // jomjol interner Real
|
||||
// host = "http://192.168.43.191";
|
||||
// host = "."; // jomjol interner localhost
|
||||
|
||||
|
||||
1
sd-card/html/github.min.css
vendored
Normal file
1
sd-card/html/github.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8f8}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-literal,.hljs-number,.hljs-tag .hljs-attr,.hljs-template-variable,.hljs-variable{color:teal}.hljs-doctag,.hljs-string{color:#d14}.hljs-section,.hljs-selector-id,.hljs-title{color:#900;font-weight:700}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-type{color:#458;font-weight:700}.hljs-attribute,.hljs-name,.hljs-tag{color:navy;font-weight:400}.hljs-link,.hljs-regexp{color:#009926}.hljs-bullet,.hljs-symbol{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999;font-weight:700}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -1,21 +1,138 @@
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript" src='plotly-2.14.0.min.js?v=$COMMIT_HASH'></script>
|
||||
<script type="text/javascript" src='plotly-basic-2.18.2.min.js?v=$COMMIT_HASH'></script>
|
||||
|
||||
<script type="text/javascript" src="common.js?v=$COMMIT_HASH"></script>
|
||||
<script type="text/javascript" src="readconfigcommon.js?v=$COMMIT_HASH"></script>
|
||||
<script type="text/javascript" src="readconfigparam.js?v=$COMMIT_HASH"></script>
|
||||
|
||||
<style>
|
||||
textarea {
|
||||
width: 600px;
|
||||
height: 300px;
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function run() {
|
||||
var el = document.getElementById('cnsl');
|
||||
el && eval(el.value);
|
||||
datefile = document.getElementById("datafiles").value;
|
||||
numbername = document.getElementById("numbers").value;
|
||||
showRrelativeValues = document.getElementById("showRrelativeValues").checked;
|
||||
//alert("Auslesen: " + datefile + " " + numbername);
|
||||
|
||||
_domainname = getDomainname();
|
||||
fetch(_domainname + '/fileserver/log/data/' + datefile)
|
||||
.then(response => {
|
||||
// handle the response
|
||||
if (response.status == 404) {
|
||||
firework.launch("No log data available for " + dateString, 'warning', 10000);
|
||||
}
|
||||
response.text()
|
||||
.then( result => {
|
||||
var lines = result.split("\n");
|
||||
var traceValue = { x: [], y: [], type: 'scatter', line: {width: 6}, name: 'Value'};
|
||||
var tracePreValue = { x: [], y: [], type: 'scatter', line: {width: 2}, name: 'Previous Value', visible: 'legendonly'};
|
||||
var traceChangeRate = { x: [], y: [], type: 'bar', yaxis: 'y2', opacity: 0.2, name: 'Change Rate'};
|
||||
var traceChangeAbsolute = { x: [], y: [], type: 'bar', yaxis: 'y2', opacity: 0.2, name: 'Change Absolute', visible: 'legendonly'};
|
||||
|
||||
var timex = 1;
|
||||
for (let line of lines) {
|
||||
{
|
||||
//console.log(line);
|
||||
if (line.split(",")[1] == numbername)
|
||||
{
|
||||
var value = line.split(",")[3];
|
||||
var preValue = line.split(",")[4];
|
||||
var changeRate = line.split(",")[5];
|
||||
var changeAbsolute = line.split(",")[6];
|
||||
var time = line.split(",")[0];
|
||||
//console.log("> "+time+" "+value+"\n");
|
||||
|
||||
traceValue.x.push(time);
|
||||
|
||||
/* Catch empty fields */
|
||||
if (value == "" || isNaN(value)) {
|
||||
value = NaN;
|
||||
}
|
||||
|
||||
if (preValue == "" || isNaN(preValue)) {
|
||||
preValue = NaN;
|
||||
}
|
||||
|
||||
if (changeRate == "" || isNaN(changeRate)) {
|
||||
changeRate = NaN;
|
||||
}
|
||||
|
||||
if (changeAbsolute == "" || isNaN(changeAbsolute)) {
|
||||
changeAbsolute = NaN;
|
||||
}
|
||||
|
||||
traceValue.y.push(value);
|
||||
tracePreValue.y.push(preValue);
|
||||
traceChangeRate.y.push(changeRate);
|
||||
traceChangeAbsolute.y.push(changeAbsolute);
|
||||
}
|
||||
}
|
||||
}
|
||||
//console.log(trace);
|
||||
|
||||
// Copy time to all traces
|
||||
tracePreValue.x = traceValue.x;
|
||||
traceChangeRate.x = traceValue.x;
|
||||
traceChangeAbsolute.x = traceValue.x;
|
||||
|
||||
//console.log(traceValue.y);
|
||||
|
||||
var offsetValue = traceValue.y[0];
|
||||
var offsetPreValue = tracePreValue.y[0];
|
||||
|
||||
traceValue.connectgaps = true;
|
||||
|
||||
if (showRrelativeValues) {
|
||||
traceValue.y.forEach(function(part, index, arr) {
|
||||
arr[index] = arr[index] - offsetValue;
|
||||
});
|
||||
|
||||
tracePreValue.y.forEach(function(part, index, arr) {
|
||||
arr[index] = arr[index] - offsetPreValue;
|
||||
});
|
||||
}
|
||||
|
||||
// console.log(traceValue.x)
|
||||
|
||||
var data = [traceValue, tracePreValue, traceChangeRate, traceChangeAbsolute];
|
||||
|
||||
var layout = {
|
||||
showlegend: true,
|
||||
colorway: ['green', 'black', 'blue', 'black'],
|
||||
|
||||
yaxis: {title: 'Value'},
|
||||
yaxis2: {
|
||||
title: 'Change',
|
||||
overlaying: 'y',
|
||||
side: 'right'
|
||||
},
|
||||
|
||||
margin: {
|
||||
l: 50,
|
||||
r: 50,
|
||||
b: 50,
|
||||
t: 50,
|
||||
pad: 4
|
||||
},
|
||||
|
||||
legend: {
|
||||
x: 0.2,
|
||||
y: 0.9,
|
||||
xanchor: 'right'
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById("chart").innerHTML = "";
|
||||
Plotly.newPlot('chart', data, layout, {displayModeBar: true});
|
||||
});
|
||||
}).catch((error) => {
|
||||
// handle the error
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<link href="firework.css?v=$COMMIT_HASH" rel="stylesheet">
|
||||
@@ -23,67 +140,16 @@
|
||||
<script type="text/javascript" src="firework.js?v=$COMMIT_HASH"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id='chart'></div>
|
||||
<h3>Data Graph</h3>
|
||||
<div id='chart'><p>Loading...<br></p></div>
|
||||
<select id="datafiles" onchange="run();"></select>
|
||||
<select id="numbers" onchange="run();"></select>
|
||||
<select id="datatype" onchange="run();">
|
||||
<option value="3">Value</option>
|
||||
<option value="4">PreValue</option>
|
||||
<option value="5">Change-Rate</option>
|
||||
<option value="6">Change-Absolut</option>
|
||||
</select>
|
||||
|
||||
<input type="checkbox" id="showRrelativeValues" onclick = 'run();' unchecked ><label for="showRrelativeValues">Show relative values</label>
|
||||
<button onclick="run();">Refresh</button>
|
||||
|
|
||||
<button onClick="window.location.href = 'data.html?v=$COMMIT_HASH'">Show data</button>
|
||||
<button onClick="window.location.href = getDomainname() + '/fileserver/log/data/'">Show data files</button>
|
||||
|
||||
<!-- <button onclick="document.getElementById('editor').hidden = false; this.hidden = true;" >Editor</button> -->
|
||||
<div id='editor' hidden='true'>
|
||||
<textarea id="cnsl">
|
||||
datefile = document.getElementById("datafiles").value;
|
||||
numbername = document.getElementById("numbers").value;
|
||||
datatype = document.getElementById("datatype").value;
|
||||
//alert("Auslesen: " + datefile + " " + numbername);
|
||||
|
||||
_domainname = getDomainname();
|
||||
fetch(_domainname + '/fileserver/log/data/' + datefile)
|
||||
.then(response => {
|
||||
// handle the response
|
||||
if (response.status == 404) {
|
||||
firework.launch("No log data available for " + dateString, 'warning', 10000);
|
||||
}
|
||||
response.text()
|
||||
.then( result => {
|
||||
var lines = result.split("\n");
|
||||
var trace = {
|
||||
x: [],
|
||||
y: [],
|
||||
type: 'scatter'
|
||||
};
|
||||
|
||||
var timex = 1;
|
||||
for (let line of lines) {
|
||||
{
|
||||
console.log(line);
|
||||
if (line.split(",")[1] == numbername)
|
||||
{
|
||||
var value = line.split(",")[datatype];
|
||||
var time = line.split(",")[0];
|
||||
console.log("> "+time+" "+value+"\n");
|
||||
trace.x.push(time);
|
||||
// timex += 1;
|
||||
trace.y.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(trace);
|
||||
var data = [trace];
|
||||
Plotly.newPlot('chart', data);
|
||||
});
|
||||
}).catch((error) => {
|
||||
// handle the error
|
||||
console.log(error);
|
||||
});
|
||||
</textarea><br />
|
||||
<button onclick="run();">run</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user