
简介这份PDF面向电子、物联网与自动化方向的在校学生及DIY爱好者围绕Arduino UNO核心控制板完整给出了一套智能家居控制系统的设计思路与实现方案可用于课程设计、毕业设计选题参考或自学开源硬件入门。资源包共1个PDF文件约1.9MB正文以中文撰写并附英文摘要与关键词含目录、系统总体设计、模块化硬件方案、EDR蓝牙通信及易安卓客户端开发等章节结构清晰便于按需查阅。内容涵盖智能开关控制、LED灯光状态提示、离家与回家等场景模式、温湿度等设备状态监控以及烟雾和门窗传感器安全报警功能并分析了高效传输、操作简便、可靠性强、易于扩展等优势。已有1108人学习下载适合希望理解蓝牙无线控制链路与模块化设计方法的读者对照参考快速建立从核心板到手机客户端的整体认知。1. 从一盏灯说起Arduino 智能家居控制设计到底在控制什么很多人对 Arduino 智能家居控制设计的第一印象是用手机点一下灯就亮了但真正动过手的人会发现卡住项目进度的往往不是代码而是继电器一上电就吸合、DHT22 第一次读出来是 NaN、断网之后设备状态一片空白这类工程问题。这套设计的本质是把温湿度、光照、人体感应这些物理量采上来经过一层确定的判断逻辑再去驱动继电器、舵机、风扇这类执行器最后给用户留一个能看、能改的入口。它的适用人群比想象中宽电子类课程设计需要一份能跑起来的完整方案创客想给出租屋加个自动通风嵌入式初学者想找一个软硬件都覆盖的练手项目。控制规模和成本完全可控一块 Arduino UNO 加几个模块就能搭出原型换成 ESP32 之后还能顺手把局域网控制页面做出来。需要提前说清边界Arduino 方案适合几十毫秒级响应的家居场景不适合直接驱动 220V 大功率负载的裸接也不适合当作安防主控。把继电器、隔离、供电这三件事做对后面所有代码才有意义。2. 主控与传感器选型Arduino UNO 和 ESP32 在家居节点里怎么分工2.1 按要不要联网决定主控而不是按性能选型的第一个岔路口是联网。Arduino UNO 没有内置无线能力想联网得外挂模块代码量和不确定性都会增加ESP32 自带 Wi-Fi跑 Arduino 框架下同一套语法直接就能起一个 HTTP 服务。常见做法是纯本地联动、只做采集和显示的节点用 UNO需要远程查看或远程改参数的节点用 ESP32。这不是性能问题。UNO 是 8 位 AVR主频 16MHz2KB SRAMESP32 是双核 240MHz几百 KB 可用内存还有硬件 PWM 和 ADC。对温湿度采样、继电器控制这种任务UNO 绰绰有余。真正的差别在于ESP32 的 ADC 线性度一般测模拟量要校准UNO 的 3.3V 输出能力弱某些模块需要电平转换。搞清楚这两点选型就不会反复。如果项目里既有本地面板又有联网需求比较稳的拆法是 ESP32 当主控负责联网和规则判断UNO 只做一块哑终端负责按键和数码管显示两者用串口协议通信。这种分工在需要驱数码管或控制舵机的场景里很常见因为 UNO 的时序控制写起来更直观。2.2 传感器、执行器与引脚分配对照表引脚分配是设计阶段最该先落纸的部分。UNO 上 D0/D1 被串口占用D2/D3 支持外部中断D9/D10 支持 PWM模拟口 A4/A5 是 I2C。ESP32 的 GPIO34 到 GPIO39 只能输入不能输出GPIO6 到 GPIO11 接了内部 Flash 不能用。这些限制直接决定模块接哪里。模块典型型号接口类型UNO 建议引脚ESP32 建议引脚关键备注温湿度DHT22单总线D2GPIO4需 4.7k 到 10k 上拉温湿度SHT30I2CA4/A5GPIO21/22精度高走 I2C 不易受干扰显示屏SSD1306 0.96 寸I2CA4/A5GPIO21/22默认地址 0x3C少数为 0x3D继电器单路 5V 光耦模块数字输出D8GPIO25低电平触发居多注意默认态舵机SG90PWMD9GPIO13单独供电勿从板子 5V 取大电流风扇驱动MOS 管模块PWMD10GPIO26感性负载必须加续流二极管人体感应HC-SR501数字输入D3GPIO27输出 3.3VUNO 可直读光照BH1750I2CA4/A5GPIO21/22与 OLED 共总线需换地址无冲突表格里最容易踩的是继电器默认态。市面上多数光耦继电器是低电平触发也就是说上电瞬间如果引脚悬空模块可能误吸合。解决办法有两个把控制脚在setup()里第一时间设成输出并写高电平或者在有条件时用高电平触发的模块。2.3 供电与继电器隔离烧板子的高频原因继电器、舵机、风扇都不要从开发板的 5V 引脚取电。SG90 堵转电流能到 700mA 以上继电器线圈吸合瞬间也有几十毫安的冲击这些电流会把板载稳压芯片拉到欠压表现就是开发板反复重启、串口打印乱码严重的直接烧稳压。正确接法是外部 5V 电源给执行器供电开发板的地和外部电源的地连在一起信号线只走控制信号。如果是控制 220V 负载继电器模块本身的光耦隔离只在信号端有效强电侧的走线间距、绝缘、外壳必须按安全规范处理别把强电和弱电捆在一根扎带里。注意舵机和继电器共用一个 5V 电源时舵机启动的电压跌落会让继电器抖动表现为灯自己闪。分开供电或者加一颗 1000uF 电解电容就近滤波都能缓解。3. Arduino IDE 与库配置把 DHT、SSD1306 和继电器跑通的最小工程3.1 开发板管理器地址与 ESP32 支持包安装Arduino IDE 装好后默认只有 AVR 系列。要用 ESP32先打开文件 - 首选项在附加开发板管理器网址里填入开发板索引地址然后进工具 - 开发板 - 开发板管理器搜索 esp32 并安装。安装包体积不小国内网络环境下载慢是常态可以先把开发板管理器网址换成一个访问更快的镜像再试。装完之后在开发板列表里能看到 ESP32 Dev Module、ESP32S3 Dev Module 等条目。选型要和实物一致ESP32S3 的引脚映射和普通 ESP32 不一样选错会出现能编译、能上传、但引脚行为完全不对的情况。串口驱动也别漏CH340 和 CP2102 是两种常见芯片需要对应的驱动。如果同时用 VS Code 加 Arduino 扩展开发板包会在两个 IDE 之间共享装一次即可。但要注意扩展的配置文件路径可能和 Arduino IDE 不同出现能编译不能上传时先看串口是否被另一个 IDE 的串口监视器占用。3.2 装库时的版本坑DHT 与 Adafruit 统一风格库管理器里搜 DHT 会出现多个同名来源。Adafruit 的 DHT sensor library 配套 Adafruit Unified Sensor装一个会自动提示装另一个。另一套是 DHTesp接口更简单但精度处理口径不同。混用会出现编译报错找不到Adafruit_Sensor.h或者读出来的湿度和另一份代码对不上。SSD1306 也是同样的情况。Adafruit_SSD1306 需要配合 Adafruit_GFXU8g2 则是另一套体系内存占用更小但 API 完全不同。课程设计里最常见的是 Adafruit 组合因为示例多。选定一套之后全工程统一别在同一个项目里既用 U8g2 又用 Adafruit_GFX。提示库装完编译报错先看文件 - 示例里对应库的官方示例能不能编译过。示例都过不了问题在库或开发板包不在自己的代码。3.3 温湿度采集 OLED 显示 继电器联动的最小可运行代码下面这段代码把 DHT22 采集、SSD1306 显示、继电器按温度联动串起来是可以直接烧进 UNO 或 ESP32 的最小骨架。#include Wire.h #include Adafruit_GFX.h #include Adafruit_SSD1306.h #include DHT.h #define DHTPIN 2 #define DHTTYPE DHT22 #define RELAY_PIN 8 #define OLED_ADDR 0x3C DHT dht(DHTPIN, DHTTYPE); Adafruit_SSD1306 oled(128, 64, Wire, -1); void setup() { Serial.begin(115200); pinMode(RELAY_PIN, OUTPUT); digitalWrite(RELAY_PIN, HIGH); // 低电平触发继电器先断开避免上电误吸合 dht.begin(); if (!oled.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) { Serial.println(F(SSD1306 init failed)); // 地址写错或 SDA/SCL 接反 } oled.clearDisplay(); oled.setTextColor(SSD1306_WHITE); } void loop() { float t dht.readTemperature(); float h dht.readHumidity(); if (isnan(t) || isnan(h)) { // 首帧或总线异常会返回 NaN Serial.println(F(DHT read failed)); delay(2000); return; } digitalWrite(RELAY_PIN, t 30.0 ? LOW : HIGH); // 超过 30 度吸合风扇 oled.clearDisplay(); oled.setCursor(0, 0); oled.print(F(Temp: )); oled.print(t, 1); oled.println(F( C)); oled.print(F(Humi: )); oled.print(h, 1); oled.println(F( %)); oled.display(); Serial.print(t, 1); Serial.print(,); Serial.println(h, 1); delay(2000); // 最小示例写法下一章会换成非阻塞结构 }代码的逻辑说明setup()里先把继电器引脚写成高电平是为了在 DHT 和屏幕初始化之前就锁死执行器状态这一步顺序不能调。oled.begin()的返回值一定要判返回 false 基本就是 I2C 地址或接线问题不判就会得到一个永远黑屏、但串口一切正常的假象。loop()里用isnan过滤无效读数是因为 DHT22 上电后第一次读取经常失败直接参与判断会导致继电器乱动。参数说明DHTTYPE改成DHT11也能跑但 DHT11 湿度分辨率只有 1%温度精度 ±2℃联动阈值别设太细。OLED_ADDR如果接的是 0x3D 型号扫描不到就换这个值。阈值 30.0 是硬编码的实际项目里应该提成变量方便后面接网页配置。3.4 Arduino 上传项目出错按这四步定位上传失败是最高频的报错按顺序排查比反复重插线快得多。第一步看端口。开发板管理器装好了、线也插了但工具菜单里没有端口说明是驱动或线材问题。很多充电线只有电源线没有数据线换一根再试。第二步看端口占用。串口监视器开着、或者 VS Code 的串口终端连着上传时会报端口被占用。关掉所有打开的串口工具。第三步看开发板型号。型号选错时上传日志里会出现avrdude: stk500_recv(): programmer is not responding之类的内容。UNO 要选 Arduino UnoNano 老版本要选处理器为 ATmega328POld Bootloader。第四步看接线。RX/TX 上挂了外设会干扰上传拔掉 D0/D1 上的线再传一次。ESP32 卡在Connecting........时按住 BOOT 键再点上传看到Writing at再松手。4. 控制逻辑设计从延时轮询走向状态机与局域网控制页面4.1 为什么 delay 写法会在智能家居里翻车上一章的代码里有delay(2000)。单看温湿度采样没问题但一旦加上按键、网页请求、舵机动作问题就出来了delay 期间处理器什么都不做按键按下去没反应网页请求超时舵机动作被推迟。家居场景里用户对点了没反应的容忍度极低两秒的卡顿就会让人认为设备坏了。另一个隐患是阈值抖动。温度在 30.0 度附近来回跳继电器就会反复吸合断开一天下来触点寿命掉得很快。这不是硬件问题是判断逻辑缺少回差。常见做法是两条线并行改把时间相关的 delay 全部换成基于millis()的周期判断把阈值判断换成带滞回的区间判断。代码结构从顺序执行变成每轮都快速扫一遍到点才做事。4.2 用 millis 搭建非阻塞采样与控制骨架const unsigned long SAMPLE_MS 2000; // 采样周期 const unsigned long CONTROL_MS 500; // 控制判定周期 const float TEMP_ON 30.0; // 高于此值吸合 const float TEMP_OFF 28.0; // 低于此值断开形成 2 度回差 unsigned long lastSample 0; unsigned long lastControl 0; float lastTemp NAN, lastHumi NAN; bool fanOn false; void loop() { unsigned long now millis(); if (now - lastSample SAMPLE_MS) { // 采样任务 lastSample now; float t dht.readTemperature(); float h dht.readHumidity(); if (!isnan(t)) lastTemp t; // 读失败时保留上次有效值 if (!isnan(h)) lastHumi h; } if (now - lastControl CONTROL_MS) { // 控制任务独立周期 lastControl now; if (!fanOn lastTemp TEMP_ON) { fanOn true; digitalWrite(RELAY_PIN, LOW); } if ( fanOn lastTemp TEMP_OFF) { fanOn false; digitalWrite(RELAY_PIN, HIGH); } } handleWebClient(); // 每轮都跑内部不能有 delay }逻辑说明millis()返回的是开机以来的毫秒数无符号长整型溢出后回绕依然能靠减法正确计算间隔所以写法上一定是now - last 周期不要写成now last 周期。采样和控制拆成两个周期是因为传感器读取慢、控制判定要快绑在一起会让判定被采样拖着走。参数说明SAMPLE_MS设 2000 是因为 DHT22 本身采样率上限约 0.5Hz设更小只会读到重复值。TEMP_ON和TEMP_OFF之间的 2 度就是回差实际调试时按传感器精度取DHT11 建议放到 3 度以上。4.3 ESP32 作为网络服务器的控制接口设计把主控换成 ESP32 之后加一层 HTTP 接口就能用手机浏览器控制。#include WiFi.h #include WebServer.h WebServer server(80); float setTemp 30.0; void handleSet() { // /set?t27.5 if (server.hasArg(t)) setTemp server.arg(t).toFloat(); server.send(200, application/json, {\ok\:true}); } void handleState() { // /state 返回当前状态快照 String json {\temp\: String(lastTemp, 1) ,\humi\: String(lastHumi, 1) ,\set\: String(setTemp, 1) }; server.send(200, application/json, json); } void setupNetwork() { WiFi.mode(WIFI_STA); WiFi.begin(your-ssid, your-pass); server.on(/set, handleSet); server.on(/state, handleState); server.begin(); } void handleWebClient() { server.handleClient(); // 必须每轮调用否则请求得不到响应 }逻辑说明WebServer是 ESP32 Arduino 核心自带的同步服务器用起来简单但它依赖handleClient()被频繁调用。如果把handleClient()放在有 delay 的循环里网页就会转圈。handleSet只更新变量不直接操作继电器实际动作留给 4.2 的控制任务统一判定这样网页请求和本地逻辑不会打架。参数说明server.arg(t)拿到的是字符串toFloat()在解析失败时返回 0所以生产代码里要加范围校验把非法值挡在 15 到 35 之外。返回 JSON 而不是纯文本是为了后面接手机页面或第三方平台时不用改协议。4.4 断网与掉电后的状态恢复联网设备一旦依赖网页下发配置断网就成了必须处理的状态。常见做法是把最近一次有效的设定值写进 EEPROM 或 Preferences开机时先读出来当作默认值网络恢复后再以远端为准。ESP32 用Preferences库比裸写 EEPROM 省事它会自动处理磨损均衡。掉电恢复的关键是执行器的安全默认态。风扇、加热这类负载上电必须默认断开等采样和控制判定跑完一轮再决定是否吸合。代码里的顺序就是引脚初始化写默认断开读存储配置启动网络进入主循环。任何一步失败都不能让继电器变成吸合状态。如果是纯 UNO 节点没有存储需求掉电恢复就退化成上电重新判定只要保证setup()里继电器先行置位就够了。5. Wokwi 仿真与实机排错智能家居控制设计的验证技巧5.1 先在 Wokwi 里验证逻辑再碰烙铁Wokwi 是一个在线 Arduino 仿真平台支持 UNO、ESP32 以及 DHT22、SSD1306、舵机、超声波这些常用器件。它的价值不在替代实机而在于把逻辑错误和接线错误分开。实机上遇到灯不亮你分不清是代码判断错了还是线接错了在仿真里接线是确定的跑不通就一定是代码问题。具体做法把工程里的引脚定义原样搬进仿真DHT22 的温湿度可以用滑动条实时拖动。拖动温度穿过 30 度看继电器状态和 OLED 显示是否同步变化再拖回 29 度确认继电器没有立刻断开验证回差逻辑生效。这一步能在几分钟内把阈值抖动、状态锁死这类问题筛出来。仿真跑通之后再上实机排查范围就只剩供电、接线和驱动。ESP32 的 Wi-Fi 部分在仿真里支持有限网络相关的逻辑建议直接上实机验证但把 SSID 和密码提成宏定义改起来会快很多。5.2 常见现象与排查对照表现象优先怀疑具体动作上电继电器就吸合触发极性判断反了把digitalWrite的 HIGH/LOW 对调验证OLED 全黑但串口正常I2C 地址或接线换 0x3D检查 SDA/SCL 是否接反DHT 一直返回 NaN上拉电阻缺失D2 与 5V 之间加 10k 电阻屏幕偶尔花屏供电跌落靠近模块加 100uF 电容执行器独立供电网页请求超时handleClient()被阻塞检查循环里是否还有 delay温度贴阈值时继电器抖动缺少回差设置 ON/OFF 两个不同阈值长时间运行后死机内存碎片或看门狗减少 String 拼接改用字符数组这张表里最值得留意的是最后一行。ESP32 上有看门狗循环里长时间阻塞会被复位而 Arduino 上频繁用String拼接 JSON 会慢慢耗尽内存跑几个小时之后表现就是莫名其妙重启。处理办法是把状态信息拆成固定长度的字符数组或者直接分段发送避免每次请求都生成新字符串。5.3 让节点长期运行更稳的三个写法第一个是给网络请求加超时。ESP32 的server.handleClient()在客户端半开连接时会占用较长时间可以在配置里限制连接数和超时时间避免一个卡住的手机把整个节点拖死。同时在主循环里加一个看门狗喂狗的动作或者干脆用任务看门狗并确保每轮循环都足够短。第二个是把执行器动作做幂等。判断逻辑里如果每次都调用digitalWrite虽然电平不变但继电器模块的输入端仍可能产生微小抖动。改成只在状态发生变化时才写引脚也就是上面代码里的fanOn布尔量能显著减少触点动作次数。第三个是给采样加异常连续计数。传感器偶发读失败保留上次值是合理的但如果连续失败超过 20 次就应该把状态标记为不可信让执行器回到安全默认态并在 OLED 上显示提示。这一条在长期无人值守的场景里比任何滤波算法都管用。本文还有配套的精品资源点击获取