FMBMS.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  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() == [82, 83].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) < 32768 ? parseInt(value.slice(1, 5), 16) : parseInt(value.slice(1, 5), 16) - 65536;
  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) & false ? 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) & false ? 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. console.log(data);
  75. return data;
  76. }
  77. return false;
  78. }
  79. function readDataBS50(value, data) {
  80. const length = parseInt(value.slice(0, 4), 16);
  81. value = value.slice(4, value.length);
  82. if (value.length == length * 2) {
  83. console.log(value);
  84. return BMSReply(
  85. value
  86. .split('')
  87. .map((p, i) => parseInt(p + value[i + 1], 16))
  88. .filter((p, i) => i % 2 == 0),
  89. data
  90. );
  91. }
  92. return false;
  93. }
  94. function BMSReply(value, data) {
  95. if (value[0] == 78 && value[1] == 87) {
  96. //value = value.slice(2, value.length)
  97. const length = value[2] * 256 + value[3];
  98. if (value.length == length + 2) {
  99. data.bmsid = value
  100. .slice(4, 8)
  101. .map((p) => ('00' + p.toString(16)).substr(p.toString(16).length))
  102. .join('');
  103. if (
  104. value[8] == 6 &&
  105. value[value.length - 5] == 104 &&
  106. value.slice(0, value.length - 4).reduce((p, c) => p + c) == value[value.length - 2] * 256 + value[value.length - 1]
  107. ) {
  108. value = value.slice(11, value.length - 9);
  109. while (value.length != 0) {
  110. switch (value[0]) {
  111. case 121:
  112. const voltageLength = parseInt(value[1] / 3);
  113. data.voltageList = Array(voltageLength);
  114. let maxVoltage = -1;
  115. let minVoltage = 99999;
  116. let sumVoltage = 0;
  117. for (var i = 0; i < voltageLength; i++) {
  118. let _v = Math.round(value[i * 3 + 3] * 256 + value[i * 3 + 4]) / 1000;
  119. console.log('xxxxx');
  120. console.log(value[i * 3 + 2] - 1);
  121. data.voltageList[value[i * 3 + 2] - 1] = _v;
  122. sumVoltage += _v;
  123. if (_v > maxVoltage) {
  124. maxVoltage = _v;
  125. }
  126. if (_v < minVoltage) {
  127. minVoltage = _v;
  128. }
  129. }
  130. data.maxVoltage = maxVoltage;
  131. data.minVoltage = minVoltage;
  132. data.sumVoltage = sumVoltage;
  133. value = value.slice(voltageLength * 3 + 2, value.length);
  134. break;
  135. case 128:
  136. data.powerMOSTemp = value[1] * 256 + value[2];
  137. if (data.powerMOSTemp > 100) {
  138. data.powerMOSTemp = 100 - data.powerMOSTemp;
  139. }
  140. value = value.slice(3, value.length);
  141. break;
  142. case 129:
  143. data.batteryChamberTemp = value[1] * 256 + value[2];
  144. if (data.batteryChamberTemp > 100) {
  145. data.batteryChamberTemp = 100 - data.batteryChamberTemp;
  146. }
  147. value = value.slice(3, value.length);
  148. break;
  149. case 130:
  150. data.temp = value[1] * 256 + value[2];
  151. if (data.temp > 100) {
  152. data.temp = 100 - data.temp;
  153. }
  154. value = value.slice(3, value.length);
  155. break;
  156. case 131:
  157. data.voltage = Math.round(value[1] * 256 + value[2]) / 100;
  158. value = value.slice(3, value.length);
  159. break;
  160. case 132:
  161. data.chargeState = 0;
  162. let _value = value[1] * 256 + value[2];
  163. if (_value > 10000) {
  164. data.current = (_value - 10000) * 0.01 * -1;
  165. data.chargeState = 2;
  166. } else if (_value < 10000) {
  167. data.current = (10000 - _value) * 0.01;
  168. data.chargeState = 1;
  169. }
  170. value = value.slice(3, value.length);
  171. break;
  172. case 133:
  173. data.quantity = value[1];
  174. data.soc = value[1];
  175. value = value.slice(2, value.length);
  176. break;
  177. case 134:
  178. data.tempCount = value[1];
  179. value = value.slice(2, value.length);
  180. break;
  181. case 135:
  182. data.cycle = value[1] * 256 + value[2];
  183. value = value.slice(3, value.length);
  184. break;
  185. case 137:
  186. data.totalCirculatingCapacity = value[1] * 16777216 + value[2] * 65536 + value[3] * 256 + value[4];
  187. value = value.slice(5, value.length);
  188. break;
  189. case 138:
  190. data.count = value[1] * 256 + value[2];
  191. value = value.slice(3, value.length);
  192. break;
  193. case 139:
  194. data.alarmState = [];
  195. if (((value[2] >> 0) & 1) == 1) {
  196. data.alarmState.push('低容量报警');
  197. }
  198. if (((value[2] >> 1) & 1) == 1) {
  199. data.alarmState.push('MOS管超温报警');
  200. }
  201. if (((value[2] >> 2) & 1) == 1) {
  202. data.alarmState.push('充电过压报警');
  203. }
  204. if (((value[2] >> 3) & 1) == 1) {
  205. data.alarmState.push('放电欠压报警');
  206. }
  207. if (((value[2] >> 4) & 1) == 1) {
  208. data.alarmState.push('电池超温报警');
  209. }
  210. if (((value[2] >> 5) & 1) == 1) {
  211. data.alarmState.push('充电过流报警');
  212. }
  213. if (((value[2] >> 6) & 1) == 1) {
  214. data.alarmState.push('放电过流报警');
  215. }
  216. if (((value[2] >> 7) & 1) == 1) {
  217. data.alarmState.push('电芯压差报警');
  218. }
  219. if (((value[1] >> 0) & 1) == 1) {
  220. data.alarmState.push('电池箱内超温报警');
  221. }
  222. if (((value[1] >> 1) & 1) == 1) {
  223. data.alarmState.push('电池低温报警');
  224. }
  225. if (((value[1] >> 2) & 1) == 1) {
  226. data.alarmState.push('单体过压报警');
  227. }
  228. if (((value[1] >> 3) & 1) == 1) {
  229. data.alarmState.push('单体欠压报警');
  230. }
  231. if (((value[1] >> 4) & 1) == 1) {
  232. data.alarmState.push('309_A保护');
  233. }
  234. if (((value[1] >> 5) & 1) == 1) {
  235. data.alarmState.push('309_B保护');
  236. }
  237. value = value.slice(3, value.length);
  238. break;
  239. case 140:
  240. let _tmp = (value[1] << 8) | value[2];
  241. // data.chargeProtectState = (value[2] >> 0 & 1)
  242. // data.dischargeProtectState = (value[2] >> 1 & 1)
  243. // data.equilibrium = (value[2] >> 2 & 1)
  244. // data.disconnectState = (value[2] >> 3 & 1)
  245. data.chargeProtectState = (_tmp & 1) == 0 ? 0 : 1;
  246. data.dischargeProtectState = (_tmp & 2) == 0 ? 0 : 1;
  247. data.equilibrium = (_tmp & 4) == 0 ? 0 : 1;
  248. data.disconnectState = (_tmp & 8) == 0 ? 0 : 1;
  249. value = value.slice(3, value.length);
  250. //console.log(value[1] + " " . value[2] + " " + _tmp + " ;" + data.equilibrium)
  251. break;
  252. case 142:
  253. value = value.slice(3, value.length);
  254. break;
  255. case 143:
  256. value = value.slice(3, value.length);
  257. break;
  258. case 144:
  259. value = value.slice(3, value.length);
  260. break;
  261. case 145:
  262. case 146:
  263. case 147:
  264. case 148:
  265. case 149:
  266. case 150:
  267. case 151:
  268. case 152:
  269. case 153:
  270. case 154:
  271. case 155:
  272. case 156:
  273. value = value.slice(3, value.length);
  274. break;
  275. case 157:
  276. value = value.slice(2, value.length);
  277. break;
  278. case 158:
  279. case 159:
  280. case 160:
  281. case 161:
  282. case 162:
  283. case 163:
  284. case 164:
  285. case 165:
  286. case 166:
  287. case 167:
  288. case 168:
  289. value = value.slice(3, value.length);
  290. break;
  291. case 169:
  292. value = value.slice(2, value.length);
  293. break;
  294. case 170:
  295. // 电池容量
  296. data.capacity = (value[1] << 24) | (value[2] << 16) | (value[3] << 8) | value[4];
  297. value = value.slice(5, value.length);
  298. break;
  299. case 171:
  300. case 172:
  301. value = value.slice(2, value.length);
  302. break;
  303. case 173:
  304. value = value.slice(3, value.length);
  305. break;
  306. case 174:
  307. case 175:
  308. value = value.slice(2, value.length);
  309. break;
  310. case 176:
  311. value = value.slice(3, value.length);
  312. break;
  313. case 177:
  314. value = value.slice(2, value.length);
  315. break;
  316. case 178:
  317. value = value.slice(11, value.length);
  318. break;
  319. case 179:
  320. value = value.slice(2, value.length);
  321. break;
  322. case 180:
  323. value = value.slice(9, value.length);
  324. break;
  325. case 181:
  326. value = value.slice(5, value.length);
  327. break;
  328. case 182:
  329. value = value.slice(5, value.length);
  330. break;
  331. case 183:
  332. // 软件版本号 15bit
  333. data.firmware = '';
  334. for (let i = 1; i <= 15; i++) {
  335. let _value = 33;
  336. let hx = value[i];
  337. if (hx >= 33 && hx <= 126) {
  338. _value = hx;
  339. }
  340. data.firmware += String.fromCharCode(_value);
  341. }
  342. value = value.slice(16, value.length);
  343. break;
  344. case 184:
  345. value = value.slice(2, value.length);
  346. break;
  347. case 185:
  348. value = value.slice(5, value.length);
  349. break;
  350. case 186:
  351. value = value.slice(25, value.length);
  352. break;
  353. default:
  354. value = [];
  355. }
  356. }
  357. }
  358. return data;
  359. }
  360. }
  361. return false;
  362. }
  363. function readData(device, value, data) {
  364. value = Array.from(new Int8Array(value))
  365. .map((p) => String.fromCharCode(p))
  366. .join('');
  367. console.log(value);
  368. if (value[value.length - 1] == ')') {
  369. value = readPart.concat(value);
  370. readPart = '';
  371. } else if (value[0] == '(') {
  372. readPart = value;
  373. } else {
  374. readPart = readPart.concat(value);
  375. }
  376. console.log(value);
  377. if (value[0] == '(' && value[value.length - 1] == ')') {
  378. value = value.slice(1, value.length - 1);
  379. if (value.slice(0, 12) == device.mac_id) {
  380. value = value.slice(12, value.length);
  381. switch (value.slice(0, 4)) {
  382. case 'BP00':
  383. return readDataBP00(value.slice(4, value.length), data);
  384. case 'BS50':
  385. return readDataBS50(value.slice(4, value.length), data);
  386. }
  387. }
  388. }
  389. return false;
  390. }
  391. /**
  392. * cmd: 命令字 02:写
  393. */
  394. function BMSCommand(data, cmd, type = 0) {
  395. // 帧来源: 01
  396. // 传输类型: 00
  397. const length = data.length + 18;
  398. 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]);
  399. const sum = data.reduce((p, c) => p + c);
  400. data.push(parseInt(sum / 16777216));
  401. data.push(parseInt((sum % 16777216) / 65536));
  402. data.push(parseInt((sum % 65536) / 256));
  403. data.push(parseInt(sum % 256));
  404. console.log('222222');
  405. console.log(data);
  406. return data;
  407. }
  408. function BMSRead() {
  409. return BMSCommand([0], 6, 0);
  410. }
  411. function BMSTurnOn() {
  412. return BMSCommand([172, 1], 2, 1);
  413. }
  414. function BMSTurnOff() {
  415. return BMSCommand([172, 0], 2, 1);
  416. }
  417. function bmsChargingMOS(value) {
  418. return BMSCommand([171, value], 2, 0);
  419. }
  420. function bmsDischargeMOS(value, device) {
  421. return BMSCommand([172, value], 2, 0);
  422. }
  423. function sendData(device, cmd, data = []) {
  424. return (
  425. '(' +
  426. device.mac_id +
  427. cmd +
  428. data
  429. .map((p) => ('00' + p.toString(16)).substr(p.toString(16).length))
  430. .join('')
  431. .toUpperCase() +
  432. ')'
  433. )
  434. .split('')
  435. .map((p) => p.charCodeAt(0));
  436. }
  437. function sendDataAE00(device, data) {
  438. return sendData(device, 'AE00', [data.length % 256, parseInt(data.length / 256)].concat(data));
  439. }
  440. function stateUpdate(device, deviceId) {
  441. return [sendData(device, 'AU20'), sendDataAE00(device, BMSRead())];
  442. }
  443. function turnOn(device, deviceId) {
  444. return [sendDataAE00(device, BMSTurnOn())];
  445. }
  446. function turnOff(device, deviceId) {
  447. return [sendDataAE00(device, BMSTurnOff())];
  448. }
  449. function bmsInfo(device, deviceId, info) {
  450. console.log('--------');
  451. console.log(info);
  452. return false;
  453. }
  454. function bmsSet(device) {
  455. return false;
  456. }
  457. module.exports = {
  458. readServiceID: readServiceID,
  459. readID: readID,
  460. writeServiceID: writeServiceID,
  461. writeID: writeID,
  462. acceptDevice: acceptDevice,
  463. isDevice: isDevice,
  464. readData: readData,
  465. stateUpdate: stateUpdate,
  466. turnOn: turnOn,
  467. turnOff: turnOff,
  468. bmsInfo: bmsInfo,
  469. bmsSet: bmsSet,
  470. BMSCommand: BMSCommand,
  471. BMSRead: BMSRead,
  472. BMSTurnOn: BMSTurnOn,
  473. BMSTurnOff: BMSTurnOff,
  474. BMSReply: BMSReply,
  475. bmsChargingMOS: bmsChargingMOS,
  476. bmsDischargeMOS: bmsDischargeMOS
  477. };