Initial import of STM32 IoT sensor node

This commit is contained in:
yzfh
2026-10-19 15:47:18 +08:00
commit a51794b022
100 changed files with 46274 additions and 0 deletions
+376
View File
@@ -0,0 +1,376 @@
# 实现逻辑详解
## 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项目组