Часы на 7-сегментном дисплее с Arduino Nano, DS3231 RTC и автояркостью от LDR
Компоненты и материалы
| Arduino Nano R3 (подходит Uno или Elegoo Uno) | ×1 |
| Adafruit 7-Segment Backpack — 1.2″ Tall Digits | ×1 |
| Adafruit 1.2″ 4-Digit 7-Segment Display w/I2C Backpack — Green | ×1 |
| Adafruit DS3231 (или DS1307) | ×1 |
| Резистор 100 кОм | ×1 |
| Фоторезистор (LDR) | ×1 |
| Макетная плата | ×1 |
| Перемычки (10-14 шт.) | ×1 |
| Элемент питания CR1220 (для RTC) | ×1 |
Приложения и сервисы
- Arduino IDE
О проекте
Введение
В эпоху смартфонов и умных часов обычные Arduino-часы могут показаться скучными. Но этот проект создает автономные часы без Wi-Fi — идеально для спальни, где нежелательны беспроводные сети.
История
Друг отказался от устройств с Wi-Fi в спальне, поэтому я собрал часы на отдельном RTC-модуле.
Выбор компонентов
Adafruit предлагает много вариантов: дешевый DS1307 и более точный DS3231. Оба подходят. Для DS1307 обратите внимание на распиновку и отключите функцию отображения температуры (закомментируйте displayTemp() в loop()). DS3231 умеет измерять температуру, чего у DS1307 нет.
Адаптация скетча
Базовый скетч взят из сообщества, переработан, сохранены чешские комментарии с добавлением английских. Выбран 7-сегментный индикатор максимального размера для лучшей читаемости.
Инициализация часов
Если есть батарея CR1220, настройте время по краткому руководству Adafruit: время берется с компьютера при загрузке скетча.
Функции
- Вывод времени и температуры на LED-матрицу (период смены настраивается переменными).
- Управление мигающими точками и разрядами описано в коде.
- Автоматический переход на летнее/зимнее время.
- Автоматическая регулировка яркости дисплея от окружающего освещения (LDR): ночью экран темнее.
- Левая точка на дисплее меняет положение (вверх/вниз) каждые 30 секунд.
При ошибке чтения температуры на дисплее показывается BEEF (см. код).
Расширение
Планируется добавить будильник и, возможно, Bluetooth-модуль с Blynk.
Обновление 01.02.2019
Перенос с макетной платы на монтажную завершен. Фото в галерее. Доступен обновленный код v1.1 и исправленная схема в Fritzing.
Код
MyClock v1.0 (C/C++)
// MyClock v1.0
// Ingo Lohs
// Hardware: tested with Arduino Uno and Arduino Nano (ATmega328P [Old Bootloader]) by using Arduino IDE v1.8.8
/* Theory for the Matrix ht16k33 ... */
#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);
byte se = second, mi = minute, ho = hour, we = dayOfWeek, dm = dayOfMonth, mo = month, ye = year;
// Переход на летнее время - последнее воскресенье марта в 2:00
if ((dayOfWeek == 7) && (dayOfMonth >= 25) && (month == 3) && (hour == 2)) {
setDS3231time(se, mi, 3, we, dm, mo, ye);
}
// Переход на зимнее время
if ((dayOfWeek == 7) && (dayOfMonth >= 25) && (month == 10) && (hour == 1) && (year != 1)) {
setDS3231time(se, mi, ho, we, dm, mo, 1);
}
if ((dayOfWeek == 7) && (dayOfMonth >= 25) && (month == 10) && (hour == 3) && (year == 1)) {
setDS3231time(se, mi, 2, we, dm, mo, 0);
}
// Управление левыми точками (blinky_dot)
// ... (подробные комментарии в коде)
if (hour > 9) {
matrix.writeDigitNum(0, (hour / 10), false);
matrix.writeDigitNum(1, (hour % 10), false);
} else {
matrix.writeDigitNum(1, hour, false);
}
if (se <= 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); // 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++) — исправлен расчет воскресенья для перехода на летнее/зимнее время
Изменение: dayOfWeek == 1 вместо 7 в функции displayTime() для корректного определения воскресенья. Полный код аналогичен v1.0 с этим исправлением.
Исходники на GitHub: https://github.com/ILohs/MySketches
Схемы
Исправленная схема подключения LDR:


Производственный процесс
- Arduino POV-часы: сборка дисплея на эффекте сохранения образа
- Ретро-часы на индикаторах Numitron с Arduino: просто, надёжно, экономно
- Стильные настенные часы на четырех кольцах Adafruit Neopixel 60 LED и Arduino
- Часы реального времени на Arduino с расчётом времени намаза
- Создание пользовательских LED-часов с будильником — видно с 12 метров
- Монитор температуры и реальные часы на Arduino Due с TFT-дисплеем 3.2"
- Управление двумя шаговыми двигателями через джойстик на Arduino Nano — простой урок (Visuino)
- DIY Arduino Smartwatch: самодельные умные часы в стиле Apple Watch
- Точные 7-сегментные часы на Arduino Pro Mini и DS1302 RTC
- Надёжные будильник на DS1302 RTC и Arduino UNO — сборка и код