Device Simulator
The Multiflexmeter project includes a device simulator for testing the dashboard without requiring physical hardware. The simulator generates generic test data for development purposes.
Overview
Section titled “Overview”The device simulator can operate in two modes:
- LOCAL Mode (default): Sends data directly to the Astro
/api/uplinkendpoint - TTN Mode: Sends data via The Things Network HTTP Integration API
The simulator generates data for multiple devices with:
- Generic sensor data payloads
- LoRaWAN signal quality (RSSI, SNR)
- GPS coordinates from real Dutch poldermill locations
Quick Start
Section titled “Quick Start”Basic Usage
Section titled “Basic Usage”Start the simulator with default settings:
npm run simulatorThis will:
- Simulate 3 devices (configurable)
- Send measurements every 10 seconds
- Send data to
http://localhost:4321/api/uplink - Generate generic test sensor data
Custom Configuration
Section titled “Custom Configuration”Customize behavior with environment variables:
# Custom number of devicesNUM_DEVICES=5 npm run simulator
# Custom interval (milliseconds)INTERVAL=5000 npm run simulator
# Custom endpointLOCAL_ENDPOINT=http://192.168.1.100:4321/api/uplink npm run simulator
# Combine multiple optionsNUM_DEVICES=2 INTERVAL=15000 npm run simulatorOperating Modes
Section titled “Operating Modes”LOCAL Mode (Default)
Section titled “LOCAL Mode (Default)”Sends data directly to your local Astro development server.
Usage:
npm run simulatorOr with explicit mode:
MODE=LOCAL npm run simulatorConfiguration:
# .env or command lineMODE=LOCALLOCAL_ENDPOINT=http://localhost:4321/api/uplinkNUM_DEVICES=3INTERVAL=10000Data Flow:
┌────────────────┐│ Simulator ││ (Node.js) │└────────┬───────┘ │ HTTP POST │ (simplified format) v┌────────────────┐│ /api/uplink ││ (Astro) │└────────┬───────┘ │ v┌────────────────┐│ Data Store ││ (SQLite/Redis) │└────────┬───────┘ │ SSE v┌────────────────┐│ Dashboard ││ (Browser) │└────────────────┘TTN Mode
Section titled “TTN Mode”Sends data via The Things Network HTTP Integration API, simulating real TTN webhook delivery.
Usage:
npm run simulator:ttnOr with explicit configuration:
MODE=TTN TTN_APP_ID=your-app-id TTN_API_KEY=your-api-key npm run simulatorConfiguration:
# .env or command lineMODE=TTNTTN_APP_ID=multiflexmeter-testTTN_API_KEY=NNSXS.YOUR.API.KEYTTN_REGION=eu1NUM_DEVICES=3INTERVAL=10000Data Flow:
┌────────────────┐│ Simulator ││ (Node.js) │└────────┬───────┘ │ HTTP POST │ (TTN format) v┌────────────────┐│ TTN ││ HTTP API │└────────┬───────┘ │ Webhook v┌────────────────┐│ /api/uplink ││ (Astro) │└────────────────┘Configuration Options
Section titled “Configuration Options”Environment Variables
Section titled “Environment Variables”| Variable | Default | Description |
|---|---|---|
| MODE | LOCAL | Operating mode: LOCAL or TTN |
| NUM_DEVICES | 3 | Number of devices to simulate |
| INTERVAL | 10000 | Milliseconds between measurements |
| LOCAL_ENDPOINT | http://localhost:4321/api/uplink | Endpoint for LOCAL mode |
| TTN_APP_ID | - | TTN application ID (required for TTN mode) |
| TTN_API_KEY | - | TTN API key (required for TTN mode) |
| TTN_REGION | eu1 | TTN region (eu1, nam1, au1, etc.) |
Device Locations
Section titled “Device Locations”The simulator uses real Dutch poldermill locations:
| Device | Name | City | Coordinates | Altitude |
|---|---|---|---|---|
| 1 | Mallemolen | Gouda | 52.0116°N, 4.7104°E | 5m |
| 2 | De Roos | Delft | 52.0116°N, 4.3571°E | 3m |
| 3 | Molen de Valk | Leiden | 52.1595°N, 4.4869°E | 2m |
| 4 | De Zwaan | Rotterdam | 51.9225°N, 4.4792°E | 4m |
| 5 | Windlust | Amsterdam | 52.3676°N, 4.9041°E | 1m |
Devices cycle through these locations if NUM_DEVICES exceeds 5.
Simulated Data
Section titled “Simulated Data”AS IS (Version 3.7.0) - Generic Test Data
Section titled “AS IS (Version 3.7.0) - Generic Test Data”The simulator generates generic test data for development purposes. This mimics the passthrough behavior of the actual firmware.
Key Points:
- The simulator sends test payload data via FPort 1
- Data format is arbitrary and for testing only
- Real devices send raw sensor module data (format depends on sensor at 0x36)
- Application-level decoding is required for both simulated and real data
Binary Format (test data example):
┌────────────────────────────────────────┐│ Generic test payload (variable bytes) ││ Used for development/testing only │└────────────────────────────────────────┘TO BE (Version 3.8.0)
Section titled “TO BE (Version 3.8.0)”Future versions will implement structured data with standardized formats:
- Defined
tx_pkt_tstructure - Consistent data format across all devices
- Firmware-level validation
Version Data (FPort 2)
Section titled “Version Data (FPort 2)”Sent once on simulator startup, matching firmware behavior.
Binary Format:
┌────────┬─────────┬─────────┬─────────┬─────────┐│ Byte 0 │ Byte 1 │ Byte 2 │ Byte 3 │ Byte 4 │├────────┼─────────┼─────────┼─────────┼─────────┤│ 0x10 │ FW_MSB │ FW_LSB │ HW_MSB │ HW_LSB │└────────┴─────────┴─────────┴─────────┴─────────┘Example: 10 03 07 03 07
- Command:
0x10 - Firmware: v3.7
- Hardware: v3.7
Test Data Generation
Section titled “Test Data Generation”The simulator generates randomized test data to simulate device activity:
// Generate test payload (format varies by implementation)const testPayload = generateTestData();Characteristics:
- Random payload generation for testing
- Simulates device uplink behavior
- Tests dashboard data processing pipeline
- Not intended to match specific sensor formats
Signal Quality
Section titled “Signal Quality”Realistic LoRaWAN signal metrics:
rssi = -80 + (Math.random() * 20); // -80 to -60 dBmsnr = 5 + (Math.random() * 5); // 5 to 10 dBTypical Values:
- RSSI: -70 dBm (good signal)
- SNR: 7.5 dB (moderate quality)
Message Formats
Section titled “Message Formats”LOCAL Mode Format
Section titled “LOCAL Mode Format”Simplified format sent directly to /api/uplink:
{ "devEui": "0000000000000001", "devAddr": "00000001", "fPort": 1, "payload": "base64_encoded_test_data", "receivedAt": "2025-11-02T10:30:00Z", "rssi": -72.3, "snr": 7.8, "latitude": 52.0116, "longitude": 4.7104, "altitude": 5}TTN Mode Format
Section titled “TTN Mode Format”Complete TTN webhook format:
{ "end_device_ids": { "device_id": "mfm-00000001", "application_ids": { "application_id": "multiflexmeter-test" }, "dev_eui": "0000000000000001", "dev_addr": "00000001" }, "uplink_message": { "f_port": 1, "f_cnt": 42, "frm_payload": "base64_encoded_test_data", "rx_metadata": [{ "gateway_ids": { "gateway_id": "simulator-gateway" }, "rssi": -72.3, "snr": 7.8, "timestamp": 1730548200000 }], "settings": { "data_rate": { "lora": { "bandwidth": 125000, "spreading_factor": 7 } }, "frequency": "868100000" }, "received_at": "2025-11-02T10:30:00Z" }, "locations": { "user": { "latitude": 52.0116, "longitude": 4.7104, "altitude": 5, "source": "SOURCE_REGISTRY" } }}Multi-Device Simulation
Section titled “Multi-Device Simulation”Running Multiple Devices
Section titled “Running Multiple Devices”Simulate multiple devices simultaneously:
# Simulate 5 devicesNUM_DEVICES=5 npm run simulatorEach device:
- Has a unique Device EUI (
0000000000000001,0000000000000002, etc.) - Has a unique location from the poldermill list
- Operates independently with randomized states
- Sends measurements with slight time offsets (0-2 second stagger)
Viewing Multi-Device Data
Section titled “Viewing Multi-Device Data”Devices Overview:
http://localhost:4321/devicesShows all simulated devices with status indicators.
Filter by Device:
http://localhost:4321/dashboard?device=0000000000000001Shows data for a specific device only.
All Devices:
http://localhost:4321/dashboardShows combined data from all devices.
Development Workflow
Section titled “Development Workflow”Typical Development Session
Section titled “Typical Development Session”-
Start Astro Dev Server:
Terminal window npm run dev -
Open Dashboard in browser:
http://localhost:4321/devices -
Start Simulator in new terminal:
Terminal window npm run simulator -
Watch Live Updates as data appears in dashboard
-
Stop Simulator with
Ctrl+Cto see statistics:=== Simulator Stopped ===Mallemolen (0000000000000001): 127 frames sentDe Roos (0000000000000002): 125 frames sentMolen de Valk (0000000000000003): 128 frames sent
Combined Start (Convenience)
Section titled “Combined Start (Convenience)”Start both Astro and simulator together:
npm run dev:dashboardThis runs npm run dev and npm run simulator concurrently.
Testing Different Scenarios
Section titled “Testing Different Scenarios”High-Frequency Updates:
INTERVAL=3000 npm run simulator # Every 3 secondsSingle Device Testing:
NUM_DEVICES=1 npm run simulatorMany Devices (Stress Test):
NUM_DEVICES=10 INTERVAL=5000 npm run simulatorCustom Endpoint (testing deployed site):
LOCAL_ENDPOINT=https://your-site.vercel.app/api/uplink npm run simulatorTroubleshooting
Section titled “Troubleshooting”Simulator Won’t Connect
Section titled “Simulator Won’t Connect”Problem: Error: ECONNREFUSED
Solution:
- Ensure Astro dev server is running (
npm run dev) - Check endpoint URL matches server address
- Verify firewall allows local connections
No Data in Dashboard
Section titled “No Data in Dashboard”Problem: Simulator runs but dashboard shows no data
Solution:
- Check browser console for errors
- Verify SSE connection at
/api/stream - Check Astro console for uplink processing messages
- Ensure database is initialized (SQLite file created)
TTN Mode Failures
Section titled “TTN Mode Failures”Problem: [TTN ERROR] HTTP 401 or HTTP 404
Solution:
- Verify
TTN_APP_IDmatches your TTN application - Check
TTN_API_KEYis valid and has correct permissions - Ensure webhook is configured in TTN console
- Verify
TTN_REGIONmatches your application region
Simulator Blocks Production
Section titled “Simulator Blocks Production”Problem: Simulator data rejected in production
Expected Behavior: This is intentional for security.
Solution:
- Use simulator only in development (
npm run dev) - Use real devices or TTN webhook in production
Advanced Usage
Section titled “Advanced Usage”Custom Device Configuration
Section titled “Custom Device Configuration”Edit simulator/device-simulator.mjs to customize:
Add New Location:
const DEVICE_LOCATIONS = [ // ... existing locations ... { name: 'My Poldermill', city: 'Utrecht', latitude: 52.0907, longitude: 5.1214, altitude: 8 },];Change Firmware Version:
const deviceInfo = { // ... existing fields ... hwVersion: { major: 3, minor: 7 }, fwVersion: { major: 3, minor: 7 },};Customize Test Data:
function generateTestData() { // Create your own test payload format return Buffer.from([0x01, 0x02, 0x03, ...]);}Programmatic Usage
Section titled “Programmatic Usage”Import and use programmatically in your own scripts:
import { DeviceSimulator } from './simulator/device-simulator.mjs';
const simulator = new DeviceSimulator({ name: 'Test Device', devEui: '0000000000000099', location: { latitude: 52.0, longitude: 4.7, altitude: 5 }, hwVersion: { major: 3, minor: 7 }, fwVersion: { major: 3, minor: 7 },});
await simulator.start();Next Steps
Section titled “Next Steps”- Real-time Dashboard - Understanding the dashboard architecture
- Data Formats - Complete data format specifications
- TTN Setup - Configuring The Things Network