基于NodeMCU的物联网窗帘控制系统设计
最终效果
基于NodeMCU的物联网窗帘控制系统设计
项目介绍
该项目是“物联网实验室监测控制系统设计(仿智能家居)”项目中的“家电控制设计”中的“窗帘控制”子项目,最前者还包括“物联网设计”、“环境监测设计”、“门禁系统设计计”和“小程序设计”等内容。本文只介绍“窗帘控制”部分。
项目功能实现的大致思路为:当单片机接收到MQTT服务器传来的窗帘新位置时,驱动步进电机转动,使窗帘移动到指定位置。
硬件设计
接线
NodeMCU | ULN2003 | 28BYJ-48 | 电源 |
OUT1 | 1 | ||
OUT2 | 2 | ||
OUT3 | 3 | ||
OUT4 | 4 | ||
D4 | INT1 | ||
D3 | INT2 | ||
D2 | INT3 | ||
D1 | INT4 | ||
+ | 5 | 5V | |
GND | - | GND |
成本
NodeMCU | 28BYJ-48模组 |
27.9 | 8.53 |
其中共需36.5元左右来购买该项目所需的模块。此外还需1根数据线、若干杜邦线、能提供5~12V中间任意电压的电源。
机械模型搭建
为使演示更贴合实际,本系统制作了一个窗帘模型,模型图见下文。
模型左上方的黑色绝缘胶带表示窗帘的移动端,位于左侧时表示窗帘闭合(遮住窗户);位于右侧时表示窗帘打开(露出窗户)。在模型中,步进电机带动齿轮旋转,从而带动传送带转动,进而实现窗帘的移动。通过控制步进电机的旋转,便可将窗帘移动至指定位置。该模型中的两齿轮中心距为800mm,主动轮的周长约为74.61mm(比两齿轮中心距的10%略小),步进电机旋转10圈可将窗帘移动到另一侧(窗帘行程留有冗余)。
器件图





器件尺寸
未完待续
皮带:未完待续
28BYJ-48型步进电机的轴:未完待续
木板:未完待续
软件设计
本次的开发环境为Arduino IDE,开发板型号为NodeMCU 0.9 (ESP-12 Module)。
本系统软件部分的流程如下图所示。在初始化之后,等待小程序下发窗帘位置,据此驱动步进电机旋转。
连接WiFi以及接收MQTT服务器传来的消息,可参考:利用ESP-01S中继实现STM32F103C8T6与MQTT服务器的串口双向通信_mqtt和stm32开发板通信-CSDN博客
解析JSON数据,可参考:Arduino中解析JSON数据-CSDN博客
驱动28BYJ-48型步进电机转动,可参考:NodeMCU驱动28BYJ-48型步进电机(Arduino)-CSDN博客
//选择NodeMCU 0.9 (ESP-12 module)
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <Arduino.h>// 设置wifi接入信息和MQTT服务器
const char* wifiname = "DOILMSBOIOT";
const char* password = "doilmsboiot";
const char* mqttServer = "broker.emqx.io";bool receive_message_flag = 0; //1表示收到信息但还未处理,0表示未收到信息或已处理WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);// 待解析的json文件,所需空间:13~15个字节,正好初始值为最多的字节;若初始化时空间不足,收到信息后无法赋值
String json = "{\"curtain\":000}";// 创建DynamicJsonDocument对象
const size_t capacity = JSON_OBJECT_SIZE(1) + 32 ; //1表示待解析的JSON对象中有1对数据,32为解析过程中需要的额外空间,可在此网站计算 https://arduinojson.org/v6/assistant/#/step1
DynamicJsonDocument doc(capacity);int curtain_position ; // 解析后的窗帘位置
int curtain_now_position =0 ; // 窗帘现在的位置void setup()
{Serial.begin(9600); // 启动串口通讯WiFi.mode(WIFI_STA); //设置ESP8266工作模式为无线终端模式connectWifi(); // 连接WiFimqttClient.setServer(mqttServer, 1883); // 设置MQTT服务器和端口号mqttClient.setCallback(receiveCallback); // 设置MQTT订阅回调函数connectMQTTserver(); // 连接MQTT服务器stepmotor_initial(); //步进电机初始化
}void loop()
{if (mqttClient.connected()) // 如果开发板成功连接服务器{ mqttClient.loop(); // 处理信息(收到信息后的回调函数)以及心跳} else // 如果开发板未能成功连接服务器{ connectMQTTserver(); // 则尝试连接服务器并订阅主题}if (receive_message_flag == 1) //收到信息但还未处理{ deserializeJson(doc, json); // 反序列化数据// 解析收到的数据信息curtain_position = doc["curtain"].as<int>();if(curtain_position - curtain_now_position > 0){Serial.print("电机要转到的位置:");Serial.println(curtain_position);Serial.print("电机现在的位置:");Serial.println(curtain_now_position);int cycle = (int)(curtain_position - curtain_now_position)/10;Serial.println("开始转动");Serial.println(cycle);for(int i=0; i < cycle; i++){clockwise_turn_one_circle();curtain_now_position += 10;Serial.print("转过的圈数:");Serial.println(i);} Serial.println("结束转动");Serial.print("电机现在的位置:");Serial.println(curtain_now_position);Serial.println("");}if(curtain_position - curtain_now_position < 0){Serial.print("电机要转到的位置:");Serial.println(curtain_position);Serial.print("电机现在的位置:");Serial.println(curtain_now_position);int cycle = (int)(curtain_now_position - curtain_position)/10;Serial.println("开始转动");Serial.println(-cycle);for(int i=0; i < cycle; i++){anti_clockwise_turn_one_circle();curtain_now_position -= 10;Serial.print("转过的圈数:");Serial.println(i);} Serial.println("结束转动");Serial.print("电机现在的位置:");Serial.println(curtain_now_position);Serial.println("");}receive_message_flag = 0; //已处理接收到的信息}}// 连接MQTT服务器并订阅主题
void connectMQTTserver()
{// 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)String clientId = "esp8266-" + WiFi.macAddress();if (mqttClient.connect(clientId.c_str())) //如果成功连接MQTT服务器{ Serial.print("MQTT Server Has Connected. ");Serial.print("Server Address: ");Serial.println(mqttServer);Serial.print("ClientId: ");Serial.println(clientId);subscribeTopic(); // 订阅指定主题} else {Serial.print("MQTT Server Connect Failed. Client State:");Serial.println(mqttClient.state());delay(3000);}
}// 收到信息后的回调函数
void receiveCallback(char* topic, byte* payload, unsigned int length)
{Serial.print("Message with the topic of [ ");Serial.print(topic);Serial.println(" ] has been received.");Serial.print("Content: ");for (int i = 0; i < length; i++) {Serial.print((char)payload[i]);json[i] = (char)payload[i]; //将收到的信息赋给json,以便后续解析和发射信号}Serial.println("");for (int i = length; i < 15; i++) //清除掉多余字符{json[i] = '\0';}receive_message_flag = 1; //表示收到信息但还未处理Serial.print("Message Length (Bytes) : ");Serial.println(length);Serial.println(" ");
}// 订阅指定主题
void subscribeTopic()
{String topicString = "deviceControl3/curtain"; // 订阅主题的名称char subTopic[topicString.length() + 1]; strcpy(subTopic, topicString.c_str());if(mqttClient.subscribe(subTopic)) //如果成功订阅主题{Serial.print("Subscrib Topic: ");Serial.println(subTopic);Serial.println("");} else {Serial.print("Subscribe Fail...");}
}// ESP8266连接wifi
void connectWifi()
{WiFi.begin(wifiname, password);Serial.println("Connecting to WiFi");while (WiFi.status() != WL_CONNECTED) //等待WiFi连接,当wifi未连接时,持续输出".";成功连接后输出连接成功信息{delay(1000);Serial.print(".");}Serial.println("");Serial.println("WiFi Connected!"); Serial.println("");
}void stepmotor_initial()
{pinMode(D1, OUTPUT);pinMode(D2, OUTPUT); pinMode(D3, OUTPUT);pinMode(D4, OUTPUT);
}void clockwise_turn_one_circle()
{for(int i=0;i<512;i++){digitalWrite(D1, HIGH);digitalWrite(D2, LOW);digitalWrite(D3, LOW);digitalWrite(D4, LOW);delay(1);digitalWrite(D1, HIGH);digitalWrite(D2, HIGH);digitalWrite(D3, LOW);digitalWrite(D4, LOW);delay(1);digitalWrite(D1, LOW);digitalWrite(D2, HIGH);digitalWrite(D3, LOW);digitalWrite(D4, LOW);delay(1);digitalWrite(D1, LOW);digitalWrite(D2, HIGH);digitalWrite(D3, HIGH);digitalWrite(D4, LOW);delay(1);digitalWrite(D1, LOW);digitalWrite(D2, LOW);digitalWrite(D3, HIGH);digitalWrite(D4, LOW);delay(1);digitalWrite(D1, LOW);digitalWrite(D2, LOW);digitalWrite(D3, HIGH);digitalWrite(D4, HIGH);delay(1);digitalWrite(D1, LOW);digitalWrite(D2, LOW);digitalWrite(D3, LOW);digitalWrite(D4, HIGH);delay(1);digitalWrite(D1, HIGH);digitalWrite(D2, LOW);digitalWrite(D3, LOW);digitalWrite(D4, HIGH);delay(1);}
}void anti_clockwise_turn_one_circle()
{for(int i=0;i<512;i++){digitalWrite(D1, LOW);digitalWrite(D2, LOW);digitalWrite(D3, LOW);digitalWrite(D4, HIGH);delay(1);digitalWrite(D1, LOW);digitalWrite(D2, LOW);digitalWrite(D3, HIGH);digitalWrite(D4, HIGH);delay(1);digitalWrite(D1, LOW);digitalWrite(D2, LOW);digitalWrite(D3, HIGH);digitalWrite(D4, LOW);delay(1);digitalWrite(D1, LOW);digitalWrite(D2, HIGH);digitalWrite(D3, HIGH);digitalWrite(D4, LOW);delay(1);digitalWrite(D1, LOW);digitalWrite(D2, HIGH);digitalWrite(D3, LOW);digitalWrite(D4, LOW);delay(1);digitalWrite(D1, HIGH);digitalWrite(D2, HIGH);digitalWrite(D3, LOW);digitalWrite(D4, LOW);delay(1);digitalWrite(D1, HIGH);digitalWrite(D2, LOW);digitalWrite(D3, LOW);digitalWrite(D4, LOW);delay(1);digitalWrite(D1, HIGH);digitalWrite(D2, LOW);digitalWrite(D3, LOW);digitalWrite(D4, HIGH);delay(1);}
}
不足之处
- 电机转速太慢
- 缺少获取窗帘当前位置的功能,无法处理手动和打滑。
相关文章:

基于NodeMCU的物联网窗帘控制系统设计
最终效果 基于NodeMCU的物联网窗帘控制系统设计 项目介绍 该项目是“物联网实验室监测控制系统设计(仿智能家居)”项目中的“家电控制设计”中的“窗帘控制”子项目,最前者还包括“物联网设计”、“环境监测设计”、“门禁系统设计计”和“小…...

喜报 | 擎创科技入围上海市优秀信创解决方案
近日,由上海市经信委组织的“2024年上海市优秀信创解决方案”征集遴选活动圆满落幕,擎创科技凭借实践经验优秀的《擎创夏洛克智能预警与应急处置解决方案》成功入选“2024年上海市优秀信创解决方案”名单。 为激发创新活力,发挥标杆作用&…...

windows10下使用沙盒多开uiautoanimation可行性验证
文章目录 ⭐前言⭐sandboxie下载使用⭐pyinstaller打包python的uiautoanimation成exe⭐结论⭐结束 ⭐前言 大家好,我是yma16,本文分享windows下使用沙盒多开uiautoanimation可行性验证。 背景 实现多开应用程序从而进行自动化控制,批量处理大…...

电脑报错wsdprintproxy.dll丢失?修复wsdprintproxy.dll文件缺失的实用方法
在使用电脑的过程中,我们可能会遇到各种各样的错误提示,其中之一就是系统提示wsdprintproxy.dll文件丢失。这个DLL文件是Windows操作系统中的一个重要组件,它通常与Windows的打印功能相关。当这个文件丢失或损坏时,可能会导致打印…...
Kubernetes 的资源管理方式
集群架构 Docker 是每一个节点(包括 Master 节点和 Node 节点)的运行时环境。 kubelet 负责控制所有容器的启动和停止等,保证每个节点(包括 Master 节点和 Node 节点)正常工作,并且帮助 Node 节点和 Maste…...

layui动态拼接生成下拉框验证必填项失效问题
利用 jQuery 动态拼接下拉框时,lay-verify"required" 失效了,有以下几种原因。 1. <form></form>标签 加入 layui 类,class"layui-form" 。提交按钮上加自动提交,lay-submit ""; 。需…...
VUE3+VITE简单的跨域代理配置
出于安全考虑,未设置前端白名单,前端开发时,需要配置代理。 在本地创建一个虚拟服务器,发送请求数据,同时接受请求的数据, 利用服务器与服务器间,交互,不会有跨域问题,也…...

Xdebug
1、开启xdebug扩展 2、修改一下php.ini文件 xdebug.remote_enable 1 xdebug.remote_autostart 13、vscode安装插件php debug 4、生成launch.json文件,好像啥都不用改 5、vscode没有配置php路径的,需要去配置: 6、发起请求 8、代码断…...

IDEA | SpringBoot 项目中使用 Apifox 上传接口
目录 1 安装 Apifox Helper 插件2 获取 Apifox 的 API 访问令牌3 IDEA 中设置 API 访问令牌4 IDEA 中上传接口5 常见问题5.1 如何自动设置目录名5.2 如何自动设置接口名5.3 如何更改上传位置 Apifox 官方指南: https://apifox.com/help/applications-and-p…...
列表分页返回对象
列表分页返回对象 仅针对于新项目,因为一般进入公司后项目都是已经搭建好的,只需要在原有框架基础上操作就可以了,但是遇到从0开始的项目并且还没有架构需要自己搭框架的时候就需要自己想办法找各种封装格式。 下面记录分页列表返回的封装格式…...

微软edge浏览器 v131.0.2903.99便携版
前言 Microsoft Edge浏览器是个新浏览器,它用起来很简单,界面也很清爽。这个浏览器功能特别多,里面还带了微软的小助手Contana,能帮用户做不少贴心的事儿。它支持安装各种小工具(插件),还能在网…...
Prometheus 专栏 —— Prometheus入门介绍
Prometheus 是? Prometheus 是一个开源的服务监控系统和时序数据库,主要用于收集、存储、查询和告警时间序列数据,这些数据通常反映了系统或者应用的状态或性能 Prometheus 的基本功能是? 数据采集数据存储数据查询告警通知 Prometheus 监控核心组件?…...

元宇宙在教育行业主要有哪些应用场景?
近两年来,元宇宙风潮在全球范围内迅速掀起了一股新的浪潮,“元宇宙”已成为各行各业探索新发展方向的热门话题,教育行业亦不例外。那么元宇宙在教育行业主要有哪些应用场景呢? 以下为主要适用场景: 课程实践ÿ…...

JZ31 栈的压入、弹出序列
题目来源:栈的压入、弹出序列_牛客题霸_牛客网 题目:如下 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序…...

电脑缺失libcurl.dll怎么解决?详解电脑libcurl.dll文件丢失问题
一、libcurl.dll文件丢失的原因 libcurl.dll是一个用于处理URL传输的库文件,广泛应用于各种基于网络的应用程序。当这个文件丢失时,可能会导致相关应用程序无法正常运行。以下是libcurl.dll文件丢失的一些常见原因: 软件安装或卸载不完整&a…...

Ribbon、Nacos
目录 Ribbon 常见负载算法 切换负载均衡算法 Nacos注册中心 下载和运行 微服务模块接入注册中心 consumer-80模块 配置类 Controller Nacos配置中心 Nacos分类配置(实现配置隔离) DataID方案 Group方案 Namespace方案 总结 Ribbon Ribbon…...

SpringCloudAlibaba实战入门之路由网关Gateway初体验(十一)
Spring Cloud 原先整合 Zuul 作为网关组件,Zuul 由 Netflix 公司提供的,现在已经不维护了。后面 Netflix 公司又出来了一个 Zuul2.0 网关,但由于一直没有发布稳定版本,所以 Spring Cloud 等不及了就自己推出一个网关,已经不打算整合 zuul2.0 了。 一、什么是网关 1、顾明…...

【C语言练习(18)—指针传递参数练习】
C语言练习(18) 文章目录 C语言练习(18)前言问题问题解析 前言 指针的使用很方便参数之间的传递,通过交换数字,来练习函数之间指针传递数据。 问题 利用函数交换两个数字的大小 问题解析 例如a5;b10;想…...

外网访问 Docker 容器的可视化管理工具 DockerUI
DockerUI 是一个 docker 容器镜像的可视化图形化管理工具,DockerUI 可以用来轻松构建、管理和维护 docker 环境。让用户维护起来更方便。 本文就介绍如何安装使用 DockerUI 并结合路由侠内网穿透来访问 DockerUI。 第一步,安装 DockerUI 1,…...

Edge SCDN酷盾安全重塑高效安全内容分发新生态
在数字化浪潮不断推进的今天,互联网内容的分发效率与安全性已成为企业业务发展的关键要素。酷盾安全推出的Edge Secure Content Delivery Network(Edge SCDN),不仅集成了分布式DDoS防护、CC防护、WAF防护及BOT行为智能分析等安全加…...

(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...

UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)
安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...

【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...