Nyambungkeun méter cai ka imah pinter

Jaman baheula, sistem otomatisasi bumi, atanapi "imah pinter" sapertos anu sering disebut, mahal pisan sareng ngan ukur anu beunghar anu mampuh. Dinten ayeuna di pasar anjeun tiasa mendakan kit anu cukup murah kalayan sensor, tombol / saklar sareng aktuator pikeun ngatur cahaya, sockets, ventilasi, suplai cai sareng konsumen sanés. Komo jalma DIY anu paling bengkok tiasa kalibet dina kageulisan sareng ngumpul alat pikeun bumi anu pinter kalayan harga anu murah.

Nyambungkeun méter cai ka imah pinter

Ilaharna, alat anu diusulkeun nyaéta sénsor atanapi aktuator. Aranjeunna ngagampangkeun pikeun nerapkeun skénario sapertos "nalika sénsor gerak dipicu, hurungkeun lampu" atanapi "saklar caket jalan kaluar mareuman lampu di sakumna apartemen." Tapi kumaha bae hal teu digawe kaluar kalawan telemetry. Pangpangna, éta mangrupikeun grafik suhu sareng kalembaban, atanapi kakuatan sakedapan di outlet khusus.

Kuring nembe dipasang méter cai kalayan kaluaran pulsa. Pikeun unggal liter anu ngaliwat méter, saklar reed diaktipkeun sareng nutup kontakna. Hiji-hijina hal anu kedah dilakukeun nyaéta nempel kana kabel sareng nyobian nyandak kauntungan tina éta. Contona, analisa konsumsi cai ku jam jeung poé dina saminggu. Nya, upami aya sababaraha risers cai di apartemen, maka langkung gampang ningali sadaya indikator ayeuna dina hiji layar tibatan naek kana ceruk anu susah dihontal kalayan senter.

Handap cut nyaeta versi abdi alat dumasar kana ESP8266, nu diitung pulsa tina méter cai sarta ngirimkeun bacaan via MQTT ka server home pinter. Urang bakal program dina micropython ngagunakeun perpustakaan uasyncio. Nalika nyiptakeun firmware, kuring mendakan sababaraha kasusah anu pikaresepeun, anu ogé bakal dibahas dina tulisan ieu. indit!

Skéma

Nyambungkeun méter cai ka imah pinter

Jantung sakabéh sirkuit nyaéta modul dina mikrokontroler ESP8266. ESP-12 mimitina direncanakeun, tapi milik abdi tétéla cacad. Urang kedah janten eusi sareng modul ESP-07, anu sayogi. Untungna, aranjeunna sami duanana dina watesan pin na fungsionalitas, hijina bédana aya dina anteneu - ESP-12 diwangun-di hiji, sedengkeun ESP-07 boga hiji éksternal. Sanajan kitu, sanajan tanpa anteneu WiFi, sinyal di kamar mandi kuring narima normal.

Kabel modul standar:

  • tombol reset kalawan pull-up na kapasitor (sanajan duanana geus aya di jero modul nu)
  • Sinyal aktipkeun (CH_PD) ditarik ka kakuatan
  • GPIO15 ditarik ka taneuh. Ieu ngan diperlukeun dina mimiti, tapi kuring masih teu boga nanaon pikeun ngagantelkeun kana leg ieu; Abdi henteu peryogi deui

Pikeun nempatkeun modul kana modeu firmware, anjeun kedah pondok-circuit GPIO2 ka taneuh, sarta sangkan eta leuwih merenah, Kuring disadiakeun hiji tombol Boot. Dina kaayaan normal, pin ieu ditarik ka kakuatan.

Kaayaan garis GPIO2 dipariksa ngan dina awal operasi - nalika kakuatan diterapkeun atanapi langsung saatos reset. Jadi modul boh sapatu saperti biasa atawa balik kana modeu firmware. Sakali dimuat, pin ieu bisa dipaké salaku GPIO biasa. Nya, sabab parantos aya tombol di dinya, anjeun tiasa ngagantelkeun sababaraha fungsi anu mangpaat pikeun éta.

Pikeun programming na debugging I bakal ngagunakeun UART, nu kaluaran ka sisir a. Upami diperlukeun, kuring ngan saukur nyambungkeun adaptor USB-UART di dinya. Anjeun ngan perlu inget yen modul ieu Powered by 3.3V. Lamun poho pindah adaptor ka tegangan ieu sareng suplai 5V, modul nu paling dipikaresep bakal kaduruk kaluar.

Kuring teu boga masalah sareng listrik di kamar mandi - stop kontak lokasina ngeunaan hiji méter ti méter, jadi kuring bakal Powered by 220V. Salaku sumber kakuatan kuring bakal boga leutik meungpeuk HLK-PM03 ku Tenstar Robot. Pribadi, abdi gaduh waktos teuas jeung analog sarta éléktronika kakuatan, tapi di dieu mangrupa catu daya siap-dijieun dina hal leutik.

Pikeun sinyal modeu operasi, Kuring disadiakeun hiji LED disambungkeun ka GPIO2. Nanging, kuring henteu ngabéréskeun éta, sabab ... ESP-07 modul geus boga LED, sarta ogé disambungkeun ka GPIO2. Tapi ngantep éta dina dewan, bisi kuring hoyong kaluaran LED ieu kana kasus éta.

Hayu urang ngaléngkah ka bagian paling metot. Méter cai henteu aya logika; anjeun moal tiasa naroskeun bacaan ayeuna. Hiji-hijina hal anu sayogi pikeun urang nyaéta impulses - nutup kontak tina switch reed unggal liter. Output switch reed kuring disambungkeun ka GPIO12 / GPIO13. Kuring bakal ngaktipkeun résistor pull-up programmatically jero modul nu.

Mimitina, kuring poho nyadiakeun resistors R8 na R9 jeung versi kuring dewan teu boga aranjeunna. Tapi saprak kuring geus ngeposkeun diagram pikeun dulur ningali, éta patut ngabenerkeun pangawasan ieu. Resistors diperlukeun ku kituna teu kaduruk port lamun firmware glitches tur nyetel pin ka hiji, sarta reed switch pondok garis ieu taneuh (kalawan résistor maksimum 3.3V / 1000Ohm = 3.3mA bakal ngalir).

Waktosna pikeun mikir ngeunaan naon anu kudu dilakukeun upami listrik pareum. Pilihan kahiji nyaéta menta nilai counter awal ti server di mimiti. Tapi ieu bakal merlukeun komplikasi signifikan tina protokol bursa. Leuwih ti éta, kinerja alat dina hal ieu gumantung kana kaayaan server. Upami pangladén henteu ngamimitian saatos listrik dipareuman (atanapi ngamimitian engké), méter cai moal tiasa naroskeun nilai awal sareng moal jalanna leres.

Ku alatan éta, kuring mutuskeun pikeun nerapkeun nyimpen nilai counter dina chip memori disambungkeun via I2C. Abdi henteu ngagaduhan syarat khusus pikeun ukuran mémori lampu kilat - anjeun ngan ukur kedah nyimpen 2 angka (jumlah liter dina méter cai panas sareng tiis). Malah modul pangleutikna bakal ngalakukeun. Tapi anjeun kedah nengetan jumlah siklus rekaman. Kanggo sabagéan ageung modul ieu 100 rébu siklus, pikeun sababaraha dugi ka sajuta.

Sigana mah sajuta teh loba. Tapi salami 4 taun cicing di apartemen kuring, kuring ngonsumsi cai langkung ti 500 méter kubik, éta 500 rébu liter! Sareng 500 rébu rékaman dina lampu kilat. Tur éta ngan cai tiis. Anjeun tiasa, tangtosna, resolder chip unggal sababaraha taun, tapi tétéla aya chip FRAM. Ti sudut pandang programming, ieu téh sarua I2C EEPROM, ngan kalawan jumlah anu kacida gedéna tina siklus nulis balik (ratusan juta). Ngan kuring masih teu tiasa angkat ka toko nganggo microcircuits sapertos kitu, janten ayeuna 24LC512 biasa bakal nangtung.

Dicitak circuit board

Mimitina, kuring ngarencanakeun ngadamel papan di bumi. Ku alatan éta, dewan dirancang salaku hiji sisi. Tapi sanggeus méakkeun sajam kalawan beusi laser sarta topeng solder (eta kumaha bae teu comme il faut tanpa eta), Kuring masih mutuskeun pikeun mesen dewan ti Cina.

Nyambungkeun méter cai ka imah pinter

Ampir sateuacan mesen dewan, kuring sadar yén salian chip memori flash, kuring tiasa nyambungkeun hal anu sanés pikeun beus I2C, sapertos tampilan. Naon kahayang pikeun kaluaran eta masih pertanyaan, tapi perlu routed on dewan. Nya, saprak kuring badé mesen papan ti pabrik, teu aya gunana pikeun ngawatesan diri ka papan tunggal, janten garis I2C mangrupikeun hiji-hijina anu aya di tukangeun papan.

Aya ogé hiji masalah badag dina wiring hiji arah. Sabab dewan ieu digambar salaku hiji sisi, jadi lagu sareng komponenana SMD ieu rencanana pikeun ditempatkeun dina hiji sisi, sarta komponén kaluaran, konektor jeung catu daya di sisi séjén. Nalika kuring narima papan sabulan engké, Kuring poho ngeunaan rencana aslina tur soldered sakabeh komponen dina sisi hareup. Sarta ngan lamun datang ka soldering catu daya tétéla yén tambah jeung dikurangan éta kabel sabalikna. Kuring kungsi tani jeung jumpers. Dina gambar di luhur, kuring geus robah wiring, tapi taneuh ditransferkeun ti hiji bagian dewan ka nu sejen ngaliwatan pin tina tombol Boot (sanajan bakal mungkin ngagambar lagu dina lapisan kadua).

Tétéla kawas kieu

Nyambungkeun méter cai ka imah pinter

perumahan

Lengkah saterusna nyaéta awak. Upami Anjeun gaduh printer 3D, ieu teu jadi masalah. Abdi henteu ngaganggu teuing - kuring ngan ukur ngagambar kotak ukuran anu pas sareng ngadamel potongan di tempat anu leres. Panutup napel kana awak ku screws timer ngetok leutik.

Nyambungkeun méter cai ka imah pinter

Kuring parantos nyarios yén tombol Boot tiasa dianggo salaku tombol tujuan umum - janten kami bakal nampilkeunana dina panel hareup. Jang ngalampahkeun ieu, kuring ngagambar "sumur" khusus dimana tombolna hirup.

Nyambungkeun méter cai ka imah pinter

Di jero kasus aya ogé studs dimana papan dipasang sareng diamankeun ku screw M3 tunggal (teu aya deui rohangan dina papan)

Kuring geus milih tampilan nalika kuring nyitak versi sampel mimiti kasus. Pamaca dua garis standar henteu pas kana hal ieu, tapi di handapeun aya tampilan OLED SSD1306 128 × 32. Ieu rada leutik, tapi kuring teu kudu neuteup ka dinya unggal poe-eta teuing keur kuring.

Figuring kaluar cara kieu jeung nu kumaha kawat bakal routed ti dinya, abdi mutuskeun pikeun nempel tampilan di tengah kasus. Érgonomi, tangtosna, aya di handap par - tombolna di luhur, tampilanna di handap. Tapi kuring parantos nyarios yén ideu pikeun ngagantelkeun tampilan éta telat sareng kuring teuing puguh rewire dewan pikeun mindahkeun tombol.

Paranti dirakit. modul tampilan ieu glued kana snot kalawan lem panas

Nyambungkeun méter cai ka imah pinter

Nyambungkeun méter cai ka imah pinter

Hasil ahirna tiasa ditingali dina KDPV

Firmware

Hayu urang ngaléngkah ka bagian software. Pikeun karajinan leutik sapertos kieu, kuring resep pisan ngagunakeun Python (micropython) - kodena tétéla kompak pisan sareng kaharti. Untungna, teu perlu turun ka tingkat register guna squeeze kaluar microseconds - sagalana bisa dipigawé ti Python.

Sigana mah sadayana saderhana, tapi henteu saderhana pisan - alatna ngagaduhan sababaraha fungsi mandiri:

  • Pamaké pokes tombol jeung kasampak di tampilan
  • Liter centang sareng ngapdet nilai dina mémori flash
  • Modul ngawas sinyal WiFi sareng nyambungkeun deui upami diperyogikeun
  • Nya, tanpa bohlam kedip-kedip mustahil

Anjeun teu tiasa nganggap yén hiji fungsi henteu jalan upami anu sanés nyangkut kusabab sababaraha alesan. Kuring parantos ngeusian kaktus dina proyék-proyék anu sanés sareng ayeuna kuring masih ningali gangguan dina gaya "kantun liter sanés kusabab tampilanna ngamutahirkeun dina waktos éta" atanapi "pamaké henteu tiasa ngalakukeun nanaon nalika modul nyambung ka Wifi." Tangtu, sababaraha hal bisa dipigawé ngaliwatan interrupts, tapi anjeun bisa ngajalankeun kana watesan dina durasi, nyarang telepon, atawa parobahan non-atom kana variabel. Nya, kodeu anu ngalakukeun sadayana gancang janten bubur.

В proyék leuwih serius Kuring nganggo multitasking preemptive klasik sareng FreeRTOS, tapi dina hal ieu modélna tétéla langkung cocog coroutines jeung perpustakaan uasync . Leuwih ti éta, palaksanaan Python of coroutines saukur endah - sagalana geus rengse basajan tur merenah pikeun programmer nu. Tulis logika anjeun nyalira, wartosan kuring dimana anjeun tiasa ngalih antara aliran.

Kuring nyarankeun diajar béda antara preemptive sarta multitasking kalapa salaku subyek pilihan. Ayeuna hayu urang tungtungna ngaléngkah ka kode.

#####################################
# Counter class - implements a single water counter on specified pin
#####################################
class Counter():
    debounce_ms = const(25)
    
    def __init__(self, pin_num, value_storage):
        self._value_storage = value_storage
        
        self._value = self._value_storage.read()
        self._value_changed = False

        self._pin = Pin(pin_num, Pin.IN, Pin.PULL_UP)

        loop = asyncio.get_event_loop()
        loop.create_task(self._switchcheck())  # Thread runs forever

Unggal counter diatur ku conto tina kelas Counter. Anu mimiti, nilai counter awal dikurangan tina EEPROM (value_storage) - ieu kumaha recovery sanggeus gagalna kakuatan dilaksanakeun.

Pin ieu initialized ku diwangun-di pull-up ka catu daya: lamun switch Reed ditutup, garis nu enol, lamun jalur dibuka, ditarik nepi ka catu daya jeung controller nu maca hiji.

Tugas anu misah ogé diluncurkeun di dieu, anu bakal polling pin. Unggal counter bakal ngajalankeun tugas sorangan. Ieu kode nya

    """ Poll pin and advance value when another litre passed """
    async def _switchcheck(self):
        last_checked_pin_state = self._pin.value()  # Get initial state

        # Poll for a pin change
        while True:
            state = self._pin.value()
            if state != last_checked_pin_state:
                # State has changed: act on it now.
                last_checked_pin_state = state
                if state == 0:
                    self._another_litre_passed()

            # Ignore further state changes until switch has settled
            await asyncio.sleep_ms(Counter.debounce_ms)

A reureuh 25ms diperlukeun pikeun nyaring kontak mumbul, sarta dina waktos anu sareng ngatur sabaraha sering tugas wakes up (bari ieu tugas keur sare, tugas séjén ngajalankeun). Unggal 25ms fungsi wakes up, pariksa pin jeung lamun kontak switch Reed ditutup, lajeng liter sejen geus ngaliwatan méter sarta ieu perlu diolah.

    def _another_litre_passed(self):
        self._value += 1
        self._value_changed = True

        self._value_storage.write(self._value)

Ngolah liter salajengna téh trivial - counter saukur naek. Nya, langkung saé nyerat nilai énggal kana flash drive.

Pikeun betah pamakéan, "accessors" disadiakeun

    def value(self):
        self._value_changed = False
        return self._value

    def set_value(self, value):
        self._value = value
        self._value_changed = False

Nya, ayeuna hayu urang ngamangpaatkeun nikmat Python sareng perpustakaan uasync sareng ngadamel objek counter anu tiasa ngantosan (kumaha urang tiasa narjamahkeun ieu kana Rusia? Anu anjeun tiasa ngarepkeun?)

    def __await__(self):
        while not self._value_changed:
            yield from asyncio.sleep(0)

        return self.value()

    __iter__ = __await__  

Ieu sapertos fungsi merenah nu waits nepi ka nilai counter ieu diropéa - fungsi wakes up ti jaman ka jaman jeung pariksa bendera _value_changed. Hal anu keren ngeunaan fungsi ieu nyaéta yén kode telepon tiasa bobo nalika nyauran fungsi ieu sareng bobo dugi ka nampi nilai énggal.

Kumaha upami interupsi?Sumuhun, dina titik ieu anjeun tiasa troll kuring, nyebutkeun yén anjeun sorangan ceuk ngeunaan interruptions, tapi kanyataanana anjeun ngalakukeun polling pin bodo. Sabenerna interrupts mangrupakeun hal kahiji kuring diusahakeun. Dina ESP8266, anjeun tiasa ngatur hiji interupsi ujung, komo nulis Handler pikeun interupsi ieu dina Python. Dina interupsi ieu, nilai variabel bisa diropéa. Panginten, ieu bakal cekap upami konter éta mangrupikeun alat budak - anu ngantosan dugi ka dipénta nilai ieu.

Hanjakalna (atanapi untungna?) Alat kuring aktip, éta sorangan kedah ngirim pesen via protokol MQTT sareng nyerat data ka EEPROM. Sarta di dieu larangan datang kana antrian - anjeun teu bisa allocate memori dina interrupts sarta ngagunakeun tumpukan badag, nu hartina anjeun bisa poho ngeunaan ngirim pesen ngaliwatan jaringan. Aya buns kawas micropython.schedule () nu ngidinan Anjeun pikeun ngajalankeun sababaraha fungsi "pas mungkin,"Tapi patarosan timbul, "Naon gunana?" Kumaha upami urang ngirim sababaraha jinis pesen ayeuna, teras interupsi sumping sareng ngarusak nilai-nilai variabel. Atawa, contona, hiji nilai counter anyar anjog ti server bari urang teu acan ditulis handap hiji heubeul. Sacara umum, anjeun kedah meungpeuk sinkronisasi atanapi kaluar tina éta kumaha waé béda.

Sareng ti waktos ka waktos RuntimeError: jadwal tumpukan ngadat pinuh sareng saha anu terang kunaon?

Kalayan polling eksplisit sareng uasync, dina hal ieu kumaha waé tétéla langkung saé sareng dipercaya

Kuring mawa karya kalawan EEPROM ka kelas leutik

class EEPROM():
    i2c_addr = const(80)

    def __init__(self, i2c):
        self.i2c = i2c
        self.i2c_buf = bytearray(4) # Avoid creation/destruction of the buffer on each call


    def read(self, eeprom_addr):
        self.i2c.readfrom_mem_into(self.i2c_addr, eeprom_addr, self.i2c_buf, addrsize=16)
        return ustruct.unpack_from("<I", self.i2c_buf)[0]    
        
    
    def write(self, eeprom_addr, value):
        ustruct.pack_into("<I", self.i2c_buf, 0, value)
        self.i2c.writeto_mem(self.i2c_addr, eeprom_addr, self.i2c_buf, addrsize=16)

Dina Python, hese dianggo langsung sareng bait, tapi bait anu ditulis dina mémori. Kuring kungsi pager konversi antara integer jeung bait ngagunakeun perpustakaan ustruct.

Pikeun henteu nransferkeun objék I2C sareng alamat sél mémori unggal waktos, kuring dibungkus sadayana dina klasik anu alit sareng merenah.

class EEPROMValue():
    def __init__(self, i2c, eeprom_addr):
        self._eeprom = EEPROM(i2c)
        self._eeprom_addr = eeprom_addr
        

    def read(self):
        return self._eeprom.read(self._eeprom_addr)


    def write(self, value):
        self._eeprom.write(self._eeprom_addr, value)

Obyék I2C sorangan dijieun kalayan parameter ieu

i2c = I2C(freq=400000, scl=Pin(5), sda=Pin(4))

Urang datang ka bagian paling narik - palaksanaan komunikasi jeung server via MQTT. Nya, henteu kedah nerapkeun protokol sorangan - kuring mendakan éta dina Internét palaksanaan Asynchronous siap-dijieun. Ieu anu bakal kami anggo.

Sadaya hal anu paling narik dikumpulkeun dina kelas CounterMQTTClient, anu dumasar kana perpustakaan MQTTClient. Hayu urang mimitian ti periphery

#####################################
# Class handles both counters and sends their status to MQTT
#####################################
class CounterMQTTClient(MQTTClient):

    blue_led = Pin(2, Pin.OUT, value = 1)
    button = Pin(0, Pin.IN)

    hot_counter = Counter(12, EEPROMValue(i2c, EEPROM_ADDR_HOT_VALUE))
    cold_counter = Counter(13, EEPROMValue(i2c, EEPROM_ADDR_COLD_VALUE))

Di dieu anjeun tiasa nyiptakeun sareng ngonpigurasikeun pin sareng tombol bohlam, ogé objék méter cai tiis sareng panas.

Kalawan initialization, teu sagalana jadi trivial

    def __init__(self):
        self.internet_outage = True
        self.internet_outages = 0
        self.internet_outage_start = ticks_ms()

        with open("config.txt") as config_file:
            config['ssid'] = config_file.readline().rstrip()
            config['wifi_pw'] = config_file.readline().rstrip()
            config['server'] = config_file.readline().rstrip()
            config['client_id'] = config_file.readline().rstrip()
            self._mqtt_cold_water_theme = config_file.readline().rstrip()
            self._mqtt_hot_water_theme = config_file.readline().rstrip()
            self._mqtt_debug_water_theme = config_file.readline().rstrip()

        config['subs_cb'] = self.mqtt_msg_handler
        config['wifi_coro'] = self.wifi_connection_handler
        config['connect_coro'] = self.mqtt_connection_handler
        config['clean'] = False
        config['clean_init'] = False
        super().__init__(config)

        loop = asyncio.get_event_loop()
        loop.create_task(self._heartbeat())
        loop.create_task(self._counter_coro(self.cold_counter, self._mqtt_cold_water_theme))
        loop.create_task(self._counter_coro(self.hot_counter, self._mqtt_hot_water_theme))
        loop.create_task(self._display_coro())

Pikeun nyetel parameter operasi perpustakaan mqtt_as, dipaké kamus badag tina setélan béda - config. Seuseueurna setélan standar henteu kunanaon pikeun urang, tapi seueur setélan kedah diatur sacara eksplisit. Dina raraga teu nulis setelan langsung dina kode, Kuring nyimpen aranjeunna dina file téks config.txt. Ieu ngidinan Anjeun pikeun ngarobah kodeu paduli setelan, kitu ogé rivet sababaraha alat idéntik jeung parameter béda.

Blok kode anu terakhir ngamimitian sababaraha coroutine pikeun ngalayanan sababaraha fungsi sistem. Contona, ieu coroutine nu jasa counters

    async def _counter_coro(self, counter, topic):
        # Publish initial value
        value = counter.value()
        await self.publish(topic, str(value))

        # Publish each new value
        while True:
            value = await counter
            await self.publish_msg(topic, str(value))

Coroutine ngantosan dina loop pikeun nilai counter anyar jeung, pas nembongan, ngirim pesen via protokol MQTT. Potongan kahiji kode ngirimkeun nilai awal sanajan euweuh cai ngalir ngaliwatan loket.

Kelas dasar MQTTClient ngawula sorangan, initiates sambungan WiFi jeung reconnects lamun sambungan nu leungit. Lamun aya parobahan dina kaayaan sambungan WiFi, perpustakaan informs kami ku nelepon wifi_connection_handler

    async def wifi_connection_handler(self, state):
        self.internet_outage = not state
        if state:
            self.dprint('WiFi is up.')
            duration = ticks_diff(ticks_ms(), self.internet_outage_start) // 1000
            await self.publish_debug_msg('ReconnectedAfter', duration)
        else:
            self.internet_outages += 1
            self.internet_outage_start = ticks_ms()
            self.dprint('WiFi is down.')
            
        await asyncio.sleep(0)

Fungsi ieu jujur ​​disalin tina conto. Dina hal ieu, éta ngitung jumlah pareum (internet_outages) sareng durasina. Nalika sambungan dibalikkeun, waktos dianggurkeun dikirim ka server.

Ku jalan kitu, sare panungtungan ngan diperlukeun sangkan fungsi Asynchronous - dina perpustakaan disebut via await, sarta ngan fungsi anu awakna ngandung await sejen bisa disebut.

Salian nyambung ka WiFi, Anjeun ogé kudu nyieun sambungan ka calo MQTT (server). Perpustakaan ogé ngalakukeun ieu, sareng kami ngagaduhan kasempetan pikeun ngalakukeun anu mangpaat nalika sambunganna didamel

    async def mqtt_connection_handler(self, client):
        await client.subscribe(self._mqtt_cold_water_theme)
        await client.subscribe(self._mqtt_hot_water_theme)

Di dieu kami ngalanggan sababaraha pesen - server ayeuna gaduh kamampuan pikeun nyetél nilai counter ayeuna ku ngirim pesen anu saluyu.

    def mqtt_msg_handler(self, topic, msg):
        topicstr = str(topic, 'utf8')
        self.dprint("Received MQTT message topic={}, msg={}".format(topicstr, msg))

        if topicstr == self._mqtt_cold_water_theme:
            self.cold_counter.set_value(int(msg))

        if topicstr == self._mqtt_hot_water_theme:
            self.hot_counter.set_value(int(msg))

Pungsi ieu ngolah pesen anu asup, sareng gumantung kana topik (judul pesen), nilai salah sahiji loket diropéa.

Sababaraha fungsi pembantu

    # Publish a message if WiFi and broker is up, else discard
    async def publish_msg(self, topic, msg):
        self.dprint("Publishing message on topic {}: {}".format(topic, msg))
        if not self.internet_outage:
            await self.publish(topic, msg)
        else:
            self.dprint("Message was not published - no internet connection")

Pungsi ieu ngirim pesen lamun sambungan geus dijieun. Upami teu aya sambungan, pesenna teu dipalire.

Sareng ieu ngan ukur fungsi anu pikaresepeun anu ngahasilkeun sareng ngirim pesen debugging.

    async def publish_debug_msg(self, subtopic, msg):
        await self.publish_msg("{}/{}".format(self._mqtt_debug_water_theme, subtopic), str(msg))

Seueur téks, sareng kami henteu acan kedip-kedip LED. Ieuh

    # Blink flash LED if WiFi down
    async def _heartbeat(self):
        while True:
            if self.internet_outage:
                self.blue_led(not self.blue_led()) # Fast blinking if no connection
                await asyncio.sleep_ms(200) 
            else:
                self.blue_led(0) # Rare blinking when connected
                await asyncio.sleep_ms(50)
                self.blue_led(1)
                await asyncio.sleep_ms(5000)

Kuring geus disadiakeun 2 modus blinking. Upami sambunganna leungit (atanapi karek ditetepkeun), alat bakal kedip-kedip gancang. Upami sambunganna dipasang, alat bakal kedip-kedip sakali unggal 5 detik. Upami diperyogikeun, modeu kedip-kedip sanésna tiasa dilaksanakeun di dieu.

Tapi LEDna ngan ukur ngémutan. Urang ogé aimed dina tampilan.

    async def _display_coro(self):
        display = SSD1306_I2C(128,32, i2c)
    
        while True:
            display.poweron()
            display.fill(0)
            display.text("COLD: {:.3f}".format(self.cold_counter.value() / 1000), 16, 4)
            display.text("HOT:  {:.3f}".format(self.hot_counter.value() / 1000), 16, 20)
            display.show()
            await asyncio.sleep(3)
            display.poweroff()

            while self.button():
                await asyncio.sleep_ms(20)

Ieu anu kuring nyarioskeun - kumaha saderhana sareng gampangna sareng coroutines. Fungsi saeutik ieu ngajelaskeun pangalaman pamaké ENTIRE. Coroutine ngan saukur ngantosan tombol dipencet sareng hurungkeun tampilan salami 3 detik. Pintonan nunjukkeun bacaan méter ayeuna.

Aya kénéh sababaraha hal saeutik ditinggalkeun. Ieu fungsi anu (deui) ngamimitian sakabeh usaha ieu. The loop utama ngan ngirimkeun rupa informasi debugging sakali menit. Sacara umum, kuring ngadugikeun éta - kuring henteu nyangka kedah seueur teuing koméntar

   async def main(self):
        while True:
            try:
                await self._connect_to_WiFi()
                await self._run_main_loop()
                    
            except Exception as e:
                self.dprint('Global communication failure: ', e)
                await asyncio.sleep(20)

    async def _connect_to_WiFi(self):
        self.dprint('Connecting to WiFi and MQTT')
        sta_if = network.WLAN(network.STA_IF)
        sta_if.connect(config['ssid'], config['wifi_pw'])
        
        conn = False
        while not conn:
            await self.connect()
            conn = True

        self.dprint('Connected!')
        self.internet_outage = False

    async def _run_main_loop(self):
        # Loop forever
        mins = 0
        while True:
            gc.collect()  # For RAM stats.
            mem_free = gc.mem_free()
            mem_alloc = gc.mem_alloc()

            try:
                await self.publish_debug_msg("Uptime", mins)
                await self.publish_debug_msg("Repubs", self.REPUB_COUNT)
                await self.publish_debug_msg("Outages", self.internet_outages)
                await self.publish_debug_msg("MemFree", mem_free)
                await self.publish_debug_msg("MemAlloc", mem_alloc)
            except Exception as e:
                self.dprint("Exception occurred: ", e)
            mins += 1

            await asyncio.sleep(60)

Nya, sababaraha setélan sareng konstanta pikeun ngalengkepan pedaran

#####################################
# Constants and configuration
#####################################


config['keepalive'] = 60
config['clean'] = False
config['will'] = ('/ESP/Wemos/Water/LastWill', 'Goodbye cruel world!', False, 0)

MQTTClient.DEBUG = True

EEPROM_ADDR_HOT_VALUE = const(0)
EEPROM_ADDR_COLD_VALUE = const(4)

Éta sadayana dimimitian sapertos kieu

client = CounterMQTTClient()
loop = asyncio.get_event_loop()
loop.run_until_complete(client.main())

Aya kajadian ka ingetan kuring

Janten, sadaya kodeu aya. Kuring unggah file nganggo utilitas ampy - éta ngamungkinkeun anjeun unggah kana flash drive internal (anu aya dina ESP-07 sorangan) teras ngaksés éta tina program salaku file normal. Di dinya kuring ogé unggah mqtt_as, uasyncio, ssd1306 sareng koleksi perpustakaan anu kuring dianggo (dipaké di jero mqtt_as).

Urang ngajalankeun na ... Simkuring meunang MemoryError a. Leuwih ti éta, beuki kuring diusahakeun ngartos dimana persis memori ieu bocor, beuki debug prints I nempatkeun, saméméhna kasalahan ieu mucunghul. Pilarian pondok Google nyababkeun kuring ngartos yén mikrokontroler, prinsipna, ngan ukur 30 kB mémori, dimana 65 kB kode (kaasup perpustakaan) ngan saukur teu cocog.

Tapi aya jalan kaluar. Tétéla micropython henteu ngaéksekusi kode langsung tina file .py - file ieu disusun munggaran. Leuwih ti éta, éta disusun langsung dina mikrokontroler, robah jadi bytecode, nu lajeng disimpen dina mémori. Nya, pikeun kompiler tiasa dianggo, anjeun ogé peryogi jumlah RAM anu tangtu.

Trikna nyaéta pikeun nyalametkeun mikrokontroler tina kompilasi intensif sumberdaya. Anjeun tiasa nyusun file dina komputer ageung sareng unggah bytecode anu tos siap kana mikrokontroler. Jang ngalampahkeun ieu, anjeun kudu ngundeur firmware micropython jeung ngawangun mpy-cross utiliti.

Kuring henteu nyerat Makefile, tapi sacara manual ngalangkungan sareng nyusun sadaya file anu diperyogikeun (kalebet perpustakaan) sapertos kieu

mpy-cross water_counter.py

Sadaya anu tetep nyaéta unggah file nganggo ekstensi .mpy, teu hilap mupus heula .py anu saluyu tina sistem file alat.

Atuh sagala ngembangkeun dina program (IDE?) ESPlorer. Éta ngamungkinkeun anjeun unggah skrip ka mikrokontroler sareng langsung ngajalankeunana. Bisi kuring, sadaya logika sareng nyiptakeun sadaya objék anu aya dina file water_counter.py (.mpy). Tapi supados sadayana ieu dimimitian sacara otomatis, kedah aya ogé file anu disebut main.py di mimiti. Leuwih ti éta, éta kudu persis .py, sarta teu pre-disusun .mpy. Di dieu aya eusi trivial na

import water_counter

Urang ngajalankeun eta - sagalana jalan. Tapi mémori bébas téh alarmingly leutik - ngeunaan 1kb. Kuring masih boga rencana rék dilegakeun fungsionalitas alat, sarta kilobyte ieu jelas teu cukup keur kuring. Tapi tétéla aya jalan kaluar pikeun kasus ieu ogé.

Ieu hal. Sanaos file disusun kana bytecode sareng cicing dina sistem file internal, kanyataanna aranjeunna masih dimuat kana RAM sareng dieksekusi ti dinya. Tapi tétéla yén micropython tiasa ngaéksekusi bytecode langsung tina mémori flash, tapi pikeun ieu anjeun kedah ngawangun langsung kana firmware. Henteu hese, sanaos peryogi sababaraha waktos dina netbook kuring (ngan aya kuring kabeneran gaduh Linux).

Algoritma nyaéta kieu:

  • Ngundeur tur masang ESP Buka SDK. Hal ieu assembles compiler jeung perpustakaan pikeun program pikeun ESP8266. Dirakit dumasar kana petunjuk dina halaman utama proyék (kuring milih STANDALONE=yes setting)
  • download jenis micropython
  • Teundeun perpustakaan diperlukeun dina port / esp8266 / modul jero tangkal micropython
  • Urang ngumpul firmware nu nurutkeun parentah dina file palabuhan / esp8266 / README.md
  • Kami unggah firmware ka mikrokontroler (Kuring ngalakukeun ieu dina Windows nganggo program ESP8266Flasher atanapi Python esptool)

Éta waé, ayeuna 'impor ssd1306' bakal ngangkat kodeu langsung tina firmware sareng RAM moal dikonsumsi pikeun ieu. Kalayan trik ieu, kuring ngan ukur unggah kode perpustakaan kana firmware, sedengkeun kode program utama dieksekusi tina sistem file. Hal ieu ngamungkinkeun anjeun gampang ngaropea program tanpa recompiling firmware nu. Di momen kuring boga ngeunaan 8.5kb RAM bébas. Ieu bakal ngidinan urang pikeun nerapkeun cukup loba fungsi mangpaat béda dina mangsa nu bakal datang. Nya, upami mémori henteu cekap, anjeun tiasa nyorong program utama kana firmware.

Janten naon anu kedah urang laksanakeun ayeuna?

Ok, hardware geus soldered, firmware ditulis, kotak dicitak, alat ieu nyangkut dina témbok jeung happily blinks bohlam lampu. Tapi pikeun ayeuna éta sadayana kotak hideung (sacara harfiah sareng figuratively) sareng éta masih sakedik dianggo. Waktosna pikeun ngalakukeun hiji hal sareng pesen MQTT anu dikirim ka server.

"Imah pinter" abdi spinning on Sistim Majordomo. Modul MQTT boh asalna tina kotak, atanapi gampang dipasang ti pasar tambihan - kuring henteu émut ti mana kuring kéngingkeun. MQTT sanes hiji hal timer cukup - anjeun perlu disebut. calo - a server nu narima, sorts tur neraskeun pesen MQTT ka klien. Kuring make reungit, nu (kawas majordomo) dijalankeun dina netbook sarua.

Saatos alat ngirim pesen sahenteuna sakali, nilai bakal langsung muncul dina daptar.

Nyambungkeun méter cai ka imah pinter

Nilai-nilai ieu ayeuna tiasa dikaitkeun sareng objék sistem, aranjeunna tiasa dianggo dina skrip otomatisasi sareng tunduk kana sababaraha analisa - sadayana di luar ruang lingkup tulisan ieu. Abdi tiasa nyarankeun sistem majordomo ka saha kabetot saluran Éléktronik Dina lénsa - babaturan ogé ngawangun imah pinter jeung jelas ngobrol ngeunaan nyetel sistem.

Kuring ngan bakal nunjukkeun anjeun sababaraha grafik. Ieu mangrupikeun grafik saderhana tina nilai harian

Nyambungkeun méter cai ka imah pinter
Ieu bisa ditempo yén ampir euweuh jalma anu ngagunakeun cai peuting. Sababaraha kali aya anu angkat ka WC, sareng sigana saringan osmosis sabalikna nyedot sababaraha liter per wengi. Isuk-isuk, konsumsi ningkat sacara signifikan. Kuring biasana ngagunakeun cai tina alat keur ngagolakkeun a, tapi lajeng abdi hoyong mandi jeung samentara switched ka cai panas kota - ieu ogé jelas katempo dina grafik handap.

Tina grafik ieu kuring diajar yén ka WC butuh 6-7 liter cai, mandi butuh 20-30 liter, ngumbah piring butuh sakitar 20 liter, sareng mandi butuh 160 liter. Kulawarga abdi meakeun wae kira-kira 500-600 liter per poé.

Pikeun maranéhanana anu utamana panasaran, Anjeun bisa nempo dina rékaman pikeun tiap nilai individu

Nyambungkeun méter cai ka imah pinter

Ti dieu kuring diajar yén nalika keran dibuka, cai ngalir dina laju kira-kira 1 liter per 5 detik.

Tapi dina formulir ieu statistik meureun teu pisan merenah pikeun nempo. Majordomo ogé gaduh kamampuan ningali grafik konsumsi ku dinten, minggu sareng bulan. Di dieu, contona, nyaéta grafik konsumsi dina bar

Nyambungkeun méter cai ka imah pinter

Sajauh ieu kuring ngan ukur gaduh data saminggu. Dina sabulan, grafik ieu bakal leuwih indicative - unggal dintenna bakal boga kolom misah. Gambar rada manja ku pangaluyuan kana nilai anu kuring lebetkeun sacara manual (kolom panggedéna). Sareng éta henteu acan écés naha kuring salah nyetél nilai anu munggaran, ampir kubus kirang, atanapi naha ieu mangrupikeun bug dina firmware sareng henteu sadayana liter diitung. Peryogi langkung waktos.

Grafik sorangan masih peryogi sababaraha magic, whitewashing, lukisan. Panginten kuring ogé bakal ngawangun grafik konsumsi mémori pikeun tujuan debugging - bisi aya anu bocor. Panginten kuring bakal ningalikeun waktos-waktos nalika teu aya Internét. Pikeun ayeuna, sadayana ieu dina tingkat ide.

kacindekan

Dinten ieu apartemen kuring geus jadi saeutik smarter. Kalayan alat leutik sapertos kitu, kuring bakal langkung gampang ngawas konsumsi cai di bumi. Mun tadi kuring ngambek "deui, urang meakeun loba cai dina sabulan," ayeuna kuring bisa manggihan sumber konsumsi ieu.

Sababaraha tiasa mendakan anéh ningali bacaan dina layar upami jarakna saméter ti méteran éta sorangan. Tapi dina mangsa nu bakal datang teu pisan jauh, abdi rencanana ngalih ka apartemen sejen, dimana bakal aya sababaraha risers cai, sarta méter sorangan paling dipikaresep bakal lokasina di badarat. Janten alat bacaan jarak jauh bakal mangpaat pisan.

Kuring ogé rencanana rék dilegakeun pungsi alat. Abdi parantos ningali klep motor. Ayeuna, pikeun ngalihkeun alat keur ngagolakkeun kana cai kota, kuring kedah ngaktipkeun 3 keran dina ceruk anu susah dihontal. Éta langkung saé pikeun ngalakukeun ieu ku hiji tombol kalayan indikasi anu saluyu. Nya, tangtosna, éta patut ngalaksanakeun panyalindungan tina bocor.

Dina artikel anu kuring ngajelaskeun versi kuring tina alat dumasar kana ESP8266. Dina pamanggih kuring, kuring datang nepi ka versi pisan metot tina firmware micropython maké coroutines - basajan tur nice. Kuring nyoba ngajelaskeun loba nuances na shortcomings yén kuring encountered salila kampanye. Panginten kuring ngajelaskeun sadayana sacara rinci teuing; sacara pribadi, salaku pamaca, langkung gampang pikeun kuring ngalangkungan barang-barang anu teu dipikabutuh tibatan engké mikirkeun naon anu teu diucapkeun.

Sakumaha biasa, kuring kabuka pikeun kritik anu ngawangun.

Kode sumber
Circuit jeung dewan
Modél kasus

sumber: www.habr.com

Tambahkeun komentar