Working around webcam reliability issues
By Patrick Wigmore, , published: , updated
After running the Hedgehog Home Hub for some time, I came to realise: There did seem to be an issue with the webcam crashing, and it did seem to require the camera to be power-cycled in order to recover from it
I don’t know whether my webcam was defective, or whether it was simply not designed to be left plugged in for more than a few hours at a time, or whether I damaged it by not giving it a very good power supply. Whichever way, I wasn’t going to buy a new one, so finding a way to power cycle the existing camera looked like the only option.
The buck regulator supplying the USB voltage does actually have an enable terminal on it. So I decided to take an output from the red broadband LED to control this enable, so that when the LED comes on, the 5V power is cut, resetting all the USB devices when the LED goes out.
Then, I modified my hhcam-record
script so that if v4l2-ctl exits with an error, or if the camera’s device file is not present, the LED flashes once, briefly, before trying again, to power-cycle the camera.
In fact, it’s a bit more complicated than that. The flash drive needs to be unmounted and re-mounted at the appropriate points. I later discovered that, if there is still a v4l2-ctl process running, it needs to be terminated so that the flash drive can be unmounted cleanly.
I ended up splitting the functionality up into a couple of scripts;
hhcam-power
to abstract away the detail of turning the LED on or offhhcam-usbreset
to kill remaining v4l2-ctl processes, unmount the flash drive and cycle the USB power
/sbin/hhcam-power
:
#!/bin/sh
# Script to set USB power state on the modified BT Home Hub.
# This script has no niceties like checking whether it's a good idea
# to turn the power on or off. It's just a raw on/off control.
if [[ "$1" == "on" ]]; then
/bin/echo "0" > /sys/class/leds/bthomehubv5a\:red\:broadband/brightness
elif [[ "$1" == "off" ]]; then
/bin/echo "255" > /sys/class/leds/bthomehubv5a\:red\:broadband/brightness
elif [[ "$1" == "" || "$1" == "status" ]]; then
usb_power_state=$(cat /sys/class/leds/bthomehubv5a\:red\:broadband/brightness)
if [[ $usb_power_state -eq 0 ]]; then
echo "on"
else
echo "off"
fi
else
echo "Usage: $0 [on|off|status]"
echo "Set USB power state to \"on\" or \"off\", or show current state."
echo "Current state is shown if action is not specified."
fi
/sbin/hhcam-usbreset
:
#!/bin/sh
# Script to reset all the USB devices on the Hedgehog Home Hub.
# Intended to be used when the webcam hardware crashes.
# First, we need to unmount the flash drive
# If there are any lingering v4l2-ctl processes, those need terminating
# because they might prevent the filesystem from unmounting cleanly
if [[ "$(pgrep v4l2-ctl | wc -l)" -ne 0 ]]; then
killall -q -s TERM v4l2-ctl
# We need to hurry, because the longer we wait the bigger the gap
# in our recordings will end up being, so only give it a second.
sleep 1
if [[ "$(pgrep v4l2-ctl | wc -l)" -ne 0 ]]; then
killall -q -s KILL v4l2-ctl
fi
fi
# Now we should be safe to unmount the flash drive
# If it wasn't successful, force the unmount
/bin/umount /mnt/hhusb || /bin/umount -f /mnt/hhusb
# Now, power-cycle the USB devices.
/sbin/hhcam-power off
sleep 1
/sbin/hhcam-power on
# Hotplug will re-mount flash drive automatically, as configured in uci
# (To see mount config, run 'uci show fstab'.)
Auto mounting the USB flash drive
To re-mount the flash drive, I configured OpenWRT to mount the flash drive when it was plugged in.
I installed the package block-mount
. The OpenWRT Wiki seemed to imply that the block-mount
package had been removed in favour of ubox
, but what it actually meant was that the original block-mount
package had been replaced by a new one that drew its functionality from ubox, so you still had to install block-mount
in order to use the functionality.
opkg install block-mount
I could view the current uci config for fstab:
uci show fstab
This gave me:
fstab.@global[0]=global
fstab.@global[0].anon_swap='0'
fstab.@global[0].anon_mount='0'
fstab.@global[0].auto_swap='1'
fstab.@global[0].auto_mount='1'
fstab.@global[0].delay_root='5'
fstab.@global[0].check_fs='0'
Block detect provides a sample configuration on the standard output, which can be piped into uci to set a new config.
block detect | uci import fstab
uci can then be made to show the config
uci show fstab
This gave me:
fstab.@global[0]=global
fstab.@global[0].anon_swap='0'
fstab.@global[0].anon_mount='0'
fstab.@global[0].auto_swap='1'
fstab.@global[0].auto_mount='1'
fstab.@global[0].delay_root='5'
fstab.@global[0].check_fs='0'
fstab.@mount[0]=mount
fstab.@mount[0].target='/mnt/sda1'
fstab.@mount[0].uuid='8DC7-2C15'
fstab.@mount[0].enabled='0'
Which was not quite what I wanted, so I changed it a little.
uci set fstab.@mount[0].target='/mnt/hhusb'
uci set fstab.@mount[0].enabled='1'
I later realised it would be worth adding:
uci set fstab.@mount[0].options='noatime,errors=continue'
Then the settings can be committed
uci commit
Then, block mount mounts the drive, and it is also mounted on hotplug events.