const readServiceID = '0000FFE0-0000-1000-8000-00805F9B34FB'; const readID = '0000FFE4-0000-1000-8000-00805F9B34FB'; const writeServiceID = '0000FFE5-0000-1000-8000-00805F9B34FB'; const writeID = '0000FFE9-0000-1000-8000-00805F9B34FB'; var readPart = ''; function acceptDevice(device) { return device.btid ? true : false; } function isDevice(device, data) { const advertisData = new Uint8Array(data.advertisData); const mac = device.btid .split('') .map((p, i) => parseInt(p + device.btid[i + 1], 16)) .filter((p, i) => i % 2 == 0); if (advertisData.slice(0, 2).toString() == [82, 83].toString() && advertisData.slice(4, 10).toString() == mac.toString()) { return true; } return false; } function readDataBP00(value, data) { if (value.split('HSO').length == 2) { data.imei = value.split('HSO')[0]; value = value.split('HSO')[1]; while (value != '') { switch (value[0]) { case 'P': data.quantity = parseInt(value.slice(1, 3)); value = value.slice(3, value.length); break; case 'S': data.gpscount = parseInt(value.slice(1, 3), 16); value = value.slice(3, value.length); break; case 'G': data.gsmlevel = parseInt(value.slice(1, 3), 16); value = value.slice(3, value.length); break; case 'T': data.temp = parseInt(value.slice(1, 5), 16) < 32768 ? parseInt(value.slice(1, 5), 16) : parseInt(value.slice(1, 5), 16) - 65536; value = value.slice(5, value.length); break; case 'C': data.cycle = parseInt(value.slice(1, 5), 16); value = value.slice(5, value.length); break; case 'B': var state = parseInt(value.slice(1, 3), 16); data.lowTempProtectState = (state >> 0) & 1; data.highTempProtectState = (state >> 1) & 1; data.dischargeProtectState = (state >> 2) & 1; data.chargeProtectState = (state >> 3) & 1; data.lowVoltageState = (state >> 4) & 1; data.chargeState = (state >> 5) & 3; data.oilState = (state >> 7) & false ? 1 : 0; value = value.slice(3, value.length); break; case 'X': var state = parseInt(value.slice(1, 3), 16); data.autoProtectState = (state >> 0) & 1; data.elecState = (state >> 1) & false ? 1 : 0; data.factoryType = state >> 2; value = value.slice(3, value.length); break; default: if (['P', 'S', 'G', 'T', 'C', 'B', 'X'].indexOf(value[3]) == -1) { //data.voltage = parseInt(value.slice(0, 4), 16) / 100 value = value.slice(4, value.length); } else { //data.voltage = parseInt(value.slice(0, 3), 16) / 100 value = value.slice(3, value.length); } } } console.log(data); return data; } return false; } function readDataBS50(value, data) { const length = parseInt(value.slice(0, 4), 16); value = value.slice(4, value.length); if (value.length == length * 2) { console.log(value); return BMSReply( value .split('') .map((p, i) => parseInt(p + value[i + 1], 16)) .filter((p, i) => i % 2 == 0), data ); } return false; } function BMSReply(value, data) { if (value[0] == 78 && value[1] == 87) { //value = value.slice(2, value.length) const length = value[2] * 256 + value[3]; if (value.length == length + 2) { data.bmsid = value .slice(4, 8) .map((p) => ('00' + p.toString(16)).substr(p.toString(16).length)) .join(''); if ( value[8] == 6 && value[value.length - 5] == 104 && value.slice(0, value.length - 4).reduce((p, c) => p + c) == value[value.length - 2] * 256 + value[value.length - 1] ) { value = value.slice(11, value.length - 9); while (value.length != 0) { switch (value[0]) { case 121: const voltageLength = parseInt(value[1] / 3); data.voltageList = Array(voltageLength); let maxVoltage = -1; let minVoltage = 99999; let sumVoltage = 0; for (var i = 0; i < voltageLength; i++) { let _v = Math.round(value[i * 3 + 3] * 256 + value[i * 3 + 4]) / 1000; console.log('xxxxx'); console.log(value[i * 3 + 2] - 1); data.voltageList[value[i * 3 + 2] - 1] = _v; sumVoltage += _v; if (_v > maxVoltage) { maxVoltage = _v; } if (_v < minVoltage) { minVoltage = _v; } } data.maxVoltage = maxVoltage; data.minVoltage = minVoltage; data.sumVoltage = sumVoltage; value = value.slice(voltageLength * 3 + 2, value.length); break; case 128: data.powerMOSTemp = value[1] * 256 + value[2]; if (data.powerMOSTemp > 100) { data.powerMOSTemp = 100 - data.powerMOSTemp; } value = value.slice(3, value.length); break; case 129: data.batteryChamberTemp = value[1] * 256 + value[2]; if (data.batteryChamberTemp > 100) { data.batteryChamberTemp = 100 - data.batteryChamberTemp; } value = value.slice(3, value.length); break; case 130: data.temp = value[1] * 256 + value[2]; if (data.temp > 100) { data.temp = 100 - data.temp; } value = value.slice(3, value.length); break; case 131: data.voltage = Math.round(value[1] * 256 + value[2]) / 100; value = value.slice(3, value.length); break; case 132: data.chargeState = 0; let _value = value[1] * 256 + value[2]; if (_value > 10000) { data.current = (_value - 10000) * 0.01 * -1; data.chargeState = 2; } else if (_value < 10000) { data.current = (10000 - _value) * 0.01; data.chargeState = 1; } value = value.slice(3, value.length); break; case 133: data.quantity = value[1]; data.soc = value[1]; value = value.slice(2, value.length); break; case 134: data.tempCount = value[1]; value = value.slice(2, value.length); break; case 135: data.cycle = value[1] * 256 + value[2]; value = value.slice(3, value.length); break; case 137: data.totalCirculatingCapacity = value[1] * 16777216 + value[2] * 65536 + value[3] * 256 + value[4]; value = value.slice(5, value.length); break; case 138: data.count = value[1] * 256 + value[2]; value = value.slice(3, value.length); break; case 139: data.alarmState = []; if (((value[2] >> 0) & 1) == 1) { data.alarmState.push('低容量报警'); } if (((value[2] >> 1) & 1) == 1) { data.alarmState.push('MOS管超温报警'); } if (((value[2] >> 2) & 1) == 1) { data.alarmState.push('充电过压报警'); } if (((value[2] >> 3) & 1) == 1) { data.alarmState.push('放电欠压报警'); } if (((value[2] >> 4) & 1) == 1) { data.alarmState.push('电池超温报警'); } if (((value[2] >> 5) & 1) == 1) { data.alarmState.push('充电过流报警'); } if (((value[2] >> 6) & 1) == 1) { data.alarmState.push('放电过流报警'); } if (((value[2] >> 7) & 1) == 1) { data.alarmState.push('电芯压差报警'); } if (((value[1] >> 0) & 1) == 1) { data.alarmState.push('电池箱内超温报警'); } if (((value[1] >> 1) & 1) == 1) { data.alarmState.push('电池低温报警'); } if (((value[1] >> 2) & 1) == 1) { data.alarmState.push('单体过压报警'); } if (((value[1] >> 3) & 1) == 1) { data.alarmState.push('单体欠压报警'); } if (((value[1] >> 4) & 1) == 1) { data.alarmState.push('309_A保护'); } if (((value[1] >> 5) & 1) == 1) { data.alarmState.push('309_B保护'); } value = value.slice(3, value.length); break; case 140: let _tmp = (value[1] << 8) | value[2]; // data.chargeProtectState = (value[2] >> 0 & 1) // data.dischargeProtectState = (value[2] >> 1 & 1) // data.equilibrium = (value[2] >> 2 & 1) // data.disconnectState = (value[2] >> 3 & 1) data.chargeProtectState = (_tmp & 1) == 0 ? 0 : 1; data.dischargeProtectState = (_tmp & 2) == 0 ? 0 : 1; data.equilibrium = (_tmp & 4) == 0 ? 0 : 1; data.disconnectState = (_tmp & 8) == 0 ? 0 : 1; value = value.slice(3, value.length); //console.log(value[1] + " " . value[2] + " " + _tmp + " ;" + data.equilibrium) break; case 142: value = value.slice(3, value.length); break; case 143: value = value.slice(3, value.length); break; case 144: value = value.slice(3, value.length); break; case 145: case 146: case 147: case 148: case 149: case 150: case 151: case 152: case 153: case 154: case 155: case 156: value = value.slice(3, value.length); break; case 157: value = value.slice(2, value.length); break; case 158: case 159: case 160: case 161: case 162: case 163: case 164: case 165: case 166: case 167: case 168: value = value.slice(3, value.length); break; case 169: value = value.slice(2, value.length); break; case 170: // 电池容量 data.capacity = (value[1] << 24) | (value[2] << 16) | (value[3] << 8) | value[4]; value = value.slice(5, value.length); break; case 171: case 172: value = value.slice(2, value.length); break; case 173: value = value.slice(3, value.length); break; case 174: case 175: value = value.slice(2, value.length); break; case 176: value = value.slice(3, value.length); break; case 177: value = value.slice(2, value.length); break; case 178: value = value.slice(11, value.length); break; case 179: value = value.slice(2, value.length); break; case 180: value = value.slice(9, value.length); break; case 181: value = value.slice(5, value.length); break; case 182: value = value.slice(5, value.length); break; case 183: // 软件版本号 15bit data.firmware = ''; for (let i = 1; i <= 15; i++) { let _value = 33; let hx = value[i]; if (hx >= 33 && hx <= 126) { _value = hx; } data.firmware += String.fromCharCode(_value); } value = value.slice(16, value.length); break; case 184: value = value.slice(2, value.length); break; case 185: value = value.slice(5, value.length); break; case 186: value = value.slice(25, value.length); break; default: value = []; } } } return data; } } return false; } function readData(device, value, data) { value = Array.from(new Int8Array(value)) .map((p) => String.fromCharCode(p)) .join(''); console.log(value); if (value[value.length - 1] == ')') { value = readPart.concat(value); readPart = ''; } else if (value[0] == '(') { readPart = value; } else { readPart = readPart.concat(value); } console.log(value); if (value[0] == '(' && value[value.length - 1] == ')') { value = value.slice(1, value.length - 1); if (value.slice(0, 12) == device.mac_id) { value = value.slice(12, value.length); switch (value.slice(0, 4)) { case 'BP00': return readDataBP00(value.slice(4, value.length), data); case 'BS50': return readDataBS50(value.slice(4, value.length), data); } } } return false; } /** * cmd: 命令字 02:写 */ function BMSCommand(data, cmd, type = 0) { // 帧来源: 01 // 传输类型: 00 const length = data.length + 18; var data = [78, 87, parseInt(length / 256), parseInt(length % 256), 0, 0, 0, 0, cmd, 1, type].concat(data).concat([0, 0, 0, 0, 104]); const sum = data.reduce((p, c) => p + c); data.push(parseInt(sum / 16777216)); data.push(parseInt((sum % 16777216) / 65536)); data.push(parseInt((sum % 65536) / 256)); data.push(parseInt(sum % 256)); console.log('222222'); console.log(data); return data; } function BMSRead() { return BMSCommand([0], 6, 0); } function BMSTurnOn() { return BMSCommand([172, 1], 2, 1); } function BMSTurnOff() { return BMSCommand([172, 0], 2, 1); } function bmsChargingMOS(value) { return BMSCommand([171, value], 2, 0); } function bmsDischargeMOS(value, device) { return BMSCommand([172, value], 2, 0); } function sendData(device, cmd, data = []) { return ( '(' + device.mac_id + cmd + data .map((p) => ('00' + p.toString(16)).substr(p.toString(16).length)) .join('') .toUpperCase() + ')' ) .split('') .map((p) => p.charCodeAt(0)); } function sendDataAE00(device, data) { return sendData(device, 'AE00', [data.length % 256, parseInt(data.length / 256)].concat(data)); } function stateUpdate(device, deviceId) { return [sendData(device, 'AU20'), sendDataAE00(device, BMSRead())]; } function turnOn(device, deviceId) { return [sendDataAE00(device, BMSTurnOn())]; } function turnOff(device, deviceId) { return [sendDataAE00(device, BMSTurnOff())]; } function bmsInfo(device, deviceId, info) { console.log('--------'); console.log(info); return false; } function bmsSet(device) { return false; } module.exports = { readServiceID: readServiceID, readID: readID, writeServiceID: writeServiceID, writeID: writeID, acceptDevice: acceptDevice, isDevice: isDevice, readData: readData, stateUpdate: stateUpdate, turnOn: turnOn, turnOff: turnOff, bmsInfo: bmsInfo, bmsSet: bmsSet, BMSCommand: BMSCommand, BMSRead: BMSRead, BMSTurnOn: BMSTurnOn, BMSTurnOff: BMSTurnOff, BMSReply: BMSReply, bmsChargingMOS: bmsChargingMOS, bmsDischargeMOS: bmsDischargeMOS };