--- 模块功能:串口1功能测试 -- @author openLuat -- @module uart.uartTask -- @license MIT -- @copyright openLuat -- @release 2018.03.27 module(...,package.seeall) require "utils" require "pm" require "sys" require "log" require "funlib" require "nvm" --[[ 功能定义: 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即可 -- air720UH 可用串口1和2,rfid串口用1 -- air724UH 可用串口1、2、3,rfid串口用3 local UART_ID = nvm.get("rfidUartId") local UART_ID_BAUD_RATE = nvm.get("rfidUartBaudRate") local rfidOfflineTimeout = nvm.get("rfidOfflineTimeout") local rfidMinDiffTime = nvm.get("rfidMinDiffTime") or 3 --local UART_ID = 3 --帧头 local FRM_HEAD = 0xaa2801 --rfid 去重缓存的超时时间 local rfid_buffer_timeout = 20 local rfid_buffer_max_count = 100 --串口读到的数据缓冲区 local rdbuf = "" --rfid串口初始化定参数时器 local rfid_uart_config_init_timer_id = nil local rfidType = nvm.get("rfidType") local timerSyncResult = false --是否厂测读rfid标签模式 local isFactoryTestReadRfidMode = false --厂测模式读rifd号 local factoryTestReadRfid = "" --是否已开始心跳 local isStartHeartbeat = false local isCmdReadRfidMode = false local cmdReadRfid = "" --重置时间差值 local resetTime = 0 require "misc" local function getTime() local tm = misc.getClock() return string.format("%04d/%02d/%02d,%02d:%02d:%02d", tm.year, tm.month, tm.day, tm.hour, tm.min, tm.sec) end --初始化rfid缓冲池 local start_time = os.time() local rfids_buf = {["start_time"]=start_time,["rfid_count"]=0,["buf"]={}} --rfid缓冲池最大存储标签个数 local rfidsBufMaxCount = 3000 --[[ 函数名:parse_dahua 功能 :按照帧结构解析处理一条完整的帧数据 参数 : data:所有未处理的数据 返回值:第一个返回值是一条完整帧报文的处理结果,第二个返回值是未处理的数据 ]] local function parse_dahua(data) local rfidRssiFilterVal = nvm.get("rfidRssiFilterVal") if not data then return end if data:len() < 14 then --log.info("uartTaskDahua.parse_dahua","data length < 42", data:toHex()) return false, data end local res,frm_head_pos = string.find( data, string.char(0xaa)..string.char(0x55)..string.char(0xc) ) if not res then log.error("uartTaskDahua.parse_dahua","FRM_HEAD not found,drop it! data:", data:toHex()) return false end -- 截取包含侦头0xaa55开始的数据包 local sub_data = data:sub(frm_head_pos-2,-1) local frm_len = string.byte(sub_data,3) --检测到不够一个完整包,返回不完整的部分 if res and (#sub_data < frm_len+3) then log.warn("uartTaskDahua.parse_dahua","only find sub frm_data,return and wait... data:",data:toHex(), "sub_data:",sub_data:toHex(),"frm_len:",frm_len) return false, sub_data end --检测是否有结束符0x0d0a,没有结束符则丢弃 local res1,tail_pos = string.find( sub_data, string.char(0x0d)..string.char(0x0a) ) if not res1 then log.error("uartTaskDahua.parse_dahua","FRM_TAIL not found,drop it! data:", data:toHex(), "sub_data:",sub_data:toHex()) return false end --检测包含侦头和侦尾的数据包长度是否合法,不合法则丢弃 if (tail_pos > frm_len+3) or (tail_pos < frm_len+3) then log.warn("uartTaskDahua.parse_dahua","only find sub frm_data,return and wait... data:",data:toHex(), "sub_data:",sub_data:toHex(),"frm_len:",frm_len,"tail_pos:",tail_pos) return false, sub_data:sub(tail_pos+1,-1) end local frm_data = sub_data:sub(1,tail_pos) --解析rfid local rfid_info = {} rfid_info.crc = string.byte(frm_data,4) --rfid_info.sn = string.byte(frm_data,5) rfid_info.id = frm_data:sub(6,9):toHex() rfid_info.type = string.byte(frm_data,10) rfid_info.status = string.byte(frm_data,11) rfid_info.version = string.byte(frm_data,12) rfid_info.rssi = string.byte(frm_data,13) --log.info("uartTaskDahua.parse_dahua", "frm_data:", frm_data:toHex()) if rfidRssiFilterVal > 0 and rfid_info.rssi > rfidRssiFilterVal then log.info("uartTaskDahua.parse_dahua", "rfid_info.rssi > ".. rfidRssiFilterVal..", drop it! rfid_info.rssi =", rfid_info.rssi) --大于一个完整包,返回剩余部分 if #sub_data > tail_pos then return false, sub_data:sub(tail_pos+1,-1) end return false end local cur_time = os.time() if resetTime >= 1 or resetTime <= -1 then cur_time = cur_time + resetTime --log.info("uartTaskDahua.parse_dahua","resetTime:",resetTime,",reset to time:",cur_time) end --进入厂测读rfid标签信号模式 if isFactoryTestReadRfidMode then --读到rfid标签信号后,退出厂测模式 if factoryTestReadRfid == rfid_info.id then sys.publish("factory_test_read_rfid_success",{rssi=rfid_info.rssi,rfid=rfid_info.id}) isFactoryTestMode = false factoryTestReadRfid = "" end --大于一个完整包,返回剩余部分 if #sub_data > tail_pos then return true, sub_data:sub(tail_pos+1,-1) end return true end --进入指令读rfid标签信号模式 if isCmdReadRfidMode then --读到rfid标签信号后,退出读标签模式 if cmdReadRfid == rfid_info.id then log.info("uartTaskDahua.parse_dahua", "rfid_info:", json.encode(rfid_info)) sys.publish("cmd_read_rfid_success",{rssi=rfid_info.rssi,rfid=rfid_info.id}) isCmdReadRfidMode = false cmdReadRfid = "" end --大于一个完整包,返回剩余部分 if #sub_data > tail_pos then return true, sub_data:sub(tail_pos+1,-1) end return true end --新增数据 if not rfids_buf["buf"][rfid_info.id] then --log.info(">>>>>>> push rfid to buf,rfid:", rfid_info.id) if rfids_buf.rfid_count > rfidsBufMaxCount then log.warn("uartTaskDahua.parse_dahua", "rfids_buf.rfid_count > ", rfidsBufMaxCount, ", drop it", rfid_info.id) --大于一个完整包,返回剩余部分 if #sub_data > tail_pos then return true, sub_data:sub(tail_pos+1,-1) end return true end rfids_buf.rfid_count = rfids_buf.rfid_count + 1 --location:0x40 rfid进入 rfids_buf["buf"][rfid_info.id] = {["id"]=rfid_info.id,["first_time"]=cur_time,["time"]=cur_time,["recv_count"]=1,["rssi"]=rfid_info.rssi,["type"]=rfid_info.type,["status"]=rfid_info.status,["sn"]=0,["location"]=0x40} --发布rfid首个信号 sys.publish("pub_packet_data_dahua_rfid_info", rfids_buf["buf"][rfid_info.id]) --大于一个完整包,返回剩余部分 if #sub_data > tail_pos then return true, sub_data:sub(tail_pos+1,-1) end return true end local first_time = rfids_buf["buf"][rfid_info.id]["first_time"] --更新数据 local recv_count = rfids_buf["buf"][rfid_info.id]["recv_count"] + 1 local rfid_sn = rfids_buf["buf"][rfid_info.id]["sn"] rfid_sn = (rfid_sn+1 < 0xff) and (rfid_sn+1) or 0 rfids_buf["buf"][rfid_info.id] = {["id"]=rfid_info.id,["first_time"]=first_time,["time"]=cur_time,["recv_count"]=recv_count,["rssi"]=rfid_info.rssi,["type"]=rfid_info.type,["status"]=rfid_info.status,["sn"]=rfid_sn,["location"]=0x40} --log.info(">>>>>>> update rfid_buf,rfid:", rfid_info.id, ", recv_count:", recv_count) --大于一个完整包,返回剩余部分 if #sub_data > tail_pos then return true, sub_data:sub(tail_pos+1,-1) end return true end --[[ 函数名:proc 功能 :处理从串口读到的数据 参数 : data:当前一次从串口读到的数据 返回值:无 ]] local function proc(data) if not data or string.len(data) == 0 then return end if not rfidType then log.info("uartTaskDahua.proc","rfidType undefined!!") rfidType = nvm.get("rfidType") return false end --追加到缓冲区 rdbuf = rdbuf..data local result,unproc unproc = rdbuf --根据帧结构循环解析未处理过的数据 while true do if rfidType == 'dahua' then result,unproc = parse_dahua(unproc) else log.info("uartTaskDahua.proc","rfidType invalid!!") return false end if not unproc or unproc == "" or not result then break end end rdbuf = unproc or "" if #rdbuf > 1000 then log.warn("uartTaskDahua.proc","rdbuf length > 1000!! rdbuf:", #rdbuf) end end --[[ 函数名: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("uartTask.read bin",data) --log.info("uartTask.read hex",data:toHex()) --处理串口收到的rfid数据 proc(data) end end --[[ 函数名:write 功能 :通过串口发送数据 参数 : s:要发送的数据 返回值:无 ]] function write(s) log.info("uartTask.write",s) uart.write(UART_ID,s.."\r\n") end local function writeOk() log.info("uartTask.writeOk") end --等待时间同步成功消息通知 sys.subscribe("TIMER_SYNC_SUCCESS", function ( ... ) log.info("uartTaskDahua.subscribe","TIMER_SYNC_SUCCESS") timerSyncResult = true sys.publish("MESSAGE_TYPE_START_HEARTBEAT") end) --启动厂测读rfid标签模式,读取指定rfid标签信号 sys.subscribe("factory_test_start_read_rfid", function ( data ) if data.rfid then log.info("uartTaskDahua.subscribe","factory test mode,start read rifd:",data.rfid) isFactoryTestReadRfidMode = true factoryTestReadRfid = data.rfid if not isStartHeartbeat then sys.publish("MESSAGE_TYPE_START_HEARTBEAT") end else log.error("uartTaskDahua.subscribe.factory_test_start_read_rfid","data.rfid not existed!!") end end) --退出厂测读rfid标签模式 sys.subscribe("factory_test_stop_read_rfid", function ( ) log.info("uartTaskDahua.subscribe.factory_test_stop_read_rfid","factory test stop read rifd") isFactoryTestReadRfidMode = false factoryTestReadRfid = "" end) --启动读rfid标签模式,读取指定rfid标签信号 sys.subscribe("cmd_start_read_rfid", function ( data ) if not isStartHeartbeat then sys.publish("MESSAGE_TYPE_START_HEARTBEAT") end if data.rfid then log.info("uartTaskDahua.subscribe.cmd_start_read_rfid","cmd start read rifd:",data.rfid) isCmdReadRfidMode = true cmdReadRfid = data.rfid else log.error("uartTaskDahua.subscribe.cmd_start_read_rfid","data.rfid not existed!!") end end) --退出厂测读rfid标签模式 sys.subscribe("cmd_stop_read_rfid", function ( ) log.info("uartTaskDahua.subscribe.cmd_stop_read_rfid","cmd stop read rifd") isCmdReadRfidMode = false cmdReadRfid = "" end) --保持系统处于唤醒状态,此处只是为了测试需要,所以此模块没有地方调用pm.sleep("uartTask")休眠,不会进入低功耗休眠状态 --在开发“要求功耗低”的项目时,一定要想办法保证pm.wake("uartTask")后,在不需要串口时调用pm.sleep("uartTask") pm.wake("uartTask") --sys.timerStart(function ( ... ) --初始化rfid串口配置参数 rfid_uart_config_init_timer_id = sys.timerLoopStart(function () if not UART_ID then log.info("UART_ID empty, attempt from nvm.get") UART_ID = nvm.get("rfidUartId") return false end if not UART_ID_BAUD_RATE then log.info("UART_ID_BAUD_RATE empty, attempt from nvm.get") UART_ID_BAUD_RATE = nvm.get("rfidUartBaudRate") return false end if not rfid_uart_config_init_timer_id then log.info("rfid_uart_config_init_timer_id empty") return false end log.info("init rfid uart","UART_ID:",UART_ID,"UART_ID_BAUD_RATE:",UART_ID_BAUD_RATE) --注册串口的数据接收函数,串口收到数据后,会以中断方式,调用read接口读取数据 --uart.on(UART_ID,"receive",read) uart.on(UART_ID,"receive",function() sys.publish("UART_RECEIVE") end) --注册串口的数据发送通知函数 uart.on(UART_ID,"sent",writeOk) --配置并且打开串口 --波特率,可选1200,2400,4800,9600,10400,14400,19200,28800,38400,57600,115200,230400,460800,921600 --uart.setup(UART_ID,115200,8,uart.PAR_NONE,uart.STOP_1) uart.setup(UART_ID,UART_ID_BAUD_RATE,8,uart.PAR_NONE,uart.STOP_1) --如果需要打开“串口发送数据完成后,通过异步消息通知”的功能,则使用下面的这行setup,注释掉上面的一行setup --uart.setup(UART_ID,115200,8,uart.PAR_NONE,uart.STOP_1,nil,1) log.info("uart timer loop stop","rfid_uart_config_init_timer_id:",rfid_uart_config_init_timer_id) sys.timerStop(rfid_uart_config_init_timer_id) end, 1000) --end, 10000) local function taskRead() --等心跳开启时,再开始接收串口rfid数据 while true do log.info("uartTaskDahua.taskRead","wait start_rfid_upload_task message ...") local res,data = sys.waitUntil("start_rfid_upload_task", 3600*1000) if res then log.info("uartTaskDahua.taskRead","receive start_rfid_upload_task messages, start UART_RECEIVE task...") isStartHeartbeat = true break else log.info("uartTaskDahua.taskRead","wait start_rfid_upload_task timeout") end end local cacheData = "" local count = 0 while true do cacheData = uart.read(UART_ID,"*l") if cacheData == "" then if not sys.waitUntil("UART_RECEIVE",100) then --log.info("uartTask.taskRead","100ms no data") end else --log.info("uartTask.read hex",cacheData:toHex()) proc(cacheData) cacheData = "" count = count + 1 if count > 1200 then sys.wait(50) count = 0 end end end end --启动串口数据接收任务 sys.taskInit(taskRead) sys.timerLoopStart(function() log.info("uartTaskDahua.timerLoopStart","打印占用的内存:", _G.collectgarbage("count"))-- 打印占用的RAM log.info("uartTaskDahua.timerLoopStart","打印可用的空间", rtos.get_fs_free_size())-- 打印剩余FALSH,单位Byte log.info("uartTaskDahua.timerLoopStart", "rfidMinDiffTime:",rfidMinDiffTime) end, 10000) --rfid离线检测任务 sys.taskInit(function() while true do while true do if rfids_buf["rfid_count"] > 0 then break else log.info("uartTaskDahua.rfidOfflineCheckTask","rfids_buf empty,wait ...") sys.wait(1000) end end local now = os.time() if resetTime >= 1 or resetTime <= -1 then now = now + resetTime --log.info("uartTaskDahua.rfidOfflineCheckTask","resetTime:",resetTime,",reset to time:",now) end --log.info("uartTaskDahua.rfidOfflineCheckTask","start time:",now) local tmp = {["rfid_count"]=0,["buf"]={}} local checkCount = 0 local onlineCount = 0 local offlineCount = 0 local totalCount = 0 for rfid, rfid_info in pairs(rfids_buf["buf"]) do totalCount = totalCount + 1 if rfid_info.time and rfid_info.time > 0 then checkCount = checkCount + 1 if now - rfid_info.time <= rfidOfflineTimeout then if rfid == "953651BE" then --log.info("uartTaskDahua.rfidOfflineCheckTask","rfid is online,skip! rfid:", rfid, "rfid_info.time:", rfid_info.time) end tmp["buf"][rfid] = rfid_info tmp["rfid_count"] = tmp["rfid_count"] + 1 onlineCount = onlineCount + 1 else --首末次信号时间差超过最小时间差值,才认为是有效的末次信号 if rfid_info.time - rfid_info.first_time > rfidMinDiffTime then offlineCount = offlineCount + 1 if rfid == "953651BE" then log.info("uartTaskDahua.rfidOfflineCheckTask","rfid offline! rfid:", rfid, "rfid_info.time:", rfid_info.time, "now:", now, "rfidOfflineTimeout:", rfidOfflineTimeout) end --上报离线后,末次信号 if rfids_buf["buf"][rfid] then --location:0x80 rfid离开 rfid_info.location = 0x80 sys.publish("pub_packet_data_dahua_rfid_info", rfid_info) if offlineCount >= 100 and offlineCount%100 == 0 then --log.info("uartTaskDahua.rfidOfflineCheckTask","wait 3s") sys.wait(50) end end else log.info("uartTaskDahua.rfidOfflineCheckTask","drop it, because: rfid_info.time - rfid_info.first_time <=",rfidMinDiffTime) end end end end rfids_buf = tmp log.info("uartTaskDahua.rfidOfflineCheckTask", "totalCount:",totalCount, "checkCount:",checkCount, "onlineCount:",onlineCount, "offlineCount:",offlineCount) sys.wait(rfidOfflineTimeout*1000 + 2000) end end) --重置系统时间消息(可以校正成自定义时间) sys.subscribe("RESET_CLOCK_TIME", function ( diffTime ) if type(diffTime) == "number" then resetTime = diffTime log.info("uartTaskDahua.subscribe.RESET_CLOCK_TIME","diffTime:",diffTime) end end)