Intern

M5Stackcore × Node-REDで作るリアルタイム投票システム

今回は、M5StackNode-RED を使って、ローカルネットワーク内で完結する「お題に対してA/Bで即時投票できるシステム」を構築してみました。


システム概要

  • 投票端末:M5Stack Core
  • 集計・表示:Node-RED(HTTP通信)
  • 通信方式:M5Stack → Node-REDへHTTP POST(MQTT不要)
  • 投票方式:A/Bボタンで即時投票、Cボタンで次のテーマへ変更
  • 投票結果:Node-REDダッシュボード上にリアルタイム表示

構成イメージ

[M5Stack端末]
↓ (HTTP POST)
[Node-RED(PC)]
→ ダッシュボードで集計・表示

M5Stack 側の実装

M5Stackでは、複数のテーマと選択肢を配列で定義し、電源投入時にランダムで1つ表示。
AまたはBボタンで即投票、Cボタンで次のテーマへ更新・送信します。

<code>#include <M5Stack.h>
#include <WiFi.h>
#include <HTTPClient.h>

// Wi-Fi設定
const char* ssid = "smartlight-net";
const char* password = "42397311";

// Node-RED HTTPエンドポイント
const char* serverName = "http://192.168.1.46:1880/vote";

// テーマ一覧
const char* themes[][3] = {
  {"ランチはどっち?", "ラーメン", "カレー"},
  {"好きな季節は?", "春", "秋"},
  {"旅行するなら?", "海", "山"},
  {"ペット飼うなら?", "犬", "猫"},
  {"観戦するなら?", "サッカー", "野球"}
};
const int themeCount = sizeof(themes) / sizeof(themes[0]);
int currentThemeIndex = 0;

void connectWiFi() {
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
}

void sendToServer(String vote, String theme = "") {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin(serverName);
    http.addHeader("Content-Type", "application/json");

    String json = "{";
    if (vote != "") json += "\"vote\":\"" + vote + "\"";
    if (theme != "") {
      if (vote != "") json += ",";
      json += "\"theme\":\"" + theme + "\"";
    }
    json += "}";

    http.POST(json);
    http.end();
  }
}

void displayTheme() {
  M5.Lcd.clear();
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.setTextSize(2);
  M5.Lcd.println("テーマ:");
  M5.Lcd.println(themes[currentThemeIndex][0]);
  M5.Lcd.println("A:" + String(themes[currentThemeIndex][1]));
  M5.Lcd.println("B:" + String(themes[currentThemeIndex][2]));
  M5.Lcd.println("C:次のテーマ");
}

void setup() {
  M5.begin();
  connectWiFi();
  randomSeed(analogRead(36));
  currentThemeIndex = random(themeCount);
  displayTheme();
  sendToServer("", themes[currentThemeIndex][0]);  // 初回テーマ送信
}

void loop() {
  M5.update();
  if (M5.BtnA.wasPressed()) {
    sendToServer("A");
    M5.Lcd.setCursor(0, 180);
    M5.Lcd.println("Aに投票しました");
  }
  if (M5.BtnB.wasPressed()) {
    sendToServer("B");
    M5.Lcd.setCursor(0, 200);
    M5.Lcd.println("Bに投票しました");
  }
  if (M5.BtnC.wasPressed()) {
    currentThemeIndex = random(themeCount);
    displayTheme();
    sendToServer("", themes[currentThemeIndex][0]);  // 新テーマ送信
  }
}
</code>Code language: PHP (php)

Node-RED 側の構成

HTTP受信フロー

functionノードのコード

<code>let voteData = flow.get('voteData') || {
  theme: '',
  A: 0,
  B: 0
};

// テーマ変更時にリセット
if (msg.payload.theme && msg.payload.theme !== voteData.theme) {
  voteData = {
    theme: msg.payload.theme,
    A: 0,
    B: 0
};
}

// 投票処理
if (msg.payload.vote === 'A') voteData.A++;
if (msg.payload.vote === 'B') voteData.B++;

flow.set('voteData', voteData);
msg.payload = voteData;
return msg;
</code>Code language: JavaScript (javascript)

M5Stuckcoreでの表示

左ボタン:A 真ん中ボタン:B 右ボタン:C

ダッシュボードでの表示

Node-REDのDashboardで以下を表示しています:

Cボタンでテーマが変わると自動でリセットされ、常に新しい投票を受け付ける設計です。

まとめ


A/Bボタンによる即時投票、Cボタンによるテーマ変更もスムーズに動作し、結果はダッシュボード上にリアルタイムで反映されます。ただしM5Stuckcoreが日本語で表示できないため英語でしか表示できません。