Browse Source

Merge pull request #180 from grindylow/development

Development -> Main 0.5.15

- v0.5.15
    * Bug fix: mqtt payload handling (thx @klahus1, silverserver)
    * Bug fix: eeprom alignment fixed (thx @klahus1)
    * mqtt reconnect improvements (thx @tastendruecker123 , @HorstG-57 )
    * simple command scheduler (one place fifo)
    * InverterDevInform_All Command parser and output to mqtt
    * New workflow to build github release
    * Introduction of a command queue (like OpenDTU)
    * Feedback from inverter for actual power limit via InfoCmd -> SystemConfigPara (0x05) placed in visualization
    * REST API will enqueue a new info command (all commands supported)
    * Change in power limit will (Setup, MQTT or REST API) enqueues a new infocmd request to get actual power limit
    * Actual power limit is available under MQTT topic <TOPIC>/<INVERTER-NAME>/ch0/PowerLimit ALWAYS in percent
    * Firmware information will be requested automatically up on start of dtu
    * Added User_Manual.md

- Known Open Points
   * Not all parsers are implemented but can now easy be down by just adding byte mapping
   * More than one InfoCmd in the loop WILL require a stream/websocket to update live data.
pull/187/head
Andreas Schiffler 3 years ago
committed by GitHub
parent
commit
348ad4bd07
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 52
      .github/workflows/compile_esp8266.yml
  2. 1
      .gitignore
  3. 79
      tools/esp8266/CHANGES.md
  4. 3
      tools/esp8266/CircularBuffer.h
  5. 62
      tools/esp8266/README.md
  6. 81
      tools/esp8266/User_Manual.md
  7. 24
      tools/esp8266/ahoywifi.cpp
  8. 23
      tools/esp8266/ahoywifi.h
  9. 173
      tools/esp8266/app.cpp
  10. 23
      tools/esp8266/app.h
  11. 2
      tools/esp8266/config.h
  12. 38
      tools/esp8266/defines.h
  13. 14
      tools/esp8266/eep.h
  14. 10
      tools/esp8266/hmDefines.h
  15. 134
      tools/esp8266/hmInverter.h
  16. 2
      tools/esp8266/hmSystem.h
  17. 2
      tools/esp8266/html/h/index_html.h
  18. 2
      tools/esp8266/html/h/setup_html.h
  19. 2
      tools/esp8266/html/h/style_css.h
  20. 10
      tools/esp8266/html/index.html
  21. 9
      tools/esp8266/html/setup.html
  22. 16
      tools/esp8266/html/style.css
  23. 4
      tools/esp8266/include/dbg.h
  24. 36
      tools/esp8266/mqtt.h
  25. 30
      tools/esp8266/platformio.ini
  26. 13
      tools/esp8266/scripts/getVersion.py
  27. 36
      tools/esp8266/web.cpp
  28. 18
      tools/esp8266/web.h

52
.github/workflows/compile_esp8266.yml

@ -1,4 +1,4 @@
name: ESP8266
name: Ahoy Release for ESP8266
on:
push:
@ -13,33 +13,63 @@ jobs:
- uses: actions/checkout@v3
with:
ref: main
- name: Cache pip
- uses: benjlevesque/short-sha@v1.2
id: short-sha
with:
length: 7
- name: cache-pip
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Cache PlatformIO
- name: cache-platformio
uses: actions/cache@v3
with:
path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
- name: Set up Python
- name: setup-python
uses: actions/setup-python@v3
- name: Install PlatformIO
- name: install-platformio
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio
- name: Update html
- name: update-html
working-directory: tools/esp8266/html
run: python convert.py
- name: Run PlatformIO
run: pio run -d tools/esp8266
- name: rename
run: pio run -d tools/esp8266 --environment esp8266-release
- name: rename-binary-files
id: rename-binary-files
working-directory: tools/esp8266/scripts
run: python getVersion.py
- uses: actions/upload-artifact@v3
- name: create-release
id: create-release
uses: actions/create-release@v1
with:
draft: false
prerelease: false
release_name: ${{ steps.rename-binary-files.outputs.name }}
tag_name: ${{ github.ref }}
body_path: tools/esp8266/CHANGES.md
env:
GITHUB_TOKEN: ${{ github.token }}
- name: set-version
uses: cschleiden/replace-tokens@v1
with:
files: tools/esp8266/User_Manual.md
env:
VERSION: ${{ steps.rename-binary-files.outputs.name }}
- name: create-artifact
run: zip --junk-paths ${{ steps.rename-binary-files.outputs.name }}.zip tools/esp8266/.pio/build/out/* tools/esp8266/User_Manual.md
- name: upload-release
id: upload-release
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
name: esp8266_ahoy
path: tools/esp8266/.pio/build/out/*.bin
upload_url: ${{ steps.create-release.outputs.upload_url }}
asset_path: ./${{ steps.rename-binary-files.outputs.name }}.zip
asset_name: ${{ steps.rename-binary-files.outputs.name }}.zip
asset_content_type: application/zip

1
.gitignore

@ -20,3 +20,4 @@ tools/esp8266/binaries
tools/esp8266/.vscode/extensions.json
.DS_Store
.vscode
tools/esp8266/platformio-device-monitor-*.log

79
tools/esp8266/CHANGES.md

@ -0,0 +1,79 @@
# Changelog
- v0.5.15
* Bug fix: mqtt payload handling (thx @klahus1, silverserver)
* Bug fix: eeprom alignment fixed (thx @klahus1)
* mqtt reconnect improvements (thx @tastendruecker123 , @HorstG-57 )
* simple command scheduler (one place fifo)
* InverterDevInform_All Command parser and output to mqtt
* New workflow to build github release
* Introduction of a command queue (like OpenDTU)
* Feedback from inverter for actual power limit via InfoCmd -> SystemConfigPara (0x05) placed in visualization
* REST API will enqueue a new info command (all commands supported)
* Change in power limit will (Setup, MQTT or REST API) enqueues a new infocmd request to get actual power limit
* Actual power limit is available under MQTT topic <TOPIC>/<INVERTER-NAME>/ch0/PowerLimit ALWAYS in percent
* Firmware information will be requested automatically up on start of dtu
* Added User_Manual.md
- v0.5.14
- v0.5.13
- v0.5.12
- v0.5.11
- v0.5.10
- v0.5.9 *fix PowerLimit PowerPFDev.Desc=0x0001 for permanent
- v0.5.8 *fix #146 device name in setup
- v0.5.7 *add collapsible setup
- v0.5.6 *fix only MQTT sub after the first loop in a conenction
- v0.5.5 *fixed MQTT sub only after connection is established (HorstG-57)
+ added in app.cpp some compiler if statements
*fix: compile possible for non repository versions (if project was download as zip - lumapu)
*fix README.md - Update line 69 (`RF24` 1.4.2 -> 1.4.5) (DanielR92)
*Update hmRadio.h (lumapu)
- v0.5.4 + added Github report text with a URL (aschiffler)
+ added auto_firmware_version.py for GIT_HASH
+ added switch case AlarmData/AlarmUpdate
- v0.5.3 #Bugfix #125 PowerLimit
+ prototype webapi to get info, improved pwr limit (aschiffler)
+ Merge remote-tracking branch 'upstream/main' into pwrlimit
- v0.5.2 add #114 ntp_server_name and port to eeprom
+ stefan123t added some functions (devcontrol/cbMqtt/...)
- v0.5.1 *Merge branch 'upstream/HEAD' into control
*update revision (0.4.26 -> 0.5.1)
- v0.4.26 first poc for power set via mqtt
- v0.4.25 added default SERIAL/MQTT/SEND_INTERVAL #100, fixed env:node_mcu_v2 build #101
- v0.4.24 added fixes for #63, #88, #93. revert #36 (*) EEPROM changes
- v0.4.23 added workflow, fix index.html to load inverter info immediately, changed timestamp to 1 for stand alone ESP #90, Implement MQTT discovery for Home Assistant
- v0.4.22 compiles with PlatformIO
- v0.4.21 reduced warnings
- v0.4.20 improved setup (if no data is in EEprom), improved NRF24 Pinout regarding to #36, Standard Pinout should be now: #36 (comment), add JSON output, fix favicon, improve eeprom default settings (*) EEPROM changes
- v0.4.19 updated debug messages: now 5 different levels are available, fixed CRC loop issue, add fritzing/schematics for Arduino, Raspberry Pi and NodeMCU
- v0.4.18 Creative Commons NC-SA-BY v3.0 license included, tried to increase stability, fix NRF24 CRClength, add debug & documentation links, added variable error messages using #pragma error
- v0.4.17 add printed circuit board layout, more debug output (#retransmits), improved loop counters (*) EEPROM changes
- v0.4.16 request only one inverter per loop (#53 (comment)), mqtt loop interval calculated by # of inverters and inverter request interval, limit maximum number of retries, added feature request #62 (readable names for channels), improved setup page, added javascript to hide / show channel fields (*) EEPROM changes
- v0.4.15 reduced debug messages, fixes after merge
- v0.4.14 added RX channel 40, improved RF24 ISR, reduced AP active time to 60s (will be increase once a client is connected), added `yield` without success -> random reboot (cause 4) (*) EEPROM changes
- v0.4.13 rename to AHOY-DTU, add RX channel 40, update stats on index based on mSendInterval, MQTT Interval, EEPROM CRC settings, fix #56 v0.4.10 ESP8266 stuck in boot loop
- v0.4.12 version skipped ?
- v0.4.11 inverter dependent mqtt (is avail), implemented heap stats #58, inserted 'break' in ISR while loop
- v0.4.10 reduced heap size (>50%) by using 'F()' for (nearly) all static strings, added Wemos D1 case STL files
- v0.4.9 try to fix mqtt and wifi loss issue #52, document libraries (*) EEPROM changes
- v0.4.8 moved mqtt loop out of checkTicker as mentioned in #49, added irritation and efficiency calculations, improved style (*) EEPROM changes
- v0.4.7 version skipped ?
- v0.4.6 version skipped ?
- v0.4.5 fix #38 4-channel inverter current assignment, added last received timestamp in /hoymiles livedata web page #47, improved style.css, improved NTP as described in #46
- v0.4.4 added free heap, mentioned in #24 (added in serial print, status on index and mqtt), fixed #45, AC current by factor 10 too high, fixed failed payload counter
- v0.4.3 fixed #41 HM800 Yield total and Yield day were mixed around. Found issue while comparing to Python version, fixed #43 HM350 channel 2 is displayed in Live-View, added #42 YieldTotal and YieldTotal Day for HM600 - HM800 inverters
- v0.4.2 fix #39 Assignment 2-Channel inverters (HM-600, HM-700, HM-800)
- v0.4.1 multi inverter support, full re transmit included
- v0.4.0 complete payload processed (and crc checked), inverter type is defined by serial number, serial debug can be switched live (using setup), Note: only one inverter is supported for now!
- v0.3.9 fix #26 ticker / interval in app.cpp
- v0.3.8 improved stability (in comparison to 0.3.7), reset wifi AP timout once a client is detected, fix #26 wrong variable reset
- v0.3.7 added rx channel switching, switched to crc8 check for valid packet-payload
- v0.3.6 improved tickers, only one ticker is active, added feature to use the ESP as access point for all the time, added serial features to setup
- v0.3.5 fixed erase settings, fixed behavior if no MQTT IP is set (the system was nearly unusable because of delayed responses), fixed Station / AP WiFi on startup -> more information will be printed to the serial console, added new ticker for serial value dump
- v0.3.4 added config.h for general configuration, added option to compile WiFi SSID + PWD to firmware, added option to configure WiFi access point name and password, added feature to retry connect to station WiFi (configurable timeouts), updated index.html, added option for factory reset, added info about project on index.html, moved "update" and "home" to footer, fixed #23 HM1200 yield day unit, fixed DNS name of ESP after setup (some commits before)
- v0.3.3 converted to "poor-man-ticker" using millis() for uptime, send and mqtt, added inverter overview, added send count to statistics
- v0.3.2 compile of merge, binary published on https://www.mikrocontroller.net/topic/525778?goto=7051413#7051413
- v0.3.1 fix: doCalculations was not called
- v0.3.0 version 0.3.0, added unit test
(*) EEPROM changes require settings to be changed, your settings will be overwritten and need to be set again!

3
tools/esp8266/CircularBuffer.h

@ -24,6 +24,9 @@
#ifdef ESP8266
#define DISABLE_IRQ noInterrupts()
#define RESTORE_IRQ interrupts()
#elif defined(ESP32)
#define DISABLE_IRQ noInterrupts()
#define RESTORE_IRQ interrupts()
#else
#define DISABLE_IRQ \
uint8_t sreg = SREG; \

62
tools/esp8266/README.md

@ -68,64 +68,4 @@ For now the following inverters should work out of the box:
- `Time` 1.6.1
- `RF24` 1.4.5
- `PubSubClient` 2.8
- `ArduinoJson` 6.19.4
## Changelog
(*) EEPROM changes require settings to be changed, your settings will be overwritten and need to be set again!
- v0.5.9 *fix PowerLimit PowerPFDev.Desc=0x0001 for permanent
- v0.5.8 *fix #146 device name in setup
- v0.5.7 *add collapsible setup
- v0.5.6 *fix only MQTT sub after the first loop in a conenction
- v0.5.5 *fixed MQTT sub only after connection is established (HorstG-57)
+ added in app.cpp some compiler if statements
*fix: compile possible for non repository versions (if project was download as zip - lumapu)
*fix README.md - Update line 69 (`RF24` 1.4.2 -> 1.4.5) (DanielR92)
*Update hmRadio.h (lumapu)
- v0.5.4 + added Github report text with a URL (aschiffler)
+ added auto_firmware_version.py for GIT_HASH
+ added switch case AlarmData/AlarmUpdate
- v0.5.3 #Bugfix #125 PowerLimit
+ prototype webapi to get info, improved pwr limit (aschiffler)
+ Merge remote-tracking branch 'upstream/main' into pwrlimit
- v0.5.2 add #114 ntp_server_name and port to eeprom
+ stefan123t added some functions (devcontrol/cbMqtt/...)
- v0.5.1 *Merge branch 'upstream/HEAD' into control
*update revision (0.4.26 -> 0.5.1)
- v0.4.26 first poc for power set via mqtt
- v0.4.25 added default SERIAL/MQTT/SEND_INTERVAL #100, fixed env:node_mcu_v2 build #101
- v0.4.24 added fixes for #63, #88, #93. revert #36 (*) EEPROM changes
- v0.4.23 added workflow, fix index.html to load inverter info immediately, changed timestamp to 1 for stand alone ESP #90, Implement MQTT discovery for Home Assistant
- v0.4.22 compiles with PlatformIO
- v0.4.21 reduced warnings
- v0.4.20 improved setup (if no data is in EEprom), improved NRF24 Pinout regarding to #36, Standard Pinout should be now: #36 (comment), add JSON output, fix favicon, improve eeprom default settings (*) EEPROM changes
- v0.4.19 updated debug messages: now 5 different levels are available, fixed CRC loop issue, add fritzing/schematics for Arduino, Raspberry Pi and NodeMCU
- v0.4.18 Creative Commons NC-SA-BY v3.0 license included, tried to increase stability, fix NRF24 CRClength, add debug & documentation links, added variable error messages using #pragma error
- v0.4.17 add printed circuit board layout, more debug output (#retransmits), improved loop counters (*) EEPROM changes
- v0.4.16 request only one inverter per loop (#53 (comment)), mqtt loop interval calculated by # of inverters and inverter request interval, limit maximum number of retries, added feature request #62 (readable names for channels), improved setup page, added javascript to hide / show channel fields (*) EEPROM changes
- v0.4.15 reduced debug messages, fixes after merge
- v0.4.14 added RX channel 40, improved RF24 ISR, reduced AP active time to 60s (will be increase once a client is connected), added `yield` without success -> random reboot (cause 4) (*) EEPROM changes
- v0.4.13 rename to AHOY-DTU, add RX channel 40, update stats on index based on mSendInterval, MQTT Interval, EEPROM CRC settings, fix #56 v0.4.10 ESP8266 stuck in boot loop
- v0.4.12 version skipped ?
- v0.4.11 inverter dependent mqtt (is avail), implemented heap stats #58, inserted 'break' in ISR while loop
- v0.4.10 reduced heap size (>50%) by using 'F()' for (nearly) all static strings, added Wemos D1 case STL files
- v0.4.9 try to fix mqtt and wifi loss issue #52, document libraries (*) EEPROM changes
- v0.4.8 moved mqtt loop out of checkTicker as mentioned in #49, added irritation and efficiency calculations, improved style (*) EEPROM changes
- v0.4.7 version skipped ?
- v0.4.6 version skipped ?
- v0.4.5 fix #38 4-channel inverter current assignment, added last received timestamp in /hoymiles livedata web page #47, improved style.css, improved NTP as described in #46
- v0.4.4 added free heap, mentioned in #24 (added in serial print, status on index and mqtt), fixed #45, AC current by factor 10 too high, fixed failed payload counter
- v0.4.3 fixed #41 HM800 Yield total and Yield day were mixed around. Found issue while comparing to Python version, fixed #43 HM350 channel 2 is displayed in Live-View, added #42 YieldTotal and YieldTotal Day for HM600 - HM800 inverters
- v0.4.2 fix #39 Assignment 2-Channel inverters (HM-600, HM-700, HM-800)
- v0.4.1 multi inverter support, full re transmit included
- v0.4.0 complete payload processed (and crc checked), inverter type is defined by serial number, serial debug can be switched live (using setup), Note: only one inverter is supported for now!
- v0.3.9 fix #26 ticker / interval in app.cpp
- v0.3.8 improved stability (in comparison to 0.3.7), reset wifi AP timout once a client is detected, fix #26 wrong variable reset
- v0.3.7 added rx channel switching, switched to crc8 check for valid packet-payload
- v0.3.6 improved tickers, only one ticker is active, added feature to use the ESP as access point for all the time, added serial features to setup
- v0.3.5 fixed erase settings, fixed behavior if no MQTT IP is set (the system was nearly unusable because of delayed responses), fixed Station / AP WiFi on startup -> more information will be printed to the serial console, added new ticker for serial value dump
- v0.3.4 added config.h for general configuration, added option to compile WiFi SSID + PWD to firmware, added option to configure WiFi access point name and password, added feature to retry connect to station WiFi (configurable timeouts), updated index.html, added option for factory reset, added info about project on index.html, moved "update" and "home" to footer, fixed #23 HM1200 yield day unit, fixed DNS name of ESP after setup (some commits before)
- v0.3.3 converted to "poor-man-ticker" using millis() for uptime, send and mqtt, added inverter overview, added send count to statistics
- v0.3.2 compile of merge, binary published on https://www.mikrocontroller.net/topic/525778?goto=7051413#7051413
- v0.3.1 fix: doCalculations was not called
- v0.3.0 version 0.3.0, added unit test
- `ArduinoJson` 6.19.4

81
tools/esp8266/User_Manual.md

@ -10,7 +10,7 @@ In the initial case or after click "erase settings" the fields for the inverter
Set at least the serial number and a name for each inverter, check the "reboot after save" and click the "Save" button.
## Active Power Limit via Setup Page
If you leave the field "Active Power Limit" empty during the setup and reboot the ahoy-dtu a value of 65535 will be filled in.
If you leave the field "Active Power Limit" empty during the setup and reboot the ahoy-dtu will set a value of 65535 in the setup.
That is the value you have to fill in case you want to operate the inverter without a active power limit.
If the value is 65535 or -1 after another reboot the value will be set automatically to "100" and in the drop-down menu "relative in percent persistent" will be set. Of course you can do this also by your self.
@ -24,12 +24,12 @@ and if this settings shall be
after a power cycle of the inverter (P_DC=0 and P_AC=0 for at least 10 seconds)
The user has to ensure correct settings. Remember that for the inverters of 3rd generation the relative active power limit is in the range of 2% up to 100%.
Also an absolute active power limit below approx. 30Watt is not correct because of the control capabilities and reactive power load.
Also an absolute active power limit below approx. 30 Watt seems to be not meanful because of the control capabilities and reactive power load.
## Active Power Limit via MQTT
The ahoy-dtu subscribes on the topic <CHOOSEN_TOPIC_FROM_SETUP>/devcontrol/# if the mqtt broker is set-up correctly. The default topic is inverter/devcontrol/#.
The ahoy-dtu subscribes on the topic ``<CHOOSEN_TOPIC_FROM_SETUP>/devcontrol/#`` if the mqtt broker is set-up correctly. The default topic is ``inverter/devcontrol/#``.
To set the absolut active power limit you have four options.
To set the active power limit (controled value is the AC Power of the inverter) you have four options. (Only single phase inverters are actually in focus).
| topic | payload | active power limit in | Condition |
@ -40,9 +40,9 @@ To set the absolut active power limit you have four options.
| <CHOOSEN_TOPIC_FROM_SETUP>/devcontrol/<INVERTER_ID>/11/257 | [2...100] | % | persistent
### Developer Information MQTT Interface
<CHOOSEN_TOPIC_FROM_SETUP>/devcontrol/<INVERTER_ID>/<DevControlCmdType>/<DATA2>
``<CHOOSEN_TOPIC_FROM_SETUP>/devcontrol/<INVERTER_ID>/<DevControlCmdType>/<DATA2>``
The implementation allows to set any of the available <DevCntrlType> Commands:
The implementation allows to set any of the available ``<DevControlCmdType>`` Commands:
```C
typedef enum {
TurnOn = 0, // 0x00
@ -58,9 +58,34 @@ The implementation allows to set any of the available <DevCntrlType> Commands:
Init = 0xff
} DevControlCmdType;
```
The MQTT payload will be set on first to bytes and DATA2 will be set on the second two bytes if the corresponding DevControlCmdType supports 4 byte data.
The MQTT payload will be set on first to bytes and ``<DATA2>``, which is taken from the topic path will be set on the second two bytes if the corresponding DevControlCmdType supports 4 byte data.
See here the actual implementation to set the send buffer bytes.
```C
void sendControlPacket(uint64_t invId, uint8_t cmd, uint16_t *data) {
sendCmdPacket(invId, TX_REQ_DEVCONTROL, ALL_FRAMES, false);
int cnt = 0;
// cmd --> 0x0b => Type_ActivePowerContr, 0 on, 1 off, 2 restart, 12 reactive power, 13 power factor
mTxBuf[10] = cmd;
mTxBuf[10 + (++cnt)] = 0x00;
if (cmd >= ActivePowerContr && cmd <= PFSet){
mTxBuf[10 + (++cnt)] = ((data[0] * 10) >> 8) & 0xff; // power limit || high byte from MQTT payload
mTxBuf[10 + (++cnt)] = ((data[0] * 10) ) & 0xff; // power limit || low byte from MQTT payload
mTxBuf[10 + (++cnt)] = ((data[1] ) >> 8) & 0xff; // high byte from MQTT topic value <DATA2>
mTxBuf[10 + (++cnt)] = ((data[1] ) ) & 0xff; // low byte from MQTT topic value <DATA2>
}
// crc control data
uint16_t crc = Hoymiles::crc16(&mTxBuf[10], cnt+1);
mTxBuf[10 + (++cnt)] = (crc >> 8) & 0xff;
mTxBuf[10 + (++cnt)] = (crc ) & 0xff;
// crc over all
cnt +=1;
mTxBuf[10 + cnt] = Hoymiles::crc8(mTxBuf, 10 + cnt);
sendPacket(invId, mTxBuf, 10 + (++cnt), true);
}
```
So as example sending any payload on inverter/devcontrol/0/1 will switch off the inverter.
So as example sending any payload on ``inverter/devcontrol/0/1`` will switch off the inverter.
## Active Power Limit via REST API
It is also implemented to set the power limit via REST API call. Therefore send a POST request to the endpoint /api.
@ -107,7 +132,16 @@ Example to set the active power limit persistent to 600Watt
```
### Developer Information REST API
In the same approach as for MQTT any other SubCmd can be applied and the response payload can be observed in the serial logs. Eg. request the Alarm Data.
In the same approach as for MQTT any other SubCmd and also MainCmd can be applied and the response payload can be observed in the serial logs. Eg. request the Alarm-Data from the Alarm-Index 5 from inverter 0 will look like this:
```json
{
"inverter":0,
"tx_request": 21,
"cmd": 17,
"payload": 5,
"payload2": 0
}
```
## Issues and Debuging for active power limit settings
@ -120,13 +154,30 @@ In case of issues please report:
**Developer Information General for Active Power Limit**
To be verified by field tests and feedback
Was verified by field tests and feedback from three users
Internally this values will be set for the second two bytes for MainCmd: 0x51 SubCmd: 0x0b --> DevControl set ActivePowerLimit
```C
typedef enum { // ToDo: to be verified by field tests
AbsolutNonPersistent = 0x0000, // 0
RelativNonPersistent = 0x0001, // 1
AbsolutPersistent = 0x0100, // 256
RelativPersistent = 0x0101 // 257
typedef enum {
AbsolutNonPersistent = 0x0000, // 0
RelativNonPersistent = 0x0001, // 1
AbsolutPersistent = 0x0100, // 256
RelativPersistent = 0x0101 // 257
} PowerLimitControlType;
```
## Firmware Version collection
Gather user inverter information here to understand what differs between some inverters.
| Name | Inverter Typ | Bootloader V. | FWVersion | FWBuild [YYYY] | FWBuild [MM-DD] | HWPartId | | |
| ---------- | ------------ | ------------- | --------- | -------------- | --------------- | --------- | -------- | --------- |
| DanielR92 | HM-1500 | | 1.0.16 | 2021 | 10-12 | 100 | | |
| isdor | HM-300 | | 1.0.14 | 2021 | 12-09 | 102 | | |
| aschiffler | HM-1500 | | 1.0.12 | 2020 | 06-24 | 100 | | |
| klahus1 | HM-300 | | 1.0.10 | 2020 | 07-07 | 102 | | |
| eeprom23 | HM-1200 | 0.1.0 | 1.0.18 | 2021 | 12-24 | 269619201 | 18:21:00 | HWRev 256 |
| eeprom23 | HM-1200 2t | 0.1.0 | 1.0.16 | 2021 | 10-12 | 269619207 | 17:06:00 | HWRev 256 |
| fila612 | HM-700 | | 1.0.10 | 2021 | 11-01 | 104 | | |
| | | | | | | | | |
| | | | | | | | | |
| | | | | | | | | |
| | | | | | | | | |

24
tools/esp8266/wifi.cpp → tools/esp8266/ahoywifi.cpp

@ -3,7 +3,11 @@
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//-----------------------------------------------------------------------------
#include "wifi.h"
#if defined(ESP32) && defined(F)
#undef F
#define F(sl) (sl)
#endif
#include "ahoywifi.h"
// NTP CONFIG
@ -12,7 +16,7 @@
//-----------------------------------------------------------------------------
wifi::wifi(app *main, sysConfig_t *sysCfg, config_t *config) {
ahoywifi::ahoywifi(app *main, sysConfig_t *sysCfg, config_t *config) {
mMain = main;
mSysCfg = sysCfg;
mConfig = config;
@ -29,7 +33,7 @@ wifi::wifi(app *main, sysConfig_t *sysCfg, config_t *config) {
//-----------------------------------------------------------------------------
void wifi::setup(uint32_t timeout, bool settingValid) {
void ahoywifi::setup(uint32_t timeout, bool settingValid) {
mWifiStationTimeout = timeout;
#ifndef AP_ONLY
if(false == mApActive)
@ -58,7 +62,7 @@ void wifi::setup(uint32_t timeout, bool settingValid) {
//-----------------------------------------------------------------------------
bool wifi::loop(void) {
bool ahoywifi::loop(void) {
if(mApActive) {
mDns->processNextRequest();
#ifndef AP_ONLY
@ -98,7 +102,7 @@ bool wifi::loop(void) {
//-----------------------------------------------------------------------------
void wifi::setupAp(const char *ssid, const char *pwd) {
void ahoywifi::setupAp(const char *ssid, const char *pwd) {
DPRINTLN(DBG_VERBOSE, F("app::setupAp"));
IPAddress apIp(192, 168, 1, 1);
@ -118,7 +122,7 @@ void wifi::setupAp(const char *ssid, const char *pwd) {
//-----------------------------------------------------------------------------
bool wifi::setupStation(uint32_t timeout) {
bool ahoywifi::setupStation(uint32_t timeout) {
DPRINTLN(DBG_VERBOSE, F("app::setupStation"));
int32_t cnt;
bool startAp = false;
@ -166,12 +170,12 @@ bool wifi::setupStation(uint32_t timeout) {
//-----------------------------------------------------------------------------
bool wifi::getApActive(void) {
bool ahoywifi::getApActive(void) {
return mApActive;
}
//-----------------------------------------------------------------------------
time_t wifi::getNtpTime(void) {
time_t ahoywifi::getNtpTime(void) {
//DPRINTLN(DBG_VERBOSE, F("wifi::getNtpTime"));
time_t date = 0;
IPAddress timeServer;
@ -209,7 +213,7 @@ time_t wifi::getNtpTime(void) {
//-----------------------------------------------------------------------------
void wifi::sendNTPpacket(IPAddress& address) {
void ahoywifi::sendNTPpacket(IPAddress& address) {
//DPRINTLN(DBG_VERBOSE, F("wifi::sendNTPpacket"));
uint8_t buf[NTP_PACKET_SIZE] = {0};
@ -232,7 +236,7 @@ void wifi::sendNTPpacket(IPAddress& address) {
//-----------------------------------------------------------------------------
// calculates the daylight saving time for middle Europe. Input: Unixtime in UTC
// from: https://forum.arduino.cc/index.php?topic=172044.msg1278536#msg1278536
time_t wifi::offsetDayLightSaving (uint32_t local_t) {
time_t ahoywifi::offsetDayLightSaving (uint32_t local_t) {
//DPRINTLN(DBG_VERBOSE, F("wifi::offsetDayLightSaving"));
int m = month (local_t);
if(m < 3 || m > 10) return 0; // no DSL in Jan, Feb, Nov, Dez

23
tools/esp8266/wifi.h → tools/esp8266/ahoywifi.h

@ -3,12 +3,17 @@
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//-----------------------------------------------------------------------------
#ifndef __WIFI_H__
#define __WIFI_H__
#ifndef __AHOYWIFI_H__
#define __AHOYWIFI_H__
#include "dbg.h"
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#ifdef ESP8266
#include <ESP8266WebServer.h>
#include <ESP8266WiFi.h>
#elif defined(ESP32)
#include <WebServer.h>
#include <WiFi.h>
#endif
// NTP
#include <WiFiUdp.h>
@ -21,10 +26,10 @@
class app;
class wifi {
class ahoywifi {
public:
wifi(app *main, sysConfig_t *sysCfg, config_t *config);
~wifi() {}
ahoywifi(app *main, sysConfig_t *sysCfg, config_t *config);
~ahoywifi() {}
void setup(uint32_t timeout, bool settingValid);
bool loop(void);
@ -32,7 +37,7 @@ class wifi {
bool setupStation(uint32_t timeout);
bool getApActive(void);
time_t getNtpTime(void);
private:
void sendNTPpacket(IPAddress& address);
time_t offsetDayLightSaving (uint32_t local_t);
@ -52,4 +57,4 @@ class wifi {
bool wifiWasEstablished;
};
#endif /*__WIFI_H__*/
#endif /*__AHOYWIFI_H__*/

173
tools/esp8266/app.cpp

@ -3,23 +3,24 @@
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//-----------------------------------------------------------------------------
#if defined(ESP32) && defined(F)
#undef F
#define F(sl) (sl)
#endif
#include "app.h"
#include <ArduinoJson.h>
//-----------------------------------------------------------------------------
app::app() {
Serial.begin(115200);
DPRINTLN(DBG_VERBOSE, F("app::app"));
mEep = new eep();
Serial.begin(115200);
mWifi = new wifi(this, &mSysConfig, &mConfig);
mWebInst = new web(this, &mSysConfig, &mConfig, mVersion);
mWebInst->setup();
mWifi = new ahoywifi(this, &mSysConfig, &mConfig);
resetSystem();
loadDefaultConfig();
loadDefaultConfig();
mSys = new HmSystemType();
}
@ -39,6 +40,9 @@ void app::setup(uint32_t timeout) {
setupMqtt();
#endif
mSys->setup(&mConfig);
mWebInst = new web(this, &mSysConfig, &mConfig, mVersion);
mWebInst->setup();
}
//-----------------------------------------------------------------------------
@ -107,41 +111,6 @@ void app::loop(void) {
}
}
}
switch (mSys->InfoCmd){
case InverterDevInform_Simple:
{
DPRINT(DBG_INFO, "Response from inform simple\n");
mSys->InfoCmd = RealTimeRunData_Debug; // Set back to default
break;
}
case InverterDevInform_All:
{
DPRINT(DBG_INFO, "Response from inform all\n");
break;
}
case GetLossRate:
{
DPRINT(DBG_INFO, "Response from get loss rate\n");
mSys->InfoCmd = RealTimeRunData_Debug; // Set back to default
break;
}
case AlarmData:
{
DPRINT(DBG_INFO, "Response from AlarmData\n");
mSys->InfoCmd = RealTimeRunData_Debug; // Set back to default
break;
}
case AlarmUpdate:
{
DPRINT(DBG_INFO, "Response from AlarmUpdate\n");
mSys->InfoCmd = RealTimeRunData_Debug; // Set back to default
break;
}
case RealTimeRunData_Debug:
{
break;
}
}
}
if(NULL != iv && p->packet[0] == (TX_REQ_DEVCONTROL + 0x80)) { // response from dev control command
DPRINTLN(DBG_DEBUG, F("Response from devcontrol request received"));
@ -149,7 +118,7 @@ void app::loop(void) {
switch (p->packet[12]){
case ActivePowerContr:
if (iv->devControlCmd >= ActivePowerContr && iv->devControlCmd <= PFSet){ // ok inverter accepted the set point copy it to dtu eeprom
if (iv->powerLimit[1]>0){ // User want to have it persistent
if ((iv->powerLimit[1] & 0xff00) >0){ // User want to have it persistent
mEep->write(ADDR_INV_PWR_LIM + iv->id * 2,iv->powerLimit[0]);
mEep->write(ADDR_INV_PWR_LIM_CON + iv->id * 2,iv->powerLimit[1]);
updateCrc();
@ -180,7 +149,7 @@ void app::loop(void) {
if(rxRdy) {
processPayload(true,mSys->InfoCmd);
processPayload(true);
}
}
@ -190,7 +159,7 @@ void app::loop(void) {
if(checkTicker(&mTicker, 1000)) {
if((++mMqttTicker >= mMqttInterval) && (mMqttInterval != 0xffff) && mMqttActive) {
mMqttTicker = 0;
mMqtt.isConnected(true);
mMqtt.isConnected(true); // really needed? See comment from HorstG-57 #176
char topic[30], val[10];
for(uint8_t id = 0; id < mSys->getNumInverters(); id++) {
Inverter<> *iv = mSys->getInverterByPos(id);
@ -266,7 +235,7 @@ void app::loop(void) {
if(NULL != iv) {
if(!mPayload[iv->id].complete)
processPayload(false,mSys->InfoCmd);
processPayload(false);
if(!mPayload[iv->id].complete) {
mRxFailed++;
@ -286,8 +255,9 @@ void app::loop(void) {
if(mConfig.serialDebug)
DPRINTLN(DBG_INFO, F("Devcontrol request ") + String(iv->devControlCmd) + F(" power limit ") + String(iv->powerLimit[0]));
mSys->Radio.sendControlPacket(iv->radioId.u64,iv->devControlCmd ,iv->powerLimit);
iv->enqueCommand<InfoCommand>(SystemConfigPara);
} else {
mSys->Radio.sendTimePacket(iv->radioId.u64, mSys->InfoCmd, mPayload[iv->id].ts,iv->alarmMesIndex);
mSys->Radio.sendTimePacket(iv->radioId.u64,iv->getQueuedCmd(), mPayload[iv->id].ts,iv->alarmMesIndex);
mRxTicker = 0;
}
}
@ -333,10 +303,7 @@ bool app::buildPayload(uint8_t id) {
//-----------------------------------------------------------------------------
void app::processPayload(bool retransmit) {
processPayload(retransmit, RealTimeRunData_Debug);
}
void app::processPayload(bool retransmit, uint8_t cmd = RealTimeRunData_Debug) { // cmd value decides which parser is used to decode payload
void app::processPayload(bool retransmit) {
#ifdef __MQTT_AFTER_RX__
boolean doMQTT = false;
@ -369,7 +336,7 @@ void app::processPayload(bool retransmit, uint8_t cmd = RealTimeRunData_Debug) {
if(0x00 != mLastPacketId)
mSys->Radio.sendCmdPacket(iv->radioId.u64, TX_REQ_INFO, mLastPacketId, true);
else
mSys->Radio.sendTimePacket(iv->radioId.u64, mSys->InfoCmd, mPayload[iv->id].ts,iv->alarmMesIndex);
mSys->Radio.sendTimePacket(iv->radioId.u64, iv->getQueuedCmd(), mPayload[iv->id].ts,iv->alarmMesIndex);
}
mSys->Radio.switchRxCh(100);
}
@ -392,19 +359,18 @@ void app::processPayload(bool retransmit, uint8_t cmd = RealTimeRunData_Debug) {
mSys->Radio.dumpBuf(NULL, payload, offs);
}
mRxSuccess++;
mSys->InfoCmd = RealTimeRunData_Debug; // On success set back to default
iv->getAssignment(cmd); // choose the parser
iv->getAssignment(); // choose the parser
for(uint8_t i = 0; i < iv->listLen; i++) {
iv->addValue(i, payload,cmd); // cmd value decides which parser is used to decode payload
iv->addValue(i, payload); // cmd value decides which parser is used to decode payload
yield();
}
iv->doCalculations(cmd); // cmd value decides which parser is used to decode payload
iv->doCalculations(); // cmd value decides which parser is used to decode payload
#ifdef __MQTT_AFTER_RX__
doMQTT = true;
#endif
iv->setQueuedCmdFinished();
}
}
yield();
@ -433,7 +399,7 @@ void app::cbMqtt(char* topic, byte* payload, unsigned int length) {
const char *token = strtok(topic, "/");
while (token != NULL)
{
if (std::strcmp(token,"devcontrol")==0){
if (strcmp(token,"devcontrol")==0){
token = strtok(NULL, "/");
uint8_t iv_id = std::stoi(token);
if (iv_id >= 0 && iv_id <= MAX_NUM_INVERTERS){
@ -481,7 +447,7 @@ void app::cbMqtt(char* topic, byte* payload, unsigned int length) {
iv->devControlCmd = ReactivePowerContr;
if (true){ // if (std::stoi((char*)payload) > 0) error handling powerlimit needed?
iv->devControlCmd = ReactivePowerContr;
iv->powerLimit[0] = std::stoi((char*)payload);
iv->powerLimit[0] = std::stoi(std::string((char*)payload, (unsigned int)length));
iv->powerLimit[1] = 0x0000; // if reactivepower limit is set via external interface --> set it temporay
DPRINTLN(DBG_DEBUG, F("Reactivepower limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit[0]) + F("W") );
iv->devControlRequest = true;
@ -559,32 +525,48 @@ String app::getStatistics(void) {
//-----------------------------------------------------------------------------
String app::getLiveData(void) {
String app::getLiveData(void)
{
String modHtml;
for(uint8_t id = 0; id < mSys->getNumInverters(); id++) {
for (uint8_t id = 0; id < mSys->getNumInverters(); id++)
{
Inverter<> *iv = mSys->getInverterByPos(id);
if(NULL != iv) {
if (NULL != iv)
{
#ifdef LIVEDATA_VISUALIZED
uint8_t modNum, pos;
switch(iv->type) {
default:
case INV_TYPE_1CH: modNum = 1; break;
case INV_TYPE_2CH: modNum = 2; break;
case INV_TYPE_4CH: modNum = 4; break;
switch (iv->type)
{
default:
case INV_TYPE_1CH:
modNum = 1;
break;
case INV_TYPE_2CH:
modNum = 2;
break;
case INV_TYPE_4CH:
modNum = 4;
break;
}
modHtml += F("<div class=\"iv\">"
"<div class=\"ch-iv\"><span class=\"head\">") + String(iv->name) + F(" Limit ") + String(iv->powerLimit[0]);
if (iv->powerLimit[1] & 0x0001){
"<div class=\"ch-iv\"><span class=\"head\">") +
String(iv->name) + F(" Limit ") + String(iv->actPowerLimit);
if (true)
{ // live Power Limit from inverter is always in %
modHtml += F(" %</span>");
} else {
}
else
{
modHtml += F(" W</span>");
}
uint8_t list[] = {FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_PCT, FLD_T, FLD_YT, FLD_YD, FLD_PDC, FLD_EFF, FLD_PRA, FLD_ALARM_MES_ID};
for(uint8_t fld = 0; fld < 12; fld++) {
for (uint8_t fld = 0; fld < 12; fld++)
{
pos = (iv->getPosByChFld(CH0, list[fld]));
if(0xff != pos) {
if (0xff != pos)
{
modHtml += F("<div class=\"subgrp\">");
modHtml += F("<span class=\"value\">") + String(iv->getValue(pos));
modHtml += F("<span class=\"unit\">") + String(iv->getUnit(pos)) + F("</span></span>");
@ -594,23 +576,39 @@ String app::getLiveData(void) {
}
modHtml += "</div>";
for(uint8_t ch = 1; ch <= modNum; ch ++) {
for (uint8_t ch = 1; ch <= modNum; ch++)
{
modHtml += F("<div class=\"ch\"><span class=\"head\">");
if(iv->chName[ch-1][0] == 0)
if (iv->chName[ch - 1][0] == 0)
modHtml += F("CHANNEL ") + String(ch);
else
modHtml += String(iv->chName[ch-1]);
modHtml += String(iv->chName[ch - 1]);
modHtml += F("</span>");
for(uint8_t j = 0; j < 6; j++) {
switch(j) {
default: pos = (iv->getPosByChFld(ch, FLD_UDC)); break;
case 1: pos = (iv->getPosByChFld(ch, FLD_IDC)); break;
case 2: pos = (iv->getPosByChFld(ch, FLD_PDC)); break;
case 3: pos = (iv->getPosByChFld(ch, FLD_YD)); break;
case 4: pos = (iv->getPosByChFld(ch, FLD_YT)); break;
case 5: pos = (iv->getPosByChFld(ch, FLD_IRR)); break;
for (uint8_t j = 0; j < 6; j++)
{
switch (j)
{
default:
pos = (iv->getPosByChFld(ch, FLD_UDC));
break;
case 1:
pos = (iv->getPosByChFld(ch, FLD_IDC));
break;
case 2:
pos = (iv->getPosByChFld(ch, FLD_PDC));
break;
case 3:
pos = (iv->getPosByChFld(ch, FLD_YD));
break;
case 4:
pos = (iv->getPosByChFld(ch, FLD_YT));
break;
case 5:
pos = (iv->getPosByChFld(ch, FLD_IRR));
break;
}
if(0xff != pos) {
if (0xff != pos)
{
modHtml += F("<span class=\"value\">") + String(iv->getValue(pos));
modHtml += F("<span class=\"unit\">") + String(iv->getUnit(pos)) + F("</span></span>");
modHtml += F("<span class=\"info\">") + String(iv->getFieldName(pos)) + F("</span>");
@ -625,7 +623,8 @@ String app::getLiveData(void) {
// dump all data to web frontend
modHtml = F("<pre>");
char topic[30], val[10];
for(uint8_t i = 0; i < iv->listLen; i++) {
for (uint8_t i = 0; i < iv->listLen; i++)
{
snprintf(topic, 30, "%s/ch%d/%s", iv->name, iv->assign[i].ch, iv->getFieldName(i));
snprintf(val, 10, "%.3f %s", iv->getValue(i), iv->getUnit(i));
modHtml += String(topic) + ": " + String(val) + "\n";
@ -637,7 +636,6 @@ String app::getLiveData(void) {
return modHtml;
}
//-----------------------------------------------------------------------------
String app::getJson(void) {
DPRINTLN(DBG_VERBOSE, F("app::showJson"));
@ -854,7 +852,6 @@ void app::loadEEpconfig(void) {
// it is "doppelt-gemoppelt" because the inverter shall remember the setting if the dtu makes a power cycle / reboot
if (iv->powerLimit[0] != 0xffff) {
iv->devControlCmd = ActivePowerContr; // set active power limit
iv->devControlRequest = true; // set to true to update the active power limit from setup html page
if (iv->powerLimit[1] & 0x0001){
DPRINTLN(DBG_INFO, F("add inverter: ") + String(name) + ", SN: " + String(invSerial, HEX) + ", Power Limit: " + String(iv->powerLimit[0]) + " in %");
} else {

23
tools/esp8266/app.h

@ -21,7 +21,7 @@
#include "CircularBuffer.h"
#include "hmSystem.h"
#include "mqtt.h"
#include "wifi.h"
#include "ahoywifi.h"
#include "web.h"
// hier läst sich das Verhalten der app in Bezug auf MQTT
@ -56,7 +56,7 @@ typedef struct {
} invPayload_t;
class wifi;
class ahoywifi;
class web;
class app {
@ -200,10 +200,19 @@ class app {
void stats(void) {
DPRINTLN(DBG_VERBOSE, F("main.h:stats"));
uint32_t free;
uint16_t max;
uint8_t frag;
ESP.getHeapStats(&free, &max, &frag);
#ifdef ESP8266
uint32_t free;
uint16_t max;
uint8_t frag;
ESP.getHeapStats(&free, &max, &frag);
#elif defined(ESP32)
uint32_t free;
uint32_t max;
uint8_t frag;
free = ESP.getFreeHeap();
max = ESP.getMaxAllocHeap();
frag = 0;
#endif
DPRINT(DBG_VERBOSE, F("free: ") + String(free));
DPRINT(DBG_VERBOSE, F(" - max: ") + String(max) + "%");
DPRINTLN(DBG_VERBOSE, F(" - frag: ") + String(frag));
@ -224,7 +233,7 @@ class app {
bool mShowRebootRequest;
wifi *mWifi;
ahoywifi *mWifi;
web *mWebInst;
sysConfig_t mSysConfig;
config_t mConfig;

2
tools/esp8266/config.h

@ -58,7 +58,7 @@
#define MAX_RF_PAYLOAD_SIZE 32
// maximum total payload buffers (must be greater than the number of received frame fragments)
#define MAX_PAYLOAD_ENTRIES 4
#define MAX_PAYLOAD_ENTRIES 10
// maximum requests for retransmits per payload (per inverter)
#define DEF_MAX_RETRANS_PER_PYLD 5

38
tools/esp8266/defines.h

@ -13,7 +13,7 @@
//-------------------------------------
#define VERSION_MAJOR 0
#define VERSION_MINOR 5
#define VERSION_PATCH 14
#define VERSION_PATCH 15
//-------------------------------------
@ -25,22 +25,22 @@ typedef struct {
typedef enum {
InverterDevInform_Simple = 0, // 0x00
InverterDevInform_All = 1, // 0x01
//GridOnProFilePara = 2, // 0x02
//HardWareConfig = 3, // 0x03
//SimpleCalibrationPara = 4, // 0x04
//SystemConfigPara = 5, // 0x05
GridOnProFilePara = 2, // 0x02
HardWareConfig = 3, // 0x03
SimpleCalibrationPara = 4, // 0x04
SystemConfigPara = 5, // 0x05
RealTimeRunData_Debug = 11, // 0x0b
//RealTimeRunData_Reality = 12, // 0x0c
//RealTimeRunData_A_Phase = 13, // 0x0d
//RealTimeRunData_B_Phase = 14, // 0x0e
//RealTimeRunData_C_Phase = 15, // 0x0f
RealTimeRunData_Reality = 12, // 0x0c
RealTimeRunData_A_Phase = 13, // 0x0d
RealTimeRunData_B_Phase = 14, // 0x0e
RealTimeRunData_C_Phase = 15, // 0x0f
AlarmData = 17, // 0x11, Alarm data - all unsent alarms
AlarmUpdate = 18, // 0x12, Alarm data - all pending alarms
//RecordData = 19, // 0x13
//InternalData = 20, // 0x14
RecordData = 19, // 0x13
InternalData = 20, // 0x14
GetLossRate = 21, // 0x15
//GetSelfCheckState = 30, // 0x1e
//InitDataState = 0xff
GetSelfCheckState = 30, // 0x1e
InitDataState = 0xff
} InfoCmdType;
typedef enum {
@ -109,15 +109,16 @@ typedef enum { // ToDo: to be verified by field tests
#define SER_DEBUG_LEN 1 // uint8_t
#define SER_INTERVAL_LEN 2 // uint16_t
#pragma pack(push) // push current alignment to stack
#pragma pack(1) // set alignment to 1 byte boundary
typedef struct {
char broker[MQTT_ADDR_LEN];
uint16_t port;
char user[MQTT_USER_LEN];
char pwd[MQTT_PWD_LEN];
char topic[MQTT_TOPIC_LEN];
} mqttConfig_t;
} /*__attribute__((__packed__))*/ mqttConfig_t;
#pragma pack(pop) // restore original alignment from stack
typedef struct {
char deviceName[DEVNAME_LEN];
@ -126,6 +127,8 @@ typedef struct {
char stationPwd[PWD_LEN];
} sysConfig_t;
#pragma pack(push) // push current alignment to stack
#pragma pack(1) // set alignment to 1 byte boundary
typedef struct {
// nrf24
uint16_t sendInterval;
@ -146,7 +149,8 @@ typedef struct {
uint16_t serialInterval;
bool serialShowIv;
bool serialDebug;
} config_t;
} /*__attribute__((__packed__))*/ config_t;
#pragma pack(pop) // restore original alignment from stack
#define CFG_MQTT_LEN MQTT_ADDR_LEN + 2 + MQTT_USER_LEN + MQTT_PWD_LEN +MQTT_TOPIC_LEN

14
tools/esp8266/eep.h

@ -8,11 +8,23 @@
#include "Arduino.h"
#include <EEPROM.h>
#ifdef ESP32
#include <nvs_flash.h>
#endif
class eep {
public:
eep() {
EEPROM.begin(4096);
#ifdef ESP32
if(!EEPROM.begin(4096)) {
nvs_flash_init();
EEPROM.begin(4096);
}
#else
EEPROM.begin(4096);
#endif
}
~eep() {
EEPROM.end();

10
tools/esp8266/hmDefines.h

@ -23,9 +23,9 @@ const char* const units[] = {"V", "A", "W", "Wh", "kWh", "Hz", "°C", "%","VAr",
// field types
enum {FLD_UDC = 0, FLD_IDC, FLD_PDC, FLD_YD, FLD_YW, FLD_YT,
FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_T, FLD_PCT, FLD_EFF, FLD_IRR, FLD_PRA,FLD_ALARM_MES_ID,FLD_FW_VERSION,FLD_FW_BUILD_YEAR,FLD_FW_BUILD_MONTH_DAY,FLD_HW_ID};
FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_T, FLD_PCT, FLD_EFF, FLD_IRR, FLD_PRA,FLD_ALARM_MES_ID,FLD_FW_VERSION,FLD_FW_BUILD_YEAR,FLD_FW_BUILD_MONTH_DAY,FLD_HW_ID,FLD_ACT_PWR_LIMIT};
const char* const fields[] = {"U_DC", "I_DC", "P_DC", "YieldDay", "YieldWeek", "YieldTotal",
"U_AC", "I_AC", "P_AC", "Freq", "Temp", "Pct", "Efficiency", "Irradiation","P_ACr","ALARM_MES_ID","FWVersion","FWBuildYear","FWBuildMonthDay","HWPartId"};
"U_AC", "I_AC", "P_AC", "Freq", "Temp", "Pct", "Efficiency", "Irradiation","P_ACr","ALARM_MES_ID","FWVersion","FWBuildYear","FWBuildMonthDay","HWPartId","PowerLimit"};
// mqtt discovery device classes
enum {DEVICE_CLS_NONE = 0, DEVICE_CLS_CURRENT, DEVICE_CLS_ENERGY, DEVICE_CLS_PWR, DEVICE_CLS_VOLTAGE, DEVICE_CLS_FREQ, DEVICE_CLS_TEMP};
@ -92,6 +92,12 @@ const byteAssign_t InfoAssignment[] = {
};
#define HMINFO_LIST_LEN (sizeof(InfoAssignment) / sizeof(byteAssign_t))
const byteAssign_t SystemConfigParaAssignment[] = {
{ FLD_ACT_PWR_LIMIT, UNIT_PCT, CH0, 2, 2, 10 }
};
#define HMSYSTEM_LIST_LEN (sizeof(SystemConfigParaAssignment) / sizeof(byteAssign_t))
//-------------------------------------

134
tools/esp8266/hmInverter.h

@ -6,7 +6,14 @@
#ifndef __HM_INVERTER_H__
#define __HM_INVERTER_H__
#if defined(ESP32) && defined(F)
#undef F
#define F(sl) (sl)
#endif
#include "hmDefines.h"
#include <memory>
#include <queue>
/**
* For values which are of interest and not transmitted by the inverter can be
@ -46,9 +53,35 @@ template<class T=float>
struct calcFunc_t {
uint8_t funcId; // unique id
func_t<T>* func; // function pointer
} ;
};
class CommandAbstract {
public:
CommandAbstract(uint8_t txType = 0, uint8_t cmd = 0){
_TxType = txType;
_Cmd = cmd;
};
virtual ~CommandAbstract() {};
const uint8_t getCmd()
{
return _Cmd;
}
protected:
uint8_t _TxType;
uint8_t _Cmd;
};
class InfoCommand : public CommandAbstract {
public:
InfoCommand(uint8_t cmd){
_TxType = 0x15;
_Cmd = cmd;
}
};
// list of all available functions, mapped in hmDefines.h
template<class T=float>
const calcFunc_t<T> calcFunctions[] = {
@ -72,6 +105,7 @@ class Inverter {
uint16_t alarmMesIndex; // Last recorded Alarm Message Index
uint16_t fwVersion; // Firmware Version from Info Command Request
uint16_t powerLimit[2]; // limit power output
uint16_t actPowerLimit; //
uint8_t devControlCmd; // carries the requested cmd
bool devControlRequest; // true if change needed
serial_u serial; // serial number as on barcode
@ -87,6 +121,7 @@ class Inverter {
ts = 0;
powerLimit[0] = 0xffff; // 65535 W Limit -> unlimited
powerLimit[1] = 0x0000; //
actPowerLimit = 0xffff; // init feedback from inverter to -1
devControlRequest = false;
devControlCmd = 0xff;
initialized = false;
@ -97,6 +132,30 @@ class Inverter {
// TODO: cleanup
}
template <typename T>
void enqueCommand(uint8_t cmd)
{
_commandQueue.push(std::make_shared<T>(cmd));
DPRINTLN(DBG_INFO, "enqueuedCmd: " + String(cmd));
}
void setQueuedCmdFinished(){
if (!_commandQueue.empty()){
_commandQueue.pop(); // Will destroy CommandAbstract Class Object (?)
}
}
uint8_t getQueuedCmd()
{
if (_commandQueue.empty()){
// Fill with default commands
enqueCommand<InfoCommand>(RealTimeRunData_Debug);
//enqueCommand<InfoCommand>(SystemConfigPara);
}
return _commandQueue.front().get()->getCmd();
}
void init(void) {
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:init"));
getAssignment();
@ -105,6 +164,8 @@ class Inverter {
memset(name, 0, MAX_NAME_LENGTH);
memset(chName, 0, MAX_NAME_LENGTH * 4);
memset(record, 0, sizeof(RECORDTYPE) * listLen);
enqueCommand<InfoCommand>(InverterDevInform_All);
enqueCommand<InfoCommand>(SystemConfigPara);
initialized = true;
}
@ -133,8 +194,9 @@ class Inverter {
return assign[pos].ch;
}
void addValue(uint8_t pos, uint8_t buf[],uint8_t cmd) {
void addValue(uint8_t pos, uint8_t buf[]) {
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:addValue"));
uint8_t cmd = getQueuedCmd();
uint8_t ptr = assign[pos].start;
uint8_t end = ptr + assign[pos].num;
uint16_t div = assign[pos].div;
@ -144,8 +206,7 @@ class Inverter {
val <<= 8;
val |= buf[ptr];
} while(++ptr != end);
record[pos] = (RECORDTYPE)(val) / (RECORDTYPE)(div);
record[pos] = (RECORDTYPE)(val) / (RECORDTYPE)(div);
}
if (cmd == RealTimeRunData_Debug) {
// get last alarm message index and save it in the inverter object
@ -160,6 +221,13 @@ class Inverter {
DPRINT(DBG_DEBUG, F("Inverter FW-Version: ") + String(fwVersion));
}
}
if (cmd == SystemConfigPara) {
// get at least the firmware version and save it to the inverter object
if (getPosByChFld(0, FLD_ACT_PWR_LIMIT) == pos){
actPowerLimit = record[pos];
DPRINT(DBG_DEBUG, F("Inverter actual power limit: ") + String(actPowerLimit));
}
}
}
RECORDTYPE getValue(uint8_t pos) {
@ -167,9 +235,10 @@ class Inverter {
return record[pos];
}
void doCalculations(uint8_t cmd=RealTimeRunData_Debug) {
void doCalculations() {
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:doCalculations"));
getAssignment(cmd);
uint8_t cmd = getQueuedCmd();
getAssignment();
if (cmd == RealTimeRunData_Debug){
for(uint8_t i = 0; i < listLen; i++) {
if(CMD_CALC == assign[i].div) {
@ -199,37 +268,52 @@ class Inverter {
return ts;
}
void getAssignment(uint8_t cmd=RealTimeRunData_Debug) {
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:getAssignment"));
if(cmd == RealTimeRunData_Debug){
if(INV_TYPE_1CH == type) {
listLen = (uint8_t)(HM1CH_LIST_LEN);
assign = (byteAssign_t*)hm1chAssignment;
void getAssignment() {
DPRINTLN(DBG_DEBUG, F("hmInverter.h:getAssignment"));
uint8_t cmd = getQueuedCmd();
switch (cmd)
{
case RealTimeRunData_Debug:
if (INV_TYPE_1CH == type)
{
listLen = (uint8_t)(HM1CH_LIST_LEN);
assign = (byteAssign_t *)hm1chAssignment;
channels = 1;
}
else if(INV_TYPE_2CH == type) {
listLen = (uint8_t)(HM2CH_LIST_LEN);
assign = (byteAssign_t*)hm2chAssignment;
else if (INV_TYPE_2CH == type)
{
listLen = (uint8_t)(HM2CH_LIST_LEN);
assign = (byteAssign_t *)hm2chAssignment;
channels = 2;
}
else if(INV_TYPE_4CH == type) {
listLen = (uint8_t)(HM4CH_LIST_LEN);
assign = (byteAssign_t*)hm4chAssignment;
else if (INV_TYPE_4CH == type)
{
listLen = (uint8_t)(HM4CH_LIST_LEN);
assign = (byteAssign_t *)hm4chAssignment;
channels = 4;
}
else {
listLen = 0;
else
{
listLen = 0;
channels = 0;
assign = NULL;
assign = NULL;
}
}
if(cmd == InverterDevInform_All){
listLen = (uint8_t)(HMINFO_LIST_LEN);
assign = (byteAssign_t*)InfoAssignment;
break;
case InverterDevInform_All:
listLen = (uint8_t)(HMINFO_LIST_LEN);
assign = (byteAssign_t *)InfoAssignment;
break;
case SystemConfigPara:
listLen = (uint8_t)(HMSYSTEM_LIST_LEN);
assign = (byteAssign_t *)SystemConfigParaAssignment;
break;
default:
DPRINTLN(DBG_INFO, "Parser not implemented");
}
}
private:
std::queue<std::shared_ptr<CommandAbstract>> _commandQueue;
void toRadioId(void) {
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:toRadioId"));
radioId.u64 = 0ULL;

2
tools/esp8266/hmSystem.h

@ -19,12 +19,10 @@ class HmSystem {
RadioType Radio;
typedef BUFFER BufferType;
BufferType BufCtrl;
InfoCmdType InfoCmd;
//DevControlCmdType DevControlCmd;
HmSystem() {
mNumInv = 0;
InfoCmd = RealTimeRunData_Debug; // default case
}
~HmSystem() {
// TODO: cleanup

2
tools/esp8266/html/h/index_html.h

@ -1,4 +1,4 @@
#ifndef __INDEX_HTML_H__
#define __INDEX_HTML_H__
const char index_html[] PROGMEM = "<!doctype html><html><head><title>Index - {DEVICE}</title><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"/><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><script type=\"text/javascript\">getAjax('/uptime', 'uptime');getAjax('/cmdstat', 'cmds');window.setInterval(\"getAjax('/uptime', 'uptime')\", {JS_TS});window.setInterval(\"getAjax('/cmdstat', 'cmds')\", {JS_TS});function getAjax(url, resid) {var http = null;http = new XMLHttpRequest();if(http != null) {http.open(\"GET\", url, true);http.onreadystatechange = print;http.send(null);}function print() {if(http.readyState == 4) {document.getElementById(resid).innerHTML = http.responseText;}}}</script></head><body><h1>AHOY - {DEVICE}</h1><div id=\"content\" class=\"content\"><p><a href=\"/visualization\">Visualization</a><br/><br/><a href=\"/setup\">Setup</a><br/></p><p><span class=\"des\">Uptime: </span><span id=\"uptime\"></span></p><p><span class=\"des\">Statistics: </span><pre id=\"cmds\"></pre></p><p>Every {TS}seconds the values are updated</p><div id=\"note\">This project was started from <a href=\"https://www.mikrocontroller.net/topic/525778\" target=\"_blank\">this discussion. (Mikrocontroller.net)</a><br/>New updates can be found on Github: <a href=\"https://github.com/grindylow/ahoy\" target=\"_blank\">https://github.com/grindylow/ahoy</a><br/><br/>Please report issues using the feature provided by <a href=\"https://github.com/grindylow/ahoy/issues\">Github</a><br/><br/>Discuss with us on <a href=\"https://discord.gg/WzhxEY62mB\">Discord</a><br/><p class=\"lic\"><a href=\"https://creativecommons.org/licenses/by-nc-sa/3.0/de\">Creative Commons - https://creativecommons.org/licenses/by-nc-sa/3.0/de/</a><br/>Check the licenses which are published on <a href=\"https://github.com/grindylow/ahoy\">https://github.com/grindylow/ahoy</a>as well</p></div></div><div id=\"footer\"><p class=\"left\">&copy 2022</p><p class=\"left\"><a href=\"/update\">Update Firmware</a></p><p class=\"right\">AHOY :: {VERSION}</p><p class=\"right\"><a href=\"/reboot\">Reboot</a></p><p class=\"right\">Git SHA: {BUILD}</p></div></body></html>";
const char index_html[] PROGMEM = "<!doctype html><html><head><title>Index - {DEVICE}</title><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"/><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><script type=\"text/javascript\">getAjax('/uptime', 'uptime');getAjax('/cmdstat', 'cmds');window.setInterval(\"getAjax('/uptime', 'uptime')\", {JS_TS});window.setInterval(\"getAjax('/cmdstat', 'cmds')\", {JS_TS});function getAjax(url, resid) {var http = null;http = new XMLHttpRequest();if(http != null) {http.open(\"GET\", url, true);http.onreadystatechange = print;http.send(null);}function print() {if(http.readyState == 4) {document.getElementById(resid).innerHTML = http.responseText;}}}function getInverterInfo(data){var http = null;http = new XMLHttpRequest();if(http != null) {http.open(\"POST\", \"/api\");http.setRequestHeader(\"Accept\", \"application/json\");http.setRequestHeader(\"Content-Type\", \"application/json\");http.send(data);}}</script></head><body><h1>AHOY - {DEVICE}</h1><div id=\"content\" class=\"content\"><p><a href=\"/visualization\">Visualization</a><br/><br/><a href=\"/setup\">Setup</a><br/></p><p><span class=\"des\">Uptime: </span><span id=\"uptime\"></span></p><p><span class=\"des\">Statistics: </span><pre id=\"cmds\"></pre></p><p>Every {TS}seconds the values are updated</p><div id=\"note\">This project was started from <a href=\"https://www.mikrocontroller.net/topic/525778\" target=\"_blank\">this discussion. (Mikrocontroller.net)</a><br/>New updates can be found on Github: <a href=\"https://github.com/grindylow/ahoy\" target=\"_blank\">https://github.com/grindylow/ahoy</a><br/><br/>Please report issues using the feature provided by <a href=\"https://github.com/grindylow/ahoy/issues\">Github</a><br/><br/>Discuss with us on <a href=\"https://discord.gg/WzhxEY62mB\">Discord</a><br/><p class=\"lic\"><a href=\"https://creativecommons.org/licenses/by-nc-sa/3.0/de\">Creative Commons - https://creativecommons.org/licenses/by-nc-sa/3.0/de/</a><br/>Check the licenses which are published on <a href=\"https://github.com/grindylow/ahoy\">https://github.com/grindylow/ahoy</a>as well</p></div></div><div id=\"footer\"><p class=\"left\">&copy 2022</p><p class=\"left\"><a href=\"/update\">Update Firmware</a></p><p class=\"right\">AHOY :: {VERSION}</p><p class=\"right\"><a href=\"/reboot\">Reboot</a></p><p class=\"right\">Git SHA: {BUILD}</p></div></body></html>";
#endif /*__INDEX_HTML_H__*/

2
tools/esp8266/html/h/setup_html.h

File diff suppressed because one or more lines are too long

2
tools/esp8266/html/h/style_css.h

@ -1,4 +1,4 @@
#ifndef __STYLE_CSS_H__
#define __STYLE_CSS_H__
const char style_css[] PROGMEM = "h1 {margin:0;padding:20pt;font-size:22pt;color:#fff;background-color:#006ec0;display:block;text-transform:uppercase;}html, body {font-family:Arial;margin:0;padding:0;}p {text-align:justify;font-size:13pt;}p.lic, p.lic a {font-size:8pt;color:#999;}.des {margin-top:20px;font-size:13pt;color:#006ec0;}.s_active, .s_collapsible:hover {background-color:#006ec0;}.s_content {display:none;overflow:hidden;margin-bottom:20px;}.s_collapsible {background-color:#044e86;color:white;cursor:pointer;padding:18px;width:100%;border:none;text-align:left;outline:none;font-size:15px;margin-bottom:4px;}.subdes {font-size:12pt;color:#006ec0;margin-left:7px;}.subsubdes {font-size:12pt;color:#006ec0;margin:0 0 7px 12px;}.hide {display:none;}a:link, a:visited {text-decoration:none;font-size:13pt;color:#006ec0;}a:hover, a:focus {color:#f00;}a.erase {background-color:#006ec0;color:#fff;padding:7px;display:inline-block;margin-top:30px;float:right;}#content {padding:15px 15px 60px 15px;}#footer {position:fixed;bottom:0px;height:45px;background-color:#006ec0;width:100%;border-top:5px solid #fff;}#footer p, #footer a {color:#fff;padding:0 7px 0 7px;font-size:10pt !important;}div.content {background-color:#fff;padding-bottom:65px;overflow:auto;}input, select {padding:7px;font-size:13pt;}input.text, select {width:70%;box-sizing:border-box;margin-bottom:10px;border:1px solid #ccc;}input.sh {max-width:150px !important;margin-right:10px;}input.btn {background-color:#006ec0;color:#fff;border:0px;float:right;margin:10px 0 30px;text-transform:uppercase;}input.cb {margin-bottom:20px;}label {width:20%;display:inline-block;font-size:12pt;padding-right:10px;margin-left:15px;}.left {float:left;}.right {float:right;}div.ch-iv {width:100%;background-color:#32b004;display:inline-block;margin-bottom:15px;padding-bottom:20px;overflow:auto;}div.ch {width:220px;min-height:350px;background-color:#006ec0;display:inline-block;margin:0 10px 15px 10px;overflow:auto;padding-bottom:20px;}div.ch .value, div.ch .info, div.ch .head, div.ch-iv .value, div.ch-iv .info, div.ch-iv .head {color:#fff;display:block;width:100%;text-align:center;}.subgrp {float:left;width:220px;}div.ch .unit, div.ch-iv .unit {font-size:19px;margin-left:10px;}div.ch .value, div.ch-iv .value {margin-top:20px;font-size:24px;}div.ch .info, div.ch-iv .info {margin-top:3px;font-size:10px;}div.ch .head {background-color:#003c80;padding:10px 0 10px 0;}div.ch-iv .head {background-color:#1c6800;padding:10px 0 10px 0;}div.iv {max-width:960px;margin-bottom:40px;}div.ts {font-size:13px;background-color:#ddd;border-top:7px solid #999;padding:7px;}#note {margin:50px 10px 10px 10px;padding-top:10px;width:100%;border-top:1px solid #bbb;}@media(max-width:500px) {div.ch .unit, div.ch-iv .unit {font-size:18px;}div.ch {width:170px;min-height:100px }.subgrp {width:180px;}}";
const char style_css[] PROGMEM = "h1 {margin:0;padding:20pt;font-size:22pt;color:#fff;background-color:#006ec0;display:block;text-transform:uppercase;}html, body {font-family:Arial;margin:0;padding:0;}p {text-align:justify;font-size:13pt;}p.lic, p.lic a {font-size:8pt;color:#999;}.des {margin-top:20px;font-size:13pt;color:#006ec0;}.s_active, .s_collapsible:hover {background-color:#006ec0;}.s_content {display:none;overflow:hidden;}.s_collapsible {background-color:#044e86;color:white;cursor:pointer;padding:18px;width:100%;border:none;text-align:left;outline:none;font-size:15px;margin-bottom:4px;}.subdes {font-size:12pt;color:#006ec0;margin-left:7px;}.subsubdes {font-size:12pt;color:#006ec0;margin:0 0 7px 12px;}.hide {display:none;}a:link, a:visited {text-decoration:none;font-size:13pt;color:#006ec0;}a:hover, a:focus {color:#f00;}a.erase {background-color:#006ec0;color:#fff;padding:7px;display:inline-block;margin-top:30px;}#content {padding:15px 15px 60px 15px;}#footer {position:fixed;bottom:0px;height:45px;background-color:#006ec0;width:100%;border-top:5px solid #fff;}#footer p, #footer a {color:#fff;padding:0 7px 0 7px;font-size:10pt !important;}div.content {background-color:#fff;padding-bottom:65px;overflow:auto;}input, select {padding:7px;font-size:13pt;}input.text, select {width:70%;box-sizing:border-box;margin-bottom:10px;border:1px solid #ccc;}input.sh {max-width:150px !important;margin-right:10px;}input.btn {background-color:#006ec0;color:#fff;border:0px;float:right;margin:10px 0 30px;text-transform:uppercase;}input.cb {margin-bottom:20px;}label {width:20%;display:inline-block;font-size:12pt;padding-right:10px;margin:10px 0px 0px 15px;vertical-align:top;}fieldset {margin-bottom:15px;}.left {float:left;}.right {float:right;}div.ch-iv {width:100%;background-color:#32b004;display:inline-block;margin-bottom:15px;padding-bottom:20px;overflow:auto;}div.ch {width:220px;min-height:350px;background-color:#006ec0;display:inline-block;margin:0 10px 15px 10px;overflow:auto;padding-bottom:20px;}div.ch .value, div.ch .info, div.ch .head, div.ch-iv .value, div.ch-iv .info, div.ch-iv .head {color:#fff;display:block;width:100%;text-align:center;}.subgrp {float:left;width:220px;}div.ch .unit, div.ch-iv .unit {font-size:19px;margin-left:10px;}div.ch .value, div.ch-iv .value {margin-top:20px;font-size:24px;}div.ch .info, div.ch-iv .info {margin-top:3px;font-size:10px;}div.ch .head {background-color:#003c80;padding:10px 0 10px 0;}div.ch-iv .head {background-color:#1c6800;padding:10px 0 10px 0;}div.iv {max-width:960px;margin-bottom:40px;}div.ts {font-size:13px;background-color:#ddd;border-top:7px solid #999;padding:7px;}div.modpwr, div.modname {width:70%;display:inline-block;}#note {margin:50px 10px 10px 10px;padding-top:10px;width:100%;border-top:1px solid #bbb;}@media(max-width:500px) {div.ch .unit, div.ch-iv .unit {font-size:18px;}div.ch {width:170px;min-height:100px }.subgrp {width:180px;}}";
#endif /*__STYLE_CSS_H__*/

10
tools/esp8266/html/index.html

@ -25,6 +25,16 @@
}
}
}
function getInverterInfo(data){
var http = null;
http = new XMLHttpRequest();
if(http != null) {
http.open("POST", "/api");
http.setRequestHeader("Accept", "application/json");
http.setRequestHeader("Content-Type", "application/json");
http.send(data);
}
}
</script>
</head>
<body>

9
tools/esp8266/html/setup.html

@ -55,9 +55,11 @@
<a class="erase" href="/erase">ERASE SETTINGS (not WiFi)</a>
<form method="post" action="{IP}/save">
<p class="des">Device Host Name</p>
<label for="device">Device Name</label>
<input type="text" class="text" name="device" value="{DEVICE}"/>
<fieldset>
<legend class="des">Device Host Name</legend>
<label for="device">Device Name</label>
<input type="text" class="text" name="device" value="{DEVICE}"/>
</fieldset>
<button type="button" class="s_collapsible">WiFi</button>
<div class="s_content">
@ -133,7 +135,6 @@
</fieldset>
</div>
<p class="des">&nbsp;</p>
<label for="reboot">Reboot device after successful save</label>
<input type="checkbox" class="cb" name="reboot"/>
<input type="submit" value="save" class="btn" />

16
tools/esp8266/html/style.css

@ -37,7 +37,6 @@ p.lic, p.lic a {
.s_content {
display: none;
overflow: hidden;
margin-bottom: 20px;
}
@ -86,7 +85,6 @@ a.erase {
padding: 7px;
display: inline-block;
margin-top: 30px;
float: right;
}
#content {
@ -149,7 +147,12 @@ label {
display: inline-block;
font-size: 12pt;
padding-right: 10px;
margin-left: 15px;
margin: 10px 0px 0px 15px;
vertical-align: top;
}
fieldset {
margin-bottom: 15px;
}
.left {
@ -179,7 +182,7 @@ div.ch {
padding-bottom: 20px;
}
div.ch .value, div.ch .info, div.ch .head, div.ch-iv .value, div.ch-iv .info, div.ch-iv .head {
div.ch .value, div.ch .info, div.ch .head, div.ch-iv .value, div.ch-iv .info, div.ch-iv .head {
color: #fff;
display: block;
width: 100%;
@ -228,6 +231,11 @@ div.ts {
padding: 7px;
}
div.modpwr, div.modname {
width:70%;
display: inline-block;
}
#note {
margin: 50px 10px 10px 10px;
padding-top: 10px;

4
tools/esp8266/include/dbg.h

@ -5,6 +5,10 @@
#ifndef __DBG_H__
#define __DBG_H__
#if defined(ESP32) && defined(F)
#undef F
#define F(sl) (sl)
#endif
//-----------------------------------------------------------------------------
// available levels

36
tools/esp8266/mqtt.h

@ -6,7 +6,16 @@
#ifndef __MQTT_H__
#define __MQTT_H__
#include <ESP8266WiFi.h>
#ifdef ESP8266
#include <ESP8266WiFi.h>
#elif defined(ESP32)
#include <WiFi.h>
#endif
#if defined(ESP32) && defined(F)
#undef F
#define F(sl) (sl)
#endif
#include <PubSubClient.h>
#include "defines.h"
@ -54,7 +63,7 @@ class mqtt {
bool isConnected(bool doRecon = false) {
//DPRINTLN(DBG_VERBOSE, F("mqtt.h:isConnected"));
if(doRecon)
if(doRecon && !mClient->connected())
reconnect();
return mClient->connected();
}
@ -70,7 +79,12 @@ class mqtt {
void reconnect(void) {
DPRINTLN(DBG_DEBUG, F("mqtt.h:reconnect"));
DPRINTLN(DBG_DEBUG, F("MQTT mClient->_state ") + String(mClient->state()) );
DPRINTLN(DBG_DEBUG, F("WIFI mEspClient.status ") + String(mEspClient.status()) );
#ifdef ESP8266
DPRINTLN(DBG_DEBUG, F("WIFI mEspClient.status ") + String(mEspClient.status()) );
#endif
boolean resub = false;
if(!mClient->connected()) {
if(strlen(mDevName) > 0) {
// der Server und der Port müssen neu gesetzt werden,
@ -78,16 +92,18 @@ class mqtt {
mClient->setServer(mCfg->broker, mCfg->port);
mClient->setBufferSize(MQTT_MAX_PACKET_SIZE);
if((strlen(mCfg->user) > 0) && (strlen(mCfg->pwd) > 0))
mClient->connect(mDevName, mCfg->user, mCfg->pwd);
resub = mClient->connect(mDevName, mCfg->user, mCfg->pwd);
else
mClient->connect(mDevName);
resub = mClient->connect(mDevName);
}
// ein Subscribe ist nur nach einem connect notwendig
char topic[MQTT_TOPIC_LEN + 13 ]; // "/devcontrol/#" --> + 6 byte
// ToDo: "/devcontrol/#" is hardcoded
snprintf(topic, MQTT_TOPIC_LEN + 13, "%s/devcontrol/#", mCfg->topic);
DPRINTLN(DBG_INFO, F("subscribe to ") + String(topic));
mClient->subscribe(topic); // subscribe to mTopic + "/devcontrol/#"
if(resub) {
char topic[MQTT_TOPIC_LEN + 13 ]; // "/devcontrol/#" --> + 6 byte
// ToDo: "/devcontrol/#" is hardcoded
snprintf(topic, MQTT_TOPIC_LEN + 13, "%s/devcontrol/#", mCfg->topic);
DPRINTLN(DBG_INFO, F("subscribe to ") + String(topic));
mClient->subscribe(topic); // subscribe to mTopic + "/devcontrol/#"
}
}
}

30
tools/esp8266/platformio.ini

@ -12,10 +12,7 @@
src_dir = .
[env]
platform = espressif8266
framework = arduino
board = esp12e
board_build.f_cpu = 80000000L
; ;;;;; Possible Debug options ;;;;;;
; https://docs.platformio.org/en/latest/platforms/espressif8266.html#debug-level
@ -46,18 +43,41 @@ lib_deps =
;esp8266/Ticker@^1.0
[env:esp8266-release]
platform = espressif8266
board = esp12e
board_build.f_cpu = 80000000L
build_flags = -D RELEASE
monitor_filters =
;default ; Remove typical terminal control codes from input
time ; Add timestamp with milliseconds for each new line
;log2file ; Log data to a file “platformio-device-monitor-*.log” located in the current working directory
[env:esp8266-debug]
platform = espressif8266
board = esp12e
board_build.f_cpu = 80000000L
build_flags = -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_OOM -DDEBUG_ESP_PORT=Serial
build_type = debug
monitor_filters =
;default ; Remove typical terminal control codes from input
time ; Add timestamp with milliseconds for each new line
log2file ; Log data to a file “platformio-device-monitor-*.log” located in the current working directory
[env:esp32-wroom32-release]
platform = espressif32
board = lolin_d32
build_flags = -D RELEASE
monitor_filters =
;default ; Remove typical terminal control codes from input
time ; Add timestamp with milliseconds for each new line
;log2file ; Log data to a file “platformio-device-monitor-*.log” located in the current working directory
[env:esp32-wroom32-debug]
platform = espressif32
board = lolin_d32
build_flags = -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_OOM -DDEBUG_ESP_PORT=Serial
build_type = debug
monitor_filters =
;default ; Remove typical terminal control codes from input
time ; Add timestamp with milliseconds for each new line
log2file ; Log data to a file “platformio-device-monitor-*.log” located in the current working directory

13
tools/esp8266/scripts/getVersion.py

@ -9,23 +9,22 @@ def readVersion(path, infile):
today = date.today()
search = ["_MAJOR", "_MINOR", "_PATCH"]
version = today.strftime("%y%m%d") + "_ahoy_"
versionnumber = "ahoy_v"
for line in lines:
if(line.find("VERSION_") != -1):
for s in search:
p = line.find(s)
if(p != -1):
version += line[p+13:].rstrip() + "."
versionnumber += line[p+13:].rstrip() + "."
os.mkdir(path + ".pio/build/out/")
versionout = version[:-1] + "_esp8266_release.bin"
sha = os.getenv("SHA",default="sha")
versionout = version[:-1] + "_esp8266_" + sha + ".bin"
src = path + ".pio/build/esp8266-release/firmware.bin"
dst = path + ".pio/build/out/" + versionout
os.rename(src, dst)
print("::set-output name=name::" + versionnumber[:-1] )
versionout = version[:-1] + "_esp8266_debug.bin"
src = path + ".pio/build/esp8266-debug/firmware.bin"
dst = path + ".pio/build/out/" + versionout
os.rename(src, dst)
readVersion("../", "defines.h")

36
tools/esp8266/web.cpp

@ -3,6 +3,11 @@
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//-----------------------------------------------------------------------------
#if defined(ESP32) && defined(F)
#undef F
#define F(sl) (sl)
#endif
#include "web.h"
#include "html/h/index_html.h"
@ -17,15 +22,22 @@ web::web(app *main, sysConfig_t *sysCfg, config_t *config, char version[]) {
mSysCfg = sysCfg;
mConfig = config;
mVersion = version;
mWeb = new ESP8266WebServer(80);
mUpdater = new ESP8266HTTPUpdateServer();
#ifdef ESP8266
mWeb = new ESP8266WebServer(80);
mUpdater = new ESP8266HTTPUpdateServer();
#elif defined(ESP32)
mWeb = new WebServer(80);
mUpdater = new HTTPUpdateServer();
#endif
mUpdater->setup(mWeb);
}
//-----------------------------------------------------------------------------
void web::setup(void) {
DPRINTLN(DBG_VERBOSE, F("app::setup-begin"));
mWeb->begin();
DPRINTLN(DBG_VERBOSE, F("app::setup-on"));
mWeb->on("/", std::bind(&web::showIndex, this));
mWeb->on("/style.css", std::bind(&web::showCss, this));
mWeb->on("/favicon.ico", std::bind(&web::showFavicon, this));
@ -205,21 +217,22 @@ void web::showSetup(void) {
// UGLY! But I do not know it a better way --//
inv += F("<label for=\"inv") + String(i) + F("ModPwr0\" name=\"lbl") + String(i);
inv += F("ModPwr\">Max Module Power (Wp)</label>");
inv += F("ModPwr\">Max Module Power (Wp)</label><div class=\"modpwr\">");
for(uint8_t j = 0; j < 4; j++) {
inv += F("<input type=\"text\" class=\"text sh\" name=\"inv") + String(i) + F("ModPwr") + String(j) + F("\" value=\"");
if(NULL != iv)
inv += String(iv->chMaxPwr[j]);
inv += F("\"/ maxlength=\"4\">");
}
inv += F("<br/><label for=\"inv") + String(i) + F("ModName0\" name=\"lbl") + String(i);
inv += F("ModName\">Module Name</label>");
inv += F("</div><br/><label for=\"inv") + String(i) + F("ModName0\" name=\"lbl") + String(i);
inv += F("ModName\">Module Name</label><div class=\"modname\">");
for(uint8_t j = 0; j < 4; j++) {
inv += F("<input type=\"text\" class=\"text sh\" name=\"inv") + String(i) + F("ModName") + String(j) + F("\" value=\"");
if(NULL != iv)
inv += String(iv->chName[j]);
inv += F("\"/ maxlength=\"") + String(MAX_NAME_LENGTH) + "\">";
}
inv += F("</div>");
}
html.replace(F("{INVERTERS}"), String(inv));
@ -329,6 +342,7 @@ void web::showSave(void) {
iv->chMaxPwr[j] = mWeb->arg("inv" + String(i) + "ModPwr" + String(j)).toInt() & 0xffff;
mWeb->arg("inv" + String(i) + "ModName" + String(j)).toCharArray(iv->chName[j], MAX_NAME_LENGTH);
}
iv->initialized = true;
}
if(mWeb->arg("invInterval") != "")
mConfig->sendInterval = mWeb->arg("invInterval").toInt();
@ -429,17 +443,19 @@ void web::showWebApi(void)
deserializeJson(response, mWeb->arg("plain"));
// ToDo: error handling for payload
uint8_t iv_id = response["inverter"];
uint8_t cmd = response["cmd"];
Inverter<> *iv = mMain->mSys->getInverterByPos(iv_id);
if (NULL != iv)
{
if (response["tx_request"] == (uint8_t)TX_REQ_INFO)
{
mMain->mSys->InfoCmd = response["cmd"];
mMain->resetPayload(iv); // start request from new
// process payload from web request corresponding to the cmd
if (mMain->mSys->InfoCmd == AlarmData)
// if the AlarmData is requested set the Alarm Index to the requested one
if (cmd == AlarmData){
iv->alarmMesIndex = response["payload"];
DPRINTLN(DBG_INFO, F("Will make tx-request 0x15 with subcmd ") + String(mMain->mSys->InfoCmd) + F(" and payload ") + String(response["payload"]));
}
DPRINTLN(DBG_INFO, F("Will make tx-request 0x15 with subcmd ") + String(cmd) + F(" and payload ") + String(response["payload"]));
// process payload from web request corresponding to the cmd
iv->enqueCommand<InfoCommand>(cmd);
}

18
tools/esp8266/web.h

@ -7,8 +7,13 @@
#define __WEB_H__
#include "dbg.h"
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>
#ifdef ESP8266
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>
#elif defined(ESP32)
#include <WebServer.h>
#include <HTTPUpdateServer.h>
#endif
#include "app.h"
@ -40,8 +45,13 @@ class web {
void showWebApi(void);
private:
ESP8266WebServer *mWeb;
ESP8266HTTPUpdateServer *mUpdater;
#ifdef ESP8266
ESP8266WebServer *mWeb;
ESP8266HTTPUpdateServer *mUpdater;
#elif defined(ESP32)
WebServer *mWeb;
HTTPUpdateServer *mUpdater;
#endif
config_t *mConfig;
sysConfig_t *mSysCfg;

Loading…
Cancel
Save