Прочитал множество статей, посвященных разработке своего устройства, и захотел рассказать о своем опыте. Происходило это несколько лет назад, на 4-м курсе универа. Сейчас я многое сделал бы уже по-другому, а в то время я только начинал осваивать электронику, это — мое первое устройство, так что не судите строго.
Мне всегда хотелось чего-то большего, чем обычный градусник за окном или ЖК-экран метеостанции с температурой на улице и в комнате. Поэтому, когда я задумался, «что же такое сделать» для того, чтобы начать знакомство с миром микроконтроллеров, ответ нашелся сам собой — свою метеостанцию. Естественно, с отображением градусов на улице и внутри помещения, влажности и давления. И с подсветкой — мне всегда нравилась реализация прогноза погоды на Яндексе — одного взгляда на фон достаточно, чтобы понять, будет тепло или холодно, и насколько.
Весь дальнейший функционал был определен небольшим мозговым штурмом. Наличие подсветки, безусловно, плюс, но как быть ночью и вечером? Я решил установить ИК-сенсор, реагирующий на приближение. При подходе к прибору на комфортное расстояние включается подсветка, в иное время экран по умолчанию не горит. Использование ИК подтолкнуло к реализации управления прибором также по ИК-каналу — через пульт (поначалу были опасения о взаимных помехах, но они не подтвердились). Вполне естественно для такого устройства наличие часов.
В качестве основы для системы была выбрана Arduino, которую я только начинал осваивать. Саму Arduino я рассматриваю сейчас (да и тогда), как фреймворк — в первую очередь программный, позволяющий быстро строить необходимую систему, подключая при необходимости плагины-библиотеки. Да, мы можем писать и на чистом С/С++, но в большинстве рядовых задач это дает лишь незначительный прирост производительности, практически незаметный на фоне простоты и удобства загрузки скетчей в Arduino, а также обширнейшей коллекции библиотек для работы с различным железом. (Само собой, что есть особые задачи, но сейчас речь не о них).
В аппаратной же реализации я предпочитаю использовать синюю плату Arduino только на этапе прототипирования на «макетке», а по окончанию разработки устройств обычно проектирую одну-единственную плату с микроконтроллером и всем остальным. Так было и в тот раз.
Подбор компонентов я начал с экрана. Довольно быстро нашелся замечательный экран с RGB-подсветкой www.adafruit.com/products/398, что позволяло получить практически любые цвета. Он построен на популярном чипе HD44780 и поддерживается огромным количеством библиотек, в том числе и LiquidCrystal в Arduino.
Также я поинтересовался ценами на индивидуальное производство дисплеев. Минимальная цена за 1 экземпляр монохромного ЖК (как в типовой метеостанции) по ценам того времени была порядка 1 000 евро, для некоммерческого проекта в единичном экземпляре я счел такой ценник нецелесообразным.
В качестве ИК-датчика был выбран Sharp GP2Y0A02YK0F. Немаловажной характеристикой в моем случае являлась дальность обнаружения объектов, у этого датчика она равна 1.5 м, в то время как у многих других сенсоров она не превышает 30 см. Как показала себя эксплуатация, для небольшого экрана 16х2 полтора метра — действительно оптимальное расстояние.
В качестве датчика давления был выбран Bosch BMP085, работающий по I2C, влажности HH10D — с частотным выходом. Забегая вперед, скажу, что сейчас я бы ни в коем случае не использовал последний, а предпочел бы исключительно I2C-варианты, например HTU21D.
В качестве внешних сенсоров температуры использовались всеми любимые DS18B20. Их огромным преимуществом является возможность подключать (и отключать при необходимости) на 1 шину сразу несколько датчиков, без необходимости менять код программы. С беспроводной передачей данных температуры я в своем первом проекте я связываться не стал, тем более что у меня была возможность проложить провода без ущерба эстетике.
ИК-приемник был взят наиболее типовой, типа TSOP382. Пультом для метеостанции стал пульт от какого-то видеорегистратора. Зуммер (она же пищалка) — самый обычный пьезоизлучатель.
В качестве часов реального времени я выбрал DS1307, также работающую по I2C, кроме того, у меня была крошечная микросхема флеш-памяти 24AA256 на 64 Кб с I2C-интерфейсом. Ее я добавил в проект из чистого любопытства — попробовать работу с внешней памятью, записывая в нее данные о погодных условиях. Питание для проекта – внешнее, от блока питания. На входе стоит преобразователь/стабилизатор напряжения LM7805, сам узел питания очень похож на используемый в Arduino (хотя и не является клоном).
Разработку я выполнял итерациями, так намного проще выполнять отладку и знакомиться с новой для себя областью. На самом первом этапе с термометра DS18B20 считывались данные и выводились на ЖК-экран. На следующем — значения температуры должны были превращаться в RGB-код для подсветки.
Тут меня подстерегала особенность цветопередачи. Управление цветом экрана осуществляется при помощи широтно-импульсной модуляции для каждого из трех светодиодов подсветки (при помощи стандартной библиотеки) с 256 ступенями – казалось бы, вполне обычный 24-битный цвет. К сожалению, длины волн для цветов R, G и B монитора и светодиодов подсветки сильно различаются, и из-за этого обычные цвета, вроде #ffff00, на фоне моего ЖК-экрана выглядят совершенно иначе («уходят в сторону»). Поэтому мне пришлось написать на С# программу с тремя ползунками и color picker’ом, передающую три компоненты цвета в последовательный порт Arduino. Дальше пришлось вспомнить азы математики и составить функцию, преобразующую температуру в градусах по Цельсию в RGB-цветовую шкалу. Естественно, я уже не ограничивался желто-голубой шкалой Яндекса, и использовал и другие цвета — на основе своих ассоциаций.
Следующими этапом стали подключение IR дальномера, датчиков температуры и влажности. И, если с первыми двумя не возникло никаких сложностей (кроме пайки BMP085 в LCC8-корпусе), то датчик влажности доставил массу проблем.
Все дело в том, что итоговое значение относительной влажности рассчитывается по формуле, одним из аргументов которой является частота меандра на выходе датчика. Измерение частоты выполняется при помощи аппаратного таймера микроконтроллера. В 328, к сожалению, таких таймеров всего лишь три, и большая их часть уже задействована при ШИМе для подсветки дисплея. (Сейчас уже не помню все детали, возможно, что-то пропустил).
Из этой ситуации было несколько выходов. Если бы я разрабатывал устройство сегодня, я бы однозначно использовал только I2C-сенсоры. Другим вариантом было использовать более мощный микроконтроллер. Я же тогда выбрал третий вариант — установить отдельный микроконтроллер для работы со звуком (пищалка) и подсветкой дисплея (напомню, это был в первую очередь образовательный проект, и мне было интересно попробовать организовать взаимодействие между двумя МК). В него же перешла функция преобразования температуры в цвет, что облегчило на пару килобайт прошивку главного МК (в Atmega328 размер памяти программы составляет всего лишь 32Кб, моя прошивка в итоге вплотную приблизилась к этому пределу). Взаимодействие между МК было все так же организовано по I2C.
После этого были добавлены пульт, часы, флеш-память. Следующей стадией стало написание удобного меню, добавление программных фич (таких, как блокировка подсветки в текущем состоянии, режим больших цифр — как на уличных часах, с прокруткой всех параметров), поддержка нескольких датчиков температуры с их добавлением / удалением онлайн (да, я знаю, что так лучше не делать). Вполне привычное дело для ПК — и необычное поначалу для собранного тобой устройства, когда без изменения схемы в несколько раз возрастает функциональность проекта…
Пульт использовал какой-то свой проприетарный протокол. Я не стал заниматься его реверс-инжинирингом, мне было вполне достаточно шестнадцатеричного представления каждой кнопки, получаемого мной от библиотеки IRRemote. Для флеш-памяти я выбрал запись данных погоды каждые 10 минут, при длине записи в 16 байт этого хватает на 4 месяца.
Код, отвечающий за работу с флеш-памятью
LOGGER.h
LOGGER.cpp
#ifndef LOGGER_h
#define LOGGER_h
#include <WProgram.h>
class LOGGER {
public:
LOGGER(int);
void storeRecord16(byte* buffer);
void getRecord16(unsigned int address, byte* buffer);
int getAddress();
void setAddress(int);
private:
int _FlashI2CAddress;
void _getAddress();
void _setAddress();
unsigned int _addr;
};
#endif
LOGGER.cpp
#include <WProgram.h>
#include <Wire.h>
#include "LOGGER.h"
/*
MEMORY MAP:
0000 - MSB of last written address
0001 - LSB of last written address
...
0040 - 7FFF - storage,
*/
LOGGER::LOGGER(int a)
{
_FlashI2CAddress = a;
}
void LOGGER::storeRecord16(byte* buffer)
{
byte c;
//get address
LOGGER::_getAddress();
//increase addr
delay(5);
_addr+=16;
if(_addr>0x7FF0) _addr = 64;
//store buffer
Wire.beginTransmission(_FlashI2CAddress);
Wire.send((byte) (_addr >> 8) & 0xFF); // MSB
Wire.send((byte) (_addr & 0xFF) ); // LSB
for ( c = 0; c < 16; c++)
Wire.send(buffer[c]);
Wire.endTransmission();
delay(20);
//save new addr
LOGGER::_setAddress();
}
void LOGGER::getRecord16(unsigned int address, byte* buffer)
{
byte c;
//set address
Wire.beginTransmission(_FlashI2CAddress);
Wire.send((byte) (address >> 8) & 0xFF); // MSB
Wire.send((byte) (address & 0xFF) ); // LSB
Wire.endTransmission();
Wire.requestFrom(_FlashI2CAddress,16);
for (c = 0; c < 16; c++ )
if (Wire.available()) buffer[c] = Wire.receive();
}
void LOGGER::_getAddress()
{
byte c;
Wire.beginTransmission(_FlashI2CAddress);
Wire.send(0);
Wire.send(0);
Wire.endTransmission();
Wire.requestFrom(_FlashI2CAddress,2);
for (c = 0; c < 2; c++ )
if (Wire.available()) _addr = _addr * 256 + Wire.receive();
}
void LOGGER::_setAddress()
{
Wire.beginTransmission(_FlashI2CAddress);
Wire.send(0); // pointer
Wire.send(0); // pointer
Wire.send((byte) (_addr >> 8) & 0xFF); // MSB
Wire.send((byte) (_addr & 0xFF) ); // LSB
Wire.endTransmission();
}
void LOGGER::setAddress(int addr)
{
_addr = addr;
LOGGER::_setAddress();
}
int LOGGER::getAddress()
{
LOGGER::_getAddress();
return _addr;
}
Выгрузка данных производится по команде из меню метеостанции (чем-то напоминает меню старых Нокиа или Самсунг, только без графики) в последовательный порт.
После этого законченная функционально метеостанция пробыла в виде Arduino и макетных плат где-то с неделю. За это время тестов обнаружилась возможность зависания (вызванная нарушением герметичности уличного датчика температуры). Конструкцию датчика я переделал (сегодня я бы брал только фабричный, например, такой ), но и саму возможность зависаний хотелось исключить в принципе. Кроме того, я предположил, что через 49 дней непрерывной работы произойдет переполнение функции millis(), что, в связи с особенностями алгоритма прошивки, также приведет к зависанию. Вещь должна быть надежной! Поэтому последним штрихом в системе стала активация watchdog таймера, в итоге любое зависание гарантированно не продлится более чем 2 сек + 5 сек на reboot (да, в наш цифровой век даже метеостанции нужно время на загрузку.
Итак, станция готова и успешно работает. Я считаю, что останавливаться на этом этапе неправильно. Устройство должно быть удобным и иметь законченный внешний вид, так, чтобы им можно было пользоваться с комфортом и показывать всем, а не только друзьям-гикам.
Я начал с похода в ближайший радиомагазин и выбора корпуса. Из 20-30 различных вариантов мне приглянулся тот, что на рисунке в начале статьи – из-за минимума требуемой переделки, из-за возможности удобно закрепить снаружи корпуса дальномер, так, чтобы он сильно не выпирал, из-за хорошей вентиляции (а значит, более точных показаний температуры в комнате). Сегодня, наверное, я бы заказал печать на 3D-принтере и сделал что-то вот в этом стиле:
(Сложно найти похожую картинку, а в 3D -моделировании я не профи. Экран располагается в верхней части, все остальное в подставке). И, разумеется, никаких проводов — 433МГц для сенсоров и Wi-Fi для связи с ПК.
После примерок и раздумий, как лучше расположить все, пришла очередь разработки платы. В своем первом проекте я использовал систему Eagle. Начертил контур платы, разместил на ней элементы и связи (да, этот проект я делал «неправильным» образом – без принципиальной схемы. Точнее, без схемы в Eagle), трассировщик с легкостью все развел. Отнес на производство…
На Хабре очень любят тему изготовления печатных плат, поэтому здесь сделаю отступление. С ЛУТом я знаком, но у меня нет желания/возможности делать платы, в том числе связываться с химией (хлорное железо та еще вещь). Зато при универе есть небольшое опытное производство, где по очень умеренным ценам (~$3 за кв.дм. на то время) могут изготовить практически любую двустороннюю ПП. Технология – фоторезист, отверстия – от 0.6мм, минимальная ширина дорожки – вроде бы 0.2мм (точно не крупнее, сейчас уже не вспомню). К сожалению, никаких паяльных масок, металлизации переходных отверстий за такую цену ждать не приходится, но это все решаемо. В конце концов, для прототипирования (а платы готовят за день-два) и мелкосерийного производства можно обойтись и без масок (имхо; пайку подразумеваю вручную и крупную).
Впрочем, когда я делал заказ, то про металлизацию переходных отверстий я не подумал. Eagle благополучно использовал ножки элементов в качестве таковых, и обошелся всего лишь 2-3 отдельно стоящими соединениями между слоями. Человек на выдаче обратил на это внимание и любезно предложил тончайший провод для создания переходов в отверстиях для элементов (то есть, сначала через отверстие к верхней и нижней контактной площадке припаивается проводок, после чего в него уже вставляется сама ножка элемента). Я попробовал так действовать, но результаты и объем дополнительной работы не внушали оптимизма. Пришлось перетрассировать плату, создав зоны запрета для расположения via вблизи всех PTH-элементов. Тут автоматический трассировщик доводить работу до конца отказался, и мне пришлось разводить оставшиеся цепи вручную (желания и времени менять систему проектирования уже не было). Сами переходные отверстия в итоге делались точно по такой же технологии, но их количество я свел к минимуму. Паять вновь заказанную плату было несравненно проще.
Далее – работа дрелью, ножом и напильниками, корпус приобрел окошко для экрана, крепления для платы. Пара вечеров – и устройство в корпусе, успешно работает. И все же, чего-то не хватает…
А не хватает наклейки на переднюю панель (особенно после прорезки окна в ней). Самоклеющаяся бумага – отличная вещь для подобных задач. То, что получилось – на титульном фото, строго не судите:) После этого внешний вид устройства изменился радикально…
Вот так (вкратце) выглядел у меня путь разработки моего первого устройства. За кадром осталось много кода, много решений различных мелких возникавших по ходу задач. Главное, что я приобрел для себя – это опыт разработки, набитые «шишки», понимание принципов работы, плюс бонусом стала сама метеостанция. Уникальная и единственная в своем роде.
P.S. Я знаю, что многие из решений не оптимальны, но, именно благодаря им (в том числе) я теперь понимаю, что оптимально, а что нет:). Бонусом прилагаю функциональную схему на английском и несколько фото, а также рендер платы.
Функциональная схема:
Рендер, реальная плата без паяльной маски. Не показан держатель литиевой батареи, пьезоизлучатель, DIN-разъем (питание, связь с ПК, датчики) и разная мелочевка. Кроме того, в реальном устройстве микроконтроллеры стоят в панельках.
Режим «Больших цифр», для лучшей читаемости издали: