blog.heeltoe.com

Brad's comments on the world of technology...

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.

Tags :