In this followup to my previous post about
hacking the Kobo, I present a few other tips.
Reindexing the collection
It turns out that you can reindex the collection on the device without
actually pluging it in a computer. This is very useful if you add or remove
files to the device directly (using sftp, for instance), though you do not need
it if you are just replacing existing files. The idea is that
nickel
uses the FIFO /tmp/nickel-hardware-status
to
get notified about events: if you write usb plug add
or usb
plug remove
to this file, it will act as if the Kobo had been connected
or disconnected to the computer.
Hence, you can create a file reindex.sh
containing:
echo usb plug add >> /tmp/nickel-hardware-status \
&& sleep 10 \
&& echo usb plug remove
Make it executable and run it with nohup ./reindex.sh
.
nickel
will present a dialog, quickly select "connect", and wait
for the fake disconnection and reindexing to take place. Unfortunately, this
will disconnect the Wi-Fi (hence the nohup
business).
[This
one was sent to me by Andreas Heider. Thanks a lot for suggesting this!] I also noticed that, when you download a file through the
built-in web-browser, it will get indexed. It could be sensible to run a
minimalistic web-server on the device to serve files already stored on the
device to make the device index them. Maybe I'll investigate this
later.
Automatic reverse ssh
You might want to connect to the device easily without having to find out its
IP or worrying about NATs. The right tool for this problem is
openvpn
, but it has a lot of dependencies and isn't exactly light.
A satisfactory (and probably simpler) alternative is to get the device to
reverse ssh to a trusted server (with key-based authentication) when it connects
to the Internet, so that you can always use this to get a shell.
I already mentioned
how to get dropbear running on the device. To get reverse ssh working, it turns
out that you have to add busybox ifconfig lo up
to the
/etc/init.d/rcS
file because it isn't done by default. Then,
generate a key using dropbearkey -f /root/.ssh/id_rsa -t rsa
and
copy the fingerprint returned by the command to your
authorized_keys
file on the trusted server. Obviously, if you worry
that unauthorized people might have access to your device, you should use an
account with very little permissions.
Now, once you have checked that dbclient -i /root/.ssh/id_rsa
USER@SERVER
works without asking for a password, you can add the following
reverse ssh command at the end of the renew|bound)
section of file
/etc/udhcpc.d/default.script
(note the unsafe "-y" to avoid unknown
host key issues, and adapt if you don't like this):
dbclient -y -i /root/.ssh/id_rsa -R 4080:127.0.0.1:22 USER@SERVER < dev/ptmx &
Now, when obtaining or renewing a DHCP lease, the device should run the
reverse ssh. I have found this to be pretty fragile when testing, for unknown
reasons: workarounds you might want to try are using the IP address of SERVER
directly (in case DNS resolution is not working properly yet) or moving the
command to a separate script which sleeps for a few seconds before running the
command.
Leaving the Wi-Fi active
I was complaining in my previous post about the Wi-Fi getting disabled after a
few minutes. According to the (very insightful) guide Hacking the Kobo Touch for
Dummies, the responsible for this is nickel
, and, indeed, just
running killall nickel
will fix the problem. Obviously, without
nickel
running, there is little you can do with the device except
from using ssh. When you're done, the /etc/init.d/rcS
file suggests
that you can get nickel
back by issuing something like:
INTERFACE=wlan0
WIFI_MODULE=ar6000
if [ $PLATFORM == ntx508 ]; then
INTERFACE=eth0
WIFI_MODULE=dhd
fi
export INTERFACE
export WIFI_MODULE
export QWS_MOUSE_PROTO="tslib_nocal:/dev/input/event1"
export QWS_KEYBOARD=imx508kbd:/dev/input/event0
export QWS_DISPLAY=Transformed:imx508:Rot90
export NICKEL_HOME=/mnt/onboard/.kobo
export LD_LIBRARY_PATH=/usr/local/Kobo
export WIFI_MODULE_PATH=/drivers/$PLATFORM/wifi/$WIFI_MODULE.ko
export LANG=en_US.UTF-8
export UBOOT_MMC=/etc/u-boot/$PLATFORM/u-boot.mmc
export UBOOT_RECOVERY=/etc/u-boot/$PLATFORM/u-boot.recovery
/usr/local/Kobo/nickel -qws
Trouble is, this does not seem to work perfectly (the new nickel
instance is a bit broken, especially with regards to Wi-Fi). I would need to
investigate more. Of course, to fix things for real, you can always just reboot
the device.
Displaying stuff on the Kobo from the shell
People have already figured out
how to display stuff on the Kobo's screen from the shell. It's pretty
minimalistic (only images in a weird format). To spare a click, here is how you
do it:
ffmpeg -i $INPUT.png -vf transpose=2 -f rawvideo
-pix_fmt rgb565 -s 600x800 -y $OUTPUT.raw
cat $OUTPUT.raw | ssh DEVICE /usr/local/Kobo/pickel showpic
Of course, this means that you can replace the images in
/etc/images
and /usr/local/Kobo/slideshow
by other
images. Disappointingly, though, the interesting images (the "powered off" and
"sleeping" logos) don't seem to be there and seem to be displayed in another
way. The most interesting thing you can customize is the boot animation
(/etc/images/on-*.raw.gz
).
Retrieving touchscreen events from the device
The (very insightful) guide Hacking the Kobo Touch for
Dummies also explains how to recover the touchscreen input events from the device:
evtest /dev/input/event1
There is a lot you can do by forwarding these events to your computer and
interpreting them creatively (a custom remote control, for instance). As always,
buffering issues are a
problem when trying to retrieve the events synchronously. Here is a possible way
to retrieve the useful info, where DEVICE is where you can reach the device:
cat <(echo 'evtest /dev/input/event1;') - |
socat EXEC:'ssh DEVICE',pty,ctty,echo=0 STDIO |
sed -u 's/$/\n/' |
stdbuf -i0 -o0 -e0 cut -d ' ' -f 9,11,13
In case you're wondering, the sed command is because the newline at the end
of the last event is outputted when the next one happens, which isn't really
convenient if you want to pipe the data to something.
Here is a small Python program to run on your computer which accepts this
input and will draw what you do on your touchscreen:
import pygame
import pygame.draw
import sys
pygame.init()
def line(x1, y1, x2, y2, r=255, g=255, b=255):
global window
pygame.draw.aaline(window, (r, g, b), (x1, y1), (x2, y2))
window = pygame.display.set_mode((600, 800))
pre = None
while True:
l = sys.stdin.readline()
if not l:
break
l = l.rstrip().split(' ')[0:3]
if len(l) != 3 or '' in l:
continue
print l
if pre:
line(600-int(pre[1]), int(pre[0]), 600-int(l[1]), int(l[0]))
if l[2] != '0':
pre = l
else:
pre = None
pygame.display.flip()
Obviously, you should try to minimize the network latency if you want this to
be pleasant.