Электронные часы на Arduino. С будильником, таймером и другими плюшками :) Начало.
Буду собирать электронные носимые часы которые будут показывать не только время, ещё там будет будильник, таймер и секундомер. Это основные функции с которых и начну. Остальное буду добавлять по ходу пьесы.
Итак, допустим я уже освоил Arduino (хотя это не так, но хочется верить) и я хочу собрать великий проект о котором заговорят все и имя моё будет вписано в историю золотыми буквами... :-)
На самом деле это заметки по ходу работы и они будут корректироваться и изменяться. А заодно и мои мысли систематизируют. Для тех кто читает, не воспринимайте данную статью как готовый проект, это "мысли в слух". Я вам гарантирую и путанные описания и ошибки, в том числе и грамматические.
На самом деле это заметки по ходу работы и они будут корректироваться и изменяться. А заодно и мои мысли систематизируют. Для тех кто читает, не воспринимайте данную статью как готовый проект, это "мысли в слух". Я вам гарантирую и путанные описания и ошибки, в том числе и грамматические.
Собирать буду долго, так как базовый курс программирования отличается от реальности, с электроникой не знаком. Могу паять, преимущественно что-то не меньше розетки. Часть схем и програмного кода буду брать из интернета, что-то буду узнавать у знакомых. В общем, типичный новичок.
Для проекта использую:
Arduino UNO (копия) - для отладки,
Макетная площадка
Провода
Модуль с часами реального времени RTC (убрал из проекта)
Жк дисплей от Nokia5110
Резисторы
Кнопки
Компьютер
Паяльник
Припой
И кучу свободного времени.
Arduino UNO (копия) - для отладки,
Макетная площадка
Провода
Модуль с часами реального времени RTC (убрал из проекта)
Жк дисплей от Nokia5110
Резисторы
Кнопки
Компьютер
Паяльник
Припой
И кучу свободного времени.
![]() |
Меню электронных часов |
Эскиз меню часов виден на картинке.
#здесь будет картинка#
#здесь будет картинка#
Подключаю компоненты согласно рисунку.
Заливаем скетч и любуемся. Любоваться придётся долго, пока я буду делать нормальный будильник.
Заливаем скетч и любуемся. Любоваться придётся долго, пока я буду делать нормальный будильник.
Имеется три графических библиотеки, но я пока использую всего две: adafruit и u8glib. Каждая по своему хороша и плоха. Выбор будет за самой легкой по коду и размеру.
В общем я начал использовать u8glib, но в какой то момент столкнулся с проблемой масштабирования шрифтов и пришлось использовать библиотеку adafruit.
Я провел перевод кода с одной библиотеки в другую. Перевод прошел успешно. Замена команд из одной библиотеки на другую создала небольшую задержку. Главное в этой ситуации найти аналог команд.
Победила дружба :) Не значительная разница. Они по разному используют память.
Библиотека U8lib
Sketch uses 18 378 bytes (56%) of program storage space. Maximum is 32 256 bytes.
Global variables use 472 bytes (23%) of dynamic memory, leaving 1 576 bytes for local variables. Maximum is 2 048 bytes.
Библиотека Adafruit GFX
Sketch uses 8 966 bytes (27%) of program storage space. Maximum is 32 256 bytes.
Global variables use 867 bytes (42%) of dynamic memory, leaving 1 181 bytes for local variables. Maximum is 2 048 bytes.
Вроде Adafruit меньше ресурсов использует.
В общем я начал использовать u8glib, но в какой то момент столкнулся с проблемой масштабирования шрифтов и пришлось использовать библиотеку adafruit.
Я провел перевод кода с одной библиотеки в другую. Перевод прошел успешно. Замена команд из одной библиотеки на другую создала небольшую задержку. Главное в этой ситуации найти аналог команд.
Например в adafruit команда для рисовании линии display.drawLine(x1, y1, x2, y2, color), у u8glib она будет такая u8g.drawLine(x1,y1,x2,y2), т.е. по сути команда практически похожа, ну и иногда координаты поменяны.Вся эта затея была предпринята ради особого шрифта в u8glib который был хорош, но не масштабированный. На днях я нашел решение по введению своих шрифтов, нужно попробовать и сравнить вообще библиотеки по объему занимаемой памяти. Конечно в u8glib чуть больше возможностей с использованием графических команд, но adafruit мне показалась проще в освоении. На данный момент я соберу два скетча с разными графическими библиотеками и посмотрю какая из них меньше весит.
Победила дружба :) Не значительная разница. Они по разному используют память.
![]() |
Скомпилировал два скетча на разных библиотеках и вот что получил. |
Sketch uses 18 378 bytes (56%) of program storage space. Maximum is 32 256 bytes.
Global variables use 472 bytes (23%) of dynamic memory, leaving 1 576 bytes for local variables. Maximum is 2 048 bytes.
Библиотека Adafruit GFX
Sketch uses 8 966 bytes (27%) of program storage space. Maximum is 32 256 bytes.
Global variables use 867 bytes (42%) of dynamic memory, leaving 1 181 bytes for local variables. Maximum is 2 048 bytes.
Вроде Adafruit меньше ресурсов использует.
В самом начале я пытался разобраться со всем кодом сразу в одном скече. Со временем я пришел к выводу что лучше сделать отдельно каждый скеч на один экран, а уже позже объединить все в один.
Управление планируется при помощи двух кнопок, третья для подсветки.
Главный экран показывает всю необходимую информацию, такую как:
День недели - прописью,
Часы, минуты, секунды,
День и название месяца.
Теперь нужно разобраться как работают часы и что вообще там написано.
В первых строчках понятно, это те библиотеки которые я использую в скетче.
Это список используемых переменных. Я тут накуролесил, аж самому страшно.
Для расчета времени используется библиотека Time.h
Ниже void в котором расписан этот процесс. В теории можно добавить функцию корректировки времени.
Наконец, закончил с названиями дней недели и месяцами. Стандартные шрифты не подходили. Сложности возникли из за количества точек по горизонту. Их 48 и туда нужно вместить 11 символов в слове "понедельник", плюс пробелы. Буквы получились 3*5. С месяцами возникла сложность из за "сентября". Он имеет 8 букв, но с левой стороны показана дата, плюс еще две цифры и пробелы. Буквы по размеру были подогнаны к основному шрифту 5*5. Методом проб и ошибок всё вместилось и выглядит хорошо.
//display.setCursor(int(month())+6, 83);
В координаты можно добавлять переменную месяц (при желании и минуты и день и год) и высчитывать новые координаты исходя из этих данных. Т.е. если выпал первый месяц, то 1+6=7, а если 7, то 7+6=13.
С автоматическим изменением координат все тоже было решено. Не сразу, но я получил необходимый результат. Мне нужно что-бы название месяца и дата центрировались. Из за того что координаты даты и координаты названия месяца прописываются в разных командных строчках мне не удавалось объединить их координаты. Может это можно как-то сделать, но я на данный момент не знаю этого. В итоге пришлось создать дополнительную переменную и прописать условия.
Итак, на данный момент показ часов на главном экране сделан, но не закончен т.к. требует отладки и оптимизации.
По логике нужно переходить к календарю, но у меня есть желание закончить лунный календарь.
Управление планируется при помощи двух кнопок, третья для подсветки.
![]() |
Главный экран электронных часов. |
Часы. (Закончил)
https://blogruoff.blogspot.com/2018/11/arduino.htmlГлавный экран показывает всю необходимую информацию, такую как:
День недели - прописью,
Часы, минуты, секунды,
День и название месяца.
Теперь нужно разобраться как работают часы и что вообще там написано.
Этот код у меня компилируется только на версии Arduino IDE 1.6.5 и ниже. Выше версии 1.6.5 компиляция вываливает кучу ошибок.
В первых строчках понятно, это те библиотеки которые я использую в скетче.
#include Adafruit_GFX.h; //графическая библиотека #include Adafruit_PCD8544.h; //библиотека для работы с дисплеем #include Time.h; //библиотека для функций расчета времени #include SPI.h; //библиотека для работы с модулями #include Fonts/Org_01.h; //основной шрифт в часах
Это список используемых переменных. Я тут накуролесил, аж самому страшно.
int knopkapauza = 250; //задержка нажатия кнопки и смена меню от случайного нажатия //эти данные будут постоянные после сброса
int chas = 12; //переменная (int) в которой хранится час.
int minuta = 59; //переменная минуты int sekunda = 55; //переменная секунды int den = 19; //переменная день //день недели высчитывается автоматически int mesyac = 12; //переменная месяц int god = 2017; //переменная год int knopset = 10; //кнопка выбор пин 10 int knopmenu = 11; //кнопка меню пин 11 int knopled = 12; //кнопка подсветки пин 12 (я не помню почему, но можно только этот пин) int vkluchiled = 0; //подсветка выключено int lichtwaarde = 0; //подсветка/выбор задержка через 0 int menuwaarde = 0; //меню/дальше задержка через 0 //float Тип данных для чисел с плавающей точкой (чисел с десятичным разделителем).
float millichas = 0; //эти данные для расчета времени float milliminuta = 00; float millisekunda = 0; float millidays = 4000; int tijd = 0; //время int milli = 0; float millitijd = 0; int oudesekunda = 0; int ledpin = 9; //вывод для подачи питания на жк int licht = LOW; int lichtsterkte = 15; //яркость света(от 0 до 254) int schermcontrast = 45; //49-95 int globalmenu= 1; //первый пункт меню (можно использовать любой номер меню) //Переменные типа long обладают расширенным размером для хранения чисел
// имеют размерность 32 бита (4 байта), что позволяет им хранить числа
//в диапазоне от -2 147 483 648 до 2 147 483 647. long tussentijd = 0; //промежуточное время long teller = 0; //счетчик long tellerStart = 0; //счетчик старт //- pin 7 - Serial clock out (SCLK) //- pin 6 - Serial data out (DIN) //- pin 5 - Data/Command select (D/C) //- pin 4 - LCD chip select (CS) //- pin 3 - LCD reset (RST) //- Устанавливаем номера пинов к которым подключен дисплей Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 5, 4, 3);Короче, фиг его знает как разбираться с переменными...
Для расчета времени используется библиотека Time.h
Ниже void в котором расписан этот процесс. В теории можно добавить функцию корректировки времени.
void setmillidays() { millichas = hour() * 41.6667; //расчет часа milliminuta = minute() * 0.6944; //расчет минуты if (oudesekunda == second()) { milli = millis() - tijd; millitijd = milli * 0.0000116; } else { millitijd = 0; milli = millis() - tijd; tijd = millis(); } oudesekunda = second(); millisekunda = oudesekunda * 0.0116; millidays = millichas + milliminuta + millisekunda + millitijd; }
После многих вариантов был выбран ровный шрифт для часов по нескольким причинам. Первое и основное, на данном разрешении экрана фигурные шрифты выглядят убого и плохо читаются. Размер шрифта показывающего часы был выбран исходя из золотого сечения, который равен 1,61. Т.е. исходя из размера минут равного 20 точкам по высоте, умножаем на 1,61 и получим 32,2. Ноль два убираем и получаем высоту шрифта часов равную 32 точки. Ширину оставляю равную минутным шрифтам т.е. 20 точек.Наконец, закончил с названиями дней недели и месяцами. Стандартные шрифты не подходили. Сложности возникли из за количества точек по горизонту. Их 48 и туда нужно вместить 11 символов в слове "понедельник", плюс пробелы. Буквы получились 3*5. С месяцами возникла сложность из за "сентября". Он имеет 8 букв, но с левой стороны показана дата, плюс еще две цифры и пробелы. Буквы по размеру были подогнаны к основному шрифту 5*5. Методом проб и ошибок всё вместилось и выглядит хорошо.
//display.setCursor(int(month())+6, 83);
В координаты можно добавлять переменную месяц (при желании и минуты и день и год) и высчитывать новые координаты исходя из этих данных. Т.е. если выпал первый месяц, то 1+6=7, а если 7, то 7+6=13.
С автоматическим изменением координат все тоже было решено. Не сразу, но я получил необходимый результат. Мне нужно что-бы название месяца и дата центрировались. Из за того что координаты даты и координаты названия месяца прописываются в разных командных строчках мне не удавалось объединить их координаты. Может это можно как-то сделать, но я на данный момент не знаю этого. В итоге пришлось создать дополнительную переменную и прописать условия.
Итак, на данный момент показ часов на главном экране сделан, но не закончен т.к. требует отладки и оптимизации.
По логике нужно переходить к календарю, но у меня есть желание закончить лунный календарь.
Лунный календарь. (Закончил)
https://blogruoff.blogspot.com/2018/11/arduino.html![]() |
Так выглядят фазы луны. |
На данный момент показ фаз луны полностью изменен и находится в стадии тестирования.
![]() |
Предполагаемый вариант отрисовки луны. |
Ниже образец расчета первой линии. Повторить 42 раза :)
display.drawFastHLine(20, 24, drawmoon*0.71, 1);
нарисовать горизонтальную линию
20 и 24 начало координат прорисовки линии
расчет длинны линии:
10/14=0.71 количество точек / количество дней полнолуния/новолуния
drawmoon*0.71 переменная текущего дня * разницу точек
цвет линии черный
Сделал через строчную закраску луны, как на картинке выше. Не понравилось. Буду делать полную заливку луны. Сразу встал вопрос в количестве строк. Если использовать индивидуальную систему координат, то для отрисовки луны с первого дня по 14 нужно 43 строки (1 на отрисовку круга и 42 линии внутри круга). И еще 43 строки для отрисовки с 15 по 29 день, т.к. там меняется цвет и формула расчета координат.
И все же решение было найдено. Я сделаю переменные для изменения координат и цвета линии. Они будут изменятся при определенных параметрах. Если лунный день равен от 1 до 14, то берем номер дня и цвет черный. Если равен от 15 до 29, то номер дня -15 и цвет белый.
Ниже пример конвертации. Она была нужна для перевода значения дня в число.
//конвертация string в int int drawmoon;
String dm = (const char*) nfm.c_str();
//эта строка преобразовывает значения из char в string
drawmoon=dm.toInt();
//то что перед точкой конвертируется в то значение которое стоит после точки
Ниже фото тестовых экранов лунного календаря. Они конечно не лучшего образца, но суть будет ясна.Верхняя строка где цифры "27 27" - обозначают количество оставшихся дней до полнолуния. В финальной версии вероятно их не будет, т.к. в этой строке планируется показ текущего времени.
Вторая строка "3.5.2018" - текущая дата.
Ниже даты собственно визуальная часть луны.
Две нижние строки для описания фазы луны. На данный момент - фаза убывающей луны.
В дальнейшем придется по колдовать над шрифтами, т.к. слова не вмещаются в строчку, а идеале нужен шрифт 5*5 точки, но места не хватеат.
Как видно на фото, буквы "Ы", "Ю" и "Щ" используют 5 точек в ширину, а буква"Щ" к тому же и по высоте выходит на 6 точек. Пришлось пожертвовать буквами "Л" и "А".
![]() |
Почему здесь надпись фазы луны на английском? Так проще готовить эскиз. |
На этом фото 15 мая 2018г., до новолуния остается один день. На визуальной части луны видна светлая полоса. Вообще новолуние, да и другая фаза луны длится примерно четыре дня (если быть точным, то нужно 29 суток разделить на 8 фаз, да и то это будет приблизительно).
Продолжительность полной смены фаз Луны (так называемый синодический месяц) непостоянна из-за эллиптичности лунной орбиты, и варьируется от 29,25 до 29,83 земных солнечных суток. Средний синодический месяц составляет 29,5305882 суток (29 суток 12 часов 44 минуты 2,82 секунды)[1].
Луна проходит следующие фазы освещения:
- новолуние — состояние, когда Луна не видна.
- молодая луна — первое появление Луны на небе после новолуния в виде узкого серпа.
- первая четверть — состояние, когда освещена половина Луны.
- прибывающая луна
- полнолуние — состояние, когда освещена вся Луна целиком.
- убывающая луна
- последняя четверть — состояние, когда снова освещена половина Луны.
- старая луна
![]() |
В верхней части количество дней до полнолуния. |
Полностью закрашенная луна. Истинное новолуние.
За один день до полнолуния.
Так выглядит полнолуние.
Это только эскизы. На данном этапе отрисовка проходит корректно без косяков. Еще ведется работа по оптимизации кода, но эта часть проекта уже идет к завершению.
Календарь. (Закончил)
Календарь на Arduino.Электронные часы на Arduino. Календарь на один месяц.
С календарем вышла заминка. Для расположения в книжном формате, шрифт очень большой, для альбомного расположение шрифта нет. Нужно мудрить.
Шрифт вроде подобрал. Теперь нужно написать как отображать даты. Скорей всего нужно написать функцию с переменной которая будет приплюсовать в зависимости от даты в начале месяца. Типа, если месяц начинается в понедельник, то вывести семь цифр с увеличением на одну до воскресенья (седьмой день). Если во вторник, то первый день установить... Но у меня названия дня недели расположены не горизонтально, а вертикально. Так это не работает. Нужно сделать массив с координатами для каждого числа.
С календарем вышла заминка. Для расположения в книжном формате, шрифт очень большой, для альбомного расположение шрифта нет. Нужно мудрить.
Шрифт вроде подобрал. Теперь нужно написать как отображать даты. Скорей всего нужно написать функцию с переменной которая будет приплюсовать в зависимости от даты в начале месяца. Типа, если месяц начинается в понедельник, то вывести семь цифр с увеличением на одну до воскресенья (седьмой день). Если во вторник, то первый день установить... Но у меня названия дня недели расположены не горизонтально, а вертикально. Так это не работает. Нужно сделать массив с координатами для каждого числа.
Долго подбирал шрифт. Вроде есть который подошел. Буду его использовать. Вопрос с локализацией русских шрифтов решил просто. Буду закрашивать пиксели в английских буквах. Если в этом шрифте в заглавной букве А закрасить одну точку, то она превращается в букву Я, буква О будет буквой П и т.д.
Нужно решить вопрос с масштабированием шрифтов. На данный момент есть только двойное увеличение.
Рабочего варианта нет. Все находиться в стадии тестирования.
P.S. Буду тут писать заметки на полях.
Секундомер.
Есть рабочий вариант, но он еще не адаптирован и не протестирован.
Таймер.
Рабочего варианта нет. Все находиться в стадии тестирования.Будильник.
Нет необходимых знаний.P.S. Буду тут писать заметки на полях.
- Было бы удобно делать гиперссылки и произвольно объединять в блоки части кода.
Логотип при включении. (Закончил)
https://blogruoff.blogspot.com/2019/06/adafruit.html
Ищите все связанны статьи с этим проектом #PIX4884
Ищите все связанны статьи с этим проектом #PIX4884
Поддержите «Блог@off»! Столько, сколько считаете нужным, если считаете нужным.
Всё просто: вы читаете и вам либо нравиться либо не нравиться.
Я пишу, а вы меня в этом поддерживаете — так мы делаем этот мир чуточку лучше.
А в чем сложность с будильником? Там ведь сложного нет?
ОтветитьУдалитьВсе будет и будильник. Просто еще не время. На данный момент я не понимаю как его писать. Если с часами и календарем разобрался, то тут даже не знаю какие вопросы гуглить.
УдалитьЯ готов принять помощь в написании кода, но тут стоит понимать что данный проект носит чисто развлекательно-познавательный характер.
Очень интересно!
ОтветитьУдалитьЖду продолжения.
Удачи!
Спасибо! Я и сам с нетерпением жду продолжения :) Иногда даже непонятно что сложнее, написать код или работа над оформлением.
УдалитьЛунный календарь получился интересный. На сколько он точен?
ОтветитьУдалитьОн достаточно точен для бытовых нужд (посевы там или когда стричься). Конечно имеются погрешности в расчетах, т.к. берутся усредненные данные и по количеству лунных дней и по количеству дней в году (имеется в виду високосный год). Ниже пример кода для расчета лунного календаря:
Удалитьint moon_phase(){
// расчет фазы луны от 0 до 7
// имеется восемь фаз, 0 -полная луна, 4 новолуние
time_t t = now(); //DateTime now = RTC.now();
double jd = 0; // Юлианская дата
double ed = 0; //количество дней, прошедших с начала полнолуния
int b= 0;
jd = julianDate (year(), month(),day()); //(now.year(), now.month(), now.day());
//jd = julianDate(1972,1,1); // используется для отладки, новолуние
jd = int(jd - 2244116.75); // отсчет с 1 Января 1972
jd /= 29.53; // разделить на лунный цикл
b = jd;
jd -= b; // оставляет дробную часть jd
ed = jd * 29.53; // дней прошедших в этом месяце
nfm = String((int(29.53 - ed))); // количество дней до следующего полнолуния
b = jd*8 +0.5;
b = b & 7; // указывает номер кейса
return b;
}
double julianDate(int y, int m, int d){
// конвертация даты в Юлианскую дату}
int mm,yy;
double k1, k2, k3;
double j;
yy = y- int((12-m)/10);
mm = m+9;
if(mm >= 12) {
mm = mm-12;
}
k1 = 365.25 *(yy +4172);
k2 = int((30.6001 * mm) + 0.5);
k3 = int((((yy/100) + 4) * 0.75) -38);
j = k1 +k2 + d + 59;
j = j-k3; // j это Юлианская дата
return j;
}
Я тоже начал изучать программирование. Сложно и не понятно. Опыта мало. А сам как учишься?
ОтветитьУдалитьНаверно как и все самоучки. Ищу информацию в интернете, читаю книги. Очень помог человек который был знаком с основами программирования. Он помог мне понять логику.
УдалитьНаверное только практика поможет сделать человека специалистом. Поэтому пробуй и спрашивай если не знаешь. И снова пробуй и спрашивай. До тех пор, пока спрашивать не начнут у тебя.