Outback Inc. sell a family of solar chargers and inverters which communicate via a proprietary bus. They intend that you use their “MATE” controller which spits out some fairly basic ASCII log data, but it’s missing a lot of nice info (and wouldn’t it be nice if I could just do away with the MATE altogether and talk to the solar charger directly?)
This project documents my efforts in reverse engineering the proprietary protocol linking the MATE and other Outback devices, because secret protocols suck and open source provides so much more flexibility!
Eventually I plan to replace the MATE with a linux gateway board which will be always on and continuously monitor and log the MX solar charger and any other sensors in our bach. This will provide so much more possibilities than the MATE alone allows.
Python Library
I have implemented a MATE emulator in python, which pretends to be a MATE and can communicate with an MX/FX/FlexNET device through a simple adapter circuit and a standard serial port. The code should be able to run on both Windows and Linux (including the Raspberry Pi!)
You can download the package from my GitHub: TODO
To use, you will need to install Python 2.7 and PySerial. You can then use one of the provided samples to log data from the MATE, but the code is really intended to integrate into your own python application so you can store it in a proper database, or serve the data on a web page.
The rest of this page documents the process I went through in reverse
Physical Layer Reverse Engineering
TODO: figuring out the physical layer
- Measuring voltages and probing the wires
- Reverse-engineering the MATE schematic
- Working out the serial format
- Final determined interface/pinout
TODO: Scope traces
Protocol Reverse Engineering
I reverse-engineered their protocol using three methods:
1. Sniffing raw data from a live system
The first approach I tried was to sniff data from a live system, with a MATE controller connected to an Outback MX charge controller, using my (isolated!) PicoScope. I collected as much data as I could, noting the readouts on the MX LCD screen
I discovered that the MATE would send a packet every second, and would immediately get a response:
TX: 00 04 00 01 00 00 00 05
RX: 03 81 80 82 00 6A 3F 01 00 1D 00 FF 02 40 03 8E
In: 56.8V, 0.9A
Out: 25.5V, 2.0A
0.050kW, 2.9kWh
(which includes battery voltage, solar voltage, currents, kWh, etc), but unfortunately a lot of the values didn’t seem to be present. I later found out that they had packed their data rather tightly, so some values had half of their binary value present in one part of the packet, with the other part in a completely different part of the packet.
2. Emulating a device
Since I don’t have a working system at home, I can’t sniff packets to see how the MATE behaves. I did find out I could re-play data back at the MATE, pretending to be one of the attached units, to see how it responds.
Emulating the MX charge controller
The first device I tried to emulate was the Outback MX charge controller, since I already had some packets captured from sniffing in a live system. I simply replayed the data I had captured back to the MATE, and sure enough, it thought there was an actual MX attached to it!
From there I was able to narrow down the status packet format by changing values and seeing their effect on the screen!
Emulating the FX & FlexNET DC devices
I also tried to emulate two other devices that we do not have (but plan to install in the future), to see if I could figure out the packet format in advance.
This was a bit trickier to emulate because I had no idea what kind of data the MATE would expect. Luckily, I found that it would show protocol errors if the packet was too short, so I simply started sending it 0x00s until it stopped reporting an error condition. I then could use the technique described above and tweak values to see what they did!
TODO: Photo of the MATE showing spoofed values
3. Emulating the MATE
(Talking directly to the MX/FX/FlexNET without a MATE)
“MateNET” Protocol
Here is the protocol format that I reverse engineered. It may not be 100% correct as there may definitely be un-documented fields.
General
All integers are big-endian (most significant byte first).
The first byte in every packet has its 9th bit (bit8) set to 1 (address byte), while all others have the 9th bit cleared (data byte)
The MATE sends a packet of the following format to the attached device:
TX
[0] Port (bit8=1)
00: Root device # Send the packet directly to the attached device (which could be a hub)
01-10: Hub port # Send the packet to a port on the hub
[1] Type
0x02: Query # Query/Get a register (Eg. aux relay state)
0x03: Control # Control/Set a register to param (Eg. inverter control)
0x04: Status # Request a status packet
0x22: Log Page # Request a log page
[2,3] Register # (Optional, the register to query/control) Default:0
[4,5] Param # (Optional, a parameter to pass, or a value to apply to the register) Default:0
[6,7] Checksum # Checksum: sum([0:-2]) % 0xFFFF
The device then responds with an appropriate packet, with the following format:
RX
[0] 0x03 (bit8=1) # Unsure, always seems to be 0x03
[1..n-2] # Payload
[-2:] # Checksum: sum([0:-2]) % 0xFFFF
Packet details are documented in the python library source code!