Merge branch 'development'
This commit is contained in:
commit
7c66965ced
9
.editorconfig
Normal file
9
.editorconfig
Normal file
@ -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
|
||||
1
.vscode/extensions.json
vendored
1
.vscode/extensions.json
vendored
@ -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"
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
#include <cstdint>
|
||||
|
||||
#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 @@ public:
|
||||
INVERTER_CONFIG_T* getInverterConfig(const uint64_t serial);
|
||||
};
|
||||
|
||||
extern ConfigurationClass Configuration;
|
||||
extern ConfigurationClass Configuration;
|
||||
|
||||
@ -6,6 +6,14 @@
|
||||
#include <TaskSchedulerDeclarations.h>
|
||||
#include <U8g2lib.h>
|
||||
|
||||
#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 @@ public:
|
||||
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 @@ private:
|
||||
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;
|
||||
extern DisplayGraphicClass Display;
|
||||
|
||||
@ -5,20 +5,14 @@
|
||||
#include <U8g2lib.h>
|
||||
#include <array>
|
||||
|
||||
#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 @@ private:
|
||||
void averageLoop();
|
||||
void dataPointLoop();
|
||||
|
||||
static uint32_t getSecondsPerDot();
|
||||
uint32_t getSecondsPerDot();
|
||||
|
||||
Task _averageTask;
|
||||
Task _dataPointTask;
|
||||
|
||||
U8G2* _display = nullptr;
|
||||
std::array<float, CHART_WIDTH> _graphValues = {};
|
||||
std::array<float, MAX_DATAPOINTS> _graphValues = {};
|
||||
uint8_t _graphValuesCount = 0;
|
||||
|
||||
uint8_t _chartWidth = MAX_DATAPOINTS;
|
||||
|
||||
float _iRunningAverage = 0;
|
||||
uint16_t _iRunningAverageCnt = 0;
|
||||
};
|
||||
|
||||
@ -10,17 +10,24 @@ public:
|
||||
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;
|
||||
|
||||
@ -13,4 +13,5 @@ private:
|
||||
void onDtuAdminPost(AsyncWebServerRequest* request);
|
||||
|
||||
AsyncWebServer* _server;
|
||||
};
|
||||
bool _performReload = false;
|
||||
};
|
||||
|
||||
@ -15,6 +15,7 @@ enum WebApiError {
|
||||
DtuPollZero,
|
||||
DtuInvalidPowerLevel,
|
||||
DtuInvalidCmtFrequency,
|
||||
DtuInvalidCmtCountry,
|
||||
|
||||
ConfigBase = 3000,
|
||||
ConfigNotDeleted,
|
||||
@ -89,4 +90,4 @@ enum WebApiError {
|
||||
|
||||
HardwareBase = 12000,
|
||||
HardwarePinMappingLength,
|
||||
};
|
||||
};
|
||||
|
||||
@ -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
|
||||
#define MAX_INVERTER_LIMIT 2250
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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 <stdint.h>
|
||||
|
||||
#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 <stdint.h>
|
||||
|
||||
#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
|
||||
|
||||
@ -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 <Arduino.h>
|
||||
|
||||
/*! ********************************************************
|
||||
* @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 <Arduino.h>
|
||||
|
||||
/*! ********************************************************
|
||||
* @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);
|
||||
}
|
||||
|
||||
@ -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 <stdint.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
#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 <stdint.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
#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
|
||||
|
||||
@ -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 <stdint.h>
|
||||
|
||||
/* [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,
|
||||
214
lib/CMT2300a/cmt2300a_params_900.h
Normal file
214
lib/CMT2300a/cmt2300a_params_900.h
Normal file
@ -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 <stdint.h>
|
||||
|
||||
/* [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
|
||||
@ -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<uint8_t*>(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;
|
||||
|
||||
@ -4,13 +4,21 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#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 @@ public:
|
||||
* 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 @@ public:
|
||||
|
||||
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 @@ private:
|
||||
int8_t _pin_cs;
|
||||
int8_t _pin_fcs;
|
||||
uint32_t _spi_speed;
|
||||
};
|
||||
|
||||
FrequencyBand_t _frequencyBand = FrequencyBand_t::BAND_860;
|
||||
};
|
||||
|
||||
@ -1,142 +1,142 @@
|
||||
#include "cmt_spi3.h"
|
||||
#include <Arduino.h>
|
||||
#include <driver/spi_master.h>
|
||||
#include <esp_rom_gpio.h> // 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 <Arduino.h>
|
||||
#include <driver/spi_master.h>
|
||||
#include <esp_rom_gpio.h> // 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();
|
||||
}
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
#ifndef __CMT_SPI3_H
|
||||
#define __CMT_SPI3_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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 <stdint.h>
|
||||
|
||||
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
|
||||
|
||||
@ -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 <FunctionalInterrupt.h>
|
||||
#include <frozen/map.h>
|
||||
|
||||
#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)
|
||||
|
||||
// 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)
|
||||
|
||||
// =923500, 0xFF does not work
|
||||
#define MAX_FREQ_KHZ ((HOY_BASE_FREQ + 0xFE * CMT2300A_ONE_STEP_SIZE * FH_OFFSET) / 1000)
|
||||
|
||||
float HoymilesRadio_CMT::getFrequencyFromChannel(const uint8_t channel)
|
||||
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)
|
||||
{
|
||||
return (CMT_BASE_FREQ + (CMT_BASE_CH_OFFSET860 + channel) * FH_OFFSET * CMT2300A_ONE_STEP_SIZE) / 1000000.0;
|
||||
// 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();
|
||||
|
||||
CountryFrequencyDefinition_t v = { Band, minFrequency, maxFrequency, Freq_Legal_Min, Freq_Legal_Max, Freq_Default, Freq_StartUp };
|
||||
return v;
|
||||
}
|
||||
|
||||
uint8_t HoymilesRadio_CMT::getChannelFromFrequency(const uint32_t freq_kHz)
|
||||
constexpr frozen::map<CountryModeId_t, CountryFrequencyDefinition_t, 3> 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) },
|
||||
};
|
||||
|
||||
uint32_t HoymilesRadio_CMT::getFrequencyFromChannel(const uint8_t channel) const
|
||||
{
|
||||
if ((freq_kHz % 250) != 0) {
|
||||
Hoymiles.getMessageOutput()->printf("%.3f MHz is not divisible by 250 kHz!\r\n", freq_kHz / 1000.0);
|
||||
return (_radio->getBaseFrequency() + channel * getChannelWidth());
|
||||
}
|
||||
|
||||
uint8_t HoymilesRadio_CMT::getChannelFromFrequency(const uint32_t frequency) const
|
||||
{
|
||||
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<uint32_t>(countryDefinition.at(_countryMode).Freq_Legal_Min / 1e6),
|
||||
static_cast<uint32_t>(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<CountryFrequencyList_t> HoymilesRadio_CMT::getCountryFrequencyList() const
|
||||
{
|
||||
const uint8_t toChannel = getChannelFromFrequency(to_freq_kHz);
|
||||
std::vector<CountryFrequencyList_t> 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 MIN_FREQ_KHZ;
|
||||
return countryDefinition.at(_countryMode).Freq_Min;
|
||||
}
|
||||
|
||||
uint32_t HoymilesRadio_CMT::getMaxFrequency()
|
||||
uint32_t HoymilesRadio_CMT::getMaxFrequency() const
|
||||
{
|
||||
return MAX_FREQ_KHZ;
|
||||
return countryDefinition.at(_countryMode).Freq_Max;
|
||||
}
|
||||
|
||||
CountryModeId_t HoymilesRadio_CMT::getCountryMode() const
|
||||
{
|
||||
return _countryMode;
|
||||
}
|
||||
|
||||
void HoymilesRadio_CMT::setCountryMode(const CountryModeId_t mode)
|
||||
{
|
||||
_radio->setFrequencyBand(countryDefinition.at(mode).Band);
|
||||
_countryMode = mode;
|
||||
}
|
||||
|
||||
uint32_t HoymilesRadio_CMT::getInvBootFrequency() const
|
||||
{
|
||||
// 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())) {
|
||||
|
||||
@ -8,14 +8,37 @@
|
||||
#include <cmt2300wrapper.h>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
// 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 @@ public:
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static float getFrequencyFromChannel(const uint8_t channel);
|
||||
static uint8_t getChannelFromFrequency(const uint32_t freq_kHz);
|
||||
CountryModeId_t getCountryMode() const;
|
||||
void setCountryMode(const CountryModeId_t mode);
|
||||
|
||||
uint32_t getInvBootFrequency() const;
|
||||
|
||||
uint32_t getFrequencyFromChannel(const uint8_t channel) const;
|
||||
uint8_t getChannelFromFrequency(const uint32_t frequency) const;
|
||||
|
||||
std::vector<CountryFrequencyList_t> getCountryFrequencyList() const;
|
||||
|
||||
private:
|
||||
void ARDUINO_ISR_ATTR handleInt1();
|
||||
@ -51,5 +85,7 @@ private:
|
||||
|
||||
uint32_t _inverterTargetFrequency = HOYMILES_CMT_WORK_FREQ;
|
||||
|
||||
bool cmtSwitchDtuFreq(const uint32_t to_freq_kHz);
|
||||
};
|
||||
bool cmtSwitchDtuFreq(const uint32_t to_frequency);
|
||||
|
||||
CountryModeId_t _countryMode;
|
||||
};
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "CommandAbstract.h"
|
||||
#include "../HoymilesRadio_CMT.h"
|
||||
|
||||
class ChannelChangeCommand : public CommandAbstract {
|
||||
public:
|
||||
@ -12,7 +13,9 @@ public:
|
||||
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();
|
||||
};
|
||||
};
|
||||
|
||||
@ -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<ChannelChangeCommand>();
|
||||
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);
|
||||
|
||||
|
||||
@ -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<ChannelChangeCommand>();
|
||||
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;
|
||||
};
|
||||
};
|
||||
|
||||
@ -9,7 +9,10 @@ import re
|
||||
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
|
||||
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
|
||||
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='')
|
||||
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()
|
||||
main()
|
||||
|
||||
@ -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
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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++;
|
||||
|
||||
@ -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)
|
||||
{
|
||||
// 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);
|
||||
_chartWidth = width;
|
||||
|
||||
const uint8_t horizontal_line_y = graphPosY + CHART_HEIGHT - 1;
|
||||
// screenSaverOffsetX expected to be in range 0..6
|
||||
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 + 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<uint16_t>(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<float>(height);
|
||||
const float scaleFactorX = static_cast<float>(MAX_DATAPOINTS) / static_cast<float>(_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<int16_t>(0, _graphValues[i - 1] / scaleFactor - 0.5),
|
||||
graphPosX + i, horizontal_line_y - std::max<int16_t>(0, _graphValues[i] / scaleFactor - 0.5));
|
||||
graphPosX + (i - 1) / scaleFactorX, horizontal_line_y - std::max<int16_t>(0, _graphValues[i - 1] / scaleFactorY - 0.5),
|
||||
graphPosX + i / scaleFactorX, horizontal_line_y - std::max<int16_t>(0, _graphValues[i] / scaleFactorY - 0.5));
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,11 +3,12 @@
|
||||
#include "HttpPowerMeter.h"
|
||||
#include "MessageOutput.h"
|
||||
#include <WiFiClientSecure.h>
|
||||
#include <FirebaseJson.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <Crypto.h>
|
||||
#include <SHA256.h>
|
||||
#include <base64.h>
|
||||
#include <memory>
|
||||
#include <ESPmDNS.h>
|
||||
|
||||
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);
|
||||
//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);
|
||||
|
||||
response[0] = '\0';
|
||||
error[0] = '\0';
|
||||
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> wifiClient;
|
||||
|
||||
if (urlProtocol == "https") {
|
||||
bool https = protocol == "https";
|
||||
if (https) {
|
||||
auto secureWifiClient = std::make_unique<WiFiClientSecure>();
|
||||
secureWifiClient->setInsecure();
|
||||
wifiClient = std::move(secureWifiClient);
|
||||
} else {
|
||||
wifiClient = std::make_unique<WiFiClient>();
|
||||
}
|
||||
|
||||
|
||||
if (!httpClient.begin(*wifiClient, url)) {
|
||||
snprintf_P(error, errorSize, "httpClient.begin(%s) failed", url);
|
||||
return false;
|
||||
}
|
||||
prepareRequest(timeout, httpHeader, httpValue);
|
||||
|
||||
return httpRequest(phase, *wifiClient, ipaddr.toString(), uri, https, authType, username, password, httpHeader, httpValue, timeout, jsonPath);
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
prepareRequest(timeout, httpHeader, httpValue);
|
||||
httpClient.addHeader("Authorization", authorization);
|
||||
httpCode = httpClient.GET();
|
||||
}
|
||||
}
|
||||
bool result = tryGetFloatValueForPhase(phase, httpCode, jsonPath);
|
||||
httpClient.end();
|
||||
return result;
|
||||
}
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
String HttpPowerMeterClass::getcNonce(const int len) {
|
||||
static const char alphanum[] = "0123456789"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
String s = "";
|
||||
|
||||
for (int i = 0; i < len; ++i) { s += alphanum[rand() % (sizeof(alphanum) - 1)]; }
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool HttpPowerMeterClass::tryGetFloatValueForPhase(int phase, int httpCode, const char* jsonPath)
|
||||
{
|
||||
bool success = 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());
|
||||
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<float>();
|
||||
//MessageOutput.printf("Power for Phase %i: %5.2fW\r\n", phase, power[phase]);
|
||||
success = true;
|
||||
}
|
||||
} else if (httpCode <= 0) {
|
||||
snprintf_P(error, errorSize, "Error(%s): %s", url, httpClient.errorToString(httpCode).c_str());
|
||||
snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError), PSTR("HTTP Error %s"), httpClient.errorToString(httpCode).c_str());
|
||||
} else if (httpCode != HTTP_CODE_OK) {
|
||||
snprintf_P(error, errorSize, "Bad HTTP code: %d", httpCode);
|
||||
}
|
||||
|
||||
httpClient.end();
|
||||
|
||||
if (error[0] != '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError), PSTR("Bad HTTP code: %d"), httpCode);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
float HttpPowerMeterClass::getFloatValueByJsonPath(const char* jsonString, const char* jsonPath, float& value)
|
||||
{
|
||||
FirebaseJson firebaseJson;
|
||||
firebaseJson.setJsonData(jsonString);
|
||||
|
||||
FirebaseJsonData firebaseJsonResult;
|
||||
if (!firebaseJson.get(firebaseJsonResult, jsonPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
value = firebaseJsonResult.to<float>();
|
||||
|
||||
firebaseJson.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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 sha256;
|
||||
uint8_t hash[HASH_SIZE];
|
||||
|
||||
sha256.reset();
|
||||
sha256.update(data.c_str(), data.length());
|
||||
sha256.finalize(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";
|
||||
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");
|
||||
|
||||
@ -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<CountryModeId_t>(config.Dtu.Cmt.CountryMode));
|
||||
MessageOutput.println(F(" Setting CMT target frequency... "));
|
||||
Hoymiles.getRadioCmt()->setInverterTargetFrequency(config.Dtu.Cmt.Frequency);
|
||||
}
|
||||
|
||||
@ -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<bool>();
|
||||
config.Display.Contrast = root["display"]["contrast"].as<uint8_t>();
|
||||
config.Display.Language = root["display"]["language"].as<uint8_t>();
|
||||
config.Display.DiagramDuration = root["display"]["diagramduration"].as<uint32_t>();
|
||||
config.Display.Diagram.Duration = root["display"]["diagramduration"].as<uint32_t>();
|
||||
config.Display.Diagram.Mode = root["display"]["diagrammode"].as<DiagramMode_t>();
|
||||
|
||||
for (uint8_t i = 0; i < PINMAPPING_LED_COUNT; i++) {
|
||||
config.Led_Single[i].Brightness = root["led"][i]["brightness"].as<uint8_t>();
|
||||
@ -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<DiagramMode_t>(config.Display.Diagram.Mode));
|
||||
Display.Diagram().updatePeriod();
|
||||
|
||||
WebApi.writeConfig(retMsg);
|
||||
@ -201,4 +204,4 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request)
|
||||
if (performRestart) {
|
||||
Utils::restartDtu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<CountryModeId_t>(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<uint32_t>() < Hoymiles.getRadioCmt()->getMinFrequency()
|
||||
|| root["cmt_frequency"].as<uint32_t>() > Hoymiles.getRadioCmt()->getMaxFrequency()
|
||||
|| root["cmt_frequency"].as<uint32_t>() % 250 > 0) {
|
||||
if (root["cmt_country"].as<uint8_t>() >= 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<CountryModeId_t>()].definition;
|
||||
if (root["cmt_frequency"].as<uint32_t>() < FrequencyDefinition.Freq_Min
|
||||
|| root["cmt_frequency"].as<uint32_t>() > FrequencyDefinition.Freq_Max
|
||||
|| root["cmt_frequency"].as<uint32_t>() % 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<uint8_t>();
|
||||
config.Dtu.Cmt.PaLevel = root["cmt_palevel"].as<int8_t>();
|
||||
config.Dtu.Cmt.Frequency = root["cmt_frequency"].as<uint32_t>();
|
||||
config.Dtu.Cmt.CountryMode = root["cmt_country"].as<CountryModeId_t>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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<String>().c_str(),
|
||||
root["auth_type"].as<Auth>(), root["username"].as<String>().c_str(), root["password"].as<String>().c_str(),
|
||||
root["header_key"].as<String>().c_str(), root["header_value"].as<String>().c_str(), root["timeout"].as<uint16_t>(),
|
||||
powerMeterResponse, sizeof(powerMeterResponse), errorMessage, sizeof(errorMessage))) {
|
||||
float power;
|
||||
char response[256];
|
||||
|
||||
if (HttpPowerMeter.getFloatValueByJsonPath(powerMeterResponse,
|
||||
root["json_path"].as<String>().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!");
|
||||
}
|
||||
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<String>().c_str(),
|
||||
root[F("auth_type")].as<Auth>(), root[F("username")].as<String>().c_str(), root[F("password")].as<String>().c_str(),
|
||||
root[F("header_key")].as<String>().c_str(), root[F("header_value")].as<String>().c_str(), root[F("timeout")].as<uint16_t>(),
|
||||
root[F("json_path")].as<String>().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;
|
||||
|
||||
@ -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()) {
|
||||
|
||||
@ -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<DiagramMode_t>(config.Display.Diagram.Mode));
|
||||
Display.setStartupDisplay();
|
||||
MessageOutput.println("done");
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -4,7 +4,12 @@
|
||||
<div class="page-header">
|
||||
<div class="row">
|
||||
<div class="col-sm-11">
|
||||
<h1>{{ title }}</h1>
|
||||
<h1>{{ title }}
|
||||
<span v-if="showWebSocket" :class="{
|
||||
'onlineMarker': isWebsocketConnected,
|
||||
'offlineMarker': !isWebsocketConnected,
|
||||
}"></span>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="col-sm-1" v-if="showReload">
|
||||
<button type="button" class="float-end btn btn-outline-primary"
|
||||
@ -28,6 +33,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { BIconArrowClockwise } from 'bootstrap-icons-vue';
|
||||
import PullToRefresh from 'pulltorefreshjs';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -37,7 +43,81 @@ export default defineComponent({
|
||||
title: { type: String, required: true },
|
||||
isLoading: { type: Boolean, required: false, default: false },
|
||||
isWideScreen: { type: Boolean, required: false, default: false },
|
||||
isWebsocketConnected: { type: Boolean, required: false, default: false },
|
||||
showWebSocket: { type: Boolean, required: false, default: false },
|
||||
showReload: { type: Boolean, required: false, default: false },
|
||||
},
|
||||
mounted() {
|
||||
var self = this;
|
||||
console.log("init");
|
||||
PullToRefresh.init({
|
||||
mainElement: 'main', // above which element?
|
||||
instructionsPullToRefresh: this.$t('base.Pull'),
|
||||
instructionsReleaseToRefresh: this.$t('base.Release'),
|
||||
instructionsRefreshing: this.$t('base.Refreshing'),
|
||||
onRefresh: function() {
|
||||
self.$emit('reload');
|
||||
}
|
||||
});
|
||||
},
|
||||
unmounted() {
|
||||
console.log("destroy");
|
||||
PullToRefresh.destroyAll();
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.ptr--text {
|
||||
color: var(--bs-primary-text-emphasis) !important;
|
||||
}
|
||||
.ptr--icon {
|
||||
color: var(--bs-primary-text-emphasis) !important;
|
||||
}
|
||||
|
||||
.offlineMarker:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #ff0000;
|
||||
border-color: #ff0000;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.onlineMarker:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #00bb00;
|
||||
border-color: #00bb00;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.onlineMarker:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin: -12px 0 0 -12px;
|
||||
border: 1px solid #00bb00;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 4px #00bb00, inset 0 0 4px rgb(56, 111, 169);
|
||||
transform: scale(0);
|
||||
animation: online 2.5s ease-in-out infinite;
|
||||
}
|
||||
@keyframes online {
|
||||
0% {
|
||||
transform: scale(.1);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: scale(2.5);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -35,7 +35,10 @@
|
||||
"Loading": "Lade...",
|
||||
"Reload": "Aktualisieren",
|
||||
"Cancel": "Abbrechen",
|
||||
"Save": "Speichern"
|
||||
"Save": "Speichern",
|
||||
"Refreshing": "Aktualisieren",
|
||||
"Pull": "Zum Aktualisieren nach unten ziehen",
|
||||
"Release": "Loslassen zum Aktualisieren"
|
||||
},
|
||||
"localeswitcher": {
|
||||
"Dark": "Dunkel",
|
||||
@ -53,6 +56,7 @@
|
||||
"2002": "Das Abfraginterval muss größer als 0 sein!",
|
||||
"2003": "Ungültige Sendeleistung angegeben!",
|
||||
"2004": "Die Frequenz muss zwischen {min} und {max} kHz liegen und ein vielfaches von 250kHz betragen!",
|
||||
"2005": "Ungültige Landesauswahl!",
|
||||
"3001": "Nichts gelöscht!",
|
||||
"3002": "Konfiguration zurückgesetzt. Starte jetzt neu...",
|
||||
"4001": "@:apiresponse.2001",
|
||||
@ -418,9 +422,14 @@
|
||||
"CmtPaLevel": "CMT2300A Sendeleistung:",
|
||||
"NrfPaLevelHint": "Verwendet für HM-Wechselrichter. Stellen Sie sicher, dass Ihre Stromversorgung stabil genug ist, bevor Sie die Sendeleistung erhöhen.",
|
||||
"CmtPaLevelHint": "Verwendet für HMS/HMT-Wechselrichter. Stellen Sie sicher, dass Ihre Stromversorgung stabil genug ist, bevor Sie die Sendeleistung erhöhen.",
|
||||
"CmtCountry": "Region/Land:",
|
||||
"CmtCountryHint": "Jedes Land hat unterschiedliche Frequenzzuteilungen.",
|
||||
"country_0": "Europa ({min}MHz - {max}MHz)",
|
||||
"country_1": "Nordamerika ({min}MHz - {max}MHz)",
|
||||
"country_2": "Brasilien ({min}MHz - {max}MHz)",
|
||||
"CmtFrequency": "CMT2300A Frequenz:",
|
||||
"CmtFrequencyHint": "Stelle sicher, dass du nur Frequenzen verwendet werden welche im entsprechenden Land erlaubt sind! Nach einer Frequenzänderung kann es bis zu 15min dauern bis eine Verbindung hergestellt wird.",
|
||||
"CmtFrequencyWarning": "Die ausgewählte Frequenz befindet außerhalb des in der EU zugelassenen Bereiches. Vergewissere dich, dass mit dieser Auswahl keine lokalen Regularien verletzt werden.",
|
||||
"CmtFrequencyWarning": "Die gewählte Frequenz liegt außerhalb des zulässigen Bereichs in der gewählten Region/dem Land. Vergewissere dich, dass mit dieser Auswahl keine lokalen Regularien verletzt werden.",
|
||||
"MHz": "{mhz} MHz",
|
||||
"dBm": "{dbm} dBm",
|
||||
"Min": "Minimum ({db} dBm)",
|
||||
@ -744,6 +753,10 @@
|
||||
"PowerSafeHint": "Schaltet das Display aus, wenn kein Wechselrichter Strom erzeugt",
|
||||
"Screensaver": "Bildschirmschoner aktivieren:",
|
||||
"ScreensaverHint": "Bewegt die Ausgabe bei jeder Aktualisierung um ein Einbrennen zu verhindern (v. a. für OLED-Displays nützlich)",
|
||||
"DiagramMode": "Diagramm Modus:",
|
||||
"off": "Deaktiviert",
|
||||
"small": "Klein",
|
||||
"fullscreen": "Vollbild",
|
||||
"DiagramDuration": "Diagramm Periode:",
|
||||
"DiagramDurationHint": "Die Zeitperiode welche im Diagramm dargestellt wird.",
|
||||
"Seconds": "Sekunden",
|
||||
|
||||
@ -35,7 +35,10 @@
|
||||
"Loading": "Loading...",
|
||||
"Reload": "Reload",
|
||||
"Cancel": "Cancel",
|
||||
"Save": "Save"
|
||||
"Save": "Save",
|
||||
"Refreshing": "Refreshing",
|
||||
"Pull": "Pull down to refresh",
|
||||
"Release": "Release to refresh"
|
||||
},
|
||||
"localeswitcher": {
|
||||
"Dark": "Dark",
|
||||
@ -53,6 +56,7 @@
|
||||
"2002": "Poll interval must be greater zero!",
|
||||
"2003": "Invalid power level setting!",
|
||||
"2004": "The frequency must be set between {min} and {max} kHz and must be a multiple of 250kHz!",
|
||||
"2005": "Invalid country selection!",
|
||||
"3001": "Not deleted anything!",
|
||||
"3002": "Configuration resettet. Rebooting now...",
|
||||
"4001": "@:apiresponse.2001",
|
||||
@ -420,9 +424,14 @@
|
||||
"CmtPaLevel": "CMT2300A Transmitting power:",
|
||||
"NrfPaLevelHint": "Used for HM-Inverters. Make sure your power supply is stable enough before increasing the transmit power.",
|
||||
"CmtPaLevelHint": "Used for HMS/HMT-Inverters. Make sure your power supply is stable enough before increasing the transmit power.",
|
||||
"CmtCountry": "Region/Country:",
|
||||
"CmtCountryHint": "Each country has different frequency allocations.",
|
||||
"country_0": "Europe ({min}MHz - {max}MHz)",
|
||||
"country_1": "North America ({min}MHz - {max}MHz)",
|
||||
"country_2": "Brazil ({min}MHz - {max}MHz)",
|
||||
"CmtFrequency": "CMT2300A Frequency:",
|
||||
"CmtFrequencyHint": "Make sure to only use frequencies that are allowed in the respective country! After a frequency change, it can take up to 15min until a connection is established.",
|
||||
"CmtFrequencyWarning": "The selected frequency is outside the range allowed in the EU. Make sure that this selection does not violate any local regulations.",
|
||||
"CmtFrequencyWarning": "The selected frequency is outside the allowed range in your selected region/country. Make sure that this selection does not violate any local regulations.",
|
||||
"MHz": "{mhz} MHz",
|
||||
"dBm": "{dbm} dBm",
|
||||
"Min": "Minimum ({db} dBm)",
|
||||
@ -753,6 +762,10 @@
|
||||
"PowerSafeHint": "Turn off the display if no inverter is producing.",
|
||||
"Screensaver": "Enable Screensaver:",
|
||||
"ScreensaverHint": "Move the display a little bit on each update to prevent burn-in. (Useful especially for OLED displays)",
|
||||
"DiagramMode": "Diagram mode:",
|
||||
"off": "Off",
|
||||
"small": "Small",
|
||||
"fullscreen": "Fullscreen",
|
||||
"DiagramDuration": "Diagram duration:",
|
||||
"DiagramDurationHint": "The time period which is shown in the diagram.",
|
||||
"Seconds": "Seconds",
|
||||
|
||||
@ -35,7 +35,10 @@
|
||||
"Loading": "Chargement...",
|
||||
"Reload": "Reload",
|
||||
"Cancel": "Annuler",
|
||||
"Save": "Sauvegarder"
|
||||
"Save": "Sauvegarder",
|
||||
"Refreshing": "Refreshing",
|
||||
"Pull": "Pull down to refresh",
|
||||
"Release": "Release to refresh"
|
||||
},
|
||||
"localeswitcher": {
|
||||
"Dark": "Sombre",
|
||||
@ -53,6 +56,7 @@
|
||||
"2002": "L'intervalle de sondage doit être supérieur à zéro !",
|
||||
"2003": "Réglage du niveau de puissance invalide !",
|
||||
"2004": "The frequency must be set between {min} and {max} kHz and must be a multiple of 250kHz!",
|
||||
"2005": "Invalid country selection !",
|
||||
"3001": "Rien n'a été supprimé !",
|
||||
"3002": "Configuration réinitialisée. Redémarrage maintenant...",
|
||||
"4001": "@:apiresponse.2001",
|
||||
@ -418,9 +422,14 @@
|
||||
"CmtPaLevel": "CMT2300A Niveau de puissance d'émission",
|
||||
"NrfPaLevelHint": "Used for HM-Inverters. Assurez-vous que votre alimentation est suffisamment stable avant d'augmenter la puissance d'émission.",
|
||||
"CmtPaLevelHint": "Used for HMS/HMT-Inverters. Assurez-vous que votre alimentation est suffisamment stable avant d'augmenter la puissance d'émission.",
|
||||
"CmtCountry": "Region/Country:",
|
||||
"CmtCountryHint": "Each country has different frequency allocations.",
|
||||
"country_0": "Europe ({min}MHz - {max}MHz)",
|
||||
"country_1": "North America ({min}MHz - {max}MHz)",
|
||||
"country_2": "Brazil ({min}MHz - {max}MHz)",
|
||||
"CmtFrequency": "CMT2300A Frequency:",
|
||||
"CmtFrequencyHint": "Make sure to only use frequencies that are allowed in the respective country! After a frequency change, it can take up to 15min until a connection is established.",
|
||||
"CmtFrequencyWarning": "The selected frequency is outside the range allowed in the EU. Make sure that this selection does not violate any local regulations.",
|
||||
"CmtFrequencyWarning": "The selected frequency is outside the allowed range in your selected region/country. Make sure that this selection does not violate any local regulations.",
|
||||
"MHz": "{mhz} MHz",
|
||||
"dBm": "{dbm} dBm",
|
||||
"Min": "Minimum ({db} dBm)",
|
||||
@ -711,6 +720,10 @@
|
||||
"PowerSafeHint": "Eteindre l'écran si aucun onduleur n'est en production.",
|
||||
"Screensaver": "Activer l'écran de veille",
|
||||
"ScreensaverHint": "Déplacez un peu l'écran à chaque mise à jour pour éviter le phénomène de brûlure. (Utile surtout pour les écrans OLED)",
|
||||
"DiagramMode": "Diagram mode:",
|
||||
"off": "Off",
|
||||
"small": "Small",
|
||||
"fullscreen": "Fullscreen",
|
||||
"DiagramDuration": "Diagram duration:",
|
||||
"DiagramDurationHint": "The time period which is shown in the diagram.",
|
||||
"Seconds": "Seconds",
|
||||
|
||||
@ -7,6 +7,7 @@ export interface Display {
|
||||
contrast: number;
|
||||
language: number;
|
||||
diagramduration: number;
|
||||
diagrammode: number;
|
||||
}
|
||||
|
||||
export interface Led {
|
||||
@ -17,4 +18,4 @@ export interface DeviceConfig {
|
||||
curPin: Device;
|
||||
display: Display;
|
||||
led: Array<Led>;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,11 @@
|
||||
export interface CountryDef {
|
||||
freq_default: number;
|
||||
freq_min: number;
|
||||
freq_max: number;
|
||||
freq_legal_min: number;
|
||||
freq_legal_max: number;
|
||||
}
|
||||
|
||||
export interface DtuConfig {
|
||||
serial: number;
|
||||
pollinterval: number;
|
||||
@ -7,4 +15,7 @@ export interface DtuConfig {
|
||||
cmt_enabled: boolean;
|
||||
cmt_palevel: number;
|
||||
cmt_frequency: number;
|
||||
}
|
||||
cmt_country: number;
|
||||
country_def: Array<CountryDef>;
|
||||
cmt_chan_width: number;
|
||||
}
|
||||
|
||||
@ -1,17 +1,8 @@
|
||||
export const timestampToString = (value: number, includeDays = false): string => {
|
||||
const days = Math.floor(value / (24 * 60 * 60));
|
||||
const secAfterDays = value - days * (24 * 60 * 60);
|
||||
const hours = Math.floor(secAfterDays / (60 * 60));
|
||||
const secAfterHours = secAfterDays - hours * (60 * 60);
|
||||
const minutes = Math.floor(secAfterHours / 60);
|
||||
const seconds = secAfterHours - minutes * 60;
|
||||
export const timestampToString = (timestampSeconds: number, includeDays = false): string => {
|
||||
const timeString = new Date(timestampSeconds * 1000).toLocaleTimeString([], { timeZone: "UTC" });
|
||||
if (!includeDays) return timeString;
|
||||
|
||||
const dHours = hours > 9 ? hours : "0" + hours;
|
||||
const dMins = minutes > 9 ? minutes : "0" + minutes;
|
||||
const dSecs = seconds > 9 ? seconds : "0" + seconds;
|
||||
|
||||
if (includeDays) {
|
||||
return days + " days " + dHours + ":" + dMins + ":" + dSecs;
|
||||
}
|
||||
return dHours + ":" + dMins + ":" + dSecs;
|
||||
const secondsPerDay = 60 * 60 * 24;
|
||||
const days = Math.floor(timestampSeconds / secondsPerDay);
|
||||
return new Intl.RelativeTimeFormat().format(-days, "day") + " " + timeString;
|
||||
}
|
||||
@ -39,7 +39,7 @@
|
||||
<div class="row mb-3">
|
||||
<div class="col-sm-2"></div>
|
||||
<div class="col-sm-10">
|
||||
<div class="btn-group" v-for="(doc, index) in pinMappingList.find(i => i.name === deviceConfigList.curPin.name)?.links" :key="index">
|
||||
<div class="btn-group mb-2 me-2" v-for="(doc, index) in pinMappingList.find(i => i.name === deviceConfigList.curPin.name)?.links" :key="index">
|
||||
<a :href="doc.url" class="btn btn-primary" target="_blank">{{ doc.name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
@ -67,6 +67,19 @@
|
||||
v-model="deviceConfigList.display.screensaver" type="checkbox"
|
||||
:tooltip="$t('deviceadmin.ScreensaverHint')" />
|
||||
|
||||
<div class="row mb-3">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
{{ $t('deviceadmin.DiagramMode') }}
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<select class="form-select" v-model="deviceConfigList.display.diagrammode">
|
||||
<option v-for="mode in diagramModeList" :key="mode.key" :value="mode.key">
|
||||
{{ $t(`deviceadmin.` + mode.value) }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<InputElement :label="$t('deviceadmin.DiagramDuration')"
|
||||
v-model="deviceConfigList.display.diagramduration" type="number"
|
||||
min=600 max=86400
|
||||
@ -183,6 +196,11 @@ export default defineComponent({
|
||||
{ key: 1, value: "de" },
|
||||
{ key: 2, value: "fr" },
|
||||
],
|
||||
diagramModeList: [
|
||||
{ key: 0, value: "off" },
|
||||
{ key: 1, value: "small" },
|
||||
{ key: 2, value: "fullscreen" },
|
||||
]
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@ -274,4 +292,4 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@ -51,6 +51,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3" v-if="dtuConfigList.cmt_enabled">
|
||||
<label for="inputCmtCountry" class="col-sm-2 col-form-label">
|
||||
{{ $t('dtuadmin.CmtCountry') }}
|
||||
<BIconInfoCircle v-tooltip :title="$t('dtuadmin.CmtCountryHint')" />
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<select id="inputCmtCountry" class="form-select" v-model="dtuConfigList.cmt_country">
|
||||
<option v-for="(country, index) in dtuConfigList.country_def" :key="index" :value="index">
|
||||
{{ $t(`dtuadmin.country_` + index, {min: country.freq_min / 1e6, max: country.freq_max / 1e6}) }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3" v-if="dtuConfigList.cmt_enabled">
|
||||
<label for="cmtFrequency" class="col-sm-2 col-form-label">
|
||||
{{ $t('dtuadmin.CmtFrequency') }}
|
||||
@ -60,12 +74,12 @@
|
||||
<div class="input-group mb-3">
|
||||
<input type="range" class="form-control form-range"
|
||||
v-model="dtuConfigList.cmt_frequency"
|
||||
min="860250" max="923500" step="250"
|
||||
:min="cmtMinFrequency" :max="cmtMaxFrequency" :step="dtuConfigList.cmt_chan_width"
|
||||
id="cmtFrequency" aria-describedby="basic-addon2"
|
||||
style="height: unset;" />
|
||||
<span class="input-group-text" id="basic-addon2">{{ cmtFrequencyText }}</span>
|
||||
</div>
|
||||
<div class="alert alert-danger" role="alert" v-html="$t('dtuadmin.CmtFrequencyWarning')" v-if="cmtIsOutOfEu"></div>
|
||||
<div class="alert alert-danger" role="alert" v-html="$t('dtuadmin.CmtFrequencyWarning')" v-if="cmtIsOutOfLegalRange"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -115,13 +129,30 @@ export default defineComponent({
|
||||
},
|
||||
computed: {
|
||||
cmtFrequencyText() {
|
||||
return this.$t("dtuadmin.MHz", { mhz: this.$n(this.dtuConfigList.cmt_frequency / 1000, "decimalTwoDigits") });
|
||||
return this.$t("dtuadmin.MHz", { mhz: this.$n(this.dtuConfigList.cmt_frequency / 1000000, "decimalTwoDigits") });
|
||||
},
|
||||
cmtPaLevelText() {
|
||||
return this.$t("dtuadmin.dBm", { dbm: this.$n(this.dtuConfigList.cmt_palevel * 1) });
|
||||
},
|
||||
cmtIsOutOfEu() {
|
||||
return this.dtuConfigList.cmt_frequency < 863000 || this.dtuConfigList.cmt_frequency > 870000;
|
||||
cmtMinFrequency() {
|
||||
return this.dtuConfigList.country_def[this.dtuConfigList.cmt_country].freq_min;
|
||||
},
|
||||
cmtMaxFrequency() {
|
||||
return this.dtuConfigList.country_def[this.dtuConfigList.cmt_country].freq_max;
|
||||
},
|
||||
cmtIsOutOfLegalRange() {
|
||||
return this.dtuConfigList.cmt_frequency < this.dtuConfigList.country_def[this.dtuConfigList.cmt_country].freq_legal_min
|
||||
|| this.dtuConfigList.cmt_frequency > this.dtuConfigList.country_def[this.dtuConfigList.cmt_country].freq_legal_max;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'dtuConfigList.cmt_country'(newValue, oldValue) {
|
||||
// Don't do anything on initial load (then oldValue equals undefined)
|
||||
if (oldValue != undefined) {
|
||||
this.$nextTick(() => {
|
||||
this.dtuConfigList.cmt_frequency = this.dtuConfigList.country_def[newValue].freq_default;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -158,4 +189,4 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<BasePage :title="$t('home.LiveData')" :isLoading="dataLoading" :isWideScreen="true">
|
||||
<BasePage :title="$t('home.LiveData')" :isLoading="dataLoading" :isWideScreen="true" :showWebSocket="true" :isWebsocketConnected="isWebsocketConnected" @reload="reloadData">
|
||||
<HintView :hints="liveData.hints" />
|
||||
<InverterTotalInfo :totalData="liveData.total" :totalVeData="liveData.vedirect" :totalBattData="liveData.battery" :powerMeterData="liveData.power_meter" :huaweiData="liveData.huawei"/><br />
|
||||
<div class="row gy-3">
|
||||
@ -457,6 +457,8 @@ export default defineComponent({
|
||||
alertTypePower: "info",
|
||||
showAlertPower: false,
|
||||
successCommandPower: "",
|
||||
|
||||
isWebsocketConnected: false,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
@ -484,17 +486,23 @@ export default defineComponent({
|
||||
this.closeSocket();
|
||||
},
|
||||
updated() {
|
||||
console.log("Updated");
|
||||
// Select first tab
|
||||
if (this.isFirstFetchAfterConnect) {
|
||||
this.isFirstFetchAfterConnect = false;
|
||||
console.log("isFirstFetchAfterConnect");
|
||||
|
||||
const firstTabEl = document.querySelector(
|
||||
"#v-pills-tab:first-child button"
|
||||
);
|
||||
if (firstTabEl != null) {
|
||||
const firstTab = new bootstrap.Tab(firstTabEl);
|
||||
firstTab.show();
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
console.log("nextTick");
|
||||
const firstTabEl = document.querySelector(
|
||||
"#v-pills-tab:first-child button"
|
||||
);
|
||||
if (firstTabEl != null) {
|
||||
this.isFirstFetchAfterConnect = false;
|
||||
console.log("Show");
|
||||
const firstTab = new bootstrap.Tab(firstTabEl);
|
||||
firstTab.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -517,15 +525,27 @@ export default defineComponent({
|
||||
},
|
||||
methods: {
|
||||
isLoggedIn,
|
||||
getInitialData() {
|
||||
this.dataLoading = true;
|
||||
getInitialData(triggerLoading : boolean = true) {
|
||||
if (triggerLoading) {
|
||||
this.dataLoading = true;
|
||||
}
|
||||
fetch("/api/livedata/status", { headers: authHeader() })
|
||||
.then((response) => handleResponse(response, this.$emitter, this.$router))
|
||||
.then((data) => {
|
||||
this.liveData = data;
|
||||
this.dataLoading = false;
|
||||
if (triggerLoading) {
|
||||
this.dataLoading = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
reloadData() {
|
||||
this.closeSocket();
|
||||
|
||||
setTimeout(() => {
|
||||
this.getInitialData(false);
|
||||
this.initSocket();
|
||||
}, 1000);
|
||||
},
|
||||
initSocket() {
|
||||
console.log("Starting connection to WebSocket Server");
|
||||
|
||||
@ -549,11 +569,19 @@ export default defineComponent({
|
||||
}
|
||||
};
|
||||
|
||||
var self = this;
|
||||
|
||||
this.socket.onopen = function (event) {
|
||||
console.log(event);
|
||||
console.log("Successfully connected to the echo websocket server...");
|
||||
self.isWebsocketConnected = true;
|
||||
};
|
||||
|
||||
this.socket.onclose = function() {
|
||||
console.log("Connection to websocket closed...")
|
||||
self.isWebsocketConnected = false;
|
||||
}
|
||||
|
||||
// Listen to window events , When the window closes , Take the initiative to disconnect websocket Connect
|
||||
window.onbeforeunload = () => {
|
||||
this.closeSocket();
|
||||
@ -772,4 +800,4 @@ export default defineComponent({
|
||||
border-radius: var(--bs-border-radius);
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
221
webapp/yarn.lock
221
webapp/yarn.lock
@ -211,20 +211,20 @@
|
||||
source-map-js "^1.0.1"
|
||||
yaml-eslint-parser "^1.2.2"
|
||||
|
||||
"@intlify/core-base@9.8.0":
|
||||
version "9.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.8.0.tgz#969ca59f55084e23e968ec0bfe71678774e568ec"
|
||||
integrity sha512-UxaSZVZ1DwqC/CltUZrWZNaWNhfmKtfyV4BJSt/Zt4Or/fZs1iFj0B+OekYk1+MRHfIOe3+x00uXGQI4PbO/9g==
|
||||
"@intlify/core-base@9.9.0":
|
||||
version "9.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.9.0.tgz#edc55a5e3dbbf8dbbbf656529ed27832c4c4f522"
|
||||
integrity sha512-C7UXPymDIOlMGSNjAhNLtKgzITc/8BjINK5gNKXg8GiWCTwL6n3MWr55czksxn8RM5wTMz0qcLOFT+adtaVQaA==
|
||||
dependencies:
|
||||
"@intlify/message-compiler" "9.8.0"
|
||||
"@intlify/shared" "9.8.0"
|
||||
"@intlify/message-compiler" "9.9.0"
|
||||
"@intlify/shared" "9.9.0"
|
||||
|
||||
"@intlify/message-compiler@9.8.0":
|
||||
version "9.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.8.0.tgz#587d69b302f9b8130a4a949b0ab4add519761787"
|
||||
integrity sha512-McnYWhcoYmDJvssVu6QGR0shqlkJuL1HHdi5lK7fNqvQqRYaQ4lSLjYmZxwc8tRNMdIe9/KUKfyPxU9M6yCtNQ==
|
||||
"@intlify/message-compiler@9.9.0":
|
||||
version "9.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.9.0.tgz#7952759329e7af0388afbce7a984820bbeff82eb"
|
||||
integrity sha512-yDU/jdUm9KuhEzYfS+wuyja209yXgdl1XFhMlKtXEgSFTxz4COZQCRXXbbH8JrAjMsaJ7bdoPSLsKlY6mXG2iA==
|
||||
dependencies:
|
||||
"@intlify/shared" "9.8.0"
|
||||
"@intlify/shared" "9.9.0"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
"@intlify/message-compiler@^9.4.0":
|
||||
@ -240,10 +240,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.4.0.tgz#4a78d462fc82433db900981e12eb5b1aae3d6085"
|
||||
integrity sha512-AFqymip2kToqA0B6KZPg5jSrdcVHoli9t/VhGKE2iiMq9utFuMoGdDC/JOCIZgwxo6aXAk86QyU2XtzEoMuZ6A==
|
||||
|
||||
"@intlify/shared@9.8.0":
|
||||
version "9.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.8.0.tgz#62adf8f6ef67c8eba6cf8d521e248f3503f237d3"
|
||||
integrity sha512-TmgR0RCLjzrSo+W3wT0ALf9851iFMlVI9EYNGeWvZFUQTAJx0bvfsMlPdgVtV1tDNRiAfhkFsMKu6jtUY1ZLKQ==
|
||||
"@intlify/shared@9.9.0":
|
||||
version "9.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.9.0.tgz#56907633c0f7b2d50f53269d31e88e7b24d39187"
|
||||
integrity sha512-1ECUyAHRrzOJbOizyGufYP2yukqGrWXtkmTu4PcswVnWbkcjzk3YQGmJ0bLkM7JZ0ZYAaohLGdYvBYnTOGYJ9g==
|
||||
|
||||
"@intlify/unplugin-vue-i18n@^2.0.0":
|
||||
version "2.0.0"
|
||||
@ -435,13 +435,18 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb"
|
||||
integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==
|
||||
|
||||
"@types/node@^20.10.6":
|
||||
version "20.10.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.6.tgz#a3ec84c22965802bf763da55b2394424f22bfbb5"
|
||||
integrity sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==
|
||||
"@types/node@^20.11.0":
|
||||
version "20.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.0.tgz#8e0b99e70c0c1ade1a86c4a282f7b7ef87c9552f"
|
||||
integrity sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==
|
||||
dependencies:
|
||||
undici-types "~5.26.4"
|
||||
|
||||
"@types/pulltorefreshjs@^0.1.7":
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/pulltorefreshjs/-/pulltorefreshjs-0.1.7.tgz#1948638b0c7071282e47bd236d2ccb88bdf66753"
|
||||
integrity sha512-Y0g/yfuycIvpvUmP97n5NE2+HDAOwfREGVERjhMWw2Y0ODh5wvbflcQ5gXPZ+ihgoq+BQZjA1DL8apw2wAsJXA==
|
||||
|
||||
"@types/semver@^7.5.0":
|
||||
version "7.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.1.tgz#0480eeb7221eb9bc398ad7432c9d7e14b1a5a367"
|
||||
@ -547,10 +552,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
|
||||
integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
|
||||
|
||||
"@vitejs/plugin-vue@^5.0.2":
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.0.2.tgz#8428ec3f446b9c2f7a7ec950f34e3d6f3c665444"
|
||||
integrity sha512-kEjJHrLb5ePBvjD0SPZwJlw1QTRcjjCA9sB5VyfonoXVBxTS7TMnqL6EkLt1Eu61RDeiuZ/WN9Hf6PxXhPI2uA==
|
||||
"@vitejs/plugin-vue@^5.0.3":
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.0.3.tgz#164b36653910d27c130cf6c945b4bd9bde5bcbee"
|
||||
integrity sha512-b8S5dVS40rgHdDrw+DQi/xOM9ed+kSRZzfm1T74bMmBDCd8XO87NKlFYInzCtwvtWwXZvo1QxE2OSspTATWrbA==
|
||||
|
||||
"@volar/language-core@1.11.1", "@volar/language-core@~1.11.1":
|
||||
version "1.11.1"
|
||||
@ -594,13 +599,13 @@
|
||||
estree-walker "^2.0.2"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
"@vue/compiler-core@3.4.5":
|
||||
version "3.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.5.tgz#9565aebaadef8649eb7c8e150a5f4f4e2542667d"
|
||||
integrity sha512-Daka7P1z2AgKjzuueWXhwzIsKu0NkLB6vGbNVEV2iJ8GJTrzraZo/Sk4GWCMRtd/qVi3zwnk+Owbd/xSZbwHtQ==
|
||||
"@vue/compiler-core@3.4.13":
|
||||
version "3.4.13"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.13.tgz#49f499034c25b0832845028ea3cd701fe5a17367"
|
||||
integrity sha512-zGUdmB3j3Irn9z51GXLJ5s0EAHxmsm5/eXl0y6MBaajMeOAaiT4+zaDoxui4Ets98dwIRr8BBaqXXHtHSfm+KA==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.23.6"
|
||||
"@vue/shared" "3.4.5"
|
||||
"@vue/shared" "3.4.13"
|
||||
entities "^4.5.0"
|
||||
estree-walker "^2.0.2"
|
||||
source-map-js "^1.0.2"
|
||||
@ -613,13 +618,13 @@
|
||||
"@vue/compiler-core" "3.2.47"
|
||||
"@vue/shared" "3.2.47"
|
||||
|
||||
"@vue/compiler-dom@3.4.5":
|
||||
version "3.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.5.tgz#c53c9d7715b777b1d6d2adcbc491bfd4f9510edd"
|
||||
integrity sha512-J8YlxknJVd90SXFJ4HwGANSAXsx5I0lK30sO/zvYV7s5gXf7gZR7r/1BmZ2ju7RGH1lnc6bpBc6nL61yW+PsAQ==
|
||||
"@vue/compiler-dom@3.4.13":
|
||||
version "3.4.13"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.13.tgz#66a80a6ee412a3d32b7175a146b75d9ec3d1c50c"
|
||||
integrity sha512-XSNbpr5Rs3kCfVAmBqMu/HDwOS+RL6y28ZZjDlnDUuf146pRWt2sQkwhsOYc9uu2lxjjJy2NcyOkK7MBLVEc7w==
|
||||
dependencies:
|
||||
"@vue/compiler-core" "3.4.5"
|
||||
"@vue/shared" "3.4.5"
|
||||
"@vue/compiler-core" "3.4.13"
|
||||
"@vue/shared" "3.4.13"
|
||||
|
||||
"@vue/compiler-dom@^3.3.0":
|
||||
version "3.3.2"
|
||||
@ -629,16 +634,16 @@
|
||||
"@vue/compiler-core" "3.3.2"
|
||||
"@vue/shared" "3.3.2"
|
||||
|
||||
"@vue/compiler-sfc@3.4.5":
|
||||
version "3.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.5.tgz#f93f986dfc5c7f72b9a5e00b48be75d9116cc948"
|
||||
integrity sha512-jauvkDuSSUbP0ebhfNqljhShA90YEfX/0wZ+w40oZF43IjGyWYjqYaJbvMJwGOd+9+vODW6eSvnk28f0SGV7OQ==
|
||||
"@vue/compiler-sfc@3.4.13":
|
||||
version "3.4.13"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.13.tgz#0f5f6db0e64f522c09995585453ae5f13ba54c60"
|
||||
integrity sha512-SkpmQN8xIFBd5onT413DFSDdjxULJf6jmJg/t3w/DZ9I8ZzyNlLIBLO0qFLewVHyHCiAgpPZlWqSRZXYrawk3Q==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.23.6"
|
||||
"@vue/compiler-core" "3.4.5"
|
||||
"@vue/compiler-dom" "3.4.5"
|
||||
"@vue/compiler-ssr" "3.4.5"
|
||||
"@vue/shared" "3.4.5"
|
||||
"@vue/compiler-core" "3.4.13"
|
||||
"@vue/compiler-dom" "3.4.13"
|
||||
"@vue/compiler-ssr" "3.4.13"
|
||||
"@vue/shared" "3.4.13"
|
||||
estree-walker "^2.0.2"
|
||||
magic-string "^0.30.5"
|
||||
postcss "^8.4.32"
|
||||
@ -668,13 +673,13 @@
|
||||
"@vue/compiler-dom" "3.2.47"
|
||||
"@vue/shared" "3.2.47"
|
||||
|
||||
"@vue/compiler-ssr@3.4.5":
|
||||
version "3.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.5.tgz#d412a4c9b10d69172a5ce0ec78de98dad441a58d"
|
||||
integrity sha512-DDdEcDzj2lWTMfUMMtEpLDhURai9LhM0zSZ219jCt7b2Vyl0/jy3keFgCPMitG0V1S1YG4Cmws3lWHWdxHQOpg==
|
||||
"@vue/compiler-ssr@3.4.13":
|
||||
version "3.4.13"
|
||||
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.13.tgz#90fa9a4116f7974d7a4e43a8a67f3fc162e8720f"
|
||||
integrity sha512-rwnw9SVBgD6eGKh8UucnwztieQo/R3RQrEGpE0b0cxb2xxvJeLs/fe7DoYlhEfaSyzM/qD5odkK87hl3G3oW+A==
|
||||
dependencies:
|
||||
"@vue/compiler-dom" "3.4.5"
|
||||
"@vue/shared" "3.4.5"
|
||||
"@vue/compiler-dom" "3.4.13"
|
||||
"@vue/shared" "3.4.13"
|
||||
|
||||
"@vue/devtools-api@^6.5.0":
|
||||
version "6.5.0"
|
||||
@ -716,37 +721,37 @@
|
||||
estree-walker "^2.0.2"
|
||||
magic-string "^0.25.7"
|
||||
|
||||
"@vue/reactivity@3.4.5":
|
||||
version "3.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.5.tgz#68bc91cd356eed95dc5e9e0570e3f7becaee578b"
|
||||
integrity sha512-BcWkKvjdvqJwb7BhhFkXPLDCecX4d4a6GATvCduJQDLv21PkPowAE5GKuIE5p6RC07/Lp9FMkkq4AYCTVF5KlQ==
|
||||
"@vue/reactivity@3.4.13":
|
||||
version "3.4.13"
|
||||
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.13.tgz#7eeeb9d598512f66e06a6438fd53464014b5ae59"
|
||||
integrity sha512-/ZdUOrGKkGVONzVJkfDqNcn2fLMvaa5VlYx2KwTbnRbX06YZ4GJE0PVTmWzIxtBYdpSTLLXgw3pDggO+96KXzg==
|
||||
dependencies:
|
||||
"@vue/shared" "3.4.5"
|
||||
"@vue/shared" "3.4.13"
|
||||
|
||||
"@vue/runtime-core@3.4.5":
|
||||
version "3.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.5.tgz#2bf253a6f6b0430af1aacf0fdfd8f5782feefce9"
|
||||
integrity sha512-wh9ELIOQKeWT9SaUPdLrsxRkZv14jp+SJm9aiQGWio+/MWNM3Lib0wE6CoKEqQ9+SCYyGjDBhTOTtO47kCgbkg==
|
||||
"@vue/runtime-core@3.4.13":
|
||||
version "3.4.13"
|
||||
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.13.tgz#776cad7c1d56ec5e92a48e040c8483b89f779542"
|
||||
integrity sha512-Ov4d4At7z3goxqzSqQxdfVYEcN5HY4dM1uDYL6Hu/Es9Za9BEN602zyjWhhi2+BEki5F9NizRSvn02k/tqNWlg==
|
||||
dependencies:
|
||||
"@vue/reactivity" "3.4.5"
|
||||
"@vue/shared" "3.4.5"
|
||||
"@vue/reactivity" "3.4.13"
|
||||
"@vue/shared" "3.4.13"
|
||||
|
||||
"@vue/runtime-dom@3.4.5":
|
||||
version "3.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.5.tgz#b43736d66c32f6038778024587592cb9d68495de"
|
||||
integrity sha512-n5ewvOjyG3IEpqGBahdPXODFSpVlSz3H4LF76Sx0XAqpIOqyJ5bIb2PrdYuH2ogBMAQPh+o5tnoH4nJpBr8U0Q==
|
||||
"@vue/runtime-dom@3.4.13":
|
||||
version "3.4.13"
|
||||
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.13.tgz#74aff1494bee49c037b9c5355d8998c793ac0977"
|
||||
integrity sha512-ynde9p16eEV3u1VCxUre2e0nKzD0l3NzH0r599+bXeLT1Yhac8Atcot3iL9XNqwolxYCI89KBII+2MSVzfrz6w==
|
||||
dependencies:
|
||||
"@vue/runtime-core" "3.4.5"
|
||||
"@vue/shared" "3.4.5"
|
||||
"@vue/runtime-core" "3.4.13"
|
||||
"@vue/shared" "3.4.13"
|
||||
csstype "^3.1.3"
|
||||
|
||||
"@vue/server-renderer@3.4.5":
|
||||
version "3.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.5.tgz#4bfa7aa763217d8b2d4767d2c8d968a9d40352c1"
|
||||
integrity sha512-jOFc/VE87yvifQpNju12VcqimH8pBLxdcT+t3xMeiED1K6DfH9SORyhFEoZlW5TG2Vwfn3Ul5KE+1aC99xnSBg==
|
||||
"@vue/server-renderer@3.4.13":
|
||||
version "3.4.13"
|
||||
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.13.tgz#b8c9cfb2147c0a01feba7f136d3a432848dafcab"
|
||||
integrity sha512-hkw+UQyDZZtSn1q30nObMfc8beVEQv2pG08nghigxGw+iOWodR+tWSuJak0mzWAHlP/xt/qLc//dG6igfgvGEA==
|
||||
dependencies:
|
||||
"@vue/compiler-ssr" "3.4.5"
|
||||
"@vue/shared" "3.4.5"
|
||||
"@vue/compiler-ssr" "3.4.13"
|
||||
"@vue/shared" "3.4.13"
|
||||
|
||||
"@vue/shared@3.2.47":
|
||||
version "3.2.47"
|
||||
@ -758,10 +763,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.3.2.tgz#774cd9b4635ce801b70a3fc3713779a5ef5d77c3"
|
||||
integrity sha512-0rFu3h8JbclbnvvKrs7Fe5FNGV9/5X2rPD7KmOzhLSUAiQH5//Hq437Gv0fR5Mev3u/nbtvmLl8XgwCU20/ZfQ==
|
||||
|
||||
"@vue/shared@3.4.5":
|
||||
version "3.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.5.tgz#c8b4eb6399a7fc986565ea736d938b3a1579256d"
|
||||
integrity sha512-6XptuzlMvN4l4cDnDw36pdGEV+9njYkQ1ZE0Q6iZLwrKefKaOJyiFmcP3/KBDHbt72cJZGtllAc1GaHe6XGAyg==
|
||||
"@vue/shared@3.4.13":
|
||||
version "3.4.13"
|
||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.13.tgz#a1eefce5ddffe207d53eafbc07f4ebbea0a4768a"
|
||||
integrity sha512-56crFKLPpzk85WXX1L1c0QzPOuoapWlPVys8eMG8kkRmqdMjWUqK8KpFdE2d7BQA4CEbXwyyHPq6MpFr8H9rcg==
|
||||
|
||||
"@vue/tsconfig@^0.5.1":
|
||||
version "0.5.1"
|
||||
@ -1141,17 +1146,17 @@ escodegen@^2.0.0:
|
||||
optionalDependencies:
|
||||
source-map "~0.6.1"
|
||||
|
||||
eslint-plugin-vue@^9.19.2:
|
||||
version "9.19.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.19.2.tgz#7ab83a001a1ac8bccae013c5b9cb5d2c644fb376"
|
||||
integrity sha512-CPDqTOG2K4Ni2o4J5wixkLVNwgctKXFu6oBpVJlpNq7f38lh9I80pRTouZSJ2MAebPJlINU/KTFSXyQfBUlymA==
|
||||
eslint-plugin-vue@^9.20.1:
|
||||
version "9.20.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.20.1.tgz#7ed78846898574b2cd26939f28b0b87798a7b528"
|
||||
integrity sha512-GyCs8K3lkEvoyC1VV97GJhP1SvqsKCiWGHnbn0gVUYiUhaH2+nB+Dv1uekv1THFMPbBfYxukrzQdltw950k+LQ==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.4.0"
|
||||
natural-compare "^1.4.0"
|
||||
nth-check "^2.1.1"
|
||||
postcss-selector-parser "^6.0.13"
|
||||
semver "^7.5.4"
|
||||
vue-eslint-parser "^9.3.1"
|
||||
vue-eslint-parser "^9.4.0"
|
||||
xml-name-validator "^4.0.0"
|
||||
|
||||
eslint-scope@^7.1.1:
|
||||
@ -2142,6 +2147,11 @@ prelude-ls@~1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
|
||||
integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==
|
||||
|
||||
pulltorefreshjs@^0.1.22:
|
||||
version "0.1.22"
|
||||
resolved "https://registry.yarnpkg.com/pulltorefreshjs/-/pulltorefreshjs-0.1.22.tgz#ddb5e3feee0b2a49fd46e1b18e84fffef2c47ac0"
|
||||
integrity sha512-haxNVEHnS4NCQA7NeG7TSV69z4uqy/N7nfPRuc4dPWe8H6ygUrMjdNeohE+6v0lVVX/ukSjbLYwPUGUYtFKfvQ==
|
||||
|
||||
punycode@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
@ -2551,10 +2561,10 @@ vite-plugin-css-injected-by-js@^3.3.1:
|
||||
resolved "https://registry.yarnpkg.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.3.1.tgz#26b41f108c5554ee728359bdec01c68c93a48547"
|
||||
integrity sha512-PjM/X45DR3/V1K1fTRs8HtZHEQ55kIfdrn+dzaqNBFrOYO073SeSNCxp4j7gSYhV9NffVHaEnOL4myoko0ePAg==
|
||||
|
||||
vite@^5.0.10:
|
||||
version "5.0.10"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.0.10.tgz#1e13ef5c3cf5aa4eed81f5df6d107b3c3f1f6356"
|
||||
integrity sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==
|
||||
vite@^5.0.11:
|
||||
version "5.0.11"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.0.11.tgz#31562e41e004cb68e1d51f5d2c641ab313b289e4"
|
||||
integrity sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==
|
||||
dependencies:
|
||||
esbuild "^0.19.3"
|
||||
postcss "^8.4.32"
|
||||
@ -2575,13 +2585,26 @@ vue-eslint-parser@^9.3.1:
|
||||
lodash "^4.17.21"
|
||||
semver "^7.3.6"
|
||||
|
||||
vue-i18n@^9.8.0:
|
||||
version "9.8.0"
|
||||
resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.8.0.tgz#54339daf377a31b234b027c5158e774728b6bc24"
|
||||
integrity sha512-Izho+6PYjejsTq2mzjcRdBZ5VLRQoSuuexvR8029h5CpN03FYqiqBrShMyf2I1DKkN6kw/xmujcbvC+4QybpsQ==
|
||||
vue-eslint-parser@^9.4.0:
|
||||
version "9.4.0"
|
||||
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.4.0.tgz#dfd22302e2992fe45748a76553cef7afa5bdde27"
|
||||
integrity sha512-7KsNBb6gHFA75BtneJsoK/dbZ281whUIwFYdQxA68QrCrGMXYzUMbPDHGcOQ0OocIVKrWSKWXZ4mL7tonCXoUw==
|
||||
dependencies:
|
||||
"@intlify/core-base" "9.8.0"
|
||||
"@intlify/shared" "9.8.0"
|
||||
debug "^4.3.4"
|
||||
eslint-scope "^7.1.1"
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
espree "^9.3.1"
|
||||
esquery "^1.4.0"
|
||||
lodash "^4.17.21"
|
||||
semver "^7.3.6"
|
||||
|
||||
vue-i18n@^9.9.0:
|
||||
version "9.9.0"
|
||||
resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.9.0.tgz#20d348fa7e37fc88e4c84f69781b2f1215c7769f"
|
||||
integrity sha512-xQ5SxszUAqK5n84N+uUyHH/PiQl9xZ24FOxyAaNonmOQgXeN+rD9z/6DStOpOxNFQn4Cgcquot05gZc+CdOujA==
|
||||
dependencies:
|
||||
"@intlify/core-base" "9.9.0"
|
||||
"@intlify/shared" "9.9.0"
|
||||
"@vue/devtools-api" "^6.5.0"
|
||||
|
||||
vue-router@^4.2.5:
|
||||
@ -2608,16 +2631,16 @@ vue-tsc@^1.8.27:
|
||||
"@vue/language-core" "1.8.27"
|
||||
semver "^7.5.4"
|
||||
|
||||
vue@^3.4.5:
|
||||
version "3.4.5"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.5.tgz#c08b9d903a20faaf4df7270bf2fa7487741b2294"
|
||||
integrity sha512-VH6nHFhLPjgu2oh5vEBXoNZxsGHuZNr3qf4PHClwJWw6IDqw6B3x+4J+ABdoZ0aJuT8Zi0zf3GpGlLQCrGWHrw==
|
||||
vue@^3.4.13:
|
||||
version "3.4.13"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.13.tgz#aa522baf2344d1c4c54c769f66c0151f1872f1ff"
|
||||
integrity sha512-FE3UZ0p+oUZTwz+SzlH/hDFg+XsVRFvwmx0LXjdD1pRK/cO4fu5v6ltAZji4za4IBih3dV78elUK3di8v3pWIg==
|
||||
dependencies:
|
||||
"@vue/compiler-dom" "3.4.5"
|
||||
"@vue/compiler-sfc" "3.4.5"
|
||||
"@vue/runtime-dom" "3.4.5"
|
||||
"@vue/server-renderer" "3.4.5"
|
||||
"@vue/shared" "3.4.5"
|
||||
"@vue/compiler-dom" "3.4.13"
|
||||
"@vue/compiler-sfc" "3.4.13"
|
||||
"@vue/runtime-dom" "3.4.13"
|
||||
"@vue/server-renderer" "3.4.13"
|
||||
"@vue/shared" "3.4.13"
|
||||
|
||||
webpack-sources@^3.2.3:
|
||||
version "3.2.3"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user