--- 模块功能:串口1功能测试 -- @author openLuat -- @module uart.testUart -- @license MIT -- @copyright openLuat -- @release 2018.03.27 module(...,package.seeall) require"utils" require"pm" local EnableRMC = "$PCAS03,0,0,0,0,1,0,0,0,0,0,,,0,0*03\r\n" --[[ 功能定义: uart按照帧结构接收外围设备的输入,收到正确的指令后,回复ASCII字符串 帧结构如下: 帧头:1字节,0x01表示扫描指令,0x02表示控制GPIO命令,0x03表示控制端口命令 帧体:字节不固定,跟帧头有关 帧尾:1字节,固定为0xC0 收到的指令帧头为0x01时,回复"CMD_SCANNER\r\n"给外围设备;例如接收到0x01 0xC0两个字节,就回复"CMD_SCANNER\r\n" 收到的指令帧头为0x02时,回复"CMD_GPIO\r\n"给外围设备;例如接收到0x02 0xC0两个字节,就回复"CMD_GPIO\r\n" 收到的指令帧头为0x03时,回复"CMD_PORT\r\n"给外围设备;例如接收到0x03 0xC0两个字节,就回复"CMD_PORT\r\n" 收到的指令帧头为其余数据时,回复"CMD_ERROR\r\n"给外围设备;例如接收到0x04 0xC0两个字节,就回复"CMD_ERROR\r\n" ]] --串口ID,1对应uart1 --如果要修改为uart2,把UART_ID赋值为2即可 local UART_ID = 1 --帧头类型以及帧尾 local CMD_SCANNER,CMD_GPIO,CMD_PORT,FRM_TAIL = 1,2,3,string.char(0xC0) --串口读到的数据缓冲区 local rdbuf = "" --[[ 函数名:parse 功能 :按照帧结构解析处理一条完整的帧数据 参数 : data:所有未处理的数据 返回值:第一个返回值是一条完整帧报文的处理结果,第二个返回值是未处理的数据 ]] local function parse(data) if not data then return end local tail = string.find(data,string.char(0xC0)) if not tail then return false,data end local cmdtyp = string.byte(data,1) local body,result = string.sub(data,2,tail-1) log.info("testUart.parse",data:toHex(),cmdtyp,body:toHex()) if cmdtyp == CMD_SCANNER then write("CMD_SCANNER") elseif cmdtyp == CMD_GPIO then write("CMD_GPIO") elseif cmdtyp == CMD_PORT then write("CMD_PORT") else write("CMD_ERROR") end return true,string.sub(data,tail+1,-1) end --[[ 函数名:proc 功能 :处理从串口读到的数据 参数 : data:当前一次从串口读到的数据 返回值:无 ]] local function proc(data) if not data or string.len(data) == 0 then return end --追加到缓冲区 rdbuf = rdbuf..data local result,unproc unproc = rdbuf --根据帧结构循环解析未处理过的数据 while true do result,unproc = parse(unproc) if not unproc or unproc == "" or not result then break end end rdbuf = unproc or "" end -- [2025-08-08 19:39:38.602] [I]-[testUart.read bin] $GNRMC,113938.000,A,3221.43244,N,11922.55915,E,0.29,0.00,080825,,,A,V*04 -- [2025-08-08 19:39:39.610] [I]-[testUart.read bin] $GNRMC,113939.000,A,3221.43246,N,11922.55922,E,0.33,0.00,080825,,,A,V*08 -- [2025-08-08 19:39:40.603] [I]-[testUart.read bin] $GNRMC,113940.000,A,3221.43240,N,11922.55931,E,0.54,0.00,080825,,,A,V*03 -- [2025-08-08 19:39:41.583] [I]-[testUart.read bin] $GNRMC,113941.000,A,3221.43247,N,11922.55928,E,0.00,0.00,080825,,,A,V*0C -- 定义RMC数据结构(使用Lua表模拟) local rmc_data = { time = "", -- 时间 state = "", -- 定位状态 lat = "", -- 纬度 NS = "", -- 南北纬 lng = "", -- 经度 WE = "", -- 东西经 date = "" -- 日期 } -- 全局变量,模拟接收缓冲区和经纬度存储 local rx_data = "" local air530_lat = "" local air530_lng = "" -- RMC数据解析函数 function AnalysisRMC(pRecRMC) print("AnalysisRMC") -- 初始化接收的数据结构 pRecRMC.time = "" pRecRMC.state = "" pRecRMC.lat = "" pRecRMC.NS = "" pRecRMC.lng = "" pRecRMC.WE = "" pRecRMC.date = "" -- 将字符串按逗号分割成字段表 local fields = {} for field in string.gmatch(rx_data, "([^,]+)") do table.insert(fields, field) end -- 检查字段数量是否足够 if #fields < 10 then print("RMC数据格式不正确,字段数量不足") rx_data = "" -- 清空接收缓存 return end -- 解析时间 (第2个字段,索引从1开始) pRecRMC.time = fields[2] or "" print("时间: " .. pRecRMC.time) -- 解析定位状态 (第3个字段) pRecRMC.state = fields[3] or "" print("定位状态: " .. pRecRMC.state) -- 检查定位是否有效 if pRecRMC.state ~= "A" then print("无效定位信息!") rx_data = "" -- 清空接收缓存 return end -- 解析纬度 (第4个字段) pRecRMC.lat = fields[4] or "" print("纬度: " .. pRecRMC.lat) air530_lat = pRecRMC.lat -- 解析南北纬 (第5个字段) pRecRMC.NS = fields[5] or "" print("南北纬: " .. pRecRMC.NS) -- 解析经度 (第6个字段) pRecRMC.lng = fields[6] or "" print("经度: " .. pRecRMC.lng) air530_lng = pRecRMC.lng -- 解析东西经 (第7个字段) pRecRMC.WE = fields[7] or "" print("东西经: " .. pRecRMC.WE) -- 解析日期 (第10个字段) pRecRMC.date = fields[10] or "" print("日期: " .. pRecRMC.date) -- 清空接收缓存 rx_data = "" print("接收缓存区已清空") end -- 示例用法 -- rx_data = "$GNRMC,113941.000,A,3221.43247,N,11922.55928,E,0.00,0.00,080825,,,A,V*0C" -- AnalysisRMC(rmc_data) --[[ 函数名:read 功能 :读取串口接收到的数据 参数 :无 返回值:无 ]] local function read() local data = "" --底层core中,串口收到数据时: --如果接收缓冲区为空,则会以中断方式通知Lua脚本收到了新数据; --如果接收缓冲器不为空,则不会通知Lua脚本 --所以Lua脚本中收到中断读串口数据时,每次都要把接收缓冲区中的数据全部读出,这样才能保证底层core中的新数据中断上来,此read函数中的while语句中就保证了这一点 while true do data = uart.read(UART_ID,"*l") if not data or string.len(data) == 0 then break end --打开下面的打印会耗时 log.info("testUart.read bin",data) -- log.info("testUart.read hex",data:toHex()) proc(data) end end --[[ 函数名:write 功能 :通过串口发送数据 参数 : s:要发送的数据 返回值:无 ]] function write(s) log.info("testUart.write",s) uart.write(UART_ID,s.."\r\n") end local function writeOk() log.info("testUart.writeOk") end --保持系统处于唤醒状态,此处只是为了测试需要,所以此模块没有地方调用pm.sleep("testUart")休眠,不会进入低功耗休眠状态 --在开发“要求功耗低”的项目时,一定要想办法保证pm.wake("testUart")后,在不需要串口时调用pm.sleep("testUart") pm.wake("testUart") --注册串口的数据接收函数,串口收到数据后,会以中断方式,调用read接口读取数据 uart.on(UART_ID,"receive",read) --注册串口的数据发送通知函数 uart.on(UART_ID,"sent",writeOk) --配置并且打开串口 uart.setup(UART_ID,9600,8,uart.PAR_NONE,uart.STOP_1) --如果需要打开“串口发送数据完成后,通过异步消息通知”的功能,则使用下面的这行setup,注释掉上面的一行setup --uart.setup(UART_ID,115200,8,uart.PAR_NONE,uart.STOP_1,nil,1) sys.timerStart(function () log.info("像GPS发生命令") write(EnableRMC) end, 5000)