Browse Source

Merge branch 'development03' into Zero-Export

pull/1155/head
DanielR92 2 years ago
parent
commit
5e46f14906
  1. 2
      .github/workflows/compile_release.yml
  2. 302
      Getting_Started.md
  3. 12
      README.md
  4. 302
      manual/Getting_Started.md
  5. 0
      manual/User_Manual.md
  6. 65
      manual/ahoy_config.md
  7. 20
      src/CHANGES.md
  8. 28
      src/app.cpp
  9. 4
      src/app.h
  10. 4
      src/config/config.h
  11. 9
      src/config/config_override_example.h
  12. 23
      src/config/settings.h
  13. 2
      src/defines.h
  14. 21
      src/hm/Communication.h
  15. 27
      src/hm/hmRadio.h
  16. 1
      src/hms/hmsRadio.h
  17. 99
      src/utils/syslog.cpp
  18. 54
      src/utils/syslog.h
  19. 55
      src/web/RestApi.h
  20. 6
      src/web/html/setup.html
  21. 13
      src/web/html/system.html
  22. 4
      src/web/html/visualization.html
  23. 74
      src/web/web.h

2
.github/workflows/compile_release.yml

@ -77,7 +77,7 @@ jobs:
VERSION: ${{ steps.rename-binary-files.outputs.name }} VERSION: ${{ steps.rename-binary-files.outputs.name }}
- name: Create Artifact - name: Create Artifact
run: zip --junk-paths ${{ steps.rename-binary-files.outputs.name }}.zip src/firmware/* rc/firmware/s3/* User_Manual.md run: zip --junk-paths ${{ steps.rename-binary-files.outputs.name }}.zip src/firmware/* User_Manual.md
- name: Upload Release - name: Upload Release
id: upload-release id: upload-release

302
Getting_Started.md

@ -1,302 +0,0 @@
## Overview
On this page, you'll find detailed instructions on how to wire the module of a Wemos D1 mini or ESP32 to the radio module, as well as how to flash it with the latest firmware. This information will enable you to communicate with compatible inverters.
You find the full [User_Manual here](User_Manual.md)
## Compatiblity
The following inverters are currently supported out of the box:
Hoymiles Inverters
| Status | Serie | Model | comment |
| ----- | ----- | ------ | ------- |
| ✔️ | MI | 300, 600, 1000/1200/⚠️ 1500 | 4-Channel is not tested yet |
| ✔️ | HM | 300, 350, 400, 600, 700, 800, 1000?, 1200, 1500 | |
| ✔️ | HMS | 350, 500, 800, 1000, 1600, 1800, 2000 | |
| ✔️ | HMT | 1600, 1800, 2250 | |
| ⚠️ | TSUN | [TSOL-M350](https://www.tsun-ess.com/Micro-Inverter/M350-M400), [TSOL-M400](https://www.tsun-ess.com/Micro-Inverter/M350-M400), [TSOL-M800/TSOL-M800(DE)](https://www.tsun-ess.com/Micro-Inverter/M800) | others may work as well (need to be verified). |
## Table of Contents
- [Table of Contents](#table-of-contents)
- [Overview](#overview)
- [Compatiblity](#compatiblity)
- [Things needed](#things-needed)
- [There are fake NRF24L01+ Modules out there](#there-are-fake-nrf24l01-modules-out-there)
- [Wiring things up](#wiring-things-up)
- [ESP8266 wiring example on WEMOS D1](#esp8266-wiring-example)
- [Schematic](#schematic)
- [Symbolic view](#symbolic-view)
- [ESP8266 wiring example on 30pin Lolin NodeMCU v3](#esp8266-wiring-example-2)
- [Schematic](#schematic-2)
- [Symbolic view](#symbolic-view-2)
- [ESP32 wiring example](#esp32-wiring-example)
- [Schematic](#schematic-1)
- [Symbolic view](#symbolic-view-1)
- [ESP32 GPIO settings](#esp32-gpio-settings)
- [Flash the Firmware on your Ahoy DTU Hardware](#flash-the-firmware-on-your-ahoy-dtu-hardware)
- [Compiling your own Version](#compiling-your-own-version)
- [Optional Configuration before compilation](#optional-configuration-before-compilation)
- [Using a ready-to-flash binary using nodemcu-pyflasher](#using-a-ready-to-flash-binary-using-nodemcu-pyflasher)
- [Connect to your Ahoy DTU](#connect-to-your-ahoy-dtu)
- [Your Ahoy DTU is very verbose using the Serial Console](#your-ahoy-dtu-is-very-verbose-using-the-serial-console)
- [Connect to the Ahoy DTU Webinterface using your Browser](#connect-to-the-ahoy-dtu-webinterface-using-your-browser)
- [HTTP based Pages](#http-based-pages)
- [MQTT command to set the DTU without webinterface](#mqtt-command-to-set-the-dtu-without-webinterface)
- [Used Libraries](#used-libraries)
- [ToDo](#todo)
***
Solenso Inverters:
- SOL-H350
## Things needed
If you're interested in building your own AhoyDTU, you'll need a few things to get started. While we've provided a list of recommended boards below, keep in mind that the maker community is constantly developing new and innovative options that we may not have covered in this readme..
For optimal performance, we recommend using a Wemos D1 mini or ESP32 along with a NRF24L01+ breakout board as a bare minimum. However, if you have experience working with other ESP boards, any board with at least 4MBytes of ROM may be suitable, depending on your skills.
Just be sure that the NRF24L01+ module you choose includes the "+" in its name, as we rely on the 250kbps features that are only provided by the plus-variant.
| **Parts** | **Price** |
| --- | --- |
| D1 ESP8266 Mini WLAN Board Microcontroller | 4,40 Euro |
| NRF24L01+ SMD Modul 2,4 GHz Wi-Fi Funkmodul | 3,45 Euro |
| 100µF / 10V Capacitor Kondensator | 0,15 Euro |
| Jumper Wire Steckbrücken Steckbrett weiblich-weiblich | 2,49 Euro |
| **Total costs** | **10,34 Euro** |
If you're interested in using our sister project OpenDTU or you want to future-proof your setup, we recommend investing in an ESP32 board that features two CPU cores. As Radio you can also use a NRF24L01+ module with an external antenna. While this option may cost a bit more, it will provide superior performance and ensure compatibility with upcoming developments.
| **Parts** | **Price** |
| --- | --- |
| ESP32 Dev Board NodeMCU WROOM32 WiFi | 7,90 Euro |
| NRF24L01+ PA LNA SMA mit Antenne Long | 4,50 Euro |
| 100µF / 10V Capacitor Kondensator | 0,15 Euro |
| Jumper Wire Steckbrücken Steckbrett weiblich-weiblich | 2,49 Euro |
| **Total costs** | **14,89 Euro** |
#### There are fake NRF24L01+ Modules out there
Watch out, there are some fake NRF24L01+ Modules out there that seem to use rebranded NRF24L01 Chips (without the +).<br/>
An example can be found in [Issue #230](https://github.com/lumapu/ahoy/issues/230).<br/>
You are welcome to add more examples of faked chips. We will add that information here.<br/>
Some users reported better connection or longer range through more walls when using the
"E01-ML01DP5" EBYTE 2,4 GHz Wireless Modul nRF24L01 + PA + LNA RF Modul, SMA-K Antenna connector,
which has an eye-catching HF cover. But beware: It comes without the antenna!
In any case you should stabilize the Vcc power by a capacitor and don't exceed the Amplifier Power Level "LOW".
Users reporting good connection over 10m through walls / ceilings with Amplifier Power Level "MIN".
It is not always the bigger the better...
Power levels "HIGH" and "MAX" are meant to wirings where the nRF24 is supplied by an extra 3.3 Volt regulator.
The bultin regulator on ESP boards has only low reserves in case WiFi and nRF are sending simultaneously.
If you operate additional interfaces like a display, the reserve is again reduced.
## Wiring things up
The NRF24L01+ radio module is connected to the standard SPI pins:
- SCLK (Signal Clock),
- MISO (Master In Slave Out) and
- MOSI (Master Out Slave In)
*These pins need to be configured in the config.h.*
Additional, there are 3 pins, which can be set individual:
- CS (Chip Select),
- CE (Chip Enable) and
- IRQ (Interrupt)
*These pins can be changed from the /setup URL.*
#### ESP8266 wiring example on WEMOS D1
This is an example wiring using a Wemos D1 mini.<br>
##### Schematic
![Schematic](https://ahoydtu.de/img/fritzing/esp8266_nrf_sch.png)
##### Symbolic view
![Symbolic](https://ahoydtu.de/img/fritzing/esp8266_nrf.png)
#### ESP8266 wiring example on 30pin Lolin NodeMCU v3
This is an example wiring using a NodeMCU V3.<br>
##### Schematic
![Schematic](doc/ESP8266_nRF24L01+_Schaltplan.jpg)
##### Symbolic view
![Symbolic](doc/ESP8266_nRF24L01+_bb.png)
#### ESP32 wiring example
Example wiring for a 38pin ESP32 module
##### Schematic
![Schematic](https://ahoydtu.de/img/fritzing/esp32-38_nrf_sch.png)
##### Symbolic view
![Symbolic](https://ahoydtu.de/img/fritzing/esp32-38_nrf.png)
##### ESP32 GPIO settings
CS, CE, IRQ must be set according to how they are wired up. For the diagram above, set the 3 individual GPIOs under the /setup URL as follows:
```
CS D1 (GPIO5)
CE D2 (GPIO4)
IRQ D0 (GPIO16 - no IRQ!)
```
IMPORTANT: From development version 108/release 0.6.0 onwards, also MISO, MOSI, and SCLK
are configurable. On new installations, their defaults are correct for most ESP32 boards.
These pins cannot be configured for ESP82xx boards, as this chip cannot move them elsewhere.
If you are upgrading an existing install though, you might see that these pins are set to '0' in the web GUI.
Communication with the NRF module wont work. For upgrading an existing installations, set MISO=19, MOSI=23, SCLK=18 in the settings.
This is the correct default for most ESP32 boards. On ESP82xx, simply saving the settings without changes should suffice.
Save and reboot.
## Flash the Firmware on your Ahoy DTU Hardware
Once your Hardware is ready to run, you need to flash the Ahoy DTU Firmware to your Board.
You can either build your own using your own configuration or use one of our pre-compiled generic builds.
### Flash from your browser (easy)
The easiest step for you is to flash online. A browser MS Edge or Google Chrome is required.
[Here you go](https://ahoydtu.de/web_install/)
### Compiling your own Version
This information suits you if you want to configure and build your own firmware.
This code comes to you as a **PlatformIO** project and can be compiled using the **PlatformIO** Addon.<br/>
Visual Studio Code, AtomIDE and other IDE's support the PlatformIO Addon.<br/>
If you do not want to compile your own build, you can use one of our ready-to-flash binaries.
##### Optional Configuration before compilation
- number of supported inverters (set to 3 by default) `config.h`
- DTU radio id `config.h` (default = 1234567801)
- unformatted list in webbrowser `/livedata` `config.h`, `LIVEDATA_VISUALIZED`
Alternativly, instead of modifying `config.h`, `config_override_example.h` can be copied to `config_override.h` and customized.
config_override.h is excluded from version control and stays local.
#### Using a ready-to-flash binary using nodemcu-pyflasher
This information suits you if you just want to use an easy way.
1. download the flash-tool [nodemcu-pyflasher](https://github.com/marcelstoer/nodemcu-pyflasher)
2. download latest release bin-file from [ahoy_](https://github.com/grindylow/ahoy/releases)
3. open flash-tool and connect the target device to your computer.
4. Set the correct serial port and select the correct *.bin file
5. click on "Flash NodeMCU"
6. flash the ESP with the compiled firmware using the UART pins or
7. repower the ESP
8. the ESP will start as access point (AP) if there is no network config stored in its eeprom
9. connect to the AP (password: `esp_8266`), you will be forwarded to the setup page
10. configure your WiFi settings, save, repower
11. check your router or serial console for the IP address of the module. You can try ping the configured device name as well.
Once your Ahoy DTU is running, you can use the Over The Air (OTA) capabilities to update your firmware.
! ATTENTION: If you update from a very low version to the newest, please make sure to wipe all flash data!
#### Flashing on Linux with `esptool.py` (ESP32)
1. install [esptool.py](https://docs.espressif.com/projects/esptool/en/latest/esp32/) if you haven't already.
2. download and extract the latest release bin-file from [ahoy_](https://github.com/grindylow/ahoy/releases)
3. `cd ahoy_v<XXX> && cp *esp32.bin esp32.bin`
4. Perhaps you need to replace `/dev/ttyUSB0` to match your acual device in the following command. Execute it afterwards: `esptool.py --port /dev/ttyUSB0 --chip esp32 --before default_reset --after hard_reset write_flash --flash_mode dout --flash_freq 40m --flash_size detect 0x1000 bootloader.bin 0x8000 partitions.bin 0x10000 esp32.bin`
5. Unplug and replug your device.
6. Open a serial monitor (e.g. Putty) @ 115200 Baud. You should see some messages regarding wifi.
## Connect to your Ahoy DTU
When everything is wired up and the firmware is flashed, it is time to connect to your Ahoy DTU.
#### Your Ahoy DTU is very verbose using the Serial Console
When connected to your computer, you can open a Serial Console to obtain additional information.<br/>
This might be useful in case of any troubles that might occur as well as to simply<br/>
obtain information about the converted values which were read out of the inverter(s).
#### Connect to the Ahoy DTU Webinterface using your Browser
After you have sucessfully flashed and powered your Ahoy DTU, you can access it via your Browser.<br/>
If your Ahoy DTU was able to log into the configured WiFi Network, it will try to obtain an IP-Address<br/>
from your local DHCP Server (in most cases thats your Router).<br/><br/>
In case it could not connect to your configured Network, it will provide its own WiFi Network that you can<br/>
connect to for furter configuration.<br/>
The WiFi SSID *(the WiFi Name)* and Passwort is configured in the config.h and defaults to the SSID "`AHOY-DTU`" with the Passwort "`esp_8266`".<br/>
The Ahoy DTU will keep that Network open for a certain amount of time (also configurable in the config.h and defaults to 60secs).<br/>
If nothing connects to it and that time runs up, it will retry to connect to the configured network an so on.<br/>
<br/>
If connected to your local Network, you just have to find out the used IP Address or try the default name [http://ahoy-dtu/](http://ahoy-dtu/). In most cases your Router will give you a hint.<br/>
If you connect to the WiFi the Ahoy DTU opens in case it could not connect to any other Network, the IP-Address of your Ahoy DTU is [http://192.168.4.1/](http://192.168.4.1/).<br/>
Just open the IP-Address in your browser.<br/>
<br/>
The webinterface has the following abilities:
- OTA Update (Over The Air Update)
- Configuration (Wifi, inverter(s), NTP Server, Pinout, MQTT, Amplifier Power Level, Debug)
- visual display of the connected inverters / modules
- some statistics about communication (debug)
##### HTTP based Pages
To take control of your Ahoy DTU, you can directly call one of the following sub-pages (e.g. [http://ahoy-dtu/setup](http://ahoy-dtu/setup) or [http://192.168.4.1/setup](http://192.168.4.1/setup) ).<br/>
| page | use | output | default availability |
| ---- | ------ | ------ | ------ |
| /uptime | displays the uptime uf your Ahoy DTU | 0 Days, 01:37:34; now: 2022-08-21 11:13:53 | yes |
| /reboot | reboots the Ahoy DTU | | yes |
| /erase | erases the EEPROM | | yes |
| /factory | resets to the factory defaults configured in config.h | | yes |
| /setup | opens the setup page | | yes |
| /save | | | yes |
| /cmdstat | show stat from the home page | | yes |
| /visualization | displays the information from your converter | | yes |
| /livedata | displays the live data | | yes |
| /metrics | gets live-data for prometheus | prometheus metrics from the livedata | no - enable via config_override.h |
| /api | gets configuration and live-data in JSON format | json output from the configuration or livedata | yes |
## MQTT command to set the DTU without webinterface
[Read here](User_Manual.md)
## Used Libraries
| Name | version | License |
| --------------------- | ------- | -------- |
| `ESP8266WiFi` | 1.0 | LGPL-2.1 |
| `DNSServer` | 1.1.1 | LGPL-2.1 |
| `SPI` | 1.0 | LGPL-2.1 |
| `Hash` | 1.0 | LGPL-2.1 |
| `EEPROM` | 1.0 | LGPL-2.1 |
| `ESP Async WebServer` | 1.2.3 | LGPL-3.0 |
| `ESPAsyncTCP` | 1.2.2 | LGPL-3.0 |
| `Time` | 1.6.1 | LGPL-2.1 |
| `RF24` | 1.4.7 | GPL-2.0 |
| `espMqttClient` | 1.4.4 | MIT |
| `ArduinoJson` | 6.21.3 | MIT |
## ToDo
[See this post](https://github.com/lumapu/ahoy/issues/142)

12
README.md

@ -20,7 +20,7 @@ This work is licensed under a
# 🖐 Ahoy! # 🖐 Ahoy!
![Logo](https://github.com/grindylow/ahoy/blob/main/doc/logo1_small.png?raw=true) ![Logo](https://github.com/grindylow/ahoy/blob/main/doc/logo1_small.png?raw=true)
This repository offers hardware and software solutions for communicating with Hoymiles inverters via radio. With our system, you can easily obtain real-time values such as power, current, and daily energy. Additionally, you can set parameters like the power limit of your inverter to achieve zero export. You can access these functionalities through our user-friendly web interface, MQTT, or JSON. Whether you're monitoring your solar panel system's performance or fine-tuning its settings, our solutions make it easy to achieve your goals. This repository provides hardware and software solutions for communicating with Hoymiles inverters via radio. Our system allows you to easily obtain real-time values, such as power, current, and daily energy, as well as set parameters like the power limit of your inverter to achieve zero export. You can access these functionalities through our user-friendly web interface, MQTT, or JSON. Our solutions simplify the process of monitoring and fine-tuning your solar panel system to help you achieve your goals.
Table of approaches: Table of approaches:
@ -32,9 +32,11 @@ Table of approaches:
| [Others, C/C++](tools/nano/NRF24_SendRcv/) | ❌ | ✔️ | ❌ | | | [Others, C/C++](tools/nano/NRF24_SendRcv/) | ❌ | ✔️ | ❌ | |
## Getting Started ## Getting Started
[Guide how to start with a ESP module](Getting_Started.md) 1. [Guide how to start with a ESP module](Getting_Started.md)
[ESP Webinstaller (Edge / Chrome Browser only)](https://ahoydtu.de/web_install) 2. [ESP Webinstaller (Edge / Chrome Browser only)](https://ahoydtu.de/web_install)
3. [Ahoy Configuration ](ahoy_config.md)
## Our Website ## Our Website
[https://ahoydtu.de](https://ahoydtu.de) [https://ahoydtu.de](https://ahoydtu.de)
@ -43,11 +45,11 @@ Table of approaches:
- [Getting the data into influxDB and visualize them in a Grafana Dashboard](https://grafana.com/grafana/dashboards/16850-pv-power-ahoy/) (thx @Carl) - [Getting the data into influxDB and visualize them in a Grafana Dashboard](https://grafana.com/grafana/dashboards/16850-pv-power-ahoy/) (thx @Carl)
## Support, Feedback, Information and Discussion ## Support, Feedback, Information and Discussion
- [Discord Server (~ 3.800 Users)](https://discord.gg/WzhxEY62mB) - [Discord Server (~ 7.300 Users)](https://discord.gg/WzhxEY62mB)
- [The root of development](https://www.mikrocontroller.net/topic/525778) - [The root of development](https://www.mikrocontroller.net/topic/525778)
### Development ### Development
If you run into any issues, please feel free to use the issue tracker here on Github. When describing your issue, please be as detailed and precise as possible, and take a moment to consider whether the issue is related to our software. This will help us to provide more effective solutions to your problem. If you encounter any problems, use the issue tracker on Github. Provide a detailed description of the issue and consider if it is related to our software. This will help us provide effective solutions.
**Contributors are always welcome!** **Contributors are always welcome!**

302
manual/Getting_Started.md

@ -0,0 +1,302 @@
## Overview
This page contains detailed instructions on building a module and flashing it with the latest firmware. Following these instructions will allow you to communicate with compatible inverters.
You find the full [User_Manual here](User_Manual.md)
## Compatiblity
Currently, the following inverters are supported:
Hoymiles Inverters
| Status | Serie | Model | comment |
| ----- | ----- | ------ | ------- |
| ✔️ | MI | 300, 600, 1000/1200/⚠️ 1500 | 4-Channel is not tested yet |
| ✔️ | HM | 300, 350, 400, 600, 700, 800, 1000?, 1200, 1500 | |
| ✔️ | HMS | 350, 500, 800, 1000, 1600, 1800, 2000 | |
| ✔️ | HMT | 1600, 1800, 2250 | |
| ⚠️ | TSUN | [TSOL-M350](https://www.tsun-ess.com/Micro-Inverter/M350-M400), [TSOL-M400](https://www.tsun-ess.com/Micro-Inverter/M350-M400), [TSOL-M800/TSOL-M800(DE)](https://www.tsun-ess.com/Micro-Inverter/M800) | others may work as well (need to be verified). |
## Table of Contents
- [Overview](#overview)
- [Compatiblity](#compatiblity)
- [Things needed](#things-needed)
- [There are fake NRF24L01+ Modules out there](#there-are-fake-nrf24l01-modules-out-there)
- [Wiring things up](#wiring-things-up)
- [ESP8266 wiring example on WEMOS D1](#esp8266-wiring-example)
- [Schematic](#schematic)
- [Symbolic view](#symbolic-view)
- [ESP8266 wiring example on 30pin Lolin NodeMCU v3](#esp8266-wiring-example-2)
- [Schematic](#schematic-2)
- [Symbolic view](#symbolic-view-2)
- [ESP32 wiring example](#esp32-wiring-example)
- [Schematic](#schematic-1)
- [Symbolic view](#symbolic-view-1)
- [ESP32 GPIO settings](#esp32-gpio-settings)
- [Flash the Firmware on your Ahoy DTU Hardware](#flash-the-firmware-on-your-ahoy-dtu-hardware)
- [Compiling your own Version](#compiling-your-own-version)
- [Using a ready-to-flash binary using nodemcu-pyflasher](#using-a-ready-to-flash-binary-using-nodemcu-pyflasher)
- [Connect to your Ahoy DTU](#connect-to-your-ahoy-dtu)
- [Your Ahoy DTU is very verbose using the Serial Console](#your-ahoy-dtu-is-very-verbose-using-the-serial-console)
- [Connect to the Ahoy DTU Webinterface using your Browser](#connect-to-the-ahoy-dtu-webinterface-using-your-browser)
- [HTTP based Pages](#http-based-pages)
- [MQTT command to set the DTU without webinterface](#mqtt-command-to-set-the-dtu-without-webinterface)
- [Used Libraries](#used-libraries)
- [ToDo](#todo)
***
Solenso Inverters:
- SOL-H350
## Things needed
To build your own AhoyDTU, you only need a few things. Remember that the maker community is always developing new and innovative options that we may not have covered in this readme.
Start with an ESP8266 or ESP32, and combine it with an NRF24L01+ breakout board. Other ESP boards with at least 4MBytes of ROM may also be suitable.
Make sure to choose an NRF24L01+ module that includes the '+' in its name. This is important because we need the 250kbps features that are only available in the plus-variant.
**Attention**: The NRF24L01+ can only communicate with the MI/HM/TSUN inverter. For the HMS/HMT it is needed to use a CMT2300A!
| **Parts** | **Price** |
| --- | --- |
| D1 ESP8266 Mini WLAN Board Microcontroller | 4,40 €|
| *NRF24L01+ SMD Modul 2,4 GHz Wi-Fi Funkmodul (not for HMS/HMT)* | *3,45 €*|
| *CMT2300A 868/915MHz (E49-900M20S)* | *4,59 €* |
| 100µF / 10V Capacitor Kondensator | 0,15 €|
| Jumper Wire Steckbrücken Steckbrett weiblich-weiblich | 2,49 €|
| **Total costs** | **10,34 € / 11,48 €** |
To future-proof your setup and use our sister project OpenDTU, we recommend investing in an ESP32 board with two CPU cores. Additionally, you can use a NRF24L01+ module with an external antenna as a radio for superior performance and compatibility with upcoming developments.
| **Parts** | **Price** |
| --- | --- |
| ESP32 Dev Board NodeMCU WROOM32 WiFi | 7,90 €|
| *NRF24L01+ SMD Modul 2,4 GHz Wi-Fi Funkmodul (not for HMS/HMT)* | *3,45 €*|
| *CMT2300A 868/915MHz (E49-900M20S)* | *4,59 €* |
| 100µF / 10V Capacitor Kondensator | 0,15 €|
| Jumper Wire breadboard female-female | 2,49 €|
| **Total costs** | **13,99 € / 15,13 €** |
#### There are fake NRF24L01+ Modules out there
Beware of fake NRF24L01+ modules that use rebranded NRF24L01 chips (without the +).
An example of this can be found in Issue #230 (https://github.com/lumapu/ahoy/issues/230).
If you have any additional examples of fake chips, please share them with us and we will add the information here.
#### NRF24L01+ improvements
Users have reported improved connections and longer range through walls when using these modules.
The "E01-ML01DP5" module is a 2.4 GHz wireless module that utilizes the nRF24L01+PA+LNA RF module and features an SMA-K antenna connector.
**The product includes an HF cover, but please note that it does not come with an antenna.**
To achieve the best results, stabilize the Vcc power by using a capacitor and do not exceed the 'LOW' Amplifier Power Level.
Users have reported good connections over 10m through walls and ceilings when using the Amplifier Power Level 'MIN'.
It's important to remember that bigger is not always better.
If you are using the NRF24 directly on the ESP board, make sure to set the transmission power to the lowest possible level (this can be adjusted later in the web interface). Using a high transmission power can potentially cause problems.
The ESP board's built-in controller has limited reserves in case both WiFi and nRF are transmitting simultaneously.
If you are using additional interfaces, such as a display, the reserves will be further reduced.
## Wiring things up
The NRF24L01+ radio module is connected to the standard SPI pins:
- SCLK (Signal Clock),
- MISO (Master In Slave Out) and
- MOSI (Master Out Slave In)
*These pins need to be configured in the config.h.*
Additional, there are 3 pins, which can be set individual:
- CS (Chip Select),
- CE (Chip Enable) and
- IRQ (Interrupt)
*These pins can be changed from the /setup URL.*
#### ESP8266 wiring example on WEMOS D1
This is an example wiring using a Wemos D1 mini.
##### Schematic
![Schematic](https://ahoydtu.de/img/fritzing/esp8266_nrf_sch.png)
##### Symbolic view
![Symbolic](https://ahoydtu.de/img/fritzing/esp8266_nrf.png)
#### ESP8266 wiring example on 30pin Lolin NodeMCU v3
This is an example wiring using a NodeMCU V3.<br>
##### Schematic
![Schematic](doc/ESP8266_nRF24L01+_Schaltplan.jpg)
##### Symbolic view
![Symbolic](doc/ESP8266_nRF24L01+_bb.png)
#### ESP32 wiring example
Example wiring for a 38pin ESP32 module
##### Schematic
![Schematic](https://ahoydtu.de/img/fritzing/esp32-38_nrf_sch.png)
##### Symbolic view
![Symbolic](https://ahoydtu.de/img/fritzing/esp32-38_nrf.png)
##### ESP32 GPIO settings
CS, CE, IRQ must be set according to how they are wired up. For the diagram above, set the 3 individual GPIOs under the /setup URL as follows:
```
CS D1 (GPIO5)
CE D2 (GPIO4)
IRQ D0 (GPIO16 - no IRQ!)
```
**IMPORTANT**: Starting from development version 108/release 0.6.0, MISO, MOSI, and SCLK are also included.
For most ESP32 boards, the default settings are correct on new installations.
However, it is not possible to configure these pins for ESP82xx boards, as they cannot be moved elsewhere.
If you are upgrading an existing installation, you may notice that the pins are set to '0' in the web GUI, which will prevent communication with the NRF module.
To resolve this, set MISO=19, MOSI=23, SCLK=18 in the settings.
This is the correct default for most ESP32 boards. For ESP82xx, simply saving the settings without changes should suffice.
Save and reboot.
## Flash the Firmware on your Ahoy DTU Hardware
After preparing your hardware, you must flash the Ahoy DTU Firmware to your board.
You can either create your own firmware using your configuration or use one of our pre-compiled generic builds.
Are you ready to flash? Then go to next Step here.
### Flash from your browser (easy)
The easiest step for you is to flash online. A browser MS Edge or Google Chrome is required.
[Here you go](https://ahoydtu.de/web_install/)
### Compiling your own Version (expert)
This information is for those who wish to configure and build their own firmware.
The code is provided as a PlatformIO project and can be compiled using the PlatformIO Addon.
The PlatformIO Addon is supported by Visual Studio Code, AtomIDE, and other IDEs.
If you do not wish to compile your own build, you can use one of our pre-compiled binaries.
#### Using a ready-to-flash binary using nodemcu-pyflasher
This information suits you if you just want to use an easy way.
1. download the flash-tool [nodemcu-pyflasher](https://github.com/marcelstoer/nodemcu-pyflasher)
2. download latest release bin-file from [ahoy_](https://github.com/grindylow/ahoy/releases)
3. open flash-tool and connect the target device to your computer.
4. Set the correct serial port and select the correct *.bin file
5. click on "Flash NodeMCU"
6. flash the ESP with the compiled firmware using the UART pins or
7. repower the ESP
8. the ESP will start as access point (AP) if there is no network config stored in its eeprom
9. connect to the AP (password: `esp_8266`), you will be forwarded to the setup page
10. configure your WiFi settings, save, repower
11. check your router or serial console for the IP address of the module. You can try ping the configured device name as well.
Once your Ahoy DTU is running, you can use the Over The Air (OTA) capabilities to update your firmware.
**! ATTENTION: If you update from a very low version to the newest, please make sure to wipe all flash data!**
#### Flashing on Linux with `esptool.py` (ESP32)
1. install [esptool.py](https://docs.espressif.com/projects/esptool/en/latest/esp32/) if you haven't already.
2. download and extract the latest release bin-file from [ahoy_](https://github.com/grindylow/ahoy/releases)
3. `cd ahoy_v<XXX> && cp *esp32.bin esp32.bin`
4. Perhaps you need to replace `/dev/ttyUSB0` to match your acual device in the following command. Execute it afterwards: `esptool.py --port /dev/ttyUSB0 --chip esp32 --before default_reset --after hard_reset write_flash --flash_mode dout --flash_freq 40m --flash_size detect 0x1000 bootloader.bin 0x8000 partitions.bin 0x10000 esp32.bin`
5. Unplug and replug your device.
6. Open a serial monitor (e.g. Putty) @ 115200 Baud. You should see some messages regarding wifi.
## Connect to your Ahoy DTU
Once everything is wired and the firmware is flashed, it is time to connect to your Ahoy DTU.
#### Your Ahoy DTU is very verbose using the Serial Console
Once connected to your computer, you can open a serial console to get additional information.
This can be useful for troubleshooting, as well as simply to get
information about the converted values read from the inverter(s).
#### Connect to the Ahoy DTU Webinterface using your Browser
After you have successfully flashed and powered up your Ahoy DTU, you can access it from your browser.<br/>
If your Ahoy DTU was able to log on to the configured WiFi network, it will try to obtain an IP address from your local DHCP server (in most cases this is your router).
If it cannot connect to your configured network, it will provide its own WiFi network that you can
to for further configuration.
The WiFi SSID *(the WiFi name)* and password are pre-configured and are set to SSID "`AHOY-DTU`" and password "`esp_8266`" by default.
The Ahoy DTU will keep this network open for a certain amount of time (default is 60sec).
If nothing connects to it and the time expires, it will retry to connect to the configured network, and so on.
If you are connected to your local network, just find out the IP address used or try the default name [http://ahoy-dtu/](http://ahoy-dtu/).
In most cases, your router will give you a hint.
If you connect to the WiFi the Ahoy DTU opens in case it could not connect to any other Network, the IP-Address of your Ahoy DTU is [http://192.168.4.1/](http://192.168.4.1/).
Just open the IP-Address in your browser.
The web interface has the following capabilities:
- Live data (values updated every 5 seconds)
Click on the title/name/alarm for more actions.
- Webserial (Debug)
- Settings (System Config, Network, Protection, Inverter, NTP Server, Sunrise/Sunset, MQTT, Display Config)
- Update (Over The Air Update)
- System (status about the modules)
##### HTTP based Pages
To take control of your Ahoy DTU, you can directly call one of the following sub-pages (e.g. [http://ahoy-dtu/setup](http://ahoy-dtu/setup) or [http://192.168.4.1/setup](http://192.168.4.1/setup) ).
| page | use | output | default availability |
| ---- | ------ | ------ | ------ |
| /logout| logout the user from webinterface | | yes |
| /reboot | reboots the Ahoy DTU | | yes |
| /system| show system inforamtion | | yes |
| /live | displays the live data | | yes |
| /save | | | yes |
| /erase | erases the EEPROM | | yes |
| /factory | resets to the factory defaults configured in config.h | | yes |
| /setup | opens the setup page | | yes |
| /metrics | gets live-data for prometheus | prometheus metrics from the livedata | no - enable via config_override.h |
| /api | gets configuration and live-data in JSON format | json output from the configuration or livedata | yes |
## MQTT command to set the DTU without webinterface
[Read here](User_Manual.md)
## Used Libraries
| Name | version | License |
| --------------------- | ------- | -------- |
| `ESP8266WiFi` | 1.0 | LGPL-2.1 |
| `DNSServer` | 1.1.1 | LGPL-2.1 |
| `SPI` | 1.0 | LGPL-2.1 |
| `Hash` | 1.0 | LGPL-2.1 |
| `EEPROM` | 1.0 | LGPL-2.1 |
| `ESPAsyncWebServer` | 1.2.3 | LGPL-3.0 |
| [ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) | 1.2.2 | [LGPL-3.0 license](https://github.com/me-no-dev/ESPAsyncTCP#LGPL-3.0-1-ov-file) |
| [Time](https://github.com/PaulStoffregen/Time) | 1.6.1 | ? |
| [RF24](https://github.com/nRF24/RF24) | 1.4.8 | [GPL-2.0 license](https://github.com/nRF24/RF24#GPL-2.0-1-ov-file) |
| [espMqttClient](https://github.com/bertmelis/espMqttClient) | ? | [MIT license](https://github.com/bertmelis/espMqttClient#MIT-1-ov-file) |
| [ArduinoJson](https://github.com/bblanchon/ArduinoJson) | 6.21.3 | [MIT license](https://github.com/bblanchon/ArduinoJson#MIT-1-ov-file)|
| [GxEPD2](https://github.com/ZinggJM/GxEPD2) | 1.5.2 | [GPL-3.0 license](https://github.com/ZinggJM/GxEPD2#GPL-3.0-1-ov-file)|
| [U8g2_Arduino](https://registry.platformio.org/libraries/olikraus/U8g2) | [2.35.9](https://registry.platformio.org/libraries/olikraus/U8g2/versions) | [BSD-2-Clause](https://spdx.org/licenses/BSD-2-Clause.html) |
## ToDo
[See this post](https://github.com/lumapu/ahoy/issues/142)

0
User_Manual.md → manual/User_Manual.md

65
manual/ahoy_config.md

@ -0,0 +1,65 @@
## Ahoy configuration
So far we have built our own DTU, written a program on it and put it into operation.
But how do I get my data from the inverter?
To do this, we need to configure the DTU.
The following steps are required:
1. Set the pinning to communicate with the radio module.
2. Check if Ahoy has a current time
3. Set inverter data
### 1.) Set the pinning
Once you are in the web interface, you will find the "System Config" sub-item in the Setup area (left).
This is where you tell the ESP how you connected the radio module.
Note the schematics you saw earlier. - If you haven't noticed them yet, here's another table of connections.
#### OpenDTU Fusion (ESP32-S3)
| NRF24 Pin | ESP Pin|
|---------| --------|
| CS (4) | GPIO37
| CE (3)| GPIO38
| IRQ (8) | GPIO47
| SCLK (5)| GPIO36
| MOSI (6)| GPIO35
| MISO (7)| GPIO48
| CMT2300A | Pin |
|---------| --------|
| CMT| Enabled |
| SCLK| GPIO6
| SDIO| GPIO5
| CSB| GPIO4
| FCSB| GPIO21
| GPIO3| GPIO8
### 2.) Set current time (normal skip this step)
Ahoy needs a current date and time to talk to the inverter.
It works without, but it is recommended to include a time. This allows you to analyze information from the inverter in more detail.
Normally, a date/time should be automatically retrieved from the NTP server. However, it may happen that the firewall of some routers does not allow this.
In the section "Settings -> NTP Server" you can also get the time from your own computer. Or set up your own NTP server.
### 3.) Set inverter data
#### add new inverter
Now it's time to place the inverter. This is necessary because it is not the inverter that speaks first, but the DTU (Ahoy).
Each inverter has its own S.Nr. This also serves as an identity for communication between the DTU and the inverter.
The S.Nr is a 12-digit number. You can look it up [here (german)](https://github.com/lumapu/ahoy/wiki/Hardware#wie-ist-die-serien-nummer-der-inverter-aufgebaut) for more information.
#### set pv-modules (not necessary)
Click on "Add Inverter" and enter the S.No. and a name. Please keep the name short!
In the upper tab "Inputs" you can enter the data of the solar modules. These are only used directly in Ahoy for calculation and have no influence on the inverter.
#### set radio parameter (not necessary, only for EU)
In the next tab "Radio" you can adjust the power and other parameters if necessary. However, these should be left as default (EU only).
#### advanced options (not necessary)
In the "Advanced" section, you can customize more settings.
# Done - Now check the live site

20
src/CHANGES.md

@ -1,5 +1,25 @@
# Development Changes # Development Changes
## 0.8.35 - 2023-12-30
* added dim option for LEDS
* changed reload time for opendtufusion after update to 5s
* fix default interval and gap for communication
* fix serial number in exported json (was decimal, now correct as hexdecimal number)
* beautified factory reset
* added second stage for erase settings
* increased maximal number of inverters to 32 for opendtufusion board (ESP32-S3)
* fixed crash if CMT inverter is enabled, but CMT isn't configured
# RELEASE 0.8.34 - 2023-12-29
## 0.8.33 - 2023-12-29
* improved communication thx @rejoe2
## 0.8.32 - 2023-12-29
* fix `start` / `stop` / `restart` commands #1287
* added message, if profile was not read until now #1300
* added developer option to use 'syslog-server' instead of 'web-serail' #1299
## 0.8.31 - 2023-12-29 ## 0.8.31 - 2023-12-29
* added class to handle timeouts PR #1298 * added class to handle timeouts PR #1298

28
src/app.cpp

@ -78,6 +78,9 @@ void app::setup() {
mApi.setup(this, &mSys, mWeb.getWebSrvPtr(), mConfig); mApi.setup(this, &mSys, mWeb.getWebSrvPtr(), mConfig);
#ifdef ENABLE_SYSLOG
mDbgSyslog.setup(mConfig); // be sure to init after mWeb.setup (webSerial uses also debug callback)
#endif
// Plugins // Plugins
#if defined(PLUGIN_DISPLAY) #if defined(PLUGIN_DISPLAY)
if (mConfig->plugin.display.type != 0) if (mConfig->plugin.display.type != 0)
@ -104,14 +107,14 @@ void app::setup() {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void app::loop(void) { void app::loop(void) {
ah::Scheduler::loop();
if(mConfig->nrf.enabled) if(mConfig->nrf.enabled)
mNrfRadio.loop(); mNrfRadio.loop();
#if defined(ESP32) #if defined(ESP32)
if(mConfig->cmt.enabled) if(mConfig->cmt.enabled)
mCmtRadio.loop(); mCmtRadio.loop();
#endif #endif
ah::Scheduler::loop();
mCommunication.loop(); mCommunication.loop();
if (mMqttEnabled && mNetworkConnected) if (mMqttEnabled && mNetworkConnected)
@ -363,6 +366,9 @@ void app::tickSend(void) {
continue; continue;
} }
if(!iv->radio->isChipConnected())
continue;
iv->tickSend([this, iv](uint8_t cmd, bool isDevControl) { iv->tickSend([this, iv](uint8_t cmd, bool isDevControl) {
if(isDevControl) if(isDevControl)
mCommunication.addImportant(iv, cmd); mCommunication.addImportant(iv, cmd);
@ -462,22 +468,22 @@ void app::mqttSubRxCb(JsonObject obj) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void app::setupLed(void) { void app::setupLed(void) {
uint8_t led_off = (mConfig->led.led_high_active) ? LOW : HIGH; uint8_t led_off = (mConfig->led.high_active) ? 0 : 255;
if (mConfig->led.led0 != DEF_PIN_OFF) { if (mConfig->led.led0 != DEF_PIN_OFF) {
pinMode(mConfig->led.led0, OUTPUT); pinMode(mConfig->led.led0, OUTPUT);
digitalWrite(mConfig->led.led0, led_off); analogWrite(mConfig->led.led0, led_off);
} }
if (mConfig->led.led1 != DEF_PIN_OFF) { if (mConfig->led.led1 != DEF_PIN_OFF) {
pinMode(mConfig->led.led1, OUTPUT); pinMode(mConfig->led.led1, OUTPUT);
digitalWrite(mConfig->led.led1, led_off); analogWrite(mConfig->led.led1, led_off);
} }
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void app::updateLed(void) { void app::updateLed(void) {
uint8_t led_off = (mConfig->led.led_high_active) ? LOW : HIGH; uint8_t led_off = (mConfig->led.high_active) ? 0 : 255;
uint8_t led_on = (mConfig->led.led_high_active) ? HIGH : LOW; uint8_t led_on = (mConfig->led.high_active) ? (mConfig->led.luminance) : (255-mConfig->led.luminance);
if (mConfig->led.led0 != DEF_PIN_OFF) { if (mConfig->led.led0 != DEF_PIN_OFF) {
Inverter<> *iv; Inverter<> *iv;
@ -486,20 +492,20 @@ void app::updateLed(void) {
if (NULL != iv) { if (NULL != iv) {
if (iv->isProducing()) { if (iv->isProducing()) {
// turn on when at least one inverter is producing // turn on when at least one inverter is producing
digitalWrite(mConfig->led.led0, led_on); analogWrite(mConfig->led.led0, led_on);
break; break;
} }
else if(iv->config->enabled) else if(iv->config->enabled)
digitalWrite(mConfig->led.led0, led_off); analogWrite(mConfig->led.led0, led_off);
} }
} }
} }
if (mConfig->led.led1 != DEF_PIN_OFF) { if (mConfig->led.led1 != DEF_PIN_OFF) {
if (getMqttIsConnected()) { if (getMqttIsConnected()) {
digitalWrite(mConfig->led.led1, led_on); analogWrite(mConfig->led.led1, led_on);
} else { } else {
digitalWrite(mConfig->led.led1, led_off); analogWrite(mConfig->led.led1, led_off);
} }
} }
} }

4
src/app.h

@ -22,6 +22,7 @@
#include "utils/crc.h" #include "utils/crc.h"
#include "utils/dbg.h" #include "utils/dbg.h"
#include "utils/scheduler.h" #include "utils/scheduler.h"
#include "utils/syslog.h"
#include "web/RestApi.h" #include "web/RestApi.h"
#include "web/web.h" #include "web/web.h"
#include "hm/Communication.h" #include "hm/Communication.h"
@ -320,6 +321,9 @@ class app : public IApp, public ah::Scheduler {
#endif /* defined(ETHERNET) */ #endif /* defined(ETHERNET) */
WebType mWeb; WebType mWeb;
RestApiType mApi; RestApiType mApi;
#ifdef ENABLE_SYSLOG
DbgSyslog mDbgSyslog;
#endif
//PayloadType mPayload; //PayloadType mPayload;
//MiPayloadType mMiPayload; //MiPayloadType mMiPayload;
PubSerialType mPubSerial; PubSerialType mPubSerial;

4
src/config/config.h

@ -153,7 +153,11 @@
// number of configurable inverters // number of configurable inverters
#if defined(ESP32) #if defined(ESP32)
#if defined(CONFIG_IDF_TARGET_ESP32S3)
#define MAX_NUM_INVERTERS 32
#else
#define MAX_NUM_INVERTERS 16 #define MAX_NUM_INVERTERS 16
#endif
#else #else
#define MAX_NUM_INVERTERS 4 #define MAX_NUM_INVERTERS 4
#endif #endif

9
src/config/config_override_example.h

@ -35,4 +35,13 @@
// #define ENABLE_PROMETHEUS_EP // #define ENABLE_PROMETHEUS_EP
// to enable the syslog logging (will disable web-serial)
//#define ENABLE_SYSLOG
#ifdef ENABLE_SYSLOG
#define SYSLOG_HOST "<hostname-or-ip-address-of-syslog-server>"
#define SYSLOG_APP "ahoy"
#define SYSLOG_FACILITY FAC_USER
#define SYSLOG_PORT 514
#endif
#endif /*__CONFIG_OVERRIDE_H__*/ #endif /*__CONFIG_OVERRIDE_H__*/

23
src/config/settings.h

@ -30,7 +30,7 @@
* https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#flash-layout * https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#flash-layout
* */ * */
#define CONFIG_VERSION 6 #define CONFIG_VERSION 7
#define PROT_MASK_INDEX 0x0001 #define PROT_MASK_INDEX 0x0001
@ -119,7 +119,8 @@ typedef struct {
typedef struct { typedef struct {
uint8_t led0; // first LED pin uint8_t led0; // first LED pin
uint8_t led1; // second LED pin uint8_t led1; // second LED pin
bool led_high_active; // determines if LEDs are high or low active bool high_active; // determines if LEDs are high or low active
uint8_t luminance; // luminance of LED
} cfgLed_t; } cfgLed_t;
typedef struct { typedef struct {
@ -448,7 +449,7 @@ class settings {
mCfg.serial.showIv = false; mCfg.serial.showIv = false;
mCfg.serial.debug = false; mCfg.serial.debug = false;
mCfg.serial.privacyLog = true; mCfg.serial.privacyLog = true;
mCfg.serial.printWholeTrace = true; mCfg.serial.printWholeTrace = false;
mCfg.mqtt.port = DEF_MQTT_PORT; mCfg.mqtt.port = DEF_MQTT_PORT;
snprintf(mCfg.mqtt.broker, MQTT_ADDR_LEN, "%s", DEF_MQTT_BROKER); snprintf(mCfg.mqtt.broker, MQTT_ADDR_LEN, "%s", DEF_MQTT_BROKER);
@ -479,7 +480,7 @@ class settings {
mCfg.inst.startWithoutTime = false; mCfg.inst.startWithoutTime = false;
mCfg.inst.rstMaxValsMidNight = false; mCfg.inst.rstMaxValsMidNight = false;
mCfg.inst.yieldEffiency = 1.0f; mCfg.inst.yieldEffiency = 1.0f;
mCfg.inst.gapMs = 2000; mCfg.inst.gapMs = 500;
mCfg.inst.readGrid = true; mCfg.inst.readGrid = true;
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
@ -491,9 +492,8 @@ class settings {
mCfg.led.led0 = DEF_LED0; mCfg.led.led0 = DEF_LED0;
mCfg.led.led1 = DEF_LED1; mCfg.led.led1 = DEF_LED1;
mCfg.led.led_high_active = LED_HIGH_ACTIVE; mCfg.led.high_active = LED_HIGH_ACTIVE;
mCfg.led.luminance = 255;
memset(&mCfg.inst, 0, sizeof(cfgInst_t));
mCfg.plugin.display.pwrSaveAtIvOffline = false; mCfg.plugin.display.pwrSaveAtIvOffline = false;
mCfg.plugin.display.contrast = 60; mCfg.plugin.display.contrast = 60;
@ -532,6 +532,9 @@ class settings {
mCfg.inst.gapMs = 500; mCfg.inst.gapMs = 500;
mCfg.inst.readGrid = true; mCfg.inst.readGrid = true;
} }
if(mCfg.configVersion < 7) {
mCfg.led.luminance = 255;
}
} }
} }
@ -743,11 +746,13 @@ class settings {
if(set) { if(set) {
obj[F("0")] = mCfg.led.led0; obj[F("0")] = mCfg.led.led0;
obj[F("1")] = mCfg.led.led1; obj[F("1")] = mCfg.led.led1;
obj[F("act_high")] = mCfg.led.led_high_active; obj[F("act_high")] = mCfg.led.high_active;
obj[F("lum")] = mCfg.led.luminance;
} else { } else {
getVal<uint8_t>(obj, F("0"), &mCfg.led.led0); getVal<uint8_t>(obj, F("0"), &mCfg.led.led0);
getVal<uint8_t>(obj, F("1"), &mCfg.led.led1); getVal<uint8_t>(obj, F("1"), &mCfg.led.led1);
getVal<bool>(obj, F("act_high"), &mCfg.led.led_high_active); getVal<bool>(obj, F("act_high"), &mCfg.led.high_active);
getVal<uint8_t>(obj, F("lum"), &mCfg.led.luminance);
} }
} }

2
src/defines.h

@ -13,7 +13,7 @@
//------------------------------------- //-------------------------------------
#define VERSION_MAJOR 0 #define VERSION_MAJOR 0
#define VERSION_MINOR 8 #define VERSION_MINOR 8
#define VERSION_PATCH 31 #define VERSION_PATCH 35
//------------------------------------- //-------------------------------------
typedef struct { typedef struct {

21
src/hm/Communication.h

@ -276,9 +276,10 @@ class Communication : public CommQueue<> {
} else { } else {
DBGPRINT(F(" ")); DBGPRINT(F(" "));
DBGPRINT(String(p->rssi)); DBGPRINT(String(p->rssi));
DBGPRINT(F("dBm | ")); DBGPRINT(F("dBm "));
} }
if(*mPrintWholeTrace) { if(*mPrintWholeTrace) {
DBGPRINT(F("| "));
if(*mPrivacyMode) if(*mPrivacyMode)
ah::dumpBuf(p->packet, p->len, 1, 8); ah::dumpBuf(p->packet, p->len, 1, 8);
else else
@ -363,8 +364,24 @@ class Communication : public CommQueue<> {
} }
inline bool parseDevCtrl(packet_t *p, const queue_s *q) { inline bool parseDevCtrl(packet_t *p, const queue_s *q) {
if((p->packet[12] != ActivePowerContr) || (p->packet[13] != 0x00)) switch(p->packet[12]) {
case ActivePowerContr:
if(p->packet[13] != 0x00)
return false; return false;
break;
case TurnOn: [[fallthrough]];
case TurnOff: [[fallthrough]];
case Restart:
return true;
break;
default:
DPRINT(DBG_WARN, F("unknown dev ctrl: "));
DBGHEXLN(p->packet[12]);
break;
}
bool accepted = true; bool accepted = true;
if((p->packet[10] == 0x00) && (p->packet[11] == 0x00)) if((p->packet[10] == 0x00) && (p->packet[11] == 0x00))
q->iv->powerLimitAck = true; q->iv->powerLimitAck = true;

27
src/hm/hmRadio.h

@ -113,34 +113,51 @@ class HmRadio : public Radio {
mNrf24->flush_tx(); // empty TX FIFO mNrf24->flush_tx(); // empty TX FIFO
// start listening // start listening
uint8_t chOffset = 2;
mRxChIdx = (mTxChIdx + chOffset) % RF_CHANNELS;
mNrf24->setChannel(mRfChLst[mRxChIdx]); mNrf24->setChannel(mRfChLst[mRxChIdx]);
mNrf24->startListening(); mNrf24->startListening();
if(NULL == mLastIv) // prevent reading on NULL object! if(NULL == mLastIv) // prevent reading on NULL object!
return; return;
uint32_t startMicros = micros(); uint32_t innerLoopTimeout = 55000;
uint32_t loopMillis = millis(); uint32_t loopMillis = millis();
uint32_t outerLoopTimeout = (mLastIv->mIsSingleframeReq) ? 100 : ((mLastIv->mCmd != AlarmData) && (mLastIv->mCmd != GridOnProFilePara)) ? 400 : 600; uint32_t outerLoopTimeout = (mLastIv->mIsSingleframeReq) ? 100 : ((mLastIv->mCmd != AlarmData) && (mLastIv->mCmd != GridOnProFilePara)) ? 400 : 600;
bool isRxInit = true;
while ((millis() - loopMillis) < outerLoopTimeout) { while ((millis() - loopMillis) < outerLoopTimeout) {
startMicros = micros(); uint32_t startMicros = micros();
while ((micros() - startMicros) < 5110) { // listen (4088us or?) 5110us to each channel while ((micros() - startMicros) < innerLoopTimeout) { // listen (4088us or?) 5110us to each channel
if (mIrqRcvd) { if (mIrqRcvd) {
mIrqRcvd = false; mIrqRcvd = false;
if (getReceived()) { // everything received if (getReceived()) { // everything received
return; return;
} }
innerLoopTimeout = 4088*5;
if (isRxInit) {
isRxInit = false;
if (micros() - startMicros < 42000) {
innerLoopTimeout = 4088*12;
mRxChIdx = (mRxChIdx + 4) % RF_CHANNELS;
mNrf24->setChannel(mRfChLst[mRxChIdx]);
}
}
startMicros = micros();
} }
yield(); yield();
} }
// switch to next RX channel // switch to next RX channel
mRxChIdx = (mRxChIdx + 1) % RF_CHANNELS; mRxChIdx = (mRxChIdx + 4) % RF_CHANNELS;
mNrf24->setChannel(mRfChLst[mRxChIdx]); mNrf24->setChannel(mRfChLst[mRxChIdx]);
innerLoopTimeout = 4088;
isRxInit = false;
} }
// not finished but time is over // not finished but time is over
mRxChIdx = (mRxChIdx + 1) % RF_CHANNELS;
return; return;
} }

1
src/hms/hmsRadio.h

@ -99,6 +99,7 @@ class CmtRadio : public Radio {
DHEX(mTxBuf[0]); DHEX(mTxBuf[0]);
DBGPRINT(F(" ")); DBGPRINT(F(" "));
DHEX(mTxBuf[10]); DHEX(mTxBuf[10]);
DBGPRINT(F(" "));
DBGHEXLN(mTxBuf[9]); DBGHEXLN(mTxBuf[9]);
} }
} }

99
src/utils/syslog.cpp

@ -0,0 +1,99 @@
#include <algorithm>
#include "syslog.h"
#ifdef ENABLE_SYSLOG
#define SYSLOG_MAX_PACKET_SIZE 256
//-----------------------------------------------------------------------------
void DbgSyslog::setup(settings_t *config) {
mConfig = config;
// Syslog callback overrides web-serial callback
registerDebugCb(std::bind(&DbgSyslog::syslogCb, this, std::placeholders::_1)); // dbg.h
}
//-----------------------------------------------------------------------------
void DbgSyslog::syslogCb (String msg)
{
if (!mSyslogIP.isSet()) {
// use WiFi.hostByName to DNS lookup for IPAddress of syslog server
if (WiFi.status() == WL_CONNECTED) {
WiFi.hostByName(SYSLOG_HOST,mSyslogIP);
}
}
if (!mSyslogIP.isSet()) {
return;
}
uint16_t msgLength = msg.length();
uint16_t msgPos = 0;
do {
uint16_t charsToCopy = std::min(msgLength-msgPos,SYSLOG_BUF_SIZE - mSyslogBufFill);
while (charsToCopy > 0) {
mSyslogBuffer[mSyslogBufFill] = msg[msgPos];
msgPos++;
mSyslogBufFill++;
charsToCopy--;
}
mSyslogBuffer[mSyslogBufFill] = '\0';
bool isBufferFull = (mSyslogBufFill == SYSLOG_BUF_SIZE);
bool isEolFound = false;
if (mSyslogBufFill >= 2) {
isEolFound = (mSyslogBuffer[mSyslogBufFill-2] == '\r' && mSyslogBuffer[mSyslogBufFill-1] == '\n');
}
// Get severity from input message
if (msgLength >= 2) {
if (':' == msg[1]) {
switch(msg[0]) {
case 'E': mSyslogSeverity = PRI_ERROR; break;
case 'W': mSyslogSeverity = PRI_WARNING; break;
case 'I': mSyslogSeverity = PRI_INFO; break;
case 'D': mSyslogSeverity = PRI_DEBUG; break;
default: mSyslogSeverity = PRI_NOTICE; break;
}
}
}
if (isBufferFull || isEolFound) {
// Send mSyslogBuffer in chunks because mSyslogBuffer is larger than syslog packet size
int packetStart = 0;
int packetSize = 122; // syslog payload depends also on hostname and app
char saveChar;
if (isEolFound) {
mSyslogBuffer[mSyslogBufFill-2]=0; // skip \r\n
}
while(packetStart < mSyslogBufFill) {
saveChar = mSyslogBuffer[packetStart+packetSize];
mSyslogBuffer[packetStart+packetSize] = 0;
log(mConfig->sys.deviceName,SYSLOG_FACILITY, mSyslogSeverity, &mSyslogBuffer[packetStart]);
mSyslogBuffer[packetStart+packetSize] = saveChar;
packetStart += packetSize;
}
mSyslogBufFill = 0;
}
} while (msgPos < msgLength); // Message not completely processed
}
//-----------------------------------------------------------------------------
void DbgSyslog::log(const char *hostname, uint8_t facility, uint8_t severity, char* msg) {
// The PRI value is an integer number which calculates by the following metric:
uint8_t priority = (8 * facility) + severity;
// This is a unit8 instead of a char because that's what udp.write() wants
uint8_t buffer[SYSLOG_MAX_PACKET_SIZE];
int len = snprintf((char*)buffer, SYSLOG_MAX_PACKET_SIZE, "<%d>%s %s: %s", priority, hostname, SYSLOG_APP, msg);
//printf("syslog::log %s\n",mSyslogIP.toString().c_str());
//printf("syslog::log %d %s\n",len,buffer);
// Send the raw UDP packet
mSyslogUdp.beginPacket(mSyslogIP, SYSLOG_PORT);
mSyslogUdp.write(buffer, len);
mSyslogUdp.endPacket();
}
#endif

54
src/utils/syslog.h

@ -0,0 +1,54 @@
#ifndef __SYSLOG_H__
#define __SYSLOG_H__
#ifdef ESP8266
#include <ESP8266WiFi.h>
#elif defined(ESP32)
#include <WiFi.h>
#endif
#include <WiFiUdp.h>
#include "../config/config.h"
#include "../config/settings.h"
#ifdef ENABLE_SYSLOG
#define SYSLOG_BUF_SIZE 255
#define PRI_EMERGENCY 0
#define PRI_ALERT 1
#define PRI_CRITICAL 2
#define PRI_ERROR 3
#define PRI_WARNING 4
#define PRI_NOTICE 5
#define PRI_INFO 6
#define PRI_DEBUG 7
#define FAC_USER 1
#define FAC_LOCAL0 16
#define FAC_LOCAL1 17
#define FAC_LOCAL2 18
#define FAC_LOCAL3 19
#define FAC_LOCAL4 20
#define FAC_LOCAL5 21
#define FAC_LOCAL6 22
#define FAC_LOCAL7 23
class DbgSyslog {
public:
void setup (settings_t *config);
void syslogCb(String msg);
void log(const char *hostname, uint8_t facility, uint8_t severity, char* msg);
private:
WiFiUDP mSyslogUdp;
IPAddress mSyslogIP;
settings_t *mConfig;
char mSyslogBuffer[SYSLOG_BUF_SIZE+1];
uint16_t mSyslogBufFill = 0;
int mSyslogSeverity = PRI_NOTICE;
};
#endif /*ENABLE_SYSLOG*/
#endif /*__SYSLOG_H__*/

55
src/web/RestApi.h

@ -88,6 +88,10 @@ class RestApi {
else if(path == "html/logout") getHtmlLogout(request, root); else if(path == "html/logout") getHtmlLogout(request, root);
else if(path == "html/reboot") getHtmlReboot(request, root); else if(path == "html/reboot") getHtmlReboot(request, root);
else if(path == "html/save") getHtmlSave(request, root); else if(path == "html/save") getHtmlSave(request, root);
else if(path == "html/erase") getHtmlErase(request, root);
else if(path == "html/erasetrue") getHtmlEraseTrue(request, root);
else if(path == "html/factory") getHtmlFactory(request, root);
else if(path == "html/factorytrue") getHtmlFactoryTrue(request, root);
else if(path == "system") getSysInfo(request, root); else if(path == "system") getSysInfo(request, root);
else if(path == "generic") getGeneric(request, root); else if(path == "generic") getGeneric(request, root);
else if(path == "reboot") getReboot(request, root); else if(path == "reboot") getReboot(request, root);
@ -214,6 +218,16 @@ class RestApi {
tmp.remove(i, tmp.indexOf("\"", i)-i); tmp.remove(i, tmp.indexOf("\"", i)-i);
} }
} }
i = 0;
// convert all serial numbers to hexadecimal
while (i != -1) {
i = tmp.indexOf("\"sn\":", i);
if(-1 != i) {
i+=5;
String sn = tmp.substring(i, tmp.indexOf("\"", i)-1);
tmp.replace(sn, String(atoll(sn.c_str()), HEX));
}
}
response = request->beginResponse(200, F("application/json; charset=utf-8"), tmp); response = request->beginResponse(200, F("application/json; charset=utf-8"), tmp);
} }
@ -316,7 +330,11 @@ class RestApi {
void getHtmlReboot(AsyncWebServerRequest *request, JsonObject obj) { void getHtmlReboot(AsyncWebServerRequest *request, JsonObject obj) {
getGeneric(request, obj.createNestedObject(F("generic"))); getGeneric(request, obj.createNestedObject(F("generic")));
#if defined(ETHERNET) && defined(CONFIG_IDF_TARGET_ESP32S3)
obj[F("refresh")] = 5;
#else
obj[F("refresh")] = 20; obj[F("refresh")] = 20;
#endif
obj[F("refresh_url")] = "/"; obj[F("refresh_url")] = "/";
obj[F("html")] = F("rebooting ..."); obj[F("html")] = F("rebooting ...");
} }
@ -333,6 +351,40 @@ class RestApi {
#endif #endif
} }
void getHtmlErase(AsyncWebServerRequest *request, JsonObject obj) {
getGeneric(request, obj.createNestedObject(F("generic")));
obj[F("html")] = F("Erase settings (not WiFi)? <a class=\"btn\" href=\"/erasetrue\">yes</a> <a class=\"btn\" href=\"/\">no</a>");
}
void getHtmlEraseTrue(AsyncWebServerRequest *request, JsonObject obj) {
getGeneric(request, obj.createNestedObject(F("generic")));
mApp->eraseSettings(false);
mApp->setRebootFlag();
obj[F("html")] = F("Erase settings: success");
#if defined(ETHERNET) && defined(CONFIG_IDF_TARGET_ESP32S3)
obj[F("reload")] = 5;
#else
obj[F("reload")] = 20;
#endif
}
void getHtmlFactory(AsyncWebServerRequest *request, JsonObject obj) {
getGeneric(request, obj.createNestedObject(F("generic")));
obj[F("html")] = F("Factory reset? <a class=\"btn\" href=\"/factorytrue\">yes</a> <a class=\"btn\" href=\"/\">no</a>");
}
void getHtmlFactoryTrue(AsyncWebServerRequest *request, JsonObject obj) {
getGeneric(request, obj.createNestedObject(F("generic")));
mApp->eraseSettings(true);
mApp->setRebootFlag();
obj[F("html")] = F("Factory reset: success");
#if defined(ETHERNET) && defined(CONFIG_IDF_TARGET_ESP32S3)
obj[F("reload")] = 5;
#else
obj[F("reload")] = 20;
#endif
}
void getReboot(AsyncWebServerRequest *request, JsonObject obj) { void getReboot(AsyncWebServerRequest *request, JsonObject obj) {
getGeneric(request, obj.createNestedObject(F("generic"))); getGeneric(request, obj.createNestedObject(F("generic")));
obj[F("refresh")] = 10; obj[F("refresh")] = 10;
@ -576,7 +628,8 @@ class RestApi {
obj[F("miso")] = mConfig->nrf.pinMiso; obj[F("miso")] = mConfig->nrf.pinMiso;
obj[F("led0")] = mConfig->led.led0; obj[F("led0")] = mConfig->led.led0;
obj[F("led1")] = mConfig->led.led1; obj[F("led1")] = mConfig->led.led1;
obj[F("led_high_active")] = mConfig->led.led_high_active; obj[F("led_high_active")] = mConfig->led.high_active;
obj[F("led_lum")] = mConfig->led.luminance;
} }
#if defined(ESP32) #if defined(ESP32)

6
src/web/html/setup.html

@ -965,8 +965,12 @@
ml("div", { class: "row mb-3" }, [ ml("div", { class: "row mb-3" }, [
ml("div", { class: "col-12 col-sm-3 my-2" }, "LED polarity"), ml("div", { class: "col-12 col-sm-3 my-2" }, "LED polarity"),
ml("div", { class: "col-12 col-sm-9" }, ml("div", { class: "col-12 col-sm-9" },
sel('pinLedHighActive', led_high_active, obj['led_high_active']) sel('pinLedHighActive', led_high_active, obj.led_high_active)
) )
]),
ml("div", { class: "row mb-3" }, [
ml("div", { class: "col-12 col-sm-3 my-2" }, "LED luminance (0-255)"),
ml("div", { class: "col-12 col-sm-9" }, ml("input", {class: "text", type: "number", name: "pinLedLum", value: obj.led_lum, min: 0, max: 255}, null))
]) ])
) )
} }

13
src/web/html/system.html

@ -119,21 +119,20 @@
function parse(obj) { function parse(obj) {
if(null != obj) { if(null != obj) {
parseGeneric(obj["generic"]); parseGeneric(obj.generic);
if(null != obj["refresh"]) { if(null != obj.refresh) {
var meta = document.createElement('meta'); var meta = document.createElement('meta');
meta.httpEquiv = "refresh" meta.httpEquiv = "refresh"
meta.content = obj["refresh"] + "; URL=" + obj["refresh_url"]; meta.content = obj.refresh + "; URL=" + obj.refresh_url;
document.getElementsByTagName('head')[0].appendChild(meta); document.getElementsByTagName('head')[0].appendChild(meta);
} } else if(null != obj.system) {
else {
parseRadio(obj.system); parseRadio(obj.system);
parseMqtt(obj.system.mqtt); parseMqtt(obj.system.mqtt);
parseSysInfo(obj["system"]); parseSysInfo(obj.system);
getAjax('/api/index', parseIndex); getAjax('/api/index', parseIndex);
} }
document.getElementById("html").innerHTML = obj["html"]; document.getElementById("html").innerHTML = obj.html;
} }
} }

4
src/web/html/visualization.html

@ -358,9 +358,13 @@
var content = []; var content = [];
var g = getGridType(glob.info.type, getGridIdentifier(glob)) var g = getGridType(glob.info.type, getGridIdentifier(glob))
if(null === g) { if(null === g) {
if(0 == obj.grid.length) {
content.push(ml("div", {class: "row"}, ml("div", {class: "col"}, ml("p", {}, "Profile was not read until now, maybe turned off?"))))
} else {
content.push(ml("div", {class: "row"}, ml("div", {class: "col"}, ml("h5", {}, "Unknown Profile")))) content.push(ml("div", {class: "row"}, ml("div", {class: "col"}, ml("h5", {}, "Unknown Profile"))))
content.push(ml("div", {class: "row"}, ml("div", {class: "col"}, ml("p", {}, "Please open a new issue at https://github.com/lumapu/ahoy and copy the raw data into it.")))) content.push(ml("div", {class: "row"}, ml("div", {class: "col"}, ml("p", {}, "Please open a new issue at https://github.com/lumapu/ahoy and copy the raw data into it."))))
content.push(ml("div", {class: "row"}, ml("div", {class: "col my-2"}, ml("pre", {}, obj.grid)))) content.push(ml("div", {class: "row"}, ml("div", {class: "col my-2"}, ml("pre", {}, obj.grid))))
}
} else { } else {
content.push(ml("div", {class: "row"}, content.push(ml("div", {class: "row"},
ml("div", {class: "col my-3"}, ml("h5", {}, g + " (Version " + getGridValue(glob).toString(16) + ")")) ml("div", {class: "col my-3"}, ml("h5", {}, g + " (Version " + getGridValue(glob).toString(16) + ")"))

74
src/web/web.h

@ -39,7 +39,7 @@
#define WEB_SERIAL_BUF_SIZE 2048 #define WEB_SERIAL_BUF_SIZE 2048
const char* const pinArgNames[] = {"pinCs", "pinCe", "pinIrq", "pinSclk", "pinMosi", "pinMiso", "pinLed0", "pinLed1", "pinLedHighActive", "pinCmtSclk", "pinSdio", "pinCsb", "pinFcsb", "pinGpio3"}; const char* const pinArgNames[] = {"pinCs", "pinCe", "pinIrq", "pinSclk", "pinMosi", "pinMiso", "pinLed0", "pinLed1", "pinLedHighActive", "pinLedLum", "pinCmtSclk", "pinSdio", "pinCsb", "pinFcsb", "pinGpio3"};
template <class HMSYSTEM> template <class HMSYSTEM>
class Web { class Web {
@ -71,14 +71,15 @@ class Web {
mWeb.onNotFound ( std::bind(&Web::showNotFound, this, std::placeholders::_1)); mWeb.onNotFound ( std::bind(&Web::showNotFound, this, std::placeholders::_1));
mWeb.on("/reboot", HTTP_ANY, std::bind(&Web::onReboot, this, std::placeholders::_1)); mWeb.on("/reboot", HTTP_ANY, std::bind(&Web::onReboot, this, std::placeholders::_1));
mWeb.on("/system", HTTP_ANY, std::bind(&Web::onSystem, this, std::placeholders::_1)); mWeb.on("/system", HTTP_ANY, std::bind(&Web::onSystem, this, std::placeholders::_1));
mWeb.on("/erase", HTTP_ANY, std::bind(&Web::showErase, this, std::placeholders::_1)); mWeb.on("/erase", HTTP_ANY, std::bind(&Web::showHtml, this, std::placeholders::_1));
mWeb.on("/factory", HTTP_ANY, std::bind(&Web::showFactoryRst, this, std::placeholders::_1)); mWeb.on("/erasetrue", HTTP_ANY, std::bind(&Web::showHtml, this, std::placeholders::_1));
mWeb.on("/factory", HTTP_ANY, std::bind(&Web::showHtml, this, std::placeholders::_1));
mWeb.on("/factorytrue", HTTP_ANY, std::bind(&Web::showHtml, this, std::placeholders::_1));
mWeb.on("/setup", HTTP_GET, std::bind(&Web::onSetup, this, std::placeholders::_1)); mWeb.on("/setup", HTTP_GET, std::bind(&Web::onSetup, this, std::placeholders::_1));
mWeb.on("/save", HTTP_POST, std::bind(&Web::showSave, this, std::placeholders::_1)); mWeb.on("/save", HTTP_POST, std::bind(&Web::showSave, this, std::placeholders::_1));
mWeb.on("/live", HTTP_ANY, std::bind(&Web::onLive, this, std::placeholders::_1)); mWeb.on("/live", HTTP_ANY, std::bind(&Web::onLive, this, std::placeholders::_1));
//mWeb.on("/api1", HTTP_POST, std::bind(&Web::showWebApi, this, std::placeholders::_1));
#ifdef ENABLE_PROMETHEUS_EP #ifdef ENABLE_PROMETHEUS_EP
mWeb.on("/metrics", HTTP_ANY, std::bind(&Web::showMetrics, this, std::placeholders::_1)); mWeb.on("/metrics", HTTP_ANY, std::bind(&Web::showMetrics, this, std::placeholders::_1));
@ -197,6 +198,11 @@ class Web {
#if !defined(ETHERNET) #if !defined(ETHERNET)
strncpy(mConfig->sys.stationPwd, pwd, PWD_LEN); // restore WiFi PWD strncpy(mConfig->sys.stationPwd, pwd, PWD_LEN); // restore WiFi PWD
#endif #endif
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
if((mConfig->inst.iv[i].serial.u64 != 0) && (mConfig->inst.iv[i].serial.u64 < 138999999999)) { // hexadecimal
mConfig->inst.iv[i].serial.u64 = ah::Serial2u64(String(mConfig->inst.iv[i].serial.u64).c_str());
}
}
mApp->saveSettings(true); mApp->saveSettings(true);
} }
if (!mUploadFail) if (!mUploadFail)
@ -284,12 +290,18 @@ class Web {
bool reboot = (!Update.hasError()); bool reboot = (!Update.hasError());
String html = F("<!doctype html><html><head><title>Update</title><meta http-equiv=\"refresh\" content=\"20; URL=/\"></head><body>Update: "); String html = F("<!doctype html><html><head><title>Update</title><meta http-equiv=\"refresh\" content=\"");
#if defined(ETHERNET) && defined(CONFIG_IDF_TARGET_ESP32S3)
html += F("5");
#else
html += F("20");
#endif
html += F("; URL=/\"></head><body>Update: ");
if (reboot) if (reboot)
html += "success"; html += "success";
else else
html += "failed"; html += "failed";
html += F("<br/><br/>rebooting ... auto reload after 20s</body></html>"); html += F("<br/><br/>rebooting ...</body></html>");
AsyncWebServerResponse *response = request->beginResponse(200, F("text/html; charset=UTF-8"), html); AsyncWebServerResponse *response = request->beginResponse(200, F("text/html; charset=UTF-8"), html);
response->addHeader("Connection", "close"); response->addHeader("Connection", "close");
@ -420,39 +432,12 @@ class Web {
request->send(response); request->send(response);
} }
void showErase(AsyncWebServerRequest *request) { void showHtml(AsyncWebServerRequest *request) {
checkProtection(request);
DPRINTLN(DBG_VERBOSE, F("showErase"));
mApp->eraseSettings(false);
onReboot(request);
}
void showFactoryRst(AsyncWebServerRequest *request) {
checkProtection(request); checkProtection(request);
DPRINTLN(DBG_VERBOSE, F("showFactoryRst")); AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), system_html, system_html_len);
String content = ""; response->addHeader(F("Content-Encoding"), "gzip");
int refresh = 3; request->send(response);
if (request->args() > 0) {
if (request->arg("reset").toInt() == 1) {
refresh = 10;
if (mApp->eraseSettings(true))
content = F("factory reset: success\n\nrebooting ... ");
else
content = F("factory reset: failed\n\nrebooting ... ");
} else {
content = F("factory reset: aborted");
refresh = 3;
}
} else {
content = F("<h1>Factory Reset</h1>"
"<p><a href=\"/factory?reset=1\">RESET</a><br/><br/><a href=\"/factory?reset=0\">CANCEL</a><br/></p>");
refresh = 120;
}
request->send(200, F("text/html; charset=UTF-8"), F("<!doctype html><html><head><title>Factory Reset</title><meta http-equiv=\"refresh\" content=\"") + String(refresh) + F("; URL=/\"></head><body>") + content + F("</body></html>"));
if (refresh == 10)
onReboot(request);
} }
void onSetup(AsyncWebServerRequest *request) { void onSetup(AsyncWebServerRequest *request) {
@ -521,7 +506,7 @@ class Web {
// pinout // pinout
uint8_t pin; uint8_t pin;
for (uint8_t i = 0; i < 14; i++) { for (uint8_t i = 0; i < 15; i++) {
pin = request->arg(String(pinArgNames[i])).toInt(); pin = request->arg(String(pinArgNames[i])).toInt();
switch(i) { switch(i) {
case 0: mConfig->nrf.pinCs = ((pin != 0xff) ? pin : DEF_NRF_CS_PIN); break; case 0: mConfig->nrf.pinCs = ((pin != 0xff) ? pin : DEF_NRF_CS_PIN); break;
@ -532,12 +517,13 @@ class Web {
case 5: mConfig->nrf.pinMiso = ((pin != 0xff) ? pin : DEF_NRF_MISO_PIN); break; case 5: mConfig->nrf.pinMiso = ((pin != 0xff) ? pin : DEF_NRF_MISO_PIN); break;
case 6: mConfig->led.led0 = pin; break; case 6: mConfig->led.led0 = pin; break;
case 7: mConfig->led.led1 = pin; break; case 7: mConfig->led.led1 = pin; break;
case 8: mConfig->led.led_high_active = pin; break; // this is not really a pin but a polarity, but handling it close to here makes sense case 8: mConfig->led.high_active = pin; break; // this is not really a pin but a polarity, but handling it close to here makes sense
case 9: mConfig->cmt.pinSclk = pin; break; case 9: mConfig->led.luminance = pin; break; // this is not really a pin but a polarity, but handling it close to here makes sense
case 10: mConfig->cmt.pinSdio = pin; break; case 10: mConfig->cmt.pinSclk = pin; break;
case 11: mConfig->cmt.pinCsb = pin; break; case 11: mConfig->cmt.pinSdio = pin; break;
case 12: mConfig->cmt.pinFcsb = pin; break; case 12: mConfig->cmt.pinCsb = pin; break;
case 13: mConfig->cmt.pinIrq = pin; break; case 13: mConfig->cmt.pinFcsb = pin; break;
case 14: mConfig->cmt.pinIrq = pin; break;
} }
} }

Loading…
Cancel
Save