Sunday 25th May 2008
18.33 - 18.39
Halfway along Grafton Street, near the clock outside Barratts: map
The Grand Central Freeze:
]]>Here's how to drive up to eight LED clusters or other high current device off a single, cheap 18-pin IC with no extra components.
It turns out someone's thought of this already and produced a handy IC that contains a bunch of these transistor circuits. The doodad in question is a €1.15 ULN2803A darlington pair array [datasheet]. Page 4 of the datasheet, figure 1a has an example circuit.
How these work: The left side of these chips are the input (pins 1-8) and the right side are the ouputs (pins 10-17). Pin 9 is the ground, pin 18 the common +V. Connect your +5V to your load, e.g. LED cluster + appropriate resistor (e.g. 20ohm for ~8 LEDs), and connect the cathode (-V) end of that circuit to an output pin. The darlington circuit acts as a sink. Here's the circuit,
| From Darlington pairs driving LED clusters |
Each darlington circuit can sink 500mA (peak 600mA) and they can be wired in parallel so e.g. connecting two can drive a 1A load.
The example here is using the included Fade demo sketch with an extra write to another pin to flip one cluster one and off just to demonstrate the circuit doing two things. Note how the LED clusters have a common +V and they source separately into different outputs via the small resistors.
]]>The technique is to light row by row, and rely on our persistence of vision effect. Playing around with the delay it seemed like 3ms for a total of 15ms (five rows) was about the point where flicker just started being noticeable.
Geeky detail after the fold...
]]> The software pulls the row pins high one by one while the column pins are set low for every LED to light, sinking the current from the one high row pin. There's a 3ms delay, then the next row pin is brought high and the last one low.The data for the little animation is a 2D byte array that looks like,
byte PATTERN[][5] = {
/* ... */
{B0111,
B1110,
B1100,
B1000,
B0000,},
/* ... */
};
('B' is a handy prefix for a binary number.)
You can see a diagonal band of 1's there in the early part of the wipe. Each byte's translated into pin writes by looking at the lowest bit in the byte and then shifting right and looking at the next. LOW is on.
void SetColumn(byte pattern) {
for (int i = COL_COUNT-1; i >= 0; i--, pattern >>= 1) {
digitalWrite(COL_PINS[i], pattern & 1 ? LOW : HIGH);
}
}
A "frame" of the display is completed by lighting each row in turn,
void ShowPattern(byte pattern[]) {
int last_row = ROW_COUNT-1;
for (int row = 0; row < ROW_COUNT; last_row = row++) {
digitalWrite(ROW_PINS[row], HIGH);
digitalWrite(ROW_PINS[last_row], LOW);
SetColumn(pattern[row]);
delay(MULTIPLEX_DELAY_MS);
}
}
The main loop draws each frame over and over until a certain amount of time has elapsed and then it goes onto the next one. millis() is a built-in that returns the number of milliseconds since the board was switched on. (This code would fail after about nine hours when the counter resets.)
void loop()
{
for (int pos = 0; pos < PATTERN_SIZE; pos++) {
long start = millis();
while (millis() - start < PATTERN_DELAY_MS) {
ShowPattern(PATTERN[pos]);
}
}
}
I actually had the circuit the other way around, cycling through column-by-column and the rows programmed in turn with a line of lights. This happened out to make writing patterns awkward so I flipped the orientation which made SetColumn pretty straightforward.
The matrix could've had more lights on it but the breadboard limited width a bit. I was quite proud of the tight wiring under the LEDS.
| From Multiplexed LED Matrix |
This was one of the projects where the bits that seemed like they'd be hard Just Worked and then silly things wasted time, like C mistakes with sizeof.
int ROW_PINS[] = {13, 12, 11, 10, 9};
int COL_PINS[] = {5, 4, 3, 2};
#define ROW_COUNT (sizeof(ROW_PINS)/sizeof(ROW_PINS[0]))
#define COL_COUNT (sizeof(COL_PINS)/sizeof(COL_PINS[0]))
Parameterizing these kinds of things made moving from a 3x4 board to 4x5, flipping the rows & columns, and using different pins trivial. An unfortunate cargo-cult effect in Arduino and Wiring code I've seen is folks not using #define and sizeof.
Some cute enhancements might be to have an array of structs, the struct containing additionally a delay time for each frame. We could also have, rather than 0/1, a wider range of values to indicate brightness. With that, (e.g. Perl/Python scripted) code could generate the animation sequences rapidly flipping frames to give an impression of anti-alias. Unfortunately I have other things to do... (yeah, I made the animations by hand in vi and pasted into the Arduino editor :-))
And finally, a Wiring-based pair of color LED matrixes are used as toilet signs... that change throughout the night [video]. More on this funny project.
]]>The tutorials are great and introduce the important concepts with example circuits and code. In fact, the set of example code is all accessible directly from the Arduino app's main menu, which is a nice touch. (I.e. you don't have to dig around trying to open a file buried on your hard drive.)
So anyway here it is. Eleven LEDs connected to digital pins 0-10, and a 22k linear pot (variable resistor) feeding analog pin 5. The LEDs "chase" up and down with a speed taken from the pot position.
It was fairly painless, at least after an initial scare where programs wouldn't upload,
avrdude: stk500_recv(): programmer is not responding avrdude: stk500_recv(): programmer is not responding
I read all sorts of scary solutions online where folks were reprogramming their bootloaders and other horrors. One Arduino hacker mentioned running the board off an external power supply (rather than rely on USB) during programming as the current draw is apparently higher. This was enough for me to try pull the LED's ground pin out so the lights turned off: Bingo! No problem programming. It became a bit annoying pulling the ground lead out every program: next trip to Maplin I get a switch...
The other issue I had was remembering how C does casting. My function to scale the analog input into a minimum and maximum delay was being simultaneously confused by my code not casting up to a float early enough, and a persistently dodgy connection with the pot.
(The fifth pin/LED didn't seem to light very strongly. Tried different LEDs and lower resistance. Didn't fix it or figure out why it was dim; I'll try a different board soon.)
So what led me to Arduino? The brand new Science Gallery at Trinity College in Dublin is running a programme to introduce high school kids who wouldn't otherwise have had the educational opportunity to do so to learn about electronics. I'm helping out there on Wednesday afternoons, which so far has mostly involved teaching them how to solder, and debugging electronics problems. Where possible I have them learn from my mistakes: my room in my parents' house, for example, was so badly burnt from soldering iron accidents and riddled with little lumps of solder they had to replace it after I left... (I suppose one useful legacy I left was installing about two dozen plug sockets :-))
So far the kids have created some very cool LED pictures each with a fierce amount of soldering and now are ready to hook them up to Arduinos. Hence the need to stay one step ahead of the younger generation ;-)
#define LED_COUNT 11
#define DELAY_MIN 10
#define DELAY_MAX 400
#define DELAY_STEP 50
#define POT_PIN 5
#define INTERNAL_LED_PIN 13
void setup()
{
for (int i = 0; i < LED_COUNT; i++) {
pinMode(i, OUTPUT);
digitalWrite(i, LOW);
}
pinMode(POT_PIN, INPUT);
pinMode(INTERNAL_LED_PIN, OUTPUT);
}
int ScaleDelay(int in)
/* Calculates a delay between DELAY_{MIN,MAX} from an analogRead.
*/
{
float d = (DELAY_MAX - DELAY_MIN) * (float)in;
d = d / 1024 + DELAY_MIN;
return (int)d;
}
void LightLED(int led_pin, int last)
/* Reads a delay, lights an LED on led_pin; turns off the last one a short time later.
*/
{
int d = ScaleDelay(analogRead(POT_PIN));
digitalWrite(led_pin, HIGH);
delay(d / 10);
digitalWrite(last, LOW);
delay(d);
}
#define TAIL_LENGTH 3
int tail[TAIL_LENGTH];
int tail_pos = 0;
int LastLED(int current)
/* Maintains a list of lit LEDs. Returns the very last one.
*/
{
tail[tail_pos] = current;
tail_pos = (tail_pos + 1) % TAIL_LENGTH;
return tail[tail_pos];
}
void loop()
{
int last;
for (int i = 0; i < LED_COUNT; ++i) {
last = LastLED(i);
LightLED(i, last);
}
digitalWrite(INTERNAL_LED_PIN, HIGH);
for (int i = LED_COUNT-1; i >= 0; --i) {
last = LastLED(i);
LightLED(i, last);
}
digitalWrite(INTERNAL_LED_PIN, LOW);
}
]]>
So being the gadgety skinflint that I am, what follows is how to disassemble it and replace the batteries with more powerful ones...
]]>






Enjoy :-)
]]>As Fortune would have it, also out kiting was Ben who very kindly let me have a crack at Samurai 5m2 monster. Here's a few pics, including it dragging me around,





(Yes, Jez, I should've looked into this when you first mentioned it a decade or so ago :-))
]]>According to my fancy Tanita scales I'm currently at 8.3% body fat. Which is pretty damn low; not quite "walking anatomy lesson" but certainly on the way. For comparison, male athletes vary between 6% and 12%; more here.
So, how did I lose 10kg? After hours of reading and assimilating scholarly nutritional texts, womens' magazines, physiologists' deepest secrets, and all the while studiously avoiding the fridge's whispered promises, I put together the following plan. My Magic Formula For Losing Weight is...
So that's basically what I did for a year.
In some future blog I'll write more detail about what I did and ate. In the meantime I will say engaging in aerobic and anaerobic exercise was key, so if you're casting about for more on weight loss there's something I wrote a long time ago, how to get, and stay, motivated.
As a final aside, to row Lightweights at Cambridge University means being not more than 72.5kg. If I were to weigh that my body fat percentage, assuming I miraculously didn't lose any muscle mass, would be 5%(!) Another reason I'm happy not to be in university...
]]>The bad news is that every morning and sometimes if I've been on my feet for hours, e.g. walking around the house cleaning, my feet ache. They overall feel weaker too. I am constantly reminded by the background pain of my mistake falling off that house. I imagine this will be with me to varying degrees the rest of my life. My main hope at this point is that it doesn't get worse; so far it's been getting better.
On a positive note, here's some examples of what I'm doing with my feet these days:
Walking. I can walk for hours now, especially with MBT shoes. Actually these shoes are a real boon for me and I'm ordering more so I'm not always wearing red... I am still limited by walking. Recently visiting Prague there were a couple of times where hours upon hours of sight-seeing took its toll and I was suffering. An excellent leg & foot massage helped dramatically though.
Hiking. Over the Easter weekend I volunteered to drive a bus around Ireland on a "4 peaks" type of challenge. The loons who were doing it cranked out four "mountains" in less than two-and-a-half days so were sleeping in the van. On the third one in Killarney I hiked with them for about three hours on the gentle early part of the slopes. That was enough although I wasn't hammered and my feet/joints didn't hurt too much later and the next day.
Running. I have had a recent pleasant surprise with running. Some background first: I never much liked running as a sport so not being able to really only impacted my being late for the occasional bus/plane/train. In the last two years I have literally run for a total of about two minutes, most of that one time in an airport. Just recently however, at work there's a timed "gym challenge" that involves a bunch of body-weight exercises (push-ups, sit-ups, etc) plus, for the Gold challenge, a 2km run. Being the competitive type this tipped me over the edge to try running again, and 2km isn't that far so I figured I'd see how it went. The first time I did it I was going at 12km/h for 10minutes. I've done it once more since at 14.2km/h (8:30). I have no intention of training any more than I have to to win this, but I know have to get at least 18km/h which is reasonably quick.
Each time I've run like this my feet ache somewhat the next day and I cannot walk around totally comfortably.
Overall if regular longer-than-sprint running is a necessary or desired (you freak! :-)) part of your life and you break your heels badly I'd say you're screwed. Find something else to do. On the other hand, it seems like despite a serious heel fracture I can run for short distances at a useful speed, and go about my life including some competitively athletic pursuits like rowing for the most part unaffected. So, considering I was so close to being paralyzed, not a bad result at all!
]]>I'm working on an application that needs to present a graph of stock prices over a given time for a list of securities. The data is missing in some cases, for example weekends, and the graph should not be discontinuous. If however the start date is Saturday there is no previous day's data in the dataset so it has to use the day (or two, or three, ...) ahead's. From the point where data appears we baxtrapolate the data to the start point.
Related: baxtrapolation (n.)
]]>(Of course, I had another bottle of JD there. Improvizing around a lack of eggs, milk, flour, ... is one thing, but to go dry on Old No. 7...)
A week after the madness that was London Santacon, Santacon arrives in Dublin! Go here for more info: Santa's on Bebo.
(I've always wanted a megaphone, and jaysus, is this thing loud. That's a "tequila belt" I'm wearing, complete with shot glasses--possibly one of the coolest and most destructive Christmas presents I've ever got [thanks Eva!]. Accompanied by Jack Daniels, and a pound of chocolates, Santa is ready!)
]]>It's frustrating that the BBC go to all the trouble of making their data available and then developers horde their little snippets of code. I just don't understand it. Big kudos to MighTyV who do open source their code. And deservedly won the BBC Backstage competition.
So, without further ado, some code to implement tag clouds off mailing list subject lines:
]]>[% INCLUDE "/home/www/paulm/paulm.com/inchoate/2006/11/subjects.pl.html" %]
(I'm happy to comment this for anyone wishing to play with it.)
]]>/archive/002981.html.
The downside to this is that after the site rebuild I had effectively created a whole 'nother blog, one all crosslinked in the new style, and one in the old style. And because the rest of my site has random links into the old style from before the change it continues to be crawled. To this day I still get search referrals pointing to the old site.

This isn't so bad except for two things. Comments go to the new site, not the new one. So referrals where someone's looking for rent scam help would, on the old site, miss out on all the useful (and entertaining in some cases) comments. The other bad thing is that the old site isn't running ads. OK, you might think that's a good thing :-)
So tonight I decided to fix this. Here's how I did it.
The strategy I took was to produce a set of redirects from the old site to the new site. The best way to do a redirect is at the webserver level as it teaches crawlers and browsers where the new pages are. There are in fact two types of redirects, temporary (aka "302") and permanent (aka "301"). Apart from being correct, the latter is also the preferred-by-search-engines method--tales abound of sites losing PageRank for using 302 temporaries.
Using the Apache webserver, I needed to produce a whole pile of RedirectPermanent directives to be included in paulm.com's Apache configuration.
Here's the answer,
export PERL5LIB=/home/mt/cgi-bin/lib
perl -MMT -MMT::Blog -le '
$mt = MT->new(Config=>"/home/mt/cgi-bin/mt-config.cgi");
$b = MT::Blog->load(13);
@e = MT::Entry->load({blog_id=>13});
for $e (@e) {
printf "RedirectPermanent /inchoate/archives/%06d.html %s\n", $e->id, $e->archive_url;
}' > /etc/apache2/paulm.com-redirect.conf
OK, what's going on here. First we need to create an instance of the top-level MT object. (Here we see an awesome example of how to abuse Perl's object model--subsequent object instantiations, e.g. MT::Blog make no reference at all to that MT instance, it all just magically works. Ah, pixie dust.)
Next up we load up my blog object. I know its id is 13 by looking in the mt.cgi URL: blog_id=13. Alternatively you can look in the mt_blog database table. I don't actually need this line of code as it turns out I can pull the blog entries out directly. I decided to leave this line in there for some extra documentation and reference.
So the load() method works with several MT types, the ones sublcassed from MT::Object, and has a fairly rich interface. This example shows loading all the entries of blog_id 13. Reassuring when I print scalar(@e) I got 336 which is the same number as reported on my blog dashboard page.
After divining that MT::Entry::archive_url was the right method for printing an entry's URL the ball's in the net. The final piece was manually constructing the old-style URL using printf "%06d" which says "print this decimal zero-padded to six digits".
Now in my /etc/apache2/sites-available/paulm.com I simply added,
Include /etc/apache2/paulm.com-redirect.conf
apache2ctl config && apache2ctl graceful
(&& is an improvement on ; in that it'll only execute the next command if the first succeeded.)
Finally I of course needed to test it worked, paulm.com/inchoate/archives/002950.html. Yep!
And in the logs,
perl -lane 'print if $F[8] == 301 and $F[6] =~ /\d{6}/' /var/log/apache2/paulm.com-access.log
x.x.x.x - - [29/Oct/2006:23:46:20 +0000] "GET /inchoate/archives/002919.html HTTP/1.1" 301 279 "-" "Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7"
paulm.com-redirect.conf. One option might be to re-run the script above and put the date as a constraint in the MT::Entry->load() call. This would require me to figure out how to do that and, being lazy, I just can't be bothered (if you know, feel free to leave a comment). So I'm going to throw my sys-admin skills at the job instead.
But... I can feel a two-in-one trickshot coming on. But to set up this trickshot, another observation:
I still have "two" blogs. I need to remove those old pages. It would be easy to just go ahead and wipe them out with rm inchoate/archives/00*.html but I'm curious to see if anything's left after I remove only we know about.
Can you see the trickshot? I'm going to remove the unnecessary redirects and at the same time remove the old files, all in a one-liner. So, I re-used the results of the last bit of code (redirect.conf) pulling out the old-style URLs (/inchoate/archives/002918.html) and turning them into their place on the filesystem. Now I can test to see if they exist and if so, remove them and keep the redirect line, and if not, remove the redirect line.
perl -ani~ -e '-f ($f = "/home/www/paulm/paulm.com$F[1]") and unlink($f), print' /etc/apache2/paulm.com-redirect.conf
This scary bit of code does an in-place modification (-i) of the redirect file saving a copy with the ~ extension in case I make a mistake. I make use of the surprisingly little-used -a switch which splits the line into the @F array ('a' for 'awk' which by default has these in $1, $2, etc). So $F[1] is the old-style URL. If the file exists, the -f test, then unlink (remove) it and print the line. The effect of printing the line retains it from file.
So as an aside we see two perl one-liner idioms,
perl -ni~ e 'print if $some_condition' to remove lines except some and perl -pi~ -e 'do_something_with_each_line(); # typically an s///'
Back to the job: I want to observe what happens, so I take before and after shots with ls $paulm/inchoate/archives/00*.html; wc -l /etc/apache2/paulm.com-redirect.conf. I.e. take the before shot, run the above perl line, and re-run the ls; wc -l "after" shot. For the latter I get, reassuringly,
ls: /home/www/paulm/paulm.com/inchoate/archives/00*.html: No such file or directory
47 /etc/apache2/paulm.com-redirect.conf
Since this has changed my apache configs another webserver kick's needed.
It's all well changing the entries but what about the old-style archives/2004_08.html? Laziness prevailing here again I checked the last week's logs for references,
ls $paulm/inchoate/archives/200*_*.html # this revealed four from 2004, hence: grep archives/2004_0 $logs
Nothing. Delete 'em: rm $paulm/inchoate/archive/200*_*.html
The same wasn't true for categories unfortunately,
grep cat_ $logs
cat_*.html pages.
The old RSS feeds could go, rm $paulm/inchoate/archives/[0-9]*.xml $paulm/inchoate/[0-9]*.xml
My site's built from little XML fragments and full HTML pages, so I'm going to look in all of them and switch references to the old style,
find $paulm \( -name '*.html' -o -name '*.xml' \) | xargs grep -l 'inchoate/archives/00' | tee /tmp/old-style-pages
There were tantalisingly few there (about four)--I was sorely tempted to hand-edit them. But no, see if I can whip out another one-liner in less time than it takes to look each one up:
</tmp/old-style-pages xargs perl -pi~ -MFile::Slurp=read_file -le '
BEGIN {
%h = map { (split)[1 => 2] } read_file("/etc/apache2/paulm.com-redirect.conf");
s~http://paulm.com~~ for values %h;
$re=join "|", map "(?:http://paulm.com)?$_", keys %h
};
s/($re)/$h{$1}/ge'
The neat trick here is that I'm doing an in-place file change while using the BEGIN clause to slurp in the redirect file and make a hash mapping the old style pages ("word" 1 of the line) to the new URL ("word" 2). Skipping the next line for a second I create a big regex to match any reference the old-style URLs.
Now those mystery references to http://paulm.com: This is quite subtle. The references in the script to my base URL http://paulm.com combine to remove that from the URLs in the pages. By not replacing with the full URL, the script also works on matches on filesystem references, e.g. /home/www/paulm/paulm.com/inchoate/archives/002918.html. Without it you'd end up with /home/www/paulm/paulm.comhttp://paulm.com/inchoate... (I didn't get this the first time round; only after I ran the script on one page and noticed it on a file that did an INCLUDE of a blog entry).
(By the way, if you're thinking, my god, who writes scripts like that off the top of his head? The answer is, if you're prepared to experiment, play, and believe it's possible: "you")
The test for success here is no 404s (missing pages), just 301s (permanent redirects). So over the next few days I'll be keeping an eye on my server logs using techniques shown above, and tail -f the error log in a window.
Changing one's URL scheme is not something to be undertaken lightly! However, if you do choose to, having a blogging engine that enables relatively straightforward construction of scripts and one-liners to manipulate its data renders the job a matter of a half-hour or so of reading docs and experimentation.
And of course behold the power of perl and unix to perform sophisticated data transformations and get the job done!
]]>But that's besides the point. The point is, when people make websites about themselves they often miss their own position in their regional or even global context. Pick a random place that's not a whole country (because Google Maps does that quite well; make it harder!) and try to get a sense of where it is, e.g. Bracknell Forest. Often you'll end up with a bunch of maps of the immediate area with absolutely no sense of where it is in its country or even the world.
It's incredibly frustrating. So it got me thinking why is this? And is it any insight into who we are at this moment in time, or as a species?
Ken Wilbur talks about levels of awareness from self to tribe to region to global consciousness, how actions and behaviors impact on others within that catchment. Landmark are another group who of all the people I've interacted with over the years seem to have this spiralling-out awareness of how we can affect our environment. Evolution of society and culture seems to proceed out through these spirals encompassing increasingly wider scope at the same time subsuming the inner spirals (this is the genius of spiral dynamics, that perception of our universe consists of viewing concepts as being whole and complete by themselves yet containing and being contained by others in that universe. For example, an atom contains quarks while being contained by molecules, but an atom is something on its own without being "subjugated" or necessarily categorised in a hierarchy).
So my takeaway from my lack of success in finding how regions are located within the world suggests first off people aren't too concerned about the world beyond their immediate borders, unless those on the other side of the border are particularly troublesome. (If you are inclined, check out the Slovenian coastline and ponder what the Croatians and Italians are up to. Yeah, you're still in Europe. Have fun.)
But also the tools to express any awareness are not particularly well understood either. Gmaps covers the world but in varying degrees. I am a great believer in tools enabling folks to consider and deal with concepts much like words in language enable chunking of ideas. But.. what else is there? What's the gap between local and global awareness? Why do some folks care about this and others not?
Where's the gap?
]]>