所在表を作ってみた
はじめに
仕事柄、席を外すことが多いので、行先表を作ってみました。
使っている主なパーツは、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つ追加しました。
組み立て
手元にアクリル板が余っていたので、画面保護用に使ってみました。また自立するよう、スペーサーを使って基板を重ねてみました。
動かしてみた
無事に動きました。