HLW8112 Driver for energy measurments (#1810)

* initial test driver for hlw8112

Signed-off-by: Nizam <code@maxters.net>

* restrict build to BK7231N
will stub other platforms later

Signed-off-by: Nizam <code@maxters.net>

* fix newer gcc build

Signed-off-by: Nizam <code@maxters.net>

* fix channel b stat restore
after all i still doesnt get pointer arithmetic

Signed-off-by: Nizam <code@maxters.net>

* clean up flashvar bk7231 still use emetering struct to store data

Signed-off-by: Nizam <code@maxters.net>

* fix OpenBK7231N_ALT build fail

Signed-off-by: Nizam <code@maxters.net>

* disable incomplete spi device raw access commands and ui

Signed-off-by: Nizam <code@maxters.net>

* remove mqtt hack for commands. leverage existing tasmota command framework

Signed-off-by: Nizam <code@maxters.net>

* missed flashvar restore

Signed-off-by: Nizam <code@maxters.net>

* disable hlw812 driver for upstream pr

Signed-off-by: Nizam <code@maxters.net>

---------

Signed-off-by: Nizam <code@maxters.net>
This commit is contained in:
Nizam Moidu 2025-09-30 18:34:15 +05:30 committed by GitHub
parent 8136ebe8d6
commit 4a2ae013ed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 1833 additions and 9 deletions

3
.gitignore vendored
View File

@ -37,4 +37,5 @@ configMemory.bin
# MSVC
enc_temp_folder/**
*.su
*.su
/.idea/**

View File

@ -487,6 +487,14 @@
"file": "new_pins.h",
"driver": ""
},
{
"name": "EnergyImport_kWh_div1000",
"title": "TODO",
"descr": "TODO",
"enum": "ChType_EnergyImport_kWh_div1000",
"file": "new_pins.h",
"driver": ""
},
{
"name": "Max",
"title": "TODO",

View File

@ -654,5 +654,13 @@
"enum": "IOR_Total_Options",
"file": "new_pins.h",
"driver": ""
},
{
"name": "HLW8112_SCSN",
"title": "HLW8112 SCSN Pin",
"descr": "SCSN pin for HLW8112 SPI energy measuring devices. ",
"enum": "IOR_BHLW8112_SCSN",
"file": "new_pins.h",
"driver": "HLW8112SPI"
}
]

View File

@ -103,6 +103,7 @@ OBKM_SRC += $(OBK_SRCS)driver/drv_freeze.c
OBKM_SRC += $(OBK_SRCS)driver/drv_gn6932.c
OBKM_SRC += $(OBK_SRCS)driver/drv_hd2015.c
OBKM_SRC += $(OBK_SRCS)driver/drv_hgs02.c
OBKM_SRC += $(OBK_SRCS)driver/drv_hlw8112.c
OBKM_SRC += $(OBK_SRCS)driver/drv_ht16k33.c
OBKM_SRC += $(OBK_SRCS)driver/drv_httpButtons.c
OBKM_SRC += $(OBK_SRCS)driver/drv_hue.c

1213
src/driver/drv_hlw8112.c Normal file

File diff suppressed because it is too large Load Diff

447
src/driver/drv_hlw8112.h Normal file
View File

@ -0,0 +1,447 @@
#pragma once
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#ifndef __DRV_HLW8112_H__
#define __DRV_HLW8112_H__
#define HLW8112_SPI_RAWACCESS 0 //WIP
#include "../httpserver/new_http.h"
#include "../hal/hal_flashVars.h"
#include <stdint.h>
//driver funcs
void HLW8112SPI_Init(void);
void HLW8112SPI_Stop(void);
void HLW8112_RunEverySecond(void);
void HLW8112_OnHassDiscovery(const char *topic);
void HLW8112_AppendInformationToHTTPIndexPage(http_request_t *request, int bPreState);
//for external use fill force write to flash if driver is running
void HLW8112_Save_Statistics();
#pragma region util macros
#define CHECK_AND_RETURN(value) \
if (value != 0) { \
return value; \
}
#define READ_REG(REG,SIZE) HLW8112_ReadRegister##SIZE(HLW8112_REG_##REG, (uint##SIZE##_t *) &device.EX_REGiSTERS._##REG);
#define REG_EDIT(REG,SIZE,RO,C) appendRegEdit(request, #REG , HLW8112_REG_##REG, RO, device.EX_REGiSTERS._##REG, SIZE, C)
#pragma endregion
// constants
#define HLW8112_SPI_BAUD_RATE 1000000 // 1000000 // 900000
#define DEFAULT_INTERNAL_CLK 3579545UL
#define HLW8112_INVALID_REGVALUE 1 << 23
#define HLW8112_SAVE_COUNTER 3600 // 1 * 60 * 60; // maybe once in a hour
#define DEFAULT_RES_KU 1.0f
#define DEFAULT_RES_KIA 0.2f
#define DEFAULT_RES_KIB 0.2f
#pragma region resistors
// Register addresses
#define HLW8112_REG_SYSCON 0x00 // System Control 2 bytes 0x0a04
#define HLW8112_REG_SYSCON_ADC3ON 11
#define HLW8112_REG_SYSCON_ADC2ON 10
#define HLW8112_REG_SYSCON_ADC1ON 9
#define HLW8112_REG_SYSCON_PGAIB 6
#define HLW8112_REG_SYSCON_PGAU 3
#define HLW8112_REG_SYSCON_PGAIA 0
#define HLW8112_REG_EMUCON 0x01 // Energy Measure Control 2 bytes 0x0000
#define HLW8112_REG_EMUCON_TEMP_STEP 14
#define HLW8112_REG_EMUCON_TEMP_EN 13
#define HLW8112_REG_EMUCON_COMP_OFF 12
#define HLW8112_REG_EMUCON_PMODE 10
#define HLW8112_REG_EMUCON_DC_MODE 9
#define HLW8112_REG_EMUCON_ZXD1 8
#define HLW8112_REG_EMUCON_ZXD0 7
#define HLW8112_REG_EMUCON_HPFIBOFF 6
#define HLW8112_REG_EMUCON_HPFIAOFF 5
#define HLW8112_REG_EMUCON_HPFUOFF 4
#define HLW8112_REG_EMUCON_PBRUN 1
#define HLW8112_REG_EMUCON_PARUN 0
#define HLW8112_REG_HFCONST 0x02 // Pulse Frequency 2bytes 0x1000
#define HLW8112_REG_PSTARTA 0x03 // Active Start Power A 2 bytes 0x0060
#define HLW8112_REG_PSTARTB 0x04 // Active Start Power B
#define HLW8112_REG_PAGAIN 0x05 // Power Gain Calibration A 2 bytes 0x0000
#define HLW8112_REG_PBGAIN 0x06 // Power Gain Calibration B
#define HLW8112_REG_PHASEA 0x07 // Phase Calibration A 1 bytes 0x00
#define HLW8112_REG_PHASEB 0x08 // Phase Calibration B
#define HLW8112_REG_PAOS 0x0A // Active Power Offset Calibration A 2 bytes 0x0000
#define HLW8112_REG_PBOS 0x0B // Active Power Offset Calibration B 2 bytes 0x0000
#define HLW8112_REG_RMSIAOS 0x0E // RMS Offset Compensation A 2 bytes 0x0000
#define HLW8112_REG_RMSIBOS 0x0F // RMS Offset Compensation B 2 bytes 0x0000
#define HLW8112_REG_IBGAIN 0x10 // Channel B Current Gain 2 bytes 0x0000
#define HLW8112_REG_PSGAIN 0x11 // Apparent Power Gain Calibration 2 bytes 0x0000
#define HLW8112_REG_PSOS 0x12 // Apparent Power Offset Compensation 2 bytes 0x0000
#define HLW8112_REG_EMUCON2 0x13 // Meter Control2 2 bytes 0x0001
#define HLW8112_REG_EMUCON2_SDOCMOS 12
#define HLW8112_REG_EMUCON2_EPB_CB 11
#define HLW8112_REG_EMUCON2_EPB_CA 10
#define HLW8112_REG_EMUCON2_DUPSEL 8
#define HLW8112_REG_EMUCON2_CHS_IB 7
#define HLW8112_REG_EMUCON2_PFACTOREN 6
#define HLW8112_REG_EMUCON2_WAVEEN 5
#define HLW8112_REG_EMUCON2_SAGEN 4
#define HLW8112_REG_EMUCON2_OVEREN 3
#define HLW8112_REG_EMUCON2_ZXEN 2
#define HLW8112_REG_EMUCON2_PEAKEN 1
#define HLW8112_REG_EMUCON2_VREFSEL 0
#define HLW8112_REG_DCIA 0x14 // DC Offset Correction A 2 bytes 0x0000
#define HLW8112_REG_DCIB 0x15 // DC Offset Correction B
#define HLW8112_REG_DCIC 0x16 // DC Offset Correction U
#define HLW8112_REG_SAGCYC 0x17 // Voltage Sag Period 2 bytes 0x0000
#define HLW8112_REG_SAGLVL 0x18 // Voltage Sag Threshold 2 bytes 0x0000
#define HLW8112_REG_OVLVL 0x19 // Overvoltage Threshold 2 bytes 0x0ffff
#define HLW8112_REG_OIALVL 0x1A // Overcurrent Threshold A 2 bytes 0x0ffff
#define HLW8112_REG_OIBLVL 0x1B // COvercurrent Threshold B 2 bytes 0x0ffff
#define HLW8112_REG_OPLVL 0x1C // Active Power Overload Threshold 2 bytes 0x0ffff
#define HLW8112_REG_INT 0x1D // INT1/INT2 Interrupt Setting 2 bytes 0x3210
// Meter Parameter and Status Registers
#define HLW8112_REG_PFCntPA 0x20 // Fast Combination Active Pulse Count A 2 bytes 0x0000
#define HLW8112_REG_PFCntPB 0x21 // Fast Combination Active Pulse Count B 2 bytes 0x0000
//readonly regss below
#define HLW8112_REG_ANGLE 0x22 // Angle between Current and Voltage 2 bytes 0x0000
#define HLW8112_REG_UFREQ 0x23 // Frequency 2 bytes 0x0000
#define HLW8112_REG_RMSIA 0x24 // RMS Current A 3 bytes 0x000000
#define HLW8112_REG_RMSIB 0x25 // RMS Current B 3 bytes 0x000000
#define HLW8112_REG_RMSU 0x26 // RMS Voltage 3 bytes 0x000000
#define HLW8112_REG_POWER_FACTOR 0x27 // Power Factor 3 bytes 0x7fffff
#define HLW8112_REG_ENERGY_PA 0x28 // Active Power Energy A 3 bytes 0x000000 (reset on read )
#define HLW8112_REG_ENERGY_PB 0x29 // Active Power Energy B
#define HLW8112_REG_POWER_PA 0x2C // Active Power A 4 bytes 0x00000000
#define HLW8112_REG_POWER_PB 0x2D // Active Power B
#define HLW8112_REG_POWER_S 0x2E // Apparent Power
#define HLW8112_REG_EMUSTATUS 0x2F // Measurement Status and Check 3 bytes 0x00b32f
#define HLW8112_REG_EMUSTATUS_CHA_SEL 21
#define HLW8112_REG_EMUSTATUS_NOPLDB 20
#define HLW8112_REG_EMUSTATUS_NOPLDA 19
#define HLW8112_REG_EMUSTATUS_REVPB 18
#define HLW8112_REG_EMUSTATUS_REVPA 17
#define HLW8112_REG_EMUSTATUS_CHKSUMBSY 16
#define HLW8112_REG_EMUSTATUS_CHKSUM 0
#define HLW8112_REG_PEAKIA 0x30 // Peak Current A 3 bytes 0x000000
#define HLW8112_REG_PEAKIB 0x31 // Peak Current B 3 bytes 0x000000
#define HLW8112_REG_PEAKU 0x32 // Peak Voltage
#define HLW8112_REG_INSTANTIA 0x33 // Instantaneous Current A 3 bytes 0x000000
#define HLW8112_REG_INSTANTIB 0x34 // Instantaneous Current B
#define HLW8112_REG_INSTANTU 0x35 // Instantaneous Voltage
#define HLW8112_REG_WAVEIA 0x36 // Waveform of Current A 3 bytes 0x000000
#define HLW8112_REG_WAVEIB 0x37 // Waveform of Current B
#define HLW8112_REG_WAVEU 0x38 // Waveform of Voltage U
#define HLW8112_REG_INSTANTP 0x3C // Instantaneous Active Power 4 bytes 0x00000000
#define HLW8112_REG_INSTANTS 0x3D // Instantaneous Apparent Power 4 bytes 0x00000000
// Interrupt Registers
#define HLW8112_REG_IE 0x40 // Interrupt Enable writable 2 bytes 0x0000
#define HLW8112_REG_IE_LEAKAGEIE 15
#define HLW8112_REG_IE_ZX_UIE 14
#define HLW8112_REG_IE_ZX_IBIE 13
#define HLW8112_REG_IE_ZX_IAIE 12
#define HLW8112_REG_IE_SAGIE 11
#define HLW8112_REG_IE_OPIE 10
#define HLW8112_REG_IE_OVIE 9
#define HLW8112_REG_IE_OIBIE 8
#define HLW8112_REG_IE_OIAIE 7
#define HLW8112_REG_IE_INSTANIE 6
#define HLW8112_REG_IE_PEBOIE 4
#define HLW8112_REG_IE_PEAOIE 3
#define HLW8112_REG_IE_PFBIE 2
#define HLW8112_REG_IE_PFAIE 1
#define HLW8112_REG_IE_DUPDIE 0
#define HLW8112_REG_INT_P2 4
#define HLW8112_REG_INT_P1 0
#define HLW8112_REG_IF 0x41 // Interrupt Flag 2 bytes 0x0000
#define HLW8112_REG_RIF 0x42 // Reset Interrupt Status 2 bytes 0x0000
#define HLW8112_REG_IF_LEAKAGEIF 15
#define HLW8112_REG_IF_ZX_UIF 14
#define HLW8112_REG_IF_ZX_IBIF 13
#define HLW8112_REG_IF_ZX_IAIF 12
#define HLW8112_REG_IF_SAGIF 11
#define HLW8112_REG_IF_OPIF 10
#define HLW8112_REG_IF_OVIF 9
#define HLW8112_REG_IF_OIBIF 8
#define HLW8112_REG_IF_OIAIF 7
#define HLW8112_REG_IF_INSTANIF 6
#define HLW8112_REG_IF_PEBOIF 4
#define HLW8112_REG_IF_PEAOIF 3
#define HLW8112_REG_IF_PFBIF 2
#define HLW8112_REG_IF_PFAIF 1
#define HLW8112_REG_IF_DUPDIF 0
// System Status Registers
#define HLW8112_REG_SYSSTATUS 0x43 // System Status 1 byte
#define HLW8112_REG_SYSSTATUS_CLKSEL 6
#define HLW8112_REG_SYSSTATUS_WREN 5
#define HLW8112_REG_SYSSTATUS_RST 0
#define HLW8112_REG_RDATA 0x44 // SPI Read Data 4 bytes
#define HLW8112_REG_WDATA 0x45 // SPI Written Data 4bytes
// Calibration Coefficients Read only
#define HLW8112_REG_COF_CHECKSUM 0x6F // Coeff checksum 2 bytes 0xffff
#define HLW8112_REG_RMSIAC 0x70 // RMS Current A 2 bytes 0xffff
#define HLW8112_REG_RMSIBC 0x71 // RMS Current B 2 bytes 0xffff
#define HLW8112_REG_RMSUC 0x72 // RMS Voltage 2 bytes 0xffff
#define HLW8112_REG_POWER_PAC 0x73 // Active Power A
#define HLW8112_REG_POWER_PBC 0x74 // Active Power B
#define HLW8112_REG_POWER_SC 0x75 // Apparent Power
#define HLW8112_REG_ENERGY_AC 0x76 // Energy A
#define HLW8112_REG_ENERGY_BC 0x77 // Energy B
// commands
#define HLW8112_REG_COMMAND 0xEA // Special command operations
#define HLW8112_COMMAND_WRITE_EN 0xE5 // Special command operations
#define HLW8112_COMMAND_WRITE_PROTECT 0xDC // Special command operations
#define HLW8112_COMMAND_SELECT_CH_A 0x5A // Special command operations
#define HLW8112_COMMAND_SELECT_CH_B 0xA5 // Special command operations
#define HLW8112_COMMAND_RESET 0x96 // Special command operations
typedef enum {
HLW8112_PGA_1 = 0,
HLW8112_PGA_2 = 1,
HLW8112_PGA_4 = 2,
HLW8112_PGA_8 = 3,
HLW8112_PGA_16 = 4,
} HLW8112_PGA_t;
typedef enum {
HLW8112_CHANNEL_A = 0,
HLW8112_CHANNEL_B = 1
} HLW8112_Channel_t;
// does it work ???
typedef enum {
HLW8112_ACTIVE_POW_CALC_METHOD_POS_NEG_ALGEBRAIC = 0,
HLW8112_ACTIVE_POW_CALC_METHOD_POS = 1,
HLW8112_ACTIVE_POW_CALC_METHOD_POS_NEG_ABSOLUTE = 2
} HLW8112_Power_CalcMethod_t;
/*
typedef enum {
HLW8112_RMS_CALC_MODE_NORMAL = 0,
HLW8112_RMS_CALC_MODE_DC = 1
} HLW8112_RMS_CalcMode_t;
typedef enum {
HLW8112_ZX_MODE_POSITIVE = 0,
HLW8112_ZX_MODE_NEGATIVE = 1,
HLW8112_ZX_MODE_BOTH = 2
} HLW8112_ZX_Mode_t;
typedef enum {
HLW8112_INTOUT_FUNC_PULSE_PFA = 0,
HLW8112_INTOUT_FUNC_PULSE_PFB = 1,
HLW8112_INTOUT_FUNC_LEAKAGE = 2,
HLW8112_INTOUT_FUNC_IRQ = 3,
HLW8112_INTOUT_FUNC_POWER_OVERLOAD = 4,
HLW8112_INTOUT_FUNC_NEGATIVE_POWER_A = 5,
HLW8112_INTOUT_FUNC_NEGATIVE_POWER_B = 6,
HLW8112_INTOUT_FUNC_INSTAN_VALUE_UPDATE_INT = 7,
HLW8112_INTOUT_FUNC_AVG_UPDATE_INT = 8,
HLW8112_INTOUT_FUNC_VOLTAGE_ZERO_CROSSING = 9,
HLW8112_INTOUT_FUNC_CURRENT_ZERO_CROSSING_A = 10,
HLW8112_INTOUT_FUNC_CURRENT_ZERO_CROSSING_B = 11,
HLW8112_INTOUT_FUNC_OVERVOLTAGE = 12,
HLW8112_INTOUT_FUNC_UNDERVOLTAGE = 13,
HLW8112_INTOUT_FUNC_OVERCURRENT_A = 14,
HLW8112_INTOUT_FUNC_OVERCURRENT_B = 15,
HLW8112_INTOUT_FUNC_NO_CHANGE = 16
} HLW8112_IntOutFunc_t;
typedef enum HLW8112_DataUpdateFreq_e {
HLW8112_DATA_UPDATE_FREQ_3_4HZ = 0,
HLW8112_DATA_UPDATE_FREQ_6_8HZ = 1,
HLW8112_DATA_UPDATE_FREQ_13_65HZ = 2,
HLW8112_DATA_UPDATE_FREQ_27_3HZ = 3
} HLW8112_DataUpdateFreq_t;
typedef enum HLW8112_EnDis_e {
HLW8112_ENDIS_NOCHANGE = -1,
HLW8112_ENDIS_DISABLE = 0,
HLW8112_ENDIS_ENABLE = 1
} HLW8112_EnDis_t;
*/
typedef enum {
HLW8112_SAVE_NONE = 0,
HLW8112_SAVE_A_IMP = 1,
HLW8112_SAVE_A_EXP = 2,
HLW8112_SAVE_A = 3,
HLW8112_SAVE_B_IMP = 4,
HLW8112_SAVE_B_EXP = 8,
HLW8112_SAVE_B = 12,
HLW8112_SAVE_ALL = 15,
HLW8112_SAVE_FORCE = 16,
} HLW8112_SaveFlag_t;
typedef uint16_t HLW8112_SaveFlags_t;
typedef struct {
uint32_t v_rms; // rms voltage
uint16_t freq; // frquency
uint32_t pf; // power factor
uint32_t ap; // Channel A aparent power
uint32_t ia_rms; // Channel A rms current
uint32_t pa; // Channel A active power
uint32_t ea; // Channel A Energy
uint32_t ib_rms; // Channel B rms current
uint32_t pb; // Channel B active power
uint32_t eb; // Channel B Energy
uint8_t sysstat; // System Status 0x43
uint32_t emustat; // Emu Status
uint16_t int_f; // Intrupt Status
} HLW8112_Data_t;
//TODO add proper units
typedef struct {
int32_t v_rms; // rms voltage
int32_t freq; // frquency
int16_t pf; // power factor
int32_t ap; // Channel A aparent power
int32_t ia_rms; // Channel A rms current
int32_t pa; // Channel A active power
ENERGY_DATA* ea; // Channel A Energy
int32_t ib_rms; // Channel B rms current
int32_t pb; // Channel B active power
ENERGY_DATA* eb; // Channel B Energy
} HLW8112_UpdateData_t;
typedef struct {
double i;
double p;
double e;
double ap;
} HLW8112_Channel_Scale_t;
typedef struct {
double v_rms;
double freq;
double pf;
HLW8112_Channel_Scale_t a;
HLW8112_Channel_Scale_t b;
} HLW8112_Scale_Factor_t;
typedef struct {
struct {
uint16_t RmsIAC;
uint16_t RmsIBC;
uint16_t RmsUC;
uint16_t PowerPAC;
uint16_t PowerPBC;
uint16_t PowerSC;
uint16_t EnergyAC;
uint16_t EnergyBC;
} DeviceRegisterCoeff;
struct {
float KU;
float KIA;
float KIB;
} ResistorCoeff ;
struct {
HLW8112_PGA_t U;
HLW8112_PGA_t IA;
HLW8112_PGA_t IB;
} PGA;
uint16_t HFconst;
uint32_t CLKI;
HLW8112_Channel_t MainChannel;
HLW8112_Scale_Factor_t ScaleFactor ;
struct
{
uint32_t _SYSCON;
uint32_t _EMUCON;
uint32_t _HFCONST;
uint32_t _PSTARTA;
uint32_t _PSTARTB;
uint32_t _PAGAIN;
uint32_t _PBGAIN;
uint32_t _PHASEA;
uint32_t _PHASEB;
uint32_t _PAOS;
uint32_t _PBOS;
uint32_t _RMSIAOS;
uint32_t _RMSIBOS;
uint32_t _IBGAIN;
uint32_t _PSGAIN;
uint32_t _PSOS;
uint32_t _EMUCON2;
} EX_REGiSTERS;
} HLW8112_Device_Conf_t;
typedef enum {
HLW8112_Channel_Voltage = 0,
HLW8112_Channel_Frequency = 1,
HLW8112_Channel_PowerFactor = 2,
HLW8112_Channel_current_A = 3,
HLW8112_Channel_current_B = 4,
HLW8112_Channel_power_A = 5,
HLW8112_Channel_power_B = 6,
HLW8112_Channel_apparent_power_A = 7,
HLW8112_Channel_export_A = 8,
HLW8112_Channel_export_B = 9,
HLW8112_Channel_import_A = 10,
HLW8112_Channel_import_B = 11,
// HLW8112_Channel_ResCof_Voltage = 12,
// HLW8112_Channel_ResCof_A = 13,
// HLW8112_Channel_ResCof_B = 14,
// HLW8112_Channel_Clk = 15,
} HLW8112_Device_channels;
void HLW8112_compute_scale_factor();
void HLW8112_ScaleEnergy(HLW8112_Channel_t channel, uint32_t regValue, int32_t* value);
int HLW8112_CheckCoeffs();
int HLW8112_UpdateCoeff();
#endif // __DRV_HLW8112_H__
#pragma GCC diagnostic pop

View File

@ -15,6 +15,7 @@
#include "drv_ds1820_simple.h"
#include "drv_ds1820_full.h"
#include "drv_ds1820_common.h"
#include "drv_hlw8112.h"
typedef struct driver_s {
@ -229,6 +230,13 @@ static driver_t g_drivers[] = {
//drvdetail:"requires":""}
{ "BL0942SPI", BL0942_SPI_Init, BL0942_SPI_RunEverySecond, BL09XX_AppendInformationToHTTPIndexPage, NULL, NULL, NULL, NULL, false },
#endif
#if ENABLE_DRIVER_HLW8112SPI
//drvdetail:{"name":"HLW8112SPI",
//drvdetail:"title":"TODO",
//drvdetail:"descr":"TODO",
//drvdetail:"requires":""}
{ "HLW8112SPI", HLW8112SPI_Init, HLW8112_RunEverySecond, HLW8112_AppendInformationToHTTPIndexPage, NULL, HLW8112SPI_Stop, NULL, HLW8112_OnHassDiscovery, false },
#endif
#if ENABLE_DRIVER_CHARGINGLIMIT
//drvdetail:{"name":"ChargingLimit",
//drvdetail:"title":"TODO",
@ -838,6 +846,7 @@ bool DRV_IsMeasuringPower() {
return DRV_IsRunning("BL0937") || DRV_IsRunning("BL0942")
|| DRV_IsRunning("CSE7766") || DRV_IsRunning("TESTPOWER")
|| DRV_IsRunning("BL0942SPI") || DRV_IsRunning("RN8209");
// || DRV_IsRunning("HLW8112SPI"); TODO messup ha config if enabled
#else
return false;
#endif

View File

@ -697,5 +697,41 @@ float HAL_FlashVars_GetEnergyExport()
return f;
}
#ifdef ENABLE_DRIVER_HLW8112SPI
void HAL_FlashVars_SaveEnergy(ENERGY_DATA** data, int channel_count)
{
#ifndef DISABLE_FLASH_VARS_VARS
FLASH_VARS_STRUCTURE tmp;
if (data != NULL)
{
uintptr_t base = (uintptr_t) &flash_vars.emetering;
for(int i =0 ; i < channel_count; i++){
int offset =( i * sizeof(ENERGY_DATA));
uintptr_t flash_addr = base + offset ;
memcpy((void *)flash_addr, data[i], sizeof(ENERGY_DATA));
}
flash_vars_write();
flash_vars_read(&tmp);
}
#endif
}
void HAL_FlashVars_GetEnergy(ENERGY_DATA* data, ENERGY_CHANNEL channel)
{
#ifndef DISABLE_FLASH_VARS_VARS
if (!flash_vars_initialised)
{
flash_vars_init();
}
if (data != NULL)
{
int offset =((channel) * sizeof(ENERGY_DATA));
uintptr_t base = (uintptr_t) &flash_vars.emetering;
uintptr_t flash_addr = base + offset;
memcpy(data ,(void *)flash_addr, sizeof(ENERGY_DATA));
}
#endif
}
#endif
#endif

View File

@ -10,6 +10,7 @@
#include "../../httpserver/new_http.h"
#include "../../driver/drv_public.h"
#include "../../driver/drv_bl_shared.h"
#include "../../driver/drv_hlw8112.h"
static unsigned char *sector = (void *)0;
int sectorlen = 0;
@ -155,6 +156,9 @@ int myhttpclientcallback(httprequest_t* request){
BL09XX_SaveEmeteringStatistics();
}
#endif
#if ENABLE_DRIVER_HLW8112SPI
HLW8112_Save_Statistics();
#endif
rtos_delay_milliseconds(1000);
bk_reboot();
break;

View File

@ -28,6 +28,17 @@ typedef struct ENERGY_METERING_DATA {
char actual_mday;
} ENERGY_METERING_DATA;
// size 8 bytes
typedef struct {
float Import;
float Export;
} ENERGY_DATA;
typedef enum {
ENERGY_CHANNEL_A = 0,
ENERGY_CHANNEL_B = 1,
} ENERGY_CHANNEL;
typedef struct flash_vars_structure
{
// offset 0
@ -62,5 +73,10 @@ void HAL_FlashVars_SaveTotalConsumption(float total_consumption);
void HAL_FlashVars_SaveEnergyExport(float f);
float HAL_FlashVars_GetEnergyExport();
#ifdef ENABLE_DRIVER_HLW8112SPI
void HAL_FlashVars_SaveEnergy(ENERGY_DATA** data, int channel_count);
void HAL_FlashVars_GetEnergy(ENERGY_DATA* data, ENERGY_CHANNEL channel);
#endif
#endif /* __HALK_FLASH_VARS_H__ */

View File

@ -131,6 +131,9 @@ void hass_populate_unique_id(ENTITY_TYPE type, int index, char* uniq_id, int ase
case HASS_PERCENT:
sprintf(uniq_id, "%s_%s_%d", longDeviceName, "number", index);
break;
case HASS_BUTTON:
sprintf(uniq_id, "%s_%s", longDeviceName, "button");
break;
default:
// TODO: USE type here as well?
// If type is not set, and we use "sensor" naming, we can easily make collision
@ -204,6 +207,9 @@ void hass_populate_device_config_channel(ENTITY_TYPE type, char* uniq_id, HassDe
case HUMIDITY_SENSOR:
sprintf(info->channel, "sensor/%s/config", uniq_id);
break;
case HASS_BUTTON:
sprintf(info->channel, "button/%s/config", uniq_id);
break;
default:
sprintf(info->channel, "sensor/%s/config", uniq_id);
break;
@ -563,13 +569,17 @@ HassDeviceInfo* hass_init_device_info(ENTITY_TYPE type, int index, const char* p
case TIMESTAMP_SENSOR:
sprintf(g_hassBuffer, "Timestamp");
break;
case HASS_BUTTON:
sprintf(g_hassBuffer, "%s" , "");
break;
default:
sprintf(g_hassBuffer, "%s", CHANNEL_GetLabel(index));
break;
}
}
if (title) {
strcat(g_hassBuffer, "_");
if (type!=HASS_BUTTON) strcat(g_hassBuffer, "_");
strcat(g_hassBuffer, title);
}
cJSON_AddStringToObject(info->root, "name", g_hassBuffer);
@ -588,8 +598,13 @@ HassDeviceInfo* hass_init_device_info(ENTITY_TYPE type, int index, const char* p
}
if (!isSensor && type != HASS_TEXTFIELD) { //Sensors (except binary_sensor) don't use payload
cJSON_AddStringToObject(info->root, "pl_on", payload_on); //payload_on
cJSON_AddStringToObject(info->root, "pl_off", payload_off); //payload_off
if(type == HASS_BUTTON) {
cJSON_AddStringToObject(info->root, "payload_press", payload_on);
}
else if(type != HASS_TEXTFIELD){
cJSON_AddStringToObject(info->root, "pl_on", payload_on); //payload_on
cJSON_AddStringToObject(info->root, "pl_off", payload_off); //payload_off
}
}
cJSON_AddStringToObject(info->root, "uniq_id", info->unique_id); //unique_id
@ -804,9 +819,23 @@ HassDeviceInfo* hass_init_energy_sensor_device_info(int index, int asensdataseti
// }
return info;
}
#endif
HassDeviceInfo* hass_init_button_device_info(char* title,char* cmd_id, char* press_payload, HASS_CATEGORY_TYPE type) {
HassDeviceInfo* info = 0;
const char* clientId = CFG_GetMQTTClientId();
info = hass_init_device_info(HASS_BUTTON, 0, press_payload, NULL, 0, title);
if (type == HASS_CATEGORY_DIAGNOSTIC){
cJSON_AddStringToObject(info->root, "entity_category", "diagnostic");
}
else {
cJSON_AddStringToObject(info->root, "entity_category", "config");
}
sprintf(g_hassBuffer, "cmnd/%s/%s", clientId, cmd_id);
cJSON_AddStringToObject(info->root, "command_topic", g_hassBuffer);
return info;
}
// generate string like "{{ float(value)*0.1|round(2) }}"
// {{ float(value)*0.1 }} for value=12 give 1.2000000000000002, using round() to limit the decimal places
// 2023 10 19 - it is not a perfect solution, it's better to use:

View File

@ -100,8 +100,13 @@ typedef enum {
HASS_SELECT,
HASS_PERCENT,
HASS_TEXTFIELD,
HASS_BUTTON,
} ENTITY_TYPE;
typedef enum {
HASS_CATEGORY_CONFIG = 0,
HASS_CATEGORY_DIAGNOSTIC = 1,
} HASS_CATEGORY_TYPE;
//unique_id is defined in hass_populate_unique_id and is based on CFG_GetDeviceName() whose size is CGF_DEVICE_NAME_SIZE.
//Sample unique_id would be deviceName_entityType_index.
//Currently supported entityType is `relay` or `light` - 5 char.
@ -147,5 +152,5 @@ const char* hass_build_discovery_json(HassDeviceInfo* info);
void hass_free_device_info(HassDeviceInfo* info);
char *hass_generate_multiplyAndRound_template(int decimalPlacesForRounding, int decimalPointOffset, int divider);
HassDeviceInfo* hass_init_textField_info(int index);
HassDeviceInfo* hass_init_button_device_info(char* title,char* cmd_id, char* press_payload, HASS_CATEGORY_TYPE type);
#endif // ENABLE_HA_DISCOVERY

View File

@ -2201,6 +2201,16 @@ void doHomeAssistantDiscovery(const char* topic, http_request_t* request) {
dev_info = hass_init_sensor_device_info(ENERGY_SENSOR, i, 3, 2, 1);
}
break;
case ChType_EnergyExport_kWh_div1000:
{
dev_info = hass_init_sensor_device_info(ENERGY_SENSOR, i, 3, 3, 1);
}
break;
case ChType_EnergyImport_kWh_div1000:
{
dev_info = hass_init_sensor_device_info(ENERGY_SENSOR, i, 3, 3, 1);
}
break;
case ChType_EnergyTotal_kWh_div1000:
{
dev_info = hass_init_sensor_device_info(ENERGY_SENSOR, i, 3, 3, 1);

View File

@ -557,6 +557,7 @@ const char* htmlPinRoleNames[] = {
"IRRecv_nPup",
"StripState",
"StripState_n",
"HLW_8112_SCSN",
"error",
"error",
"error",

View File

@ -138,6 +138,7 @@ OBK_Publish_Result MQTT_ChannelPublish(int channel, int flags);
void MQTT_ClearCallbacks();
int MQTT_RegisterCallback(const char* basetopic, const char* subscriptiontopic, int ID, mqtt_callback_fn callback);
int MQTT_RemoveCallback(int ID);
const char* MQTT_RemoveClientFromTopic(const char* topic, const char *prefix);
// this is called from tcp_thread context to queue received mqtt,
// and then we'll retrieve them from our own thread for processing.

View File

@ -479,7 +479,8 @@ int PIN_IOR_NofChan(int test){
|| test == IOR_BL0937_CF || test == IOR_BL0937_CF1 || test == IOR_BL0937_SEL
|| test == IOR_LED_WIFI || test == IOR_LED_WIFI_n || test == IOR_LED_WIFI_n
|| (test >= IOR_IRRecv && test <= IOR_DHT11)
|| (test >= IOR_SM2135_DAT && test <= IOR_BP1658CJ_CLK)) {
|| (test >= IOR_SM2135_DAT && test <= IOR_BP1658CJ_CLK)
|| (test == IOR_HLW8112_SCSN)) {
return 0;
}
// all others have 1 channel
@ -1381,6 +1382,7 @@ int ChannelType_GetDivider(int type) {
case ChType_EnergyTotal_kWh_div1000:
case ChType_EnergyExport_kWh_div1000:
case ChType_EnergyToday_kWh_div1000:
case ChType_EnergyImport_kWh_div1000:
case ChType_Current_div1000:
case ChType_LeakageCurrent_div1000:
case ChType_ReadOnly_div1000:
@ -1422,6 +1424,7 @@ const char *ChannelType_GetUnit(int type) {
case ChType_EnergyExport_kWh_div1000:
case ChType_EnergyToday_kWh_div1000:
case ChType_EnergyTotal_kWh_div100:
case ChType_EnergyImport_kWh_div1000:
return "kWh";
case ChType_PowerFactor_div1000:
case ChType_PowerFactor_div100:
@ -1477,6 +1480,8 @@ const char *ChannelType_GetTitle(int type) {
return "EnergyExport";
case ChType_EnergyToday_kWh_div1000:
return "EnergyToday";
case ChType_EnergyImport_kWh_div1000:
return "EnergyImport";
case ChType_PowerFactor_div1000:
case ChType_PowerFactor_div100:
return "PowerFactor";
@ -2383,6 +2388,7 @@ const char* g_channelTypeNames[] = {
"OpenStopClose",
"Percent",
"StopUpDown",
"EnergyImport_kWh_div1000",
"error",
"error",
};

View File

@ -609,6 +609,13 @@ typedef enum ioRole_e {
//iodetail:"file":"new_pins.h",
//iodetail:"driver":""}
IOR_StripState_n,
//iodetail:{"name":"HLW8112_SCSN",
//iodetail:"title":"HLW8112 SCSN Pin",
//iodetail:"descr":"SCSN pin for HLW8112 SPI energy measuring devices.",
//iodetail:"enum":"IOR_HLW8112_SCSN",
//iodetail:"file":"new_pins.h",
//iodetail:"driver":"HLW8112SPI"}
IOR_HLW8112_SCSN,
//iodetail:{"name":"Total_Options",
//iodetail:"title":"TODO",
//iodetail:"descr":"Current total number of available IOR roles",
@ -1057,6 +1064,13 @@ typedef enum channelType_e {
//chandetail:"file":"new_pins.h",
//chandetail:"driver":""}
ChType_StopUpDown,
//chandetail:{"name":"EnergyImport_kWh_div1000",
//chandetail:"title":"EnergyImport_kWh_div1000",
//chandetail:"descr":"TODO",
//chandetail:"enum":"ChType_EnergyImport_kWh_div1000",
//chandetail:"file":"new_pins.h",
//chandetail:"driver":""}
ChType_EnergyImport_kWh_div1000,
//chandetail:{"name":"Max",
//chandetail:"title":"TODO",
//chandetail:"descr":"This is the current total number of available channel types.",
@ -1319,7 +1333,11 @@ enum {
CFG_OBK_VOLTAGE = 0,
CFG_OBK_CURRENT,
CFG_OBK_POWER,
CFG_OBK_POWER_MAX
CFG_OBK_POWER_MAX,
CFG_OBK_CLK, // HLW8112 clock freq internal or external
CFG_OBK_RES_KU, // HLW8112 voltage channel K
CFG_OBK_RES_KIA, // HLW8112 current A channel K
CFG_OBK_RES_KIB, // HLW8112 current B channel K
};
typedef struct led_corr_s { // LED gamma correction and calibration data block

View File

@ -292,6 +292,9 @@
#define NEW_TCP_SERVER 1
#endif
#if PLATFORM_BK7231N
#define ENABLE_DRIVER_HLW8112SPI 0
#endif
// ENABLE_I2C_ is a syntax for
// our I2C system defines for drv_i2c_main.c
// #define ENABLE_I2C_ADS1115 1
@ -322,6 +325,7 @@
#undef ENABLE_DRIVER_BL0937
#undef ENABLE_DRIVER_BL0942
#undef ENABLE_DRIVER_BL0942SPI
#undef ENABLE_DRIVER_HLW8112SPI
#undef ENABLE_DRIVER_CSE7766
#undef ENABLE_DRIVER_BRIDGE
#endif

View File

@ -11,6 +11,7 @@
//#include "driver/drv_ir.h"
#include "driver/drv_public.h"
#include "driver/drv_bl_shared.h"
#include "driver/drv_hlw8112.h"
//#include "ir/ir_local.h"
// Commands register, execution API and cmd tokenizer
@ -1020,7 +1021,10 @@ void Main_OnEverySecond()
{
BL09XX_SaveEmeteringStatistics();
}
#endif
#endif
#if ENABLE_DRIVER_HLW8112SPI
HLW8112_Save_Statistics();
#endif
ADDLOGF_INFO("Going to call HAL_RebootModule\r\n");
HAL_RebootModule();
}
@ -1369,6 +1373,9 @@ void Main_Init_BeforeDelay_Unsafe(bool bAutoRunScripts) {
{
DRV_StartDriver("GN6932");
}
if (PIN_FindPinIndexForRole(IOR_HLW8112_SCSN, -1) != -1) {
DRV_StartDriver("HLW8112SPI");
}
// if ((PIN_FindPinIndexForRole(IOR_TM1638_CLK, -1) != -1) &&
// (PIN_FindPinIndexForRole(IOR_TM1638_DAT, -1) != -1) &&
// (PIN_FindPinIndexForRole(IOR_TM1638_STB, -1) != -1))