The spb012ble, better known as Voltcraft SEM6000 SE, is a “smart meter” plug with the following features:
It looks very similar to the revogi Smart Meter Plug EU and may be a rebranded version.
This repository contains documentation on the hardware and firmware, as well as software to control the “smart meter”.
To install the software, clone this repository:
git clone https://codeberg.org/ldb/spb012ble.git
virtualenv -p python3 sandbox
source sandbox/bin/activate
pip install .
Controlling the device works like this:
spb012ble-monitor connect:A3:00:00:00:08:16 auth:0000 sync \
switch:on led:on measure
The device is marked as model no. SEM 6000 40th, but sold as Voltcraft SEM6000 SE. On the bottom in the hole for the type E plug a screw can be removed. This allows removing the cover of the female socket on the opposite side.
This reveals a transparent plastic ring, which is slightly glued. It can be removed with a guitar pick or thin plastic piece.
On the bottom of the device there are a few capacitors and resistors, as well as four RGB-LED’s for the light ring.
The top half only carries a crystal osciallator.
The microcontroller PCB (green), which is connected to a flexible PCB (black), connects to the left side of the main PCB (white). The microcontroller board is marked with BK3432 V001. The flexible PCB has the marking SPB012-BLE V0.05 RW, 2021.08.31
The Bluetooth chip is marked as Beken BK3432 EU024JFE, an ARM968E microcontroller with built-in Bluetooth 4.2 or 5.01 transceiver. The antenna is to the left.
[1] | Datasheet and website disagree. |
The remaining housing seems to be glued together, which prevents further non-destructive analysis. There must be a mechanical relais hidden in the plug, responsible for the noticeable click when turning it on or off. Also, according to the magazine c’t, it uses a RN8209D chip for measurements.
The meter’s firmware is updateable through the smartphone app. It is fetched via the URL https://upgrade.revogi.com/?SN=<sn>, where sn is a hex-encoded string that can be read from the GATT endpoint 0000fff1-0000-1000-8000-00805f9b34fb and contains an identifier, as well as software and hardware revision. For hardware revision 3 devices are currently shipped with firmware version v1.16. That version is not available for download, but v1.17 is part of the Android app as bk3432_ble_app_app22.bin. v1.18 is available on the update servers, but currently not advertised for updates yet.
The downloadable firmware image has a 16 byte header followed by the interrupt vector.
typedef struct { uint32_t crc32; uint16_t version; uint16_t length; /* Image length in 4 byte blocks */ uint32_t uid; uint8_t unknown1, unknown2; uint16_t unknown3; } imageHeader_t;
It looks like it is mapped at address 0x0001b020. Most of the code is run in ARM Thumb mode.
A high-level datasheet for the BK3432 as well as a (semi-public) SDK are floating around the internet. According to that SDK the firmware runs the proprietary RivieraWaves Bluetooth real-time operating system (RTOS). Certain parts of this OS are not upgradeable over-the-air (OTA), including the kernel and the Bluetooth stack itself. Thus the OTA image above only contains the application-specific code of the spb012ble.
The RN8209D is connected to the microcontroller through the UART2 interface at 9600 baud while UART1 at 115200 baud seems to be used for debugging messages. Every 500 ms the firmware polls the following registers in order:
Power, voltage and current can be calibrated via the command 240 while the UFreq register value is scaled interally by the constant 0x6d3d3, as described by the RN8209D datasheet on page 25.
The current power readout is accumulated every five seconds, which is rather slow for quickly-changing loads like PC power supplies. This value is truncated to 1 W resolution for hourly energy statistics without proper rounding and propagated from there to daily and monthly statistics.
/* Runs every five seconds */ powerSum += power; if (minute == 59 && second == 55) { energy += powerSum / (SECONDS_PER_HOUR / FIVE_SECONDS) / 1000; update_energy_history_24h(); powerSum = 0;
This can result in significant under-reporting of energy use for devices with low power draw of a few watts.
Inputs checks in the firmware are basically nonexistent. For instance the Android app allows setting power limits from 1000 W to 4000 W, while the firmware happily accepts any value from 0 W to 65535 W.
globalPowerLimit = (packet[4] << 8) | packet[5]; status_reply(5, 0); flash_settings();
The same applies to the device name, which overwrites arbitrary memory if too long:
memset(globalDeviceNameA, 20); memcpy(globalDeviceNameA, &packet[4], packet[1]-3); memset(globalDeviceNameB, 20); memcpy(globalDeviceNameB, &packet[4], packet[1]-3); globalDeviceNameBLen = 20; status_reply(2, 0); flash_settings();
The client unlocks the meter using a four-digit PIN code. If that client – for whatever reason – disconnects the meter will advertise itself again over BLE and any other client can access the device without providing the PIN code, since it is already unlocked.
Brute-forcing four digits takes about 30 minutes worst-case at a measured average rate of 5.7 tries per second.
But even worse: The PIN can be reset to the default without prior authentication due to how commands are dispatched:
bool checksum_correct = verify_checksum(packet); if (checksum_correct == false) { return 0; } uint8_t action = packet[2]; if (action == 23) { handle_authentication(&packet[4]); } else if (action == 240) { handle_calibration(&packet[4]); } else if (action == 48) { handle_set_mac(&packet[4]); } if (is_authenticated != true) { return 0; } if (action == 12) {