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 表示
フォントと色は以下のサイトで探した。
今回は、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);
}