Skip to content

Hardware Troubleshooting Guide

Systematic approach to diagnosing and fixing common hardware issues with the PicoCar robot.


The Troubleshooting Mindset

┌─────────────────────────────────────────────────────────────────┐
│  TROUBLESHOOTING STEPS                                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  1. OBSERVE    → What exactly is happening?                      │
│       │                                                          │
│       ▼                                                          │
│  2. ISOLATE    → Is it hardware, software, or connection?        │
│       │                                                          │
│       ▼                                                          │
│  3. SIMPLIFY   → Test with minimal code                          │
│       │                                                          │
│       ▼                                                          │
│  4. MEASURE    → Use print, LED, or multimeter                   │
│       │                                                          │
│       ▼                                                          │
│  5. FIX        → Change ONE thing at a time                      │
│       │                                                          │
│       ▼                                                          │
│  6. VERIFY     → Confirm fix, test edge cases                    │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Quick Checks (Do These First!)

Before deep debugging, check these common issues:

  • [ ] Power: Is the battery charged? (Check LED or measure voltage)
  • [ ] USB: Is the cable data-capable? (Some are charge-only)
  • [ ] Connection: Is Thonny/IDE connected to the right port?
  • [ ] Code uploaded: Is the latest code on the Pico? (Not just saved on PC)
  • [ ] BOOTSEL: Did you accidentally boot into UF2 mode?

Pico Won't Connect

Symptom: Thonny says "Could not find device"

Check 1: USB Cable

Many USB cables are charge-only (no data wires).
Test: Does the cable work with another device that needs data?
Fix: Use a known-good data cable

Check 2: Correct Port

Windows: Device Manager → Ports (COM & LPT) → Look for "USB Serial Device"
Linux: ls /dev/ttyACM* or /dev/ttyUSB*
Mac: ls /dev/tty.usbmodem*

Check 3: BOOTSEL Mode

If Pico shows as a USB drive (RPI-RP2), it's in bootloader mode.
Fix: Unplug, replug WITHOUT holding BOOTSEL

Check 4: Crashed Firmware

If infinite loop or crash prevents connection:
1. Hold BOOTSEL
2. Plug in USB
3. Release BOOTSEL
4. Drag MicroPython .uf2 onto the RPI-RP2 drive


Symptom: "Device is busy" error

Another program (or Thonny instance) has the port open.
Fix: Close other terminals, IDEs, or serial monitors

LED Problems

Symptom: LED doesn't light up

Flowchart:

LED doesn't light
Is it the onboard LED?
   ┌───┴───┐
   │ Yes   │ No (external)
   │       │
   ▼       ▼
Test code:              Check wiring:
led = Pin("LED", OUT)   - Correct GPIO pin?
led.on()                - LED polarity correct?
                        - Resistor present? (220-1kΩ)
                        - GND connected?

Test code for onboard LED:

from machine import Pin
led = Pin("LED", Pin.OUT)  # Onboard LED on Pico/Pico 2
led.on()                   # Should light up


Symptom: LED flickers or dims unexpectedly

Possible causes:
1. PWM frequency too low (< 100Hz visible flicker)
2. Voltage drop from other components (motors)
3. Loose connection

Test: Disconnect motors, test LED alone

Button Problems

Symptom: Button always reads HIGH or LOW

Check 1: Pull-up/Pull-down Configuration

# WRONG - floating input gives undefined values
# On Pico 2 (RP2350), leakage current means it may read a stuck 0 or 1
# instead of random values — which is worse (looks correct but isn't)
button = Pin(14, Pin.IN)  # No pull specified!

# RIGHT - use internal pull-up
button = Pin(14, Pin.IN, Pin.PULL_UP)
# Button pressed = 0 (connects to GND)
# Button released = 1 (pulled to VCC)

Check 2: Wiring

With PULL_UP:
  Button should connect GPIO to GND (not VCC!)

         GPIO14 ─────┤ ├──── GND
                  (button)


Symptom: Button triggers multiple times per press

This is switch bounce (normal!).
Fix: Add debouncing in software

debounce_ms = 50
last_press = 0

def button_handler():
    global last_press
    now = time.ticks_ms()
    if time.ticks_diff(now, last_press) > debounce_ms:
        # Real press
        last_press = now
        return True
    return False

Motor Problems

Symptom: Motors don't move at all

Flowchart:

Motors don't move
Is battery connected and charged?
   ┌───┴───┐
   │ No    │ Yes
   │       │
   ▼       ▼
Charge    Test motor directly:
battery   - Disconnect from Pico
          - Apply 3-6V directly to motor
          - Does it spin?
            ┌────┴────┐
            │ No      │ Yes
            │         │
            ▼         ▼
         Motor      Check H-bridge
         dead       and connections

Test code:

from pico_car import pico_car
Motor = pico_car()

# Test at maximum speed
Motor.Car_Run(255, 255)
time.sleep(2)
Motor.Car_Stop()


Symptom: Motors move but not at low PWM

This is the dead zone - normal for DC motors.
The motor needs enough power to overcome friction.

Typical dead zone: 40-80 PWM (out of 255)

Measure yours:
for pwm in range(0, 100, 5):
    print(f"Testing PWM {pwm}")
    Motor.Car_Run(pwm, pwm)
    time.sleep(1)
    # Note when it starts moving
Motor.Car_Stop()

Symptom: Robot curves when it should go straight

This is motor mismatch - normal for cheap motors!

1. Measure: Run both motors at same PWM, observe which is faster
2. Calculate: balance = slow_motor_pwm / fast_motor_pwm
3. Apply: Multiply faster motor by balance factor

Example:
  Left at 100 PWM = 15 cm/s
  Right at 100 PWM = 14 cm/s
  Balance = 14/15 = 0.93
  Use: Motor.Car_Run(int(100*0.93), 100)

Symptom: Motors only spin one direction

H-bridge issue. Check:
1. Direction control pins (usually 2 pins per motor)
2. PWM pin is connected
3. H-bridge power supply connected

Test each direction separately:
Motor.Car_Run(100, 100)   # Forward
time.sleep(1)
Motor.Car_Run(-100, -100) # Backward (if library supports)
# Or control direction pins manually

Sensor Problems

Symptom: Ultrasonic sensor gives wrong distance

Check 1: Timing

# Ultrasonic needs precise timing
# Minimum: 60ms between readings
# If reading too fast: garbage values

time.sleep_ms(60)  # Wait between readings

Check 2: Surface

Ultrasonic fails on:
- Soft/fuzzy surfaces (absorb sound)
- Angled surfaces (reflect away)
- Very close objects (<2cm)
- Very far objects (>400cm)

Check 3: Wiring

TRIG: GPIO output (your pulse out)
ECHO: GPIO input (their pulse back)
VCC: 5V (most modules, check yours)
GND: Common ground with Pico


Symptom: Line sensors give inconsistent readings

Check 1: Height from Surface

Optimal height: 5-15mm from surface
Too close: saturated readings
Too far: weak signal

Check 2: Ambient Light

Sunlight contains IR, interferes with readings.
Test: Cover sensor, readings should change significantly.

Check 3: Calibration

# Calibrate on YOUR surface
print("WHITE surface:")
white = read_line_sensors()
print(f"  Values: {white}")

print("BLACK line:")
black = read_line_sensors()
print(f"  Values: {black}")

threshold = (white + black) // 2
print(f"Use threshold: {threshold}")


Symptom: ADC readings are noisy

Normal! ADC always has some noise.

Solutions:
1. Average multiple readings
2. Add capacitor (0.1µF) between signal and GND
3. Use shorter wires
4. Keep away from motor wires (EMI)

def read_adc_clean(adc, samples=16):
    readings = [adc.read_u16() for _ in range(samples)]
    readings.sort()
    # Remove outliers, average middle values
    return sum(readings[2:-2]) // (samples - 4)

I2C Problems (OLED, IMU)

Symptom: Device not found

Check 1: Scan the Bus

from machine import I2C, Pin

i2c = I2C(0, scl=Pin(17), sda=Pin(16), freq=100000)
devices = i2c.scan()
print(f"Found devices: {[hex(d) for d in devices]}")

# Expected:
# OLED SSD1306: 0x3C or 0x3D
# IMU BMI160: 0x68 or 0x69

Check 2: Wiring

I2C needs 4 connections:
  SDA (data)   - GPIO16 (default I2C0)
  SCL (clock)  - GPIO17 (default I2C0)
  VCC          - 3.3V (check device, some need 5V)
  GND          - Ground

Pull-ups: Most modules have built-in. If not, add 4.7kΩ to VCC.

Check 3: Speed

# Start slow, increase if working
i2c = I2C(0, scl=Pin(17), sda=Pin(16), freq=100000)  # 100kHz (safe)
# Then try:
i2c = I2C(0, scl=Pin(17), sda=Pin(16), freq=400000)  # 400kHz (standard)


Symptom: I2C works but corrupted data

Possible causes:
1. Wires too long (>30cm = problems)
2. Missing ground connection
3. Bus speed too high
4. Interference from motors (EMI)

Fix: Shorter wires, add 0.1µF capacitor on VCC, lower frequency

Power Problems

Symptom: System resets randomly

Usually power-related:

1. Battery voltage dropping (especially when motors start)
2. USB power insufficient for motors
3. Ground loop issues

Test: Print battery voltage continuously while running motors
      Watch for drops below 5V (or 3V for 3.3V rail)

Symptom: Motors start but system crashes

Motors draw high current when starting (stall current).
This can cause voltage dip that resets the Pico.

Solutions:
1. Use fresh/charged battery
2. Add large capacitor (100-1000µF) across motor power
3. Don't start motors at 100% - ramp up gradually

def smooth_start(target_pwm, ramp_time_ms=500):
    steps = 20
    for i in range(steps + 1):
        pwm = target_pwm * i // steps
        Motor.Car_Run(pwm, pwm)
        time.sleep_ms(ramp_time_ms // steps)

Wireless Problems (Pico 2 W)

Symptom: WiFi won't connect

Check 1: Credentials

# Common mistakes:
SSID = "MyNetwork "     # Trailing space!
SSID = "MyNetwork"      # Correct

PASSWORD = "pass word"  # Watch for spaces

Check 2: 2.4GHz Only

Pico W only supports 2.4GHz WiFi, not 5GHz.
Check your router settings.

Check 3: Connection Code

import network
import time

wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(SSID, PASSWORD)

# Wait for connection
max_wait = 10
while max_wait > 0:
    if wlan.status() == 3:  # Connected
        break
    print(f"Waiting... status={wlan.status()}")
    time.sleep(1)
    max_wait -= 1

if wlan.isconnected():
    print(f"Connected! IP: {wlan.ifconfig()[0]}")
else:
    print(f"Failed! Status: {wlan.status()}")
    # Status codes:
    # 0 = Link down
    # 1 = Joining
    # 2 = No IP
    # 3 = Connected
    # -1 = Failed
    # -2 = No network
    # -3 = Bad password


Emergency Recovery

Pico completely unresponsive

1. Unplug everything
2. Hold BOOTSEL button
3. Plug in USB while holding BOOTSEL
4. Release BOOTSEL
5. Pico appears as USB drive "RPI-RP2"
6. Download latest MicroPython .uf2 from micropython.org
7. Drag .uf2 file onto RPI-RP2 drive
8. Pico reboots with fresh MicroPython

Code runs but can't stop it (infinite loop)

Method 1: Thonny
  - Click red STOP button
  - Press Ctrl+C multiple times

Method 2: Hard reset
  - Unplug USB
  - Delete main.py via BOOTSEL mode if needed

Method 3: Safe mode
  - Connect while holding BOOTSEL
  - Pico won't auto-run main.py
  - Delete or rename main.py

See Also

  • [[Reference/extras/common-mistakes|Common Mistakes]]
  • [[Reference/extras/hardware/picocar-pinout|PicoCar Pinout]]
  • [[Reference/extras/glossary|Glossary]]