Yet Another Writing Productivity Tool

As I've been hinting about on twitter, I'm working on a fiction writing project. I'm discovering I like writing, but still am finding my feet in terms of getting my productivity up. When you're in that situation, there is always the geek approach: "I know, I'll spend several hours making a tool that will make me more productive!" That was exactly what I did last Sunday. I happened to have an RGB led strip lying around, and I thought that it could make a good accent light for those times when I'm working in the middle of the night. As I mentioned in my biphasic sleep post, if you have an inspiration to get some work done either right before bed, or during a bout of midnight wakefulness, the last thing you want to do is to be exposed to bright light that's well represented at the blue end of the spectrum, at least not if you want good quality sleep afterward.
My solution in that post was to cover the screen with a make-shift red filter. That worked fine if I was satisfied sitting in the dark with the computer screen as my only light source. That can be a trying work environment, however. No matter how low you turn down the brightness, you're still stairing into the only light source in the room. It's very tiring on the eyes, and it makes it nearly impossible to see anything you may have put on your desk to reference, or if (for some reason) you want to see the keys on your keyboard. That was where the idea of making a night-work-friendly light came from. As sometimes happens with such projects, it grew a bit out of control. I could have found a potentiometer and wired the LED strip up with that, but I had a spare arduino uno laying around, so it was a relatively quick job to wire up a circuit so it could provide the dimming. But it didn't stop there. An arduino can communicate with the computer, and my first thought was to just use the arduino IDE to send signals to control the light, but why should I have to do that manually? In fact, couldn't the light, in addition to giving me a bit of ambient illumination, provide some important feedback? Couldn't it well me how well I'm doing and by extension, when it's time to give up and go to bed? This is what I ended up with. It's still pretty raw, but I'm frankly amazed with how well it works.

On the arduino is the following bit of code.

/* Drive the RGB LED strip with an arduino
*
*/

void setup()
{
Serial.begin(9600);
}

void loop()
{
int REDPIN = 10;
int GREENPIN = 11;
int BLUEPIN = 9;
int i, input, color;
int r = 0;
int g = 0;
int b = 0;
while (1) {
if (Serial.available() >= 1) {
if ( (i<48) || (i>57)) { // i is a letter from the previous loop
color = i;
} else {
color = Serial.read();
}
input = 0;
i = Serial.read();
while ( (i>=48) && (i<=57)) {
input = input*10 + i-48;
i = Serial.read();
}
if (input > 255) {
input = 255;
}
if (color == 82) { //ASCII 'R'
r = input;
} else if (color == 71) { //ASCII 'G'
g = input;
} else if (color == 66) { //ASCII 'B'
b = input;
}
Serial.write(color); //Write-backs are just for debugging purposes
Serial.write(r);
Serial.write(g);
Serial.write(b);
analogWrite(REDPIN, r);
analogWrite(GREENPIN, g);
analogWrite(BLUEPIN, b);
//clear the buffer
// while (Serial.available()) {
// i = Serial.read();
// }

}
delay(20);
}
}

All the code does is read the stream of bytes coming in from the serial bus and parse them into Red, Green and Blue values. It then sets the corresponding PWM values on pins 9,10, and 11. As you'll see in the picture, the circuit is still sitting on a breadboard. Knowing me, I'll keep it there until I need the breadboard.

In fact, I've already put the electronics in a case and mounted it to the underside of a shelf. The LED strip runs under the front edge under the front edge of the shelf with a wooden lip in front of it so no light can go direcly into your eyes. The result is a nice even glow on the desk and back wall (dinosaur wallpaper is essential) that greatly reduces the perceived glare of the screen.

On the computer side of things I have this thrown together and very poorly written Perl script, which you can find below. Every minute it totals the number of words found in all the files within Scrivener's document store. When you're writing, that number should be going up, of course. The script tallies the number of new words and uses it to maintain a running average of your words per hour. The color and intensity of the light is set according to that. It uses the nifty arduino-serial command line app to handle communications with the arduino.

You may scoff, but the effect has been impressive and immediate. You may ask why should a slightly different color of light make any difference to your creativity or your productivity. I can answer that in one word: feedback. We humans may be the peak of reason within the animal kingdom, but we’re actually still pretty darned bad it it. We can fool ourselves into all sorts of fictions.
“Just one cookie won’t make that big a difference.” “I only smoke when I’m stressed.” “I’ll just work on it extra hard tomorrow.” “Just one more level. “ The first day I hooked up the light, I found it was giving direct and very helpful feedback. I was writting for a bit, and had driving the light through the red, and was getting some quite sharp shades of green, when a text message came in from Mrs.'Struction with some questions about shopping, child drop off, etc. I switched over to chat and started a conversation. Aa we chatted, the light lost its greenish hue and started falling back to red. “Chatting is fine,” it seemed to be saying, “but there’s writting to be done.”

You might think that getting that message would be a source of stress, but in fact it was quite liberating. I didn’t have to constantly have some ticker running in the back of my head. “What if I’m chatting too long. Will I be able to catch up? How will I even know?” Instead I could watch the light go red and start to dim knowing that as long as I didn’t let things go too far, I would catch up, and it was all being tracked for me. At the same time, I knew that the longer I waited, the longer it was going to take me to win back the green, and maybe power through to the blue.

It was even more useful when it was time to do some research. “What were the different sizes of ale barrels called?” Wikipedia is a dangerous place, and it’s so easy to get lost in an orgie of learning, but now if my excursion to the browser takes too long, there’s a subtle reminder that maybe I’ve spent enough time and it’s time to get back to scrivener.

There are a lot of possible improvements I can see. Maybe an OS X native app. Maybe something that can just monitor keyboard input directly so it's program agnostic. How about a hot key to add info to the logs, or something that can log what I'm listenning too for later corolation against productivity. I see a lot of possibilties, but I should get back to writting. I've spent more that enough time work-crastingating.

#!/usr/bin/perl -w
#use strict;
#my $filename = shift @ARGV;
use POSIX;

my $filenum = 1;
my @files; #list of files in the docs directory
my $wordtotal; #sum of the words from each file.
my $target = 500; #target words per hour
#(Actually half your target if you goal is to get to green)

my $wordcount = 200; #Sets the starting state of the light.
my $max_brightness = 200; #Adjusts how bright the lights can get. Strictly a matter of taste
my $oldwordtotal = 0; #Used to keep track of the delta in words per minute
my $scrivener_file = "/Dropbox/Writing/Mabelliad/The_Mabelliad-2.scriv";
#Name of the scrivener file
my $logfile = "/Dropbox/Writing/Mabelliad/Writing_Log.dat";
#file to track writing productivity
my $home = $ENV{HOME};
my @wc;
#Open the log file
open LOG, ">>" . $home . $logfile or die "Couldn't open log file.\n";
my $ofh = select LOG;
$| = 1;
select $ofh;

while (1) {
#get the list of the files in the directory
opendir(D, $home . $scrivener_file . "/Files/Docs") || die "Can't opedir $!\n";
my @files = readdir(D);
closedir(D);
$wordtotal = 0;
foreach (@files) {
if (/\.rtf$/) { #only process the .rtf files
my $fullpath = $home . $scrivener_file . "/Files/Docs/" . $_;
(@wc) = (`wc -w "$fullpath"` =~ m/(\d+)/);
$wordtotal += $wc[0];
# print "Processing file $fullpath \t wc = @wc\n";
}
}
print "wordtotal = $wordtotal\t oldwordtotal = $oldwordtotal\n";
$wordcount *= 55/60; #'Decay' the old words
if ($oldwordtotal) {
$wordcount += 5*($wordtotal - $oldwordtotal); #add the new words you just wrote
}
print "Wordcount = $wordcount\n";
if ($wordtotal > $oldwordtotal) {
print LOG POSIX::strftime("%m/%d/%Y %H:%M:%S", localtime) . "," . ($wordtotal - $oldwordtotal) . "\n";
print POSIX::strftime("%m/%d/%Y %H:%M:%S", localtime) . "," . ($wordtotal - $oldwordtotal) . "\n";
}
$oldwordtotal = $wordtotal;

#now determine the color and send it to the arduino
if ($wordcount < $target) {
my $brightness = int($max_brightness*$wordcount / $target);
system("./arduino-serial -b 9600 -p /dev/tty.usbmodemfa12731 -s \"R" . $brightness . "G0B0\"");
} elsif ($wordcount < 2* $target) {
my $brightness = int($max_brightness*($wordcount-$target)/$target);
system("./arduino-serial -b 9600 -p /dev/tty.usbmodemfa12731 -s \"R" . ($max_brightness - $brightness) . "G" . $brightness . "B0\"");
} elsif ($wordcount < 3* $target) {
my $brightness = int($max_brightness*($wordcount-2*$target)/$target);
system("./arduino-serial -b 9600 -p /dev/tty.usbmodemfa12731 -s \"R0G" . ($max_brightness - $brightness) . "B" . $brightness . "\"");
}
sleep(60);
}