Собираем
Сам Electric Imp на момент покупки стоил $29.95, но чтобы начать им пользоваться придется в добавок приобрести плату, которую оценили в $19.95 (сейчас цена уменьшилась до $12.95).
После прибытия комплекта вместо привычного “Hello, world!” моргания светодиодами решил сделать что-то более полезное. Немного пораскинув мозгами и просмотрев примеры остановился на датчике температуры. Порылся в закромах и нашел термистор 10КОм и DS18B20. С DS18B20 не сложилось так как в текущем API для Electric Imp отсутствует поддержка OneWire.
Схема подключения термистора очень простая
Схема подключения термистора (источник)
Чтобы избавится от проводов, подключил аккумулятор на 750mAh, при этом на самой плате нужно джампером выставить питание от батареи. Получилось очень компактное устройство.
Для визуального отображения температуры взял валявшийся без дела Raspberry Pi и 16х2 LCD индикатор из какого-то китайского набора.
Схема подключения LCD 16×2 индикатора к Raspberry Pi (источник)
Пришлось немного помучаться потому как мне попалась какая-то промежуточная ревизия малины и код с ходу не завелся. Оказалось что на плате Raspberry Pi поменяли назначение нескольких ног, одна из них как раз нужна была для подключения LCD.
Программируем
Код для работы с термистором довольно простой, все замечательно работает и результат можно наблюдать в планере на сайте electricimp. Теперь нужно отсылать результат туда, куда мне нужно, благо в API есть возможность слать HTTP запросы, но тут есть свои нюансы. Первым делом я попытался слать результат внутри своей локальной сети, и ничего не получил. После разбора полетов выяснилось что Imp запрос шлет не непосредственно, а через свой сервер. Выхода два, или пробрасывать порт на роутере внутрь локальной сети, или слать на свой внешний сервер, а локальным сервером периодически затягивать оттуда последние показания. Проброс портов накладывает определенные ограничения на использование, поэтому я решил поднять сервер с простеньким API на App Engine, попутно заставив его рисовать графики, что оказалось очень кстати.
#!/usr/bin/env python import webapp2 import json import logging import utils import time import os import datetime from google.appengine.ext.webapp import template from google.appengine.ext import db class Sensor(db.Model): temperature = db.FloatProperty(required = True) battery = db.FloatProperty(required = True) added = db.DateTimeProperty(auto_now_add = True, indexed=True) class SensorRequestHandler(webapp2.RequestHandler): def post(self): data = json.loads(self.request.body) params = json.loads(data['value']) temp = params['temp'] battery = params['battery'] sensor = Sensor(temperature = temp, battery = battery) sensor.put() self.response.out.write('OK') def get(self): sensors_data = Sensor.all().order('added').fetch(None) temperature_data = [] battery_data = [] for item in sensors_data: temperature_data.append([int(time.mktime(item.added.timetuple()))*1000 ,round(item.temperature, 1)]) battery_data.append([int(time.mktime(item.added.timetuple()))*1000, round(item.battery, 2)]) path = os.path.join(os.path.dirname(__file__), 'templates/charts.html') self.response.out.write(template.render(path, { 'temperature_data' : utils.GqlEncoder().encode(temperature_data), 'battery_data' : utils.GqlEncoder().encode(battery_data) })) class LastRequestHandler(webapp2.RequestHandler): def get(self): ordered_list = db.GqlQuery('select * from Sensor order by added desc limit 1') last = ordered_list.get() self.response.headers['Content-Type'] = 'application/json' self.response.out.write(utils.GqlEncoder().encode(last)) class CleanRequestHandler(webapp2.RequestHandler): def get(self, bulk = 'old'): logging.debug("bulk: %s", bulk) try: while True: q = Sensor.all() if bulk != 'all': q.filter('added <', datetime.date.today() - datetime.timedelta(days=60)) assert q.count() db.delete(q.fetch(200)) time.sleep(0.5) except Exception, e: self.response.out.write(repr(e)+'\n') pass app = webapp2.WSGIApplication([ ('/sensor', SensorRequestHandler), ('/sensor/last', LastRequestHandler), ('/sensor/clean/?(all)?', CleanRequestHandler) ], debug=True)
В итоге результат выглядит так:
— Imp раз в 10 мин шлет на внешний сервер показания температуры
— Сервер сохраняет это значение
— Raspberry Pi раз в несколько минут подтягивает показания температуры и отображает ее на LCD дисплее.
Параллельно с температурой решил собирать показания напряжения батареи, но оказалось что функция из API возвращает напряжение с самой платы (~ 3.25В).
К сожалению текущие возможности выполнения HTTP запроса сильно ограничены, поэтому пришлось извратиться и паковать данные в JSON, который в свою очередь второй раз пакуется в JSON уже внутри HTTPRequest ноды.
class Termistor { pin_num = null; constructor(pin){ pin_num = pin hardware["pin" + pin_num].configure(ANALOG_IN); } function read(){ return hardware["pin" + pin_num].read(); } function getTemperature(){ local temp = math.log(((655350000/read()) - 10000)); temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * temp * temp ))* temp ); temp = temp - 273.15; return temp; } } local sensor = Termistor(9); local output = OutputPort("Temperature", "string"); imp.configure("Termistor 10K", [], [output]); function capture(){ imp.wakeup(600.0, capture); local temp = sensor.getTemperature(); local jsonOut = "{\"temp\":"+temp+", \"battery\":"+hardware.voltage()+"}"; output.set(jsonOut); server.show(format("%1.1fºC", temp)); } capture(); imp.sleep(2.0) server.sleepfor(600.0)
Пожинаем плоды
Графики стабильно строятся, выглядят так:
Raspberry Pi выводит температуру
Для более наглядной демонстрации снял небольшое видео (рекомендую смотреть в 720p):
Энергосбережение
Imp может запускаться в трех режимах
Отключенный powersave mode (по умолчанию)
В этом режиме WiFi модуль всегда включен. Это способствует низким сетевым зажержкам но приводит к высокому потреблению тока ~60-80мА для 3.3В
Включенный powersave mode
В данном режиме потребление падает до 5мА в моменты, когда нет передачи по WiFi, но соответственно увеличивается задержка перед передачей и она может быть больше 250мс.
Глубокий сон
В этом режиме Imp отключается от WiFi сети, перестает выполнять код, теряет контекст (кроме данных в специальной nv таблице) и переходит в режим очень низкого электропотребления (~6мкА). Вывести из сна может либо предварительно взведенный таймер, или wake-up пин. Старт и подключение к WiFi сети занимает около 2с.
Должен признать что опыты с powersave режимом у меня закончились неудачей. Что с включенным режимом, что с выключенным Imp у меня одиноково жил трое суток. В принципе можно прийти к выводу что по какой-то причине этот самый powersave режим у меня был всегда включен потому как среднее потребление вышло
750/24*3 ~ 10 мА, что соответствует включенному powersave режиму.
В режиме глубокого сна в качестве датчика температуры Imp трудится уже месяц, и напряжение упало с 4.2 до 3.68 что не может не радовать.
Планы
Добавить честный измеритель напряжения батареи (что особо актуально для LiPo батарей), прикрутить солнечную батарею и, пока особых планов нет, использовать как местный датчик температуры в одном из энтузиастских погодных проектов. Также возможно добавлю счетчик Гейгера.
Вывод
Устройство вышло очень интересным и перспективным, но на данный момент сильно ограничивает область применения жесткая привязка к собственным серверам Electric Imp и скудность API. Если предположить что эти недостатки будут исправлены, то Imp идеально подходит для применения совместно с различными сенсорами в системах умного дома.
Полезные ссылки
1. Исходный код на github
1. Arduino + termistor
2. Electric Imp Wiki
3. Drive a 16×2 LCD with the Raspberry Pi
ссылка на оригинал статьи http://habrahabr.ru/post/164105/
Добавить комментарий