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

发布时间:2026/9/22 1:05:00

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 # 使用说明、接线说明
延伸阅读

更多相关文章

2026/9/22 1:04:58

Leaflet框架:轻量级WebGIS开发的核心优势与实践

1. Leaflet框架概述与核心优势Leaflet作为当前最流行的轻量级WebGIS开发框架,已经成为前端地图开发领域的标配工具。我在多个实际项目中深度使用Leaflet后,发现其核心价值在于极致的轻量化设计和高度灵活的扩展性。压缩后仅约40KB的体积,却能…

2026/9/22 1:04:58

SSM+Vue构建考公知识共享平台的技术实践

1. 项目背景与核心价值作为一名经历过考公煎熬的过来人,我深知备考过程中最痛苦的三件事:错过关键公告、重复购买资料、找不到学习方向。2026年高校毕业生预计突破1179万,考公竞争将更加激烈。这正是我选择开发"考公知识共享平台"的…

2026/9/22 1:04:58

怎么下载mp3歌曲保姆级教程:解决版本升级后API全变的性能优化

怎么下载mp3歌曲保姆级教程:解决版本升级后API全变的性能优化 版本升级后 API 全变了,原本跑通的音乐下载脚本直接报错,这种崩溃感每个开发者都懂。别再盲目重试了,这篇保姆级教程带你从底层原理到实战代码,彻底解决怎么下载mp3歌曲过程中…

2026/9/22 1:04:58

基于ADMM的多微网分布式优化与碳成本管理

1. 项目背景与核心价值微电网作为分布式能源的重要载体,正在从独立运行向多微网协同交互演进。在实际工程中,我们常常遇到这样的矛盾:单个微网由于风光资源波动性难以实现100%自平衡,而传统集中式调度又面临通信负担重、隐私泄露等…

2026/9/21 3:28:31

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/21 3:33:19

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/22 0:04:49

输电线路在线监测高频面试题拆解 3秒抓住官方文档重点

输电线路在线监测高频面试题拆解 3秒抓住官方文档重点 官方文档几百页翻到头还是懵?面试问到 输电线路在线监测 的数据链路时,脑子一片空白?别慌,这种 高频面试题 我整理了10年,专门治各种“文档太长抓不住重点”的毛病。…

2026/9/22 0:04:49

中介房源管理系统重构避坑:3个关键步骤搞定API变更

中介房源管理系统重构避坑:3个关键步骤搞定API变更 版本升级后 API 全变了,这种痛只有真做过的人懂。 很多团队在接手老旧房产项目时,最崩溃的不是代码烂,而是底层框架升级后,原本熟悉的接口调用方式彻底失效。 这份 保姆级教程…

2026/9/22 0:04:49

3个坑点带你一文搞懂55gg小游戏源码

3个坑点带你一文搞懂55gg小游戏源码 盯着控制台满屏的红色报错,看着那一长串 StackTrace ,是不是脑子瞬间宕机?别急,这种时候最忌讳的就是盲目改代码。很多刚入行的前端同学,面对 55gg 小游戏这类轻量级 H5…

2026/9/20 4:54:47

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/21 18:32:12

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/21 10:29:02

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码