Serial Communication and REPL - No More Serial.begin!

Learn how print() replaces Serial.print() and discover the power of MicroPython's REPL for interactive debugging

By Kevin McAleer,    9 Minutes


Serial Communication: Simpler in Python

Arduino requires you to initialize serial communication with Serial.begin(). MicroPython’s print() function just works - no setup required. And the REPL (Read-Eval-Print Loop) gives you an interactive Python console on your microcontroller!

This is one area where MicroPython is dramatically simpler and more powerful than Arduino.

Serial Output: print() vs Serial.println()

Arduino C++:

void setup() {
    Serial.begin(9600);  // Must initialize serial
}

void loop() {
    Serial.print("Temperature: ");
    Serial.println(temperature);
    delay(1000);
}

MicroPython:

import time

while True:
    print(f"Temperature: {temperature}")
    time.sleep(1)

Key differences:

  • No initialization needed - print() just works
  • No baud rate - USB serial is always ready
  • No println vs print - print() adds newline by default
  • F-strings make formatting easier

Arduino C++:

int value = 42;
float voltage = 3.3;
String message = "Hello";

Serial.print("Value: ");
Serial.println(value);

Serial.print("Voltage: ");
Serial.print(voltage, 2);  // 2 decimal places
Serial.println("V");

Serial.print(message);
Serial.print(" ");
Serial.println("World");

MicroPython:

value = 42
voltage = 3.3
message = "Hello"

print(f"Value: {value}")
print(f"Voltage: {voltage:.2f}V")  # 2 decimal places
print(f"{message} World")

# Or combine everything
print(f"Value: {value}, Voltage: {voltage:.2f}V, Message: {message} World")

Python’s f-strings are much cleaner!

Sometimes you want to print without a newline:

Arduino C++:

Serial.print("Loading");
for (int i = 0; i < 5; i++) {
    Serial.print(".");
    delay(1000);
}
Serial.println(" Done!");

MicroPython:

import time

print("Loading", end="")  # No newline
for i in range(5):
    print(".", end="")  # No newline
    time.sleep(1)
print(" Done!")  # With newline

Use the end="" parameter to suppress the newline.

Printing Multiple Values

Arduino C++:

int x = 10, y = 20, z = 30;
Serial.print("X: ");
Serial.print(x);
Serial.print(", Y: ");
Serial.print(y);
Serial.print(", Z: ");
Serial.println(z);

MicroPython:

x, y, z = 10, 20, 30

# Method 1: F-string (best)
print(f"X: {x}, Y: {y}, Z: {z}")

# Method 2: Multiple arguments
print("X:", x, ", Y:", y, ", Z:", z)

# Method 3: Old-style formatting
print("X: {}, Y: {}, Z: {}".format(x, y, z))

The REPL: Interactive Python

Here’s where MicroPython really shines. The REPL (Read-Eval-Print Loop) is an interactive Python prompt that runs on your microcontroller.

How to access the REPL:

  1. Connect your board via USB
  2. Open a serial terminal:
    • Thonny IDE (easiest) - Tools β†’ Options β†’ Interpreter
    • PuTTY (Windows) - Connect to COM port at 115200 baud
    • screen (Mac/Linux) - screen /dev/tty.usbmodem* 115200

You’ll see the Python prompt:

>>>

Now you can type Python commands and see instant results!

REPL Examples:

>>> print("Hello from MicroPython!")
Hello from MicroPython!

>>> 2 + 2
4

>>> from machine import Pin
>>> led = Pin(25, Pin.OUT)
>>> led.on()
>>> led.off()

>>> import time
>>> for i in range(5):
...     print(i)
...     time.sleep(0.5)
...
0
1
2
3
4

This is impossible in Arduino! You can’t interact with your code while it’s running. The REPL is perfect for:

  • Testing pin numbers
  • Debugging sensor readings
  • Trying out code snippets
  • Learning MicroPython

REPL Special Commands

The REPL has special commands:

Ctrl+C - Stop running program, return to REPL prompt Ctrl+D - Soft reboot (restarts MicroPython, runs boot.py and main.py) help() - Show help information help(object) - Show help for specific object

>>> help()
Welcome to MicroPython!
...

>>> from machine import Pin
>>> help(Pin)
# Shows Pin documentation

Debugging with Print Statements

Arduino C++:

void loop() {
    int sensorValue = analogRead(A0);

    Serial.print("DEBUG: Raw value = ");
    Serial.println(sensorValue);

    if (sensorValue > 500) {
        Serial.println("DEBUG: Value is high");
        digitalWrite(LED_PIN, HIGH);
    } else {
        Serial.println("DEBUG: Value is low");
        digitalWrite(LED_PIN, LOW);
    }

    delay(1000);
}

MicroPython:

from machine import Pin, ADC
import time

sensor = ADC(Pin(26))
led = Pin(25, Pin.OUT)

while True:
    sensor_value = sensor.read_u16()

    print(f"DEBUG: Raw value = {sensor_value}")

    if sensor_value > 32768:
        print("DEBUG: Value is high")
        led.on()
    else:
        print("DEBUG: Value is low")
        led.off()

    time.sleep(1)

Pro tip: Use descriptive debug messages:

print(f"[SENSOR] Raw: {sensor_value}, Voltage: {voltage:.2f}V")
print(f"[LED] State: {'ON' if led.value() else 'OFF'}")
print(f"[ERROR] Sensor reading out of range: {sensor_value}")

Formatted Output

Python has powerful string formatting:

Arduino C++:

float voltage = 3.287;
Serial.print("Voltage: ");
Serial.print(voltage, 2);  // 2 decimals: 3.29
Serial.println("V");

MicroPython:

voltage = 3.287

# F-string with formatting
print(f"Voltage: {voltage:.2f}V")  # 2 decimals: 3.29

# More formatting examples
print(f"Voltage: {voltage:.3f}V")   # 3 decimals: 3.287
print(f"Voltage: {voltage:.1f}V")   # 1 decimal: 3.3
print(f"Hex: 0x{42:02X}")           # Hex: 0x2A
print(f"Binary: {42:08b}")          # Binary: 00101010
print(f"Padded: {42:05d}")          # Padded: 00042

Format specification:

  • :.2f - Float with 2 decimal places
  • :05d - Integer padded to 5 digits
  • :08b - Binary with 8 digits
  • :02X - Hex with 2 digits (uppercase)

Reading Serial Input

In Arduino, you read serial input with Serial.read(). In MicroPython, it’s trickier because the REPL uses the serial port. You can use sys.stdin.read() but it’s not commonly used in MicroPython projects.

Arduino C++:

void loop() {
    if (Serial.available() > 0) {
        char incoming = Serial.read();
        Serial.print("Received: ");
        Serial.println(incoming);
    }
}

MicroPython (less common):

import sys
import select

poll = select.poll()
poll.register(sys.stdin, select.POLLIN)

while True:
    events = poll.poll(0)  # Non-blocking
    if events:
        char = sys.stdin.read(1)
        print(f"Received: {char}")

Note: For most MicroPython projects, you don’t read from serial. Instead, you:

  1. Use the REPL interactively
  2. Control your program through WiFi/network
  3. Use buttons or sensors for input

Logging Sensor Data

Arduino C++:

void loop() {
    int temp = readTemperature();
    int humidity = readHumidity();

    Serial.print(millis());
    Serial.print(",");
    Serial.print(temp);
    Serial.print(",");
    Serial.println(humidity);

    delay(1000);
}

MicroPython:

import time

def read_temperature():
    # Your sensor code
    return 25

def read_humidity():
    # Your sensor code
    return 65

while True:
    temp = read_temperature()
    humidity = read_humidity()
    timestamp = time.ticks_ms()

    # CSV format
    print(f"{timestamp},{temp},{humidity}")

    time.sleep(1)

This CSV output can be copied from the terminal and imported into Excel or Python for analysis.

Conditional Debug Messages

Use a debug flag to toggle verbose output:

MicroPython:

from machine import Pin
import time

DEBUG = True  # Set to False to disable debug output

led = Pin(25, Pin.OUT)

def debug(message):
    if DEBUG:
        print(f"[DEBUG] {message}")

while True:
    debug("Turning LED on")
    led.on()
    time.sleep(1)

    debug("Turning LED off")
    led.off()
    time.sleep(1)

Pretty Printing Data

For complex data, use formatting to make it readable:

MicroPython:

from machine import Pin, ADC
import time

sensor1 = ADC(Pin(26))
sensor2 = ADC(Pin(27))

while True:
    s1 = sensor1.read_u16()
    s2 = sensor2.read_u16()
    v1 = (s1 / 65535) * 3.3
    v2 = (s2 / 65535) * 3.3

    # Pretty formatted table
    print("=" * 40)
    print(f"{'Sensor':<10} {'Raw':>10} {'Voltage':>10}")
    print("-" * 40)
    print(f"{'Sensor 1':<10} {s1:>10} {v1:>9.2f}V")
    print(f"{'Sensor 2':<10} {s2:>10} {v2:>9.2f}V")
    print("=" * 40)

    time.sleep(2)

Output:

========================================
Sensor         Raw    Voltage
----------------------------------------
Sensor 1     32768      1.65V
Sensor 2     45678      2.30V
========================================

Complete Example: Sensor Monitor

MicroPython:

from machine import Pin, ADC
import time

# Hardware setup
temp_sensor = ADC(Pin(26))
light_sensor = ADC(Pin(27))
led = Pin(25, Pin.OUT)

# Configuration
TEMP_THRESHOLD = 30000
LIGHT_THRESHOLD = 20000
DEBUG = True

def debug(message):
    if DEBUG:
        print(f"[DEBUG] {message}")

def read_sensors():
    temp_raw = temp_sensor.read_u16()
    light_raw = light_sensor.read_u16()

    temp_voltage = (temp_raw / 65535) * 3.3
    light_voltage = (light_raw / 65535) * 3.3

    return temp_raw, light_raw, temp_voltage, light_voltage

def check_alerts(temp_raw, light_raw):
    alert = False

    if temp_raw > TEMP_THRESHOLD:
        print("[ALERT] Temperature too high!")
        alert = True

    if light_raw < LIGHT_THRESHOLD:
        print("[ALERT] Light level too low!")
        alert = True

    return alert

while True:
    debug("Reading sensors...")
    temp_raw, light_raw, temp_v, light_v = read_sensors()

    debug(f"Temperature: {temp_raw} ({temp_v:.2f}V)")
    debug(f"Light: {light_raw} ({light_v:.2f}V)")

    alert = check_alerts(temp_raw, light_raw)

    if alert:
        led.on()
    else:
        led.off()

    # Summary output
    print(f"T:{temp_v:.2f}V | L:{light_v:.2f}V | LED:{'ON' if led.value() else 'OFF'}")

    time.sleep(1)

Try It Yourself

Exercise 1: Interactive LED Control via REPL

Connect to the REPL and control an LED interactively:

  1. Import the Pin class
  2. Create an LED on pin 25
  3. Turn it on, wait, turn it off
  4. Try different timing patterns

No code to write - just interact with the REPL!

Answer:

# In the REPL:
>>> from machine import Pin
>>> import time
>>> led = Pin(25, Pin.OUT)
>>> led.on()
>>> time.sleep(2)
>>> led.off()
>>> led.toggle()
>>> for i in range(5):
...     led.toggle()
...     time.sleep(0.5)
...

Exercise 2: Data Logger

Create a program that logs sensor data in CSV format:

  • Read an ADC sensor every second
  • Print: timestamp, raw value, voltage
  • Format as CSV (comma-separated)
  • Include a header row

Answer:

from machine import Pin, ADC
import time

sensor = ADC(Pin(26))

# Print CSV header
print("Timestamp(ms),Raw,Voltage(V)")

start_time = time.ticks_ms()

while True:
    timestamp = time.ticks_diff(time.ticks_ms(), start_time)
    raw = sensor.read_u16()
    voltage = (raw / 65535) * 3.3

    # CSV format
    print(f"{timestamp},{raw},{voltage:.3f}")

    time.sleep(1)

Exercise 3: Formatted Sensor Dashboard

Create a continuously updating dashboard that displays:

  • Current time (seconds since boot)
  • Three sensor readings with labels
  • Status indicators (OK/WARNING/ALERT based on values)
  • Use clear formatting with borders

Make it look professional!

Answer:

from machine import Pin, ADC
import time

sensor1 = ADC(Pin(26))
sensor2 = ADC(Pin(27))
sensor3 = ADC(Pin(28))

def get_status(value, low, high):
    if value < low:
        return "ALERT  "
    elif value > high:
        return "WARNING"
    else:
        return "OK     "

start_time = time.ticks_ms()

while True:
    uptime = time.ticks_diff(time.ticks_ms(), start_time) / 1000

    s1 = sensor1.read_u16()
    s2 = sensor2.read_u16()
    s3 = sensor3.read_u16()

    # Clear screen (terminal escape code)
    print("\033[2J\033[H", end="")

    # Dashboard
    print("=" * 50)
    print(f"    SENSOR DASHBOARD - Uptime: {uptime:.1f}s")
    print("=" * 50)
    print(f"{'Sensor':<12} {'Value':>10} {'Status':>10}")
    print("-" * 50)
    print(f"{'Temperature':<12} {s1:>10} {get_status(s1, 20000, 40000):>10}")
    print(f"{'Light':<12} {s2:>10} {get_status(s2, 15000, 50000):>10}")
    print(f"{'Pressure':<12} {s3:>10} {get_status(s3, 25000, 45000):>10}")
    print("=" * 50)

    time.sleep(1)

< Previous Next >

You can use the arrows  ← β†’ on your keyboard to navigate between lessons.