Wishful Coding

Didn't you ever wish your
computer understood you?

Kindle 4 as a paper terminal

After reading about the Kindleberry Pi, I really wanted to experiment with e-paper as a computer monitor.

Sadly, I own a Kindle 4, which has no keyboard and consequently a lot less hacks. Still, I’m typing this post from my Kindle, so I will document how I got here.

Initially, I could not find a jailbreak for the Kindle 4, but at some point, I found this wiki page that documents everything you need to get started.

I used the universal method, which involves downloading data.tar.gz, entering and leaving diagnostic mode and rebooting a few times.

The jailbreak only installs a developer key, that allows you to install software. The piece of software we need is usbnetwork, which allows you to SSH over USB.

Download usbnetwork from here and follow the instructions.

Before you enable usbnetwork by renaming the auto file, go into /usbnet/etc/config and set USE_VOLUMD="true"

If all went well, the next time you connect your Kindle, it will present a network instead of mass storage.

You should now be able to ssh root@192.168.2.2. Linux users should run sudo ifconfig usb0 192.168.2.1 netmask 255.255.255.0 first, to bring up the network.

The terminal emulator used in the Kindleberry hack does not work on the Kindle 4. It expects a keyboard, I’ve been told.

However, vdp wrote a Java kindlet terminal emulator called KindleTERM that seems to run on Kindle 4.

KindleTERM was writen 2 years ago, and while it pretty much worked out of the box, it did not make an SSH connection to my PC.

I finally got something working using the remote keyboard feature, sshing to localhost and using dbclient to ssh into my Mac and start a tmux session.

Hawhill made a new version that just telnets to localhost, but he removed the remote keyboard. That’s useless.

Then hippy dave came around and added back the remote keyboard. Yay!

Then I came around and added a config file to specify which host/port/username/password/command to use.

Long story short, follow his instructions, but upload my version of KindleTerm to your Kindle. You should be using SCP or SFTP, since you can no longer use mass storage.

By default, my version still telnets to localhost, but it tries to read from /developer/KindleTermPV/work/kindleterm.properties. Mine looks like this:

host=192.168.2.1
login=pepijn
password=secret
cmd=tmux -S /tmp/kindle

After rebooting, you should see KindleTermPV on your home screen. When you start it, it should telnet to your PC.

If you see a blank screen, press back+keyboard or relaunch the app a few times.

If you are on Mac, you can run telnetd with sudo launchctl load -w /System/Library/LaunchDaemons/telnet.plist

You might want to export PS1=">" to save some precious screen space.

You should also export TERM=ansi if you experience any formatting problems. The VT320 implementation is a bit buggy.

kindleterm screenshot

RCX Plotter with Glidewheel

I modified the UBS plotter model to work with the NXT using 2 glidewheels and a normal converter between NXT and classic LEGO connectors.

The original model has two spoke wheels with touch sensors on them, to count the rotations. I replaced these with glidewheels.

Since the UBS is modular, it should be fairly simple to power other models with the same glidewheel-enhanced modules.

I also slightly modified the compressor gear train and paper feed to be more reliable with my old LEGO. Other than that, the model is completely original.

For a nice introduction to the original model, I refer you to Henrik Bækdahl.

Mindsensors Glidewheel

I just got two Glidewheels from Mindsensors for testing. The Glidewheel allows you to integrate other motors into your NXT models, providing precise control over even the oldest 9V motors.

The Glidewheel is designed for Power Functions motors, but works just as well for my RCX motors.

The hard part of using them with RCX LEGO is that they don’t fit directly or close to the RCX motors. You’ll have to mount them elsewhere and stick an axle through them. This is further complicated by the stud-based RCX LEGO.

The first thing I made with them is this small car that uses a drive and steer motor. Even with the rotation sensor that you could buy for the RCX, it was incredibly hard to steer a robot like this.

With the Glidewheel it is incredibly easy. Well, almost. My first attempt looked much like my past attempts with the RCX.

The RCX motors are a lot faster than the NXT motors, so what happens is that the PID controller in the NXT starts overreacting.

To stop this, I used an algorithm called gradual descent(or twiddle, as prof Thrun calls it), which basically modifies P, I or D a little, and sees if it gets better or worse.

float pid[];
float delta[];
int err;
int newerr;
mutex running;

task record() {
	newerr = 0;
	int tacho;
	Acquire(running);
	for(int i=0; i<200; i++) {
		tacho = MotorRotationCount(OUT_A);
		newerr += abs(tacho - 365);
		Wait(10);
	}
	Release(running);
}

inline void run() {
	ResetRotationCount(OUT_A);
	start record;
	RotateMotorPID(OUT_A, 100, 365, pid[0], pid[1], pid[2]);
	Acquire(running);
	printf("%d", newerr)
	Release(running);
}


task main() {
	ArrayInit(pid, 32, 3);
	ArrayInit(delta, 5, 3);
	err = INT_MAX;
	run();
	while(ArraySum(delta, NA, NA) > 0.1) {
		NumOut(0, LCD_LINE2, pid[0]);
		NumOut(0, LCD_LINE3, pid[1]);
		NumOut(0, LCD_LINE4, pid[2]);
		for(int i=0; i<3; i++) {
			pid[i] += delta[i];
			run();
			if(newerr < err) {
				err = newerr;
				delta[i] *= 1.1;
			} else {
				pid[i] -= 2*delta[i];
				run();
				if(newerr < err) {
					err = newerr;
					delta[i] *= 1.1;
				} else {
					pid[i] += delta[i];
					delta *= 0.9;
				}
			}
		}
	}
	PlayTone(432,1000);
	Wait(10000);
}

</code>

The result of this code for my little car was

PID
Default963232
Free404032
Load403240

Inserting these values in my code, I get this smooth motion.

Code:

mutex inControl;

task avoid() {
	while(true) {
		while(SensorUS(IN_4)>30);
		Acquire(inControl);
		PosRegSetAngle(OUT_C, 90);
		OnRevRegPID(OUT_A, 50, OUT_REGMODE_SPEED, 40, 32, 40);
		Wait(2000);
		OnFwdRegPID(OUT_A, 50, OUT_REGMODE_SPEED, 40, 32, 40);
		Release(inControl);
	}
}

task turn() {
        while(true) {
                Wait(1000);
		Acquire(inControl);
                PosRegSetAngle(OUT_C, 90);
		Release(inControl);
                Wait(1000);
		Acquire(inControl);
                PosRegSetAngle(OUT_C, -90);
		Release(inControl);
                Wait(1000);
		Acquire(inControl);
                PosRegSetAngle(OUT_C, 0);
		Release(inControl);
        }       

}

task main() {
	SetSensorLowspeed(IN_4);
        OnFwdRegPID(OUT_A, 50, OUT_REGMODE_SPEED, 40, 32, 40);
        PosRegEnable(OUT_C, 40, 40, 32);
	Precedes(turn, avoid);
}

</code>

Published on