Events

:

:

Elektronik | Funk | Software

Der Technik-Blog

  • Social Media

    YouTube

    Werbung:


    Neue Artikel


    Events

    • Keine zukünftigen Events vorhanden

    Der Technik-Blog

    Daly BMS Fernauslesung via LoRaWAN

    Daly BMS Fernauslesen mit LoRaWAN

    Alex @ AEQ-WEB

    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.

    Verdrahtung

    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.

    Werbung:

    Firmware hochladen

    Ü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.

    Lokaler Verbindungstest

    Das folgende Bild zeigt die Ausgabe vom Testprogramm in der Terminal Software RealTerm:

    Werbung:

    LoRaWAN & Payload Decoder

    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;
        }
    }
    


    122X122

    Über den Autor

    Alex, der Gründer von AEQ-WEB. Seit über 10 Jahren beschäftigt er sich mit Computern und elektronischen Bauteilen aller Art. Neben den Hardware-Projekten entwickelt er auch Webseiten, Apps und Software für Computer.

    Top Artikel in dieser Kategorie:

    AEQ-WEB LoWTrack App

    LoWTRACK - Die App für LoRaWAN GPS-Tracker

    LoWTRACK ist eine App für LoRaWAN GPS-Tracker. LoWTRACK zeigt Positionsdaten, Gateway-Details und viele Informationen über das verbundene LoRaWAN-Netzwerk an.

    Weiterlesen
    Daly BMS Fernauslesung via LoRaWAN

    Daly BMS Fernauslesen mit LoRaWAN

    • Video

    Über die serielle Schnittstelle kann mit einer LW-Base ein BMS vom Hersteller Daly ausgelesen werden. Die Datenübertragung erfolgt via LoRaWAN.

    Weiterlesen

    Social Media

    YouTube

    Werbung:


    Neue Artikel


    Events

    • Keine zukünftigen Events vorhanden