A comprehensive IoT protocol gateway that automatically detects device types and normalizes data from multiple transport protocols into a unified MQTT stream. The MPA service acts as a smart intermediary that can handle various IoT devices and communication protocols while providing a standardized output format.
- π Multi-Transport Support - HTTP, MQTT Subscriber, WebSocket, SocketIO
- π Automatic Device Detection - Path, header, payload, and rule-based detection
- π Device-Specific Parsing - RAK2270, RAK7200, Dragino LHT65, and extensible
- π‘ Unified MQTT Output - All devices publish to standardized MQTT format
- π Backward Compatible - Existing ChirpStack integrations work unchanged
- β‘ Real-time Communication - WebSocket and SocketIO for live data
- π§ Runtime Management - Dynamic device profile and parser registration
- π₯ Comprehensive Health Monitoring - Per-transport and global health checks
- π Structured Logging - Detailed operational insights
- π Horizontally Scalable - Multiple concurrent connections and protocols
IoT Devices (Various Makes/Models)
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Transport Protocols β
βββββββββββββββ¬ββββββββββββββ¬ββββββββββββββ¬ββββββββββββββββββ€
β HTTP β MQTT β WebSocket β SocketIO β
β POST /http β Subscriber β /ws β /socket.io/ β
βββββββββββββββ΄ββββββββββββββ΄ββββββββββββββ΄ββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Device Detection β
βββββββββββββββ¬ββββββββββββββ¬ββββββββββββββ¬ββββββββββββββββββ€
β Path β Header β Payload β Rules β
β Based β Based β Analysis β Conditional β
βββββββββββββββ΄ββββββββββββββ΄ββββββββββββββ΄ββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Device-Specific Parsers β
βββββββββββββββ¬ββββββββββββββ¬ββββββββββββββ¬ββββββββββββββββββ€
β RAK2270 β RAK7200 β Dragino LHT β Generic LoRaWAN β
β Tracker β EnvSensor β 65 β (fallback) β
βββββββββββββββ΄ββββββββββββββ΄ββββββββββββββ΄ββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Unified MQTT Output β
β (Standardized Message Format) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MQTT Broker β
β (Your Main Data Stream) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Copy the example configuration file:
cp .env.example .envEdit the configuration files:
configs/config.yaml- Main configuration.env- Environment variables (optional)
# Install dependencies
make deps
# Build the binary
make build
# Run the service
make run
# Or run in development mode
make devFROM golang:1.24-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main ./cmd/mpa
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/main .
COPY --from=builder /app/configs ./configs
CMD ["./main"]Create a .env file with the following example settings:
# Server Configuration
SERVER_LOG_LEVEL=info
SERVER_API_PORT=8080
SERVER_READ_TIMEOUT=30
SERVER_WRITE_TIMEOUT=30
# MQTT Output Configuration (where to publish normalized data)
MQTT_BROKER=localhost
MQTT_PORT=1883
MQTT_CLIENT_ID=mpa-service
MQTT_USERNAME=admin
MQTT_PASSWORD=public
MQTT_TOPIC=mpa/devices/data
MQTT_QOS=0
MQTT_RETAINED=false
# HTTP Protocol Configuration
PROTOCOLS_HTTP_ENABLED=true
PROTOCOLS_HTTP_PATH=/http
# SMS Protocol Configuration
PROTOCOLS_SMS_ENABLED=false
PROTOCOLS_SMS_PROVIDER=twilio
PROTOCOLS_SMS_API_KEY=
PROTOCOLS_SMS_API_SECRET=
PROTOCOLS_SMS_WEBHOOK_URL=
PROTOCOLS_SMS_PORT=8081
# WebSocket Protocol Configuration
PROTOCOLS_WEBSOCKET_ENABLED=false
PROTOCOLS_WEBSOCKET_PATH=/ws
PROTOCOLS_WEBSOCKET_PORT=8082
# MQTT Protocol Configuration
PROTOCOLS_MQTT_PROTOCOL_ENABLED=false
PROTOCOLS_MQTT_PROTOCOL_PORT=1884http:
port: 8080 # Server port
read_timeout: 30 # Request read timeout (seconds)
write_timeout: 30 # Response write timeout (seconds)
idle_timeout: 60 # Idle connection timeout (seconds)mqtt:
broker: "localhost" # MQTT broker host
port: 1883 # MQTT broker port
client_id: "mpa-service" # MQTT client ID
username: "" # MQTT username (optional)
password: "" # MQTT password (optional)
topic: "mpa/devices/data" # MQTT topic to publish to
qos: 0 # Quality of Service (0, 1, or 2)
retained: false # Retain messages flagAll configuration can also be set via environment variables:
export HTTP_PORT=8080
export MQTT_BROKER=localhost
export MQTT_USERNAME=myuser
export MQTT_PASSWORD=mypassprotocols:
# Generic HTTP transport (recommended)
http:
enabled: true
path: "/http" # Universal endpoint for all HTTP devices
# WebSocket transport
websocket:
enabled: true
path: "/ws"
port: 8082
# MQTT subscriber transport
mqtt_protocol:
enabled: true
port: 1884 # MQTT broker port for incoming devices
# Backward compatibility
chirpstack:
enabled: false # Legacy endpoint (optional)
path: "/chirpstack"The service automatically detects these device types:
- RAK2270 - RAK Sticker Tracker (GPS/LoRaWAN)
- RAK7200 - RAK WisNode Track Lite (Environmental)
- Dragino LHT65 - Temperature & Humidity Sensor
- Generic LoRaWAN - Any unidentified LoRaWAN device
- Generic HTTP - Custom HTTP IoT devices
- Extensible - Easy to add new device profiles
- In your ChirpStack Application Server:
- Go to Applications β [Your App] β Integrations
- Add HTTP Integration
- Set URL to:
http://your-mpa-service:8080/http - Set Method to:
POST - Enable events: up, join, ack, txack, status, location, log, integration
The service accepts standard ChirpStack webhook payloads and automatically detects device types:
{
"deduplicationId": "12345-abc-def",
"time": "2024-01-15T14:30:00.123456Z",
"event": "up",
"deviceInfo": {
"tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242",
"tenantName": "SpaceDF",
"applicationId": "ca739e26-7b67-4f14-b95e-c4ca0e4c8c9e",
"applicationName": "Asset Trackers",
"deviceName": "RAK2270-Tracker-001",
"devEui": "1122334455667788",
"devAddr": "aabbccdd"
},
"uplinkEvent": {
"fCnt": 1543,
"fPort": 2,
"data": "AWcB6AIBAWE=",
"object": {
"latitude": 52.520008,
"longitude": 13.404954,
"altitude": 100.5,
"battery": 85,
"temperature": 23.4,
"motion": true
},
"rxInfo": [
{
"gatewayId": "gateway-001-berlin",
"rssi": -85,
"snr": 12.3,
"location": {
"latitude": 52.51945,
"longitude": 13.40443,
"altitude": 65.0
}
}
]
}
}{
"time": "2024-01-15T14:25:12.654321Z",
"event": "up",
"deviceInfo": {
"tenantName": "SpaceDF",
"applicationName": "Environmental Monitoring",
"deviceName": "RAK7200-Office-A12",
"devEui": "9988776655443322"
},
"uplinkEvent": {
"fCnt": 89,
"fPort": 1,
"object": {
"temperature": 25.3,
"humidity": 65.2,
"pressure": 1013.25,
"battery": 3.6
}
}
}{
"time": "2024-01-15T14:35:45.789012Z",
"event": "up",
"deviceInfo": {
"deviceName": "Custom-Sensor-456",
"devEui": "5566778899aabbcc"
},
"uplinkEvent": {
"object": {
"temperature": 25.3,
"humidity": 65.2
}
}
}The service publishes the following format to MQTT:
{
"device_id": "aa555a0026012345",
"device_name": "Device-001",
"timestamp": "2023-01-01T12:00:00Z",
"raw_data": "SGVsbG8gV29ybGQ=",
"decoded_data": {
"temperature": 23.5,
"humidity": 65
},
"rssi": -45,
"snr": 7.2,
"location": {
"latitude": 52.1234,
"longitude": 4.5678,
"altitude": 10
},
"port": 1,
"frame_counter": 123
}POST /http- Universal HTTP endpoint (auto-detects device types)GET /ws- WebSocket upgrade endpointGET /socket.io/- SocketIO endpoint- MQTT Subscriber - Subscribes to configured topics automatically
GET /health- Global service health (all transports + device profiles)GET /health/http- HTTP transport healthGET /health/websocket- WebSocket transport healthGET /health/mqtt-subscriber- MQTT subscriber healthGET /health/socketio- SocketIO transport healthGET /device-profiles- List all supported device types and detection rules
POST /chirpstack- Legacy ChirpStack endpoint (if enabled)
The MPA architecture makes it easy to add new protocol handlers:
- Create a new handler package in
internal/handlers/[protocol-name]/ - Implement the
ProtocolHandlerinterface:Name() string- Protocol namePath() string- HTTP endpoint pathMethod() string- HTTP methodHandle(c echo.Context) error- Request handlerHealthCheck(c echo.Context) error- Health check
- Register the handler in the configuration
- Update the config file to enable the new protocol
make build # Build for current platform
make build-linux # Build for Linux AMD64
make build-windows # Build for Windows AMD64
make build-arm # Build for ARM
make test # Run tests
make clean # Clean build artifactsmake test- Tooling (once):
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.4.0 go install github.com/securego/gosec/v2/cmd/gosec@master
- MPA service:
cd mpa-service (optional) golangci-lint run gosec ./...
- Start the service:
make dev- Test RAK2270 device:
curl -X POST http://localhost:8080/http \
-H "Content-Type: application/json" \
-d '{
"event": "up",
"deviceInfo": {
"deviceName": "RAK2270-Tracker-001",
"devEui": "1122334455667788"
},
"uplinkEvent": {
"object": {
"latitude": 52.520008,
"longitude": 13.404954,
"battery": 85
}
}
}'- Test generic device:
curl -X POST http://localhost:8080/http \
-H "Content-Type: application/json" \
-d '{
"id": "sensor123",
"name": "Temperature Sensor",
"temperature": 25.5
}'- Check health and device profiles:
curl http://localhost:8080/health
curl http://localhost:8080/device-profiles- Test WebSocket (requires a WebSocket client):
const ws = new WebSocket('ws://localhost:8080/ws');
ws.send(JSON.stringify({
device_id: "ws_sensor01",
temperature: 22.1
}));The service provides structured logging with different levels:
- INFO: General operational messages
- WARN: Warning messages
- ERROR: Error messages
- Invalid JSON payloads return HTTP 400
- MQTT connection issues are logged and retried
- Graceful shutdown on SIGINT/SIGTERM
- Health check fails if MQTT is disconnected
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
Licensed under the Apache License, Version 2.0
See the LICENSE file for details.
