123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550 |
- --- 模块功能:GPS TRACKERE 主逻辑
- -- @author openLuat
- -- @module default
- -- @license MIT
- -- @copyright openLuat
- -- @release 2018.03.27
- require "net"
- require "misc"
- require "mqtt"
- require "gpsv2"
- require "utils"
- require "update"
- require "funlib"
- require "pins"
- require"nvm"
- module(..., package.seeall)
- -- 无网络重启时间,飞行模式启动时间
- local rstTim, flyTim = 600000, 300000
- -- 解除报警的等待时间秒,GPS打开的起始时间utc秒
- local clearTime, startTime = 300, 0
- -- 轨迹消息缓冲区
- local trackFile = {{}, {}, {}, {}, {}, {}, {}, {}, {}, {}}
- -- 传感器数据
- local sens = {
- vib = false, -- 震动检测
- acc = false, -- 开锁检测
- act = false, -- 启动检测
- chg = false, -- 充电检测
- und = false, -- 剪线检测
- wup = false, -- 唤醒检测
- vcc = 0, -- 电池电压
- }
- local frameSn = 0
- --是否发送报警位置
- local isSendAlarm = false
- -- 配置文件
- local dtu = {
- host = "", -- 自定义参数服务器
- passon = 0, --透传标志位
- plate = 0, --识别码标志位
- convert = 0, --hex转换标志位
- reg = 0, -- 登陆注册包
- param_ver = 0, -- 参数版本
- flow = 0, -- 流量监控
- fota = 0, -- 远程升级
- uartReadTime = 500, -- 串口读超时
- netReadTime = 50, -- 网络读超时
- pwrmod = "normal",
- password = "",
- upprot = {}, -- 上行自定义协议
- dwprot = {}, -- 下行自定义协议
- apn = {nil, nil, nil}, -- 用户自定义APN
- cmds = {{}, {}}, -- 自动采集任务参数
- pins = {"", "", ""}, -- 用户自定义IO: netled,netready,rstcnf,
- conf = {{
- -- 数组下标代表通道ID号
- "tcp", -- 协议 TCP | UDP | MQTT |
- "ping", -- 心跳包内容
- "180", -- 心跳包间隔
- "116.62.220.88", -- HOST 地址
- "21890", -- HOST 端口
- 1, -- 通道透传捆绑的串口ID
- "", -- 保持为""
- "", -- 保持为""
- "", -- 自动采集任务间隔
- "" -- SSL 标志位 可选"ssl"
- }, {}, {}, {}, {}, {}, {}}, -- 用户通道参数
- preset = {number = "", delay = 1, smsword = "SMS_UPDATE"}, -- 用户预定义的来电电话,延时时间,短信关键字
- uconf = {
- {1, 115200, 8, uart.PAR_NONE, uart.STOP_1},
- {2, 115200, 8, uart.PAR_NONE, uart.STOP_1},
- {3, 115200, 8, uart.PAR_NONE, uart.STOP_1},
- }, -- 串口配置表
- gps = {
- fun = {"1", "9600", "0", "1", "1", "json", "0", ";", "60"}, -- 用户捆绑GPS的串口,波特率,功耗模式,采集间隔,采集方式支持触发和持续, 报文数据格式支持 json 和 hex,缓冲条数,分隔符,状态报文间隔
- pio = {"", "", "", "", "0", "16"}, -- 配置GPS用到的IO: led脚,vib震动输入脚,ACC输入脚,内置电池充电状态监视脚,adc通道,分压比
- },
- warn = {
- gpio = {},
- adc0 = {},
- adc1 = {},
- vbatt = {}
- },
- task = {}, -- 用户自定义任务列表
- }
- --停止时间阀值
- local stopTimeThreshold = 10
- --定位上报间隔
- local locateReportInterval = 30
- ----------------------------------------------------------传感器部分----------------------------------------------------------
- -- 配置GPS用到的IO: led脚,vib震动输入脚,ACC输入脚,内置电池充电状态监视脚,adc通道,分压比
- function sensMonitor(ledio, vibio, accio, chgio, adcid, ratio)
- -- 点火监测采样队列
- local powerVolt, adcQue, acc, chg = 0, {0, 0, 0, 0, 0}
- -- GPS 定位成功指示灯
- if ledio and default.pios[ledio] then
- default.pios[ledio] = nil
- local led = pins.setup(tonumber(ledio:sub(4, -1)), 0)
- sys.subscribe("GPS_MSG_REPORT", led)
- end
- -- 震动传感器检测
- if vibio and default.pios[vibio] then
- pins.setup(tonumber(vibio:sub(4, -1)), function(msg) if msg == cpu.INT_GPIO_NEGEDGE then sens.vib = true end end, pio.PULLUP)
- default.pios[vibio] = nil
- end
- -- ACC开锁检测
- if accio and default.pios[accio] then
- acc = pins.setup(tonumber(accio:sub(4, -1)), nil, pio.PULLUP)
- default.pios[accio] = nil
- end
- -- 内置锂电池充电状态监控脚
- if chgio and default.pios[chgio] then
- chg = pins.setup(tonumber(chgio:sub(4, -1)), nil, pio.PULLUP)
- default.pios[chgio] = nil
- end
- adc.open(tonumber(adcid) or 0)
- while true do
- local adcValue, voltValue = adc.read(tonumber(adcid) or 0)
- if adcValue ~= 0xFFFF or voltValue ~= 0xFFFF then
- voltValue = voltValue * (tonumber(ratio)) / 3
- -- 点火检测部分
- powerVolt = (adcQue[1] + adcQue[2] + adcQue[3] + adcQue[4] + adcQue[5]) / 5
- table.remove(adcQue, 1)
- table.insert(adcQue, voltValue)
- if voltValue + 1500 < powerVolt or voltValue - 1500 > powerVolt then
- sens.act = true
- else
- sens.act = false
- end
- end
- sens.acc, sens.chg = acc and acc() == 0, chg and chg() == 0
- sens.vcc, sens.und = voltValue, voltValue < 4000
- sys.wait(1000)
- sens.vib = false
- end
- adc.close(tonumber(adcid) or 0)
- end
- ----------------------------------------------------------设备逻辑任务----------------------------------------------------------
- -- 上报设备状态,这里是用户自定义上报报文的顺序的
- -- sta = {"isopen", "vib", "acc", "act", "chg", "und", "volt", "vbat", "csq"}
- function deviceMessage(format)
- if format:lower() ~= "hex" then
- return json.encode({
- sta = {gpsv2.isOpen(), sens.vib, sens.acc, sens.act, sens.chg, sens.und, sens.vcc, misc.getVbatt(), net.getRssi()}
- })
- else
- return pack.pack(">b7IHb", 0x55, gpsv2.isOpen() and 1 or 0, sens.vib and 1 or 0,
- sens.acc and 1 or 0, sens.act and 1 or 0, sens.chg and 1 or 0, sens.und and 1 or 0, sens.vcc, misc.getVbatt(), net.getRssi())
- end
- end
- -- 上传定位信息
- -- [是否有效,经度,纬度,海拔,方位角,速度,载噪比,定位卫星,时间戳]
- -- 用户自定义上报GPS数据的报文顺序
- -- msg = {"isfix", "stamp", "lng", "lat", "altitude", "azimuth", "speed", "sateCno", "sateCnt"},
- function locateMessage(format)
- local isFix = gpsv2.isFix() and 1 or 0
- local lng, lat = gpsv2.getDegLocation()
- local altitude = gpsv2.getAltitude()
- local azimuth = gpsv2.getAzimuth()
- local speed = gpsv2.getSpeed()
- local sateCnt = gpsv2.getUsedSateCnt()
- local sateCno = gpsv2.getCno()
- table.sort(sateCno)
- sateCno = table.remove(sateCno) or 0
- if format:lower() ~= "hex" then
- local msg = {["isFix"] = isFix, ["time"] = os.time(), ["lng"] = lng*60*30000, ["lat"] = lat*60*30000,
- ["altitude"] = altitude, ["azimuth"] = azimuth, ["speed"] = speed, ["sateCno"] = sateCno, ["sateCnt"] = sateCnt}
- log.info("tracker.locateMessage", "------->locate data:", json.encode(msg))
- return msg
- else
- return pack.pack(">b2i3H2b3", 0xAA, isFix and 1 or 0, os.time(), lng, lat, altitude, azimuth, speed, sateCno, sateCnt)
- end
- end
- -- 用户捆绑GPS的串口,波特率,功耗模式,采集间隔,采集方式支持触发和持续, 报文数据格式支持 json 和 hex,缓冲条数,数据分隔符(不包含,),状态报文间隔分钟
- function alert(uid, baud, pwmode, sleep, guard, format, num, sep, interval, cid)
- while true do
- if sys.waitUntil("NTP_SUCCEED", 3600*1000) then
- log.info("tracker.alert", "------->ntp succeed, unpack(dtu.gps.fun):", unpack(dtu.gps.fun))
- break
- end
- end
- uid, baud, pwmode, sleep, num = tonumber(uid), tonumber(baud), tonumber(pwmode), tonumber(sleep), tonumber(num) or 0
- guard, interval = tonumber(guard) == 0, (tonumber(interval) or 0) * 60000
- local cnt, report = 0, function(format)
- log.info("tracker.alert","---------> 0")
- sys.publish("NET_SENT_RDY_" .. (tonumber(cid) or uid), deviceMessage(format))
- end
- --log.info("tracker.alert","------------>", uid, baud, pwmode, sleep, num)
- while true do
- -- 布防判断
- --if not gpsv2.isOpen() and (not guard or sens.vib or sens.acc or sens.act or sens.und or sens.wup) then
- if not gpsv2.isOpen() then
- --sens.wup = false
- startTime = os.time()
- -- GPS TRACKER 模式
- gpsv2.open(uid, baud, pwmode, sleep)
- -- 布防上报
- --report(format)
- --if interval ~= 0 then sys.timerLoopStart(report, interval, format) end
- end
- while gpsv2.isOpen() do
- --[[
- -- 撤防判断
- if os.difftime(os.time(), startTime) > clearTime then
- if guard and sens.vib and sens.acc and sens.act and sens.und and gpsv2.getSpeed() == 0 then
- sys.timerStopAll(report)
- gpsv2.close(uid)
- else
- startTime = os.time()
- end
- end
- ]]
- -- 上报消息
- if sys.waitUntil("GPS_MSG_REPORT") then
- if num == 0 then
- sys.publish("NET_SENT_RDY_" .. (tonumber(cid) or uid), locateMessage(format))
- else
- cnt = cnt < num and cnt + 1 or 0
- table.insert(trackFile, locateMessage(format))
- if cnt == 0 then sys.publish("NET_SENT_RDY_" .. (tonumber(cid) or uid), table.concat(trackFile, sep)) end
- end
- end
- sys.wait(100)
- end
- sys.wait(100)
- end
- end
- --传感器监测任务
- --sys.taskInit(sensMonitor, unpack(dtu.gps.pio))
- --GPS串口任务
- sys.taskInit(alert, unpack(dtu.gps.fun))
- ---------------------------------------------------------- DTU的网络任务部分 ----------------------------------------------------------
- --生成请求报文
- local function createReqData(protoType)
- if tonumber(protoType) == 0x01 then
- --登录包
- frameSn = 1
- imei = misc.getImei()
- if not imei then
- log.error("tracker.loginMsg","getImei failed!")
- return false
- end
- local binData = funlib.hex2bin("676701000b"..string.format("%04x",frameSn)..string.format("%016s",imei).."00")
- return binData
- elseif tonumber(protoType) == 0x03 then
- --心跳包
- frameSn = (frameSn + 1 > 65535) and 1 or (frameSn + 1)
- local status = gpsv2.isFix() and 1 or 0
- local binData = funlib.hex2bin("6767030004"..string.format("%04x",frameSn)..string.format("%04x",status))
- return binData
- elseif tonumber(protoType) == 0x84 then
- --告警包
- local data = locateMessage("json")
- frameSn = (frameSn + 1 > 65535) and 1 or (frameSn + 1)
- binData = funlib.hex2bin("676784001F"..string.format("%04x",frameSn)..string.format("%08x",data.time)..string.format("%08x",data.lat)
- ..string.format("%08x",data.lng)..string.format("%02x",data.speed)..string.format("%04x",data.azimuth).."00000000000000000000"
- ..string.format("%02x",data.isFix).."010000")
- return binData
- else
- return false
- end
- end
- --解析响应报文
- local function parseRespData(packet)
- if not packet then
- log.error("tracker.parseRespData", "packet not existed!")
- return false
- end
- if #packet < 7 then
- log.error("tracker.parseRespData", "packet length < 7!")
- return false
- end
- local parse = {}
- local header = packet:sub(1,2):toHex()
- if header ~= "6767" then
- log.error("tracker.parseRespData", "packet header invalid! packet:", packet:toHex())
- return false
- end
- parse.heaser = header
- local protoType = packet:sub(3,3):toHex()
- parse.protoType = protoType
- local packetLen = tonumber(packet:sub(4,5):toHex(),16)
- if packetLen + 5 ~= #packet then
- log.error("tracker.parseRespData", "packet length error! packet:", packet:toHex())
- return false
- end
- parse.packetLen = packetLen
- --登录响应包
- if protoType == "01" then
- local respSn = tonumber(packet:sub(6,7):toHex(),16)
- parse.respSn = respSn
- return true,parse
- end
- return false
- end
- ---------------------------------------------------------- SOKCET 服务 ----------------------------------------------------------
- local function tcpTask(cid, pios, reg, convert, passon, upprot, dwprot, prot, ping, timeout, addr, port, uid, gap, report, intervalTime, ssl)
- cid, prot, timeout, uid = tonumber(cid) or 1, prot:upper(), tonumber(timeout) or 120, tonumber(uid) or 1
- if not ping or ping == "" then ping = "0x00" end
- local login = false
- local heartbeatTimerId = false
- while true do
- local idx = 0
- if not socket.isReady() and not sys.waitUntil("IP_READY_IND", rstTim) then sys.restart("网络初始化失败!") end
- local c = prot == "TCP" and socket.tcp(ssl and ssl:lower() == "ssl") or socket.udp()
- if c:connect(addr, port) then
- -- 登陆报文
- datalink = true
- local retryCount = 0
- while not login do
- local loginData = createReqData(0x01)
- if loginData then
- if not c:send(loginData) then
- retryCount = retryCount + 1
- log.error("tracker.tcpTask","send loginData failed! loginData:", loginData:toHex(), "retryCount:", retryCount)
- else
- local result, data = c:recv(2 * 1000)
- if not result then
- retryCount = retryCount + 1
- log.error("tracker.tcpTask","wait login response timeout! loginData:", loginData:toHex(), "retryCount:", retryCount)
- else
- local reqSn = tonumber(loginData:sub(6,7):toHex(),16)
- local res,parseData = parseRespData(data)
- if res and parseData.respSn == reqSn then
- login = true
- retryCount = 0
- break
- else
- retryCount = retryCount + 1
- log.error("tracker.tcpTask","parseRespData failed! respData:", data:toHex(), "retryCount:", retryCount)
- end
- end
- end
- else
- log.error("tracker.tcpTask","loginMsg failed!")
- break
- end
- if retryCount > 3 then
- retryCount = 0
- break
- end
- sys.wait(10*1000)
- end
-
- --上报定位/心跳数据报文
- while login do
- local result, data, param = c:recv(timeout * 1000, "GPS_SENT_RDY_" .. (cid or uid))
- --log.info("tracker.tcpTask", "result, data, param", result, data, param and param:toHex())
- if result then
- if #data>=7 and data:sub(1, 3):toHex() == "676703" then
- log.info("tracker.tcpTask","recv heartbeat resp data, data:", data:toHex())
- elseif #data>=7 and data:sub(1, 3):toHex() == "676782" then
- log.info("tracker.tcpTask","recv locate resp data, data:", data:toHex())
- elseif #data>=7 and data:sub(1, 3):toHex() == "676784" then
- log.info("tracker.tcpTask","recv alarm resp data, data:", data:toHex())
- end
- elseif data == "timeout" then
- local heartbeatData = createReqData(0x03)
- if heartbeatData then
- if not c:send(heartbeatData) then
- log.error("tracker.tcpTask","send heartbeatData failed! heartbeatData:", heartbeatData:toHex())
- break
- else
- log.info("tracker.tcpTask","send heartbeatData success, heartbeatData:", heartbeatData:toHex())
- end
- else
- log.error("tracker.tcpTask","create heartbeatData failed!")
- break
- end
- elseif data == ("GPS_SENT_RDY_" .. (cid or uid)) then
- if not c:send(param) then
- log.error("tracker.tcpTask","send locate data failed! locateData:", param:toHex())
- break
- else
- log.info("tracker.tcpTask","send locate data success, locateData:", param:toHex())
- end
- else
- log.error("tracker.tcpTask","recv data failed!")
- break
- end
- end
- end
- c:close()
- log.error("tracker.tcpTask","datalink failed!")
- datalink = false
- login = false
- sys.wait((2 ^ idx) * 1000)
- idx = idx > 9 and 0 or idx + 1
- end
- end
- --定位数据预处理
- local function locateDataFilterTask( cid )
- cid = tonumber(cid) or 1
- local startTime = os.time()
- local locateData = ""
- local points = {}
- local stopStartTime = 0
- local isStopStart = false
- local isTurning = false
- while true do
- local res,data = sys.waitUntil("NET_SENT_RDY_" .. cid, 60*1000)
- if res then
- --静止超过时间阀值,停止上报位置
- if data.speed == 0 and not isStopStart then
- isStopStart = true
- stopStartTime = os.time()
- elseif data.speed == 0 and isStopStart and stopStartTime>0 and os.time()-stopStartTime > stopTimeThreshold then
- log.info("tracker.locateDataFilterTask","stop > 10s,stop gps send!")
- elseif data.speed > 0 then
- --缓存点位
- if #points == 0 then
- table.insert(points,data)
- else
- if #points >= 3 then
- table.remove(points,1)
- end
- table.insert(points,data)
- --过滤过期的点
- local tmp_points = {}
- for i=1,#points do
- if data.time - points[i].time <= 4 then
- table.insert(tmp_points,points[i])
- end
- end
- points = tmp_points
- --达到三个点时,进行拐点检测
- if #points>=3 then
- if math.abs(points[2].azimuth-points[1].azimuth) >= 10 and math.abs(points[3].azimuth-points[2].azimuth) >= 10 then
- isTurning = true
- else
- isTurning = false
- end
- else
- isTurning = false
- end
- end
- isStopStart = false
- stopStartTime = 0
- --拐弯时,收到定位数据就上报
- if isTurning then
- log.info("tracker.locateDataFilterTask","device is turning, start fast report locate!")
- frameSn = (frameSn + 1 > 65535) and 1 or (frameSn + 1)
- locateData = funlib.hex2bin("676782001C"..string.format("%04x",frameSn)..string.format("%08x",data.time)..string.format("%08x",data.lat)
- ..string.format("%08x",data.lng)..string.format("%02x",data.speed)..string.format("%04x",data.azimuth).."00000000000000000000"..string.format("%02x",data.isFix))
- sys.publish("GPS_SENT_RDY_"..cid, locateData)
- locateData = ""
- startTime = os.time()
- else
- --如果达到间隔时间上报定位包
- if os.time()-startTime >= locateReportInterval then
- log.info("tracker.locateDataFilterTask","report interval locate data")
- frameSn = (frameSn + 1 > 65535) and 1 or (frameSn + 1)
- locateData = funlib.hex2bin("676782001C"..string.format("%04x",frameSn)..string.format("%08x",data.time)..string.format("%08x",data.lat)
- ..string.format("%08x",data.lng)..string.format("%02x",data.speed)..string.format("%04x",data.azimuth).."00000000000000000000"..string.format("%02x",data.isFix))
- sys.publish("GPS_SENT_RDY_"..cid, locateData)
- locateData = ""
- startTime = os.time()
- else
- locateData = funlib.hex2bin("676702001C"..string.format("%04x",frameSn)..string.format("%08x",data.time)..string.format("%08x",data.lat)
- ..string.format("%08x",data.lng)..string.format("%02x",data.speed)..string.format("%04x",data.azimuth).."00000000000000000000"..string.format("%02x",data.isFix))
- end
- end
- else
- log.warn("tracker.locateDataFilterTask","stop start ", os.time()-stopStartTime)
- end
-
- else
- if locateData ~= "" then
- log.info("tracker.locateDataFilterTask", "wait ".."NET_SENT_RDY_" .. cid .." timeout, upload locateData")
- sys.publish("GPS_SENT_RDY_"..cid, locateData)
- locateData = ""
- startTime = os.time()
- else
- log.warn("tracker.locateDataFilterTask", "wait NET_SENT_RDY_" .. cid .." timeout!" )
- end
- end
- end
- end
- ---------------------------------------------------------- 参数配置,任务转发,线程守护主进程----------------------------------------------------------
- function connect(pios, conf, reg, convert, passon, upprot, dwprot)
- local flyTag = false
- if not socket.isReady() and not sys.waitUntil("IP_READY_IND", rstTim) then sys.restart("网络初始化失败!") end
- --sys.waitUntil("DTU_PARAM_READY", 120000)
- -- 自动创建透传任务并填入参数
- for k, v in pairs(conf or {}) do
- -- log.info("Task parameter information:", k, pios, reg, convert, passon, upprot, dwprot, unpack(v))
- if v[1] and (v[1]:upper() == "TCP" or v[1]:upper() == "UDP") then
- log.warn("----------------------- TCP/UDP is start! --------------------------------------")
- sys.taskInit(tcpTask, k, pios, reg, convert, passon, upprot, dwprot, unpack(v))
- sys.taskInit(locateDataFilterTask, k)
- end
- end
- -- 守护进程
- datalink = true
- while true do
- -- 这里是网络正常,但是链接服务器失败重启
- if datalink then sys.timerStart(sys.restart, rstTim, "Server connection failed") end
- sys.wait(1000)
- end
- end
- sys.taskInit(connect, pios, dtu.conf, dtu.reg, tonumber(dtu.convert) or 0, (tonumber(dtu.passon) == 0), dtu.upprot, dtu.dwprot)
- --gpio中断
- function gpio13IntFnc(msg)
- log.info("tracker.gpio13IntFnc",msg,getGpio13Fnc())
- --上升沿中断
- if msg==cpu.INT_GPIO_POSEDGE then
- log.info("tracker.gpio13IntFnc","up edge")
- locateData = createReqData(0x84)
- sys.publish("GPS_SENT_RDY_1", locateData)
- --下降沿中断
- else
- log.info("tracker.gpio13IntFnc","down edge")
- end
- end
- --GPIO16配置为中断,可通过getGpio16Fnc()获取输入电平,产生中断时,自动执行gpio16IntFnc函数
- getGpio13Fnc = pins.setup(pio.P0_13,gpio13IntFnc)
- -- NTP同步后清零一次startTime,避免第一次开机的时候utc时间跳变
- sys.subscribe("NTP_SUCCEED", function()
- log.info("tracker.subscribe.NTP_SUCCEED","recv NTP_SUCCEED")
- startTime = os.time()
- end)
- -- 订阅服务器远程唤醒指令
- --sys.subscribe("REMOTE_WAKEUP", function()sens.wup = true end)
|