A developer-friendly IoT test data generator CLI written in Rust.
Generate realistic IoT sensor payloads and send them to your system over MQTT, HTTP, or TCP — with zero infrastructure required to get started.
This is not a load-testing tool. It is focused on high-quality, realistic IoT test data.
From setup to sending data over MQTT:
- Random payload generation — temperature, humidity, voltage, current, power, energy, status
- Custom Handlebars templates — full control over payload structure
- Three protocols — MQTT (plain & TLS), HTTP, TCP
- Authentication — username/password, Bearer token, API key header
- Environment variable expansion — keep secrets out of config files with
${VAR_NAME} - Validation & preview — check config and see sample payloads before sending
- Run control — total message count, interval, or hard time limit
- Cross-platform — Linux, macOS, Windows (prebuilt binaries available)
- Docker ready — single image, compose with a local Mosquitto broker included
Download the latest binary for your platform from GitHub Releases. Binaries are built automatically when a release is published:
# Linux x86_64
curl -L https://github.com/iotmertech/iot-data-generator/releases/latest/download/mer-latest-x86_64-unknown-linux-gnu.tar.gz | tar xz
sudo mv mer /usr/local/bin/
# Linux ARM64
curl -L https://github.com/iotmertech/iot-data-generator/releases/latest/download/mer-latest-aarch64-unknown-linux-gnu.tar.gz | tar xz
sudo mv mer /usr/local/bin/
# macOS Apple Silicon
curl -L https://github.com/iotmertech/iot-data-generator/releases/latest/download/mer-latest-aarch64-apple-darwin.tar.gz | tar xz
sudo mv mer /usr/local/bin/
# macOS Intel
curl -L https://github.com/iotmertech/iot-data-generator/releases/latest/download/mer-latest-x86_64-apple-darwin.tar.gz | tar xz
sudo mv mer /usr/local/bin/
# Windows (PowerShell)
Invoke-WebRequest -Uri "https://github.com/iotmertech/iot-data-generator/releases/latest/download/mer-latest-x86_64-pc-windows-msvc.zip" -OutFile mer.zip
Expand-Archive mer.zip -DestinationPath .
# Add mer.exe to your PATHRequires Rust (stable):
git clone https://github.com/iotmertech/iot-data-generator.git
cd iot-data-generator
cargo build --release
# Binary: ./target/release/mer
sudo cp target/release/mer /usr/local/bin/ # optional: add to PATH# Pull and run (no local Rust needed)
docker run --rm ghcr.io/iotmertech/mer:latest --help
# Run an example against a local broker
docker run --rm \
-v $(pwd)/examples:/app/examples:ro \
--network host \
ghcr.io/iotmertech/mer:latest \
mqtt run -f /app/examples/mqtt-basic.yamlRuns mer in a container and connects to your existing broker:
git clone https://github.com/iotmertech/iot-data-generator.git
cd iot-data-generator
cp .env.example .env # set MQTT_BROKER and MQTT_TOPIC
docker compose upFor brokers that require auth, edit the command in docker-compose.yml to use docker/mer.docker-auth.yaml and fill in MQTT_USERNAME / MQTT_PASSWORD in your .env.
No broker is bundled —
merconnects to whatever broker you point it at.
mer init --protocol mqtt > mer.yamlmer preview payload -f mer.yaml
mer preview payload -f mer.yaml -n 5 # show 5 samplesmer validate config -f mer.yamlmer mqtt run -f mer.yaml
mer http run -f mer.yaml
mer tcp run -f mer.yaml| Command | Description |
|---|---|
| `mer init --protocol <mqtt | http |
mer validate config -f <file> |
Validate a config file (exits 0 on success) |
mer preview payload -f <file> [-n N] |
Preview N generated payloads (default 3) |
mer mqtt run -f <file> |
Send MQTT messages |
mer http run -f <file> |
Send HTTP requests |
mer tcp run -f <file> |
Send TCP messages |
protocol: mqtt
target:
broker: "mqtt://mqtt.iotmer.cloud:1883" # or mqtts:// for TLS (port 8883)
topic: "devices/{device_id}/telemetry"
client_id: "mer-{device_id}" # optional, auto-generated if omitted
qos: 1 # 0, 1, or 2
retain: false
device:
count: 5
id_prefix: device # device IDs: device-0000, device-0001, …
payload:
mode: random # or template
run:
total_messages: 100
interval_ms: 1000 # ms between messages
duration_secs: 60 # optional hard time limitprotocol: http
target:
url: "http://ingest.iotmer.cloud:8080/api/v1/devices/{device_id}/data"
method: POST # GET, POST, PUT, PATCH
timeout_secs: 10
headers:
Content-Type: "application/json"
X-Custom-Header: "value"
device:
count: 3
id_prefix: sensor
payload:
mode: random
run:
total_messages: 50
interval_ms: 500protocol: tcp
target:
host: localhost
port: 9000
timeout_secs: 5
line_delimiter: true # append \n to each message
device:
count: 2
id_prefix: node
payload:
mode: random
run:
total_messages: 20
interval_ms: 1000Generates a realistic IoT JSON payload automatically:
{
"device_id": "device-0001",
"device_index": 1,
"seq": 42,
"ts": "2024-01-01T12:00:00Z",
"temperature": 23.45,
"humidity": 61.2,
"voltage": 230.5,
"current": 3.2,
"power": 738.0,
"energy_total": 1523.7,
"status": "online"
}Field ranges:
| Field | Range |
|---|---|
temperature |
15.0 – 45.0 °C |
humidity |
20.0 – 95.0 % |
voltage |
220.0 – 240.0 V |
current |
0.1 – 15.0 A |
power |
10.0 – 3600.0 W |
energy_total |
0.0 – 10000.0 kWh |
status |
online (4/6), offline (1/6), error (1/6) |
payload:
mode: template
template_inline: |
{
"device_id": "{{device_id}}",
"ts": "{{now_utc}}",
"temperature": {{random_float 18.0 35.0}},
"humidity": {{random_float 30.0 90.0}},
"active": {{random_bool}},
"seq": {{seq}},
"level": {{random_int 0 5}}
}Or load from an external file:
payload:
mode: template
template_file: ./my-template.json| Helper | Example | Output |
|---|---|---|
{{now_utc}} |
— | 2024-01-01T12:00:00Z |
{{random_int min max}} |
{{random_int 0 100}} |
42 |
{{random_float min max}} |
{{random_float 10.0 50.0}} |
27.35 |
{{random_bool}} |
— | true or false |
{{device_id}} |
— | sensor-0003 |
{{device.index}} |
— | 3 |
{{seq}} |
— | 17 |
auth:
type: username_password
username: myuser
password: "${MQTT_PASSWORD}"For MQTT, you can also set credentials directly under target:
target:
broker: "mqtts://mqtt.iotmer.cloud:8883"
topic: "test/{device_id}"
auth:
username: myuser
password: "${MQTT_PASSWORD}"auth:
type: bearer
token: "${API_TOKEN}"auth:
type: api_key
header: X-API-Key
value: "${API_KEY}"Use ${VAR_NAME} anywhere in your config to inject secrets at runtime:
auth:
type: bearer
token: "${MY_API_TOKEN}"export MY_API_TOKEN="secret-token"
mer http run -f mer.yamlIf a variable is not set, mer exits with a clear error:
Error: Environment variable 'MY_API_TOKEN' is not set
After each run, mer prints a summary:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Run Summary — MQTT
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Total sent : 100
Successful : 98
Failed : 2
Success rate : 98.0%
Errors : 2
Bytes sent : 24.5 KB
Elapsed : 100.12s
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
The [examples/](examples/) directory contains ready-to-use configs:
| File | Description |
|---|---|
[mqtt-basic.yaml](examples/mqtt-basic.yaml) |
5 devices, 20 messages, random payload, MQTT localhost |
[demo-mqtt.yaml](examples/demo-mqtt.yaml) |
Demo: mqtt.iotmer.cloud, public broker, README GIF |
[mqtt-auth.yaml](examples/mqtt-auth.yaml) |
MQTT with username/password auth |
[mqtt-tls.yaml](examples/mqtt-tls.yaml) |
MQTT over TLS (mqtts://) with auth |
[http-basic.yaml](examples/http-basic.yaml) |
3 sensors, HTTP POST with custom header |
[http-bearer.yaml](examples/http-bearer.yaml) |
HTTP POST with Bearer token auth |
[http-api-key.yaml](examples/http-api-key.yaml) |
HTTP POST with API key header auth |
[tcp-basic.yaml](examples/tcp-basic.yaml) |
2 nodes, TCP with line delimiter |
[custom-template.yaml](examples/custom-template.yaml) |
MQTT with inline Handlebars template |
[smart-home.yaml](examples/smart-home.yaml) |
Smart home sensors (temp, plug, door, CO₂, lux) |
[industrial.yaml](examples/industrial.yaml) |
Factory floor machines (vibration, pressure, flow, RPM) |
[fleet-tracking.yaml](examples/fleet-tracking.yaml) |
Vehicle GPS tracking over HTTP |
[energy-meter.yaml](examples/energy-meter.yaml) |
Three-phase smart energy meters over MQTT |
[weather-station.yaml](examples/weather-station.yaml) |
Weather stations over TCP (wind, rain, UV, pressure) |
[duration-limited.yaml](examples/duration-limited.yaml) |
Time-capped run with duration_secs |
# Build
cargo build
# Run tests
cargo test
# Lint
cargo clippy -- -D warnings
# Format
cargo fmt
# All checks (same as CI)
make checkSee [CONTRIBUTING.md](CONTRIBUTING.md) for the full contribution guide.
Please read our Security Policy before reporting vulnerabilities.
Key points:
- Never commit plaintext secrets — use
${ENV_VAR}in config files - Use
mqtts://for production MQTT connections - Use
https://for production HTTP endpoints
MIT © 2026 iotmertech
