I want this guide to constantly evolve. If you want to suggest changes I could make I have uploaded the markdown file from which this blog post is being generated here: https://cdn.merlin-glander.de/posts/assets/cheap-meshtastic-node.md For changes you want to suggest, email me here: merlin@merlin-glander.de
This guide documents how to build a fully custom Meshtastic node using a generic ESP32 DevKit and an SX1262 LoRa module, powered from a single 18650 battery with proper charging, protection, and voltage regulation.
The focus of this guide is doing it the correct way:
- No runtime pin hacks
- No trial-and-error wiring
- No prebuilt Meshtastic boards
- Fully reproducible and explainable
If you are comfortable with soldering and the terminal, this guide is for you.
Why Build a Custom Node?
Building your own Meshtastic node gives you:
- Full control over hardware and power design
- Better understanding of how Meshtastic actually works
- Flexibility to reuse parts you already have
- No vendor lock-in
- Easier debugging and modification later
This setup is electrically equivalent to many commercial nodes — just without the markup.
Important Concepts (Read This First)
Radio pins are compile-time only
Meshtastic does not allow changing LoRa radio pins at runtime via the CLI or the mobile app.
All radio pin assignments:
- Are defined in firmware
- Must be compiled in
- Require a rebuild if you change wiring
If you try to set radio pins using the CLI, it will not work.
SX1262 is not “just SPI”
Unlike older SX127x radios, the SX1262 requires additional control signals:
BUSY— mandatoryDIO1— mandatory (interrupt/IRQ)NRST— strongly recommended
If any of these are missing or miswired, the radio will fail with:
SX126x init result -2
This error almost always means wiring, not software.
Hardware Used
Core electronics
- ESP32 DevKit (ESP32-WROOM-32, e.g.
esp32doit-devkit-v1) - SX1262 LoRa module (RA-62 / HT-RA62 / LLCC68-compatible)
- 18650 Li-ion battery
Power system
- TP4056 with battery protection
- MT3608 DC-DC boost converter
Misc
- USB cable (flashing/debugging)
- Jumper wires or soldered connections
- Antenna appropriate for your region (EU868 / US915, etc.)
⚠️ SX1262 is 3.3 V only. Never connect it to 5 V.
Power System Design (Battery + Charging)
This build uses a safe and robust battery system suitable for unattended operation.
Components and roles
TP4056 (with protection)
Charges a single 18650 cell via USB
Includes:
- Over-charge protection
- Over-discharge protection
- Short-circuit protection
Battery connects to
B+ / B-Load is taken from
OUT+ / OUT-
MT3608 boost converter
- Boosts battery voltage (≈3.0–4.2 V)
- Output is adjusted to 5.0 V
- Feeds the ESP32 via its VIN / VN pin
Power wiring overview
18650 Battery
│
▼
TP4056 (with protection)
├─ B+ / B- → Battery
└─ OUT+ / OUT- → MT3608 input
│
▼
MT3608 (set to 5.0 V)
│
▼
ESP32 VIN / VN
Why this works well
- ESP32 receives stable voltage even as battery discharges
- Battery is protected from damage
- Charging and load paths are properly isolated
- SX1262 is powered from ESP32’s onboard 3.3 V regulator
SX1262 ↔ ESP32 Pin Assignment
This is the exact pin mapping used in the working firmware.
| SX1262 Pin | ESP32 GPIO | Purpose |
|---|---|---|
| VCC | 3V3 | Radio power |
| GND | GND | Common ground |
| NSS (CS) | GPIO5 | SPI chip select |
| SCK | GPIO18 | SPI clock |
| MOSI | GPIO23 | SPI data (ESP32 → radio) |
| MISO | GPIO19 | SPI data (radio → ESP32) |
| NRST | GPIO14 | Radio reset |
| DIO1 | GPIO26 | Radio interrupt (IRQ) |
| BUSY | GPIO25 | Radio busy indicator |
| DIO2 | — | Not used |
| DIO3 | — | Not used |
| TXEN | — | Not used |
| RXEN | — | Not used |
Wiring Checklist
Before powering on:
- MT3608 output verified at 5.0 V
- ESP32 powered via VIN / VN
- SX1262 powered from 3V3
- NSS → GPIO5
- BUSY → GPIO25
- DIO1 → GPIO26
- NRST → GPIO14
- All grounds connected
- Antenna attached
Most SX1262 problems are wiring mistakes — triple-check this.
Getting the Meshtastic Firmware
Meshtastic uses PlatformIO, not the Arduino IDE.
git clone https://github.com/meshtastic/firmware.git
cd firmware
git checkout develop
Creating a Custom Variant
Meshtastic organizes board definitions as variants.
Create a new variant directory:
cd variants
mkdir esp32_devkit_sx1262
Directory structure:
variants/esp32_devkit_sx1262/
├── platformio.ini
└── variant.h
platformio.ini (Exact Working Configuration)
[env:esp32_devkit_sx1262]
extends = esp32_base
board = esp32doit-devkit-v1
build_flags =
${esp32_base.build_flags}
-D PRIVATE_HW
-I variants/esp32_devkit_sx1262
lib_deps =
${esp32_base.lib_deps}
What this does
- Uses Meshtastic’s ESP32 defaults
- Targets a generic ESP32 DevKit
- Marks this as private/custom hardware
- Ensures your variant header is included
variant.h (Exact Working Configuration)
#pragma once
// Minimal custom variant for ESP32 DevKit + external SX1262 module
// --- Radio selection ---
#define USE_SX1262
// --- SX1262 control pins ---
#define SX126X_CS 5 // NSS / CS
#define SX126X_RESET 14 // NRST
#define SX126X_BUSY 25 // BUSY
#define SX126X_DIO1 26 // DIO1 (IRQ)
// --- SPI bus (ESP32 VSPI) ---
#define LORA_SCK 18
#define LORA_MOSI 23
#define LORA_MISO 19
// Compatibility with older Meshtastic code paths
#define LORA_CS SX126X_CS
#define LORA_DIO1 SX126X_DIO1
// Maximum safe output power for most SX1262 modules
#define SX126X_MAX_POWER 22
// Enable these ONLY if your module explicitly requires them
// #define SX126X_DIO3_TCXO_VOLTAGE 1.8
// #define SX126X_DIO2_AS_RF_SWITCH
Building the Firmware
From the firmware root:
pio run -e esp32_devkit_sx1262
If the build complains about mklittlefs, install a prebuilt mklittlefs binary and ensure it is available in your PATH.
Flashing the ESP32
pio run \
-e esp32_devkit_sx1262 \
-t upload \
--upload-port /dev/serial/by-id/usb-1a86_USB_Serial-if00-port0
A successful flash ends with:
Hash of data verified.
Hard resetting via RTS pin...
[SUCCESS]
Verifying Radio Initialization
Open a serial monitor:
picocom -b 115200 /dev/serial/by-id/usb-1a86_USB_Serial-if00-port0
Expected output:
SX126xInterface(cs=5, irq=26, rst=14, busy=25)
SX126x init result 0
If you see init result -2, recheck wiring.
Connecting to Meshtastic
Serial
meshtastic --port /dev/serial/by-id/usb-1a86_USB_Serial-if00-port0 --info
Bluetooth
- Open the Meshtastic mobile app
- Connect via BLE to
Meshtastic_XXXX
Setting the Regulatory Region
Always set the correct region:
meshtastic --port /dev/serial/... --set lora.region EU_868
Common Pitfalls
- Confusing GPIO numbers with board pin labels
- Leaving BUSY unconnected
- Powering SX1262 from 5 V
- Feeding raw battery voltage into VIN
- Expecting radio pin configuration via CLI
- Forgetting the antenna
Final Thoughts
This build gives you:
- A safe battery-powered Meshtastic node
- Full hardware control
- A clean, maintainable firmware setup
- Zero dependence on proprietary boards
Once the SX1262 initializes correctly, Meshtastic is extremely stable and reliable.
Happy building 🛰️