The personal website of Scott W Harden
⚠️ Warning: This article is obsolete.
Articles typically receive this designation when the technology they describe is no longer relevant, code provided is later deemed to be of poor quality, or the topics discussed are better presented in future articles. Articles like this are retained for the sake of preservation, but their content should be critically assessed.

A Prime Idea

I'm completely drained of energy. I visited my wife's family in Tennessee last week. I left Thursday and came back Tuesday (yesterday). I drove a total of 2,180 miles. The drive to Humboldt, TN (the destination) and back is only 1,658 miles. That means that I drove over 520 miles over the 3 days while at my destination. That's about 174 miles a day. At 50 MPH average speed that's about 4 hours in the car. So, 13 hour drive (each way) to get there, then 4 hours in the car every day I was there. That's a lot of car time!

While speaking with my brother-in-law (who just got a BS in computer science with a minor in mathematics) I learned that a faculty member at the university challenged him to write a computer program which could find the N'th prime number (up to 10^15) for a graduate school project. I was fascinated by the idea project and the various techniques, and workarounds related to it. After working on the theory behind the software (which I tested in Python) for a few hours, I had the idea to attempt to perform a similar task at the microcontroller level.

Here's the project I want to begin: I want to build a microcontroller-powered prime number generator which displays results in binary. The binary-encoded output is similar to the binary clocks which are nothing new. My project will calculate prime numbers up to 2^25 (33,554,432) and display the results in binary using long strips of 20 LEDs. There will be 3 rows of LEDs. The middle row (red) will simply count from 0 to 2^25. Every time it gets to a new number, the bottom row (blue) counts from 0 to the square root of the middle row. For every number on the bottom row, the remainder (modulus) of the middle/bottom is calculated. If the remainder is 0, the middle (red) number is divisible by the bottom (blue) therefore it is not prime. If the bottom number gets all the way to the square root of the middle number, the middle number is assumed to be prime and it is copied to the top row (green). The top row displays the most recent number determined to be prime.

Technical details of the project further reveal its dual simplicity/complexity nature. I'll add some buttons/switches for extra features. For example, I want to be able to start the program at a number of my choosing rather than forcing it to start at 0. Also, I want to be able to adjust the speed at which it runs (I don't want the blue row to just flicker forever). The ATTiny2313 (my microcontroller of choice because I have a few extra of them) has 18 IO pins. If I get creative with my multiplexing techniques, I can probably run 81 LEDs from 18 pins (9 rows of 9 LEDs). I've specifically chosen against charlieplexing because I will be lighting many LEDs "simultaneously" and I think the degree of flicker would be far too great to satisfy my sensitive eyes, even though I could do it all with only 10 pins.

I've decided to transistorize the entire project to provide a greater and more constant current to the LEDs. I'll use a set of 9 transistors to set the row that gets power (when the microcontroller powers the base, the row gets power) and another set of 9 transistors to set the LEDs in each row that light up (when the microcontroller powers the base, the LED gets grounded and lights up). To have consistently-bright, non-flickering LEDs which don't dim as more LEDs illuminate, I will add a resistor to every LED. Maybe I can get creative and utilize 10-pin resistor networks (one for each row) immediately after the row-selecting transistor! That will save me so much time. (I just came up with that idea - just now!) Anyway, that's my project idea.

I'd love to make this project look nice. All of my other projects were housed in junky plastic or cardboard boxes (if they were housed at all!) and this is something I want to keep. I start dental school soon, and I've love to have a fancy-looking piece of artsy/geeky/electrical memorabilia so I'll never forget who I am, my roots, and my true interests. Plus, it will give me something groovy to stare at when I come home after a long day cleaning the teeth of manikins and wondering why I went to dental school [sigh].

Update (nextday): I've been toying over some various layouts for the LEDs. I most like the rectangle and hex-rectangle configurations, and am already working on assembly of the "mini" (prototype). Here are some random images of my thinking process.

⚠️ Warning: This article is obsolete.
Articles typically receive this designation when the technology they describe is no longer relevant, code provided is later deemed to be of poor quality, or the topics discussed are better presented in future articles. Articles like this are retained for the sake of preservation, but their content should be critically assessed.

Graphing Computer Usage

I enjoy writing Python scripts to analyze and display linear data. One of my favorite blog entries is Linear Data Smoothing with Python, developed for my homemade electrocardiogram project. I installed a program called TimeTrack.exe on my work computer. It basically logs whenever you open or close a program. The data output looks like this:

"Firefox","Prototype of a Digital Biopsy Device - Mozilla Firefox","05/19/2009  9:45a","05/19/2009  9:45a","766ms","0.0"
"Firefox","Dual-Channel Mobile Surface Electromyograph - Mozilla Firefox","05/19/2009  9:46a","05/19/2009  9:46a","797ms","0.0"
"Windows Explorer","","03/24/2008  9:30a","05/19/2009  9:48a","49d 6h 9m","20.7"
"Windows Explorer","09_04_07_RA_SA_AV","05/19/2009  8:48a","05/19/2009  8:48a","1.0s","0.0"
"Windows Explorer","Image003.jpg - Windows Picture and Fax Viewer","05/18/2009  4:03p","05/18/2009  4:03p","1.2s","0.0"

I have a 13 MB file containing lines like this which I parse, condense, analyze, and display with Python. The script finds the first and last entry time and creates a dictionary where keys are the hours between the 1st and last log lines, parses the log, determines which time block each entry belongs to, and increments the integer (value of the dictionary) for its respective key. Something similar is repeated, but with respect to days rather than hours. The result is:

The code I used to generate this graph is:

# This script analyzes data exported from "TimeTrack" (a free computer usage
# monitoring program for windows) and graphs the data visually.

import time, pylab, datetime, numpy

# This is my computer usage data.  Generate yours however you want.
allHours = ['2008_10_29 0', '2009_03_11 5', '2009_04_09 5', '2008_07_04 10',
'2008_12_18 9', '2009_01_30 12', '2008_09_04 7', '2008_05_17 1',
'2008_05_11 5', '2008_11_03 3', '2008_05_21 3', '2009_02_19 11',
'2008_08_15 13', '2008_04_02 4', '2008_07_16 5', '2008_09_16 8',
'2008_04_10 5', '2009_05_10 1', '2008_12_30 4', '2008_06_07 2',
'2008_11_23 0', '2008_08_03 0', '2008_04_30 4', '2008_07_28 9',
'2008_05_19 0', '2009_03_30 7', '2008_06_19 3', '2009_01_24 3',
'2008_08_23 6', '2008_12_01 0', '2009_02_23 6', '2008_11_27 0',
'2008_05_02 5', '2008_10_20 13', '2008_03_27 5', '2009_04_02 9',
'2009_02_21 0', '2008_09_13 1', '2008_12_13 0', '2009_04_14 11',
'2009_01_31 7', '2008_11_04 10', '2008_07_09 6', '2008_10_24 10',
'2009_02_22 0', '2008_09_25 12', '2008_12_25 0', '2008_05_26 4',
'2009_05_01 10', '2009_04_26 11', '2008_08_10 8', '2008_11_08 6',
'2008_07_21 12', '2009_04_21 3', '2009_05_13 8', '2009_02_02 8',
'2008_10_07 2', '2008_06_10 6', '2008_09_21 0', '2009_03_17 9',
'2008_08_30 7', '2008_11_28 4', '2009_02_14 0', '2009_01_22 6',
'2008_10_11 0', '2008_06_22 8', '2008_12_04 0', '2008_03_28 0',
'2009_04_07 2', '2008_09_10 0', '2008_05_15 5', '2008_08_18 12',
'2008_10_31 5', '2009_03_09 7', '2009_02_25 8', '2008_07_02 4',
'2008_12_16 7', '2008_09_06 2', '2009_01_26 5', '2009_04_19 0',
'2008_07_14 13', '2008_11_01 5', '2009_01_18 0', '2009_05_04 0',
'2008_08_13 10', '2009_02_27 3', '2009_01_16 12', '2008_09_18 8',
'2009_02_03 7', '2008_06_01 0', '2008_12_28 0', '2008_07_26 0',
'2008_11_21 1', '2008_08_01 8', '2008_04_28 3', '2009_05_16 0',
'2008_06_13 5', '2008_10_02 11', '2009_03_28 6', '2008_08_21 7',
'2009_01_13 6', '2008_11_25 4', '2008_06_25 1', '2008_10_22 11',
'2008_03_25 6', '2009_02_07 6', '2008_12_11 4', '2009_01_01 4',
'2008_09_15 2', '2009_02_05 12', '2008_07_07 9', '2009_04_12 0',
'2008_04_11 5', '2008_10_26 4', '2008_05_28 3', '2008_09_27 14',
'2009_05_03 0', '2008_12_23 5', '2009_05_12 10', '2008_11_14 3',
'2008_07_19 0', '2009_04_24 8', '2008_04_07 1', '2008_08_08 11',
'2008_06_04 0', '2009_05_15 12', '2009_03_23 13', '2009_02_01 10',
'2008_09_23 11', '2009_02_08 3', '2008_08_28 4', '2008_11_18 9',
'2008_07_31 7', '2008_10_13 0', '2008_06_16 9', '2009_03_27 6',
'2008_12_02 0', '2008_05_01 7', '2009_04_05 1', '2008_08_16 9',
'2009_03_15 0', '2008_04_16 6', '2008_10_17 4', '2008_06_28 5',
'2009_01_28 10', '2008_04_18 0', '2008_12_14 0', '2008_11_07 6',
'2009_04_17 7', '2008_04_14 7', '2008_07_12 0', '2009_01_15 7',
'2009_05_06 8', '2008_12_26 0', '2008_06_03 7', '2008_09_28 0',
'2008_05_25 4', '2008_08_07 8', '2008_04_26 7', '2008_07_24 1',
'2008_04_20 0', '2008_11_11 4', '2009_04_29 0', '2008_10_04 0',
'2009_05_18 9', '2009_03_18 4', '2008_06_15 8', '2009_02_13 6',
'2008_05_04 5', '2009_03_04 2', '2009_03_06 3', '2008_05_06 0',
'2008_08_27 11', '2008_04_22 0', '2009_03_26 6', '2008_03_31 9',
'2008_06_27 5', '2008_10_08 4', '2008_09_09 4', '2008_12_09 3',
'2008_05_10 0', '2008_05_14 5', '2009_04_10 0', '2009_01_11 0',
'2008_07_05 8', '2009_01_05 7', '2008_10_28 0', '2009_02_18 11',
'2009_03_10 7', '2008_05_30 3', '2008_09_05 7', '2008_12_21 6',
'2009_03_02 6', '2008_08_14 5', '2008_11_12 5', '2008_07_17 8',
'2008_04_05 6', '2009_04_22 11', '2009_05_09 0', '2008_06_06 0',
'2009_01_03 0', '2008_09_17 6', '2009_03_21 3', '2009_02_10 7',
'2008_05_08 4', '2008_08_02 0', '2008_11_16 0', '2008_07_29 12',
'2008_10_15 5', '2008_06_18 5', '2009_03_25 2', '2009_01_10 0',
'2009_04_03 5', '2008_08_22 7', '2009_03_13 11', '2008_10_19 0',
'2008_06_30 8', '2008_09_02 9', '2008_05_23 4', '2008_12_12 7',
'2008_07_10 11', '2008_11_05 8', '2008_04_12 4', '2009_04_15 7',
'2008_12_24 1', '2008_09_30 0', '2008_05_27 2', '2008_08_05 10',
'2008_04_24 6', '2009_04_27 6', '2008_07_22 3', '2008_11_09 1',
'2008_06_09 6', '2008_10_06 14', '2009_03_16 7', '2008_05_22 5',
'2009_01_29 12', '2008_11_29 4', '2008_04_09 7', '2008_08_25 12',
'2009_02_15 0', '2008_03_29 7', '2008_06_21 7', '2008_10_10 9',
'2008_05_12 6', '2009_02_16 10', '2008_09_11 11', '2008_12_07 0',
'2008_07_03 6', '2009_04_08 3', '2009_01_23 7', '2009_01_27 5',
'2008_10_30 0', '2009_03_08 0', '2009_01_21 8', '2008_12_19 0',
'2008_05_16 2', '2009_01_25 1', '2009_02_26 5', '2008_09_07 2',
'2008_04_03 1', '2008_08_12 6', '2008_04_13 10', '2008_11_02 0',
'2008_07_15 0', '2009_04_20 3', '2009_02_24 10', '2009_05_11 8',
'2008_12_31 8', '2008_04_15 7', '2008_09_19 10', '2009_01_19 0',
'2008_11_22 3', '2008_07_27 2', '2009_02_04 7', '2009_03_31 1',
'2008_05_24 3', '2008_10_01 8', '2008_06_12 6', '2009_01_12 11',
'2008_11_26 8', '2009_04_01 10', '2009_02_28 0', '2008_08_20 6',
'2008_10_21 10', '2008_06_24 4', '2008_03_26 4', '2008_12_10 0',
'2008_09_12 0', '2008_05_09 7', '2009_02_17 7', '2008_07_08 6',
'2008_10_25 5', '2009_04_13 9', '2009_05_02 0', '2008_12_22 8',
'2008_09_24 9', '2009_01_20 5', '2008_11_15 6', '2009_04_25 10',
'2008_08_11 9', '2008_04_06 8', '2008_07_20 1', '2009_03_22 3',
'2008_06_11 6', '2008_09_20 3', '2009_05_14 10', '2008_11_19 0',
'2008_08_31 2', '2009_02_09 8', '2008_10_12 0', '2008_04_25 5',
'2008_06_23 4', '2009_01_07 8', '2008_08_19 0', '2008_12_05 2',
'2008_07_01 8', '2008_10_16 6', '2009_04_06 3', '2009_03_14 5',
'2008_09_01 2', '2008_12_17 14', '2008_05_18 7', '2008_04_01 2',
'2009_04_18 0', '2008_04_17 0', '2008_07_13 0', '2008_06_02 10',
'2008_09_29 6', '2008_12_29 0', '2009_05_05 8', '2008_04_19 0',
'2009_04_30 8', '2008_08_06 4', '2008_11_20 0', '2008_07_25 6',
'2009_02_06 6', '2009_03_29 3', '2009_05_17 0', '2009_03_19 7',
'2008_10_03 1', '2008_06_14 3', '2008_05_07 5', '2008_08_26 3',
'2008_11_24 9', '2008_04_21 8', '2008_04_23 4', '2008_10_23 11',
'2008_06_26 4', '2008_03_24 8', '2008_12_08 5', '2008_09_14 2',
'2009_01_02 6', '2008_04_08 0', '2008_10_27 6', '2009_04_11 0',
'2008_07_06 0', '2008_12_20 3', '2009_04_23 6', '2008_09_26 9',
'2008_05_31 0', '2008_07_18 4', '2008_11_13 6', '2008_08_09 2',
'2008_04_04 0', '2009_03_20 5', '2008_09_22 7', '2009_05_08 9',
'2008_06_05 7', '2008_07_30 7', '2008_11_17 10', '2008_05_03 0',
'2008_08_29 3', '2009_02_11 12', '2009_01_08 8', '2008_06_17 0',
'2008_10_14 7', '2009_03_24 11', '2008_08_17 6', '2008_12_03 0',
'2009_01_09 4', '2008_05_29 5', '2008_06_29 9', '2008_10_18 5',
'2009_04_04 0', '2008_12_15 10', '2009_03_12 0', '2009_03_05 7',
'2008_05_20 4', '2008_09_03 7', '2009_03_07 8', '2009_01_14 6',
'2008_05_05 5', '2008_11_06 7', '2008_07_11 6', '2009_04_16 9',
'2009_02_20 0', '2008_12_27 0', '2009_01_17 0', '2009_05_07 7',
'2008_11_10 5', '2008_07_23 11', '2009_04_28 0', '2008_04_27 2',
'2008_08_04 0', '2009_03_01 11', '2008_10_05 0', '2008_06_08 8',
'2009_05_19 5', '2008_04_29 4', '2008_11_30 0', '2009_01_06 8',
'2009_02_12 3', '2008_08_24 2', '2009_03_03 10', '2008_10_09 6',
'2008_06_20 2', '2008_05_13 10', '2008_12_06 0', '2008_03_30 7']

def genTimes():
    ## opens  exported timetrack data (CSV) and re-saves a compressed version.
    print "ANALYZING..."
    f=open('timetrack.txt')
    raw=f.readlines()
    f.close()
    times=["05/15/2009 12:00am"] #start time
    for line in raw[1:]:
        if not line.count('","') == 5: continue
        test = line.strip("n")[1:-1].split('","')[-3].replace("  "," ")+"m"
        test = test.replace(" 0:"," 12:")
        times.append(test) #end time
        test = line.strip("n")[1:-1].split('","')[-4].replace("  "," ")+"m"
        test = test.replace(" 0:"," 12:")
        times.append(test) #start time

    times.sort()
    print "WRITING..."
    f=open('times.txt','w')
    f.write(str(times))
    f.close()

def loadTimes():
    ## loads the times from the compressed file.
    f=open("times.txt")
    times = eval(f.read())
    newtimes=[]
    f.close()
    for i in range(len(times)):
        if "s" in times[i]: print times[i]
        newtimes.append(datetime.datetime(*time.strptime(times[i],
                                        "%m/%d/%Y %I:%M%p")[0:5]))
        #if i>1000: break #for debugging
    newtimes.sort()
    return newtimes

def linearize(times):
    ## does all the big math to calculate hours per day.
    for i in range(len(times)):
        times[i]=times[i]-datetime.timedelta(minutes=times[i].minute,
                                             seconds=times[i].second)
    hr = datetime.timedelta(hours=1)
    pos = times[0]-hr
    counts = {}
    days = {}
    lasthr=pos
    lastday=None
    while pos1:counts[pos]=1 #flatten
        if not daypos in days: days[daypos]=0
        if not lasthr == pos:
            if counts[pos]>0:
                days[daypos]=days[daypos]+1
                lasthr=pos
        pos+=hr
    return days #[counts,days]

def genHours(days):
    ## outputs the hours per day as a file.
    out=""
    for day in days:
        print day
        out+="%s %in"%(day.strftime("%Y_%m_%d"),days[day])
    f=open('hours.txt','w')
    f.write(out)
    f.close()
    return

def smoothListGaussian(list,degree=7):
    ## (from an article I wrote) - Google "linear data smoothing with python".
    firstlen=len(list)
    window=degree*2-1
    weight=numpy.array([1.0]*window)
    weightGauss=[]
    for i in range(window):
     i=i-degree+1
     frac=i/float(window)
     gauss=1/(numpy.exp((4*(frac))**2))
     weightGauss.append(gauss)
    weight=numpy.array(weightGauss)*weight
    smoothed=[0.0]*(len(list)-window)
    for i in range(len(smoothed)):
     smoothed[i]=sum(numpy.array(list[i:i+window])*weight)/sum(weight)
    pad_before = [smoothed[0]]*((firstlen-len(smoothed))/2)
    pad_after  = [smoothed[-1]]*((firstlen-len(smoothed))/2+1)
    return pad_before+smoothed+pad_after

### IF YOU USE MY DATA, YOU ONLY USE THE FOLLOWING CODE ###

def graphIt():
    ## Graph the data!
    #f=open('hours.txt')
    #data=f.readlines()
    data=allHours
    data.sort()
    f.close()
    days,hours=[],[]
    for i in range(len(data)):
        day = data[i].split(" ")
        if int(day[1])<4: continue
        days.append(datetime.datetime.strptime(day[0], "%Y_%m_%d"))
        hours.append(int(day[1]))
    fig=pylab.figure(figsize=(14,5))
    pylab.plot(days,smoothListGaussian(hours,1),'.',color='.5',label="single day")
    pylab.plot(days,smoothListGaussian(hours,1),'-',color='.8')
    pylab.plot(days,smoothListGaussian(hours,7),color='b',label="7-day gausian average")
    pylab.axhline(8,color='k',ls=":")
    pylab.title("Computer Usage at Work")
    pylab.ylabel("hours (rounded)")
    pylab.legend()
    pylab.show()
    return

#times = genTimes()
#genHours(linearize(loadTimes()))
graphIt()

Audio and Video on an ATMega88 Microcontroller!

At least 99% of my blog entries contain original content I created. However, I was so impressed by something I stumbled upon tonight that I absolutely cannot resist sharing it. It's a project which aims to output audio and video simultaneously from a single microcontroller. The author's site has all the details, but if you watch the video below you'll be amazed. (Just get through the 30 second narration at the beginning) Apparently the guy rendered video in horizontal lines from the software and outputted audio signals between horizontal lines! Amazing!

On second thought, this was no big deal in the 80's, so why am I so impressed by it now? The 8-bit microcontrollers this guy is programming is likely on par with the PCs of the 80's. I guess that in the 70's this would have been amazing because it was cutting edge. In the 80's this would have been boring because it was commonplace. In the 2000's, this is amazing because no one in my generation is old enough to remember how amazing this was in the 70's and 80's!

After researching some similar projects I realized I'm becoming fascinated with chiptune synthesizing code, hardware, and music. It's basically a type of code to tell a synthesizer how to synthesize the music, rather than having it play pre-recorded music. It's like a merge of a programming language and MIDI. For non-technical people, it's like giving a microchip the sheet music for all the different instruments of an orchestra and having the microchip play everything from the sheet music, rather than giving it a CD to play. Here's some video of a PC-based front-end to the audio creation process. Notice how each line represents a time, and how certain codes in certain channels represent notes (hence the MIDI aspect). Numbers on the far right represent the location of the memory, and notice also how it goes back and forth, replaying certain areas when necessary (to safe space, hence the coding aspect).

I can't describe my emotional state right now. It's like I have an extreme nostalgia for an era I never even lived in. This chip level audio synthesis stuff sounds amazingly fun to me. (I've already bookmarked nolife-radio.com and 8bitFM.com) I feel drawn toward it... but I'm scared to get sucked in. I wish I were a college student in the 80's studying electrical engineering. Here I am, having just gotten a master's in molecular biology and microbiology, and I feel like I wasted six years of my life in the process. I'm about to start dental school in August. Hopefully when I look back from the future I won't feel like I wasted another four years doing that.

Either way, I have an endless supply of possible projects to do this summer (not even going into the small list of projects I'm trying/expected to complete in the cardioneurophysiology lab I work in). I feel like I'm running out of time. I start dental school in August, and I dread the date. Not necessarily because I expect it to be difficult, but because I feel [however illogical, irrational, or ridiculous] that I'm actually doing something significant, working with my hands and working with my brain to do things that [almost] no one has done before, and doing things in a way that no one has ever thought of doing them. I feel like when I start resume classes, it's another four years of people telling me how I should do things so I can be exactly like everyone else. How can you exercise creativity as a dental student? I'm sure there are ways, but it's certainly leagues away from the projects engineering college students work on. As far as the career goes, if you're an engineer the best case scenario is that you do something no one has ever done before. If you're a dentist, the best case scenario is to do things exactly as everyone else does them. Maybe I'll go crazy and change the wallpaper in my office every few months.

⚠️ Warning: This article is obsolete.
Articles typically receive this designation when the technology they describe is no longer relevant, code provided is later deemed to be of poor quality, or the topics discussed are better presented in future articles. Articles like this are retained for the sake of preservation, but their content should be critically assessed.

ATTiny2313 Controlling a HD44780 LCD with AVR-GCC

After a day of tinkering I finally figured out how to control a HD44780 display from an ATTiny2313 microcontroller. There are a lot of websites out there claiming to show you how to do this on similar AVRs. I tried about 10 of them and, intriguingly, only one of them worked! I think the problem is that many of those websites show code for an ATMega8 and I'm using an ATTiny2313. Since it took me so long to get this right I decided to share it on the internet for anyone else having a similar struggle.

You might recognize this LCD panel from some PC parallel port / LCD interface projects I worked on about 5 years ago. It's a 20-column, 2-row, 8-bit parallel character LCD. This means that rather than telling each little square to light up to form individual letters, you can just output text to the microcontroller embedded in the display and it can draw the letters, move the cursor, or clear the screen. These are the connections I made were:

  • LCD1 -> GND
  • LCD2 -> +5V
  • LCD3 (contrast) -> GND
  • LCD4 (RS) -> AVR D0 (pin2)
  • LCD5 (R/W) -> AVR D1 (pin3)
  • LCD6 (ES) -> AVR D2 (pin6)
  • LCD 11-14 (data) -> AVR B0-B3 (pins 12-15)

The code to control this LCD from the ATTiny2313 was found on Martin Thomas' page (dead link removed in 2019). I included the .h and .c files and successfully ran the following program on my AVR. I used the internal RC clock.

// ATTiny2313 / HD44780 LCD INTERFACE
#include <stdlib.h>;
#include <avr/io.h>;
#include <util/delay.h>;
#include "lcd.h"
#include "lcd.c"

int main(void)
{
    int i=0;
    lcd_init(LCD_DISP_ON);
    lcd_clrscr();
    lcd_puts("ATTiny 2313 LCD Demo");
    lcd_puts("  www.SWHarden.com  ");
    _delay_ms(1000);
    lcd_clrscr();
    for (;;) {
        lcd_putc(i);
        i++;
        _delay_ms(50);
    }
}
// modified the top of "lcd.h"
#define LCD_PORT         PORTB        /**&lt; port for the LCD lines   */
#define LCD_DATA0_PORT   LCD_PORT     /**&lt; port for 4bit data bit 0 */
#define LCD_DATA1_PORT   LCD_PORT     /**&lt; port for 4bit data bit 1 */
#define LCD_DATA2_PORT   LCD_PORT     /**&lt; port for 4bit data bit 2 */
#define LCD_DATA3_PORT   LCD_PORT     /**&lt; port for 4bit data bit 3 */
#define LCD_DATA0_PIN    0            /**&lt; pin for 4bit data bit 0  */
#define LCD_DATA1_PIN    1            /**&lt; pin for 4bit data bit 1  */
#define LCD_DATA2_PIN    2            /**&lt; pin for 4bit data bit 2  */
#define LCD_DATA3_PIN    3            /**&lt; pin for 4bit data bit 3  */
#define LCD_RS_PORT      PORTD     /**&lt; port for RS line         */
#define LCD_RS_PIN       0            /**&lt; pin  for RS line         */
#define LCD_RW_PORT      PORTD     /**&lt; port for RW line         */
#define LCD_RW_PIN       1            /**&lt; pin  for RW line         */
#define LCD_E_PORT       PORTD     /**&lt; port for Enable line     */
#define LCD_E_PIN        2            /**&lt; pin  for Enable line     */

// AND A LITTLE LOWER, I CHANGED THIS LINE TO 4-BIT MODE
#define LCD_FUNCTION_8BIT     0      /*   DB4: set 8BIT mode (0-&gt;4BIT mode) */

Here is video of the output. Notice how this display can show English (lowercase/uppercase/numbers) as well as the Japanese character set!

Note from Future Scott (ten years later, August, 2019):

The link to the downloadable source code from Martin Thomas' page is no longer functional. These links do work:

A more recent project which uses these displays is:

⚠️ Warning: This article is obsolete.
Articles typically receive this designation when the technology they describe is no longer relevant, code provided is later deemed to be of poor quality, or the topics discussed are better presented in future articles. Articles like this are retained for the sake of preservation, but their content should be critically assessed.

Removing Textile Markup From Wordpress Entries

I realized that the C code from yesterday wasn't showing-up properly because of textile, a rapid, inline, tag-based formatting system. While it's fun and convenient to use, it's not always practical. The problem I was having was that in C code, variable names (such as delay) were becoming irrevocably italicized, and nothing I did could prevent textile from ignoring code while styling text. The kicker is that I couldn't disable it easily, because I've been writing in this style for over four years! I decided that the time was now to put my mad Python skills to the test and write code to handle the conversion from textile-format to raw HTML. I accomplished this feat in a number of steps. Yeah, I could have done hours of research to find a "faster way", but it simply wouldn't have been as creative. In a nutshell, I backed-up the SQL database using PHPMyAdmin to a single "x.sql" file. I then wrote a pythons script to parse this [massive] file and output "o.sql", the same data but with all of the textile tags I commonly used replaced by their HTML equivalent. It's not 100% perfect, but it's 99.999% perfect. I'll accept that. The output? You're viewing it! Here's the code I used to do it:

## This Python script removes *SOME* textile formatting from Wordpress
## backups in plain text SQL format (dumped from PHP MyAdmin). Specifically,
## it corrects bold and itallic fonts and corrects links. It should be easy
## to expand if you need to do something else with it.

infile = 'x.sql'

replacements=   ["r"," "],["n"," n "],["*:","* :"],["_:","_ :"],
                ["n","&lt;br&gt;n"],["&gt;*","&gt; *"],["*&lt; ","* &lt;"],
                ["&gt;_","&gt; _"],["_&lt; ","_ &lt;"],
                [" *"," &lt;b&gt;"],["* "," "],[" _"," &lt;i&gt;"],["_ ","&lt;/i&gt; "]
                #These are the easy replacements

def fixLinks(line):
    ## replace ["links":URL] with [&lt;a href="/blog/2009-05-15-removing-textile-from-wordpress/URL"&gt;links&lt;/a&gt;]. ##
    words = line.split(" ")
    for i in range(len(words)):
        word = words[i]
        if '":' in word:
            upto=1
            while (word.count('"')&amp;lt;2):
                word = words[i-upto]+" "+word
                upto+=1
            word_orig = word
            extra=""
            word = word.split('":')
            word[0]=word[0][1:]
            for char in ".),'":
                if word[1][-1]==char: extra=char
            if len(extra)&gt;0: word[1]=word[1][:-1]
            word_new='&lt;a href="/blog/2009-05-15-removing-textile-from-wordpress/%s"&gt;%s&lt;/a&gt;'%(word[1],word[0])+extra
            line=line.replace(word_orig,word_new)
    return line

def stripTextile(orig):
    ## Handle the replacements and link fixing for each line. ##
    if not orig.count("', '") == 13: return orig #non-normal post
    line=orig
    temp = line.split
    line = line.split("', '",5)[2]
    if len(line)&amp;lt;10:return orig #non-normal post
    origline = line
    line = " "+line
    for replacement in replacements:
        line = line.replace(replacement[0],replacement[1])
    line=fixLinks(line)
    line = orig.replace(origline,line)
    return line

f=open(infile)
raw=f.readlines()
f.close
posts=0
for raw_i in range(len(raw)):
    if raw[raw_i][:11]=="INSERT INTO":
        if "wp_posts" in raw[raw_i]: #if it's a post, handle it!
            posts+=1
            print "on post",posts
            raw[raw_i]=stripTextile(raw[raw_i])

print "WRITING..."
out = ""
for line in raw:
    out+=line
f=open('o.sql','w')
f.write(out)
f.close()

I certainly held my breath while the thing ran. As I previously mentioned, this thing modified SQL tables. Therefore, when I uploaded the "corrected" versions, I kept breaking the site until I got all the bugs worked out. Here's an image from earlier today when my site was totally dead (0 blog posts)

page 1, page 2, page 3, page 4, page 5, page 6, page 7, page 8, page 9, page 10, page 11, page 12, page 13, page 14, page 15, page 16, page 17, page 18, page 19, page 20, page 21, page 22, page 23, page 24, page 25, page 26, page 27, page 28, page 29, page 30, page 31, page 32, page 33, page 34, page 35, page 36, page 37, page 38, page 39, page 40, page 41, page 42, page 43, page 44, page 45, page 46, page 47, page 48, page 49
All Blog Posts