Как стать автором
Обновить
0

Проснись с комфортом — делаем светобудильник своими руками

Время на прочтение 8 мин
Количество просмотров 18K
Вот и настала осень…
Впереди неумолимо маячит зима – утром темно, вечером темно.
Просыпаться по будильнику в полной темноте равносильно нырянию под лед. Стресс с утра – гарантированно унылый день.

Решение-то есть, светобудильник называется, но цена этой штуки от 5000 руб. Колется, однако. К тому же, алгоритм работы жестко запрограммирован, а ведь совершенно очевидно, что, по результатам просыпания, захочется что-то подкорректировать или расширить функционал.
А что, если на основе подручных средств соорудить что-то в этом духе? Хорошая задачка для DIY!



Для решения были использованы следующие материалы:

— прототип отладочной платы набора для изучения программирования Ардуино (входит в будущий набор NR05), рассчитанная на установку Arduino Nano;
— часы реального времени MP1095;
— светодиоды разные;
— пьезопищалка со встроенным генератором;
— белая полупрозрачная штука, когда-то напечатанная при отладке 3D-принтера (можно приспособить еще что-нибудь полупрозрачное в качестве рассеивателя, а у кого есть 3D принтер, то напечатать что нибудь свое);
— желание все это соединить и запрограммировать подходящий алгоритм работы.

Белую штуку захотелось взгромоздить на какой-нибудь постамент, чтобы смонтировать туда светодиод и подключить его кабелем к плате. Недолго думая, рисуем в SketchUp и печатаем на 3D принтере оранжевую подставочку. При сильном желании можно напечатать корпус для платы-прототипа, но прототип и так немыслимо красив!
К тому же, его можно использовать и под разработку других проектов, а будильничек, по результатам эксплуатации опытного образца, можно оформить в более компактную конструкцию на основе той же платы Arduino Nano.

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









Тем же клеем приклеили разъем.





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



Потребляют в максимуме порядка 100 мА, напрямую к порту Ардуино не подсоединишь, но на отладочной плате имеется усилитель тока на транзисторе, обеспечивающий 200 мА.



Также на плате есть пять кнопок для всяких экспериментов, их задействуем для управления режимами и настройки будильника.

В целом, отладочная плата интересна наличием маркированных разъемов-штырей для подключения различных датчиков и исполнительных устройств, обеспечивая хороший функционал и дружелюбный интерфейс – можно, особо не задумываясь (разъемы подписаны) подключить датчики температуры, давления, сервомашинки, реле, устройства с интерфейсом I2C и т.п. Для подключение нужны только провода с разъемами-розетками.



Яркость будем регулировать ШИМом с девятого пина Ардуино, разведенного на плате к усилителю. Пищалку подсоединим к разъему SOUND, на DAT плюсом, на GND минусом. Часы реального времени – к разъему I2C_5V, поскольку 5В им и надо для питания. На плате уже установлен двухстрочный LCD индикатор, весьма контрастный, с приятными белыми знаками на синем фоне.

Собственно, все железо на месте, теперь дело за софтом.

Алгоритм был задуман так:

— при совпадении текущего времени с временем, на которое установлен будильник, яркость светильника должна медленно повышаться по полной;
— при этом несколько раз по нарастающей должны подаваться звуковые сигналы, не дающие wakeUp-объекту начихать на лампочку и уснуть при свете;
— надо дать возможность выключить в конце-концов надоедливый звук, а также включить полный свет вручную, или выключить его, уже будучи в полном сознании.
— должна быть возможность ставить время на часах и будильнике.
Получившийся скетч приведен в конце материала и снабжен подробными комментариями. Его несложно модифицировать под ваши желания и предпочтения.

Всем приятного пробуждения!



Скетч программы светобудильника
// Подключаем библиотеки вывода кириллицы на индикатор
#include <LiquidCrystalRus.h>
#include <LiquidCrystalExt.h>
#include <LineDriver.h>
// Подключаем библиотеку последовательной шины I2C
#include <Wire.h>
// Подключаем библиотеку часов реального времени RTC
#include «RTClib.h»
// Подключаем библиотеку для использование встроенного таймера 2
// для управления временными интервалами сигнала
#include <MsTimer2.h>

//-----------------------------------------------------------------------
#define NUM_KEYS 5
// Для каждой кнопки заносим калибровочные значения (выведены экспериментально)
int adcKeyVal[NUM_KEYS] = {30, 150, 360, 535, 760};
//-----------------------------------------------------------------------

int led13 = 13; // встроенный в Arduino Nano светодиод
int alarmPin = 3; // пин для управления сигналом будильника
int led = 9; // пин вывода ШИМ на светодиодную лампу
int brightness = 0; // начальная яркость
int riseAmount = 1; // шаг увеличения яркости 0-255
int timeAmount = 500; // шаг времени, мс
int beepCount = 0; // служебные переменные
int numberOfBeeps = 0;
unsigned long time_old;
unsigned long time_curr;
int alarm = 0; // флаг срабатывания будильника
int Step; // служебные переменные
unsigned long tSetOld;
unsigned long tTickOld;

// Создаем программный объект дисплей lcd, объясняя программе куда подключены линии RS,EN,DB4,DB5,DB6,DB7
LiquidCrystalRus lcd(A1, A2, A3, 2, 4, 7);

// Создаем программный объект RTC
RTC_DS1307 RTC;

int Year; // переменные для хранения данных с RTC
int Month;
int Day;
int Hour;
int Minute;
int Second;
int alarmHour = 0;
int alarmMinute = 0;
int setAlarm = 0; // будильник вкл./выкл.
int SetMode = 0; // режим установки часов/будильника вкл./выкл.
int AlarmOn; // сигнал вкл./выкл.

void setup() {
pinMode(led13, OUTPUT);
pinMode(3, OUTPUT);
lcd.begin(16, 2);
Wire.begin(); // инициализируем 1Wire (необходио для RTC)
RTC.begin(); // инициализируем RTC
RTC.writenvram(2, 0); // секунды сигнала устанавливаем в 00 (ячейка 2 постоянной памяти RTC)
tTickOld = millis(); // запомнаем число мс с момента запуска микроконтроллера для отсчета интервалов времени
// если нажата кнопка 5, установить текущее время из компьюьера на момент компиляции программы
// и сбросить будильник в 0
if (get_key() == 5) {
RTC.adjust(DateTime(__DATE__, __TIME__));
for (int i = 0; i < 5; i++) RTC.writenvram(i, 0);
}

}

void loop() {

AlarmOn = RTC.readnvram(3); // читаем 3-ю ячейку памяти RAM RTC: будильник включен (1) или выключен (0)
if (get_key() == 2) { // если нажата кнопка 2
SetMode++; // вход в режим установки часов
delay(500);
tSetOld = millis();
}

while (SetMode) { // установка показаний часов и будильника
if (get_key() == 2) {
SetMode++;
delay(200);
tSetOld = millis();
}
if (SetMode > 4) { // выход из режима установки
SetMode = 0;
lcd.noBlink();
lcd.noCursor();
}
switch (SetMode) { // установка мигающего курсора
case 1:
lcd.setCursor(12, 1); lcd.blink();
break;
case 2:
lcd.setCursor(9, 1); lcd.blink();
break;
case 3:
lcd.setCursor(12, 0); lcd.blink();
break;
case 4:
lcd.setCursor(9, 0); lcd.blink();
break;
}

if (get_key() == 1 or get_key() == 3) { // если нажата кнопка 1 или 3
tSetOld = millis(); // обнуляем счетчик нахождения в режиме установки
if (get_key() == 3) Step = 1; // если кнопка 3 — увеличиваем
if (get_key() == 1) Step = -1; // если кнопка 1 — уменьшаем
switch (SetMode) {
case 1:
SetMinuteAlarm(Step); // минуты будильника
break;
case 2:
SetHrAlarm(Step); // часы будильника
break;
case 3:
SetMinute(Step); // минуты текущего времени
break;
case 4:
SetHr(Step); // часы текущего времени
break;

}
}

if ((millis() — tTickOld) > 1000) {
displayTime(); // раз в секунду отображаем время на индикаторе в режиме установки
tTickOld = millis();
}
if ((millis() — tSetOld) > 10000) {
SetMode = 0; // автовыход из режима установки через 10 сек.
lcd.noBlink();
}
} // end SetMode

if (get_key() == 4) { // включаем/выключаем будильник
int alarm_ram = RTC.readnvram(3);
RTC.writenvram(3, !alarm_ram);
delay(50);
AlarmOn = alarm_ram;
delay(500);
}

if (get_key() == 5) { // выключение света вручную
alarm = 0;
digitalWrite(led13, LOW);
lightDown();
}

if (get_key() == 5 && brightness >= 0) { // включение света вручную
lightUp();
}

if (get_key() == 1 && !SetMode) { // выключение звука сработавшего будильника
//alarm = 0;
MsTimer2::stop();
digitalWrite(alarmPin, LOW);
}

if ((millis() — tTickOld) > 1000) {
displayTime(); // раз в секунду отображаем время на индикаторе
tTickOld = millis();
}

if (alarm == 1) // если будильник сработал
{
digitalWrite(led13, HIGH);
time_curr = millis();
if ((time_curr — time_old) > timeAmount) {
if (brightness < 255) {
brightness = brightness + riseAmount;
time_old = time_curr;
if (brightness > 255) brightness = 255;
analogWrite(led, brightness); // медленно повышаем яркость с помощью ШИМ
switch (brightness) { // пищим пьезодинамиком по мере повышения яркости
case 60:
numberOfBeeps = 4;
alarmRun();
break;
case 120:
numberOfBeeps = 4;
alarmRun();
break;
case 180:
numberOfBeeps = 8;
alarmRun();
break;
case 220:
numberOfBeeps = 14;
alarmRun();
break;
}
if (brightness >= 255) {
numberOfBeeps = 32000; // пищим непрерывно (32000 раз)
alarmRun();
}
}
else digitalWrite(led13, LOW);
}
}

} // end loop

//----------------------------------------------
int get_key() // функция считывания номера нажатой кнопки
{
int input = analogRead(A6);
int k;
for (k = 0; k < NUM_KEYS; k++)
if (input < adcKeyVal[k])
return k + 1;
return 0;
}

void SetMinuteAlarm(int Step) { // установка минут будильника
alarmMinute = RTC.readnvram(1);
alarmMinute += Step;
if (alarmMinute > 59) alarmMinute = 0;
if (alarmMinute < 0) alarmMinute = 59;
RTC.writenvram(1, alarmMinute);
delay(300);
}

void SetHrAlarm(int Step) { // установка часов будильника
alarmHour = RTC.readnvram(0);
alarmHour += Step;
if (alarmHour > 23) alarmHour = 0;
if (alarmHour < 0) alarmHour = 23;
RTC.writenvram(0, alarmHour);
delay(300);
}

void SetMinute(int Step) { // установка минут времени
DateTime now = RTC.now();
Year = now.year();
Month = now.month();
Day = now.day();
Hour = now.hour();
Minute = now.minute();
Second = now.second();
Minute += Step;
if (Minute > 59) Minute = 0;
if (Minute < 0) Minute = 59;
RTC.adjust(DateTime(Year, Month, Day, Hour, Minute, Second));
delay(300);
}

void SetHr(int Step) { // установка часов часов
DateTime now = RTC.now();
Year = now.year();
Month = now.month();
Day = now.day();
Hour = now.hour();
Minute = now.minute();
Second = now.second();
Hour += Step;
if (Hour > 23) Hour = 0;
if (Hour < 0) Hour = 23;
RTC.adjust(DateTime(Year, Month, Day, Hour, Minute, Second));
delay(300);
}

void displayTime() { // вывод на индикатор
DateTime now = RTC.now();
Year = now.year() — 2000;
Month = now.month();
Day = now.day();
Hour = now.hour();
Minute = now.minute();
Second = now.second();

lcd.setCursor(0, 0);
lcd.print(«Время: „);
if (Hour < 10) lcd.print(' ');
lcd.print(Hour);
lcd.print(':');
if (Minute < 10) lcd.print('0');
lcd.print(Minute);
lcd.print(':');
if (Second < 10) lcd.print('0');
lcd.print(Second);

int h = RTC.readnvram(0);
int m = RTC.readnvram(1);
int s = RTC.readnvram(2);

if (AlarmOn) {
lcd.setCursor(0, 1);
lcd.print(“Сигнал: „);
if (h < 10) lcd.print(' ');
lcd.print(h);
lcd.print(':');
if (m < 10) lcd.print('0');
lcd.print(m);
lcd.print(':');
if (s < 10) lcd.print('0');
lcd.print(s);
}
else {
lcd.setCursor(0, 1);
lcd.print(“Сигнал: --:--:--»);
}

if (Hour == h && Minute == m && Second == s && AlarmOn) { // срабатывание будильника!
alarm = 1;
numberOfBeeps = 2;
alarmRun();
}

}

void lightDown() { // плавное выключение светодиода
while (brightness > 0) {
time_curr = millis();
if ((time_curr — time_old) > 10) {
if (brightness > 0) {
brightness = brightness — 1;
time_old = time_curr;
if (brightness < 1) brightness = 0;
analogWrite(led, brightness);
}
}
}
}

void lightUp() { // плавное выключение светодиода
while (brightness < 255) {
time_curr = millis();
if ((time_curr — time_old) > 10) {
if (brightness < 255) {
brightness++;
time_old = time_curr;
if (brightness > 255) brightness = 255;
analogWrite(led, brightness);
}
}
}
}

void alarmRun() { // включение сигнала 1 раз (один бип)
digitalWrite(alarmPin, HIGH);
beepCount = 0;
MsTimer2::set(500, beep); // 500ms period
MsTimer2::start();
}

void beep() { // выключение сигнала через 500 мс по прерыванию от таймера 2
if (beepCount < numberOfBeeps) {
static boolean output = HIGH;
digitalWrite(alarmPin, output);
output = !output;
beepCount++;
}
else {
MsTimer2::stop();
digitalWrite(alarmPin, LOW);
}
}
.

п.с. Внимание! С с 23 сентября по 23 октября 2015 г.мы проводим фотоконкурс

«3 D печать для решения бытовых вопросов»!

Победитель получит в подарок 3D принтер-конструктор MC5 с акриловым корпусом.

Желающие могут просто зайти и проголосовать за понравившиеся работы.

Условия фотоконкурса.
Теги:
Хабы:
+8
Комментарии 46
Комментарии Комментарии 46

Публикации

Информация

Сайт
masterkit.ru
Дата регистрации
Дата основания
Численность
Неизвестно
Местоположение
Россия

Истории