{"id":19701,"date":"2026-08-07T11:36:16","date_gmt":"2026-08-07T03:36:16","guid":{"rendered":"https:\/\/www.lansitec.com\/?post_type=docs&#038;p=19701"},"modified":"2026-08-07T11:36:22","modified_gmt":"2026-08-07T03:36:22","password":"","slug":"decoder-lorawan-container-tracker-ttn-chirpstack","status":"publish","type":"docs","link":"https:\/\/www.lansitec.com\/ko\/docs\/decoders\/decoder-lorawan-container-tracker-ttn-chirpstack\/","title":{"rendered":"LoRaWAN \ub514\ucf54\ub354 \u2013 \ucee8\ud14c\uc774\ub108 \ud2b8\ub798\ucee4(TTN\/Chirpstack)"},"content":{"rendered":"<div class=\"wp-block-file\"><a id=\"wp-block-file--media-fa25cc6d-42d9-4950-bc61-2551137f2a26\" href=\"https:\/\/www.lansitec.com\/wp-content\/uploads\/2026\/08\/decode-container-tracker-ttn_chirpstackv4.js.zip\">decode-container-tracker-ttn_chirpstackv4.js<\/a><a href=\"https:\/\/www.lansitec.com\/wp-content\/uploads\/2026\/08\/decode-container-tracker-ttn_chirpstackv4.js.zip\" class=\"wp-block-file__button wp-element-button\" download aria-describedby=\"wp-block-file--media-fa25cc6d-42d9-4950-bc61-2551137f2a26\">\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\/\/ Container Tracker\nfunction decodeUplink(input) {\n  \/\/ bytes\n  var bytes = input.bytes;\n  \/\/ type\n  var uplinkType = (bytes&#91;0] >> 4) &amp; 0x0f;\n\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 0x06:\n      return { data: decodeMovementThreshold(bytes) };\n\n    case 0x07:\n      return { data: decodePositionBeacon(bytes) };\n\n    case 0x08:\n      return { data: decodeAssetBeacon(bytes) };\n\n    case 0x09:\n      return { data: decodeAlarm(bytes) };\n\n    case 0x0a:\n      return { data: decodeVibrationShockDetectionReport(bytes) };\n\n    case 0x0b:\n      return { data: decodeOfflineCachePosition(bytes) };\n\n    default:\n      return null;\n  }\n}\n\/\/ type: 0x1 Registration\nfunction decodeRegistration(bytes) {\n  var data = {};\n  data.type = \"Registration\";\n  data.adr = ((bytes&#91;0] >> 3) &amp; 0x1) == 0 ? \"OFF\" : \"ON\";\n  \/\/ mode\n  data.mode = 0x00;\n  \/\/ smode\n  data.smode = 0x00;\n  \/\/ power\n  data.power = ((bytes&#91;2] >> 3) &amp; 0x1f) + \"dBm\";\n  \/\/ offlineCacheEnable\n  data.offlineCacheEnable =\n    ((bytes&#91;2] >> 2) &amp; 0x01) == 0 ? \"Disable\" : \"Enable\";\n  \/\/ alarmEnable\n  data.alarmEnable = ((bytes&#91;2] >> 1) &amp; 0x01) == 0 ? \"Disable\" : \"Enable\";\n  \/\/ singleKeyEnable\n  data.singleKeyEnable = (bytes&#91;2] &amp; 0x01) == 0 ? \"Disable\" : \"Enable\";\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 positionReportModeValue = (bytes&#91;3] >> 1) &amp; 0x03;\n  if (positionReportModeValue == 0) {\n    data.positionReportMode = \"Period\";\n  } else if (positionReportModeValue == 1) {\n    data.positionReportMode = \"Autonomous\";\n  } else if (positionReportModeValue == 2) {\n    data.positionReportMode = \"On-Demand\";\n  }\n  \/\/ switchEnable\n  data.switchEnable = (bytes&#91;3] &amp; 0x01) == 0 ? \"Disable\" : \"Enable\";\n  \/\/ heartbeatReportInterval\n  data.heartbeatReportInterval =\n    (((bytes&#91;4] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;5] &amp; 0xff)) * 30 + \"s\";\n  \/\/ blePositionReportInterval\n  data.blePositionReportInterval =\n    (((bytes&#91;6] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;7] &amp; 0xff)) * 5 + \"s\";\n  \/\/ div\n  data.div = bytes&#91;8] &amp; 0xff;\n  \/\/ bleEnable\n  data.bleEnable = (bytes&#91;9] &amp; 0x01) == 0 ? \"Disable\" : \"Enable\";\n  \/\/ positioningUUID\n  var positioningUUID = \"\";\n  for (let i = 0; i &lt; 4; i++) {\n    var byte1 = bytes&#91;10 + 4 * i];\n    var byte2 = bytes&#91;11 + 4 * i];\n    var byte3 = bytes&#91;12 + 4 * i];\n    var byte4 = bytes&#91;13 + 4 * i];\n\n    var part1 = ((byte1 &lt;&lt; 8) | byte2)\n      .toString(16)\n      .padStart(4, \"0\")\n      .toUpperCase();\n    var part2 = ((byte3 &lt;&lt; 8) | byte4)\n      .toString(16)\n      .padStart(4, \"0\")\n      .toUpperCase();\n\n    positioningUUID += part1 + part2;\n  }\n  data.positioningUUID = positioningUUID;\n  \/\/ accelerometerThreshold\n  data.accelerometerThreshold = (50 + bytes&#91;26] * 5) * 0.001 + \"g\";\n  \/\/ version\n  data.version = (((bytes&#91;27] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;28] &amp; 0xff))\n    .toString(16)\n    .toUpperCase();\n  \/\/ cfmmsg\n  data.cfmmsg = bytes&#91;29];\n  \/\/ hbCount\n  data.hbCount = bytes&#91;30];\n  \/\/ assetBeaconReportPeriod\n  data.assetBeaconReportPeriod = bytes&#91;31] + \"min\";\n  \/\/ bluetoothReceivingDuration\n  data.bluetoothReceivingDuration = bytes&#91;32] + \"s\";\n  \/\/ extraAssetBeaconReportInterval\n  data.extraAssetBeaconReportInterval = bytes&#91;33] + \"s\";\n  \/\/ assetBeaconUUID\n  var assetBeaconUUID = \"\";\n  for (let i = 0; i &lt; 4; i++) {\n    var byte1 = bytes&#91;34 + 4 * i];\n    var byte2 = bytes&#91;35 + 4 * i];\n    var byte3 = bytes&#91;36 + 4 * i];\n    var byte4 = bytes&#91;37 + 4 * i];\n\n    var part1 = ((byte1 &lt;&lt; 8) | byte2)\n      .toString(16)\n      .toUpperCase()\n      .padStart(4, \"0\");\n    var part2 = ((byte3 &lt;&lt; 8) | byte4)\n      .toString(16)\n      .toUpperCase()\n      .padStart(4, \"0\");\n\n    assetBeaconUUID += part1 + part2;\n  }\n  data.assetBeaconUUID = assetBeaconUUID;\n  \/\/ vibrationShockDetectionThreshold\n  data.vibrationShockDetectionThreshold = (50 + bytes&#91;50] * 5) * 0.001 + \"g\";\n  \/\/ vibrationShockDetectionReportPeriod\n  data.vibrationShockDetectionReportPeriod = bytes&#91;51] * 30 + \"s\";\n  \/\/ gnssPositionReportInterval\n  data.gnssPositionReportInterval =\n    (((bytes&#91;52] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;53] &amp; 0xff)) * 5 + \"s\";\n\n  return data;\n}\n\n\/\/ type: 0x2 Heartbeat\nfunction decodeHeartbeat(bytes) {\n  var data = {};\n  data.type = \"Heartbeat\";\n  \/\/ snrEnable\n  data.snrEnable = (bytes&#91;0] &amp; 0x0f) == 0 ? \"No SNR field\" : \"SNR field Enable\";\n  \/\/ voltage\n  data.voltage = bytes&#91;1] \/ 100 + 1.5 + \"V\";\n  \/\/ <a href=\"https:\/\/www.lansitec.com\/blogs\/rssi-indoor-positioning\/\">rssi<\/a>\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\n  \/\/ gnssState\n  var gnssStateValue = (bytes&#91;5] >> 4) &amp; 0x0f;\n  if (gnssStateValue == 0) {\n    data.gnssState = \"Off\";\n  } else if (gnssStateValue == 1) {\n    data.gnssState = \"Boot GNSS\";\n  } else if (gnssStateValue == 2) {\n    data.gnssState = \"Locating\";\n  } else if (gnssStateValue == 3) {\n    data.gnssState = \"Located\";\n  } else if (gnssStateValue == 9) {\n    data.gnssState = \"No signal\";\n  }\n  \/\/ moveState\n  data.moveState = bytes&#91;5] &amp; 0x0f;\n  \/\/ temperature\n  data.temperature = (((bytes&#91;6] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;7] &amp; 0xff)) + \"\u2103\";\n  \/\/ movement\n  data.movement = (((bytes&#91;8] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;9] &amp; 0xff)) * 5 + \"s\";\n\n  return data;\n}\n\n\/\/ type: 0x03 GNSSPosition\nfunction decodeGNSSPosition(bytes) {\n  var data = {};\n  data.type = \"GNSSPosition\";\n  \/\/ longitude\n  let longitude =\n    (bytes&#91;1] &lt;&lt; 24) | (bytes&#91;2] &lt;&lt; 16) | (bytes&#91;3] &lt;&lt; 8) | bytes&#91;4];\n  data.longitude = hex2float(longitude);\n\n  \/\/ latitude\n  let latitude =\n    (bytes&#91;5] &lt;&lt; 24) | (bytes&#91;6] &lt;&lt; 16) | (bytes&#91;7] &lt;&lt; 8) | bytes&#91;8];\n  data.latitude = hex2float(latitude);\n\n  \/\/ time\n  let time =\n    (bytes&#91;9] &lt;&lt; 24) | (bytes&#91;10] &lt;&lt; 16) | (bytes&#91;11] &lt;&lt; 8) | bytes&#91;12];\n  data.time = timestampToTime((time + 8 * 60 * 60) * 1000);\n\n  return data;\n}\n\n\/\/ type: 0x06 MovementThreshold\nfunction decodeMovementThreshold() {\n  var data = {};\n  data.type = \"MovementThreshold\";\n  data.thresholdFlag = bytes&#91;1];\n  data.movementThreshold = bytes&#91;2];\n  data.stationaryThreshold = bytes&#91;3];\n\n  return data;\n}\n\n\/\/ type: 0x07 PositionBeacon\nfunction decodePositionBeacon(bytes) {\n  var data = {};\n  data.type = \"PositionBeacon\";\n  data.length = bytes&#91;0] &amp; 0x0f;\n  for (let i = 0; i &lt; data.length; i++) {\n    var major = (((bytes&#91;6 + 5 * i] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;7 + 5 * i] &amp; 0xff))\n      .toString(16)\n      .toUpperCase()\n      .padStart(4, \"0\");\n    var minor = (((bytes&#91;8 + 5 * i] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;9 + 5 * i] &amp; 0xff))\n      .toString(16)\n      .toUpperCase()\n      .padStart(4, \"0\");\n    var <a href=\"https:\/\/www.lansitec.com\/blogs\/rssi-indoor-positioning\/\">rssi<\/a> = bytes&#91;10 + 5 * i] - 256 + \"dBm\";\n\n    data&#91;\"beacon\" + (i + 1)] = major + minor;\n    data&#91;\"<a href=\"https:\/\/www.lansitec.com\/blogs\/rssi-indoor-positioning\/\">rssi<\/a>\" + (i + 1)] = <a href=\"https:\/\/www.lansitec.com\/blogs\/rssi-indoor-positioning\/\">rssi<\/a>;\n  }\n\n  return data;\n}\n\n\/\/ type: 0x08 AssetBeacon\nfunction decodeAssetBeacon(bytes) {\n  var data = {};\n  data.type = \"AssetBeacon\";\n  data.qty = bytes&#91;1] &amp; 0xff;\n  for (let i = 0; i &lt; data.qty; i++) {\n    var major = (((bytes&#91;2 + 5 * i] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;3 + 5 * i] &amp; 0xff))\n      .toString(16)\n      .toUpperCase()\n      .padStart(4, \"0\");\n    var minor = (((bytes&#91;4 + 5 * i] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;5 + 5 * i] &amp; 0xff))\n      .toString(16)\n      .toUpperCase()\n      .padStart(4, \"0\");\n    var <a href=\"https:\/\/www.lansitec.com\/blogs\/rssi-indoor-positioning\/\">rssi<\/a> = bytes&#91;6 + 5 * i] - 256 + \"dBm\";\n\n    data&#91;\"beacon\" + (i + 1)] = major + minor;\n    data&#91;\"<a href=\"https:\/\/www.lansitec.com\/blogs\/rssi-indoor-positioning\/\">rssi<\/a>\" + (i + 1)] = <a href=\"https:\/\/www.lansitec.com\/blogs\/rssi-indoor-positioning\/\">rssi<\/a>;\n  }\n\n  return data;\n}\n\n\/\/ type: 0x09 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 = \"Alarm\";\n  }\n  return data;\n}\n\n\/\/ type: 0x0B OfflineCachePosition\nfunction decodeOfflineCachePosition(bytes) {\n  var data = {};\n  \/\/ type\n  data.type = \"OfflineCachePosition\";\n  \/\/ length\n  data.length = bytes&#91;0] &amp; 0x0f;\n  \/\/ cacheDataType\n  data.cacheDataType = bytes&#91;1] &amp; 0xff;\n  var beaconIndex = 1;\n  var gnssIndex = 1;\n  var index = 2;\n  for (let i = 0; i &lt; data.length; i++) {\n    var flag = (data.cacheDataType >> (7 - i)) &amp; 0x01;\n    if (flag == 0) {\n      var major = (((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 minor = (\n        ((bytes&#91;index + 2] &lt;&lt; 8) &amp; 0xff00) |\n        (bytes&#91;index + 3] &amp; 0xff)\n      )\n        .toString(16)\n        .toUpperCase()\n        .padStart(4, \"0\");\n      var <a href=\"https:\/\/www.lansitec.com\/blogs\/rssi-indoor-positioning\/\">rssi<\/a> = bytes&#91;index + 4] - 256 + \"dBm\";\n\n      data&#91;\"beacon\" + beaconIndex] = major + minor;\n      data&#91;\"<a href=\"https:\/\/www.lansitec.com\/blogs\/rssi-indoor-positioning\/\">rssi<\/a>\" + beaconIndex] = <a href=\"https:\/\/www.lansitec.com\/blogs\/rssi-indoor-positioning\/\">rssi<\/a>;\n      beaconIndex = beaconIndex + 1;\n      index = index + 5;\n    } else if (flag == 1) {\n      var gnss = {};\n      \/\/ longitude\n      var longitude =\n        (bytes&#91;index] &lt;&lt; 24) |\n        (bytes&#91;index + 1] &lt;&lt; 16) |\n        (bytes&#91;index + 2] &lt;&lt; 8) |\n        bytes&#91;index + 3];\n      gnss.longitude = hex2float(longitude);\n      \/\/ latitude\n      var latitude =\n        (bytes&#91;index + 4] &lt;&lt; 24) |\n        (bytes&#91;index + 5] &lt;&lt; 16) |\n        (bytes&#91;index + 6] &lt;&lt; 8) |\n        bytes&#91;index + 7];\n      gnss.latitude = hex2float(latitude);\n      \/\/ time\n      var time =\n        (bytes&#91;index + 8] &lt;&lt; 24) |\n        (bytes&#91;index + 9] &lt;&lt; 16) |\n        (bytes&#91;index + 10] &lt;&lt; 8) |\n        bytes&#91;index + 11];\n      gnss.time = timestampToTime((time + 8 * 60 * 60) * 1000);\n\n      data&#91;\"longitude\" + gnssIndex] = gnss.longitude;\n      data&#91;\"latitude\" + gnssIndex] = gnss.latitude;\n      data&#91;\"time\" + gnssIndex] = gnss.time;\n\n      gnssIndex = gnssIndex + 1;\n      index = index + 12;\n    }\n  }\n\n  return data;\n}\n\n\/\/ type: 0x0A VibrationShockDetectionReport\nfunction decodeVibrationShockDetectionReport(bytes) {\n  var data = {};\n  data.type = \"VibrationShockDetectionReport\";\n  data.vibrationShockCount = ((bytes&#91;1] &lt;&lt; 8) &amp; 0xff00) | (bytes&#91;2] &amp; 0xff);\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-19701","docs","type-docs","status-publish","hentry","doc_category-decoders"],"year_month":"2026-08","word_count":1105,"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\/19701","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=19701"}],"version-history":[{"count":1,"href":"https:\/\/www.lansitec.com\/ko\/wp-json\/wp\/v2\/docs\/19701\/revisions"}],"predecessor-version":[{"id":19704,"href":"https:\/\/www.lansitec.com\/ko\/wp-json\/wp\/v2\/docs\/19701\/revisions\/19704"}],"wp:attachment":[{"href":"https:\/\/www.lansitec.com\/ko\/wp-json\/wp\/v2\/media?parent=19701"}],"wp:term":[{"taxonomy":"doc_category","embeddable":true,"href":"https:\/\/www.lansitec.com\/ko\/wp-json\/wp\/v2\/doc_category?post=19701"},{"taxonomy":"doc_tag","embeddable":true,"href":"https:\/\/www.lansitec.com\/ko\/wp-json\/wp\/v2\/doc_tag?post=19701"}],"curies":[{"name":"\uc6cc\ub4dc\ud504\ub808\uc2a4","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}