The MultiGeiger is an ESP32-based radioactivity measurement device designed for citizen science projects. It features a modern web interface, multiple connectivity options (WiFi, LoRa, BLE), and environmental sensors for comprehensive environmental monitoring.
- π Radiation Measurement - Accurate detection using Geiger-MΓΌller tubes with real-time CPM/CPS/Β΅Sv/h display
- π Modern Web Interface - Single-page application (SPA) with live updates and responsive design
- π Secure Access - Session-based authentication protects configuration and OTA updates
- π‘ Multiple Connectivity - WiFi, LoRaWAN/TTN, BLE, MQTT with TLS support
- π‘οΈ Environmental Sensors - Optional temperature, humidity, pressure, and air quality monitoring (BMP280/BME280/BME680)
- βοΈ Cloud Integration - MQTT + TTN/LoRaWAN forwarding with OpenSenseMap compatibility
- β° Time Synchronization - Automatic browser-to-device time sync for accurate timestamps
- π Low Power Design - Optimized for battery operation with deep sleep support
Default Credentials:
WiFi Access Point:
- SSID:
MultiGeiger-XXXXXX(last 6 digits of MAC address) - Password:
ESP32Geiger
Web Interface Login:
- Username:
admin - Password:
admin β οΈ Change these immediately in the Settings page!
Steps:
- π Power on your MultiGeiger device
- πΆ Connect to the WiFi access point
MultiGeiger-XXXXXX - π Open http://192.168.4.1 in your browser
- π View live data on the Dashboard (no login required)
- βοΈ Click "Settings" β Login with
admin/adminβ Configure device - π Important: Change password in Settings β Authorization section!
The MultiGeiger features a modern, single-page web application (SPA) with session-based authentication, real-time updates, and a mobile-optimized design:
- π Session Authentication - Secure login with HttpOnly cookies (30min session timeout)
- π Live Updates - Real-time polling (2s interval) for instant feedback
- β° Time Sync - Automatic browser-to-device time synchronization
- π¨ Modern UI - Clean, responsive design built with vanilla JavaScript (no frameworks!)
- π± Mobile-First - Touch-friendly interface optimized for smartphones and tablets
- π§ͺ Mock API - Local development mode with simulated device data
Access Points:
- AP Mode: http://192.168.4.1/
- Network Mode: http://multigeiger.local/ (mDNS)
- Direct IP: http://<device-ip>/
Default Login:
- Username:
admin - Password:
admin β οΈ Change these immediately after first setup!
Protected endpoints (configuration, OTA updates) require login:
- Session Management: HttpOnly cookies with 30-minute sliding expiration
- CSRF Protection: SameSite=Lax cookie attribute
- AP Mode: Authentication skipped (WiFi password provides access control)
- Default Credentials:
admin/admin(β οΈ Change immediately!)
/config- Configuration page/api/config- Configuration API (GET/POST)/api/config/ping- Heartbeat for session keep-alive/update- OTA firmware upload
- β Change default password immediately after first setup
- β Use strong credentials (min. 8 characters, mixed case + numbers)
- β Enable WiFi encryption (WPA2 or better)
β οΈ HTTP only - ESP32 doesn't support HTTPS (use VPN for remote access)- π AP Mode Security - Strong AP password acts as first authentication layer
Login:
curl -X POST http://multigeiger.local/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin"}' \
-c cookies.txtAuthenticated Request:
curl http://multigeiger.local/api/config \
-b cookies.txtStandard 802.11 b/g/n connectivity for:
- Web interface access
- MQTT data publishing
- OTA firmware updates
Long-range connectivity via The Things Network (TTN v3):
- Activation Mode: ABP (Activation By Personalization)
- Frequency Plan: EU868 (868.1 MHz)
- Payload: 10 bytes (radiation data) + 5 bytes (environmental data)
- Compatibility: Works with single-channel gateways (e.g., Dragino LG01-N)
Note: MultiGeiger uses ABP instead of OTAA to ensure compatibility with single-channel LoRaWAN gateways which cannot reliably handle OTAA join procedures.
See LoRa Setup Guide for TTN configuration.
Publish data to any MQTT broker (Mosquitto, HiveMQ, etc.):
- Protocols: MQTT 3.1.1, MQTT over TLS
- Topics: Configurable (default:
multigeiger/<chip-id>/data) - Payload: JSON format with all sensor readings
- QoS: Configurable (0, 1, or 2)
Local data access for mobile apps and nearby devices.
The MultiGeiger can send data to various platforms. Beyond direct uploads, you can leverage TTN's MQTT server to forward data to additional services.
The Things Network provides an MQTT server that pushes real-time uplink messages. You can use this to forward MultiGeiger data to other platforms like OpenSenseMap.
Setup:
-
π Create an API key in your TTN Application:
- Go to Applications β Your Application β API keys
- Click + Add API key
- Grant rights:
Read application traffic - Copy the generated key
-
π‘ Connect to TTN MQTT broker:
- Host:
<region>.cloud.thethings.network(e.g.,eu1.cloud.thethings.network) - Port:
8883(TLS) or1883(plain) - Username:
<application-id>@ttn - Password:
<api-key> - Topic:
v3/<application-id>/devices/+/up
- Host:
-
π Forward data using Node-RED, n8n, or custom scripts
Forward TTN data to OpenSenseMap for public visualization:
Step 1: Create OpenSenseMap Sensor
- Register at https://opensensemap.org/
- Create a new senseBox (manual configuration)
- Add a sensor for radiation (phenomenon: "Ionizing Radiation", unit: "Β΅Sv/h")
- Note your
senseBoxIdandsensorId
Step 2: n8n Workflow for Data Forwarding
Use n8n (or Node-RED) to subscribe to TTN MQTT and forward to OpenSenseMap:
n8n Code Node Example:
const senseBoxId = '<your-sensebox-id>'; // Your Box-ID from OpenSenseMap
const sensorId = '<your-sensor-id>'; // Your Sensor-ID for radiation
// Extract decoded payload from TTN
const rawValue = $input.first().json.message.uplink_message.decoded_payload.uSvph;
const roundedValue = parseFloat(rawValue.toFixed(3)); // Round to 3 decimals
const data = { value: roundedValue };
// POST to OpenSenseMap ingress API
const response = await this.helpers.httpRequest({
method: 'POST',
url: `https://ingress.opensensemap.org/boxes/${senseBoxId}/${sensorId}`,
headers: {
'Content-Type': 'application/json'
},
body: data,
options: {
response: { fullResponse: true }
}
});
return [{ json: {
status: response.statusCode,
data: data
}}];Workflow Overview:
- π¨ MQTT Trigger Node: Subscribe to TTN uplink topic
- π§ Code Node: Extract and transform payload
- π HTTP Request: POST to OpenSenseMap API
Legacy HTTP uploads (sensor.community/madavi/custom) have been removed. Use MQTT or TTN/LoRaWAN forwarding pipelines for cloud ingestion.
- PlatformIO: For ESP32 firmware compilation
- Python 3.11+: For build tools and documentation
- uv: Modern Python package manager (
pip install uv)
The project uses a Makefile for common tasks:
make setup # prepare config + docs venv
make build # build web assets + compile firmware
make flash # upload firmware
make monitor # serial console
make docs # build Sphinx docs
make clean # clean build artifacts
make release v=1.23.0 # tag + push release (injects VERSION/core.hpp)More details and additional targets: see docs/source/development.rst.
MultiGeiger/
βββ src/ # ESP32 firmware (C++)
β βββ app/ # Application logic
β βββ comm/ # Communication modules
β β βββ wifi/ # WiFi, HTTP, mDNS
β β βββ lora/ # LoRaWAN (LMIC)
β β βββ mqtt/ # MQTT client
β βββ sensors/ # Geiger tube, BMP280/BME280/BME680
β βββ main.cpp # Entry point
βββ web/ # Web interface (Single-Page Application)
β βββ index.html # SPA entry point (Dashboard + Status + Settings)
β βββ src/ # JavaScript/CSS sources
β β βββ main.js # Entry point & initialization
β β βββ app.js # Main application class (MultiGeigerApp)
β β βββ style.css # Responsive UI styles
β βββ public/ # Static assets
β β βββ mock/api.js # Mock API for local development
β βββ vite.config.js # Vite build configuration
β βββ package.json # Web dependencies
βββ docs/ # Sphinx documentation
β βββ source/ # reStructuredText files
β βββ images/ # Screenshots
β βββ assembly/ # Assembly PDFs
βββ tools/ # Build & data tools
β βββ ttn_fetcher/ # TTN data downloader
β β βββ fetch_ttn_data.py # CLI tool
β β βββ ttn_daemon.py # Background daemon
β β βββ README.md # TTN fetcher docs
β βββ mqtt_logger/ # MQTT to SQLite logger
βββ scripts/ # Helper scripts
β βββ web_to_header.py # dist/ β gzip Header (genutzt im Makefile)
βββ .github/ # CI/CD workflows
β βββ workflows/
β βββ build.yml # Automated builds
βββ platformio.ini # PlatformIO configuration
βββ Makefile # Build automation
βββ README.md # This file
Download and archive LoRaWAN uplink data from TTN Storage Integration API:
cd tools/ttn_fetcher
# Install dependencies
pip install -r requirements.txt
# Create config
cp ttn_config.example.json ttn_config.json
nano ttn_config.json # Add your TTN API credentials
# Fetch data once
python3 fetch_ttn_data.py --config ttn_config.json
# Run as daemon (poll every 5 minutes)
python3 ttn_daemon.py --config ttn_config.json --interval 300Features:
- πΎ SQLite database storage with automatic deduplication
- π Parse decoded payloads (GM counts, CPM, CPS, tube info)
- π€ Export to JSON or CSV
- π Daemon mode with systemd service support
- π Query historical data with SQL
See tools/ttn_fetcher/README.md for full documentation.
Log MQTT data to SQLite database:
cd tools/mqtt_logger
cp .env.example .env
nano .env # Configure MQTT broker
uv sync
uv run mqtt_logger.pySee tools/mqtt_logger/README.md for details.
- Heltec WiFi Kit 32 (recommended)
- Heltec Wireless Stick
- Generic ESP32 (with modifications)
- β’οΈ Geiger-MΓΌller tube (various types supported: SBM-20, SBM-19, SI-3BG, etc.)
- β‘ High voltage generator (400-500V for GM tube)
- π‘οΈ Optional: BMP280/BME280/BME680 environmental sensor (IΒ²C)
- BMP280: Temperature + Pressure only
- BME280: Temperature + Humidity + Pressure
- BME680: Temperature + Humidity + Pressure + Air Quality (Gas)
- π‘ Optional: LoRa module (SX1276/RFM95W for TTN)
See hardware documentation in docs/hardware/ for schematics and PCB files.
Download the detailed assembly instructions (German):
π https://multigeiger.readthedocs.org/
Comprehensive documentation with:
- π Multi-language support (English + Deutsch) - use the language switcher in the lower right
- π Versioned docs (latest, stable, specific releases)
- π Full-text search
- π± Mobile-optimized
- Setup Instructions - WiFi, MQTT, platform configuration
- LoRa/TTN Setup - ABP configuration for The Things Network
- Deployment Guide - Production setup and troubleshooting
- FAQ - Common questions and solutions
- Development Guide - Contributing and development setup
make docs # Build with Sphinx
make docs-serve # Serve at http://localhost:8000Generated docs: docs/build/html/index.html
- π Live Radiation Map: https://multigeiger.citysensor.de/ - Real-time data from deployed sensors
- ποΈ Ecocurious Project Page: https://ecocurious.de/projekte/multigeiger-2/ (German)
- π₯ Video Tutorials: https://play.wa.binary-kitchen.de/_/global/raw.githubusercontent.com/Friedjof/rc3_2020/main/main.json (German)
- π¬ Discussion & Support: GitHub Issues and Discussions
Contributions are welcome! π
- π Bug Reports: Open an issue with reproduction steps
- β¨ Feature Requests: Describe your use case
- π§ Pull Requests: Fork, branch, test, and submit
Quality Standards:
- β Automated CI/CD: GitHub Actions runs builds and tests on all PRs
- π Documentation: Update docs for user-facing changes
- π§ͺ Testing: Ensure existing functionality works
See .github/README.md for CI/CD details.
See LICENSE file for details.
See AUTHORS file for contributors.
Made with β€οΈ by the Ecocurious community for citizen science and environmental monitoring
Support the project: β Star this repo | π Report bugs | π Improve docs | π‘ Share ideas




