Programování | Pojďme programovat elektroniku | Počasí

Pojďme programovat elektroniku: Spojíme se bezdrátově s domovem z kopce za městem

  • Byt a dům vám pokryje Wi-Fi
  • Vrata otevřete 433MHz ovladačem
  • My si dnes vyzkoušíme 868MHz vysílač a vyrazíme s ním na kopec

Jedním z největších zážitků začínajícího bastlíře je to, když se mu poprvé podaří bezdrátově přenést bajt z jedné krabičky do druhé a nepoužije k tomu Wi-Fi nebo Bluetooth, ale některý z mnoha desítek vysílačů, které dnes najdete na eBayi, AliExpressu, Banggoodu a dalších.

V ten okamžik totiž vytvořil svoji vlastní a unikátní bezdrátovou síť a to je přeci jen poněkud vyšší liga než rozblikat nějakou tu diodu a přečíst hodnotu z čidla.

A zatímco bastlíř jásá a cítí se nejspíše stejně jako pračlověk v jeskyni, kterému se poprvé v životě podařilo rozdělat oheň, v ČTÚ už startuje měřící vůz, aby odhalil zdroj rušivého signálu kdesi uprostřed města, kvůli kterému ostatním nefungují garážová vrata nebo dálkové zvonky.

868MHz vysílač, který slibuje 3km dosah

Dnes se na jeden takový vysílač podíváme. Na rozdíl od mnoha ostatních, u nichž jen velmi těžko odhalíte, kdo je vlastně vyrábí – natož, abyste k nim sehnali oficiální dokumentaci, jsem si objednal páreček vysílačů E45-TTL-100 od čínské fabriky Chengdu Ebyte Electronic Technology, kterou na zahraničních e-shopech najdete pod zkratkou ebyte nebo cdebyte.

17f36d69-9474-45ca-b796-a6f7d2b0c8e5e38cbeca-1cdd-4d9c-8acc-48baae02d8d4db363dab-b621-4184-b4fa-6f0f422a582e
Modul vysílače E45-TTL-100 s SMA konektorem a přišroubovanou malou anténou

Tento výrobce se od mnoha ostatních liší především tím, že je dostatečně prozíravý, aby měl celý web ve srozumitelné angličtině, ke každému produktu přikládá podrobnou dokumentaci, a přestože se jako ostatní orientuje na velké odběratele, přímo na eBayi prodává zkušební vzorky za deset dolarů.

Díky růstu koruny tak za cirka dvě stovky získáte modul s těmito parametry:

  • Čip: Semtech SX1276 s populární modulační technologií LoRa
  • Dosah: až 3 km (přímá viditelnost, kvalitní anténa, nejvyšší výkon)
  • Pásmo: „volné“ 868 MHz (32 kanálů)
  • Vysílací výkon: až 100 mW (20 dBm)
  • Přenosová rychlost: 0,3 až 19,2 kb/s
  • Spotřeba: až 120 mA (nejvyšší výkon), až 3 uA (režim spánku)
  • Napájení: 2,1 až 5,5 V
  • Komunikační rozhraní: UART + dva piny pro nastavení režimu

Pojďme si to trošku rozebrat. Vysílač používá technologii kódování signálu LoRa, jejímž základem je modulace CSS. Tu zde rozebírat nebudeme – bylo by to téma na knihu –, nicméně podstatné je to, že by se měly vysílače s technologií LoRa pochlubit opravdu slušným dosahem i s maličkou anténou, která dorazí společně s modulem.

I ve volném vysílacím pásmu musíte dodržovat drakonické podmínky

Vysílač komunikuje v licenčně volném pásmu 868 MHz, slovíčko volné jsem však nedal ve výčtu vlastností do uvozovek jen tak. Znamená to, že na frekvencích okolo 868-869 MHz můžete komunikovat, aniž byste potřebovali komerční vysílací frekvenci, ale musíte splnit hromadu podmínek, které stanoví ČTÚ. Ty základní najdete ve všeobecném oprávnění k využívání rádiových kmitočtů a k provozování zařízení krátkého dosahu (PDF)

c279ffce-7880-4459-a6e2-e0af78e4fab1
Výřez tabulky Všeobecného oprávnění č. VO-R/10/12.2017-10 k využívání rádiových kmitočtů a k provozování zařízení krátkého dosahu z konce loňského roku

V sekci pro kmitočet 868 MHz se tak například dočtete, že můžete do okolí zářit nejvýše s výkonem 25 mW, ale na frekvenci 869,4 MHz až 869,65 MHz je to při splnění dalších podmínek až 500 mW. Náš vysílač nabízí 32 vysílacích kanálů v rozsahu 862 MHz až 893 MHz, takže na to budu muset pamatovat a snížit vysílací výkon na legální hladinu.

Jenže pozor, vysílací výkon není jediná podmínka. ČTÚ stanoví ještě tzv. klíčovací poměr (duty cycle), který zase říká, kolik času můžete strávit aktivním vysíláním za jednotku času – v tomto případě za jednu hodinu.

Pokud náš seriál čtete pravidelně, jistě si vzpomínáte, když jsme si pohráli s vysílačem IoT sítě Sigfox, který umožňuje posílat nejvýše 12B zprávy a to zhruba 140× za den. I toto číslo vychází z klíčovacího poměru, Sigfox totiž stejně jako náš testovací vysílač běží v pásmu 868-869 MHz, pro které ČTÚ stanovuje klíčovací poměr nejvýše 0,1 až 1 % (v případě 869,4-869,65 MHz až 10 %).

36 sekund

1 % z celé hodiny činí 36 sekund a tímto způsobem by se mělo docílit mimo jiné toho, aby byla pravděpodobnost, že se bude hromada vysílačů v jeden okamžik bít o jeden kanál, co nejmenší.

36 sekund. Je to málo, nebo hodně? Platí obojí. Pokud bych použil vysílač na relativně krátkou vzdálenost, kde nehrozí ztráta signálu, mohu na svém E45-TTL-100 nastavit mnohem vyšší přenosovou rychlost, takže odeslání zprávy třeba z nějakého čidla na druhé straně zahrady rodinného domu, kam nedosáhne Wi-Fi, by trvalo jen zlomek času a podobných zpráv bych mohl i v rámci jedné hodiny odeslat poměrně hodně.

Já však chci dnes vyzkoušet ten dálkový scénář a pokusím se s přijímačem u mě doma v 5. patře panelového domu spojit ze vzdáleného kopce, který jako jediný vystupuje na horizontu z husté zástavby. Pokusím se tedy spojit s domovem na vzdálenost 1-1,5 kilometru.

V takovém případě budu muset nastavit co nejnižší přenosovou rychlost, která zajistí co nejnižší pravděpodobnost ztráty dat, a také co nejvyšší vysílací výkon. V každém případě, při nejnižší možné rychlosti 300 b/s odešlu zprávu o velikosti asi 10 B za několik drahocenných sekund.

300 b/s totiž sice činí 37,5 B/s, takže 10B zprávu bych měl odeslat za pár set milisekund, ale ono je to složitější, k tomuto času totiž musím připočítat ještě režii samotného vysílače. Celé to bude trvat asi 2 sekundy a to už jsem z toho časového koláče ukrojil pořádný kus času.

Komunikuje skrze sériovou linku

Fajn, dost teorie, jdeme na to. E45-TTL-100 testuji už nějaký měsíc, vysílač totiž periodicky posílá data z mé meteorologické sondy na balkoně do centrály v bytě postavené na Raspberry Pi Zero W. Zatím to má snadné, signál totiž musí překonat vzdálenost pouhých 3 metrů a jeden zateplený a reálně-socialistický železobetonový panel.

Jelikož vysílač komunikuje skrze UART a tedy starou dobrou sériovou linku, se kterou umí nativně pracovat jak Arduino, tak mnoho dalších mikrokontrolerů, nemusím na webu hledat jakoukoliv knihovnu.

Kód k odeslání ASCII zprávy „Ahoj“ z Arduina v tom nejjednodušším scénáři, kdy piny RX a TX vysílače připojím křížově na RX a TX piny mikrokontroleru, by tedy vypadal takto:

void setup(){
  Serial.begin(9600);
  Serial.print("Ahoj");
}

A pro čekání a přečtení zprávy z modemu:

void setup(){
  Serial.begin(9600);
}

void loop(){
  // Pokud jsou k dispozici nejaka data
  while(Serial.available()){
    // Precti 1 znak/bajt a uloz jej do promenne c
    char c = Serial.read();
    // Zpracuj znak
  }
}

Ano, to je opravdu celé! Žádné AT příkazy jako u Sigfoxu nebo GSM modemů. Co pošlete vysílači sériovou linkou, to vyzáří do širého okolí. Mimochodem, úplně stejně se chová další čínský blbuvzdorný 433MHz vysílač HC-12, anebo neméně populární Bluetooth vysílač HC-05. Oběma jsme se už v našem seriálu věnovali. Viz odkazy.

E45-TTL-100 nicméně přes svoji jednoduchost přeci jen může na některých Arduinech tropit problémy. V tomto článku se dočtete, jak to snadno vyřešit.

Jak se to vlastně celé konfiguruje?

Fajn, pokud je sériová linka natolik přímočará, jak se vlastně dá vysílač nastavit? Od toho tu jsou jeho dva další piny M0 a M1, které vzájemným nastavením logické 1 nebo 0 určí režim, ve kterém bude modul pracovat.

Když budou oba piny nastavené na 0 (LOW), vysílač bude pracovat v normálním režimu. Když ale u obou nastavíte 1 (HIGH), přepne se do režimu spánku, kdy by měla spotřeba klesnout v ideálním případě až na 3 uA. To se hodí tehdy, když je vysílač součástí nějaké sondy, která periodicky usíná a probouzí se třeba jen jednou za deset minut, aby změřila a odeslala data – třeba zrovna teplotu z toho vzdáleného konce zahrady, kde máte v ohrádce zazimované želvy a nechcete, aby vám zmrzly.

Ještě podstatnější je však to, že v tomto režimu se modul přepne do konfiguračního módu, takže mohu skrze sériovou linku nastavovat dle dokumentace parametry. Pro začátečníka to nemusí být úplně nejjednodušší, a tak Ebyte nabízí ke stažení drobnou utilitu pro Windows RF Setting. Modul stačí připojit přes UART/USB převodník k počítači a v aplikaci pak snadno nastavíte všechny parametry.

9e4756c9-ba11-46c1-8005-5e696186e33189460960-e192-47ea-be80-6911a4db4ed1fcbec69f-db4b-44ae-9c67-e08c1b8040a247adebb2-7b0a-42f9-a41a-27b67c23654e
Konfigurační utilita a aktuální nastavení vysílače E45-TTL-100. Z hlediska povoleného výkonu v mW se hodí tento přepočet z dBm.

Aplikace odhalí ještě jednu funkci vysílače: fixed mode. Zatímco v tom dosavadním platí, že co pošlu modemu sériovou linkou, to také vyzáří do širého okolí a zprávu mohou zachytit všechny ostatní krabičky E45-100-TTL na stejném kanálu (jedná se tedy o broadcast), je tu ještě tento adresovatelný fixní režim. Když jej aktivujete a nastavíte každému ze svých modemů třeba zrovna v konfigurační aplikaci 16bitovou adresu, první dva bajty ve zprávě budou představovat adresu zařízení, kterému je zpráva určená. Ostatní krabičky s odlišnou adresou budou zprávu ignorovat a zahodí ji.

Aby byla konfigurace modemu na PC co nejjednodušší, Ebyte nabízí i vlastní převodník s čipem CP2102 a jumpery pro nastavení stavu na pinech M0 a M1. S tímto převodníkem tedy můžete bez dalších vodičů připojit modem nejen do velkého PC, ale třeba i k Raspberry Pi a s modemem se spojit skrze Python a jeho knihovnu pySerial. Ostatně přesně tak používám na Raspberry Pi Zero W modem E45-TTL-100 i já.

76b96bc3-668a-4b67-abe2-a3bd36b187d316563d81-8832-4d70-84bd-c366fd85b5e2042c7ab4-da26-4952-8750-6c55bc4e7e7a
UART/USB převodník s jumpery pro nastavení pinů M0 a M1

Hurá na kopec!

Tak, dost bylo teoretické omáčky, jdeme testovat! Jak už jsem napsal výše, E45-TTL-100 už používám v ostrém nasazení a venkovní sonda na balkoně skrze něj odesílá meteorologické údaje do centrály postavené na Raspberry Pi Zero W.

fa5eaaf5-627d-4673-abcc-b25c883e38b3
Moje domácí centrála Raspberry Pi Zero W – v pozadí je patrný vysílač E45-TTL-100 připojený skrze převodník do USB

Stačí tedy odmontovat celou sondu, strčit ji i s dobíjecím solárním panelem do ruksaku, nasmažit řízky, nazout pohorky a vyrazit na ten daleký štít kdesi na obzoru. Sonda se každých deset minut probouzí a odesílá data, když tedy úspěšně odešle data i z kilometr vzdáleného kopce, mohu otevřít šampus.

fb7e8414-365e-4503-9a3b-8d7b9bfacd6d
A takto vypadá sonda, ze které čouhá jen kabel dobíjení pomocí solárního panelu a luxmetr přilepený na povrchu. Uvnitř je deska s čipem ESP32 a nabíjecím obvodem, 3,7V Li-ion článek 18650, barometr BME280, teploměr a vlhkoměr SHT30 a voltmetr MAX17043, pomocí kterého sleduji stav baterie.

Až do tohoto bodu jsem vše napsal, aniž bych tušil, jestli se experiment opravdu podaří. Předpokládal jsem pravý opak a tušil jsem, že nakonec budu psát o něčem úplně jiném. Jenže…

1 400 metrů od domácí centrály

Jenže když jsem konečně dorazil na úpatí kopce a podíval se na mobilu na webový výpis z domácí centrály, uviděl jsem, že sonda „Balkon 2,“ kterou mám právě na zádech v batohu, před pár minutami úspěšně odeslala data a centrála je bezchybně přijala.

d7f3941a-4feb-4ccd-95bc-484ced4f96d0826a6fe7-42f7-47b8-9701-8280522c3dcfa66000f5-8edd-4db6-9de6-fe5f78551323
Podařilo se! Sonda v bílém radiačním štítu, kam se vešla i anténa, vysílá domů, což potvrzuje čas posledního příjmu dat v prohlížeči na mobilu, kde mám načtené webové rozhraní své domácí centrály.

Počkal jsem ještě dalších deset minut a i následující zpráva korektně dorazila. Nebyla to náhoda! V tuto chvíli mě od přijímače za oknem dělilo 1 400 metrů! Nu, když už jsem pod vrcholem, vyšlápnu si až na něj.

1 500 metrů od domácí centrály

Tak, teď mě alespoň podle Mapy.cz dělí od přijímače rovných 1 500 metrů a já mám z vrcholu Střeleckého kopce hned nad kolejemi brněnského VUT celé Brno jako na dlani. Kouknu na mobil a sonda bez přestání vysílá!

511de8fd-c5f5-4dd7-a326-60ea3f903b661b6ed572-e97b-4772-a336-783dc8d47e38172bcd84-edb2-46f9-9d3f-1e81c045fb38
Domácí centrála stále přijímá data, přestože jsem 1,5 kilometrů daleko, ale zachovávám přímou viditelnost

Podařilo se a já se přiznávám, že jsem to opravdu nečekal.

Zdá se, že i s malými a tuctovými SMA anténkami dosáhnu opravdu daleko. Nicméně 1,5 kilometrů vzdálený vrchol je mé měřící maximum – tedy alespoň při splnění přímé viditelnosti. Skutečné maximum tedy momentálně nezměřím.

Vysílač E145-TTL-100 tedy vůbec není špatný, a i když si během vysílání řekne o pořádný kus energie podobný Wi-Fi, během spánku nespotřebovává téměř nic. Jeho použití je pak naprosto přímočaré a bez potřeby specializovaných knihoven. Stačí, aby mikročip podporoval sériovou linku, přičemž v případě Arduina pomůže i vestavěná knihovna SoftwareSerial, která v sériovou linku promění libovolné dva piny GPIO.

Podívejte se, jak E45-TTL-100 „hraje“ při nejnižší přenosové rychlosti ve spektrálním analyzéru pomocí SDR přijímače. První blok je zpráva ze sondy, po které následuje odpověď centrály:

Z čeho se ta bílá sonda vlastně skládala?

Na úplný závěr opět nesmí chybět kód. Dnes sem dám rovnou celý měřící a vysílací kód sondy, která je postavená na prototypovací desce „Wemos Pro“ a čipu ESP32 zhruba za dvě stovky. Proč zrovna tato deska? Nabízí konektor na lithiovou 3,7V baterii a tedy rovnou i nabíjecí obvod. Do microUSB v takovém případě mohu rovnou připojit 5V solární panel, který bude lithiový článek přes den dobíjet. Deska má zároveň vyvedeno několik pinů VBAT, takže z baterie mohu rovnou napájet vysílač.

Jelikož se jedná o meteosondu, skrze sběrnici I2C je k ní připojený ještě barometr Bosch BME280, teploměr a vlhkoměr Sensirion SHT30 a luxmetr BH1750. Na závěr chci také sledovat stav baterie, čili jsem do obvodu zapojil ještě modul voltmetru MAX17043. Všechny tyto moduly komunikují s řídícím čipem skrze sběrnici I2C a můžete je ovládat skrze nespočet knihoven pro Arduino, takže váš vlastní kód bude docela jednoduchý a včetně vysílače to celé zabere na desce pouhých 5 pinů GPIO (2 pro I2C – SDA, SCL a 3 pro vysílač – RX, TX a společný pin pro nastavení M0 a M1).

Kód, který desku každých deset minut probudí, ta změří data z čidel, uloží je do pole bajtů, to odešle do domácí centrály a počká na odpověď o úspěšném zpracování, aby pak čip a s ním i vysílač přešly do spánku, vypadá v mém případě takto:

#include <cactus_io_BME280_I2C.h>
#include <BH1750.h>
#include <ClosedCube_SHT31D.h>
#include <MAX17043.h>

/*
  Napsal jsem si vlastni protokol a system 8bitovych adres
  ADDR_FROM = adresa teto sondy, tedy odesilatel
  ADDR_TO = adresa domaci centraly, tedy prijemce
  MAGIC_BYTE = 1. B zpravy. Pokud zachytim zpravu, 
  jejiz 1. B je roven 0xAB (cislo 171), predpokladam,
  ze to je muj komunikacni protokol a zpravu dale zpracuji
*/
#define ADDR_FROM 0xBB
#define ADDR_TO 0xAA
#define MAGIC_BYTE 0xAB

// Objekty cidel
BME280_I2C bme280(0x76);
BH1750 bh1750;
ClosedCube_SHT31D sht30;
MAX17043 bat;

// Vysilac bude pouzivat alternativni seriovou linku cipu ESP32
HardwareSerial lora(2);

// Funkce pro zmereni a odeslani dat
bool meassureAndSendPacket() {
  bool retval = false;
  // Na pinu 5 je na desce systemova LED, tak ji pro kontrolu rozsvitim
  digitalWrite(5, HIGH);
  // Zmerim okolni svetlo v luxech
  uint16_t lux = bh1750.readLightLevel();
  // Zmerim data z tlakomeru
  bme280.readSensor();
  // Ziskam udaj o atmosferickem tlaku
  float baro = bme280.getPressure_MB();
  // Zmerim data z teplomeru
  SHT31D result = sht30.readTempAndHumidity(SHT3XD_REPEATABILITY_LOW, SHT3XD_MODE_CLOCK_STRETCH, 50);
  // Ziskam udaj o teplote
  float temp = result.t;
  // Ziskam udaj o vlhkosti
  uint8_t humidity = result.rh;
  // Zmerim napeti a odhadovany stav nabiti baterie (0-100%)
  float v = bat.getVCell();
  uint8_t c = bat.getSoC();

  /*Pokud je deska pripojena k pocitaci,
    vypisu do seriove linky pro kontrolu ziskane udaje
  */
  Serial.print("TEPLOTA: "); Serial.println(temp);
  Serial.print("VLHKOST: "); Serial.println(humidity);
  Serial.print("TLAK: "); Serial.println(baro);
  Serial.print("LUX: "); Serial.println(lux);
  Serial.print("VOLT: "); Serial.println(v);
  Serial.print("BAT: "); Serial.println(c);

  /*Prepocitam promenne typu FLOAT (32bitovy)
    na 16bitove cislo, at je zprava co nejkratsi
  */
  uint16_t vC = v * 100.0f;
  uint16_t baroC = (baro - 900) * 100.0f;
  int16_t tempC = temp * 100.0f;

  // Vytvorim pole samotne zpravy, bude 14 B dlouhe
  uint8_t packet[14];
  /* Na prvnich ctyrech bajtech je nas uvodni bajt 0xAB,
    na druhe pozici je adresa prijemce, na treti adresa odesilatele
    a posledni bajt havicky je delka dat, ktera nasleduji
  */
  packet[0] = MAGIC_BYTE;
  packet[1] = ADDR_TO;
  packet[2] = ADDR_FROM;
  packet[3] = 10;
  // Do zbyvajicich 10 B zkopiruji hodnoty z cidel
  memcpy(&packet[4], &tempC, 2);
  memcpy(&packet[6], &humidity, 1);
  memcpy(&packet[7], &baroC, 2);
  memcpy(&packet[9], &lux, 2);
  memcpy(&packet[11], &vC, 2);
  memcpy(&packet[13], &c, 1);

  uint32_t t0 = millis();
  uint32_t tm = 0;
  // Poslu do vysilace 14B pole se zpravou
  lora.write(packet, 14);
  Serial.println("Cekam na ACK");

  // Ulozim si aktualni procesorovy cas
  uint32_t start = millis();
  /*Ted v ve smycce cekam, dokud od vysilace
    nedorazi nejaka data. Ta by mela jako odpoved
    odeslat moje domaci centrala hned pote,
    jakmile zpracuje prichozi data
  */
  while (!lora.available()) {
    // Pokud ani po 10 s nic nedorazilo, ukoncim funkci
    if ((millis() - start) > 10000) {
      digitalWrite(5, LOW);
      return false;
    }
    delay(100);
  }
  /* Pokud nejaka data dorazila, zkusim je rozlustit
     Pokud to budou data od me centraly, bude to rada bajtu:
     0xAB, 0xBB, 0xAA, 0x01, 0x01
     Uz vime, ze prvni je identifikacni bajt, pak prijemce,
     odesilatel a delka. Datova cast zpravy ma delku 1 B a
     tento bajt ovsahuje cislo 1. Tim sonde dava centrala vedet,
     ze data korektne zpracovala
  */
  while (lora.available()) {
    tm = millis() - t0;
    uint8_t magic = lora.read();
    if (magic == 0xAB) {
      uint8_t recipient = lora.read();
      if (recipient == ADDR_FROM) {
        uint8_t sender = lora.read();
        if (sender == ADDR_TO) {
          uint8_t sz = lora.read();
          if (sz == 1) {
            uint8_t ack = lora.read();
            if (ack == 1) {
              Serial.println("Data korektne odeslana");
              retval = true;
            }
          }
        }
      }
    }
  }
  // Zhasnu LED a ukoncim funkci
  digitalWrite(5, LOW);
  if(retval){
    Serial.print("Odeslani zpravy vcetne ACK trvalo ");
    Serial.print(tm);
    Serial.println(" ms");
    return true;
  }
  return retval;
}

// Hlavni funkce programu
void setup() {
  // Pokud cip prejde do hlubokeho spanku, po 600 sekundach se probudi
  // Oproti deskam s cipem ESP8266 neni treba propojit piny WAKE a RST
  esp_sleep_enable_timer_wakeup(600e6);
  // Nastartovani seriove linky pri pripojeni s PC (skrze USB)
  Serial.begin(9600);
  // Nastartovani vysilace, ktery je pripojeny na piny 16 a 17
  lora.begin(9600, SERIAL_8N1, 16, 17);
  // Nastaveni pinu 5 na zapis (LED dioda)
  pinMode(5, OUTPUT);
  // Nastaveni pinu 15 na zapis (propojene piny M0 a M1 vysilace)
  pinMode(15, OUTPUT);

  /*Nastaveni M0/M1 na LOW a tedy prechod vysilace z
    rezimu spanku zpet do bdeleho stavu. Tento pin je pres rezistor
    pripojeny na VCC (pull-up), takze kdyz ESP32 prejde do spanku,
    bude na nem HIGH a usne i vysilac (M0 a M1 budou HIGH)
  */
  digitalWrite(15, LOW);

  // Nastartovani sbernice I2C
  Wire.begin();
  // Nastartovani vsech cidel pripojenych na I2C
  bh1750.begin(BH1750::ONE_TIME_HIGH_RES_MODE);
  bme280.begin();
  bme280.setTempCal(-1);
  sht30.begin(0x44);
  bat.reset();
  bat.quickStart();

  // Pockam dve sekundy, aby se cidla stabilizovala
  delay(2000);
  /*Zavolam funkci pro precteni hodnot a odeslani do centraly
    Kdyz funkce vrati True, dostala kladnou odpoved od domaci
    centraly, kdyz False, nedostala odpoved od centraly,
    takze odeslani mozna nebylo uspesne. V takovme pripade bych
    mohl odeslani zopakovat, nebo treba do EEPROM ulozit
    informaci o chybe kvuli statistice. Nic takoveho ale
    zatim nemam implementovane.
  */
  meassureAndSendPacket();
  // At uz se odeslani podarilo, nebo ne, prejdu do spanku
  esp_deep_sleep_start();
}

// Hlavni smycka loop je prazdna, protoze ji vubec nepouzivam
void loop() {
  ;
}

A jak by vypadal kód přijímací stanice? Jelikož se jedná o sériovou linku, analogicky bych čekal, dokud nedorazí nějaká data a postupně je zpracoval. Jelikož moje stanice běží na Raspberry Pi Zero W a obsluhující služba je napsaná v Pythonu 3, nelze sem vypsat celý její kód (aktuálně to se všemi rutinami dělá několik tisíc řádků kódu), nicméně jako ukázku napíšu alespoň úryvek kódu funkce, která čeká, jakmile se na sériové lince objeví nějaká data:

# Knihovna pySerial
import serial
# Knihovna pro praci s bajty
import struct
# Funkce, ktera poslouzi stejne jako delay na Arduinu
from time import sleep


# Funkce pro vytvoreni packetu, ktery bude mit podobu:
# 0xAB, prijemce, odesilatel, delka, data
def createLoraPacket(recipient, sender, data):
  packet = [0xAB]
  packet.append(recipient)
  packet.append(sender)
  packet.append(len(data))
  packet += data
  return bytearray(packet)

# Otevreni seriove linky do vysilace E45-TTL-100, ktery
# je pomoci UART/USB prevodniku pripojeny do USB na Raspberry Pi Zero W
lora = serial.Serial("/dev/ttyUSB0", 9600, timeout=5)

# Nekonecna smycka podobna funkci loop na Arduinu
while True:
  # Pokud vysilac zachytil nejaka data
  if lora.in_waiting > 0:
    # precti 1 B
    raw = lora.read()
    if len(raw) == 1:
      # Pokud ma bajt hodnotu 0xAB, pokracuj v praci
      magic = struct.unpack("<B", raw)[0]
      if(magic == 0xAB):
        # Precti dalsi bajt prijemce, pokud je shodny s moji
        # adresou 0xAA, pokracuj v praci
        raw = lora.read()
        if len(raw) == 1:
          recipientAddr = struct.unpack("<B", raw)[0]
          if(recipientAddr == 0xAA):
            # Precti dalsi bajt, ve kterem je
            # adresa odesilatele a pokracuj v praci   
            raw = lora.read()
            if len(raw) == 1:
              senderAddr = struct.unpack("<B", raw)[0]
              # Precti dalsi bajt, ve kterem je delka dat
              raw = lora.read()
              if len(raw) == 1:
                length = struct.unpack("<B", raw)[0]
                # Podle delky precti prave tolik bajtu
                raw = lora.read(length)
                if len(raw) == length:
                  # Z pole bajtu zpetne dekoduji hodnoty
                  temperature = struct.unpack("<h", raw[0:2])[0] / 100.0
                  humidity = struct.unpack("<B", raw[2:3])[0]
                  pressure = (struct.unpack("<H", raw[3:5])[0] / 100.0) + 900
                  light = struct.unpack("<H", raw[5:7])[0]
                  voltage = struct.unpack("<H", raw[7:9])[0] / 100.0
                  charge = struct.unpack("<B", raw[9:10])[0]
                  # Ted vytvorim odpoved, ze jsem data zpracoval
                  packet = createLoraPacket(senderAddr, 0xAA, [0x01])
                  # Odeslu odpoved
                  lora.write(packet)
                  # Ulozeni do databaze atp.

  # Pockam 500 ms a opakuji smycku
  sleep(.5)

Nabízí se otázka, proč jsem si hned na začátku nepožádal o celou zprávu a teprve poté zkoumal, co obsahuje. Jednoduše proto, že každý požadavek na čtení ze sériové linky má v této konfiguraci timeout až 5 sekund. Je to prostě robustnější a to i s ohledem na to, že při nastavené nízké přenosové rychlosti, abych dosáhl co největšího dosahu, přijímač často rozdělí na sériové lince zprávu do několika samostatných bloků.

Nejprve tedy dorazí třeba jen čtyři bajty a druhá část až po kratší prodlevě. Při čtení hlavičky bajt po bajtu mám lepší kontrolu atomicity celého příjmu zprávy, a pokud by jakýkoliv aktuálně čtený bajt neodpovídal, mohu data zahodit.

Do hry bych mohl ještě zapojit nějaký vlastní kontrolní součet/CRC, kterým bych si ověřil, že samotná data v závěru dorazila v takové podobě, jak jsem je odeslal.

A to je pro dnešek už opravdu vše. Takže ať žije zahlcené subgigahertzové pásmo. Ale raději legálně, jinak by vám také jednou mohla domů dorazit obálka s pruhem a pozvánka do správního řízení. Technické schopnosti ČTÚ a jejich partnerů v odhalování frekvenčních hříšníků nejsou vůbec špatné!


Poznámka pod čarou pro ty, kteří to celé dočetli až sem: Někteří z vás se mě v e-mailu ptají, jestli nemám v plánu všechny kódy z našeho seriálu umístit na GitHub pro snazší stažení (a případné nadávky, kde mám všude chyby). Odpověď: Už se na tom pracuje. Příště napíšu odkaz.

Diskuze (17) Další článek: Týden Živě: Meteostanice pro geeky, antiviry zítřka a betaverze Seznamu

Témata článku: , , , , , , , , , , , , , , , , , , , , , , , ,