카테고리 없음

Pico + W5500, MicroPython통신 확인하기.

trustworthyhand 2025. 9. 30. 16:24
# 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) 최소/표준 패턴(암기용)
  • 고정 IP(직결 테스트용)
    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))