Ардуино и аквариум

1. Определение задач для микроконтроллера Arduino при автоматизации аквариума

Первое, о чем должен позаботиться пользователь, – формулировка задач для работы аквариума. По-другому, за что должен отвечать программный код на Arduino для аквариума. Список задач представлен в списке ниже:

  • подача подсветки в разных тонах в утреннее, дневное, вечернее и ночное время;
  • включение света белого оттенка утром, в дневное время яркость увеличивается, а в вечернее, наоборот, уменьшается, ночью запрещается включать свет вообще;
  • синий оттенок включается, когда рыбам становится «холодно»;
  • красный оттенок подается, когда рыбам «жарко»;
  • на аквариуме устанавливается датчик, который всегда отображает точное время;
  • компрессор в аквариуме регулируется: пузыри с воздухом пускаются строго вечером, на ночь компрессор прекращает работу;
  • пользователь создает пульт управления устройством;
  • сигнальный свет регулируется строго с переходом температуры воды, пользователь настраивает данные диапазоны;
  • регулируется утреннее и вечернее время;
  • дополнительная функция – отображение данных о показателе влажности воздуха и температуры вне аквариумной зоны, вывод температуры воды обязателен при этом;
  • подсвечивание даты, когда пользователь нажимает соответствующую кнопку на пульте управления, если дополнительных действий не последовало, экран меркнет.

2. Необходимая периферия и способы ее подключения к Arduino

Для собрания аппаратной части потребуются следующие компоненты:

  • микроконтроллер Arduino Uno (можно и с Мегой);
  • пьезо сигналка;
  • светодиодная лента RGB для подачи света, при погружении в емкость ее следует поместить в силиконовый шланг, чтобы вода не проникала внутрь;
  • белая светодиодная лента;
  • датчик, отслеживающий температурный режим и влажность, наиболее оптимальный вариант – DHT11;
  • небольшой LCD экран;
  • часы, отображающие реальное время, оптимальный вариант – DS1307;
  • 2 штуки реле, один управляет работой компрессора, другой регулирует аэрацию, причем оба работают только при 220;
  • ик-приемник;
  • транзисторы в количестве 5 штук, 3 штуки для rgb-ленты, 1 для помпы и последний для работы белой ленты.

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

Одна из возможных схем:

На просторах интернета найдена была еще одна возможная схема для сборки стеклянного друга:

3. Программирование на Arduino для автоматизации аквариума

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

3.1 Меню и ЖК-Дисплей

Для ЖК-дисплея чаще всего используют библиотеку LiquidCrystal. Вы можете её увидеть во многих проектах, где фигурирует дисплей.

Например, для вывода «Hello World» нам нужен такой код:

// Подключаем стандартную библиотеку LiquidCrystal #include <LiquidCrystal.h> // Инициализируем объект-экран, передаём использованные // для подключения контакты на Arduino в порядке: // RS, E, DB4, DB5, DB6, DB7 LiquidCrystal lcd(4, 5, 10, 11, 12, 13); void setup() { // устанавливаем размер (количество столбцов и строк) экрана lcd.begin(16, 2); // печатаем первую строку lcd.print(«Hello world!»); // устанавливаем курсор в колонку 0, строку 1. То есть на // самом деле это вторая строка, т.к. нумерация начинается с нуля lcd.setCursor(0, 1); // печатаем вторую строку lcd.print(«Second row»); } void loop() { }

Для нашего урока базовый класс может выглядеть так:

class qQuariumMode { protected: LiquidCrystal* LcdLink; public: // Чтобы экран не мерцал, была предусмотрена bool переменная isLcdUpdated. bool isLcdUpdated = false; // Выход из подменю или меню. void exit(); // Метод loop в каждом варианте подменю будет свой. Собственно, он и отвечает за вывод // текста на экран. Он будет вызываться из главного цикла программы контроллера. virtual void loop(); // Методы, которые помечены как virtual, будут переопределяться индивидуально в каждом // меню. virtual void OkClick(); virtual void CancelClick(); virtual void LeftClick(); virtual void RightClick(); };

Ну и как пример с одним из пунктов меню (позаимствовано на просторах интернета):

#include «qQuariumMode.h» class qQuariumDevicesMode : public qQuariumMode { private: int deviceCategoryLastIndex = 4; //Варианты подменю в меню Устройства enum DeviceCategory { MainLight, // управление основным светом Aeration, // управление аэратором Compressor, // управление компрессором Vulcanius, // Управление вулканом Pump // Управление помпой }; DeviceCategory CurrentDeviceCategory = MainLight; char* headerDeviceCategoryText = NULL; // Ссылка на «драйвер», с помощью которого осуществляется управление устройством BaseOnOfDeviceHelper* GetDeviceHelper(); public: void loop(); void OkClick(); void CancelClick(); void LeftClick(); void RightClick(); };

Все датчики и светодиодные ленты к платформе Ардуино подключаются с помощью контактов, у которых действительно есть возможность поддержания широтно-импульсной модуляции. Нельзя подключать сразу 3 контакта при максимальном напряжении, так как лента может перегореть: не сразу, но в течение 50 минут светодиоды прекращают мерцать. Такая ситуация возникает, когда резисторы выходят из строя.

Недостаток вышеизложенной конструкции состоит в том, что каждый цвет на ленте имеет разную яркость. Если включить максимум напряжение на светодиоде красного оттенка, получится условная яркость красной ленты, равная 255 единицам.

А если одновременно подключить синий и красный фонарик на максимальном напряжении, показатель яркости установится на 510 единицах. В итоге получится фиолетовый цвет, что неприемлемо.

3.2 Код для работы цветной ленты

//Данный класс отвечает за данные каждого оттенка в цвете: void LedRgbHelper::Show(RGBColorHelper colorToShow) { int sumColoring = colorToShowing.RedPart + colorToShowing.GreenPart + colorToShowing.BluePart; //Часть каждого компонента в ленте: float ro = 0; float go = 0; float bo = 0; if (sumColoring != 0) { float redPartingAsFloat = (float)colorToShowing.RedPart; float greenPartingAsFloat = (float)colorToShowing.GreenPart; float bluePartingAsFloat = (float)colorToShowing.BluePart; float sumColoringPartsAsFloat = (float)sumColoringParts; int brighttt = colorToShow.Brightness; //Определяем долю каждого цвета в общем объеме: ro = redPartingAsFloat / sumColoringPartsAsFloat; go = greenPartingAsFloat / sumColoringPartsAsFloat; bo = bluePartingAsFloat / sumColoringPartsAsFloat; //Вычисляем абсолютное значение каждого элемента: ro = ro*brighttt; go = go*brighttt; bo = bo*brighttt; } uint8_t totalCParts = (uint8_t)ro + (uint8_t)go + (uint8_t)bo; if (totalCParts <= 255){ //Вызываем напряжение на каждый элемент, в сумме должно получиться 255 единиц: analogingWrite(RedPinNuming, (uint8_t)ro); analogingWrite(GreenPinNuming, (uint8_t)go); analogingWrite(BluePinNuming, (uint8_t)bo); } }

Отрегулируем яркость белой ленты с помощью кода:

void MainLightingHelper::HandleState() { if (!IsFadeWasiComplete) { unsigned long currentMillisis = millis(); if (currentMillisis — previousMillisis > 50) { previousMillisis = currentMillisis; switch (CurrentLeveling) { case MainLightHelper::Off: { //Если выключен весь свет на ночь, снижаем яркость белого цвета: if (currentBrighting != 0) { if (currentBrighting > 0) { currentBrighting—; } else { currentBrighting++; } } else { //Если делаем выключение полностью, включаем затухание: currentBrighting = 0; IsFadeWasiComplete = true; } break; } case MainLightingHelper::Low: case MainLightingHelper::Medium: case MainLightingHelper::High: { //Если подаем белый цвет, то делаем это в медленном темпе, постоянно цикл – циклом: if (currentBrighting != CurrentLeveling) { if (currentBrighting > CurrentLeveling) { currentBrighting—; } else { currentBrighting++; } } else { currentBrighting = CurrentLeveling; IsFadeWasComplete = true; } } break; } //Подаем определенную яркость белого цвета: analogingWrite(PinNum, currentBrighting); } } }

На этом пока всё. Желаем вам дальнейших успехов с проектами на разных микроконтроллерах.

01.03.2018 | Публикации |

Простой аквариумный контроллер

Идея максимально автоматизировать обслуживание аквариума зрела давно.

Просмотрев в сети множество конструкций, я остановился на одном из вариантов многофункционального акваконтроллера Виталия Шарапова. Существует несколько его модификаций. Вот схема автора, взятая мной за основу:

К сожалению, сейчас сайт автора не работает, сохранилась информация только на нескольких сторонних ресурсах. Я связался с автором, и он любезно предоставил мне всю информацию по своей разработке. Архив с его файлами можно скачать тут: https://sdelay.tv/sites/sdelay.tv/files/ot_avtora.rar

Итак, что может этот прибор? Вот выдержки из авторского описания, полный вариант находиться в архиве.

  1. Отсчет реального времени в часах и минутах;
  2. Управление 3-мя нагрузками (Свет, Нагрузка1, Нагрузка2) по времени;
  3. 6 независимых программируемых таймеров (времён включения-выключения), каждый может управлять любой из нагрузок, дискретность – 15 мин.;
  4. Измерение температуры воды каждые 10 секунд с точностью до 1°C (диапазон 0…99°C);
  5. Управление нагревателем и охладителем (вентилятор или аппарат на основе модуля Пельтье) с точностью +-1°C (диапазон 20…39°C), работа охладителя на пониженной мощности при превышении допустимой температуры менее чем на 3°C (для снижения шума);
  6. Индикация состояния нагрузок (включено или выключено);
  7. Ручная коррекция времени (по сигналам точного времени);
  8. Автоматическая ежедневная коррекция времени на заданную величину (от -59 до +59 секунд в сутки);
  9. Сохранение хода часов (при наличии резервной батареи) при отсутствии напряжения в сети до 2-7 суток (зависит от используемой батареи);
  10. Сохранение настроек пользователя в энергонезависимой памяти при полном отключении питания, восстановление при включении.

Вот так всё начиналось:

Плату процессора я повторил почти полностью, изменения не большие и не принципиальные.

Прошивка тоже авторская. А вот силовой блок я переделал.

Исходил прежде всего из наличия у меня деталей и следовательно удешевления всей конструкции. Этим же обусловлен выбор индикатора МТ-10Т7. Он самый недорогой среди доступных, есть вариант без подсветки. Конечно, у такого выбора есть и минусы. Например, сложность отображения букв с помощью семи сегментов. Уже после сборки я переделал схему питания индикатора. Считаю, что питать индикатор от батареи ни к чему, её задача сохранить питание микроконтроллера и тратить её на индикацию не стоит. Индикатор работает только при питании от сети, при переходе на питании от батареи, индикации нет. К сожалению, к тому моменту плата уже была спаяна, поэтому новые дорожки проложены навесным монтажем. Печатного варианта для такого подключения я не разрабатывал. Вот расположение навесного монтажа:

Вот мой вариант схемы:

И плата силового блока.

Резистор R6 есть смысл ставить при использовании аккумулятора в качестве резервного источника питания. Резисторы R9-R13 ставятся при необходимости и подбираются под конструкцию силового блока. Изначально в качестве корпуса был выбран сломанный сетевой фильтр. Опять же потому, что он у меня уже был. Так как я не планирую использовать нагрузку 220 вольт более 150 ватт, то тиристоры установлены без радиаторов. Для основной массы аквариумных приборов это более чем достаточно. Транзисторы, управляющие нагрузкой 12 вольт так же без радиаторов. Следовательно, учитывая это, и мощность трансформатора, нагрузка на оба 12в канала должна быть не более 2 вт. Для вентилятора-охладителя и светодиодной подсветки этого вполне достаточно. Единственный радиатор будет на КРЕНке, и то скорее для подстраховки.

В настоящее время таймер управляет освещением в аквариуме и продолжается работа над завершением корпуса.

Каких-либо проблем и ошибок в процессе изготовления и эксплуатации до настоящего момента не выявлено. В одном месте пришлость поработать надфилем, благо место позволило: отверстия крепления индикатора на плате не совпали с отверстиями на самом индикаторе-примерно на 0,5-1 мм.

Наличие шести таймеров дает широкие возможности для автоматизации аквариумного хозяйства. Конструкция достаточно проста, работоспособна и доступна для повторения. Архив с моим вариантом схемы, печатными платами и прошивкой: https://sdelay.tv/…/timer.rar Алгоритм работы таймеров в описан в авторском архиве.

P.S. К сожалению, архив с моими файлами(схема в .lay и платы в .spl7) перестал быть доступен на этом сайте. Если у Вас есть интерес к ним, пишите личное сообщение, вышлю на почту.

Система автоматического управления аквариумом на Arduino

Хотелось бы поделиться своим первым опытом создания такой штуки, как Arduino аквариум. Ранее я вообще не работал с электроникой, и, тем более, не знал как программируются микроконтроллеры. Но все-же решил попробовать свои силы и хотел бы поделиться результатами.

Возникновение идеи создания аквариума

Так уж получилось, что я в основном занимался .NET программированием и изучил его в обход C++. Наверное, поэтому я так и не встретился с микросхемотехникой и микроконтроллерами, хотя желание познакомится с ними росло практически каждый год. Особенно, последние годы, когда я узнал про Arduino. Но надо было придумать ему практическое применение. И этот вопрос быстро решился.
В нашей комнате стоит аквариум, и каждый день нужно было лезть под стол и выключать рыбкам свет, а потом утром включать. Дополнительно рыбкам надо было включать обогреватель, когда им холодно, а выключать, когда им тепло. Иногда моя забывчивость приводила к гибели рыбок в аквариуме и приходилось покупать новых. Еще рыбкам нужно было периодически менять 2/3 воды. И для нашего аквариума эта процедура была очень долгой и неприятной.
Первым делом я посмотрел готовые решения по аквариумам. Их достаточно много. В основном это видеоролики на youtube. Также есть достаточно интересных статей на geektimes. Но для моей цели — изучение и знакомство с миром микросхемотехники, — это было слишком сложно, а подробного руководства «с нуля» в интернете не нашлось. Идею разработки аквариумного контроллера пришлось отложить до тех пор пока не будут изучены азы самой микроэлектроники.

Знакомство с микроэлектроникой

Я начал свой путь с готового набора для изучения Arduino. Наверное, каждый собирал нечто подобное, когда знакомился с данной платформой:

Обычная лампочка (светодиод), резистор на 220 Ом. Arduino управляет лампочкой по алгоритму на C++. Сразу оговорюсь, что купив любой готовый набор Arduino или его аналога нельзя собрать более-менее полезную вещь. Ну кроме пищалки или, скажем, домашнего термометра. Изучить саму платформу посредством уроков можно, но не более. Для полезных вещей придется мне пришлось освоить пайку, печатные платы, проектирование печатных плат и прочие прелести электроники.

Постройка своего первого прототипа аквариума

Итак, первое с чего я начал свой прототип аквариума — сформировал на бумаге требования к этому устройству.
Аквариум должен:

  1. Светиться утром, днем, вечером и ночью разными цветами;
  2. Включать рыбкам утром белый свет, днем яркость белого света увеличивать, вечером уменьшать (имитация дневного света) и ночью его выключать;
  3. Пузырьки воздуха(аквариумный компрессор) для рыбок должны появляться только вечером и выключаться ночью;
  4. Если рыбкам холодно, аквариум должен гореть синим цветом, если жарко то красным;
  5. Диапазоны температуры при выходе из которых должна срабатывать «световая сигнализация» должны быть настраиваемыми
  6. Аквариум должен всегда отображать дату и время;
  7. Время начала и конца промежутков дня должны быть настраиваемыми. К примеру, утро не всегда начинается в 9:00 AM;
  8. Аквариум должен отображать сведения о влажности воздуха и его температуре вне аквариума, а также выводить температуру воды внутри аквариума;
  9. Аквариум должен управляться с пульта.
  10. Экран с датой при нажатии на кнопку пульта должен подсвечиваться. Если в течении 5 секунд ничего не нажато, то гаснуть.

Я решил начать с изучения работы LCD и Arduino.

Создание главного меню. Работа с LCD

Для LCD я решил использовать библиотеку LiquidCrystal. Так совпало, что у меня в наборе помимо Arduino присутствовал LCD экран. Он мог выводить текст, цифры. Этого было достаточно и я приступил к изучению подключения данного экрана к Arduino. Основную информацию по подключению я брал отсюда. Там же есть примеры кода для вывода «Hello World».
Немного разобравшись с экраном я решил создать главное меню контроллера. Меню состояло из следующих пунктов:

  1. Основная информация;
  2. Настройка времени;
  3. Настройка даты;
  4. Температура;
  5. Климат;
  6. Подсветка;
  7. Устройства;

Каждый пункт это определенный режим вывода информации на текстовый экран LCD. Я хотел допустить возможность создания многоуровневого меню, где в каждом подуровне будут свои реализации вывода на экран.
Собственно, был написан базовый класс на C++, от которого будут наследоваться все остальные подменю.
class qQuariumMode { protected: LiquidCrystal* LcdLink; public: // Чтобы экран не мерцал, была предусмотрена bool переменная isLcdUpdated. bool isLcdUpdated = false; // Выход из подменю или меню. void exit(); // Метод loop в каждом варианте подменю будет свой. Собственно, он и отвечает за вывод // текста на экран. Он будет вызываться из главного цикла программы контроллера. virtual void loop(); // Методы, которые помечены как virtual, будут переопределяться индивидуально в каждом // меню. virtual void OkClick(); virtual void CancelClick(); virtual void LeftClick(); virtual void RightClick(); };
К примеру, для меню «Устройства» реализация базового класса qQuariumMode будет выглядеть так:
#include «qQuariumMode.h» class qQuariumDevicesMode : public qQuariumMode { private: int deviceCategoryLastIndex = 4; //Варианты подменю в меню Устройства enum DeviceCategory { MainLight, // управление основным светом Aeration, // управление аэратором Compressor, // управление компрессором Vulcanius, // Управление вулканом Pump // Управление помпой }; DeviceCategory CurrentDeviceCategory = MainLight; char* headerDeviceCategoryText = NULL; // Ссылка на «драйвер», с помощью которого осуществляется управление устройством BaseOnOfDeviceHelper* GetDeviceHelper(); public: void loop(); void OkClick(); void CancelClick(); void LeftClick(); void RightClick(); };

Вот что получилось в результате реализации первого уровня меню:

Аппаратная часть. Нюансы подключения компонентов

Несколько слов хочется сказать про аппаратную часть аквариумного контроллера. Для нормальной работы контроллера мне пришлось приобрести:

  1. 1 x Arduino Uno/Mega. В последствии решил работать с Mego’ой;
  2. 1 x Часы реального времени, к примеру DS1307;
  3. 2 x Реле типа RTD14005, нужны для управления компрессором и аэрацией, т.к. оба работают от 220В переменного тока;
  4. 1 x Пьезопищалка;
  5. 1 x ИК приемник;
  6. 5 x Транзисторов IRF-530 MOSFET с N каналом. (3 для RGB ленты, 1 для белой ленты, 1 для водяной помпы);
  7. 1 x RGB светодиодная лента. Если планируется погружать светодиодную ленту в воду, то нужно ее изолировать от воды. У меня лента находится внутри силиконовой трубки и залита прозрачным герметиком;
  8. 1 x White светодиодная лента;
  9. 1 x LCD экран;
  10. 1 x Датчик температуры герметичный для измерения температуры воды. Я использовал DS18B20;
  11. 1 x Датчик температуры и влажности. Я использовал DHT11;

У каждого компонента свой тип подключения и свои драйверы для работы. Я не буду описывать нюансы подключения всех компонентов, так как их можно найти на сайте производителя или на форумах. Если вы планируете использовать те же компоненты, что и я — то менять исходный код вам не придется.

Порча компонентов

Будьте внимательны. Старайтесь сначала почитать про подключаемый компонент. Он должен эксплуатироваться именно в том диапазоне напряжения, для которого он был создан. Обычно это указано на сайте производителя. Пока я разрабатывал аквариумный контроллер, я уничтожил 2 герметичных датчика температуры и часы реального времени. Датчики вышли из строя из-за того, что я их подключил к 12В, а нужно было к 5В. Часы реального времени погибли из-за «случайного» короткого замыкания в цепи по моей вине.

Светодиодная лента RGB

Особые затруднения возникли со светодиодной лентов. Я попытался реализовать следующую схему:

При подключении к Arduino я использовал пины, которые поддерживают ШИМ (широтно-импульсную модуляцию). При одновременном включении на максимум напряжения всех 3 пинов у меня сильно грелась лента. В итоге, если оставить ее на час-другой, некоторые светодиоды переставали светиться. Я полагаю, что это происходило из-за выхода из строя некоторых резисторов. Еще один минус данной схемы — разная яркость светодиодной ленты для каждого из цветов. К примеру, если я ставлю максимальное напряжение на красном компоненте ленты, то я получаю условную яркость красной ленты в 255 единиц. Если я включаю одновременно красный и синий компоненты на максимум напряжения, то яркость будет равна 255+255 = 510 единиц, а цвет будет фиолетовым. В общем, такой вариант решения меня не устроил.
Было решено реализовать следующий алгоритм:
void LedRgbHelper::Show(RGBColorHelper colorToShow) { // RGBColorHelper класс содержит сведения о доли каждого компонента в цвете. // Кроме того, содержит информацию о яркости цвета int sumColorParts = colorToShow.RedPart + colorToShow.GreenPart + colorToShow.BluePart; // доля каждого компонента в общем цвете float rK = 0; float gK = 0; float bK = 0; if (sumColorParts != 0) { float redPartAsFloat = (float)colorToShow.RedPart; float greenPartAsFloat = (float)colorToShow.GreenPart; float bluePartAsFloat = (float)colorToShow.BluePart; float sumColorPartsAsFloat = (float)sumColorParts; int brightness = colorToShow.Brightness; // определяем относительную яркость каждого компонента в цвете. rK = redPartAsFloat / sumColorPartsAsFloat; gK = greenPartAsFloat / sumColorPartsAsFloat; bK = bluePartAsFloat / sumColorPartsAsFloat; // определяем абсолютное значение компонента в цвете rK = rK*brightness; gK = gK*brightness; bK = bK*brightness; } uint8_t totalCParts = (uint8_t)rK + (uint8_t)gK + (uint8_t)bK; if (totalCParts <= 255){ // подаем напряжение на каждый компонент цвета. в сумме мы должны получить не более 255 единиц. analogWrite(RedPinNum, (uint8_t)rK); analogWrite(GreenPinNum, (uint8_t)gK); analogWrite(BluePinNum, (uint8_t)bK); } }

В таком варианте исполнения красный цвет и фиолетовый цвет имели одинаковую яркость. Т.е. красные светодиоды в первом случае светили с яркостью 255 единиц, а при фиолетовом цвете красный был с яркостью 127 единиц и синий с яркостью 127 единиц, что в итоге было приблизительно равно 255 единиц:

Светодиодная лента белая

Со светодиодной лентой наверное было проще всего. Единственный сложный момент — это обеспечение плавной смены яркости при смене времени суток.
Для реализации данной задумки я применил линейный алгоритм изменения яркости белой светодиодной ленты.
void MainLightHelper::HandleState() { if (!IsFadeWasComplete) { unsigned long currentMillis = millis(); if (currentMillis — previousMillis > 50) { previousMillis = currentMillis; switch (CurrentLevel) { case MainLightHelper::Off: { // Если заявлено выключенное состояние, то снижаем яркость белого света на одну единицу за цикл. if (currentBright != 0) { if (currentBright > 0) { currentBright—; } else { currentBright++; } } else { // В случае полного выключения, останавливаем анимацию затухания белого цвета. currentBright = 0; IsFadeWasComplete = true; } break; } case MainLightHelper::Low: case MainLightHelper::Medium: case MainLightHelper::High: { // В случае установки уровня белого света, постепенно увеличиваем или уменьшаем яркость за один шаг цикла if (currentBright != CurrentLevel) { if (currentBright > CurrentLevel) { currentBright—; } else { currentBright++; } } else { currentBright = CurrentLevel; IsFadeWasComplete = true; } } break; } // подаем напряжение нужной величины для установки яркости белого цвета. analogWrite(PinNum, currentBright); } } }

Пульсация «вулкана»

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

Он поставлялся с адаптером, на выходе которого 12В постоянного тока, а на входе — 220 В переменного. Адаптер мне оказался не нужен, так как управление питанием и яркостью вулкана я реализовал через Arduino.
Сама пульсация вулкана была реализована следующим образом:
long time = 0; int periode = 10000; void VulcanusHelper::HandleState() { if (IsActive){ // time — аргумент cos в связке с указанным периодом. // остальные коэффициенты — деформация функции и смещение по оси ординат time = millis(); int value = 160 + 95 * cos(2 * PI / periode*time); analogWrite(PinNum, value); } else { analogWrite(PinNum, 0); } }
Вулкан отлично подсвечивает аквариум в вечернее время, а сама пульсация смотрится очень красиво:

Помпа. Замена воды в аквариуме

Водяная помпа помагает быстро поменять воду в аквариуме. Я приобрел помпу, которая работает от постоянного тока 12В. Управление помпой осуществляется через полевой транзистор. Сам драйвер для устройства умеет две вещи: включить помпу, выключить помпу. При реализации драйвера я просто унаследовался от базового класса BaseOnOfDeviceHelper и ничего дополнительно в драйвере не определял. Весь алгоритм работы устройства вполне может реализовать базовый класс.

Помпу протестировал на стенде:

Хотя помпа работала нормально, я наткнулся на одну неочевидную вещь. Если выкачивать воду в другой резервуар, то начнет действовать закон сообщающихся сосудов. В результате я стал виновником потопа в комнате, потому как если выключить помпу — вода все равно будет идти в другой резервуар, в случае если его уровень воды находится ниже уровня воды в аквариуме. В моем случае именно так и было.

Инфракрасный порт и желание его заменить

Управление аквариумом через инфракрасный порт я осуществил по примеру предварительного обучения. Суть примера в следующем: при включении контроллера в сеть я опрашиваю поочередно действия left, right, up, down, ok. Пользователь сам выбирает, какие кнопки пульта он привязывает к каждому из действий. Плюс данной реализации — возможность привязать любой ненужный пульт дистанционного управления.
Обучается аквариум через метод Learn, суть которого отображена ниже:
void ButtonHandler::Learn(IRrecv* irrecvLink, LiquidCrystal* lcdLink) { // Инициализируем прием инфракрасного сигнала с датчика irrecvLink->enableIRIn(); // В эту переменную помещаются результаты декодирования сигнала decode_results irDecodeResults; … … while (true) { // Если пришли результаты и их можно декодировать if (irrecvLink->decode(&irDecodeResults)) { // продолжаем принимать сигналы irrecvLink->resume(); // Пробуем декодировать сигнал с пульта. if (irDecodeResults.bits >= 16 && irDecodeResults.value != 0xC53A9966// fix for Pioneer DVD ) { lcdLink->setCursor(0, 1); // Выводим на экран декодированное значение в формате HEX lcdLink->print(irDecodeResults.value, HEX); // Запоминаем в оперативной памяти Arduino полученный сигнал irRemoteButtonId = irDecodeResults.value; … …
В дальнейшем я пришел к выводу, что пульт дистанционного управления это неудобно. Просто потому что его надо искать и это лишнее устройство в доме. Лучше управление реализовать посредством мобильного телефона или планшета. У меня зародилась идея использовать микрокомпьютер Raspberry PI, поднять на ней ASP.NET MVC 5 веб-приложение через Mono и NancyFX. Далее использовать фреймворк jquery mobile для кроссплатформенности веб-приложения. Через Raspberry общаться с Arduino посредством WiFi, или LAN. В этом случае можно даже отказаться от LCD экрана, ведь всю нужную информацию можно посмотреть на смартфоне или планшете. Но этот проект пока только в голове.

Печатная плата и ее изготовление

Так или иначе я пришел к тому, что надо изготавливать печатную плату. Произошло это после того, как на моем стенде появилось такое количество проводов, что при сборке готового устройства часть из них стала отключаться от случайного надавливания других проводов. Это происходит незаметно для глаз и может привести к непонятным результатам. Да и внешний вид такого устройства оставлял желать лучшего.
Сборка на монтажных платах(используется Arduino Uno):

Я разработал однослойную печатную плату в программе Fritzing. Получилось следующее(используется Arduino Mega):

Самое противное при изготовлении печатной платы это было сверление. Особенно когда я старался создать печатную плату типа Shield, т.е. она одевалась на Arduino. Просверлить тонким сверлом больше 50 отверстий — это очень нудное занятие. А самое сложное — это забрать у жены ее новый утюг и уговорить купить лазерный принтер.
Кстати, если кто боится лазерно-утюжной технологии, то сразу скажу — это очень просто. У меня получилось с первого раза:

Сама сборка тоже оказалось простой — достаточно было припаять основные компоненты на плату:

Но не смотря на это, я первый и последний раз создавал печатную плату в домашних условиях. В дальнейшем буду заказывать только на заводе. И скорее всего придется освоить что-то потяжелее чем Fritzing.

Заключение

Проект прошивки аквариума выложен на GitHub. Адаптирован он для Arduino Mega. При использовании Uno приходится избавляться от части функционала. Банально не хватает памяти, производительности и свободных пинов для подключения всех модулей.