Lidhim matësin e ujit me shtëpinë inteligjente

Dikur, sistemet e automatizimit të shtëpisë, ose siç i quajnë shpesh "shtëpia e mençur", ishin jashtëzakonisht të shtrenjta dhe vetëm të pasurit mund t'i përballonin ato. Sot, në treg mund të gjeni sete mjaft të përballueshme me sensorë, butona/disjedhës dhe pajisje ekzekutimi për menaxhimin e ndriçimit, prizave, ventilacionit, ujit dhe konsumatorëve të tjerë. Edhe personat më të papërgatitur për DIY mund të angazhohen në këtë mrekulli dhe për pak para të mbledhin pajisje për shtëpinë e mençur.

Lidhim matësin e ujit me shtëpinë inteligjente

Si rregull, pajisjet që ofrohen janë ose sensorë, ose mekanizma ekzekutivë. Ato lejojnë që të realizohen lehtësisht skenarë si "kur sensorët e lëvizjes aktivizohen, ndizet drita" ose "ndezësi pranë daljes fik dritën në të gjithë apartamentin". Por me telemetrinë, ndodhi ndryshe. Në rastin më të mirë, kemi një grafik të temperaturës dhe lagështisë, ose fuqinë aktuale në një prizë të caktuar.

Kohët e fundit, kam instaluar matës të ujit me dalje impulse. Pas çdo litri që kalon nëpër matës, një kontaktet e herkonit aktivizohet dhe mbyllet. E vetmja gjë që mbetet për t'u bërë është t'i lidhim telat dhe të provosh si mund të përfitohet nga kjo. Për shembull, të analizohet përdorimi i ujit sipas orëve dhe ditëve të javës. Nëse ka disa stoka për ujë në apartament, ndihmon më shumë të shohësh të dhënat aktuale në një ekran, sesa të lëvizësh në vende të pakapshme me një dritë.

Poshtë, është varianti im i pajisjes së bazuar në ESP8266, e cila numëron impulsat nga matësat e ujit dhe dërgon të dhënat përmes MQTT në serverin e shtëpisë së mençur. Do ta programojmë në micropython duke përdorur bibliotekën uasyncio. Gjatë krijimit të firmware-it, u ndesha me disa vështirësi interesante, për të cilat do flas në këtë artikull. Le të fillojmë!

Skema

Lidhim matësin e ujit me shtëpinë inteligjente

Zemra e gjithĂ« skemĂ«s Ă«shtĂ« njĂ« modul me mikroprocesorin ESP8266. Fillimisht ishte planifikuar ESP-12, por qĂ« doli i defektuar. Duhet tĂ« isha i kĂ«naqur me modulin ESP-07, qĂ« ishte i disponueshĂ«m. FatmirĂ«sisht, ato janĂ« tĂ« njĂ«jta pĂ«r sa i pĂ«rket pinĂ«ve dhe funksionalitetit, ndryshimi Ă«shtĂ« vetĂ«m nĂ« antenĂ« — ESP-12 ka njĂ« antenĂ« tĂ« integruar, ndĂ«rsa ESP-07 ka njĂ« antenĂ« tĂ« jashtme. MegjithatĂ«, madje edhe pa antenĂ«, sinjali WiFi nĂ« banjĂ«n time merret normalisht.

Për lidhjen e modulit është standarde:

  • butoni reset me tĂ«rheqje dhe kondensator (nĂ« tĂ« dyja rastet, ato janĂ« tĂ« pranishme brenda modulit)
  • Sinjali enable (CH_PD) Ă«shtĂ« i lidhur me furnizimin me energji
  • GPIO15 Ă«shtĂ« i lidhur me tokĂ«n. Kjo Ă«shtĂ« e nevojshme vetĂ«m nĂ« fillim, por nuk kam asgjĂ« tjetĂ«r pĂ«r ta lidhur nĂ« atĂ« pin

Për të kaluar modulit në modusin e firmware-it, duhet të mbyllet GPIO2 në tokë, dhe për ta bërë më të lehtë, kam parashikuar një buton Boot. Në gjendjen normale, ky pin është i lidhur me furnizimin.

GjatĂ« fillimit tĂ« punĂ«s, kontrollohet gjendja e pin-it GPIO2 — kur furnizohet energjia ose menjĂ«herĂ« pas reset-it. KĂ«shtu, moduli ngarkohet si zakonisht ose kalon nĂ« modusin e firmware-it. Pas ngarkesĂ«s, ky pin mund tĂ« pĂ«rdoret si njĂ« GPIO i zakonshĂ«m. Dhe qĂ« tashmĂ« kam njĂ« buton, mund tĂ« ngarkoj ndonjĂ« funksion tĂ« dobishĂ«m.

PĂ«r programimin dhe debugin, do tĂ« pĂ«rdor UART, i cili Ă«shtĂ« çuar nĂ« grebĂ«n. Kur kam nevojĂ« — thjesht e lidh USB-UART. Duhet vetĂ«m tĂ« mos harroj se moduli furnizohet me 3.3V. NĂ«se harroj ta kaloj konvertuesin nĂ« kĂ«tĂ« tension dhe aplikoj 5V, moduli shumĂ« mund tĂ« digjet.

Me elektricitetin nĂ« banjĂ«n time nuk kam problem — prizĂ«n e kam tĂ« vendosur rreth njĂ« meter nga matĂ«sat, kĂ«shtu qĂ« do ta ushqej nga 220V. Si burim energjie do tĂ« pĂ«rdor njĂ« tĂ« vogĂ«l blok HLK-PM03 nga Tenstar Robot. Personaliteti im Ă«shtĂ« i kĂ«qij me elektronikĂ«n analoge dhe energjike, dhe kĂ«tu kam njĂ« bllok tĂ« gatshĂ«m energjie nĂ« njĂ« kasĂ« tĂ« vogĂ«l.

PĂ«r tĂ« paralajmĂ«ruar modet e funksionimit, kam parashikuar njĂ« LED tĂ« lidhur me GPIO2. MegjithatĂ«, nuk e kam lidhur, sepse nĂ« modul ESP-07 ka njĂ« LED tashmĂ« tĂ« lidhur me tĂ« njĂ«jtin GPIO2. Por le tĂ« jetĂ« nĂ« bord — ndoshta do tĂ« doja ta nxjerr atĂ« LED nĂ« trup.

Kalon te gjĂ«ja mĂ« interesante. MatĂ«sit e ujit nuk kanĂ« asnjĂ« logjikĂ«, nuk mund t'i pyesĂ«sh pĂ«r tĂ« dhĂ«nat aktuale. E vetmja gjĂ« qĂ« na Ă«shtĂ« e qasshme janĂ« impulsat — mbyllja e kontakteve tĂ« herkonit pĂ«r çdo litĂ«r. Daljet e herkonĂ«ve janĂ« lidhur nĂ« GPIO12/GPIO13. Rezistori tĂ«rheqĂ«s do ta pĂ«rfshij brenda modulit.

Fillimisht harrova të parashikoj rezistorët R8 dhe R9 dhe në variantin tim të bordit nuk i kam. Por duke qenë se po e publikoj skemën për shikim të gjithëve, është e nevojshme të korrigjohet ky gabim. Rezistorët janë të nevojshëm, për të mos djegur portin në rast se firmware-i dështoi dhe vendos një '1' në pin, ndërsa herkoni e mbyll këtë linjë në tokë (me rezistor, do lëvizë maksimumi 3.3V/1000Ω = 3.3mA).

ËshtĂ« koha tĂ« mendojmĂ« se çfarĂ« tĂ« bĂ«jmĂ« nĂ«se ndalet energjia elektrike. Opcioni i parĂ« Ă«shtĂ« qĂ« nĂ« fillim tĂ« kĂ«rkoni nga serveri vlerat fillestare tĂ« matĂ«sve. Por kjo do tĂ« kĂ«rkonte njĂ« komplikim tĂ« konsiderueshĂ«m tĂ« protokollit tĂ« shkĂ«mbimit. PĂ«r mĂ« tepĂ«r, funksionimi i pajisjes nĂ« kĂ«tĂ« rast varet nga gjendja e serverit. NĂ«se pas ndalimit tĂ« energjisĂ« serveri nuk do tĂ« fillonte (ose do tĂ« fillonte mĂ« vonĂ«), atĂ«herĂ« matĂ«si i ujit nuk do tĂ« mund tĂ« kĂ«rkonte vlerat fillestare dhe do tĂ« funksiononte gabim.

Prandaj, vendosa tĂ« realizoj ruajtjen e vlerave tĂ« matĂ«sve nĂ« njĂ« memeore tĂ« lidhur pĂ«rmes I2C. Nuk kam kĂ«rkesa tĂ« veçanta pĂ«r madhĂ«sinĂ« e memories flash — duhet thjesht tĂ« ruaj 2 numra (sasia e litrave sipas matĂ«sve tĂ« ujit tĂ« ngrohtĂ« dhe tĂ« ftohtĂ«). Madhi modul mĂ« i vogĂ«l do tĂ« pĂ«rshtatej. Por duhet tĂ« merret parasysh numri i cikleve tĂ« shkruar. Te shumica e moduleve ky numĂ«r Ă«shtĂ« 100 mijĂ« cikle, ndonjĂ«herĂ« deri nĂ« njĂ« milion.

Duket se një milion është shumë. Por në 4 vitet që kam jetuar në apartamentin tim, kam konsumuar pak më shumë se 500 metra kub ujë, që do të thotë 500 mijë litra! Dhe kjo është vetëm uji i ftohtë. Mund të riu jok mikrokontrollin çdo dy vjet, por ka gjithashtu mikroçipa FRAM. Nga pikëpamja e programimit, ai është po e njëjta EEPROM I2C, thjesht me një numër shumë të madh ciklesh rishkrimi (qindra milion). Por deri tani nuk e kam pasur mundësinë të shkoj në dyqan për të blerë të tillë mikroçipa, prandaj për momentin kam vënë një 24LC512 të zakonshme.

Bordi i printuar

Fillimisht planifikoja të bënja platinë në shtëpi. Prandaj, plati ishte projektuar si një anë. Por, pas një ore përpjekjesh me një hekur të laserit dhe maskën për saldimin (pa të, ndonjëherë nuk është comme il faut), vendosa të porosis plakatë nga kinezët.

Lidhim matësin e ujit me shtëpinë inteligjente

Gati duke porositur plakatĂ«, kuptova se pĂ«rveç mikroçipit tĂ« memories flash, mund tĂ« lidhja diçka tjetĂ«r tĂ« dobishme nĂ« autobusin I2C, si njĂ« ekran. ÇfarĂ« do tĂ« shfaqja atij — akoma Ă«shtĂ« njĂ« pyetje, por duhet ta studioj nĂ« platinĂ«. Dhe qĂ« unĂ« po planifikoja tĂ« porosija pllaka nga fabrika, nuk kishte mĂ« kuptim tĂ« kufizoja veten me njĂ« pllakatĂ« tĂ« njĂ«anshme, prandaj rrugĂ«t I2C janĂ« tĂ« vetmet nĂ« anĂ«n e pasme tĂ« pllakĂ«s.

I lidhur gjithashtu me dizajnimin e njëanshëm ishte një gabim i madh. Duke qenë se plaka ishte dizajnuar njëanshëm, rrugët dhe komponentët SMD ishin planifikuar të vendoseshin nga njëra anë, dhe komponentët e daljes, lidhësit dhe burimi i energjisë nga ana tjetër. Kur pas një muaji mora pllakatë, harrova planin fillestar dhe saldova të gjithë komponentët në anën e para. Dhe vetëm kur erdhi koha për të salduar burimin e energjisë u zbulua se plusi dhe minus janë ndarë në mënyrë të kundërt. Më duhej të improvizoja me lidhje. Në imazhin e sipërm, tashmë kam ndërruar dizajnin, por toka kalon nga një pjesë e pllakës në tjetrën përmes lidhjeve të butonit Boot (ndonëse mund të kishte kaluar një rrugë në shtresën e dytë).

Ndodhi kështu

Lidhim matësin e ujit me shtëpinë inteligjente

Kasa

Hapi tjetĂ«r Ă«shtĂ« struktura. Me njĂ« printer 3D, kjo nuk Ă«shtĂ« njĂ« problem. Nuk kam bĂ«rĂ« ndonjĂ« shqetĂ«sim tĂ« veçantĂ« — thjesht e kam vizatuar njĂ« kuti me madhĂ«sinĂ« e nevojshme dhe kam bĂ«rĂ« prerje nĂ« vendet e duhura. Kapaku bashkangjitet me kutinĂ« me vidĂ« tĂ« vogla.

Lidhim matësin e ujit me shtëpinë inteligjente

Kisha pĂ«rmendur se butoni Boot mund tĂ« pĂ«rdoret si buton me shumĂ« funksione — kĂ«shtu qĂ« do ta çojmĂ« nĂ« panelin pĂ«rpara. PĂ«r kĂ«tĂ«, vizatova njĂ« “kanal” tĂ« veçantĂ« ku do tĂ« bĂ«nte jetĂ« butoni.

Lidhim matësin e ujit me shtëpinë inteligjente

Brenda kutisë gjithashtu vendosen suportet, mbi të cilat vendoset pllakata dhe sigurohet me një vidë M3 (në pllakë nuk kishte më shumë vend).

Ekranin e zgjodha pasi e printova variantin e parĂ« provues tĂ« kutisĂ«. NjĂ« dysheshe standarde nuk hynte nĂ« kĂ«tĂ« kuti, por nĂ« magazina zbulova njĂ« ekran OLED SSD1306 128×32. I vogĂ«l, por unĂ« nuk kam nevojĂ« ta shikoj çdo ditĂ« — Ă«shtĂ« nĂ« rregull.

Duke menduar si do tĂ« ishin kabllot, vendosa tĂ« ngjitur ekranin nĂ« mes tĂ« kutisĂ«. Ergonomia, sigurisht, Ă«shtĂ« shumĂ« e ulĂ«t — butoni Ă«shtĂ« lart, ekrani Ă«shtĂ« poshtĂ«. Por tashmĂ« e kam thĂ«nĂ«, ideja pĂ«r tĂ« lidhur njĂ« ekran erdhi shumĂ« vonĂ« dhe mĂ« ishte bĂ«rĂ« leni tĂ« ndryshoja pllakĂ«n pĂ«r tĂ« lĂ«vizur butonin.

Pajisja është përmbledhur. Moduli i ekranit është lidhur me ngjitës termik.

Lidhim matësin e ujit me shtëpinë inteligjente

Lidhim matësin e ujit me shtëpinë inteligjente

Rezultati përfundimtar mund të shihet në KDPV.

Firmware

TĂ« kalojmĂ« te pjesa programore. PĂ«r tĂ« tilla projekte tĂ« vogla, mĂ« pĂ«lqen tĂ« pĂ«rdor gjuhĂ«n Python (micropython) - kodi Ă«shtĂ« shumĂ« kompakt dhe i qartĂ«. FatmirĂ«sisht, kĂ«tu nuk ka nevojĂ« tĂ« zbresim nĂ« nivelin e regjistrave pĂ«r tĂ« nxjerrĂ« mikrosekonda — gjithçka mund tĂ« bĂ«het nga Python.

Duket e thjeshtĂ«, por nuk Ă«shtĂ« shumĂ« — nĂ« pajisje parashikohen disa funksione tĂ« pavarura:

  • PĂ«rdoruesi godet butonin dhe shikon ekranin
  • Litrat numĂ«rohen dhe pĂ«rditĂ«sojnĂ« vlerat nĂ« memorien flash
  • Moduli monitoron sinjalin WiFi dhe lidhet pĂ«rsĂ«ri nĂ«se Ă«shtĂ« e nevojshme
  • Por paqĂ« nuk ka dritĂ« qĂ« pulson, nuk mund tĂ« shkojĂ« ashtu.

Nuk duhet lejuar që një funksion të mos punojë, nëse një tjetër vanohet për ndonjë arsye. Jam lodhur nga cactusët në projekte të tjera dhe tani e shoh gabimin në stilin "kaluam një litër të ri, sepse në atë moment po përditësohej ekrani" ose "përdoruesi nuk mund të bëjë asgjë për aq kohë sa moduli po lidhet me WiFi". Sigurisht, disa gjëra mund të bëhen me ndërprerje, por mund të hasni në kufizime për kohëzgjatjen, thellësinë e thirrjeve ose ndryshimin jo-atoma të variablave. Po ashtu, kodi që merret me gjithçka shpejt kthehet në një pjatë të çrregullt.

NĂ« projekt mĂ« serioz Kam pĂ«rdorur multitasking tĂ« klasifikuesit dhe FreeRTOS, por nĂ« kĂ«tĂ« rast model i bashkĂ«programimeve (coroutines) dhe bibliotekĂ«s uasync . Realizimi i korutinave nĂ« Python Ă«shtĂ« thjesht bombastik — pĂ«r programuesin Ă«shtĂ« bĂ«rĂ« e thjeshtĂ« dhe praktike. Thjesht shko me logjikĂ«n tĂ«nde, vetĂ«m thuaj nĂ« cilat pika mund tĂ« kalosh midis proceseve.

Dallimet midis multitasking të klasifikuesit dhe asaj konkuruese sugjeroj të studiohen fakultativisht. Tani le të kalojmë në kod.

#####################################
# 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

Çdo numĂ«r trajtohet nga njĂ« instancĂ« e klasĂ«s Counter. Hapi i parĂ« Ă«shtĂ« tĂ« lexoni vlerĂ«n fillestare tĂ« numrit nga EEPROM (value_storage) — kĂ«shtu realizohet rikthimi pas humbjes sĂ« energjisĂ«.

Pini inicializohet me ngarkesĂ« tĂ« integruar: nĂ«se sensori Ă«shtĂ« i mbyllur — nĂ« vijĂ« Ă«shtĂ« zero, nĂ«se Ă«shtĂ« i hapur, vijĂ« ngrihet nĂ« energji dhe kontrollori lexon njĂ«.

Po ashtu, kĂ«tu fillon njĂ« detyrĂ« e veçantĂ« qĂ« do tĂ« bĂ«jĂ« anketimin e pin-it. Çdo numĂ«r do tĂ« fillojĂ« detyrĂ«n e vet. Ja kodi i saj

    """ Ankonë pin-in dhe avancojnë vlerën kur kalon një litër tjetër """
    async def _switchcheck(self):
        last_checked_pin_state = self._pin.value()  # Merrni gjendjen fillestare

        # Anketoni për një ndryshim në pin
        while True:
            state = self._pin.value()
            if state != last_checked_pin_state:
                # Gjendja ka ndryshuar: veproni tani.
                last_checked_pin_state = state
                if state == 0:
                    self._another_litre_passed()

            # Injoroni ndryshimet e mëtejshme të gjendjes derisa kyçi të stabilizohet
            await asyncio.sleep_ms(Counter.debounce_ms)

Vonimi prej 25ms Ă«shtĂ« i nevojshĂ«m pĂ«r filtrimin e dridhjes sĂ« kontakteve, njĂ«kohĂ«sisht rregullon sa shpesh zgjohhet detyra (kur kjo detyrĂ« fle — funksionojnĂ« detyra tĂ« tjera). Çdo 25ms funksioni zgjoheshtĂ«, kontrollon pin-in dhe nĂ«se kontaktet e sensorit janĂ« mbyllur, atĂ«herĂ« do tĂ« thotĂ« qĂ« njĂ« litĂ«r kaloi dhe duhet tĂ« trajtohet.

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

        self._value_storage.write(self._value)

Trajtimi i njĂ« litri tjetĂ«r Ă«shtĂ« triviale — thjesht rritet numri. E reja do tĂ« ishte mirĂ« tĂ« shkruhej nĂ« flash.

Për lehtësinë e përdorimit janë parashikuar "aksesuesit"

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

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

Tani le të shfrytëzojmë bukuritë e Pythonit dhe bibliotekës uasync dhe ta bëjmë objektin e numrit të pritur (si ta përkthej këtë në shqip? Ai që mund të pritet?)

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

        return self.value()

    __iter__ = __await__  

Kjo Ă«shtĂ« njĂ« funksion i pĂ«rdorshĂ«m, qĂ« pret derisa vlera e numrit tĂ« pĂ«rditĂ«sohet — funksioni zgjohĂ«t nga koha nĂ« kohĂ« dhe kontrollon flamurin _value_changed. KĂ«nga e kĂ«saj funksioni Ă«shtĂ« qĂ« kodi qĂ« e thĂ«rret mund tĂ« flejĂ« nĂ« thirrjen e kĂ«tij funksioni dhe tĂ« flejĂ« deri nĂ« marrjen e vlerĂ«s sĂ« re.

Po si pĂ«r ndĂ«rprerjet?Po, nĂ« kĂ«tĂ« pikĂ« mund tĂ« mĂ« shigjroni, sepse thashĂ« pĂ«r ndĂ«rprerjet, por nĂ« tĂ« vĂ«rtetĂ« kam organizuar njĂ« anketim tĂ« thjeshtĂ« tĂ« pin-it. NĂ« tĂ« vĂ«rtetĂ«, ndĂ«rprerjet ishin e para qĂ« provova. NĂ« ESP8266 mund tĂ« organizoni njĂ« ndĂ«rprerje sipas frontit dhe madje tĂ« shkruani njĂ« trajtues tĂ« kĂ«saj ndĂ«rprerjeje nĂ« Python. NĂ« kĂ«tĂ« ndĂ«rprerje mund tĂ« pĂ«rditĂ«soni vlerĂ«n e variablave. Ndoshta, do tĂ« mjaftonte qĂ« numri tĂ« ishte njĂ« pajisje e udhĂ«hequr — njĂ« gjĂ« qĂ« pret derisa t'i kĂ«rkohet vlera.

FatkeqĂ«sisht (ose pĂ«r fat tĂ« mirĂ«?) pajisja ime Ă«shtĂ« aktive, ajo duhet tĂ« dĂ«rgojĂ« vetĂ« mesazhe me protokollin MQTT dhe tĂ« regjistrojĂ« tĂ« dhĂ«na nĂ« EEPROM. Dhe kĂ«tu ndodhin kufizimet — nĂ« ndĂ«rprerje nuk mund tĂ« alokoni memorie dhe tĂ« pĂ«rdorni njĂ« grumbull tĂ« madh, prandaj dĂ«rgimi i mesazheve pĂ«rmes rrjetit mund tĂ« harrohet. Ka gjĂ«ra si micropython.schedule(), qĂ« lejojnĂ« tĂ« filloni njĂ« ndonjĂ« funksion "sa herĂ« qĂ« ndodh", por lind pyetja "cila Ă«shtĂ« pĂ«rfitimi?". Papritmas mund tĂ« dĂ«rgojmĂ« ndonjĂ« mesazh, dhe pastaj njĂ« ndĂ«rprerje ndĂ«rhyn dhe prish vlerat e variablave. Ose, pĂ«r shembull, njĂ« vlerĂ« e re e numrit ka arritur nga serveri derisa ende nuk kemi regjistruar vlerĂ«n e vjetĂ«r. NĂ« pĂ«rgjithĂ«si, duhet tĂ« organizoni sinkronizimin ose tĂ« gjeni njĂ« mĂ«nyrĂ« tjetĂ«r.

Dhe koha pas kohe ndodh një RuntimeError: schedule stack full dhe kush e di pse?

Me pyetje të qarta dhe uasync është më e bukur dhe e besueshme në këtë rast.

E kam nxjerrë punën me EEPROM në një klasë të vogël.

class EEPROM():
    i2c_addr = const(80)

    def __init__(self, i2c):
        self.i2c = i2c
        self.i2c_buf = bytearray(4) # Shmangni krijimin/destrojimin e tamponit në çdo thirrje


    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)

Në Python është pak e vështirë të punosh direkt me byte, megjithatë pikërisht byte ruhet në memorje. Duhej të kisha ndërtuar një konvertim midis numrit të plotë dhe byte me përdorimin e bibliotekës ustruct.

Për të mos e kaluar çdo herë objektin I2C dhe adresën e memorie, e kam mbështjellë gjithçka në një klasë të vogël dhe të përshtatshme.

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)

Objekti I2C krijohet me këta parametra.

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

Po i qasje mĂ« interesante — implementimit tĂ« komunikimit me serverin pĂ«rmes MQTT. Nuk Ă«shtĂ« e nevojshme tĂ« implementohet protokolli vetĂ« — nĂ« hapĂ«sirĂ«n e internetit u gjet njĂ« implementim asinkron i gatshĂ«m.. KĂ«tĂ« do ta pĂ«rdorim.

Të gjitha gjërat më interesante janë mbledhur në klasën CounterMQTTClient, e cila bazohet në bibliotekën MQTTClient. Të fillojmë me periferinë.

#####################################
# 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))

Këtu krijohen dhe konfigurohen pinët e llambës dhe butonit, si dhe objektet e matësve të ujit të ftohtë dhe të nxehtë.

Me inkuadimin nuk është gjithçka kaq triviale.

    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())

PĂ«r tĂ« caktuar parametrat e funksionit tĂ« bibliotekĂ«s mqtt_as pĂ«rdoret njĂ« fjalor i madh me konfigurime tĂ« ndryshme — config. Pjesa mĂ« e madhe e konfigurimeve tĂ« paracaktuara na pĂ«rshtatet, por shumĂ« konfigurime duhet tĂ« caktohen qartĂ«. PĂ«r tĂ« mos i shkruar ato direkt nĂ« kod, i ruaj nĂ« njĂ« skedĂ« tekstuale config.txt. Kjo lejon ndryshimin e kodit pa ndikuar nĂ« konfigurime, si dhe krijimin e disa pajisjeve tĂ« njĂ«jta me parametra tĂ« ndryshĂ«m.

Blloku i fundit i kodit aktivizon disa korutina për shërbimin e funksioneve të ndryshme të sistemit. Ja, për shembull, korutina që shërben matësit.

    async def _counter_coro(self, counter, topic):
        # Publiko vlerën fillestare
        value = counter.value()
        await self.publish(topic, str(value))

        # Publiko çdo vlerë të re
        while True:
            value = await counter
            await self.publish_msg(topic, str(value))

Korutina nĂ« cikĂ«l pret njĂ« vlerĂ« tĂ« re tĂ« matĂ«sit dhe sa herĂ« qĂ« ajo shfaqet — dĂ«rgon njĂ« mesazh pĂ«rmes protokollit MQTT. Pjesa fillestare e kodit dĂ«rgon vlerĂ«n fillestare edhe nĂ«se uji nuk kalon pĂ«rmes matĂ«sit.

Klasa bazë MQTTClient e menaxhon veten, e iniciaton lidhjen përmes WiFi dhe rikonfigurimin e saj kur lidhja ndërpritet. Kur ndodhin ndryshime në gjendjen e lidhjes WiFi, biblioteka na informon me thirrjen e wifi_connection_handler.

    async def wifi_connection_handler(self, state):
        self.internet_outage = not state
        if state:
            self.dprint('WiFi është aktiv.')
            duration = ticks_diff(ticks_ms(), self.internet_outage_start) // 1000
            await self.publish_debug_msg('RikthyerPas', duration)
        else:
            self.internet_outages += 1
            self.internet_outage_start = ticks_ms()
            self.dprint('WiFi është jashtë.')
            
        await asyncio.sleep(0)

Funksioni është marrë direkt nga shembujt. Në këtë rast, ai numëron numrin e ndalesave (internet_outages) dhe kohën e tyre. Kur lidhja rikthehet, dërgohet në server koha e ndërprerjes.

PĂ«r ta thĂ«nĂ« tĂ« vĂ«rtetĂ«n, sleep i fundit nevojitet vetĂ«m pĂ«r ta bĂ«rĂ« funksionin asinkron — nĂ« bibliotekĂ« ai thirret pĂ«rmes await, ndĂ«rsa ndryshe mund tĂ« thirren vetĂ«m funksionet nĂ« trupin e tĂ« cilave ka njĂ« await tjetĂ«r.

Përveç lidhjes me WiFi, nevojitet gjithashtu të krijohet një lidhje me brokerin MQTT (serverin). Këtë e menaxhon gjithashtu biblioteka, ndërsa ne kemi mundësinë të bëjmë diçka të dobishme kur lidhja është e vendosur.

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

KĂ«tu abonoheni pĂ«r disa mesazhe — serveri tani ka mundĂ«sinĂ« tĂ« caktojĂ« vlerat aktuale tĂ« matĂ«sve duke dĂ«rguar njĂ« mesazh tĂ« pĂ«rshtatshĂ«m.

    def mqtt_msg_handler(self, topic, msg):
        topicstr = str(topic, 'utf8')
        self.dprint("Mesazhi MQTT që u mor: tema={}, mesazhi={}".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))

Kjo funksion ndihmon në përpunimin e mesazheve që vijnë, dhe në varësi të temës (emrit të mesazhit) përditëson vlerat e një nga numëruesit.

Një çift funksionesh ndihmëse

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

Ky funksion merret me dĂ«rgimin e mesazhit nĂ« rast se Ă«shtĂ« krijuar lidhja. NĂ«se nuk ka lidhje — mesazhi injorohet.

Ky është vetëm një funksion i përshtatshëm, i cili formon dhe dërgon mesazhe për debugging.

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

Ka shumë tekst, dhe ne ende nuk i kemi ndërruar dritat LED. Këtu është

    # 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)

Kam parashikuar 2 moda ndĂ«rrimi. NĂ«se lidhja Ă«shtĂ« e ndĂ«rprerĂ« (ose po krijohet), pajisja do tĂ« ndĂ«rronjĂ« me shpejtĂ«si. NĂ«se lidhja Ă«shtĂ« krijuar — pajisja ndĂ«rron çdo 5 sekonda. NĂ«se Ă«shtĂ« e nevojshme, kĂ«tu mund tĂ« realizohen dhe modifikime tĂ« tjera tĂ« ndĂ«rrimit.

Por drita LED është vetëm një shaka. Ne jemi gjithashtu në kërkim të ekranit.

    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)

Ky Ă«shtĂ« ajo qĂ« po flisja — sa e lehtĂ« dhe e pĂ«rshtatshme Ă«shtĂ« me korutina. Ky funksion i vogĂ«l pĂ«rshkruan TË GJITHA ndĂ«rveprimet me pĂ«rdoruesin. Korutina thjesht pret pĂ«r tĂ« shtypur butonin dhe aktivizon ekranin pĂ«r 3 sekonda. NĂ« ekran shfaqen tĂ« dhĂ«nat aktuale tĂ« numĂ«ruesve.

Ka edhe disa detaje tĂ« tjera. Ky Ă«shtĂ« funksioni qĂ« pĂ«rgatit (rindez) tĂ« gjitha kĂ«to punĂ«. Cikli kryesor merret vetĂ«m me dĂ«rgimin e informacionit tĂ« ndryshĂ«m pĂ«r debugging çdo minutĂ«. NĂ« pĂ«rgjithĂ«si, po e sjell si Ă«shtĂ« — nuk mendoj se ka nevojĂ« pĂ«r shumĂ« komente.

   async def main(self):
        while True:
            try:
                await self._connect_to_WiFi()
                await self._run_main_loop()
                    
            except Exception as e:
                self.dprint('Dështim global në komunikim: ', e)
                await asyncio.sleep(20)

    async def _connect_to_WiFi(self):
        self.dprint('Po lidhem me WiFi dhe 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('TĂ« lidhuar!')
        self.internet_outage = False

    async def _run_main_loop(self):
        # Cikli i pafund
        mins = 0
        while True:
            gc.collect()  # Për statistikat e RAM-it.
            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("Ndodhi një përjashtim: ", e)
            mins += 1

            await asyncio.sleep(60)

Dhe disa cilësime dhe konstanta të tjera për mbulimin e përshkrimit

#####################################
# 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)

Kjo fillohet kështu

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

Ka diçka me memorjen time

Pra, tĂ« gjithĂ« kodi Ă«shtĂ« kĂ«tu. SkedarĂ«t i ngarkoja me utilitarin ampy — ai lejon ngarkimin e tyre nĂ« flash-in e brendshĂ«m (atĂ« qĂ« Ă«shtĂ« nĂ« ESP-07) dhe pastaj tĂ« mbĂ«shtetet nga programi si skedarĂ« tĂ« zakonshĂ«m. Po ashtu ngarkoja bibliotekat mqtt_as, uasyncio, ssd1306 dhe collections (e cila pĂ«rdoret brenda mqtt_as).

E nisim dhe
 marrim MemoryError. Dhe sa më shumë përpiqesha të kuptoja se ku po iku memorja, aq më herët shfaqej kjo gabim. Një kërkim i shkurtër në Google më solli në kuptimin se në mikro-kontroller, ekzistojnë vetëm 30kB memorie, në të cilën 65 kB kod (me bibliotekat) nuk mund të strehohen.

Por ka njĂ« zgjidhje. Doli se micropython nuk e ekzekuton kodin direkt nga skedari .py — ky skedar fillimisht kompilohen. Shkurt, ai kompilohen pĂ«rmes mikro-kontrollerit, duke u shndĂ«rruar nĂ« kodin bajt, i cili pastaj ruhet nĂ« memorie. Por gjithashtu, pĂ«r funksionimin e kompilerit Ă«shtĂ« e nevojshme njĂ« sasi e caktuar memorie.

Truku qëndron në faktin se duhet ta çlirosh mikro-kontrollerin nga kompilimi që konsumon shumë burime. Mund të kompilosh skedarët në një kompjuter të madh, dhe pastaj të ngarkosh kodin e gatshëm bajt në mikro-kontroller. Për këtë duhet të shkarkosh firmwarin micropython dhe të krijosh utilitarin mpy-cross.

Nuk shkrova Makefile, por kalova dorazi dhe kompilova të gjithë skedarët e nevojshëm (duke përfshirë bibliotekat) në këtë mënyrë

mpy-cross water_counter.py

Në fund duhet të ngarkohet skedarët me zgjerimin .mpy, duke mos harruar të fshihet përpara përkatësin .py nga sistemi i skedarëve të pajisjes.

E gjithë zhvillimi e kam bërë në programin (IDE?) ESPlorer. Ajo lejon ngarkimin e skripteve në mikrocontroler dhe menjëherë ekzekutimin e tyre. Në rastin tim, e gjithë logjika dhe krijimi i të gjithë objekteve ndodhen në skedarin water_counter.py (.mpy). Por që të gjithë të nisnin automatikisht në fillim, duhet të ketë një skedar tjetër me emrin main.py. Dhe ky duhet të jetë pikërisht .py, jo një .mpy të parapergatitur. Ja përmbajtja e tij triviale.

import water_counter

E nisim — gjithçka funksionon. Por memoria e lirĂ« Ă«shtĂ« shqetĂ«suese e vogĂ«l — rreth 1kB. Kam edhe plane pĂ«r zgjerimin e funksionalitetit tĂ« pajisjes, dhe ky kilobajt Ă«shtĂ« me siguri i pamjaftueshĂ«m. Por duket se ka njĂ« zgjidhje edhe pĂ«r kĂ«tĂ« rast.

Çështja Ă«shtĂ« kĂ«shtu. Edhe pse skedarĂ«t janĂ« kompaktuar nĂ« bytecode dhe ndodhen nĂ« sistemin e brendshĂ«m tĂ« skedarĂ«ve, nĂ« tĂ« vĂ«rtetĂ« ata ngarkohen nĂ« memorjen operative dhe ekzekutohen nga aty. Por, micropython di tĂ« ekzekutojĂ« bytecode direkt nga memoria flash, por pĂ«r kĂ«tĂ« duhet ta integrosh atĂ« direkt nĂ« firmware. Kjo nuk Ă«shtĂ« e komplikuar, megjithatĂ« nĂ« netbook-un tim mori njĂ« kohĂ« tĂ« konsiderueshme (aty kam gjeneruar Linux).

Algoritmi është ky:

  • Shkarko dhe instalo ESP Open SDK. Kjo gjĂ« ndĂ«rton kompiluesin dhe bibliotekat pĂ«r programet pĂ«r ESP8266. NdĂ«rtohet sipas udhĂ«zimeve nĂ« faqen kryesore tĂ« projektit (unĂ« zgjodha instalimin STANDALONE=yes)
  • Shkarkoni burimet micropython
  • Bibliotekat e nevojshme duhet tĂ« vendosen nĂ« ports/esp8266/modules brenda strukturĂ«s sĂ« micropython
  • NdĂ«rto firmware sipas udhĂ«zimeve nĂ« skedarin ports/esp8266/README.md
  • Ngarko firmware nĂ« mikrocontroler (unĂ« e bĂ«j kĂ«tĂ« nĂ« Windows me programet ESP8266Flasher ose me esptool-n python)

Tani, ‘import ssd1306’ do ngrejĂ« kodin direkt nga firmware dhe memorja operative nuk do shpenzohet pĂ«r kĂ«tĂ«. Me kĂ«tĂ« truk kam ngarkuar nĂ« firmware vetĂ«m kodin e bibliotekave, ndĂ«rsa kodi kryesor i programit ekzekutohet nga sistemi i skedarĂ«ve. Kjo lejon tĂ« modifikosh lehtĂ« programin pa e riparĂ« firmware-n. NĂ« kĂ«tĂ« moment, kam rreth 8.5kB RAM tĂ« lirĂ«. Kjo do tĂ« lejojĂ« realizimin e njĂ« numri tĂ« konsiderueshĂ«m funksionesh tĂ« dobishme nĂ« tĂ« ardhmen. Dhe nĂ«se memoria tĂ« jetĂ« krejt e pamjaftueshme, mund ta vendos edhe programin kryesor nĂ« firmware.

Dhe çfarë të bëj tani me këtë?

Ok, hardueri Ă«shtĂ« i montuar, firmware Ă«shtĂ« shkruar, kutia Ă«shtĂ« printuar, pajisja Ă«shtĂ« ngjitur nĂ« mur dhe gĂ«zon njĂ« dritĂ« qĂ« ndriçon. Por deri tani, Ă«shtĂ« njĂ« kuti e zezĂ« (nĂ« kuptimin e drejtpĂ«rdrejtĂ« dhe figurativ) dhe pĂ«rfitimi nga ajo ende Ă«shtĂ« shumĂ« i vogĂ«l. ËshtĂ« koha tĂ« bĂ«jmĂ« diçka me mesazhet MQTT qĂ« dĂ«rgohen nĂ« server.

ShtĂ«pia ime “inteligjente” operon me sistemin Majordomo. Moduli MQTT ndoshta ekziston nga kutia, ose lehtĂ« instalohet nga tregu i shtesave — nuk e mbaj mend se si kam marrĂ« atĂ«. MQTT nuk Ă«shtĂ« njĂ« sistem i pavarur — nevojitet njĂ« broker — njĂ« server, qĂ« pranon, kategorizon dhe pĂ«rcjell mesazhet MQTT pĂ«r klientĂ«t. UnĂ« pĂ«rdor mosquitto, i cili (si dhe majordomo) gjithashtu funksionon nĂ« tĂ« njĂ«jtin netbook.

Pas dërgimit të një mesazhi nga pajisja, vlera menjëherë shfaqet në listë.

Lidhim matësin e ujit me shtëpinë inteligjente

KĂ«to vlera tani mund tĂ« lidhen me objektet e sistemit, mund tĂ« pĂ«rdoren nĂ« skenarĂ« automatizimi dhe t'u nĂ«nshtrohen analizave tĂ« ndryshme — gjithçka kjo Ă«shtĂ« jashtĂ« skopit tĂ« kĂ«tij arti. PĂ«r ata qĂ« janĂ« tĂ« interesuar pĂ«r sistemin majordomo, mund t'u rekomandoj kanalin Elektronika NĂ« Objektiv — mik qĂ« gjithashtu ndĂ«rton shtĂ«pi inteligjente dhe shpjegon qartĂ« konfigurimin e sistemit.

Do të tregoj vetëm disa grafika. Ky është një grafik i thjeshtë i vlerave për një ditë

Lidhim matësin e ujit me shtëpinë inteligjente
Dhe duket se gjatĂ« natĂ«s, pak kush ka pĂ«rdorur ujĂ«. Disa herĂ« di kush ka shkuar nĂ« tualet, dhe duket se filtri i osmozĂ«s sĂ« revers Ă«shtĂ« duke thithur disa litra gjatĂ« natĂ«s. NĂ« mĂ«ngjes, pĂ«rdorimi rritet ndjeshĂ«m. Zakonisht unĂ« pĂ«rdor ujĂ« nga bojleri, por kĂ«tu kam dashur tĂ« bĂ«j njĂ« banjĂ« dhe pĂ«rkohĂ«sisht e kam kaluar nĂ« ujĂ« tĂ« nxehtĂ« nga qyteti — kjo gjithashtu duket qartĂ« nĂ« grafik.

Nga ky grafik kam mĂ«suar se shkuarja nĂ« tualet kĂ«rkon 6-7 litra ujĂ«, tĂ« bĂ«sh dush — 20-30 litra, tĂ« lajĂ« enĂ«t rreth 20 litra, dhe pĂ«r tĂ« bĂ«rĂ« njĂ« banjĂ« nevojitet 160 litra. GjatĂ« ditĂ«s, familja ime konsumon rreth 500-600 litra.

Për ata që janë veçanërisht kuriozë, mund të shikoni regjistrimet për çdo vlerë të veçantë

Lidhim matësin e ujit me shtëpinë inteligjente

Nga kjo kuptova se kur rubineti është i hapur, uji rrjedh me shpejtësi rreth 1 litër për 5 sekonda.

Por në këtë formë, ndoshta, statistika nuk është shumë e lehtë për t'u parë. Në majordomo ka gjithashtu mundësi për të parë grafikët e konsumit për ditë, javë dhe muaj. Ja, për shembull, grafik i konsumit në kolona.

Lidhim matësin e ujit me shtëpinë inteligjente

Aktualisht kam vetĂ«m tĂ« dhĂ«na pĂ«r njĂ« javĂ«. Pas njĂ« muaji, ky grafik do tĂ« jetĂ« mĂ« informues — çdo ditĂ« do tĂ« ketĂ« njĂ« kolonĂ« tĂ« veçantĂ«. Disi e prishin pamjen korrigjimet e vlerave qĂ« i fut manualisht (kolona mĂ« e madhe). Dhe ende nuk Ă«shtĂ« e qartĂ« nĂ«se kam vendosur gabim vlerat e para, njĂ« kub mĂ« pak, apo nĂ«se Ă«shtĂ« njĂ« gabim nĂ« firmware dhe nuk tĂ« gjitha litrat u pĂ«rfshinĂ«. MĂ« duhet mĂ« shumĂ« kohĂ«.

Duhet tĂ« punoj akoma mbi grafikĂ«t, t'i zbardohem, t'i pikturoj. Ndoshta do tĂ« ndĂ«rtoj njĂ« grafik tĂ« konsumit tĂ« memories pĂ«r qĂ«llime debugimi — ndoshta ka ndonjĂ« rrjedhje. Mund tĂ« shfaqin ndonjĂ« periudhĂ« kur interneti mungonte. Deri tani, e gjithĂ« kjo Ă«shtĂ« nĂ« nivelin e ideve.

Përfundimi

Sot, apartamenti im u bĂ« pak mĂ« i mençur. Me kĂ«tĂ« pajisje tĂ« vogĂ«l, do tĂ« jetĂ« mĂ« lehtĂ« tĂ« ndjek konsumimin e ujit nĂ« shtĂ«pi. NĂ«se mĂ« parĂ« kisha ankesa mbi “sĂ«rish shumĂ« ujĂ« u konsumua gjatĂ« muajit”, tani do tĂ« mund tĂ« gjej burimin e kĂ«tij konsumimi.

Dikujt mund t'i duket e çuditshme të shikojë shifrat në ekran, kur është një metër larg nga treguesi vetë. Por në një të ardhme jo shumë të largët, planifikoj të traslad dhe të shkoj në një apartament tjetër, ku do të ketë disa stela uji dhe treguesit vetë, shumë për të gjitha mund të jenë të vendosur në shkallë. Kështu që pajisja për leximin e largët të treguesve do të jetë shumë e dobishme.

Funksionalitetin e pajisjes dëshiroj ta shtrij gjithashtu. Kam filluar të shikoj ventila motorike. Aktualisht, për të kaluar nga uji i qytetit në bojler, më duhet të kthej 3 rubineta në një disheku të vështirë për t'u arritur. Do të ishte shumë më e përshtatshme ta bëja me një buton me përkatësin përkatës. Natyrisht, duhet të realizohet gjithashtu mbrojtja nga rrjedhjet.

NĂ« artikull, pĂ«rshkrova variantin tim tĂ« pajisjes qĂ« bazohet nĂ« ESP8266. Sipas mendimit tim, kam arritur nĂ« njĂ« variant tĂ«rheqĂ«s tĂ« firmware nĂ« micropython me pĂ«rdorimin e korutinave — e thjeshtĂ« dhe e bukur. Punoha qĂ« tĂ« pĂ«rshkruaja shumĂ« nuanca dhe gabime me tĂ« cilat u pĂ«rballa gjatĂ« rrugĂ«s. Ndoshta e kam pĂ«rshkruar shumĂ« nĂ« detaje, personalisht mĂ« duket mĂ« e lehtĂ« si lexues ta kaloj pa lexuar tĂ« tepĂ«rta sesa mĂ« pas tĂ« mendoj atĂ« qĂ« nuk Ă«shtĂ« thĂ«nĂ«.

Si gjithmonë, jam i hapur për kritika konstruktive.

Kod i burimit
Diagrami dhe blloku
Modeli i korpusit

Burimi: habr.com

Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« đŸ”„ Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« | ProHoster