工業製造
産業用モノのインターネット | 工業材料 | 機器のメンテナンスと修理 | 産業プログラミング |
home  MfgRobots >> 工業製造 >  >> Manufacturing Technology >> 製造プロセス

Arduino Nano、DS3231 RTC、LDR自動輝度調整で作る7セグメント時計

必要な部品と材料

Arduino Nano、DS3231 RTC、LDR自動輝度調整で作る7セグメント時計 Arduino Nano R3 (Arduino UnoやElegoo Unoでも動作) × 1
Adafruit 7セグメントバックパック - 1.2インチ (バンドル購入時は不要) × 1
Adafruit 1.2インチ 4桁 7セグメントディスプレイ I2Cバックパック付き - 緑 × 1
Adafruit DS3231 (DS1307でも可) × 1
Arduino Nano、DS3231 RTC、LDR自動輝度調整で作る7セグメント時計 抵抗 100kΩ × 1
Arduino Nano、DS3231 RTC、LDR自動輝度調整で作る7セグメント時計 フォトレジスタ(CdSセル) × 1
Arduino Nano、DS3231 RTC、LDR自動輝度調整で作る7セグメント時計 ブレッドボード × 1
Arduino Nano、DS3231 RTC、LDR自動輝度調整で作る7セグメント時計 ジャンパーワイヤ (10〜14本) × 1
コイン電池 CR1220 × 1

使用するアプリとオンラインサービス

Arduino Nano、DS3231 RTC、LDR自動輝度調整で作る7セグメント時計 Arduino IDE

プロジェクトについて

イントロ

腕時計やスマートフォンで常に時刻が確認できる現代において、Arduino Uno/Nanoで作る時計は少し地味かもしれません。また、NTPサーバーと同期する常時オンラインのデバイスも存在します。

背景

私の友人が個人的な理由で寝室にWiFi接続デバイスを置きたくないと言ったため、インターネット接続不要の独立した時計をArduinoで作ることにしました。

適切な製品の選択

Adafruitだけでも驚くほど多くのRTCモジュールがあります:電圧や精度の違いなど様々です。ここでは安価なDS1307と、より高精度なDS3231の2つを検討しました。

どちらもこの時計で使用可能です。DS1307を使う場合は、正しいピン配置と温度表示機能の制限(displayTemp()関数をコメントアウト)に注意してください。

スケッチの調整

既存のスケッチを参考にし、再構成しました。元のチェコ語コメントを残しつつ、英語の注釈を追加しています。

表示はできるだけ大きくしたかったため、時刻のみ表示なら7セグメントが最適と判断しました。

スケッチを見て嬉しかったのは、DS3231には温度取得機能があることです。DS1307にはこれがなく、精度も劣ります。

時計の初期化

CR1220ボタン電池がある場合、Adafruitのチュートリアルに従って時計を設定できます。要約すると、インターネットからではなくローカルPCの時刻を書き込みます。アップロード時のタイムスタンプがRTCに転送されます。

機能

LEDマトリクスで時刻と温度を表示します。時刻と温度の表示切替間隔は変数で設定可能です。

LEDマトリクス上の個々のドットや数字の制御(点滅含む)については、スケッチ内で詳しく説明しています。Adafruitのドキュメントも参考にしてください。

さらに、夏時間/冬時間の自動切替と、周囲の明るさに応じたLEDマトリクスの輝度調整(夜間は暗く、昼間は明るく)を実装しています。

動画で時刻と温度の切り替わりに気づいたかもしれません。左側のドットの位置(上/下)が秒に応じて変化しているのも確認できます。

フィードバック募集: 時計読み取り失敗時にLEDマトリクスに「ERR」を表示する方法をご存知の方は教えてください。現在は「bEEF」と表示されます:

Arduino Nano、DS3231 RTC、LDR自動輝度調整で作る7セグメント時計
// データなし!
Serial.println("DS3231 Sensor Error - cant read temperature");
matrix.print(0xBEEF, HEX); // ユーザーにBEEFで通知
matrix.writeDisplay();
delay(5000);

拡張案

更新履歴 (2019/02/01)

ブレッドボードからユニバーサル基板への移行が完了しました。関連写真:

Arduino Nano、DS3231 RTC、LDR自動輝度調整で作る7セグメント時計
Arduino Nano、DS3231 RTC、LDR自動輝度調整で作る7セグメント時計
Arduino Nano、DS3231 RTC、LDR自動輝度調整で作る7セグメント時計
Arduino Nano、DS3231 RTC、LDR自動輝度調整で作る7セグメント時計
Arduino Nano、DS3231 RTC、LDR自動輝度調整で作る7セグメント時計

更新版コード v1.1と修正済みFritzing図を添付しています。

コード

MyClock v1.0 C/C++
// MyClock v1.0
// Ingo Lohs
// Hardware: Arduino Uno / Nano (ATmega328P) で動作確認、Arduino IDE v1.8.8使用

/* HT16K33マトリクス理論
print(variable, HEX) - 16進数表示 (0000-FFFF)
print(variable, DEC) / print(variable) - 10進数表示 (0000-9999)
writeDigitNum(location, number) - 指定位置に数字(0-9)書き込み
  Location #0: 左端, #2: コロン(ドット), #4: 右端
writeDigitRaw(location, bitmap) - コロン/小数点制御
  Location 2のビットマップ:
  0x02 - 中央コロン(両ドット)
  0x04 - 左コロン・下ドット
  0x08 - 左コロン・上ドット
  0x10 - 小数点
writeDigitNum(location, number, true) - 小数点付き数字
drawColon(true/false) - コロン表示制御
writeDigitRaw(location, bitmask) - 全セグメント完全制御
全描画後は writeDisplay() でI2C送信必須
Source: https://www.mouser.com/ds/2/737/adafruit-led-backpack-932846.pdf */

#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"

#define DS3231_I2C_ADDRESS 0x68

int ldr_sensor = A0;        // LDR + 100kΩ to GND, 他方5V
int ldr_value = 0;
int brightness_matrix;
int threshold_brightness = 400;
int blinkrate_value = 0;
int delay_matrix_time = 20;    // 時刻表示時間(秒)
int delay_matrix_temp = 2000;  // 温度表示時間(ミリ秒)
int blinky_dot = 2;

Adafruit_7segment matrix = Adafruit_7segment();

byte decToBcd(byte val) { return ((val/10*16) + (val%10)); }
byte bcdToDec(byte val) { return ((val/16*10) + (val%16)); }

void setup() {
  Serial.begin(9600);
  Wire.begin();
  matrix.begin(0x70);
}

void loop() {
  lightBrightness();
  matrixBlinkrate();
  displayTime();
  displayTemp();
}

void lightBrightness() {
  ldr_value = analogRead(ldr_sensor);
  Serial.print("LDR: "); Serial.println(ldr_value);
  if (ldr_value <= threshold_brightness) {
    brightness_matrix = 0;  // 昼: 暗め
  } else {
    brightness_matrix = 15; // 夜: 明るめ
  }
}

void matrixBlinkrate() {
  matrix.blinkRate(blinkrate_value);
}

void displayTime() {
  matrix.setBrightness(brightness_matrix);
  matrix.clear();
  
  for (uint16_t i = 0; i < delay_matrix_time; i++) {
    byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
    getDateDs3231(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
    
    // 夏時間切替: 3月最終日曜日 2時 → 3時
    if ((dayOfWeek == 7) && (dayOfMonth >= 25) && (month == 3) && (hour == 2)) {
      setDS3231time(second, minute, 3, dayOfWeek, dayOfMonth, month, year);
    }
    // 冬時間切替: 10月最終日曜日
    if ((dayOfWeek == 7) && (dayOfMonth >= 25) && (month == 10) && (hour == 1) && (year != 1)) {
      setDS3231time(second, minute, hour, dayOfWeek, dayOfMonth, month, 1);
    }
    if ((dayOfWeek == 7) && (dayOfMonth >= 25) && (month == 10) && (hour == 3) && (year == 1)) {
      setDS3231time(second, minute, 2, dayOfWeek, dayOfMonth, month, 0);
    }

    // 時刻フォーマット: _0:mm または 00:mm
    if (hour > 9) {
      matrix.writeDigitNum(0, (hour / 10), false);
      matrix.writeDigitNum(1, (hour % 10), false);
    } else {
      matrix.writeDigitNum(1, hour, false);
    }

    // 左側ドットの点滅パターン (blinky_dot)
    if (second <= 30) {
      blinky_dot = 10; // 下ドット + 中央コロン
    } else {
      blinky_dot = 6;  // 上ドット + 中央コロン
    }
    matrix.writeDigitRaw(2, blinky_dot);
    
    matrix.writeDigitNum(3, (minute / 10), false);
    matrix.writeDigitNum(4, (minute % 10), false);
    matrix.writeDisplay();
    
    // コロン点滅 (0.5秒ごと)
    delay(500);
    matrix.drawColon(false);
    matrix.writeDisplay();
    delay(500);
  }
}

void displayTemp() {
  matrix.setBrightness(brightness_matrix);
  matrix.clear();
  byte temp = get3231Temp();
  int abs_temp = abs(temp);
  
  matrix.writeDigitNum(1, (abs_temp % 10), false);
  if (temp < 0) matrix.writeDigitRaw(0, 64);        // マイナス記号
  if (temp <= -10) matrix.writeDigitRaw(2, 12);     // -10度以下
  if (temp >= 10) matrix.writeDigitNum(0, (abs_temp/10), false);
  if (temp <= -10) matrix.writeDigitNum(0, (abs_temp/10), false);
  
  matrix.writeDigitRaw(3, 99); // "°"
  matrix.writeDigitRaw(4, 57); // "C"
  matrix.writeDisplay();
  
  Serial.print("Temperatur in C: ");
  Serial.println(get3231Temp());
  delay(delay_matrix_temp);
}

void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year) {
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0);
  Wire.write(decToBcd(second));
  Wire.write(decToBcd(minute));
  Wire.write(decToBcd(hour));
  Wire.write(decToBcd(dayOfWeek));
  Wire.write(decToBcd(dayOfMonth));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));
  Wire.endTransmission();
}

void getDateDs3231(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year) {
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0);
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
  *second = bcdToDec(Wire.read() & 0x7f);
  *minute = bcdToDec(Wire.read());
  *hour = bcdToDec(Wire.read() & 0x3f);
  *dayOfWeek = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month = bcdToDec(Wire.read());
  *year = bcdToDec(Wire.read());
}

float get3231Temp() {
  byte tMSB, tLSB;
  float temp3231;
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0x11);
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 2);
  if (Wire.available()) {
    tMSB = Wire.read();
    tLSB = Wire.read();
    temp3231 = (tMSB & B01111111);
    temp3231 += ((tLSB >> 6) * 0.25);
    return temp3231;
  } else {
    Serial.println("DS3231 Sensor Error - cant read temperature");
    matrix.print(0xBEEF, HEX);
    matrix.writeDisplay();
    delay(5000);
  }
}
MyClock v1.1 C/C++

夏時間/冬時間判定の曜日修正(日曜日=1に変更)

// MyClock v1.1
// Ingo Lohs
// 変更点: dayOfWeek == 7 → 1 に修正 (displayTime()内の夏時間/冬時間判定)

#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"

#define DS3231_I2C_ADDRESS 0x68

int ldr_sensor = A0;
int ldr_value = 0;
int brightness_matrix;
int threshold_brightness = 400;
int blinkrate_value = 0;
int delay_matrix_time = 20;
int delay_matrix_temp = 2000;
int blinky_dot = 2;

Adafruit_7segment matrix = Adafruit_7segment();

byte decToBcd(byte val) { return ((val/10*16) + (val%10)); }
byte bcdToDec(byte val) { return ((val/16*10) + (val%16)); }

void setup() {
  Serial.begin(9600);
  Wire.begin();
  matrix.begin(0x70);
}

void loop() {
  lightBrightness();
  matrixBlinkrate();
  displayTime();
  displayTemp();
}

void lightBrightness() {
  ldr_value = analogRead(ldr_sensor);
  Serial.print("LDR: "); Serial.println(ldr_value);
  if (ldr_value <= threshold_brightness) brightness_matrix = 0;
  else brightness_matrix = 15;
}

void matrixBlinkrate() {
  matrix.blinkRate(blinkrate_value);
}

void displayTime() {
  matrix.setBrightness(brightness_matrix);
  matrix.clear();
  
  for (uint16_t i = 0; i < delay_matrix_time; i++) {
    byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
    getDateDs3231(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
    
    // 夏時間: 3月最終日曜日(=1) 2時
    if ((dayOfWeek == 1) && (dayOfMonth >= 25) && (month == 3) && (hour == 2)) {
      setDS3231time(second, minute, 3, dayOfWeek, dayOfMonth, month, year);
    }
    // 冬時間
    if ((dayOfWeek == 1) && (dayOfMonth >= 25) && (month == 10) && (hour == 1) && (year != 1)) {
      setDS3231time(second, minute, hour, dayOfWeek, dayOfMonth, month, 1);
    }
    if ((dayOfWeek == 1) && (dayOfMonth >= 25) && (month == 10) && (hour == 3) && (year == 1)) {
      setDS3231time(second, minute, 2, dayOfWeek, dayOfMonth, month, 0);
    }

    if (hour > 9) {
      matrix.writeDigitNum(0, (hour / 10), false);
      matrix.writeDigitNum(1, (hour % 10), false);
    } else {
      matrix.writeDigitNum(1, hour, false);
    }

    if (second <= 30) blinky_dot = 10;
    else blinky_dot = 6;
    matrix.writeDigitRaw(2, blinky_dot);
    
    matrix.writeDigitNum(3, (minute / 10), false);
    matrix.writeDigitNum(4, (minute % 10), false);
    matrix.writeDisplay();
    delay(500);
    matrix.drawColon(false);
    matrix.writeDisplay();
    delay(500);
  }
}

void displayTemp() {
  matrix.setBrightness(brightness_matrix);
  matrix.clear();
  byte temp = get3231Temp();
  int abs_temp = abs(temp);
  
  matrix.writeDigitNum(1, (abs_temp % 10), false);
  if (temp < 0) matrix.writeDigitRaw(0, 64);
  if (temp <= -10) matrix.writeDigitRaw(2, 12);
  if (temp >= 10) matrix.writeDigitNum(0, (abs_temp/10), false);
  if (temp <= -10) matrix.writeDigitNum(0, (abs_temp/10), false);
  
  matrix.writeDigitRaw(3, 99);
  matrix.writeDigitRaw(4, 57);
  matrix.writeDisplay();
  
  Serial.print("Temperatur in C: ");
  Serial.println(get3231Temp());
  delay(delay_matrix_temp);
}

void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year) {
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0);
  Wire.write(decToBcd(second));
  Wire.write(decToBcd(minute));
  Wire.write(decToBcd(hour));
  Wire.write(decToBcd(dayOfWeek));
  Wire.write(decToBcd(dayOfMonth));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));
  Wire.endTransmission();
}

void getDateDs3231(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year) {
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0);
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
  *second = bcdToDec(Wire.read() & 0x7f);
  *minute = bcdToDec(Wire.read());
  *hour = bcdToDec(Wire.read() & 0x3f);
  *dayOfWeek = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month = bcdToDec(Wire.read());
  *year = bcdToDec(Wire.read());
}

float get3231Temp() {
  byte tMSB, tLSB;
  float temp3231;
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0x11);
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 2);
  if (Wire.available()) {
    tMSB = Wire.read();
    tLSB = Wire.read();
    temp3231 = (tMSB & B01111111);
    temp3231 += ((tLSB >> 6) * 0.25);
    return temp3231;
  } else {
    Serial.println("DS3231 Sensor Error - cant read temperature");
    matrix.print(0xBEEF, HEX);
    matrix.writeDisplay();
    delay(5000);
  }
}
GitHub

https://github.com/ILohs/MySketches

回路図

LDR経路修正版

Arduino Nano、DS3231 RTC、LDR自動輝度調整で作る7セグメント時計 Arduino Nano、DS3231 RTC、LDR自動輝度調整で作る7セグメント時計

製造プロセス

  1. Arduino POVクロック:残像効果で浮かび上がるLED時計を自作する
  2. Arduinoで作るレトロNumitron管時計:シンプル、信頼性高く、省エネ
  3. Adafruit 60 LED NeoPixelリングとArduinoで作るスタイリッシュな壁掛け時計
  4. Arduino で作るイスラム教礼拝時刻付きリアルタイムクロック
  5. 12メートル先から見える大型LEDマスタークロック(アラーム機能付き)を自作しよう
  6. Arduino Dueと3.2インチTFTで作る温度監視付きリアルタイムクロック
  7. Arduino Nanoとジョイスティックで2軸ステッピングモーターを制御:Visuinoで簡単プログラミング
  8. DIY Arduino スマートウォッチ — Apple Watch 風腕時計を自作
  9. Arduino Pro MiniとDS1302 RTCで作る高精度7セグメント時計
  10. Arduino Nanoで作る携帯型ガイガーカウンター
  11. DS1302 RTCとArduino UNOで作る信頼性の高い目覚まし時計