Rss

  • youtube
  • linkedin
  • google

Dropping Dropbox + Hardware Hacking

Recently, I purchased some very cheap mobile routers for a project idea. That project didn’t work out, so I needed something do with them. Fortunately, I had another problem which needed to be solved, and this gave me a good excuse to do a little hardware hacking!

SO… Dropbox and I are breaking up. Well, cutting back at least. Also, I met someone new. Here’s the “why”, the “who”, and the “how”.

Why?

The cloud is becoming unavoidable. I’ve been using Dropbox for a while now, and it’s a wonderful service. When they announced that you could get free, bonus storage for enabling automatic photo uploads, I jumped at the opportunity.

For a while, it was great. I’d take a picture from my phone, Dropbox would upload it automatically, my desktop and laptop would download it automatically… all was right with the world.

And then it wasn’t.

What happened? Well, I ran out of space while I was in the Philippines. I took SOOO many pictures while I was there, and it didn’t take long to eat up the rest of my 5 or 6gb. To make matters worse, the camera on my phone will only save photos to the internal storage, which kept running out of space – so I had to keep moving images manually to the SD card. So I had some pictures on the SD card, some on the internal storage, some in my Dropbox, and some moved out of my Dropbox and into a plain folder on my laptop to make room for new photos.

STILL WORSE: Dropbox RENAMES YOUR PICTURES when automatically uploaded from your camera. So while I was trying to combine all of the photos from all of these places, many of them were duplicates but with different filenames. ARGH!

Eventually, I sorted all of it out – but it was a pain. Wouldn’t have been an issue if I had more space though.. so I started shopping. Google Drive, Box.com, Amazon S3, etc, etc. None of them fit all of my needs (linux client, android client, automatic photo uploads), and none of them had enough space. I thought about separating my files – pictures on Dropbox, documents on Google Drive, code on Box.com… but that sounded messy. Besides, even if I moved all of the non-photos from Dropbox, it still wasn’t enough room.

Who?

Would I bite the bullet and drop money on this? Would I have to switch back to traditional backup methods? Negative. Enter: OwnCloud.

It’s open source, which means – if you have a computer, you can host your own file server, available anywhere you have an Internet connection. It’s also extensible with it’s plugin API. Great! But does it have a Linux client? YES! Alright, but does it have an Android client? YES! Does it support auto-photo-uploads? YES! Even the Android client is open source ($1 in the Google Play store… or FREE if you download the source and build the .apk yourself!).

I heard about this project when they first started, but it was quite lacking. It’s come a long way, and not a moment to soon.

How?

Naturally, my instinct was to load the code on domstyle.net - unfortunately, it requires a newer version of PHP than my hosting company has installed. I asked what their upgrade schedule was, and they seemed confused about the word “schedule.” No problem, the PC I built a few years ago is underused and has an 80gb hard drive with nothing on it… PERFECT!

Format hard drive, install a LAMP stack, SSL cert, install OwnCloud, DONE! In less than an hour I was moving files out of the Dropbox and into the OwnCloud folder. Plugged the PC in downstairs in the laundry room – out of site, out of mind.

“So, what… you just leave your computer on 24/7? That’s not very green, Dom!”

Totally agree. So I built a Wake-On-Wifi solution. Wake-On-LAN has existed for a long time, but not all hardware supports it. And some that claim to support don’t actually work correctly. Even if it did work properly on my machine, I had no way to send the magic wake-up packet to the computer through the router.

Thankfully, I had another router which was looking for a purpose. Enter: the TP-Link 703n. As is, this little device can’t help me…

First step: replace the firmware with OpenWRT. That’s right, we’re putting Linux on it.

Second step: hijack some GPIO pins and tap into the power. There are two pins on the processor inside this thing which aren’t being used. With linux installed, I can control them. And by controlling them, I can control other things… like a relay.IMG_20130209_181712

IMG_20130209_181854

Third step: wire it up to the relay!

IMG_20130215_162927

Fourth step: wire it up to the power button on the PC

IMG_20130215_165046

Final step: code! The final step is really a two-parter – code on the router to wake up the computer, and code on the computer to shutdown when idle.

The computer was easy. A cron job runs every 5 minutes which monitors network activity for the next 10 (yes, the overlap is intentional). If the computer sends and receives less than 1kb during that period, it sends a shutdown signal with a 2 minute warning.

The router pings the PC to see if it’s alive.

  • If it isn’t alive, the router borrows the PC’s static IP address so it can monitor for traffic. As soon as the router hears any traffic on port 443 or port 80, it activates the relay for 1 second, which simulates the power button being pressed. It also gives up the IP address and takes back its own.
  • If it is alive, the router pings the PC at regular intervals and does nothing while it continues receiving a response.

How well does it work?

So far, so good! The PC takes ~20 seconds to fire up, which is quick enough that requests don’t timeout while waiting. So I can snap a pic on my phone, it attempts the upload, the router hears the request and powers on the PC, the PC takes over the IP address and processes the request! Likewise, I can turn on the OwnCloud sync client on any of my computers, and 10 minutes after I turn it off, the computer shuts itself down. Over-engineered? Probably. A hack-job? Perhaps… but I am very pleased nonetheless!

UPDATE

By request, here’s the server code which is ran via cron job:

#!/bin/bash

#/root/shutdown-if-idle.sh

# Dominic Canare
# dom@domstyle.net

shutdownDelay=2  # minutes to wait before shutdown
samplePeriod=300 # seconds to query network per sample
sampleCount=2    # number of samples

threshold=1      # kb threshold for idle status
iface=wlan0      # interface to watch

# get current net stats | look at the average for our iface || get total transfer | truncate to integer
usage=$(sar -n DEV $samplePeriod $sampleCount | grep "Average: *$iface" | tr -s " ")
tx=$(echo "$usage" | cut -d " " -f 5 | cut -d "." -f 1)
rx=$(echo "$usage" | cut -d " " -f 6 | cut -d "." -f 1)
echo "Usage is '$tx' + '$rx' vs '$threshold' $(date)" >> /tmp/usage.log
usage=$(($tx + $rx))

if [ "$usage" -lt "$threshold" ]; then
	cp /tmp/usage.log /root/usage.log
	/sbin/shutdown -hP +$shutdownDelay "System is idle. You have $shutdownDelay minutes to save your work." &> /tmp/test-shutdown-log
else
	killall shutdown
fi

and the router code

#!/bin/sh

#/root/wake-me-up.sh

# Dominic Canare
# dom@domstyle.net

IP_SUBSCRIPTION=192.168.1.8
IP_HEARTBEAT=192.168.3.2
STANDBY_IP=192.168.1.3

tcpdump=/tmp/usr/sbin/tcpdump

GPIO_PIN=29
GPIO_PATH=/sys/class/gpio/gpio$GPIO_PIN

sleeping=0

killEverything() {
	kill 0
}

trap killEverything SIGINT
trap killEverything SIGTERM

installDependencies() {
	opkg update
	# there isn't enough flash memory, but there's plenty of RAM
	# this will install tcpdump binaries to RAM
	opkg -d ram install tcpdump
}

setupGPIO() {
	# enable pin 29 on the processor for output
	# default it to 0 (low)
	echo "$GPIO_PIN" > /sys/class/gpio/export
	echo "out" > $GPIO_PATH/direction
	echo "0" > $GPIO_PATH/value
}

pressButton() {
		echo "1" > $GPIO_PATH/value
		sleep 1
		echo "0" > $GPIO_PATH/value
}

setIP() {
	# if the requested IP address is not currently set, set it and ping the router, so the router knows who we are
	currentIP=`ifconfig wlan0 | grep "inet addr" | tr ":" " " | awk '{ print $3 }'`
	if [ "$currentIP" != "$1" ]; then
		ifconfig wlan0 $1
		ping -W 1 -c 1 192.168.1.1 &>/dev/null &
	fi
}

checkForClient() {
	while [ 1 ];
	do
		if [ $sleeping -eq 1 ]; then
			setIP $STANDBY_IP
		else
			# send one PING to the server
			ping -c 1 $IP_HEARTBEAT
			if [ "$?" -eq 0 ]; then
				# the server is awake, we should be standing by
				setIP $STANDBY_IP
			else
				# the server is asleep, we should hijack his IP
				setIP $IP_SUBSCRIPTION
			fi
		fi
		sleep 10
	done
}

waitForWakeUp() {
	echo "Waiting for wakeup call..." &> /tmp/status

	# listen for web traffic on the wireless interface
	$tcpdump -vv -c 1 -i wlan0 "dst $IP_SUBSCRIPTION and (dst port 80 or dst port 443)" > /tmp/tcpdump

	echo "POWER button press" &> /tmp/status
	pressButton
	date > /tmp/last-wakeup

	setIP $STANDBY_IP
	# wait a bit while the server starts up
	sleeping=1
	sleep 300
	sleeping=0
}

if [ ! -e "$tcpdump" ]; then
	installDependencies
fi
if [ ! -e "$GPIO_PATH" ]; then
	setupGPIO
fi

checkForClient &
while [ 1 ];
do
	waitForWakeUp
done

Which is installed as a service via /etc/init.d/wake-me-up:

#!/bin/sh /etc/rc.common

#/etc/init.d/wake-me-up

START=99

start() {
	/root/wake-me-up.sh &
}

stop() {
	killall -9 wake-me-up.sh
}

Comments are closed.

Comments (37)

  1. It’s really impressing what you did here, awesome work, awesome post. Thank you very much!
    Greetings from Buenos Aires, Argentina.

  2. I would be very grateful if you can post the script of the router and the cron job. Thanks again!!

  3. regulus

    I’d love to see how you controlled the GPIO on the router!
    That’s something I would find extremely interested in doing myself.

    • it was actually pretty simple. i had to de-solder a resistor and add a wire. within linux, the high/low state can be controlled via /sys/class/gpio files. have a look at the code i posted for the router

  4. chinmay

    hey Dom nice work man.. i hv been looking for dis kind of solution and ur one just work for me… i going to try it. Thanks ya. :)

    • thanks, and good luck on yours! if there’s anything i can help you out with, let me know :)

  5. Greg

    Very nice!
    Now i have got a good solution for wake on wireless lan =)

    1) Why do you use different heartbeat and subscription ips?
    2) I think it would be better to use a transistor and a reverse diode for circuit protection and for better stability of the relay

    Greets Greg

    • originally, i tried to use a single IP, but it did not work well if i powered up the server manually. i tried putting the router’s wifi in monitor mode to detect packets coming from the server, but it was unreliable. in the end, sending the heartbeat ping over wired ethernet was simpler and far more reliable.

      and you’re absolutely right about needing to add the transistor/diode to the relay. i still need to do this!

  6. Steve

    For those of you who either can’t handle or don’t want to handle the DIY solution, Bitcasa offers unlimited cloud storage and auto upload of photos from iOS and Android.

    http://l.bitcasa.com/.HBpcBa-

    I don’t work for them or anything (that is a referral link) but it might be a solution for the less technical.

  7. JackSchofield

    You could have used Microsoft SkyDrive to supplement Dropbox. This gives you 7GB of free storage and retains your original file names and file sizes.

    SkyDrive comes free with a Microsoft email account so you could easily make it 14GB of free storage by opening an Outlook.com account, and so on.

    SkyDrive supports drag-and-drop uploading but you can also have sync accounts on your various computers.

    • decent, but 14gb is still far smaller than the 80gb drive i had serving no other purpose. and if i run out of that space, upgrading is a one-time cost (instead of paying $X every month for extra space. also, does SkyDrive have linux and android clients?

  8. rakekniven

    Very nice job!

  9. Ted M

    very cool! I haven’t had reliability with WOL, but do use a low power <4W pogo 1.2ghz running arch linux arm with a large usb stick or usb drive (that will sleep and wake). to create a fast in-house sharing solution.

  10. Love your code! Clean and documented! How rare is that!

    Nice work on the project.

  11. Great piece to learn bash scripting!
    you could use GPIO8 to switch off 5V USB power and control a relay.

  12. Could you help me, I´m interested in the project, and want to make one of my own

  13. WOW! I’m very excited to find someone else that is doing this! I had been working on a solution just like this for my own owncloud server but was stuck at how to wake the server up without WOL support. With the cost you posted I should be able to utilize my raspberry pi to wake it up. Thank you again, this makes me very excited!

    • this was a fun project, indeed! but probably overkill.

      say, if you’ve got a Raspberry Pi lying around and plan on keeping it up 24/7, why don’t you just host OwnCloud on your Pi?

      • My owncloud server had several terabytes of disk in a raidz2. It would be hard for my pi to support that much space via USB. My pi is already on 24/7 for other things anyway. I’ll probably make a small modification to your setup and instead of having the pi change its ip, I’ll stand up a virtual ethernet interface and assign the IP to it so my pi can remain online with its normal IP add well.

  14. Ricardo N Feliciano (@IronPatriotNY)

    Awesome read, awesome project.

  15. Leo

    Great piece of crafty work.

    I like to tweak router setup and need your help.
    From the two gpio pins, since only one is used to drive the relay, how do I use the other as an input?.
    In your checkforclient() routine, instead of pinging to see if the server is live, how do I use the 2nd gpio pin to monitor low/high status?
    Then when the 2nd gpio pin goes low, how do I set the router interface up and set the router ip as the server ip?
    When powering on the server, instead of setting a standbyip as the router ip, how do I set the router interface administratively down?
    Also instead of sending one pulse to relay, how do I keep that pin high?

    Thanks Dom
    Two thumbs up from Toronto, Canada

    • Have a look at the setupGPIO function:

      setupGPIO() {
      	echo "$GPIO_PIN" > /sys/class/gpio/export
      	echo "out" > $GPIO_PATH/direction
      	echo "0" > $GPIO_PATH/value
      }

      The first line tells the system that you’re going to start controlling that pin.
      The second line says that it’s an output. To use it as an input, you would echo “in” instead.

      Then, to read the value, you can simply cat the file:

      value=`cat /sys/class/gpio/gpio29/value`

      and you can test it with an if statement:

      if [ $value -eq 1 ]; then
          # do something when the pin is high
      else
          # do something when the pin is low
      fi

      I’m not sure I fully understand your goal though. Do you want to hook the second GPIO pin up to the server to monitor power status of the server? I tried to keep the server and router as separate circuits (hence the relay). To keep with this idea, you could use a phototransistor to detect the power LED on the server.

      Lastly, if you want to maintain a GPIO pin high, set it’s value to 1 and just leave it at one. In my code, I set it to 1, sleep for a second, and then set it to 0. If I left it high, it would be equivalent to holding down the power button.

      • Leo

        Dom – thanks for the input.

        I like to adapt this work for a HP 8150 network printer. When the printer goes to power-save mode, it is using about 63 watts and noisy. The only thing it does (visibly at least) is turn the back-light of display off and say “power save mode” on the displayl. The on-off button shuts it down (incl. nic) so no one can print to it.

        Here’s the plan:
        Make a Y connection to the cat5 cable, one goes to printer and other to the tp-link.
        1. Tp-link will monitor printer traffic destined to printer ip at port 9600
        2. When there is traffic, it will drive a relay to turn the power-on.
        3. Tp-link will turn its interface down or set to a non-used ip.
        4. Tp-link will sleep until next gpio pin goes to low (when the power-save mode of printer kicks in)
        5. It will then turn relay off (printer power off), turn the interface back on and hijack the printer ip and listen on port 9600 for printer traffic.

  16. S G

    Hi,

    This is a fantastic project. Do you know if it is doable on a Linksys WRT54 router instead of a TP-Link?

  17. Very impressed with the concept of monitoring network traffic and cycling the sleep functionality. I for one will be making use of this code ;)

    Really nice work!

  18. Phil2.0

    and yet no surprise by unattented power on?
    Until your machine is the target “attack”(scan, attack etc)?

    • nothing like that so far! i keep a log of power events, and every one of them (so far) has been expected

  19. Gary

    Love your project.
    I have a question if you don’t mind. Besides the GPIO29 what is the other non-used gpio pin?
    Thanks.