Skip to content

warp-core

warp-core: A Hybrid Rocket Launch Controller Built on an 8-bit AVR and the W5500

scott

Published October 20, 2026

Original author: qhandley (Oregon State University hybrid rocket team)Original source (new tab)

warp-core

Components

Hardware components

WIZnet parts

Project description

Summary

warp-core is the launch control firmware that an ECE capstone team built for the Oregon State University (OSU) hybrid rocket team. It receives JSON commands from a mission control PC over the hardware TCP socket of the WIZnet W5500 and hands them to FreeRTOS tasks running on an ATmega1284p. It is a useful reference because it shows that an 8-bit MCU with 16 KB of SRAM can run a remote control link in a small amount of code, with no TCP/IP stack of its own.

OSU hybrid rocket team 2020 mission patch

Overview

A hybrid rocket uses a solid fuel together with a liquid or gaseous oxidizer. The oxidizer is usually loaded at the pad shortly before launch. Once filling starts, people must stay far from the rocket and operate the valves and ignition remotely.

warp-core is the pad-side brain of that remote operation. Its README names two challenges unique to hybrid rockets: pre-flight remote oxidizer filling and ignition timing matched to oxidizer injection.

  • Developers: Quinn Handley, Thomas Prihoda
  • Timeline: first commit 2019-10-22, last commit 2020-03-08, 48 commits
  • Environment: custom AVR controller, FreeRTOS AVR port, avr-gcc 5.4.0

→ GitHub: https://github.com/qhandley/warp-core

Why Remote Control Is Required

How the Repository Was Actually Used

  • Confirmed: According to the capstone page, the subscale system (T3st Core) supported many fuel grain tests
  • Full-scale launch not completed: The phase 3 full-scale launch was postponed due to COVID-19. Spaceport America Cup 2020, planned for the same year, was also canceled in March 2020
  • Activity: 1 star, 1 fork. The repository was created for a university capstone and has not been updated since

→ Capstone project page: https://sites.google.com/oregonstate.edu/ece44x201932/home
→ Competition cancellation notice (ESRA, 2020-03-24, archived copy): http://web.archive.org/web/20250914092355/https://www.soundingrocket.org/news1/2020-spaceport-america-cup-cancelled

What the Competition Rules Require

The Design, Test & Evaluation Guide (DTEG) from ESRA, which runs the student rocketry competition, sets these requirements for hybrid propulsion and launch equipment.

Section Requirement
5.2.2.3 Nitrous oxide (N₂O) is the only allowed hybrid oxidizer
5.10.1 Hybrid and liquid systems must be able to remotely vent all propellants
11.3.4 Hybrid launch controllers must include oxidizer fill level instrumentation
11.5.1 Launch control systems must be single fault tolerant (key or jumper interlock)
11.5.3 Hybrid launch controllers must automatically vent oxidizer if power or communication is lost during fill

In other words, remote operation itself is mandatory, and what happens when the link drops is an explicit requirement. We come back to this in "Limitations and Future Improvements."

The edition cited here is the 2024 revision (Rev 2024 V1.1.3). We could not confirm the section numbers or wording of the edition in force in 2020, when warp-core was written.

→ ESRA IREC DTEG (2024 V1.1.3): https://www.soundingrocket.org/uploads/9/0/6/4/9064598/2025_irec_dteg_v1.1.3_10-22-24.pdf

Architecture

The full system designed by the capstone team consists of an MCU board, FPGA board, I/O board, power board, and a C# GUI. The warp-core repository contains the firmware for the MCU board.

warp-core system architecture: from the mission control PC over TCP to the W5500, over SPI to the ATmega1284p, through two FreeRTOS tasks and a queue, then below the dashed line (capstone design only) to the FPGA board, I/O board, and valves, igniter and sensors

  • Covered by the code: mission control → W5500 → TCP task → queue → control task
  • Described only on the capstone page: the FPGA gateway between the MCU and the I/O board with its failsafe, the RS232 link to the rocket avionics, and the C# GUI

Technology Background

Filling and Igniting a Hybrid Rocket

A hybrid rocket stores its fuel (a solid grain) and its oxidizer (a fluid such as N₂O) separately. Combustion starts only when the oxidizer flows into the combustion chamber. Unlike a solid motor, the oxidizer valve can start and stop combustion.

  • Fill: N₂O flows from a supply tank at the pad into the rocket's tank. From this point the rocket carries a pressure vessel
  • Vent: If the launch is scrubbed, the oxidizer must be dumped remotely (DTEG 5.10.1)
  • Ignite: The igniter heats the fuel grain surface, then the main oxidizer valve opens. The order and spacing of these two steps decide whether ignition is clean

This is why warp-core lists "ignition timing matched to oxidizer injection" as a core challenge. Every command from mission control acts on high-pressure oxidizer, so the reliability of the command link is a safety issue.

FreeRTOS Tasks and Queues

FreeRTOS is a real-time kernel for small MCUs. A program is split into tasks, and the kernel shares the CPU among them by priority. Tasks exchange data through queues, which decouple the sender from the receiver in time.

warp-core uses this structure directly.

  • vTCPServerTask (priority +2): handles only the network. It wakes every 10 ticks (100 ms at a 100 Hz tick) through vTaskDelayUntil() and checks the socket
  • vControlTask (priority +1): only takes commands from the queue and drives the output
  • xControlCmdQueue: a 10-entry queue that connects the two tasks

The benefit of this split is that the control task's code path stays the same even when the network side slows down or reconnects.

→ FreeRTOS queue documentation: https://www.freertos.org/Documentation/02-Kernel/02-Kernel-features/02-Queues-mutexes-and-semaphores/01-Queues

Knowing When a TCP Connection Is Gone

TCP is connection-oriented, but it does not always tell you that the peer has disappeared.

Situation Does the server know?
The mission control program exits normally Yes — the peer sends a FIN to close the connection
The mission control PC loses power, or the cable is cut No — no packet arrives, so the connection still looks alive

There are two ways to catch the second case.

  • TCP keep-alive: after a period with no data, send an empty probe to ask whether the peer is still there (RFC 1122)
  • Application-level heartbeat: mission control sends an "alive" message at a fixed interval, and the pad side enters a safe state if it misses them for too long

To meet the "automatic venting on communication loss" rule in DTEG 11.5.3 in software, you need at least one of these.

→ RFC 1122 (4.2.3.6 TCP Keep-Alives): https://www.rfc-editor.org/rfc/rfc1122

Technical Highlights

  • Communication and control are separated: the TCP task only receives and parses, and the control task on the other side of the queue drives the output. Communication (+2) runs at a higher priority than control (+1)
  • A command is a single integer: from {"cmd": 5}, only the value of the cmd key is extracted and converted with atoi(). The parser is jsmn (MIT license), a single header file that uses no dynamic memory
  • Bare-metal port without Arduino: the firmware brings in WIZnet ioLibrary and fills in the hardware-dependent functions in wizchip_conf.c — SPI chip select and byte read/write — with AVR registers. Burst read/write calls these byte functions in a loop
  • Debug output: the _TCP_DEBUG_ macro prints received data, the chip version, and the IP settings to USART1
  • Signs of iterative development: the commit log includes "Fixed memory bug causing reset loop" and "Protected wizchip recv with critical section," and the README FAQ explains how to check RTOS reset causes (stack overflow, heap settings, watchdog)

Where WIZnet Fits

TOE in Use: Yes

warp-core uses the W5500 as a hardware TCP/IP offload engine (TOE). The evidence is in the code.

  • socket(sn, Sn_MR_TCP, tcpPORT, 0x00) — opens an ioLibrary hardware socket in TCP mode
  • getSn_SR() reads the socket status register inside the chip to drive the state machine
  • The firmware build (the source list in the makefile) does not include a software TCP/IP stack such as lwIP. The repository does contain the demo lwIP and uIP sources that ship with FreeRTOS, but they are not built

Configuration

Item Value Location
Chip W5500 (_WIZCHIP_ W5500) lib/Ethernet/wizchip_conf.h
Sockets used Socket 0 only tcp_server.c
Socket buffers TX 16 KB, RX 16 KB, all assigned to socket 0 tcp_server.c
Address Static IP 10.0.0.10, gateway 10.0.0.1 tcp_server.h
Port 8080 tcp_server.h
SPI chip select PB3 wizchip_conf.c

The W5500's 8 sockets share an internal buffer of 16 KB TX and 16 KB RX. Because warp-core accepts only one connection, it gives the entire buffer to socket 0.

→ W5500 datasheet: https://docs.wiznet.io/Product/Chip/Ethernet/W5500/datasheet

How One Command Is Processed

  1. Open the socket — if socket 0 is closed (SOCK_CLOSED), the MCU opens it in TCP mode with socket(). When the chip reaches SOCK_INIT, the MCU calls listen()
  2. Connect — when the mission control PC connects to 10.0.0.10:8080, the W5500 handles the SYN/SYN-ACK/ACK handshake and the state changes to SOCK_ESTABLISHED. The MCU sees this when it reads the status register every 100 ms
  3. Receive — when the PC sends {"cmd": 3}, the W5500 receives the TCP segment, returns the ACK, and stores the payload in its internal RX buffer
  4. Fetch — the MCU reads the number of waiting bytes with getSn_RX_RSR(0) and pulls up to 64 bytes over SPI with recv()
  5. Parse and forward — the MCU extracts the cmd value 3 with jsmn and passes it to the control task with xQueueSend()
  6. Close — when mission control closes the connection, the W5500 receives the FIN and moves to SOCK_CLOSE_WAIT. The MCU finishes with disconnect() and returns to step 1

What the MCU does not do: ARP replies, the TCP handshake, ACKs, retransmission, buffering received data
What the MCU does: check the status register, read 64 bytes, parse JSON, forward to the queue

Analogy: The Radio Operator at the Pad

The W5500 is the radio operator in the pad bunker. Tuning the radio, answering calls, and asking for repeats when a message is garbled are all the operator's job. The commander (ATmega1284p) only asks the operator every 100 ms, "Any orders?", reads the one line the operator wrote down, and hands it to a subordinate (the control task). The commander never needs to learn radio procedure.

What If the Chip Were Not There [Inferred]

The ATmega1284p has 16 KB of SRAM. warp-core reserves 10,000 bytes of it as the FreeRTOS heap and allocates 2,048 (bytes, in the AVR port) from that heap for the TCP task stack.

  • With a MAC/PHY-only Ethernet chip and a software TCP/IP stack (uIP, lwIP, etc.), packet buffers, connection state, and retransmission timers compete with the RTOS for the same SRAM
  • The MCU would have to keep retransmission timing, so TCP handling would be tied to task scheduling
  • In the current design, received data stays in the chip's 16 KB buffer, and the MCU uses a single 64-byte array for incoming data

Similar Projects on WIZnet Makers

Several university rocket teams already use the W5500 in their ground equipment. They solve the same problem in different ways, which makes them worth reading side by side.

  • Hybrid PCC — the hybrid rocket pad control box from the CU InSpace team at Carleton University in Canada. It is also hybrid rocket ground control, but it is built on an RP2040 + W5500-EVB-Pico board, in contrast to warp-core's custom PCB
    → Project link: https://maker.wiznet.io/Sunny_/projects/hybrid-pcc/
  • phoenix-gs (Phoenix Ground Station) — the liquid rocket ground station from RPL at UC San Diego. Like warp-core, it sends valve commands and receives sensor data. But it opens the W5500 in MACRAW mode and talks in custom Layer-2 frames with no TCP/IP, the opposite choice to warp-core's TOE approach
    → Project link: https://maker.wiznet.io/matthew/projects/phoenix-gs-phoenix-ground-station/
  • PSPL CMS — the liquid rocket avionics from the Purdue Space Program Liquids team. Built on RP2040 + W5500, it splits commands into TCP request-response (CommandNet) and sensor data into UDP multicast (SensorNet). Commands get a reply, which is the difference from warp-core (Hybrid PCC also reports command results by design)
    → Project link: https://maker.wiznet.io/Aimee0/projects/cms--rocket-developed-by-the-purdue-space-program-liquids-team/

All four projects build their wired Ethernet segment with a W5500 attached over SPI. Hybrid PCC, however, relies on a separate wireless bridge for the long link between mission control and the pad, and PSPL CMS uses Ethernet as a backbone between the rocket's flight computers and ground equipment. What differs is how much of the chip each one uses.

Item warp-core (OSU) Hybrid PCC (Carleton) phoenix-gs (UCSD) PSPL CMS (Purdue)
Propulsion Hybrid Hybrid Liquid Liquid
MCU ATmega1284p (8-bit) RP2040 Arduino-compatible (model unknown) RP2040
W5500 mode TOE Unknown MACRAW Unknown
Command transport TCP, JSON Unknown Layer-2 frame, 1 byte TCP, MessagePack
Command reply No Yes (reports results by design) No Yes (request-response)
RTOS FreeRTOS Apache NuttX Unknown FreeRTOS

Of these, warp-core is the only one confirmed to run on an 8-bit MCU, and it leans on the chip's TCP engine to build its remote control link. Placed next to phoenix-gs, which builds raw frames over MACRAW, it shows clearly that the same chip can serve as either "a finished TCP socket" or "a bare Ethernet port."

Use Cases

  • Remote valve and ignition links for student rocket teams and engine test stands — a minimal skeleton that connects mission control and the pad with a single wired TCP link. The communication-loss handling described under "Limitations" must be added first
  • Adding wired remote control to 8-bit equipment — an example of attaching one W5500 over SPI to existing AVR-based equipment to create a TCP command port
  • Porting ioLibrary to bare-metal AVR — a minimal port that changes only the chip select and SPI read/write functions in wizchip_conf.c, without the Arduino library
  • Practicing the network/control split on an RTOS — the structure that links a communication task and a control task through a queue fits in about 260 lines of non-comment code (tcp_server, control), which makes it good teaching material

Limitations and Future Improvements

In roughly one capstone cycle, warp-core got a hardware socket state machine, RTOS task separation, and JSON command parsing working. When the full-scale launch stopped because of COVID, some parts were left unpolished. Teams that want to build on it should address the following first.

1. Communication Loss Goes Undetected — The Most Important Improvement

  • Today: keep-alive is not configured, and there is no application-level heartbeat. If the mission control PC shuts off or a cable is pulled, no FIN arrives, so the socket stays in SOCK_ESTABLISHED
  • Why it matters: DTEG 11.5.3 requires automatic oxidizer venting on communication loss during fill. If the firmware cannot detect the loss, it cannot meet that rule in software (we could not confirm whether the FPGA failsafe in the capstone design covered this)
  • Improvements:
    • Use the W5500 keep-alive — the W5500 has a per-socket keep-alive timer register, Sn_KPALVTR. Writing a value above 0 (in 5-second units) makes the chip send keep-alive packets on its own. If the peer does not respond, the chip drops the connection and raises a timeout interrupt. The MCU only needs to watch getSn_SR() change to SOCK_CLOSED. Setup is one line: setSn_KPALVTR(). Note that keep-alive starts only after data has been sent or received at least once following SOCK_ESTABLISHED, so this alone cannot catch a loss right after connecting, before any command arrives
    • Application-level heartbeat — mission control sends, for example, {"cmd": 0} every second. The control task uses a queue timeout and, if no command arrives within the limit, enters a safe state that opens the vent valve. This can detect a loss faster than keep-alive

→ W5500 datasheet PDF v1.1.0 (Sn_KPALVTR, p. 56): https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf

2. Commands Get No Reply

  • Today: the server code never calls send(). Mission control cannot tell whether a command arrived or ran
  • Improvement: send back the result and the current valve state. The request-response structure in PSPL CMS is a good reference

3. The Control Task Is Still a Skeleton

  • Today: it toggles an LED regardless of the command value. The real valve and ignition outputs are not in the repository
  • Improvement: add a per-command output map, an interlock check before ignition, and sequence validation (fill → ignite) to the control task

4. Small Code Defects

  • Received string boundary: if exactly 64 bytes arrive, there is no NUL terminator, so strlen() can read past the buffer. Sizing the buffer at 65 bytes and keeping the last byte 0 fixes it
  • Queue type mismatch: an int8_t command goes into a queue sized for BaseType_t, and the receiver reads it into a BaseType_t * variable. Making the three types match is safer
  • Busy waiting: the control task polls the queue with a timeout of 0, leaving no time for lower-priority tasks. Giving the queue a wait time lets the CPU rest, and the same timeout can serve as the heartbeat monitor

5. Operational Notes

  • One client, static IP, no authentication — the design assumes a closed launch site network
  • The repository has no LICENSE file. Check with the authors before reuse (the bundled jsmn and the FreeRTOS kernel are each MIT-licensed)

FAQ

Q1. Can it be moved to an MCU other than the ATmega1284p?
The README says to first check whether a FreeRTOS port exists for the target MCU. On the W5500 side, only four ioLibrary functions (chip select and SPI read/write) need to be rewritten for the new MCU, so most of the porting effort is on the RTOS side.

Q2. Why TCP instead of UDP?
The repository does not say. [Inferred] Valve and ignition commands must not be lost but are small in volume, so TCP, with retransmission and ordering handled by the chip, was probably the simpler choice. For high-volume data where only the latest value matters, such as sensor readings, some designs add a separate UDP channel, as PSPL CMS does.

Q3. Can several mission control PCs connect at once?
The current code uses only socket 0. The W5500 has 8 hardware sockets, so calling listen() on the same port for each socket would accept multiple clients. The 16 KB buffers would need to be split across those sockets, though, and you first need logic that decides who holds command authority.

Q4. What does it add to the parts list?
On the MCU board, roughly a W5500, a 25 MHz crystal, and an RJ45 jack with integrated magnetics (MagJack). [Inferred] The README links to the OSU team's schematic on Google Drive for the actual parts, but the link would not open as of October 2026, so we could not confirm them.



요약

warp-core는 오리건주립대(OSU) 하이브리드 로켓 팀을 위해 ECE 캡스톤 팀이 만든 발사 제어 펌웨어입니다. 관제 PC가 보낸 JSON 명령을 WIZnet W5500의 하드웨어 TCP 소켓으로 받아, ATmega1284p 위의 FreeRTOS 태스크로 넘깁니다. SRAM 16KB짜리 8비트 MCU에서도 TCP/IP 스택 없이 원격 제어 링크를 짧은 코드로 구성할 수 있다는 점에서 참고할 만한 사례입니다.

OSU 하이브리드 로켓 팀 2020 미션 패치

개요

하이브리드 로켓은 고체 연료와 액체·기체 산화제를 함께 씁니다. 산화제는 보통 발사 직전 발사대에서 충전하므로, 충전이 시작되면 사람은 로켓에서 멀리 떨어진 채로 밸브와 점화를 조작해야 합니다.

warp-core는 이 원격 조작의 발사대 쪽 두뇌입니다. README는 이 프로그램이 다루는 하이브리드 로켓 고유의 과제로 발사 전 원격 산화제 충전과 산화제 주입에 맞춘 점화 타이밍을 꼽습니다.

  • 개발: Quinn Handley, Thomas Prihoda
  • 기간: 2019-10-22 첫 커밋 ~ 2020-03-08 마지막 커밋, 커밋 48개
  • 환경: 맞춤 제작한 AVR 컨트롤러, FreeRTOS AVR 포트, avr-gcc 5.4.0

→ GitHub: https://github.com/qhandley/warp-core

왜 원격 제어가 필요한가

저장소의 실제 사용

  • 확인됨: 캡스톤 페이지에 따르면 서브스케일 시스템(T3st Core)이 연료 그레인 시험을 여러 차례 지원했습니다
  • 실물 크기 발사는 미완료: 3단계 실물 크기 발사는 COVID-19로 연기됐습니다. 같은 해 열릴 예정이던 Spaceport America Cup 2020도 2020년 3월 취소됐습니다
  • 활동 지표: Star 1, Fork 1. 대학 캡스톤 과제로 만들어진 저장소이며, 이후 업데이트는 없습니다

→ 캡스톤 프로젝트 페이지: https://sites.google.com/oregonstate.edu/ece44x201932/home
→ 대회 취소 공지(ESRA, 2020-03-24, 보관본): http://web.archive.org/web/20250914092355/https://www.soundingrocket.org/news1/2020-spaceport-america-cup-cancelled

대회 규정이 요구하는 것

학생 로켓 대회를 주관하는 ESRA의 설계·시험·평가 지침(DTEG)은 하이브리드 추진계와 발사 장비에 다음을 요구합니다.

조항 요구 내용
5.2.2.3 하이브리드 산화제는 아산화질소(N₂O)만 허용
5.10.1 하이브리드·액체 추진은 모든 추진제를 원격으로 배출(venting) 할 수단을 갖출 것
11.3.4 하이브리드 발사 제어기는 산화제 충전량 계측을 포함할 것
11.5.1 발사 제어 시스템은 단일 고장 허용(키·점퍼 인터록)일 것
11.5.3 충전 중 전원 또는 통신이 끊기면 산화제를 자동 배출하는 페일세이프를 갖출 것

즉 원격 조작 자체가 규정상 필수이고, 특히 통신 링크가 끊겼을 때 어떻게 되는가가 명시적 요구사항입니다. 이 점은 아래 「한계와 개선 방향」에서 다시 다룹니다.

인용한 판본은 2024년판(Rev 2024 V1.1.3) 이며, warp-core가 개발된 2020년 당시 판본의 조항 번호와 문구는 확인하지 못했습니다.

→ ESRA IREC DTEG (2024 V1.1.3): https://www.soundingrocket.org/uploads/9/0/6/4/9064598/2025_irec_dteg_v1.1.3_10-22-24.pdf

시스템 구성

캡스톤 팀이 설계한 전체 시스템은 MCU 보드, FPGA 보드, I/O 보드, 전원 보드, C# GUI로 이뤄집니다. 그중 warp-core 저장소가 담고 있는 것은 MCU 보드의 펌웨어입니다.

warp-core 시스템 구성도: 관제 PC에서 TCP로 W5500, SPI로 ATmega1284p, FreeRTOS 두 태스크와 큐를 거쳐, 점선 아래 캡스톤 설계 영역의 FPGA 보드, I/O 보드, 밸브·점화기·센서로 이어지는 흐름

  • 코드로 확인되는 범위: 관제 → W5500 → TCP 태스크 → 큐 → 제어 태스크까지
  • 캡스톤 페이지에만 있는 범위: MCU와 I/O 보드 사이의 FPGA 게이트웨이와 페일세이프, 로켓 아비오닉스와의 RS232 연결, C# GUI

기술 배경

하이브리드 로켓의 충전과 점화

하이브리드 로켓은 연료(고체 그레인)와 산화제(N₂O 등 유체)를 따로 보관하다가, 산화제를 연소실로 흘려보낼 때 비로소 연소가 시작됩니다. 고체 모터와 달리 산화제 밸브로 연소를 시작하고 멈출 수 있다는 것이 핵심입니다.

  • 충전: 발사대의 공급 탱크에서 로켓 탱크로 N₂O를 채웁니다. 이때부터 로켓은 고압 용기를 실은 상태가 됩니다
  • 배출: 발사를 취소하면 탱크의 산화제를 원격으로 내보내야 합니다(DTEG 5.10.1)
  • 점화: 점화기로 연료 그레인 표면을 달군 뒤 주 산화제 밸브를 연다. 두 동작의 순서와 간격이 맞아야 정상 점화가 됩니다

warp-core가 「산화제 주입에 맞춘 점화 타이밍」을 과제로 꼽는 이유가 여기에 있습니다. 관제 쪽 명령 하나하나가 고압 산화제를 다루는 동작이므로, 명령 링크의 신뢰성이 곧 안전 문제가 됩니다.

FreeRTOS 태스크와 큐

FreeRTOS는 소형 MCU용 실시간 커널입니다. 프로그램을 여러 태스크로 나누고, 커널이 우선순위에 따라 CPU를 나눠 줍니다. 태스크끼리는 큐로 데이터를 주고받으며, 큐는 보내는 쪽과 받는 쪽을 시간적으로 분리합니다.

warp-core는 이 구조를 그대로 씁니다.

  • vTCPServerTask (우선순위 +2): 네트워크만 담당. vTaskDelayUntil()로 10 tick(100 Hz tick 기준 100 ms) 마다 깨어나 소켓을 확인합니다
  • vControlTask (우선순위 +1): 큐에서 명령을 꺼내 출력만 담당합니다
  • xControlCmdQueue: 길이 10의 큐로 두 태스크를 잇습니다

네트워크 쪽이 느려지거나 재접속을 하더라도 제어 태스크의 코드 경로는 바뀌지 않는다는 것이 이 분리의 장점입니다.

→ FreeRTOS 큐 문서: https://www.freertos.org/Documentation/02-Kernel/02-Kernel-features/02-Queues-mutexes-and-semaphores/01-Queues

TCP 연결이 끊긴 것을 아는 방법

TCP는 연결형 프로토콜이지만, 상대가 사라졌다는 사실을 늘 알려주지는 않습니다.

상황 서버가 아는가
관제 프로그램이 정상 종료 안다 — 상대가 FIN을 보내 연결 종료를 알림
관제 PC가 갑자기 꺼짐, 케이블 단선 모른다 — 아무 패킷도 오지 않으므로 연결이 살아 있는 것처럼 보임

이 두 번째 경우를 잡는 방법은 두 가지입니다.

  • TCP keep-alive: 일정 시간 데이터가 없으면 빈 확인 패킷을 보내 상대가 살아 있는지 묻는 기능입니다(RFC 1122)
  • 앱 레벨 하트비트: 관제 쪽이 주기적으로 "살아 있음" 메시지를 보내고, 발사대 쪽은 일정 시간 못 받으면 안전 동작으로 들어갑니다

DTEG 11.5.3의 「통신 두절 시 자동 배출」을 소프트웨어로 지키려면 이 중 하나 이상이 필요합니다.

→ RFC 1122 (4.2.3.6 TCP Keep-Alives): https://www.rfc-editor.org/rfc/rfc1122

기술 특징

  • 통신과 제어의 분리: TCP 태스크는 수신과 파싱까지만, 출력은 큐 너머의 제어 태스크가 맡습니다. 우선순위도 통신(+2)이 제어(+1)보다 높게 잡혀 있습니다
  • 명령 형식은 정수 하나: {"cmd": 5} 처럼 cmd 키의 값만 꺼내 atoi()로 변환합니다. 파서는 헤더 파일 하나로 된 jsmn(MIT 라이선스)으로, 동적 메모리를 쓰지 않습니다
  • Arduino 없는 bare-metal 이식: WIZnet ioLibrary를 가져와 wizchip_conf.c 의 하드웨어 의존 함수인 SPI 칩 선택·바이트 읽기·쓰기를 AVR 레지스터로 채웠습니다(버스트 읽기·쓰기는 이 바이트 함수를 반복 호출합니다)
  • 디버그 출력: _TCP_DEBUG_ 매크로로 USART1에 수신 내용, 칩 버전, IP 설정을 찍습니다
  • 반복 개발의 흔적: 커밋 기록에 「Fixed memory bug causing reset loop」, 「Protected wizchip recv with critical section」이 남아 있고, README FAQ에도 RTOS 리셋 원인(스택 오버플로, 힙 설정, 워치독)을 점검하는 법을 적어 두었습니다

W5500이 맡은 일

TOE 사용 판정: 예

warp-core는 W5500을 하드웨어 TCP/IP 오프로드(TOE) 칩으로 씁니다. 근거는 코드에 그대로 있습니다.

  • socket(sn, Sn_MR_TCP, tcpPORT, 0x00) — ioLibrary의 하드웨어 소켓을 TCP 모드로 엽니다
  • getSn_SR() 로 칩 내부의 소켓 상태 레지스터를 읽어 상태머신을 돌립니다
  • 펌웨어 빌드(makefile의 소스 목록)에 lwIP 같은 소프트웨어 TCP/IP 스택이 들어가지 않습니다. 저장소에는 FreeRTOS 배포본의 데모용 lwIP·uIP 소스가 함께 들어 있지만 빌드에 쓰이지 않습니다

설정값

항목 값 위치
칩 W5500 (_WIZCHIP_ W5500) lib/Ethernet/wizchip_conf.h
사용 소켓 소켓 0 하나 tcp_server.c
소켓 버퍼 TX 16KB, RX 16KB 모두 소켓 0에 할당 tcp_server.c
주소 고정 IP 10.0.0.10, 게이트웨이 10.0.0.1 tcp_server.h
포트 8080 tcp_server.h
SPI 칩 선택 PB3 wizchip_conf.c

W5500은 소켓 8개가 TX 16KB, RX 16KB의 내부 버퍼를 나눠 씁니다. warp-core는 연결을 하나만 받으므로 버퍼 전체를 소켓 0에 몰아준 설정입니다.

→ W5500 데이터시트: https://docs.wiznet.io/Product/Chip/Ethernet/W5500/datasheet

명령 하나가 처리되는 순서

  1. 소켓 열기 — 소켓 0이 닫혀 있으면(SOCK_CLOSED) MCU가 socket()으로 TCP 모드로 엽니다. 칩이 SOCK_INIT이 되면 listen()을 호출합니다
  2. 접속 — 관제 PC가 10.0.0.10:8080으로 접속하면 SYN/SYN-ACK/ACK 핸드셰이크를 W5500이 처리하고, 상태가 SOCK_ESTABLISHED로 바뀝니다. MCU는 100 ms마다 상태 레지스터를 읽다가 이를 확인합니다
  3. 수신 — 관제 PC가 {"cmd": 3}을 보내면 W5500이 TCP 세그먼트를 받아 ACK를 돌려주고 페이로드를 내부 RX 버퍼에 쌓습니다
  4. 꺼내기 — MCU가 getSn_RX_RSR(0)으로 쌓인 바이트 수를 읽고, recv()로 최대 64바이트를 SPI로 가져옵니다
  5. 파싱과 전달 — MCU가 jsmn으로 cmd 값 3을 꺼내 xQueueSend()로 제어 태스크에 넘깁니다
  6. 종료 — 관제 쪽이 연결을 닫으면 W5500이 FIN을 받아 SOCK_CLOSE_WAIT로 바뀌고, MCU가 disconnect()로 마무리한 뒤 1번으로 돌아갑니다

MCU가 하지 않는 일: ARP 응답, TCP 핸드셰이크, ACK, 재전송, 수신 데이터 버퍼링
MCU가 하는 일: 상태 레지스터 확인, 64바이트 읽기, JSON 파싱, 큐 전달

비유: 발사대의 통신병

W5500은 발사대 벙커의 통신병입니다. 무전기 주파수를 맞추고, 호출에 응답하고, 잘 안 들린 말은 다시 불러 달라고 하는 일은 모두 통신병이 합니다. 지휘관(ATmega1284p)은 100 ms마다 통신병에게 "온 명령 있나?"라고만 묻고, 받아 적은 명령문 한 줄만 읽어 부하(제어 태스크)에게 넘깁니다. 지휘관은 무전 절차를 몰라도 됩니다.

만약 이 칩이 없었다면 [추정]

ATmega1284p의 SRAM은 16KB이고, warp-core는 그중 10,000바이트를 FreeRTOS 힙으로 잡고, 그 힙에서 TCP 태스크 스택으로 2,048(AVR 포트에서는 바이트 단위)을 받습니다.

  • MAC/PHY만 있는 이더넷 칩에 소프트웨어 TCP/IP 스택(uIP, lwIP 등)을 올리면, 패킷 버퍼·연결 상태·재전송 타이머가 같은 SRAM을 두고 RTOS와 경쟁합니다
  • 재전송 타이밍을 MCU가 지켜야 하므로 TCP 처리가 태스크 스케줄링에 묶입니다
  • 현재 구조에서는 수신 데이터가 칩 안의 16KB 버퍼에 머물고, MCU는 수신 데이터용으로 64바이트 배열 하나만 씁니다

WIZnet Makers 유사 사례

대학 로켓 팀의 지상 장비에서 W5500을 쓴 사례가 이미 여럿 있습니다. 같은 문제를 서로 다른 방식으로 풀었기 때문에 비교하며 읽기 좋습니다.

  • Hybrid PCC — 캐나다 칼턴대 CU InSpace 팀의 하이브리드 로켓 발사대 제어 상자입니다. 같은 하이브리드 로켓 지상 제어이지만, RP2040 + W5500-EVB-Pico 보드로 구성해 맞춤 PCB를 만든 warp-core와 대비됩니다
    → 프로젝트 링크: https://maker.wiznet.io/Sunny_/projects/hybrid-pcc/
  • phoenix-gs (Phoenix Ground Station) — UC 샌디에이고 RPL의 액체 로켓 지상국입니다. 밸브 명령을 보내고 센서 값을 받는 점은 같지만, W5500을 MACRAW 모드로 열어 TCP/IP 없이 커스텀 Layer-2 프레임으로 통신합니다. warp-core의 TOE 방식과 정반대 선택입니다
    → 프로젝트 링크: https://maker.wiznet.io/matthew/projects/phoenix-gs-phoenix-ground-station/
  • PSPL CMS — 퍼듀대 Purdue Space Program Liquids 팀의 액체 로켓 아비오닉스입니다. RP2040 + W5500으로, 명령은 TCP 요청-응답(CommandNet), 센서는 UDP 멀티캐스트(SensorNet) 로 나눴습니다. 명령에 응답이 돌아온다는 점이 warp-core와의 차이입니다(Hybrid PCC도 설계상 명령 처리 결과를 회신합니다)
    → 프로젝트 링크: https://maker.wiznet.io/Aimee0/projects/cms--rocket-developed-by-the-purdue-space-program-liquids-team/

네 사례 모두 SPI로 붙는 W5500으로 유선 Ethernet 구간을 만들었습니다. 다만 Hybrid PCC는 관제와 발사대 사이의 장거리 구간을 별도 무선 브리지가 맡고, PSPL CMS의 Ethernet은 로켓 내부 비행 컴퓨터와 지상 장비를 잇는 백본입니다. 차이는 칩을 어디까지 쓰느냐에 있습니다.

항목 warp-core (OSU) Hybrid PCC (Carleton) phoenix-gs (UCSD) PSPL CMS (Purdue)
추진 방식 하이브리드 하이브리드 액체 액체
MCU ATmega1284p (8비트) RP2040 Arduino 호환(모델 미상) RP2040
W5500 모드 TOE 미상 MACRAW 미상
명령 전송 TCP, JSON 미상 Layer-2 프레임, 1바이트 TCP, MessagePack
명령 응답 없음 있음(설계상 처리 결과 회신) 없음 있음(요청-응답)
RTOS FreeRTOS Apache NuttX 미상 FreeRTOS

warp-core는 이 중 MCU가 8비트로 확인된 유일한 사례이며, 칩의 TCP 기능에 기대어 원격 제어 링크를 만든 사례입니다. MACRAW로 프레임을 직접 짜는 phoenix-gs와 나란히 놓으면, 같은 칩이 「완성된 TCP 소켓」과 「맨 이더넷 포트」 양쪽으로 쓰일 수 있다는 점이 잘 드러납니다.

어디에 쓰나

  • 학생 로켓 팀·엔진 시험대의 원격 밸브·점화 링크 — 관제와 발사대 사이를 유선 TCP 하나로 잇는 최소 골격으로 쓸 수 있습니다. 다만 아래 「한계」의 통신 두절 처리는 반드시 보강해야 합니다
  • 8비트 장비에 유선 원격 제어 추가 — 기존 AVR 기반 장비에 W5500 하나를 SPI로 붙여 TCP 명령 포트를 만드는 예제로 읽을 수 있습니다
  • ioLibrary를 bare-metal AVR에 이식할 때 — Arduino 라이브러리 없이 wizchip_conf.c 의 칩 선택·SPI 읽기·쓰기 함수만 고치는 최소 이식 예입니다
  • RTOS에서 네트워크와 제어를 나누는 연습 — 통신 태스크와 제어 태스크를 큐로 잇는 구조가 주석을 빼면 260줄 남짓(tcp_server, control)의 코드로 들어 있어 교육용으로 적당합니다

한계와 개선 방향

warp-core는 한 학기 남짓한 캡스톤 기간에 하드웨어 소켓 상태머신, RTOS 태스크 분리, JSON 명령 파싱까지 동작시킨 저장소입니다. 실물 발사가 COVID로 멈추면서 다듬어지지 못한 부분이 남아 있고, 이어받아 쓰려는 팀이라면 아래를 먼저 손보는 것이 좋습니다.

1. 통신 두절을 감지하지 못한다 — 가장 중요한 개선 지점

  • 현재: keep-alive를 설정하지 않았고, 앱 레벨 하트비트도 없습니다. 관제 PC가 꺼지거나 케이블이 빠지면 FIN이 오지 않으므로 소켓은 계속 SOCK_ESTABLISHED에 머뭅니다
  • 왜 중요한가: DTEG 11.5.3은 충전 중 통신 두절 시 산화제 자동 배출을 요구합니다. 펌웨어가 두절을 모르면 이 요구를 소프트웨어로 지킬 수 없습니다(캡스톤 설계의 FPGA 페일세이프가 이를 맡았는지는 확인하지 못했습니다)
  • 개선 방향:
    • W5500 keep-alive 사용 — W5500에는 소켓별 keep-alive 타이머 레지스터 Sn_KPALVTR 가 있습니다. 값을 0보다 크게 쓰면(단위 5초) 칩이 스스로 keep-alive 패킷을 보내고, 상대가 응답하지 않으면 연결을 끊고 타임아웃 인터럽트를 냅니다. MCU는 getSn_SR()이 SOCK_CLOSED로 바뀌는 것만 보면 됩니다. 설정은 setSn_KPALVTR() 한 줄입니다. 단, keep-alive는 SOCK_ESTABLISHED 이후 데이터를 한 번 이상 주고받은 뒤부터 나가므로, 접속 직후 아무 명령도 오지 않은 상태의 두절은 이것만으로 잡지 못합니다
    • 앱 레벨 하트비트 — 관제 쪽이 예를 들어 1초마다 {"cmd": 0}을 보내고, 제어 태스크가 큐 타임아웃으로 정해진 시간 동안 명령이 없으면 배출 밸브를 여는 안전 상태로 들어가게 합니다. keep-alive보다 감지 시간을 짧게 잡을 수 있습니다

→ W5500 데이터시트 PDF v1.1.0 (Sn_KPALVTR, 56쪽): https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf

2. 명령에 응답하지 않는다

  • 현재: 서버 코드에 send() 호출이 없습니다. 관제 쪽은 명령이 도착했는지, 실행됐는지 알 수 없습니다
  • 개선 방향: 처리 결과와 현재 밸브 상태를 돌려보내는 응답을 추가합니다. PSPL CMS의 요청-응답 구조가 참고가 됩니다

3. 제어 태스크가 아직 골격이다

  • 현재: 명령 값과 관계없이 LED를 토글합니다. 실제 밸브·점화 출력은 저장소에 없습니다
  • 개선 방향: 명령 번호별 출력 매핑, 점화 전 인터록 확인, 순서 검증(충전 → 점화)을 제어 태스크에 넣습니다

4. 작은 코드 결함

  • 수신 문자열 경계: 64바이트를 꽉 채워 받으면 NUL 종료 문자가 없어 strlen()이 버퍼 밖을 읽을 수 있습니다. 버퍼를 65바이트로 잡고 끝을 0으로 두면 됩니다
  • 큐 타입 불일치: int8_t 명령을 sizeof(BaseType_t) 크기 큐에 넣고, 받는 쪽은 BaseType_t * 변수로 받습니다. 세 곳의 타입을 맞추는 것이 안전합니다
  • 바쁜 대기: 제어 태스크가 타임아웃 0으로 큐를 계속 확인해 낮은 우선순위 태스크가 실행될 틈이 없습니다. 큐 대기 시간을 주면 CPU가 쉬고, 이 타임아웃을 하트비트 감시에 그대로 쓸 수 있습니다

5. 운영 측면

  • 클라이언트 1개, 고정 IP, 인증 없음 — 닫힌 발사장 네트워크를 전제로 한 설계입니다
  • 저장소에 LICENSE 파일이 없습니다. 재사용 전 저자에게 확인하는 것이 좋습니다(포함된 jsmn과 FreeRTOS 커널은 각각 MIT 라이선스)

FAQ

Q1. ATmega1284p가 아닌 다른 MCU로 옮길 수 있나요?
README는 먼저 대상 MCU용 FreeRTOS 포트가 있는지 확인하라고 안내합니다. W5500 쪽은 ioLibrary의 칩 선택·SPI 콜백 4개만 새 MCU에 맞게 다시 쓰면 되므로, 이식 부담은 주로 RTOS 쪽에 있습니다.

Q2. 왜 UDP가 아니라 TCP를 썼나요?
저장소에 이유는 적혀 있지 않습니다. [추정] 밸브·점화 명령은 유실되면 안 되는 반면 양이 적어서, 재전송과 순서 보장을 칩이 해 주는 TCP가 단순했을 것으로 보입니다. 센서처럼 양이 많고 최신 값만 중요한 데이터라면 PSPL CMS처럼 UDP를 따로 두는 구성도 있습니다.

Q3. 관제 PC를 여러 대 붙일 수 있나요?
현재 코드는 소켓 0 하나만 씁니다. W5500은 하드웨어 소켓이 8개이므로 소켓마다 같은 포트로 listen()하면 여러 클라이언트를 받을 수 있습니다. 다만 16KB 버퍼를 소켓 수만큼 나눠 다시 할당해야 하고, 명령 권한을 누가 갖는지 정하는 로직이 먼저 필요합니다.

Q4. 부품 구성에는 무엇이 더해지나요?
MCU 보드 기준으로 W5500, 25MHz 크리스털, 트랜스포머 내장 RJ45(MagJack) 정도가 추가됩니다. [추정] OSU 팀의 실제 부품 구성은 README에 회로도(Google Drive) 링크로 안내되어 있으나, 2026년 10월 현재 링크가 열리지 않아 확인하지 못했습니다.

Comments

Similar projects you might like

Comments