Skip to content

laser-projector

This project is for a quadruple laser projection controller, aimed at supporting room-scale projections

Lihan__

Published July 06, 2026

Original author: adeboniOriginal source (new tab)

laser-projector

Project description

Room-Scale Quad Laser Projector β€” When Four Galvo Heads Dance to the Same Beat, No Wires Attached

#W5500 #UDP #RP2040 #GalvoLaser #RealTimeStreaming #ArtInstallation #MusicSync #WIZnet-Pico-SDK #DualCore #MulticastControl

πŸ“š Context: Personal art/entertainment installation β€” a room-scale interactive laser show system, built and iterated across three build generations (2023 β†’ 2025). βœ… Implementation status: Full hardware (custom KiCad PCB, BOM-verified) + full firmware (RP2040 + W5500) + full server software (Python control station with audio/BLE/DMX integration) all present in-repo and consistent with each other.


01 β€” What is this project?

Most DIY laser shows on GitHub stop at "one MCU drives one galvo pair." This project goes further: it's a four-head, room-scale laser installation where every head is an independent network node, and a single Python "control station" chΠΎΡ€Π΅ographs all of them in real time β€” synced to music, to a hand-held motion wand, to DMX/sACN lighting cues, or to generative visual algorithms (spirographs, audio-reactive patterns, even a two-player Pong game rendered in laser light).

The project has visibly evolved through three hardware generations:

  • 2023 β€” an Arduino-class MCU driving 4 lasers over a CAN bus. No networking; everything wired point-to-point.
  • 2024 β€” a full redesign: each laser head becomes its own RP2040 + W5500 board, receiving its frame data over standard Ethernet UDP from a Raspberry Pi running the Python server. A SuperCollider synthesizer patch was added for audio-reactive shows.
  • 2025 β€” same hardware, refined server software (bug fixes across song handling, wand tracking, and the Sierpinski/spirograph generators).

The result is a laser installation that behaves less like a single gadget and more like a small distributed real-time rendering cluster, with WIZnet's Ethernet offload doing the unglamorous but critical job of getting frame data to four independent heads without jitter.


02 β€” Why Ethernet over Wi-Fi or a wired protocol?

πŸ”· Room-scale = physically separated heads

Four laser projectors, mounted around a room, all need to receive synchronized frame data. Running four Wi-Fi radios in the same room with tight-timing laser data is an invitation to jitter and packet loss from RF contention. A wired star topology from a central Raspberry Pi avoids that problem entirely.

πŸ”· Deterministic, low-jitter delivery

Laser vector graphics are extremely sensitive to timing β€” a delayed point doesn't just look wrong, it visibly distorts the image or "wobbles" the beam. Standard Ethernet with a dedicated hardware TCP/IP stack per node keeps that jitter bounded in a way a shared Wi-Fi channel across four receivers cannot.

πŸ”· Simplicity of the star topology

One Raspberry Pi source, four independent Ethernet-connected sinks β€” no mesh, no pairing, no discovery protocol. The Pi simply targets each head at a fixed IP (10.0.0.10, .11, .12, .13) and streams.


03 β€” System architecture

03 β€” System architecture

Each head is board-strapped with a 3-bit GPIO ID (pins 6/7/8, pulled up) that auto-derives its IP address (10.0.0.10 + board_id) and MAC address suffix β€” so all four boards run identical firmware, differentiated only by a jumper.


04 β€” Why WIZnet W5500? ⭐

πŸ”· Dedicated core, dedicated job β€” no stack overhead competing with real-time rendering

The RP2040 is a dual-core MCU, and this project puts that architecture to direct use: core1 does nothing but service the W5500 (getSn_SR, getSn_RX_RSR, recvfrom), while core0 runs a tight 150 Β΅s loop driving the DAC and PWM outputs for the actual laser beam. Because the W5500 handles the TCP/IP stack entirely in hardware, core1's job reduces to a handful of register polls and a recvfrom() call β€” there is no software network stack competing for the same core cycles that need to stay free for beam-steering timing. On a bare RP2040 without hardware offload, that separation would be much harder to guarantee.

πŸ”· Socket mode used: UDP (Sn_MR_UDP)

The firmware opens socket 0 in UDP mode with SF_IO_NONBLOCK:

 
c
sockfd = socket(0, Sn_MR_UDP, 8090, SF_IO_NONBLOCK); ... size = getSn_RX_RSR(sockfd); size = recvfrom(sockfd, recv_buf, size, dest_ip, &dest_port);

UDP is the correct choice here, not TCP: laser frame data is a continuous real-time stream where a dropped frame should be silently skipped, not retransmitted. TCP's retransmission and head-of-line blocking would actively hurt a system where "just draw the next frame" is always the right behavior. A one-byte sequence counter in the packet lets the firmware detect and discard out-of-order or dropped packets without any handshake overhead.

πŸ”· Built on WIZnet's own reference port

The SPI glue code (w5x00_spi.c/h) carries the WIZnet Co.,Ltd copyright header and matches WIZnet's official ioLibrary_Driver RP2040 port β€” the project correctly reused the vendor reference implementation rather than reinventing SPI/CS handling, which is exactly the kind of integration WIZnet's ports are meant to enable.

πŸ”· Verified in a real, physical multi-head deployment

This isn't a single-board demo β€” it's four physically distinct, independently-addressed W5500 boards deployed simultaneously in one room, each fed from the same source stream, with a custom KiCad-designed carrier board (combo_laser_driver) putting the W5500 directly next to the RP2040 and DAC on a single PCB (BOM-confirmed: W5500 LQFP-48, RP2040 QFN-56, MCP4922 DAC, W25Q128JVS flash).


05 β€” Key components

🌐 WIZnet W5500 β€” UDP socket mode, hardware-offloaded frame receiver

Bare W5500 (LQFP-48) on a custom PCB, one per laser head, each running the identical firmware image and differentiated only by a GPIO strap. Handles all Ethernet/IP/UDP processing in hardware so the RP2040's second core is free for microsecond-precision beam control.

πŸŽ›οΈ RP2040 (dual-core)

  • Core0: 150 Β΅s main loop β€” reads points from a lock-free queue, writes PWM (RGB laser intensity) and drives the DAC (galvo X/Y position), with a 3-second no-signal timeout that blanks the beam for safety.
  • Core1: dedicated W5500 UDP receive loop, unpacking 6-byte point records (12-bit X, 12-bit Y, 8-bit R/G/B) from each incoming packet into the shared queue.

🎯 MCP4922 β€” dual 12-bit SPI DAC

Drives the X/Y galvanometer mirrors that steer the laser beam β€” the same vector-scanning principle used in commercial ILDA laser projectors.

πŸ’‘ Direct RGB laser diode driving

Red/Green/Blue diodes driven directly via PWM (documented operating points: Red 3.3 V/46 mA, Green 7.2 V/27 mA, Blue 7.2 V/44 mA) rather than through a separate laser projector engine.

πŸ–₯️ Python control station (Raspberry Pi)

app.py (pygame GUI) orchestrates laser_server.py, which streams UDP frames to all four heads at a fixed ~25.8 ms cadence. Multiple generative and interactive modes are implemented: audio visualization, parametric equations, spirographs, a laser Pong game, drum-triggered graphics, BLE-wand-driven freehand drawing (using quaternion IMU tracking via pyquaternion), and a calibration mode. sacn_handler.py additionally drives sACN/DMX-based ambient lighting in sync with the laser show.


06 β€” Application scenarios

01. Music-synced laser installations β€” the audio-visualization and drum-graphics generators make this directly usable for live performance or exhibit spaces without further development.

02. Interactive art exhibits β€” the BLE motion wand mode lets a visitor "draw" in the air with the laser responding to their hand motion in real time, a pattern reusable for any hands-on installation.

03. Multi-node real-time UDP streaming reference β€” the core1/core0 split and the custom lightweight framing protocol (sequence byte + fixed-size point records) is a clean reference for anyone building a low-latency, hardware-offloaded UDP sink on RP2040 + W5500.

04. Scalable venue lighting β€” the board-strap addressing scheme (GPIO-derived IP/MAC) means adding a 5th, 6th, or Nth head is a matter of flashing identical firmware and setting a jumper β€” a pattern applicable well beyond laser shows, to any array of identical Ethernet-addressed peripheral nodes.


Conclusion

Four independently-addressed W5500 nodes, one Raspberry Pi source, and a beam-steering loop that never has to think about the network β€” this project shows what "hardware offload" buys you when microseconds actually matter.

  • βœ… Custom KiCad PCB with W5500 + RP2040 + MCP4922 co-located, BOM-verified
  • βœ… Clean UDP socket-mode usage matching the real-time, loss-tolerant nature of laser streaming
  • βœ… Dual-core RP2040 architecture that isolates network I/O from beam-timing-critical code
  • βœ… Built on WIZnet's official ioLibrary RP2040 SPI port rather than a from-scratch driver
  • βœ… Three build generations of iteration, from wired CAN bus to a networked multi-head system
  • βœ… A full accompanying server ecosystem: audio reactivity, BLE wand tracking, sACN lighting sync, generative visual modes
  • βœ… Auto-addressing via GPIO strap β€” a reusable pattern for any identical multi-node deployment

07 β€” Similar Projects on WIZnet Makers

OpenLaserDAC β€” An open-source ESP32-S3 laser DAC firmware implementing the Ether Dream protocol over W5500 Ethernet (with WiFi fallback), driving an 8-channel DAC80508 for a single laser head. It targets compatibility with existing laser-show software (MadMapper, Liberation, any Ether Dream client) and adds a web UI with OTA firmware updates.

 Room-Scale Quad Laser ProjectorOpenLaserDAC
MCURP2040 (dual-core)ESP32-S3 (dual-core)
WIZnet chipW5500 (bare chip, custom carrier PCB)W5500 (SPI, interrupt-driven)
Socket modeUDP (Sn_MR_UDP), custom framingTCP (Ether Dream, port 7765) + UDP broadcast discovery (port 7654)
Position DACMCP4922 (12-bit, 2-channel)DAC80508 (16-bit, 8-channel)
Head count4, independently addressed1
Client compatibilityCustom Python server onlyStandard Ether Dream ecosystem (MadMapper, Liberation, etc.)
Extra featuresBLE motion wand, sACN/DMX sync, audio-reactive & generative visual modesWeb dashboard, OTA updates, WiFi fallback
Deployment modelFixed multi-node installationSingle, software-agnostic laser head

Insight: the two projects sit at opposite ends of the same design space. The quad projector optimizes for a closed, purpose-built installation β€” custom protocol, custom server, multiple synchronized heads, rich interactive/generative content built in. OpenLaserDAC optimizes for interoperability β€” standard Ether Dream protocol means it drops into any existing laser-show software stack with zero custom server work, at the cost of being a single head with no built-in show logic of its own. Both independently chose the W5500 for the same underlying reason: a laser head is a latency-sensitive embedded target where a software network stack would compete with real-time DAC timing, and both land on UDP-based real-time delivery for the actual laser point stream (Ether Dream itself uses UDP for point-streaming, with TCP reserved for control/handshake). Together they sketch a natural roadmap: a future head design could adopt Ether Dream compatibility (from OpenLaserDAC) while keeping the multi-head synchronization and rich show content (from the quad projector).


Q&A

Q. Why UDP instead of TCP, given TCP's reliability guarantees? A. Laser frame data is inherently disposable β€” a lost frame should be skipped, not retransmitted, since retransmission would arrive after its display window has passed. UDP's fire-and-forget model plus a simple sequence-number check for drop detection is the right fit; TCP's retransmission and ordering guarantees would only add latency without adding value here.

Q. Could this run on the W6100 or W5500-based module boards instead of a bare chip on a custom PCB? A. Functionally yes β€” the socket API used (socket(), getSn_RX_RSR(), recvfrom()) is identical across WIZnet's W5x00 family. The custom PCB choice here looks driven by wanting the W5500 co-located with the RP2040 and DAC on one board rather than by any W5500-specific requirement.

Q. How does the 3-second timeout affect safety? A. If core0 stops receiving valid points from the queue for 3 seconds (LASER_TIMEOUT), it forces the laser output to zero β€” a reasonable fail-safe against a network hiccup leaving a beam parked (and potentially hazardous) at a fixed point.


Original Link: https://github.com/adeboni/laser-projector



λ£ΈμŠ€μΌ€μΌ 4채널 λ ˆμ΄μ € ν”„λ‘œμ ν„° β€” 4개의 갈보 ν—€λ“œκ°€ μ„  ν•˜λ‚˜ 없이 같은 λ°•μžμ— 맞좰 μΆ€μΆ˜λ‹€

#W5500 #UDP #RP2040 #κ°ˆλ³΄λ ˆμ΄μ € #μ‹€μ‹œκ°„μŠ€νŠΈλ¦¬λ° #μ•„νŠΈμ„€μΉ˜λ¬Ό #μŒμ•…λ™κΈ°ν™” #WIZnet-Pico-SDK #λ“€μ–Όμ½”μ–΄ #λ©€ν‹°μΊμŠ€νŠΈμ œμ–΄

πŸ“š μ»¨ν…μŠ€νŠΈ: 개인 μ•„νŠΈ/μ—”ν„°ν…ŒμΈλ¨ΌνŠΈ μ„€μΉ˜ ν”„λ‘œμ νŠΈ β€” λ£ΈμŠ€μΌ€μΌ μΈν„°λž™ν‹°λΈŒ λ ˆμ΄μ €μ‡Ό μ‹œμŠ€ν…œμœΌλ‘œ, 2023λ…„λΆ€ν„° 2025λ…„κΉŒμ§€ μ„Έ μ„ΈλŒ€μ— 걸쳐 반볡 개발됨. βœ… κ΅¬ν˜„ μƒνƒœ: ν•˜λ“œμ›¨μ–΄(KiCad μ»€μŠ€ν…€ PCB, BOM 검증 μ™„λ£Œ) + νŽŒμ›¨μ–΄(RP2040 + W5500) + μ„œλ²„ μ†Œν”„νŠΈμ›¨μ–΄(μ˜€λ””μ˜€/BLE/DMX 톡합 파이썬 μ œμ–΄ μŠ€ν…Œμ΄μ…˜) 전체가 μ €μž₯μ†Œμ— μ‘΄μž¬ν•˜λ©° μ„œλ‘œ 정합성이 맞음.


01 β€” 이 ν”„λ‘œμ νŠΈλŠ” 무엇인가?

GitHub의 μ›¬λ§Œν•œ DIY λ ˆμ΄μ €μ‡ΌλŠ” "MCU ν•˜λ‚˜κ°€ 갈보 ν•œ μŒμ„ κ΅¬λ™ν•œλ‹€"μ—μ„œ λ©ˆμΆ˜λ‹€. 이 ν”„λ‘œμ νŠΈλŠ” ν•œ 단계 더 λ‚˜μ•„κ°€ 4개의 λ…λ¦½λœ λ„€νŠΈμ›Œν¬ λ…Έλ“œλ‘œ κ΅¬μ„±λœ λ£ΈμŠ€μΌ€μΌ λ ˆμ΄μ € μ„€μΉ˜λ¬Όμ„ λ§Œλ“€μ—ˆλ‹€. ν•˜λ‚˜μ˜ 파이썬 "μ œμ–΄ μŠ€ν…Œμ΄μ…˜"이 μŒμ•…, 손에 λ“  λͺ¨μ…˜ μ™„λ“œ, DMX/sACN μ‘°λͺ… 큐, λ˜λŠ” μƒμ„±ν˜• λΉ„μ£Όμ–Ό μ•Œκ³ λ¦¬μ¦˜(μŠ€ν”Όλ‘œκ·Έλž˜ν”„, μ˜€λ””μ˜€ λ°˜μ‘ νŒ¨ν„΄, 심지어 λ ˆμ΄μ €λ‘œ λ Œλ”λ§ν•œ 2인용 퐁 κ²Œμž„κΉŒμ§€)에 맞좰 λ„€ λŒ€λ₯Ό λ™μ‹œμ— μ§€νœ˜ν•œλ‹€.

이 ν”„λ‘œμ νŠΈλŠ” μ„Έ μ„ΈλŒ€μ˜ ν•˜λ“œμ›¨μ–΄ μ§„ν™”λ₯Ό 거친 흔적이 λšœλ ·ν•˜λ‹€:

  • 2023λ…„ β€” ArduinoκΈ‰ MCU ν•˜λ‚˜κ°€ CAN λ²„μŠ€λ‘œ 4개의 λ ˆμ΄μ €λ₯Ό ꡬ동. λ„€νŠΈμ›Œν¬ 없이 μ „λΆ€ μ λŒ€μ  λ°°μ„ .
  • 2024λ…„ β€” μ „λ©΄ μž¬μ„€κ³„: 각 λ ˆμ΄μ € ν—€λ“œκ°€ λ…λ¦½λœ RP2040 + W5500 λ³΄λ“œκ°€ λ˜μ–΄, λΌμ¦ˆλ² λ¦¬νŒŒμ΄μ—μ„œ λŒμ•„κ°€λŠ” 파이썬 μ„œλ²„λ‘œλΆ€ν„° ν‘œμ€€ 이더넷 UDP둜 ν”„λ ˆμž„ 데이터λ₯Ό λ°›μŒ. μ˜€λ””μ˜€ λ°˜μ‘ν˜• μ‡Όλ₯Ό μœ„ν•œ SuperCollider 신디사이저 νŒ¨μΉ˜λ„ 좔가됨.
  • 2025λ…„ β€” ν•˜λ“œμ›¨μ–΄λŠ” 동일, μ„œλ²„ μ†Œν”„νŠΈμ›¨μ–΄λ§Œ λ‹€λ“¬μŒ(곑 처리, μ™„λ“œ νŠΈλž˜ν‚Ή, μ‹œμ—λ₯΄ν•€μŠ€ν‚€/μŠ€ν”Όλ‘œκ·Έλž˜ν”„ 생성기 버그 μˆ˜μ •).

결과물은 단일 기기라기보닀 μž‘μ€ 규λͺ¨μ˜ λΆ„μ‚° μ‹€μ‹œκ°„ λ Œλ”λ§ ν΄λŸ¬μŠ€ν„°μ— 가깝고, WIZnet의 이더넷 μ˜€ν”„λ‘œλ“œκ°€ λˆˆμ— 띄진 μ•Šμ§€λ§Œ 결정적인 μ—­ν•  β€” 4개의 독립 ν—€λ“œμ— μ§€ν„° 없이 ν”„λ ˆμž„ 데이터λ₯Ό μ „λ‹¬ν•˜λŠ” 일 β€” 을 λ§‘κ³  μžˆλ‹€.


02 β€” μ™œ Wi-Fiλ‚˜ μœ μ„  ν”„λ‘œν† μ½œμ΄ μ•„λ‹ˆλΌ 이더넷인가?

πŸ”· λ£ΈμŠ€μΌ€μΌ = 물리적으둜 λΆ„μ‚°λœ ν—€λ“œ

λ£Έ 곳곳에 μ„€μΉ˜λœ 4λŒ€μ˜ λ ˆμ΄μ € ν”„λ‘œμ ν„°κ°€ λ™κΈ°ν™”λœ ν”„λ ˆμž„ 데이터λ₯Ό λ°›μ•„μ•Ό ν•œλ‹€. 타이밍이 μ΄˜μ΄˜ν•œ λ ˆμ΄μ € 데이터λ₯Ό μœ„ν•΄ ν•œ λ°©μ—μ„œ 4개의 Wi-Fi λΌλ””μ˜€λ₯Ό λ™μ‹œμ— λŒλ¦¬λŠ” 건 RF κ°„μ„­μœΌλ‘œ μΈν•œ 지터와 νŒ¨ν‚· 손싀을 μžμ΄ˆν•˜λŠ” 일이닀. 쀑앙 λΌμ¦ˆλ² λ¦¬νŒŒμ΄μ—μ„œ λ»—μ–΄λ‚˜μ˜€λŠ” μœ μ„  μŠ€νƒ€ ν† ν΄λ‘œμ§€λŠ” 이 문제λ₯Ό μ• μ΄ˆμ— ν”Όν•΄κ°„λ‹€.

πŸ”· 결정둠적, μ €μ§€ν„° 전달

λ ˆμ΄μ € 벑터 κ·Έλž˜ν”½μ€ 타이밍에 κ·Ήλ„λ‘œ λ―Όκ°ν•˜λ‹€ β€” 포인트 ν•˜λ‚˜κ°€ μ§€μ—°λ˜λ©΄ λ‹¨μˆœνžˆ 이상해 λ³΄μ΄λŠ” 게 μ•„λ‹ˆλΌ 이미지가 λˆˆμ— λ„κ²Œ μΌκ·ΈλŸ¬μ§€κ±°λ‚˜ 빔이 "흔듀린닀." λ…Έλ“œλ§ˆλ‹€ μ „μš© ν•˜λ“œμ›¨μ–΄ TCP/IP μŠ€νƒμ„ κ°–μΆ˜ ν‘œμ€€ 이더넷은, 4개 μˆ˜μ‹ λ‹¨μ΄ κ³΅μœ ν•˜λŠ” Wi-Fi μ±„λ„λ‘œλŠ” λΆˆκ°€λŠ₯ν•œ λ°©μ‹μœΌλ‘œ κ·Έ μ§€ν„°λ₯Ό μ–΅μ œν•œλ‹€.

πŸ”· μŠ€νƒ€ ν† ν΄λ‘œμ§€μ˜ λ‹¨μˆœν•¨

라즈베리파이 μ†ŒμŠ€ ν•˜λ‚˜, μ΄λ”λ„·μœΌλ‘œ μ—°κ²°λœ 독립 싱크 4개 β€” λ©”μ‹œλ„, νŽ˜μ–΄λ§λ„, λ””μŠ€μ»€λ²„λ¦¬ ν”„λ‘œν† μ½œλ„ ν•„μš” μ—†λ‹€. νŒŒμ΄λŠ” 각 ν—€λ“œλ₯Ό κ³ μ • IP(10.0.0.10, .11, .12, .13)둜 μ§€μ •ν•΄ κ·Έλƒ₯ μŠ€νŠΈλ¦¬λ°ν•œλ‹€.


03 β€” μ‹œμŠ€ν…œ μ•„ν‚€ν…μ²˜

03 β€” μ‹œμŠ€ν…œ μ•„ν‚€ν…μ²˜

각 ν—€λ“œλŠ” GPIO 3λΉ„νŠΈ ID(6/7/8번 ν•€, ν’€μ—…)둜 λ³΄λ“œλ₯Ό μŠ€νŠΈλž©ν•΄μ„œ IP μ£Όμ†Œ(10.0.0.10 + board_id)와 MAC μ£Όμ†Œ λ’·μžλ¦¬λ₯Ό μžλ™μœΌλ‘œ κ²°μ •ν•œλ‹€ β€” κ·Έλž˜μ„œ 4개 λ³΄λ“œ λͺ¨λ‘ λ™μΌν•œ νŽŒμ›¨μ–΄λ₯Ό 돌리고 점퍼 ν•˜λ‚˜λ‘œλ§Œ κ΅¬λΆ„λœλ‹€.


04 β€” μ™œ WIZnet W5500인가? ⭐

πŸ”· μ „μš© μ½”μ–΄, μ „μš© μž„λ¬΄ β€” μ‹€μ‹œκ°„ λ Œλ”λ§κ³Ό κ²½μŸν•˜μ§€ μ•ŠλŠ” λ„€νŠΈμ›Œν¬ μŠ€νƒ

RP2040은 λ“€μ–Όμ½”μ–΄ MCU이고, 이 ν”„λ‘œμ νŠΈλŠ” κ·Έ ꡬ쑰λ₯Ό μ •ν™•νžˆ ν™œμš©ν•œλ‹€. core1은 였직 W5500 μ„œλΉ„μŠ€λ§Œ λ‹΄λ‹Ήν•˜κ³ (getSn_SR, getSn_RX_RSR, recvfrom), core0은 150Β΅s 주기의 νƒ€μ΄νŠΈν•œ λ£¨ν”„λ‘œ DAC와 PWM 좜λ ₯을 ꡬ동해 μ‹€μ œ λ ˆμ΄μ € 빔을 μ œμ–΄ν•œλ‹€. W5500이 TCP/IP μŠ€νƒ 전체λ₯Ό ν•˜λ“œμ›¨μ–΄λ‘œ μ²˜λ¦¬ν•˜κΈ° λ•Œλ¬Έμ—, core1의 일은 λ ˆμ§€μŠ€ν„° 폴링 λͺ‡ 번과 recvfrom() 호좜둜 쀄어든닀 β€” λΉ” μ‘°ν–₯ 타이밍을 μœ„ν•΄ λΉ„μ›Œλ‘¬μ•Ό ν•  μ½”μ–΄ 사이클을 놓고 κ²½μŸν•˜λŠ” μ†Œν”„νŠΈμ›¨μ–΄ λ„€νŠΈμ›Œν¬ μŠ€νƒμ΄ μ—†λ‹€. ν•˜λ“œμ›¨μ–΄ μ˜€ν”„λ‘œλ“œκ°€ μ—†λŠ” μˆœμ • RP2040μ΄μ—ˆλ‹€λ©΄ 이런 뢄리λ₯Ό 보μž₯ν•˜κΈ°κ°€ 훨씬 어렀웠을 것이닀.

πŸ”· μ‚¬μš© λͺ¨λ“œ: UDP (Sn_MR_UDP)

νŽŒμ›¨μ–΄λŠ” μ†ŒμΌ“ 0λ²ˆμ„ UDP λͺ¨λ“œ, SF_IO_NONBLOCK으둜 μ—°λ‹€:

sockfd = socket(0, Sn_MR_UDP, 8090, SF_IO_NONBLOCK); ... size = getSn_RX_RSR(sockfd); size = recvfrom(sockfd, recv_buf, size, dest_ip, &dest_port);

μ—¬κΈ°μ„œ UDPλŠ” TCPκ°€ μ•„λ‹ˆλΌ μ •ν™•ν•œ 선택이닀. λ ˆμ΄μ € ν”„λ ˆμž„ λ°μ΄ν„°λŠ” 연속적인 μ‹€μ‹œκ°„ 슀트림이고, ν”„λ ˆμž„ ν•˜λ‚˜κ°€ μ†μ‹€λ˜λ©΄ μž¬μ „μ†‘ν•  게 μ•„λ‹ˆλΌ 쑰용히 κ±΄λ„ˆλ›°μ–΄μ•Ό ν•œλ‹€. TCP의 μž¬μ „μ†‘κ³Ό ν—€λ“œμ˜€λΈŒλΌμΈ λΈ”λ‘œν‚Ήμ€ "κ·Έλƒ₯ λ‹€μŒ ν”„λ ˆμž„μ„ 그렀라"κ°€ 항상 정닡인 μ‹œμŠ€ν…œμ—μ„œ 였히렀 ν•΄κ°€ λœλ‹€. νŒ¨ν‚·μ— λ‹΄κΈ΄ 1λ°”μ΄νŠΈ μ‹œν€€μŠ€ μΉ΄μš΄ν„° 덕뢄에 ν•Έλ“œμ…°μ΄ν¬ μ˜€λ²„ν—€λ“œ 없이 μˆœμ„œκ°€ μ–΄κΈ‹λ‚˜κ±°λ‚˜ λˆ„λ½λœ νŒ¨ν‚·μ„ 감지해 버릴 수 μžˆλ‹€.

πŸ”· WIZnet 곡식 레퍼런슀 포트 μœ„μ— ꡬ좕

SPI 글루 μ½”λ“œ(w5x00_spi.c/h)μ—λŠ” WIZnet Co.,Ltd μ €μž‘κΆŒ 헀더가 λΆ™μ–΄μžˆκ³  WIZnet 곡식 ioLibrary_Driver의 RP2040 ν¬νŠΈμ™€ μΌμΉ˜ν•œλ‹€ β€” SPI/CS 처리λ₯Ό μ²˜μŒλΆ€ν„° μƒˆλ‘œ μ§œλŠ” λŒ€μ‹  벀더 레퍼런슀 κ΅¬ν˜„μ„ κ·ΈλŒ€λ‘œ μž¬μ‚¬μš©ν–ˆλŠ”λ°, 이건 WIZnet ν¬νŠΈκ°€ λͺ©ν‘œλ‘œ ν•˜λŠ” μ •ν™•ν•œ 톡합 방식이닀.

πŸ”· μ‹€μ œ 물리적 λ©€ν‹°ν—€λ“œ λ°°ν¬μ—μ„œ 검증됨

단일 λ³΄λ“œ 데λͺ¨κ°€ μ•„λ‹ˆλΌ, ν•œ 방에 λ™μ‹œμ— 배치된 4개의 물리적으둜 λ…λ¦½λœ, 각기 λ‹€λ₯Έ μ£Όμ†Œλ₯Ό κ°€μ§„ W5500 λ³΄λ“œλ‹€. λͺ¨λ‘ 같은 μ†ŒμŠ€ μŠ€νŠΈλ¦Όμ„ λ°›κ³ , μ»€μŠ€ν…€ KiCad 섀계 캐리어 λ³΄λ“œ(combo_laser_driver)λŠ” W5500을 RP2040Β·DAC와 ν•œ PCB에 λ‚˜λž€νžˆ λ°°μΉ˜ν–ˆλ‹€(BOM 확인: W5500 LQFP-48, RP2040 QFN-56, MCP4922 DAC, W25Q128JVS ν”Œλž˜μ‹œ).


05 β€” 핡심 κ΅¬μ„±μš”μ†Œ

🌐 WIZnet W5500 β€” UDP μ†ŒμΌ“ λͺ¨λ“œ, ν•˜λ“œμ›¨μ–΄ μ˜€ν”„λ‘œλ“œ ν”„λ ˆμž„ μˆ˜μ‹ κΈ°

μ»€μŠ€ν…€ PCB μœ„μ˜ λ² μ–΄ W5500(LQFP-48), λ ˆμ΄μ € ν—€λ“œλ§ˆλ‹€ ν•œ κ°œμ”©, λ™μΌν•œ νŽŒμ›¨μ–΄ 이미지λ₯Ό 돌리며 GPIO 슀트랩만으둜 κ΅¬λΆ„λœλ‹€. 이더넷/IP/UDP 처리 전체λ₯Ό ν•˜λ“œμ›¨μ–΄μ—μ„œ λ‹΄λ‹Ήν•΄ RP2040의 두 번째 μ½”μ–΄λ₯Ό 마이크둜초 λ‹¨μœ„ λΉ” μ œμ–΄μ— μ˜¨μ „νžˆ λΉ„μ›Œμ€€λ‹€.

πŸŽ›οΈ RP2040 (λ“€μ–Όμ½”μ–΄)

  • Core0: 150Β΅s 메인 루프 β€” 락프리 νμ—μ„œ 포인트λ₯Ό 읽어 PWM(RGB λ ˆμ΄μ € μ„ΈκΈ°)을 μ“°κ³  DAC(갈보 X/Y μœ„μΉ˜)λ₯Ό κ΅¬λ™ν•˜λ©°, μ‹ ν˜Έ μ—†μŒ 3초 νƒ€μž„μ•„μ›ƒ μ‹œ μ•ˆμ „μ„ μœ„ν•΄ 빔을 꺼버림.
  • Core1: W5500 UDP μˆ˜μ‹  μ „μš© 루프. λ“€μ–΄μ˜€λŠ” νŒ¨ν‚·λ§ˆλ‹€ 6λ°”μ΄νŠΈ 포인트 λ ˆμ½”λ“œ(12λΉ„νŠΈ X, 12λΉ„νŠΈ Y, 8λΉ„νŠΈ R/G/B)λ₯Ό ν’€μ–΄μ„œ 곡유 큐에 λ„£μŒ.

🎯 MCP4922 β€” λ“€μ–Ό 12λΉ„νŠΈ SPI DAC

X/Y κ°ˆλ°”λ…Έλ―Έν„° 미러λ₯Ό ꡬ동해 λ ˆμ΄μ € 빔을 μ‘°ν–₯ν•œλ‹€ β€” μƒμš© ILDA λ ˆμ΄μ € ν”„λ‘œμ ν„°μ™€ λ™μΌν•œ 벑터 μŠ€μΊλ‹ 원리.

πŸ’‘ RGB λ ˆμ΄μ € λ‹€μ΄μ˜€λ“œ 직접 ꡬ동

λ³„λ„μ˜ λ ˆμ΄μ € ν”„λ‘œμ ν„° μ—”μ§„ 없이 R/G/B λ‹€μ΄μ˜€λ“œλ₯Ό PWM으둜 직접 κ΅¬λ™ν•œλ‹€(λ™μž‘μ  λ¬Έμ„œν™”λ¨: λΉ¨κ°• 3.3V/46mA, 녹색 7.2V/27mA, νŒŒλž‘ 7.2V/44mA).

πŸ–₯️ 파이썬 μ œμ–΄ μŠ€ν…Œμ΄μ…˜ (라즈베리파이)

app.py(pygame GUI)κ°€ laser_server.pyλ₯Ό μ§€νœ˜ν•˜λ©°, μ•½ 25.8ms 주기둜 4개 ν—€λ“œ 전체에 UDP ν”„λ ˆμž„μ„ μŠ€νŠΈλ¦¬λ°ν•œλ‹€. μ˜€λ””μ˜€ μ‹œκ°ν™”, νŒŒλΌλ©”νŠΈλ¦­ 방정식, μŠ€ν”Όλ‘œκ·Έλž˜ν”„, λ ˆμ΄μ € 퐁 κ²Œμž„, λ“œλŸΌ 트리거 κ·Έλž˜ν”½, BLE μ™„λ“œ 기반 자유 λ“œλ‘œμž‰(pyquaternion으둜 μΏΌν„°λ‹ˆμ–Έ IMU νŠΈλž˜ν‚Ή), μΊ˜λ¦¬λΈŒλ ˆμ΄μ…˜ λͺ¨λ“œκΉŒμ§€ λ‹€μ–‘ν•œ μƒμ„±ν˜•Β·μΈν„°λž™ν‹°λΈŒ λͺ¨λ“œκ°€ κ΅¬ν˜„λΌ μžˆλ‹€. sacn_handler.pyλŠ” sACN/DMX 기반 μ£Όλ³€ μ‘°λͺ…을 λ ˆμ΄μ €μ‡Όμ™€ 동기화해 μΆ”κ°€λ‘œ κ΅¬λ™ν•œλ‹€.


06 β€” μ‘μš© μ‹œλ‚˜λ¦¬μ˜€

01. μŒμ•… 동기화 λ ˆμ΄μ € μ„€μΉ˜λ¬Ό β€” μ˜€λ””μ˜€ 비주얼라이저와 λ“œλŸΌ κ·Έλž˜ν”½ 생성기가 μžˆμ–΄ 별도 개발 없이 라이브 κ³΅μ—°μ΄λ‚˜ μ „μ‹œ 곡간에 λ°”λ‘œ μ“Έ 수 μžˆλ‹€.

02. μΈν„°λž™ν‹°λΈŒ μ•„νŠΈ μ „μ‹œ β€” BLE λͺ¨μ…˜ μ™„λ“œ λͺ¨λ“œλŠ” 방문객이 ν—ˆκ³΅μ— "그림을 그리면" λ ˆμ΄μ €κ°€ μ†λ™μž‘μ— μ‹€μ‹œκ°„μœΌλ‘œ λ°˜μ‘ν•˜κ²Œ ν•΄μ€€λ‹€. μ–΄λ–€ μ²΄ν—˜ν˜• μ„€μΉ˜λ¬Όμ—λ„ μž¬μ‚¬μš© κ°€λŠ₯ν•œ νŒ¨ν„΄μ΄λ‹€.

03. λ©€ν‹°λ…Έλ“œ μ‹€μ‹œκ°„ UDP 슀트리밍 레퍼런슀 β€” core1/core0 뢄리 ꡬ쑰와 자체 κ²½λŸ‰ ν”„λ ˆμ΄λ° ν”„λ‘œν† μ½œ(μ‹œν€€μŠ€ λ°”μ΄νŠΈ + κ³ μ • 크기 포인트 λ ˆμ½”λ“œ)은 RP2040 + W5500으둜 μ €μ§€μ—° ν•˜λ“œμ›¨μ–΄ μ˜€ν”„λ‘œλ“œ UDP 싱크λ₯Ό λ§Œλ“€λ €λŠ” λˆ„κ΅¬μ—κ²Œλ‚˜ κΉ”λ”ν•œ μ°Έκ³  사둀닀.

04. ν™•μž₯ κ°€λŠ₯ν•œ 곡연μž₯ μ‘°λͺ… β€” GPIO 슀트랩 기반 μžλ™ μ£Όμ†Œμ§€μ • 방식 덕뢄에 5번째, 6번째, N번째 ν—€λ“œλ₯Ό μΆ”κ°€ν•˜λŠ” 건 동일 νŽŒμ›¨μ–΄λ₯Ό ν”Œλž˜μ‹œν•˜κ³  점퍼 ν•˜λ‚˜ μ„ΈνŒ…ν•˜λŠ” 문제둜 쀄어든닀 β€” λ ˆμ΄μ €μ‡Όλ₯Ό λ„˜μ–΄ λ™μΌν•œ 이더넷 μ£Όμ†Œμ§€μ • μ£Όλ³€μž₯치 배열이라면 어디에든 적용 κ°€λŠ₯ν•œ νŒ¨ν„΄μ΄λ‹€.


Conclusion

λ…λ¦½μ μœΌλ‘œ μ£Όμ†Œκ°€ μ§€μ •λœ W5500 λ…Έλ“œ 4개, 라즈베리파이 μ†ŒμŠ€ ν•˜λ‚˜, 그리고 λ„€νŠΈμ›Œν¬λ₯Ό μ‹ κ²½ μ“Έ ν•„μš”κ°€ μ „ν˜€ μ—†λŠ” λΉ” μ‘°ν–₯ 루프 β€” 이 ν”„λ‘œμ νŠΈλŠ” λ§ˆμ΄ν¬λ‘œμ΄ˆκ°€ μ§„μ§œλ‘œ μ€‘μš”ν•  λ•Œ "ν•˜λ“œμ›¨μ–΄ μ˜€ν”„λ‘œλ“œ"κ°€ 무엇을 사쀄 수 μžˆλŠ”μ§€ 보여쀀닀.

  • βœ… W5500 + RP2040 + MCP4922λ₯Ό ν•œ λ³΄λ“œμ— λ°°μΉ˜ν•œ μ»€μŠ€ν…€ KiCad PCB, BOM 검증 μ™„λ£Œ
  • βœ… λ ˆμ΄μ € 슀트리밍의 μ‹€μ‹œκ°„Β·μ†μ‹€ν—ˆμš© νŠΉμ„±μ— μ •ν™•νžˆ λ§žλŠ” κΉ”λ”ν•œ UDP μ†ŒμΌ“ λͺ¨λ“œ μ‚¬μš©
  • βœ… λ„€νŠΈμ›Œν¬ I/O와 λΉ” 타이밍 크리티컬 μ½”λ“œλ₯Ό λΆ„λ¦¬ν•˜λŠ” λ“€μ–Όμ½”μ–΄ RP2040 μ•„ν‚€ν…μ²˜
  • βœ… μ²˜μŒλΆ€ν„° μ§  λ“œλΌμ΄λ²„κ°€ μ•„λ‹ˆλΌ WIZnet 곡식 ioLibrary RP2040 SPI 포트 μœ„μ— ꡬ좕
  • βœ… μœ μ„  CAN λ²„μŠ€μ—μ„œ λ„€νŠΈμ›Œν¬ν˜• λ©€ν‹°ν—€λ“œ μ‹œμŠ€ν…œκΉŒμ§€, μ„Έ μ„ΈλŒ€μ— 걸친 반볡 개발
  • βœ… μ˜€λ””μ˜€ λ°˜μ‘, BLE μ™„λ“œ νŠΈλž˜ν‚Ή, sACN μ‘°λͺ… 동기화, μƒμ„±ν˜• λΉ„μ£Όμ–Ό λͺ¨λ“œκΉŒμ§€ κ°–μΆ˜ ν’€ μ„œλ²„ μƒνƒœκ³„
  • βœ… GPIO 슀트랩 기반 μžλ™ μ£Όμ†Œμ§€μ • β€” 동일 ꡬ성 λ©€ν‹°λ…Έλ“œ 배포라면 어디에든 μž¬μ‚¬μš© κ°€λŠ₯ν•œ νŒ¨ν„΄

07 β€” WIZnet Makers λ‚΄ μœ μ‚¬ ν”„λ‘œμ νŠΈ

OpenLaserDAC β€” Ether Dream ν”„λ‘œν† μ½œμ„ W5500 이더넷(WiFi 폴백 포함)으둜 κ΅¬ν˜„ν•œ μ˜€ν”ˆμ†ŒμŠ€ ESP32-S3 λ ˆμ΄μ € DAC νŽŒμ›¨μ–΄. 8채널 DAC80508둜 단일 λ ˆμ΄μ € ν—€λ“œλ₯Ό κ΅¬λ™ν•œλ‹€. κΈ°μ‘΄ λ ˆμ΄μ €μ‡Ό μ†Œν”„νŠΈμ›¨μ–΄(MadMapper, Liberation, λͺ¨λ“  Ether Dream ν΄λΌμ΄μ–ΈνŠΈ)μ™€μ˜ ν˜Έν™˜μ„±μ„ λͺ©ν‘œλ‘œ ν•˜λ©°, OTA νŽŒμ›¨μ–΄ μ—…λ°μ΄νŠΈκ°€ λ˜λŠ” μ›Ή UI도 κ°–μ·„λ‹€.

 λ£ΈμŠ€μΌ€μΌ μΏΌλ“œ λ ˆμ΄μ € ν”„λ‘œμ ν„°OpenLaserDAC
MCURP2040 (λ“€μ–Όμ½”μ–΄)ESP32-S3 (λ“€μ–Όμ½”μ–΄)
WIZnet μΉ©W5500 (λ² μ–΄μΉ©, μ»€μŠ€ν…€ 캐리어 PCB)W5500 (SPI, μΈν„°λŸ½νŠΈ ꡬ동)
μ†ŒμΌ“ λͺ¨λ“œUDP (Sn_MR_UDP), 자체 ν”„λ ˆμ΄λ°TCP (Ether Dream, 포트 7765) + UDP λΈŒλ‘œλ“œμΊμŠ€νŠΈ λ””μŠ€μ»€λ²„λ¦¬ (포트 7654)
μœ„μΉ˜μ œμ–΄ DACMCP4922 (12λΉ„νŠΈ, 2채널)DAC80508 (16λΉ„νŠΈ, 8채널)
ν—€λ“œ 개수4개, 독립 μ£Όμ†Œμ§€μ •1개
ν΄λΌμ΄μ–ΈνŠΈ ν˜Έν™˜μ„±μžμ²΄ 파이썬 μ„œλ²„ μ „μš©ν‘œμ€€ Ether Dream μƒνƒœκ³„(MadMapper, Liberation λ“±)
μΆ”κ°€ κΈ°λŠ₯BLE λͺ¨μ…˜ μ™„λ“œ, sACN/DMX 동기화, μ˜€λ””μ˜€λ°˜μ‘Β·μƒμ„±ν˜• λΉ„μ£Όμ–Ό λͺ¨λ“œμ›Ή λŒ€μ‹œλ³΄λ“œ, OTA μ—…λ°μ΄νŠΈ, WiFi 폴백
배포 λͺ¨λΈκ³ μ • λ©€ν‹°λ…Έλ“œ μ„€μΉ˜λ¬Όμ†Œν”„νŠΈμ›¨μ–΄ 무관 단일 λ ˆμ΄μ € ν—€λ“œ

Q&A

Q. TCP의 μ‹ λ’°μ„± 보μž₯이 μžˆλŠ”λ° μ™œ UDPλ₯Ό μΌλ‚˜? A. λ ˆμ΄μ € ν”„λ ˆμž„ λ°μ΄ν„°λŠ” 본질적으둜 "버렀도 λ˜λŠ”" 데이터닀 β€” μ†μ‹€λœ ν”„λ ˆμž„μ€ μž¬μ „μ†‘ν•  게 μ•„λ‹ˆλΌ κ±΄λ„ˆλ›°μ–΄μ•Ό ν•œλ‹€. μž¬μ „μ†‘μ΄ 도착할 즈음이면 이미 κ·Έ ν”„λ ˆμž„μ„ ν‘œμ‹œν•  타이밍은 μ§€λ‚˜κ°€ 있기 λ•Œλ¬Έμ΄λ‹€. UDP의 λ°œμ‚¬ ν›„ 망각(fire-and-forget) 방식에 손싀 감지λ₯Ό μœ„ν•œ κ°„λ‹¨ν•œ μ‹œν€€μŠ€ λ„˜λ²„ 체크λ₯Ό λ”ν•œ 쑰합이 μ •ν™•νžˆ λ§žλŠ” 선택이고, TCP의 μž¬μ „μ†‘Β·μˆœμ„œλ³΄μž₯은 μ—¬κΈ°μ„  μ§€μ—°λ§Œ 늘릴 뿐 μ–»λŠ” 게 μ—†λ‹€.

Q. μ»€μŠ€ν…€ PCB의 λ² μ–΄μΉ© λŒ€μ‹  W6100μ΄λ‚˜ W5500 λͺ¨λ“ˆ λ³΄λ“œλ‘œλ„ λŒμ•„κ°ˆκΉŒ? A. κΈ°λŠ₯μ μœΌλ‘œλŠ” κ·Έλ ‡λ‹€ β€” μ‚¬μš©λœ μ†ŒμΌ“ API(socket(), getSn_RX_RSR(), recvfrom())λŠ” WIZnet W5x00 계열 μ „μ²΄μ—μ„œ λ™μΌν•˜λ‹€. 이 ν”„λ‘œμ νŠΈκ°€ μ»€μŠ€ν…€ PCBλ₯Ό νƒν•œ μ΄μœ λŠ” W5500 자체의 특수 μš”κ΅¬μ‚¬ν•­μ΄λΌκΈ°λ³΄λ‹€ RP2040Β·DAC와 ν•œ λ³΄λ“œμ— λ‚˜λž€νžˆ λ°°μΉ˜ν•˜κ³  μ‹Άμ—ˆλ˜ 섀계 μ˜λ„λ‘œ 보인닀.

Q. 3초 νƒ€μž„μ•„μ›ƒμ€ μ•ˆμ „μ„± μΈ‘λ©΄μ—μ„œ μ–΄λ–€ 역할을 ν•˜λ‚˜? A. core0이 3μ΄ˆκ°„(LASER_TIMEOUT) νμ—μ„œ μœ νš¨ν•œ 포인트λ₯Ό λ°›μ§€ λͺ»ν•˜λ©΄ λ ˆμ΄μ € 좜λ ₯을 κ°•μ œλ‘œ 0으둜 λ§Œλ“ λ‹€ β€” λ„€νŠΈμ›Œν¬ 문제둜 빔이 ν•œ 점에 κ³ μ •λœ 채(μœ„ν—˜ν•  수 μžˆλŠ” μƒνƒœλ‘œ) λ°©μΉ˜λ˜λŠ” κ±Έ λ§‰λŠ” 합리적인 μ•ˆμ „μž₯μΉ˜λ‹€.

Comments

Comments