mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2025-12-07 20:16:55 +03:00
Compare commits
138 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
884dd9fc3b | ||
|
|
04c564936a | ||
|
|
957138960a | ||
|
|
58a0297915 | ||
|
|
61bf536207 | ||
|
|
4136a7b372 | ||
|
|
b3afc9961d | ||
|
|
27e2e042da | ||
|
|
cc659bad30 | ||
|
|
776931c0ad | ||
|
|
e22b4b6fe1 | ||
|
|
cad534bffe | ||
|
|
3b44adb6fa | ||
|
|
cc0bd1473f | ||
|
|
58124d27bf | ||
|
|
9ad118814a | ||
|
|
270f8dd093 | ||
|
|
fde0ae4781 | ||
|
|
b87ce8c75d | ||
|
|
e372c87836 | ||
|
|
f8478d7742 | ||
|
|
a3d6923fec | ||
|
|
7bfa22187d | ||
|
|
7a8affae32 | ||
|
|
e48dd7c4c4 | ||
|
|
6d298c1746 | ||
|
|
4fe9ab92e0 | ||
|
|
f2ac4cdc64 | ||
|
|
be902401d1 | ||
|
|
020e93bca2 | ||
|
|
61dfdc2258 | ||
|
|
a98e3c8eab | ||
|
|
58a90ff706 | ||
|
|
d0bf12f3d4 | ||
|
|
af16785bbf | ||
|
|
18f6e83a2c | ||
|
|
147d97421b | ||
|
|
dcf2feb7aa | ||
|
|
e63e940b96 | ||
|
|
68b0fb83ee | ||
|
|
f15e5f060a | ||
|
|
e2a403441f | ||
|
|
9b3665b9c6 | ||
|
|
f4c8bf9206 | ||
|
|
c033db9c31 | ||
|
|
9300526f49 | ||
|
|
b6dd1f7f2d | ||
|
|
1e6eddca04 | ||
|
|
19ca0d7dd7 | ||
|
|
7fcb5d1c0c | ||
|
|
dd995ec28a | ||
|
|
af99de3535 | ||
|
|
3567cc2fb0 | ||
|
|
5e9d9bd264 | ||
|
|
62447c1bb9 | ||
|
|
a86434c9a2 | ||
|
|
b7b70299f7 | ||
|
|
eb02e0aec1 | ||
|
|
7816e53db7 | ||
|
|
7ae08e572a | ||
|
|
47d15d8adb | ||
|
|
0dac0e87e4 | ||
|
|
b290099d5b | ||
|
|
f6b1a41a0b | ||
|
|
e529af04cf | ||
|
|
6c365dd949 | ||
|
|
32f15fc557 | ||
|
|
6f06af1d5f | ||
|
|
a91f99faab | ||
|
|
17a87b23a1 | ||
|
|
d4b5ec2ae2 | ||
|
|
1bcaf09855 | ||
|
|
fa3842b2b4 | ||
|
|
ea72256e56 | ||
|
|
be5828cb3e | ||
|
|
104b72505c | ||
|
|
23728a0686 | ||
|
|
eaaa856b13 | ||
|
|
01e81d02b5 | ||
|
|
9ae8d0a512 | ||
|
|
da16322fb8 | ||
|
|
a6d39afc26 | ||
|
|
1b6a124f54 | ||
|
|
8aff6bf8f3 | ||
|
|
21115752aa | ||
|
|
025c2b88b9 | ||
|
|
655f9d7c97 | ||
|
|
03b5e36114 | ||
|
|
9c78c1e9ca | ||
|
|
136186a526 | ||
|
|
1f6b02a671 | ||
|
|
76a0518d52 | ||
|
|
a688a69af6 | ||
|
|
b890ffc5f3 | ||
|
|
b98558107e | ||
|
|
3e360ad0fb | ||
|
|
a7ced407f8 | ||
|
|
45a1e137d1 | ||
|
|
a44e0d81cc | ||
|
|
749fc6699c | ||
|
|
d7bb147a23 | ||
|
|
08b0b254f2 | ||
|
|
5414a4c3f1 | ||
|
|
7944ab329d | ||
|
|
8ca14a434c | ||
|
|
e24ba68fec | ||
|
|
b205326782 | ||
|
|
daa1960dff | ||
|
|
894c7f6972 | ||
|
|
737dcc76b8 | ||
|
|
b42f17916b | ||
|
|
2c6ce6fd07 | ||
|
|
f243f4b8ea | ||
|
|
02e881ebc0 | ||
|
|
7b8f10a14e | ||
|
|
d995c31b7b | ||
|
|
45154cb55c | ||
|
|
48067b10cd | ||
|
|
f24c40d780 | ||
|
|
f4edd36744 | ||
|
|
a202a6abdc | ||
|
|
c25adfe28a | ||
|
|
822c6cc45c | ||
|
|
c48b44d06a | ||
|
|
21a59fbd35 | ||
|
|
cdcf940d12 | ||
|
|
6cefc44fb6 | ||
|
|
8308f159ad | ||
|
|
e5ff8f2164 | ||
|
|
a000252c8a | ||
|
|
9a42c580cf | ||
|
|
6e0a7a742e | ||
|
|
026bac121f | ||
|
|
8a26b817f7 | ||
|
|
528a4435a9 | ||
|
|
9b791bb7a7 | ||
|
|
58eb0b1292 | ||
|
|
39eda4a4be |
93
Changelog.md
93
Changelog.md
@@ -2,6 +2,99 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##### 8.5.0 - Multi Meter Support (2021-10-07)
|
||||||
|
|
||||||
|
* Upgrade digital CNN to v13.1.0 (added new images)
|
||||||
|
* bug fix: wlan password with space, double digit output
|
||||||
|
|
||||||
|
##### 8.4.0 - Multi Meter Support (2021-09-25)
|
||||||
|
|
||||||
|
* License change (remove MIT license, remark see below)
|
||||||
|
|
||||||
|
* html: show hostname in title and main page
|
||||||
|
|
||||||
|
* configuration:
|
||||||
|
|
||||||
|
* moved setting `ExtendedResolution` to individual number settings
|
||||||
|
* New parameter `IgnoreLeadingNaN` (delete leading NaN's specifically)
|
||||||
|
* **ATTENTION**: update of the `config.ini` needed (open, adjust `ExtendedResolution`, save)
|
||||||
|
|
||||||
|
* Bug fixing (html, images of recognized numbers)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### **ATTENTION: LICENSE CHANGE - removal of MIT License.**
|
||||||
|
|
||||||
|
- Currently no licence published - copyright belongs to author
|
||||||
|
- If you are interested in a commercial usage or dedicated versions please contact the developer
|
||||||
|
- no limits to private usage
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##### 8.3.0 - Multi Meter Support (2021-09-12)
|
||||||
|
|
||||||
|
* Upgrade digital CNN to v12.1.0 (added new images)
|
||||||
|
* Dedicated NaN handling, internal refactoring (CNN-Handling)
|
||||||
|
* HTML: confirmation after config.ini update
|
||||||
|
* Bug fixing
|
||||||
|
|
||||||
|
##### 8.2.0 - Multi Meter Support (2021-08-24)
|
||||||
|
|
||||||
|
* Improve server responsiveness
|
||||||
|
* Flow status and prevalue status in overview
|
||||||
|
* Improved prevalue handling
|
||||||
|
|
||||||
|
##### 8.1.0 - Multi Meter Support (2021-08-12)
|
||||||
|
|
||||||
|
* GPIO: using the general mqtt main topic for GPIO
|
||||||
|
|
||||||
|
* Upgrade digital CNN to v12.0.0 (added new images)
|
||||||
|
* Update tfmicro to new master (2021-08-07)
|
||||||
|
* Bug fix: remove text in mqtt value, remove connect limit in wlan reconnet
|
||||||
|
|
||||||
|
##### 8.0.5 - Multi Meter Support (2021-08-01)
|
||||||
|
|
||||||
|
* NEW 8.0.5: bug fix: saving prevalue
|
||||||
|
* NEW 8.0.4: bug fix: load config.ini after upgrade
|
||||||
|
* NEW 8.0.3: bug fix: reboot during `config.ini` handling, html error
|
||||||
|
* NEW 8.0.2: saving roundes prevalue, bug fix html server
|
||||||
|
* NEW 8.0.1: bug fix: html handling of parameter `FixedExposure` and `ImageSize`
|
||||||
|
* Dual / multi meter support (more than 1 number to be recognized)
|
||||||
|
This is implemented with the feature "number" on the ROI definition as well as selected options
|
||||||
|
* MQTT: standardization of the naming - including new topics (`json`, `freeMem `, `uptime`)c
|
||||||
|
* Preparation for extended GPIO support (thanks to Zwerk2k) - not tested and fully functional yet
|
||||||
|
* Bug fixing: html server, memory leak, MQTT connect, hostname, turn of flash LED
|
||||||
|
|
||||||
|
<span style="color: red;">**ATTENTION: the configuration and prevalue files are modified automatically and will not be backward compatible!**</span>
|
||||||
|
|
||||||
|
##### 7.1.2 MQTT-Update - (2021-06-17)
|
||||||
|
|
||||||
|
* NEW: 7.1.2: bug fix setting hostname, Flash-LED not off during reboot
|
||||||
|
|
||||||
|
* NEW: 7.1.1: bug fix wlan password with "=" (again)
|
||||||
|
|
||||||
|
* MQTT error message: changes "no error", send retain flag
|
||||||
|
|
||||||
|
* Update wlan handling to esp-idf 4.1
|
||||||
|
|
||||||
|
* Upgrade digital CNN to v8.7.0 (added new images)
|
||||||
|
|
||||||
|
* Bug fix: MQTT, WLAN, LED-Controll, GPIO usage, fixed IP, calculation flow rate
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##### 7.0.1 MQTT-Update - (2021-05-13)
|
||||||
|
|
||||||
|
* NEW: 7.0.1: bug fix wlan password with "="
|
||||||
|
|
||||||
|
* Upgrade digital CNN to v8.5.0 (added new images)
|
||||||
|
|
||||||
|
* New MQTT topics: flow rate (units/minute), time stamp (last correct read readout)
|
||||||
|
|
||||||
|
* Update MQTT/Error topic to " " in case no error (instead of empty string)
|
||||||
|
|
||||||
|
* Portrait or landscape image orientation in rotated image (avoid cropping)
|
||||||
|
|
||||||
##### 6.7.2 Image Processing in Memory - (2021-05-01)
|
##### 6.7.2 Image Processing in Memory - (2021-05-01)
|
||||||
|
|
||||||
* NEW 6.7.2: Updated html for setup modus - remove reboot on edit configuration)
|
* NEW 6.7.2: Updated html for setup modus - remove reboot on edit configuration)
|
||||||
|
|||||||
@@ -11,13 +11,71 @@
|
|||||||
|
|
||||||
____
|
____
|
||||||
|
|
||||||
#### #6 Check for double ROI names
|
#### #14 Backup and restore option for configuration
|
||||||
|
|
||||||
Check during configuration, that ROI names are unique.
|
* https://github.com/jomjol/AI-on-the-edge-device/issues/459
|
||||||
|
|
||||||
|
* Implement a zip file compression for store and restore
|
||||||
|
|
||||||
|
* Update the html to handle it
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### #13 Manage non linear gauge without CNN re-training
|
||||||
|
|
||||||
|
* https://github.com/jomjol/AI-on-the-edge-device/issues/443
|
||||||
|
|
||||||
|
* Implement a look up table for non linear analog meters
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### #12 Less reboots due to memory leakage
|
||||||
|
|
||||||
|
* Issue: #414 & #425 #430
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### #11 MQTT - configurable payload
|
||||||
|
|
||||||
|
* https://github.com/jomjol/AI-on-the-edge-device/issues/344
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### #10 Improve and bug fix logging of images
|
||||||
|
|
||||||
|
* https://github.com/jomjol/AI-on-the-edge-device/issues/307
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### #9 Basic auth for the UI
|
||||||
|
|
||||||
|
* https://github.com/jomjol/AI-on-the-edge-device/issues/283
|
||||||
|
|
||||||
|
* Implementation of an authentication mechanism.
|
||||||
|
|
||||||
|
#### #8 MQTT configurable readout intervall
|
||||||
|
|
||||||
|
Make the readout intervall configurable via MQTT.
|
||||||
|
|
||||||
|
* Change the mqtt part to receive and process input and not only sending
|
||||||
|
|
||||||
|
#### #7 Extended Error Handling
|
||||||
|
|
||||||
|
Check different types of error (e.g. tflite not availabe) and generate an error on the html page.
|
||||||
|
|
||||||
To do:
|
To do:
|
||||||
|
|
||||||
* Implementation of ROI name checking in html code before saving analog or digital ROIs
|
* Make a list of "important" errors
|
||||||
|
* Implement a checking algo
|
||||||
|
* Extend the firmware and html page for the error handling
|
||||||
|
|
||||||
|
#### ~~#6 Check for double ROI names~~ - implemented v8.0.0
|
||||||
|
|
||||||
|
~~Check during configuration, that ROI names are unique.~~
|
||||||
|
|
||||||
|
~~To do:~~
|
||||||
|
|
||||||
|
* ~~Implementation of ROI name checking in html code before saving analog or digital ROIs~~
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -33,31 +91,31 @@ To do:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### #4 Initial Shifting and Rotation
|
#### ~~#4 Initial Shifting and Rotation~~ - implemented v7.0.0
|
||||||
|
|
||||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/123
|
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/123~~
|
||||||
|
|
||||||
Implementation of a shifting additional to the initial rotation of the raw camera input
|
~~Implementation of a shifting additional to the initial rotation of the raw camera input~~
|
||||||
|
|
||||||
To do:
|
~~To do:~~
|
||||||
|
|
||||||
* Implementation of shifting
|
* ~~Implementation of shifting~~
|
||||||
* Extension of configuration
|
* ~~Extension of configuration~~
|
||||||
* Adaption of the html configuration to implement shifting
|
* ~~Adaption of the html configuration to implement shifting~~
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### #3 Allow grouping of digits to multiple reading values
|
#### ~~#3 Allow grouping of digits to multiple reading values~~ - implemented v8.0.0
|
||||||
|
|
||||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/123
|
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/123~~
|
||||||
|
|
||||||
Implementation of two different independent readouts in one setup
|
~~Implementation of two different independent readouts in one setup~~
|
||||||
|
|
||||||
To do:
|
~~To do:~~
|
||||||
|
|
||||||
|
* ~~Extend the configuration, setting and processing flow for two independend readouts~~
|
||||||
|
|
||||||
* Extend the configuration, setting and processing flow for two independend readouts
|
|
||||||
|
|
||||||
https://github.com/jomjol/AI-on-the-edge-device/issues/123
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -80,15 +138,16 @@ To do:
|
|||||||
|
|
||||||
____
|
____
|
||||||
|
|
||||||
#### #1 Optional GPIO for external flash/lighting
|
#### ~~#1 Optional GPIO for external flash/lighting~~ - implemented (v8.0.0)
|
||||||
|
|
||||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/133
|
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/133~~
|
||||||
|
|
||||||
Implementation of an an extrnal flash / lightning through GPIOs.
|
~~Implementation of an an extrnal flash / lightning through GPIOs.~~
|
||||||
* available GPIOs: 12 & 13 (currently in use for html switching)
|
|
||||||
|
|
||||||
To do:
|
* ~~available GPIOs: 12 & 13 (currently in use for html switching)~~
|
||||||
|
|
||||||
* Implementation of a software module for external light source (e.g. WS8132 LED controller, ...)
|
~~To do:~~
|
||||||
* Update of the camera module to use the external light instead of the internal flash light
|
|
||||||
* Adopt the configuration algorithm with a configurable light source
|
* ~~Implementation of a software module for external light source (e.g. WS8132 LED controller, ...)~~
|
||||||
|
* ~~Update of the camera module to use the external light instead of the internal flash light~~
|
||||||
|
* ~~Adopt the configuration algorithm with a configurable light source~~
|
||||||
21
LICENSE
21
LICENSE
@@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2020 jomjol
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
67
README.md
67
README.md
@@ -6,12 +6,16 @@ This is an example of Artificial Intelligence (AI) calculations on a very cheap
|
|||||||
|
|
||||||
A 3d-printable housing can be found here: https://www.thingiverse.com/thing:4573481
|
A 3d-printable housing can be found here: https://www.thingiverse.com/thing:4573481
|
||||||
|
|
||||||
|
or here https://www.thingiverse.com/thing:5028229
|
||||||
|
|
||||||
respectively ESP32-Cam housing only: https://www.thingiverse.com/thing:4571627
|
respectively ESP32-Cam housing only: https://www.thingiverse.com/thing:4571627
|
||||||
|
|
||||||
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/watermeter_all.jpg" width="200"><img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/main.jpg" width="200"><img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/size.png" width="200">
|
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/watermeter_all.jpg" width="200"><img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/main.jpg" width="200"><img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/size.png" width="200">
|
||||||
|
|
||||||
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/watermeter.jpg" width="600">
|
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/watermeter.jpg" width="600">
|
||||||
|
|
||||||
|
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/powermeter.jpg" width="600">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -30,35 +34,65 @@ If you have any technical topics, you can file a issue in this repository.
|
|||||||
|
|
||||||
In other cases you can contact the developer via email: <img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/mail.jpg" height="25">
|
In other cases you can contact the developer via email: <img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/mail.jpg" height="25">
|
||||||
|
|
||||||
## Change log
|
------
|
||||||
|
## Coming next
|
||||||
|
|
||||||
|
* Automated update of the neural network file (tflite) to make the learing of additional pictures much easier and automated (GitHub action)
|
||||||
|
* New "hyprid" neural network for digital numbers --> allowing the detection of intermediate states ("ring between two numbers") as a subdigit
|
||||||
|
|
||||||
|
|
||||||
------
|
------
|
||||||
|
## Change log
|
||||||
### Known Issues
|
### Known Issues
|
||||||
|
|
||||||
* slow response of web server during picture analysis
|
* slow response of web server during picture analysis
|
||||||
* spontaneous reboots (mostly due to html access during image processing) - self recovery implemented
|
* spontaneous reboots (mostly due to html access during image processing) - self recovery implemented
|
||||||
|
|
||||||
------
|
|
||||||
|
|
||||||
**General remark:** Beside the `firmware.bin`, typically also the content of `/html` needs to be updated!
|
**General remark:** Beside the `firmware.bin`, typically also the content of `/html` needs to be updated!
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
##### 10.0.0 - Stability Increase (2021-12-30)
|
||||||
|
|
||||||
##### 7.0.1 MQTT-Update - (2021-05-13)
|
- Updated compiler toolchain to ESP-IDF 4.3
|
||||||
|
- Removal of memory leak
|
||||||
|
- Improved error handling during startup (check PSRAM and camera with remark in logfile)
|
||||||
|
- MQTT: implemented raw value additionally, removal of regex contrain
|
||||||
|
|
||||||
* NEW: 7.0.1: bug fix wlan password with "="
|
- Normalized Parameter ``MaxRateValue`` to "change per minute"
|
||||||
|
|
||||||
* Upgrade digital CNN to v8.5.0 (added new images)
|
- HTML: improved input handling
|
||||||
|
|
||||||
* New MQTT topics: flow rate (units/minute), time stamp (last correct read readout)
|
- Corrected error handling: in case of error the old value, rate, timestamp are not transmitted any more
|
||||||
|
|
||||||
* Update MQTT/Error topic to " " in case no error (instead of empty string)
|
|
||||||
|
|
||||||
* Portrait or landscape image orientation in rotated image (avoid cropping)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##### 9.2.0 - External Illumination (2021-12-02)
|
||||||
|
|
||||||
|
- Direct JSON access: ``http://IP-ADRESS/json``
|
||||||
|
- Error message in log file in case camera error during startup
|
||||||
|
- Upgrade analog CNN to v9.1.0
|
||||||
|
- Upgrade digital CNN to v13.3.0 (added new images)
|
||||||
|
- html: support of different ports
|
||||||
|
|
||||||
|
##### 9.1.1 - External Illumination (2021-11-16)
|
||||||
|
|
||||||
|
- NEW 9.1.1 bug fix: LED implemenetation
|
||||||
|
- External LEDs: change control mode (resolve bug with more than 2 LEDs)
|
||||||
|
- Additional info into log file
|
||||||
|
- Bug fix: decimal shift, html, log file
|
||||||
|
|
||||||
|
##### 9.0.0 - External Illumination (2021-10-23)
|
||||||
|
|
||||||
|
* Implementation of external illumination to adjust positioning, brightness and color of the illumination now individually
|
||||||
|
* Technical details can be found in the wiki: https://github.com/jomjol/AI-on-the-edge-device/wiki/External-LED
|
||||||
|
<img src="https://raw.githubusercontent.com/jomjol/ai-on-the-edge-device/master/images/intern_vs_external.jpg" width="500">
|
||||||
|
* New housing published for external LEDs and small clearing: https://www.thingiverse.com/thing:5028229
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Additional ideas
|
## Additional ideas
|
||||||
|
|
||||||
@@ -70,6 +104,10 @@ There are some ideas and feature request, which are not followed currently - mai
|
|||||||
|
|
||||||
## History
|
## History
|
||||||
|
|
||||||
|
##### 8.5.0 - Multi Meter Support (2021-10-07)
|
||||||
|
|
||||||
|
##### 7.1.2 MQTT-Update - (2021-06-17)
|
||||||
|
|
||||||
##### 6.7.2 Image Processing in Memory - (2021-05-01)
|
##### 6.7.2 Image Processing in Memory - (2021-05-01)
|
||||||
|
|
||||||
##### 5.0.0 Setup Modus - (2020-12-06)
|
##### 5.0.0 Setup Modus - (2020-12-06)
|
||||||
@@ -92,8 +130,3 @@ There are some ideas and feature request, which are not followed currently - mai
|
|||||||
|
|
||||||
#### [Full Changelog](Changelog.md)
|
#### [Full Changelog](Changelog.md)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Solved topics
|
|
||||||
|
|
||||||
* n.a.
|
|
||||||
3
code/.helper/copy-esp-idf.bat
Normal file
3
code/.helper/copy-esp-idf.bat
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
copy "..\..\code\build\esp32cam-server-only.bin" "..\..\firmware\firmware.bin"
|
||||||
|
copy "..\..\code\build\bootloader\bootloader.bin" "..\..\firmware\bootloader.bin"
|
||||||
|
copy "..\..\code\build\partition_table\partition-table.bin" "..\..\firmware\partitions.bin"
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
copy "C:\Users\Muell\Documents\Programmieren\GitHub\AI-on-the-edge-device\code\.pio\build\esp32cam\firmware.bin" "C:\Users\Muell\Documents\Programmieren\GitHub\AI-on-the-edge-device\firmware\firmware.bin"
|
copy "..\..\code\.pio\build\esp32cam\firmware.bin" "..\..\firmware\firmware.bin"
|
||||||
copy "C:\Users\Muell\Documents\Programmieren\GitHub\AI-on-the-edge-device\code\.pio\build\esp32cam\bootloader.bin" "C:\Users\Muell\Documents\Programmieren\GitHub\AI-on-the-edge-device\firmware\bootloader.bin"
|
copy "..\..\code\.pio\build\esp32cam\bootloader.bin" "..\..\firmware\bootloader.bin"
|
||||||
copy "C:\Users\Muell\Documents\Programmieren\GitHub\AI-on-the-edge-device\code\.pio\build\esp32cam\partitions.bin" "C:\Users\Muell\Documents\Programmieren\GitHub\AI-on-the-edge-device\firmware\partitions.bin"
|
copy "..\..\code\.pio\build\esp32cam\partitions.bin" "..\..\firmware\partitions.bin"
|
||||||
@@ -1 +1 @@
|
|||||||
powershell Compress-Archive "C:\Users\Muell\Documents\Programmieren\GitHub\AI-on-the-edge-device\sd-card\html\*.*" "C:\Users\Muell\Documents\Programmieren\GitHub\AI-on-the-edge-device\firmware\html.zip"
|
powershell Compress-Archive "..\..\sd-card\html\*.*" "..\..\firmware\html.zip"
|
||||||
@@ -1,492 +0,0 @@
|
|||||||
#include "connect_wlan.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include "esp_wifi.h"
|
|
||||||
#include "esp_event_loop.h"
|
|
||||||
#include "freertos/event_groups.h"
|
|
||||||
#include "esp_log.h"
|
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <sstream>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
#include "Helper.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static const char *MAIN_TAG = "connect_wlan";
|
|
||||||
|
|
||||||
std::string ssid = "";
|
|
||||||
std::string passphrase = "";
|
|
||||||
std::string hostname = "";
|
|
||||||
std::string ipaddress = "";
|
|
||||||
std::string gw = "";
|
|
||||||
std::string netmask = "";
|
|
||||||
std::string dns = "";
|
|
||||||
std::string std_hostname = "watermeter";
|
|
||||||
|
|
||||||
static EventGroupHandle_t wifi_event_group;
|
|
||||||
|
|
||||||
|
|
||||||
#define BLINK_GPIO GPIO_NUM_33
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<string> ZerlegeZeile(std::string input, std::string _delimiter = "")
|
|
||||||
{
|
|
||||||
std::vector<string> Output;
|
|
||||||
std::string delimiter = " =,";
|
|
||||||
if (_delimiter.length() > 0){
|
|
||||||
delimiter = _delimiter;
|
|
||||||
}
|
|
||||||
|
|
||||||
input = trim(input, delimiter);
|
|
||||||
size_t pos = findDelimiterPos(input, delimiter);
|
|
||||||
std::string token;
|
|
||||||
while (pos != std::string::npos) {
|
|
||||||
token = input.substr(0, pos);
|
|
||||||
token = trim(token, delimiter);
|
|
||||||
Output.push_back(token);
|
|
||||||
input.erase(0, pos + 1);
|
|
||||||
input = trim(input, delimiter);
|
|
||||||
pos = findDelimiterPos(input, delimiter);
|
|
||||||
}
|
|
||||||
Output.push_back(input);
|
|
||||||
|
|
||||||
return Output;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void wifi_connect(){
|
|
||||||
wifi_config_t cfg = { };
|
|
||||||
strcpy((char*)cfg.sta.ssid, (const char*)ssid.c_str());
|
|
||||||
strcpy((char*)cfg.sta.password, (const char*)passphrase.c_str());
|
|
||||||
|
|
||||||
ESP_ERROR_CHECK( esp_wifi_disconnect() );
|
|
||||||
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &cfg) );
|
|
||||||
ESP_ERROR_CHECK( esp_wifi_connect() );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void blinkstatus(int dauer, int _anzahl)
|
|
||||||
{
|
|
||||||
gpio_reset_pin(BLINK_GPIO);
|
|
||||||
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
|
|
||||||
for (int i = 0; i < _anzahl; ++i)
|
|
||||||
{
|
|
||||||
gpio_set_level(BLINK_GPIO, 0);
|
|
||||||
vTaskDelay(dauer / portTICK_PERIOD_MS);
|
|
||||||
gpio_set_level(BLINK_GPIO, 1);
|
|
||||||
vTaskDelay(dauer / portTICK_PERIOD_MS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t event_handler(void *ctx, system_event_t *event)
|
|
||||||
{
|
|
||||||
switch(event->event_id) {
|
|
||||||
case SYSTEM_EVENT_STA_START:
|
|
||||||
blinkstatus(200, 1);
|
|
||||||
wifi_connect();
|
|
||||||
break;
|
|
||||||
case SYSTEM_EVENT_STA_GOT_IP:
|
|
||||||
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
|
|
||||||
blinkstatus(1000, 3);
|
|
||||||
break;
|
|
||||||
case SYSTEM_EVENT_STA_DISCONNECTED:
|
|
||||||
blinkstatus(200, 5);
|
|
||||||
esp_wifi_connect();
|
|
||||||
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void initialise_wifi()
|
|
||||||
{
|
|
||||||
ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL) );
|
|
||||||
wifi_event_group = xEventGroupCreate();
|
|
||||||
|
|
||||||
esp_log_level_set("wifi", ESP_LOG_NONE); // disable wifi driver logging
|
|
||||||
tcpip_adapter_init();
|
|
||||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
|
||||||
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
|
|
||||||
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
|
|
||||||
ESP_ERROR_CHECK( esp_wifi_start() );
|
|
||||||
esp_err_t ret = tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA , hostname.c_str());
|
|
||||||
if(ret != ESP_OK ){
|
|
||||||
ESP_LOGE(MAIN_TAG,"failed to set hostname:%d",ret);
|
|
||||||
}
|
|
||||||
xEventGroupWaitBits(wifi_event_group,CONNECTED_BIT,true,true,portMAX_DELAY);
|
|
||||||
tcpip_adapter_ip_info_t ip_info;
|
|
||||||
ESP_ERROR_CHECK(tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info));
|
|
||||||
ipaddress = std::string(ip4addr_ntoa(&ip_info.ip));
|
|
||||||
netmask = std::string(ip4addr_ntoa(&ip_info.netmask));
|
|
||||||
gw = std::string(ip4addr_ntoa(&ip_info.gw));
|
|
||||||
printf("IPv4 : %s\n", ip4addr_ntoa(&ip_info.ip));
|
|
||||||
printf("HostName : %s\n", hostname.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void strinttoip4(std::string ip, int &a, int &b, int &c, int &d) {
|
|
||||||
std::stringstream s(ip);
|
|
||||||
char ch; //to temporarily store the '.'
|
|
||||||
s >> a >> ch >> b >> ch >> c >> ch >> d;
|
|
||||||
}
|
|
||||||
|
|
||||||
void initialise_wifi_fixed_ip()
|
|
||||||
{
|
|
||||||
|
|
||||||
wifi_event_group = xEventGroupCreate();
|
|
||||||
|
|
||||||
ESP_ERROR_CHECK(esp_netif_init());
|
|
||||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
|
||||||
|
|
||||||
esp_netif_t *my_sta = esp_netif_create_default_wifi_sta();
|
|
||||||
|
|
||||||
esp_netif_dhcpc_stop(my_sta);
|
|
||||||
|
|
||||||
esp_netif_ip_info_t ip_info;
|
|
||||||
|
|
||||||
int a, b, c, d;
|
|
||||||
|
|
||||||
strinttoip4(ipaddress, 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);
|
|
||||||
|
|
||||||
esp_netif_set_ip_info(my_sta, &ip_info);
|
|
||||||
|
|
||||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
|
||||||
|
|
||||||
if (dns.length() > 0) {
|
|
||||||
esp_netif_dns_info_t dns_info;
|
|
||||||
ip4_addr_t ip;
|
|
||||||
ip.addr = esp_ip4addr_aton(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_ERROR_CHECK(esp_event_loop_init(event_handler, NULL) );
|
|
||||||
|
|
||||||
wifi_config_t wifi_config = { };
|
|
||||||
strcpy((char*)wifi_config.sta.ssid, (const char*)ssid.c_str());
|
|
||||||
strcpy((char*)wifi_config.sta.password, (const char*)passphrase.c_str());
|
|
||||||
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_start() );
|
|
||||||
|
|
||||||
ESP_LOGI(MAIN_TAG, "wifi_init_sta finished.");
|
|
||||||
|
|
||||||
EventBits_t bits = xEventGroupWaitBits(wifi_event_group,CONNECTED_BIT,true,true,portMAX_DELAY);
|
|
||||||
|
|
||||||
if (bits & CONNECTED_BIT) {
|
|
||||||
ESP_LOGI(MAIN_TAG, "connected to ap SSID:%s password:%s",
|
|
||||||
ssid.c_str(), passphrase.c_str());
|
|
||||||
} else {
|
|
||||||
ESP_LOGI(MAIN_TAG, "Failed to connect to SSID:%s, password:%s",
|
|
||||||
ssid.c_str(), passphrase.c_str());
|
|
||||||
}
|
|
||||||
tcpip_adapter_ip_info_t ip_info2;
|
|
||||||
ESP_ERROR_CHECK(tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info2));
|
|
||||||
ipaddress = std::string(ip4addr_ntoa(&ip_info2.ip));
|
|
||||||
netmask = std::string(ip4addr_ntoa(&ip_info2.netmask));
|
|
||||||
gw = std::string(ip4addr_ntoa(&ip_info2.gw));
|
|
||||||
|
|
||||||
// vEventGroupDelete(wifi_event_group);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void ConnectToWLAN()
|
|
||||||
{
|
|
||||||
if (ipaddress.length() == 0 || gw.length() == 0 || netmask.length() == 0)
|
|
||||||
{
|
|
||||||
printf("Connect to WLAN with dyn. IP\n");
|
|
||||||
initialise_wifi();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("Connect to WLAN with fixed IP\n");
|
|
||||||
initialise_wifi_fixed_ip();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool ChangeHostName(std::string fn, std::string _newhostname)
|
|
||||||
{
|
|
||||||
if (_newhostname == hostname)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
string line = "";
|
|
||||||
std::vector<string> zerlegt;
|
|
||||||
|
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
std::vector<string> neuesfile;
|
|
||||||
|
|
||||||
FILE* pFile;
|
|
||||||
fn = FormatFileName(fn);
|
|
||||||
pFile = OpenFileAndWait(fn.c_str(), "r");
|
|
||||||
|
|
||||||
printf("file loaded\n");
|
|
||||||
|
|
||||||
if (pFile == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
char zw[1024];
|
|
||||||
fgets(zw, 1024, pFile);
|
|
||||||
line = std::string(zw);
|
|
||||||
|
|
||||||
while ((line.size() > 0) || !(feof(pFile)))
|
|
||||||
{
|
|
||||||
printf("%s", line.c_str());
|
|
||||||
zerlegt = ZerlegeZeile(line, "=");
|
|
||||||
zerlegt[0] = trim(zerlegt[0], " ");
|
|
||||||
|
|
||||||
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "HOSTNAME")){
|
|
||||||
line = "hostname = \"" + _newhostname + "\"\n";
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
neuesfile.push_back(line);
|
|
||||||
|
|
||||||
if (fgets(zw, 1024, pFile) == NULL)
|
|
||||||
{
|
|
||||||
line = "";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
line = std::string(zw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
{
|
|
||||||
line = "\nhostname = \"" + _newhostname + "\"\n";
|
|
||||||
neuesfile.push_back(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(pFile);
|
|
||||||
|
|
||||||
pFile = OpenFileAndWait(fn.c_str(), "w+");
|
|
||||||
|
|
||||||
for (int i = 0; i < neuesfile.size(); ++i)
|
|
||||||
{
|
|
||||||
printf(neuesfile[i].c_str());
|
|
||||||
fputs(neuesfile[i].c_str(), pFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(pFile);
|
|
||||||
|
|
||||||
printf("*** Update hostname done ***\n");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void LoadWlanFromFile(std::string fn)
|
|
||||||
{
|
|
||||||
string line = "";
|
|
||||||
std::vector<string> zerlegt;
|
|
||||||
hostname = std_hostname;
|
|
||||||
|
|
||||||
FILE* pFile;
|
|
||||||
fn = FormatFileName(fn);
|
|
||||||
|
|
||||||
pFile = OpenFileAndWait(fn.c_str(), "r");
|
|
||||||
printf("file loaded\n");
|
|
||||||
|
|
||||||
if (pFile == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
char zw[1024];
|
|
||||||
fgets(zw, 1024, pFile);
|
|
||||||
line = std::string(zw);
|
|
||||||
|
|
||||||
while ((line.size() > 0) || !(feof(pFile)))
|
|
||||||
{
|
|
||||||
printf("%s", line.c_str());
|
|
||||||
zerlegt = ZerlegeZeile(line, "=");
|
|
||||||
zerlegt[0] = trim(zerlegt[0], " ");
|
|
||||||
for (int i = 2; i < zerlegt.size(); ++i)
|
|
||||||
zerlegt[1] = zerlegt[1] + "=" + zerlegt[i];
|
|
||||||
|
|
||||||
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "HOSTNAME")){
|
|
||||||
hostname = trim(zerlegt[1]);
|
|
||||||
if ((hostname[0] == '"') && (hostname[hostname.length()-1] == '"')){
|
|
||||||
hostname = hostname.substr(1, hostname.length()-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "SSID")){
|
|
||||||
ssid = trim(zerlegt[1]);
|
|
||||||
if ((ssid[0] == '"') && (ssid[ssid.length()-1] == '"')){
|
|
||||||
ssid = ssid.substr(1, ssid.length()-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "PASSWORD")){
|
|
||||||
passphrase = zerlegt[1];
|
|
||||||
if ((passphrase[0] == '"') && (passphrase[passphrase.length()-1] == '"')){
|
|
||||||
passphrase = passphrase.substr(1, passphrase.length()-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "IP")){
|
|
||||||
ipaddress = zerlegt[1];
|
|
||||||
if ((ipaddress[0] == '"') && (ipaddress[ipaddress.length()-1] == '"')){
|
|
||||||
ipaddress = ipaddress.substr(1, ipaddress.length()-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "GATEWAY")){
|
|
||||||
gw = zerlegt[1];
|
|
||||||
if ((gw[0] == '"') && (gw[gw.length()-1] == '"')){
|
|
||||||
gw = gw.substr(1, gw.length()-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "NETMASK")){
|
|
||||||
netmask = zerlegt[1];
|
|
||||||
if ((netmask[0] == '"') && (netmask[netmask.length()-1] == '"')){
|
|
||||||
netmask = netmask.substr(1, netmask.length()-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "DNS")){
|
|
||||||
dns = zerlegt[1];
|
|
||||||
if ((dns[0] == '"') && (dns[dns.length()-1] == '"')){
|
|
||||||
dns = dns.substr(1, dns.length()-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (fgets(zw, 1024, pFile) == NULL)
|
|
||||||
{
|
|
||||||
line = "";
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\nWLan: %s, %s\n", ssid.c_str(), passphrase.c_str());
|
|
||||||
printf("Hostename: %s\n", hostname.c_str());
|
|
||||||
printf("Fixed IP: %s, Gateway %s, Netmask %s, DNS %s\n", ipaddress.c_str(), gw.c_str(), netmask.c_str(), dns.c_str());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void LoadNetConfigFromFile(std::string fn, std::string &_ip, std::string &_gw, std::string &_netmask, std::string &_dns)
|
|
||||||
{
|
|
||||||
string line = "";
|
|
||||||
std::vector<string> zerlegt;
|
|
||||||
|
|
||||||
FILE* pFile;
|
|
||||||
fn = FormatFileName(fn);
|
|
||||||
pFile = OpenFileAndWait(fn.c_str(), "r");
|
|
||||||
|
|
||||||
printf("file loaded\n");
|
|
||||||
|
|
||||||
if (pFile == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
char zw[1024];
|
|
||||||
fgets(zw, 1024, pFile);
|
|
||||||
line = std::string(zw);
|
|
||||||
|
|
||||||
while ((line.size() > 0) || !(feof(pFile)))
|
|
||||||
{
|
|
||||||
printf("%s", line.c_str());
|
|
||||||
zerlegt = ZerlegeZeile(line, "=");
|
|
||||||
zerlegt[0] = trim(zerlegt[0], " ");
|
|
||||||
|
|
||||||
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "IP")){
|
|
||||||
_ip = zerlegt[1];
|
|
||||||
if ((_ip[0] == '"') && (_ip[_ip.length()-1] == '"')){
|
|
||||||
_ip = _ip.substr(1, _ip.length()-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "GATEWAY")){
|
|
||||||
_gw = zerlegt[1];
|
|
||||||
if ((_gw[0] == '"') && (_gw[_gw.length()-1] == '"')){
|
|
||||||
_gw = _gw.substr(1, _gw.length()-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "NETMASK")){
|
|
||||||
_netmask = zerlegt[1];
|
|
||||||
if ((_netmask[0] == '"') && (_netmask[_netmask.length()-1] == '"')){
|
|
||||||
_netmask = _netmask.substr(1, _netmask.length()-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "DNS")){
|
|
||||||
_dns = zerlegt[1];
|
|
||||||
if ((_dns[0] == '"') && (_dns[_dns.length()-1] == '"')){
|
|
||||||
_dns = _dns.substr(1, _dns.length()-2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fgets(zw, 1024, pFile) == NULL)
|
|
||||||
{
|
|
||||||
line = "";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
line = std::string(zw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(pFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::string getHostname(){
|
|
||||||
return hostname;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string getIPAddress(){
|
|
||||||
return ipaddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string getSSID(){
|
|
||||||
return ssid;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string getNetMask(){
|
|
||||||
return netmask;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string getGW(){
|
|
||||||
return gw;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
#ifndef CONNECT_WLAN_H
|
|
||||||
#define CONNECT_WLAN_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include "driver/gpio.h"
|
|
||||||
|
|
||||||
const int CONNECTED_BIT = BIT0;
|
|
||||||
void ConnectToWLAN();
|
|
||||||
|
|
||||||
void LoadWlanFromFile(std::string fn);
|
|
||||||
|
|
||||||
bool ChangeHostName(std::string fn, std::string _newhostname);
|
|
||||||
|
|
||||||
std::string getHostname();
|
|
||||||
std::string getIPAddress();
|
|
||||||
std::string getSSID();
|
|
||||||
std::string getNetMask();
|
|
||||||
std::string getGW();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
// ================================ CODE ======================================
|
// ================================ CODE ======================================
|
||||||
|
|
||||||
#include <esp_event_loop.h>
|
#include <esp_event.h>
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
#include <esp_system.h>
|
#include <esp_system.h>
|
||||||
#include <nvs_flash.h>
|
#include <nvs_flash.h>
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
|||||||
|
|
||||||
idf_component_register(SRCS ${app_sources}
|
idf_component_register(SRCS ${app_sources}
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
REQUIRES jomjol_helper)
|
REQUIRES jomjol_logfile)
|
||||||
|
|
||||||
|
|
||||||
105
code/components/jomjol_configfile/configFile.cpp
Normal file
105
code/components/jomjol_configfile/configFile.cpp
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
#include "Helper.h"
|
||||||
|
#include "configFile.h"
|
||||||
|
|
||||||
|
//static const char *TAGCONFIGFILE = "configFile";
|
||||||
|
|
||||||
|
ConfigFile::ConfigFile(std::string filePath)
|
||||||
|
{
|
||||||
|
std::string config = FormatFileName(filePath);
|
||||||
|
pFile = OpenFileAndWait(config.c_str(), "r");
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigFile::~ConfigFile()
|
||||||
|
{
|
||||||
|
fclose(pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigFile::isNewParagraph(std::string input)
|
||||||
|
{
|
||||||
|
if ((input[0] == '[') || ((input[0] == ';') && (input[1] == '[')))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigFile::GetNextParagraph(std::string& aktparamgraph, bool &disabled, bool &eof)
|
||||||
|
{
|
||||||
|
while (getNextLine(&aktparamgraph, disabled, eof) && !isNewParagraph(aktparamgraph));
|
||||||
|
|
||||||
|
if (isNewParagraph(aktparamgraph))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigFile::getNextLine(std::string *rt, bool &disabled, bool &eof)
|
||||||
|
{
|
||||||
|
eof = false;
|
||||||
|
char zw[1024] = "";
|
||||||
|
if (pFile == NULL)
|
||||||
|
{
|
||||||
|
*rt = "";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fgets(zw, 1024, pFile))
|
||||||
|
{
|
||||||
|
printf("%s", zw);
|
||||||
|
if ((strlen(zw) == 0) && feof(pFile))
|
||||||
|
{
|
||||||
|
*rt = "";
|
||||||
|
eof = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*rt = "";
|
||||||
|
eof = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*rt = zw;
|
||||||
|
*rt = trim(*rt);
|
||||||
|
while ((zw[0] == ';' || zw[0] == '#' || (rt->size() == 0)) && !(zw[1] == '[')) // Kommentarzeilen (; oder #) und Leerzeilen überspringen, es sei denn es ist ein neuer auskommentierter Paragraph
|
||||||
|
{
|
||||||
|
fgets(zw, 1024, pFile);
|
||||||
|
printf("%s", zw);
|
||||||
|
if (feof(pFile))
|
||||||
|
{
|
||||||
|
*rt = "";
|
||||||
|
eof = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*rt = zw;
|
||||||
|
*rt = trim(*rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
disabled = ((*rt)[0] == ';');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<string> ConfigFile::ZerlegeZeile(std::string input, std::string delimiter)
|
||||||
|
{
|
||||||
|
std::vector<string> Output;
|
||||||
|
// std::string delimiter = " =,";
|
||||||
|
|
||||||
|
input = trim(input, delimiter);
|
||||||
|
size_t pos = findDelimiterPos(input, delimiter);
|
||||||
|
std::string token;
|
||||||
|
while (pos != std::string::npos) {
|
||||||
|
token = input.substr(0, pos);
|
||||||
|
token = trim(token, delimiter);
|
||||||
|
Output.push_back(token);
|
||||||
|
input.erase(0, pos + 1);
|
||||||
|
input = trim(input, delimiter);
|
||||||
|
pos = findDelimiterPos(input, delimiter);
|
||||||
|
}
|
||||||
|
Output.push_back(input);
|
||||||
|
|
||||||
|
return Output;
|
||||||
|
|
||||||
|
}
|
||||||
16
code/components/jomjol_configfile/configFile.h
Normal file
16
code/components/jomjol_configfile/configFile.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class ConfigFile {
|
||||||
|
public:
|
||||||
|
ConfigFile(std::string filePath);
|
||||||
|
~ConfigFile();
|
||||||
|
|
||||||
|
bool isNewParagraph(std::string input);
|
||||||
|
bool GetNextParagraph(std::string& aktparamgraph, bool &disabled, bool &eof);
|
||||||
|
bool getNextLine(std::string* rt, bool &disabled, bool &eof);
|
||||||
|
std::vector<std::string> ZerlegeZeile(std::string input, std::string delimiter = " =, \t");
|
||||||
|
|
||||||
|
private:
|
||||||
|
FILE* pFile;
|
||||||
|
};
|
||||||
@@ -3,7 +3,7 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
|||||||
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||||
|
|
||||||
idf_component_register(SRCS ${app_sources}
|
idf_component_register(SRCS ${app_sources}
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "." "../../include"
|
||||||
REQUIRES esp_http_server jomjol_logfile)
|
REQUIRES esp_http_server jomjol_logfile jomjol_configfile jomjol_mqtt jomjol_flowcontroll)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
132
code/components/jomjol_controlGPIO/Color.cpp
Normal file
132
code/components/jomjol_controlGPIO/Color.cpp
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
#include "Color.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Int -> fixed point
|
||||||
|
int up( int x ) { return x * 255; }
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int iRgbSqrt( int num ) {
|
||||||
|
// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_.28base_2.29
|
||||||
|
assert( "sqrt input should be non-negative" && num >= 0 );
|
||||||
|
assert( "sqrt input should no exceed 16 bits" && num <= 0xFFFF );
|
||||||
|
int res = 0;
|
||||||
|
int bit = 1 << 16;
|
||||||
|
while ( bit > num )
|
||||||
|
bit >>= 2;
|
||||||
|
while ( bit != 0 ) {
|
||||||
|
if ( num >= res + bit ) {
|
||||||
|
num -= res + bit;
|
||||||
|
res = ( res >> 1 ) + bit;
|
||||||
|
} else
|
||||||
|
res >>= 1;
|
||||||
|
bit >>= 2;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rgb::Rgb( Hsv y ) {
|
||||||
|
// https://stackoverflow.com/questions/24152553/hsv-to-rgb-and-back-without-floating-point-math-in-python
|
||||||
|
// greyscale
|
||||||
|
if( y.s == 0 ) {
|
||||||
|
r = g = b = y.v;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int region = y.h / 43;
|
||||||
|
const int remainder = ( y.h - ( region * 43 ) ) * 6;
|
||||||
|
|
||||||
|
const int p = ( y.v * ( 255 - y.s ) ) >> 8;
|
||||||
|
const int q = ( y.v * ( 255 - ( ( y.s * remainder ) >> 8 ) ) ) >> 8;
|
||||||
|
const int t = ( y.v * ( 255 - ( ( y.s * (255 -remainder ) ) >> 8 ) ) ) >> 8;
|
||||||
|
|
||||||
|
switch( region ) {
|
||||||
|
case 0: r = y.v; g = t; b = p; break;
|
||||||
|
case 1: r = q; g = y.v; b = p; break;
|
||||||
|
case 2: r = p; g = y.v; b = t; break;
|
||||||
|
case 3: r = p; g = q; b = y.v; break;
|
||||||
|
case 4: r = t; g = p; b = y.v; break;
|
||||||
|
case 5: r = y.v; g = p; b = q; break;
|
||||||
|
default: __builtin_trap();
|
||||||
|
}
|
||||||
|
|
||||||
|
a = y.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rgb& Rgb::operator=( Hsv hsv ) {
|
||||||
|
Rgb r{ hsv };
|
||||||
|
swap( r );
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rgb Rgb::operator+( Rgb in ) const {
|
||||||
|
auto copy = *this;
|
||||||
|
copy += in;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rgb& Rgb::operator+=( Rgb in ) {
|
||||||
|
unsigned int red = r + in.r;
|
||||||
|
r = ( red < 255 ) ? red : 255;
|
||||||
|
unsigned int green = g + in.g;
|
||||||
|
g = ( green < 255 ) ? green : 255;
|
||||||
|
unsigned int blue = b + in.b;
|
||||||
|
b = ( blue < 255 ) ? blue : 255;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rgb& Rgb::blend( Rgb in ) {
|
||||||
|
unsigned int inAlpha = in.a * ( 255 - a );
|
||||||
|
unsigned int alpha = a + inAlpha;
|
||||||
|
r = iRgbSqrt( ( ( r * r * a ) + ( in.r * in.r * inAlpha ) ) / alpha );
|
||||||
|
g = iRgbSqrt( ( ( g * g * a ) + ( in.g * in.g * inAlpha ) ) / alpha );
|
||||||
|
b = iRgbSqrt( ( ( b * b * a ) + ( in.b * in.b * inAlpha ) ) / alpha );
|
||||||
|
a = alpha;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t IRAM_ATTR Rgb::getGrb( int idx ) {
|
||||||
|
switch ( idx ) {
|
||||||
|
case 0: return g;
|
||||||
|
case 1: return r;
|
||||||
|
case 2: return b;
|
||||||
|
}
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
Hsv::Hsv( Rgb r ) {
|
||||||
|
int min = std::min( r.r, std::min( r.g, r.b ) );
|
||||||
|
int max = std::max( r.r, std::max( r.g, r.b ) );
|
||||||
|
int chroma = max - min;
|
||||||
|
|
||||||
|
v = max;
|
||||||
|
if ( chroma == 0 ) {
|
||||||
|
h = s = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = up( chroma ) / max;
|
||||||
|
int hh;
|
||||||
|
if ( max == r.r )
|
||||||
|
hh = ( up( int( r.g ) - int( r.b ) ) ) / chroma / 6;
|
||||||
|
else if ( max == r.g )
|
||||||
|
hh = 255 / 3 + ( up( int( r.b ) - int( r.r ) ) ) / chroma / 6;
|
||||||
|
else
|
||||||
|
hh = 2 * 255 / 3 + ( up( int( r.r ) - int( r.g ) ) ) / chroma / 6;
|
||||||
|
|
||||||
|
if ( hh < 0 )
|
||||||
|
hh += 255;
|
||||||
|
h = hh;
|
||||||
|
|
||||||
|
a = r.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
Hsv& Hsv::operator=( Rgb rgb ) {
|
||||||
|
Hsv h{ rgb };
|
||||||
|
swap( h );
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
69
code/components/jomjol_controlGPIO/Color.h
Normal file
69
code/components/jomjol_controlGPIO/Color.h
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include "esp_attr.h"
|
||||||
|
union Hsv;
|
||||||
|
|
||||||
|
union Rgb {
|
||||||
|
struct __attribute__ ((packed)) {
|
||||||
|
uint8_t r, g, b, a;
|
||||||
|
};
|
||||||
|
uint32_t value;
|
||||||
|
|
||||||
|
Rgb( uint8_t r = 0, uint8_t g = 0, uint8_t b = 0, uint8_t a = 255 ) : r( r ), g( g ), b( b ), a( a ) {}
|
||||||
|
Rgb( Hsv c );
|
||||||
|
Rgb& operator=( Rgb rgb ) { swap( rgb ); return *this; }
|
||||||
|
Rgb& operator=( Hsv hsv );
|
||||||
|
Rgb operator+( Rgb in ) const;
|
||||||
|
Rgb& operator+=( Rgb in );
|
||||||
|
bool operator==( Rgb in ) const { return in.value == value; }
|
||||||
|
Rgb& blend( Rgb in );
|
||||||
|
void swap( Rgb& o ) { value = o.value; }
|
||||||
|
void linearize() {
|
||||||
|
r = channelGamma(r);
|
||||||
|
g = channelGamma(g);
|
||||||
|
b = channelGamma(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t IRAM_ATTR getGrb( int idx );
|
||||||
|
|
||||||
|
void stretchChannels( uint8_t maxR, uint8_t maxG, uint8_t maxB ) {
|
||||||
|
r = stretch( r, maxR );
|
||||||
|
g = stretch( g, maxG );
|
||||||
|
b = stretch( b, maxB );
|
||||||
|
}
|
||||||
|
|
||||||
|
void stretchChannelsEvenly( uint8_t max ) {
|
||||||
|
stretchChannels( max, max, max );
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t stretch( int value, uint8_t max ) {
|
||||||
|
return ( value * max ) >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t channelGamma( int channel ) {
|
||||||
|
/* The optimal gamma correction is x^2.8. However, this is expensive to
|
||||||
|
* compute. Therefore, we use x^3 for gamma correction. Also, we add a
|
||||||
|
* bias as the WS2812 LEDs do not turn on for values less than 4. */
|
||||||
|
if (channel == 0)
|
||||||
|
return channel;
|
||||||
|
channel = channel * channel * channel * 251;
|
||||||
|
channel >>= 24;
|
||||||
|
return static_cast< uint8_t >( 4 + channel );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
union Hsv {
|
||||||
|
struct __attribute__ ((packed)) {
|
||||||
|
uint8_t h, s, v, a;
|
||||||
|
};
|
||||||
|
uint32_t value;
|
||||||
|
|
||||||
|
Hsv( uint8_t h, uint8_t s = 0, uint8_t v = 0, uint8_t a = 255 ) : h( h ), s( s ), v( v ), a( a ) {}
|
||||||
|
Hsv( Rgb r );
|
||||||
|
Hsv& operator=( Hsv h ) { swap( h ); return *this; }
|
||||||
|
Hsv& operator=( Rgb rgb );
|
||||||
|
bool operator==( Hsv in ) const { return in.value == value; }
|
||||||
|
void swap( Hsv& o ) { value = o.value; }
|
||||||
|
};
|
||||||
63
code/components/jomjol_controlGPIO/SmartLeds.cpp
Normal file
63
code/components/jomjol_controlGPIO/SmartLeds.cpp
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#include "SmartLeds.h"
|
||||||
|
|
||||||
|
IsrCore SmartLed::_interruptCore = CoreCurrent;
|
||||||
|
intr_handle_t SmartLed::_interruptHandle = NULL;
|
||||||
|
|
||||||
|
SmartLed*& IRAM_ATTR SmartLed::ledForChannel( int channel ) {
|
||||||
|
static SmartLed* table[8] = { nullptr };
|
||||||
|
assert( channel < 8 );
|
||||||
|
return table[ channel ];
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRAM_ATTR SmartLed::interruptHandler(void*) {
|
||||||
|
for (int channel = 0; channel != 8; channel++) {
|
||||||
|
auto self = ledForChannel( channel );
|
||||||
|
|
||||||
|
if ( RMT.int_st.val & (1 << (24 + channel ) ) ) { // tx_thr_event
|
||||||
|
if ( self )
|
||||||
|
self->copyRmtHalfBlock();
|
||||||
|
RMT.int_clr.val |= 1 << ( 24 + channel );
|
||||||
|
} else if ( RMT.int_st.val & ( 1 << (3 * channel ) ) ) { // tx_end
|
||||||
|
if ( self )
|
||||||
|
xSemaphoreGiveFromISR( self->_finishedFlag, nullptr );
|
||||||
|
RMT.int_clr.val |= 1 << ( 3 * channel );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRAM_ATTR SmartLed::copyRmtHalfBlock() {
|
||||||
|
int offset = detail::MAX_PULSES * _halfIdx;
|
||||||
|
_halfIdx = !_halfIdx;
|
||||||
|
int len = 3 - _componentPosition + 3 * ( _count - 1 );
|
||||||
|
len = std::min( len, detail::MAX_PULSES / 8 );
|
||||||
|
|
||||||
|
if ( !len ) {
|
||||||
|
for ( int i = 0; i < detail::MAX_PULSES; i++) {
|
||||||
|
RMTMEM.chan[ _channel].data32[i + offset ].val = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for ( i = 0; i != len && _pixelPosition != _count; i++ ) {
|
||||||
|
uint8_t val = _buffer[ _pixelPosition ].getGrb( _componentPosition );
|
||||||
|
for ( int j = 0; j != 8; j++, val <<= 1 ) {
|
||||||
|
int bit = val >> 7;
|
||||||
|
int idx = i * 8 + offset + j;
|
||||||
|
RMTMEM.chan[ _channel ].data32[ idx ].val = _bitToRmt[ bit & 0x01 ].value;
|
||||||
|
}
|
||||||
|
if ( _pixelPosition == _count - 1 && _componentPosition == 2 ) {
|
||||||
|
RMTMEM.chan[ _channel ].data32[ i * 8 + offset + 7 ].duration1 =
|
||||||
|
_timing.TRS / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||||
|
}
|
||||||
|
|
||||||
|
_componentPosition++;
|
||||||
|
if ( _componentPosition == 3 ) {
|
||||||
|
_componentPosition = 0;
|
||||||
|
_pixelPosition++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( i *= 8; i != detail::MAX_PULSES; i++ ) {
|
||||||
|
RMTMEM.chan[ _channel ].data32[ i + offset ].val = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
530
code/components/jomjol_controlGPIO/SmartLeds.h
Normal file
530
code/components/jomjol_controlGPIO/SmartLeds.h
Normal file
@@ -0,0 +1,530 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A C++ driver for the WS2812 LEDs using the RMT peripheral on the ESP32.
|
||||||
|
*
|
||||||
|
* Jan "yaqwsx" Mrázek <email@honzamrazek.cz>
|
||||||
|
*
|
||||||
|
* Based on the work by Martin F. Falatic - https://github.com/FozzTexx/ws2812-demo
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#if defined ( ARDUINO )
|
||||||
|
extern "C" { // ...someone forgot to put in the includes...
|
||||||
|
#include "esp32-hal.h"
|
||||||
|
#include "esp_intr_alloc.h"
|
||||||
|
#include "esp_ipc.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/periph_ctrl.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
#include "soc/rmt_struct.h"
|
||||||
|
#include <driver/spi_master.h>
|
||||||
|
#include "esp_idf_version.h"
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL( 4, 0, 0 )
|
||||||
|
#include "soc/dport_reg.h"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#elif defined ( ESP_PLATFORM )
|
||||||
|
extern "C" { // ...someone forgot to put in the includes...
|
||||||
|
#include <esp_intr_alloc.h>
|
||||||
|
#include <esp_ipc.h>
|
||||||
|
#include <driver/gpio.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/semphr.h>
|
||||||
|
#include <soc/dport_reg.h>
|
||||||
|
#include <soc/gpio_sig_map.h>
|
||||||
|
#include <soc/rmt_struct.h>
|
||||||
|
#include <driver/spi_master.h>
|
||||||
|
}
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "Color.h"
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
struct TimingParams {
|
||||||
|
uint32_t T0H;
|
||||||
|
uint32_t T1H;
|
||||||
|
uint32_t T0L;
|
||||||
|
uint32_t T1L;
|
||||||
|
uint32_t TRS;
|
||||||
|
};
|
||||||
|
|
||||||
|
union RmtPulsePair {
|
||||||
|
struct {
|
||||||
|
int duration0:15;
|
||||||
|
int level0:1;
|
||||||
|
int duration1:15;
|
||||||
|
int level1:1;
|
||||||
|
};
|
||||||
|
uint32_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int DIVIDER = 4; // 8 still seems to work, but timings become marginal
|
||||||
|
static const int MAX_PULSES = 32; // A channel has a 64 "pulse" buffer - we use half per pass
|
||||||
|
static const double RMT_DURATION_NS = 12.5; // minimum time of a single RMT duration based on clock ns
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
using LedType = detail::TimingParams;
|
||||||
|
|
||||||
|
static const LedType LED_WS2812 = { 350, 700, 800, 600, 50000 };
|
||||||
|
static const LedType LED_WS2812B = { 400, 850, 850, 400, 50100 };
|
||||||
|
static const LedType LED_SK6812 = { 300, 600, 900, 600, 80000 };
|
||||||
|
static const LedType LED_WS2813 = { 350, 800, 350, 350, 300000 };
|
||||||
|
|
||||||
|
enum BufferType { SingleBuffer = 0, DoubleBuffer };
|
||||||
|
|
||||||
|
enum IsrCore { CoreFirst = 0, CoreSecond = 1, CoreCurrent = 2};
|
||||||
|
|
||||||
|
class SmartLed {
|
||||||
|
public:
|
||||||
|
// The RMT interrupt must not run on the same core as WiFi interrupts, otherwise SmartLeds
|
||||||
|
// can't fill the RMT buffer fast enough, resulting in rendering artifacts.
|
||||||
|
// Usually, that means you have to set isrCore == CoreSecond.
|
||||||
|
//
|
||||||
|
// If you use anything other than CoreCurrent, the FreeRTOS scheduler MUST be already running,
|
||||||
|
// so you can't use it if you define SmartLed as global variable.
|
||||||
|
SmartLed( const LedType& type, int count, int pin, int channel = 0, BufferType doubleBuffer = SingleBuffer, IsrCore isrCore = CoreCurrent)
|
||||||
|
: _timing( type ),
|
||||||
|
_channel( channel ),
|
||||||
|
_count( count ),
|
||||||
|
_firstBuffer( new Rgb[ count ] ),
|
||||||
|
_secondBuffer( doubleBuffer ? new Rgb[ count ] : nullptr ),
|
||||||
|
_finishedFlag( xSemaphoreCreateBinary() )
|
||||||
|
{
|
||||||
|
assert( channel >= 0 && channel < 8 );
|
||||||
|
assert( ledForChannel( channel ) == nullptr );
|
||||||
|
|
||||||
|
xSemaphoreGive( _finishedFlag );
|
||||||
|
|
||||||
|
DPORT_SET_PERI_REG_MASK( DPORT_PERIP_CLK_EN_REG, DPORT_RMT_CLK_EN );
|
||||||
|
DPORT_CLEAR_PERI_REG_MASK( DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST );
|
||||||
|
|
||||||
|
PIN_FUNC_SELECT( GPIO_PIN_MUX_REG[ pin ], 2 );
|
||||||
|
gpio_set_direction( static_cast< gpio_num_t >( pin ), GPIO_MODE_OUTPUT );
|
||||||
|
gpio_matrix_out( static_cast< gpio_num_t >( pin ), RMT_SIG_OUT0_IDX + _channel, 0, 0 );
|
||||||
|
initChannel( _channel );
|
||||||
|
|
||||||
|
RMT.tx_lim_ch[ _channel ].limit = detail::MAX_PULSES;
|
||||||
|
RMT.int_ena.val |= 1 << ( 24 + _channel );
|
||||||
|
RMT.int_ena.val |= 1 << ( 3 * _channel );
|
||||||
|
|
||||||
|
_bitToRmt[ 0 ].level0 = 1;
|
||||||
|
_bitToRmt[ 0 ].level1 = 0;
|
||||||
|
_bitToRmt[ 0 ].duration0 = _timing.T0H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||||
|
_bitToRmt[ 0 ].duration1 = _timing.T0L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||||
|
|
||||||
|
_bitToRmt[ 1 ].level0 = 1;
|
||||||
|
_bitToRmt[ 1 ].level1 = 0;
|
||||||
|
_bitToRmt[ 1 ].duration0 = _timing.T1H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||||
|
_bitToRmt[ 1 ].duration1 = _timing.T1L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||||
|
|
||||||
|
if ( !anyAlive() ) {
|
||||||
|
_interruptCore = isrCore;
|
||||||
|
if(isrCore != CoreCurrent) {
|
||||||
|
ESP_ERROR_CHECK(esp_ipc_call_blocking(isrCore, registerInterrupt, NULL));
|
||||||
|
} else {
|
||||||
|
registerInterrupt(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ledForChannel( channel ) = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~SmartLed() {
|
||||||
|
ledForChannel( _channel ) = nullptr;
|
||||||
|
if ( !anyAlive() ) {
|
||||||
|
if(_interruptCore != CoreCurrent) {
|
||||||
|
ESP_ERROR_CHECK(esp_ipc_call_blocking(_interruptCore, unregisterInterrupt, NULL));
|
||||||
|
} else {
|
||||||
|
unregisterInterrupt(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vSemaphoreDelete( _finishedFlag );
|
||||||
|
}
|
||||||
|
|
||||||
|
Rgb& operator[]( int idx ) {
|
||||||
|
return _firstBuffer[ idx ];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Rgb& operator[]( int idx ) const {
|
||||||
|
return _firstBuffer[ idx ];
|
||||||
|
}
|
||||||
|
|
||||||
|
void show() {
|
||||||
|
_buffer = _firstBuffer.get();
|
||||||
|
startTransmission();
|
||||||
|
swapBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wait( TickType_t timeout = portMAX_DELAY ) {
|
||||||
|
if( xSemaphoreTake( _finishedFlag, timeout ) == pdTRUE ) {
|
||||||
|
xSemaphoreGive( _finishedFlag );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int size() const {
|
||||||
|
return _count;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rgb *begin() { return _firstBuffer.get(); }
|
||||||
|
const Rgb *begin() const { return _firstBuffer.get(); }
|
||||||
|
const Rgb *cbegin() const { return _firstBuffer.get(); }
|
||||||
|
|
||||||
|
Rgb *end() { return _firstBuffer.get() + _count; }
|
||||||
|
const Rgb *end() const { return _firstBuffer.get() + _count; }
|
||||||
|
const Rgb *cend() const { return _firstBuffer.get() + _count; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static intr_handle_t _interruptHandle;
|
||||||
|
static IsrCore _interruptCore;
|
||||||
|
|
||||||
|
static void initChannel( int channel ) {
|
||||||
|
RMT.apb_conf.fifo_mask = 1; //enable memory access, instead of FIFO mode.
|
||||||
|
RMT.apb_conf.mem_tx_wrap_en = 1; //wrap around when hitting end of buffer
|
||||||
|
RMT.conf_ch[ channel ].conf0.div_cnt = detail::DIVIDER;
|
||||||
|
RMT.conf_ch[ channel ].conf0.mem_size = 1;
|
||||||
|
RMT.conf_ch[ channel ].conf0.carrier_en = 0;
|
||||||
|
RMT.conf_ch[ channel ].conf0.carrier_out_lv = 1;
|
||||||
|
RMT.conf_ch[ channel ].conf0.mem_pd = 0;
|
||||||
|
|
||||||
|
RMT.conf_ch[ channel ].conf1.rx_en = 0;
|
||||||
|
RMT.conf_ch[ channel ].conf1.mem_owner = 0;
|
||||||
|
RMT.conf_ch[ channel ].conf1.tx_conti_mode = 0; //loop back mode.
|
||||||
|
RMT.conf_ch[ channel ].conf1.ref_always_on = 1; // use apb clock: 80M
|
||||||
|
RMT.conf_ch[ channel ].conf1.idle_out_en = 1;
|
||||||
|
RMT.conf_ch[ channel ].conf1.idle_out_lv = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void registerInterrupt(void *) {
|
||||||
|
ESP_ERROR_CHECK(esp_intr_alloc( ETS_RMT_INTR_SOURCE, 0, interruptHandler, nullptr, &_interruptHandle));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unregisterInterrupt(void*) {
|
||||||
|
esp_intr_free( _interruptHandle );
|
||||||
|
}
|
||||||
|
|
||||||
|
static SmartLed*& IRAM_ATTR ledForChannel( int channel );
|
||||||
|
static void IRAM_ATTR interruptHandler( void* );
|
||||||
|
|
||||||
|
void IRAM_ATTR copyRmtHalfBlock();
|
||||||
|
|
||||||
|
void swapBuffers() {
|
||||||
|
if ( _secondBuffer )
|
||||||
|
_firstBuffer.swap( _secondBuffer );
|
||||||
|
}
|
||||||
|
|
||||||
|
void startTransmission() {
|
||||||
|
// Invalid use of the library
|
||||||
|
if( xSemaphoreTake( _finishedFlag, 0 ) != pdTRUE )
|
||||||
|
abort();
|
||||||
|
|
||||||
|
_pixelPosition = _componentPosition = _halfIdx = 0;
|
||||||
|
copyRmtHalfBlock();
|
||||||
|
if ( _pixelPosition < _count )
|
||||||
|
copyRmtHalfBlock();
|
||||||
|
|
||||||
|
RMT.conf_ch[ _channel ].conf1.mem_rd_rst = 1;
|
||||||
|
RMT.conf_ch[ _channel ].conf1.tx_start = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool anyAlive() {
|
||||||
|
for ( int i = 0; i != 8; i++ )
|
||||||
|
if ( ledForChannel( i ) != nullptr ) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LedType& _timing;
|
||||||
|
int _channel;
|
||||||
|
detail::RmtPulsePair _bitToRmt[ 2 ];
|
||||||
|
int _count;
|
||||||
|
std::unique_ptr< Rgb[] > _firstBuffer;
|
||||||
|
std::unique_ptr< Rgb[] > _secondBuffer;
|
||||||
|
Rgb *_buffer;
|
||||||
|
|
||||||
|
xSemaphoreHandle _finishedFlag;
|
||||||
|
|
||||||
|
int _pixelPosition;
|
||||||
|
int _componentPosition;
|
||||||
|
int _halfIdx;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Apa102 {
|
||||||
|
public:
|
||||||
|
struct ApaRgb {
|
||||||
|
ApaRgb( uint8_t r = 0, uint8_t g = 0, uint32_t b = 0, uint32_t v = 0xFF )
|
||||||
|
: v( 0xE0 | v ), b( b ), g( g ), r( r )
|
||||||
|
{}
|
||||||
|
|
||||||
|
ApaRgb& operator=( const Rgb& o ) {
|
||||||
|
r = o.r;
|
||||||
|
g = o.g;
|
||||||
|
b = o.b;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApaRgb& operator=( const Hsv& o ) {
|
||||||
|
*this = Rgb{ o };
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t v, b, g, r;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int FINAL_FRAME_SIZE = 4;
|
||||||
|
static const int TRANS_COUNT = 2 + 8;
|
||||||
|
|
||||||
|
Apa102( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer )
|
||||||
|
: _count( count ),
|
||||||
|
_firstBuffer( new ApaRgb[ count ] ),
|
||||||
|
_secondBuffer( doubleBuffer ? new ApaRgb[ count ] : nullptr ),
|
||||||
|
_initFrame( 0 )
|
||||||
|
{
|
||||||
|
spi_bus_config_t buscfg;
|
||||||
|
memset( &buscfg, 0, sizeof( buscfg ) );
|
||||||
|
buscfg.mosi_io_num = datapin;
|
||||||
|
buscfg.miso_io_num = -1;
|
||||||
|
buscfg.sclk_io_num = clkpin;
|
||||||
|
buscfg.quadwp_io_num = -1;
|
||||||
|
buscfg.quadhd_io_num = -1;
|
||||||
|
buscfg.max_transfer_sz = 65535;
|
||||||
|
|
||||||
|
spi_device_interface_config_t devcfg;
|
||||||
|
memset( &devcfg, 0, sizeof( devcfg ) );
|
||||||
|
devcfg.clock_speed_hz = 1000000;
|
||||||
|
devcfg.mode = 0;
|
||||||
|
devcfg.spics_io_num = -1;
|
||||||
|
devcfg.queue_size = TRANS_COUNT;
|
||||||
|
devcfg.pre_cb = nullptr;
|
||||||
|
|
||||||
|
auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
|
||||||
|
assert( ret == ESP_OK );
|
||||||
|
|
||||||
|
ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
|
||||||
|
assert( ret == ESP_OK );
|
||||||
|
|
||||||
|
std::fill_n( _finalFrame, FINAL_FRAME_SIZE, 0xFFFFFFFF );
|
||||||
|
}
|
||||||
|
|
||||||
|
~Apa102() {
|
||||||
|
// ToDo
|
||||||
|
}
|
||||||
|
|
||||||
|
ApaRgb& operator[]( int idx ) {
|
||||||
|
return _firstBuffer[ idx ];
|
||||||
|
}
|
||||||
|
|
||||||
|
const ApaRgb& operator[]( int idx ) const {
|
||||||
|
return _firstBuffer[ idx ];
|
||||||
|
}
|
||||||
|
|
||||||
|
void show() {
|
||||||
|
_buffer = _firstBuffer.get();
|
||||||
|
startTransmission();
|
||||||
|
swapBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait() {
|
||||||
|
for ( int i = 0; i != _transCount; i++ ) {
|
||||||
|
spi_transaction_t *t;
|
||||||
|
spi_device_get_trans_result( _spi, &t, portMAX_DELAY );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void swapBuffers() {
|
||||||
|
if ( _secondBuffer )
|
||||||
|
_firstBuffer.swap( _secondBuffer );
|
||||||
|
}
|
||||||
|
|
||||||
|
void startTransmission() {
|
||||||
|
for ( int i = 0; i != TRANS_COUNT; i++ ) {
|
||||||
|
_transactions[ i ].cmd = 0;
|
||||||
|
_transactions[ i ].addr = 0;
|
||||||
|
_transactions[ i ].flags = 0;
|
||||||
|
_transactions[ i ].rxlength = 0;
|
||||||
|
_transactions[ i ].rx_buffer = nullptr;
|
||||||
|
}
|
||||||
|
// Init frame
|
||||||
|
_transactions[ 0 ].length = 32;
|
||||||
|
_transactions[ 0 ].tx_buffer = &_initFrame;
|
||||||
|
spi_device_queue_trans( _spi, _transactions + 0, portMAX_DELAY );
|
||||||
|
// Data
|
||||||
|
_transactions[ 1 ].length = 32 * _count;
|
||||||
|
_transactions[ 1 ].tx_buffer = _buffer;
|
||||||
|
spi_device_queue_trans( _spi, _transactions + 1, portMAX_DELAY );
|
||||||
|
_transCount = 2;
|
||||||
|
// End frame
|
||||||
|
for ( int i = 0; i != 1 + _count / 32 / FINAL_FRAME_SIZE; i++ ) {
|
||||||
|
_transactions[ 2 + i ].length = 32 * FINAL_FRAME_SIZE;
|
||||||
|
_transactions[ 2 + i ].tx_buffer = _finalFrame;
|
||||||
|
spi_device_queue_trans( _spi, _transactions + 2 + i, portMAX_DELAY );
|
||||||
|
_transCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_device_handle_t _spi;
|
||||||
|
int _count;
|
||||||
|
std::unique_ptr< ApaRgb[] > _firstBuffer, _secondBuffer;
|
||||||
|
ApaRgb *_buffer;
|
||||||
|
|
||||||
|
spi_transaction_t _transactions[ TRANS_COUNT ];
|
||||||
|
int _transCount;
|
||||||
|
|
||||||
|
uint32_t _initFrame;
|
||||||
|
uint32_t _finalFrame[ FINAL_FRAME_SIZE ];
|
||||||
|
};
|
||||||
|
|
||||||
|
class LDP8806 {
|
||||||
|
public:
|
||||||
|
struct LDP8806_GRB {
|
||||||
|
|
||||||
|
LDP8806_GRB( uint8_t g_7bit = 0, uint8_t r_7bit = 0, uint32_t b_7bit = 0 )
|
||||||
|
: g( g_7bit ), r( r_7bit ), b( b_7bit )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LDP8806_GRB& operator=( const Rgb& o ) {
|
||||||
|
//Convert 8->7bit colour
|
||||||
|
r = ( o.r * 127 / 256 ) | 0x80;
|
||||||
|
g = ( o.g * 127 / 256 ) | 0x80;
|
||||||
|
b = ( o.b * 127 / 256 ) | 0x80;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
LDP8806_GRB& operator=( const Hsv& o ) {
|
||||||
|
*this = Rgb{ o };
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t g, r, b;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int LED_FRAME_SIZE_BYTES = sizeof( LDP8806_GRB );
|
||||||
|
static const int LATCH_FRAME_SIZE_BYTES = 3;
|
||||||
|
static const int TRANS_COUNT_MAX = 20;//Arbitrary, supports up to 600 LED
|
||||||
|
|
||||||
|
LDP8806( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, uint32_t clock_speed_hz = 2000000 )
|
||||||
|
: _count( count ),
|
||||||
|
_firstBuffer( new LDP8806_GRB[ count ] ),
|
||||||
|
_secondBuffer( doubleBuffer ? new LDP8806_GRB[ count ] : nullptr ),
|
||||||
|
// one 'latch'/start-of-data mark frame for every 32 leds
|
||||||
|
_latchFrames( ( count + 31 ) / 32 )
|
||||||
|
{
|
||||||
|
spi_bus_config_t buscfg;
|
||||||
|
memset( &buscfg, 0, sizeof( buscfg ) );
|
||||||
|
buscfg.mosi_io_num = datapin;
|
||||||
|
buscfg.miso_io_num = -1;
|
||||||
|
buscfg.sclk_io_num = clkpin;
|
||||||
|
buscfg.quadwp_io_num = -1;
|
||||||
|
buscfg.quadhd_io_num = -1;
|
||||||
|
buscfg.max_transfer_sz = 65535;
|
||||||
|
|
||||||
|
spi_device_interface_config_t devcfg;
|
||||||
|
memset( &devcfg, 0, sizeof( devcfg ) );
|
||||||
|
devcfg.clock_speed_hz = clock_speed_hz;
|
||||||
|
devcfg.mode = 0;
|
||||||
|
devcfg.spics_io_num = -1;
|
||||||
|
devcfg.queue_size = TRANS_COUNT_MAX;
|
||||||
|
devcfg.pre_cb = nullptr;
|
||||||
|
|
||||||
|
auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
|
||||||
|
assert( ret == ESP_OK );
|
||||||
|
|
||||||
|
ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
|
||||||
|
assert( ret == ESP_OK );
|
||||||
|
|
||||||
|
std::fill_n( _latchBuffer, LATCH_FRAME_SIZE_BYTES, 0x0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
~LDP8806() {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
LDP8806_GRB& operator[]( int idx ) {
|
||||||
|
return _firstBuffer[ idx ];
|
||||||
|
}
|
||||||
|
|
||||||
|
const LDP8806_GRB& operator[]( int idx ) const {
|
||||||
|
return _firstBuffer[ idx ];
|
||||||
|
}
|
||||||
|
|
||||||
|
void show() {
|
||||||
|
_buffer = _firstBuffer.get();
|
||||||
|
startTransmission();
|
||||||
|
swapBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait() {
|
||||||
|
while ( _transCount-- ) {
|
||||||
|
spi_transaction_t *t;
|
||||||
|
spi_device_get_trans_result( _spi, &t, portMAX_DELAY );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void swapBuffers() {
|
||||||
|
if ( _secondBuffer )
|
||||||
|
_firstBuffer.swap( _secondBuffer );
|
||||||
|
}
|
||||||
|
|
||||||
|
void startTransmission() {
|
||||||
|
_transCount = 0;
|
||||||
|
for ( int i = 0; i != TRANS_COUNT_MAX; i++ ) {
|
||||||
|
_transactions[ i ].cmd = 0;
|
||||||
|
_transactions[ i ].addr = 0;
|
||||||
|
_transactions[ i ].flags = 0;
|
||||||
|
_transactions[ i ].rxlength = 0;
|
||||||
|
_transactions[ i ].rx_buffer = nullptr;
|
||||||
|
}
|
||||||
|
// LED Data
|
||||||
|
_transactions[ 0 ].length = ( LED_FRAME_SIZE_BYTES * 8 ) * _count;
|
||||||
|
_transactions[ 0 ].tx_buffer = _buffer;
|
||||||
|
spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY );
|
||||||
|
_transCount++;
|
||||||
|
|
||||||
|
// 'latch'/start-of-data marker frames
|
||||||
|
for ( int i = 0; i < _latchFrames; i++ ) {
|
||||||
|
_transactions[ _transCount ].length = ( LATCH_FRAME_SIZE_BYTES * 8 );
|
||||||
|
_transactions[ _transCount ].tx_buffer = _latchBuffer;
|
||||||
|
spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY );
|
||||||
|
_transCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_device_handle_t _spi;
|
||||||
|
int _count;
|
||||||
|
std::unique_ptr< LDP8806_GRB[] > _firstBuffer, _secondBuffer;
|
||||||
|
LDP8806_GRB *_buffer;
|
||||||
|
|
||||||
|
spi_transaction_t _transactions[ TRANS_COUNT_MAX ];
|
||||||
|
int _transCount;
|
||||||
|
|
||||||
|
int _latchFrames;
|
||||||
|
uint8_t _latchBuffer[ LATCH_FRAME_SIZE_BYTES ];
|
||||||
|
};
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -6,22 +7,453 @@
|
|||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
#include "esp_event.h"
|
#include "esp_event.h"
|
||||||
|
|
||||||
|
#include "server_tflite.h"
|
||||||
|
|
||||||
|
//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "driver/gpio.h"
|
|
||||||
//#include "errno.h"
|
//#include "errno.h"
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <vector>
|
||||||
|
//#include <regex>
|
||||||
|
|
||||||
|
#include "defines.h"
|
||||||
|
|
||||||
#include "server_GPIO.h"
|
#include "server_GPIO.h"
|
||||||
|
|
||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
|
#include "configFile.h"
|
||||||
#include "Helper.h"
|
#include "Helper.h"
|
||||||
|
#include "interface_mqtt.h"
|
||||||
|
|
||||||
// #define DEBUG_DETAIL_ON
|
static const char *TAG_SERVERGPIO = "server_GPIO";
|
||||||
|
QueueHandle_t gpio_queue_handle = NULL;
|
||||||
|
|
||||||
esp_err_t handler_switch_GPIO(httpd_req_t *req)
|
#define DEBUG_DETAIL_ON
|
||||||
|
|
||||||
|
GpioPin::GpioPin(gpio_num_t gpio, const char* name, gpio_pin_mode_t mode, gpio_int_type_t interruptType, uint8_t dutyResolution, std::string mqttTopic, bool httpEnable)
|
||||||
{
|
{
|
||||||
|
_gpio = gpio;
|
||||||
|
_name = name;
|
||||||
|
_mode = mode;
|
||||||
|
_interruptType = interruptType;
|
||||||
|
_mqttTopic = mqttTopic;
|
||||||
|
}
|
||||||
|
|
||||||
|
GpioPin::~GpioPin()
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG_SERVERGPIO,"reset GPIO pin %d", _gpio);
|
||||||
|
if (_interruptType != GPIO_INTR_DISABLE) {
|
||||||
|
//hook isr handler for specific gpio pin
|
||||||
|
gpio_isr_handler_remove(_gpio);
|
||||||
|
}
|
||||||
|
gpio_reset_pin(_gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void IRAM_ATTR gpio_isr_handler(void* arg)
|
||||||
|
{
|
||||||
|
GpioResult gpioResult;
|
||||||
|
gpioResult.gpio = *(gpio_num_t*) arg;
|
||||||
|
gpioResult.value = gpio_get_level(gpioResult.gpio);
|
||||||
|
BaseType_t ContextSwitchRequest = pdFALSE;
|
||||||
|
|
||||||
|
xQueueSendToBackFromISR(gpio_queue_handle,(void*)&gpioResult,&ContextSwitchRequest);
|
||||||
|
|
||||||
|
if(ContextSwitchRequest){
|
||||||
|
taskYIELD();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gpioHandlerTask(void *arg) {
|
||||||
|
ESP_LOGD(TAG_SERVERGPIO,"start interrupt task");
|
||||||
|
while(1){
|
||||||
|
if(uxQueueMessagesWaiting(gpio_queue_handle)){
|
||||||
|
while(uxQueueMessagesWaiting(gpio_queue_handle)){
|
||||||
|
GpioResult gpioResult;
|
||||||
|
xQueueReceive(gpio_queue_handle,(void*)&gpioResult,10);
|
||||||
|
ESP_LOGD(TAG_SERVERGPIO,"gpio: %d state: %d", gpioResult.gpio, gpioResult.value);
|
||||||
|
((GpioHandler*)arg)->gpioInterrupt(&gpioResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
((GpioHandler*)arg)->taskHandler();
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpioPin::gpioInterrupt(int value) {
|
||||||
|
if (_mqttTopic != "") {
|
||||||
|
ESP_LOGD(TAG_SERVERGPIO, "gpioInterrupt %s %d", _mqttTopic.c_str(), value);
|
||||||
|
|
||||||
|
MQTTPublish(_mqttTopic, value ? "true" : "false");
|
||||||
|
currentState = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpioPin::init()
|
||||||
|
{
|
||||||
|
gpio_config_t io_conf;
|
||||||
|
//set interrupt
|
||||||
|
io_conf.intr_type = _interruptType;
|
||||||
|
//set as output mode
|
||||||
|
io_conf.mode = (_mode == GPIO_PIN_MODE_OUTPUT) || (_mode == GPIO_PIN_MODE_BUILT_IN_FLASH_LED) ? gpio_mode_t::GPIO_MODE_OUTPUT : gpio_mode_t::GPIO_MODE_INPUT;
|
||||||
|
//bit mask of the pins that you want to set,e.g.GPIO18/19
|
||||||
|
io_conf.pin_bit_mask = (1ULL << _gpio);
|
||||||
|
//set pull-down mode
|
||||||
|
io_conf.pull_down_en = _mode == GPIO_PIN_MODE_INPUT_PULLDOWN ? gpio_pulldown_t::GPIO_PULLDOWN_ENABLE : gpio_pulldown_t::GPIO_PULLDOWN_DISABLE;
|
||||||
|
//set pull-up mode
|
||||||
|
io_conf.pull_up_en = _mode == GPIO_PIN_MODE_INPUT_PULLDOWN ? gpio_pullup_t::GPIO_PULLUP_ENABLE : gpio_pullup_t::GPIO_PULLUP_DISABLE;
|
||||||
|
//configure GPIO with the given settings
|
||||||
|
gpio_config(&io_conf);
|
||||||
|
|
||||||
|
// if (_interruptType != GPIO_INTR_DISABLE) { // ohne GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X, wenn das genutzt wird, dann soll auch der Handler hier nicht initialisiert werden, da das dann über SmartLED erfolgt.
|
||||||
|
if ((_interruptType != GPIO_INTR_DISABLE) && (_interruptType != GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)) {
|
||||||
|
//hook isr handler for specific gpio pin
|
||||||
|
ESP_LOGD(TAG_SERVERGPIO, "GpioPin::init add isr handler for GPIO %d\r\n", _gpio);
|
||||||
|
gpio_isr_handler_add(_gpio, gpio_isr_handler, (void*)&_gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_mqttTopic != "") && ((_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GpioPin::getValue(std::string* errorText)
|
||||||
|
{
|
||||||
|
if ((_mode != GPIO_PIN_MODE_INPUT) && (_mode != GPIO_PIN_MODE_INPUT_PULLUP) && (_mode != GPIO_PIN_MODE_INPUT_PULLDOWN)) {
|
||||||
|
(*errorText) = "GPIO is not in input mode";
|
||||||
|
}
|
||||||
|
|
||||||
|
return gpio_get_level(_gpio) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpioPin::setValue(bool value, gpio_set_source setSource, std::string* errorText)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG_SERVERGPIO, "GpioPin::setValue %d\r\n", value);
|
||||||
|
|
||||||
|
if ((_mode != GPIO_PIN_MODE_OUTPUT) && (_mode != GPIO_PIN_MODE_OUTPUT_PWM) && (_mode != GPIO_PIN_MODE_BUILT_IN_FLASH_LED)) {
|
||||||
|
(*errorText) = "GPIO is not in output mode";
|
||||||
|
} else {
|
||||||
|
gpio_set_level(_gpio, value);
|
||||||
|
|
||||||
|
if ((_mqttTopic != "") && (setSource != GPIO_SET_SOURCE_MQTT)) {
|
||||||
|
MQTTPublish(_mqttTopic, value ? "true" : "false");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpioPin::publishState() {
|
||||||
|
int newState = gpio_get_level(_gpio);
|
||||||
|
if (newState != currentState) {
|
||||||
|
ESP_LOGD(TAG_SERVERGPIO,"publish state of GPIO %d new state %d", _gpio, newState);
|
||||||
|
MQTTPublish(_mqttTopic, newState ? "true" : "false");
|
||||||
|
currentState = newState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GpioPin::handleMQTT(std::string, char* data, int data_len) {
|
||||||
|
ESP_LOGD(TAG_SERVERGPIO, "GpioPin::handleMQTT data %.*s\r\n", data_len, data);
|
||||||
|
|
||||||
|
std::string dataStr(data, data_len);
|
||||||
|
dataStr = toLower(dataStr);
|
||||||
|
std::string errorText = "";
|
||||||
|
if ((dataStr == "true") || (dataStr == "1")) {
|
||||||
|
setValue(true, GPIO_SET_SOURCE_MQTT, &errorText);
|
||||||
|
} else if ((dataStr == "false") || (dataStr == "0")) {
|
||||||
|
setValue(false, GPIO_SET_SOURCE_MQTT, &errorText);
|
||||||
|
} else {
|
||||||
|
errorText = "wrong value ";
|
||||||
|
errorText.append(data, data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorText != "") {
|
||||||
|
ESP_LOGE(TAG_SERVERGPIO, "%s", errorText.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (errorText == "");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t callHandleHttpRequest(httpd_req_t *req)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG_SERVERGPIO,"callHandleHttpRequest");
|
||||||
|
|
||||||
|
GpioHandler *gpioHandler = (GpioHandler*)req->user_ctx;
|
||||||
|
return gpioHandler->handleHttpRequest(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
void taskGpioHandler(void *pvParameter)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG_SERVERGPIO,"taskGpioHandler");
|
||||||
|
((GpioHandler*)pvParameter)->init();
|
||||||
|
}
|
||||||
|
|
||||||
|
GpioHandler::GpioHandler(std::string configFile, httpd_handle_t httpServer)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG_SERVERGPIO,"start GpioHandler");
|
||||||
|
_configFile = configFile;
|
||||||
|
_httpServer = httpServer;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG_SERVERGPIO, "register GPIO Uri");
|
||||||
|
registerGpioUri();
|
||||||
|
}
|
||||||
|
|
||||||
|
GpioHandler::~GpioHandler() {
|
||||||
|
if (gpioMap != NULL) {
|
||||||
|
clear();
|
||||||
|
delete gpioMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpioHandler::init()
|
||||||
|
{
|
||||||
|
// TickType_t xDelay = 60000 / portTICK_PERIOD_MS;
|
||||||
|
// printf("wait before start %ldms\r\n", (long) xDelay);
|
||||||
|
// vTaskDelay( xDelay );
|
||||||
|
|
||||||
|
printf("*************** Start GPIOHandler_Init *****************\n");
|
||||||
|
|
||||||
|
if (gpioMap == NULL) {
|
||||||
|
gpioMap = new std::map<gpio_num_t, GpioPin*>();
|
||||||
|
} else {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ESP_LOGI(TAG_SERVERGPIO, "read GPIO config and init GPIO");
|
||||||
|
if (!readConfig()) {
|
||||||
|
clear();
|
||||||
|
delete gpioMap;
|
||||||
|
gpioMap = NULL;
|
||||||
|
ESP_LOGI(TAG_SERVERGPIO, "GPIO init comleted, handler is disabled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for(std::map<gpio_num_t, GpioPin*>::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) {
|
||||||
|
it->second->init();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::function<void()> f = std::bind(&GpioHandler::handleMQTTconnect, this);
|
||||||
|
MQTTregisterConnectFunction("gpio-handler", f);
|
||||||
|
|
||||||
|
if (xHandleTaskGpio == NULL) {
|
||||||
|
gpio_queue_handle = xQueueCreate(10,sizeof(GpioResult));
|
||||||
|
BaseType_t xReturned = xTaskCreate(&gpioHandlerTask, "gpio_int", configMINIMAL_STACK_SIZE * 8, (void *)this, tskIDLE_PRIORITY + 2, &xHandleTaskGpio);
|
||||||
|
if(xReturned == pdPASS ) {
|
||||||
|
ESP_LOGD(TAG_SERVERGPIO, "xHandletaskGpioHandler started");
|
||||||
|
} else {
|
||||||
|
ESP_LOGD(TAG_SERVERGPIO, "xHandletaskGpioHandler not started %d ", (int)xHandleTaskGpio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG_SERVERGPIO, "GPIO init comleted, is enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpioHandler::taskHandler() {
|
||||||
|
if (gpioMap != NULL) {
|
||||||
|
for(std::map<gpio_num_t, GpioPin*>::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) {
|
||||||
|
if ((it->second->getInterruptType() == GPIO_INTR_DISABLE))
|
||||||
|
it->second->publishState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void GpioHandler::handleMQTTconnect()
|
||||||
|
{
|
||||||
|
if (gpioMap != NULL) {
|
||||||
|
for(std::map<gpio_num_t, GpioPin*>::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) {
|
||||||
|
if ((it->second->getMode() == GPIO_PIN_MODE_INPUT) || (it->second->getMode() == GPIO_PIN_MODE_INPUT_PULLDOWN) || (it->second->getMode() == GPIO_PIN_MODE_INPUT_PULLUP))
|
||||||
|
it->second->publishState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpioHandler::deinit() {
|
||||||
|
MQTTunregisterConnectFunction("gpio-handler");
|
||||||
|
clear();
|
||||||
|
if (xHandleTaskGpio != NULL) {
|
||||||
|
vTaskDelete(xHandleTaskGpio);
|
||||||
|
xHandleTaskGpio = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpioHandler::gpioInterrupt(GpioResult* gpioResult) {
|
||||||
|
if ((gpioMap != NULL) && (gpioMap->find(gpioResult->gpio) != gpioMap->end())) {
|
||||||
|
(*gpioMap)[gpioResult->gpio]->gpioInterrupt(gpioResult->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GpioHandler::readConfig()
|
||||||
|
{
|
||||||
|
if (!gpioMap->empty())
|
||||||
|
clear();
|
||||||
|
|
||||||
|
ConfigFile configFile = ConfigFile(_configFile);
|
||||||
|
|
||||||
|
std::vector<std::string> zerlegt;
|
||||||
|
std::string line = "";
|
||||||
|
bool disabledLine = false;
|
||||||
|
bool eof = false;
|
||||||
|
gpio_num_t gpioExtLED = (gpio_num_t) 0;
|
||||||
|
|
||||||
|
// printf("readConfig - Start 1\n");
|
||||||
|
|
||||||
|
while ((!configFile.GetNextParagraph(line, disabledLine, eof) || (line.compare("[GPIO]") != 0)) && !eof) {}
|
||||||
|
if (eof)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// printf("readConfig - Start 2 line: %s, disabbledLine: %d\n", line.c_str(), (int) disabledLine);
|
||||||
|
|
||||||
|
|
||||||
|
_isEnabled = !disabledLine;
|
||||||
|
|
||||||
|
if (!_isEnabled)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// printf("readConfig - Start 3\n");
|
||||||
|
|
||||||
|
// std::string mainTopicMQTT = "";
|
||||||
|
std::string mainTopicMQTT = GetMQTTMainTopic();
|
||||||
|
if (mainTopicMQTT.length() > 0)
|
||||||
|
{
|
||||||
|
mainTopicMQTT = mainTopicMQTT + "/GPIO";
|
||||||
|
ESP_LOGD(TAG_SERVERGPIO, "MAINTOPICMQTT found\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool registerISR = false;
|
||||||
|
while (configFile.getNextLine(&line, disabledLine, eof) && !configFile.isNewParagraph(line))
|
||||||
|
{
|
||||||
|
zerlegt = configFile.ZerlegeZeile(line);
|
||||||
|
// const std::regex pieces_regex("IO([0-9]{1,2})");
|
||||||
|
// std::smatch pieces_match;
|
||||||
|
// if (std::regex_match(zerlegt[0], pieces_match, pieces_regex) && (pieces_match.size() == 2))
|
||||||
|
// {
|
||||||
|
// std::string gpioStr = pieces_match[1];
|
||||||
|
ESP_LOGD(TAG_SERVERGPIO, "conf param %s\r\n", toUpper(zerlegt[0]).c_str());
|
||||||
|
if (toUpper(zerlegt[0]) == "MAINTOPICMQTT") {
|
||||||
|
// ESP_LOGD(TAG_SERVERGPIO, "MAINTOPICMQTT found\r\n");
|
||||||
|
// mainTopicMQTT = zerlegt[1];
|
||||||
|
} else if ((zerlegt[0].rfind("IO", 0) == 0) && (zerlegt.size() >= 6))
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG_SERVERGPIO,"Enable GP%s in %s mode", zerlegt[0].c_str(), zerlegt[1].c_str());
|
||||||
|
std::string gpioStr = zerlegt[0].substr(2, 2);
|
||||||
|
gpio_num_t gpioNr = (gpio_num_t)atoi(gpioStr.c_str());
|
||||||
|
gpio_pin_mode_t pinMode = resolvePinMode(toLower(zerlegt[1]));
|
||||||
|
gpio_int_type_t intType = resolveIntType(toLower(zerlegt[2]));
|
||||||
|
uint16_t dutyResolution = (uint8_t)atoi(zerlegt[3].c_str());
|
||||||
|
bool mqttEnabled = toLower(zerlegt[4]) == "true";
|
||||||
|
bool httpEnabled = toLower(zerlegt[5]) == "true";
|
||||||
|
char gpioName[100];
|
||||||
|
if (zerlegt.size() >= 7) {
|
||||||
|
strcpy(gpioName, trim(zerlegt[6]).c_str());
|
||||||
|
} else {
|
||||||
|
sprintf(gpioName, "GPIO%d", gpioNr);
|
||||||
|
}
|
||||||
|
std::string mqttTopic = mqttEnabled ? (mainTopicMQTT + "/" + gpioName) : "";
|
||||||
|
GpioPin* gpioPin = new GpioPin(gpioNr, gpioName, pinMode, intType,dutyResolution, mqttTopic, httpEnabled);
|
||||||
|
(*gpioMap)[gpioNr] = gpioPin;
|
||||||
|
|
||||||
|
if (pinMode == GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)
|
||||||
|
{
|
||||||
|
printf("Set WS2812 to GPIO %d\n", gpioNr);
|
||||||
|
gpioExtLED = gpioNr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intType != GPIO_INTR_DISABLE) {
|
||||||
|
registerISR = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (toUpper(zerlegt[0]) == "LEDNUMBERS")
|
||||||
|
{
|
||||||
|
LEDNumbers = stoi(zerlegt[1]);
|
||||||
|
}
|
||||||
|
if (toUpper(zerlegt[0]) == "LEDCOLOR")
|
||||||
|
{
|
||||||
|
uint8_t _r, _g, _b;
|
||||||
|
_r = stoi(zerlegt[1]);
|
||||||
|
_g = stoi(zerlegt[2]);
|
||||||
|
_b = stoi(zerlegt[3]);
|
||||||
|
|
||||||
|
LEDColor = Rgb{_r, _g, _b};
|
||||||
|
}
|
||||||
|
if (toUpper(zerlegt[0]) == "LEDTYPE")
|
||||||
|
{
|
||||||
|
if (zerlegt[1] == "WS2812")
|
||||||
|
LEDType = LED_WS2812;
|
||||||
|
if (zerlegt[1] == "WS2812B")
|
||||||
|
LEDType = LED_WS2812B;
|
||||||
|
if (zerlegt[1] == "SK6812")
|
||||||
|
LEDType = LED_SK6812;
|
||||||
|
if (zerlegt[1] == "WS2813")
|
||||||
|
LEDType = LED_WS2813;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (registerISR) {
|
||||||
|
//install gpio isr service
|
||||||
|
gpio_install_isr_service(ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gpioExtLED > 0)
|
||||||
|
{
|
||||||
|
// LogFile.WriteToFile("Startsequence 06"); // Nremove
|
||||||
|
// vTaskDelay( xDelay );
|
||||||
|
// xDelay = 5000 / portTICK_PERIOD_MS;
|
||||||
|
// printf("main: sleep for : %ldms\n", (long) xDelay);
|
||||||
|
|
||||||
|
// SmartLed leds( LED_WS2812, 2, GPIO_NUM_12, 0, DoubleBuffer );
|
||||||
|
|
||||||
|
|
||||||
|
// leds[ 0 ] = Rgb{ 255, 0, 0 };
|
||||||
|
// leds[ 1 ] = Rgb{ 255, 255, 255 };
|
||||||
|
// leds.show();
|
||||||
|
// SmartLed leds = new SmartLed(LEDType, LEDNumbers, gpioExtLED, 0, DoubleBuffer);
|
||||||
|
// _SmartLED = new SmartLed( LED_WS2812, 2, GPIO_NUM_12, 0, DoubleBuffer );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpioHandler::clear()
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG_SERVERGPIO, "GpioHandler::clear\r\n");
|
||||||
|
|
||||||
|
if (gpioMap != NULL) {
|
||||||
|
for(std::map<gpio_num_t, GpioPin*>::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) {
|
||||||
|
delete it->second;
|
||||||
|
}
|
||||||
|
gpioMap->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// gpio_uninstall_isr_service(); can't uninstall, isr service is used by camera
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpioHandler::registerGpioUri()
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG_SERVERGPIO, "server_GPIO - Registering URI handlers");
|
||||||
|
|
||||||
|
httpd_uri_t camuri = { };
|
||||||
|
camuri.method = HTTP_GET;
|
||||||
|
camuri.uri = "/GPIO";
|
||||||
|
camuri.handler = callHandleHttpRequest;
|
||||||
|
camuri.user_ctx = (void*)this;
|
||||||
|
httpd_register_uri_handler(_httpServer, &camuri);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t GpioHandler::handleHttpRequest(httpd_req_t *req)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG_SERVERGPIO, "handleHttpRequest");
|
||||||
|
|
||||||
|
if (gpioMap == NULL) {
|
||||||
|
std::string resp_str = "GPIO handler not initialized";
|
||||||
|
httpd_resp_send(req, resp_str.c_str(), resp_str.length());
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
LogFile.WriteHeapInfo("handler_switch_GPIO - Start");
|
LogFile.WriteHeapInfo("handler_switch_GPIO - Start");
|
||||||
#endif
|
#endif
|
||||||
@@ -30,95 +462,223 @@ esp_err_t handler_switch_GPIO(httpd_req_t *req)
|
|||||||
char _query[200];
|
char _query[200];
|
||||||
char _valueGPIO[30];
|
char _valueGPIO[30];
|
||||||
char _valueStatus[30];
|
char _valueStatus[30];
|
||||||
std::string gpio, status, zw;
|
std::string gpio, status;
|
||||||
int gpionum = 0;
|
|
||||||
gpio_num_t gpio_num;
|
|
||||||
|
|
||||||
if (httpd_req_get_url_query_str(req, _query, 200) == ESP_OK)
|
if (httpd_req_get_url_query_str(req, _query, 200) == ESP_OK) {
|
||||||
{
|
ESP_LOGD(TAG_SERVERGPIO, "Query: %s", _query);
|
||||||
printf("Query: "); printf(_query); printf("\n");
|
|
||||||
|
|
||||||
if (httpd_query_key_value(_query, "GPIO", _valueGPIO, 30) == ESP_OK)
|
if (httpd_query_key_value(_query, "GPIO", _valueGPIO, 30) == ESP_OK)
|
||||||
{
|
{
|
||||||
printf("GPIO is found"); printf(_valueGPIO); printf("\n");
|
ESP_LOGD(TAG_SERVERGPIO, "GPIO is found %s", _valueGPIO);
|
||||||
gpio = std::string(_valueGPIO);
|
gpio = std::string(_valueGPIO);
|
||||||
|
} else {
|
||||||
|
std::string resp_str = "GPIO No is not defined";
|
||||||
|
httpd_resp_send(req, resp_str.c_str(), resp_str.length());
|
||||||
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
if (httpd_query_key_value(_query, "Status", _valueStatus, 30) == ESP_OK)
|
if (httpd_query_key_value(_query, "Status", _valueStatus, 30) == ESP_OK)
|
||||||
{
|
{
|
||||||
printf("Status is found"); printf(_valueStatus); printf("\n");
|
ESP_LOGD(TAG_SERVERGPIO, "Status is found %s", _valueStatus);
|
||||||
status = std::string(_valueStatus);
|
status = std::string(_valueStatus);
|
||||||
}
|
}
|
||||||
};
|
} else {
|
||||||
|
const char* resp_str = "Error in call. Use /GPIO?GPIO=12&Status=high";
|
||||||
|
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
status = toUpper(status);
|
status = toUpper(status);
|
||||||
if (!(status == "HIGH") && !(status == "LOW"))
|
if ((status != "HIGH") && (status != "LOW") && (status != "TRUE") && (status != "FALSE") && (status != "0") && (status != "1") && (status != ""))
|
||||||
{
|
{
|
||||||
zw = "Status not valid: " + status;;
|
std::string zw = "Status not valid: " + status;
|
||||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||||
httpd_resp_sendstr_chunk(req, NULL);
|
httpd_resp_sendstr_chunk(req, NULL);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpionum = stoi(gpio);
|
int gpionum = stoi(gpio);
|
||||||
|
|
||||||
// frei: 16; 12-15; 2; 4 // nur 12 und 13 funktionieren 2: reboot, 4: BlitzLED, 14/15: DMA für SDKarte ???
|
|
||||||
|
|
||||||
switch (gpionum) {
|
// frei: 16; 12-15; 2; 4 // nur 12 und 13 funktionieren 2: reboot, 4: BlitzLED, 15: PSRAM, 14/15: DMA für SDKarte ???
|
||||||
case 12:
|
gpio_num_t gpio_num = resolvePinNr(gpionum);
|
||||||
gpio_num = GPIO_NUM_12;
|
if (gpio_num == GPIO_NUM_NC)
|
||||||
break;
|
{
|
||||||
case 13:
|
std::string zw = "GPIO" + std::to_string(gpionum) + " not support - only 12 & 13 free";
|
||||||
gpio_num = GPIO_NUM_13;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
zw = "GPIO" + std::to_string(gpionum) + " not support - only 12 & 13 free";
|
|
||||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||||
httpd_resp_sendstr_chunk(req, NULL);
|
httpd_resp_sendstr_chunk(req, NULL);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == "HIGH")
|
if (gpioMap->count(gpio_num) == 0) {
|
||||||
gpio_set_level(gpio_num, 1);
|
char resp_str [30];
|
||||||
|
sprintf(resp_str, "GPIO%d is not registred", gpio_num);
|
||||||
|
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == "")
|
||||||
|
{
|
||||||
|
std::string resp_str = "";
|
||||||
|
status = (*gpioMap)[gpio_num]->getValue(&resp_str) ? "HIGH" : "LOW";
|
||||||
|
if (resp_str == "") {
|
||||||
|
resp_str = status;
|
||||||
|
}
|
||||||
|
httpd_resp_sendstr_chunk(req, resp_str.c_str());
|
||||||
|
httpd_resp_sendstr_chunk(req, NULL);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
gpio_set_level(gpio_num, 0);
|
{
|
||||||
|
std::string resp_str = "";
|
||||||
|
(*gpioMap)[gpio_num]->setValue((status == "HIGH") || (status == "TRUE") || (status == "1"), GPIO_SET_SOURCE_HTTP, &resp_str);
|
||||||
zw = "GPIO" + std::to_string(gpionum) + " switched to " + status;
|
if (resp_str == "") {
|
||||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
resp_str = "GPIO" + std::to_string(gpionum) + " switched to " + status;
|
||||||
httpd_resp_sendstr_chunk(req, NULL);
|
}
|
||||||
|
httpd_resp_sendstr_chunk(req, resp_str.c_str());
|
||||||
|
httpd_resp_sendstr_chunk(req, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
};
|
};
|
||||||
|
|
||||||
void initGPIO()
|
void GpioHandler::flashLightEnable(bool value)
|
||||||
{
|
{
|
||||||
gpio_config_t io_conf;
|
ESP_LOGD(TAG_SERVERGPIO, "GpioHandler::flashLightEnable %s\r\n", value ? "true" : "false");
|
||||||
//disable interrupt
|
|
||||||
io_conf.intr_type = GPIO_INTR_DISABLE;
|
if (gpioMap != NULL) {
|
||||||
//set as output mode
|
for(std::map<gpio_num_t, GpioPin*>::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it)
|
||||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
{
|
||||||
//bit mask of the pins that you want to set,e.g.GPIO18/19
|
if (it->second->getMode() == GPIO_PIN_MODE_BUILT_IN_FLASH_LED) //|| (it->second->getMode() == GPIO_PIN_MODE_EXTERNAL_FLASH_PWM) || (it->second->getMode() == GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X))
|
||||||
// io_conf.pin_bit_mask = ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1));
|
{
|
||||||
// io_conf.pin_bit_mask = ((1ULL << GPIO_NUM_12) | (1ULL << GPIO_NUM_2) | (1ULL << GPIO_NUM_4) | (1ULL << GPIO_NUM_12) | (1ULL << GPIO_NUM_13) | (1ULL << GPIO_NUM_14) | (1ULL << GPIO_NUM_15));
|
std::string resp_str = "";
|
||||||
io_conf.pin_bit_mask = ((1ULL << GPIO_NUM_12) | (1ULL << GPIO_NUM_13));
|
it->second->setValue(value, GPIO_SET_SOURCE_INTERNAL, &resp_str);
|
||||||
//disable pull-down mode
|
|
||||||
io_conf.pull_down_en = (gpio_pulldown_t) 0;
|
if (resp_str == "") {
|
||||||
//disable pull-up mode
|
ESP_LOGD(TAG_SERVERGPIO, "Flash light pin GPIO %d switched to %s\r\n", (int)it->first, (value ? "on" : "off"));
|
||||||
io_conf.pull_up_en = (gpio_pullup_t) 0;
|
} else {
|
||||||
//configure GPIO with the given settings
|
ESP_LOGE(TAG_SERVERGPIO, "Can't set flash light pin GPIO %d. Error: %s\r\n", (int)it->first, resp_str.c_str());
|
||||||
gpio_config(&io_conf);
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
if (it->second->getMode() == GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)
|
||||||
|
{
|
||||||
|
#ifdef __LEDGLOBAL
|
||||||
|
if (leds_global == NULL) {
|
||||||
|
ESP_LOGI(TAG_SERVERGPIO, "init SmartLed: LEDNumber=%d, GPIO=%d", LEDNumbers, (int)it->second->getGPIO());
|
||||||
|
leds_global = new SmartLed( LEDType, LEDNumbers, it->second->getGPIO(), 0, DoubleBuffer );
|
||||||
|
} else {
|
||||||
|
// wait until we can update: https://github.com/RoboticsBrno/SmartLeds/issues/10#issuecomment-386921623
|
||||||
|
leds_global->wait();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
SmartLed leds( LEDType, LEDNumbers, it->second->getGPIO(), 0, DoubleBuffer );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < LEDNumbers; ++i)
|
||||||
|
#ifdef __LEDGLOBAL
|
||||||
|
(*leds_global)[i] = LEDColor;
|
||||||
|
#else
|
||||||
|
leds[i] = LEDColor;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < LEDNumbers; ++i)
|
||||||
|
#ifdef __LEDGLOBAL
|
||||||
|
(*leds_global)[i] = Rgb{0, 0, 0};
|
||||||
|
#else
|
||||||
|
leds[i] = Rgb{0, 0, 0};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#ifdef __LEDGLOBAL
|
||||||
|
leds_global->show();
|
||||||
|
#else
|
||||||
|
leds.show();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gpio_num_t GpioHandler::resolvePinNr(uint8_t pinNr)
|
||||||
void register_server_GPIO_uri(httpd_handle_t server)
|
|
||||||
{
|
{
|
||||||
ESP_LOGI(TAGPARTGPIO, "server_GPIO - Registering URI handlers");
|
switch(pinNr) {
|
||||||
|
case 0:
|
||||||
httpd_uri_t camuri = { };
|
return GPIO_NUM_0;
|
||||||
camuri.method = HTTP_GET;
|
case 1:
|
||||||
camuri.uri = "/GPIO";
|
return GPIO_NUM_1;
|
||||||
camuri.handler = handler_switch_GPIO;
|
case 3:
|
||||||
camuri.user_ctx = (void*) "switch GPIO";
|
return GPIO_NUM_3;
|
||||||
httpd_register_uri_handler(server, &camuri);
|
case 4:
|
||||||
|
return GPIO_NUM_4;
|
||||||
initGPIO();
|
case 12:
|
||||||
|
return GPIO_NUM_12;
|
||||||
|
case 13:
|
||||||
|
return GPIO_NUM_13;
|
||||||
|
default:
|
||||||
|
return GPIO_NUM_NC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio_pin_mode_t GpioHandler::resolvePinMode(std::string input)
|
||||||
|
{
|
||||||
|
if( input == "disabled" ) return GPIO_PIN_MODE_DISABLED;
|
||||||
|
if( input == "input" ) return GPIO_PIN_MODE_INPUT;
|
||||||
|
if( input == "input-pullup" ) return GPIO_PIN_MODE_INPUT_PULLUP;
|
||||||
|
if( input == "input-pulldown" ) return GPIO_PIN_MODE_INPUT_PULLDOWN;
|
||||||
|
if( input == "output" ) return GPIO_PIN_MODE_OUTPUT;
|
||||||
|
if( input == "built-in-led" ) return GPIO_PIN_MODE_BUILT_IN_FLASH_LED;
|
||||||
|
if( input == "output-pwm" ) return GPIO_PIN_MODE_OUTPUT_PWM;
|
||||||
|
if( input == "external-flash-pwm" ) return GPIO_PIN_MODE_EXTERNAL_FLASH_PWM;
|
||||||
|
if( input == "external-flash-ws281x" ) return GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X;
|
||||||
|
|
||||||
|
return GPIO_PIN_MODE_DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio_int_type_t GpioHandler::resolveIntType(std::string input)
|
||||||
|
{
|
||||||
|
if( input == "disabled" ) return GPIO_INTR_DISABLE;
|
||||||
|
if( input == "rising-edge" ) return GPIO_INTR_POSEDGE;
|
||||||
|
if( input == "falling-edge" ) return GPIO_INTR_NEGEDGE;
|
||||||
|
if( input == "rising-and-falling" ) return GPIO_INTR_ANYEDGE ;
|
||||||
|
if( input == "low-level-trigger" ) return GPIO_INTR_LOW_LEVEL;
|
||||||
|
if( input == "high-level-trigger" ) return GPIO_INTR_HIGH_LEVEL;
|
||||||
|
|
||||||
|
|
||||||
|
return GPIO_INTR_DISABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GpioHandler *gpioHandler = NULL;
|
||||||
|
|
||||||
|
void gpio_handler_create(httpd_handle_t server)
|
||||||
|
{
|
||||||
|
if (gpioHandler == NULL)
|
||||||
|
gpioHandler = new GpioHandler(CONFIG_FILE, server);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gpio_handler_init()
|
||||||
|
{
|
||||||
|
if (gpioHandler != NULL) {
|
||||||
|
gpioHandler->init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gpio_handler_deinit() {
|
||||||
|
if (gpioHandler != NULL) {
|
||||||
|
gpioHandler->deinit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gpio_handler_destroy()
|
||||||
|
{
|
||||||
|
if (gpioHandler != NULL) {
|
||||||
|
delete gpioHandler;
|
||||||
|
gpioHandler = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GpioHandler* gpio_handler_get()
|
||||||
|
{
|
||||||
|
return gpioHandler;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,112 @@
|
|||||||
|
#ifndef SERVER_GPIO_H
|
||||||
|
#define SERVER_GPIO_H
|
||||||
|
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
|
|
||||||
#include <esp_http_server.h>
|
#include <esp_http_server.h>
|
||||||
|
#include <map>
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
|
||||||
|
#include "SmartLeds.h"
|
||||||
|
|
||||||
//#include "ClassControllCamera.h"
|
//#include "ClassControllCamera.h"
|
||||||
|
|
||||||
static const char *TAGPARTGPIO = "server_GPIO";
|
// wenn __LEDGLOBAL definiert ist, wird eine globale Variable für die LED-Ansteuerung verwendet, ansonsten lokal und jedesmal neu
|
||||||
|
#define __LEDGLOBAL
|
||||||
|
|
||||||
void register_server_GPIO_uri(httpd_handle_t server);
|
typedef enum {
|
||||||
|
GPIO_PIN_MODE_DISABLED = 0x0,
|
||||||
|
GPIO_PIN_MODE_INPUT = 0x1,
|
||||||
|
GPIO_PIN_MODE_INPUT_PULLUP = 0x2,
|
||||||
|
GPIO_PIN_MODE_INPUT_PULLDOWN = 0x3,
|
||||||
|
GPIO_PIN_MODE_OUTPUT = 0x4,
|
||||||
|
GPIO_PIN_MODE_BUILT_IN_FLASH_LED = 0x5,
|
||||||
|
GPIO_PIN_MODE_OUTPUT_PWM = 0x6,
|
||||||
|
GPIO_PIN_MODE_EXTERNAL_FLASH_PWM = 0x7,
|
||||||
|
GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X = 0x8,
|
||||||
|
} gpio_pin_mode_t;
|
||||||
|
|
||||||
|
struct GpioResult {
|
||||||
|
gpio_num_t gpio;
|
||||||
|
int value;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GPIO_SET_SOURCE_INTERNAL = 0,
|
||||||
|
GPIO_SET_SOURCE_MQTT = 1,
|
||||||
|
GPIO_SET_SOURCE_HTTP = 2,
|
||||||
|
} gpio_set_source;
|
||||||
|
|
||||||
|
class GpioPin {
|
||||||
|
public:
|
||||||
|
GpioPin(gpio_num_t gpio, const char* name, gpio_pin_mode_t mode, gpio_int_type_t interruptType, uint8_t dutyResolution, std::string mqttTopic, bool httpEnable);
|
||||||
|
~GpioPin();
|
||||||
|
|
||||||
|
void init();
|
||||||
|
bool getValue(std::string* errorText);
|
||||||
|
void setValue(bool value, gpio_set_source setSource, std::string* errorText);
|
||||||
|
bool handleMQTT(std::string, char* data, int data_len);
|
||||||
|
void publishState();
|
||||||
|
void gpioInterrupt(int value);
|
||||||
|
gpio_int_type_t getInterruptType() { return _interruptType; }
|
||||||
|
gpio_pin_mode_t getMode() { return _mode; }
|
||||||
|
gpio_num_t getGPIO(){return _gpio;};
|
||||||
|
|
||||||
|
private:
|
||||||
|
gpio_num_t _gpio;
|
||||||
|
const char* _name;
|
||||||
|
gpio_pin_mode_t _mode;
|
||||||
|
gpio_int_type_t _interruptType;
|
||||||
|
std::string _mqttTopic;
|
||||||
|
int currentState = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_err_t callHandleHttpRequest(httpd_req_t *req);
|
||||||
|
void taskGpioHandler(void *pvParameter);
|
||||||
|
|
||||||
|
class GpioHandler {
|
||||||
|
public:
|
||||||
|
GpioHandler(std::string configFile, httpd_handle_t httpServer);
|
||||||
|
~GpioHandler();
|
||||||
|
|
||||||
|
void init();
|
||||||
|
void deinit();
|
||||||
|
void registerGpioUri();
|
||||||
|
esp_err_t handleHttpRequest(httpd_req_t *req);
|
||||||
|
void taskHandler();
|
||||||
|
void gpioInterrupt(GpioResult* gpioResult);
|
||||||
|
void flashLightEnable(bool value);
|
||||||
|
bool isEnabled() { return _isEnabled; }
|
||||||
|
void handleMQTTconnect();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string _configFile;
|
||||||
|
httpd_handle_t _httpServer;
|
||||||
|
std::map<gpio_num_t, GpioPin*> *gpioMap = NULL;
|
||||||
|
TaskHandle_t xHandleTaskGpio = NULL;
|
||||||
|
bool _isEnabled = false;
|
||||||
|
|
||||||
|
int LEDNumbers = 2;
|
||||||
|
Rgb LEDColor = Rgb{ 255, 255, 255 };
|
||||||
|
LedType LEDType = LED_WS2812;
|
||||||
|
#ifdef __LEDGLOBAL
|
||||||
|
SmartLed *leds_global = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool readConfig();
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
gpio_num_t resolvePinNr(uint8_t pinNr);
|
||||||
|
gpio_pin_mode_t resolvePinMode(std::string input);
|
||||||
|
gpio_int_type_t resolveIntType(std::string input);
|
||||||
|
};
|
||||||
|
|
||||||
|
void gpio_handler_create(httpd_handle_t server);
|
||||||
|
void gpio_handler_init();
|
||||||
|
void gpio_handler_deinit();
|
||||||
|
void gpio_handler_destroy();
|
||||||
|
GpioHandler* gpio_handler_get();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif //SERVER_GPIO_H
|
||||||
@@ -4,6 +4,6 @@ list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/proto
|
|||||||
|
|
||||||
idf_component_register(SRCS ${app_sources}
|
idf_component_register(SRCS ${app_sources}
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
REQUIRES esp32-camera-master esp_http_server jomjol_logfile jomjol_image_proc nvs_flash)
|
REQUIRES esp32-camera-master esp_http_server jomjol_logfile jomjol_image_proc nvs_flash jomjol_fileserver_ota jomjol_controlGPIO)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,14 @@
|
|||||||
#include "Helper.h"
|
#include "Helper.h"
|
||||||
#include "CImageBasis.h"
|
#include "CImageBasis.h"
|
||||||
|
|
||||||
|
#include "server_ota.h"
|
||||||
|
#include "server_GPIO.h"
|
||||||
|
|
||||||
|
|
||||||
#define BOARD_ESP32CAM_AITHINKER
|
#define BOARD_ESP32CAM_AITHINKER
|
||||||
|
|
||||||
|
|
||||||
#include <esp_event_loop.h>
|
#include <esp_event.h>
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
#include <esp_system.h>
|
#include <esp_system.h>
|
||||||
#include <nvs_flash.h>
|
#include <nvs_flash.h>
|
||||||
@@ -48,7 +51,7 @@
|
|||||||
#define CAM_PIN_HREF 23
|
#define CAM_PIN_HREF 23
|
||||||
#define CAM_PIN_PCLK 22
|
#define CAM_PIN_PCLK 22
|
||||||
|
|
||||||
static const char *TAG = "example:take_picture";
|
static const char *TAGCAMERACLASS = "server_part_camera";
|
||||||
|
|
||||||
static camera_config_t camera_config = {
|
static camera_config_t camera_config = {
|
||||||
.pin_pwdn = CAM_PIN_PWDN,
|
.pin_pwdn = CAM_PIN_PWDN,
|
||||||
@@ -71,7 +74,7 @@ static camera_config_t camera_config = {
|
|||||||
|
|
||||||
//XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental)
|
//XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental)
|
||||||
// .xclk_freq_hz = 20000000, // Orginalwert
|
// .xclk_freq_hz = 20000000, // Orginalwert
|
||||||
.xclk_freq_hz = 5000000, // Test, um die Bildfehler los zu werden !!!!
|
.xclk_freq_hz = 5000000, // Test, um die Bildfehler los zu werden !!!!
|
||||||
.ledc_timer = LEDC_TIMER_0,
|
.ledc_timer = LEDC_TIMER_0,
|
||||||
.ledc_channel = LEDC_CHANNEL_0,
|
.ledc_channel = LEDC_CHANNEL_0,
|
||||||
|
|
||||||
@@ -83,6 +86,8 @@ static camera_config_t camera_config = {
|
|||||||
|
|
||||||
.jpeg_quality = 5, //0-63 lower number means higher quality
|
.jpeg_quality = 5, //0-63 lower number means higher quality
|
||||||
.fb_count = 1 //if more than one, i2s runs in continuous mode. Use only with JPEG
|
.fb_count = 1 //if more than one, i2s runs in continuous mode. Use only with JPEG
|
||||||
|
// .grab_mode = CAMERA_GRAB_WHEN_EMPTY,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -220,13 +225,17 @@ void CCamera::SetQualitySize(int qual, framesize_t resol)
|
|||||||
void CCamera::EnableAutoExposure(int flashdauer)
|
void CCamera::EnableAutoExposure(int flashdauer)
|
||||||
{
|
{
|
||||||
LEDOnOff(true);
|
LEDOnOff(true);
|
||||||
LightOnOff(true);
|
if (flashdauer > 0)
|
||||||
|
LightOnOff(true);
|
||||||
const TickType_t xDelay = flashdauer / portTICK_PERIOD_MS;
|
const TickType_t xDelay = flashdauer / portTICK_PERIOD_MS;
|
||||||
vTaskDelay( xDelay );
|
vTaskDelay( xDelay );
|
||||||
|
|
||||||
camera_fb_t * fb = esp_camera_fb_get();
|
camera_fb_t * fb = esp_camera_fb_get();
|
||||||
if (!fb) {
|
if (!fb) {
|
||||||
ESP_LOGE(TAGCAMERACLASS, "Camera Capture Failed");
|
ESP_LOGE(TAGCAMERACLASS, "Camera Capture Failed");
|
||||||
|
LEDOnOff(false);
|
||||||
|
LightOnOff(false);
|
||||||
|
doReboot();
|
||||||
}
|
}
|
||||||
esp_camera_fb_return(fb);
|
esp_camera_fb_return(fb);
|
||||||
|
|
||||||
@@ -269,13 +278,26 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
|
|||||||
|
|
||||||
camera_fb_t * fb = esp_camera_fb_get();
|
camera_fb_t * fb = esp_camera_fb_get();
|
||||||
if (!fb) {
|
if (!fb) {
|
||||||
ESP_LOGE(TAGCAMERACLASS, "Camera Capture Failed");
|
ESP_LOGE(TAGCAMERACLASS, "CaptureToBasisImage: Camera Capture Failed");
|
||||||
LEDOnOff(false);
|
LEDOnOff(false);
|
||||||
|
LightOnOff(false);
|
||||||
|
|
||||||
|
LogFile.SwitchOnOff(true);
|
||||||
|
LogFile.WriteToFile("Camera is not working anymore - most propably hardware problem (instablility, ...). "
|
||||||
|
"System will reboot.");
|
||||||
|
doReboot();
|
||||||
|
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int _size = fb->len;
|
int _size = fb->len;
|
||||||
zwischenspeicher = (uint8_t*) malloc(_size);
|
zwischenspeicher = (uint8_t*) malloc(_size);
|
||||||
|
if (!zwischenspeicher)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAGCAMERACLASS, "Nicht ausreichend Speicherplatz für Bild in Funktion CaptureToBasisImage()");
|
||||||
|
LogFile.SwitchOnOff(true);
|
||||||
|
LogFile.WriteToFile("Nicht ausreichend Speicherplatz für Bild in Funktion CaptureToBasisImage()");
|
||||||
|
}
|
||||||
for (int i = 0; i < _size; ++i)
|
for (int i = 0; i < _size; ++i)
|
||||||
*(zwischenspeicher + i) = *(fb->buf + i);
|
*(zwischenspeicher + i) = *(fb->buf + i);
|
||||||
esp_camera_fb_return(fb);
|
esp_camera_fb_return(fb);
|
||||||
@@ -353,8 +375,11 @@ esp_err_t CCamera::CaptureToFile(std::string nm, int delay)
|
|||||||
|
|
||||||
camera_fb_t * fb = esp_camera_fb_get();
|
camera_fb_t * fb = esp_camera_fb_get();
|
||||||
if (!fb) {
|
if (!fb) {
|
||||||
ESP_LOGE(TAGCAMERACLASS, "Camera Capture Failed");
|
ESP_LOGE(TAGCAMERACLASS, "CaptureToFile: Camera Capture Failed");
|
||||||
LEDOnOff(false);
|
LEDOnOff(false);
|
||||||
|
LightOnOff(false);
|
||||||
|
doReboot();
|
||||||
|
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
LEDOnOff(false);
|
LEDOnOff(false);
|
||||||
@@ -443,7 +468,11 @@ esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay)
|
|||||||
fb = esp_camera_fb_get();
|
fb = esp_camera_fb_get();
|
||||||
if (!fb) {
|
if (!fb) {
|
||||||
ESP_LOGE(TAGCAMERACLASS, "Camera capture failed");
|
ESP_LOGE(TAGCAMERACLASS, "Camera capture failed");
|
||||||
|
LEDOnOff(false);
|
||||||
|
LightOnOff(false);
|
||||||
httpd_resp_send_500(req);
|
httpd_resp_send_500(req);
|
||||||
|
// doReboot();
|
||||||
|
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,15 +509,21 @@ esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay)
|
|||||||
|
|
||||||
void CCamera::LightOnOff(bool status)
|
void CCamera::LightOnOff(bool status)
|
||||||
{
|
{
|
||||||
// Init the GPIO
|
GpioHandler* gpioHandler = gpio_handler_get();
|
||||||
gpio_pad_select_gpio(FLASH_GPIO);
|
if ((gpioHandler != NULL) && (gpioHandler->isEnabled())) {
|
||||||
/* Set the GPIO as a push/pull output */
|
printf("Use gpioHandler flashLigh\n");
|
||||||
gpio_set_direction(FLASH_GPIO, GPIO_MODE_OUTPUT);
|
gpioHandler->flashLightEnable(status);
|
||||||
|
} else {
|
||||||
|
// Init the GPIO
|
||||||
|
gpio_pad_select_gpio(FLASH_GPIO);
|
||||||
|
/* Set the GPIO as a push/pull output */
|
||||||
|
gpio_set_direction(FLASH_GPIO, GPIO_MODE_OUTPUT);
|
||||||
|
|
||||||
if (status)
|
if (status)
|
||||||
gpio_set_level(FLASH_GPIO, 1);
|
gpio_set_level(FLASH_GPIO, 1);
|
||||||
else
|
else
|
||||||
gpio_set_level(FLASH_GPIO, 0);
|
gpio_set_level(FLASH_GPIO, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCamera::LEDOnOff(bool status)
|
void CCamera::LEDOnOff(bool status)
|
||||||
|
|||||||
@@ -16,9 +16,6 @@
|
|||||||
#define CAMERA_MODEL_AI_THINKER
|
#define CAMERA_MODEL_AI_THINKER
|
||||||
|
|
||||||
|
|
||||||
static const char *TAGCAMERACLASS = "server_part_camera";
|
|
||||||
|
|
||||||
|
|
||||||
class CCamera {
|
class CCamera {
|
||||||
protected:
|
protected:
|
||||||
int ActualQuality;
|
int ActualQuality;
|
||||||
|
|||||||
@@ -11,11 +11,48 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#define OV9650_PID (0x96)
|
#ifdef __cplusplus
|
||||||
#define OV7725_PID (0x77)
|
extern "C" {
|
||||||
#define OV2640_PID (0x26)
|
#endif
|
||||||
#define OV3660_PID (0x36)
|
|
||||||
#define OV5640_PID (0x56)
|
typedef enum {
|
||||||
|
OV9650_PID = 0x96,
|
||||||
|
OV7725_PID = 0x77,
|
||||||
|
OV2640_PID = 0x26,
|
||||||
|
OV3660_PID = 0x3660,
|
||||||
|
OV5640_PID = 0x5640,
|
||||||
|
OV7670_PID = 0x76,
|
||||||
|
NT99141_PID = 0x1410,
|
||||||
|
GC2145_PID = 0x2145,
|
||||||
|
GC032A_PID = 0x232a,
|
||||||
|
GC0308_PID = 0x9b,
|
||||||
|
} camera_pid_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CAMERA_OV7725,
|
||||||
|
CAMERA_OV2640,
|
||||||
|
CAMERA_OV3660,
|
||||||
|
CAMERA_OV5640,
|
||||||
|
CAMERA_OV7670,
|
||||||
|
CAMERA_NT99141,
|
||||||
|
CAMERA_GC2145,
|
||||||
|
CAMERA_GC032A,
|
||||||
|
CAMERA_GC0308,
|
||||||
|
CAMERA_MODEL_MAX,
|
||||||
|
CAMERA_NONE,
|
||||||
|
} camera_model_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
OV2640_SCCB_ADDR = 0x30,// 0x60 >> 1
|
||||||
|
OV5640_SCCB_ADDR = 0x3C,// 0x78 >> 1
|
||||||
|
OV3660_SCCB_ADDR = 0x3C,// 0x78 >> 1
|
||||||
|
OV7725_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||||
|
OV7670_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||||
|
NT99141_SCCB_ADDR = 0x2A,// 0x54 >> 1
|
||||||
|
GC2145_SCCB_ADDR = 0x3C,// 0x78 >> 1
|
||||||
|
GC032A_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||||
|
GC0308_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||||
|
} camera_sccb_addr_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PIXFORMAT_RGB565, // 2BPP/RGB565
|
PIXFORMAT_RGB565, // 2BPP/RGB565
|
||||||
@@ -56,6 +93,15 @@ typedef enum {
|
|||||||
FRAMESIZE_INVALID
|
FRAMESIZE_INVALID
|
||||||
} framesize_t;
|
} framesize_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const camera_model_t model;
|
||||||
|
const char *name;
|
||||||
|
const camera_sccb_addr_t sccb_addr;
|
||||||
|
const camera_pid_t pid;
|
||||||
|
const framesize_t max_size;
|
||||||
|
const bool support_jpeg;
|
||||||
|
} camera_sensor_info_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ASPECT_RATIO_4X3,
|
ASPECT_RATIO_4X3,
|
||||||
ASPECT_RATIO_3X2,
|
ASPECT_RATIO_3X2,
|
||||||
@@ -99,11 +145,13 @@ typedef struct {
|
|||||||
|
|
||||||
// Resolution table (in sensor.c)
|
// Resolution table (in sensor.c)
|
||||||
extern const resolution_info_t resolution[];
|
extern const resolution_info_t resolution[];
|
||||||
|
// camera sensor table (in sensor.c)
|
||||||
|
extern const camera_sensor_info_t camera_sensor[];
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t MIDH;
|
uint8_t MIDH;
|
||||||
uint8_t MIDL;
|
uint8_t MIDL;
|
||||||
uint8_t PID;
|
uint16_t PID;
|
||||||
uint8_t VER;
|
uint8_t VER;
|
||||||
} sensor_id_t;
|
} sensor_id_t;
|
||||||
|
|
||||||
@@ -188,4 +236,10 @@ typedef struct _sensor {
|
|||||||
int (*set_xclk) (sensor_t *sensor, int timer, int xclk);
|
int (*set_xclk) (sensor_t *sensor, int timer, int xclk);
|
||||||
} sensor_t;
|
} sensor_t;
|
||||||
|
|
||||||
|
camera_sensor_info_t *esp_camera_sensor_get_info(sensor_id_t *id);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* __SENSOR_H__ */
|
#endif /* __SENSOR_H__ */
|
||||||
|
|||||||
@@ -8,18 +8,21 @@
|
|||||||
|
|
||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
|
|
||||||
#define SCRATCH_BUFSIZE2 8192
|
// #define SCRATCH_BUFSIZE2 8192
|
||||||
char scratch2[SCRATCH_BUFSIZE2];
|
// char scratch2[SCRATCH_BUFSIZE2];
|
||||||
|
|
||||||
//#define DEBUG_DETAIL_ON
|
//#define DEBUG_DETAIL_ON
|
||||||
|
static const char *TAGPARTCAMERA = "server_camera";
|
||||||
|
|
||||||
|
|
||||||
void PowerResetCamera(){
|
void PowerResetCamera(){
|
||||||
ESP_LOGD(TAGPARTCAMERA, "Resetting camera by power down line");
|
ESP_LOGD(TAGPARTCAMERA, "Resetting camera by power down line");
|
||||||
gpio_config_t conf = { 0 };
|
gpio_config_t conf;
|
||||||
|
conf.intr_type = GPIO_INTR_DISABLE;
|
||||||
conf.pin_bit_mask = 1LL << GPIO_NUM_32;
|
conf.pin_bit_mask = 1LL << GPIO_NUM_32;
|
||||||
conf.mode = GPIO_MODE_OUTPUT;
|
conf.mode = GPIO_MODE_OUTPUT;
|
||||||
|
conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||||
|
conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||||
gpio_config(&conf);
|
gpio_config(&conf);
|
||||||
|
|
||||||
// carefull, logic is inverted compared to reset pin
|
// carefull, logic is inverted compared to reset pin
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
|
|
||||||
//#include "ClassControllCamera.h"
|
//#include "ClassControllCamera.h"
|
||||||
|
|
||||||
static const char *TAGPARTCAMERA = "server_camera";
|
|
||||||
|
|
||||||
void register_server_camera_uri(httpd_handle_t server);
|
void register_server_camera_uri(httpd_handle_t server);
|
||||||
|
|
||||||
void PowerResetCamera();
|
void PowerResetCamera();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||||
|
|
||||||
idf_component_register(SRCS ${app_sources}
|
idf_component_register(SRCS ${app_sources}
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "." "../../include"
|
||||||
REQUIRES tfmicro esp_http_server app_update esp_http_client nvs_flash jomjol_tfliteclass jomjol_flowcontroll spiffs jomjol_helper)
|
REQUIRES tfmicro esp_http_server app_update esp_http_client nvs_flash jomjol_tfliteclass jomjol_flowcontroll spiffs jomjol_helper jomjol_controlGPIO)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -26,9 +26,12 @@
|
|||||||
#include <esp_spiffs.h>
|
#include <esp_spiffs.h>
|
||||||
#include "esp_http_server.h"
|
#include "esp_http_server.h"
|
||||||
|
|
||||||
|
#include "defines.h"
|
||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
|
|
||||||
#include "server_help.h"
|
#include "server_help.h"
|
||||||
|
#include "interface_mqtt.h"
|
||||||
|
#include "server_GPIO.h"
|
||||||
|
|
||||||
#include "Helper.h"
|
#include "Helper.h"
|
||||||
#include "miniz.h"
|
#include "miniz.h"
|
||||||
@@ -53,17 +56,17 @@ struct file_server_data {
|
|||||||
char scratch[SCRATCH_BUFSIZE];
|
char scratch[SCRATCH_BUFSIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *TAG = "file_server";
|
static const char *TAG_FILESERVER = "file_server";
|
||||||
|
|
||||||
/* Handler to redirect incoming GET request for /index.html to /
|
/* Handler to redirect incoming GET request for /index.html to /
|
||||||
* This can be overridden by uploading file with same name */
|
* This can be overridden by uploading file with same name */
|
||||||
static esp_err_t index_html_get_handler(httpd_req_t *req)
|
// static esp_err_t index_html_get_handler(httpd_req_t *req)
|
||||||
{
|
// {
|
||||||
httpd_resp_set_status(req, "307 Temporary Redirect");
|
// httpd_resp_set_status(req, "307 Temporary Redirect");
|
||||||
httpd_resp_set_hdr(req, "Location", "/");
|
// httpd_resp_set_hdr(req, "Location", "/");
|
||||||
httpd_resp_send(req, NULL, 0); // Response body can be empty
|
// httpd_resp_send(req, NULL, 0); // Response body can be empty
|
||||||
return ESP_OK;
|
// return ESP_OK;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/* Send HTTP response with a run-time generated html consisting of
|
/* Send HTTP response with a run-time generated html consisting of
|
||||||
* a list of all files and folders under the requested path.
|
* a list of all files and folders under the requested path.
|
||||||
@@ -95,7 +98,7 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath, const
|
|||||||
printf("entrypath: <%s>\n", entrypath);
|
printf("entrypath: <%s>\n", entrypath);
|
||||||
|
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
ESP_LOGE(TAG, "Failed to stat dir : %s", dirpath);
|
ESP_LOGE(TAG_FILESERVER, "Failed to stat dir : %s", dirpath);
|
||||||
/* Respond with 404 Not Found */
|
/* Respond with 404 Not Found */
|
||||||
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Directory does not exist");
|
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Directory does not exist");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
@@ -115,7 +118,7 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath, const
|
|||||||
if (chunksize > 0){
|
if (chunksize > 0){
|
||||||
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||||
fclose(fd);
|
fclose(fd);
|
||||||
ESP_LOGE(TAG, "File sending failed!");
|
ESP_LOGE(TAG_FILESERVER, "File sending failed!");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,11 +157,11 @@ 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);
|
strlcpy(entrypath + dirpath_len, entry->d_name, sizeof(entrypath) - dirpath_len);
|
||||||
printf("Entrypath: %s\n", entrypath);
|
printf("Entrypath: %s\n", entrypath);
|
||||||
if (stat(entrypath, &entry_stat) == -1) {
|
if (stat(entrypath, &entry_stat) == -1) {
|
||||||
ESP_LOGE(TAG, "Failed to stat %s : %s", entrytype, entry->d_name);
|
ESP_LOGE(TAG_FILESERVER, "Failed to stat %s : %s", entrytype, entry->d_name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
sprintf(entrysize, "%ld", entry_stat.st_size);
|
sprintf(entrysize, "%ld", entry_stat.st_size);
|
||||||
ESP_LOGI(TAG, "Found %s : %s (%s bytes)", entrytype, entry->d_name, entrysize);
|
ESP_LOGI(TAG_FILESERVER, "Found %s : %s (%s bytes)", entrytype, entry->d_name, entrysize);
|
||||||
|
|
||||||
/* Send chunk of HTML file containing table entries with file name and size */
|
/* Send chunk of HTML file containing table entries with file name and size */
|
||||||
httpd_resp_sendstr_chunk(req, "<tr><td><a href=\"");
|
httpd_resp_sendstr_chunk(req, "<tr><td><a href=\"");
|
||||||
@@ -206,19 +209,19 @@ static esp_err_t logfileact_get_handler(httpd_req_t *req)
|
|||||||
LogFile.WriteToFile("logfileact_get_handler");
|
LogFile.WriteToFile("logfileact_get_handler");
|
||||||
char filepath[FILE_PATH_MAX];
|
char filepath[FILE_PATH_MAX];
|
||||||
FILE *fd = NULL;
|
FILE *fd = NULL;
|
||||||
struct stat file_stat;
|
//struct stat file_stat;
|
||||||
printf("uri: %s\n", req->uri);
|
printf("uri: %s\n", req->uri);
|
||||||
|
|
||||||
const char filename = 'log_current.txt';
|
const char* filename = "log_current.txt";
|
||||||
|
|
||||||
printf("uri: %s, filename: %s, filepath: %s\n", req->uri, &filename, filepath);
|
printf("uri: %s, filename: %s, filepath: %s\n", req->uri, filename, filepath);
|
||||||
|
|
||||||
std::string currentfilename = LogFile.GetCurrentFileName();
|
std::string currentfilename = LogFile.GetCurrentFileName();
|
||||||
|
|
||||||
|
|
||||||
fd = OpenFileAndWait(currentfilename.c_str(), "r");
|
fd = OpenFileAndWait(currentfilename.c_str(), "r");
|
||||||
if (!fd) {
|
if (!fd) {
|
||||||
ESP_LOGE(TAG, "Failed to read existing file : %s", filepath);
|
ESP_LOGE(TAG_FILESERVER, "Failed to read existing file : %s", filepath);
|
||||||
/* Respond with 500 Internal Server Error */
|
/* Respond with 500 Internal Server Error */
|
||||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file");
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
@@ -226,8 +229,8 @@ static esp_err_t logfileact_get_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||||
|
|
||||||
// ESP_LOGI(TAG, "Sending file : %s (%ld bytes)...", &filename, file_stat.st_size);
|
// ESP_LOGI(TAG_FILESERVER, "Sending file : %s (%ld bytes)...", &filename, file_stat.st_size);
|
||||||
set_content_type_from_file(req, &filename);
|
set_content_type_from_file(req, filename);
|
||||||
|
|
||||||
/* Retrieve the pointer to scratch buffer for temporary storage */
|
/* Retrieve the pointer to scratch buffer for temporary storage */
|
||||||
char *chunk = ((struct file_server_data *)req->user_ctx)->scratch;
|
char *chunk = ((struct file_server_data *)req->user_ctx)->scratch;
|
||||||
@@ -239,7 +242,7 @@ static esp_err_t logfileact_get_handler(httpd_req_t *req)
|
|||||||
/* Send the buffer contents as HTTP response chunk */
|
/* Send the buffer contents as HTTP response chunk */
|
||||||
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||||
fclose(fd);
|
fclose(fd);
|
||||||
ESP_LOGE(TAG, "File sending failed!");
|
ESP_LOGE(TAG_FILESERVER, "File sending failed!");
|
||||||
/* Abort sending file */
|
/* Abort sending file */
|
||||||
httpd_resp_sendstr_chunk(req, NULL);
|
httpd_resp_sendstr_chunk(req, NULL);
|
||||||
/* Respond with 500 Internal Server Error */
|
/* Respond with 500 Internal Server Error */
|
||||||
@@ -252,7 +255,7 @@ static esp_err_t logfileact_get_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
/* Close file after sending complete */
|
/* Close file after sending complete */
|
||||||
fclose(fd);
|
fclose(fd);
|
||||||
ESP_LOGI(TAG, "File sending complete");
|
ESP_LOGI(TAG_FILESERVER, "File sending complete");
|
||||||
|
|
||||||
/* Respond with an empty chunk to signal HTTP response completion */
|
/* Respond with an empty chunk to signal HTTP response completion */
|
||||||
httpd_resp_send_chunk(req, NULL, 0);
|
httpd_resp_send_chunk(req, NULL, 0);
|
||||||
@@ -284,7 +287,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
|
|
||||||
if (!filename) {
|
if (!filename) {
|
||||||
ESP_LOGE(TAG, "Filename is too long");
|
ESP_LOGE(TAG_FILESERVER, "Filename is too long");
|
||||||
/* Respond with 500 Internal Server Error */
|
/* Respond with 500 Internal Server Error */
|
||||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename too long");
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename too long");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
@@ -297,11 +300,11 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
|||||||
if (buf_len > 1) {
|
if (buf_len > 1) {
|
||||||
char buf[buf_len];
|
char buf[buf_len];
|
||||||
if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
|
if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
|
||||||
ESP_LOGI(TAG, "Found URL query => %s", buf);
|
ESP_LOGI(TAG_FILESERVER, "Found URL query => %s", buf);
|
||||||
char param[32];
|
char param[32];
|
||||||
/* Get value of expected key from query string */
|
/* Get value of expected key from query string */
|
||||||
if (httpd_query_key_value(buf, "readonly", param, sizeof(param)) == ESP_OK) {
|
if (httpd_query_key_value(buf, "readonly", param, sizeof(param)) == ESP_OK) {
|
||||||
ESP_LOGI(TAG, "Found URL query parameter => readonly=%s", param);
|
ESP_LOGI(TAG_FILESERVER, "Found URL query parameter => readonly=%s", param);
|
||||||
readonly = param && strcmp(param,"true")==0;
|
readonly = param && strcmp(param,"true")==0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -316,7 +319,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
/* If file not present on SPIFFS check if URI
|
/* If file not present on SPIFFS check if URI
|
||||||
* corresponds to one of the hardcoded paths */
|
* corresponds to one of the hardcoded paths */
|
||||||
ESP_LOGE(TAG, "Failed to stat file : %s", filepath);
|
ESP_LOGE(TAG_FILESERVER, "Failed to stat file : %s", filepath);
|
||||||
/* Respond with 404 Not Found */
|
/* Respond with 404 Not Found */
|
||||||
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "File does not exist");
|
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "File does not exist");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
@@ -324,7 +327,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
fd = OpenFileAndWait(filepath, "r");
|
fd = OpenFileAndWait(filepath, "r");
|
||||||
if (!fd) {
|
if (!fd) {
|
||||||
ESP_LOGE(TAG, "Failed to read existing file : %s", filepath);
|
ESP_LOGE(TAG_FILESERVER, "Failed to read existing file : %s", filepath);
|
||||||
/* Respond with 500 Internal Server Error */
|
/* Respond with 500 Internal Server Error */
|
||||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file");
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
@@ -332,7 +335,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Sending file : %s (%ld bytes)...", filename, file_stat.st_size);
|
ESP_LOGI(TAG_FILESERVER, "Sending file : %s (%ld bytes)...", filename, file_stat.st_size);
|
||||||
set_content_type_from_file(req, filename);
|
set_content_type_from_file(req, filename);
|
||||||
|
|
||||||
/* Retrieve the pointer to scratch buffer for temporary storage */
|
/* Retrieve the pointer to scratch buffer for temporary storage */
|
||||||
@@ -345,7 +348,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
|||||||
/* Send the buffer contents as HTTP response chunk */
|
/* Send the buffer contents as HTTP response chunk */
|
||||||
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||||
fclose(fd);
|
fclose(fd);
|
||||||
ESP_LOGE(TAG, "File sending failed!");
|
ESP_LOGE(TAG_FILESERVER, "File sending failed!");
|
||||||
/* Abort sending file */
|
/* Abort sending file */
|
||||||
httpd_resp_sendstr_chunk(req, NULL);
|
httpd_resp_sendstr_chunk(req, NULL);
|
||||||
/* Respond with 500 Internal Server Error */
|
/* Respond with 500 Internal Server Error */
|
||||||
@@ -358,7 +361,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
/* Close file after sending complete */
|
/* Close file after sending complete */
|
||||||
fclose(fd);
|
fclose(fd);
|
||||||
ESP_LOGI(TAG, "File sending complete");
|
ESP_LOGI(TAG_FILESERVER, "File sending complete");
|
||||||
|
|
||||||
/* Respond with an empty chunk to signal HTTP response completion */
|
/* Respond with an empty chunk to signal HTTP response completion */
|
||||||
httpd_resp_send_chunk(req, NULL, 0);
|
httpd_resp_send_chunk(req, NULL, 0);
|
||||||
@@ -385,13 +388,13 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
/* Filename cannot have a trailing '/' */
|
/* Filename cannot have a trailing '/' */
|
||||||
if (filename[strlen(filename) - 1] == '/') {
|
if (filename[strlen(filename) - 1] == '/') {
|
||||||
ESP_LOGE(TAG, "Invalid filename : %s", filename);
|
ESP_LOGE(TAG_FILESERVER, "Invalid filename : %s", filename);
|
||||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Invalid filename");
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Invalid filename");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stat(filepath, &file_stat) == 0) {
|
if (stat(filepath, &file_stat) == 0) {
|
||||||
ESP_LOGE(TAG, "File already exists : %s", filepath);
|
ESP_LOGE(TAG_FILESERVER, "File already exists : %s", filepath);
|
||||||
/* Respond with 400 Bad Request */
|
/* Respond with 400 Bad Request */
|
||||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File already exists");
|
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File already exists");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
@@ -399,7 +402,7 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
/* File cannot be larger than a limit */
|
/* File cannot be larger than a limit */
|
||||||
if (req->content_len > MAX_FILE_SIZE) {
|
if (req->content_len > MAX_FILE_SIZE) {
|
||||||
ESP_LOGE(TAG, "File too large : %d bytes", req->content_len);
|
ESP_LOGE(TAG_FILESERVER, "File too large : %d bytes", req->content_len);
|
||||||
/* Respond with 400 Bad Request */
|
/* Respond with 400 Bad Request */
|
||||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
|
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
|
||||||
"File size must be less than "
|
"File size must be less than "
|
||||||
@@ -411,13 +414,13 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
fd = OpenFileAndWait(filepath, "w");
|
fd = OpenFileAndWait(filepath, "w");
|
||||||
if (!fd) {
|
if (!fd) {
|
||||||
ESP_LOGE(TAG, "Failed to create file : %s", filepath);
|
ESP_LOGE(TAG_FILESERVER, "Failed to create file : %s", filepath);
|
||||||
/* Respond with 500 Internal Server Error */
|
/* Respond with 500 Internal Server Error */
|
||||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create file");
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create file");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Receiving file : %s...", filename);
|
ESP_LOGI(TAG_FILESERVER, "Receiving file : %s...", filename);
|
||||||
|
|
||||||
/* Retrieve the pointer to scratch buffer for temporary storage */
|
/* Retrieve the pointer to scratch buffer for temporary storage */
|
||||||
char *buf = ((struct file_server_data *)req->user_ctx)->scratch;
|
char *buf = ((struct file_server_data *)req->user_ctx)->scratch;
|
||||||
@@ -429,7 +432,7 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
while (remaining > 0) {
|
while (remaining > 0) {
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Remaining size : %d", remaining);
|
ESP_LOGI(TAG_FILESERVER, "Remaining size : %d", remaining);
|
||||||
/* Receive the file part by part into a buffer */
|
/* Receive the file part by part into a buffer */
|
||||||
if ((received = httpd_req_recv(req, buf, MIN(remaining, SCRATCH_BUFSIZE))) <= 0) {
|
if ((received = httpd_req_recv(req, buf, MIN(remaining, SCRATCH_BUFSIZE))) <= 0) {
|
||||||
if (received == HTTPD_SOCK_ERR_TIMEOUT) {
|
if (received == HTTPD_SOCK_ERR_TIMEOUT) {
|
||||||
@@ -442,7 +445,7 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
|||||||
fclose(fd);
|
fclose(fd);
|
||||||
unlink(filepath);
|
unlink(filepath);
|
||||||
|
|
||||||
ESP_LOGE(TAG, "File reception failed!");
|
ESP_LOGE(TAG_FILESERVER, "File reception failed!");
|
||||||
/* Respond with 500 Internal Server Error */
|
/* Respond with 500 Internal Server Error */
|
||||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file");
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
@@ -455,7 +458,7 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
|||||||
fclose(fd);
|
fclose(fd);
|
||||||
unlink(filepath);
|
unlink(filepath);
|
||||||
|
|
||||||
ESP_LOGE(TAG, "File write failed!");
|
ESP_LOGE(TAG_FILESERVER, "File write failed!");
|
||||||
/* Respond with 500 Internal Server Error */
|
/* Respond with 500 Internal Server Error */
|
||||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to write file to storage");
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to write file to storage");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
@@ -468,7 +471,7 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
/* Close file upon upload completion */
|
/* Close file upon upload completion */
|
||||||
fclose(fd);
|
fclose(fd);
|
||||||
ESP_LOGI(TAG, "File reception complete");
|
ESP_LOGI(TAG_FILESERVER, "File reception complete");
|
||||||
|
|
||||||
std::string directory = std::string(filepath);
|
std::string directory = std::string(filepath);
|
||||||
size_t zw = directory.find("/");
|
size_t zw = directory.find("/");
|
||||||
@@ -483,10 +486,10 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
|||||||
int start_fn = strlen(((struct file_server_data *)req->user_ctx)->base_path);
|
int start_fn = strlen(((struct file_server_data *)req->user_ctx)->base_path);
|
||||||
printf("Directory: %s, start_fn: %d, found: %d\n", directory.c_str(), start_fn, found);
|
printf("Directory: %s, start_fn: %d, found: %d\n", directory.c_str(), start_fn, found);
|
||||||
directory = directory.substr(start_fn, found - start_fn + 1);
|
directory = directory.substr(start_fn, found - start_fn + 1);
|
||||||
printf("Directory danach: %s\n", directory.c_str());
|
printf("Directory danach 1: %s\n", directory.c_str());
|
||||||
|
|
||||||
directory = "/fileserver" + directory;
|
directory = "/fileserver" + directory;
|
||||||
printf("Directory danach: %s\n", directory.c_str());
|
printf("Directory danach 2: %s\n", directory.c_str());
|
||||||
|
|
||||||
/* Redirect onto root to see the updated file list */
|
/* Redirect onto root to see the updated file list */
|
||||||
httpd_resp_set_status(req, "303 See Other");
|
httpd_resp_set_status(req, "303 See Other");
|
||||||
@@ -496,6 +499,15 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
|||||||
httpd_resp_set_status(req, "303 See Other");
|
httpd_resp_set_status(req, "303 See Other");
|
||||||
httpd_resp_set_hdr(req, "Location", directory.c_str());
|
httpd_resp_set_hdr(req, "Location", directory.c_str());
|
||||||
httpd_resp_sendstr(req, "File uploaded successfully");
|
httpd_resp_sendstr(req, "File uploaded successfully");
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (strcmp(filepath, CONFIG_FILE) == 0) {
|
||||||
|
printf("New config found. Reload handler.");
|
||||||
|
gpio_handler_deinit();
|
||||||
|
MQTTdestroy();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,19 +579,19 @@ static esp_err_t delete_post_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
/* Filename cannot have a trailing '/' */
|
/* Filename cannot have a trailing '/' */
|
||||||
if (filename[strlen(filename) - 1] == '/') {
|
if (filename[strlen(filename) - 1] == '/') {
|
||||||
ESP_LOGE(TAG, "Invalid filename : %s", filename);
|
ESP_LOGE(TAG_FILESERVER, "Invalid filename : %s", filename);
|
||||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Invalid filename");
|
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Invalid filename");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stat(filepath, &file_stat) == -1) {
|
if (stat(filepath, &file_stat) == -1) {
|
||||||
ESP_LOGE(TAG, "File does not exist : %s", filename);
|
ESP_LOGE(TAG_FILESERVER, "File does not exist : %s", filename);
|
||||||
/* Respond with 400 Bad Request */
|
/* Respond with 400 Bad Request */
|
||||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File does not exist");
|
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File does not exist");
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Deleting file : %s", filename);
|
ESP_LOGI(TAG_FILESERVER, "Deleting file : %s", filename);
|
||||||
/* Delete file */
|
/* Delete file */
|
||||||
unlink(filepath);
|
unlink(filepath);
|
||||||
|
|
||||||
@@ -596,10 +608,10 @@ static esp_err_t delete_post_handler(httpd_req_t *req)
|
|||||||
int start_fn = strlen(((struct file_server_data *)req->user_ctx)->base_path);
|
int start_fn = strlen(((struct file_server_data *)req->user_ctx)->base_path);
|
||||||
printf("Directory: %s, start_fn: %d, found: %d\n", directory.c_str(), start_fn, found);
|
printf("Directory: %s, start_fn: %d, found: %d\n", directory.c_str(), start_fn, found);
|
||||||
directory = directory.substr(start_fn, found - start_fn + 1);
|
directory = directory.substr(start_fn, found - start_fn + 1);
|
||||||
printf("Directory danach: %s\n", directory.c_str());
|
printf("Directory danach 3: %s\n", directory.c_str());
|
||||||
|
|
||||||
directory = "/fileserver" + directory;
|
directory = "/fileserver" + directory;
|
||||||
printf("Directory danach: %s\n", directory.c_str());
|
printf("Directory danach 4: %s\n", directory.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -623,7 +635,7 @@ void delete_all_in_directory(std::string _directory)
|
|||||||
std::string filename;
|
std::string filename;
|
||||||
|
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
ESP_LOGE(TAG, "Failed to stat dir : %s", _directory.c_str());
|
ESP_LOGE(TAG_FILESERVER, "Failed to stat dir : %s", _directory.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -632,7 +644,7 @@ void delete_all_in_directory(std::string _directory)
|
|||||||
if (!(entry->d_type == DT_DIR)){
|
if (!(entry->d_type == DT_DIR)){
|
||||||
if (strcmp("wlan.ini", entry->d_name) != 0){ // auf wlan.ini soll nicht zugegriffen werden !!!
|
if (strcmp("wlan.ini", entry->d_name) != 0){ // auf wlan.ini soll nicht zugegriffen werden !!!
|
||||||
filename = _directory + "/" + std::string(entry->d_name);
|
filename = _directory + "/" + std::string(entry->d_name);
|
||||||
ESP_LOGI(TAG, "Deleting file : %s", filename.c_str());
|
ESP_LOGI(TAG_FILESERVER, "Deleting file : %s", filename.c_str());
|
||||||
/* Delete file */
|
/* Delete file */
|
||||||
unlink(filename.c_str());
|
unlink(filename.c_str());
|
||||||
}
|
}
|
||||||
@@ -722,19 +734,19 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
|
|||||||
/* Validate file storage base path */
|
/* Validate file storage base path */
|
||||||
if (!base_path) {
|
if (!base_path) {
|
||||||
// if (!base_path || strcmp(base_path, "/spiffs") != 0) {
|
// if (!base_path || strcmp(base_path, "/spiffs") != 0) {
|
||||||
ESP_LOGE(TAG, "File server base_path not set");
|
ESP_LOGE(TAG_FILESERVER, "File server base_path not set");
|
||||||
// return ESP_ERR_INVALID_ARG;
|
// return ESP_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server_data) {
|
if (server_data) {
|
||||||
ESP_LOGE(TAG, "File server already started");
|
ESP_LOGE(TAG_FILESERVER, "File server already started");
|
||||||
// return ESP_ERR_INVALID_STATE;
|
// return ESP_ERR_INVALID_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate memory for server data */
|
/* Allocate memory for server data */
|
||||||
server_data = (file_server_data *) calloc(1, sizeof(struct file_server_data));
|
server_data = (file_server_data *) calloc(1, sizeof(struct file_server_data));
|
||||||
if (!server_data) {
|
if (!server_data) {
|
||||||
ESP_LOGE(TAG, "Failed to allocate memory for server data");
|
ESP_LOGE(TAG_FILESERVER, "Failed to allocate memory for server data");
|
||||||
// return ESP_ERR_NO_MEM;
|
// return ESP_ERR_NO_MEM;
|
||||||
}
|
}
|
||||||
strlcpy(server_data->base_path, base_path,
|
strlcpy(server_data->base_path, base_path,
|
||||||
|
|||||||
@@ -107,8 +107,12 @@ esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filename)
|
|||||||
return httpd_resp_set_type(req, "text/html");
|
return httpd_resp_set_type(req, "text/html");
|
||||||
} else if (IS_FILE_EXT(filename, ".jpeg")) {
|
} else if (IS_FILE_EXT(filename, ".jpeg")) {
|
||||||
return httpd_resp_set_type(req, "image/jpeg");
|
return httpd_resp_set_type(req, "image/jpeg");
|
||||||
|
} else if (IS_FILE_EXT(filename, ".jpg")) {
|
||||||
|
return httpd_resp_set_type(req, "image/jpeg");
|
||||||
} else if (IS_FILE_EXT(filename, ".ico")) {
|
} else if (IS_FILE_EXT(filename, ".ico")) {
|
||||||
return httpd_resp_set_type(req, "image/x-icon");
|
return httpd_resp_set_type(req, "image/x-icon");
|
||||||
|
} else if (IS_FILE_EXT(filename, ".js")) {
|
||||||
|
return httpd_resp_set_type(req, "text/javascript");
|
||||||
}
|
}
|
||||||
/* This is a limited set only */
|
/* This is a limited set only */
|
||||||
/* For any other type always set as plain text */
|
/* For any other type always set as plain text */
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
#include "esp_event.h"
|
#include "esp_event.h"
|
||||||
#include "esp_event_loop.h"
|
#include "esp_event.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include <esp_ota_ops.h>
|
#include <esp_ota_ops.h>
|
||||||
#include "esp_http_client.h"
|
#include "esp_http_client.h"
|
||||||
@@ -28,6 +28,8 @@
|
|||||||
|
|
||||||
#include "server_tflite.h"
|
#include "server_tflite.h"
|
||||||
#include "server_file.h"
|
#include "server_file.h"
|
||||||
|
#include "server_GPIO.h"
|
||||||
|
|
||||||
|
|
||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
|
|
||||||
@@ -46,6 +48,7 @@ static char ota_write_data[BUFFSIZE + 1] = { 0 };
|
|||||||
|
|
||||||
|
|
||||||
#define OTA_URL_SIZE 256
|
#define OTA_URL_SIZE 256
|
||||||
|
static const char *TAGPARTOTA = "server_ota";
|
||||||
|
|
||||||
|
|
||||||
static void infinite_loop(void)
|
static void infinite_loop(void)
|
||||||
@@ -60,14 +63,14 @@ static void infinite_loop(void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static bool ota_example_task(std::string fn)
|
static bool ota_update_task(std::string fn)
|
||||||
{
|
{
|
||||||
esp_err_t err;
|
esp_err_t err;
|
||||||
/* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */
|
/* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */
|
||||||
esp_ota_handle_t update_handle = 0 ;
|
esp_ota_handle_t update_handle = 0 ;
|
||||||
const esp_partition_t *update_partition = NULL;
|
const esp_partition_t *update_partition = NULL;
|
||||||
|
|
||||||
ESP_LOGI(TAGPARTOTA, "Starting OTA example");
|
ESP_LOGI(TAGPARTOTA, "Starting OTA update");
|
||||||
|
|
||||||
const esp_partition_t *configured = esp_ota_get_boot_partition();
|
const esp_partition_t *configured = esp_ota_get_boot_partition();
|
||||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||||
@@ -374,7 +377,9 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
|||||||
|
|
||||||
const char* resp_str;
|
const char* resp_str;
|
||||||
|
|
||||||
if (ota_example_task(fn))
|
KillTFliteTasks();
|
||||||
|
gpio_handler_deinit();
|
||||||
|
if (ota_update_task(fn))
|
||||||
{
|
{
|
||||||
resp_str = "Firmware Update Successfull!<br><br>You can restart now.";
|
resp_str = "Firmware Update Successfull!<br><br>You can restart now.";
|
||||||
}
|
}
|
||||||
@@ -400,8 +405,6 @@ void hard_restart() {
|
|||||||
|
|
||||||
void task_reboot(void *pvParameter)
|
void task_reboot(void *pvParameter)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||||
@@ -413,12 +416,14 @@ void task_reboot(void *pvParameter)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void doReboot(){
|
void doReboot(){
|
||||||
LogFile.WriteToFile("Reboot - now");
|
ESP_LOGI(TAGPARTOTA, "Reboot in 5sec");
|
||||||
KillTFliteTasks();
|
LogFile.WriteToFile("Reboot in 5sec");
|
||||||
xTaskCreate(&task_reboot, "reboot", configMINIMAL_STACK_SIZE * 64, NULL, 10, NULL);
|
xTaskCreate(&task_reboot, "reboot", configMINIMAL_STACK_SIZE * 64, NULL, 10, NULL);
|
||||||
|
// KillTFliteTasks(); // kills itself
|
||||||
|
gpio_handler_destroy();
|
||||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||||
esp_restart();
|
esp_restart();
|
||||||
hard_restart();
|
hard_restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
|
|
||||||
//#include "ClassControllCamera.h"
|
//#include "ClassControllCamera.h"
|
||||||
|
|
||||||
static const char *TAGPARTOTA = "server_ota";
|
|
||||||
|
|
||||||
void register_server_ota_sdcard_uri(httpd_handle_t server);
|
void register_server_ota_sdcard_uri(httpd_handle_t server);
|
||||||
void CheckOTAUpdate();
|
void CheckOTAUpdate();
|
||||||
void doReboot();
|
void doReboot();
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
|||||||
|
|
||||||
idf_component_register(SRCS ${app_sources}
|
idf_component_register(SRCS ${app_sources}
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
REQUIRES jomjol_tfliteclass jomjol_helper jomjol_controlcamera jomjol_mqtt jomjol_fileserver_ota jomjol_image_proc connect_wlan)
|
REQUIRES jomjol_tfliteclass jomjol_helper jomjol_controlcamera jomjol_mqtt jomjol_fileserver_ota jomjol_image_proc jomjol_wlan)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -94,6 +94,23 @@ string ClassFlow::getReadout()
|
|||||||
return string();
|
return string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ClassFlow::GetParameterName(std::string _input)
|
||||||
|
{
|
||||||
|
string _param;
|
||||||
|
int _pospunkt = _input.find_first_of(".");
|
||||||
|
if (_pospunkt > -1)
|
||||||
|
{
|
||||||
|
_param = _input.substr(_pospunkt+1, _input.length() - _pospunkt - 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_param = _input;
|
||||||
|
}
|
||||||
|
// printf("Parameter: %s, Pospunkt: %d\n", _param.c_str(), _pospunkt);
|
||||||
|
return _param;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool ClassFlow::getNextLine(FILE* pfile, string *rt)
|
bool ClassFlow::getNextLine(FILE* pfile, string *rt)
|
||||||
{
|
{
|
||||||
char zw[1024];
|
char zw[1024];
|
||||||
@@ -102,24 +119,21 @@ bool ClassFlow::getNextLine(FILE* pfile, string *rt)
|
|||||||
*rt = "";
|
*rt = "";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
fgets(zw, 1024, pfile);
|
if (!fgets(zw, 1024, pfile))
|
||||||
printf("%s", zw);
|
|
||||||
if ((strlen(zw) == 0) && feof(pfile))
|
|
||||||
{
|
{
|
||||||
*rt = "";
|
*rt = "";
|
||||||
|
printf("END OF FILE\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
printf("%s", zw);
|
||||||
*rt = zw;
|
*rt = zw;
|
||||||
*rt = trim(*rt);
|
*rt = trim(*rt);
|
||||||
while ((zw[0] == ';' || zw[0] == '#' || (rt->size() == 0)) && !(zw[1] == '[')) // Kommentarzeilen (; oder #) und Leerzeilen überspringen, es sei denn es ist ein neuer auskommentierter Paragraph
|
while ((zw[0] == ';' || zw[0] == '#' || (rt->size() == 0)) && !(zw[1] == '[')) // Kommentarzeilen (; oder #) und Leerzeilen überspringen, es sei denn es ist ein neuer auskommentierter Paragraph
|
||||||
{
|
{
|
||||||
fgets(zw, 1024, pfile);
|
*rt = "";
|
||||||
printf("%s", zw);
|
if (!fgets(zw, 1024, pfile))
|
||||||
if (feof(pfile))
|
|
||||||
{
|
|
||||||
*rt = "";
|
|
||||||
return false;
|
return false;
|
||||||
}
|
printf("%s", zw);
|
||||||
*rt = zw;
|
*rt = zw;
|
||||||
*rt = trim(*rt);
|
*rt = trim(*rt);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ protected:
|
|||||||
|
|
||||||
virtual void SetInitialParameter(void);
|
virtual void SetInitialParameter(void);
|
||||||
|
|
||||||
|
std::string GetParameterName(std::string _input);
|
||||||
|
|
||||||
bool disabled;
|
bool disabled;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -1,350 +0,0 @@
|
|||||||
#include "ClassFlowAnalog.h"
|
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sstream> // std::stringstream
|
|
||||||
|
|
||||||
|
|
||||||
// #define OHNETFLITE
|
|
||||||
|
|
||||||
#ifndef OHNETFLITE
|
|
||||||
#include "CTfLiteClass.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "ClassLogFile.h"
|
|
||||||
|
|
||||||
static const char* TAG = "flow_analog";
|
|
||||||
|
|
||||||
bool debugdetailanalog = false;
|
|
||||||
|
|
||||||
void ClassFlowAnalog::SetInitialParameter(void)
|
|
||||||
{
|
|
||||||
string cnnmodelfile = "";
|
|
||||||
modelxsize = 1;
|
|
||||||
modelysize = 1;
|
|
||||||
ListFlowControll = NULL;
|
|
||||||
previousElement = NULL;
|
|
||||||
SaveAllFiles = false;
|
|
||||||
disabled = false;
|
|
||||||
extendedResolution = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassFlowAnalog::ClassFlowAnalog(std::vector<ClassFlow*>* lfc) : ClassFlowImage(lfc, TAG)
|
|
||||||
{
|
|
||||||
SetInitialParameter();
|
|
||||||
ListFlowControll = lfc;
|
|
||||||
|
|
||||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
|
||||||
{
|
|
||||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0)
|
|
||||||
{
|
|
||||||
flowpostalignment = (ClassFlowAlignment*) (*ListFlowControll)[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int ClassFlowAnalog::AnzahlROIs()
|
|
||||||
{
|
|
||||||
int zw = ROI.size();
|
|
||||||
if (extendedResolution)
|
|
||||||
zw++;
|
|
||||||
|
|
||||||
return zw;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
string ClassFlowAnalog::getReadout()
|
|
||||||
{
|
|
||||||
string result = "";
|
|
||||||
if (ROI.size() == 0)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
|
|
||||||
float zahl = ROI[ROI.size() - 1]->result;
|
|
||||||
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
|
|
||||||
|
|
||||||
int prev = -1;
|
|
||||||
|
|
||||||
prev = ZeigerEval(ROI[ROI.size() - 1]->result, prev);
|
|
||||||
result = std::to_string(prev);
|
|
||||||
|
|
||||||
if (extendedResolution)
|
|
||||||
result = result + std::to_string(ergebnis_nachkomma);
|
|
||||||
|
|
||||||
for (int i = ROI.size() - 2; i >= 0; --i)
|
|
||||||
{
|
|
||||||
prev = ZeigerEval(ROI[i]->result, prev);
|
|
||||||
result = std::to_string(prev) + result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ClassFlowAnalog::ZeigerEval(float zahl, int ziffer_vorgaenger)
|
|
||||||
{
|
|
||||||
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
|
|
||||||
int ergebnis_vorkomma = ((int) floor(zahl)) % 10;
|
|
||||||
int ergebnis, ergebnis_rating;
|
|
||||||
|
|
||||||
if (ziffer_vorgaenger == -1)
|
|
||||||
return ergebnis_vorkomma % 10;
|
|
||||||
|
|
||||||
ergebnis_rating = ergebnis_nachkomma - ziffer_vorgaenger;
|
|
||||||
if (ergebnis_nachkomma >= 5)
|
|
||||||
ergebnis_rating-=5;
|
|
||||||
else
|
|
||||||
ergebnis_rating+=5;
|
|
||||||
ergebnis = (int) round(zahl);
|
|
||||||
if (ergebnis_rating < 0)
|
|
||||||
ergebnis-=1;
|
|
||||||
if (ergebnis == -1)
|
|
||||||
ergebnis+=10;
|
|
||||||
|
|
||||||
ergebnis = ergebnis % 10;
|
|
||||||
return ergebnis;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClassFlowAnalog::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|
||||||
{
|
|
||||||
std::vector<string> zerlegt;
|
|
||||||
|
|
||||||
aktparamgraph = trim(aktparamgraph);
|
|
||||||
|
|
||||||
if (aktparamgraph.size() == 0)
|
|
||||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
|
|
||||||
if ((aktparamgraph.compare("[Analog]") != 0) && (aktparamgraph.compare(";[Analog]") != 0)) // Paragraph passt nich zu MakeImage
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (aktparamgraph[0] == ';')
|
|
||||||
{
|
|
||||||
disabled = true;
|
|
||||||
while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph));
|
|
||||||
printf("[Analog] is disabled !!!\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
|
||||||
{
|
|
||||||
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
|
||||||
if ((zerlegt[0] == "LogImageLocation") && (zerlegt.size() > 1))
|
|
||||||
{
|
|
||||||
this->LogImageLocation = "/sdcard" + zerlegt[1];
|
|
||||||
this->isLogImage = true;
|
|
||||||
}
|
|
||||||
if ((toUpper(zerlegt[0]) == "LOGFILERETENTIONINDAYS") && (zerlegt.size() > 1))
|
|
||||||
{
|
|
||||||
this->logfileRetentionInDays = std::stoi(zerlegt[1]);
|
|
||||||
}
|
|
||||||
if ((zerlegt[0] == "Model") && (zerlegt.size() > 1))
|
|
||||||
{
|
|
||||||
this->cnnmodelfile = zerlegt[1];
|
|
||||||
}
|
|
||||||
if ((zerlegt[0] == "ModelInputSize") && (zerlegt.size() > 2))
|
|
||||||
{
|
|
||||||
this->modelxsize = std::stoi(zerlegt[1]);
|
|
||||||
this->modelysize = std::stoi(zerlegt[2]);
|
|
||||||
}
|
|
||||||
if (zerlegt.size() >= 5)
|
|
||||||
{
|
|
||||||
roianalog* neuroi = new roianalog;
|
|
||||||
neuroi->name = zerlegt[0];
|
|
||||||
neuroi->posx = std::stoi(zerlegt[1]);
|
|
||||||
neuroi->posy = std::stoi(zerlegt[2]);
|
|
||||||
neuroi->deltax = std::stoi(zerlegt[3]);
|
|
||||||
neuroi->deltay = std::stoi(zerlegt[4]);
|
|
||||||
neuroi->result = -1;
|
|
||||||
neuroi->image = NULL;
|
|
||||||
neuroi->image_org = NULL;
|
|
||||||
ROI.push_back(neuroi);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((toUpper(zerlegt[0]) == "SAVEALLFILES") && (zerlegt.size() > 1))
|
|
||||||
{
|
|
||||||
if (toUpper(zerlegt[1]) == "TRUE")
|
|
||||||
SaveAllFiles = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((toUpper(zerlegt[0]) == "EXTENDEDRESOLUTION") && (zerlegt.size() > 1))
|
|
||||||
{
|
|
||||||
if (toUpper(zerlegt[1]) == "TRUE")
|
|
||||||
extendedResolution = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < ROI.size(); ++i)
|
|
||||||
{
|
|
||||||
ROI[i]->image = new CImageBasis(modelxsize, modelysize, 3);
|
|
||||||
ROI[i]->image_org = new CImageBasis(ROI[i]->deltax, ROI[i]->deltay, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
string ClassFlowAnalog::getHTMLSingleStep(string host)
|
|
||||||
{
|
|
||||||
string result, zw;
|
|
||||||
std::vector<HTMLInfo*> htmlinfo;
|
|
||||||
|
|
||||||
result = "<p>Found ROIs: </p> <p><img src=\"" + host + "/img_tmp/alg_roi.jpg\"></p>\n";
|
|
||||||
result = result + "Analog Pointers: <p> ";
|
|
||||||
|
|
||||||
htmlinfo = GetHTMLInfo();
|
|
||||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
|
||||||
{
|
|
||||||
std::stringstream stream;
|
|
||||||
stream << std::fixed << std::setprecision(1) << htmlinfo[i]->val;
|
|
||||||
zw = stream.str();
|
|
||||||
|
|
||||||
result = result + "<img src=\"" + host + "/img_tmp/" + htmlinfo[i]->filename + "\"> " + zw;
|
|
||||||
delete htmlinfo[i];
|
|
||||||
}
|
|
||||||
htmlinfo.clear();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool ClassFlowAnalog::doFlow(string time)
|
|
||||||
{
|
|
||||||
if (disabled)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!doAlignAndCut(time)){
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (debugdetailanalog) LogFile.WriteToFile("ClassFlowAnalog::doFlow nach Alignment");
|
|
||||||
|
|
||||||
doNeuralNetwork(time);
|
|
||||||
|
|
||||||
RemoveOldLogs();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClassFlowAnalog::doAlignAndCut(string time)
|
|
||||||
{
|
|
||||||
if (disabled)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
CAlignAndCutImage *caic = flowpostalignment->GetAlignAndCutImage();
|
|
||||||
|
|
||||||
for (int i = 0; i < ROI.size(); ++i)
|
|
||||||
{
|
|
||||||
printf("Analog %d - Align&Cut\n", i);
|
|
||||||
|
|
||||||
caic->CutAndSave(ROI[i]->posx, ROI[i]->posy, ROI[i]->deltax, ROI[i]->deltay, ROI[i]->image_org);
|
|
||||||
if (SaveAllFiles) ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ROI[i]->name + ".jpg"));
|
|
||||||
|
|
||||||
ROI[i]->image_org->Resize(modelxsize, modelysize, ROI[i]->image);
|
|
||||||
if (SaveAllFiles) ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ROI[i]->name + ".bmp"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClassFlowAnalog::DrawROI(CImageBasis *_zw)
|
|
||||||
{
|
|
||||||
int r = 0;
|
|
||||||
int g = 255;
|
|
||||||
int b = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < ROI.size(); ++i)
|
|
||||||
{
|
|
||||||
_zw->drawRect(ROI[i]->posx, ROI[i]->posy, ROI[i]->deltax, ROI[i]->deltay, r, g, b, 1);
|
|
||||||
_zw->drawCircle((int) (ROI[i]->posx + ROI[i]->deltax/2), (int) (ROI[i]->posy + ROI[i]->deltay/2), (int) (ROI[i]->deltax/2), r, g, b, 2);
|
|
||||||
_zw->drawLine((int) (ROI[i]->posx + ROI[i]->deltax/2), (int) ROI[i]->posy, (int) (ROI[i]->posx + ROI[i]->deltax/2), (int) (ROI[i]->posy + ROI[i]->deltay), r, g, b, 2);
|
|
||||||
_zw->drawLine((int) ROI[i]->posx, (int) (ROI[i]->posy + ROI[i]->deltay/2), (int) ROI[i]->posx + ROI[i]->deltax, (int) (ROI[i]->posy + ROI[i]->deltay/2), r, g, b, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClassFlowAnalog::doNeuralNetwork(string time)
|
|
||||||
{
|
|
||||||
if (disabled)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
string logPath = CreateLogFolder(time);
|
|
||||||
|
|
||||||
string input = "/sdcard/img_tmp/alg.jpg";
|
|
||||||
string ioresize = "/sdcard/img_tmp/resize.bmp";
|
|
||||||
string output;
|
|
||||||
input = FormatFileName(input);
|
|
||||||
|
|
||||||
#ifndef OHNETFLITE
|
|
||||||
CTfLiteClass *tflite = new CTfLiteClass;
|
|
||||||
string zwcnn = "/sdcard" + cnnmodelfile;
|
|
||||||
zwcnn = FormatFileName(zwcnn);
|
|
||||||
printf(zwcnn.c_str());printf("\n");
|
|
||||||
tflite->LoadModel(zwcnn);
|
|
||||||
tflite->MakeAllocate();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (int i = 0; i < ROI.size(); ++i)
|
|
||||||
{
|
|
||||||
printf("Analog %d - TfLite\n", i);
|
|
||||||
ioresize = "/sdcard/img_tmp/ra" + std::to_string(i) + ".bmp";
|
|
||||||
ioresize = FormatFileName(ioresize);
|
|
||||||
|
|
||||||
|
|
||||||
float f1, f2;
|
|
||||||
f1 = 0; f2 = 0;
|
|
||||||
|
|
||||||
#ifndef OHNETFLITE
|
|
||||||
// LogFile.WriteToFile("ClassFlowAnalog::doNeuralNetwork vor CNN tflite->LoadInputImage(ioresize)");
|
|
||||||
// tflite->LoadInputImage(ioresize);
|
|
||||||
tflite->LoadInputImageBasis(ROI[i]->image);
|
|
||||||
tflite->Invoke();
|
|
||||||
if (debugdetailanalog) LogFile.WriteToFile("Nach Invoke");
|
|
||||||
|
|
||||||
|
|
||||||
f1 = tflite->GetOutputValue(0);
|
|
||||||
f2 = tflite->GetOutputValue(1);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
float result = fmod(atan2(f1, f2) / (M_PI * 2) + 2, 1);
|
|
||||||
// printf("Result sin, cos, ziffer: %f, %f, %f\n", f1, f2, result);
|
|
||||||
ROI[i]->result = result * 10;
|
|
||||||
|
|
||||||
printf("Result Analog%i: %f\n", i, ROI[i]->result);
|
|
||||||
|
|
||||||
if (isLogImage)
|
|
||||||
{
|
|
||||||
LogImage(logPath, ROI[i]->name, &ROI[i]->result, NULL, time, ROI[i]->image_org);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifndef OHNETFLITE
|
|
||||||
delete tflite;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<HTMLInfo*> ClassFlowAnalog::GetHTMLInfo()
|
|
||||||
{
|
|
||||||
std::vector<HTMLInfo*> result;
|
|
||||||
|
|
||||||
for (int i = 0; i < ROI.size(); ++i)
|
|
||||||
{
|
|
||||||
HTMLInfo *zw = new HTMLInfo;
|
|
||||||
zw->filename = ROI[i]->name + ".bmp";
|
|
||||||
zw->filename_org = ROI[i]->name + ".jpg";
|
|
||||||
zw->val = ROI[i]->result;
|
|
||||||
zw->image = ROI[i]->image;
|
|
||||||
zw->image_org = ROI[i]->image_org;
|
|
||||||
result.push_back(zw);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "ClassFlowImage.h"
|
|
||||||
#include "ClassFlowAlignment.h"
|
|
||||||
// #include "CTfLiteClass.h"
|
|
||||||
|
|
||||||
struct roianalog {
|
|
||||||
int posx, posy, deltax, deltay;
|
|
||||||
float result;
|
|
||||||
CImageBasis *image, *image_org;
|
|
||||||
string name;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class ClassFlowAnalog :
|
|
||||||
public ClassFlowImage
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
std::vector<roianalog*> ROI;
|
|
||||||
string cnnmodelfile;
|
|
||||||
int modelxsize, modelysize;
|
|
||||||
int ZeigerEval(float zahl, int ziffer_vorgaenger);
|
|
||||||
bool SaveAllFiles;
|
|
||||||
|
|
||||||
|
|
||||||
ClassFlowAlignment* flowpostalignment;
|
|
||||||
|
|
||||||
void SetInitialParameter(void);
|
|
||||||
|
|
||||||
public:
|
|
||||||
bool extendedResolution;
|
|
||||||
|
|
||||||
ClassFlowAnalog(std::vector<ClassFlow*>* lfc);
|
|
||||||
|
|
||||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
|
||||||
bool doFlow(string time);
|
|
||||||
string getHTMLSingleStep(string host);
|
|
||||||
string getReadout();
|
|
||||||
|
|
||||||
void DrawROI(CImageBasis *_zw);
|
|
||||||
|
|
||||||
bool doNeuralNetwork(string time);
|
|
||||||
bool doAlignAndCut(string time);
|
|
||||||
std::vector<HTMLInfo*> GetHTMLInfo();
|
|
||||||
int AnzahlROIs();
|
|
||||||
|
|
||||||
string name(){return "ClassFlowAnalog";};
|
|
||||||
};
|
|
||||||
|
|
||||||
665
code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp
Normal file
665
code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp
Normal file
@@ -0,0 +1,665 @@
|
|||||||
|
#include "ClassFlowCNNGeneral.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sstream> // std::stringstream
|
||||||
|
|
||||||
|
#include "CTfLiteClass.h"
|
||||||
|
#include "ClassLogFile.h"
|
||||||
|
|
||||||
|
static const char* TAG = "flow_analog";
|
||||||
|
|
||||||
|
bool debugdetailgeneral = false;
|
||||||
|
|
||||||
|
ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype) : ClassFlowImage(NULL, TAG)
|
||||||
|
{
|
||||||
|
string cnnmodelfile = "";
|
||||||
|
modelxsize = 1;
|
||||||
|
modelysize = 1;
|
||||||
|
ListFlowControll = NULL;
|
||||||
|
previousElement = NULL;
|
||||||
|
SaveAllFiles = false;
|
||||||
|
disabled = false;
|
||||||
|
// extendedResolution = false;
|
||||||
|
isLogImageSelect = false;
|
||||||
|
CNNType = AutoDetect;
|
||||||
|
CNNType = _cnntype;
|
||||||
|
flowpostalignment = _flowalign;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
int ClassFlowCNNGeneral::AnzahlROIs(int _analog = 0)
|
||||||
|
{
|
||||||
|
int zw = GENERAL[_analog]->ROI.size();
|
||||||
|
if (extendedResolution && (CNNType != Digital)) zw++; // da letzte Ziffer inkl Nachhkomma, es sei denn, das Nachkomma gibt es nicht (Digital)
|
||||||
|
return zw;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution = false)
|
||||||
|
{
|
||||||
|
string result = "";
|
||||||
|
if (GENERAL[_analog]->ROI.size() == 0)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (CNNType == Analogue)
|
||||||
|
{
|
||||||
|
float zahl = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float;
|
||||||
|
int ergebnis_nachkomma = ((int) floor(zahl * 10) + 10) % 10;
|
||||||
|
|
||||||
|
int prev = -1;
|
||||||
|
|
||||||
|
prev = ZeigerEval(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev);
|
||||||
|
result = std::to_string(prev);
|
||||||
|
|
||||||
|
if (_extendedResolution && (CNNType != Digital))
|
||||||
|
result = result + std::to_string(ergebnis_nachkomma);
|
||||||
|
|
||||||
|
for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i)
|
||||||
|
{
|
||||||
|
prev = ZeigerEval(GENERAL[_analog]->ROI[i]->result_float, prev);
|
||||||
|
result = std::to_string(prev) + result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CNNType == Digital)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < GENERAL[_analog]->ROI.size(); ++i)
|
||||||
|
{
|
||||||
|
if (GENERAL[_analog]->ROI[i]->result_klasse >= 10)
|
||||||
|
result = result + "N";
|
||||||
|
else
|
||||||
|
result = result + std::to_string(GENERAL[_analog]->ROI[i]->result_klasse);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CNNType == DigitalHyprid)
|
||||||
|
{
|
||||||
|
// int ergebnis_nachkomma = -1;
|
||||||
|
int zif_akt = -1;
|
||||||
|
|
||||||
|
float zahl = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float;
|
||||||
|
if (zahl >= 0) // NaN?
|
||||||
|
{
|
||||||
|
if (_extendedResolution)
|
||||||
|
{
|
||||||
|
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
|
||||||
|
int ergebnis_vorkomma = ((int) floor(zahl)) % 10;
|
||||||
|
|
||||||
|
result = std::to_string(ergebnis_vorkomma) + std::to_string(ergebnis_nachkomma);
|
||||||
|
zif_akt = ergebnis_vorkomma;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
zif_akt = ZeigerEvalHybrid(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, -1, -1);
|
||||||
|
result = std::to_string(zif_akt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = "N";
|
||||||
|
if (_extendedResolution && (CNNType != Digital))
|
||||||
|
result = "NN";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i)
|
||||||
|
{
|
||||||
|
if (GENERAL[_analog]->ROI[i]->result_float >= 0)
|
||||||
|
{
|
||||||
|
zif_akt = ZeigerEvalHybrid(GENERAL[_analog]->ROI[i]->result_float, GENERAL[_analog]->ROI[i+1]->result_float, zif_akt);
|
||||||
|
result = std::to_string(zif_akt) + result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
zif_akt = -1;
|
||||||
|
result = "N" + result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ClassFlowCNNGeneral::ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int eval_vorgaenger)
|
||||||
|
{
|
||||||
|
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
|
||||||
|
// int ergebnis_vorkomma = ((int) floor(zahl)) % 10;
|
||||||
|
|
||||||
|
if (zahl_vorgaenger < 0) // keine Vorzahl vorhanden !!! --> Runde die Zahl
|
||||||
|
{
|
||||||
|
if ((ergebnis_nachkomma <= 2) || (ergebnis_nachkomma >= 8)) // Band um die Ziffer --> Runden, da Ziffer im Rahmen Ungenauigkeit erreicht
|
||||||
|
return ((int) round(zahl) + 10) % 10;
|
||||||
|
else
|
||||||
|
return ((int) trunc(zahl) + 10) % 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zahl_vorgaenger > 9.2) // Ziffernwechsel beginnt
|
||||||
|
{
|
||||||
|
if (eval_vorgaenger == 0) // Wechsel hat schon stattgefunden
|
||||||
|
{
|
||||||
|
return ((int) round(zahl) + 10) % 10; // Annahme, dass die neue Zahl schon in der Nähe des Ziels ist
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (zahl_vorgaenger <= 9.5) // Wechsel startet gerade, aber beginnt erst
|
||||||
|
{
|
||||||
|
if ((ergebnis_nachkomma <= 2) || (ergebnis_nachkomma >= 8)) // Band um die Ziffer --> Runden, da Ziffer im Rahmen Ungenauigkeit erreicht
|
||||||
|
return ((int) round(zahl) + 10) % 10;
|
||||||
|
else
|
||||||
|
return ((int) trunc(zahl) + 10) % 10;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ((int) trunc(zahl) + 10) % 10; // Wechsel schon weiter fortgeschritten, d.h. über 2 als Nachkomma
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ergebnis_nachkomma <= 2) || (ergebnis_nachkomma >= 8)) // Band um die Ziffer --> Runden, da Ziffer im Rahmen Ungenauigkeit erreicht
|
||||||
|
return ((int) round(zahl) + 10) % 10;
|
||||||
|
|
||||||
|
return ((int) trunc(zahl) + 10) % 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ClassFlowCNNGeneral::ZeigerEval(float zahl, int ziffer_vorgaenger)
|
||||||
|
{
|
||||||
|
int ergebnis_nachkomma = ((int) floor(zahl * 10) + 10) % 10;
|
||||||
|
int ergebnis_vorkomma = ((int) floor(zahl) + 10) % 10;
|
||||||
|
int ergebnis, ergebnis_rating;
|
||||||
|
|
||||||
|
if (ziffer_vorgaenger == -1)
|
||||||
|
return ergebnis_vorkomma % 10;
|
||||||
|
|
||||||
|
ergebnis_rating = ergebnis_nachkomma - ziffer_vorgaenger;
|
||||||
|
if (ergebnis_nachkomma >= 5)
|
||||||
|
ergebnis_rating-=5;
|
||||||
|
else
|
||||||
|
ergebnis_rating+=5;
|
||||||
|
ergebnis = (int) round(zahl);
|
||||||
|
if (ergebnis_rating < 0)
|
||||||
|
ergebnis-=1;
|
||||||
|
if (ergebnis == -1)
|
||||||
|
ergebnis+=10;
|
||||||
|
|
||||||
|
ergebnis = (ergebnis + 10) % 10;
|
||||||
|
return ergebnis;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||||
|
{
|
||||||
|
std::vector<string> zerlegt;
|
||||||
|
|
||||||
|
aktparamgraph = trim(aktparamgraph);
|
||||||
|
|
||||||
|
if (aktparamgraph.size() == 0)
|
||||||
|
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
if ((toUpper(aktparamgraph) != "[ANALOG]") && (toUpper(aktparamgraph) != ";[ANALOG]")
|
||||||
|
&& (toUpper(aktparamgraph) != "[DIGIT]") && (toUpper(aktparamgraph) != ";[DIGIT]")
|
||||||
|
&& (toUpper(aktparamgraph) != "[DIGITS]") && (toUpper(aktparamgraph) != ";[DIGITS]")
|
||||||
|
) // Paragraph passt nicht
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
if ((aktparamgraph.compare("[Analog]") != 0) && (aktparamgraph.compare(";[Analog]") != 0)
|
||||||
|
&& (aktparamgraph.compare("[Digit]") != 0) && (aktparamgraph.compare(";[Digit]"))) // Paragraph passt nicht
|
||||||
|
return false;
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (aktparamgraph[0] == ';')
|
||||||
|
{
|
||||||
|
disabled = true;
|
||||||
|
while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph));
|
||||||
|
printf("[Analog/Digit] is disabled !!!\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||||
|
{
|
||||||
|
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
||||||
|
if ((zerlegt[0] == "LogImageLocation") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
this->LogImageLocation = "/sdcard" + zerlegt[1];
|
||||||
|
this->isLogImage = true;
|
||||||
|
}
|
||||||
|
if ((zerlegt[0] == "LogImageSelect") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
LogImageSelect = zerlegt[1];
|
||||||
|
isLogImageSelect = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((toUpper(zerlegt[0]) == "LOGFILERETENTIONINDAYS") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
this->logfileRetentionInDays = std::stoi(zerlegt[1]);
|
||||||
|
}
|
||||||
|
if ((toUpper(zerlegt[0]) == "MODELTYPE") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(zerlegt[1]) == "DIGITHYPRID")
|
||||||
|
CNNType = DigitalHyprid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((zerlegt[0] == "Model") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
this->cnnmodelfile = zerlegt[1];
|
||||||
|
}
|
||||||
|
if ((zerlegt[0] == "ModelInputSize") && (zerlegt.size() > 2))
|
||||||
|
{
|
||||||
|
this->modelxsize = std::stoi(zerlegt[1]);
|
||||||
|
this->modelysize = std::stoi(zerlegt[2]);
|
||||||
|
}
|
||||||
|
if (zerlegt.size() >= 5)
|
||||||
|
{
|
||||||
|
general* _analog = GetGENERAL(zerlegt[0], true);
|
||||||
|
roi* neuroi = _analog->ROI[_analog->ROI.size()-1];
|
||||||
|
neuroi->posx = std::stoi(zerlegt[1]);
|
||||||
|
neuroi->posy = std::stoi(zerlegt[2]);
|
||||||
|
neuroi->deltax = std::stoi(zerlegt[3]);
|
||||||
|
neuroi->deltay = std::stoi(zerlegt[4]);
|
||||||
|
neuroi->result_float = -1;
|
||||||
|
neuroi->image = NULL;
|
||||||
|
neuroi->image_org = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((toUpper(zerlegt[0]) == "SAVEALLFILES") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(zerlegt[1]) == "TRUE")
|
||||||
|
SaveAllFiles = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if ((toUpper(zerlegt[0]) == "EXTENDEDRESOLUTION") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
if (toUpper(zerlegt[1]) == "TRUE")
|
||||||
|
extendedResolution = true;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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, 3);
|
||||||
|
GENERAL[_ana]->ROI[i]->image_org = new CImageBasis(GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
general* ClassFlowCNNGeneral::FindGENERAL(string _name_number)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < GENERAL.size(); ++i)
|
||||||
|
if (GENERAL[i]->name == _name_number)
|
||||||
|
return GENERAL[i];
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
general* ClassFlowCNNGeneral::GetGENERAL(string _name, bool _create = true)
|
||||||
|
{
|
||||||
|
string _analog, _roi;
|
||||||
|
int _pospunkt = _name.find_first_of(".");
|
||||||
|
|
||||||
|
if (_pospunkt > -1)
|
||||||
|
{
|
||||||
|
_analog = _name.substr(0, _pospunkt);
|
||||||
|
_roi = _name.substr(_pospunkt+1, _name.length() - _pospunkt - 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_analog = "default";
|
||||||
|
_roi = _name;
|
||||||
|
}
|
||||||
|
|
||||||
|
general *_ret = NULL;
|
||||||
|
|
||||||
|
for (int i = 0; i < GENERAL.size(); ++i)
|
||||||
|
if (GENERAL[i]->name == _analog)
|
||||||
|
_ret = GENERAL[i];
|
||||||
|
|
||||||
|
if (!_create) // nicht gefunden und soll auch nicht erzeugt werden
|
||||||
|
return _ret;
|
||||||
|
|
||||||
|
if (_ret == NULL)
|
||||||
|
{
|
||||||
|
_ret = new general;
|
||||||
|
_ret->name = _analog;
|
||||||
|
GENERAL.push_back(_ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
roi* neuroi = new roi;
|
||||||
|
neuroi->name = _roi;
|
||||||
|
_ret->ROI.push_back(neuroi);
|
||||||
|
|
||||||
|
printf("GetGENERAL - GENERAL %s - roi %s\n", _analog.c_str(), _roi.c_str());
|
||||||
|
|
||||||
|
return _ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
string ClassFlowCNNGeneral::getHTMLSingleStep(string host)
|
||||||
|
{
|
||||||
|
string result, zw;
|
||||||
|
std::vector<HTMLInfo*> htmlinfo;
|
||||||
|
|
||||||
|
result = "<p>Found ROIs: </p> <p><img src=\"" + host + "/img_tmp/alg_roi.jpg\"></p>\n";
|
||||||
|
result = result + "Analog Pointers: <p> ";
|
||||||
|
|
||||||
|
htmlinfo = GetHTMLInfo();
|
||||||
|
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||||
|
{
|
||||||
|
std::stringstream stream;
|
||||||
|
stream << std::fixed << std::setprecision(1) << htmlinfo[i]->val;
|
||||||
|
zw = stream.str();
|
||||||
|
|
||||||
|
result = result + "<img src=\"" + host + "/img_tmp/" + htmlinfo[i]->filename + "\"> " + zw;
|
||||||
|
delete htmlinfo[i];
|
||||||
|
}
|
||||||
|
htmlinfo.clear();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool ClassFlowCNNGeneral::doFlow(string time)
|
||||||
|
{
|
||||||
|
if (disabled)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!doAlignAndCut(time)){
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::doFlow nach Alignment");
|
||||||
|
|
||||||
|
doNeuralNetwork(time);
|
||||||
|
|
||||||
|
RemoveOldLogs();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlowCNNGeneral::doAlignAndCut(string time)
|
||||||
|
{
|
||||||
|
if (disabled)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
CAlignAndCutImage *caic = flowpostalignment->GetAlignAndCutImage();
|
||||||
|
|
||||||
|
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||||
|
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||||
|
{
|
||||||
|
printf("General %d - Align&Cut\n", i);
|
||||||
|
|
||||||
|
caic->CutAndSave(GENERAL[_ana]->ROI[i]->posx, GENERAL[_ana]->ROI[i]->posy, GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, GENERAL[_ana]->ROI[i]->image_org);
|
||||||
|
if (SaveAllFiles)
|
||||||
|
{
|
||||||
|
if (GENERAL[_ana]->name == "default")
|
||||||
|
GENERAL[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".jpg"));
|
||||||
|
else
|
||||||
|
GENERAL[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
GENERAL[_ana]->ROI[i]->image_org->Resize(modelxsize, modelysize, GENERAL[_ana]->ROI[i]->image);
|
||||||
|
if (SaveAllFiles)
|
||||||
|
{
|
||||||
|
if (GENERAL[_ana]->name == "default")
|
||||||
|
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".bmp"));
|
||||||
|
else
|
||||||
|
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".bmp"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClassFlowCNNGeneral::DrawROI(CImageBasis *_zw)
|
||||||
|
{
|
||||||
|
if (CNNType == Analogue)
|
||||||
|
{
|
||||||
|
int r = 0;
|
||||||
|
int g = 255;
|
||||||
|
int b = 0;
|
||||||
|
|
||||||
|
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||||
|
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||||
|
{
|
||||||
|
_zw->drawRect(GENERAL[_ana]->ROI[i]->posx, GENERAL[_ana]->ROI[i]->posy, GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, r, g, b, 1);
|
||||||
|
_zw->drawCircle((int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) (GENERAL[_ana]->ROI[i]->deltax/2), r, g, b, 2);
|
||||||
|
_zw->drawLine((int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) GENERAL[_ana]->ROI[i]->posy, (int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay), r, g, b, 2);
|
||||||
|
_zw->drawLine((int) GENERAL[_ana]->ROI[i]->posx, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), r, g, b, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int _dig = 0; _dig < GENERAL.size(); ++_dig)
|
||||||
|
for (int i = 0; i < GENERAL[_dig]->ROI.size(); ++i)
|
||||||
|
_zw->drawRect(GENERAL[_dig]->ROI[i]->posx, GENERAL[_dig]->ROI[i]->posy, GENERAL[_dig]->ROI[i]->deltax, GENERAL[_dig]->ROI[i]->deltay, 0, 0, (255 - _dig*100), 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
||||||
|
{
|
||||||
|
if (disabled)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
string logPath = CreateLogFolder(time);
|
||||||
|
|
||||||
|
CTfLiteClass *tflite = new CTfLiteClass;
|
||||||
|
string zwcnn = "/sdcard" + cnnmodelfile;
|
||||||
|
zwcnn = FormatFileName(zwcnn);
|
||||||
|
printf(zwcnn.c_str());printf("\n");
|
||||||
|
if (!tflite->LoadModel(zwcnn)) {
|
||||||
|
printf("Can't read model file /sdcard%s\n", cnnmodelfile.c_str());
|
||||||
|
LogFile.WriteToFile("Cannot load model");
|
||||||
|
|
||||||
|
delete tflite;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
tflite->MakeAllocate();
|
||||||
|
|
||||||
|
if (CNNType == AutoDetect)
|
||||||
|
{
|
||||||
|
int _anzoutputdimensions = tflite->GetAnzOutPut();
|
||||||
|
switch (_anzoutputdimensions)
|
||||||
|
{
|
||||||
|
case 2:
|
||||||
|
CNNType = Analogue;
|
||||||
|
printf("TFlite-Type set to Analogue\n");
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
CNNType = Digital;
|
||||||
|
printf("TFlite-Type set to Digital\n");
|
||||||
|
break;
|
||||||
|
case 22:
|
||||||
|
CNNType = DigitalHyprid;
|
||||||
|
printf("TFlite-Type set to DigitalHyprid\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("ERROR ERROR ERROR - tflite passt nicht zur Firmware - ERROR ERROR ERROR\n");
|
||||||
|
}
|
||||||
|
// flowpostprocessing->UpdateNachkommaDecimalShift();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||||
|
{
|
||||||
|
printf("General %d - TfLite\n", i);
|
||||||
|
|
||||||
|
switch (CNNType) {
|
||||||
|
case Analogue:
|
||||||
|
{
|
||||||
|
float f1, f2;
|
||||||
|
f1 = 0; f2 = 0;
|
||||||
|
|
||||||
|
tflite->LoadInputImageBasis(GENERAL[_ana]->ROI[i]->image);
|
||||||
|
tflite->Invoke();
|
||||||
|
if (debugdetailgeneral) LogFile.WriteToFile("Nach Invoke");
|
||||||
|
|
||||||
|
f1 = tflite->GetOutputValue(0);
|
||||||
|
f2 = tflite->GetOutputValue(1);
|
||||||
|
float result = fmod(atan2(f1, f2) / (M_PI * 2) + 2, 1);
|
||||||
|
GENERAL[_ana]->ROI[i]->result_float = result * 10;
|
||||||
|
printf("Result General(Analog)%i: %f\n", i, GENERAL[_ana]->ROI[i]->result_float);
|
||||||
|
if (isLogImage)
|
||||||
|
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, &GENERAL[_ana]->ROI[i]->result_float, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||||
|
} break;
|
||||||
|
case Digital:
|
||||||
|
{
|
||||||
|
GENERAL[_ana]->ROI[i]->result_klasse = 0;
|
||||||
|
GENERAL[_ana]->ROI[i]->result_klasse = tflite->GetClassFromImageBasis(GENERAL[_ana]->ROI[i]->image);
|
||||||
|
printf("Result General(Digit)%i: %d\n", i, GENERAL[_ana]->ROI[i]->result_klasse);
|
||||||
|
|
||||||
|
if (isLogImage)
|
||||||
|
{
|
||||||
|
if (isLogImageSelect)
|
||||||
|
{
|
||||||
|
if (LogImageSelect.find(GENERAL[_ana]->ROI[i]->name) != std::string::npos)
|
||||||
|
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case DigitalHyprid:
|
||||||
|
{
|
||||||
|
int _num, _nachkomma;
|
||||||
|
|
||||||
|
tflite->LoadInputImageBasis(GENERAL[_ana]->ROI[i]->image);
|
||||||
|
tflite->Invoke();
|
||||||
|
if (debugdetailgeneral) LogFile.WriteToFile("Nach Invoke");
|
||||||
|
|
||||||
|
_num = tflite->GetOutClassification(0, 10);
|
||||||
|
_nachkomma = tflite->GetOutClassification(11, 21);
|
||||||
|
|
||||||
|
|
||||||
|
string _zwres = "Nach Invoke - Nummer: " + to_string(_num) + " Nachkomma: " + to_string(_nachkomma);
|
||||||
|
if (debugdetailgeneral) LogFile.WriteToFile(_zwres);
|
||||||
|
|
||||||
|
if ((_num == 10) || (_nachkomma == 10)) // NaN detektiert
|
||||||
|
GENERAL[_ana]->ROI[i]->result_float = -1;
|
||||||
|
else
|
||||||
|
GENERAL[_ana]->ROI[i]->result_float = fmod((double) _num + (((double)_nachkomma)-5)/10 + (double) 10, 10);
|
||||||
|
|
||||||
|
printf("Result General(DigitalHyprid)%i: %f\n", i, GENERAL[_ana]->ROI[i]->result_float);
|
||||||
|
_zwres = "Result General(DigitalHyprid)" + to_string(i) + ": " + to_string(GENERAL[_ana]->ROI[i]->result_float);
|
||||||
|
if (debugdetailgeneral) LogFile.WriteToFile(_zwres);
|
||||||
|
|
||||||
|
if (isLogImage)
|
||||||
|
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, &GENERAL[_ana]->ROI[i]->result_float, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete tflite;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassFlowCNNGeneral::isExtendedResolution(int _number)
|
||||||
|
{
|
||||||
|
// if (extendedResolution && !(CNNType == Digital))
|
||||||
|
if (!(CNNType == Digital))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<HTMLInfo*> ClassFlowCNNGeneral::GetHTMLInfo()
|
||||||
|
{
|
||||||
|
std::vector<HTMLInfo*> result;
|
||||||
|
|
||||||
|
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||||
|
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||||
|
{
|
||||||
|
if (GENERAL[_ana]->name == "default")
|
||||||
|
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".bmp"));
|
||||||
|
else
|
||||||
|
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".bmp"));
|
||||||
|
|
||||||
|
|
||||||
|
HTMLInfo *zw = new HTMLInfo;
|
||||||
|
if (GENERAL[_ana]->name == "default")
|
||||||
|
{
|
||||||
|
zw->filename = GENERAL[_ana]->ROI[i]->name + ".bmp";
|
||||||
|
zw->filename_org = GENERAL[_ana]->ROI[i]->name + ".jpg";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
zw->filename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".bmp";
|
||||||
|
zw->filename_org = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CNNType == Digital)
|
||||||
|
zw->val = GENERAL[_ana]->ROI[i]->result_klasse;
|
||||||
|
else
|
||||||
|
zw->val = GENERAL[_ana]->ROI[i]->result_float;
|
||||||
|
zw->image = GENERAL[_ana]->ROI[i]->image;
|
||||||
|
zw->image_org = GENERAL[_ana]->ROI[i]->image_org;
|
||||||
|
|
||||||
|
// printf("Push %s\n", zw->filename.c_str());
|
||||||
|
|
||||||
|
result.push_back(zw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// printf("größe: %d\n", result.size());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ClassFlowCNNGeneral::getAnzahlGENERAL()
|
||||||
|
{
|
||||||
|
return GENERAL.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
string ClassFlowCNNGeneral::getNameGENERAL(int _analog)
|
||||||
|
{
|
||||||
|
if (_analog < GENERAL.size())
|
||||||
|
return GENERAL[_analog]->name;
|
||||||
|
|
||||||
|
return "GENERAL DOES NOT EXIST";
|
||||||
|
}
|
||||||
|
|
||||||
|
general* ClassFlowCNNGeneral::GetGENERAL(int _analog)
|
||||||
|
{
|
||||||
|
if (_analog < GENERAL.size())
|
||||||
|
return GENERAL[_analog];
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void ClassFlowCNNGeneral::UpdateNameNumbers(std::vector<std::string> *_name_numbers)
|
||||||
|
{
|
||||||
|
for (int _dig = 0; _dig < GENERAL.size(); _dig++)
|
||||||
|
{
|
||||||
|
std::string _name = GENERAL[_dig]->name;
|
||||||
|
bool found = false;
|
||||||
|
for (int i = 0; i < (*_name_numbers).size(); ++i)
|
||||||
|
{
|
||||||
|
if ((*_name_numbers)[i] == _name)
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
(*_name_numbers).push_back(_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
72
code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.h
Normal file
72
code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#ifndef __CLASSCNNGENERAL__
|
||||||
|
#define __CLASSCNNGENERAL__
|
||||||
|
|
||||||
|
#include"ClassFlowDefineTypes.h"
|
||||||
|
#include "ClassFlowAlignment.h"
|
||||||
|
// #include "ClassFlowPostProcessing.h"
|
||||||
|
|
||||||
|
|
||||||
|
enum t_CNNType {
|
||||||
|
AutoDetect,
|
||||||
|
Analogue,
|
||||||
|
Digital,
|
||||||
|
DigitalHyprid,
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
class ClassFlowCNNGeneral :
|
||||||
|
public ClassFlowImage
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
t_CNNType CNNType;
|
||||||
|
std::vector<general*> GENERAL;
|
||||||
|
|
||||||
|
string cnnmodelfile;
|
||||||
|
int modelxsize, modelysize;
|
||||||
|
bool isLogImageSelect;
|
||||||
|
string LogImageSelect;
|
||||||
|
ClassFlowAlignment* flowpostalignment;
|
||||||
|
// ClassFlowPostProcessing *flowpostprocessing = NULL;
|
||||||
|
bool SaveAllFiles;
|
||||||
|
// bool extendedResolution;
|
||||||
|
|
||||||
|
int ZeigerEval(float zahl, int ziffer_vorgaenger);
|
||||||
|
int ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int eval_vorgaenger);
|
||||||
|
|
||||||
|
|
||||||
|
bool doNeuralNetwork(string time);
|
||||||
|
bool doAlignAndCut(string time);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype = AutoDetect);
|
||||||
|
|
||||||
|
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||||
|
bool doFlow(string time);
|
||||||
|
|
||||||
|
string getHTMLSingleStep(string host);
|
||||||
|
string getReadout(int _analog, bool _extendedResolution);
|
||||||
|
|
||||||
|
void DrawROI(CImageBasis *_zw);
|
||||||
|
|
||||||
|
std::vector<HTMLInfo*> GetHTMLInfo();
|
||||||
|
|
||||||
|
// int AnzahlROIs(int _analog);
|
||||||
|
int getAnzahlGENERAL();
|
||||||
|
general* GetGENERAL(int _analog);
|
||||||
|
general* GetGENERAL(string _name, bool _create);
|
||||||
|
general* FindGENERAL(string _name_number);
|
||||||
|
string getNameGENERAL(int _analog);
|
||||||
|
|
||||||
|
bool isExtendedResolution(int _number = 0);
|
||||||
|
|
||||||
|
// void setPostprocessing(ClassFlowPostProcessing *_fpp){flowpostprocessing = _fpp;};
|
||||||
|
|
||||||
|
void UpdateNameNumbers(std::vector<std::string> *_name_numbers);
|
||||||
|
|
||||||
|
t_CNNType getCNNType(){return CNNType;};
|
||||||
|
|
||||||
|
string name(){return "ClassFlowCNNGeneral";};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "ClassFlowControll.h"
|
#include "ClassFlowControll.h"
|
||||||
|
|
||||||
#include "connect_wlan.h"
|
#include "connect_wlan.h"
|
||||||
|
#include "read_wlanini.h"
|
||||||
|
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
|
|
||||||
@@ -11,6 +12,9 @@
|
|||||||
#include "Helper.h"
|
#include "Helper.h"
|
||||||
#include "server_ota.h"
|
#include "server_ota.h"
|
||||||
|
|
||||||
|
|
||||||
|
//#include "CImg.h"
|
||||||
|
|
||||||
#include "server_help.h"
|
#include "server_help.h"
|
||||||
|
|
||||||
//#define DEBUG_DETAIL_ON
|
//#define DEBUG_DETAIL_ON
|
||||||
@@ -21,17 +25,20 @@ static const char* TAG = "flow_controll";
|
|||||||
std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _host){
|
std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _host){
|
||||||
std::string _classname = "";
|
std::string _classname = "";
|
||||||
std::string result = "";
|
std::string result = "";
|
||||||
|
// printf("_stepname: %s\n", _stepname.c_str());
|
||||||
if ((_stepname.compare("[MakeImage]") == 0) || (_stepname.compare(";[MakeImage]") == 0)){
|
if ((_stepname.compare("[MakeImage]") == 0) || (_stepname.compare(";[MakeImage]") == 0)){
|
||||||
_classname = "ClassFlowMakeImage";
|
_classname = "ClassFlowMakeImage";
|
||||||
}
|
}
|
||||||
if ((_stepname.compare("[Alignment]") == 0) || (_stepname.compare(";[Alignment]") == 0)){
|
if ((_stepname.compare("[Alignment]") == 0) || (_stepname.compare(";[Alignment]") == 0)){
|
||||||
_classname = "ClassFlowAlignment";
|
_classname = "ClassFlowAlignment";
|
||||||
}
|
}
|
||||||
if ((_stepname.compare("[Digits]") == 0) || (_stepname.compare(";[Digits]") == 0)){
|
if ((_stepname.compare(0, 7, "[Digits") == 0) || (_stepname.compare(0, 8, ";[Digits") == 0)) {
|
||||||
_classname = "ClassFlowDigit";
|
// if ((_stepname.compare("[Digits]") == 0) || (_stepname.compare(";[Digits]") == 0)){
|
||||||
|
// printf("Digits!!!\n");
|
||||||
|
_classname = "ClassFlowCNNGeneral";
|
||||||
}
|
}
|
||||||
if ((_stepname.compare("[Analog]") == 0) || (_stepname.compare(";[Analog]") == 0)){
|
if ((_stepname.compare("[Analog]") == 0) || (_stepname.compare(";[Analog]") == 0)){
|
||||||
_classname = "ClassFlowAnalog";
|
_classname = "ClassFlowCNNGeneral";
|
||||||
}
|
}
|
||||||
if ((_stepname.compare("[MQTT]") == 0) || (_stepname.compare(";[MQTT]") == 0)){
|
if ((_stepname.compare("[MQTT]") == 0) || (_stepname.compare(";[MQTT]") == 0)){
|
||||||
_classname = "ClassFlowMQTT";
|
_classname = "ClassFlowMQTT";
|
||||||
@@ -47,11 +54,33 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<HTMLInfo*> ClassFlowControll::GetAllDigital()
|
|
||||||
|
std::string ClassFlowControll::TranslateAktstatus(std::string _input)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < FlowControll.size(); ++i)
|
if (_input.compare("ClassFlowMakeImage") == 0)
|
||||||
if (FlowControll[i]->name().compare("ClassFlowDigit") == 0)
|
return ("Take Image");
|
||||||
return ((ClassFlowDigit*) (FlowControll[i]))->GetHTMLInfo();
|
if (_input.compare("ClassFlowAlignment") == 0)
|
||||||
|
return ("Aligning");
|
||||||
|
//if (_input.compare("ClassFlowAnalog") == 0)
|
||||||
|
// return ("Analog ROIs");
|
||||||
|
if (_input.compare("ClassFlowCNNGeneral") == 0)
|
||||||
|
return ("Digitalization of ROIs");
|
||||||
|
if (_input.compare("ClassFlowMQTT") == 0)
|
||||||
|
return ("Sending MQTT");
|
||||||
|
if (_input.compare("ClassFlowPostProcessing") == 0)
|
||||||
|
return ("Processing");
|
||||||
|
|
||||||
|
return "Unkown Status";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<HTMLInfo*> ClassFlowControll::GetAllDigital()
|
||||||
|
{
|
||||||
|
if (flowdigit)
|
||||||
|
{
|
||||||
|
printf("ClassFlowControll::GetAllDigital - flowdigit != NULL\n");
|
||||||
|
return flowdigit->GetHTMLInfo();
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<HTMLInfo*> empty;
|
std::vector<HTMLInfo*> empty;
|
||||||
return empty;
|
return empty;
|
||||||
@@ -59,14 +88,43 @@ std::vector<HTMLInfo*> ClassFlowControll::GetAllDigital()
|
|||||||
|
|
||||||
std::vector<HTMLInfo*> ClassFlowControll::GetAllAnalog()
|
std::vector<HTMLInfo*> ClassFlowControll::GetAllAnalog()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < FlowControll.size(); ++i)
|
if (flowanalog)
|
||||||
if (FlowControll[i]->name().compare("ClassFlowAnalog") == 0)
|
return flowanalog->GetHTMLInfo();
|
||||||
return ((ClassFlowAnalog*) (FlowControll[i]))->GetHTMLInfo();
|
|
||||||
|
|
||||||
std::vector<HTMLInfo*> empty;
|
std::vector<HTMLInfo*> empty;
|
||||||
return empty;
|
return empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t_CNNType ClassFlowControll::GetTypeDigital()
|
||||||
|
{
|
||||||
|
if (flowdigit)
|
||||||
|
return flowdigit->getCNNType();
|
||||||
|
|
||||||
|
return t_CNNType::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_CNNType ClassFlowControll::GetTypeAnalog()
|
||||||
|
{
|
||||||
|
if (flowanalog)
|
||||||
|
return flowanalog->getCNNType();
|
||||||
|
|
||||||
|
return t_CNNType::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
string ClassFlowControll::GetMQTTMainTopic()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < FlowControll.size(); ++i)
|
||||||
|
if (FlowControll[i]->name().compare("ClassFlowMQTT") == 0)
|
||||||
|
return ((ClassFlowMQTT*) (FlowControll[i]))->GetMQTTMainTopic();
|
||||||
|
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void ClassFlowControll::SetInitialParameter(void)
|
void ClassFlowControll::SetInitialParameter(void)
|
||||||
{
|
{
|
||||||
@@ -78,7 +136,7 @@ void ClassFlowControll::SetInitialParameter(void)
|
|||||||
flowpostprocessing = NULL;
|
flowpostprocessing = NULL;
|
||||||
disabled = false;
|
disabled = false;
|
||||||
aktRunNr = 0;
|
aktRunNr = 0;
|
||||||
aktstatus = "Startup";
|
aktstatus = "Booting ...";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,19 +164,20 @@ ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
|
|||||||
}
|
}
|
||||||
if (toUpper(_type).compare("[ANALOG]") == 0)
|
if (toUpper(_type).compare("[ANALOG]") == 0)
|
||||||
{
|
{
|
||||||
cfc = new ClassFlowAnalog(&FlowControll);
|
cfc = new ClassFlowCNNGeneral(flowalignment);
|
||||||
flowanalog = (ClassFlowAnalog*) cfc;
|
flowanalog = (ClassFlowCNNGeneral*) cfc;
|
||||||
}
|
}
|
||||||
if (toUpper(_type).compare("[DIGITS]") == 0)
|
if (toUpper(_type).compare(0, 7, "[DIGITS") == 0)
|
||||||
{
|
{
|
||||||
cfc = new ClassFlowDigit(&FlowControll);
|
cfc = new ClassFlowCNNGeneral(flowalignment);
|
||||||
flowdigit = (ClassFlowDigit*) cfc;
|
flowdigit = (ClassFlowCNNGeneral*) cfc;
|
||||||
}
|
}
|
||||||
if (toUpper(_type).compare("[MQTT]") == 0)
|
if (toUpper(_type).compare("[MQTT]") == 0)
|
||||||
cfc = new ClassFlowMQTT(&FlowControll);
|
cfc = new ClassFlowMQTT(&FlowControll);
|
||||||
|
|
||||||
if (toUpper(_type).compare("[POSTPROCESSING]") == 0)
|
if (toUpper(_type).compare("[POSTPROCESSING]") == 0)
|
||||||
{
|
{
|
||||||
cfc = new ClassFlowPostProcessing(&FlowControll);
|
cfc = new ClassFlowPostProcessing(&FlowControll, flowanalog, flowdigit);
|
||||||
flowpostprocessing = (ClassFlowPostProcessing*) cfc;
|
flowpostprocessing = (ClassFlowPostProcessing*) cfc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,14 +222,17 @@ void ClassFlowControll::InitFlow(std::string config)
|
|||||||
cfc = CreateClassFlow(line);
|
cfc = CreateClassFlow(line);
|
||||||
if (cfc)
|
if (cfc)
|
||||||
{
|
{
|
||||||
printf("Start ReadParameter\n");
|
printf("Start ReadParameter (%s)\n", line.c_str());
|
||||||
cfc->ReadParameter(pFile, line);
|
cfc->ReadParameter(pFile, line);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fgets(zw, 1024, pFile);
|
line = "";
|
||||||
printf("%s", zw);
|
if (fgets(zw, 1024, pFile) && !feof(pFile))
|
||||||
line = std::string(zw);
|
{
|
||||||
|
printf("Read: %s", zw);
|
||||||
|
line = std::string(zw);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,8 +240,8 @@ void ClassFlowControll::InitFlow(std::string config)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ClassFlowControll::getActStatus(){
|
std::string* ClassFlowControll::getActStatus(){
|
||||||
return aktstatus;
|
return &aktstatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClassFlowControll::doFlowMakeImageOnly(string time){
|
void ClassFlowControll::doFlowMakeImageOnly(string time){
|
||||||
@@ -188,9 +250,9 @@ void ClassFlowControll::doFlowMakeImageOnly(string time){
|
|||||||
for (int i = 0; i < FlowControll.size(); ++i)
|
for (int i = 0; i < FlowControll.size(); ++i)
|
||||||
{
|
{
|
||||||
if (FlowControll[i]->name() == "ClassFlowMakeImage") {
|
if (FlowControll[i]->name() == "ClassFlowMakeImage") {
|
||||||
zw_time = gettimestring("%Y%m%d-%H%M%S");
|
// zw_time = gettimestring("%Y%m%d-%H%M%S");
|
||||||
aktstatus = zw_time + ": " + FlowControll[i]->name();
|
zw_time = gettimestring("%H:%M:%S");
|
||||||
string zw = "FlowControll.doFlowMakeImageOnly - " + FlowControll[i]->name();
|
aktstatus = TranslateAktstatus(FlowControll[i]->name()) + " (" + zw_time + ")";
|
||||||
FlowControll[i]->doFlow(time);
|
FlowControll[i]->doFlow(time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,13 +267,16 @@ bool ClassFlowControll::doFlow(string time)
|
|||||||
int repeat = 0;
|
int repeat = 0;
|
||||||
|
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
LogFile.WriteHeapInfo("ClassFlowAnalog::doFlow - Start");
|
LogFile.WriteHeapInfo("ClassFlowControll::doFlow - Start");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (int i = 0; i < FlowControll.size(); ++i)
|
for (int i = 0; i < FlowControll.size(); ++i)
|
||||||
{
|
{
|
||||||
zw_time = gettimestring("%Y%m%d-%H%M%S");
|
zw_time = gettimestring("%H:%M:%S");
|
||||||
aktstatus = zw_time + ": " + FlowControll[i]->name();
|
aktstatus = TranslateAktstatus(FlowControll[i]->name()) + " (" + zw_time + ")";
|
||||||
|
|
||||||
|
// zw_time = gettimestring("%Y%m%d-%H%M%S");
|
||||||
|
// aktstatus = zw_time + ": " + FlowControll[i]->name();
|
||||||
|
|
||||||
|
|
||||||
string zw = "FlowControll.doFlow - " + FlowControll[i]->name();
|
string zw = "FlowControll.doFlow - " + FlowControll[i]->name();
|
||||||
@@ -220,7 +285,7 @@ bool ClassFlowControll::doFlow(string time)
|
|||||||
if (!FlowControll[i]->doFlow(time)){
|
if (!FlowControll[i]->doFlow(time)){
|
||||||
repeat++;
|
repeat++;
|
||||||
LogFile.WriteToFile("Fehler im vorheriger Schritt - wird zum " + to_string(repeat) + ". Mal wiederholt");
|
LogFile.WriteToFile("Fehler im vorheriger Schritt - wird zum " + to_string(repeat) + ". Mal wiederholt");
|
||||||
i = -1; // vorheriger Schritt muss wiederholt werden (vermutlich Bilder aufnehmen)
|
if (i) i -= 1; // vorheriger Schritt muss wiederholt werden (vermutlich Bilder aufnehmen)
|
||||||
result = false;
|
result = false;
|
||||||
if (repeat > 5) {
|
if (repeat > 5) {
|
||||||
LogFile.WriteToFile("Wiederholung 5x nicht erfolgreich --> reboot");
|
LogFile.WriteToFile("Wiederholung 5x nicht erfolgreich --> reboot");
|
||||||
@@ -234,28 +299,56 @@ bool ClassFlowControll::doFlow(string time)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
LogFile.WriteHeapInfo("ClassFlowAnalog::doFlow");
|
LogFile.WriteHeapInfo("ClassFlowControll::doFlow");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
zw_time = gettimestring("%Y%m%d-%H%M%S");
|
zw_time = gettimestring("%H:%M:%S");
|
||||||
aktstatus = zw_time + ": Flow is done";
|
aktstatus = "Flow finished (" + zw_time + ")";
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClassFlowControll::UpdateAktStatus(std::string _flow)
|
|
||||||
|
string ClassFlowControll::getReadoutAll(int _type)
|
||||||
{
|
{
|
||||||
aktstatus = gettimestring("%Y%m%d-%H%M%S");
|
std::string out = "";
|
||||||
aktstatus = aktstatus + "\t" + std::to_string(aktRunNr) + "\t";
|
if (flowpostprocessing)
|
||||||
|
{
|
||||||
if (_flow == "ClassFlowMakeImage")
|
std::vector<NumberPost*> *numbers = flowpostprocessing->GetNumbers();
|
||||||
aktstatus = aktstatus + "Taking Raw Image";
|
|
||||||
else
|
|
||||||
if (_flow == "ClassFlowAlignment")
|
|
||||||
aktstatus = aktstatus + "Aligning Image";
|
|
||||||
|
|
||||||
|
for (int i = 0; i < (*numbers).size(); ++i)
|
||||||
|
{
|
||||||
|
out = out + (*numbers)[i]->name + "\t";
|
||||||
|
switch (_type) {
|
||||||
|
case READOUT_TYPE_VALUE:
|
||||||
|
out = out + (*numbers)[i]->ReturnValueNoError;
|
||||||
|
break;
|
||||||
|
case READOUT_TYPE_PREVALUE:
|
||||||
|
if (flowpostprocessing->PreValueUse)
|
||||||
|
{
|
||||||
|
if ((*numbers)[i]->PreValueOkay)
|
||||||
|
out = out + (*numbers)[i]->ReturnPreValue;
|
||||||
|
else
|
||||||
|
out = out + "PreValue too old";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
out = out + "PreValue deactivated";
|
||||||
|
break;
|
||||||
|
case READOUT_TYPE_RAWVALUE:
|
||||||
|
out = out + (*numbers)[i]->ReturnRawValue;
|
||||||
|
break;
|
||||||
|
case READOUT_TYPE_ERROR:
|
||||||
|
out = out + (*numbers)[i]->ErrorMessageText;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i < (*numbers).size()-1)
|
||||||
|
out = out + "\r\n";
|
||||||
|
}
|
||||||
|
// printf("OUT: %s", out.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
string ClassFlowControll::getReadout(bool _rawvalue = false, bool _noerror = false)
|
string ClassFlowControll::getReadout(bool _rawvalue = false, bool _noerror = false)
|
||||||
@@ -281,17 +374,17 @@ string ClassFlowControll::getReadout(bool _rawvalue = false, bool _noerror = fal
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
string ClassFlowControll::GetPrevalue()
|
string ClassFlowControll::GetPrevalue(std::string _number)
|
||||||
{
|
{
|
||||||
if (flowpostprocessing)
|
if (flowpostprocessing)
|
||||||
{
|
{
|
||||||
return flowpostprocessing->GetPreValue();
|
return flowpostprocessing->GetPreValue(_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ClassFlowControll::UpdatePrevalue(std::string _newvalue)
|
std::string ClassFlowControll::UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern)
|
||||||
{
|
{
|
||||||
float zw;
|
float zw;
|
||||||
char* p;
|
char* p;
|
||||||
@@ -313,7 +406,7 @@ std::string ClassFlowControll::UpdatePrevalue(std::string _newvalue)
|
|||||||
|
|
||||||
if (flowpostprocessing)
|
if (flowpostprocessing)
|
||||||
{
|
{
|
||||||
flowpostprocessing->SavePreValue(zw);
|
flowpostprocessing->SetPreValue(zw, _numbers, _extern);
|
||||||
return _newvalue;
|
return _newvalue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,6 +498,7 @@ bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int ClassFlowControll::CleanTempFolder() {
|
int ClassFlowControll::CleanTempFolder() {
|
||||||
const char* folderPath = "/sdcard/img_tmp";
|
const char* folderPath = "/sdcard/img_tmp";
|
||||||
|
|
||||||
@@ -438,7 +532,7 @@ int ClassFlowControll::CleanTempFolder() {
|
|||||||
|
|
||||||
esp_err_t ClassFlowControll::SendRawJPG(httpd_req_t *req)
|
esp_err_t ClassFlowControll::SendRawJPG(httpd_req_t *req)
|
||||||
{
|
{
|
||||||
return flowmakeimage->SendRawJPG(req);
|
return flowmakeimage != NULL ? flowmakeimage->SendRawJPG(req) : ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -450,51 +544,67 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
|||||||
esp_err_t result = ESP_FAIL;
|
esp_err_t result = ESP_FAIL;
|
||||||
bool Dodelete = false;
|
bool Dodelete = false;
|
||||||
|
|
||||||
|
if (flowalignment == NULL)
|
||||||
|
{
|
||||||
|
printf("Can't continue, flowalignment is NULL\n");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
if (_fn == "alg.jpg")
|
if (_fn == "alg.jpg")
|
||||||
{
|
{
|
||||||
_send = flowalignment->ImageBasis;
|
_send = flowalignment->ImageBasis;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
|
||||||
|
|
||||||
if (_fn == "alg_roi.jpg")
|
|
||||||
{
|
{
|
||||||
CImageBasis* _imgzw = new CImageBasis(flowalignment->ImageBasis);
|
if (_fn == "alg_roi.jpg")
|
||||||
flowalignment->DrawRef(_imgzw);
|
{
|
||||||
if (flowdigit) flowdigit->DrawROI(_imgzw);
|
CImageBasis* _imgzw = new CImageBasis(flowalignment->ImageBasis);
|
||||||
if (flowanalog) flowanalog->DrawROI(_imgzw);
|
flowalignment->DrawRef(_imgzw);
|
||||||
_send = _imgzw;
|
if (flowdigit) flowdigit->DrawROI(_imgzw);
|
||||||
Dodelete = true;
|
if (flowanalog) flowanalog->DrawROI(_imgzw);
|
||||||
}
|
_send = _imgzw;
|
||||||
|
Dodelete = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::vector<HTMLInfo*> htmlinfo;
|
||||||
|
htmlinfo = GetAllDigital();
|
||||||
|
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||||
|
{
|
||||||
|
if (_fn == htmlinfo[i]->filename)
|
||||||
|
{
|
||||||
|
if (htmlinfo[i]->image)
|
||||||
|
_send = htmlinfo[i]->image;
|
||||||
|
}
|
||||||
|
if (_fn == htmlinfo[i]->filename_org)
|
||||||
|
{
|
||||||
|
if (htmlinfo[i]->image_org)
|
||||||
|
_send = htmlinfo[i]->image_org;
|
||||||
|
}
|
||||||
|
delete htmlinfo[i];
|
||||||
|
}
|
||||||
|
htmlinfo.clear();
|
||||||
|
|
||||||
std::vector<HTMLInfo*> htmlinfo;
|
if (!_send)
|
||||||
htmlinfo = GetAllDigital();
|
{
|
||||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
htmlinfo = GetAllAnalog();
|
||||||
{
|
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||||
if (_fn == htmlinfo[i]->filename)
|
{
|
||||||
{
|
if (_fn == htmlinfo[i]->filename)
|
||||||
if (htmlinfo[i]->image)
|
{
|
||||||
_send = htmlinfo[i]->image;
|
if (htmlinfo[i]->image)
|
||||||
}
|
_send = htmlinfo[i]->image;
|
||||||
if (_fn == htmlinfo[i]->filename_org)
|
}
|
||||||
{
|
if (_fn == htmlinfo[i]->filename_org)
|
||||||
if (htmlinfo[i]->image_org)
|
{
|
||||||
_send = htmlinfo[i]->image_org;
|
if (htmlinfo[i]->image_org)
|
||||||
}
|
_send = htmlinfo[i]->image_org;
|
||||||
}
|
}
|
||||||
|
delete htmlinfo[i];
|
||||||
|
}
|
||||||
|
htmlinfo.clear();
|
||||||
|
|
||||||
htmlinfo = GetAllAnalog();
|
}
|
||||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
|
||||||
{
|
|
||||||
if (_fn == htmlinfo[i]->filename)
|
|
||||||
{
|
|
||||||
if (htmlinfo[i]->image)
|
|
||||||
_send = htmlinfo[i]->image;
|
|
||||||
}
|
|
||||||
if (_fn == htmlinfo[i]->filename_org)
|
|
||||||
{
|
|
||||||
if (htmlinfo[i]->image_org)
|
|
||||||
_send = htmlinfo[i]->image_org;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,4 +624,37 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string ClassFlowControll::getJSON()
|
||||||
|
{
|
||||||
|
std::vector<NumberPost*>* NUMBERS = flowpostprocessing->GetNumbers();
|
||||||
|
|
||||||
|
std::string json="{\n";
|
||||||
|
|
||||||
|
for (int i = 0; i < (*NUMBERS).size(); ++i)
|
||||||
|
{
|
||||||
|
json += "\"" + (*NUMBERS)[i]->name + "\":\n";
|
||||||
|
json += " {\n";
|
||||||
|
if ((*NUMBERS)[i]->ReturnValueNoError.length() > 0)
|
||||||
|
json += " \"value\": " + (*NUMBERS)[i]->ReturnValueNoError + ",\n";
|
||||||
|
else
|
||||||
|
json += " \"value\": \"\",\n";
|
||||||
|
json += " \"raw\": \"" + (*NUMBERS)[i]->ReturnRawValue + "\",\n";
|
||||||
|
json += " \"error\": \"" + (*NUMBERS)[i]->ErrorMessageText + "\",\n";
|
||||||
|
if ((*NUMBERS)[i]->ReturnRateValue.length() > 0)
|
||||||
|
json += " \"rate\": " + (*NUMBERS)[i]->ReturnRateValue + ",\n";
|
||||||
|
else
|
||||||
|
json += " \"rate\": \"\",\n";
|
||||||
|
|
||||||
|
json += " \"timestamp\": \"" + (*NUMBERS)[i]->timeStamp + "\"\n";
|
||||||
|
if ((i+1) < (*NUMBERS).size())
|
||||||
|
json += " },\n";
|
||||||
|
else
|
||||||
|
json += " }\n";
|
||||||
|
}
|
||||||
|
json += "}";
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
#pragma once
|
#ifndef __FLOWCONTROLL__
|
||||||
|
#define __FLOWCONTROLL__
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "ClassFlow.h"
|
#include "ClassFlow.h"
|
||||||
#include "ClassFlowMakeImage.h"
|
#include "ClassFlowMakeImage.h"
|
||||||
#include "ClassFlowAlignment.h"
|
#include "ClassFlowAlignment.h"
|
||||||
#include "ClassFlowDigit.h"
|
#include "ClassFlowCNNGeneral.h"
|
||||||
#include "ClassFlowAnalog.h"
|
|
||||||
#include "ClassFlowPostProcessing.h"
|
#include "ClassFlowPostProcessing.h"
|
||||||
#include "ClassFlowMQTT.h"
|
#include "ClassFlowMQTT.h"
|
||||||
|
#include "ClassFlowCNNGeneral.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define READOUT_TYPE_VALUE 0
|
||||||
|
#define READOUT_TYPE_PREVALUE 1
|
||||||
|
#define READOUT_TYPE_RAWVALUE 2
|
||||||
|
#define READOUT_TYPE_ERROR 3
|
||||||
|
|
||||||
|
|
||||||
class ClassFlowControll :
|
class ClassFlowControll :
|
||||||
@@ -18,8 +25,9 @@ protected:
|
|||||||
std::vector<ClassFlow*> FlowControll;
|
std::vector<ClassFlow*> FlowControll;
|
||||||
ClassFlowPostProcessing* flowpostprocessing;
|
ClassFlowPostProcessing* flowpostprocessing;
|
||||||
ClassFlowAlignment* flowalignment;
|
ClassFlowAlignment* flowalignment;
|
||||||
ClassFlowAnalog* flowanalog;
|
ClassFlowCNNGeneral* flowanalog;
|
||||||
ClassFlowDigit* flowdigit;
|
ClassFlowCNNGeneral* flowdigit;
|
||||||
|
// ClassFlowDigit* flowdigit;
|
||||||
ClassFlowMakeImage* flowmakeimage;
|
ClassFlowMakeImage* flowmakeimage;
|
||||||
ClassFlow* CreateClassFlow(std::string _type);
|
ClassFlow* CreateClassFlow(std::string _type);
|
||||||
|
|
||||||
@@ -30,17 +38,21 @@ protected:
|
|||||||
std::string aktstatus;
|
std::string aktstatus;
|
||||||
int aktRunNr;
|
int aktRunNr;
|
||||||
|
|
||||||
void UpdateAktStatus(std::string _flow);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void InitFlow(std::string config);
|
void InitFlow(std::string config);
|
||||||
bool doFlow(string time);
|
bool doFlow(string time);
|
||||||
void doFlowMakeImageOnly(string time);
|
void doFlowMakeImageOnly(string time);
|
||||||
bool getStatusSetupModus(){return SetupModeActive;};
|
bool getStatusSetupModus(){return SetupModeActive;};
|
||||||
string getReadout(bool _rawvalue, bool _noerror);
|
string getReadout(bool _rawvalue, bool _noerror);
|
||||||
string UpdatePrevalue(std::string _newvalue);
|
string getReadoutAll(int _type);
|
||||||
string GetPrevalue();
|
string UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern);
|
||||||
|
string GetPrevalue(std::string _number = "");
|
||||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||||
|
string getJSON();
|
||||||
|
|
||||||
|
string TranslateAktstatus(std::string _input);
|
||||||
|
|
||||||
|
string GetMQTTMainTopic();
|
||||||
|
|
||||||
esp_err_t GetJPGStream(std::string _fn, httpd_req_t *req);
|
esp_err_t GetJPGStream(std::string _fn, httpd_req_t *req);
|
||||||
esp_err_t SendRawJPG(httpd_req_t *req);
|
esp_err_t SendRawJPG(httpd_req_t *req);
|
||||||
@@ -49,14 +61,19 @@ public:
|
|||||||
|
|
||||||
bool isAutoStart(long &_intervall);
|
bool isAutoStart(long &_intervall);
|
||||||
|
|
||||||
std::string getActStatus();
|
std::string* getActStatus();
|
||||||
|
|
||||||
std::vector<HTMLInfo*> GetAllDigital();
|
std::vector<HTMLInfo*> GetAllDigital();
|
||||||
std::vector<HTMLInfo*> GetAllAnalog();
|
std::vector<HTMLInfo*> GetAllAnalog();
|
||||||
|
|
||||||
|
t_CNNType GetTypeDigital();
|
||||||
|
t_CNNType GetTypeAnalog();
|
||||||
|
|
||||||
int CleanTempFolder();
|
int CleanTempFolder();
|
||||||
|
|
||||||
string name(){return "ClassFlowControll";};
|
string name(){return "ClassFlowControll";};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
53
code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h
Normal file
53
code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#ifndef __CLASSFLOWIMAGE_CLASS__
|
||||||
|
#define __CLASSFLOWIMAGE_CLASS__
|
||||||
|
|
||||||
|
#include "ClassFlowImage.h"
|
||||||
|
|
||||||
|
struct roi {
|
||||||
|
int posx, posy, deltax, deltay;
|
||||||
|
float result_float;
|
||||||
|
int result_klasse;
|
||||||
|
string name;
|
||||||
|
CImageBasis *image, *image_org;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct general {
|
||||||
|
string name;
|
||||||
|
std::vector<roi*> ROI;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct NumberPost {
|
||||||
|
float MaxRateValue;
|
||||||
|
bool useMaxRateValue;
|
||||||
|
bool ErrorMessage;
|
||||||
|
bool PreValueOkay;
|
||||||
|
bool AllowNegativeRates;
|
||||||
|
bool checkDigitIncreaseConsistency;
|
||||||
|
time_t lastvalue;
|
||||||
|
string timeStamp;
|
||||||
|
float FlowRateAct; // m3 / min
|
||||||
|
float PreValue; // letzter Wert, der gut ausgelesen wurde
|
||||||
|
float Value; // letzer ausgelesener Wert, inkl. Korrekturen
|
||||||
|
string ReturnRateValue; // RückgabewertRate
|
||||||
|
string ReturnRawValue; // Rohwert (mit N & führenden 0)
|
||||||
|
string ReturnValue; // korrigierter Rückgabewert, ggf. mit Fehlermeldung
|
||||||
|
string ReturnPreValue; // korrigierter Rückgabewert ohne Fehlermeldung
|
||||||
|
string ReturnValueNoError;
|
||||||
|
string ErrorMessageText; // Fehlermeldung bei Consistency Check
|
||||||
|
int AnzahlAnalog;
|
||||||
|
int AnzahlDigital;
|
||||||
|
int DecimalShift;
|
||||||
|
int DecimalShiftInitial;
|
||||||
|
int Nachkomma;
|
||||||
|
|
||||||
|
bool isExtendedResolution;
|
||||||
|
|
||||||
|
general *digit_roi;
|
||||||
|
general *analog_roi;
|
||||||
|
|
||||||
|
string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,275 +0,0 @@
|
|||||||
#include "ClassFlowDigit.h"
|
|
||||||
|
|
||||||
|
|
||||||
//#include "CFindTemplate.h"
|
|
||||||
//#include "CTfLiteClass.h"
|
|
||||||
|
|
||||||
// #define OHNETFLITE
|
|
||||||
|
|
||||||
#ifndef OHNETFLITE
|
|
||||||
#include "CTfLiteClass.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// #include "bitmap_image.hpp"
|
|
||||||
|
|
||||||
#include "ClassLogFile.h"
|
|
||||||
|
|
||||||
static const char* TAG = "flow_digital";
|
|
||||||
|
|
||||||
|
|
||||||
void ClassFlowDigit::SetInitialParameter(void)
|
|
||||||
{
|
|
||||||
string cnnmodelfile = "";
|
|
||||||
modelxsize = 1;
|
|
||||||
modelysize = 1;
|
|
||||||
ListFlowControll = NULL;
|
|
||||||
previousElement = NULL;
|
|
||||||
SaveAllFiles = false;
|
|
||||||
disabled = false;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassFlowDigit::ClassFlowDigit() : ClassFlowImage(TAG)
|
|
||||||
{
|
|
||||||
SetInitialParameter();
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassFlowDigit::ClassFlowDigit(std::vector<ClassFlow*>* lfc) : ClassFlowImage(lfc, TAG)
|
|
||||||
{
|
|
||||||
SetInitialParameter();
|
|
||||||
ListFlowControll = lfc;
|
|
||||||
|
|
||||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
|
||||||
{
|
|
||||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0)
|
|
||||||
{
|
|
||||||
flowpostalignment = (ClassFlowAlignment*) (*ListFlowControll)[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassFlowDigit::ClassFlowDigit(std::vector<ClassFlow*>* lfc, ClassFlow *_prev) : ClassFlowImage(lfc, _prev, TAG)
|
|
||||||
{
|
|
||||||
SetInitialParameter();
|
|
||||||
ListFlowControll = lfc;
|
|
||||||
previousElement = _prev;
|
|
||||||
|
|
||||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
|
||||||
{
|
|
||||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0)
|
|
||||||
{
|
|
||||||
flowpostalignment = (ClassFlowAlignment*) (*ListFlowControll)[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string ClassFlowDigit::getReadout()
|
|
||||||
{
|
|
||||||
string rst = "";
|
|
||||||
|
|
||||||
for (int i = 0; i < ROI.size(); ++i)
|
|
||||||
{
|
|
||||||
if (ROI[i]->resultklasse == 10)
|
|
||||||
rst = rst + "N";
|
|
||||||
else
|
|
||||||
rst = rst + std::to_string(ROI[i]->resultklasse);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rst;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClassFlowDigit::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|
||||||
{
|
|
||||||
std::vector<string> zerlegt;
|
|
||||||
|
|
||||||
aktparamgraph = trim(aktparamgraph);
|
|
||||||
|
|
||||||
if (aktparamgraph.size() == 0)
|
|
||||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ((aktparamgraph.compare("[Digits]") != 0) && (aktparamgraph.compare(";[Digits]") != 0)) // Paragraph passt nich zu MakeImage
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (aktparamgraph[0] == ';')
|
|
||||||
{
|
|
||||||
disabled = true;
|
|
||||||
while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph));
|
|
||||||
printf("[Digits] is disabled !!!\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph))
|
|
||||||
{
|
|
||||||
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
|
||||||
if ((zerlegt[0] == "LogImageLocation") && (zerlegt.size() > 1))
|
|
||||||
{
|
|
||||||
LogImageLocation = "/sdcard" + zerlegt[1];
|
|
||||||
isLogImage = true;
|
|
||||||
}
|
|
||||||
if ((zerlegt[0] == "Model") && (zerlegt.size() > 1))
|
|
||||||
{
|
|
||||||
cnnmodelfile = zerlegt[1];
|
|
||||||
}
|
|
||||||
if ((zerlegt[0] == "ModelInputSize") && (zerlegt.size() > 2))
|
|
||||||
{
|
|
||||||
modelxsize = std::stoi(zerlegt[1]);
|
|
||||||
modelysize = std::stoi(zerlegt[2]);
|
|
||||||
}
|
|
||||||
if (zerlegt.size() >= 5)
|
|
||||||
{
|
|
||||||
roi* neuroi = new roi;
|
|
||||||
neuroi->name = zerlegt[0];
|
|
||||||
neuroi->posx = std::stoi(zerlegt[1]);
|
|
||||||
neuroi->posy = std::stoi(zerlegt[2]);
|
|
||||||
neuroi->deltax = std::stoi(zerlegt[3]);
|
|
||||||
neuroi->deltay = std::stoi(zerlegt[4]);
|
|
||||||
neuroi->resultklasse = -1;
|
|
||||||
neuroi->image = NULL;
|
|
||||||
neuroi->image_org = NULL;
|
|
||||||
ROI.push_back(neuroi);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((toUpper(zerlegt[0]) == "SAVEALLFILES") && (zerlegt.size() > 1))
|
|
||||||
{
|
|
||||||
if (toUpper(zerlegt[1]) == "TRUE")
|
|
||||||
SaveAllFiles = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < ROI.size(); ++i)
|
|
||||||
{
|
|
||||||
ROI[i]->image = new CImageBasis(modelxsize, modelysize, 3);
|
|
||||||
ROI[i]->image_org = new CImageBasis(ROI[i]->deltax, ROI[i]->deltay, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
string ClassFlowDigit::getHTMLSingleStep(string host)
|
|
||||||
{
|
|
||||||
string result, zw;
|
|
||||||
std::vector<HTMLInfo*> htmlinfo;
|
|
||||||
|
|
||||||
result = "<p>Found ROIs: </p> <p><img src=\"" + host + "/img_tmp/alg_roi.jpg\"></p>\n";
|
|
||||||
result = result + "Digital Counter: <p> ";
|
|
||||||
|
|
||||||
htmlinfo = GetHTMLInfo();
|
|
||||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
|
||||||
{
|
|
||||||
if (htmlinfo[i]->val == 10)
|
|
||||||
zw = "NaN";
|
|
||||||
else
|
|
||||||
{
|
|
||||||
zw = to_string((int) htmlinfo[i]->val);
|
|
||||||
}
|
|
||||||
result = result + "<img src=\"" + host + "/img_tmp/" + htmlinfo[i]->filename + "\"> " + zw;
|
|
||||||
delete htmlinfo[i];
|
|
||||||
}
|
|
||||||
htmlinfo.clear();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool ClassFlowDigit::doFlow(string time)
|
|
||||||
{
|
|
||||||
if (disabled)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!doAlignAndCut(time)){
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
doNeuralNetwork(time);
|
|
||||||
|
|
||||||
RemoveOldLogs();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClassFlowDigit::doAlignAndCut(string time)
|
|
||||||
{
|
|
||||||
if (disabled)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
CAlignAndCutImage *caic = flowpostalignment->GetAlignAndCutImage();
|
|
||||||
|
|
||||||
for (int i = 0; i < ROI.size(); ++i)
|
|
||||||
{
|
|
||||||
printf("DigitalDigit %d - Align&Cut\n", i);
|
|
||||||
|
|
||||||
caic->CutAndSave(ROI[i]->posx, ROI[i]->posy, ROI[i]->deltax, ROI[i]->deltay, ROI[i]->image_org);
|
|
||||||
if (SaveAllFiles) ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ROI[i]->name + ".jpg"));
|
|
||||||
|
|
||||||
ROI[i]->image_org->Resize(modelxsize, modelysize, ROI[i]->image);
|
|
||||||
if (SaveAllFiles) ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ROI[i]->name + ".bmp"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClassFlowDigit::doNeuralNetwork(string time)
|
|
||||||
{
|
|
||||||
if (disabled)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
string logPath = CreateLogFolder(time);
|
|
||||||
|
|
||||||
#ifndef OHNETFLITE
|
|
||||||
CTfLiteClass *tflite = new CTfLiteClass;
|
|
||||||
string zwcnn = FormatFileName("/sdcard" + cnnmodelfile);
|
|
||||||
printf(zwcnn.c_str());printf("\n");
|
|
||||||
tflite->LoadModel(zwcnn);
|
|
||||||
tflite->MakeAllocate();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (int i = 0; i < ROI.size(); ++i)
|
|
||||||
{
|
|
||||||
printf("DigitalDigit %d - TfLite\n", i);
|
|
||||||
|
|
||||||
ROI[i]->resultklasse = 0;
|
|
||||||
#ifndef OHNETFLITE
|
|
||||||
ROI[i]->resultklasse = tflite->GetClassFromImageBasis(ROI[i]->image);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
printf("Result Digit%i: %d\n", i, ROI[i]->resultklasse);
|
|
||||||
|
|
||||||
if (isLogImage)
|
|
||||||
{
|
|
||||||
LogImage(logPath, ROI[i]->name, NULL, &ROI[i]->resultklasse, time, ROI[i]->image_org);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifndef OHNETFLITE
|
|
||||||
delete tflite;
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClassFlowDigit::DrawROI(CImageBasis *_zw)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < ROI.size(); ++i)
|
|
||||||
_zw->drawRect(ROI[i]->posx, ROI[i]->posy, ROI[i]->deltax, ROI[i]->deltay, 0, 0, 255, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<HTMLInfo*> ClassFlowDigit::GetHTMLInfo()
|
|
||||||
{
|
|
||||||
std::vector<HTMLInfo*> result;
|
|
||||||
|
|
||||||
for (int i = 0; i < ROI.size(); ++i)
|
|
||||||
{
|
|
||||||
HTMLInfo *zw = new HTMLInfo;
|
|
||||||
zw->filename = ROI[i]->name + ".bmp";
|
|
||||||
zw->filename_org = ROI[i]->name + ".jpg";
|
|
||||||
zw->val = ROI[i]->resultklasse;
|
|
||||||
zw->image = ROI[i]->image;
|
|
||||||
zw->image_org = ROI[i]->image_org;
|
|
||||||
result.push_back(zw);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "ClassFlowImage.h"
|
|
||||||
#include "ClassFlowAlignment.h"
|
|
||||||
#include "Helper.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
struct roi {
|
|
||||||
int posx, posy, deltax, deltay;
|
|
||||||
int resultklasse;
|
|
||||||
string name;
|
|
||||||
CImageBasis *image, *image_org;
|
|
||||||
roi* next;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ClassFlowDigit :
|
|
||||||
public ClassFlowImage
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
std::vector<roi*> ROI;
|
|
||||||
string cnnmodelfile;
|
|
||||||
int modelxsize, modelysize;
|
|
||||||
bool SaveAllFiles;
|
|
||||||
|
|
||||||
ClassFlowAlignment* flowpostalignment;
|
|
||||||
|
|
||||||
bool doNeuralNetwork(string time);
|
|
||||||
bool doAlignAndCut(string time);
|
|
||||||
|
|
||||||
void SetInitialParameter(void);
|
|
||||||
|
|
||||||
public:
|
|
||||||
ClassFlowDigit();
|
|
||||||
ClassFlowDigit(std::vector<ClassFlow*>* lfc);
|
|
||||||
ClassFlowDigit(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
|
||||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
|
||||||
bool doFlow(string time);
|
|
||||||
string getHTMLSingleStep(string host);
|
|
||||||
string getReadout();
|
|
||||||
std::vector<HTMLInfo*> GetHTMLInfo();
|
|
||||||
|
|
||||||
void DrawROI(CImageBasis *_zw);
|
|
||||||
|
|
||||||
string name(){return "ClassFlowDigit";};
|
|
||||||
};
|
|
||||||
|
|
||||||
@@ -48,9 +48,14 @@ void ClassFlowImage::LogImage(string logPath, string name, float *resultFloat, i
|
|||||||
if (!isLogImage)
|
if (!isLogImage)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
char buf[10];
|
char buf[10];
|
||||||
|
|
||||||
if (resultFloat != NULL) {
|
if (resultFloat != NULL) {
|
||||||
sprintf(buf, "%.1f_", *resultFloat);
|
if (*resultFloat < 0)
|
||||||
|
sprintf(buf, "N.N_");
|
||||||
|
else
|
||||||
|
sprintf(buf, "%.1f_", *resultFloat);
|
||||||
} else if (resultInt != NULL) {
|
} else if (resultInt != NULL) {
|
||||||
sprintf(buf, "%d_", *resultInt);
|
sprintf(buf, "%d_", *resultInt);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
#include <sstream>
|
||||||
#include "ClassFlowMQTT.h"
|
#include "ClassFlowMQTT.h"
|
||||||
#include "Helper.h"
|
#include "Helper.h"
|
||||||
|
|
||||||
|
#include "time_sntp.h"
|
||||||
#include "interface_mqtt.h"
|
#include "interface_mqtt.h"
|
||||||
#include "ClassFlowPostProcessing.h"
|
#include "ClassFlowPostProcessing.h"
|
||||||
|
|
||||||
@@ -13,6 +15,11 @@ void ClassFlowMQTT::SetInitialParameter(void)
|
|||||||
topicError = "";
|
topicError = "";
|
||||||
topicRate = "";
|
topicRate = "";
|
||||||
topicTimeStamp = "";
|
topicTimeStamp = "";
|
||||||
|
maintopic = "";
|
||||||
|
mainerrortopic = "";
|
||||||
|
|
||||||
|
topicUptime = "";
|
||||||
|
topicFreeMem = "";
|
||||||
clientname = "watermeter";
|
clientname = "watermeter";
|
||||||
OldValue = "";
|
OldValue = "";
|
||||||
flowpostprocessing = NULL;
|
flowpostprocessing = NULL;
|
||||||
@@ -21,6 +28,9 @@ void ClassFlowMQTT::SetInitialParameter(void)
|
|||||||
previousElement = NULL;
|
previousElement = NULL;
|
||||||
ListFlowControll = NULL;
|
ListFlowControll = NULL;
|
||||||
disabled = false;
|
disabled = false;
|
||||||
|
MQTTenable = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,53 +98,112 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
{
|
{
|
||||||
this->uri = zerlegt[1];
|
this->uri = zerlegt[1];
|
||||||
}
|
}
|
||||||
if ((toUpper(zerlegt[0]) == "TOPIC") && (zerlegt.size() > 1))
|
|
||||||
{
|
|
||||||
this->topic = zerlegt[1];
|
|
||||||
}
|
|
||||||
if ((toUpper(zerlegt[0]) == "TOPICERROR") && (zerlegt.size() > 1))
|
|
||||||
{
|
|
||||||
this->topicError = zerlegt[1];
|
|
||||||
}
|
|
||||||
if ((toUpper(zerlegt[0]) == "TOPICRATE") && (zerlegt.size() > 1))
|
|
||||||
{
|
|
||||||
this->topicRate = zerlegt[1];
|
|
||||||
}
|
|
||||||
if ((toUpper(zerlegt[0]) == "TOPICTIMESTAMP") && (zerlegt.size() > 1))
|
|
||||||
{
|
|
||||||
this->topicTimeStamp = zerlegt[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((toUpper(zerlegt[0]) == "CLIENTID") && (zerlegt.size() > 1))
|
if ((toUpper(zerlegt[0]) == "CLIENTID") && (zerlegt.size() > 1))
|
||||||
{
|
{
|
||||||
this->clientname = zerlegt[1];
|
this->clientname = zerlegt[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (((toUpper(zerlegt[0]) == "TOPIC") || (toUpper(zerlegt[0]) == "MAINTOPIC")) && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
maintopic = zerlegt[1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((uri.length() > 0) && (topic.length() > 0))
|
if (!MQTTisConnected() && (uri.length() > 0) && (maintopic.length() > 0))
|
||||||
{
|
{
|
||||||
MQTTInit(uri, clientname, user, password, topicError, 60);
|
mainerrortopic = maintopic + "/connection";
|
||||||
|
MQTTInit(uri, clientname, user, password, mainerrortopic, 60);
|
||||||
|
MQTTPublish(mainerrortopic, "connected");
|
||||||
|
MQTTenable = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string ClassFlowMQTT::GetMQTTMainTopic()
|
||||||
|
{
|
||||||
|
return maintopic;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool ClassFlowMQTT::doFlow(string zwtime)
|
bool ClassFlowMQTT::doFlow(string zwtime)
|
||||||
{
|
{
|
||||||
|
if (!MQTTenable)
|
||||||
|
return true;
|
||||||
|
|
||||||
std::string result;
|
std::string result;
|
||||||
std::string resulterror = "";
|
std::string resulterror = "";
|
||||||
|
std::string resultraw = "";
|
||||||
std::string resultrate = "";
|
std::string resultrate = "";
|
||||||
std::string resulttimestamp = "";
|
std::string resulttimestamp = "";
|
||||||
string zw = "";
|
string zw = "";
|
||||||
|
string namenumber = "";
|
||||||
|
|
||||||
|
MQTTPublish(mainerrortopic, "connected");
|
||||||
|
|
||||||
|
zw = maintopic + "/" + "uptime";
|
||||||
|
char uptimeStr[11];
|
||||||
|
sprintf(uptimeStr, "%ld", (long)getUpTime());
|
||||||
|
MQTTPublish(zw, uptimeStr);
|
||||||
|
|
||||||
|
zw = maintopic + "/" + "freeMem";
|
||||||
|
char freeheapmem[11];
|
||||||
|
sprintf(freeheapmem, "%zu", esp_get_free_heap_size());
|
||||||
|
MQTTPublish(zw, freeheapmem);
|
||||||
|
|
||||||
if (flowpostprocessing)
|
if (flowpostprocessing)
|
||||||
{
|
{
|
||||||
result = flowpostprocessing->getReadoutParam(false, true);
|
std::vector<NumberPost*>* NUMBERS = flowpostprocessing->GetNumbers();
|
||||||
resulterror = flowpostprocessing->getReadoutError();
|
|
||||||
resultrate = flowpostprocessing->getReadoutRate();
|
for (int i = 0; i < (*NUMBERS).size(); ++i)
|
||||||
resulttimestamp = flowpostprocessing->getReadoutTimeStamp();
|
{
|
||||||
|
result = (*NUMBERS)[i]->ReturnValueNoError;
|
||||||
|
resultraw = (*NUMBERS)[i]->ReturnRawValue;
|
||||||
|
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
||||||
|
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
||||||
|
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
||||||
|
|
||||||
|
namenumber = (*NUMBERS)[i]->name;
|
||||||
|
if (namenumber == "default")
|
||||||
|
namenumber = maintopic + "/";
|
||||||
|
else
|
||||||
|
namenumber = maintopic + "/" + namenumber + "/";
|
||||||
|
|
||||||
|
zw = namenumber + "value";
|
||||||
|
if (result.length() > 0)
|
||||||
|
MQTTPublish(zw, result);
|
||||||
|
|
||||||
|
zw = namenumber + "error";
|
||||||
|
if (resulterror.length() > 0)
|
||||||
|
MQTTPublish(zw, resulterror, 1);
|
||||||
|
|
||||||
|
zw = namenumber + "rate";
|
||||||
|
if (resultrate.length() > 0)
|
||||||
|
MQTTPublish(zw, resultrate);
|
||||||
|
|
||||||
|
zw = namenumber + "raw";
|
||||||
|
if (resultraw.length() > 0)
|
||||||
|
MQTTPublish(zw, resultraw);
|
||||||
|
|
||||||
|
zw = namenumber + "timestamp";
|
||||||
|
if (resulttimestamp.length() > 0)
|
||||||
|
MQTTPublish(zw, resulttimestamp);
|
||||||
|
|
||||||
|
|
||||||
|
std::string json="{\"value\":"+result;
|
||||||
|
json += ",\"raw\":\""+resultraw;
|
||||||
|
json += ",\"error\":\""+resulterror;
|
||||||
|
if (resultrate.length() > 0)
|
||||||
|
json += "\",\"rate\":"+resultrate;
|
||||||
|
else
|
||||||
|
json += "\",\"rate\":\"\"";
|
||||||
|
json += ",\"timestamp\":\""+resulttimestamp+"\"}";
|
||||||
|
|
||||||
|
zw = namenumber + "json";
|
||||||
|
MQTTPublish(zw, json);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -149,26 +218,9 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
|||||||
result = result + "\t" + zw;
|
result = result + "\t" + zw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
MQTTPublish(topic, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
MQTTPublish(topic, result);
|
|
||||||
|
|
||||||
if (topicError.length() > 0) {
|
|
||||||
if (resulterror.length() == 0)
|
|
||||||
{
|
|
||||||
resulterror = " ";
|
|
||||||
}
|
|
||||||
MQTTPublish(topicError, resulterror);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (topicRate.length() > 0) {
|
|
||||||
MQTTPublish(topicRate, resultrate);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (topicRate.length() > 0) {
|
|
||||||
MQTTPublish(topicTimeStamp, resulttimestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
OldValue = result;
|
OldValue = result;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -9,10 +9,13 @@ class ClassFlowMQTT :
|
|||||||
public ClassFlow
|
public ClassFlow
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::string uri, topic, topicError, clientname, topicRate, topicTimeStamp;
|
std::string uri, topic, topicError, clientname, topicRate, topicTimeStamp, topicUptime, topicFreeMem;
|
||||||
std::string OldValue;
|
std::string OldValue;
|
||||||
ClassFlowPostProcessing* flowpostprocessing;
|
ClassFlowPostProcessing* flowpostprocessing;
|
||||||
std::string user, password;
|
std::string user, password;
|
||||||
|
bool MQTTenable;
|
||||||
|
|
||||||
|
std::string maintopic, mainerrortopic;
|
||||||
void SetInitialParameter(void);
|
void SetInitialParameter(void);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -20,6 +23,8 @@ public:
|
|||||||
ClassFlowMQTT(std::vector<ClassFlow*>* lfc);
|
ClassFlowMQTT(std::vector<ClassFlow*>* lfc);
|
||||||
ClassFlowMQTT(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
ClassFlowMQTT(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
||||||
|
|
||||||
|
string GetMQTTMainTopic();
|
||||||
|
|
||||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||||
bool doFlow(string time);
|
bool doFlow(string time);
|
||||||
string name(){return "ClassFlowMQTT";};
|
string name(){return "ClassFlowMQTT";};
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ static const char* TAG = "flow_make_image";
|
|||||||
esp_err_t ClassFlowMakeImage::camera_capture(){
|
esp_err_t ClassFlowMakeImage::camera_capture(){
|
||||||
string nm = namerawimage;
|
string nm = namerawimage;
|
||||||
Camera.CaptureToFile(nm);
|
Camera.CaptureToFile(nm);
|
||||||
|
time(&TimeImageTaken);
|
||||||
|
localtime(&TimeImageTaken);
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,7 +26,11 @@ void ClassFlowMakeImage::takePictureWithFlash(int flashdauer)
|
|||||||
rawImage->width = image_width;
|
rawImage->width = image_width;
|
||||||
rawImage->height = image_height;
|
rawImage->height = image_height;
|
||||||
/////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
printf("Flashdauer: %d\n", flashdauer);
|
||||||
Camera.CaptureToBasisImage(rawImage, flashdauer);
|
Camera.CaptureToBasisImage(rawImage, flashdauer);
|
||||||
|
time(&TimeImageTaken);
|
||||||
|
localtime(&TimeImageTaken);
|
||||||
|
|
||||||
if (SaveAllFiles) rawImage->SaveToFile(namerawimage);
|
if (SaveAllFiles) rawImage->SaveToFile(namerawimage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,6 +93,12 @@ bool ClassFlowMakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
if (toUpper(zerlegt[1]) == "TRUE")
|
if (toUpper(zerlegt[1]) == "TRUE")
|
||||||
SaveAllFiles = true;
|
SaveAllFiles = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((toUpper(zerlegt[0]) == "WAITBEFORETAKINGPICTURE") && (zerlegt.size() > 1))
|
||||||
|
{
|
||||||
|
waitbeforepicture = stoi(zerlegt[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if ((toUpper(zerlegt[0]) == "BRIGHTNESS") && (zerlegt.size() > 1))
|
if ((toUpper(zerlegt[0]) == "BRIGHTNESS") && (zerlegt.size() > 1))
|
||||||
{
|
{
|
||||||
@@ -118,9 +131,9 @@ bool ClassFlowMakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
rawImage->CreateEmptyImage(image_width, image_height, 3);
|
rawImage->CreateEmptyImage(image_width, image_height, 3);
|
||||||
|
|
||||||
waitbeforepicture_store = waitbeforepicture;
|
waitbeforepicture_store = waitbeforepicture;
|
||||||
if (FixedExposure)
|
if (FixedExposure && (waitbeforepicture > 0))
|
||||||
{
|
{
|
||||||
printf("Fixed Exposure enabled!\n");
|
// printf("Fixed Exposure enabled!\n");
|
||||||
int flashdauer = (int) (waitbeforepicture * 1000);
|
int flashdauer = (int) (waitbeforepicture * 1000);
|
||||||
Camera.EnableAutoExposure(flashdauer);
|
Camera.EnableAutoExposure(flashdauer);
|
||||||
waitbeforepicture = 0.2;
|
waitbeforepicture = 0.2;
|
||||||
@@ -169,6 +182,9 @@ bool ClassFlowMakeImage::doFlow(string zwtime)
|
|||||||
esp_err_t ClassFlowMakeImage::SendRawJPG(httpd_req_t *req)
|
esp_err_t ClassFlowMakeImage::SendRawJPG(httpd_req_t *req)
|
||||||
{
|
{
|
||||||
int flashdauer = (int) (waitbeforepicture * 1000);
|
int flashdauer = (int) (waitbeforepicture * 1000);
|
||||||
|
time(&TimeImageTaken);
|
||||||
|
localtime(&TimeImageTaken);
|
||||||
|
|
||||||
return Camera.CaptureToHTTP(req, flashdauer);
|
return Camera.CaptureToHTTP(req, flashdauer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,6 +195,9 @@ ImageData* ClassFlowMakeImage::SendRawImage()
|
|||||||
ImageData *id;
|
ImageData *id;
|
||||||
int flashdauer = (int) (waitbeforepicture * 1000);
|
int flashdauer = (int) (waitbeforepicture * 1000);
|
||||||
Camera.CaptureToBasisImage(zw, flashdauer);
|
Camera.CaptureToBasisImage(zw, flashdauer);
|
||||||
|
time(&TimeImageTaken);
|
||||||
|
localtime(&TimeImageTaken);
|
||||||
|
|
||||||
id = zw->writeToMemoryAsJPG();
|
id = zw->writeToMemoryAsJPG();
|
||||||
delete zw;
|
delete zw;
|
||||||
return id;
|
return id;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,54 +1,68 @@
|
|||||||
#pragma once
|
#ifndef __FLOWPOSTPROCESSING__
|
||||||
|
#define __FLOWPOSTPROCESSING__
|
||||||
|
|
||||||
#include "ClassFlow.h"
|
#include "ClassFlow.h"
|
||||||
|
#include "ClassFlowMakeImage.h"
|
||||||
|
#include "ClassFlowCNNGeneral.h"
|
||||||
|
#include "ClassFlowDefineTypes.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
class ClassFlowPostProcessing :
|
class ClassFlowPostProcessing :
|
||||||
public ClassFlow
|
public ClassFlow
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
bool PreValueUse;
|
std::vector<NumberPost*> NUMBERS;
|
||||||
|
bool UpdatePreValueINI;
|
||||||
|
|
||||||
int PreValueAgeStartup;
|
int PreValueAgeStartup;
|
||||||
bool AllowNegativeRates;
|
|
||||||
float MaxRateValue;
|
|
||||||
bool useMaxRateValue;
|
|
||||||
bool ErrorMessage;
|
bool ErrorMessage;
|
||||||
bool PreValueOkay;
|
bool IgnoreLeadingNaN; // SPEZIALFALL für User Gustl
|
||||||
bool checkDigitIncreaseConsistency;
|
|
||||||
int DecimalShift;
|
|
||||||
time_t lastvalue;
|
ClassFlowCNNGeneral* flowAnalog;
|
||||||
float FlowRateAct; // m3 / min
|
ClassFlowCNNGeneral* flowDigit;
|
||||||
|
|
||||||
|
|
||||||
string FilePreValue;
|
string FilePreValue;
|
||||||
float PreValue; // letzter Wert, der gut ausgelesen wurde
|
|
||||||
float Value; // letzer ausgelesener Wert, inkl. Korrekturen
|
ClassFlowMakeImage *flowMakeImage;
|
||||||
string ReturnRawValue; // Rohwert (mit N & führenden 0)
|
|
||||||
string ReturnValue; // korrigierter Rückgabewert, ggf. mit Fehlermeldung
|
|
||||||
string ReturnValueNoError; // korrigierter Rückgabewert ohne Fehlermeldung
|
|
||||||
string ErrorMessageText; // Fehlermeldung bei Consistency Check
|
|
||||||
string timeStamp;
|
|
||||||
|
|
||||||
bool LoadPreValue(void);
|
bool LoadPreValue(void);
|
||||||
string ShiftDecimal(string in, int _decShift);
|
string ShiftDecimal(string in, int _decShift);
|
||||||
|
|
||||||
string ErsetzteN(string);
|
string ErsetzteN(string, float _prevalue);
|
||||||
float checkDigitConsistency(float input, int _decilamshift, bool _isanalog);
|
float checkDigitConsistency(float input, int _decilamshift, bool _isanalog, float _preValue);
|
||||||
string RundeOutput(float _in, int _anzNachkomma);
|
string RundeOutput(float _in, int _anzNachkomma);
|
||||||
|
|
||||||
|
void InitNUMBERS();
|
||||||
|
void handleDecimalSeparator(string _decsep, string _value);
|
||||||
|
void handleMaxRateValue(string _decsep, string _value);
|
||||||
|
void handleDecimalExtendedResolution(string _decsep, string _value);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc);
|
bool PreValueUse;
|
||||||
|
|
||||||
|
ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit);
|
||||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||||
bool doFlow(string time);
|
bool doFlow(string time);
|
||||||
string getReadout();
|
string getReadout(int _number);
|
||||||
string getReadoutParam(bool _rawValue, bool _noerror);
|
string getReadoutParam(bool _rawValue, bool _noerror, int _number = 0);
|
||||||
string getReadoutError();
|
string getReadoutError(int _number = 0);
|
||||||
string getReadoutRate();
|
string getReadoutRate(int _number = 0);
|
||||||
string getReadoutTimeStamp();
|
string getReadoutTimeStamp(int _number = 0);
|
||||||
void SavePreValue(float value, string time = "");
|
void SavePreValue();
|
||||||
string GetPreValue();
|
string GetPreValue(std::string _number = "");
|
||||||
|
void SetPreValue(float zw, string _numbers, bool _extern = false);
|
||||||
|
|
||||||
|
void UpdateNachkommaDecimalShift();
|
||||||
|
|
||||||
|
std::vector<NumberPost*>* GetNumbers(){return &NUMBERS;};
|
||||||
|
|
||||||
string name(){return "ClassFlowPostProcessing";};
|
string name(){return "ClassFlowPostProcessing";};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
|
|
||||||
|
|
||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
//#include "ClassLogFile.h"
|
//#include "ClassLogFile.h"
|
||||||
|
|
||||||
@@ -77,21 +78,25 @@ void memCopyGen(uint8_t* _source, uint8_t* _target, int _size)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
FILE* OpenFileAndWait(const char* nm, char* _mode, int _waitsec)
|
FILE* OpenFileAndWait(const char* nm, const char* _mode, int _waitsec)
|
||||||
{
|
{
|
||||||
|
printf("open file %s in mode %s\n", nm, _mode);
|
||||||
FILE *pfile = fopen(nm, _mode);
|
FILE *pfile = fopen(nm, _mode);
|
||||||
|
|
||||||
|
/*
|
||||||
if (pfile == NULL)
|
if (pfile == NULL)
|
||||||
{
|
{
|
||||||
TickType_t xDelay;
|
TickType_t xDelay;
|
||||||
xDelay = _waitsec * 1000 / portTICK_PERIOD_MS;
|
xDelay = _waitsec * 1000 / portTICK_PERIOD_MS;
|
||||||
std::string zw = "File is locked: " + std::string(nm) + " - wait for " + std::to_string(_waitsec);
|
std::string zw = "File is locked: " + std::string(nm) + " - wait for " + std::to_string(_waitsec) + " seconds";
|
||||||
printf(zw.c_str());
|
printf(zw.c_str());
|
||||||
printf("\n");
|
printf("\n");
|
||||||
LogFile.WriteToFile(zw);
|
LogFile.WriteToFile(zw);
|
||||||
vTaskDelay( xDelay );
|
vTaskDelay( xDelay );
|
||||||
pfile = fopen(nm, _mode);
|
pfile = fopen(nm, _mode);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
return pfile;
|
return pfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,6 +318,14 @@ string toUpper(string in)
|
|||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string toLower(string in)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < in.length(); ++i)
|
||||||
|
in[i] = tolower(in[i]);
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
// CPU Temp
|
// CPU Temp
|
||||||
extern "C" uint8_t temprature_sens_read();
|
extern "C" uint8_t temprature_sens_read();
|
||||||
float temperatureRead()
|
float temperatureRead()
|
||||||
@@ -358,3 +371,30 @@ int removeFolder(const char* folderPath, const char* logTag) {
|
|||||||
|
|
||||||
return deleted;
|
return deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<string> HelperZerlegeZeile(std::string input, std::string _delimiter = "")
|
||||||
|
{
|
||||||
|
std::vector<string> Output;
|
||||||
|
std::string delimiter = " =,";
|
||||||
|
if (_delimiter.length() > 0){
|
||||||
|
delimiter = _delimiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
input = trim(input, delimiter);
|
||||||
|
size_t pos = findDelimiterPos(input, delimiter);
|
||||||
|
std::string token;
|
||||||
|
while (pos != std::string::npos) {
|
||||||
|
token = input.substr(0, pos);
|
||||||
|
token = trim(token, delimiter);
|
||||||
|
Output.push_back(token);
|
||||||
|
input.erase(0, pos + 1);
|
||||||
|
input = trim(input, delimiter);
|
||||||
|
pos = findDelimiterPos(input, delimiter);
|
||||||
|
}
|
||||||
|
Output.push_back(input);
|
||||||
|
|
||||||
|
return Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@@ -10,7 +11,7 @@ void FindReplace(std::string& line, std::string& oldString, std::string& newStri
|
|||||||
|
|
||||||
void CopyFile(string input, string output);
|
void CopyFile(string input, string output);
|
||||||
|
|
||||||
FILE* OpenFileAndWait(const char* nm, char* _mode, int _waitsec = 1);
|
FILE* OpenFileAndWait(const char* nm, const char* _mode, int _waitsec = 1);
|
||||||
|
|
||||||
size_t findDelimiterPos(string input, string delimiter);
|
size_t findDelimiterPos(string input, string delimiter);
|
||||||
//string trim(string istring);
|
//string trim(string istring);
|
||||||
@@ -22,6 +23,7 @@ string getFileType(string filename);
|
|||||||
int mkdir_r(const char *dir, const mode_t mode);
|
int mkdir_r(const char *dir, const mode_t mode);
|
||||||
int removeFolder(const char* folderPath, const char* logTag);
|
int removeFolder(const char* folderPath, const char* logTag);
|
||||||
|
|
||||||
|
string toLower(string in);
|
||||||
string toUpper(string in);
|
string toUpper(string in);
|
||||||
|
|
||||||
float temperatureRead();
|
float temperatureRead();
|
||||||
@@ -30,6 +32,8 @@ time_t addDays(time_t startTime, int days);
|
|||||||
|
|
||||||
void memCopyGen(uint8_t* _source, uint8_t* _target, int _size);
|
void memCopyGen(uint8_t* _source, uint8_t* _target, int _size);
|
||||||
|
|
||||||
|
std::vector<string> HelperZerlegeZeile(std::string input, std::string _delimiter);
|
||||||
|
|
||||||
///////////////////////////
|
///////////////////////////
|
||||||
size_t getInternalESPHeapSize();
|
size_t getInternalESPHeapSize();
|
||||||
size_t getESPHeapSize();
|
size_t getESPHeapSize();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "CImageBasis.h"
|
#include "CImageBasis.h"
|
||||||
#include "Helper.h"
|
#include "Helper.h"
|
||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
|
#include "server_ota.h"
|
||||||
|
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
|
|
||||||
@@ -337,6 +338,18 @@ void CImageBasis::LoadFromMemory(stbi_uc *_buffer, int len)
|
|||||||
rgb_image = stbi_load_from_memory(_buffer, len, &width, &height, &channels, 3);
|
rgb_image = stbi_load_from_memory(_buffer, len, &width, &height, &channels, 3);
|
||||||
bpp = channels;
|
bpp = channels;
|
||||||
printf("Image loaded from memory: %d, %d, %d\n", width, height, channels);
|
printf("Image loaded from memory: %d, %d, %d\n", width, height, channels);
|
||||||
|
if ((width * height * channels) == 0)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Image with size 0 loaded --> reboot to be done! "
|
||||||
|
"Check that your camera module is working and connected properly.");
|
||||||
|
|
||||||
|
LogFile.SwitchOnOff(true);
|
||||||
|
LogFile.WriteToFile("Image with size 0 loaded --> reboot to be done! "
|
||||||
|
"Check that your camera module is working and connected properly.");
|
||||||
|
|
||||||
|
doReboot();
|
||||||
|
|
||||||
|
}
|
||||||
RGBImageRelease();
|
RGBImageRelease();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,12 +367,10 @@ CImageBasis::CImageBasis(CImageBasis *_copyfrom, int _anzrepeat)
|
|||||||
int memsize = width * height * channels;
|
int memsize = width * height * channels;
|
||||||
rgb_image = (unsigned char*)GET_MEMORY(memsize);
|
rgb_image = (unsigned char*)GET_MEMORY(memsize);
|
||||||
|
|
||||||
TickType_t xDelay;
|
|
||||||
int anz = 1;
|
int anz = 1;
|
||||||
while (!rgb_image && (anz < _anzrepeat))
|
while (!rgb_image && (anz < _anzrepeat))
|
||||||
{
|
{
|
||||||
printf("Create Image from Copy - Speicher ist voll - Versuche es erneut: %d.\n", anz);
|
printf("Create Image from Copy - Speicher ist voll - Versuche es erneut: %d.\n", anz);
|
||||||
xDelay = 1000 / portTICK_PERIOD_MS;
|
|
||||||
rgb_image = (unsigned char*) malloc(memsize);
|
rgb_image = (unsigned char*) malloc(memsize);
|
||||||
anz++;
|
anz++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
|||||||
|
|
||||||
idf_component_register(SRCS ${app_sources}
|
idf_component_register(SRCS ${app_sources}
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
REQUIRES jomjol_helper jomjol_logfile esp_http_server)
|
REQUIRES jomjol_helper jomjol_logfile esp_http_server jomjol_fileserver_ota)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ ClassLogFile LogFile("/sdcard/log/message", "log_%Y-%m-%d.txt");
|
|||||||
|
|
||||||
void ClassLogFile::WriteHeapInfo(std::string _id)
|
void ClassLogFile::WriteHeapInfo(std::string _id)
|
||||||
{
|
{
|
||||||
std::string _zw = "\t" + _id;
|
std::string _zw = "\t" + _id;
|
||||||
if (loglevel > 0)
|
if (loglevel > 0)
|
||||||
_zw = _zw + "\t" + getESPHeapInfo();
|
_zw = _zw + "\t" + getESPHeapInfo();
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
#include "interface_mqtt.h"
|
#include "interface_mqtt.h"
|
||||||
|
|
||||||
|
//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "mqtt_client.h"
|
#include "mqtt_client.h"
|
||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
|
|
||||||
static const char *TAG = "interface_mqtt";
|
static const char *TAG_INTERFACEMQTT = "interface_mqtt";
|
||||||
|
|
||||||
|
std::map<std::string, std::function<void()>>* connectFunktionMap = NULL;
|
||||||
|
std::map<std::string, std::function<bool(std::string, char*, int)>>* subscribeFunktionMap = NULL;
|
||||||
bool debugdetail = true;
|
bool debugdetail = true;
|
||||||
|
|
||||||
// #define CONFIG_BROKER_URL "mqtt://192.168.178.43:1883"
|
// #define CONFIG_BROKER_URL "mqtt://192.168.178.43:1883"
|
||||||
@@ -16,51 +18,74 @@ esp_mqtt_event_id_t esp_mmqtt_ID = MQTT_EVENT_ANY;
|
|||||||
bool mqtt_connected = false;
|
bool mqtt_connected = false;
|
||||||
esp_mqtt_client_handle_t client = NULL;
|
esp_mqtt_client_handle_t client = NULL;
|
||||||
|
|
||||||
void MQTTPublish(std::string _key, std::string _content){
|
void MQTTPublish(std::string _key, std::string _content, int retained_flag){
|
||||||
if (client && mqtt_connected) {
|
if (client && mqtt_connected) {
|
||||||
int msg_id;
|
int msg_id;
|
||||||
std::string zw;
|
std::string zw;
|
||||||
msg_id = esp_mqtt_client_publish(client, _key.c_str(), _content.c_str(), 0, 1, 0);
|
msg_id = esp_mqtt_client_publish(client, _key.c_str(), _content.c_str(), 0, 1, retained_flag);
|
||||||
zw = "sent publish successful in MQTTPublish, msg_id=" + std::to_string(msg_id) + ", " + _key + ", " + _content;
|
zw = "sent publish successful in MQTTPublish, msg_id=" + std::to_string(msg_id) + ", " + _key + ", " + _content;
|
||||||
if (debugdetail) LogFile.WriteToFile(zw);
|
if (debugdetail) LogFile.WriteToFile(zw);
|
||||||
ESP_LOGI(TAG, "sent publish successful in MQTTPublish, msg_id=%d, %s, %s", msg_id, _key.c_str(), _content.c_str());
|
ESP_LOGD(TAG_INTERFACEMQTT, "sent publish successful in MQTTPublish, msg_id=%d, %s, %s", msg_id, _key.c_str(), _content.c_str());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ESP_LOGI(TAG, "Problem with Publish, client=%d, mqtt_connected %d", (int) client, (int) mqtt_connected);
|
ESP_LOGW(TAG_INTERFACEMQTT, "Problem with Publish, client=%d, mqtt_connected %d", (int) client, (int) mqtt_connected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
|
static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
|
||||||
{
|
{
|
||||||
|
int msg_id;
|
||||||
|
std::string topic = "";
|
||||||
switch (event->event_id) {
|
switch (event->event_id) {
|
||||||
|
case MQTT_EVENT_BEFORE_CONNECT:
|
||||||
|
ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_BEFORE_CONNECT");
|
||||||
|
break;
|
||||||
case MQTT_EVENT_CONNECTED:
|
case MQTT_EVENT_CONNECTED:
|
||||||
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
|
ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_CONNECTED");
|
||||||
mqtt_connected = true;
|
mqtt_connected = true;
|
||||||
|
MQTTconnected();
|
||||||
break;
|
break;
|
||||||
case MQTT_EVENT_DISCONNECTED:
|
case MQTT_EVENT_DISCONNECTED:
|
||||||
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
|
ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_DISCONNECTED");
|
||||||
|
break;
|
||||||
|
case MQTT_EVENT_SUBSCRIBED:
|
||||||
|
ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
|
||||||
|
msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0);
|
||||||
|
ESP_LOGI(TAG_INTERFACEMQTT, "sent publish successful, msg_id=%d", msg_id);
|
||||||
|
break;
|
||||||
|
case MQTT_EVENT_UNSUBSCRIBED:
|
||||||
|
ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
|
||||||
break;
|
break;
|
||||||
case MQTT_EVENT_PUBLISHED:
|
case MQTT_EVENT_PUBLISHED:
|
||||||
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
|
ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
|
||||||
break;
|
break;
|
||||||
case MQTT_EVENT_DATA:
|
case MQTT_EVENT_DATA:
|
||||||
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
|
ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_DATA");
|
||||||
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
|
ESP_LOGI(TAG_INTERFACEMQTT, "TOPIC=%.*s\r\n", event->topic_len, event->topic);
|
||||||
printf("DATA=%.*s\r\n", event->data_len, event->data);
|
ESP_LOGI(TAG_INTERFACEMQTT, "DATA=%.*s\r\n", event->data_len, event->data);
|
||||||
|
topic.assign(event->topic, event->topic_len);
|
||||||
|
if (subscribeFunktionMap != NULL) {
|
||||||
|
if (subscribeFunktionMap->find(topic) != subscribeFunktionMap->end()) {
|
||||||
|
ESP_LOGD(TAG_INTERFACEMQTT, "call handler function\r\n");
|
||||||
|
(*subscribeFunktionMap)[topic](topic, event->data, event->data_len);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG_INTERFACEMQTT, "no handler available\r\n");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case MQTT_EVENT_ERROR:
|
case MQTT_EVENT_ERROR:
|
||||||
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
|
ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_ERROR");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
|
ESP_LOGI(TAG_INTERFACEMQTT, "Other event id:%d", event->event_id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
|
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
|
||||||
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
|
ESP_LOGD(TAG_INTERFACEMQTT, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
|
||||||
mqtt_event_handler_cb((esp_mqtt_event_handle_t) event_data);
|
mqtt_event_handler_cb((esp_mqtt_event_handle_t) event_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +99,7 @@ void MQTTInit(std::string _mqttURI, std::string _clientid, std::string _user, st
|
|||||||
.client_id = _clientid.c_str(),
|
.client_id = _clientid.c_str(),
|
||||||
.lwt_topic = _LWTContext.c_str(),
|
.lwt_topic = _LWTContext.c_str(),
|
||||||
.lwt_msg = _zwmessage.c_str(),
|
.lwt_msg = _zwmessage.c_str(),
|
||||||
|
.lwt_retain = 1,
|
||||||
.lwt_msg_len = _lzw,
|
.lwt_msg_len = _lzw,
|
||||||
.keepalive = _keepalive
|
.keepalive = _keepalive
|
||||||
};
|
};
|
||||||
@@ -81,12 +107,100 @@ void MQTTInit(std::string _mqttURI, std::string _clientid, std::string _user, st
|
|||||||
if (_user.length() && _password.length()){
|
if (_user.length() && _password.length()){
|
||||||
mqtt_cfg.username = _user.c_str();
|
mqtt_cfg.username = _user.c_str();
|
||||||
mqtt_cfg.password = _password.c_str();
|
mqtt_cfg.password = _password.c_str();
|
||||||
printf("Connect to MQTT: %s, %s", mqtt_cfg.username, mqtt_cfg.password);
|
ESP_LOGI(TAG_INTERFACEMQTT, "Connect to MQTT: %s, %s", mqtt_cfg.username, mqtt_cfg.password);
|
||||||
};
|
};
|
||||||
|
|
||||||
client = esp_mqtt_client_init(&mqtt_cfg);
|
client = esp_mqtt_client_init(&mqtt_cfg);
|
||||||
esp_mqtt_client_register_event(client, esp_mmqtt_ID, mqtt_event_handler, client);
|
esp_mqtt_client_register_event(client, esp_mmqtt_ID, mqtt_event_handler, client);
|
||||||
esp_mqtt_client_start(client);
|
esp_mqtt_client_start(client);
|
||||||
|
|
||||||
MQTTPublish(_LWTContext, "");
|
MQTTPublish(_LWTContext, "", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MQTTdestroy() {
|
||||||
|
if (client != NULL) {
|
||||||
|
esp_mqtt_client_stop(client);
|
||||||
|
esp_mqtt_client_destroy(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MQTTisConnected() {
|
||||||
|
return mqtt_connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MQTTregisterConnectFunction(std::string name, std::function<void()> func){
|
||||||
|
ESP_LOGD(TAG_INTERFACEMQTT, "MQTTregisteronnectFunction %s\r\n", name.c_str());
|
||||||
|
if (connectFunktionMap == NULL) {
|
||||||
|
connectFunktionMap = new std::map<std::string, std::function<void()>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((*connectFunktionMap)[name] != NULL) {
|
||||||
|
ESP_LOGW(TAG_INTERFACEMQTT, "connect function %s already registred", name.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*connectFunktionMap)[name] = func;
|
||||||
|
|
||||||
|
if (mqtt_connected) {
|
||||||
|
func();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MQTTunregisterConnectFunction(std::string name){
|
||||||
|
ESP_LOGD(TAG_INTERFACEMQTT, "MQTTregisteronnectFunction %s\r\n", name.c_str());
|
||||||
|
if ((connectFunktionMap != NULL) && (connectFunktionMap->find(name) != connectFunktionMap->end())) {
|
||||||
|
connectFunktionMap->erase(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MQTTregisterSubscribeFunction(std::string topic, std::function<bool(std::string, char*, int)> func){
|
||||||
|
ESP_LOGD(TAG_INTERFACEMQTT, "MQTTregisterSubscribeFunction %s\r\n", topic.c_str());
|
||||||
|
if (subscribeFunktionMap == NULL) {
|
||||||
|
subscribeFunktionMap = new std::map<std::string, std::function<bool(std::string, char*, int)>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((*subscribeFunktionMap)[topic] != NULL) {
|
||||||
|
ESP_LOGW(TAG_INTERFACEMQTT, "topic %s already registred for subscription", topic.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*subscribeFunktionMap)[topic] = func;
|
||||||
|
|
||||||
|
if (mqtt_connected) {
|
||||||
|
int msg_id = esp_mqtt_client_subscribe(client, topic.c_str(), 0);
|
||||||
|
ESP_LOGD(TAG_INTERFACEMQTT, "topic %s subscribe successful, msg_id=%d", topic.c_str(), msg_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MQTTconnected(){
|
||||||
|
if (mqtt_connected) {
|
||||||
|
if (connectFunktionMap != NULL) {
|
||||||
|
for(std::map<std::string, std::function<void()>>::iterator it = connectFunktionMap->begin(); it != connectFunktionMap->end(); ++it) {
|
||||||
|
it->second();
|
||||||
|
ESP_LOGD(TAG_INTERFACEMQTT, "call connect function %s", it->first.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subscribeFunktionMap != NULL) {
|
||||||
|
for(std::map<std::string, std::function<bool(std::string, char*, int)>>::iterator it = subscribeFunktionMap->begin(); it != subscribeFunktionMap->end(); ++it) {
|
||||||
|
int msg_id = esp_mqtt_client_subscribe(client, it->first.c_str(), 0);
|
||||||
|
ESP_LOGD(TAG_INTERFACEMQTT, "topic %s subscribe successful, msg_id=%d", it->first.c_str(), msg_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MQTTdestroySubscribeFunction(){
|
||||||
|
if (subscribeFunktionMap != NULL) {
|
||||||
|
if (mqtt_connected) {
|
||||||
|
for(std::map<std::string, std::function<bool(std::string, char*, int)>>::iterator it = subscribeFunktionMap->begin(); it != subscribeFunktionMap->end(); ++it) {
|
||||||
|
int msg_id = esp_mqtt_client_unsubscribe(client, it->first.c_str());
|
||||||
|
ESP_LOGI(TAG_INTERFACEMQTT, "topic %s unsubscribe successful, msg_id=%d", it->first.c_str(), msg_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribeFunktionMap->clear();
|
||||||
|
delete subscribeFunktionMap;
|
||||||
|
subscribeFunktionMap = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,23 @@
|
|||||||
|
#ifndef INTERFACE_MQTT_H
|
||||||
|
#define INTERFACE_MQTT_H
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
void MQTTInit(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password, std::string _LWTContext, int _keepalive);
|
void MQTTInit(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password, std::string _LWTContext, int _keepalive);
|
||||||
|
void MQTTdestroy();
|
||||||
|
|
||||||
//void MQTTInit(std::string _mqttURI, std::string _clientid, std::string _user = "", std::string _password = "");
|
//void MQTTInit(std::string _mqttURI, std::string _clientid, std::string _user = "", std::string _password = "");
|
||||||
|
|
||||||
void MQTTPublish(std::string _key, std::string _content);
|
void MQTTPublish(std::string _key, std::string _content, int retained_flag = 0);
|
||||||
|
|
||||||
|
bool MQTTisConnected();
|
||||||
|
|
||||||
|
void MQTTregisterConnectFunction(std::string name, std::function<void()> func);
|
||||||
|
void MQTTunregisterConnectFunction(std::string name);
|
||||||
|
void MQTTregisterSubscribeFunction(std::string topic, std::function<bool(std::string, char*, int)> func);
|
||||||
|
void MQTTdestroySubscribeFunction();
|
||||||
|
void MQTTconnected();
|
||||||
|
|
||||||
|
#endif //INTERFACE_MQTT_H
|
||||||
@@ -17,6 +17,7 @@ float CTfLiteClass::GetOutputValue(int nr)
|
|||||||
return output2->data.f[nr];
|
return output2->data.f[nr];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int CTfLiteClass::GetClassFromImageBasis(CImageBasis *rs)
|
int CTfLiteClass::GetClassFromImageBasis(CImageBasis *rs)
|
||||||
{
|
{
|
||||||
if (!LoadInputImageBasis(rs))
|
if (!LoadInputImageBasis(rs))
|
||||||
@@ -27,19 +28,36 @@ int CTfLiteClass::GetClassFromImageBasis(CImageBasis *rs)
|
|||||||
return GetOutClassification();
|
return GetOutClassification();
|
||||||
}
|
}
|
||||||
|
|
||||||
int CTfLiteClass::GetOutClassification()
|
|
||||||
|
int CTfLiteClass::GetOutClassification(int _von, int _bis)
|
||||||
{
|
{
|
||||||
TfLiteTensor* output2 = interpreter->output(0);
|
TfLiteTensor* output2 = interpreter->output(0);
|
||||||
|
|
||||||
float zw_max = 0;
|
float zw_max;
|
||||||
float zw;
|
float zw;
|
||||||
int zw_class = -1;
|
int zw_class;
|
||||||
|
|
||||||
if (output2 == NULL)
|
if (output2 == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
int numeroutput = output2->dims->data[1];
|
int numeroutput = output2->dims->data[1];
|
||||||
for (int i = 0; i < numeroutput; ++i)
|
//printf("\n number output neurons: %d\n\n", numeroutput);
|
||||||
|
|
||||||
|
if (_bis == -1)
|
||||||
|
_bis = numeroutput -1;
|
||||||
|
|
||||||
|
if (_von == -1)
|
||||||
|
_von = 0;
|
||||||
|
|
||||||
|
if (_bis >= numeroutput)
|
||||||
|
{
|
||||||
|
printf("ANZAHL OUTPUT NEURONS passt nicht zu geforderter Classifizierung!");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
zw_max = output2->data.f[_von];
|
||||||
|
zw_class = _von;
|
||||||
|
for (int i = _von + 1; i <= _bis; ++i)
|
||||||
{
|
{
|
||||||
zw = output2->data.f[i];
|
zw = output2->data.f[i];
|
||||||
if (zw > zw_max)
|
if (zw > zw_max)
|
||||||
@@ -48,7 +66,7 @@ int CTfLiteClass::GetOutClassification()
|
|||||||
zw_class = i;
|
zw_class = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return zw_class;
|
return (zw_class - _von);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTfLiteClass::GetInputDimension(bool silent = false)
|
void CTfLiteClass::GetInputDimension(bool silent = false)
|
||||||
@@ -70,18 +88,18 @@ void CTfLiteClass::GetInputDimension(bool silent = false)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CTfLiteClass::GetOutPut()
|
int CTfLiteClass::GetAnzOutPut(bool silent)
|
||||||
{
|
{
|
||||||
TfLiteTensor* output2 = this->interpreter->output(0);
|
TfLiteTensor* output2 = this->interpreter->output(0);
|
||||||
|
|
||||||
int numdim = output2->dims->size;
|
int numdim = output2->dims->size;
|
||||||
printf("NumDimension: %d\n", numdim);
|
if (!silent) printf("NumDimension: %d\n", numdim);
|
||||||
|
|
||||||
int sizeofdim;
|
int sizeofdim;
|
||||||
for (int j = 0; j < numdim; ++j)
|
for (int j = 0; j < numdim; ++j)
|
||||||
{
|
{
|
||||||
sizeofdim = output2->dims->data[j];
|
sizeofdim = output2->dims->data[j];
|
||||||
printf("SizeOfDimension %d: %d\n", j, sizeofdim);
|
if (!silent) printf("SizeOfDimension %d: %d\n", j, sizeofdim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -92,20 +110,22 @@ void CTfLiteClass::GetOutPut()
|
|||||||
for (int i = 0; i < numeroutput; ++i)
|
for (int i = 0; i < numeroutput; ++i)
|
||||||
{
|
{
|
||||||
fo = output2->data.f[i];
|
fo = output2->data.f[i];
|
||||||
printf("Result %d: %f\n", i, fo);
|
if (!silent) printf("Result %d: %f\n", i, fo);
|
||||||
}
|
}
|
||||||
|
return numeroutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTfLiteClass::Invoke()
|
void CTfLiteClass::Invoke()
|
||||||
{
|
{
|
||||||
interpreter->Invoke();
|
if (interpreter != nullptr)
|
||||||
|
interpreter->Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool CTfLiteClass::LoadInputImageBasis(CImageBasis *rs)
|
bool CTfLiteClass::LoadInputImageBasis(CImageBasis *rs)
|
||||||
{
|
{
|
||||||
std::string zw = "ClassFlowAnalog::doNeuralNetwork nach LoadInputResizeImage: ";
|
std::string zw = "ClassFlowCNNGeneral::doNeuralNetwork nach LoadInputResizeImage: ";
|
||||||
|
|
||||||
unsigned int w = rs->width;
|
unsigned int w = rs->width;
|
||||||
unsigned int h = rs->height;
|
unsigned int h = rs->height;
|
||||||
@@ -148,6 +168,8 @@ void CTfLiteClass::MakeAllocate()
|
|||||||
TfLiteStatus allocate_status = this->interpreter->AllocateTensors();
|
TfLiteStatus allocate_status = this->interpreter->AllocateTensors();
|
||||||
if (allocate_status != kTfLiteOk) {
|
if (allocate_status != kTfLiteOk) {
|
||||||
TF_LITE_REPORT_ERROR(error_reporter, "AllocateTensors() failed");
|
TF_LITE_REPORT_ERROR(error_reporter, "AllocateTensors() failed");
|
||||||
|
LogFile.WriteToFile("AllocateTensors() failed");
|
||||||
|
|
||||||
this->GetInputDimension();
|
this->GetInputDimension();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -155,9 +177,9 @@ void CTfLiteClass::MakeAllocate()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CTfLiteClass::GetInputTensorSize(){
|
void CTfLiteClass::GetInputTensorSize(){
|
||||||
|
#ifdef DEBUG_DETAIL_ON
|
||||||
float *zw = this->input;
|
float *zw = this->input;
|
||||||
int test = sizeof(zw);
|
int test = sizeof(zw);
|
||||||
#ifdef DEBUG_DETAIL_ON
|
|
||||||
printf("Input Tensor Dimension: %d\n", test);
|
printf("Input Tensor Dimension: %d\n", test);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -184,13 +206,11 @@ unsigned char* CTfLiteClass::ReadFileToCharArray(std::string _fn)
|
|||||||
|
|
||||||
unsigned char *result = (unsigned char*) malloc(size);
|
unsigned char *result = (unsigned char*) malloc(size);
|
||||||
int anz = 1;
|
int anz = 1;
|
||||||
TickType_t xDelay;
|
|
||||||
while (!result && (anz < 6)) // maximal 5x versuchen (= 5s)
|
while (!result && (anz < 6)) // maximal 5x versuchen (= 5s)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
printf("Speicher ist voll - Versuche es erneut: %d.\n", anz);
|
printf("Speicher ist voll - Versuche es erneut: %d.\n", anz);
|
||||||
#endif
|
#endif
|
||||||
xDelay = 1000 / portTICK_PERIOD_MS;
|
|
||||||
result = (unsigned char*) malloc(size);
|
result = (unsigned char*) malloc(size);
|
||||||
anz++;
|
anz++;
|
||||||
}
|
}
|
||||||
@@ -208,7 +228,7 @@ unsigned char* CTfLiteClass::ReadFileToCharArray(std::string _fn)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTfLiteClass::LoadModel(std::string _fn){
|
bool CTfLiteClass::LoadModel(std::string _fn){
|
||||||
|
|
||||||
#ifdef SUPRESS_TFLITE_ERRORS
|
#ifdef SUPRESS_TFLITE_ERRORS
|
||||||
this->error_reporter = new tflite::OwnMicroErrorReporter;
|
this->error_reporter = new tflite::OwnMicroErrorReporter;
|
||||||
@@ -216,12 +236,16 @@ void CTfLiteClass::LoadModel(std::string _fn){
|
|||||||
this->error_reporter = new tflite::MicroErrorReporter;
|
this->error_reporter = new tflite::MicroErrorReporter;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
unsigned char *rd;
|
modelload = ReadFileToCharArray(_fn.c_str());
|
||||||
rd = ReadFileToCharArray(_fn.c_str());
|
|
||||||
|
|
||||||
this->model = tflite::GetModel(rd);
|
if (modelload == NULL)
|
||||||
free(rd);
|
return false;
|
||||||
|
|
||||||
|
model = tflite::GetModel(modelload);
|
||||||
|
// free(rd);
|
||||||
TFLITE_MINIMAL_CHECK(model != nullptr);
|
TFLITE_MINIMAL_CHECK(model != nullptr);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -232,7 +256,7 @@ CTfLiteClass::CTfLiteClass()
|
|||||||
this->interpreter = nullptr;
|
this->interpreter = nullptr;
|
||||||
this->input = nullptr;
|
this->input = nullptr;
|
||||||
this->output = nullptr;
|
this->output = nullptr;
|
||||||
this->kTensorArenaSize = 200 * 1024; /// laut testfile: 108000 - bisher 600
|
this->kTensorArenaSize = 800 * 1024; /// laut testfile: 108000 - bisher 600;; 2021-09-11: 200 * 1024
|
||||||
this->tensor_arena = new uint8_t[kTensorArenaSize];
|
this->tensor_arena = new uint8_t[kTensorArenaSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,6 +265,8 @@ CTfLiteClass::~CTfLiteClass()
|
|||||||
delete this->tensor_arena;
|
delete this->tensor_arena;
|
||||||
delete this->interpreter;
|
delete this->interpreter;
|
||||||
delete this->error_reporter;
|
delete this->error_reporter;
|
||||||
|
if (modelload)
|
||||||
|
free(modelload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ class CTfLiteClass
|
|||||||
int kTensorArenaSize;
|
int kTensorArenaSize;
|
||||||
uint8_t *tensor_arena;
|
uint8_t *tensor_arena;
|
||||||
|
|
||||||
|
unsigned char *modelload = NULL;
|
||||||
|
|
||||||
|
|
||||||
float* input;
|
float* input;
|
||||||
int input_i;
|
int input_i;
|
||||||
int im_height, im_width, im_channel;
|
int im_height, im_width, im_channel;
|
||||||
@@ -56,14 +59,18 @@ class CTfLiteClass
|
|||||||
public:
|
public:
|
||||||
CTfLiteClass();
|
CTfLiteClass();
|
||||||
~CTfLiteClass();
|
~CTfLiteClass();
|
||||||
void LoadModel(std::string _fn);
|
bool LoadModel(std::string _fn);
|
||||||
void MakeAllocate();
|
void MakeAllocate();
|
||||||
void GetInputTensorSize();
|
void GetInputTensorSize();
|
||||||
bool LoadInputImageBasis(CImageBasis *rs);
|
bool LoadInputImageBasis(CImageBasis *rs);
|
||||||
void Invoke();
|
void Invoke();
|
||||||
void GetOutPut();
|
int GetAnzOutPut(bool silent = true);
|
||||||
int GetOutClassification();
|
// void GetOutPut();
|
||||||
|
// int GetOutClassification();
|
||||||
|
int GetOutClassification(int _von = -1, int _bis = -1);
|
||||||
|
|
||||||
int GetClassFromImageBasis(CImageBasis *rs);
|
int GetClassFromImageBasis(CImageBasis *rs);
|
||||||
|
std::string GetStatusFlow();
|
||||||
|
|
||||||
float GetOutputValue(int nr);
|
float GetOutputValue(int nr);
|
||||||
void GetInputDimension(bool silent);
|
void GetInputDimension(bool silent);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "defines.h"
|
||||||
#include "Helper.h"
|
#include "Helper.h"
|
||||||
|
|
||||||
#include "esp_camera.h"
|
#include "esp_camera.h"
|
||||||
@@ -17,8 +18,9 @@
|
|||||||
#include "ClassFlowControll.h"
|
#include "ClassFlowControll.h"
|
||||||
|
|
||||||
#include "ClassLogFile.h"
|
#include "ClassLogFile.h"
|
||||||
|
#include "server_GPIO.h"
|
||||||
|
|
||||||
//#define DEBUG_DETAIL_ON
|
// #define DEBUG_DETAIL_ON
|
||||||
|
|
||||||
|
|
||||||
ClassFlowControll tfliteflow;
|
ClassFlowControll tfliteflow;
|
||||||
@@ -26,9 +28,6 @@ ClassFlowControll tfliteflow;
|
|||||||
TaskHandle_t xHandleblink_task_doFlow = NULL;
|
TaskHandle_t xHandleblink_task_doFlow = NULL;
|
||||||
TaskHandle_t xHandletask_autodoFlow = NULL;
|
TaskHandle_t xHandletask_autodoFlow = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool flowisrunning = false;
|
bool flowisrunning = false;
|
||||||
|
|
||||||
long auto_intervall = 0;
|
long auto_intervall = 0;
|
||||||
@@ -37,6 +36,9 @@ bool auto_isrunning = false;
|
|||||||
|
|
||||||
int countRounds = 0;
|
int countRounds = 0;
|
||||||
|
|
||||||
|
static const char *TAGTFLITE = "server_tflite";
|
||||||
|
|
||||||
|
|
||||||
int getCountFlowRounds() {
|
int getCountFlowRounds() {
|
||||||
return countRounds;
|
return countRounds;
|
||||||
}
|
}
|
||||||
@@ -64,9 +66,11 @@ void KillTFliteTasks()
|
|||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
printf("Handle: xHandleblink_task_doFlow: %ld\n", (long) xHandleblink_task_doFlow);
|
printf("Handle: xHandleblink_task_doFlow: %ld\n", (long) xHandleblink_task_doFlow);
|
||||||
#endif
|
#endif
|
||||||
if (xHandleblink_task_doFlow)
|
if (xHandleblink_task_doFlow != NULL)
|
||||||
{
|
{
|
||||||
vTaskDelete(xHandleblink_task_doFlow);
|
TaskHandle_t xHandleblink_task_doFlowTmp = xHandleblink_task_doFlow;
|
||||||
|
xHandleblink_task_doFlow = NULL;
|
||||||
|
vTaskDelete(xHandleblink_task_doFlowTmp);
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
printf("Killed: xHandleblink_task_doFlow\n");
|
printf("Killed: xHandleblink_task_doFlow\n");
|
||||||
#endif
|
#endif
|
||||||
@@ -75,9 +79,11 @@ void KillTFliteTasks()
|
|||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
printf("Handle: xHandletask_autodoFlow: %ld\n", (long) xHandletask_autodoFlow);
|
printf("Handle: xHandletask_autodoFlow: %ld\n", (long) xHandletask_autodoFlow);
|
||||||
#endif
|
#endif
|
||||||
if (xHandletask_autodoFlow)
|
if (xHandletask_autodoFlow != NULL)
|
||||||
{
|
{
|
||||||
vTaskDelete(xHandletask_autodoFlow);
|
TaskHandle_t xHandletask_autodoFlowTmp = xHandletask_autodoFlow;
|
||||||
|
xHandletask_autodoFlow = NULL;
|
||||||
|
vTaskDelete(xHandletask_autodoFlowTmp);
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
printf("Killed: xHandletask_autodoFlow\n");
|
printf("Killed: xHandletask_autodoFlow\n");
|
||||||
#endif
|
#endif
|
||||||
@@ -87,11 +93,10 @@ void KillTFliteTasks()
|
|||||||
|
|
||||||
void doInit(void)
|
void doInit(void)
|
||||||
{
|
{
|
||||||
string config = "/sdcard/config/config.ini";
|
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
printf("Start tfliteflow.InitFlow(config);\n");
|
printf("Start tfliteflow.InitFlow(config);\n");
|
||||||
#endif
|
#endif
|
||||||
tfliteflow.InitFlow(config);
|
tfliteflow.InitFlow(CONFIG_FILE);
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
printf("Finished tfliteflow.InitFlow(config);\n");
|
printf("Finished tfliteflow.InitFlow(config);\n");
|
||||||
#endif
|
#endif
|
||||||
@@ -136,7 +141,7 @@ esp_err_t handler_init(httpd_req_t *req)
|
|||||||
printf("handler_doinit uri:\n"); printf(req->uri); printf("\n");
|
printf("handler_doinit uri:\n"); printf(req->uri); printf("\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char* resp_str = "Init started<br>";
|
const char* resp_str = "Init started<br>";
|
||||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||||
|
|
||||||
doInit();
|
doInit();
|
||||||
@@ -159,8 +164,6 @@ esp_err_t handler_doflow(httpd_req_t *req)
|
|||||||
LogFile.WriteHeapInfo("handler_doflow - Start");
|
LogFile.WriteHeapInfo("handler_doflow - Start");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char* resp_str;
|
|
||||||
|
|
||||||
printf("handler_doFlow uri: "); printf(req->uri); printf("\n");
|
printf("handler_doFlow uri: "); printf(req->uri); printf("\n");
|
||||||
|
|
||||||
if (flowisrunning)
|
if (flowisrunning)
|
||||||
@@ -173,7 +176,7 @@ esp_err_t handler_doflow(httpd_req_t *req)
|
|||||||
{
|
{
|
||||||
xTaskCreate(&blink_task_doFlow, "blink_doFlow", configMINIMAL_STACK_SIZE * 64, NULL, tskIDLE_PRIORITY+1, &xHandleblink_task_doFlow);
|
xTaskCreate(&blink_task_doFlow, "blink_doFlow", configMINIMAL_STACK_SIZE * 64, NULL, tskIDLE_PRIORITY+1, &xHandleblink_task_doFlow);
|
||||||
}
|
}
|
||||||
resp_str = "doFlow gestartet - dauert ca. 60 Sekunden";
|
const char* resp_str = "doFlow gestartet - dauert ca. 60 Sekunden";
|
||||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||||
/* Respond with an empty chunk to signal HTTP response completion */
|
/* Respond with an empty chunk to signal HTTP response completion */
|
||||||
httpd_resp_send_chunk(req, NULL, 0);
|
httpd_resp_send_chunk(req, NULL, 0);
|
||||||
@@ -186,6 +189,36 @@ esp_err_t handler_doflow(httpd_req_t *req)
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t handler_json(httpd_req_t *req)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_DETAIL_ON
|
||||||
|
LogFile.WriteHeapInfo("handler_json - Start");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
printf("handler_JSON uri:\n"); printf(req->uri); printf("\n");
|
||||||
|
|
||||||
|
char _query[100];
|
||||||
|
char _size[10];
|
||||||
|
|
||||||
|
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||||
|
httpd_resp_set_type(req, "application/json");
|
||||||
|
|
||||||
|
std::string zw = tfliteflow.getJSON();
|
||||||
|
if (zw.length() > 0)
|
||||||
|
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||||
|
|
||||||
|
string query = std::string(_query);
|
||||||
|
|
||||||
|
/* Respond with an empty chunk to signal HTTP response completion */
|
||||||
|
httpd_resp_sendstr_chunk(req, NULL);
|
||||||
|
|
||||||
|
#ifdef DEBUG_DETAIL_ON
|
||||||
|
LogFile.WriteHeapInfo("handler_JSON - Done");
|
||||||
|
#endif
|
||||||
|
return ESP_OK;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
esp_err_t handler_wasserzaehler(httpd_req_t *req)
|
esp_err_t handler_wasserzaehler(httpd_req_t *req)
|
||||||
@@ -196,6 +229,8 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req)
|
|||||||
|
|
||||||
bool _rawValue = false;
|
bool _rawValue = false;
|
||||||
bool _noerror = false;
|
bool _noerror = false;
|
||||||
|
bool _all = false;
|
||||||
|
std::string _type = "value";
|
||||||
string zw;
|
string zw;
|
||||||
|
|
||||||
printf("handler_wasserzaehler uri:\n"); printf(req->uri); printf("\n");
|
printf("handler_wasserzaehler uri:\n"); printf(req->uri); printf("\n");
|
||||||
@@ -206,6 +241,22 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req)
|
|||||||
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
|
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
|
||||||
{
|
{
|
||||||
// printf("Query: "); printf(_query); printf("\n");
|
// printf("Query: "); printf(_query); printf("\n");
|
||||||
|
if (httpd_query_key_value(_query, "all", _size, 10) == ESP_OK)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_DETAIL_ON
|
||||||
|
printf("all is found"); printf(_size); printf("\n");
|
||||||
|
#endif
|
||||||
|
_all = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (httpd_query_key_value(_query, "type", _size, 10) == ESP_OK)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_DETAIL_ON
|
||||||
|
printf("all is found"); printf(_size); printf("\n");
|
||||||
|
#endif
|
||||||
|
_type = std::string(_size);
|
||||||
|
}
|
||||||
|
|
||||||
if (httpd_query_key_value(_query, "rawvalue", _size, 10) == ESP_OK)
|
if (httpd_query_key_value(_query, "rawvalue", _size, 10) == ESP_OK)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
@@ -222,6 +273,29 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||||
|
|
||||||
|
if (_all)
|
||||||
|
{
|
||||||
|
httpd_resp_set_type(req, "text/plain");
|
||||||
|
printf("TYPE: %s\n", _type.c_str());
|
||||||
|
int _intype = READOUT_TYPE_VALUE;
|
||||||
|
if (_type == "prevalue")
|
||||||
|
_intype = READOUT_TYPE_PREVALUE;
|
||||||
|
if (_type == "raw")
|
||||||
|
_intype = READOUT_TYPE_RAWVALUE;
|
||||||
|
if (_type == "error")
|
||||||
|
_intype = READOUT_TYPE_ERROR;
|
||||||
|
|
||||||
|
|
||||||
|
zw = tfliteflow.getReadoutAll(_intype);
|
||||||
|
printf("ZW: %s\n", zw.c_str());
|
||||||
|
if (zw.length() > 0)
|
||||||
|
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||||
|
httpd_resp_sendstr_chunk(req, NULL);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
zw = tfliteflow.getReadout(_rawValue, _noerror);
|
zw = tfliteflow.getReadout(_rawValue, _noerror);
|
||||||
if (zw.length() > 0)
|
if (zw.length() > 0)
|
||||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||||
@@ -236,37 +310,48 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req)
|
|||||||
txt = txt + "Digital Counter: <p> ";
|
txt = txt + "Digital Counter: <p> ";
|
||||||
httpd_resp_sendstr_chunk(req, txt.c_str());
|
httpd_resp_sendstr_chunk(req, txt.c_str());
|
||||||
|
|
||||||
std::vector<HTMLInfo*> htmlinfo;
|
std::vector<HTMLInfo*> htmlinfodig;
|
||||||
htmlinfo = tfliteflow.GetAllDigital();
|
htmlinfodig = tfliteflow.GetAllDigital();
|
||||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
for (int i = 0; i < htmlinfodig.size(); ++i)
|
||||||
{
|
{
|
||||||
if (htmlinfo[i]->val == 10)
|
if (tfliteflow.GetTypeDigital() == Digital)
|
||||||
zw = "NaN";
|
{
|
||||||
|
if (htmlinfodig[i]->val == 10)
|
||||||
|
zw = "NaN";
|
||||||
|
else
|
||||||
|
zw = to_string((int) htmlinfodig[i]->val);
|
||||||
|
|
||||||
|
txt = "<img src=\"/img_tmp/" + htmlinfodig[i]->filename + "\"> " + zw;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
zw = to_string((int) htmlinfo[i]->val);
|
std::stringstream stream;
|
||||||
|
stream << std::fixed << std::setprecision(1) << htmlinfodig[i]->val;
|
||||||
|
zw = stream.str();
|
||||||
|
|
||||||
|
txt = "<img src=\"/img_tmp/" + htmlinfodig[i]->filename + "\"> " + zw;
|
||||||
}
|
}
|
||||||
txt = "<img src=\"/img_tmp/" + htmlinfo[i]->filename + "\"> " + zw;
|
|
||||||
httpd_resp_sendstr_chunk(req, txt.c_str());
|
httpd_resp_sendstr_chunk(req, txt.c_str());
|
||||||
delete htmlinfo[i];
|
delete htmlinfodig[i];
|
||||||
}
|
}
|
||||||
htmlinfo.clear();
|
htmlinfodig.clear();
|
||||||
|
|
||||||
txt = " <p> Analog Meter: <p> ";
|
txt = " <p> Analog Meter: <p> ";
|
||||||
httpd_resp_sendstr_chunk(req, txt.c_str());
|
httpd_resp_sendstr_chunk(req, txt.c_str());
|
||||||
|
|
||||||
htmlinfo = tfliteflow.GetAllAnalog();
|
std::vector<HTMLInfo*> htmlinfoana;
|
||||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
htmlinfoana = tfliteflow.GetAllAnalog();
|
||||||
|
for (int i = 0; i < htmlinfoana.size(); ++i)
|
||||||
{
|
{
|
||||||
std::stringstream stream;
|
std::stringstream stream;
|
||||||
stream << std::fixed << std::setprecision(1) << htmlinfo[i]->val;
|
stream << std::fixed << std::setprecision(1) << htmlinfoana[i]->val;
|
||||||
zw = stream.str();
|
zw = stream.str();
|
||||||
|
|
||||||
txt = "<img src=\"/img_tmp/" + htmlinfo[i]->filename + "\"> " + zw;
|
txt = "<img src=\"/img_tmp/" + htmlinfoana[i]->filename + "\"> " + zw;
|
||||||
httpd_resp_sendstr_chunk(req, txt.c_str());
|
httpd_resp_sendstr_chunk(req, txt.c_str());
|
||||||
delete htmlinfo[i];
|
delete htmlinfoana[i];
|
||||||
}
|
}
|
||||||
htmlinfo.clear();
|
htmlinfoana.clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,7 +514,7 @@ esp_err_t handler_editflow(httpd_req_t *req)
|
|||||||
|
|
||||||
// printf("Parameter host: "); printf(_host.c_str()); printf("\n");
|
// printf("Parameter host: "); printf(_host.c_str()); printf("\n");
|
||||||
// string zwzw = "Do " + _task + " start\n"; printf(zwzw.c_str());
|
// string zwzw = "Do " + _task + " start\n"; printf(zwzw.c_str());
|
||||||
bool changed = Camera.SetBrightnessContrastSaturation(bri, con, sat);
|
Camera.SetBrightnessContrastSaturation(bri, con, sat);
|
||||||
std::string zw = tfliteflow.doSingleStep("[MakeImage]", _host);
|
std::string zw = tfliteflow.doSingleStep("[MakeImage]", _host);
|
||||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||||
}
|
}
|
||||||
@@ -446,31 +531,7 @@ esp_err_t handler_editflow(httpd_req_t *req)
|
|||||||
// string zwzw = "Do " + _task + " start\n"; printf(zwzw.c_str());
|
// string zwzw = "Do " + _task + " start\n"; printf(zwzw.c_str());
|
||||||
std::string zw = tfliteflow.doSingleStep("[Alignment]", _host);
|
std::string zw = tfliteflow.doSingleStep("[Alignment]", _host);
|
||||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||||
}
|
}
|
||||||
if (_task.compare("test_analog") == 0)
|
|
||||||
{
|
|
||||||
std::string _host = "";
|
|
||||||
if (httpd_query_key_value(_query, "host", _valuechar, 30) == ESP_OK) {
|
|
||||||
_host = std::string(_valuechar);
|
|
||||||
}
|
|
||||||
// printf("Parameter host: "); printf(_host.c_str()); printf("\n");
|
|
||||||
// string zwzw = "Do " + _task + " start\n"; printf(zwzw.c_str());
|
|
||||||
std::string zw = tfliteflow.doSingleStep("[Analog]", _host);
|
|
||||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
|
||||||
}
|
|
||||||
if (_task.compare("test_digits") == 0)
|
|
||||||
{
|
|
||||||
std::string _host = "";
|
|
||||||
if (httpd_query_key_value(_query, "host", _valuechar, 30) == ESP_OK) {
|
|
||||||
_host = std::string(_valuechar);
|
|
||||||
}
|
|
||||||
// printf("Parameter host: "); printf(_host.c_str()); printf("\n");
|
|
||||||
|
|
||||||
// string zwzw = "Do " + _task + " start\n"; printf(zwzw.c_str());
|
|
||||||
std::string zw = tfliteflow.doSingleStep("[Digits]", _host);
|
|
||||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Respond with an empty chunk to signal HTTP response completion */
|
/* Respond with an empty chunk to signal HTTP response completion */
|
||||||
httpd_resp_sendstr_chunk(req, NULL);
|
httpd_resp_sendstr_chunk(req, NULL);
|
||||||
@@ -483,6 +544,34 @@ esp_err_t handler_editflow(httpd_req_t *req)
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t handler_statusflow(httpd_req_t *req)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_DETAIL_ON
|
||||||
|
LogFile.WriteHeapInfo("handler_prevalue - Start");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const char* resp_str;
|
||||||
|
|
||||||
|
#ifdef DEBUG_DETAIL_ON
|
||||||
|
printf("handler_prevalue:\n"); printf(req->uri); printf("\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
string* zw = tfliteflow.getActStatus();
|
||||||
|
resp_str = zw->c_str();
|
||||||
|
|
||||||
|
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||||
|
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||||
|
/* Respond with an empty chunk to signal HTTP response completion */
|
||||||
|
httpd_resp_send_chunk(req, NULL, 0);
|
||||||
|
|
||||||
|
#ifdef DEBUG_DETAIL_ON
|
||||||
|
LogFile.WriteHeapInfo("handler_prevalue - Start");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
esp_err_t handler_prevalue(httpd_req_t *req)
|
esp_err_t handler_prevalue(httpd_req_t *req)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_DETAIL_ON
|
#ifdef DEBUG_DETAIL_ON
|
||||||
@@ -498,6 +587,7 @@ esp_err_t handler_prevalue(httpd_req_t *req)
|
|||||||
|
|
||||||
char _query[100];
|
char _query[100];
|
||||||
char _size[10] = "";
|
char _size[10] = "";
|
||||||
|
char _numbers[50] = "default";
|
||||||
|
|
||||||
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
|
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
|
||||||
{
|
{
|
||||||
@@ -511,15 +601,24 @@ esp_err_t handler_prevalue(httpd_req_t *req)
|
|||||||
printf("Value: "); printf(_size); printf("\n");
|
printf("Value: "); printf(_size); printf("\n");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
httpd_query_key_value(_query, "numbers", _numbers, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen(_size) == 0)
|
if (strlen(_size) == 0)
|
||||||
zw = tfliteflow.GetPrevalue();
|
{
|
||||||
|
zw = tfliteflow.GetPrevalue(std::string(_numbers));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
zw = "SetPrevalue to " + tfliteflow.UpdatePrevalue(_size);
|
{
|
||||||
|
zw = "SetPrevalue to " + tfliteflow.UpdatePrevalue(_size, _numbers, true);
|
||||||
|
}
|
||||||
|
|
||||||
resp_str = zw.c_str();
|
resp_str = zw.c_str();
|
||||||
|
|
||||||
|
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||||
|
|
||||||
|
|
||||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||||
/* Respond with an empty chunk to signal HTTP response completion */
|
/* Respond with an empty chunk to signal HTTP response completion */
|
||||||
httpd_resp_send_chunk(req, NULL, 0);
|
httpd_resp_send_chunk(req, NULL, 0);
|
||||||
@@ -535,17 +634,17 @@ void task_autodoFlow(void *pvParameter)
|
|||||||
{
|
{
|
||||||
int64_t fr_start, fr_delta_ms;
|
int64_t fr_start, fr_delta_ms;
|
||||||
|
|
||||||
|
printf("task_autodoFlow: start\r\n");
|
||||||
doInit();
|
doInit();
|
||||||
|
gpio_handler_init();
|
||||||
auto_isrunning = tfliteflow.isAutoStart(auto_intervall);
|
|
||||||
|
|
||||||
|
auto_isrunning = tfliteflow.isAutoStart(auto_intervall);
|
||||||
if (isSetupModusActive()) {
|
if (isSetupModusActive()) {
|
||||||
auto_isrunning = false;
|
auto_isrunning = false;
|
||||||
std::string zw_time = gettimestring(LOGFILE_TIME_FORMAT);
|
std::string zw_time = gettimestring(LOGFILE_TIME_FORMAT);
|
||||||
tfliteflow.doFlowMakeImageOnly(zw_time);
|
tfliteflow.doFlowMakeImageOnly(zw_time);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (auto_isrunning)
|
while (auto_isrunning)
|
||||||
{
|
{
|
||||||
std::string _zw = "task_autodoFlow - next round - Round #" + std::to_string(++countRounds);
|
std::string _zw = "task_autodoFlow - next round - Round #" + std::to_string(++countRounds);
|
||||||
@@ -590,11 +689,33 @@ void task_autodoFlow(void *pvParameter)
|
|||||||
}
|
}
|
||||||
vTaskDelete(NULL); //Delete this task if it exits from the loop above
|
vTaskDelete(NULL); //Delete this task if it exits from the loop above
|
||||||
xHandletask_autodoFlow = NULL;
|
xHandletask_autodoFlow = NULL;
|
||||||
|
printf("task_autodoFlow: end\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TFliteDoAutoStart()
|
void TFliteDoAutoStart()
|
||||||
{
|
{
|
||||||
xTaskCreate(&task_autodoFlow, "task_autodoFlow", configMINIMAL_STACK_SIZE * 64, NULL, tskIDLE_PRIORITY+1, &xHandletask_autodoFlow);
|
BaseType_t xReturned;
|
||||||
|
|
||||||
|
int _i = configMINIMAL_STACK_SIZE;
|
||||||
|
|
||||||
|
printf("task_autodoFlow configMINIMAL_STACK_SIZE: %d\n", _i);
|
||||||
|
printf("getESPHeapInfo: %s\n", getESPHeapInfo().c_str());
|
||||||
|
|
||||||
|
xReturned = xTaskCreate(&task_autodoFlow, "task_autodoFlow", configMINIMAL_STACK_SIZE * 60, NULL, tskIDLE_PRIORITY+1, &xHandletask_autodoFlow);
|
||||||
|
if( xReturned != pdPASS )
|
||||||
|
{
|
||||||
|
|
||||||
|
//Memory: 64 --> 48 --> 35 --> 25
|
||||||
|
printf("ERROR task_autodoFlow konnte nicht erzeugt werden !!\r\n");
|
||||||
|
}
|
||||||
|
printf("getESPHeapInfo: %s\n", getESPHeapInfo().c_str());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetMQTTMainTopic()
|
||||||
|
{
|
||||||
|
return tfliteflow.GetMQTTMainTopic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -621,6 +742,10 @@ void register_server_tflite_uri(httpd_handle_t server)
|
|||||||
camuri.user_ctx = (void*) "Light Off";
|
camuri.user_ctx = (void*) "Light Off";
|
||||||
httpd_register_uri_handler(server, &camuri);
|
httpd_register_uri_handler(server, &camuri);
|
||||||
|
|
||||||
|
camuri.uri = "/statusflow.html";
|
||||||
|
camuri.handler = handler_statusflow;
|
||||||
|
camuri.user_ctx = (void*) "Light Off";
|
||||||
|
httpd_register_uri_handler(server, &camuri);
|
||||||
|
|
||||||
camuri.uri = "/editflow.html";
|
camuri.uri = "/editflow.html";
|
||||||
camuri.handler = handler_editflow;
|
camuri.handler = handler_editflow;
|
||||||
@@ -631,4 +756,10 @@ void register_server_tflite_uri(httpd_handle_t server)
|
|||||||
camuri.handler = handler_wasserzaehler;
|
camuri.handler = handler_wasserzaehler;
|
||||||
camuri.user_ctx = (void*) "Wasserzaehler";
|
camuri.user_ctx = (void*) "Wasserzaehler";
|
||||||
httpd_register_uri_handler(server, &camuri);
|
httpd_register_uri_handler(server, &camuri);
|
||||||
|
|
||||||
|
camuri.uri = "/json";
|
||||||
|
camuri.handler = handler_json;
|
||||||
|
camuri.user_ctx = (void*) "JSON";
|
||||||
|
httpd_register_uri_handler(server, &camuri);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <esp_http_server.h>
|
#include <esp_http_server.h>
|
||||||
#include "CImageBasis.h"
|
#include "CImageBasis.h"
|
||||||
|
|
||||||
//#include "ClassControllCamera.h"
|
//#include "ClassControllCamera.h"
|
||||||
|
|
||||||
static const char *TAGTFLITE = "server_tflite";
|
|
||||||
|
|
||||||
void register_server_tflite_uri(httpd_handle_t server);
|
void register_server_tflite_uri(httpd_handle_t server);
|
||||||
|
|
||||||
void KillTFliteTasks();
|
void KillTFliteTasks();
|
||||||
@@ -15,6 +14,8 @@ void TFliteDoAutoStart();
|
|||||||
|
|
||||||
bool isSetupModusActive();
|
bool isSetupModusActive();
|
||||||
|
|
||||||
|
std::string GetMQTTMainTopic();
|
||||||
|
|
||||||
esp_err_t GetJPG(std::string _filename, httpd_req_t *req);
|
esp_err_t GetJPG(std::string _filename, httpd_req_t *req);
|
||||||
|
|
||||||
esp_err_t GetRawJPG(httpd_req_t *req);
|
esp_err_t GetRawJPG(httpd_req_t *req);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
static const char *TAG = "sntp";
|
static const char *TAG = "sntp";
|
||||||
|
|
||||||
bool setTimeAlwaysOnReboot = true;
|
bool setTimeAlwaysOnReboot = true;
|
||||||
|
time_t bootTime;
|
||||||
|
|
||||||
static void obtain_time(void);
|
static void obtain_time(void);
|
||||||
static void initialize_sntp(void);
|
static void initialize_sntp(void);
|
||||||
@@ -27,6 +28,17 @@ void time_sync_notification_cb(struct timeval *tv)
|
|||||||
ESP_LOGI(TAG, "Notification of a time synchronization event");
|
ESP_LOGI(TAG, "Notification of a time synchronization event");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ConvertTimeToString(time_t _time, const char * frm)
|
||||||
|
{
|
||||||
|
struct tm timeinfo;
|
||||||
|
char strftime_buf[64];
|
||||||
|
localtime_r(&_time, &timeinfo);
|
||||||
|
strftime(strftime_buf, sizeof(strftime_buf), frm, &timeinfo);
|
||||||
|
|
||||||
|
std::string result(strftime_buf);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
std::string gettimestring(const char * frm)
|
std::string gettimestring(const char * frm)
|
||||||
{
|
{
|
||||||
time_t now;
|
time_t now;
|
||||||
@@ -114,4 +126,17 @@ static void initialize_sntp(void)
|
|||||||
sntp_setservername(0, "pool.ntp.org");
|
sntp_setservername(0, "pool.ntp.org");
|
||||||
// sntp_set_time_sync_notification_cb(time_sync_notification_cb);
|
// sntp_set_time_sync_notification_cb(time_sync_notification_cb);
|
||||||
sntp_init();
|
sntp_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBootTime()
|
||||||
|
{
|
||||||
|
time(&bootTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t getUpTime()
|
||||||
|
{
|
||||||
|
time_t now;
|
||||||
|
time(&now);
|
||||||
|
|
||||||
|
return now - bootTime;
|
||||||
}
|
}
|
||||||
@@ -15,5 +15,10 @@
|
|||||||
void setup_time(void);
|
void setup_time(void);
|
||||||
|
|
||||||
std::string gettimestring(const char * frm);
|
std::string gettimestring(const char * frm);
|
||||||
|
std::string ConvertTimeToString(time_t _time, const char * frm);
|
||||||
|
|
||||||
void setTimeZone(std::string _tzstring);
|
void setTimeZone(std::string _tzstring);
|
||||||
void reset_servername(std::string _servername);
|
void reset_servername(std::string _servername);
|
||||||
|
|
||||||
|
void setBootTime();
|
||||||
|
time_t getUpTime();
|
||||||
|
|||||||
7
code/components/jomjol_wlan/CMakeLists.txt
Normal file
7
code/components/jomjol_wlan/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||||
|
|
||||||
|
idf_component_register(SRCS ${app_sources}
|
||||||
|
INCLUDE_DIRS "."
|
||||||
|
REQUIRES nvs_flash jomjol_helper)
|
||||||
|
|
||||||
|
|
||||||
242
code/components/jomjol_wlan/connect_wlan.cpp
Normal file
242
code/components/jomjol_wlan/connect_wlan.cpp
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
#include "connect_wlan.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#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_event.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
|
||||||
|
#include "lwip/err.h"
|
||||||
|
#include "lwip/sys.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define EXAMPLE_ESP_MAXIMUM_RETRY 1000
|
||||||
|
|
||||||
|
/* FreeRTOS event group to signal when we are connected*/
|
||||||
|
static EventGroupHandle_t s_wifi_event_group;
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
static const char *TAG = "wifi station";
|
||||||
|
|
||||||
|
static int s_retry_num = 0;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
#define BLINK_GPIO GPIO_NUM_33
|
||||||
|
|
||||||
|
int BlinkDauer;
|
||||||
|
int BlinkAnzahl;
|
||||||
|
bool BlinkOff;
|
||||||
|
bool BlinkIsRunning = false;
|
||||||
|
|
||||||
|
std::string hostname = "";
|
||||||
|
std::string std_hostname = "watermeter";
|
||||||
|
std::string ipadress = "";
|
||||||
|
std::string ssid = "";
|
||||||
|
|
||||||
|
std::string* getIPAddress()
|
||||||
|
{
|
||||||
|
return &ipadress;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string* getSSID()
|
||||||
|
{
|
||||||
|
return &ssid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void task_doBlink(void *pvParameter)
|
||||||
|
{
|
||||||
|
ESP_LOGI("BLINK", "Blinken - 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", "Blinken - 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", configMINIMAL_STACK_SIZE * 8, 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) {
|
||||||
|
LEDBlinkTask(200, 1, true);
|
||||||
|
esp_wifi_connect();
|
||||||
|
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||||
|
// if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY){
|
||||||
|
esp_wifi_connect();
|
||||||
|
s_retry_num++;
|
||||||
|
ESP_LOGI(TAG, "retry to connect to the AP");
|
||||||
|
ESP_LOGI(TAG,"connect to the AP fail");
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
ssid = std::string(_ssid);
|
||||||
|
|
||||||
|
s_wifi_event_group = xEventGroupCreate();
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_netif_init());
|
||||||
|
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
esp_netif_t *my_sta = esp_netif_create_default_wifi_sta();
|
||||||
|
|
||||||
|
if ((_ipadr != NULL) && (_gw != NULL) && (_netmask != NULL))
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "set IP %s, GW %s, Netmask %s manual", _ipadr, _gw, _netmask);
|
||||||
|
esp_netif_dhcpc_stop(my_sta);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
esp_netif_set_ip_info(my_sta, &ip_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||||
|
|
||||||
|
if ((_ipadr != NULL) && (_gw != NULL) && (_netmask != NULL))
|
||||||
|
{
|
||||||
|
if (_dns == NULL)
|
||||||
|
_dns = _gw;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "set DNS manual");
|
||||||
|
esp_netif_dns_info_t dns_info;
|
||||||
|
ip4_addr_t ip;
|
||||||
|
ip.addr = esp_ip4addr_aton(_dns);
|
||||||
|
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_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
|
||||||
|
|
||||||
|
wifi_config_t wifi_config = { };
|
||||||
|
|
||||||
|
strcpy((char*)wifi_config.sta.ssid, (const char*)_ssid);
|
||||||
|
strcpy((char*)wifi_config.sta.password, (const char*)_password);
|
||||||
|
|
||||||
|
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() );
|
||||||
|
|
||||||
|
if (_hostname != NULL)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
|
||||||
|
_ssid, _password);
|
||||||
|
} else if (bits & WIFI_FAIL_BIT) {
|
||||||
|
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
|
||||||
|
_ssid, _password);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "UNEXPECTED EVENT");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void wifi_init_sta(const char *_ssid, const char *_password, const char *_hostname)
|
||||||
|
{
|
||||||
|
wifi_init_sta(_ssid, _password, _hostname, NULL, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wifi_init_sta(const char *_ssid, const char *_password)
|
||||||
|
{
|
||||||
|
wifi_init_sta(_ssid, _password, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
16
code/components/jomjol_wlan/connect_wlan.h
Normal file
16
code/components/jomjol_wlan/connect_wlan.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#ifndef CONNECT_WLAN_H
|
||||||
|
#define CONNECT_WLAN_H
|
||||||
|
|
||||||
|
#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);
|
||||||
|
void wifi_init_sta(const char *_ssid, const char *_password, const char *_hostname);
|
||||||
|
void wifi_init_sta(const char *_ssid, const char *_password);
|
||||||
|
|
||||||
|
std::string* getIPAddress();
|
||||||
|
std::string* getSSID();
|
||||||
|
|
||||||
|
extern std::string hostname;
|
||||||
|
extern std::string std_hostname;
|
||||||
|
|
||||||
|
#endif
|
||||||
255
code/components/jomjol_wlan/read_wlanini.cpp
Normal file
255
code/components/jomjol_wlan/read_wlanini.cpp
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
#include "read_wlanini.h"
|
||||||
|
|
||||||
|
#include "Helper.h"
|
||||||
|
|
||||||
|
#include "connect_wlan.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<string> ZerlegeZeileWLAN(std::string input, std::string _delimiter = "")
|
||||||
|
{
|
||||||
|
std::vector<string> Output;
|
||||||
|
std::string delimiter = " =,";
|
||||||
|
if (_delimiter.length() > 0){
|
||||||
|
delimiter = _delimiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
input = trim(input, delimiter);
|
||||||
|
size_t pos = findDelimiterPos(input, delimiter);
|
||||||
|
std::string token;
|
||||||
|
if (pos != std::string::npos) // Zerlegt nur bis ersten Gleichheitszeichen !!! Sonderfall für WLAN.ini
|
||||||
|
{
|
||||||
|
token = input.substr(0, pos);
|
||||||
|
token = trim(token, delimiter);
|
||||||
|
Output.push_back(token);
|
||||||
|
input.erase(0, pos + 1);
|
||||||
|
input = trim(input, delimiter);
|
||||||
|
}
|
||||||
|
Output.push_back(input);
|
||||||
|
|
||||||
|
return Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void LoadWlanFromFile(std::string fn, char *&_ssid, char *&_password, char *&_hostname, char *&_ipadr, char *&_gw, char *&_netmask, char *&_dns)
|
||||||
|
{
|
||||||
|
std::string ssid = "";
|
||||||
|
std::string passphrase = "";
|
||||||
|
std::string ipaddress = "";
|
||||||
|
std::string gw = "";
|
||||||
|
std::string netmask = "";
|
||||||
|
std::string dns = "";
|
||||||
|
|
||||||
|
std::string line = "";
|
||||||
|
std::vector<string> zerlegt;
|
||||||
|
hostname = std_hostname;
|
||||||
|
|
||||||
|
FILE* pFile;
|
||||||
|
fn = FormatFileName(fn);
|
||||||
|
|
||||||
|
pFile = OpenFileAndWait(fn.c_str(), "r");
|
||||||
|
printf("file loaded\n");
|
||||||
|
|
||||||
|
if (pFile == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char zw[1024];
|
||||||
|
fgets(zw, 1024, pFile);
|
||||||
|
line = std::string(zw);
|
||||||
|
|
||||||
|
while ((line.size() > 0) || !(feof(pFile)))
|
||||||
|
{
|
||||||
|
// printf("%s", line.c_str());
|
||||||
|
zerlegt = ZerlegeZeileWLAN(line, "=");
|
||||||
|
zerlegt[0] = trim(zerlegt[0], " ");
|
||||||
|
|
||||||
|
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "HOSTNAME")){
|
||||||
|
hostname = trim(zerlegt[1]);
|
||||||
|
if ((hostname[0] == '"') && (hostname[hostname.length()-1] == '"')){
|
||||||
|
hostname = hostname.substr(1, hostname.length()-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "SSID")){
|
||||||
|
ssid = trim(zerlegt[1]);
|
||||||
|
if ((ssid[0] == '"') && (ssid[ssid.length()-1] == '"')){
|
||||||
|
ssid = ssid.substr(1, ssid.length()-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "PASSWORD")){
|
||||||
|
passphrase = zerlegt[1];
|
||||||
|
if ((passphrase[0] == '"') && (passphrase[passphrase.length()-1] == '"')){
|
||||||
|
passphrase = passphrase.substr(1, passphrase.length()-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "IP")){
|
||||||
|
ipaddress = zerlegt[1];
|
||||||
|
if ((ipaddress[0] == '"') && (ipaddress[ipaddress.length()-1] == '"')){
|
||||||
|
ipaddress = ipaddress.substr(1, ipaddress.length()-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "GATEWAY")){
|
||||||
|
gw = zerlegt[1];
|
||||||
|
if ((gw[0] == '"') && (gw[gw.length()-1] == '"')){
|
||||||
|
gw = gw.substr(1, gw.length()-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "NETMASK")){
|
||||||
|
netmask = zerlegt[1];
|
||||||
|
if ((netmask[0] == '"') && (netmask[netmask.length()-1] == '"')){
|
||||||
|
netmask = netmask.substr(1, netmask.length()-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "DNS")){
|
||||||
|
dns = zerlegt[1];
|
||||||
|
if ((dns[0] == '"') && (dns[dns.length()-1] == '"')){
|
||||||
|
dns = dns.substr(1, dns.length()-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (fgets(zw, 1024, pFile) == NULL)
|
||||||
|
{
|
||||||
|
line = "";
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
_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());
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool ChangeHostName(std::string fn, std::string _newhostname)
|
||||||
|
{
|
||||||
|
if (_newhostname == hostname)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
string line = "";
|
||||||
|
std::vector<string> zerlegt;
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
std::vector<string> neuesfile;
|
||||||
|
|
||||||
|
FILE* pFile;
|
||||||
|
fn = FormatFileName(fn);
|
||||||
|
pFile = OpenFileAndWait(fn.c_str(), "r");
|
||||||
|
|
||||||
|
printf("file loaded\n");
|
||||||
|
|
||||||
|
if (pFile == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
char zw[1024];
|
||||||
|
fgets(zw, 1024, pFile);
|
||||||
|
line = std::string(zw);
|
||||||
|
|
||||||
|
while ((line.size() > 0) || !(feof(pFile)))
|
||||||
|
{
|
||||||
|
printf("%s", line.c_str());
|
||||||
|
zerlegt = ZerlegeZeileWLAN(line, "=");
|
||||||
|
zerlegt[0] = trim(zerlegt[0], " ");
|
||||||
|
|
||||||
|
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "HOSTNAME")){
|
||||||
|
line = "hostname = \"" + _newhostname + "\"\n";
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
neuesfile.push_back(line);
|
||||||
|
|
||||||
|
if (fgets(zw, 1024, pFile) == NULL)
|
||||||
|
{
|
||||||
|
line = "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
line = std::string(zw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
line = "\nhostname = \"" + _newhostname + "\"\n";
|
||||||
|
neuesfile.push_back(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(pFile);
|
||||||
|
|
||||||
|
pFile = OpenFileAndWait(fn.c_str(), "w+");
|
||||||
|
|
||||||
|
for (int i = 0; i < neuesfile.size(); ++i)
|
||||||
|
{
|
||||||
|
printf(neuesfile[i].c_str());
|
||||||
|
fputs(neuesfile[i].c_str(), pFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(pFile);
|
||||||
|
|
||||||
|
printf("*** Update hostname done ***\n");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
11
code/components/jomjol_wlan/read_wlanini.h
Normal file
11
code/components/jomjol_wlan/read_wlanini.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef READ_WLANINI_H
|
||||||
|
#define READ_WLANINI_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
void LoadWlanFromFile(std::string fn, char *&_ssid, char *&_password, char *&_hostname, char *&_ipadr, char *&_gw, char *&_netmask, char *&_dns);
|
||||||
|
|
||||||
|
bool ChangeHostName(std::string fn, std::string _newhostname);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -23,7 +23,7 @@ if(NOT DEFINED ENV{IDF_PATH})
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS tensorflow/lite/micro/simple_memory_allocator.cc tensorflow/lite/micro/micro_error_reporter.cc tensorflow/lite/micro/memory_helpers.cc tensorflow/lite/micro/test_helpers.cc tensorflow/lite/micro/recording_micro_allocator.cc tensorflow/lite/micro/micro_time.cc tensorflow/lite/micro/recording_simple_memory_allocator.cc tensorflow/lite/micro/micro_string.cc tensorflow/lite/micro/micro_profiler.cc tensorflow/lite/micro/debug_log.cc tensorflow/lite/micro/all_ops_resolver.cc tensorflow/lite/micro/micro_utils.cc tensorflow/lite/micro/micro_interpreter.cc tensorflow/lite/micro/micro_allocator.cc tensorflow/lite/micro/system_setup.cc tensorflow/lite/micro/memory_planner/linear_memory_planner.cc tensorflow/lite/micro/memory_planner/greedy_memory_planner.cc tensorflow/lite/c/common.c tensorflow/lite/core/api/error_reporter.cc tensorflow/lite/core/api/flatbuffer_conversions.cc tensorflow/lite/core/api/op_resolver.cc tensorflow/lite/core/api/tensor_utils.cc tensorflow/lite/kernels/internal/quantization_util.cc tensorflow/lite/kernels/kernel_util.cc tensorflow/lite/schema/schema_utils.cc tensorflow/lite/micro/kernels/activations.cc tensorflow/lite/micro/kernels/add.cc tensorflow/lite/micro/kernels/add_n.cc tensorflow/lite/micro/kernels/arg_min_max.cc tensorflow/lite/micro/kernels/batch_to_space_nd.cc tensorflow/lite/micro/kernels/cast.cc tensorflow/lite/micro/kernels/ceil.cc tensorflow/lite/micro/kernels/circular_buffer.cc tensorflow/lite/micro/kernels/comparisons.cc tensorflow/lite/micro/kernels/concatenation.cc tensorflow/lite/micro/kernels/conv.cc tensorflow/lite/micro/kernels/conv_common.cc tensorflow/lite/micro/kernels/depthwise_conv.cc tensorflow/lite/micro/kernels/depthwise_conv_common.cc tensorflow/lite/micro/kernels/dequantize.cc tensorflow/lite/micro/kernels/detection_postprocess.cc tensorflow/lite/micro/kernels/div.cc tensorflow/lite/micro/kernels/elementwise.cc tensorflow/lite/micro/kernels/elu.cc tensorflow/lite/micro/kernels/ethosu.cc tensorflow/lite/micro/kernels/exp.cc tensorflow/lite/micro/kernels/expand_dims.cc tensorflow/lite/micro/kernels/fill.cc tensorflow/lite/micro/kernels/floor.cc tensorflow/lite/micro/kernels/fully_connected.cc tensorflow/lite/micro/kernels/fully_connected_common.cc tensorflow/lite/micro/kernels/hard_swish.cc tensorflow/lite/micro/kernels/kernel_runner.cc tensorflow/lite/micro/kernels/kernel_util.cc tensorflow/lite/micro/kernels/l2norm.cc tensorflow/lite/micro/kernels/l2_pool_2d.cc tensorflow/lite/micro/kernels/leaky_relu.cc tensorflow/lite/micro/kernels/logical.cc tensorflow/lite/micro/kernels/logistic.cc tensorflow/lite/micro/kernels/maximum_minimum.cc tensorflow/lite/micro/kernels/mul.cc tensorflow/lite/micro/kernels/neg.cc tensorflow/lite/micro/kernels/pack.cc tensorflow/lite/micro/kernels/pad.cc tensorflow/lite/micro/kernels/pooling.cc tensorflow/lite/micro/kernels/prelu.cc tensorflow/lite/micro/kernels/quantize.cc tensorflow/lite/micro/kernels/quantize_common.cc tensorflow/lite/micro/kernels/reduce.cc tensorflow/lite/micro/kernels/reshape.cc tensorflow/lite/micro/kernels/resize_nearest_neighbor.cc tensorflow/lite/micro/kernels/round.cc tensorflow/lite/micro/kernels/shape.cc tensorflow/lite/micro/kernels/softmax.cc tensorflow/lite/micro/kernels/softmax_common.cc tensorflow/lite/micro/kernels/space_to_batch_nd.cc tensorflow/lite/micro/kernels/split.cc tensorflow/lite/micro/kernels/split_v.cc tensorflow/lite/micro/kernels/squeeze.cc tensorflow/lite/micro/kernels/strided_slice.cc tensorflow/lite/micro/kernels/sub.cc tensorflow/lite/micro/kernels/svdf.cc tensorflow/lite/micro/kernels/svdf_common.cc tensorflow/lite/micro/kernels/tanh.cc tensorflow/lite/micro/kernels/transpose_conv.cc tensorflow/lite/micro/kernels/unpack.cc tensorflow/lite/micro/kernels/zeros_like.cc
|
SRCS tensorflow/lite/micro/simple_memory_allocator.cc tensorflow/lite/micro/debug_log.cc tensorflow/lite/micro/micro_error_reporter.cc tensorflow/lite/micro/memory_helpers.cc tensorflow/lite/micro/test_helpers.cc tensorflow/lite/micro/recording_micro_allocator.cc tensorflow/lite/micro/micro_time.cc tensorflow/lite/micro/recording_simple_memory_allocator.cc tensorflow/lite/micro/micro_string.cc tensorflow/lite/micro/micro_profiler.cc tensorflow/lite/micro/flatbuffer_utils.cc tensorflow/lite/micro/micro_graph.cc tensorflow/lite/micro/mock_micro_graph.cc tensorflow/lite/micro/all_ops_resolver.cc tensorflow/lite/micro/micro_utils.cc tensorflow/lite/micro/micro_interpreter.cc tensorflow/lite/micro/micro_allocator.cc tensorflow/lite/micro/system_setup.cc tensorflow/lite/micro/memory_planner/linear_memory_planner.cc tensorflow/lite/micro/memory_planner/greedy_memory_planner.cc tensorflow/lite/schema/schema_utils.cc tensorflow/lite/c/common.c tensorflow/lite/core/api/tensor_utils.cc tensorflow/lite/core/api/error_reporter.cc tensorflow/lite/core/api/flatbuffer_conversions.cc tensorflow/lite/core/api/op_resolver.cc tensorflow/lite/kernels/kernel_util.cc tensorflow/lite/kernels/internal/quantization_util.cc tensorflow/lite/kernels/internal/reference/portable_tensor_utils.cc tensorflow/lite/micro/kernels/activations.cc tensorflow/lite/micro/kernels/activations_common.cc tensorflow/lite/micro/kernels/add.cc tensorflow/lite/micro/kernels/add_n.cc tensorflow/lite/micro/kernels/arg_min_max.cc tensorflow/lite/micro/kernels/batch_to_space_nd.cc tensorflow/lite/micro/kernels/cast.cc tensorflow/lite/micro/kernels/ceil.cc tensorflow/lite/micro/kernels/circular_buffer.cc tensorflow/lite/micro/kernels/comparisons.cc tensorflow/lite/micro/kernels/concatenation.cc tensorflow/lite/micro/kernels/conv.cc tensorflow/lite/micro/kernels/conv_common.cc tensorflow/lite/micro/kernels/cumsum.cc tensorflow/lite/micro/kernels/depth_to_space.cc tensorflow/lite/micro/kernels/depthwise_conv.cc tensorflow/lite/micro/kernels/depthwise_conv_common.cc tensorflow/lite/micro/kernels/dequantize.cc tensorflow/lite/micro/kernels/detection_postprocess.cc tensorflow/lite/micro/kernels/elementwise.cc tensorflow/lite/micro/kernels/elu.cc tensorflow/lite/micro/kernels/ethosu.cc tensorflow/lite/micro/kernels/exp.cc tensorflow/lite/micro/kernels/expand_dims.cc tensorflow/lite/micro/kernels/fill.cc tensorflow/lite/micro/kernels/floor.cc tensorflow/lite/micro/kernels/floor_div.cc tensorflow/lite/micro/kernels/floor_mod.cc tensorflow/lite/micro/kernels/fully_connected.cc tensorflow/lite/micro/kernels/fully_connected_common.cc tensorflow/lite/micro/kernels/gather.cc tensorflow/lite/micro/kernels/gather_nd.cc tensorflow/lite/micro/kernels/hard_swish.cc tensorflow/lite/micro/kernels/hard_swish_common.cc tensorflow/lite/micro/kernels/if.cc tensorflow/lite/micro/kernels/kernel_runner.cc tensorflow/lite/micro/kernels/kernel_util.cc tensorflow/lite/micro/kernels/l2norm.cc tensorflow/lite/micro/kernels/l2_pool_2d.cc tensorflow/lite/micro/kernels/leaky_relu.cc tensorflow/lite/micro/kernels/logical.cc tensorflow/lite/micro/kernels/logical_common.cc tensorflow/lite/micro/kernels/logistic.cc tensorflow/lite/micro/kernels/logistic_common.cc tensorflow/lite/micro/kernels/log_softmax.cc tensorflow/lite/micro/kernels/maximum_minimum.cc tensorflow/lite/micro/kernels/mul.cc tensorflow/lite/micro/kernels/neg.cc tensorflow/lite/micro/kernels/pack.cc tensorflow/lite/micro/kernels/pad.cc tensorflow/lite/micro/kernels/pooling.cc tensorflow/lite/micro/kernels/pooling_common.cc tensorflow/lite/micro/kernels/prelu.cc tensorflow/lite/micro/kernels/quantize.cc tensorflow/lite/micro/kernels/quantize_common.cc tensorflow/lite/micro/kernels/reduce.cc tensorflow/lite/micro/kernels/reshape.cc tensorflow/lite/micro/kernels/resize_bilinear.cc tensorflow/lite/micro/kernels/resize_nearest_neighbor.cc tensorflow/lite/micro/kernels/round.cc tensorflow/lite/micro/kernels/shape.cc tensorflow/lite/micro/kernels/softmax.cc tensorflow/lite/micro/kernels/softmax_common.cc tensorflow/lite/micro/kernels/space_to_batch_nd.cc tensorflow/lite/micro/kernels/space_to_depth.cc tensorflow/lite/micro/kernels/split.cc tensorflow/lite/micro/kernels/split_v.cc tensorflow/lite/micro/kernels/squeeze.cc tensorflow/lite/micro/kernels/strided_slice.cc tensorflow/lite/micro/kernels/sub.cc tensorflow/lite/micro/kernels/svdf.cc tensorflow/lite/micro/kernels/svdf_common.cc tensorflow/lite/micro/kernels/tanh.cc tensorflow/lite/micro/kernels/transpose.cc tensorflow/lite/micro/kernels/transpose_conv.cc tensorflow/lite/micro/kernels/unpack.cc tensorflow/lite/micro/kernels/zeros_like.cc
|
||||||
INCLUDE_DIRS . third_party/gemmlowp third_party/flatbuffers/include third_party/ruy)
|
INCLUDE_DIRS . third_party/gemmlowp third_party/flatbuffers/include third_party/ruy)
|
||||||
|
|
||||||
# Reduce the level of paranoia to be able to compile TF sources
|
# Reduce the level of paranoia to be able to compile TF sources
|
||||||
@@ -32,7 +32,7 @@ target_compile_options(${COMPONENT_LIB} PRIVATE
|
|||||||
-Wno-missing-field-initializers
|
-Wno-missing-field-initializers
|
||||||
-Wno-type-limits)
|
-Wno-type-limits)
|
||||||
|
|
||||||
target_compile_options(${COMPONENT_LIB} PRIVATE -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -O3 -Werror -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -DESP)
|
target_compile_options(${COMPONENT_LIB} PRIVATE -Wimplicit-function-declaration -Werror -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -DESP)
|
||||||
target_compile_options(${COMPONENT_LIB} PRIVATE $<$<COMPILE_LANGUAGE:CXX>: -std=c++11 -fno-rtti -fno-exceptions -fno-threadsafe-statics -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -O3 -Werror -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -DESP >)
|
target_compile_options(${COMPONENT_LIB} PRIVATE $<$<COMPILE_LANGUAGE:CXX>: -std=c++11 -fno-rtti -fno-exceptions -fno-threadsafe-statics -Werror -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -DESP >)
|
||||||
target_compile_options(${COMPONENT_LIB} INTERFACE $<$<IN_LIST:-DTF_LITE_STATIC_MEMORY,$<TARGET_PROPERTY:${COMPONENT_LIB},COMPILE_OPTIONS>>:-DTF_LITE_STATIC_MEMORY>)
|
target_compile_options(${COMPONENT_LIB} INTERFACE $<$<IN_LIST:-DTF_LITE_STATIC_MEMORY,$<TARGET_PROPERTY:${COMPONENT_LIB},COMPILE_OPTIONS>>:-DTF_LITE_STATIC_MEMORY>)
|
||||||
target_link_libraries(${COMPONENT_LIB} PRIVATE -lm)
|
target_link_libraries(${COMPONENT_LIB} PRIVATE -lm)
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ typedef struct {
|
|||||||
} TfLiteMirrorPaddingParams;
|
} TfLiteMirrorPaddingParams;
|
||||||
|
|
||||||
// Possible fused activation functions.
|
// Possible fused activation functions.
|
||||||
// TODO(aselle): rename to TfLiteActivation
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
kTfLiteActNone = 0,
|
kTfLiteActNone = 0,
|
||||||
kTfLiteActRelu,
|
kTfLiteActRelu,
|
||||||
@@ -98,6 +97,8 @@ typedef struct {
|
|||||||
TfLiteFusedActivation activation;
|
TfLiteFusedActivation activation;
|
||||||
} TfLiteConv3DParams;
|
} TfLiteConv3DParams;
|
||||||
|
|
||||||
|
typedef TfLiteConv3DParams TfLiteConv3DTransposeParams;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
TfLitePadding padding;
|
TfLitePadding padding;
|
||||||
int stride_width;
|
int stride_width;
|
||||||
@@ -328,8 +329,9 @@ typedef struct {
|
|||||||
} TfLitePadV2Params;
|
} TfLitePadV2Params;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
// TODO(ahentz): We can't have dynamic data in this struct, at least not yet.
|
// These fields are only used in old models for backward compatibility.
|
||||||
// For now we will fix the maximum possible number of dimensions.
|
// In the current implementation, we use the 2nd input of the op as the shape,
|
||||||
|
// and these fields are unused.
|
||||||
int shape[TFLITE_RESHAPE_PARAMS_MAX_DIMENSION_COUNT];
|
int shape[TFLITE_RESHAPE_PARAMS_MAX_DIMENSION_COUNT];
|
||||||
int num_dimensions;
|
int num_dimensions;
|
||||||
} TfLiteReshapeParams;
|
} TfLiteReshapeParams;
|
||||||
@@ -495,6 +497,11 @@ typedef struct {
|
|||||||
TfLiteType value_dtype;
|
TfLiteType value_dtype;
|
||||||
} TfLiteHashtableParams;
|
} TfLiteHashtableParams;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* container;
|
||||||
|
const char* shared_name;
|
||||||
|
} TfLiteVarHandleParams;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ extern "C" {
|
|||||||
// library.
|
// library.
|
||||||
#ifdef SWIG
|
#ifdef SWIG
|
||||||
#define TFL_CAPI_EXPORT
|
#define TFL_CAPI_EXPORT
|
||||||
#else
|
#elif defined(TFL_STATIC_LIBRARY_BUILD)
|
||||||
|
#define TFL_CAPI_EXPORT
|
||||||
|
#else // not definded TFL_STATIC_LIBRARY_BUILD
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#ifdef TFL_COMPILE_LIBRARY
|
#ifdef TFL_COMPILE_LIBRARY
|
||||||
#define TFL_CAPI_EXPORT __declspec(dllexport)
|
#define TFL_CAPI_EXPORT __declspec(dllexport)
|
||||||
@@ -54,7 +56,19 @@ typedef enum TfLiteStatus {
|
|||||||
// incompatibility between runtime and delegate, e.g., this error is returned
|
// incompatibility between runtime and delegate, e.g., this error is returned
|
||||||
// when trying to apply a TfLite delegate onto a model graph that's already
|
// when trying to apply a TfLite delegate onto a model graph that's already
|
||||||
// immutable.
|
// immutable.
|
||||||
kTfLiteApplicationError = 3
|
kTfLiteApplicationError = 3,
|
||||||
|
|
||||||
|
// Generally referring to serialized delegate data not being found.
|
||||||
|
// See tflite::delegates::Serialization.
|
||||||
|
kTfLiteDelegateDataNotFound = 4,
|
||||||
|
|
||||||
|
// Generally referring to data-writing issues in delegate serialization.
|
||||||
|
// See tflite::delegates::Serialization.
|
||||||
|
kTfLiteDelegateDataWriteError = 5,
|
||||||
|
|
||||||
|
// Generally referring to data-reading issues in delegate serialization.
|
||||||
|
// See tflite::delegates::Serialization.
|
||||||
|
kTfLiteDelegateDataReadError = 5,
|
||||||
} TfLiteStatus;
|
} TfLiteStatus;
|
||||||
|
|
||||||
// Types supported by tensor
|
// Types supported by tensor
|
||||||
|
|||||||
@@ -45,8 +45,10 @@ int TfLiteIntArrayEqualsArray(const TfLiteIntArray* a, int b_size,
|
|||||||
#ifndef TF_LITE_STATIC_MEMORY
|
#ifndef TF_LITE_STATIC_MEMORY
|
||||||
|
|
||||||
TfLiteIntArray* TfLiteIntArrayCreate(int size) {
|
TfLiteIntArray* TfLiteIntArrayCreate(int size) {
|
||||||
TfLiteIntArray* ret =
|
int alloc_size = TfLiteIntArrayGetSizeInBytes(size);
|
||||||
(TfLiteIntArray*)malloc(TfLiteIntArrayGetSizeInBytes(size));
|
if (alloc_size <= 0) return NULL;
|
||||||
|
TfLiteIntArray* ret = (TfLiteIntArray*)malloc(alloc_size);
|
||||||
|
if (!ret) return ret;
|
||||||
ret->size = size;
|
ret->size = size;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -181,9 +183,9 @@ void TfLiteTensorRealloc(size_t num_bytes, TfLiteTensor* tensor) {
|
|||||||
}
|
}
|
||||||
// TODO(b/145340303): Tensor data should be aligned.
|
// TODO(b/145340303): Tensor data should be aligned.
|
||||||
if (!tensor->data.raw) {
|
if (!tensor->data.raw) {
|
||||||
tensor->data.raw = malloc(num_bytes);
|
tensor->data.raw = (char*)malloc(num_bytes);
|
||||||
} else if (num_bytes > tensor->bytes) {
|
} else if (num_bytes > tensor->bytes) {
|
||||||
tensor->data.raw = realloc(tensor->data.raw, num_bytes);
|
tensor->data.raw = (char*)realloc(tensor->data.raw, num_bytes);
|
||||||
}
|
}
|
||||||
tensor->bytes = num_bytes;
|
tensor->bytes = num_bytes;
|
||||||
}
|
}
|
||||||
@@ -229,7 +231,7 @@ const char* TfLiteTypeGetName(TfLiteType type) {
|
|||||||
return "Unknown type";
|
return "Unknown type";
|
||||||
}
|
}
|
||||||
|
|
||||||
TfLiteDelegate TfLiteDelegateCreate() {
|
TfLiteDelegate TfLiteDelegateCreate(void) {
|
||||||
TfLiteDelegate d = {
|
TfLiteDelegate d = {
|
||||||
.data_ = NULL,
|
.data_ = NULL,
|
||||||
.Prepare = NULL,
|
.Prepare = NULL,
|
||||||
|
|||||||
@@ -456,8 +456,8 @@ typedef struct TfLiteTensor {
|
|||||||
} TfLiteTensor;
|
} TfLiteTensor;
|
||||||
|
|
||||||
// A structure representing an instance of a node.
|
// A structure representing an instance of a node.
|
||||||
// This structure only exhibits the inputs, outputs and user defined data, not
|
// This structure only exhibits the inputs, outputs, user defined data and some
|
||||||
// other features like the type.
|
// node properties (like statefulness), not other features like the type.
|
||||||
typedef struct TfLiteNode {
|
typedef struct TfLiteNode {
|
||||||
// Inputs to this node expressed as indices into the simulator's tensors.
|
// Inputs to this node expressed as indices into the simulator's tensors.
|
||||||
TfLiteIntArray* inputs;
|
TfLiteIntArray* inputs;
|
||||||
@@ -490,6 +490,9 @@ typedef struct TfLiteNode {
|
|||||||
// created by calling `interpreter.ModifyGraphWithDelegate`.
|
// created by calling `interpreter.ModifyGraphWithDelegate`.
|
||||||
// WARNING: This is an experimental interface that is subject to change.
|
// WARNING: This is an experimental interface that is subject to change.
|
||||||
struct TfLiteDelegate* delegate;
|
struct TfLiteDelegate* delegate;
|
||||||
|
|
||||||
|
// Whether this op might have side effect (e.g. stateful op).
|
||||||
|
bool might_have_side_effect;
|
||||||
} TfLiteNode;
|
} TfLiteNode;
|
||||||
#else // defined(TF_LITE_STATIC_MEMORY)?
|
#else // defined(TF_LITE_STATIC_MEMORY)?
|
||||||
// NOTE: This flag is opt-in only at compile time.
|
// NOTE: This flag is opt-in only at compile time.
|
||||||
@@ -640,6 +643,7 @@ typedef struct TfLiteContext {
|
|||||||
// TfLiteDelegates can traverse the current execution plan by iterating
|
// TfLiteDelegates can traverse the current execution plan by iterating
|
||||||
// through each member of this array and using GetNodeAndRegistration() to
|
// through each member of this array and using GetNodeAndRegistration() to
|
||||||
// access details about a node. i.e.
|
// access details about a node. i.e.
|
||||||
|
//
|
||||||
// TfLiteIntArray* execution_plan;
|
// TfLiteIntArray* execution_plan;
|
||||||
// TF_LITE_ENSURE_STATUS(context->GetExecutionPlan(context, &execution_plan));
|
// TF_LITE_ENSURE_STATUS(context->GetExecutionPlan(context, &execution_plan));
|
||||||
// for (int exec_index = 0; exec_index < execution_plan->size; exec_index++) {
|
// for (int exec_index = 0; exec_index < execution_plan->size; exec_index++) {
|
||||||
@@ -648,6 +652,28 @@ typedef struct TfLiteContext {
|
|||||||
// TfLiteRegistration* reg;
|
// TfLiteRegistration* reg;
|
||||||
// context->GetNodeAndRegistration(context, node_index, &node, ®);
|
// context->GetNodeAndRegistration(context, node_index, &node, ®);
|
||||||
// }
|
// }
|
||||||
|
// Note: the memory pointed by '`*execution_plan` is OWNED by TfLite runtime.
|
||||||
|
// Future calls to GetExecutionPlan invalidates earlier outputs. The following
|
||||||
|
// code snippet shows the issue of such an invocation pattern. After calling
|
||||||
|
// CheckNode, subsequent access to `plan_1st` is undefined.
|
||||||
|
//
|
||||||
|
// void CheckNode(const TfLiteNode* node) {
|
||||||
|
// ...
|
||||||
|
// TfLiteIntArray* plan_2nd;
|
||||||
|
// TF_LITE_ENSURE_STATUS(context->GetExecutionPlan(context, &plan_2nd));
|
||||||
|
// ...
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// TfLiteIntArray* plan_1st;
|
||||||
|
// TF_LITE_ENSURE_STATUS(context->GetExecutionPlan(context, &plan_1st));
|
||||||
|
// for (int exec_index = 0; exec_index < plan_1st->size; exec_index++) {
|
||||||
|
// int node_index = plan_1st->data[exec_index];
|
||||||
|
// TfLiteNode* node;
|
||||||
|
// TfLiteRegistration* reg;
|
||||||
|
// context->GetNodeAndRegistration(context, node_index, &node, ®);
|
||||||
|
// CheckNode(node);
|
||||||
|
// }
|
||||||
|
//
|
||||||
// WARNING: This is an experimental interface that is subject to change.
|
// WARNING: This is an experimental interface that is subject to change.
|
||||||
TfLiteStatus (*GetExecutionPlan)(struct TfLiteContext* context,
|
TfLiteStatus (*GetExecutionPlan)(struct TfLiteContext* context,
|
||||||
TfLiteIntArray** execution_plan);
|
TfLiteIntArray** execution_plan);
|
||||||
@@ -777,6 +803,18 @@ typedef struct TfLiteContext {
|
|||||||
// WARNING: This method may not be available on all platforms.
|
// WARNING: This method may not be available on all platforms.
|
||||||
TfLiteEvalTensor* (*GetEvalTensor)(const struct TfLiteContext* context,
|
TfLiteEvalTensor* (*GetEvalTensor)(const struct TfLiteContext* context,
|
||||||
int tensor_idx);
|
int tensor_idx);
|
||||||
|
|
||||||
|
// Retrieves named metadata buffer from the TFLite model.
|
||||||
|
// Returns kTfLiteOk if metadata is successfully obtained from the flatbuffer
|
||||||
|
// Model: that is, there exists a `metadata` entry with given `name` string.
|
||||||
|
// (see TFLite's schema.fbs).
|
||||||
|
// The corresponding `buffer` information is populated in `ptr` & `bytes`.
|
||||||
|
// The data from `ptr` is valid for the lifetime of the Interpreter.
|
||||||
|
//
|
||||||
|
// WARNING: This is an experimental interface that is subject to change.
|
||||||
|
TfLiteStatus (*GetModelMetadata)(const struct TfLiteContext* context,
|
||||||
|
const char* name, const char** ptr,
|
||||||
|
size_t* bytes);
|
||||||
} TfLiteContext;
|
} TfLiteContext;
|
||||||
|
|
||||||
typedef struct TfLiteRegistration {
|
typedef struct TfLiteRegistration {
|
||||||
@@ -918,7 +956,7 @@ typedef struct TfLiteDelegate {
|
|||||||
|
|
||||||
// Build a 'null' delegate, with all the fields properly set to their default
|
// Build a 'null' delegate, with all the fields properly set to their default
|
||||||
// values.
|
// values.
|
||||||
TfLiteDelegate TfLiteDelegateCreate();
|
TfLiteDelegate TfLiteDelegateCreate(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|||||||
@@ -373,6 +373,10 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
|||||||
return ParseReducer(op, error_reporter, allocator, builtin_data);
|
return ParseReducer(op, error_reporter, allocator, builtin_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case BuiltinOperator_REDUCE_ALL: {
|
||||||
|
return ParseReducer(op, error_reporter, allocator, builtin_data);
|
||||||
|
}
|
||||||
|
|
||||||
case BuiltinOperator_REDUCE_MAX: {
|
case BuiltinOperator_REDUCE_MAX: {
|
||||||
return ParseReducer(op, error_reporter, allocator, builtin_data);
|
return ParseReducer(op, error_reporter, allocator, builtin_data);
|
||||||
}
|
}
|
||||||
@@ -663,7 +667,6 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
|||||||
return kTfLiteOk;
|
return kTfLiteOk;
|
||||||
}
|
}
|
||||||
case BuiltinOperator_DELEGATE: {
|
case BuiltinOperator_DELEGATE: {
|
||||||
// TODO(ycling): Revisit when supporting saving delegated models.
|
|
||||||
TF_LITE_REPORT_ERROR(error_reporter,
|
TF_LITE_REPORT_ERROR(error_reporter,
|
||||||
"DELEGATE op shouldn't exist in model.");
|
"DELEGATE op shouldn't exist in model.");
|
||||||
return kTfLiteError;
|
return kTfLiteError;
|
||||||
@@ -757,7 +760,8 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
|||||||
*builtin_data = params.release();
|
*builtin_data = params.release();
|
||||||
return kTfLiteOk;
|
return kTfLiteOk;
|
||||||
}
|
}
|
||||||
case BuiltinOperator_CONV_3D: {
|
case BuiltinOperator_CONV_3D:
|
||||||
|
case BuiltinOperator_CONV_3D_TRANSPOSE: {
|
||||||
auto params = safe_allocator.Allocate<TfLiteConv3DParams>();
|
auto params = safe_allocator.Allocate<TfLiteConv3DParams>();
|
||||||
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
||||||
if (const auto* conv3d_params = op->builtin_options_as_Conv3DOptions()) {
|
if (const auto* conv3d_params = op->builtin_options_as_Conv3DOptions()) {
|
||||||
@@ -789,6 +793,21 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
|||||||
*builtin_data = params.release();
|
*builtin_data = params.release();
|
||||||
return kTfLiteOk;
|
return kTfLiteOk;
|
||||||
}
|
}
|
||||||
|
case BuiltinOperator_VAR_HANDLE: {
|
||||||
|
auto params = safe_allocator.Allocate<TfLiteVarHandleParams>();
|
||||||
|
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
||||||
|
params->container = nullptr;
|
||||||
|
params->shared_name = nullptr;
|
||||||
|
if (const auto* var_handle_params =
|
||||||
|
op->builtin_options_as_VarHandleOptions()) {
|
||||||
|
if (var_handle_params->container())
|
||||||
|
params->container = var_handle_params->container()->c_str();
|
||||||
|
if (var_handle_params->shared_name())
|
||||||
|
params->shared_name = var_handle_params->shared_name()->c_str();
|
||||||
|
}
|
||||||
|
*builtin_data = params.release();
|
||||||
|
return kTfLiteOk;
|
||||||
|
}
|
||||||
// Below are the ops with no builtin_data structure.
|
// Below are the ops with no builtin_data structure.
|
||||||
// TODO(aselle): Implement call in BuiltinOptions, but nullptrs are
|
// TODO(aselle): Implement call in BuiltinOptions, but nullptrs are
|
||||||
// ok for now, since there is no call implementation either.
|
// ok for now, since there is no call implementation either.
|
||||||
@@ -825,6 +844,9 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
|||||||
case BuiltinOperator_HASHTABLE_FIND:
|
case BuiltinOperator_HASHTABLE_FIND:
|
||||||
case BuiltinOperator_HASHTABLE_IMPORT:
|
case BuiltinOperator_HASHTABLE_IMPORT:
|
||||||
case BuiltinOperator_HASHTABLE_SIZE:
|
case BuiltinOperator_HASHTABLE_SIZE:
|
||||||
|
case BuiltinOperator_READ_VARIABLE:
|
||||||
|
case BuiltinOperator_ASSIGN_VARIABLE:
|
||||||
|
case BuiltinOperator_BROADCAST_ARGS:
|
||||||
return kTfLiteOk;
|
return kTfLiteOk;
|
||||||
case BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES:
|
case BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES:
|
||||||
return kTfLiteError;
|
return kTfLiteError;
|
||||||
@@ -1372,6 +1394,30 @@ TfLiteStatus ParseHardSwish(const Operator*, ErrorReporter*,
|
|||||||
return kTfLiteOk;
|
return kTfLiteOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TfLiteStatus ParseIf(const Operator* op, ErrorReporter* error_reporter,
|
||||||
|
BuiltinDataAllocator* allocator, void** builtin_data) {
|
||||||
|
CheckParsePointerParams(op, error_reporter, allocator, builtin_data);
|
||||||
|
|
||||||
|
SafeBuiltinDataAllocator safe_allocator(allocator);
|
||||||
|
std::unique_ptr<TfLiteIfParams, SafeBuiltinDataAllocator::BuiltinDataDeleter>
|
||||||
|
params = safe_allocator.Allocate<TfLiteIfParams>();
|
||||||
|
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
||||||
|
|
||||||
|
const IfOptions* schema_params = op->builtin_options_as_IfOptions();
|
||||||
|
|
||||||
|
if (schema_params != nullptr) {
|
||||||
|
params->then_subgraph_index = schema_params->then_subgraph_index();
|
||||||
|
params->else_subgraph_index = schema_params->else_subgraph_index();
|
||||||
|
} else {
|
||||||
|
// TODO(b/157480169): We should either return kTfLiteError or fill in some
|
||||||
|
// reasonable defaults in the params struct. We are not doing so until we
|
||||||
|
// better undertand the ramifications of changing the legacy behavior.
|
||||||
|
}
|
||||||
|
|
||||||
|
*builtin_data = params.release();
|
||||||
|
return kTfLiteOk;
|
||||||
|
}
|
||||||
|
|
||||||
TfLiteStatus ParseL2Normalization(const Operator* op,
|
TfLiteStatus ParseL2Normalization(const Operator* op,
|
||||||
ErrorReporter* error_reporter,
|
ErrorReporter* error_reporter,
|
||||||
BuiltinDataAllocator* allocator,
|
BuiltinDataAllocator* allocator,
|
||||||
|
|||||||
@@ -181,6 +181,9 @@ TfLiteStatus ParseHardSwish(const Operator* op, ErrorReporter* error_reporter,
|
|||||||
BuiltinDataAllocator* allocator,
|
BuiltinDataAllocator* allocator,
|
||||||
void** builtin_data);
|
void** builtin_data);
|
||||||
|
|
||||||
|
TfLiteStatus ParseIf(const Operator* op, ErrorReporter* error_reporter,
|
||||||
|
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||||
|
|
||||||
TfLiteStatus ParseL2Normalization(const Operator* op,
|
TfLiteStatus ParseL2Normalization(const Operator* op,
|
||||||
ErrorReporter* error_reporter,
|
ErrorReporter* error_reporter,
|
||||||
BuiltinDataAllocator* allocator,
|
BuiltinDataAllocator* allocator,
|
||||||
|
|||||||
@@ -30,8 +30,7 @@ TfLiteStatus GetRegistrationFromOpCode(
|
|||||||
auto builtin_code = GetBuiltinCode(opcode);
|
auto builtin_code = GetBuiltinCode(opcode);
|
||||||
int version = opcode->version();
|
int version = opcode->version();
|
||||||
|
|
||||||
if (builtin_code > BuiltinOperator_MAX ||
|
if (builtin_code > BuiltinOperator_MAX) {
|
||||||
builtin_code < BuiltinOperator_MIN) {
|
|
||||||
TF_LITE_REPORT_ERROR(
|
TF_LITE_REPORT_ERROR(
|
||||||
error_reporter,
|
error_reporter,
|
||||||
"Op builtin_code out of range: %d. Are you using old TFLite binary "
|
"Op builtin_code out of range: %d. Are you using old TFLite binary "
|
||||||
|
|||||||
@@ -46,6 +46,22 @@ class OpResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual ~OpResolver() {}
|
virtual ~OpResolver() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Returns true if this OpResolver may contain any "user defined" ops.
|
||||||
|
/// By "user defined" ops, we mean any op definitions other than those
|
||||||
|
/// contained in tflite::ops::builtin::BuiltinOpResolver.
|
||||||
|
///
|
||||||
|
/// If this method returns true, it doesn't necessarily mean that the
|
||||||
|
/// OpResolver contains a user-defined op, just that the absence of
|
||||||
|
/// user-defined ops can't be guaranteed.
|
||||||
|
///
|
||||||
|
/// Note that "user-defined" ops are not the same as "custom" ops;
|
||||||
|
/// BuiltinOpResolver may support certain "custom" ops, in addition to
|
||||||
|
/// "builtin" ops, and may not support all of the "builtin" op enum values.
|
||||||
|
virtual bool MayContainUserDefinedOps() const { return true; }
|
||||||
|
|
||||||
|
friend class OpResolverInternal;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handles the logic for converting between an OperatorCode structure extracted
|
// Handles the logic for converting between an OperatorCode structure extracted
|
||||||
|
|||||||
@@ -279,81 +279,125 @@ inline Integer FloorLog2(Integer n) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate INT16 LUT for function(), e.g., table exp(x) and 1/(1+x) used in
|
// The size of the LUT depends on the type of input. For int8 inputs a simple
|
||||||
// softmax
|
// 256 entries LUT is used. For int16 inputs the high 9 bits are used for
|
||||||
// func - the function to build the LUT for (e.g exp(x))
|
// indexing and the 7 remaining bits are used for interpolation. We thus use a
|
||||||
// min,max - table limits
|
// 513-entries LUT for int16 cases, 512 for the 9-bit indexing and 1 extra entry
|
||||||
// table - pointer to buffer
|
// to interpolate the last value.
|
||||||
// num - number of elements in the LUT
|
template <typename LutInT>
|
||||||
inline void gen_lut(double (*func)(double), double min, double max,
|
constexpr int lut_size() {
|
||||||
int16_t* table, const int num) {
|
static_assert(std::is_same<LutInT, int8_t>::value ||
|
||||||
// size of table should equal to num + 1
|
std::is_same<LutInT, int16_t>::value,
|
||||||
// last element only for slope calculation
|
"Only LUTs with int8 or int16 inputs are supported.");
|
||||||
double step = (max - min) / (num - 1);
|
return std::is_same<LutInT, int8_t>::value ? 256 : 513;
|
||||||
double half_step = step / 2.0;
|
|
||||||
for (int i = 0; i < num - 1; i++) {
|
|
||||||
double sample_val = TfLiteRound(func(min + i * step) * 32768.0);
|
|
||||||
double midpoint_interp_val =
|
|
||||||
TfLiteRound((func(min + (i + 1) * step) * 32768.0 +
|
|
||||||
TfLiteRound(func(min + i * step) * 32768.0)) /
|
|
||||||
2.0);
|
|
||||||
double midpoint_val =
|
|
||||||
TfLiteRound(func(min + i * step + half_step) * 32768.0);
|
|
||||||
double midpoint_err = midpoint_interp_val - midpoint_val;
|
|
||||||
double bias = TfLiteRound(midpoint_err / 2.0);
|
|
||||||
table[i] = std::min<double>(std::max<double>(sample_val - bias, -32768.0),
|
|
||||||
32767.0);
|
|
||||||
}
|
|
||||||
table[num - 1] = std::min<double>(
|
|
||||||
std::max<double>(TfLiteRound(func(max) * 32768.0), -32768.0), 32767.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate INT16 LUT for function(), e.g., table exp(x) and 1/(1+x) used in
|
// Generate a LUT for 'func' which can be used to approximate functions like
|
||||||
// softmax
|
// exp, log, ...
|
||||||
// func - the function to build the LUT for (e.g exp(x))
|
//
|
||||||
// min,max - table limits
|
// - func: the function to build the LUT for (e.g exp(x))
|
||||||
// table - pointer to buffer
|
// - input_min, input_max: range of the func inputs
|
||||||
// num - number of elements in the LUT
|
// - output_min, output_max: range of the func outputs
|
||||||
inline void gen_lut(float (*func)(float), float min, float max, int16_t* table,
|
// - lut: pointer to the LUT table to fill, the table must be of size
|
||||||
const int num) {
|
// lut_size<LutInT>()
|
||||||
// size of table should equal to num + 1
|
template <typename FloatT, typename LutInT, typename LutOutT>
|
||||||
// last element only for slope calculation
|
inline void gen_lut(FloatT (*func)(FloatT), FloatT input_min, FloatT input_max,
|
||||||
float step = (max - min) / (num - 1);
|
FloatT output_min, FloatT output_max, LutOutT* lut) {
|
||||||
float half_step = step / 2.0f;
|
static_assert(std::is_same<LutInT, int8_t>::value ||
|
||||||
for (int i = 0; i < num - 1; i++) {
|
std::is_same<LutInT, int16_t>::value,
|
||||||
float sample_val = TfLiteRound(func(min + i * step) * 32768.0f);
|
"Only LUTs with int8 or int16 inputs are supported.");
|
||||||
float midpoint_interp_val =
|
static_assert(std::is_same<LutOutT, int8_t>::value ||
|
||||||
TfLiteRound((func(min + (i + 1) * step) * 32768.0f +
|
std::is_same<LutOutT, int16_t>::value,
|
||||||
TfLiteRound(func(min + i * step) * 32768.0f)) /
|
"Only LUTs with int8 or int16 outputs are supported.");
|
||||||
2.0f);
|
static_assert(std::is_floating_point<FloatT>::value,
|
||||||
float midpoint_val =
|
"FloatT must be a floating-point type.");
|
||||||
TfLiteRound(func(min + i * step + half_step) * 32768.0f);
|
|
||||||
float midpoint_err = midpoint_interp_val - midpoint_val;
|
const int nb_steps = std::is_same<LutInT, int8_t>::value ? 256 : 512;
|
||||||
float bias = TfLiteRound(midpoint_err / 2.0f);
|
const FloatT step = (input_max - input_min) / nb_steps;
|
||||||
table[i] = std::min<float>(std::max<float>(sample_val - bias, -32768.0f),
|
const FloatT half_step = step / 2;
|
||||||
32767.0f);
|
const FloatT output_scaling_inv =
|
||||||
|
static_cast<FloatT>(std::numeric_limits<LutOutT>::max() -
|
||||||
|
std::numeric_limits<LutOutT>::min() + 1) /
|
||||||
|
(output_max - output_min);
|
||||||
|
const FloatT table_min =
|
||||||
|
static_cast<FloatT>(std::numeric_limits<LutOutT>::min());
|
||||||
|
const FloatT table_max =
|
||||||
|
static_cast<FloatT>(std::numeric_limits<LutOutT>::max());
|
||||||
|
|
||||||
|
for (int i = 0; i < nb_steps; i++) {
|
||||||
|
const FloatT val = func(input_min + i * step);
|
||||||
|
const FloatT val_midpoint = func(input_min + i * step + half_step);
|
||||||
|
const FloatT val_next = func(input_min + (i + 1) * step);
|
||||||
|
|
||||||
|
const FloatT sample_val = TfLiteRound(val * output_scaling_inv);
|
||||||
|
const FloatT midpoint_interp_val =
|
||||||
|
TfLiteRound((val_next * output_scaling_inv +
|
||||||
|
TfLiteRound(val * output_scaling_inv)) /
|
||||||
|
2);
|
||||||
|
const FloatT midpoint_val = TfLiteRound(val_midpoint * output_scaling_inv);
|
||||||
|
const FloatT midpoint_err = midpoint_interp_val - midpoint_val;
|
||||||
|
const FloatT bias = TfLiteRound(midpoint_err / 2);
|
||||||
|
|
||||||
|
lut[i] = static_cast<LutOutT>(std::min<FloatT>(
|
||||||
|
std::max<FloatT>(sample_val - bias, table_min), table_max));
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool with_extra_interpolation_value =
|
||||||
|
std::is_same<LutInT, int16_t>::value;
|
||||||
|
if (with_extra_interpolation_value) {
|
||||||
|
lut[nb_steps] = static_cast<LutOutT>(std::min<FloatT>(
|
||||||
|
std::max<FloatT>(TfLiteRound(func(input_max) * output_scaling_inv),
|
||||||
|
table_min),
|
||||||
|
table_max));
|
||||||
}
|
}
|
||||||
table[num - 1] = std::min<float>(
|
|
||||||
std::max<float>(TfLiteRound(func(max) * 32768.0f), -32768.0f), 32767.0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// int16_t func table lookup, e.g., lookup exp() and 1/(1+x) used in softmax
|
// LUT must have 513 values
|
||||||
inline int16_t generic_int16_table_lookup(int16_t value, const int16_t* lut) {
|
template <typename LutOutT>
|
||||||
// 512 base value, lut[513] only for calculate slope
|
inline LutOutT lut_lookup_with_interpolation(int16_t value,
|
||||||
uint16_t index = static_cast<uint16_t>(256 + (value >> 7));
|
const LutOutT* lut) {
|
||||||
|
static_assert(std::is_same<LutOutT, int8_t>::value ||
|
||||||
|
std::is_same<LutOutT, int16_t>::value,
|
||||||
|
"Only LUTs with int8 or int16 outputs are supported.");
|
||||||
|
// 512 base values, lut[513] is only used to calculate the slope
|
||||||
|
const uint16_t index = static_cast<uint16_t>(256 + (value >> 7));
|
||||||
assert(index < 512 && "LUT index out of range.");
|
assert(index < 512 && "LUT index out of range.");
|
||||||
int16_t offset = value & 0x7f;
|
const int16_t offset = value & 0x7f;
|
||||||
|
|
||||||
// base and slope are Q0.15
|
// Base and slope are Q0.x
|
||||||
int16_t base = lut[index];
|
const LutOutT base = lut[index];
|
||||||
int16_t slope = lut[index + 1] - lut[index];
|
const LutOutT slope = lut[index + 1] - lut[index];
|
||||||
|
|
||||||
// Q0.15 * Q0.7 = Q0.22
|
// Q0.x * Q0.7 = Q0.(x + 7)
|
||||||
// Round and convert from Q0.22 to Q0.15
|
// Round and convert from Q0.(x + 7) to Q0.x
|
||||||
int32_t delta = (static_cast<int32_t>(slope) * offset + 64) >> 7;
|
const int delta = (slope * offset + 64) >> 7;
|
||||||
|
|
||||||
// Q0.15 + Q0.15
|
// Q0.15 + Q0.15
|
||||||
return base + delta;
|
return static_cast<LutOutT>(base + delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
// int16_t -> int16_t table lookup with interpolation
|
||||||
|
// LUT must have 513 values
|
||||||
|
inline int16_t lut_lookup(int16_t value, const int16_t* lut) {
|
||||||
|
return lut_lookup_with_interpolation(value, lut);
|
||||||
|
}
|
||||||
|
|
||||||
|
// int16_t -> int8_t table lookup with interpolation
|
||||||
|
// LUT must have 513 values
|
||||||
|
inline int8_t lut_lookup(int16_t value, const int8_t* lut) {
|
||||||
|
return lut_lookup_with_interpolation(value, lut);
|
||||||
|
}
|
||||||
|
|
||||||
|
// int8_t -> int8_t table lookup without interpolation
|
||||||
|
// LUT must have 256 values
|
||||||
|
inline int8_t lut_lookup(int8_t value, const int8_t* lut) {
|
||||||
|
return lut[128 + value];
|
||||||
|
}
|
||||||
|
|
||||||
|
// int8_t -> int16_t table lookup without interpolation
|
||||||
|
// LUT must have 256 values
|
||||||
|
inline int16_t lut_lookup(int8_t value, const int16_t* lut) {
|
||||||
|
return lut[128 + value];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Table of sigmoid(i/24) at 0.16 format - 256 elements.
|
// Table of sigmoid(i/24) at 0.16 format - 256 elements.
|
||||||
@@ -575,7 +619,8 @@ log_x_for_x_greater_than_or_equal_to_1_impl(
|
|||||||
// InputIntegerBits - z_b_headroom - 0.25);
|
// InputIntegerBits - z_b_headroom - 0.25);
|
||||||
const FixedPointAccum z_a_pow_2_adj = SaturatingAddNonGemmlowp(
|
const FixedPointAccum z_a_pow_2_adj = SaturatingAddNonGemmlowp(
|
||||||
FixedPointAccum::FromRaw(SaturatingRoundingMultiplyByPOTParam(
|
FixedPointAccum::FromRaw(SaturatingRoundingMultiplyByPOTParam(
|
||||||
InputIntegerBits - z_a_headroom_plus_1, 31 - kAccumIntegerBits)),
|
static_cast<int32_t>(InputIntegerBits - z_a_headroom_plus_1),
|
||||||
|
31 - kAccumIntegerBits)),
|
||||||
shifted_quarter);
|
shifted_quarter);
|
||||||
|
|
||||||
// z_b is treated like z_a, but premultiplying by sqrt(0.5).
|
// z_b is treated like z_a, but premultiplying by sqrt(0.5).
|
||||||
@@ -585,7 +630,8 @@ log_x_for_x_greater_than_or_equal_to_1_impl(
|
|||||||
SaturatingRoundingMultiplyByPOTParam(z_a.raw(), z_b_headroom);
|
SaturatingRoundingMultiplyByPOTParam(z_a.raw(), z_b_headroom);
|
||||||
const FixedPointAccum z_b_pow_2_adj = SaturatingSub(
|
const FixedPointAccum z_b_pow_2_adj = SaturatingSub(
|
||||||
FixedPointAccum::FromRaw(SaturatingRoundingMultiplyByPOTParam(
|
FixedPointAccum::FromRaw(SaturatingRoundingMultiplyByPOTParam(
|
||||||
InputIntegerBits - z_b_headroom, 31 - kAccumIntegerBits)),
|
static_cast<int32_t>(InputIntegerBits - z_b_headroom),
|
||||||
|
31 - kAccumIntegerBits)),
|
||||||
shifted_quarter);
|
shifted_quarter);
|
||||||
|
|
||||||
const FixedPoint0 r = FixedPoint0::FromRaw(std::min(r_a_raw, r_b_raw));
|
const FixedPoint0 r = FixedPoint0::FromRaw(std::min(r_a_raw, r_b_raw));
|
||||||
|
|||||||
@@ -19,9 +19,8 @@ limitations under the License.
|
|||||||
|
|
||||||
namespace tflite {
|
namespace tflite {
|
||||||
|
|
||||||
#if defined(TF_LITE_USE_GLOBAL_CMATH_FUNCTIONS) || \
|
#if defined(TF_LITE_USE_GLOBAL_CMATH_FUNCTIONS) || \
|
||||||
(defined(__ANDROID__) && !defined(__NDK_MAJOR__)) || defined(ARDUINO) || \
|
(defined(__ANDROID__) && !defined(__NDK_MAJOR__)) || defined(__ZEPHYR__)
|
||||||
defined(__ZEPHYR__)
|
|
||||||
#define TF_LITE_GLOBAL_STD_PREFIX
|
#define TF_LITE_GLOBAL_STD_PREFIX
|
||||||
#else
|
#else
|
||||||
#define TF_LITE_GLOBAL_STD_PREFIX std
|
#define TF_LITE_GLOBAL_STD_PREFIX std
|
||||||
|
|||||||
@@ -15,26 +15,6 @@ limitations under the License.
|
|||||||
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_OPTIMIZED_NEON_CHECK_H_
|
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_OPTIMIZED_NEON_CHECK_H_
|
||||||
#define TENSORFLOW_LITE_KERNELS_INTERNAL_OPTIMIZED_NEON_CHECK_H_
|
#define TENSORFLOW_LITE_KERNELS_INTERNAL_OPTIMIZED_NEON_CHECK_H_
|
||||||
|
|
||||||
#if defined(__ARM_NEON__) || defined(__ARM_NEON)
|
// TFLM does not need to utilize any Neon optimizations.
|
||||||
#define USE_NEON
|
|
||||||
#include <arm_neon.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined __GNUC__ && defined __SSE4_1__ && !defined TF_LITE_DISABLE_X86_NEON
|
|
||||||
#define USE_NEON
|
|
||||||
#include "NEON_2_SSE.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// NEON_OR_PORTABLE(SomeFunc, args) calls NeonSomeFunc(args) if USE_NEON is
|
|
||||||
// defined, PortableSomeFunc(args) otherwise.
|
|
||||||
#ifdef USE_NEON
|
|
||||||
// Always use Neon code
|
|
||||||
#define NEON_OR_PORTABLE(funcname, ...) Neon##funcname(__VA_ARGS__)
|
|
||||||
|
|
||||||
#else
|
|
||||||
// No NEON available: Use Portable code
|
|
||||||
#define NEON_OR_PORTABLE(funcname, ...) Portable##funcname(__VA_ARGS__)
|
|
||||||
|
|
||||||
#endif // defined(USE_NEON)
|
|
||||||
|
|
||||||
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_OPTIMIZED_NEON_CHECK_H_
|
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_OPTIMIZED_NEON_CHECK_H_
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ limitations under the License.
|
|||||||
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ADD_H_
|
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ADD_H_
|
||||||
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ADD_H_
|
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ADD_H_
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#include "fixedpoint/fixedpoint.h"
|
#include "fixedpoint/fixedpoint.h"
|
||||||
#include "tensorflow/lite/kernels/internal/common.h"
|
#include "tensorflow/lite/kernels/internal/common.h"
|
||||||
|
|
||||||
@@ -27,25 +29,14 @@ inline void Add(const ArithmeticParams& params,
|
|||||||
const RuntimeShape& input1_shape, const T* input1_data,
|
const RuntimeShape& input1_shape, const T* input1_data,
|
||||||
const RuntimeShape& input2_shape, const T* input2_data,
|
const RuntimeShape& input2_shape, const T* input2_data,
|
||||||
const RuntimeShape& output_shape, T* output_data) {
|
const RuntimeShape& output_shape, T* output_data) {
|
||||||
|
T activation_min, activation_max;
|
||||||
|
GetActivationParams(params, &activation_min, &activation_max);
|
||||||
|
|
||||||
const int flat_size =
|
const int flat_size =
|
||||||
MatchingElementsSize(input1_shape, input2_shape, output_shape);
|
MatchingElementsSize(input1_shape, input2_shape, output_shape);
|
||||||
for (int i = 0; i < flat_size; ++i) {
|
for (int i = 0; i < flat_size; ++i) {
|
||||||
output_data[i] = ActivationFunctionWithMinMax(
|
output_data[i] = ActivationFunctionWithMinMax(
|
||||||
input1_data[i] + input2_data[i], params.quantized_activation_min,
|
input1_data[i] + input2_data[i], activation_min, activation_max);
|
||||||
params.quantized_activation_max);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Add(const ArithmeticParams& params,
|
|
||||||
const RuntimeShape& input1_shape, const float* input1_data,
|
|
||||||
const RuntimeShape& input2_shape, const float* input2_data,
|
|
||||||
const RuntimeShape& output_shape, float* output_data) {
|
|
||||||
const int flat_size =
|
|
||||||
MatchingElementsSize(input1_shape, input2_shape, output_shape);
|
|
||||||
for (int i = 0; i < flat_size; i++) {
|
|
||||||
auto x = input1_data[i] + input2_data[i];
|
|
||||||
output_data[i] = ActivationFunctionWithMinMax(
|
|
||||||
x, params.float_activation_min, params.float_activation_max);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,13 +193,12 @@ inline void Add(const ArithmeticParams& params,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void BroadcastAdd4DSlow(const ArithmeticParams& params,
|
template <typename T>
|
||||||
const RuntimeShape& input1_shape,
|
inline typename std::enable_if<!is_small_integer<T>::value, void>::type
|
||||||
const float* input1_data,
|
BroadcastAdd4DSlow(const ArithmeticParams& params,
|
||||||
const RuntimeShape& input2_shape,
|
const RuntimeShape& input1_shape, const T* input1_data,
|
||||||
const float* input2_data,
|
const RuntimeShape& input2_shape, const T* input2_data,
|
||||||
const RuntimeShape& output_shape,
|
const RuntimeShape& output_shape, T* output_data) {
|
||||||
float* output_data) {
|
|
||||||
NdArrayDesc<4> desc1;
|
NdArrayDesc<4> desc1;
|
||||||
NdArrayDesc<4> desc2;
|
NdArrayDesc<4> desc2;
|
||||||
NdArrayDescsForElementwiseBroadcast(input1_shape, input2_shape, &desc1,
|
NdArrayDescsForElementwiseBroadcast(input1_shape, input2_shape, &desc1,
|
||||||
@@ -216,6 +206,9 @@ inline void BroadcastAdd4DSlow(const ArithmeticParams& params,
|
|||||||
const RuntimeShape extended_output_shape =
|
const RuntimeShape extended_output_shape =
|
||||||
RuntimeShape::ExtendedShape(4, output_shape);
|
RuntimeShape::ExtendedShape(4, output_shape);
|
||||||
|
|
||||||
|
T activation_min, activation_max;
|
||||||
|
GetActivationParams(params, &activation_min, &activation_max);
|
||||||
|
|
||||||
// In Tensorflow, the dimensions are canonically named (batch_number, row,
|
// In Tensorflow, the dimensions are canonically named (batch_number, row,
|
||||||
// col, channel), with extents (batches, height, width, depth), with the
|
// col, channel), with extents (batches, height, width, depth), with the
|
||||||
// trailing dimension changing most rapidly (channels has the smallest stride,
|
// trailing dimension changing most rapidly (channels has the smallest stride,
|
||||||
@@ -232,51 +225,10 @@ inline void BroadcastAdd4DSlow(const ArithmeticParams& params,
|
|||||||
for (int x = 0; x < extended_output_shape.Dims(2); ++x) {
|
for (int x = 0; x < extended_output_shape.Dims(2); ++x) {
|
||||||
for (int c = 0; c < extended_output_shape.Dims(3); ++c) {
|
for (int c = 0; c < extended_output_shape.Dims(3); ++c) {
|
||||||
output_data[Offset(extended_output_shape, b, y, x, c)] =
|
output_data[Offset(extended_output_shape, b, y, x, c)] =
|
||||||
ActivationFunctionWithMinMax(
|
ActivationFunctionWithMinMax<T>(
|
||||||
input1_data[SubscriptToIndex(desc1, b, y, x, c)] +
|
input1_data[SubscriptToIndex(desc1, b, y, x, c)] +
|
||||||
input2_data[SubscriptToIndex(desc2, b, y, x, c)],
|
input2_data[SubscriptToIndex(desc2, b, y, x, c)],
|
||||||
params.float_activation_min, params.float_activation_max);
|
activation_min, activation_max);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void BroadcastAdd4DSlow(const ArithmeticParams& params,
|
|
||||||
const RuntimeShape& input1_shape,
|
|
||||||
const int32_t* input1_data,
|
|
||||||
const RuntimeShape& input2_shape,
|
|
||||||
const int32_t* input2_data,
|
|
||||||
const RuntimeShape& output_shape,
|
|
||||||
int32_t* output_data) {
|
|
||||||
NdArrayDesc<4> desc1;
|
|
||||||
NdArrayDesc<4> desc2;
|
|
||||||
NdArrayDescsForElementwiseBroadcast(input1_shape, input2_shape, &desc1,
|
|
||||||
&desc2);
|
|
||||||
const RuntimeShape extended_output_shape =
|
|
||||||
RuntimeShape::ExtendedShape(4, output_shape);
|
|
||||||
|
|
||||||
// In Tensorflow, the dimensions are canonically named (batch_number, row,
|
|
||||||
// col, channel), with extents (batches, height, width, depth), with the
|
|
||||||
// trailing dimension changing most rapidly (channels has the smallest stride,
|
|
||||||
// typically 1 element).
|
|
||||||
//
|
|
||||||
// In generated C code, we store arrays with the dimensions reversed. The
|
|
||||||
// first dimension has smallest stride.
|
|
||||||
//
|
|
||||||
// We name our variables by their Tensorflow convention, but generate C code
|
|
||||||
// nesting loops such that the innermost loop has the smallest stride for the
|
|
||||||
// best cache behavior.
|
|
||||||
for (int b = 0; b < extended_output_shape.Dims(0); ++b) {
|
|
||||||
for (int y = 0; y < extended_output_shape.Dims(1); ++y) {
|
|
||||||
for (int x = 0; x < extended_output_shape.Dims(2); ++x) {
|
|
||||||
for (int c = 0; c < extended_output_shape.Dims(3); ++c) {
|
|
||||||
output_data[Offset(extended_output_shape, b, y, x, c)] =
|
|
||||||
ActivationFunctionWithMinMax(
|
|
||||||
input1_data[SubscriptToIndex(desc1, b, y, x, c)] +
|
|
||||||
input2_data[SubscriptToIndex(desc2, b, y, x, c)],
|
|
||||||
params.quantized_activation_min,
|
|
||||||
params.quantized_activation_max);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -287,10 +239,11 @@ inline void BroadcastAdd4DSlow(const ArithmeticParams& params,
|
|||||||
// is 32-bit for both cases. The overflow does not happen due to the
|
// is 32-bit for both cases. The overflow does not happen due to the
|
||||||
// choice of the shift (20 or 15, accordingly - see add.cc for more comments).
|
// choice of the shift (20 or 15, accordingly - see add.cc for more comments).
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline void BroadcastAdd4DSlow(
|
inline typename std::enable_if<is_small_integer<T>::value, void>::type
|
||||||
const ArithmeticParams& params, const RuntimeShape& input1_shape,
|
BroadcastAdd4DSlow(const ArithmeticParams& params,
|
||||||
const T* input1_data, const RuntimeShape& input2_shape,
|
const RuntimeShape& input1_shape, const T* input1_data,
|
||||||
const T* input2_data, const RuntimeShape& output_shape, T* output_data) {
|
const RuntimeShape& input2_shape, const T* input2_data,
|
||||||
|
const RuntimeShape& output_shape, T* output_data) {
|
||||||
NdArrayDesc<4> desc1;
|
NdArrayDesc<4> desc1;
|
||||||
NdArrayDesc<4> desc2;
|
NdArrayDesc<4> desc2;
|
||||||
NdArrayDescsForElementwiseBroadcast(input1_shape, input2_shape, &desc1,
|
NdArrayDescsForElementwiseBroadcast(input1_shape, input2_shape, &desc1,
|
||||||
|
|||||||
@@ -15,7 +15,10 @@ limitations under the License.
|
|||||||
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ADD_N_H_
|
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ADD_N_H_
|
||||||
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ADD_N_H_
|
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ADD_N_H_
|
||||||
|
|
||||||
#include "tensorflow/lite/kernels/internal/types.h"
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include "tensorflow/lite/kernels/internal/common.h"
|
||||||
|
|
||||||
namespace tflite {
|
namespace tflite {
|
||||||
namespace reference_ops {
|
namespace reference_ops {
|
||||||
@@ -36,6 +39,47 @@ inline void AddN(const RuntimeShape& input_shape, const size_t num_inputs,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void AddN(const ArithmeticParams& params,
|
||||||
|
const RuntimeShape& input_shape, const size_t num_inputs,
|
||||||
|
const int8_t* const* input_data, int8_t* output_data) {
|
||||||
|
TFLITE_DCHECK_LE(params.quantized_activation_min,
|
||||||
|
params.quantized_activation_max);
|
||||||
|
// Input offset is negative input zero point. Activation tensors are
|
||||||
|
// asymmetric quantized so they span the full int8 range.
|
||||||
|
// All inputs should have same zero-point and scale, this is checked during
|
||||||
|
// Prepare stage.
|
||||||
|
TFLITE_DCHECK_GE(-params.input1_offset, std::numeric_limits<int8_t>::min());
|
||||||
|
TFLITE_DCHECK_LE(-params.input1_offset, std::numeric_limits<int8_t>::max());
|
||||||
|
|
||||||
|
// All inputs and output should have the same shape, this is checked during
|
||||||
|
// Prepare stage.
|
||||||
|
const size_t size = input_shape.FlatSize();
|
||||||
|
for (size_t i = 0; i < size; ++i) {
|
||||||
|
// accumulate in scaled_x before clamping to avoid overflow
|
||||||
|
const int32_t x = params.input1_offset; // x = 0
|
||||||
|
const int32_t shifted_x = x * (1 << params.left_shift);
|
||||||
|
int32_t scaled_x = MultiplyByQuantizedMultiplierSmallerThanOneExp(
|
||||||
|
shifted_x, params.input1_multiplier, params.input1_shift);
|
||||||
|
|
||||||
|
for (size_t j = 0; j < num_inputs; ++j) {
|
||||||
|
const int32_t y = params.input1_offset + input_data[j][i];
|
||||||
|
const int32_t shifted_y = y * (1 << params.left_shift);
|
||||||
|
int32_t scaled_y = MultiplyByQuantizedMultiplierSmallerThanOneExp(
|
||||||
|
shifted_y, params.input1_multiplier, params.input1_shift);
|
||||||
|
scaled_x += scaled_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int32_t raw_output =
|
||||||
|
MultiplyByQuantizedMultiplierSmallerThanOneExp(
|
||||||
|
scaled_x, params.output_multiplier, params.output_shift) +
|
||||||
|
params.output_offset;
|
||||||
|
const int32_t clamped_output =
|
||||||
|
std::min(params.quantized_activation_max,
|
||||||
|
std::max(params.quantized_activation_min, raw_output));
|
||||||
|
output_data[i] = static_cast<int8_t>(clamped_output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace reference_ops
|
} // namespace reference_ops
|
||||||
} // namespace tflite
|
} // namespace tflite
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,275 @@
|
|||||||
|
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==============================================================================*/
|
||||||
|
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_BATCH_MATMUL_H_
|
||||||
|
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_BATCH_MATMUL_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "tensorflow/lite/kernels/internal/common.h"
|
||||||
|
#include "tensorflow/lite/kernels/internal/compatibility.h"
|
||||||
|
#include "tensorflow/lite/kernels/internal/tensor_utils_common.h"
|
||||||
|
#include "tensorflow/lite/kernels/internal/types.h"
|
||||||
|
|
||||||
|
namespace tflite {
|
||||||
|
namespace reference_ops {
|
||||||
|
namespace batch_matmul {
|
||||||
|
|
||||||
|
// Determine which dimension is the broadcast dimension.
|
||||||
|
inline int broadcast_dim(int lhs_dim, int rhs_dim) {
|
||||||
|
if (lhs_dim == rhs_dim) return lhs_dim;
|
||||||
|
if (lhs_dim == 1) return rhs_dim;
|
||||||
|
TFLITE_DCHECK_EQ(rhs_dim, 1);
|
||||||
|
return lhs_dim;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the "extent" for iterating on this dimension.
|
||||||
|
// If we are broadcasting, then don't advance (i.e return 0).
|
||||||
|
inline int extent(const RuntimeShape& shape, int x) {
|
||||||
|
if (shape.Dims(x) == 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int prod = 1;
|
||||||
|
for (int i = x + 1; i < shape.DimensionsCount(); ++i) {
|
||||||
|
prod *= shape.Dims(i);
|
||||||
|
}
|
||||||
|
return prod;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace batch_matmul
|
||||||
|
|
||||||
|
template <typename Ta, typename Tb, typename Tout>
|
||||||
|
inline void BatchMatMul(const RuntimeShape& lhs_shape, const Ta* lhs_data,
|
||||||
|
const RuntimeShape& rhs_shape, const Tb* rhs_data,
|
||||||
|
const RuntimeShape& output_shape, Tout* output_data) {
|
||||||
|
const RuntimeShape extended_lhs_shape =
|
||||||
|
RuntimeShape::ExtendedShape(5, lhs_shape);
|
||||||
|
const RuntimeShape extended_rhs_shape =
|
||||||
|
RuntimeShape::ExtendedShape(5, rhs_shape);
|
||||||
|
|
||||||
|
const int batch_dim0 = batch_matmul::broadcast_dim(
|
||||||
|
extended_lhs_shape.Dims(0), extended_rhs_shape.Dims(0));
|
||||||
|
const int batch_dim1 = batch_matmul::broadcast_dim(
|
||||||
|
extended_lhs_shape.Dims(1), extended_rhs_shape.Dims(1));
|
||||||
|
const int batch_dim2 = batch_matmul::broadcast_dim(
|
||||||
|
extended_lhs_shape.Dims(2), extended_rhs_shape.Dims(2));
|
||||||
|
|
||||||
|
const int lhs_ext0 = batch_matmul::extent(extended_lhs_shape, 0);
|
||||||
|
const int lhs_ext1 = batch_matmul::extent(extended_lhs_shape, 1);
|
||||||
|
const int lhs_ext2 = batch_matmul::extent(extended_lhs_shape, 2);
|
||||||
|
const int rhs_ext0 = batch_matmul::extent(extended_rhs_shape, 0);
|
||||||
|
const int rhs_ext1 = batch_matmul::extent(extended_rhs_shape, 1);
|
||||||
|
const int rhs_ext2 = batch_matmul::extent(extended_rhs_shape, 2);
|
||||||
|
|
||||||
|
// Set params for each matrix multiply.
|
||||||
|
const int lhs_rows = extended_lhs_shape.Dims(3);
|
||||||
|
const int rhs_cols = extended_rhs_shape.Dims(4);
|
||||||
|
const int accum_depth = extended_lhs_shape.Dims(4);
|
||||||
|
|
||||||
|
for (int b0 = 0; b0 < batch_dim0; ++b0) {
|
||||||
|
const Ta* lhs_ptr0 = lhs_data + (b0 * lhs_ext0);
|
||||||
|
const Tb* rhs_ptr0 = rhs_data + (b0 * rhs_ext0);
|
||||||
|
for (int b1 = 0; b1 < batch_dim1; ++b1) {
|
||||||
|
const Ta* lhs_ptr1 = lhs_ptr0 + b1 * lhs_ext1;
|
||||||
|
const Tb* rhs_ptr1 = rhs_ptr0 + b1 * rhs_ext1;
|
||||||
|
for (int b2 = 0; b2 < batch_dim2; ++b2) {
|
||||||
|
const Ta* lhs_ptr2 = lhs_ptr1 + b2 * lhs_ext2;
|
||||||
|
const Tb* rhs_ptr2 = rhs_ptr1 + b2 * rhs_ext2;
|
||||||
|
Tout* out_ptr = output_data + ((b0 * batch_dim1 * batch_dim2) +
|
||||||
|
b1 * batch_dim2 + b2) *
|
||||||
|
lhs_rows * rhs_cols;
|
||||||
|
for (int j = 0; j < rhs_cols; ++j) {
|
||||||
|
for (int i = 0; i < lhs_rows; ++i) {
|
||||||
|
Tout total = 0;
|
||||||
|
for (int k = 0; k < accum_depth; ++k) {
|
||||||
|
total += static_cast<Tout>(lhs_ptr2[accum_depth * i + k]) *
|
||||||
|
static_cast<Tout>(rhs_ptr2[j * accum_depth + k]);
|
||||||
|
}
|
||||||
|
int idx = lhs_rows * j + i;
|
||||||
|
out_ptr[idx] = total;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void BatchMatMul(const RuntimeShape& lhs_shape, const int8_t* lhs_data,
|
||||||
|
const RuntimeShape& rhs_shape, const int8_t* rhs_data,
|
||||||
|
const float* scaling_factors,
|
||||||
|
const int32_t* input_offset, int32_t* row_sums,
|
||||||
|
const RuntimeShape& output_shape, float* output_data,
|
||||||
|
bool* compute_row_sums) {
|
||||||
|
const RuntimeShape extended_lhs_shape =
|
||||||
|
RuntimeShape::ExtendedShape(5, lhs_shape);
|
||||||
|
const RuntimeShape extended_rhs_shape =
|
||||||
|
RuntimeShape::ExtendedShape(5, rhs_shape);
|
||||||
|
|
||||||
|
const int batch_dim0 = batch_matmul::broadcast_dim(
|
||||||
|
extended_lhs_shape.Dims(0), extended_rhs_shape.Dims(0));
|
||||||
|
const int batch_dim1 = batch_matmul::broadcast_dim(
|
||||||
|
extended_lhs_shape.Dims(1), extended_rhs_shape.Dims(1));
|
||||||
|
const int batch_dim2 = batch_matmul::broadcast_dim(
|
||||||
|
extended_lhs_shape.Dims(2), extended_rhs_shape.Dims(2));
|
||||||
|
|
||||||
|
const int lhs_ext0 = batch_matmul::extent(extended_lhs_shape, 0);
|
||||||
|
const int lhs_ext1 = batch_matmul::extent(extended_lhs_shape, 1);
|
||||||
|
const int lhs_ext2 = batch_matmul::extent(extended_lhs_shape, 2);
|
||||||
|
const int rhs_ext0 = batch_matmul::extent(extended_rhs_shape, 0);
|
||||||
|
const int rhs_ext1 = batch_matmul::extent(extended_rhs_shape, 1);
|
||||||
|
const int rhs_ext2 = batch_matmul::extent(extended_rhs_shape, 2);
|
||||||
|
|
||||||
|
// Set params for each matrix multiply.
|
||||||
|
const int lhs_rows = extended_lhs_shape.Dims(3);
|
||||||
|
const int rhs_cols = extended_rhs_shape.Dims(4);
|
||||||
|
const int accum_depth = extended_lhs_shape.Dims(4);
|
||||||
|
|
||||||
|
const int ioff_ext0 = rhs_ext0 == 0 ? 0 : rhs_cols;
|
||||||
|
const int ioff_ext1 = rhs_ext1 == 0 ? 0 : rhs_cols;
|
||||||
|
const int ioff_ext2 = rhs_ext2 == 0 ? 0 : rhs_cols;
|
||||||
|
const int woff_ext0 = lhs_ext0 == 0 ? 0 : lhs_rows;
|
||||||
|
const int woff_ext1 = lhs_ext1 == 0 ? 0 : lhs_rows;
|
||||||
|
const int woff_ext2 = lhs_ext2 == 0 ? 0 : lhs_rows;
|
||||||
|
|
||||||
|
if (!compute_row_sums || *compute_row_sums) {
|
||||||
|
int num_weights_matrices = 1;
|
||||||
|
for (int i = 1; i < extended_lhs_shape.DimensionsCount() - 2; ++i) {
|
||||||
|
num_weights_matrices *= extended_lhs_shape.Dims(i);
|
||||||
|
}
|
||||||
|
tensor_utils::ReductionSumVector(
|
||||||
|
lhs_data, row_sums, num_weights_matrices * lhs_rows, accum_depth);
|
||||||
|
if (compute_row_sums) {
|
||||||
|
*compute_row_sums = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int b0 = 0; b0 < batch_dim0; ++b0) {
|
||||||
|
const int8_t* lhs_ptr0 = lhs_data + (b0 * lhs_ext0);
|
||||||
|
const int8_t* rhs_ptr0 = rhs_data + (b0 * rhs_ext0);
|
||||||
|
const int32_t* ioff_ptr0 = input_offset + (b0 * ioff_ext0);
|
||||||
|
const float* scale_ptr0 = scaling_factors + (b0 * ioff_ext0);
|
||||||
|
const int32_t* woff_ptr0 = row_sums + (b0 * woff_ext0);
|
||||||
|
for (int b1 = 0; b1 < batch_dim1; ++b1) {
|
||||||
|
const int8_t* lhs_ptr1 = lhs_ptr0 + b1 * lhs_ext1;
|
||||||
|
const int8_t* rhs_ptr1 = rhs_ptr0 + b1 * rhs_ext1;
|
||||||
|
const int32_t* ioff_ptr1 = ioff_ptr0 + (b1 * ioff_ext1);
|
||||||
|
const float* scale_ptr1 = scale_ptr0 + (b1 * ioff_ext1);
|
||||||
|
const int32_t* woff_ptr1 = woff_ptr0 + (b1 * woff_ext1);
|
||||||
|
for (int b2 = 0; b2 < batch_dim2; ++b2) {
|
||||||
|
const int8_t* lhs_ptr2 = lhs_ptr1 + b2 * lhs_ext2;
|
||||||
|
const int8_t* rhs_ptr2 = rhs_ptr1 + b2 * rhs_ext2;
|
||||||
|
const int32_t* ioff_ptr2 = ioff_ptr1 + (b2 * ioff_ext2);
|
||||||
|
const float* scale_ptr2 = scale_ptr1 + (b2 * ioff_ext2);
|
||||||
|
const int32_t* woff_ptr2 = woff_ptr1 + (b2 * woff_ext2);
|
||||||
|
float* out_ptr = output_data + ((b0 * batch_dim1 * batch_dim2) +
|
||||||
|
b1 * batch_dim2 + b2) *
|
||||||
|
lhs_rows * rhs_cols;
|
||||||
|
for (int j = 0; j < rhs_cols; ++j) {
|
||||||
|
const float batch_scaling_factor = scale_ptr2[j];
|
||||||
|
const float batch_offset = static_cast<float>(ioff_ptr2[j]);
|
||||||
|
for (int i = 0; i < lhs_rows; ++i) {
|
||||||
|
int32_t total = 0;
|
||||||
|
for (int k = 0; k < accum_depth; ++k) {
|
||||||
|
total +=
|
||||||
|
lhs_ptr2[accum_depth * i + k] * rhs_ptr2[j * accum_depth + k];
|
||||||
|
}
|
||||||
|
int32_t row_sum = woff_ptr2[i];
|
||||||
|
total -= row_sum * batch_offset;
|
||||||
|
int idx = lhs_rows * j + i;
|
||||||
|
out_ptr[idx] += batch_scaling_factor * total;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename AccumT>
|
||||||
|
inline void BatchMatMul(const FullyConnectedParams& params,
|
||||||
|
const RuntimeShape& lhs_shape, const T* lhs_data,
|
||||||
|
const RuntimeShape& rhs_shape, const T* rhs_data,
|
||||||
|
const RuntimeShape& output_shape, T* output_data) {
|
||||||
|
const RuntimeShape extended_lhs_shape =
|
||||||
|
RuntimeShape::ExtendedShape(5, lhs_shape);
|
||||||
|
const RuntimeShape extended_rhs_shape =
|
||||||
|
RuntimeShape::ExtendedShape(5, rhs_shape);
|
||||||
|
|
||||||
|
const int batch_dim0 = batch_matmul::broadcast_dim(
|
||||||
|
extended_lhs_shape.Dims(0), extended_rhs_shape.Dims(0));
|
||||||
|
const int batch_dim1 = batch_matmul::broadcast_dim(
|
||||||
|
extended_lhs_shape.Dims(1), extended_rhs_shape.Dims(1));
|
||||||
|
const int batch_dim2 = batch_matmul::broadcast_dim(
|
||||||
|
extended_lhs_shape.Dims(2), extended_rhs_shape.Dims(2));
|
||||||
|
|
||||||
|
const int lhs_ext0 = batch_matmul::extent(extended_lhs_shape, 0);
|
||||||
|
const int lhs_ext1 = batch_matmul::extent(extended_lhs_shape, 1);
|
||||||
|
const int lhs_ext2 = batch_matmul::extent(extended_lhs_shape, 2);
|
||||||
|
const int rhs_ext0 = batch_matmul::extent(extended_rhs_shape, 0);
|
||||||
|
const int rhs_ext1 = batch_matmul::extent(extended_rhs_shape, 1);
|
||||||
|
const int rhs_ext2 = batch_matmul::extent(extended_rhs_shape, 2);
|
||||||
|
|
||||||
|
// Set params for each matrix multiply.
|
||||||
|
const int lhs_rows = extended_lhs_shape.Dims(3);
|
||||||
|
const int rhs_cols = extended_rhs_shape.Dims(4);
|
||||||
|
const int accum_depth = extended_lhs_shape.Dims(4);
|
||||||
|
|
||||||
|
const int32_t input_offset = params.input_offset;
|
||||||
|
const int32_t filter_offset = params.weights_offset;
|
||||||
|
const int32_t output_offset = params.output_offset;
|
||||||
|
const int32_t output_multiplier = params.output_multiplier;
|
||||||
|
const int output_shift = params.output_shift;
|
||||||
|
const int32_t output_activation_min = params.quantized_activation_min;
|
||||||
|
const int32_t output_activation_max = params.quantized_activation_max;
|
||||||
|
TFLITE_DCHECK_LE(output_activation_min, output_activation_max);
|
||||||
|
|
||||||
|
for (int b0 = 0; b0 < batch_dim0; ++b0) {
|
||||||
|
const T* lhs_ptr0 = lhs_data + (b0 * lhs_ext0);
|
||||||
|
const T* rhs_ptr0 = rhs_data + (b0 * rhs_ext0);
|
||||||
|
for (int b1 = 0; b1 < batch_dim1; ++b1) {
|
||||||
|
const T* lhs_ptr1 = lhs_ptr0 + b1 * lhs_ext1;
|
||||||
|
const T* rhs_ptr1 = rhs_ptr0 + b1 * rhs_ext1;
|
||||||
|
for (int b2 = 0; b2 < batch_dim2; ++b2) {
|
||||||
|
const T* lhs_ptr2 = lhs_ptr1 + b2 * lhs_ext2;
|
||||||
|
const T* rhs_ptr2 = rhs_ptr1 + b2 * rhs_ext2;
|
||||||
|
T* out_ptr = output_data +
|
||||||
|
((b0 * batch_dim1 * batch_dim2) + b1 * batch_dim2 + b2) *
|
||||||
|
lhs_rows * rhs_cols;
|
||||||
|
|
||||||
|
for (int j = 0; j < rhs_cols; ++j) {
|
||||||
|
for (int i = 0; i < lhs_rows; ++i) {
|
||||||
|
AccumT total = 0;
|
||||||
|
for (int k = 0; k < accum_depth; ++k) {
|
||||||
|
AccumT lhs_val = lhs_ptr2[accum_depth * i + k];
|
||||||
|
AccumT rhs_val = rhs_ptr2[accum_depth * j + k];
|
||||||
|
total += (lhs_val + filter_offset) * (rhs_val + input_offset);
|
||||||
|
}
|
||||||
|
int32_t total_scaled = MultiplyByQuantizedMultiplier(
|
||||||
|
total, output_multiplier, output_shift);
|
||||||
|
total_scaled += output_offset;
|
||||||
|
total_scaled = std::max(total_scaled, output_activation_min);
|
||||||
|
total_scaled = std::min(total_scaled, output_activation_max);
|
||||||
|
const int idx = lhs_rows * j + i;
|
||||||
|
out_ptr[idx] = static_cast<T>(total_scaled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace reference_ops
|
||||||
|
} // namespace tflite
|
||||||
|
|
||||||
|
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_BATCH_MATMUL_H_
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==============================================================================*/
|
||||||
|
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_CUMSUM_H_
|
||||||
|
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_CUMSUM_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include "tensorflow/lite/kernels/internal/common.h"
|
||||||
|
#include "tensorflow/lite/kernels/internal/compatibility.h"
|
||||||
|
|
||||||
|
namespace tflite {
|
||||||
|
namespace reference_ops {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void CumSum(const T* input_data, const RuntimeShape& shape, int32_t axis,
|
||||||
|
bool exclusive, bool reverse, T* output_data) {
|
||||||
|
const int32_t rank = shape.DimensionsCount();
|
||||||
|
TFLITE_DCHECK_GE(rank, 1);
|
||||||
|
TFLITE_DCHECK_GE(axis, 0);
|
||||||
|
TFLITE_DCHECK_LT(axis, rank);
|
||||||
|
|
||||||
|
size_t inner = 1;
|
||||||
|
size_t outer = 1;
|
||||||
|
size_t depth = 1;
|
||||||
|
for (int32_t i = 0; i < rank; i++) {
|
||||||
|
if (i < axis)
|
||||||
|
inner *= shape.Dims(i);
|
||||||
|
else if (i > axis)
|
||||||
|
outer *= shape.Dims(i);
|
||||||
|
else
|
||||||
|
depth = shape.Dims(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t outer_index = 0; outer_index < outer; outer_index++) {
|
||||||
|
size_t outer_index_adj;
|
||||||
|
if (reverse)
|
||||||
|
outer_index_adj = (outer - 1) - outer_index;
|
||||||
|
else
|
||||||
|
outer_index_adj = outer_index;
|
||||||
|
for (size_t inner_index = 0; inner_index < inner; inner_index++) {
|
||||||
|
T accumulator = 0;
|
||||||
|
size_t inner_index_adj;
|
||||||
|
if (reverse)
|
||||||
|
inner_index_adj = (inner - 1) - inner_index;
|
||||||
|
else
|
||||||
|
inner_index_adj = inner_index;
|
||||||
|
for (size_t depth_index = 0; depth_index < depth; depth_index++) {
|
||||||
|
size_t depth_index_adj;
|
||||||
|
if (reverse)
|
||||||
|
depth_index_adj = (depth - 1) - depth_index;
|
||||||
|
else
|
||||||
|
depth_index_adj = depth_index;
|
||||||
|
|
||||||
|
size_t index = outer_index_adj;
|
||||||
|
index += inner_index_adj * depth * outer;
|
||||||
|
index += depth_index_adj * outer;
|
||||||
|
|
||||||
|
if (exclusive) {
|
||||||
|
output_data[index] = accumulator;
|
||||||
|
accumulator += input_data[index];
|
||||||
|
} else {
|
||||||
|
accumulator += input_data[index];
|
||||||
|
output_data[index] = accumulator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Quantized INT8 CUMSUM
|
||||||
|
//
|
||||||
|
inline void CumSum(const ArithmeticParams& params, const int8_t* input_data,
|
||||||
|
const RuntimeShape& shape, int32_t axis, bool exclusive,
|
||||||
|
bool reverse, int8_t* output_data) {
|
||||||
|
TFLITE_DCHECK_LE(params.quantized_activation_min,
|
||||||
|
params.quantized_activation_max);
|
||||||
|
// Input offset is negative input zero point. Activation tensors are
|
||||||
|
// asymmetric quantized so they span the full int8 range.
|
||||||
|
// All inputs should have same zero-point and scale, this is checked during
|
||||||
|
// Prepare stage.
|
||||||
|
TFLITE_DCHECK_GE(-params.input1_offset, std::numeric_limits<int8_t>::min());
|
||||||
|
TFLITE_DCHECK_LE(-params.input1_offset, std::numeric_limits<int8_t>::max());
|
||||||
|
|
||||||
|
const int32_t rank = shape.DimensionsCount();
|
||||||
|
TFLITE_DCHECK_GE(rank, 1);
|
||||||
|
TFLITE_DCHECK_GE(axis, 0);
|
||||||
|
TFLITE_DCHECK_LT(axis, rank);
|
||||||
|
|
||||||
|
size_t inner = 1;
|
||||||
|
size_t outer = 1;
|
||||||
|
size_t depth = 1;
|
||||||
|
for (int32_t i = 0; i < rank; i++) {
|
||||||
|
if (i < axis)
|
||||||
|
inner *= shape.Dims(i);
|
||||||
|
else if (i > axis)
|
||||||
|
outer *= shape.Dims(i);
|
||||||
|
else
|
||||||
|
depth = shape.Dims(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t outer_index = 0; outer_index < outer; outer_index++) {
|
||||||
|
size_t outer_index_adj;
|
||||||
|
if (reverse)
|
||||||
|
outer_index_adj = (outer - 1) - outer_index;
|
||||||
|
else
|
||||||
|
outer_index_adj = outer_index;
|
||||||
|
for (size_t inner_index = 0; inner_index < inner; inner_index++) {
|
||||||
|
int32_t accumulator = params.input1_offset; // accumulator = 0
|
||||||
|
accumulator *= (1 << params.left_shift);
|
||||||
|
accumulator = MultiplyByQuantizedMultiplierSmallerThanOneExp(
|
||||||
|
accumulator, params.input1_multiplier, params.input1_shift);
|
||||||
|
|
||||||
|
size_t inner_index_adj;
|
||||||
|
if (reverse)
|
||||||
|
inner_index_adj = (inner - 1) - inner_index;
|
||||||
|
else
|
||||||
|
inner_index_adj = inner_index;
|
||||||
|
|
||||||
|
for (size_t depth_index = 0; depth_index < depth; depth_index++) {
|
||||||
|
size_t depth_index_adj;
|
||||||
|
if (reverse)
|
||||||
|
depth_index_adj = (depth - 1) - depth_index;
|
||||||
|
else
|
||||||
|
depth_index_adj = depth_index;
|
||||||
|
|
||||||
|
size_t index = outer_index_adj;
|
||||||
|
index += inner_index_adj * depth * outer;
|
||||||
|
index += depth_index_adj * outer;
|
||||||
|
|
||||||
|
const int32_t y = params.input1_offset + input_data[index];
|
||||||
|
const int32_t shifted_y = y * (1 << params.left_shift);
|
||||||
|
const int32_t scaled_y = MultiplyByQuantizedMultiplierSmallerThanOneExp(
|
||||||
|
shifted_y, params.input1_multiplier, params.input1_shift);
|
||||||
|
|
||||||
|
int32_t scaled_output;
|
||||||
|
if (exclusive) {
|
||||||
|
scaled_output = accumulator;
|
||||||
|
accumulator += scaled_y;
|
||||||
|
} else {
|
||||||
|
accumulator += scaled_y;
|
||||||
|
scaled_output = accumulator;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int32_t raw_output =
|
||||||
|
MultiplyByQuantizedMultiplierSmallerThanOneExp(
|
||||||
|
scaled_output, params.output_multiplier, params.output_shift) +
|
||||||
|
params.output_offset;
|
||||||
|
const int32_t clamped_output =
|
||||||
|
std::min(params.quantized_activation_max,
|
||||||
|
std::max(params.quantized_activation_min, raw_output));
|
||||||
|
output_data[index] = static_cast<int8_t>(clamped_output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace reference_ops
|
||||||
|
} // namespace tflite
|
||||||
|
|
||||||
|
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_CUMSUM_H_
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==============================================================================*/
|
||||||
|
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_DEPTH_TO_SPACE_H_
|
||||||
|
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_DEPTH_TO_SPACE_H_
|
||||||
|
|
||||||
|
#include "tensorflow/lite/kernels/internal/types.h"
|
||||||
|
|
||||||
|
namespace tflite {
|
||||||
|
namespace reference_ops {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void DepthToSpace(const tflite::DepthToSpaceParams& op_params,
|
||||||
|
const RuntimeShape& unextended_input_shape,
|
||||||
|
const T* input_data,
|
||||||
|
const RuntimeShape& unextended_output_shape,
|
||||||
|
T* output_data) {
|
||||||
|
TFLITE_DCHECK_LE(unextended_input_shape.DimensionsCount(), 4);
|
||||||
|
TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4);
|
||||||
|
const RuntimeShape input_shape =
|
||||||
|
RuntimeShape::ExtendedShape(4, unextended_input_shape);
|
||||||
|
const RuntimeShape output_shape =
|
||||||
|
RuntimeShape::ExtendedShape(4, unextended_output_shape);
|
||||||
|
|
||||||
|
const int input_depth = input_shape.Dims(3);
|
||||||
|
const int input_width = input_shape.Dims(2);
|
||||||
|
const int input_height = input_shape.Dims(1);
|
||||||
|
const int input_batch = input_shape.Dims(0);
|
||||||
|
|
||||||
|
const int output_depth = output_shape.Dims(3);
|
||||||
|
const int output_width = output_shape.Dims(2);
|
||||||
|
const int output_height = output_shape.Dims(1);
|
||||||
|
const int output_batch = output_shape.Dims(0);
|
||||||
|
|
||||||
|
const int32_t block_size = op_params.block_size;
|
||||||
|
|
||||||
|
TFLITE_DCHECK_EQ(input_width * block_size, output_width);
|
||||||
|
TFLITE_DCHECK_EQ(input_height * block_size, output_height);
|
||||||
|
TFLITE_DCHECK_EQ(input_depth, output_depth * block_size * block_size);
|
||||||
|
TFLITE_DCHECK_EQ(input_batch, output_batch);
|
||||||
|
|
||||||
|
for (int out_b = 0; out_b < output_batch; ++out_b) {
|
||||||
|
for (int out_h = 0; out_h < output_height; ++out_h) {
|
||||||
|
for (int out_w = 0; out_w < output_width; ++out_w) {
|
||||||
|
for (int out_d = 0; out_d < output_depth; ++out_d) {
|
||||||
|
const int in_d =
|
||||||
|
out_d + ((out_h % block_size) * block_size + out_w % block_size) *
|
||||||
|
output_depth;
|
||||||
|
|
||||||
|
const int in_w = out_w / block_size;
|
||||||
|
const int in_h = out_h / block_size;
|
||||||
|
const int in_b = out_b;
|
||||||
|
|
||||||
|
const int input_index = Offset(input_shape, in_b, in_h, in_w, in_d);
|
||||||
|
const int output_index =
|
||||||
|
Offset(output_shape, out_b, out_h, out_w, out_d);
|
||||||
|
|
||||||
|
output_data[output_index] = input_data[input_index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace reference_ops
|
||||||
|
} // namespace tflite
|
||||||
|
|
||||||
|
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_DEPTH_TO_SPACE_H_
|
||||||
@@ -1,239 +0,0 @@
|
|||||||
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
==============================================================================*/
|
|
||||||
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_DIV_H_
|
|
||||||
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_DIV_H_
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "tensorflow/lite/kernels/internal/common.h"
|
|
||||||
|
|
||||||
namespace tflite {
|
|
||||||
|
|
||||||
namespace reference_ops {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline void DivCheckArithmeticParams(const ArithmeticParams& params) {
|
|
||||||
TFLITE_DCHECK_LE(params.quantized_activation_min,
|
|
||||||
params.quantized_activation_max);
|
|
||||||
// Input offset is negative input zero point. Activation tensors are
|
|
||||||
// asymmetric quantized so they span the full int8 range.
|
|
||||||
constexpr int32_t max_value =
|
|
||||||
static_cast<int32_t>(std::numeric_limits<T>::max());
|
|
||||||
TFLITE_DCHECK_GE(params.input1_offset, -max_value);
|
|
||||||
TFLITE_DCHECK_LE(params.input1_offset, max_value);
|
|
||||||
TFLITE_DCHECK_GE(params.input2_offset, -max_value);
|
|
||||||
TFLITE_DCHECK_LE(params.input2_offset, max_value);
|
|
||||||
TFLITE_DCHECK_GE(params.output_offset, -max_value);
|
|
||||||
TFLITE_DCHECK_LE(params.output_offset, max_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Element-wise div that can often be used for inner loop of broadcast Div as
|
|
||||||
// well as the non-broadcast Div.
|
|
||||||
template <typename T>
|
|
||||||
inline void DivElementwise(int size, const ArithmeticParams& params,
|
|
||||||
const T* input1_data, const T* input2_data,
|
|
||||||
T* output_data) {
|
|
||||||
DivCheckArithmeticParams<T>(params);
|
|
||||||
|
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
const int32_t input1_val = params.input1_offset + input1_data[i];
|
|
||||||
const int32_t input2_val = params.input2_offset + input2_data[i];
|
|
||||||
TFLITE_DCHECK_NE(input2_val, 0);
|
|
||||||
int recip_shift;
|
|
||||||
const int32_t input2_inv =
|
|
||||||
(input2_val > 0) ? GetReciprocal(input2_val, 31, &recip_shift)
|
|
||||||
: -GetReciprocal(-input2_val, 31, &recip_shift);
|
|
||||||
const int headroom = CountLeadingSignBits(input1_val);
|
|
||||||
const int32_t unscaled_quotient =
|
|
||||||
MultiplyByQuantizedMultiplierGreaterThanOne(input1_val, input2_inv,
|
|
||||||
headroom);
|
|
||||||
const int total_shift = params.output_shift - recip_shift - headroom;
|
|
||||||
const int32_t unclamped_result =
|
|
||||||
params.output_offset +
|
|
||||||
MultiplyByQuantizedMultiplierSmallerThanOneExp(
|
|
||||||
unscaled_quotient, params.output_multiplier, total_shift);
|
|
||||||
const int32_t clamped_output =
|
|
||||||
std::min(params.quantized_activation_max,
|
|
||||||
std::max(params.quantized_activation_min, unclamped_result));
|
|
||||||
output_data[i] = static_cast<T>(clamped_output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Div(const ArithmeticParams& params,
|
|
||||||
const RuntimeShape& input1_shape, const uint8_t* input1_data,
|
|
||||||
const RuntimeShape& input2_shape, const uint8_t* input2_data,
|
|
||||||
const RuntimeShape& output_shape, uint8_t* output_data) {
|
|
||||||
TFLITE_DCHECK_LE(params.quantized_activation_min,
|
|
||||||
params.quantized_activation_max);
|
|
||||||
const int flat_size =
|
|
||||||
MatchingElementsSize(input1_shape, input2_shape, output_shape);
|
|
||||||
|
|
||||||
DivElementwise(flat_size, params, input1_data, input2_data, output_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Div(const ArithmeticParams& params,
|
|
||||||
const RuntimeShape& input1_shape, const int8_t* input1_data,
|
|
||||||
const RuntimeShape& input2_shape, const int8_t* input2_data,
|
|
||||||
const RuntimeShape& output_shape, int8_t* output_data) {
|
|
||||||
TFLITE_DCHECK_LE(params.quantized_activation_min,
|
|
||||||
params.quantized_activation_max);
|
|
||||||
const int flat_size =
|
|
||||||
MatchingElementsSize(input1_shape, input2_shape, output_shape);
|
|
||||||
|
|
||||||
DivElementwise(flat_size, params, input1_data, input2_data, output_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, int N = 5>
|
|
||||||
inline void BroadcastDivSlowQuantized(
|
|
||||||
const ArithmeticParams& params, const RuntimeShape& unextended_input1_shape,
|
|
||||||
const T* input1_data, const RuntimeShape& unextended_input2_shape,
|
|
||||||
const T* input2_data, const RuntimeShape& unextended_output_shape,
|
|
||||||
T* output_data) {
|
|
||||||
TFLITE_DCHECK_LE(unextended_input1_shape.DimensionsCount(), N);
|
|
||||||
TFLITE_DCHECK_LE(unextended_input2_shape.DimensionsCount(), N);
|
|
||||||
TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), N);
|
|
||||||
|
|
||||||
NdArrayDesc<N> desc1;
|
|
||||||
NdArrayDesc<N> desc2;
|
|
||||||
NdArrayDesc<N> output_desc;
|
|
||||||
NdArrayDescsForElementwiseBroadcast(unextended_input1_shape,
|
|
||||||
unextended_input2_shape, &desc1, &desc2);
|
|
||||||
CopyDimsToDesc(RuntimeShape::ExtendedShape(N, unextended_output_shape),
|
|
||||||
&output_desc);
|
|
||||||
|
|
||||||
DivCheckArithmeticParams<T>(params);
|
|
||||||
|
|
||||||
auto div_func = [&](int indexes[N]) {
|
|
||||||
const int32_t input1_val =
|
|
||||||
params.input1_offset + input1_data[SubscriptToIndex(desc1, indexes)];
|
|
||||||
const int32_t input2_val =
|
|
||||||
params.input2_offset + input2_data[SubscriptToIndex(desc2, indexes)];
|
|
||||||
TFLITE_DCHECK_NE(input2_val, 0);
|
|
||||||
int recip_shift;
|
|
||||||
const int32_t input2_inv =
|
|
||||||
(input2_val > 0) ? GetReciprocal(input2_val, 31, &recip_shift)
|
|
||||||
: -GetReciprocal(-input2_val, 31, &recip_shift);
|
|
||||||
const int headroom = CountLeadingSignBits(input1_val);
|
|
||||||
const int32_t unscaled_quotient =
|
|
||||||
MultiplyByQuantizedMultiplierGreaterThanOne(input1_val, input2_inv,
|
|
||||||
headroom);
|
|
||||||
const int total_shift = params.output_shift - recip_shift - headroom;
|
|
||||||
const int32_t unclamped_result =
|
|
||||||
params.output_offset +
|
|
||||||
MultiplyByQuantizedMultiplierSmallerThanOneExp(
|
|
||||||
unscaled_quotient, params.output_multiplier, total_shift);
|
|
||||||
const int32_t clamped_output =
|
|
||||||
std::min(params.quantized_activation_max,
|
|
||||||
std::max(params.quantized_activation_min, unclamped_result));
|
|
||||||
output_data[SubscriptToIndex(output_desc, indexes)] =
|
|
||||||
static_cast<T>(clamped_output);
|
|
||||||
};
|
|
||||||
NDOpsHelper<N>(output_desc, div_func);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int N = 5>
|
|
||||||
inline void BroadcastDivSlow(const ArithmeticParams& params,
|
|
||||||
const RuntimeShape& unextended_input1_shape,
|
|
||||||
const uint8_t* input1_data,
|
|
||||||
const RuntimeShape& unextended_input2_shape,
|
|
||||||
const uint8_t* input2_data,
|
|
||||||
const RuntimeShape& unextended_output_shape,
|
|
||||||
uint8_t* output_data) {
|
|
||||||
BroadcastDivSlowQuantized<uint8_t, N>(
|
|
||||||
params, unextended_input1_shape, input1_data, unextended_input2_shape,
|
|
||||||
input2_data, unextended_output_shape, output_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int N = 5>
|
|
||||||
inline void BroadcastDivSlow(const ArithmeticParams& params,
|
|
||||||
const RuntimeShape& unextended_input1_shape,
|
|
||||||
const int8_t* input1_data,
|
|
||||||
const RuntimeShape& unextended_input2_shape,
|
|
||||||
const int8_t* input2_data,
|
|
||||||
const RuntimeShape& unextended_output_shape,
|
|
||||||
int8_t* output_data) {
|
|
||||||
BroadcastDivSlowQuantized<int8_t, N>(
|
|
||||||
params, unextended_input1_shape, input1_data, unextended_input2_shape,
|
|
||||||
input2_data, unextended_output_shape, output_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(jiawen): We can implement BroadcastDiv on buffers of arbitrary
|
|
||||||
// dimensionality if the runtime code does a single loop over one dimension
|
|
||||||
// that handles broadcasting as the base case. The code generator would then
|
|
||||||
// generate max(D1, D2) nested for loops.
|
|
||||||
template <typename T, int N = 5>
|
|
||||||
void BroadcastDivSlow(const ArithmeticParams& params,
|
|
||||||
const RuntimeShape& unextended_input1_shape,
|
|
||||||
const T* input1_data,
|
|
||||||
const RuntimeShape& unextended_input2_shape,
|
|
||||||
const T* input2_data,
|
|
||||||
const RuntimeShape& unextended_output_shape,
|
|
||||||
T* output_data) {
|
|
||||||
T output_activation_min;
|
|
||||||
T output_activation_max;
|
|
||||||
GetActivationParams(params, &output_activation_min, &output_activation_max);
|
|
||||||
|
|
||||||
TFLITE_DCHECK_LE(unextended_input1_shape.DimensionsCount(), N);
|
|
||||||
TFLITE_DCHECK_LE(unextended_input2_shape.DimensionsCount(), N);
|
|
||||||
TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), N);
|
|
||||||
|
|
||||||
NdArrayDesc<N> desc1;
|
|
||||||
NdArrayDesc<N> desc2;
|
|
||||||
NdArrayDesc<N> output_desc;
|
|
||||||
NdArrayDescsForElementwiseBroadcast(unextended_input1_shape,
|
|
||||||
unextended_input2_shape, &desc1, &desc2);
|
|
||||||
CopyDimsToDesc(RuntimeShape::ExtendedShape(N, unextended_output_shape),
|
|
||||||
&output_desc);
|
|
||||||
|
|
||||||
// In Tensorflow, the dimensions are canonically named (batch_number, row,
|
|
||||||
// col, channel), with extents (batches, height, width, depth), with the
|
|
||||||
// trailing dimension changing most rapidly (channels has the smallest
|
|
||||||
// stride, typically 1 element).
|
|
||||||
//
|
|
||||||
// In generated C code, we store arrays with the dimensions reversed. The
|
|
||||||
// first dimension has smallest stride.
|
|
||||||
|
|
||||||
auto div_func = [&](int indexes[N]) {
|
|
||||||
output_data[SubscriptToIndex(output_desc, indexes)] =
|
|
||||||
ActivationFunctionWithMinMax(
|
|
||||||
input1_data[SubscriptToIndex(desc1, indexes)] /
|
|
||||||
input2_data[SubscriptToIndex(desc2, indexes)],
|
|
||||||
output_activation_min, output_activation_max);
|
|
||||||
};
|
|
||||||
NDOpsHelper<N>(output_desc, div_func);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline void Div(const ArithmeticParams& params,
|
|
||||||
const RuntimeShape& input1_shape, const T* input1_data,
|
|
||||||
const RuntimeShape& input2_shape, const T* input2_data,
|
|
||||||
const RuntimeShape& output_shape, T* output_data) {
|
|
||||||
T output_activation_min;
|
|
||||||
T output_activation_max;
|
|
||||||
GetActivationParams(params, &output_activation_min, &output_activation_max);
|
|
||||||
|
|
||||||
const int flat_size =
|
|
||||||
MatchingElementsSize(input1_shape, input2_shape, output_shape);
|
|
||||||
for (int i = 0; i < flat_size; ++i) {
|
|
||||||
output_data[i] = ActivationFunctionWithMinMax(
|
|
||||||
input1_data[i] / input2_data[i], output_activation_min,
|
|
||||||
output_activation_max);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace reference_ops
|
|
||||||
} // namespace tflite
|
|
||||||
|
|
||||||
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_DIV_H_
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==============================================================================*/
|
||||||
|
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_FLOOR_DIV_H_
|
||||||
|
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_FLOOR_DIV_H_
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "tensorflow/lite/kernels/internal/types.h"
|
||||||
|
|
||||||
|
namespace tflite {
|
||||||
|
namespace reference_ops {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T FloorDiv(T input1, T input2) {
|
||||||
|
return std::floor(std::divides<double>()(static_cast<double>(input1),
|
||||||
|
static_cast<double>(input2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace reference_ops
|
||||||
|
} // namespace tflite
|
||||||
|
|
||||||
|
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_FLOOR_DIV_H_
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==============================================================================*/
|
||||||
|
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_FLOOR_MOD_H_
|
||||||
|
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_FLOOR_MOD_H_
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace tflite {
|
||||||
|
|
||||||
|
namespace reference_ops {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T FloorMod(T input1, T input2) {
|
||||||
|
struct FloatMod {
|
||||||
|
float operator()(const float lhs, const float rhs) const {
|
||||||
|
return std::fmod(lhs, rhs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using ModFunc = typename std::conditional<std::is_integral<T>::value,
|
||||||
|
std::modulus<T>, FloatMod>::type;
|
||||||
|
ModFunc mod_func;
|
||||||
|
T trunc_mod = mod_func(input1, input2);
|
||||||
|
return (trunc_mod != 0) && ((input2 < 0) != (trunc_mod < 0))
|
||||||
|
? (trunc_mod + input2)
|
||||||
|
: trunc_mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace reference_ops
|
||||||
|
} // namespace tflite
|
||||||
|
|
||||||
|
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_FLOOR_MOD_H_
|
||||||
@@ -21,7 +21,7 @@ limitations under the License.
|
|||||||
namespace tflite {
|
namespace tflite {
|
||||||
namespace reference_integer_ops {
|
namespace reference_integer_ops {
|
||||||
|
|
||||||
inline void AveragePool(const PoolParams& params,
|
inline bool AveragePool(const PoolParams& params,
|
||||||
const RuntimeShape& input_shape,
|
const RuntimeShape& input_shape,
|
||||||
const int8_t* input_data,
|
const int8_t* input_data,
|
||||||
const RuntimeShape& output_shape, int8_t* output_data) {
|
const RuntimeShape& output_shape, int8_t* output_data) {
|
||||||
@@ -66,6 +66,7 @@ inline void AveragePool(const PoolParams& params,
|
|||||||
filter_count++;
|
filter_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (filter_count == 0) return false;
|
||||||
// Round to the closest integer value.
|
// Round to the closest integer value.
|
||||||
acc = acc > 0 ? (acc + filter_count / 2) / filter_count
|
acc = acc > 0 ? (acc + filter_count / 2) / filter_count
|
||||||
: (acc - filter_count / 2) / filter_count;
|
: (acc - filter_count / 2) / filter_count;
|
||||||
@@ -77,6 +78,7 @@ inline void AveragePool(const PoolParams& params,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void MaxPool(const PoolParams& params, const RuntimeShape& input_shape,
|
inline void MaxPool(const PoolParams& params, const RuntimeShape& input_shape,
|
||||||
@@ -136,7 +138,7 @@ inline void MaxPool(const PoolParams& params, const RuntimeShape& input_shape,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void AveragePool(const PoolParams& params,
|
inline bool AveragePool(const PoolParams& params,
|
||||||
const RuntimeShape& input_shape,
|
const RuntimeShape& input_shape,
|
||||||
const int16_t* input_data,
|
const int16_t* input_data,
|
||||||
const RuntimeShape& output_shape,
|
const RuntimeShape& output_shape,
|
||||||
@@ -182,6 +184,7 @@ inline void AveragePool(const PoolParams& params,
|
|||||||
filter_count++;
|
filter_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (filter_count == 0) return false;
|
||||||
// Round to the closest integer value.
|
// Round to the closest integer value.
|
||||||
acc = acc > 0 ? (acc + filter_count / 2) / filter_count
|
acc = acc > 0 ? (acc + filter_count / 2) / filter_count
|
||||||
: (acc - filter_count / 2) / filter_count;
|
: (acc - filter_count / 2) / filter_count;
|
||||||
@@ -193,6 +196,7 @@ inline void AveragePool(const PoolParams& params,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void MaxPool(const PoolParams& params, const RuntimeShape& input_shape,
|
inline void MaxPool(const PoolParams& params, const RuntimeShape& input_shape,
|
||||||
|
|||||||
@@ -0,0 +1,256 @@
|
|||||||
|
/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==============================================================================*/
|
||||||
|
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_LOG_SOFTMAX_H_
|
||||||
|
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_LOG_SOFTMAX_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include "fixedpoint/fixedpoint.h"
|
||||||
|
#include "tensorflow/lite/kernels/internal/common.h"
|
||||||
|
|
||||||
|
namespace tflite {
|
||||||
|
namespace reference_ops {
|
||||||
|
|
||||||
|
inline void LogSoftmax(const SoftmaxParams& params,
|
||||||
|
const RuntimeShape& input_shape, const float* input_data,
|
||||||
|
const RuntimeShape& output_shape, float* output_data) {
|
||||||
|
const int trailing_dim = input_shape.DimensionsCount() - 1;
|
||||||
|
const int outer_size =
|
||||||
|
MatchingFlatSizeSkipDim(input_shape, trailing_dim, output_shape);
|
||||||
|
const int depth =
|
||||||
|
MatchingDim(input_shape, trailing_dim, output_shape, trailing_dim);
|
||||||
|
|
||||||
|
for (int i = 0; i < outer_size; ++i) {
|
||||||
|
// Find max element value which we'll use to ensure numerical stability
|
||||||
|
// taking advantage of the following equality:
|
||||||
|
// log(exp(x[i])/sum(exp(x[i]))) == log(exp(x[i]+C)/sum(exp(x[i]+C)))
|
||||||
|
float max = std::numeric_limits<float>::lowest();
|
||||||
|
for (int c = 0; c < depth; ++c) {
|
||||||
|
max = std::max(max, input_data[i * depth + c]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute sum.
|
||||||
|
float sum = 0.f;
|
||||||
|
for (int c = 0; c < depth; ++c) {
|
||||||
|
sum += std::exp(input_data[i * depth + c] - max);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute result.
|
||||||
|
const float log_sum = std::log(sum);
|
||||||
|
for (int c = 0; c < depth; ++c) {
|
||||||
|
output_data[i * depth + c] = input_data[i * depth + c] - max - log_sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void LogSoftmax(const SoftmaxParams& params,
|
||||||
|
const RuntimeShape& input_shape,
|
||||||
|
const uint8_t* input_data,
|
||||||
|
const RuntimeShape& output_shape, uint8_t* output_data) {
|
||||||
|
const int32_t input_multiplier = params.input_multiplier;
|
||||||
|
const int32_t input_left_shift = params.input_left_shift;
|
||||||
|
const int32_t reverse_scaling_divisor = params.reverse_scaling_divisor;
|
||||||
|
const int32_t reverse_scaling_right_shift =
|
||||||
|
params.reverse_scaling_right_shift;
|
||||||
|
const int diff_min = params.diff_min;
|
||||||
|
// The representation chosen for the input to the exp() function is Q5.26.
|
||||||
|
// We need to leave extra space since values that we skip might be as large
|
||||||
|
// as -32 before multiplying by input_beta_multiplier, and therefore as
|
||||||
|
// large as -16 afterwards. Note that exp(-8) is definitely not
|
||||||
|
// insignificant to accumulation, but exp(-16) definitely is.
|
||||||
|
static constexpr int kScaledDiffIntegerBits = 5;
|
||||||
|
static constexpr int kAccumulationIntegerBits = 12;
|
||||||
|
static constexpr int kOutputIntegerBits = 4;
|
||||||
|
using FixedPointScaledDiff =
|
||||||
|
gemmlowp::FixedPoint<int32_t, kScaledDiffIntegerBits>;
|
||||||
|
using FixedPointAccum =
|
||||||
|
gemmlowp::FixedPoint<int32_t, kAccumulationIntegerBits>;
|
||||||
|
|
||||||
|
const int trailing_dim = input_shape.DimensionsCount() - 1;
|
||||||
|
const int outer_size =
|
||||||
|
MatchingFlatSizeSkipDim(input_shape, trailing_dim, output_shape);
|
||||||
|
const int depth =
|
||||||
|
MatchingDim(input_shape, trailing_dim, output_shape, trailing_dim);
|
||||||
|
|
||||||
|
for (int i = 0; i < outer_size; ++i) {
|
||||||
|
uint8_t max_in_row = 0;
|
||||||
|
for (int c = 0; c < depth; ++c) {
|
||||||
|
max_in_row = std::max(max_in_row, input_data[i * depth + c]);
|
||||||
|
}
|
||||||
|
|
||||||
|
FixedPointAccum sum_of_exps = FixedPointAccum::Zero();
|
||||||
|
for (int c = 0; c < depth; ++c) {
|
||||||
|
int32_t input_diff =
|
||||||
|
static_cast<int32_t>(input_data[i * depth + c]) - max_in_row;
|
||||||
|
if (input_diff >= diff_min) {
|
||||||
|
const int32_t input_diff_rescaled =
|
||||||
|
MultiplyByQuantizedMultiplierGreaterThanOne(
|
||||||
|
input_diff, input_multiplier, input_left_shift);
|
||||||
|
const FixedPointScaledDiff scaled_diff_f8 =
|
||||||
|
FixedPointScaledDiff::FromRaw(input_diff_rescaled);
|
||||||
|
sum_of_exps = sum_of_exps + gemmlowp::Rescale<kAccumulationIntegerBits>(
|
||||||
|
exp_on_negative_values(scaled_diff_f8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const int32_t fixed_log_sum_of_exps =
|
||||||
|
log_x_for_x_greater_than_or_equal_to_1<kScaledDiffIntegerBits>(
|
||||||
|
sum_of_exps)
|
||||||
|
.raw();
|
||||||
|
|
||||||
|
// rescaled_diff_min is smallest representable in
|
||||||
|
// Q(kScaledDiffIntegerBits).(31-kScaledDiffIntegerBits) plus the
|
||||||
|
// log-sub-exps that will be subtracted in the loop.
|
||||||
|
//
|
||||||
|
// The thresholds diff_min, etc are negative.
|
||||||
|
const int rescaled_diff_min =
|
||||||
|
fixed_log_sum_of_exps + std::numeric_limits<int32_t>::lowest();
|
||||||
|
const int adjusted_diff_min =
|
||||||
|
std::max(static_cast<int32_t>(
|
||||||
|
diff_min - 1), // Note use of > below instead of >= above.
|
||||||
|
MultiplyByQuantizedMultiplierSmallerThanOneExp(
|
||||||
|
rescaled_diff_min, reverse_scaling_divisor,
|
||||||
|
-reverse_scaling_right_shift));
|
||||||
|
|
||||||
|
for (int c = 0; c < depth; ++c) {
|
||||||
|
int32_t input_diff =
|
||||||
|
static_cast<int32_t>(input_data[i * depth + c]) - max_in_row;
|
||||||
|
if (input_diff > adjusted_diff_min) {
|
||||||
|
const int32_t input_diff_rescaled =
|
||||||
|
MultiplyByQuantizedMultiplierGreaterThanOne(
|
||||||
|
input_diff, input_multiplier, input_left_shift);
|
||||||
|
int32_t unsat_output =
|
||||||
|
gemmlowp::RoundingDivideByPOT(
|
||||||
|
(input_diff_rescaled - fixed_log_sum_of_exps),
|
||||||
|
31 - kScaledDiffIntegerBits - kOutputIntegerBits) +
|
||||||
|
255;
|
||||||
|
|
||||||
|
output_data[i * depth + c] = static_cast<uint8_t>(
|
||||||
|
std::max(std::min(unsat_output, static_cast<int32_t>(255)),
|
||||||
|
static_cast<int32_t>(0)));
|
||||||
|
} else {
|
||||||
|
// Set output to smallest value.
|
||||||
|
output_data[i * depth + c] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void LogSoftmaxQuantized(const SoftmaxParams& params,
|
||||||
|
const size_t outer_size, const size_t depth,
|
||||||
|
const RuntimeShape& input_shape,
|
||||||
|
const T* input_data,
|
||||||
|
const RuntimeShape& output_shape,
|
||||||
|
T* output_data) {
|
||||||
|
const int32_t input_multiplier = params.input_multiplier;
|
||||||
|
const int32_t input_left_shift = params.input_left_shift;
|
||||||
|
const int32_t reverse_scaling_divisor = params.reverse_scaling_divisor;
|
||||||
|
const int32_t reverse_scaling_right_shift =
|
||||||
|
params.reverse_scaling_right_shift;
|
||||||
|
const int diff_min = params.diff_min;
|
||||||
|
|
||||||
|
static constexpr T kMinT8 = std::numeric_limits<T>::min();
|
||||||
|
static constexpr T kMaxT8 = std::numeric_limits<T>::max();
|
||||||
|
static constexpr int32_t kMinInt32 = std::numeric_limits<int32_t>::min();
|
||||||
|
|
||||||
|
// All IntegerBits must agree with Prepare function.
|
||||||
|
// Input is chosen as Q5.26 so exp(-1 * 2^5 * 2^-1) = exp(-16) is negligible.
|
||||||
|
static constexpr int kInputIntegerBits = 5;
|
||||||
|
static constexpr int kAccumulationIntegerBits = 12;
|
||||||
|
static constexpr int kOutputIntegerBits = 4;
|
||||||
|
using F5 = gemmlowp::FixedPoint<int32_t, kInputIntegerBits>;
|
||||||
|
using F12 = gemmlowp::FixedPoint<int32_t, kAccumulationIntegerBits>;
|
||||||
|
|
||||||
|
for (size_t outer_index = 0; outer_index < outer_size; ++outer_index) {
|
||||||
|
T max_in_row = kMinT8;
|
||||||
|
for (size_t inner_index = 0; inner_index < depth; ++inner_index) {
|
||||||
|
max_in_row =
|
||||||
|
std::max(max_in_row, input_data[outer_index * depth + inner_index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accumulator "sum_of_exps_in_q12" is safe from overflowing in 2^12 steps.
|
||||||
|
F12 sum_of_exps_in_q12 = F12::FromRaw(0);
|
||||||
|
for (size_t inner_index = 0; inner_index < depth; ++inner_index) {
|
||||||
|
int32_t input_diff =
|
||||||
|
static_cast<int32_t>(input_data[outer_index * depth + inner_index]) -
|
||||||
|
max_in_row;
|
||||||
|
if (input_diff >= diff_min) {
|
||||||
|
const int32_t input_diff_in_q5 = MultiplyByQuantizedMultiplier(
|
||||||
|
input_diff, input_multiplier, input_left_shift);
|
||||||
|
sum_of_exps_in_q12 =
|
||||||
|
sum_of_exps_in_q12 +
|
||||||
|
gemmlowp::Rescale<kAccumulationIntegerBits>(
|
||||||
|
exp_on_negative_values(F5::FromRaw(input_diff_in_q5)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const int32_t log_sum_of_exps_in_q5 =
|
||||||
|
log_x_for_x_greater_than_or_equal_to_1<kInputIntegerBits>(
|
||||||
|
sum_of_exps_in_q12)
|
||||||
|
.raw();
|
||||||
|
|
||||||
|
// Potentially reduced the valid range. shifted_log_sum_of_exps_in_q5 is
|
||||||
|
// smallest representable in Q5.26 plus the log_sum_of_exps.
|
||||||
|
const int32_t shifted_log_sum_of_exps_in_q5 =
|
||||||
|
log_sum_of_exps_in_q5 + kMinInt32;
|
||||||
|
const int32_t adjusted_diff_min =
|
||||||
|
std::max(static_cast<int32_t>(diff_min - 1),
|
||||||
|
MultiplyByQuantizedMultiplier(shifted_log_sum_of_exps_in_q5,
|
||||||
|
reverse_scaling_divisor,
|
||||||
|
-reverse_scaling_right_shift));
|
||||||
|
|
||||||
|
for (size_t inner_index = 0; inner_index < depth; ++inner_index) {
|
||||||
|
int32_t input_diff =
|
||||||
|
static_cast<int32_t>(input_data[outer_index * depth + inner_index]) -
|
||||||
|
max_in_row;
|
||||||
|
// Note use of > below instead of >= above.
|
||||||
|
if (input_diff > adjusted_diff_min) {
|
||||||
|
const int32_t input_diff_in_q5 = MultiplyByQuantizedMultiplier(
|
||||||
|
input_diff, input_multiplier, input_left_shift);
|
||||||
|
|
||||||
|
// Rescale and downcast.
|
||||||
|
int32_t output_in_q27 =
|
||||||
|
gemmlowp::RoundingDivideByPOT(
|
||||||
|
(input_diff_in_q5 - log_sum_of_exps_in_q5),
|
||||||
|
31 - kInputIntegerBits - kOutputIntegerBits) +
|
||||||
|
kMaxT8;
|
||||||
|
|
||||||
|
output_in_q27 =
|
||||||
|
std::max(std::min(output_in_q27, static_cast<int32_t>(kMaxT8)),
|
||||||
|
static_cast<int32_t>(kMinT8));
|
||||||
|
output_data[outer_index * depth + inner_index] =
|
||||||
|
static_cast<T>(output_in_q27);
|
||||||
|
} else {
|
||||||
|
output_data[outer_index * depth + inner_index] = kMinT8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void LogSoftmax(const SoftmaxParams& params, const size_t outer_size,
|
||||||
|
const size_t depth, const RuntimeShape& input_shape,
|
||||||
|
const int8_t* input_data,
|
||||||
|
const RuntimeShape& output_shape, int8_t* output_data) {
|
||||||
|
LogSoftmaxQuantized(params, outer_size, depth, input_shape, input_data,
|
||||||
|
output_shape, output_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace reference_ops
|
||||||
|
} // namespace tflite
|
||||||
|
|
||||||
|
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_LOG_SOFTMAX_H_
|
||||||
@@ -51,7 +51,7 @@ inline void Mul(const ArithmeticParams& params,
|
|||||||
GetActivationParams(params, &output_activation_min, &output_activation_max);
|
GetActivationParams(params, &output_activation_min, &output_activation_max);
|
||||||
|
|
||||||
const int flat_size =
|
const int flat_size =
|
||||||
MatchingFlatSize(input1_shape, input2_shape, output_shape);
|
MatchingExtendedShapeFlatSize(input1_shape, input2_shape, output_shape);
|
||||||
for (int i = 0; i < flat_size; ++i) {
|
for (int i = 0; i < flat_size; ++i) {
|
||||||
output_data[i] = ActivationFunctionWithMinMax(
|
output_data[i] = ActivationFunctionWithMinMax(
|
||||||
input1_data[i] * input2_data[i], output_activation_min,
|
input1_data[i] * input2_data[i], output_activation_min,
|
||||||
@@ -66,7 +66,7 @@ inline void Mul(const ArithmeticParams& params,
|
|||||||
TFLITE_DCHECK_LE(params.quantized_activation_min,
|
TFLITE_DCHECK_LE(params.quantized_activation_min,
|
||||||
params.quantized_activation_max);
|
params.quantized_activation_max);
|
||||||
const int flat_size =
|
const int flat_size =
|
||||||
MatchingFlatSize(input1_shape, input2_shape, output_shape);
|
MatchingExtendedShapeFlatSize(input1_shape, input2_shape, output_shape);
|
||||||
|
|
||||||
MulElementwise(flat_size, params, input1_data, input2_data, output_data);
|
MulElementwise(flat_size, params, input1_data, input2_data, output_data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ namespace tflite {
|
|||||||
|
|
||||||
namespace reference_ops {
|
namespace reference_ops {
|
||||||
|
|
||||||
// TFLite Pad supports activation tensors with up to 4 dimensions.
|
// TFLite Pad supports activation tensors with up to 5 dimensions.
|
||||||
constexpr int PadKernelMaxDimensionCount() { return 4; }
|
constexpr int PadKernelMaxDimensionCount() { return 5; }
|
||||||
|
|
||||||
// There are two versions of pad: Pad and PadV2. In PadV2 there is a second
|
// There are two versions of pad: Pad and PadV2. In PadV2 there is a second
|
||||||
// scalar input that provides the padding value. Therefore pad_value_ptr can be
|
// scalar input that provides the padding value. Therefore pad_value_ptr can be
|
||||||
@@ -46,8 +46,8 @@ inline void PadImpl(const tflite::PadParams& op_params,
|
|||||||
TFLITE_DCHECK_LE(op_params.left_padding_count, PadKernelMaxDimensionCount());
|
TFLITE_DCHECK_LE(op_params.left_padding_count, PadKernelMaxDimensionCount());
|
||||||
TFLITE_DCHECK_LE(op_params.right_padding_count, PadKernelMaxDimensionCount());
|
TFLITE_DCHECK_LE(op_params.right_padding_count, PadKernelMaxDimensionCount());
|
||||||
|
|
||||||
// Runtime calls are currently fixed at 4 dimensions. Copy inputs so we can
|
// Runtime calls are currently fixed at 5 dimensions. Copy inputs so we can
|
||||||
// pad them to 4 dims (yes, we are "padding the padding").
|
// pad them to 5 dims (yes, we are "padding the padding").
|
||||||
int left_padding_copy[PadKernelMaxDimensionCount()];
|
int left_padding_copy[PadKernelMaxDimensionCount()];
|
||||||
for (int i = 0; i < PadKernelMaxDimensionCount(); i++) {
|
for (int i = 0; i < PadKernelMaxDimensionCount(); i++) {
|
||||||
left_padding_copy[i] = 0;
|
left_padding_copy[i] = 0;
|
||||||
@@ -67,39 +67,46 @@ inline void PadImpl(const tflite::PadParams& op_params,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const int output_batch = ext_output_shape.Dims(0);
|
const int output_batch = ext_output_shape.Dims(0);
|
||||||
const int output_height = ext_output_shape.Dims(1);
|
const int output_plane = ext_output_shape.Dims(1);
|
||||||
const int output_width = ext_output_shape.Dims(2);
|
const int output_height = ext_output_shape.Dims(2);
|
||||||
const int output_depth = ext_output_shape.Dims(3);
|
const int output_width = ext_output_shape.Dims(3);
|
||||||
|
const int output_depth = ext_output_shape.Dims(4);
|
||||||
|
|
||||||
const int left_b_padding = left_padding_copy[0];
|
const int left_b_padding = left_padding_copy[0];
|
||||||
const int left_h_padding = left_padding_copy[1];
|
const int left_p_padding = left_padding_copy[1];
|
||||||
const int left_w_padding = left_padding_copy[2];
|
const int left_h_padding = left_padding_copy[2];
|
||||||
const int left_d_padding = left_padding_copy[3];
|
const int left_w_padding = left_padding_copy[3];
|
||||||
|
const int left_d_padding = left_padding_copy[4];
|
||||||
|
|
||||||
const int right_b_padding = right_padding_copy[0];
|
const int right_b_padding = right_padding_copy[0];
|
||||||
const int right_h_padding = right_padding_copy[1];
|
const int right_p_padding = right_padding_copy[1];
|
||||||
const int right_w_padding = right_padding_copy[2];
|
const int right_h_padding = right_padding_copy[2];
|
||||||
const int right_d_padding = right_padding_copy[3];
|
const int right_w_padding = right_padding_copy[3];
|
||||||
|
const int right_d_padding = right_padding_copy[4];
|
||||||
|
|
||||||
const T pad_value = *pad_value_ptr;
|
const T pad_value = *pad_value_ptr;
|
||||||
|
|
||||||
const T* in_ptr = input_data;
|
const T* in_ptr = input_data;
|
||||||
T* out_ptr = output_data;
|
T* out_ptr = output_data;
|
||||||
for (int out_b = 0; out_b < output_batch; ++out_b) {
|
for (int out_b = 0; out_b < output_batch; ++out_b) {
|
||||||
for (int out_h = 0; out_h < output_height; ++out_h) {
|
for (int out_p = 0; out_p < output_plane; ++out_p) {
|
||||||
for (int out_w = 0; out_w < output_width; ++out_w) {
|
for (int out_h = 0; out_h < output_height; ++out_h) {
|
||||||
for (int out_d = 0; out_d < output_depth; ++out_d) {
|
for (int out_w = 0; out_w < output_width; ++out_w) {
|
||||||
if (out_b < left_b_padding ||
|
for (int out_d = 0; out_d < output_depth; ++out_d) {
|
||||||
out_b >= output_batch - right_b_padding ||
|
if (out_b < left_b_padding ||
|
||||||
out_h < left_h_padding ||
|
out_b >= output_batch - right_b_padding ||
|
||||||
out_h >= output_height - right_h_padding ||
|
out_p < left_p_padding ||
|
||||||
out_w < left_w_padding ||
|
out_p >= output_plane - right_p_padding ||
|
||||||
out_w >= output_width - right_w_padding ||
|
out_h < left_h_padding ||
|
||||||
out_d < left_d_padding ||
|
out_h >= output_height - right_h_padding ||
|
||||||
out_d >= output_depth - right_d_padding) {
|
out_w < left_w_padding ||
|
||||||
*out_ptr++ = pad_value;
|
out_w >= output_width - right_w_padding ||
|
||||||
} else {
|
out_d < left_d_padding ||
|
||||||
*out_ptr++ = *in_ptr++;
|
out_d >= output_depth - right_d_padding) {
|
||||||
|
*out_ptr++ = pad_value;
|
||||||
|
} else {
|
||||||
|
*out_ptr++ = *in_ptr++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ limitations under the License.
|
|||||||
namespace tflite {
|
namespace tflite {
|
||||||
namespace reference_ops {
|
namespace reference_ops {
|
||||||
|
|
||||||
inline void AveragePool(const PoolParams& params,
|
inline bool AveragePool(const PoolParams& params,
|
||||||
const RuntimeShape& input_shape,
|
const RuntimeShape& input_shape,
|
||||||
const float* input_data,
|
const float* input_data,
|
||||||
const RuntimeShape& output_shape, float* output_data) {
|
const RuntimeShape& output_shape, float* output_data) {
|
||||||
@@ -66,6 +66,7 @@ inline void AveragePool(const PoolParams& params,
|
|||||||
filter_count++;
|
filter_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (filter_count == 0) return false;
|
||||||
const float average = total / filter_count;
|
const float average = total / filter_count;
|
||||||
output_data[Offset(output_shape, batch, out_y, out_x, channel)] =
|
output_data[Offset(output_shape, batch, out_y, out_x, channel)] =
|
||||||
ActivationFunctionWithMinMax(average, params.float_activation_min,
|
ActivationFunctionWithMinMax(average, params.float_activation_min,
|
||||||
@@ -74,9 +75,10 @@ inline void AveragePool(const PoolParams& params,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void AveragePool(const PoolParams& params,
|
inline bool AveragePool(const PoolParams& params,
|
||||||
const RuntimeShape& input_shape,
|
const RuntimeShape& input_shape,
|
||||||
const uint8_t* input_data,
|
const uint8_t* input_data,
|
||||||
const RuntimeShape& output_shape,
|
const RuntimeShape& output_shape,
|
||||||
@@ -122,6 +124,7 @@ inline void AveragePool(const PoolParams& params,
|
|||||||
filter_count++;
|
filter_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (filter_count == 0) return false;
|
||||||
acc = (acc + filter_count / 2) / filter_count;
|
acc = (acc + filter_count / 2) / filter_count;
|
||||||
acc = std::max(acc, params.quantized_activation_min);
|
acc = std::max(acc, params.quantized_activation_min);
|
||||||
acc = std::min(acc, params.quantized_activation_max);
|
acc = std::min(acc, params.quantized_activation_max);
|
||||||
@@ -131,6 +134,7 @@ inline void AveragePool(const PoolParams& params,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void L2Pool(const PoolParams& params, const RuntimeShape& input_shape,
|
inline void L2Pool(const PoolParams& params, const RuntimeShape& input_shape,
|
||||||
|
|||||||
@@ -0,0 +1,774 @@
|
|||||||
|
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==============================================================================*/
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <limits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "fixedpoint/fixedpoint.h"
|
||||||
|
#include "tensorflow/lite/kernels/internal/common.h"
|
||||||
|
#include "tensorflow/lite/kernels/internal/compatibility.h"
|
||||||
|
#include "tensorflow/lite/kernels/internal/cppmath.h"
|
||||||
|
#include "tensorflow/lite/kernels/internal/reference/portable_tensor_utils_impl.h"
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#define __restrict__ __restrict
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace tflite {
|
||||||
|
namespace tensor_utils {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const int32_t kInt16Max = std::numeric_limits<int16_t>::max();
|
||||||
|
const int32_t kInt16Min = std::numeric_limits<int16_t>::min();
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void PortableSymmetricQuantizeFloats(const float* values, const int size,
|
||||||
|
int8_t* quantized_values, float* min_value,
|
||||||
|
float* max_value, float* scaling_factor) {
|
||||||
|
auto minmax = std::minmax_element(values, values + size);
|
||||||
|
*min_value = *minmax.first;
|
||||||
|
*max_value = *minmax.second;
|
||||||
|
|
||||||
|
PortableSymmetricQuantizeFloats(values, size, quantized_values, *min_value,
|
||||||
|
*max_value, scaling_factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableSymmetricQuantizeFloats(const float* values, const int size,
|
||||||
|
int8_t* quantized_values, float min_value,
|
||||||
|
float max_value, float* scaling_factor) {
|
||||||
|
const int32_t kScale = 127;
|
||||||
|
const float range = std::max(std::abs(min_value), std::abs(max_value));
|
||||||
|
if (range == 0) {
|
||||||
|
memset(quantized_values, 0, size * sizeof(int8_t));
|
||||||
|
*scaling_factor = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*scaling_factor = range / kScale;
|
||||||
|
const float scaling_factor_inv = kScale / range;
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
const int32_t quantized_value =
|
||||||
|
static_cast<int32_t>(TfLiteRound(values[i] * scaling_factor_inv));
|
||||||
|
// Clamp: just in case some odd numeric offset.
|
||||||
|
quantized_values[i] = static_cast<int8_t>(
|
||||||
|
std::min(kScale, std::max(-kScale, quantized_value)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableAsymmetricQuantizeFloats(const float* values, const int size,
|
||||||
|
int8_t* quantized_values,
|
||||||
|
float* scaling_factor, int32_t* offset) {
|
||||||
|
const int32_t kMinScale = -128;
|
||||||
|
const int32_t kMaxScale = 127;
|
||||||
|
const double qmin_double = kMinScale;
|
||||||
|
const double qmax_double = kMaxScale;
|
||||||
|
const auto minmax = std::minmax_element(values, values + size);
|
||||||
|
const double rmin = std::fmin(0, *minmax.first);
|
||||||
|
const double rmax = std::fmax(0, *minmax.second);
|
||||||
|
if (rmin == rmax) {
|
||||||
|
memset(quantized_values, 0, size * sizeof(int8_t));
|
||||||
|
*scaling_factor = 1;
|
||||||
|
*offset = 0;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
double scale = (rmax - rmin) / (qmax_double - qmin_double);
|
||||||
|
const double zero_point_from_min = qmin_double - rmin / scale;
|
||||||
|
const double zero_point_from_max = qmax_double - rmax / scale;
|
||||||
|
const double zero_point_from_min_error =
|
||||||
|
std::abs(qmin_double) + std::abs(rmin / scale);
|
||||||
|
const double zero_point_from_max_error =
|
||||||
|
std::abs(qmax_double) + std::abs(rmax / scale);
|
||||||
|
const double zero_point_double =
|
||||||
|
zero_point_from_min_error < zero_point_from_max_error
|
||||||
|
? zero_point_from_min
|
||||||
|
: zero_point_from_max;
|
||||||
|
int8_t nudged_zero_point = 0;
|
||||||
|
if (zero_point_double <= qmin_double) {
|
||||||
|
nudged_zero_point = kMinScale;
|
||||||
|
} else if (zero_point_double >= qmax_double) {
|
||||||
|
nudged_zero_point = kMaxScale;
|
||||||
|
} else {
|
||||||
|
nudged_zero_point = static_cast<int8_t>(round(zero_point_double));
|
||||||
|
}
|
||||||
|
*scaling_factor = scale;
|
||||||
|
*offset = nudged_zero_point;
|
||||||
|
}
|
||||||
|
const float scaling_factor_inv = 1.0f / *scaling_factor;
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
const int32_t quantized_value = static_cast<int32_t>(
|
||||||
|
TfLiteRound(*offset + values[i] * scaling_factor_inv));
|
||||||
|
quantized_values[i] =
|
||||||
|
std::min(kMaxScale, std::max(kMinScale, quantized_value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableMatrixBatchVectorMultiplyAccumulate(const float* matrix,
|
||||||
|
int m_rows, int m_cols,
|
||||||
|
const float* vector,
|
||||||
|
int n_batch, float* result) {
|
||||||
|
float* result_in_batch = result;
|
||||||
|
for (int b = 0; b < n_batch; b++) {
|
||||||
|
const float* matrix_ptr = matrix;
|
||||||
|
for (int r = 0; r < m_rows; r++) {
|
||||||
|
float dot_prod = 0.0f;
|
||||||
|
const float* vector_in_batch = vector + b * m_cols;
|
||||||
|
for (int c = 0; c < m_cols; c++) {
|
||||||
|
dot_prod += *matrix_ptr++ * *vector_in_batch++;
|
||||||
|
}
|
||||||
|
*result_in_batch += dot_prod;
|
||||||
|
++result_in_batch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableMatrixBatchVectorMultiplyAccumulate(
|
||||||
|
const int8_t* __restrict__ matrix, const int m_rows, const int m_cols,
|
||||||
|
const int8_t* __restrict__ vectors, const float* scaling_factors,
|
||||||
|
int n_batch, float* __restrict__ result) {
|
||||||
|
for (int batch = 0; batch < n_batch; ++batch, vectors += m_cols) {
|
||||||
|
const float batch_scaling_factor = scaling_factors[batch];
|
||||||
|
// Get the address of the first row.
|
||||||
|
const int8_t* row_ptr = matrix;
|
||||||
|
for (int row = 0; row < m_rows; ++row) {
|
||||||
|
// Initialize the dot product sum for the row to 0.
|
||||||
|
int32_t dotprod = 0;
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
// Prefetch the row to cache.
|
||||||
|
__builtin_prefetch(row_ptr, 0 /* prefetch for read */,
|
||||||
|
3 /* temporal locality */);
|
||||||
|
#endif
|
||||||
|
for (int col = 0; col < m_cols; ++col, ++row_ptr) {
|
||||||
|
dotprod += (*row_ptr) * (vectors[col]);
|
||||||
|
} // for col
|
||||||
|
*result += dotprod * batch_scaling_factor;
|
||||||
|
++result;
|
||||||
|
} // for row
|
||||||
|
} // for batch
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableMatrixBatchVectorMultiplyAccumulate(
|
||||||
|
const int8_t* __restrict__ matrix, const int m_rows, const int m_cols,
|
||||||
|
const int8_t* __restrict__ vectors, const float* scaling_factors,
|
||||||
|
int n_batch, float* __restrict__ result, const float* per_channel_scale,
|
||||||
|
const int32_t* input_offset, int32_t* scratch, int32_t* row_sums,
|
||||||
|
bool* compute_row_sums, CpuBackendContext* context) {
|
||||||
|
if (input_offset == nullptr) {
|
||||||
|
PortableMatrixBatchVectorMultiplyAccumulate(
|
||||||
|
matrix, m_rows, m_cols, vectors, scaling_factors, n_batch, result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!compute_row_sums || *compute_row_sums) {
|
||||||
|
PortableReductionSumVector(matrix, row_sums, m_rows, m_cols);
|
||||||
|
if (compute_row_sums) {
|
||||||
|
*compute_row_sums = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int batch = 0; batch < n_batch; ++batch, vectors += m_cols) {
|
||||||
|
const float batch_scaling_factor = scaling_factors[batch];
|
||||||
|
const int32_t batch_offset = input_offset[batch];
|
||||||
|
const int8_t* row_ptr = matrix;
|
||||||
|
for (int row = 0; row < m_rows; ++row) {
|
||||||
|
int32_t dotprod = 0;
|
||||||
|
float scale = batch_scaling_factor;
|
||||||
|
if (per_channel_scale) {
|
||||||
|
scale *= per_channel_scale[row];
|
||||||
|
}
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
// Prefetch the row to cache.
|
||||||
|
__builtin_prefetch(row_ptr, 0 /* prefetch for read */,
|
||||||
|
3 /* temporal locality */);
|
||||||
|
#endif
|
||||||
|
for (int col = 0; col < m_cols; ++col, ++row_ptr) {
|
||||||
|
dotprod += (*row_ptr) * vectors[col];
|
||||||
|
} // for col
|
||||||
|
dotprod -= row_sums[row] * batch_offset;
|
||||||
|
*result += dotprod * scale;
|
||||||
|
++result;
|
||||||
|
} // for row
|
||||||
|
} // for batch
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableSparseMatrixBatchVectorMultiplyAccumulate1x4(
|
||||||
|
const float* __restrict__ matrix, const int32_t* __restrict__ segments,
|
||||||
|
const int32_t* __restrict__ indices, int m_rows, int m_cols,
|
||||||
|
const float* __restrict__ vector, int n_batch, float* __restrict__ result) {
|
||||||
|
const int kBlockSize = 4;
|
||||||
|
TFLITE_DCHECK_EQ(m_cols % kBlockSize, 0);
|
||||||
|
for (int batch = 0; batch < n_batch; batch++) {
|
||||||
|
const float* matrix_ptr = matrix;
|
||||||
|
for (int row = 0; row < m_rows; row++) {
|
||||||
|
float dot_prod = 0.0f;
|
||||||
|
const float* vector_in_batch = vector + batch * m_cols;
|
||||||
|
for (int i = segments[row]; i < segments[row + 1]; i++) {
|
||||||
|
const int block_start_index = indices[i] * kBlockSize;
|
||||||
|
const float* vector_block_in_batch_ptr =
|
||||||
|
vector_in_batch + block_start_index;
|
||||||
|
for (int c = 0; c < kBlockSize; c++) {
|
||||||
|
dot_prod += *matrix_ptr++ * *vector_block_in_batch_ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result[batch * m_rows + row] += dot_prod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableSparseMatrixBatchVectorMultiplyAccumulate(
|
||||||
|
const float* __restrict__ matrix, const uint8_t* __restrict__ ledger,
|
||||||
|
int m_rows, int m_cols, const float* __restrict__ vector, int n_batch,
|
||||||
|
float* __restrict__ result) {
|
||||||
|
const int kBlockSize = 16;
|
||||||
|
TFLITE_DCHECK_EQ( // NOLINT
|
||||||
|
m_cols % kBlockSize, 0);
|
||||||
|
for (int batch = 0; batch < n_batch; batch++) {
|
||||||
|
const float* matrix_ptr = matrix;
|
||||||
|
const uint8_t* ledger_ptr = ledger;
|
||||||
|
for (int row = 0; row < m_rows; row++) {
|
||||||
|
float dot_prod = 0.0f;
|
||||||
|
int num_nonzero_blocks = *ledger_ptr++;
|
||||||
|
if (num_nonzero_blocks > 0) {
|
||||||
|
const float* vector_in_batch = vector + batch * m_cols;
|
||||||
|
for (int i = 0; i < num_nonzero_blocks; i++) {
|
||||||
|
const int block_start_index = *ledger_ptr++ * kBlockSize;
|
||||||
|
const float* vector_block_in_batch_ptr =
|
||||||
|
vector_in_batch + block_start_index;
|
||||||
|
for (int c = 0; c < kBlockSize; c++) {
|
||||||
|
dot_prod += *matrix_ptr++ * *vector_block_in_batch_ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result[batch * m_rows + row] += dot_prod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableSparseMatrixBatchVectorMultiplyAccumulate(
|
||||||
|
const int8_t* __restrict__ matrix, const uint8_t* ledger, const int m_rows,
|
||||||
|
const int m_cols, const int8_t* __restrict__ vectors,
|
||||||
|
const float* scaling_factors, int n_batch, float* __restrict__ result) {
|
||||||
|
static const int kBlockSize = 16;
|
||||||
|
TFLITE_DCHECK_EQ( // NOLINT
|
||||||
|
m_cols % kBlockSize, 0);
|
||||||
|
for (int batch = 0; batch < n_batch; ++batch, vectors += m_cols) {
|
||||||
|
const float batch_scaling_factor = scaling_factors[batch];
|
||||||
|
const uint8_t* ledger_ptr = ledger;
|
||||||
|
// Get the address of the first row.
|
||||||
|
const int8_t* row_ptr = matrix;
|
||||||
|
for (int row = 0; row < m_rows; ++row) {
|
||||||
|
// Initialize the dot product sum for the row to 0.
|
||||||
|
int32_t dotprod = 0;
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
// Prefetch the row to cache.
|
||||||
|
__builtin_prefetch(row_ptr, 0 /* prefetch for read */,
|
||||||
|
3 /* temporal locality */);
|
||||||
|
#endif
|
||||||
|
int num_nonzero_blocks = *ledger_ptr++;
|
||||||
|
for (int i = 0; i < num_nonzero_blocks; i++) {
|
||||||
|
const int block_start_index = *ledger_ptr++ * kBlockSize;
|
||||||
|
const int8_t* vector_block_ptr = vectors + block_start_index;
|
||||||
|
for (int c = 0; c < kBlockSize; c++) {
|
||||||
|
dotprod += (*row_ptr++) * (*vector_block_ptr++);
|
||||||
|
} // for block
|
||||||
|
} // for num_nonzero_blocks
|
||||||
|
result[batch * m_rows + row] += dotprod * batch_scaling_factor;
|
||||||
|
} // for row
|
||||||
|
} // for batch
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void PortableMatrixBatchVectorMultiplyAccumulateImpl(
|
||||||
|
const int8_t* input, const int32_t* bias,
|
||||||
|
const int8_t* input_to_gate_weights, int32_t multiplier, int32_t shift,
|
||||||
|
int32_t n_batch, int32_t n_input, int32_t n_output, int32_t output_zp,
|
||||||
|
T* output) {
|
||||||
|
const int16_t output_max = std::numeric_limits<T>::max();
|
||||||
|
const int16_t output_min = std::numeric_limits<T>::min();
|
||||||
|
for (int batch = 0; batch < n_batch; ++batch) {
|
||||||
|
for (int row = 0; row < n_output; ++row) {
|
||||||
|
int32_t acc = bias[row];
|
||||||
|
for (int col = 0; col < n_input; ++col) {
|
||||||
|
int8_t input_val = input[batch * n_input + col];
|
||||||
|
int8_t weights_val = input_to_gate_weights[row * n_input + col];
|
||||||
|
acc += input_val * weights_val;
|
||||||
|
}
|
||||||
|
acc = MultiplyByQuantizedMultiplier(acc, multiplier, shift);
|
||||||
|
acc += output_zp;
|
||||||
|
acc += output[batch * n_output + row];
|
||||||
|
if (acc > output_max) {
|
||||||
|
acc = output_max;
|
||||||
|
}
|
||||||
|
if (acc < output_min) {
|
||||||
|
acc = output_min;
|
||||||
|
}
|
||||||
|
output[batch * n_output + row] = static_cast<T>(acc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableMatrixBatchVectorMultiplyAccumulate(
|
||||||
|
const int8_t* input, const int32_t* bias,
|
||||||
|
const int8_t* input_to_gate_weights, int32_t multiplier, int32_t shift,
|
||||||
|
int32_t n_batch, int32_t n_input, int32_t n_output, int32_t output_zp,
|
||||||
|
int32_t* scratch, int16_t* output, CpuBackendContext* context) {
|
||||||
|
PortableMatrixBatchVectorMultiplyAccumulateImpl(
|
||||||
|
input, bias, input_to_gate_weights, multiplier, shift, n_batch, n_input,
|
||||||
|
n_output, output_zp, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableMatrixBatchVectorMultiplyAccumulate(
|
||||||
|
const int8_t* input, const int32_t* bias,
|
||||||
|
const int8_t* input_to_gate_weights, int32_t multiplier, int32_t shift,
|
||||||
|
int32_t n_batch, int32_t n_input, int32_t n_output, int32_t output_zp,
|
||||||
|
int32_t* scratch, int8_t* output, CpuBackendContext* context) {
|
||||||
|
PortableMatrixBatchVectorMultiplyAccumulateImpl(
|
||||||
|
input, bias, input_to_gate_weights, multiplier, shift, n_batch, n_input,
|
||||||
|
n_output, output_zp, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableMatrixBatchVectorMultiply(const int8_t* input,
|
||||||
|
int32_t input_zeropoint,
|
||||||
|
const int8_t* input_to_gate_weights,
|
||||||
|
int32_t input_to_gate_effective_scale_a,
|
||||||
|
int32_t input_to_gate_effective_scale_b,
|
||||||
|
int32_t n_batch, int32_t n_input,
|
||||||
|
int32_t n_cell, int8_t* gate_output,
|
||||||
|
int8_t gate_output_zp) {
|
||||||
|
const int32_t int8_max = std::numeric_limits<int8_t>::max();
|
||||||
|
const int32_t int8_min = std::numeric_limits<int8_t>::min();
|
||||||
|
for (int batch = 0; batch < n_batch; ++batch) {
|
||||||
|
for (int row = 0; row < n_cell; ++row) {
|
||||||
|
int32_t acc = 0;
|
||||||
|
for (int col = 0; col < n_input; ++col) {
|
||||||
|
int32_t input_val = input[batch * n_input + col];
|
||||||
|
int8_t weights_val = input_to_gate_weights[row * n_input + col];
|
||||||
|
acc += (input_val - input_zeropoint) * weights_val;
|
||||||
|
}
|
||||||
|
acc = MultiplyByQuantizedMultiplier(acc, input_to_gate_effective_scale_a,
|
||||||
|
input_to_gate_effective_scale_b);
|
||||||
|
acc += gate_output_zp;
|
||||||
|
if (acc > int8_max) {
|
||||||
|
acc = int8_max;
|
||||||
|
}
|
||||||
|
if (acc < int8_min) {
|
||||||
|
acc = int8_min;
|
||||||
|
}
|
||||||
|
gate_output[batch * n_cell + row] = static_cast<int8_t>(acc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableMatrixBatchVectorMultiply(
|
||||||
|
const int16_t* hidden, const int8_t* hidden_to_output_weights,
|
||||||
|
int32_t proj_effective_scale_a, int32_t proj_effective_scale_b,
|
||||||
|
const int32_t* gate_bias, int32_t n_batch, int32_t n_hidden,
|
||||||
|
int32_t n_output, int32_t output_zp, int8_t* proj_output) {
|
||||||
|
const int16_t int8_max = std::numeric_limits<int8_t>::max();
|
||||||
|
const int16_t int8_min = std::numeric_limits<int8_t>::min();
|
||||||
|
for (int batch = 0; batch < n_batch; ++batch) {
|
||||||
|
for (int row = 0; row < n_output; ++row) {
|
||||||
|
int64_t acc = gate_bias[row];
|
||||||
|
for (int col = 0; col < n_hidden; ++col) {
|
||||||
|
int16_t input_val = hidden[batch * n_hidden + col];
|
||||||
|
int8_t weights_val = hidden_to_output_weights[row * n_hidden + col];
|
||||||
|
int64_t curr = acc;
|
||||||
|
acc += input_val * weights_val;
|
||||||
|
if (input_val * weights_val > 0 && acc < curr) {
|
||||||
|
acc = std::numeric_limits<int32_t>::max();
|
||||||
|
}
|
||||||
|
if (input_val * weights_val < 0 && acc > curr) {
|
||||||
|
acc = std::numeric_limits<int32_t>::min();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
acc = MultiplyByQuantizedMultiplier(acc, proj_effective_scale_a,
|
||||||
|
proj_effective_scale_b);
|
||||||
|
acc += output_zp;
|
||||||
|
if (acc > int8_max) {
|
||||||
|
acc = int8_max;
|
||||||
|
}
|
||||||
|
if (acc < int8_min) {
|
||||||
|
acc = int8_min;
|
||||||
|
}
|
||||||
|
proj_output[batch * n_output + row] = acc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableApplyLayerNorm(const int16_t* input,
|
||||||
|
const int16_t* layer_norm_weights,
|
||||||
|
const int32_t* bias, int32_t layer_norm_scale_a,
|
||||||
|
int32_t layer_norm_scale_b, int32_t variance_limit,
|
||||||
|
int n_batch, int n_input, int16_t* output) {
|
||||||
|
// The square of std::pow(2, 10), which is the extra factor that makes sure
|
||||||
|
// normalized values has enough resolution.
|
||||||
|
static const int kTwoToPower20 = 1 << 20;
|
||||||
|
for (int i = 0; i < n_batch; ++i) {
|
||||||
|
int64_t sum = 0;
|
||||||
|
int64_t sum_sq = 0;
|
||||||
|
for (int j = 0; j < n_input; ++j) {
|
||||||
|
const int32_t index = i * n_input + j;
|
||||||
|
int32_t val = static_cast<int32_t>(input[index]);
|
||||||
|
sum += val;
|
||||||
|
sum_sq += val * val;
|
||||||
|
}
|
||||||
|
int32_t mean =
|
||||||
|
static_cast<int32_t>(static_cast<int64_t>(sum) * 1024 / n_input);
|
||||||
|
// TODO(b/173994730): Avoids overflow but only works for POT n_input.
|
||||||
|
int32_t temp = kTwoToPower20 / n_input;
|
||||||
|
int64_t variance =
|
||||||
|
sum_sq * temp - static_cast<int64_t>(mean) * static_cast<int64_t>(mean);
|
||||||
|
int32_t variance2 = static_cast<int32_t>(variance / kTwoToPower20);
|
||||||
|
if (variance2 < 1) {
|
||||||
|
variance2 = variance_limit;
|
||||||
|
}
|
||||||
|
int32_t stddev_inverse_a;
|
||||||
|
int stddev_inverse_b;
|
||||||
|
GetInvSqrtQuantizedMultiplierExp(variance2, /*reverse_shift*/ -1,
|
||||||
|
&stddev_inverse_a, &stddev_inverse_b);
|
||||||
|
|
||||||
|
for (int j = 0; j < n_input; ++j) {
|
||||||
|
const int32_t index = i * n_input + j;
|
||||||
|
int32_t val = static_cast<int32_t>(input[index]);
|
||||||
|
int32_t shifted = 1024 * val - mean;
|
||||||
|
int32_t rescaled = MultiplyByQuantizedMultiplier(
|
||||||
|
shifted, stddev_inverse_a, stddev_inverse_b);
|
||||||
|
// TODO(jianlijianli): Saturate this.
|
||||||
|
int64_t val3 = rescaled * layer_norm_weights[j] + bias[j];
|
||||||
|
int32_t val4 =
|
||||||
|
static_cast<int32_t>((val3 > 0 ? val3 + 512 : val3 - 512) / 1024);
|
||||||
|
int32_t val5 = MultiplyByQuantizedMultiplier(val4, layer_norm_scale_a,
|
||||||
|
layer_norm_scale_b + 12);
|
||||||
|
val5 = std::min(std::max(kInt16Min, val5), kInt16Max);
|
||||||
|
output[index] = static_cast<int16_t>(val5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableApplyLayerNormFloat(const int16_t* input,
|
||||||
|
const int16_t* layer_norm_weights,
|
||||||
|
int32_t layer_norm_scale_a,
|
||||||
|
int32_t layer_norm_scale_b,
|
||||||
|
const int32_t* bias, int n_batch, int n_input,
|
||||||
|
int16_t* output) {
|
||||||
|
const int32_t int16_max = std::numeric_limits<int16_t>::max();
|
||||||
|
const int32_t int16_min = std::numeric_limits<int16_t>::min();
|
||||||
|
const float layer_norm_scale =
|
||||||
|
layer_norm_scale_a *
|
||||||
|
std::pow(2.0, static_cast<double>(layer_norm_scale_b - 31));
|
||||||
|
const float bias_scale =
|
||||||
|
static_cast<float>(std::pow(2.0, -10)) * layer_norm_scale;
|
||||||
|
|
||||||
|
for (int batch = 0; batch < n_batch; ++batch) {
|
||||||
|
float sum = 0.0f;
|
||||||
|
float sum_sq = 0.0f;
|
||||||
|
for (int i = 0; i < n_input; ++i) {
|
||||||
|
const int index = batch * n_input + i;
|
||||||
|
const float value = static_cast<float>(input[index]);
|
||||||
|
sum += value;
|
||||||
|
sum_sq += value * value;
|
||||||
|
}
|
||||||
|
const float mean = sum / n_input;
|
||||||
|
float stddev_inv = 0.0f;
|
||||||
|
const float variance = sum_sq / n_input - mean * mean;
|
||||||
|
if (variance == 0) {
|
||||||
|
stddev_inv = 1.0f / std::sqrt(1e-8f);
|
||||||
|
} else {
|
||||||
|
stddev_inv = 1.0f / std::sqrt(variance);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < n_input; ++i) {
|
||||||
|
const int index = batch * n_input + i;
|
||||||
|
const float normalized_value =
|
||||||
|
(static_cast<float>(input[index]) - mean) * stddev_inv;
|
||||||
|
const float weighted_normalized_value =
|
||||||
|
normalized_value * layer_norm_weights[i] * layer_norm_scale +
|
||||||
|
bias[i] * bias_scale;
|
||||||
|
const int32_t quant_output = static_cast<int32_t>(std::round(
|
||||||
|
weighted_normalized_value * static_cast<float>(std::pow(2, 12))));
|
||||||
|
output[index] = std::min(int16_max, std::max(int16_min, quant_output));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableMatrixScalarMultiplyAccumulate(const int8_t* matrix,
|
||||||
|
int32_t scalar, int32_t n_row,
|
||||||
|
int32_t n_col, int32_t* output) {
|
||||||
|
for (int i = 0; i < n_row; ++i) {
|
||||||
|
int32_t row_sum = 0;
|
||||||
|
for (int j = 0; j < n_col; ++j) {
|
||||||
|
row_sum += *matrix++;
|
||||||
|
}
|
||||||
|
output[i] += row_sum * scalar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableApplySigmoid(const int16_t* input, int32_t n_batch,
|
||||||
|
int32_t n_input, int16_t* output) {
|
||||||
|
for (int batch = 0; batch < n_batch; ++batch) {
|
||||||
|
for (int c = 0; c < n_input; c++) {
|
||||||
|
using F3 = gemmlowp::FixedPoint<std::int16_t, 3>;
|
||||||
|
using F0 = gemmlowp::FixedPoint<std::int16_t, 0>;
|
||||||
|
const int index = batch * n_input + c;
|
||||||
|
F3 sigmoid_input = F3::FromRaw(input[index]);
|
||||||
|
F0 sigmoid_output = gemmlowp::logistic(sigmoid_input);
|
||||||
|
output[index] = sigmoid_output.raw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableApplySigmoidFloat(const int16_t* input, int32_t n_batch,
|
||||||
|
int32_t n_input, int16_t* output) {
|
||||||
|
const int32_t int16_max = std::numeric_limits<int16_t>::max();
|
||||||
|
const int32_t int16_min = std::numeric_limits<int16_t>::min();
|
||||||
|
for (int batch = 0; batch < n_batch; ++batch) {
|
||||||
|
for (int i = 0; i < n_input; ++i) {
|
||||||
|
const int index = batch * n_input + i;
|
||||||
|
const float float_input =
|
||||||
|
input[index] * static_cast<float>(std::pow(2, -12));
|
||||||
|
const float float_output = 1.0f / (1.0f + std::exp(-float_input));
|
||||||
|
const int32_t quant_output = static_cast<int32_t>(
|
||||||
|
float_output * static_cast<float>(std::pow(2, 15)));
|
||||||
|
const int32_t quant_output_clamped =
|
||||||
|
std::min(int16_max, std::max(int16_min, quant_output));
|
||||||
|
output[index] = static_cast<int16_t>(quant_output_clamped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int IntegerBits>
|
||||||
|
void PortableApplyTanhImpl(const int16_t* input, int32_t n_batch,
|
||||||
|
int32_t n_input, int16_t* output) {
|
||||||
|
using FX = gemmlowp::FixedPoint<std::int16_t, IntegerBits>;
|
||||||
|
using F0 = gemmlowp::FixedPoint<std::int16_t, 0>;
|
||||||
|
for (int batch = 0; batch < n_batch; ++batch) {
|
||||||
|
for (int i = 0; i < n_input; ++i) {
|
||||||
|
const int index = batch * n_input + i;
|
||||||
|
FX tanh_input = FX::FromRaw(input[index]);
|
||||||
|
F0 tanh_output = gemmlowp::tanh(tanh_input);
|
||||||
|
output[index] = tanh_output.raw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableApplyTanh(int32_t integer_bits, const int16_t* input,
|
||||||
|
int32_t n_batch, int32_t n_input, int16_t* output) {
|
||||||
|
assert(integer_bits <= 6);
|
||||||
|
#define DISPATCH_TANH(i) \
|
||||||
|
case i: \
|
||||||
|
PortableApplyTanhImpl<i>(input, n_batch, n_input, output); \
|
||||||
|
break;
|
||||||
|
switch (integer_bits) {
|
||||||
|
DISPATCH_TANH(0);
|
||||||
|
DISPATCH_TANH(1);
|
||||||
|
DISPATCH_TANH(2);
|
||||||
|
DISPATCH_TANH(3);
|
||||||
|
DISPATCH_TANH(4);
|
||||||
|
DISPATCH_TANH(5);
|
||||||
|
DISPATCH_TANH(6);
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#undef DISPATCH_TANH
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableApplyTanhFloat(const int16_t* input, int32_t n_batch,
|
||||||
|
int32_t n_input, int32_t integer_bits,
|
||||||
|
int16_t* output) {
|
||||||
|
const int32_t int16_max = std::numeric_limits<int16_t>::max();
|
||||||
|
const int32_t int16_min = std::numeric_limits<int16_t>::min();
|
||||||
|
const double two = 2.0;
|
||||||
|
for (int batch = 0; batch < n_batch; ++batch) {
|
||||||
|
for (int i = 0; i < n_input; ++i) {
|
||||||
|
const int index = batch * n_input + i;
|
||||||
|
const float float_input =
|
||||||
|
input[index] * std::pow(two, static_cast<double>(integer_bits));
|
||||||
|
const float float_output = std::tanh(float_input);
|
||||||
|
const int32_t quant_output = static_cast<int32_t>(
|
||||||
|
float_output * static_cast<float>(std::pow(2, 15)));
|
||||||
|
const int32_t quant_output_clamped =
|
||||||
|
std::min(int16_max, std::max(int16_min, quant_output));
|
||||||
|
output[index] = static_cast<int16_t>(quant_output_clamped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableCwiseMul(const int16_t* input_1, const int16_t* input_2,
|
||||||
|
int n_batch, int n_input, int shift, int16_t* output) {
|
||||||
|
for (int batch = 0; batch < n_batch; ++batch) {
|
||||||
|
for (int i = 0; i < n_input; ++i) {
|
||||||
|
const int index = batch * n_input + i;
|
||||||
|
const int16_t a = input_1[index];
|
||||||
|
const int16_t b = input_2[index];
|
||||||
|
const int32_t value = static_cast<int32_t>(a) * static_cast<int32_t>(b);
|
||||||
|
output[index] =
|
||||||
|
static_cast<int16_t>(gemmlowp::RoundingDivideByPOT(value, shift));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableCwiseMul(const int16_t* input_1, const int16_t* input_2,
|
||||||
|
int32_t multiplier, int32_t shift, int32_t n_batch,
|
||||||
|
int32_t n_input, int32_t output_zp, int8_t* output) {
|
||||||
|
for (int batch = 0; batch < n_batch; ++batch) {
|
||||||
|
for (int i = 0; i < n_input; ++i) {
|
||||||
|
const int index = batch * n_input + i;
|
||||||
|
const int16_t a = input_1[index];
|
||||||
|
const int16_t b = input_2[index];
|
||||||
|
int32_t value = static_cast<int32_t>(a) * static_cast<int32_t>(b);
|
||||||
|
value = MultiplyByQuantizedMultiplier(value, multiplier, shift);
|
||||||
|
value -= output_zp;
|
||||||
|
value = std::min(std::max(static_cast<int32_t>(-128), value),
|
||||||
|
static_cast<int32_t>(127));
|
||||||
|
|
||||||
|
output[index] = static_cast<int8_t>(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableCwiseAdd(const int16_t* input_1, const int16_t* input_2,
|
||||||
|
int n_batch, int n_input, int16_t* output) {
|
||||||
|
for (int batch = 0; batch < n_batch; ++batch) {
|
||||||
|
for (int i = 0; i < n_input; ++i) {
|
||||||
|
const int index = batch * n_input + i;
|
||||||
|
int32_t sum = input_1[index] + input_2[index];
|
||||||
|
const int32_t sum_clamped = std::min(kInt16Max, std::max(kInt16Min, sum));
|
||||||
|
output[index] = static_cast<int16_t>(sum_clamped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float PortableVectorVectorDotProduct(const float* vector1, const float* vector2,
|
||||||
|
int v_size) {
|
||||||
|
float result = 0.0;
|
||||||
|
for (int v = 0; v < v_size; v++) {
|
||||||
|
result += *vector1++ * *vector2++;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
inline int32_t VectorVectorDotProduct(const int16_t* vector1,
|
||||||
|
const int16_t* vector2, int v_size) {
|
||||||
|
int32_t result = 0;
|
||||||
|
for (int v = 0; v < v_size; v++) {
|
||||||
|
result += *vector1++ * *vector2++;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void PortableBatchVectorBatchVectorDotProduct(const int16_t* vector1,
|
||||||
|
const int16_t* vector2,
|
||||||
|
int v_size, int n_batch,
|
||||||
|
int32_t* result) {
|
||||||
|
for (int b = 0; b < n_batch; b++) {
|
||||||
|
result[b] = VectorVectorDotProduct(vector1, vector2, v_size);
|
||||||
|
vector1 += v_size;
|
||||||
|
vector2 += v_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableVectorBatchVectorCwiseProductAccumulate(
|
||||||
|
const int16_t* vector, int v_size, const int16_t* batch_vector, int n_batch,
|
||||||
|
int32_t multiplier, int shift, int16_t* result) {
|
||||||
|
for (int b = 0; b < n_batch; b++) {
|
||||||
|
for (int v = 0; v < v_size; v++) {
|
||||||
|
int32_t prod = vector[v] * *batch_vector++;
|
||||||
|
prod = MultiplyByQuantizedMultiplier(prod, multiplier, shift);
|
||||||
|
int32_t output = prod + *result;
|
||||||
|
output = std::max(std::min(static_cast<int32_t>(32767), output),
|
||||||
|
static_cast<int32_t>(-32768));
|
||||||
|
*result++ = output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableSub1Vector(const float* vector, int v_size, float* result) {
|
||||||
|
for (int v = 0; v < v_size; v++) {
|
||||||
|
*result++ = 1.0f - *vector++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableSub1Vector(const int16_t* vector, int v_size, int16_t* result) {
|
||||||
|
static const int16_t kOne = 32767;
|
||||||
|
for (int v = 0; v < v_size; v++) {
|
||||||
|
*result++ = kOne - *vector++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableVectorScalarMultiply(const int8_t* vector, const int v_size,
|
||||||
|
const float scale, float* result) {
|
||||||
|
for (int v = 0; v < v_size; ++v) {
|
||||||
|
*result++ = scale * *vector++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableMeanStddevNormalization(const float* __restrict__ input_vector,
|
||||||
|
float* __restrict__ output_vector,
|
||||||
|
int v_size, int n_batch) {
|
||||||
|
for (int batch = 0; batch < n_batch; ++batch) {
|
||||||
|
float sum = 0.0f;
|
||||||
|
for (int i = 0; i < v_size; ++i) {
|
||||||
|
sum += input_vector[i];
|
||||||
|
}
|
||||||
|
const float mean = sum / v_size;
|
||||||
|
float sum_diff_sq = 0.0f;
|
||||||
|
for (int i = 0; i < v_size; ++i) {
|
||||||
|
const float diff = input_vector[i] - mean;
|
||||||
|
sum_diff_sq += diff * diff;
|
||||||
|
}
|
||||||
|
const float variance = sum_diff_sq / v_size;
|
||||||
|
constexpr float kNormalizationConstant = 1e-8f;
|
||||||
|
const float stddev_inv =
|
||||||
|
1.0f / std::sqrt(variance + kNormalizationConstant);
|
||||||
|
for (int i = 0; i < v_size; ++i) {
|
||||||
|
output_vector[i] = (input_vector[i] - mean) * stddev_inv;
|
||||||
|
}
|
||||||
|
input_vector += v_size;
|
||||||
|
output_vector += v_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableTwoGateSaturatingAdd(const int8_t* input, int8_t input_zp,
|
||||||
|
const int8_t* recurrent, int8_t recurrent_zp,
|
||||||
|
int32_t input_effective_scale_a,
|
||||||
|
int32_t input_effective_scale_b,
|
||||||
|
int32_t recurrent_effective_scale_a,
|
||||||
|
int32_t recurrent_effective_scale_b,
|
||||||
|
int32_t n_batch, int32_t n_cell,
|
||||||
|
int16_t* output) {
|
||||||
|
const int32_t int16_max = std::numeric_limits<int16_t>::max();
|
||||||
|
const int32_t int16_min = std::numeric_limits<int16_t>::min();
|
||||||
|
for (int i = 0; i < n_batch * n_cell; ++i) {
|
||||||
|
int32_t x = static_cast<int32_t>(input[i]) - static_cast<int32_t>(input_zp);
|
||||||
|
int32_t h =
|
||||||
|
static_cast<int32_t>(recurrent[i]) - static_cast<int32_t>(recurrent_zp);
|
||||||
|
int32_t x_scaled = MultiplyByQuantizedMultiplier(x, input_effective_scale_a,
|
||||||
|
input_effective_scale_b);
|
||||||
|
int32_t h_scaled = MultiplyByQuantizedMultiplier(
|
||||||
|
h, recurrent_effective_scale_a, recurrent_effective_scale_b);
|
||||||
|
int32_t y = h_scaled + x_scaled;
|
||||||
|
if (y > int16_max) {
|
||||||
|
y = int16_max;
|
||||||
|
}
|
||||||
|
if (y < int16_min) {
|
||||||
|
y = int16_min;
|
||||||
|
}
|
||||||
|
output[i] = static_cast<int16_t>(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tensor_utils
|
||||||
|
} // namespace tflite
|
||||||
@@ -0,0 +1,235 @@
|
|||||||
|
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==============================================================================*/
|
||||||
|
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_PORTABLE_TENSOR_UTILS_IMPL_H_
|
||||||
|
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_PORTABLE_TENSOR_UTILS_IMPL_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#define __restrict__ __restrict
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace tflite {
|
||||||
|
|
||||||
|
// Not all backends support CpuBackendContext usage, so forward declare to avoid
|
||||||
|
// pulling in its implementation.
|
||||||
|
class CpuBackendContext;
|
||||||
|
|
||||||
|
namespace tensor_utils {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool PortableIsZeroVector(const T* vector, int v_size) {
|
||||||
|
for (int i = 0; i < v_size; ++i) {
|
||||||
|
if (vector[i] != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortableSymmetricQuantizeFloats(const float* values, const int size,
|
||||||
|
int8_t* quantized_values, float* min_value,
|
||||||
|
float* max_value, float* scaling_factor);
|
||||||
|
|
||||||
|
void PortableSymmetricQuantizeFloats(const float* values, const int size,
|
||||||
|
int8_t* quantized_values, float min_value,
|
||||||
|
float max_value, float* scaling_factor);
|
||||||
|
|
||||||
|
void PortableAsymmetricQuantizeFloats(const float* values, const int size,
|
||||||
|
int8_t* quantized_values,
|
||||||
|
float* scaling_factor, int32_t* offset);
|
||||||
|
|
||||||
|
// Multiply a matrix by a batch vector, and store results in a batch-size
|
||||||
|
// vector.
|
||||||
|
void PortableMatrixBatchVectorMultiplyAccumulate(const float* matrix,
|
||||||
|
int m_rows, int m_cols,
|
||||||
|
const float* vector,
|
||||||
|
int n_batch, float* result);
|
||||||
|
|
||||||
|
void PortableMatrixBatchVectorMultiplyAccumulate(
|
||||||
|
const int8_t* __restrict__ matrix, const int m_rows, const int m_cols,
|
||||||
|
const int8_t* __restrict__ vectors, const float* scaling_factors,
|
||||||
|
int n_batch, float* __restrict__ result);
|
||||||
|
|
||||||
|
void PortableMatrixBatchVectorMultiplyAccumulate(
|
||||||
|
const int8_t* __restrict__ matrix, const int m_rows, const int m_cols,
|
||||||
|
const int8_t* __restrict__ vectors, const float* scaling_factors,
|
||||||
|
int n_batch, float* __restrict__ result, const float* per_channel_scale,
|
||||||
|
const int32_t* input_offset, int32_t* scratch, int32_t* row_sums,
|
||||||
|
bool* compute_row_sums, CpuBackendContext* context);
|
||||||
|
|
||||||
|
void PortableMatrixBatchVectorMultiplyAccumulate(
|
||||||
|
const int8_t* __restrict__ matrix, const int m_rows, const int m_cols,
|
||||||
|
const int8_t* __restrict__ vector, const float* scaling_factors,
|
||||||
|
int n_batch, int32_t* scratch, float* __restrict__ result,
|
||||||
|
CpuBackendContext* context);
|
||||||
|
|
||||||
|
void PortableSparseMatrixBatchVectorMultiplyAccumulate1x4(
|
||||||
|
const float* __restrict__ matrix, const int32_t* __restrict__ segments,
|
||||||
|
const int32_t* __restrict__ indices, int m_rows, int m_cols,
|
||||||
|
const float* __restrict__ vector, int n_batch, float* __restrict__ result);
|
||||||
|
|
||||||
|
void PortableSparseMatrixBatchVectorMultiplyAccumulate(
|
||||||
|
const float* __restrict__ matrix, const uint8_t* __restrict__ ledger,
|
||||||
|
int m_rows, int m_cols, const float* __restrict__ vector, int n_batch,
|
||||||
|
float* __restrict__ result);
|
||||||
|
|
||||||
|
void PortableSparseMatrixBatchVectorMultiplyAccumulate(
|
||||||
|
const int8_t* __restrict__ matrix, const uint8_t* ledger, const int m_rows,
|
||||||
|
const int m_cols, const int8_t* __restrict__ vectors,
|
||||||
|
const float* scaling_factors, int n_batch, float* __restrict__ result);
|
||||||
|
|
||||||
|
// Dot product of two vectors.
|
||||||
|
float PortableVectorVectorDotProduct(const float* vector1, const float* vector2,
|
||||||
|
int v_size);
|
||||||
|
|
||||||
|
void PortableBatchVectorBatchVectorDotProduct(const int16_t* vector1,
|
||||||
|
const int16_t* vector2,
|
||||||
|
int v_size, int n_batch,
|
||||||
|
int32_t* result);
|
||||||
|
|
||||||
|
void PortableVectorBatchVectorCwiseProductAccumulate(
|
||||||
|
const int16_t* vector, int v_size, const int16_t* batch_vector, int n_batch,
|
||||||
|
int32_t multiplier, int shift, int16_t* result);
|
||||||
|
|
||||||
|
void PortableMatrixBatchVectorMultiplyAccumulate(
|
||||||
|
const int8_t* input, const int32_t* bias,
|
||||||
|
const int8_t* input_to_gate_weights, int32_t multiplier, int32_t shift,
|
||||||
|
int32_t n_batch, int32_t n_input, int32_t n_output, int32_t output_zp,
|
||||||
|
int32_t* scratch, int16_t* output, CpuBackendContext* context);
|
||||||
|
|
||||||
|
void PortableMatrixBatchVectorMultiplyAccumulate(
|
||||||
|
const int8_t* input, const int32_t* bias,
|
||||||
|
const int8_t* input_to_gate_weights, int32_t multiplier, int32_t shift,
|
||||||
|
int32_t n_batch, int32_t n_input, int32_t n_output, int32_t output_zp,
|
||||||
|
int32_t* scratch, int8_t* output, CpuBackendContext* context);
|
||||||
|
|
||||||
|
void PortableMatrixBatchVectorMultiply(const int8_t* input,
|
||||||
|
int32_t input_zeropoint,
|
||||||
|
const int8_t* input_to_gate_weights,
|
||||||
|
int32_t input_to_gate_effective_scale_a,
|
||||||
|
int32_t input_to_gate_effective_scale_b,
|
||||||
|
int32_t n_batch, int32_t n_input,
|
||||||
|
int32_t n_cell, int8_t* gate_output,
|
||||||
|
int8_t gate_output_zp);
|
||||||
|
|
||||||
|
void PortableMatrixBatchVectorMultiply(
|
||||||
|
const int16_t* hidden, const int8_t* hidden_to_output_weights,
|
||||||
|
int32_t proj_effective_scale_a, int32_t proj_effective_scale_b,
|
||||||
|
const int32_t* gate_bias, int32_t n_batch, int32_t n_hidden,
|
||||||
|
int32_t n_output, int32_t output_zp, int8_t* proj_output);
|
||||||
|
|
||||||
|
void PortableMatrixScalarMultiplyAccumulate(const int8_t* matrix,
|
||||||
|
int32_t scalar, int32_t n_row,
|
||||||
|
int32_t n_col, int32_t* output);
|
||||||
|
|
||||||
|
void PortableApplyLayerNorm(const int16_t* input,
|
||||||
|
const int16_t* layer_norm_weights,
|
||||||
|
const int32_t* bias, int32_t layer_norm_scale_a,
|
||||||
|
int32_t layer_norm_scale_b, int32_t variance_limit,
|
||||||
|
int n_batch, int n_input, int16_t* output);
|
||||||
|
|
||||||
|
void PortableApplyLayerNormFloat(const int16_t* input,
|
||||||
|
const int16_t* layer_norm_weights,
|
||||||
|
int32_t layer_norm_scale_a,
|
||||||
|
int32_t layer_norm_scale_b,
|
||||||
|
const int32_t* bias, int n_batch, int n_input,
|
||||||
|
int16_t* output);
|
||||||
|
|
||||||
|
void PortableApplySigmoid(const int16_t* input, int32_t n_batch,
|
||||||
|
int32_t n_input, int16_t* output);
|
||||||
|
|
||||||
|
void PortableApplySigmoidFloat(const int16_t* input, int32_t n_batch,
|
||||||
|
int32_t n_input, int16_t* output);
|
||||||
|
|
||||||
|
void PortableApplyTanh(int32_t integer_bits, const int16_t* input,
|
||||||
|
int32_t n_batch, int32_t n_input, int16_t* output);
|
||||||
|
|
||||||
|
void PortableApplyTanhFloat(const int16_t* input, int32_t n_batch,
|
||||||
|
int32_t n_input, int32_t integer_bits,
|
||||||
|
int16_t* output);
|
||||||
|
|
||||||
|
void PortableCwiseMul(const int16_t* input_1, const int16_t* input_2,
|
||||||
|
int n_batch, int n_input, int shift, int16_t* output);
|
||||||
|
|
||||||
|
void PortableCwiseMul(const int16_t* input_1, const int16_t* input_2,
|
||||||
|
int32_t multiplier, int32_t shift, int32_t n_batch,
|
||||||
|
int32_t n_input, int32_t output_zp, int8_t* output);
|
||||||
|
|
||||||
|
void PortableCwiseAdd(const int16_t* input_1, const int16_t* input_2,
|
||||||
|
int n_batch, int n_input, int16_t* output);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void PortableCwiseClipping(T* vector, const int v_size,
|
||||||
|
const T& clipping_value) {
|
||||||
|
for (int i = 0; i < v_size; i++) {
|
||||||
|
vector[i] = std::max(std::min(clipping_value, vector[i]),
|
||||||
|
static_cast<T>(-clipping_value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch vector initialization with another vector.
|
||||||
|
void PortableVectorBatchVectorAssign(const float* vector, int v_size,
|
||||||
|
int n_batch, float* batch_vector);
|
||||||
|
|
||||||
|
// Compute "1.0f - elements of vector" (used in CIFG).
|
||||||
|
void PortableSub1Vector(const float* vector, int v_size, float* result);
|
||||||
|
|
||||||
|
void PortableSub1Vector(const int16_t* vector, int v_size, int16_t* result);
|
||||||
|
|
||||||
|
// Multiply all elements of vector with a scalar.
|
||||||
|
void PortableVectorScalarMultiply(const int8_t* vector, int v_size, float scale,
|
||||||
|
float* result);
|
||||||
|
|
||||||
|
// Reduce-sum on a vector:
|
||||||
|
// input_vector: pointer to input vector.
|
||||||
|
// output_vector: pointer to vector.
|
||||||
|
// output_size: output vector size.
|
||||||
|
// reduction_size: number of consecutive elements from input vector which are
|
||||||
|
// added to get one element of output.
|
||||||
|
template <typename INPUT, typename OUTPUT>
|
||||||
|
void PortableReductionSumVector(const INPUT* input_vector,
|
||||||
|
OUTPUT* output_vector, int output_size,
|
||||||
|
int reduction_size) {
|
||||||
|
for (int o = 0; o < output_size; o++) {
|
||||||
|
OUTPUT result = 0;
|
||||||
|
for (int r = 0; r < reduction_size; r++) {
|
||||||
|
result += input_vector[r];
|
||||||
|
}
|
||||||
|
output_vector[o] = result;
|
||||||
|
input_vector += reduction_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Layer norm for each batch.
|
||||||
|
void PortableMeanStddevNormalization(const float* __restrict__ input_vector,
|
||||||
|
float* __restrict__ output_vector,
|
||||||
|
int v_size, int n_batch);
|
||||||
|
|
||||||
|
// Saturate Add.
|
||||||
|
void PortableTwoGateSaturatingAdd(const int8_t* input, int8_t input_zp,
|
||||||
|
const int8_t* recurrent, int8_t recurrent_zp,
|
||||||
|
int32_t input_effective_scale_a,
|
||||||
|
int32_t input_effective_scale_b,
|
||||||
|
int32_t recurrent_effective_scale_a,
|
||||||
|
int32_t recurrent_effective_scale_b,
|
||||||
|
int32_t n_batch, int32_t n_cell,
|
||||||
|
int16_t* output);
|
||||||
|
|
||||||
|
} // namespace tensor_utils
|
||||||
|
} // namespace tflite
|
||||||
|
|
||||||
|
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_PORTABLE_TENSOR_UTILS_IMPL_H_
|
||||||
@@ -23,6 +23,25 @@ limitations under the License.
|
|||||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||||
#include "tensorflow/lite/kernels/internal/types.h"
|
#include "tensorflow/lite/kernels/internal/types.h"
|
||||||
|
|
||||||
|
// Check if the reduction at index is the first one along the dimensions given
|
||||||
|
// in axis.
|
||||||
|
inline bool IsFirstReduction(const int* index, const int num_axis,
|
||||||
|
const int* axis) {
|
||||||
|
if (num_axis == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TFLITE_DCHECK(index != nullptr);
|
||||||
|
TFLITE_DCHECK(axis != nullptr);
|
||||||
|
for (int axis_idx = 0; axis_idx < num_axis; ++axis_idx) {
|
||||||
|
if (index[axis[axis_idx]] != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
namespace tflite {
|
namespace tflite {
|
||||||
|
|
||||||
namespace reference_ops {
|
namespace reference_ops {
|
||||||
@@ -35,8 +54,7 @@ inline bool Reduce(const In* input_data, const int* input_dims,
|
|||||||
const int* output_dims, const int input_num_dims,
|
const int* output_dims, const int input_num_dims,
|
||||||
const int output_num_dims, const int* axis,
|
const int output_num_dims, const int* axis,
|
||||||
const int num_axis, int* input_iter,
|
const int num_axis, int* input_iter,
|
||||||
Out reducer(const Out current, const In in),
|
Out reducer(Out current, const In in), Out* output_data) {
|
||||||
Out* output_data) {
|
|
||||||
// Reset input iterator.
|
// Reset input iterator.
|
||||||
for (int idx = 0; idx < input_num_dims; ++idx) {
|
for (int idx = 0; idx < input_num_dims; ++idx) {
|
||||||
input_iter[idx] = 0;
|
input_iter[idx] = 0;
|
||||||
@@ -53,6 +71,37 @@ inline bool Reduce(const In* input_data, const int* input_dims,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Similar to above Reduce function but takes two reducer functions.
|
||||||
|
// The 'reducer_first' is called with the first value of the reduction,
|
||||||
|
// 'reducer_next' is then called for all the others.
|
||||||
|
template <typename In, typename Out>
|
||||||
|
inline bool Reduce(const In* input_data, const int* input_dims,
|
||||||
|
const int* output_dims, const int input_num_dims,
|
||||||
|
const int output_num_dims, const int* axis,
|
||||||
|
const int num_axis, int* input_iter,
|
||||||
|
const std::function<Out(In in)>& reducer_first,
|
||||||
|
const std::function<Out(Out current, In in)>& reducer_next,
|
||||||
|
Out* output_data) {
|
||||||
|
// Reset input iterator.
|
||||||
|
for (int idx = 0; idx < input_num_dims; ++idx) {
|
||||||
|
input_iter[idx] = 0;
|
||||||
|
}
|
||||||
|
// Iterate through input_data.
|
||||||
|
do {
|
||||||
|
size_t input_offset =
|
||||||
|
ReducedOutputOffset(input_num_dims, input_dims, input_iter, 0, nullptr);
|
||||||
|
size_t output_offset = ReducedOutputOffset(input_num_dims, input_dims,
|
||||||
|
input_iter, num_axis, axis);
|
||||||
|
if (IsFirstReduction(input_iter, num_axis, axis)) {
|
||||||
|
output_data[output_offset] = reducer_first(input_data[input_offset]);
|
||||||
|
} else {
|
||||||
|
output_data[output_offset] =
|
||||||
|
reducer_next(output_data[output_offset], input_data[input_offset]);
|
||||||
|
}
|
||||||
|
} while (NextIndex(input_num_dims, input_dims, input_iter));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// This method parses the input 'axis' to remove duplicates and handle negative
|
// This method parses the input 'axis' to remove duplicates and handle negative
|
||||||
// values, and returns a valid 'out_axis'
|
// values, and returns a valid 'out_axis'
|
||||||
inline bool ResolveAxis(const int num_dims, const int* axis,
|
inline bool ResolveAxis(const int num_dims, const int* axis,
|
||||||
@@ -111,7 +160,8 @@ inline bool InitTensorDataForReduce(const int* dims, const int num_dims,
|
|||||||
for (int idx = 0; idx < num_dims; ++idx) {
|
for (int idx = 0; idx < num_dims; ++idx) {
|
||||||
size_t current = static_cast<size_t>(dims[idx]);
|
size_t current = static_cast<size_t>(dims[idx]);
|
||||||
// Overflow prevention.
|
// Overflow prevention.
|
||||||
if (num_elements > std::numeric_limits<size_t>::max() / current) {
|
if (current > 0 &&
|
||||||
|
num_elements > std::numeric_limits<size_t>::max() / current) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
num_elements *= current;
|
num_elements *= current;
|
||||||
@@ -132,17 +182,20 @@ inline bool ReduceGeneric(const T* input_data, const int* input_dims,
|
|||||||
bool keep_dims, int* temp_index, int* resolved_axis,
|
bool keep_dims, int* temp_index, int* resolved_axis,
|
||||||
T init_value,
|
T init_value,
|
||||||
T reducer(const T current, const T in)) {
|
T reducer(const T current, const T in)) {
|
||||||
// Return early when input shape has zero dim.
|
|
||||||
for (int i = 0; i < input_num_dims; ++i) {
|
|
||||||
if (input_dims[i] == 0) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset output data.
|
// Reset output data.
|
||||||
if (!InitTensorDataForReduce(output_dims, output_num_dims, init_value,
|
if (!InitTensorDataForReduce(output_dims, output_num_dims, init_value,
|
||||||
output_data)) {
|
output_data)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return early when input shape has zero dim. This is done after initializing
|
||||||
|
// data for output tensor because there are cases that the input tensor is
|
||||||
|
// empty but output tensor is not. In that case, output tensor should be
|
||||||
|
// filled with init_value.
|
||||||
|
for (int i = 0; i < input_num_dims; ++i) {
|
||||||
|
if (input_dims[i] == 0) return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Resolve axis.
|
// Resolve axis.
|
||||||
int num_resolved_axis = 0;
|
int num_resolved_axis = 0;
|
||||||
if (!ResolveAxis(input_num_dims, axis, num_axis_dimensions, resolved_axis,
|
if (!ResolveAxis(input_num_dims, axis, num_axis_dimensions, resolved_axis,
|
||||||
@@ -290,9 +343,9 @@ inline void Mean(const tflite::MeanParams& op_params,
|
|||||||
constexpr int32_t kMinValue = std::numeric_limits<uint8_t>::min();
|
constexpr int32_t kMinValue = std::numeric_limits<uint8_t>::min();
|
||||||
constexpr int32_t kMaxValue = std::numeric_limits<uint8_t>::max();
|
constexpr int32_t kMaxValue = std::numeric_limits<uint8_t>::max();
|
||||||
|
|
||||||
int32_t bias =
|
float temp = input_zero_point * input_scale / output_scale;
|
||||||
output_zero_point -
|
temp = temp > 0 ? temp + 0.5f : temp - 0.5f;
|
||||||
static_cast<int32_t>(input_zero_point * input_scale / output_scale);
|
int32_t bias = output_zero_point - static_cast<int32_t>(temp);
|
||||||
double real_scale =
|
double real_scale =
|
||||||
static_cast<double>(input_scale / (num_elements_in_axis * output_scale));
|
static_cast<double>(input_scale / (num_elements_in_axis * output_scale));
|
||||||
|
|
||||||
@@ -353,6 +406,14 @@ inline bool QuantizedMeanOrSum(const T* input_data, int32_t input_zero_point,
|
|||||||
temp_sum[idx] = U();
|
temp_sum[idx] = U();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return early when input shape has zero dim. This is done after initializing
|
||||||
|
// data for output tensor because there are cases that the input tensor is
|
||||||
|
// empty but output tensor is not. In that case, output tensor should be
|
||||||
|
// filled with init_value.
|
||||||
|
for (int i = 0; i < input_num_dims; ++i) {
|
||||||
|
if (input_dims[i] == 0) return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Resolve axis.
|
// Resolve axis.
|
||||||
int num_resolved_axis = 0;
|
int num_resolved_axis = 0;
|
||||||
if (!ResolveAxis(input_num_dims, axis, num_axis_dimensions, resolved_axis,
|
if (!ResolveAxis(input_num_dims, axis, num_axis_dimensions, resolved_axis,
|
||||||
@@ -405,6 +466,57 @@ inline bool QuantizedMeanOrSum(const T* input_data, int32_t input_zero_point,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline bool QuantizedReduceProd(const T* input_data, int32_t input_zero_point,
|
||||||
|
const RuntimeShape& input_shape, T* output_data,
|
||||||
|
int32_t output_zero_point,
|
||||||
|
const RuntimeShape& output_shape,
|
||||||
|
const int* axis,
|
||||||
|
const int64_t num_axis_dimensions,
|
||||||
|
bool keep_dims, int* temp_index,
|
||||||
|
int* resolved_axis, int32_t* temp_prod,
|
||||||
|
int32_t scaling_multiplier, int scaling_shift) {
|
||||||
|
const int32_t kMinValue = std::numeric_limits<T>::min();
|
||||||
|
const int32_t kMaxValue = std::numeric_limits<T>::max();
|
||||||
|
|
||||||
|
// Resolve axis.
|
||||||
|
int num_resolved_axis = 0;
|
||||||
|
if (!ResolveAxis(input_shape.DimensionsCount(), axis, num_axis_dimensions,
|
||||||
|
resolved_axis, &num_resolved_axis)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the reduced product by rescaling each multiplication step to
|
||||||
|
// avoid an overflow.
|
||||||
|
auto reducer_first = [&](T in) -> int32_t { return in - input_zero_point; };
|
||||||
|
|
||||||
|
auto reducer_next = [&](int32_t current, T in) -> int32_t {
|
||||||
|
const int64_t result =
|
||||||
|
static_cast<int64_t>(current) * (in - input_zero_point);
|
||||||
|
return MultiplyByQuantizedMultiplier(result, scaling_multiplier,
|
||||||
|
scaling_shift);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!Reduce<T, int32_t>(
|
||||||
|
input_data, input_shape.DimsData(), output_shape.DimsData(),
|
||||||
|
input_shape.DimensionsCount(), output_shape.DimensionsCount(),
|
||||||
|
resolved_axis, num_resolved_axis, temp_index, reducer_first,
|
||||||
|
reducer_next, temp_prod)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < output_shape.FlatSize(); i++) {
|
||||||
|
int32_t result =
|
||||||
|
MultiplyByQuantizedMultiplier(static_cast<int64_t>(temp_prod[i]),
|
||||||
|
scaling_multiplier, scaling_shift) +
|
||||||
|
output_zero_point;
|
||||||
|
result = std::min(std::max(result, kMinValue), kMaxValue);
|
||||||
|
output_data[i] = static_cast<T>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace reference_ops
|
} // namespace reference_ops
|
||||||
|
|
||||||
} // namespace tflite
|
} // namespace tflite
|
||||||
|
|||||||
@@ -0,0 +1,228 @@
|
|||||||
|
/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==============================================================================*/
|
||||||
|
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_RESIZE_BILINEAR_H_
|
||||||
|
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_RESIZE_BILINEAR_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include "tensorflow/lite/kernels/internal/cppmath.h"
|
||||||
|
#include "tensorflow/lite/kernels/internal/types.h"
|
||||||
|
|
||||||
|
namespace tflite {
|
||||||
|
namespace reference_ops {
|
||||||
|
|
||||||
|
inline void ComputeInterpolationValues(const float value, const float scale,
|
||||||
|
const bool half_pixel_centers,
|
||||||
|
int32_t input_size, float* scaled_value,
|
||||||
|
int32_t* lower_bound,
|
||||||
|
int32_t* upper_bound) {
|
||||||
|
if (half_pixel_centers) {
|
||||||
|
*scaled_value = (value + 0.5f) * scale - 0.5f;
|
||||||
|
} else {
|
||||||
|
*scaled_value = value * scale;
|
||||||
|
}
|
||||||
|
float scaled_value_floor = std::floor(*scaled_value);
|
||||||
|
*lower_bound = std::max(static_cast<int32_t>(scaled_value_floor),
|
||||||
|
static_cast<int32_t>(0));
|
||||||
|
*upper_bound =
|
||||||
|
std::min(static_cast<int32_t>(std::ceil(*scaled_value)), input_size - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void ResizeBilinear(const tflite::ResizeBilinearParams& op_params,
|
||||||
|
const RuntimeShape& unextended_input_shape,
|
||||||
|
const T* input_data,
|
||||||
|
const RuntimeShape& unextended_output_size_shape,
|
||||||
|
const int32_t* output_size_data,
|
||||||
|
const RuntimeShape& unextended_output_shape,
|
||||||
|
T* output_data) {
|
||||||
|
// If half_pixel_centers is True, align_corners must be False.
|
||||||
|
TFLITE_DCHECK(!op_params.half_pixel_centers || !op_params.align_corners);
|
||||||
|
TFLITE_DCHECK_LE(unextended_input_shape.DimensionsCount(), 4);
|
||||||
|
TFLITE_DCHECK_LE(unextended_output_size_shape.DimensionsCount(), 4);
|
||||||
|
TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4);
|
||||||
|
const RuntimeShape input_shape =
|
||||||
|
RuntimeShape::ExtendedShape(4, unextended_input_shape);
|
||||||
|
const RuntimeShape output_size_shape =
|
||||||
|
RuntimeShape::ExtendedShape(4, unextended_output_size_shape);
|
||||||
|
const RuntimeShape output_shape =
|
||||||
|
RuntimeShape::ExtendedShape(4, unextended_output_shape);
|
||||||
|
|
||||||
|
int32_t batches = MatchingDim(input_shape, 0, output_shape, 0);
|
||||||
|
int32_t input_height = input_shape.Dims(1);
|
||||||
|
int32_t input_width = input_shape.Dims(2);
|
||||||
|
int32_t depth = MatchingDim(input_shape, 3, output_shape, 3);
|
||||||
|
|
||||||
|
TFLITE_DCHECK_EQ(output_size_shape.Dims(0), 1);
|
||||||
|
TFLITE_DCHECK_EQ(output_size_shape.Dims(1), 1);
|
||||||
|
TFLITE_DCHECK_EQ(output_size_shape.Dims(2), 1);
|
||||||
|
TFLITE_DCHECK_EQ(output_size_shape.Dims(3), 2);
|
||||||
|
int32_t output_height =
|
||||||
|
output_size_data[Offset(output_size_shape, 0, 0, 0, 0)];
|
||||||
|
int32_t output_width =
|
||||||
|
output_size_data[Offset(output_size_shape, 0, 0, 0, 1)];
|
||||||
|
|
||||||
|
float height_scale = static_cast<float>(input_height) / output_height;
|
||||||
|
float width_scale = static_cast<float>(input_width) / output_width;
|
||||||
|
if (op_params.align_corners && output_height > 1) {
|
||||||
|
height_scale = static_cast<float>(input_height - 1) / (output_height - 1);
|
||||||
|
}
|
||||||
|
if (op_params.align_corners && output_width > 1) {
|
||||||
|
width_scale = static_cast<float>(input_width - 1) / (output_width - 1);
|
||||||
|
}
|
||||||
|
const float rounding_offset = std::numeric_limits<T>::is_integer ? .5f : .0f;
|
||||||
|
|
||||||
|
for (int b = 0; b < batches; ++b) {
|
||||||
|
for (int y = 0; y < output_height; ++y) {
|
||||||
|
float input_y;
|
||||||
|
int32_t y0, y1;
|
||||||
|
ComputeInterpolationValues(y, height_scale, op_params.half_pixel_centers,
|
||||||
|
input_height, &input_y, &y0, &y1);
|
||||||
|
for (int x = 0; x < output_width; ++x) {
|
||||||
|
float input_x;
|
||||||
|
int32_t x0, x1;
|
||||||
|
ComputeInterpolationValues(x, width_scale, op_params.half_pixel_centers,
|
||||||
|
input_width, &input_x, &x0, &x1);
|
||||||
|
for (int c = 0; c < depth; ++c) {
|
||||||
|
T interpolation =
|
||||||
|
static_cast<T>(input_data[Offset(input_shape, b, y0, x0, c)] *
|
||||||
|
(1 - (input_y - y0)) * (1 - (input_x - x0)) +
|
||||||
|
input_data[Offset(input_shape, b, y1, x0, c)] *
|
||||||
|
(input_y - y0) * (1 - (input_x - x0)) +
|
||||||
|
input_data[Offset(input_shape, b, y0, x1, c)] *
|
||||||
|
(1 - (input_y - y0)) * (input_x - x0) +
|
||||||
|
input_data[Offset(input_shape, b, y1, x1, c)] *
|
||||||
|
(input_y - y0) * (input_x - x0) +
|
||||||
|
rounding_offset);
|
||||||
|
output_data[Offset(output_shape, b, y, x, c)] = interpolation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ComputeInterpolationValuesInteger(
|
||||||
|
const int32_t value, const int32_t scale_10, const bool half_pixel_centers,
|
||||||
|
int32_t input_size, int32_t* scaled_value, int32_t* lower_bound,
|
||||||
|
int32_t* upper_bound) {
|
||||||
|
if (half_pixel_centers) {
|
||||||
|
*scaled_value = value * scale_10 + scale_10 / 2 - (1 << 9);
|
||||||
|
} else {
|
||||||
|
*scaled_value = value * scale_10;
|
||||||
|
}
|
||||||
|
constexpr int32_t zero = 0;
|
||||||
|
*lower_bound = std::max(*scaled_value / (1 << 10), zero);
|
||||||
|
*upper_bound =
|
||||||
|
std::min((*scaled_value + (1 << 10) - 1) / (1 << 10), input_size - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same as above but doesn't use any floating-point for the resize
|
||||||
|
template <typename T>
|
||||||
|
inline void ResizeBilinearInteger(
|
||||||
|
const tflite::ResizeBilinearParams& op_params,
|
||||||
|
const RuntimeShape& unextended_input_shape, const T* input_data,
|
||||||
|
const RuntimeShape& unextended_output_size_shape,
|
||||||
|
const int32_t* output_size_data,
|
||||||
|
const RuntimeShape& unextended_output_shape, T* output_data) {
|
||||||
|
// If half_pixel_centers is True, align_corners must be False.
|
||||||
|
TFLITE_DCHECK(!op_params.half_pixel_centers || !op_params.align_corners);
|
||||||
|
TFLITE_DCHECK_LE(unextended_input_shape.DimensionsCount(), 4);
|
||||||
|
TFLITE_DCHECK_LE(unextended_output_size_shape.DimensionsCount(), 4);
|
||||||
|
TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4);
|
||||||
|
const RuntimeShape input_shape =
|
||||||
|
RuntimeShape::ExtendedShape(4, unextended_input_shape);
|
||||||
|
const RuntimeShape output_size_shape =
|
||||||
|
RuntimeShape::ExtendedShape(4, unextended_output_size_shape);
|
||||||
|
const RuntimeShape output_shape =
|
||||||
|
RuntimeShape::ExtendedShape(4, unextended_output_shape);
|
||||||
|
|
||||||
|
const int32_t batches = MatchingDim(input_shape, 0, output_shape, 0);
|
||||||
|
const int32_t input_height = input_shape.Dims(1);
|
||||||
|
const int32_t input_width = input_shape.Dims(2);
|
||||||
|
const int32_t depth = MatchingDim(input_shape, 3, output_shape, 3);
|
||||||
|
|
||||||
|
TFLITE_DCHECK_EQ(output_size_shape.Dims(0), 1);
|
||||||
|
TFLITE_DCHECK_EQ(output_size_shape.Dims(1), 1);
|
||||||
|
TFLITE_DCHECK_EQ(output_size_shape.Dims(2), 1);
|
||||||
|
TFLITE_DCHECK_EQ(output_size_shape.Dims(3), 2);
|
||||||
|
const int32_t output_height =
|
||||||
|
output_size_data[Offset(output_size_shape, 0, 0, 0, 0)];
|
||||||
|
const int32_t output_width =
|
||||||
|
output_size_data[Offset(output_size_shape, 0, 0, 0, 1)];
|
||||||
|
|
||||||
|
int32_t height_scale_10 =
|
||||||
|
((1 << 10) * input_height + output_height / 2) / output_height;
|
||||||
|
int32_t width_scale_10 =
|
||||||
|
((1 << 10) * input_width + output_width / 2) / output_width;
|
||||||
|
if (op_params.align_corners && output_height > 1) {
|
||||||
|
height_scale_10 =
|
||||||
|
((1 << 10) * (input_height - 1) + (output_height - 1) / 2) /
|
||||||
|
(output_height - 1);
|
||||||
|
}
|
||||||
|
if (op_params.align_corners && output_width > 1) {
|
||||||
|
width_scale_10 = ((1 << 10) * (input_width - 1) + (output_width - 1) / 2) /
|
||||||
|
(output_width - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int b = 0; b < batches; ++b) {
|
||||||
|
for (int y = 0; y < output_height; ++y) {
|
||||||
|
int32_t input_y, y0, y1;
|
||||||
|
ComputeInterpolationValuesInteger(y, height_scale_10,
|
||||||
|
op_params.half_pixel_centers,
|
||||||
|
input_height, &input_y, &y0, &y1);
|
||||||
|
for (int x = 0; x < output_width; ++x) {
|
||||||
|
int32_t input_x, x0, x1;
|
||||||
|
ComputeInterpolationValuesInteger(x, width_scale_10,
|
||||||
|
op_params.half_pixel_centers,
|
||||||
|
input_width, &input_x, &x0, &x1);
|
||||||
|
for (int c = 0; c < depth; ++c) {
|
||||||
|
const int64_t output_20_ll =
|
||||||
|
static_cast<int64_t>(
|
||||||
|
input_data[Offset(input_shape, b, y0, x0, c)]) *
|
||||||
|
((1 << 10) - (input_y - (1 << 10) * y0)) *
|
||||||
|
((1 << 10) - (input_x - (1 << 10) * x0));
|
||||||
|
const int64_t output_20_lu =
|
||||||
|
static_cast<int64_t>(
|
||||||
|
input_data[Offset(input_shape, b, y1, x0, c)]) *
|
||||||
|
(input_y - (1 << 10) * y0) *
|
||||||
|
((1 << 10) - (input_x - (1 << 10) * x0));
|
||||||
|
const int64_t output_20_rl =
|
||||||
|
static_cast<int64_t>(
|
||||||
|
input_data[Offset(input_shape, b, y0, x1, c)]) *
|
||||||
|
((1 << 10) - (input_y - (1 << 10) * y0)) *
|
||||||
|
(input_x - (1 << 10) * x0);
|
||||||
|
const int64_t output_20_ru =
|
||||||
|
static_cast<int64_t>(
|
||||||
|
input_data[Offset(input_shape, b, y1, x1, c)]) *
|
||||||
|
(input_y - (1 << 10) * y0) * (input_x - (1 << 10) * x0);
|
||||||
|
const int64_t output_20 =
|
||||||
|
output_20_ll + output_20_lu + output_20_rl + output_20_ru;
|
||||||
|
const int64_t round = (output_20 > 0) ? (1 << 19) : -(1 << 19);
|
||||||
|
const T interpolation =
|
||||||
|
static_cast<T>((output_20 + round) / (1 << 20));
|
||||||
|
output_data[Offset(output_shape, b, y, x, c)] = interpolation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace reference_ops
|
||||||
|
} // namespace tflite
|
||||||
|
|
||||||
|
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_RESIZE_BILINEAR_H_
|
||||||
@@ -159,7 +159,7 @@ inline int16_t SoftMaxCalculateExp(const SoftmaxParams& params,
|
|||||||
std::min(std::max(sym_scaled_diff, static_cast<int32_t>(-32768)),
|
std::min(std::max(sym_scaled_diff, static_cast<int32_t>(-32768)),
|
||||||
static_cast<int32_t>(32767));
|
static_cast<int32_t>(32767));
|
||||||
// apply the exp() LUT activation function
|
// apply the exp() LUT activation function
|
||||||
return generic_int16_table_lookup(sat_sym_scaled_diff, params.exp_lut);
|
return lut_lookup(sat_sym_scaled_diff, params.exp_lut);
|
||||||
}
|
}
|
||||||
// Quantized softmax with int16_t input and int16_t output.
|
// Quantized softmax with int16_t input and int16_t output.
|
||||||
inline void SoftmaxInt16(const SoftmaxParams& params,
|
inline void SoftmaxInt16(const SoftmaxParams& params,
|
||||||
@@ -207,8 +207,8 @@ inline void SoftmaxInt16(const SoftmaxParams& params,
|
|||||||
std::min(std::max(sym_shifted_sum, static_cast<int32_t>(-32768)),
|
std::min(std::max(sym_shifted_sum, static_cast<int32_t>(-32768)),
|
||||||
static_cast<int32_t>(32767)));
|
static_cast<int32_t>(32767)));
|
||||||
// apply 1/(1 + x) LUT activation function
|
// apply 1/(1 + x) LUT activation function
|
||||||
int16_t reciprocal_scale_Q015 = generic_int16_table_lookup(
|
int16_t reciprocal_scale_Q015 =
|
||||||
sat_sym_shifted_sum, params.one_over_one_plus_x_lut);
|
lut_lookup(sat_sym_shifted_sum, params.one_over_one_plus_x_lut);
|
||||||
|
|
||||||
// Rescale the exp_result with reciprocal
|
// Rescale the exp_result with reciprocal
|
||||||
// range of output is [0, 32767] correspond to [0.0, 1.0]
|
// range of output is [0, 32767] correspond to [0.0, 1.0]
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==============================================================================*/
|
||||||
|
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SPACE_TO_DEPTH_H_
|
||||||
|
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SPACE_TO_DEPTH_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "tensorflow/lite/kernels/internal/types.h"
|
||||||
|
|
||||||
|
namespace tflite {
|
||||||
|
namespace reference_ops {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void SpaceToDepth(const tflite::SpaceToDepthParams& op_params,
|
||||||
|
const RuntimeShape& unextended_input_shape,
|
||||||
|
const T* input_data,
|
||||||
|
const RuntimeShape& unextended_output_shape,
|
||||||
|
T* output_data) {
|
||||||
|
TFLITE_DCHECK_LE(unextended_input_shape.DimensionsCount(), 4);
|
||||||
|
TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4);
|
||||||
|
const RuntimeShape input_shape =
|
||||||
|
RuntimeShape::ExtendedShape(4, unextended_input_shape);
|
||||||
|
const RuntimeShape output_shape =
|
||||||
|
RuntimeShape::ExtendedShape(4, unextended_output_shape);
|
||||||
|
|
||||||
|
const int input_depth = input_shape.Dims(3);
|
||||||
|
const int input_width = input_shape.Dims(2);
|
||||||
|
const int input_height = input_shape.Dims(1);
|
||||||
|
const int input_batch = input_shape.Dims(0);
|
||||||
|
|
||||||
|
const int output_depth = output_shape.Dims(3);
|
||||||
|
const int output_width = output_shape.Dims(2);
|
||||||
|
const int output_height = output_shape.Dims(1);
|
||||||
|
const int output_batch = output_shape.Dims(0);
|
||||||
|
|
||||||
|
const int32_t block_size = op_params.block_size;
|
||||||
|
|
||||||
|
TFLITE_DCHECK_EQ(input_width, output_width * block_size);
|
||||||
|
TFLITE_DCHECK_EQ(input_height, output_height * block_size);
|
||||||
|
TFLITE_DCHECK_EQ(input_depth * block_size * block_size, output_depth);
|
||||||
|
TFLITE_DCHECK_EQ(input_batch, output_batch);
|
||||||
|
|
||||||
|
for (int in_b = 0; in_b < input_batch; ++in_b) {
|
||||||
|
for (int in_h = 0; in_h < input_height; ++in_h) {
|
||||||
|
for (int in_w = 0; in_w < input_width; ++in_w) {
|
||||||
|
for (int in_d = 0; in_d < input_depth; ++in_d) {
|
||||||
|
const int out_d =
|
||||||
|
in_d + ((in_h % block_size) * block_size + in_w % block_size) *
|
||||||
|
input_depth;
|
||||||
|
const int out_w = in_w / block_size;
|
||||||
|
const int out_h = in_h / block_size;
|
||||||
|
const int out_b = in_b;
|
||||||
|
|
||||||
|
const int input_index = Offset(input_shape, in_b, in_h, in_w, in_d);
|
||||||
|
const int output_index =
|
||||||
|
Offset(output_shape, out_b, out_h, out_w, out_d);
|
||||||
|
|
||||||
|
output_data[output_index] = input_data[input_index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace reference_ops
|
||||||
|
} // namespace tflite
|
||||||
|
|
||||||
|
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SPACE_TO_DEPTH_H_
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==============================================================================*/
|
||||||
|
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_TRANSPOSE_H_
|
||||||
|
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_TRANSPOSE_H_
|
||||||
|
|
||||||
|
#include "tensorflow/lite/kernels/internal/common.h"
|
||||||
|
#include "tensorflow/lite/kernels/internal/types.h"
|
||||||
|
|
||||||
|
namespace tflite {
|
||||||
|
|
||||||
|
namespace reference_ops {
|
||||||
|
|
||||||
|
template <typename T, int N>
|
||||||
|
void TransposeImpl(const TransposeParams& params,
|
||||||
|
const RuntimeShape& unextended_input_shape,
|
||||||
|
const T* input_data,
|
||||||
|
const RuntimeShape& unextended_output_shape,
|
||||||
|
T* output_data) {
|
||||||
|
const int unextended_input_size = unextended_input_shape.DimensionsCount();
|
||||||
|
const int unextended_output_size = unextended_output_shape.DimensionsCount();
|
||||||
|
TFLITE_DCHECK_LE(unextended_input_size, N);
|
||||||
|
TFLITE_DCHECK_LE(unextended_output_size, N);
|
||||||
|
TFLITE_DCHECK_EQ(unextended_output_size, params.perm_count);
|
||||||
|
const int input_ext_size = N - unextended_input_size;
|
||||||
|
const int output_ext_size = N - unextended_output_size;
|
||||||
|
NdArrayDesc<N> input_desc;
|
||||||
|
NdArrayDesc<N> output_desc;
|
||||||
|
CopyDimsToDesc(RuntimeShape::ExtendedShape(N, unextended_input_shape),
|
||||||
|
&input_desc);
|
||||||
|
CopyDimsToDesc(RuntimeShape::ExtendedShape(N, unextended_output_shape),
|
||||||
|
&output_desc);
|
||||||
|
|
||||||
|
// The perm data is extended to match the output, each index incremented by
|
||||||
|
// the amount of front padding of the input shape.
|
||||||
|
int extended_perm[N];
|
||||||
|
for (int i = 0; i < N; ++i) {
|
||||||
|
extended_perm[i] = i < output_ext_size
|
||||||
|
? i
|
||||||
|
: params.perm[i - output_ext_size] + input_ext_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Permutes the input shape so we don't need to permute the indexes inside
|
||||||
|
// the loop. Check to make sure output_dims is matching input_dims.
|
||||||
|
NdArrayDesc<N> perm_input_desc;
|
||||||
|
for (int k = 0; k < N; ++k) {
|
||||||
|
TFLITE_DCHECK_EQ(input_desc.extents[extended_perm[k]],
|
||||||
|
output_desc.extents[k]);
|
||||||
|
perm_input_desc.extents[k] = input_desc.extents[extended_perm[k]];
|
||||||
|
perm_input_desc.strides[k] = input_desc.strides[extended_perm[k]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Naive transpose loop (iterate on output index and compute input index).
|
||||||
|
auto tranpose_func = [&](int indexes[N]) {
|
||||||
|
output_data[SubscriptToIndex(output_desc, indexes)] =
|
||||||
|
input_data[SubscriptToIndex(perm_input_desc, indexes)];
|
||||||
|
};
|
||||||
|
NDOpsHelper<N>(output_desc, tranpose_func);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, int N = 5>
|
||||||
|
void Transpose(const TransposeParams& params,
|
||||||
|
const RuntimeShape& unextended_input_shape, const T* input_data,
|
||||||
|
const RuntimeShape& unextended_output_shape, T* output_data) {
|
||||||
|
// Transpose kernel only does rearranging values not numeric evaluations on
|
||||||
|
// each cell. It's safe to implement per size of scalar type and this trick
|
||||||
|
// keeps the total code size in a reasonable range.
|
||||||
|
switch (sizeof(T)) {
|
||||||
|
case 1:
|
||||||
|
TransposeImpl<int8_t, N>(params, unextended_input_shape,
|
||||||
|
reinterpret_cast<const int8_t*>(input_data),
|
||||||
|
unextended_output_shape,
|
||||||
|
reinterpret_cast<int8_t*>(output_data));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
TransposeImpl<int16_t, N>(params, unextended_input_shape,
|
||||||
|
reinterpret_cast<const int16_t*>(input_data),
|
||||||
|
unextended_output_shape,
|
||||||
|
reinterpret_cast<int16_t*>(output_data));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
TransposeImpl<int32_t, N>(params, unextended_input_shape,
|
||||||
|
reinterpret_cast<const int32_t*>(input_data),
|
||||||
|
unextended_output_shape,
|
||||||
|
reinterpret_cast<int32_t*>(output_data));
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
TransposeImpl<int64_t, N>(params, unextended_input_shape,
|
||||||
|
reinterpret_cast<const int64_t*>(input_data),
|
||||||
|
unextended_output_shape,
|
||||||
|
reinterpret_cast<int64_t*>(output_data));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace reference_ops
|
||||||
|
} // namespace tflite
|
||||||
|
|
||||||
|
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_TRANSPOSE_H_
|
||||||
@@ -400,13 +400,22 @@ inline size_t ReducedOutputOffset(const int num_dims, const int* dims,
|
|||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Since tensors with '0' in their shape are valid in TF, these offset functions
|
||||||
|
// allow that as long as the corresponding index is also 0. It is upto the
|
||||||
|
// calling ops to ensure that they perform verification checks on tensor shapes
|
||||||
|
// if they don't support a particular behavior.
|
||||||
|
|
||||||
inline int Offset(const RuntimeShape& shape, int i0, int i1, int i2, int i3) {
|
inline int Offset(const RuntimeShape& shape, int i0, int i1, int i2, int i3) {
|
||||||
TFLITE_DCHECK_EQ(shape.DimensionsCount(), 4);
|
TFLITE_DCHECK_EQ(shape.DimensionsCount(), 4);
|
||||||
const int* dims_data = reinterpret_cast<const int*>(shape.DimsDataUpTo5D());
|
const int* dims_data = reinterpret_cast<const int*>(shape.DimsDataUpTo5D());
|
||||||
TFLITE_DCHECK(i0 >= 0 && i0 < dims_data[0]);
|
TFLITE_DCHECK((dims_data[0] == 0 && i0 == 0) ||
|
||||||
TFLITE_DCHECK(i1 >= 0 && i1 < dims_data[1]);
|
(i0 >= 0 && i0 < dims_data[0]));
|
||||||
TFLITE_DCHECK(i2 >= 0 && i2 < dims_data[2]);
|
TFLITE_DCHECK((dims_data[1] == 0 && i1 == 0) ||
|
||||||
TFLITE_DCHECK(i3 >= 0 && i3 < dims_data[3]);
|
(i1 >= 0 && i1 < dims_data[1]));
|
||||||
|
TFLITE_DCHECK((dims_data[2] == 0 && i2 == 0) ||
|
||||||
|
(i2 >= 0 && i2 < dims_data[2]));
|
||||||
|
TFLITE_DCHECK((dims_data[3] == 0 && i3 == 0) ||
|
||||||
|
(i3 >= 0 && i3 < dims_data[3]));
|
||||||
return ((i0 * dims_data[1] + i1) * dims_data[2] + i2) * dims_data[3] + i3;
|
return ((i0 * dims_data[1] + i1) * dims_data[2] + i2) * dims_data[3] + i3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -414,21 +423,34 @@ inline int Offset(const RuntimeShape& shape, int i0, int i1, int i2, int i3,
|
|||||||
int i4) {
|
int i4) {
|
||||||
TFLITE_DCHECK_EQ(shape.DimensionsCount(), 5);
|
TFLITE_DCHECK_EQ(shape.DimensionsCount(), 5);
|
||||||
const int* dims_data = reinterpret_cast<const int*>(shape.DimsDataUpTo5D());
|
const int* dims_data = reinterpret_cast<const int*>(shape.DimsDataUpTo5D());
|
||||||
TFLITE_DCHECK(i0 >= 0 && i0 < dims_data[0]);
|
TFLITE_DCHECK((dims_data[0] == 0 && i0 == 0) ||
|
||||||
TFLITE_DCHECK(i1 >= 0 && i1 < dims_data[1]);
|
(i0 >= 0 && i0 < dims_data[0]));
|
||||||
TFLITE_DCHECK(i2 >= 0 && i2 < dims_data[2]);
|
TFLITE_DCHECK((dims_data[1] == 0 && i1 == 0) ||
|
||||||
TFLITE_DCHECK(i3 >= 0 && i3 < dims_data[3]);
|
(i1 >= 0 && i1 < dims_data[1]));
|
||||||
TFLITE_DCHECK(i4 >= 0 && i4 < dims_data[4]);
|
TFLITE_DCHECK((dims_data[2] == 0 && i2 == 0) ||
|
||||||
|
(i2 >= 0 && i2 < dims_data[2]));
|
||||||
|
TFLITE_DCHECK((dims_data[3] == 0 && i3 == 0) ||
|
||||||
|
(i3 >= 0 && i3 < dims_data[3]));
|
||||||
|
TFLITE_DCHECK((dims_data[4] == 0 && i4 == 0) ||
|
||||||
|
(i4 >= 0 && i4 < dims_data[4]));
|
||||||
return (((i0 * dims_data[1] + i1) * dims_data[2] + i2) * dims_data[3] + i3) *
|
return (((i0 * dims_data[1] + i1) * dims_data[2] + i2) * dims_data[3] + i3) *
|
||||||
dims_data[4] +
|
dims_data[4] +
|
||||||
i4;
|
i4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline int Offset(const RuntimeShape& shape, int* index) {
|
||||||
|
return Offset(shape, index[0], index[1], index[2], index[3]);
|
||||||
|
}
|
||||||
|
|
||||||
inline int Offset(const Dims<4>& dims, int i0, int i1, int i2, int i3) {
|
inline int Offset(const Dims<4>& dims, int i0, int i1, int i2, int i3) {
|
||||||
TFLITE_DCHECK(i0 >= 0 && i0 < dims.sizes[0]);
|
TFLITE_DCHECK((i0 == 0 && dims.sizes[0] == 0) ||
|
||||||
TFLITE_DCHECK(i1 >= 0 && i1 < dims.sizes[1]);
|
(i0 >= 0 && i0 < dims.sizes[0]));
|
||||||
TFLITE_DCHECK(i2 >= 0 && i2 < dims.sizes[2]);
|
TFLITE_DCHECK((i1 == 0 && dims.sizes[1] == 0) ||
|
||||||
TFLITE_DCHECK(i3 >= 0 && i3 < dims.sizes[3]);
|
(i1 >= 0 && i1 < dims.sizes[1]));
|
||||||
|
TFLITE_DCHECK((i2 == 0 && dims.sizes[2] == 0) ||
|
||||||
|
(i2 >= 0 && i2 < dims.sizes[2]));
|
||||||
|
TFLITE_DCHECK((i3 == 0 && dims.sizes[3] == 0) ||
|
||||||
|
(i3 >= 0 && i3 < dims.sizes[3]));
|
||||||
return i0 * dims.strides[0] + i1 * dims.strides[1] + i2 * dims.strides[2] +
|
return i0 * dims.strides[0] + i1 * dims.strides[1] + i2 * dims.strides[2] +
|
||||||
i3 * dims.strides[3];
|
i3 * dims.strides[3];
|
||||||
}
|
}
|
||||||
@@ -437,10 +459,6 @@ inline int Offset(const Dims<4>& dims, int* index) {
|
|||||||
return Offset(dims, index[0], index[1], index[2], index[3]);
|
return Offset(dims, index[0], index[1], index[2], index[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int Offset(const RuntimeShape& shape, int* index) {
|
|
||||||
return Offset(shape, index[0], index[1], index[2], index[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get array size, DCHECKing that the dim index is in range.
|
// Get array size, DCHECKing that the dim index is in range.
|
||||||
//
|
//
|
||||||
// Note that this will be phased out with Dims<4>, since RuntimeShape::Dims()
|
// Note that this will be phased out with Dims<4>, since RuntimeShape::Dims()
|
||||||
@@ -602,6 +620,58 @@ inline int MatchingFlatSize(const Dims<N>& dims, const Dims<N>& check_dims_0,
|
|||||||
return MatchingFlatSize(dims, check_dims_1, check_dims_2, check_dims_3);
|
return MatchingFlatSize(dims, check_dims_1, check_dims_2, check_dims_3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flat size calculation, checking if their extended shapes match.
|
||||||
|
inline int MatchingExtendedShapeFlatSize(const RuntimeShape& shape,
|
||||||
|
const RuntimeShape& check_shape_0) {
|
||||||
|
const int shape_dims = shape.DimensionsCount();
|
||||||
|
const int check_shape_0_dims = check_shape_0.DimensionsCount();
|
||||||
|
const int min_dims = std::min(shape_dims, check_shape_0_dims);
|
||||||
|
|
||||||
|
for (int i = 0; i < min_dims; ++i) {
|
||||||
|
TFLITE_DCHECK_EQ(shape.Dims(shape_dims - 1 - i),
|
||||||
|
check_shape_0.Dims(check_shape_0_dims - 1 - i));
|
||||||
|
}
|
||||||
|
for (int i = min_dims; i < shape_dims; ++i) {
|
||||||
|
TFLITE_DCHECK_EQ(shape.Dims(shape_dims - 1 - i), 1);
|
||||||
|
}
|
||||||
|
for (int i = min_dims; i < check_shape_0_dims; ++i) {
|
||||||
|
TFLITE_DCHECK_EQ(check_shape_0.Dims(check_shape_0_dims - 1 - i), 1);
|
||||||
|
}
|
||||||
|
return shape.FlatSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int MatchingExtendedShapeFlatSize(const RuntimeShape& shape,
|
||||||
|
const RuntimeShape& check_shape_0,
|
||||||
|
const RuntimeShape& check_shape_1) {
|
||||||
|
const int flat_size = MatchingExtendedShapeFlatSize(shape, check_shape_0);
|
||||||
|
TFLITE_DCHECK_EQ(MatchingExtendedShapeFlatSize(shape, check_shape_1),
|
||||||
|
flat_size);
|
||||||
|
return flat_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int MatchingExtendedShapeFlatSize(const RuntimeShape& shape,
|
||||||
|
const RuntimeShape& check_shape_0,
|
||||||
|
const RuntimeShape& check_shape_1,
|
||||||
|
const RuntimeShape& check_shape_2) {
|
||||||
|
const int flat_size = MatchingExtendedShapeFlatSize(shape, check_shape_0);
|
||||||
|
TFLITE_DCHECK_EQ(
|
||||||
|
MatchingExtendedShapeFlatSize(shape, check_shape_1, check_shape_2),
|
||||||
|
flat_size);
|
||||||
|
return flat_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int MatchingExtendedShapeFlatSize(const RuntimeShape& shape,
|
||||||
|
const RuntimeShape& check_shape_0,
|
||||||
|
const RuntimeShape& check_shape_1,
|
||||||
|
const RuntimeShape& check_shape_2,
|
||||||
|
const RuntimeShape& check_shape_3) {
|
||||||
|
const int flat_size = MatchingExtendedShapeFlatSize(shape, check_shape_0);
|
||||||
|
TFLITE_DCHECK_EQ(MatchingExtendedShapeFlatSize(shape, check_shape_1,
|
||||||
|
check_shape_2, check_shape_3),
|
||||||
|
flat_size);
|
||||||
|
return flat_size;
|
||||||
|
}
|
||||||
|
|
||||||
// Data is required to be contiguous, and so many operators can use either the
|
// Data is required to be contiguous, and so many operators can use either the
|
||||||
// full array flat size or the flat size with one dimension skipped (commonly
|
// full array flat size or the flat size with one dimension skipped (commonly
|
||||||
// the depth).
|
// the depth).
|
||||||
@@ -885,6 +955,8 @@ struct Conv3DParams {
|
|||||||
float float_activation_max;
|
float float_activation_max;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef Conv3DParams Conv3DTransposeParams;
|
||||||
|
|
||||||
struct DepthToSpaceParams {
|
struct DepthToSpaceParams {
|
||||||
int32_t block_size;
|
int32_t block_size;
|
||||||
};
|
};
|
||||||
@@ -1019,9 +1091,9 @@ struct PackParams {
|
|||||||
|
|
||||||
struct PadParams {
|
struct PadParams {
|
||||||
int8_t left_padding_count;
|
int8_t left_padding_count;
|
||||||
int32_t left_padding[4];
|
int32_t left_padding[5];
|
||||||
int8_t right_padding_count;
|
int8_t right_padding_count;
|
||||||
int32_t right_padding[4];
|
int32_t right_padding[5];
|
||||||
ResizingCategory resizing_category;
|
ResizingCategory resizing_category;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1196,6 +1268,23 @@ inline void GetActivationParams(const P& params, int64_t* min, int64_t* max) {
|
|||||||
*min = params.int64_activation_min;
|
*min = params.int64_activation_min;
|
||||||
*max = params.int64_activation_max;
|
*max = params.int64_activation_max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type trait to check of given type has size smaller than 4 bytes.
|
||||||
|
template <typename T>
|
||||||
|
struct is_small_integer
|
||||||
|
: public std::integral_constant<bool,
|
||||||
|
std::is_same<T, int8_t>::value ||
|
||||||
|
std::is_same<T, uint8_t>::value ||
|
||||||
|
std::is_same<T, int16_t>::value ||
|
||||||
|
std::is_same<T, uint16_t>::value> {};
|
||||||
|
|
||||||
|
// Type trait to check of given type is int32 or int64.
|
||||||
|
template <typename T>
|
||||||
|
struct is_int32_or_int64
|
||||||
|
: public std::integral_constant<bool, std::is_same<T, int32_t>::value ||
|
||||||
|
std::is_same<T, int64_t>::value> {
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace tflite
|
} // namespace tflite
|
||||||
|
|
||||||
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_TYPES_H_
|
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_TYPES_H_
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user