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,6 +127,7 @@ 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);
@ -104,7 +145,11 @@ void DalyHkmsBmsComponent::on_modbus_data(const std::vector<uint8_t> &data) {
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
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);
@ -172,6 +217,9 @@ void DalyHkmsBmsComponent::on_modbus_data(const std::vector<uint8_t> &data) {
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