← Blog 一覧へ

ESP32を使ったWOLの作り方

·9 min read
ESP32WOL

こんにちは、ほたかっち です。今回は前回の続きをやっていこうと思います。

前回の振り返り

前回は実際にパケットを送信してPCを起動してみました。今回は外部からでもPCを起動できるようにしたいと思います。

作成手順

  1. Google Sheetsを作成
  2. Google App Scriptsを作成
  3. ESP32のプログラムを作成

Google Sheetsを作成

Google Sheetsのセルの値でパケットを送信するかを判断します。

まずは,Chromeを開いてGoogleアカウントにログインします。Google Driveにアクセスして,新しいGoogle Sheetsファイルを作成します。今後も使う可能性を考慮して,ファイル名を「IoTプロジェクト」にしておきました。このシートでは,PCのステータスを管理するためのセルを一つ用意します。

今回は,ステータス列のB2セルにPCがオンかオフかを示す値を設定します。ここではTRUEとFALSEを使って表記しますが,値をいちいち入力するのは面倒なので,B2セルを選択した状態で「挿入」から「プルダウン」を選択します。すると,右側にデータの入力規則が表示されるので,オプション 1をFALSE(色は赤),オプション 2をTRUE(色は青)に変更して「完了」をクリック。これでステータスを簡単に切り替えられるようになりました。
スプレッドシート スプレッドシート

Google App Scriptsを作成

GAS(Google Apps Script)は,Google Sheetsの拡張機能として簡単に連携できます。まず,Google Sheetsの「拡張機能」タブから「Apps Script」をクリックします。これで,Google Sheets専用のプロジェクトがGoogle Apps Scriptに自動的に作成されます。プロジェクト名は,わかりやすく「IoTプロジェクト」に変更しておきましょう。

Google Apps ScriptはJavaScriptをベースにしているので,JavaScriptの知識があればすぐに始められます。ただし,ファイルの拡張子は.jsではなく.gsになります。また,通常のJavaScriptとは少し異なり,Google独自の専用関数も用意されているので,それを使うことでGoogle Sheetsや他のGoogleサービスと簡単に連携できます。

まずは,Google SheetsとESP32がやりとりできるようにGASで関数を作成します。関数は,標準のJavaScriptを使って書くことができますが,Google Apps Scriptの特性を活かして作ると,さらに便利なツールになります。

その関数で,Google SheetsとESP32がやりとりできるAPIを作成します。

API(Application Programming Interface)とは,アプリケーション間でデータや機能をやりとりするためのインターフェースです。APIといっても広義的な意味があり,今回の場合はWeb APIと呼ばれるものになります。Web APIは,HTTP通信を使ってデータを送受信することができ,通常はJSON(JavaScript Object Notation)というデータフォーマットでデータが返ってきます。JSONは,データ交換に広く使われる軽量なフォーマットです。

以下のコードは,Google Sheetsのセルから値を取得し,その値をJSON形式で返すように設定したものです。このコードをGoogle Apps Scriptのコード.gsに貼り付けます。

const SECRET = "password"; //パスワード
const SHEET_NAME = "シート1"; //シート名
const CELL = "B2"; // フラグを置いているセル

function doGet(e) {
  if (e.parameter.key !== SECRET) {
    return ContentService.createTextOutput(JSON.stringify({ok: false, error: "forbidden"}))
      .setMimeType(ContentService.MimeType.JSON);
  }

  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_NAME);

  if (e.parameter.action === "set") {
    sheet.getRange(CELL).setValue(e.parameter.value);
    return ContentService.createTextOutput(JSON.stringify({ok: true, value: e.parameter.value}))
      .setMimeType(ContentService.MimeType.JSON);
  }

  const value = sheet.getRange(CELL).getValue();
  return ContentService.createTextOutput(JSON.stringify({ok: true, value: String(value)}))
    .setMimeType(ContentService.MimeType.JSON);
}

このdoGet関数は,HTTP GETリクエストが来たときに呼び出されます。Google Sheetsの「シート1」シートのB2セルの値を取得し,それをJSON形式で返します。これにより,ESP32からこのAPIを呼び出すことで,Google Sheetsのセルの値を簡単に取得することができます。

プログラムを貼り付けたら、デプロイを押して新しいデプロイを作成します。

GAS

アクセスできるユーザーを必ず全員にしてデプロイをします。ウェブアプリのURL(https://script.google.com/macros/s/~~~/exec)は後ほど使うので保存しておいてください。
(追記)保存したURLは一度ログインした方がいいかも。

GASの作成は以上です。

ESP32のプログラムを作成

完成したプログラムはこちらです。

#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#include <WakeOnLan.h>
#include <ESPping.h>

bool getGASValue(bool &outValue);
bool setGASValue(const String& status);
bool checkPcStatus();
void handleHttpError(int httpResponseCode, HTTPClient& http);
void ledOn();
void ledOff();
void ledBlink();
void ledFastBlink();

WiFiUDP UDP;
WiFiClient client;
WakeOnLan WOL(UDP);
const char* ssid = "wifi";
const char* password = "password";

// GAS Webアプリ設定(OAuth不要)
const char* gasBaseUrl = "https://script.google.com/macros/s/~~~/exec";
const char* gasSecret  = "password"; //gasで設定したパスワード

const int LED_PIN = 2;
const char* pcMacAddress = "AA:BB:CC:DD:EE:FF";
const IPAddress pcIP(128, 0, 0, 1);

RTC_DATA_ATTR bool previousValue = true;
const uint64_t sleepIntervalOff = 1 * 60 * 1000000;  // 1分

void setup() {
  pinMode(LED_PIN, OUTPUT);
  ledOff();
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    ledBlink();
    Serial.print(".");
  }
  Serial.println("\nWiFiに接続しました:");
  Serial.print("IPアドレス: ");
  Serial.println(WiFi.localIP());

  bool sheetValue = false;
  if (getGASValue(sheetValue)) {
    if (sheetValue) {
      Serial.println("Google Sheets が TRUE → WOL を送信します");
      if (setGASValue("FALSE")) {
        ledOn();
        WOL.sendMagicPacket(pcMacAddress);
        delay(1000);
        previousValue = false;
        ledOff();
      } else {
        Serial.println("FALSE に戻せなかったため WOL を中止します");
        ledFastBlink();
      }
    } else {
      Serial.println("Google Sheets は FALSE です");
      previousValue = false;
    }
  } else {
    Serial.println("シートの取得に失敗しました");
    ledFastBlink();
  }

  WiFi.disconnect();
  Serial.println("Wi-Fi接続を解除しました");
  Serial.println("1分間スリープします...");
  esp_sleep_enable_timer_wakeup(sleepIntervalOff);
  esp_deep_sleep_start();
}

void loop() {
  // setup()内でディープスリープするため未使用
}

bool checkPcStatus() {
  Serial.print("Pingを送信中...");
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
  }
  bool isOnline = client.connect(pcIP, 3389);
  Serial.println(isOnline ? "PCはオンラインです。" : "PCはオフラインまたは到達不能です。");
  return isOnline;
}

bool getGASValue(bool &outValue) {
  WiFiClientSecure secureClient;
  secureClient.setInsecure();
  HTTPClient http;

  String url = String(gasBaseUrl) + "?key=" + gasSecret;
  http.begin(secureClient, url);
  http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);

  int code = http.GET();
  if (code == HTTP_CODE_OK) {
    String response = http.getString();
    DynamicJsonDocument doc(512);
    if (deserializeJson(doc, response) || doc["ok"] != true) {
      Serial.println("応答の解析に失敗: " + response);
      http.end();
      return false;
    }
    String v = doc["value"].as<String>();
    v.trim();
    v.toLowerCase();
    outValue = (v == "true" || v == "1");
    http.end();
    return true;
  }
  handleHttpError(code, http);
  http.end();
  return false;
}

bool setGASValue(const String& status) {
  WiFiClientSecure secureClient;
  secureClient.setInsecure();
  HTTPClient http;

  String url = String(gasBaseUrl) + "?key=" + gasSecret + "&action=set&value=" + status;
  http.begin(secureClient, url);
  http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);

  int code = http.GET();
  bool ok = (code == HTTP_CODE_OK);
  if (ok) {
    Serial.println("Google Sheetsの更新結果: " + http.getString());
  } else {
    handleHttpError(code, http);
  }
  http.end();
  return ok;
}

void handleHttpError(int httpResponseCode, HTTPClient& http) {
  Serial.print("HTTPリクエストでエラーが発生しました: HTTP/1.1 ");
  Serial.println(httpResponseCode);
  String errorResponse = http.getString();
  if (errorResponse.length() > 0) {
    Serial.println("エラーレスポンス: ");
    Serial.println(errorResponse);
  }
}

void ledOn() { digitalWrite(LED_PIN, HIGH); Serial.println("LED: 点灯"); }
void ledOff() { digitalWrite(LED_PIN, LOW); Serial.println("LED: 消灯"); }
void ledBlink() {
  digitalWrite(LED_PIN, HIGH); delayMicroseconds(500);
  digitalWrite(LED_PIN, LOW); delayMicroseconds(500);
}
void ledFastBlink() {
  for (int i = 0; i < 10; i++) {
    digitalWrite(LED_PIN, HIGH); delay(100);
    digitalWrite(LED_PIN, LOW); delay(100);
  }
  Serial.println("LED: エラー点滅完了");
}

Wi-FiのSSIDとパスワードと、先ほどのウェブアプリのURLとパスワードと、PCのMACアドレスとIPアドレスを書き換えてください。

この状態だとセキュリティ面でいろいろと心配ですが……セルの値を変えてPCが起動すると完成です。

最後まで見ていただきありがとうございました。

© 2026 Hotakacchi. All rights reserved.