AN:ChirpStack 网关接入 ThinkLink(经 lns-bridge 转发桥)
适用:ChirpStack v4 + ThinkLink(AS MQTT)+ lns-bridge 转发桥。 场景:把原本直连 ThinkOne NS 的 ManThink LoRaWAN 设备/网关,改由自建 ChirpStack 作 NS,再经转发桥把数据双向桥接到 ThinkLink。凭据/密钥/地址用占位符,部署时替换。
1. 架构
设备 ─上行─▶ 网关 ─▶ ChirpStack(NS) ─▶ application up(MQTT/JSON)
└─ lns-chirpstack-up 桥 ─▶ ThinkLink AS /v32/{tenant}/as/up/data/{eui} ─▶ 物模型 parser ─▶ 遥测
下行:ThinkLink RPC ─▶ AS /v32/{tenant}/as/dn/data/{eui} ─▶ lns-chirpstack-down 桥 ─▶ ChirpStack command/down ─▶ 网关 ─▶ 设备涉及 3 个 broker:ChirpStack mosquitto(外部)、ThinkLink AS(内部)、桥在两者间搬运。 铁律:同一设备不可同时在两个 NS 上有效——迁移前必须先禁用源 NS(ThinkOne)的设备档案,否则双 NS 各自应答 Join / 管帧计数器 / 发下行 → 数据紊乱。
2. 前置
- Linux + Docker 主机(≥2vCPU/4GB,内存紧张加 swap)。
- 设备入网参数(DevEUI、频段、Class、OTAA AppKey 或 ABP 的 DevAddr/NwkSKey/AppSKey);ThinkLink 侧可经
mtSdevPfMacsFindPage取。 - 网关可 SSH,且包转发器支持 Semtech UDP。
- ThinkLink 账号;平台交互一律经
tkl-api-caller。
3. 部署 ChirpStack v4
git clone https://github.com/chirpstack/chirpstack-docker && cd chirpstack-docker && docker compose up -d- 国内主机先配 Docker 镜像加速器(
/etc/docker/daemon.json的registry-mirrors)。 - 宿主端口冲突时改 UI 映射(如
8070:8080)。 - CN470 频段:在
configuration/chirpstack/chirpstack.toml的enabled_regions加入与设备频点匹配的子带(设备上行 470.3–471.7 MHz →cn470_0,各子带频点见region_cn470_*.toml),docker compose restart chirpstack。
4. ChirpStack 建档(gRPC)
登录只在 gRPC 暴露(REST 网关不含 internal/login),用 grpcurl:
JWT=$(grpcurl -plaintext -d '{"email":"<admin>","password":"<pw>"}' <HOST>:<GRPC_PORT> api.InternalService/Login | jq -r .jwt)依次建:Application(记 applicationId)→ Device Profile(region=CN470、regionConfigId=cn470_0、macVersion 按设备、Class 按设备真实能力,电池传感器多为 Class A)→ Device(关联 app+profile、skipFcntCheck=true)→ ABP 激活(Activate 传 devAddr+appSKey+nwkSEncKey/sNwkSIntKey/fNwkSIntKey,1.0.x 三者同为 NwkSKey)→ Gateway(gatewayId=网关 EUI)。
仅有会话密钥、想免重入网用 ABP;OTAA 需 JoinEUI+AppKey 且设备会重 Join。
5. gateway-bridge 区域前缀
gateway-bridge 发布到 <prefix>/gateway/{eui}/event/*,<prefix> 必须等于启用的 region_id。chirpstack-docker 默认 eu868(在 docker-compose.yml 的 INTEGRATION__MQTT__*_TOPIC_TEMPLATE),CN470 须改成子带:
INTEGRATION__MQTT__EVENT_TOPIC_TEMPLATE=cn470_0/gateway/{{ .GatewayID }}/event/{{ .EventType }} # STATE/COMMAND 同改docker compose up -d chirpstack-gateway-bridge 重建。
6. ManThink 网关切 Semtech UDP
数据链 SX130x → pfd(localhost:1661) → gwbrgmt → NS,改上行目标在 gwbrgmt 读的 gwgen_conf.json(非 pfd 的 global_conf):nsNms.nsProtocol="pkt_fwd"、nsAddress="<CHIRPSTACK_HOST>"、nsPort_up=nsPort_down=1700;改完重启 gwbrgmt(gwm_mtc 是维护进程不用动,gwgdn 看门狗会守 gwbrgmt)。网关信道须与 ChirpStack 子带一致。
⚠️
nsAddress若有前导空格会致上行 socket 解析失败、静默不发——务必去空格。
7. ThinkLink 转发桥
- Broker 连接(
forwarderEmqxBatchSaveUpdate):AS broker(type=AS,地址自动填,但options须带租户 MQTT 账密);ChirpStack broker(type=Customize,host/port 1883)。 - 脚本桥(
forwarderScriptBatchSaveUpdate):up(from=ChirpStack、topicapplication/+/device/+/event/up、to=AS);down(from=AS、topic/v32/{tenant}/as/dn/data/+、to=ChirpStack)。脚本须顶层return {topic,msg,option}——引擎把 script 包成function forwarderRunner(){<script>} forwarderRunner(),topic/msg/org_params是注入变量;若源脚本是function forwardScript(){}形式,末尾补return forwardScript({topic,msg,org_params})。 - org_params(
tenantBatchSaveUpdate读-改-写):lns.tenant、lns.chirpstack.appId(=applicationId,down 桥需要)。
8. 设备迁移(一设备一 NS)
设备真正经 ChirpStack 上行前,先 mtSdevPfMacsBatchSaveUpdate 设 enable=false 禁用 ThinkOne 档案(响应 tkoResult=true 即已同步 NS)。
9. 验证
- 上行:gateway-bridge 日志出现
event=up topic=cn470_0/gateway/{eui}/...,再endDeviceDataRecordFindPage查新遥测。 - 下行:
deviceDispatchRpc下发,订 ChirpStackapplication/+/device/+/command/down看 payload,现场看效果。
10. 排错速查
| 现象 | 根因 | 处理 |
|---|---|---|
| 收不到任何网关 UDP | 安全组只放 TCP、没放 UDP 1700 | 加 UDP 1700 |
| 网关发包被拒(非法/单字节) | 没用 Semtech UDP / 私有协议 | gwbrgmt nsProtocol=pkt_fwd |
| gwbrgmt 收 pfd 包却不上行 | nsAddress 前导空格 | 去空格 + 重启 gwbrgmt |
| 网关连上但帧被丢 | gateway-bridge 前缀(region)与设备频段不符 | 改 *_TOPIC_TEMPLATE 为正确子带 |
| ChirpStack 发 app 事件、ThinkLink 无遥测 | 桥 forwarder MQTT 订阅在重启后假死 | 重新保存该 forwarder 强制重订阅 |
| 设备上行抖动/频繁重传 | profile 误设 Class C+ADR+DevStatusReq,给电池 Class A 设备发多余下行 | profile 调 Class A、关 ADR/DevStatusReq |
| AS 数据层空但遥测正常 | 原始报文留存开关未开 | 按需开设备 real_time_storage |
来源:tkl-opt 真机联调(ManThink KS61 + 网关,2026-06-15),通用流程,凭据/密钥/地址用占位符。