How I did a smart aquarium (backend)

image



Prologue



Working as a programmer in one of the large and successful companies in Moscow, I never stopped improving my programming skills and took various courses on the Udemy platform.

Of course, just watching the course and repeating everything after the author was boring, and there were moments that I did not understand due to my incompetence at that time. You need to make your projects based on what the author of the course gives - I thought, and of course I was right. Only real difficulties and their resolution give you invaluable experience, this is real learning.



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, .


WebREPLssh 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")


:



  • Automate work with lighting, so that he himself checks the very illumination in the room + creates the effect of sunset and dawn, and at night - moonlight.
  • Automate water analysis
  • Automate water heating
  • Automate flow pump
  • Auto feeder
  • Autofill



All Articles