# TC66C â MQTT Bridge
A simple TC66C to MQTT bridge I'm using to keep track of my Raspberry Pi4's
electrical load in
[Home Assistant](https://github.com/thijsputman/home-assistant-config).
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.
- [Prerequisites](#prerequisites)
- [TC66C](#tc66c)
- [Hardware & OS](#hardware--os)
- [Usage](#usage)
- [MQTT](#mqtt)
- [Docker](#docker)
- [Long-running service](#long-running-service)
- [To-do](#to-do)
## Prerequisites
### TC66C
The TC66C is a USB-C load meter that communicates its measurements over
Bluetooth Low Energy â
[you'll need one of them](https://www.aliexpress.com/item/32968303350.html) 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`](./docs/TC66C.md).
### Hardware & OS
The code most likely works on any Linux-system with BlueZ properly configured.
The only tested/supported configuration is the following though:
1. Raspberry Pi 4 Model B
2. Ubuntu 20.04 or 20.10 (`aarch64`)
- Ensure you Bluetooth-stack is working (i.e. `apt install pi-bluetooth` and
reboot)
## Usage
1. `npm install`
2. Follow the [`node-ble`](https://github.com/chrvadala/node-ble) instructions
to properly configure D-Bus
3. `./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.
### MQTT
Once running, the script will publish the following measurements to the `tc66c`
MQTT-topic at the configured `interval`:
- `tc66c/deviceAlias/voltage_V` â voltage in Volt
- `tc66c/deviceAlias/current_A` â current in Ampere
- `tc66c/deviceAlias/power_W` â power usage in Watt
#### Testing the MQTT output
Run the following command in another shell (or on another machine) to subscribe
to the `tc66c/#` topic (using [Mosquitto](https://mosquitto.org/)) to validate
the measurements are sent out properly.
```shell
mosquitto_sub -h -t "tc66c/#"
```
### Docker
Alternatively, you can use
[the pre-built Docker image](https://hub.docker.com/r/thijsputman/tc66c-mqtt).
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](./docker/docker-ble) prior to starting the container:
```shell
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:
```shell
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`
```yaml
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=
```
#### Environment variables
- `TC66C_BLE_MAC` â Bluetooth MAC address of your TC66C
- `MQTT_BROKER` â Hostname or IP address of your MQTT broker
- `M_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 are `debug`, `warn` and `error`
- `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`)
- `PUID` & `PGID` â Optional; run under the specified UID and GID (instead of as
`root`)
#### Docker and Bluetooth
See [`ð docker/README.md`](./docker/README.md#docker-and-bluetooth) for all the
ins and outs with regards to using Bluetooth in a Docker container.
### Long-running service
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`:
```yaml
restart: unless-stopped
```
## To-do
See [`ð TODO`](./TODO).