Blog
Метеостанция

Есть вещи которые заставляют мое сердце биться чаще, делать более томным дыхание, а подушечки пальцев потеть - это микроконтроллеры. Те возможности, которые они дают обычному человеку, поистине безграничны, а их доступность - как бальзам на душу.
В этой статье хочу поделится проделанной работой, и камнями через которые пришлось споткнутся, прежде чем получить рабочую версию устройства.
Все материалы будут доступны на GitHub, там же будет и исходный код для прошивки микроконтроллеров, в котором не сложно разобраться (ты ж программист).

Web interface

Прежде чем писать о железе и коде, думаю стоит показать результат работы того, что описано ниже.
Идея сделать подобное устройство заключается в удобном градуснике, который можно смотреть с мобильного телефона, и как результат - для этого написан простой веб интерфейс: weather.tedor.kr.ua
Так же на него можно попасть нажав по вкладке Weather на этом сайте.

Для работы интерфейса и сохранения данных с датчиков есть следующие API запросы:

  • /api/get_summary - получение последних данных с датчиков
  • /api/get_summary_by_12hours - получение статистики за 12 часов
  • /api/add - добавление данных (запрос выполняется каждые 5 мин) куда передается информация о температуре, влажности и давлении.

Исходники можно посмотреть тут: weather-station-frontend

Грабли

Перед тем как описывать реализацию устройств, хочу начать с описания проблем с которыми прошлось столкнутся.
На самом деле, первую версию устройства я сделал уже давно, она имела большой ряд недостатков, и толком не работала.
Основная проблема была в том что внешний датчик работает на батарейках, а это значит что после чтения показаний микроконтроллер должен уходить в глубокий сон, попутно отключая всю периферию для энергосбережения. Естественно ничего этого не было сделано, и как результат - датчик работал около 5 дней от батареек facepalm.jpg

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

Следующим валуном под ногами было отсутсвие возможности использовать модуль arduino pro mini для датчика, поскольку они бывают 2-х видов - на 16 и 8 мГц. Если полезть в datasheet atmega 328P то можно увидеть, что на частоте 8 мГц Atmel гарантирует устойчивую работу камня где-то на уровне 2.5 вольт, вот картинка:

atmel-power-graph.png

Так же в стандартной конфигурации arduino pro mini 3.3V включен Brown-out Detector на 2.7 вольта, который выключит камень при достижении этого порога, а это недопустимо при питании от батареек, где диапазон напряжений должен составлять как минимум от 3-х до 2-х вольт.
Из всего выше сказанного следует - мы не можем использовать готовый модуль arduino потому как минимальная доступная частота для нашей задачи 4 мГц.

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

Input

Основная идея реализации метеостанции заключалась во внешнем устройстве, которое стоит на улице, снимает показание с датчиков, и по радиоканалу передает информацию на приемник, дальше эти данные передаются на компьютер, он в свою очередь сохраняет их локально в БД и отсылает на удаленный сервер.
Для реализации этой затеи была выбрана Arduino платформа как со стороны приемника, где используется готовый модуль, так и стороны передатчика, где используется прошивка и библиотека для работы с Atmega 328P камнем.

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

Итак, есть 2 блока, внутренний и внешний.
Внутренний по сути - приемник, который по радиоканалу принимает данные с метеостанции и через com->usb конвертер передает их на компьютер, он состоит из:

  • Arduino pro mini 328 (3.3v 8Mhz) - микроконтроллер - 4 $
  • SI4432 - радиомодуль - 2.8 $
  • USB To RS232 TTL UART PL2303HX - usb -> com порт конвертер - 1.9 $
  • Корпус - на Ebay выглядит так

Внешний модуль, это наша метеостанция, которая снимает 3 показания - абсолютное давление, температуру и влажность. Для реализации использовались следующие детали:

  • SI4432 - радиомодуль - 2.8 $
  • DHT22/AM2302 - сенсор температуры и влажности - 4.66 $
  • BOSCH BMP085 - сенсор абсолютного давления - 3.45 $
  • Коробочка для батареек - 1$
  • Бокс для компоновки деталей - у меня уже года 2 валялся дома

А теперь более детально.

Внутренний блок

Его задача принимать информацию. Для этого у него есть SI4432 радоимодуль, он постоянно включен и слушает раиоканал, когда появились новые данные, они через com -> usb конвертер передаются на сервер.
Никаких других операций он не делает и есть по сути роутером.
Питается от USB конвертера от 3.3v.
Схему подключения не привожу, все что нужно сделать это соединить радиомодуль с Arduino с одной стороны и подключить usb -> com конвертер с другой, будет вполне достаточно описать весь процесс на словах.
Для его работы нужно правильно подключить SI4432 радиомодуль по SPI интерфейсу, инструкцию по подключению можно найти на страничке библиотеки работы с этим модулем RF22 в разделе "Connecting RFM-22 to Arduino".
Алгоритм работы устройства такой:

  • В бесконечном цикле ждем приема новых данных, первое что мы должны получить - секретный ключ, который говорит нам что наше устройство начинает передавать данные
  • Когда секретный ключ получили и сравнили, ждем непосредственно данные с датчиков
  • После получения информации с датчиков, высылаем в ответ "OK" сообщение

Исходники можно посмотреть тут: Receiver

Решил все смонтировать на маленькой макетной плате, так как не вижу смысла делать печатную плату для такой простой схемы.
Вот фотографии как это выглядит:

DSC_6641.JPG DSC_6759.JPG

Client демон

Все данные, которые принимает внутренний блок, передаются на компьютер где запущен демон.
Его задача заключается в сохранении данных в БД, а так же передача их на удаленный сервер через API /api/add запрос, о котором упоминалось выше.

Исходники можно посмотреть тут: Client

Для конфигурирования демона есть файл config_default.py, его нужно скопировать в config.py и сконфигурить:

  • com порт к которому подключен приемник
  • подключение к БД
  • путь к удаленному серверу, который будет принимать данные через API запросы

Для запуска демона достаточно написать команду:

python daemon.py

Для запуска демона вместе с сервером я использовал supervisor, ниже пример его конфигурации:

 [program:arduino-weather-station]
 command=/usr/local/bin/python /usr/local/www/arduino-weather-station/client/daemon.py   
 directory=/usr/local/www/arduino-weather-station/client   
 pidfile=/var/run/uwsgi-arduino-weather-station.pid   
 autostart=true   
 autorestart=true
 redirect_stderr=true
 stdout_logfile=/var/log/supervisor/arduino-weather-station.log
 stdout_logfile_maxbytes=50MB
 stdout_logfile_backups=50
 stdout_capture_maxbytes=1MB
 stdout_events_enabled=false
 loglevel=warn

Внешний блок

Внешний блок работает от батареек, основную часть времени спит, раз в 5 мин просыпается, снимает данные с датчиков и передает их по радиоканалу, потом снова засыпает - все просто.
Если более детально, то логика работа такая:

  1. Для снятия показаний с модуля DHT22, на него нужно подать питание, и после истечения 2 сек времени начать считывать данные. Поэтому первый этап - микроконтроллер просыпается, подает питание на DHT22 и BMP085 модули и засыпает на 2 сек
  2. Дальше снова просыпаемся, считываем показания с модулей
  3. Подаем питание на SI4432 радиомодуль
  4. Отправляем по радиоканалу секретный ключ
  5. Потом отправляем сгрупированные данные с модулей, примерно в таком виде "20|t:3.6,p:738,h:99.9,bv:2.56", где:
    • 20 - номер устройства
    • t:3.6 - температура
    • p:738 - давление в мм.рт.ст.
    • h:99.9 - влажность
    • bv:2.56 - напряжение на микроконтроллере
  6. Дальше ждем "OK" сообщение от удаленного приемника
  7. Если получаем в ответ "OK", тогда засыпаем на 5 мин, при этом сняв питание со всей периферии
  8. Если "OK" не получен, то делаем еще 5 попыток, начиная с пункта 4, после чего засыпаем, сняв питание со всей периферии

Исходники можно посмотреть тут: Transmitter

Вот как это все выглядит:

DSC_6654.JPG DSC_6750.JPG
DSC_6747.JPG DSC_6754.JPG

Внешний блок: принципиальная схема

Как уже говорилось выше, для работы устройства был использован Atmega 328P камень, который на борту имеет 32 Кб Flash памяти для прошивки, SPI интерфейс для работы с SI4432, I2C шину для работы с BMP085 модулем. Для работы с DHT22 модулем используется прямое подключение в цифровому выходу микроконтроллера.
Микроконтроллер я взял в TQFP32 корпусе.
Для питания устройства используется 3 батарейки, с которых снимается 3 и 4.5 вольтовое питание.
3 вольтами питаются: микроконтроллер, SI4432 радиомодуль, BMP085 датчик давления.
4.5 вольтами питается DHT22 датчик влажности и температуры.
Когда микроконтроллер засыпает, то он перед этим отключает питание всех модулей для экономии энергии батареек, соотвественно модули подключены следующим образом:

  • BMP085 - подключен напрямую к ноге микроконтроллера, перед засыпанием мы эту ногу переводим в режим INPUT, таким образом снимая с него питание
  • DHT22 - поскольку модуль работает от 4.5 вольтовой линии, мы не можем питать его от ноги напрямую, для этого он подключен через транзистор, который работает в ключевом режиме, а базой транзистора управляет та же нога что и модулем BMP085
  • SI4432 - модуль в режиме передачи данных потребляет больше 30 мА, а на выводы микроконтроллера мы не можем нагружать устройства с потреблением больше 20 мА, соответсвенно этот раиомодуль так же подключен к питанию через транзистор, базой которого мы управляем.

В схеме я использовал NPN КТ305В транзисторы, только потому что они были под рукой, так что для этих целей можно использовать любой другой транзистор со схожими характеристиками.
Так же в схеме на 3-х вольтовой линии стоит диод Шоттки, для предотвращение обратного тока на 4.5 вольтовой линии, когда устройство засыпает.

Собственно вот сама схема:

schem.png

И печатная плата для этой схемы:

layout.png

Более детально печатную плату и принципиальную схему можно рассмотреть в программах Sprint Layout и Kicad соответственно, сами файлы можно найти на том же GitHub в папке info.

Внешний блок: прошивка

Для работы устройства я использую Arduino Bootloader по той причине, что обновление прошивки через UART интерфейс довольно удобно, но поскольку внешний модуль работает не на Arduino, а на чистом Atmega камне, первое что нужно сделать - прошить Bootloader. Поскольку готовые Bootloader-ы в проектре Arduino для микроконтроллера Atmega 328P есть только на 8 и 16 мГц версии, то необходимо скомпилировать свой Bootloader.
Ниже приведена инструкция как это сделать.
Для начала ставим Arduino IDE, после находим каталог с прошивками Bootloader-ов, он имеет примерно такой путь:

<Arduino IDE>/hardware/arduino/bootloaders/atmega/

Для компьютеров с Mac OS на борту, он должен выглядеть примерно так:

/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/bootloaders/atmega/

Заходим в этот каталог, и в Makefile добавляем следующие строчки:

atmega328_4_tedor: TARGET = atmega328_tedor_4MHz
atmega328_4_tedor: MCU_TARGET = atmega328p
atmega328_4_tedor: CFLAGS += '-DMAX_TIME_COUNT=F_CPU>>4' '-DNUM_LED_FLASHES=1' -DBAUD_RATE=19200 -DDOUBLE_SPEED
atmega328_4_tedor: AVR_FREQ = 4000000L
atmega328_4_tedor: LDSECTION = --section-start=.text=0x7800
atmega328_4_tedor: $(PROGRAM)_atmega328_tedor_4MHz.hex

В этом же файле указываем прямой путь к компилятору, что бы все заработало:

CC         = /Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin/avr-gcc

Запускаем компиляцию нашего Bootloader-а на 4 мГц:

/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin/make atmega328_4_tedor

Так же что бы не танцевать с бубном, как описано выше, можно скачать готовый Bootloader.

Теперь нужно поправить файлик boards.txt, где добавим конфигруцию для нашей новой прошивки, файл должен находится по пути:

/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/boards.txt

Конфигурация выглядит так:

tedorPro328.name=ATmega328 (1.8-5.5V, 4 MHz) Brown-out detector OFF

tedorPro328.upload.protocol=arduino
tedorPro328.upload.maximum_size=30720
tedorPro328.upload.speed=19200

tedorPro328.bootloader.low_fuses=0xFD
tedorPro328.bootloader.high_fuses=0xDA
tedorPro328.bootloader.extended_fuses=0x07
tedorPro328.bootloader.path=atmega
tedorPro328.bootloader.file=ATmegaBOOT_168_atmega328_tedor_4MHz.hex
tedorPro328.bootloader.unlock_bits=0x3F
tedorPro328.bootloader.lock_bits=0x0F

tedorPro328.build.mcu=atmega328p
tedorPro328.build.f_cpu=4000000L
tedorPro328.build.core=arduino:arduino
tedorPro328.build.variant=arduino:standard

Опишу немного подробнее конфигурацию выше. Для подсчета FUSE bits использовал этот калькулятор, за основу была взята стандартная Arduino конфигурация FUSE bits, и изменено использование внешнего кварца с 8 мГц на диапазон кварцев с 3 до 8 мГц, так же был отключен Brown-out detector. И это все изменения.
Так же на частоте 4 мГц у меня не получилось прошивать со скоростью выше 19200, поэтому выставлена именно такая скорость, как наиболее стабильная.

В принципе это все, дальше ваша задача состоит в том что бы залить эту прошивку в микроконтроллер, я для этого использовал USPAsp программатор, и на время прошивки припаивал напрямую к ногам микроконтроллера провода. Так как это делается один раз, а дальнейшая прошивка будет происходить через UART интерфейс, то необходимости выводить отдельный интерфейс для прошивки Bootloader-а я не видел смысла. Единственный момент - если вы решите прошивать Atmega на печатной плате, то делайте это до того как впаяете SI4432 радиомодуль, иначе большая вероятность его спалить, поскольку USBAsp программатор работает на 5 вольтовом питании.

Заключение

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

  • 4.2 мкА (!) по 3 вольтовой линии
  • 6 мкА по 4.5 вольтовой линии

Честно говоря - результаты впечатляющие.
В режиме чтения данных микроконтроллер потребляет примерно 5 мА, а в режиме передачи - больше 30 мА, но очень короткий промежуток времени.
Так что я ожидаю, что внешний датчик будет работать несколько лет на одних и тех же батарейках.

И немного о том, что бы я поменял. Самая большая проблема это 4.5 вольтовая линия, на которой у меня не получилось убрать потребление 6 мкА тока в режиме сна, из-за того что на этой линии параллельно плюсу стоит резистор на 10 кОм, его нельзя убирать, потому что без него может перестать работать датчик DHT22, если сгорит подтягивающий резистор внутри микроконтроллера.
Так же на входе 3 вольтовой линии мне пришлось поставить диод Шоттки для ограничения обратного тока в период сна микроконтроллера, без него DHT22 потребляет 180 мкА на 4.5 вольтовой линии. Но из-за него на входе питания устройства падает 0.3 вольта, и так как минимальное напряжение работы микроконтроллера является 1.8 вольт, это значит что при достижении 2.1 вольт на батарейках - устройство перестанет работать, и как результат мы не сможем посадить батарейки в полный ноль.
Я заменил бы DHT22 на HTU21D модуль, который работает в диапазоне 1.8 - 3.6 вольт и стоит в пределах 4$. Он даст возможность питать все датчики от двух батареек, и выкинуть диод Шоттки из схемы.

В конечном счете приемник и передатчик выглядит примерно так:

DSC_6763.JPG

Вот в принципе и все, спасибо за внимание, всем хорошего дня.