RIOT unicoap 之 CoAP over Slipmux 驱动在 UART 串行链路上承载 CoAP 的传输层实现【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT导读本文聚焦 RIOT 操作系统 unicoapUnified CoAP Suite框架中的一个传输驱动——unicoap_driver_slipmuxCoAP over Slipmux。它让 CoAP 报文不再依赖 IP 网络而是通过 Slipmux 协议在 UART 串行链路上传输适合资源极度受限、仅有一根串口线的嵌入式部署。读完本文你将掌握该驱动的启用方式、依赖关系、transport.c的接收/发送调用链以及底层slipdev的组帧、转义与校验和机制。什么是 unicoap 与传输驱动transport driverunicoap是 RIOT 中统一的、模块化的 CoAP 通信框架目标是最终取代gcoap、nanocoap与nanosock见 unicoap 总览文档。它采用分层 模块化设计将“CoAP 语义”与“具体传输方式”解耦每个传输通道都是一个独立的driver驱动统称为net_unicoap_drivers见 drivers 总览。每个 driver 负责两部分工作Framing组帧把 CoAP 报文编码为该传输方式要求的帧格式Messaging model消息模型针对该传输的特性实现收发与消息处理。unicoap 已提供的传输驱动包括驱动传输介质说明unicoap_driver_udpIP/UDP常规网络传输unicoap_driver_dtlsIP/UDP DTLS加密传输unicoap_driver_slipmuxUART 串行链路本文主角走 Slipmux 协议启用 CoAP over Slipmux 驱动按照 slipmux 驱动文档在应用Makefile中指定USEMODULE unicoap_driver_slipmux但绝大多数情况下不需要手动选择该模块该模块通常由slipdev_config作为依赖自动选中你无需直接选择它。也就是说当你在应用中启用slipdev_configSLIP 网络设备 CoAP 配置帧支持时RIOT 的依赖解析系统会自动把unicoap_driver_slipmux拉入构建。这与slipdev的定位一致——slipdev是 RIOT 中通过 UART 复用传输网络数据、STDIO 与 CoAP 配置帧的 SLIP 设备见 drivers/include/slipdev.h。驱动依赖图与构建结构原文档给出了该驱动的依赖图unicoap_driver_slipmux ├── unicoap_driver_rfc7252_common │ ├── unicoap_driver_rfc7252_common_messaging │ └── unicoap_driver_rfc7252_common_pdu └── ... (operating system networking module)这张图揭示了驱动的分层复用策略unicoap_driver_rfc7252_common是 RFC 7252CoAP 标准驱动的公共实现位于 sys/net/application_layer/unicoap/drivers/rfc7252/common它进一步拆成两个子模块unicoap_driver_rfc7252_common_messagingRFC 7252 的消息层如 CON/NON、ACK、重传语义unicoap_driver_rfc7252_common_pduRFC 7252 的 PDU 编解码见 PDU 文档最后一层是操作系统网络模块——对 Slipmux 而言就是slipdev及其 UART 外设支持。这样设计的好处是Slipmux、UDP、DTLS 三个驱动共享同一套 RFC 7252 的 PDU 编解码与消息处理逻辑各自只实现“如何把字节流送上物理介质/网络”。从源码看该驱动的构建文件 Makefile 非常精简MODULE unicoap_driver_slipmux include $(RIOTBASE)/Makefile.base目录下只有两个源文件——transport.c传输实现和Makefile这印证了“驱动 传输适配层”的定位重逻辑在公共层驱动只做 I/O 对接。传输实现源码解析该驱动的核心实现位于 transport.c共四个函数全部是 unicoap 内部私有 API见 include/private.h 中对应的unicoap_init_slipmux/unicoap_deinit_slipmux声明。接收路径unicoap_slipdev_recv_handlervoid unicoap_slipdev_recv_handler(event_t *event) { slipdev_t *dev container_of(event, slipdev_t, rxevent); unicoap_endpoint_t remote { .proto UNICOAP_PROTO_SLIPMUX, .slipmux_ep dev, }; unicoap_packet_t packet { .remote remote }; while (1) { ssize_t len slipdev_coap_recv(unicoap_receiver_buffer, sizeof(unicoap_receiver_buffer), dev); if (len 0) { if (len -2) { _SLIPMUX_DEBUG(received configuration frame with bad checksum on UART(%d)\n, dev-config.uart); } if (len -1) { _SLIPMUX_DEBUG(received configuration frame is too long or too short on UART(%d)\n, dev-config.uart); } return; } unicoap_messaging_process_rfc7252(unicoap_receiver_buffer, len, false, packet); } }关键点事件驱动该函数是slipdev_t内嵌的event_t rxevent的事件回调container_of反推出设备指针。当串口收到一个完整的配置帧后slipdev 会把该事件投递到 CoAP 服务器的 event queue端点标识远端端点remote的协议被标记为UNICOAP_PROTO_SLIPMUX并以slipmux_ep直接持有slipdev_t*指针——这说明 Slipmux 传输中“对端”就是那条 UART 上的对端设备循环接收while (1)持续调用slipdev_coap_recv直到没有可用帧为止从而在一次事件唤醒中处理完所有已到达的帧错误分级-2表示校验和错误-1表示帧过长或过短详见下文slipdev_coap_recv语义收到错误后直接返回接收缓冲区unicoap_receiver_buffer由UNICOAP_DECL_RECEIVER_STORAGE_EXTERN声明引用实际大小由CONFIG_UNICOAP_PDU_SIZE_MAX决定见 include/private.h交给公共消息层成功的帧调用unicoap_messaging_process_rfc7252()声明于 include/private/messaging.h从而复用 RFC 7252 的消息处理逻辑truncated传false。发送路径unicoap_transport_sendv_slipmuxint unicoap_transport_sendv_slipmux(iolist_t *iolist, const unicoap_endpoint_t *remote) { _SLIPMUX_DEBUG(sendv: % PRIuSIZE bytes via UART(%d)\n, iolist_size(iolist), remote-slipmux_ep-config.uart); slipdev_coap_send(iolist, remote-slipmux_ep); return 0; }发送采用 RIOT 标准的iolist 分散 I/O形式上层把待发送的数据组织成iolist_t链表驱动直接把它交给slipdev_coap_send()。debug 日志会打印字节数与目标 UART 编号remote-slipmux_ep-config.uart方便在串口多路复用场景下排查问题。初始化与去初始化int unicoap_init_slipmux(event_queue_t *queue) { slipdev_coap_set_event_queue(queue); return 0; } int unicoap_deinit_slipmux(event_queue_t *queue) { (void)queue; slipdev_coap_unset_event_queue(); return 0; }初始化的核心动作是注册事件队列slipdev_coap_set_event_queue()告诉 slipdev“配置帧到达后把事件投递到哪个队列”unicoap 后台线程UNICOAP_THREAD_IDENTIFIER为unicoap随后处理。去初始化则撤销该注册。底层 slipdev组帧、转义与校验和unicoap_driver_slipmux复用的底层设备是slipdevSLIP network device见 drivers/include/slipdev.h它参照 RFC 1055SLIP 与 Slipmux draftdraft-bormann-t2trg-slipmux-03 实现。文档中注明这些模块仍在优化开发中模块名可能变化。数据帧与状态机slipdev 通过帧类型字节区分承载内容状态机包括SLIPDEV_STATE_NONE空闲、SLIPDEV_STATE_NETIP 包、SLIPDEV_STATE_STDINSTDIO、SLIPDEV_STATE_CONFIGCoAP 配置帧以及各自的_ESC转义态还有UNKNOWN、STANDBY、SLEEP等状态。Slipmux 驱动关心的正是CONFIG配置帧串口上到达的、目标为 CoAP 的帧会被写入rb_config环形缓冲区并触发rxevent事件。接收函数返回值语义slipdev_coap_recv()的返回值见 drivers/include/slipdev.h与transport.c中的错误分支一一对应返回值含义 0成功接收一帧返回帧长度字节0当前无可用帧缓冲区未被修改-1帧长度超过缓冲区大小或帧太短而不合法-2校验和FCS无效缓冲区被修改slipdev_coap_recv()负责去转义unescaping并校验 FCSslipdev_coap_send()则负责计算校验和、字节转义、组帧并最终通过 UART 发出。这一收一发的对称设计保证了串行链路上 CoAP 帧的完整性与错误可检测性。串口配置每个 slipdev 实例由slipdev_params_t描述见 drivers/include/slipdev.htypedef struct { uart_t uart; /** UART interface the device is connected to */ uint32_t baudrate; /** baudrate to use with slipdev_params_t::uart */ } slipdev_params_t;缓冲方面CONFIG_SLIPDEV_BUFSIZE默认2048U可通过CONFIG_SLIPDEV_BUFSIZE_EXP按 2 的幂展开调整文档建议若预期流量不包含满 IPv6 MTU 大小的报文可减小该值以节省内存。TX/RX 缓冲区以及配置帧专用的rb_config环形缓冲区都基于这一大小。端到端数据流小结综合以上分析CoAP over Slipmux 的完整数据通路为接收方向 UART 中断 → slipdev 状态机识别 CONFIG 帧并转义/校验 → 写入rb_config→ 投递rxevent到 event queue →unicoap_slipdev_recv_handler()循环调用slipdev_coap_recv()→unicoap_messaging_process_rfc7252()解析并交给 unicoap 消息/交换层处理。发送方向 unicoap 消息层编码 PDU →unicoap_transport_sendv_slipmux()拿到 iolist →slipdev_coap_send()计算校验和、转义、组帧 → UART 发送。使用前提与注意事项该驱动是 unicoap 框架的一部分而 unicoap 官方文档明确标注work in progress并非所有功能都已在 RIOT 中实现使用前应关注对应版本的发布说明与源码状态见 unicoap 总览文档该驱动文档同样标注 slipdev 相关模块处于开发中模块名可能变更与unicoap_driver_udp、unicoap_driver_dtls不同Slipmux 传输不经过 IP 网络其“对端”是串行链路另一端的设备因此端到端语义与消息可靠性都由 RFC 7252 公共层CON/NON、ACK、重传与 slipdev 的帧校验共同保证若希望直接使用消息 API 而不走网络也可以只引入 RFC 7252 的 PDU 子模块unicoap_driver_rfc7252_common_pdu这与本文驱动的公共层复用思路一致。延伸阅读unicoap 框架总览分层架构与快速上手unicoap 驱动总览各传输驱动的选型建议RFC 7252 公共层PDU 编解码子模块slipdev 头文件SLIP 设备、配置参数与收发函数完整声明传输实现源码本文剖析的四个核心函数。【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考