Прежде чем делать свой скетч, посмотрел в Интернете, что делают другие. И вот мне попался интересный проект http://arduinolab.pw/index.php/2016/06/23/chasy-na-arduino/, решил взять его за основу.
Повторив проект, обнаружил, что он имеет ряд недостатков. Существенные недостатки, на мой взгляд, это:
Отсутствие регулировки яркости индикатора.
Отсутствие гашения незначащего нуля.
Поэтому эти недостатки устранил, и в моём проекте уже присутствует и гашение нуля, и регулировка яркости, значение которой записывается в EEPROM, чтобы при включении часов яркость выставлялась та, которую мы установили.
Кто-то делает автоматическую регулировку яркости с помощью фоторезистора. Так вполне можно сделать, но я не стал, нет необходимости в такой опции, хотя проект и можно расширить, или модернизировать на такой вариант.
Ещё хотел реализовать будильник, но потом что-то передумал и выведенная кнопка на переднюю панель для установки будильника так и осталась не подключенной. Может быть при необходимости и доработаю часы под будильник.
Схема часов
Схема часов не сложная. Благодаря тому, что индикатор управляется по протоколу IIC (I2C), соединений совсем немного. Блок питания используется импульсный, обратноходовый, от какой-то зарядки для телефона. Выдаёт он +6,5 В. Это напряжение подаётся на стабилизатор Arduino Pro Mini – на контакт RAW.
Монтажную схему см. ниже.
В реальности у меня не четыре кнопки, а пять. Пятая задумывалась для управления установкой будильника, но так до него и не дошло. Ещё была мысль этой кнопкой выводить число и месяц... может быть потом допишу программу и реализую эту функцию.
Конструкция часов
Корпус традиционно склеен из пластика ABS. Габариты 80 х 60 х 54 мм (Ш х Г х В). Сама конструкция представляет собой блок из модулей: индикатора, реального времени, платы с Ардуино, модуля питания и планки с кнопками. Arduino Pro Mini вставлено в панельку DIP24, которая распаяна на монтажной плате. К этой плате и идут соединения от модулей. Конструктивно модули соединены между собой стойками из нейлона и винтами М3. К этим же стойкам прикручивается и задняя крышка. Блок из модулей вставлен в корпус и фиксируется снизу так же винтом М3.
Обращу внимание на то, что сверху установлена клавиша выключения внешнего питания. Нет смысла жечь индикатор пока целый день на работе. Так как в модуле реального времени имеется своя литивая батарейка на 3 В, то время продолжает отсчитываться независимо от питания Ардуино и индикатора. Очень удобная функция.
Кнопки управления вынес на переднюю панель и немного утопил вглубь. Сейчас объясню зачем так. Есть у меня часы ASSISTANT ah-1066, габариты у них чуть-чуть больше, а индикатор такой же, зелёненький. Так у них кнопки управления вынесены на верхнюю крышку, что с моей точки зрения и опыта эксплуатации такой конструкции не совсем удобно, да и пыль на них оседает. Батарейки в тех часах мизинчиковые (ААА) 3 шт. – недолговечно. Нет регулировки яркости индикатора. Поэтому, создавав свою конструкцию старался устранить эти недостатки. К примеру, когда кнопки впереди, то легко подстраивать или устанавливать время, так как одновременно видишь и индикатор, и кнопки.
Ниже небольшой фотоотчёт по конструкции часов.
Скетч часов
Далее представлен скетч. Рассмотрю некоторые его особенности. Так, у автора для мигания точек используется сигнал с модуля реального времени с контакта SQW, который подаётся на контакт 2 Ардуино. На этом контакте установлено внешнее прерывание 0 на изменение (CHANGE). Можно реализовать и по-другому, к примеру, через Таймер1. Оставил так, как у автора, может быть в другом проекте сделаю через Таймер1, для разнообразия.
Весь код подробно закомментирован, с его пониманием, надеюсь, проблем не будет. Хочу лишь обратить внимание, что когда происходит запись значения в EEPROM, то индикатор гашу на пол секунды, чтобы видеть, что команда записи отработала. В некотором роде – индикация записи.
/*************************************************************
* 2018-05-25 Mr.ALB
*
* Основа взята с сайта:
* http://arduinolab.pw/index.php/2016/06/23/chasy-na-arduino/
* Часы на 7 сегментном индикаторе TM1637
* и модуле реального времени RTC DS3231
*
* Модуль RTC DS3231:
* SCL – A5
* SDA – A4
* SQW – 2 – Для мигания разделительных точек
* VCC – 5V
* GND – GND
*
* Подключить кнопки управления на пины и на GND
*
* 2018-11-20 Запись значения яркости в EEPROM
* 2018-11-22 Гашение незначащего нуля
* 2020-01-02 Доработка считывания яркости из EEPROM
* 2020-05-06 Доработка считывания из EEPROM
*************************************************************/#include <EEPROM.h> // для EEPROM#include <Wire.h>
#include"TM1637.h"// 7 сигментный индикатор TM1637#define CLK 6
#define DIO 7
TM1637 tm1637(CLK,DIO); // Создание объекта индикатора// Кнопки установки времени#define buttonHour 5 // Часы#define buttonMin 4 // Минуты#define buttonSet 3 // Установка#define buttonBR 8 // Кнопка яркости// Адрес часов реального времени#define DS3231_I2C_ADDRESS 0x68
byte brightness = 1; // Яркость, от 1 до 7byte addrBR = 6; // Адрес в EEPROM для записи яркостиboolean flag_br = false;// Флаг для записи яркостиboolean flag_null=false;// Флаг для гашения незначащего нуляvolatileboolean flag; // Переменная для прерывания/********************** **** Подпрограммы **** **********************//* часы .. */byte decToBcd(byte val)
{
return ((val/10*16) + (val%10));
}
byte bcdToDec(byte val)
{
return ((val/16*10) + (val%16));
}
/* Установка данных в модуль RTC DS3231 */void setDateDs3231
(
bytesecond, // 0-59byteminute, // 0-59bytehour, // 1-23bytedayOfWeek, // 1-7bytedayOfMonth, // 1-28/29/30/31bytemonth, // 1-12byteyear// 0-99
)
{
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();
}
/* Получение данных с модуля RTC DS3231 */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());
}
/* Включаем выход SQW,
* который вроде выключен по умолчанию
*/void setINT()
{
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0x0E);
Wire.write(0x0);
Wire.endTransmission();
}
/* Для мигания точек */voidblink()
{
digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));
flag = !flag;
tm1637.point(flag);
}
voidsetup()
{
Wire.begin();
// Для мигания точекpinMode(LED_BUILTIN, OUTPUT);
// Кнопка изменения часовpinMode(buttonHour, INPUT_PULLUP);
// Кнопка изменения минутpinMode(buttonMin, INPUT_PULLUP);
// Кнопка разрешения установокpinMode(buttonSet, INPUT_PULLUP);
// Кнопка изменения яркости pinMode(buttonBR, INPUT_PULLUP);
// Инициализация индикатора
tm1637.init();
// Включаем выход SQW на RTC3231
setINT();
// Установка начальной яркости
tm1637.set(brightness);
// Установка значений из EEPROM
// Считывание яркости из EEPROM byte setBR = EEPROM.read(addrBR);
if(setBR >= 0 && setBR < 8 && setBR != brightness) tm1637.set(setBR);
// Подключаем прерывание 0 на пин 2attachInterrupt(0, blink, CHANGE);
// Или подключаем прерывание 1 на пин 3
// Тогда убрать строчки с прерыванием 0 // attachInterrupt(1, blink, CHANGE);
}
voidloop()
{
bytesecond,
minute,
hour,
dayOfWeek,
dayOfMonth,
month,
year;
// Читаем время из модуля
getDateDs3231
(
&second,
&minute,
&hour,
&dayOfWeek,
&dayOfMonth,
&month,
&year
);
// Заполняем массив значениями
// для отпарвки на индикаторint8_t TimeDisp[4];
TimeDisp[0] = hour / 10; // Десятки часов
TimeDisp[1] = hour % 10; // Единицы часов
TimeDisp[2] = minute / 10;// Десятки минут
TimeDisp[3] = minute % 10;// Единицы минут/* Обработка кнопок * Изменения при одновременном совместном нажитии
* на кнопку buttonSet */if(
!digitalRead(buttonHour) &&
!digitalRead(buttonSet)
)
{ // Часы// сбрасываем секундыsecond = 0;
// прибавляем единицу к часамhour++;
// если вылезли за границы присваеваем 0if(hour > 23) hour = 0;
// записываем в модуль
setDateDs3231
(
second,
minute,
hour,
dayOfWeek,
dayOfMonth,
month,
year
);
delay(200);
}
if(
!digitalRead(buttonMin) &&
!digitalRead(buttonSet)
)
{ // Минутыsecond = 0;
minute++;
if(minute > 59) minute = 0;
setDateDs3231
(
second,
minute,
hour,
dayOfWeek,
dayOfMonth,
month,
year
);
delay(200);
}
if(
!digitalRead(buttonBR) &&
!digitalRead(buttonSet)
)
{ // Яркость индикатора
brightness++;
if (brightness > 7) brightness = 1;
tm1637.set(brightness);
flag_br = true;
delay(200);
}
if(
flag_br==true &&
digitalRead(buttonSet)==HIGH
)
{ // Запись значения в EEPROM
// при отжатой кнопке buttonSet
flag_br=false;
EEPROM.write(addrBR, brightness);
// Гашение экрана как индикация записи
tm1637.clearDisplay();
delay(900);
}
// Отправляем массив на индикаторif(TimeDisp[0]!=0)
{ // Если первое значение не 0, то выводимif(flag_null)flag_null=false; // Сбросим флаг нуля
tm1637.display(0,TimeDisp[0]);// Вывод первой цифры
}
else
{ // Иначе гашение незначащего нуля// Гасим один первый раз при смене на 0 if(!flag_null)
{
flag_null=true; // Выставим флаг нуля
tm1637.clearDisplay();// Гашение экрана
}
}
tm1637.display(1,TimeDisp[1]); // Вывод второй цифры
tm1637.display(2,TimeDisp[2]); // Вывод третьей цифры
tm1637.display(3,TimeDisp[3]); // Вывод четвёртой цифры
}
Этими часами уже пользуюсь длительное время, до этого использовал сам блок-модулей, а теперь уже сделан полноценный корпус.
На данный момент получилось то, что получилось . Конструкция полностью товарная готовая к использованию в обиходе. Довольно удобно. Рекомендую к повторению.
Вариант мигания точек, если нет SQW
Несколько раз меня спрашивали что делать, если нет вывода SQW на DS3231, могу предложить использовать прерывания по Таймеру1. Доработка скетча не займёт много времени, а эффект тот же, что и с выводом SQW.
/*******************************************************************
* 2021-08-27 MrALB Вариант мигания точек, если нет SQW
* Вопрос:
* "Здравствуйте. На моей DS3231-mini нет вывода SQW. Как быть?"
* Ответ:
* "Используйте Таймер1"
*
* Ниже небольшой пример как использовать Таймер1
********************************************************************/#include<TimerOne.h>// Подключение библиотеки Таймер1// Уберите в скетче строчки 113-123, 149-150// Далее...voidsetup(void)
{
// Заменить строчки 160-161 на эти:Timer1.initialize(500000); // инициализация 500 мс или сколько нужноTimer1.attachInterrupt(blink);// обработка прерывания
}
Проверено мной и подписчиками – работает. В последующих проектах уже сразу использую этот метод.
2020-01-02Доработка программы 2020-05-06Доработка программы 2021-08-27Доработка программы
Проект получил логическое продолжение. В обновлённой версии всё сделано по другому. Добавлен будильник. Добавлены вывод температуры и измерение атмосферного давления. Внешний датчик температуры позволяет измерять в диапазоне от -55°С до +125°С.