UVOD
Naloga, ki sem si jo zastavil, je oddaljen nadzor in upravljanje ogrevanja vikenda. V vseh primerih za oddaljen nadzor potrebujemo dostop do interneta. Ker na vikendu še nimamo WiFi omrežja, se za dostop do interneta ponuja uporaba GSM signala. Potrebujem novo SIM kartico za moj odsluženi telefon ali tablico, ki bo stalno vključena na vikendu in skrbela za dostop do interneta. Na telefonu/tablici vključen hotspot (posredovanje GSM -Wifi) omogoči delovanje naprav, ki se krmilijo preko WiFi vmesnikov kot je na primer SONOFF switch.
Pri kupljenih rešitvah vedno nekaj manjka ter se pri dodajanju funkcionalnosti sistem draži in večkrat ne omogoča željenih funkcionalnosti. Doma izdelan vmesnik omogoča širitev funkcionalnosti iz preprostega stikala pa vse do tako imenovanega pametnega vikenda. Seveda me zanima sama rešitev, zato sem si za začetek zastavil preprosto nalogo: vklop in izklop peči ter spremljanje temperature. Vse ostale funkcionalnosti, kot so nastavitev termostata, odpiranje vrat, spremljanje alarmnega senzorja, itd pridejo na vrsto kasneje in bodo narejene na enak način in na istem HW.
GUI-O aplikacija je natanko to, kar sem iskal. Moj star odsluženi telefon bom na vikendu uporabil kot GUI mojega procesorja. Na telefonu bo hkrati dostop do interneta. Povezava na mojo uP elektroniko bo preko UART/Bluetooth modula, na internet pa se bo telefon povezal preko standardnega GSM podatkovnega prenosa.
V domači delavnici sem nabral vse potrebne komponente:
-
STM32F103CBT6 uP enota – BluePill (lahko je katerikoli drug uP: Atmel, PIC, ...)
-
HC06 Bluetooth modul (ali katerikoli drug BT ali BLE modul)
-
5V napajalnik (5V Converter Board Module Power Supply Isolated AC-DC ali kaj podobnega)
-
Rele vmesnik (D1 Mini Relay Module For Arduino – ali kaj podobnega)
-
Temperaturna sonda (DS18S20 NTC 10K – oziroma karkoli zna izmeriti temperaturo)
-
Primerno ohišje z vtičem in vtičnico ali ohišje, ki ga montiramo na 230V podaljšek med vtičem in vtičnicami.
V razvojni fazi dodatno uporabljam ST Link V2 za programiranje in debagiranje ter UART/USB vmesnik za razvojno konzolno komunikacijo. Ti dve enoti povežem s konektorjem in jih kasneje odklopim. Po zaključenem razvoju programa izdelek samostojno deluje brez ST Link in UART/USB vmesnika.
Mnoge rešitve na trgu nam ponudijo kompleksen SW, ki ga naložimo in tudi deluje. Ko pa želimo svoj izdelek nadgraditi, pa naletimo na problem razumevanja tega SW. Glede tega je GUI-O rešitev drugačna. Vso zapisano kodo boste v vseh detajlih razumeli in dobili orodje za širitev sistema brez omejitev. Namen članka je opis same rešitve, zato ne bom podrobneje opisoval, kako sem rešil posamezne funkcionalnosti v SW samega procesorja. Omejil se bom na samo komunikacijo. Verjamem, da se v uP svetu znajdete, za pošiljanje stringov lahko uporabite že razvit SW ter nove funkcionalnosti rešujete na vam optimalen način.
Pa začnimo – prvi korak - postavitev GUI na uP:
Povezal sem procesor UART2 na BlueTooth modul (3v3, GND, Rx, Tx) ter naložil eno od svojih aplikacij s konzolno komunikacijo. Na uP SW sem inicializiral dodaten UART2 port ter ga konfiguriral na startno hitrost 9600 bodov. Sprejem stringov iz UART2 (Bluetooth modul) sem preusmeril na interpreter za sprejem stringov iz UART1 (UART1 že dolgo uporabljam kot razvojno konzolo). Tako je SW pripravljen za odzive iz GUI-O vmesnika. Če še nimate interpreterja, vam je lahko v pomoč minimalen SW https://www.gui-o.com/resources-stm-led.html.
Na svoj odslužen telefon sem naložil demo GUI-O aplikacijo. Dokler nisem plačal licence, se je v spodnji del ekrana dodala reklama, sicer pa funkcionalnost deluje v celoti. Povezal sem GUI-O aplikacijo z BlueTooth modulom:
GUI-O: tri črtice na vrhu ekrana, ali poteg po ekranu iz leve proti desni:
Ko GUI-O najde dostopne Bluetooth naprave, izberemo HC06, vpišemo PIN (običajno 1234). Aplikacija in uP sta s tem povezana. Ko pritisnem na startni gumb sredi ekrana GUI-O aplikacije, le ta pošlje zahtevo za inicializacijo ekrana: @init. Seveda sedaj aplikacija vrne opozorilo: ,Connected device not responding' , kar je razumljivo, saj manjka odgovor na zahtevo @init.
Ker je bistvo moje rešitve, vklop in izklop ogrevanja, sem v dokumentaciji poiskal preklopnik (TG – toggle) ter v string za odgovor na @init prekopiral minimalne obvezne podatke:
sendstr2("|TG UID:tg1 X:50 Y:20\n\r"); /* stikalo z obveznimi parametri */
Rezultat: po dotiku na startni gumb se prikaže delujoč preklopnik. Spodaj je slika pred in po preklopu.
Malo sem popravil parametre. Na mojem mobitelu (po občutku in za moj prst) je preklopnik nekoliko premajhen, zato sem spremenil parametre oziroma dodal W:25 H:5 HAW:13 HAH:13 RAD:3, dodal sem nekoliko močnejše senčenje SHVR:1 SHHR:1, na koncu spremenil barvo FGC:#C70039 ter dodal transparentnost na ugasnjenem stikalu BGC:#304C4C4C. Tako je sedaj inicializacija stikala:
sendstr2("|TG UID:tg1 X:50 Y:20 W:25 H:5 HAW:13 HAH:13 RAD:3 SHVR:1 SHHR:1 FGC:#C70039 BGC:#304C4C4C\r\n"); */ na preklopniku dodani parametri za lepši design */
Podrobnejši opis vseh parametrov ter prednastavljenih vrednosti je v poglavju Toggle na strani 47 v priročniku: https://www.gui-o.com/assets/gui-o_developer_manual.pdf
Za določanje barv je priročna aplikacija: https://htmlcolorcodes.com/color-picker/
Na dotik stikala GUI-O aplikacija vrača @tg1 1\r\n oziroma @tg1 0\r\n. Dogradil sem parser za sprejem vrednosti druge besede ob sprejemu stringa @tg1 ter na 1/0 zakrmilil/razkrmilčil port na uP za vklop/izklop releja. Tako je osnovna funkcionalnost vklopa in izklopa z GUI narejena.
Še nekaj potrebnih dodatnih informacij:
Po izklopu GUI-O aplikacije ter ponovnem vklopu in pritisku na startni gumb, le ta vrne ,Bluetooth disconnected'. Nerodno je vedno znova aplikacijo povezovati z Bluetooth modulom: Settings->Connections->Bluetooth and IoT -> Available devices -> HC06, zato je na nastavitvah predviden Settings->Connections->Bluetooth and IoT -> autoconnect preklopnik. Ko ga aktiviramo, GUI-O aplikacija takoj po startu sama izvede povezavo z Bluetooth modulom ter pošlje @init. Po ponovnem startu se prikaže inicializiran GUI, pripravljen za delo.
Na HC06 ter uP SW sem dvignil Boderate na primernejšo višjo hitrost 115200. Nato sem na HC06 vpisal tudi ime, ki ga uporabnik vidi ob prvem povezovanju GUI-O aplikacije na HC06 modul. Imena HC06 so različna in če je v prostoru ob povezovanju več vmesnikov, je uporabnik lahko v dvomih, kateri je pravi. Vpisal sem ime weekend_controller. Podrobnejša navodila komand za spremembo hitrosti in vpis imena najdete v dokumentaciji za izbran Bluetooth modul.
Popravljanje inicializacije v izvorni kodi SW mojega uP zahteva ponovno nalaganje popravljenega SW. Ob zagonu programa na uP izvedem celotno inicializacijo enako, kot na sprejem @init. To naredim tako, da na koncu inicializacije pred main() zanko zaženem celotno inicializacijo kot klic podprograma. Za lažje in hitrejše delo, da hkrati ne potrebujem ugašati in prižigati GUI-O aplikacijo, sem na začetku inicializacije dodal @cls / briši celotni ekran / ,sicer GUI-O opozori na napako (element se ne sme inicializirati dvakrat z istim imenom – tg1). Tako sedaj cela inicializacija izgleda:
sendstr2("@cls\r\n"); /* briši celotni ekran */
sendstr2("|TG UID:tg1 X:50 Y:20 W:25 H:5 HAW:13 HAH:13 RAD:3 SHVR:1 SHHR:1 FGC:#C70039 BGC:#304C4C4C\r\n"); */ na preklopniku dodani parametri za lepši design */
Kasneje bom opisal, kako sem nadgradil GUI s prikazom temperature, nastavitvijo termostata, dodatnimi slikami, itd. Sedaj pa me predvsem zanima, kako GUI vmesnik spraviti čez internet do kateregakoli uporabnika, saj je to bistvo mojega sistema.
IoT – drugi korak - GUI na telefon preko interneta
V navodilih https://www.gui-o.com/assets/gui-o_developer_manual.pdf v poglavju: Bluetooth with optional IoT forwarding je spodnja slika ki ponazarja IoT delovanje.
Kot ste že spoznali, grafične elemente v programu GUI-O upravlja uP. Z IoT funkcionalnostjo pa do GUI dostopa več uporabnikov. V splošnem so uporabniki lahko različni oziroma imajo različne GUI vmesnike. Predviden je način, kako narediti različne GUI za različne uporabnike.
Na nek način ločena funkcionalnost pa je: postavitev komunikacijske poti med telefoni. Uporabljena je komunikacija preko GUI-O standardnega MQTT serverja. Ta del je avtomatiziran in se z njim podrobneje ne boste ukvarjali, le vključili ga boste ter uporabili.
a) Delo na uP z oddaljenimi uporabniki
Če v inicializaciji elementa dodam parameter PUB:”ime”, potem GUI-O aplikacija ne kreira grafičnega elementa na svoj ekran, temveč posreduje string na internet oziroma ga objavi na MQTT setver z dodanim imenom. Če želimo postaviti različne vmesnike pri različnih oddaljenih uporabnikih, potem uporabimo imena. Element se kreira le uporabniku, ki ga ob postavitvi komunikacije vpiše lokalni uporabnik. (glej podrobnejši opis spodaj: Settings -> Quick pair -> Generate QR code : ter dodatno opcijski vpis imena uporabnika.) Če dodaten parameter pri inicializaciji pustimo brez imena (prazen string PUB:””), to pomeni, da pošiljamo string na vse oddaljene uporabnike hkrati – na vse, z imeni in brez imen. Odziv na dotik ekrana lokalnega GUI-O že poznamo, odziv na dotik ekrana oddaljenega GUI-O je enak, le da je dodan parameter usr:ime. Tako nam na primer stikalo vrne: @tg1 1 usr:ime. Pri interpretiranju sprejetih stringov v uP imamo tako več možnosti. Ko sprejmemo prvo besedo @tg lahko izvedemo dalje analizo le druge besedo 1/0 ter izvedemo vklop/izklop naprave. Lahko pa obdelamo še tretjo besedo in razpoznamo preklop oddaljenega uporabnika ter dalje tudi dekodiramo kateri uporabnik je izvedel preklop.
Tako lahko z uporabo imen inicializiramo različen GUI za lokalni telefon in oddaljene uporabnike in seveda tudi različne GUI za različne oddaljene uporabnike. V obratni smeri pa imamo polen nadzor vseh uporabnikov. Za začetek bom naredil najenostavneje, vsi uporabniki imajo enak GUI in enake pravice, imena bodo prazni stringi.
b) Postavitev komunikacijske poti med telefoni
Generiranje komunikacijskih parametrov za IoT je na GUI-O aplikaciji avtomatizirano in tako za uporabnike maksimalno poenostavljeno, sicer pa se komunikacijski kanal zgradi z najvišjim nivojem standardnih varnostnih mehanizmov, ki preprečijo vdor tretjega uporabnika. MQTT protokol je namenjen samo komuniciranju med lokalnim GUI in oddaljenem GUI. Vsak lokalni GUI generira unikaten naslov, ki je dodeljen imenu naprave: na primer Vikend_controler. Unikaten naslov se od lokalnega GUI proti oddaljenemu GUI prenese na več načinov. Najenostavneje je z generatorjem QR kode, ki generira potrebne podatke na lokalnem telefonu in jih nato z branjem QR kode vpišemo na oddaljen telefon. Branje se izvede direktno na lokalni telefon, ali s prenosom kode preko maila ali ostalih komunikacijskih virov.
Naložil sem GUI-O aplikacijo tudi na svoj telefon, ki je v funkciji oddaljenega GUI.
Na lokalnem GUI_O dodatno nastavimo.
- Settings -> Connections -> IoT -> Autoconnect (ob vklopu GUI-O se naredi povezava na MQTT server)
Generiramo potrebne podatke za komunikacijo preko interneta.
- Settings -> Quick pair -> Generate QR code
GUI-O aplikacija generira potrebne podatke za prenos preko interneta ter le te na ekranu prikaže v obliki QR kode. Pred generiranjem je obvezen vpis imena naprave: na primer weekend_controller ter opcijski vpis imena uporabnika, pustil sem prazno. V primeru, da z GUI-O aplikacijo upravljamo več naprav, nam ob startu GUI-O aplikacija ponudi seznam naprav oziroma imen v izbor. Izbor je vezan na komunikacijske parametre proti napravi. Ime naprave se na nivoju uP SW ne uporablja, saj je uP SW vezan na eno napravo. Vpis imena uporabnika je opcijski. Če ga ne vpišemo, je parameter usr:ime ki se vrača na koncu stringov v smei od oddaljenega GUI-O proti lokelnemu GUI-O, samo usr: .
Pozor: Po skeniranju QR iz lokalnega na oddaljeni telefon ne pozabite na lokalnem potrditi uspešen prenos (Press to add device). Šele po potrditvi se na lokalnem telefonu podatki zapišejo v tabelo, ki je dostopna:
Na oddaljenem GUI-O nastavimo.
Settings -> Connections -> IoT -> Autoconnect (ob vklopu GUI-O se naredi povezava na MQTT server)
Ter skeniramo potrebne podatke za komunikacijo preko interneta.
Settings -> Quick pair -> Scan QR code
Na oddaljenem telefonu preberemo kodo in komunikacijski kanal je postavljen.
Manjka še nekaj kode v uP. Tako kot sem zgoraj opisal v točki a), je potrebno oddaljenim uporabnikom inicializirati GUI. Izbral sem najenostavnejši način – vsi uporabniki imajo enak ekran in enake pravice, zato sem dodal vsem inicializacijskim ukazom PUB:”” (objava na MQTT strežnik). Tako sem v inicializacijski blok, ki je zapisan zgoraj dodal še:
/* inicializacija oddaljenih uporabnikov */
sendstr2("@cls PUB:\”\”\r\n"); /* briši ekrane vseh oddaljenih uporabnikov */
sendstr2("|TG UID:tg1 X:50 Y:20 W:25 H:5 HAW:13 HAH:13 RAD:3 SHVR:1 SHHR:1 FGC:#C70039 BGC:#304C4C4C PUB:\”\”\r\n"); */enak preklopnik tudi za vse oddaljene uporabnike */
Po nalaganju SW oba telefona delujeta z istim preklopnikom.
Preklop na lokalnem GUI-O proti uP vrača @tg1 1\r\n oziroma @tg1 0\r\n.
Preklop na oddaljenem pa vrača @tg1 1 usr:\r\n oziroma @tg1 0 usr:\r\n ker imena nisem vpisal.
V konkretnem primeru lahko uporabim celo isto ime preklopnika tg1, saj se tg1 poslan oddaljenemu GUI-O na lokalnem GUI-O ne obdeluje in le ta ne javi napake. To omogoča, da preklop enega ali drugega obdelamo z istim interpreterjem ter prožimo enake akcije – konkretno v našem sistemu vklop/izklop peči. Po sprejemu @tg1 obdelamo le drugo besedo 0 oziroma 1.
Pomembne malenkosti
a) Opazili pa boste, da preklop enega preklopnika ne povzroči preklopa drugega, saj sta v konceptu ta dva preklopnika neodvisna – dve različni inicializaciji in dva različna odziva. Programer mora za sinhronizacijo poskrbeti programsko: po preklopu kateregakoli preklopnika v pravilno stanje postavimo rele in tudi oba preklopnika v pravilno stanje, saj ne analiziramo, kater preklopnik je prožil akcijo oziroma ne obdelujemo prisotnost tretje besede usr:, da bi popravili le enega od dveh. Ko je element inicializiran, mu lahko parameter oziroma stanje popravimo s komando @ime_elementa sprememba_parametra. Za element |TG proti GUI-O oddamo @tg1 EN:1\r\n oziroma @tg1 EN:0\r\n . Moja koda izgleda tako:
if (!strcmp(argument[0],"@tg1")) //sprejet toggle za vklop/izklop peči – analiza prve besede
{
if (!strcmp(argument[1],"1")) //tg1 na ON iz lokalnega ali oddaljenega GUI-O 2.beseda
{
HEATER_RELE_ON; //vklop peči na port PB7
sendstr2("@tg1 EN:1\r\n"); //posavim tg1 lokalnega GUI-O na ON
sendstr2("@tg1 EN:1 PUB:\"\"\r\n"); //postavim tg1 oddaljenega GUI-O na ON
}
else if (!strcmp(argument[1],"0"))
{
HEATER_RELE_OFF; //izklop peči na port PB7
sendstr2("@tg1 EN:0\r\n"); //posavim tg1 lokalnega GUI-O na OFF
sendstr2("@tg1 EN:0 PUB:\"\"\r\n"); //postavim tg1 oddaljenega GUI-O na OFF
}
}
Rezultat: Če preklopim katerikoli preklopnik, na drugem vmesniku preklop sledi prvemu. Izbira rešitve je seveda vaša. Pomembno je le, da če peč vklopi en uporabnik, to na vmesniku vidi tudi drug uporabnik.
b) Še en problem delovanja:
Predpostavimo, da peč na vikendu gori. Ob startu kateregakoli GUI-O se prednastavljeno inicializira stanje stikala v OFF (EN:0), kar pomeni, da bo GUI-O vmesnik obeh telefonov kazal stikalo na OFF, peč pa bo ostala prižgana. V inicializacijo lahko vključim parameter EN:1, vendar s tem ne rešim problema. Po inicializaciji bosta obe stikali kazali stanje ON ne glede na delovanje peči.
Pravilna rešitev je le ena: Stanje ststema nadzira in ohranja podatek v uP. Ob inicializaciji grafičnih elementov, ki prikazujejo stanje, moramo le te inicializirati v stanje skladno z delovanjem sistema, ali jih s komando po inicializaciji postaviti v to stanje.
Sam sem stanje upošteval pri sami inicializaciji ter inicializiral ITG s parametrom EN:1 ali EN:0 glede na stanje releja. Kje v string dodamo parameter ni pomembno. Dodal sem ga za obvezne parametre. Stanje 1 oziroma 0 sem bral direktno iz porta, ki krmili rele
(((GPIOB->IDR&0x0080)>>7)&0x01) //branje porta PB7 vrne 1 ali 0
String za mojo inicializacijo stikala
|TG UID:tg1 X:50 Y:20 W:25 …. \r\n
sem razbil na zaporedno generiranje treh stringov
|TG UID:tg1 X:50 Y:20 EN: //prvi del stringa
(((GPIOB->IDR&0x0080)>>7)&0x01) //drugi del stringa 0 ali 1 (glede na stanje)
W:25 H:5 HAW:13 HAH:13 RAD:3 SHVR:1 SHHR:1 FGC:#C70039 BGC:#304C4C4C\r\n //tretji del
In nastane strnjena koda za inicializacijo stikala:
sendstr2("|TG UID:tg1 X:50 Y:20 W:25 EN:"); //izpis stringa prvega dela inicializacije stikala
sendnr2(((GPIOB->IDR&0x0080)>>7)&0x01); //izpis številke 0 ali 1 (glede na stanje)
sendstr2(" H:5 HAW:13 HAH:13 RAD:3 SHVR:1 SHHR:1 FGC:#C70039 BGC:#304C4C4C\r\n"); //3. del
POZOR: Ne pozabite dodati presledek na začetek tretjega stringa.
Seveda je potreben enako narediti tudi na inicializaciji za oddaljen telefon. Tako je celoten Init blok:
/* inicializacija lokalnega GUI-O */
sendstr2("@cls\r\n"); /* briši celotni ekran */
sendstr2("|TG UID:tg1 X:50 Y:20 W:25 EN:"); //izpis stringa prvega dela inicializacije stikala
sendnr2(((GPIOB->IDR&0x0080)>>7)&0x01); //izpis številke 0 ali 1 (glede na stanje)
sendstr2(" H:5 HAW:13 HAH:13 RAD:3 SHVR:1 SHHR:1 FGC:#C70039 BGC:#304C4C4C\r\n"); //3. del
/* inicializacija oddaljenih uporabnikov */
sendstr2("@cls PUB:\"\"\r\n"); /* briši celotni ekran na oddaljenem telefonu */
sendstr2("|TG UID:tg1 X:50 Y:20 W:25 EN:"); //izpis stringa prvega dela inicializacije stikala
sendnr2(((GPIOB->IDR&0x0080)>>7)&0x01); //izpis številke 0 ali 1 (glede na stanje)
sendstr2(" H:5 HAW:13 HAH:13 RAD:3 SHVR:1 SHHR:1 FGC:#C70039 BGC:#304C4C4C PUB:\"\"\r\n");
Prenos GUI na dodaten oddaljen telefon:
Tudi žena bi rada prižgala ali ugasnila peč na vikendu, pa sem pobrskal po dokumentaciji ter poizkusil sledeče:
Če primerjate topic MQTT podatke lokalnega in oddaljenega telefona:
Settings → Quick pair → IoT devices …. prva ikona (puščica navzdol)
lahko opazite, da je topic koda na lokalnem telefonu in: skladna z topic-om na oddaljenim telefonu out: in obratno, kar pomeni, da se loči komunikacija v eni in drugi smeri. Če želimo aktivirati novega oddaljenega uporabnika moramo kopirati podatke oddaljenega uporabnika dalje, kar naredimo lahko iz oddaljenega GUI-O:
Settings -> Quick pair → IoT devices
vsebuje 4 ikone:
- ikona: pogledamo (ali ročno prepišemo) generirane unikatne kode – topic-i za mqtt komunikacijo
- ikona: prikažemo vpisane podatke v QR obliki. Ta QR koda kot sem že zapisal zgoraj je drugačna kot generirana na lokalnem telefonu, obrnjen topic in: ter out: Ta koda je namenjena kopiranju podatkov iz oddaljenega mobitela na novega uporabnika oddaljenega telefona. Če pomotoma kodo skeniramo iz lokalnega telefona, dobimo klon 2. lokalnega vmesnika, ki poizkuša komunicirati preko mqtt – rezultat so napake zaradi podvajanja imen elementov z oddaljenim mobitelom ...Rešitev je pisana na kožo komunikaciji preko WiFi modulov, kjer oddaljeni telefoni komunicirajo direktno z uP brez vmesnega lokalnega GUI-O. Tu do take napake ne more priti.
- ikona: dostopamo do komunikacijskih kanalov za pošiljanje QR kode iz odaljenega na dodaten oddaljen telefon – tu je v aplikaciji nekaj nerodnosti, ker je v GUI-O vgrajen skener kot fotoaparat in na novem oddaljenem telefonu ne moremo skenirati sprejete slike. Rabimo dodatno napravo, kjer prikažemo sliko, ali le to printamo ter jo potem skeniramo.
- ikona: brišemo podatke za dostop do naprave – na primer iz določenega telefona ne želimo več komunicirati z weekend_controller napravo
Torej na ženin telefon sem skeniral kodo 2. ikone mojega (oddaljenega) telefona in s tem prekopiral vse potrebne podatke. GUI-O aplikacija je takoj delovala na startni gumb. Po vklopu Settings -> Connections -> IoT -> Autoconnect pa se je vmesnik postavil takoj po zagonu GUI-O aplikacije.
Sedaj stikalo deluje na treh telefonih BP, eden je povezan lokalno preko BlueTooth vmesnika, ostala dva pa preko interneta. Ko deluje, je za nazaj videti vse preprosto. Postavljena je osnova, naslednjič pa opišem, kako sem zgradil lepši vmesnik. Se nadaljuje...