Arduino-R4-Minimaでデジタル時計作ってみた!(RTC未使用)

Arduino-R4-Minimaでデジタル時計作ってみた!(RTC未使用)

はじめに

こんにちは!香月です。

電子工作について勉強したことをアウトプットとしてブログで情報発信しています!

勉強中なので、間違っていること等あります。
気づいた方は、公式ラインで教えていただけると幸いです。

今回は、デジタル時計を作っていきましょう!
今回のArduinoR4はRTCという時刻を正確に取得できるようになりました。
この機能を使って、時計を作りたいと思います。

しかし、問題が一つあります。それは、、、
最初の時刻を取得できないということです。

R4にはWi-Fiモジュールがついているものと、ついていないMinimaがあり、僕が持っているのはMinimaなんです。

ということで、最初に時刻を設定する機能をつける必要があります。

ということで、今回は、、、
1.ディスプレイに文字表示
2.時刻設定機能の開発
3.時計完成!  
     
この3本建てで開発していきます!

それでは早速必要なものから見ていきましょう!

使うもの

ArduinoR4-Minima

OLEDディスプレイ

ジャンパー線+ブレッドボード

モジュールの解説

今回使うマイコンはArduinoR4-Minimaを使用します!
新機能のRTCを使って、デジタル時計を作ろうとしたのですが、結局使いませんでした。(使えませんでした。後述)

処理が早く、書き込み読み込み同時に早くなりました。
また、書き込み容量も増えこれまでのR3でできなかったことがかなり多くなりました!

今回は、数字を表示するのに0.96インチOLEDディスプレイを使用します。
このディスプレイは1つ約600円で安価で簡単に扱うことができるディスプレイで初心者にもかなりおススメのものになっています!

ライブラリなどを用いれば、日本語でも文字を表示することもできます

ライブラリのインストール

今回のプログラムで使っているライブラリです。
先にインストールしておいてください!

*依存関係のものもすべてインストールしてください!

手順1(ディスプレイに文字を表示させる)

まずは、ディスプレイに文字を表示させてみます。
今回は、126×64ドットOLEDを使用します!
このディスプレイは、簡単に扱えて安価で視認性が良いモジュールです。

それでは、回路とプログラムを見ていきましょう!

回路

上の画像を参考にして、まずは回路を作ってください!
今回は、A4,A5ピンを使用していますが、コードのほうで変更が可能です。
もし、配線の都合等で変更したい場合は、以下のコードのSDA,SCL=のところを変更してください!

プログラム1

以下のコードが文字を表示させるプログラムになります。
書き込みをしたら、RESETボタンを押すと文字が表示されます!
*普通に書き込むだけだと、僕の場合は表示されませんでした…
分かったら追記しておきます

#include <Wire.h>                       // I2C通信を行うためのライブラリ
#include <Adafruit_GFX.h>               // グラフィック描画をサポートするAdafruitのライブラリ
#include <Adafruit_SSD1306.h>           // SSD1306 OLEDディスプレイを制御するためのAdafruitのライブラリ

#define SCREEN_WIDTH 128                // OLEDディスプレイの幅を定義(128ピクセル)
#define SCREEN_HEIGHT 64                // OLEDディスプレイの高さを定義(64ピクセル)

#define OLED_RESET     -1               // リセットピンを指定。ハードウェアリセットを使用しないため -1 に設定
#define SCREEN_ADDRESS 0x3C             // I2Cアドレスを定義。通常、OLEDディスプレイのアドレスは 0x3C

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // Adafruit_SSD1306 オブジェクトを作成

#define PIN_SDA A4                      // I2CのSDAピンを指定
#define PIN_SCL A5                      // I2CのSCLピンを指定

void setup()
{
  // ディスプレイの初期化を行う。失敗した場合は無限ループで停止
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    for(;;); // 無限ループで停止
  }

  display.clearDisplay();               // ディスプレイをクリアする

  display.setTextSize(2);               // テキストサイズを設定(2倍の大きさ)
  display.setTextColor(SSD1306_WHITE);  // テキストカラーを白に設定
  display.setCursor(45,0);             // テキストの表示位置を設定(X:45, Y:0)
  display.print(F("2024"));         // 「2024」というテキストを表示
  display.setCursor(38, 25);            // テキストの表示位置を設定(X:38, Y:25)
  display.print(F("02/16"));         // 「02/16」というテキストを表示
  display.setCursor(15, 45);            // テキストの表示位置を設定(X:15, Y:45)
  display.print(F("12:13:43"));            // 「12:13:43」というテキストを表示

  display.display();                    // バッファ内容をディスプレイに表示
}

void loop()
{
  // メインループ。今回は特に何も行わない
}

動かしてみると、、、

こんな感じで文字が表示されました!
続いて、ボタンを押して時刻を設定する機能を追加していきます。

プログラム2(時刻設定機能の追加)

まずは、ボタンを押して、変更したい場所を選択し、プラス・マイナスボタンで時刻を変更していきます。
まずは、先ほどのプログラムの時刻を変更する機能を追加します。
それぞれの数字をまずは関数に置き換え、その関数をボタン3つを使い変更していきます。 

まずは、簡単に時間を「時間・分・秒」の3つを一つづつ設定していくものを作ります!

回路2

この画像を参考に回路を組んでみてください!
最初の回路にボタン3つつなぐだけです!

プログラム2(時刻設定機能)

以下のプログラムを書き込んで、RESETボタンを押してみてください!

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128                // 解像度 128 x 64
#define SCREEN_HEIGHT 64                // 解像度 64

#define OLED_RESET     -1               // 使用しないので -1を設定する
#define SCREEN_ADDRESS 0x3C             // I2Cアドレスは 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define PIN_RESET 0                     // カウンターリセット用ピン
#define PIN_INCREMENT 1                 // カウンター増加用ピン
#define PIN_DECREMENT 2                 // カウンター減少用ピン

int days[3] = {12, 20, 20};             // 初期値
int mode = 0;                          // 現在のモード
bool blinkState = false;               // 点滅状態
unsigned long previousMillis = 0;      // 前回のミリ秒
const long interval = 500;             // 点滅のインターバル
int resetCount = 0;                    // リセットボタンが押された回数

void setup() {
  pinMode(PIN_RESET, INPUT_PULLDOWN);     // リセットボタンピンをプルダウン入力に設定
  pinMode(PIN_INCREMENT, INPUT_PULLDOWN); // 増加ボタンピンをプルダウン入力に設定
  pinMode(PIN_DECREMENT, INPUT_PULLDOWN); // 減少ボタンピンをプルダウン入力に設定

  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    for (;;);                           // 初期化に失敗した場合は無限ループ
  }

  display.clearDisplay();               // 何か表示されている場合に備えて表示クリア
  updateDisplay();                      // 初期カウンター値を表示
}

void loop() {
  if (digitalRead(PIN_RESET) == HIGH) {
    resetCount++;                      // リセットボタンが押された回数をカウント
    mode = resetCount % 4;            // モードを設定 (0, 1, 2, 3のいずれか)
    updateDisplay();
    delay(200);                       // デバウンス処理
  }

  if (digitalRead(PIN_INCREMENT) == HIGH) {
    days[mode]++;
    updateDisplay();
    delay(200);                       // デバウンス処理
  }

  if (digitalRead(PIN_DECREMENT) == HIGH) {
    days[mode]--;
    updateDisplay();
    delay(200);                       // デバウンス処理
  }

  if (mode < 3) {                     // 点滅処理
    unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= interval) {
      previousMillis = currentMillis;
      blinkState = !blinkState;
      updateDisplay();
    }
  }
}

void updateDisplay() {
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);

  for (int i = 0; i < 3; i++) {
    if (i == mode && blinkState) {
      display.fillRect(0, i * 20, SCREEN_WIDTH, 20, SSD1306_BLACK); // 点滅効果
    } else {
      display.setCursor(0, i * 20);
      display.print(days[i]);
    }
  }
  
  display.display();
}


そうすると、、、
1番上の数字が点滅しました!

ボタンを押すと、、、数字が増えました!

2回設定ボタンを押すと、、最後の数字が点滅しました!

もう一度押すと、、、数字の点滅が止まりました!

これで時刻設定機能が出来上がりました!
あとは、これにArduinoR4のRTC機能をつけていきます!

(RTC機能使えずマイコン側でタイミングをとって作りました。)

回路3(完成版)

回路3は回路2で使ったものをそのまま使います!

それでは!プログラムを見ていきましょう!

プログラム(失敗)

ここでプログラムを紹介する前に報告が、、、RTC機能が使えませんでした

時刻設定機能が完成して、あとはRTC機能と連携するだけだったのですが、うまくいきませんでした。

ということで、RTC機能を使わずマイコン側でタイミングをとるものを作成しました!

以下がそのプログラムになります。
使用方法
・年を設定
・セットボタンを押す
・月を設定
・セットボタンを押す
この繰り返しで分までセットして、セットボタンを押すと、時計して機能します。
*時刻を修正したい場合は、ArduinoR4本体のRESETボタンを押すと年から修正することができます!

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128                // 解像度 128 x 64
#define SCREEN_HEIGHT 64                // 解像度 64

#define OLED_RESET -1                   // 使用しないので -1を設定する
#define SCREEN_ADDRESS 0x3C             // I2Cアドレスは 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// ピン定義
#define PIN_TIME_SET 0                  // 時間設定ボタン用ピン
#define PIN_TIME_UP 1                   // 時間増加ボタン用ピン
#define PIN_TIME_DOWN 2                 // 時間減少ボタン用ピン

int days[6] = {2024, 7, 23, 12, 20, 20}; // 初期値
int mode = 0;                          // 現在のモード
bool blinkState = false;               // 点滅状態
unsigned long previousMillis = 0;      // 前回のミリ秒
const long interval = 500;             // 点滅のインターバル
int resetCount = 0;                    // リセットボタンが押された回数
bool clockRunning = false;             // 時計が動いているかどうかのフラグ

// 各月の日数を設定 (2月は28日固定でうるう年は考慮しない)
const int daysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

void setup() {
  pinMode(PIN_TIME_SET, INPUT_PULLDOWN);     // 時間設定ボタンピンをプルダウン入力に設定
  pinMode(PIN_TIME_UP, INPUT_PULLDOWN);      // 時間増加ボタンピンをプルダウン入力に設定
  pinMode(PIN_TIME_DOWN, INPUT_PULLDOWN);    // 時間減少ボタンピンをプルダウン入力に設定

  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    for (;;);                           // 初期化に失敗した場合は無限ループ
  }

  display.clearDisplay();               // 何か表示されている場合に備えて表示クリア
  updateDisplay();                      // 初期カウンター値を表示
}

void loop() {
  if (!clockRunning) {
    if (digitalRead(PIN_TIME_SET) == HIGH) {
      resetCount++;                      // 時間設定ボタンが押された回数をカウント
      mode = resetCount % 6;             // モードを設定 (0, 1, 2, 3, 4, 5のいずれか)
      updateDisplay();
      delay(200);                        // デバウンス処理
    }

    if (digitalRead(PIN_TIME_UP) == HIGH) {
      days[mode]++;
      if (mode == 0 && days[mode] > 2099) days[mode] = 2099; // 年の範囲を制限
      if (mode == 1 && days[mode] > 12) days[mode] = 12;     // 月の範囲を制限
      if (mode == 2 && days[mode] > daysInMonth[days[1] - 1]) days[mode] = daysInMonth[days[1] - 1]; // 日の範囲を制限
      if (mode == 3 && days[mode] > 23) days[mode] = 23;     // 時間の範囲を制限
      if (mode == 4 && days[mode] > 59) days[mode] = 59;     // 分の範囲を制限
      if (mode == 5 && days[mode] > 59) days[mode] = 59;     // 秒の範囲を制限
      updateDisplay();
      delay(200);                       // デバウンス処理
    }

    if (digitalRead(PIN_TIME_DOWN) == HIGH) {
      days[mode]--;
      if (mode == 0 && days[mode] < 2000) days[mode] = 2000; // 年の範囲を制限
      if (mode == 1 && days[mode] < 1) days[mode] = 1;       // 月の範囲を制限
      if (mode == 2 && days[mode] < 1) days[mode] = 1;       // 日の範囲を制限
      if (mode == 3 && days[mode] < 0) days[mode] = 0;       // 時間の範囲を制限
      if (mode == 4 && days[mode] < 0) days[mode] = 0;       // 分の範囲を制限
      if (mode == 5 && days[mode] < 0) days[mode] = 0;       // 秒の範囲を制限
      updateDisplay();
      delay(200);                       // デバウンス処理
    }

    if (resetCount % 6 == 5) {          // 6回目のクリックで時計をスタート
      clockRunning = true;
      previousMillis = millis();        // 現在のミリ秒を取得
    } else {
      // 点滅処理
      unsigned long currentMillis = millis();
      if (currentMillis - previousMillis >= interval) {
        previousMillis = currentMillis;
        blinkState = !blinkState;
        updateDisplay();
      }
    }
  } else {
    unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= 1000) {
      previousMillis = currentMillis;
      days[5]++;
      if (days[5] > 59) {
        days[5] = 0;
        days[4]++;
        if (days[4] > 59) {
          days[4] = 0;
          days[3]++;
          if (days[3] > 23) {
            days[3] = 0;
            days[2]++;
            if (days[2] > daysInMonth[days[1] - 1]) { // 各月の日数を考慮
              days[2] = 1;
              days[1]++;
              if (days[1] > 12) {
                days[1] = 1;
                days[0]++;
                if (days[0] > 2099) {
                  days[0] = 2000;
                }
              }
            }
          }
        }
      }
      updateDisplay();
    }
  }
}

void updateDisplay() {
  display.clearDisplay();

  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);

  // 年
  display.setCursor(45, 0);
  if (mode == 0 && blinkState) display.print("  ");
  else display.print(days[0]);

  // 月/日
  display.setCursor(25, 25);
  if (mode == 1 && blinkState) display.print("  ");
  else display.print(days[1]);
  display.setCursor(60, 25); // スラッシュの位置
  display.print('/');
  display.setCursor(85, 25);
  if (mode == 2 && blinkState) display.print("  ");
  else display.print(days[2]);

  // 時/分/秒
  display.setCursor(15, 45);
  if (mode == 3 && blinkState) display.print("  ");
  else display.print(days[3]);
  display.setCursor(40, 45); // コロンの位置
  display.print(':');
  display.setCursor(50, 45);
  if (mode == 4 && blinkState) display.print("  ");
  else display.print(days[4]);
  display.setCursor(70, 45); // コロンの位置
  display.print(':');
  display.setCursor(85, 45);
  if (mode == 5 && blinkState) display.print("  ");
  else display.print(days[5]);

  display.display();
}

動かしてみた!

動かしてみると、まずは年が点滅します。

セットして、セットボタンを2回押すと、、
日にちが点滅します。

そして、最後までセットすると、、、

秒を刻み始めました!!

動かない場合

動作しない場合は、以下の点を確認してみてください!

回路の確認

ジャンパー線のつなぐ位置があっているか。
また、ジャンパー線の断線、ディスプレイが断線していないかを確認してください。

プログラムが書き込めているか

プログラムがきちんと書き込みされているかを確認してください。
書き込みエラーが発生している場合は、エラーメッセージを参考に修正してください。

マイコンボードの設定やポートがあっているかを確認してください。

公式ラインに連絡

以上のことを確認しいても解決しない場合は、公式ラインに連絡ください。
自分も電子工作を勉強している身なので、すべてに対応できるわけではありません。
可能な範囲で対応させていただきます!

友だち追加

おわりに

どうだったでしょうか??

本当はRTC機能を使って時計を作成して、皆さんにやり方を共有したかったのですが、今の自分には知識不足でした、、、

他にもArduinoR4には新機能があるので、それらに触りつつ使えるようになったら、また記事にします!
ということで!ArduinoR4-Minimaでデジタル時計を作ってみました!

ほかにも様々なものを作っていきます!
また次回の記事でお会いしましょう!

ArduinoNanoR4カテゴリの最新記事