mensi.ch

Custom firmware for an Athom Smart Plug

I got a couple of Athom Smart Plugs to measure the power consumption of some appliances. They come with either Tasmota or ESPHome pre-flashed. Mine came with ESPHome - but my main goal is to just have them report data via MQTT so I can get it into my InfluxDB + Grafana stack. ESPHome seems to have a philosophy where pretty much only the Wifi credentials are configurable, but something like setting an MQTT server doesn't seem possible.

So I also tried Tasmota by just flashing it via the web-based firmware upgrade which worked without issues. Tasmota however seemed to publish new values to MQTT very infrequently and didn't seem to have any options to change that.

Luckily, it looks like Athom publishes their ESPHome configs on Github, so we don't have to start from scratch.

Customizing the config

Since I only want to publish data to MQTT, I don't need the Homeassistant API, so I removed the api: and dashboard_import: sections. Instead, I added a section for MQTT:

mqtt:
  topic_prefix: smart_plug/some_appliance
  broker: 192.168.1.2
  port: 1883

The broker: IP address is the address of my MQTT broker and topic_prefix: is prepended to the MQTT topics for the measured data.

I also wanted that the relay is always turned on - imagine for example if you use this to measure the power consumption of your freezer - you don't want to accidentally turn it off. So I turned the switch: into an output: like this:

output:
  - platform: gpio
    pin: GPIO12
    id: relay

The power button on the side can also be used to toggle the relay, so I made it toggle the blue LED instead:

    on_multi_click:
      - timing:
        - ON for at most 1s
        - OFF for at least 0.2s
        then:
          - light.toggle: blue_led
      - timing:
        - ON for at least 4s
        then:
          - button.press: Reset

There's also an overcurrent protection. While one could argue that that's a good thing, your circuits in the house should be properly fused and this should be quite redundant. So I simply removed this block:

      on_value_range:
        - above: ${current_limit}
          then:
            - switch.turn_off: relay

Finally, we need to turn our output on when the plug boots. To do so, just append this to the esphome: section:

  on_boot:
    - priority: 90
      then:
      - output.turn_on: relay

Compiling the firmware

ESPHome provides a docker container with all the prerequisites for compiling ESPHome, so we can be lazy and just use that:

$ docker pull ghcr.io/esphome/esphome
$ docker run --rm -v "${PWD}":/config -it ghcr.io/esphome/esphome compile athom-smart-plug-v2.yaml

The compiled firmware will end up in .esphome/build/athom-smart-plug-v2/.pioenvs/athom-smart-plug-v2/firmware.bin

You can flash it via the web UI of your smart plug. In case the firmware is too large, just flash Tasmota's tasmota-minimal.bin firmware first so you have more space available to flash your custom ESPHome firmware.

MQTT data

The topics published to MQTT will look something like this:

smart_plug/some_appliance/sensor/voltage/state 233.4
smart_plug/some_appliance/sensor/current/state 0.00
smart_plug/some_appliance/sensor/power/state 0.0
smart_plug/some_appliance/sensor/total_energy/state 0.000
smart_plug/some_appliance/sensor/total_daily_energy/state 0.000

and more - use mosquitto_sub or your favorite MQTT client to listen to smart_plug/#.

Telegraf

I'm using Telegraf to subscribe to MQTT and push the data into InfluxDB. The config for it looks like this:

[[inputs.mqtt_consumer]]
  servers = ["tcp://192.168.1.2:1883"]
  topics = [
    "smart_plug/+/sensor/power/state",
    "smart_plug/+/sensor/voltage/state",
    "smart_plug/+/sensor/power_factor/state",
    "smart_plug/+/sensor/current/state",
    "smart_plug/+/sensor/apparent_power/state",
    "smart_plug/+/sensor/total_energy/state",
  ]
  data_format="value"
  data_type="float"

  [[inputs.mqtt_consumer.topic_parsing]]
    topic = "smart_plug/+/sensor/+/state"
    measurement = "_/_/_/measurement/_"
    tags = "_/location/_/_/_"

Getting the topic_parsing right was a bit tricky since the documentation on it seemed a bit sparse. The idea is to use measurement and tags to extract parts of the topic to be either the _measurement or a tag for the stored timeseries data.

Final Thoughts

During my research for this project, I found some threads where people were complaining about Athom, basically accusing them of just using Open Source projects like ESPHome or Tasmota and shifting the support burden to those projects instead of supporting the products themselves.

I personally prefer if customizing the behavior of bought hardware is this easy. Instead of spending endless hours on reverse engineering, I was just able to customize their config, compile and flash it in a short amount of time.

On the other hand, it feels like both Tasmota and ESPHome are not terribly user friendly. I don't have much of an issue myself, but I wouldn't want to see any non-techy person trying to do this. So my conclusion would be that I'd want more userfriendlyness in Tasmota/ESPHome rather than trying to make higher barriers for their use.


Creating an USB interface for SunnyBoy Inverters

Previously, I looked at the data interface of a SunnyBoy inverter. Since we now know the pinout, we can attempt at building our own interface board. To make our life simple, it would be nice to get it to be USB, so we don't have to mess around with RS485 or RS232 adapters.

Considerations for the USB Interface

USB uses differential signalling at a speed at depends on the version. While the inverter itself uses a relatively slow baud rate, our electrical interface still needs to be good enough for USB. If we want to use the existing screw terminals to connect the wires, USB3 is not going to work, so we want to aim at an UART to USB converter IC that uses as old or slow of an USB version as possible.

The popular cheap option - the CH340 series - is an USB full speed device, so USB 1.1 with 12 Mbps. Maybe we can get away with the screw terminals. Otherwise we have to use a proper USB connector.

Since USB also provides 5V power, we do not need an isolated DC-DC power supply. Instead, we can just use the power provided by USB.

Isolation

The manufacturer-designed PiggyBack modules use a combination of ESD protection diodes, resistors and optocouplers to isolate the data signals. Another option is to use dedicated digital isolation ICs. We however have to be a bit careful about what part we select. The cheap, widely available SOIC-8 parts often have weak or no input protection and a 560V rating for the working voltage. They tend to have a higher peak isolation voltage rating, but can only survive that for short amounts of time. We need more than 600V isolation sustained.

There are also higher rated parts with integrated ESD protection, for example the TI ISO7721DWR. It comes in a wider package to also increase the creepage distances around the body of the part. It only needs one external part: A 0.1 uF capacitor on the power pins for each side.

Circuit

So, given the choice of digital isolator and TTL-to-USB converter IC, we can put together a simple circuit based on the recommended external parts in their respective datasheets:

Since the ISO7721DWR already has ESD protection integrated, we do not need external resistors or ESD diode arrays. A 100nF decoupling capacitor is recommended on each side though.

Likewise, the CH340N includes an oscillator and all required resistors for USB, so there we also get away with just adding some power supply decoupling.

PCB

Due to the low part count, the PCB is quite trivial:

The only slightly noteworthy thing is that the ground planes on the back have a nice separation gap between them. Otherwise we'd be circumventing the isolation the ISO7721DWR provides. Even with the solder mask, the isolation should be good enough for 1000V.

Getting the Boards built

I like the integration between lcsc.com, easyeda.com and jlcpcb.com for quick prototypes - you get the symbols and footprints directly via the part-number and ordering PCBs with or without assembly is just a few clicks. So I used them here as well - you could of course achieve the same with KiCad and some other manufacturer.

At the time of writing, 5 PCBs cost $3.10 and parts for 4 boards $10.69. Shipping and handling for both (old customers get a discount on LCSC when also ordering from JLCPCB) came out to $11.56. This is quite a bit less than what even a single second-hand RS485 piggy back sells for.

I also ordered some insulating silicone tube and cable glands which came out to ~$5 per inverter.

Testing

Warning!

If you're following along and want to install a similar board, please keep in mind that the inverter has mains voltage and the full PV string voltage present. This can be hundreds of volts DC. Disconnect the inverter completely and let capacitors drain. If in doubt, hire a certified electrician to do the work.

Also, keep in mind that the design presented in this article is the work of an amateur and has not been tested or certified by any authority or lab. I cannot guarantee that it is safe and does not burn down your house.

With the parts soldered, we're ready to test if the gamble with USB 1.1 over the screw terminals worked out. While just chopping off one end of an old USB A to mini or micro USB cable would be enough, I'd recommend putting ferrules on the exposed ends. Otherwise the screw terminals can easily break the thin strands for the D+ and D- signal wires.

USB can be tested even with the inverter turned off - since the required power is provided via USB as well. And it indeed looks like the CH340N works just fine hooked up to a Raspberry Pi.

With everything closed up and the inverter powered up, we can try to read out some values. SMA provides an open source library to interface with their devices called YASDI. After building the library with the instructions supplied with the SDK, we can use the yasdishell executable to test. For that, we first need to create a yasdi.ini config file:

[DriverModules]
Driver0=yasdi_drv_serial

[COM1]
Device=/dev/ttyUSB0
Media=RS232
Baudrate=1200
Protocol=SMANet

[Misc]
DebugOutput=/dev/stdout

It does not seem to make much of a difference whether we use RS232 or RS485 as the media. In yasdishell, scan for devices and get spot values as per the built-in help.

Conclusion

With this, we have a working, simple way of getting stats out of old inverters. To process the data further, one can for example use github.com/pkwagner/yasdi2mqtt to publish measurements to MQTT. I'm then using my InfluxDB-Telegraf-Grafana stack I use for other things to also store and visualize the inverter data.


Data Interface of an SMA SunnyBoy Inverter

We have a few old SMA SunnyBoy solar inverters I'd like to interface with - so after some research, it looks like these can be interfaced with different types of technologies (including RS232 and RS485). The way this is achieved is via "PiggyBack" modules that are installed in the inverter to provide one of these interface types.

Warning!

Before we proceed further with poking at the hardware, please keep in mind that these inverters have both mains voltage and the string voltage of your PV panels present. This voltage can easily be hundreds of volts DC! The connection between inverter and panels does not have an RCD. The panels will happily push current through you as long as the sun shines.

It is absolutely crucial that you completely disconnect the inverter from both mains and PV panels before even thinking about opening it. Additionally, the inverter has very large capacitors that need to discharge. Follow the instructions provided in the service manual. Do not open the inverter if you don't know what you're doing - get a certified electrician to help.

Taking a closer look at an RS485 PiggyBack

While one can find pictures on the internet and infer some information from installation manuals, it doesn't compare to having one in your hands. I luckily found an RS485 PiggyBack for a reasonable price on ebay and it even included all accessories and manuals.

Here it is:

The construction is clearly built around a large isolation gap in the middle, where we only have optocouplers and a transformer going across. There's even a slit below the optocouplers and the copper layers are pulled back.

On the upper side (with the 2*7 pin header), we have two large ICs:

  • a MAX253 transformer driver for isolated power supplies designed for isolated RS485 interfaces
  • some variant of an AHCT14 inverter with Schmitt-Trigger inputs

There are also two SRV05 ESD protection diode arrays (the small 6 pin ICs) and some passives. This looks very much like the "hot", inverter side.

On the bottom side (with the 2*5 pin header), there are also 2 large ICs:

  • a MAX487E RS485/RS422 transceiver
  • an ST KF50 low drop voltage regulator

We also have a couple of diodes and larger capacitors - these are part of the secondary side of the transformer isolated power supply. They pretty much follow the example circuit from the datasheet of the MAX253.

In general, it looks like this board is designed to take the interface and power from the inverter, isolate it and provide RS485 on the bottom header.

Where does it all plug into?

The inverter has the matching headers to plug it in:

We can again see that the upper part is connected to the rest of the inverter, while there is a large isolation gap around the lower header.

The screw terminal is labelled "2 3 5 7" and is used to connect the RS485 interface. The jumpers next to it can be used to enable 680Ω pull ups / downs and a 120Ω termination resistor. The 680Ω resistors can be seen on top of the header. The 120Ω resistor is not present on the inverter side (but can be found on the PiggyBack)

We can get the screw terminal assignment from the installation manual. The positions are used differently if we compare the manuals from the RS232 and RS485 PiggyBacks. The connections between screw terminal and pin header are quite easy to determine with a multimeter:

2 3 5 7
RS485 DATA+ GND DATA-
RS232 RX TX GND

Some pins seem unconnected - but based on installation manuals, it looks like other devices also supporting PiggyBacks have wider screw terminals. Maybe these pins are connected in those devices.

Just visually speaking, it looks like screw terminal positions labelled 2 and 7, so the DATA signals for RS485 are best suited for differential signals, as they are of similar length and only 7 has a short (5mm-ish) stub to the side for the jumper.

It's also interesting how there's a mix of resistors on the inverter and on the PiggyBack - naively I would assume that you'd just want to have connections to the jumpers and then supply any required resistors on the PiggyBack. It could however be that either for space constraints or signal conditioning this arrangement worked better.

For the 680Ω pull up, pin 2 on the lower header has to be supplied with 5V from the LDO. To protect against improper installation, the pin is connected via two 100Ω resistors in parallel to limit the current as well as a diode to avoid reverse current flow.

Pinout of the Hot Side

Trying to figure out the pinout of the hot side, we immediately have obvious candidates for power and ground:

Pin 10 has a thicker trace than the others, and pin 9 is fully connected to something kinda ground-plane-looking. We can easily confirm with a continuity test to the power and ground pins on the MAX253 given in its datasheet.

Since I do not want to mess with my inverter under power, I don't know whether power is 3.3V or 5V. All ICs work with both voltages. Another trick to see which one works is to just power the board with 3.3V and seeing whether the secondary side gets enough voltage for the 5V LDO to work. Unfortunately the transformer is wound to give a slightly higher voltage on the secondary and both 3.3V and 5V create secondary voltages acceptable for the 5V LDO.

For the other pins its best to work our way backwards. From the MAX487E we know which pin is what. We essentially have 4 signals:

  • RO: Receiver Output. This is the data received from RS485.
  • RE: Receiver Output Enable. Can be used to put RO into high impedance mode.
  • DE: Driver Output Enable. Has to be high to enable data transmission on RS485.
  • DI: Driver Input. This is the data sent to RS485.

Note that RE, DE and DI all have to be driven by the inverter side, while RO is the only signal in the other direction. This matches up with the optocouplers: 3 of them point from inverter to isolated side, while only one goes in reverse.

Following the signals, they all cross the optocoupler, pass through the Schmitt-Trigger inverters at least once, go through a protection resistor, pass by the ESD diode array and end up on the pin header. The mapping on the header ends up being:

  • RO: Pin 3
  • DE: Pin 4
  • DI: Pin 5
  • RE: Pin 7
  • GND: Pin 9
  • VCC: Pin 10

In terms of the other pins, it looks like some can optionally be connected via unpopulated resistor footprints. Only pin 13 is connected via a 0Ω resistor to an inverted version of DI. No idea what that's used for, maybe some feedback to see if the PiggyBack is present / working?

Mechanical Aspects

Finally, a short note on the mechanical aspects: The hole in the inverter housing seems to be sized for a PG16 cable gland. Since that is a bit wide for the cables used in RS485, a reduction to PG11 and a PG11 cable gland are actually used.

Since the cable is likely going to touch the main DC input assembly, a silicone tube is provided to add further insulation between the data cable and the rest of the inverter. Depending on the insulating material and thickness of the data cable you use, this could actually be necessary to provide the required insulation - after all, it would be a bit silly to add this many protection mechanisms on the PiggyBack to then have a poorly insulated data cable rest directly on the high voltage carrying parts.