Back to LoRa Communication System

README

LoRa

End-to-end LoRa communication system: bare-metal STM32 firmware, Go backend, TypeScript dashboard, Android range-test app, and a custom PCB design.

shell
[Phone + LoRa Board] --LoRa 433MHz--> [Base LoRa Board] --USB Serial--> [PC / Go Server]
                                                                              |
                                                                        :8080 HTTP + WS
                                                                              |
                                                                        [Web Dashboard]

Screenshots

Range Test Dashboard

Live Leaflet map with color-coded signal markers, heatmap overlay, and real-time stats.

Range Test Dashboard

Dev Kit Info Dashboard

System info, USB devices, serial ports, and LoRa module configuration via GraphQL.

Dev Kit Dashboard

Components

ComponentStackDescription
FirmwareC99, bare-metal STM32F103, SX1262Transparent UART-to-LoRa serial bridge
BackendGo 1.24, GraphQL, SQLite, WebSocketDev kit dashboard + range test server
FrontendVite + TypeScriptHardware info dashboard
Range AppAndroid / JavaGPS + LoRa signal mapper
HardwareKiCadDog tracker PCB (ESP32 + SX1262 + GPS)

Hardware

  • 2x DX-LR30 boards: STM32F103C8T6 + SX1262 LoRa + CH340C USB-serial
  • Android phone with USB OTG
  • Linux PC

Prerequisites

bash
# Firmware toolchain
sudo apt install gcc-arm-none-eabi stm32flash python3-serial

# Backend + Frontend
# Go 1.24+, Node.js + pnpm
go install github.com/air-verse/air@latest        # Go hot reload
go install github.com/99designs/gqlgen/cmd/gqlgen@latest  # GraphQL codegen

# Serial port access
sudo usermod -aG dialout $USER

Android SDK at ~/Android/Sdk for building the range test app.

Quick Start

Dev Kit Dashboard

bash
make install   # pnpm install + go mod tidy
make dev       # Start Go + Vite with hot reload

  • Dashboard: http://localhost:5173
  • GraphQL Playground: http://localhost:8080/playground

Range Test

bash
# One command: builds firmware, starts server
./dev-all.sh

# Or manually:
cd firmware && make
cd ../backend && CGO_ENABLED=1 go build -o bin/rangetest ./cmd/rangetest
sudo ./bin/rangetest -firmware ../firmware/build/firmware.bin -listen :8080

  • Plug base board into PC, run the server
  • Connect second board to phone via USB OTG
  • Open the LoRa Range Test app, grant location permission
  • Press THIS IS HOME at your starting location
  • Walk around -- phone records GPS + signal strength every 5 seconds
  • Readings appear as color-coded markers on the web dashboard in real time
  • Out of range: phone buffers locally. Back in range: flushes in batches
  • CSV log saved to Downloads/lora_range_YYYYMMDD_HHmmss.csv on phone
  • Firmware Only

    bash
    cd firmware && make                # Build
    make flash                         # Flash to /dev/ttyUSB0
    
    # Test serial bridge between two boards:
    sudo screen /dev/ttyUSB0 9600     # Terminal 1
    sudo screen /dev/ttyUSB1 9600     # Terminal 2
    # Type in one -> appears in the other via LoRa

    To enter bootloader mode: hold KEY, press RST, release KEY.

    Android App

    bash
    cd range_app && ./gradlew assembleDebug
    adb install app/build/outputs/apk/debug/app-debug.apk

    Make Targets

    CommandDescription
    make devStart both servers with hot reload
    make dev-rangetestBuild firmware + range test server, run with sudo
    make buildProduction build (Go binary + Vite bundle)
    make generateRegenerate GraphQL code from schema
    make installInstall all dependencies
    make cleanRemove build artifacts

    Architecture

    Backend (Hexagonal)

    shell
    infrastructure/        HTTP server, GraphQL, WebSocket
      app/                 Service layer
        ports/             Interfaces (SystemReader, USBReader, SerialReader, LoRa)
          adapters/        Implementations (sysfs, lsusb, serial, SQLite)
            domain/        Pure data structs

    Firmware (Bare-Metal C99)

    No HAL, no RTOS. Direct register access. ~2.7KB flash.

    ModulePurpose
    main.cUART <-> LoRa bridge loop
    sx1262.cSX1262 LoRa radio driver
    uart.cIRQ-driven USART1 with ring buffers
    spi.cHardware SPI1 @ 4.5 MHz
    gpio.cPin configuration
    system.c72 MHz clock setup, SysTick
    startup.cVector table, Reset_Handler

    LoRa Parameters

    ParameterValue
    Frequency433.0 MHz
    Spreading Factor7
    Bandwidth125 kHz
    Coding Rate4/5
    TX Power+22 dBm
    Max Packet255 bytes

    Binary Protocol

    Packets are binary structs, base64-encoded over 9600 baud serial.

    TypeCodePayload
    Single Reading0x01seq(2) lat(4) lon(4) rssi(1) snr(1) time_offset(2)
    Batch0x02count(1) + N x reading (max 13)
    Home Location0x03lat(4) lon(4)
    Version0x04major(1) minor(1) patch(1) hash(1)
    Coordinates: int32 degrees x 1e7, big-endian. Base station prepends: [RSSI:-45 SNR:12] <base64>\r\n

    Signal Strength

    RSSIQualityColor
    > -70 dBmExcellentGreen
    -70 to -90GoodYellow
    -90 to -110WeakOrange
    < -110 dBmVery WeakRed

    Range Test API

    RouteDescription
    GET /Leaflet map dashboard
    GET /wsWebSocket (init, reading, home messages)
    GET /api/readingsAll readings JSON
    GET /api/sessionCurrent session + home location

    GraphQL API (Dev Kit Dashboard)

    graphql
    type Query {
      systemInfo: SystemInfo!
      usbDevices: [USBDevice!]!
      serialPorts: [SerialPort!]!
      loraModules: [LoRaModule!]!
      sendATCommand(port: String!, command: String!): ATResponse!
    }

    Measured Throughput

    Packet SizeEffective Rate
    1 byte29 B/s
    32 bytes225 B/s
    100 bytes258 B/s
    250 bytes273 B/s

    Hardware Design

    The hardware/ directory contains a KiCad project for a dog tracker PCB (ESP32 + SX1262 + GPS + accelerometer). Includes schematic, routed PCB, Gerber files, BOM, and a JLCPCB ordering guide.

    Project Structure

    shell
    firmware/                    Bare-metal STM32 + SX1262 firmware
      src/                       C source files
      include/                   CMSIS headers, version defines
      Makefile                   ARM GCC build
      range_test_pc.py           PC-side ping sender
      test_throughput.py         Throughput measurement
    
    backend/                     Go backend
      cmd/server/                Dev kit dashboard entry point
      cmd/rangetest/             Range test server entry point
      domain/                    Data structs
      ports/                     Interfaces
      adapters/                  Implementations (sysfs, lsusb, serial, SQLite, WebSocket)
      app/                       Service layer
      infrastructure/            HTTP + GraphQL
    
    frontend/                    Vite + TypeScript dashboard
      src/components/            Card renderers (system, USB, serial, LoRa)
      src/api.ts                 GraphQL client
    
    range_app/                   Android range test app
      app/src/main/java/         GPS, serial, map, binary protocol, CSV export
    
    hardware/                    KiCad dog tracker PCB
      kicad-dog-tracker/         Schematic, PCB, Gerbers
      BOM.csv                    Bill of materials

    © 2026 Jonathan Leahy · v0.8.1-31-g196fa14