
На первый взгляд проект проще простого! Часы повесить, температуру мерить. Взял Arduino, припаял датчики, сунул в коробку — и готово.
Именно так я и сделал в 2018 году. Но из-за недостатка знаний проект хоть и работал, но до полноценного устройства ему было далеко.
Спустя годы, когда я вернулся к проекту, стало понятно: за кажущейся простотой скрываются интересные и разнообразные задачи.
Как сделать так, чтобы уличный датчик работал годы от одной батарейки?
Как добиться плавной смены цвета подсветки, чтобы она повторяла природные оттенки в течение дня?
Как совместить компактность, функциональность и прочность в одном корпусе?
И почему обычной фильтрации значений недостаточно, чтобы победить мерцание?
В этой статье — подробный разбор всех узлов, ошибки, которые я совершил, и решения, которые в итоге сработали!
Базовая станция
Принципиальная схема

Питание и управление
Устройство работает на МК Atmega328 с внешним керамическим резонатором на 16 МГц. Питание схемы — 5 В. Для модуля NRF24L01 используется отдельный линейный стабилизатор AMS1117 на 3,3 В.
Балансировка яркости RGB-светодиодов
Красный цвет кажется менее ярким из-за физиологии человеческого глаза: наша чувствительность к длинноволновой (красной) части спектра ниже, чем к средней (зеленой). К тому же красный кристалл светодиода физически часто светит слабее синего и зеленого.
Чтобы визуально выровнять яркость всех трёх цветов, на красный светодиод установлен резистор 100 Ом, а на зелёный и синий — 200 Ом. Благодаря этому красный получает больший ток и визуально уравновешивается с остальными цветами.
Ошибки проектирования
В схеме установлен 1 блокировочный конденсатор по цепям питания и 1 конденсатор на вывод AREF (источник опорного напряжения для АЦП). Конфигурация работоспособна, но, как выяснилось позднее, не соответствует правилам проектирования. Для подавления импульсных помех, создаваемых микроконтроллером при работе, на каждую пару выводов питания (VCC и GND) требуется отдельный блокировочный конденсатор. Оптимальным решением была бы установка 3 конденсаторов по цепям питания и 1 на AREF.
Ошибочно на шину I²C установлены две пары подтягивающих резисторов, тогда как для корректного формирования логической единицы достаточно одной пары.
Печатная плата
Топология (Altium Designer)


Готовая плата
��зготовлена вручную фоторезистивным методом, для защиты дорожек на плату нанесена паяльная маска, сформированная методом УФ-экспонирования через фотошаблон


Особенности разводки
На нижнем слое расположен сплошной полигон земли. В условиях ручного изготовления переходные отверстия между слоями выполнены методом пайки отрезков провода с обеих сторон платы.
В разводке предусмотрено разделение цифровой и аналоговой земли — специально для фоторезистора. Однако, как я узнал позднее, в этом не было необходимости
Почему разделение оказалось лишним?
Фоторезистор — низкочастотный резистивный датчик. В схеме делителя напряжения сигнал на входе АЦП составляет единицы вольт (например, от 0,5 до 4,5 В), а наводки от цифровых компонентов на земле — единицы милливольт. Для вольтового сигнала такая погрешность составляет доли процента
Высокое сопротивление фоторезистора (кОм-МОм) вместе с конденсатором 0,1 мкФ на входе АЦП образуют фильтр нижних частот, подавляющий высокочастотные помехи от цифровой части. Конденсатор решает две задачи одновременно: он шунтирует на землю помехи, наведенные на сигнальный провод, и принудительно удерживает вход АЦП на том же потенциале, что и земля при её скачках, превращая помеху в синфазную. Поэтому вместо разделения земель достаточно было просто добав��ть этот конденсатор.
Для сравнения: фотодиод генерирует ток единицы–сотни микроампер. Если преобразовать этот ток в напряжение с помощью резистора, полезный сигнал окажется в милливольтах — того же порядка, что и наводки на земле. В этом случае конденсатор уже не спасает, так как сигнал слишком слаб, и требуется отдельная чистая аналоговая земля.
Вывод
Фоторезистору достаточно общей земли с цифровой частью и керамического конденсатора 0,1 мкФ на входе АЦП. Разделение земель оправдано для слаботочных датчиков (фотодиоды, пьезодатчики, термопары), где полезный сигнал сопоставим с уровнем помех. В случае с фоторезистором разделение земель оказалось избыточным.
Прошивка
Борьба с мерцанием подсветки
Яркость дисплея и нижней подсветки регулируется ШИМ в зависимости от уровня освещенности. Колебания показаний фоторезистора в пределах 3–4 единиц вызваны собственным шумом АЦП и тепловым шумом фоторезистора, они напрямую передаются в управление яркостью, вызывая заметное мерцание подсветки.

Фильтрация по питанию не позволила полностью устранить эти колебания, поэтому были применены программные фильтры: медианный третьего порядка и экспоненциальный
int rawValue = analogRead(0); // Чтение сырого значения с фоторезистора int filteredValue = med.filter(rawValue); // Применение медианного фильтра 3-го порядка float smoothValue = expF.filter(filteredValue); // Применение экспоненциального фильтра int fotoresistor = map(smoothValue, 260, 1023, 0, 255); // Преобразование отфильтрованного значения fotoresistor = constrain(fotoresistor, 0, 255); analogWrite(3, 255 - fotoresistor); // Управление подсветкой дисплея через ШИМ
На приведенном ниже графике красным показаны значения после обработки медианным и экспоненциальным фильтрами, черным — после усреднения по 10 значениям. Связка двух фильтров показала себя лучше: несмотря на некоторое снижение скорости реакции на изменение освещенности, удалось полностью исключить случайные всплески, вызывавшие мерцание подсветки.

Прогноз дождя по давлению
Предсказание осадков основано на анализе изменения атмосферного давления: резкое падение — к дождю, рост — к улучшению погоды. Данную функцию я решил добавить, т.к. в 2017 году делал предсказатель погоды по проекту AlexGyver'a

Датчик BMP280 измеряет давление каждые 10 минут. Для отсеивания случайных выбросов берётся среднее арифметическое 10 последовательных измерений. Последние 6 значений давления сохраняются в массиве.
if (millis() - lasttime > 600000) { lasttime = millis(); PP = davlenie(); // Функция измерения давления и расчета среднего по 10 значениям for (byte i = 0; i < 5; i++) { pres1_array[i] = pres1_array[i + 1]; } pres1_array[5] = PP; // Последний элемент массива теперь - новое давление
По шести точкам строится аппроксимирующая прямая методом наименьших квадратов. Коэффициент наклона a показывает скорость изменения давления за один шаг (10 минут), умножая на 6 получаем суммарное изменение за час.
// Расчёт коэффициента наклона (метод наименьших квадратов) for (byte i = 0; i < 6; i++) { sumX += time_array[i]; sumY += (long)pres1_array[i]; // Приводим к long для расчётов sumX2 += time_array[i] * time_array[i]; sumXY += (long)time_array[i] * pres1_array[i]; } a = (long)6 * sumXY; a = a - (long)sumX * sumY; a = (float)a / (6 * sumX2 - sumX * sumX); delta = a * 6; // Расчёт изменения давления за час rain = map(delta, -250, 250, 100, -100);
Вероятность осадков рассчитывается по изменению давления за час delta. Диапазон -250…250 Па выбран как порог значимых погодных изменений. Итоговый показатель rain отображается в шкале от –100 до 100: +100 — резкое падение давления (дождь), –100 — резкий рост (ясно).
Контроль связи с уличным датчиком
Чтобы базовая станция не показывала устаревшие показания при потере связи с уличным датчиком, используется механизм таймаута.
При каждом успешном приёме данных фиксируется время:
if (radio.available()) { radio.read(&rxData, sizeof(rxData)); lastReceiveTime = millis(); dataIsFresh = true; }
Если данные не поступают более 60 минут, срабатывает сброс:
if (lastReceiveTime > 0 && millis() - lastReceiveTime > 3600000) { resetWirelessData(); }
Функция resetWirelessData() сбрасывает флаг свежести dataIsFresh = false. На экране это отображается прочерками вместо температуры и влажности.
Таким образом, на экране всегда отображаются либо актуальные показания, либо индикация потери связи.
Атмосферная RGB подсветка
Нижняя подсветка меняет цвет в зависимости от времени суток, имитируя красивые природные явления, пусть и происходящие не совсем в это время :)
Утро (8:00–11:00) — золотисто-желтый рассвет
День (11:00–17:00) — голубое небо
Вечер (17:00–20:00) — розовый закат
Поздний вечер (20:00-22:00) — угасающий алый закат
Ночь (23:00–8:00) — фиолетовое северное сияние

Задача состояла в том, чтобы сделать плавные, незаметные для глаза переходы между этими состояниями.
Каждому времени суток соответствует свой временной интервал, внутри которого цвет рассчитывается программно. Для плавности используется линейная интерполяция цветовых каналов RGB. В зависимости от текущего часа и минут вычисляется коэффициент прогресса — число от 0 до 1, показывающее, насколько далеко мы продвинулись от начала интервала к его концу.
float progress = (currentHour - 8 + (float)currentMinute / 60.0) / 3.0;
Некоторые интервалы для большей естественности разбиты на несколько этапов. Например, утро состоит из двух фаз: сначала появляются зеленый и синий оттенки, затем красный начинает уступать место голубому. Для каждого такого этапа используется свой внутренний коэффициент (subProgress), который также изменяется от 0 до 1, но уже в пределах данной фазы.
if (progress < 0.5) { // Первая половина утра float subProgress = progress * 2.0; r = 255; // красный постоянный g = 80 + subProgress * 90; // зеленый растет от 80 до 170 b = 0 + subProgress * 50; // синий растет от 0 до 50 } else { // Вторая половина утра float subProgress = (progress - 0.5) * 2.0; r = 255 - subProgress * 170; // красный падает от 255 до 85 g = 180 - subProgress * 50; // зеленый падает от 180 до 130 b = 60 + subProgress * 195; // синий растет от 60 до 255 } // Таким образом, желтый плавно перешел в голубой, который включается с 11:00 void dayy () { r = 85; g = 130; b = 255; }
Этот подход позволил описать сложные цветовые переходы простыми линейными формулами. В результате цвет подсветки плавно следует за временем суток, оставаясь в заданной "природной гамме".
Загрузка прошивки
Для прошивки микроконтроллера Atmega328 используется Arduino в режиме программатора. Для этого в неё предварительно загружается скетч Arduino as ISP. Затем программатор подключается к PLS-разъемам на ПП и прошивка заливается непосредственно в МК.

Корпус
Материалы: основной корпус — белый ABS, нижняя пластина и верхняя пирамидка — прозрачный PETG.


Компактность
Плата сделана по размеру дисплея и напаяна непосредственно на его выводы (конкретнее на модуль I²C)

Вентиляция датчика
Верхняя пирамидка обеспечивает свободный приток воздуха, поэтому температура и влажность измеряются в комнате, а не внутри корпуса.
Разборность конструкции
Нижняя пластина крепится двумя винтами М2 к вплавляемым гайкам в основном корпусе, а задняя крышка — четырьмя винтами М3 длиной 30 мм. Проставки между крышкой и платой прижимают дисплей к лицевой грани, фиксируя его без люфта
Подсветка
Для равномерной засветки лицевой части пластины были выбраны светодиоды с широкой диаграммой направленности (120°). Однако в процессе 3D-печати выяснилось, что решающую роль играет не только расположение диодов, но и внутренняя структура пластины.
При заполнении сотами свет рассеивался р��вномерно во все стороны, из-за чего на пластине возникали две ярко выраженные зоны засветки. Замена заполнения на хорды Архимеда перенаправила световой поток на лицевую часть, что дало более яркое и равномерное свечение. Тем не менее, полностью устранить неравномерность не удалось.
На рисунках ниже выделены наиболее яркие области для сравнения двух типов заполнения.

Благодарности
Хочу выразить благодарность Передовой инженерной школе «ЛЭТИ» и аспиранту кафедры ЭПУ Кушнаревой Олесе Андреевне за содействие и предоставленную возможность в реализации корпуса для проекта!
Выносной датчик
Принципиальная схема выносного датчика

В данной схеме блокировочные конденсаторы установлены правильно: на каждую пару выводов питания (VCC и GND) стоит отдельный керамический конденсатор 0.1 мкФ. Дополнительно на входе питания установлен керамический конденсатор 10 мкФ. Он выполняет роль накопительного конденсатора: при выходе из сна потребление резко возрастает с 23 мкА до 750 мкА, конденсатор отдаёт этот импульсный всплеск, предотвращая кратковременную просадку напряжения от батарейки.
Выбор источника питания
Поскольку датчик работает на улице в течение всего года, к источнику питания предъявляются повышенные требования. Литиевая батарейка CR123A стала оптимальным решением: при компактных размерах её ёмкость составляет 1300 мА·ч, и, что важнее всего, она стабильно работает в широком диапазоне температур от –40 °C до +60 °C. В отличие от щелочных элементов, которые на сильном морозе перестают отдавать энергию, литиевый источник сохраняет работоспособность в суровых зимних условиях.
Энергосбережение
Для максимального энергосбережения были предприняты следующие меры:
Все модули выбраны с учётом питания от 3 В, что позволило отказаться от повышающих преобразователей
Режим сна для Atmega328 выбран Power-Down. В этом режиме отключается практически всё: ядро, память, АЦП, цифровые интерфейсы (SPI, UART, I²C) и все тактовые генераторы. Активными остаются только сторожевой таймер (WDT) и логика пробуждения по внешним прерываниям.
Детектор понижения напряжения (BOD) настроен на порог 1.8 В под литиевую батарейку CR123A (номинальное напряжение 3 В).
Датчик BME280 работает в принудительном режиме (forced mode) — он просыпается, делает одно измерение и снова засыпает. Радиомодуль NRF24L01 также большую часть времени находится во сне.
Измерение потребления
При различных частотах тактирования и напряжениях питания были выполнены замеры потребления. Результаты на графиках ниже:


Как и ожидалось, минимальное потребление в активном режиме достигнуто на частоте 1 МГц. Именно в активном режиме разница наиболее заметна: снижение частоты позволяет значительно уменьшить ток потребления. В режиме же сна значения оказались практически одинаковыми для всех конфигураций, что логично — в глубоком сне тактирование большей части модулей остановлено, и частота ядра уже не играет роли.
Расчет автономности
Благодаря перечисленным мерам и тому, что устройство просыпается всего на 2 секунды каждые полчаса, удалось достичь впечатляющего результата:
Усреднив ток потребления по всему диапазону рабочих напряжений (от 3.3 В до 2.0 В), получили средние значения: 23 мкА в режиме сна и 748 мкА в активном режиме. Во сне схема проводит 99,89% времени, в активном режиме — 0,11%. При ёмкости батареи CR123A 1300 мА·ч и с учётом саморазряда 2% в год расчётное время автономной работы составляет чуть более 6 лет.
С учётом внешних факторов, таких как пониженные температуры зимой и возможное падение ёмкости со временем, можно говорить о реальном ресурсе около 5 лет.
Печатная плата выносного датчика
Топология (Altium Designer)

Готовая плата

Защита от влаги
Для защиты от осадков плата покрыта двумя слоями электроизоляционного лака Plastik 71. Чтобы лак не повредил чувствительную мембрану, отверстие датчика перед нанесением было закрыто малярным скотчем.
Испытания холодом
Тестирование схемы проводилось в морозилке при –25°C. Но при достижении –15°C связь с базой пропадала. Под подозрение попал внутренний RC-генератор Atmega328, однако замена на внешний кварц не помогла. Узнав, что все компоненты по даташиту должны работать до –40°C, я заметил странную особенность: стоило достать плату из морозилки — данные тут же приходили.
Стало понятно: проблема не в холоде, а в клетке Фарадея. Металлические стенки морозилки просто глушат сигнал, поэтому он не пробивался наружу, а снижение температуры ниже -15°C лишь добавляло ослабления радиоволнам.
Намотав пару витков провода на антенну передатчика, связь стала устойчивой при закрытой дверце и –25°C. На улице, разумеется, никаких дополнительных витков не нужно — там нет металлических стенок, образующих замкнутый контур.
Схема выдержала длительное тестирование в морозилке. Осталась последняя задача — корпус.
Экран Стивенсона
Плата передатчика спрятана в закрытый отсек и накрыта крышкой. Через отверстия в крышке выведены провода к датчику BME280, который приподнят над ней — так основная электроника остаётся в защищённом объёме, а чувствительный элемент свободно обдувается воздухом.
Первая версия корпуса оказалась недостаточно жёсткой: тонкие стенки и малые рёбра приводили к заметному изгибу конструкции, что создавало риск излома при сильном ветре или налипании мокрого снега.

По результатам прочностного расчёта в SolidWorks толщина стенок была увеличена вдвое, а ребра жесткости сделаны крупнее. Прочность заложена с запасом, поскольку модель рассчитывалась как монолитная деталь из ABS-пластика — корректно учесть реальный процент заполнения при 3D-печати в данном случае не представлялось возможным. Для наглядности на визуализации коэффициент деформации установлен равным 5, чтобы ярче показать зоны изгиба.

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

Демонстрация работы устройства
Конструкция и функционал — на видео (Rutube)
Все исходники — на GitHub
Первая версия
А начиналось всё с этого: ниже представлены фотографии самой первой версии станции, которую я собрал в 14 лет. С тех пор прошивка и все внутренние процессы были полностью переработаны — чтобы наконец воплотить ту задумку, которая была у меня изначально

Итог
Метеостанция стала следующим этапом после проекта «Копилка», ошибки в котором позволили пересмотреть подход к энергосбережению. Если раньше автономность ограничивалась 20 днями, то выносной датчик теперь работает от одной литиевой батарейки 5 лет — благодаря мерам, описанным в статье. Проект представляет собой законченную систему: от полностью автономного уличного блока до информативной базовой станции.
Если вам интересна разработка DIY-проектов со всеми разборами ошибок и техническими нюансами — подписывайтесь! Жду ваши комментарии: какие функции стоит добавить? Что можно улучшить?