Прежде чем делать свой скетч, посмотрел в Интернете, что делают другие. И вот мне попался интересный проект http://arduinolab.pw/index.php/2016/06/23/chasy-na-arduino/, решил взять его за основу.
Повторив проект, обнаружил, что он имеет ряд недостатков. Существенные недостатки, на мой взгляд, это:
Отсутствие регулировки яркости индикатора.
Отсутствие гашения незначащего нуля.
Поэтому эти недостатки устранил, и в моём проекте уже присутствует и гашение нуля, и регулировка яркости, значение которой записывается в EEPROM, чтобы при включении часов яркость выставлялась та, которую мы установили.
Кто-то делает автоматическую регулировку яркости с помощью фоторезистора. Так вполне можно сделать, но я не стал, нет необходимости в такой опции, хотя проект и можно расширить, или модернизировать на такой вариант.
Ещё хотел реализовать будильник, но потом что-то передумал и выведенная кнопка на переднюю панель для установки будильника так и осталась не подключенной. Может быть при необходимости и доработаю часы под будильник.
Схема часов
Схема часов не сложная. Благодаря тому, что индикатор управляется по протоколу IIC (I2C), соединений совсем немного. Блок питания используется импульсный, обратноходовый, от какой-то зарядки для телефона. Выдаёт он +6,5 В. Это напряжение подаётся на стабилизатор Arduino Pro Mini – на контакт RAW.
Pic 1. Схема электрическая принципиальная для скетча #8
Монтажную схему см. ниже.
Pic 2. Схема монтажная для скетча #8
В реальности у меня не четыре кнопки, а пять. Пятая задумывалась для управления установкой будильника, но так до него и не дошло. Ещё была мысль этой кнопкой выводить число и месяц... может быть потом допишу программу и реализую эту функцию.
Конструкция часов
Корпус традиционно склеен из пластика ABS. Габариты 80 х 60 х 54 мм (Ш х Г х В). Сама конструкция представляет собой блок из модулей: индикатора, реального времени, платы с Ардуино, модуля питания и планки с кнопками. Arduino Pro Mini вставлено в панельку DIP24, которая распаяна на монтажной плате. К этой плате и идут соединения от модулей. Конструктивно модули соединены между собой стойками из нейлона и винтами М3. К этим же стойкам прикручивается и задняя крышка. Блок из модулей вставлен в корпус и фиксируется снизу так же винтом М3.
Обращу внимание на то, что сверху установлена клавиша выключения внешнего питания. Нет смысла жечь индикатор пока целый день на работе. Так как в модуле реального времени имеется своя литивая батарейка на 3 В, то время продолжает отсчитываться независимо от питания Ардуино и индикатора. Очень удобная функция.
Кнопки управления вынес на переднюю панель и немного утопил вглубь. Сейчас объясню зачем так. Есть у меня часы ASSISTANT ah-1066, габариты у них чуть-чуть больше, а индикатор такой же, зелёненький. Так у них кнопки управления вынесены на верхнюю крышку, что с моей точки зрения и опыта эксплуатации такой конструкции не совсем удобно, да и пыль на них оседает. Батарейки в тех часах мизинчиковые (ААА) 3 шт. – недолговечно. Нет регулировки яркости индикатора. Поэтому, создавав свою конструкцию старался устранить эти недостатки. К примеру, когда кнопки впереди, то легко подстраивать или устанавливать время, так как одновременно видишь и индикатор, и кнопки.
Ниже небольшой фотоотчёт по конструкции часов.
Pic 3. Часы. Внешний вид, разные ракурсы Pic 4. Часы. Внешний вид, разные ракурсы Pic 5. Часы. Внешний вид, вид снизу Pic 6. Часы. Внешний вид, снята задняя крышка Pic 7. Часы. Вынут блок часов из корпуса Pic 8. Часы. Вынут блок-модуль часов из корпуса Pic 9. Часы. Блок-модуль часов разъединён. Отделена плата RTC DS3231 Pic 10. Часы. Блок-модуль часов разъединён. Отделена плата RTC DS3231 Pic 11. Часы. Блок-модуль часов разъединён. Отделена плата индикатора TM1637 и панель кнопок Pic 12. Часы. Окончательный вариант
Скетч часов
Далее представлен скетч. Рассмотрю некоторые его особенности. Так, у автора для мигания точек используется сигнал с модуля реального времени с контакта 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°С.