Advertisement
lemnalord

TTN Formatter

Jun 8th, 2023
45
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.31 KB | None | 0 0
  1. /**
  2. * SenseCAP & TTN (new v3) Converter
  3. *
  4. * @since 3.0
  5. * @return Object
  6. * @param Boolean valid Indicates whether the payload is a valid payload.
  7. * @param String err The reason for the payload to be invalid. 0 means valid, minus means invalid.
  8. * @param String payload Hexadecimal string, to show the payload.
  9. * @param Array messages One or more messages are parsed according to payload.
  10. * type // Enum:
  11. * // - "report_telemetry"
  12. * // - "upload_battery"
  13. * // - "upload_interval"
  14. * // - "upload_version"
  15. * // - "upload_sensor_id"
  16. * // - "report_remove_sensor"
  17. * // - "unknown_message"
  18. *
  19. *
  20. *
  21. *
  22. * @sample-1
  23. * var sample = Decoder(["00", "00", "00", "01", "01", "00", "01", "00", "07", "00", "64", "00", "3C", "00", "01", "20", "01", "00", "00", "00", "00", "28", "90"], null);
  24. * {
  25. * valid: true,
  26. * err: 0,
  27. * payload: '0000000101000100070064003C00012001000000002890',
  28. * messages: [
  29. * { type: 'upload_version',
  30. * hardwareVersion: '1.0',
  31. * softwareVersion: '1.1' },
  32. * { type: 'upload_battery', battery: 100 },
  33. * { type: 'upload_interval', interval: 3600 },
  34. * { type: 'report_remove_sensor', channel: 1 }
  35. * ]
  36. * }
  37. * @sample-2
  38. * var sample = Decoder(["01", "01", "10", "98", "53", "00", "00", "01", "02", "10", "A8", "7A", "00", "00", "AF", "51"], null);
  39. * {
  40. * valid: true,
  41. * err: 0,
  42. * payload: '01011098530000010210A87A0000AF51',
  43. * messages: [
  44. * { type: 'report_telemetry',
  45. * measurementId: 4097,
  46. * measurementValue: 21.4 },
  47. * { type: 'report_telemetry',
  48. * measurementId: 4098,
  49. * measurementValue: 31.4 }
  50. * ]
  51. * }
  52. * @sample-3
  53. * var sample = Decoder(["01", "01", "00", "01", "01", "00", "01", "01", "02", "00", "6A", "01", "00", "15", "01", "03", "00", "30", "F1", "F7", "2C", "01", "04", "00", "09", "0C", "13", "14", "01", "05", "00", "7F", "4D", "00", "00", "01", "06", "00", "00", "00", "00", "00", "4C", "BE"], null);
  54. * {
  55. * valid: true,
  56. * err: 0,
  57. * payload: '010100010100010102006A01001501030030F1F72C010400090C13140105007F4D0000010600000000004CBE',
  58. * messages: [
  59. * { type: 'upload_sensor_id', sensorId: '30F1F72C6A010015', channel: 1 }
  60. * ]
  61. * }
  62. */
  63.  
  64. /**
  65. * Entry, decoder.js
  66. */
  67. function decodeUplink(input) {
  68. var bytes = input['bytes'];
  69. // init
  70. var bytesString = bytes2HexString(bytes).toLocaleUpperCase();
  71. var decoded = {
  72. // valid
  73. valid: true,
  74. err: 0,
  75. // bytes
  76. payload: bytesString,
  77. // messages array
  78. messages: [],
  79. };
  80.  
  81. // CRC check
  82. if (!crc16Check(bytesString)) {
  83. decoded['valid'] = false;
  84. decoded['err'] = -1; // "crc check fail."
  85. return { data: decoded };
  86. }
  87.  
  88. // Length Check
  89. if ((bytesString.length / 2 - 2) % 7 !== 0) {
  90. decoded['valid'] = false;
  91. decoded['err'] = -2; // "length check fail."
  92. return { data: decoded };
  93. }
  94.  
  95. // Cache sensor id
  96. var sensorEuiLowBytes;
  97. var sensorEuiHighBytes;
  98.  
  99. // Handle each frame
  100. var frameArray = divideBy7Bytes(bytesString);
  101. for (var forFrame = 0; forFrame < frameArray.length; forFrame++) {
  102. var frame = frameArray[forFrame];
  103. // Extract key parameters
  104. var channel = strTo10SysNub(frame.substring(0, 2));
  105. var dataID = strTo10SysNub(frame.substring(2, 6));
  106. var dataValue = frame.substring(6, 14);
  107. var realDataValue = isSpecialDataId(dataID) ? ttnDataSpecialFormat(dataID, dataValue) : ttnDataFormat(dataValue);
  108.  
  109. if (checkDataIdIsMeasureUpload(dataID)) {
  110. // if telemetry.
  111. decoded.messages.push({
  112. type: 'report_telemetry',
  113. measurementId: dataID,
  114. measurementValue: realDataValue,
  115. });
  116. } else if (isSpecialDataId(dataID) || dataID === 5 || dataID === 6) {
  117. // if special order, except "report_sensor_id".
  118. switch (dataID) {
  119. case 0x00:
  120. // node version
  121. var versionData = sensorAttrForVersion(realDataValue);
  122. decoded.messages.push({
  123. type: 'upload_version',
  124. hardwareVersion: versionData.ver_hardware,
  125. softwareVersion: versionData.ver_software,
  126. });
  127. break;
  128. case 1:
  129. // sensor version
  130. break;
  131. case 2:
  132. // sensor eui, low bytes
  133. sensorEuiLowBytes = realDataValue;
  134. break;
  135. case 3:
  136. // sensor eui, high bytes
  137. sensorEuiHighBytes = realDataValue;
  138. break;
  139. case 7:
  140. // battery power && interval
  141. decoded.messages.push(
  142. {
  143. type: 'upload_battery',
  144. battery: realDataValue.power,
  145. },
  146. {
  147. type: 'upload_interval',
  148. interval: parseInt(realDataValue.interval) * 60,
  149. }
  150. );
  151. break;
  152. case 0x120:
  153. // remove sensor
  154. decoded.messages.push({
  155. type: 'report_remove_sensor',
  156. channel: 1,
  157. });
  158. break;
  159. default:
  160. break;
  161. }
  162. } else {
  163. decoded.messages.push({
  164. type: 'unknown_message',
  165. dataID: dataID,
  166. dataValue: dataValue,
  167. });
  168. }
  169. }
  170.  
  171. // if the complete id received, as "upload_sensor_id"
  172. if (sensorEuiHighBytes && sensorEuiLowBytes) {
  173. decoded.messages.unshift({
  174. type: 'upload_sensor_id',
  175. channel: 1,
  176. sensorId: (sensorEuiHighBytes + sensorEuiLowBytes).toUpperCase(),
  177. });
  178. }
  179.  
  180. // return
  181. return { data: decoded };
  182. }
  183.  
  184. function crc16Check(data) {
  185. return true;
  186. }
  187.  
  188. // util
  189. function bytes2HexString(arrBytes) {
  190. var str = '';
  191. for (var i = 0; i < arrBytes.length; i++) {
  192. var tmp;
  193. var num = arrBytes[i];
  194. if (num < 0) {
  195. tmp = (255 + num + 1).toString(16);
  196. } else {
  197. tmp = num.toString(16);
  198. }
  199. if (tmp.length === 1) {
  200. tmp = '0' + tmp;
  201. }
  202. str += tmp;
  203. }
  204. return str;
  205. }
  206.  
  207. // util
  208. function divideBy7Bytes(str) {
  209. var frameArray = [];
  210. for (var i = 0; i < str.length - 4; i += 14) {
  211. var data = str.substring(i, i + 14);
  212. frameArray.push(data);
  213. }
  214. return frameArray;
  215. }
  216.  
  217. // util
  218. function littleEndianTransform(data) {
  219. var dataArray = [];
  220. for (var i = 0; i < data.length; i += 2) {
  221. dataArray.push(data.substring(i, i + 2));
  222. }
  223. dataArray.reverse();
  224. return dataArray;
  225. }
  226.  
  227. // util
  228. function strTo10SysNub(str) {
  229. var arr = littleEndianTransform(str);
  230. return parseInt(arr.toString().replace(/,/g, ''), 16);
  231. }
  232.  
  233. // util
  234. function checkDataIdIsMeasureUpload(dataId) {
  235. return parseInt(dataId) > 4096;
  236. }
  237.  
  238. // configurable.
  239. function isSpecialDataId(dataID) {
  240. switch (dataID) {
  241. case 0:
  242. case 1:
  243. case 2:
  244. case 3:
  245. case 4:
  246. case 7:
  247. case 0x120:
  248. return true;
  249. default:
  250. return false;
  251. }
  252. }
  253.  
  254. // configurable
  255. function ttnDataSpecialFormat(dataId, str) {
  256. var strReverse = littleEndianTransform(str);
  257. if (dataId === 2 || dataId === 3) {
  258. return strReverse.join('');
  259. }
  260.  
  261. // handle unsigned number
  262. var str2 = toBinary(strReverse);
  263.  
  264. var dataArray = [];
  265. switch (dataId) {
  266. case 0: // DATA_BOARD_VERSION
  267. case 1: // DATA_SENSOR_VERSION
  268. // Using point segmentation
  269. for (var k = 0; k < str2.length; k += 16) {
  270. var tmp146 = str2.substring(k, k + 16);
  271. tmp146 = (parseInt(tmp146.substring(0, 8), 2) || 0) + '.' + (parseInt(tmp146.substring(8, 16), 2) || 0);
  272. dataArray.push(tmp146);
  273. }
  274. return dataArray.join(',');
  275. case 4:
  276. for (var i = 0; i < str2.length; i += 8) {
  277. var item = parseInt(str2.substring(i, i + 8), 2);
  278. if (item < 10) {
  279. item = '0' + item.toString();
  280. } else {
  281. item = item.toString();
  282. }
  283. dataArray.push(item);
  284. }
  285. return dataArray.join('');
  286. case 7:
  287. // battery && interval
  288. return {
  289. interval: parseInt(str2.substr(0, 16), 2),
  290. power: parseInt(str2.substr(-16, 16), 2),
  291. };
  292. }
  293. }
  294.  
  295. // util
  296. function ttnDataFormat(str) {
  297. var strReverse = littleEndianTransform(str);
  298. var str2 = toBinary(strReverse);
  299. if (str2.substring(0, 1) === '1') {
  300. var arr = str2.split('');
  301. var reverseArr = [];
  302. for (var forArr = 0; forArr < arr.length; forArr++) {
  303. var item = arr[forArr];
  304. if (parseInt(item) === 1) {
  305. reverseArr.push(0);
  306. } else {
  307. reverseArr.push(1);
  308. }
  309. }
  310. str2 = parseInt(reverseArr.join(''), 2) + 1;
  311. return parseFloat('-' + str2 / 1000);
  312. }
  313. return parseInt(str2, 2) / 1000;
  314. }
  315.  
  316. // util
  317. function sensorAttrForVersion(dataValue) {
  318. var dataValueSplitArray = dataValue.split(',');
  319. return {
  320. ver_hardware: dataValueSplitArray[0],
  321. ver_software: dataValueSplitArray[1],
  322. };
  323. }
  324.  
  325. // util
  326. function toBinary(arr) {
  327. var binaryData = [];
  328. for (var forArr = 0; forArr < arr.length; forArr++) {
  329. var item = arr[forArr];
  330. var data = parseInt(item, 16).toString(2);
  331. var dataLength = data.length;
  332. if (data.length !== 8) {
  333. for (var i = 0; i < 8 - dataLength; i++) {
  334. data = '0' + data;
  335. }
  336. }
  337. binaryData.push(data);
  338. }
  339. return binaryData.toString().replace(/,/g, '');
  340. }
  341.  
  342. // Samples
  343. // var sample = Decoder(["00", "00", "00", "01", "01", "00", "01", "00", "07", "00", "64", "00", "3C", "00", "01", "20", "01", "00", "00", "00", "00", "28", "90"], null);
  344. // var sample = Decoder(["01", "01", "10", "98", "53", "00", "00", "01", "02", "10", "A8", "7A", "00", "00", "AF", "51"], null);
  345. // var sample = Decoder(["01", "01", "00", "01", "01", "00", "01", "01", "02", "00", "6A", "01", "00", "15", "01", "03", "00", "30", "F1", "F7", "2C", "01", "04", "00", "09", "0C", "13", "14", "01", "05", "00", "7F", "4D", "00", "00", "01", "06", "00", "00", "00", "00", "00", "4C", "BE"], null);
  346. // console.log(sample);
  347.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement