[Из песочницы] Ночник с выключением по расписанию
С рождением ребёнка встал вопрос о ночнике. Где-то прочитали, что он необходим для спокойного сна. Быстро привыкли спать с тусклым светом. Очень удобно просыпаться от криков и воев среди ночи и видеть на что жалуется малыш (если удастся понять). Так же при тусклом свете можно укачать, перевернуть и продолжить спать.
Вначале был изготовлен тестовый образец светильника из куска жёлтой светодиодной ленты (12 вольт), который использовался 1,5 года.
Помимо хлипкости конструкции стало раздражать каждое утро вынимать блок питания светильника из розетки. Встаю я утром, с улицы в комнату поступает достаточно света. Таким образом, светильник работает впустую несколько часов каждый день. Ещё раз в пол года китайский контроллер RGB ленты забывал текущую настройку и нужно было искать пульт управления чтобы напомнить ему как нужно работать. Решил сделать новый светильник с автоматическим отключением, с регулировкой цвета как с помощью потенциометров, так и по радио.
Быстро собрал прототип на базе arduino nano. Отладил базовую функциональность.
Пользуясь случаем попробовал Fritzing. Не понравилось, но картинки получаются наглядные и «весёлые». Как видно ничего нового не изобрёл.
Заменил «nano» на малоизвестный, ардуино-совместимый модуль с встроенным радио приёмо-передатчиком (контроллер 8-ми битный, производительность и нафаршерованность сравнима с «наной»). Дома у меня уже есть одно устройство работающее на частоте 868 МГц, это будет второе. Краткие характеристики с сайта производителя:
Не вижу больших проблем сделать тоже самое на ESP8266 (есть удобный онлайн сборщик прошивок для скриптов на LUA). Немного сложнее на Bluetooth (для перепрошивки модуля HM-10 нужен недорогой программатор, среда для разработки и понимание протокола). Хотя можно использовать Bluetooth модуль вместе с ардуиной. Но я использовал ZUNo, потому что уже давно лежит и ждёт меня, а также готова вся инфраструктура по соединению и управлению подобными устройствами в одну сеть (я про контроллер сети умного дома).
Для всех используемых ножек на ардуино нашлись аналоги в модуле.
Чтобы работать с модулем из Arduino IDE нужно её настроить (описание настройки есть на сайте производителя). Чуда не произошло. При попытке компиляции получил ошибку «doesn’t support «for» statement with empty columns or without body!». Я использовал библиотеку Adafruit_NeoPixel. Залез в неё увидел сколько в ней циклов и закрыл. Пришлось опять идти на сайт производителя и искать в примерах работу со светодиодами (пример быстро нашёлся). Значит, я не первый, кто столкнулся с подобной проблемой.
Чтобы данный светильник управлялся по радио в код ардуино нужно добавить макрос и реализовать несколько функций:
ZUNO_SETUP_CHANNELS(
ZUNO_SWITCH_MULTILEVEL(getRed, setRed),
ZUNO_SWITCH_MULTILEVEL(getGreen, setGreen),
ZUNO_SWITCH_MULTILEVEL(getBlue, setBlue),
ZUNO_SWITCH_BINARY(switch_getter, switch_setter)
);
Данный макрос описывает устройство Z-Wave c тремя многоуровневыми переключателями (управление RGB) и одним простым переключателем (простое включение/выключение).
Реализация функций у меня простейшая (как в примерах на сайте производителя). Посмотреть можно в прикреплённом листинге.
Подбор корпуса
Корпус у меня уже был. Герметичный с прозрачной крышкой. Под крышкой поместилось 25 светодиодов. Испытания прошли успешно. Светильник имеет большой запас по яркости, для моей комнаты. Крышка у данного корпуса прозрачная, поэтому решил немного рассеять свет.
Набросал цветных бусинок и акриловых кубиков, залил прозрачной эпоксидкой. Краска с цветных бусинок под воздействием смолы растворилась.
Самое интересное, что крышка от герметичного корпуса протекла и почти вся смола вытекла. Не знаю где я успел ударить крышку, но после высыхания трещина отчётливо видна.
Печатную плату изготовил фоторезистивным методом.
После травления и механической обработки
Модуль с микроконтроллером заменил на его прототип, валявшийся в шкафу (потому что не жалко, а ZUNo нужно беречь). Первая версия ZUNo, но габариты больше и хуже антенна, да и не купишь уже. Испытания прошли более-менее успешно. Последний сегмент пришлось перепаять. Изначально был впаян не той стороной. И скорректировать количество светодиодов в прошивке.
Вот что получилось:
Управление по радио
Главное окно с каналами управления светильника
Настройка яркости одного канала светодиодной ленты
Настройка утреннего отключения ночника
Заключение
Устройство работает. Оно компактное и аккуратное. Питается от зарядки мобильного телефона.
Из замеченных проблем:
- во время сборки оторвал часть дорожек на переменных резисторах, поэтому в ручном режиме можно управлять только одним каналом.
- из 25 светодиодов нормально работают только 20. Мне и этого много, поэтому, скорее всего, оставлю до выявления более серьёзных недостатков
#include "ZUNO_NeoPixel.h"
#define MAX_PIXELS 20 // NB! Z-Uno can not control more than 25 WS2811 without harming RF communications
#define PIXEL_SIZE 3 // Three colors per pixel
#define BUFF_SIZE (MAX_PIXELS * PIXEL_SIZE)
byte pixel_buff[BUFF_SIZE];
NeoPixel pixels(pixel_buff, BUFF_SIZE);
#define B_PRESSED 1
#define BUTTON_PIN 1 // Digital IO pin connected to the button. This will be
#define DEF_RED 30
#define DEF_GREEN 20
byte red = DEF_RED;
byte green = DEF_GREEN;
byte blue = 0;
#define POWER_ON 1
#define POWER_OFF 0
byte light_power = POWER_ON;
byte last_light_power = POWER_OFF;
ZUNO_SETUP_CHANNELS(
ZUNO_SWITCH_MULTILEVEL(getRed, setRed),
ZUNO_SWITCH_MULTILEVEL(getGreen, setGreen),
ZUNO_SWITCH_MULTILEVEL(getBlue, setBlue),
ZUNO_SWITCH_BINARY(switch_getter, switch_setter)
);
void switch_setter(byte value)
{
Serial.println("switch");
Serial.print("value= ");
Serial.println(value);
if(value > 1)
light_power = POWER_ON;
else
light_power = POWER_OFF;
}
byte switch_getter()
{
return light_power;
}
int getRed() {
return red/2.56;
}
int getGreen() {
return green/2.56;
}
int getBlue() {
return blue/2.56;
}
void setRed(byte value) {
red = value * 2,56;
for(uint8_t i = 0; i < MAX_PIXELS; i++)
pixels.setPixelColor(i, pixels.Color(red, green, blue));
pixels.show();
Serial.print("set red = ");
Serial.println(value);
}
void setGreen(byte value) {
green = value * 2,56;
for(uint8_t i = 0; i < MAX_PIXELS; i++)
pixels.setPixelColor(i, pixels.Color(red, green, blue));
pixels.show();
Serial.print("set red = ");
Serial.println(value);
}
void setBlue(byte value) {
blue = value * 2,56;
for(uint8_t i = 0; i < MAX_PIXELS; i++)
pixels.setPixelColor(i, pixels.Color(red, green, blue));
pixels.show();
Serial.print("set red = ");
Serial.println(value);
}
void set_LEDS()
{
for(uint8_t i = 0; i < MAX_PIXELS; i++)
pixels.setPixelColor(i, pixels.Color(red, green, blue));
pixels.show();
}
void read_resistors()
{
red = (analogRead(A0) >> 2) & 0xff;
green = (analogRead(A1) >> 2) & 0xff;
blue = (analogRead(A3) >> 2) & 0xff;
Serial.print(red);
Serial.print(" ");
Serial.print(green);
Serial.print(" ");
Serial.print(blue);
Serial.print(" ");
Serial.println();
set_LEDS();
}
#define DEBOUNCE_ACK 10
byte check_button()
{
static bool oldState = HIGH;
byte debounce_cnt = 0;
static byte ret = 0;
if(digitalRead(BUTTON_PIN) == LOW)
{
if(ret != B_PRESSED)
while(digitalRead(BUTTON_PIN) == LOW)
{
if(debounce_cnt == DEBOUNCE_ACK)
{
ret = B_PRESSED;
break;
}
else
debounce_cnt++;
delay(10);
}
}
else
{
debounce_cnt = 0;
ret = 0;
}
return ret;
}
void setup() {
Serial.begin(9600);
pixels.begin();
pixels.clear();
pinMode(BUTTON_PIN, INPUT_PULLUP);
}
void loop()
{
if(check_button() == B_PRESSED)
read_resistors();
if(last_light_power != light_power)
{
Serial.println("set power");
if(light_power == POWER_OFF)
{
Serial.println("power off");
red = 0;
green = 0;
blue = 0;
}
else
{
Serial.println("power on");
red = DEF_RED;
green = DEF_GREEN;
blue = 0;
}
set_LEDS();
last_light_power = light_power;
}
}