This project provides a Docker container to read signals from a microcontroller and publish them over MQTT.
Its main use case is to integrate impulse devices like S0 counters into a home automation system.
-
A microcontroller running a Firmata-compatible firmware (e.g. an Arduino running Firmata or ConfigurableFirmata)
-
A running MQTT broker (e.g. mosquitto)
-
Docker
-
Optional and recommended: Docker Compose
The Docker Compose [documentation](https://docs.docker.com/compose/install/) contains a comprehensive guide explaining several install options. On recent Debian based systems, dependencies may be installed using the command
$ sudo apt-get install docker-ce docker-compose-plugin
-
Install Docker and MQTT broker.
-
Download Arduino IDE and flash Firmata.
-
Connect a hardware device (e.g. a S0 counter) to the Arduino:
-
Device signal (+) → Arduino digital pin (e.g. pin 2)
-
Device GND (-) → Arduino GND
-
-
Create
compose.yml
configuration file. -
Run
docker compose up -d
.
-
Create a run configuration, e.g.
compose.yml--- services: firmata-mqtt: image: "ckware/firmata-mqtt" container_name: "firmata-mqtt" init: "true" restart: "unless-stopped" devices: - /dev/ttyUSB0 environment: MQTT_OPTIONS: "-h broker-host" FIRMATA_PINS: "9:boiler,12" FIRMATA_TEMPLATE: "pullup:debounce:falling:1:W"
-
Start a container:
$ docker compose up -d
-
Receive value changes:
$ mosquitto_sub -h broker-host -v -t firmata/# firmata/9/json {"pin":9,"name":"boiler","rate":155.193070189773,"unit":"W","value":0,"timestamp":1592731404.896913,"duration":23.1969120502472,"count":1,"total":2} firmata/12/json {"pin":12,"rate":85.9448365143275,"unit":"W","value":0,"timestamp":1592731423.586332,"duration":41.8873331546783,"count":1,"total":2} ^C $ docker compose down $ docker compose logs Connecting to Firmata device at /dev/ttyUSB0 Observed pin(s): 9, 12 Stopping due to signal TERM Disconnected from Firmata device.
Environment variable | Description | Allowed values | Default | Example |
---|---|---|---|---|
|
Comma-separated list of pin number with optional name and pin configuration |
pin[:name[:configuration]][,pin[:name[:configuration]] …] |
no default |
|
|
Serial firmata device to read messages from. |
Any special character file |
|
|
|
Template containing the default pin configuration for all pins. See Pin Configuration for details. |
|
|
|
|
Poll interval in milliseconds. When strategy is |
Positive integer |
|
|
|
Command that is run for each message. See Command Line Interface for details. |
Any executable file |
|
|
|
Log verbosity. |
|
|
|
Environment variable | Description | Allowed values | Default | Example |
---|---|---|---|---|
|
MQTT options |
All options supported by |
none |
|
|
MQTT topic for publishing sensor data |
|
|
|
|
Append sensor ID to topic? |
|
|
|
|
Append format (one of: |
|
|
|
|
Publish sensor data in JSON format? |
|
|
|
|
Publish sensor data in raw format? |
|
|
|
|
Field separator for raw format |
String |
Whitespace ( |
|
|
Use Mosquitto as MQTT client? |
|
|
|
Environment variable | Description | Allowed values | Default | Example |
---|---|---|---|---|
|
If set, topic and message are appended to a log file |
File path |
not set |
|
|
Format to write topic and message to log file |
|
|
|
A pin configuration is a colon-separated list of the properties listed in this section. All properties are optional, trailing colons may be omitted.
Supported pin modes: input
, pullup
. Default: input
.
See Digital Pins in the Arduino Tutorial for details.
Supported detection strategies: instant
, debounce
. Default: instant
.
-
instant
: Changes will be reported instantly. -
debounce
: Value changes will be buffered until the current poll interval has elapsed (seeFIRMATA_INTERVAL
). This may help when bouncing switches are connected to the Firmata device. Please note that this option effectively reduces the sample rate to the poll interval, thus the poll interval should be chosen carefully according to the connected hardware.
Supported rate triggers: none
, falling
, rising
, any
. Default: none
.
When rate trigger ist set, the value changes are supposed to come from a meter device, and a rate per hour is calculated as
3600 / (frequency * duration)
-
falling
: The rate will be calculated for every value change from1
to0
. -
rising
: The rate will be calculated for every value change from0
to1
. -
any
: The rate will be calculated for every value change. -
none
: The rate will not be calculated.
The frequency tells how many changes per hour the meter device emits.
Supported frequencies: positive numbers
-
Example: S0 energy meters emitting 1000 impulses per kWh
environment: FIRMATA_PINS: "2:boiler,3:washer" FIRMATA_TEMPLATE: "pullup:debounce:falling:1:W" MQTT_OPTIONS: "-h broker-host"
-
Connect to firmata device at
/dev/ttyUSB0
(default) -
Configure pin
2
with nameboiler
and pin3
with namewasher
-
Enable pullup, software debouncing and rate calculation for all pins
-
Publish to
broker-host
-
-
Example: Different devices with lower sample rate
environment: FIRMATA_DEVICE: "/dev/ttyUSB1" FIRMATA_PINS: "2::pullup,3::input" FIRMATA_INTERVAL: "20" FIRMATA_VERBOSITY: "1" MQTT_OPTIONS: "-h broker-host"
-
Connect to firmata device at
/dev/ttyUSB1
-
Disable software debouncing (default)
-
Configure pin
2
as digital input with pullup -
Configure pin
3
as digital input pin (without pullup) -
Poll every
20
ms for changes -
Log verbose messages
-
Publish to
broker-host
-
-
Example: Debugging
environment: FIRMATA_PINS: "2" FIRMATA_VERBOSITY: "2" FIRMATA_COMMAND: "/bin/echo"
-
Connect to firmata device at
/dev/ttyUSB0
-
Configure pin
2
as digital input with pullup -
Log debug messages
-
Do not publish over MQTT but call
/bin/echo
instead.
-
The main part of this project is a command line program that connects to a microcontroller using the Firmata protocol and observes its input pins. For each value change of an observed pin, an external command is called with the following arguments:
pin name value timestamp duration count total rate unit
By default, the external command is a shell script that converts the arguments
to JSON (or optionally keeps them raw) and publishes them over MQTT. The
command
option may be used to set a different command for custom processing.
Illustration of a value change:
──┐ ┌── 1 │←───── d ─────→│ └───────────────┘ 0 ↑ ↑ t v v: value t: timestamp d: duration
-
pin
: An integer containing the pin number. -
name
: A string containing a name for the connected device. -
value
: The value as reported by Firmata, e.g.0
or1
. -
timestamp
: A decimal containing the timestamp of the value change. The integer part contains a unix timestamp (seconds since epoch). The fractional part has a precision of 9 digits (nanoseconds). -
duration
: A decimal containing the duration since the previous value change with a precision of 9 digits (nanoseconds). -
count
An integer containing ths pin’s number of changes to the current value. -
total
An integer containing the pin’s total number of changes. -
rate
A string containing the value change rate per hour. -
unit
A string containing the unit (e.g. W or m³).
'9' 'boiler' '1' '1591428675.880354881' '2.1215808391571' '3' '5' '1696.85' 'W'
Explanation: pin 9 with name boiler has changed to value 1 at 1591428675.880354881 (2020-06-06 07:31:15 and 880 ms, 385 µs, 881 ns). Before the change, the pin was stable for about 2.122 seconds (with value 0). This is the 3rd time that pin 9 changed to 1. The total number of value changes (either 0 → 1 or 1 → 0) of pin 9 is 5. If we suppose that an energy meter is connected and 1 Wh has been consumed within the duration, then the duration corresponds to the power of 1696.85 W.
This section contains an example configuration to integrate a power meter with
FHEM. The power meter is a S0 counter emitting 1000 impulses
per kWh. It is connected to pin 9 of an Arduino. The pin value stays at 1
when idle and changes to 0
shortly (~70 ms) for every consumed Wh.
---
services:
firmata-mqtt:
image: "ckware/firmata-mqtt"
container_name: "firmata-mqtt"
init: "true"
restart: "unless-stopped"
devices:
- /dev/ttyUSB0
environment:
FIRMATA_PINS: "9:boiler:pullup:debounce:falling:1:W"
MQTT_OPTIONS: "-h broker-host"
define mqtt_firmata_pin9 MQTT2_DEVICE
attr mqtt_firmata_pin9 devicetopic firmata/9
# rename 'rate' to 'power' and suppress some readings
attr mqtt_firmata_pin9 jsonMap rate:power pin:0 value:0 total:0
attr mqtt_firmata_pin9 readingList $DEVICETOPIC/json.* { json2nameValue($EVENT, '', $JSONMAP) }
attr mqtt_firmata_pin9 event-on-change-reading name,unit,power,timestamp,duration
attr mqtt_firmata_pin9 stateFormat { sprintf '%.2f %s', ReadingsNum($name, 'power', '???'), ReadingsVal($name, 'unit', '') }
attr mqtt_firmata_pin9 icon icoBlitz
-
This project is an integration of
-
History and details (in German): S0 Zähler mit ConfigurableFirmata
-
Arduino Firmata impementation: ConfigurableFirmata
-
A similar project for temperature sensors: tfrec-mqtt