在物联网项目实施过程中,我们经常需要对接各类终端设备。其中NB-IoT(窄带物联网)因其低功耗、广覆盖的特性,特别适合烟感、门磁、水表等需要长期稳定运行的设备。电信AEP平台作为国内主流的物联网连接管理平台,为设备接入提供了标准化的解决方案。
选择NB-IoT+电信AEP的组合主要基于以下考虑:
实际项目中我们发现,在老旧小区改造场景下,NB-IoT的信号强度比4G平均高出15dB,这对确保烟感报警的实时性至关重要。
典型接入架构包含三个核心组件:
数据流向示意图:
code复制[烟感设备] --NB-IoT/LwM2M--> [电信AEP] --HTTP/JSON--> [业务平台]
在开始编码前,需在电信物联网开放平台完成以下准备:
productId:产品唯一标识appKey/appSecret:API调用凭证masterKey:敏感操作密钥AEP提供多语言SDK,Java项目推荐通过Maven引入:
xml复制<dependency>
<groupId>com.ctwing</groupId>
<artifactId>aep-sdk-java</artifactId>
<version>2.1.3</version>
</dependency>
初始化客户端时需注意:
java复制// 最佳实践示例
public class AepClientHolder {
private static final int MAX_CONN = 50;
private static final int TIMEOUT = 3000;
private static AepDeviceManagementClient dmClient;
public static synchronized AepDeviceManagementClient getDMClient() {
if (dmClient == null) {
dmClient = AepDeviceManagementClient.newClient()
.appKey(Config.getAppKey())
.appSecret(Config.getAppSecret())
.connectTimeout(TIMEOUT)
.readTimeout(TIMEOUT)
.maxConnTotal(MAX_CONN)
.build();
}
return dmClient;
}
}
设备注册是接入的第一环节,需要注意LwM2M协议的特殊字段:
java复制public DeviceRegisterResult registerDevice(String imei) {
CreateDevice.Other other = new CreateDevice.Other();
other.setAutoObserver(0); // 必须设为0开启自动订阅
CreateDevice device = new CreateDevice();
device.setDeviceName(imei);
device.setDeviceSn(imei);
device.setImei(imei); // NB-IoT设备IMEI即标识
device.setOther(other);
device.setProductId(productId);
device.setOperator("系统自动注册");
// 构建请求
CreateDeviceRequest request = new CreateDeviceRequest();
request.setParamMasterKey(masterKey);
request.setBody(JsonUtil.toJson(device).getBytes(StandardCharsets.UTF_8));
// 执行注册
CreateDeviceResponse response = AepClientHolder.getDMClient()
.CreateDevice(request);
// 处理响应
CreateDeviceResult result = parseResponse(response);
if (result.getCode().equals("2010115")) {
logger.warn("设备已注册: {}", imei);
return DeviceRegisterResult.duplicate(result.getResult().getDeviceId());
}
if (!result.success()) {
throw new AepException("注册失败: " + result.getMsg());
}
return DeviceRegisterResult.success(
result.getResult().getDeviceId(),
result.getResult().getDeviceSn()
);
}
关键注意事项:
当设备需要移出系统时,应同步清理AEP注册信息:
java复制public void unregisterDevice(String deviceId) {
DeleteDeviceRequest request = new DeleteDeviceRequest();
request.setParamMasterKey(masterKey);
request.addParamProductId(productId);
request.addParamDeviceIds(deviceId);
DeleteDeviceResponse response = AepClientHolder.getDMClient()
.DeleteDevice(request);
AepResult result = parseResponse(response);
if (!result.success()) {
throw new AepException("注销失败: " + result.getCode());
}
}
生产环境建议增加重试机制,应对网络抖动等情况。我们实践发现,在删除操作后延迟2秒查询设备状态可确保操作生效。
NB-IoT设备采用"休眠-唤醒"的工作模式,命令下发需要特殊处理:
java复制public String sendCommand(String deviceId, Command command) {
CmdReq cmdReq = new CmdReq();
cmdReq.setDeviceId(deviceId);
cmdReq.setTtl(172800); // 48小时TTL
cmdReq.setProductId(productId);
cmdReq.setOperator(getOperator());
byte[] requestBody;
if (command instanceof Lwm2mProfileCommand) {
cmdReq.setCommand(command.getContent());
requestBody = buildLwm2mRequest(cmdReq);
} else {
cmdReq.setContent(command.getContent());
requestBody = buildStandardRequest(cmdReq);
}
// 记录命令到本地数据库
String localCmdId = commandDao.save(deviceId, command);
try {
CmdAck ack = sendToAep(requestBody);
commandDao.bindAepCommandId(localCmdId, ack.getCommandId());
return localCmdId;
} catch (Exception e) {
commandDao.markFailed(localCmdId);
throw e;
}
}
关键参数说明:
| 参数 | 建议值 | 说明 |
|---|---|---|
| TTL | 172800 | 命令有效期(秒),NB-IoT建议≥24小时 |
| 重试间隔 | 30分钟 | 平台重试间隔,需小于设备唤醒周期 |
| 超时时间 | 5000ms | HTTP调用超时 |
AEP推送的消息通过统一入口处理:
java复制@PostMapping("/aep/callback")
public ResponseEntity<?> handleCallback(@RequestBody String json) {
Message message = parseMessage(json);
Device device = getDevice(message.getDeviceId());
// 更新设备活跃时间
device.setLastActiveTime(now());
deviceRepository.save(device);
switch (message.getMessageType()) {
case "dataReport":
processDataReport(device, message);
break;
case "commandResponse":
processCommandResponse(device, message);
break;
// 其他消息类型...
}
return ResponseEntity.ok().build();
}
数据解析时的注意事项:
不建议依赖AEP的上下线通知,推荐采用心跳超时机制:
java复制@Scheduled(fixedRate = 180000) // 3分钟轮询
public void checkDeviceStatus() {
Instant offlineThreshold = Instant.now().minus(120, ChronoUnit.HOURS);
List<Device> inactiveDevices = deviceRepository
.findByLastActiveTimeBeforeAndOnlineIsTrue(offlineThreshold);
inactiveDevices.forEach(device -> {
device.setOnline(false);
deviceRepository.save(device);
eventPublisher.publishEvent(new DeviceOfflineEvent(device));
});
}
阈值设置建议:
常见异常及解决方案:
| 异常现象 | 可能原因 | 解决方案 |
|---|---|---|
| 注册失败2010113 | IMEI格式错误 | 校验IMEI长度和字符集 |
| 命令超时 | 设备未唤醒 | 延长TTL,调整设备唤醒周期 |
| 数据解析失败 | Payload格式变更 | 增加版本兼容处理 |
| 回调接收失败 | 证书过期 | 定期检查HTTPS证书 |
连接池配置:
properties复制# 最大连接数(根据QPS调整)
aep.sdk.maxConnTotal=200
# 单路由最大连接
aep.sdk.maxConnPerRoute=50
# 空闲连接存活时间(分钟)
aep.sdk.idleConnTime=5
异步处理:
对非实时性操作(如历史数据同步),建议采用消息队列异步处理。
建议监控以下关键指标:
Prometheus配置示例:
yaml复制- name: aep_command_stats
metrics_path: /actuator/prometheus
static_configs:
- targets: ['localhost:8080']
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: localhost:9090
通过策略模式实现不同设备的差异化处理:
java复制public interface DeviceHandler {
void processData(Device device, JsonNode payload);
Command createCommand(JsonNode params);
}
@Service
public class SmokeDetectorHandler implements DeviceHandler {
@Override
public void processData(Device device, JsonNode payload) {
// 烟感特有数据处理逻辑
}
}
@Service
public class WaterMeterHandler implements DeviceHandler {
@Override
public void processData(Device device, JsonNode payload) {
// 水表特有数据处理逻辑
}
}
对于大规模设备管理,使用AEP的批量接口:
java复制public BatchOperationResult batchRegister(List<String> imeis) {
BatchDeviceRequest request = new BatchDeviceRequest();
request.setProductId(productId);
request.setDevices(imeis.stream()
.map(this::buildDeviceInfo)
.collect(Collectors.toList()));
// 执行批量注册
BatchDeviceResponse response = aepClient.batchRegister(request);
// 处理结果
return parseBatchResult(response);
}
批量操作建议每批次不超过500个设备,避免请求超时。实际测试显示,500设备批量注册平均耗时8秒。
通过以上实践,我们成功接入了超过10万台NB-IoT设备,日均处理消息量达200万条。最关键的经验是:针对NB-IoT的特性设计足够宽容的超时机制,并建立完善的状态监测体系。