{"id":19719,"date":"2026-08-07T11:50:05","date_gmt":"2026-08-07T03:50:05","guid":{"rendered":"https:\/\/www.lansitec.com\/?post_type=docs&#038;p=19719"},"modified":"2026-08-07T11:50:13","modified_gmt":"2026-08-07T03:50:13","password":"","slug":"decoder-lorawan-uwb-tracker-ttn-chirpstack","status":"publish","type":"docs","link":"https:\/\/www.lansitec.com\/ar\/docs\/decoders\/decoder-lorawan-uwb-tracker-ttn-chirpstack\/","title":{"rendered":"\u062c\u0647\u0627\u0632 \u0641\u0643 \u062a\u0634\u0641\u064a\u0631 LoRaWAN - \u0645\u062a\u062a\u0628\u0639 UWB (TTN\/Chirpstack)"},"content":{"rendered":"<div class=\"wp-block-file\"><a id=\"wp-block-file--media-7ac40e11-009a-4fcf-9573-e18fad6f0f40\" href=\"https:\/\/www.lansitec.com\/wp-content\/uploads\/2026\/08\/decode-uwb-tracker.js.zip\">decode-uwb-tracker.js<\/a><a href=\"https:\/\/www.lansitec.com\/wp-content\/uploads\/2026\/08\/decode-uwb-tracker.js.zip\" class=\"wp-block-file__button wp-element-button\" download aria-describedby=\"wp-block-file--media-7ac40e11-009a-4fcf-9573-e18fad6f0f40\">\u062a\u062d\u0645\u064a\u0644<\/a><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Below the full decoder code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ UWB\nfunction decodeUplink(input) {\n  \/\/ bytes\n  var bytes = input.bytes;\n  \/\/ type\n  var type = (bytes&#91;0] >> 4) &amp; 0x0f;\n\n  switch (type) {\n    case 0x01:\n      return { data: decodeRegistration(bytes) };\n\n    case 0x02:\n      return { data: decodeHeartbeat(bytes) };\n     \n    case 0x05:\n      return { data: decodeAlarm(bytes) };  \n\n    case 0x06:\n      return { data: decodeSingleConfigurationParameter(bytes) };\n\n    case 0x08:\n      return { data: decodeDistance(bytes) };\n\n    default:\n      return;\n  }\n}\n\n\/\/ type: 0x1 Registration\nfunction decodeRegistration(bytes) {\n  var data = {};\n  data.type = \"RegistrationMessage\";\n  data.softwareVersion = \"V\" + (((bytes&#91;1] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;2] &amp; 0xff)).toString(16).toUpperCase().padStart(4, \"0\");\n  data.hardwareVersion = \"V\" + (((bytes&#91;3] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;4] &amp; 0xff)).toString(16).toUpperCase().padStart(4, \"0\");\n  data.loraClockSyncInterval = (((bytes&#91;9] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;10] &amp; 0xff)) + \"s\";\n  data.loraClockSyncFreq = ((bytes&#91;11] &lt;&lt; 24) | (bytes&#91;12] &lt;&lt; 16) | (bytes&#91;13] &lt;&lt; 8) | bytes&#91;14]) \/ 1000000;\n  data.loraClockSyncSF = bytes&#91;15];\n  var loraClockSyncBWValue = bytes&#91;16];\n  if (loraClockSyncBWValue == 0) {\n    data.loraClockSyncBW = \"500kHz\";\n  } else if (loraClockSyncBWValue == 1) {\n    data.loraClockSyncBW = \"250kHz\";\n  } else if (loraClockSyncBWValue == 2) {\n    data.loraClockSyncBW = \"125kHz\";\n  }\n  data.heartbeatInterval = (((bytes&#91;17] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;18] &amp; 0xff)) * 30 + \"s\";\n\n  return data;\n}\n\n\/\/ type: 0x2 Heartbeat\nfunction decodeHeartbeat(bytes) {\n  var data = {};\n  data.type = \"HeartbeatMessage\";\n  data.batteryVoltage = (bytes&#91;5] * 0.1).toFixed(1) + \"V\";\n  data.batteryPercentage = bytes&#91;6] + \"%\";\n  data.uwbPositioningCount = bytes&#91;7];\n  data.temperature = (((bytes&#91;10] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;11] &amp; 0xff)) + \"\u00b0C\";\n  data.chargeTime = (((bytes&#91;16] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;17] &amp; 0xff)) * 30 + \"s\";\n\n  return data;\n}\n\n\/\/ type: 0x5 Alarm\nfunction decodeAlarm(bytes) {\n  var data = {};\n  data.type = \"Alarm\";\n  var alarmValue = bytes&#91;1] &amp; 0xff;\n  if (alarmValue == 1) {\n    data.alarm = \"SOS\";\n  } else if (alarmValue == 2) {\n    data.alarm = \"Fall\";\n  } \n  return data;\n}\n\n\/\/ type: 0x6 Single Configuration Parameter\nfunction decodeSingleConfigurationParameter(bytes) {\n  var data = {};\n  data.type = \"ConfigurationParameterResponse\";\n  var parameter = &#91;];\n  let byteLength = bytes.length;\n  var index = 0;\n  while (index + 1 &lt; byteLength) {\n    var commandBitField = {};\n    var parameterTypevalue = bytes&#91;index + 1] &amp; 0xff;\n    commandBitField.parameterType = parameterTypevalue.toString(16).toUpperCase().padStart(2, \"0\");\n    var commandBitFieldLength = getCommandBitFieldLength(parameterTypevalue);\n    var parameterValue = getParameterValue(bytes, index, commandBitFieldLength);\n    commandBitField.parameterValue = parameterValue;\n    commandBitField.name = getParameterName(parameterTypevalue);\n    commandBitField.parameterDefinition = getParameterDefinition(\n      parameterTypevalue,\n      parameterValue\n    );\n    index = index + commandBitFieldLength;\n    parameter.push(commandBitField);\n  }\n  data.parameter = parameter;\n  return data;\n}\n\n\/\/ type: 0x8 Distance\nfunction decodeDistance(bytes) {\n  var data = {};\n  data.type = \"Distance\";\n  data.uwbAnchorQuantity = bytes&#91;1] &amp; 0x0f;\n  for (let i = 0; i &lt; data.uwbAnchorQuantity; i++) {\n    var index = 2 + 7 * i;\n    var dev = (((bytes&#91;index] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;index + 1] &amp; 0xff))\n      .toString(16)\n      .toUpperCase()\n      .padStart(4, \"0\");\n    var eui = (((bytes&#91;index + 2] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;index + 3] &amp; 0xff))\n      .toString(16)\n      .toUpperCase()\n      .padStart(4, \"0\");\n\n    data&#91;\"deveui\" + (i + 1)] = dev + eui;\n    data&#91;\"distance\" + (i + 1)] =\n      ((bytes&#91;index + 4] &lt;&lt; 16) | (bytes&#91;index + 5] &lt;&lt; 8) | bytes&#91;index + 6]) + \"cm\";\n  }\n\n  return data;\n}\n\n\/\/ getParameterValue hexString\nfunction getParameterValue(bytes, index, length) {\n  var hexString = \"\";\n  for (var i = 2; i &lt;= length; i++) {\n    var hex = (bytes&#91;index + i] &amp; 0xff).toString(16).toUpperCase();\n    hexString += hex.padStart(2, \"0\");\n  }\n  return hexString;\n}\n\n\/\/ getCommandBitFieldLength\nfunction getCommandBitFieldLength(parameterType) {\n  let lengths = {\n    0x00: 3,\n    0x01: 3,\n    0x08: 2,\n    0x32: 2,\n    0x33: 2,\n    0x52: 3,\n    0x53: 5,\n    0x54: 2,\n    0x55: 2,\n    0x61: 3,\n  };\n  return lengths&#91;parameterType] ?? 0;\n}\n\n\/\/ Parameter Name\nfunction getParameterName(parameterType) {\n  let name = {\n    0x00: \"SoftwareVersion\",\n    0x01: \"HBPeriod\",\n    0x08: \"FallDetectionThreshold\",\n    0x32: \"CFMMSG\",\n    0x33: \"HBCOUNT\",\n    0x52: \"LoRaCLKSYNCInterval\",\n    0x53: \"LoRaCLKSYNCFrequency\",\n    0x54: \"LoRaCLKSYNCSF\",\n    0x55: \"LoRaCLKSYNCBW\",\n    0x61: \"PositionReportInterval\",\n  };\n  return name&#91;parameterType] ?? \"No matching parameter names\";\n}\n\n\/\/ Parameter Definition\nfunction getParameterDefinition(parameterType, parameterValue) {\n  var val = parseInt(parameterValue, 16);\n  let name = {\n    0x00: \"SoftwareVersion\",\n    0x01: val * 30 + \"s, The interval of the heartbeat message, unit: 30s\",\n    0x08: val * 0.5 + \"m, The threshold of the fall detection, unit: 0.5m\",\n    0x32: val + \" The interval of messages that must be acknowledged\",\n    0x33: val + \" The number of heartbeat ACK that the tracker misses\",\n    0x52: val + \"s, LoRa clock synchronization interval, unit: 1s\",\n    0x53: val + \" LoRa clock synchronization frequency\",\n    0x54: val + \" LoRa clock synchronization spreading factor\",\n    0x55: val + \" LoRa clock synchronization bandwidth\",\n    0x61: val + \"s, The tracker's UWB position report interval, unit: 1s\",\n  };\n  return name&#91;parameterType] ?? \"No matching parameter names\";\n}\n\n\/\/ Floating point conversion\nfunction hex2float(num) {\n  num = num >>> 0;\n\n  var sign = (num &amp; 0x80000000) ? -1 : 1;\n  var exponent = (num >>> 23) &amp; 0xff;\n  var fraction = num &amp; 0x7fffff;\n\n  if (exponent === 0xff) {\n    return fraction === 0 ? sign * Infinity : NaN;\n  }\n\n  if (exponent === 0) {\n    return fraction === 0 ? sign * 0 : sign * Math.pow(2, -126) * (fraction \/ 0x800000);\n  }\n\n  return sign * Math.pow(2, exponent - 127) * (1 + fraction \/ 0x800000);\n}\n\nfunction asciiToHex(str) {\n  var hexString = \"\";\n  for (let i = 0; i &lt; str.length; i++) {\n    var hex = str.charCodeAt(i).toString(16);\n    hexString += hex.padStart(2, \"0\");\n  }\n  return hexString;\n}\n\nfunction hexStringToByteArray(hexString) {\n  var byteArray = &#91;];\n  for (let i = 0; i &lt; hexString.length; i += 2) {\n    byteArray.push(parseInt(hexString.substr(i, 2), 16));\n  }\n  return new Uint8Array(byteArray);\n}\n\nfunction timestampToTime(timestamp) {\n  const date = new Date(timestamp);\n  const year = date.getFullYear();\n  const month = (date.getMonth() + 1).toString().padStart(2, \"0\");\n  const day = date.getDate().toString().padStart(2, \"0\");\n  const hour = date.getHours().toString().padStart(2, \"0\");\n  const minute = date.getMinutes().toString().padStart(2, \"0\");\n  const second = date.getSeconds().toString().padStart(2, \"0\");\n  return `${year}-${month}-${day} ${hour}:${minute}:${second}`;\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>","protected":false},"excerpt":{"rendered":"<p>Below the full decoder code:<\/p>","protected":false},"author":5,"featured_media":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"neve_meta_sidebar":"","neve_meta_container":"","neve_meta_enable_content_width":"","neve_meta_content_width":0,"neve_meta_title_alignment":"","neve_meta_author_avatar":"","neve_post_elements_order":"","neve_meta_disable_header":"","neve_meta_disable_footer":"","neve_meta_disable_title":"","footnotes":""},"doc_category":[338],"doc_tag":[],"class_list":["post-19719","docs","type-docs","status-publish","hentry","doc_category-decoders"],"year_month":"2026-08","word_count":719,"total_views":"21","reactions":{"happy":"0","normal":"0","sad":"0"},"author_info":{"name":"\u0645\u0627\u064a\u0632\u064a","author_nicename":"maizi","author_url":"https:\/\/www.lansitec.com\/ar\/author\/maizi\/"},"doc_category_info":[{"term_name":"Decoders","term_url":"https:\/\/www.lansitec.com\/ar\/doc\/decoders\/"}],"doc_tag_info":[],"knowledge_base_info":[],"knowledge_base_slug":[],"_links":{"self":[{"href":"https:\/\/www.lansitec.com\/ar\/wp-json\/wp\/v2\/docs\/19719","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lansitec.com\/ar\/wp-json\/wp\/v2\/docs"}],"about":[{"href":"https:\/\/www.lansitec.com\/ar\/wp-json\/wp\/v2\/types\/docs"}],"author":[{"embeddable":true,"href":"https:\/\/www.lansitec.com\/ar\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lansitec.com\/ar\/wp-json\/wp\/v2\/comments?post=19719"}],"version-history":[{"count":1,"href":"https:\/\/www.lansitec.com\/ar\/wp-json\/wp\/v2\/docs\/19719\/revisions"}],"predecessor-version":[{"id":19722,"href":"https:\/\/www.lansitec.com\/ar\/wp-json\/wp\/v2\/docs\/19719\/revisions\/19722"}],"wp:attachment":[{"href":"https:\/\/www.lansitec.com\/ar\/wp-json\/wp\/v2\/media?parent=19719"}],"wp:term":[{"taxonomy":"doc_category","embeddable":true,"href":"https:\/\/www.lansitec.com\/ar\/wp-json\/wp\/v2\/doc_category?post=19719"},{"taxonomy":"doc_tag","embeddable":true,"href":"https:\/\/www.lansitec.com\/ar\/wp-json\/wp\/v2\/doc_tag?post=19719"}],"curies":[{"name":"\u0648\u0648\u0631\u062f\u0628\u0631\u064a\u0633","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}