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

Dikur, sistemet e automatizimit të shtëpisë, ose siç quhen shpesh "shtëpia inteligjente", ishin jashtëzakonisht të shtrenjta dhe vetëm të pasurit mund t'i përballonin ato. Sot në treg mund të gjeni sete mjaft buxhetore me sensorë, butona/kështu që dhe pajisje ekzekutuese për të menaxhuar ndriçimin, prizat, ventilimin, furnizimin me ujë dhe konsumatorë të tjerë. Edhe DIY-shi më i papërvojë mund të lidhet me mrekullinë dhe të mbledhë pajisje për shtëpinë inteligjente për pak para.

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

Si rregull, pajisjet e ofruara janë ose sensorë, ose mekanizma ekzekutues. Ato lejojnë të realizohen lehtësisht skenarë si "kur aktivizohet sensori i lëvizjes, ndiz dritën" ose "ndërruesi afër daljes fik dritën në të gjithë apartamentin". Por me telemetrinë diçka nuk u arrit. Në rastin më të mirë, është një grafik i temperaturës dhe lagështisë, ose fuqia e menjëhershme në një prizë të caktuar.

Së fundmi, kam instaluar matës të ujit me dalje impulsive. Pasi çdo litër që kalon përmes matësit aktivizon një kontak dhe mbyll kontaktin. E gjithë çështja tani është të lidhem me kabllot dhe të provoj të nxjerr disa përfitime nga kjo. Për shembull, analizimi i konsumit të ujit sipas orëve dhe ditëve të javës. Dhe nëse ka disa sola për ujë në apartament, është më komode të shohësh të gjitha treguesit aktualë në një ekran, sesa të ngjitesh në vende të vështira me një ndriçues.

Poshtë do të gjesh versionin tim të pajisjes bazuar në ESP8266, e cila numëron impulsat nga matësit e ujit dhe dërgon të dhënat në serverin e shtëpisë inteligjente përmes MQTT. Do ta programojmë në micropython duke përdorur bibliotekën uasyncio. Gjatë krijimit të firmware-it, natyrisht, u ndesha me disa vështirësi interesante, të cilat do t'i ndaj gjithashtu në këtë artikull. Le të fillojmë!

Skema

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

Zgjedhja qendrore e gjithĂ« skemĂ«s Ă«shtĂ« moduli me mikroprocesorin ESP8266. Fillimisht kishte planifikuar ESP-12, por ana ime doli e defektuar. Duhej tĂ« kĂ«naqem me modulin ESP-07 qĂ« ishte nĂ« dispozicion. FatmirĂ«sisht, ato janĂ« tĂ« njĂ«jta si pĂ«r pinĂ«t, ashtu edhe pĂ«r funksionalitetin; diferenca Ă«shtĂ« vetĂ«m nĂ« antenĂ« — ESP-12 ka antenĂ« tĂ« integruar, ndĂ«rsa ESP-07 ka antenĂ« tĂ« jashtme. SidoqoftĂ«, edhe pa antenĂ«, sinjali WiFi kapet normalisht nĂ« banjon time.

Rrethimi i modulit është standart:

  • butoni i resetimit me tĂ«rheqje dhe kapacitor (edhe pse kĂ«to janĂ« brenda modulit)
  • Sinjali enable (CH_PD) tĂ«rhequr nĂ« energji
  • GPIO15 Ă«shtĂ« tĂ«rhequr nĂ« tokĂ«. Kjo Ă«shtĂ« e nevojshme vetĂ«m gjatĂ« seancĂ«s sĂ« nisjes, por unĂ« nuk kam ndonjĂ« gjĂ« pĂ«r tĂ« lidhur mĂ« shumĂ« nĂ« kĂ«tĂ« pin.

Për të kaluar modulin në mënyrën e programimit, duhet të lidhësh GPIO2 në tokë, dhe për ta bërë më të lehtë, kam parashikuar një buton Boot. Në gjendje normale, ky pin tërhiqet në energji.

Gjendja e vijĂ«s GPIO2 kontrollohet vetĂ«m nĂ« fillim tĂ« funksionimit — kur ndodhet energjia ose menjĂ«herĂ« pas rinitjes. KĂ«shtu moduli ngarkohet si zakonisht ose kalon nĂ« mĂ«nyrĂ«n e programimit. Pas ngarkimit, ky pin mund tĂ« pĂ«rdoret si njĂ« GPIO normal. Dhe pasi qĂ« ka njĂ« buton, mund ta lidhim me ndonjĂ« funksion tĂ« dobishĂ«m.

Për programimin dhe debugimin do të përdor UART-in, të cilin e kam çuar në shiritin e lidhjeve. Kur më nevojitet, thjesht e lidh USB-UART konvertorin. Duhet vetëm të mos harroj se moduli ka nevojë për 3.3V. Nëse harroj ta kaloj konvertorin në këtë tension dhe i jap 5V, moduli do të digjet.

Nuk kam probleme me elektricitetin nĂ« banjĂ« — prizat janĂ« tĂ« vendosura rreth njĂ« metri nga numĂ«ruesit, kĂ«shtu qĂ« do ta lidh me 220V. Si burim energjie do tĂ« pĂ«rdor njĂ« blok HLK-PM03 nga Tenstar Robot. UnĂ« kam vĂ«shtirĂ«si me elektronikĂ«n analoge dhe energjike, dhe kĂ«tu ka njĂ« burim energjie tĂ« gatshĂ«m nĂ« njĂ« trup tĂ« vogĂ«l.

PĂ«r sinjalizimin e mĂ«nyrave tĂ« funksionimit kam parashikuar njĂ« LED, tĂ« lidhur me GPIO2. MegjithatĂ«, nuk e kam ndĂ«rtuar atĂ«, pasi moduli ESP-07 ka tashmĂ« njĂ« LED, i cili Ă«shtĂ« gjithashtu i lidhur me GPIO2. Por nĂ« pllakĂ« le tĂ« jetĂ« — ndoshta do tĂ« dĂ«shiroj ta lidh atĂ« LED nĂ« trup.

Tani kalojmĂ« nĂ« atĂ« qĂ« Ă«shtĂ« mĂ« interesante. NumĂ«ruesit e ujit nuk kanĂ« asnjĂ« logjikĂ«, nuk mund t'i pyesim pĂ«r vlerat aktuale. E vetmja gjĂ« qĂ« kemi nĂ« dispozicion janĂ« impulset — kontaktet e mbylljes sĂ« herkonit pĂ«r çdo litĂ«r. Daljet e herkonĂ«ve janĂ« tĂ« lidhura nĂ« GPIO12/GPIO13. Rezistori tĂ«rheqĂ«s do ta aktivizoj programikisht brenda modulit.

Fillimisht harrova të parashikoj rezistorët R8 dhe R9 dhe në variantin tim të pllakës ato nuk ekzistojnë. Por tani që po e ndaj skemën për publikun, është e nevojshme ta rregulloj këtë gabim. Rezistorët janë të nevojshëm për të mos djegur portin në rast se programimi bllokohet dhe vendos njësi në pin, dhe herkoni e lidh këtë linjë me tokën (me rezistori do të kalojë maksimumi 3.3V/1000Ω = 3.3mA).

ËshtĂ« koha tĂ« mendojmĂ« se çfarĂ« tĂ« bĂ«jmĂ« nĂ«se ndalet energjia elektrike. Varianti i parĂ« — nĂ« fillim tĂ« kĂ«rkojmĂ« nga serveri vlerat fillestare tĂ« matĂ«sve. Por kjo do tĂ« kĂ«rkonte njĂ« komplikuar tĂ« konsiderueshĂ«m tĂ« protokollit tĂ« shkĂ«mbimit. MĂ« shumĂ« se kaq, funksionaliteti i pajisjes nĂ« kĂ«tĂ« rast varet nga gjendja e serverit. NĂ«se pas ndalimit tĂ« dritave serveri nuk ndizet (ose ndizet mĂ« vonĂ«), atĂ«herĂ« matĂ«si i ujit nuk do tĂ« mund tĂ« kĂ«rkonte vlerat fillestare dhe do tĂ« punonte gabim.

Prandaj vendosa tĂ« realizoj ruajtjen e vlerave tĂ« matĂ«sve nĂ« njĂ« çip memorjeje, i lidhur me I2C. Nuk kam kĂ«rkesa tĂ« veçanta pĂ«r pĂ«rmasat e memories flash — duhet tĂ« ruaj vetĂ«m 2 numra (sasia nĂ« litra nga matĂ«sit e ujit tĂ« nxehtĂ« dhe tĂ« ftohtĂ«). Edhe moduli mĂ« i vogĂ«l do tĂ« pĂ«rshtatej. Por duhet t'i kushtohet vĂ«mendje numrit tĂ« cikleve tĂ« shkrimit. Te shumica e moduleve ky Ă«shtĂ« 100 mijĂ« cikle, te disa deri nĂ« njĂ« milion.

Duket se një milion është shumë. Por gjatë 4 viteve që kam jetuar në apartamentin tim kam konsumuar pak më shumë se 500 kub ujë, që janë 500 mijë litra! Dhe 500 mijë regjistrime në flash. Dhe kjo është vetëm uji i ftohtë. Natyrisht mund të riparoj çipin çdo disa vite, por doli që ekzistojnë çipet FRAM. Nga pikëpamja e programimit, këto janë të njëjta me I2C EEPROM, vetëm se me një numër shumë të madh ciklesh rishkrimi (qindra miliona). Mirëpo deri tani nuk kam arritur të shkoj në dyqan për këto çipe, pra për momentin do të mbetet e zakonshmja 24LC512.

Tabela e shtypjes

Fillimisht planifikuar të bëja tabelën në kushte shtëpie. Prandaj tabela u projektua si njëra anësore. Por pas një ore duke u munduar me një hekur lazer dhe maskë saldimi (pa të si duket s'është si duhet), vendosa ta porosis tabelën nga kinezët.

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

TĂ« ndjemĂ« pothuajse pĂ«r ta porositur tabelĂ«n, kuptova se pĂ«rveç çipit tĂ« memories flash nĂ« busin I2C mund tĂ« lidhja diçka tjetĂ«r tĂ« dobishme, pĂ«r shembull njĂ« ekran. ÇfarĂ« tĂ« shfaqja nĂ« tĂ« — ende Ă«shtĂ« njĂ« pyetje, por duhet ta ndarĂ« nĂ« tabelĂ«. Dhe pĂ«r faktin qĂ« kam planifikuar tĂ« porosis tabela nga fabrika, nuk kishte kuptim tĂ« kufizoja veten me njĂ« tabelĂ« njĂ« anĂ«sore, pra linjat nĂ« I2C — janĂ« tĂ« vetmet nĂ« anĂ«n e pasme tĂ« tabelĂ«s.

Një problem i madh lidhej gjithashtu me dizajnimin me një anë. Pasi karta ishte projektuar si njëanshëm, rrugicat dhe komponentët SMD ishin planifikuar të vendoseshin nga njëra anë, ndërsa komponentët e daljes, lidhësit dhe burimi i energjisë nga ana tjetër. Kur pas një muaji mora kartat, harrova planin fillestar dhe solderova të gjitha komponentët në anën e përparme. Dhe vetëm kur arrita te ndihma e burimit të energjisë u zbulua se plus dhe minus ishin të përziera. Duhej të improvizoja me lidhëse. Në imazhin e mësipërm, kam ndryshuar tashmë baterinë, por toka kalon nga një pjesë e kartës në tjetrën përmes pikave të butonit Boot (edhe pse mund të isha bërë një rrugicë në nivelin e dytë).

Ja si doli

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

Kasa

Hapi tjetĂ«r — kutia. Me njĂ« printer 3D, kjo nuk Ă«shtĂ« problem. Nuk u lodha shumĂ« — thjesht vizatova njĂ« kuti me pĂ«rmasat e nevojshme dhe bĂ«ra prerjet nĂ« vendet e nevojshme. Kapaku bashkohet me kutinĂ« me vida tĂ« vogla.

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

TashmĂ« e pĂ«rmenda se butoni Boot mund tĂ« pĂ«rdoret si buton universale — pra, do ta nxjerrim atĂ« nĂ« panelin e pĂ«rparmĂ«. PĂ«r kĂ«tĂ« vizatova njĂ« 'pajisje' speciale ku ndodhet butoni.

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

Brenda kutisë gjithashtu ndodhen mbetje, mbi të cilat vendoset karta dhe sigurohet me një vidë M3 (në kartë nuk kishte më shumë hapësirë).

Ekranin e pĂ«rzgjodha kur printova versionin e parĂ« tĂ« kutisĂ«. Ekrani standard me dy rreshta nuk pĂ«rmbushte sasinĂ« e nevojshme, por nĂ« seksionin e brendshĂ«m zbulova njĂ« ekran OLED SSD1306 128×32. I vogĂ«l, por nuk Ă«shtĂ« e nevojshme ta shoh çdo ditĂ« — do tĂ« bĂ«jĂ«.

Duke llogaritur nĂ« tĂ« gjitha mĂ«nyrat si do tĂ« lidhen kabllot, vendosa ta vendos ekranin nĂ« mes tĂ« kutisĂ«. Ergonomika, natyrisht, Ă«shtĂ« shumĂ« e dobĂ«t — butoni lart, ekrani poshtĂ«. Por tashmĂ« kam thĂ«nĂ« se ideja pĂ«r tĂ« vendosur ekranin erdhi shumĂ« vonĂ« dhe nuk kisha dĂ«shirĂ« tĂ« riorganizoja kartĂ«n pĂ«r tĂ« zhvendosur butonin.

Pajisja e përbërë. Moduli i ekranit është ngjitur me qilim ngjitës.

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Ă« nĂ« pjesĂ«n programore. PĂ«r kĂ«to lloje tĂ« vogla tĂ« pajisjeve, mĂ« pĂ«lqen shumĂ« tĂ« pĂ«rdor gjuhĂ«n Python (micropython)- kodi rezulton shumĂ« kompakt dhe i kuptueshĂ«m. FatmirĂ«sisht, kĂ«tu nuk ka nevojĂ« tĂ« zbret nĂ« nivelin e regjistrave pĂ«r tĂ« nxjerrĂ« mikrosekondat — gjithçka mund tĂ« bĂ«het nga Python.

Duket si gjithçka e thjeshtĂ«, por nuk Ă«shtĂ« — nĂ« pajisje janĂ« tĂ« planifikuara disa funksione tĂ« pavarura:

  • PĂ«rdoruesi shtyp nĂ« buton dhe shikon nĂ« ekran
  • Litrat kalojnĂ« dhe pĂ«rditĂ«sojnĂ« vlerat nĂ« kujtesĂ«n flash
  • Moduli monitoron sinjalin WiFi dhe ri-konnektohet nĂ«se Ă«shtĂ« e nevojshme
  • Po pa dritĂ«n qĂ« shkĂ«lqen, gjithçka do ishte e pamundur

Nuk duhet lejuar qĂ« njĂ« funksion tĂ« mos funksionojĂ«, nĂ«se tjetri ndalon pĂ«r ndonjĂ« arsye. Kam pasur mjaft eksperiencĂ« me probleme nĂ« projekte tĂ« tjera dhe tani e imagjinoj se çfarĂ« problematikash ndodhin si “kaluam njĂ« litrĂ«, sepse nĂ« atĂ« moment po pĂ«rditĂ«sohej ekrani” ose “pĂ«rdoruesi nuk mund tĂ« bĂ«jĂ« asgjĂ« derisa moduli tĂ« lidhĂ«t me WiFi”. Sigurisht, disa gjĂ«ra mund tĂ« bĂ«hen pĂ«rmes ndĂ«rprerjeve, por mund tĂ« hasim nĂ« kufizimin e qĂ«ndrueshmĂ«risĂ«, thellĂ«sisĂ« sĂ« thirrjeve ose ndryshimeve jo atomike tĂ« variablave. Dhe po, kodi qĂ« merret me gjithçka nĂ« tĂ« njĂ«jtĂ«n kohĂ« shpejt shndĂ«rrohet nĂ« njĂ« kaos.

NĂ« projekti Ă«shtĂ« mĂ« serioz kam pĂ«rdorur multitasking klasike me ndĂ«rrim dhe FreeRTOS, por nĂ« kĂ«tĂ« rast modeli mĂ« i pĂ«rshtatshĂ«m ishte kooperat (coroutines) dhe biblioteka uasync . PĂ«r mĂ« tepĂ«r, implementimi nĂ« Python i kooperatave Ă«shtĂ« thjesht fantastik — gjithçka Ă«shtĂ« e thjeshtĂ« dhe e rehatshme pĂ«r programuesin. Thjesht shkruaj logjikĂ«n tĂ«nde, vetĂ«m thuaj nĂ« cilat vende mund tĂ« kalosh ndĂ«rmjet lĂ«ngjeve.

Dallimet midis multitasking me ndërrim dhe multitasking konkurrues i sugjeroj të studiohen si opsion i zgjedhjes. 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 pĂ«rpunhet nga njĂ« instancĂ« e klasĂ«s Counter. SĂ« pari, vlera fillestare e numrit lexoohet nga EEPROM (storage_vlera) — kĂ«shtu realizohet rinovimi pas humbjes sĂ« energjisĂ«.

Pin fillohet me tĂ«rheqje tĂ« integruar: nĂ«se sensori Ă«shtĂ« i mbyllur — nĂ« linjĂ« Ă«shtĂ« zero, nĂ«se Ă«shtĂ« i hapur, linja tĂ«rhiqet nĂ« energji dhe kontrolluesi lexon njĂ«si.

Gjithashtu kĂ«tu startohet njĂ« detyrĂ« e veçantĂ«, e cila do tĂ« realizojĂ« anketimin e pinit. Çdo numĂ«r do tĂ« startojĂ« detyrĂ«n e tij. Ja kodi i saj

    """ Anketoni pinin dhe avanconi vlerën kur kalon një litrë tjetër """\n    async def _switchcheck(self):\n        last_checked_pin_state = self._pin.value()  # Merrni gjendjen fillestare\n\n        # Anketoni për një ndryshim në pin\n        while True:\n            state = self._pin.value()\n            if state != last_checked_pin_state:\n                # Gjendja ka ndryshuar: veproni tani.\n                last_checked_pin_state = state\n                if state == 0:\n                    self._another_litre_passed()\n\n            # Injoroni ndryshimet e mëtejshme të gjendjes derisa switches të stabilizohen\n            await asyncio.sleep_ms(Counter.debounce_ms)

NjĂ« vonesĂ« prej 25ms Ă«shtĂ« e nevojshme pĂ«r filtĂ«rimin e zhurmĂ«s sĂ« kontakteve dhe gjithashtu rregullon sa shpesh zgjohet detyra (derisa kjo detyrĂ« fle, punojnĂ« detyra tĂ« tjera). Çdo 25ms funksioni zgjohet, kontrollon pinin dhe nĂ«se kontaktet e herkonit janĂ« tĂ« mbyllura, atĂ«herĂ« do tĂ« thotĂ« se njĂ« litĂ«r ka kaluar pĂ«rmes numĂ«ruesit dhe kjo duhet tĂ« pĂ«rpunohet.

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

        self._value_storage.write(self._value)

PĂ«rpunimi i litrit tĂ« ardhshĂ«m Ă«shtĂ« trivial – thjesht rritet numĂ«ruesi. Dhe do tĂ« ishte mirĂ« qĂ« vlera e re 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 do të shfrytëzojmë bukuritë e pythonit dhe bibliotekës uasync dhe do të bëjmë që objekti numërues të jetë i pritur (si ta përkthejmë 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 shumĂ« i dobishĂ«m, qĂ« pret derisa vlera e numĂ«ruesit tĂ« pĂ«rditĂ«sohet – funksioni zgjohet nga koha nĂ« kohĂ« dhe kontrollon flamurin _value_changed. KĂ«ndi i kĂ«tij funksioni Ă«shtĂ« se kodi qĂ« e thĂ«rret mund tĂ« flejĂ« nĂ« kĂ«tĂ« funksion dhe tĂ« flejĂ« deri sa tĂ« marrĂ« vlerĂ«n e re.

Por çfarĂ« ndodhi me ndĂ«rprerjet?Po, nĂ« kĂ«tĂ« pikĂ« mund tĂ« mĂ« trolloni, duke thĂ«nĂ« se vetĂ« e thashĂ« pĂ«r ndĂ«rprerjet, por nĂ« realitet bĂ«ra njĂ« pyetje tĂ« thjeshtĂ« tĂ« pin-it. NĂ« tĂ« vĂ«rtetĂ«, ndĂ«rprerjet janĂ« gjĂ«ja e parĂ« qĂ« provova. NĂ« ESP8266 mund tĂ« organizoni njĂ« ndĂ«rprerje sipas frontit, madje tĂ« shkruani njĂ« trajtues pĂ«r kĂ«tĂ« ndĂ«rprerje nĂ« python. NĂ« kĂ«tĂ« ndĂ«rprerje mund tĂ« pĂ«rditĂ«sohet vlera e variablit. Ndoshta, kjo do tĂ« mjaftonte nĂ«se numĂ«ruesi do tĂ« ishte njĂ« pajisje e varur — njĂ« qĂ« pret derisa tĂ« kĂ«rkohet vlera e saj.

Fatke mĂ« mirĂ« (apo keq?) pajisja ime Ă«shtĂ« aktive, ajo duhet tĂ« dĂ«rgojĂ« mesazhe pĂ«rmes protokollit MQTT dhe tĂ« regjistrojĂ« tĂ« dhĂ«nat nĂ« EEPROM. KĂ«tu fillojnĂ« kufizimet — nĂ« ndĂ«rprerje nuk mund tĂ« ndash memorie dhe tĂ« pĂ«rdorĂ«sh njĂ« stek tĂ« madh, qĂ« do tĂ« thotĂ« se mund tĂ« harrojmĂ« dĂ«rgimin e mesazheve pĂ«rmes rrjetit. Ka disa funksione si micropython.schedule(), qĂ« lejojnĂ« tĂ« fillosh ndonjĂ« funksion “sa mĂ« shpejt tĂ« jetĂ« e mundur”, por lind pyetja “çfarĂ« do tĂ« arrijmĂ«?”. Papritur po dĂ«rgojmĂ« ndonjĂ« mesazh dhe aty ndĂ«rhyn njĂ« ndĂ«rprerje dhe prish vlerat e variablĂ«ve. Ose, pĂ«r shembull, njĂ« vlerĂ« e re e numĂ«ruesit ka marrĂ« vesh nga serveri ndĂ«rsa ne akoma nuk e kemi shkruar vlerĂ«n e vjetĂ«r. Pra, duhet tĂ« ndĂ«rtojmĂ« njĂ« sinkronizim ose tĂ« gjejmĂ« njĂ« mĂ«nyrĂ« tjetĂ«r pĂ«r kĂ«tĂ«.

Po ashtu, herë pas here ndodh që del RuntimeError: schedule stack full dhe kush e di pse?

Me pyetjen e qartë dhe uasync, në këtë rast duket më elegant dhe më i besueshëm.

Puna me EEPROM e kam transferuar në një klasë të vogël.

class EEPROM():
    i2c_addr = const(80)

    def __init__(self, i2c):
        self.i2c = i2c
        self.i2c_buf = bytearray(4) # Shmang krijimin/zgjidhjen e buffers për ç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, të punosh drejtpërdrejt me byte është paksa e ndërlikuar, ndërsa pikërisht bajtat shkruhen në memorie. Dola në nevojë të ndërtoj një konvertim midis numrit të plotë dhe byte me ndihmën e bibliotekës ustruct.

Që të mos kaloj çdo herë objektin I2C dhe adresën e memories, e kam mbështjellë të gjithë këtë në një klasë të vogël dhe të rehatshme.

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ëto parametra.

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

KĂ«tu arrijmĂ« te gjĂ«ja mĂ« interesante — zbatimi i komunikimit me serverin pĂ«rmes MQTT. MirĂ«po, nuk Ă«shtĂ« e nevojshme tĂ« zbatojmĂ« protokollin vetĂ« — nĂ« hapĂ«sirat e internetit u gjet njĂ« implementim asinkron i gatshĂ«m.. Dhe atĂ« do ta pĂ«rdorim.

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

#####################################
# 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 konfiguroren pinët e llampave dhe butonit, si dhe objektet e numëruesve të ujit të ngrohtë dhe të ftohtë.

Me përgatitjen e nuk është gjithçka kaq e thjeshtë

    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Ă« vendosur parametrat e funksionimit tĂ« bibliotekĂ«s mqtt_as pĂ«rdoret njĂ« fjalor i madh me cilĂ«sime tĂ« ndryshme — config. Pjesa mĂ« e madhe e cilĂ«simeve pĂ«r default na pĂ«rshtatet, por shumĂ« cilĂ«sime duhet tĂ« japim eksplicite. NĂ« mĂ«nyrĂ« qĂ« tĂ« mos i shkruajmĂ« cilĂ«simet direkt nĂ« kod, unĂ« i ruaj ato nĂ« njĂ« skedar tekstual config.txt. Kjo lejon ndryshimin e kodit pa ndikuar nĂ« cilĂ«sime, si dhe tĂ« krijojmĂ« disa pajisje tĂ« ngjashme me parametra tĂ« ndryshĂ«m.

Blloku i fundit i kodit ekzekuton disa korutina për shërbimin e funksioneve të ndryshme të sistemit. Ja për shembull korutina që shërben për numëruesit

    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 pret nĂ« cikĂ«l njĂ« vlerĂ« tĂ« re nga numĂ«ruesi dhe sa herĂ« qĂ« ajo shfaqet — dĂ«rgon njĂ« mesazh pĂ«rmes protokollit MQTT. Pjesa e parĂ« e kodit dĂ«rgon vlerĂ«n fillestare edhe nĂ«se uji nuk po rrjedh pĂ«rmes numĂ«ruesit.

Klasa bazë MQTTClient e menaxhon veten, fillon vetë lidhjen përmes WiFi dhe rikonecton kur lidhja shkëputet. Kur ndodhin ndryshime në gjendjen e lidhjes WiFi, biblioteka na informon përmes thirrjes 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('RikonektuarPas', duration)
        else:
            self.internet_outages += 1
            self.internet_outage_start = ticks_ms()
            self.dprint('WiFi është i mbyllur.')
            
        await asyncio.sleep(0)

Funksioni është marrë në mënyrë të ndershme nga shembujt. Në këtë rast ai llogarit numrin e ndërprerjeve (internet_outages) dhe gjatësi e tyre. Kur rikthehet lidhja, një kohë e downtime dërgohet në server.

PĂ«r t'u thĂ«nĂ« tĂ« vĂ«rtetĂ«n, pushimi i fundit Ă«shtĂ« i nevojshĂ«m vetĂ«m pĂ«r ta bĂ«rĂ« funksionin asinkron — nĂ« bibliotekĂ« ai thirret pĂ«rmes await, ndryshe mund tĂ« thirren vetĂ«m funksionet qĂ« pĂ«rmbajnĂ« njĂ« await tjetĂ«r.

Përveç lidhjes WiFi, duhet të krijohet gjithashtu një lidhje me brokerin MQTT (serverin). Biblioteka merret me këtë gjithashtu, dhe na jep mundësinë për të bërë diçka të dobishme kur lidhja është 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 ne abonohemi nĂ« disa mesazhe — serveri tani ka mundĂ«sinĂ« tĂ« caktojĂ« vlerat aktuale tĂ« numĂ«ruesve duke dĂ«rguar mesazhin pĂ«rkatĂ«s.

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

Ky funksion trajton mesazhet që vijnë, dhe në varësi të temës (emrit të mesazhit) përditëson vlerat e njërit 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 lidhja është vendosur. Nëse nuk ka lidhje, mesazhi injorohet.

Dhe ky është thjesht një funksion i përshtatshëm që formon dhe dërgon mesazhe të debug.

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

Ka kaq shumë tekst, dhe ne ende s'kemi ndriçuar ledyme. Ja

    # 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 mĂ«nyra ndriçimi. NĂ«se lidhja Ă«shtĂ« humbur (ose vetĂ«m po vendoset), atĂ«herĂ« pajisja do tĂ« ndriçohet shpejt. NĂ«se lidhja Ă«shtĂ« vendosur — pajisja ndriçon çdo 5 sekonda. NĂ«se nevojitet, kĂ«tu mund tĂ« realizohen dhe mĂ«nyra tĂ« tjera ndriçimi.

Por ledyme është një lojë, ne kemi menduar gjithashtu për ekranin.

    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Ă« kam thĂ«nĂ« — sa e thjeshtĂ« dhe e pĂ«rshtatshme Ă«shtĂ« me korutinat. Kjo funksion e vogĂ«l pĂ«rshkruan TË GJITHA ndĂ«rveprimet me pĂ«rdoruesin. Korutina thjesht pret shtypjen e butonit dhe aktivizon ekranin pĂ«r 3 sekonda. NĂ« ekran shfaqen treguesit aktualĂ« tĂ« numĂ«ruesve.

Ka edhe disa detaje tĂ« vogla. KĂ«tu Ă«shtĂ« funksioni qĂ« e rinis kĂ«tĂ« tĂ« gjithĂ« punĂ«n. Cikli kryesor merret vetĂ«m me dĂ«rgimin e informacionit tĂ« ndryshĂ«m pĂ«r debugging çdo minutĂ«. NĂ« pĂ«rgjithĂ«si, po e jap siç Ă«shtĂ« — nuk mendoj se Ă«shtĂ« e nevojshme tĂ« komentoj shumĂ«.

   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 i komunikimit: ', e)
                await asyncio.sleep(20)

    async def _connect_to_WiFi(self):
        self.dprint('Po lidhemi 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('E lidhur!')
        self.internet_outage = False

    async def _run_main_loop(self):
        # Cicli përherë
        mins = 0
        while True:
            gc.collect()  # Për statistikën e RAM-it.
            mem_free = gc.mem_free()
            mem_alloc = gc.mem_alloc()

            try:
                await self.publish_debug_msg("Koha e punës", mins)
                await self.publish_debug_msg("Ripublikimet", self.REPUB_COUNT)
                await self.publish_debug_msg("Ndërprerjet", 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)

Pra, janë edhe disa rregullime dhe konstanta për plotësinë 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 fillon ashtu siç duhet.

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

Diçka ndodhi me kujtesën time.

Pra, tĂ« gjithĂ« kodi Ă«shtĂ« kĂ«tu. SkedarĂ«t i ngarkova me ndihmĂ«n e utilitarit ampy — ai lejon ngarkimin nĂ« flash-n e brendshme (ajo qĂ« ndodhet nĂ« ESP-07) dhe pastaj tĂ« aksesohen nga programi si skedarĂ« tĂ« zakonshĂ«m. Atje ngarkova bibliotekat qĂ« pĂ«rdor mqtt_as, uasyncio, ssd1306 dhe collections (kjo pĂ«rdoret brenda mqtt_as).

E aktivizojmĂ« dhe
 Marrim MemoryError. Dhe sa mĂ« shumĂ« pĂ«rpiqesha tĂ« kuptoja se ku po rrjedh kujtesa, aq mĂ« herĂ«t dilte ky gabim. NjĂ« kĂ«rkim i shkurtĂ«r nĂ« Google mĂ« çoi nĂ« pĂ«rfundimin se nĂ« mikrocontroller nĂ« tĂ« vĂ«rtetĂ« ka vetĂ«m 30kb memorie, ndĂ«rsa 65 kb kod (sĂ« bashku me bibliotekat) nuk mund tĂ« bjerĂ« kurrĂ«.

Por megjithatĂ«, ka njĂ« zgjidhje. Doli se micropython nuk e ekzekuton kodin drejtpĂ«rdrejt nga fichiers .py — ky skedar fillimisht kompilohen. PĂ«r mĂ« tepĂ«r, ai kompilohen direkt nĂ« mikro-kontroller, duke u transformuar nĂ« bytecode, i cili mĂ« pas ruhet nĂ« memorie. Gjithashtu, pĂ«r punĂ«n e kompilatorit nevojitet gjithashtu njĂ« kapacitet i caktuar RAM.

Truku qëndron tek çlirimi i mikro-kontrollerit nga kompilimi i kërkueshëm për burime. Mund të kompiloni skedarët në një kompjuter të madh, dhe në mikro-kontroller të ngarkoni tashmë bytecode-in e përfunduar. Për këtë, është e nevojshme të shkarkoni firmware-in micropython dhe ta ndërtoni përgjegjësinë mpy-cross.

Nuk e shkrova Makefile, por manualisht e kalova dhe kompilova të gjithë skedarët e nevojshëm (përfshirë bibliotekat) afërsisht kështu

mpy-cross water_counter.py

I mbetet vetëm të ngarkoni skedarët me zgjatjen .mpy, pa harruar të fshini paraprakisht përkatësit .py nga sistemi i skedarëve të pajisjes.

TĂ« gjithĂ« zhvillimin e kam bĂ«rĂ« nĂ« programin (IDE?) ESPlorer. Ai lejon tĂ« ngarkohet skriptet nĂ« mikro-kontroller dhe t’i ekzekutojĂ« menjĂ«herĂ«. NĂ« rastin tim, tĂ« gjitha logjikat dhe krijimi i tĂ« gjithĂ« objekteve ndodhen nĂ« skedarin water_counter.py (.mpy). Por qĂ« tĂ« gjitha kĂ«to tĂ« fillojnĂ« automatikisht nĂ« nisje duhet tĂ« ketĂ« njĂ« skedar me emrin main.py. PĂ«rsĂ«ri, ky duhet tĂ« jetĂ« pikĂ«risht njĂ« .py, dhe jo njĂ« .mpy tĂ« para-kompiluar. Ja pĂ«rmbajtja e tij triviale

import water_counter

E nisĂ«m — gjithçka funksionon. Por mbetet shumĂ« pak memorie e lirĂ« — rreth 1kB. UnĂ« kam gjithashtu plane pĂ«r tĂ« rritur funksionalitetin e pajisjes, dhe ky kilobajt do tĂ« jetĂ« padyshim i pamjaftueshĂ«m. Por duket se ka njĂ« zgjidhje edhe pĂ«r kĂ«tĂ« rast.

Kjo është çështja. Edhe pse skedarët janë kompiluar në bytecode dhe ndodhen në sistemin e brendshëm të skedarëve, në të vërtetë ata ngarkohen gjithsesi në memorien RAM dhe ekzekutohen nga aty. Por duket se micropython është në gjendje të ekzekutojë bytecode direkt nga memorja flash, por për këtë është e nevojshme ta inkorporoni atë drejtpërdrejt në firmware. Kjo nuk është e vështirë, megjithatë në netbook-un tim kjo e mori një kohë të konsiderueshme (aty kishte Linuks).

Algoritmi është kështu:

  • Shkarko dhe instalosh ESP Open SDK. Kjo gjĂ« mbledh kompajlerin dhe bibliotekat pĂ«r programet pĂ«r ESP8266. Kjo ndĂ«rtohet sipas udhĂ«zimit nĂ« faqen kryesore tĂ« projektit (unĂ« zgjodha instalimin STANDALONE=yes)
  • Descargar shkencat micropython
  • Bibliotekat e nevojshme shtoni nĂ« ports/esp8266/modules brenda pemĂ«s sĂ« micropyhon
  • NdĂ«rto firmware-in sipas udhĂ«zimeve nĂ« skedarin ports/esp8266/README.md
  • Ngarko firmosĂ«n nĂ« mikro-kontroller (unĂ« e bĂ«j kĂ«tĂ« nĂ« Windows me programet ESP8266Flasher ose esptool me Python)

Tani, 'import ssd1306' do të ngrejë kodin direkt nga firmosja dhe memorie operative nuk do të shpenzohet për këtë. Me këtë truk unë e kam ngarkuar në firmosje vetëm kodin e bibliotekave, ndërsa kodi kryesor i programit ekzekutohet nga sistemi i skedarëve. Kjo lejon të modifikoj lehtësisht programin pa e riparë firmosjen. Aktualisht kam rreth 8.5 kB RAM të lirë. Kjo do të lejojë realizimin e një sërë funksionalitetesh të tjera të dobishme në të ardhmen. Nëse memorie do të jetë krejt e pamjaftueshme, mund ta vendos edhe programin kryesor në firmosje.

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

Ok, pajisja Ă«shtĂ« e bashkuar, firmosja Ă«shtĂ« shkruar, kutia Ă«shtĂ« printuar, pajisja Ă«shtĂ« ngjitur nĂ« mur dhe ndriçohet me njĂ« llambĂ« shpĂ«rthyese. Por deri tani, kjo Ă«shtĂ« ende njĂ« kutĂ« e zezĂ« (nĂ« kuptimin e drejtĂ« dhe figurativ) dhe pĂ«rfitimi nga ajo Ă«shtĂ« i vogĂ«l. ËshtĂ« koha tĂ« bĂ«jmĂ« diçka me mesazhet MQTT qĂ« dĂ«rgohen nĂ« server.

ShtĂ«pia ime "intelligente" funksionon nĂ« sistemin Majordomo. Moduli MQTT ose Ă«shtĂ« i pranishĂ«m nga kutia, ose instalimi i tij Ă«shtĂ« i lehtĂ« nga tregu i shtesave — nuk e mbaj mend nga e kam marrĂ«. MQTT nuk Ă«shtĂ« njĂ« entitet i pavarur — nevojitet njĂ« broker i njohur — njĂ« server qĂ« pranon, klasifikon dhe ri-drejton mesazhet MQTT pĂ«r klientĂ«t. UnĂ« pĂ«rdor mosquitto, i cili (si dhe majordomo) funksionon nĂ« tĂ« njĂ«jtin netbook.

Pasi pajisja dërgon për herë të parë një mesazh, vlera do të shfaqet menjëherë 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Ă« analizohet nĂ« mĂ«nyra tĂ« ndryshme — kjo Ă«shtĂ« jashtĂ« pĂ«rmasave tĂ« kĂ«tij artikulli. PĂ«r ata qĂ« janĂ« tĂ« interesuar nĂ« sistemin majordomo, mund t'ju rekomandoj kanalin Elektronika NĂ« Objektiv — njĂ« shok po ashtu po ndĂ«rton njĂ« shtĂ«pi inteligjente dhe flet qartĂ« pĂ«r konfigurimin e sistemit.

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

Lidhim matësin e ujit me shtëpinë inteligjente
E dukshme Ă«shtĂ« qĂ« gjatĂ« natĂ«s, askush nuk e pĂ«rdori ujin. Disa herĂ« dikush shkon nĂ« tualet, dhe duket se filtri i osmozĂ«s reverzibile thith disa litra gjatĂ« natĂ«s. NĂ« mĂ«ngjes, konsumimi rritet ndjeshĂ«m. Zakonisht unĂ« pĂ«rdor ujĂ« nga boileri, por kĂ«tu desha tĂ« marr njĂ« banjĂ« dhe pĂ«rkohĂ«sisht e kalova nĂ« ujin e ngrohtĂ« qytetar — kjo gjithashtu Ă«shtĂ« e dukshme nĂ« grafikĂ«n e poshtme.

Nga kjo grafik mësova se shkuarja në tualet është 6-7l ujë, duke marrë dush - 20-30l, duke larë enët rreth 20l, dhe për të marrë një banjë nevojiten 160l. Gjatë ditës familja ime konsumohet rreth 500-600l.

Për ata që janë veçanërisht kuriozë, mund të hidhni një sy në regjistrimet për çdo vlerë të veçantë.

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

Nga këtu mësova se kur krani është hapur, uji rrjedh me shpejtësi prej rreth 1l për 5 sekonda.

Por në këtë formë, statistikat ndoshta nuk janë shumë të lehta për tu parë. Në majordomo ka gjithashtu mundësi për të parë grafikët e konsumit sipas ditëve, javëve dhe muajve. Ja, për shembull, grafiku i konsumit në kolonë.

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

Tani kam të dhëna vetëm për një javë. Pas një muaji, ky grafik do të jetë më tregues - çdo ditë do të ketë një kolonë të veçantë. Paksa e prishin korrigjimet e vlerave që unë i fut manualisht (kolona më e madhe). Dhe për momentin nuk është e qartë, nëse nuk kam vendosur ndonjëherë vlerat e para thuajse një kub më pak, apo nëse është një defekt në firmware dhe nuk janë regjistruar të gjitha litrat. Duhet më shumë kohë.

Duhet akoma të punoj me grafikët, t'i bëj më të bardhë, t'i pikturoj. Mund edhe të ndërtoj një grafik të konsumit të memories për qëllime debugimi - ndoshta aty ka ndonjë rrjedhje. Mund të shfaq perioda kur nuk ka qenë interneti. Për momentin, të gjitha këto janë në nivelin e ideve.

Përfundim

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

Dikush do t'i duket e çuditshme të shikojë treguesit në ekran, kur është një metër larg matësit. Por në një të ardhme jo shumë të distancuar planifikoj të shpërngulem në një apartament tjetër, ku do të ketë disa stakë ujë, dhe matësit, shumë gjasa, do të jenë të vendosur në platformën e shkallëve. Kështu që pajisja për marrjen e të dhënave nga distanca do të jetë shumë e nevojshme.

Funksionalitetin e pajisjes do ta zgjas gjithashtu. Kam filluar të shikoj për ventilat motorike. Tani, për të kaluar nga uji i qytetit në bojler, duhet të kthej 3 kranë në një niçë të vështirë për t'u arritur. Do të ishte shumë më e rehatshme ta bëja këtë me një buton me treguesin përkatës. Po ashtu, natyrisht, ka kuptim të realizohet mbrojtja nga rrjedhjet.

Në këtë artikull unë tregova versionin tim të një dispositivi të bazuar në ESP8266. Sipas mendimit tim, kam realizuar një firmware mjaft interesant në micropython duke përdorur korutina - thjeshtë dhe të bukur. Kam përpiquar të përshkruaj një sërë nuancash dhe gabimesh me të cilat u përballa gjatë rrugës. Ndoshta i kam përshkruar shumë detajet, personalisht si lexues më duket më e lehtë të shoh disa pika të tepërta, sesa të mendoj për ato që kanë mbetur të paqarta.

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

Kodi burimor
Skema dhe tabla
Modeli i kutisë

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster