M5NanoC6とadafruitで外出先からWake on LANしてみた

# はじめに
Wake on LANによるパソコン起動は同一ネットワーク内からしかできません。
遠隔地からやろうとするとVPN、リモートアクセスサービス、ルーターの設定変更が必要。
知識が足りないし下手するとセキュリティの穴を仕込みかねないので躊躇します。
# どうしようか
超激安ioTスイッチを作ってみる！@umaxiaotian(Uma Xiaotian)様がAdafruit経由でサーボモータを動かすプログラムを公開されています。
サーボモータの代わりにWake on LANパケットを送信するコードを付け足せばいいのでは？
そこで次の構成で電源ONできるようにしました。
スマートフォン→インターネット→Adafruit(MQTT)→インターネット→M5NanoC6(マジックパケット送信)→リモートPC
これならネットワークに穴をあける必要はありません。
# 必要なハード
M5NanoC6　…　通販送料込みで約1,600円(2025/04/12時点)
手持ちの無線LAN親機
# 開発に使ったソフト
Arudono IDE 2.3.5
WakeOnLan 1.1.7 by a7md0.dev
Adafruit MQTT Library 2.5.9 by adafruit
# 準備
### 1.Adafruit側の設定
[超激安ioTスイッチを作ってみる！](https://qiita.com/umaxiaotian/items/d0540141c898b3731507)の通りにAdafruitを設定します。最新は細かな画面が変わっているようですが大筋は同じでした。
### 2.リモートPCのBIOS設定
BIOSのマニュアルを見てWake on LANを有効にします。マザーボードにより項目が違うのでマニュアルのpdfをWakeで検索するとよいでしょう。
同一ネットワーク内のPCよりマジックパケットを送信して、リモートPCが起動することを確認しておいてください。

# サンプルプログラム
SSID,Adafruitの設定を自分の環境に合わせて書き換えて下さい。
機能
1.M5NanoC6の電源ON時にマジックパケットを送信する。
2.AdafruitでスイッチをON⇔OFF切換のたびにマジックパケットを送信する。
```
#include <M5Unified.h>
#include <Adafruit_NeoPixel.h>  // NeoPixel（フルカラーLED）制御用
#include <WiFi.h>           // Wifi制御用ライブラリ
#include <WiFiUdp.h>        // UDPを使ってWake On LANを実装する
#include <WakeOnLan.h>      // Wake On LANのライブラリ
#include "Adafruit_MQTT.h"  // Adafruitを使うためのライブラリ
#include "Adafruit_MQTT_Client.h"

// Adafruit_NeoPixelライブラリでフルカラーLEDを使用するための設定
#define BTN_PIN 9           // 本体ボタン端子
#define RGB_LED_PWR_PIN 19  // NeoPixel電源端子
#define RGB_LED_DATA_PIN 20 // NeoPixel信号端子
#define NUM_LEDS 1  // NeoPixel使用数

// NeoPixel初期化
Adafruit_NeoPixel strip(NUM_LEDS, RGB_LED_DATA_PIN, NEO_GRB + NEO_KHZ800);

// BLUE LEDピンの設定
#define BLUE_LED_PIN 7  // 青色LED端子番号

// Wi-Fi, Wake on LAN設定
WiFiUDP UDP;
WakeOnLan WOL(UDP);
const char* ssid = "YOUR-SSID";  // ここにWi-FiのSSIDを入力
const char* password = "YOUR-PASSWORD";   // ここにWi-Fiのパスワードを入力

// Adfruit 定義
#define MQTT_AIO_SERVER "io.adafruit.com"
#define MQTT_AIO_SERVER_PORT 1883
#define MQTT_AIO_USERNAME "YOUR-Adafruit-USERNAME"  //ここにAdafruitのUSERNAMEを入力
#define MQTT_AIO_KEY "YOUR-Adafruit-aio_KEY"      //ここにAdafruitのAIO KEYを入力
#define READ_TIMEOUT 5000

//MQTTの設定
WiFiClient client;
Adafruit_MQTT_Client mqtt(&client, MQTT_AIO_SERVER, MQTT_AIO_SERVER_PORT, MQTT_AIO_USERNAME, MQTT_AIO_KEY);
Adafruit_MQTT_Subscribe onoff = Adafruit_MQTT_Subscribe(&mqtt, MQTT_AIO_USERNAME "/f/onoff");
Adafruit_MQTT_Publish LightsStatus = Adafruit_MQTT_Publish(&mqtt, MQTT_AIO_USERNAME "/f/LightsStatus");

// MACアドレス設定
const char* pcMacAddress = "XX:XX:XX:XX:XX:XX";  // ここにWake On LANで電源ONにするPCのMACアドレスを入力　区切り:

void setup() {
  // M5nanoC6 setup
  auto cfg = M5.config();      // 設定用の構造体を代入。
  cfg.serial_baudrate = 9600;  // USB通信ボーレート9600bps
  cfg.output_power = true;
  M5.begin(cfg); // 本体初期設定

  // 入力設定
  pinMode(BTN_PIN, INPUT_PULLUP);  // 本体ボタン

  // 出力設定
  pinMode(RGB_LED_PWR_PIN, OUTPUT);    // NeoPixel電源(19)
  digitalWrite(RGB_LED_PWR_PIN, HIGH); // NeoPixel電源をHigh設定
  pinMode(BLUE_LED_PIN, OUTPUT); // 本体LED青 設定

  // NeoPixel初期設定
  strip.begin();  // NeoPixel初期化

  // WiFi接続開始
  strip.setPixelColor(0, strip.Color(255, 0, 0)); // NeoPixel色指定（赤） WiFi接続開始
  strip.show();   // NeoPixel点灯実行
 
  // Wi-Fiに接続
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("WiFiに接続中...");
  }
  Serial.println("WiFiに接続完了");
  Serial.println(WiFi.localIP());

  // WiFi接続完了
  strip.setPixelColor(0, strip.Color(0, 255, 0)); // NeoPixel色指定（緑） WiFI接続完了
  strip.show();   // NeoPixel点灯実行

  // RSSIをシリアルモニタに表示
  int rssi = WiFi.RSSI(); 
  Serial.printf("RSSI: %d\r\n", rssi);
  // CPU温度をシリアルモニタに表示
  float CpuTemp = temperatureRead();
  Serial.printf("CPU TEMPARTURE: %.1f\r\n", CpuTemp);

  // M5NanoC6の電源ON時にマジックパケット送信してPC電源ON
  on();

  //onoffへ要求を出します。
  mqtt.subscribe(&onoff);
}

void loop() {
  //MQTTに接続する
  MQTT_connect();

  //不足するまでサブスクリプションキューから読み取る
  //サブスクリプションが更新されるまで最大READ_TIMEOUT間待機
  Adafruit_MQTT_Subscribe * subscription;
  while ((subscription = mqtt.readSubscription(READ_TIMEOUT)))
  {
    //要求が完了したか判定
    if (subscription == &onoff)
    {
      //更新値をシリアルモニタに表示
      Serial.print("onoff: ");
      Serial.println((char*) onoff.lastread);

      //更新値が "ON"の場合は、ライトを点灯
      //それ以外の場合はオフに
      if (!strcmp((char*) onoff.lastread, "ON"))
      {
        //点灯   
        on();
        LightsStatus.publish("ON");
      }
      else if (!strcmp((char*) onoff.lastread, "OFF"))
      {
      //消灯
        off();
        LightsStatus.publish("OFF");
      }
      else
      {
        LightsStatus.publish("ERROR");
      }
    }
    else
    {
      //LightsStatus.publish("ERROR");
    }
  }
}

void MQTT_connect()
{
  // Stop if already connected
  if (mqtt.connected() && mqtt.ping())
  {
    //    mqtt.disconnect();
    return;
  }

  int8_t ret;

  mqtt.disconnect();

  Serial.print("Connecting to MQTT... ");
  uint8_t retries = 3;
  while ((ret = mqtt.connect()) != 0) // connect will return 0 for connected
  {
    Serial.println(mqtt.connectErrorString(ret));
    Serial.println("Retrying MQTT connection in 5 seconds...");
    mqtt.disconnect();
    delay(5000);  // wait 5 seconds
    retries--;
    if (retries == 0)
    {
      ESP.restart();
    }
  }
  Serial.println("MQTT Connected!");
}


//スイッチ定義（オン）の時
void on(void){
  //Adafruit側でスイッチONにするとマジックパケット送信
  WOL.sendMagicPacket(pcMacAddress);  // MACアドレスを使用してマジックパケットを送信
  Serial.println("スイッチオン Wake on LANパケット送信");
}

//スイッチ定義（オフ）の時
void off(void){
  //Adafruit側でスイッチOFFでもマジックパケット送信
  WOL.sendMagicPacket(pcMacAddress);  // MACアドレスを使用してマジックパケットを送信
  Serial.println("スイッチオフ Wake on LANパケット送信");
}
```
後から色々付け足そうと余分なコードが入ってますがご容赦を。
参考になれば幸いです。

追記：
稀にWindowsの起動／終了時に固まると本構成は無力。リレーをパソコンの電源スイッチに割り込ませるのがいいのかしら。自作機ならそれもアリ。