diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..559f9bfd2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +# http://editorconfig.org +root = true + +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true diff --git a/.vscode/extensions.json b/.vscode/extensions.json index d14c6bfee..bd7f35aa6 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,6 +3,7 @@ // for the documentation about the extensions.json format "recommendations": [ "DavidAnson.vscode-markdownlint", + "EditorConfig.EditorConfig", "Vue.volar", "Vue.vscode-typescript-vue-plugin", "platformio.platformio-ide" diff --git a/include/Configuration.h b/include/Configuration.h index 5031f6228..cc3456434 100644 --- a/include/Configuration.h +++ b/include/Configuration.h @@ -5,7 +5,7 @@ #include #define CONFIG_FILENAME "/config.json" -#define CONFIG_VERSION 0x00011a00 // 0.1.26 // make sure to clean all after change +#define CONFIG_VERSION 0x00011b00 // 0.1.27 // make sure to clean all after change #define WIFI_MAX_SSID_STRLEN 32 #define WIFI_MAX_PASSWORD_STRLEN 64 @@ -152,6 +152,7 @@ struct CONFIG_T { struct { int8_t PaLevel; uint32_t Frequency; + uint8_t CountryMode; } Cmt; bool VerboseLogging; } Dtu; @@ -167,7 +168,10 @@ struct CONFIG_T { uint8_t Rotation; uint8_t Contrast; uint8_t Language; - uint32_t DiagramDuration; + struct { + uint32_t Duration; + uint8_t Mode; + } Diagram; } Display; struct { @@ -256,4 +260,4 @@ class ConfigurationClass { INVERTER_CONFIG_T* getInverterConfig(const uint64_t serial); }; -extern ConfigurationClass Configuration; \ No newline at end of file +extern ConfigurationClass Configuration; diff --git a/include/Display_Graphic.h b/include/Display_Graphic.h index 707812271..270ee81da 100644 --- a/include/Display_Graphic.h +++ b/include/Display_Graphic.h @@ -6,6 +6,14 @@ #include #include +#define CHART_HEIGHT 20 // chart area hight in pixels +#define CHART_WIDTH 47 // chart area width in pixels + +// Left-Upper position of diagram is drawn +// (text of Y-axis is display left of that pos) +#define CHART_POSX 80 +#define CHART_POSY 0 + enum DisplayType_t { None, PCD8544, @@ -15,6 +23,13 @@ enum DisplayType_t { DisplayType_Max, }; +enum DiagramMode_t { + Off, + Small, + Fullscreen, + DisplayMode_Max, +}; + class DisplayGraphicClass { public: DisplayGraphicClass(); @@ -25,6 +40,7 @@ class DisplayGraphicClass { void setStatus(const bool turnOn); void setOrientation(const uint8_t rotation = DISPLAY_ROTATION); void setLanguage(const uint8_t language); + void setDiagramMode(DiagramMode_t mode); void setStartupDisplay(); DisplayGraphicDiagramClass& Diagram(); @@ -47,14 +63,15 @@ class DisplayGraphicClass { bool _displayTurnedOn; DisplayType_t _display_type = DisplayType_t::None; + DiagramMode_t _diagram_mode = DiagramMode_t::Off; uint8_t _display_language = DISPLAY_LANGUAGE; uint8_t _mExtra; - uint16_t _period = 1000; - uint16_t _interval = 60000; // interval at which to power save (milliseconds) + const uint16_t _period = 1000; + const uint16_t _interval = 60000; // interval at which to power save (milliseconds) uint32_t _previousMillis = 0; char _fmtText[32]; bool _isLarge = false; uint8_t _lineOffsets[5]; }; -extern DisplayGraphicClass Display; \ No newline at end of file +extern DisplayGraphicClass Display; diff --git a/include/Display_Graphic_Diagram.h b/include/Display_Graphic_Diagram.h index 2067a3f09..26cacc372 100644 --- a/include/Display_Graphic_Diagram.h +++ b/include/Display_Graphic_Diagram.h @@ -5,20 +5,14 @@ #include #include -#define CHART_HEIGHT 20 // chart area hight in pixels -#define CHART_WIDTH 47 // chart area width in pixels - -// Left-Upper position of diagram is drawn -// (text of Y-axis is display left of that pos) -#define DIAG_POSX 80 -#define DIAG_POSY 0 +#define MAX_DATAPOINTS 128 class DisplayGraphicDiagramClass { public: DisplayGraphicDiagramClass(); void init(Scheduler& scheduler, U8G2* display); - void redraw(uint8_t screenSaverOffsetX); + void redraw(uint8_t screenSaverOffsetX, uint8_t xPos, uint8_t yPos, uint8_t width, uint8_t height, bool isFullscreen); void updatePeriod(); @@ -26,15 +20,17 @@ class DisplayGraphicDiagramClass { void averageLoop(); void dataPointLoop(); - static uint32_t getSecondsPerDot(); + uint32_t getSecondsPerDot(); Task _averageTask; Task _dataPointTask; U8G2* _display = nullptr; - std::array _graphValues = {}; + std::array _graphValues = {}; uint8_t _graphValuesCount = 0; + uint8_t _chartWidth = MAX_DATAPOINTS; + float _iRunningAverage = 0; uint16_t _iRunningAverageCnt = 0; }; diff --git a/include/HttpPowerMeter.h b/include/HttpPowerMeter.h index aff05e64e..95cc1781d 100644 --- a/include/HttpPowerMeter.h +++ b/include/HttpPowerMeter.h @@ -10,17 +10,24 @@ class HttpPowerMeterClass { void init(); bool updateValues(); float getPower(int8_t phase); - bool httpRequest(const char* url, Auth authType, const char* username, const char* password, const char* httpHeader, const char* httpValue, uint32_t timeout, - char* response, size_t responseSize, char* error, size_t errorSize); - float getFloatValueByJsonPath(const char* jsonString, const char* jsonPath, float &value); + char httpPowerMeterError[256]; + bool queryPhase(int phase, const String& url, Auth authType, const char* username, const char* password, + const char* httpHeader, const char* httpValue, uint32_t timeout, const char* jsonPath); -private: - void extractUrlComponents(const String& url, String& protocol, String& hostname, String& uri); - void prepareRequest(uint32_t timeout, const char* httpHeader, const char* httpValue); - HTTPClient httpClient; - float power[POWERMETER_MAX_PHASES]; - String sha256(const String& data); - + +private: + float power[POWERMETER_MAX_PHASES]; + HTTPClient httpClient; + String httpResponse; + bool httpRequest(int phase, WiFiClient &wifiClient, const String& host, const String& uri, bool https, Auth authType, const char* username, + const char* password, const char* httpHeader, const char* httpValue, uint32_t timeout, const char* jsonPath); + void extractUrlComponents(const String& url, String& protocol, String& host, String& uri); + String extractParam(String& authReq, const String& param, const char delimit); + String getcNonce(const int len); + String getDigestAuth(String& authReq, const String& username, const String& password, const String& method, const String& uri, unsigned int counter); + bool tryGetFloatValueForPhase(int phase, int httpCode, const char* jsonPath); + void prepareRequest(uint32_t timeout, const char* httpHeader, const char* httpValue); + String sha256(const String& data); }; extern HttpPowerMeterClass HttpPowerMeter; diff --git a/include/WebApi_dtu.h b/include/WebApi_dtu.h index 45f58d32e..98d286522 100644 --- a/include/WebApi_dtu.h +++ b/include/WebApi_dtu.h @@ -13,4 +13,5 @@ class WebApiDtuClass { void onDtuAdminPost(AsyncWebServerRequest* request); AsyncWebServer* _server; -}; \ No newline at end of file + bool _performReload = false; +}; diff --git a/include/WebApi_errors.h b/include/WebApi_errors.h index e5703a83a..efb890c5c 100644 --- a/include/WebApi_errors.h +++ b/include/WebApi_errors.h @@ -15,6 +15,7 @@ enum WebApiError { DtuPollZero, DtuInvalidPowerLevel, DtuInvalidCmtFrequency, + DtuInvalidCmtCountry, ConfigBase = 3000, ConfigNotDeleted, @@ -89,4 +90,4 @@ enum WebApiError { HardwareBase = 12000, HardwarePinMappingLength, -}; \ No newline at end of file +}; diff --git a/include/defaults.h b/include/defaults.h index fbbe5368e..7e1d7a0c1 100644 --- a/include/defaults.h +++ b/include/defaults.h @@ -83,7 +83,8 @@ #define DTU_POLL_INTERVAL 5U #define DTU_NRF_PA_LEVEL 0U #define DTU_CMT_PA_LEVEL 0 -#define DTU_CMT_FREQUENCY 865000U +#define DTU_CMT_FREQUENCY 865000000U +#define DTU_CMT_COUNTRY_MODE 0U #define MQTT_HASS_ENABLED false #define MQTT_HASS_EXPIRE true @@ -99,9 +100,58 @@ #define DISPLAY_CONTRAST 60U #define DISPLAY_LANGUAGE 0U #define DISPLAY_DIAGRAM_DURATION (10UL * 60UL * 60UL) +#define DISPLAY_DIAGRAM_MODE 1U #define REACHABLE_THRESHOLD 2U +#define MAX_INVERTER_LIMIT 2250 +#define VEDIRECT_ENABLED false +#define VEDIRECT_VERBOSE_LOGGING false +#define VEDIRECT_UPDATESONLY true + +#define POWERMETER_ENABLED false +#define POWERMETER_INTERVAL 10 +#define POWERMETER_SOURCE 2 +#define POWERMETER_SDMBAUDRATE 9600 +#define POWERMETER_SDMADDRESS 1 + + +#define POWERLIMITER_ENABLED false +#define POWERLIMITER_SOLAR_PASSTHROUGH_ENABLED true +#define POWERLIMITER_SOLAR_PASSTHROUGH_LOSSES 3 +#define POWERLIMITER_BATTERY_DRAIN_STRATEGY 0 +#define POWERLIMITER_INTERVAL 10 +#define POWERLIMITER_IS_INVERTER_BEHIND_POWER_METER true +#define POWERLIMITER_INVERTER_ID 0 +#define POWERLIMITER_INVERTER_CHANNEL_ID 0 +#define POWERLIMITER_TARGET_POWER_CONSUMPTION 0 +#define POWERLIMITER_TARGET_POWER_CONSUMPTION_HYSTERESIS 0 +#define POWERLIMITER_LOWER_POWER_LIMIT 10 +#define POWERLIMITER_UPPER_POWER_LIMIT 800 +#define POWERLIMITER_BATTERY_SOC_START_THRESHOLD 80 +#define POWERLIMITER_BATTERY_SOC_STOP_THRESHOLD 20 +#define POWERLIMITER_VOLTAGE_START_THRESHOLD 50.0 +#define POWERLIMITER_VOLTAGE_STOP_THRESHOLD 49.0 +#define POWERLIMITER_VOLTAGE_LOAD_CORRECTION_FACTOR 0.001 +#define POWERLIMITER_RESTART_HOUR -1 +#define POWERLIMITER_FULL_SOLAR_PASSTHROUGH_SOC 100 +#define POWERLIMITER_FULL_SOLAR_PASSTHROUGH_START_VOLTAGE 100.0 +#define POWERLIMITER_FULL_SOLAR_PASSTHROUGH_STOP_VOLTAGE 100.0 + +#define BATTERY_ENABLED false +#define BATTERY_PROVIDER 0 // Pylontech CAN receiver +#define BATTERY_JKBMS_INTERFACE 0 +#define BATTERY_JKBMS_POLLING_INTERVAL 5 + +#define HUAWEI_ENABLED false +#define HUAWEI_CAN_CONTROLLER_FREQUENCY 8000000UL +#define HUAWEI_AUTO_POWER_VOLTAGE_LIMIT 42.0 +#define HUAWEI_AUTO_POWER_ENABLE_VOLTAGE_LIMIT 42.0 +#define HUAWEI_AUTO_POWER_LOWER_POWER_LIMIT 150 +#define HUAWEI_AUTO_POWER_UPPER_POWER_LIMIT 2000 + +#define VERBOSE_LOGGING true + #define LED_BRIGHTNESS 100U #define MAX_INVERTER_LIMIT 2250 @@ -154,4 +204,4 @@ #define LED_BRIGHTNESS 100U -#define MAX_INVERTER_LIMIT 2250 \ No newline at end of file +#define MAX_INVERTER_LIMIT 2250 diff --git a/lib/CMT2300a/cmt2300a.c b/lib/CMT2300a/cmt2300a.c index 45b09f5e7..ae1a3ed95 100644 --- a/lib/CMT2300a/cmt2300a.c +++ b/lib/CMT2300a/cmt2300a.c @@ -1,778 +1,778 @@ -/* - * THE FOLLOWING FIRMWARE IS PROVIDED: (1) "AS IS" WITH NO WARRANTY; AND - * (2)TO ENABLE ACCESS TO CODING INFORMATION TO GUIDE AND FACILITATE CUSTOMER. - * CONSEQUENTLY, CMOSTEK SHALL NOT BE HELD LIABLE FOR ANY DIRECT, INDIRECT OR - * CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT - * OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION - * CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. - * - * Copyright (C) CMOSTEK SZ. - */ - -/*! - * @file cmt2300a.c - * @brief CMT2300A transceiver RF chip driver - * - * @version 1.3 - * @date Jul 17 2017 - * @author CMOSTEK R@D - */ - -#include "cmt2300a.h" - -/*! ******************************************************** - * @name CMT2300A_SoftReset - * @desc Soft reset. - * *********************************************************/ -void CMT2300A_SoftReset(void) -{ - CMT2300A_WriteReg(0x7F, 0xFF); -} - -/*! ******************************************************** - * @name CMT2300A_GetChipStatus - * @desc Get the chip status. - * @return - * CMT2300A_STA_PUP - * CMT2300A_STA_SLEEP - * CMT2300A_STA_STBY - * CMT2300A_STA_RFS - * CMT2300A_STA_TFS - * CMT2300A_STA_RX - * CMT2300A_STA_TX - * CMT2300A_STA_EEPROM - * CMT2300A_STA_ERROR - * CMT2300A_STA_CAL - * *********************************************************/ -uint8_t CMT2300A_GetChipStatus(void) -{ - return CMT2300A_ReadReg(CMT2300A_CUS_MODE_STA) & CMT2300A_MASK_CHIP_MODE_STA; -} - -/*! ******************************************************** - * @name CMT2300A_AutoSwitchStatus - * @desc Auto switch the chip status, and 10 ms as timeout. - * @param nGoCmd: the chip next status - * @return TRUE or FALSE - * *********************************************************/ -bool CMT2300A_AutoSwitchStatus(uint8_t nGoCmd) -{ -#ifdef ENABLE_AUTO_SWITCH_CHIP_STATUS - uint32_t nBegTick = CMT2300A_GetTickCount(); - uint8_t nWaitStatus = 0; - - switch (nGoCmd) { - case CMT2300A_GO_SLEEP: - nWaitStatus = CMT2300A_STA_SLEEP; - break; - case CMT2300A_GO_STBY: - nWaitStatus = CMT2300A_STA_STBY; - break; - case CMT2300A_GO_TFS: - nWaitStatus = CMT2300A_STA_TFS; - break; - case CMT2300A_GO_TX: - nWaitStatus = CMT2300A_STA_TX; - break; - case CMT2300A_GO_RFS: - nWaitStatus = CMT2300A_STA_RFS; - break; - case CMT2300A_GO_RX: - nWaitStatus = CMT2300A_STA_RX; - break; - } - - CMT2300A_WriteReg(CMT2300A_CUS_MODE_CTL, nGoCmd); - - while (CMT2300A_GetTickCount() - nBegTick < 10) { - CMT2300A_DelayUs(100); - - if (nWaitStatus == CMT2300A_GetChipStatus()) - return true; - - if (CMT2300A_GO_TX == nGoCmd) { - CMT2300A_DelayUs(100); - - if (CMT2300A_MASK_TX_DONE_FLG & CMT2300A_ReadReg(CMT2300A_CUS_INT_CLR1)) - return true; - } - - if (CMT2300A_GO_RX == nGoCmd) { - CMT2300A_DelayUs(100); - - if (CMT2300A_MASK_PKT_OK_FLG & CMT2300A_ReadReg(CMT2300A_CUS_INT_FLAG)) - return true; - } - } - - return false; - -#else - CMT2300A_WriteReg(CMT2300A_CUS_MODE_CTL, nGoCmd); - return true; -#endif -} - -/*! ******************************************************** - * @name CMT2300A_GoSleep - * @desc Entry SLEEP mode. - * @return TRUE or FALSE - * *********************************************************/ -bool CMT2300A_GoSleep(void) -{ - return CMT2300A_AutoSwitchStatus(CMT2300A_GO_SLEEP); -} - -/*! ******************************************************** - * @name CMT2300A_GoStby - * @desc Entry Sleep mode. - * @return TRUE or FALSE - * *********************************************************/ -bool CMT2300A_GoStby(void) -{ - return CMT2300A_AutoSwitchStatus(CMT2300A_GO_STBY); -} - -/*! ******************************************************** - * @name CMT2300A_GoTFS - * @desc Entry TFS mode. - * @return TRUE or FALSE - * *********************************************************/ -bool CMT2300A_GoTFS(void) -{ - return CMT2300A_AutoSwitchStatus(CMT2300A_GO_TFS); -} - -/*! ******************************************************** - * @name CMT2300A_GoRFS - * @desc Entry RFS mode. - * @return TRUE or FALSE - * *********************************************************/ -bool CMT2300A_GoRFS(void) -{ - return CMT2300A_AutoSwitchStatus(CMT2300A_GO_RFS); -} - -/*! ******************************************************** - * @name CMT2300A_GoTx - * @desc Entry Tx mode. - * @return TRUE or FALSE - * *********************************************************/ -bool CMT2300A_GoTx(void) -{ - return CMT2300A_AutoSwitchStatus(CMT2300A_GO_TX); -} - -/*! ******************************************************** - * @name CMT2300A_GoRx - * @desc Entry Rx mode. - * @return TRUE or FALSE - * *********************************************************/ -bool CMT2300A_GoRx(void) -{ - return CMT2300A_AutoSwitchStatus(CMT2300A_GO_RX); -} - -/*! ******************************************************** - * @name CMT2300A_ConfigGpio - * @desc Config GPIO pins mode. - * @param nGpioSel: GPIO1_SEL | GPIO2_SEL | GPIO3_SEL | GPIO4_SEL - * GPIO1_SEL: - * CMT2300A_GPIO1_SEL_DOUT/DIN - * CMT2300A_GPIO1_SEL_INT1 - * CMT2300A_GPIO1_SEL_INT2 - * CMT2300A_GPIO1_SEL_DCLK - * - * GPIO2_SEL: - * CMT2300A_GPIO2_SEL_INT1 - * CMT2300A_GPIO2_SEL_INT2 - * CMT2300A_GPIO2_SEL_DOUT/DIN - * CMT2300A_GPIO2_SEL_DCLK - * - * GPIO3_SEL: - * CMT2300A_GPIO3_SEL_CLKO - * CMT2300A_GPIO3_SEL_DOUT/DIN - * CMT2300A_GPIO3_SEL_INT2 - * CMT2300A_GPIO3_SEL_DCLK - * - * GPIO4_SEL: - * CMT2300A_GPIO4_SEL_RSTIN - * CMT2300A_GPIO4_SEL_INT1 - * CMT2300A_GPIO4_SEL_DOUT - * CMT2300A_GPIO4_SEL_DCLK - * *********************************************************/ -void CMT2300A_ConfigGpio(uint8_t nGpioSel) -{ - CMT2300A_WriteReg(CMT2300A_CUS_IO_SEL, nGpioSel); -} - -/*! ******************************************************** - * @name CMT2300A_ConfigInterrupt - * @desc Config interrupt on INT1 and INT2. - * @param nInt1Sel, nInt2Sel - * CMT2300A_INT_SEL_RX_ACTIVE - * CMT2300A_INT_SEL_TX_ACTIVE - * CMT2300A_INT_SEL_RSSI_VLD - * CMT2300A_INT_SEL_PREAM_OK - * CMT2300A_INT_SEL_SYNC_OK - * CMT2300A_INT_SEL_NODE_OK - * CMT2300A_INT_SEL_CRC_OK - * CMT2300A_INT_SEL_PKT_OK - * CMT2300A_INT_SEL_SL_TMO - * CMT2300A_INT_SEL_RX_TMO - * CMT2300A_INT_SEL_TX_DONE - * CMT2300A_INT_SEL_RX_FIFO_NMTY - * CMT2300A_INT_SEL_RX_FIFO_TH - * CMT2300A_INT_SEL_RX_FIFO_FULL - * CMT2300A_INT_SEL_RX_FIFO_WBYTE - * CMT2300A_INT_SEL_RX_FIFO_OVF - * CMT2300A_INT_SEL_TX_FIFO_NMTY - * CMT2300A_INT_SEL_TX_FIFO_TH - * CMT2300A_INT_SEL_TX_FIFO_FULL - * CMT2300A_INT_SEL_STATE_IS_STBY - * CMT2300A_INT_SEL_STATE_IS_FS - * CMT2300A_INT_SEL_STATE_IS_RX - * CMT2300A_INT_SEL_STATE_IS_TX - * CMT2300A_INT_SEL_LED - * CMT2300A_INT_SEL_TRX_ACTIVE - * CMT2300A_INT_SEL_PKT_DONE - * *********************************************************/ -void CMT2300A_ConfigInterrupt(uint8_t nInt1Sel, uint8_t nInt2Sel) -{ - nInt1Sel &= CMT2300A_MASK_INT1_SEL; - nInt1Sel |= (~CMT2300A_MASK_INT1_SEL) & CMT2300A_ReadReg(CMT2300A_CUS_INT1_CTL); - CMT2300A_WriteReg(CMT2300A_CUS_INT1_CTL, nInt1Sel); - - nInt2Sel &= CMT2300A_MASK_INT2_SEL; - nInt2Sel |= (~CMT2300A_MASK_INT2_SEL) & CMT2300A_ReadReg(CMT2300A_CUS_INT2_CTL); - CMT2300A_WriteReg(CMT2300A_CUS_INT2_CTL, nInt2Sel); -} - -/*! ******************************************************** - * @name CMT2300A_SetInterruptPolar - * @desc Set the polarity of the interrupt. - * @param bEnable(TRUE): active-high (default) - * bEnable(FALSE): active-low - * *********************************************************/ -void CMT2300A_SetInterruptPolar(bool bActiveHigh) -{ - uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_INT1_CTL); - - if (bActiveHigh) - tmp &= ~CMT2300A_MASK_INT_POLAR; - else - tmp |= CMT2300A_MASK_INT_POLAR; - - CMT2300A_WriteReg(CMT2300A_CUS_INT1_CTL, tmp); -} - -/*! ******************************************************** - * @name CMT2300A_SetFifoThreshold - * @desc Set FIFO threshold. - * @param nFifoThreshold - * *********************************************************/ -void CMT2300A_SetFifoThreshold(uint8_t nFifoThreshold) -{ - uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_PKT29); - - tmp &= ~CMT2300A_MASK_FIFO_TH; - tmp |= nFifoThreshold & CMT2300A_MASK_FIFO_TH; - - CMT2300A_WriteReg(CMT2300A_CUS_PKT29, tmp); -} - -/*! ******************************************************** - * @name CMT2300A_EnableAntennaSwitch - * @desc Enable antenna switch, output TX_ACTIVE/RX_ACTIVE - * via GPIO1/GPIO2. - * @param nMode - * 0: RF_SWT1_EN=1, RF_SWT2_EN=0 - * GPIO1: RX_ACTIVE, GPIO2: TX_ACTIVE - * 1: RF_SWT1_EN=0, RF_SWT2_EN=1 - * GPIO1: RX_ACTIVE, GPIO2: ~RX_ACTIVE - * *********************************************************/ -void CMT2300A_EnableAntennaSwitch(uint8_t nMode) -{ - uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_INT1_CTL); - - if (0 == nMode) { - tmp |= CMT2300A_MASK_RF_SWT1_EN; - tmp &= ~CMT2300A_MASK_RF_SWT2_EN; - } else if (1 == nMode) { - tmp &= ~CMT2300A_MASK_RF_SWT1_EN; - tmp |= CMT2300A_MASK_RF_SWT2_EN; - } - - CMT2300A_WriteReg(CMT2300A_CUS_INT1_CTL, tmp); -} - -/*! ******************************************************** - * @name CMT2300A_EnableInterrupt - * @desc Enable interrupt. - * @param nEnable - * CMT2300A_MASK_SL_TMO_EN | - * CMT2300A_MASK_RX_TMO_EN | - * CMT2300A_MASK_TX_DONE_EN | - * CMT2300A_MASK_PREAM_OK_EN | - * CMT2300A_MASK_SYNC_OK_EN | - * CMT2300A_MASK_NODE_OK_EN | - * CMT2300A_MASK_CRC_OK_EN | - * CMT2300A_MASK_PKT_DONE_EN - * *********************************************************/ -void CMT2300A_EnableInterrupt(uint8_t nEnable) -{ - CMT2300A_WriteReg(CMT2300A_CUS_INT_EN, nEnable); -} - -/*! ******************************************************** - * @name CMT2300A_EnableRxFifoAutoClear - * @desc Auto clear Rx FIFO before entry Rx mode. - * @param bEnable(TRUE): Enable it(default) - * bEnable(FALSE): Disable it - * *********************************************************/ -void CMT2300A_EnableRxFifoAutoClear(bool bEnable) -{ - uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_CTL); - - if (bEnable) - tmp &= ~CMT2300A_MASK_FIFO_AUTO_CLR_DIS; - else - tmp |= CMT2300A_MASK_FIFO_AUTO_CLR_DIS; - - CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CTL, tmp); -} - -/*! ******************************************************** - * @name CMT2300A_EnableFifoMerge - * @desc Enable FIFO merge. - * @param bEnable(TRUE): use a single 64-byte FIFO for either Tx or Rx - * bEnable(FALSE): use a 32-byte FIFO for Tx and another 32-byte FIFO for Rx(default) - * *********************************************************/ -void CMT2300A_EnableFifoMerge(bool bEnable) -{ - uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_CTL); - - if (bEnable) - tmp |= CMT2300A_MASK_FIFO_MERGE_EN; - else - tmp &= ~CMT2300A_MASK_FIFO_MERGE_EN; - - CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CTL, tmp); -} - -/*! ******************************************************** - * @name CMT2300A_EnableReadFifo - * @desc Enable SPI to read the FIFO. - * *********************************************************/ -void CMT2300A_EnableReadFifo(void) -{ - uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_CTL); - tmp &= ~CMT2300A_MASK_SPI_FIFO_RD_WR_SEL; - tmp &= ~CMT2300A_MASK_FIFO_RX_TX_SEL; - CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CTL, tmp); -} - -/*! ******************************************************** - * @name CMT2300A_EnableWriteFifo - * @desc Enable SPI to write the FIFO. - * *********************************************************/ -void CMT2300A_EnableWriteFifo(void) -{ - uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_CTL); - tmp |= CMT2300A_MASK_SPI_FIFO_RD_WR_SEL; - tmp |= CMT2300A_MASK_FIFO_RX_TX_SEL; - CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CTL, tmp); -} - -/*! ******************************************************** - * @name CMT2300A_RestoreFifo - * @desc Restore the FIFO. - * *********************************************************/ -void CMT2300A_RestoreFifo(void) -{ - CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CLR, CMT2300A_MASK_FIFO_RESTORE); -} - -/*! ******************************************************** - * @name CMT2300A_ClearFifo - * @desc Clear the Tx FIFO. - * @return FIFO flags - * CMT2300A_MASK_RX_FIFO_FULL_FLG | - * CMT2300A_MASK_RX_FIFO_NMTY_FLG | - * CMT2300A_MASK_RX_FIFO_TH_FLG | - * CMT2300A_MASK_RX_FIFO_OVF_FLG | - * CMT2300A_MASK_TX_FIFO_FULL_FLG | - * CMT2300A_MASK_TX_FIFO_NMTY_FLG | - * CMT2300A_MASK_TX_FIFO_TH_FLG - * *********************************************************/ -uint8_t CMT2300A_ClearTxFifo(void) -{ - uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_FLAG); - CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CLR, CMT2300A_MASK_FIFO_CLR_TX); - return tmp; -} - -/*! ******************************************************** - * @name CMT2300A_ClearFifo - * @desc Clear the Rx FIFO. - * @return FIFO flags - * CMT2300A_MASK_RX_FIFO_FULL_FLG | - * CMT2300A_MASK_RX_FIFO_NMTY_FLG | - * CMT2300A_MASK_RX_FIFO_TH_FLG | - * CMT2300A_MASK_RX_FIFO_OVF_FLG | - * CMT2300A_MASK_TX_FIFO_FULL_FLG | - * CMT2300A_MASK_TX_FIFO_NMTY_FLG | - * CMT2300A_MASK_TX_FIFO_TH_FLG - * *********************************************************/ -uint8_t CMT2300A_ClearRxFifo(void) -{ - uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_FLAG); - CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CLR, CMT2300A_MASK_FIFO_CLR_RX); - return tmp; -} - -/*! ******************************************************** - * @name CMT2300A_ClearInterruptFlags - * @desc Clear all interrupt flags. - * @return Some interrupt flags - * CMT2300A_MASK_SL_TMO_EN | - * CMT2300A_MASK_RX_TMO_EN | - * CMT2300A_MASK_TX_DONE_EN | - * CMT2300A_MASK_PREAM_OK_FLG | - * CMT2300A_MASK_SYNC_OK_FLG | - * CMT2300A_MASK_NODE_OK_FLG | - * CMT2300A_MASK_CRC_OK_FLG | - * CMT2300A_MASK_PKT_OK_FLG - * *********************************************************/ -uint8_t CMT2300A_ClearInterruptFlags(void) -{ - uint8_t nFlag1, nFlag2; - uint8_t nClr1 = 0; - uint8_t nClr2 = 0; - uint8_t nRet = 0; - uint8_t nIntPolar; - - nIntPolar = CMT2300A_ReadReg(CMT2300A_CUS_INT1_CTL); - nIntPolar = (nIntPolar & CMT2300A_MASK_INT_POLAR) ? 1 : 0; - - nFlag1 = CMT2300A_ReadReg(CMT2300A_CUS_INT_FLAG); - nFlag2 = CMT2300A_ReadReg(CMT2300A_CUS_INT_CLR1); - - if (nIntPolar) { - /* Interrupt flag active-low */ - nFlag1 = ~nFlag1; - nFlag2 = ~nFlag2; - } - - if (CMT2300A_MASK_LBD_FLG & nFlag1) { - nClr2 |= CMT2300A_MASK_LBD_CLR; /* Clear LBD_FLG */ - } - - if (CMT2300A_MASK_COL_ERR_FLG & nFlag1) { - nClr2 |= CMT2300A_MASK_PKT_DONE_CLR; /* Clear COL_ERR_FLG by PKT_DONE_CLR */ - } - - if (CMT2300A_MASK_PKT_ERR_FLG & nFlag1) { - nClr2 |= CMT2300A_MASK_PKT_DONE_CLR; /* Clear PKT_ERR_FLG by PKT_DONE_CLR */ - } - - if (CMT2300A_MASK_PREAM_OK_FLG & nFlag1) { - nClr2 |= CMT2300A_MASK_PREAM_OK_CLR; /* Clear PREAM_OK_FLG */ - nRet |= CMT2300A_MASK_PREAM_OK_FLG; /* Return PREAM_OK_FLG */ - } - - if (CMT2300A_MASK_SYNC_OK_FLG & nFlag1) { - nClr2 |= CMT2300A_MASK_SYNC_OK_CLR; /* Clear SYNC_OK_FLG */ - nRet |= CMT2300A_MASK_SYNC_OK_FLG; /* Return SYNC_OK_FLG */ - } - - if (CMT2300A_MASK_NODE_OK_FLG & nFlag1) { - nClr2 |= CMT2300A_MASK_NODE_OK_CLR; /* Clear NODE_OK_FLG */ - nRet |= CMT2300A_MASK_NODE_OK_FLG; /* Return NODE_OK_FLG */ - } - - if (CMT2300A_MASK_CRC_OK_FLG & nFlag1) { - nClr2 |= CMT2300A_MASK_CRC_OK_CLR; /* Clear CRC_OK_FLG */ - nRet |= CMT2300A_MASK_CRC_OK_FLG; /* Return CRC_OK_FLG */ - } - - if (CMT2300A_MASK_PKT_OK_FLG & nFlag1) { - nClr2 |= CMT2300A_MASK_PKT_DONE_CLR; /* Clear PKT_OK_FLG */ - nRet |= CMT2300A_MASK_PKT_OK_FLG; /* Return PKT_OK_FLG */ - } - - if (CMT2300A_MASK_SL_TMO_FLG & nFlag2) { - nClr1 |= CMT2300A_MASK_SL_TMO_CLR; /* Clear SL_TMO_FLG */ - nRet |= CMT2300A_MASK_SL_TMO_EN; /* Return SL_TMO_FLG by SL_TMO_EN */ - } - - if (CMT2300A_MASK_RX_TMO_FLG & nFlag2) { - nClr1 |= CMT2300A_MASK_RX_TMO_CLR; /* Clear RX_TMO_FLG */ - nRet |= CMT2300A_MASK_RX_TMO_EN; /* Return RX_TMO_FLG by RX_TMO_EN */ - } - - if (CMT2300A_MASK_TX_DONE_FLG & nFlag2) { - nClr1 |= CMT2300A_MASK_TX_DONE_CLR; /* Clear TX_DONE_FLG */ - nRet |= CMT2300A_MASK_TX_DONE_EN; /* Return TX_DONE_FLG by TX_DONE_EN */ - } - - CMT2300A_WriteReg(CMT2300A_CUS_INT_CLR1, nClr1); - CMT2300A_WriteReg(CMT2300A_CUS_INT_CLR2, nClr2); - - if (nIntPolar) { - /* Interrupt flag active-low */ - nRet = ~nRet; - } - - return nRet; -} - -/*! ******************************************************** - * @name CMT2300A_ConfigTxDin - * @desc Used to select whether to use GPIO1 or GPIO2 or GPIO3 - * as DIN in the direct mode. It only takes effect when - * call CMT2300A_EnableTxDin(TRUE) in the direct mode. - * @param nDinSel - * CMT2300A_TX_DIN_SEL_GPIO1 - * CMT2300A_TX_DIN_SEL_GPIO2 - * CMT2300A_TX_DIN_SEL_GPIO3 - * *********************************************************/ -void CMT2300A_ConfigTxDin(uint8_t nDinSel) -{ - uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_CTL); - tmp &= ~CMT2300A_MASK_TX_DIN_SEL; - tmp |= nDinSel; - CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CTL, tmp); -} - -/*! ******************************************************** - * @name CMT2300A_EnableTxDin - * @desc Used to change GPIO1/GPIO2/GPIO3 between DOUT and DIN. - * @param bEnable(TRUE): used as DIN - * bEnable(FALSE): used as DOUT(default) - * *********************************************************/ -void CMT2300A_EnableTxDin(bool bEnable) -{ - uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_CTL); - - if (bEnable) - tmp |= CMT2300A_MASK_TX_DIN_EN; - else - tmp &= ~CMT2300A_MASK_TX_DIN_EN; - - CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CTL, tmp); -} - -/*! ******************************************************** - * @name CMT2300A_EnableTxDinInvert - * @desc Used to invert DIN data in direct mode. - * @param bEnable(TRUE): invert DIN - * bEnable(FALSE): not invert DIN(default) - * *********************************************************/ -void CMT2300A_EnableTxDinInvert(bool bEnable) -{ - uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_INT2_CTL); - - if (bEnable) - tmp |= CMT2300A_MASK_TX_DIN_INV; - else - tmp &= ~CMT2300A_MASK_TX_DIN_INV; - - CMT2300A_WriteReg(CMT2300A_CUS_INT2_CTL, tmp); -} - -/*! ******************************************************** - * @name CMT2300A_IsExist - * @desc Chip indentify. - * @return TRUE: chip is exist, FALSE: chip not found - * *********************************************************/ -bool CMT2300A_IsExist(void) -{ - uint8_t back, dat; - - back = CMT2300A_ReadReg(CMT2300A_CUS_PKT17); - CMT2300A_WriteReg(CMT2300A_CUS_PKT17, 0xAA); - - dat = CMT2300A_ReadReg(CMT2300A_CUS_PKT17); - CMT2300A_WriteReg(CMT2300A_CUS_PKT17, back); - - if (0xAA == dat) - return true; - else - return false; -} - -/*! ******************************************************** - * @name CMT2300A_GetRssiCode - * @desc Get RSSI code. - * @return RSSI code - * *********************************************************/ -uint8_t CMT2300A_GetRssiCode(void) -{ - return CMT2300A_ReadReg(CMT2300A_CUS_RSSI_CODE); -} - -/*! ******************************************************** - * @name CMT2300A_GetRssiDBm - * @desc Get RSSI dBm. - * @return dBm - * *********************************************************/ -int CMT2300A_GetRssiDBm(void) -{ - return (int)CMT2300A_ReadReg(CMT2300A_CUS_RSSI_DBM) - 128; -} - -/*! ******************************************************** - * @name CMT2300A_SetFrequencyChannel - * @desc This defines up to 255 frequency channel - * for fast frequency hopping operation. - * @param nChann: the frequency channel - * *********************************************************/ -void CMT2300A_SetFrequencyChannel(uint8_t nChann) -{ - CMT2300A_WriteReg(CMT2300A_CUS_FREQ_CHNL, nChann); -} - -/*! ******************************************************** - * @name CMT2300A_SetFrequencyStep - * @desc This defines the frequency channel step size - * for fast frequency hopping operation. - * One step size is 2.5 kHz. - * @param nOffset: the frequency step - * *********************************************************/ -void CMT2300A_SetFrequencyStep(uint8_t nOffset) -{ - CMT2300A_WriteReg(CMT2300A_CUS_FREQ_OFS, nOffset); -} - -/*! ******************************************************** - * @name CMT2300A_SetPayloadLength - * @desc Set payload length. - * @param nLength - * *********************************************************/ -void CMT2300A_SetPayloadLength(uint16_t nLength) -{ - uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_PKT14); - - tmp &= ~CMT2300A_MASK_PAYLOAD_LENG_10_8; - tmp |= (nLength >> 4) & CMT2300A_MASK_PAYLOAD_LENG_10_8; - CMT2300A_WriteReg(CMT2300A_CUS_PKT14, tmp); - - tmp = nLength & CMT2300A_MASK_PAYLOAD_LENG_7_0; - CMT2300A_WriteReg(CMT2300A_CUS_PKT15, tmp); -} - -/*! ******************************************************** - * @name CMT2300A_EnableLfosc - * @desc If you need use sleep timer, you should enable LFOSC. - * @param bEnable(TRUE): Enable it(default) - * bEnable(FALSE): Disable it - * *********************************************************/ -void CMT2300A_EnableLfosc(bool bEnable) -{ - uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_SYS2); - - if (bEnable) { - tmp |= CMT2300A_MASK_LFOSC_RECAL_EN; - tmp |= CMT2300A_MASK_LFOSC_CAL1_EN; - tmp |= CMT2300A_MASK_LFOSC_CAL2_EN; - } else { - tmp &= ~CMT2300A_MASK_LFOSC_RECAL_EN; - tmp &= ~CMT2300A_MASK_LFOSC_CAL1_EN; - tmp &= ~CMT2300A_MASK_LFOSC_CAL2_EN; - } - - CMT2300A_WriteReg(CMT2300A_CUS_SYS2, tmp); -} - -/*! ******************************************************** - * @name CMT2300A_EnableLfoscOutput - * @desc LFOSC clock is output via GPIO3. - * @param bEnable(TRUE): Enable it - * bEnable(FALSE): Disable it(default) - * *********************************************************/ -void CMT2300A_EnableLfoscOutput(bool bEnable) -{ - uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_INT2_CTL); - - if (bEnable) - tmp |= CMT2300A_MASK_LFOSC_OUT_EN; - else - tmp &= ~CMT2300A_MASK_LFOSC_OUT_EN; - - CMT2300A_WriteReg(CMT2300A_CUS_INT2_CTL, tmp); -} - -/*! ******************************************************** - * @name CMT2300A_EnableAfc - * @desc AFC enable or disanble. - * @param bEnable(TRUE): Enable it - * bEnable(FALSE): Disable it(default) - * *********************************************************/ -void CMT2300A_EnableAfc(bool bEnable) -{ - uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FSK5); - - if (bEnable) - tmp |= 0x10; - else - tmp &= ~0x10; - - CMT2300A_WriteReg(CMT2300A_CUS_FSK5, tmp); -} - -/*! ******************************************************** - * @name CMT2300A_SetAfcOvfTh - * @desc This is optional, only needed when using Rx fast frequency hopping. - * @param afcOvfTh: AFC_OVF_TH see AN142 and AN197 for details. - * *********************************************************/ -void CMT2300A_SetAfcOvfTh(uint8_t afcOvfTh) -{ - CMT2300A_WriteReg(CMT2300A_CUS_FSK4, afcOvfTh); -} - -/*! ******************************************************** - * @name CMT2300A_Init - * @desc Initialize chip status. - * *********************************************************/ -bool CMT2300A_Init(void) -{ - uint8_t tmp; - - CMT2300A_SoftReset(); - CMT2300A_DelayMs(20); - - if (!CMT2300A_GoStby()) - return false; // CMT2300A not switched to standby mode! - - if (!CMT2300A_IsExist()) - return false; // CMT2300A not found! - - tmp = CMT2300A_ReadReg(CMT2300A_CUS_MODE_STA); - tmp |= CMT2300A_MASK_CFG_RETAIN; /* Enable CFG_RETAIN */ - tmp &= ~CMT2300A_MASK_RSTN_IN_EN; /* Disable RSTN_IN */ - CMT2300A_WriteReg(CMT2300A_CUS_MODE_STA, tmp); - - tmp = CMT2300A_ReadReg(CMT2300A_CUS_EN_CTL); - tmp |= CMT2300A_MASK_LOCKING_EN; /* Enable LOCKING_EN */ - CMT2300A_WriteReg(CMT2300A_CUS_EN_CTL, tmp); - - CMT2300A_EnableLfosc(false); /* Disable LFOSC */ - - CMT2300A_ClearInterruptFlags(); - - return true; -} - -/*! ******************************************************** - * @name CMT2300A_ConfigRegBank - * @desc Config one register bank. - * *********************************************************/ -bool CMT2300A_ConfigRegBank(uint8_t base_addr, const uint8_t bank[], uint8_t len) -{ - uint8_t i; - for (i = 0; i < len; i++) - CMT2300A_WriteReg(i + base_addr, bank[i]); - - return true; -} +/* + * THE FOLLOWING FIRMWARE IS PROVIDED: (1) "AS IS" WITH NO WARRANTY; AND + * (2)TO ENABLE ACCESS TO CODING INFORMATION TO GUIDE AND FACILITATE CUSTOMER. + * CONSEQUENTLY, CMOSTEK SHALL NOT BE HELD LIABLE FOR ANY DIRECT, INDIRECT OR + * CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT + * OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION + * CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + * Copyright (C) CMOSTEK SZ. + */ + +/*! + * @file cmt2300a.c + * @brief CMT2300A transceiver RF chip driver + * + * @version 1.3 + * @date Jul 17 2017 + * @author CMOSTEK R@D + */ + +#include "cmt2300a.h" + +/*! ******************************************************** + * @name CMT2300A_SoftReset + * @desc Soft reset. + * *********************************************************/ +void CMT2300A_SoftReset(void) +{ + CMT2300A_WriteReg(0x7F, 0xFF); +} + +/*! ******************************************************** + * @name CMT2300A_GetChipStatus + * @desc Get the chip status. + * @return + * CMT2300A_STA_PUP + * CMT2300A_STA_SLEEP + * CMT2300A_STA_STBY + * CMT2300A_STA_RFS + * CMT2300A_STA_TFS + * CMT2300A_STA_RX + * CMT2300A_STA_TX + * CMT2300A_STA_EEPROM + * CMT2300A_STA_ERROR + * CMT2300A_STA_CAL + * *********************************************************/ +uint8_t CMT2300A_GetChipStatus(void) +{ + return CMT2300A_ReadReg(CMT2300A_CUS_MODE_STA) & CMT2300A_MASK_CHIP_MODE_STA; +} + +/*! ******************************************************** + * @name CMT2300A_AutoSwitchStatus + * @desc Auto switch the chip status, and 10 ms as timeout. + * @param nGoCmd: the chip next status + * @return TRUE or FALSE + * *********************************************************/ +bool CMT2300A_AutoSwitchStatus(uint8_t nGoCmd) +{ +#ifdef ENABLE_AUTO_SWITCH_CHIP_STATUS + uint32_t nBegTick = CMT2300A_GetTickCount(); + uint8_t nWaitStatus = 0; + + switch (nGoCmd) { + case CMT2300A_GO_SLEEP: + nWaitStatus = CMT2300A_STA_SLEEP; + break; + case CMT2300A_GO_STBY: + nWaitStatus = CMT2300A_STA_STBY; + break; + case CMT2300A_GO_TFS: + nWaitStatus = CMT2300A_STA_TFS; + break; + case CMT2300A_GO_TX: + nWaitStatus = CMT2300A_STA_TX; + break; + case CMT2300A_GO_RFS: + nWaitStatus = CMT2300A_STA_RFS; + break; + case CMT2300A_GO_RX: + nWaitStatus = CMT2300A_STA_RX; + break; + } + + CMT2300A_WriteReg(CMT2300A_CUS_MODE_CTL, nGoCmd); + + while (CMT2300A_GetTickCount() - nBegTick < 10) { + CMT2300A_DelayUs(100); + + if (nWaitStatus == CMT2300A_GetChipStatus()) + return true; + + if (CMT2300A_GO_TX == nGoCmd) { + CMT2300A_DelayUs(100); + + if (CMT2300A_MASK_TX_DONE_FLG & CMT2300A_ReadReg(CMT2300A_CUS_INT_CLR1)) + return true; + } + + if (CMT2300A_GO_RX == nGoCmd) { + CMT2300A_DelayUs(100); + + if (CMT2300A_MASK_PKT_OK_FLG & CMT2300A_ReadReg(CMT2300A_CUS_INT_FLAG)) + return true; + } + } + + return false; + +#else + CMT2300A_WriteReg(CMT2300A_CUS_MODE_CTL, nGoCmd); + return true; +#endif +} + +/*! ******************************************************** + * @name CMT2300A_GoSleep + * @desc Entry SLEEP mode. + * @return TRUE or FALSE + * *********************************************************/ +bool CMT2300A_GoSleep(void) +{ + return CMT2300A_AutoSwitchStatus(CMT2300A_GO_SLEEP); +} + +/*! ******************************************************** + * @name CMT2300A_GoStby + * @desc Entry Sleep mode. + * @return TRUE or FALSE + * *********************************************************/ +bool CMT2300A_GoStby(void) +{ + return CMT2300A_AutoSwitchStatus(CMT2300A_GO_STBY); +} + +/*! ******************************************************** + * @name CMT2300A_GoTFS + * @desc Entry TFS mode. + * @return TRUE or FALSE + * *********************************************************/ +bool CMT2300A_GoTFS(void) +{ + return CMT2300A_AutoSwitchStatus(CMT2300A_GO_TFS); +} + +/*! ******************************************************** + * @name CMT2300A_GoRFS + * @desc Entry RFS mode. + * @return TRUE or FALSE + * *********************************************************/ +bool CMT2300A_GoRFS(void) +{ + return CMT2300A_AutoSwitchStatus(CMT2300A_GO_RFS); +} + +/*! ******************************************************** + * @name CMT2300A_GoTx + * @desc Entry Tx mode. + * @return TRUE or FALSE + * *********************************************************/ +bool CMT2300A_GoTx(void) +{ + return CMT2300A_AutoSwitchStatus(CMT2300A_GO_TX); +} + +/*! ******************************************************** + * @name CMT2300A_GoRx + * @desc Entry Rx mode. + * @return TRUE or FALSE + * *********************************************************/ +bool CMT2300A_GoRx(void) +{ + return CMT2300A_AutoSwitchStatus(CMT2300A_GO_RX); +} + +/*! ******************************************************** + * @name CMT2300A_ConfigGpio + * @desc Config GPIO pins mode. + * @param nGpioSel: GPIO1_SEL | GPIO2_SEL | GPIO3_SEL | GPIO4_SEL + * GPIO1_SEL: + * CMT2300A_GPIO1_SEL_DOUT/DIN + * CMT2300A_GPIO1_SEL_INT1 + * CMT2300A_GPIO1_SEL_INT2 + * CMT2300A_GPIO1_SEL_DCLK + * + * GPIO2_SEL: + * CMT2300A_GPIO2_SEL_INT1 + * CMT2300A_GPIO2_SEL_INT2 + * CMT2300A_GPIO2_SEL_DOUT/DIN + * CMT2300A_GPIO2_SEL_DCLK + * + * GPIO3_SEL: + * CMT2300A_GPIO3_SEL_CLKO + * CMT2300A_GPIO3_SEL_DOUT/DIN + * CMT2300A_GPIO3_SEL_INT2 + * CMT2300A_GPIO3_SEL_DCLK + * + * GPIO4_SEL: + * CMT2300A_GPIO4_SEL_RSTIN + * CMT2300A_GPIO4_SEL_INT1 + * CMT2300A_GPIO4_SEL_DOUT + * CMT2300A_GPIO4_SEL_DCLK + * *********************************************************/ +void CMT2300A_ConfigGpio(uint8_t nGpioSel) +{ + CMT2300A_WriteReg(CMT2300A_CUS_IO_SEL, nGpioSel); +} + +/*! ******************************************************** + * @name CMT2300A_ConfigInterrupt + * @desc Config interrupt on INT1 and INT2. + * @param nInt1Sel, nInt2Sel + * CMT2300A_INT_SEL_RX_ACTIVE + * CMT2300A_INT_SEL_TX_ACTIVE + * CMT2300A_INT_SEL_RSSI_VLD + * CMT2300A_INT_SEL_PREAM_OK + * CMT2300A_INT_SEL_SYNC_OK + * CMT2300A_INT_SEL_NODE_OK + * CMT2300A_INT_SEL_CRC_OK + * CMT2300A_INT_SEL_PKT_OK + * CMT2300A_INT_SEL_SL_TMO + * CMT2300A_INT_SEL_RX_TMO + * CMT2300A_INT_SEL_TX_DONE + * CMT2300A_INT_SEL_RX_FIFO_NMTY + * CMT2300A_INT_SEL_RX_FIFO_TH + * CMT2300A_INT_SEL_RX_FIFO_FULL + * CMT2300A_INT_SEL_RX_FIFO_WBYTE + * CMT2300A_INT_SEL_RX_FIFO_OVF + * CMT2300A_INT_SEL_TX_FIFO_NMTY + * CMT2300A_INT_SEL_TX_FIFO_TH + * CMT2300A_INT_SEL_TX_FIFO_FULL + * CMT2300A_INT_SEL_STATE_IS_STBY + * CMT2300A_INT_SEL_STATE_IS_FS + * CMT2300A_INT_SEL_STATE_IS_RX + * CMT2300A_INT_SEL_STATE_IS_TX + * CMT2300A_INT_SEL_LED + * CMT2300A_INT_SEL_TRX_ACTIVE + * CMT2300A_INT_SEL_PKT_DONE + * *********************************************************/ +void CMT2300A_ConfigInterrupt(uint8_t nInt1Sel, uint8_t nInt2Sel) +{ + nInt1Sel &= CMT2300A_MASK_INT1_SEL; + nInt1Sel |= (~CMT2300A_MASK_INT1_SEL) & CMT2300A_ReadReg(CMT2300A_CUS_INT1_CTL); + CMT2300A_WriteReg(CMT2300A_CUS_INT1_CTL, nInt1Sel); + + nInt2Sel &= CMT2300A_MASK_INT2_SEL; + nInt2Sel |= (~CMT2300A_MASK_INT2_SEL) & CMT2300A_ReadReg(CMT2300A_CUS_INT2_CTL); + CMT2300A_WriteReg(CMT2300A_CUS_INT2_CTL, nInt2Sel); +} + +/*! ******************************************************** + * @name CMT2300A_SetInterruptPolar + * @desc Set the polarity of the interrupt. + * @param bEnable(TRUE): active-high (default) + * bEnable(FALSE): active-low + * *********************************************************/ +void CMT2300A_SetInterruptPolar(bool bActiveHigh) +{ + uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_INT1_CTL); + + if (bActiveHigh) + tmp &= ~CMT2300A_MASK_INT_POLAR; + else + tmp |= CMT2300A_MASK_INT_POLAR; + + CMT2300A_WriteReg(CMT2300A_CUS_INT1_CTL, tmp); +} + +/*! ******************************************************** + * @name CMT2300A_SetFifoThreshold + * @desc Set FIFO threshold. + * @param nFifoThreshold + * *********************************************************/ +void CMT2300A_SetFifoThreshold(uint8_t nFifoThreshold) +{ + uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_PKT29); + + tmp &= ~CMT2300A_MASK_FIFO_TH; + tmp |= nFifoThreshold & CMT2300A_MASK_FIFO_TH; + + CMT2300A_WriteReg(CMT2300A_CUS_PKT29, tmp); +} + +/*! ******************************************************** + * @name CMT2300A_EnableAntennaSwitch + * @desc Enable antenna switch, output TX_ACTIVE/RX_ACTIVE + * via GPIO1/GPIO2. + * @param nMode + * 0: RF_SWT1_EN=1, RF_SWT2_EN=0 + * GPIO1: RX_ACTIVE, GPIO2: TX_ACTIVE + * 1: RF_SWT1_EN=0, RF_SWT2_EN=1 + * GPIO1: RX_ACTIVE, GPIO2: ~RX_ACTIVE + * *********************************************************/ +void CMT2300A_EnableAntennaSwitch(uint8_t nMode) +{ + uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_INT1_CTL); + + if (0 == nMode) { + tmp |= CMT2300A_MASK_RF_SWT1_EN; + tmp &= ~CMT2300A_MASK_RF_SWT2_EN; + } else if (1 == nMode) { + tmp &= ~CMT2300A_MASK_RF_SWT1_EN; + tmp |= CMT2300A_MASK_RF_SWT2_EN; + } + + CMT2300A_WriteReg(CMT2300A_CUS_INT1_CTL, tmp); +} + +/*! ******************************************************** + * @name CMT2300A_EnableInterrupt + * @desc Enable interrupt. + * @param nEnable + * CMT2300A_MASK_SL_TMO_EN | + * CMT2300A_MASK_RX_TMO_EN | + * CMT2300A_MASK_TX_DONE_EN | + * CMT2300A_MASK_PREAM_OK_EN | + * CMT2300A_MASK_SYNC_OK_EN | + * CMT2300A_MASK_NODE_OK_EN | + * CMT2300A_MASK_CRC_OK_EN | + * CMT2300A_MASK_PKT_DONE_EN + * *********************************************************/ +void CMT2300A_EnableInterrupt(uint8_t nEnable) +{ + CMT2300A_WriteReg(CMT2300A_CUS_INT_EN, nEnable); +} + +/*! ******************************************************** + * @name CMT2300A_EnableRxFifoAutoClear + * @desc Auto clear Rx FIFO before entry Rx mode. + * @param bEnable(TRUE): Enable it(default) + * bEnable(FALSE): Disable it + * *********************************************************/ +void CMT2300A_EnableRxFifoAutoClear(bool bEnable) +{ + uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_CTL); + + if (bEnable) + tmp &= ~CMT2300A_MASK_FIFO_AUTO_CLR_DIS; + else + tmp |= CMT2300A_MASK_FIFO_AUTO_CLR_DIS; + + CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CTL, tmp); +} + +/*! ******************************************************** + * @name CMT2300A_EnableFifoMerge + * @desc Enable FIFO merge. + * @param bEnable(TRUE): use a single 64-byte FIFO for either Tx or Rx + * bEnable(FALSE): use a 32-byte FIFO for Tx and another 32-byte FIFO for Rx(default) + * *********************************************************/ +void CMT2300A_EnableFifoMerge(bool bEnable) +{ + uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_CTL); + + if (bEnable) + tmp |= CMT2300A_MASK_FIFO_MERGE_EN; + else + tmp &= ~CMT2300A_MASK_FIFO_MERGE_EN; + + CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CTL, tmp); +} + +/*! ******************************************************** + * @name CMT2300A_EnableReadFifo + * @desc Enable SPI to read the FIFO. + * *********************************************************/ +void CMT2300A_EnableReadFifo(void) +{ + uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_CTL); + tmp &= ~CMT2300A_MASK_SPI_FIFO_RD_WR_SEL; + tmp &= ~CMT2300A_MASK_FIFO_RX_TX_SEL; + CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CTL, tmp); +} + +/*! ******************************************************** + * @name CMT2300A_EnableWriteFifo + * @desc Enable SPI to write the FIFO. + * *********************************************************/ +void CMT2300A_EnableWriteFifo(void) +{ + uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_CTL); + tmp |= CMT2300A_MASK_SPI_FIFO_RD_WR_SEL; + tmp |= CMT2300A_MASK_FIFO_RX_TX_SEL; + CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CTL, tmp); +} + +/*! ******************************************************** + * @name CMT2300A_RestoreFifo + * @desc Restore the FIFO. + * *********************************************************/ +void CMT2300A_RestoreFifo(void) +{ + CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CLR, CMT2300A_MASK_FIFO_RESTORE); +} + +/*! ******************************************************** + * @name CMT2300A_ClearFifo + * @desc Clear the Tx FIFO. + * @return FIFO flags + * CMT2300A_MASK_RX_FIFO_FULL_FLG | + * CMT2300A_MASK_RX_FIFO_NMTY_FLG | + * CMT2300A_MASK_RX_FIFO_TH_FLG | + * CMT2300A_MASK_RX_FIFO_OVF_FLG | + * CMT2300A_MASK_TX_FIFO_FULL_FLG | + * CMT2300A_MASK_TX_FIFO_NMTY_FLG | + * CMT2300A_MASK_TX_FIFO_TH_FLG + * *********************************************************/ +uint8_t CMT2300A_ClearTxFifo(void) +{ + uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_FLAG); + CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CLR, CMT2300A_MASK_FIFO_CLR_TX); + return tmp; +} + +/*! ******************************************************** + * @name CMT2300A_ClearFifo + * @desc Clear the Rx FIFO. + * @return FIFO flags + * CMT2300A_MASK_RX_FIFO_FULL_FLG | + * CMT2300A_MASK_RX_FIFO_NMTY_FLG | + * CMT2300A_MASK_RX_FIFO_TH_FLG | + * CMT2300A_MASK_RX_FIFO_OVF_FLG | + * CMT2300A_MASK_TX_FIFO_FULL_FLG | + * CMT2300A_MASK_TX_FIFO_NMTY_FLG | + * CMT2300A_MASK_TX_FIFO_TH_FLG + * *********************************************************/ +uint8_t CMT2300A_ClearRxFifo(void) +{ + uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_FLAG); + CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CLR, CMT2300A_MASK_FIFO_CLR_RX); + return tmp; +} + +/*! ******************************************************** + * @name CMT2300A_ClearInterruptFlags + * @desc Clear all interrupt flags. + * @return Some interrupt flags + * CMT2300A_MASK_SL_TMO_EN | + * CMT2300A_MASK_RX_TMO_EN | + * CMT2300A_MASK_TX_DONE_EN | + * CMT2300A_MASK_PREAM_OK_FLG | + * CMT2300A_MASK_SYNC_OK_FLG | + * CMT2300A_MASK_NODE_OK_FLG | + * CMT2300A_MASK_CRC_OK_FLG | + * CMT2300A_MASK_PKT_OK_FLG + * *********************************************************/ +uint8_t CMT2300A_ClearInterruptFlags(void) +{ + uint8_t nFlag1, nFlag2; + uint8_t nClr1 = 0; + uint8_t nClr2 = 0; + uint8_t nRet = 0; + uint8_t nIntPolar; + + nIntPolar = CMT2300A_ReadReg(CMT2300A_CUS_INT1_CTL); + nIntPolar = (nIntPolar & CMT2300A_MASK_INT_POLAR) ? 1 : 0; + + nFlag1 = CMT2300A_ReadReg(CMT2300A_CUS_INT_FLAG); + nFlag2 = CMT2300A_ReadReg(CMT2300A_CUS_INT_CLR1); + + if (nIntPolar) { + /* Interrupt flag active-low */ + nFlag1 = ~nFlag1; + nFlag2 = ~nFlag2; + } + + if (CMT2300A_MASK_LBD_FLG & nFlag1) { + nClr2 |= CMT2300A_MASK_LBD_CLR; /* Clear LBD_FLG */ + } + + if (CMT2300A_MASK_COL_ERR_FLG & nFlag1) { + nClr2 |= CMT2300A_MASK_PKT_DONE_CLR; /* Clear COL_ERR_FLG by PKT_DONE_CLR */ + } + + if (CMT2300A_MASK_PKT_ERR_FLG & nFlag1) { + nClr2 |= CMT2300A_MASK_PKT_DONE_CLR; /* Clear PKT_ERR_FLG by PKT_DONE_CLR */ + } + + if (CMT2300A_MASK_PREAM_OK_FLG & nFlag1) { + nClr2 |= CMT2300A_MASK_PREAM_OK_CLR; /* Clear PREAM_OK_FLG */ + nRet |= CMT2300A_MASK_PREAM_OK_FLG; /* Return PREAM_OK_FLG */ + } + + if (CMT2300A_MASK_SYNC_OK_FLG & nFlag1) { + nClr2 |= CMT2300A_MASK_SYNC_OK_CLR; /* Clear SYNC_OK_FLG */ + nRet |= CMT2300A_MASK_SYNC_OK_FLG; /* Return SYNC_OK_FLG */ + } + + if (CMT2300A_MASK_NODE_OK_FLG & nFlag1) { + nClr2 |= CMT2300A_MASK_NODE_OK_CLR; /* Clear NODE_OK_FLG */ + nRet |= CMT2300A_MASK_NODE_OK_FLG; /* Return NODE_OK_FLG */ + } + + if (CMT2300A_MASK_CRC_OK_FLG & nFlag1) { + nClr2 |= CMT2300A_MASK_CRC_OK_CLR; /* Clear CRC_OK_FLG */ + nRet |= CMT2300A_MASK_CRC_OK_FLG; /* Return CRC_OK_FLG */ + } + + if (CMT2300A_MASK_PKT_OK_FLG & nFlag1) { + nClr2 |= CMT2300A_MASK_PKT_DONE_CLR; /* Clear PKT_OK_FLG */ + nRet |= CMT2300A_MASK_PKT_OK_FLG; /* Return PKT_OK_FLG */ + } + + if (CMT2300A_MASK_SL_TMO_FLG & nFlag2) { + nClr1 |= CMT2300A_MASK_SL_TMO_CLR; /* Clear SL_TMO_FLG */ + nRet |= CMT2300A_MASK_SL_TMO_EN; /* Return SL_TMO_FLG by SL_TMO_EN */ + } + + if (CMT2300A_MASK_RX_TMO_FLG & nFlag2) { + nClr1 |= CMT2300A_MASK_RX_TMO_CLR; /* Clear RX_TMO_FLG */ + nRet |= CMT2300A_MASK_RX_TMO_EN; /* Return RX_TMO_FLG by RX_TMO_EN */ + } + + if (CMT2300A_MASK_TX_DONE_FLG & nFlag2) { + nClr1 |= CMT2300A_MASK_TX_DONE_CLR; /* Clear TX_DONE_FLG */ + nRet |= CMT2300A_MASK_TX_DONE_EN; /* Return TX_DONE_FLG by TX_DONE_EN */ + } + + CMT2300A_WriteReg(CMT2300A_CUS_INT_CLR1, nClr1); + CMT2300A_WriteReg(CMT2300A_CUS_INT_CLR2, nClr2); + + if (nIntPolar) { + /* Interrupt flag active-low */ + nRet = ~nRet; + } + + return nRet; +} + +/*! ******************************************************** + * @name CMT2300A_ConfigTxDin + * @desc Used to select whether to use GPIO1 or GPIO2 or GPIO3 + * as DIN in the direct mode. It only takes effect when + * call CMT2300A_EnableTxDin(TRUE) in the direct mode. + * @param nDinSel + * CMT2300A_TX_DIN_SEL_GPIO1 + * CMT2300A_TX_DIN_SEL_GPIO2 + * CMT2300A_TX_DIN_SEL_GPIO3 + * *********************************************************/ +void CMT2300A_ConfigTxDin(uint8_t nDinSel) +{ + uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_CTL); + tmp &= ~CMT2300A_MASK_TX_DIN_SEL; + tmp |= nDinSel; + CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CTL, tmp); +} + +/*! ******************************************************** + * @name CMT2300A_EnableTxDin + * @desc Used to change GPIO1/GPIO2/GPIO3 between DOUT and DIN. + * @param bEnable(TRUE): used as DIN + * bEnable(FALSE): used as DOUT(default) + * *********************************************************/ +void CMT2300A_EnableTxDin(bool bEnable) +{ + uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_CTL); + + if (bEnable) + tmp |= CMT2300A_MASK_TX_DIN_EN; + else + tmp &= ~CMT2300A_MASK_TX_DIN_EN; + + CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CTL, tmp); +} + +/*! ******************************************************** + * @name CMT2300A_EnableTxDinInvert + * @desc Used to invert DIN data in direct mode. + * @param bEnable(TRUE): invert DIN + * bEnable(FALSE): not invert DIN(default) + * *********************************************************/ +void CMT2300A_EnableTxDinInvert(bool bEnable) +{ + uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_INT2_CTL); + + if (bEnable) + tmp |= CMT2300A_MASK_TX_DIN_INV; + else + tmp &= ~CMT2300A_MASK_TX_DIN_INV; + + CMT2300A_WriteReg(CMT2300A_CUS_INT2_CTL, tmp); +} + +/*! ******************************************************** + * @name CMT2300A_IsExist + * @desc Chip indentify. + * @return TRUE: chip is exist, FALSE: chip not found + * *********************************************************/ +bool CMT2300A_IsExist(void) +{ + uint8_t back, dat; + + back = CMT2300A_ReadReg(CMT2300A_CUS_PKT17); + CMT2300A_WriteReg(CMT2300A_CUS_PKT17, 0xAA); + + dat = CMT2300A_ReadReg(CMT2300A_CUS_PKT17); + CMT2300A_WriteReg(CMT2300A_CUS_PKT17, back); + + if (0xAA == dat) + return true; + else + return false; +} + +/*! ******************************************************** + * @name CMT2300A_GetRssiCode + * @desc Get RSSI code. + * @return RSSI code + * *********************************************************/ +uint8_t CMT2300A_GetRssiCode(void) +{ + return CMT2300A_ReadReg(CMT2300A_CUS_RSSI_CODE); +} + +/*! ******************************************************** + * @name CMT2300A_GetRssiDBm + * @desc Get RSSI dBm. + * @return dBm + * *********************************************************/ +int CMT2300A_GetRssiDBm(void) +{ + return (int)CMT2300A_ReadReg(CMT2300A_CUS_RSSI_DBM) - 128; +} + +/*! ******************************************************** + * @name CMT2300A_SetFrequencyChannel + * @desc This defines up to 255 frequency channel + * for fast frequency hopping operation. + * @param nChann: the frequency channel + * *********************************************************/ +void CMT2300A_SetFrequencyChannel(const uint8_t nChann) +{ + CMT2300A_WriteReg(CMT2300A_CUS_FREQ_CHNL, nChann); +} + +/*! ******************************************************** + * @name CMT2300A_SetFrequencyStep + * @desc This defines the frequency channel step size + * for fast frequency hopping operation. + * One step size is 2.5 kHz. + * @param nOffset: the frequency step + * *********************************************************/ +void CMT2300A_SetFrequencyStep(uint8_t nOffset) +{ + CMT2300A_WriteReg(CMT2300A_CUS_FREQ_OFS, nOffset); +} + +/*! ******************************************************** + * @name CMT2300A_SetPayloadLength + * @desc Set payload length. + * @param nLength + * *********************************************************/ +void CMT2300A_SetPayloadLength(uint16_t nLength) +{ + uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_PKT14); + + tmp &= ~CMT2300A_MASK_PAYLOAD_LENG_10_8; + tmp |= (nLength >> 4) & CMT2300A_MASK_PAYLOAD_LENG_10_8; + CMT2300A_WriteReg(CMT2300A_CUS_PKT14, tmp); + + tmp = nLength & CMT2300A_MASK_PAYLOAD_LENG_7_0; + CMT2300A_WriteReg(CMT2300A_CUS_PKT15, tmp); +} + +/*! ******************************************************** + * @name CMT2300A_EnableLfosc + * @desc If you need use sleep timer, you should enable LFOSC. + * @param bEnable(TRUE): Enable it(default) + * bEnable(FALSE): Disable it + * *********************************************************/ +void CMT2300A_EnableLfosc(bool bEnable) +{ + uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_SYS2); + + if (bEnable) { + tmp |= CMT2300A_MASK_LFOSC_RECAL_EN; + tmp |= CMT2300A_MASK_LFOSC_CAL1_EN; + tmp |= CMT2300A_MASK_LFOSC_CAL2_EN; + } else { + tmp &= ~CMT2300A_MASK_LFOSC_RECAL_EN; + tmp &= ~CMT2300A_MASK_LFOSC_CAL1_EN; + tmp &= ~CMT2300A_MASK_LFOSC_CAL2_EN; + } + + CMT2300A_WriteReg(CMT2300A_CUS_SYS2, tmp); +} + +/*! ******************************************************** + * @name CMT2300A_EnableLfoscOutput + * @desc LFOSC clock is output via GPIO3. + * @param bEnable(TRUE): Enable it + * bEnable(FALSE): Disable it(default) + * *********************************************************/ +void CMT2300A_EnableLfoscOutput(bool bEnable) +{ + uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_INT2_CTL); + + if (bEnable) + tmp |= CMT2300A_MASK_LFOSC_OUT_EN; + else + tmp &= ~CMT2300A_MASK_LFOSC_OUT_EN; + + CMT2300A_WriteReg(CMT2300A_CUS_INT2_CTL, tmp); +} + +/*! ******************************************************** + * @name CMT2300A_EnableAfc + * @desc AFC enable or disanble. + * @param bEnable(TRUE): Enable it + * bEnable(FALSE): Disable it(default) + * *********************************************************/ +void CMT2300A_EnableAfc(bool bEnable) +{ + uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FSK5); + + if (bEnable) + tmp |= 0x10; + else + tmp &= ~0x10; + + CMT2300A_WriteReg(CMT2300A_CUS_FSK5, tmp); +} + +/*! ******************************************************** + * @name CMT2300A_SetAfcOvfTh + * @desc This is optional, only needed when using Rx fast frequency hopping. + * @param afcOvfTh: AFC_OVF_TH see AN142 and AN197 for details. + * *********************************************************/ +void CMT2300A_SetAfcOvfTh(uint8_t afcOvfTh) +{ + CMT2300A_WriteReg(CMT2300A_CUS_FSK4, afcOvfTh); +} + +/*! ******************************************************** + * @name CMT2300A_Init + * @desc Initialize chip status. + * *********************************************************/ +bool CMT2300A_Init(void) +{ + uint8_t tmp; + + CMT2300A_SoftReset(); + CMT2300A_DelayMs(20); + + if (!CMT2300A_GoStby()) + return false; // CMT2300A not switched to standby mode! + + if (!CMT2300A_IsExist()) + return false; // CMT2300A not found! + + tmp = CMT2300A_ReadReg(CMT2300A_CUS_MODE_STA); + tmp |= CMT2300A_MASK_CFG_RETAIN; /* Enable CFG_RETAIN */ + tmp &= ~CMT2300A_MASK_RSTN_IN_EN; /* Disable RSTN_IN */ + CMT2300A_WriteReg(CMT2300A_CUS_MODE_STA, tmp); + + tmp = CMT2300A_ReadReg(CMT2300A_CUS_EN_CTL); + tmp |= CMT2300A_MASK_LOCKING_EN; /* Enable LOCKING_EN */ + CMT2300A_WriteReg(CMT2300A_CUS_EN_CTL, tmp); + + CMT2300A_EnableLfosc(false); /* Disable LFOSC */ + + CMT2300A_ClearInterruptFlags(); + + return true; +} + +/*! ******************************************************** + * @name CMT2300A_ConfigRegBank + * @desc Config one register bank. + * *********************************************************/ +bool CMT2300A_ConfigRegBank(uint8_t base_addr, const uint8_t bank[], uint8_t len) +{ + uint8_t i; + for (i = 0; i < len; i++) + CMT2300A_WriteReg(i + base_addr, bank[i]); + + return true; +} diff --git a/lib/CMT2300a/cmt2300a.h b/lib/CMT2300a/cmt2300a.h index e6d484d81..1f5cdd0a4 100644 --- a/lib/CMT2300a/cmt2300a.h +++ b/lib/CMT2300a/cmt2300a.h @@ -1,96 +1,96 @@ -/* - * THE FOLLOWING FIRMWARE IS PROVIDED: (1) "AS IS" WITH NO WARRANTY; AND - * (2)TO ENABLE ACCESS TO CODING INFORMATION TO GUIDE AND FACILITATE CUSTOMER. - * CONSEQUENTLY, CMOSTEK SHALL NOT BE HELD LIABLE FOR ANY DIRECT, INDIRECT OR - * CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT - * OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION - * CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. - * - * Copyright (C) CMOSTEK SZ. - */ - -/*! - * @file cmt2300a.h - * @brief CMT2300A transceiver RF chip driver - * - * @version 1.3 - * @date Jul 17 2017 - * @author CMOSTEK R@D - */ - -#ifndef __CMT2300A_H -#define __CMT2300A_H - -#include "cmt2300a_defs.h" -#include "cmt2300a_hal.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define ENABLE_AUTO_SWITCH_CHIP_STATUS /* Enable the auto switch chip status */ - -/* ************************************************************************ - The following are for chip status controls. -* ************************************************************************ */ -void CMT2300A_SoftReset(void); -uint8_t CMT2300A_GetChipStatus(void); -bool CMT2300A_AutoSwitchStatus(uint8_t nGoCmd); -bool CMT2300A_GoSleep(void); -bool CMT2300A_GoStby(void); -bool CMT2300A_GoTFS(void); -bool CMT2300A_GoRFS(void); -bool CMT2300A_GoTx(void); -bool CMT2300A_GoRx(void); - -/* ************************************************************************ - * The following are for chip interrupts, GPIO, FIFO operations. - * ************************************************************************ */ -void CMT2300A_ConfigGpio(uint8_t nGpioSel); -void CMT2300A_ConfigInterrupt(uint8_t nInt1Sel, uint8_t nInt2Sel); -void CMT2300A_SetInterruptPolar(bool bActiveHigh); -void CMT2300A_SetFifoThreshold(uint8_t nFifoThreshold); -void CMT2300A_EnableAntennaSwitch(uint8_t nMode); -void CMT2300A_EnableInterrupt(uint8_t nEnable); -void CMT2300A_EnableRxFifoAutoClear(bool bEnable); -void CMT2300A_EnableFifoMerge(bool bEnable); -void CMT2300A_EnableReadFifo(void); -void CMT2300A_EnableWriteFifo(void); -void CMT2300A_RestoreFifo(void); -uint8_t CMT2300A_ClearTxFifo(void); -uint8_t CMT2300A_ClearRxFifo(void); -uint8_t CMT2300A_ClearInterruptFlags(void); - -/* ************************************************************************ - * The following are for Tx DIN operations in direct mode. - * ************************************************************************ */ -void CMT2300A_ConfigTxDin(uint8_t nDinSel); -void CMT2300A_EnableTxDin(bool bEnable); -void CMT2300A_EnableTxDinInvert(bool bEnable); - -/* ************************************************************************ - * The following are general operations. - * ************************************************************************ */ -bool CMT2300A_IsExist(void); -uint8_t CMT2300A_GetRssiCode(void); -int CMT2300A_GetRssiDBm(void); -void CMT2300A_SetFrequencyChannel(uint8_t nChann); -void CMT2300A_SetFrequencyStep(uint8_t nOffset); -void CMT2300A_SetPayloadLength(uint16_t nLength); -void CMT2300A_EnableLfosc(bool bEnable); -void CMT2300A_EnableLfoscOutput(bool bEnable); -void CMT2300A_EnableAfc(bool bEnable); -void CMT2300A_SetAfcOvfTh(uint8_t afcOvfTh); - -/* ************************************************************************ - * The following are for chip initializes. - * ************************************************************************ */ -bool CMT2300A_Init(void); -bool CMT2300A_ConfigRegBank(uint8_t base_addr, const uint8_t bank[], uint8_t len); - -#ifdef __cplusplus -} -#endif - -#endif +/* + * THE FOLLOWING FIRMWARE IS PROVIDED: (1) "AS IS" WITH NO WARRANTY; AND + * (2)TO ENABLE ACCESS TO CODING INFORMATION TO GUIDE AND FACILITATE CUSTOMER. + * CONSEQUENTLY, CMOSTEK SHALL NOT BE HELD LIABLE FOR ANY DIRECT, INDIRECT OR + * CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT + * OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION + * CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + * Copyright (C) CMOSTEK SZ. + */ + +/*! + * @file cmt2300a.h + * @brief CMT2300A transceiver RF chip driver + * + * @version 1.3 + * @date Jul 17 2017 + * @author CMOSTEK R@D + */ + +#ifndef __CMT2300A_H +#define __CMT2300A_H + +#include "cmt2300a_defs.h" +#include "cmt2300a_hal.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ENABLE_AUTO_SWITCH_CHIP_STATUS /* Enable the auto switch chip status */ + +/* ************************************************************************ + The following are for chip status controls. +* ************************************************************************ */ +void CMT2300A_SoftReset(void); +uint8_t CMT2300A_GetChipStatus(void); +bool CMT2300A_AutoSwitchStatus(uint8_t nGoCmd); +bool CMT2300A_GoSleep(void); +bool CMT2300A_GoStby(void); +bool CMT2300A_GoTFS(void); +bool CMT2300A_GoRFS(void); +bool CMT2300A_GoTx(void); +bool CMT2300A_GoRx(void); + +/* ************************************************************************ + * The following are for chip interrupts, GPIO, FIFO operations. + * ************************************************************************ */ +void CMT2300A_ConfigGpio(uint8_t nGpioSel); +void CMT2300A_ConfigInterrupt(uint8_t nInt1Sel, uint8_t nInt2Sel); +void CMT2300A_SetInterruptPolar(bool bActiveHigh); +void CMT2300A_SetFifoThreshold(uint8_t nFifoThreshold); +void CMT2300A_EnableAntennaSwitch(uint8_t nMode); +void CMT2300A_EnableInterrupt(uint8_t nEnable); +void CMT2300A_EnableRxFifoAutoClear(bool bEnable); +void CMT2300A_EnableFifoMerge(bool bEnable); +void CMT2300A_EnableReadFifo(void); +void CMT2300A_EnableWriteFifo(void); +void CMT2300A_RestoreFifo(void); +uint8_t CMT2300A_ClearTxFifo(void); +uint8_t CMT2300A_ClearRxFifo(void); +uint8_t CMT2300A_ClearInterruptFlags(void); + +/* ************************************************************************ + * The following are for Tx DIN operations in direct mode. + * ************************************************************************ */ +void CMT2300A_ConfigTxDin(uint8_t nDinSel); +void CMT2300A_EnableTxDin(bool bEnable); +void CMT2300A_EnableTxDinInvert(bool bEnable); + +/* ************************************************************************ + * The following are general operations. + * ************************************************************************ */ +bool CMT2300A_IsExist(void); +uint8_t CMT2300A_GetRssiCode(void); +int CMT2300A_GetRssiDBm(void); +void CMT2300A_SetFrequencyChannel(const uint8_t nChann); +void CMT2300A_SetFrequencyStep(uint8_t nOffset); +void CMT2300A_SetPayloadLength(uint16_t nLength); +void CMT2300A_EnableLfosc(bool bEnable); +void CMT2300A_EnableLfoscOutput(bool bEnable); +void CMT2300A_EnableAfc(bool bEnable); +void CMT2300A_SetAfcOvfTh(uint8_t afcOvfTh); + +/* ************************************************************************ + * The following are for chip initializes. + * ************************************************************************ */ +bool CMT2300A_Init(void); +bool CMT2300A_ConfigRegBank(uint8_t base_addr, const uint8_t bank[], uint8_t len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/CMT2300a/cmt2300a_hal.c b/lib/CMT2300a/cmt2300a_hal.c index 7bf1b60ff..7b07d499f 100644 --- a/lib/CMT2300a/cmt2300a_hal.c +++ b/lib/CMT2300a/cmt2300a_hal.c @@ -1,76 +1,76 @@ -/* - * THE FOLLOWING FIRMWARE IS PROVIDED: (1) "AS IS" WITH NO WARRANTY; AND - * (2)TO ENABLE ACCESS TO CODING INFORMATION TO GUIDE AND FACILITATE CUSTOMER. - * CONSEQUENTLY, CMOSTEK SHALL NOT BE HELD LIABLE FOR ANY DIRECT, INDIRECT OR - * CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT - * OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION - * CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. - * - * Copyright (C) CMOSTEK SZ. - */ - -/*! - * @file cmt2300a_hal.c - * @brief CMT2300A hardware abstraction layer - * - * @version 1.2 - * @date Jul 17 2017 - * @author CMOSTEK R@D - */ - -#include "cmt2300a_hal.h" -#include "cmt_spi3.h" -#include - -/*! ******************************************************** - * @name CMT2300A_InitSpi - * @desc Initializes the CMT2300A SPI interface. - * *********************************************************/ -void CMT2300A_InitSpi(int8_t pin_sdio, int8_t pin_clk, int8_t pin_cs, int8_t pin_fcs, uint32_t spi_speed) -{ - cmt_spi3_init(pin_sdio, pin_clk, pin_cs, pin_fcs, spi_speed); -} - -/*! ******************************************************** - * @name CMT2300A_ReadReg - * @desc Read the CMT2300A register at the specified address. - * @param addr: register address - * @return Register value - * *********************************************************/ -uint8_t CMT2300A_ReadReg(uint8_t addr) -{ - return cmt_spi3_read(addr); -} - -/*! ******************************************************** - * @name CMT2300A_WriteReg - * @desc Write the CMT2300A register at the specified address. - * @param addr: register address - * dat: register value - * *********************************************************/ -void CMT2300A_WriteReg(uint8_t addr, uint8_t dat) -{ - cmt_spi3_write(addr, dat); -} - -/*! ******************************************************** - * @name CMT2300A_ReadFifo - * @desc Reads the contents of the CMT2300A FIFO. - * @param buf: buffer where to copy the FIFO read data - * len: number of bytes to be read from the FIFO - * *********************************************************/ -void CMT2300A_ReadFifo(uint8_t buf[], uint16_t len) -{ - cmt_spi3_read_fifo(buf, len); -} - -/*! ******************************************************** - * @name CMT2300A_WriteFifo - * @desc Writes the buffer contents to the CMT2300A FIFO. - * @param buf: buffer containing data to be put on the FIFO - * len: number of bytes to be written to the FIFO - * *********************************************************/ -void CMT2300A_WriteFifo(const uint8_t buf[], uint16_t len) -{ - cmt_spi3_write_fifo(buf, len); -} +/* + * THE FOLLOWING FIRMWARE IS PROVIDED: (1) "AS IS" WITH NO WARRANTY; AND + * (2)TO ENABLE ACCESS TO CODING INFORMATION TO GUIDE AND FACILITATE CUSTOMER. + * CONSEQUENTLY, CMOSTEK SHALL NOT BE HELD LIABLE FOR ANY DIRECT, INDIRECT OR + * CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT + * OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION + * CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + * Copyright (C) CMOSTEK SZ. + */ + +/*! + * @file cmt2300a_hal.c + * @brief CMT2300A hardware abstraction layer + * + * @version 1.2 + * @date Jul 17 2017 + * @author CMOSTEK R@D + */ + +#include "cmt2300a_hal.h" +#include "cmt_spi3.h" +#include + +/*! ******************************************************** + * @name CMT2300A_InitSpi + * @desc Initializes the CMT2300A SPI interface. + * *********************************************************/ +void CMT2300A_InitSpi(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed) +{ + cmt_spi3_init(pin_sdio, pin_clk, pin_cs, pin_fcs, spi_speed); +} + +/*! ******************************************************** + * @name CMT2300A_ReadReg + * @desc Read the CMT2300A register at the specified address. + * @param addr: register address + * @return Register value + * *********************************************************/ +uint8_t CMT2300A_ReadReg(const uint8_t addr) +{ + return cmt_spi3_read(addr); +} + +/*! ******************************************************** + * @name CMT2300A_WriteReg + * @desc Write the CMT2300A register at the specified address. + * @param addr: register address + * dat: register value + * *********************************************************/ +void CMT2300A_WriteReg(const uint8_t addr, const uint8_t dat) +{ + cmt_spi3_write(addr, dat); +} + +/*! ******************************************************** + * @name CMT2300A_ReadFifo + * @desc Reads the contents of the CMT2300A FIFO. + * @param buf: buffer where to copy the FIFO read data + * len: number of bytes to be read from the FIFO + * *********************************************************/ +void CMT2300A_ReadFifo(uint8_t buf[], const uint16_t len) +{ + cmt_spi3_read_fifo(buf, len); +} + +/*! ******************************************************** + * @name CMT2300A_WriteFifo + * @desc Writes the buffer contents to the CMT2300A FIFO. + * @param buf: buffer containing data to be put on the FIFO + * len: number of bytes to be written to the FIFO + * *********************************************************/ +void CMT2300A_WriteFifo(const uint8_t buf[], const uint16_t len) +{ + cmt_spi3_write_fifo(buf, len); +} diff --git a/lib/CMT2300a/cmt2300a_hal.h b/lib/CMT2300a/cmt2300a_hal.h index 1d6e1f4f3..a465b1490 100644 --- a/lib/CMT2300a/cmt2300a_hal.h +++ b/lib/CMT2300a/cmt2300a_hal.h @@ -1,51 +1,51 @@ -/* - * THE FOLLOWING FIRMWARE IS PROVIDED: (1) "AS IS" WITH NO WARRANTY; AND - * (2)TO ENABLE ACCESS TO CODING INFORMATION TO GUIDE AND FACILITATE CUSTOMER. - * CONSEQUENTLY, CMOSTEK SHALL NOT BE HELD LIABLE FOR ANY DIRECT, INDIRECT OR - * CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT - * OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION - * CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. - * - * Copyright (C) CMOSTEK SZ. - */ - -/*! - * @file cmt2300a_hal.h - * @brief CMT2300A hardware abstraction layer - * - * @version 1.2 - * @date Jul 17 2017 - * @author CMOSTEK R@D - */ - -#ifndef __CMT2300A_HAL_H -#define __CMT2300A_HAL_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* ************************************************************************ - * The following need to be modified by user - * ************************************************************************ */ -#define CMT2300A_DelayMs(ms) delay(ms) -#define CMT2300A_DelayUs(us) delayMicroseconds(us) -#define CMT2300A_GetTickCount() millis() -/* ************************************************************************ */ - -void CMT2300A_InitSpi(int8_t pin_sdio, int8_t pin_clk, int8_t pin_cs, int8_t pin_fcs, uint32_t spi_speed); - -uint8_t CMT2300A_ReadReg(uint8_t addr); -void CMT2300A_WriteReg(uint8_t addr, uint8_t dat); - -void CMT2300A_ReadFifo(uint8_t buf[], uint16_t len); -void CMT2300A_WriteFifo(const uint8_t buf[], uint16_t len); - -#ifdef __cplusplus -} -#endif - -#endif +/* + * THE FOLLOWING FIRMWARE IS PROVIDED: (1) "AS IS" WITH NO WARRANTY; AND + * (2)TO ENABLE ACCESS TO CODING INFORMATION TO GUIDE AND FACILITATE CUSTOMER. + * CONSEQUENTLY, CMOSTEK SHALL NOT BE HELD LIABLE FOR ANY DIRECT, INDIRECT OR + * CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT + * OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION + * CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + * Copyright (C) CMOSTEK SZ. + */ + +/*! + * @file cmt2300a_hal.h + * @brief CMT2300A hardware abstraction layer + * + * @version 1.2 + * @date Jul 17 2017 + * @author CMOSTEK R@D + */ + +#ifndef __CMT2300A_HAL_H +#define __CMT2300A_HAL_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ************************************************************************ + * The following need to be modified by user + * ************************************************************************ */ +#define CMT2300A_DelayMs(ms) delay(ms) +#define CMT2300A_DelayUs(us) delayMicroseconds(us) +#define CMT2300A_GetTickCount() millis() +/* ************************************************************************ */ + +void CMT2300A_InitSpi(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed); + +uint8_t CMT2300A_ReadReg(const uint8_t addr); +void CMT2300A_WriteReg(const uint8_t addr, const uint8_t dat); + +void CMT2300A_ReadFifo(uint8_t buf[], const uint16_t len); +void CMT2300A_WriteFifo(const uint8_t buf[], const uint16_t len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/CMT2300a/cmt2300a_params.h b/lib/CMT2300a/cmt2300a_params_860.h similarity index 89% rename from lib/CMT2300a/cmt2300a_params.h rename to lib/CMT2300a/cmt2300a_params_860.h index 4e10f6a25..94f155269 100644 --- a/lib/CMT2300a/cmt2300a_params.h +++ b/lib/CMT2300a/cmt2300a_params_860.h @@ -85,14 +85,14 @@ ; RSSI Offset = 0 ; RSSI Offset Sign = 0 */ -#ifndef __CMT2300A_PARAMS_H -#define __CMT2300A_PARAMS_H +#ifndef __CMT2300A_PARAMS_860_H +#define __CMT2300A_PARAMS_860_H #include "cmt2300a_defs.h" #include /* [CMT Bank] with RSSI offset of +- 0 (and Tx power double bit not set) */ -static uint8_t g_cmt2300aCmtBank[CMT2300A_CMT_BANK_SIZE] = { +static uint8_t g_cmt2300aCmtBank_860[CMT2300A_CMT_BANK_SIZE] = { 0x00, 0x66, 0xEC, @@ -108,7 +108,7 @@ static uint8_t g_cmt2300aCmtBank[CMT2300A_CMT_BANK_SIZE] = { }; /* [System Bank] */ -static uint8_t g_cmt2300aSystemBank[CMT2300A_SYSTEM_BANK_SIZE] = { +static uint8_t g_cmt2300aSystemBank_860[CMT2300A_SYSTEM_BANK_SIZE] = { 0xAE, 0xE0, 0x35, @@ -124,7 +124,7 @@ static uint8_t g_cmt2300aSystemBank[CMT2300A_SYSTEM_BANK_SIZE] = { }; /* [Frequency Bank] 860 MHz */ -static uint8_t g_cmt2300aFrequencyBank[CMT2300A_FREQUENCY_BANK_SIZE] = { +static uint8_t g_cmt2300aFrequencyBank_860[CMT2300A_FREQUENCY_BANK_SIZE] = { 0x42, 0x32, 0xCF, @@ -136,7 +136,7 @@ static uint8_t g_cmt2300aFrequencyBank[CMT2300A_FREQUENCY_BANK_SIZE] = { }; /* [Data Rate Bank] */ -static uint8_t g_cmt2300aDataRateBank[CMT2300A_DATA_RATE_BANK_SIZE] = { +static uint8_t g_cmt2300aDataRateBank_860[CMT2300A_DATA_RATE_BANK_SIZE] = { 0xA6, 0xC9, 0x20, @@ -164,7 +164,7 @@ static uint8_t g_cmt2300aDataRateBank[CMT2300A_DATA_RATE_BANK_SIZE] = { }; /* [Baseband Bank] - EU */ -static uint8_t g_cmt2300aBasebandBank[CMT2300A_BASEBAND_BANK_SIZE] = { +static uint8_t g_cmt2300aBasebandBank_860[CMT2300A_BASEBAND_BANK_SIZE] = { 0x12, 0x1E, 0x00, @@ -197,7 +197,7 @@ static uint8_t g_cmt2300aBasebandBank[CMT2300A_BASEBAND_BANK_SIZE] = { }; /* [Tx Bank] 13 dBm */ -static uint8_t g_cmt2300aTxBank[CMT2300A_TX_BANK_SIZE] = { +static uint8_t g_cmt2300aTxBank_860[CMT2300A_TX_BANK_SIZE] = { 0x70, 0x4D, 0x06, diff --git a/lib/CMT2300a/cmt2300a_params_900.h b/lib/CMT2300a/cmt2300a_params_900.h new file mode 100644 index 000000000..19b8eeac1 --- /dev/null +++ b/lib/CMT2300a/cmt2300a_params_900.h @@ -0,0 +1,214 @@ +/* +;--------------------------------------- +; CMT2300A Configuration File +; Generated by CMOSTEK RFPDK 1.46 +; 2023.03.17 23:16 +;--------------------------------------- +; Mode = Advanced +; Part Number = CMT2300A +; Frequency = 900.000 MHz +; Xtal Frequency = 26.0000 MHz +; Demodulation = GFSK +; AGC = On +; Data Rate = 20.0 kbps +; Deviation = 20.0 kHz +; Tx Xtal Tol. = 20 ppm +; Rx Xtal Tol. = 20 ppm +; TRx Matching Network Type = 20 dBm +; Tx Power = +13 dBm +; Gaussian BT = 0.5 +; Bandwidth = Auto-Select kHz +; CDR Type = Counting +; CDR DR Range = NA +; AFC = On +; AFC Method = Auto-Select +; Data Representation = 0:F-low 1:F-high +; Rx Duty-Cycle = Off +; Tx Duty-Cycle = Off +; Sleep Timer = Off +; Sleep Time = NA +; Rx Timer = Off +; Rx Time T1 = NA +; Rx Time T2 = NA +; Rx Exit State = STBY +; Tx Exit State = STBY +; SLP Mode = Disable +; RSSI Valid Source = PJD +; PJD Window = 8 Jumps +; LFOSC Calibration = On +; Xtal Stable Time = 155 us +; RSSI Compare TH = NA +; Data Mode = Packet +; Whitening = Disable +; Whiten Type = NA +; Whiten Seed Type = NA +; Whiten Seed = NA +; Manchester = Disable +; Manchester Type = NA +; FEC = Enable +; FEC Type = x^3+x^2+1 +; Tx Prefix Type = 0 +; Tx Packet Number = 1 +; Tx Packet Gap = 32 +; Packet Type = Variable Length +; Node-Length Position = First Node, then Length +; Payload Bit Order = Start from msb +; Preamble Rx Size = 2 +; Preamble Tx Size = 30 +; Preamble Value = 170 +; Preamble Unit = 8-bit +; Sync Size = 4-byte +; Sync Value = 1296587336 +; Sync Tolerance = None +; Sync Manchester = Disable +; Node ID Size = NA +; Node ID Value = NA +; Node ID Mode = None +; Node ID Err Mask = Disable +; Node ID Free = Disable +; Payload Length = 32 +; CRC Options = IBM-16 +; CRC Seed = 0 crc_seed +; CRC Range = Entire Payload +; CRC Swap = Start from MSB +; CRC Bit Invert = Normal +; CRC Bit Order = Start from bit 15 +; Dout Mute = Off +; Dout Adjust Mode = Disable +; Dout Adjust Percentage = NA +; Collision Detect = Off +; Collision Detect Offset = NA +; RSSI Detect Mode = At PREAM_OK +; RSSI Filter Setting = 32-tap +; RF Performance = High +; LBD Threshold = 2.4 V +; RSSI Offset = 0 +; RSSI Offset Sign = 0 +*/ +#ifndef __CMT2300A_PARAMS_900_H +#define __CMT2300A_PARAMS_900_H + +#include "cmt2300a_defs.h" +#include + +/* [CMT Bank] with RSSI offset of +- 0 (and Tx power double bit not set) */ +static uint8_t g_cmt2300aCmtBank_900[CMT2300A_CMT_BANK_SIZE] = { +0x00, +0x66, +0xEC, +0x1C, +0x70, +0x80, +0x14, +0x08, +0x11, +0x02, +0x02, +0x00, +}; + +/* [System Bank] */ +static uint8_t g_cmt2300aSystemBank_900[CMT2300A_SYSTEM_BANK_SIZE] = { +0xAE, +0xE0, +0x35, +0x00, +0x00, +0xF4, +0x10, +0xE2, +0x42, +0x20, +0x0C, +0x81, +}; + +/* [Frequency Bank] 900 MHz */ +static uint8_t g_cmt2300aFrequencyBank_900[CMT2300A_FREQUENCY_BANK_SIZE] = { +0x45, +0x46, +0x0A, +0x84, +0x45, +0x3B, +0xB1, +0x13, +}; + +/* [Data Rate Bank] */ +static uint8_t g_cmt2300aDataRateBank_900[CMT2300A_DATA_RATE_BANK_SIZE] = { +0xA6, +0xC9, +0x20, +0x20, +0xD2, +0x35, +0x0C, +0x0B, +0x9F, +0x4B, +0x29, +0x29, +0xC0, +0x14, +0x05, +0x53, +0x10, +0x00, +0xB4, +0x00, +0x00, +0x01, +0x00, +0x00, +}; + +/* [Baseband Bank] - EU */ +static uint8_t g_cmt2300aBasebandBank_900[CMT2300A_BASEBAND_BANK_SIZE] = { +0x12, +0x1E, +0x00, +0xAA, +0x06, +0x00, +0x00, +0x00, +0x00, +0x48, +0x5A, +0x48, +0x4D, +0x01, +0x1F, +0x00, +0x00, +0x00, +0x00, +0x00, +0xC3, +0x00, +0x00, +0x60, +0xFF, +0x00, +0x00, +0x1F, +0x10, +}; + +/* [Tx Bank] 13 dBm */ +static uint8_t g_cmt2300aTxBank_900[CMT2300A_TX_BANK_SIZE] = { +0x70, +0x4D, +0x06, +0x00, +0x07, +0x50, +0x00, +0x53, +0x09, +0x3F, +0x7F, +}; + +#endif diff --git a/lib/CMT2300a/cmt2300wrapper.cpp b/lib/CMT2300a/cmt2300wrapper.cpp index 21265f50a..016ef56fd 100644 --- a/lib/CMT2300a/cmt2300wrapper.cpp +++ b/lib/CMT2300a/cmt2300wrapper.cpp @@ -1,12 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2023 Thomas Basler and others + * Copyright (C) 2023-2024 Thomas Basler and others */ #include "cmt2300wrapper.h" #include "cmt2300a.h" -#include "cmt2300a_params.h" +#include "cmt2300a_params_860.h" +#include "cmt2300a_params_900.h" -CMT2300A::CMT2300A(uint8_t pin_sdio, uint8_t pin_clk, uint8_t pin_cs, uint8_t pin_fcs, uint32_t spi_speed) +CMT2300A::CMT2300A(const uint8_t pin_sdio, const uint8_t pin_clk, const uint8_t pin_cs, const uint8_t pin_fcs, const uint32_t spi_speed) { _pin_sdio = pin_sdio; _pin_clk = pin_clk; @@ -57,7 +58,7 @@ bool CMT2300A::available(void) ) & CMT2300A_ReadReg(CMT2300A_CUS_INT_FLAG); } -void CMT2300A::read(void* buf, uint8_t len) +void CMT2300A::read(void* buf, const uint8_t len) { // Fetch the payload CMT2300A_ReadFifo(static_cast(buf), len); @@ -65,7 +66,7 @@ void CMT2300A::read(void* buf, uint8_t len) CMT2300A_ClearInterruptFlags(); } -bool CMT2300A::write(const uint8_t* buf, uint8_t len) +bool CMT2300A::write(const uint8_t* buf, const uint8_t len) { CMT2300A_GoStby(); CMT2300A_ClearInterruptFlags(); @@ -100,7 +101,7 @@ bool CMT2300A::write(const uint8_t* buf, uint8_t len) return true; } -void CMT2300A::setChannel(uint8_t channel) +void CMT2300A::setChannel(const uint8_t channel) { CMT2300A_SetFrequencyChannel(channel); } @@ -122,7 +123,7 @@ int CMT2300A::getRssiDBm() return CMT2300A_GetRssiDBm(); } -bool CMT2300A::setPALevel(int8_t level) +bool CMT2300A::setPALevel(const int8_t level) { uint16_t Tx_dBm_word; switch (level) { @@ -242,6 +243,22 @@ bool CMT2300A::rxFifoAvailable() ) & CMT2300A_ReadReg(CMT2300A_CUS_INT_FLAG); } +uint32_t CMT2300A::getBaseFrequency() const +{ + return getBaseFrequency(_frequencyBand); +} + +FrequencyBand_t CMT2300A::getFrequencyBand() const +{ + return _frequencyBand; +} + +void CMT2300A::setFrequencyBand(const FrequencyBand_t mode) +{ + _frequencyBand = mode; + _init_radio(); +} + void CMT2300A::flush_rx(void) { CMT2300A_ClearRxFifo(); @@ -261,12 +278,24 @@ bool CMT2300A::_init_radio() } /* config registers */ - CMT2300A_ConfigRegBank(CMT2300A_CMT_BANK_ADDR, g_cmt2300aCmtBank, CMT2300A_CMT_BANK_SIZE); - CMT2300A_ConfigRegBank(CMT2300A_SYSTEM_BANK_ADDR, g_cmt2300aSystemBank, CMT2300A_SYSTEM_BANK_SIZE); - CMT2300A_ConfigRegBank(CMT2300A_FREQUENCY_BANK_ADDR, g_cmt2300aFrequencyBank, CMT2300A_FREQUENCY_BANK_SIZE); - CMT2300A_ConfigRegBank(CMT2300A_DATA_RATE_BANK_ADDR, g_cmt2300aDataRateBank, CMT2300A_DATA_RATE_BANK_SIZE); - CMT2300A_ConfigRegBank(CMT2300A_BASEBAND_BANK_ADDR, g_cmt2300aBasebandBank, CMT2300A_BASEBAND_BANK_SIZE); - CMT2300A_ConfigRegBank(CMT2300A_TX_BANK_ADDR, g_cmt2300aTxBank, CMT2300A_TX_BANK_SIZE); + switch (_frequencyBand) { + case FrequencyBand_t::BAND_900: + CMT2300A_ConfigRegBank(CMT2300A_CMT_BANK_ADDR, g_cmt2300aCmtBank_900, CMT2300A_CMT_BANK_SIZE); + CMT2300A_ConfigRegBank(CMT2300A_SYSTEM_BANK_ADDR, g_cmt2300aSystemBank_900, CMT2300A_SYSTEM_BANK_SIZE); + CMT2300A_ConfigRegBank(CMT2300A_FREQUENCY_BANK_ADDR, g_cmt2300aFrequencyBank_900, CMT2300A_FREQUENCY_BANK_SIZE); + CMT2300A_ConfigRegBank(CMT2300A_DATA_RATE_BANK_ADDR, g_cmt2300aDataRateBank_900, CMT2300A_DATA_RATE_BANK_SIZE); + CMT2300A_ConfigRegBank(CMT2300A_BASEBAND_BANK_ADDR, g_cmt2300aBasebandBank_900, CMT2300A_BASEBAND_BANK_SIZE); + CMT2300A_ConfigRegBank(CMT2300A_TX_BANK_ADDR, g_cmt2300aTxBank_900, CMT2300A_TX_BANK_SIZE); + break; + default: + CMT2300A_ConfigRegBank(CMT2300A_CMT_BANK_ADDR, g_cmt2300aCmtBank_860, CMT2300A_CMT_BANK_SIZE); + CMT2300A_ConfigRegBank(CMT2300A_SYSTEM_BANK_ADDR, g_cmt2300aSystemBank_860, CMT2300A_SYSTEM_BANK_SIZE); + CMT2300A_ConfigRegBank(CMT2300A_FREQUENCY_BANK_ADDR, g_cmt2300aFrequencyBank_860, CMT2300A_FREQUENCY_BANK_SIZE); + CMT2300A_ConfigRegBank(CMT2300A_DATA_RATE_BANK_ADDR, g_cmt2300aDataRateBank_860, CMT2300A_DATA_RATE_BANK_SIZE); + CMT2300A_ConfigRegBank(CMT2300A_BASEBAND_BANK_ADDR, g_cmt2300aBasebandBank_860, CMT2300A_BASEBAND_BANK_SIZE); + CMT2300A_ConfigRegBank(CMT2300A_TX_BANK_ADDR, g_cmt2300aTxBank_860, CMT2300A_TX_BANK_SIZE); + break; + } // xosc_aac_code[2:0] = 2 uint8_t tmp; diff --git a/lib/CMT2300a/cmt2300wrapper.h b/lib/CMT2300a/cmt2300wrapper.h index 5f76b06f2..d1639fe9b 100644 --- a/lib/CMT2300a/cmt2300wrapper.h +++ b/lib/CMT2300a/cmt2300wrapper.h @@ -4,13 +4,21 @@ #include #define CMT2300A_ONE_STEP_SIZE 2500 // frequency channel step size for fast frequency hopping operation: One step size is 2.5 kHz. -#define CMT_BASE_FREQ 860000000 // from Frequency Bank in cmt2300a_params.h #define FH_OFFSET 100 // value * CMT2300A_ONE_STEP_SIZE = channel frequency offset #define CMT_SPI_SPEED 4000000 // 4 MHz +#define CMT_BASE_FREQ_900 900000000 +#define CMT_BASE_FREQ_860 860000000 + +enum FrequencyBand_t { + BAND_860, + BAND_900, + FrequencyBand_Max, +}; + class CMT2300A { public: - CMT2300A(uint8_t pin_sdio, uint8_t pin_clk, uint8_t pin_cs, uint8_t pin_fcs, uint32_t _spi_speed = CMT_SPI_SPEED); + CMT2300A(const uint8_t pin_sdio, const uint8_t pin_clk, const uint8_t pin_cs, const uint8_t pin_fcs, const uint32_t _spi_speed = CMT_SPI_SPEED); bool begin(void); @@ -54,15 +62,15 @@ class CMT2300A { * in one call is 32 (for dynamic payload lengths) or whatever number was * previously passed to setPayloadSize() (for static payload lengths). */ - void read(void* buf, uint8_t len); + void read(void* buf, const uint8_t len); - bool write(const uint8_t* buf, uint8_t len); + bool write(const uint8_t* buf, const uint8_t len); /** * Set RF communication channel. The frequency used by a channel is * @param channel Which RF channel to communicate on, 0-254 */ - void setChannel(uint8_t channel); + void setChannel(const uint8_t channel); /** * Get RF communication channel @@ -82,10 +90,26 @@ class CMT2300A { int getRssiDBm(); - bool setPALevel(int8_t level); + bool setPALevel(const int8_t level); bool rxFifoAvailable(); + uint32_t getBaseFrequency() const; + static constexpr uint32_t getBaseFrequency(FrequencyBand_t band) + { + switch (band) { + case FrequencyBand_t::BAND_900: + return CMT_BASE_FREQ_900; + break; + default: + return CMT_BASE_FREQ_860; + break; + } + } + + FrequencyBand_t getFrequencyBand() const; + void setFrequencyBand(const FrequencyBand_t mode); + /** * Empty the RX (receive) FIFO buffers. */ @@ -109,4 +133,6 @@ class CMT2300A { int8_t _pin_cs; int8_t _pin_fcs; uint32_t _spi_speed; -}; \ No newline at end of file + + FrequencyBand_t _frequencyBand = FrequencyBand_t::BAND_860; +}; diff --git a/lib/CMT2300a/cmt_spi3.c b/lib/CMT2300a/cmt_spi3.c index 95954e4e2..59aad36f7 100644 --- a/lib/CMT2300a/cmt_spi3.c +++ b/lib/CMT2300a/cmt_spi3.c @@ -1,142 +1,142 @@ -#include "cmt_spi3.h" -#include -#include -#include // for esp_rom_gpio_connect_out_signal - -SemaphoreHandle_t paramLock = NULL; -#define SPI_PARAM_LOCK() \ - do { \ - } while (xSemaphoreTake(paramLock, portMAX_DELAY) != pdPASS) -#define SPI_PARAM_UNLOCK() xSemaphoreGive(paramLock) - -// for ESP32 this is the so-called HSPI -// for ESP32-S2/S3/C3 this nomenclature does not really exist anymore, -// it is simply the first externally usable hardware SPI master controller -#define SPI_CMT SPI2_HOST - -spi_device_handle_t spi_reg, spi_fifo; - -void cmt_spi3_init(int8_t pin_sdio, int8_t pin_clk, int8_t pin_cs, int8_t pin_fcs, uint32_t spi_speed) -{ - paramLock = xSemaphoreCreateMutex(); - - spi_bus_config_t buscfg = { - .mosi_io_num = pin_sdio, - .miso_io_num = -1, // single wire MOSI/MISO - .sclk_io_num = pin_clk, - .quadwp_io_num = -1, - .quadhd_io_num = -1, - .max_transfer_sz = 32, - }; - spi_device_interface_config_t devcfg = { - .command_bits = 1, - .address_bits = 7, - .dummy_bits = 0, - .mode = 0, // SPI mode 0 - .cs_ena_pretrans = 1, - .cs_ena_posttrans = 1, - .clock_speed_hz = spi_speed, - .spics_io_num = pin_cs, - .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, - .queue_size = 1, - .pre_cb = NULL, - .post_cb = NULL, - }; - - ESP_ERROR_CHECK(spi_bus_initialize(SPI_CMT, &buscfg, SPI_DMA_DISABLED)); - ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg, &spi_reg)); - - // FiFo - spi_device_interface_config_t devcfg2 = { - .command_bits = 0, - .address_bits = 0, - .dummy_bits = 0, - .mode = 0, // SPI mode 0 - .cs_ena_pretrans = 2, - .cs_ena_posttrans = (uint8_t)(1 / (spi_speed * 10e6 * 2) + 2), // >2 us - .clock_speed_hz = spi_speed, - .spics_io_num = pin_fcs, - .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, - .queue_size = 1, - .pre_cb = NULL, - .post_cb = NULL, - }; - ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg2, &spi_fifo)); - - esp_rom_gpio_connect_out_signal(pin_sdio, spi_periph_signal[SPI_CMT].spid_out, true, false); - delay(100); -} - -void cmt_spi3_write(uint8_t addr, uint8_t dat) -{ - uint8_t tx_data; - tx_data = ~dat; - spi_transaction_t t = { - .cmd = 1, - .addr = ~addr, - .length = 8, - .tx_buffer = &tx_data, - .rx_buffer = NULL - }; - SPI_PARAM_LOCK(); - ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); - SPI_PARAM_UNLOCK(); - delayMicroseconds(100); -} - -uint8_t cmt_spi3_read(uint8_t addr) -{ - uint8_t rx_data; - spi_transaction_t t = { - .cmd = 0, - .addr = ~addr, - .length = 8, - .rxlength = 8, - .tx_buffer = NULL, - .rx_buffer = &rx_data - }; - SPI_PARAM_LOCK(); - ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); - SPI_PARAM_UNLOCK(); - delayMicroseconds(100); - return rx_data; -} - -void cmt_spi3_write_fifo(const uint8_t* buf, uint16_t len) -{ - uint8_t tx_data; - - spi_transaction_t t = { - .length = 8, - .tx_buffer = &tx_data, // reference to write data - .rx_buffer = NULL - }; - - SPI_PARAM_LOCK(); - for (uint8_t i = 0; i < len; i++) { - tx_data = ~buf[i]; // negate buffer contents - ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); - delayMicroseconds(4); // > 4 us - } - SPI_PARAM_UNLOCK(); -} - -void cmt_spi3_read_fifo(uint8_t* buf, uint16_t len) -{ - uint8_t rx_data; - - spi_transaction_t t = { - .length = 8, - .rxlength = 8, - .tx_buffer = NULL, - .rx_buffer = &rx_data - }; - - SPI_PARAM_LOCK(); - for (uint8_t i = 0; i < len; i++) { - ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); - delayMicroseconds(4); // > 4 us - buf[i] = rx_data; - } - SPI_PARAM_UNLOCK(); -} +#include "cmt_spi3.h" +#include +#include +#include // for esp_rom_gpio_connect_out_signal + +SemaphoreHandle_t paramLock = NULL; +#define SPI_PARAM_LOCK() \ + do { \ + } while (xSemaphoreTake(paramLock, portMAX_DELAY) != pdPASS) +#define SPI_PARAM_UNLOCK() xSemaphoreGive(paramLock) + +// for ESP32 this is the so-called HSPI +// for ESP32-S2/S3/C3 this nomenclature does not really exist anymore, +// it is simply the first externally usable hardware SPI master controller +#define SPI_CMT SPI2_HOST + +spi_device_handle_t spi_reg, spi_fifo; + +void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed) +{ + paramLock = xSemaphoreCreateMutex(); + + spi_bus_config_t buscfg = { + .mosi_io_num = pin_sdio, + .miso_io_num = -1, // single wire MOSI/MISO + .sclk_io_num = pin_clk, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = 32, + }; + spi_device_interface_config_t devcfg = { + .command_bits = 1, + .address_bits = 7, + .dummy_bits = 0, + .mode = 0, // SPI mode 0 + .cs_ena_pretrans = 1, + .cs_ena_posttrans = 1, + .clock_speed_hz = spi_speed, + .spics_io_num = pin_cs, + .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, + .queue_size = 1, + .pre_cb = NULL, + .post_cb = NULL, + }; + + ESP_ERROR_CHECK(spi_bus_initialize(SPI_CMT, &buscfg, SPI_DMA_DISABLED)); + ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg, &spi_reg)); + + // FiFo + spi_device_interface_config_t devcfg2 = { + .command_bits = 0, + .address_bits = 0, + .dummy_bits = 0, + .mode = 0, // SPI mode 0 + .cs_ena_pretrans = 2, + .cs_ena_posttrans = (uint8_t)(1 / (spi_speed * 10e6 * 2) + 2), // >2 us + .clock_speed_hz = spi_speed, + .spics_io_num = pin_fcs, + .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, + .queue_size = 1, + .pre_cb = NULL, + .post_cb = NULL, + }; + ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg2, &spi_fifo)); + + esp_rom_gpio_connect_out_signal(pin_sdio, spi_periph_signal[SPI_CMT].spid_out, true, false); + delay(100); +} + +void cmt_spi3_write(const uint8_t addr, const uint8_t dat) +{ + uint8_t tx_data; + tx_data = ~dat; + spi_transaction_t t = { + .cmd = 1, + .addr = ~addr, + .length = 8, + .tx_buffer = &tx_data, + .rx_buffer = NULL + }; + SPI_PARAM_LOCK(); + ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); + SPI_PARAM_UNLOCK(); + delayMicroseconds(100); +} + +uint8_t cmt_spi3_read(const uint8_t addr) +{ + uint8_t rx_data; + spi_transaction_t t = { + .cmd = 0, + .addr = ~addr, + .length = 8, + .rxlength = 8, + .tx_buffer = NULL, + .rx_buffer = &rx_data + }; + SPI_PARAM_LOCK(); + ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); + SPI_PARAM_UNLOCK(); + delayMicroseconds(100); + return rx_data; +} + +void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len) +{ + uint8_t tx_data; + + spi_transaction_t t = { + .length = 8, + .tx_buffer = &tx_data, // reference to write data + .rx_buffer = NULL + }; + + SPI_PARAM_LOCK(); + for (uint8_t i = 0; i < len; i++) { + tx_data = ~buf[i]; // negate buffer contents + ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); + delayMicroseconds(4); // > 4 us + } + SPI_PARAM_UNLOCK(); +} + +void cmt_spi3_read_fifo(uint8_t* buf, const uint16_t len) +{ + uint8_t rx_data; + + spi_transaction_t t = { + .length = 8, + .rxlength = 8, + .tx_buffer = NULL, + .rx_buffer = &rx_data + }; + + SPI_PARAM_LOCK(); + for (uint8_t i = 0; i < len; i++) { + ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); + delayMicroseconds(4); // > 4 us + buf[i] = rx_data; + } + SPI_PARAM_UNLOCK(); +} diff --git a/lib/CMT2300a/cmt_spi3.h b/lib/CMT2300a/cmt_spi3.h index 0e77e311f..6d3a67b62 100644 --- a/lib/CMT2300a/cmt_spi3.h +++ b/lib/CMT2300a/cmt_spi3.h @@ -1,14 +1,14 @@ -#ifndef __CMT_SPI3_H -#define __CMT_SPI3_H - -#include - -void cmt_spi3_init(int8_t pin_sdio, int8_t pin_clk, int8_t pin_cs, int8_t pin_fcs, uint32_t spi_speed); - -void cmt_spi3_write(uint8_t addr, uint8_t dat); -uint8_t cmt_spi3_read(uint8_t addr); - -void cmt_spi3_write_fifo(const uint8_t* p_buf, uint16_t len); -void cmt_spi3_read_fifo(uint8_t* p_buf, uint16_t len); - -#endif +#ifndef __CMT_SPI3_H +#define __CMT_SPI3_H + +#include + +void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed); + +void cmt_spi3_write(const uint8_t addr, const uint8_t dat); +uint8_t cmt_spi3_read(const uint8_t addr); + +void cmt_spi3_write_fifo(const uint8_t* p_buf, const uint16_t len); +void cmt_spi3_read_fifo(uint8_t* p_buf, const uint16_t len); + +#endif diff --git a/lib/Hoymiles/src/HoymilesRadio_CMT.cpp b/lib/Hoymiles/src/HoymilesRadio_CMT.cpp index 5c61c3b6a..d112fd6f2 100644 --- a/lib/Hoymiles/src/HoymilesRadio_CMT.cpp +++ b/lib/Hoymiles/src/HoymilesRadio_CMT.cpp @@ -1,49 +1,79 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2023 Thomas Basler and others + * Copyright (C) 2023-2024 Thomas Basler and others */ #include "HoymilesRadio_CMT.h" #include "Hoymiles.h" #include "crc.h" #include +#include -#define HOY_BOOT_FREQ 868000000 // Hoymiles boot/init frequency after power up inverter or connection lost for 15 min -#define HOY_BASE_FREQ 860000000 -// offset from initalized CMT base frequency to Hoy base frequency in channels -#define CMT_BASE_CH_OFFSET860 ((CMT_BASE_FREQ - HOY_BASE_FREQ) / CMT2300A_ONE_STEP_SIZE / FH_OFFSET) +constexpr CountryFrequencyDefinition_t make_value(FrequencyBand_t Band, uint32_t Freq_Legal_Min, uint32_t Freq_Legal_Max, uint32_t Freq_Default, uint32_t Freq_StartUp) +{ + // frequency can not be lower than actual initailized base freq + 250000 + uint32_t minFrequency = CMT2300A::getBaseFrequency(Band) + HoymilesRadio_CMT::getChannelWidth(); + + // =923500, 0xFF does not work + uint32_t maxFrequency = CMT2300A::getBaseFrequency(Band) + 0xFE * HoymilesRadio_CMT::getChannelWidth(); -// frequency can not be lower than actual initailized base freq -#define MIN_FREQ_KHZ ((HOY_BASE_FREQ + (CMT_BASE_CH_OFFSET860 >= 1 ? CMT_BASE_CH_OFFSET860 : 1) * CMT2300A_ONE_STEP_SIZE * FH_OFFSET) / 1000) + CountryFrequencyDefinition_t v = { Band, minFrequency, maxFrequency, Freq_Legal_Min, Freq_Legal_Max, Freq_Default, Freq_StartUp }; + return v; +} -// =923500, 0xFF does not work -#define MAX_FREQ_KHZ ((HOY_BASE_FREQ + 0xFE * CMT2300A_ONE_STEP_SIZE * FH_OFFSET) / 1000) +constexpr frozen::map countryDefinition = { + { CountryModeId_t::MODE_EU, make_value(FrequencyBand_t::BAND_860, 863e6, 870e6, 865e6, 868e6) }, + { CountryModeId_t::MODE_US, make_value(FrequencyBand_t::BAND_900, 905e6, 925e6, 918e6, 915e6) }, + { CountryModeId_t::MODE_BR, make_value(FrequencyBand_t::BAND_900, 915e6, 928e6, 918e6, 915e6) }, +}; -float HoymilesRadio_CMT::getFrequencyFromChannel(const uint8_t channel) +uint32_t HoymilesRadio_CMT::getFrequencyFromChannel(const uint8_t channel) const { - return (CMT_BASE_FREQ + (CMT_BASE_CH_OFFSET860 + channel) * FH_OFFSET * CMT2300A_ONE_STEP_SIZE) / 1000000.0; + return (_radio->getBaseFrequency() + channel * getChannelWidth()); } -uint8_t HoymilesRadio_CMT::getChannelFromFrequency(const uint32_t freq_kHz) +uint8_t HoymilesRadio_CMT::getChannelFromFrequency(const uint32_t frequency) const { - if ((freq_kHz % 250) != 0) { - Hoymiles.getMessageOutput()->printf("%.3f MHz is not divisible by 250 kHz!\r\n", freq_kHz / 1000.0); + if ((frequency % getChannelWidth()) != 0) { + Hoymiles.getMessageOutput()->printf("%.3f MHz is not divisible by %d kHz!\r\n", frequency / 1000000.0, getChannelWidth()); return 0xFF; // ERROR } - if (freq_kHz < MIN_FREQ_KHZ || freq_kHz > MAX_FREQ_KHZ) { + if (frequency < getMinFrequency() || frequency > getMaxFrequency()) { Hoymiles.getMessageOutput()->printf("%.2f MHz is out of Hoymiles/CMT range! (%.2f MHz - %.2f MHz)\r\n", - freq_kHz / 1000.0, MIN_FREQ_KHZ / 1000.0, MAX_FREQ_KHZ / 1000.0); + frequency / 1000000.0, getMinFrequency() / 1000000.0, getMaxFrequency() / 1000000.0); return 0xFF; // ERROR } - if (freq_kHz < 863000 || freq_kHz > 870000) { - Hoymiles.getMessageOutput()->printf("!!! caution: %.2f MHz is out of EU legal range! (863 - 870 MHz)\r\n", - freq_kHz / 1000.0); + if (frequency < countryDefinition.at(_countryMode).Freq_Legal_Min || frequency > countryDefinition.at(_countryMode).Freq_Legal_Max) { + Hoymiles.getMessageOutput()->printf("!!! caution: %.2f MHz is out of region legal range! (%d - %d MHz)\r\n", + frequency / 1000000.0, + static_cast(countryDefinition.at(_countryMode).Freq_Legal_Min / 1e6), + static_cast(countryDefinition.at(_countryMode).Freq_Legal_Max / 1e6)); } - return (freq_kHz * 1000 - CMT_BASE_FREQ) / CMT2300A_ONE_STEP_SIZE / FH_OFFSET - CMT_BASE_CH_OFFSET860; // frequency to channel + + return (frequency - _radio->getBaseFrequency()) / getChannelWidth(); // frequency to channel } -bool HoymilesRadio_CMT::cmtSwitchDtuFreq(const uint32_t to_freq_kHz) +std::vector HoymilesRadio_CMT::getCountryFrequencyList() const { - const uint8_t toChannel = getChannelFromFrequency(to_freq_kHz); + std::vector v; + for (const auto& [key, value] : countryDefinition) { + CountryFrequencyList_t s; + s.mode = key; + s.definition.Band = value.Band; + s.definition.Freq_Default = value.Freq_Default; + s.definition.Freq_StartUp = value.Freq_StartUp; + s.definition.Freq_Min = value.Freq_Min; + s.definition.Freq_Max = value.Freq_Max; + s.definition.Freq_Legal_Max = value.Freq_Legal_Max; + s.definition.Freq_Legal_Min = value.Freq_Legal_Min; + + v.push_back(s); + } + return v; +} + +bool HoymilesRadio_CMT::cmtSwitchDtuFreq(const uint32_t to_frequency) +{ + const uint8_t toChannel = getChannelFromFrequency(to_frequency); if (toChannel == 0xFF) { return false; } @@ -61,6 +91,7 @@ void HoymilesRadio_CMT::init(const int8_t pin_sdio, const int8_t pin_clk, const _radio->begin(); + setCountryMode(CountryModeId_t::MODE_EU); cmtSwitchDtuFreq(_inverterTargetFrequency); // start dtu at work freqency, for fast Rx if inverter is already on and frequency switched if (!_radio->isChipConnected()) { @@ -134,7 +165,7 @@ void HoymilesRadio_CMT::loop() if (nullptr != inv) { // Save packet in inverter rx buffer - Hoymiles.getVerboseMessageOutput()->printf("RX %.2f MHz --> ", getFrequencyFromChannel(f.channel)); + Hoymiles.getVerboseMessageOutput()->printf("RX %.2f MHz --> ", getFrequencyFromChannel(f.channel) / 1000000.0); dumpBuf(f.fragment, f.len, false); Hoymiles.getVerboseMessageOutput()->printf("| %d dBm\r\n", f.rssi); @@ -191,14 +222,31 @@ bool HoymilesRadio_CMT::isConnected() const return _radio->isChipConnected(); } -uint32_t HoymilesRadio_CMT::getMinFrequency() +uint32_t HoymilesRadio_CMT::getMinFrequency() const +{ + return countryDefinition.at(_countryMode).Freq_Min; +} + +uint32_t HoymilesRadio_CMT::getMaxFrequency() const +{ + return countryDefinition.at(_countryMode).Freq_Max; +} + +CountryModeId_t HoymilesRadio_CMT::getCountryMode() const +{ + return _countryMode; +} + +void HoymilesRadio_CMT::setCountryMode(const CountryModeId_t mode) { - return MIN_FREQ_KHZ; + _radio->setFrequencyBand(countryDefinition.at(mode).Band); + _countryMode = mode; } -uint32_t HoymilesRadio_CMT::getMaxFrequency() +uint32_t HoymilesRadio_CMT::getInvBootFrequency() const { - return MAX_FREQ_KHZ; + // Hoymiles boot/init frequency after power up inverter or connection lost for 15 min + return countryDefinition.at(_countryMode).Freq_StartUp; } void ARDUINO_ISR_ATTR HoymilesRadio_CMT::handleInt1() @@ -220,11 +268,11 @@ void HoymilesRadio_CMT::sendEsbPacket(CommandAbstract& cmd) _radio->stopListening(); if (cmd.getDataPayload()[0] == 0x56) { // @todo(tbnobody) Bad hack to identify ChannelChange Command - cmtSwitchDtuFreq(HOY_BOOT_FREQ / 1000); + cmtSwitchDtuFreq(getInvBootFrequency()); } Hoymiles.getVerboseMessageOutput()->printf("TX %s %.2f MHz --> ", - cmd.getCommandName().c_str(), getFrequencyFromChannel(_radio->getChannel())); + cmd.getCommandName().c_str(), getFrequencyFromChannel(_radio->getChannel()) / 1000000.0); cmd.dumpDataPayload(Hoymiles.getVerboseMessageOutput()); if (!_radio->write(cmd.getDataPayload(), cmd.getDataSize())) { diff --git a/lib/Hoymiles/src/HoymilesRadio_CMT.h b/lib/Hoymiles/src/HoymilesRadio_CMT.h index ee566c3e9..770617fe3 100644 --- a/lib/Hoymiles/src/HoymilesRadio_CMT.h +++ b/lib/Hoymiles/src/HoymilesRadio_CMT.h @@ -8,14 +8,37 @@ #include #include #include +#include // number of fragments hold in buffer #define FRAGMENT_BUFFER_SIZE 30 #ifndef HOYMILES_CMT_WORK_FREQ -#define HOYMILES_CMT_WORK_FREQ 865000 +#define HOYMILES_CMT_WORK_FREQ 865000000 #endif +enum CountryModeId_t { + MODE_EU, + MODE_US, + MODE_BR, + CountryModeId_Max +}; + +struct CountryFrequencyDefinition_t { + FrequencyBand_t Band; + uint32_t Freq_Min; + uint32_t Freq_Max; + uint32_t Freq_Legal_Min; + uint32_t Freq_Legal_Max; + uint32_t Freq_Default; + uint32_t Freq_StartUp; +}; + +struct CountryFrequencyList_t { + CountryModeId_t mode; + CountryFrequencyDefinition_t definition; +}; + class HoymilesRadio_CMT : public HoymilesRadio { public: void init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3); @@ -26,11 +49,22 @@ class HoymilesRadio_CMT : public HoymilesRadio { bool isConnected() const; - static uint32_t getMinFrequency(); - static uint32_t getMaxFrequency(); + uint32_t getMinFrequency() const; + uint32_t getMaxFrequency() const; + static constexpr uint32_t getChannelWidth() + { + return FH_OFFSET * CMT2300A_ONE_STEP_SIZE; + } + + CountryModeId_t getCountryMode() const; + void setCountryMode(const CountryModeId_t mode); - static float getFrequencyFromChannel(const uint8_t channel); - static uint8_t getChannelFromFrequency(const uint32_t freq_kHz); + uint32_t getInvBootFrequency() const; + + uint32_t getFrequencyFromChannel(const uint8_t channel) const; + uint8_t getChannelFromFrequency(const uint32_t frequency) const; + + std::vector getCountryFrequencyList() const; private: void ARDUINO_ISR_ATTR handleInt1(); @@ -51,5 +85,7 @@ class HoymilesRadio_CMT : public HoymilesRadio { uint32_t _inverterTargetFrequency = HOYMILES_CMT_WORK_FREQ; - bool cmtSwitchDtuFreq(const uint32_t to_freq_kHz); -}; \ No newline at end of file + bool cmtSwitchDtuFreq(const uint32_t to_frequency); + + CountryModeId_t _countryMode; +}; diff --git a/lib/Hoymiles/src/commands/ChannelChangeCommand.cpp b/lib/Hoymiles/src/commands/ChannelChangeCommand.cpp index 1001790d2..301785098 100644 --- a/lib/Hoymiles/src/commands/ChannelChangeCommand.cpp +++ b/lib/Hoymiles/src/commands/ChannelChangeCommand.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2023 Thomas Basler and others + * Copyright (C) 2023-2024 Thomas Basler and others */ /* @@ -12,7 +12,8 @@ Command structure: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ----------------------------------------------------------------------------------------------------------------- -56 71 60 35 46 80 12 23 04 02 15 21 00 14 00 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- +56 71 60 35 46 80 12 23 04 02 15 21 00 14 00 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- (860 MHz band) +56 71 60 35 46 80 12 23 04 03 17 3c 00 14 00 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- (900 MHz band) ^^ ^^^^^^^^^^^ ^^^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^ ID Target Addr Source Addr ? ? ? CH ? CRC8 */ @@ -22,12 +23,10 @@ ChannelChangeCommand::ChannelChangeCommand(const uint64_t target_address, const : CommandAbstract(target_address, router_address) { _payload[0] = 0x56; - _payload[9] = 0x02; - _payload[10] = 0x15; - _payload[11] = 0x21; _payload[13] = 0x14; _payload_size = 14; + setCountryMode(CountryModeId_t::MODE_EU); setChannel(channel); setTimeout(10); } @@ -47,6 +46,27 @@ uint8_t ChannelChangeCommand::getChannel() const return _payload[12]; } +void ChannelChangeCommand::setCountryMode(const CountryModeId_t mode) +{ + switch (mode) { + case CountryModeId_t::MODE_US: + _payload[9] = 0x03; + _payload[10] = 0x17; + _payload[11] = 0x3c; + break; + case CountryModeId_t::MODE_BR: + _payload[9] = 0x03; + _payload[10] = 0x17; + _payload[11] = 0x3c; + break; + default: + _payload[9] = 0x02; + _payload[10] = 0x15; + _payload[11] = 0x21; + break; + } +} + bool ChannelChangeCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id) { return true; @@ -56,4 +76,4 @@ uint8_t ChannelChangeCommand::getMaxResendCount() { // This command will never retrieve an answer. Therefor it's not required to repeat it return 0; -} \ No newline at end of file +} diff --git a/lib/Hoymiles/src/commands/ChannelChangeCommand.h b/lib/Hoymiles/src/commands/ChannelChangeCommand.h index a0b38cab1..44a4c9ebd 100644 --- a/lib/Hoymiles/src/commands/ChannelChangeCommand.h +++ b/lib/Hoymiles/src/commands/ChannelChangeCommand.h @@ -2,6 +2,7 @@ #pragma once #include "CommandAbstract.h" +#include "../HoymilesRadio_CMT.h" class ChannelChangeCommand : public CommandAbstract { public: @@ -12,7 +13,9 @@ class ChannelChangeCommand : public CommandAbstract { void setChannel(const uint8_t channel); uint8_t getChannel() const; + void setCountryMode(const CountryModeId_t mode); + virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id); virtual uint8_t getMaxResendCount(); -}; \ No newline at end of file +}; diff --git a/lib/Hoymiles/src/inverters/HMS_Abstract.cpp b/lib/Hoymiles/src/inverters/HMS_Abstract.cpp index 235a9ba77..ffa7d7219 100644 --- a/lib/Hoymiles/src/inverters/HMS_Abstract.cpp +++ b/lib/Hoymiles/src/inverters/HMS_Abstract.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2023 Thomas Basler and others + * Copyright (C) 2023-2024 Thomas Basler and others */ #include "HMS_Abstract.h" #include "Hoymiles.h" @@ -19,7 +19,8 @@ bool HMS_Abstract::sendChangeChannelRequest() } auto cmdChannel = _radio->prepareCommand(); - cmdChannel->setChannel(HoymilesRadio_CMT::getChannelFromFrequency(Hoymiles.getRadioCmt()->getInverterTargetFrequency())); + cmdChannel->setCountryMode(Hoymiles.getRadioCmt()->getCountryMode()); + cmdChannel->setChannel(Hoymiles.getRadioCmt()->getChannelFromFrequency(Hoymiles.getRadioCmt()->getInverterTargetFrequency())); cmdChannel->setTargetAddress(serial()); _radio->enqueCommand(cmdChannel); diff --git a/lib/Hoymiles/src/inverters/HMT_Abstract.cpp b/lib/Hoymiles/src/inverters/HMT_Abstract.cpp index 578233ee1..5c232b900 100644 --- a/lib/Hoymiles/src/inverters/HMT_Abstract.cpp +++ b/lib/Hoymiles/src/inverters/HMT_Abstract.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2023 Thomas Basler and others + * Copyright (C) 2023-2024 Thomas Basler and others */ #include "HMT_Abstract.h" #include "Hoymiles.h" @@ -21,9 +21,10 @@ bool HMT_Abstract::sendChangeChannelRequest() } auto cmdChannel = _radio->prepareCommand(); - cmdChannel->setChannel(HoymilesRadio_CMT::getChannelFromFrequency(Hoymiles.getRadioCmt()->getInverterTargetFrequency())); + cmdChannel->setCountryMode(Hoymiles.getRadioCmt()->getCountryMode()); + cmdChannel->setChannel(Hoymiles.getRadioCmt()->getChannelFromFrequency(Hoymiles.getRadioCmt()->getInverterTargetFrequency())); cmdChannel->setTargetAddress(serial()); _radio->enqueCommand(cmdChannel); return true; -}; \ No newline at end of file +}; diff --git a/pio-scripts/patch_apply.py b/pio-scripts/patch_apply.py index 17e25584c..5734c4aa0 100644 --- a/pio-scripts/patch_apply.py +++ b/pio-scripts/patch_apply.py @@ -9,7 +9,10 @@ Import("env") def getPatchPath(env): - return os.path.join(env["PROJECT_DIR"], "patches", env.GetProjectOption('custom_patches')) + patchList = [] + for patch in env.GetProjectOption('custom_patches').split(","): + patchList.append(os.path.join(env["PROJECT_DIR"], "patches", patch)) + return patchList def is_tool(name): """Check whether `name` is on PATH and marked as executable.""" @@ -44,35 +47,36 @@ def main(): print('Git not found. Will not apply custom patches!') return - directory = getPatchPath(env) - if (not os.path.isdir(directory)): - print('Patch directory not found: ' + directory) - return - - for file in os.listdir(directory): - if (not file.endswith('.patch')): - continue - - fullPath = os.path.join(directory, file) - preparePath = fullPath + '.prepare' - replaceInFile(fullPath, preparePath, '$$$env$$$', env['PIOENV']) - print('Working on patch: ' + fullPath + '... ', end='') + directories = getPatchPath(env) + for directory in directories: + if (not os.path.isdir(directory)): + print('Patch directory not found: ' + directory) + return + + for file in os.listdir(directory): + if (not file.endswith('.patch')): + continue + + fullPath = os.path.join(directory, file) + preparePath = fullPath + '.prepare' + replaceInFile(fullPath, preparePath, '$$$env$$$', env['PIOENV']) + print('Working on patch: ' + fullPath + '... ', end='') + + # Check if patch was already applied + process = subprocess.run(['git', 'apply', '--reverse', '--check', preparePath], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + if (process.returncode == 0): + print('already applied') + os.remove(preparePath) + continue + + # Apply patch + process = subprocess.run(['git', 'apply', preparePath]) + if (process.returncode == 0): + print('applied') + else: + print('failed') - # Check if patch was already applied - process = subprocess.run(['git', 'apply', '--reverse', '--check', preparePath], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - if (process.returncode == 0): - print('already applied') os.remove(preparePath) - continue - - # Apply patch - process = subprocess.run(['git', 'apply', preparePath]) - if (process.returncode == 0): - print('applied') - else: - print('failed') - - os.remove(preparePath) -main() \ No newline at end of file +main() diff --git a/platformio.ini b/platformio.ini index 379d59f14..57bc9a3cf 100644 --- a/platformio.ini +++ b/platformio.ini @@ -45,8 +45,8 @@ lib_deps = https://github.com/arkhipenko/TaskScheduler#testing https://github.com/arkhipenko/TaskScheduler#testing https://github.com/coryjfowler/MCP_CAN_lib - plerup/EspSoftwareSerial @ 8.0.1 - mobizt/FirebaseJson @ ^3.0.6 + plerup/EspSoftwareSerial @ ^8.0.1 + https://github.com/dok-net/ghostl @ ^1.0.1 rweather/Crypto@^0.4.0 extra_scripts = @@ -64,6 +64,8 @@ board_build.embed_files = webapp_dist/js/app.js.gz webapp_dist/site.webmanifest +custom_patches = + monitor_filters = esp32_exception_decoder, time, log2file, colorize monitor_speed = 115200 upload_protocol = esptool @@ -80,13 +82,13 @@ build_flags = ${env.build_flags} [env:generic_esp32c3] board = esp32-c3-devkitc-02 -custom_patches = esp32c3 +custom_patches = ${env.custom_patches},esp32c3 build_flags = ${env.build_flags} [env:generic_esp32c3_usb] board = esp32-c3-devkitc-02 -custom_patches = esp32c3 +custom_patches = ${env.custom_patches},esp32c3 build_flags = ${env.build_flags} -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 @@ -248,4 +250,4 @@ build_flags = ${env.build_flags} -DCMT_GPIO3=8 -DCMT_SDIO=5 -DARDUINO_USB_MODE=1 - -DARDUINO_USB_CDC_ON_BOOT=1 \ No newline at end of file + -DARDUINO_USB_CDC_ON_BOOT=1 diff --git a/src/Configuration.cpp b/src/Configuration.cpp index ff593e22a..5004b3cbe 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ #include "Configuration.h" #include "MessageOutput.h" @@ -97,6 +97,7 @@ bool ConfigurationClass::write() dtu["nrf_pa_level"] = config.Dtu.Nrf.PaLevel; dtu["cmt_pa_level"] = config.Dtu.Cmt.PaLevel; dtu["cmt_frequency"] = config.Dtu.Cmt.Frequency; + dtu["cmt_country_mode"] = config.Dtu.Cmt.CountryMode; JsonObject security = doc.createNestedObject("security"); security["password"] = config.Security.Password; @@ -111,7 +112,8 @@ bool ConfigurationClass::write() display["rotation"] = config.Display.Rotation; display["contrast"] = config.Display.Contrast; display["language"] = config.Display.Language; - display["diagram_duration"] = config.Display.DiagramDuration; + display["diagram_duration"] = config.Display.Diagram.Duration; + display["diagram_mode"] = config.Display.Diagram.Mode; JsonArray leds = device.createNestedArray("led"); for (uint8_t i = 0; i < PINMAPPING_LED_COUNT; i++) { @@ -339,6 +341,7 @@ bool ConfigurationClass::read() config.Dtu.Nrf.PaLevel = dtu["nrf_pa_level"] | DTU_NRF_PA_LEVEL; config.Dtu.Cmt.PaLevel = dtu["cmt_pa_level"] | DTU_CMT_PA_LEVEL; config.Dtu.Cmt.Frequency = dtu["cmt_frequency"] | DTU_CMT_FREQUENCY; + config.Dtu.Cmt.CountryMode = dtu["cmt_country_mode"] | DTU_CMT_COUNTRY_MODE; JsonObject security = doc["security"]; strlcpy(config.Security.Password, security["password"] | ACCESS_POINT_PASSWORD, sizeof(config.Security.Password)); @@ -353,7 +356,8 @@ bool ConfigurationClass::read() config.Display.Rotation = display["rotation"] | DISPLAY_ROTATION; config.Display.Contrast = display["contrast"] | DISPLAY_CONTRAST; config.Display.Language = display["language"] | DISPLAY_LANGUAGE; - config.Display.DiagramDuration = display["diagram_duration"] | DISPLAY_DIAGRAM_DURATION; + config.Display.Diagram.Duration = display["diagram_duration"] | DISPLAY_DIAGRAM_DURATION; + config.Display.Diagram.Mode = display["diagram_mode"] | DISPLAY_DIAGRAM_MODE; JsonArray leds = device["led"]; for (uint8_t i = 0; i < PINMAPPING_LED_COUNT; i++) { @@ -513,6 +517,11 @@ void ConfigurationClass::migrate() nvs_flash_init(); } + if (config.Cfg.Version < 0x00011b00) { + // Convert from kHz to Hz + config.Dtu.Cmt.Frequency *= 1000; + } + f.close(); config.Cfg.Version = CONFIG_VERSION; diff --git a/src/Display_Graphic.cpp b/src/Display_Graphic.cpp index feb4ce804..04195fd66 100644 --- a/src/Display_Graphic.cpp +++ b/src/Display_Graphic.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2023 Thomas Basler and others + * Copyright (C) 2023-2024 Thomas Basler and others */ #include "Display_Graphic.h" #include "Datastore.h" @@ -28,8 +28,8 @@ const uint8_t languages[] = { }; static const char* const i18n_offline[] = { "Offline", "Offline", "Offline" }; -static const char* const i18n_current_power_w[] = { "%3.0f W", "%3.0f W", "%3.0f W" }; -static const char* const i18n_current_power_kw[] = { "%2.1f kW", "%2.1f kW", "%2.1f kW" }; +static const char* const i18n_current_power_w[] = { "%.0f W", "%.0f W", "%.0f W" }; +static const char* const i18n_current_power_kw[] = { "%.1f kW", "%.1f kW", "%.1f kW" }; static const char* const i18n_yield_today_wh[] = { "today: %4.0f Wh", "Heute: %4.0f Wh", "auj.: %4.0f Wh" }; static const char* const i18n_yield_total_kwh[] = { "total: %.1f kWh", "Ges.: %.1f kWh", "total: %.1f kWh" }; static const char* const i18n_date_format[] = { "%m/%d/%Y %H:%M", "%d.%m.%Y %H:%M", "%d/%m/%Y %H:%M" }; @@ -94,13 +94,31 @@ bool DisplayGraphicClass::isValidDisplay() void DisplayGraphicClass::printText(const char* text, const uint8_t line) { + setFont(line); + uint8_t dispX; if (!_isLarge) { dispX = (line == 0) ? 5 : 0; } else { - dispX = (line == 0) ? 10 : 5; + switch (line) { + case 0: + if (_diagram_mode == DiagramMode_t::Small) { + // Center between left border and diagram + dispX = (CHART_POSX - _display->getStrWidth(text)) / 2; + } else { + // Center on screen + dispX = (_display->getDisplayWidth() - _display->getStrWidth(text)) / 2; + } + break; + case 3: + // Center on screen + dispX = (_display->getDisplayWidth() - _display->getStrWidth(text)) / 2; + break; + default: + dispX = 5; + break; + } } - setFont(line); dispX += enableScreensaver ? (_mExtra % 7) : 0; _display->drawStr(dispX, _lineOffsets[line], text); @@ -136,6 +154,13 @@ void DisplayGraphicClass::setLanguage(const uint8_t language) _display_language = language < sizeof(languages) / sizeof(languages[0]) ? language : DISPLAY_LANGUAGE; } +void DisplayGraphicClass::setDiagramMode(DiagramMode_t mode) +{ + if (mode < DiagramMode_t::DisplayMode_Max) { + _diagram_mode = mode; + } +} + void DisplayGraphicClass::setStartupDisplay() { if (!isValidDisplay()) { @@ -158,21 +183,37 @@ void DisplayGraphicClass::loop() _display->clearBuffer(); bool displayPowerSave = false; + bool showText = true; //=====> Actual Production ========== if (Datastore.getIsAtLeastOneReachable()) { displayPowerSave = false; if (_isLarge) { uint8_t screenSaverOffsetX = enableScreensaver ? (_mExtra % 7) : 0; - _diagram.redraw(screenSaverOffsetX); + switch (_diagram_mode) { + case DiagramMode_t::Small: + _diagram.redraw(screenSaverOffsetX, CHART_POSX, CHART_POSY, CHART_WIDTH, CHART_HEIGHT, false); + break; + case DiagramMode_t::Fullscreen: + // Every 10 seconds + if (_mExtra % (10 * 2) < 10) { + _diagram.redraw(screenSaverOffsetX, 10, 0, _display->getDisplayWidth() - 12, _display->getDisplayHeight() - 3, true); + showText = false; + } + break; + default: + break; + } } - const float watts = Datastore.getTotalAcPowerEnabled(); - if (watts > 999) { - snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_kw[_display_language], watts / 1000); - } else { - snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_w[_display_language], watts); + if (showText) { + const float watts = Datastore.getTotalAcPowerEnabled(); + if (watts > 999) { + snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_kw[_display_language], watts / 1000); + } else { + snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_w[_display_language], watts); + } + printText(_fmtText, 0); } - printText(_fmtText, 0); _previousMillis = millis(); } //<======================= @@ -187,23 +228,27 @@ void DisplayGraphicClass::loop() } //<======================= - //=====> Today & Total Production ======= - snprintf(_fmtText, sizeof(_fmtText), i18n_yield_today_wh[_display_language], Datastore.getTotalAcYieldDayEnabled()); - printText(_fmtText, 1); + if (showText) { + //=====> Today & Total Production ======= + snprintf(_fmtText, sizeof(_fmtText), i18n_yield_today_wh[_display_language], Datastore.getTotalAcYieldDayEnabled()); + printText(_fmtText, 1); - snprintf(_fmtText, sizeof(_fmtText), i18n_yield_total_kwh[_display_language], Datastore.getTotalAcYieldTotalEnabled()); - printText(_fmtText, 2); - //<======================= + snprintf(_fmtText, sizeof(_fmtText), i18n_yield_total_kwh[_display_language], Datastore.getTotalAcYieldTotalEnabled()); + printText(_fmtText, 2); + //<======================= - //=====> IP or Date-Time ======== - if (!(_mExtra % 10) && NetworkSettings.localIP()) { - printText(NetworkSettings.localIP().toString().c_str(), 3); - } else { - // Get current time - time_t now = time(nullptr); - strftime(_fmtText, sizeof(_fmtText), i18n_date_format[_display_language], localtime(&now)); - printText(_fmtText, 3); + //=====> IP or Date-Time ======== + // Change every 3 seconds + if (!(_mExtra % (3 * 2) < 3) && NetworkSettings.localIP()) { + printText(NetworkSettings.localIP().toString().c_str(), 3); + } else { + // Get current time + time_t now = time(nullptr); + strftime(_fmtText, sizeof(_fmtText), i18n_date_format[_display_language], localtime(&now)); + printText(_fmtText, 3); + } } + _display->sendBuffer(); _mExtra++; diff --git a/src/Display_Graphic_Diagram.cpp b/src/Display_Graphic_Diagram.cpp index 0fd136184..498123b90 100644 --- a/src/Display_Graphic_Diagram.cpp +++ b/src/Display_Graphic_Diagram.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2023 Thomas Basler and others + * Copyright (C) 2023-2024 Thomas Basler and others */ #include "Display_Graphic_Diagram.h" #include "Configuration.h" @@ -52,38 +52,39 @@ void DisplayGraphicDiagramClass::dataPointLoop() uint32_t DisplayGraphicDiagramClass::getSecondsPerDot() { - return Configuration.get().Display.DiagramDuration / CHART_WIDTH; + return Configuration.get().Display.Diagram.Duration / _chartWidth; } void DisplayGraphicDiagramClass::updatePeriod() { - _dataPointTask.setInterval(getSecondsPerDot() * TASK_SECOND); + // Calculate seconds per datapoint + _dataPointTask.setInterval(Configuration.get().Display.Diagram.Duration * TASK_SECOND / MAX_DATAPOINTS ); } -void DisplayGraphicDiagramClass::redraw(uint8_t screenSaverOffsetX) +void DisplayGraphicDiagramClass::redraw(uint8_t screenSaverOffsetX, uint8_t xPos, uint8_t yPos, uint8_t width, uint8_t height, bool isFullscreen) { + _chartWidth = width; + // screenSaverOffsetX expected to be in range 0..6 - const uint8_t graphPosX = DIAG_POSX + ((screenSaverOffsetX > 3) ? 1 : 0); - const uint8_t graphPosY = DIAG_POSY + ((screenSaverOffsetX > 3) ? 1 : 0); + const uint8_t graphPosX = xPos + ((screenSaverOffsetX > 3) ? 1 : 0); + const uint8_t graphPosY = yPos + ((screenSaverOffsetX > 3) ? 1 : 0); - const uint8_t horizontal_line_y = graphPosY + CHART_HEIGHT - 1; + const uint8_t horizontal_line_y = graphPosY + height - 1; const uint8_t arrow_size = 2; // draw diagram axis - _display->drawVLine(graphPosX, graphPosY, CHART_HEIGHT); - _display->drawHLine(graphPosX, horizontal_line_y, CHART_WIDTH); + _display->drawVLine(graphPosX, graphPosY, height); + _display->drawHLine(graphPosX, horizontal_line_y, width); // UP-arrow _display->drawLine(graphPosX, graphPosY, graphPosX + arrow_size, graphPosY + arrow_size); _display->drawLine(graphPosX, graphPosY, graphPosX - arrow_size, graphPosY + arrow_size); // LEFT-arrow - _display->drawLine(graphPosX + CHART_WIDTH - 1, horizontal_line_y, graphPosX + CHART_WIDTH - 1 - arrow_size, horizontal_line_y - arrow_size); - _display->drawLine(graphPosX + CHART_WIDTH - 1, horizontal_line_y, graphPosX + CHART_WIDTH - 1 - arrow_size, horizontal_line_y + arrow_size); + _display->drawLine(graphPosX + width - 1, horizontal_line_y, graphPosX + width - 1 - arrow_size, horizontal_line_y - arrow_size); + _display->drawLine(graphPosX + width - 1, horizontal_line_y, graphPosX + width - 1 - arrow_size, horizontal_line_y + arrow_size); // draw AC value - // 4 pixels per char - _display->setFont(u8g2_font_tom_thumb_4x6_mr); char fmtText[7]; const float maxWatts = *std::max_element(_graphValues.begin(), _graphValues.end()); if (maxWatts > 999) { @@ -91,25 +92,46 @@ void DisplayGraphicDiagramClass::redraw(uint8_t screenSaverOffsetX) } else { snprintf(fmtText, sizeof(fmtText), "%dW", static_cast(maxWatts)); } - const uint8_t textLength = strlen(fmtText); - _display->drawStr(graphPosX - arrow_size - textLength * 4, graphPosY + 5, fmtText); + + if (isFullscreen) { + _display->setFont(u8g2_font_5x8_tr); + _display->setFontDirection(3); + _display->drawStr(graphPosX - arrow_size, graphPosY + _display->getStrWidth(fmtText), fmtText); + _display->setFontDirection(0); + } else { + // 4 pixels per char + _display->setFont(u8g2_font_tom_thumb_4x6_mr); + _display->drawStr(graphPosX - arrow_size - _display->getStrWidth(fmtText), graphPosY + 5, fmtText); + } // draw chart - const float scaleFactor = maxWatts / CHART_HEIGHT; - uint8_t axisTick = 1; + const float scaleFactorY = maxWatts / static_cast(height); + const float scaleFactorX = static_cast(MAX_DATAPOINTS) / static_cast(_chartWidth); + + if (maxWatts > 0 && isFullscreen) { + // draw y axis ticks + const uint16_t yAxisWattPerTick = maxWatts <= 100 ? 10 : maxWatts <= 1000 ? 100 : maxWatts < 5000 ? 500 : 1000; + const uint8_t yAxisTickSizePixel = height / (maxWatts / yAxisWattPerTick); + + for (int16_t tickYPos = graphPosY + height; tickYPos > graphPosY - arrow_size; tickYPos -= yAxisTickSizePixel) { + _display->drawPixel(graphPosX - 1, tickYPos); + } + } + + uint8_t xAxisTicks = 1; for (uint8_t i = 1; i < _graphValuesCount; i++) { // draw one tick per hour to the x-axis - if (i * getSecondsPerDot() > (3600u * axisTick)) { - _display->drawPixel(graphPosX + 1 + i, graphPosY + CHART_HEIGHT); - axisTick++; + if (i * getSecondsPerDot() > (3600u * xAxisTicks)) { + _display->drawPixel((graphPosX + 1 + i) * scaleFactorX, graphPosY + height); + xAxisTicks++; } - if (scaleFactor == 0) { + if (scaleFactorY == 0 || scaleFactorX == 0) { continue; } _display->drawLine( - graphPosX + i - 1, horizontal_line_y - std::max(0, _graphValues[i - 1] / scaleFactor - 0.5), - graphPosX + i, horizontal_line_y - std::max(0, _graphValues[i] / scaleFactor - 0.5)); + graphPosX + (i - 1) / scaleFactorX, horizontal_line_y - std::max(0, _graphValues[i - 1] / scaleFactorY - 0.5), + graphPosX + i / scaleFactorX, horizontal_line_y - std::max(0, _graphValues[i] / scaleFactorY - 0.5)); } } diff --git a/src/HttpPowerMeter.cpp b/src/HttpPowerMeter.cpp index 09da47a53..ade8d3802 100644 --- a/src/HttpPowerMeter.cpp +++ b/src/HttpPowerMeter.cpp @@ -3,11 +3,12 @@ #include "HttpPowerMeter.h" #include "MessageOutput.h" #include -#include +#include #include #include #include #include +#include void HttpPowerMeterClass::init() { @@ -20,10 +21,7 @@ float HttpPowerMeterClass::getPower(int8_t phase) bool HttpPowerMeterClass::updateValues() { - const CONFIG_T& config = Configuration.get(); - - char response[2000], - errorMessage[256]; + const CONFIG_T& config = Configuration.get(); for (uint8_t i = 0; i < POWERMETER_MAX_PHASES; i++) { POWERMETER_HTTP_PHASE_CONFIG_T phaseConfig = config.PowerMeter.Http_Phase[i]; @@ -34,53 +32,83 @@ bool HttpPowerMeterClass::updateValues() } if (i == 0 || config.PowerMeter.HttpIndividualRequests) { - if (httpRequest(phaseConfig.Url, phaseConfig.AuthType, phaseConfig.Username, phaseConfig.Password, phaseConfig.HeaderKey, phaseConfig.HeaderValue, phaseConfig.Timeout, - response, sizeof(response), errorMessage, sizeof(errorMessage))) { - if (!getFloatValueByJsonPath(response, phaseConfig.JsonPath, power[i])) { - MessageOutput.printf("[HttpPowerMeter] Couldn't find a value with Json query \"%s\"\r\n", phaseConfig.JsonPath); - return false; - } - } else { - MessageOutput.printf("[HttpPowerMeter] Getting the power of phase %d failed. Error: %s\r\n", - i + 1, errorMessage); + if (!queryPhase(i, phaseConfig.Url, phaseConfig.AuthType, phaseConfig.Username, phaseConfig.Password, phaseConfig.HeaderKey, phaseConfig.HeaderValue, phaseConfig.Timeout, + phaseConfig.JsonPath)) { + MessageOutput.printf("[HttpPowerMeter] Getting the power of phase %d failed.\r\n", i + 1); + MessageOutput.printf("%s\r\n", httpPowerMeterError); return false; } } } - return true; } -bool HttpPowerMeterClass::httpRequest(const char* url, Auth authType, const char* username, const char* password, const char* httpHeader, const char* httpValue, uint32_t timeout, - char* response, size_t responseSize, char* error, size_t errorSize) +bool HttpPowerMeterClass::queryPhase(int phase, const String& url, Auth authType, const char* username, const char* password, + const char* httpHeader, const char* httpValue, uint32_t timeout, const char* jsonPath) { - String urlProtocol; - String urlHostname; - String urlUri; - extractUrlComponents(url, urlProtocol, urlHostname, urlUri); - - response[0] = '\0'; - error[0] = '\0'; + //hostByName in WiFiGeneric fails to resolve local names. issue described in + //https://github.com/espressif/arduino-esp32/issues/3822 + //and in depth analyzed in https://github.com/espressif/esp-idf/issues/2507#issuecomment-761836300 + //in conclusion: we cannot rely on httpClient.begin(*wifiClient, url) to resolve IP adresses. + //have to do it manually here. Feels Hacky... + String protocol; + String host; + String uri; + extractUrlComponents(url, protocol, host, uri); + + IPAddress ipaddr((uint32_t)0); + //first check if "host" is already an IP adress + if (!ipaddr.fromString(host)) + { + //"host"" is not an IP address so try to resolve the IP adress + //first try locally via mDNS, then via DNS. WiFiGeneric::hostByName() will spam the console if done the otherway around. + const bool mdnsEnabled = Configuration.get().Mdns.Enabled; + if (!mdnsEnabled) { + snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError), PSTR("Error resolving host %s via DNS, try to enable mDNS in Network Settings"), host.c_str()); + //ensure we try resolving via DNS even if mDNS is disabled + if(!WiFiGenericClass::hostByName(host.c_str(), ipaddr)){ + snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError), PSTR("Error resolving host %s via DNS"), host.c_str()); + } + } + else + { + ipaddr = MDNS.queryHost(host); + if (ipaddr == INADDR_NONE){ + snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError), PSTR("Error resolving host %s via mDNS"), host.c_str()); + //when we cannot find local server via mDNS, try resolving via DNS + if(!WiFiGenericClass::hostByName(host.c_str(), ipaddr)){ + snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError), PSTR("Error resolving host %s via DNS"), host.c_str()); + } + } + } + } // secureWifiClient MUST be created before HTTPClient // see discussion: https://github.com/helgeerbe/OpenDTU-OnBattery/issues/381 std::unique_ptr wifiClient; - if (urlProtocol == "https") { + bool https = protocol == "https"; + if (https) { auto secureWifiClient = std::make_unique(); secureWifiClient->setInsecure(); wifiClient = std::move(secureWifiClient); } else { wifiClient = std::make_unique(); } + + return httpRequest(phase, *wifiClient, ipaddr.toString(), uri, https, authType, username, password, httpHeader, httpValue, timeout, jsonPath); +} - - if (!httpClient.begin(*wifiClient, url)) { - snprintf_P(error, errorSize, "httpClient.begin(%s) failed", url); - return false; +bool HttpPowerMeterClass::httpRequest(int phase, WiFiClient &wifiClient, const String& host, const String& uri, bool https, Auth authType, const char* username, + const char* password, const char* httpHeader, const char* httpValue, uint32_t timeout, const char* jsonPath) +{ + int port = (https ? 443 : 80); + if(!httpClient.begin(wifiClient, host, port, uri, https)){ + snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError), PSTR("httpClient.begin() failed for %s://%s"), (https ? "https" : "http"), host.c_str()); + return false; } - prepareRequest(timeout, httpHeader, httpValue); - + + prepareRequest(timeout, httpHeader, httpValue); if (authType == Auth::digest) { const char *headers[1] = {"WWW-Authenticate"}; httpClient.collectHeaders(headers, 1); @@ -92,111 +120,108 @@ bool HttpPowerMeterClass::httpRequest(const char* url, Auth authType, const char auth.concat(base64::encode(authString)); httpClient.addHeader("Authorization", auth); } - int httpCode = httpClient.GET(); + if (httpCode == HTTP_CODE_UNAUTHORIZED && authType == Auth::digest) { // Handle authentication challenge - char realm[256]; // Buffer to store the realm received from the server - char nonce[256]; // Buffer to store the nonce received from the server if (httpClient.hasHeader("WWW-Authenticate")) { - String authHeader = httpClient.header("WWW-Authenticate"); - if (authHeader.indexOf("Digest") != -1) { - int realmIndex = authHeader.indexOf("realm=\""); - int nonceIndex = authHeader.indexOf("nonce=\""); - if (realmIndex != -1 && nonceIndex != -1) { - int realmEndIndex = authHeader.indexOf("\"", realmIndex + 7); - int nonceEndIndex = authHeader.indexOf("\"", nonceIndex + 7); - if (realmEndIndex != -1 && nonceEndIndex != -1) { - authHeader.substring(realmIndex + 7, realmEndIndex).toCharArray(realm, sizeof(realm)); - authHeader.substring(nonceIndex + 7, nonceEndIndex).toCharArray(nonce, sizeof(nonce)); - } - } - String cnonce = String(random(1000)); // Generate client nonce - String str = username; - str += ":"; - str += realm; - str += ":"; - str += password; - String ha1 = sha256(str); - str = "GET:"; - str += urlUri; - String ha2 = sha256(str); - str = ha1; - str += ":"; - str += nonce; - str += ":00000001:"; - str += cnonce; - str += ":auth:"; - str += ha2; - String response = sha256(str); - - String authorization = "Digest username=\""; - authorization += username; - authorization += "\", realm=\""; - authorization += realm; - authorization += "\", nonce=\""; - authorization += nonce; - authorization += "\", uri=\""; - authorization += urlUri; - authorization += "\", cnonce=\""; - authorization += cnonce; - authorization += "\", nc=00000001, qop=auth, response=\""; - authorization += response; - authorization += "\", algorithm=SHA-256"; - httpClient.end(); - if (!httpClient.begin(*wifiClient, url)) { - snprintf_P(error, errorSize, "httpClient.begin(%s) for digest auth failed", url); - return false; - } - prepareRequest(timeout, httpHeader, httpValue); - httpClient.addHeader("Authorization", authorization); - httpCode = httpClient.GET(); + String authReq = httpClient.header("WWW-Authenticate"); + String authorization = getDigestAuth(authReq, String(username), String(password), "GET", String(uri), 1); + httpClient.end(); + if(!httpClient.begin(wifiClient, host, port, uri, https)){ + snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError), PSTR("httpClient.begin() failed for %s://%s using digest auth"), (https ? "https" : "http"), host.c_str()); + return false; } - } - } - - if (httpCode == HTTP_CODE_OK) { - String responseBody = httpClient.getString(); - if (responseBody.length() > (responseSize - 1)) { - snprintf_P(error, errorSize, "Response too large! Response length: %d Body start: %s", - httpClient.getSize(), responseBody.c_str()); - } else { - snprintf(response, responseSize, responseBody.c_str()); + prepareRequest(timeout, httpHeader, httpValue); + httpClient.addHeader("Authorization", authorization); + httpCode = httpClient.GET(); } - } else if (httpCode <= 0) { - snprintf_P(error, errorSize, "Error(%s): %s", url, httpClient.errorToString(httpCode).c_str()); - } else if (httpCode != HTTP_CODE_OK) { - snprintf_P(error, errorSize, "Bad HTTP code: %d", httpCode); } - + bool result = tryGetFloatValueForPhase(phase, httpCode, jsonPath); httpClient.end(); + return result; +} - if (error[0] != '\0') { - return false; - } - - return true; +String HttpPowerMeterClass::extractParam(String& authReq, const String& param, const char delimit) { + int _begin = authReq.indexOf(param); + if (_begin == -1) { return ""; } + return authReq.substring(_begin + param.length(), authReq.indexOf(delimit, _begin + param.length())); } -float HttpPowerMeterClass::getFloatValueByJsonPath(const char* jsonString, const char* jsonPath, float& value) -{ - FirebaseJson firebaseJson; - firebaseJson.setJsonData(jsonString); +String HttpPowerMeterClass::getcNonce(const int len) { + static const char alphanum[] = "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + String s = ""; - FirebaseJsonData firebaseJsonResult; - if (!firebaseJson.get(firebaseJsonResult, jsonPath)) { - return false; - } + for (int i = 0; i < len; ++i) { s += alphanum[rand() % (sizeof(alphanum) - 1)]; } - value = firebaseJsonResult.to(); + return s; +} - firebaseJson.clear(); +String HttpPowerMeterClass::getDigestAuth(String& authReq, const String& username, const String& password, const String& method, const String& uri, unsigned int counter) { + // extracting required parameters for RFC 2617 Digest + String realm = extractParam(authReq, "realm=\"", '"'); + String nonce = extractParam(authReq, "nonce=\"", '"'); + String cNonce = getcNonce(8); + + char nc[9]; + snprintf(nc, sizeof(nc), "%08x", counter); + + //sha256 of the user:realm:password + String ha1 = sha256(username + ":" + realm + ":" + password); + + //sha256 of method:uri + String ha2 = sha256(method + ":" + uri); + + //sha256 of h1:nonce:nc:cNonce:auth:h2 + String response = sha256(ha1 + ":" + nonce + ":" + String(nc) + ":" + cNonce + ":" + "auth" + ":" + ha2); + + //Final authorization String; + String authorization = "Digest username=\""; + authorization += username; + authorization += "\", realm=\""; + authorization += realm; + authorization += "\", nonce=\""; + authorization += nonce; + authorization += "\", uri=\""; + authorization += uri; + authorization += "\", cnonce=\""; + authorization += cNonce; + authorization += "\", nc="; + authorization += String(nc); + authorization += ", qop=auth, response=\""; + authorization += response; + authorization += "\", algorithm=SHA-256"; + + return authorization; +} - return true; +bool HttpPowerMeterClass::tryGetFloatValueForPhase(int phase, int httpCode, const char* jsonPath) +{ + bool success = false; + if (httpCode == HTTP_CODE_OK) { + httpResponse = httpClient.getString(); //very unfortunate that we cannot parse WifiClient stream directly + StaticJsonDocument<2048> json; //however creating these allocations on stack should be fine to avoid heap fragmentation + deserializeJson(json, httpResponse); + if(!json.containsKey(jsonPath)) + { + snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError), PSTR("[HttpPowerMeter] Couldn't find a value for phase %i with Json query \"%s\""), phase, jsonPath); + }else { + power[phase] = json[jsonPath].as(); + //MessageOutput.printf("Power for Phase %i: %5.2fW\r\n", phase, power[phase]); + success = true; + } + } else if (httpCode <= 0) { + snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError), PSTR("HTTP Error %s"), httpClient.errorToString(httpCode).c_str()); + } else if (httpCode != HTTP_CODE_OK) { + snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError), PSTR("Bad HTTP code: %d"), httpCode); + } + return success; } - void HttpPowerMeterClass::extractUrlComponents(const String& url, String& protocol, String& hostname, String& uri) { +void HttpPowerMeterClass::extractUrlComponents(const String& url, String& protocol, String& hostname, String& uri) { // Find protocol delimiter int protocolEndIndex = url.indexOf(":"); if (protocolEndIndex != -1) { @@ -229,25 +254,24 @@ float HttpPowerMeterClass::getFloatValueByJsonPath(const char* jsonString, const #define HASH_SIZE 32 String HttpPowerMeterClass::sha256(const String& data) { - SHA256 sha256; - uint8_t hash[HASH_SIZE]; - - sha256.reset(); - sha256.update(data.c_str(), data.length()); - sha256.finalize(hash, HASH_SIZE); - - String hashStr = ""; - for (int i = 0; i < HASH_SIZE; i++) { - String hex = String(hash[i], HEX); - if (hex.length() == 1) { - hashStr += "0"; + SHA256 sha256; + uint8_t hash[HASH_SIZE]; + + sha256.reset(); + sha256.update(data.c_str(), data.length()); + sha256.finalize(hash, HASH_SIZE); + + String hashStr = ""; + for (int i = 0; i < HASH_SIZE; i++) { + String hex = String(hash[i], HEX); + if (hex.length() == 1) { + hashStr += "0"; + } + hashStr += hex; } - hashStr += hex; - } - return hashStr; + return hashStr; } - void HttpPowerMeterClass::prepareRequest(uint32_t timeout, const char* httpHeader, const char* httpValue) { httpClient.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); httpClient.setUserAgent("OpenDTU-OnBattery"); diff --git a/src/InverterSettings.cpp b/src/InverterSettings.cpp index a81527bf4..02bef2f1e 100644 --- a/src/InverterSettings.cpp +++ b/src/InverterSettings.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2023 Thomas Basler and others + * Copyright (C) 2023-2024 Thomas Basler and others */ #include "InverterSettings.h" #include "Configuration.h" @@ -45,6 +45,8 @@ void InverterSettingsClass::init(Scheduler& scheduler) if (PinMapping.isValidCmt2300Config()) { Hoymiles.initCMT(pin.cmt_sdio, pin.cmt_clk, pin.cmt_cs, pin.cmt_fcs, pin.cmt_gpio2, pin.cmt_gpio3); + MessageOutput.println(F(" Setting country mode... ")); + Hoymiles.getRadioCmt()->setCountryMode(static_cast(config.Dtu.Cmt.CountryMode)); MessageOutput.println(F(" Setting CMT target frequency... ")); Hoymiles.getRadioCmt()->setInverterTargetFrequency(config.Dtu.Cmt.Frequency); } diff --git a/src/WebApi_device.cpp b/src/WebApi_device.cpp index daf42be19..bb66563cd 100644 --- a/src/WebApi_device.cpp +++ b/src/WebApi_device.cpp @@ -83,7 +83,8 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request) display["screensaver"] = config.Display.ScreenSaver; display["contrast"] = config.Display.Contrast; display["language"] = config.Display.Language; - display["diagramduration"] = config.Display.DiagramDuration; + display["diagramduration"] = config.Display.Diagram.Duration; + display["diagrammode"] = config.Display.Diagram.Mode; auto leds = root.createNestedArray("led"); for (uint8_t i = 0; i < PINMAPPING_LED_COUNT; i++) { @@ -179,7 +180,8 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request) config.Display.ScreenSaver = root["display"]["screensaver"].as(); config.Display.Contrast = root["display"]["contrast"].as(); config.Display.Language = root["display"]["language"].as(); - config.Display.DiagramDuration = root["display"]["diagramduration"].as(); + config.Display.Diagram.Duration = root["display"]["diagramduration"].as(); + config.Display.Diagram.Mode = root["display"]["diagrammode"].as(); for (uint8_t i = 0; i < PINMAPPING_LED_COUNT; i++) { config.Led_Single[i].Brightness = root["led"][i]["brightness"].as(); @@ -191,6 +193,7 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request) Display.enableScreensaver = config.Display.ScreenSaver; Display.setContrast(config.Display.Contrast); Display.setLanguage(config.Display.Language); + Display.setDiagramMode(static_cast(config.Display.Diagram.Mode)); Display.Diagram().updatePeriod(); WebApi.writeConfig(retMsg); @@ -201,4 +204,4 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request) if (performRestart) { Utils::restartDtu(); } -} \ No newline at end of file +} diff --git a/src/WebApi_dtu.cpp b/src/WebApi_dtu.cpp index 327d17e34..3aef58a92 100644 --- a/src/WebApi_dtu.cpp +++ b/src/WebApi_dtu.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022-2023 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ #include "WebApi_dtu.h" #include "Configuration.h" @@ -21,6 +21,18 @@ void WebApiDtuClass::init(AsyncWebServer& server) void WebApiDtuClass::loop() { + if (_performReload) { + // Execute stuff in main thread to avoid busy SPI bus + CONFIG_T& config = Configuration.get(); + Hoymiles.getRadioNrf()->setPALevel((rf24_pa_dbm_e)config.Dtu.Nrf.PaLevel); + Hoymiles.getRadioCmt()->setPALevel(config.Dtu.Cmt.PaLevel); + Hoymiles.getRadioNrf()->setDtuSerial(config.Dtu.Serial); + Hoymiles.getRadioCmt()->setDtuSerial(config.Dtu.Serial); + Hoymiles.getRadioCmt()->setCountryMode(static_cast(config.Dtu.Cmt.CountryMode)); + Hoymiles.getRadioCmt()->setInverterTargetFrequency(config.Dtu.Cmt.Frequency); + Hoymiles.setPollInterval(config.Dtu.PollInterval); + _performReload = false; + } } void WebApiDtuClass::onDtuAdminGet(AsyncWebServerRequest* request) @@ -46,6 +58,19 @@ void WebApiDtuClass::onDtuAdminGet(AsyncWebServerRequest* request) root["cmt_enabled"] = Hoymiles.getRadioCmt()->isInitialized(); root["cmt_palevel"] = config.Dtu.Cmt.PaLevel; root["cmt_frequency"] = config.Dtu.Cmt.Frequency; + root["cmt_country"] = config.Dtu.Cmt.CountryMode; + root["cmt_chan_width"] = Hoymiles.getRadioCmt()->getChannelWidth(); + + auto data = root.createNestedArray("country_def"); + auto countryDefs = Hoymiles.getRadioCmt()->getCountryFrequencyList(); + for (const auto& definition : countryDefs) { + auto obj = data.createNestedObject(); + obj["freq_default"] = definition.definition.Freq_Default; + obj["freq_min"] = definition.definition.Freq_Min; + obj["freq_max"] = definition.definition.Freq_Max; + obj["freq_legal_min"] = definition.definition.Freq_Legal_Min; + obj["freq_legal_max"] = definition.definition.Freq_Legal_Max; + } response->setLength(); request->send(response); @@ -95,7 +120,8 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request) && root.containsKey("verbose_logging") && root.containsKey("nrf_palevel") && root.containsKey("cmt_palevel") - && root.containsKey("cmt_frequency"))) { + && root.containsKey("cmt_frequency") + && root.containsKey("cmt_country"))) { retMsg["message"] = "Values are missing!"; retMsg["code"] = WebApiError::GenericValueMissing; response->setLength(); @@ -135,14 +161,23 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request) return; } - if (root["cmt_frequency"].as() < Hoymiles.getRadioCmt()->getMinFrequency() - || root["cmt_frequency"].as() > Hoymiles.getRadioCmt()->getMaxFrequency() - || root["cmt_frequency"].as() % 250 > 0) { + if (root["cmt_country"].as() >= CountryModeId_t::CountryModeId_Max) { + retMsg["message"] = "Invalid country setting!"; + retMsg["code"] = WebApiError::DtuInvalidCmtCountry; + response->setLength(); + request->send(response); + return; + } + + auto FrequencyDefinition = Hoymiles.getRadioCmt()->getCountryFrequencyList()[root["cmt_country"].as()].definition; + if (root["cmt_frequency"].as() < FrequencyDefinition.Freq_Min + || root["cmt_frequency"].as() > FrequencyDefinition.Freq_Max + || root["cmt_frequency"].as() % Hoymiles.getRadioCmt()->getChannelWidth() > 0) { retMsg["message"] = "Invalid CMT frequency setting!"; retMsg["code"] = WebApiError::DtuInvalidCmtFrequency; - retMsg["param"]["min"] = Hoymiles.getRadioCmt()->getMinFrequency(); - retMsg["param"]["max"] = Hoymiles.getRadioCmt()->getMaxFrequency(); + retMsg["param"]["min"] = FrequencyDefinition.Freq_Min; + retMsg["param"]["max"] = FrequencyDefinition.Freq_Max; response->setLength(); request->send(response); return; @@ -157,17 +192,12 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request) config.Dtu.Nrf.PaLevel = root["nrf_palevel"].as(); config.Dtu.Cmt.PaLevel = root["cmt_palevel"].as(); config.Dtu.Cmt.Frequency = root["cmt_frequency"].as(); + config.Dtu.Cmt.CountryMode = root["cmt_country"].as(); WebApi.writeConfig(retMsg); response->setLength(); request->send(response); - Hoymiles.getRadioNrf()->setPALevel((rf24_pa_dbm_e)config.Dtu.Nrf.PaLevel); - Hoymiles.getRadioCmt()->setPALevel(config.Dtu.Cmt.PaLevel); - Hoymiles.getRadioNrf()->setDtuSerial(config.Dtu.Serial); - Hoymiles.getRadioCmt()->setDtuSerial(config.Dtu.Serial); - Hoymiles.getRadioCmt()->setInverterTargetFrequency(config.Dtu.Cmt.Frequency); - Hoymiles.setPollInterval(config.Dtu.PollInterval); - Hoymiles.setVerboseLogging(config.Dtu.VerboseLogging); + _performReload = true; } diff --git a/src/WebApi_powermeter.cpp b/src/WebApi_powermeter.cpp index ee8f893c1..d91f251b6 100644 --- a/src/WebApi_powermeter.cpp +++ b/src/WebApi_powermeter.cpp @@ -200,6 +200,7 @@ void WebApiPowerMeterClass::onAdminPost(AsyncWebServerRequest* request) response->setLength(); request->send(response); + // reboot requiered as per https://github.com/helgeerbe/OpenDTU-OnBattery/issues/565#issuecomment-1872552559 yield(); delay(1000); yield(); @@ -251,25 +252,18 @@ void WebApiPowerMeterClass::onTestHttpRequest(AsyncWebServerRequest* request) return; } - char powerMeterResponse[2000], - errorMessage[256]; - char response[200]; - - if (HttpPowerMeter.httpRequest(root["url"].as().c_str(), - root["auth_type"].as(), root["username"].as().c_str(), root["password"].as().c_str(), - root["header_key"].as().c_str(), root["header_value"].as().c_str(), root["timeout"].as(), - powerMeterResponse, sizeof(powerMeterResponse), errorMessage, sizeof(errorMessage))) { - float power; - - if (HttpPowerMeter.getFloatValueByJsonPath(powerMeterResponse, - root["json_path"].as().c_str(), power)) { - retMsg["type"] = "success"; - snprintf_P(response, sizeof(response), "Success! Power: %5.2fW", power); - } else { - snprintf_P(response, sizeof(response), "Error: Could not find value for JSON path!"); - } + + char response[256]; + + int phase = 0;//"absuing" index 0 of the float power[3] in HttpPowerMeter to store the result + if (HttpPowerMeter.queryPhase(phase, root[F("url")].as().c_str(), + root[F("auth_type")].as(), root[F("username")].as().c_str(), root[F("password")].as().c_str(), + root[F("header_key")].as().c_str(), root[F("header_value")].as().c_str(), root[F("timeout")].as(), + root[F("json_path")].as().c_str())) { + retMsg[F("type")] = F("success"); + snprintf_P(response, sizeof(response), "Success! Power: %5.2fW", HttpPowerMeter.getPower(phase + 1)); } else { - snprintf_P(response, sizeof(response), errorMessage); + snprintf_P(response, sizeof(response), "%s", HttpPowerMeter.httpPowerMeterError); } retMsg["message"] = response; diff --git a/src/WebApi_prometheus.cpp b/src/WebApi_prometheus.cpp index b2e566545..5839dc802 100644 --- a/src/WebApi_prometheus.cpp +++ b/src/WebApi_prometheus.cpp @@ -82,6 +82,22 @@ void WebApiPrometheusClass::onPrometheusMetricsGet(AsyncWebServerRequest* reques stream->printf("opendtu_last_update{serial=\"%s\",unit=\"%d\",name=\"%s\"} %d\n", serial.c_str(), i, name, inv->Statistics()->getLastUpdate() / 1000); + if (i == 0) { + stream->print("# HELP opendtu_inverter_limit_relative current relative limit of the inverter\n"); + stream->print("# TYPE opendtu_inverter_limit_relative gauge\n"); + } + stream->printf("opendtu_inverter_limit_relative{serial=\"%s\",unit=\"%d\",name=\"%s\"} %f\n", + serial.c_str(), i, name, inv->SystemConfigPara()->getLimitPercent() / 100.0); + + if (inv->DevInfo()->getMaxPower() > 0) { + if (i == 0) { + stream->print("# HELP opendtu_inverter_limit_absolute current relative limit of the inverter\n"); + stream->print("# TYPE opendtu_inverter_limit_absolute gauge\n"); + } + stream->printf("opendtu_inverter_limit_absolute{serial=\"%s\",unit=\"%d\",name=\"%s\"} %f\n", + serial.c_str(), i, name, inv->SystemConfigPara()->getLimitPercent() * inv->DevInfo()->getMaxPower() / 100.0); + } + // Loop all channels if Statistics have been updated at least once since DTU boot if (inv->Statistics()->getLastUpdate() > 0) { for (auto& t : inv->Statistics()->getChannelTypes()) { diff --git a/src/main.cpp b/src/main.cpp index b02317740..38fcbc2e6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022-2023 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ #include "Configuration.h" #include "Datastore.h" @@ -139,6 +139,7 @@ void setup() Display.enableScreensaver = config.Display.ScreenSaver; Display.setContrast(config.Display.Contrast); Display.setLanguage(config.Display.Language); + Display.setDiagramMode(static_cast(config.Display.Diagram.Mode)); Display.setStartupDisplay(); MessageOutput.println("done"); diff --git a/webapp/package.json b/webapp/package.json index 8b36555a9..f587c00fd 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -18,8 +18,8 @@ "mitt": "^3.0.1", "sortablejs": "^1.15.1", "spark-md5": "^3.0.2", - "vue": "^3.4.5", - "vue-i18n": "^9.8.0", + "vue": "^3.4.13", + "vue-i18n": "^9.9.0", "vue-router": "^4.2.5" }, "devDependencies": { @@ -27,19 +27,21 @@ "@rushstack/eslint-patch": "^1.6.1", "@tsconfig/node18": "^18.2.2", "@types/bootstrap": "^5.2.10", - "@types/node": "^20.10.6", + "@types/node": "^20.11.0", + "@types/pulltorefreshjs": "^0.1.7", "@types/sortablejs": "^1.15.7", "@types/spark-md5": "^3.0.4", - "@vitejs/plugin-vue": "^5.0.2", + "@vitejs/plugin-vue": "^5.0.3", "@vue/eslint-config-typescript": "^12.0.0", "@vue/tsconfig": "^0.5.1", "eslint": "^8.56.0", - "eslint-plugin-vue": "^9.19.2", + "eslint-plugin-vue": "^9.20.1", "npm-run-all": "^4.1.5", + "pulltorefreshjs": "^0.1.22", "sass": "^1.69.7", "terser": "^5.26.0", "typescript": "^5.3.3", - "vite": "^5.0.10", + "vite": "^5.0.11", "vite-plugin-compression": "^0.5.1", "vite-plugin-css-injected-by-js": "^3.3.1", "vue-tsc": "^1.8.27" diff --git a/webapp/src/components/BasePage.vue b/webapp/src/components/BasePage.vue index 8bb463b53..10b924a89 100644 --- a/webapp/src/components/BasePage.vue +++ b/webapp/src/components/BasePage.vue @@ -4,7 +4,12 @@