From browser to Arduino: open the port, send commands, read responses. With '1' turn LED on, with '0' turn it off.
Web Serial API is a browser feature (Chrome, Edge) that allows a web page to talk directly to serial devices like Arduino, ESP32, GRBL boards, etc.
No plugins, no external software. Just HTML and JavaScript.
Connect Arduino with the program in the "What happens inside Arduino" section. Then click "Connect" and choose the right port.
Try: send '1' (turns LED on D13 on), send '0' (turns LED off). Arduino always echoes back the same character.
let port, writer, reader โ three variables we'll use to talk to Arduino. They're like three empty boxes.
async function connect() โ does all the opening work:
navigator.serial.requestPort() โ asks the browser to show the list of serial portsport.open({ baudRate: 115200 }) โ opens the chosen port at 115200 speed (must match Arduino)writer and reader โ prepares channels for writing and readingwhile (keepReading) โ stays listening forever, whenever a message arrives it shows it in the boxasync function disconnect() โ closes everything when you're done
async function sendData() โ takes the text you wrote and sends it to Arduino
๐ก Try modifying: change baudRate to 9600 if your Arduino uses that speed.
The browser sends bytes. Inside Arduino, these are the registers that activate:
| Register | Address | Bit | Name | What it does |
|---|---|---|---|---|
| UBRR0L | 0xC4 | 7-0 | UBRR[7:0] | baud rate low byte |
| UBRR0H | 0xC5 | 7-0 | UBRR[15:8] | baud rate high byte |
| UCSR0B | 0xC1 | 4 | RXEN0 | =1 to enable reception |
| UCSR0B | 0xC1 | 3 | TXEN0 | =1 to enable transmission |
| UCSR0C | 0xC2 | 2-1 | UCSZ0[1:0] | =11 for 8 data bits |
| UCSR0A | 0xC0 | 7 | RXC0 | =1 when a byte arrived |
| UCSR0A | 0xC0 | 5 | UDRE0 | =1 when transmission buffer empty |
| UDR0 | 0xC6 | 7-0 | UDR | received byte (or to transmit) |
| DDRB | 0x04 | 5 | DDB5 | =1 to set pin D13 as OUTPUT |
| PORTB | 0x05 | 5 | PORTB5 | =1 to turn LED on (5V), =0 to turn off (0V) |
Initial configuration (done once):
When the browser SENDS a byte:
When Arduino REPLIES (sends a byte to browser):
UDR0 (0xC6) is the mailbox.
UCSR0A bit7 (RXC0) is the "mail arrived" flag.
UCSR0A bit5 (UDRE0) is the "outgoing mailbox empty" flag.
PORTB bit5 (0x05.5) is the LED switch.
๐ฅ Complete program to upload to Arduino:
Receives bytes, turns LED on if '1', off if '0', and always echoes back the character.
With CostyCNC online assembler uploader: https://www.costycnc.it/avr1/compiler.html
Writing directly to registers on a tiny ATmega328 is NOT a useless nostalgic exercise. It's EXACTLY how the most complex systems on the planet work.
What you see here with UDR0 (0xC6) and PORTB (0x05) is the exact same logic used by:
NVIDIA RTX 5090, AMD Radeon: thousands of registers. The driver writes to addresses like 0x3C0 to set resolution, 0x3C4 for frequency. Exactly the same thing: an address, a value, and the hardware reacts.
๐ Your sbi 0x05,5 = NVIDIA driver writing to 0x3C0
Realtek, Sound Blaster: hundreds of registers for volume, EQ. Writing 0x50 to register 0x2A = 50% volume. Identical to your sts 0xC6, r17.
๐ Your sts 0xC6, r17 = audio driver writing to 0x2A
Bosch, Denso: thousands of registers via OBD2. Read 0x0C = RPM, 0x0D = speed. Like you reading UCSR0A.
๐ Your lds r16, 0xC0 = OBD2 scanner reading 0x0C
NVMe, USB, network cards: memory-mapped registers. The driver writes to 0xF0008000. Same philosophy as your out 0x04, r16.
๐ Your out 0x04, r16 = NVMe driver writing to 0xF0008000
Intel i9, Apple M4, STM32, ESP32: all have registers. Names change, principle is identical. You already know 90% of how to program any micro.
๐ sbi 0x05,5 = GPIOB->BSRR on STM32
Intel Z790, AMD B650: manage USB, SATA, PCIe via registers. BIOS writes to 0xCF8/0xCFC. Same logic.
๐ Your sts 0xC6, r17 = BIOS writing to 0xCFC
0x3C0
0x2A
0x0C
0xC6 (UDR0) ยท 0x05 (PORTB)
๐ข IT'S THE EXACT SAME THING!
When you learn to write sts 0xC6, r17 on Arduino, you're not learning something old and useless.
You're learning how the ENTIRE DIGITAL WORLD works.
โโโโโโโโโโโโโโโโโโโโโโโโโ
THERE IS NO MAGIC. ONLY NUMBERS IN REGISTERS.
โโโโโโโโโโโโโโโโโโโโโโโโโ
Arduino is not a toy. It's the gym where you learn the rules that govern ALL hardware.
Question: When a byte arrives from the browser, which bit rises and in which register? And to turn the LED on, which register and bit do you use?
Bit RXC0 (bit 7) in register UCSR0A (address 0xC0) indicates a byte arrived. To turn the LED on you use PORTB (0x05) bit 5 = 1.