Yes of course
There is also a possibility to use the WECO and Soltaro CAN bus protocols. I have some information on these protocols but not this type of inverter to develop and test.
for anyone else in the future, i think for SRNE and other rs485 protcols its kinda a dead end as from my testing as most the polling i saw only allowed for SOC communcating to the BMS. so all the dynamic charge logic in yambus goes to waste, but the aggregating does still have value though.
for anyone who wants to dig further into it, heres my code MODBUS server code for PACE (which SRNE and other budget inverters support over rs485 if they dont have CAN BUS for the project). I'm going to probably to go my own path of using my current SRNE modbus connection for device control to adjust the charging registers dynamically based on SOC using yambus logic, but thats beyond the scope of this library.
# Pace BMS yaml
uart:
- id: uart_modbus
tx_pin: 18
rx_pin: 17
baud_rate: 9600
parity: NONE
stop_bits: 1
modbus:
- id: modbus_slave
uart_id: uart_modbus
role: server
modbus_controller:
- id: pace_bms
modbus_id: modbus_slave
address: 1 # Needs logic/location to put in rs485 address for yambus yaml implementation
server_registers:
# 40001 → offset 0
- address: 0
value_type: S_WORD
read_lambda: |-
return int(id(${yambms_id}_current).state * 100);
# 40002 → offset 1
- address: 1
value_type: U_WORD
read_lambda: |-
return int(id(${yambms_id}_total_voltage).state * 100);
# 40003 → offset 2
- address: 2
value_type: U_WORD
read_lambda: |-
return int(id(${yambms_id}_state_of_charge).state);
# 40004 → offset 3
- address: 3
value_type: U_WORD
read_lambda: |-
return int(id(${yambms_id}_battery_soh).state);
# 40005 → offset 4
- address: 4
value_type: U_WORD
read_lambda: |-
return int(id(${yambms_id}_capacity_remaining_ah).state * 100);
# 40006 → offset 5
- address: 5
value_type: U_WORD
read_lambda: |-
return int(id(${yambms_id}_battery_capacity).state * 100);
# 40007 → offset 6
- address: 6
value_type: U_WORD
read_lambda: |-
return int(id(${yambms_id}_installed_battery_capacity).state * 100);
# 40008 → offset 7
- address: 7
value_type: U_WORD
read_lambda: |-
return int(id(${yambms_id}_charging_cycles).state);
# 40009 → offset 8 (reserved)
- address: 8
value_type: U_WORD
read_lambda: |-
return 0;
# 40010 → offset 9 (Warning Bitmask Mapping)
- address: 9
value_type: U_WORD
read_lambda: |-
uint16_t mask = static_cast<uint16_t>(id(yambms_errors_bitmask_warning).state);
uint16_t warning = 0;
if (mask & 0x0002) warning |= 1 << 0; // cell overvoltage
if (mask & 0x0004) warning |= 1 << 1; // cell undervoltage
if (mask & 0x0002) warning |= 1 << 2; // pack overvoltage
if (mask & 0x0004) warning |= 1 << 3; // pack undervoltage
if (mask & 0x0100) warning |= 1 << 4; // charging overcurrent
if (mask & 0x0080) warning |= 1 << 5; // discharging overcurrent
if (mask & 0x0020) warning |= 1 << 8; // charging high temp
if (mask & 0x0008) warning |= 1 << 9; // discharging high temp
if (mask & 0x0040) warning |= 1 << 10; // charging low temp
if (mask & 0x0010) warning |= 1 << 11; // discharging low temp
if (mask & 0x0800) warning |= 1 << 14; // MOSFET high temp
if (id(yambms_battery_soc).state <= 10) warning |= 1 << 15;
return warning;
# 40011 → offset 10 (Protection Bitmask Mapping)
- address: 10
value_type: U_WORD
read_lambda: |-
uint16_t mask = static_cast<uint16_t>(id(yambms_errors_bitmask_alarm).state);
uint16_t protection = 0;
if (mask & 0x0002) protection |= 1 << 0; // cell OV protection
if (mask & 0x0004) protection |= 1 << 1; // cell UV protection
if (mask & 0x0100) protection |= 1 << 2; // charge OC protection
if (mask & 0x0080) protection |= 1 << 3; // discharge OC protection
if (mask & 0x0400) protection |= 1 << 4; // short circuit protection
if (mask & 0x0010) protection |= 1 << 5; // charge low temp protection
if (mask & 0x0020) protection |= 1 << 6; // charge high temp protection
if (mask & 0x0040) protection |= 1 << 7; // discharge low temp protection
if (mask & 0x0008) protection |= 1 << 8; // discharge high temp protection
return protection;
# 40012 → offset 11 (Status Flag Mapping)
- address: 11
value_type: U_WORD
read_lambda: |-
uint16_t flag = 0;
// Faults
// BIT0: charging MOSFET fault (no direct sensor)
// BIT1: discharging MOSFET fault (no direct sensor)
// BIT2: temperature sensor fault (no direct sensor)
// BIT3: reserve
// BIT4: battery cell fault (map to cell imbalance/alarm)
if (id(${yambms_id}_alarm).state == "UBC") flag |= 1 << 4;
// BIT5: comm fault (no direct sensor)
// Status
// BIT8: state of charge (charging or charged)
if (id(${yambms_id}_current).state > 0.5) flag |= 1 << 8;
// BIT9: state of discharge
if (id(${yambms_id}_current).state < -0.5) flag |= 1 << 9;
// BIT10: charging MOSFET ON (charging)
if (id(${yambms_id}_current).state > 0.5) flag |= 1 << 10;
// BIT11: discharging MOSFET ON (discharging)
if (id(${yambms_id}_current).state < -0.5) flag |= 1 << 11;
// BIT12: charging limiter (no direct sensor)
// BIT14: charger inversed (no direct sensor)
// BIT15: heater ON (no direct sensor)
return flag;