概述
设备与服务器通讯使用 WebSocket 或 MQTT 协议,数据采用 json 格式,可直接解析, WebSocket 服务器后台使用“/CloudEmbeddedSDKWebsocket/(设备号)”接口,接收设备 发出的 Websocket 请求,并按该文档定义的 json 格式处理数据。
开发者对接该协议,需要向我司申请开发者密钥,密钥由服务器 IP 或域名按一定的算 法生成,考虑到项目安全性,开发者可以提交多个备用域名,申请多个开发者密钥,当新增 或变更服务器,需要对设备下发产品认证指令。
设备开机会检测产品激活码,如果本地没有激活码,会主动向我司产品认证服务器申请 激活码,激活成功后,除主动下发产品认证,不会跟我司服务器有任何通信。
设备与服务器通信需要鉴权,设备连接成功,首先会发出开发者密钥请求,服务器应答 开发者密钥,设备收到开发者密钥进行鉴权,鉴权结果会上行服务器,只有鉴权成功,设备 才会维持正常的通信链路。
由设备发往服务器的数据,遵循设备上行协议,由服务器发往设备的数据,遵循设备下 行协议。
稳定性保障
设备固件运行期间,有守护进程、看门狗进程提供稳定性保障措施,守护进程定时监测设备固件是否存在,如果发现未运行,则尝试启动,看门狗进程主要起监测固件是否正常运行(定时发送喂狗信号),以及与后台通信是否正常的作用。
在web配置界面,我们可以启用和关闭看门狗,当启用看门狗功能,固件每隔5分钟发送喂狗信号到用户后台,用户后台需返回上行数据,设备收到该信号,才会通过管道发送喂狗信号给看门狗进程,从而起到监测固件正常运行和网络通讯正常双重作用,如果关闭看门狗功能,固件则每隔5分钟直接通过管道发送喂狗信号给看门狗进程,可以起到监测固件正常运行的作用。
通讯方式
WebSocket
首先我们进入设备的web配置界面,将协议类型设置为WebSocket,然后保存设置,再重启设备,设备便会以WebSocket通讯方式接入用户后台,WebSocket 服务器后台使用“/CloudEmbeddedSDKWebsocket/(设备号)”接口对接设备。
MQTT
进入设备的web配置界面,将协议类型设置为MQTT,可使用默认的发布主题、注册主题以及客户端ID,然后保存设置,再重启设备,设备便会以MQTT通讯方式接入用户后台,用户后台订阅以上发布主题接收设备上行数据,用户后台下发数据到设备,需要指定以上注册主题。
通讯参数
上传时间间隔:gps数据定时上报的时间间隔,单位秒,gps数据无效时,停止上报。
心跳间隔:心跳包定时上报的时间间隔,单位秒,心跳包主要起维持链路作用,用户后台可根据心跳到达时间与当前时间差,判断设备链路是否读超时,可作为释放链接资源的判断条件。
读超时:固件允许用户后台下行数据时间与当前时间差的最大值,单位秒,设备固件根据读超时判断设备链路是否正常,若超时,固件会释放旧连接资源,并创建新的连接。若新连接创建失败,则会等一个读超时的时间,继续创建连接,直至连接创建成功。
设备配置
设备支持网页、短信、下行指令修改相关参数,下行指令主要由用户后台发起,我们放在后续章节详细讲解,下面主要介绍网页和短信修改参数。
网页配置
设备网页地址:http://192.168.100.1
默认用户名:admin,密码:123456
短信指令
短信指令 | 功能 |
| 系统升级 |
| 系统重启 |
| 请求设备刷新激活码 |
| 服务器域名:api.uddgps.com 服务器端口:80 回传间隔:10秒协议类型:3(websocket),4(mqtt) 心跳间隔:60秒 读超时:300秒 |
| 设置mqtt服务器登录信息 |
| 升级lua脚本 |
| 启动虚拟机 |
| 停止虚拟机 |
| 清除lua脚本 |
协议调试
为了方便调试,我们将设备协议类型设置为MQTT,采用一台免费并可网页调试的MQTT服务器来完成相关功能测试。
MQTT调试网址:MQTT Websocket Client
CF-IOT云端通讯协议:CF-IOT云端通讯协议_物联网平台研发的博客-CSDN博客_cf协议
进入该调试界面,我们点连接按钮后,网页端会创建一个websocket连接,如果与MQTT服务器连接成功,我们便可通过mqtt.p2hp.com这台服务器与设备进行双向通信,在与设备通信前,我们要订阅主题,并设置发布主题。
通常在这个界面我们订阅的主题,对应设备发布主题,设置的发布主题,对应设备的注册主题。
密钥、鉴权、激活码
设备创建链路后,首先发的第一个JSON数据包便是密钥请求,如果用户后台不应答开发者密钥进行鉴权,设备会按心跳间隔重复发此包,直至鉴权成功。
↑密钥请求
{
"channel": "ServerKey_Request",
"DeviceNo": "864977047765106",
"Data": null
}
↓密钥应答
{
"channel": "ServerKey_Response",
"DeviceNo": "864977047765106",
"Data": {
"ServerKey": "0F3EFB4E40C69C7A39C90E319732D6A3"
}
}
↑鉴权结果
{
"channel": "Authentication",
"DeviceNo": "864977047765106",
"Data": {
"Result": true
}
}
↑设备激活码请求
{
"channel": "DeviceActivationCode_Request",
"DeviceNo": "864977047765106",
"Data": null
}
设备第一次请求激活码,是从我司服务器申请,若请求成功,会将激活码保存本地,并上报一条包含激活码的固件信息到用户后台,用户后台应将该激活码保存,当设备数据文件损坏,我司授权服务器不能正常访问时,设备会发送该激活码请求包,用户后台应下发一条包含设备激活码的固件设置指令。
固件信息
↓查询固件
{
"channel": "Parameters_Request",
"DeviceNo": "864977047765106",
"Data": {
"ParameterName": "Firmware",
"Filter": ""
}
}
↑固件信息
{
"channel": "Firmware_Parameters_Response",
"DeviceNo": "864977047765106",
"Data": {
"ModuleState": {
"IMEI": "864977047765106",
"CCID": "89860318245512075960",
"CSQ": "25,99",
"CSQtime": "2020-11-12 19:36:31",
"Revision": "EC20CEFILGR06A03M1G_OCPU_BETA0628"
},
"FunctionSwitch": {
"Watchdog": false,
"GPS": false
},
"DeviceActivationCode": ",E4DFE403DC960AC21DEDA29B4DDC2548,F63FBF4299C4F546B6B36D50F8CB47F6,9F069D3D4F7B29FE35EF9910898AC373,",
"Revision": "HeFeiQILIAN_miniCFIOT_v1.3.5.1.2_20201109"
}
}
↓设置固件
{
"channel": "Firmware_Settings",
"DeviceNo": "864977047765106",
"Data": {
"Switch_Watchdog": 1,
"Switch_GPS": 1,
"DeviceActivationCode": ""
}
}
↑心跳包
{
"channel": "H",
"DeviceNo": "864977047765106",
"Data": null
}
位置测试
↑GPS数据
{
"channel": "GPS",
"DeviceNo": "864977047765106",
"Data": {
"Longitude": 117.2304914,
"Latitude": 31.842440516666667,
"N_S": "N",
"E_W": "E",
"Speed": 0,
"Direction": 164.2,
"Mode": "A",
"DateTime": "2020-11-09 16:43:22.00"
}
}
↓按需秒刷新
用户后台定时下发该信号,设备在GPS数据有效时,自动秒传GPS数据,当停止下发该信号超过30秒,设备会自动关闭秒传GPS功能。
{
"channel": "UpdateSendNowForGPSSignal",
"DeviceNo": "864977047765106",
"Data": null
}
↑GPS刷新数据
{
"channel": "GPS_Refresh_GPRMC",
"DeviceNo": "864977047765106",
"Data": "GPRMC,164826.00,A,3150.563094,N,11713.823810,E,0.0,341.0,091120,4.4,W,A"
}
串口调试
↓串口初始化
{
"channel": "Serial_Open",
"DeviceNo": "862607059109339",
"Data": {
"portName": "Serial1",
"Baud": 9600,
"DataBits": 8,
"Parity": "None",
"StopBits": 1
}
}
↑串口连接事件
{
"channel": "Hardware_onEvent",
"DeviceNo": "862607059109339",
"Data": {
"EventName": "Serial_Open_Success",
"portName": "Serial1",
"Message": ""
}
}
↓写串口
{
"channel": "Serial_writeData",
"DeviceNo": "862607059109339",
"Data": {
"portName": "Serial1",
"Data": "ff 05 00 00 00 00 d8 14"
}
}
↑读串口
{
"channel": "Serial_onData",
"DeviceNo": "862607059109339",
"Data": {
"portName": "Serial1",
"Data": "ff 05 00 00 00 00 d8 14"
}
}
摄像机调试
↓摄像机搜索
固件每隔三分钟通过udp广播方式搜索摄像机、更新状态,网页配置界面里也可以手动搜索摄像机,用户后台可以下发摄像机搜索指令,实现主动搜索摄像机并更新摄像机状态。
{
"channel": "SearchAllIPCamera",
"DeviceNo": "864977047765106",
"Data": null
}
↓查询摄像机信息
{
"channel": "Parameters_Request",
"DeviceNo": "864977047765106",
"Data": {
"ParameterName": "IPCameraParameters",
"Filter": ""
}
}
Filter字段可指定摄像机IP
↑摄像机信息
{
"channel": "IPCamera_Parameters_Response",
"DeviceNo": "864977047765106",
"Data": {
"192.168.100.48": {
"IP": "192.168.100.48",
"SerialNumber": "fe5002562668",
"UserName": "",
"Password": "",
"MediaArray": [{
"ProfileToken": "stream0_0",
"Width": 1920,
"Height": 1080,
"RtspUrl": "rtsp://192.168.100.48/live/ch00_1"
}, {
"ProfileToken": "stream0_1",
"Width": 640,
"Height": 480,
"RtspUrl": "rtsp://192.168.100.48/live/ch00_0"
}],
"SelectMediaKey": "",
"StreamUrl": "",
"HLSurl": "",
"RTMPurl": "",
"Flvurl": "",
"TimeRange": {}
},
"192.168.100.75": {
"IP": "192.168.100.75",
"SerialNumber": "",
"UserName": "",
"Password": "",
"MediaArray": [],
"SelectMediaKey": "",
"StreamUrl": "",
"HLSurl": "",
"RTMPurl": "",
"Flvurl": "",
"TimeRange": {}
}
}
}
从以上返回的摄像机列表里,IP为192.168.100.75的摄像机码流是空的,这是因为该摄像机启用了onvif鉴权,我们要给该摄像机配置用户名和密码,固件才能获取到码流信息。
↓摄像机鉴权
{
"channel": "IPCamera_Settings",
"DeviceNo": "864977047765106",
"Data": {
"Type": 0,
"Data": {
"IP": "192.168.100.75",
"UserName": "admin",
"Password": "admin"
}
}
}
通过以上鉴权设置后,我们下发摄像机搜索指令,并查询摄像机,便可以获取到该摄像机码流信息了。
{
"channel": "IPCamera_Parameters_Response",
"DeviceNo": "864977047765106",
"Data": {
"192.168.100.75": {
"IP": "192.168.100.75",
"SerialNumber": "007D50117625",
"UserName": "admin",
"Password": "admin",
"MediaArray": [{
"ProfileToken": "MainStreamProfileToken",
"Width": 1920,
"Height": 1080,
"RtspUrl": "rtsp://192.168.100.75:554/11"
}, {
"ProfileToken": "SecondStreamProfileToken",
"Width": 640,
"Height": 352,
"RtspUrl": "rtsp://192.168.100.75:554/12"
}],
"SelectMediaKey": "",
"StreamUrl": "",
"HLSurl": "",
"RTMPurl": "",
"Flvurl": "",
"TimeRange": {}
}
}
}
↓图像采集
{
"channel": "Media_Screenshot",
"DeviceNo": "864977047765106",
"Data": {
"SerialNumber": "007D50117625",
"MediaKey": "SecondStreamProfileToken_640_352",
"FileName": "testScreenshot",
"FtpPath": "/Media"
}
}
摄像机拍照操作,会上报相关通知事件
↑拍照成功事件
{
"channel": "Media_onEvent",
"DeviceNo": "864977047765106",
"Data": {
"EventName": "Media_Screenshot_Success",
"Message": "",
"FileName": "/Media/testScreenshot.jpg"
}
}
↑ftp上传成功事件
{
"channel": "Ftp_onEvent",
"DeviceNo": "864977047765106",
"Data": {
"EventName": "FTP_Upload_Success",
"Message": "",
"FileName": "/Media/testScreenshot.jpg"
}
}
↓视频录制
{
"channel": "Media_VideoRecording",
"DeviceNo": "864977047765106",
"Data": {
"SerialNumber": "007D50117625",
"MediaKey": "SecondStreamProfileToken_640_352",
"FileName": "testVideoRecording",
"FtpPath": "/Media",
"Duration": 10,
"MediaType": "flv"
}
}
摄像机录制操作,会上报相关通知事件
↑录制成功事件
{
"channel": "Media_onEvent",
"DeviceNo": "864977047765106",
"Data": {
"EventName": "Media_VideoRecording_Success",
"Message": "",
"FileName": "/Media/testVideoRecording.flv"
}
}
↑ftp上传成功事件
{
"channel": "Ftp_onEvent",
"DeviceNo": "864977047765106",
"Data": {
"EventName": "FTP_Upload_Success",
"Message": "",
"FileName": "/Media/testVideoRecording.flv"
}
}
↓查询摄像机状态
{
"channel": "Parameters_Request",
"DeviceNo": "864977047765106",
"Data": {
"ParameterName": "IPCameraState",
"Filter": ""
}
}
↑摄像机状态信息
{
"channel": "IPCameraState_Response",
"DeviceNo": "864977047765106",
"Data": [{
"IP": "192.168.100.75",
"OnVifServiceUrl": "http://192.168.100.75:8080/onvif/device_service",
"Manufacturer": "IPCAM",
"FirmwareVersion": "V20.1.41.16.3-20200430",
"SerialNumber": "007D50117625",
"MediaArray": [{
"ProfileToken": "MainStreamProfileToken",
"Width": 1920,
"Height": 1080,
"MainStream": false,
"RtspUrl": "rtsp://192.168.100.75:554/11",
"SnapshotUri": "http://192.168.100.75:80/tmpfs/auto.jpg",
"PushStream": {
"State": false,
"HeartbeatTime": "0001-01-01T00:00:00Z",
"StreamUrl": "",
"HLSurl": "",
"RTMPurl": "",
"Flvurl": ""
},
"Screenshot": "",
"InUse": false
}, {
"ProfileToken": "SecondStreamProfileToken",
"Width": 640,
"Height": 352,
"MainStream": false,
"RtspUrl": "rtsp://192.168.100.75:554/12",
"SnapshotUri": "http://192.168.100.75:80/tmpfs/auto.jpg",
"PushStream": {
"State": false,
"HeartbeatTime": "0001-01-01T00:00:00Z",
"StreamUrl": "",
"HLSurl": "",
"RTMPurl": "",
"Flvurl": ""
},
"Screenshot": "",
"InUse": false
}],
"State": true,
"PlayState": false
}]
}
摄像机推流
设备支持按需推流、定时推流,并且两种推流方式可互补使用,如果在摄像机信息里设置了定时推流时间段,当处在非定时推流时间段,我们可以从用户后台定时下发按需推流指令来控制摄像机推流,在摄像机状态信息中,每路摄像机码流都有一个推流结构体,该结构体执行最终的推流操作,我们将推流操作加锁,这就意味着定时推流和按需推流具有互斥性,可以起到互补作用。
定时推流
在摄像机信息里,我们可以为摄像机的某个码流设置推流时间段,目前一个摄像机只能设置一路码流定时推流,并且支持网页和下发指令设置推流属性。
↓设置推流
{
"channel": "IPCamera_Settings",
"DeviceNo": "864977047765106",
"Data": {
"Type": 1,
"Data": {
"IP": "192.168.100.75",
"SelectMediaKey": "SecondStreamProfileToken_640_352",
"StreamUrl": "rtmp://tx.direct.huya.com/huyalive/*********",
"HLSurl": "",
"RTMPurl": "",
"Flvurl": ""
}
}
}
↓设置推流时间段
{
"channel": "IPCamera_Settings",
"DeviceNo": "864977047765106",
"Data": {
"Type": 2,
"Data": {
"IP": "192.168.100.75",
"TimeRange": {
"Friday": [{
"StartTime": "06:00:00",
"EndTime": "18:00:00",
"StartTime1": 21600,
"EndTime1": 64800
}, {
"StartTime": "19:00:00",
"EndTime": "20:00:00",
"StartTime1": 68400,
"EndTime1": 72000
}],
"Monday": [{
"StartTime": "06:00:00",
"EndTime": "18:00:00",
"StartTime1": 21600,
"EndTime1": 64800
}, {
"StartTime": "19:00:00",
"EndTime": "20:00:00",
"StartTime1": 68400,
"EndTime1": 72000
}],
"Saturday": [{
"StartTime": "06:00:00",
"EndTime": "18:00:00",
"StartTime1": 21600,
"EndTime1": 64800
}, {
"StartTime": "19:00:00",
"EndTime": "20:00:00",
"StartTime1": 68400,
"EndTime1": 72000
}],
"Sunday": [{
"StartTime": "06:00:00",
"EndTime": "18:00:00",
"StartTime1": 21600,
"EndTime1": 64800
}, {
"StartTime": "19:00:00",
"EndTime": "20:00:00",
"StartTime1": 68400,
"EndTime1": 72000
}],
"Thursday": [{
"StartTime": "06:00:00",
"EndTime": "18:00:00",
"StartTime1": 21600,
"EndTime1": 64800
}, {
"StartTime": "19:00:00",
"EndTime": "20:00:00",
"StartTime1": 68400,
"EndTime1": 72000
}],
"Tuesday": [{
"StartTime": "06:00:00",
"EndTime": "18:00:00",
"StartTime1": 21600,
"EndTime1": 64800
}, {
"StartTime": "19:00:00",
"EndTime": "20:00:00",
"StartTime1": 68400,
"EndTime1": 72000
}],
"Wednesday": [{
"StartTime": "06:00:00",
"EndTime": "18:00:00",
"StartTime1": 21600,
"EndTime1": 64800
}, {
"StartTime": "19:00:00",
"EndTime": "20:00:00",
"StartTime1": 68400,
"EndTime1": 72000
}]
}
}
}
}
↓按需推流
{
"channel": "StartPushStream",
"DeviceNo": "864977047765106",
"Data": {
"SerialNumber": "007D50117625",
"MediaKey": "SecondStreamProfileToken_640_352",
"StreamUrl": "rtmp://tx.direct.huya.com/huyalive/1199558764847-1199558764847-6888343667394952147-2399117653150-10057-A-1603817515-1?seq=1605155499051&type=simple",
"HLSurl": "",
"RTMPurl": "",
"Flvurl": ""
}
}
用户后台需要定时下发推流指令,才能维持推流过程,当停止下发该推流指令,30秒后自动停止推流。
LUA虚拟机
脚本升级
{
"channel": "LuaVM_Upgrading",
"DeviceNo": "864977047765106",
"Data": {
"URL": "http://www.***/lua.zip"
}
}
启动虚拟机
{
"channel": "LuaVM_Load",
"DeviceNo": "864977047765106",
"Data": null
}
停止虚拟机
{
"channel": "LuaVM_Stop",
"DeviceNo": "864977047765106",
"Data": null
}
清除脚本
{
"channel": "LuaVM_Clear",
"DeviceNo": "864977047765106",
"Data": null
}