diff --git a/sites/en/docs/Sensor/SenseCAP/SenseCAP_Decoder.md b/sites/en/docs/Sensor/SenseCAP/SenseCAP_Decoder.md index b678b5aa0ac17..9ce20223dd07f 100644 --- a/sites/en/docs/Sensor/SenseCAP/SenseCAP_Decoder.md +++ b/sites/en/docs/Sensor/SenseCAP/SenseCAP_Decoder.md @@ -6,10 +6,10 @@ keywords: image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png slug: /SenseCAP_Decoder last_update: - date: 1/26/2026 + date: 3/12/2026 author: Janet createdAt: '2023-08-24' -updatedAt: '2026-03-03' +updatedAt: '2026-03-12' url: https://wiki.seeedstudio.com/SenseCAP_Decoder/ --- @@ -49,833 +49,6 @@ function decodeUplink (input) { decoded.messages.push({fport: fport, payload: bytesString}) return { data: decoded } } - if (fport !== 5) { - decoded.valid = false - return { data: decoded } - } - let measurement = messageAnalyzed(originMessage) - if (measurement.length === 0) { - decoded.valid = false - return { data: decoded } - } - - for (let message of measurement) { - if (message.length === 0) { - continue - } - let elements = [] - for (let element of message) { - if (element.errorCode) { - decoded.err = element.errorCode - decoded.errMessage = element.error - } else { - elements.push(element) - } - } - if (elements.length > 0) { - decoded.messages.push(elements) - } - } - // decoded.messages = measurement - return { data: decoded } -} - -function messageAnalyzed (messageValue) { - try { - let frames = unpack(messageValue) - let measurementResultArray = [] - for (let i = 0; i < frames.length; i++) { - let item = frames[i] - let dataId = item.dataId - let dataValue = item.dataValue - let measurementArray = deserialize(dataId, dataValue) - measurementResultArray.push(measurementArray) - } - return measurementResultArray - } catch (e) { - return e.toString() - } -} - -function unpack (messageValue) { - let frameArray = [] - - for (let i = 0; i < messageValue.length; i++) { - let remainMessage = messageValue - let dataId = remainMessage.substring(0, 2).toUpperCase() - let dataValue - let dataObj = {} - let packageLen - switch (dataId) { - case '01': - packageLen = 94 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) - dataObj = { - 'dataId': dataId, 'dataValue': dataValue - } - break - case '02': - packageLen = 32 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) - dataObj = { - 'dataId': dataId, 'dataValue': dataValue - } - break - case '03': - packageLen = 64 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) - dataObj = { - 'dataId': dataId, 'dataValue': dataValue - } - break - case '04': - packageLen = 20 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) - dataObj = { - 'dataId': dataId, 'dataValue': dataValue - } - break - case '05': - packageLen = 10 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) - dataObj = { - 'dataId': dataId, 'dataValue': dataValue - } - break - case '06': - packageLen = 44 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) - dataObj = { - 'dataId': dataId, 'dataValue': dataValue - } - break - case '07': - packageLen = 84 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) - dataObj = { - 'dataId': dataId, 'dataValue': dataValue - } - break - case '08': - packageLen = 70 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) - dataObj = { - 'dataId': dataId, 'dataValue': dataValue - } - break - case '09': - packageLen = 36 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) - dataObj = { - 'dataId': dataId, 'dataValue': dataValue - } - break - case '0A': - packageLen = 76 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) - dataObj = { - 'dataId': dataId, 'dataValue': dataValue - } - break - case '0B': - packageLen = 62 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) - dataObj = { - 'dataId': dataId, 'dataValue': dataValue - } - break - case '0C': - packageLen = 2 - if (remainMessage.length < packageLen) { - return frameArray - } - break - case '0D': - packageLen = 10 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) - dataObj = { - 'dataId': dataId, 'dataValue': dataValue - } - break - case '0E': - packageLen = getInt(remainMessage.substring(8, 10)) * 2 + 10 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, 8) + remainMessage.substring(10, packageLen) - messageValue = remainMessage.substring(packageLen) - dataObj = { - 'dataId': dataId, 'dataValue': dataValue - } - break - case '0F': - packageLen = 34 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) - dataObj = { - 'dataId': dataId, 'dataValue': dataValue - } - break - case '10': - packageLen = 26 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) - dataObj = { - 'dataId': dataId, 'dataValue': dataValue - } - break - case '11': - packageLen = 28 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) - dataObj = { - 'dataId': dataId, 'dataValue': dataValue - } - break - default: - return frameArray - } - if (dataValue.length < 2) { - break - } - frameArray.push(dataObj) - } - return frameArray -} - -function deserialize (dataId, dataValue) { - let measurementArray = [] - let eventList = [] - let measurement = {} - let collectTime = 0 - let groupId = 0 - let shardFlag = {} - let payload = '' - let result = [] - let dataArr = [] - switch (dataId) { - case '01': - measurementArray = getUpShortInfo(dataValue) - measurementArray.push(...getMotionSetting(dataValue.substring(30, 40))) - measurementArray.push(...getStaticSetting(dataValue.substring(40, 46))) - measurementArray.push(...getShockSetting(dataValue.substring(46, 52))) - measurementArray.push(...getTempSetting(dataValue.substring(52, 72))) - measurementArray.push(...getLightSetting(dataValue.substring(72, 92))) - break - case '02': - measurementArray = getUpShortInfo(dataValue) - break - case '03': - measurementArray.push(...getMotionSetting(dataValue.substring(0, 10))) - measurementArray.push(...getStaticSetting(dataValue.substring(10, 16))) - measurementArray.push(...getShockSetting(dataValue.substring(16, 22))) - measurementArray.push(...getTempSetting(dataValue.substring(22, 42))) - measurementArray.push(...getLightSetting(dataValue.substring(42, 62))) - break - case '04': - let interval = 0 - let workMode = getInt(dataValue.substring(0, 2)) - let heartbeatInterval = getMinsByMin(dataValue.substring(4, 8)) - let periodicInterval = getMinsByMin(dataValue.substring(8, 12)) - let eventInterval = getMinsByMin(dataValue.substring(12, 16)) - switch (workMode) { - case 0: - interval = heartbeatInterval - break - case 1: - interval = periodicInterval - break - case 2: - interval = eventInterval - break - } - measurementArray = [ - {measurementId: '3940', type: 'Work Mode', measurementValue: workMode}, - {measurementId: '3942', type: 'Heartbeat Interval', measurementValue: heartbeatInterval}, - {measurementId: '3943', type: 'Periodic Interval', measurementValue: periodicInterval}, - {measurementId: '3944', type: 'Event Interval', measurementValue: eventInterval}, - {measurementId: '3941', type: 'SOS Mode', measurementValue: getSOSMode(dataValue.substring(16, 18))}, - {measurementId: '3900', type: 'Uplink Interval', measurementValue: interval} - ] - break; - case '05': - measurementArray = [ - {measurementId: '3000', type: 'Battery', measurementValue: getBattery(dataValue.substring(0, 2))}, - {measurementId: '3940', type: 'Work Mode', measurementValue: getWorkingMode(dataValue.substring(2, 4))}, - {measurementId: '3965', type: 'Positioning Strategy', measurementValue: getPositioningStrategy(dataValue.substring(4, 6))}, - {measurementId: '3941', type: 'SOS Mode', measurementValue: getSOSMode(dataValue.substring(6, 8))} - ] - break - case '06': - collectTime = getUTCTimestamp(dataValue.substring(8, 16)) - measurementArray = [ - {measurementId: '4200', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, - {measurementId: '4197', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Longitude', measurementValue: parseFloat(getSensorValue(dataValue.substring(16, 24), 1000000))}, - {measurementId: '4198', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Latitude', measurementValue: parseFloat(getSensorValue(dataValue.substring(24, 32), 1000000))}, - {measurementId: '4097', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Air Temperature', measurementValue: getSensorValue(dataValue.substring(32, 36), 10)}, - {measurementId: '4199', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Light', measurementValue: getSensorValue(dataValue.substring(36, 40))}, - {measurementId: '3000', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Battery', measurementValue: getBattery(dataValue.substring(40, 42))} - ] - break - case '07': - eventList = getEventStatus(dataValue.substring(0, 6)) - collectTime = getUTCTimestamp(dataValue.substring(8, 16)) - measurementArray = [ - {measurementId: '4200', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, - {measurementId: '5001', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Wi-Fi Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 72))}, - {measurementId: '4097', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Air Temperature', measurementValue: getSensorValue(dataValue.substring(72, 76), 10)}, - {measurementId: '4199', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Light', measurementValue: getSensorValue(dataValue.substring(76, 80))}, - {measurementId: '3000', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Battery', measurementValue: getBattery(dataValue.substring(80, 82))} - ] - break - case '08': - collectTime = getUTCTimestamp(dataValue.substring(8, 16)) - measurementArray = [ - {measurementId: '4200', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, - {measurementId: '5002', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'BLE Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 58))}, - {measurementId: '4097', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Air Temperature', measurementValue: getSensorValue(dataValue.substring(58, 62), 10)}, - {measurementId: '4199', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Light', measurementValue: getSensorValue(dataValue.substring(62, 66))}, - {measurementId: '3000', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Battery', measurementValue: getBattery(dataValue.substring(66, 68))} - ] - break - case '09': - collectTime = getUTCTimestamp(dataValue.substring(8, 16)) - measurementArray = [ - {measurementId: '4200', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, - {measurementId: '4197', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Longitude', measurementValue: parseFloat(getSensorValue(dataValue.substring(16, 24), 1000000))}, - {measurementId: '4198', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Latitude', measurementValue: parseFloat(getSensorValue(dataValue.substring(24, 32), 1000000))}, - {measurementId: '3000', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Battery', measurementValue: getBattery(dataValue.substring(32, 34))} - ] - break - case '0A': - collectTime = getUTCTimestamp(dataValue.substring(8, 16)) - measurementArray = [ - {measurementId: '4200', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, - {measurementId: '5001', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Wi-Fi Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 72))}, - {measurementId: '3000', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Battery', measurementValue: getBattery(dataValue.substring(72, 74))} - ] - break - case '0B': - collectTime = getUTCTimestamp(dataValue.substring(8, 16)) - measurementArray = [ - {measurementId: '4200', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, - {measurementId: '5002', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'BLE Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 58))}, - {measurementId: '3000', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Battery', measurementValue: getBattery(dataValue.substring(58, 60))}, - ] - break - case '0D': - let errorCode = getInt(dataValue) - let error = '' - switch (errorCode) { - case 1: - error = 'FAILED TO OBTAIN THE UTC TIMESTAMP' - break - case 2: - error = 'ALMANAC TOO OLD' - break - case 3: - error = 'DOPPLER ERROR' - break - } - measurementArray.push({errorCode, error}) - break - case '0E': - shardFlag = getShardFlag(dataValue.substring(0, 2)) - groupId = getInt(dataValue.substring(2, 6)) - payload = dataValue.substring(6) - measurement = { - measurementId: '6152', - groupId: groupId, - index: shardFlag.index, - count: shardFlag.count, - type: 'gnss-ng payload', - measurementValue: payload - } - measurementArray.push(measurement) - break - case '0F': - collectTime = getUTCTimestamp(dataValue.substring(8, 16)) - shardFlag = getShardFlag(dataValue.substring(26, 28)) - groupId = getInt(dataValue.substring(28, 32)) - measurementArray.push({ - measurementId: '4200', - timestamp: collectTime, - motionId: getMotionId(dataValue.substring(6, 8)), - groupId: groupId, - index: shardFlag.index, - count: shardFlag.count, - type: 'Event Status', - measurementValue: getEventStatus(dataValue.substring(0, 6)) - }) - measurementArray.push({ - measurementId: '4097', - timestamp: collectTime, - motionId: getMotionId(dataValue.substring(6, 8)), - groupId: groupId, - index: shardFlag.index, - count: shardFlag.count, - type: 'Air Temperature', - measurementValue: '' + getSensorValue(dataValue.substring(16, 20), 10) - }) - measurementArray.push({ - measurementId: '4199', - timestamp: collectTime, - motionId: getMotionId(dataValue.substring(6, 8)), - groupId: groupId, - index: shardFlag.index, - count: shardFlag.count, - type: 'Light', - measurementValue: '' + getSensorValue(dataValue.substring(20, 24)) - }) - measurementArray.push({ - measurementId: '3000', - timestamp: collectTime, - motionId: getMotionId(dataValue.substring(6, 8)), - groupId: groupId, - index: shardFlag.index, - count: shardFlag.count, - type: 'Battery', - measurementValue: '' + getBattery(dataValue.substring(24, 26)) - }) - break - case '10': - collectTime = getUTCTimestamp(dataValue.substring(8, 16)) - shardFlag = getShardFlag(dataValue.substring(18, 20)) - groupId = getInt(dataValue.substring(20, 24)) - measurementArray.push({ - measurementId: '4200', - timestamp: collectTime, - motionId: getMotionId(dataValue.substring(6, 8)), - groupId: groupId, - index: shardFlag.index, - count: shardFlag.count, - type: 'Event Status', - measurementValue: getEventStatus(dataValue.substring(0, 6)) - }) - measurementArray.push({ - measurementId: '3000', - timestamp: collectTime, - motionId: getMotionId(dataValue.substring(6, 8)), - groupId: groupId, - index: shardFlag.index, - count: shardFlag.count, - type: 'Battery', - measurementValue: '' + getBattery(dataValue.substring(16, 18)) - }) - break - case '11': - collectTime = getUTCTimestamp(dataValue.substring(8, 16)) - measurementArray.push({ - measurementId: '3576', - timestamp: collectTime, - type: 'Positing Status', - measurementValue: '' + getPositingStatus(dataValue.substring(0, 2)) - }) - measurementArray.push({ - timestamp: collectTime, - measurementId: '4200', - type: 'Event Status', - measurementValue: getEventStatus(dataValue.substring(2, 8)) - }) - if (!isNaN(parseFloat(getSensorValue(dataValue.substring(16, 20), 10)))) { - measurementArray.push({ - timestamp: collectTime, - measurementId: '4097', - type: 'Air Temperature', - measurementValue: '' + getSensorValue(dataValue.substring(16, 20), 10) - }) - } - if (!isNaN(parseFloat(getSensorValue(dataValue.substring(20, 24))))) { - measurementArray.push({ - timestamp: collectTime, - measurementId: '4199', - type: 'Light', - measurementValue: '' + getSensorValue(dataValue.substring(20, 24)) - }) - } - measurementArray.push({ - timestamp: collectTime, - measurementId: '3000', - type: 'Battery', - measurementValue: '' + getBattery(dataValue.substring(24, 26)) - }) - break - } - return measurementArray -} - -function getMotionId (str) { - return getInt(str) -} - -function getPositingStatus (str) { - return getInt(str) -} - -function getUpShortInfo (messageValue) { - return [ - { - measurementId: '3000', type: 'Battery', measurementValue: getBattery(messageValue.substring(0, 2)) - }, { - measurementId: '3502', type: 'Firmware Version', measurementValue: getSoftVersion(messageValue.substring(2, 6)) - }, { - measurementId: '3001', type: 'Hardware Version', measurementValue: getHardVersion(messageValue.substring(6, 10)) - }, { - measurementId: '3940', type: 'Work Mode', measurementValue: getWorkingMode(messageValue.substring(10, 12)) - }, { - measurementId: '3965', type: 'Positioning Strategy', measurementValue: getPositioningStrategy(messageValue.substring(12, 14)) - }, { - measurementId: '3942', type: 'Heartbeat Interval', measurementValue: getMinsByMin(messageValue.substring(14, 18)) - }, { - measurementId: '3943', type: 'Periodic Interval', measurementValue: getMinsByMin(messageValue.substring(18, 22)) - }, { - measurementId: '3944', type: 'Event Interval', measurementValue: getMinsByMin(messageValue.substring(22, 26)) - }, { - measurementId: '3945', type: 'Sensor Enable', measurementValue: getInt(messageValue.substring(26, 28)) - }, { - measurementId: '3941', type: 'SOS Mode', measurementValue: getSOSMode(messageValue.substring(28, 30)) - } - ] -} - -function getMotionSetting (str) { - return [ - {measurementId: '3946', type: 'Motion Enable', measurementValue: getInt(str.substring(0, 2))}, - {measurementId: '3947', type: 'Any Motion Threshold', measurementValue: getSensorValue(str.substring(2, 6), 1)}, - {measurementId: '3948', type: 'Motion Start Interval', measurementValue: getMinsByMin(str.substring(6, 10))}, - ] -} - -function getStaticSetting (str) { - return [ - {measurementId: '3949', type: 'Static Enable', measurementValue: getInt(str.substring(0, 2))}, - {measurementId: '3950', type: 'Device Static Timeout', measurementValue: getMinsByMin(str.substring(2, 6))} - ] -} - -function getShockSetting (str) { - return [ - {measurementId: '3951', type: 'Shock Enable', measurementValue: getInt(str.substring(0, 2))}, - {measurementId: '3952', type: 'Shock Threshold', measurementValue: getInt(str.substring(2, 6))} - ] -} - -function getTempSetting (str) { - return [ - {measurementId: '3953', type: 'Temp Enable', measurementValue: getInt(str.substring(0, 2))}, - {measurementId: '3954', type: 'Event Temp Interval', measurementValue: getMinsByMin(str.substring(2, 6))}, - {measurementId: '3955', type: 'Event Temp Sample Interval', measurementValue: getSecondsByInt(str.substring(6, 10))}, - {measurementId: '3956', type: 'Temp ThMax', measurementValue: getSensorValue(str.substring(10, 14), 10)}, - {measurementId: '3957', type: 'Temp ThMin', measurementValue: getSensorValue(str.substring(14, 18), 10)}, - {measurementId: '3958', type: 'Temp Warning Type', measurementValue: getInt(str.substring(18, 20))} - ] -} - -function getLightSetting (str) { - return [ - {measurementId: '3959', type: 'Light Enable', measurementValue: getInt(str.substring(0, 2))}, - {measurementId: '3960', type: 'Event Light Interval', measurementValue: getMinsByMin(str.substring(2, 6))}, - {measurementId: '3961', type: 'Event Light Sample Interval', measurementValue: getSecondsByInt(str.substring(6, 10))}, - {measurementId: '3962', type: 'Light ThMax', measurementValue: getSensorValue(str.substring(10, 14), 10)}, - {measurementId: '3963', type: 'Light ThMin', measurementValue: getSensorValue(str.substring(14, 18), 10)}, - {measurementId: '3964', type: 'Light Warning Type', measurementValue: getInt(str.substring(18, 20))} - ] -} - -function getShardFlag (str) { - let bitStr = getByteArray(str) - return { - count: parseInt(bitStr.substring(0, 4), 2), - index: parseInt(bitStr.substring(4), 2) - } -} - -function getBattery (batteryStr) { - return loraWANV2DataFormat(batteryStr) -} -function getSoftVersion (softVersion) { - return `${loraWANV2DataFormat(softVersion.substring(0, 2))}.${loraWANV2DataFormat(softVersion.substring(2, 4))}` -} -function getHardVersion (hardVersion) { - return `${loraWANV2DataFormat(hardVersion.substring(0, 2))}.${loraWANV2DataFormat(hardVersion.substring(2, 4))}` -} - -function getSecondsByInt (str) { - return getInt(str) -} - -function getMinsByMin (str) { - return getInt(str) -} - -function getSensorValue (str, dig) { - if (str === '8000') { - return null - } else { - return loraWANV2DataFormat(str, dig) - } -} - -function bytes2HexString (arrBytes) { - var str = '' - for (var i = 0; i < arrBytes.length; i++) { - var tmp - var num = arrBytes[i] - if (num < 0) { - tmp = (255 + num + 1).toString(16) - } else { - tmp = num.toString(16) - } - if (tmp.length === 1) { - tmp = '0' + tmp - } - str += tmp - } - return str -} -function loraWANV2DataFormat (str, divisor = 1) { - let strReverse = bigEndianTransform(str) - let str2 = toBinary(strReverse) - if (str2.substring(0, 1) === '1') { - let arr = str2.split('') - let reverseArr = arr.map((item) => { - if (parseInt(item) === 1) { - return 0 - } else { - return 1 - } - }) - str2 = parseInt(reverseArr.join(''), 2) + 1 - return '-' + str2 / divisor - } - return parseInt(str2, 2) / divisor -} - -function bigEndianTransform (data) { - let dataArray = [] - for (let i = 0; i < data.length; i += 2) { - dataArray.push(data.substring(i, i + 2)) - } - return dataArray -} - -function toBinary (arr) { - let binaryData = arr.map((item) => { - let data = parseInt(item, 16) - .toString(2) - let dataLength = data.length - if (data.length !== 8) { - for (let i = 0; i < 8 - dataLength; i++) { - data = `0` + data - } - } - return data - }) - return binaryData.toString().replace(/,/g, '') -} - -function getSOSMode (str) { - return loraWANV2DataFormat(str) -} - -function getMacAndRssiObj (pair) { - let pairs = [] - if (pair.length % 14 === 0) { - for (let i = 0; i < pair.length; i += 14) { - let mac = getMacAddress(pair.substring(i, i + 12)) - if (mac) { - let rssi = getInt8RSSI(pair.substring(i + 12, i + 14)) - pairs.push({mac: mac, rssi: rssi}) - } else { - continue - } - } - } - return pairs -} - -function getMacAddress (str) { - if (str.toLowerCase() === 'ffffffffffff') { - return null - } - let macArr = [] - for (let i = 1; i < str.length; i++) { - if (i % 2 === 1) { - macArr.push(str.substring(i - 1, i + 1)) - } - } - let mac = '' - for (let i = 0; i < macArr.length; i++) { - mac = mac + macArr[i] - if (i < macArr.length - 1) { - mac = mac + ':' - } - } - return mac -} - -function getInt8RSSI (str) { - return loraWANV2DataFormat(str) -} - -function getInt (str) { - return parseInt(str, 16) -} - -function getEventStatus (str) { - // return getInt(str) - let bitStr = getByteArray(str) - let bitArr = [] - for (let i = 0; i < bitStr.length; i++) { - bitArr[i] = bitStr.substring(i, i + 1) - } - bitArr = bitArr.reverse() - let event = [] - for (let i = 0; i < bitArr.length; i++) { - if (bitArr[i] !== '1') { - continue - } - switch (i){ - case 0: - event.push({id:1, eventName:"Start moving event."}) - break - case 1: - event.push({id:2, eventName:"End movement event."}) - break - case 2: - event.push({id:3, eventName:"Motionless event."}) - break - case 3: - event.push({id:4, eventName:"Shock event."}) - break - case 4: - event.push({id:5, eventName:"Temperature event."}) - break - case 5: - event.push({id:6, eventName:"Light event."}) - break - case 6: - event.push({id:7, eventName:"SOS event."}) - break - case 7: - event.push({id:8, eventName:"Press once event."}) - break - } - } - return event -} - -function getByteArray (str) { - let bytes = [] - for (let i = 0; i < str.length; i += 2) { - bytes.push(str.substring(i, i + 2)) - } - return toBinary(bytes) -} - -function getWorkingMode (workingMode) { - return getInt(workingMode) -} - -function getPositioningStrategy (strategy) { - return getInt(strategy) -} - -function getUTCTimestamp(str){ - return parseInt(loraWANV2PositiveDataFormat(str)) * 1000 -} - -function loraWANV2PositiveDataFormat (str, divisor = 1) { - let strReverse = bigEndianTransform(str) - let str2 = toBinary(strReverse) - return parseInt(str2, 2) / divisor -} -``` - - - -
- -For Helium - -```cpp -function Decoder (bytes, port) { - const bytesString = bytes2HexString(bytes) - const originMessage = bytesString.toLocaleUpperCase() - const fport = parseInt(port) - const decoded = { - valid: true, - err: 0, - payload: bytesString, - messages: [] - } - - if (fport === 199 || fport === 192) { - decoded.messages.push({fport: fport, payload: bytesString}) - return { data: decoded } - } - if (fport !== 5) { - decoded.valid = false - return { data: decoded } - } - let measurement = messageAnalyzed(originMessage) if (measurement.length === 0) { decoded.valid = false @@ -899,7 +72,6 @@ function Decoder (bytes, port) { decoded.messages.push(elements) } } - // decoded.messages = measurement return { data: decoded } } @@ -922,7 +94,6 @@ function messageAnalyzed (messageValue) { function unpack (messageValue) { let frameArray = [] - for (let i = 0; i < messageValue.length; i++) { let remainMessage = messageValue let dataId = remainMessage.substring(0, 2).toUpperCase() @@ -931,148 +102,93 @@ function unpack (messageValue) { let packageLen switch (dataId) { case '01': - packageLen = 94 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) + dataValue = remainMessage.substring(2, 94) + messageValue = remainMessage.substring(94) dataObj = { 'dataId': dataId, 'dataValue': dataValue } break case '02': - packageLen = 32 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) + dataValue = remainMessage.substring(2, 32) + messageValue = remainMessage.substring(32) dataObj = { 'dataId': dataId, 'dataValue': dataValue } break case '03': - packageLen = 64 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) + dataValue = remainMessage.substring(2, 64) + messageValue = remainMessage.substring(64) dataObj = { 'dataId': dataId, 'dataValue': dataValue } break case '04': - packageLen = 20 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) + dataValue = remainMessage.substring(2, 20) + messageValue = remainMessage.substring(20) dataObj = { 'dataId': dataId, 'dataValue': dataValue } break case '05': - packageLen = 10 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) + dataValue = remainMessage.substring(2, 10) + messageValue = remainMessage.substring(10) dataObj = { 'dataId': dataId, 'dataValue': dataValue } break case '06': - packageLen = 44 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) + dataValue = remainMessage.substring(2, 44) + messageValue = remainMessage.substring(44) dataObj = { 'dataId': dataId, 'dataValue': dataValue } break case '07': - packageLen = 84 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) + dataValue = remainMessage.substring(2, 84) + messageValue = remainMessage.substring(84) dataObj = { 'dataId': dataId, 'dataValue': dataValue } break case '08': - packageLen = 70 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) + dataValue = remainMessage.substring(2, 70) + messageValue = remainMessage.substring(70) dataObj = { 'dataId': dataId, 'dataValue': dataValue } break case '09': - packageLen = 36 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) + dataValue = remainMessage.substring(2, 36) + messageValue = remainMessage.substring(36) dataObj = { 'dataId': dataId, 'dataValue': dataValue } break case '0A': - packageLen = 76 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) + dataValue = remainMessage.substring(2, 76) + messageValue = remainMessage.substring(76) dataObj = { 'dataId': dataId, 'dataValue': dataValue } break case '0B': - packageLen = 62 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) + dataValue = remainMessage.substring(2, 62) + messageValue = remainMessage.substring(62) dataObj = { 'dataId': dataId, 'dataValue': dataValue } break case '0C': - packageLen = 2 - if (remainMessage.length < packageLen) { - return frameArray - } break case '0D': - packageLen = 10 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) + dataValue = remainMessage.substring(2, 10) + messageValue = remainMessage.substring(10) dataObj = { 'dataId': dataId, 'dataValue': dataValue } break case '0E': packageLen = getInt(remainMessage.substring(8, 10)) * 2 + 10 - if (remainMessage.length < packageLen) { - return frameArray - } dataValue = remainMessage.substring(2, 8) + remainMessage.substring(10, packageLen) messageValue = remainMessage.substring(packageLen) dataObj = { @@ -1080,34 +196,50 @@ function unpack (messageValue) { } break case '0F': - packageLen = 34 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) + dataValue = remainMessage.substring(2, 34) + messageValue = remainMessage.substring(34) dataObj = { 'dataId': dataId, 'dataValue': dataValue } break case '10': - packageLen = 26 - if (remainMessage.length < packageLen) { - return frameArray - } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) + dataValue = remainMessage.substring(2, 26) + messageValue = remainMessage.substring(26) dataObj = { 'dataId': dataId, 'dataValue': dataValue } break case '11': - packageLen = 28 - if (remainMessage.length < packageLen) { - return frameArray + dataValue = remainMessage.substring(2, 28) + messageValue = remainMessage.substring(28) + dataObj = { + 'dataId': dataId, 'dataValue': dataValue } - dataValue = remainMessage.substring(2, packageLen) - messageValue = remainMessage.substring(packageLen) + break + case '1A': + dataValue = remainMessage.substring(2, 56) + messageValue = remainMessage.substring(56) + dataObj = { + 'dataId': dataId, 'dataValue': dataValue + } + break + case '1B': + dataValue = remainMessage.substring(2, 96) + messageValue = remainMessage.substring(96) + dataObj = { + 'dataId': dataId, 'dataValue': dataValue + } + break + case '1C': + dataValue = remainMessage.substring(2, 82) + messageValue = remainMessage.substring(82) + dataObj = { + 'dataId': dataId, 'dataValue': dataValue + } + break + case '1D': + dataValue = remainMessage.substring(2, 40) + messageValue = remainMessage.substring(40) dataObj = { 'dataId': dataId, 'dataValue': dataValue } @@ -1131,8 +263,7 @@ function deserialize (dataId, dataValue) { let groupId = 0 let shardFlag = {} let payload = '' - let result = [] - let dataArr = [] + let motionId = '' switch (dataId) { case '01': measurementArray = getUpShortInfo(dataValue) @@ -1188,59 +319,65 @@ function deserialize (dataId, dataValue) { break case '06': collectTime = getUTCTimestamp(dataValue.substring(8, 16)) + motionId = getMotionId(dataValue.substring(6, 8)) measurementArray = [ - {measurementId: '4200', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, - {measurementId: '4197', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Longitude', measurementValue: parseFloat(getSensorValue(dataValue.substring(16, 24), 1000000))}, - {measurementId: '4198', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Latitude', measurementValue: parseFloat(getSensorValue(dataValue.substring(24, 32), 1000000))}, - {measurementId: '4097', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Air Temperature', measurementValue: getSensorValue(dataValue.substring(32, 36), 10)}, - {measurementId: '4199', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Light', measurementValue: getSensorValue(dataValue.substring(36, 40))}, - {measurementId: '3000', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Battery', measurementValue: getBattery(dataValue.substring(40, 42))} + {measurementId: '4200', timestamp: collectTime, motionId: motionId, type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, + {measurementId: '4197', timestamp: collectTime, motionId: motionId, type: 'Longitude', measurementValue: parseFloat(getSensorValue(dataValue.substring(16, 24), 1000000))}, + {measurementId: '4198', timestamp: collectTime, motionId: motionId, type: 'Latitude', measurementValue: parseFloat(getSensorValue(dataValue.substring(24, 32), 1000000))}, + {measurementId: '4097', timestamp: collectTime, motionId: motionId, type: 'Air Temperature', measurementValue: getSensorValue(dataValue.substring(32, 36), 10)}, + {measurementId: '4199', timestamp: collectTime, motionId: motionId, type: 'Light', measurementValue: getSensorValue(dataValue.substring(36, 40))}, + {measurementId: '3000', timestamp: collectTime, motionId: motionId, type: 'Battery', measurementValue: getBattery(dataValue.substring(40, 42))} ] break case '07': eventList = getEventStatus(dataValue.substring(0, 6)) collectTime = getUTCTimestamp(dataValue.substring(8, 16)) + motionId = getMotionId(dataValue.substring(6, 8)) measurementArray = [ - {measurementId: '4200', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, - {measurementId: '5001', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Wi-Fi Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 72))}, - {measurementId: '4097', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Air Temperature', measurementValue: getSensorValue(dataValue.substring(72, 76), 10)}, - {measurementId: '4199', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Light', measurementValue: getSensorValue(dataValue.substring(76, 80))}, - {measurementId: '3000', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Battery', measurementValue: getBattery(dataValue.substring(80, 82))} + {measurementId: '4200', timestamp: collectTime, motionId: motionId, type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, + {measurementId: '5001', timestamp: collectTime, motionId: motionId, type: 'Wi-Fi Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 72))}, + {measurementId: '4097', timestamp: collectTime, motionId: motionId, type: 'Air Temperature', measurementValue: getSensorValue(dataValue.substring(72, 76), 10)}, + {measurementId: '4199', timestamp: collectTime, motionId: motionId, type: 'Light', measurementValue: getSensorValue(dataValue.substring(76, 80))}, + {measurementId: '3000', timestamp: collectTime, motionId: motionId, type: 'Battery', measurementValue: getBattery(dataValue.substring(80, 82))} ] break case '08': collectTime = getUTCTimestamp(dataValue.substring(8, 16)) + motionId = getMotionId(dataValue.substring(6, 8)) measurementArray = [ - {measurementId: '4200', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, - {measurementId: '5002', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'BLE Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 58))}, - {measurementId: '4097', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Air Temperature', measurementValue: getSensorValue(dataValue.substring(58, 62), 10)}, - {measurementId: '4199', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Light', measurementValue: getSensorValue(dataValue.substring(62, 66))}, - {measurementId: '3000', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Battery', measurementValue: getBattery(dataValue.substring(66, 68))} + {measurementId: '4200', timestamp: collectTime, motionId: motionId, type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, + {measurementId: '5002', timestamp: collectTime, motionId: motionId, type: 'BLE Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 58))}, + {measurementId: '4097', timestamp: collectTime, motionId: motionId, type: 'Air Temperature', measurementValue: getSensorValue(dataValue.substring(58, 62), 10)}, + {measurementId: '4199', timestamp: collectTime, motionId: motionId, type: 'Light', measurementValue: getSensorValue(dataValue.substring(62, 66))}, + {measurementId: '3000', timestamp: collectTime, motionId: motionId, type: 'Battery', measurementValue: getBattery(dataValue.substring(66, 68))} ] break case '09': collectTime = getUTCTimestamp(dataValue.substring(8, 16)) + motionId = getMotionId(dataValue.substring(6, 8)) measurementArray = [ - {measurementId: '4200', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, - {measurementId: '4197', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Longitude', measurementValue: parseFloat(getSensorValue(dataValue.substring(16, 24), 1000000))}, - {measurementId: '4198', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Latitude', measurementValue: parseFloat(getSensorValue(dataValue.substring(24, 32), 1000000))}, - {measurementId: '3000', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Battery', measurementValue: getBattery(dataValue.substring(32, 34))} + {measurementId: '4200', timestamp: collectTime, motionId: motionId, type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, + {measurementId: '4197', timestamp: collectTime, motionId: motionId, type: 'Longitude', measurementValue: parseFloat(getSensorValue(dataValue.substring(16, 24), 1000000))}, + {measurementId: '4198', timestamp: collectTime, motionId: motionId, type: 'Latitude', measurementValue: parseFloat(getSensorValue(dataValue.substring(24, 32), 1000000))}, + {measurementId: '3000', timestamp: collectTime, motionId: motionId, type: 'Battery', measurementValue: getBattery(dataValue.substring(32, 34))} ] break case '0A': collectTime = getUTCTimestamp(dataValue.substring(8, 16)) + motionId = getMotionId(dataValue.substring(6, 8)) measurementArray = [ - {measurementId: '4200', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, - {measurementId: '5001', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Wi-Fi Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 72))}, - {measurementId: '3000', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Battery', measurementValue: getBattery(dataValue.substring(72, 74))} + {measurementId: '4200', timestamp: collectTime, motionId, type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, + {measurementId: '5001', timestamp: collectTime, motionId, type: 'Wi-Fi Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 72))}, + {measurementId: '3000', timestamp: collectTime, motionId, type: 'Battery', measurementValue: getBattery(dataValue.substring(72, 74))} ] break case '0B': collectTime = getUTCTimestamp(dataValue.substring(8, 16)) + motionId = getMotionId(dataValue.substring(6, 8)) measurementArray = [ - {measurementId: '4200', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, - {measurementId: '5002', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'BLE Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 58))}, - {measurementId: '3000', timestamp: collectTime, motionId: getMotionId(dataValue.substring(6, 8)), type: 'Battery', measurementValue: getBattery(dataValue.substring(58, 60))}, + {measurementId: '4200', timestamp: collectTime, motionId, type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, + {measurementId: '5002', timestamp: collectTime, motionId, type: 'BLE Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 58))}, + {measurementId: '3000', timestamp: collectTime, motionId, type: 'Battery', measurementValue: getBattery(dataValue.substring(58, 60))}, ] break case '0D': @@ -1277,10 +414,11 @@ function deserialize (dataId, dataValue) { collectTime = getUTCTimestamp(dataValue.substring(8, 16)) shardFlag = getShardFlag(dataValue.substring(26, 28)) groupId = getInt(dataValue.substring(28, 32)) + motionId = getMotionId(dataValue.substring(6, 8)) measurementArray.push({ measurementId: '4200', timestamp: collectTime, - motionId: getMotionId(dataValue.substring(6, 8)), + motionId, groupId: groupId, index: shardFlag.index, count: shardFlag.count, @@ -1290,42 +428,43 @@ function deserialize (dataId, dataValue) { measurementArray.push({ measurementId: '4097', timestamp: collectTime, - motionId: getMotionId(dataValue.substring(6, 8)), + motionId, groupId: groupId, index: shardFlag.index, count: shardFlag.count, type: 'Air Temperature', - measurementValue: '' + getSensorValue(dataValue.substring(16, 20), 10) + measurementValue: getSensorValue(dataValue.substring(16, 20), 10) }) measurementArray.push({ measurementId: '4199', timestamp: collectTime, - motionId: getMotionId(dataValue.substring(6, 8)), + motionId, groupId: groupId, index: shardFlag.index, count: shardFlag.count, type: 'Light', - measurementValue: '' + getSensorValue(dataValue.substring(20, 24)) + measurementValue: getSensorValue(dataValue.substring(20, 24)) }) measurementArray.push({ measurementId: '3000', timestamp: collectTime, - motionId: getMotionId(dataValue.substring(6, 8)), + motionId, groupId: groupId, index: shardFlag.index, count: shardFlag.count, type: 'Battery', - measurementValue: '' + getBattery(dataValue.substring(24, 26)) + measurementValue: getBattery(dataValue.substring(24, 26)) }) break case '10': collectTime = getUTCTimestamp(dataValue.substring(8, 16)) shardFlag = getShardFlag(dataValue.substring(18, 20)) groupId = getInt(dataValue.substring(20, 24)) + motionId = getMotionId(dataValue.substring(6, 8)) measurementArray.push({ measurementId: '4200', timestamp: collectTime, - motionId: getMotionId(dataValue.substring(6, 8)), + motionId, groupId: groupId, index: shardFlag.index, count: shardFlag.count, @@ -1335,12 +474,12 @@ function deserialize (dataId, dataValue) { measurementArray.push({ measurementId: '3000', timestamp: collectTime, - motionId: getMotionId(dataValue.substring(6, 8)), + motionId, groupId: groupId, index: shardFlag.index, count: shardFlag.count, type: 'Battery', - measurementValue: '' + getBattery(dataValue.substring(16, 18)) + measurementValue: getBattery(dataValue.substring(16, 18)) }) break case '11': @@ -1348,8 +487,8 @@ function deserialize (dataId, dataValue) { measurementArray.push({ measurementId: '3576', timestamp: collectTime, - type: 'Positing Status', - measurementValue: '' + getPositingStatus(dataValue.substring(0, 2)) + type: 'Positioning Status', + measurementValue: getPositingStatus(dataValue.substring(0, 2)) }) measurementArray.push({ timestamp: collectTime, @@ -1362,7 +501,7 @@ function deserialize (dataId, dataValue) { timestamp: collectTime, measurementId: '4097', type: 'Air Temperature', - measurementValue: '' + getSensorValue(dataValue.substring(16, 20), 10) + measurementValue: getSensorValue(dataValue.substring(16, 20), 10) }) } if (!isNaN(parseFloat(getSensorValue(dataValue.substring(20, 24))))) { @@ -1370,14 +509,112 @@ function deserialize (dataId, dataValue) { timestamp: collectTime, measurementId: '4199', type: 'Light', - measurementValue: '' + getSensorValue(dataValue.substring(20, 24)) + measurementValue: getSensorValue(dataValue.substring(20, 24)) }) } measurementArray.push({ timestamp: collectTime, measurementId: '3000', type: 'Battery', - measurementValue: '' + getBattery(dataValue.substring(24, 26)) + measurementValue: getBattery(dataValue.substring(24, 26)) + }) + break + case '1A': + collectTime = getUTCTimestamp(dataValue.substring(8, 16)) + motionId = getMotionId(dataValue.substring(6, 8)) + measurementArray = [ + {measurementId: '4200', timestamp: collectTime, motionId, type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, + {measurementId: '4197', timestamp: collectTime, motionId, type: 'Longitude', measurementValue: parseFloat(getSensorValue(dataValue.substring(16, 24), 1000000))}, + {measurementId: '4198', timestamp: collectTime, motionId, type: 'Latitude', measurementValue: parseFloat(getSensorValue(dataValue.substring(24, 32), 1000000))}, + {measurementId: '4097', timestamp: collectTime, motionId, type: 'Air Temperature', measurementValue: getSensorValue(dataValue.substring(32, 36), 10)}, + {measurementId: '4199', timestamp: collectTime, motionId, type: 'Light', measurementValue: getSensorValue(dataValue.substring(36, 40))}, + {measurementId: '4210', timestamp: collectTime, motionId, type: 'AccelerometerX', measurementValue: getSensorValue(dataValue.substring(40, 44))}, + {measurementId: '4211', timestamp: collectTime, motionId, type: 'AccelerometerY', measurementValue: getSensorValue(dataValue.substring(44, 48))}, + {measurementId: '4212', timestamp: collectTime, motionId, type: 'AccelerometerZ', measurementValue: getSensorValue(dataValue.substring(48, 52))}, + {measurementId: '3000', timestamp: collectTime, motionId, type: 'Battery', measurementValue: getBattery(dataValue.substring(52, 54))}, + ] + break + case '1B': + collectTime = getUTCTimestamp(dataValue.substring(8, 16)) + motionId = getMotionId(dataValue.substring(6, 8)) + measurementArray = [ + {measurementId: '4200', timestamp: collectTime, motionId, type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, + {measurementId: '5001', timestamp: collectTime, motionId, type: 'Wi-Fi Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 72))}, + {measurementId: '4097', timestamp: collectTime, motionId, type: 'Air Temperature', measurementValue: getSensorValue(dataValue.substring(72, 76), 10)}, + {measurementId: '4199', timestamp: collectTime, motionId, type: 'Light', measurementValue: getSensorValue(dataValue.substring(76, 80))}, + {measurementId: '4210', timestamp: collectTime, motionId, type: 'AccelerometerX', measurementValue: getSensorValue(dataValue.substring(80, 84))}, + {measurementId: '4211', timestamp: collectTime, motionId, type: 'AccelerometerY', measurementValue: getSensorValue(dataValue.substring(84, 88))}, + {measurementId: '4212', timestamp: collectTime, motionId, type: 'AccelerometerZ', measurementValue: getSensorValue(dataValue.substring(88, 92))}, + {measurementId: '3000', timestamp: collectTime, motionId, type: 'Battery', measurementValue: getBattery(dataValue.substring(92, 94))} + ] + break + case '1C': + collectTime = getUTCTimestamp(dataValue.substring(8, 16)) + motionId = getMotionId(dataValue.substring(6, 8)) + measurementArray = [ + {measurementId: '4200', timestamp: collectTime, motionId, type: 'Event Status', measurementValue: getEventStatus(dataValue.substring(0, 6))}, + {measurementId: '5002', timestamp: collectTime, motionId, type: 'BLE Scan', measurementValue: getMacAndRssiObj(dataValue.substring(16, 58))}, + {measurementId: '4097', timestamp: collectTime, motionId, type: 'Air Temperature', measurementValue: getSensorValue(dataValue.substring(58, 62), 10)}, + {measurementId: '4199', timestamp: collectTime, motionId, type: 'Light', measurementValue: getSensorValue(dataValue.substring(62, 66))}, + {measurementId: '4210', timestamp: collectTime, motionId, type: 'AccelerometerX', measurementValue: getSensorValue(dataValue.substring(66, 70))}, + {measurementId: '4211', timestamp: collectTime, motionId, type: 'AccelerometerY', measurementValue: getSensorValue(dataValue.substring(70, 74))}, + {measurementId: '4212', timestamp: collectTime, motionId, type: 'AccelerometerZ', measurementValue: getSensorValue(dataValue.substring(74, 78))}, + {measurementId: '3000', timestamp: collectTime, motionId, type: 'Battery', measurementValue: getBattery(dataValue.substring(78, 80))} + ] + break + case '1D': + collectTime = getUTCTimestamp(dataValue.substring(8, 16)) + measurementArray.push({ + measurementId: '3576', + timestamp: collectTime, + type: 'Positioning Status', + measurementValue: getPositingStatus(dataValue.substring(0, 2)) + }) + measurementArray.push({ + timestamp: collectTime, + measurementId: '4200', + type: 'Event Status', + measurementValue: getEventStatus(dataValue.substring(2, 8)) + }) + if (!isNaN(parseFloat(getSensorValue(dataValue.substring(16, 20), 10)))) { + measurementArray.push({ + timestamp: collectTime, + measurementId: '4097', + type: 'Air Temperature', + measurementValue: getSensorValue(dataValue.substring(16, 20), 10) + }) + } + if (!isNaN(parseFloat(getSensorValue(dataValue.substring(20, 24))))) { + measurementArray.push({ + timestamp: collectTime, + measurementId: '4199', + type: 'Light', + measurementValue: getSensorValue(dataValue.substring(20, 24)) + }) + } + measurementArray.push({ + timestamp: collectTime, + measurementId: '4210', + type: 'AccelerometerX', + measurementValue: getSensorValue(dataValue.substring(24, 28)) + }) + measurementArray.push({ + timestamp: collectTime, + measurementId: '4211', + type: 'AccelerometerY', + measurementValue: getSensorValue(dataValue.substring(28, 32)) + }) + measurementArray.push({ + timestamp: collectTime, + measurementId: '4212', + type: 'AccelerometerZ', + measurementValue: getSensorValue(dataValue.substring(32, 36)) + }) + measurementArray.push({ + timestamp: collectTime, + measurementId: '3000', + type: 'Battery', + measurementValue: getBattery(dataValue.substring(36, 38)) }) break } @@ -1389,6 +626,39 @@ function getMotionId (str) { } function getPositingStatus (str) { + let status = getInt(str) + switch (status) { + case 0: + return {id:status, statusName:"locate successful."} + case 1: + return {id:status, statusName:"The GNSS scan timed out."} + case 2: + return {id:status, statusName:"The Wi-Fi scan timed out."} + case 3: + return {id:status, statusName:"The Wi-Fi + GNSS scan timed out."} + case 4: + return {id:status, statusName:"The GNSS + Wi-Fi scan timed out."} + case 5: + return {id:status, statusName:"The Bluetooth scan timed out."} + case 6: + return {id:status, statusName:"The Bluetooth + Wi-Fi scan timed out."} + case 7: + return {id:status, statusName:"The Bluetooth + GNSS scan timed out."} + case 8: + return {id:status, statusName:"The Bluetooth + Wi-Fi + GNSS scan timed out."} + case 9: + return {id:status, statusName:"Location Server failed to parse the GNSS location."} + case 10: + return {id:status, statusName:"Location Server failed to parse the Wi-Fi location."} + case 11: + return {id:status, statusName:"Location Server failed to parse the Bluetooth location."} + case 12: + return {id:status, statusName:"Failed to parse location due to the poor accuracy."} + case 13: + return {id:status, statusName:"Time synchronization failed."} + case 14: + return {id:status, statusName:"Failed due to the old Almanac."} + } return getInt(str) } @@ -1526,7 +796,7 @@ function loraWANV2DataFormat (str, divisor = 1) { } }) str2 = parseInt(reverseArr.join(''), 2) + 1 - return '-' + str2 / divisor + return parseFloat('-' + str2 / divisor) } return parseInt(str2, 2) / divisor } @@ -1603,7 +873,6 @@ function getInt (str) { } function getEventStatus (str) { - // return getInt(str) let bitStr = getByteArray(str) let bitArr = [] for (let i = 0; i < bitStr.length; i++) { diff --git a/sites/en/docs/Sensor/SenseCAP/SenseCAP_T2000_Tracker/SenseCAP_T2000_intro.md b/sites/en/docs/Sensor/SenseCAP/SenseCAP_T2000_Tracker/SenseCAP_T2000_intro.md index ab4798d375fa2..e049825ec491a 100644 --- a/sites/en/docs/Sensor/SenseCAP/SenseCAP_T2000_Tracker/SenseCAP_T2000_intro.md +++ b/sites/en/docs/Sensor/SenseCAP/SenseCAP_T2000_Tracker/SenseCAP_T2000_intro.md @@ -7,10 +7,10 @@ keywords: image: https://files.seeedstudio.com/wiki/wiki-platform/S-tempor.png slug: /sensecap_t2000_tracker last_update: - date: 1/28/2026 + date: 3/12/2026 author: Janet createdAt: '2025-11-27' -updatedAt: '2026-03-03' +updatedAt: '2026-03-12' url: https://wiki.seeedstudio.com/sensecap_t2000_tracker/ --- @@ -23,7 +23,9 @@ url: https://wiki.seeedstudio.com/sensecap_t2000_tracker/
Introduction - User Guide + Quick Start + Payload Format + FAQ
### Integrated with LoRaWAN® Network Server diff --git a/sites/en/docs/Sensor/SenseCAP/SenseCAP_T2000_Tracker/User_Guide/FAQ.md b/sites/en/docs/Sensor/SenseCAP/SenseCAP_T2000_Tracker/User_Guide/FAQ.md new file mode 100644 index 0000000000000..246b9d5c96af0 --- /dev/null +++ b/sites/en/docs/Sensor/SenseCAP/SenseCAP_T2000_Tracker/User_Guide/FAQ.md @@ -0,0 +1,136 @@ +--- +description: SenseCAP T2000 Tracker FAQ +title: FAQ +keywords: + - Tracker + - SenseCAP +image: https://files.seeedstudio.com/wiki/SenseCAP/SenseCAP_T2000_Tracker/SenseCAP_T2000_Tracker_QuickStart.webp +slug: /t2000_faq +last_update: + date: 3/12/2026 + author: Janet +createdAt: '2026-03-12' +updatedAt: '2026-03-12' +url: https://wiki.seeedstudio.com/t2000_faq/ +--- + +# FAQ + +### Location Related + +
+What is the typical GNSS positioning accuracy of the T2000? + +- Under open-sky conditions, the GNSS positioning accuracy of the T2000 typically reaches meter-level accuracy. +- Test results show a CEP50 (Circular Error Probable 50%) of approximately **5–7 meters**, meaning that more than half of the location points fall within this range from the true position. +- Actual positioning accuracy may vary depending on environment, satellite visibility, and installation conditions etc. + + +
+ + +
+Why does GNSS positioning sometimes show drift, or no GNSS latitude and longitude data? + +- GNSS accuracy can be affected by several environmental factors: + - Buildings, trees, or other obstacles blocking satellite signals. + - Multipath effects caused by signal reflections from walls or metal surfaces. + - Electromagnetic interference from nearby electronic equipment. + - Poor antenna orientation or installation location. + +- In some cases, the device may not report GNSS latitude and longitude data because the GNSS scanning has timed out. This status can be seen in the uplink payload, where the **positioning status** field will display **"GNSS scan timeout"** due to the same environmental conditions mentioned above. + +- For best results, please install the device in an open outdoor area with a clear view of the sky. + +
+ + +
+How should the T2000 be installed to achieve the best GNSS performance? + +- Place the device in an open environment with minimal obstruction to satellite signals. +- Ensure the GNSS antenna area faces upward toward the sky. +- Avoid installing the device near large metal objects or dense structures. +- Avoid covering the device or placing it inside sealed metal enclosures. +![Antenna](https://files.seeedstudio.com/wiki/SenseCAP/SenseCAP_T2000_Tracker/T2000-antenna.png) + +
+ +
+Why doesn't Wi-Fi or Bluetooth location display on the SenseCraft App map? + +- Wi-Fi and Bluetooth location requires a third-party map parsing service, which must be invoked by users for parsing. Currently, the SenseCraft App supports GNSS positioning display only. + +
+ +
+ +For more details on GNSS positioning, please refer to the blog: [How Accurate is the SenseCAP T2000 GNSS Positioning?](https://www.seeedstudio.com/blog/2026/01/19/how-accurate-is-the-sensecap-t2000-gnss-positioning/) + + +### Battery Related + +
+What is the difference between T2000-A/B and T2000-C battery? + +- **T2000-A/B** + - Powered by an **8000mAh primary battery**. + - Designed for long-term deployment without recharging. + +- **T2000-C** + - Powered by a **4000mAh rechargeable battery**. + - Equipped with a **0.5W solar panel** for continuous outdoor operation. + - Suitable for deployments where sunlight is available and maintenance needs to be minimized. + +
+ + +
+How efficient is the solar charging on the T2000-C? + +- The T2000-C uses a **0.5W solar panel with a rechargeable battery** to support long-term outdoor operation. +- The solar panel can generate **up to about 60mA charging current**, producing roughly **60mAh** of energy per hour under good sunlight conditions.(this data is for reference only) + +
+ + +
+What factors affect the solar charging efficiency? + +- Solar charging performance can vary depending on: + - Sunlight exposure and intensity + - Panel orientation and installation angle + - Shading from nearby objects + - Dust, dirt, or debris on the solar panel + - Ambient temperature (battery charging works between 0–45°C) + +- For best performance, install the device in a location with direct sunlight and periodically check the panel surface. + +
+ + +
+Can the T2000-C operate continuously with solar power? + +- In low-power configurations (such as longer uplink intervals), solar charging can even maintain or increase battery level during daily operation. +- However, frequent reporting intervals (for example, every 1 minute) may consume more power than the solar panel can replenish. +- For more detailed analysis of the solar charging performance, please refer to the following blog: [How Efficient Is the Solar Charging on the SenseCAP T2000‑C?](https://www.seeedstudio.com/blog/2026/01/19/how-efficient-is-the-solar-charging-on-the-sensecap-t2000-c/) + +
+ +The estimated battery life can be calculated using the following [Battery Life Calculator](https://files.seeedstudio.com/products/SenseCAP/T2000_Tracker/SenseCAP_Tracker_Battery_Life_Calculator_T2000.xls). + + +## Tech Support & Product Discussion + +Thank you for choosing our products! We are here to provide you with different support to ensure that your experience with our products is as smooth as possible. We offer several communication channels to cater to different preferences and needs. + +
+ + +
+ +
+ + +
\ No newline at end of file diff --git a/sites/en/docs/Sensor/SenseCAP/SenseCAP_T2000_Tracker/User_Guide/Payload_Format.md b/sites/en/docs/Sensor/SenseCAP/SenseCAP_T2000_Tracker/User_Guide/Payload_Format.md new file mode 100644 index 0000000000000..a8b1574e5b89e --- /dev/null +++ b/sites/en/docs/Sensor/SenseCAP/SenseCAP_T2000_Tracker/User_Guide/Payload_Format.md @@ -0,0 +1,522 @@ +--- +description: SenseCAP T2000 Tracker Payload Format +title: Payload Format +keywords: + - Tracker + - SenseCAP +image: https://files.seeedstudio.com/wiki/SenseCAP/SenseCAP_T2000_Tracker/SenseCAP_T2000_Tracker_QuickStart.webp +slug: /t2000_payload_format +last_update: + date: 3/12/2026 + author: Janet +createdAt: '2026-03-12' +updatedAt: '2026-03-12' +url: https://wiki.seeedstudio.com/t2000_payload_format/ +--- + +# Payload Format + +## Uplink Packet Parsing + +The tracker data protocol provides different packets to correspond to different information, and the number of bytes of each packet may vary. The structure of the frame is shown in the image below. The frame content is sent in **big-endian byte order**. + +|Data ID|Data Value| +| - | :- | +|1 byte|50 bytes (Max)| + +**Data ID**: Function number.
+**Data Value**: Position, sensor data and other information. + +### Power-on Packet (0x27) + +The power-on packet is sent by the device immediately after booting. It contains the current configuration parameters and device status. The frame ID is `0x27`, and the total length is 46 bytes. + +| 0x27 | Byte2 | Byte3~4 | Byte5~6 | Byte7 | Byte8 | Byte9~10 | Byte11~12 | +| :--: | :---: | :-----: | :-----: | :--: | :--: | :------: | :-------: | +| ID | Battery Level | Software Version | Hardware Version | Work Mode | Positioning Strategy | Heartbeat Interval | Periodic Mode Uplink Interval | + +| Byte13~14 | Byte15 | Byte16 | Byte17 | Byte18 | Byte19~20 | Byte21~22 | +| :-------: | :----: | :----: | :----: | :----: | :-------: | :-------: | +| Event Mode Uplink Interval | Enable 3-Axis Accelerometer | Enable Disassembly Alarm | GNSS Scan Timeout | Enable Motion Event | 3-Axis Motion Threshold | Uplink Interval On Motion | + +| Byte23 | Byte24~25 | Byte26 | Byte27~28 | Byte29 | Byte30 | Byte31~46 | +| :----: | :-------: | :----: | :-------: | :----: | :----: | :-------: | +| Enable Motionless Event | Motionless Timeout | Enable Shock Event | 3-Axis Shock Threshold | iBeacon Scan Timeout (s) | UUID Filter Valid Bytes | UUID Filter (16 Bytes) | + +**Raw Payload Example** + +`27 56 0100 0101 01 08 02d0 003c 003c 00 01 3c 00 001e 0005 00 0168 00 012c 03 00 00000000000000000000000000000000` + +| Byte | Value | Type | Raw Data | Description | +| :---: | :--- | :---: | :---: | :--- | +| 1 | Frame ID | uint8 | 27 | 27 is the packet ID | +| 2 | Battery Level | uint8 | 56 | `0x56` = 86(DEC)
The battery level is 86% | +| 3~4 | Software Version | uint16 | 0100 | `0x0100` = v1.0
The software version is v1.0 | +| 5~6 | Hardware Version | uint16 | 0101 | `0x0101` = v1.1
The hardware version is v1.1 | +| 7 | Work Mode | uint8 | 01 | 01 = Periodic Mode
`00`: Standby Mode
`01`: Periodic Mode
`02`: Event Mode | +| 8 | Positioning Strategy | uint8 | 00 | 07 = 0x07, means the device use Bluetooth + Wi-Fi + GNSS positioning strategy
`00`: Only GNSS
`01`: Only Wi-Fi
`02`: Wi-Fi + GNSS
`03`: GNSS + Wi-Fi
`04`: Only Bluetooth
`05`: Bluetooth + Wi-Fi
`06`: Bluetooth + GNSS
`07`: Bluetooth + Wi-Fi + GNSS
`08`: GNSS + Bluetooth | +| 9~10 | Heartbeat Interval | uint16 | 02d0 | `0x02D0` = 720 minutes | +| 11~12 | Periodic Mode Uplink Interval | uint16 | 003c | `0x003C` = 60 minutes | +| 13~14 | Event Mode Uplink Interval | uint16 | 003c | `0x003C` = 60 minutes | +| 15 | Enable 3-Axis Accelerometer | uint8 | 00 | `00`: Disable
`01`: Enable | +| 16 | Enable Disassembly Alarm | uint8 | 01 | `00`: Disable
`01`: Enable | +| 17 | GNSS Scan Timeout | uint8 | 3c | `0x3C` = 60 seconds | +| 18 | Enable Motion Event | uint8 | 00 | `00`: Disable
`01`: Enable | +| 19~20 | 3-Axis Motion Threshold | uint16 | 001e | `0x001e` = 30 mg | +| 21~22 | Uplink Interval On Motion | uint16 | 0005 | `0x05` = 5 minutes | +| 23 | Enable Motionless Event | uint8 | 00 | `0x00`: Disable
`0x01`: Enable | +| 24~25 | Motionless Timeout | uint16 | 0168 | `0x0168` = 360 minutes | +| 26 | Enable Shock Event | uint8 | 00 | `00`: Disable
`01`: Enable | +| 27~28 | 3-Axis Shock Threshold | uint16 | 012c | `0x012c` = 300 mg | +| 29 | iBeacon Scan Timeout (s) | uint8 | 03 | `0x03` = 3 seconds | +| 30 | UUID Filter Valid Bytes | uint8 | 00 | Number of valid bytes in UUID filter (0–16) | +| 31~46 | UUID Filter | 16 bytes | 0000000000000000
0000000000000000 | 16‑byte Bluetooth UUID filter. Only first N bytes (defined by byte30) are meaningful | + +### Periodic Mode Packet (0x28) + +The periodic mode parameters packet contains the current work mode configuration. The frame ID is `0x28`, and the total length is 30 bytes. + +| 0x28 | Byte2 | Byte3 | Byte4~5 | Byte6~7 | Byte8~9 | Byte10 | Byte11 | Byte12 | Byte13 | Byte14 | Byte15~30 | +| :--: | :---: | :---: | :-----: | :-----: | :-----: | :----: | :----: | :----: | :----: | :----: | :-------: | +| ID | Work Mode | Positioning Strategy | Heartbeat Interval | Uplink Interval | Event Mode Uplink Interval | Enable 3-Axis Accelerometer | Enable Disassembly Alarm | GNSS Scan Timeout | iBeacon Scan Timeout | UUID Filter Valid Bytes | UUID Filter (16 Bytes) | + +**Raw Payload Example** + +`28 01 07 02d0 003c 003c 01 00 3c 0a 10 00000000000000000000000000000000` + +| Byte | Value | Type | Raw Data | Description | +| :---: | :--- | :---: | :---: | :--- | +| 1 | Frame ID | uint8 | 28 | 28 is the packet ID | +| 2 | Work Mode | uint8 | 01 | 01 = Periodic Mode
`00`: Standby Mode
`01`: Periodic Mode
`02`: Event Mode | +| 3 | Positioning Strategy | uint8 | 07 | 07 = 0x07, means the device use Bluetooth + Wi-Fi + GNSS positioning strategy
`00`: Only GNSS
`01`: Only Wi-Fi
`02`: Wi-Fi + GNSS
`03`: GNSS + Wi-Fi
`04`: Only Bluetooth
`05`: Bluetooth + Wi-Fi
`06`: Bluetooth + GNSS
`07`: Bluetooth + Wi-Fi + GNSS
`08`: GNSS + Bluetooth | +| 4~5 | Heartbeat Interval | uint16 | 02d0 | `0x02D0` = 720 minutes | +| 6~7 | Uplink Interval | uint16 | 003c | `0x003C` = 60 minutes | +| 8~9 | Event Mode Uplink Interval | uint16 | 003c | `0x003C` = 60 minutes
When no event is triggered, data will be uploaded every 60 minutes.
| +| 10 | Enable 3-Axis Accelerometer | uint8 | 01 | `00`: Disable
`01`: Enable | +| 11 | Enable Disassembly Alarm | uint8 | 00 | `00`: Disable
`01`: Enable | +| 12 | GNSS Scan Timeout | uint8 | 3c | `0x3C` = 60 seconds | +| 13 | iBeacon Scan Timeout | uint8 | 0a | `0x0A` = 10 seconds | +| 14 | UUID Filter Valid Bytes | uint8 | 10 | Number of valid bytes in UUID filter (0–16) | +| 15~30 | UUID Filter | 16 bytes | 0000000000000000
0000000000000000 | 16‑byte Bluetooth UUID filter. Only first N bytes (defined by byte14) are meaningful | + +### Event Mode Packet (0x29) + +The event parameters packet contains the motion, motionless, and shock event configuration settings. The frame ID is `0x29`, and the total length is 12 bytes. + +| 0x29 | Byte2 | Byte3~4 | Byte5~6 | Byte7 | Byte8~9 | Byte10 | Byte11~12 | +| :--: | :---: | :-----: | :-----: | :--: | :-----: | :----: | :-------: | +| ID | Enable Motion Event | 3-Axis Motion Threshold | Uplink Interval On Motion | Enable Motionless Event | Motionless Timeout | Enable Shock Event | 3-Axis Shock Threshold | + +**Raw Payload Example** + +`29 01 0064 001e 01 012c 00 012c` + +| Byte | Value | Type | Raw Data | Description | +| :---: | :--- | :---: | :---: | :--- | +| 1 | Frame ID | uint8 | 29 | 29 is the packet ID | +| 2 | Enable Motion Event | uint8 | 01 | `00`: Disable
`01`: Enable | +| 3~4 | 3-Axis Motion Threshold | uint16 | 0064 | `0x0064` = 100 mg | +| 5~6 | Uplink Interval On Motion | uint16 | 001e | `0x001E` = 30 minitues | +| 7 | Enable Motionless Event | uint8 | 01 | `0x00`: Disable
`0x01`: Enable | +| 8~9 | Motionless Timeout | uint16 | 012c | `0x012C` = 300 minitues | +| 10 | Enable Shock Event | uint8 | 00 | `0x00`: Disable
`0x01`: Enable | +| 11~12 | 3-Axis Shock Threshold | uint16 | 0000 | `0x012c` = 300 mg | + +### Heartbeat Packet (0x2A) + +The heartbeat packet is sent periodically by the device to report its current status. It contains basic device information and sensor states. The frame ID is `0x2A`, and the total length is 6 bytes. + +| 0x2A | Byte2 | Byte3 | Byte4 | Byte5 | Byte6 | +| :--: | :---: | :---: | :---: | :---: | :---: | +| ID | Battery Level | Work Mode | Positioning Strategy | Enable 3-Axis Accelerometer | Enable Disassembly Alarm | + +**Raw Payload Example** + +`2a 56 01 07 01 00` + +| Byte | Value | Type | Raw Data | Description | +| :---: | :--- | :---: | :---: | :--- | +| 1 | Frame ID | uint8 | 2A | 2A is the packet ID | +| 2 | Battery Level | uint8 | 56 | `0x56` = 86(DEC)
The battery level is 86% | +| 3 | Work Mode | uint8 | 01 | 01 = Periodic Mode
`00`: Standby Mode
`01`: Periodic Mode
`02`: Event Mode | +| 4 | Positioning Strategy | uint8 | 07 | 07 = 0x07, means the device use Bluetooth + Wi-Fi + GNSS positioning strategy
`00`: Only GNSS
`01`: Only Wi-Fi
`02`: Wi-Fi + GNSS
`03`: GNSS + Wi-Fi
`04`: Only Bluetooth
`05`: Bluetooth + Wi-Fi
`06`: Bluetooth + GNSS
`07`: Bluetooth + Wi-Fi + GNSS
`08`: GNSS + Bluetooth | +| 5 | Enable 3-Axis Accelerometer | uint8 | 01 | `00`: Disable
`01`: Enable | +| 6 | Enable Disassembly Alarm | uint8 | 00 | `00`: Disable
`01`: Enable | + +### GNSS Location Data Packet (Accelerometer On, 0x2B) + +The GPS location data packet contains GNSS positioning data along with accelerometer and battery information. The frame ID is `0x2B`, and the total length is 23 bytes. + +| 0x2B | Byte2~3 | Byte4 | Byte5~8 | Byte9~10 | Byte11~12 | Byte13~14 | Byte15~18 | Byte19~22 | Byte23 | +| :--: | :-----: | :--: | :-----: | :------: | :-------: | :-------: | :------: | :------: | :---: | +| ID | Event Status | Motion ID | UTC Timestamp | Accelerometer X | Accelerometer Y | Accelerometer Z | Longitude | Latitude | Battery Level | + +**Raw Payload Example** + +`2b 0100 00 694b3dc6 032f fffe 0241 06ca5098 01587ee4 62` + +| Byte | Value | Type | Raw Data | Description | +| :---: | :--- | :---: | :---: | :--- | +| 1 | Frame ID | uint8 | 2B | 2B is the packet ID | +| 2~3 | Event Status | uint16 | 0100 | `0x0100` = disassembled event
Bit 0: false
Bit 1: Start moving event
Bit 2: End movement event
Bit 3: Motionless event
Bit 4: Shock event
Bit 5: Temperature event
Bit 6: Light event
Bit 7: SOS event
Bit 8: Press once event
Bit 9: Disassembled event

Convert to hexadecimal:
`0x0001`: Start moving event
`0x0002`: End movement event
`0x0004`: Motionless event
`0x0008`: Shock event
`0x0010`: Temperature event
`0x0020`: Light event
`0x0040`: SOS event
`0x0080`: Press once event
`0x0100`: Disassembled event | +| 4 | Motion ID | uint8 | 00 | `0`: Does not need to be recorded as a specific motion.
`1~255`: Positioning data reported under the same motion status (same ID refers to the same motion) | +| 5~8 | UTC Timestamp | uint32 | 694b3dc6 | `0x694B3DC6` = 1766538694(DEC) seconds

Convert it to UTC Time:
2025-12-24 01:11:34 | +| 9~10 | Accelerometer X | int16 | 032f | `0x032F` = 815 mg | +| 11~12 | Accelerometer Y | int16 | fffe | `0xFFFE` = -2 mg | +| 13~14 | Accelerometer Z | int16 | 0241 | `0x0241` = 577 mg | +| 15~18 | Longitude | uint32 | 06ca5098 | `0x06CA5098` = 113,922,200 → 113.922200° | +| 19~22 | Latitude | uint32 | 01587ee4 | `0x01587EE4` = 22,576,868 → 22.576868° | +| 23 | Battery Level | uint8 | 62 | `0x62` = 98% | + +### Wi-Fi Location Data Packet (Accelerometer On, 0x2C) + +The Wi-Fi location packet contains Wi-Fi scan results along with accelerometer and battery information. The frame ID is `0x2C`, and the total length is dynamic based on the number of Wi-Fi access points scanned (23 + (n-1) * 7 bytes, where n is the number of MAC-RSSI pairs). + +| 0x2C | Byte2~3 | Byte4 | Byte5~8 | Byte9~10 | Byte11~12 | Byte13~14 | Byte15 | Byte16 | Byte17+(n-1)*7 ~ Byte23+(n-1)*7 | +| :--: | :-----: | :--: | :-----: | :------: | :-------: | :-------: | :---: | :---: | :---------------------------: | +| ID | Event Status | Motion ID | UTC Timestamp | Accelerometer X | Accelerometer Y | Accelerometer Z | Battery Level | MAC-RSSI Count (n) | MAC-RSSI Pairs (n) | + +**MAC-RSSI Format** + +| Byte0~5 | Byte6 | +| :-----: | :---: | +| MAC Address (6 bytes) | RSSI (int8) | + +**Raw Payload Example** + +`2c 0000 00 69685f82 0004 0015 03e5 64 05 107c61841bf8 e4 3447d468f627 e1 a4ba70bc229d d3 9483c46d5dfc d2 4c10d567b467 d0` + +| Byte | Value | Type | Raw Data | Description | +| :---: | :--- | :---: | :---: | :--- | +| 1 | Frame ID | uint8 | 2C | 2C is the packet ID | +| 2~3 | Event Status | uint16 | 0000 |`0x0000` = No events triggered
Bit 0: false
Bit 1: Start moving event
Bit 2: End movement event
Bit 3: Motionless event
Bit 4: Shock event
Bit 5: Temperature event
Bit 6: Light event
Bit 7: SOS event
Bit 8: Press once event
Bit 9: Disassembled event

Convert to hexadecimal:
`0x0001`: Start moving event
`0x0002`: End movement event
`0x0004`: Motionless event
`0x0008`: Shock event
`0x0010`: Temperature event
`0x0020`: Light event
`0x0040`: SOS event
`0x0080`: Press once event
`0x0100`: Disassembled event| +| 4 | Motion ID | uint8 | 00 | `0`: Does not need to be recorded as a specific motion.
`1~255`: Positioning data reported under the same motion status (same ID refers to the same motion) | +| 5~8 | UTC Timestamp | uint32 | 69685f82 | `0x69685F82` = 1768447874(DEC) seconds

Convert it to UTC Time:
2026-01-15 03:31:14 | +| 9~10 | Accelerometer X | int16 | 0004 | `0x0004` = 4 mg | +| 11~12 | Accelerometer Y | int16 | 0015 | `0x0015` = 21 mg | +| 13~14 | Accelerometer Z | int16 | 03e5 | `0x03E5` = 997 mg | +| 15 | Battery Level | uint8 | 64 | `0x64` = 100% | +| 16 | MAC-RSSI Count (n) | uint8 | 05 | Number of Wi-Fi access points detected (n = 5) | +| 17~23 | MAC-RSSI Pair 1 | 7 bytes | 107c61841bf8 e4 | MAC: `10:7C:61:84:1B:F8`, RSSI: `0xE4` = -28 (int8) | +| 24~30 | MAC-RSSI Pair 2 | 7 bytes | 3447d468f627 e1 | MAC: `34:47:D4:68:F6:27`, RSSI: `0xE1` = -31 (int8) | +| 31~37 | MAC-RSSI Pair 3 | 7 bytes | a4ba70bc229d d3 | MAC: `A4:BA:70:BC:22:9D`, RSSI: `0xD3` = -45 (int8) | +| 38~44 | MAC-RSSI Pair 4 | 7 bytes | 9483c46d5dfc d2 | MAC: `94:83:C4:6D:5D:FC`, RSSI: `0xD2` = -46 (int8) | +| 45~51 | MAC-RSSI Pair 5 | 7 bytes | 4c10d567b467 d0 | MAC: `4C:10:D5:67:B4:67`, RSSI: `0xD0` = -48 (int8) | + +### BLE Location Data Packet (Accelerometer On,0x2D) + +The BLE location packet contains Bluetooth scan results along with accelerometer and battery information. The frame ID is `0x2D`, and the total length is dynamic based on the number of Bluetooth devices scanned (23 + (n-1) * 7 bytes, where n is the number of MAC-RSSI pairs, maximum n = 5). + +| 0x2D | Byte2~3 | Byte4 | Byte5~8 | Byte9~10 | Byte11~12 | Byte13~14 | Byte15 | Byte16 | Byte17+(n-1)*7 ~ Byte23+(n-1)*7 | +| :--: | :-----: | :--: | :-----: | :------: | :-------: | :-------: | :---: | :---: | :---------------------------: | +| ID | Event Status | Motion ID | UTC Timestamp | Accelerometer X | Accelerometer Y | Accelerometer Z | Battery Level | MAC-RSSI Count (n) | MAC-RSSI Pairs (n) | + +**MAC-RSSI Format** + +| Byte0~5 | Byte6 | +| :-----: | :---: | +| MAC Address (6 bytes) | RSSI (int8) | + +**Raw Payload Example** + +`2d 0000 00 69686032 fff9 0015 03df 64 05 c30000564b3b ce c20303003f00 ce 588c81a0fbf2 cc c20303003f03 cb c30000564af2 c7` + +| Byte | Value | Type | Raw Data | Description | +| :---: | :--- | :---: | :---: | :--- | +| 1 | Frame ID | uint8 | 2D | 2D is the packet ID | +| 2~3 | Event Status | uint16 | 0000 |`0x0000` = No events triggered
Bit 0: false
Bit 1: Start moving event
Bit 2: End movement event
Bit 3: Motionless event
Bit 4: Shock event
Bit 5: Temperature event
Bit 6: Light event
Bit 7: SOS event
Bit 8: Press once event
Bit 9: Disassembled event

Convert to hexadecimal:
`0x0001`: Start moving event
`0x0002`: End movement event
`0x0004`: Motionless event
`0x0008`: Shock event
`0x0010`: Temperature event
`0x0020`: Light event
`0x0040`: SOS event
`0x0080`: Press once event
`0x0100`: Disassembled event| +| 4 | Motion ID | uint8 | 00 | `0`: Does not need to be recorded as a specific motion.
`1~255`: Positioning data reported under the same motion status (same ID refers to the same motion) | +| 5~8 | UTC Timestamp | uint32 | 69686032 | `0x69686032` = 1768448050(DEC) seconds

Convert it to UTC Time:
2026-01-15 03:34:10 | +| 9~10 | Accelerometer X | int16 | fff9 | `0xFFF9` = -7 mg | +| 11~12 | Accelerometer Y | int16 | 0015 | `0x0015` = 21 mg | +| 13~14 | Accelerometer Z | int16 | 03df | `0x03DF` = 991 mg | +| 15 | Battery Level | uint8 | 64 | `0x64` = 100% | +| 16 | MAC-RSSI Count (n) | uint8 | 05 | Number of Bluetooth devices detected (n = 5, maximum 5) | +| 17~23 | MAC-RSSI Pair 1 | 7 bytes | c30000564b3b ce | MAC: `C3:00:00:56:4B:3B`, RSSI: `0xCE` = -50 (int8) | +| 24~30 | MAC-RSSI Pair 2 | 7 bytes | c20303003f00 ce | MAC: `C2:03:03:00:3F:00`, RSSI: `0xCE` = -50 (int8) | +| 31~37 | MAC-RSSI Pair 3 | 7 bytes | 588c81a0fbf2 cc | MAC: `58:8C:81:A0:FB:F2`, RSSI: `0xCC` = -52 (int8) | +| 38~44 | MAC-RSSI Pair 4 | 7 bytes | c20303003f03 cb | MAC: `C2:03:03:00:3F:03`, RSSI: `0xCB` = -53 (int8) | +| 45~51 | MAC-RSSI Pair 5 | 7 bytes | c30000564af2 c7 | MAC: `C3:00:00:56:4A:F2`, RSSI: `0xC7` = -57 (int8) | + +### GNSS Location Data Packet(Accelerometer Off, 0x2E) + +The GNSS location data packet contains GPS positioning data along with battery information. The frame ID is `0x2E`, and the total length is 17 bytes. + +| 0x2E | Byte2~3 | Byte4 | Byte5~8 | Byte9~12 | Byte13~16 | Byte17 | +| :--: | :-----: | :--: | :-----: | :------: | :-------: | :----: | +| ID | Event Status | Motion ID | UTC Timestamp | Longitude | Latitude | Battery Level | + +**Raw Payload Example** + +`2e 0100 01 64f1a2b3 06ca5098 01587ee4 62` + +| Byte | Value | Type | Raw Data | Description | +| :---: | :--- | :---: | :---: | :--- | +| 1 | Frame ID | uint8 | 2E | 2E is the packet ID | +| 2~3 | Event Status | uint16 | 0000 |`0x0000` = No events triggered
Bit 0: false
Bit 1: Start moving event
Bit 2: End movement event
Bit 3: Motionless event
Bit 4: Shock event
Bit 5: Temperature event
Bit 6: Light event
Bit 7: SOS event
Bit 8: Press once event
Bit 9: Disassembled event

Convert to hexadecimal:
`0x0001`: Start moving event
`0x0002`: End movement event
`0x0004`: Motionless event
`0x0008`: Shock event
`0x0010`: Temperature event
`0x0020`: Light event
`0x0040`: SOS event
`0x0080`: Press once event
`0x0100`: Disassembled event| +| 4 | Motion ID | uint8 | 00 | `0`: Does not need to be recorded as a specific motion.
`1~255`: Positioning data reported under the same motion status (same ID refers to the same motion) | +| 5~8 | UTC Timestamp | uint32 | 64f1a2b3 | `0x64f1a2b3` = 1693557427(DEC) seconds

Convert it to UTC Time:
2023-09-01 08:37:07 | +| 9~12 | Longitude | uint32 | 06ca5098 | `0x06CA5098` = 113,922,200 → 113.922200° | +| 13~16 | Latitude | uint32 | 01587ee4 | `0x01587EE4` = 22,576,868 → 22.576868° | +| 17 | Battery Level | uint8 | 62 | `0x62` = 98% | + +### Wi-Fi Location Data Packet (Accelerometer Off, 0x2F) + +The Wi-Fi location data packet contains Wi-Fi scan results along with battery information. The frame ID is `0x2F`, and the total length is dynamic based on the number of Wi-Fi access points scanned (17 + (n-1) * 7 bytes, where n is the number of MAC-RSSI pairs, maximum n = 5). + +| 0x2F | Byte2~3 | Byte4 | Byte5~8 | Byte9 | Byte10 | Byte11+(n-1)*7 ~ Byte16+(n-1)*7 | +| :--: | :-----: | :--: | :-----: | :---: | :----: | :---------------------------: | +| ID | Event Status | Motion ID | UTC Timestamp | Battery Level | MAC-RSSI Count (n) | MAC-RSSI Pairs (n) | + +**MAC-RSSI Format** + +| Byte0~5 | Byte6 | +| :-----: | :---: | +| MAC Address (6 bytes) | RSSI (int8) | + +**Raw Payload Example** + +`2f 0000 00 69685f82 64 05 107c61841bf8 e4 3447d468f627 e1 a4ba70bc229d d3 9483c46d5dfc d2 4c10d567b467 d0` + +| Byte | Value | Type | Raw Data | Description | +| :---: | :--- | :---: | :---: | :--- | +| 1 | Frame ID | uint8 | 2F | 2F is the packet ID | +| 2~3 | Event Status | uint16 | 0000 |`0x0000` = No events triggered
Bit 0: false
Bit 1: Start moving event
Bit 2: End movement event
Bit 3: Motionless event
Bit 4: Shock event
Bit 5: Temperature event
Bit 6: Light event
Bit 7: SOS event
Bit 8: Press once event
Bit 9: Disassembled event

Convert to hexadecimal:
`0x0001`: Start moving event
`0x0002`: End movement event
`0x0004`: Motionless event
`0x0008`: Shock event
`0x0010`: Temperature event
`0x0020`: Light event
`0x0040`: SOS event
`0x0080`: Press once event
`0x0100`: Disassembled event| +| 4 | Motion ID | uint8 | 00 | `0`: Does not need to be recorded as a specific motion.
`1~255`: Positioning data reported under the same motion status (same ID refers to the same motion) | +| 5~8 | UTC Timestamp | uint32 | 69685f82 | `0x69685F82` = 1768447874(DEC) seconds

Convert it to UTC Time:
2026-01-15 03:31:14 | +| 9 | Battery Level | uint8 | 64 | `0x64` = 100% | +| 10 | MAC-RSSI Count (n) | uint8 | 05 | Number of Wi-Fi access points detected (n = 5, maximum 5) | +| 11~17 | MAC-RSSI Pair 1 | 7 bytes | 107c61841bf8 e4 | MAC: `10:7C:61:84:1B:F8`,
RSSI: `0xE4` = -28 (int8) | +| 18~24 | MAC-RSSI Pair 2 | 7 bytes | 3447d468f627 e1 | MAC: `34:47:D4:68:F6:27`,
RSSI: `0xE1` = -31 (int8) | +| 25~31 | MAC-RSSI Pair 3 | 7 bytes | a4ba70bc229d d3 | MAC: `A4:BA:70:BC:22:9D`,
RSSI: `0xD3` = -45 (int8) | +| 32~38 | MAC-RSSI Pair 4 | 7 bytes | 9483c46d5dfc d2 | MAC: `94:83:C4:6D:5D:FC`,
RSSI: `0xD2` = -46 (int8) | +| 39~45 | MAC-RSSI Pair 5 | 7 bytes | 4c10d567b467 d0 | MAC: `4C:10:D5:67:B4:67`,
RSSI: `0xD0` = -48 (int8) | + +### BLE Location Data Packet (Accelerometer Off, 0x30) + +The BLE location data packet contains Bluetooth scan results along with battery information. The frame ID is `0x30`, and the total length is dynamic based on the number of Bluetooth devices scanned (17 + (n-1) * 7 bytes, where n is the number of MAC-RSSI pairs, maximum n = 5). + +| 0x30 | Byte2~3 | Byte4 | Byte5~8 | Byte9 | Byte10 | Byte11+(n-1)*7 ~ Byte16+(n-1)*7 | +| :--: | :-----: | :--: | :-----: | :---: | :----: | :---------------------------: | +| ID | Event Status | Motion ID | UTC Timestamp | Battery Level | MAC-RSSI Count (n) | MAC-RSSI Pairs (n) | + +**MAC-RSSI Format** + +| Byte0~5 | Byte6 | +| :-----: | :---: | +| MAC Address (6 bytes) | RSSI (int8) | + +**Raw Payload Example** + +`30 0000 00 69686032 64 05 c30000564b3b ce c20303003f00 ce 588c81a0fbf2 cc c20303003f03 cb c30000564af2 c7` + +| Byte | Value | Type | Raw Data | Description | +| :---: | :--- | :---: | :---: | :--- | +| 1 | Frame ID | uint8 | 30 | 30 is the packet ID | +| 2~3 | Event Status | uint16 | 0000 |`0x0000` = No events triggered
Bit 0: false
Bit 1: Start moving event
Bit 2: End movement event
Bit 3: Motionless event
Bit 4: Shock event
Bit 5: Temperature event
Bit 6: Light event
Bit 7: SOS event
Bit 8: Press once event
Bit 9: Disassembled event

Convert to hexadecimal:
`0x0001`: Start moving event
`0x0002`: End movement event
`0x0004`: Motionless event
`0x0008`: Shock event
`0x0010`: Temperature event
`0x0020`: Light event
`0x0040`: SOS event
`0x0080`: Press once event
`0x0100`: Disassembled event| +| 4 | Motion ID | uint8 | 00 | `0`: Does not need to be recorded as a specific motion.
`1~255`: Positioning data reported under the same motion status (same ID refers to the same motion) | +| 5~8 | UTC Timestamp | uint32 | 69686032 | `0x69686032` = 1768448050(DEC) seconds

Convert it to UTC Time:
2026-01-15 03:34:10 | +| 9 | Battery Level | uint8 | 64 | `0x64` = 100% | +| 10 | MAC-RSSI Count (n) | uint8 | 05 | Number of Bluetooth devices detected (n = 5, maximum 5) | +| 11~17 | MAC-RSSI Pair 1 | 7 bytes | c30000564b3b ce | MAC: `C3:00:00:56:4B:3B`,
RSSI: `0xCE` = -50 (int8) | +| 18~24 | MAC-RSSI Pair 2 | 7 bytes | c20303003f00 ce | MAC: `C2:03:03:00:3F:00`,
RSSI: `0xCE` = -50 (int8) | +| 25~31 | MAC-RSSI Pair 3 | 7 bytes | 588c81a0fbf2 cc | MAC: `58:8C:81:A0:FB:F2`,
RSSI: `0xCC` = -52 (int8) | +| 32~38 | MAC-RSSI Pair 4 | 7 bytes | c20303003f03 cb | MAC: `C2:03:03:00:3F:03`,
RSSI: `0xCB` = -53 (int8) | +| 39~45 | MAC-RSSI Pair 5 | 7 bytes | c30000564af2 c7 | MAC: `C3:00:00:56:4A:F2`,
RSSI: `0xC7` = -57 (int8) | + +### Positioning Status Packet with Accelerometer (0x31) + +The positioning status packet contains the positioning status along with accelerometer data, event status, and battery information. The frame ID is `0x31`, and the total length is 15 bytes. + +| 0x31 | Byte2 | Byte3~4 | Byte5~8 | Byte9~10 | Byte11~12 | Byte13~14 | Byte15 | +| :--: | :---: | :-----: | :-----: | :------: | :-------: | :-------: | :---: | +| ID | Positioning Status | Event Status | UTC Timestamp | Accelerometer X | Accelerometer Y | Accelerometer Z | Battery Level | + +**Raw Payload Example** + +`31 00 0100 694b3db0 003a 039d fe84 62` + +| Byte | Value | Type | Raw Data | Description | +| :---: | :--- | :---: | :---: | :--- | +| 1 | Frame ID | uint8 | 31 | 31 is the packet ID | +| 2 | Positioning Status | uint8 | 00 |`0x00`: locate successful.
`0x01`: The GNSS scan timed out.
`0x02`: The Wi-Fi scan timed out.
`0x03`: The Wi-Fi + GNSS scan timed out.
`0x04`: The GNSS + Wi-Fi scan timed out.
`0x05`: The Bluetooth scan timed out.
`0x06`: The Bluetooth + Wi-Fi scan timed out.
`0x07`: The Bluetooth + GNSS scan timed out.
`0x08`: The Bluetooth + Wi-Fi + GNSS scan timed out.
`0x09`: Location Server failed to parse the GNSS location.
`0x0A`: Location Server failed to parse the Wi-Fi location.
`0x0B`: Location Server failed to parse the Bluetooth location.
`0x0C`: Failed to parse location due to the poor accuracy.
`0x0D`: Time synchronization failed.
`0x0E`: Failed due to the old Almanac.
`0x0F`: The GNSS + Bluetooth scan timed out. | +| 3~4 | Event Status | uint16 | 0000 |`0x0000` = No events triggered
Bit 0: false
Bit 1: Start moving event
Bit 2: End movement event
Bit 3: Motionless event
Bit 4: Shock event
Bit 5: Temperature event
Bit 6: Light event
Bit 7: SOS event
Bit 8: Press once event
Bit 9: Disassembled event

Convert to hexadecimal:
`0x0001`: Start moving event
`0x0002`: End movement event
`0x0004`: Motionless event
`0x0008`: Shock event
`0x0010`: Temperature event
`0x0020`: Light event
`0x0040`: SOS event
`0x0080`: Press once event
`0x0100`: Disassembled event | +| 5~8 | UTC Timestamp | uint32 | 694B3DB0 | `0x694B3DB0` = 1766538672(DEC) seconds

Convert it to UTC Time:
2025-12-24 01:11:12 | +| 9~10 | Accelerometer X | int16 | 003a | `0x003A` = 58 mg | +| 11~12 | Accelerometer Y | int16 | 039d | `0x039D` = 925 mg | +| 13~14 | Accelerometer Z | int16 | fe84 | `0xFE84` = -380 mg | +| 15 | Battery Level | uint8 | 62 | `0x62` = 98% | + +### Positioning Status Packet (Accelerometer Off, 0x32) + +The positioning status packet contains the positioning status along with event status and battery information. The frame ID is `0x32`, and the total length is 9 bytes. + +| 0x32 | Byte2 | Byte3~4 | Byte5~8 | Byte9 | +| :--: | :---: | :-----: | :-----: | :---: | +| ID | Positioning Status | Event Status | UTC Timestamp | Battery Level | + +**Raw Payload Example** + +`32 00 0100 694b3db0 62` + +| Byte | Value | Type | Raw Data | Description | +| :---: | :--- | :---: | :---: | :--- | +| 1 | Frame ID | uint8 | 32 | 32 is the packet ID | +| 2 | Positioning Status | uint8 | 00 |`0x00`: locate successful.
`0x01`: The GNSS scan timed out.
`0x02`: The Wi-Fi scan timed out.
`0x03`: The Wi-Fi + GNSS scan timed out.
`0x04`: The GNSS + Wi-Fi scan timed out.
`0x05`: The Bluetooth scan timed out.
`0x06`: The Bluetooth + Wi-Fi scan timed out.
`0x07`: The Bluetooth + GNSS scan timed out.
`0x08`: The Bluetooth + Wi-Fi + GNSS scan timed out.
`0x09`: Location Server failed to parse the GNSS location.
`0x0A`: Location Server failed to parse the Wi-Fi location.
`0x0B`: Location Server failed to parse the Bluetooth location.
`0x0C`: Failed to parse location due to the poor accuracy.
`0x0D`: Time synchronization failed.
`0x0E`: Failed due to the old Almanac.
`0x0F`: The GNSS + Bluetooth scan timed out. | +| 3~4 | Event Status | uint16 | 0100 | `0x0000` = No events triggered
Bit 0: false
Bit 1: Start moving event
Bit 2: End movement event
Bit 3: Motionless event
Bit 4: Shock event
Bit 5: Temperature event
Bit 6: Light event
Bit 7: SOS event
Bit 8: Press once event
Bit 9: Disassembled event

Convert to hexadecimal:
`0x0001`: Start moving event
`0x0002`: End movement event
`0x0004`: Motionless event
`0x0008`: Shock event
`0x0010`: Temperature event
`0x0020`: Light event
`0x0040`: SOS event
`0x0080`: Press once event
`0x0100`: Disassembled event | +| 5~8 | UTC Timestamp | uint32 | 694B3DB0 | `0x694B3DB0` = 1766538672(DEC) seconds

Convert it to UTC Time:
2025-12-24 01:11:12 | +| 9 | Battery Level | uint8 | 62 | `0x62` = 98% | + +## Downlink Packet, FPort=5 + +The tracker supports LoRaWAN to downlink some commands to adjust parameters. If the device is hibernated, the downlink command takes effect the next time the device wakes up to upload data. + +Due to the LoRaWAN Class A, where downlink windows only open following an uplink, commands are not real-time. For instance, if the reporting interval is set to 10 minutes, it may take up to 10 minutes for the device to receive the downlink command during its next transmission window. + +**Note: FPort=5** + +### Request Device Status Packet (0x8F) + +|0x8F| +| - | +|ID| + +Example: + +8F: Request the latest device status and location packet. + +### Setting Work Mode & Positioning Strategy (0x90) + +|0x90|Byte2|Byte3|Byte4~5|Byte6~7|Byte8~9| +| - | :- | :- | :- | :- | :- | +|ID|Work Mode|Positioning Strategy|Heartbeat Interval|Periodic Mode Uplink Interval|Event Mode Uplink Interval| + +|Byte10|Byte11|Byte12|Byte13|Byte14|Byte15~30| +| - | - | - | - | - | :- | +|Enable 3-Axis Accelerometer|Enable Disassemble Alarm|GNSS Scan Timeout(S)|iBeacon Scan Timeout(S)|UUID Filter Valid Byte|UUID Filter| + +Note: +Heartbeat Interval / Periodic Mode Uplink Interval / Event Mode Uplink Interval unit: **minutes** + +Example: + +`90 01 01 02d0 0014 0005 01 01 1e 0a 10 00000000000000000000000000000000` + + +|**Byte**|**Value**|**Type**|**Raw Data**|**Description**| +| - | - | - | - | - | +|1|Frame ID|uint8|90|90 is the packet ID| +|2|Work Mode|uint8|01|01 = Periodic Mode
`00`: Standby Mode
`01`: Periodic Mode
`02`: Event Mode| +|3|Positioning Strategy|uint8|01|`00`: Only GNSS
`01`: Only Wi-Fi
`02`: Wi-Fi + GNSS
`03`: GNSS + Wi-Fi
`04`: Only Bluetooth
`05`: Bluetooth + Wi-Fi
`06`: Bluetooth + GNSS
`07`: Bluetooth + Wi-Fi + GNSS
`08`: GNSS + Bluetooth | +|4~5|Heartbeat Interval | uint16 | 02d0 | `0x02D0` = 720 minutes| +|6~7|Periodic Mode Uplink Interval|uint16|0014|`0x0014` = 20 minutes | +|8~9|Event Mode Uplink Interval|uint16|0005|`0x0005` = 5 minutes
When no event is triggered, data will be uploaded every 5 minutes.
| +|10|Enable 3-Axis Accelerometer|uint8|01|`00`: Disable
`01`: Enable| +|11|Enable Disassembly Alarm|uint8|01|`00`: Disable
`01`: Enable| +|12|GNSS scan timeout|uint8|1E|`0x1E` = 30 seconds | +|13|iBeacon scan timeout|uint8|0A|`0x0A` = 10 seconds | +|14|UUID Filter Valid Bytes|uint8|10| Number of valid bytes in UUID filter (0–16)| +|15~30|UUID Filter| 16 bytes | 0000000000000000
0000000000000000 | 16‑byte Bluetooth UUID filter. Only first N bytes (defined by byte30) are meaningful| + + +### Setting Event Mode Threshold (0x91) + +|0x91|Byte2|Byte3~4|Byte5~6|Byte7|Byte8~9| +| - | :- | :- | :- | :- | :- | +|ID|Enable Motion Event|3-Axis Motion Threshold|Uplink Interval On Motion|Enable Motionless Event|Motionless Timeout| + +|Byte10|Byte11~12| +| - | :- | +|Enable Shock Event|3-Axis Shock Threshold| + + +Example: + +`91 01 001e 0005 01 01 2c` + +|**Byte**|**Value**|**Type**|**Raw Data**|**Description**| +| - | - | - | - | - | +|1|Frame ID|uint8|91|91 is the packet ID| +|2|Enable Motion Event|uint8|01|`00`: Disable
`01`: Enable| +|3~4|3-Axis Motion Threshold|uint16|001e|`0x001E` = 30 mg
When the acceleration exceeds 30 mg, the device determines it is in motion
| +|5~6|Uplink Interval On Motion|uint16|0005|`0x0005` = 5 minutes
When motion is detected, the reporting interval is 5 minutes
| +|7|Enable Motionless Event|uint8|01|`00`: Disable
`01`: Enable| +|8~9|Motionless Timeout|uint16|012c|`0x012C` = 300 minutes
If the device remains stationary for more than 300 minutes, a motionless event will be triggered
| +|10|Enable Shock Event|uint8|01|`00`: Disable
`01`: Enable| +|11~12|3-Axis Shock Threshold|uint16|012c|`0x012C` = 300 mg
When the acceleration exceeds 300 mg, the shock event will be triggered
| + +### Request Device Status Packet (0x92) + +|0x92| +| - | +|ID| + +Example: + +92: Force a GNSS location fix. + +### Setting Work Mode & Positioning Strategy & Event Mode Threshold (0x97) + +|0x97|Byte2|Byte3|Byte4~5|Byte6~7|Byte8~9| +| - | :- | :- | :- | :- | :- | +|ID|Working Mode|Positioning Strategy|Heartbeat Interval|Periodic Mode Uplink Interval|Event Mode Uplink Interval| + +|Byte10|Byte11|Byte12|Byte13|Byte14|Byte15~30| +| - | :- | :- | :- | :- | :- | +|Enable 3-Axis Accelerometer|Enable Disassembly Alarm|GNSS Scan Timeout |iBeacon Scan Timeout |UUID Filter Valid Bytes|UUID Filter| + +#### Motion Event Settings +|Byte31|Byte32~33|Byte34~35| +| - | :- | :- | +|Enable Motion Event|3-Axis Motion Threshold|Uplink Interval On Motion| + +#### Motionless Event Settings +|Byte36|Byte37~38| +| - | :- | +|Enable Motionless Event|Motionless Timeout| + + +#### Shock Event Settings +|Byte39|Byte40~41| +| - | :- | +|Enable Shock Event|3-Axis Shock Threshold| + +Example: + +`97 01 02 003c 001e 000a 01 01 0a 05 10 00000000000000000000000000000000 01 001e 0005 01 012c 01 012c` + +|**Byte**|**Value**|**Type**|**Raw Data**|**Description**| +| - | - | - | - | - | +|1|Frame ID|uint8|97|97 is the packet ID| +|2|Work Mode|uint8|01|`00`: Standby Mode
`01`: Periodic Mode
`02`: Event Mode| +|3|Positioning Strategy|uint8|02|`00`: Only GNSS
`01`: Only Wi-Fi
`02`: Wi-Fi + GNSS
`03`: GNSS + Wi-Fi
`04`: Only Bluetooth
`05`: Bluetooth + Wi-Fi
`06`: Bluetooth + GNSS
`07`: Bluetooth + Wi-Fi + GNSS
`08`: GNSS + Bluetooth | +|4~5|Heartbeat Interval|uint16|003c|`0x003C` = 60 seconds| +|6~7|Periodic Mode Uplink Interval|uint16|001e|`0x001E` = 30 seconds| +|8~9|Event Mode Uplink Interval|uint16|000a|`0x000A` = 10 minutes
When no event is triggered, data will be uploaded every 10 minutes.
| +|10|Enable 3-Axis Accelerometer|uint8|01|`00`: Disable
`01`: Enable| +|11|Enable Disassembly Alarm|uint8|01|`00`: Disable
`01`: Enable| +|12|GNSS Scan Timeout|uint8|0a|`0x0A` = 10 seconds| +|13|iBeacon Scan Timeout|uint8|05|`0x05` = 5 seconds| +|14|UUID Filter Valid Bytes|uint8|10|`0x10` = 16 bytes| +|15~30|UUID Filter|byte[16]|0000000000000000
0000000000000000|UUID filter value (16 bytes)
| +|31|Enable Motion Event|uint8|01|`00`: Disable
`01`: Enable| +|32~33|3-Axis Motion Threshold|uint16|001e|`0x001E` = 30 mg | +|34~35|Uplink Interval On Motion|uint16|0005|`0x0005` = 5 minutes
When motion is detected, the reporting interval is 5 minutes| +|36|Enable Motionless Event|uint8|01|`00`: Disable
`01`: Enable| +|37~38|Motionless Timeout|uint16|012c|`0x012C` = 300 minutes | +|39|Enable Shock Event|uint8|01|`00`: Disable
`01`: Enable| +|40~41|3-Axis Shock Threshold|uint16|012c|`0x012C` = 300 mg | + +## Tech Support & Product Discussion + +Thank you for choosing our products! We are here to provide you with different support to ensure that your experience with our products is as smooth as possible. We offer several communication channels to cater to different preferences and needs. + +
+ + +
+ +
+ + +
\ No newline at end of file diff --git a/sites/en/sidebars.js b/sites/en/sidebars.js index 1e1036ba44c3b..cef0b4a71cc9b 100644 --- a/sites/en/sidebars.js +++ b/sites/en/sidebars.js @@ -720,6 +720,8 @@ const sidebars = { label: 'User Guide', items: [ 'Sensor/SenseCAP/SenseCAP_T2000_Tracker/User_Guide/Quick_Start', + 'Sensor/SenseCAP/SenseCAP_T2000_Tracker/User_Guide/Payload_Format', + 'Sensor/SenseCAP/SenseCAP_T2000_Tracker/User_Guide/FAQ', ], }, {