1 前言
本文实现使用 nodejs、express、scoket.io、nodemcu 以实现在网页、nodemcu、服务器之间实时通信。 使用 Arduino IDE 进行编码。
综上述所,我们需要写一个 scoket 服务的代码,一个 nodemuc 板子的代码,一个网页客户端的代码。
nodemcu 使用 Arduino IDE 编写,所以这里使用的是 C 的语法。
服务端使用 nodejs 编写。
客户端(网页)使用 js 编写。
2、效果展示
1、网页发送信息到服务器->服务器立马将信息发送到 nodemcu
2、nodemcu 也会在一些时候向服务器发送信息
3、文件夹结构
test-nodemcu-scoket #根文件夹
scoketServer #服务端
node_modules
src
index.js 服务端代码入口
client #服务端
index.html #客户端代码入口
test-nodemcu-scoket.ino #nodemcu
4、服务端代码编写
在写别的两个端之前必须要有一个服务,所以先用 express 撸一个服务吧!
下面代码没有任何特殊的,直接使用 express、socket.io 将服务搭起来即可
以下是使用的依赖版本
node@10.16.1
express@4.17.1
socket.io@4.5.4
将 3002 端口用于服务端。
const bodyParser = require("body-parser");
const express = require("express");
const app = express();
const http = require("http");
const { Server } = require("socket.io");
const server = http.createServer(app);
const port = 3002;
// 解析文件使用。本项目暂时未用到
// for parsing application/json
app.use(bodyParser.json({ limit: "50mb" }));
// for parsing application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
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();
});
app.get(`/hello`,(req,res) => {
console.log(`收到一个请求:`,req.url)
res.json({ data: "hello" });
});
const io = new Server(server,{
allowEIO3: true,
credentials: true,
cors: {},
});
io.on("connection",(socket) => {
try {
console.log("一个新的 scoket 连接");
io.emit("serverMsg","hi!");
socket.on("clientMsg",function (data) {
console.log(`收到客户端信息:`,data);
io.emit("mcuMsg",data);
});
socket.on("mcuMsg",function (data) {
const date = new Date();
const YY = date.getFullYear();
const MM = date.getMonth() + 1;
const DD = date.getDate();
const H = date.getHours();
const M = date.getMinutes();
const S = date.getSeconds();
// linux 上面有8个时差
console.log(`[${YY}-${MM}-${DD} ${H + 8}:${M}:${S}] 收到mcu信息:`,data);
});
// 发生错误时触发
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}`);
});
启动服务后访问康康。
接着我们写客户端代码来调试ws接口
5、客户端代码编写
发送的数据格式: { data:"xxx" }, 本文中所有的数据交互都用这种格式。
<!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>
<input type="text" id="text">
<button id="btn">发送信息</button>
</body>
</html>
<script type="module">
import { io } from "https://cdn.socket.io/4.4.1/socket.io.esm.min.js";
const server = "ws://localhost:3002/"
// const server = "ws://线上地址,不给你们看.top:3002/"
const socket = io(server,{
reconnectionDelayMax: 10000,
auth: {},
query: { "my-key": "my-value" }
});
socket.on("serverMsg",(data) => {
console.log("接收到服务端信息:", data);
});
const btn = document.querySelector("#btn"),
input = document.querySelector("#text");
btn.addEventListener("click",function () {
// 发送输入框数据
socket.emit("clientMsg", { data: input.value })
})
</script>
6、nodemcu 代码编写
这里需要用到一个第三方的库,在 Arduino IDE 中下载不到。自行到 github 下载即可。 arduinoWebSockets:github.com/Links2004/a…
ArduinoJson 这个库直接在 Arduino IDE 安装即可。
/*
* 客户端依赖:https://github.com/Links2004/arduinoWebSockets
*/
#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 "oldwang520"
#endif
const char* ssid = STASSID;
const char* password = STAPSK;
const char* websockets_server_host = "www.不给你们看.top"; // 服务器名
const uint16_t websockets_server_port = 3002; // 端口
SocketIOclient socketIO;
#define USE_SERIAL Serial
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);
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.setDebugOutput(true);
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 URL
socketIO.begin(websockets_server_host, websockets_server_port, "/socket.io/?EIO=4");
// event handler
socketIO.onEvent(socketIOEvent);
}
unsigned long messageTimestamp = 0;
void loop() {
socketIO.loop();
uint64_t now = millis();
// 发送案例:1分钟向服务器发送一次消息
if (now - messageTimestamp > 60000) {
messageTimestamp = now;
sendMsg("mcuMsg", "hi, i'm oldWang.");
}
}
// 发送信息的方法
// @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;
// JSON to String (serializion)
String output;
serializeJson(doc, output);
// Send event
socketIO.sendEVENT(output);
Serial.print("send:");
Serial.println(output);
}
7、链接
看完点个赞~~~