Browse Source

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
pull/1394/head^2
lumapu 1 year ago
parent
commit
dc696d727f
  1. 4
      src/CHANGES.md
  2. 2
      src/app.cpp
  3. 14
      src/config/settings.h
  4. 2
      src/defines.h
  5. 2
      src/hm/Communication.h
  6. 8
      src/hm/hmDefines.h
  7. 2
      src/hm/hmInverter.h
  8. 7
      src/hm/radio.h
  9. 259
      src/hms/cmt2300a.h
  10. 34
      src/hms/hmsRadio.h
  11. 5
      src/web/RestApi.h
  12. 25
      src/web/html/setup.html
  13. 10
      src/web/lang.json
  14. 3
      src/web/web.h

4
src/CHANGES.md

@ -1,5 +1,9 @@
# Development Changes # 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 ## 0.8.65 - 2024-01-24
* removed patch for NRF `PLOS` * removed patch for NRF `PLOS`
* fix lang issues #1388 * fix lang issues #1388

2
src/app.cpp

@ -47,7 +47,7 @@ void app::setup() {
} }
#if defined(ESP32) #if defined(ESP32)
if(mConfig->cmt.enabled) { 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 #endif
#ifdef ETHERNET #ifdef ETHERNET

14
src/config/settings.h

@ -30,7 +30,7 @@
* https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#flash-layout * https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#flash-layout
* */ * */
#define CONFIG_VERSION 9 #define CONFIG_VERSION 10
#define PROT_MASK_INDEX 0x0001 #define PROT_MASK_INDEX 0x0001
@ -68,6 +68,8 @@ typedef struct {
uint16_t protectionMask; uint16_t protectionMask;
bool darkMode; bool darkMode;
bool schedReboot; bool schedReboot;
uint8_t region;
int8_t timezone;
#if !defined(ETHERNET) #if !defined(ETHERNET)
// wifi // wifi
@ -395,6 +397,8 @@ class settings {
#endif /* !defined(ETHERNET) */ #endif /* !defined(ETHERNET) */
snprintf(mCfg.sys.deviceName, DEVNAME_LEN, DEF_DEVICE_NAME); 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.pinCs = DEF_NRF_CS_PIN;
mCfg.nrf.pinCe = DEF_NRF_CE_PIN; mCfg.nrf.pinCe = DEF_NRF_CE_PIN;
@ -512,6 +516,10 @@ class settings {
if(mCfg.configVersion < 9) { if(mCfg.configVersion < 9) {
mCfg.inst.gapMs = 1; 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("prot_mask")] = mCfg.sys.protectionMask;
obj[F("dark")] = mCfg.sys.darkMode; obj[F("dark")] = mCfg.sys.darkMode;
obj[F("reb")] = (bool) mCfg.sys.schedReboot; 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.ip, buf); obj[F("ip")] = String(buf);
ah::ip2Char(mCfg.sys.ip.mask, buf); obj[F("mask")] = 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); 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<uint16_t>(obj, F("prot_mask"), &mCfg.sys.protectionMask);
getVal<bool>(obj, F("dark"), &mCfg.sys.darkMode); getVal<bool>(obj, F("dark"), &mCfg.sys.darkMode);
getVal<bool>(obj, F("reb"), &mCfg.sys.schedReboot); 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("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("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*>()); if(obj.containsKey(F("dns1"))) ah::ip2Arr(mCfg.sys.ip.dns1, obj[F("dns1")].as<const char*>());

2
src/defines.h

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

2
src/hm/Communication.h

@ -138,7 +138,7 @@ class Communication : public CommQueue<> {
} }
if(!q->iv->mGotFragment) { if(!q->iv->mGotFragment) {
if(INV_RADIO_TYPE_CMT == q->iv->ivRadioType) { 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); mWaitTime.startTimeMonitor(1000);
} else { } else {
if(IV_MI == q->iv->ivGen) if(IV_MI == q->iv->ivGen)

8
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_TYPE_1CH = 0, INV_TYPE_2CH, INV_TYPE_4CH, INV_TYPE_6CH};
enum {INV_RADIO_TYPE_NRF = 0, INV_RADIO_TYPE_CMT}; 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_ONEFRAME 50 // timeout parameter for each expected frame (ms)
//#define DURATION_RESERVE {90,120} // timeout parameter to still wait after last expected frame (ms) //#define DURATION_RESERVE {90,120} // timeout parameter to still wait after last expected frame (ms)

2
src/hm/hmInverter.h

@ -179,8 +179,6 @@ class Inverter {
tsMaxAcPower = 0; tsMaxAcPower = 0;
memset(&radioStatistics, 0, sizeof(statistics_t)); memset(&radioStatistics, 0, sizeof(statistics_t));
memset(heuristics.txRfQuality, -6, 5);
memset(mOffYD, 0, sizeof(float) * 6); memset(mOffYD, 0, sizeof(float) * 6);
memset(mLastYD, 0, sizeof(float) * 6); memset(mLastYD, 0, sizeof(float) * 6);
} }

7
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 // 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 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 switchFrequencyCh(Inverter<> *iv, uint8_t fromCh, uint8_t toCh) { return true; }
virtual bool isChipConnected(void) const { return false; } 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; virtual bool loop(void) = 0;
void handleIntr(void) { void handleIntr(void) {
@ -113,6 +116,7 @@ class Radio {
chipID = ESP.getChipId(); chipID = ESP.getChipId();
#endif #endif
mDtuSn = 0;
uint8_t t; uint8_t t;
for(int i = 0; i < (7 << 2); i += 4) { for(int i = 0; i < (7 << 2); i += 4) {
t = (chipID >> i) & 0x0f; t = (chipID >> i) & 0x0f;
@ -123,6 +127,7 @@ class Radio {
mDtuSn |= 0x80000000; // the first digit is an 8 for DTU production year 2022, the rest is filled with the ESP chipID in decimal 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; uint32_t mDtuSn;
volatile bool mIrqRcvd; volatile bool mIrqRcvd;
bool *mSerialDebug, *mPrivacyMode, *mPrintWholeTrace; bool *mSerialDebug, *mPrivacyMode, *mPrintWholeTrace;

259
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 // Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -12,8 +12,23 @@
#include "esp32_3wSpi.h" #include "esp32_3wSpi.h"
#endif #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_CFG_RETAIN 0x10
#define CMT2300A_MASK_RSTN_IN_EN 0x20 #define CMT2300A_MASK_RSTN_IN_EN 0x20
#define CMT2300A_MASK_LOCKING_EN 0x20 #define CMT2300A_MASK_LOCKING_EN 0x20
@ -152,67 +167,6 @@
#define CMT2300A_MASK_TX_DONE_FLG 0x08 #define CMT2300A_MASK_TX_DONE_FLG 0x08
#define CMT2300A_MASK_PKT_OK_FLG 0x01 #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 { class Cmt2300a {
public: public:
Cmt2300a() {} Cmt2300a() {}
@ -234,12 +188,12 @@ class Cmt2300a {
} }
} }
uint8_t goRx(void) { CmtStatus goRx(void) {
if(mTxPending) if(mTxPending)
return CMT_ERR_TX_PENDING; return CmtStatus::ERR_TX_PENDING;
if(mInRxMode) if(mInRxMode)
return CMT_SUCCESS; return CmtStatus::SUCCESS;
mSpi.readReg(CMT2300A_CUS_INT1_CTL); mSpi.readReg(CMT2300A_CUS_INT1_CTL);
mSpi.writeReg(CMT2300A_CUS_INT1_CTL, CMT2300A_INT_SEL_TX_DONE); 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 mSpi.writeReg(0x16, 0x0C); // [4:3]: RSSI_DET_SEL, [2:0]: RSSI_AVG_MODE
if(!cmtSwitchStatus(CMT2300A_GO_RX, CMT2300A_STA_RX)) if(!cmtSwitchStatus(CMT2300A_GO_RX, CMT2300A_STA_RX))
return CMT_ERR_SWITCH_STATE; return CmtStatus::ERR_SWITCH_STATE;
mInRxMode = true; 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) if(mTxPending)
return CMT_ERR_TX_PENDING; return CmtStatus::ERR_TX_PENDING;
if(0x1b != (mSpi.readReg(CMT2300A_CUS_INT_FLAG) & 0x1b)) if(0x1b != (mSpi.readReg(CMT2300A_CUS_INT_FLAG) & 0x1b))
return CMT_FIFO_EMPTY; return CmtStatus::FIFO_EMPTY;
// receive ok (pream, sync, node, crc) // receive ok (pream, sync, node, crc)
if(!cmtSwitchStatus(CMT2300A_GO_STBY, CMT2300A_STA_STBY)) if(!cmtSwitchStatus(CMT2300A_GO_STBY, CMT2300A_STA_STBY))
return CMT_ERR_SWITCH_STATE; return CmtStatus::ERR_SWITCH_STATE;
mSpi.readFifo(buf, rxLen, maxlen); mSpi.readFifo(buf, rxLen, maxlen);
*rssi = mSpi.readReg(CMT2300A_CUS_RSSI_DBM) - 128; *rssi = mSpi.readReg(CMT2300A_CUS_RSSI_DBM) - 128;
if(!cmtSwitchStatus(CMT2300A_GO_SLEEP, CMT2300A_STA_SLEEP)) 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)) if(!cmtSwitchStatus(CMT2300A_GO_STBY, CMT2300A_STA_STBY))
return CMT_ERR_SWITCH_STATE; return CmtStatus::ERR_SWITCH_STATE;
mInRxMode = false; mInRxMode = false;
mCusIntFlag = mSpi.readReg(CMT2300A_CUS_INT_FLAG); 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) if(mTxPending)
return CMT_ERR_TX_PENDING; return CmtStatus::ERR_TX_PENDING;
if(mInRxMode) { if(mInRxMode) {
mInRxMode = false; mInRxMode = false;
if(!cmtSwitchStatus(CMT2300A_GO_STBY, CMT2300A_STA_STBY)) 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); 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)) if(!cmtSwitchStatus(CMT2300A_GO_TX, CMT2300A_STA_TX))
return CMT_ERR_SWITCH_STATE; return CmtStatus::ERR_SWITCH_STATE;
// wait for tx done // wait for tx done
mTxPending = true; mTxPending = true;
return CMT_SUCCESS; return CmtStatus::SUCCESS;
} }
// initialize CMT2300A, returns true on success // initialize CMT2300A, returns true on success
bool reset(void) { bool reset(RegionCfg region) {
mRegionCfg = region;
mSpi.writeReg(0x7f, 0xff); // soft reset mSpi.writeReg(0x7f, 0xff); // soft reset
delay(30); delay(30);
@ -346,10 +301,19 @@ class Cmt2300a {
if(mSpi.readReg(0x62) != 0x20) if(mSpi.readReg(0x62) != 0x20)
return false; // not connected! 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]); 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 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) { inline uint8_t freq2Chan(const uint32_t freqKhz) {
if((freqKhz % FREQ_STEP_KHZ) != 0) { if((freqKhz % FREQ_STEP_KHZ) != 0)
DPRINT(DBG_WARN, F("switch frequency to "));
DBGPRINT(String(freqKhz));
DBGPRINT(F("kHz not possible!"));
return 0xff; // error 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 return 0xff; // error
if((freqKhz < FREQ_WARN_MIN_KHZ) || (freqKhz > FREQ_WARN_MAX_KHZ)) return (freqKhz - getBaseFreqMhz()) / FREQ_STEP_KHZ;
DPRINTLN(DBG_WARN, F("Desired frequency is out of EU legal range! (863 - 870MHz)"));
return (freqKhz - HOY_BASE_FREQ_KHZ) / FREQ_STEP_KHZ;
} }
inline void switchChannel(uint8_t ch) { inline void switchChannel(uint8_t ch) {
@ -414,9 +369,9 @@ class Cmt2300a {
inline uint32_t getFreqKhz(void) { inline uint32_t getFreqKhz(void) {
if(0xff != mRqstCh) if(0xff != mRqstCh)
return HOY_BASE_FREQ_KHZ + (mRqstCh * FREQ_STEP_KHZ); return getBaseFreqMhz() + (mRqstCh * FREQ_STEP_KHZ);
else else
return HOY_BASE_FREQ_KHZ + (mCurCh * FREQ_STEP_KHZ); return getBaseFreqMhz() + (mCurCh * FREQ_STEP_KHZ);
} }
uint8_t getCurrentChannel(void) { uint8_t getCurrentChannel(void) {
@ -443,6 +398,114 @@ class Cmt2300a {
mSpi.writeReg(CMT2300A_CUS_TX9, paLevelList[level][1]); 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: private:
void init() { void init() {
mTxPending = false; mTxPending = false;
@ -480,6 +543,7 @@ class Cmt2300a {
return mSpi.readReg(CMT2300A_CUS_MODE_STA) & CMT2300A_MASK_CHIP_MODE_STA; return mSpi.readReg(CMT2300A_CUS_MODE_STA) & CMT2300A_MASK_CHIP_MODE_STA;
} }
private:
#if defined(CONFIG_IDF_TARGET_ESP32S3) && defined(SPI_HAL) #if defined(CONFIG_IDF_TARGET_ESP32S3) && defined(SPI_HAL)
cmtHal mSpi; cmtHal mSpi;
#else #else
@ -490,6 +554,7 @@ class Cmt2300a {
bool mInRxMode; bool mInRxMode;
uint8_t mCusIntFlag; uint8_t mCusIntFlag;
uint8_t mRqstCh, mCurCh; uint8_t mRqstCh, mCurCh;
RegionCfg mRegionCfg = RegionCfg::EUROPE;
}; };
#endif /*__CMT2300A_H__*/ #endif /*__CMT2300A_H__*/

34
src/hms/hmsRadio.h

@ -17,9 +17,9 @@ class CmtRadio : public Radio {
mDtuSn = DTU_SN; 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); mCmt.setup(pinSclk, pinSdio, pinCsb, pinFcsb);
reset(genDtuSn); reset(genDtuSn, static_cast<RegionCfg>(region));
mPrivacyMode = privacyMode; mPrivacyMode = privacyMode;
mSerialDebug = serialDebug; mSerialDebug = serialDebug;
mPrintWholeTrace = printWholeTrace; mPrintWholeTrace = printWholeTrace;
@ -30,7 +30,7 @@ class CmtRadio : public Radio {
if((!mIrqRcvd) && (!mRqstGetRx)) if((!mIrqRcvd) && (!mRqstGetRx))
return false; return false;
getRx(); getRx();
if(CMT_SUCCESS == mCmt.goRx()) { if(CmtStatus::SUCCESS == mCmt.goRx()) {
mIrqRcvd = false; mIrqRcvd = false;
mRqstGetRx = false; mRqstGetRx = false;
} }
@ -76,6 +76,18 @@ class CmtRadio : public Radio {
return true; 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: private:
void sendPacket(Inverter<> *iv, uint8_t len, bool isRetransmit, bool appendCrc16=true) { 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(); mMillis = millis();
if(CMT_SUCCESS != status) { if(CmtStatus::SUCCESS != status) {
DPRINT(DBG_WARN, F("CMT TX failed, code: ")); DPRINT(DBG_WARN, F("CMT TX failed, code: "));
DBGPRINTLN(String(status)); DBGPRINTLN(String(static_cast<uint8_t>(status)));
if(CMT_ERR_RX_IN_FIFO == status) if(CmtStatus::ERR_RX_IN_FIFO == status)
mIrqRcvd = true; mIrqRcvd = true;
} }
iv->mDtuTxCnt++; iv->mDtuTxCnt++;
@ -123,10 +135,10 @@ class CmtRadio : public Radio {
return iv->ivGen; return iv->ivGen;
} }
inline void reset(bool genDtuSn) { inline void reset(bool genDtuSn, RegionCfg region) {
if(genDtuSn) if(genDtuSn)
generateDtuSn(); generateDtuSn();
if(!mCmt.reset()) { if(!mCmt.reset(region)) {
mCmtAvail = false; mCmtAvail = false;
DPRINTLN(DBG_WARN, F("Initializing CMT2300A failed!")); DPRINTLN(DBG_WARN, F("Initializing CMT2300A failed!"));
} else { } else {
@ -160,8 +172,8 @@ class CmtRadio : public Radio {
inline void getRx(void) { inline void getRx(void) {
packet_t p; packet_t p;
p.millis = millis() - mMillis; p.millis = millis() - mMillis;
uint8_t status = mCmt.getRx(p.packet, &p.len, 28, &p.rssi); CmtStatus status = mCmt.getRx(p.packet, &p.len, 28, &p.rssi);
if(CMT_SUCCESS == status) if(CmtStatus::SUCCESS == status)
mBufCtrl.push(p); mBufCtrl.push(p);
// this code completly stops communication! // this code completly stops communication!

5
src/web/RestApi.h

@ -268,6 +268,8 @@ class RestApi {
obj[F("menu_protEn")] = (bool) (strlen(mConfig->sys.adminPwd) > 0); obj[F("menu_protEn")] = (bool) (strlen(mConfig->sys.adminPwd) > 0);
obj[F("cst_lnk")] = String(mConfig->plugin.customLink); obj[F("cst_lnk")] = String(mConfig->plugin.customLink);
obj[F("cst_lnk_txt")] = String(mConfig->plugin.customLinkText); obj[F("cst_lnk_txt")] = String(mConfig->plugin.customLinkText);
obj[F("region")] = mConfig->sys.region;
obj[F("timezone")] = mConfig->sys.timezone;
#if defined(ESP32) #if defined(ESP32)
obj[F("esp_type")] = F("ESP32"); obj[F("esp_type")] = F("ESP32");
@ -651,6 +653,9 @@ class RestApi {
obj[F("fcsb")] = mConfig->cmt.pinFcsb; obj[F("fcsb")] = mConfig->cmt.pinFcsb;
obj[F("gpio3")] = mConfig->cmt.pinIrq; obj[F("gpio3")] = mConfig->cmt.pinIrq;
obj[F("en")] = (bool) mConfig->cmt.enabled; 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) { void getRadioCmtInfo(JsonObject obj) {

25
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-4 col-sm-9"><input type="checkbox" name="darkMode"/></div>
<div class="col-12">{#DARK_MODE_NOTE}</div> <div class="col-12">{#DARK_MODE_NOTE}</div>
</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="row mb-3">
<div class="col-8 col-sm-3">{#CUSTOM_LINK}</div> <div class="col-8 col-sm-3">{#CUSTOM_LINK}</div>
<div class="col-4 col-sm-9"><input type="text" name="cstLnk"/></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++) { for(var i = 0; i < 31; i++) {
esp32cmtPa.push([i, String(i-10) + " dBm"]); 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*/ /*ENDIF_ESP32*/
var led_high_active = [ var led_high_active = [
[0, "{#PIN_LOW_ACTIVE}"], [0, "{#PIN_LOW_ACTIVE}"],
@ -650,6 +655,14 @@
el.push(mlCb("protMask" + i, a[i], chk)) el.push(mlCb("protMask" + i, a[i], chk))
} }
d.append(...el); 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) { 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*/ /*ENDIF_ESP32*/

10
src/web/lang.json

@ -148,6 +148,16 @@
"en": "(empty browser cache or use CTRL + F5 after reboot to apply this setting)", "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)" "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", "token": "CUSTOM_LINK",
"en": "Custom link (leave empty to hide element in navigation)", "en": "Custom link (leave empty to hide element in navigation)",

3
src/web/web.h

@ -481,7 +481,8 @@ class Web {
request->arg("device").toCharArray(mConfig->sys.deviceName, DEVNAME_LEN); request->arg("device").toCharArray(mConfig->sys.deviceName, DEVNAME_LEN);
mConfig->sys.darkMode = (request->arg("darkMode") == "on"); mConfig->sys.darkMode = (request->arg("darkMode") == "on");
mConfig->sys.schedReboot = (request->arg("schedReboot") == "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") != "") { if (request->arg("cstLnk") != "") {
request->arg("cstLnk").toCharArray(mConfig->plugin.customLink, MAX_CUSTOM_LINK_LEN); request->arg("cstLnk").toCharArray(mConfig->plugin.customLink, MAX_CUSTOM_LINK_LEN);

Loading…
Cancel
Save