見出し画像

所在表を作ってみた

はじめに

仕事柄、席を外すことが多いので、行先表を作ってみました。
使っている主なパーツは、Digispark、LCDモジュール (LCD 1602A)+I2Cモジュール、RTC (DS1307)、ボタン電池 (LIR2032)です。

プログラミング

基本的な機能として、日時の表示と所在の確認が可能です。また、少し動きのあるデザインを加えるため、所在が右から左に流れる形式で表示するようにしました。しかし、LCDのレスポンスが悪く、視認性が低下してしまったため、表示を点滅形式に変更しました。

  • RTC搭載
    RTC(リアルタイムクロック)が搭載されており、コンパイル後やボードへの書き込み後、電池が装着されていれば正確な日時を刻みます。
    ただし、電池が切れると、次に電源を接続した際、書き込み時点の日付からカウントが始まってしまいます。この問題を解決するため、時刻を手動で変更できる機能を追加しました。

  • 表示仕様
    時刻や所在の表示は、2秒間点灯し、0.5秒間消灯を繰り返します。カタカナ表示は文字化けするので注意が必要です。

#include <TinyWireM.h>
#include <LiquidCrystal_I2C.h>
#include <TinyRTClib.h>

RTC_DS1307 RTC;
#define GPIO_ADDR 0x27 // LCDモジュールのI2Cアドレス
LiquidCrystal_I2C lcd(GPIO_ADDR, 16, 2);

unsigned long lastButtonPressTime = 0; // ボタン押下のデバウンス処理用
const int debounceDelay = 200; // デバウンス遅延(ミリ秒)
bool isTimeSettingMode = false; // 時刻設定モードのフラグ
int timeSettingItem = 0; // 時刻設定項目(0: 月, 1: 日, 2: 時, 3: 分, 4: 秒)
int mode = 1; // 通常モード時の表示内容を決定するモード

// 点滅管理用変数
bool isDisplayOn = true; // 表示状態フラグ
unsigned long lastBlinkTime = 0; // 点滅タイミング管理用
const int displayOnDuration = 2000; // 点灯時間(ミリ秒)
const int displayOffDuration = 500; // 消灯時間(ミリ秒)

void setup() {
  TinyWireM.begin();
  lcd.init();
  lcd.backlight();
  lcd.print("Initializing...");
  delay(2000);
  lcd.clear();

  // RTC初期化
  if (!RTC.begin()) {
    lcd.print("RTC Error");
    while (1);
  }

  // 初期時刻をPCの時刻に設定
  RTC.adjust(DateTime(__DATE__, __TIME__));

  pinMode(1, INPUT); // ボタン1:値変更
  pinMode(4, INPUT); // ボタン4:変更箇所切り替え
}

void loop() {
  // ボタン1が押されたかをチェック(値の変更)
  if (digitalRead(1) == HIGH) {
    if (millis() - lastButtonPressTime > debounceDelay) {
      lastButtonPressTime = millis();
      if (isTimeSettingMode) {
        adjustTime(); // 選択された項目の値を変更
      } else {
        // 通常モード時はモードを変更
        mode = (mode % 10) + 1;
        updateDisplay();
      }
    }
  }

  // ボタン4が押されたかをチェック(変更箇所の切り替え)
  if (digitalRead(4) == HIGH) {
    if (millis() - lastButtonPressTime > debounceDelay) {
      lastButtonPressTime = millis();
      if (!isTimeSettingMode) {
        isTimeSettingMode = true; // 時刻設定モードに入る
        timeSettingItem = 0; // 月から開始
      } else {
        // 設定項目を切り替え
        timeSettingItem++;
        if (timeSettingItem > 4) { // 秒の設定後に通常モードに戻る
          isTimeSettingMode = false;
        }
      }
      updateDisplay();
    }
  }

  // 現在の時刻を常に表示(1行目)
  displayTime();

  // 2行目のメッセージを点滅
  manageBlinking();
}

void displayTime() {
  char timeBuf[16];
  DateTime now = RTC.now();
  snprintf(timeBuf, sizeof(timeBuf), "%02d/%02d %02d:%02d:%02d", now.month(), now.day(), now.hour(), now.minute(), now.second());
  lcd.setCursor(0, 0);
  lcd.print(timeBuf); // 1行目に時刻を表示
}

void updateDisplay() {
  const char *message;

  if (isTimeSettingMode) {
    switch (timeSettingItem) {
      case 0: message = "Set Month"; break;
      case 1: message = "Set Day"; break;
      case 2: message = "Set Hour"; break;
      case 3: message = "Set Minute"; break;
      case 4: message = "Set Second"; break;
      default: message = ""; break;
    }
  } else {
    // 通常モード時のメッセージ
    switch (mode) {
      case 1: message = "Room A (111)"; break;
      case 2: message = "Room B (121)"; break;
      case 3: message = "Room C (131)"; break;
      case 4: message = "Room D (201)"; break;
      case 5: message = "Room E (211)"; break;
      case 6: message = "Room F (221)"; break;
      case 7: message = "\xb6\xb2\xb7\xde / \xb3\xc1\xb1\xdc\xbe"; break;
      case 8: message = "\xbc\xad\xaf\xc1\xae\xb3"; break;
      case 9: message = "\xd7\xdd\xc1 / \xcb\xd9\xd4\xbd\xd0"; break;
      case 10: message = "\xd5\xb8\xb4\xcc\xd2\xb2 (*_*)"; break;
      default: message = ""; break;
    }
  }

  // メッセージを保持
  lcd.setCursor(0, 1);
  lcd.print("                "); // 2行目をクリア
  lcd.setCursor(0, 1);
  lcd.print("-> ");
  lcd.print(message);
}

void manageBlinking() {
  unsigned long currentMillis = millis();

  if (isDisplayOn && currentMillis - lastBlinkTime >= displayOnDuration) {
    // 点灯時間を超えたら消灯
    isDisplayOn = false;
    lastBlinkTime = currentMillis;
    lcd.setCursor(0, 1);
    lcd.print("                "); // 2行目をクリア
  } else if (!isDisplayOn && currentMillis - lastBlinkTime >= displayOffDuration) {
    // 消灯時間を超えたら点灯
    isDisplayOn = true;
    lastBlinkTime = currentMillis;
    updateDisplay(); // メッセージを再表示
  }
}

void adjustTime() {
  DateTime now = RTC.now();
  int month = now.month();
  int day = now.day();
  int hour = now.hour();
  int minute = now.minute();
  int second = now.second();

  switch (timeSettingItem) {
    case 0: // 月
      month++;
      if (month > 12) month = 1;
      break;
    case 1: // 日
      day++;
      if (day > daysInMonth(month)) day = 1;
      break;
    case 2: // 時
      hour = (hour + 1) % 24;
      break;
    case 3: // 分
      minute = (minute + 1) % 60;
      break;
    case 4: // 秒
      second = (second + 1) % 60;
      break;
  }

  RTC.adjust(DateTime(2000, month, day, hour, minute, second));
}

int daysInMonth(int month) {
  if (month == 2) {
    return 28; 
  }
  if (month == 4 || month == 6 || month == 9 || month == 11) {
    return 30;
  }
  return 31;
}

接続

最初はスイッチ1を押すことで行先を変更できるように設定していましたが、新たに日時を変更できるスイッチ(スイッチ4)を1つ追加しました。

ブレッドボード上の接続(時刻設定ボタン無し)
時刻設定ボタンを追加

組み立て

手元にアクリル板が余っていたので、画面保護用に使ってみました。また自立するよう、スペーサーを使って基板を重ねてみました。

正面
斜め上から見たところ
背面(操作部)

動かしてみた

無事に動きました。

時刻の設定画面
行先の表示

いいなと思ったら応援しよう!