Электронные часы на Arduino. С будильником, таймером и другими плюшками :) Начало.

Буду собирать электронные носимые часы которые будут показывать не только время, ещё там будет будильник, таймер и секундомер. Это основные функции с которых и начну. Остальное буду добавлять по ходу пьесы.
Итак, допустим я уже освоил Arduino (хотя это не так, но хочется верить) и я хочу собрать великий проект о котором заговорят все и имя моё будет вписано в историю золотыми буквами... :-)

На самом деле это заметки по ходу работы и они будут корректироваться и изменяться. А заодно и мои мысли систематизируют. Для тех кто читает, не воспринимайте данную статью как готовый проект, это "мысли в слух". Я вам гарантирую и путанные описания и ошибки, в том числе и грамматические.
Собирать буду долго, так как базовый курс программирования отличается от реальности, с электроникой не знаком. Могу паять, преимущественно что-то не меньше розетки. Часть схем и програмного кода буду брать из интернета, что-то буду узнавать у знакомых. В общем, типичный новичок.

Для проекта использую:
Arduino UNO (копия) - для отладки,
Макетная площадка
Провода
Модуль с часами реального времени RTC (убрал из проекта)
Жк дисплей от Nokia5110
Резисторы
Кнопки
Компьютер
Паяльник
Припой
И кучу свободного времени.

Меню электронных часов на arduino
Меню электронных часов

Эскиз меню часов виден на картинке.

#здесь будет картинка#

Подключаю компоненты согласно рисунку.

Заливаем скетч и любуемся. Любоваться придётся долго, пока я буду делать нормальный будильник.
Имеется три графических библиотеки, но я пока использую всего две: adafruit и u8glib. Каждая по своему хороша и плоха. Выбор будет за самой легкой по коду и размеру.
В общем я начал использовать u8glib, но в какой то момент столкнулся с проблемой масштабирования шрифтов и пришлось использовать библиотеку adafruit.
Я провел перевод кода с одной библиотеки в другую. Перевод прошел успешно. Замена команд из одной библиотеки на другую создала небольшую задержку. Главное в этой ситуации найти аналог команд.
Например в adafruit команда для рисовании линии display.drawLine(x1, y1, x2, y2, color), у u8glib  она будет такая u8g.drawLine(x1,y1,x2,y2), т.е. по сути команда практически похожа, ну и иногда координаты поменяны. 
Вся эта затея была предпринята ради особого шрифта в u8glib который был хорош, но не масштабированный. На днях я нашел решение по введению своих шрифтов, нужно попробовать и сравнить вообще библиотеки по объему занимаемой памяти. Конечно в u8glib чуть больше возможностей с использованием графических команд, но adafruit мне показалась проще в освоении. На данный момент я соберу два скетча с разными графическими библиотеками и посмотрю какая из них меньше весит.
Победила дружба :) Не значительная разница. Они по разному используют память.
Сравнение библиотек U8lib и Adafruit GFX
Скомпилировал два скетча на разных библиотеках  и вот что получил.
Библиотека 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 меньше ресурсов использует.

В самом начале я пытался разобраться со всем кодом сразу в одном скече. Со временем я пришел к выводу что лучше сделать отдельно каждый скеч на один экран, а уже позже объединить все в один.
Управление планируется при помощи двух кнопок, третья для подсветки.

Электронные часы на Arduino - главный экран
Главный экран электронных часов.


Часы. (Закончил)

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 точкам (максимальный диаметр круга на этом дисплее). Если сегодня 14 лунный день, то и линия равна 21 точке. Рассчитать центральную линию не сложно, сложнее с остальными, т.к. например седьмая линия во время полнолуния должна быть примерно 11 точек, а вторая или третья всего 5-6 точек. Мне нужно написать простую и понятную функцию. Пока буду использовать сложный метод вычисления. Он заключается в построчном расчете разницы между центральной линией и той которая короче.

Ниже образец расчета первой линии. Повторить 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();
 //то что перед точкой конвертируется в то значение которое стоит после точки

Ниже фото тестовых экранов лунного календаря. Они конечно не лучшего образца, но суть будет ясна.

Arduino убывающая луна

Верхняя строка где цифры "27 27" - обозначают количество оставшихся дней до полнолуния. В финальной версии вероятно их не будет, т.к. в этой строке планируется показ текущего времени.
Вторая строка "3.5.2018" - текущая дата.
Ниже даты собственно визуальная часть луны.
Две нижние строки для описания фазы луны. На данный момент - фаза убывающей луны.
В дальнейшем придется по колдовать над шрифтами, т.к. слова не вмещаются в строчку, а идеале нужен шрифт 5*5 точки, но места не хватеат.
Как видно на фото, буквы "Ы", "Ю" и "Щ" используют 5 точек в ширину, а буква"Щ" к тому же и по высоте выходит на 6 точек. Пришлось пожертвовать буквами "Л" и "А".

Arduino эскиз экрана лунного календаря
Почему здесь надпись фазы луны на английском? Так проще готовить эскиз.

На этом фото 15 мая 2018г., до новолуния остается один день. На визуальной части луны видна светлая полоса. Вообще новолуние, да и другая фаза луны длится примерно четыре дня (если быть точным, то нужно 29 суток разделить на 8 фаз, да и то это будет приблизительно).
Продолжительность полной смены фаз Луны (так называемый синодический месяц) непостоянна из-за эллиптичности лунной орбиты, и варьируется от 29,25 до 29,83 земных солнечных суток. Средний синодический месяц составляет 29,5305882 суток (29 суток 12 часов 44 минуты 2,82 секунды)[1].
 Луна проходит следующие фазы освещения:

  1. новолуние — состояние, когда Луна не видна.
  2. молодая луна — первое появление Луны на небе после новолуния в виде узкого серпа.
  3. первая четверть — состояние, когда освещена половина Луны.
  4. прибывающая луна
  5. полнолуние — состояние, когда освещена вся Луна целиком.
  6. убывающая луна
  7. последняя четверть — состояние, когда снова освещена половина Луны.
  8. старая луна


Arduino Новолуние
В верхней части количество дней до полнолуния.

Полностью закрашенная луна. Истинное новолуние.

Arduino эскиз Полнолуние

За один день до полнолуния.

Arduino Полнолуние

Так выглядит полнолуние.
Это только эскизы.  На данном этапе отрисовка проходит корректно без косяков. Еще ведется работа по оптимизации кода, но эта часть  проекта уже идет к завершению.

Календарь. (Закончил)

Календарь на Arduino.
Электронные часы на Arduino. Календарь на один месяц.

С календарем вышла заминка. Для расположения в книжном формате, шрифт очень большой, для альбомного расположение шрифта нет. Нужно мудрить.
Шрифт вроде подобрал. Теперь нужно написать как отображать даты. Скорей всего нужно написать функцию с переменной которая будет приплюсовать в зависимости от даты в начале месяца. Типа, если месяц начинается в понедельник, то вывести семь цифр с увеличением на одну до воскресенья (седьмой день). Если во вторник, то первый день установить... Но у меня названия дня недели расположены не горизонтально, а вертикально. Так это не работает. Нужно сделать массив с координатами для каждого числа.
Долго подбирал шрифт. Вроде есть который подошел. Буду его использовать. Вопрос с локализацией русских шрифтов решил просто. Буду закрашивать пиксели в английских буквах. Если в этом шрифте в заглавной букве А закрасить одну точку, то она превращается в букву Я, буква О будет буквой П и т.д.
Нужно решить вопрос с масштабированием шрифтов. На данный момент есть только двойное увеличение.

Секундомер.

Есть рабочий вариант, но он еще не адаптирован и не протестирован.


Таймер.

Рабочего варианта нет. Все находиться в стадии тестирования.

Будильник.

Нет необходимых знаний.



P.S. Буду тут писать заметки на полях.

  • Было бы удобно делать гиперссылки и произвольно объединять в блоки части кода.

Логотип при включении. (Закончил)

https://blogruoff.blogspot.com/2019/06/adafruit.html

Ищите все связанны статьи с этим проектом #PIX4884

Поддержите «Блог@off»! Столько, сколько считаете нужным, если считаете нужным.

Комментарии

  1. А в чем сложность с будильником? Там ведь сложного нет?

    ОтветитьУдалить
    Ответы
    1. Все будет и будильник. Просто еще не время. На данный момент я не понимаю как его писать. Если с часами и календарем разобрался, то тут даже не знаю какие вопросы гуглить.
      Я готов принять помощь в написании кода, но тут стоит понимать что данный проект носит чисто развлекательно-познавательный характер.

      Удалить
  2. Очень интересно!
    Жду продолжения.
    Удачи!

    ОтветитьУдалить
    Ответы
    1. Спасибо! Я и сам с нетерпением жду продолжения :) Иногда даже непонятно что сложнее, написать код или работа над оформлением.

      Удалить
  3. Лунный календарь получился интересный. На сколько он точен?

    ОтветитьУдалить
    Ответы
    1. Он достаточно точен для бытовых нужд (посевы там или когда стричься). Конечно имеются погрешности в расчетах, т.к. берутся усредненные данные и по количеству лунных дней и по количеству дней в году (имеется в виду високосный год). Ниже пример кода для расчета лунного календаря:

      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;
      }

      Удалить
  4. Я тоже начал изучать программирование. Сложно и не понятно. Опыта мало. А сам как учишься?

    ОтветитьУдалить
    Ответы
    1. Наверно как и все самоучки. Ищу информацию в интернете, читаю книги. Очень помог человек который был знаком с основами программирования. Он помог мне понять логику.
      Наверное только практика поможет сделать человека специалистом. Поэтому пробуй и спрашивай если не знаешь. И снова пробуй и спрашивай. До тех пор, пока спрашивать не начнут у тебя.

      Удалить

Отправить комментарий

Популярные сообщения из этого блога

Ремонт подсветки в электронных часах.

На рюкзаке сломалась пряжка. Вариант ремонта.

Восстановление кнопки шуруповёрта длиною в год.