STM32_MQTT_Project
Repository dedicated to storing tests and experiments carried out with the STM32G0B0RET6 and the W5500 using the MQTT protocol.
요약
STM32G0B0RET6(Cortex-M0+)와 WIZnet W5500을 SPI로 연결해 RTOS 없이 MQTT 통신을 구현한 프로젝트입니다. W5500이 TCP/IP 처리를 전담하기 때문에 lwIP나 FreeRTOS 없이도 안정적인 Ethernet MQTT 클라이언트를 구성할 수 있습니다. 저가 Value line MCU로 IoT 통신 노드를 구축하는 것이 현실적으로 가능함을 실증하는 레퍼런스 구현입니다.
개요
임베디드 시스템에서 Ethernet 기반 MQTT 통신을 구현하는 경로는 크게 두 가지입니다. lwIP + FreeRTOS 조합, 또는 Wi-Fi 모듈(ESP8266 등)과 AT 커맨드를 사용하는 방식입니다. 두 경로 모두 소프트웨어 복잡도가 높습니다. lwIP는 RTOS 스케줄러와 결합되어야 안정적으로 동작하고, Wi-Fi 모듈은 AT 커맨드 계층이 개입해 지연과 불확실성이 생깁니다.
이 프로젝트는 세 번째 길을 선택했습니다. WIZnet W5500 하드웨어 TCP/IP 컨트롤러를 SPI로 연결해, MCU가 네트워크 처리를 직접 맡지 않아도 되는 구조입니다. ARP 해석, TCP 핸드셰이크, ACK 처리, 재전송 — 이 모든 것을 W5500이 처리합니다. MCU는 SPI로 소켓을 열고, 데이터를 쓰는 것만 하면 됩니다.
대상은 Cortex-M0+ 계열의 저가 MCU로 산업 IoT 또는 스마트홈 데이터 전송 노드를 개발하려는 임베디드 엔지니어입니다. RTOS 없이 신뢰할 수 있는 Ethernet MQTT 클라이언트를 구현하는 것이 주요 목표입니다.
→ GitHub 저장소: https://github.com/MatheusCipolotti/STM32_MQTT_Project
아키텍처
시스템은 세 레이어로 구성됩니다. MCU 애플리케이션 레이어, W5500 네트워크 오프로드 레이어, 외부 MQTT 브로커입니다.
MCU는 ioLibrary_Driver를 통해 W5500 레지스터를 SPI로 조작합니다. MQTT 클라이언트 레이어(Paho Embedded C 기반)는 W5500이 열어준 TCP 소켓을 Publish/Subscribe 채널로 사용합니다. MCU 입장에서 네트워크는 단순한 소켓 I/O일 뿐입니다.
기술 배경
W5500 하드웨어 TCP/IP 오프로딩
하드웨어 TCP/IP 오프로딩이란 TCP/IP 프로토콜 스택 전체를 소프트웨어가 아닌 전용 하드웨어 로직으로 처리하는 방식입니다. W5500은 TCP, UDP, ICMP, IPv4, ARP, IGMP, PPPoE를 하드웨어로 지원하며, 8개 독립 소켓과 32KB 내부 버퍼를 갖춘 원칩 솔루션입니다. MCU는 SPI(최대 80MHz)를 통해 레지스터를 읽고 쓰는 것만으로 완전한 TCP 소켓을 제어합니다. ACK 처리, 재전송, 흐름 제어는 W5500 내부에서 자율적으로 처리됩니다.
이 프로젝트에서 W5500은 MQTT가 요구하는 TCP 연결 유지, ACK 관리, 패킷 재조합을 전담합니다. Cortex-M0+, 64MHz라는 제한된 연산 자원을 가진 G0B0가 RTOS 없이 MQTT를 운용할 수 있는 이유가 바로 여기 있습니다.
→ W5500 공식 문서: https://docs.wiznet.io/Product/Chip/Ethernet/W5500
→ WIZnet ioLibrary_Driver: https://github.com/Wiznet/ioLibrary_Driver
MQTT Publish/Subscribe 프로토콜
**MQTT(Message Queuing Telemetry Transport)**는 발행(Publish)/구독(Subscribe) 모델 기반의 경량 메시지 프로토콜입니다(OASIS 표준, v3.1.1/v5.0). 클라이언트는 브로커를 통해 메시지를 교환하며, 서로 직접 연결하지 않습니다. 고정 헤더가 최소 2바이트에 불과해 임베디드 환경에 적합합니다. QoS 레벨(0: 최대 1회, 1: 최소 1회, 2: 정확히 1회)과 Keep-Alive 타이머로 신뢰성과 연결 유지를 조절합니다.
이 프로젝트에서 MQTT는 W5500이 열어준 TCP 소켓(포트 1883) 위에서 동작합니다. WIZnet ioLibrary_Driver에 내장된 MQTT 클라이언트가 Paho Embedded C의 패킷 직렬화를 처리하므로, MCU 코드는 브로커 IP, 포트, 토픽 설정만 작성하면 연결이 성립됩니다.
→ MQTT v3.1.1 명세: https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html
기술 특징
STM32G0B0는 STM32G0 시리즈의 Value line 모델입니다. Cortex-M0+, 64MHz, 512KB Flash, 144KB SRAM을 갖추면서 USB·DAC·하드웨어 RNG를 제거해 단가를 낮췄습니다. 네트워크 처리가 W5500으로 위임되기 때문에 이 단순화된 MCU로도 MQTT 통신 노드를 운용하는 데 충분합니다.
소프트웨어 측면에서 핵심은 RTOS 미사용입니다. [추정] 단일 while(1) 루프에서 MQTTYield()를 주기적으로 호출해 수신 메시지를 처리하고, 연결 끊김 시 재연결 루틴을 실행하는 구조입니다. W5500이 TCP 세션을 자율적으로 유지해 주므로 MCU 측 상태 기계는 단순하게 유지됩니다.
WIZnet ioLibrary_Driver는 MCU 종속성 없이 W5500 레지스터를 추상화합니다. SPI 콜백 함수만 플랫폼에 맞게 구현하면 MQTT, DHCP, DNS, HTTP 등 Internet 레이어 전체를 그대로 사용할 수 있어, 다른 MCU로의 이식도 용이합니다.
WIZnet W5500 적용 구조
W5500은 이 프로젝트에서 네트워크 처리 전담 역할을 맡습니다. STM32G0B0의 SPI 포트에 연결되어, MCU가 소켓 API를 호출하면 W5500이 실제 TCP 프레임을 생성·전송·수신합니다.
- 기존 구조 (소프트웨어 TCP/IP): MCU가 TCP/IP 스택을 직접 실행 → CPU 사이클 소비 + RTOS 의존성 + 메모리 요구 증가
- W5500 적용 구조: MCU는 SPI 소켓 제어만 담당 → TCP/IP 전담은 W5500, MCU 부하 최소화
- 실질적 효과: RTOS 없이 안정적인 MQTT 세션 유지, 코드 복잡도 대폭 감소, G0B0의 144KB SRAM을 애플리케이션 로직에 온전히 활용 가능
비교 분석: W5500-STM32-cJSON 프로젝트와 무엇이 다른가
같은 WIZnet 생태계에서 유사한 구성으로 주목할 만한 프로젝트가 있습니다. W5500-STM32-cJSON 프로젝트는 STM32F103 + W5500 + MQTT 베이스에 cJSON 직렬화 레이어를 추가하고, 이식 과정의 디버깅 기록을 상세히 남긴 구현입니다.
→ WIZnet Makers 아티클: https://maker.wiznet.io/scott/projects/w5500-stm32-cjson/
두 프로젝트는 STM32 + W5500 + MQTT + 베어메탈이라는 동일한 기반을 공유합니다. 차이는 목적과 완성도, 그리고 하드웨어 리소스에서 드러납니다.
| 항목 | 이 프로젝트 (G0B0) | W5500-STM32-cJSON (F103) |
|---|---|---|
| MCU | G0B0 (Cortex-M0+, 144KB SRAM) | F103 (Cortex-M3, 20KB SRAM) |
| 데이터 포맷 | Raw MQTT payload | JSON (cJSON 직렬화) |
| IoT 플랫폼 연동 | 제한적 | 가능 (REST / MQTT JSON) |
| 문서화 | 최소 (제목 + 한 줄) | 상세 (이식 기록, 디버그 로그) |
| 주요 병목 | 보안 (TLS 미지원) | 힙 부족 (기본 512B) |
여기서 역설적인 관계가 드러납니다. W5500-STM32-cJSON 프로젝트의 실질적 병목은 cJSON이 동적 메모리 할당을 사용하는데, STM32F103의 기본 힙(512B)이 턱없이 부족하다는 점이었습니다. 반면 이 프로젝트의 G0B0는 144KB SRAM을 갖춰, cJSON을 올려도 메모리 압박이 훨씬 작습니다.
두 프로젝트를 합친 구조 — G0B0 + W5500 + MQTT + cJSON — 는 각각의 약점을 상쇄합니다. G0B0의 넉넉한 SRAM이 cJSON 힙 문제를 해결하고, cJSON 직렬화가 이 프로젝트의 IoT 플랫폼 연동 공백을 채웁니다. 이 조합이 현실적인 다음 단계입니다.
비즈니스 가치
이 프로젝트의 아키텍처는 다음 시나리오에 바로 적용 가능합니다.
- 산업 센서 게이트웨이: G0B0의 12-bit ADC(19채널)로 아날로그 센서를 수집하고, W5500으로 MQTT 브로커에 주기 발행. RTOS 없이도 안정적인 주기 전송이 가능합니다 [추정].
- 스마트홈 유선 엣지 노드: Wi-Fi 대비 간섭·지연이 적은 Ethernet 유선 연결로 빌딩 자동화 및 홈 서버 연동에 적합합니다.
- 원격 모니터링 장치: G0B0의 저소비 전력 설계와 W5500의 Wake-on-LAN 기능 조합으로 대기 전력 최소화 구성이 가능합니다.
- 저비용 대량 배포 노드: Value line MCU + W5500의 낮은 BOM 비용은 수십~수백 대 규모 IoT 배포에 경제적입니다.
WIZnet 관점에서는 STM32G0 계열과 W5500의 조합 검증이 이루어졌다는 점이 중요합니다. 기존 레퍼런스가 STM32F1/F4 중심이었던 것에서 최신 G0 Value line으로 적용 범위가 확장됩니다.
한계 및 개선 방향
이 프로젝트가 W5500 베어메탈 MQTT의 실현 가능성을 보여준 것은 분명한 기여입니다. 다만 실제 배포를 위해서는 몇 가지 개선이 필요합니다.
현재 한계:
- 보안 부재: G0B0에 하드웨어 암호화 가속기가 없고, W5500도 TLS를 지원하지 않습니다. 평문 MQTT(포트 1883)만 현실적이어서 공개 네트워크 배포에는 적합하지 않습니다.
- 베어메탈 타이밍 리스크:
while(1)구조에서MQTTYield()호출 주기와 재연결 루틴이 경쟁할 경우 응답 지연이 발생할 수 있습니다. - 문서 부재: README에 배선도, 브로커 설정, 빌드 방법이 없어 재현 난이도가 높습니다.
개선 방향:
- cJSON 레이어 추가: G0B0의 144KB SRAM은 cJSON 통합에 유리합니다. JSON 직렬화를 추가하면 AWS IoT Core, HiveMQ Cloud 등 주요 플랫폼과의 연동이 쉬워집니다.
- MQTT over TLS: mbedTLS 소프트웨어 구현과 G0B0의 넉넉한 SRAM 조합으로 TLS를 검토할 수 있습니다. 혹은 IPv6와 TLS를 지원하는 W6100으로 업그레이드하면 하드웨어 레이어에서 해결됩니다.
- DHCP 지원: ioLibrary_Driver의 DHCP 모듈을 활성화해 네트워크 환경 의존성을 줄이는 것을 권장합니다.
FAQ
Q. lwIP + FreeRTOS 대비 이 구성의 실질적인 이점은 무엇인가요? lwIP는 RTOS 스케줄러와 결합되어야 안정적으로 동작합니다. W5500은 TCP/IP를 하드웨어로 완결하므로 MCU는 소켓 API 호출만 하면 됩니다. 코드 복잡도와 메모리 요구량이 크게 줄어들고, RTOS 학습 곡선도 없습니다.
Q. G0B0 Value line과 G0B1의 차이가 실제 구현에 영향을 주나요? G0B0은 G0B1에서 USB, DAC, 하드웨어 RNG를 제거한 모델입니다. 순수 MQTT 전송 노드 용도라면 이 기능이 불필요하고 단가를 낮출 수 있습니다. 단, TLS 구현 시 소프트웨어 난수 생성이 필요해지므로 보안 요구사항이 높다면 G0B1이 더 적합합니다.
Q. 이 구조에서 몇 개의 MQTT 토픽을 동시에 처리할 수 있나요? MQTT 프로토콜은 단일 TCP 연결로 다중 토픽 구독을 처리합니다. W5500의 8소켓 한도보다 MQTTYield() 호출 주기와 브로커 QoS 설정이 처리량에 더 직접적인 영향을 줍니다.
Q. 다른 STM32 시리즈로 포팅하기 어렵나요? WIZnet ioLibrary_Driver는 MCU 독립적으로 설계되어 있습니다. SPI 읽기/쓰기 콜백과 딜레이 함수만 대상 MCU에 맞게 구현하면 STM32F1, STM32H7, RP2040 등에도 동일하게 사용할 수 있습니다.
Q. cJSON을 이 프로젝트에 추가하려면 무엇이 필요한가요? cJSON.c / cJSON.h를 프로젝트에 추가하고, CubeIDE 링커 설정에서 힙 크기를 최소 4KB 이상으로 늘려야 합니다. G0B0의 144KB SRAM은 이를 충분히 수용합니다. ioLibrary_Driver의 MQTT 클라이언트는 페이로드를 바이트 배열로 전달하므로, cJSON으로 생성한 JSON 문자열을 그대로 MQTT 페이로드로 사용할 수 있습니다.
Summary
This project connects an STM32G0B0RET6 (Cortex-M0+) to a WIZnet W5500 over SPI and implements MQTT communication without an RTOS. Because the W5500 handles all TCP/IP processing, a stable Ethernet MQTT client runs on a bare-metal loop — no lwIP, no FreeRTOS needed. It is a practical proof-of-concept that a low-cost Value line MCU can serve as a reliable IoT communication node.
Overview
There are typically two paths to Ethernet-based MQTT on embedded systems. The first is lwIP + FreeRTOS, and the second is a Wi-Fi module with AT commands (e.g., ESP8266). Both carry significant software complexity. lwIP requires a scheduler to operate reliably, and Wi-Fi modules introduce an AT command layer that adds latency and uncertainty.
This project takes a third path. It connects a WIZnet W5500 hardware TCP/IP controller over SPI, so the MCU never has to touch the network stack directly. ARP resolution, TCP handshakes, ACK management, and retransmission — the W5500 handles all of it. The MCU simply opens a socket and writes data.
The target audience is embedded engineers who want to build industrial IoT or smart-home data nodes using low-cost Cortex-M0+ MCUs. The primary goal is a dependable Ethernet MQTT client without the overhead of an RTOS.
→ GitHub repository: https://github.com/MatheusCipolotti/STM32_MQTT_Project
Architecture
The system consists of three layers: the MCU application layer, the W5500 network offload layer, and an external MQTT broker.
The MCU accesses W5500 registers over SPI via the ioLibrary_Driver. The MQTT client layer (based on Paho Embedded C) uses the TCP socket the W5500 opens as its Publish/Subscribe channel. From the MCU's perspective, the network is just socket I/O.
Technology Background
W5500 Hardware TCP/IP Offloading
Hardware TCP/IP offloading means processing the entire TCP/IP protocol stack in dedicated hardware logic rather than software. The W5500 is a single-chip solution that does exactly this. It supports TCP, UDP, ICMP, IPv4, ARP, IGMP, and PPPoE in hardwired logic, and provides 8 independent sockets with a 32KB internal buffer. The MCU communicates over SPI (up to 80MHz) and controls a complete TCP socket simply by reading and writing registers. ACK handling, retransmission, and flow control all happen autonomously inside the W5500.
In this project, the W5500 takes full ownership of TCP session maintenance, ACK management, and packet reassembly — tasks that MQTT requires but that would otherwise burden the MCU. This is what enables a Cortex-M0+ running at 64MHz to run a stable MQTT client without an RTOS.
→ W5500 official documentation: https://docs.wiznet.io/Product/Chip/Ethernet/W5500 → WIZnet ioLibrary_Driver: https://github.com/Wiznet/ioLibrary_Driver
MQTT Publish/Subscribe Protocol
MQTT (Message Queuing Telemetry Transport) is a lightweight messaging protocol based on the Publish/Subscribe model (OASIS standard, v3.1.1/v5.0). Clients exchange messages through a broker — they never connect directly to each other. The fixed header is as small as 2 bytes, making MQTT well-suited to constrained embedded environments. QoS levels (0: at most once, 1: at least once, 2: exactly once), Keep-Alive timers, and Last Will Testament provide tunable reliability.
In this project, MQTT runs on the TCP socket (port 1883) that the W5500 opens. The MQTT client built into the WIZnet ioLibrary_Driver handles Paho Embedded C packet serialization. The MCU code only needs to configure the broker IP, port, and topic — the connection handles itself.
→ MQTT v3.1.1 specification: https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html
Technical Highlights
The STM32G0B0 is the Value line variant of the STM32G0 series. It pairs a Cortex-M0+, 64MHz core with 512KB Flash and 144KB SRAM, while dropping USB, DAC, and hardware RNG to reduce cost. Because W5500 handles all network processing, this stripped-down MCU is more than capable of running an MQTT node.
On the software side, the standout design choice is no RTOS. [Inferred] A single while(1) loop periodically calls MQTTYield() to process incoming messages and triggers a reconnect routine on connection loss. Because the W5500 autonomously maintains the TCP session, the MCU-side state machine stays simple.
The WIZnet ioLibrary_Driver abstracts W5500 registers independently of any MCU. Implement the SPI read/write callbacks for your platform, and the entire Internet layer — MQTT, DHCP, DNS, HTTP — becomes available as-is. Porting to a different MCU requires minimal effort.
Where WIZnet Fits
The W5500 serves as the dedicated network processing unit in this project. Connected to the STM32G0B0's SPI port, it builds and transmits real TCP frames whenever the MCU calls a socket API.
- Without W5500 (software TCP/IP): The MCU runs the TCP/IP stack directly — consuming CPU cycles, requiring an RTOS, and increasing memory pressure.
- With W5500: The MCU only manages socket control over SPI. TCP/IP processing is fully offloaded. MCU load drops to a minimum.
- Practical result: Stable MQTT sessions without an RTOS, dramatically simpler code, and the G0B0's full 144KB SRAM available for application logic.
Comparative Analysis: How Does This Differ from W5500-STM32-cJSON?
Another project in the WIZnet ecosystem is worth examining closely. W5500-STM32-cJSON builds on the same STM32 + W5500 + MQTT bare-metal foundation, then adds a cJSON serialization layer and detailed porting records.
→ WIZnet Makers article: https://maker.wiznet.io/scott/projects/w5500-stm32-cjson/
Both projects share the same core stack — STM32 + W5500 + MQTT + bare-metal — but diverge in purpose, completeness, and hardware resources.
| Item | This Project (G0B0) | W5500-STM32-cJSON (F103) |
|---|---|---|
| MCU | G0B0 (Cortex-M0+, 144KB SRAM) | F103 (Cortex-M3, 20KB SRAM) |
| Data format | Raw MQTT payload | JSON (cJSON library) |
| IoT platform integration | Limited | Possible (REST / MQTT JSON) |
| Documentation | Minimal (title + one line) | Detailed (porting log, debug records) |
| Primary bottleneck | Security (no TLS) | Heap exhaustion (default 512B) |
A paradoxical relationship emerges here. The main bottleneck in W5500-STM32-cJSON was that cJSON uses dynamic memory allocation, but the STM32F103's default heap (512B) is far too small. This project's G0B0, with 144KB SRAM, faces no such constraint — it is actually the better hardware for running cJSON.
Combining both projects — G0B0 + W5500 + MQTT + cJSON — cancels out each project's weakness. The G0B0's generous SRAM solves the heap problem, and cJSON serialization closes the IoT platform integration gap this project leaves open. That combination is the logical next step.
Business Value
This architecture maps directly to the following deployment scenarios.
- Industrial sensor gateway: G0B0's 12-bit ADC (up to 19 channels) collects analog sensor data; W5500 publishes it to an MQTT broker on a fixed cycle. Stable periodic transmission runs without an RTOS. [Inferred]
- Wired smart-home edge node: Ethernet offers lower interference and latency than Wi-Fi. This makes the setup well-suited for building automation and home server integration.
- Remote monitoring device: Combining the G0B0's low-power design with W5500's Wake-on-LAN support enables minimal standby power consumption.
- Low-cost, large-scale IoT deployment: The Value line MCU plus W5500 keeps BOM costs low — practical for deployments in the tens to hundreds of units.
From WIZnet's perspective, this project validates the STM32G0 + W5500 combination. Most existing references target the STM32F1/F4 family. This expands the verified scope to the latest G0 Value line.
Limitations and Future Improvements
This project makes a real contribution by demonstrating that bare-metal MQTT over W5500 is feasible. That said, several improvements are necessary before production deployment.
Current limitations:
- No security: The G0B0 has no hardware crypto accelerator, and the W5500 does not support TLS natively. Only plaintext MQTT (port 1883) is realistic. This rules out deployment on public networks.
- Bare-metal timing risk: In a
while(1)loop,MQTTYield()timing and the reconnect routine can interfere with each other, causing response delays. - No documentation: The repository has no wiring diagram, broker configuration guide, or build instructions, making replication difficult.
Recommended improvements:
- Add cJSON: As outlined above, the G0B0's 144KB SRAM accommodates cJSON comfortably. Adding JSON serialization enables direct integration with AWS IoT Core, HiveMQ Cloud, and similar platforms.
- MQTT over TLS: mbedTLS in software, combined with the G0B0's ample SRAM, makes TLS feasible. Alternatively, upgrading to the W6100 (which adds IPv6 and TLS support) solves it at the hardware level.
- Enable DHCP: Activating the DHCP module in the WIZnet ioLibrary_Driver removes dependency on a fixed network configuration.
FAQ
Q. What is the practical advantage over lwIP + FreeRTOS? lwIP is tightly coupled with an RTOS scheduler and behaves unreliably without one. The W5500 completes TCP/IP processing in hardware, so the MCU only calls socket APIs. Code complexity drops significantly, and there is no RTOS learning curve.
Q. Does using the G0B0 Value line over G0B1 cause any real limitations? The G0B0 removes USB, DAC, and hardware RNG from the G0B1. For a pure MQTT transmission node, none of those are needed — and the cost saving is meaningful. However, if TLS is required, software-based random number generation becomes necessary, at which point the G0B1 is a better fit.
Q. How many MQTT topics can this handle simultaneously? MQTT handles multiple topic subscriptions over a single TCP connection. The W5500's 8-socket limit is not the constraint here. The MQTTYield() call interval and the broker-side QoS settings have a more direct impact on throughput.
Q. How difficult is it to port this to another MCU? The WIZnet ioLibrary_Driver is MCU-agnostic by design. Implement the SPI read/write callbacks and a delay function for your target platform, and the full driver works on STM32F1, STM32H7, RP2040, NXP LPC, and others.
Q. What is needed to add cJSON to this project? Add cJSON.c and cJSON.h to the project, then increase the heap size to at least 4KB in the linker settings. The G0B0's 144KB SRAM accommodates this without issue. Since the ioLibrary_Driver's MQTT client accepts payload as a byte array, a JSON string generated by cJSON can be passed directly as the MQTT payload.
