远程继电器模块实现(nodemcu D1 + 继电器)

前言

接下来将实现一个远程继电器,实时远程控制和查询的开关状态。用 5v 直流电控制 220v 交流电。

硬件上: 使用 nodemcu D1JQC-3FF-S-Z 继电器

软件上: 使用 nodejs 作为服务端,和 html 作为客户端。

在开始之前在电脑中建立一个文件夹 /project

效果

远程继电器模块

材料准备

硬件

名称数量
nodemcu D11
JQC-3FF-S-Z 继电器1
杜邦线若干
led(3.3v)1
面包板1

服务端依赖

/project/package.json

{"name": "server","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"author": "","license": "ISC","dependencies": {"body-parser": "^1.20.2","express": "^4.18.2","socket.io": "^4.7.1"}
}

IDE

Arduino

vsCode

服务端实现

/project/index.js


const bodyParser = require("body-parser");
const express = require("express");
const path = require("path");
const app = express();
const http = require("http");
const { Server } = require("socket.io");
const server = http.createServer(app);const port = 3005;/*** 所有开关的状态记录
*/
const switchs = {// 台灯开关desklamp: "0",
};app.use(bodyParser.json({ limit: "50mb" }));
// for parsing application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
// 前端静态服务,包括文件和页面
app.use(express.static(path.join(__dirname,"./client")))app.all("*",function (req,res,next) {res.set({"Content-Type": "text/plain","Access-Control-Allow-Credentials": "true","Access-Control-Allow-Headers": "X-Requested-With,Content-Type,token,authorization","Access-Control-Allow-Origin": "*","Access-Control-Allow-Origin": req.headers.origin,"Access-Control-Allow-Methods": "POST,GET","Content-Type": "application/json",});next();
});const io = new Server(server,{allowEIO3: true,credentials: true,cors: {},
}); 
io.on("connection",(socket) => {try {console.log("一个新的 scoket 连接"); /*** 用户连接即推送信息*/ io.to(socket.id).emit("switchs",switchs);/*** 查询请求,返回所有开关状态*/socket.on("query",function (data) {console.log(`[query]`, data ? data : "");io.to(socket.id).emit("switchs",switchs);});/*** 设置开关状态* { id: "desklamp", data: "0" | "1" }*/socket.on("set",function (body) {const id = body.id;const status = body.data;if(!id) {console.log('没有传入id,更新失败!')return};switchs[id] = status;console.log(`[set]`, id, body.data); // 更新完后广播io.emit("switchs",switchs);});// 发生错误时触发socket.on("error",function (err) {console.log("socket 错误:",err);});} catch (err) {console.log("socket 错误:",err);io.to(socket.id).emit("error",`系统异常:${err}`);}
});server.listen(port,() => {console.log(`服务正在运行: http://localhost:${port}`);
});

代码分析

  1. 首先是使用 3005 端口启动了一个服务器.
  2. ./client 设置为静态文件夹,该文件夹中将会放置客户端页面
  3. 使用 socket.io 插件实现一个 ws 服务。
  4. 监听 query 消息,用于返回当前开关状态
  5. 监听 set 消息,用于改变开关状态

代码中 switchs 写为对象形式是为了方便后期继续扩展别的继电器。

客户端实现

/project/client/index.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><div id="info" style="margin-bottom: 16px; width: 100%;height: 300px;border: 1px solid red;"> </div><button id="btn">手动查询</button><button id="open">开灯</button><button id="close">关灯</button>
</body></html>
<script type="module">import { io } from "https://cdn.socket.io/4.4.1/socket.io.esm.min.js";const server = "ws://" + location.host;const socket = io(server, {reconnectionDelayMax: 10000,auth: {},query: { }});function onSwitchs(data) {console.log("[switchs]接收:", data);document.querySelector("#info").innerHTML = JSON.stringify(data, null, 4)}socket.on("switchs", onSwitchs);const btn = document.querySelector("#btn");const obtn = document.querySelector("#open");const cbtn = document.querySelector("#close");btn.addEventListener("click", function () {// 发送输入框数据socket.emit("query")})obtn.addEventListener("click", function () {// 发送输入框数据socket.emit("set", { id: "desklamp", data: "1" })})cbtn.addEventListener("click", function () {// 发送输入框数据socket.emit("set", { id: "desklamp", data: "0" })})
</script>

页面如下:

浏览器访问:127.0.0.1:3005

image.png

代码分析

代码比较简单,只是实现了控制开关和查询的按钮。

硬件代码

/project/main/main.ino

#include <ESP8266WiFi.h>
#include <Arduino.h>
#include <ArduinoJson.h>
#include <WebSocketsClient.h>
#include <SocketIOclient.h>
#include <Hash.h>#ifndef STASSID
#define STASSID "oldwang"
#define STAPSK "wifi密码"
#endif// 继电器引脚 高电平断开,低电平接通
int jdqPot = 12;// 开关状态   "0"关闭, "1" 开启
String switchStatus = "0";const char* ssid = STASSID;
const char* password = STAPSK;const char* websockets_server_host = "192.168.xx.xx"; //"www.xxx.top";  // 服务器名
const uint16_t websockets_server_port = 3005;         // 端口SocketIOclient socketIO; StaticJsonDocument<1024> iomsg;void socketIOEvent(socketIOmessageType_t type, uint8_t* payload, size_t length) {switch (type) {case sIOtype_DISCONNECT:Serial.printf("[IOc] Disconnected!\n");break;case sIOtype_CONNECT:Serial.printf("[IOc] Connected to url: %s\n", payload);// join default namespace (no auto join in Socket.IO V3)socketIO.send(sIOtype_CONNECT, "/");break;case sIOtype_EVENT:// 获取到服务的消息后打印出来Serial.printf("[IOc] get event: %s\n", payload);deserializeJson(iomsg, &(*payload));if (iomsg[0] == "switchs") { if (iomsg[1]["desklamp"] == "1") {// 开灯switchStatus = "1";}else{// 关灯 switchStatus = "0";}} break;case sIOtype_ACK:Serial.printf("[IOc] get ack: %u\n", length);hexdump(payload, length);break;case sIOtype_ERROR:Serial.printf("[IOc] get error: %u\n", length);hexdump(payload, length);break;case sIOtype_BINARY_EVENT:Serial.printf("[IOc] get binary: %u\n", length);hexdump(payload, length);break;case sIOtype_BINARY_ACK:Serial.printf("[IOc] get binary ack: %u\n", length);hexdump(payload, length);break;}
}void setup() {// Serial.begin(115200);Serial.begin(9600);Serial.setDebugOutput(true);pinMode(jdqPot, OUTPUT);Serial.println();Serial.println();Serial.print("wifi 连接中 ");Serial.println(ssid);WiFi.mode(WIFI_STA);WiFi.begin(ssid, password);while (WiFi.status() != WL_CONNECTED) {delay(500);Serial.print(".");}Serial.println("");Serial.println("WiFi connected");Serial.println("IP address: ");Serial.println(WiFi.localIP());Serial.println("socketIO begin: ");// server address, port and URLsocketIO.begin(websockets_server_host, websockets_server_port, "/socket.io/?EIO=4");// event handlersocketIO.onEvent(socketIOEvent);
}unsigned long messageTimestamp = 0;
void loop() {socketIO.loop();uint64_t now = millis();//  1分钟向服务器发送一次心跳消息if (now - messageTimestamp > 60000) {// if (now - messageTimestamp > 20000) {messageTimestamp = now;sendMsg("heartbeatMonitoring", "hi");// 测试打开开关// sendMsg("set", "1");// digitalWrite(jdqPot, LOW);}// 根据状态控制继电器状态if (switchStatus == "0") {// 关闭digitalWrite(jdqPot, LOW);} else {// 接通digitalWrite(jdqPot, HIGH);}
}// 发送信息的方法
// @msgName 服务端监听的事件名
// @msg     会当到 data 中进行发送
void sendMsg(char msgName[100], char msg[100]) {// 创建一个 scoket.io json 消息DynamicJsonDocument doc(1024);JsonArray array = doc.to<JsonArray>();// 消息名称// ps: socket.on('event_name', ....array.add(msgName);// 添加消息内容JsonObject param = array.createNestedObject();param["data"] = msg;param["id"] = "desklamp";// JSON to String (serializion)String output;serializeJson(doc, output);// Send eventsocketIO.sendEVENT(output);Serial.print("send:");Serial.println(output);
}

注意

arduinoWebSockets 这个依赖必须去 github 下载,不能在 Arduino IDE 中直接搜索安装,因为不是一个作者写的,不一样。下载地址:https://github.com/Links2004/arduinoWebSockets 。

下载后的依赖放到IDE首选项中设置的地址中即可,还不懂就百度一下安装 arduino 依赖库。

ArduinoJson 依赖直接在 Arduino IDE 中搜索安装即可

IDE 中开发版选择和 nodemcu 一样:

image.png

引脚接线

D1继电器led
5VDC+
3.3VNO
GNDDC-负极
D6IN
COM正极

附上一张 nodemcu d1 引脚和 arduino 引脚的对应图

d1 引脚图.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/337915.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Qt GraphicView 解析 CIM/G 电力接线图文件

本文讲述了如何使用Qt的框架来渲染展示标准的CIM/G格式的图形文件&#xff0c;也就是公用信息模型&#xff08;common information model&#xff0c;CIM&#xff09;中的G文件部分的内容。这是一种电力系统图形的交换规则&#xff0c;用于电网图形交换。 [by amjieker] CIM/G …

⌈ 传知代码 ⌋ 命名实体识别

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

vue3组件传值---vue组件通过属性,事件和provide,inject进行传值

通过属性传值&#xff08;父传子&#xff09; vue的组件具有props自建属性&#xff08;自定义名称&#xff0c;类似于class&#xff0c;id的属性&#xff09;&#xff0c;通过这个属性&#xff0c;父组件可以向子组件传递参数&#xff0c;从而实现组件之间的信息传递&#xff0…

深入探讨npm、Yarn、pnpm和npx之间的区别

前端生态系统是一个快速发展的领域&#xff0c;充满了各种工具和技术。对于开发人员来说&#xff0c;跟上这些创新可能是一项艰巨的挑战。 在本文中&#xff0c;我们将深入探讨npm、Yarn、pnpm和npx之间的区别&#xff0c;帮助你理解每个工具的不同之处。 包管理器比较 npm …

原码一位乘法(计算机组成原理)

算法原理 每次将1位乘数所对应的部分积与原部分积的“累积和”相加&#xff0c;并移位 设置寄存器 存放部分积累积和、乘积高位存放被乘数存放乘数、乘积低位 法则 乘积的数值位俩数绝对值之积&#xff1b;符号位 位 俩数符号位进行异或&#xff0c;即 p x ⊕ y 步骤 设…

你认识nginx吗,nginx是做什么的,nginx可以做什么 --1)nginx介绍

一.Nginx 介绍 Nginx&#xff08;发音同engine x&#xff09;是一个异步框架的 Web 服务器&#xff0c;也可以用作反向代理&#xff0c;负载平衡器 和 HTTP 缓存。该软件由 Igor Sysoev 创建&#xff0c;并于2004年首次公开发布。同名公司成立于2011年&#xff0c;以提供支持。…

【原型模式】详解

一.概念 原型模式是一种创建型设计模式&#xff0c;它的主要思想是通过复制现有对象来创建新对象&#xff0c;而不是通过实例化一个类来创建。在原型模式中&#xff0c;我们称被复制的对象为原型&#xff08;Prototype&#xff09;&#xff0c;新创建的对象为克隆体&#xff0…

Redis缓存(笔记一:缓存介绍和数据库启动)

目录 1、NoSQL数据库简介 2、Redis介绍 3、Redis(win系统、linux系统中操作) 3.1 win版本Redis启动 3.2 linux版本Redis启动 1、NoSQL数据库简介 技术的分类&#xff1a;&#xff08;发展史&#xff09; 1、解决功能性的问题&#xff1a;Java、Jsp、RDBMS、Tomcat、HTML、…

IC开发——VCS基本用法

1. 简介 VCS是编译型verilog仿真器&#xff0c;处理verilog的源码过程如下&#xff1a; VCS先将verilog/systemverilog文件转化为C文件&#xff0c;在linux下编译链接生成可执行文件&#xff0c;在linux下运行simv即可得到仿真结果。 VCS使用步骤&#xff0c;先编译verilog源…

【计算机毕业设计】388微信小程序足球赛事及队伍管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

开发一个comfyui的自定义节点-支持输入中文prompt

文章目录 目标功能开发环境实现过程翻译中文CLIP编码拓展仓库地址完整代码目标功能 目前comfyui的prompt提示词输入节点 CLIP Text Encode 只支持输入英文的prompt,而有时候我们需要自己制定一些prompt,所以就得将我们想要的提示词翻译为英文后再复制粘贴到该节点的输入框中…

Java的JDK环境变量配置(Windows)

只写了需要配置的环境变量 注&#xff1a;从JDK1.5开始&#xff0c;配置Java环境变量时&#xff0c;不再需要配置CLASSPATH&#xff0c;只需要配置JAVA_HOME和Path 1、配置JAVA_HOME 找到自己的JDK位置&#xff0c;我这里是 C:\dev\java\jdk-17.0.119在环境变量-系统变量中&…

ubuntu系统下安装mysql的步骤详解

一、下载安装包 下载地址&#xff1a; https://dev.mysql.com/downloads/repo/apt 跳转到这个页面&#xff1a; 直接点击Download。 直接点击最下面的开始下载安装包即可。 二、将安装包下载到ubuntu系统中 先将用户切换成root用户&#xff0c;把下载好的安装包复制到桌面上&…

互联网简史-分久必合,合久必分

六一儿童节&#xff0c;给孩子们讲讲互联网的历史。 任何当代技术都是古老技术的重组&#xff0c;这是真的。我从电话网络开始&#xff0c;两幅图完事。电波可以承载语音作为最开始&#xff0c;后面的事自然而然&#xff1a; 说实话&#xff0c;网络这种事&#xff0c;它的 …

各大平台取消一年期免费SSL证书后,如何申请超长期免费SSL证书

一&#xff1a;为什么一定要用SSL证书 SSL证书是一种提供网络安全的协议&#xff0c;主要作用是提供对用户和服务器的认证以及确保传送的数据进行加密和隐藏&#xff0c;从而保证数据的完整性和安全性。网站安装SSL证书后就可以实现HTTPS访问&#xff0c;消除网站访问不安全提…

从0开始制作微信小程序

目录 前言 正文 需要事先准备的 需要事先掌握的 什么是uniapp 平台应用的分类方式 什么是TypeScript 创建项目 项目文件作用 源码地址 尾声 &#x1f52d; Hi,I’m Pleasure1234&#x1f331; I’m currently learning Vue.js,SpringBoot,Computer Security and so on.&#x1…

一、大模型推理

https://github.com/hiyouga/LLaMA-Factory/blob/main/README_zh.md https://github.com/hiyouga/LLaMA-Factory/blob/main/examples/README_zh.md 安装 v7.1 https://github.com/hiyouga/LLaMA-Factory/releases/tag/v0.7.1 git clone --depth 1 https://github.com/hiyoug…

TS38.300中的切换流程(很一般)

本文根据3GPP R18 TS 38.300第9.2.3节整理 切换(Handover)是移动终端(UE)进入RRC_CONNECTED状态后在不同服务小区(Cell)之间保持与网络联系唯一手段&#xff0c;期间首先通过控制面(C-Plane)进行无线测量、切换协商及触发等&#xff1b;为此3GPP在TS38.300中定义如下。 RAN系统…

【六一儿童节】的科技奇幻旅程:解锁【机器学习】与【人工智能】的无限创意

目录 一、机器学习与人工智能简介 二、六一儿童节的特殊意义 三、项目概述&#xff1a;智能绘画助手 四、技术栈和工具 五、数据准备 六、模型训练 1. 数据预处理 2. 构建和训练模型 七、智能绘画助手的实现 1. 搭建Flask应用 2. 客户端界面 八、扩展功能与优化 1…

【算法】贪心算法——柠檬水找零

题解&#xff1a;柠檬水找零(贪心算法) 目录 1.题目2.题解3.参考代码4.证明5.总结 1.题目 题目链接&#xff1a;LINK 2.题解 分情况讨论 贪心算法 当顾客为5元时&#xff0c;收下当顾客为10元时&#xff0c;收下10元并找回5元当顾客为20元时&#xff0c;收下20元并找回10…