Контроллер комнатной подсветки на базе Arduino nano


Контроллер на своём месте

Здравствуйте! После того, как c помощью arduino я намигался светодиодами, устал выводил “Hello World” на дисплей и крутить сервами, появилась мысль сделать нечто, что будет приносить хоть какую-то пользу. И, собственно, возжелал я автоматической подсветки в комнате. Предупреждение: этот проект имеет скорее статус учебного, то есть цель — как можно сильнее выпендриться, а не экономить ресурсы. Поэтому, если вы относитесь к харкорным микроконтроллерщикам, прошиваете МК одним взглядом и способны впихнуть функционал моего проекта в схему с двумя транзисторами и фоторезистором — статья вам не понравится.

Общее описание

На самом деле, как только я захотел подсветку, я взял кусок текстолита, грубо нарисовал на нём дорожки “методом квадратов” и напаял светодиоды с резисторами. Получилась ужасная на вид линейка светодиодов. В таком виде она светила около трех дней, после чего я подумал, что так жить нельзя.

Итак, были сформирован нехитрый список требований:

1) Модульность. Любая часть системы должна быть легко заменяема.
2) Аккуратный внешний вид.

Исходя их этих требований была придумана система, состоящая из двух основных компонентов:

  • контроллер (про себя я называю его “управляющий центр”), который содержит в себе трансформатор 220В->5В, логику, драйверы для светодиодных модулей.
  • светодиодный модуль, который является светодиодной линейкой, с подключёнными параллельно белыми светодиодами (у каждого светодиода свой токоограничительный резистор 220 Ом).

Логика реализована на Arduino Nano. Именно эта разновидность Arduino была выбрана потому, что она сочетает в себе малые габариты и удобство подключения кабеля для заливки прошивки.
Разумеется, для пущего эффекта, подсветка должна зажигаться и гаснуть плавно. Для этого я решил использовать ШИМ. Как известно Arduino Nano имеет 6 выводов с ШИМ. Получается, контроллер будет иметь 6 разъёмов для подключения модулей.
В качестве драйвера светодиодов я выбрал микросхему ULN2003. Это транзисторная сборка, она имеет 7 выводов, каждый из которых может “протащить” ток до 0,5А. Я использовал по одной микросхеме на 2 разъёма, спаяв выводы группами по 3 и 4 вывода. Итого, я получил 3 разъёма для подключения модулей подсветки, с максимальным током 1,5А и 3 вывода с током до 2А. Для соединения модулей с управляющим центром я использовал USB разъёмы.

Подробности про светодиодные модули

Светодиодный модуль представляет собой горстку светодиодов, напаянную на кусок макетной платы. Возможны модули двух видов: основной и ведомый. Основной модуль несёт на себе: разъём для подключения датчика, разъём для подключения к управляющему центру, по которому подаётся питание для датчика (напрямую от БП) и питание для светодиодов (через транзистор). Всего мне требуется 5 контактов для подключения модуля к контроллеру. Земля, питание, земля для светодиодов, 2 контакта для подключения датчика. Именно 2 контакта потому, что изначально я планировал использовать ультразвуковые дальномеры. Но они требуют усложнения кода для определения движущегося объекта, поэтому, я отказался от них в пользу PIR сенсоров. Поэтому, сейчас один контакт USB разъёма фактически не используется, хотя провод в соединительных кабелях припаян. Так же, основной модуль имеет один разъём для подключения ведомого модуля. Ведомый модуль состоит из светодиодов и двух разъёмов для подключения ведомых модулей. Таким образом, для организации подсветки одной локации необходим один основной модуль и неограниченное количество (главное — влезть в ограничение по току) ведомых модулей, подключаемых цепочкой.

Для визуалов схемы:

Основной:

Ведомый:

Подробности про центр управления

Сначала, я собрал на макетке всё это с одной платой Arduino Nano. Но неожиданно открыл для себя тот факт, что ШИМ работает не на магии, а на таймере. И этот самый таймер могут занимать сторонние библиотеки (в моём случае — библиотека для работы с пультом ДУ). Ситуация при которой мне прихошлось выбирать — плавное зажигание светодиодов или управление с пульта мне не понравилась. Поэтому, я не долго думая, взял вторую плату. Одна платка у меня занимается включением модулей и отсчитывает время их свечения, другая — обрабатывает события с датчиков и внешние сигналы управления (ИК пульт ДУ и “локальные” кнопки). Между собой платы общаются по I2C.
Раз уж я привёл схему модуля, то озаботился “вычерчиванием” и контроллера тоже (картинка кликабельна):

Воплощение в железе

В качестве “носителя” я использовал макетную плату — текстолит с дырочками. БП взял из закромов. Лежал у меня крутой блок питания для светодиодных лент, купленный в китайском интернет магазине. Характеристики блока достойные — 5В, 7А. Из ПКЛ смастерил стойки, «впаянные» в решётчатый корпус БП. На Стойки прилепил площадку с угловыми прихватами. На площадку как родная встаёт плата центра управления, и фиксируется для надёжности канцелярским зажимом. Это мой первый опыт работы с ПКЛ, площадка прогнулась, но изначально была сделана идеально ровной (придётся поверить мне на слово). Как всё это выглядит, видно на фотографии:

Центр управления

В качестве гнезд для Arduino использовал отрезки PBS-40 (сорокадырочная гребёнка «мама»). ULN2003 установлены в специальные кроватки. Разъём для подключения ИК приёмника выполнен из отрезка “гребёнки”. Я отрезал 4 пина и вытащил один из них — получился разъём с ключём. О изготовлении ответной части разъёма можно почитать в конце статьи. Есть шесть 6мм кнопок. Каждая включает/выключает модуль, подключённый к соответствующему разъёму. Эти 6 кнопок подключены к одному аналоговому пину. Одна 12 миллиметровая кнопка включает/выключает все модули сразу. Разъём, по которому подаётся питание на плату так же выполнен в виде USB разъёма.
Фотографии пустой платы и платы в состоянии боевой готовности:


Модули подсветки

Модули выполнены так-же на макетке. “Косое” расположение резисторов позволяет делать модули более узкими, но снижает плотность монтажа светодиодов. Впрочем, более плотно их сажать и не к чему. Сейчас модуль “стандартной” длинны вмещает 14 светодиодов, чего вполне хватает для моих целей. Как не трудно догадаться — модуль может быть выполнен в любом виде. Например, можно использовать отрезок 5В светодиодной ленты, а разъёмы смонтировать прямо на кабеле.

Основной:

Ведомый:

Изготовление разъёмов с ключём

Для изготовления разъёма необходима гребёнка, гребёнка-мама и кое-какие инструменты.

Откусываем 4 штырька от “гребёнки”. Берём PBS-40, и отсчитываем 4 дырочки. На пятой кусаем кусачками. Именно на пятой потому, что гнездо, по которому проходит линия “надкуса”, разрушается. По желанию, облагораживаем срез. Я для этого пользуюсь перочинным ножом.


Из обоих разъёмов второй справа (с любого права) контакт вытаскиваем чем-нибудь (например, теми же кусачками). Штырёк от гребёнки вставляем в освободившееся отверстие второй части разъёма. Прислоняем к штырьку горячий паяльник. Когда штырёк прогреется, жалом паяльника толкаем его вглубь, одновременно прижимая его к стенке разъёма, то есть вводим его наискось. Продолжаем впаивать заглушку до тех пор, пока она не покажется с обратной стороны.

Ответная часть разъёма с ключём готова. Можно припаивать провода и радоваться.

Прошивки

В архиве 2 скетча. Один для платы, работающей с датчиками и кнопками (master), второй для платы, работающей непосредственно с модулями (slave): архив.

В каждой прошивке есть структуры Joint. В выглядят они так:

master:

struct Joint
{
    byte modul_num; //номер модуля (к какому пину подключён модуль знает только PWM контроллер)
    byte sens_type; //тип датчика (0 - цифровой, 1 - ультразвуковой дальномер)
    byte code; //Код датчика.
               //Нужен для соотнесения описания с конкретным аналоговым датчиком
    byte sensor_pin; //вывод датчика (для цифровых)
    byte detect; //1, когда датчик сработал
    byte detect_old_state;
    byte event; //Что считать срабатыванием датчика (ноль или единица)
    unsigned long detect_delay; //задержка срабатывания
    unsigned long last_on_timestamp; //дата последнего срабатывания
};
Joint joints[] =
{
  //Модуль на письменном столе
  {0, 0, 0, //modul_num, sens_type, code
   2, 0, 0, LOW, //sensor_pin, detect, detect_old_state, event
   1000, 0 //detect_delay, last_on_timestamp
  },
  //Модуль в тумбочке
  {1, 0, 0, //modul_num, sens_type, code
   3, 0, 0, LOW, //sensor_pin, detect, detect_old_state, event
   500, 0 //detect_delay, last_on_timestamp
  },
};

slave:

struct Joint
{
    byte modulepin; //вывод для управления светодиодами
    byte pwm; //Значение ШИМ
    byte on; //0 - включить по таймеру. 1 - держать включённым, пока с датчика приходит "1"
    byte power; //0-не включать, 1- включить
    byte detect; //Состояние датчика
    byte fadeup; //Задержка при повышении яркости, микросекунды, настроечная
    byte fadedown; //Задержка при понижении яркости, микросекунды, настроечная
    byte OUTon; //Пришла команда не с датчика включить модуль
    unsigned long off_timeout; //задержка выключения (1000 - 1 секунда)
    unsigned long on_timestamp; //когда включен (для модулей, светящих по таймеру)
};

Joint joints[] =
{
  //Модуль на письменном столе
  {3, 0, 0, 0, //modulepin, pwm, on, power
   0, 10, 10, 0, //detect, fadeup, fadedown, OUTon
   120000, 0 //off_timeout, on_timestamp
  },
  //Модуль в тумбочке
  {5, 0, 1, 0, //modulepin, pwm, on, power
   0, 1, 5, 0, //detect, fadeup, fadedown, OUTon
   120000, 0 //off_timeout, on_timestamp
  },
};

Что есть что должно быть понятно из комментариев. Поясню некоторые моменты: master оперирует абстрактным «номером» модуля. События в ШИМ контроллер передаются в виде <тип сообщения> <номер модуля> <состояние датчика>. А уже slave знает, как соотносить номер модуля с конкретным пином. В моём случае, номер пина в master — просто порядковый номер структуры, описанной в slave.
В прошивке для master можно видеть «byte code; //Код датчика //Нужен для соотнесения описания с конкретным аналоговым датчиком» — это как раз использовалось для работы с УЗ дальномерами. Сейчас не убрано…на всякий случай. При подключении нового модуля, нужно просто описать ещё одну структуру в обеих прошивках.

Мастер

Идея прошивки мастера заключается в том, что бы циклично опрашивать датчики. Если обнаруживается изменение состояния датчика, инициируется передача события ведомому модулю. Каждые 5 секунд осуществляется синхронизация. Синхронизация была добавлена мной опять таки «на всякий случай». Не разу такой ситуации не возникало, но она призвана избавить систему от ситуации, когда сообщение о изменении состояния датчика «потерялось». Во время синхронизации просто передаётся текущее состояние всех датчиков. После проверок состояний датчиков, вызывается процедура OUTcontrol, которая: считывает состояние большой кнопки, считывает напряжение на аналоговом пине, к которому подключено шесть кнопок, проверяет, не пришло ли чего на ИК датчик. По результатам работы процедуры изменяется значение переменной OUTcode. Если оно оказывается отличным от нуля, осуществляется передача на ведомый модуль сообщения о значении этой переменной.

Ведомый (ШИМ контроллер)

Скажу сразу — для плавного управления яркостью, используется delay во время изменения значения ШИМ. Я знаю, что это плохо и в планах стоит избавиться от этого. Сейчас же, что бы не допустить запредельного увеличения задержки, при одновременно наличии нескольких загорающихся/затухающих модулей вычисляется среднее значение задержки для всех модулей некрасивой процедурой real_delay. При получении сообщения от мастера, вызывается процедура receiveEvent. Первым байтом передаётся тип сообщения. Сообщения бывают двух типов: 0-передача состояния датчика и 1-внешняя команда. При получении нуля, ожидается ещё 2 байта: номер модуля и состояние датчика на модуле, после их получения, значения сохраняются в соответствующей структуре. При получении сообщения типа «один», внешняя команда считывается сразу в переменную OUTcode, которая затем обрабатывается процедурой OUTcontrol, вызываемой из основного цикла. Если в качестве типа сообщения приходит не ноль и не единица (ну, помеха, например), выбираем всё сообщение до конца, что бы освободить буфер и не прозевать «настоящее» сообщение.

Финансовые затраты

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

Для управляющей платы:
Arduino Nano — 800р x2
ULN2003 — 10р x3
«Кроватки» для микросхем в корпусе DIP-16 — 2р x3
Резисторы, Конденсаторы — 100р
Тактовая нопка 6мм (5 шт в наборе) — 10р х2
Тактовая кнопка 12мм — 2р
Поликапролактон — 100р
Макетная плата (5 в наборе) — 160р
Однорядная гребёнка (PLS-40) — 15р
Однорядная гребёнка мама (PBS-20) — 30р х2
USB разъёмы мама (10 шт в наборе) — 48 р
USB разъёмы папа (10 шт в наборе) — 54 р
Блок питания — 700 р
Витая пара для соединительных кабелей — бесценно

Для модуля:
Светодиоды 5мм. (5 штук в наборе) — 70р х2
Резисторы 240 Ом (10 штук в наборе) — 15 р
+ датчик на ваш вкус.

Итого (контроллер): 2900 рублей.
Итого (модуль без датчика): 155 рублей.


Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Android Arduino Bluetooth CraftDuino DIY IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение