Tuesday, September 10, 2013

Kindle digital photo frame part 5: testing and finalizing

The scripting and programming is mostly complete. Time to test the Kindle. For the purpose of testing, I set the Kindle to change screensavers every 5 minutes instead of every 3 hours. It works flawlessly for hours at a time, then fails. It always fails in the same way: displaying no picture in screensaver mode. Even manually pressing the button power twice does not solve it. Perhaps the Blanket program crashed or something, I don't know, but it fixes itself after a restart.

After hours of debugging I still cannot figure out what the problem is. Decided to call it quits, but added a recovery option should that happen. I installed KUAL and kterm, so that after a reboot I can manually run my screensaver changing script again.

Finally, I decided to consolidate both the script to change screensavers and that to download images. Here's the final masterpiece:

I've reset the screensavers to change every 3 hours instead of 5 minutes. It'll still be a few days before I present this to my girlfriend, a final few days to test and make sure it works perfect!

Kindle digital photo frame part 4: downloading pictures from the net + more screensaver problems

Even more problems came to bite me. I had intended to use wget with some options like -r to easily download all the images that I have listed on the front page of my CakePHP backend. However, Kindle's busybox wget is quite primitive; it does not have so many options, and I am forced to perform grep, awk and a lot more scripting to download the pictures that I uploaded. Here's the code:

In addition, Kindle renames screensavers after restart. All the photos inside the screensavers folder will be renamed to bg_xsmall_ssXX.png. That implies that a maximum of 100 photos are allowed. In addition, if the order of the photos is to be randomized via creating a file called "random", all the photos' filenames will be randomized again upon bootup, meaning I won't be able to tell if /var/local/blanket/screensaver/last_ss points to a photo or downloaded image.

In view of these, I decided to rotate the photos myself by placing one 1 photo inside the screensavers directory: bg_small_ss00.png, and replace that file everytime I change my screensaver. As usual, Busybox is so limited in functionality that I have to go through massive scripting just to choose a random photo or determine a random number. This is my updated screensaver-changing script:

Monday, September 9, 2013

MK 809 III Android mini PC

The MK 809 looks like an oversized thumb drive that plugs into a HDMI port, but it is a full fledged android pc. I was quite excited when I saw it on sale on Qoo10; I've been wanting a powerful arm computer to test BOINC on, and the MK 809 III has 4 cores, fits into my palm and uses a phone charger. Sweet!

I'm more bitter about the price though. The Qoo10 seller was selling at S$96 including shipping fees. That's out of my budget. Decided to source upstream, and found it alibaba.com, selling for US$45 a piece. However, there is a minimum order of 10. Thus finally, I went to it's sister site, aliexpress, and found one selling at US$57. Perfect!

I wonder if an Android OS can be used for programming?

Would update when the item arrives.

Final Fantasy Fables: Chocobo's Dungeon

Just cleared the 100 levels dungeon in Chopin's dungeon for the Wii. I killed the boss at level 71, and was level 8 dragoon wearing a genji's  talon+79 and king's saddle+57 when I did it. All I did was spam Holy Lance and Gungnir, drink ethers like crazy, and ate just one Phoenix down when near death.

Phew, that was fast!

Sunday, September 8, 2013

Kindle digital photo frame part 3: Changing screensavers every 3 hours

Getting the Kindle to change its screensaver is as simple as running /usr/bin/powerd_test -p twice. The script to do so:

I wanted the screensaver to be changed every 3 hours, excluding nighttime. It's a matter of appending to /etc/crontab/root:

Then I ran into a pesky problem. The screensaver changer works if I ran it manually. It doesn't work via the cron job. Or rather, I discovered that the Kindle goes to sleep after 1 minute in screensaver mode.

After fiddling with /usr/bin/powerd_test -s for quite some time I discovered the following. The Kindle has 4 power levels: Active, Screen Saver, Ready to suspend, and sleep. Active stays on for 10 minutes, Screen Saver stays on for 1 minute, and Ready to suspend stays on for 5 seconds. This is my log:

I can wake up my Kindle via the command "lipc-set-prop com.lab126.powerd wakeUp 1" provided it is in one of those 3 states. To try and suspend my Kindle in the "ScreenSaver" or "Ready to suspend" state for as long as possible, I investigated the seemingly useless property, deferSuspend, from com.lab126.powerd. When you run it, it gives the error:

com.lab126.powerd failed to set value for property deferSuspend (0x8 lipcErrNoSuchProperty)

I discovered that the property can only be set DURING THE READY TO SUSPEND STATE. That means after 11 minutes of leaving the Kindle alone I have a 5 second window to change the time left in "Ready to Suspend" state. See my logs:

My screensaver changing script ended up as:

Digital photo frame: Kindle Touch vs Kindle Keyboard

In the midst of hacking my newly acquired Kindle keyboard I got myself another Kindle: this time a Kindle Touch 3G. I intended to use one as the photo frame and another as my ebook reader.

Now, which to use for my digital photo frame?

The Touch, being slimmer without a keyboard, would be easier to conceal within a frame. Not to mention that my Keyboard came with a leather case and I like it a lot. The Touch seems like a natural choice.

Until I got down to hacking it. After the usual jailbreaking and stuff, I found out that the Touch only supports PNG. This, while my Kindle keyboard can support jpg, gif and images of all shapes and sizes. Ended up using the Kindle Touch anyway.

Kindle digital photo frame part 2

Now that I can SSH into the Kindle I need to figure out the functions that I need. I want the Kindle to cycle through me and my gf's photos every 3 hours. Apart from that, I want to be able to upload photos of cute cats or love messages onto the internet, and those will show up, ONLY ONCE, on the Kindle.

For cycling photos, I need to:
  1. Wake up the Kindle from sleeping
  2. Put it to sleep again.

(1) is easy to find. The command, lipc-set-prop com.lab126.powerd wakeUp 1, will wake up the Kindle. (http://www.mobileread.com/. forums/archive/index.php/t-160328.html) Fortunately, cron works even when the Kindle is sleeping, so cycling photos is definitely possible.

(2) took me a while to find. The command is, /usr/bin/powerd_test -p . (http://www.mobileread.com/forums/showthread.php?t=220810) It works for waking up the Kindle as well, so now I can simply cycle my photos by running this command twice!

For displaying my uploaded pictures, I need to:
  1. Setup a server backend for uploading images
  2. Periodically downloading them into the Kindle
  3. Display the images occasionally, deleting them after they've been displayed.

For (1), I can simply use OpenShift and CakePHP to setup a backend for uploading and storing images. I've familiarized myself with that setup thanks to a previous competition, so no worries here.

(2), I can simply write a script for downloading the images from my backend and add it to crontab. I can even turn on wireless using cron, no issues here.

(3), I had no idea to detect which image is being displayed on screen. Thankfully, I found this page https://github.com/yifanlu/OpenBlanket/blob/master/screensaver.c, line 363 of the code shows that the last screensaver displayed is stored at "/var/local/blanket/screensaver/last_ss". I can use this while waking up the Kindle, to delete the images shown before.

Stay tuned to the next part!

Kindle digital photo frame part 1

Weeks ago I found out that my girlfriend would look at our pictures to relax herself when she is bored/stressed. I had the idea of putting a digital photo frame at her desk at work, that would cycle through our photos on a regular basis. She would be so delighted.

So I went to search for some digital photo frames on Qoo10.sg. Lots of choices, but there's a major problem. They're all using LED/LCD screens. This means: either they have an annoyingly short battery life (this item - 4 to 5 hours only?! Seriously?) or AC adaptor cables. I want a frame that is portable, not entangled by wires, and have a battery life counted in weeks, not hours.

So, the Kindle suits my purposes well. It does not consume power when displaying pictures, only when changing them. Suppose I change the images only once per hour... how much power can I save? The Kindle will last weeks!

I was lucky enough to find a seller on Hardwarezone selling his old Kindle keyboard WiFi at $70. He posted his offer just 3 hours before I saw it. And, coincidentally, he was visiting my office on the next day. I don't even need to arrange a pickup point! ^^

The next part: hacking the kindle. I followed a series of guides:

  1. http://anoved.net/2012/02/custom-kindle-screensaver-images-with-kite/ to enable custom screensavers.
  2. http://speely.wordpress.com/tag/3-4/ to enable ssh, and used this to figure out the root password
More to come!

Kindle lipc property list

=~=~=~=~=~=~=~=~=~=~=~= PuTTY log 2013.08.30 15:54:14 =~=~=~=~=~=~=~=~=~=~=~=
login as: root


Welcome to Kindle!

root@192.168.43.115's password: 
#################################################
#  N O T I C E  *  N O T I C E  *  N O T I C E  # 
#################################################
Rootfs is mounted read-only. Invoke mntroot rw to
switch back to a writable rootfs.
#################################################
[root@kindle root]# k   lipc-probe -a -v
com.lab126.pmond
 r  Str summary [name[pid] - state - mem_limit - mem_curr
----------------------------------------
tmd[13086] - started - 9000 - 2728
mcsd[13088] - started - 5000 - 1180
syslog[964] - started - 5000 - 808
netwatchd[0] - stopped - 5000 - 0
phd[13090] - started - 5000 - 2800
cvm[14228] - started - 103424 - 68272
wifid[13272] - started - 5000 - 3468
browserd[14055] - started - 90000 - 8408
webreader[14013] - started - 80000 - 15668
audioserver[15975] - started - 50000 - 4144
ttsd[13790] - started - 150000 - 6388
powerd[13143] - started - 5000 - 1456
volumd[13095] - started - 5000 - 1348
wand[0] - stopped - 5000 - 0
cmd[13267] - started - 5000 - 1516
]
 w Str start
 w Str restart
 w Str stop
 rw Str logMask [0xffff0000]
 w Str kill
 w Str heartbeat_start
 w Str mem_limit
 rw Str logLevel [Current log level=info
(Possible levels: all, perf, debug[9-0], info, warn, error, crit, none)]
 w Str heartbeat_stop
com.lab126.audio
 rw  Str URI []
 w Str PlayerStop
 rw Int Control [0]
 rw Int GstrLatency [10000]
 r Int ManagerInitialize [1]
 rw Str logLevel [Current log level=none
(Possible levels: all, perf, debug[9-0], info, warn, error, crit, none)]
 rw Int CmdIVolume [14]
 r Int CmdISpeakerMax [15]
 w Str SendEvent
 w Int ThreadUp
 rw Str logMask [0x00000000]
 w Str PlayerDataReady
 w Str PlayerRelease
 r Int ManagerCleanup [1]
 w Str PlayerResume
WARNING: Failed to get value of Seek <0x213>
 rw Int Seek
 w Str PlayerCreate
 w Str PlayerPrefetch
 r Int CmdINSinks [2]
 rw Int Chapter [0]
WARNING: Failed to get value of ManagerSuspend <0xf>
 r Int ManagerSuspend
 w Str PlayerLoop
 w Str PlayerSeek
 w Str PlayerPause
 r Int CmdICurrentMax [15]
 w Str PlayerSuspend
 w Int ResetVolume
 rw Int GstrBufferTime [200000]
 w Str PlayerStart
 r Int ManagerShutdown [-4]
 w Int Kill
 rw Int Volume [70]
 r Int ManagerResume [1]
com.lab126.ttsd
 rw  Has enqueueSnippet [*NOT SHOWN*]
 w Int unpause2
 w Int cancelSnippetPlayback
 rw Has playSnippetNow [*NOT SHOWN*]
 w Int pause2
com.lab126.tts
 rw  Str TtsSVoice [Tom]
 w Int CtrlBookmark
 rw Has playFile [*NOT SHOWN*]
 rw Int TextToProcess [10]
 w Int stop
 rw Str logMask [0xffff0000]
 w Int pause
 rw Str logLevel [Current log level=info
(Possible levels: all, perf, debug[9-0], info, warn, error, crit, none)]
 w Int unpause
 rw Str TtsSModel [full_155mrf22]
 rw Int TtsISpeed [100]
com.lab126.powerd
 w  Int addSuspendLevels
 r Str status [Powerd state: Active
Remaining time in this state: 308.400766
defer_suspend:0
suspend_grace:0
prevent_screen_saver:0
drive_mode:off
Battery Level: 97%
Last batt event at: 97%
Charging: No
batt_full=1
Battery logging: On
]
 w Int wakeUp
 rw Int preventScreenSaver [0]
 rw Str logMask [0xffff0000]
 w Int suspendGrace
 w Int deferSuspend
 rw Str logLevel [Current log level=info
(Possible levels: all, perf, debug[9-0], info, warn, error, crit, none)]
 w Int touchScreenSaverTimeout
 r Str state [active]
 w Int abortSuspend
 r Int isCharging [0]
 r Int battLevel [97]
 w Int rtcWakeup
com.lab126.system
 w  Str date
 r Str version [System Software Version: 040-S4_luigi-172597 Sat Sep 1 14:29:41 PDT 2012]
 r Str boardid []
 r Str waveformversion [V220_C024_60_WJ7501_D (M24, S/N 1301, 85Hz)]
 r Str usid [B008A0A004357F9E]
 r Str orientation []
 w Str sendEvent
com.lab126.wifid
 rw  Has createProfile [*NOT SHOWN*]
 r Str signalStrength [5/5
]
 rw Has cmNWProperties [*NOT SHOWN*]
 rw Has netConfig [*NOT SHOWN*]
 r Str manufacturerCode [53GRADB9SMRC2WEX5DF4]
 rw Has profileData [*NOT SHOWN*]
 r Str feelingLuckyProfile [CBTL]
 w Str cmDisconnect
 rw Has currentEssid [*NOT SHOWN*]
 r Str 711 [********* 1- Connection *********
1.1 MAC: 28:EF:01:A8:69:3B
1.2 Wireless: On(1)
1.3 AP: A (02:08:22:d2:18:87)
1.3.1   Signal strength: 5/5
1.3.2   Captive: no
1.3.3   Security: WPA2-PSK
1.3.4   Channel: 1
1.6 Country: CN

********* 2- Wireless Configuration *********
2.1   A 0 [WPA2-PSK][CCMP] (6) 

********* 3- Interface Configuration *********
3.1 IP Address: 192.168.43.115
3.2 Netmask   : 255.255.255.0
3.3 Broadcast : 192.168.43.255
3.4 Gateway   : 192.168.43.1
3.5 Config    : DHCP
3.6 DNS       : 192.168.43.1, 
3.7 Sponsored    : no

********* 4- Last DHCP Session *********
Sending discover...
Offer from server xxx.xxx.43.1 received
Sending select for xxx.xxx.43.115...
Lease of xxx.xxx.43.115 obtained, lease time 43200

5 Device Time: Fri Aug 30 07:54:38 2013

]
 r Int profileCount [2]
 w Str cmConnMode
 r Str cmState [CONNECTED]
 w Str scan
 rw Str logMask [0xffff0000]
 rw Has createNetConfig [*NOT SHOWN*]
 w Str cmCheckConnection
 r Str cmProfile [A]
 r Int cmIntfInUse [1]
 rw Int enable [1]
 r Str macAddress [28:EF:01:A8:69:3B]
 rw Str logLevel [Current log level=info
(Possible levels: all, perf, debug[9-0], info, warn, error, crit, none)]
 w Str deleteProfile
 r Str scanState [idle]
 w Str cmConnect
 rw Has scanList [*NOT SHOWN*]
 rw Has cmIntfInfo [*NOT SHOWN*]
 r Str macSecret [53GRADB9SMRC2WEX5DF4]
com.lab126.framework
 rw  Has transfer_status [*NOT SHOWN*]
 w Int insertKeystroke
 r Str xfsn [ELVvgP9mY0kDCu9gkeNQNYZqmnIHyNbJ9uP9xltY1/eJ3IvONcTj3bGDy7UE1w4Wb2yV3F0K9J4IFWBv9D8tGJXYHZgXqF5gXJG8eYsgooKWWH5iaa3vADv04BEdO29ecIIPVfUVejk=]
 rw Str logMask [crit | error | warn | info | event | perf | test | journal | debug0 | debug1 | debug2 | debug3 | debug4 | debug5 | debug6 | debug7 | debug8 | debug9 (0xffffff0)]
 w Int logContent
 rw Str logLevel [debug9]
 r Int wirelessSwitch [1]
 w Int read
 r Int isRegistered [0]
 r Int wanSwitch [1]
 w Int dismissDialog
com.lab126.transfer
 rw  Has dump_queues [*NOT SHOWN*]
 rw Has modify [*NOT SHOWN*]
 rw Has get_info [*NOT SHOWN*]
 rw Has request_upload [*NOT SHOWN*]
 rw Has dequeue [*NOT SHOWN*]
 rw Str logMask [0xffff0000]
 rw Str logLevel [Current log level=info
(Possible levels: all, perf, debug[9-0], info, warn, error, crit, none)]
 rw Has send_status [*NOT SHOWN*]
 rw Has request_download [*NOT SHOWN*]
 rw Has obliterate [*NOT SHOWN*]
com.lab126.phd
 w  Str newSPHSchedule
 rw Str logMask [0xffff0000]
 rw Str logLevel [Current log level=info
(Possible levels: all, perf, debug[9-0], info, warn, error, crit, none)]
com.lab126.volumd
 r  Int mmcIsAvailable [0]
 r Int userstoreFreeSpace [3127952]
 r Int driveModeState [0]
 r Int mmcFreeSpace [-1]
 rw Int useUsbForSerial [0]
 w Int userstoreReadyToUnMount
 rw Str logMask [0xffff0000]
 r Int mmcTotalSpace [-1]
 rw Int useUsbForNetwork [0]
 r Int userstoreTotalSpace [3201936]
 rw Str logLevel [Current log level=info
(Possible levels: all, perf, debug[9-0], info, warn, error, crit, none)]
 r Int userstoreIsAvailable [1]
com.lab126.webreaderListener
com.lab126.cmd
 r  Str activeInterface [wifi]
 w Str ensureConnection
 rw Str logMask [0xffff0000]
 rw Str logLevel [Current log level=info
(Possible levels: all, perf, debug[9-0], info, warn, error, crit, none)]
 rw Has availableInterfaces [*NOT SHOWN*]
 rw Has interfaceProperties [*NOT SHOWN*]
com.lab126.cvm
 rw  Str logMask [0xffff0000]
 rw Str logLevel [Current log level=info
(Possible levels: all, perf, debug[9-0], info, warn, error, crit, none)]
com.lab126.mcsd
 w  Int performScan
 w Int selectAutomaticMode
 r Int getManagementState [0]
 w Int enableManagementState
 w Int requestCurrentState
 w Int abortScan
 w Str selectNetwork
[root@kindle root]# exit