AR22 LCU: W5500 Ethernet TCP Dose Logging on a Dual-Range STM32F446 Radiation Detector
AR22 LCU v1.1 adds W5500 TCP logging to an STM32F446 gamma detector, streaming live dose-rate JSON without disturbing the core firmware.

WIZnet - W5500
SPI1 @12.5 MHz, CS=PA4, RST=PC4. Static IP 192.168.1.22:15022. Single-socket TCP server streams dose-rate JSON using WIZnet ioLibrary.
📌 Overview
kzeta23 publishes AR22 LCU v1.1 as a Korean-documented STM32F446 firmware repository for a dual-range gamma radiation detector. The public GitHub profile does not publish a name, country, or organization, so this post keeps those fields conservative and focuses on the firmware evidence.
AR22 LCU v1.1 adds a WIZnet W5500 Ethernet TCP logging path to a validated radiation detector firmware. The detector counts pulses from two Geiger-Mueller tube channels, calculates dose rate with adaptive filtering, drives a 256x64 SSD1322 OLED and alarm output, and then streams the current dose rate as one JSON line over TCP port 15022.
Generated technical illustration: AR22 LCU as a dose-rate instrument with W5500 TCP logging.
The repository is more than a small networking demo. It documents a real embedded instrument firmware pattern: preserve the measurement and alarm core, then add a self-contained W5500 layer behind ETH_LOG_ENABLE. With that macro set to 1, a TCP client can connect to 192.168.1.22:15022 and receive output such as {"val":1.23,"unit":"uSv/h"}. With the macro set to 0, the Ethernet code is removed from the build.
System Configuration
The main controller is an STM32F446VET6 running as a Cortex-M4 application at 100 MHz. The firmware uses hardware timers for pulse counting, SPI and DMA for the OLED display, I2C for EEPROM settings, GPIO/EXTI for the front-panel switches, TIM5 PWM for the buzzer, and SPI1 for the W5500 Ethernet controller.
The current code and CubeMX project show this GM channel mapping:
- LOW dose channel: LND 7128 class GM tube,
GM_COUNT_LO, PA0, TIM8_ETR. - HIGH dose channel: LND 71631 class GM tube,
GM_COUNT_HI, PE7, TIM1_ETR.
That detail matters because an older overview table in the repository still describes the channel mapping in the opposite direction. The firmware itself is clearer: HAL_TIM_PeriodElapsedCallback() reads the LOW count from TIM8 and the HIGH count from TIM1, and comments in the ISR state that the GM channels were swapped to match the current wiring and labels.
The display is an SSD1322 256x64 OLED on SPI4 with DMA. A 24C01 I2C EEPROM stores the alarm threshold and the LOW/HIGH conversion factors. The firmware also includes a power-on self-test for display, LEDs, buzzer, and EEPROM, plus watchdog handling that is intentionally started only after long blocking startup work.
Measurement Chain and Firmware Timing
The radiation measurement path is built around a one-second sampling rhythm. TIM4 provides the timing source. Every second, the firmware snapshots the two hardware counters, resets them, and marks the one-second processing tick.
Generated technical diagram: the LOW and HIGH GM counts flow through timer snapshots, adaptive EMA, range logic, display, alarm, and W5500 TCP output.
process_dose_ema() then runs the signal-processing step. For each range it computes an exponential moving average of the raw count rate. The smoothing factor is adaptive: it rises when the recent reference diverges from the filtered value, which improves response to sudden dose changes, and it falls during stable readings, which reduces Poisson count noise on the display.
The LOW channel also applies dead-time correction before converting count rate to dose rate. The repository's sensor note explains why this asymmetry is intentional: the low-dose tube is much more sensitive, so dead-time loss is significant near the upper end of the LOW range, while the high-dose tube is deliberately lower sensitivity and stays within a more forgiving count-rate region at the documented system range.
Range switching is centered around 1000 uSv/h with +/-200 uSv/h hysteresis. LOW covers the low-background to about 1 mSv/h region, while HIGH carries the higher-dose region up to the 100 mSv/h system specification. The display code changes numeric formatting and units across the range, and the HIGH display shows OVERLOAD above the configured high-end threshold.
System Architecture and Data Flow
The W5500 layer does not own the measurement loop. It receives the same current dose variable that already feeds the OLED and alarm logic.
Generated technical diagram: GM pulses are counted by STM32F446 timers, processed into dose rate, displayed locally, and sent through a W5500 TCP socket.
During startup, the firmware initializes the display and self-test first, then loads EEPROM data, then initializes the W5500 before the independent watchdog starts. That ordering is not accidental. The repository notes explain that CubeMX regeneration may reinsert an early MX_IWDG_Init() call, and starting the watchdog before POST, EEPROM loading, and W5500 link wait can cause a boot loop. The project documents this as a maintenance checklist, not just a code comment.
The main loop has two relevant cadences:
- Every 1 second: alarm processing, range checking, dose EMA processing, USB diagnostic logging, and every second tick pair, W5500 JSON sending.
- Every 0.2 seconds: watchdog refresh, W5500 socket polling, and OLED display refresh.
This split keeps the W5500 socket state machine out of the timer ISR. The ISR snapshots counters and sets flags; the main loop does the network work.
W5500 TCP Logging Layer
The Ethernet integration is contained under MyLib/W5500/. It is useful because it separates application policy from chip-port details:
w5500_port_ar22.cmaps W5500 CS to PA4, reset to PC4, and SPI transfer calls toHAL_SPI_TransmitReceive,HAL_SPI_Transmit, andHAL_SPI_Receiveon SPI1.w5500_eth.cresets the chip, registers ioLibrary callbacks, checksVERSIONR == 0x04, allocates 2 KB RX/TX buffers for all eight sockets, writes static MAC/IP/subnet/gateway values, reads them back, and polls PHY link state.net_app.cowns the application socket: W5500 socket 0, TCP mode, port 15022.
Generated technical diagram:
net_app_poll() opens and maintains the TCP socket, while net_app_send_dose() sends a JSON line when a client is connected.
The core socket state machine is short and recognizable to anyone who has used WIZnet ioLibrary:
case SOCK_CLOSED:
socket(NET_SOCK, Sn_MR_TCP, NET_PORT, 0);
break;
case SOCK_INIT:
listen(NET_SOCK);
break;
case SOCK_ESTABLISHED:
/* drain RX, ready to send JSON */
break;
case SOCK_CLOSE_WAIT:
disconnect(NET_SOCK);
break;
When the socket is established, net_app_send_dose(float dose_uSv) formats the current value as newline-delimited JSON and calls send(). Below 100 uSv/h it reports uSv/h; at or above that threshold it converts the value to mSv/h. If no client is connected, the function returns immediately.
⚙️ Role of the WIZnet Chip
The W5500 is the wired telemetry interface for the detector. It handles the TCP/IP stack in hardware, while the STM32F446 application continues to focus on pulse counting, filtering, display, alarm, EEPROM, and watchdog behavior.
That is the right fit for this project. Radiation monitoring is not a place where the main firmware should be burdened with wireless reconnect behavior, software TCP state management, or a large network stack. Here, the firmware opens one TCP socket through the WIZnet ioLibrary API and lets the W5500 handle framing, checksums, and TCP state internally.
The implementation is also easy to remove or port. ETH_LOG_ENABLE gates the entire Ethernet feature at compile time, and net_app.c uses standard ioLibrary socket calls rather than application-specific W5500 register pokes. A future move to another WIZnet chip would mainly affect the port and chip setup layer, not the dose-processing core.
Where It Fits - Value and Limits
AR22 LCU is valuable as a W5500 example because it is attached to a meaningful instrument firmware, not just a loopback server. The project shows how wired Ethernet can be grafted onto an existing embedded device as a low-risk telemetry channel: a lab PC, data logger, or bridge script can connect with telnet, netcat, or Python sockets and record live dose-rate readings.
The limits are also important. The public repository does not include schematic or PCB files, so the hardware layout cannot be audited from the repo alone. The network configuration is static IP only. There is no DHCP, no TLS, no authentication, and no multi-client design. Any host on the same LAN segment that can connect to port 15022 can receive the stream. This is appropriate for a controlled lab network, but it should not be exposed directly to an untrusted network.
The project also has a documentation mismatch worth noting for future maintainers: some overview text still describes the original LOW/HIGH timer mapping, while the current ISR and .ioc show the swapped mapping used by the firmware. For actual integration work, trust the code, .ioc, and pin labels.
Related WIZnet Maker Projects
A Low-Cost IoT Gamma-Ray Spectrometer with the Easy-F407V-ETH (W5500) Board is the closest radiation-domain comparison. It uses an STM32F407 and W5500-enabled board to stream gamma-ray spectrum data from a scintillation detector. AR22 LCU is different: it is a GM-tube dose-rate detector, not an energy spectrometer, and it adds W5500 as a firmware-isolated logging channel to an existing instrument.
Scintillation Monitor with Ethernet Interface is another radiation-monitoring example with Ethernet, KiCad hardware, and enclosure files. That project is stronger on physical design materials, while AR22 LCU is stronger on firmware depth: adaptive dual-range processing, EEPROM settings, watchdog startup notes, and a working W5500 TCP server.
Interrupt-Driven W5500 UDP Server on STM32F4 with FreeRTOS and ioLibrary is a useful implementation companion. It shows the ioLibrary style on STM32F4 in a FreeRTOS UDP reference. AR22 LCU uses the same WIZnet socket API family, but chooses TCP, no RTOS, and a production-like detector firmware context.
❓ FAQ
Q. What does AR22 LCU use the W5500 for? It uses W5500 as a wired TCP telemetry interface. A client connects to 192.168.1.22:15022 and receives a dose-rate JSON line every two seconds when the socket is established.
Q. Is the W5500 path part of the radiation measurement algorithm? No. The measurement algorithm runs on STM32 timers and firmware variables. The W5500 path reads the current dose value and sends it to a TCP client.
Q. Which timer input is LOW and which is HIGH in the current code? The current firmware maps LOW to PA0/TIM8 and HIGH to PE7/TIM1. Some overview text is stale, but the ISR, .ioc, and pin labels confirm the current mapping.
Q. Can Ethernet be removed from the firmware build? Yes. ETH_LOG_ENABLE controls the W5500 feature at compile time. Setting it to 0 removes the Ethernet includes and calls from the build.
Q. Is this ready for an untrusted network? No. It uses a fixed IP TCP server without TLS or authentication. Treat it as a controlled-LAN telemetry channel unless extra security is added outside this firmware.
한국어 (Korean)
📌 개요
kzeta23는 AR22 LCU v1.1을 STM32F446 기반 이중 레인지 감마선 검출기 펌웨어로 공개했습니다. GitHub 공개 프로필에는 이름, 국가, 조직 정보가 없으므로 이 글에서는 확인된 레포와 코드 근거만 보수적으로 다룹니다. 레포의 문서와 주석은 대부분 한국어로 작성되어 있습니다.
AR22 LCU v1.1의 핵심은 검증된 방사선 검출기 펌웨어에 WIZnet W5500 Ethernet TCP 로깅 경로를 추가한 점입니다. 펌웨어는 두 개의 가이거-뮬러 관 채널에서 펄스를 계수하고, 적응형 필터로 선량률을 계산하며, SSD1322 OLED와 알람 출력을 구동합니다. 여기에 TCP 포트 15022를 통해 현재 선량률을 JSON 한 줄로 전송하는 W5500 경로가 붙습니다.


가이거-뮐러 계수관(Geiger–Müller tube)이라고 하는 관(tube)을 통해 방사선 입자 하나하나를 셀 수 있으며, 측정 단위는 CPS(Count Per Second)이다.
생성 기술 일러스트: AR22 LCU를 선량률 계측기와 W5500 TCP 로깅 구조로 표현했습니다.
이 레포는 단순 TCP 예제가 아닙니다. 기존 계측과 알람 코어를 보존하고, ETH_LOG_ENABLE 매크로 뒤에 W5500 계층을 분리해서 붙이는 구조입니다. 매크로가 1이면 192.168.1.22:15022로 접속한 TCP 클라이언트가 {"val":1.23,"unit":"uSv/h"} 같은 JSON 데이터를 받을 수 있고, 0이면 Ethernet 관련 코드가 빌드에서 빠집니다.
시스템 구성
주 MCU는 STM32F446VET6이며 Cortex-M4 애플리케이션으로 100 MHz에서 동작합니다. 펄스 계수에는 하드웨어 타이머를 쓰고, OLED는 SPI와 DMA, 설정 저장은 I2C EEPROM, 스위치는 GPIO/EXTI, 부저는 TIM5 PWM, W5500은 SPI1을 사용합니다.
최신 코드와 CubeMX 프로젝트 기준의 GM 채널 매핑은 다음과 같습니다.
- LOW 선량 채널: LND 7128 계열 GM 관,
GM_COUNT_LO, PA0, TIM8_ETR. - HIGH 선량 채널: LND 71631 계열 GM 관,
GM_COUNT_HI, PE7, TIM1_ETR.
이 부분은 중요합니다. 레포의 오래된 overview 표에는 LOW/HIGH 타이머 매핑이 반대로 적혀 있지만, 실제 펌웨어의 HAL_TIM_PeriodElapsedCallback()은 LOW 카운트를 TIM8에서 읽고 HIGH 카운트를 TIM1에서 읽습니다. ISR 주석에도 현재 배선과 라벨에 맞춰 채널이 swap되었다고 적혀 있습니다.
디스플레이는 SPI4+DMA 기반 SSD1322 256x64 OLED입니다. 24C01 I2C EEPROM에는 알람 임계값과 LOW/HIGH 변환계수가 저장됩니다. 부팅 시에는 디스플레이, LED, 부저, EEPROM을 확인하는 power-on self-test가 실행되며, watchdog은 긴 부팅 작업이 끝난 뒤 시작되도록 구성되어 있습니다.
측정 체인과 펌웨어 타이밍
방사선 측정 경로는 1초 샘플링 리듬을 중심으로 구성됩니다. TIM4가 시간 기준을 만들고, 1초마다 두 하드웨어 카운터 값을 읽은 뒤 카운터를 리셋합니다.
생성 기술 다이어그램: LOW/HIGH GM 카운트가 타이머 스냅샷, 적응형 EMA, 레인지 로직, 표시, 알람, W5500 TCP 출력으로 흐릅니다.
이후 process_dose_ema()가 신호 처리를 수행합니다. 각 레인지에서 raw count rate의 지수이동평균을 계산하고, 최근 reference와 filtered 값의 차이가 크면 alpha를 높여 빠르게 반응하고, 안정 상태에서는 alpha를 낮춰 계수 노이즈를 줄입니다.
LOW 채널에는 dead-time correction도 적용됩니다. 레포의 센서 문서는 이 비대칭 처리가 의도된 설계라고 설명합니다. LOW 관은 고감도라 상단에서 dead-time 손실이 의미 있게 커지고, HIGH 관은 일부러 낮은 감도와 짧은 dead time을 가진 소자라 문서화된 시스템 범위에서는 보정 필요성이 낮습니다.
레인지 전환은 1000 uSv/h를 중심으로 +/-200 uSv/h 히스테리시스를 둡니다. LOW는 저선량부터 약 1 mSv/h 근처까지, HIGH는 더 높은 선량 영역을 담당합니다. 표시 코드는 값 범위에 따라 단위와 포맷을 바꾸고, HIGH 표시에서는 상단 조건에서 OVERLOAD를 표시합니다.
시스템 아키텍처와 데이터 흐름
W5500 계층은 측정 루프를 소유하지 않습니다. OLED와 알람에 쓰이는 현재 선량 변수를 읽어 TCP로 내보내는 구조입니다.
생성 기술 다이어그램: GM 펄스는 STM32F446 타이머에서 계수되고, 선량률로 처리된 뒤 로컬 표시와 알람, W5500 TCP 소켓으로 전달됩니다.
부팅 순서도 신중합니다. 펌웨어는 먼저 디스플레이와 self-test를 초기화하고, EEPROM 데이터를 로드한 뒤, W5500을 초기화하고 나서 watchdog을 시작합니다. 레포 문서에는 CubeMX 재생성 시 MX_IWDG_Init()가 너무 이른 위치에 다시 삽입될 수 있고, 그렇게 되면 POST나 EEPROM 로딩, W5500 링크 대기 중 watchdog reset으로 boot loop가 발생할 수 있다고 정리되어 있습니다.
메인 루프에는 두 가지 주기가 있습니다.
- 1초마다: 알람 처리, 레인지 확인, dose EMA 처리, USB 진단 로그, 그리고 2초마다 W5500 JSON 전송.
- 0.2초마다: watchdog refresh, W5500 socket polling, OLED 갱신.
이 구조 덕분에 W5500 socket state machine은 timer ISR 안에 들어가지 않습니다. ISR은 카운터 스냅샷과 플래그 설정만 하고, 네트워크 작업은 메인 루프가 처리합니다.
W5500 TCP 로깅 계층
Ethernet 통합은 MyLib/W5500/ 아래에 분리되어 있습니다. 애플리케이션 정책과 칩 포트 세부 구현을 나눈 점이 좋습니다.
w5500_port_ar22.c: W5500 CS=PA4, reset=PC4, SPI 전송을 STM32 HAL SPI1 호출로 매핑합니다.w5500_eth.c: 칩 리셋, ioLibrary callback 등록,VERSIONR == 0x04확인, 8개 socket RX/TX 버퍼 2 KB 할당, static network 설정, readback 검증, PHY link polling을 맡습니다.net_app.c: 애플리케이션 socket을 담당합니다. W5500 socket 0, TCP mode, port 15022입니다.
생성 기술 다이어그램:
net_app_poll()이 TCP socket을 열고 유지하며, net_app_send_dose()가 클라이언트 연결 시 JSON 한 줄을 전송합니다.
핵심 socket state machine은 WIZnet ioLibrary를 써본 독자에게 익숙한 형태입니다.
case SOCK_CLOSED:
socket(NET_SOCK, Sn_MR_TCP, NET_PORT, 0);
break;
case SOCK_INIT:
listen(NET_SOCK);
break;
case SOCK_ESTABLISHED:
/* drain RX, ready to send JSON */
break;
case SOCK_CLOSE_WAIT:
disconnect(NET_SOCK);
break;
socket이 연결되면 net_app_send_dose(float dose_uSv)가 현재 값을 newline-delimited JSON으로 포맷하고 send()를 호출합니다. 100 uSv/h 미만에서는 uSv/h, 그 이상에서는 mSv/h로 변환합니다. 클라이언트가 연결되어 있지 않으면 함수는 바로 반환됩니다.
⚙️ WIZnet 칩의 역할
W5500은 검출기의 유선 telemetry 인터페이스입니다. TCP/IP stack을 하드웨어에서 처리하고, STM32F446 애플리케이션은 펄스 계수, 필터링, 표시, 알람, EEPROM, watchdog 동작에 집중합니다.
방사선 모니터링 펌웨어에서는 이 선택이 자연스럽습니다. 메인 펌웨어가 wireless reconnect, software TCP state, 큰 network stack을 직접 들고 있을 필요가 없습니다. 이 프로젝트는 WIZnet ioLibrary API로 socket 하나를 열고, W5500이 framing, checksum, TCP state를 맡게 합니다.
구현을 제거하거나 이식하기도 쉽습니다. ETH_LOG_ENABLE이 Ethernet 기능 전체를 compile time에서 제어하고, net_app.c는 애플리케이션별 register 직접 조작이 아니라 표준 ioLibrary socket 호출을 사용합니다. 다른 WIZnet 칩으로 옮긴다면 dose-processing core보다 port/chip setup layer를 주로 바꾸면 됩니다.
적용 가치와 한계
AR22 LCU는 W5500 예제로 가치가 있습니다. 단순 echo server가 아니라 실제 계측기 펌웨어에 붙은 telemetry 채널이기 때문입니다. 실험실 PC, 데이터 로거, bridge script가 telnet, netcat, Python socket으로 접속해서 실시간 선량률을 기록할 수 있습니다.
한계도 분명합니다. 공개 레포에는 회로도, PCB, 인클로저 CAD, 실제 하드웨어 사진이 없습니다. 네트워크 설정은 static IP 전용이고, DHCP, TLS, 인증, 다중 클라이언트 구조는 없습니다. 같은 LAN에서 포트 15022에 접근 가능한 장치는 데이터를 받을 수 있습니다. 통제된 실험실 LAN에는 적합하지만, 신뢰할 수 없는 네트워크에 직접 노출하면 안 됩니다.
또 하나의 유지보수 포인트는 문서 불일치입니다. 일부 overview 문서는 원래 LOW/HIGH 타이머 매핑을 설명하지만, 현재 ISR과 .ioc는 swap된 매핑을 사용합니다. 실제 통합이나 디버깅에서는 코드, .ioc, pin label을 기준으로 보는 것이 맞습니다.
관련 WIZnet Maker 프로젝트
W5500 기반 저비용 IoT 감마선 분광기는 가장 가까운 방사선 분야 비교 대상입니다. STM32F407과 W5500 보드를 사용해 scintillation detector의 gamma spectrum 데이터를 PC로 전송합니다. AR22 LCU는 에너지 spectrum이 아니라 GM tube dose rate를 다루고, 기존 계측기 펌웨어에 W5500 logging channel을 분리해서 붙인 점이 다릅니다.
Scintillation Monitor with Ethernet Interface는 Ethernet을 포함한 또 다른 방사선 모니터링 예시이며 KiCad hardware와 enclosure 자료가 있습니다. 그 프로젝트가 물리 설계 자료에 강하다면, AR22 LCU는 adaptive dual-range 처리, EEPROM 설정, watchdog startup 주의, W5500 TCP server 같은 펌웨어 깊이에 강점이 있습니다.
STM32F4 FreeRTOS W5500 UDP ioLibrary 레퍼런스는 구현 관점에서 같이 보면 좋습니다. 해당 글은 STM32F4와 FreeRTOS에서 ioLibrary UDP server를 정리한 레퍼런스이고, AR22 LCU는 같은 WIZnet socket API 계열을 쓰지만 TCP, no RTOS, 실제 검출기 펌웨어라는 맥락이 다릅니다.
❓ FAQ
AR22 LCU는 W5500을 어디에 사용하나요? W5500은 유선 TCP telemetry 인터페이스입니다. 클라이언트가 192.168.1.22:15022로 접속하면 socket이 연결된 동안 2초마다 dose-rate JSON 한 줄을 받습니다.
W5500 경로가 방사선 측정 알고리즘의 일부인가요? 아닙니다. 측정 알고리즘은 STM32 하드웨어 타이머와 펌웨어 변수에서 동작합니다. W5500 경로는 현재 dose 값을 읽어 TCP 클라이언트로 보내는 출력 채널입니다.
현재 코드에서 LOW와 HIGH 타이머 입력은 어떻게 매핑되어 있나요? 현재 펌웨어는 LOW를 PA0/TIM8, HIGH를 PE7/TIM1에 매핑합니다. 일부 overview 문서는 오래된 매핑을 담고 있지만, ISR, .ioc, pin label은 현재 매핑을 확인해 줍니다.
Ethernet 기능을 빌드에서 제거할 수 있나요? 네. ETH_LOG_ENABLE이 W5500 기능을 compile time에서 제어합니다. 0으로 설정하면 Ethernet include와 호출이 빌드에서 빠집니다.
이 펌웨어를 신뢰할 수 없는 네트워크에 바로 연결해도 되나요? 안 됩니다. fixed IP TCP server이며 TLS나 인증이 없습니다. 별도 보안 계층이 없다면 통제된 LAN telemetry 채널로 보는 것이 맞습니다.
-
AR22_v1_1 Source Code
STM32F446 AR22 LCU firmware with dual-range GM counting, SSD1322 OLED, EEPROM settings, and W5500 TCP dose logging.
-
AR22 LCU System Overview
Korean system overview covering measurement chain, display, alarm, EEPROM, and build flow.
-
AR22 GM Sensor Notes
Sensor and firmware consistency notes for LND 7128 and LND 71631 dual-range operation.
-
WIZnet W5500 Documentation
W5500 chip documentation and product information.
-
WIZnet ioLibrary Driver
Reference WIZnet socket API used by the bundled MyLib/W5500/ioLibrary code.
