mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	bl0942: Fix init sequence, add address and line_frequency options (#7250)
This commit is contained in:
		| @@ -2,6 +2,8 @@ | ||||
| #include "esphome/core/log.h" | ||||
| #include <cinttypes> | ||||
|  | ||||
| // Datasheet: https://www.belling.com.cn/media/file_object/bel_product/BL0942/datasheet/BL0942_V1.06_en.pdf | ||||
|  | ||||
| namespace esphome { | ||||
| namespace bl0942 { | ||||
|  | ||||
| @@ -12,33 +14,41 @@ static const uint8_t BL0942_FULL_PACKET = 0xAA; | ||||
| static const uint8_t BL0942_PACKET_HEADER = 0x55; | ||||
|  | ||||
| static const uint8_t BL0942_WRITE_COMMAND = 0xA8; | ||||
| static const uint8_t BL0942_REG_I_FAST_RMS_CTRL = 0x10; | ||||
| static const uint8_t BL0942_REG_MODE = 0x18; | ||||
| static const uint8_t BL0942_REG_SOFT_RESET = 0x19; | ||||
| static const uint8_t BL0942_REG_USR_WRPROT = 0x1A; | ||||
|  | ||||
| static const uint8_t BL0942_REG_I_RMSOS = 0x12; | ||||
| static const uint8_t BL0942_REG_WA_CREEP = 0x14; | ||||
| static const uint8_t BL0942_REG_I_FAST_RMS_TH = 0x15; | ||||
| static const uint8_t BL0942_REG_I_FAST_RMS_CYC = 0x16; | ||||
| static const uint8_t BL0942_REG_FREQ_CYC = 0x17; | ||||
| static const uint8_t BL0942_REG_OT_FUNX = 0x18; | ||||
| static const uint8_t BL0942_REG_MODE = 0x19; | ||||
| static const uint8_t BL0942_REG_SOFT_RESET = 0x1C; | ||||
| static const uint8_t BL0942_REG_USR_WRPROT = 0x1D; | ||||
| static const uint8_t BL0942_REG_TPS_CTRL = 0x1B; | ||||
|  | ||||
| // TODO: Confirm insialisation works as intended | ||||
| const uint8_t BL0942_INIT[5][6] = { | ||||
|     // Reset to default | ||||
|     {BL0942_WRITE_COMMAND, BL0942_REG_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x38}, | ||||
|     // Enable User Operation Write | ||||
|     {BL0942_WRITE_COMMAND, BL0942_REG_USR_WRPROT, 0x55, 0x00, 0x00, 0xF0}, | ||||
|     // 0x0100 = CF_UNABLE energy pulse, AC_FREQ_SEL 50Hz, RMS_UPDATE_SEL 800mS | ||||
|     {BL0942_WRITE_COMMAND, BL0942_REG_MODE, 0x00, 0x10, 0x00, 0x37}, | ||||
|     // 0x47FF = Over-current and leakage alarm on, Automatic temperature measurement, Interval 100mS | ||||
|     {BL0942_WRITE_COMMAND, BL0942_REG_TPS_CTRL, 0xFF, 0x47, 0x00, 0xFE}, | ||||
|     // 0x181C = Half cycle, Fast RMS threshold 6172 | ||||
|     {BL0942_WRITE_COMMAND, BL0942_REG_I_FAST_RMS_CTRL, 0x1C, 0x18, 0x00, 0x1B}}; | ||||
| static const uint32_t BL0942_REG_MODE_RESV = 0x03; | ||||
| static const uint32_t BL0942_REG_MODE_CF_EN = 0x04; | ||||
| static const uint32_t BL0942_REG_MODE_RMS_UPDATE_SEL = 0x08; | ||||
| static const uint32_t BL0942_REG_MODE_FAST_RMS_SEL = 0x10; | ||||
| static const uint32_t BL0942_REG_MODE_AC_FREQ_SEL = 0x20; | ||||
| static const uint32_t BL0942_REG_MODE_CF_CNT_CLR_SEL = 0x40; | ||||
| static const uint32_t BL0942_REG_MODE_CF_CNT_ADD_SEL = 0x80; | ||||
| static const uint32_t BL0942_REG_MODE_UART_RATE_19200 = 0x200; | ||||
| static const uint32_t BL0942_REG_MODE_UART_RATE_38400 = 0x300; | ||||
| static const uint32_t BL0942_REG_MODE_DEFAULT = | ||||
|     BL0942_REG_MODE_RESV | BL0942_REG_MODE_CF_EN | BL0942_REG_MODE_CF_CNT_ADD_SEL; | ||||
|  | ||||
| static const uint32_t BL0942_REG_SOFT_RESET_MAGIC = 0x5a5a5a; | ||||
| static const uint32_t BL0942_REG_USR_WRPROT_MAGIC = 0x55; | ||||
|  | ||||
| void BL0942::loop() { | ||||
|   DataPacket buffer; | ||||
|   if (!this->available()) { | ||||
|     return; | ||||
|   } | ||||
|   if (read_array((uint8_t *) &buffer, sizeof(buffer))) { | ||||
|     if (validate_checksum(&buffer)) { | ||||
|       received_package_(&buffer); | ||||
|   if (this->read_array((uint8_t *) &buffer, sizeof(buffer))) { | ||||
|     if (this->validate_checksum_(&buffer)) { | ||||
|       this->received_package_(&buffer); | ||||
|     } | ||||
|   } else { | ||||
|     ESP_LOGW(TAG, "Junk on wire. Throwing away partial message"); | ||||
| @@ -47,8 +57,8 @@ void BL0942::loop() { | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool BL0942::validate_checksum(DataPacket *data) { | ||||
|   uint8_t checksum = BL0942_READ_COMMAND; | ||||
| bool BL0942::validate_checksum_(DataPacket *data) { | ||||
|   uint8_t checksum = BL0942_READ_COMMAND | this->address_; | ||||
|   // Whole package but checksum | ||||
|   uint8_t *raw = (uint8_t *) data; | ||||
|   for (uint32_t i = 0; i < sizeof(*data) - 1; i++) { | ||||
| @@ -61,17 +71,58 @@ bool BL0942::validate_checksum(DataPacket *data) { | ||||
|   return checksum == data->checksum; | ||||
| } | ||||
|  | ||||
| void BL0942::update() { | ||||
| void BL0942::write_reg_(uint8_t reg, uint32_t val) { | ||||
|   uint8_t pkt[6]; | ||||
|  | ||||
|   this->flush(); | ||||
|   this->write_byte(BL0942_READ_COMMAND); | ||||
|   pkt[0] = BL0942_WRITE_COMMAND | this->address_; | ||||
|   pkt[1] = reg; | ||||
|   pkt[2] = (val & 0xff); | ||||
|   pkt[3] = (val >> 8) & 0xff; | ||||
|   pkt[4] = (val >> 16) & 0xff; | ||||
|   pkt[5] = (pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4]) ^ 0xff; | ||||
|   this->write_array(pkt, 6); | ||||
|   delay(1); | ||||
| } | ||||
|  | ||||
| int BL0942::read_reg_(uint8_t reg) { | ||||
|   union { | ||||
|     uint8_t b[4]; | ||||
|     uint32_le_t le32; | ||||
|   } resp; | ||||
|  | ||||
|   this->write_byte(BL0942_READ_COMMAND | this->address_); | ||||
|   this->write_byte(reg); | ||||
|   this->flush(); | ||||
|   if (this->read_array(resp.b, 4) && | ||||
|       resp.b[3] == | ||||
|           (uint8_t) ((BL0942_READ_COMMAND + this->address_ + reg + resp.b[0] + resp.b[1] + resp.b[2]) ^ 0xff)) { | ||||
|     resp.b[3] = 0; | ||||
|     return resp.le32; | ||||
|   } | ||||
|   return -1; | ||||
| } | ||||
|  | ||||
| void BL0942::update() { | ||||
|   this->write_byte(BL0942_READ_COMMAND | this->address_); | ||||
|   this->write_byte(BL0942_FULL_PACKET); | ||||
| } | ||||
|  | ||||
| void BL0942::setup() { | ||||
|   for (auto *i : BL0942_INIT) { | ||||
|     this->write_array(i, 6); | ||||
|     delay(1); | ||||
|   } | ||||
|   this->write_reg_(BL0942_REG_USR_WRPROT, BL0942_REG_USR_WRPROT_MAGIC); | ||||
|   this->write_reg_(BL0942_REG_SOFT_RESET, BL0942_REG_SOFT_RESET_MAGIC); | ||||
|  | ||||
|   uint32_t mode = BL0942_REG_MODE_DEFAULT; | ||||
|   mode |= BL0942_REG_MODE_RMS_UPDATE_SEL; /* 800ms refresh time */ | ||||
|   if (this->line_freq_ == LINE_FREQUENCY_60HZ) | ||||
|     mode |= BL0942_REG_MODE_AC_FREQ_SEL; | ||||
|   this->write_reg_(BL0942_REG_MODE, mode); | ||||
|  | ||||
|   this->write_reg_(BL0942_REG_USR_WRPROT, 0); | ||||
|  | ||||
|   if (this->read_reg_(BL0942_REG_MODE) != mode) | ||||
|     this->status_set_warning("BL0942 setup failed!"); | ||||
|  | ||||
|   this->flush(); | ||||
| } | ||||
|  | ||||
| @@ -104,13 +155,15 @@ void BL0942::received_package_(DataPacket *data) { | ||||
|   if (frequency_sensor_ != nullptr) { | ||||
|     frequency_sensor_->publish_state(frequency); | ||||
|   } | ||||
|  | ||||
|   this->status_clear_warning(); | ||||
|   ESP_LOGV(TAG, "BL0942: U %fV, I %fA, P %fW, Cnt %" PRId32 ", ∫P %fkWh, frequency %fHz, status 0x%08X", v_rms, i_rms, | ||||
|            watt, cf_cnt, total_energy_consumption, frequency, data->status); | ||||
| } | ||||
|  | ||||
| void BL0942::dump_config() {  // NOLINT(readability-function-cognitive-complexity) | ||||
|   ESP_LOGCONFIG(TAG, "BL0942:"); | ||||
|   ESP_LOGCONFIG(TAG, "  Address: %d", this->address_); | ||||
|   ESP_LOGCONFIG(TAG, "  Nominal line frequency: %d Hz", this->line_freq_); | ||||
|   LOG_SENSOR("", "Voltage", this->voltage_sensor_); | ||||
|   LOG_SENSOR("", "Current", this->current_sensor_); | ||||
|   LOG_SENSOR("", "Power", this->power_sensor_); | ||||
|   | ||||
| @@ -28,6 +28,11 @@ struct DataPacket { | ||||
|   uint8_t checksum; | ||||
| } __attribute__((packed)); | ||||
|  | ||||
| enum LineFrequency : uint8_t { | ||||
|   LINE_FREQUENCY_50HZ = 50, | ||||
|   LINE_FREQUENCY_60HZ = 60, | ||||
| }; | ||||
|  | ||||
| class BL0942 : public PollingComponent, public uart::UARTDevice { | ||||
|  public: | ||||
|   void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; } | ||||
| @@ -35,9 +40,10 @@ class BL0942 : public PollingComponent, public uart::UARTDevice { | ||||
|   void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; } | ||||
|   void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; } | ||||
|   void set_frequency_sensor(sensor::Sensor *frequency_sensor) { frequency_sensor_ = frequency_sensor; } | ||||
|   void set_line_freq(LineFrequency freq) { this->line_freq_ = freq; } | ||||
|   void set_address(uint8_t address) { this->address_ = address; } | ||||
|  | ||||
|   void loop() override; | ||||
|  | ||||
|   void update() override; | ||||
|   void setup() override; | ||||
|   void dump_config() override; | ||||
| @@ -59,9 +65,12 @@ class BL0942 : public PollingComponent, public uart::UARTDevice { | ||||
|   float current_reference_ = BL0942_IREF; | ||||
|   // Divide by this to turn into kWh | ||||
|   float energy_reference_ = BL0942_EREF; | ||||
|   uint8_t address_ = 0; | ||||
|   LineFrequency line_freq_ = LINE_FREQUENCY_50HZ; | ||||
|  | ||||
|   static bool validate_checksum(DataPacket *data); | ||||
|  | ||||
|   bool validate_checksum_(DataPacket *data); | ||||
|   int read_reg_(uint8_t reg); | ||||
|   void write_reg_(uint8_t reg, uint32_t val); | ||||
|   void received_package_(DataPacket *data); | ||||
| }; | ||||
| }  // namespace bl0942 | ||||
|   | ||||
| @@ -1,25 +1,27 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import sensor, uart | ||||
| import esphome.config_validation as cv | ||||
| from esphome.const import ( | ||||
|     CONF_ADDRESS, | ||||
|     CONF_CURRENT, | ||||
|     CONF_ENERGY, | ||||
|     CONF_FREQUENCY, | ||||
|     CONF_ID, | ||||
|     CONF_LINE_FREQUENCY, | ||||
|     CONF_POWER, | ||||
|     CONF_VOLTAGE, | ||||
|     CONF_FREQUENCY, | ||||
|     DEVICE_CLASS_CURRENT, | ||||
|     DEVICE_CLASS_ENERGY, | ||||
|     DEVICE_CLASS_FREQUENCY, | ||||
|     DEVICE_CLASS_POWER, | ||||
|     DEVICE_CLASS_VOLTAGE, | ||||
|     DEVICE_CLASS_FREQUENCY, | ||||
|     STATE_CLASS_MEASUREMENT, | ||||
|     STATE_CLASS_TOTAL_INCREASING, | ||||
|     UNIT_AMPERE, | ||||
|     UNIT_HERTZ, | ||||
|     UNIT_KILOWATT_HOURS, | ||||
|     UNIT_VOLT, | ||||
|     UNIT_WATT, | ||||
|     UNIT_HERTZ, | ||||
|     STATE_CLASS_TOTAL_INCREASING, | ||||
| ) | ||||
|  | ||||
| DEPENDENCIES = ["uart"] | ||||
| @@ -27,6 +29,12 @@ DEPENDENCIES = ["uart"] | ||||
| bl0942_ns = cg.esphome_ns.namespace("bl0942") | ||||
| BL0942 = bl0942_ns.class_("BL0942", cg.PollingComponent, uart.UARTDevice) | ||||
|  | ||||
| LineFrequency = bl0942_ns.enum("LineFrequency") | ||||
| LINE_FREQS = { | ||||
|     50: LineFrequency.LINE_FREQUENCY_50HZ, | ||||
|     60: LineFrequency.LINE_FREQUENCY_60HZ, | ||||
| } | ||||
|  | ||||
| CONFIG_SCHEMA = ( | ||||
|     cv.Schema( | ||||
|         { | ||||
| @@ -61,6 +69,14 @@ CONFIG_SCHEMA = ( | ||||
|                 device_class=DEVICE_CLASS_FREQUENCY, | ||||
|                 state_class=STATE_CLASS_MEASUREMENT, | ||||
|             ), | ||||
|             cv.Optional(CONF_LINE_FREQUENCY, default="50HZ"): cv.All( | ||||
|                 cv.frequency, | ||||
|                 cv.enum( | ||||
|                     LINE_FREQS, | ||||
|                     int=True, | ||||
|                 ), | ||||
|             ), | ||||
|             cv.Optional(CONF_ADDRESS, default=0): cv.int_range(min=0, max=3), | ||||
|         } | ||||
|     ) | ||||
|     .extend(cv.polling_component_schema("60s")) | ||||
| @@ -88,3 +104,5 @@ async def to_code(config): | ||||
|     if frequency_config := config.get(CONF_FREQUENCY): | ||||
|         sens = await sensor.new_sensor(frequency_config) | ||||
|         cg.add(var.set_frequency_sensor(sens)) | ||||
|     cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY])) | ||||
|     cg.add(var.set_address(config[CONF_ADDRESS])) | ||||
|   | ||||
							
								
								
									
										22
									
								
								tests/components/bl0942/test.bk72xx-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								tests/components/bl0942/test.bk72xx-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| uart: | ||||
|   - id: uart_bl0942 | ||||
|     tx_pin: | ||||
|       number: TX1 | ||||
|     rx_pin: | ||||
|       number: RX1 | ||||
|     baud_rate: 2400 | ||||
|  | ||||
| sensor: | ||||
|   - platform: bl0942 | ||||
|     address: 0 | ||||
|     line_frequency: 50Hz | ||||
|     voltage: | ||||
|       name: BL0942 Voltage | ||||
|     current: | ||||
|       name: BL0942 Current | ||||
|     power: | ||||
|       name: BL0942 Power | ||||
|     energy: | ||||
|       name: BL0942 Energy | ||||
|     frequency: | ||||
|       name: BL0942 Frequency | ||||
		Reference in New Issue
	
	Block a user