Fuelled by insatiable curiosity
One day a friend became unhappy with his spa pool. The problem that the maximum selectable temperature of 41 degrees was not hot enough for him. The first option was to contact the manufacturer to see if they could increase the maximum set point temperature for him. This initially proved to be a dead end. Another option was to simply to trick the system mechanically by mounting the sensor in a different environment than it was trying to regulate. This indeed kept the pool's heaters on longer, but the water became too hot, and at around 50 degrees, the system shut down (using its backup temperature sensor). Finally, he tried placing some temperature insulating material between the sensor and the sensor's pool housing. This did the trick, and gave him a few extra degrees depending on the day. However, this fix was not reliable and was weather dependent. After exhausting the simplier options, the stage had been set for me to hack his spa pool.
I designed a gadget that tricked the spa pool controller in thinking it was a few degrees colder than it was. This was achieved by placing a gizmo between the temperature sensor and the controller. The device read the temperature sensor and also created a pseudo temperature sensor. The fake sensor was programmed to output a temperature a few degrees less than the real sensor. Because this hack involves a imitating a fairly common sensor (DS18B20), I've tried to generalise this page as you could probably use this information to hack other similar products.
Note that in the end, the manufacturer eventually bowed to his wishes and created custom firmware that permitted a higher pool temperature. This made this hack null and void, but I still had a bit of fun and learnt a few things along the way.
Before you consider pulling something like this off, bear in mind that this hack circumvent save guards that manufacturers legally have in place to ensure that their product complies with relevant health codes. These codes were design to protect you, the user, from being hurt. This hack also involves development of a 1-wire slave device, which is not overly 'legit' either. Note that I'm merely sharing this information for its educational value, rather than a 'how to guide to increase your spa pool temperature' etc. Hence, I'm not going to mention the specifics of which spa pool this hack was performed on. Information is provided as is, without warranty or any support from me should shit hit the fan. Okay, now that I've cleared my conscience, lets get on with the hack!
The first step with any hardware related hack is to gather as much intel as possible. A handy tool is an oscilloscope, which you should own anyway if you're into embedded systems and electronics like me. Bear in mind the end goal. You don't have to reverse engineer the entire system to hack it. In this case, I just had to trick the system's temperature sensor to think the water temperature was a couple of degrees colder than it actually was.
I started off by exposing the temperature sensor cable wires, and found out which ones were active. Of the six + shield wires, only three seemed active. There was a 0V, a VCC (+5V), and a data wire. It did not appear to be an analog voltage, suggesting it was a digital temperature sensor of some description. The probing the data wire revealed the following trace:
This looked suspiciously like a 1-wire bus, or at least from what I recalled from a previous project in which I interfaced to a DS18B20 temperature sensor. I suspected that this was probably a DS18B20 (what are the odds the manufacturer custom-made their own digital sensor that used one wire for data?), but I could not verify this as I could not get at the sensor. I decided it would be best to order a spare temperature sensor that I could later hack open. In the mean time, after confirming the signals were indeed the general form of a 1-wire bus, I operated under the assumption that this was either a DS18B20 sensor or a derivative.
Using the microcontroller of my choice for simple tasks, I built up a circuit on veroboard with an ATmega and a bunch of other components. The circuit I had in mind was to have the microcontroller interface directly to the temperature sensor. The microcontroller would also act as a virtual temperature sensor that essentially spat out the temperature data with a user-defined offset. This offset would be set by a selection of DIP switches. The schematic of the circuit I built ended up being:
A few things to note about the above schematic. There is at least one LED, and there is also a UART port. These are handy for debugging and development work, and you should put them in your embedded projects as I've bleated about before. I also like to put an I2C port on device, just incase it needs to be expanded. You'll also observe that I also clocked the microcontroller from a 16MHz external crystal. First, a faster CPU always helps when we're not overly concerned about power consumption (and that 1-wire buses, should it turn out to be that, are quite demanding on the timing). Since the device was to be deployed in a semi-harsh environment (high ambient temperature and potential water hazards), I put the electronics in a waterproof enclosure. A picture of the completed unit with the front cover removed:
In order to do this hack back at home, I needed two microcontroller boards - one that I had just made, and another to talk to it as it if was a 1-wire device. For the second device, I simply used a spare Arduino board I had lying around. The test setup looked like:
While I was waiting for a spare in-pool temperature sensor to arrive, I had a DS18B20 handy that I could work with developing the code to interface to the sensor, and later emulate it on the microcontroller. I started out by digging out an old project of mine which I had used this DS18B20 on an ATmega device, and tidied up the code. Once I had got the code to read the temperature correctly, I probed the data bus to confirm the results from the investigation phase. They indeed looked similar, and the signals matched that of a 'convert temperature to all devices' (reset_pulse-0xCC-0x44) sequence. When the replacement in-pool sensor arrived, I opened it up and confirmed that it indeed had a DS18B20 - great news!
From the data collected during investigation I could not rule out the possibility that the spa pool controller firstly scans the bus, and later addresses the sensors by their ROM code. I decided to simplify my work by making the assumption it did not and just assumed one 1-wire device on the bus. This should be a fair assumption considering I doubt the manufacturer would hard-code ROM codes in the controllers firmware should they want their product to have multiple temperature sensors while having knowledge of where they were in the pool. There was also only one port for an in-pool temperature sensor, a dead giveaway. Another simplifying assumption I made was that the default startup parameters were used with the sensor.
I set out creating a simple, slim and incomplete 1-wire slave driver. I did have a squiz on the internet, however, I decided I would be more educational for me to roll my own code. This ended out being not too difficult, and the resources my 1-wire slave driver used was a 16-bit timer (free running counter with overflow and compare match interrupts), and a GPIO pin with an external interrupt. I started off with the basics - that is respond to a reset pulse with a presence pulse. After that had been tested, I then wrote code to make it read a byte, and then finally transmit a byte.
I also added various other nice features to my driver. In its initialisation, I hand it a pointer to the scatch pad buffer, as well as provide it a function callback when it receives bytes other than control bytes and ones requesting transmission of the scratch pad data. This concluded the software development at home, and I was able to get the microcontroller to read an actual DS18B20 sensor, and respond in a 1-wire bus as if itself was a DS18B20 sensor.
Once I had the software and hardware sorted, I returned to my friends place to test the device out. I knew that I had made assumptions, and that it would be a minor miracle if it worked first time. As expected, it did not. The first problem that was encoutered was that the bus voltage sagged from 5V to around 3V when the device was plugged in. Remember V = IR? This observation then implies that the manufacture has put in an inline resistor on the supply, which would be a good idea if it was only powering a DS18B20 sensor and to provide protection if something short circuits. I quickly rewired the circuit and gave the device external power. Much better, the microcontroller is now running.
Unfortunately, this was not the only problem. The device was happily reading the temperature sensor directly, but the spa pool controller was not reading the microcontroller. Out came the oscilloscope, and when the data line was probed again, it yielded some slightly different signals than what I saw in the investigation phase. I hooked the temperature sensor straight into the spa pool controller, and the first trace looked like:
This is close to what was expected. The bus sort of starts off with a reset pulse, followed by a presence pulse sent from the the sensor. This is immediately followed by 11 bytes, which are likely to be SKIP_ROM, READ_SCRATCHPAD, and then the 9 byte scratchpad. The bus then sends off another sequence, the convert temperature instruction to all devices. However, the first 8 pulses preceding the first reset pulse were something I had not seen before, and my 1-wire slave state machine had not been designed to handle them. I had made a simplifying assumption that the first falling edge after a while would usually be the start of a reset pulse. A closeup of the first quarter or so of the activity on the bus:
I also encountered more wierd signals. The bus appeared to be sending out two different types of sequences. This was in contrast to what was observed in the investigation, whereby it was only sending out a 'convert temperature to all devices' command every 30ms. There was still activity on the bus every 30ms, however, most of the time it would be of a small 8-bit packet (shown below), and a full scratch pad read and covert temperature packet every 600ms.
From the above observations, I had concluded that the communication with the sensor from the spa pool controller must not have been right in the prelimary investigation. I theorised that the controller was not very persistant with the in-pool sensor, and if it failed to read it more than a couple of times in a row, it reverted to its less accurate built-in sensor. I then imagined that it would only revert back to the in-pool sensor if its power was cycled. This theory was proven by firstly having the sensor being read correctly, then briefly removing and plugging it back in. Another odd thing though was that the controller still emitted the 'convert temperature to all devices' command every 30ms after this (and with the sensor completely removed, the reset pulse was only emitted every 30ms). I guess the investigatory observations were with the controller reverting to its built-in sensor, but still dishing out convert temperature commands to the sensor (despite not actually being queried for its last temperature reading!).
Armed with this extra knowledge and scope traces, I headed back home to rework the code. I started the hypothesis that the extra 'random' pulses actually meaned something and were part of the 1-wire protocol. I had another look at how other people interfaced to the DS18B20. It then struck me that these extra pulses could be checks to see if the sensor had completed a temperature conversion. This is usually done, provided that only one device is on the bus, by simply issuing a master read command. I noticed that transfer of a temperature data followed when a preliminary read returned true (i.e., non-zero). After making the required mods, I returned for another round of testing with the spa pool. Hey presto, it works!
Before downloading these files pertaining to this hack, you agree to accept all liability for the harm you may cause to your spa pool or to yourself. The resources for this hack:
1. Software for the device to read an actual DS18B20 sensor, and itself emulate a DS18B20: ds18b20_hack.zip
2. Software for another board to read the device: 1-wire.zip
Also note that the 1-wire slave interface I've written is far from complete. It the thing you are trying to hack has more than one 1-wire devices on the same bus, you'll need to implement a fair bit more code to get things to work. I have also made some simplifying assumptions which may limit the extendability of the driver. Perhaps have a look at Matthias Urlichs implementation on GitHub.