Skip to content

CoAP 设备接入 ​

名尚智联实现了 RFC 7252 精简子集 的 CoAP Server(端口 5683),用于低功耗、UDP 受限的物联网设备接入。不依赖 Californium,协议栈完全自研。


一、协议支持 ​

能力支持
CON / NON / ACK / RST✅
GET / POST / PUT / DELETE✅(仅用 POST)
Confirmable Ping✅(空 CON → 空 ACK)
选项 delta/len 扩展编码(13/14)✅
URI-Path / Content-Format / Max-Age✅
Blockwise Transfer❌(暂不支持)
DTLS / OSCORE❌(暂不支持)

二、接入流程 ​

1. POST /dev/{serial}
   Authorization: Bearer {authCode}
   Content-Format: application/json

   Payload: {"props":{"temperature":25.6,"humidity":62.3}}

2. 服务端认证
   → serial + authCode 查 ms_device 表
   → 支持主库 + tenant_* 动态库全局检索

3. 返回
   2.04 Changed → 成功
   4.00 Bad Request → payload 解析失败
   4.01 Unauthorized → 认证失败
   4.04 Not Found → 路径非法
   4.05 Method Not Allowed → 非 POST

三、命令行测试(coap-cli) ​

bash
# 安装
npm install -g coap-cli

# 上报属性
coap post coap://your-server:5683/dev/ESP32-001 \
  -H "Authorization: Bearer <authCode>" \
  -H "Content-Format: application/json" \
  -d '{"props":{"temperature":25.6,"humidity":62.3}}'

# → 2.04 Changed

四、Python 测试脚本 ​

python
import socket
import struct
import json

def build_coap_post(uri_path: str, auth_token: str, payload: bytes) -> bytes:
    """构建 CoAP CON POST 请求(4 字节首部 + Token + 选项 + payload)"""
    version = 1
    msg_type = 0          # CON
    code = 0x02           # 2.01 Created
    mid = struct.unpack('>H', __import__('os').urandom(2))[0]
    token = b'\x00'       # 可选,空 Token
    token_len = len(token)

    # 选项:URI-Path + Content-Format + Authorization
    # delta/len 编码参考 RFC 7252 第 3 节
    options = bytearray()
    # URI-Path = 11,Content-Format = 12,Authorization = 2048
    uri_path = uri_path.encode()
    cf = struct.pack('>H', 50)   # application/json
    auth = auth_token.encode()

    # 简化版:只用 URI-Path
    # 实际生产需完整选项编码,此处示意
    ...

    header = bytearray([
        (version << 6) | (msg_type << 4) | token_len,
        code,
        mid >> 8, mid & 0xFF
    ])
    return bytes(header + token + options + b'\xff' + payload)

def parse_coap_response(data: bytes):
    """解析 CoAP 响应"""
    first_byte = data[0]
    ver = (first_byte >> 6) & 0x03
    typ = (first_byte >> 4) & 0x03
    tkl = first_byte & 0x0f
    code = data[1]
    mid = (data[2] << 8) | data[3]
    # code 拆解
    cls = code >> 5
    detail = code & 0x1f
    return {"ver": ver, "type": typ, "code": f"{cls}.{detail:02d}", "mid": mid}

# 发送
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(5)
msg = build_coap_post("/dev/ESP32-001", "<authCode>",
                      json.dumps({"props": {"temperature": 25.6}}).encode())
sock.sendto(msg, ("your-server.com", 5683))

try:
    resp, _ = sock.recvfrom(4096)
    print(parse_coap_response(resp))
except socket.timeout:
    print("超时未收到 ACK")

五、响应码对照 ​

CoAP Code十进制含义
2.040x44成功(Changed)
4.000x80Payload 格式错误
4.010x81认证失败(serial / authCode 不匹配)
4.040x84路径非法(非 /dev/{serial})
4.050x85方法不允许(非 POST)
0.000x00空 CON(Ping,正常用于保活)

六、设备状态维护 ​

  • 首次连接:设备通过认证后自动在服务端创建会话(TTL 10 分钟)
  • 会话复用:同 socket 的后续请求自动命中会话,无需重复认证
  • 离线检测:超过 reader-idle-seconds(默认 300)未收到数据则标记离线

七、服务端配置 ​

yaml
ms:
  coap:
    enabled: true
    port: 5683
  netty:
    udp:
      enabled: true     # CoAP 复用 UDP Netty 通道
      port: 11001       # 全局 UDP 端口(CoAP 用独立 5683)

💡 CoAP 端口 5683 独立于全局 UDP 端口 11001,由 ms.netty.udp.enabled 门控 + ms.coap.enabled 独立开关控制。