Как я умный аквариум делал (backend)

image


Пролог


Работая программистом в одной из больших и успешных компаний Москвы, я не переставал совершенствовать свои навыки программирования и проходил различные курсы на платформе Udemy.
Конечно просто смотря курс и повторяя все за автором было скучновато, да и были моменты которые я не понимал ввиду своей некомпетентности на тот момент. Нужно делать свои проекты, основываясь на том, что дает автор курса — подумал я, и был конечно же прав. Только настоящие трудности и их разрешение дает вам бесценный опыт, это и есть настоящая обучение.


Обучался я в основном web программированию, поскольку и работал на том же направлении. Охватывал Full-stack разработку, поскольку решил, что нужно разбираться как в серверной части, так и во фронтовой. Учил JavaScript и различные фреймворки для бека это были Express, Appolo GraphQL (поскольку на работе был именно такой стек, да и в целом хотелось попробовать что-то отличное от REST подхода), на фронте это был все тот же Apollo GQL и Vue.


И вот немного окрепнув в этой связке, завершая один из учебных проектов, я задумался, что бы сделать интересного, где можно было бы задействовать мои знания. Эта мысль меня уже давно беспокоила, конечно многие мне советовали, для того что бы получить опыт, можно сделать с нуля свой "В контакте", "The Facebook", "Instagram" и т.д. и они были правы, действительно это бы прибавило мне опыта, но такие большие проекты я боялся не вывести в одиночку и забросить его.


Как то раз мой друг решил купить себе аквариум, тогда я думал, что это глупое занятие, что толку пялится на рыб? Подумал я тогда...


Но случилось то что должно было, друг купил себе аквариум на 15 литров и тройку рыбешек по 3см каждая. Спустя неделю друг понял, что хочет больше и конечно же свой старый он подсунул именно мне со словами "Да ладно он не большой, тебе понравится, просто поставь, пусть стоит рядом с кроватью". Но тех рыб он оставил себе. Я же пошел в ближайшей магазин и купил себе "Петушка" — Кто не в курсе погуглите, вкратце скажу, что это бойцовская рыбка, которую всегда селят в отдельном аквариуме.


Спустя время аквариумистика мне понравилась и прочитав кучу инфы, понял что мне хочется морской аквариум. Во-первых это красиво, настоящее море и кораллы у тебя дома. Во-вторых это сложно и бросает тебе вызов. Будет интересно подумал я.


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


Еще несколько лет назад я делал несложные приспособления на платформе Arduino. Было весело и интересно, было решено сделать умный аквариум на базе этой платформы, позже я поменял саму платформу.


Не переплачивай — делай сам


Выбор был сделан в пользу платы NodeMCU — это такая "Ардуина" с уже встроенным WI-FI на борту (ESP 8266), что мне и нужно было, хотя у меня и была в закромах Arduino nano и сам wi-fi модуль в отдельном исполнении, но его надо было прошивать для этого и т.д. Заказал на известном сайте в поднебесной новенькую NodeMCU, пришла быстро и работа закипела.



По старинке скачал Arduino IDE настроил ее на работу с этой платой (на Windows 10, сам драйвер встал по умолчанию), и открыл пример для подключения к WI-FI. Не знаю почему, долго я мучил ее но так и не влетело мое подключение, начал гуглить. Почитав несколько статей, узнал, что на платку можно залить прошивку для некоего языка Lua(потратив усилия и время потом уже вычитал, что еще есть Micro python, но об этом позже)


Прошив плату и открыв документацию вместе с форумом, увидел примеры кода, которые шокировали даже меня, человека который нормально относится к C++ в Arduino и знающего JS и Python(немного). И там был синтаксис которого я не хотел:


init.lua


print ( "Waiting ...")
tmr.register (0, 5000, tmr.ALARM_SINGLE, function (t) tmr.unregister (0); print ( "Starting ..."); dofile ( "main.lua") end)
tmr.start (0)

main.lua


--WiFi Settup
wifi.setmode(wifi.STATION)
local cfg={}
cfg.ssid="wifi_point_name"
cfg.pwd="point_pass"
wifi.sta.config(cfg)
cfg = nil
collectgarbage()

=wifi.ap.getip()

Пример для подключения к WI-FI был менее ужасным, но тем не менее, я не хотел разбираться в этих begin и end. Почитав документацию к esp-8266 узнал, что есть на ней некая прошивка с Питоном на борту. Точнее это было не просто обычный Питон, это было Micro python, некая обрезанная версия его, но все же это было лучше чем lua ИМХО. И я полез искать прошивки и инструменты


Скачиваем с сайта прошивку под нужную плату и прошиваем при помощи esptool прошиваем


pip install -g esptool

esptool.py --port COM3 --baud 460800 write_flash --flash_size=detect 0 esp8266-20191220-v1.12.bin

скачиваем специальную IDE EsPy для работы с "Микропитоном" и понеслась.


Для начала прочитал что на плату надо залить обязательных два файла, без которых ничего не поедет, а именно:


  • boot.py — скрипт, который загружается первым при включении платы. Обычно в него вставляют функции для инициализации модулей, подключения к Wi-Fi и запуска WebREPL;
  • main.py — основной скрипт, который запускается сразу после выполнения boot.py, в него записывается основная программа.

Далее читаю как все используют интерфейс взаимодействия WebREPL — очень схоже с тем как используют ssh подключение для работы с raspberri, но он был мне не нужен, поскольку EsPy умел общаться с платой и можно было также закачивать свеженаписанный скрипт на плату без использования esptool, хотя и там не было ничего сложного. Первый шаг — подключение к WI-FI — засунул в скрипт самый базовый и легкий код.


boot.py


import network
import time

''' Код подключения к WiFi '''
wlan_id = "my_point"
wlan_pass = "strong_pass"

wlan = network.WLAN(network.STA_IF)
wlan.active(True)

if wlan.isconnected() == False:
    wlan.connect(wlan_id, wlan_pass)
    while wlan.isconnected() == False:
        time.sleep(3)
        print("Connection Fail...")
print('Device IP:', wlan.ifconfig()[0])

В файлик main.py решил добавить запуск самого приложения, код получился простым и лаконичным, в дальнейшем добавил туда создание таски для asyncio, что бы было все асинхронно(хоть и питоновские библиотеки были сыроваты для этого).


main.py


from app import app_start

app_start()

Далее наткнулся на библиотеку MicroPyServer которая была не большой (всего один файлик), и позволял на базовом уровне создать веб-сервер с роутами(библиотека была построена на сокетах). Ну что ж напишем Хэлсчек для проверки и зальем все на плату.


app.py


from micropyserver import MicroPyServer

import ujson

server = MicroPyServer()

def send(self, **kwargs):
        '''
            Отправляем ответ
        '''
        server.send(
            ujson.dumps(kwargs),
            content_type="Content-Type: application/json",
            # Добавляем заголовки для CORS политики
            extra_headers=["Access-Control-Allow-Origin: *"]
        )
        gc.collect()

def healthcheck():
    send(success=1, healthcheck='green')

server.add_route("/healthcheck", healthcheck)

def app_start():
    server.start()

Со структурой (иерархией) проекта решил не выпентриваться, все таки памяти маловато и размещать все в отдельных папках боялся, а вдруг она закончится в самый не походящий момент. Залил все файлики прям в корень платы.


  • main.py
  • boot.py
  • app.py
  • micropyserver.py

Запускаем, видим что сервак локально поднялся и предлагает сходить на http://192.168.1.70/ и проверить. Идем в браузер, делаем запрос http://192.168.1.70/healthcheck, и о чудо — все работает, сервер отвечает



// Json from chrome browser
{
    "success": 1,
    "healthcheck": "green",
}

Далее ждал когда приедут нужные мне hardware запчасти (датчик температуры, LED-матрицы, LCD-дисплей и т.д. ), пока ждал, принялся писать бекенд дальше, все разбил на классы и начал писать. Для начала решил написать Помошника для сервера, который бы включал в себя парсер запросов и функцию отправки, и добавил Класс для работы с датчиком температуры.


app.py


from heater import Heater

# ... пропущен код

class HttpHelper:
    """
        Простой парсер GET запросов
    """
    def __init__(self):
        pass

    def parse(self, request):
        '''
            парсер строки ответа сервера
        '''
        lines = request.split("\r\n")

        result = {
            'lines': lines,
            'method': ure.search("^([A-Z]+)", lines[0]).group(1),
            'path': ure.search(
                "^[A-Z]+\\s+(/[-a-zA-Z0-9_.]*)", lines[0]
            ).group(1),
        }

        param_split = ure.sub("\/([a-z]+_?)+?\?", '', lines[0].split(" ")[1])
        result['params'] = self.get_params(param_split.split("&"))

        return result

    def get_params(self, params_as_array):
        '''
            создает словарь для query параметров
        '''
        params = {}

        for element in params_as_array:
            splited = element.split("=")

            params[splited[0]] = splited[1]

        return params

    def send(self, **kwargs):
        '''
            Отправляем ответ
        '''
        server.send(
            ujson.dumps(kwargs),
            content_type="Content-Type: application/json",
            extra_headers=["Access-Control-Allow-Origin: *"]
        )
        gc.collect()

# ... пропущен код

def get_water_temperature_C(request):
    '''
        Отдает температуру в Цельсиях
    '''
    try:
        water_heater.get_water_tmp_C()

        http_helper.send(
            success=1,
            water_temperature_c=water_heater.water_tmp
        )

    except Exception as e:
        print(e)
        http_helper.send(success=0, error=e)

# ... пропущен код

server.add_route("/healthcheck", healthcheck)
server.add_route("/get_water_tmp", get_water_temperature_C)

# ... пропущен код

heater.py


import machine
import onewire
import ds18x20

HEATER_PIN = 2

class Heater:
    def __init__(self):
        self.water_tmp = 0.00
        self.heater_pin = machine.Pin(HEATER_PIN)
        self.sensor = ds18x20.DS18X20(onewire.OneWire(self.heater_pin))

    def get_water_tmp_C(self):
        rows = self.sensor.scan()

        self.sensor.convert_temp()

        for rom in rows:
            self.water_tmp = self.sensor.read_temp(rom)

        return self.water_tmp

LED я ждал больше всего и хотелось поскорей уже их прикрутить к проекту и все проверить. Основу для размещения я взял достаточно большой корпус от видеокарты, с алюминиевым радиатором. По моим расчетам туда встанут 5 матриц:



  • белый 2 штуки
  • зеленый 1 штука
  • красный 1 штука
  • синий 1 штука

Все они питались от 32 вольт, взял блок питания для термотринтеров (24V) и повышающий преобразователь. Посоветовавшись со знающим другом, принял решение подключать все их через микросхему, которая называется в простонародье "составной транзистор Дарлингтона", понравилось, что все на одной небольшой микросхеме и не нужно париться с радиаторами охлаждения, если бы я выбрал биполярные транзисторы. Все приехало, все спаял и прикрутил к радиатору. Светодиоды не стал промазывать термо-пастой, так как знал, что на все 100% они не будут гореть и тепло будет хорошо отводится и так. Получилось все конечно не на продажу, но в целом меня внешний вид пока устраивал.


Написал класс для них, и добавил роуты для работы через API.


Прицепил еще LCD дисплей, что бы видеть на каком IP стартанул сервер, поскольку на тот момент еще не было реализовано статичный ip для этого. Ну и после этого пошел писать frontend для того, что бы можно было с любого устройства смотреть инфу и управлять аквариумом.



Эпилог


В целом получил хороший опыт в разработке IOT устройства, работа над которым еще не завершена и будет продолжаться. В процессе пришлось немного подкорректировать работу библиотеки MicroPyServer, а именно объем передаваемых данных в сокете. Поскольку я пришел к тому, что при нескольких запросах в течении небольшого промежутка, память просто заканчивалась и контроллер падал с ошибкой, пока не придумал как ее можно отловить в try except, но это надо будет сделать.


micropyserver.py


# ... пропущенный код

def _get_request(self):
        """ Return request body """
        # было выставлено 4096 , при этом быстро возникает MemoryError
        return str(self._connect.recv(1024), "utf8")

В планах:


  • Автоматизировать работу с освещение, что бы он сам проверял саму освещенность в комнате + создавал эффект заката и рассвета, а ночью — лунный свет.
  • Автоматизировать анализ воды
  • Автоматизировать нагрев воды
  • Автоматизировать помпу течения
  • Автокормушка
  • Автодолив
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 39

    0
    Плюсануть не могу, но цель интересная. Жаль конечно, что ардуино опять, хотелось бы хардкор на STM32, ну да ладно, можно будет самим адаптировать. А часть неэлектронную и программистскую вы где-то опишете? Видел как-то раз морской аквариум (не ржать, океанариумов прошёл не мало, и да, кстати по мне так Питерский круче чем в Барселоне). Зацепило тоже.
      +3
      STM32 — это не хардкор:)
        0
        ок, хотя бы слабенький хардкор с юсб на регистрах с STM32, без куба
        :)
        0
        Добрый день, я вроде бы все основную идею описал, просто скидывать весь код с комментариями в статью, считаю лишним, тут вроде и так идея понятно, веб сервер который на всякие роуты запускает нужные функции. в скором будет вторая часть с `Front-end` там я еще немного расскажу про всю связку целиком.
          +1
          Arduino IDE конечно "фу", но вот не использовать библиотеки которые написаны для ESP, а изобретать велосипед было бы глупо.
          Но выход есть: под ESP можно писать используя Eclipse, при этом все библиотеки будут доступны.

          STM32 не вариант если нужен WiFi.
            0

            Почему не вариант если нужен Wi-Fi?

              0
              Назовите хоть один STM32 со встроенным WiFi? А брать STM32+ESP это извращение если всё можно сделать только на ESP.
                0

                Ну интегрированных решений нет, но есть Wi-Fi-модули.

                  0

                  Их надо прошивать, я же написал в начале статье, что у меня была ардуина свободная и отдельный wi-fi модуль.

                  0

                  ESP2866 ненадежный чип. Если задача не критичная, то можно и попытаться. А если нужна надежность, использовать mcu хорошее решение. Вачдоги хардварные тоже не лишними будут.


                  Плюс, у esp8266 весьма мало gpio.


                  А вообще, решений подобных уже пачка. Сам делал решение, которое уже годами успешно работает.


                  https://m.habr.com/ru/post/474816/ в статье есть схема.

                    0

                    Вот спасибо Мил человек ) почитаю обязательно, кстати что касается пинов, есть еще же ESP32 там и пинов больше и памяти и сам проц помощнее )

                      0

                      Да. Этот чип гораздо бодрее.

                      0
                      да как-то контроллером с передачей данных у вас там все еще выступает esp8266, разве что остальные функции частично другим отдали.

                      Контроллер нравится почти как ардуина, но не нравится, что прошивка состоит из бинарника от авторов, на который клеится ваш скомпиленный бинарник, который дергает функции из первого, но не получает хоть какого-то прямого доступа к железу. И что там в этом бинарнике от авторов понаверчено — никто пока с исходниками не показал.

                      А чтобы вифи, по смешной цене и не esp8266?
                        0
                        какой интересный светильник, а на него схема есть?
                +2
                Делали такое в далеком 2015 с Speccyfan — спросите, может чего интересно будет. Тоже ESP8266 под капотом, Atmega как watchdog, серверный бэк на питоне и на сях прошивка — весь этот микропитон как и луа только пустая трата времени — проверено.
                  –2

                  Мне кажется тут смотря что вы хотите сделать, ЯП не так важен, если ты знаешь что ты хочешь. Для моих нужд, Микропитон оправдал себя полностью.

                    +2
                    язык нет, а текущая выжираемая память и периодические зависания играют. Хотя честно говоря, глядя на код — мне кажется и с питоном вы не очень
                      0

                      Все упирается в опыт, у меня он не очень большой. Написал так, как мог) Ваше право написать, что именно вам не нравится в коде.

                  –1

                  А почему вдруг не подойдут RGB диоды?
                  Почему они должны всегда все светить?
                  Главное — нужно равномерное освещение определённой мощности, а все диоды горят или часть из них — абсолютно все равно. Как раз RGB отлично подходят для такой лампы. Берётся лента с пиксельной адресацией, режется на равные отрезки, соединяется, клеится на алюминиевую основу и все — может эмулировать любые погодные условия, даже облачность с молниями.
                  Мало того, хорошие дорогие брэндовые лампы для аквариумов так и устроены.


                  Самое главное, что для управления всем этим счастьем нужно всего 1 вывод. Причём WiFi здесь вообще не к месту, но если очень хочется, то можно все сделать на Wemos ESP, без связок с ардуиной и тд.


                  Ну и на заметку — готовая такая лампа, в нормальном корпусе, с креплениями, бп и пультом стоит на али от 25$.

                    0

                    Да, ты прав, можно было сделать и при помощи адресной ленты.


                    Wifi мне тут нужен для того что бы сделать веб сервер, позднее будет статья как я фронтенд делал для этого.


                    Как я написал в прологе, я все это делал для того что бы получить опыт и интересно провести время, потому я не купил готовую лампу.

                      0
                      Что за лампа такая? Она действительно подходит для аквариумов или там взяли кусок рандомной ленты и на алюминий наклеили?
                      Спрашиваю не из праздного интереса — сам использую спектральные ЛДС, светодиоды еще не пробовал, но было бы интересно на будущий аквас попробовать.
                        0

                        Нет, совершенно обычные светодиоды. В температурных показателях света уже не помню… Белый это нейтральный ( не теплый, не холодный). А остальные как есть красный синий и зелёный… Возможно в дальнейшем попробую так называемые grow led, которые используют для освещения растения. Имеют такой фиолетовый свет.

                          0

                          Спектр светодиодов очень важен. Смотря что будете садить в аквариум. Жесткие кораллы потребуют хорошей доли ултрафиолета в спектре.
                          Красный свет запрещен. Зацветет. В любом случае нужен синий цвет однозначно и белый. Синий для плавного перехода от ночи к дню чтоб рыбы не стрессовали. Впринцтпе ночевать может и со слабым синим.

                            0

                            Так и есть, основные это белые и синий, остальные включаю только пока для красоты. Пока сама банку пресноводная, не цветет вроде.

                      0

                      На данный момент это пока пресноводный аквариум. Одна скалярия и три неоновых рыбки. Как только сделаю MVP (Минимальный жизнеспособный продукт: minimum viable product). Буду запускать морскую банку на 50 литров.

                        0

                        А какая живность в аквариуме?

                          0

                          См. коммент выше

                          +2

                          2 кружочка нарисовано, осталось нарисовать остальную сову ;) Унылая математика, pid-регуляторы, датчики… Переходите сразу на С++; если для какого-то из датчиков придется делать ногодрыг — лучше это делать на более низком уровне.

                            0
                            А каким в будущем будет управление?
                            Голосовое или бота в телеграмме под этот проект сделаете?)
                              +1

                              Будет автономное все. Но для наблюдения и ручной корректировки я сделал front-end, статья скоро выйдет

                                0

                                Лучше бы сразу смотреть в сторону mqtt например.

                                  0

                                  На сокетах у меня будет умный дом. Возможно и аквариум будет в этой экосистеме.

                              0

                              Питон для такого не лучшее решение, но как хобби-проект сгодится.
                              Не слишком мощное освещение для такого малого объема?

                                0

                                Я на все 100% не использую, в среднем днем белые стоят на 18-25% остальные по 5-10%, ночью и того меньше, горит только синий на 1-3%, запас как говорится есть и хорошо, возможно в будущем перейду на большую банку.

                                +1
                                Есть прекрасная альтернатива Arduino IDE PlatformIO IDE for VSCode
                                  0
                                  Автору конечно респект, но вот не понятно что конкретно получилось автоматизировать. Дело в том, что я почти собрал «умный аквариум» на arduino nano, но с bluetooth-ом и управлением с помощью смартфона. Но пока управляется только свет. Вот и хочется узнать про автоматизацию больше, чем просто включать/отключать свет по расписанию.
                                    0
                                    Свет для пресноводного и морского аквариума — это две разные вселенные.
                                    Особенно если хочется выращивать кораллы. Погуглите настройки популярной лампы AI Prime к примеру, там 7 разных спектров контролируется. Даже в дешевой китайской лампе будет минимум 4 вида светодиодов. Кораллы очень требовательны к свету. Еще дело в эстетике, в нано рифе лучше всего смотрятся флуоресцентные кораллы. А сделать такое освещение чтобы они и светились и чтобы было не вырвиглазно смотрелось как раз поможет светильник с гибкими настройками по спектрам.
                                    Автору респект в стараниях.
                                    Но имхо лампа в рифовом аквариуме — компонент очень важный, и неправильный выбор может привести ко многим проблемам как и с кораллами, рыбами, водорослями, планктоном.
                                    Плюс морская живность гораздо дороже, и рисковать ей не так хочется.
                                    В пресном все проще — ну максимум водорослями зарастет.

                                      0
                                      А в морском разве есть водоросли?
                                        0
                                        к сожалению да, в «домашних» морях может быть более десятка видов. От самых простых в виде налета на стекле, до всяких нитчатых, слизи, в виде пузырьков. Одни из самых неприятных — динофлагелляты, они могут выделять токсины, ночной кошмар «моряков». У меня из-за их вспышки умерли все рыбы и крабы отшельники. А все потому что аквариум был не стабильный, и я как раз экспериментировал с освещением.

                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                    Самое читаемое