FMBMS.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. const readServiceID = '0000FFE0-0000-1000-8000-00805F9B34FB';
  2. const readID = '0000FFE4-0000-1000-8000-00805F9B34FB';
  3. const writeServiceID = '0000FFE5-0000-1000-8000-00805F9B34FB';
  4. const writeID = '0000FFE9-0000-1000-8000-00805F9B34FB';
  5. var readPart = '';
  6. function acceptDevice(device) {
  7. return device.btid ? true : false;
  8. }
  9. function isDevice(device, data) {
  10. const advertisData = new Uint8Array(data.advertisData);
  11. const mac = device.btid
  12. .split('')
  13. .map((p, i) => parseInt(p + device.btid[i + 1], 16))
  14. .filter((p, i) => i % 2 == 0);
  15. if (advertisData.slice(0, 2).toString() == [0x52, 0x53].toString() && advertisData.slice(4, 10).toString() == mac.toString()) {
  16. return true;
  17. }
  18. return false;
  19. }
  20. function readDataBP00(value, data) {
  21. if (value.split('HSO').length == 2) {
  22. data.imei = value.split('HSO')[0];
  23. value = value.split('HSO')[1];
  24. while (value != '') {
  25. switch (value[0]) {
  26. case 'P':
  27. data.quantity = parseInt(value.slice(1, 3));
  28. value = value.slice(3, value.length);
  29. break;
  30. case 'S':
  31. data.gpscount = parseInt(value.slice(1, 3), 16);
  32. value = value.slice(3, value.length);
  33. break;
  34. case 'G':
  35. data.gsmlevel = parseInt(value.slice(1, 3), 16);
  36. value = value.slice(3, value.length);
  37. break;
  38. case 'T':
  39. data.temp = parseInt(value.slice(1, 5), 16) < 0x8000 ? parseInt(value.slice(1, 5), 16) : parseInt(value.slice(1, 5), 16) - 0x10000;
  40. value = value.slice(5, value.length);
  41. break;
  42. case 'C':
  43. data.cycle = parseInt(value.slice(1, 5), 16);
  44. value = value.slice(5, value.length);
  45. break;
  46. case 'B':
  47. var state = parseInt(value.slice(1, 3), 16);
  48. data.lowTempProtectState = (state >> 0) & 1;
  49. data.highTempProtectState = (state >> 1) & 1;
  50. data.dischargeProtectState = (state >> 2) & 1;
  51. data.chargeProtectState = (state >> 3) & 1;
  52. data.lowVoltageState = (state >> 4) & 1;
  53. data.chargeState = (state >> 5) & 3;
  54. data.oilState = (state >> 7) & (1 == 0) ? 1 : 0;
  55. value = value.slice(3, value.length);
  56. break;
  57. case 'X':
  58. var state = parseInt(value.slice(1, 3), 16);
  59. data.autoProtectState = (state >> 0) & 1;
  60. data.elecState = (state >> 1) & (1 == 0) ? 1 : 0;
  61. data.factoryType = state >> 2;
  62. value = value.slice(3, value.length);
  63. break;
  64. default:
  65. if (['P', 'S', 'G', 'T', 'C', 'B', 'X'].indexOf(value[3]) == -1) {
  66. //data.voltage = parseInt(value.slice(0, 4), 16) / 100
  67. value = value.slice(4, value.length);
  68. } else {
  69. //data.voltage = parseInt(value.slice(0, 3), 16) / 100
  70. value = value.slice(3, value.length);
  71. }
  72. }
  73. }
  74. return data;
  75. }
  76. return false;
  77. }
  78. function readDataBS50(value, data) {
  79. const length = parseInt(value.slice(0, 4), 16);
  80. value = value.slice(4, value.length);
  81. if (value.length == length * 2) {
  82. return BMSReply(
  83. value
  84. .split('')
  85. .map((p, i) => parseInt(p + value[i + 1], 16))
  86. .filter((p, i) => i % 2 == 0),
  87. data
  88. );
  89. }
  90. return false;
  91. }
  92. function BMSReply(value, data) {
  93. if (value[0] == 0x4e && value[1] == 0x57) {
  94. //value = value.slice(2, value.length)
  95. const length = value[2] * 0x100 + value[3];
  96. if (value.length == length + 2) {
  97. data.bmsid = value
  98. .slice(4, 8)
  99. .map((p) => ('00' + p.toString(16)).substr(p.toString(16).length))
  100. .join('');
  101. if (
  102. value[8] == 0x06 &&
  103. value[value.length - 5] == 0x68 &&
  104. value.slice(0, value.length - 4).reduce((p, c) => p + c) == value[value.length - 2] * 0x100 + value[value.length - 1]
  105. ) {
  106. value = value.slice(11, value.length - 9);
  107. while (value.length != 0) {
  108. switch (value[0]) {
  109. case 0x79:
  110. const voltageLength = parseInt(value[1] / 3);
  111. data.voltageList = Array(voltageLength);
  112. let maxVoltage = -1;
  113. let minVoltage = 99999;
  114. let sumVoltage = 0;
  115. for (var i = 0; i < voltageLength; i++) {
  116. let _v = Math.round(value[i * 3 + 3] * 0x100 + value[i * 3 + 4]) / 1000;
  117. data.voltageList[value[i * 3 + 2] - 1] = _v;
  118. sumVoltage += _v;
  119. if (_v > maxVoltage) maxVoltage = _v;
  120. if (_v < minVoltage) minVoltage = _v;
  121. }
  122. data.maxVoltage = maxVoltage;
  123. data.minVoltage = minVoltage;
  124. data.sumVoltage = sumVoltage;
  125. value = value.slice(voltageLength * 3 + 2, value.length);
  126. break;
  127. case 0x80:
  128. data.powerMOSTemp = value[1] * 0x100 + value[2];
  129. if (data.powerMOSTemp > 100) data.powerMOSTemp = 100 - data.powerMOSTemp;
  130. value = value.slice(3, value.length);
  131. break;
  132. case 0x81:
  133. data.batteryChamberTemp = value[1] * 0x100 + value[2];
  134. if (data.batteryChamberTemp > 100) data.batteryChamberTemp = 100 - data.batteryChamberTemp;
  135. value = value.slice(3, value.length);
  136. break;
  137. case 0x82:
  138. data.temp = value[1] * 0x100 + value[2];
  139. if (data.temp > 100) data.temp = 100 - data.temp;
  140. value = value.slice(3, value.length);
  141. break;
  142. case 0x83:
  143. data.voltage = Math.round(value[1] * 0x100 + value[2]) / 100;
  144. value = value.slice(3, value.length);
  145. break;
  146. case 0x84:
  147. data.chargeState = 0;
  148. _value = value[1] * 0x100 + value[2];
  149. if (_value > 10000) {
  150. data.current = (_value - 10000) * 0.01 * -1;
  151. data.chargeState = 2;
  152. } else if (_value < 10000) {
  153. data.current = (10000 - _value) * 0.01;
  154. data.chargeState = 1;
  155. }
  156. value = value.slice(3, value.length);
  157. break;
  158. case 0x85:
  159. data.quantity = value[1];
  160. data.soc = value[1];
  161. value = value.slice(2, value.length);
  162. break;
  163. case 0x86:
  164. data.tempCount = value[1];
  165. value = value.slice(2, value.length);
  166. break;
  167. case 0x87:
  168. data.cycle = value[1] * 0x100 + value[2];
  169. value = value.slice(3, value.length);
  170. break;
  171. case 0x89:
  172. data.totalCirculatingCapacity = value[1] * 0x1000000 + value[2] * 0x10000 + value[3] * 0x100 + value[4];
  173. value = value.slice(5, value.length);
  174. break;
  175. case 0x8a:
  176. data.count = value[1] * 0x100 + value[2];
  177. value = value.slice(3, value.length);
  178. break;
  179. case 0x8b:
  180. data.alarmState = [];
  181. if (((value[2] >> 0) & 1) == 1) data.alarmState.push('低容量报警');
  182. if (((value[2] >> 1) & 1) == 1) data.alarmState.push('MOS管超温报警');
  183. if (((value[2] >> 2) & 1) == 1) data.alarmState.push('充电过压报警');
  184. if (((value[2] >> 3) & 1) == 1) data.alarmState.push('放电欠压报警');
  185. if (((value[2] >> 4) & 1) == 1) data.alarmState.push('电池超温报警');
  186. if (((value[2] >> 5) & 1) == 1) data.alarmState.push('充电过流报警');
  187. if (((value[2] >> 6) & 1) == 1) data.alarmState.push('放电过流报警');
  188. if (((value[2] >> 7) & 1) == 1) data.alarmState.push('电芯压差报警');
  189. if (((value[1] >> 0) & 1) == 1) data.alarmState.push('电池箱内超温报警');
  190. if (((value[1] >> 1) & 1) == 1) data.alarmState.push('电池低温报警');
  191. if (((value[1] >> 2) & 1) == 1) data.alarmState.push('单体过压报警');
  192. if (((value[1] >> 3) & 1) == 1) data.alarmState.push('单体欠压报警');
  193. if (((value[1] >> 4) & 1) == 1) data.alarmState.push('309_A保护');
  194. if (((value[1] >> 5) & 1) == 1) data.alarmState.push('309_B保护');
  195. value = value.slice(3, value.length);
  196. break;
  197. case 0x8c:
  198. data.chargeProtectState = (value[2] >> 0) & 1;
  199. data.dischargeProtectState = (value[2] >> 1) & 1;
  200. data.equilibrium = (value[2] >> 2) & 1;
  201. data.disconnectState = (value[2] >> 3) & 1;
  202. value = value.slice(3, value.length);
  203. break;
  204. case 0x8e:
  205. value = value.slice(3, value.length);
  206. break;
  207. case 0x8f:
  208. value = value.slice(3, value.length);
  209. break;
  210. case 0x90:
  211. value = value.slice(3, value.length);
  212. break;
  213. case 0x91:
  214. case 0x92:
  215. case 0x93:
  216. case 0x94:
  217. case 0x95:
  218. case 0x96:
  219. case 0x97:
  220. case 0x98:
  221. case 0x99:
  222. case 0x9a:
  223. case 0x9b:
  224. case 0x9c:
  225. value = value.slice(3, value.length);
  226. break;
  227. case 0x9d:
  228. value = value.slice(2, value.length);
  229. break;
  230. case 0x9e:
  231. case 0x9f:
  232. case 0xa0:
  233. case 0xa1:
  234. case 0xa2:
  235. case 0xa3:
  236. case 0xa4:
  237. case 0xa5:
  238. case 0xa6:
  239. case 0xa7:
  240. case 0xa8:
  241. value = value.slice(3, value.length);
  242. break;
  243. case 0xa9:
  244. value = value.slice(2, value.length);
  245. break;
  246. case 0xaa:
  247. // 电池容量
  248. data.capacity = (value[1] << 24) | (value[2] << 16) | (value[3] << 8) | value[4];
  249. value = value.slice(5, value.length);
  250. break;
  251. case 0xab:
  252. case 0xac:
  253. value = value.slice(2, value.length);
  254. break;
  255. case 0xad:
  256. value = value.slice(3, value.length);
  257. break;
  258. case 0xae:
  259. case 0xaf:
  260. value = value.slice(2, value.length);
  261. break;
  262. case 0xb0:
  263. value = value.slice(3, value.length);
  264. break;
  265. case 0xb1:
  266. value = value.slice(2, value.length);
  267. break;
  268. case 0xb2:
  269. value = value.slice(11, value.length);
  270. break;
  271. case 0xb3:
  272. value = value.slice(2, value.length);
  273. break;
  274. case 0xb4:
  275. value = value.slice(9, value.length);
  276. break;
  277. case 0xb5:
  278. value = value.slice(5, value.length);
  279. break;
  280. case 0xb6:
  281. value = value.slice(5, value.length);
  282. break;
  283. case 0xb7:
  284. // 软件版本号 15bit
  285. data.firmware = '';
  286. for (let i = 1; i <= 15; i++) {
  287. data.firmware += String.fromCharCode(value[i]);
  288. }
  289. value = value.slice(16, value.length);
  290. break;
  291. case 0xb8:
  292. value = value.slice(2, value.length);
  293. break;
  294. case 0xb9:
  295. value = value.slice(5, value.length);
  296. break;
  297. case 0xba:
  298. value = value.slice(25, value.length);
  299. break;
  300. default:
  301. value = [];
  302. }
  303. }
  304. }
  305. return data;
  306. }
  307. }
  308. return false;
  309. }
  310. function readData(device, value, data) {
  311. value = Array.from(new Int8Array(value))
  312. .map((p) => String.fromCharCode(p))
  313. .join('');
  314. if (value[value.length - 1] == ')') {
  315. value = readPart.concat(value);
  316. readPart = '';
  317. } else if (value[0] == '(') {
  318. readPart = value;
  319. } else {
  320. readPart = readPart.concat(value);
  321. }
  322. if (value[0] == '(' && value[value.length - 1] == ')') {
  323. value = value.slice(1, value.length - 1);
  324. if (value.slice(0, 12) == device.mac_id) {
  325. value = value.slice(12, value.length);
  326. switch (value.slice(0, 4)) {
  327. case 'BP00':
  328. return readDataBP00(value.slice(4, value.length), data);
  329. case 'BS50':
  330. return readDataBS50(value.slice(4, value.length), data);
  331. }
  332. }
  333. }
  334. return false;
  335. }
  336. /**
  337. * cmd: 命令字 02:写
  338. */
  339. function BMSCommand(data, cmd, type = 0) {
  340. // 帧来源: 01
  341. // 传输类型: 00
  342. const length = data.length + 18;
  343. var data = [0x4e, 0x57, parseInt(length / 0x100), parseInt(length % 0x100), 0x00, 0x00, 0x00, 0x00, cmd, 0x01, type].concat(data).concat([0x00, 0x00, 0x00, 0x00, 0x68]);
  344. const sum = data.reduce((p, c) => p + c);
  345. data.push(parseInt(sum / 0x1000000));
  346. data.push(parseInt((sum % 0x1000000) / 0x10000));
  347. data.push(parseInt((sum % 0x10000) / 0x100));
  348. data.push(parseInt(sum % 0x100));
  349. console.log(data);
  350. return data;
  351. }
  352. function BMSRead() {
  353. return BMSCommand([0x00], 0x06, 0);
  354. }
  355. function BMSTurnOn() {
  356. return BMSCommand([0xac, 1], 0x02, 1);
  357. }
  358. function BMSTurnOff() {
  359. return BMSCommand([0xac, 0], 0x02, 1);
  360. }
  361. function bmsChargingMOS(value) {
  362. return BMSCommand([0xab, value], 0x02, 0);
  363. }
  364. function bmsDischargeMOS(value) {
  365. return BMSCommand([0xac, value], 0x02, 0);
  366. }
  367. function sendData(device, cmd, data = []) {
  368. return (
  369. '(' +
  370. device.mac_id +
  371. cmd +
  372. data
  373. .map((p) => ('00' + p.toString(16)).substr(p.toString(16).length))
  374. .join('')
  375. .toUpperCase() +
  376. ')'
  377. )
  378. .split('')
  379. .map((p) => p.charCodeAt(0));
  380. }
  381. function sendDataAE00(device, data) {
  382. return sendData(device, 'AE00', [data.length % 0x100, parseInt(data.length / 0x100)].concat(data));
  383. }
  384. function stateUpdate(device, deviceId) {
  385. return [sendData(device, 'AU20'), sendDataAE00(device, BMSRead())];
  386. }
  387. function turnOn(device, deviceId) {
  388. return [sendDataAE00(device, BMSTurnOn())];
  389. }
  390. function turnOff(device, deviceId) {
  391. return [sendDataAE00(device, BMSTurnOff())];
  392. }
  393. function bmsInfo(device, deviceId, info) {
  394. return false;
  395. }
  396. function bmsSet(device, deviceId, name, vars) {
  397. return false;
  398. }
  399. module.exports = {
  400. readServiceID: readServiceID,
  401. readID: readID,
  402. writeServiceID: writeServiceID,
  403. writeID: writeID,
  404. acceptDevice: acceptDevice,
  405. isDevice: isDevice,
  406. readData: readData,
  407. stateUpdate: stateUpdate,
  408. turnOn: turnOn,
  409. turnOff: turnOff,
  410. bmsInfo: bmsInfo,
  411. bmsSet: bmsSet,
  412. BMSCommand: BMSCommand,
  413. BMSRead: BMSRead,
  414. BMSTurnOn: BMSTurnOn,
  415. BMSTurnOff: BMSTurnOff,
  416. BMSReply: BMSReply,
  417. bmsChargingMOS: bmsChargingMOS,
  418. bmsDischargeMOS: bmsDischargeMOS
  419. };