单片机如何接收JSON数据:从原理到实践
在物联网、智能设备等应用场景中,单片机作为核心控制器,常需与上位机、服务器或其他设备进行数据交互,JSON(JavaScript Object Notation)因其轻量、易读、跨语言兼容等优势,成为数据交换的主流格式之一,单片机资源有限(如RAM小、处理能力弱),接收JSON数据需结合硬件特性和软件优化,本文将从JSON数据特点出发,详解单片机接收JSON的原理、方法及实践步骤,并提供关键代码示例。
JSON数据特点与单片机接收的挑战
JSON是一种基于文本的键值对数据格式,常见结构包括:
- 对象:用包裹,键值对形式,如
{"temperature":25.5,"humidity":60}; - 数组:用
[]包裹,有序值列表,如[{"id":1,"value":10},{"id":2,"value":20}]; - 值类型:支持字符串(
"abc")、数字(123、5)、布尔值(true/false)、null等。
单片机接收JSON时面临核心挑战:
- 资源限制:RAM通常仅有KB级别(如STM32F103的20KB RAM),而JSON文本需缓存到内存中,复杂JSON易导致溢出;
- 解析能力:单片机无操作系统或轻量级系统,难以运行重型JSON解析库(如ArduinoJson的完整版需数KB RAM);
- 通信接口:需通过串口、SPI、I2C或网络(如ESP8266的WiFi)传输数据,需处理数据帧完整性(如避免粘包/分包)。
单片机接收JSON的原理流程
单片机接收JSON数据的核心流程可分为四步:数据接收→预处理→解析→提取字段,具体如下:
数据接收:通过通信接口获取原始数据
单片机通过串口(UART)、SPI、I2C或网络接口(如ESP8266的AT指令)接收JSON文本流,串口接收时需配置波特率(如9600bps)、数据位(8位)、停止位(1位),并开启中断或轮询读取数据缓冲区。
数据预处理:确保JSON文本完整
接收到的数据可能存在不完整(如分包传输)、含干扰字符(如换行符\n、回车符\r)等问题,需通过以下方式预处理:
- 帧头帧尾标记:约定数据帧以开始,以结束,丢弃不完整帧;
- 过滤非JSON字符:移除换行符、空格、校验位(如Modbus的CRC)等无关字符;
- 缓存管理:使用环形缓冲区(Ring Buffer)暂存数据,避免溢出(如限制缓冲区大小为512字节)。
JSON解析:将文本转为结构化数据
解析是关键步骤,需选择适合单片机的轻量级解析库,核心目标是提取键值对,例如从{"temp":25}中获取"temp"对应的值25。
数据提取与应用:根据键值控制硬件
解析完成后,提取目标字段(如温度、湿度),通过GPIO控制LED、继电器,或通过ADC采集传感器数据,实现设备联动。
单片机接收JSON的实践方法
硬件平台选择
根据通信需求选择单片机:
- 串口通信:STM32、Arduino(如UNO R3,ATmega328P)、ESP8266(WiFi串口透传);
- 网络通信:ESP8266/ESP32(支持TCP/UDP,可连接MQTT服务器接收JSON)。
软件工具与库选择
(1)JSON解析库
单片机需使用轻量级JSON库,推荐以下选项:
- ArduinoJson:专为Arduino/ESP32优化,支持静态分配(编译时分配内存)和动态分配(运行时分配),静态分配更节省RAM,ArduinoJson v6+的“精简模式”仅需1-2KB RAM。
- cJSON:适用于C语言单片机(如STM32 bare-metal),代码小巧(约5KB Flash),支持流式解析(适合大JSON)。
- TinyJSON:更轻量(约1KB Flash),仅支持基础JSON类型,适合极简场景。
(2)开发环境
- Arduino IDE:适合快速开发,内置库管理器可直接安装ArduinoJson;
- STM32CubeIDE:基于Eclipse,适合STM32裸机开发,需手动添加cJSON库;
- PlatformIO:跨平台IDE,支持多种单片机,可灵活配置库依赖。
具体实现步骤(以Arduino+ESP8266为例)
场景描述
ESP8266通过串口接收服务器下发的JSON数据:{"led":1,"fan":0},其中"led":1控制LED点亮,"fan":0控制风扇关闭。
步骤1:硬件连接
- ESP8266的RXD连接单片机TXD,TXD连接单片机RXD(交叉连接);
- ESP8266的CH_PD接3.3V,VCC接3.3V,GND接地。
步骤2:配置ESP8266(AT指令透传)
通过单片机向ESP8266发送AT指令,配置为透传模式:
// 串口初始化(ESP8266与单片机通信)
SoftwareSerial espSerial(2, 3); // RX=2, TX=3
void setup() {
Serial.begin(9600); // 单片机与电脑通信(调试)
espSerial.begin(115200); // ESP8266默认波特率
// 连接WiFi
espSerial.println("AT+CWJAP=\"WiFi名称\",\"密码\"");
delay(2000);
// 设置单连接透传模式
espSerial.println("AT+CIPMODE=1");
espSerial.println("AT+CIPSTART=\"TCP\",\"服务器IP\",8080");
delay(1000);
}
步骤3:接收JSON数据(预处理)
通过串口中断或轮询接收数据,使用环形缓冲区暂存:
#define BUFFER_SIZE 256
char jsonBuffer[BUFFER_SIZE];
int bufferIndex = 0;
void loop() {
if (espSerial.available()) {
char c = espSerial.read();
// 过滤非JSON字符,仅保留{ }及内部内容
if (c == '{' || c == '}' || (c >= ' ' && c <= '~')) {
jsonBuffer[bufferIndex++] = c;
// 防止缓冲区溢出
if (bufferIndex >= BUFFER_SIZE - 1) {
bufferIndex = 0;
}
}
// 检测JSON结束(}),触发解析
if (c == '}' && bufferIndex > 0) {
jsonBuffer[bufferIndex] = '\0'; // 字符串结束符
parseJSON(jsonBuffer);
bufferIndex = 0; // 清空缓冲区
}
}
}
步骤4:JSON解析(ArduinoJson静态分配)
安装ArduinoJson库(通过Arduino IDE库管理器),编写解析函数:
#include <ArduinoJson.h>
void parseJSON(const char* json) {
// 静态分配:根据JSON大小调整容量(此处预估50字节)
StaticJsonDocument<50> doc;
DeserializationError error = deserializeJson(doc, json);
if (error) {
Serial.print("解析失败: ");
Serial.println(error.c_str());
return;
}
// 提取字段
int ledState = doc["led"] | 0; // 默认0
int fanState = doc["fan"] | 0;
// 控制硬件
digitalWrite(LED_PIN, ledState ? HIGH : LOW);
digitalWrite(FAN_PIN, fanState ? HIGH : LOW);
Serial.print("LED: "); Serial.println(ledState);
Serial.print("风扇: "); Serial.println(fanState);
}
步骤5:调试与优化
- 串口打印:通过
Serial.println输出解析结果,验证数据正确性; - 内存监控:使用Arduino的
FreeMemory()函数监控剩余RAM,避免溢出; - 错误处理:增加JSON格式校验(如是否以开头、键值对是否匹配)。
常见问题与解决方案
JSON数据不完整(分包问题)
现象:接收到的JSON被截断(如{"tem)。
解决:
- 增加超时机制:若连续



还没有评论,来说两句吧...