Okos otthon is fókuszban

Arduino kalandok

Arduino kalandok

Computherm Q8RF - Szoftver és publikáció - part 5

2019. december 09. - denx

Rögös út vezetett idáig: már tudok úgy csinálni, mintha az én kis ESP8266-om kombinálva a 868 MHz-es rádióadóval egy Computherm Q8RF termosztát lenne, vagyis tudok olyan csomagokat küldeni az éterbe, amit a Computherm vevője megeszik és reagál rá. Viszont mivel vettem egy 868-as vevő egységet is, logikusnak tűnt ezt is hadra fogni.

Vétel

Ha már úgy is itt van, miért ne használjam hát?! Az eszköz megvan, a protokoll ismert, hát vágjunk bele a jelek feldolgozásába is! Nos, itt más fajta kihívásokkal kellett szembenéznem, mint az adásnál. Az elég nyilvánvaló, hogy nem lesz semmi tökéletesen pontosan időzítve, amit kapok, vagyis szinte mindenhol ráhagyással kell dolgozzak. A legelső dolog, amit fel kell ismerjen a program, az a szinkronizációs csomag, vagyis 3 ütem hosszú szünetek és adások egymásutánja. Erre azt a közelítést használtam, hogy a 2.5 x T és 3.5 x T közötti hosszban érkeznek a váltások, akkor én azt elfogadom. Ha 0-t akarok értelmezni, akkor a 2 alacsony után 1 magas jön elvileg, ezt úgy próbáltam értelmezni, hogy a magas rész hossza legyen 0.5 x T és 1.5 x T időtartományon belül, míg az 1-esnél (1 alacsony, 2 magas) 1.5 x T és 2.5 x T között. Ami még érdekes az a STOP üzenet, ami elvileg 6 ütem hosszú, de után mindig egy SYNC jön, ami 3 alacsonnyal indul, így ezek összesen 9 ütemnyi alacsony jelszintet adnak ki. Ezt némi ráhagyással úgy próbáltam értelmezni, hogy 7 x T és 10 x T között legyen. Ezekből jöttek ki a következő konstansok:

#define TICK_LENGTH 220
#define SHORT_MIN TICK_LENGTH * 0.5 // 110
#define LONG_MIN TICK_LENGTH * 1.5 // 330
#define SYNC_MIN TICK_LENGTH * 2.5 // 550
#define SYNC_MAX TICK_LENGTH * 3.5 // 770
#define STOP_MIN TICK_LENGTH * 7 // 1760
#define STOP_MAX TICK_LENGTH * 10 // 2200

Értelem szerűen a fenti levezetésből következik, hogy ami a SHORT-nak a MAX értéke lenne, az ugyanannyi, mint a LONG-nak a MIN és így tovább.

Ami adáskor a delayMicroseconds() volt, az vételnél a micros() lett. Ez a függyvény adja vissza, hogy az MCU indítása óta hány mikroszekundum telt el. Mivel ez egy unsigned long típusú értéket ad vissza, így ha az ember utánaszámol, akkor ez az érték nagyjából 70 percentént túlcsordul és nullázódik. Ez azonban nem fog minket befolyásolni, mert mi minden esetben kivonjuk a kapott értéket egy korábban ugyanettől a függvénytől kapott értékből és csak a különbségüket viszgáljuk. A túlcsordulás pedig ugyanúgy fog bekövetkezni akkor is, amikor egy kicsi számból vonunk ki egy hatalmasat, vagyis nekünk ezzel az egésszel nem is kell fgolalkoznunk!

Hogy a mérések a lehető legpontosabbak legyenek így az nem megoldás, hogy a loop függvényben folyamatosan nézegetjük a bemeneti láb állapotát, ide más fegyverre lesz szükség! Ez pedig a megszakítás:

attachInterrupt(digitalPinToInterrupt(INPUT_PIN), handler, CHANGE);

A mágia pedig a következő helyen van:

void handler() {
  static unsigned long lastMs = 0, currMs, diffMs;
  currMs = micros();
  diffMs = currMs - lastMs;
  lastMs = currMs;
  if (buffEnd == BUFF_SIZE) {
    buffEnd = 0;
  }
  if (!avail){
    if (digitalRead(INPUT_PIN) == LOW){ // Falling edge
      if (diffMs >= SHORT_MIN && diffMs <= SYNC_MAX){ // filter out the too short and too long high pulses
        if (diffMs >= SYNC_MIN){
          buffEnd = 0;
        } else {
          if (diffMs <= LONG_MIN) {
            buff[buffEnd++] = 0;
          } else {
            if (diffMs < SYNC_MIN) {
              buff[buffEnd++] = 1;
            }
          }
        }
      }
    } else { // Raising edge, only stop could be detected
      if (diffMs >= STOP_MIN) {
        if (buffEnd == MSG_LENGTH && !isRepeat()) {
          avail = true;
        } else {
          buffEnd = 0;
        }
      }
    }
  }
}

Némi magyarázoatot lehet igényel a megszakításkor lefutó kódrészlet:

  • Az avail nevű változóban azt tárolom, hogy van-e feldolgozott, kész csomag, amit már lehet a fő ciklusban használni
  • Ahogy az látható a leszálló élek időpontja a fontos nekem, a felszálló élek csak a STOP üzenetek detektálásakor fontosak. (Természetesen a felszállóknál is megjegyezzük az időpontot, vagyis nyomunk egyet a stopperórán, hogy utána ahoz tudjuk majd viszonítani a következő leszálló élet.)
  • A buff nevű változó maga a puffer, aminek az aktuális hosszát a buffEnd tárolja. Ebben gyakorlatilag 0 és 1 értékek gyülekeznek.
  • Az isRepeat() nevű függvény azt nézi meg, hogy ami épp most érkezett, az nem az előző csomag ismétlése-e véletlenül.(Ezt a nem túl bonyolult kódot nem rakom ide, ha valaki idáig eljutott biztosan megtalálja a módját, hogy egy másik változóba mentett puffer-rel össze tudja hasonlítani az érkezettet.)

Szóval emellé jött még pár egyszerű rutin, ami a hexadecimális értékeket kiírja, meg hasonlók, de a lényeg, hogy ezeken az alapokon simán össze lehet rakni a kódot, ami fel tudja dolgozni az éterben keringő adatokat.

Könyvtár

Ha már ilyen sikeresen megcsináltam külön-külön az adó és a vevő részeket, akkor adta magát a feladat, hogy legyen ebből valami olyan, amit könnyedén tudok majd használni. Arduino környezetben nagy kultúrája van a library-k publikálásának, előszedtem hát a régi 433-as libemnél tanultakat és publikáltam a második opensource lib-emet is. Akit érdekel itt található a cucc, van benne egyszerű példa a küldésre és a fogadásra is:

https://github.com/denxhun/ComputhermRF

Kész cucc

Ezután már csak egy lépés volt hátra: csinálni egy tényleg használható programot. Ehhez én egy Wemos D1 mini nevű ESP8266 alapú modult választottam, ami egy őrületesen drága (~2 USD ha jól emlékszem) kínai termék. Erre pár hét alatt összerkatm egy izgi kis projektet:

  • Wifi-n csatlakozik az otthoni hálózatomhoz
  • MQTT-n csatlakozik a Home Assistant rendszeréhez
  • A rákötött vevő segítségével érzékeli, ha a termosztátok bármelyike ki, vagy bekapcsol, ezt az infót pedig beküldi az MQTT-be is.
  • Ha az MQTT megfelelő csatornájára üzenünk az eszköznek, akkor a rádióadón keresztül maga küld olyan üzenetet, mintha a termosztát szólt volna a központi egységnek, vagyis ki-be tudja kapcsolgatni a kazánt.
  • Mindezt egy csinos kis weboldalon keresztül lehet állítgatni, vagy akár kézzel is kapcsolgatni.
  • A rendszer egy websocket alapú egyszerű kommunikációval old meg mindent, a HTML oldal maga statikus, de a böngészőben futó jQuery rendez mindent.
  • Természetesen amit csak lehetett igyekeztem nem beleégetni a kódba, hanem a Settings fülön konfigurálhatóvá tettem.

Ez maga a mini weboldal és hónapok óta stabilan fut - ami nem kis büszkeséggel tölt el.

Tanulság és TODO

Miután élesbe állítottam a kis eszközömet, azóta egy dolog biztosan kiderült számomra: van még kihívás a témában. Kiderült ugyanis, hogy a termosztát azt csinálja hogy pár percenként újra meg újra elküldi a be- illetve kikapcsoló üzeneteit a központnak. Vagyis ha megy a fűtés, akkor 2 percenként (emlékeim szerint, de ez a szám csak halvány emlék) újra és újra elküldi a bekapcsoló üzenetet. Azt még nem kisérleteztem ki, hogy a központ kikapcsolja-e a fűtést, ha nem érkezik meg bizonyos időn belül újra az üzenet, de nem tartom ezt sem kizártnak.

Ez nekem azért problémás, mert nem tudom egyszerűen megvalósítani azt a műveletet, hogy a Home Assistant szól az eszközömön keresztül a kazánnak, hogy most kapcsolj be, annak ellenére, hogy a termosztát szerint erre nem lenne most szükség. (Ugye a sorozat elején már vázoltam azt a scenáriót, hogy elutazik a család és beállítom a termosztátot, hogy ne fűtsön a következő napokban, majd amikor indulunk haza, akkor távolról beindítom a fűtést, hogy mire hazaér a család már jó idő legyen.) Viszont van erre egy megoldási ötletem, amit még nem teszteltem, de hamarosan meg lesz az is: a 4 csatornából jelenleg 2 van kihasználva. A most kihasználatlan kettőt párhuzamosan lehetne kötni az éles körökkel, így ha az 1. számű kör ki van kapcsolva (amit a termosztát vezérel) attól én még a 3-as számút be tudom kapcsolni (Home assistant-on és a gateway-en keresztül) így a kazán beindul. Hasonlóan a 2-4 párost is párhuzamosítani lehet. Na ez az ami még a TODO listámon van...

A bejegyzés trackback címe:

https://ardu.blog.hu/api/trackback/id/tr5615339284

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben és az adatvédelmi tájékoztatóban.

Erik H. 2019.12.27. 17:00:36

Remek sorozat! Le a kalappal..

denx 2019.12.28. 20:39:20

@Erik H.: Köszönöm, remélem tudsz belőle meríteni!

DaeMonS 2020.03.11. 21:29:52

Erdekes elkepzelesek, tetszenek.
Nekialltam en is osszerakni a wemos+cc1101 vevot, hogy a computhermes Q8-asokkal "jatszadozzak".
Januarban meg ment, most valami miatt nem sikerul eletre birni a kommunikaciot...

1. Kivancsi lettem a weblapos megoldasra, nem publikus veletlenul?
2. elemzes celjabol eloszor domoticz-nek kuldenem be az adatokat, igy lenne egy log, amiben latnank, hogy milyen idokozonkent kuldi a homero az ismetlo jelet, szivesen asszisztalnek - ill. osszerakhatnank egy lib-frissitest ( lib irashoz meg nem kezdenek).

DaeMonS 2020.03.15. 16:40:50

Pedig sajnos a jelenlegi android ide+ esp komponensek alapjaiban valtozas lehet, sajnos nem sikerul eletrebirnom a lib-et :(
Van esetleg valaki aki rajott hol lehet a hiba?
denx: Kerhetek egy ellenorzest?:)

denx 2020.03.16. 10:20:41

Szia,

Örülök ha tetszik a lib. cc1101-gyel nem próbáltam még, nekem a korábban említett HW-ekkel megy azóta is. Nem változott semmi az én oldalamon, rég nem nyőltam a kódhoz. Ha írsz privátban (nevemre kattintva) akkor megbeszélhetjük miben tudok segíteni!

DaeMonS 2020.03.18. 21:16:48

@denx: ment PU, esetleg mailban folytatjuk? gyorsabb lenne.

denx 2020.03.19. 10:04:58

Szia @DaeMonS,

2 napja válaszoltam és a freemail-es címre, ahonnan írtál...

D
süti beállítások módosítása