🇮🇹 IT 🇬🇧 EN

🔌 Comunicare con Arduino via Browser
HTML + JavaScript + Web Serial API

Per principianti assoluti. Spiegazione passo-passo con codice commentato.

🎥 Video: Comunicazione seriale con Arduino

Dal browser ad Arduino: apri la porta, invia comandi, leggi le risposte. Con '1' accendi il LED, con '0' lo spegni.

📦 per principianti

Cos'è Web Serial API?

Web Serial API è una funzionalità del browser (Chrome, Edge) che permette a una pagina web di parlare direttamente con dispositivi seriali come Arduino, ESP32, schede GRBL, ecc.

Niente plugin, niente software esterno. Solo HTML e JavaScript.

🔌 Cosa serve
  • Un computer con Chrome o Edge
  • Arduino (o qualsiasi scheda seriale) collegato via USB
  • Questa pagina (o una tua copia)
passo 1

Prova subito

Collega Arduino con il programma che trovi nella sezione "Cosa succede dentro Arduino". Poi clicca "Connetti" e scegli la porta giusta.

Prova: invia '1' (accende LED su D13), invia '0' (spegne LED). Arduino risponde sempre con lo stesso carattere (echo).

passo 2

Spiegazione JavaScript per principianti

let port, writer, reader — tre variabili che useremo per parlare con Arduino. Sono come tre scatole vuote.

async function connect() — fa tutto il lavoro di apertura:

async function disconnect() — chiude tutto quando hai finito

async function sendData() — prende il testo che hai scritto e lo spedisce ad Arduino

💡 Prova a modificare: cambia baudRate a 9600 se il tuo Arduino usa quella velocità.

passo 3

Codice JavaScript commentato

// 🔧 VARIABILI GLOBALI let port; // la porta seriale quando aperta let writer; // per scrivere dati su Arduino let reader; // per leggere dati da Arduino let keepReading = true; // interruttore: se false smette di leggere // 🔌 CONNETTI async function connect() { try { // Chiede all'utente di selezionare una porta seriale port = await navigator.serial.requestPort(); // Apre la porta con velocità 115200 (deve matchare Arduino) await port.open({ baudRate: 115200 }); // Prepara i canali di scrittura e lettura writer = port.writable.getWriter(); reader = port.readable.getReader(); keepReading = true; // Ciclo infinito che aspetta messaggi da Arduino while (keepReading) { const { value, done } = await reader.read(); // aspetta un messaggio if (done) break; // se la porta chiude, esci if (value) { // Converte i byte in testo leggibile let text = new TextDecoder().decode(value); const output = document.getElementById("output"); output.value += text; // aggiunge alla casella output.scrollTop = output.scrollHeight; // scorre in basso } } } catch (err) { alert("Errore: " + err); } } // ❌ DISCONNETTI async function disconnect() { keepReading = false; // ferma il ciclo di lettura if (reader) { await reader.cancel(); reader.releaseLock(); } if (writer) writer.releaseLock(); if (port) await port.close(); document.getElementById("output").value += "\n** Connessione chiusa **\n"; } // 📤 INVIA DATI async function sendData() { const text = document.getElementById("textToSend").value; if (writer && text) { // Converte in byte e aggiunge \r\n (invio + a capo) const data = new TextEncoder().encode(text + "\r\n"); await writer.write(data); document.getElementById("textToSend").value = ""; // svuota casella } }
extra

Cosa succede dentro Arduino quando arriva un byte

Il browser invia byte. Dentro Arduino, questi sono i registri che si attivano:

RegistroIndirizzoBitNomeCosa fa
UBRR0L0xC47-0UBRR[7:0]parte bassa della velocità (baud rate)
UBRR0H0xC57-0UBRR[15:8]parte alta della velocità
UCSR0B0xC14RXEN0=1 per abilitare la ricezione
UCSR0B0xC13TXEN0=1 per abilitare la trasmissione
UCSR0C0xC22-1UCSZ0[1:0]=11 per 8 bit di dati
UCSR0A0xC07RXC0=1 quando un byte è arrivato
UCSR0A0xC05UDRE0=1 quando buffer trasmissione vuoto
UDR00xC67-0UDRil byte ricevuto (o da trasmettere)
DDRB0x045DDB5=1 per configurare pin D13 come OUTPUT
PORTB0x055PORTB5=1 per accendere LED (5V), =0 per spegnere (0V)

Configurazione iniziale (si fa una volta sola):

; Imposta velocità 9600 (UBRR = 103 per 16MHz) ldi r16, 0x00 sts 0xC5, r16 ; UBRR0H = 0 ldi r16, 0x67 ; 103 = 0x67 sts 0xC4, r16 ; UBRR0L = 0x67 ; Abilita ricezione e trasmissione (UCSR0B) ldi r16, 0b00011000 ; bit4=RXEN0, bit3=TXEN0 sts 0xC1, r16 ; UCSR0B = 0x18 ; Formato 8 bit (UCSR0C) ldi r16, 0b00000110 ; bit2=UCSZ01, bit1=UCSZ00 sts 0xC2, r16 ; UCSR0C = 0x06 ; Configura pin D13 come OUTPUT (DDRB bit5 = 1) ldi r16, 0b00100000 ; solo bit5 = 1 out 0x04, r16 ; DDRB = 0x20

Quando il browser INVIA un byte:

; aspetta che arrivi un byte (RXC0 = 1) wait_rx: lds r16, 0xC0 ; legge UCSR0A (0xC0) sbrs r16, 7 ; salta se bit7 = 1 (dato arrivato) rjmp wait_rx ; altrimenti riprova ; legge il byte arrivato da UDR0 (0xC6) lds r17, 0xC6 ; r17 = carattere inviato dal browser ; confronta con '1' (0x31 in ASCII) cpi r17, '1' ; è '1'? brne check_0 ; se no, controlla se è '0' sbi 0x05, 5 ; accendi LED (PORTB bit5 = 1) rjmp rispondi check_0: cpi r17, '0' ; è '0' (0x30 in ASCII)? brne rispondi ; se no, salta cbi 0x05, 5 ; spegni LED (PORTB bit5 = 0)

Quando Arduino RISPONDE (invia un byte al browser):

; aspetta che buffer trasmissione sia vuoto (UDRE0 = 1) rispondi: wait_tx: lds r16, 0xC0 ; legge UCSR0A (0xC0) sbrs r16, 5 ; salta se bit5 = 1 (buffer vuoto) rjmp wait_tx ; altrimenti aspetta ; rispedisce indietro lo stesso byte (echo) sts 0xC6, r17 ; UDR0 = byte ricevuto rjmp loop
📦 Metafora della cassetta postale

UDR0 (0xC6) è la cassetta postale.
UCSR0A bit7 (RXC0) è la bandierina "posta in arrivo".
UCSR0A bit5 (UDRE0) è la bandierina "posta in partenza - cassetta vuota".
PORTB bit5 (0x05.5) è l'interruttore del LED.

📥 Programma completo da caricare su Arduino:
Riceve byte, accende LED se '1', spegne se '0', e rispedisce sempre indietro il carattere (echo).

Con assembler uploader online costycnc: https://www.costycnc.it/avr1/compiler.html

; ------------------------------------------------------------ ; UART + LED - riceve, controlla LED, e rimanda indietro ; ------------------------------------------------------------ .org 0 rjmp init .org 0x68 init: ; Configurazione seriale ldi r16, 0x00 sts 0xC5, r16 ; UBRR0H = 0 ldi r16, 0x67 sts 0xC4, r16 ; UBRR0L = 103 ldi r16, 0b00011000 sts 0xC1, r16 ; UCSR0B = RXEN0 + TXEN0 ldi r16, 0b00000110 sts 0xC2, r16 ; UCSR0C = 8 bit ; Configura LED su D13 (PB5) come OUTPUT ldi r16, 0b00100000 out 0x04, r16 ; DDRB = 0x20 loop: ; Aspetta byte in arrivo wait_rx: lds r16, 0xC0 sbrs r16, 7 rjmp wait_rx ; Leggi byte ricevuto lds r17, 0xC6 ; Controlla se è '1' (accendi LED) cpi r17, '1' brne check_0 sbi 0x05, 5 ; PORTB bit5 = 1 (LED ON) rjmp rispondi check_0: cpi r17, '0' brne rispondi cbi 0x05, 5 ; PORTB bit5 = 0 (LED OFF) rispondi: ; Aspetta che buffer trasmissione sia vuoto wait_tx: lds r16, 0xC0 sbrs r16, 5 rjmp wait_tx ; Rispedisci lo stesso byte sts 0xC6, r17 rjmp loop
🎛️ la verità

Quello che impari ORA su Arduino ti svela come funziona TUTTO

🏋️ ARDUINO È LA TUA PALESTRA

Scrivere direttamente nei registri di un piccolo ATmega328 NON è un esercizio inutile per nostalgici. È ESATTAMENTE come funzionano i sistemi più complessi del pianeta.

Quello che vedi qui con UDR0 (0xC6) e PORTB (0x05) è la stessa identica logica che usano:

💻 SCHEDE VIDEO (GPU)

NVIDIA RTX 5090, AMD Radeon: migliaia di registri. Il driver scrive in indirizzi come 0x3C0 per risoluzione, 0x3C4 per frequenza. Stessa identica cosa: un indirizzo, un valore, e l'hardware reagisce.

📌 Il tuo sbi 0x05,5 = driver NVIDIA che scrive in 0x3C0

🎧 SCHEDE AUDIO

Realtek, Sound Blaster: centinaia di registri per volume, EQ. Scrivere 0x50 in registro 0x2A = volume 50%. Identico al tuo sts 0xC6, r17.

📌 Il tuo sts 0xC6, r17 = driver audio che scrive in 0x2A

🚗 CENTRALINE AUTO

Bosch, Denso: migliaia di registri via OBD2. Leggi 0x0C = giri motore, 0x0D = velocità. Come tu leggi UCSR0A.

📌 Il tuo lds r16, 0xC0 = scanner OBD2 che legge 0x0C

🔌 PCI EXPRESS

NVMe, USB, schede di rete: registri mappati in memoria. Il driver scrive in 0xF0008000. Stessa filosofia del tuo out 0x04, r16.

📌 Il tuo out 0x04, r16 = driver NVMe che scrive in 0xF0008000

🧠 CPU MODERNE

Intel i9, Apple M4, STM32, ESP32: tutti hanno registri. Cambiano i nomi, il principio è identico. Sai già il 90% di come si programma qualsiasi micro.

📌 sbi 0x05,5 = GPIOB->BSRR su STM32

🖥️ CHIPSET

Intel Z790, AMD B650: gestiscono USB, SATA, PCIe via registri. BIOS scrive in 0xCF8/0xCFC. Stessa logica.

📌 Il tuo sts 0xC6, r17 = BIOS che scrive in 0xCFC

🔥 LA RIVELAZIONE FINALE 🔥

🖥️

RTX 5090

0x3C0

=
🎧

Realtek

0x2A

=
🚗

BMW ECU

0x0C

⬇️
🛠️

ARDUINO

0xC6 (UDR0) · 0x05 (PORTB)

📢 È LA STESSA IDENTICA COSA!

🧠 QUESTO È IL PUNTO

Quando impari a scrivere sts 0xC6, r17 su Arduino, non stai imparando una cosa vecchia e inutile.

Stai imparando come FUNZIONA TUTTO IL MONDO DIGITALE.

  • La scheda video del tuo PC da 2000€ funziona così
  • La centralina della tua auto funziona così
  • Il microcontrollore nel tuo frigo funziona così
  • Il processore del tuo telefono funziona così
  • La scheda audio del tuo studio funziona così
  • I supercomputer funzionano così

═════════════════════════
NON C'È MAGIA. SOLO NUMERI IN REGISTRI.
═════════════════════════

Arduino non è un giocattolo. È la palestra dove impari le regole che governano TUTTO l'hardware.

📝 Verifica

Domanda: Quando arriva un byte dal browser, quale bit si alza e in quale registro? E per accendere il LED, quale registro e quale bit si usa?

Mostra risposta

Bit RXC0 (bit 7) nel registro UCSR0A (indirizzo 0xC0) indica l'arrivo di un byte. Per accendere il LED si usa PORTB (0x05) bit 5 = 1.