Cameleering / Cameleer / Use cases / MQTT edge gateways
Edge and IoT
MQTT edge gateways that keep working when the uplink doesn't
Gateways sit between machines and the cloud. They speak local protocols, keep working offline, and share small hardware with other software. Cameleer runs Apache Camel's routes there without a JVM: five tested patterns, one binary, about 10 MB of memory.
Five tested patterns
Each is a complete route file in examples/edge/, run by the test suite against the embedded MQTT and Kafka brokers and HTTP mocks. The edge cookbook walks through each one.
| pattern | what the route does | file |
|---|---|---|
| Sensor rollups | subscribe to sensors/+/temperature, drop malformed and out-of-range readings, aggregate five readings per sensor (or a minute's worth) into count, min, max and average, publish to Kafka keyed by sensor | mqtt-sensor-rollup.camel.yaml |
| Store and forward | telemetry into a seda queue, post to the cloud with retries and back-off, dead-letter to a buffer folder, replay every 30 seconds and move to .sent on success | store-and-forward.camel.yaml |
| Syslog to Kafka | receive RFC 5424 and 3164 syslog over UDP, TCP or TLS, parse it, keep warnings and above, publish to Kafka | syslog-to-kafka.camel.yaml |
| HL7 over MLLP | health pack; hospital gateway: MLLP in, HTTP to the EHR, acknowledge after delivery (own page) | hl7-mllp-to-http.camel.yaml |
| Gateway presence | publish a retained online status with a last-will offline, plus a heartbeat on a timer | gateway-heartbeat.camel.yaml |
Store and forward, the part that matters
From store-and-forward.camel.yaml: the uplink route's error handler. When the post fails three times, the original message becomes a file in the buffer; a second route replays the buffer.
- route:
id: uplink
errorHandler:
deadLetterChannel:
deadLetterUri: "file:{{env:BUFFER_DIR:buffer}}?fileName=${date:now:yyyyMMddHHmmssSSS}-${exchangeId}.msg"
useOriginalMessage: true
redeliveryPolicy:
maximumRedeliveries: 3
redeliveryDelay: "{{env:RETRY_DELAY_MS:1000}}"
useExponentialBackOff: true
from:
uri: "seda:uplink?size=10000"
steps:
# ... set headers, post to {{env:UPLINK_URL}}/ingest
Why it holds up
- The MQTT consumer hands off to a bounded
sedaqueue, so a slow uplink never blocks the broker subscription. useOriginalMessagebuffers what arrived, not a half-transformed body.- The buffer is a folder: it survives a restart, and it is the only thing the container writes, so the image runs
--read-onlywith one volume.
Footprint on the device
| build | binary | HTTP route ready | memory, HTTP route |
|---|---|---|---|
| all components, glibc | 23.9 MB | 1.17 ms | 10.4 MB |
--features cloud, static musl | 11.8 MB | 0.75 ms | 5.3 MB |
--features core, static musl | 7.5 MB | 0.69 ms | 4.2 MB |
Measured on Linux x86-64 with cameleer-bench coldstart; method and the full table in BENCHMARKS. The container image is about 20 MB to download with every component, 12.5 MB with FEATURES=core. For a gateway, --features kafka-mqtt,protocols keeps MQTT, syslog and the core, and adding health brings MLLP (since 0.6.0); the build features guide lists every family.
Let an AI agent work with the plant, through MCP tools on the gateway
The gateway can serve MCP tools next to the devices: a technician asks Claude Desktop which sensors run hot and turns a fan down, or an agent in the cloud adjusts setpoints overnight. Readings stay on site, and the agent reaches only what the tools expose.
The tested example
latest_readingsreturns the last MQTT reading of every sensor, kept in the embedded in-memory SQL database.set_setpointpublishes todevices/<device>/setpoint; only known devices pass the schema, and a setpoint above 30 °C waits for an operator.- The route and its test
Run it on the gateway
docker run -d --name gw --network host --read-only --tmpfs /tmp -e CAMEL_LOCAL_STATUS=true -e MQTT_BROKER -e GATEWAY_ID -v "$PWD:/routes:ro" cameleering/cameleer run /routes/mcp-gateway-tools.camel.yaml --mcp docker exec gw cameleer tool approve <handle> # a parked setpoint
Agents connect to http://<gateway>:8080/mcp. Add API-key or JWT checks before the port is reachable beyond the plant network (routes as MCP tools).
Common questions
Does Cameleer run on a Raspberry Pi?
Yes, on a 64-bit OS. The image is published for linux/arm64 (Raspberry Pi 3, 4 and 5 with 64-bit Raspberry Pi OS, NVIDIA Jetson, Graviton) and a running route uses about 10 MB of memory. There is no 32-bit ARM image.
Which MQTT versions and brokers are supported?
paho speaks MQTT 3.1.1 and paho-mqtt5 speaks MQTT 5.0, with QoS 0 to 2, retained messages, last will, persistent sessions and TLS. They are tested against Mosquitto, RabbitMQ and ActiveMQ and work with any broker, including EMQX, HiveMQ and AWS IoT Core.
What happens to messages when the uplink is down?
The store-and-forward route retries with back-off, then writes the original message to a buffer folder through a dead letter channel. A replay route polls the folder every 30 seconds and moves each file to .sent once the cloud accepted it. The buffer is on disk, so it survives restarts.
Can a gateway bridge protocols?
Yes. The tested routes bridge MQTT into Kafka and HTTP, and syslog (RFC 5424 and 3164 over UDP, TCP or TLS) into Kafka with severity filtering. AMQP, STOMP and files are available on the same gateway, and HL7 over MLLP with the health pack.
Is there a Modbus or OPC UA component?
No. Field protocols are expected to arrive through a local broker or an HTTP, TCP or UDP adapter; the netty component handles raw TCP and UDP.
Run it
The buffer is the only thing written, on its own volume; everything else is read-only.
export MQTT_BROKER=tcp://localhost:1883 UPLINK_URL=https://ingest.example.com GATEWAY_ID=plant-7 docker run --rm --network host --read-only -e MQTT_BROKER -e UPLINK_URL -e GATEWAY_ID \ -e BUFFER_DIR=/buffer -v cameleer-buffer:/buffer -v "$PWD:/routes:ro" \ cameleering/cameleer run /routes/store-and-forward.camel.yaml