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

相关新闻

Excel批量打印解决方案:提升办公效率的云端实践

Excel批量打印解决方案:提升办公效率的云端实践

1. 为什么需要Excel批量打印解决方案在日常办公中,我们经常遇到需要批量打印Excel表格的场景。传统的手动操作方式存在几个明显痛点:每次只能打印单个文件,当需要处理几十甚至上百个文件时,重复操作极其耗时打印设置无法保存&…

2026/10/18 5:44:35 阅读更多 →
破解生物医学图像分析难题:为什么DINOv2是你的终极自监督学习方案

破解生物医学图像分析难题:为什么DINOv2是你的终极自监督学习方案

破解生物医学图像分析难题:为什么DINOv2是你的终极自监督学习方案 【免费下载链接】dinov2 PyTorch code and models for the DINOv2 self-supervised learning method. 项目地址: https://gitcode.com/GitHub_Trending/di/dinov2 面对生物医学图像分析中标注…

2026/10/18 5:43:27 阅读更多 →
毕业论文高效写作:科研绘图、排版与AI降重实战指南

毕业论文高效写作:科研绘图、排版与AI降重实战指南

1. 毕业论文初稿的痛点与解决方案又到一年毕业季,看着身边学弟学妹们为毕业论文抓耳挠腮的样子,不禁想起当年自己熬夜改格式、反复查重的痛苦经历。作为过来人,今天想分享一套经过实战检验的毕业论文初稿全流程解决方案,特别适合时…

2026/10/4 11:55:09 阅读更多 →

最新新闻

自建 GitLab 部署实战:资源规划、安装配置与性能调优

自建 GitLab 部署实战:资源规划、安装配置与性能调优

1. 为什么选择自建 GitLab 而不是直接用托管服务很多团队在起步阶段会直接使用公有托管平台来管理代码,省事、零维护,这没什么问题。但当团队规模超过十人、项目数量超过二十个之后,一系列现实问题就会浮出水面:CI/CD 的并发任务需…

2026/10/18 5:44:43 阅读更多 →
Spring Boot个人云盘管理系统毕设项目:从搭建到避坑全解析

Spring Boot个人云盘管理系统毕设项目:从搭建到避坑全解析

简介:一份基于Spring Boot的个人云盘管理系统的毕业设计完整项目,面向Java初学者、计算机专业学生及需要快速搭建云存储项目的开发者。系统涵盖用户注册登录与权限管理、文件多格式上传下载、文件夹树状组织与搜索、分享链接与协同编辑、版本控制及数据加…

2026/10/18 5:44:43 阅读更多 →
量化交易策略基础与市场逻辑:从赚钱逻辑到策略落地

量化交易策略基础与市场逻辑:从赚钱逻辑到策略落地

量化交易这东西,听起来门槛很高,其实拆开来看就两件事:第一,你有没有一个关于市场为什么会波动的、可验证的逻辑;第二,你愿不愿意把这个逻辑老老实实地写成规则,让机器去执行。我做了几年量化策…

2026/10/18 5:44:43 阅读更多 →
Human PTH-(1-31) Amide;173833-08-4

Human PTH-(1-31) Amide;173833-08-4

基本性质中文名称:人甲状旁腺激素 (1-31) 酰胺CAS 号:173833-08-4类别:线性活性多肽,人甲状旁腺激素 N 端截短片段,C 端酰胺修饰氨基酸数量:31 个残基三字母序列:H-Ser-Val-Ser-Glu-Ile-Gln-Leu…

2026/10/18 5:44:43 阅读更多 →
CentOS 7 从零安装 CUDA 11.2 并配置 PyTorch GPU 环境实战

CentOS 7 从零安装 CUDA 11.2 并配置 PyTorch GPU 环境实战

1. 为什么现在还有人要在 CentOS 7 上装 CUDA 11.2先把场景说清楚。CentOS 7 这个系统,2024 年 6 月 30 日已经走完了它的生命周期,官方不再提供任何更新。但现实情况是,大量企业的生产环境、高校实验室的算力节点、工业视觉设备,…

2026/10/18 5:44:43 阅读更多 →
ST-GCN骨骼动作识别实战:从环境配置到毕设级部署

ST-GCN骨骼动作识别实战:从环境配置到毕设级部署

简介:本资源是一套基于时空图卷积网络(ST-GCN)实现骨骼动作识别的完整毕业设计项目,面向计算机、人工智能及相关专业本科生,专为毕设开发、课程设计与深度学习实战训练打造。项目已通过导师审核并获98分高分评价&#…

2026/10/18 5:43:43 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →