{"id":19706,"date":"2026-08-07T11:39:11","date_gmt":"2026-08-07T03:39:11","guid":{"rendered":"https:\/\/www.lansitec.com\/?post_type=docs&#038;p=19706"},"modified":"2026-08-07T11:39:19","modified_gmt":"2026-08-07T03:39:19","password":"","slug":"decoder-lorawan-helmet-tracker-ttn-chirpstack","status":"publish","type":"docs","link":"https:\/\/www.lansitec.com\/ko\/docs\/decoders\/decoder-lorawan-helmet-tracker-ttn-chirpstack\/","title":{"rendered":"LoRaWAN \ub514\ucf54\ub354 \u2013 \ud5ec\uba67 \ud2b8\ub798\ucee4(TTN\/Chirpstack)"},"content":{"rendered":"<div class=\"wp-block-file\"><a id=\"wp-block-file--media-a1fe2a91-a7ef-4081-958c-b4d227d0178a\" href=\"https:\/\/www.lansitec.com\/wp-content\/uploads\/2026\/08\/decode-helmet-tracker-ttn_chirpstackv4.js.zip\">decode-helmet-tracker-ttn_chirpstackv4.js<\/a><a href=\"https:\/\/www.lansitec.com\/wp-content\/uploads\/2026\/08\/decode-helmet-tracker-ttn_chirpstackv4.js.zip\" class=\"wp-block-file__button wp-element-button\" download aria-describedby=\"wp-block-file--media-a1fe2a91-a7ef-4081-958c-b4d227d0178a\">\ub2e4\uc6b4\ub85c\ub4dc<\/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>\/\/ Decode uplink function.\n\/\/\n\/\/ Input is an object with the following fields:\n\/\/ - bytes = Byte array containing the uplink payload, e.g. &#91;255, 230, 255, 0]\n\/\/ - fPort = Uplink fPort.\n\/\/ - variables = Object containing the configured device variables.\n\/\/\n\/\/ Output must be an object with the following fields:\n\/\/ - data = Object representing the decoded payload.\n\n\/\/ Helmet Tracker\nfunction decodeUplink(input) {\n  \/\/ bytes\n  var bytes = input.bytes;\n  \/\/ type\n  var uplinkType = (bytes&#91;0] >> 4) &amp; 0x0f;\n  switch (uplinkType) {\n    case 0x01:\n      return { data: decodeRegistration(bytes) };\n\n    case 0x02:\n      return { data: decodeHeartbeat(bytes) };\n\n    case 0x03:\n      return { data: decodeGNSSPosition(bytes) };\n\n    case 0x05:\n      return { data: decodeUUIDReport(bytes) };\n\n    case 0x07:\n      return { data: decodeBeacon(bytes) };\n\n    case 0x08:\n      return { data: decodeAlarm(bytes) };\n\n    case 0x0c:\n\t    return { data: decodeBracelet(bytes) };\n\n    default:\n      return null;\n  }\n}\n\n\/\/ type: 0x1 Registration\nfunction decodeRegistration(bytes) {\n  var data = {};\n  data.type = \"Registration\";\n  \/\/ adr\n  data.adr = ((bytes&#91;0] >> 3) &amp; 0x1) == 0 ? \"OFF\" : \"ON\";\n  \/\/ power\n  data.power = ((bytes&#91;2] >> 3) &amp; 0x1f) + \"dBm\";\n  \/\/ dr\n  data.dr = (bytes&#91;3] >> 4) &amp; 0x0f;\n  \/\/ gnssEnable\n  data.gnssEnable = ((bytes&#91;3] >> 3) &amp; 0x01) == 0 ? \"Disable\" : \"Enable\";\n  \/\/ positionReportMode\n  var positionReportMode = (bytes&#91;3] >> 1) &amp; 0x03;\n  if (positionReportMode == 0) {\n    data.positionReportMode = \"Period\";\n  } else if (positionReportMode == 0) {\n    data.positionReportMode = \"Autonomous\";\n  } else if (positionReportMode == 0) {\n    data.positionReportMode = \"On-Demand\";\n  }\n  \/\/ bleEnable\n  data.bleEnable = (bytes&#91;3] &amp; 0x01) == 0 ? \"Disable\" : \"Enable\";\n  \/\/ blePositionReportInterval\n  data.blePositionReportInterval =\n    (((bytes&#91;4] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;5] &amp; 0xff)) * 5 + \"s\";\n  \/\/ gnssPositionReportInterval\n  data.gnssPositionReportInterval =\n    (((bytes&#91;6] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;7] &amp; 0xff)) * 5 + \"s\";\n  \/\/ heartbeatPeriod\n  data.heartbeatPeriod = (bytes&#91;8] &amp; 0xff) * 30 + \"s\";\n  \/\/ version\n  data.version =\n    (bytes&#91;9] &amp; 0xff).toString(16).toUpperCase() +\n    \".\" +\n    (bytes&#91;10] &amp; 0xff).toString(16).toUpperCase();\n  \/\/ cfmmsg\n  data.cfmmsg = \"1 Confirmed every \" + (bytes&#91;11] &amp; 0xff) + \" Heartbeat\";\n  \/\/ hbCount\n  data.hbCount = \"Disconnect Judgement \" + (bytes&#91;12] &amp; 0xff);\n  \/\/ fallDetectFeatureThreshold\n  data.fallDetectFeatureThreshold = (bytes&#91;13] &amp; 0xff) * 0.5 + \" meters\";\n\n  return data;\n}\n\n\/\/ type: 0x2 Heartbeat\nfunction decodeHeartbeat(bytes) {\n  var data = {};\n  \/\/ type\n  data.type = \"Heartbeat\";\n  \/\/ battery\n  data.battery = bytes&#91;1] + \"%\";\n  \/\/ rssi\n  data.rssi = bytes&#91;2] * -1 + \"dBm\";\n  \/\/ snr\n  data.snr = (((bytes&#91;3] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;4] &amp; 0xff)) \/ 100 + \"dB\";\n  \/\/ bleReceivingNumber\n  data.bleReceivingNumber = bytes&#91;5];\n  \/\/ gnssSearchingNumber\n  data.gnssSearchingNumber = bytes&#91;6];\n  \/\/ chargeTime\n  data.chargeTime = bytes&#91;7] * 30 + \"s\";\n  \/\/ wearTime\n  data.wearTime = bytes&#91;8] * 30 + \"s\";\n  \/\/ moveState\n  data.moveState = (bytes&#91;9] >> 4) &amp; 0x0f;\n  \/\/ temperature\n  data.temperature = 0;\n\n  return data;\n}\n\n\/\/ type: 0x3 GNSSPosition\nfunction decodeGNSSPosition(bytes) {\n  var data = {};\n  \/\/ type\n  data.type = \"GNSSPosition\";\n  \/\/ gnssState\n  data.gnssState = ((bytes&#91;0] >> 3) &amp; 0x01) == 0 ? \"Success\" : \"Fail\";\n  \/\/ wearState\n  data.wearState = (bytes&#91;0] &amp; 0x01) == 0 ? \"Do not wear\" : \"Wear\";\n  \/\/ pressure\n  let pressure =\n    (bytes&#91;1] &lt;&lt; 24) | (bytes&#91;2] &lt;&lt; 16) | (bytes&#91;3] &lt;&lt; 8) | bytes&#91;4];\n  data.pressure = pressure \/ 10 + \"pa\";\n  \/\/ longitude\n  let longitude =\n    (bytes&#91;5] &lt;&lt; 24) | (bytes&#91;6] &lt;&lt; 16) | (bytes&#91;7] &lt;&lt; 8) | bytes&#91;8];\n  data.longitude = hex2float(longitude);\n  \/\/ latitude\n  let latitude =\n    (bytes&#91;9] &lt;&lt; 24) | (bytes&#91;10] &lt;&lt; 16) | (bytes&#91;11] &lt;&lt; 8) | bytes&#91;12];\n  data.latitude = hex2float(latitude);\n  \/\/ time\n  let time =\n    (bytes&#91;13] &lt;&lt; 24) | (bytes&#91;14] &lt;&lt; 16) | (bytes&#91;15] &lt;&lt; 8) | bytes&#91;16];\n  data.time = timestampToTime((time + 8 * 60 * 60) * 1000);\n    \/\/ altitude\n  let altitude =\n    (bytes&#91;17] &lt;&lt; 24) | (bytes&#91;18] &lt;&lt; 16) | (bytes&#91;19] &lt;&lt; 8) | bytes&#91;20];\n  data.altitude = hex2float(altitude) + \"m\";\n\n  return data;\n}\n\n\/\/ type: 0x5 UUIDReport\nfunction decodeUUIDReport(bytes) {\n  var data = {};\n  \/\/ type\n  data.type = \"UUIDReport\";\n  \/\/ number\n  data.number = Math.floor((bytes.length - 1) \/ 17);\n  var beaconUUIDList = &#91;];\n  for (let i = 0; i &lt; data.number; i++) {\n    var beaconTypeId = bytes&#91;1 + 17 * i] &amp; 0x03;\n    if (beaconTypeId == 0x00) {\n      beaconTypeId = \"PositionBeaconUUID\";\n    } else if (beaconTypeId == 0x01) {\n      beaconTypeId = \"AssetBeaconUUID\";\n    } else if (beaconTypeId == 0x02) {\n      beaconTypeId = \"SpecialBeaconUUID\";\n    } else if (beaconTypeId == 0x03) {\n      beaconTypeId = \"SearchBeaconUUID\";\n    }\n\n    var beaconUUID = \"\";\n    for (let j = 0; j &lt; 16; j++) {\n      beaconUUID += (bytes&#91;2 + 17 * i + j] &amp; 0xff)\n        .toString(16)\n        .toUpperCase()\n        .padStart(2, \"0\");\n    }\n    beaconUUIDList.push({ beaconTypeId, beaconUUID });\n  }\n  data.beaconUUIDList = beaconUUIDList;\n\n  return data;\n}\n\n\/\/ type: 0x7 Beacon\nfunction decodeBeacon(bytes) {\n  var data = {};\n  data.type = \"Beacon\";\n  \/\/ wearState\n  data.wearState = (bytes&#91;0] &amp; 0x01) == 0 ? \"Not wearing\" : \"Wearing\";\n  \/\/ pressure\n  let pressure =\n    (bytes&#91;1] &lt;&lt; 24) | (bytes&#91;2] &lt;&lt; 16) | (bytes&#91;3] &lt;&lt; 8) | bytes&#91;4];\n  data.pressure = pressure \/ 10 + \"pa\";\n  \/\/ numner\n  data.number = bytes&#91;5] &amp; 0x0f;\n  for (let i = 0; i &lt; data.number; i++) {\n    var index = 7 + 5 * i;\n    var major = ((bytes&#91;index] &lt;&lt; 8) | bytes&#91;index + 1])\n      .toString(16)\n      .toUpperCase()\n      .padStart(4, \"0\");\n    var minor = ((bytes&#91;index + 2] &lt;&lt; 8) | bytes&#91;index + 3])\n      .toString(16)\n      .toUpperCase()\n      .padStart(4, \"0\");\n    var rssi = bytes&#91;index + 4] - 256 + \"dBm\";\n\n    data&#91;\"beacon\" + (i + 1)] = major + minor;\n    data&#91;\"rssi\" + (i + 1)] = rssi;\n  }\n\n  return data;\n}\n\n\/\/ type: 0x8 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  } else if (alarmValue == 3) {\n    data.alarm = \"Special area\";\n  } else if (alarmValue == 4) {\n    data.alarm = \"Search\";\n  }\n  return data;\n}\n\n\/\/ type: 0x0c Bracelet\nfunction decodeBracelet(bytes) {\n  var data = {};\n  data.type = \"Bracelet\";\n  var number =  bytes&#91;1];\n  data.number = number;\n  var braceletList = &#91;];\n  for (let i = 0; i &lt; number; i++) {\n\t  var bracelet = {};\n      var mac = \"\"\n\t  for (let j = 0; j &lt; 6; j++) {\n\t\tmac += bytes&#91;j + 2 + i * 21]\n\t\t  .toString(16)\n\t\t  .toUpperCase()\n\t\t  .padStart(2, \"0\");\n\t  }\n\t  bracelet.mac = mac;\n\t  bracelet.batteryLevel = bytes&#91;8 + i * 21]; \/\/ \u7535\u91cf\u7b49\u7ea7\n\t  bracelet.heartrate = bytes&#91;9 + i * 21]; \/\/ \u5fc3\u7387\n\t  bracelet.systolicPressure = bytes&#91;10 + i * 21]; \/\/ \u6536\u7f29\u538b\n\t  bracelet.diastolicPressure = bytes&#91;11 + i * 21]; \/\/ \u8212\u5f20\u538b\n\t  bracelet.wristTemperature = (((bytes&#91;12 + i * 21] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;13 + i * 21] &amp; 0xff)) \/ 10 + \"\u2103\"; \/\/ \u8155\u6e29\n\t  bracelet.bodyTemperature = (((bytes&#91;14 + i * 21] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;15 + i * 21] &amp; 0xff)) \/ 10 + \"\u2103\"; \/\/ \u4f53\u6e29\n\t  bracelet.stepNumber = ((bytes&#91;16 + i * 21] &lt;&lt; 16) &amp; 0xff0000) | ((bytes&#91;17 + i * 21] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;18 + i * 21] &amp; 0xff); \/\/ \u6b65\u6570\n\t  bracelet.wearStatus = ((bytes&#91;19 + i * 21]) &amp; 0x01) == 0 ? \"Not wearing\" : \"Wearing\";\n\t  bracelet.bluetoothBroadcastInterval = (bytes&#91;20 + i * 21] &amp; 0xff) + \"s\";\n\t  bracelet.samplingInterval = (bytes&#91;21 + i * 21]&amp; 0x7f) + \"s\";\n\t  bracelet.rssi = (bytes&#91;22 + i * 21] - 256) + \"dBm\";\n\t  braceletList.push(bracelet);\n  }\n  data.braceletList = braceletList;\n  return data;\n}\n\nfunction hex2float(num) {\n  var sign = num &amp; 0x80000000 ? -1 : 1;\n  var exponent = ((num >> 23) &amp; 0xff) - 127;\n  var mantissa = 1 + (num &amp; 0x7fffff) \/ 0x7fffff;\n  return sign * mantissa * Math.pow(2, exponent);\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-19706","docs","type-docs","status-publish","hentry","doc_category-decoders"],"year_month":"2026-08","word_count":895,"total_views":0,"reactions":{"happy":0,"normal":0,"sad":0},"author_info":{"name":"\ub9c8\uc774\uc9c0","author_nicename":"maizi","author_url":"https:\/\/www.lansitec.com\/ko\/author\/maizi\/"},"doc_category_info":[{"term_name":"Decoders","term_url":"https:\/\/www.lansitec.com\/ko\/doc\/decoders\/"}],"doc_tag_info":[],"knowledge_base_info":[],"knowledge_base_slug":[],"_links":{"self":[{"href":"https:\/\/www.lansitec.com\/ko\/wp-json\/wp\/v2\/docs\/19706","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lansitec.com\/ko\/wp-json\/wp\/v2\/docs"}],"about":[{"href":"https:\/\/www.lansitec.com\/ko\/wp-json\/wp\/v2\/types\/docs"}],"author":[{"embeddable":true,"href":"https:\/\/www.lansitec.com\/ko\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lansitec.com\/ko\/wp-json\/wp\/v2\/comments?post=19706"}],"version-history":[{"count":1,"href":"https:\/\/www.lansitec.com\/ko\/wp-json\/wp\/v2\/docs\/19706\/revisions"}],"predecessor-version":[{"id":19708,"href":"https:\/\/www.lansitec.com\/ko\/wp-json\/wp\/v2\/docs\/19706\/revisions\/19708"}],"wp:attachment":[{"href":"https:\/\/www.lansitec.com\/ko\/wp-json\/wp\/v2\/media?parent=19706"}],"wp:term":[{"taxonomy":"doc_category","embeddable":true,"href":"https:\/\/www.lansitec.com\/ko\/wp-json\/wp\/v2\/doc_category?post=19706"},{"taxonomy":"doc_tag","embeddable":true,"href":"https:\/\/www.lansitec.com\/ko\/wp-json\/wp\/v2\/doc_tag?post=19706"}],"curies":[{"name":"\uc6cc\ub4dc\ud504\ub808\uc2a4","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}