mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +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 "esphome/core/log.h" | ||||||
| #include <cinttypes> | #include <cinttypes> | ||||||
|  |  | ||||||
|  | // Datasheet: https://www.belling.com.cn/media/file_object/bel_product/BL0942/datasheet/BL0942_V1.06_en.pdf | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace bl0942 { | 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_PACKET_HEADER = 0x55; | ||||||
|  |  | ||||||
| static const uint8_t BL0942_WRITE_COMMAND = 0xA8; | 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_I_RMSOS = 0x12; | ||||||
| static const uint8_t BL0942_REG_SOFT_RESET = 0x19; | static const uint8_t BL0942_REG_WA_CREEP = 0x14; | ||||||
| static const uint8_t BL0942_REG_USR_WRPROT = 0x1A; | 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; | static const uint8_t BL0942_REG_TPS_CTRL = 0x1B; | ||||||
|  |  | ||||||
| // TODO: Confirm insialisation works as intended | static const uint32_t BL0942_REG_MODE_RESV = 0x03; | ||||||
| const uint8_t BL0942_INIT[5][6] = { | static const uint32_t BL0942_REG_MODE_CF_EN = 0x04; | ||||||
|     // Reset to default | static const uint32_t BL0942_REG_MODE_RMS_UPDATE_SEL = 0x08; | ||||||
|     {BL0942_WRITE_COMMAND, BL0942_REG_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x38}, | static const uint32_t BL0942_REG_MODE_FAST_RMS_SEL = 0x10; | ||||||
|     // Enable User Operation Write | static const uint32_t BL0942_REG_MODE_AC_FREQ_SEL = 0x20; | ||||||
|     {BL0942_WRITE_COMMAND, BL0942_REG_USR_WRPROT, 0x55, 0x00, 0x00, 0xF0}, | static const uint32_t BL0942_REG_MODE_CF_CNT_CLR_SEL = 0x40; | ||||||
|     // 0x0100 = CF_UNABLE energy pulse, AC_FREQ_SEL 50Hz, RMS_UPDATE_SEL 800mS | static const uint32_t BL0942_REG_MODE_CF_CNT_ADD_SEL = 0x80; | ||||||
|     {BL0942_WRITE_COMMAND, BL0942_REG_MODE, 0x00, 0x10, 0x00, 0x37}, | static const uint32_t BL0942_REG_MODE_UART_RATE_19200 = 0x200; | ||||||
|     // 0x47FF = Over-current and leakage alarm on, Automatic temperature measurement, Interval 100mS | static const uint32_t BL0942_REG_MODE_UART_RATE_38400 = 0x300; | ||||||
|     {BL0942_WRITE_COMMAND, BL0942_REG_TPS_CTRL, 0xFF, 0x47, 0x00, 0xFE}, | static const uint32_t BL0942_REG_MODE_DEFAULT = | ||||||
|     // 0x181C = Half cycle, Fast RMS threshold 6172 |     BL0942_REG_MODE_RESV | BL0942_REG_MODE_CF_EN | BL0942_REG_MODE_CF_CNT_ADD_SEL; | ||||||
|     {BL0942_WRITE_COMMAND, BL0942_REG_I_FAST_RMS_CTRL, 0x1C, 0x18, 0x00, 0x1B}}; |  | ||||||
|  | static const uint32_t BL0942_REG_SOFT_RESET_MAGIC = 0x5a5a5a; | ||||||
|  | static const uint32_t BL0942_REG_USR_WRPROT_MAGIC = 0x55; | ||||||
|  |  | ||||||
| void BL0942::loop() { | void BL0942::loop() { | ||||||
|   DataPacket buffer; |   DataPacket buffer; | ||||||
|   if (!this->available()) { |   if (!this->available()) { | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|   if (read_array((uint8_t *) &buffer, sizeof(buffer))) { |   if (this->read_array((uint8_t *) &buffer, sizeof(buffer))) { | ||||||
|     if (validate_checksum(&buffer)) { |     if (this->validate_checksum_(&buffer)) { | ||||||
|       received_package_(&buffer); |       this->received_package_(&buffer); | ||||||
|     } |     } | ||||||
|   } else { |   } else { | ||||||
|     ESP_LOGW(TAG, "Junk on wire. Throwing away partial message"); |     ESP_LOGW(TAG, "Junk on wire. Throwing away partial message"); | ||||||
| @@ -47,8 +57,8 @@ void BL0942::loop() { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| bool BL0942::validate_checksum(DataPacket *data) { | bool BL0942::validate_checksum_(DataPacket *data) { | ||||||
|   uint8_t checksum = BL0942_READ_COMMAND; |   uint8_t checksum = BL0942_READ_COMMAND | this->address_; | ||||||
|   // Whole package but checksum |   // Whole package but checksum | ||||||
|   uint8_t *raw = (uint8_t *) data; |   uint8_t *raw = (uint8_t *) data; | ||||||
|   for (uint32_t i = 0; i < sizeof(*data) - 1; i++) { |   for (uint32_t i = 0; i < sizeof(*data) - 1; i++) { | ||||||
| @@ -61,17 +71,58 @@ bool BL0942::validate_checksum(DataPacket *data) { | |||||||
|   return checksum == data->checksum; |   return checksum == data->checksum; | ||||||
| } | } | ||||||
|  |  | ||||||
| void BL0942::update() { | void BL0942::write_reg_(uint8_t reg, uint32_t val) { | ||||||
|  |   uint8_t pkt[6]; | ||||||
|  |  | ||||||
|   this->flush(); |   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); |   this->write_byte(BL0942_FULL_PACKET); | ||||||
| } | } | ||||||
|  |  | ||||||
| void BL0942::setup() { | void BL0942::setup() { | ||||||
|   for (auto *i : BL0942_INIT) { |   this->write_reg_(BL0942_REG_USR_WRPROT, BL0942_REG_USR_WRPROT_MAGIC); | ||||||
|     this->write_array(i, 6); |   this->write_reg_(BL0942_REG_SOFT_RESET, BL0942_REG_SOFT_RESET_MAGIC); | ||||||
|     delay(1); |  | ||||||
|   } |   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(); |   this->flush(); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -104,13 +155,15 @@ void BL0942::received_package_(DataPacket *data) { | |||||||
|   if (frequency_sensor_ != nullptr) { |   if (frequency_sensor_ != nullptr) { | ||||||
|     frequency_sensor_->publish_state(frequency); |     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, |   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); |            watt, cf_cnt, total_energy_consumption, frequency, data->status); | ||||||
| } | } | ||||||
|  |  | ||||||
| void BL0942::dump_config() {  // NOLINT(readability-function-cognitive-complexity) | void BL0942::dump_config() {  // NOLINT(readability-function-cognitive-complexity) | ||||||
|   ESP_LOGCONFIG(TAG, "BL0942:"); |   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("", "Voltage", this->voltage_sensor_); | ||||||
|   LOG_SENSOR("", "Current", this->current_sensor_); |   LOG_SENSOR("", "Current", this->current_sensor_); | ||||||
|   LOG_SENSOR("", "Power", this->power_sensor_); |   LOG_SENSOR("", "Power", this->power_sensor_); | ||||||
|   | |||||||
| @@ -28,6 +28,11 @@ struct DataPacket { | |||||||
|   uint8_t checksum; |   uint8_t checksum; | ||||||
| } __attribute__((packed)); | } __attribute__((packed)); | ||||||
|  |  | ||||||
|  | enum LineFrequency : uint8_t { | ||||||
|  |   LINE_FREQUENCY_50HZ = 50, | ||||||
|  |   LINE_FREQUENCY_60HZ = 60, | ||||||
|  | }; | ||||||
|  |  | ||||||
| class BL0942 : public PollingComponent, public uart::UARTDevice { | class BL0942 : public PollingComponent, public uart::UARTDevice { | ||||||
|  public: |  public: | ||||||
|   void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; } |   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_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; } | ||||||
|   void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_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_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 loop() override; | ||||||
|  |  | ||||||
|   void update() override; |   void update() override; | ||||||
|   void setup() override; |   void setup() override; | ||||||
|   void dump_config() override; |   void dump_config() override; | ||||||
| @@ -59,9 +65,12 @@ class BL0942 : public PollingComponent, public uart::UARTDevice { | |||||||
|   float current_reference_ = BL0942_IREF; |   float current_reference_ = BL0942_IREF; | ||||||
|   // Divide by this to turn into kWh |   // Divide by this to turn into kWh | ||||||
|   float energy_reference_ = BL0942_EREF; |   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); |   void received_package_(DataPacket *data); | ||||||
| }; | }; | ||||||
| }  // namespace bl0942 | }  // namespace bl0942 | ||||||
|   | |||||||
| @@ -1,25 +1,27 @@ | |||||||
| import esphome.codegen as cg | import esphome.codegen as cg | ||||||
| import esphome.config_validation as cv |  | ||||||
| from esphome.components import sensor, uart | from esphome.components import sensor, uart | ||||||
|  | import esphome.config_validation as cv | ||||||
| from esphome.const import ( | from esphome.const import ( | ||||||
|  |     CONF_ADDRESS, | ||||||
|     CONF_CURRENT, |     CONF_CURRENT, | ||||||
|     CONF_ENERGY, |     CONF_ENERGY, | ||||||
|  |     CONF_FREQUENCY, | ||||||
|     CONF_ID, |     CONF_ID, | ||||||
|  |     CONF_LINE_FREQUENCY, | ||||||
|     CONF_POWER, |     CONF_POWER, | ||||||
|     CONF_VOLTAGE, |     CONF_VOLTAGE, | ||||||
|     CONF_FREQUENCY, |  | ||||||
|     DEVICE_CLASS_CURRENT, |     DEVICE_CLASS_CURRENT, | ||||||
|     DEVICE_CLASS_ENERGY, |     DEVICE_CLASS_ENERGY, | ||||||
|  |     DEVICE_CLASS_FREQUENCY, | ||||||
|     DEVICE_CLASS_POWER, |     DEVICE_CLASS_POWER, | ||||||
|     DEVICE_CLASS_VOLTAGE, |     DEVICE_CLASS_VOLTAGE, | ||||||
|     DEVICE_CLASS_FREQUENCY, |  | ||||||
|     STATE_CLASS_MEASUREMENT, |     STATE_CLASS_MEASUREMENT, | ||||||
|  |     STATE_CLASS_TOTAL_INCREASING, | ||||||
|     UNIT_AMPERE, |     UNIT_AMPERE, | ||||||
|  |     UNIT_HERTZ, | ||||||
|     UNIT_KILOWATT_HOURS, |     UNIT_KILOWATT_HOURS, | ||||||
|     UNIT_VOLT, |     UNIT_VOLT, | ||||||
|     UNIT_WATT, |     UNIT_WATT, | ||||||
|     UNIT_HERTZ, |  | ||||||
|     STATE_CLASS_TOTAL_INCREASING, |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| DEPENDENCIES = ["uart"] | DEPENDENCIES = ["uart"] | ||||||
| @@ -27,6 +29,12 @@ DEPENDENCIES = ["uart"] | |||||||
| bl0942_ns = cg.esphome_ns.namespace("bl0942") | bl0942_ns = cg.esphome_ns.namespace("bl0942") | ||||||
| BL0942 = bl0942_ns.class_("BL0942", cg.PollingComponent, uart.UARTDevice) | 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 = ( | CONFIG_SCHEMA = ( | ||||||
|     cv.Schema( |     cv.Schema( | ||||||
|         { |         { | ||||||
| @@ -61,6 +69,14 @@ CONFIG_SCHEMA = ( | |||||||
|                 device_class=DEVICE_CLASS_FREQUENCY, |                 device_class=DEVICE_CLASS_FREQUENCY, | ||||||
|                 state_class=STATE_CLASS_MEASUREMENT, |                 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")) |     .extend(cv.polling_component_schema("60s")) | ||||||
| @@ -88,3 +104,5 @@ async def to_code(config): | |||||||
|     if frequency_config := config.get(CONF_FREQUENCY): |     if frequency_config := config.get(CONF_FREQUENCY): | ||||||
|         sens = await sensor.new_sensor(frequency_config) |         sens = await sensor.new_sensor(frequency_config) | ||||||
|         cg.add(var.set_frequency_sensor(sens)) |         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