手搓开机棒:使用.Net nanoFramework 实现WOL唤醒远程开机

发布于:2024-12-21 ⋅ 阅读:(9) ⋅ 点赞:(0)

本文将介绍如何使用ESP32硬件制作一个开机棒,通过.Net nanoFramework实现WOL(Wake-on-LAN)功能,发送WOL数据包来唤醒远程计算机。

1. 背景

在之前的文章中,我们介绍了如何使用.Net nanoFramework相关的库来实现一些功能,比如读取传感器数据、控制LED灯、OLED显示等。也介绍了通过.NET实现WOL(Wake-on-LAN)功能,并在NAS或通过ASP.NET进行部署。可以能有的小伙伴没有NAS或者服务器,但是有ESP32硬件,那么我们可以使用ESP32硬件制作一个开机棒,通过发送WOL数据包来唤醒远程计算机。在这篇文章中,我们将介绍如何使用.Net nanoFramework实现WOL(Wake-on-LAN)功能,通过发送WOL数据包来唤醒远程计算机。

相关文章:

以上的文章包含了基本的WOL相关实现和介绍以及.Net nanoFramework基础开发知识,如果你对.Net nanoFramework不熟悉,建议先阅读这些文章,可以帮助你更好的读懂本文的内容。

wol

2. 实现方法

在工作状态灯的应用文章中,我们介绍了一个 ProjectImprovWifi 的示例项目,这个示例包含了基本的工作状态灯控制,我们可以基于这个项目框架来快速实现WOL功能。

WOL的实现原理是通过网络发送一个特定的数据包(Magic Packet)到目标计算机的MAC地址,目标计算机就会被唤醒。在.Net nanoFramework中,我们可以使用 nanoFramework.System.Net.Sockets.UdpClient 库来实现UDP数据包的发送。

下面的代码示例演示了如何使用.Net nanoFramework实现WOL功能,这里需要注意的是,因为环境的限制,UdpClient 的使用,以及没有Replace方法等问题,需要我们对之前.NET的代码进行一些修改:

using System;
using System.Net;
using System.Net.Sockets;

namespace WakeOnLan_ESP32
{
    internal class WakeOnLan
    {

        internal static void Send(string macAddress)
        {
            byte[] magicPacket = CreateMagicPacket(macAddress);
            SendMagicPacket(magicPacket);
        }

        static byte[] CreateMagicPacket(string macAddress)
        {
            byte[] macBytes = ParseMacAddress(macAddress);
            byte[] magicPacket = new byte[6 + (6 * 16)];

            for (int i = 0; i < 6; i++)
            {
                magicPacket[i] = 0xFF;
            }

            for (int i = 6; i < magicPacket.Length; i += 6)
            {
                Array.Copy(macBytes, 0, magicPacket, i, 6);
            }

            return magicPacket;
        }

        static byte[] ParseMacAddress(string macAddress)
        {
            if (macAddress.Length == 17)
            {
                macAddress = macAddress.Substring(0, 2) + macAddress.Substring(3, 2) + macAddress.Substring(6, 2) + macAddress.Substring(9, 2) + macAddress.Substring(12, 2) + macAddress.Substring(15, 2);
            }

            if (macAddress.Length != 12)
            {
                throw new ArgumentException("Invalid MAC address.");
            }

            byte[] macBytes = new byte[6];

            for (int i = 0; i < 6; i++)
            {
                macBytes[i] = Convert.ToByte(macAddress.Substring(i * 2, 2), 16);
            }

            return macBytes;
        }

        static void SendMagicPacket(byte[] magicPacket)
        {
            UdpClient udpClient = new UdpClient();
            udpClient.Connect(IPAddress.Broadcast, 9);
            udpClient.Send(magicPacket);
            udpClient.Close();
            Console.WriteLine("Magic packet sent.");
        }

    }
}

在这个示例中,我们定义了一个 WakeOnLan 类,其中包含了 Send 方法,用于发送WOL数据包。在 Send 方法中,我们首先调用 CreateMagicPacket 方法创建一个Magic Packet,然后调用 SendMagicPacket 方法发送数据包。

3. 页面设计

完成了核心的唤醒代码后,我们需要让用户输入目标计算机的MAC地址,然后点击按钮发送WOL数据包。这里我们可以使用OLED显示屏来显示提示信息和用户输入的MAC地址。这里可以使用 ESP32 创建一个Web服务器,通过浏览器输入MAC地址,然后发送WOL数据包。

这里我们需要用到nanoFramework.System.Net.Http库,这个库可以用来创建一个简单的Web服务器,接收用户的输入,然后发送WOL数据包。

string responseString =
    "<HTML><HEAD>" +
    "<meta name='viewport' content='width=device-width, initial-scale=1'>" +
    "<style>" +
    "body { font-family: Arial, sans-serif; margin: 20px;text-align: center;}" +
    "h2 { color: #333; }" +
    "form { margin-top: 20px; }" +
    "input[type='text'] { padding: 10px; border: 1px solid #ccc; border-radius:4px 0 0 4px; width: 250px; }" +
    "input[type='submit'] { padding: 10px 20px; border: none; border-radius:0 4px 4px 0; background-color: #4CAF50; color: white; cursor: pointer; }" +
    "input[type='submit']:hover { background-color: #45a049; }" +
    "p { line-height: 1.6; }" +
    "a { color: #1E90FF; text-decoration: none; }" +
    "a:hover { text-decoration: underline; }" +
    "#mac-list { margin: 20px auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); width: 100%; max-width: 350px; }" +
    "#mac-list ul { list-style-type: none; padding: 0; }" +
    "#mac-list li { padding: 5px 0; border-bottom: 1px solid #ccc; }" +
    "</style>" +
    "</HEAD><BODY>" +
    "<h2>ESP32 WakeOnLan</h2>" +
    "<p>Click the button to send a Wake On LAN packet.</p>" +
    "<form method='get' action='/wol' οnsubmit='return validateAndFormatMac()'>" +
    "<input type='text' id='mac' name='mac' value='' />" +
    "<input type='submit' value='Wake On LAN' />" +
    "</form>" +
    "<div id='mac-list'>" +
    "<h3>Stored MAC Addresses</h3>" +
    "<ul id='list'></ul>" +
    "</div>" +
    "<script>" +
    "function validateAndFormatMac() {" +
    "    var macInput = document.getElementById('mac');" +
    "    var mac = macInput.value;" +
    "    var macRegex = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;" +
    "    if (!macRegex.test(mac)) {" +
    "        alert('Invalid MAC address format. Please use XX:XX:XX:XX:XX:XX or XX-XX-XX-XX-XX-XX.');" +
    "        return false;" +
    "    }" +
    "    mac = mac.replace(/[:-]/g, '');" +
    "    macInput.value = mac;" +
    "    storeMac(mac);" +
    "    return true;" +
    "}" +
    "function storeMac(mac) {" +
    "    var macs = JSON.parse(localStorage.getItem('macs')) || [];" +
    "    macs.push(mac);" +
    "    localStorage.setItem('macs', JSON.stringify(macs));" +
    "    displayMacs();" +
    "}" +
    "function displayMacs() {" +
    "    var macs = JSON.parse(localStorage.getItem('macs')) || [];" +
    "    var list = document.getElementById('list');" +
    "    list.innerHTML = '';" +
    "    macs.forEach(function(mac) {" +
    "        var li = document.createElement('li');" +
    "        var a = document.createElement('a'); " +
    "        a.href = '/wol?mac=' + mac;" +
    "        a.textContent = mac;" +
    "        li.appendChild(a);" +
    "        list.appendChild(li);" +
    "    });" +
    "}" +
    "document.addEventListener('DOMContentLoaded', displayMacs);" +
    "</script>" +
    "</BODY></HTML>";

byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);

HttpListener listener = new("http", 80);

listener.Start();

这里的网页内容是硬编码在代码中的,我们可以通过 HttpListener 类来创建一个简单的Web服务器,然后监听用户的输入。用户输入的MAC地址会被存储在本地存储中,然后显示在页面的底部。用户可以点击列表中的MAC地址来发送WOL数据包。

启动服务后,我们简单的使用一个死循环来监听用户的请求,通过判断URL是否包含/wol?mac=来发送WOL数据包,然后返回一个JSON格式的响应。

while (true)
{
    try
    {
        HttpListenerContext context = listener.GetContext();
        var url = context.Request.RawUrl;
        HttpListenerResponse response = context.Response;

        if (url.StartsWith("/wol?mac="))
        {
            var macAddress = url.Substring(9);
            Console.WriteLine($"WOL packet sent to {macAddress}");
            WakeOnLan.Send(macAddress);

            // 输出json格式
            response.ContentType = "application/json";
            var json = System.Text.Encoding.UTF8.GetBytes("{\"status\":\"ok\"}");
            response.ContentLength64 = json.Length;
            response.OutputStream.Write(json, 0, json.Length);
        }
        else
        {
            // 输出默认页面
            response.ContentLength64 = buffer.Length;
            response.OutputStream.Write(buffer, 0, buffer.Length);
        }
        context.Response.Close();
        Console.WriteLine("Web response sent");
        context.Close();
    }
    catch (Exception ex)
    {
        Console.WriteLine("* Error getting context: " + ex.Message + "\r\nSack = " + ex.StackTrace);
    }
}

4. 使用方法

在完成了代码的编写后,我们可以将代码部署到ESP32上。首次使用需要先完成 Improv 蓝牙配网,这里可以通过"Improv 蓝牙配网"微信小程序或者 Improv 官网来完成。建议是使用小程序,这样可以在配网成功后,可以直接显示ESP32的IP地址。

在这里插入图片描述

然后通过浏览器访问ESP32的IP地址,即可看到硬件的 Web 服务,可以在页面上输入MAC地址,然后点击按钮发送WOL数据包。

在这里插入图片描述

5. 总结

在这篇文章中,我们介绍了如何使用.Net nanoFramework实现WOL功能,通过发送WOL数据包来唤醒远程计算机。我们首先实现了核心的WOL代码,然后通过Web服务器来接收用户的输入,最后发送WOL数据包。这样可以方便我们通过浏览器来发送WOL数据包,实现远程开机的功能。

文章介绍的相关代码已开源在GitHub,欢迎查看和收藏。希望这篇文章对你有所帮助,如果有任何问题或建议,欢迎在评论区留言。

如果你对ESP32版本的WOL感兴趣,可以关注“桑榆肖物”,回复“网络唤醒”获取完整源码。

Github 主页:https://github.com/sangyuxiaowu?WT.mc_id=DT-MVP-5005195