학습 목표
이 자료는 라즈베리 파이 피코 WH로 장애물 회피 자동차를 만들기 위한 전체 과정을 다룹니다. 처음에는 보드와 센서를 따로 테스트하고, 이후 모터 제어 함수를 만들며, 마지막에는 앞쪽 장애물을 감지하고 좌우 거리를 비교해 더 넓은 방향으로 이동하는 자동차를 완성합니다.
필요 부품
| 부품 | 수량 | 역할 | 비고 |
|---|---|---|---|
| Raspberry Pi Pico WH | 1개 | 자동차의 두뇌 | Wi-Fi 가능, 헤더 핀 납땜형 |
| HC-SR04 초음파 센서 | 1개 | 앞쪽 장애물 거리 측정 | ECHO 전압 주의 |
| L298N 모터 드라이버 | 1개 | DC 모터 제어 | TB6612 사용 시 핀 이름만 바꿔 응용 가능 |
| DC 기어드 모터 | 2개 | 왼쪽·오른쪽 바퀴 구동 | 자동차 키트 모터 |
| 서보모터 SG90 | 1개 | 초음파 센서 좌우 회전 | 외부 5V 권장 |
| 배터리 팩 | 1개 | 모터 전원 | 모터 사양에 맞게 6~12V |
| 전원 스위치 | 1개 | 전체 전원 차단 | 안전용 |
| 점퍼선 | 여러 개 | 부품 연결 | 수-수, 암-수 혼합 |
| 저항 1kΩ, 2kΩ | 각 1개 | ECHO 전압 분배 | 5V → 약 3.3V 낮춤 |
전체 배선 요약
| 장치 | 장치 핀 | Pico WH 핀 | 설명 |
|---|---|---|---|
| HC-SR04 | TRIG | GP2 | 초음파 발사 신호 |
| HC-SR04 | ECHO | GP3 | 초음파 반사 입력, 전압 분배 권장 |
| L298N | IN1 | GP10 | 왼쪽 모터 방향 1 |
| L298N | IN2 | GP11 | 왼쪽 모터 방향 2 |
| L298N | IN3 | GP12 | 오른쪽 모터 방향 1 |
| L298N | IN4 | GP13 | 오른쪽 모터 방향 2 |
| L298N | ENA | GP14 | 왼쪽 모터 속도 PWM |
| L298N | ENB | GP15 | 오른쪽 모터 속도 PWM |
| SG90 서보 | Signal | GP16 | 초음파 센서 좌우 회전 |
| 공통 | GND | GND | Pico, 센서, 드라이버, 배터리 GND 공통 |
중요 주의사항
- Pico WH의 GPIO 입력은 3.3V 기준입니다. HC-SR04 ECHO가 5V로 출력되면 전압 분배 회로를 사용하세요.
- 모터는 전류를 많이 사용하므로 Pico WH의 3.3V 핀에서 직접 모터를 구동하지 마세요.
- 배터리 GND, L298N GND, Pico WH GND는 반드시 공통으로 연결해야 신호가 정상 동작합니다.
- 처음 테스트할 때는 자동차 바퀴를 공중에 띄우고 실행하면 충돌을 막을 수 있습니다.
- USB 연결 상태에서 모터 전원을 함께 사용할 때 배선이 잘못되면 PC나 보드가 손상될 수 있으므로 전원 연결을 다시 확인하세요.
제작 순서
① Pico WH LED 테스트 → ② 초음파 센서 테스트 → ③ 모터 단독 테스트 → ④ 전진·후진·회전 함수 작성 → ⑤ PWM 속도 제어 → ⑥ 장애물 정지 → ⑦ 후진 후 회전 → ⑧ 서보 좌우 탐색 → ⑨ 좌우 비교 회피 완성 → ⑩ Wi-Fi 원격 출발·정지
1단계
부품 준비와 Pico WH 내장 LED 테스트
목적
Pico WH 보드와 MicroPython 실행 환경이 정상인지 확인합니다. Pico WH는 보드 위에 LED가 있어 별도 LED 없이 테스트할 수 있습니다.
Pico WH 보드와 MicroPython 실행 환경이 정상인지 확인합니다. Pico WH는 보드 위에 LED가 있어 별도 LED 없이 테스트할 수 있습니다.
배선
Pico WH만 USB로 PC에 연결
Pico WH만 USB로 PC에 연결
from machine import Pin
from time import sleep
led = Pin("LED", Pin.OUT)
while True:
led.on()
print("LED 켜짐")
sleep(0.5)
led.off()
print("LED 꺼짐")
sleep(0.5)
확인 포인트
- Thonny 오른쪽 아래 인터프리터가 MicroPython(Raspberry Pi Pico)인지 확인합니다.
- Pico WH 내장 LED가 0.5초 간격으로 깜빡이면 보드와 업로드 환경이 정상입니다.
- Pico 일반 버전은 Pin(25, Pin.OUT)을 사용할 수 있지만 Pico WH는 Pin('LED')가 편리합니다.
2단계
초음파 센서 HC-SR04 거리 측정
목적
자동차의 눈 역할을 하는 초음파 센서가 앞쪽 장애물까지의 거리를 제대로 읽는지 확인합니다.
자동차의 눈 역할을 하는 초음파 센서가 앞쪽 장애물까지의 거리를 제대로 읽는지 확인합니다.
배선
HC-SR04 VCC→5V, GND→GND, TRIG→GP2, ECHO→GP3
HC-SR04 VCC→5V, GND→GND, TRIG→GP2, ECHO→GP3
from machine import Pin, time_pulse_us
from time import sleep_us, sleep
trig = Pin(2, Pin.OUT)
echo = Pin(3, Pin.IN)
def get_distance_cm():
trig.low()
sleep_us(2)
trig.high()
sleep_us(10)
trig.low()
duration = time_pulse_us(echo, 1, 30000)
if duration < 0:
return 999
distance = duration * 0.0343 / 2
return distance
while True:
distance = get_distance_cm()
print("거리:", round(distance, 1), "cm")
sleep(0.3)
확인 포인트
- 손이나 책을 센서 앞에 가까이 가져가면 거리값이 작아져야 합니다.
- 값이 계속 999이면 ECHO, TRIG 배선을 다시 확인합니다.
- HC-SR04 ECHO가 5V로 나오는 모듈은 저항 분배로 3.3V 이하로 낮추는 것이 안전합니다.
3단계
모터 드라이버 L298N 기본 배선 테스트
목적
왼쪽 모터와 오른쪽 모터가 각각 정방향·역방향으로 움직이는지 확인합니다.
왼쪽 모터와 오른쪽 모터가 각각 정방향·역방향으로 움직이는지 확인합니다.
배선
L298N IN1→GP10, IN2→GP11, IN3→GP12, IN4→GP13, GND 공통
L298N IN1→GP10, IN2→GP11, IN3→GP12, IN4→GP13, GND 공통
from machine import Pin
from time import sleep
in1 = Pin(10, Pin.OUT)
in2 = Pin(11, Pin.OUT)
in3 = Pin(12, Pin.OUT)
in4 = Pin(13, Pin.OUT)
def left_forward():
in1.high()
in2.low()
def left_backward():
in1.low()
in2.high()
def right_forward():
in3.high()
in4.low()
def right_backward():
in3.low()
in4.high()
def stop_all():
in1.low()
in2.low()
in3.low()
in4.low()
while True:
print("왼쪽 모터 전진")
left_forward()
sleep(1)
stop_all()
sleep(0.5)
print("왼쪽 모터 후진")
left_backward()
sleep(1)
stop_all()
sleep(0.5)
print("오른쪽 모터 전진")
right_forward()
sleep(1)
stop_all()
sleep(0.5)
print("오른쪽 모터 후진")
right_backward()
sleep(1)
stop_all()
sleep(1)
확인 포인트
- 한쪽 모터가 반대로 돌면 모터 단자 두 선을 서로 바꾸거나 코드의 high/low를 바꿉니다.
- 모터 전원은 Pico WH의 3.3V 핀이 아니라 별도 배터리와 모터 드라이버로 공급합니다.
- 배터리 GND, L298N GND, Pico WH GND는 반드시 공통 연결합니다.
4단계
자동차 기본 동작 함수 만들기
목적
전진, 후진, 좌회전, 우회전, 정지를 함수로 정리해 이후 코드에서 쉽게 재사용합니다.
전진, 후진, 좌회전, 우회전, 정지를 함수로 정리해 이후 코드에서 쉽게 재사용합니다.
배선
3단계와 같은 모터 드라이버 배선
3단계와 같은 모터 드라이버 배선
from machine import Pin
from time import sleep
in1 = Pin(10, Pin.OUT)
in2 = Pin(11, Pin.OUT)
in3 = Pin(12, Pin.OUT)
in4 = Pin(13, Pin.OUT)
def stop():
in1.low()
in2.low()
in3.low()
in4.low()
def forward():
in1.high()
in2.low()
in3.high()
in4.low()
def backward():
in1.low()
in2.high()
in3.low()
in4.high()
def turn_left():
in1.low()
in2.high()
in3.high()
in4.low()
def turn_right():
in1.high()
in2.low()
in3.low()
in4.high()
while True:
forward()
print("전진")
sleep(1.5)
stop()
sleep(0.5)
backward()
print("후진")
sleep(1)
stop()
sleep(0.5)
turn_left()
print("좌회전")
sleep(0.8)
turn_right()
print("우회전")
sleep(0.8)
stop()
sleep(1)
확인 포인트
- 자동차가 전진 명령에서 뒤로 가면 양쪽 모터 방향을 함께 바꿔야 합니다.
- 좌회전·우회전 시간이 길수록 회전 각도가 커집니다.
- 바퀴, 바닥 마찰, 배터리 전압에 따라 sleep 시간을 조절합니다.
5단계
PWM으로 모터 속도 제어하기
목적
ENA, ENB 핀을 사용해 양쪽 모터 속도를 조절합니다. 장애물 회피 자동차가 너무 빠르면 센서 판단 전에 충돌할 수 있습니다.
ENA, ENB 핀을 사용해 양쪽 모터 속도를 조절합니다. 장애물 회피 자동차가 너무 빠르면 센서 판단 전에 충돌할 수 있습니다.
배선
IN1→GP10, IN2→GP11, IN3→GP12, IN4→GP13, ENA→GP14, ENB→GP15
IN1→GP10, IN2→GP11, IN3→GP12, IN4→GP13, ENA→GP14, ENB→GP15
from machine import Pin, PWM
from time import sleep
in1 = Pin(10, Pin.OUT)
in2 = Pin(11, Pin.OUT)
in3 = Pin(12, Pin.OUT)
in4 = Pin(13, Pin.OUT)
ena = PWM(Pin(14))
enb = PWM(Pin(15))
ena.freq(1000)
enb.freq(1000)
def set_speed(left, right):
left = max(0, min(65535, left))
right = max(0, min(65535, right))
ena.duty_u16(left)
enb.duty_u16(right)
def forward(speed=35000):
set_speed(speed, speed)
in1.high()
in2.low()
in3.high()
in4.low()
def stop():
set_speed(0, 0)
in1.low()
in2.low()
in3.low()
in4.low()
while True:
print("느리게 전진")
forward(25000)
sleep(2)
print("빠르게 전진")
forward(50000)
sleep(2)
stop()
sleep(1)
확인 포인트
- L298N의 ENA/ENB 점퍼가 꽂혀 있으면 PWM 제어가 안 될 수 있으므로 점퍼를 제거하고 Pico WH에 연결합니다.
- 속도값은 0~65535 범위입니다.
- 양쪽 모터 속도가 다르면 left, right 값을 다르게 보정합니다.
6단계
장애물이 가까우면 정지하기
목적
센서와 모터를 결합하는 첫 번째 단계입니다. 앞에 장애물이 가까우면 자동차가 멈춥니다.
센서와 모터를 결합하는 첫 번째 단계입니다. 앞에 장애물이 가까우면 자동차가 멈춥니다.
배선
초음파 TRIG→GP2, ECHO→GP3 / 모터는 5단계 배선
초음파 TRIG→GP2, ECHO→GP3 / 모터는 5단계 배선
from machine import Pin, PWM, time_pulse_us
from time import sleep_us, sleep
trig = Pin(2, Pin.OUT)
echo = Pin(3, Pin.IN)
in1 = Pin(10, Pin.OUT)
in2 = Pin(11, Pin.OUT)
in3 = Pin(12, Pin.OUT)
in4 = Pin(13, Pin.OUT)
ena = PWM(Pin(14))
enb = PWM(Pin(15))
ena.freq(1000)
enb.freq(1000)
def get_distance_cm():
trig.low()
sleep_us(2)
trig.high()
sleep_us(10)
trig.low()
duration = time_pulse_us(echo, 1, 30000)
if duration < 0:
return 999
return duration * 0.0343 / 2
def set_speed(left, right):
ena.duty_u16(left)
enb.duty_u16(right)
def forward(speed=35000):
set_speed(speed, speed)
in1.high()
in2.low()
in3.high()
in4.low()
def stop():
set_speed(0, 0)
in1.low()
in2.low()
in3.low()
in4.low()
while True:
distance = get_distance_cm()
print("앞 거리:", round(distance, 1), "cm")
if distance < 18:
stop()
print("장애물 감지 → 정지")
else:
forward(33000)
print("안전 → 전진")
sleep(0.1)
확인 포인트
- 장애물 기준 거리 18cm는 교실 환경에 맞게 15~30cm 사이에서 조절합니다.
- 차가 너무 빠르면 기준 거리를 더 크게 잡습니다.
- 정지 후 계속 떨리면 센서값 평균을 내거나 sleep 시간을 늘립니다.
7단계
장애물이 가까우면 후진 후 회전하기
목적
가까운 장애물을 만나면 멈추고, 뒤로 물러난 뒤 오른쪽으로 회전하여 회피합니다.
가까운 장애물을 만나면 멈추고, 뒤로 물러난 뒤 오른쪽으로 회전하여 회피합니다.
배선
초음파와 PWM 모터 배선은 6단계와 같습니다.
초음파와 PWM 모터 배선은 6단계와 같습니다.
from machine import Pin, PWM, time_pulse_us
from time import sleep_us, sleep
trig = Pin(2, Pin.OUT)
echo = Pin(3, Pin.IN)
in1 = Pin(10, Pin.OUT)
in2 = Pin(11, Pin.OUT)
in3 = Pin(12, Pin.OUT)
in4 = Pin(13, Pin.OUT)
ena = PWM(Pin(14))
enb = PWM(Pin(15))
ena.freq(1000)
enb.freq(1000)
def get_distance_cm():
trig.low()
sleep_us(2)
trig.high()
sleep_us(10)
trig.low()
duration = time_pulse_us(echo, 1, 30000)
if duration < 0:
return 999
return duration * 0.0343 / 2
def set_speed(left, right):
ena.duty_u16(left)
enb.duty_u16(right)
def stop():
set_speed(0, 0)
in1.low()
in2.low()
in3.low()
in4.low()
def forward(speed=33000):
set_speed(speed, speed)
in1.high()
in2.low()
in3.high()
in4.low()
def backward(speed=32000):
set_speed(speed, speed)
in1.low()
in2.high()
in3.low()
in4.high()
def turn_right(speed=35000):
set_speed(speed, speed)
in1.high()
in2.low()
in3.low()
in4.high()
while True:
distance = get_distance_cm()
print("거리:", round(distance, 1), "cm")
if distance < 20:
print("장애물 → 정지")
stop()
sleep(0.2)
print("후진")
backward()
sleep(0.5)
print("우회전")
turn_right()
sleep(0.65)
stop()
sleep(0.2)
else:
forward()
sleep(0.05)
확인 포인트
- 후진 시간이 길면 장애물에서 더 멀어집니다.
- 회전 시간이 길면 더 많이 방향을 바꿉니다.
- 오른쪽으로만 회전하면 같은 상황에서 반복적으로 걸릴 수 있으므로 다음 단계에서 좌우 판단을 추가합니다.
8단계
서보모터로 초음파 센서 좌우 살피기
목적
초음파 센서를 서보모터 위에 올려 왼쪽, 앞, 오른쪽 거리를 비교합니다.
초음파 센서를 서보모터 위에 올려 왼쪽, 앞, 오른쪽 거리를 비교합니다.
배선
서보 신호선→GP16, VCC→외부 5V, GND→공통 GND / 초음파는 6단계와 같음
서보 신호선→GP16, VCC→외부 5V, GND→공통 GND / 초음파는 6단계와 같음
from machine import Pin, PWM, time_pulse_us
from time import sleep_us, sleep
trig = Pin(2, Pin.OUT)
echo = Pin(3, Pin.IN)
servo = PWM(Pin(16))
servo.freq(50)
def set_angle(angle):
min_duty = 1638
max_duty = 8192
duty = int(min_duty + (angle / 180) * (max_duty - min_duty))
servo.duty_u16(duty)
sleep(0.35)
def get_distance_cm():
trig.low()
sleep_us(2)
trig.high()
sleep_us(10)
trig.low()
duration = time_pulse_us(echo, 1, 30000)
if duration < 0:
return 999
return duration * 0.0343 / 2
while True:
set_angle(150)
left = get_distance_cm()
set_angle(90)
front = get_distance_cm()
set_angle(30)
right = get_distance_cm()
set_angle(90)
print("왼쪽:", round(left, 1), "cm")
print("앞쪽:", round(front, 1), "cm")
print("오른쪽:", round(right, 1), "cm")
print("-" * 25)
sleep(1)
확인 포인트
- 서보가 떨리면 외부 5V 전원을 사용하고 GND를 공통 연결합니다.
- 각도 150, 90, 30은 장착 방향에 따라 140, 90, 40처럼 조절할 수 있습니다.
- 센서가 왼쪽을 볼 때 오른쪽 값이 나오는 경우 서보 장착 방향을 확인합니다.
9단계
좌우 거리 비교 장애물 회피 자동차 완성
목적
앞이 막히면 왼쪽과 오른쪽을 비교하고 더 넓은 방향으로 회전합니다.
앞이 막히면 왼쪽과 오른쪽을 비교하고 더 넓은 방향으로 회전합니다.
배선
초음파 TRIG→GP2, ECHO→GP3 / IN1~IN4→GP10~GP13 / ENA→GP14 / ENB→GP15 / 서보→GP16
초음파 TRIG→GP2, ECHO→GP3 / IN1~IN4→GP10~GP13 / ENA→GP14 / ENB→GP15 / 서보→GP16
from machine import Pin, PWM, time_pulse_us
from time import sleep_us, sleep
trig = Pin(2, Pin.OUT)
echo = Pin(3, Pin.IN)
in1 = Pin(10, Pin.OUT)
in2 = Pin(11, Pin.OUT)
in3 = Pin(12, Pin.OUT)
in4 = Pin(13, Pin.OUT)
ena = PWM(Pin(14))
enb = PWM(Pin(15))
ena.freq(1000)
enb.freq(1000)
servo = PWM(Pin(16))
servo.freq(50)
SAFE_DISTANCE = 22
DRIVE_SPEED = 33000
TURN_SPEED = 37000
def set_angle(angle):
min_duty = 1638
max_duty = 8192
duty = int(min_duty + (angle / 180) * (max_duty - min_duty))
servo.duty_u16(duty)
sleep(0.35)
def get_distance_cm():
trig.low()
sleep_us(2)
trig.high()
sleep_us(10)
trig.low()
duration = time_pulse_us(echo, 1, 30000)
if duration < 0:
return 999
return duration * 0.0343 / 2
def set_speed(left, right):
left = max(0, min(65535, left))
right = max(0, min(65535, right))
ena.duty_u16(left)
enb.duty_u16(right)
def stop():
set_speed(0, 0)
in1.low()
in2.low()
in3.low()
in4.low()
def forward(speed=DRIVE_SPEED):
set_speed(speed, speed)
in1.high()
in2.low()
in3.high()
in4.low()
def backward(speed=DRIVE_SPEED):
set_speed(speed, speed)
in1.low()
in2.high()
in3.low()
in4.high()
def turn_left(speed=TURN_SPEED):
set_speed(speed, speed)
in1.low()
in2.high()
in3.high()
in4.low()
def turn_right(speed=TURN_SPEED):
set_speed(speed, speed)
in1.high()
in2.low()
in3.low()
in4.high()
def look_left():
set_angle(150)
return get_distance_cm()
def look_front():
set_angle(90)
return get_distance_cm()
def look_right():
set_angle(30)
return get_distance_cm()
set_angle(90)
while True:
front = look_front()
print("앞:", round(front, 1), "cm")
if front > SAFE_DISTANCE:
forward()
else:
stop()
sleep(0.2)
backward()
sleep(0.35)
stop()
sleep(0.2)
left = look_left()
right = look_right()
set_angle(90)
print("왼쪽:", round(left, 1), "cm / 오른쪽:", round(right, 1), "cm")
if left > right:
print("왼쪽으로 회피")
turn_left()
sleep(0.65)
else:
print("오른쪽으로 회피")
turn_right()
sleep(0.65)
stop()
sleep(0.1)
확인 포인트
- 가장 완성도 높은 기본 장애물 회피 코드입니다.
- SAFE_DISTANCE, DRIVE_SPEED, TURN_SPEED만 바꿔도 자동차 성격이 크게 달라집니다.
- 교실 바닥에서 직진이 휘면 왼쪽·오른쪽 PWM 값을 다르게 주어 보정합니다.
10단계
Pico WH Wi-Fi 웹 원격 정지·출발 기능 추가
목적
Pico WH의 Wi-Fi 기능을 이용해 휴대폰이나 PC 브라우저에서 자동차를 시작·정지합니다. 장애물 회피 로직은 9단계와 결합했습니다.
Pico WH의 Wi-Fi 기능을 이용해 휴대폰이나 PC 브라우저에서 자동차를 시작·정지합니다. 장애물 회피 로직은 9단계와 결합했습니다.
배선
9단계 전체 배선 + Wi-Fi 공유기 접속 정보 필요
9단계 전체 배선 + Wi-Fi 공유기 접속 정보 필요
import network
import socket
from machine import Pin, PWM, time_pulse_us
from time import sleep_us, sleep
SSID = "와이파이이름"
PASSWORD = "와이파이비밀번호"
trig = Pin(2, Pin.OUT)
echo = Pin(3, Pin.IN)
in1 = Pin(10, Pin.OUT)
in2 = Pin(11, Pin.OUT)
in3 = Pin(12, Pin.OUT)
in4 = Pin(13, Pin.OUT)
ena = PWM(Pin(14))
enb = PWM(Pin(15))
ena.freq(1000)
enb.freq(1000)
servo = PWM(Pin(16))
servo.freq(50)
led = Pin("LED", Pin.OUT)
running = False
SAFE_DISTANCE = 22
DRIVE_SPEED = 32000
TURN_SPEED = 36000
def connect_wifi():
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(SSID, PASSWORD)
print("Wi-Fi 연결 중...")
while not wlan.isconnected():
led.toggle()
sleep(0.3)
led.on()
ip = wlan.ifconfig()[0]
print("연결 완료 IP:", ip)
return ip
def set_angle(angle):
min_duty = 1638
max_duty = 8192
duty = int(min_duty + (angle / 180) * (max_duty - min_duty))
servo.duty_u16(duty)
sleep(0.25)
def get_distance_cm():
trig.low()
sleep_us(2)
trig.high()
sleep_us(10)
trig.low()
duration = time_pulse_us(echo, 1, 30000)
if duration < 0:
return 999
return duration * 0.0343 / 2
def set_speed(left, right):
ena.duty_u16(max(0, min(65535, left)))
enb.duty_u16(max(0, min(65535, right)))
def stop():
set_speed(0, 0)
in1.low()
in2.low()
in3.low()
in4.low()
def forward(speed=DRIVE_SPEED):
set_speed(speed, speed)
in1.high()
in2.low()
in3.high()
in4.low()
def backward(speed=DRIVE_SPEED):
set_speed(speed, speed)
in1.low()
in2.high()
in3.low()
in4.high()
def turn_left(speed=TURN_SPEED):
set_speed(speed, speed)
in1.low()
in2.high()
in3.high()
in4.low()
def turn_right(speed=TURN_SPEED):
set_speed(speed, speed)
in1.high()
in2.low()
in3.low()
in4.high()
def avoid_once():
front = get_distance_cm()
if front > SAFE_DISTANCE:
forward()
return front
stop()
sleep(0.15)
backward()
sleep(0.3)
stop()
set_angle(150)
left = get_distance_cm()
set_angle(30)
right = get_distance_cm()
set_angle(90)
if left > right:
turn_left()
sleep(0.6)
else:
turn_right()
sleep(0.6)
stop()
return front
def web_page(ip, distance):
state = "주행 중" if running else "정지"
return f"""HTTP/1.1 200 OK
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>Pico WH 장애물 회피 자동차</title>
<style>
body {{
font-family: Arial, sans-serif;
text-align: center;
padding: 40px;
}}
a {{
display: inline-block;
margin: 12px;
padding: 18px 26px;
border-radius: 14px;
color: white;
text-decoration: none;
font-size: 24px;
}}
.start {{ background: #16a34a; }}
.stop {{ background: #dc2626; }}
</style>
</head>
<body>
<h1>Pico WH 장애물 회피 자동차</h1>
<h2>상태: {state}</h2>
<h2>최근 거리: {distance:.1f} cm</h2>
<p>접속 주소: http://{ip}</p>
<a class="start" href="/start">출발</a>
<a class="stop" href="/stop">정지</a>
</body>
</html>
"""
ip = connect_wifi()
set_angle(90)
addr = socket.getaddrinfo("0.0.0.0", 80)[0][-1]
server = socket.socket()
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(addr)
server.listen(1)
server.settimeout(0.05)
last_distance = 999
print("브라우저에서 접속:", "http://" + ip)
while True:
try:
client, address = server.accept()
request = client.recv(1024).decode()
if "GET /start" in request:
running = True
if "GET /stop" in request:
running = False
stop()
response = web_page(ip, last_distance)
client.send(response)
client.close()
except OSError:
pass
if running:
last_distance = avoid_once()
else:
stop()
sleep(0.05)
확인 포인트
- SSID와 PASSWORD를 실제 Wi-Fi 정보로 바꿉니다.
- Thonny Shell에 출력되는 IP 주소를 휴대폰 또는 PC 브라우저에 입력합니다.
- 같은 Wi-Fi에 연결된 기기에서만 접속되는 경우가 많습니다.
- 주행 중 긴급 정지를 위해 정지 버튼이 잘 작동하는지 바퀴를 띄운 상태에서 먼저 테스트합니다.