DALY Modbus BMS: split data request into two requests

This commit is contained in:
patagona 2024-09-30 00:08:13 +02:00
parent 206562bebf
commit 2f6d552589
3 changed files with 140 additions and 90 deletions

View file

@ -13,6 +13,12 @@ static const char *const TAG = "daly_hkms_bms";
static const uint8_t DALY_MODBUS_REQUEST_ADDRESS_OFFSET = 0x80;
static const uint8_t DALY_MODBUS_RESPONSE_ADDRESS_OFFSET = 0x50;
static const uint8_t DALY_MODBUS_READ_CELL_VOLTAGES_ADDR = DALY_MODBUS_ADDR_CELL_VOLT_1;
static const uint8_t DALY_MODBUS_READ_CELL_VOLTAGES_LENGTH = 16; // 16 cell voltages
static const uint8_t DALY_MODBUS_READ_DATA_ADDR = DALY_MODBUS_ADDR_CELL_TEMP_1;
static const uint8_t DALY_MODBUS_READ_DATA_LENGTH = DALY_MODBUS_REGISTER_MAX - DALY_MODBUS_ADDR_CELL_TEMP_1 + 1;
static const uint8_t MODBUS_CMD_READ_HOLDING_REGISTERS = 0x03;
void DalyHkmsBmsComponent::set_daly_address(uint8_t daly_address) {
@ -25,13 +31,10 @@ void DalyHkmsBmsComponent::set_daly_address(uint8_t daly_address) {
}
void DalyHkmsBmsComponent::loop() {
// If update() was unable to send we retry until we can send.
if (!this->waiting_to_update_)
if (this->read_state_ == ReadState::IDLE) {
return;
update();
}
}
void DalyHkmsBmsComponent::update() {
// If our last send has had no reply yet, and it wasn't that long ago, do nothing.
uint32_t now = millis();
if (now - this->last_send_ < this->get_update_interval() / 2) {
@ -40,25 +43,43 @@ void DalyHkmsBmsComponent::update() {
// The bus might be slow, or there might be other devices, or other components might be talking to our device.
if (this->waiting_for_response()) {
this->waiting_to_update_ = true;
return;
}
this->waiting_to_update_ = false;
uint8_t start_address;
uint8_t register_count;
switch (this->read_state_)
{
case ReadState::READ_CELL_VOLTAGES:
start_address = DALY_MODBUS_READ_CELL_VOLTAGES_ADDR;
register_count = DALY_MODBUS_READ_CELL_VOLTAGES_LENGTH;
break;
case ReadState::READ_DATA:
start_address = DALY_MODBUS_READ_DATA_ADDR;
register_count = DALY_MODBUS_READ_DATA_LENGTH;
break;
default:
return;
}
ESP_LOGD(TAG, "Sending modbus read request to %d: start register %d, register count %d",
this->daly_address_, start_address, register_count);
// send the request using Modbus directly instead of ModbusDevice so we can send the data with the request address
uint8_t modbus_request_address = this->daly_address_ + DALY_MODBUS_REQUEST_ADDRESS_OFFSET;
this->parent_->send(
modbus_request_address,
MODBUS_CMD_READ_HOLDING_REGISTERS,
0x00,
DALY_MODBUS_REGISTER_COUNT,
0,
nullptr);
uint8_t modbus_device_request_address = this->daly_address_ + DALY_MODBUS_REQUEST_ADDRESS_OFFSET;
this->parent_->send(modbus_device_request_address, MODBUS_CMD_READ_HOLDING_REGISTERS, start_address, register_count,
0, nullptr);
this->last_send_ = millis();
}
void DalyHkmsBmsComponent::update() {
if(this->read_state_ == ReadState::IDLE){
this->read_state_ = ReadState::READ_CELL_VOLTAGES;
}
}
void DalyHkmsBmsComponent::on_modbus_data(const std::vector<uint8_t> &data) {
// Other components might be sending commands to our device. But we don't get called with enough
// context to know what is what. So if we didn't do a send, we ignore the data.
@ -68,18 +89,37 @@ void DalyHkmsBmsComponent::on_modbus_data(const std::vector<uint8_t> &data) {
}
this->last_send_ = 0;
// Also ignore the data if the message is too short. Otherwise we will publish invalid values.
if (data.size() < DALY_MODBUS_REGISTER_COUNT * 2) {
ESP_LOGD(TAG, "Got modbus response: %d bytes", data.size());
size_t register_count;
size_t register_offset;
switch (this->read_state_)
{
case ReadState::READ_CELL_VOLTAGES:
register_offset = DALY_MODBUS_READ_CELL_VOLTAGES_ADDR;
register_count = DALY_MODBUS_READ_CELL_VOLTAGES_LENGTH;
break;
case ReadState::READ_DATA:
register_offset = DALY_MODBUS_READ_DATA_ADDR;
register_count = DALY_MODBUS_READ_DATA_LENGTH;
break;
default:
ESP_LOGE(TAG, "Invalid read state");
return;
}
if (data.size() < register_count * 2) {
ESP_LOGD(TAG, "Not enough data in modbus response");
return;
}
auto get_register = [&](size_t i) -> uint16_t {
return encode_uint16(data[i * 2], data[i * 2 + 1]);
return encode_uint16(data[(i - register_offset) * 2], data[(i - register_offset) * 2 + 1]);
};
auto publish_sensor_state = [&](sensor::Sensor *sensor, size_t i, int16_t offset, float factor,
int32_t unavailable_value = -1) -> void {
auto publish_sensor_state = [&](sensor::Sensor *sensor, size_t i,
int16_t offset, float factor, int32_t unavailable_value = -1) -> void {
if (sensor == nullptr)
return;
uint16_t register_value = get_register(i);
@ -87,91 +127,99 @@ void DalyHkmsBmsComponent::on_modbus_data(const std::vector<uint8_t> &data) {
sensor->publish_state(value);
};
if (this->read_state_ == ReadState::READ_CELL_VOLTAGES) {
#ifdef USE_SENSOR
publish_sensor_state(this->cell_1_voltage_sensor_ , DALY_MODBUS_ADDR_CELL_VOLT_1 , 0, 0.001);
publish_sensor_state(this->cell_2_voltage_sensor_ , DALY_MODBUS_ADDR_CELL_VOLT_1 + 1 , 0, 0.001);
publish_sensor_state(this->cell_3_voltage_sensor_ , DALY_MODBUS_ADDR_CELL_VOLT_1 + 2 , 0, 0.001);
publish_sensor_state(this->cell_4_voltage_sensor_ , DALY_MODBUS_ADDR_CELL_VOLT_1 + 3 , 0, 0.001);
publish_sensor_state(this->cell_5_voltage_sensor_ , DALY_MODBUS_ADDR_CELL_VOLT_1 + 4 , 0, 0.001);
publish_sensor_state(this->cell_6_voltage_sensor_ , DALY_MODBUS_ADDR_CELL_VOLT_1 + 5 , 0, 0.001);
publish_sensor_state(this->cell_7_voltage_sensor_ , DALY_MODBUS_ADDR_CELL_VOLT_1 + 6 , 0, 0.001);
publish_sensor_state(this->cell_8_voltage_sensor_ , DALY_MODBUS_ADDR_CELL_VOLT_1 + 7 , 0, 0.001);
publish_sensor_state(this->cell_9_voltage_sensor_ , DALY_MODBUS_ADDR_CELL_VOLT_1 + 8 , 0, 0.001);
publish_sensor_state(this->cell_10_voltage_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_1 + 9 , 0, 0.001);
publish_sensor_state(this->cell_11_voltage_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_1 + 10, 0, 0.001);
publish_sensor_state(this->cell_12_voltage_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_1 + 11, 0, 0.001);
publish_sensor_state(this->cell_13_voltage_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_1 + 12, 0, 0.001);
publish_sensor_state(this->cell_14_voltage_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_1 + 13, 0, 0.001);
publish_sensor_state(this->cell_15_voltage_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_1 + 14, 0, 0.001);
publish_sensor_state(this->cell_16_voltage_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_1 + 15, 0, 0.001);
publish_sensor_state(this->cell_1_voltage_sensor_ , DALY_MODBUS_ADDR_CELL_VOLT_1 , 0, 0.001);
publish_sensor_state(this->cell_2_voltage_sensor_ , DALY_MODBUS_ADDR_CELL_VOLT_1 + 1 , 0, 0.001);
publish_sensor_state(this->cell_3_voltage_sensor_ , DALY_MODBUS_ADDR_CELL_VOLT_1 + 2 , 0, 0.001);
publish_sensor_state(this->cell_4_voltage_sensor_ , DALY_MODBUS_ADDR_CELL_VOLT_1 + 3 , 0, 0.001);
publish_sensor_state(this->cell_5_voltage_sensor_ , DALY_MODBUS_ADDR_CELL_VOLT_1 + 4 , 0, 0.001);
publish_sensor_state(this->cell_6_voltage_sensor_ , DALY_MODBUS_ADDR_CELL_VOLT_1 + 5 , 0, 0.001);
publish_sensor_state(this->cell_7_voltage_sensor_ , DALY_MODBUS_ADDR_CELL_VOLT_1 + 6 , 0, 0.001);
publish_sensor_state(this->cell_8_voltage_sensor_ , DALY_MODBUS_ADDR_CELL_VOLT_1 + 7 , 0, 0.001);
publish_sensor_state(this->cell_9_voltage_sensor_ , DALY_MODBUS_ADDR_CELL_VOLT_1 + 8 , 0, 0.001);
publish_sensor_state(this->cell_10_voltage_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_1 + 9 , 0, 0.001);
publish_sensor_state(this->cell_11_voltage_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_1 + 10, 0, 0.001);
publish_sensor_state(this->cell_12_voltage_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_1 + 11, 0, 0.001);
publish_sensor_state(this->cell_13_voltage_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_1 + 12, 0, 0.001);
publish_sensor_state(this->cell_14_voltage_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_1 + 13, 0, 0.001);
publish_sensor_state(this->cell_15_voltage_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_1 + 14, 0, 0.001);
publish_sensor_state(this->cell_16_voltage_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_1 + 15, 0, 0.001);
#endif
publish_sensor_state(this->temperature_1_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_1 , -40, 1, 255);
publish_sensor_state(this->temperature_2_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_1 + 1, -40, 1, 255);
publish_sensor_state(this->temperature_3_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_1 + 2, -40, 1, 255);
publish_sensor_state(this->temperature_4_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_1 + 3, -40, 1, 255);
publish_sensor_state(this->temperature_5_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_1 + 4, -40, 1, 255);
publish_sensor_state(this->temperature_6_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_1 + 5, -40, 1, 255);
publish_sensor_state(this->temperature_7_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_1 + 6, -40, 1, 255);
publish_sensor_state(this->temperature_8_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_1 + 7, -40, 1, 255);
this->read_state_ = ReadState::READ_DATA;
} else if (this->read_state_ == ReadState::READ_DATA) {
#ifdef USE_SENSOR
publish_sensor_state(this->temperature_1_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_1 , -40, 1, 255);
publish_sensor_state(this->temperature_2_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_1 + 1, -40, 1, 255);
publish_sensor_state(this->temperature_3_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_1 + 2, -40, 1, 255);
publish_sensor_state(this->temperature_4_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_1 + 3, -40, 1, 255);
publish_sensor_state(this->temperature_5_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_1 + 4, -40, 1, 255);
publish_sensor_state(this->temperature_6_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_1 + 5, -40, 1, 255);
publish_sensor_state(this->temperature_7_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_1 + 6, -40, 1, 255);
publish_sensor_state(this->temperature_8_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_1 + 7, -40, 1, 255);
publish_sensor_state(this->voltage_sensor_, DALY_MODBUS_ADDR_VOLT, 0, 0.1);
publish_sensor_state(this->current_sensor_, DALY_MODBUS_ADDR_CURR, -30000, 0.1);
publish_sensor_state(this->battery_level_sensor_, DALY_MODBUS_ADDR_SOC, 0, 0.1);
publish_sensor_state(this->voltage_sensor_, DALY_MODBUS_ADDR_VOLT, 0, 0.1);
publish_sensor_state(this->current_sensor_, DALY_MODBUS_ADDR_CURR, -30000, 0.1);
publish_sensor_state(this->battery_level_sensor_, DALY_MODBUS_ADDR_SOC, 0, 0.1);
publish_sensor_state(this->cells_number_sensor_, DALY_MODBUS_ADDR_CELL_COUNT, 0, 1);
publish_sensor_state(this->temps_number_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_COUNT, 0, 1);
publish_sensor_state(this->cells_number_sensor_, DALY_MODBUS_ADDR_CELL_COUNT, 0, 1);
publish_sensor_state(this->temps_number_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_COUNT, 0, 1);
publish_sensor_state(this->max_cell_voltage_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_MAX, 0, 0.001);
publish_sensor_state(this->max_cell_voltage_number_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_MAX_NUM, 0, 1);
publish_sensor_state(this->max_cell_voltage_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_MAX, 0, 0.001);
publish_sensor_state(this->max_cell_voltage_number_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_MAX_NUM, 0, 1);
publish_sensor_state(this->min_cell_voltage_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_MIN, 0, 0.001);
publish_sensor_state(this->min_cell_voltage_number_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_MIN_NUM, 0, 1);
publish_sensor_state(this->min_cell_voltage_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_MIN, 0, 0.001);
publish_sensor_state(this->min_cell_voltage_number_sensor_, DALY_MODBUS_ADDR_CELL_VOLT_MIN_NUM, 0, 1);
publish_sensor_state(this->max_temperature_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_MAX, -40, 1, 255);
publish_sensor_state(this->max_temperature_probe_number_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_MAX_NUM, 0, 1);
publish_sensor_state(this->max_temperature_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_MAX, -40, 1, 255);
publish_sensor_state(this->max_temperature_probe_number_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_MAX_NUM, 0, 1);
publish_sensor_state(this->min_temperature_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_MIN, -40, 1, 255);
publish_sensor_state(this->min_temperature_probe_number_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_MIN_NUM, 0, 1);
publish_sensor_state(this->min_temperature_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_MIN, -40, 1, 255);
publish_sensor_state(this->min_temperature_probe_number_sensor_, DALY_MODBUS_ADDR_CELL_TEMP_MIN_NUM, 0, 1);
publish_sensor_state(this->remaining_capacity_sensor_, DALY_MODBUS_ADDR_REMAINING_CAPACITY, 0, 0.1);
publish_sensor_state(this->cycles_sensor_, DALY_MODBUS_ADDR_CYCLES, 0, 1);
publish_sensor_state(this->remaining_capacity_sensor_, DALY_MODBUS_ADDR_REMAINING_CAPACITY, 0, 0.1);
publish_sensor_state(this->cycles_sensor_, DALY_MODBUS_ADDR_CYCLES, 0, 1);
publish_sensor_state(this->temperature_mos_sensor_, DALY_MODBUS_ADDR_MOS_TEMP, -40, 1, 255);
publish_sensor_state(this->temperature_board_sensor_, DALY_MODBUS_ADDR_BOARD_TEMP, -40, 1, 255);
publish_sensor_state(this->temperature_mos_sensor_, DALY_MODBUS_ADDR_MOS_TEMP, -40, 1, 255);
publish_sensor_state(this->temperature_board_sensor_, DALY_MODBUS_ADDR_BOARD_TEMP, -40, 1, 255);
#endif
#ifdef USE_TEXT_SENSOR
if (this->status_text_sensor_ != nullptr) {
switch (get_register(DALY_MODBUS_ADDR_CHG_DSCHG_STATUS)) {
case 0:
this->status_text_sensor_->publish_state("Stationary");
break;
case 1:
this->status_text_sensor_->publish_state("Charging");
break;
case 2:
this->status_text_sensor_->publish_state("Discharging");
break;
default:
break;
if (this->status_text_sensor_ != nullptr) {
switch (get_register(DALY_MODBUS_ADDR_CHG_DSCHG_STATUS)) {
case 0:
this->status_text_sensor_->publish_state("Stationary");
break;
case 1:
this->status_text_sensor_->publish_state("Charging");
break;
case 2:
this->status_text_sensor_->publish_state("Discharging");
break;
default:
break;
}
}
}
#endif
#ifdef USE_BINARY_SENSOR
if (this->balancing_active_binary_sensor_) {
this->balancing_active_binary_sensor_->publish_state(get_register(DALY_MODBUS_ADDR_BALANCE_STATUS) > 0);
}
if (this->charging_mos_enabled_binary_sensor_) {
this->charging_mos_enabled_binary_sensor_->publish_state(get_register(DALY_MODBUS_ADDR_CHG_MOS_ACTIVE) > 0);
}
if (this->discharging_mos_enabled_binary_sensor_) {
this->discharging_mos_enabled_binary_sensor_->publish_state(get_register(DALY_MODBUS_ADDR_DSCHG_MOS_ACTIVE) > 0);
}
if (this->precharging_mos_enabled_binary_sensor_) {
this->precharging_mos_enabled_binary_sensor_->publish_state(get_register(DALY_MODBUS_ADDR_PRECHG_MOS_ACTIVE) > 0);
}
if (this->balancing_active_binary_sensor_) {
this->balancing_active_binary_sensor_->publish_state(get_register(DALY_MODBUS_ADDR_BALANCE_STATUS) > 0);
}
if (this->charging_mos_enabled_binary_sensor_) {
this->charging_mos_enabled_binary_sensor_->publish_state(get_register(DALY_MODBUS_ADDR_CHG_MOS_ACTIVE) > 0);
}
if (this->discharging_mos_enabled_binary_sensor_) {
this->discharging_mos_enabled_binary_sensor_->publish_state(get_register(DALY_MODBUS_ADDR_DSCHG_MOS_ACTIVE) > 0);
}
if (this->precharging_mos_enabled_binary_sensor_) {
this->precharging_mos_enabled_binary_sensor_->publish_state(get_register(DALY_MODBUS_ADDR_PRECHG_MOS_ACTIVE) > 0);
}
#endif
this->read_state_ = ReadState::IDLE;
}
}
void DalyHkmsBmsComponent::dump_config() {

View file

@ -87,6 +87,8 @@ class DalyHkmsBmsComponent : public PollingComponent, public modbus::ModbusDevic
uint32_t last_send_;
uint8_t daly_address_;
enum class ReadState{ READ_CELL_VOLTAGES, READ_DATA, IDLE } read_state_{ReadState::IDLE};
};
} // namespace daly_hkms_bms

View file

@ -47,7 +47,7 @@ static const uint16_t DALY_MODBUS_ADDR_MOS_TEMP = 0x5A;
static const uint16_t DALY_MODBUS_ADDR_BOARD_TEMP = 0x5B;
static const uint16_t DALY_MODBUS_ADDR_HEATING_TEMP = 0x5C;
static const uint16_t DALY_MODBUS_REGISTER_COUNT = 0x5D;
static const uint16_t DALY_MODBUS_REGISTER_MAX = 0x5C;
} // namespace daly_hkms_bms
} // namespace esphome