M5StickC をguest Wifi のパスワード表示機にする

最終更新日

お客さんにguest用のWifiのパスワードを表示する方法を考えた。

要件

  • 日毎に変更するパスワードを表示するために、ディスプレイが必要
  • 会議室の反対側にいる人にも見えるように、壁掛けならA4くらい大きさが必要
  • パスワードはアルファベット大文字・小文字と数字8桁
  • パスワードは某サーバの認証付きWebサーバ上にある

問題は、離せばわかるお年頃の方にも問題なく見えるようにすること。

最初はMatrix LEDの64x16くらいの大きさを考えていた。

しかし、パスワードが電光掲示板に常に表示されているなんて、かっこ悪い。

ということで、数時間なら電源接続がいらず、手元に参照でき、フォントもわかりやすい26pixelカーニングフォントが使える、M5StickCを利用することにした。

処理概要

初期化

既存のWifi AccessPoint に接続する。

接続には意外と時間がかかるため、接続するたびに32x500msec=16sec の接続成功待ちをする。500msec毎に . を表示。

#include <WiFi.h>

const char* _ssid     = "接続するAPのSSID";
const char* _password = "接続APのパスワード";

wifi_setup() {
 WiFi.begin(_ssid, _password);

  int count = 32;   // 32 * 500msec = 16sec
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    M5.Lcd.print(".");
    count -= 1;
    if (count == 0) {
      M5.Lcd.setTextSize(1);
      M5.Lcd.setTextFont(2); 
      M5.Lcd.setTextColor(RED);
      M5.Lcd.println("fail wifi connect");
      delay(500);
      return -1;
    }
  }

2回の待ちも失敗すると、エラー表示する。

接続成功後は、取得したIPアドレスなどが以下のようにわかる。

  M5.Lcd.println("");
  M5.Lcd.println("WiFi connected.");
  M5.Lcd.print("IP address: ");
  M5.Lcd.println(WiFi.localIP());
  M5.Lcd.println();
  delay(1000);
}

guest Wifi 表示

フォントと色は以下のサイトで探した。

M5Displayクラスの使い方

今回は、1: Adafruit 8ピクセルASCIIフォント2: 16ピクセルASCIIフォント と、 4: 26ピクセルASCIIフォント を利用した。

  • SSID: などタイトルは、16px。
  • 値は、26px。
  • 時刻表示は、8px。

SSIDなどはこんな感じで表示。

  • M5.Lcd.fillScreen(色) ... 画面全体を塗りつぶし
  • M5.Lcd.setTextFonr(フォントの種類)
  • M5.Lcd.setTextSize(フォントの倍角数) ... 1で1倍。2で2倍。
  • M5.Lcd.setCursor(x, y)
  • M5.Lcd.setTextColor(色)

  // display values on LCD
  M5.Lcd.setTextSize(1);
  M5.Lcd.setTextFont(4); 

  M5.Lcd.fillScreen(BLACK);

  M5.Lcd.setCursor(0, 0);
  M5.Lcd.setTextFont(2);
  M5.Lcd.setTextColor(DARKGREY);
  M5.Lcd.printf("SSID: ");
  M5.Lcd.setCursor(36, 0);
  M5.Lcd.setTextFont(4);
  M5.Lcd.setTextColor(DARKGREEN);
  M5.Lcd.printf("%s\r\n", _display_ssid);

  M5.Lcd.setCursor(0, 24);
  M5.Lcd.setTextFont(2);
  M5.Lcd.setTextColor(DARKGREY);
  M5.Lcd.printf("USER: ");
  M5.Lcd.setTextFont(4);
  M5.Lcd.setCursor(36, 20);
  M5.Lcd.setTextColor(DARKGREEN);
  M5.Lcd.printf("guest\r\n");

  M5.Lcd.setCursor(0, 48);
  M5.Lcd.setTextFont(2);
  M5.Lcd.setTextColor(DARKGREY);
  M5.Lcd.printf("PWD: ");
  M5.Lcd.setCursor(36, 48);
  M5.Lcd.setTextFont(4);
  M5.Lcd.setTextColor(DARKGREEN);
  M5.Lcd.printf(passwd);

時刻表示

電源投入時、M5StickCの時計は、当然狂っている。

ntp serverに時間をとりにいって、時間を合わせる。

#include <WiFi.h>
#include "time.h"

//
// setup RTC by NTP
//
void setup_rtc() {
  struct tm timeinfo;
  uint8_t hh, mm, ss;
  RTC_TimeTypeDef TimeStruct;

  // connect NTP server
  configTime(9 * 3600, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");
  if (!getLocalTime(&timeinfo)) {
    M5.Lcd.println("Failed to obtain time");
    return;
  }

  //
  // set localtime to RTC
  //
  hh = timeinfo.tm_hour;
  mm = timeinfo.tm_min;
  ss = timeinfo.tm_sec;

  TimeStruct.Hours   = hh;
  TimeStruct.Minutes = mm;
  TimeStruct.Seconds = ss;
  M5.Rtc.SetTime(&TimeStruct);
}

時刻表示はこんな感じ。

あまり頻繁に書き換えたくないので、static変数 _RTC_TimeStruct で前回の時刻取得内容を保持し、秒が変更されたときだけ、画面を書き換える。

void display_time() {
  static RTC_TimeTypeDef _RTC_TimeStruct;
  RTC_TimeTypeDef RTC_TimeStruct;

  // get time
  M5.Rtc.GetTime(&RTC_TimeStruct);

  if (RTC_TimeStruct.Seconds != _RTC_TimeStruct.Seconds) {
    // display time
    M5.Lcd.setTextSize(1);
    M5.Lcd.setTextFont(1);
    M5.Lcd.setCursor(32, 9*8);
    M5.Lcd.setTextColor(DARKGREEN);
    M5.Lcd.fillRect(40, 9*8, 256, 12, BLACK);
    M5.Lcd.printf("Time: %02d : %02d : %02d\n",
      RTC_TimeStruct.Hours, RTC_TimeStruct.Minutes, RTC_TimeStruct.Seconds);
  }
  memcpy(&_RTC_TimeStruct, &RTC_TimeStruct, sizeof(RTC_TimeStruct));
}

電源管理

起動して、guest Wifi用のパスワードを表示した後、どうするか?

そのまま表示しつづけると、2つの問題がある

  • 翌日になればパスワードは変更される
  • 大抵は操作後、1分も表示できれば十分。その後は消してもいい。

ということで、以下の処理を loop() に記述した。

  • 100秒ほどで画面を暗くして、バッテリを節約
  • Button A を押すとリセット(Wifi 再接続&パスワード再取得)
  • Button B を押すと画面を100秒ほど明るくする
#define BRIGHT_TIME 1000
int16_t _countdown = BRIGHT_TIME;  // 1000 * 100sec = 100sec

void loop() {

  // pinの状態など更新
  M5.update();

  // for reset
  if (M5.BtnA.wasPressed() == 1) {
    esp_restart();
  }

  if (M5.BtnB.wasPressed() == 1) {
    _countdown = BRIGHT_TIME;
    M5.Axp.ScreenBreath(10);
  }

  if (_countdown < 0) {
    // dark screen for battery
    M5.Axp.ScreenBreath(7);
  } else {
    display_time();
    --_countdown;
  }

  delay(100);
}

シェアする