The hardware

TP-Link WL-740Nv4 is a great piece of hardware for hackers. It’s a low end home WIFI router that can run OpenWRT. It’s based on Atheros AR9331 SoC (which is hacking friendly as there is a lot of information about it on Internet), has MIPS CPU clocked at 400MHz, 4MB of flash, 32MB of RAM (clocked at 400MHz). What’s most important, however, is it’s price - you can easily buy it for about 16 USD here in Poland.

The project

I had to create cheap Ethernet based thermometer. While you can buy commercial products like this, they tend to cost more than 300 USD here in Poland. That’s strange, providing it’s so simple to create such device. I guess there’s not much demand on such devices and low production scale makes price high.

So my first though was to use Atmega or even Attiny (but there’s not much price difference in them these days) connected to ENC28J60 Ethernet module and DS18B20 1-wire thermometer. Problem is, it has to be cheap. Microcontroller is about 1.5 USD, Ethernet module is 6 USD, DC power supply - 2 USD, some voltage regulator, capacitors, connectors, Veroboard (or even a PCB), let’s say 3.5USD. That sums up to 13 USD and we have quite a lot of soldering and we we don’t have an enclosure. At the volume of about 250 devices, we can’t really use custom parts and I had real problems with finding ready made enclosure.

The solution

It quickly turned out that using microcontroller will be more expensive than using WL-740N which is full blown Linux based device with a lot more hardware inside (while we don’t need it right now, it may be useful in the future). I just needed 1 signal wire to control the thermometer. There are some GPIO controlled LEDs and buttons so I though it shouldn’t be much of a problem.

QSS button

I started with QSS button pin because I didn’t want to turn diodes off. In order to test it, I first removed gpio_button_hotplug kernel module, then exported and set GPIO26 as an output:

# rmmod gpio_button_hotplug
# echo 26 > /sys/class/gpio/export
# echo out > /sys/class/gpio/gpio26/direction
# echo 1 > /sys/class/gpio/gpio26/value

I was able to drive this pin without problems, driving it low with pull-up worked too (although I had to use 10k instead of 4k7 resistor) and I was also able to read values from this pin without problems. So I connected DS18B20 and loaded drivers:

# echo 26 > /sys/class/gpio/unexport
# insmod /lib/modules/3.3.8/wire.ko
# insmod /lib/modules/3.3.8/w1-gpio-custom.ko bus0=0,26,0
# insmod /lib/modules/3.3.8/w1-gpio.ko

and… it didn’t work. I’ve spend some time debugging it - checked all the connections, double checked pull-up resistor, connected logic analyzer and found nothing. Sensor was not responding for RESETs send by the host. I have no schematics and I’m not sure how this pin is connected to the SoC so I assumed there might me some capacitance that is preventing 1-wire to work.

WIFI diode

Prototype photo

Round 2, let’s connect to some diode. GPIO0 sounds good. I changed my connections, removed leds_gpio module, exported GPIO0 and did the same tests as before. It worked so I decided to test 1-wire:

# rmmod leds_gpio
# insmod /lib/modules/3.3.8/wire.ko
# insmod /lib/modules/3.3.8/w1-gpio-custom.ko bus0=0,0,0
# insmod /lib/modules/3.3.8/w1-gpio.ko

and.. it worked too:

# ls -1 /sys/bus/w1/drivers/w1_slave_driver/*/w1_slave
/sys/bus/w1/drivers/w1_slave_driver/28-000001bcb9b3/w1_slave
# cat /sys/bus/w1/drivers/w1_slave_driver/28-000001bcb9b3/w1_slave
dc 01 4b 46 7f ff 04 10 33 : crc=92 YES
dc 01 4b 46 7f ff 04 10 33 t=25000

Great, so I soldered everything up and rebooted device to test. It had no networking. I found out the reason after short debugging - it was pull-up resistor. It turns out that some of the GPIO pins on the SoC re used for boot configuration. GPIO0 is one of them - if it’s pulled high on boot, device boots in some strange way. I wasn’t really able to

find out what’s happening since serial console wasn’t working too.

3rd round

Third time lucky, I thought. This time I started by checking all the pins capabilities:

  • GPIO26 - QSS button (pull-up safe, OUT works, IN doesn’t work, w1 not friendly)
  • GPIO0 - WIFI diode (not pull-up safe, OUT works, IN works)
  • GPIO13 - WAN diode (pull-up safe, IN/OUT works)
  • GPIO14 - LAN1 diode (pull-up not safe, IN/OUT works)
  • GPIO15 - LAN2 diode (pull-up not safe, IN/OUT works)
  • GPIO16 - LAN3 diode (pull-up safe, IN/OUT works)
  • GPIO17 - LAN4 diode (pull-up safe, reversed logic)
  • GPIO27 - GEAR diode (pull-up safe, IN doesn’t work, reversed logic)
  • GPIO1 - QSS diode (pull-up not safe, IN doesn’t work)

I decided to use WAN diode (GPIO13) and this time everything worked as it should. I created simple CGI script that displays temperature over HTTP:

#!/bin/sh

echo "Content-type: text/html"
echo ""
echo '<html><head>'
echo '<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">'
echo '<meta http-equiv="refresh" content="5">'
echo '<title>Temperatura</title>'
echo '</head><body>'
for f in /sys/bus/w1/drivers/w1_slave_driver/*/w1_slave; do
    cat $f | sed -rn 's/.*t=(-?[0-9]+)$/\1<br>/p'  
done
echo '</body></html>'
exit 0

Conclusion

This was very quick proof of concept hack. Since I don’t really care (right now) about the diodes, I removed the driver for all of them. I could have changed it so that rest of the diodes works as they should, instead. The CGI script is not checking for CRC errors right now and checks the temperature each time we refresh the page instead of caching it.

This cheap device is great for hacking and since there are some more GPIO pins available there, I will probably do some more hacks with it in near future.