一个人用业余时间写的库,凭什么统治了整个嵌入式行业
一个人用业余时间写的库,凭什么统治了整个嵌入式行业
优秀的 C 库有一种共同气质:代码量克制,职责单一,集成毫不费力。它们往往由一位深思熟虑的工程师用业余时间写出,却在十年后成为整个行业的基础设施。
一、多任务处理 / 操作系统内核
1. FreeRTOS — 嵌入式世界里最广泛使用的实时操作系统内核
一句话:内核只有三个 C 文件,每 170 秒被下载一次,是事实上的嵌入式 RTOS 标准。
背景:2003 年,英国工程师 Richard Barry 在自己的公司 Real Time Engineers Ltd. 开发了 FreeRTOS,目标是写一个"足够小、足够简单"的抢占式调度器,让任何工程师都能在一天内上手。他坚持用纯 C 编写(仅少量汇编用于上下文切换),并亲自维护 30 余个平台的移植代码长达 14 年。2017 年,Amazon Web Services 接管项目并转为 MIT 许可,Richard Barry 继续在 AWS 团队工作。
作者:Richard Barry(英国,实时系统专业一等荣誉学士,荣誉博士)
代码位置:https://github.com/FreeRTOS/FreeRTOS-Kernel — 内核核心文件:tasks.c、queue.c、list.c 三个文件加若干头文件;各平台 port 独立存放。
许可证:MIT
适用场景:Cortex-M、RISC-V、ESP32、STM32 等平台的多任务嵌入式固件;需要任务调度、队列、互斥量、软件定时器的裸机项目;IoT 设备固件。
/* 典型用法:创建两个任务并启动调度器 */
#include "FreeRTOS.h"
#include "task.h"
void vTask1(void *pvParam) {
for (;;) {
printf("Task1 running\n");
vTaskDelay(pdMS_TO_TICKS(500));
}
}
void vTask2(void *pvParam) {
for (;;) {
printf("Task2 running\n");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
int main(void) {
xTaskCreate(vTask1, "T1", 128, NULL, 1, NULL);
xTaskCreate(vTask2, "T2", 128, NULL, 2, NULL);
vTaskStartScheduler();
for (;;); /* 永远不应到达这里 */
}
优点:移植层设计优雅,新平台移植通常只需实现 7 个函数;支持静态内存分配,无需堆;文档和社区极为完善;AWS 背书,长期维护有保障。
缺点:内核本身功能精简,TCP/IP、文件系统等需要额外引入(FreeRTOS-Plus 系列);动态内存管理有 5 种方案(heap_1 到 heap_5),初学者选型容易困惑;相比 Zephyr 等现代 RTOS,功能和工具链支持稍显保守。
2. Protothreads — 无栈协程,200 行 C 代码实现多任务
一句话:用两个头文件和宏魔法,在无 RTOS 的裸机上实现协作式多任务,内存占用接近于零。
背景:2006 年,瑞典计算机科学家 Adam Dunkels(也是 lwIP 和 uIP 的作者)在研究极度内存受限的传感器网络时,提出了 Protothread 的概念。他发现大多数嵌入式任务并不需要独立的调用栈,只需要一个"恢复点"——于是利用 C 语言中 switch 语句的 Duff's Device 技巧,用宏实现了可以"在任意位置 yield"的协程,整个实现只有约 100 行。
作者:Adam Dunkels(瑞典计算机科学家,SICS 研究院)
代码位置:http://dunkels.com/adam/pt / https://github.com/gburd/pt — 核心文件:pt.h、pt-sem.h,共约 200 行。
许可证:BSD
适用场景:8 位/16 位 MCU(ATmega、PIC)等无法运行 RTOS 的场合;只有几百字节 RAM 的极限场景;用事件驱动代替忙等待,同时保持线性代码风格。
#include "pt.h"
static struct pt pt1, pt2;
PT_THREAD(producer(struct pt *pt)) {
PT_BEGIN(pt);
while (1) {
PT_WAIT_UNTIL(pt, buffer_empty());
fill_buffer();
PT_YIELD(pt);
}
PT_END(pt);
}
PT_THREAD(consumer(struct pt *pt)) {
PT_BEGIN(pt);
while (1) {
PT_WAIT_UNTIL(pt, buffer_full());
consume_buffer();
PT_YIELD(pt);
}
PT_END(pt);
}
/* 主循环轮询所有 protothread */
int main(void) {
PT_INIT(&pt1); PT_INIT(&pt2);
for (;;) {
producer(&pt1);
consumer(&pt2);
}
}
优点:每个线程零栈开销(局部变量需用 static),内存占用极端低;无需 RTOS,完全裸机;代码可读性远好于手写状态机。
缺点:局部变量必须声明为 static,否则跨 yield 会丢失;无法在 switch 内部使用 PT_WAIT;本质是协作式,没有抢占,一个任务阻塞会拖住全局。
二、网络通信
3. lwIP — 嵌入式 TCP/IP 协议栈的事实标准
一句话:Adam Dunkels 在博士论文中写出的 TCP/IP 栈,几 KB RAM 就能跑完整的 IP/TCP/UDP/DHCP/DNS。
背景:2001 年,Adam Dunkels 在瑞典计算机科学研究所读博期间,为了在内存只有几十 KB 的传感器节点上运行 TCP/IP,从零实现了 lwIP(lightweight IP)。他的论文《Design and Implementation of the lwIP TCP/IP Stack》成为嵌入式网络领域的经典文献。lwIP 随后被 ST、Espressif、NXP 等所有主流 MCU 厂商的 SDK 内置,是目前嵌入式领域使用最广的 TCP/IP 栈。
作者:Adam Dunkels(瑞典,SICS/RISE Research Institutes)
代码位置:https://github.com/lwip-tcpip/lwip — 核心约 20 个 C 文件,分层清晰(core/、api/、netif/)。
许可证:BSD
适用场景:STM32+LAN8742 以太网;ESP32/W5500 TCP 通信;需要完整 TCP/IP 而不想上 Linux 的 MCU 项目;RTOS 和裸机均可(raw API / callback API)。
/* RAW API:非阻塞回调风格 */
#include "lwip/tcp.h"
static err_t on_recv(void *arg, struct tcp_pcb *pcb,
struct pbuf *p, err_t err) {
if (p == NULL) { tcp_close(pcb); return ERR_OK; }
/* 回显数据 */
tcp_write(pcb, p->payload, p->len, TCP_WRITE_FLAG_COPY);
pbuf_free(p);
return ERR_OK;
}
static err_t on_accept(void *arg, struct tcp_pcb *newpcb, err_t err) {
tcp_recv(newpcb, on_recv);
return ERR_OK;
}
void start_echo_server(void) {
struct tcp_pcb *pcb = tcp_new();
tcp_bind(pcb, IP_ADDR_ANY, 7);
pcb = tcp_listen(pcb);
tcp_accept(pcb, on_accept);
}
优点:RAM 需求低至 40KB,Flash 约 40KB;BSD 许可,无商业授权顾虑;厂商支持完善,移植板级驱动通常只需实现一个网卡 netif;RAW API 无堆开销,适合资源极限场合。
缺点:RAW API 的回调编程模型对初学者不友好;Netconn/Socket API 需要 RTOS;在高并发连接场景下性能和 Linux 差距明显;调试较困难,状态机内部复杂。
4. Mongoose — 两个文件实现完整嵌入式 Web Server + 网络库
一句话:
mongoose.c + mongoose.h,内置 HTTP/WebSocket/MQTT/TCP/UDP,可直接运行在 MCU 裸机上,无需操作系统网络栈。
背景:2004 年,爱尔兰软件工程师 Sergey Lyubka 开始开发 Mongoose,最初目标是为嵌入式设备提供一个轻量 HTTP 服务器。他持续投入数千小时,从 HTTP 扩展到 WebSocket、MQTT、TLS,随着 IoT 时代到来,功能日益完善。2013 年以 Mongoose 为基础创立了 Cesanta 公司。目前已被 NASA 国际空间站使用,是嵌入式 Web 开发的热门选择。
作者:Sergey Lyubka(爱尔兰,Cesanta 联合创始人兼技术总监)
代码位置:https://github.com/cesanta/mongoose — 核心 mongoose.c + mongoose.h 两个文件,内含自己的 TCP/IP 栈实现。
许可证:GPLv2 / 商业双授权
适用场景:工业设备 Web 配置界面;IoT 设备本地 HTTP REST API;嵌入式 MQTT 客户端;OTA 固件升级服务;STM32、ESP32、Raspberry Pi 等平台的网络应用。
#include "mongoose.h"
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = ev_data;
if (mg_match(hm->uri, mg_str("/api/temp"), NULL)) {
mg_http_reply(c, 200,
"Content-Type: application/json\r\n",
"{\"temp\":%.1f}\n", read_temperature());
} else {
mg_http_reply(c, 404, "", "Not found\n");
}
}
}
int main(void) {
struct mg_mgr mgr;
mg_mgr_init(&mgr);
mg_http_listen(&mgr, "http://0.0.0.0:8080", fn, NULL);
for (;;) mg_mgr_poll(&mgr, 100);
}
优点:两文件无依赖,内置 TCP/IP 栈,无需操作系统网络层;支持 TLS、WebSocket、MQTT;官方提供 STM32/ESP32/FreeRTOS 完整示例。
缺点:开源版 GPLv2,商业闭源需购买授权;mongoose.c 已达数万行,远不如最初"小巧";高并发场景性能不如 libuv/Nginx。
5. libmodbus — 工业现场最常用的 Modbus 通信库
一句话:覆盖 Modbus RTU(串口/RS485)和 Modbus TCP 的 C 库,API 统一,4 个核心源文件,Linux 工控必备。
背景:法国工程师 Stéphane Raimbault 从 2001 年开始维护 libmodbus,最初是为了解决工业自动化项目中 Modbus 通信的痛点:各厂商实现互不兼容,调试困难。他设计了一套后端抽象层,让 RTU 和 TCP 共享同一套读写 API,极大简化了上层代码。libmodbus 目前是 Linux 工控领域使用最广泛的 Modbus 实现。
作者:Stéphane Raimbault(法国)
代码位置:https://github.com/stephane/libmodbus — 核心文件:modbus.c、modbus-rtu.c、modbus-tcp.c、modbus-data.c,共约 4 个源文件。
许可证:LGPL v2.1+
适用场景:Linux 工控机读写 PLC/变频器/仪表(RS485 Modbus RTU);通过以太网采集 Modbus TCP 设备数据;工业网关协议转换;SCADA 系统数据采集。
#include <modbus.h>
/* Modbus RTU:读取从机 1 号的保持寄存器 */
modbus_t *ctx = modbus_new_rtu("/dev/ttyS0", 9600, 'N', 8, 1);
modbus_set_slave(ctx, 1);
modbus_connect(ctx);
uint16_t regs[10];
int rc = modbus_read_registers(ctx, 0, 10, regs);
if (rc == -1) fprintf(stderr, "%s\n", modbus_strerror(errno));
modbus_close(ctx);
modbus_free(ctx);
优点:RTU/TCP 统一 API,切换后端只改一行;错误处理遵循 POSIX errno 惯例;线程安全;LGPL 许可,商业使用无顾虑(动态链接)。
缺点:不支持 Modbus ASCII;不直接支持裸机 MCU(需要 OS 或 RTOS);对 RS485 的 DE/RE 引脚控制需自行处理;不支持 Modbus RTU Over TCP 的混合场景。
三、数据存储 / 文件系统
6. SQLite — 从战舰上诞生的世界最广泛嵌入式数据库
一句话:一个 C 文件实现完整 SQL 数据库引擎,无服务器,无配置,被安装在地球上超过一万亿个设备上。
背景:2000 年春,D. Richard Hipp 为美国海军导弹驱逐舰 USS Oscar Austin 开发软件,Informix 数据库服务器频繁掉线让他深感挫败。恰逢政府合同短暂停摆,他利用这段空闲,凭借编译器开发经验,用字节码引擎从头实现了 SQLite。2001 年 Motorola 打来电话要在新手机操作系统里使用它——那是他第一次意识到开源软件可以变现。此后 Symbian、Android、iOS、Firefox 相继采用,SQLite 成为有史以来安装数量最多的数据库。
作者:D. Richard Hipp(美国,杜克大学计算机科学博士,Hwaci 公司创始人)
代码位置:https://github.com/sqlite/sqlite — 发行版提供合并文件 sqlite3.c + sqlite3.h(约 20 万行,单文件合并版)。
许可证:Public Domain(完全公有领域)
适用场景:嵌入式 Linux 设备本地结构化数据存储;MCU 数据记录(有足够 Flash);替代配置文件存储复杂结构化数据;需要 ACID 事务保证的本地存储。
#include "sqlite3.h"
sqlite3 *db;
sqlite3_open("sensor.db", &db);
sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS log"
"(ts INTEGER, val REAL);", NULL, NULL, NULL);
sqlite3_exec(db, "INSERT INTO log VALUES(1700000000, 25.3);",
NULL, NULL, NULL);
sqlite3_close(db);
优点:公有领域无任何授权限制;经过极端严苛的测试(测试代码量是源码的 600 倍);被 NASA、Airbus 等高可靠性场合使用;断电安全(WAL 模式)。
缺点:写并发只支持单写,不适合高并发写入;sqlite3.c 合并文件超 20 万行,阅读源码门槛高;对 RAM 有一定需求,不适合 < 64KB RAM 的裸机 MCU。
7. FatFs — 嵌入式 FAT/exFAT 文件系统的标准实现
一句话:ChaN 用纯 ANSI C 写的 FAT 文件系统模块,完全独立于存储硬件,SD 卡/Flash/RAM Disk 通用,代码只有约 5 个文件。
背景:FatFs 由日本电子爱好者 ChaN(真名未公开)作为个人项目开发,从最初支持 FAT12/16,到后续增加 FAT32、exFAT、LFN、多卷等特性,始终保持了极简的代码风格和完整的 ANSI C89 兼容性。ChaN 以匿名状态维护此项目多年,只留下网站 http://elm-chan.org。FatFs 被 ST、ARM Mbed、Zephyr 等主流平台官方集成,是嵌入式 FAT 实现的事实标准。
作者:ChaN(日本,个人开发者,身份匿名)
代码位置:http://elm-chan.org/fsw/ff/ — 核心文件:ff.c、ff.h、ffconf.h、diskio.h,用户实现 diskio.c 适配存储驱动。
许可证:FatFs 自定义许可(类 BSD,允许商业使用)
适用场景:STM32 + SD 卡数据记录;MCU + SPI Flash 文件存储;嵌入式设备读写 U 盘(USB MSC);需要与 Windows/Linux 文件交换的嵌入式场合。
#include "ff.h"
FATFS fs;
FIL fil;
UINT bw;
/* 挂载 SD 卡,写入数据 */
f_mount(&fs, "", 0);
f_open(&fil, "log.csv", FA_WRITE | FA_CREATE_ALWAYS);
f_write(&fil, "time,temp\r\n", 10, &bw);
f_printf(&fil, "%u,%.2f\r\n", get_timestamp(), 25.3f);
f_close(&fil);
优点:完全独立于存储硬件,只需实现 disk_read/write/ioctl 三个函数;支持 RTOS 多线程(可配);支持多卷、LFN(长文件名)、exFAT;代码极为稳健,已验证数十年。
缺点:FAT 本身不适合频繁小写(磨损和碎片问题);不支持日志(掉电可能丢数据,需应用层保护);ffconf.h 配置项繁多,初次集成需要仔细阅读文档。
8. miniz — 单文件 zlib/DEFLATE/ZIP 压缩库
一句话:miniz.c + miniz.h 两个文件,完整兼容 zlib API,同时支持 ZIP 文件读写,无需安装 zlib。
背景:Rich Geldreich 是游戏行业资深工程师,他的压缩代码曾用在《光晕》《帝国时代》《极限竞速》等 Xbox 游戏中。他研究 DEFLATE 算法纯属兴趣,2010 年前后将多年积累浓缩成 miniz,最初发布在 Google Code,后迁移至 GitHub。他在博客写道:"写 miniz 的动机之一是为了学透 zlib API,结果写出的东西比 zlib 更容易集成。"
作者:Rich Geldreich(前 Valve 软件工程师,游戏图形与压缩技术专家)
代码位置:https://github.com/richgel999/miniz — 发布包为 miniz.c + miniz.h 两个文件(由多个源文件合并生成)。
许可证:MIT
适用场景:嵌入式 OTA 固件包解压;替代 zlib 避免复杂构建依赖;需要读写 ZIP 文件的嵌入式 Linux 应用;游戏/MCU 资源包压缩。
#include "miniz.h"
uint8_t src[] = "Hello, Embedded! Hello, Embedded! Hello!";
size_t src_len = sizeof(src);
size_t cmp_len = compressBound(src_len);
uint8_t *cmp = malloc(cmp_len);
compress(cmp, &cmp_len, src, src_len);
printf("原始 %zu → 压缩后 %zu 字节\n", src_len, cmp_len);
uint8_t out[128]; size_t out_len = sizeof(out);
uncompress(out, &out_len, cmp, cmp_len);
free(cmp);
优点:API 与 zlib 几乎完全兼容,迁移零成本;单文件无依赖,集成比 zlib 简单;低层 API 不使用堆,可用于裸机。
缺点:压缩率不如 LZMA;ZIP 不支持加密;文档偏少;miniz.c 单文件较大(约 225KB),编译时间稍长。
四、数据格式 / 序列化
9. cJSON — 最小可用的 ANSI C JSON 解析器
一句话:cJSON.c + cJSON.h 两个文件,C89 写成,秉持"能用就行"哲学,是嵌入式 JSON 处理的首选。
背景:2009 年 8 月,Dave Gamble 受 http://json.org 上"JSON 就像 XML,但去掉了所有脂肪"这句话的启发,立志写一个"同样哑到极致"的解析器。他在 README 里写道:"cJSON 的目标是成为你能完成工作的最蠢的 parser。" 这种务实哲学使它在嵌入式和服务端社区迅速流行,目前由 Max Bruckner 和 Alan Wang 维护。
作者:Dave Gamble(2009 年原始版本),现由社区维护
代码位置:https://github.com/DaveGamble/cJSON
核心只有 cJSON.c 和 cJSON.h。
许可证:MIT
适用场景:嵌入式设备与云端的 JSON 数据交换;MCU 串口/网络协议解析;需要在 C89 编译器下运行的项目。
#include "cJSON.h"
/* 解析 */
cJSON *root = cJSON_Parse("{\"temp\":25.3,\"unit\":\"C\"}");
double temp = cJSON_GetObjectItem(root, "temp")->valuedouble;
/* 构造 */
cJSON *resp = cJSON_CreateObject();
cJSON_AddNumberToObject(resp, "status", 0);
char *out = cJSON_PrintUnformatted(resp);
/* 使用 out... */
cJSON_free(out);
cJSON_Delete(resp);
cJSON_Delete(root);
优点:两文件零依赖;C89 兼容;MIT 许可无顾虑;链式 API 直观。
缺点:大量 malloc/free,不适合无堆裸机;解析速度一般;无 JSON Schema 验证。
10. jsmn — 零动态分配、单头文件 JSON Tokenizer
一句话:一个 jsmn.h 头文件,不分配任何内存,把 JSON 字符串切成 Token 数组,MCU 裸机可直接使用。
背景:2010 年,俄罗斯工程师 Serge Zaitsev(网名 zserge)在研究极端内存受限的嵌入式 JSON 处理时,发现现有 JSON 库都需要 malloc 建立树结构,于是另辟蹊径:不建树,不分配内存,只记录每个 JSON Token 的起止位置,让调用者自己取值。这个思路使 jsmn 可以运行在只有几百字节 RAM 的设备上,并被 AWS FreeRTOS 等项目选用为内置 JSON 库。
作者:Serge Zaitsev(俄罗斯,zserge)
代码位置:https://github.com/zserge/jsmn — 单头文件 jsmn.h,约 300 行。
许可证:MIT
适用场景:MCU 裸机解析 MQTT JSON payload;内存极度受限(< 4KB RAM)场景;只需提取少量字段,不需要完整 DOM 树的场合。
#include "jsmn.h"
const char *json = "{\"temp\":25,\"humi\":60}";
jsmntok_t tokens[16];
jsmn_parser p;
jsmn_init(&p);
int r = jsmn_parse(&p, json, strlen(json), tokens, 16);
/* tokens[0] = 整个 Object, tokens[1] = "temp", tokens[2] = 25 ... */
/* 取值:使用 tokens[i].start/end 切出子串,自己 atoi/atof */
for (int i = 1; i < r; i += 2) {
printf("key=%.*s val=%.*s\n",
tokens[i].end - tokens[i].start, json + tokens[i].start,
tokens[i+1].end - tokens[i+1].start, json + tokens[i+1].start);
}
优点:零动态内存,Token 数组由调用者提供;单头文件,直接 #include;速度极快(只做线性扫描)。
缺点:只做 tokenize,不做 DOM,取值需要手动字符串比较;不验证 JSON 合法性;嵌套层次深的 JSON 处理代码冗长。
11. nanopb — 为 MCU 量身定制的 Protocol Buffers 实现
一句话:Google Protobuf 的嵌入式 C 实现,ROM < 10KB,RAM < 1KB,三个 C 文件,支持静态内存分配。
背景:芬兰工程师 Petteri Aimonen 在使用 Protobuf 做嵌入式项目时,发现官方实现完全不适合 MCU(需要 C++ 和动态内存)。他从 2011 年开始开发 nanopb,设计核心决策是:从 .proto 文件生成静态大小的 C 结构体,编解码时不做任何动态分配。nanopb 现已被 Google 自己在嵌入式项目中使用,并收入了 Arduino、PlatformIO 等生态。
作者:Petteri Aimonen(芬兰)
代码位置:https://github.com/nanopb/nanopb
运行时库:pb_encode.c、pb_decode.c、pb_common.c 三个文件;代码生成器为 Python 脚本。
许可证:zlib
适用场景:MCU 与服务器之间的结构化二进制通信;替代自定义二进制协议;需要跨平台/跨语言兼容的嵌入式通信协议;BLE、UART、MQTT payload 序列化。
/* message Sensor { float temp = 1; uint32 id = 2; } */
#include "sensor.pb.h"
#include "pb_encode.h"
Sensor msg = Sensor_init_zero;
msg.temp = 25.3f;
msg.id = 42;
uint8_t buf[64];
pb_ostream_t stream = pb_ostream_from_buffer(buf, sizeof(buf));
pb_encode(&stream, Sensor_fields, &msg);
/* stream.bytes_written 是编码后的字节数 */
uart_send(buf, stream.bytes_written);
优点:零动态分配,所有大小在编译期确定;比 JSON/XML 编码紧凑 3-10 倍;与所有官方 Protobuf 语言互通;支持流式编解码,可直接对接 UART 缓冲。
缺点:需要 Python + protoc 工具链生成代码,构建系统集成稍复杂;动态长度字段(string、bytes)需要在 .options 里指定最大长度;调试时二进制数据不可读。
五、配置与 INI 解析
12. inih — 嵌入式首选的 INI 配置文件解析器
一句话:ini.c + ini.h 两个文件,SAX 风格逐行回调,无堆分配可选,专为嵌入式设计。
背景:2009 年,新西兰开发者 Ben Hoyt(Python os.scandir() 的作者)在嵌入式项目中找不到合适的 INI 解析库,用周末时间写了 inih(INI Not Invented Here)。核心决策是 SAX 风格——逐行读取并回调,从不在内存中建树,天然适合低内存设备。持续维护至今超过 15 年。
作者:Ben Hoyt(新西兰,现就职于 Canonical)
代码位置:https://github.com/benhoyt/inih
ini.c + ini.h,约 300 行。
许可证:BSD-3-Clause
适用场景:嵌入式 Linux 设备(路由器、工控机)配置文件读取;MCU 从 SD 卡读取 INI 配置;需要与 Python ConfigParser 格式兼容的场景。
#include "ini.h"
typedef struct { char ssid[64]; int port; } Config;
static int handler(void *u, const char *sec,
const char *name, const char *val) {
Config *c = u;
if (!strcmp(sec,"wifi") && !strcmp(name,"ssid"))
strncpy(c->ssid, val, sizeof(c->ssid));
else if (!strcmp(sec,"server") && !strcmp(name,"port"))
c->port = atoi(val);
return 1;
}
Config cfg = {0};
ini_parse("config.ini", handler, &cfg);
优点:回调方式不建堆,逐行读取内存极小;可通过宏禁用动态分配;支持注释、多行值、UTF-8 BOM;活跃维护超 15 年。
缺点:SAX 风格不如 DOM 直观,随机访问某个 key 不方便;不支持嵌套 section;复杂配置结构建议用 TOML 替代。
六、安全 / 加密
13. Mbed TLS — 嵌入式 TLS/SSL 和密码学库
一句话:前身 PolarSSL,ARM 旗下的嵌入式 TLS 实现,模块化设计,可单独使用 AES/SHA/ECC 等算法,无需完整 TLS 栈。
背景:PolarSSL 由荷兰开发者 Paul Bakker 于 2006 年创建,目标是提供一个比 OpenSSL 更小、更易于嵌入式使用的 SSL/TLS 库。2015 年 ARM 收购 PolarSSL 并更名为 Mbed TLS,纳入 ARM Mbed 生态。目前被 FreeRTOS、lwIP、Mongoose、ESP-IDF 等主流嵌入式框架官方集成,是嵌入式 TLS 领域的事实标准。
作者:Paul Bakker(荷兰)创建,现由 ARM/Mbed 团队维护
代码位置:https://github.com/Mbed-TLS/mbedtls — 核心约 30 余个 C 文件,按功能分层(library/),可按需裁剪。
许可证:Apache 2.0 / GPL 2.0 双许可
适用场景:MCU HTTPS 客户端(REST API 通信);MQTT over TLS;证书验证和 PKI 操作;AES/SHA/ECDSA 等密码学原语单独使用;IoT 设备安全固件更新。
#include "mbedtls/aes.h"
/* 单独使用 AES-128-CBC 加密 */
mbedtls_aes_context aes;
mbedtls_aes_init(&aes);
uint8_t key[16] = {0}; /* 实际应使用真随机密钥 */
uint8_t iv[16] = {0};
uint8_t plain[16] = "Hello, MCU! ";
uint8_t cipher[16] = {0};
mbedtls_aes_setkey_enc(&aes, key, 128);
mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_ENCRYPT,
16, iv, plain, cipher);
mbedtls_aes_free(&aes);
优点:模块化极佳,可单独编译某一算法;配置文件 mbedtls_config.h 可精确裁剪到最小;Apache 2.0 许可商业友好;文档和移植指南完善。
缺点:完整 TLS 栈代码量较大(约 300KB Flash);TLS 握手 RAM 占用较高(约 30-50KB),小 MCU 需仔细裁剪;性能不如硬件加速方案。
七、接口通信协议
14. libserialport — 跨平台串口(UART)访问库
一句话:sigrok 项目出品,4 个核心文件,统一封装 Linux/Windows/macOS 串口 API,彻底告别 termios 的繁琐。
背景:libserialport 是 sigrok(开源示波器/逻辑分析仪软件)项目的衍生产物。sigrok 需要在多平台访问串口设备(USB 转串口、RS232 等),但各平台 API 差异巨大(Linux termios,Windows CreateFile/DCB,macOS IOKit)。开发团队于 2013 年将这层抽象剥离为独立库。
作者:sigrok 项目组(Martin Ling 等)
代码位置:https://github.com/sigrokproject/libserialport — 核心文件:serialport.c、linux.c、windows.c、macosx.c(平台 + 公共层),共约 4 个源文件。
许可证:LGPL v3
适用场景:嵌入式 Linux 上读写 RS232/RS485 设备;跨平台调试工具开发;PC 端与 MCU 串口通信的上位机;USB 转串口设备管理。
#include <libserialport.h>
struct sp_port *port;
sp_get_port_by_name("/dev/ttyUSB0", &port);
sp_open(port, SP_MODE_READ_WRITE);
sp_set_baudrate(port, 115200);
sp_set_bits(port, 8);
sp_set_parity(port, SP_PARITY_NONE);
sp_set_stopbits(port, 1);
/* 发送数据 */
const char *cmd = "AT\r\n";
sp_blocking_write(port, cmd, strlen(cmd), 1000);
/* 读取响应 */
char buf[64] = {0};
sp_blocking_read(port, buf, sizeof(buf)-1, 1000);
printf("Response: %s\n", buf);
sp_close(port);
sp_free_port(port);
优点:一套 API 覆盖 Linux/Windows/macOS,移植成本极低;自动枚举系统串口,无需硬编码设备路径;支持阻塞/非阻塞/超时读写;LGPL 许可,商业使用友好(动态链接)。
缺点:不支持 MCU 裸机(仅限有 OS 的平台);LGPL v3(需注意静态链接的许可合规);不支持 RS485 半双工 DE/RE 引脚控制(需额外 ioctl)。
八、工具 / 通用基础库
15. libuv — Node.js 底层的跨平台异步 I/O 库
一句话:Node.js 运行时的心脏,用纯 C 统一封装 epoll/kqueue/IOCP,让跨平台异步 I/O 像写同步代码一样清晰。
背景:Node.js 早期使用 libev 做事件循环,但 libev 不支持 Windows。2011 年,Node.js 核心团队(Bert Belder、Ben Noordhuis 等)重写了跨平台抽象层,命名为 libuv,同年随 Node.js v0.6 发布。此后 libuv 迅速独立,被 Julia 语言、uvloop(Python asyncio 加速后端)、Luvit 等项目采用。
作者:Node.js 核心团队(Bert Belder、Ben Noordhuis 等),现由 libuv 社区维护
代码位置:https://github.com/libuv/libuv — 约 40 余个 C 源文件,平台抽象层清晰分离(src/unix/、src/win/)。
许可证:MIT
适用场景:嵌入式 Linux 上的高并发网络服务(工业网关、边缘计算节点);需要异步文件 I/O 的嵌入式 Linux 程序;替代 select/epoll 的跨平台网络应用;守护进程、TCP 服务端开发。
#include <uv.h>
#include <stdio.h>
void on_timer(uv_timer_t *handle) {
static int count = 0;
printf("心跳 #%d\n", ++count);
if (count >= 5) uv_stop(handle->loop);
}
int main(void) {
uv_loop_t *loop = uv_default_loop();
uv_timer_t timer;
uv_timer_init(loop, &timer);
uv_timer_start(&timer, on_timer, 0, 1000);
uv_run(loop, UV_RUN_DEFAULT);
uv_loop_close(loop);
return 0;
}
优点:久经考验(Node.js 数亿设备验证);跨平台一致性极佳;支持 TCP、UDP、管道、文件 I/O、子进程、信号全家桶;MIT 许可无顾虑。
缺点:源文件较多(约 40 个),集成需要 CMake 或 Makefile;回调嵌套在复杂逻辑下容易形成"回调地狱";不适合裸机 MCU(需要操作系统)。
速查索引
| # | 库 | 核心文件数 | 领域 | 许可证 | 典型平台 |
| 1 | FreeRTOS | 3(内核) | 多任务/RTOS | MIT | 所有 MCU |
| 2 | Protothreads | 2(头文件) | 协程/多任务 | BSD | 8/16/32 位 MCU |
| 3 | lwIP | ~20 | TCP/IP 网络 | BSD | MCU + RTOS |
| 4 | Mongoose | 2 | HTTP/MQTT 网络 | GPLv2/商业 | MCU / 嵌入式 Linux |
| 5 | libmodbus | 4 | Modbus 工业通信 | LGPL 2.1 | 嵌入式 Linux |
| 6 | SQLite | 2(合并版) | 关系数据库 | Public Domain | 嵌入式 Linux |
| 7 | FatFs | 4 | FAT 文件系统 | FatFs License | 所有 MCU |
| 8 | miniz | 2(合并版) | 压缩/解压/ZIP | MIT | MCU / Linux |
| 9 | cJSON | 2 | JSON 解析 | MIT | MCU / Linux |
| 10 | jsmn | 1(头文件) | JSON Tokenize | MIT | 裸机 MCU |
| 11 | nanopb | 3 | Protobuf 序列化 | zlib | MCU |
| 12 | inih | 2 | INI 配置解析 | BSD-3 | MCU / Linux |
| 13 | Mbed TLS | ~30 | TLS/密码学 | Apache 2.0 | MCU / Linux |
| 14 | libserialport | 4 | UART 串口访问 | LGPL 3 | 嵌入式 Linux |
| 15 | libuv | ~40 | 异步 I/O 事件 | MIT | 嵌入式 Linux |
1352

被折叠的 条评论
为什么被折叠?



