A simple TC66C to MQTT bridge I'm using to keep track of my Raspberry Pi4's electrical load in Home Assistant.
Simultaneously, a playground for me to get more familiar with some advanced Docker/container concepts and to dust off my Node.js knowledge.
It publishes measurements of voltage, current and power usage to the tc66c
MQTT-topic at a configurable interval.
The TC66C is a USB-C load meter that communicates its measurements over Bluetooth Low Energy – you'll need one of them to be able to retrieve actual measurements...
After prolonged periods of use (and/or after repeatedly connecting/disconnecting), the TC66C may stop accepting Bluetooth connections (or, might still accept connections, but refuse to respond to any subsequent commands issued).
The easiest way to recover from this, is to toggle the dip-switch marked C in the above picture back and forth. This switches the TC66C from drawing power over USB-C to its (unpowered) micro USB port, effectively resetting the unit (without cutting power to the device connected to the TC66C's USB-C port).
For more details regarding the TC66C, see 📄 docs/TC66C.md
.
The code most likely works on any Linux-system with BlueZ properly configured. The only tested/supported configuration is the following though:
- Raspberry Pi 4 Model B
- Ubuntu 20.04 or 20.10 (
aarch64
)- Ensure you Bluetooth-stack is working (i.e.
apt install pi-bluetooth
and reboot)
- Ensure you Bluetooth-stack is working (i.e.
npm install
- Follow the
node-ble
instructions to properly configure D-Bus ./index.js ble-address mqtt-broker [--interval ms] [--logLevel level] [--deviceAlias alias]
The default interval
at which measurements are fetched and returned is 2,000
ms. Use --interval 0
to disable the interval and fetch measurements as fast as
possible. The maximum retrieval speed appears mainly dependent on the hardware
revision of your TC66C unit. The newer units (firmware 1.15) return in around
500 ms, the older units (firmware 1.14) in around 800 ms.
The default logLevel
is info
. You can optionally change it into debug
to
get (a lot) more output, or to warn
or error
to get virtually no feedback.
The deviceAlias
is used to uniquely identify the TC66C unit in the MQTT-topic.
It defaults to the unit's Bluetooth MAC address (lower-cased, with all colons
replaced by underscores – e.g., 12_34_56_78_90_fe
).
To terminate the script, send a SIGTERM
or SIGINT
(Ctrl
+C
). The script
will attempt a graceful exit with status code 0
. If that fails, sending either
SIGTERM
or SIGINT
again will instruct Node.js to forcefully terminate the
script.
If something goes (unexpectedly) wrong during execution, the script will attempt
a graceful exit with status code 1
instead.
Once running, the script will publish the following measurements to the tc66c
MQTT-topic at the configured interval
:
tc66c/deviceAlias/voltage_V
– voltage in Volttc66c/deviceAlias/current_A
– current in Amperetc66c/deviceAlias/power_W
– power usage in Watt
Run the following command in another shell (or on another machine) to subscribe
to the tc66c/#
topic (using Mosquitto) to validate
the measurements are sent out properly.
mosquitto_sub -h <mqtt-broker> -t "tc66c/#"
Alternatively, you can use
the pre-built Docker image.
The Docker image is available for aarch64
, armhf
/armv7
and amd64
. Note
that only the aarch64
image is actively tested.
In this case, there's no need to npm install
nor to configure D-Bus.
For things to work in Docker, you do need to load a custom AppArmor-policy prior to starting the container:
sudo apparmor_parser -r -W ./docker/docker-ble
The AppArmor-policy needs to be reloaded after each system boot. There are many ways to automate this, one would be:
sudo mkdir -p /etc/apparmor.d/containers && sudo cp ./docker/docker-ble "$_"
sudo crontab -e
# Insert the following into the crontab:
@reboot /usr/sbin/apparmor_parser -r -W /etc/apparmor.d/containers/docker-ble
Once you've loaded the AppArmor-policy, the easiest way to get it up and running
is via docker-compose up [-d]
:
📄 docker-compose.yml
version: "2.3"
services:
tc66c-mqtt:
image: thijsputman/tc66c-mqtt:stable
security_opt:
- apparmor=docker-ble
volumes:
- /var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket
environment:
- TC66C_BLE_MAC=
- MQTT_BROKER=
# - M_INTERVAL=
# - LOG_LEVEL=
# - DEVICE_ALIAS=
# - PUID=
# - PGID=
TC66C_BLE_MAC
– Bluetooth MAC address of your TC66CMQTT_BROKER
– Hostname or IP address of your MQTT brokerM_INTERVAL
– Optional; measurement interval (in milliseconds)- Leaving it empty will use the default interval of 2,000 ms
- Set it to
0
to retrieve measurements as fast as possible
LOG_LEVEL
– Optional; logging verbosity- Defaults to
info
; alternatives aredebug
,warn
anderror
- Defaults to
DEVICE_ALIAS
– Optional; custom device name to use in the MQTT-topic- Defaults to unit's Bluetooth MAC address (e.g.
12_34_56_78_90_fe
)
- Defaults to unit's Bluetooth MAC address (e.g.
PUID
&PGID
– Optional; run under the specified UID and GID (instead of asroot
)
See 📄 docker/README.md
for all the
ins and outs with regards to using Bluetooth in a Docker container.
Personally I'm using the TC66C to provide some insight into the power consumption of my Raspberry Pi 4 "home server". The script is running inside a Docker container alongside the Home Assistant and mosquitto containers.
I've increased M_INTERVAL
interval to 60
(as I'm graphing power consumption
over a 24-hour period) and set LOG_LEVEL
to warn
to reduce the amount of
chatter in the output of docker-compose logs
.
A failure of the script terminates the container. To ensure the script keeps
running (i.e. the container is automatically restarted) add the following to
📄 docker-compose.yml
:
restart: unless-stopped
See 📄 TODO
.