main.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. -- LuaTools需要PROJECT和VERSION这两个信息
  2. PROJECT = "JT808"
  3. VERSION = "2.0.0"
  4. --[[
  5. 本demo演示使用string.pack与unpack函数,实现JT808 终端注册协议数据生成与解析
  6. ]]
  7. --加载sys库
  8. sys = require("sys")
  9. local netLed = require("netLed")
  10. --GPIO27配置为输出,用作网络指示灯
  11. local LEDA= gpio.setup(27, 0, gpio.PULLUP)
  12. -- 自定义位运算函数
  13. local bit = {}
  14. --终端消息ID
  15. T_COMMON_RSP = 0x0001
  16. T_REGISTER = 0x0100
  17. function bit.bxor(a, b)
  18. return a ~ b -- 按位异或
  19. end
  20. --十六进制转二进制
  21. local function hexToBinary(hexStr)
  22. local binaryData = {}
  23. for i = 1, #hexStr, 2 do
  24. local byte = tonumber(hexStr:sub(i, i+1), 16) -- 每两个字符转换为一个字节
  25. table.insert(binaryData, string.char(byte))
  26. end
  27. return table.concat(binaryData) -- 拼接成二进制字符串
  28. end
  29. --字符串转字节数组
  30. local function stringToBytes(hexStr)
  31. local bytes = {}
  32. for i = 1, #hexStr, 2 do
  33. local byteStr = hexStr:sub(i, i+1)
  34. local byte = tonumber(byteStr, 16)
  35. table.insert(bytes, byte)
  36. end
  37. return bytes
  38. end
  39. --BCD编码
  40. local function encodeBcdNum(d, n)
  41. if d:len() < n then
  42. return (string.rep('0', n - d:len()) .. d):fromHex()
  43. else
  44. return (d:sub(1, n)):fromHex()
  45. end
  46. end
  47. --计算异或校验(十六进制字符串)
  48. local function calculateXor(data)
  49. local sum = 0
  50. for i = 1, #data, 2 do
  51. local byte = tonumber(data:sub(i, i+1), 16)
  52. -- 检查是否转换成功
  53. if not byte then
  54. error(string.format("Invalid hex character: '%s' at position %d", byteStr, i))
  55. end
  56. -- 打印当前字节的值(十六进制和十进制)
  57. --print(string.format("当前字节: %s (十进制: %d)", byteStr, byte))
  58. if not byte then
  59. error("Invalid hex character in input string!")
  60. end
  61. sum = bit.bxor(sum, byte)
  62. -- 打印异或后的中间结果
  63. --print(string.format("异或后 sum = %d (十六进制: 0x%X)", sum, sum))
  64. end
  65. return sum
  66. end
  67. --
  68. local function calCrc(hexStr)
  69. -- 解析hexStr转换为十六进制字符数组
  70. local byteArray = HexOutput(hexStr)
  71. -- 计算 CRC
  72. local crc = calculateXor(byteArray)
  73. log.info("CRCxor:",crc)
  74. -- 返回 CRC 值
  75. return crc
  76. end
  77. --将时间转换为BCD格式
  78. function timeToBCD()
  79. local t = os.date("*t")
  80. -- 转换为BCD格式
  81. local year = string.format("%02d", t.year % 100)
  82. local month = string.format("%02d", t.month)
  83. local day = string.format("%02d", t.day)
  84. local hour = string.format("%02d", t.hour)
  85. local min = string.format("%02d", t.min)
  86. local sec = string.format("%02d", t.sec)
  87. -- 组合BCD格式字符串
  88. local bcdTime = year .. month .. day .. hour .. min .. sec
  89. return bcdTime
  90. end
  91. --构建通用应答
  92. function cmnRsp(svrSeq, svrId, result)
  93. return string.pack('>HHb', svrSeq, svrId, result)
  94. -- >表示使用大端字节序;
  95. -- HH 表示两个 16 位无符号整数(svrSeq 和 svrId);
  96. -- b 表示一个 8 位有符号整数(result)
  97. end
  98. --构建设备注册
  99. function register(ProvinceId,CityId,ManufactureId,TerminalModule,TerminalId,CarColor,CarId)
  100. local termMd = TerminalModule..string.rep(string.char(0), 16 - (TerminalModule):len())..string.rep(string.char(0), 4)
  101. --log.info("HexDataByteLength:",#termMd)
  102. --log.info("HexData is: ",termMd)
  103. return string.pack('>HHc5c20c7Bc11',ProvinceId,CityId,ManufactureId,termMd,TerminalId:fromHex(),CarColor,CarId)
  104. end
  105. --构建位置信息汇报
  106. function locRpt(alarm, status, lat, lng, alt, spd, course, tm, extInfo)
  107. -- 使用 '>iiiiHHH' 格式打包数据
  108. return string.pack('>iiiiHHH', alarm, status, lat, lng, alt, spd, course)..tm:fromHex().. extInfo
  109. end
  110. function HexOutput(data)
  111. log.info("HexDataByteLength:",#data)
  112. local hex = ""
  113. for i = 1, #data do
  114. hex = hex.. string.format("%02x", string.byte(data, i))
  115. end
  116. log.info("HexData is: ",hex)
  117. return hex
  118. end
  119. --封装一个完整的JT808终端注册数据帧
  120. function encodeReg(phoneNo,tSendSeq)
  121. --消息体
  122. local msgBody = register(12,123,'10001','GT808','00000000000002',0,'41048063212')
  123. --消息头
  124. local msgHead =string.pack('>HH',T_REGISTER,msgBody:len())..encodeBcdNum(phoneNo,12)..tSendSeq
  125. local curSeq = tSendSeq
  126. tSendSeq = (tSendSeq == 0xFFFF) and 0 or (tSendSeq + 1)
  127. --校验码
  128. HexOutput(msgHead .. msgBody)
  129. local frame = msgHead .. msgBody
  130. log.info("encodeReg Frame length is: ",#frame)
  131. local crc = calCrc(frame)
  132. --转义
  133. local s = msgHead .. msgBody .. string.char(crc)
  134. s = s:gsub('\125', '\125\1') -- 7D -> 7D 01
  135. s = s:gsub('\126', '\125\2') -- 7E -> 7D 02
  136. return string.char(0x7E) .. s .. string.char(0x7E), curSeq
  137. end
  138. function decodeCmnRsp(s)
  139. log.info("hereNow.",s)
  140. --local start, tail, msg = s:find('^(7E[^7E]+7E)$')
  141. --local decPacket, msgAttr = {}
  142. -- 检查是否以 7E 开头和结尾
  143. if not s:match("^7E") or not s:match("7E$") then
  144. log.warn("prot.decode", "packet does not start or end with 7E")
  145. return nil
  146. end
  147. -- 匹配以 7E 开头和结尾的数据包
  148. local start, tail, msg = s:find('^(7E.+7E)$') -- 改为允许中间部分包含 7E
  149. log.info("start:", start)
  150. log.info("tail:", tail)
  151. log.info("msg:", msg)
  152. if not msg then
  153. -- 如果未匹配到
  154. log.warn('prot.decode', 'wait packet complete')
  155. return nil
  156. end
  157. --反转义
  158. msg = msg:gsub('\125\2', '\126') -- 7D 02 -> 7E
  159. msg = msg:gsub('\125\1', '\125') -- 7D 01 -> 7D
  160. log.info("msg:", msg,msg:len())
  161. if msg:len() < 13 then
  162. log.error('prot.decode', 'packet len is too short')
  163. return s:sub(tail + 1, -1), nil
  164. end
  165. local hexArray = stringToBytes(msg)
  166. local dataHexStr = msg:sub(3, -5)
  167. log.info("消息体和其字节长度:", dataHexStr,dataHexStr:len())
  168. local crcVal = calculateXor(dataHexStr)
  169. print("XOR校验值:", crcVal)
  170. if crcVal ~= hexArray[#hexArray-1] then
  171. log.error('prot.decode', 'crc value error!')
  172. return s:sub(tail + 1, -1), nil
  173. else
  174. log.info("XOR:校验通过")
  175. end
  176. ----------------------------解析消息id
  177. msgdata = msg:sub(3, 6)
  178. -- 转换为二进制数据
  179. local binaryData = hexToBinary(msgdata)
  180. -- 解析二进制数据
  181. msgId = string.unpack(">H", binaryData)
  182. -- 输出结果
  183. log.info("消息id十六进制:", string.format("0x%04X", msgId))
  184. ----------------------------解析应答结果
  185. msgdata = msg:sub(7, 8)
  186. -- 转换为二进制数据
  187. local binaryData = hexToBinary(msgdata)
  188. -- 解析二进制数据
  189. result = string.unpack(">B", binaryData)
  190. -- 输出结果
  191. log.info("结果十六进制:", string.format("0x%02X", result))
  192. ----------------------------解析鉴权码
  193. msgdata = msg:sub(9, 10)
  194. -- 转换为二进制数据
  195. local binaryData = hexToBinary(msgdata)
  196. -- 解析二进制数据
  197. result = string.unpack(">B", binaryData)
  198. -- 输出结果
  199. log.info("鉴权码十六进制:", string.format("0x%02X", result))
  200. ----------------------------解析电话号码
  201. msgdata = msg:sub(11, 22)
  202. -- 转换为二进制数据
  203. local binaryData = hexToBinary(msgdata)
  204. -- 解析二进制数据
  205. local b1, b2, b3, b4, b5, b6 = string.unpack(">BBBBBB", binaryData)
  206. -- 格式化输出为 6 字节的十六进制字符串
  207. local TerminalPhoneNo = string.format("0x%02X%02X%02X%02X%02X%02X", b1, b2, b3, b4, b5, b6)
  208. -- 输出电话号码
  209. log.info("电话号码十六进制:", TerminalPhoneNo)
  210. ----------------------------解析标头流水号
  211. msgdata = msg:sub(23, 26)
  212. -- 转换为二进制数据
  213. local binaryData = hexToBinary(msgdata)
  214. -- 解析二进制数据
  215. local ManualMsgNum = string.unpack(">H", binaryData)
  216. -- 输出结果
  217. log.info("标头流水号十六进制:", string.format("0x%04X", ManualMsgNum))
  218. ----------------------------解析标尾流水号
  219. msgdata = msg:sub(23, 26)
  220. -- 转换为二进制数据
  221. local binaryData = hexToBinary(msgdata)
  222. -- 解析二进制数据
  223. local MsgNum = string.unpack(">H", binaryData)
  224. -- 输出结果
  225. log.info("标尾流水号十六进制:", string.format("0x%04X", MsgNum))
  226. ----------------------------解析鉴权码
  227. msgdata = msg:sub(33, 50)
  228. -- 转换为二进制数据
  229. local binaryData = hexToBinary(msgdata)
  230. local b1, b2, b3, b4, b5, b6, b7, b8, b9 = string.unpack(">BBBBBBBBB", binaryData)
  231. -- 格式化输出为 6 字节的十六进制字符串
  232. local JianquanCode = string.format("0x%02X%02X%02X%02X%02X%02X%02X%02X%02X", b1, b2, b3, b4, b5, b6, b7, b8, b9)
  233. -- 输出结果
  234. log.info("鉴权码十六进制:", JianquanCode)
  235. end
  236. --新建任务,每休眠2000ms继续一次
  237. sys.taskInit(function()
  238. --实验1:构建立通用应答数据帧,协议可参考JT808协议8.1与8.2
  239. --输入参数:
  240. --应答流水号为123,
  241. --应答 ID为456,
  242. --结果为1,
  243. local data = cmnRsp(123, 456, 1)
  244. HexOutput(data) --HexDataByteLength:5,HexData is:007b01c801 007b为123,01c8为456,1为1
  245. --实验2:构建终端注册数据帧,协议可参考JT808协议8.5
  246. --输入参数:
  247. --12, --省域 ID WORD
  248. --123, --市县域 ID WORD
  249. --'10001', --制造商 ID BYTE[5]
  250. --'GT808' --终端型号 BYTE[20]
  251. --'00000000000002', --终端 ID BYTE[7]
  252. --0, --车牌颜色 BYTE
  253. --'41048063212') --车辆标识 STRING VIN或车牌号均为固定长度,这里假设字符串长度为11
  254. local data = register(12,123,'10001','GT808','00000000000002',0,'41048063212')
  255. HexOutput(data)--[HexDataByteLength:] 48,[HexData is: ]
  256. --输出解析:
  257. --000c,对应12, --省域 ID WORD
  258. --007b,对应123, --市县域 ID WORD
  259. --3130303031,对应'10001', --制造商 ID BYTE[5]
  260. --4754383038,对应'GT808' --终端型号 BYTE[20],不足字节补0
  261. --0000000000
  262. --0000000000
  263. --0000000000
  264. --00000000000002 对应'00000000000002', --终端 ID BYTE[7]
  265. --000, --车牌颜色 BYTE
  266. --3431303438303633323132,对应'41048063212') --车辆标识 STRING
  267. --实验3:构建位置信息汇报数据帧,协议可参考JT808协议8.18
  268. --输入参数:
  269. --0, 报警标志 DWORD
  270. --1, 状态位 DWORD
  271. --12345678, 纬度 DWORD
  272. --87654321, 经度 DWORD
  273. --100, 高程 WORD
  274. --60, 速度 WORD
  275. --180, 方向 WORD
  276. --os.time(), 时间 BCD[6] YY-MM-DD-hh-mm-ss(GMT+8 时间,本标准中之后涉及的时间均采用此时区)
  277. --"extra"
  278. local time = timeToBCD();
  279. log.info("CurrentTime: ",time)
  280. local data = locRpt(0, 1, 12345678, 87654321, 100, 60, 180, time, "extra")
  281. HexOutput(data)--[HexDataByteLength:] 33,[HexData is: ] 000000000000000100bc614e05397fb10064003c00b42411241232046578747261
  282. --输出解析:
  283. --00000000,对应 0, 报警标志 DWORD,4字节
  284. --00000001,对应 1, 状态位 DWORD
  285. --00bc614e,对应 12345678, 纬度 DWORD
  286. --05397fb1, 对应 87654321, 经度 DWORD
  287. --0064,对应 100, 高程 WORD
  288. --003c,对应60, 速度 WORD
  289. --00b4,对应180, 方向 WORD
  290. --241124123204,对应时间 BCD[6] 24-11-24-12-32-04
  291. --6578747261,对应"extra"
  292. --实验4:封闭一个完整的JT808注册数据帧
  293. --输入参数:
  294. --'018068821447',手机号不足 12位,则在前补充数字,大陆手机号补充数字 0
  295. --'101',消息流水号
  296. local data = encodeReg('018068821447','101')
  297. HexOutput(data) --[HexDataByteLength:] 64
  298. --[HexData is: ] 7e01000030018068821447313031000c007b3130303031475438303800000000000000000000000000000000000000000002003431303438303633323132627e
  299. --输出解析:
  300. --7e,标识位固定为0x7e
  301. --0100,消息 ID
  302. --0030,消息体属性
  303. --018068821447,手机号
  304. --313031,消息流水号
  305. --000c007b3130303031475438303800000000000000000000000000000000000000000002003431303438303633323132,注册消息体
  306. --62,校验
  307. --7e,标识位固定为0x7e
  308. --实验5:解析JT808终端注册应答数据帧
  309. local hexStr = '7E8100000C0404571031660030003000736875616E67776569E07E'
  310. decodeCmnRsp(hexStr)
  311. end)
  312. -- 这里演示4G模块上网后,会自动点亮网络灯,方便用户判断模块是否正常开机
  313. sys.taskInit(function()
  314. while true do
  315. sys.wait(6000)
  316. if mobile.status() == 1 then
  317. gpio.set(27, 1)
  318. else
  319. gpio.set(27, 0)
  320. mobile.reset()
  321. end
  322. end
  323. end)
  324. -- 用户代码已结束---------------------------------------------
  325. -- 运行lua task,只能调用一次,而且必须写在末尾
  326. -- 结尾总是这一句
  327. sys.run()
  328. -- sys.run()之后后面不要加任何语句!!!!!