尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Kindle+ESP32-S3实现TCP温湿度采集与灯光控制系统

Kindle+ESP32-S3实现TCP温湿度采集与灯光控制系统 项目概述本项目基于 ESP32-S3 搭建 TCP 服务端外接 AHT20 温湿度传感器、WS2812 彩灯Kindle 8Linux 环境作为 TCP 客户端采用交互式命令行菜单通过 TCP 网络实现远程查询温湿度、控制彩灯模式。通信采用自定义文本协议以换行符作为消息边界解决 TCP 流式粘包、单次指令连接断开问题。硬件清单ESP32-S3 开发板AHT20 数字温湿度传感器WS2812 8 路 RGB 灯带Kindle 8越狱 Linux 环境arm-gcc 编译运行 TCP 客户端一、硬件设计1.1 硬件接线定义ESP32-S3外设说明GPIO1WS2812 DIN彩灯数据引脚GPIO10AHT20 SDAI2C1 数据GPIO11AHT20 SCLI2C1 时钟3.3VAHT20、WS2812 电源共电源GNDAHT20、WS2812 GND必须共地1.2 硬件注意事项WS2812 工作电压 5V若使用 3.3V 驱动容易色彩异常、频闪长时间点亮建议灯带外接 5V 供电信号端串联 330Ω 电阻。AHT20 模块自带 I2C 上拉电阻无需额外焊接。ESP32 模块供电充足大电流负载会导致电压跌落WiFi 断连。二、通信协议规范2.1 传输层TCP IPv4ESP32 作为服务端监听端口8899客户端Kindle8 Linux TCP Client一问一答模式。2.2 报文格式通信编码ASCII 文本消息分隔符\n换行符每条发送指令末尾必须追加\n服务端响应末尾追加\n客户端 → ESP32 服务端指令集指令功能描述ESP32 返回应答GET查询温湿度TEMP:XX.XX,HUM:XX.XXCMD1WS2812 红色全亮OK:REDCMD2WS2812 绿色全亮OK:GREENCMD3WS2812 黄色全亮OK:YELLOWCMD4WS2812 彩虹跑马灯OK:RUNLIGHTCMD5WS2812 全部熄灭OK:OFF其他非法指令-ERR:Unknown Command2.3 通信容错设计ESP32 服务端逐字符非阻塞解析指令废弃阻塞函数readStringUntil()防止 WiFi 任务卡死、TCP 主动断开。Linux 客户端循环接收数据直到捕获换行符避免 TCP 流式粘包、读取历史缓存数据。客户端 Socket 设置接收超时防止网络异常程序永久阻塞。三、ESP32-S3 TCP 服务端软件设计Arduino3.1 开发环境Arduino IDE平台esp32 v2.x 依赖库FastLEDWS2812 驱动Adafruit AHTX0AHT20 温湿度驱动3.2 软件架构WiFi STA 模式连接路由器自动获取 IP启动 TCP Server (8899)非阻塞轮询接收 TCP 字符缓冲区缓存识别\n切割完整指令指令分发处理温湿度读取 / WS2812 模式切换独立定时器驱动1 秒串口打印温湿度150ms 刷新跑马灯效果客户端断线自动清空灯带、释放资源3.3 完整源代码直接可用#include Arduino.h #include WiFi.h #include FastLED.h #include Adafruit_AHTX0.h // 配置区 const char* WIFI_SSID WIFI名称; const char* WIFI_PASSWORD WIFI密码; const uint16_t TCP_PORT 8899; #define WS2812_PIN 1 #define LED_COUNT 8 #define AHT_SDA_PIN 10 #define AHT_SCL_PIN 11 #define RECV_BUF_LEN 64 // CRGB leds[LED_COUNT]; Adafruit_AHTX0 aht; WiFiServer tcpServer(TCP_PORT); WiFiClient client; char recvBuf[RECV_BUF_LEN]; uint16_t bufIndex 0; enum LedMode { MODE_OFF, MODE_RED, MODE_GREEN, MODE_YELLOW, MODE_RUNLIGHT }; LedMode currentMode MODE_OFF; uint8_t hueValue 0; void setAllLed(CRGB color) { for(int i0; iLED_COUNT; i){ leds[i] color; } FastLED.show(); } void handleTcpCommand(String cmd) { cmd.trim(); cmd.toUpperCase(); Serial.print(收到TCP指令: ); Serial.println(cmd); if(cmd GET){ sensors_event_t humEvent, tempEvent; String resp; if(aht.getEvent(humEvent, tempEvent)){ resp TEMP: String(tempEvent.temperature,2) ,HUM: String(humEvent.relative_humidity,2); }else{ resp TEMP:ERR,HUM:ERR; } client.print(resp \n); } else if(cmd CMD1){ currentMode MODE_RED; setAllLed(CRGB::Red); client.print(OK:RED\n); } else if(cmd CMD2){ currentMode MODE_GREEN; setAllLed(CRGB::Green); client.print(OK:GREEN\n); } else if(cmd CMD3){ currentMode MODE_YELLOW; setAllLed(CRGB::Yellow); client.print(OK:YELLOW\n); } else if(cmd CMD4){ currentMode MODE_RUNLIGHT; client.print(OK:RUNLIGHT\n); } else if(cmd CMD5){ currentMode MODE_OFF; setAllLed(CRGB::Black); client.print(OK:OFF\n); } else{ client.print(ERR:Unknown Command\n); } } void setup() { Serial.begin(115200); while(!Serial) delay(10); FastLED.addLedsWS2812, WS2812_PIN, GRB(leds, LED_COUNT); FastLED.setBrightness(64); FastLED.clear(); FastLED.show(); Wire1.begin(AHT_SDA_PIN, AHT_SCL_PIN); if (!aht.begin(Wire1)) { Serial.println(【错误】AHT20初始化失败 SDA:10 SCL:11); } else { Serial.println(AHT20 初始化成功); } Serial.print(连接WiFi:); Serial.println(WIFI_SSID); WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); while (WiFi.status() ! WL_CONNECTED) { delay(500); Serial.print(.); } Serial.println(\nWiFi连接成功); Serial.print(本机IP: ); Serial.println(WiFi.localIP()); tcpServer.begin(); Serial.printf(TCP服务器启动,端口:%d\r\n,TCP_PORT); Serial.println(); } void loop() { if (!client.connected()) { if (client) { client.stop(); bufIndex 0; memset(recvBuf, 0, RECV_BUF_LEN); currentMode MODE_OFF; FastLED.clear(); FastLED.show(); } client tcpServer.accept(); if(client){ Serial.println(新客户端接入); } } else { while(client.available() 0) { char c client.read(); if (c \n) { if(bufIndex 0) { recvBuf[bufIndex] \0; String cmdStr(recvBuf); handleTcpCommand(cmdStr); bufIndex 0; memset(recvBuf, 0, RECV_BUF_LEN); } } else { if(bufIndex RECV_BUF_LEN-1) { recvBuf[bufIndex] c; } } } } static uint32_t printTimer 0; if(millis() - printTimer 1000){ printTimer millis(); sensors_event_t humEvent, tempEvent; if (aht.getEvent(humEvent, tempEvent)) { Serial.print(温度); Serial.print(tempEvent.temperature, 2); Serial.print( ℃ | 湿度); Serial.print(humEvent.relative_humidity, 2); Serial.println( %RH); } else { Serial.println(读取AHT20数据失败); } } static uint32_t ledTimer 0; if(millis() - ledTimer 150){ ledTimer millis(); if(currentMode MODE_RUNLIGHT){ static uint8_t pos 0; FastLED.clear(); leds[pos] CHSV(hueValue,255,255); FastLED.show(); pos; if(pos LED_COUNT) pos 0; hueValue 3; } } delay(10); }四、Kindle8 Linux TCP 客户端软件设计4.1 运行环境Kindle 8 越狱系统Linuxgcc 编译器纯 C 语言标准 POSIX Socket无第三方依赖。 功能交互式菜单、TCP 连接、一发一收、超时保护、完整接收单行应答解决粘包问题。4.2 客户端源代码tcp_client.c#include stdio.h #include stdlib.h #include string.h #include unistd.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include errno.h #include sys/time.h #define SERVER_IP 192.168.1.11 // 修改为ESP32 IP #define SERVER_PORT 8899 #define BUF_SIZE 256 #define RECV_TIMEOUT_SEC 3 // 接收超时2秒 int sock_fd -1; /* 设置socket接收超时 */ static void set_sock_timeout(int fd, int sec) { struct timeval timeout; timeout.tv_sec sec; timeout.tv_usec 0; setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, timeout, sizeof(timeout)); } /* 建立TCP连接 */ int connect_server(void) { struct sockaddr_in serv_addr; if (sock_fd 0) { printf(已经连接无需重连\n); return 0; } sock_fd socket(AF_INET, SOCK_STREAM, 0); if (sock_fd 0) { perror(socket create failed); return -1; } set_sock_timeout(sock_fd, RECV_TIMEOUT_SEC); memset(serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family AF_INET; serv_addr.sin_port htons(SERVER_PORT); if (inet_pton(AF_INET, SERVER_IP, serv_addr.sin_addr) 0) { printf(IP地址格式错误\n); close(sock_fd); sock_fd -1; return -1; } if (connect(sock_fd, (struct sockaddr *)serv_addr, sizeof(serv_addr)) 0) { perror(connect failed); close(sock_fd); sock_fd -1; return -1; } printf(✅ 成功连接 ESP32 TCP Server %s:%d\n, SERVER_IP, SERVER_PORT); return 0; } /* * 发送指令循环接收直到读到换行符获取完整一行应答 * 返回 0成功-1失败/超时/断开 */ int send_command(const char *cmd, char *resp, int resp_len) { char send_buf[BUF_SIZE]; char tmp[BUF_SIZE]; int total 0; int i 0; ssize_t ret; memset(resp, 0, resp_len); if (sock_fd 0) { printf(❌ 未连接服务器请先连接\n); return -1; } // 指令必须带换行匹配ESP32 readStringUntil(\n) snprintf(send_buf, sizeof(send_buf), %s\n, cmd); if (write(sock_fd, send_buf, strlen(send_buf)) 0) { perror(send fail); close(sock_fd); sock_fd -1; return -1; } // 循环读取直到收到 \n 代表一条应答结束 while (1) { ret read(sock_fd, tmp, sizeof(tmp)-1); if (ret 0) { printf(❌ 读取应答失败 / 超时 / 服务器断开\n); close(sock_fd); sock_fd -1; return -1; } for (i ; i ret; i) { if (total resp_len - 1) { resp[total] tmp[i]; } // 遇到换行本条响应接收完成直接退出 if (tmp[i] \n) { resp[total] \0; return 0; } } } } void show_menu(void) { printf(\n MENU \n); printf( 1 - 连接ESP32服务端\n); printf( 2 - 查询温湿度 (GET)\n); printf( 3 - WS2812 红色全亮 CMD1\n); printf( 4 - WS2812 绿色全亮 CMD2\n); printf( 5 - WS2812 黄色全亮 CMD3\n); printf( 6 - WS2812 开启跑马灯 CMD4\n); printf( 7 - WS2812 全部关灯 CMD5\n); printf( 0 - 退出程序\n); printf(\n); printf(请输入选项: ); } int main(void) { int choice; char response[BUF_SIZE]; while (1) { show_menu(); if (scanf(%d, choice) ! 1) { // 清空错误输入 while (getchar() ! \n); printf(输入错误请输入数字\n); continue; } switch (choice) { case 1: connect_server(); break; case 2: if (0 send_command(GET, response, sizeof(response))) { printf( 服务器回复: %s, response); } break; case 3: if (0 send_command(CMD1, response, sizeof(response))) { printf( 服务器回复: %s, response); } break; case 4: if (0 send_command(CMD2, response, sizeof(response))) { printf( 服务器回复: %s, response); } break; case 5: if (0 send_command(CMD3, response, sizeof(response))) { printf( 服务器回复: %s, response); } break; case 6: if (0 send_command(CMD4, response, sizeof(response))) { printf( 服务器回复: %s, response); } break; case 7: if (0 send_command(CMD5, response, sizeof(response))) { printf( 服务器回复: %s, response); } break; case 0: printf(程序退出\n); if (sock_fd 0) close(sock_fd); return 0; default: printf(无效选项请重新选择\n); break; } } }4.3 Kindle8 编译与运行命令修改代码内SERVER_IP为 ESP32 打印出的局域网 IP编译在 Linux 主机上使用交叉编译器进行编译生成 kindle8 架构的执行程序arm-linux-gnueabi-gcc -marcharmv7-a -mtunecortex-a9 -msoft-float -mabiaapcs-linux tcp_client.c -o tcp_client上传程序 ssh 登录到 kindle 主机切换到/mnt/us/gcc-test 目录gcc-test 要自行创建[rootkindle gcc-test]# scp tony192.168.1.12:/home/tony/kindle-esp32/kindle-tcp ./ kindle-tcp 100% 13KB 2.2MB/s 00:00运行[rootkindle gcc-test]#./tcp_client五、整体测试流程ESP32 上电串口监视器查看 WiFi 连接状态记录 IP 地址修改 Kindle 客户端代码内 ESP32 IP编译Kindle 运行客户端选项【1】连接服务端依次调用功能2 查询温湿度3~7 控制彩灯重复多次切换验证 TCP 连接不会断开、指令应答不乱序。六、文件目录结构ESP32_TCP_LIGHT_AHT20/ ├── doc/ │ └── 开发设计文档.md # 本文档 ├── esp32_server/ │ └── esp32_server.ino # ESP32 Arduino源码 ├── kindle_client/ │ ├── tcp_client.c │ └── Makefile # 可选一键编译脚本 └── README.md # 使用说明、接线说明
返回列表