Support for BL0910 10 channel current sensor

This commit is contained in:
Jimmy Wennlund 2024-09-03 10:37:07 +02:00
parent 3a7aabb2eb
commit 74a1ffe8ce
5 changed files with 671 additions and 0 deletions

View file

@ -59,6 +59,7 @@ esphome/components/bh1750/* @OttoWinter
esphome/components/binary_sensor/* @esphome/core
esphome/components/bk72xx/* @kuba2k2
esphome/components/bl0906/* @athom-tech @jesserockz @tarontop
esphome/components/bl0910/* @jimmyw
esphome/components/bl0939/* @ziceva
esphome/components/bl0940/* @tobias-
esphome/components/bl0942/* @dbuezas

View file

@ -0,0 +1 @@
CODEOWNERS = ["@jimmyw"]

View file

@ -0,0 +1,455 @@
#include "bl0910.h"
#include "esphome/core/log.h"
#include <cinttypes>
namespace esphome {
namespace bl0910 {
static const char *const TAG = "bl0910";
// Credit to JustNoot for the following constants
// https://github.com/JustNoot/10CH-Energy-Meter/blob/main/bl0910_esp32_test.ino
// https://www.compel.ru/item-pdf/5ba3245acc89f7213ea0cc7e9a0b8c0e/pn/cn-bell~bl0910.pdf
static const uint8_t BL0910_READ_COMMAND = 0x82;
static const uint8_t BL0910_WRITE_COMMAND = 0x81;
// current and voltage waveform data, 24bit signed number, default: 0x000000
static const uint8_t BL0910_REG_WAVE_1 = 0x01;
static const uint8_t BL0910_REG_WAVE_2 = 0x02;
static const uint8_t BL0910_REG_WAVE_3 = 0x03;
static const uint8_t BL0910_REG_WAVE_4 = 0x04;
static const uint8_t BL0910_REG_WAVE_5 = 0x05;
static const uint8_t BL0910_REG_WAVE_6 = 0x06;
static const uint8_t BL0910_REG_WAVE_7 = 0x07;
static const uint8_t BL0910_REG_WAVE_8 = 0x08;
static const uint8_t BL0910_REG_WAVE_9 = 0x09;
static const uint8_t BL0910_REG_WAVE_10 = 0x0A;
static const uint8_t BL0910_REG_WAVE_11 = 0x0B;
// effective value calculation register, 24 bit unsigned number, default: 0x000000
static const uint8_t BL0910_REG_RMS_1 = 0x0C;
static const uint8_t BL0910_REG_RMS_2 = 0x0D;
static const uint8_t BL0910_REG_RMS_3 = 0x0E;
static const uint8_t BL0910_REG_RMS_4 = 0x0F;
static const uint8_t BL0910_REG_RMS_5 = 0x10;
static const uint8_t BL0910_REG_RMS_6 = 0x11;
static const uint8_t BL0910_REG_RMS_7 = 0x12;
static const uint8_t BL0910_REG_RMS_8 = 0x13;
static const uint8_t BL0910_REG_RMS_9 = 0x14;
static const uint8_t BL0910_REG_RMS_10 = 0x15;
static const uint8_t BL0910_REG_RMS_11 = 0x16;
static const uint8_t BL0910_REG_RMS[] = {BL0910_REG_RMS_1, BL0910_REG_RMS_2, BL0910_REG_RMS_3, BL0910_REG_RMS_4,
BL0910_REG_RMS_5, BL0910_REG_RMS_6, BL0910_REG_RMS_7, BL0910_REG_RMS_8,
BL0910_REG_RMS_9, BL0910_REG_RMS_10, BL0910_REG_RMS_11};
// fast effective value register, 24 bit unsigned number, default: 0x000000
static const uint8_t BL0910_REG_FAST_RMS_1 = 0x17;
static const uint8_t BL0910_REG_FAST_RMS_2 = 0x18;
static const uint8_t BL0910_REG_FAST_RMS_3 = 0x19;
static const uint8_t BL0910_REG_FAST_RMS_4 = 0x1A;
static const uint8_t BL0910_REG_FAST_RMS_5 = 0x1B;
static const uint8_t BL0910_REG_FAST_RMS_6 = 0x1C;
static const uint8_t BL0910_REG_FAST_RMS_7 = 0x1D;
static const uint8_t BL0910_REG_FAST_RMS_8 = 0x1E;
static const uint8_t BL0910_REG_FAST_RMS_9 = 0x1F;
static const uint8_t BL0910_REG_FAST_RMS_10 = 0x20;
static const uint8_t BL0910_REG_FAST_RMS_11 = 0x21;
// active power, 24 bit signed number, default 0x000000
static const uint8_t BL0910_REG_WATT_1 = 0x22;
static const uint8_t BL0910_REG_WATT_2 = 0x23;
static const uint8_t BL0910_REG_WATT_3 = 0x24;
static const uint8_t BL0910_REG_WATT_4 = 0x25;
static const uint8_t BL0910_REG_WATT_5 = 0x26;
static const uint8_t BL0910_REG_WATT_6 = 0x27;
static const uint8_t BL0910_REG_WATT_7 = 0x28;
static const uint8_t BL0910_REG_WATT_8 = 0x29;
static const uint8_t BL0910_REG_WATT_9 = 0x2A;
static const uint8_t BL0910_REG_WATT_10 = 0x2B;
static const uint8_t BL0910_REG_WATT[] = {BL0910_REG_WATT_1, BL0910_REG_WATT_2, BL0910_REG_WATT_3, BL0910_REG_WATT_4,
BL0910_REG_WATT_5, BL0910_REG_WATT_6, BL0910_REG_WATT_7, BL0910_REG_WATT_8,
BL0910_REG_WATT_9, BL0910_REG_WATT_10};
static const uint8_t BL0910_REG_WATT_TOTAL = 0x2C;
static const uint8_t BL0910_REG_FVAR = 0x2D;
static const uint8_t BL0910_REG_VA = 0x2E;
// active energy pulse count register, 24 bit unsigned number, default: 0x000000
static const uint8_t BL0910_REG_CF_CNT_1 = 0x2F;
static const uint8_t BL0910_REG_CF_CNT_2 = 0x30;
static const uint8_t BL0910_REG_CF_CNT_3 = 0x31;
static const uint8_t BL0910_REG_CF_CNT_4 = 0x32;
static const uint8_t BL0910_REG_CF_CNT_5 = 0x33;
static const uint8_t BL0910_REG_CF_CNT_6 = 0x34;
static const uint8_t BL0910_REG_CF_CNT_7 = 0x35;
static const uint8_t BL0910_REG_CF_CNT_8 = 0x36;
static const uint8_t BL0910_REG_CF_CNT_9 = 0x37;
static const uint8_t BL0910_REG_CF_CNT_10 = 0x38;
static const uint8_t BL0910_REG_CF_CNT[] = {
BL0910_REG_CF_CNT_1, BL0910_REG_CF_CNT_2, BL0910_REG_CF_CNT_3, BL0910_REG_CF_CNT_4, BL0910_REG_CF_CNT_5,
BL0910_REG_CF_CNT_6, BL0910_REG_CF_CNT_7, BL0910_REG_CF_CNT_8, BL0910_REG_CF_CNT_9, BL0910_REG_CF_CNT_10};
static const uint8_t BL0910_REG_CF_CNT_TOTAL = 0x39;
static const uint8_t BL0910_REG_CFQ_CNT_TOTAL = 0x3A;
static const uint8_t BL0910_REG_CFS_CNT_TOTAL = 0x3B;
// phase angle measurement register, 16 bit unsigned number, default: 0x0000
static const uint8_t BL0910_REG_ANGLE_1 = 0x3C;
static const uint8_t BL0910_REG_ANGLE_2 = 0x3D;
static const uint8_t BL0910_REG_ANGLE_3 = 0x3E;
static const uint8_t BL0910_REG_ANGLE_4 = 0x3F;
static const uint8_t BL0910_REG_ANGLE_5 = 0x40;
static const uint8_t BL0910_REG_ANGLE_6 = 0x41;
static const uint8_t BL0910_REG_ANGLE_7 = 0x42;
static const uint8_t BL0910_REG_ANGLE_8 = 0x43;
static const uint8_t BL0910_REG_ANGLE_9 = 0x44;
static const uint8_t BL0910_REG_ANGLE_10 = 0x45;
static const uint8_t BL0910_REG_ANGLE[] = {
BL0910_REG_ANGLE_1, BL0910_REG_ANGLE_2, BL0910_REG_ANGLE_3, BL0910_REG_ANGLE_4, BL0910_REG_ANGLE_5,
BL0910_REG_ANGLE_6, BL0910_REG_ANGLE_7, BL0910_REG_ANGLE_8, BL0910_REG_ANGLE_9, BL0910_REG_ANGLE_10};
static const uint8_t BL0910_REG_FAST_RMS_H_1 = 0x46;
static const uint8_t BL0910_REG_FAST_RMS_H_2 = 0x47;
static const uint8_t BL0910_REG_FAST_RMS_H_3 = 0x48;
static const uint8_t BL0910_REG_FAST_RMS_H_4 = 0x49;
static const uint8_t BL0910_REG_FAST_RMS_H_5 = 0x57;
static const uint8_t BL0910_REG_FAST_RMS_H_6 = 0x58;
static const uint8_t BL0910_REG_FAST_RMS_H_7 = 0x59;
static const uint8_t BL0910_REG_FAST_RMS_H_8 = 0x5A;
static const uint8_t BL0910_REG_FAST_RMS_H_9 = 0x5B;
static const uint8_t BL0910_REG_FAST_RMS_H_10 = 0x5C;
static const uint8_t BL0910_REG_PF = 0x4A;
static const uint8_t BL0910_REG_LINE_WATTHR = 0x4B;
static const uint8_t BL0910_REG_LINE_VARH = 0x4C;
static const uint8_t BL0910_REG_SIGN = 0x4D;
static const uint8_t BL0910_REG_PERIOD = 0x4E;
static const uint8_t BL0910_REG_STATUS1 = 0x54;
static const uint8_t BL0910_REG_STATUS3 = 0x56;
// temperature measurement register, 24 bit unsigned numver, default: 0x000000
static const uint8_t BL0910_REG_TPS1 = 0x5E; // internal temperature, (TPS1-64)*12.5/59-40 ˚C
static const uint8_t BL0910_REG_TPS2 = 0x5F; // VT pin ADC value
// PGA gain adjustment register, 24 bit (4 bits per channel), default: 0x000000
// 4 PGA settings: 0000 = 1; 0001 = 2; 0010 = 8; 0011 = 16
// [3:0] voltage channel, current channel 6
// [7:4] current channel 1, current channel 7
// [11:8] current channel 2, current channel 8
// [15:12] current channel 3, current channel 9
// [19:16] current channel 4, current channel 10
// [23:20] current channel 5
static const uint8_t BL0910_REG_GAIN1 = 0x60;
static const uint8_t BL0910_REG_GAIN2 = 0x61;
// channel phase compensation register, 16 bit (8 bit per channel), default: 0x0000
static const uint8_t BL0910_REG_PHASE_1_2 = 0x64;
static const uint8_t BL0910_REG_PHASE_3_4 = 0x65;
static const uint8_t BL0910_REG_PHASE_5_6 = 0x66;
static const uint8_t BL0910_REG_PHASE_7_8 = 0x67;
static const uint8_t BL0910_REG_PHASE_9_10 = 0x68;
static const uint8_t BL0910_REG_PHASE_11 = 0x69; // 8 bit, default: 0x00
// phase compensation register, 5 bit number, default: 0000H
static const uint8_t BL0910_REG_VAR_PHCAL_I = 0x6A;
static const uint8_t BL0910_REG_VAR_PHCAL_V = 0x6B;
static const uint8_t BL0910_REG_RMS_GAIN_1 = 0x6C;
static const uint8_t BL0910_REG_RMS_GAIN_2 = 0x6D;
static const uint8_t BL0910_REG_RMS_GAIN_3 = 0x6E;
static const uint8_t BL0910_REG_RMS_GAIN_4 = 0x6F;
static const uint8_t BL0910_REG_RMS_GAIN_5 = 0x70;
static const uint8_t BL0910_REG_RMS_GAIN_6 = 0x71;
static const uint8_t BL0910_REG_RMS_GAIN_7 = 0x72;
static const uint8_t BL0910_REG_RMS_GAIN_8 = 0x73;
static const uint8_t BL0910_REG_RMS_GAIN_9 = 0x74;
static const uint8_t BL0910_REG_RMS_GAIN_10 = 0x75;
static const uint8_t BL0910_REG_RMS_GAIN_11 = 0x76;
static const uint8_t BL0910_REG_RMS_OFFSET_1 = 0x77;
static const uint8_t BL0910_REG_RMS_OFFSET_2 = 0x78;
static const uint8_t BL0910_REG_RMS_OFFSET_3 = 0x79;
static const uint8_t BL0910_REG_RMS_OFFSET_4 = 0x7A;
static const uint8_t BL0910_REG_RMS_OFFSET_5 = 0x7B;
static const uint8_t BL0910_REG_RMS_OFFSET_6 = 0x7C;
static const uint8_t BL0910_REG_RMS_OFFSET_7 = 0x7D;
static const uint8_t BL0910_REG_RMS_OFFSET_8 = 0x7E;
static const uint8_t BL0910_REG_RMS_OFFSET_9 = 0x7F;
static const uint8_t BL0910_REG_RMS_OFFSET_10 = 0x80;
static const uint8_t BL0910_REG_RMS_OFFSET_11 = 0x81;
// active power small signal compensation register, 12 bit two's complement (register is 24 bit), default: 0x000
static const uint8_t BL0910_REG_WA_LOW_OFFSET_1_2 = 0x82;
static const uint8_t BL0910_REG_WA_LOW_OFFSET_3_4 = 0x83;
static const uint8_t BL0910_REG_WA_LOW_OFFSET_5_6 = 0x84;
static const uint8_t BL0910_REG_WA_LOW_OFFSET_7_8 = 0x85;
static const uint8_t BL0910_REG_WA_LOW_OFFSET_9_10 = 0x86;
static const uint8_t BL0910_REG_FVAR_LOW_OFFSET = 0x87;
// [11:0] active power anti-creep threshold register, 12 bit unsigned number, default: 0x04C
// [23:12] reactive power anti-creep threshold register, 12 bit unsigned number, default: 0x04C
// 24 bit unsigned number, default: 0x04C04C
static const uint8_t BL0910_REG_VAR_CREEP_WA_CREEP = 0x88;
// total power anti-creep threshold register, 12 bit unsigned number, default: 0x000
static const uint8_t BL0910_REG_WA_CREEP2 = 0x89;
// effective value anti-creep threshold register, 12 bit unsigned number, default: 0x200
static const uint8_t BL0910_REG_RMS_CREEP = 0x8A;
static const uint8_t BL0910_REG_FAST_RMS_CTRL = 0x8B;
// peak threshold register, 24 bit number, deafult: 0xFFFFFF
// [11:0] voltage peak threshold, default: 0xFFF
// [23:12] current peak threshold, default: 0xFFF
static const uint8_t BL0910_REG_I_PKLVL_V_PKLVL = 0x8C;
static const uint8_t BL0910_REG_SAGCYC_ZXTOUT = 0x8E;
static const uint8_t BL0910_REG_SAGLVL_LINECYC = 0x8F;
static const uint8_t BL0910_REG_FLAG_CTRL = 0x90;
static const uint8_t BL0910_REG_FLAG_CTRL1 = 0x91;
static const uint8_t BL0910_REG_FLAG_CTRL2 = 0x92;
// ADC shutdown register, 11 bit number, default: 0x000
static const uint8_t BL0910_REG_ADC_PD = 0x93;
// temperature control register, 16 bit number, default: 0x07FF
static const uint8_t BL0910_REG_TPS_CTRL = 0x94;
// external temperature coefficient register, 16 bit number, default: 0x0000
static const uint8_t BL0910_REG_TPS2_A_B = 0x95;
static const uint8_t BL0910_REG_MODE1 = 0x96;
static const uint8_t BL0910_REG_MODE2 = 0x97;
static const uint8_t BL0910_REG_MODE = 0x98;
static const uint8_t BL0910_REG_MASK1 = 0x9A;
static const uint8_t BL0910_REG_RST_ENG = 0x9D;
static const uint8_t BL0910_REG_USR_WRPROT = 0x9E;
static const uint8_t BL0910_REG_SOFT_RESET = 0x9F;
// channel gain calibration register, 16 bit two's complement, default: 0x0000
static const uint8_t BL0910_REG_CHANNEL_GAIN_1 = 0xA0;
static const uint8_t BL0910_REG_CHANNEL_GAIN_2 = 0xA1;
static const uint8_t BL0910_REG_CHANNEL_GAIN_3 = 0xA2;
static const uint8_t BL0910_REG_CHANNEL_GAIN_4 = 0xA3;
static const uint8_t BL0910_REG_CHANNEL_GAIN_5 = 0xA4;
static const uint8_t BL0910_REG_CHANNEL_GAIN_6 = 0xA5;
static const uint8_t BL0910_REG_CHANNEL_GAIN_7 = 0xA6;
static const uint8_t BL0910_REG_CHANNEL_GAIN_8 = 0xA7;
static const uint8_t BL0910_REG_CHANNEL_GAIN_9 = 0xA8;
static const uint8_t BL0910_REG_CHANNEL_GAIN_10 = 0xA9;
static const uint8_t BL0910_REG_CHANNEL_GAIN_11 = 0xAA;
// channel offset calibration register, 16 bit two's complement, default: 0x0000
static const uint8_t BL0910_REG_CHANNEL_OFFSET_1 = 0xAB;
static const uint8_t BL0910_REG_CHANNEL_OFFSET_2 = 0xAC;
static const uint8_t BL0910_REG_CHANNEL_OFFSET_3 = 0xAD;
static const uint8_t BL0910_REG_CHANNEL_OFFSET_4 = 0xAE;
static const uint8_t BL0910_REG_CHANNEL_OFFSET_5 = 0xAF;
static const uint8_t BL0910_REG_CHANNEL_OFFSET_6 = 0xB0;
static const uint8_t BL0910_REG_CHANNEL_OFFSET_7 = 0xB1;
static const uint8_t BL0910_REG_CHANNEL_OFFSET_8 = 0xB2;
static const uint8_t BL0910_REG_CHANNEL_OFFSET_9 = 0xB3;
static const uint8_t BL0910_REG_CHANNEL_OFFSET_10 = 0xB4;
static const uint8_t BL0910_REG_CHANNEL_OFFSET_11 = 0xB5;
// active power gain correction register, 16bit signed number, default: 0x0000
static const uint8_t BL0910_REG_WATT_GAIN_1 = 0xB6;
static const uint8_t BL0910_REG_WATT_GAIN_2 = 0xB7;
static const uint8_t BL0910_REG_WATT_GAIN_3 = 0xB8;
static const uint8_t BL0910_REG_WATT_GAIN_4 = 0xB9;
static const uint8_t BL0910_REG_WATT_GAIN_5 = 0xBA;
static const uint8_t BL0910_REG_WATT_GAIN_6 = 0xBB;
static const uint8_t BL0910_REG_WATT_GAIN_7 = 0xBC;
static const uint8_t BL0910_REG_WATT_GAIN_8 = 0xBD;
static const uint8_t BL0910_REG_WATT_GAIN_9 = 0xBE;
static const uint8_t BL0910_REG_WATT_GAIN_10 = 0xBF;
// active power offset correction register, 16bit signed number, default: 0x0000
static const uint8_t BL0910_REG_WATT_OFFSET_1 = 0xC0;
static const uint8_t BL0910_REG_WATT_OFFSET_2 = 0xC1;
static const uint8_t BL0910_REG_WATT_OFFSET_3 = 0xC2;
static const uint8_t BL0910_REG_WATT_OFFSET_4 = 0xC3;
static const uint8_t BL0910_REG_WATT_OFFSET_5 = 0xC4;
static const uint8_t BL0910_REG_WATT_OFFSET_6 = 0xC5;
static const uint8_t BL0910_REG_WATT_OFFSET_7 = 0xC6;
static const uint8_t BL0910_REG_WATT_OFFSET_8 = 0xC7;
static const uint8_t BL0910_REG_WATT_OFFSET_9 = 0xC8;
static const uint8_t BL0910_REG_WATT_OFFSET_10 = 0xC9;
static const uint8_t BL0910_REG_VAR_GAIN = 0xCA;
static const uint8_t BL0910_REG_VAR_OFFSET = 0xCB;
static const uint8_t BL0910_REG_VA_GAIN = 0xCC;
static const uint8_t BL0910_REG_VA_OFFSET = 0xCD;
static const uint8_t BL0910_REG_CFDIV = 0xCE;
static const uint8_t BL0910_REG_OTP_CHECKSUM1 = 0xD0;
int8_t BL0910::checksum_calc(uint8_t *data) {
uint8_t checksum = 0;
for (int i = 0; i < 5; i++) {
checksum += data[i];
}
checksum &= 0xFF;
checksum ^= 0xFF;
return checksum;
}
void BL0910::write_register(uint8_t addr, uint8_t data_h, uint8_t data_m, uint8_t data_l) {
uint8_t packet[6] = {0};
packet[0] = BL0910_WRITE_COMMAND;
packet[1] = addr;
packet[2] = data_h;
packet[3] = data_m;
packet[4] = data_l;
packet[5] = checksum_calc(packet);
this->enable();
this->transfer_array(packet, sizeof(packet) / sizeof(packet[0]));
this->disable();
}
int32_t BL0910::read_register(uint8_t addr) {
int32_t data = 0;
uint8_t packet[6] = {0};
packet[0] = BL0910_READ_COMMAND;
packet[1] = addr;
this->enable();
this->transfer_array(packet, sizeof(packet) / sizeof(packet[0]));
this->disable();
// Verify checksum
uint8_t checksum = BL0910_READ_COMMAND + addr;
for (int i = 2; i < 5; i++) {
checksum += packet[i];
}
checksum &= 0xFF;
checksum ^= 0xFF;
if (checksum != packet[5]) // checksum is byte 6
{
ESP_LOGE(TAG, "Checksum error calculated: %x != read: %x", checksum, packet[5]);
return 0xFF000000;
}
return (((int8_t) packet[2]) << 16) | (packet[3] << 8) | packet[4];
}
float BL0910::getVoltage(uint8_t channel) {
return ((float) read_register(BL0910_REG_RMS[channel])) / this->uref[channel];
}
float BL0910::getCurrent(uint8_t channel) {
return ((float) read_register(BL0910_REG_RMS[channel])) / this->iref[channel];
}
float BL0910::getPower(uint8_t channel) {
return ((float) read_register(BL0910_REG_WATT[channel])) / this->pref[channel];
}
float BL0910::getEnergy(uint8_t channel) {
return ((float) read_register(BL0910_REG_CF_CNT[channel])) / this->eref[channel];
}
float BL0910::getFreq(void) {
const float freq = (float) read_register(BL0910_REG_PERIOD);
return 10000000.0 / freq;
}
float BL0910::getTemperature(void) {
const float temp = (float) read_register(BL0910_REG_TPS1);
return (temp - 64.0) * 12.5 / 59.0 - 40.0;
}
float BL0910::getPowerFactor(uint8_t channel, float freq) {
const float angle = (float) read_register(BL0910_REG_ANGLE[channel]);
return (360.0f * angle * freq) / 500000.0f;
}
void BL0910::loop() {}
void BL0910::update() {
static int i = 0;
static float freq = 50.0;
if (i < NUM_CHANNELS) {
if (voltage_sensor[i])
voltage_sensor[i]->publish_state(getVoltage(i));
if (current_sensor[i])
current_sensor[i]->publish_state(getCurrent(i));
if (power_sensor[i])
power_sensor[i]->publish_state(getPower(i));
if (energy_sensor[i])
energy_sensor[i]->publish_state(getEnergy(i));
if (power_factor_sensor[i])
power_factor_sensor[i]->publish_state(getPowerFactor(i, freq));
i++;
} else {
freq = getFreq();
if (frequency_sensor)
frequency_sensor->publish_state(freq);
if (temperature_sensor)
temperature_sensor->publish_state(getTemperature());
i = 0;
}
}
void BL0910::setup() {
this->spi_setup();
this->write_register(BL0910_REG_SOFT_RESET, 0x5A, 0x5A, 0x5A); // Reset to default
this->write_register(BL0910_REG_USR_WRPROT, 0x00, 0x55, 0x55); // Enable User Operation Write
this->write_register(BL0910_REG_MODE, 0x00, 0x00, 0x00);
this->write_register(BL0910_REG_TPS_CTRL, 0x00, 0x07, 0xFF);
this->write_register(BL0910_REG_FAST_RMS_CTRL, 0x40, 0xFF, 0xFF);
this->write_register(BL0910_REG_GAIN1, 0x00, 0x00, 0x00);
}
void BL0910::dump_config() { // NOLINT(readability-function-cognitive-complexity)
ESP_LOGCONFIG(TAG, "BL0910:");
for (int i = 0; i < NUM_CHANNELS; i++) {
if (voltage_sensor[i]) {
char buf[20];
snprintf(buf, sizeof(buf), "Voltage %d", i + 1);
LOG_SENSOR("", buf, voltage_sensor[i]);
}
if (current_sensor[i]) {
char buf[20];
snprintf(buf, sizeof(buf), "Current %d", i + 1);
LOG_SENSOR("", buf, current_sensor[i]);
}
if (power_sensor[i]) {
char buf[20];
snprintf(buf, sizeof(buf), "Power %d", i + 1);
LOG_SENSOR("", buf, power_sensor[i]);
}
if (energy_sensor[i]) {
char buf[20];
snprintf(buf, sizeof(buf), "Energy %d", i + 1);
LOG_SENSOR("", buf, energy_sensor[i]);
}
if (power_factor_sensor[i]) {
char buf[20];
snprintf(buf, sizeof(buf), "Power Factor %d", i + 1);
LOG_SENSOR("", buf, power_factor_sensor[i]);
}
}
if (this->frequency_sensor) {
LOG_SENSOR("", "Frequency", this->frequency_sensor);
}
if (this->temperature_sensor) {
LOG_SENSOR("", "Temperature", this->temperature_sensor);
}
}
} // namespace bl0910
} // namespace esphome

View file

@ -0,0 +1,79 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/spi/spi.h"
#include "esphome/components/sensor/sensor.h"
namespace esphome {
namespace bl0910 {
// Mode 1 Trailing clock CPHA=1, Polarity low CPOL=0
#define NUM_CHANNELS 11
class BL0910 : public PollingComponent,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_TRAILING,
spi::DATA_RATE_1MHZ> {
public:
void set_voltage_sensor(sensor::Sensor *voltage_sensor_, int index, float uref_) {
voltage_sensor[index - 1] = voltage_sensor_;
uref[index - 1] = uref_;
}
void set_current_sensor(sensor::Sensor *current_sensor_, int index, float iref_) {
current_sensor[index - 1] = current_sensor_;
iref[index - 1] = iref_;
}
void set_power_sensor(sensor::Sensor *power_sensor_, int index, float pref_) {
power_sensor[index - 1] = power_sensor_;
pref[index - 1] = pref_;
}
void set_energy_sensor(sensor::Sensor *energy_sensor_, int index, float eref_) {
energy_sensor[index - 1] = energy_sensor_;
eref[index - 1] = eref_;
}
void set_power_factor_sensor(sensor::Sensor *power_factor_sensor_, int index) {
power_factor_sensor[index - 1] = power_factor_sensor_;
}
void set_frequency_sensor(sensor::Sensor *frequency_sensor_) { frequency_sensor = frequency_sensor_; }
void set_temperature_sensor(sensor::Sensor *temperature_sensor_) { temperature_sensor = temperature_sensor_; }
void loop() override;
void update() override;
void setup() override;
void dump_config() override;
protected:
sensor::Sensor *voltage_sensor[NUM_CHANNELS] = {};
sensor::Sensor *current_sensor[NUM_CHANNELS] = {};
// NB This may be negative as the circuits is seemingly able to measure
// power in both directions
sensor::Sensor *power_sensor[NUM_CHANNELS] = {};
sensor::Sensor *energy_sensor[NUM_CHANNELS] = {};
sensor::Sensor *power_factor_sensor[NUM_CHANNELS] = {};
sensor::Sensor *frequency_sensor{nullptr};
sensor::Sensor *temperature_sensor{nullptr};
float uref[NUM_CHANNELS] = {};
float iref[NUM_CHANNELS] = {};
float pref[NUM_CHANNELS] = {};
float eref[NUM_CHANNELS] = {};
private:
int8_t checksum_calc(uint8_t *data);
void write_register(uint8_t addr, uint32_t data) {
return this->write_register(addr, (data >> 16) & 0xFF, (data >> 8) & 0xFF, data & 0xFF);
}
void write_register(uint8_t addr, uint8_t data_h, uint8_t data_m, uint8_t data_l);
int32_t read_register(uint8_t addr);
float getVoltage(uint8_t channel);
float getFreq(void);
float getCurrent(uint8_t channel);
float getPower(uint8_t channel);
float getEnergy(uint8_t channel);
float getTemperature(void);
float getPowerFactor(uint8_t channel, float freq);
};
} // namespace bl0910
} // namespace esphome

View file

@ -0,0 +1,135 @@
import esphome.codegen as cg
from esphome.components import sensor, spi
import esphome.config_validation as cv
from esphome.const import (
CONF_FREQUENCY,
CONF_ID,
CONF_TEMPERATURE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_FREQUENCY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_POWER_FACTOR,
DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_TOTAL_INCREASING,
UNIT_AMPERE,
UNIT_DEGREES,
UNIT_KILOWATT_HOURS,
UNIT_VOLT,
UNIT_WATT,
)
# 10 sensors in range 1-11
SENSOR_RANGE = range(1, 12)
DEPENDENCIES = ["spi"]
bl0910_ns = cg.esphome_ns.namespace("bl0910")
BL0910 = bl0910_ns.class_("BL0910", cg.PollingComponent, spi.SPIDevice)
VOLTAGE_CB = {
cv.Optional(f"voltage_{i}"): sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
).extend({cv.Optional("uref", default=1.0): cv.float_})
for i in SENSOR_RANGE
}
CURRENT_CV = {
cv.Optional(f"current_{i}"): sensor.sensor_schema(
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
).extend({cv.Optional("iref", default=1.0): cv.float_})
for i in SENSOR_RANGE
}
ACTIVE_POWER_CV = {
cv.Optional(f"active_power_{i}"): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
).extend({cv.Optional("pref", default=1.0): cv.float_})
for i in SENSOR_RANGE
}
ENERGY_CV = {
cv.Optional(f"energy_{i}"): sensor.sensor_schema(
unit_of_measurement=UNIT_KILOWATT_HOURS,
accuracy_decimals=3,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
).extend({cv.Optional("eref", default=1.0): cv.float_})
for i in SENSOR_RANGE
}
POWER_FACTOR_CV = {
cv.Optional(f"power_factor_{i}"): sensor.sensor_schema(
unit_of_measurement=UNIT_DEGREES,
accuracy_decimals=0,
device_class=DEVICE_CLASS_POWER_FACTOR,
state_class=STATE_CLASS_MEASUREMENT,
)
for i in SENSOR_RANGE
}
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(BL0910),
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
unit_of_measurement="Hz",
accuracy_decimals=1,
device_class=DEVICE_CLASS_FREQUENCY,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement="°C",
accuracy_decimals=1,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(spi.spi_device_schema())
.extend(VOLTAGE_CB)
.extend(CURRENT_CV)
.extend(ACTIVE_POWER_CV)
.extend(ENERGY_CV)
.extend(POWER_FACTOR_CV)
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await spi.register_spi_device(var, config)
if frequency_config := config.get(CONF_FREQUENCY):
sens = await sensor.new_sensor(frequency_config)
cg.add(var.set_frequency_sensor(sens))
if temperature_config := config.get(CONF_TEMPERATURE):
sens = await sensor.new_sensor(temperature_config)
cg.add(var.set_temperature_sensor(sens))
for i in SENSOR_RANGE:
if voltage_config := config.get(f"voltage_{i}"):
sens = await sensor.new_sensor(voltage_config)
cg.add(var.set_voltage_sensor(sens, i, voltage_config.get("uref")))
if current_config := config.get(f"current_{i}"):
sens = await sensor.new_sensor(current_config)
cg.add(var.set_current_sensor(sens, i, current_config.get("iref")))
if active_power_config := config.get(f"active_power_{i}"):
sens = await sensor.new_sensor(active_power_config)
cg.add(var.set_power_sensor(sens, i, active_power_config.get("pref")))
if energy_config := config.get(f"energy_{i}"):
sens = await sensor.new_sensor(energy_config)
cg.add(var.set_energy_sensor(sens, i, energy_config.get("eref")))
if power_factor_config := config.get(f"power_factor_{i}"):
sens = await sensor.new_sensor(power_factor_config)
cg.add(var.set_power_factor_sensor(sens, i))