# main.py (Pico + W5500, MicroPython)
import time, machine, network, usocket as socket, binascii
# --- 네트워크 초기화 ---
spi = machine.SPI(0, baudrate=2_000_000,
sck=machine.Pin(18), mosi=machine.Pin(19), miso=machine.Pin(16))
cs = machine.Pin(17, machine.Pin.OUT)
rst = machine.Pin(20, machine.Pin.OUT)
nic = network.WIZNET5K(spi, cs, rst)
nic.active(True)
nic.ifconfig(('192.168.10.2', '255.255.255.0', '192.168.10.1', '192.168.10.1'))
print("Network up:", nic.ifconfig())
# --- 하드웨어(온보드 LED) ---
LED = machine.Pin(25, machine.Pin.OUT)
# --- HEX 변환 유틸 ---
def parse_hex(s: str):
s = s.strip().replace(" ", "")
if len(s) % 2 != 0:
raise ValueError("HEX 길이는 짝수여야 합니다.")
return binascii.unhexlify(s)
def bytes_to_hex(b: bytes):
return binascii.hexlify(b).decode().upper()
# --- TCP 서버 ---
HOST, PORT = '0.0.0.0', 5000
srv = socket.socket()
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind((HOST, PORT))
srv.listen(1)
print("listening on", PORT)
HELP_TEXT = (
b"Commands:\r\n"
b" LEDON -> LED \xEC\xBC\x9C\xEA\xB8\xb0\r\n"
b" LEDOFF -> LED \xB9\xB0\xEA\xB8\xb0\r\n"
b" BLINK N -> LED N\xED\x9A\x8C \xEC\xA0\x90\xEB\xA9\xB8\r\n"
b" SENDHEX XXXXXXXX -> 4\xEB\xB0\xB0\xEC\x9D\xb4\xED\x8A\xb8 HEX \xED\x85\x8C\xEC\x8A\xa4\xED\x8A\xb8 (e.g., SENDHEX 01020304)\r\n"
b" \xEC\x97\x90\xEC\xBD\x94 + \xED\x95\xa9\xEA\xB3\x84(sum) \xEC\x95\x8C\xEB\xA6\xBC\r\n"
b" WHO -> \xEC\x9E\xA5\xEC\xB9\x98 \xEC\xA0\x95\xEB\xB3\xb4\r\n"
b" HELP -> \xEB\xAA\x85\xEB\xA0\xb9 \xEB\x8F\x84\xEC\x9B\x80\xEB\xA7\x90\r\n"
b" QUIT -> \xEC\x97\xB0\xEA\xB2\xBD \xEC\xA2\x85\xEB\xA3\x8C\r\n"
)
while True:
conn, addr = srv.accept()
print("client:", addr)
conn.settimeout(300) # 필요시 조정
try:
conn.send(b"Connected to Pico W5500.\r\n" + HELP_TEXT)
while True:
data = conn.recv(1024)
if not data:
print("client closed")
break
print("RAW:", len(data), repr(data)) # 디버그
# ✨ 여기 수정: 키워드 인자 미사용
try:
text = data.decode() # 혹은 data.decode('utf-8')
except:
text = "" # 디코드 실패 시 안전 처리
text = text.strip()
if text == "":
# 엔터만 입력된 경우 무시하고 계속
continue
print("recv:", repr(text))
up = text.upper()
try:
if up == "LEDON":
LED.on()
conn.send(b"OK: LED ON\r\n")
elif up == "LEDOFF":
LED.off()
conn.send(b"OK: LED OFF\r\n")
elif up.startswith("BLINK"):
try:
n = int(up.split()[1])
except:
n = 3
for _ in range(max(0, n)):
LED.on(); time.sleep(0.15)
LED.off(); time.sleep(0.15)
conn.send(("OK: BLINK %d\r\n" % n).encode())
elif up.startswith("SENDHEX"):
try:
hexstr = text.split(None, 1)[1]
payload = parse_hex(hexstr)
if len(payload) != 4:
conn.send(b"ERR: 4 bytes only (e.g., SENDHEX 01020304)\r\n")
continue
s = (payload[0] + payload[1] + payload[2] + payload[3]) & 0xFF
conn.send(b"ECHO: " + bytes_to_hex(payload).encode() + b"\r\n")
conn.send(("SUM : 0x%02X (%d)\r\n" % (s, s)).encode())
except Exception as e:
conn.send(b"ERR: invalid hex (e.g., SENDHEX 01020304)\r\n")
elif up == "WHO":
conn.send(b"Pico + W5500 (MicroPython)\r\n")
elif up == "HELP":
conn.send(HELP_TEXT)
elif up == "QUIT":
conn.send(b"BYE\r\n")
break
else:
conn.send(b"UNKNOWN CMD. Type HELP.\r\n")
except Exception as e:
print("handler error:", e)
conn.send(b"ERR: exception occurred\r\n")
except Exception as e:
print("session error:", e)
finally:
conn.close()
print("session closed")
- machine: 핀/하드웨어 제어
- network: Wiznet NIC 드라이버
- usocket: 경량 소켓(TCP/UDP)
- binascii: 바이트 ↔ 16진수 문자열 변환
- 1) SPI 객체 만들기
- machine.SPI(0, ...)
- SPI0 컨트롤러 사용(피코엔 SPI0, SPI1 두 개가 있어요).
- baudrate=2_000_000
- SPI 클록 2 MHz. 처음엔 낮게 시작해 안정성 확인 → OK면 8~12 MHz까지 점진적으로 올릴 수 있어요.
- sck / mosi / miso
- SCK(클록): GP18
- MOSI(마스터→슬레이브 데이터): GP19
- MISO(슬레이브→마스터 데이터): GP16
- 이 핀맵은 Pico의 SPI0 기본 핀 조합 중 하나입니다. (하드웨어 라우팅이 고정이라 임의 핀 못 써요)
SPI는 “동기 직렬”이라 클록(SCK)과 데이터선(MOSI/MISO) 3가지를 꼭 맞춰야 하고,
같은 컨트롤러(SPI0)에 묶여 있어야 합니다.
2) CS(Chip Select) / RST(Reset) 핀
- CS (GP17)
- SPI 버스에 여러 슬레이브가 있을 때 “지금 너랑 통신할게”라고 지정하는 신호.
- Active-Low(일반적으로): 통신할 때 LOW로 내리고, 끝나면 HIGH.
- 드라이버가 내부에서 토글해줘서 우리가 직접 내릴 일은 거의 없지만, 핀을 OUT으로 잡아주는 것이 중요해요.
- RST (GP20)
- W5500 리셋 핀. 전원 불안정/초기화 필요 시 LOW→HIGH 토글로 깨끗하게 시작.
- 일부 보드엔 풀업이 있어도, 처음 연결/디버깅 땐 연결 권장(초기화가 확실해집니다).
3) WIZNET5K NIC 객체 생성
- MicroPython이 제공하는 Wiznet W5x00 드라이버 인스턴스.
- 내부적으로 W5500 레지스터에 접근해 MAC, IP, 소켓 등을 제어합니다.
- 생성자 인자로 SPI, CS, RST를 넘겨 W5500과 연결을 알려줍니다.
4) NIC 활성화
- 네트워크 인터페이스 “전원 켜기” 같은 동작.
- 이 줄 이후부터 ifconfig, socket 통신이 가능해져요.
5) IP 설정 (고정 IP or DHCP)
- 고정 IP 튜플: (IP, Subnet Mask, Gateway, DNS)
- 지금은 PC와 직결이라 DHCP 서버(공유기)가 없어서 고정 IP를 씁니다.
- Pico: 192.168.10.2
- PC: 192.168.10.1
- 서브넷: 255.255.255.0
- 게이트웨이/DNS는 직결에선 크게 의미 없지만 값은 넣어도 됩니다.
DHCP를 쓴다면(공유기에 꽂을 때)
- 공유기가 자동으로 IP를 줍니다. (예: 192.168.0.x)
6) 제대로 됐는지 빠른 체크
- 링크 LED: RJ45 포트 LED 켜져야 정상(케이블/상대 NIC OK)
- print(nic.ifconfig()) → IP 정보가 보이면 OK
- PC에서 핑: ping 192.168.10.2 성공
- 포트 열었을 땐 PowerShell:
Test-NetConnection 192.168.10.2 -Port 5000 → TcpTestSucceeded : True
7) 자주 하는 실수/함정
- 핀 틀림: SPI0이 아닌 SPI1 핀에 꽂아놓고 SPI(0)를 만드는 경우 → 통신 불가
- 3.3 V 전원: W5500은 5 V 금지. GND 공통 필수.
- RST 미연결: 초기화가 애매해 링크만 켜지고 소켓이 죽는 증상 → RST 연결 권장
- SPI 속도 과속: 처음부터 12 MHz로 올리면 불안정. 2 MHz로 시작
- Auto MDI-X: 요즘 PC NIC는 크로스/스트레이트 자동이지만, 아주 구형이면 케이블 유형 영향.
8) 최소/표준 패턴(암기용)
-
spi = machine.SPI(0, baudrate=2_000_000, sck=machine.Pin(18), mosi=machine.Pin(19), miso=machine.Pin(16)) cs = machine.Pin(17, machine.Pin.OUT)
rst = machine.Pin(20, machine.Pin.OUT)
nic = network.WIZNET5K(spi, cs, rst)
nic.active(True)
nic.ifconfig(('192.168.10.2','255.255.255.0','192.168.10.1','192.168.10.1'))
- DHCP(공유기 연결용) spi = machine.SPI(0, baudrate=8_000_000, sck=machine.Pin(18), mosi=machine.Pin(19), miso=machine.Pin(16))
cs, rst = machine.Pin(17, machine.Pin.OUT), machine.Pin(20, machine.Pin.OUT)
nic = network.WIZNET5K(spi, cs, rst)
nic.active(True)
nic.ifconfig('dhcp')
while not nic.isconnected():
time.sleep(0.2)
print("IF:", nic.ifconfig())
9) 왜 이렇게 해야 하는가(개념 정리)
- Pico는 이더넷 MAC/PHY가 없음 → W5500이 대신 이더넷 스택의 하드웨어 가속을 담당.
- Pico는 SPI로 W5500에 명령/데이터를 밀어 넣고 꺼내오며,
MicroPython의 network.WIZNET5K가 이를 소켓 API(usocket) 로 감싸줘서 파이썬 방식으로 TCP/UDP를 쓰게 해줍니다.
- 그래서 (B) 단계는 “하드웨어 네트워킹 장치를 OS에 붙이는 과정”이라고 이해하면 편해요.
-
spi = machine.SPI(0, baudrate=8_000_000, sck=machine.Pin(18), mosi=machine.Pin(19), miso=machine.Pin(16)) cs, rst = machine.Pin(17, machine.Pin.OUT), machine.Pin(20, machine.Pin.OUT) nic = network.WIZNET5K(spi, cs, rst) nic.active(True) nic.ifconfig('dhcp') while not nic.isconnected(): time.sleep(0.2) print("IF:", nic.ifconfig())
- 고정 IP(직결 테스트용)
-
nic.ifconfig('dhcp') while not nic.isconnected(): time.sleep(0.2) print(nic.ifconfig()) # 할당된 IP 확인
-
nic.ifconfig(('192.168.10.2', '255.255.255.0', '192.168.10.1', '192.168.10.1'))
-
nic.active(True)
-
nic = network.WIZNET5K(spi, cs, rst)
-
cs = machine.Pin(17, machine.Pin.OUT) rst = machine.Pin(20, machine.Pin.OUT)
-
spi = machine.SPI(0, baudrate=2_000_000, sck=machine.Pin(18), mosi=machine.Pin(19), miso=machine.Pin(16))