From 97d74d309000e8e210178ee829c51a1ad6a4545f Mon Sep 17 00:00:00 2001
From: lumapu <lp@lufami.de>
Date: Thu, 25 Jan 2024 00:03:49 +0100
Subject: [PATCH 1/5] 0.8.65 - 2024-01-24 * removed patch for NRF `PLOS` * fix
 lang issues #1388 * fix build on Windows of `opendtufusion` environments
 (git: trailing whitespaces)

---
 patches/RF24.patch      | 35 -----------------------------------
 scripts/applyPatches.py |  6 ++----
 src/CHANGES.md          |  5 +++++
 src/defines.h           |  2 +-
 src/hm/CommQueue.h      |  6 ++----
 src/hm/Communication.h  | 14 +-------------
 src/hm/hmRadio.h        | 27 ++++++---------------------
 src/hm/radio.h          | 10 ++++------
 src/hms/hmsRadio.h      | 11 +++++------
 src/web/lang.json       |  4 ++--
 10 files changed, 28 insertions(+), 92 deletions(-)
 delete mode 100644 patches/RF24.patch

diff --git a/patches/RF24.patch b/patches/RF24.patch
deleted file mode 100644
index 63db8823..00000000
--- a/patches/RF24.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-diff --git a/RF24.cpp b/RF24.cpp
-index 9e5b4a8..a4de63c 100644
---- a/RF24.cpp
-+++ b/RF24.cpp
-@@ -1871,6 +1871,11 @@ uint8_t RF24::getARC(void)
-     return read_register(OBSERVE_TX) & 0x0F;
- }
- 
-+uint8_t RF24::getPLOS(void)
-+{
-+    return read_register(OBSERVE_TX) & 0x0F;
-+}
-+
- /****************************************************************************/
- 
- bool RF24::setDataRate(rf24_datarate_e speed)
-diff --git a/RF24.h b/RF24.h
-index dbd32ae..a3d6b52 100644
---- a/RF24.h
-+++ b/RF24.h
-@@ -1644,6 +1644,7 @@ public:
-      * @return Returns values from 0 to 15.
-      */
-     uint8_t getARC(void);
-+    uint8_t getPLOS(void);
- 
-     /**
-      * Set the transmission @ref Datarate
-@@ -2415,4 +2416,4 @@ private:
-  * Use `ctrl+c` to quit at any time.
-  */
- 
--#endif // __RF24_H__
-\ No newline at end of file
-+#endif // __RF24_H__
diff --git a/scripts/applyPatches.py b/scripts/applyPatches.py
index 3289badb..147fb0f3 100644
--- a/scripts/applyPatches.py
+++ b/scripts/applyPatches.py
@@ -12,11 +12,11 @@ def applyPatch(libName, patchFile):
 
     os.chdir('.pio/libdeps/' + env['PIOENV'] + '/' + libName)
 
-    process = subprocess.run(['git', 'apply', '--reverse', '--check', '../../../../' + patchFile], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+    process = subprocess.run(['git', 'apply', '--ignore-whitespace', '--reverse', '--check', '../../../../' + patchFile], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
     if (process.returncode == 0):
         print('\'' + patchFile + '\' already applied')
     else:
-        process = subprocess.run(['git', 'apply', '../../../../' + patchFile])
+        process = subprocess.run(['git', 'apply', '--ignore-whitespace', '../../../../' + patchFile])
         if (process.returncode == 0):
             print('\'' + patchFile + '\' applied')
         else:
@@ -32,5 +32,3 @@ if env['PIOENV'][:22] != "opendtufusion-ethernet":
 if env['PIOENV'][:13] == "opendtufusion":
     applyPatch("GxEPD2", "../patches/GxEPD2_SW_SPI.patch")
     applyPatch("RF24", "../patches/RF24_Hal.patch")
-else:
-    applyPatch("RF24", "../patches/RF24.patch")
diff --git a/src/CHANGES.md b/src/CHANGES.md
index a4942d55..b8547302 100644
--- a/src/CHANGES.md
+++ b/src/CHANGES.md
@@ -1,5 +1,10 @@
 # Development Changes
 
+## 0.8.65 - 2024-01-24
+* removed patch for NRF `PLOS`
+* fix lang issues #1388
+* fix build on Windows of `opendtufusion` environments (git: trailing whitespaces)
+
 ## 0.8.64 - 2024-01-22
 * add `ARC` to log (NRF24 Debug)
 * merge PR: ETH NTP update bugfix #1385
diff --git a/src/defines.h b/src/defines.h
index b8ddace9..ed975b7a 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -13,7 +13,7 @@
 //-------------------------------------
 #define VERSION_MAJOR       0
 #define VERSION_MINOR       8
-#define VERSION_PATCH       64
+#define VERSION_PATCH       65
 
 //-------------------------------------
 typedef struct {
diff --git a/src/hm/CommQueue.h b/src/hm/CommQueue.h
index d3fe1c69..0ca9279f 100644
--- a/src/hm/CommQueue.h
+++ b/src/hm/CommQueue.h
@@ -18,8 +18,6 @@
 template <uint8_t N=100>
 class CommQueue {
     public:
-        CommQueue() {}
-
         void addImportant(Inverter<> *iv, uint8_t cmd) {
             dec(&mRdPtr);
             mQueue[mRdPtr] = queue_s(iv, cmd, true);
@@ -34,12 +32,12 @@ class CommQueue {
             mQueue[mWrPtr] = queue_s(iv, cmd, false);
         }
 
-        uint8_t getFillState(void) {
+        uint8_t getFillState(void) const {
             //DPRINTLN(DBG_INFO, "wr: " + String(mWrPtr) + ", rd: " + String(mRdPtr));
             return abs(mRdPtr - mWrPtr);
         }
 
-        uint8_t getMaxFill(void) {
+        uint8_t getMaxFill(void) const {
             return N;
         }
 
diff --git a/src/hm/Communication.h b/src/hm/Communication.h
index 98dd06e1..3a6fdd0b 100644
--- a/src/hm/Communication.h
+++ b/src/hm/Communication.h
@@ -134,14 +134,7 @@ class Communication : public CommQueue<> {
                             DPRINT_IVID(DBG_INFO, q->iv->id);
                             DBGPRINT(F("request timeout: "));
                             DBGPRINT(String(q->iv->radio->mRadioWaitTime.getRunTime()));
-                            DBGPRINT(F("ms"));
-                            if(INV_RADIO_TYPE_NRF == q->iv->ivRadioType) {
-                                DBGPRINT(F(", ARC "));
-                                DBGPRINT(String(q->iv->radio->getARC()));
-                                DBGPRINT(F(", PLOS "));
-                                DBGPRINTLN(String(q->iv->radio->getPLOS()));
-                            } else
-                                DBGPRINTLN("");
+                            DBGPRINTLN(F("ms"));
                         }
                         if(!q->iv->mGotFragment) {
                             if(INV_RADIO_TYPE_CMT == q->iv->ivRadioType) {
@@ -291,11 +284,6 @@ class Communication : public CommQueue<> {
             DBGPRINT(String(p->millis));
             DBGPRINT(F("ms | "));
             DBGPRINT(String(p->len));
-            DBGPRINT(F(", ARC "));
-            DBGPRINT(String(p->arc));
-            DBGPRINT(F(", PLOS "));
-            DBGPRINT(String(p->plos));
-            DBGPRINT(F(" |"));
             if(INV_RADIO_TYPE_NRF == q->iv->ivRadioType) {
                 DBGPRINT(F(" CH"));
                 if(3 == p->ch)
diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h
index ef37e266..d73b2b0e 100644
--- a/src/hm/hmRadio.h
+++ b/src/hm/hmRadio.h
@@ -15,7 +15,6 @@
 #endif
 
 #define SPI_SPEED           1000000
-
 #define RF_CHANNELS         5
 
 const char* const rf24AmpPowerNames[] = {"MIN", "LOW", "HIGH", "MAX"};
@@ -183,17 +182,13 @@ class HmRadio : public Radio {
                         }
                     }
                     return mNRFisInRX;
-                } /*else if(tx_fail) {
-                    mNRFisInRX = false;
-                    return false;
-                }*/
+                }
             }
 
             return false;
         }
 
-        bool isChipConnected(void) {
-            //DPRINTLN(DBG_VERBOSE, F("hmRadio.h:isChipConnected"));
+        bool isChipConnected(void) const {
             return mNrf24->isChipConnected();
         }
 
@@ -283,24 +278,16 @@ class HmRadio : public Radio {
             sendPacket(iv, cnt, isRetransmit, (IV_MI != iv->ivGen));
         }
 
-        uint8_t getDataRate(void) {
+        uint8_t getDataRate(void) const {
             if(!mNrf24->isChipConnected())
                 return 3; // unknown
             return mNrf24->getDataRate();
         }
 
-        bool isPVariant(void) {
+        bool isPVariant(void) const {
             return mNrf24->isPVariant();
         }
 
-        uint8_t getARC(void) {
-            return mNrf24->getARC();
-        }
-
-        uint8_t getPLOS(void) {
-            return mNrf24->getPLOS();
-        }
-
     private:
         inline bool getReceived(void) {
             bool isLastPackage = false;
@@ -315,8 +302,6 @@ class HmRadio : public Radio {
                     p.len  = (len > MAX_RF_PAYLOAD_SIZE) ? MAX_RF_PAYLOAD_SIZE : len;
                     p.rssi = mNrf24->testRPD() ? -64 : -75;
                     p.millis = millis() - mMillis;
-                    p.arc  = mNrf24->getARC();
-                    p.plos = mNrf24->getPLOS();
                     mNrf24->read(p.packet, p.len);
 
                     if (p.packet[0] != 0x00) {
@@ -393,11 +378,11 @@ class HmRadio : public Radio {
             mNRFisInRX = false;
         }
 
-        uint64_t getIvId(Inverter<> *iv) {
+        uint64_t getIvId(Inverter<> *iv) const {
             return iv->radioId.u64;
         }
 
-        uint8_t getIvGen(Inverter<> *iv) {
+        uint8_t getIvGen(Inverter<> *iv) const {
             return iv->ivGen;
         }
 
diff --git a/src/hm/radio.h b/src/hm/radio.h
index a71e24c7..f7d61e42 100644
--- a/src/hm/radio.h
+++ b/src/hm/radio.h
@@ -27,10 +27,8 @@ class Radio {
         virtual void sendControlPacket(Inverter<> *iv, uint8_t cmd, uint16_t *data, bool isRetransmit) = 0;
         virtual bool switchFrequency(Inverter<> *iv, uint32_t fromkHz, uint32_t tokHz) { return true; }
         virtual bool switchFrequencyCh(Inverter<> *iv, uint8_t fromCh, uint8_t toCh) { return true; }
-        virtual bool isChipConnected(void) { return false; }
+        virtual bool isChipConnected(void) const { return false; }
         virtual bool loop(void) = 0;
-        virtual uint8_t getARC(void) { return 0xff; }
-        virtual uint8_t getPLOS(void) { return 0xff; }
 
         void handleIntr(void) {
             mIrqRcvd = true;
@@ -66,7 +64,7 @@ class Radio {
             sendPacket(iv, 24, isRetransmit);
         }
 
-        uint32_t getDTUSn(void) {
+        uint32_t getDTUSn(void) const {
             return mDtuSn;
         }
 
@@ -81,8 +79,8 @@ class Radio {
 
     protected:
         virtual void sendPacket(Inverter<> *iv, uint8_t len, bool isRetransmit, bool appendCrc16=true) = 0;
-        virtual uint64_t getIvId(Inverter<> *iv) = 0;
-        virtual uint8_t getIvGen(Inverter<> *iv) = 0;
+        virtual uint64_t getIvId(Inverter<> *iv) const = 0;
+        virtual uint8_t getIvGen(Inverter<> *iv) const = 0;
 
         void initPacket(uint64_t ivId, uint8_t mid, uint8_t pid) {
             mTxBuf[0] = mid;
diff --git a/src/hms/hmsRadio.h b/src/hms/hmsRadio.h
index 48ff3750..2f0a5f31 100644
--- a/src/hms/hmsRadio.h
+++ b/src/hms/hmsRadio.h
@@ -14,8 +14,7 @@ class CmtRadio : public Radio {
     typedef Cmt2300a CmtType;
     public:
         CmtRadio() {
-            mDtuSn    = DTU_SN;
-            mCmtAvail = false;
+            mDtuSn = DTU_SN;
         }
 
         void setup(bool *serialDebug, bool *privacyMode, bool *printWholeTrace, uint8_t pinSclk, uint8_t pinSdio, uint8_t pinCsb, uint8_t pinFcsb, bool genDtuSn = true) {
@@ -38,7 +37,7 @@ class CmtRadio : public Radio {
             return false;
         }
 
-        bool isChipConnected(void) {
+        bool isChipConnected(void) const {
             return mCmtAvail;
         }
 
@@ -116,11 +115,11 @@ class CmtRadio : public Radio {
             iv->mDtuTxCnt++;
         }
 
-        uint64_t getIvId(Inverter<> *iv) {
+        uint64_t getIvId(Inverter<> *iv) const {
             return iv->radioId.u64;
         }
 
-        uint8_t getIvGen(Inverter<> *iv) {
+        uint8_t getIvGen(Inverter<> *iv) const {
             return iv->ivGen;
         }
 
@@ -172,7 +171,7 @@ class CmtRadio : public Radio {
         }
 
         CmtType mCmt;
-        bool mCmtAvail;
+        bool mCmtAvail = false;
         bool mRqstGetRx = false;
         uint32_t mMillis;
 };
diff --git a/src/web/lang.json b/src/web/lang.json
index 1eece937..9f3df46e 100644
--- a/src/web/lang.json
+++ b/src/web/lang.json
@@ -670,7 +670,7 @@
                 },
                 {
                     "token": "INV_DELETE_SURE",
-                    "en": "do you realy want to delete inverter?",
+                    "en": "do you really want to delete inverter?",
                     "de": "Willst du den Wechselrichter wirklich l&ouml;schen?"
                 },
                 {
@@ -1070,7 +1070,7 @@
                 },
                 {
                     "token": "WARN_DIFF_ENV",
-                    "en": "your environment does not match the update file!",
+                    "en": "your environment may not match the update file!",
                     "de": "Die ausgew&auml;hlte Firmware passt u.U. nicht zum Chipsatz!"
                 },
                 {

From dc696d727fa0eecff28e301febe1f3f72fe8a09f Mon Sep 17 00:00:00 2001
From: lumapu <lp@lufami.de>
Date: Sun, 28 Jan 2024 23:32:44 +0100
Subject: [PATCH 2/5] 0.8.66 * added support for other regions - untested #1271
 * fix generation of DTU-ID; was computed twice without reset if two radios
 are enabled

---
 src/CHANGES.md          |   4 +
 src/app.cpp             |   2 +-
 src/config/settings.h   |  14 ++-
 src/defines.h           |   2 +-
 src/hm/Communication.h  |   2 +-
 src/hm/hmDefines.h      |   8 --
 src/hm/hmInverter.h     |   2 -
 src/hm/radio.h          |   9 +-
 src/hms/cmt2300a.h      | 259 +++++++++++++++++++++++++---------------
 src/hms/hmsRadio.h      |  34 ++++--
 src/web/RestApi.h       |   5 +
 src/web/html/setup.html |  25 +++-
 src/web/lang.json       |  10 ++
 src/web/web.h           |   3 +-
 14 files changed, 251 insertions(+), 128 deletions(-)

diff --git a/src/CHANGES.md b/src/CHANGES.md
index b8547302..7f544e69 100644
--- a/src/CHANGES.md
+++ b/src/CHANGES.md
@@ -1,5 +1,9 @@
 # Development Changes
 
+## 0.8.66 - 2024-01-28
+* added support for other regions - untested #1271
+* fix generation of DTU-ID; was computed twice without reset if two radios are enabled
+
 ## 0.8.65 - 2024-01-24
 * removed patch for NRF `PLOS`
 * fix lang issues #1388
diff --git a/src/app.cpp b/src/app.cpp
index 9f569259..a14a8811 100644
--- a/src/app.cpp
+++ b/src/app.cpp
@@ -47,7 +47,7 @@ void app::setup() {
     }
     #if defined(ESP32)
     if(mConfig->cmt.enabled) {
-        mCmtRadio.setup(&mConfig->serial.debug, &mConfig->serial.privacyLog, &mConfig->serial.printWholeTrace, mConfig->cmt.pinSclk, mConfig->cmt.pinSdio, mConfig->cmt.pinCsb, mConfig->cmt.pinFcsb);
+        mCmtRadio.setup(&mConfig->serial.debug, &mConfig->serial.privacyLog, &mConfig->serial.printWholeTrace, mConfig->cmt.pinSclk, mConfig->cmt.pinSdio, mConfig->cmt.pinCsb, mConfig->cmt.pinFcsb, mConfig->sys.region);
     }
     #endif
     #ifdef ETHERNET
diff --git a/src/config/settings.h b/src/config/settings.h
index d8c93b10..e074d5b1 100644
--- a/src/config/settings.h
+++ b/src/config/settings.h
@@ -30,7 +30,7 @@
  * https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#flash-layout
  * */
 
-#define CONFIG_VERSION      9
+#define CONFIG_VERSION      10
 
 
 #define PROT_MASK_INDEX     0x0001
@@ -68,6 +68,8 @@ typedef struct {
     uint16_t protectionMask;
     bool darkMode;
     bool schedReboot;
+    uint8_t region;
+    int8_t timezone;
 
 #if !defined(ETHERNET)
     // wifi
@@ -395,6 +397,8 @@ class settings {
             #endif /* !defined(ETHERNET) */
 
             snprintf(mCfg.sys.deviceName,  DEVNAME_LEN, DEF_DEVICE_NAME);
+            mCfg.sys.region   = 0; // Europe
+            mCfg.sys.timezone = 1;
 
             mCfg.nrf.pinCs             = DEF_NRF_CS_PIN;
             mCfg.nrf.pinCe             = DEF_NRF_CE_PIN;
@@ -512,6 +516,10 @@ class settings {
                 if(mCfg.configVersion < 9) {
                     mCfg.inst.gapMs = 1;
                 }
+                if(mCfg.configVersion < 10) {
+                    mCfg.sys.region   = 0; // Europe
+                    mCfg.sys.timezone = 1;
+                }
             }
         }
 
@@ -537,6 +545,8 @@ class settings {
                 obj[F("prot_mask")] = mCfg.sys.protectionMask;
                 obj[F("dark")] = mCfg.sys.darkMode;
                 obj[F("reb")] = (bool) mCfg.sys.schedReboot;
+                obj[F("region")] = mCfg.sys.region;
+                obj[F("timezone")] = mCfg.sys.timezone;
                 ah::ip2Char(mCfg.sys.ip.ip, buf);      obj[F("ip")]   = String(buf);
                 ah::ip2Char(mCfg.sys.ip.mask, buf);    obj[F("mask")] = String(buf);
                 ah::ip2Char(mCfg.sys.ip.dns1, buf);    obj[F("dns1")] = String(buf);
@@ -554,6 +564,8 @@ class settings {
                 getVal<uint16_t>(obj, F("prot_mask"), &mCfg.sys.protectionMask);
                 getVal<bool>(obj, F("dark"), &mCfg.sys.darkMode);
                 getVal<bool>(obj, F("reb"), &mCfg.sys.schedReboot);
+                getVal<uint8_t>(obj, F("region"), &mCfg.sys.region);
+                getVal<int8_t>(obj, F("timezone"), &mCfg.sys.timezone);
                 if(obj.containsKey(F("ip"))) ah::ip2Arr(mCfg.sys.ip.ip,      obj[F("ip")].as<const char*>());
                 if(obj.containsKey(F("mask"))) ah::ip2Arr(mCfg.sys.ip.mask,    obj[F("mask")].as<const char*>());
                 if(obj.containsKey(F("dns1"))) ah::ip2Arr(mCfg.sys.ip.dns1,    obj[F("dns1")].as<const char*>());
diff --git a/src/defines.h b/src/defines.h
index ed975b7a..786908d0 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -13,7 +13,7 @@
 //-------------------------------------
 #define VERSION_MAJOR       0
 #define VERSION_MINOR       8
-#define VERSION_PATCH       65
+#define VERSION_PATCH       66
 
 //-------------------------------------
 typedef struct {
diff --git a/src/hm/Communication.h b/src/hm/Communication.h
index 3a6fdd0b..ae97dbb2 100644
--- a/src/hm/Communication.h
+++ b/src/hm/Communication.h
@@ -138,7 +138,7 @@ class Communication : public CommQueue<> {
                         }
                         if(!q->iv->mGotFragment) {
                             if(INV_RADIO_TYPE_CMT == q->iv->ivRadioType) {
-                                q->iv->radio->switchFrequency(q->iv, HOY_BOOT_FREQ_KHZ, (q->iv->config->frequency*FREQ_STEP_KHZ + HOY_BASE_FREQ_KHZ));
+                                q->iv->radio->switchFrequency(q->iv, q->iv->radio->getBootFreqMhz() * 1000, (q->iv->config->frequency*FREQ_STEP_KHZ + q->iv->radio->getBaseFreqMhz() * 1000));
                                 mWaitTime.startTimeMonitor(1000);
                             } else {
                                 if(IV_MI == q->iv->ivGen)
diff --git a/src/hm/hmDefines.h b/src/hm/hmDefines.h
index 969e4e1b..8df06dc1 100644
--- a/src/hm/hmDefines.h
+++ b/src/hm/hmDefines.h
@@ -78,14 +78,6 @@ enum {CH0 = 0, CH1, CH2, CH3, CH4, CH5, CH6};
 enum {INV_TYPE_1CH = 0, INV_TYPE_2CH, INV_TYPE_4CH, INV_TYPE_6CH};
 enum {INV_RADIO_TYPE_NRF = 0, INV_RADIO_TYPE_CMT};
 
-#define WORK_FREQ_KHZ       865000 // desired work frequency between DTU and
-                                   // inverter in kHz
-#define HOY_BASE_FREQ_KHZ   860000 // in kHz
-#define HOY_MAX_FREQ_KHZ    923500 // 0xFE * 250kHz + Base_freq
-#define HOY_BOOT_FREQ_KHZ   868000 // Hoymiles boot/init frequency after power up inverter
-#define FREQ_STEP_KHZ       250    // channel step size in kHz
-#define FREQ_WARN_MIN_KHZ   863000 // for EU 863 - 870 MHz is allowed
-#define FREQ_WARN_MAX_KHZ   870000 // for EU 863 - 870 MHz is allowed
 
 #define DURATION_ONEFRAME       50 // timeout parameter for each expected frame (ms)
 //#define DURATION_RESERVE  {90,120} // timeout parameter to still wait after last expected frame (ms)
diff --git a/src/hm/hmInverter.h b/src/hm/hmInverter.h
index b28682e5..cc73cb0b 100644
--- a/src/hm/hmInverter.h
+++ b/src/hm/hmInverter.h
@@ -179,8 +179,6 @@ class Inverter {
             tsMaxAcPower       = 0;
 
             memset(&radioStatistics, 0, sizeof(statistics_t));
-            memset(heuristics.txRfQuality, -6, 5);
-
             memset(mOffYD, 0, sizeof(float) * 6);
             memset(mLastYD, 0, sizeof(float) * 6);
         }
diff --git a/src/hm/radio.h b/src/hm/radio.h
index f7d61e42..907a2023 100644
--- a/src/hm/radio.h
+++ b/src/hm/radio.h
@@ -1,5 +1,5 @@
 //-----------------------------------------------------------------------------
-// 2023 Ahoy, https://github.com/lumpapu/ahoy
+// 2024 Ahoy, https://github.com/lumpapu/ahoy
 // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/4.0/deed
 //-----------------------------------------------------------------------------
 
@@ -28,6 +28,9 @@ class Radio {
         virtual bool switchFrequency(Inverter<> *iv, uint32_t fromkHz, uint32_t tokHz) { return true; }
         virtual bool switchFrequencyCh(Inverter<> *iv, uint8_t fromCh, uint8_t toCh) { return true; }
         virtual bool isChipConnected(void) const { return false; }
+        virtual uint16_t getBaseFreqMhz() { return 0; }
+        virtual uint16_t getBootFreqMhz() { return 0; }
+        virtual std::pair<uint16_t,uint16_t> getFreqRangeMhz(void) { return std::make_pair(0, 0); }
         virtual bool loop(void) = 0;
 
         void handleIntr(void) {
@@ -113,6 +116,7 @@ class Radio {
             chipID = ESP.getChipId();
             #endif
 
+            mDtuSn = 0;
             uint8_t t;
             for(int i = 0; i < (7 << 2); i += 4) {
                 t = (chipID >> i) & 0x0f;
@@ -121,7 +125,8 @@ class Radio {
                 mDtuSn |= (t << i);
             }
             mDtuSn |= 0x80000000; // the first digit is an 8 for DTU production year 2022, the rest is filled with the ESP chipID in decimal
-                    }
+        }
+
 
         uint32_t mDtuSn;
         volatile bool mIrqRcvd;
diff --git a/src/hms/cmt2300a.h b/src/hms/cmt2300a.h
index 1ff112e2..524472f0 100644
--- a/src/hms/cmt2300a.h
+++ b/src/hms/cmt2300a.h
@@ -1,5 +1,5 @@
 //-----------------------------------------------------------------------------
-// 2023 Ahoy, https://github.com/lumpapu/ahoy
+// 2024 Ahoy, https://github.com/lumpapu/ahoy
 // Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed
 //-----------------------------------------------------------------------------
 
@@ -12,8 +12,23 @@
 #include "esp32_3wSpi.h"
 #endif
 
-// detailed register infos from AN142_CMT2300AW_Quick_Start_Guide-Rev0.8.pdf
+#include <utility>
+
+enum class RegionCfg : uint8_t {
+    EUROPE, USA, BRAZIL, NUM
+};
+
+enum class CmtStatus : uint8_t {
+    SUCCESS = 0,
+    ERR_SWITCH_STATE,
+    ERR_TX_PENDING,
+    FIFO_EMPTY,
+    ERR_RX_IN_FIFO
+};
+
+#define FREQ_STEP_KHZ       250    // channel step size in kHz
 
+// detailed register infos from AN142_CMT2300AW_Quick_Start_Guide-Rev0.8.pdf
 #define CMT2300A_MASK_CFG_RETAIN        0x10
 #define CMT2300A_MASK_RSTN_IN_EN        0x20
 #define CMT2300A_MASK_LOCKING_EN        0x20
@@ -152,67 +167,6 @@
 #define CMT2300A_MASK_TX_DONE_FLG       0x08
 #define CMT2300A_MASK_PKT_OK_FLG        0x01
 
-// this list and the TX5, TX10 registers were compiled from the output of
-// HopeRF RFPDK Tool v1.54
-static uint8_t paLevelList[31][2] PROGMEM = {
-    {0x17, 0x01}, // -10dBm
-    {0x1a, 0x01}, // -09dBm
-    {0x1d, 0x01}, // -08dBm
-    {0x21, 0x01}, // -07dBm
-    {0x25, 0x01}, // -06dBm
-    {0x29, 0x01}, // -05dBm
-    {0x2d, 0x01}, // -04dBm
-    {0x33, 0x01}, // -03dBm
-    {0x39, 0x02}, // -02dBm
-    {0x41, 0x02}, // -01dBm
-    {0x4b, 0x02}, //  00dBm
-    {0x56, 0x03}, //  01dBm
-    {0x63, 0x03}, //  02dBm
-    {0x71, 0x04}, //  03dBm
-    {0x80, 0x04}, //  04dBm
-    {0x22, 0x01}, //  05dBm
-    {0x27, 0x04}, //  06dBm
-    {0x2c, 0x05}, //  07dBm
-    {0x31, 0x06}, //  08dBm
-    {0x38, 0x06}, //  09dBm
-    {0x3f, 0x07}, //  10dBm
-    {0x48, 0x08}, //  11dBm
-    {0x52, 0x09}, //  12dBm
-    {0x5d, 0x0b}, //  13dBm
-    {0x6a, 0x0c}, //  14dBm
-    {0x79, 0x0d}, //  15dBm
-    {0x46, 0x10}, //  16dBm
-    {0x51, 0x10}, //  17dBm
-    {0x60, 0x12}, //  18dBm
-    {0x71, 0x14}, //  19dBm
-    {0x8c, 0x1c}  //  20dBm
-};
-
-// default CMT parameters
-static uint8_t cmtConfig[0x60] PROGMEM {
-    // 0x00 - 0x0f -- RSSI offset +- 0 and 13dBm
-    0x00, 0x66, 0xEC, 0x1C, 0x70, 0x80, 0x14, 0x08,
-    0x11, 0x02, 0x02, 0x00, 0xAE, 0xE0, 0x35, 0x00,
-    // 0x10 - 0x1f
-    0x00, 0xF4, 0x10, 0xE2, 0x42, 0x20, 0x0C, 0x81,
-    0x42, 0x32, 0xCF, 0x82, 0x42, 0x27, 0x76, 0x12, // 860MHz as default
-    // 0x20 - 0x2f
-    0xA6, 0xC9, 0x20, 0x20, 0xD2, 0x35, 0x0C, 0x0A,
-    0x9F, 0x4B, 0x29, 0x29, 0xC0, 0x14, 0x05, 0x53,
-    // 0x30 - 0x3f
-    0x10, 0x00, 0xB4, 0x00, 0x00, 0x01, 0x00, 0x00,
-    0x12, 0x1E, 0x00, 0xAA, 0x06, 0x00, 0x00, 0x00,
-    // 0x40 - 0x4f
-    0x00, 0x48, 0x5A, 0x48, 0x4D, 0x01, 0x1F, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x60,
-    // 0x50 - 0x5f
-    0xFF, 0x00, 0x00, 0x1F, 0x10, 0x70, 0x4D, 0x06,
-    0x00, 0x07, 0x50, 0x00, 0x5D, 0x0B, 0x3F, 0x7F //  - TX 13dBm
-};
-
-
-enum {CMT_SUCCESS = 0, CMT_ERR_SWITCH_STATE, CMT_ERR_TX_PENDING, CMT_FIFO_EMPTY, CMT_ERR_RX_IN_FIFO};
-
 class Cmt2300a {
     public:
         Cmt2300a() {}
@@ -234,12 +188,12 @@ class Cmt2300a {
             }
         }
 
-        uint8_t goRx(void) {
+        CmtStatus goRx(void) {
             if(mTxPending)
-                return CMT_ERR_TX_PENDING;
+                return CmtStatus::ERR_TX_PENDING;
 
             if(mInRxMode)
-                return CMT_SUCCESS;
+                return CmtStatus::SUCCESS;
 
             mSpi.readReg(CMT2300A_CUS_INT1_CTL);
             mSpi.writeReg(CMT2300A_CUS_INT1_CTL, CMT2300A_INT_SEL_TX_DONE);
@@ -260,47 +214,47 @@ class Cmt2300a {
             mSpi.writeReg(0x16, 0x0C); // [4:3]: RSSI_DET_SEL, [2:0]: RSSI_AVG_MODE
 
             if(!cmtSwitchStatus(CMT2300A_GO_RX, CMT2300A_STA_RX))
-                return CMT_ERR_SWITCH_STATE;
+                return CmtStatus::ERR_SWITCH_STATE;
 
             mInRxMode = true;
 
-            return CMT_SUCCESS;
+            return CmtStatus::SUCCESS;
         }
 
-        uint8_t getRx(uint8_t buf[], uint8_t *rxLen, uint8_t maxlen, int8_t *rssi) {
+        CmtStatus getRx(uint8_t buf[], uint8_t *rxLen, uint8_t maxlen, int8_t *rssi) {
             if(mTxPending)
-                return CMT_ERR_TX_PENDING;
+                return CmtStatus::ERR_TX_PENDING;
 
             if(0x1b != (mSpi.readReg(CMT2300A_CUS_INT_FLAG) & 0x1b))
-                return CMT_FIFO_EMPTY;
+                return CmtStatus::FIFO_EMPTY;
 
             // receive ok (pream, sync, node, crc)
             if(!cmtSwitchStatus(CMT2300A_GO_STBY, CMT2300A_STA_STBY))
-                return CMT_ERR_SWITCH_STATE;
+                return CmtStatus::ERR_SWITCH_STATE;
 
             mSpi.readFifo(buf, rxLen, maxlen);
             *rssi = mSpi.readReg(CMT2300A_CUS_RSSI_DBM) - 128;
 
             if(!cmtSwitchStatus(CMT2300A_GO_SLEEP, CMT2300A_STA_SLEEP))
-                return CMT_ERR_SWITCH_STATE;
+                return CmtStatus::ERR_SWITCH_STATE;
 
             if(!cmtSwitchStatus(CMT2300A_GO_STBY, CMT2300A_STA_STBY))
-                return CMT_ERR_SWITCH_STATE;
+                return CmtStatus::ERR_SWITCH_STATE;
 
             mInRxMode   = false;
             mCusIntFlag = mSpi.readReg(CMT2300A_CUS_INT_FLAG);
 
-            return CMT_SUCCESS;
+            return CmtStatus::SUCCESS;
         }
 
-        uint8_t tx(uint8_t buf[], uint8_t len) {
+        CmtStatus tx(uint8_t buf[], uint8_t len) {
             if(mTxPending)
-                return CMT_ERR_TX_PENDING;
+                return CmtStatus::ERR_TX_PENDING;
 
             if(mInRxMode) {
                 mInRxMode = false;
                 if(!cmtSwitchStatus(CMT2300A_GO_STBY, CMT2300A_STA_STBY))
-                    return CMT_ERR_SWITCH_STATE;
+                    return CmtStatus::ERR_SWITCH_STATE;
             }
 
             mSpi.writeReg(CMT2300A_CUS_INT1_CTL, CMT2300A_INT_SEL_TX_DONE);
@@ -325,16 +279,17 @@ class Cmt2300a {
             }
 
             if(!cmtSwitchStatus(CMT2300A_GO_TX, CMT2300A_STA_TX))
-                return CMT_ERR_SWITCH_STATE;
+                return CmtStatus::ERR_SWITCH_STATE;
 
             // wait for tx done
             mTxPending = true;
 
-            return CMT_SUCCESS;
+            return CmtStatus::SUCCESS;
         }
 
         // initialize CMT2300A, returns true on success
-        bool reset(void) {
+        bool reset(RegionCfg region) {
+            mRegionCfg = region;
             mSpi.writeReg(0x7f, 0xff); // soft reset
             delay(30);
 
@@ -346,10 +301,19 @@ class Cmt2300a {
             if(mSpi.readReg(0x62) != 0x20)
                 return false; // not connected!
 
-            for(uint8_t i = 0; i < 0x60; i++) {
+            for(uint8_t i = 0; i < 0x18; i++) {
+                mSpi.writeReg(i, cmtConfig[i]);
+            }
+            for(uint8_t i = 0; i < 8; i++) {
+                mSpi.writeReg(0x18 + i, mBaseFreqCfg[static_cast<uint8_t>(region)][i]);
+            }
+            for(uint8_t i = 0x20; i < 0x60; i++) {
                 mSpi.writeReg(i, cmtConfig[i]);
             }
 
+            if(RegionCfg::EUROPE != region)
+                mSpi.writeReg(0x27, 0x0B);
+
 
             mSpi.writeReg(CMT2300A_CUS_IO_SEL, 0x20); // -> GPIO3_SEL[1:0] = 0x02
 
@@ -389,23 +353,14 @@ class Cmt2300a {
         }
 
         inline uint8_t freq2Chan(const uint32_t freqKhz) {
-            if((freqKhz % FREQ_STEP_KHZ) != 0) {
-                DPRINT(DBG_WARN, F("switch frequency to "));
-                DBGPRINT(String(freqKhz));
-                DBGPRINT(F("kHz not possible!"));
+            if((freqKhz % FREQ_STEP_KHZ) != 0)
                 return 0xff; // error
-                // apply the nearest frequency
-                //freqKhz = (freqKhz + FREQ_STEP_KHZ/2) / FREQ_STEP_KHZ;
-                //freqKhz *= FREQ_STEP_KHZ;
-            }
 
-            if((freqKhz < HOY_BASE_FREQ_KHZ) || (freqKhz > HOY_MAX_FREQ_KHZ))
+            std::pair<uint8_t, uint8_t> range = getFreqRangeMhz();
+            if((freqKhz < range.first) || (freqKhz > range.second))
                 return 0xff; // error
 
-            if((freqKhz < FREQ_WARN_MIN_KHZ) || (freqKhz > FREQ_WARN_MAX_KHZ))
-                DPRINTLN(DBG_WARN, F("Desired frequency is out of EU legal range! (863 - 870MHz)"));
-
-            return (freqKhz - HOY_BASE_FREQ_KHZ) / FREQ_STEP_KHZ;
+            return (freqKhz - getBaseFreqMhz()) / FREQ_STEP_KHZ;
         }
 
         inline void switchChannel(uint8_t ch) {
@@ -414,9 +369,9 @@ class Cmt2300a {
 
         inline uint32_t getFreqKhz(void) {
             if(0xff != mRqstCh)
-                return HOY_BASE_FREQ_KHZ + (mRqstCh * FREQ_STEP_KHZ);
+                return getBaseFreqMhz() + (mRqstCh * FREQ_STEP_KHZ);
             else
-                return HOY_BASE_FREQ_KHZ + (mCurCh * FREQ_STEP_KHZ);
+                return getBaseFreqMhz() + (mCurCh * FREQ_STEP_KHZ);
         }
 
         uint8_t getCurrentChannel(void) {
@@ -443,6 +398,114 @@ class Cmt2300a {
             mSpi.writeReg(CMT2300A_CUS_TX9, paLevelList[level][1]);
         }
 
+    public:
+        uint16_t getBaseFreqMhz(void) {
+            switch(mRegionCfg) {
+                default:
+                    [[fallthrough]];
+                case RegionCfg::EUROPE:
+                    break;
+                case RegionCfg::USA:
+                    return 905;
+                case RegionCfg::BRAZIL:
+                    return 915;
+            }
+            return 860;
+        }
+
+        uint16_t getBootFreqMhz(void) {
+            switch(mRegionCfg) {
+                default:
+                    [[fallthrough]];
+                case RegionCfg::EUROPE:
+                    break;
+                case RegionCfg::USA:
+                    return 915;
+                case RegionCfg::BRAZIL:
+                    return 915;
+            }
+            return 868;
+        }
+
+        std::pair<uint16_t,uint16_t> getFreqRangeMhz(void) {
+            switch(mRegionCfg) {
+                default:
+                    [[fallthrough]];
+                case RegionCfg::EUROPE:
+                    break;
+                case RegionCfg::USA:
+                    return std::make_pair(905, 925);
+                case RegionCfg::BRAZIL:
+                    return std::make_pair(915, 928);
+            }
+            return std::make_pair(860, 870); // Europe
+        }
+
+    private:
+        // this list and the TX5, TX10 registers were compiled from the output of
+        // HopeRF RFPDK Tool v1.54
+        constexpr static uint8_t paLevelList[31][2] PROGMEM = {
+            {0x17, 0x01}, // -10dBm
+            {0x1a, 0x01}, // -09dBm
+            {0x1d, 0x01}, // -08dBm
+            {0x21, 0x01}, // -07dBm
+            {0x25, 0x01}, // -06dBm
+            {0x29, 0x01}, // -05dBm
+            {0x2d, 0x01}, // -04dBm
+            {0x33, 0x01}, // -03dBm
+            {0x39, 0x02}, // -02dBm
+            {0x41, 0x02}, // -01dBm
+            {0x4b, 0x02}, //  00dBm
+            {0x56, 0x03}, //  01dBm
+            {0x63, 0x03}, //  02dBm
+            {0x71, 0x04}, //  03dBm
+            {0x80, 0x04}, //  04dBm
+            {0x22, 0x01}, //  05dBm
+            {0x27, 0x04}, //  06dBm
+            {0x2c, 0x05}, //  07dBm
+            {0x31, 0x06}, //  08dBm
+            {0x38, 0x06}, //  09dBm
+            {0x3f, 0x07}, //  10dBm
+            {0x48, 0x08}, //  11dBm
+            {0x52, 0x09}, //  12dBm
+            {0x5d, 0x0b}, //  13dBm
+            {0x6a, 0x0c}, //  14dBm
+            {0x79, 0x0d}, //  15dBm
+            {0x46, 0x10}, //  16dBm
+            {0x51, 0x10}, //  17dBm
+            {0x60, 0x12}, //  18dBm
+            {0x71, 0x14}, //  19dBm
+            {0x8c, 0x1c}  //  20dBm
+        };
+
+        // default CMT parameters
+        constexpr static uint8_t cmtConfig[0x60] PROGMEM {
+            // 0x00 - 0x0f -- RSSI offset +- 0 and 13dBm
+            0x00, 0x66, 0xEC, 0x1C, 0x70, 0x80, 0x14, 0x08,
+            0x11, 0x02, 0x02, 0x00, 0xAE, 0xE0, 0x35, 0x00,
+            // 0x10 - 0x1f
+            0x00, 0xF4, 0x10, 0xE2, 0x42, 0x20, 0x0C, 0x81,
+            0x42, 0x32, 0xCF, 0x82, 0x42, 0x27, 0x76, 0x12, // 860MHz as default
+            // 0x20 - 0x2f
+            0xA6, 0xC9, 0x20, 0x20, 0xD2, 0x35, 0x0C, 0x0A,
+            0x9F, 0x4B, 0x29, 0x29, 0xC0, 0x14, 0x05, 0x53,
+            // 0x30 - 0x3f
+            0x10, 0x00, 0xB4, 0x00, 0x00, 0x01, 0x00, 0x00,
+            0x12, 0x1E, 0x00, 0xAA, 0x06, 0x00, 0x00, 0x00,
+            // 0x40 - 0x4f
+            0x00, 0x48, 0x5A, 0x48, 0x4D, 0x01, 0x1F, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x60,
+            // 0x50 - 0x5f
+            0xFF, 0x00, 0x00, 0x1F, 0x10, 0x70, 0x4D, 0x06,
+            0x00, 0x07, 0x50, 0x00, 0x5D, 0x0B, 0x3F, 0x7F // TX 13dBm
+        };
+
+        constexpr static uint8_t mBaseFreqCfg[static_cast<uint8_t>(RegionCfg::NUM)][8] {
+            {0x42, 0x32, 0xCF, 0x82, 0x42, 0x27, 0x76, 0x12}, // 860MHz
+            {0x45, 0xA8, 0x31, 0x8A, 0x45, 0x9D, 0xD8, 0x19}, // 905MHz (USA, Indonesia)
+            {0x46, 0x6D, 0x80, 0x86, 0x46, 0x62, 0x27, 0x16}  // 915MHz (Brazil)
+        };
+
     private:
         void init() {
             mTxPending  = false;
@@ -480,6 +543,7 @@ class Cmt2300a {
             return mSpi.readReg(CMT2300A_CUS_MODE_STA) & CMT2300A_MASK_CHIP_MODE_STA;
         }
 
+    private:
         #if defined(CONFIG_IDF_TARGET_ESP32S3) && defined(SPI_HAL)
         cmtHal mSpi;
         #else
@@ -490,6 +554,7 @@ class Cmt2300a {
         bool mInRxMode;
         uint8_t mCusIntFlag;
         uint8_t mRqstCh, mCurCh;
+        RegionCfg mRegionCfg = RegionCfg::EUROPE;
 };
 
 #endif /*__CMT2300A_H__*/
diff --git a/src/hms/hmsRadio.h b/src/hms/hmsRadio.h
index 2f0a5f31..beea5829 100644
--- a/src/hms/hmsRadio.h
+++ b/src/hms/hmsRadio.h
@@ -17,9 +17,9 @@ class CmtRadio : public Radio {
             mDtuSn = DTU_SN;
         }
 
-        void setup(bool *serialDebug, bool *privacyMode, bool *printWholeTrace, uint8_t pinSclk, uint8_t pinSdio, uint8_t pinCsb, uint8_t pinFcsb, bool genDtuSn = true) {
+        void setup(bool *serialDebug, bool *privacyMode, bool *printWholeTrace, uint8_t pinSclk, uint8_t pinSdio, uint8_t pinCsb, uint8_t pinFcsb, uint8_t region = 0, bool genDtuSn = true) {
             mCmt.setup(pinSclk, pinSdio, pinCsb, pinFcsb);
-            reset(genDtuSn);
+            reset(genDtuSn, static_cast<RegionCfg>(region));
             mPrivacyMode = privacyMode;
             mSerialDebug = serialDebug;
             mPrintWholeTrace = printWholeTrace;
@@ -30,7 +30,7 @@ class CmtRadio : public Radio {
             if((!mIrqRcvd) && (!mRqstGetRx))
                 return false;
             getRx();
-            if(CMT_SUCCESS == mCmt.goRx()) {
+            if(CmtStatus::SUCCESS == mCmt.goRx()) {
                 mIrqRcvd   = false;
                 mRqstGetRx = false;
             }
@@ -76,6 +76,18 @@ class CmtRadio : public Radio {
             return true;
         }
 
+        uint16_t getBaseFreqMhz(void) override {
+            return mCmt.getBaseFreqMhz();
+        }
+
+        uint16_t getBootFreqMhz(void) override {
+            return mCmt.getBootFreqMhz();
+        }
+
+        std::pair<uint16_t,uint16_t> getFreqRangeMhz(void) override {
+            return mCmt.getFreqRangeMhz();
+        }
+
     private:
 
         void sendPacket(Inverter<> *iv, uint8_t len, bool isRetransmit, bool appendCrc16=true) {
@@ -104,12 +116,12 @@ class CmtRadio : public Radio {
                 }
             }
 
-            uint8_t status = mCmt.tx(mTxBuf, len);
+            CmtStatus status = mCmt.tx(mTxBuf, len);
             mMillis = millis();
-            if(CMT_SUCCESS != status) {
+            if(CmtStatus::SUCCESS != status) {
                 DPRINT(DBG_WARN, F("CMT TX failed, code: "));
-                DBGPRINTLN(String(status));
-                if(CMT_ERR_RX_IN_FIFO == status)
+                DBGPRINTLN(String(static_cast<uint8_t>(status)));
+                if(CmtStatus::ERR_RX_IN_FIFO == status)
                     mIrqRcvd = true;
             }
             iv->mDtuTxCnt++;
@@ -123,10 +135,10 @@ class CmtRadio : public Radio {
             return iv->ivGen;
         }
 
-        inline void reset(bool genDtuSn) {
+        inline void reset(bool genDtuSn, RegionCfg region) {
             if(genDtuSn)
                 generateDtuSn();
-            if(!mCmt.reset()) {
+            if(!mCmt.reset(region)) {
                 mCmtAvail = false;
                 DPRINTLN(DBG_WARN, F("Initializing CMT2300A failed!"));
             } else {
@@ -160,8 +172,8 @@ class CmtRadio : public Radio {
         inline void getRx(void) {
             packet_t p;
             p.millis = millis() - mMillis;
-            uint8_t status = mCmt.getRx(p.packet, &p.len, 28, &p.rssi);
-            if(CMT_SUCCESS == status)
+            CmtStatus status = mCmt.getRx(p.packet, &p.len, 28, &p.rssi);
+            if(CmtStatus::SUCCESS == status)
                 mBufCtrl.push(p);
 
             // this code completly stops communication!
diff --git a/src/web/RestApi.h b/src/web/RestApi.h
index 98e0afd2..9ec717c1 100644
--- a/src/web/RestApi.h
+++ b/src/web/RestApi.h
@@ -268,6 +268,8 @@ class RestApi {
             obj[F("menu_protEn")] = (bool) (strlen(mConfig->sys.adminPwd) > 0);
             obj[F("cst_lnk")]     = String(mConfig->plugin.customLink);
             obj[F("cst_lnk_txt")] = String(mConfig->plugin.customLinkText);
+            obj[F("region")]      = mConfig->sys.region;
+            obj[F("timezone")]    = mConfig->sys.timezone;
 
         #if defined(ESP32)
             obj[F("esp_type")]    = F("ESP32");
@@ -651,6 +653,9 @@ class RestApi {
             obj[F("fcsb")]  = mConfig->cmt.pinFcsb;
             obj[F("gpio3")] = mConfig->cmt.pinIrq;
             obj[F("en")]    = (bool) mConfig->cmt.enabled;
+            std::pair<uint16_t, uint16_t> range = mRadioCmt->getFreqRangeMhz();
+            obj[F("freq_min")] = range.first;
+            obj[F("freq_max")] = range.second;
         }
 
         void getRadioCmtInfo(JsonObject obj) {
diff --git a/src/web/html/setup.html b/src/web/html/setup.html
index b4c72069..d4f245ef 100644
--- a/src/web/html/setup.html
+++ b/src/web/html/setup.html
@@ -26,6 +26,14 @@
                                 <div class="col-4 col-sm-9"><input type="checkbox" name="darkMode"/></div>
                                 <div class="col-12">{#DARK_MODE_NOTE}</div>
                             </div>
+                            <div class="row mb-3">
+                                <div class="col-8 col-sm-3">{#REGION}</div>
+                                <div class="col-4 col-sm-9" id="region"></div>
+                            </div>
+                            <div class="row mb-5">
+                                <div class="col-8 col-sm-3">{#TIMEZONE}</div>
+                                <div class="col-4 col-sm-9" id="timezone"></div>
+                            </div>
                             <div class="row mb-3">
                                 <div class="col-8 col-sm-3">{#CUSTOM_LINK}</div>
                                 <div class="col-4 col-sm-9"><input type="text" name="cstLnk"/></div>
@@ -499,9 +507,6 @@
             for(var i = 0; i < 31; i++) {
                 esp32cmtPa.push([i, String(i-10) + " dBm"]);
             }
-            for(var i = 12; i < 41; i++) {
-                esp32cmtFreq.push([i, freqFmt.format(860 + i*0.25) + " MHz"]);
-            }
             /*ENDIF_ESP32*/
             var led_high_active = [
                 [0, "{#PIN_LOW_ACTIVE}"],
@@ -650,6 +655,14 @@
                     el.push(mlCb("protMask" + i, a[i], chk))
                 }
                 d.append(...el);
+
+                var tz = []
+                for(i = 0; i < 24; i += 0.5)
+                    tz.push([i, ((i-12 > 0) ? "+" : "") + String(i-12)]);
+                document.getElementById("timezone").append(sel("timezone", tz, obj.timezone + 12))
+                var region = [[0, "Europe (860 - 870 MHz)"], [1, "USA, Indonesia (905 - 925 MHz)"], [2, "Brazil (915 - 928 MHz)"]]
+                document.getElementById("region").append(sel("region", region, obj.region))
+
             }
 
             function parseGeneric(obj) {
@@ -1019,6 +1032,12 @@
                         ])
                     );
                 }
+
+                var i = 0
+                while(obj.freq_max >= (obj.freq_min + i * 0.25)) {
+                    esp32cmtFreq.push([i, freqFmt.format(obj.freq_min + i * 0.25) + " MHz"])
+                    i++
+                }
             }
             /*ENDIF_ESP32*/
 
diff --git a/src/web/lang.json b/src/web/lang.json
index 9f3df46e..d083ddff 100644
--- a/src/web/lang.json
+++ b/src/web/lang.json
@@ -148,6 +148,16 @@
                     "en": "(empty browser cache or use CTRL + F5 after reboot to apply this setting)",
                     "de": "(der Browser-Cache muss geleert oder STRG + F5 gedr&uuml;ckt werden, um diese Einstellung zu aktivieren)"
                 },
+                {
+                    "token": "REGION",
+                    "en": "Region",
+                    "de": "Region"
+                },
+                {
+                    "token": "TIMEZONE",
+                    "en": "Timezone",
+                    "de": "Zeitzone"
+                },
                 {
                     "token": "CUSTOM_LINK",
                     "en": "Custom link (leave empty to hide element in navigation)",
diff --git a/src/web/web.h b/src/web/web.h
index ba4540b1..6de9c877 100644
--- a/src/web/web.h
+++ b/src/web/web.h
@@ -481,7 +481,8 @@ class Web {
                 request->arg("device").toCharArray(mConfig->sys.deviceName, DEVNAME_LEN);
             mConfig->sys.darkMode = (request->arg("darkMode") == "on");
             mConfig->sys.schedReboot = (request->arg("schedReboot") == "on");
-
+            mConfig->sys.region = (request->arg("region")).toInt();
+            mConfig->sys.timezone = (request->arg("timezone")).toInt() - 12;
 
             if (request->arg("cstLnk") != "") {
                 request->arg("cstLnk").toCharArray(mConfig->plugin.customLink, MAX_CUSTOM_LINK_LEN);

From 7655abc805e2ef7aaf8f95f423fea7f607f49269 Mon Sep 17 00:00:00 2001
From: lumapu <lp@lufami.de>
Date: Sun, 28 Jan 2024 23:39:04 +0100
Subject: [PATCH 3/5] 0.8.66 fix compile

---
 src/hm/Communication.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/hm/Communication.h b/src/hm/Communication.h
index ae97dbb2..132faeb8 100644
--- a/src/hm/Communication.h
+++ b/src/hm/Communication.h
@@ -138,8 +138,10 @@ class Communication : public CommQueue<> {
                         }
                         if(!q->iv->mGotFragment) {
                             if(INV_RADIO_TYPE_CMT == q->iv->ivRadioType) {
+                                #if defined(ESP32)
                                 q->iv->radio->switchFrequency(q->iv, q->iv->radio->getBootFreqMhz() * 1000, (q->iv->config->frequency*FREQ_STEP_KHZ + q->iv->radio->getBaseFreqMhz() * 1000));
                                 mWaitTime.startTimeMonitor(1000);
+                                #endif
                             } else {
                                 if(IV_MI == q->iv->ivGen)
                                     q->iv->mIvTxCnt++;

From 6b305651affcb4753699b935ff8c3c1eb32d2622 Mon Sep 17 00:00:00 2001
From: lumapu <lp@lufami.de>
Date: Mon, 29 Jan 2024 09:28:17 +0100
Subject: [PATCH 4/5] 0.8.67 * fix HMS frequency * fix display of inverter id
 in serial log (was displayed twice)

---
 src/CHANGES.md         | 6 +++++-
 src/defines.h          | 2 +-
 src/hm/Communication.h | 1 -
 src/hms/cmt2300a.h     | 6 +++---
 4 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/src/CHANGES.md b/src/CHANGES.md
index 7f544e69..651d068d 100644
--- a/src/CHANGES.md
+++ b/src/CHANGES.md
@@ -1,5 +1,9 @@
 # Development Changes
 
+## 0.8.67 - 2024-01-29
+* fix HMS frequency
+* fix display of inverter id in serial log (was displayed twice)
+
 ## 0.8.66 - 2024-01-28
 * added support for other regions - untested #1271
 * fix generation of DTU-ID; was computed twice without reset if two radios are enabled
@@ -169,7 +173,7 @@
 
 ## 0.8.37 - 2023-12-30
 * added grid profiles
-* format version of grid profile 
+* format version of grid profile
 
 # RELEASE 0.8.36 - 2023-12-30
 
diff --git a/src/defines.h b/src/defines.h
index 786908d0..4ff2fc1e 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -13,7 +13,7 @@
 //-------------------------------------
 #define VERSION_MAJOR       0
 #define VERSION_MINOR       8
-#define VERSION_PATCH       66
+#define VERSION_PATCH       67
 
 //-------------------------------------
 typedef struct {
diff --git a/src/hm/Communication.h b/src/hm/Communication.h
index 132faeb8..396a98a5 100644
--- a/src/hm/Communication.h
+++ b/src/hm/Communication.h
@@ -502,7 +502,6 @@ class Communication : public CommQueue<> {
             int8_t rssi = -127;
             uint8_t len = 0;
 
-            DPRINT_IVID(DBG_INFO, q->iv->id);
             for(uint8_t i = 0; i < mMaxFrameId; i++) {
                 if(mLocalBuf[i].len + len > MAX_BUFFER) {
                     DPRINTLN(DBG_ERROR, F("payload buffer to small!"));
diff --git a/src/hms/cmt2300a.h b/src/hms/cmt2300a.h
index 524472f0..3cdc2660 100644
--- a/src/hms/cmt2300a.h
+++ b/src/hms/cmt2300a.h
@@ -360,7 +360,7 @@ class Cmt2300a {
             if((freqKhz < range.first) || (freqKhz > range.second))
                 return 0xff; // error
 
-            return (freqKhz - getBaseFreqMhz()) / FREQ_STEP_KHZ;
+            return (freqKhz - getBaseFreqMhz() * 1000) / FREQ_STEP_KHZ;
         }
 
         inline void switchChannel(uint8_t ch) {
@@ -369,9 +369,9 @@ class Cmt2300a {
 
         inline uint32_t getFreqKhz(void) {
             if(0xff != mRqstCh)
-                return getBaseFreqMhz() + (mRqstCh * FREQ_STEP_KHZ);
+                return getBaseFreqMhz() * 1000 + (mRqstCh * FREQ_STEP_KHZ);
             else
-                return getBaseFreqMhz() + (mCurCh * FREQ_STEP_KHZ);
+                return getBaseFreqMhz() * 1000 + (mCurCh * FREQ_STEP_KHZ);
         }
 
         uint8_t getCurrentChannel(void) {

From 163a9e485ef7f01cd85548d41aeb575fbc15146b Mon Sep 17 00:00:00 2001
From: lumapu <lp@lufami.de>
Date: Mon, 29 Jan 2024 21:51:49 +0100
Subject: [PATCH 5/5] 0.8.68 * fix HMS / HMT startup * added `flush_rx` to NRF
 on TX * start with heuristics set to `0` * added warning for WiFi channel
 12-14 (ESP8266 only) #1381

---
 src/CHANGES.md         |  6 ++++++
 src/app.h              |  4 ++++
 src/appInterface.h     |  1 +
 src/defines.h          |  2 +-
 src/hm/Communication.h |  5 ++++-
 src/hm/HeuristicInv.h  | 19 +++++++++++++++++--
 src/hm/hmInverter.h    |  9 +++++++++
 src/hm/hmRadio.h       | 26 ++++++++++++++------------
 src/hms/cmt2300a.h     |  6 +++---
 src/web/RestApi.h      |  6 ++++++
 src/web/lang.h         |  6 ++++++
 src/wifi/ahoywifi.cpp  |  2 ++
 src/wifi/ahoywifi.h    |  5 +++++
 13 files changed, 78 insertions(+), 19 deletions(-)

diff --git a/src/CHANGES.md b/src/CHANGES.md
index 651d068d..f690b192 100644
--- a/src/CHANGES.md
+++ b/src/CHANGES.md
@@ -1,5 +1,11 @@
 # Development Changes
 
+## 0.8.68 - 2024-01-29
+* fix HMS / HMT startup
+* added `flush_rx` to NRF on TX
+* start with heuristics set to `0`
+* added warning for WiFi channel 12-14 (ESP8266 only) #1381
+
 ## 0.8.67 - 2024-01-29
 * fix HMS frequency
 * fix display of inverter id in serial log (was displayed twice)
diff --git a/src/app.h b/src/app.h
index 851ad3c8..ccf46297 100644
--- a/src/app.h
+++ b/src/app.h
@@ -182,6 +182,10 @@ class app : public IApp, public ah::Scheduler {
             return mWifi.getStationIp();
         }
 
+        bool getWasInCh12to14(void) const {
+            return mWifi.getWasInCh12to14();
+        }
+
         #endif /* !defined(ETHERNET) */
 
         void setRebootFlag() {
diff --git a/src/appInterface.h b/src/appInterface.h
index ad38f756..f98fb0cb 100644
--- a/src/appInterface.h
+++ b/src/appInterface.h
@@ -35,6 +35,7 @@ class IApp {
         virtual void setupStation(void) = 0;
         virtual void setStopApAllowedMode(bool allowed) = 0;
         virtual String getStationIp(void) = 0;
+        virtual bool getWasInCh12to14(void) const = 0;
         #endif /* defined(ETHERNET) */
 
         virtual uint32_t getUptime() = 0;
diff --git a/src/defines.h b/src/defines.h
index 4ff2fc1e..442008ff 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -13,7 +13,7 @@
 //-------------------------------------
 #define VERSION_MAJOR       0
 #define VERSION_MINOR       8
-#define VERSION_PATCH       67
+#define VERSION_PATCH       68
 
 //-------------------------------------
 typedef struct {
diff --git a/src/hm/Communication.h b/src/hm/Communication.h
index 396a98a5..b74f689d 100644
--- a/src/hm/Communication.h
+++ b/src/hm/Communication.h
@@ -139,7 +139,10 @@ class Communication : public CommQueue<> {
                         if(!q->iv->mGotFragment) {
                             if(INV_RADIO_TYPE_CMT == q->iv->ivRadioType) {
                                 #if defined(ESP32)
-                                q->iv->radio->switchFrequency(q->iv, q->iv->radio->getBootFreqMhz() * 1000, (q->iv->config->frequency*FREQ_STEP_KHZ + q->iv->radio->getBaseFreqMhz() * 1000));
+                                if(!q->iv->radio->switchFrequency(q->iv, q->iv->radio->getBootFreqMhz() * 1000, (q->iv->config->frequency*FREQ_STEP_KHZ + q->iv->radio->getBaseFreqMhz() * 1000))) {
+                                    DPRINT_IVID(DBG_INFO, q->iv->id);
+                                    DBGPRINTLN(F("switch frequency failed!"));
+                                }
                                 mWaitTime.startTimeMonitor(1000);
                                 #endif
                             } else {
diff --git a/src/hm/HeuristicInv.h b/src/hm/HeuristicInv.h
index e7ad6edd..0391e758 100644
--- a/src/hm/HeuristicInv.h
+++ b/src/hm/HeuristicInv.h
@@ -1,5 +1,5 @@
 //-----------------------------------------------------------------------------
-// 2023 Ahoy, https://github.com/lumpapu/ahoy
+// 2024 Ahoy, https://github.com/lumpapu/ahoy
 // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/4.0/deed
 //-----------------------------------------------------------------------------
 
@@ -14,7 +14,22 @@
 class HeuristicInv {
     public:
         HeuristicInv() {
-            memset(txRfQuality, -6, RF_MAX_CHANNEL_ID);
+            clear();
+        }
+
+        void clear() {
+            memset(txRfQuality, 0, RF_MAX_CHANNEL_ID);
+            txRfChId           = 0;
+            lastBestTxChId     = 0;
+            testPeriodSendCnt  = 0;
+            testPeriodFailCnt  = 0;
+            testChId           = 0;
+            saveOldTestQuality = -6;
+            lastRxFragments    = 0;
+        }
+
+        bool isTxAtMax(void) const {
+            return (RF_MAX_QUALITY == txRfQuality[txRfChId]);
         }
 
     public:
diff --git a/src/hm/hmInverter.h b/src/hm/hmInverter.h
index cc73cb0b..f63fa688 100644
--- a/src/hm/hmInverter.h
+++ b/src/hm/hmInverter.h
@@ -192,6 +192,14 @@ class Inverter {
                 if(mNextLive)
                     cb(RealTimeRunData_Debug, false);    // get live data
                 else {
+                    if(INV_RADIO_TYPE_NRF == ivRadioType) {
+                        // get live data until quility reaches maximum
+                        if(!heuristics.isTxAtMax()) {
+                            cb(RealTimeRunData_Debug, false);    // get live data
+                            return;
+                        }
+                    }
+
                     if(actPowerLimit == 0xffff)
                         cb(SystemConfigPara, false);         // power limit info
                     else if(InitDataState != devControlCmd) {
@@ -449,6 +457,7 @@ class Inverter {
                     status = InverterStatus::OFF;
                     actPowerLimit = 0xffff; // power limit will be read once inverter becomes available
                     alarmMesIndex = 0;
+                    heuristics.clear();
                 }
                 else
                     status = InverterStatus::WAS_ON;
diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h
index d73b2b0e..5730c492 100644
--- a/src/hm/hmRadio.h
+++ b/src/hm/hmRadio.h
@@ -77,7 +77,7 @@ class HmRadio : public Radio {
             #else
                 mNrf24->begin(mSpi.get(), ce, cs);
             #endif
-            mNrf24->setRetries(3, 15); // wait 3*250 = 750us, 16 * 250us -> 4000us = 4ms
+            mNrf24->setRetries(3, 9); // wait 3*250 = 750us, 16 * 250us -> 4000us = 4ms
 
             mNrf24->setDataRate(RF24_250KBPS);
             //mNrf24->setAutoAck(true); // enabled by default
@@ -120,14 +120,14 @@ class HmRadio : public Radio {
                 if(!mNRFloopChannels && ((mTimeslotStart - mLastIrqTime) > (DURATION_TXFRAME+DURATION_ONEFRAME)))
                     mNRFloopChannels = true;
 
-                rxPendular = !rxPendular;
-                //innerLoopTimeout = (rxPendular ? 1 : 2)*DURATION_LISTEN_MIN;
+                mRxPendular = !mRxPendular;
+                //innerLoopTimeout = (mRxPendular ? 1 : 2)*DURATION_LISTEN_MIN;
                 innerLoopTimeout = DURATION_LISTEN_MIN;
 
                 if(mNRFloopChannels)
                     tempRxChIdx = (tempRxChIdx + 4) % RF_CHANNELS;
                 else
-                    tempRxChIdx = (mRxChIdx + rxPendular*4) % RF_CHANNELS;
+                    tempRxChIdx = (mRxChIdx + mRxPendular*4) % RF_CHANNELS;
 
                 mNrf24->setChannel(mRfChLst[tempRxChIdx]);
                 isRxInit = false;
@@ -141,7 +141,7 @@ class HmRadio : public Radio {
 
                 if(tx_ok || tx_fail) { // tx related interrupt, basically we should start listening
                     mNrf24->flush_tx();                         // empty TX FIFO
-                    mTxSetupTime = millis() - mMillis;
+                    //mTxSetupTime = millis() - mMillis;
 
                     if(mNRFisInRX) {
                         DPRINTLN(DBG_WARN, F("unexpected tx irq!"));
@@ -157,7 +157,7 @@ class HmRadio : public Radio {
                     mNrf24->startListening();
                     mTimeslotStart = millis();
                     tempRxChIdx = mRxChIdx;
-                    rxPendular  = false;
+                    mRxPendular = false;
                     mNRFloopChannels = (mLastIv->ivGen == IV_MI);
 
                     innerLoopTimeout = DURATION_TXFRAME;
@@ -168,11 +168,12 @@ class HmRadio : public Radio {
                         mNRFisInRX = false;
                         mRadioWaitTime.startTimeMonitor(DURATION_PAUSE_LASTFR); // let the inverter first end his transmissions
                         mNrf24->stopListening();
+                        return false;
                     } else {
                         innerLoopTimeout = DURATION_LISTEN_MIN;
                         mTimeslotStart = millis();
                         if (!mNRFloopChannels) {
-                            //rxPendular = true; // stay longer on the next rx channel
+                            //mRxPendular = true; // stay longer on the next rx channel
                             if (isRxInit) {
                                 isRxInit = false;
                                 tempRxChIdx = (mRxChIdx + 4) % RF_CHANNELS;
@@ -180,8 +181,8 @@ class HmRadio : public Radio {
                             } else
                                 mRxChIdx = tempRxChIdx;
                         }
+                        return true;
                     }
-                    return mNRFisInRX;
                 }
             }
 
@@ -341,11 +342,11 @@ class HmRadio : public Radio {
             mTxChIdx = iv->heuristics.txRfChId;
 
             if(*mSerialDebug) {
-                if(!isRetransmit) {
+                /*if(!isRetransmit) {
                     DPRINT(DBG_INFO, "last tx setup: ");
                     DBGPRINT(String(mTxSetupTime));
                     DBGPRINTLN("ms");
-                }
+                }*/
 
                 DPRINT_IVID(DBG_INFO, iv->id);
                 DBGPRINT(F("TX "));
@@ -368,6 +369,7 @@ class HmRadio : public Radio {
             }
 
             mNrf24->stopListening();
+            mNrf24->flush_rx();
             mNrf24->setChannel(mRfChLst[mTxChIdx]);
             mNrf24->openWritingPipe(reinterpret_cast<uint8_t*>(&iv->radioId.u64));
             mNrf24->startWrite(mTxBuf, len, false); // false = request ACK response
@@ -407,9 +409,9 @@ class HmRadio : public Radio {
         bool mNRFloopChannels = false;
         bool mNRFisInRX = false;
         bool isRxInit = true;
-        bool rxPendular = false;
+        bool mRxPendular = false;
         uint32_t innerLoopTimeout = DURATION_LISTEN_MIN;
-        uint8_t mTxSetupTime = 0;
+        //uint8_t mTxSetupTime = 0;
 
         std::unique_ptr<SPIClass> mSpi;
         std::unique_ptr<RF24> mNrf24;
diff --git a/src/hms/cmt2300a.h b/src/hms/cmt2300a.h
index 3cdc2660..e6456f5b 100644
--- a/src/hms/cmt2300a.h
+++ b/src/hms/cmt2300a.h
@@ -356,11 +356,11 @@ class Cmt2300a {
             if((freqKhz % FREQ_STEP_KHZ) != 0)
                 return 0xff; // error
 
-            std::pair<uint8_t, uint8_t> range = getFreqRangeMhz();
-            if((freqKhz < range.first) || (freqKhz > range.second))
+            std::pair<uint16_t, uint16_t> range = getFreqRangeMhz();
+            if((freqKhz < (range.first * 1000)) || (freqKhz > (range.second * 1000)))
                 return 0xff; // error
 
-            return (freqKhz - getBaseFreqMhz() * 1000) / FREQ_STEP_KHZ;
+            return (freqKhz - (getBaseFreqMhz() * 1000)) / FREQ_STEP_KHZ;
         }
 
         inline void switchChannel(uint8_t ch) {
diff --git a/src/web/RestApi.h b/src/web/RestApi.h
index 9ec717c1..ccdc8747 100644
--- a/src/web/RestApi.h
+++ b/src/web/RestApi.h
@@ -754,6 +754,12 @@ class RestApi {
                 warn.add(F(REBOOT_ESP_APPLY_CHANGES));
             if(0 == mApp->getTimestamp())
                 warn.add(F(TIME_NOT_SET));
+            #if !defined(ETHERNET)
+                #if !defined(ESP32)
+                if(mApp->getWasInCh12to14())
+                    warn.add(F(WAS_IN_CH_12_TO_14));
+                #endif
+            #endif
         }
 
         void getSetup(AsyncWebServerRequest *request, JsonObject obj) {
diff --git a/src/web/lang.h b/src/web/lang.h
index 546399aa..e55493eb 100644
--- a/src/web/lang.h
+++ b/src/web/lang.h
@@ -18,6 +18,12 @@
     #define TIME_NOT_SET "time not set. No communication to inverter possible"
 #endif
 
+#ifdef LANG_DE
+    #define WAS_IN_CH_12_TO_14 "Der ESP war in WLAN Kanal 12 bis 14, was uU. zu Abstürzen führt"
+#else /*LANG_EN*/
+    #define WAS_IN_CH_12_TO_14 "Your ESP was in wifi channel 12 to 14. It may cause reboots of your AhoyDTU"
+#endif
+
 #ifdef LANG_DE
     #define INV_INDEX_INVALID "Wechselrichterindex ungültig; "
 #else /*LANG_EN*/
diff --git a/src/wifi/ahoywifi.cpp b/src/wifi/ahoywifi.cpp
index defca5bc..9bcbdadc 100644
--- a/src/wifi/ahoywifi.cpp
+++ b/src/wifi/ahoywifi.cpp
@@ -92,6 +92,8 @@ void ahoywifi::tickWifiLoop() {
             }
             #if !defined(ESP32)
             MDNS.update();
+            if(WiFi.channel() > 11)
+                mWasInCh12to14 = true;
             #endif
             return;
         case IN_AP_MODE:
diff --git a/src/wifi/ahoywifi.h b/src/wifi/ahoywifi.h
index 7fb62a20..709c08c4 100644
--- a/src/wifi/ahoywifi.h
+++ b/src/wifi/ahoywifi.h
@@ -37,6 +37,10 @@ class ahoywifi {
         }
         void setupStation(void);
 
+        bool getWasInCh12to14() const {
+            return mWasInCh12to14;
+        }
+
     private:
         typedef enum WiFiStatus {
             DISCONNECTED = 0,
@@ -86,6 +90,7 @@ class ahoywifi {
         bool mGotDisconnect;
         std::list<uint8_t> mBSSIDList;
         bool mStopApAllowed;
+        bool mWasInCh12to14 = false;
 };
 
 #endif /*__AHOYWIFI_H__*/