TM32F407 + W5500: Ethernet-Connected DiSEqC Satellite Motor Controller
Custom PCB with STM32F407 and W5500 for remote MQTT control of satellite LNB polarization, DiSEqC motor positioning, and IF signal routing over Ethernet.
Software Apps and online services
What This Project Does
This is a purpose-built Ethernet-connected controller for satellite antenna systems. It replaces the manual process of switching LNB polarization, selecting frequency bands, and positioning DiSEqC motor-driven satellite dishes — all remotely controllable through MQTT over a standard Ethernet connection.
The hardware is a custom 4-layer PCB designed from scratch in KiCad, centered around an STM32F407VGT6 microcontroller paired with a WIZnet W5500 for network connectivity. The software runs on .NET nanoFramework with a native C++ interop layer for time-critical DiSEqC signal generation.
From the project README: "Enables remote control of LNB power supply voltage (13 V / 18 V polarization selection), DiSEqC 1.x command generation, and IF signal routing — all manageable over a 10/100 Mbps Ethernet link or USB."
Note: The project explicitly states it is still in development and no hardware or software testing has been performed yet.
Architecture: Two-Layer Software Stack on Custom Hardware
The system uses a dual-layer software architecture that is worth examining closely. The managed C# layer handles high-level orchestration — MQTT messaging, configuration management, command routing — while the native C++ layer running on ChibiOS RTOS handles time-critical hardware interactions with microsecond precision.
DiSEqC Protocol: Bit-Level Signal Generation
DiSEqC (Digital Satellite Equipment Control) is a communication protocol that sends commands through the same coaxial cable carrying the satellite signal. Commands are encoded as 22 kHz tone bursts with specific timing to represent binary data.
The native driver in nf-native/diseqc_native.cpp implements this from scratch using ChibiOS PWM and GPT (General Purpose Timer) peripherals:
// nf-native/diseqc_native.cpp:L22-L37
static PWMConfig pwm_config = {
1000000, // 1MHz PWM clock frequency
45, // PWM period (45 ticks at 1MHz = ~22kHz)
NULL,
{
{PWM_OUTPUT_ACTIVE_HIGH, NULL}, // Channel 0 (TIM4_CH1)
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_DISABLED, NULL}
},
0, 0, 0
};The 22 kHz carrier is generated by configuring TIM4 at 1 MHz clock with a period of 45 ticks, producing a 22.2 kHz square wave. Each DiSEqC bit is encoded through tone duration: a logical '1' uses 500 µs ON / 1000 µs OFF, while a logical '0' uses 1000 µs ON / 500 µs OFF.
The GotoX command (DiSEqC 1.2) for satellite positioning is built as a 5-byte sequence:
// nf-native/diseqc_native.cpp:L238-L258
diseqc_status_t diseqc_goto_angle(float angle)
{
if (angle > g_diseqc.max_angle) angle = g_diseqc.max_angle;
if (angle < -g_diseqc.max_angle) angle = -g_diseqc.max_angle;
uint8_t cmd[5];
cmd[0] = 0xE0; // Command from master, no reply
cmd[1] = 0x31; // Any positioner
cmd[2] = 0x6E; // GotoX
uint8_t direction = (angle < 0) ? 0xE0 : 0xD0;
int16_t angle_16 = (int16_t)(16.0f * fabsf(angle) + 0.5f);
cmd[3] = direction | ((angle_16 >> 8) & 0x0F);
cmd[4] = angle_16 & 0xFF;
diseqc_status_t status = diseqc_transmit(cmd, 5);
if (status == DISEQC_OK) {
g_diseqc.current_angle = angle;
}
return status;
}A dedicated ChibiOS thread (diseqc_tx_thread) handles transmission to avoid blocking the main application while the precise timing segments are clocked out via GPT one-shot interrupts.
W5500 Integration: Register-Level Native Driver + Managed MQTT Channel
This project's W5500 integration goes significantly deeper than typical library-based approaches. The file nf-native/w5500_interop.cpp implements direct SPI register access to the W5500, bypassing the nanoFramework network stack entirely for an alternative MQTT transport path.
The SPI communication follows the W5500's variable-length data frame protocol — a 2-byte address, 1-byte control (block select + read/write), then data:
// nf-native/w5500_interop.cpp:L114-L133
static uint8_t w5500_read8(uint16_t address, uint8_t bsb)
{
uint8_t tx[4] = {
(uint8_t)(address >> 8),
(uint8_t)(address & 0xFF),
(uint8_t)((bsb << 3) | 0x00),
0x00
};
uint8_t rx[4] = {0};
spiSelect(&W5500_SPI_DRIVER);
spiExchange(&W5500_SPI_DRIVER, sizeof(tx), tx, rx);
spiUnselect(&W5500_SPI_DRIVER);
return rx[3];
}On the managed C# side, a custom W5500MqttNetworkChannel class implements the nanoFramework IMqttNetworkChannel interface, routing MQTT traffic through the native W5500 socket API instead of the standard System.Net stack. The transport mode is selectable at runtime via the MqttTransportMode configuration field — either "system-net" (default) or "w5500-native":
// DiSEqC_Control/Program.cs:L370-L395
private static MqttClient CreateMqttClient()
{
MqttClient client = new MqttClient(
_runtimeConfig.MqttBroker,
_runtimeConfig.MqttPort, false, null, null,
MqttSslProtocols.None);
if (UseW5500NativeMqtt)
{
var w5500Channel = new W5500MqttNetworkChannel(
_runtimeConfig.MqttBroker,
_runtimeConfig.MqttPort,
client.Settings.TimeoutOnConnection,
client.Settings.TimeoutOnReceiving);
if (MqttClientChannelInjector.TryInject(
client, w5500Channel, out string error))
{
Debug.WriteLine("[MQTT] W5500 IMqttNetworkChannel injected");
}
}
return client;
}This dual-transport design is a practical choice. The hardware TCP/IP offload in the W5500 keeps MQTT traffic deterministic regardless of what the nanoFramework CLR is doing — critical for a control system where command latency matters.
LNB Power Control via LNBH26PQR
The LNBH26PQR is an integrated LNB supply regulator from ST that handles the analog complexities of satellite signal conditioning. The native driver (nf-native/lnb_control.cpp) communicates via I2C to set voltage selection (13 V for vertical polarization, 18 V for horizontal), enable/disable the 22 kHz tone for band selection, and monitor fault conditions.
Persistent Configuration with F-RAM
Network settings, MQTT broker addresses, and device identity are persisted to a FM24CL16B F-RAM (Ferroelectric RAM) over I2C3. The storage format uses a custom binary header with magic bytes (DCFG), versioning, and a Fletcher-16 checksum for integrity validation (FramConfigurationStorage.cs). F-RAM's unlimited write endurance makes it well-suited for a device that may update configuration frequently without wearing out flash memory.
The serial CLI (USART3 @ 115200 baud) provides direct configuration access with commands like config set mqtt.broker=192.168.1.50, config save, config fram-dump, and config fram-clear ERASE — a thoughtful inclusion for field setup and debugging.
MQTT Command Interface
The MQTT topic structure exposes comprehensive control over the satellite system:
- Position control:
diseqc/command/goto/angle,goto/satellite(hardcoded presets likeastra_19.2e,hotbird_13e) - Manual control:
step_east,step_west,drive_east,drive_west - LNB control:
voltage(13/18V),polarization,tone,band - Configuration:
config/get,config/set,config/save,config/reset
The controller publishes retained status messages on diseqc/status/* topics and implements LWT (Last Will and Testament) on diseqc/availability for clean online/offline detection.
PCB Design Highlights
The 4-layer board uses split ground planes — digital GND, analog GNDA, and power PGND — connected via net ties at controlled star-ground points. This is a deliberate design choice to minimize noise coupling into the sensitive LNB signal path, where even small interference can degrade satellite reception quality.
The power input chain includes PTC fuse → TVS diode → Schottky reverse-polarity protection, followed by a two-stage buck regulation (input → 12V → 3.3V). A filtered +3V3_ANA rail derived through a ferrite bead supplies the MCU's analog reference. KiCad project files include full schematics, PCB layout, and production-ready Gerbers.
Current Status and Limitations
The project is transparent about its development stage: no hardware or software testing has been performed yet. The satellite database is hardcoded to three presets (Astra 19.2°E, Hotbird 13°E, Astra 28.2°E). Calibration (HandleCalibrateReference) is stubbed with a TODO. The build chain relies on a Docker-based nf-interpreter patching workflow that has known Linux compatibility notes documented in the TODO.
Despite this, the codebase demonstrates significant engineering depth — the native DiSEqC driver with microsecond timing, the register-level W5500 transport, and the FRAM persistence layer are all fully implemented.
FAQ
Q1: What is DiSEqC and why does it need precise signal timing? DiSEqC (Digital Satellite Equipment Control) is a protocol developed by Eutelsat for controlling satellite equipment over the coaxial cable. Commands are modulated onto a 22 kHz carrier with bit timing accuracy requirements in the hundreds-of-microseconds range. The ChibiOS GPT-based implementation here achieves 1 µs resolution using TIM5 one-shot mode.
Q2: Why use a W5500 instead of the STM32F407's built-in Ethernet MAC? The STM32F407 has an Ethernet MAC that requires an external PHY (like LAN8720) connected via RMII, plus the software TCP/IP stack (lwIP) consuming significant RAM and CPU. The W5500 offloads the entire TCP/IP stack to hardware, communicates via a simple SPI interface, and frees the MCU to focus on real-time DiSEqC timing. For a nanoFramework application where managed code already has runtime overhead, hardware TCP/IP offload is a practical advantage.
Q3: Can this board be used with other MCUs? The W5500 portion communicates over standard SPI, so the Ethernet connectivity concept is portable. However, the DiSEqC native driver depends on ChibiOS PWM/GPT peripherals specific to STM32, and the PCB is designed specifically for the STM32F407VGT6 in LQFP-100. A different MCU would require a new PCB and native driver adaptation.
Q4: What satellites are supported for the goto command? Currently, only three are hardcoded: Astra 19.2°E, Hotbird 13°E, and Astra 28.2°E. However, the goto/angle MQTT command accepts arbitrary float angles (clamped to ±80°), so any satellite within the motor's travel range can be targeted by publishing the orbital position directly.
Q5: Why F-RAM instead of EEPROM or MCU flash for configuration? F-RAM offers unlimited write endurance (vs. ~100K–1M cycles for EEPROM/flash), byte-level random access, and no write delays. For a device where configuration may be updated frequently via MQTT or serial CLI during commissioning, F-RAM eliminates wear-out concerns entirely.
Q6: Is the W5500 native MQTT transport production-ready? The native transport (w5500-native mode) is implemented and injectable via the MqttClientChannelInjector, but the TODO file notes that hardware smoke testing for W5500 RX/TX has not yet been performed. The default system-net transport uses nanoFramework's built-in networking stack and is the safer option until hardware validation is complete.
Q7: What is the CERN-OHL-P v2 license on the hardware? CERN Open Hardware Licence Version 2 — Permissive is a well-established open-hardware license from CERN. It allows free use, modification, and redistribution of the hardware design (schematics, PCB layout, Gerbers) with minimal restrictions, similar in spirit to the MIT license used for the software.



