Při programování her s dvěma micro:bity budeme potřebovat některé algoritmy, které nám pomohou při psaní programového kódu. Tato lekce je jedna z nich a popisuje způsob, jak u programů typu Peer to Peer (viz lekce A15) přece jenom stanovit, který micro:bit bude například zahajovat hru, který bude mít bílé a který černé figury a pod.
název: B15 Radio III
kategorie: B mírně pokročilí
ref.číslo: B15
projekt: kurz micro:bit
verze: 01, 2017-09-22
autor: Ivo Obr, Lanškroun
Lekce je spíše výuková a naučíme se :
- Práce s obrázky hodin, například čekání na nějakou událost
- Jak u stejného programu nahraného do dvou micro:bitů se mikropočítače domluví na pořadí
- Jak si budou micro:bity kontrolovat rádiové spojení a reagovat na výpadek
Program začneme jako vždy, ale přidáme k němu import modulu “radio”, a modulu generátoru náhodných čísel.
from microbit import *
import radio
import random
Programy nahrané ve dvou micro:bitech jsou stejné a proto budou reagovat stejně. Přesto se mohou v něčem lišit, a to ve vygenerovaných náhodných číslech. Abychom mohli popisovat naše postupy, jeden micro:bit si nazveme “miA” a druhý “miB”.
Jestliže bychom oba micro:bity zapnuli v jeden okamžik (například z jednoho napájení), pak by oba začaly vysílat ve stejný čas, nebo přijímat ve stejný čas a asi by se špatně domlouvaly. Zařídíme to proto tak, že si na začátku programy vygenerují náhodné číslo. Toto číslo pak bude časovou prodlevou před prvním vysíláním. Kdo z nich začne vysílat první, když budou oba na příjmu, ten si zvolí pořadí číslo 1, druhý už bude mít pořadí číslo 2.
Jak to bude vypadat prakticky v programu :
1) miA a miB si vygenerují náhodná čísla. Micro:bit miA např. 56, miB třeba 21.
2) oba stále opakovaně naslouchají a čekají, zdali partner nevyšle zprávu
3) miB už spotřeboval čekací dobu 21 a tak vyšle zprávu “SLAVE”, což znamená, že dává pokyn druhému micro:bitu být podřízený.
4) miA zprávu “SLAVE” příjme, poznamená si že bude “dvojkou” a odešle zprávu “MASTER” kterou dává najevo miB, že bude “jedničkou”
5) miB přijme zprávu “MASTER” a zapíše si, že bude “jedničkou”
6) tím si rozdělily micro:bity role, ukončí algoritmus spojování a vypíšou svá pořadí na displeji
Při čekání na spojení budeme vykreslovat otáčející se ručičku hodin na displeji. K tomu si vytvoříme seznam se jménem “hodiny” složený z dvanácti obrázků :
hodiny = [Image.CLOCK1, Image.CLOCK2, Image.CLOCK3, Image.CLOCK4,
Image.CLOCK5, Image.CLOCK6, Image.CLOCK7, Image.CLOCK8,
Image.CLOCK9, Image.CLOCK10, Image.CLOCK11, Image.CLOCK12]
Nastavíme parametry přenosu a zapneme vysílání:
radio.config(length=10, group=15, power=7)
radio.on()
Vykreslíme úvodní obrázek a vygenerujeme si náhodné číslo. Rozsah 1 až 200 jsem zvolil jako kompromis, můžete si vyzkoušet jiné hodnoty. Více jak 500 znamená, že se prodlužuje průměrný čas na zahájení spojení. Pokud je menší než 10, je velká pravděpodobnost, že si miA a miB vygenerují shodná čísla. (Prakticky si nezapnete oba micro:bity současně, takže by kolize hrozila zcela výjimečně. Pro některé postupy by to ale mohlo být hrozbou.) To číslo 200 jsem zvolil pro tento příklad, aby lidskými měřítky jsme mohli sledovat, jak se miA a miB připojují.
Další úvodní nastavení proměnných :
i = 0 celkový počet cyklů while, použití v hodinách
citac = 0 počitadlo, kolik je třeba udělat cyklů, než se začne vysílat
poradi = 0 na začátku nula, pak dle domluvy miA a miB (jedna a dvě)
Cyklus, který trvá tak dlouho, dokud se micro:bity nespojí (“poradi” různé od nuly)
while poradi == 0:
Vykreslíme ručičku hodin – obrázek s indexem 0 až 11. Index získáme operací “zbytek po dělení”, kde je operátorem znak “procento”. Obsah proměnné “i” dělíme dvanácti a zbytek po dělení je indexem do seznamu “hodiny”.
display.show(hodiny[i % 12])
Čitač každým průchodem cyklu se zvětší o hodnotu jedna. Až dosáhne hodnoty vygenerovaného náhodného čísla, tak se odešle zpráva “SLAVE”. Čitač se vynuluje, aby se mohlo pokračovat znovu, pokud například druhý micro:bit ještě není zapnutý.
if citac == cas:
radio.send(“SLAVE”)
citac = 0
Do proměnné se jménem “prijem” přesuneme z přijímače zprávu. Pokud není v proměnné nic, tak zpráva žádná nedošla. Pokud tam ale zpráva je, musíme si zjistit, jaká.
Pokud jsme přijali zprávu “SLAVE”, pak budeme “dvojkou”. Odpovíme “jedničce” zprávou “MASTER”, a zapíšeme si do svého pořadí dvojku. Tím také ukončíme cyklus while.
Pokud jsme přijaly zprávu “MASTER”, je to zpráva od “dvojky” a my si do pořadí zapíšeme jedničku, a tím ukončíme cyklus while.
prijem = radio.receive()
if not(prijem is None):
if prijem == “SLAVE”:
radio.send(“MASTER”)
poradi = 2
if prijem == “MASTER”:
poradi = 1
Zbývá už jenom zvýšit hodnoty proměnných “i” a “citac”.
i += 1
citac += 1
Na konci těla cyklu while musíme trochu přibrzdit micro:bit, aby se hodiny točily přiměřeně a časy byly přijatelné pro lidskou rychlost.
sleep(40)
Jakmile si miA a miB rozdělily role, tak každý micro:bit si své pořadí vypíše na displeji. Pokud si budete opakovat zapnutí micro:bitů, můžete sledovat, že si role vyměňují zcela náhodně.
Pokud zapnete jen jeden micro:bit, bude se ručička na displeji otáčet stále dokola a on bude stále čekat na zapnutí druhého.
Tím jsme vyřešili spojení micro:bitů a rozdělení rolí. Dále bude třeba kontrolovat, zdali micro:bity jsou stále spojené. Může se stát, že s jedním micro:bitem odejdete na vzdálenost, kdy už se spojení ztratí, dojde bateriové napájení nebo někdo naše vysílání bude rušit.
Celý systém kontroly bude jednoduchý. Každou vteřinu necháme micro:bit vysílat zprávu “HEART”. V praxi se to obvykle uvádí jako “heart_beat” – tlukot srdce. Jestliže budeme přijímat takovou zprávu z druhého micro:bitu, víme že žije a že s námi komunikuje. Pokud dojde ke ztrátě takové zprávy, a to v námi stanovené délce časového intervalu, pak budeme považovat tuto situaci za ztrátu rádiového spojení. V našem případě na to budeme reagovat obrázkem “Image.NO” na displeji a po dvou vteřinách resetujeme micro:bit. Reset nám nastartuje znovu program od začátku (začne se hledat spojení).
Nejdříve si poznamenáme čas, kdy začneme. Jsou to časy, kdy začínáme s algoritmem “heart_beat”.
cas_vys = running_time()
cas_pri = running_time()
Celý náš další program budeme opakovat
While True:
První částí bude pravidelné odesílání zprávy “HEART” a to v intervalu 1sec :
if running_time() > (cas_vys + 1000):
radio.send(“HEART”)
cas_vys = running_time()
Pak se podíváme, jestli přišla nějaká zpráva od protějšku.
prijem = radio.receive()
Pokud zpráva došla, výraz “not(prijem is None)” je pravdivý, pak nás bude zajímat obsah této zprávy, protože protějšek může posílat i jiná data, než nás nyní zajímají. Pokud je obsah “HEART”, poznamenáme si čas, kdy zpráva došla.
if not(prijem is None):
if prijem == “HEART”:
cas_pri = running_time()
Na konci našeho kódu se zeptáme, zdali aktuální čas není příliš velký proti času poslední zprávy. Já jsem si zvolil 3sec, ale můžete si zde nastavit časový interval jiný podle složitosti vašeho kódu. Pokud čas překročil tento limit, znamená to, že už delší dobu náš protějšek nic neodvysílal. V tom případě vykreslíme na displeji “Image.NO”, počkáme dvě vteřiny a resetujem micro:bit. Poslední sleep(10) je od cyklu “while True”.
if running_time() > (cas_pri + 3000):
display.show(Image.NO)
sleep(2000)
reset()
sleep(10)
Samozřejmě si program můžete upravovat dle svého, vyzkoušet si jiná časování a pod. Na konec ještě celý program :
# Writen by Ivo Obr # zari 2017 from microbit import * import radio import random hodiny = [Image.CLOCK1, Image.CLOCK2, Image.CLOCK3, Image.CLOCK4, Image.CLOCK5, Image.CLOCK6, Image.CLOCK7, Image.CLOCK8, Image.CLOCK9, Image.CLOCK10, Image.CLOCK11, Image.CLOCK12] radio.config(length=10, group=15, power=7) radio.on() display.show(Image.HEART) sleep(300) cas = random.randint(0, 200) i = 0 citac = 0 poradi = 0 while poradi == 0: display.show(hodiny[i % 12]) if citac == cas: radio.send("SLAVE") citac = 0 prijem = radio.receive() if not(prijem is None): if prijem == "SLAVE": radio.send("MASTER") poradi = 2 if prijem == "MASTER": poradi = 1 i += 1 citac += 1 sleep(40) display.show(str(poradi)) # kontrola spojeni pomoci HEART_BEAT cas_vys = running_time() cas_pri = running_time() while True: if running_time() > (cas_vys + 1000): radio.send("HEART") cas_vys = running_time() prijem = radio.receive() if not(prijem is None): if prijem == "HEART": cas_pri = running_time() # TADY MUZE BYT PROGRAMOVY KOD HRY A POD. if running_time() > (cas_pri + 3000): display.show(Image.NO) sleep(2000) reset() sleep(10)