Daly BMS Fernauslesen mit LoRaWAN
05.12.2025
Elektronik | Funk | Software
Der Technik-Blog
Mit der hier angebotenen Firmware kann die LW-Base von ELV über die serielle Schnittstelle mit einem BMS vom Hersteller Daly kommunizieren. Damit ist eine Fernauslesung und Datenübertragung per Funk (LoRaWAN) möglich.
Die LW-Base kommuniziert mit dem Daly BMS seriell. Dafür werden die Pins PA2 (TX) und PA3 (RX) verwendet. Ein Schnittstellenwandler konvertiert die Spannungspegel zwischen TTL und RS485.
Über das ELV Flasher Tool wird die gewünschte Firmware auf das Board geladen. Im ZIP-Ordner befindet sich ein Testprogramm, welches das Daly BMS ausliest und die Daten über die serielle Schnittstelle ausgibt. Die Baud-Rate beträgt 115200. Funktioniert das Testprogramm, so kann im Anschluss die Firmware für die LoRaWAN Unterstützung hochgeladen werden.
Das folgende Bild zeigt die Ausgabe vom Testprogramm in der Terminal Software RealTerm:
Nachdem die LW-Base beim LoRaWAN Netzwerkserver registriert wurde, muss nur noch der folgende JS Payload Decoder eingebunden werden:
function decodeUplink(input) { var bytes = input.bytes; var port = input.fPort; var tx_reason_list = [ "Timer_Event", "User_Button_Event", "App_Event", "FUOTA_Event", "Cyclic_Event", "Timeout_Event" ]; var TX_Reason = (bytes[0] < tx_reason_list.length) ? tx_reason_list[bytes[0]] : "Unknown"; var Supply_Voltage = (bytes[3] << 8) | bytes[4]; var BMS_Connection = bytes[5]; var Cell_Cnt = bytes[6]; var Temp_Cnt = bytes[7]; if (BMS_Connection === 0x01 && port === 11) { var Total_Voltage = (bytes[8] << 8) | bytes[9]; var Current = (bytes[10] << 8) | bytes[11]; if (Current & 0x8000) Current -= 0x10000; var Soc_State = bytes[12]; var Mos_State = bytes[13]; var Charge_MOS = (Mos_State & (1 << 0)) !== 0; var Discharge_MOS = (Mos_State & (1 << 1)) !== 0; var StateBits = (Mos_State >> 2) & 0x03; var Mos_Operation_State = "Idle"; if (StateBits === 1) Mos_Operation_State = "Charging"; else if (StateBits === 2) Mos_Operation_State = "Discharging"; var Remain_Capacity = (bytes[14] << 8) | bytes[15]; const BMS_Errors = BMSErrors(bytes, 16); return { data: { TX_Reason: TX_Reason, Frame_Type: "MAIN_GENERAL", Supply_Voltage: Supply_Voltage, BMS_Connection: true, Cells: Cell_Cnt, Temperature_Sensors: Temp_Cnt, Total_Voltage: Total_Voltage / 100.0, Current: Current / 100.0, SOC: Soc_State, Charge_MOS: Charge_MOS, Discharge_MOS: Discharge_MOS, Operation_State: Mos_Operation_State, Remain_Capacity: Remain_Capacity / 100.0, BMS_Errors: BMS_Errors } }; } else if (BMS_Connection === 0x01 && port === 12) { var voltages = []; var idx = 8; var max_cells = 21; var total_cells = (Cell_Cnt > max_cells) ? max_cells : Cell_Cnt; for (var i = 0; i < total_cells && (idx + 1) < bytes.length; i++) { var mv = (bytes[idx] << 8) | bytes[idx + 1]; voltages.push({ cell: i + 1, voltage: mv / 100.0 }); idx += 2; } return { data: { TX_Reason: TX_Reason, Frame_Type: "CELL_VOLTAGES", Supply_Voltage: Supply_Voltage, BMS_Connection: true, Cells: Cell_Cnt, Cell_Voltages: voltages } }; } else if (BMS_Connection === 0x01 && port === 13) { var temperatures = []; var idx = 8; var total_temps = (Temp_Cnt > 16) ? 16 : Temp_Cnt; for (var i = 0; i < total_temps && (idx + 1) < bytes.length; i++) { var raw = (bytes[idx] << 8) | bytes[idx + 1]; var tempC = raw / 100.0; temperatures.push({ sensor: i + 1, temperature: tempC }); idx += 2; } return { data: { TX_Reason: TX_Reason, Frame_Type: "TEMPERATURE_SENSORS", Supply_Voltage: Supply_Voltage, BMS_Connection: true, Temperature_Sensors: Temp_Cnt, Temperatures: temperatures } }; } else if (BMS_Connection === 0x01 && port === 10) { let idx = 8; let Total_Voltage = (bytes[idx++] << 8) | bytes[idx++]; let Current = (bytes[idx++] << 8) | bytes[idx++]; if (Current & 0x8000) Current -= 0x10000; let SOC = bytes[idx++]; const Mos_State = bytes[idx++]; const Charge_MOS = (Mos_State & 1) !== 0; const Discharge_MOS = (Mos_State & 2) !== 0; const StateBits = (Mos_State >> 2) & 0x03; let Mos_Operation_State = "Idle"; if (StateBits === 1) Mos_Operation_State = "Charging"; else if (StateBits === 2) Mos_Operation_State = "Discharging"; const Remain_Capacity = (bytes[idx++] << 8) | bytes[idx++]; const BMS_Errors = BMSErrors(bytes, idx); idx += 8; const voltages = []; const total_cells = Math.min(Cell_Cnt, Math.floor((bytes.length - idx) / 2)); for (let i = 0; i < total_cells; i++) { const mv = (bytes[idx++] << 8) | bytes[idx++]; voltages.push({cell: i + 1, voltage: mv / 100.0}); } const temps = []; const total_temps = Math.min(Temp_Cnt, bytes.length - idx); for (let i = 0; i < total_temps; i++) { temps.push({sensor: i + 1, temperature: bytes[idx++] - 40}); } return { data: { TX_Reason: TX_Reason, Frame_Type: "MAIN_MAX8C4T", Supply_Voltage: Supply_Voltage, BMS_Connection: true, Cells: Cell_Cnt, Temperature_Sensors: Temp_Cnt, Total_Voltage: Total_Voltage / 100.0, Current: Current / 100.0, SOC: SOC, Charge_MOS: Charge_MOS, Discharge_MOS: Discharge_MOS, Operation_State: Mos_Operation_State, Remain_Capacity: Remain_Capacity / 100.0, BMS_Errors: BMS_Errors, Cell_Voltages: voltages, Temperatures: temps } }; } else { return { data: { TX_Reason: TX_Reason, Supply_Voltage: Supply_Voltage, BMS_Connection: false } }; } function BMSErrors(bytes, startIndex) { const statusTexts = [ "Cell volt high level 1", "Cell volt high level 2", "Cell volt low level 1", "Cell volt low level 2", "Sum volt high level 1", "Sum volt high level 2", "Sum volt low level 1", "Sum volt low level 2", "Chg temp high level 1", "Chg temp high level 2", "Chg temp low level 1", "Chg temp low level 2", "Dischg temp high level 1", "Dischg temp high level 2", "Dischg temp low level 1", "Dischg temp low level 2", "Chg overcurrent level 1", "Chg overcurrent level 2", "Dischg overcurrent level 1", "Dischg overcurrent level 2", "SOC high level 1", "SOC high level 2", "SOC low level 1", "SOC low level 2", "Diff volt level 1", "Diff volt level 2", "Diff temp level 1", "Diff temp level 2", null, null, null, null, "Chg MOS temp high alarm", "Dischg MOS temp high alarm", "Chg MOS temp sensor error", "Dischg MOS temp sensor error", "Chg MOS adhesion error", "Dischg MOS adhesion error", "Chg MOS open circuit error", "Dischg MOS open circuit error", "AFE collect chip error", "Voltage collect dropped", "Cell temp sensor error", "EEPROM error", "RTC error", "Precharge failure", "Communication failure", "Internal communication failure", "Current module fault", "Sum voltage detect fault", "Short circuit protect fault", "Low volt forbidden chg fault", null, null, null, null, "Fault code" ]; let errors = []; for (let byteIdx = 0; byteIdx < 8; byteIdx++) { let byte = bytes[startIndex + byteIdx]; for (let bit = 0; bit < 8; bit++) { let bitIndex = byteIdx * 8 + bit; if ((byte & (1 << bit)) !== 0 && statusTexts[bitIndex]) { errors.push(statusTexts[bitIndex]); } } } return errors; } }
LoWTRACK ist eine App für LoRaWAN GPS-Tracker. LoWTRACK zeigt Positionsdaten, Gateway-Details und viele Informationen über das verbundene LoRaWAN-Netzwerk an.
Weiterlesen
Über die serielle Schnittstelle kann mit einer LW-Base ein BMS vom Hersteller Daly ausgelesen werden. Die Datenübertragung erfolgt via LoRaWAN.
WeiterlesenAEQ-WEB © 2015-2025 All Right Reserved