RGB led remote pixel FrontPage

RGB led remote pixel FrontPage

Pixel project


  • individually-addressable red/green/blue leds with 8-bit PWM brightness control
  • each pixel is under $5
  • pixels can be spaced apart and require only 2 wires chaining them together (or maybe 3)
  • possible compatibility with 1wire, allowing other devices to share the bus


  • Sending data with lots of power, possibly continuous power.
  • Reflecting LEDs to the right shape, and with complete mixing of the colors

Hardware sources:

Related projects:

Still looking for:

  • good comparison of LED brightnesses to common home and theater fixtures
  • a 1wire driver that sends lots of power (several amps)


Program to summarize groups of lines of text. Lines can be in paragraphs like this:

Or tagged with single-char keys like this:
a red
b green
b blue
a UV
Outputs look like this:
Table 1 **: 2 people (one, two)
Table 2 **: 2 people (three, four)
Table a **: 2 people (red, UV)
Table b **: 2 people (green, blue)
python source

Klein bottle topology demonstration

Klein bottle topology demonstration
Click images for larger versions

Drew Perttula

This project demonstrates a series of manipulations on Klein bottles and Mobius strips through the use of computer animation.

The product animation may be useful as a teaching tool for students of topology or computer graphics (of course).

I did my work on 3 machines running Linux and one Macintosh (for the video output only). While the programs, which are in C and Tcl, can be compiled and run on any platform that supports Geomview, the results of the project can be viewed with a VCR or web browser.

My final program has a Tk interface to a C++ program which sends geometry in real time to Geomview. By using the interface on the screen or sending it inter-application Tcl commands, one can obtain all the geometries visible in the movie. I wrote a script for StageTools, a set of Geomview modules, that sends instructions to my shape generation program and sends motion and output commands to Geomview.

The output is a video about 1:20s long (and these web pages). The left half of the page shows the images from the video spaced about two seconds apart (plus one extra, which shows the split bottle more clearly). Click any image to see a full-size version.

Even though there are a few different shapes and movements shown in the video, my program has only one loop that moves all points one step in some direction, determined by 5 routines.

For the effect where the half-bottle bends into a simpler Mobius strip configuration, I used 3 spring functions. For every iteration, each control point (11x4 for the half-bottle, 25x4 for the ring sequences) is given a null vector called "force."

Then, in the operation I called "iso-grid," each point's force vector is pointed a fraction of the amount the point would have to move to equalize the lengths of the grid segments on all sides. I controlled the goal grid rectangle sizes in u and v in my interface.

Depending on the magnitude of the "v-straighten" parameter, each point's force vector is pointed a certain amount in the direction that would flatten the local cross-section in the v (small) axis. This parameter causes the half circle of the Klein bottle to become a straight line for the circular Mobius strip.

Finally, the "u-straighten" parameter performs a similar function as v-straighten, but in the perpendicular axis along the surface. However, the naive smoothing action of the v-straighten routine didn't look very good, nor did it cause the shape to approach a circle. So I rewrote u-straighten to find the angle between the neighboring points along the u-axis and push *those two points* closer into a line through the current point.

The last two routines causes points to travel towards some stage of the figure-8 bottle configuration. A parameter adjusts whether I get an open bottle (equivalent to a full-twist Mobius strip), or a complete Klein bottle.

To get the different actions in the video, my movie script (Tcl) adjusts the various parameters to turn on and off different sections of the point-adjustment loop. Here is a breakdown of the video, listing what routines are run in my program for each scene:

    Load standard Klein bottle. My program actually never outputs more than half of the Klein bottle. I use Geomview to mirror the output to create the other half.

    Load the figure-8 ("ring") immersion and attract all the points to the sealed-up position for a while. (This is how I show the ring model pre-built)

    Load the standard immersion. (I use Geomview to show one half separating from the other)

    For a sequence of frames, run the iso-grid, v-straighten, and u-straighten operations (described below) to make the half bottle bend into a simple Mobius strip.

    Load the doubly-wrapped Mobius strip from my parameterization. For a sequence of frames, interpolate points on the strip to points in the unfolded ring. Only operate on points that are in the "strike zone," a factor that runs from 0 (no points) to 1 (affect all points) over the sequence. The strike zone check makes the effect appear to start at one point on the strip and travel around the strip.

    For a sequence of frames, interpolate from the unfolded ring to the folded, sealed ring. The start and end points for each vertex are parameterized, and a similar strike zone effect is used.

Using Geomview saved me from having to write any display functions at all!

Here are the line counts for the programs that I had to write. This does not count some other test code that I used. Don't be fooled by the small line counts! Tcl is a very compact language, and the C code merely has to evaluate my parameterizations and sum the various forces on each point. I used the same vector math library as Jordan Smith does on his projects.

Shape generator module for Geomview:
430 lines of C++
100 lines of Tcl

Movie script for StageTools:
150 lines of Tcl

Additional Geomview commands (lighting, etc):
50 lines (output from Geomview, after I set values with the UI)

After discussion with Prof. Sequin, I would like to try some other transformations on Klein bottles. Hopefully, I will have a new movie soon that shows how to split a Klein bottle into *one* Mobius strip and bend that strip into the figure-8 immersion.

See my CS294 project

Powered by Zope

Pyrex demo for baypiggies

Pyrex demo for baypiggies

Drew's BayPIGgies Pyrex presentation

At the 2004/11/11 BayPiggies meeting, I did some Pyrex demos.

My first demo was how to build a minimal Pyrex module that has a Python statement and a C call.

Then I talked about using Pyrex to optimize a simple Python program.

Initial Python program


from glob import glob
import itertools

class Bigrams:
    """original python version"""
    def __init__(self):
        self.pairs = {}

    def add(self,word):
        for i in range(len(word)-1):
            pair = word[i]+word[i+1]
            self.pairs[pair] = self.pairs.get(pair,0) + 1

    def freqs(self):
        """get list of (pair,freq)"""
        return self.pairs.items()

files = "/usr/include/*.h"

b = Bigrams()

for line in itertools.chain(*[file(x) for x in glob(files)]):
    for word in line.split():

freq_pair = [(v,k) for k,v in b.freqs()]
for fp in freq_pair[-8:]:
    print "%8d %s" % fp 
On my box, I get this list of the most popular bigrams:
% cat /proc/cpuinfo | egrep "vendor|MHz"
vendor_id       : AuthenticAMD
cpu MHz         : 1400.081
vendor_id       : AuthenticAMD
cpu MHz         : 1400.081

% time ./bigrams
   12210 te
   12486 fi
   13053 nt
   13267 ef
   13780 er
   13940 ne
   17057 de
   30403 in
./bigrams  4.19s user 0.07s system 92% cpu 4.604 total

Move to Pyrex

I can't wait 4 seconds for this stuff! So, I moved the Bigrams class into Pyrex and built an extension. That version runs in about 4.7sec. Pyrex translates (most) Python to C code that uses the Python API. Run times are usually unaffected. The big speedups come when you let Pyrex write some of your code in faster C by avoiding PyObjects; calling other C libs; etc.

Try a Pyrex language extension

Then, I changed "for i in range(len(word)-1):" to "for i from 0 <= i < len(word)-1:" to demo one of Pyrex's bonus features. That loop syntax isn't related to translating normal Python to C; nor is it related to calling C functions. The for/from Pyrex loop uses a plain C integer as the loop index, avoiding a few Python API calls per iteration. This gets me a bit of a speedup. With the Pyrex int loop, the program takes 3.7sec.

Rewrite the algorithm

Now I am ready to tinker with the algorithm. I rewrite the Bigrams class in Pyrex, using a fixed-size C buffer to store the frequency counts. This removes the Python dict access, and I am also now looping over characters in a C string.
cdef class Bigrams4:
    """pack results into a fixed array like a C programmer would"""
    cdef int pairs[65536]
    def __init__(self):
        for x in range(65536):

    def add(self,word):
        cdef char *cword
        cdef unsigned short pairidx
        cword = word

        for i from 0 <= i < len(word)-1:
            pairidx = (cword[i]<<8) + cword[i+1]
            self.pairs[pairidx] = self.pairs[pairidx] + 1

    def freqs(self):
        """get list of (pair,freq)"""
        letter_pairs = []
        for x in range(65536):
            if self.pairs[x]>0:
                letter_pairs.append(("%s%s" % (chr(x >> 8), chr(x & 0xff)),
        return letter_pairs
Note 'cdef class', which means this is a new C type. Pyrex writes this C struct for the new type:
struct __pyx_obj_6bigram_Bigrams4 {
  int (pairs[65536]);
My 'self.pairs' code, formerly an attribute lookup, is now a C struct member access:

Pyrex source:
self.pairs[pairidx] = self.pairs[pairidx] + 1
C emitted from the Pyrex compiler:
/* "/home/drewp/projects/digrams/bigram.pyx":51 */
    (((struct __pyx_obj_6bigram_Bigrams4 *)__pyx_v_self)->pairs[__pyx_v_pairidx]) = ((((struct __pyx_obj_6bigram_Bigrams4 *)__pyx_v_self)->pairs[__pyx_v_pairidx]) + 1);

This runs in 1.2 sec, which I felt was good enough for my demo. Note that my rewritten algorithm could also be ported to plain Python. That version takes 3.7 sec-- the C datatypes made the big difference here.

Swig version

I also did a swig version which runs in .68 sec. It's probably faster because it doesn't use a class at all, because the final packing is faster, or because it doesn't contain the error checks that Pyrex includes. I haven't investigated the exact reason.


Get bigrams.tgz and run "python setup.py build_ext --inplace" to build the Pyrex extension (you'll need Pyrex installed). Run "make" to build the swig extension (you'll need swig).

Then, try "time bigrams" after setting line 29 to the class version you want to run. Edit line 25 to adjust which files that are scanned. The file "bigrams5" contains the fixed-array version written in Python, which I didn't want to have cluttering the original bigrams program.

These source files also contain a demo of calling uuid_generate from libuuid. Remove all the uuid code if you're on a platform that doesn't have that lib, I guess.

Auto gate latch

Auto gate latch

The images on this page are broken because the photo module for zope drifts around over the years. It's really hard to rescue old pics when they break, because they're locked up in ZODB. Use archive.org if you want to see them. -drewp 2007-02-27

This is an electric gate latch. When you open the gate all the way, the gate presses the switch that turns on an electromagnet that holds the gate open. You can close the gate by pulling on it gently. The system is powered during the day only, so the gate cannot be left open at night. We can also close the gate from inside the house.

Here is the gate being held open:

6902 This plate is mounted loosely so it can snap flat onto the electromagnet.
4030 This is a switch from a laser printer I took apart.
4627 This is an electromagnet from a laser printer I took apart. We don't know what it's rated for, but we give it about 12V. The wires are covered with something to make them weatherproof.

Powered by Zope

skim text file reader

skim text file reader

skim text file reader

by Drew Perttula and David McClosky

some highlighting on the #python irc channel
some highlighting on the #python irc channel
using skim to locate a particular discussion in one of my own IM chat logs
using skim to locate a particular discussion in one of my own IM chat logs
skim helps me scan a source file for the part I'm interested in (a real-life skim usage!)
skim helps me scan a source file for the part I'm interested in (a real-life skim usage!)
another real-life use of skim to browse a source file
another real-life use of skim to browse a source file

skim is an alternative text file viewer with a graphical interface and advanced searching and browsing features. It requires Python 2.2 or later with Tkinter, and it can make use of Wordnet with pywn.

The important features of skim are:

  • the birdseye view, where you can see a representation of your whole file (or a zoomed piece) at the same time that you browse the text
  • the advanced search features where you can, for example, highlight several words at once (like Google's cache) and even highlight related words (synonyms, misspellings)
  • the combination of the above two features; which means that you can see all the matches in your whole document at once. Now you can browse right to the clusters of matches. Compare this to your other programs, where you have to hit 'next' a bunch of times and wade through all the out-of-context matches. In skim, you can see instantly where the search matches occur together, which is probably the part you wanted to read.


ez_install users can simply run easy_install skim.

You can download an archive as a gzipped tar file or zip file.

Skim uses the Python distutils. Run "python setup.py install" to install it.

CVS access

Anonymous CVS is available. Run this command:

cvs -z3 -d :pserver:anonymous@bigasterisk.com:/srcmirror co skim
That will make a new directory "skim" off the current directory and checkout the latest version of skim into it. In that directory, you can run "cvs update -d" anytime to freshen your skim version.

ViewCVS is available

Skim roundup issue tracker

Powered by Zope

GIS mapping with Python

GIS mapping with Python

GIS mapping with Python

Driving times from San Mateo County Forensic Lab to the rest of the bay area. Numbers are in minutes. Bright splotches are multiples of 10 minutes. Speed limits for each road class are from the TMRS project and might be experimental. There's a suspicious road classification on Willow Road at the east end of the Dumbarton Bridge that might be throwing off all the Fremont times. Code is in CVS at the tag 'sanmateo1'.

I presented the San Mateo image at dorkbot. Here is a photo of my presentation.

I made plots of driving times from my house, with every multiple of 5 minutes highlighted in yellow, and every minute alternating between light and dark. Red increases gradually.

Before I connected some gaps in the bridges, I was getting this wrong image. Here's the corrected one.

Here's a first version of running shortest-paths on the map. Streets are not yet weighted differently (and the coord system is still in degrees).

I also plotted the streets in the San Francisco Bay Area using these colors for the first letter of the street (except freeways):

1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

Here is a similar google maps view. Mine looks stretched in width because Google's map uses some "reasonable" projection, whereas mine uses a "very simple" one.

This is based on my previous work with TMRS. Most of the new work was converting the drawing from pygame to a new python binding to the excellent agg library. The street data is from TIGER.

Code is in CVS at the tag 'color_first_letter'. viewcvs is here.

Related work


Travel-time Maps and their Uses

Powered by Zope




by Drew Perttula

Sometimes we grep for something in a bunch of files, and then we want to make edits to the lines that were returned from the grep. It's a hassle in most editors to open all the files and find all the lines. grepedit puts the match lines in a temp file for easy editing all at once.

The cmdline arguments to grepedit are the same as those to grep. Do not use arguments that alter the output line format; -C is ok.

grepedit also reads the following arguments:

  --sort-text sort lines by the text part of the line (this probably
    doesn't make sense with -C)
Install with:
pip install grepedit

PyPI page

Download version 2.0 (2022-04-05) (py3 support- thanks Benjamin!)

Download version 1.0 (2005-10-09)


Run grepedit on itself and setup.py:
% grepedit the *
Edit the result lines in vi (according to $EDITOR):
grepedit:3:"""sometimes we grep for something in a bunch of files, and then we
grepedit:4:want to make edits to the lines that were returned from the grep. It's
grepedit:5:a hassle to edit all the files; this program puts the match lines in a
grepedit:8:EDITOR is used on the temporary file.
grepedit:10:The cmdline arguments to grepedit are the same as those to grep. Do
grepedit:11:not use arguments that alter the output line format; -C is ok.
grepedit:13:grepedit also reads the the following arguments:
grepedit:15:  --sort-text sort lines by the text part of the line (this probably
grepedit:100:                print "%s:%s has changed since the grep command ran- not modifying this line" % key
setup.py:6:      description="edit lines the result of a grep and modify the original files",
"/tmp/grepedit_IOLHsa" 10 lines, 792 characters
grepedit reports what changes it made:
% grepedit the *
no changes made in file setup.py
grepedit:13 substituting new line


emacs occur-edit-mode, new in emacs24 (a short howto)

hide-lines.el similar thing, but for working on one buffer in emacs

all.el --- Edit all lines matching a given regexp (in emacs)

Water splashing

This is a simulation of water splashing.

A C program applies the wave equations to a grid and creates the three wave effects: a sinusoidal source on the left, a circling point, and a raised rectangle that disappears after part of the frames.

The program displays a preview of dots using OpenGL, and can output heightfields, which I had POV-Ray render, here.

I want an effect close to this one for the liquid in the wine bottle in another project.

Serial foot pedals

Serial foot pedals
Here are some pedals for controlling the mpd (music player daemon) for easier transcribing of lectures. Play the audio with any mpd client, then run this to use pedals for pause and rewind-2-secs.

Uses pyserial and py-libmpdclient.


import serial,time,mpdclient

class RetryingMpdController:
    def __init__(self,**kw):
        self.mpd_kw = kw

    def reconnect(self):
        self.mpd = mpdclient.MpdController(**self.mpd_kw)
        print "connecting to mpd"

    def __getattr__(self,attr):
        def retrying_method(*args):
            func = getattr(self.mpd,attr)
            except mpdclient.MpdError:
        return retrying_method

class Pedals:
    connect RTS to DSR and CTS like so (pin numbers are for 9-pin
    serial port):

    pin 7 RTS -->-----------+
                left   /    |
    pin 6 DSR <-------/ o---+ 
                right  /    |
    pin 8 CTS <-------/ o---+
    def __init__(self,*actions):
        self.ser = serial.Serial(port="/dev/ttyS0",rtscts=1)
        self.prev = False,False
        self.actions = actions

    def poll(self):
        pedals = self.ser.getDSR(),self.ser.getCTS()
        for which in range(2):
            if pedals[which] and not self.prev[which]:
        self.prev = pedals

class Playback:
    def __init__(self):
        self.mpd = RetryingMpdController(host='localhost',port=6600)

    def rewind(self,secs=2):
        pos,length,frac = self.mpd.getSongPosition()

    def pause(self):

play = Playback()

ped = Pedals(play.rewind, play.pause)

print "monitoring pedals (press ctrl-c to exit)..."
while 1: