ESP32 + SX1276 RTK gateway connecting NTRIP clients to LoRa transmitters for RTCM corrections.

Open Source Surveying Transmitter is an ESP32 firmware that transforms the device into a professional RTK gateway. The system receives GNSS corrections from NTRIP servers over WiFi and retransmits them over long distances via LoRa, enabling precise RTK positioning for rovers without internet access.
NSS → GPIO 18
MOSI → GPIO 27
MISO → GPIO 19
SCK → GPIO 5
RST → GPIO 23
DIO0 → GPIO 26
Every text screen shows a battery icon in the top-right corner of the header:
BATTERY_LOW_PERCENT (10 %) one segment blinksBatteryMonitor (battery_monitor.cpp) auto-detects the PMU at boot:
AXP2101 (T-Beam v1.2) or AXP192 (v1.0/v1.1), via XPowersLib.
The PMU is only read, its power rails are not changed. Boards without a PMU fall back to the VBAT/2 divider
on BATTERY_ADC_PIN (GPIO35), without charge or presence detection. The serial log shows which source was found
(✓ Battery monitor: AXP192 PMU). The same filtered voltage goes into the maintenance telemetry (battery_mv).
Constants are in include/config.h (BATTERY_*, PMU_*).
# Install PlatformIO
pip install platformio
# Clone repository
git clone <repo-url>
cd oss-transmitter
All configuration is now managed via the web interface or by editing the configuration file stored in LittleFS.
On first boot, the device creates a WiFi access point:
OSS-LoRa-TX-XXXXXX (where XXXXXX is chip ID)Connect to this AP and configure:
Alternatively, edit include/config.h for default values:
// NTRIP Caster
#define NTRIP_HOST "91.198.76.2"
#define NTRIP_PORT 2101
#define NTRIP_MOUNTPOINT "RTN4G_VRS_RTCM32"
// GGA Position (for VRS)
#define GGA_LATITUDE 50.2346 // Your latitude
#define GGA_LONGITUDE 19.2084 // Your longitude
#define GGA_ALTITUDE 266.0 // Altitude MSL
// LoRa Radio
#define LORA_FREQUENCY 433E6
#define LORA_SPREADING_FACTOR 7
#define LORA_TX_POWER 20
# Build firmware
platformio run --environment ttgo-t-beam
# Upload to device
platformio run --target upload --environment ttgo-t-beam
# Monitor serial output
platformio device monitor --baud 115200
┌────────────┬───────────────┬──────────────┬─────────────┬──────────────────┐
│ Message ID │ Total Packets │ Packet Index │ Data Length │ Data │
│ (1 byte) │ (1 byte) │ (1 byte) │ (1 byte) │ (0-246 bytes) │
└────────────┴───────────────┴──────────────┴─────────────┴──────────────────┘
Large RTCM messages are automatically fragmented:
Example: RTCM message 1077 (500 bytes) → 3 LoRa packets
See lora-protocol.md for the complete (shared) specification.
[INIT] → [WAITING_FOR_FIRST_RTCM] → [TRANSMISSION] ⇄ [GATHERING]
↓
[ERROR]
| Module | File | Responsibility |
|---|---|---|
| ConfigManager | config_manager.cpp | LittleFS storage, web API |
| WiFiManager | wifi_manager.cpp | WiFi client + AP mode |
| NTRIPClient | ntrip_client.cpp | TCP client, GGA transmission |
| RTCMParser | rtcm_parser.cpp | RTCM3 parsing, CRC validation |
| RTCMBuffer | rtcm_buffer.h | Type-based buffering and grouping |
| LoRaTransmitter | lora_transmitter.cpp | Fragmentation and transmission |
| DisplayManager | display_manager.cpp | OLED status screen |
| BatteryMonitor | battery_monitor.cpp | PMU/ADC battery voltage, charging |
| WebInterface | web_interface_manager.cpp | AsyncWebServer + REST API |
// GPS RTK Observables
1001, 1002, 1003, 1004
// GPS/GLONASS Ephemeris
1019, 1020
// GPS SSR Corrections
1057, 1058, 1059, 1060, 1061, 1062
// GLONASS RTK
1009, 1010, 1011, 1012
// MSM (Multiple Signal Messages)
1074, 1075, 1076, 1077 // GPS L1/L2
1084, 1085, 1086, 1087 // GLONASS
1094, 1095, 1096, 1097 // Galileo
// Reference Station Info
1005, 1006, 1007, 1008, 1033
Due to the limited bandwidth of the LoRa radio link, RTCM messages coming from the NTRIP server must be filtered. The current configuration supports the minimum set of messages necessary to achieve a stable RTK fix, balancing data completeness with transmission constraints.
Filtering is configurable via Web Interface → RTCM Filtering.
In include/config.h:
#define DEBUG // General debug output
#define DEBUG_LORA_PACKETS true // Detailed packet logs
#define DEBUG_LORA_VERBOSE true // Ultra-verbose logging
| Problem | Solution |
|---|---|
| WiFi won’t connect | Check credentials in web interface, increase timeout to 60s |
| NTRIP error 401 | Wrong credentials, verify username/password in web interface |
| No RTCM data | Wrong GGA position (out of VRS range) or mountpoint |
| LoRa not transmitting | Check antenna, SWR, power settings |
| Restart every minute | Watchdog - possible deadlock in NTRIP client |
Open source project - contributions welcome!
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)Important: Read AGENTS.md before starting work - it contains coding standards and workflow for this project.
These live in firmware/shared/docs/ because both firmwares must stay byte-compatible with them:
MIT License - free to use in private and commercial projects.
Bug reports: GitHub Issues
Project Status: Active Development