TechLab - Laboratórium budúcnosti
- Laboratórium budúcnosti
- Naše vybavenie
- MICROBIT-BLOCKLY
- JOYSTICKBIT-BLOCKLY
- MAKECODE ARCADE
- SCULPFUN LASER
- Linux – Rýchlokurz
- DOBOT MAGICIAN - PYTHON + LINUX
- XGO ROBOTICKÝ PES - BLOCKLY
- CUTEBOT - AUTONÓMNE VOZIDLO
- Joystick:bit – Rozšírenie – Lekcia 1
- Joystick:bit – Rozšírenie – Lekcia 2
- Joystick:bit – Rozšírenie – Lekcia 3
- Joystick:bit – Rozšírenie – Lekcia 4
- Joystick:bit – Rozšírenie – Lekcia 5
- Poznámky
Poznámky
1. WebSerial
Použijeme veľmi jednoduchý protokol.
Počítač → micro:bit:
LED 1alebo
LED 0micro:bit → počítač:
READYLED 1LED 0a pri stlačení tlačidla:
BUTTON AKaždá správa končí
\n.
2. Priprav micro:bit
Najjednoduchšie je použiť MakeCode a nahrať tento firmware.
Microsoft MakeCode pre micro:bit
Vytvor nový projekt a otvor JavaScript.
Použi:
serial.redirectToUSB() basic.showIcon(IconNames.Happy) serial.writeLine("READY") input.onButtonPressed(Button.A, function () { serial.writeLine("BUTTON A") }) serial.onDataReceived(serial.delimiters(Delimiters.NewLine), function () { let command = serial.readUntil(serial.delimiters(Delimiters.NewLine)) if (command == "LED 1") { led.plot(2, 2) serial.writeLine("LED 1") } if (command == "LED 0") { led.unplot(2, 2) serial.writeLine("LED 0") } })serial.redirectToUSB()zabezpečí, že serial komunikácia ide cez USB; micro:bit používa USB ako predvolenú cestu pre sériové dáta.Poznámka: Toto ovláda stredný bod 5×5 LED displeja. Neskôr môžeme namiesto jedného LED bodu ovládať externú LED pripojenú na pin.
Nahraj program do micro:bitu.
Po nahratí ho nechaj pripojený cez USB.
3. Vytvor
weblab01.htmlTeraz prichádza zaujímavejšia časť.
Vytvor jediný súbor:
weblab01.htmlA vlož doň celé toto:
<!DOCTYPE html> <html lang="sk"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>WebLab 01</title> <style> * { box-sizing: border-box; } body { margin: 0; padding: 30px; font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: #f3f4f6; color: #111827; } main { max-width: 760px; margin: auto; } h1 { margin-bottom: 5px; } .subtitle { color: #6b7280; margin-top: 0; } .card { background: white; border-radius: 16px; padding: 24px; margin-top: 20px; box-shadow: 0 4px 20px rgba(0,0,0,0.06); } .status { display: flex; align-items: center; gap: 10px; padding: 12px 16px; border-radius: 10px; background: #f3f4f6; } .dot { width: 12px; height: 12px; border-radius: 50%; background: #9ca3af; } .dot.connected { background: #22c55e; } button { border: 0; border-radius: 10px; padding: 12px 18px; font-size: 16px; font-weight: 600; cursor: pointer; } button:disabled { opacity: 0.5; cursor: not-allowed; } .primary { background: #111827; color: white; } .led { font-size: 48px; margin: 20px 0; } .log { height: 180px; overflow-y: auto; background: #111827; color: #d1d5db; border-radius: 10px; padding: 15px; font-family: monospace; font-size: 14px; } .log div { margin-bottom: 5px; } .value { font-size: 28px; font-weight: bold; } </style> </head> <body> <main> <h1>WebLab</h1> <p class="subtitle"> Lekcia 01 — môj prvý elektronický projekt </p> <!-- CONNECTION --> <section class="card"> <h2>micro:bit</h2> <div class="status"> <div id="statusDot" class="dot"> </div> <span id="statusText"> Nepripojené </span> </div> <br> <button id="connectButton" class="primary"> Pripojiť micro:bit </button> </section> <!-- LED --> <section class="card"> <h2>LED</h2> <div id="ledDisplay" class="led"> ⚫ </div> <button id="ledButton" disabled> Zapnúť LED </button> </section> <!-- BUTTON --> <section class="card"> <h2>Tlačidlo A</h2> <div id="buttonValue" class="value"> Nestlačené </div> </section> <!-- LOG --> <section class="card"> <h2>Komunikácia</h2> <div id="log" class="log"> </div> </section> </main> <script> // ============================================================ // STAV APLIKÁCIE // ============================================================ let port = null; let reader = null; let writer = null; let ledOn = false; // ============================================================ // UI ELEMENTY // ============================================================ const connectButton = document.querySelector("#connectButton"); const ledButton = document.querySelector("#ledButton"); const ledDisplay = document.querySelector("#ledDisplay"); const buttonValue = document.querySelector("#buttonValue"); const statusText = document.querySelector("#statusText"); const statusDot = document.querySelector("#statusDot"); const logElement = document.querySelector("#log"); // ============================================================ // LOG // ============================================================ function log(message) { const line = document.createElement("div"); line.textContent = "> " + message; logElement.appendChild(line); logElement.scrollTop = logElement.scrollHeight; } // ============================================================ // STAV PRIPOJENIA // ============================================================ function setConnected(connected) { if (connected) { statusText.textContent = "Pripojené"; statusDot.classList.add( "connected" ); connectButton.textContent = "Pripojené"; connectButton.disabled = true; ledButton.disabled = false; } else { statusText.textContent = "Nepripojené"; statusDot.classList.remove( "connected" ); connectButton.textContent = "Pripojiť micro:bit"; connectButton.disabled = false; ledButton.disabled = true; } } // ============================================================ // ZMENA LED V UI // ============================================================ function updateLedUI() { if (ledOn) { ledDisplay.textContent = "🔴"; ledButton.textContent = "Vypnúť LED"; } else { ledDisplay.textContent = "⚫"; ledButton.textContent = "Zapnúť LED"; } } // ============================================================ // ODOSLANIE SPRÁVY // ============================================================ async function send(message) { if (!writer) { log("Nie je pripojené zariadenie."); return; } const data = message + "\n"; const bytes = new TextEncoder().encode(data); await writer.write(bytes); log("TX: " + message); } // ============================================================ // SPRACOVANIE SPRÁV // ============================================================ function handleMessage(message) { message = message.trim(); if (!message) { return; } log("RX: " + message); // micro:bit potvrdil LED if (message === "LED 1") { ledOn = true; updateLedUI(); } if (message === "LED 0") { ledOn = false; updateLedUI(); } // tlačidlo A if (message === "BUTTON A") { buttonValue.textContent = "Stlačené!"; setTimeout(() => { buttonValue.textContent = "Nestlačené"; }, 500); } // micro:bit sa ozval if (message === "READY") { log("micro:bit je pripravený."); } } // ============================================================ // ČÍTANIE ZO SERIAL PORTU // ============================================================ async function readLoop() { const decoder = new TextDecoder(); let buffer = ""; reader = port.readable.getReader(); try { while (true) { const { value, done } = await reader.read(); if (done) { break; } if (value) { buffer += decoder.decode( value, { stream: true } ); const lines = buffer.split("\n"); buffer = lines.pop(); for (const line of lines) { handleMessage(line); } } } } catch (error) { log( "Chyba čítania: " + error.message ); } finally { reader.releaseLock(); reader = null; } } // ============================================================ // PRIPOJENIE // ============================================================ async function connect() { try { if (!("serial" in navigator)) { alert( "Tento prehliadač nepodporuje Web Serial." ); return; } log("Vyber sériové zariadenie..."); // Používateľ vyberie micro:bit port = await navigator.serial.requestPort(); // Otvoríme sériové spojenie await port.open({ baudRate: 115200 }); log("Serial port otvorený."); // Vytvoríme writer writer = port.writable.getWriter(); setConnected(true); // Spustíme čítanie readLoop(); } catch (error) { log( "Chyba pripojenia: " + error.message ); port = null; } } // ============================================================ // LED // ============================================================ async function toggleLed() { if (ledOn) { await send("LED 0"); } else { await send("LED 1"); } } // ============================================================ // EVENTY // ============================================================ connectButton.addEventListener( "click", connect ); ledButton.addEventListener( "click", toggleLed ); </script> </body> </html>
4. Prvé spustenie
Tu je jedna dôležitá vec.
Web Serial je secure-context API a nie je dostupné vo všetkých prehliadačoch.
navigator.serialfunguje napríklad v podporovaných Chromium prehliadačoch, ale nie je univerzálnou súčasťou všetkých browserov.🚀 Web Serial API + micro:bit + Joystick:bit - tutoriál
V tomto projekte si postavíme webovú stránku, ktorá cez USB kábel priamo rozpráva s micro:bitom:
- Hýbeš joystickom na Joystick:bit doske → na obrazovke počítača lieta vesmírna loď
- Klikneš myšou na stránke → na micro:bite sa rozsvieti LEDka
Naučíš sa pri tom:
- čo je sériový port a ako s ním hovorí prehliadač (Web Serial API),
- ako naprogramovať micro:bit, aby posielal a prijímal dáta cez USB,
- ako postaviť jednoduché webové GUI (tlačidlá, canvas hru, mriežku LED).
Čo budeš potrebovať
Vec Poznámka micro:bit (V1 alebo V2) mozog celého projektu Joystick:bit / gamer shield od Elecrow joystick na piny P1 (X) a P2 (Y) USB kábel micro → USB-A slúži aj na napájanie, aj na dáta Počítač s Google Chrome alebo Microsoft Edge Web Serial API funguje LEN v týchto prehliadačoch (nie Firefox, nie Safari, nie mobil) Účet netreba MakeCode aj naša stránka fungujú bez registrácie ⚠️ Dôležité: Web Serial API vyžaduje, aby stránka bežala buď na
https://alebo ako lokálny súbor otvorený priamo v Chrome/Edge. Preto sme pripravili hotový súbordemo-vesmirna-lod.html— stačí ho dvojklikom otvoriť v Chrome alebo Edge z tvojho počítača.
Krok 1 — Ako funguje Joystick:bit
Elecrow gamer shield je jednoduchý — nemá vlastný čip, len posiela signály priamo do pinov micro:bitu:
Súčiastka Pin Typ Joystick — os X P1 analógová (0–1023) Joystick — os Y P2 analógová (0–1023) Tlačidlo C P12 digitálne Tlačidlo D P13 digitálne Tlačidlo E P14 digitálne Tlačidlo F P15 digitálne Vibračný motor P16 digitálny výstup Tlačidlá A, B — vstavané priamo na micro:bit Keď joystick pustíš, obe analógové hodnoty sa vrátia okolo stredu, cca 512. Fyzicky je ale os na tomto shielde otočená — surová hodnota
0na oboch pinoch zodpovedá vychýleniu joysticku do pravého dolného rohu, nie do ľavého horného, ako by sme čakali. Micro:bit posiela tieto surové hodnoty bez akejkoľvek úpravy (0 až 1023) — otočenie smeru aj určenie, kedy sa má loď hýbať (hraničná hodnota / deadzone), rieši až JavaScript v HTML appke v kroku 3. Je to praktickejšie: keď budeš chcieť doladiť citlivosť alebo smer, stačí upraviť pár riadkov v appke a hneď obnoviť stránku — bez opakovaného nahrávania nového programu do micro:bitu.
Krok 2 — Program pre micro:bit (MakeCode)
- Choď na makecode.microbit.org
- Klikni New Project, daj mu meno napr.
vesmirna-lod - Hore vpravo prepni z Blocks na JavaScript
- Zmaž pôvodný kód a vlož tento:
// ============================================================ // MICRO:BIT — VESMÍRNA LOĎ (posielanie joysticku + prijímanie LED) // ============================================================ // Uisti sa, že sériová komunikácia ide cez USB kábel serial.redirectToUSB() // Rýchlosť komunikácie musí byť rovnaká ako v prehliadači: 115200 serial.setBaudRate(BaudRate.BaudRate115200) // Tlačidlá C, D, E, F na shielde sú digitálne vstupy — zapneme im // interný pull-up rezistor, aby v pokoji čítali 1 a pri stlačení 0 pins.setPull(DigitalPin.P12, PinPullMode.PullUp) pins.setPull(DigitalPin.P13, PinPullMode.PullUp) pins.setPull(DigitalPin.P14, PinPullMode.PullUp) pins.setPull(DigitalPin.P15, PinPullMode.PullUp) // Pri štarte ukáž usmiate zjavenie, nech vieme, že je nahraté basic.showIcon(IconNames.Happy) // -------------------------------------------------------- // 1) STÁLE DOKOLA: prečítaj joystick + tlačidlá a pošli SUROVÉ dáta do počítača // -------------------------------------------------------- basic.forever(function () { let x = pins.analogReadPin(AnalogPin.P1) // surová hodnota 0-1023 let y = pins.analogReadPin(AnalogPin.P2) // surová hodnota 0-1023 let a = input.buttonIsPressed(Button.A) ? 1 : 0 let b = input.buttonIsPressed(Button.B) ? 1 : 0 // tlačidlá shieldu majú pull-up, takže stlačené = 0 (preto ten "== 0") let c = pins.digitalReadPin(DigitalPin.P12) == 0 ? 1 : 0 let d = pins.digitalReadPin(DigitalPin.P13) == 0 ? 1 : 0 let e = pins.digitalReadPin(DigitalPin.P14) == 0 ? 1 : 0 let f = pins.digitalReadPin(DigitalPin.P15) == 0 ? 1 : 0 // Formát riadku, ktorý pošleme: X:512,Y:611,A:0,B:1,C:0,D:0,E:0,F:1 serial.writeLine( "X:" + x + ",Y:" + y + ",A:" + a + ",B:" + b + ",C:" + c + ",D:" + d + ",E:" + e + ",F:" + f ) basic.pause(100) // 10-krát za sekundu stačí }) // -------------------------------------------------------- // 2) KEĎ PRÍDE SPRÁVA Z POČÍTAČA: rozsvieť/zhasni LED alebo zavibruj // -------------------------------------------------------- serial.onDataReceived(serial.delimiters(Delimiters.NewLine), function () { let data = serial.readUntil(serial.delimiters(Delimiters.NewLine)) if (data == "CLEAR") { basic.clearScreen() } else if (data == "ALLON") { for (let r = 0; r <= 4; r++) { for (let c = 0; c <= 4; c++) { led.plot(c, r) } } } else if (data == "VIB") { // krátky impulz vibračného motora (cca 300 ms) pins.digitalWritePin(DigitalPin.P16, 1) basic.pause(300) pins.digitalWritePin(DigitalPin.P16, 0) } else if (data.substr(0, 4) == "LED:") { // formát: LED:riadok,stlpec,stav napr. LED:2,3,1 let parts = data.substr(4).split(",") let row = parseInt(parts[0]) let col = parseInt(parts[1]) let state = parseInt(parts[2]) if (state == 1) { led.plot(col, row) } else { led.unplot(col, row) } } })- Pripoj micro:bit USB káblom, klikni Download — súbor
.hexsa stiahne - Presuň (drag & drop) stiahnutý
.hexsúbor na disk MICROBIT, ktorý sa objaví v počítači ako USB kľúč - Micro:bit na chvíľu zabliká a nahrá si program — keď skončí, ukáže usmiatu tvár
Ako to funguje — vysvetlenie kódu
serial.redirectToUSB()— hovorí micro:bitu "posielaj sériové správy cez USB kábel", nie cez fyzické piny.pins.setPull(DigitalPin.P12, PinPullMode.PullUp)— tlačidlá C–F nemajú vlastný rezistor, takže bez tohto nastavenia by pin "plával" a čítal by náhodné hodnoty. Pull-up zariadi, že v pokoji je na pine jasná 1 a stlačením tlačidla sa spojí so zemou, čiže prečítame 0 — preto neskôr píšeme== 0 ? 1 : 0(otočíme logiku, nech nám "1" vždy znamená "stlačené").basic.forever(...)— nekonečná slučka, ktorá beží stále dokola, presne akowhile(true)v iných jazykoch.pins.analogReadPin(AnalogPin.P1)— prečíta napätie na pine P1 a vráti surové číslo od 0 (0V) po 1023 (3.3V) — presne to isté číslo, čo posiela joystick. Toto surové číslo posielame priamo, bez akéhokoľvek prepočtu — smer a citlivosť riešime až v HTML appke (viď nižšie), kde sa to ľahšie ladí bez opakovaného nahrávania kódu do micro:bitu.pins.digitalReadPin(DigitalPin.P12)— prečíta, či je na pine 0 alebo 1 (digitálny vstup, žiadne medzičísla ako pri analógovom pine).serial.writeLine(...)— pošle jeden riadok textu cez USB do počítača. Text si poskladáme spájaním (+) tak, aby appka vedela, kde je X, Y a jednotlivé tlačidlá.serial.onDataReceived(...)— toto je "poslucháč": kedykoľvek príde z počítača kompletný riadok textu (ukončený znakom nového riadku), spustí sa tento kód.led.plot(stlpec, riadok)aled.unplot(...)— rozsvieti/zhasne jednu konkrétnu LED na 5×5 displeji micro:bitu.pins.digitalWritePin(DigitalPin.P16, 1)— pošle na pin plné napätie, čím sa roztočí vibračný motor; po 300 ms ho vypneme späť na 0.
💡 Ak chceš kód napísať cez Bloky namiesto JavaScriptu, presne rovnaký efekt dosiahneš blokmi
serial redirect to USB,forever,analog read pin,serial write line,serial on data receivedaplot/unplot— nájdeš ich v kategóriách Serial, Pins, Led a Input.
Krok 3 — Webová stránka (HTML demo)
Otvor súbor
demo-vesmirna-lod.htmldvojklikom v Chrome alebo Edge.Stránka má tri časti:
- Ovládanie lode joystickom — canvas s vesmírnou loďou, ktorá reaguje na dáta prichádzajúce z micro:bitu
- Rozsvecovanie LED — klikacia mriežka 5×5, ktorá posiela príkazy späť do micro:bitu
- Konzola sériového portu — surový log všetkého, čo si stránka a micro:bit posielajú (super na debugovanie!)
Ako sa pripojiť
- Klikni 🔌 Pripojiť micro:bit
- Prehliadač ukáže zoznam sériových portov — vyber ten s micro:bitom (často sa volá "mbed Serial Port" alebo podobne)
- Hotovo — hýb joystickom a sleduj, ako sa loď hýbe!
Vysvetlenie kódu stránky (podľa častí v súbore)
Časť A – kontrola podpory
if (!('serial' in navigator)) { warnBox.classList.add('show'); }Web Serial API nie je vo všetkých prehliadačoch. Tento riadok skontroluje, či prehliadač objekt
navigator.serialvôbec má, a ak nie, zobrazí varovanie.Časť B – pripojenie
port = await navigator.serial.requestPort(); await port.open({ baudRate: 115200 });requestPort()otvorí systémové okno na výber zariadenia — toto vie spraviť len samotný používateľ kliknutím (bezpečnostné pravidlo prehliadača).open()potom otvorí komunikáciu s rovnakou rýchlosťou (baud rate), akú sme nastavili aj na micro:bite.Časť C – čítanie dát
const decoder = new TextDecoderStream(); port.readable.pipeTo(decoder.writable); const reader = decoder.readable.getReader();Dáta prichádzajú po malých kúskoch (bajtoch), nie vždy pekne po celých riadkoch. Preto si ich skladáme do premennej
buffer, kým nenájdeme znak nového riadku\n— až potom vieme, že máme kompletnú správu.Časť D – posielanie dát
const data = new TextEncoder().encode(text + '\n'); await writer.write(data);Presný opak čítania — text zmeníme na bajty (
TextEncoder) a pošleme cezwriter. Nezabudni na\nna konci — presne to isté hľadá aj kód na micro:bite (Delimiters.NewLine).Časť F – hra
function axisToSpeed(raw) { const offset = raw - CENTER; // odchýlka od stredu (512) if (Math.abs(offset) < DEADZONE) return 0; // v strede sa loď nehýbe return -(offset / CENTER) * MAX_SPEED; // mínus = invertovanie smeru }Keďže joystick v strede posiela hodnotu okolo 512 (nie 0), musíme si najprv vypočítať odchýlku od stredu (
offset). Ak je odchýlka malá (menej akoDEADZONE), loď sa nehýbe — to je "mŕtva zóna", ktorá rieši prirodzené chvenie joysticku. Mínus pred zátvorkou invertuje smer oboch osí naraz — presne to potrebujeme, lebo shield má os fyzicky otočenú (surová hodnota 0 zodpovedá opačnému rohu, než by sme čakali, pozri krok 1).💡 Ak sa ti loď hýbe opačným smerom, než čakáš, len pri jednej osi (napríklad X je správne, ale Y obrátené), stačí spraviť dve samostatné funkcie — jednu s mínusom a jednu bez — a každú os cez ňu poslať zvlášť.
Časť G – LED mriežka
sendLine(`LED:${row},${col},${newState ? 1 : 0}`);Keď klikneš na políčko, poskladáme presne ten istý formát textu, aký očakáva náš kód na micro:bite (
LED:riadok,stlpec,stav), a pošleme ho.
Krok 4 — Over si, že to funguje
Vyskúšaj postupne:
- [ ] Po pripojení sa v konzole objavujú riadky typu
X:512,Y:498,A:0,B:0,C:0,D:0,E:0,F:0 - [ ] Keď hýbeš joystickom doprava, loď letí doprava (ak nie, over si tip v kroku 1 o otočení osi)
- [ ] Keď stlačíš tlačidlo A alebo B na micro:bite, v telemetrii sa to prejaví
- [ ] Keď stlačíš tlačidlo C, D, E alebo F na shielde, v telemetrii sa to tiež prejaví
- [ ] Klik na LED políčko rozsvieti presne tú istú LEDku na micro:bite
- [ ] Tlačidlo "Zhasni všetky" vyčistí displej micro:bitu
- [ ] Tlačidlo "Test vibrácie" rozochveje vibračný motor na cca 0,3 sekundy
Ak niečo nefunguje, konzola sériového portu (dole na stránke) ti ukáže presne, čo sa posiela a prijíma — najčastejšia chyba býva zlá rýchlosť (baud rate) alebo zabudnuté
\nna konci správy.
Nápady na rozšírenie (domáca úloha 🎯)
- Zvuk — pridaj na micro:bit
music.playTone(), keď loď narazí do okraja - Farby — ak máš NeoPixel pásik, skús namiesto LED matice ovládať farebné LEDky
- Skóre — pridaj na stránku hviezdy, ktoré loď zbiera, a počítadlo skóre
- Obojsmerná hra — pošli z počítača na micro:bit pozíciu "nepriateľa", ktorý sa zobrazí ako blikajúca LED
- Vlastný tvar lode — priprav kresliacu funkciu v
drawShip()pre inú loď (raketu, UFO, auto...)
Prehľad komunikačného protokolu
Aby si sa v projekte vedel rýchlo zorientovať, tu je zhrnutie "reči", ktorou spolu micro:bit a stránka hovoria:
micro:bit → počítač (posiela sa 10× za sekundu, surové hodnoty)
X:<0-1023>,Y:<0-1023>,A:<0/1>,B:<0/1>,C:<0/1>,D:<0/1>,E:<0/1>,F:<0/1>počítač → micro:bit (posiela sa pri kliknutí)
LED:<riadok 0-4>,<stlpec 0-4>,<0=zhasni/1=rozsvieť> ALLON CLEAR VIBKaždá správa v oboch smeroch končí znakom nového riadku (
\n) — to je signál "správa skončila, môžeš ju spracovať".
