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
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
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
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]]