Świat nie kończy się na Arduino i Raspberry Pi. W sprzedaży znajdziecie wiele podobnych płytek. Fakt, chyba żadna z nich nie ma takiego wsparcia społeczności jak te dwie. Ale micro:bit zasługuje na szczególną uwagę. W ramach ogólnokrajowej akcji, każdy uczeń w Wielkiej Brytanii dostał za darmo zestaw z micro:bit. Idea była prosta: wzbudzić zainteresowanie nowymi technologiami. Jeżeli choć część z obdarowanych uczniów złapie „bakcyla” – biorąc pod uwagę rozmach akcji – istnieje całkiem realna szansa na pozyskanie w niedalekiej przyszłości całkiem pokaźniej kadry inżynierów… A tych każda współczesna gospodarka potrzebuje jak kredytów.
Od połowy 2016 roku moduł można również nabyć komercyjnie.
Wbrew “zabawkowemu” wyglądowi – micro:bit można wykorzystać na wiele sposobów. Tutaj pokażę, jak na jego podstawie zbudować zegarek. Więcej o micro:bit znajdziecie na moim blogu: “Elektronika bez Spięcia”.
Najlepiej zacząć od potrzeby. Po remoncie okazało się, że w pokoju… nie ma żadnego zegarka:) Pokój otrzymał dość nowoczesny wystrój. Nic okrągłego czy tradycyjnego nie pasowało… micro:bit wyposażono w pole 5×5 diod – czy da radę na tym wyświetlić godziny, minuty?
Oczywiście można przewijać czas, ale to niezbyt czytelne. Analogowy zegar (ze wskazówkami) nie wyglądał zbytnio wyraźnie. Dlatego postanowiłem skonstruować… zegar binarny:) Ale po kolei.
Pomysł
Zbuduję zegar binarny. Zegar zasilę z baterii AA. Godzinę i minutę wyświetlę na polu LED micro:bit. Zegar będzie działał w oparciu o źródło odniesienia czasu. W tej roli użyję ds1307 (RTC: real-time clock, zegar czasu rzeczywistego). RTC będzie posiadał niezależne zasilanie tak, aby w razie rozładowania baterii – nie stracił ustawień.
Godzinę i minuty wyświetlę binarnie.
Na czym to polega? Każdą cyfrę możemy zapisać w systemie dwójkowym. Przyjmiemy 5 bitów – czyli tyle, ile w każdej kolumnie pola led mamy rzędów. Wtedy:
Liczba Zapisana w systemie dziesiętnym |
Liczba zapisana w systemie binarnym |
---|---|
0 | 00000 |
1 | 00001 |
2 | 00010 |
3 | 00011 |
4 | 00100 |
5 | 00101 |
6 | 00110 |
7 | 00111 |
8 | 01000 |
9 | 01001 |
Jak widzicie, wystarczą nam 4 rzędy (4 bity) – piąty nie jest już potrzebny.
4 kolumny będą mi potrzebne, żeby pokazać godzinę (2 cyfry) i minuty (2 cyfry). Środkowa kolumna wyświetli migający dwukropek, co pomoże w oddzieleniu godzin i cyfr.
Dolny rząd użyję do pokazywania 10-tek sekund. 1 zapalona dioda – ponad 10 sekund, 4 zapalone – ponad 40 sekund. Łatwe? Już sprawdziłem – można się przyzwyczaić:)
Zegary RTC
Zegar czasu rzeczywistego (RTC – real time clock) dostarczy aktualną godzinę i minutę. O RTC takich jak ds1307 lub pcf8563 – pisałem ostatnio kilka razy. Warto przeczytać:
- Zegar czasu rzeczywistego: podłączenie pcf8563 do Arduino,
- Kolejny zegar czasu rzeczywistego: podłączenie ds1307 do Arduino i kilka słów o i2c.
Zwłaszcza drugi tekst tu się przyda. Wiele mówi o i2c, która to wiedza będzie wykorzystywana poniżej.
ds1307 i pcf8563 komunikują się po szynie i2c. Obydwa wymagają podobnych peryferiów – kwarcu, kondensatora, rezystorów podciągających linie SDA i SCL. Obydwa mogą być wyposażone w dodatkowe baterie (lub kondensatory) podtrzymujące pracę, gdy wyłączone jest zasilanie. Łatwiejszy do aplikacji jest pcf8563. Działa on z napięciami od 1v. DS1307 wymaga napięcia 4.5 do 5.5v – czyli powyżej maksymalnego poziomu zasilania micro:bit. To pewna komplikacja, bo będę musiał znaleźć takie napięcie zasilania – oraz dopasować poziomy logiki między wszystkimi układami. Z pcf byłoby łatwiej bo może działać w takim samym zakresie napięć jak micro:bit.
Niestety po przeszukaniu sieci znalazłem przykładowy kod jedynie dla ds1307. I to w pythonie. Jest też specjalny bloczek ds1307 znalazłem też dla JavaScript, na razie w wersji beta – spróbuję go użyć następnym razem.
Zegar RTC DS1307
Więcej szczegółów na temat ds1307 znajdziecie w tekście: ds1307 – podłączenie do Arduino.
W skrócie:
- GND podłączcie do masy,
- Vcc podłączcie do zasilania – 4.5 do 5v,
- Miedzy X1 i X2 podłączcie kwarc 32k,
- Kwarc podłączacie do masy przez kondensator 22pF,
- Linie SCL i SDA to część szyny i2c – podciągnijcie je przez rezystory 10kΩ do zasilania,
- Vbat podłączacie do “+” baterii cr2032.
5v z baterii AA
Zegar planuję zasilić z dwóch baterii AA – zwykłych paluszków. Dwa paluszki to około 3v, co idealnie nadaje się do zasilania micro:bit. Niestety – ds1307 wymaga zasilania z zakresu 4.5…5.5v. Powstaje więc problem: jak ‘wydusić’ z paluszków 5v?
Układy konwertujące napięcie niższe na wyższe nazywane są popularnie przetwornicami step-up (w odróżnieniu do step-down, które zamieniają napięcie wyższe na niższe). Oczywiście nie ma nic za darmo. Przetworzenie kosztuje trochę mocy, co na pewno wpłynie na żywotność baterii.
W szufladzie znalazłem niewielką przetwornicę z portem USB. Dzięki niej można np. doładować telefon z bateryjki AA. Gniazdo USB nie było mi potrzebne – dodatkowo zajmowało trochę miejsca. Pozbyłem się go – a zamiast gniazda wlutowałem piny. W ten sposób będę mógł łatwo wpiąć przetwornicę do płytki.
Logika 5v, 3.3v (i i2c)?!
Normalnie łączenie układów posługujących się napięciem 5v (jak ds1307) i niższym (jak micro:bit) wymaga stosowania dodatkowych zabiegów dostosowujących poziomy logiki. Dla przykładu, port Raspberry Pi (3.3v) można łatwo uszkodzić, wysyłając do niego dane z Arduino – które jest 5v. Z drugiej strony – Arduino “powinno” zrozumieć impulsy z Raspberry – dlatego, że poziomy “1” z Raspberry i Arduino “zazębiają” się.
Jeżeli spojrzycie na dokumentację ds1307, zobaczycie, że dla niego logiczna “1” zaczyna się od 2.2v. Biorąc pod uwagę, że dwie AA, którymi chcę zasilić micro:bit, produkują 3v – margines wydaje się wystarczający. 2.2v na dwie AA daje po 1.1v na ogniwo – co może być uznane za prawie martwą baterię. Pamiętajcie, że w miarę wyczerpywania AA, ich napięcie niewiele spada – aż do “załamania” się przy pewnej wartości
Dodatkowo, micro:bit i ds1307 będą połączone poprzez szynę i2c. Linie SDA/SCl szyny i2s podciąga się do zasilania. Ale jeżeli macie układy zasilane z różnych napięć, można je podciągnąć do niższego z nich. O ile poziomy logiki będą się zazębiać, komunikacja powinna się udać. Oczywiście jest tu pewne ryzyko, mogą wystąpić błędy w komunikacji. Takie sytuacje powinno się rozwiązywać za pomocą konwerterów. Ale tutaj – “1” zazębiają się na tyle, że warto spróbować.
Przykładowy kod
Kod obsługi, który znalazłem w sieci, pokazuje jak komunikować się z ds1307, odczytać datę (rtc_gettime()) i ją zapisać (rtc_settime()). Stanowił on podstawę dla mojego oprogramowania.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
from microbit import i2c RTC_ADDR = 0x68 def bcd2bin(v): return v - 6 * (v >> 4) def bin2bcd(v): return v + 6 * (v // 10) def rtc_gettime(): i2c.write(RTC_ADDR, b'\x00') value = i2c.read(RTC_ADDR, 7) ss = bcd2bin(value[0] & 0x7F) mm = bcd2bin(value[1]) hh = bcd2bin(value[2]) d = bcd2bin(value[4]) m = bcd2bin(value[5]) y = bcd2bin(value[6]) + 2000 return [y, m, d, hh, mm, ss] def rtc_settime(y, m, d, hh, mm, ss): value = [] value.append(bin2bcd(ss)) value.append(bin2bcd(mm)) value.append(bin2bcd(hh)) value.append(bin2bcd(0)) value.append(bin2bcd(d)) value.append(bin2bcd(m)) value.append(bin2bcd(y - 2000)) i2c.write(RTC_ADDR, b'\x00' + bytearray(value)) # rtc_gettime() # rtc_settime(2017, 5, 22, 14, 23, 0) # rtc_gettime() -> [2017, 5, 22, 14, 23, 0] |
Kod napisano w micro python’ie – kolejnym języku, który możecie użyć do programowania micro:bit. Dla tych, którzy nigdy nie mieli nic wspólnego z pythonem: cóż, uważajcie na wcięcia:) W pythonie zastępują one grupowanie komend za pomocą nawiasów. To znaczy, poniższe jest poprawne i wypisze w pętli wartość zmiennej ‘i’ (od 1 do 9) oraz jej kwadratu (od 1 do 81):
1 2 3 |
for i in range(1, 10): print i print i*i |
A to poniżej – skończy sie błędem, bo “i” nie jest znane w ostatniej linijce:
1 2 3 |
for i in range(1,10): print i print i*i |
Reszta to już składnia języka i metody biblioteki micro:bit.
Jeżeli chodzi o komunikację po i2c:
1 2 |
i2c.write(RTC_ADDR, b'\x00') value = i2c.read(RTC_ADDR, 7) |
Metoda i2c.write() zapisze na szynę wartość “0” w kierunku urządzenia pod adresem RTC_ADDR. Wartość “0” wybierze adres w pamięci ds1307 (0, czyli rejestr sekund). Metoda i2c.read() odczyta 7 bajtów z urządzenia RTC_ADDR – czyli wszystkie dane związane z czasem.
Zmiany w kodzie
Przedstawiony powyżej kod jest wystarczający, żeby przeprowadzić podstawowe operacje na ds1307. Postanowiłem trochę go rozszerzyć. Przede wszystkim stworzyłem funkcje, które zapisują/odczytują dane z linii:
- safeWritei2c(): zapisuje dane do i2c
- safeReadi2c(): odczytuje dane z
W odróżnieniu do oryginalnego kodu, moje funkcje “chronią” się przed wystąpieniem możliwych błędów. Jeżeli transmisja nie powiedzie się, próbują jeszcze raz – maksymalnie IO_RETRY razy. Jeżeli po tylu próbach dalej nie można uzyskać poprawnej wartości, funkcje zwracają błąd COMM_ERR – oznaczający błąd komunikacji.
Wyizolowanie kodu do wysyłania/odbierania wpłynie na funkcje rtc_settime() i rtc_gettime():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# ----------------------------------------- # get rtc time def rtc_gettime(): if safeWritei2c(RTC_ADDR, b'\x00'): ret = safeReadi2c(RTC_ADDR, 7) if not ret == COMM_ERR: ss = bcd2bin(ret[0] & 0x7F) mm = bcd2bin(ret[1]) hh = bcd2bin(ret[2]) d = bcd2bin(ret[4]) m = bcd2bin(ret[5]) y = bcd2bin(ret[6]) + 2000 return [y, m, d, hh, mm, ss] return COMM_ERR # ----------------------------------------- # set rtc time def rtc_settime(y, m, d, hh, mm, ss): value = [] value.append(bin2bcd(ss)) value.append(bin2bcd(mm)) value.append(bin2bcd(hh)) value.append(bin2bcd(0)) value.append(bin2bcd(d)) value.append(bin2bcd(m)) value.append(bin2bcd(y - 2000)) return safeWritei2c(RTC_ADDR, b'\x00' + bytearray(value)) |
Pozostały kod rozszerzyłem o analizę wartości zwracanych przez te funkcje. Umożliwia to obsługę sytuacji związanych z niemożnością skontaktowania się z ds1307.
Kolejną modyfikacją było dodanie funkcji rtc_isrunning(). Znacie ją z implementacji biblioteki RTClib dla Arduino.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# ----------------------------------------- # returns 0 if the clock is not running (CH=1) # returns 1 if the clock is not running (CH=0) # return COMM_ERR if no communication def rtc_isrunning(): if safeWritei2c(RTC_ADDR, b'\x00'): ret = safeReadi2c(RTC_ADDR, 1) if not ret == COMM_ERR: if (ret[0] >> 7): return 0 else: return 1 return COMM_ERR |
Mając taką funkcję, będę wiedział, czy przy starcie powinienem ustawić czas na nowszy – czy może czas zapamiętany w czipie była wcześniej ustawiony przez użytkownika.
Kolejne modyfikacje dotyczyły:
- Klawisz A: wyświetla datę w formacie cyfrowym,
- Klawisz B: wprowadza w tryb ustawiania czasu.
Ostatnie funkcja setTime() umożliwia ustawienie czasu na wyświetlaczu LED – co w oryginalnym kodzie było zaszyte na stałe. W trybie ustawiania daty:
- Klawisz A: zmiana wartości o 1,
- Klawisz B: przełączanie między godzinami (‘hh’), minutami (‘mm”) i przed południem/po południu (am/pm),
- Godziny ustawiane są w zakresie 0…12,
- Minuty ustawiane są w zakresie 0…59,
- Przełączanie na pm zwiększa godziny o 12 (zegarek domyślnie wyświetla godziny w formacie 24 godzinnym),
- Przytrzymaj B i przytrzymaj A żeby zapamiętać ustawioną datę i powrócić do trybu wyświetlania czasu.
Pełny kod:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# Add your Python code here. E.g. from microbit import * # ----------------------------------------- RTC_ADDR = 0x68 # how many times try before give up IO_RETRY = 3 COMM_ERR = -10 IO_RETRY_SLEEP = 200 # ----------------------------------------- # convert bcd to decimal def bcd2bin(v): return v - 6 * (v >> 4) # ----------------------------------------- # convert decimal to bcd def bin2bcd(v): return v + 6 * (v // 10) # ----------------------------------------- # write buffer buf to i2c of address addr # retutns True if ok, COMM_ERR otherwise def safeWritei2c(addr, buf): ok = False for retry in range(0, IO_RETRY): try: i2c.write(addr, buf) ok = True break except: sleep(IO_RETRY_SLEEP) pass if ok: return ok else: return COMM_ERR # ----------------------------------------- # read number of bytes numberOfBytes from i2c device of address addr # retutns True if ok, COMM_ERR otherwise def safeReadi2c(addr, numberOfBytes): ok = False value = [] for retry in range(0, IO_RETRY): try: value = i2c.read(addr, numberOfBytes) ok = True break except: sleep(IO_RETRY_SLEEP) pass if ok: return value else: return COMM_ERR # ----------------------------------------- # returns 0 if the clock is not running (CH=1) # returns 1 if the clock is not running (CH=0) # return COMM_ERR if no communication def rtc_isrunning(): if safeWritei2c(RTC_ADDR, b'\x00'): ret = safeReadi2c(RTC_ADDR, 1) if not ret == COMM_ERR: if (ret[0] >> 7): return 0 else: return 1 return COMM_ERR # ----------------------------------------- # get rtc time def rtc_gettime(): if safeWritei2c(RTC_ADDR, b'\x00'): ret = safeReadi2c(RTC_ADDR, 7) if not ret == COMM_ERR: ss = bcd2bin(ret[0] & 0x7F) mm = bcd2bin(ret[1]) hh = bcd2bin(ret[2]) d = bcd2bin(ret[4]) m = bcd2bin(ret[5]) y = bcd2bin(ret[6]) + 2000 return [y, m, d, hh, mm, ss] return COMM_ERR # ----------------------------------------- # set rtc time def rtc_settime(y, m, d, hh, mm, ss): value = [] value.append(bin2bcd(ss)) value.append(bin2bcd(mm)) value.append(bin2bcd(hh)) value.append(bin2bcd(0)) value.append(bin2bcd(d)) value.append(bin2bcd(m)) value.append(bin2bcd(y - 2000)) return safeWritei2c(RTC_ADDR, b'\x00' + bytearray(value)) # ----------------------------------------- # converts dec value to column display of micro:bit def val2bintime(timeValue): value = [] # display.scroll(str( timeValue ) ) # lowest row is full set value.append(1) value.append((timeValue & 1)) value.append((timeValue & 2) >> 1) value.append((timeValue & 4) >> 2) value.append((timeValue & 8) >> 3) return value # ----------------------------------------- # sets the time using micro:bit led display # press A to increase # press B to switch between hours/minutes/ampm # press B and then A to set the time def setMyTime(hh, mm): HOURS = 0 MINUTES = 1 AMPM = 2 ampm = 0 if hh > 12: hh = hh - 12 ampm = 1 value = [hh, mm, ampm] valueMax = [12, 59, 1] # 0 is hh, 1 is mm, 2 is AM/PM mode = HOURS evenOdd = True display.show('hhh') while True: if not evenOdd: if mode == AMPM: if value[mode] == 0: display.show('A') else: display.show('P') else: display.show(str(value[mode])) else: display.clear() if button_a.is_pressed(): value[mode] = value[mode] + 1 if value[mode] > valueMax[mode]: value[mode] = 0 if button_b.is_pressed(): mode = mode + 1 if mode >= len(value): mode = HOURS if mode == HOURS: display.show('hhh') elif mode == MINUTES: display.show('mmm') else: # ampm display.show('...') if button_a.is_pressed() and button_b.is_pressed(): break evenOdd = not evenOdd sleep(50) if value[AMPM] == 1: value[HOURS] = value[HOURS]+12 return rtc_settime(0, 0, 0, value[HOURS], value[MINUTES], 0) # ----------------------------------------- display.scroll("hello!") # if clock is not running - CH must be set enabled res = rtc_isrunning() if res == 0: if setMyTime() == 1: display.scroll("Set!") elif setMyTime() == 0: # time was no set and no time on chip - let;s get to some default rtc_settime(2018, 2, 1, 23, 55, 0) else: res = COMM_ERR display.scroll("Error") elif res != COMM_ERR: display.clear() evenOdd = False while True: tm = rtc_gettime() hh = tm[3] mm = tm[4] ss = tm[5] # button a: scroll time if button_a.is_pressed(): display.scroll(str(hh)+":"+str(mm)) continue # button b: set time if button_b.is_pressed(): if not setMyTime(hh, mm) == COMM_ERR: display.scroll("Set!") continue # columns of clock cols = [] cols.append(val2bintime(hh//10)) cols.append(val2bintime(hh % 10)) # displa hh:mm divider if not evenOdd: cols.append([1, 1, 1, 0, 0]) else: cols.append([1, 0, 0, 0, 0]) evenOdd = not evenOdd # display 10ths of seconds in bottom line cols.append(val2bintime(mm//10)) cols.append(val2bintime(mm % 10)) # display hour and minute for col in range(0, 5): for row in range(0, 5): if row == 0: if ss//10 > col: cols[col][row] = 1 else: cols[col][row] = 0 if cols[col][row] == 1: display.set_pixel(col, 4-row, 5) else: display.set_pixel(col, 4-row, 0) sleep(500) # loop on COMM_ERR while True: display.scroll("COMM_ERROR") pass |
Schemat
Nie ma co kombinować – typowa aplikacja z instrukcji do ds1307:
RPU to rezystory 10kΩ. Crystal to kwarc 32k. Dodatko podłączyłem go do masy przez kondensator 18pF. Zwróćcie uwagę na pin Vbat – tam podepnę baterię cr2032.
Budowa
Płytka
Złącza micro:bit wyprowadzono na krawędź płytki. Niestety nie przewidziano tu pinów, jedynie styki. SDA/SCL znajdują się miedzy masą GND i zasilaniem 3v (odpowiednio 20 i 19). Żeby dobrać się do nich, trzeba użyć specjalnego złącza krawedziowego:
Zauważcie, że tylko 2 rzędy niższych styków są podłączone. Styki rozmieszczone są co standardowe 2.54mm – ale rzędy przesunięto wzgledem siebie. Mimo to złącze dobrze pasuje do standardowej płytki 5×7.
Oczywiście mogłem użyć mniejszej płytki, ale potrzebuję miejsca dla dodatkowych eksperymentów.
Na płytce umieściłem kolejne elementy. Zacząłem od gniazda na wtyczkę baterii i wyłącznika:
Teraz wlutowałem złącze dla przetwornicy:
Układ ds1307 umieściłem na podstawce:
Na koniec dolutowałem baterię podtrzymująca zegar w wypadku braku zasilania. Niestety nie miałem pod ręką podstawki, zrobiłem to trochę domowym sposobem – z resztek taśmy od gold pinów:
Obudowa
Zegarek potrzebował obudowy. Zrobiłem ją z odpadów sklejki 3mm. Część obudowy jest zdejmowana tak, żeby można było latwo wymienić baterie lub wyjąć całość i przerobić płytkę.
Do budowy wystarczy właściwie piłka reczna, kilka pilników i klej wikolowy. Oczywiście dużo łatwiej będzie, jeżeli dysponujecie piłą stołową, mini szlifierką itp. – ale przy odrobinie chęci można się bez nich obejść.
Podsumowanie
Jak widzicie, mimo ‘zabawkowego’ wyglądu, na podstawie micro:bit można budować nowe urządzenia nie gorzej niż w przypadku Arduino. Zamiast bloczków JavaScript można użyć pythona, który oferuje znacznie większe możliwości. Niestety wymaga też większej ‘biegłości’ w programowaniu.
Kolejną zaletą micro:bit jest niskie napięcie zasilania. Tu wystarczyły dwa paluszki AA. W urządzeniach wbudowanych, gdzie mamy zazwyczaj niewiele miejsca w obudowie, może być to dużą zaletą. Z drugiej strony – czasami do zintegrowania całości – konieczne są dodatkowe zabiegi (np. dopasowanie poziomów napięć logiki).
Zegarek działa już ponad tydzień. Prawie przyzwyczaiłem się do odczytywania z niego czasu. Chociaż niektórzy znajomi… wolą spojrzeć na komórkę…:)
Źródła
- Konwerter msx-elektronika
- Funkcje i2c – dokumentacja
- Kod, na którym się oparłem
- Blog Jarzębski o ds1307
- Dokumentacja DS1307
- Zegar czasu rzeczywistego: podłączenie pcf8563 do Arduino,
- Kolejny zegar czasu rzeczywistego: podłączenie ds1307 do Arduino i kilka słów o i2c.
Tak. Fajne. Nawet bardzo.
Ale dostałbym szału próbując ustalić godzinę w nocy po przebudzeniu. Albo po użyciu napojów rozweselających.
Zajebistość 10, użyteczność 1.
Pomyśl tak: odczytasz godzinę – możesz jechać, nie odczytasz – kluczyki zostają w domu:)
Pozdrawiam,
A
PS. Warstwę prezentacji można łatwo zmienić:) Nawet zachęcam:) Mi chodziło o same możliwości zastosowania micro:bit:)