From 16e5ba45ff2b4c477f2974ba6e69bb86e6be2a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Metrich?= <45318189+FredM67@users.noreply.github.com> Date: Sat, 9 Mar 2024 13:02:04 +0100 Subject: [PATCH] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit fb99ae1cdf6c4a730b228571943398f2252a3405 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Sat Mar 9 12:45:35 2024 +0100 Add DEMA commit 52892c216bb24a84eb3b306bd855d5e772279857 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Fri Mar 8 12:42:21 2024 +0100 Add parenthesis commit 843ad1c5c39ea61edb3e4cbddbcff542a2e1559b Merge: f817917 8ef9f7b Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Fri Mar 8 12:41:28 2024 +0100 Merge branch 'multi-relays' of https://github.com/FredM67/PVRouter-3-phase into multi-relays commit f8179178ad1bb59e95896107f9c56015dc4559e7 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Fri Mar 8 12:39:40 2024 +0100 Try DEMA commit 8ef9f7b109f1a73f865820015cf1b823b0a3f30e Author: Frédéric Metrich Date: Thu Mar 7 21:53:34 2024 +0100 Try double EWMA commit f9cfd640ba573f5a457e1edc988865429af03766 Merge: 685e204 d3bedad Author: Frédéric Metrich Date: Wed Mar 6 10:08:53 2024 +0100 Merge branch 'multi-relays' of https://github.com/FredM67/PVRouter-3-phase into multi-relays commit 685e204663b5208605a3e942393b3d7bc61cf523 Author: Frédéric Metrich Date: Wed Mar 6 10:08:49 2024 +0100 Fix test commit d3bedadf52bee6170c16646e505a842e7559989a Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Tue Mar 5 23:01:56 2024 +0100 Some doc commit fcf91d3a3e976c4d234c12df8877fb1ac6532ca1 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Wed Feb 28 21:03:13 2024 +0100 Fix links commit cdbd3e6e22f2f61a6d8aed3d8b8d4e83047d6f4e Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Wed Feb 28 20:59:52 2024 +0100 Default readme to fr commit b334503c7ae84919ea3e96c1a78e8a78b74ba949 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Wed Feb 28 20:39:10 2024 +0100 Set default to no relay commit a8160a0dd2bd8f6af972969fb65f747dc83d4eee Author: Frédéric Metrich Date: Wed Feb 28 15:02:38 2024 +0100 Typo commit 47b9ebea85e55d4052ec08081b19771e1fd3f12a Author: Frédéric Metrich Date: Wed Feb 28 14:57:37 2024 +0100 Multi relays engine commit 26f896b781efbe4e882211ad089f7ae0677ce3f3 Author: Frédéric Metrich Date: Mon Feb 26 16:12:32 2024 +0100 Squashed commit of the following: commit c73c56707a9a117782e6ba1d50dad73ad147cf06 Author: Frédéric Metrich Date: Mon Feb 26 11:11:24 2024 +0100 Add some alias for type_trait commit eb53c16c3b383636fe67ef69e4b1f79f697811e0 Author: Frédéric Metrich Date: Mon Feb 26 11:10:55 2024 +0100 Update color in schematic commit eac769e212aed59986f902bcc4318e3b40182d97 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Thu Feb 15 21:29:47 2024 +0100 Update Readme.md (#82) commit 82d84ee59fdf7121618036f469a9b4b8bdcd15f2 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Tue Feb 13 14:37:48 2024 +0100 Added Thermostat_tri_HC.pdf commit fad5e19d184ebaca3c9c28775c33db6bf8169ffe Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Tue Feb 13 14:36:53 2024 +0100 Update Thermostat_tri_HC.drawio commit f03b4ca1154081332f08630e5087c53441d0ce41 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Mon Feb 12 11:07:04 2024 +0100 WIP: 1772479 Fix mvAvg commit 5e5646a87c3171f6502554d15db9f1cfc45aaf14 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Mon Feb 5 00:04:30 2024 +0100 Update CodeQL commit 4684e01c8d943dbc6905200ff75231f572f936e2 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Sun Feb 4 23:35:01 2024 +0100 Upt readme commit d9574942e85f6383fbd3b5db0fb3869f553b86e4 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Sun Feb 4 23:32:24 2024 +0100 Try again commit 46884f3e394aec08f183b89d77574258b6eddb07 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Sun Feb 4 23:30:21 2024 +0100 Try fix commit c7db21ecee9ed8eba8b796a8ccf45d0fd8629aa6 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Sun Feb 4 23:28:09 2024 +0100 Fix readme commit 248a89aa92b0b65f696dee206f19fd693d667112 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Sun Feb 4 23:23:14 2024 +0100 Reorganize readmes commit fff68e050ec7cee872a0b0472fe43d0060fe05fe Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Sun Feb 4 23:11:19 2024 +0100 Fix branch name commit 656308b35b81c26ae137cd11c3aac04d2529bad0 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Sun Feb 4 23:10:39 2024 +0100 Fix filenames commit b911d8d9162ff69b2f591248cdb857c2cbc5e0d3 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Sun Feb 4 23:08:55 2024 +0100 Fix links commit 83d5591bad46a031d885f809b694aebe9e6f2dd7 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Sun Feb 4 23:07:18 2024 +0100 Multilinugal readme + more details commit 1772479ab36ed7630e16b3b6989479528de0239b Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Fri Feb 2 11:39:13 2024 +0100 Fix mvAvg commit 90204aa90e974376f71b6b5aafa4b6f7ea478902 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Mon Jan 22 22:02:08 2024 +0100 Fix relay template commit eef84ea712ca974c4b78f1d9ba9a12b3354476f8 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Mon Jan 22 16:36:28 2024 +0100 Enhanced sliding average... commit 1ea9c5359b312927c34330e6bcdf5a1a2e443e1b Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Tue Nov 14 05:22:20 2023 +0100 Reorganize folders commit 8522341013f48f3cdc3902979a7841a3fd198cf8 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Tue Nov 14 05:20:39 2023 +0100 Upd commit a6a660b096ee56b6720fe93719b3b6446ab4519b Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Wed Nov 8 23:11:58 2023 +0100 Update Readme.md commit 145c86e8aa9b1a480f242d45190b305864065dd6 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Mon Nov 6 09:13:08 2023 +0100 Update Readme.md commit 4d0936b12672faf2be82b3a853320f79c1e40fb7 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Mon Nov 6 07:40:13 2023 +0100 Update Readme.md commit 85910b9f9745431e164e80d1f899355a78612254 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Mon Nov 6 07:21:08 2023 +0100 Update Readme.md commit efbda93c789e882383738638b882a608c0dac1a4 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Mon Nov 6 07:19:52 2023 +0100 Update Readme.md (#79) commit cfb0204c64a764e463b51736b5e7085e11815717 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Wed Oct 4 07:58:16 2023 +0200 Formatting commit e0cefcd666d08ca53a3cc7dfe8cd20926fa3a1f1 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Thu Sep 21 17:29:28 2023 +0200 Fix comment commit 0cf1661cd2a63b1cd4678a69e1b7a0c3e5475310 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Thu Sep 21 13:21:59 2023 +0200 Update schema commit 839af94fb3c6daef4f74231dbb98619a796d6d74 Author: Frédéric Metrich <45318189+FredM67@users.noreply.github.com> Date: Wed Sep 20 15:58:02 2023 +0200 Move pin assignment to ctor --- Mk2_3phase_RFdatalog_temp/config.h | 7 +- Mk2_3phase_RFdatalog_temp/ewma_avg.hpp | 100 ++++++ Mk2_3phase_RFdatalog_temp/main.cpp | 6 +- Mk2_3phase_RFdatalog_temp/movingAvg.h | 55 ---- Mk2_3phase_RFdatalog_temp/processing.cpp | 7 +- .../type_traits/integral_constant.hpp | 14 +- Mk2_3phase_RFdatalog_temp/types.h | 19 ++ Mk2_3phase_RFdatalog_temp/utils.h | 4 +- Mk2_3phase_RFdatalog_temp/utils_relay.h | 209 +++++++++++-- Mk2_3phase_RFdatalog_temp/validation.h | 42 ++- PVRouter-3-phase.code-workspace | 7 +- Readme.en.md | 265 ++++++++++++++++ Readme.fr.md | 280 ----------------- Readme.md | 285 +++++++++--------- dev/MathPerfTests/.gitignore | 5 + dev/MathPerfTests/.vscode/extensions.json | 10 + dev/MathPerfTests/MathPerfTests.ino | 112 +++++-- dev/MathPerfTests/movingAvg.h | 4 +- dev/MathPerfTests/platformio.ini | 24 ++ 19 files changed, 913 insertions(+), 542 deletions(-) create mode 100644 Mk2_3phase_RFdatalog_temp/ewma_avg.hpp create mode 100644 Readme.en.md delete mode 100644 Readme.fr.md create mode 100644 dev/MathPerfTests/.gitignore create mode 100644 dev/MathPerfTests/.vscode/extensions.json create mode 100644 dev/MathPerfTests/platformio.ini diff --git a/Mk2_3phase_RFdatalog_temp/config.h b/Mk2_3phase_RFdatalog_temp/config.h index 9c3be1d0..7bea63d1 100644 --- a/Mk2_3phase_RFdatalog_temp/config.h +++ b/Mk2_3phase_RFdatalog_temp/config.h @@ -50,7 +50,7 @@ inline constexpr bool OVERRIDE_PIN_PRESENT{ false }; /**< set #endif inline constexpr bool WATCHDOG_PIN_PRESENT{ false }; /**< set it to 'true' if there's a watch led */ -inline constexpr bool RELAY_DIVERSION{ false }; /**< set it to 'true' if a relay is used for diversion */ +inline constexpr bool RELAY_DIVERSION{ false }; /**< set it to 'true' if a relay is used for diversion */ inline constexpr bool DUAL_TARIFF{ false }; /**< set it to 'true' if there's a dual tariff each day AND the router is connected to the billing meter */ // ----------- Pinout assignments ----------- @@ -75,14 +75,15 @@ inline constexpr uint8_t physicalLoadPin[NO_OF_DUMPLOADS]{ 5, 7 }; /**< inline constexpr uint8_t loadPrioritiesAtStartup[NO_OF_DUMPLOADS]{ 0, 1 }; /**< load priorities and states at startup */ // Set the value to 0xff when the pin is not needed (feature deactivated) -inline constexpr uint8_t relayPin{ 0xff }; /**< for 3-phase PCB, relay trigger */ inline constexpr uint8_t dualTariffPin{ 0xff }; /**< for 3-phase PCB, off-peak trigger */ inline constexpr uint8_t diversionPin{ 0xff }; /**< if LOW, set diversion on standby */ inline constexpr uint8_t rotationPin{ 0xff }; /**< if LOW, trigger a load priority rotation */ inline constexpr uint8_t forcePin{ 0xff }; /**< for 3-phase PCB, force pin */ inline constexpr uint8_t watchDogPin{ 0xff }; /**< watch dog LED */ -inline constexpr relayOutput relay_Output{ relayPin, 1000, 200, 1, 1 }; /**< config for relay diversion, see class definition for defaults and advanced options */ +inline constexpr uint8_t tempSensorPin{ 0xff }; /**< for 3-phase PCB, sensor pin */ + +inline constexpr RelayEngine relays{ { { 0xff, 1000, 200, 1, 1 } } }; /**< config for relay diversion, see class definition for defaults and advanced options */ inline constexpr uint8_t ul_OFF_PEAK_DURATION{ 8 }; /**< Duration of the off-peak period in hours */ inline constexpr pairForceLoad rg_ForceLoad[NO_OF_DUMPLOADS]{ { -3, 2 } }; /**< force config for load #1 ONLY for dual tariff */ diff --git a/Mk2_3phase_RFdatalog_temp/ewma_avg.hpp b/Mk2_3phase_RFdatalog_temp/ewma_avg.hpp new file mode 100644 index 00000000..2bc7e72a --- /dev/null +++ b/Mk2_3phase_RFdatalog_temp/ewma_avg.hpp @@ -0,0 +1,100 @@ +/** + * @file ewma_avg.hpp + * @author Frédéric Metrich (frederic.metrich@live.fr) + * @brief This file implements an Exponentially Weighted Moving Average template class + * @version 0.1 + * @date 2024-02-27 + * + * @section description Description + * The Exponentially Weighted Moving Average (EWMA) is a quantitative or statistical measure used to model or describe a time series. + * The EWMA is widely used in finance, the main applications being technical analysis and volatility modeling. + * + * The moving average is designed as such that older observations are given lower weights. + * The weights fall exponentially as the data point gets older – hence the name exponentially weighted. + * + * The only decision a user of the EWMA must make is the parameter alpha. + * The parameter decides how important the current observation is in the calculation of the EWMA. + * The higher the value of alpha, the more closely the EWMA tracks the original time series. + * + * @section note Note + * This class is implemented in way to use only integer math. + * This comes with some restrictions on the alpha parameter, but the benefit of full integer math wins + * on the side-drawback. + * + * @copyright Copyright (c) 2024 + * + */ + +#ifndef EWMA_AVG_H +#define EWMA_AVG_H + +#include + +#include "type_traits.hpp" + +/** + * @brief Helper compile-time function to retrieve the previous power of 2 of the given number (120 => 64 => 6) + * + * @param v The input number + * @return constexpr uint8_t The next power of two + */ +constexpr uint8_t round_up_to_power_of_2(uint16_t v) +{ + if (__builtin_popcount(v) == 1) { return __builtin_ctz(v) - 1; } + + uint8_t next_pow_of_2{ 0 }; + + while (v) + { + v >>= 1; + ++next_pow_of_2; + } + + return --next_pow_of_2; +} + +/** + * @brief Exponentially Weighted Moving Average + * + * @details The smoothing factor is the approximate amount of values taken to calculate the average. + * Since the Arduino is very slow and does not provide any dedicated math co-processor, + * the smoothing factor will be rounded to the previous power of 2. Ie 120 will be rounded to 64. + * This allows to perform all the calculations with integer math, which is much faster ! + * + * @note Because of the 'sign extension', the sign is copied into lower bits. + * + * @tparam A Smoothing factor + * @param input Input value + * @return long Output value + */ +template< uint8_t A = 10 > +class EWMA_average +{ +public: + void addValue(int32_t input) + { + ema_raw = ema_raw - ema + input; + ema = ema_raw >> round_up_to_power_of_2(A); + + ema_ema_raw = ema_ema_raw - ema_ema + ema; + ema_ema = ema_ema_raw >> round_up_to_power_of_2(A); + } + + auto getAverageS() const + { + return ema; + } + + auto getAverageD() const + { + return (ema << 1) - ema_ema; + } + +private: + int32_t ema_ema_raw{ 0 }; + int32_t ema_ema{ 0 }; + int32_t ema_raw{ 0 }; + int32_t ema{ 0 }; +}; + +#endif \ No newline at end of file diff --git a/Mk2_3phase_RFdatalog_temp/main.cpp b/Mk2_3phase_RFdatalog_temp/main.cpp index 8fd0a405..846d2765 100644 --- a/Mk2_3phase_RFdatalog_temp/main.cpp +++ b/Mk2_3phase_RFdatalog_temp/main.cpp @@ -377,8 +377,8 @@ void loop() if constexpr (RELAY_DIVERSION) { - relay_Output.inc_duration(); - relay_Output.proceed_relay(); + relays.inc_duration(); + relays.proceed_relays(); } } } @@ -407,7 +407,7 @@ void loop() if constexpr (RELAY_DIVERSION) { - relay_Output.update_average(tx_data.power); + relays.update_average(tx_data.power); } if constexpr (TEMP_SENSOR_PRESENT) diff --git a/Mk2_3phase_RFdatalog_temp/movingAvg.h b/Mk2_3phase_RFdatalog_temp/movingAvg.h index fa56c968..a0ba6da8 100644 --- a/Mk2_3phase_RFdatalog_temp/movingAvg.h +++ b/Mk2_3phase_RFdatalog_temp/movingAvg.h @@ -16,61 +16,6 @@ #include "type_traits.hpp" -/** - * @brief Helper compile-time function to retrieve the previous power of 2 of the given number (120 => 64 => 6) - * - * @param v The input number - * @return constexpr uint8_t The next power of two - */ -constexpr uint8_t round_up_to_power_of_2(uint16_t v) -{ - if (__builtin_popcount(v) == 1) { return __builtin_ctz(v) - 1; } - - uint8_t next_pow_of_2{ 0 }; - - while (v) - { - v >>= 1; - ++next_pow_of_2; - } - - return --next_pow_of_2; -} - -/** - * @brief Exponentially Weighted Moving Average - * - * @details The smoothing factor is the approximate amount of values taken to calculate the average. - * Since the Arduino is very slow and does not provide any dedicated math co-processor, - * the smoothing factor will be rounded to the previous power of 2. Ie 120 will be rounded to 64. - * This allows to perform all the calculations with integer math, which is much faster ! - * - * @note Because of the 'sign extension', the sign is copied into lower bits. - * - * @tparam A Smoothing factor - * @param input Input value - * @return long Output value - */ -template< uint8_t A = 10 > -class EWMA_average -{ -public: - void addValue(int32_t input) - { - w = w - x + input; - x = w >> round_up_to_power_of_2(A); - } - - auto getAverage() const - { - return x; - } - -private: - int32_t w{ 0 }; - int32_t x{ 0 }; -}; - /** * @brief Template class for implementing a sliding average * diff --git a/Mk2_3phase_RFdatalog_temp/processing.cpp b/Mk2_3phase_RFdatalog_temp/processing.cpp index b211f37a..e7a65c64 100644 --- a/Mk2_3phase_RFdatalog_temp/processing.cpp +++ b/Mk2_3phase_RFdatalog_temp/processing.cpp @@ -177,8 +177,7 @@ void initializeOptionalPins() if constexpr (RELAY_DIVERSION) { - pinMode(relayPin, OUTPUT); - delay(100); + relays.initializePins(); } if constexpr (WATCHDOG_PIN_PRESENT) @@ -583,6 +582,7 @@ void processMinusHalfCycle(const uint8_t phase) * * @ingroup TimeCritical */ +uint8_t nextLogicalLoadToBeAdded() __attribute__((optimize("-O3"))); uint8_t nextLogicalLoadToBeAdded() { for (uint8_t index = 0; index < NO_OF_DUMPLOADS; ++index) @@ -603,6 +603,7 @@ uint8_t nextLogicalLoadToBeAdded() * * @ingroup TimeCritical */ +uint8_t nextLogicalLoadToBeRemoved() __attribute__((optimize("-O3"))); uint8_t nextLogicalLoadToBeRemoved() { uint8_t index{ NO_OF_DUMPLOADS }; @@ -651,6 +652,8 @@ void processLatestContribution(const uint8_t phase) * * @ingroup TimeCritical */ +void processDataLogging() __attribute__((optimize("-O3"))); + void processDataLogging() { if (++n_cycleCountForDatalogging < DATALOG_PERIOD_IN_MAINS_CYCLES) diff --git a/Mk2_3phase_RFdatalog_temp/type_traits/integral_constant.hpp b/Mk2_3phase_RFdatalog_temp/type_traits/integral_constant.hpp index 632d3d97..bd49d2e0 100644 --- a/Mk2_3phase_RFdatalog_temp/type_traits/integral_constant.hpp +++ b/Mk2_3phase_RFdatalog_temp/type_traits/integral_constant.hpp @@ -7,8 +7,18 @@ template< typename T, T v > struct integral_constant { - static const T value = v; + static constexpr T value = v; + using value_type = T; + using type = integral_constant; // using injected-class-name + constexpr operator value_type() const noexcept + { + return value; + } + constexpr value_type operator()() const noexcept + { + return value; + } // since c++14 }; typedef integral_constant< bool, true > true_type; -typedef integral_constant< bool, false > false_type; \ No newline at end of file +typedef integral_constant< bool, false > false_type; diff --git a/Mk2_3phase_RFdatalog_temp/types.h b/Mk2_3phase_RFdatalog_temp/types.h index 2787ac6b..48479946 100644 --- a/Mk2_3phase_RFdatalog_temp/types.h +++ b/Mk2_3phase_RFdatalog_temp/types.h @@ -88,4 +88,23 @@ template< typename _Tp > constexpr size_t size(const _Tp (& /*__array*/)[0]) noe return 0; } +template< class... Ts > +constexpr uint8_t ival(Ts... Vs) +{ + char vals[sizeof...(Vs)] = { Vs... }; + uint8_t result = 0; + for (uint8_t i = 0; i < sizeof...(Vs); i++) + { + result *= 10; + result += vals[i] - '0'; + } + return result; +} + +template< char... Vs > +constexpr integral_constant< uint8_t, ival(Vs...) > operator""_i() +{ + return {}; +} + #endif // _TYPES_H diff --git a/Mk2_3phase_RFdatalog_temp/utils.h b/Mk2_3phase_RFdatalog_temp/utils.h index a28d9608..552d8787 100644 --- a/Mk2_3phase_RFdatalog_temp/utils.h +++ b/Mk2_3phase_RFdatalog_temp/utils.h @@ -119,7 +119,7 @@ inline void printConfiguration() { DBUGLN(F("is present")); - relay_Output.printRelayConfiguration(); + relays.printConfiguration(); } else { @@ -264,7 +264,7 @@ inline void printForSerialText() if constexpr (RELAY_DIVERSION) { Serial.print(F("/")); - Serial.print(relay_Output.get_average()); + Serial.print(relays.get_average()); } for (phase = 0; phase < NO_OF_PHASES; ++phase) diff --git a/Mk2_3phase_RFdatalog_temp/utils_relay.h b/Mk2_3phase_RFdatalog_temp/utils_relay.h index 636171cd..4c4beca0 100644 --- a/Mk2_3phase_RFdatalog_temp/utils_relay.h +++ b/Mk2_3phase_RFdatalog_temp/utils_relay.h @@ -13,21 +13,18 @@ #define _UTILS_RELAY_H #include "types.h" +#include "type_traits.hpp" #include "config_system.h" #include "movingAvg.h" +#include "ewma_avg.hpp" #include "utils_pins.h" /** * @brief Relay diversion config and engine - * @details By default, the sliding average is calculated over 1 minute. - * If the user wants to calculate over a longer period, - * decare the variable like this: - * relayOutput<2> relay_Output{ relayPin, 1000, 200, 1, 1 } * - * @tparam T Duration in minutes of the sliding average */ -template< uint8_t T = 1 > class relayOutput +class relayOutput { public: constexpr relayOutput() = delete; @@ -118,6 +115,16 @@ template< uint8_t T = 1 > class relayOutput return minOFF; } + /** + * @brief Return the state + * + * @return auto + */ + auto isRelayON() const + { + return relayIsON; + } + /** * @brief Increment the duration of the current state * @details This function must be called every second. @@ -134,39 +141,30 @@ template< uint8_t T = 1 > class relayOutput /** * @brief Proceed with the relay * + * @return bool True if state has changed */ - void proceed_relay() const + bool proceed_relay(const int32_t currentAvgPower) const { - const auto currentAvgPower{ sliding_Average.getAverage() }; - // To avoid changing sign, surplus is a negative value if (currentAvgPower < surplusThreshold) { - try_turnON(); + return try_turnON(); } else if (currentAvgPower > importThreshold) { - try_turnOFF(); + return try_turnOFF(); } - } - - inline static auto get_average() - { - return sliding_Average.getAverage(); - } - - inline static void update_average(int16_t currentPower) - { - sliding_Average.addValue(currentPower); + return false; } /** * @brief Print the configuration of the current relay-diversion * */ - void printRelayConfiguration() const + void printRelayConfiguration(uint8_t idx) const { - Serial.println(F("\tRelay configuration:")); + Serial.print(F("\tRelay configuration: #")); + Serial.println(idx + 1); Serial.print(F("\t\tPin is ")); Serial.println(get_pin()); @@ -188,12 +186,13 @@ template< uint8_t T = 1 > class relayOutput /** * @brief Turn ON the relay if the 'time' condition is met * + * @return bool True if state has changed */ - void try_turnON() const + bool try_turnON() const { if (relayIsON || duration < minOFF) { - return; + return false; } setPinON(relay_pin); @@ -202,17 +201,20 @@ template< uint8_t T = 1 > class relayOutput relayIsON = true; duration = 0; + + return true; } /** * @brief Turn OFF the relay if the 'time' condition is met * + * @return bool True if state has changed */ - void try_turnOFF() const + bool try_turnOFF() const { if (!relayIsON || duration < minON) { - return; + return false; } setPinOFF(relay_pin); @@ -221,6 +223,8 @@ template< uint8_t T = 1 > class relayOutput relayIsON = false; duration = 0; + + return true; } private: @@ -232,8 +236,157 @@ template< uint8_t T = 1 > class relayOutput mutable uint16_t duration{ 0 }; /**< Duration of the current state */ mutable bool relayIsON{ false }; /**< True if the relay is ON */ +}; + +/** + * @brief This class implements the relay management engine + * + * @tparam D The duration in minutes of the sliding average + * @tparam N The number of relays to be used + */ +template< uint8_t N, uint8_t D = 10 > +class RelayEngine +{ +public: + /** + * @brief Construct a list of relays + * + */ + constexpr RelayEngine(const relayOutput (&ref)[N]) + : relay(ref) + { + } + + constexpr RelayEngine(integral_constant< uint8_t, D > ic, const relayOutput (&ref)[N]) + : relay(ref) + { + } + + + constexpr auto get_size() const + { + return N; + } + + /** + * @brief Get the relay object + * + * @tparam idx The index of the relay + * @return constexpr const auto& The relay object + */ + constexpr const auto& get_relay(uint8_t idx) const + { + return relay[idx]; + } - static inline movingAvg< int16_t, T, 60 / DATALOG_PERIOD_IN_SECONDS > sliding_Average; + /** + * @brief Get the current average + * + * @return auto The current average + */ + inline static auto get_average() + { + return ewma_average.getAverageS(); + } + + /** + * @brief Update the sliding average + * + * @param currentPower Current power at the grid + */ + inline static void update_average(int16_t currentPower) + { + ewma_average.addValue(currentPower); + } + + void inc_duration() const __attribute__((optimize("-O3"))); + + /** + * @brief Proceed all relays in increasing order (surplus) or decreasing order (import) + * + */ + void proceed_relays() const + { + if (settle_change != 0) + { + // A relay has been toggle less than a minute ago, wait until changes take effect + return; + } + + if (ewma_average.getAverageS() > 0) + { + // Currently importing, try to turn OFF some relays + uint8_t idx{ N }; + do + { + if (relay[--idx].proceed_relay(ewma_average.getAverageS())) + { + settle_change = 60; + return; + } + } while (idx); + } + else + { + // Remaining surplus, try to turn ON more relays + uint8_t idx{ 0 }; + do + { + if (relay[idx].proceed_relay(ewma_average.getAverageS())) + { + settle_change = 60; + return; + } + } while (++idx < N); + } + } + + void initializePins() const + { + uint8_t idx{ N }; + do + { + pinMode(relay[--idx].get_pin(), OUTPUT); + delay(100); + } while (idx); + } + + /** + * @brief Print the configuration of each relay + * + */ + void printConfiguration() const + { + Serial.println(F("\t*** Relay(s) configuration ***")); + Serial.print(F("\t\tSliding average: ")); + Serial.println(D); + + for (uint8_t i = 0; i < N; ++i) + { + relay[i].printRelayConfiguration(i); + } + } + +private: + const relayOutput relay[N]; /**< Array of relays */ + + mutable uint8_t settle_change{ 60 }; /**< Delay in seconds until next change occurs */ + + static inline EWMA_average< D * 60 / DATALOG_PERIOD_IN_SECONDS > ewma_average; /**< EWMA average */ }; +template< uint8_t N, uint8_t D > void RelayEngine< N, D >::inc_duration() const +{ + uint8_t idx{ N }; + do + { + relay[--idx].inc_duration(); + } while (idx); + + if (settle_change) + { + --settle_change; + } +} + #endif // _UTILS_RELAY_H \ No newline at end of file diff --git a/Mk2_3phase_RFdatalog_temp/validation.h b/Mk2_3phase_RFdatalog_temp/validation.h index 0a476ee1..9329531d 100644 --- a/Mk2_3phase_RFdatalog_temp/validation.h +++ b/Mk2_3phase_RFdatalog_temp/validation.h @@ -34,8 +34,6 @@ static_assert((PRIORITY_ROTATION == RotationModes::PIN) ^ (rotationPin == 0xff), static_assert(OVERRIDE_PIN_PRESENT ^ (forcePin == 0xff), "******** Wrong pin value for override command. Please check your config.h ! ********"); static_assert(WATCHDOG_PIN_PRESENT ^ (watchDogPin == 0xff), "******** Wrong pin value for watchdog. Please check your config.h ! ********"); -static_assert(RELAY_DIVERSION ^ (relayPin == 0xff), "******** Wrong pin value for relay diversion. Please check your config.h ! ********"); - static_assert(DUAL_TARIFF ^ (dualTariffPin == 0xff), "******** Wrong pin value for dual tariff. Please check your config.h ! ********"); static_assert(!DUAL_TARIFF | (ul_OFF_PEAK_DURATION == 0), "******** Off-peak duration cannot be zero. Please check your config.h ! ********"); static_assert(!(DUAL_TARIFF & (ul_OFF_PEAK_DURATION > 12)), "******** Off-peak duration cannot last more than 12 hours. Please check your config.h ! ********"); @@ -87,7 +85,7 @@ check_pins() bit_set(used_pins, watchDogPin); } - //physicalLoadPin + //physicalLoadPin for the TRIACS for (const auto &loadPin : physicalLoadPin) { if (loadPin == 0xff) @@ -99,9 +97,46 @@ check_pins() bit_set(used_pins, loadPin); } + if constexpr (RELAY_DIVERSION) + { + for (uint8_t idx = 0; idx < relays.get_size(); ++idx) + { + const auto relayPin = relays.get_relay(idx).get_pin(); + + if (relayPin != 0xff) + { + if (bitRead(used_pins, relayPin)) + return 0; + + bit_set(used_pins, relayPin); + } + } + } + return used_pins; } +constexpr uint16_t check_relay_pins() +{ + bool pins_ok{ true }; + + for (uint8_t idx = 0; idx < relays.get_size(); ++idx) + { + const auto relayPin = relays.get_relay(idx).get_pin(); + + if constexpr (RELAY_DIVERSION) + { + pins_ok &= (relayPin != 0xff); + } + else + { + pins_ok &= (relayPin == 0xff); + } + } + + return pins_ok; +} + constexpr bool check_load_priorities() { uint8_t _sum{ 0 }; @@ -127,5 +162,6 @@ static_assert(check_pins(), "******** Duplicate pin definition ! Please check yo static_assert((check_pins() & B00000011) == 0, "******** Pins 0 & 1 are reserved for RX/TX ! Please check your config ! ********"); static_assert((check_pins() & 0xC000) == 0, "******** Pins 14 and/or 15 do not exist ! Please check your config ! ********"); static_assert(!(RF_CHIP_PRESENT && ((check_pins() & 0x3C04) != 0)), "******** Pins from RF chip are reserved ! Please check your config ! ********"); +static_assert(check_relay_pins(), "******** Wrong pin(s) configuration for relay(s) ********"); #endif diff --git a/PVRouter-3-phase.code-workspace b/PVRouter-3-phase.code-workspace index 7a76bec6..2329b137 100644 --- a/PVRouter-3-phase.code-workspace +++ b/PVRouter-3-phase.code-workspace @@ -19,10 +19,15 @@ "name": "6-channel raw sampler", "path": "dev/RawSamplesTool_6chan" }, + { + "name": "MathPerfTests", + "path": "dev/MathPerfTests" + } ], "settings": { "files.associations": { - "xtr1common": "cpp" + "xtr1common": "cpp", + "new": "cpp" } } } \ No newline at end of file diff --git a/Readme.en.md b/Readme.en.md new file mode 100644 index 00000000..5bdde103 --- /dev/null +++ b/Readme.en.md @@ -0,0 +1,265 @@ +
+ +[![GitHub issues](https://img.shields.io/github/issues/FredM67/PVRouter-3-phase)](https://github.com/FredM67/PVRouter-3-phase/issues) +[![GitHub forks](https://img.shields.io/github/forks/FredM67/PVRouter-3-phase)](https://github.com/FredM67/PVRouter-3-phase/network) +[![GitHub stars](https://img.shields.io/github/stars/FredM67/PVRouter-3-phase)](https://github.com/FredM67/PVRouter-3-phase/stargazers) +[![CodeQL](https://github.com/FredM67/PVRouter-3-phase/actions/workflows/codeql.yml/badge.svg)](https://github.com/FredM67/PVRouter-3-phase/actions/workflows/codeql.yml) +[![Doxygen](https://github.com/FredM67/PVRouter-3-phase/actions/workflows/doxygen-gh-pages.yml/badge.svg)](https://github.com/FredM67/PVRouter-3-phase/actions/workflows/doxygen-gh-pages.yml) +
+[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua) +
+
+ [![en](https://img.shields.io/badge/lang-en-red.svg)](Readme.en.md) + [![fr](https://img.shields.io/badge/lang-fr-blue.svg)](Readme.md) +
+ +# PVRouter (3-phase version) + +My version of the 3-phase Mk2PVRouter firmware (see ). + +Robin Emley already proposes a 3 phase PV-router (). +It supports up to 12 resistive output loads, which are completely independent. + +--- +**_NOTE:_** For a single phase version, please see [PVRouter-Single](https://github.com/FredM67/PVRouter-Single). + +--- + +- [PVRouter (3-phase version)](#pvrouter-3-phase-version) + - [Photo Gallery](#photo-gallery) + - [Schematic of the mainboard](#schematic-of-the-mainboard) + - [Implementation documentation](#implementation-documentation) + - [End-user documentation](#end-user-documentation) + - [Overview](#overview) + - [Load priorities management](#load-priorities-management) + - [Off-peak period detection](#off-peak-period-detection) + - [Force full power](#force-full-power) + - [Temperature sensor](#temperature-sensor) + - [Enphase zero-export profile](#enphase-zero-export-profile) +- [How to wire the router](#how-to-wire-the-router) +- [Use cases](#use-cases) + - [Requirements](#requirements) + - [Heater with mechanical thermostat](#heater-with-mechanical-thermostat) + - [Migrate from single-phase to 3-phase (with neutral wire)](#migrate-from-single-phase-to-3-phase-with-neutral-wire) + - [Wiring](#wiring) + - [Heater with ACI single phase thermostat](#heater-with-aci-single-phase-thermostat) + - [Heater with ACI 3-phase thermostat (without neutral wire)](#heater-with-aci-3-phase-thermostat-without-neutral-wire) + - [Alternatives WITHOUT neutral wire](#alternatives-without-neutral-wire) + - [Heater with mechanical thermostat](#heater-with-mechanical-thermostat-1) + - [Support](#support) + - [Roadmap](#roadmap) + - [Contributing](#contributing) + - [Authors and acknowledgment](#authors-and-acknowledgment) + +## Photo Gallery + +[Here](Gallery.md) a couple of pictures of assembled routers. + +## Schematic of the mainboard + +[Here](../../schematics/3phase_Mainboard.pdf) the schematic of the mainboard. + +## Implementation documentation + +You can start reading the documentation here [3-phase diverter](https://fredm67.github.io/PVRouter-3-phase/html/index.html). + +## End-user documentation + +### Overview + +Goal was to modify/optimize the sketch for the "special" case of a 3-phase water heater. A 3-phase water heater is composed in fact of 3 independent heating elements. Most of the time, such a heater can be connected in mono, or 3-phase WYE or 3-phase Delta. +When connected in WYE (without varistor), there's no need of a neutral wire because the system is equally distributed, so at any time, there's no current flowing to the neutral. + +If a diverter is used, the neutral wire must be connected. + +Added functionalities: + +- load priorities management (configurable) +- off-peak period detection (configurable) +- force full power +- temperature sensor (just reading for the moment) +- optimized (RF) data logging +- serial output in JSON or TXT + +The original sketch had to be completely re-worked and re-structured to support temperature reading. In the original sketch, the ISR "just" reads and converts the analog data, and the processing is done in the loop. This won't work with a temperature sensor because of its slow performance. It would break the whole system, current/voltage data will be lost, ... + +Now, all the time-critical processing is done inside the ISR, other stuff like (RF) data logging, Serial printing, temperature reading is made inside the loop(). The ISR and main processor communicate with each other through "events". + +### Load priorities management + +In my variant of Robin's sketch, the 3 loads are still physically independent, so it means, the router will divert surplus of energy to the first load (highest priority) from 0% to 100%, then to the second (0% to 100%) and finally to the third. + +To avoid that the priorities stay all the time unchanged, which would mean that load 1 will run much more than load 2, which again will run much more than 3, I've added a priority management. +Each day, the load priorities are rotated, so over many days, all the heating elements will run somehow the same amount of time. + +### Off-peak period detection + +Depending on the country, some energy meters provide a switch/relay which toggles on at the beginning of the off-peak period. It is intended to control a relay. If you wire it to a free digital pin of the router (in my case D3), you can detect off-peak/peak period. + +### Force full power + +Support has been added to force full power on specific loads. Each load can be forced independently from each other, start time and duration can be set individually. + +In my variant, that's used to switch the heater one during off-peak period if not enough surplus has been routed during the day. Here, to optimize the behavior, a temp-sensor will be used to check the temperature of the water and decide to switch on or not during night. + +### Temperature sensor + +For the moment, just reading. It'll be used to optimize force full power, to make the right decision during night. + +### Enphase zero-export profile + +When zero-export settings is enabled, the PV system curtails power production if the production of the system exceeds the consumption needs of the site. This ensures zero feed into the grid. + +As a side effect, the diverter won't see at any time surplus of energy. +So the idea is to apply a certain offset to the energy measured by the diverter. +As it is already commented in the code, after setting a negative value to *REQUIRED_EXPORT_IN_WATTS*, the diverter will act as a PV generator. +If you set a value of -20, each time the diverter measures the energy flowing, it'll add *-20* to the measurements. + +So, now let see what happen in a couple of cases: + +- measured value is **positive** (energy import = no surplus), after adding *-20*, it stays positive, the diverter doesn't do anything. By a value between -20 and 0, the diverter won't do anything either. +- measured value is **around zero**. In this situation, the "zero export profile" limitation is active. +After adding *-20*, we get a negative value that will make the diverter start diverting energy to the water heater. +Now, there's a sort of chain reaction. The Envoy detects more consumption, decides to raise production. +On the next measurement, the diverter measures again a value around zero, add again *-20*, and diverts even more energy. +When production (and surplus) gets to the maximum possible, the measured value will stay around zero+ and the system is stable. + +This has been tested in real by Amorim. Depending of each situation, it might be necessary to tweak this value of *-20* to a bigger or smaller value. + +# How to wire the router +[Here](../../docs/HowToInstall.pdf) you'll find a quick how-to for installing/wiring your router. + +# Use cases + +I want to: + +- change my (mechanical) single-phase water heater to 3-phase, see [Heater with mechanical thermostat](#heater-with-mechanical-thermostat) +- connect my (mechanical) 3-phase water heater, see [Heater with mechanical thermostat](#heater-with-mechanical-thermostat) +- change my ACI single-phase water heater to 3-phase w/o buying a 3-phase kit, see [Heater with ACI single phase thermostat](#heater-with-aci-single-phase-thermostat) +- connect my ACI 3-phase water heater, see [Heater with ACI 3-phase thermostat (without neutral wire)](#heater-with-aci-3-phase-thermostat-without-neutral-wire) +- connect multiple pure resistive charges, simply wire them, one on each output, and do not forget to disable [Load priorities management](#load-priorities-management). + +## Requirements + +To change your single-phase water heater to 3-phase, it MUST support 3-phase wiring (i.e. it must have 3 heating elements). + +--- +**_Safety Warning_** + +To modify the existing wiring, access to 240V mains voltage is required. +Please take great care, and do not undertake this stage unless you feel confident to do so. + +--- + +## Heater with mechanical thermostat + +#### Migrate from single-phase to 3-phase (with neutral wire) + +--- +**_A router with 3 outputs is needed_** + +With this solution, you'll control each heating element separately. + +--- + +You'll have to separate all 3 heating elements, and probably add a new wire for each of them. Sometime, the elements are connected together with a sort of metallic "star". There's one for the (single) phase, and one for the neutral wire. You only need to remove one of them, the one for neutral must stay wired. + +#### Wiring + +Since on all (3-phase) water heaters I've seen, the thermostat switches only 2 phases in normal mode (all 3 phases in security mode), it must be wired in another way to achieve a full switch on all 3 phases. In a fully balanced 3-phase situation, you don't need any neutral wire. To switch off the device, you only need to switch off 2 phases. + +--- +**_Note_** + +In a balanced situation, you don't need any neutral wire. To switch off the device, you just need to switch off 2 phases out of 3. That's why most thermostats are build like this. + +--- + +For that, I've "recycled" a peak/off peak 3-phase relay but you can use any 3-phase relay. It doesn't matter on which phase the command coil is connected, but it must be permanent (not through the router). + +![Heater with mechanical thermostat](img/Heater_mechanical.png) +*Figure: Wiring diagram* + +## Heater with ACI single phase thermostat + +In this case, it's somehow the same situation as before. +You don't need to buy a 3-phase kit to convert your single phase heater. +The ACI pcb must be connected to a permanent phase. It will then control any 3-phase relay. + +![Heater with ACI single phase thermostat](img/Heater_ACI_Mono.png) +*Figure: Wiring diagram* + +## Heater with ACI 3-phase thermostat (without neutral wire) + +--- +**_A router with 2 outputs is needed_** + +With this solution, you'll control each heating element separately. + +--- + +The ACI board does not cut all 3 phases when the temperature is reached. Only 2 phases are disconnected. + +The remaining connected phase is the one in the middle of the power connector. +***It is very IMPORTANT that this phase, which remains permanent, does not pass through a triac***. + +The ACI pcb must be connected to 3 permanent phases. + +![Heater with ACI 3-phase thermostat](img/Heater_ACI_Tri.png) +*Figure: Wiring diagram* + +## Alternatives WITHOUT neutral wire + +--- +**_A router with 2 outputs is needed_** + +With this solution, you won't need to add an additional neutral wire nor add a relay. + +--- + +### Heater with mechanical thermostat + +This configuration allows to simplify the wiring and specially does not require any 3-4 poles relay. + +--- +**_Zoom on the thermostat_** + +You need to take care of which wires are switched off. + +In **red**, security switch (see the 'S' on each pole) : all 3 phases are switched off. + +In **green**, only 2 phases are switched off, L2 et L3. ***It is IMPORTANT that the phase L1, not switched by the thermostat, DOES NOT pass through the triac***. + +![Mechanical thermostat](img/Thermostat.png) +*Figure: An example of a thermostat* + +--- + +![Heater with mechanical thermostat](img/Heater_mechanical-No_neutral.png) +*Figure: Wiring diagram* + +## Support + +This project is maintained by [@FredM67](https://github.com/FredM67). Please understand that we won't be able to provide individual support via email. We also believe that help is much more valuable if it's shared publicly, so that more people can benefit from it. + +| Type | Platforms | +| -------------------------------------- | ----------------------------------------------------------------------------- | +| ?? **Bug Reports** | [GitHub Issue Tracker](https://github.com/FredM67/PVRouter-3-phase/issues) | +| ?? **Docs Issue** | [GitHub Issue Tracker](https://github.com/FredM67/PVRouter-3-phase/issues) | +| ?? **Feature Requests** | [GitHub Issue Tracker](https://github.com/FredM67/PVRouter-3-phase/issues) | +| ?? **Report a security vulnerability** | See [SECURITY.md](SECURITY.md) | +| ?? **General Questions** | [GitHub Discussions](https://github.com/FredM67/PVRouter-3-phase/discussions) | + +## Roadmap + +No changes are currently planned. + +## Contributing + +Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us. + +## Authors and acknowledgment + +- **Fr�d�ric Metrich** - _Initial work_ - [FredM67](https://github.com/FredM67) + +See also the list of [contributors](https://github.com/FredM67/PVRouter-3-phase/graphs/contributors) who participated in this project. diff --git a/Readme.fr.md b/Readme.fr.md deleted file mode 100644 index 40639fbd..00000000 --- a/Readme.fr.md +++ /dev/null @@ -1,280 +0,0 @@ -
- -[![GitHub issues](https://img.shields.io/github/issues/FredM67/PVRouter-3-phase)](https://github.com/FredM67/PVRouter-3-phase/issues) -[![GitHub forks](https://img.shields.io/github/forks/FredM67/PVRouter-3-phase)](https://github.com/FredM67/PVRouter-3-phase/network) -[![GitHub stars](https://img.shields.io/github/stars/FredM67/PVRouter-3-phase)](https://github.com/FredM67/PVRouter-3-phase/stargazers) -[![CodeQL](https://github.com/FredM67/PVRouter-3-phase/actions/workflows/codeql.yml/badge.svg)](https://github.com/FredM67/PVRouter-3-phase/actions/workflows/codeql.yml) -[![Doxygen](https://github.com/FredM67/PVRouter-3-phase/actions/workflows/doxygen-gh-pages.yml/badge.svg)](https://github.com/FredM67/PVRouter-3-phase/actions/workflows/doxygen-gh-pages.yml) -
-[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua) -
-
- [![en](https://img.shields.io/badge/lang-en-red.svg)](https://github.com/FredM67/PVRouter-3-phase/blob/main/Readme.md) - [![fr](https://img.shields.io/badge/lang-fr-blue.svg)](https://github.com/FredM67/PVRouter-3-phase/blob/main/Readme.fr.md) -
- -# PVRouter (version triphasée) - -Ma version du firmware Mk2PVRouter en 3 phases (voir http://www.mk2pvrouter.co.uk). - -Robin Emley propose déjà un routeur PV triphasé (https://www.mk2pvrouter.co.uk/3-phase-version.html). -Il prend en charge jusqu'à 12 sorties pour charges résistives, qui sont complètement indépendantes. - ---- -**_NOTE:_** Pour une version en monophasé, voir [PVRouter-Single](https://github.com/FredM67/PVRouter-Single). - ---- - -- [PVRouter (version triphasée)](#pvrouter-version-triphasée) - - [Aperçu des dossiers](#aperçu-des-dossiers) - - [Gallerie photo](#gallerie-photo) - - [Schéma de la carte-mère](#schéma-de-la-carte-mère) - - [Documentation de développement](#documentation-de-développement) - - [Documentation de l’utilisateur final](#documentation-de-lutilisateur-final) - - [Aperçu](#aperçu) - - [Gestion des priorités de charge](#gestion-des-priorités-de-charge) - - [Détection HC](#détection-hc) - - [Marche forcée pleine puissance](#marche-forcée-pleine-puissance) - - [Sortie(s) relais tout-ou-rien \*\* NEW \*\*](#sorties-relais-tout-ou-rien--new-) - - [Capteur de température](#capteur-de-température) - - [Profil Enphase zéro export](#profil-enphase-zéro-export) - - [Comment câbler le routeur](#comment-câbler-le-routeur) - - [Applications / Diagrammes de câblage](#applications--diagrammes-de-câblage) - - [Pré-requis](#pré-requis) - - [Chauffe-eau avec thermostat mécanique](#chauffe-eau-avec-thermostat-mécanique) - - [Passage du monophasé au triphasé (avec neutre)](#passage-du-monophasé-au-triphasé-avec-neutre) - - [Câblage](#câblage) - - [Chauffe-eau avec thermostat ACI monophasé](#chauffe-eau-avec-thermostat-aci-monophasé) - - [Chauffe-eau avec thermostat ACI triphasé (SANS neutre)](#chauffe-eau-avec-thermostat-aci-triphasé-sans-neutre) - - [Alternatives SANS neutre](#alternatives-sans-neutre) - - [Chauffe-eau avec thermostat mécanique](#chauffe-eau-avec-thermostat-mécanique-1) - - [Support](#support) - - [Roadmap](#roadmap) - - [Contributing](#contributing) - - [Authors and acknowledgment](#authors-and-acknowledgment) - -## Aperçu des dossiers -- [**Mk2_3phase_RFdatalog_temp**](Mk2_3phase_RFdatalog_temp) : contient tous les fichiers nécessaires au programme du routeur. -- [**dev**](dev) : contient divers programmes pour le développement du routeur. - - [**cal_CTx_v_meter**](dev/cal_CTx_v_meter) : contient tous les fichiers nécessaires au programme d'étalonnage du routeur. - - **RawSamplesTool_6chan** : permet de tester les 6 canaux de mesure. -- autres dossiers : contiennent des fichiers divers et variés relatifs au site. - -## Gallerie photo - -Vous trouverez quelques [photos](Gallery.md) de routeurs assemblés. - -## Schéma de la carte-mère - -Vous trouverez [ici](schematics/3phase_Mainboard.pdf) le schéma de la carte-mère. - -## Documentation de développement - -Vous pouvez commencer à lire la documentation ici [3-phase diverter](https://fredm67.github.io/PVRouter-3-phase/) (en anglais). - -## Documentation de l’utilisateur final - -### Aperçu - -L’objectif était de modifier/optimiser le programme pour le cas « spécial » d’un chauffe-eau triphasé. Un chauffe-eau triphasé est composé en fait de 3 éléments de chauffage indépendants. La plupart du temps, un tel chauffe-eau peut être connecté en monophasé, en triphasé étoile (WYE) ou triphasé triangle (Delta). Lorsqu’il est connecté en étoile (sans varistor), il n’y a pas besoin de fil de neutre parce que le système est équilibré, donc à tout moment, il n’y a pas de courant qui circule vers le neutre. - -Fonctionnalités ajoutées : - -- gestion des priorités de charge (configurable) -- détection HC/HP (configurable) -- forçage à pleine puissance -- capteur de température (juste la lecture pour le moment) -- enregistrement de données optimisé (RF) -- sortie série en JSON ou TXT - -Le programme original a dû être entièrement retravaillé et re-structuré pour permettre la lecture de la température. Dans le programme d’origine, l’ISR ne fait que lire et convertir les données analogiques, et le traitement se fait dans la boucle *loop*. Cela ne fonctionnera pas avec un capteur de température en raison de ses performances lentes. Il déstabiliserait l’ensemble du système, des données de courant / tension seraient perdues, ... - -Maintenant, tout le traitement critique en termes de temps se fait à l’intérieur de l’ISR, les autres tâches comme la journalisation des données (RF), la sortie série, la lecture de la température sont faites à l’intérieur de la boucle *loop()*. L’ISR et le processeur principal communiquent entre eux par le biais d'« événements ». - -### Gestion des priorités de charge - -Dans ma variante du programme de Robin, les 3 charges sont toujours physiquement indépendantes, c'est-à-dire que le routeur va détourner l’excédent d’énergie à la première charge (priorité la plus élevée) de 0% à 100%, puis à la seconde (0% à 100%) et enfin à la troisième. - -Pour éviter que les priorités restent tout le temps inchangées, ce qui signifie que la charge 1 fonctionnera beaucoup plus que la charge 2, qui elle-même fonctionnera plus que la charge 3, j’ai ajouté une gestion des priorités. Chaque jour, les priorités des charges sont permutées, donc sur plusieurs jours, tous les éléments de chauffage fonctionneront en moyenne de façon équitable. - -### Détection HC - -Selon le pays, certains compteurs d’énergie disposent d'interrupteur/relais qui bascule au début de la période creuse. Il est destiné à contrôler un commutateur HC/HP. Si vous le reliez à une broche numérique libre du routeur (dans mon cas D3), vous pouvez détecter le début et fin des HC. - -### Marche forcée pleine puissance - -Le support a été ajouté pour forcer la pleine puissance sur des charges spécifiques. Chaque charge peut être forcée indépendamment les unes des autres, l’heure de début et la durée peuvent être définies individuellement. - -Dans ma variante, c’est utilisé pour changer le chauffage pendant la période creuse, dans le cas où le surplus a été trop faible au cours de la journée. Ici, pour optimiser le comportement, un capteur de température sera utilisé pour vérifier la température de l’eau et décider d’allumer ou non pendant la nuit. - -### Sortie(s) relais tout-ou-rien ** NEW ** - -Une ou plusieurs sorties tout-ou-rien via un relais peuvent être maintenant pilotées par le routeur. -Leur priorité sera toujours en dernier, c'est-à-dire que les sorties TRIAC hachées auront toujours une priorité plus élevée. - -L'utilisateur devra définir pour cela, et ce pour chaque sortie relais : -- le seuil de surplus pour le déclenchement du relais (par défaut 1000W) -- le seuil d'import pour l'arrêt du relais (par défaut 200W) -- le temps minimal de fonctionnement du relais en minutes (par défaut 5 mn) -- le temps minimal d'arrêt du relais en minutes (par défaut 5 mn) - -Les seuils de surplus et d'import sont calculés par une moyenne glissante sur une période de temps donnée. Par défaut, les moyennes sont calculées sur 1 minute. - -### Capteur de température - -Pour l’instant, uniquement lecture. Il sera utilisé pour optimiser la pleine puissance de la force, pour prendre la bonne décision pendant la nuit. - -### Profil Enphase zéro export - -Lorsque le profil zéro-export est activé, le système PV réduit la production d’énergie si la production du système dépasse les besoins de consommation du site. Cela garantit zéro injection dans le réseau. - -Comme effet secondaire, le diverteur ne verra jamais, à aucun moment, un surplus d’énergie. -L’idée est donc d’appliquer un certain décalage à l’énergie mesurée par le diverteur. -Comme il est déjà commenté dans le code, après l'assignation d’une valeur négative à *REQUIRED_EXPORT_IN_WATTS*, le diverter agira comme un générateur PV. -Si vous définissez une valeur de *-20*, chaque fois que le diverter mesure le flux d’énergie, il ajoutera *-20* aux mesures. - -Alors, maintenant voyons ce qui se passe dans différents cas: - -- la valeur mesurée est **positive** (importation d’énergie = pas d’excédent), après avoir ajouté *-20*, cela reste positif, le diverter ne fait rien. Pour une valeur comprise entre -20 et 0, le déviateur ne fera rien non plus. -- la valeur mesurée est **autour de zéro**. Dans cette situation, la limitation du "profil zéro exportation" est active. -Après l’ajout de *-20*, nous obtenons une valeur négative, ce qui déclenchera le détournement d’énergie vers le chauffe-eau. -Ensuite, il y a une sorte de réaction en chaîne. L’Envoy détecte plus de consommation, décide d’augmenter la production. -À la mesure suivante, le diverter mesure à nouveau une valeur autour de zéro, ajoute à nouveau -20, et détourne encore plus d’énergie. -Lorsque la production (et l’excédent) arrive au maximum possible, la valeur mesurée restera autour de zéro+ et le système deviendra stable. - -Cela a été testé en situation réelle par Amorim. Selon chaque situation, il peut être nécessaire de modifier cette valeur de *-20* à une valeur plus grande ou plus petite. - -## Comment câbler le routeur -[Ici](docs/HowToInstall.pdf) vous trouverez une rapide notice d'installation du routeur. - -## Applications / Diagrammes de câblage - -Je veux: - -- changer mon chauffe-eau (avec thermostat mécanique) monophasé en triphasé, voir [Chauffe-eau avec thermostat mécanique](#chauffe-eau-avec-thermostat-mécanique) -- connecter mon chauffe-eau (avec thermostat mécanique) en triphasé, voir [Chauffe-eau avec thermostat mécanique](#chauffe-eau-avec-thermostat-mécanique) -- changer mon chauffe-eau aci monophasé en triphasé sans acheter de kit triphasé, voir [Chauffe-eau avec thermostat ACI monophasé](#chauffe-eau-avec-thermostat-aci-monophasé) -- connecter mon chauffe-eau ACI triphasé, voir [Chauffe-eau avec thermostat ACI triphasé (SANS neutre)](#chauffe-eau-avec-thermostat-aci-triphasé-sans-neutre) -- connecter plusieurs charges résistives pures, il suffit de les câbler, une sur chaque sortie. N’oubliez pas de désactiver la gestion des priorités de charge. - -### Pré-requis - -Votre chauffe-eau DOIT supporter le câblage en triphasé (c'est-à-dire il doit y avoir 3 éléments chauffants). - ---- -**_Avertissement de sécurité_** - -Pour modifier le câblage existant, l’accès à la tension du réseau 240V est nécessaire. -Soyez sûr de savoir ce que vous entreprenez. Au besoin, faîtes appel à un électricien qualifié. - ---- - -### Chauffe-eau avec thermostat mécanique - -#### Passage du monophasé au triphasé (avec neutre) - ---- -**_Nécessite un routeur avec 3 sorties_** - -Avec cette solution, vous commandez chaque résistance séparément l'une de l'autre. - ---- - -Vous devrez séparer les 3 éléments de chauffage, et probablement ajouter un nouveau fil pour chacun d’eux. Parfois, les éléments sont reliés ensemble avec une sorte "d'étoile" métallique. Il y en a une pour la phase, et une pour le fil neutre. Vous n’avez qu’à supprimer celle de la phase, celle pour neutre doit rester câblée. - -#### Câblage - -Sur tous les chauffe-eau (triphasé) que j’ai vu, le thermostat ne coupe que 2 phases en mode normal (les 3 phases en mode de sécurité), il doit donc être câblé d’une autre manière pour obtenir une commutation complète sur les 3 phases. - ---- -**_Rappel_** - -Dans une situation entièrement équilibrée en triphasé, vous n’avez pas besoin de fil neutre. Pour éteindre l’appareil, il suffit de couper 2 phases, ce qui explique la construction de ces thermostats - ---- - -Pour cela, j’ai « recyclé » un commutateur HC/HP triphasé, mais vous pouvez utiliser n’importe quel relais triphasé. La bobine de commande doit être connectée à une alimentation "permanente" (et non à travers le routeur) contrôlée par le thermostat. - -![Chauffe-eau avec thermostat mécanique](img/Heater_mechanical.png) -*Figure: Diagramme de câblage* - -### Chauffe-eau avec thermostat ACI monophasé - -Dans ce cas, c’est en quelque sorte la même situation qu’avant. Vous n’avez pas besoin d’acheter un kit ACI en triphasé pour convertir votre chauffe-eau monophasé. La carte ACI doit être connectée à une phase permanente. Elle contrôlera ensuite n’importe quel relais en triphasé. - -![Chauffe-eau avec thermostat ACI monophasé](img/Heater_ACI_Mono.png) -*Figure : Diagramme de câblage* - -### Chauffe-eau avec thermostat ACI triphasé (SANS neutre) - ---- -**_Nécessite un routeur avec 2 sorties_** - -Avec cette solution, vous commandez chaque résistance séparément l'une de l'autre. - ---- - -La carte ACI ne coupe pas les 3 phases lorsque la température est atteinte. Seules 2 phases sont coupées. - -La phase non coupée est celle qui correspond au fil du milieu sur le connecteur. ***Il est très IMPORTANT que cette phase, non coupée par le thermostat, ne passe pas par un triac***. - -La carte ACI doit être reliée à 3 phases permanentes. - -![Chauffe-eau avec thermostat ACI triphasé](img/Heater_ACI_Tri.png) -*Figure : Diagramme de câblage* - -### Alternatives SANS neutre - ---- -**_Nécessite un routeur avec 2 sorties_** - -Cette solution vous permet d'économiser le rajout d'un fil de neutre et/ou l'ajout un contacteur. - ---- - -#### Chauffe-eau avec thermostat mécanique - -Cette configuration permet de simplifier les branchements et surtout, il n'est plus nécessaire de rajouter un contacteur tri-/quadripolaire. - ---- -**_Zoom sur le thermostat_** - -Il faut bien faire attention, en regardant sur le thermostat, quelles bornes sont coupées. - -En **rouge**, coupure de sécurité (remarquez le 'S' sur chaque contact) : les 3 phases sont coupées. - -En **vert**, seules 2 phases sont coupées, L2 et L3. ***Il est très IMPORTANT que la phase L1, non coupée par le thermostat, ne passe pas par un triac***. - -![Thermostat mécanique](img/Thermostat.png) -*Figure: Exemple de thermostat* - ---- - -![Chauffe-eau avec thermostat mécanique](img/Heater_mechanical-No_neutral.png) -*Figure: Diagramme de câblage* - -## Support - -This project is maintained by [@FredM67](https://github.com/FredM67). Please understand that we won't be able to provide individual support via email. We also believe that help is much more valuable if it's shared publicly, so that more people can benefit from it. - -| Type | Platforms | -| ------------------------------------- | ----------------------------------------------------------------------------- | -| 🚨 **Bug Reports** | [GitHub Issue Tracker](https://github.com/FredM67/PVRouter-3-phase/issues) | -| 📚 **Docs Issue** | [GitHub Issue Tracker](https://github.com/FredM67/PVRouter-3-phase/issues) | -| 🎁 **Feature Requests** | [GitHub Issue Tracker](https://github.com/FredM67/PVRouter-3-phase/issues) | -| 🛡 **Report a security vulnerability** | See [SECURITY.md](SECURITY.md) | -| 💬 **General Questions** | [GitHub Discussions](https://github.com/FredM67/PVRouter-3-phase/discussions) | - -## Roadmap - -No changes are currently planned. - -## Contributing - -Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us. - -## Authors and acknowledgment - -- **Frédéric Metrich** - _Initial work_ - [FredM67](https://github.com/FredM67) - -See also the list of [contributors](https://github.com/FredM67/PVRouter-3-phase/graphs/contributors) who participated in this project. diff --git a/Readme.md b/Readme.md index ea390108..318b137d 100644 --- a/Readme.md +++ b/Readme.md @@ -9,246 +9,261 @@ [![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua)

- [![en](https://img.shields.io/badge/lang-en-red.svg)](https://github.com/FredM67/PVRouter-3-phase/blob/main/Readme.md) - [![fr](https://img.shields.io/badge/lang-fr-blue.svg)](https://github.com/FredM67/PVRouter-3-phase/blob/main/Readme.fr.md) + [![en](https://img.shields.io/badge/lang-en-red.svg)](Readme.en.md) + [![fr](https://img.shields.io/badge/lang-fr-blue.svg)](Readme.md) -# PVRouter (3-phase version) +# PVRouter (version triphasée) -My version of the 3-phase Mk2PVRouter firmware (see ). +Ma version du firmware Mk2PVRouter en 3 phases (voir http://www.mk2pvrouter.co.uk). -Robin Emley already proposes a 3 phase PV-router (). -It supports up to 12 resistive output loads, which are completely independent. +Robin Emley propose déjà un routeur PV triphasé (https://www.mk2pvrouter.co.uk/3-phase-version.html). +Il prend en charge jusqu'à 12 sorties pour charges résistives, qui sont complètement indépendantes. --- -**_NOTE:_** For a single phase version, please see [PVRouter-Single](https://github.com/FredM67/PVRouter-Single). +**_NOTE:_** Pour une version en monophasé, voir [PVRouter-Single](https://github.com/FredM67/PVRouter-Single). --- -- [PVRouter (3-phase version)](#pvrouter-3-phase-version) - - [Photo Gallery](#photo-gallery) - - [Schematic of the mainboard](#schematic-of-the-mainboard) - - [Implementation documentation](#implementation-documentation) - - [End-user documentation](#end-user-documentation) - - [Overview](#overview) - - [Load priorities management](#load-priorities-management) - - [Off-peak period detection](#off-peak-period-detection) - - [Force full power](#force-full-power) - - [Temperature sensor](#temperature-sensor) - - [Enphase zero-export profile](#enphase-zero-export-profile) -- [How to wire the router](#how-to-wire-the-router) -- [Use cases](#use-cases) - - [Requirements](#requirements) - - [Heater with mechanical thermostat](#heater-with-mechanical-thermostat) - - [Migrate from single-phase to 3-phase (with neutral wire)](#migrate-from-single-phase-to-3-phase-with-neutral-wire) - - [Wiring](#wiring) - - [Heater with ACI single phase thermostat](#heater-with-aci-single-phase-thermostat) - - [Heater with ACI 3-phase thermostat (without neutral wire)](#heater-with-aci-3-phase-thermostat-without-neutral-wire) - - [Alternatives WITHOUT neutral wire](#alternatives-without-neutral-wire) - - [Heater with mechanical thermostat](#heater-with-mechanical-thermostat-1) +- [PVRouter (version triphasée)](#pvrouter-version-triphasée) + - [Aperçu des dossiers](#aperçu-des-dossiers) + - [Gallerie photo](#gallerie-photo) + - [Schéma de la carte-mère](#schéma-de-la-carte-mère) + - [Documentation de développement](#documentation-de-développement) + - [Documentation de l’utilisateur final](#documentation-de-lutilisateur-final) + - [Aperçu](#aperçu) + - [Gestion des priorités de charge](#gestion-des-priorités-de-charge) + - [Détection HC](#détection-hc) + - [Marche forcée pleine puissance](#marche-forcée-pleine-puissance) + - [Sortie(s) relais tout-ou-rien \*\* NEW \*\*](#sorties-relais-tout-ou-rien--new-) + - [Capteur de température](#capteur-de-température) + - [Profil Enphase zéro export](#profil-enphase-zéro-export) + - [Comment câbler le routeur](#comment-câbler-le-routeur) + - [Applications / Diagrammes de câblage](#applications--diagrammes-de-câblage) + - [Pré-requis](#pré-requis) + - [Chauffe-eau avec thermostat mécanique](#chauffe-eau-avec-thermostat-mécanique) + - [Passage du monophasé au triphasé (avec neutre)](#passage-du-monophasé-au-triphasé-avec-neutre) + - [Câblage](#câblage) + - [Chauffe-eau avec thermostat ACI monophasé](#chauffe-eau-avec-thermostat-aci-monophasé) + - [Chauffe-eau avec thermostat ACI triphasé (SANS neutre)](#chauffe-eau-avec-thermostat-aci-triphasé-sans-neutre) + - [Alternatives SANS neutre](#alternatives-sans-neutre) + - [Chauffe-eau avec thermostat mécanique](#chauffe-eau-avec-thermostat-mécanique-1) - [Support](#support) - [Roadmap](#roadmap) - [Contributing](#contributing) - [Authors and acknowledgment](#authors-and-acknowledgment) -## Photo Gallery +## Aperçu des dossiers +- [**Mk2_3phase_RFdatalog_temp**](Mk2_3phase_RFdatalog_temp) : contient tous les fichiers nécessaires au programme du routeur. +- [**dev**](dev) : contient divers programmes pour le développement du routeur. + - [**cal_CTx_v_meter**](dev/cal_CTx_v_meter) : contient tous les fichiers nécessaires au programme d'étalonnage du routeur. + - **RawSamplesTool_6chan** : permet de tester les 6 canaux de mesure. +- autres dossiers : contiennent des fichiers divers et variés relatifs au site. -[Here](Gallery.md) a couple of pictures of assembled routers. +## Gallerie photo -## Schematic of the mainboard +Vous trouverez quelques [photos](Gallery.md) de routeurs assemblés. -[Here](../../schematics/3phase_Mainboard.pdf) the schematic of the mainboard. +## Schéma de la carte-mère -## Implementation documentation +Vous trouverez [ici](schematics/3phase_Mainboard.pdf) le schéma de la carte-mère. -You can start reading the documentation here [3-phase diverter](https://fredm67.github.io/PVRouter-3-phase/html/index.html). +## Documentation de développement -## End-user documentation +Vous pouvez commencer à lire la documentation ici [3-phase diverter](https://fredm67.github.io/PVRouter-3-phase/) (en anglais). -### Overview +## Documentation de l’utilisateur final -Goal was to modify/optimize the sketch for the "special" case of a 3-phase water heater. A 3-phase water heater is composed in fact of 3 independent heating elements. Most of the time, such a heater can be connected in mono, or 3-phase WYE or 3-phase Delta. -When connected in WYE (without varistor), there's no need of a neutral wire because the system is equally distributed, so at any time, there's no current flowing to the neutral. +### Aperçu -If a diverter is used, the neutral wire must be connected. +L’objectif était de modifier/optimiser le programme pour le cas « spécial » d’un chauffe-eau triphasé. Un chauffe-eau triphasé est composé en fait de 3 éléments de chauffage indépendants. La plupart du temps, un tel chauffe-eau peut être connecté en monophasé, en triphasé étoile (WYE) ou triphasé triangle (Delta). Lorsqu’il est connecté en étoile (sans varistor), il n’y a pas besoin de fil de neutre parce que le système est équilibré, donc à tout moment, il n’y a pas de courant qui circule vers le neutre. -Added functionalities: +Fonctionnalités ajoutées : -- load priorities management (configurable) -- off-peak period detection (configurable) -- force full power -- temperature sensor (just reading for the moment) -- optimized (RF) data logging -- serial output in JSON or TXT +- gestion des priorités de charge (configurable) +- détection HC/HP (configurable) +- forçage à pleine puissance +- capteur de température (juste la lecture pour le moment) +- enregistrement de données optimisé (RF) +- sortie série en JSON ou TXT + +Le programme original a dû être entièrement retravaillé et re-structuré pour permettre la lecture de la température. Dans le programme d’origine, l’ISR ne fait que lire et convertir les données analogiques, et le traitement se fait dans la boucle *loop*. Cela ne fonctionnera pas avec un capteur de température en raison de ses performances lentes. Il déstabiliserait l’ensemble du système, des données de courant / tension seraient perdues, ... -The original sketch had to be completely re-worked and re-structured to support temperature reading. In the original sketch, the ISR "just" reads and converts the analog data, and the processing is done in the loop. This won't work with a temperature sensor because of its slow performance. It would break the whole system, current/voltage data will be lost, ... +Maintenant, tout le traitement critique en termes de temps se fait à l’intérieur de l’ISR, les autres tâches comme la journalisation des données (RF), la sortie série, la lecture de la température sont faites à l’intérieur de la boucle *loop()*. L’ISR et le processeur principal communiquent entre eux par le biais d'« événements ». -Now, all the time-critical processing is done inside the ISR, other stuff like (RF) data logging, Serial printing, temperature reading is made inside the loop(). The ISR and main processor communicate with each other through "events". +### Gestion des priorités de charge -### Load priorities management +Dans ma variante du programme de Robin, les 3 charges sont toujours physiquement indépendantes, c'est-à-dire que le routeur va détourner l’excédent d’énergie à la première charge (priorité la plus élevée) de 0% à 100%, puis à la seconde (0% à 100%) et enfin à la troisième. -In my variant of Robin's sketch, the 3 loads are still physically independent, so it means, the router will divert surplus of energy to the first load (highest priority) from 0% to 100%, then to the second (0% to 100%) and finally to the third. +Pour éviter que les priorités restent tout le temps inchangées, ce qui signifie que la charge 1 fonctionnera beaucoup plus que la charge 2, qui elle-même fonctionnera plus que la charge 3, j’ai ajouté une gestion des priorités. Chaque jour, les priorités des charges sont permutées, donc sur plusieurs jours, tous les éléments de chauffage fonctionneront en moyenne de façon équitable. -To avoid that the priorities stay all the time unchanged, which would mean that load 1 will run much more than load 2, which again will run much more than 3, I've added a priority management. -Each day, the load priorities are rotated, so over many days, all the heating elements will run somehow the same amount of time. +### Détection HC -### Off-peak period detection +Selon le pays, certains compteurs d’énergie disposent d'interrupteur/relais qui bascule au début de la période creuse. Il est destiné à contrôler un commutateur HC/HP. Si vous le reliez à une broche numérique libre du routeur (dans mon cas D3), vous pouvez détecter le début et fin des HC. -Depending on the country, some energy meters provide a switch/relay which toggles on at the beginning of the off-peak period. It is intended to control a relay. If you wire it to a free digital pin of the router (in my case D3), you can detect off-peak/peak period. +### Marche forcée pleine puissance -### Force full power +Le support a été ajouté pour forcer la pleine puissance sur des charges spécifiques. Chaque charge peut être forcée indépendamment les unes des autres, l’heure de début et la durée peuvent être définies individuellement. -Support has been added to force full power on specific loads. Each load can be forced independently from each other, start time and duration can be set individually. +Dans ma variante, c’est utilisé pour changer le chauffage pendant la période creuse, dans le cas où le surplus a été trop faible au cours de la journée. Ici, pour optimiser le comportement, un capteur de température sera utilisé pour vérifier la température de l’eau et décider d’allumer ou non pendant la nuit. -In my variant, that's used to switch the heater one during off-peak period if not enough surplus has been routed during the day. Here, to optimize the behavior, a temp-sensor will be used to check the temperature of the water and decide to switch on or not during night. +### Sortie(s) relais tout-ou-rien ** NEW ** -### Temperature sensor +Une ou plusieurs sorties tout-ou-rien via un relais peuvent être maintenant pilotées par le routeur. +Leur priorité sera toujours en dernier, c'est-à-dire que les sorties TRIAC hachées auront toujours une priorité plus élevée. -For the moment, just reading. It'll be used to optimize force full power, to make the right decision during night. +L'utilisateur devra définir pour cela, et ce pour chaque sortie relais : +- le seuil de surplus pour le déclenchement du relais (par défaut 1000W) +- le seuil d'import pour l'arrêt du relais (par défaut 200W) +- le temps minimal de fonctionnement du relais en minutes (par défaut 5 mn) +- le temps minimal d'arrêt du relais en minutes (par défaut 5 mn) -### Enphase zero-export profile +Les seuils de surplus et d'import sont calculés par une moyenne glissante sur une période de temps donnée. Par défaut, les moyennes sont calculées sur 1 minute. -When zero-export settings is enabled, the PV system curtails power production if the production of the system exceeds the consumption needs of the site. This ensures zero feed into the grid. +### Capteur de température -As a side effect, the diverter won't see at any time surplus of energy. -So the idea is to apply a certain offset to the energy measured by the diverter. -As it is already commented in the code, after setting a negative value to *REQUIRED_EXPORT_IN_WATTS*, the diverter will act as a PV generator. -If you set a value of -20, each time the diverter measures the energy flowing, it'll add *-20* to the measurements. +Pour l’instant, uniquement lecture. Il sera utilisé pour optimiser la pleine puissance de la force, pour prendre la bonne décision pendant la nuit. -So, now let see what happen in a couple of cases: +### Profil Enphase zéro export -- measured value is **positive** (energy import = no surplus), after adding *-20*, it stays positive, the diverter doesn't do anything. By a value between -20 and 0, the diverter won't do anything either. -- measured value is **around zero**. In this situation, the "zero export profile" limitation is active. -After adding *-20*, we get a negative value that will make the diverter start diverting energy to the water heater. -Now, there's a sort of chain reaction. The Envoy detects more consumption, decides to raise production. -On the next measurement, the diverter measures again a value around zero, add again *-20*, and diverts even more energy. -When production (and surplus) gets to the maximum possible, the measured value will stay around zero+ and the system is stable. +Lorsque le profil zéro-export est activé, le système PV réduit la production d’énergie si la production du système dépasse les besoins de consommation du site. Cela garantit zéro injection dans le réseau. -This has been tested in real by Amorim. Depending of each situation, it might be necessary to tweak this value of *-20* to a bigger or smaller value. +Comme effet secondaire, le diverteur ne verra pas à aucun moment un surplus d’énergie. +L’idée est donc d’appliquer un certain décalage à l’énergie mesurée par le diverteur. +Comme il est déjà commenté dans le code, après l'assignation d’une valeur négative à *REQUIRED_EXPORT_IN_WATTS*, le diverter agira comme un générateur PV. +Si vous définissez une valeur de *-20*, chaque fois que le diverter mesure le flux d’énergie, il ajoutera *-20* aux mesures. -# How to wire the router -[Here](../../docs/HowToInstall.pdf) you'll find a quick how-to for installing/wiring your router. +Alors, maintenant voyons ce qui se passe dans différents cas: -# Use cases +- la valeur mesurée est **positive** (importation d’énergie = pas d’excédent), après avoir ajouté *-20*, cela reste positif, le diverter ne fait rien. Pour une valeur comprise entre -20 et 0, le déviateur ne fera rien non plus. +- la valeur mesurée est **autour de zéro**. Dans cette situation, la limitation du "profil zéro exportation" est active. +Après l’ajout de *-20*, nous obtenons une valeur négative, ce qui déclenchera le détournement d’énergie vers le chauffe-eau. +Ensuite, il y a une sorte de réaction en chaîne. L’Envoy détecte plus de consommation, décide d’augmenter la production. +À la mesure suivante, le diverter mesure à nouveau une valeur autour de zéro, ajoute à nouveau -20, et détourne encore plus d’énergie. +Lorsque la production (et l’excédent) arrive au maximum possible, la valeur mesurée restera autour de zéro+ et le système deviendra stable. -I want to: +Cela a été testé en situation réelle par Amorim. Selon chaque situation, il peut être nécessaire de modifier cette valeur de *-20* à une valeur plus grande ou plus petite. -- change my (mechanical) single-phase water heater to 3-phase, see [Heater with mechanical thermostat](#heater-with-mechanical-thermostat) -- connect my (mechanical) 3-phase water heater, see [Heater with mechanical thermostat](#heater-with-mechanical-thermostat) -- change my ACI single-phase water heater to 3-phase w/o buying a 3-phase kit, see [Heater with ACI single phase thermostat](#heater-with-aci-single-phase-thermostat) -- connect my ACI 3-phase water heater, see [Heater with ACI 3-phase thermostat (without neutral wire)](#heater-with-aci-3-phase-thermostat-without-neutral-wire) -- connect multiple pure resistive charges, simply wire them, one on each output, and do not forget to disable [Load priorities management](#load-priorities-management). +## Comment câbler le routeur +[Ici](docs/HowToInstall.pdf) vous trouverez une rapide notice d'installation du routeur. -## Requirements +## Applications / Diagrammes de câblage -To change your single-phase water heater to 3-phase, it MUST support 3-phase wiring (i.e. it must have 3 heating elements). +Je veux: + +- changer mon chauffe-eau (avec thermostat mécanique) monophasé en triphasé, voir [Chauffe-eau avec thermostat mécanique](#chauffe-eau-avec-thermostat-mécanique) +- connecter mon chauffe-eau (avec thermostat mécanique) en triphasé, voir [Chauffe-eau avec thermostat mécanique](#chauffe-eau-avec-thermostat-mécanique) +- changer mon chauffe-eau aci monophasé en triphasé sans acheter de kit triphasé, voir [Chauffe-eau avec thermostat ACI monophasé](#chauffe-eau-avec-thermostat-aci-monophasé) +- connecter mon chauffe-eau ACI triphasé, voir [Chauffe-eau avec thermostat ACI triphasé (SANS neutre)](#chauffe-eau-avec-thermostat-aci-triphasé-sans-neutre) +- connecter plusieurs charges résistives pures, il suffit de les câbler, une sur chaque sortie. N’oubliez pas de désactiver la gestion des priorités de charge. + +### Pré-requis + +Votre chauffe-eau DOIT supporter le câblage en triphasé (c'est-à-dire il doit y avoir 3 éléments chauffants). --- -**_Safety Warning_** +**_Avertissement de sécurité_** -To modify the existing wiring, access to 240V mains voltage is required. -Please take great care, and do not undertake this stage unless you feel confident to do so. +Pour modifier le câblage existant, l’accès à la tension du réseau 240V est nécessaire. +Soyez sûr de savoir ce que vous entreprenez. Au besoin, faîtes appel à un électricien qualifié. --- -## Heater with mechanical thermostat +### Chauffe-eau avec thermostat mécanique -#### Migrate from single-phase to 3-phase (with neutral wire) +#### Passage du monophasé au triphasé (avec neutre) --- -**_A router with 3 outputs is needed_** +**_Nécessite un routeur avec 3 sorties_** -With this solution, you'll control each heating element separately. +Avec cette solution, vous commandez chaque résistance séparément l'une de l'autre. --- -You'll have to separate all 3 heating elements, and probably add a new wire for each of them. Sometime, the elements are connected together with a sort of metallic "star". There's one for the (single) phase, and one for the neutral wire. You only need to remove one of them, the one for neutral must stay wired. +Vous devrez séparer les 3 éléments de chauffage, et probablement ajouter un nouveau fil pour chacun d’eux. Parfois, les éléments sont reliés ensemble avec une sorte "d'étoile" métallique. Il y en a une pour la phase, et une pour le fil neutre. Vous n’avez qu’à supprimer celle de la phase, celle pour neutre doit rester câblée. -#### Wiring +#### Câblage -Since on all (3-phase) water heaters I've seen, the thermostat switches only 2 phases in normal mode (all 3 phases in security mode), it must be wired in another way to achieve a full switch on all 3 phases. In a fully balanced 3-phase situation, you don't need any neutral wire. To switch off the device, you only need to switch off 2 phases. +Sur tous les chauffe-eau (triphasé) que j’ai vu, le thermostat ne coupe que 2 phases en mode normal (les 3 phases en mode de sécurité), il doit donc être câblé d’une autre manière pour obtenir une commutation complète sur les 3 phases. --- -**_Note_** +**_Rappel_** -In a balanced situation, you don't need any neutral wire. To switch off the device, you just need to switch off 2 phases out of 3. That's why most thermostats are build like this. +Dans une situation entièrement équilibrée en triphasé, vous n’avez pas besoin de fil neutre. Pour éteindre l’appareil, il suffit de couper 2 phases, ce qui explique la construction de ces thermostats --- -For that, I've "recycled" a peak/off peak 3-phase relay but you can use any 3-phase relay. It doesn't matter on which phase the command coil is connected, but it must be permanent (not through the router). +Pour cela, j’ai « recyclé » un commutateur HC/HP triphasé, mais vous pouvez utiliser n’importe quel relais triphasé. La bobine de commande doit être connectée à une alimentation "permanente" (et non à travers le routeur) contrôlée par le thermostat. -![Heater with mechanical thermostat](img/Heater_mechanical.png) -*Figure: Wiring diagram* +![Chauffe-eau avec thermostat mécanique](img/Heater_mechanical.png) +*Figure: Diagramme de câblage* -## Heater with ACI single phase thermostat +### Chauffe-eau avec thermostat ACI monophasé -In this case, it's somehow the same situation as before. -You don't need to buy a 3-phase kit to convert your single phase heater. -The ACI pcb must be connected to a permanent phase. It will then control any 3-phase relay. +Dans ce cas, c’est en quelque sorte la même situation qu’avant. Vous n’avez pas besoin d’acheter un kit ACI en triphasé pour convertir votre chauffe-eau monophasé. La carte ACI doit être connectée à une phase permanente. Elle contrôlera ensuite n’importe quel relais en triphasé. -![Heater with ACI single phase thermostat](img/Heater_ACI_Mono.png) -*Figure: Wiring diagram* +![Chauffe-eau avec thermostat ACI monophasé](img/Heater_ACI_Mono.png) +*Figure : Diagramme de câblage* -## Heater with ACI 3-phase thermostat (without neutral wire) +### Chauffe-eau avec thermostat ACI triphasé (SANS neutre) --- -**_A router with 2 outputs is needed_** +**_Nécessite un routeur avec 2 sorties_** -With this solution, you'll control each heating element separately. +Avec cette solution, vous commandez chaque résistance séparément l'une de l'autre. --- -The ACI board does not cut all 3 phases when the temperature is reached. Only 2 phases are disconnected. +La carte ACI ne coupe pas les 3 phases lorsque la température est atteinte. Seules 2 phases sont coupées. -The remaining connected phase is the one in the middle of the power connector. -***It is very IMPORTANT that this phase, which remains permanent, does not pass through a triac***. +La phase non coupée est celle qui correspond au fil du milieu sur le connecteur. ***Il est très IMPORTANT que cette phase, non coupée par le thermostat, ne passe pas par un triac***. -The ACI pcb must be connected to 3 permanent phases. +La carte ACI doit être reliée à 3 phases permanentes. -![Heater with ACI 3-phase thermostat](img/Heater_ACI_Tri.png) -*Figure: Wiring diagram* +![Chauffe-eau avec thermostat ACI triphasé](img/Heater_ACI_Tri.png) +*Figure : Diagramme de câblage* -## Alternatives WITHOUT neutral wire +### Alternatives SANS neutre --- -**_A router with 2 outputs is needed_** +**_Nécessite un routeur avec 2 sorties_** -With this solution, you won't need to add an additional neutral wire nor add a relay. +Cette solution vous permet d'économiser le rajout d'un fil de neutre et/ou l'ajout un contacteur. --- -### Heater with mechanical thermostat +#### Chauffe-eau avec thermostat mécanique -This configuration allows to simplify the wiring and specially does not require any 3-4 poles relay. +Cette configuration permet de simplifier les branchements et surtout, il n'est plus nécessaire de rajouter un contacteur tri-/quadripolaire. --- -**_Zoom on the thermostat_** +**_Zoom sur le thermostat_** -You need to take care of which wires are switched off. +Il faut bien faire attention, en regardant sur le thermostat, quelles bornes sont coupées. -In **red**, security switch (see the 'S' on each pole) : all 3 phases are switched off. +En **rouge**, coupure de sécurité (remarquez le 'S' sur chaque contact) : les 3 phases sont coupées. -In **green**, only 2 phases are switched off, L2 et L3. ***It is IMPORTANT that the phase L1, not switched by the thermostat, DOES NOT pass through the triac***. +En **vert**, seules 2 phases sont coupées, L2 et L3. ***Il est très IMPORTANT que la phase L1, non coupée par le thermostat, ne passe pas par un triac***. -![Mechanical thermostat](img/Thermostat.png) -*Figure: An example of a thermostat* +![Thermostat mécanique](img/Thermostat.png) +*Figure: Exemple de thermostat* --- -![Heater with mechanical thermostat](img/Heater_mechanical-No_neutral.png) -*Figure: Wiring diagram* +![Chauffe-eau avec thermostat mécanique](img/Heater_mechanical-No_neutral.png) +*Figure: Diagramme de câblage* ## Support This project is maintained by [@FredM67](https://github.com/FredM67). Please understand that we won't be able to provide individual support via email. We also believe that help is much more valuable if it's shared publicly, so that more people can benefit from it. -| Type | Platforms | -| -------------------------------------- | ----------------------------------------------------------------------------- | -| ?? **Bug Reports** | [GitHub Issue Tracker](https://github.com/FredM67/PVRouter-3-phase/issues) | -| ?? **Docs Issue** | [GitHub Issue Tracker](https://github.com/FredM67/PVRouter-3-phase/issues) | -| ?? **Feature Requests** | [GitHub Issue Tracker](https://github.com/FredM67/PVRouter-3-phase/issues) | -| ?? **Report a security vulnerability** | See [SECURITY.md](SECURITY.md) | -| ?? **General Questions** | [GitHub Discussions](https://github.com/FredM67/PVRouter-3-phase/discussions) | +| Type | Platforms | +| ------------------------------------- | ----------------------------------------------------------------------------- | +| 🚨 **Bug Reports** | [GitHub Issue Tracker](https://github.com/FredM67/PVRouter-3-phase/issues) | +| 📚 **Docs Issue** | [GitHub Issue Tracker](https://github.com/FredM67/PVRouter-3-phase/issues) | +| 🎁 **Feature Requests** | [GitHub Issue Tracker](https://github.com/FredM67/PVRouter-3-phase/issues) | +| 🛡 **Report a security vulnerability** | See [SECURITY.md](SECURITY.md) | +| 💬 **General Questions** | [GitHub Discussions](https://github.com/FredM67/PVRouter-3-phase/discussions) | ## Roadmap @@ -260,6 +275,6 @@ Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduc ## Authors and acknowledgment -- **Fr�d�ric Metrich** - _Initial work_ - [FredM67](https://github.com/FredM67) +- **Frédéric Metrich** - _Initial work_ - [FredM67](https://github.com/FredM67) See also the list of [contributors](https://github.com/FredM67/PVRouter-3-phase/graphs/contributors) who participated in this project. diff --git a/dev/MathPerfTests/.gitignore b/dev/MathPerfTests/.gitignore new file mode 100644 index 00000000..89cc49cb --- /dev/null +++ b/dev/MathPerfTests/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/dev/MathPerfTests/.vscode/extensions.json b/dev/MathPerfTests/.vscode/extensions.json new file mode 100644 index 00000000..080e70d0 --- /dev/null +++ b/dev/MathPerfTests/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/dev/MathPerfTests/MathPerfTests.ino b/dev/MathPerfTests/MathPerfTests.ino index 87abab3f..bbc0f240 100644 --- a/dev/MathPerfTests/MathPerfTests.ino +++ b/dev/MathPerfTests/MathPerfTests.ino @@ -4,7 +4,7 @@ #include "movingAvg.h" const int nb_of_interation_per_pass = 500; -const int nb_of_pass = 10; +const int nb_of_pass = 1; unsigned long initial_time = 0; unsigned long final_time = 0; @@ -39,52 +39,88 @@ volatile boolean bool_1 = 0; volatile boolean bool_2 = 0; volatile boolean bool_3 = 0; -constexpr uint8_t round_up_to_power_of_2(uint16_t v) { +volatile int16_t input_data[nb_of_interation_per_pass]; + +constexpr uint8_t round_up_to_power_of_2(uint16_t v) +{ if (__builtin_popcount(v) == 1) { return __builtin_ctz(v) - 1; } uint8_t next_pow_of_2{ 0 }; - while (v) { + while (v) + { v >>= 1; ++next_pow_of_2; } - return --next_pow_of_2; + return next_pow_of_2; } template< uint8_t A = 10 > -class EWMA_average { +class EWMA_average +{ public: - void addValue(int32_t input) { - w = w - x + input; - x = w >> round_up_to_power_of_2(A); + void addValue(int32_t input) + { + ema_raw = ema_raw - ema + input; + ema = ema_raw >> round_up_to_power_of_2(A); + + ema_ema_raw = ema_ema_raw - ema_ema + ema; + ema_ema = ema_ema_raw >> round_up_to_power_of_2(A); } - auto getAverage() const { - return x; + auto getAverageS() const + { + return ema; + } + + auto getAverageD() const + { + return (ema << 1) - ema_ema; } private: - int32_t w{ 0 }; - int32_t x{ 0 }; + int32_t ema_ema_raw{ 0 }; + int32_t ema_ema{ 0 }; + int32_t ema_raw{ 0 }; + int32_t ema{ 0 }; }; movingAvg< int32_t, 10, 12 > sliding_Average; -EWMA_average<120> ewma_average; +EWMA_average< 120 > ewma_average; -void setup() { +constexpr auto alpha = round_up_to_power_of_2(120); +void setup() +{ Serial.begin(115200); Serial.println("Setup ***"); } -void loop() { +void pause() +{ + byte done = false; + byte dummyByte; + + while (done != true) + { + if (Serial.available() > 0) + { + dummyByte = Serial.read(); // to 'consume' the incoming byte + if (dummyByte == 'g') done++; + } + } +} - for (int j = 0; j < nb_of_pass; j++) { +void loop() +{ + for (int j = 0; j < nb_of_pass; j++) + { // STEP 1: We first calculate the time taken to run a dummy FOR loop to measure the overhead cause by the execution of the loop. initial_time = micros(); - for (int i = 0; i < nb_of_interation_per_pass; i++) { + for (int i = 0; i < nb_of_interation_per_pass; i++) + { dummy++; // A dummy instruction is introduced here. If not, the compiler is smart enough to just skip the loop entirely... } final_time = micros(); @@ -92,18 +128,39 @@ void loop() { dummy = 0; // STEP 2 (optional): Pick some relevant random numbers to test the command under random conditions. Make sure to pick numbers appropriate for your command (e.g. no negative number for the command "sqrt()") - randomSeed(micros() * analogRead(0)); - long_1 = random(-36000, 36000); + //randomSeed(micros() * analogRead(0)); + //long_1 = random(-36000, 36000); + + for (int i = 0; i < nb_of_interation_per_pass; i++) + { + if (i < 1) + input_data[i] = 0; + else if (i < 125) + input_data[i] = 500; + else if (i < 250) + input_data[i] = 1000; + else /* if (i < 375) */ + input_data[i] = -500; + // else + // input_data[i] = 500; + } // STEP 3: Calculation of the time taken to run the dummy FOR loop and the command to test. initial_time = micros(); - for (int i = 0; i < nb_of_interation_per_pass; i++) { + for (int i = 0; i < nb_of_interation_per_pass; i++) + { dummy++; // The dummy instruction is also performed here so that we can remove the effect of the dummy FOR loop accurately. // **************** PUT YOUR COMMAND TO TEST HERE ******************** - long_1 = sin(i) * 1000; + //sliding_Average.addValue(long_1); + ewma_average.addValue(input_data[i]); - sliding_Average.addValue(long_1); - ewma_average.addValue(long_1); + Serial.print(i); + Serial.print(" | "); + Serial.print(input_data[i]); + Serial.print(" | "); + Serial.print(ewma_average.getAverageS()); + Serial.print(" | "); + Serial.println(ewma_average.getAverageD()); // **************** PUT YOUR COMMAND TO TEST HERE ******************** } @@ -116,7 +173,9 @@ void loop() { Serial.print(sliding_Average.getAverage()); Serial.print(" - "); - Serial.print(ewma_average.getAverage()); + Serial.print(ewma_average.getAverageS()); + Serial.print(" - "); + Serial.print(ewma_average.getAverageD()); Serial.print(" - "); Serial.print(j); Serial.print(". "); @@ -135,10 +194,11 @@ void loop() { //} //Serial.println(); duration_sum = 0; - delay(2000); + pause(); } -void print_result(float value_to_print) { +void print_result(float value_to_print) +{ Serial.print("Time to execute command: "); Serial.print("\t"); Serial.print(value_to_print, 3); diff --git a/dev/MathPerfTests/movingAvg.h b/dev/MathPerfTests/movingAvg.h index 304e5e9d..d18f6f16 100644 --- a/dev/MathPerfTests/movingAvg.h +++ b/dev/MathPerfTests/movingAvg.h @@ -195,8 +195,8 @@ class movingAvg T _sub_ar[VALUES_PER_MINUTE]{}; T _ar[DURATION_IN_MINUTES]{}; - static constexpr float invD{ 1.0F / VALUES_PER_MINUTE }; - static constexpr float invN{ 1.0F / DURATION_IN_MINUTES }; + static constexpr float invD{ 1.0 / VALUES_PER_MINUTE }; + static constexpr float invN{ 1.0 / DURATION_IN_MINUTES }; }; #endif diff --git a/dev/MathPerfTests/platformio.ini b/dev/MathPerfTests/platformio.ini new file mode 100644 index 00000000..383a1bf6 --- /dev/null +++ b/dev/MathPerfTests/platformio.ini @@ -0,0 +1,24 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +src_dir = ./ + +[env:uno] +platform = atmelavr +board = uno +framework = arduino +build_flags = + -std=c++1z + -std=gnu++1z +build_unflags = + -std=c++11 + -std=gnu++11 +monitor_speed = 115200