一、项目概述
在物联网(IoT)时代,智能设备的远程控制变得越来越重要。本文介绍了一个构建智能设备控制面板的项目,允许用户通过 Web 应用来控制多个 ESP32 设备。用户可以通过该面板查看设备列表,实时了解设备状态,并对设备进行操作(例如开关、调节亮度等)。
二、系统架构
为了满足项目需求,我们设计了一套系统架构,包括前端、后端、数据库和通信协议,如下图所示:
用户 前端 后端 数据库 ESP32 设备
1. 单片机和设备
- 
单片机:ESP32 
- 
设备:智能灯光、智能插座等 
2. 通信协议
- 
WebSocket:用于实时通信,保证设备状态的实时更新 
- 
RESTful API:用于设备的管理操作 
3. 技术栈
- 
前端:React、Vue.js 或 Angular(本文选择 React) 
- 
后端:Node.js、Flask 或 Django(本文选择 Node.js) 
- 
数据库:MongoDB 或 Firebase(本文选择 MongoDB) 
三、环境搭建
1. 前端环境
安装 React 开发环境:
            
            
              bash
              
              
            
          
          npx create-react-app smart-device-control-panel
cd smart-device-control-panel
npm start2. 后端环境
安装 Node.js 和 Express:
            
            
              bash
              
              
            
          
          mkdir backend
cd backend
npm init -y
npm install express mongoose body-parser ws3. 数据库环境
安装 MongoDB:
            
            
              bash
              
              
            
          
          # 对于 macOS 用户
brew tap mongodb/brew
brew install mongodb-community@5.0
# 启动 MongoDB
brew services start mongodb/brew/mongodb-community四、代码实现
1. 前端代码
在 src 目录下创建组件和服务,主要包括设备列表管理、设备控制和实时状态更新。
设备列表组件
DeviceList 组件用于显示所有设备的列表。
            
            
              js
              
              
            
          
          // src/components/DeviceList.js
import React from 'react';
function DeviceList({ devices }) {
  return (
    <div>
      <h2>设备列表</h2>
      <ul>
        {devices.map((device) => (
          <li key={device.id}>
            {device.name} - {device.status}
          </li>
        ))}
      </ul>
    </div>
  );
}
export default DeviceList;设备控制组件
DeviceControl 组件用于控制设备的状态。
            
            
              js
              
              
            
          
          // src/components/DeviceControl.js
import React, { useState } from 'react';
function DeviceControl({ onControl }) {
  const [deviceId, setDeviceId] = useState('');
  const [command, setCommand] = useState('');
  const handleSubmit = (e) => {
    e.preventDefault();
    if (deviceId && command) {
      onControl(deviceId, command);
    }
  };
  return (
    <div>
      <h2>设备控制</h2>
      <form onSubmit={handleSubmit}>
        <div>
          <label htmlFor="deviceId">设备 ID:</label>
          <input
            type="text"
            id="deviceId"
            value={deviceId}
            onChange={(e) => setDeviceId(e.target.value)}
          />
        </div>
        <div>
          <label htmlFor="command">指令:</label>
          <input
            type="text"
            id="command"
            value={command}
            onChange={(e) => setCommand(e.target.value)}
          />
        </div>
        <button type="submit">发送</button>
      </form>
    </div>
  );
}
export default DeviceControl;服务模块
deviceService 模块用于与后端进行通信。
            
            
              js
              
              
            
          
          // src/services/deviceService.js
const API_URL = 'http://localhost:3000';
export async function getDevices() {
  const response = await fetch(`${API_URL}/devices`);
  return response.json();
}
export async function controlDevice(id, command) {
  await fetch(`${API_URL}/control/${id}`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ command }),
  });
}
export function subscribeToDeviceUpdates(callback) {
  const ws = new WebSocket('ws://localhost:8080');
  ws.onmessage = (event) => {
    const device = JSON.parse(event.data);
    callback(device);
  };
}主应用组件
App 组件是主应用组件,负责管理设备列表和控制操作。
            
            
              js
              
              
            
          
          // src/App.js
import React, { useState, useEffect } from 'react';
import DeviceList from './components/DeviceList';
import DeviceControl from './components/DeviceControl';
import { getDevices, controlDevice, subscribeToDeviceUpdates } from './services/deviceService';
function App() {
  const [devices, setDevices] = useState([]);
  useEffect(() => {
    getDevices().then(setDevices);
    subscribeToDeviceUpdates((updatedDevice) => {
      setDevices((prevDevices) =>
        prevDevices.map((device) => (device.id === updatedDevice.id ? updatedDevice : device))
      );
    });
  }, []);
  const handleControl = (id, command) => {
    controlDevice(id, command);
  };
  return (
    <div className="App">
      <DeviceList devices={devices} />
      <DeviceControl onControl={handleControl} />
    </div>
  );
}
export default App;2. 后端代码
在 backend 目录下创建服务器和路由,处理设备管理和控制请求。
设备模型
Device 模型定义设备的结构。
            
            
              js
              
              
            
          
          // backend/models/Device.js
const mongoose = require('mongoose');
const deviceSchema = new mongoose.Schema({
  name: String,
  status: String,
});
module.exports = mongoose.model('Device', deviceSchema);服务器和路由
server.js 文件设置服务器,处理设备列表和控制请求,并通过 WebSocket 实现实时状态更新。
            
            
              js
              
              
            
          
          // backend/server.js
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const WebSocket = require('ws');
const Device = require('./models/Device');
const app = express();
const wss = new WebSocket.Server({ port: 8080 });
app.use(bodyParser.json());
// RESTful API
app.get('/devices', async (req, res) => {
  const devices = await Device.find();
  res.json(devices);
});
app.post('/control/:id', async (req, res) => {
  const { id } = req.params;
  const { command } = req.body;
  // 发送控制命令给设备
  // 这里可以通过与 ESP32 设备通信的特定实现来发送命令
  const device = await Device.findById(id);
  if (device) {
    // 更新设备状态逻辑
    device.status = command.status; // 假设 command 包含 status 属性
    await device.save();
    // 通知所有 WebSocket 客户端
    wss.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(JSON.stringify(device));
      }
    });
    res.sendStatus(200);
  } else {
    res.sendStatus(404);
  }
});
// WebSocket
wss.on('connection', (ws) => {
  ws.on('message', (message) => {
    const { id, status } = JSON.parse(message);
    Device.findByIdAndUpdate(id, { status }, { new: true }, (err, device) => {
      if (err) return;
      wss.clients.forEach((client) => {
        if (client.readyState === WebSocket.OPEN) {
          client.send(JSON.stringify(device));
        }
      });
    });
  });
});
app.listen(3000, () => {
  console.log('后端服务器在 http://localhost:3000 运行');
});代码说明
前端代码说明
- DeviceList 组件 (DeviceList.js)
- 
用于显示设备列表。 
- 
从 props获取设备数据,并生成设备列表的 HTML 元素。
- DeviceControl 组件 (DeviceControl.js)
- 
用于控制设备。 
- 
包含设备 ID 和控制指令的输入框,并在表单提交时调用 onControl函数。
- deviceService 模块 (deviceService.js)
- 
getDevices函数:从后端获取设备列表。
- 
controlDevice函数:向后端发送控制设备的指令。
- 
subscribeToDeviceUpdates函数:通过 WebSocket 订阅设备状态更新。
- App 组件 (App.js)
- 
主应用组件,管理设备列表和控制操作。 
- 
使用 useEffect钩子在组件挂载时获取设备列表,并订阅设备状态更新。
- 
handleControl函数调用controlDevice发送控制指令。
后端代码说明
- 设备模型 (Device.js)
- 定义设备的 Mongoose 模型,包含 name和status属性。
- 服务器和路由 (server.js)
- 
监听客户端连接和消息,更新设备状态并通知所有客户端。 
- 
GET /devices:获取所有设备。
- 
POST /control/:id:控制指定 ID 的设备,并通过 WebSocket 通知所有客户端。
- 
使用 Express 创建服务器,并连接到 MongoDB 数据库。 
五、项目总结
在本项目中,我们构建了一个智能设备控制面板,用户可以通过 Web 应用控制多个 ESP32 设备。项目使用 React 构建前端,用 Node.js 和 Express 搭建后端,使用 MongoDB 存储设备数据,并通过 WebSocket 实现设备状态的实时更新。项目架构清晰,功能完备,为用户提供了良好的体验。