Hello world over serial
The first program that proves the board is running your code and can talk back. Two lines of setup, one line of output, and the two settings that decide whether you see any of it.
What the two lines do
Serial.begin(115200) starts the UART that runs over the same USB cable that
powers the board. Serial.println sends characters down it. That is the whole
mechanism, and for most projects it stays the only debugger you use.
The two numbers have to match
Serial.begin(115200) plus a delay — and it is the only debugger most projects ever need.Drag the delay down and you will see the second failure, which is subtler than
garbled text: the output is perfectly correct and completely unreadable, and
Serial.print is now the slowest thing in your loop. At 115200 baud each
character costs about 87 µs, so a forty-character line printed every pass of a
tight loop is milliseconds of work the loop was supposed to be doing something
else with.
Print things worth printing
millis() and ESP.getFreeHeap() cost nothing and answer the two questions
you will actually have later: how long has it been up, and is something leaking.
Add them to the loop of every project now and you will not have to add them at
two in the morning.
A note on the first 200 ms
On boards with native USB — S3, C3, C6, P4 — the port is created by the chip
itself, and it does not exist until a moment after boot. Anything printed in
that window goes nowhere. The short delay after Serial.begin is not
superstition; it is waiting for the host to enumerate a device that has only
just appeared.
The code
Nine lines, no wiring. Upload it, open the Serial Monitor, set the dropdown to 115200, and a line appears every second.
void setup() {
Serial.begin(115200);
delay(200); // let the USB serial port come up
Serial.println("Hello, World!");
}
void loop() {
Serial.printf("up %lu ms, heap %u\n", millis(), ESP.getFreeHeap());
delay(1000);
}The number in Serial.begin and the number in the Serial Monitor dropdown are two separate settings and neither knows about the other. Getting them to agree is the whole trick.
In MicroPython the REPL is already a serial connection, so print goes to your terminal with nothing to configure at all.
import time
print("Hello, World!")
while True:
print("up", time.ticks_ms(), "ms")
time.sleep(1)Saved as main.py on the board it runs at every power-up. Under any other name it is a file sitting in flash doing nothing.
When it does not work
Either the wrong port or a board that has not been reset. Silence with a correct port usually means the sketch printed its greeting before you opened the monitor — opening the Serial Monitor resets most boards, so press EN and watch the first line arrive.
The two baud rates disagree. Readable-but-wrong characters are the signature of a mismatch, not of a broken board. Set both to 115200 and it clears immediately.
That is the ROM bootloader talking at 115200 in a fixed format before your sketch starts. It is normal. If it repeats every few seconds, it is a boot loop and the text after "rst:" names the cause.
Serial.printf exists on ESP32 and not on an Uno, so old sketches use Serial.print with string concatenation. Both work here; mixing the two styles in one line is what produces this.
The other traditional first program, and the one that needs a resistor. Two components, one piece of arithmetic, and a GPIO that survives it.
Blink an LED →Edit this page — content/esp32/hello-world-over-serial.mdx
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.