« January 2006 | Main | November 2006 »

October 27, 2006

Scheme + Bogl + SDL = simple, quick embedded UI

I have been struggling with a way to make a simple user userface for an embedded system with a 640x480 LCD but no keyboard or mouse. The machine only has 64mb of ram and wants to run largely out of ram. This eliminates things like X windows, TK, etc... And, I wanted to be able to hack up experiments quickly.

I originally tried Java, but it ate the machine and all it's ram. Then I recoded in C++. This worked well but required recompiling and was hard to debug. It was fun, however, but not fast to develope and not flexible.

Then I found a nice small scheme interpreter called "siod". It supports very simple extension via shared libraries. I also foubd "bogl" -- ben's own graphics library. This is a simple set of graphics primative (lines, blt, simple fonts) which runs directly on a frame buffer, i.e. "/dev/fb" in the unix world.

I wrote an extension library for bogl and was quickly writing scheme (lisp) code to draw on the screen. In order to make progress quickly I hacked bogl to also output using SDL. This way I could compile everything on my desktop X86 machine and SDL provides a 640x480 X window which looks like my target display.

Once the library was stable I had X86 and target (ARM) versions in place an I had to do wasmove updated scheme text files to the target after each debugging session.

Scheme is very nice and easy to learn. It's basically a simplified version of lisp. If you are scared of lisp Scheme is a great place to start. You write scheme code in a text file and siod interprets the text files. It's just like writing shell scripts only much more expressive.

for example, here are some simple scheme functions to blank, unblank and draw a little text on the screen

;;
(define (fb-blank)
  (ioctl *fb-fd* FBIOBLANK 3))

;;
(define (fb-unblank)
  (ioctl *fb-fd* FBIOBLANK 0))

;;
(define (fb-comment current last)
  (let* ((word1 (number->string current 10))
	 (word2 (number->string last 10))
	 (text (string-append word1 "/" word2))
	 (tx 0)
	 (ty 0))
    (bogl-drawtext tx ty text 0 12)))

In lisp/scheme the convension is to decorate global variables with "*"'s. (i.e. *fb-fd* is a global variable). And scheme is nice because is knows about strings.

Basically you can crank out a simple UI pretty fast, testing it all on an X86 box. I've added support for gif, jpeg, http get and unix low level file i/o (read/write/poll). So now I have crank out a sophisticated UI very quickly and easily make changes.

I like scheme because you end up writing verbs which describe the problem you want to solve, like

      ;;
      (define (idle-weather)
	(if (not (weather-still-valid-p))
	    (begin
	      (update-weather)
	      (show-weather)
	      (show-rss)))
	(weather-idle))

This function gets called when the program is idle and in "weather display mode". The weather page also shows various rss feeds, as well as getting the XML weather from weather.com and displaying it in a nice day by day format with an icon and text.

And it's all interpreted. Pretty fast too. I can fix bugs right on the target using vim on a text file.

October 21, 2006

Never Say Die... (reviving a Linksys WRT54GS)

A while back I did a project using the Linksys wireless routers. Well,
more truthfully an associate did all the work, I just managed the project.

Anyway, my associate, being rusty with hardware at that point managed to "brick"
one of the units. It was toasted and would not respond to the simple
recovery proceedure. (aside: I do this all the time. generally I like
to produce smoke, sparks and flames. apparently all he got was a dead
unit. He'll need to practice shorting wires of higher amperage if he wants
to catch up)

Most recently I found myself needing two more Linksys boxes for some
distributed computing I'm doing. I found one and it runs OpenWRT which
I now love. The other was the "bricked" unit. (to brick = to render a device
no more useful than a brick)

I figured I'm a tough guy so I soldered the serial connector and JTAG
port. I connected up a simple jtag box and ran the "debrick" code
someone (hairdairymaid) wrote for windows. It didn't work. I fooled
around with it for hours. hours. I resoldered the pins. I used the
o-scope. I beeped out the lines. I compiled/hacked/fixed the linux
"wince jtag tools" to work with this particular broadcom cpu chip
(which, I might add, I did the original board port for). Still nothing.
I almost managed to get the linux jtag tools to erase the flash and
rewrite it but I had to modify the code and it was ugly. pain.
frustration. why am I spending 1/2 a day on a $50 box?

So, I put the board in the trash. But I didn't take out the trash. A
day goes buy. I break down and use a bogus WRT54GP box I had which
won't run linux. Blech. But it worked.

Today I notice (on accident) a posting where a guy says "hey, I tried
that hairydairymaid debrick code but it didn't work on my Intel J3
strataflash until I added a 2 second delay here and a 5 second delay
here..." His symptoms matched mine (erase appeared not to work). So, I
made the same change to the code and VIOLA! it's debricked! it works!
it runs OpenWRT! happy happy! joy joy!

Seems dumb all for a $50 box.

But, the interesting thing is that I found the answer on the web, but it
was not the first one. Many of the low level 'instructructions'
surrouding the WRT are marginal/flawed. But they often work well
enough. The high level ones seem to be largely good.

Wasn't that fun? :-)