Files
stm32-iot-sensor-node/32_IMPLEMENTATION.md
T
2026-10-19 15:47:18 +08:00

376 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 实现逻辑详解
## 1. 系统工作流程
### 1.1 主程序流程图
```
┌─────────────────┐
│ 系统初始化 │
├─────────────────┤
│ 硬件模块初始化 │
├─────────────────┤
│ WiFi连接尝试 │
├─────────────────┤
│ TCP连接建立 │
├─────────────────┤
│ 设备注册发送 │
├─────────────────┤
│ 主循环处理 │
└─────────────────┘
```
### 1.2 详细工作流程
#### 第一阶段:系统初始化 (main.c:32-50)
```c
// 1. 中断优先级配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 2. 硬件模块初始化顺序
led_init(); // LED状态指示
beep_init(); // 蜂鸣器控制
systick_init(); // 系统时钟
usart1_init(115200); // 调试串口
eeprom_init(); // 配置存储
oled_init(); // 显示屏
dht11_init(); // 温湿度传感器
adc1_init(); // ADC采集
```
**初始化逻辑说明**:
- LED初始关闭,避免误触发
- 串口初始化为115200波特率,用于调试输出
- OLED显示屏初始化后立即准备显示数据
#### 第二阶段:网络连接 (main.c:52-79)
```c
// WiFi连接逻辑
if (esp8266_connect_wifi(WIFI_SSID, WIFI_PASS)) {
printf("Connected to WiFi\r\n");
// TCP连接逻辑
if (esp8266_tcp_connect(SERVER_IP, SERVER_PORT)) {
tcp_connected = 1;
// 设备注册消息
char reg_msg[128];
int reg_len = snprintf(reg_msg, sizeof(reg_msg),
"{\"type\":\"register\",\"device_id\":\"%s\",\"action\":\"connect\"}\n",
DEVICE_ID);
}
}
```
**连接策略**:
- 先建立WiFi连接,再尝试TCP连接
- 连接成功后发送设备注册消息
- 失败时设置tcp_connected=0,进入重连机制
## 2. 主循环实现逻辑
### 2.1 数据采集与显示 (main.c:95-112)
```c
// 传感器数据采集
u8 value[5];
dht11_get_values(value); // 获取温湿度数据
// OLED显示更新
sprintf(buf, "\xce\xc2\xb6\xc8: %5.1f \xa1\xe6", (float)value[2]); // 温度
oled_draw_text16(8, 8, buf);
sprintf(buf, "\xca\xaa\xb6\xc8: %5.1f%%", (float)value[0]); // 湿度
oled_draw_text16(8, 24, buf);
sprintf(buf, "ADC Value: %04d", adc1_get_value(10)); // ADC
oled_draw_text8(8, 40, buf);
sprintf(buf, "LED(0): %3s", led_get_status(0) ? "ON" : "OFF"); // LED状态
oled_draw_text8(8, 48, buf);
oled_refresh_framebuffer(); // 刷新显示
```
**实现细节**:
- DHT11数据格式:`value[0-1]`湿度,`value[2-3]`温度,`value[4]`校验和
- OLED使用GBK编码显示中文
- 显示区域划分:温度(8,8),湿度(8,24),ADC(8,40),LED状态(8,48)
### 2.2 网络重连机制 (main.c:116-148)
```c
static int reconnect_counter = 0;
int reconnect_threshold = RECONNECT_PERIOD_MS / SEND_INTERVAL_MS;
if (!tcp_connected) {
if (reconnect_counter >= reconnect_threshold) {
reconnect_counter = 0;
// 尝试重连
if (esp8266_tcp_connect(SERVER_IP, SERVER_PORT)) {
tcp_connected = 1;
// 重连后重新注册
char reg_msg[128];
int reg_len = snprintf(reg_msg, sizeof(reg_msg),
"{\"type\":\"register\",\"device_id\":\"%s\",\"action\":\"reconnect\"}\n",
DEVICE_ID);
}
} else {
printf("Not connected to TCP server, skipping send\r\n");
delay_ms(SEND_INTERVAL_MS);
continue; // 跳过本次循环的数据发送
}
}
```
**重连策略**:
- 每5秒尝试一次重连(RECONNECT_PERIOD_MS = 5000ms)
- 重连成功后发送"reconnect"类型的注册消息
- 连接断开期间跳过数据发送,避免错误
### 2.3 数据发送逻辑 (main.c:150-169)
```c
// JSON数据构造
char json[256];
int msg_len = snprintf(json, sizeof(json),
"{\"device_id\":\"%s\",\"temp\":%.1f,\"humidity\":%.1f,\"buzzer\":%d,\"led0\":%d,\"led1\":%d,\"led2\":%d}\n",
DEVICE_ID,
(float)value[2], // 温度
(float)value[0], // 湿度
beep_get_status(), // 蜂鸣器状态
led_get_status(0), // LED0状态
led_get_status(1), // LED1状态
led_get_status(2)); // LED2状态
// 发送数据
esp8266_clear_read_buf(); // 清空接收缓冲区
int sent = esp8266_write(json, (uint16_t)msg_len);
```
**数据格式说明**:
- 发送频率:200ms一次(SEND_INTERVAL_MS)
- 数据格式:JSON,以换行符结束
- 包含设备ID、传感器数据、执行器状态
### 2.4 控制命令接收与解析 (main.c:171-218)
```c
// 主动请求控制命令
if (tcp_connected) {
char getcmd[64];
snprintf(getcmd, sizeof(getcmd), "GET control %s\n", DEVICE_ID);
int gsent = esp8266_write(getcmd, (uint16_t)strlen(getcmd));
}
// 接收并解析控制命令
char inbuf[256];
int rlen = esp8266_read(inbuf, sizeof(inbuf));
if (rlen > 0) {
inbuf[rlen] = '\0';
printf("Control recv: %s\r\n", inbuf);
// 解析蜂鸣器控制
char *p = strstr(inbuf, "\"buzzer\"");
if (p) {
p = strchr(p, ':');
if (p && sscanf(p+1, "%d", &val) == 1) {
if (val) {
beep_on();
} else {
beep_off();
}
}
}
// 解析LED控制 (led0, led1, led2)
for (int i = 0; i < 3; i++) {
char key[16];
sprintf(key, "\"led%d\"", i);
p = strstr(inbuf, key);
if (p) {
p = strchr(p, ':');
if (p && sscanf(p+1, "%d", &val) == 1) {
led_set_status(i, val ? 1 : 0);
}
}
}
}
```
**控制协议解析**:
- 使用轮询模式主动获取控制命令
- 支持的控制命令:`{"buzzer":1,"led0":1,"led1":0,"led2":0}`
- 实时应用控制命令到硬件设备
## 3. 关键算法实现
### 3.1 DHT11时序控制算法 (dht11.c)
```c
int dht11_get_values(u8 buf[5]) {
// 1. 发送起始信号
DHT11_DATA_OUT = 0; // 拉低数据线
delay_ms(20); // 保持18ms以上
DHT11_DATA_OUT = 1; // 拉高数据线
delay_us(30); // 等待20-40us
// 2. 等待传感器响应
while(DHT11_DATA_IN && timeout < 20) {
delay_us(2);
timeout++;
}
// 3. 读取40位数据
for(int i = 0; i < 5; i++) {
for(int bit = 0; bit < 8; bit++) {
// 等待50us低电平
while(!DHT11_DATA_IN);
// 等待高电平结束
delay_us(40);
if(DHT11_DATA_IN) {
data_bit = 1; // '1'位
} else {
data_bit = 0; // '0'位
}
// 等待高电平结束
while(DHT11_DATA_IN);
// 组装字节
buf[i] <<= 1;
buf[i] |= data_bit;
}
}
return 1;
}
```
**时序要点**:
- 起始信号:拉低18ms,拉高20-40us
- 数据位判断:40us后检测电平状态
- 完整数据:40位(5字节),包含校验和
### 3.2 ESP8266 AT指令控制 (esp8266.c)
```c
int esp8266_connect_wifi(const char *ssid, const char *passwd) {
char cmd[128];
// 1. 设置WiFi模式
usart2_send_string("AT+CWMODE=1\r\n");
if(!esp8266_wait_response("OK", 2000)) return 0;
// 2. 连接WiFi
sprintf(cmd, "AT+CWJAP=\"%s\",\"%s\"\r\n", ssid, passwd);
usart2_send_string(cmd);
if(!esp8266_wait_response("OK", 10000)) return 0;
return 1;
}
int esp8266_tcp_connect(const char *ip, uint16_t port) {
char cmd[64];
sprintf(cmd, "AT+CIPSTART=\"TCP\",\"%s\",%d\r\n", ip, port);
usart2_send_string(cmd);
return esp8266_wait_response("CONNECT", 5000);
}
```
**AT指令流程**:
- 设置WiFi模式为Station模式
- 连接指定WiFi网络(最长等待10秒)
- 建立TCP连接到服务器
### 3.3 OLED显示驱动 (oled.c)
```c
void oled_refresh_framebuffer(void) {
// 1. 设置列地址范围
OLED_write_command(0x21); // 列地址设置命令
OLED_write_command(0); // 起始列
OLED_write_command(127); // 结束列
// 2. 设置页地址范围
OLED_write_command(0x22); // 页地址设置命令
OLED_write_command(0); // 起始页
OLED_write_command(7); // 结束页
// 3. 发送帧缓冲区数据
for(int i = 0; i < 1024; i++) {
OLED_write_data(OLED_FrameBuffer[i]);
}
}
```
**显示原理**:
- 使用1024字节帧缓冲区(128×64÷8)
- SSD1306支持页寻址模式,每页8行
- 批量发送整个帧缓冲区内容
## 4. 错误处理与容错机制
### 4.1 网络错误处理
```c
// 发送失败处理
int sent = esp8266_write(json, (uint16_t)msg_len);
if (sent > 0) {
printf("Sent: %s\r\n", json);
} else {
printf("Send failed\r\n");
tcp_connected = 0; // 标记连接断开
}
```
### 4.2 传感器读取异常处理
```c
u8 dht11_data[5];
if (dht11_get_values(dht11_data)) {
// 数据校验
u8 checksum = dht11_data[0] + dht11_data[1] + dht11_data[2] + dht11_data[3];
if (checksum != dht11_data[4]) {
printf("DHT11 checksum error\r\n");
// 使用上一次的有效数据或默认值
}
}
```
### 4.3 内存保护机制
```c
// 缓冲区溢出保护
int msg_len = snprintf(json, sizeof(json), "...");
if (msg_len >= sizeof(json)) {
msg_len = sizeof(json) - 1; // 截断过长数据
json[msg_len] = '\0';
}
```
## 5. 性能优化策略
### 5.1 时间片调度
- 数据采集:每次循环执行
- 网络发送:200ms间隔
- 显示刷新:每次循环执行
- 控制请求:每次循环执行
### 5.2 资源使用优化
- 静态内存分配,避免动态内存碎片
- 字符串缓冲区复用
- 中断驱动的外设操作
### 5.3 网络通信优化
- 批量发送JSON数据
- 清空接收缓冲区避免数据累积
- 连接断开时跳过发送操作
---
**文档版本**: v1.0
**最后更新**: 2025-11-16
**作者**: STM32项目组