You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否通过WebSocket在指定时间触发树莓派GPIO引脚?Node.js求助

解决树莓派Node.js闹钟触发GPIO的问题

看起来你现在的代码逻辑有点错位——前端的socket事件发送时机不对,后端也没处理定时触发GPIO的核心逻辑,而是一收到事件就直接改GPIO,这肯定没法实现闹钟到点触发的效果。我来帮你一步步修正:

先梳理核心流程

我们要实现的是:

  1. 前端选好闹钟时间,点击「Set Alarm」把时间发给后端
  2. 后端计算当前时间到闹钟时间的延迟,设置定时器
  3. 时间到后,后端触发GPIO输出高电平,同时通知前端触发闹钟(播放声音、显示控制按钮)
  4. 前端点击「Stop Alarm」或「Snooze」时,通知后端关闭GPIO,snooze的话还要重新设置定时器

第一步:修复前端代码

首先,你需要在HTML里引入socket.io的客户端脚本(后端的socket.io会自动提供这个文件,不用额外下载),然后修正socket事件的发送逻辑:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Alarm Clock</title>
    <!-- 引入socket.io客户端 -->
    <script src="/socket.io/socket.io.js"></script>
</head>
<body>
    <input type="datetime-local" id="alarmTime" />
    <button onclick="setAlarm(this);" id="alarmButton">Set Alarm</button>
    <div id="alarmOptions" style="display: none;">
        <button onclick="snooze();">Snooze 5 minutes</button>
        <button onclick="stopAlarm();">Stop Alarm</button>
    </div>

    <script>
        var alarmSound = new Audio('alarm.mp3');
        var alarmTimer;
        // 初始化socket连接
        var socket = io();

        // 监听后端发来的闹钟触发通知
        socket.on('alarmTriggered', function() {
            initAlarm();
        });

        function setAlarm(button) {
            var ms = document.getElementById('alarmTime').valueAsNumber;
            if(isNaN(ms)) {
                alert('Invalid Date');
                return;
            }
            var alarmTime = new Date(ms);
            var differenceInMs = alarmTime.getTime() - (new Date()).getTime();
            
            if(differenceInMs < 0) {
                alert('Specified time is already passed');
                return;
            }

            // 把闹钟时间戳发给后端
            socket.emit('setAlarm', ms);
            
            // 更新按钮状态
            button.innerText = 'Cancel Alarm';
            button.setAttribute('onclick', 'cancelAlarm(this);');
        };

        function cancelAlarm(button) {
            // 通知后端取消闹钟
            socket.emit('cancelAlarm');
            // 重置前端状态
            button.innerText = 'Set Alarm';
            button.setAttribute('onclick', 'setAlarm(this);');
        };

        function initAlarm() {
            alarmSound.play();
            document.getElementById('alarmOptions').style.display = '';
            console.log("ALAAAARM");
        };

        function stopAlarm() {
            alarmSound.pause();
            alarmSound.currentTime = 0;
            document.getElementById('alarmOptions').style.display = 'none';
            // 通知后端关闭GPIO并取消闹钟
            socket.emit('stopAlarm');
            cancelAlarm(document.getElementById('alarmButton'));
        };

        function snooze() {
            // 计算5分钟后的时间戳
            var snoozeTime = new Date().getTime() + 5 * 60 * 1000;
            // 发给后端设置新的闹钟
            socket.emit('setAlarm', snoozeTime);
            // 停止当前声音,隐藏控制按钮
            alarmSound.pause();
            alarmSound.currentTime = 0;
            document.getElementById('alarmOptions').style.display = 'none';
            // 更新按钮状态
            document.getElementById('alarmButton').innerText = 'Cancel Alarm';
            document.getElementById('alarmButton').setAttribute('onclick', 'cancelAlarm(this);');
        };
    </script>
</body>
</html>

第二步:修复后端代码

后端需要维护一个定时器,处理闹钟的定时触发,同时监听前端的各种事件来控制GPIO和定时器:

var http = require('http').createServer(handler);
var fs = require('fs');
var io = require('socket.io')(http);
var Gpio = require('onoff').Gpio;
var LED = new Gpio(4, 'out'); // 假设你控制的是GPIO4
var alarmTimer = null; // 用于存储闹钟定时器

http.listen(8080, () => {
    console.log('Server running on http://raspberrypi.local:8080');
});

function handler(req, res) {
    fs.readFile(__dirname + '/test.html', function(err, data) {
        if (err) {
            res.writeHead(404, {'Content-Type': 'text/html'});
            return res.end("404 Not Found");
        }
        res.writeHead(200, {'Content-Type': 'text/html'});
        res.write(data);
        return res.end();
    });
}

io.sockets.on('connection', function(socket) {
    console.log('A client connected');

    // 监听前端设置闹钟的事件
    socket.on('setAlarm', function(alarmTimestamp) {
        // 如果已有定时器,先取消
        if (alarmTimer) {
            clearTimeout(alarmTimer);
        }
        var now = new Date().getTime();
        var delay = alarmTimestamp - now;
        
        if (delay > 0) {
            alarmTimer = setTimeout(() => {
                // 时间到,触发GPIO为高电平
                LED.writeSync(1);
                // 通知前端触发闹钟
                socket.emit('alarmTriggered');
            }, delay);
            console.log(`Alarm set for ${new Date(alarmTimestamp)}`);
        }
    });

    // 监听取消闹钟事件
    socket.on('cancelAlarm', function() {
        if (alarmTimer) {
            clearTimeout(alarmTimer);
            alarmTimer = null;
            // 确保GPIO处于低电平
            LED.writeSync(0);
            console.log('Alarm cancelled');
        }
    });

    // 监听停止闹钟事件
    socket.on('stopAlarm', function() {
        if (alarmTimer) {
            clearTimeout(alarmTimer);
            alarmTimer = null;
        }
        // 关闭GPIO
        LED.writeSync(0);
        console.log('Alarm stopped');
    });

    // 客户端断开连接时清理资源
    socket.on('disconnect', function() {
        if (alarmTimer) {
            clearTimeout(alarmTimer);
            alarmTimer = null;
        }
        LED.writeSync(0);
        console.log('Client disconnected');
    });
});

// 程序退出时清理GPIO
process.on('SIGINT', function() {
    LED.writeSync(0);
    LED.unexport();
    if (alarmTimer) {
        clearTimeout(alarmTimer);
    }
    process.exit();
});

关键修正点说明

  1. 前端socket逻辑:

    • 引入了socket.io客户端脚本,确保能和后端建立连接
    • 设置闹钟时把时间戳发给后端,由后端处理定时逻辑(后端的时间更可靠,避免前端页面关闭或刷新导致定时器失效)
    • 监听后端的alarmTriggered事件,触发前端的闹钟音效和UI变化
    • 停止/ snooze时通知后端控制GPIO并管理定时器
  2. 后端逻辑:

    • 新增了alarmTimer变量来维护闹钟定时器,确保同一时间只有一个闹钟在运行
    • 收到setAlarm事件后计算延迟,设置定时器,到点后触发GPIO并通知前端
    • 处理取消、停止闹钟的事件,清理定时器并重置GPIO状态
    • 客户端断开连接时自动清理资源,避免GPIO一直处于高电平

测试步骤

  1. 确保你已经安装了所需依赖:npm install onoff socket.io
  2. 把前端代码保存为test.html,和后端代码放在同一目录
  3. 运行后端:node your-server-file.js
  4. 在浏览器访问树莓派的IP:8080,设置闹钟时间,等待触发即可

内容的提问来源于stack exchange,提问作者Onjieee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:29:57