From 36abaeee12c7358cfcfc8fa2f27268d558d46b2d Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Sun, 27 Nov 2022 12:07:25 +0100 Subject: [PATCH 01/15] Moved pin assignment outside the hoymiles library Allows setting the pins during runtime and not just using the defines --- lib/Hoymiles/src/Hoymiles.cpp | 4 ++-- lib/Hoymiles/src/Hoymiles.h | 2 +- lib/Hoymiles/src/HoymilesRadio.cpp | 11 +++++------ lib/Hoymiles/src/HoymilesRadio.h | 28 ++-------------------------- platformio.ini | 12 ++++++++++-- src/main.cpp | 4 +++- 6 files changed, 23 insertions(+), 38 deletions(-) diff --git a/lib/Hoymiles/src/Hoymiles.cpp b/lib/Hoymiles/src/Hoymiles.cpp index 355a4db2b..2b04ddc0d 100644 --- a/lib/Hoymiles/src/Hoymiles.cpp +++ b/lib/Hoymiles/src/Hoymiles.cpp @@ -9,14 +9,14 @@ HoymilesClass Hoymiles; -void HoymilesClass::init() +void HoymilesClass::init(SPIClass* initialisedSpiBus, uint8_t pinCE, uint8_t pinIRQ) { _xSemaphore = xSemaphoreCreateMutex(); HOY_SEMAPHORE_GIVE(); // release before first use _pollInterval = 0; _radio.reset(new HoymilesRadio()); - _radio->init(); + _radio->init(initialisedSpiBus, pinCE, pinIRQ); } void HoymilesClass::loop() diff --git a/lib/Hoymiles/src/Hoymiles.h b/lib/Hoymiles/src/Hoymiles.h index 4b6d37d6e..033eabe03 100644 --- a/lib/Hoymiles/src/Hoymiles.h +++ b/lib/Hoymiles/src/Hoymiles.h @@ -12,7 +12,7 @@ class HoymilesClass { public: - void init(); + void init(SPIClass* initialisedSpiBus, uint8_t pinCE, uint8_t pinIRQ); void loop(); std::shared_ptr addInverter(const char* name, uint64_t serial); diff --git a/lib/Hoymiles/src/HoymilesRadio.cpp b/lib/Hoymiles/src/HoymilesRadio.cpp index db70d8b5a..a011105c5 100644 --- a/lib/Hoymiles/src/HoymilesRadio.cpp +++ b/lib/Hoymiles/src/HoymilesRadio.cpp @@ -5,15 +5,14 @@ #include #include -void HoymilesRadio::init() +void HoymilesRadio::init(SPIClass* initialisedSpiBus, uint8_t pinCE, uint8_t pinIRQ) { _dtuSerial.u64 = 0; - _hspi.reset(new SPIClass(HSPI)); - _radio.reset(new RF24(HOYMILES_PIN_CE, HOYMILES_PIN_CS)); + _spiPtr.reset(initialisedSpiBus); + _radio.reset(new RF24(pinCE, initialisedSpiBus->pinSS())); - _hspi->begin(HOYMILES_PIN_SCLK, HOYMILES_PIN_MISO, HOYMILES_PIN_MOSI, HOYMILES_PIN_CS); - _radio->begin(_hspi.get()); + _radio->begin(_spiPtr.get()); _radio->setDataRate(RF24_250KBPS); _radio->enableDynamicPayloads(); @@ -27,7 +26,7 @@ void HoymilesRadio::init() Serial.println(F("Connection error!!")); } - attachInterrupt(digitalPinToInterrupt(HOYMILES_PIN_IRQ), std::bind(&HoymilesRadio::handleIntr, this), FALLING); + attachInterrupt(digitalPinToInterrupt(pinIRQ), std::bind(&HoymilesRadio::handleIntr, this), FALLING); openReadingPipe(); _radio->startListening(); diff --git a/lib/Hoymiles/src/HoymilesRadio.h b/lib/Hoymiles/src/HoymilesRadio.h index 1a29af93f..7c6246817 100644 --- a/lib/Hoymiles/src/HoymilesRadio.h +++ b/lib/Hoymiles/src/HoymilesRadio.h @@ -12,33 +12,9 @@ // number of fragments hold in buffer #define FRAGMENT_BUFFER_SIZE 30 -#ifndef HOYMILES_PIN_MISO -#define HOYMILES_PIN_MISO 19 -#endif - -#ifndef HOYMILES_PIN_MOSI -#define HOYMILES_PIN_MOSI 23 -#endif - -#ifndef HOYMILES_PIN_SCLK -#define HOYMILES_PIN_SCLK 18 -#endif - -#ifndef HOYMILES_PIN_IRQ -#define HOYMILES_PIN_IRQ 16 -#endif - -#ifndef HOYMILES_PIN_CE -#define HOYMILES_PIN_CE 4 -#endif - -#ifndef HOYMILES_PIN_CS -#define HOYMILES_PIN_CS 5 -#endif - class HoymilesRadio { public: - void init(); + void init(SPIClass* initialisedSpiBus, uint8_t pinCE, uint8_t pinIRQ); void loop(); void setPALevel(rf24_pa_dbm_e paLevel); @@ -71,7 +47,7 @@ class HoymilesRadio { void sendRetransmitPacket(uint8_t fragment_id); void sendLastPacketAgain(); - std::unique_ptr _hspi; + std::unique_ptr _spiPtr; std::unique_ptr _radio; uint8_t _rxChLst[5] = { 3, 23, 40, 61, 75 }; uint8_t _rxChIdx = 0; diff --git a/platformio.ini b/platformio.ini index 99f64fcf0..41af65e47 100644 --- a/platformio.ini +++ b/platformio.ini @@ -20,6 +20,14 @@ build_flags = -DCOMPONENT_EMBED_FILES=webapp_dist/index.html.gz:webapp_dist/zones.json.gz:webapp_dist/favicon.ico:webapp_dist/js/app.js.gz -Wall -Wextra -Werror + ; Default pin assignment for all boards if not specified otherwise + -DHOYMILES_PIN_MISO=19 + -DHOYMILES_PIN_MOSI=23 + -DHOYMILES_PIN_SCLK=18 + -DHOYMILES_PIN_IRQ=16 + -DHOYMILES_PIN_CE=4 + -DHOYMILES_PIN_CS=5 + lib_deps = https://github.com/yubox-node-org/ESPAsyncWebServer bblanchon/ArduinoJson @ ^6.19.4 @@ -36,8 +44,8 @@ monitor_speed = 115200 upload_protocol = esptool ; Specify port here. Comment out (add ; in front of line) to use auto detection. -monitor_port = COM5 -upload_port = COM5 +monitor_port = COM4 +upload_port = COM4 [env:generic] diff --git a/src/main.cpp b/src/main.cpp index 24102c19a..f641960b8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -93,7 +93,9 @@ void setup() // Initialize inverter communication Serial.print(F("Initialize Hoymiles interface... ")); - Hoymiles.init(); + SPIClass* spiClass = new SPIClass(HSPI); + spiClass->begin(HOYMILES_PIN_SCLK, HOYMILES_PIN_MISO, HOYMILES_PIN_MOSI, HOYMILES_PIN_CS); + Hoymiles.init(spiClass, HOYMILES_PIN_CE, HOYMILES_PIN_IRQ); Serial.println(F(" Setting radio PA level... ")); Hoymiles.getRadio()->setPALevel((rf24_pa_dbm_e)config.Dtu_PaLevel); From f6a74043149a47488b0ede43946b9c4798c31240 Mon Sep 17 00:00:00 2001 From: MrReSc Date: Mon, 28 Nov 2022 10:03:36 +0100 Subject: [PATCH 02/15] Support for NodeMCU-ESP-S3-12K-Kit added --- platformio.ini | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 99f64fcf0..af805ef90 100644 --- a/platformio.ini +++ b/platformio.ini @@ -110,4 +110,15 @@ build_flags = ${env.build_flags} -DETH_TYPE=ETH_PHY_LAN8720 -DETH_ADDR=0 -DETH_MDC_PIN=23 - -DETH_MDIO_PIN=18 \ No newline at end of file + -DETH_MDIO_PIN=18 + +[env:esp_s3_12k_kit] +; https://www.waveshare.com/wiki/NodeMCU-ESP-S3-12K-Kit +board = esp32-s3-devkitc-1 +build_flags = ${env.build_flags} + -DHOYMILES_PIN_MISO=16 + -DHOYMILES_PIN_MOSI=17 + -DHOYMILES_PIN_SCLK=18 + -DHOYMILES_PIN_IRQ=3 + -DHOYMILES_PIN_CE=4 + -DHOYMILES_PIN_CS=5 \ No newline at end of file From 3c8bd5657699990299a55c39b6fb5592353094c6 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Mon, 28 Nov 2022 18:16:46 +0100 Subject: [PATCH 03/15] Added MI-1500 with Gen3 Protocol to Hardware Id list --- lib/Hoymiles/src/parser/DevInfoParser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Hoymiles/src/parser/DevInfoParser.cpp b/lib/Hoymiles/src/parser/DevInfoParser.cpp index a9d031359..2b1bd330e 100644 --- a/lib/Hoymiles/src/parser/DevInfoParser.cpp +++ b/lib/Hoymiles/src/parser/DevInfoParser.cpp @@ -18,6 +18,7 @@ const devInfo_t devInfo[] = { { { 0x10, 0x11, 0x30, ALL }, 800, "HM-800" }, { { 0x10, 0x11, 0x40, ALL }, 800, "HM-800" }, { { 0x10, 0x12, 0x10, ALL }, 1200, "HM-1200" }, + { { 0x10, 0x02, 0x30, ALL }, 1500, "MI-1500 Gen3" }, { { 0x10, 0x12, 0x30, ALL }, 1500, "HM-1500" }, { { 0x10, 0x10, 0x10, 0x15 }, static_cast(300 * 0.7), "HM-300" }, // HM-300 factory limitted to 70% }; From 0a722e4bf4ef8ff586b36d2167c0e3caa5ee1242 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Mon, 28 Nov 2022 18:19:49 +0100 Subject: [PATCH 04/15] Adjusted download and install instructions --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 01927092b..015ac8cc6 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ This can be achieved by editing the 'platformio.ini' file and add/change one or * erase flash: `platformio run -e generic -t erase` ### using the pre-compiled .bin files -The pre-compiled files can be found on the [github page](https://github.com/tbnobody/OpenDTU) in the tab "Actions" and the sub menu "OpenDTU Build". Just choose the latest build from the master branch (blue font). You need to be logged in with your github account to download the files. +The pre-compiled files can be found on the [github page](https://github.com/tbnobody/OpenDTU) in the tab "Actions" and the sub menu "OpenDTU Build". Just choose the latest build from the master branch (search for "master" in the blue font text but click on the white header text!). You need to be logged in with your github account to download the files. Use a ESP32 flash tool of your choice (see next chapter) and flash the `.bin` files to the right addresses: | Address | File | @@ -146,7 +146,7 @@ Use a ESP32 flash tool of your choice (see next chapter) and flash the `.bin` fi For further updates you can just use the web interface and upload the `opendtu-*.bin` file. -### Flash with esptool.py (Linux) +#### Flash with esptool.py (Linux) ``` esptool.py --port /dev/ttyUSB0 --chip esp32 --before default_reset --after hard_reset \ write_flash --flash_mode dout --flash_freq 40m --flash_size detect \ @@ -156,7 +156,7 @@ esptool.py --port /dev/ttyUSB0 --chip esp32 --before default_reset --after hard_ 0x10000 opendtu-generic.bin ``` -### Flash with Espressif Flash Download Tool (Windows) +#### Flash with Espressif Flash Download Tool (Windows) [Download link](https://www.espressif.com/en/support/download/other-tools) @@ -167,7 +167,7 @@ esptool.py --port /dev/ttyUSB0 --chip esp32 --before default_reset --after hard_ - To program, press "Start" on screen, then the "Boot" button. - When flashing is complete (FINISH appears) then press the Reset button on the ESP32 board (or powercycle ) to start the OpenDTU application. -### Flash with ESP_Flasher (Windows) +#### Flash with ESP_Flasher (Windows) Users report that [ESP_Flasher](https://github.com/Jason2866/ESP_Flasher/releases/) is suitable for flashing OpenDTU on Windows. ## First configuration From 487ee5c8db391151e2e426e47eee4804c3d7b17a Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Mon, 28 Nov 2022 18:21:31 +0100 Subject: [PATCH 05/15] Added additional cases to README.md See https://github.com/tbnobody/OpenDTU/discussions/393 --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 015ac8cc6..226b97a7f 100644 --- a/README.md +++ b/README.md @@ -198,6 +198,9 @@ A documentation of the Web API can be found here: [Web-API Documentation](docs/W ## Available cases * * +* +* + ## Building * Building the WebApp From 8e7f40f56cb4867e5d2ee67b106ddced2f82abaf Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Mon, 28 Nov 2022 18:24:01 +0100 Subject: [PATCH 06/15] Better description when limit values show up in web UI --- docs/MQTT_Topics.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/MQTT_Topics.md b/docs/MQTT_Topics.md index 4f73b6166..46a2b6738 100644 --- a/docs/MQTT_Topics.md +++ b/docs/MQTT_Topics.md @@ -66,8 +66,8 @@ cmd topics are used to set values. Status topics are updated from values set in | [serial]/status/limit_relative | R | Current applied production limit of the inverter | % of total possible output | | [serial]/status/limit_absolute | R | Current applied production limit of the inverter | Watt (W) | | [serial]/cmd/limit_persistent_relative | W | Set the inverter limit as a percentage of total production capability. The value will survive the night without power. The updated value will show up in the web GUI and limit_relative topic immediatly. | % | -| [serial]/cmd/limit_persistent_absolute | W | Set the inverter limit as a absolute value. The value will survive the night without power. The updated value will show up in the web GUI and limit_relative topic after around 4 minutes. | Watt (W) | +| [serial]/cmd/limit_persistent_absolute | W | Set the inverter limit as a absolute value. The value will survive the night without power. The updated value will set immediatly within the inverter but show up in the web GUI and limit_relative topic after around 4 minutes. If you are using a already known inverter (known Hardware ID), the updated value will show up within a few seconds. | Watt (W) | | [serial]/cmd/limit_nonpersistent_relative | W | Set the inverter limit as a percentage of total production capability. The value will reset to the last persistent value at night without power. The updated value will show up in the web GUI and limit_relative topic immediatly. | % | -| [serial]/cmd/limit_nonpersistent_absolute | W | Set the inverter limit as a absolute value. The value will reset to the last persistent value at night without power. The updated value will show up in the web GUI and limit_relative topic after around 4 minutes. | Watt (W) | +| [serial]/cmd/limit_nonpersistent_absolute | W | Set the inverter limit as a absolute value. The value will reset to the last persistent value at night without power. The updated value will set immediatly within the inverter but show up in the web GUI and limit_relative topic after around 4 minutes. If you are using a already known inverter (known Hardware ID), the updated value will show up within a few seconds. | Watt (W) | | [serial]/cmd/power | W | Turn the inverter on (1) or off (0) | 0 or 1 | | [serial]/cmd/restart | W | Restarts the inverters (also resets YieldDay) | 1 | From 0d350b14bb83e239c9e8c07e2be4bf14945bcb04 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Tue, 4 Oct 2022 22:48:49 +0200 Subject: [PATCH 07/15] Migrated _rxBuffer from CircularBuffer to queue --- lib/CircularBuffer/CircularBuffer.h | 166 ---------------------------- lib/Hoymiles/src/HoymilesRadio.cpp | 34 +++--- lib/Hoymiles/src/HoymilesRadio.h | 3 +- 3 files changed, 17 insertions(+), 186 deletions(-) delete mode 100644 lib/CircularBuffer/CircularBuffer.h diff --git a/lib/CircularBuffer/CircularBuffer.h b/lib/CircularBuffer/CircularBuffer.h deleted file mode 100644 index 32db3871a..000000000 --- a/lib/CircularBuffer/CircularBuffer.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - CircularBuffer - An Arduino circular buffering library for arbitrary types. - - Created by Ivo Pullens, Emmission, 2014 -- www.emmission.nl - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef CircularBuffer_h -#define CircularBuffer_h - -#include - -#ifdef ESP8266 -#define DISABLE_IRQ noInterrupts() -#define RESTORE_IRQ interrupts() -#elif ESP32 -#define DISABLE_IRQ ; -#define RESTORE_IRQ ; -#else -#define DISABLE_IRQ \ - uint8_t sreg = SREG; \ - cli(); - -#define RESTORE_IRQ \ - SREG = sreg; -#endif - -template -class CircularBuffer { - - typedef BUFFERTYPE BufferType; - BufferType Buffer[BUFFERSIZE]; - - public: - CircularBuffer() : m_buff(Buffer) { - m_size = BUFFERSIZE; - clear(); - } - - /** Clear all entries in the circular buffer. */ - void clear(void) - { - m_front = 0; - m_fill = 0; - } - - /** Test if the circular buffer is empty */ - inline bool empty(void) const - { - return !m_fill; - } - - /** Return the number of records stored in the buffer */ - inline uint8_t available(void) const - { - return m_fill; - } - - /** Test if the circular buffer is full */ - inline bool full(void) const - { - return m_fill == m_size; - } - - inline uint8_t getFill(void) const { - return m_fill; - } - - /** Aquire record on front of the buffer, for writing. - * After filling the record, it has to be pushed to actually - * add it to the buffer. - * @return Pointer to record, or NULL when buffer is full. - */ - BUFFERTYPE* getFront(void) const - { - DISABLE_IRQ; - BUFFERTYPE* f = NULL; - if (!full()) - f = get(m_front); - RESTORE_IRQ; - return f; - } - - /** Push record to front of the buffer - * @param record Record to push. If record was aquired previously (using getFront) its - * data will not be copied as it is already present in the buffer. - * @return True, when record was pushed successfully. - */ - bool pushFront(BUFFERTYPE* record) - { - bool ok = false; - DISABLE_IRQ; - if (!full()) - { - BUFFERTYPE* f = get(m_front); - if (f != record) - *f = *record; - m_front = (m_front+1) % m_size; - m_fill++; - ok = true; - } - RESTORE_IRQ; - return ok; - } - - /** Aquire record on back of the buffer, for reading. - * After reading the record, it has to be pop'ed to actually - * remove it from the buffer. - * @return Pointer to record, or NULL when buffer is empty. - */ - BUFFERTYPE* getBack(void) const - { - BUFFERTYPE* b = NULL; - DISABLE_IRQ; - if (!empty()) - b = get(back()); - RESTORE_IRQ; - return b; - } - - /** Remove record from back of the buffer. - * @return True, when record was pop'ed successfully. - */ - bool popBack(void) - { - bool ok = false; - DISABLE_IRQ; - if (!empty()) - { - m_fill--; - ok = true; - } - RESTORE_IRQ; - return ok; - } - - protected: - inline BUFFERTYPE * get(const uint8_t idx) const - { - return &(m_buff[idx]); - } - inline uint8_t back(void) const - { - return (m_front - m_fill + m_size) % m_size; - } - - uint8_t m_size; // Total number of records that can be stored in the buffer. - BUFFERTYPE* const m_buff; - volatile uint8_t m_front; // Index of front element (not pushed yet). - volatile uint8_t m_fill; // Amount of records currently pushed. -}; - -#endif // CircularBuffer_h \ No newline at end of file diff --git a/lib/Hoymiles/src/HoymilesRadio.cpp b/lib/Hoymiles/src/HoymilesRadio.cpp index a011105c5..9272b278e 100644 --- a/lib/Hoymiles/src/HoymilesRadio.cpp +++ b/lib/Hoymiles/src/HoymilesRadio.cpp @@ -42,17 +42,15 @@ void HoymilesRadio::loop() if (_packetReceived) { Serial.println(F("Interrupt received")); while (_radio->available()) { - if (!_rxBuffer.full()) { - fragment_t* f; - f = _rxBuffer.getFront(); - memset(f->fragment, 0xcc, MAX_RF_PAYLOAD_SIZE); - f->len = _radio->getDynamicPayloadSize(); - f->channel = _radio->getChannel(); - if (f->len > MAX_RF_PAYLOAD_SIZE) - f->len = MAX_RF_PAYLOAD_SIZE; - - _radio->read(f->fragment, f->len); - _rxBuffer.pushFront(f); + if (!(_rxBuffer.size() > FRAGMENT_BUFFER_SIZE)) { + fragment_t f; + memset(f.fragment, 0xcc, MAX_RF_PAYLOAD_SIZE); + f.len = _radio->getDynamicPayloadSize(); + f.channel = _radio->getChannel(); + if (f.len > MAX_RF_PAYLOAD_SIZE) + f.len = MAX_RF_PAYLOAD_SIZE; + _radio->read(f.fragment, f.len); + _rxBuffer.push(f); } else { Serial.println(F("Buffer full")); _radio->flush_rx(); @@ -63,16 +61,16 @@ void HoymilesRadio::loop() } else { // Perform package parsing only if no packages are received if (!_rxBuffer.empty()) { - fragment_t* f = _rxBuffer.getBack(); - if (checkFragmentCrc(f)) { - std::shared_ptr inv = Hoymiles.getInverterByFragment(f); + fragment_t f = _rxBuffer.back(); + if (checkFragmentCrc(&f)) { + std::shared_ptr inv = Hoymiles.getInverterByFragment(&f); if (nullptr != inv) { // Save packet in inverter rx buffer char buf[30]; - snprintf(buf, sizeof(buf), "RX Channel: %d --> ", f->channel); - dumpBuf(buf, f->fragment, f->len); - inv->addRxFragment(f->fragment, f->len); + snprintf(buf, sizeof(buf), "RX Channel: %d --> ", f.channel); + dumpBuf(buf, f.fragment, f.len); + inv->addRxFragment(f.fragment, f.len); } else { Serial.println(F("Inverter Not found!")); } @@ -82,7 +80,7 @@ void HoymilesRadio::loop() } // Remove paket from buffer even it was corrupted - _rxBuffer.popBack(); + _rxBuffer.pop(); } } diff --git a/lib/Hoymiles/src/HoymilesRadio.h b/lib/Hoymiles/src/HoymilesRadio.h index 7c6246817..189321dfa 100644 --- a/lib/Hoymiles/src/HoymilesRadio.h +++ b/lib/Hoymiles/src/HoymilesRadio.h @@ -1,6 +1,5 @@ #pragma once -#include "CircularBuffer.h" #include "TimeoutHelper.h" #include "commands/CommandAbstract.h" #include "types.h" @@ -57,7 +56,7 @@ class HoymilesRadio { volatile bool _packetReceived = false; - CircularBuffer _rxBuffer; + std::queue _rxBuffer; TimeoutHelper _rxTimeout; serial_u _dtuSerial; From 06ae722b0b999946620beaff1fe68a9aadce4ac7 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Mon, 28 Nov 2022 18:29:31 +0100 Subject: [PATCH 08/15] webapp: upgrade dependencies --- webapp/package.json | 2 +- webapp/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index dd160a24b..83df423c2 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -28,7 +28,7 @@ "@vue/eslint-config-typescript": "^11.0.2", "@vue/tsconfig": "^0.1.3", "eslint": "^8.28.0", - "eslint-plugin-vue": "^9.7.0", + "eslint-plugin-vue": "^9.8.0", "npm-run-all": "^4.1.5", "sass": "^1.56.1", "typescript": "^4.9.3", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 055bc34c9..590c9c032 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -878,10 +878,10 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -eslint-plugin-vue@^9.7.0: - version "9.7.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.7.0.tgz#d391b9864f128ea2d1ee4dabeafb5f7c0cea981f" - integrity sha512-DrOO3WZCZEwcLsnd3ohFwqCoipGRSTKTBTnLwdhqAbYZtzWl0o7D+D8ZhlmiZvABKTEl8AFsqH1GHGdybyoQmw== +eslint-plugin-vue@^9.8.0: + version "9.8.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.8.0.tgz#91de2aabbee8cdbef078ccd4f650a9ecfa445f4f" + integrity sha512-E/AXwcTzunyzM83C2QqDHxepMzvI2y6x+mmeYHbVDQlKFqmKYvRrhaVixEeeG27uI44p9oKDFiyCRw4XxgtfHA== dependencies: eslint-utils "^3.0.0" natural-compare "^1.4.0" From 40203f73165b0bd0438bf4cd9b77cbabcf853362 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Mon, 28 Nov 2022 18:33:38 +0100 Subject: [PATCH 09/15] Fix #387: Allow a little bit more loss before HASS shows unavailable --- src/MqttHassPublishing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MqttHassPublishing.cpp b/src/MqttHassPublishing.cpp index d103281e3..5f77b70e9 100644 --- a/src/MqttHassPublishing.cpp +++ b/src/MqttHassPublishing.cpp @@ -120,7 +120,7 @@ void MqttHassPublishingClass::publishField(std::shared_ptr inv createDeviceInfo(deviceObj, inv); if (Configuration.get().Mqtt_Hass_Expire) { - root[F("exp_aft")] = Hoymiles.getNumInverters() * Configuration.get().Mqtt_PublishInterval * 2; + root[F("exp_aft")] = Hoymiles.getNumInverters() * Configuration.get().Mqtt_PublishInterval * 3; } if (devCls != 0) { root[F("dev_cla")] = devCls; From ae023674fcd8e210c3d50fe266ffb794eb21cedb Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Tue, 29 Nov 2022 18:48:39 +0100 Subject: [PATCH 10/15] Fix #399: Move pin assignment to generic environment to prevent compilation errors --- platformio.ini | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/platformio.ini b/platformio.ini index 6483354fa..28872a5d1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -20,14 +20,6 @@ build_flags = -DCOMPONENT_EMBED_FILES=webapp_dist/index.html.gz:webapp_dist/zones.json.gz:webapp_dist/favicon.ico:webapp_dist/js/app.js.gz -Wall -Wextra -Werror - ; Default pin assignment for all boards if not specified otherwise - -DHOYMILES_PIN_MISO=19 - -DHOYMILES_PIN_MOSI=23 - -DHOYMILES_PIN_SCLK=18 - -DHOYMILES_PIN_IRQ=16 - -DHOYMILES_PIN_CE=4 - -DHOYMILES_PIN_CS=5 - lib_deps = https://github.com/yubox-node-org/ESPAsyncWebServer bblanchon/ArduinoJson @ ^6.19.4 @@ -50,6 +42,13 @@ upload_port = COM4 [env:generic] board = esp32dev +build_flags = ${env.build_flags} + -DHOYMILES_PIN_MISO=19 + -DHOYMILES_PIN_MOSI=23 + -DHOYMILES_PIN_SCLK=18 + -DHOYMILES_PIN_IRQ=16 + -DHOYMILES_PIN_CE=4 + -DHOYMILES_PIN_CS=5 [env:olimex_esp32_poe] From d73a4d96746bd9f87fcb6885cbc52ba0603d101b Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Tue, 29 Nov 2022 18:49:36 +0100 Subject: [PATCH 11/15] Added lolin32_lite board --- platformio.ini | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/platformio.ini b/platformio.ini index 28872a5d1..ef61c0664 100644 --- a/platformio.ini +++ b/platformio.ini @@ -101,6 +101,7 @@ build_flags = ${env.build_flags} -DHOYMILES_PIN_CS=15 -DOPENDTU_ETHERNET + [env:LilyGO_T_ETH_POE] ; http://www.lilygo.cn/claprod_view.aspx?TypeId=21&Id=1344&FId=t28:21:28 board = esp32dev @@ -119,6 +120,7 @@ build_flags = ${env.build_flags} -DETH_MDC_PIN=23 -DETH_MDIO_PIN=18 + [env:esp_s3_12k_kit] ; https://www.waveshare.com/wiki/NodeMCU-ESP-S3-12K-Kit board = esp32-s3-devkitc-1 @@ -128,4 +130,17 @@ build_flags = ${env.build_flags} -DHOYMILES_PIN_SCLK=18 -DHOYMILES_PIN_IRQ=3 -DHOYMILES_PIN_CE=4 + -DHOYMILES_PIN_CS=5 + + +[env:lolin32_lite] +; https://www.makershop.de/plattformen/esp8266/wemos-lolin32/ +; https://www.az-delivery.de/products/esp32-lolin-lolin32 +board = lolin32_lite +build_flags = ${env.build_flags} + -DHOYMILES_PIN_MISO=19 + -DHOYMILES_PIN_MOSI=23 + -DHOYMILES_PIN_SCLK=18 + -DHOYMILES_PIN_IRQ=16 + -DHOYMILES_PIN_CE=17 -DHOYMILES_PIN_CS=5 \ No newline at end of file From c5daff53d7b2f32e6ab6e548b91cf364b16239d8 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Tue, 29 Nov 2022 18:52:31 +0100 Subject: [PATCH 12/15] webapp: Correct unit of max string power --- webapp/src/views/InverterAdminView.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp/src/views/InverterAdminView.vue b/webapp/src/views/InverterAdminView.vue index 47f0585b3..a19e7bbae 100644 --- a/webapp/src/views/InverterAdminView.vue +++ b/webapp/src/views/InverterAdminView.vue @@ -99,13 +99,13 @@ - W* + Wp* -
*) Input the kWp of the channel to +
*) Input the Wp of the channel to calculate irradiation.
From 977130ac0339bc42d16fcc35901a7ef923c2b19e Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Tue, 29 Nov 2022 18:57:50 +0100 Subject: [PATCH 13/15] webapp: Update hint text in NtpAdminView --- webapp/src/views/NtpAdminView.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webapp/src/views/NtpAdminView.vue b/webapp/src/views/NtpAdminView.vue index ce18c79c9..dbb104353 100644 --- a/webapp/src/views/NtpAdminView.vue +++ b/webapp/src/views/NtpAdminView.vue @@ -63,7 +63,8 @@
From 0d98cbbe91c872f9801286defc22145ecee6f48d Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Tue, 29 Nov 2022 19:01:06 +0100 Subject: [PATCH 14/15] webapp: add app.js.gz --- webapp_dist/js/app.js.gz | Bin 116733 -> 116793 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/webapp_dist/js/app.js.gz b/webapp_dist/js/app.js.gz index 476264e0da6765d45c069bba3130b17a8b2f7918..f79190d18fc8cb1693d215565b523cf63d5d98ce 100644 GIT binary patch delta 23121 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 delta 23103 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 From e8c517f650b83ecb37a8763619d78cad9c11984a Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Tue, 29 Nov 2022 22:55:14 +0100 Subject: [PATCH 15/15] Fix timezone specific issue when syncing time manually Time was off by one hour when using the sync button in the web UI. tm_isdst = -1 means that mktime determines the DST flag based on the current timezone --- src/WebApi_ntp.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/WebApi_ntp.cpp b/src/WebApi_ntp.cpp index a2d1e9361..4c3715473 100644 --- a/src/WebApi_ntp.cpp +++ b/src/WebApi_ntp.cpp @@ -277,6 +277,7 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request) local.tm_mday = root[F("day")].as(); // day of the month - [ 1 to 31 ] local.tm_mon = root[F("month")].as() - 1; // months since January - [ 0 to 11 ] local.tm_year = root[F("year")].as() - 1900; // years since 1900 + local.tm_isdst = -1; time_t t = mktime(&local); struct timeval now = { .tv_sec = t, .tv_usec = 0 };