C200K
[TOC]
1. 前言
1.1 背景
方便客户基于C200K基站设备使用 通信基站数据接收及解析。
1.2 编写目的
此说明书是为了说明协议内容,开发流程,方便基于此进行二次开发。
1.3 读者对象
- 相关研发人员
- 方案制定人员
- 决策人员
2. 开发流程
C200K基站,采用MQTT协议进行开发,基站接入MQTT服务器,应用从MQTT服务器下发指令和接收数据
整个交互图如下:
2.1 MQTT服务器准备
MQTT服务器要求:
厂家使用EMQX 进行测试
能够正常收发MQTT数据。
服务器的性能参数由业务决定。
域名:
公网域名或ip或者基站所处局域网ip。
MQTT服务器配置结果检查
使用MQTT.FX客户端,配置域名,端口,用户名和密码信息,然后连接,如果提示连接成功,则表示MQTT服务器可用。基站clientid 是基站编号。
2.2 基站配置
通过USB连接基站,打开基站服务器配置软件。
如果基站没有连接电脑,工具显示检测基站中,这时候读写无效。
连接正常后,按照各个信息进行写入。
设置基站服务器域名(ip)、端口,用户信息、密码信息,写入基站。
如果MQTT服务器没有启用用户密码验证,用户密码信息可以随机设置。
拔掉USB,连接网线,接入网络,查看网络是否连接正常。(基站上面的灯是否蓝色闪烁)
如果选用POE供电,请使用标准的POE(48V)供电
基站说明
服务器IP设置软件下载
2.3 应用开发
整体开发流程图如下:
3. 功能清单
3.1 支持的功能清单
功能 | 说明 |
---|---|
授时 | 给基站校时 |
基站上下线 | 基站上下线信息 |
键盘上下线 | 键盘上下线信息 |
基站信息设置与读取 | 基站信息设置与读取 |
基站信息上报 | 基站信息上报 |
基站错误信息上报 | 基站错误信息上报 |
键盘上线 | 键盘上线 |
读取白名单 | 读取白名单 |
添加白名单 | 添加白名单 |
删除白名单 | 删除白名单 |
清空白名单 | 清空白名单 |
基站复位 | 基站复位 |
签到 | 键盘签到 |
单题 | 下发一题练习 |
抢答 | 抢答 |
多级题号多题 | 多级题号多题 |
基站升级 | 基站固件升级 |
键盘升级 | 键盘固件升级 |
文本下发 | 文本下发 |
学生卡信息上报 | 学生卡信息上报 |
下发生避字 | 下发生避字 |
3.2 协议简介
3.2.1 MQTT
关于MQTT说明,请参考 https://mqtt.org/
${clinetId} clinetId 是基站编号,为替换字符串。
3.2.1.1 应用程序发布的主题
主题 | 功能 |
---|---|
/client/${clientId}/operate | 基站的设置,查询等指令 |
/lesson/class/${clinetId}/receive | 接收答题的指令 |
3.2.1.2 应用程序订阅的主题
主题 | 功能 |
---|---|
/client/${clientId}/online | 基站上线通知。 |
/client/${clientId}/offline | 基站下线通知。 |
/client/${clientId}/card/online | 答题器上线通知。 |
/client/${client}/operate | 对基站进行设置或查询信息 |
/client/${clientId}/report | 基站主动上报状态,或者对设置指令的回应。 |
/lesson/class/${clinetId}/receive | 基站对答题的设置 |
/lesson/class/${clinetId}/send | 基站对答题的反馈 |
/client/${clientId}/error | 基站错误信息上报 |
3.2.2 协议格式
协议采用json格式,数据中字段见属性介绍。
3.2.3 指令属性介绍
名称 | 类型 | 说明 |
---|---|---|
i | number | 消息指令类型(instruction),取值范围: 1-9999。 i = 3 添加白名单 i = 4 删除白名单 i = 5 清除白名单 i = 11 停止 i = 12开启答题 i = 13 接收答题数据 i = 14 键盘上线 i = 17 基站升级 i = 18 升级报告 i = 19 授时 i = 20 设置基站信息 i = 21基站信息上报 i = 22基站错误信息上报 i = 28 键盘升级 i = 29 键盘升级报告 I= 30 读取白名单 I= 31 白名单上传 i = 40 基站上线 i = 41 基站下线 i = 60 文本下发 i = 61 文本下发返回 |
m | object | 答题消息的内容(message) 对象或数组 数组对象,一般与分包结合,分包原则是发送的json数据不要超过1k,如果超过1k,基站将无法接收。 大数据将采用分包发送,每包的数据不超过1k |
c | string | 答题器的序号(card) |
t | number | 时间戳(10位timestamp) 单位秒 需要先校时 |
qm | number | 模式(question model)取值范围: 0:停止 1:单题 7:签到 14:多级编号答题 |
qt | number | 题型(question type),取值范围: 0:字母单选 1:字母多选 2:数字题 3:判断题 5:抢答 7: 自判题判断题 |
qs | number | 该题型数量(question sum),单题为1,多题则为实际数量。 |
qn | number | 题目序号(question No),题目顺序号 |
dn | string | display no 显示编号,键盘上显示的题目编号 |
a | string | 答案(answer) 示例:字母单选:A 字母多选:A|B|C |
en | string | 练习(考试)编号(exercise No)每次考试或者练习的唯一编号,用于确定提交的答案属于那次练习。 |
on | number | 单选和多选题的选项数量(option no) |
cid | string | 客户端ID(client Id) |
cm | string | 客户端型号(client Model) |
cv | string | 客户端版本(client Version)基站软件版本号 |
f | number | 信道频点(frequency) 基站频点取值:1-80 |
content | string | 题干内容(选项信息都包含在此),中文信息用hex进行传输。内容前面需要 hex:详见后面示例 |
sn | string | sn 序列号 |
et | number | 错误类型et =1 基站频点冲突 |
em | string | 错误消息em = "3" 配合et使用,如et=1 em=3 表示3号频点冲突 |
success | number | 0 成功 -1 错误/失败 -2 基站忙 |
3.2.4 分包示例
如果要下发第二行文本,一次业务需要下发50个键盘。 正常组包,基站将会接收失败:
{
"i": 60,
"en": "6",
"cmd": 10,
"total": 1,
"current": 1,
"qs": 50,
"m": [{
"sn": "2126833671",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833672",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833673",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833674",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833675",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833676",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833677",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833678",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833679",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833680",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833681",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833682",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833683",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833684",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833685",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833686",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833687",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833688",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833689",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833690",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833691",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833692",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833693",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833694",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833695",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833696",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833697",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833698",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833699",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833700",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833701",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833702",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833703",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833704",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833705",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833706",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833707",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833708",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833709",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833710",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833711",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833712",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833713",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833714",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833715",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833716",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833717",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833718",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833719",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833720",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}]
}
该数据大小超过了1k(实际4091字节),基站会接收失败。
要采取分包进行分送,每包大小不要超过1k(1024字节)
现在对数据进行分包,通过计算每包发送10个键盘数据。分为5包。
下面是5包的每一包数据:
第一包:
{
"i": 60,
"en": "6",
"cmd": 10,
"total": 5,
"current": 1,
"qs": 50,
"m": [{
"sn": "2126833671",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833672",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833673",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833674",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833675",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833676",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833677",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833678",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833679",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833680",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}]
}
5包,total 要赋值5
当前是第一包 current 赋值1
总数有50个,qs赋值50
数据包大小892字节,基站能接收。
第二包:
{
"i": 60,
"en": "6",
"cmd": 10,
"total": 5,
"current": 2,
"qs": 50,
"m": [{
"sn": "2126833681",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833682",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833683",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833684",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833685",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833686",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833687",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833688",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833689",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833690",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}]
}
5包,total 要赋值5
当前是第二包 current 赋值2
总数有50个,qs赋值50
数据包大小892字节,基站能接收。
第三包:
{
"i": 60,
"en": "6",
"cmd": 10,
"total": 5,
"current": 3,
"qs": 50,
"m": [{
"sn": "2126833691",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833692",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833693",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833694",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833695",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833696",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833697",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833698",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833699",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833700",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}]
}
5包,total 要赋值5
当前是第三包 current 赋值3
总数有50个,qs赋值50
数据包大小892字节,基站能接收。
第四包:
{
"i": 60,
"en": "6",
"cmd": 10,
"total": 5,
"current": 4,
"qs": 50,
"m": [{
"sn": "2126833701",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833702",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833703",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833704",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833705",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833706",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833707",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833708",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833709",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833710",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}]
}
5包,total 要赋值5
当前是第四包 current 赋值4
总数有50个,qs赋值50
数据包大小892字节,基站能接收。
第五包:
{
"i": 60,
"en": "6",
"cmd": 10,
"total": 5,
"current": 5,
"qs": 60,
"m": [{
"sn": "2126833711",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833712",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833713",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833714",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833715",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833716",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833717",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833718",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833719",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}, {
"sn": "2126833720",
"text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8"
}]
}
5包,total 要赋值5
当前是第五包 current 赋值5
总数有50个,qs赋值50
数据包大小892字节,基站能接收。
每包的数据计算,少于1k即可。
3.2.5 生避字检测方法
下发文本姓名时,需要先检测下发的姓名内容
下发的内容当中,如果有些字属于生避字,需要在业务进行前,先把生避字对应的字码下发给到基站。
目前键盘最多存储10个生避字。
字码表文件:字码表下载
判断字符的GB2312编码是否是生避字,方法:
字符的GB2312编码:
byte[] inputs = input.getBytes("GB2312"); // 原文内容
byte数组转int型
public static int bytes2ToInt(byte[] b, int offset) {
int x = 0;
if (b.length - offset >= 2) {
x = (int) (((b[offset ] & 0xff) << 8) | ((b[offset + 1] & 0xff) << 0));
}
return x;
}
判断这个字符是否是生避字
public static boolean isGB2312Font(int code) {
int GB_H = (code >> 8) & 0xFF;
int GB_L = code & 0xFF;
if (GB_H < 0xA1 || GB_H > 0xF7) {
return true;
} else if (GB_L < 0xA1 || GB_L > 0xFE) {
return true;
}
return false;
}
获取这个生避字的编码:
获取字码表的方法是该字符的GB2312的16进制数,然后根据该16进制数据从字码文件中定位该字符的字码
例如计算出“綩”的GB2312得到的byte,转换byte字节为整数48719。
然后在字码表文件code.dat文件偏移48719*24位置,然后读取24个字节,为该字的字码(每个字的字码为24个字节)
下发给基站
byte[] inputs = "綩".getBytes("GB2312"); // 原文内容
int code = bytes2ToInt(inputs,0);
byte[] result = readFileContent(code,24);
String hex = bytesToHexString(inputs) + bytesToHexString(result);
public byte[] readFileContent(int start, int len) {
byte[] buffer = new byte[len];
try {
InputStream inputStream = new FileInputStream(new File("path/code.dat"));
inputStream.skip(start*len);
inputStream.read(buffer, 0, len);
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
return buffer;
}
public static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return stringBuilder.toString();
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
stringBuilder.append("");
}
return stringBuilder.toString();
}
最后下发的json数据
{
"i": 60,
"cmd": 70,
"current": 1,
"total": 1,
"qs": 1,
"en": "17",
"m": [{
"text": "hex:be4fdcb3c880669af203f292f6000e0006000806010007080e00",
"sn": "2126833671"
}]
}
3.3 功能说明
授时
对客户端授时,发送指令即可,基站会完成校时。
主题 : /client/${client}/operate
{
"i": 19,
"t": 1634802943
}
名称 | 备注 |
---|---|
i = 19 | 授时 |
t | 当前时间轴秒 |
基站管理
基站上线
由基站发给应用程序,应用需要订阅主题
主题 : /client/${clientId}/online
指令内容:
{
"i": 40
}
名称 | 备注 |
---|---|
i = 40 | 基站上线 |
基站下线
由服务器发给应用程序,应用需要订阅主题
主题:/client/${clientId}/offline
指令内容:
{
"i": 41
}
名称 | 备注 |
---|---|
i = 41 | 基站下线 |
基站信息设置与读取
应用程序发送给基站
主题: /client/${client}/operate
指令内容:
设置
{
"i": 20,
"m": {
"f": 1,
"mqtt_ip": "edunew.sunvotecloud.cn",
"mqtt_port": 1883,
"mqtt_username": "admin",
"mqtt_password": "1223456789"
}
}
读取
{
"i": 20,
"m": {
}
}
名称 | 备注 |
---|---|
i= 20 | 基站信息设置与读取 |
f | 基站频点 频点范围1-80 |
mqtt_ip | 连接服务器的IP |
mqtt_port | 连接mqtt服务器的端口 |
mqtt_username | 连接mqtt服务器的用户名 |
mqtt_password | 连接mqtt服务器的密码 |
注意 | 如果没有该字段则不设置该值,保持原值下发对有需要修改的值设置完成后,基站信息会自动上报一次 |
基站信息上报
基站发送给应用程序
主题: /client/${client}/report
指令内容:
{
"i": 21,
"cId": "2024010282",
"m": {
"f": 1,
"mqtt_ip": "edunew.sunvotecloud.cn",
"mqtt_port": 1883,
"mqtt_username": "admin",
"mqtt_password": "1223456789"
}
}
名称 | 备注 |
---|---|
i= 21 | 基站信息上报 |
cId | 客户端ID |
f | 基站频点 |
mqtt_ip | 连接服务器的IP |
mqtt_port | 连接mqtt服务器的端口 |
mqtt_username | 连接mqtt服务器的用户名 |
mqtt_password | 连接mqtt服务器的密码 |
基站错误信息上报
基站发送给应用程序主题
5S 报一次
主题:/client/${client}/error
指令内容:
{
"i": 22,
"m": {
"et": 1,
"em": "3"
}
}
名称 | 备注 |
---|---|
i = 22 | 基站错误信息上报 |
et | 错误类型 et = 1 :基站频点冲突 |
em | 错误消息 em=3 :3号频点示例 基站频点3冲突 |
键盘管理
键盘上线
由基站发给应用程序,应用需要订阅主题
主题: /client/${clientId}/card/online
指令内容:
{
"i": 14,
"m": [{
"c": "2126833671",
"t": 1626016299
}]
}
名称 | 备注 |
---|---|
i = 14 | 键盘上线 |
c | 键盘sn号 |
t | 下线时间 时间戳单位秒 需要先校时 |
读取白名单
应用程序发送给基站
主题: /lesson/class/${clinetId}/receive
指令内容:
{
"i": 30
}
名称 | 备注 |
---|---|
i = 30 | 读取白名单 |
基站发给应用程序主题
主题: /lesson/class/${clinetId}/receive
指令内容:
{
"i": 31,
"total": 1,
"current": 1,
"m": ["2126932551"]
}
名称 | 备注 |
---|---|
i = 31 | 白名单上传 |
total | 一共上传的数据包数,这里分包原则参考指令属性m介绍说明及分包示例 |
current | 当前第几包 ,这里分包原则参考指令属性m介绍说明及分包示例 |
m | 键盘序列号列表 |
添加白名单
应用程序发送给基站
主题: /lesson/class/${clinetId}/receive
指令内容:
{
"i": 3,
"m": ["2126833703", "2126833671"]
}
名称 | 备注 |
---|---|
i = 3 | 添加白名单m内包含白名单键盘按编号 |
m | 键盘序列号列表 |
删除白名单
应用程序发送给基站
主题: /lesson/class/${clinetId}/receive
指令内容:
{
"i": 4,
"m": ["2126833703", "2126833671"]
}
名称 | 备注 |
---|---|
i = 4 | 删除白名单m内包含白名单键盘按编号 |
m | 键盘序列号列表 |
清空白名单
应用程序发送给基站
主题: /lesson/class/${clinetId}/receive
指令内容:
{
"i": 5
}
名称 | 备注 |
---|---|
i = 5 | 清空白名单 |
签到
开启签到
由应用程序发给基站,应用按照主题发送
主题: /lesson/class/${clinetId}/receive
指令内容:
{
"i": 12,
"en": "6",
"qm": 7,
"m": {
"tp": "pin",
"content": "hex:cae4c8ebd5cbbac5cdeab3c9b0f3b6a8"
}
}
名称 | 备注 |
---|---|
i = 12 | 下发答题 |
en | 序号 |
qm = 7 | 签到 |
tp | 签到功能 目前支持传入固定值:"pin" 为pin码签到 |
content | 签到标题,hex: 为内容的GB2312编码的hex内容 |
接收签到信息
由基站发给应用程序,应用订阅主题
主题: /lesson/class/${clinetId}/send
指令内容:
{
"i": 13,
"en": "6",
"qm": 7,
"c": "2126833671",
"m": [{
"a": "6542",
"t": 1626016599
}]
}
名称 | 备注 |
---|---|
i = 13 | 签到数据 |
t | 提交答案时间 时间戳单位秒 需要先校时 |
a | 签到信息(签到码) |
停止签到
由应用程序发给基站,应用按照发送
主题:/lesson/class/${clinetId}/receive
指令内容:
{
"i": 11,
"en": "6"
}
名称 | 备注 |
---|---|
i = 11 | 停止签到 |
en | 停止对序号6的练习 |
单题
开启答题
由应用程序发给基站,应用按照主题发送
主题: /lesson/class/${clinetId}/receive
指令内容:
{
"i": 12,
"en": "1",
"qm": 1,
"m": {
"qt": 0,
"on": 4,
"content": "hex:d7d4b6a8d2e5b1eacce2"
}
}
名称 | 备注 |
---|---|
i = 12 | 下发答题 |
en | 序号 |
qm = 1 | 单题模式 示例为单题模式的字母单选 |
qt | 字母单选 |
on | 选项数 |
content | 题目主干内容,hex: 为内容的GB2312编码的hex内容 |
System.out.println(new String(hex2Bytes(bytesToHexString("李四".getBytes("GB2312"))),"GB2312"));
public static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return "";
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
stringBuilder.append("");
}
return stringBuilder.toString();
}
public static byte[] hex2Bytes(String hex){
if(hex.length() % 2 == 0){
byte[] ret = new byte[hex.length() / 2];
for(int i = 0 ; i< hex.length() / 2 ; i++){
ret[i] = (byte)Integer.parseInt(hex.substring(2*i,2*i+2),16);
}
return ret;
}
return null;
}
接收答题数据
由基站发给应用程序,应用订阅主题
主题: /lesson/class/${clinetId}/send
指令内容:
{
"i": 13,
"en": "1",
"qm": 1,
"c": "2126833671",
"m": [{
"qn": 1,
"a": "A",
"t": 1626016299
}]
}
名称 | 备注 |
---|---|
i = 13 | 答题数据 |
qn | 上报答题数据,单题题号固定为1 |
t | 提交答案时间 时间戳单位秒 需要先校时 |
a | 答题数据 |
停止答题
由应用程序发给基站,应用按照发送
主题:/lesson/class/${clinetId}/receive
指令内容:
{
"i": 11,
"en": "1"
}
名称 | 备注 |
---|---|
i = 11 | 停止答题 |
en | 停止对序号1的练习 |
抢答
开启答题
由应用程序发给基站,应用按照发送
主题: /lesson/class/${clinetId}/receive
指令内容:
{
"i": 12,
"en": "2",
"qm": 4
}
名称 | 备注 |
---|---|
i = 12 | 开启答题 |
en | 序号2的练习 |
qm | qm =4 抢答题 |
接收答题数据
由基站发给应用程序,应用订阅
主题: /lesson/class/${clinetId}/send
指令内容:
{
"i": 13,
"en": "2",
"qm": 4,
"m": [{
"c": "2126833671",
"t": 1626016299
}]
}
名称 | 备注 |
---|---|
i = 13 | 接收答题数据 |
en | 序号2的练习 |
qm | qm =4 抢答题 |
c | 键盘编号 |
t | 提交答案时间 时间戳单位秒 需要先校时 |
停止答题
由应用程序发给基站,应用按照主题发送
主题:/lesson/class/${clinetId}/receive
指令内容:
{
"i": 11,
"en": "2"
}
名称 | 备注 |
---|---|
i = 11 | 停止答题 |
en | 停止对序号2的练习 |
多题题号多题
开启答题
由应用程序发给基站,应用按照主题发送
主题: /lesson/class/${clinetId}/receive
指令内容:
{
"i": 12,
"en": "3",
"qm": 14,
"total": 1,
"current": 1,
"qs": 5,
"m": [{
"qt": 0,
"on": 4,
"dn": "1"
}, {
"qt": 1,
"on": 4,
"dn": "2-1"
}, {
"qt": 2,
"dn": "2-2"
}, {
"qt": 3,
"dn": "2-2-1"
}, {
"qt": 7,
"dn": "3"
}]
}
名称 | 备注 |
---|---|
i = 12 | 开启答题 |
en | 12341练习 |
qm = 14 | 多题多级编号模式 |
current | 当前第几包数据,这里分包原则参考指令属性m介绍说明及分包示例 |
total | 一共几包数据 ,这里分包原则参考指令属性m介绍说明及分包示例 |
qs | 题目总数 |
qt | 0:字母单选 1: 字母多选 2: 数字题 3: 判断题 7: 自判题 |
on | 选项数量 |
dn | 显示编号,题目最多3级数字编号,多级数字编号中间必须以“-”隔开,每级最大数200 |
接收答题数据
由基站发给应用程序,应用订阅主题
主题: /lesson/class/${clinetId}/send
指令内容:
{
"i": 13,
"en": "3",
"qm": 14,
"c": "2126833671",
"m": [{
"qn": 1,
"a": "A",
"t": 1626016299
}]
}
名称 | 备注 |
---|---|
i = 13 | 接收答题数据 |
en | 12341练习 |
qm = 14 | 多题多级编号模式 |
c | 键盘编号 |
qn | 顺序题号,不为显示编号,是上述题目的顺序号 |
t | 提交答案时间 时间戳单位秒 需要先校时 |
a | 答题数据 |
停止答题
由应用程序发给基站,应用按照主题发送
主题:/lesson/class/${clinetId}/receive
指令内容:
{
"i": 11,
"en": "3"
}
名称 | 备注 |
---|---|
i = 11 | 停止答题 |
en | 停止对序号12341的练习 |
基站升级
开始升级
主题: /client/${client}/operate
指令内容:
{
"i": 17,
"m":
{
"url": "http://120.78.57.84:8888/file/C200K-V200.0.21.bin",
"version": "200.0.21",
"CRC": 2382669270
}
}
名称 | 备注 |
---|---|
i = 17 | 基站升级 |
en | 序号5 |
url | 固件包地址,基站能访问到的路径 |
version | 新固件的版本 |
CRC | 固件文件的CRC值 |
升级进度报告
主题: /client/${client}/report
指令内容:
{
"i": 18,
"m": {
"cId": "3411111156",
"percent": 0,
"status": 0
}
}
名称 | 备注 |
---|---|
i = 18 | 升级进度报告 |
cId | 基站编号 |
percent | 升级百分比 |
status | 升级状态信息。 0:未开始 1:升级中 2:升级成功 4:固件版本号错误 5:不在升级名单内 6:升级指令错误 >127:升级中的错误码。 |
键盘升级
开始升级
主题: /client/${client}/operate
指令内容:
{
"i": 28,
"m": {
"url": "http://120.78.57.84:8888/file/S6_2.4G_2.1.2.50_GB2312_CGC_add_head.bin",
"version": "2.1.2.50",
"CRC": 10605263
}
}
文本下发
文本下发
主题: /lesson/class/${clinetId}/receive
指令内容:
{
"i": 60,
"en": "6",
"cmd": 1,
"total": 1,
"current": 1,
"qs": 2,
"m": [{
"sn": "2126833671",
"text": "hex:d5c5c8fd"
},{
"sn": "2126833672",
"text": "hex:c0eecbc4"
}]
}
名称 | 备注 |
---|---|
i = 60 | 文本下发 |
sn | 键盘列表 |
current | 当前第几包 ,这里分包原则参考指令属性m介绍说明及分包示例 |
total | 一共多少包,这里分包原则参考指令属性m介绍说明及分包示例 |
qs | 键盘总数 如果是多包,是多包总和 |
cmd | 下发的命令 1: 姓名下发 10: 第二行文本 70: 生避字,生避字规则:内容为GB2312的hex编码,先是生避字的hex编码,紧接着是生避字的字符编码,生避字的字符编码为24个字节。一个生避字是26个字节52个字符(hex编码) 一包最多发3个生避字,如果超过3个生避字,需要多包发送 85: 自判题标题 87: 清除 |
text | 标题信息,以hex:开头添加的内容 内容为GB2312的hex编码 |
System.out.println(new String(hex2Bytes(bytesToHexString("李四".getBytes("GB2312"))),"GB2312"));
public static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return "";
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
stringBuilder.append("");
}
return stringBuilder.toString();
}
public static byte[] hex2Bytes(String hex){
if(hex.length() % 2 == 0){
byte[] ret = new byte[hex.length() / 2];
for(int i = 0 ; i< hex.length() / 2 ; i++){
ret[i] = (byte)Integer.parseInt(hex.substring(2*i,2*i+2),16);
}
return ret;
}
return null;
}
文本下发返回
主题: /lesson/class/${clinetId}/send
指令内容:
{
"i": 61,
"cmd": 1,
"m": [{
"sn": "2126833671",
"success": 0
}]
}
名称 | 备注 |
---|---|
i = 61 | 文本下发返回 |
sn | 键盘号 |
cmd | 下发的命令 1: 姓名下发 10: 第二行文本 70: 生避字,生避字规则(参考:生避字检测方法):内容为GB2312的hex编码,先是生避字的hex编码,紧接着是生避字的字符编码,生避字的字符编码为24个字节。一个生避字是26个字节52个字符(hex编码) 85:自判题标题 87:清除 |
success | 是否成功。 0 表示成功 -1 失败 -2 基站忙,应用程序需要等待一段时间再发送 |
注意:连续文本下发,需要等待上一条命令返回(成功或失败)后,再发送下一条指令。
学生卡信息上报
卡初始化组网的时候会上报一次
主题:/client/${client}/report
指令内容:
{
"i":32,
"m":[
{
"c":"1873373905",
"cv":"0.2.2",
"hw":"67.0",
"t":1638865040
}
]
}
名称 | 备注 |
---|---|
i = 32 | 学生卡信息上报 |
c | 键盘编号 |
cv | 固件版本号 |
hw | 硬件型号 |
t | 时间戳 时间戳单位秒 需要先校时 |
基站复位
应用程序发送给基站
主题: /lesson/class/${clinetId}/receive
指令内容:
{
"i": 62
}
名称 | 备注 |
---|---|
i = 62 | 基站复位 |