Скидки, ограниченное предложение! Полный список акционных товаров

Урок 37. Часы с «вредным» будильником

Необходимые детали
Видео уроки

В этом уроке мы создадим часы с «вредным» будильником, выключить этот будильник простым нажатием не получится!

Заводя будильник мы надеемся проснуться вовремя, но как только он начинает звонить, мы выключаем его и думаем что встанем через 5 минут ... ))).

Наш будильник тоже можно выключить, но не нажатием на кнопку, а установкой двух значений индикатора в ноль. Значения меняются при повороте Trema энкодера. Если поворачивать Trema энкодер влево, то меняется левое значение, а если вправо, то меняется правое значение. Можно конечно усложнить схему, но тогда мы рискуем что наш будильник станет не только «вредным», но и летающим ...

Управление часами будет осуществляться с помощью Trema энкодера. Точность хода обеспечит Trema-модуль реального времени. Текущее значение времени и будильника будет отображаться на Trema четырёхразрядном LED индикаторе. А о срабатывании будильника будут сигнализировать Trema зуммер и Trema вибромодуль.

Нам понадобится:

Для реализации проекта нам необходимо установить библиотеки:

  • iarduino_4LED для работы с четырёхразрядными LED индикаторами.
  • iarduino_RTC для работы с модулями реального времени.
  • iarduino_Encoder_tmr для работы с энкодерами через аппаратный таймер.

Видео:


Схема подключения:

Trema модуль RTC подключается к аппаратной шине I2C, остальные модули подключаются к любым (цифровым или аналоговым) выводам Arduino, номера указываются в скетче.

В примере модули подключаются в следующем порядке: Trema четырехразрядный LED индикатор к выводам D3 и D4, Trema вибромодуль к выводу D7, Trema зуммер к выводу D9, Кнопка Trema энкодера к выводу D11, Сигнальные выводы поворота Trema энкодера к выводам D12 и D13. Аналоговый вывод A0 должен оставаться свободным, так как он используется для выбора начальной позиции последовательности чисел для функции random.

Часы с будильником на Arduino

Алгоритм работы:

Режим просмотра времени: При включении питания на индикаторе отображается текущее время (часы и минуты) и мигает двоеточие.

Режим просмотра будильника: Для того чтобы посмотреть время будильника, достаточно повернуть знкодер на один такт в любую сторону. Экран будет менее ярким, а двоеточие мигать не будет. Повторный поворот энкодера в любую сторону приведёт к возврату в режим прсмотра времени

Вкл/выкл будильника: Для того чтобы включить/выключить будильник, достаточно однократно нажать на энкодер, в режиме просмотра времени или будильника. Если будильник включен, то на индикаторе будет гореть последняя (крайняя правая) точка.

Режим установки текущего времени: Для установки текущего времени нужно удерживать нажатым энкодер пока на индикаторе отображается текущее время. После входа в данный режим, на индикаторе будут мигать часы, значение которых можно менять поворотом энкодера. Если однократно нажать на энкодер, то будут мигать минуты, значение которых можно менять поворотом энкодера. Если удерживать энкодер нажатым то выбранное время сохранится, секунды сбросятся в ноль, а часы выйдут из режима установки текущего времени.

Режим установки времени будильника: Для установки времени будильника нужно удерживать нажатым энкодер пока на индикаторе отображается время будильника. После входа в данный режим, на индикаторе будут мигать часы, значение которых можно менять поворотом энкодера. Если однократно нажать на энкодер, то будут мигать минуты, значение которых можно менять поворотом энкодера. Если удерживать энкодер нажатым то время будильника сохранится, будильник включится, а часы выйдут из режима установки времени будильника.

Срабатывание будильника: Если будильник включён (светится последняя точка индикатора) а часы и минуты текущего времени совпали с часами и минутами будильника, то часы начнут сигнализировать повторяя три звуковых и один вибро сигнал в секунду. А на дисплее будут отображены две случайные цифры со значением от 1 до 9 включительно.

Выключение сигнала будильника: Для выключения будильника нужно поворачивать энкодер влево пока левая цифра не станет навна нулю и поворачивать энкодер вправо пока правая цифра не станет равна нулю. Как только обе цифры станут равны нулю сигнал будильника отключится а сам будильник выключится и на дисплее отобразится текущее время.

Код программы:

//                   Подключаем библиотеки:
#include                                          //  Подключаем библиотеку iarduino_4LED        для работы с четырёхразрядными LED индикаторами
#include                                           //  Подключаем библиотеку iarduino_RTC         для работы с модулями реального времени
#include                                   //  Подключаем библиотеку iarduino_Encoder_tmr для работы с энкодерами через аппаратный таймер
//                   Определяем номера выводов Arduino:
const uint8_t        pinRandom = 0;                                //  Определяем константу с указанием № свободного аналогового вывода Arduino сигнал с которого будет использован для корректировки работы функции random
const uint8_t        pinLedCLK = 3;                                //  Определяем константу с указанием № вывода Arduino, к которому подключён вывод CLK индикатора
const uint8_t        pinLedDIO = 4;                                //  Определяем константу с указанием № вывода Arduino, к которому подключён вывод DIO индикатора
const uint8_t        pinVibro  = 7;                                //  Определяем константу с указанием № вывода Arduino, к которому подключён вибромодуль
const uint8_t        pinBuzzer = 9;                                //  Определяем константу с указанием № вывода Arduino, к которому подключён зуммер
const uint8_t        pinEncBTN = 11;                               //  Определяем константу с указанием № вывода Arduino, к которому подключён вывод кнопки энкодера
const uint8_t        pinEncA   = 12;                               //  Определяем константу с указанием № вывода Arduino, к которому подключён вывод A энкодера
const uint8_t        pinEncB   = 13;                               //  Определяем константу с указанием № вывода Arduino, к которому подключён вывод B энкодера
//                   Создаём объекты библиотек:
iarduino_4LED        display     (pinLedCLK, pinLedDIO);           //  Создаём    объект display указывая (№ CLK, № DIO) четырёхразрядного LED индикатора
iarduino_RTC         time        (RTC_DS1307);                     //  Создаём    объект time    указывая (НАЗВАНИЕ) чипа на базе которого создан модуль часов
iarduino_Encoder_tmr encoder     (pinEncA, pinEncB);               //  Создаём    объект encoder указывая (№ A, № B) энкодера
//                   Создаём переменные:
uint8_t              valMode=0;                                    //  Определяем переменную для хранения режима работы
bool                 flgMode=0;                                    //  Определяем флаг       указывающий на смену режима работы
int8_t               valEnc=0;                                     //  Определяем переменную для хранения состояния энкодера (будет принимать значения: encLEFT или 0 или encRIGHT)
bool                 valEncBTN;                                    //  Объявляем  переменную для хранения состояния кнопки энкодера (нажата/отпущена)
bool                 flgEncBTN_s;                                  //  Объявляем  флаг       указывающий на кратковременное нажатие на кнопку энкодера (1/0)
bool                 flgEncBTN_l;                                  //  Объявляем  флаг       указывающий на удержание кнопки энкодера (1/0)
int8_t               tmrEncBTN=0;                                  //  Определяем переменную для хранения времени удержания кнопки энкодера в децисекундах (десятых долях секунды)
int8_t               tmrAlarmH, tmrSetH, valAlarmL;                //  Объявляем  переменные для хранения часов будильника (tmrAlarmH), часов устанавливаемого времени (tmrSetH) и левого  значения для выключения будильника
int8_t               tmrAlarmM, tmrSetM, valAlarmR;                //  Объявляем  переменные для хранения минут будильника (tmrAlarmM), минут устанавливаемого времени (tmrSetM) и правого значения для выключения будильника
bool                 flgAlarm=1;                                   //  Определяем флаг       указывающий на то, что будильник включён (не сработал а включён)
bool                 flgBuzzer=0;                                  //  Определяем флаг       указывающий на то, что будильник сработал
uint32_t             valMillis;                                    //  Объявляем  переменную для хранения количества миллисекунд прошедших с момента старта скетча (от 0 до 999)
//                   Объявляем функции:
void                 funcAnimationSave(void);                      //  Объявляем  функцию    анимации (создаёт эффект сохранения)
void                 funcMyTone(uint32_t);                         //  Объявляем  функцию    вывода звукового сигнала (в качестве параметра указывается длительность сигнала)
void setup(){
//  Конфигурируем выводы и устанавливаем логические уровни:
    pinMode          (pinVibro,  OUTPUT);                          //  Конфигурируем вывод pinVibro  как выход
    digitalWrite     (pinVibro,  LOW);                             //  Устанавливаем уровень логического «0» на выводе pinVibro
    pinMode          (pinBuzzer, OUTPUT);                          //  Конфигурируем вывод PinBuzzer как выход
    digitalWrite     (pinBuzzer, LOW);                             //  Устанавливаем уровень логического «0» на выводе PinBuzzer
    pinMode          (pinEncBTN, INPUT);                           //  Конфигурируем вывод pinEncBTN как вход
//  Подготавливаем работу функции random:
    randomSeed(analogRead(0));                                     //  Выбираем начальную позицию в последовательности чисел для функции random
//  Инициируем работу с модулями:
    display.begin();                                               //  Инициируем работу с четырёхразрядным LED индикатором
    time.begin();                                                  //  Инициируем работу с модулем часов реального времени
    encoder.begin();                                               //  Инициируем работу с энкодером
//  Определяем время будильника по умолчанию:
    tmrAlarmH   = 6; /* 6 часов */                                 //  Определяем время срабатывания будильника на 06 часов
    tmrAlarmM   = 0; /* 0 минут */                                 //  Определяем время срабатывания будильника на 00 минут
}
void loop(){
    valMillis   =                 millis()%1000;                   //  Получаем количество миллисекунд прошедших с момента старта скетча (в виде числа от 0 до 999)
//  Определяем состояние энкодера:
    valEnc      =                 encoder.read();                  //  Читаем состояние энкодера в переменную valEnc
    valEncBTN   =                 digitalRead(pinEncBTN);          //  Читаем состояние кнопки энкодера с вывода pinEncBTN в переменную valEncBTN
    flgEncBTN_s =                 0;                               //  Сбрасываем флаг кратковременного нажатия кнопки энкодера
    flgEncBTN_l =                 0;                               //  Сбрасываем флаг удержания кнопки энкодера
    if( valEncBTN)                {tmrEncBTN++; delay(100);}       //  Если кнопка энкодера нажата, то увеличиваем время её удержания (tmrEncBTN) на 1 за каждую децисекунду (то еть через каждые 100мс)
    if(!valEncBTN&&tmrEncBTN&&!flgEncBTN_l){flgEncBTN_s=1;}        //  Если кнопка энкодера отпущена, но была ранее нажата и не удерживалась, то устанавливаем флаг кратковременного нажатия flgEncBTN_s
    if(!valEncBTN)                {tmrEncBTN=0;}                   //  Если кнопка энкодера отпущена, то сбрасываем время удержания кнопки tmrEncBTN
    if( tmrEncBTN>=20)            {flgEncBTN_l=1; tmrEncBTN=20;}   //  Если время удержания кнопки превышает 20 децисекунд, то устанавливаем флаг удержания кнопки энкодера flgEncBTN_l и не даём дальше увеличиваться времени tmrEncBTN
    if( flgMode)                  {flgEncBTN_l=0; flgEncBTN_s=0;}  //  Если установлен флаг смены режима работы, то не даём установиться флагам нажатия или удержания кнопки энкодера, иначе нажатие на кнопку в одном режиме может отразиться на работе другого
    if(!valEncBTN)                {flgMode=0;}                     //  Если кнопка энкодера отпущена, то сбрасываем флаг смены режима flgMode
//  Выполняем участок кода соответствующий режиму valMode:
    switch(valMode){                                                                                              //  В зависимости от значения переменной valMode, будет выполняться только один участок оператора switch
        case 0: //                Режим просмотра времени
            if(valMillis<500)     {display.print(time.gettime("H:i"));}                                           //  Выводим текущее время с двоеточием
            else                  {display.print(time.gettime("Hi" ));}                                           //  Выводим текущее время без двоеточия (эта и предыдущая строка создают эфект мигания двоеточий)
            if(flgEncBTN_l)       {valMode=2; flgMode=1; tmrSetH=time.Hours; tmrSetM=time.minutes;}               //  Если установлен флаг удержания кнопки энкодера, то меняем режим valMode на 2 (установка часов текущего времени) и сохраняем текущее время в переменные tmrSetH и tmrSetM
            if(flgEncBTN_s)       {flgAlarm= !flgAlarm;}                                                          //  Если установлен флаг кратковременного нажатия на кнопку энкодера, то меняем флаг flgAlarm (будильник вкл/выкл)
            if(valEnc)            {valMode=1; flgMode=1; display.light(1);}                                       //  Если энкодер зафиксировал поворот, то меняем режим valMode на 1 (просмотр будильника) и устанавливаем слабую яркость свечения индикатора
        break;
        case 1: //                Режим просмотра будильника
                                   display.print(tmrAlarmH, tmrAlarmM, TIME);                                     //  Выводим значения переменных будильника tmrAlarmH и tmrAlarmM как время (с ведущими нулями и двоеточием)
            if(flgEncBTN_l)       {valMode=3; flgMode=1;}                                                         //  Если установлен флаг удержания кнопки энкодера, то меняем режим valMode на 3 (установка часов будильника)
            if(flgEncBTN_s)       {flgAlarm= !flgAlarm;}                                                          //  Если установлен флаг кратковременного нажатия на кнопку энкодера, то меняем флаг flgAlarm (будильник вкл/выкл)
            if(valEnc)            {valMode=0; flgMode=1; display.light(5);}                                       //  Если энкодер зафиксировал поворот, то меняем режим valMode на 0 (просмотр времени) и устанавливаем максимальную яркость свечения индикатора
        break;
        case 2: //                Режим установки часов текущего времени
            if(valMillis<500)     {display.print(tmrSetH, tmrSetM, TIME);}                                        //  Выводим устанавливаемое время (с ведущими нулями и двоеточием)
            else                  {display.print(tmrSetM, POS3, RIGHT, LEN2);}                                    //  Выводим только минуты (tmrSetM) начиная с 3 позиции индикатора (POS3), с направлением сдвига в право (RIGHT) от позиции, указывая что требуется вывести 2 цифры (LEN2) числа
            if(flgEncBTN_l)       {valMode=0; flgMode=1; time.settime(0, tmrSetM, tmrSetH); funcAnimationSave();} //  Если установлен флаг удержания кнопки энкодера, то меняем режим valMode на 0 (просмотр текущего времени), сохраняем время из переменных tmrSetH и tmrSetM в модуль реального времени и выполняем анимацию функцией funcAnimationSave()
            if(flgEncBTN_s)       {valMode=4; flgMode=1;}                                                         //  Если установлен флаг кратковременного нажатия на кнопку энкодера, то меняем режим valMode на 4 (установка минут текущего времени)
            if(valEnc==encLEFT)   {tmrSetH--; if(tmrSetH<0 ){tmrSetH=23;}}                                        //  Если энкодер зафиксировал поворот налево,  то уменьшаем   значение переменной tmrSetH хранящей устанавливаемый час текущего времени
            if(valEnc==encRIGHT)  {tmrSetH++; if(tmrSetH>23){tmrSetH=0; }}                                        //  Если энкодер зафиксировал поворот направо, то увеличиваем значение переменной tmrSetH хранящей устанавливаемый час текущего времени
        break;
        case 3: //                Режим установки часов будильника
            if(valMillis<500)     {display.print(tmrAlarmH, tmrAlarmM, TIME);}                                    //  Выводим устанавливаемое время (с ведущими нулями и двоеточием)
            else                  {display.print(tmrAlarmM, POS3, RIGHT, LEN2);}                                  //  Выводим только минуты (tmrAlarmM) начиная с 3 позиции индикатора (POS3), с направлением сдвига в право (RIGHT) от позиции, указывая что требуется вывести 2 цифры (LEN2) числа
            if(flgEncBTN_l)       {valMode=0; flgMode=1; display.light(5); flgAlarm=1;      funcAnimationSave();} //  Если установлен флаг удержания кнопки энкодера, то меняем режим valMode на 0 (просмотр текущего времени), включаем будильник (flgAlarm), устанавливаем максимальную яркость свечения индикатора и выполняем анимацию функцией funcAnimationSave()
            if(flgEncBTN_s)       {valMode=5; flgMode=1;}                                                         //  Если установлен флаг кратковременного нажатия на кнопку энкодера, то меняем режим valMode на 5 (установка минут будильника)
            if(valEnc==encLEFT)   {tmrAlarmH--; if(tmrAlarmH<0 ){tmrAlarmH=23;}}                                  //  Если энкодер зафиксировал поворот налево,  то уменьшаем   значение переменной tmrAlarmH хранящей устанавливаемый час будильника
            if(valEnc==encRIGHT)  {tmrAlarmH++; if(tmrAlarmH>23){tmrAlarmH=0; }}                                  //  Если энкодер зафиксировал поворот направо, то увеличиваем значение переменной tmrAlarmH хранящей устанавливаемый час будильника
        break;
        case 4: //                Режим установки минут текущего времени
            if(valMillis<500)     {display.print(tmrSetH, tmrSetM, TIME);}                                        //  Выводим устанавливаемое время (с ведущими нулями и двоеточием)
            else                  {display.print(tmrSetH, POS1, RIGHT, LEN2);}                                    //  Выводим только часы (tmrSetH) начиная с 1 позиции индикатора (POS1), с направлением сдвига в право (RIGHT) от позиции, указывая что требуется вывести 2 цифры (LEN2) числа
            if(flgEncBTN_l)       {valMode=0; flgMode=1; time.settime(0, tmrSetM, tmrSetH); funcAnimationSave();} //  Если установлен флаг удержания кнопки энкодера, то меняем режим valMode на 0 (просмотр текущего времени), сохраняем время из переменных tmrSetH и tmrSetM в модуль реального времени, и выполняем анимацию функцией funcAnimationSave()
            if(flgEncBTN_s)       {valMode=2; flgMode=1;}                                                         //  Если установлен флаг кратковременного нажатия на кнопку энкодера, то меняем режим valMode на 2 (установка часов текущего времени)
            if(valEnc==encLEFT)   {tmrSetM--; if(tmrSetM<0 ){tmrSetM=59;}}                                        //  Если энкодер зафиксировал поворот налево,  то уменьшаем   значение переменной tmrSetM хранящей устанавливаемые минуты текущего времени
            if(valEnc==encRIGHT)  {tmrSetM++; if(tmrSetM>59){tmrSetM=0; }}                                        //  Если энкодер зафиксировал поворот направо, то увеличиваем значение переменной tmrSetM хранящей устанавливаемые минуты текущего времени
        break;
        case 5: //                Режим установки минут будильника
            if(valMillis<500)     {display.print(tmrAlarmH, tmrAlarmM, TIME);}                                    //  Выводим устанавливаемое время (с ведущими нулями и двоеточием)
            else                  {display.print(tmrAlarmH, POS1, RIGHT, LEN2);}                                  //  Выводим только часы (tmrAlarmH) начиная 1 3 позиции индикатора (POS1), с направлением сдвига в право (RIGHT) от позиции, указывая что требуется вывести 2 цифры (LEN2) числа
            if(flgEncBTN_l)       {valMode=0; flgMode=1; display.light(5); flgAlarm=1;      funcAnimationSave();} //  Если установлен флаг удержания кнопки энкодера, то меняем режим valMode на 0 (просмотр текущего времени), включаем будильник (flgAlarm), устанавливаем максимальную яркость свечения индикатора и выполняем анимацию функцией funcAnimationSave()
            if(flgEncBTN_s)       {valMode=3; flgMode=1;}                                                         //  Если установлен флаг кратковременного нажатия на кнопку энкодера, то меняем режим valMode на 3 (установка минут текущего времени)
            if(valEnc==encLEFT)   {tmrAlarmM--; if(tmrAlarmM<0 ){tmrAlarmM=59;}}                                  //  Если энкодер зафиксировал поворот налево,  то уменьшаем   значение переменной tmrAlarmM хранящей устанавливаемые минуты будильника
            if(valEnc==encRIGHT)  {tmrAlarmM++; if(tmrAlarmM>59){tmrAlarmM=0; }}                                  //  Если энкодер зафиксировал поворот направо, то увеличиваем значение переменной tmrAlarmM хранящей устанавливаемые минуты будильника
        break;
        case 6: //                Режим сработавшего будильника
                                  display.print((String) valAlarmL + "  " + valAlarmR);                           //  Выводим два числа (valAlarmL и valAlarmR) по бокам дисплея
            if(valEnc==encLEFT)   {valAlarmL--; if(valAlarmL<0){valAlarmL=9;}}                                    //  Если энкодер зафиксировал поворот налево,  то уменьшаем   значение переменной valAlarmL
            if(valEnc==encRIGHT)  {valAlarmR++; if(valAlarmR>9){valAlarmR=0;}}                                    //  Если энкодер зафиксировал поворот направо, то увеличиваем значение переменной valAlarmR
        break;
    }
//  Если будильник включён:
    if(flgAlarm){
        display.point(4, true);                                                                                   //  Включаем последнюю (четвертую) точку на индикаторе
        time.gettime();                                                                                           //  Читаем время из модуля реального времени
        if(tmrAlarmH==time.Hours && tmrAlarmM==time.minutes && valMode<2){                                        //  Если время из модуля реального времени совпало с временем будильника и часы находятся в режиме просмотра времени или будильника, то ...
            flgAlarm=0; flgBuzzer=1; valMode=6; valAlarmL=random(1,10); valAlarmR=random(1,10); display.light(5); //  Сбрасываем флаг flgAlarm (будильник включён), устанавливаем флаг flgBuzzer (будильник сработал), меняем режим valMode на 6 (будильник сработал), выбираем псевдослучайные числа для переменных valAlarmL и valAlarmR (от 1 до 9 включительно), устанавливаем максимальную яркость дисплея
        }
    }
//  Если будильник сработал:
    if(flgBuzzer){
        if(  0millis()){                                                                                            //  Если время с момента старта скетча не достигло времени окончания звука, то
        digitalWrite(pinBuzzer, HIGH); delayMicroseconds(250);                                                    //  Устанавливаем импульс на время 250 мкс 
        digitalWrite(pinBuzzer, LOW ); delayMicroseconds(250);                                                    //  Устанавливаем паузу   на время 250 мкс 
    }                                                                                                             //  Получается меандр с периодом   500 мкс (1/500мкс = 2кГц)
}

Так как стандартная функция tone() использует второй аппаратный таймер, который уже используется библиотекой iarduino_Encoder_tmr для работы с энкодерами, то для вывода звука мы создали свою функцию funcMyTone().

Ссылки:

Обсуждение

Присоединяйся

Другие уроки

На главную