Posts

Showing posts from November, 2019

updated Pi NAS: automatic hard drive spin-down, and introducing virtual USB unplugging

After running for only a year my last Raspberry Pi Zero NAS died. The hard drive failed. It's not clear if it was the disk's fault, or if the 1A USB hub that powered the drive was insufficient and caused the heads to crash. Either way, the 500GB WD My Passport USB3 drive stopped being able to spin up, making a loud whir-click every 2 seconds or so. Time to build a new setup. The Pi itself seemed fine, thankfully.

I switched to a 2TB WD My Book with its own OEM 12v power supply, so I knew that the supplied power was sufficient. It's somewhat power hungry when spinning but idle: around 6.3w. If you can get it to go into standby mode that drops to 1.7w. Unplugging the USB drops it all the way to .7w. So we HAVE to get the drive to go into standby mode and it would be nice to get the drive unplugged somehow. Out of the box it appears to be stuck always spinning under linux, so there's no option but to dig in.

Unfortunately I was back to square one on making the drive spin down. So far no drive I've worked with (a "coolmax 500gb" and the "WD my passport 500gb") supported the same method for automatically going into standby under linux.

My failures:


hdparm -S 1 /dev/sda did nothing mounted or not.

 hdparm -B 1 /dev/sda  "HDIO_DRIVE_CMD failed: Input/output error APM_level      = not supported".

sdparm --flexible --command=stop /dev/sda just did nothing.

smartctl -d sat --set=standby,now /dev/sda did nothing while the drive was mounted. After unmounting it did.

There are ways to unmount drives that are not in use. While researching them I realized there might be a way to also unplug the usb, virtually, too... So I decided to roll my own solution.

My success: spin down and unplug USB too!


First, we need automount (autofs) which knows how to mount hard drives on demand and unmount after a timeout.

apt-get install autofs

we also need at for some of the scheduling.

apt-get install at

The key thing about autofs is that it executes a shell script before mounting, so we can do whatever magic we want as part of that.

Here's the script, named /etc/auto.disks


#!/bin/bash

# $1 is passed-over from automount
# key refers to the mount point we are looking for
key="$1"

if [ "$key" == "sda" ]; then
  echo 1 > /sys/devices/platform/soc/20980000.usb/buspower
  while ! [ -b /dev/$key ]; # wait until the file can be found
   do
    sleep .5
   done
   echo usbOffOnIdle | at now+11min
fi

# default mount options
opts="-fstype=ext4,rw"

# if a block device exists at /dev/[key]
# pass it back to automount
[ -b /dev/${key} ] && { echo "$opts :/dev/${key}"; }

SDA is my hard drive. We manage it as a special case, turning on the USB port when we want to mount it, and waiting until the device is detected before continuing. We also fire off an at command which will be responsible for shutting off the device when not being used, see below. It  will first start checking for idle at 11 minutes after mounting. 
echo 1 > sys/devices/platform/soc/20980000.usb/ buspower
is raspberry pi zero specific, and turns power on to the USB port (we'll turn it off later, and at boot)

We need to make autofs use this script by editing auto.master and adding 
/mnt /etc/auto.disks --timeout=600
which makes it call our script when trying to mount any device under /mnt, as in ls /mnt/sda . The timeout is in seconds.

So now we have a system that will turn on USB power whenever the drive is mounted, and will unmount 10 minutes after it is last used. But there's no unmount script triggered by autofs, which is why I need at, a handy scheduler that we can run in a loop until it detects the device is unmounted. 

Here's my usbOffOnIdle script, which I put in /usr/local/bin:
#!/bin/bash

if `ls /mnt | grep -q sda`; then  
 echo usbOffOnIdle | at now+10min  
else
 echo unmounted `date` >> /root/log
 smartctl -d sat --set=standby,now /dev/sda
 sleep 10
 echo 0 > /sys/devices/platform/soc/20980000.usb/buspower
fi

I use if `ls /mnt | grep -q sda`; to check for the mount because the regular  bash -e option will actually force the device to mount- oops!

If the drive is still mounted I just call myself again 10 minutes in the future and wait for autofs to do the umount-ing. If it's not mounted, I put the drive in standby (probably not needed) sleep a bit to be sure it happened, and then turn off power to the USB. 

In order to make the drive spin down first, you do need smartctl:

 apt-get install smartctl


Finally, we can use my script to unplug the drive on bootup. Using crontab -e add this line:


@reboot sleep 600; /usr/local/bin/usbOffOnIdle

Wait, that's it?

There's more than that to setting up a NAS, of course. All that was just to keep the power demands low, and to (probably) reduce wear on the drive. My previous post explains how to set up the rest of the Pi Zero NAS using Unison. You can ignore the parts about hard drive idling and fstab.

Some sources:

https://unix.stackexchange.com/questions/101680/automount-post-unmount-script

https://www.raspberrypi.org/forums/viewtopic.php?t=134351

Postscript

It's been running one year with 99.9% uptime. The only downtime seems to be network related but I'm not sure. I have it reboot once a week and that's reliably brought it back up. 


Raspberry Pi Automation

Since we have no network access on the road:

timedatectl set-ntp false

timedatectl set-time 16:00 --adjust-system-clock

map of the gpio pins

control the gpio pins from the shell just by echoing numbers to the file system

Here's a handy bash script for turning on pin(s). Name gpon, in /usr/local/bin, and chmod u+x gpon

#!/bin/bash
if [ ! -e /sys/class/gpio/gpio$1 ]; then
  echo "$1" > /sys/class/gpio/export
  sleep .25
fi

if [ `cat /sys/class/gpio/gpio$1/direction` != "out" ]; then
 echo "out" > /sys/class/gpio/gpio$1/direction
fi

echo "1" > /sys/class/gpio/gpio$1/value

if ! [ -z "$2" ]; then
 shift
 gpon $@
fi


likewise, gpoff:

#!/bin/bash
if [ ! -e /sys/class/gpio/gpio$1 ]; then
  echo "$1" > /sys/class/gpio/export
  sleep .25
fi

if [ `cat /sys/class/gpio/gpio$1/direction` != "out" ]; then
 echo "out" > /sys/class/gpio/gpio$1/direction
fi

echo "0" > /sys/class/gpio/gpio$1/value

if ! [ -z "$2" ]; then
 shift
 gpoff $@
fi


If timing is important, you can do the exporting at boot. 

#!/bin/bash
echo "14" > /sys/class/gpio/export
echo "15" > /sys/class/gpio/export
echo "18" > /sys/class/gpio/export
(etc.)

place in /user/local/bin/autoexec and then crontab -e, adding @reboot autoexec on a line by itself.

Setting up a webserver and PHP

Adding a clock for keeping time while the pi is off. Note the website is junk, no schematics anywhere for the plug-in version we bought. But fyi it plugs in flush with the 5v and 3.3v pins. The plug is passthru so you could connect more to the 5v and 3.3v pins. The guide is also wrong on how to enable i2c - it's under boot. Them the first time I query the clock I get the error "hwclock: ioctl(RTC_RD_TIME) to /dev/rtc to read the time failed: Invalid argument"Running sudo hwclock --show twice seems to solve the problem, or running hwclock --systohc (caution: resets the hw clock to the current system clock) 




Email me

Name

Email *

Message *