Thursday, September 18, 2014

High Performance Python by Micha Gorelick & Ian Ozsvald; O'Reilly Media

One of the big draws of the Python programming language is that it is very easy to develop something relatively complex quite rapidly.  However, Python is much more than a prototyping language, and High Performance Python: Practical Performant Programming for Humans is a great resource to help you think about how you approach problems in Python, as well as tracking down and improving bottlenecks in your code.


This book is definitely not going to teach you Python.  There are many other tutorials and references out there to learn about the language, and this book assumes you are already a proficient Python programmer and will be able to read and understand the code examples they provide.  This book is about tuning your Python code to run faster. 

The progression of the chapters is very logical, and some of the same toy problems re-appear throughout the book as additional optimizations provide even greater efficiency improvements.  The book introduces a large number of tools, and it mostly gives you an idea of what the tool is and why you might consider it.  To really use any of the tools in practice, you'll want to reference online documentation, but this book gives you a good idea of where to start looking.


I was particularly interested in reading the "Clusters and Job Queues" chapter before I got the book, and it helped guide me to an IPython.parallel solution that fits my current problem quite nicely, as well as provide some other tools I may investigate in the future.

The authors recommend the Anaconda Python distribution by Continuum Analytics on several occasions, and I definitely agree.  Some of the tools and techniques in the book use only the Standard Library, but most of the more advanced topics require external modules.  Many of the modules referenced (numpy, Cython, Tornado, & IPython to name just a few) are included in the Anaconda distribution as one simple download.

This book's use is twofold.  First, it is worth a full read-through for the discussion of the various things that tend to slow down Python code (or code in general) and what kinds of approaches you should be aware of.  Second, it provides good, brief examples of many different tools in practice, as well as listing other recommended resources at the end of each chapter, allowing it to serve as a good reference text.


One point the authors make repeatedly is that you must consider the trade between code execution time and development velocity.  Many of the things you can do to speed up your code will make it considerably harder to understand and work with in the future.  It's important to always have proof that you are optimizing the right portions of code and that the benefits are worth it.  They help you to look for the "big wins" where you can get drastic speed improvements with minimal effort and complexity.

Disclaimer: I received a free Ebook copy of this work under the O'Reilly Blogger Review Program.  I also happened to like it so much that I bought a hard copy as well so I can have it on my reference shelf at work.

Tuesday, August 13, 2013

Raspberry Pi Pandora Streamer


I recently finished a project to turn the Raspberry Pi into a music streamer.  I had seen a couple of other projects here and here that were the same basic concept, so I knew it was possible (this is nothing new...just new for me), and I wanted to try to figure it out by myself.  My basic justification for the project was that I listen to a lot of music on Pandora or Spotify while reading and doing homework, but having it up on the computer means I have an instant distraction any time I feel like checking email, news, or whatever.  Building a small, standalone player with just a screen to display song titles and a simple interface using a few buttons removes that temptation.  I also had a couple of weeks off school between summer and fall sessions, and I wanted a quick project I could finish up before I lose all free time again.

Spotify provides a C API which looks pretty cool, but you need to have a premium membership ($10/month) to use it.  It would also be more involved to get working correctly, but I may come back to that in the future if I get more time, because it seems like it'd be a fun project.  For Pandora, there is a Linux command line program called pianobar that provides a really clean interface to Pandora and doesn't have any advertisements, and it's free.  I decided to stick with Pandora via pianobar for now and create my program to control it in Python.

I'm doing the development work using my Ubuntu desktop to ssh into the Raspberry Pi.  I'm using the Raspbian Wheezy distribution on the Pi, and you have to enable ssh when you first configure it.  To enter the configuration menu again later you can use the following command.
sudo raspi-config
I connected my Raspberry Pi to a monitor to figure out it's IP address (using the "ifconfig" command), which is 192.168.1.11 (replace that with your IP address the following command).  Running the following command from the terminal on your local computer will give you a terminal on the Raspberry Pi.  Using the "-X" option lets you open up GUI windows like the text editor "nedit" (or the simple Pandora GUI below).  You will be prompted for your password for the "pi" user, which is "raspberry", unless you've changed it.
ssh -X pi@192.168.1.11
First off, you need to install pianobar.  The instructions here are for Lubuntu, but they work on the Raspberry Pi as well.  The "make" program was already installed for me, but the rest were new.  You need to install "git" in order to download the source code from github, and then the rest of the packages are needed to build.
sudo apt-get install git
git clone https://github.com/PromyLOPh/pianobar.git
cd pianobar
sudo apt-get install libao-dev libgcrypt11-dev libgnutls-dev libfaad-dev libmad0-dev libjson0-dev make pkg-config
make
sudo make install
After all of this you can type "pianobar" on the command line and the program will start, prompting you for your Pandora username (email) and password.
 
When I first started pianobar, it printed the following error: "ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.front".  I found a post here that describes how to fix it.  You need to change the line that says "pcm.front cards.pcm.front" to "pcm.front cards.pcm.default" in /usr/share/alsa/alsa.conf.  The file is read only, so you'll need to open up the permissions or edit it using sudo.  For me, the line number to change was 130 but I'm not sure if that will be true for others.

I first set up pianobar back in May, and at that time there was a loud popping sound at the beginning and end of every song.  I did an update since then, and the popping went away, so they must have fixed whatever was causing that issue. 

Another issue I've had (and still do) is that if you try to skip songs too many times Pandora locks you out.  It starts out by making all tracks 42 seconds long of just silence, and if you keep skipping it eventually locks you out entirely.  It's not really consistent how many skips triggers it, but just realize that if it stops working you should probably just give it a break for a bit.

You can store the username and password in a file that pianobar will automatically load when it starts.  The filename is "/home/pi/.config/pianobar/config" and it should contain the following two lines.  This worked for me when I used pianobar interactively, but I later realized that you have to run programs that use the GPIO pins as root, so you'll want to put the same file at "/root/.config/pianobar/config".
user = zjonesengineering@gmail.com
password = your_password_here
The final step to configuring pianobar so that you can control it from python is to set up a fifo using the following command.  The same thing goes here for running as root, so I've included a second command for that.
mkfifo ~/.config/pianobar/ctl
sudo mkfifo /root/.config/pianobar/ctl
I started out by making a simple python GUI as a substitute for the buttons and screen that would later be connected to the Raspberry Pi.  This allowed me to get the interface between python and pianobar working correctly before having to worry about any of the hardware.  I used Tkinter for the GUI, and it was pretty basic.  There were a couple of lines that displayed information that would later be shown on a 16x2 LCD, and then buttons that have the same functions as the physical buttons that would be added later.  I have buttons to start/stop pianobar, play/pause the song, skip to next song, ban song (don't play again for 1 month), or "love" song.  There are plenty of other things I could add, and pianobar has a very full-featured command set, but I wanted to keep it simple.  These are the same buttons I kept on my final physical version.
# input_GUI.py

from tkinter import *
import subprocess
import re

fifo = "/home/zach/.config/pianobar/ctl"


class App:
    
    def __init__(self,master):
        frame = Frame(master)
        frame.pack()
        
        self.row_counter = 0
        self.pianobarOnFlag = 0

        #Start/Stop
        button = Button(frame, text='Start/Stop', command=self.startStopButton)
        button.grid(row=self.row_counter, column = 0)

        #Play/Pause
        button = Button(frame, text='Play/Pause', command=self.playPauseButton)
        button.grid(row=self.row_counter, column = 1)
        
        #Next Song
        button = Button(frame, text='Next', command=self.nextButton)
        button.grid(row=self.row_counter, column = 2)

        #Love Song
        button = Button(frame, text='Love', command=self.loveButton)
        button.grid(row=self.row_counter, column = 3)
        
        #Ban Song
        button = Button(frame, text='Ban', command=self.banButton)
        button.grid(row=self.row_counter, column = 4)
        
        self.row_counter+=1
        
        #Display: Line 1
        self.line1  =StringVar()
        Label(frame, text = 'Song').grid(row=self.row_counter, column=0)
        Entry(frame, textvariable=self.line1).grid(row=self.row_counter, column=1)
        self.row_counter+=1

        #Display: Line 1
        self.line2  =StringVar()
        Label(frame, text = 'Artist').grid(row=self.row_counter, column=0)
        Entry(frame, textvariable=self.line2).grid(row=self.row_counter, column=1)
        self.row_counter+=1
        
        
    def startStopButton(self):
        print('Start/Stop button pressed')
        if self.pianobarOnFlag == 0:
            print('Starting pianobar!')
            self.pianobar_output = open("pianobar.out","w")
            self.pianobar_output.close()
            self.pianobar_output = open("pianobar.out","r+")
            self.pianobarOnFlag = 1
            self.outfilePosition = 0
            subprocess.Popen("pianobar", stdout = self.pianobar_output)
        else:
            print('Stopping pianobar!')
            self.writeFifo(command = 'q')
            self.pianobarOnFlag = 0
            self.pianobar_output.close()

    def playPauseButton(self):
        print('Play/Pause button pressed')
        self.writeFifo(command = 'p')
        
    def nextButton(self):
        print('Next button pressed')
        self.writeFifo(command = 'n')

    def loveButton(self):
        print('Love button pressed!')
        self.writeFifo(command = '+')

    def banButton(self):
        print('Ban button pressed!')
        self.writeFifo(command = '-')
        
        
    def getSongInfo(self):
        print('getSongInfo Called!')
        if self.pianobarOnFlag == 1:
            print('Grepping for song info!')
            self.pianobar_output.seek(self.outfilePosition)
            text = self.pianobar_output.read()
            self.outfilePosition = self.pianobar_output.tell()
            print(text)

            infoStr = re.search('\>.*by.*on.*', text)
            if infoStr is not None:
                print('Info String:')
                print(infoStr.group(0))
                infoStrSplit = infoStr.group(0).split()
                print(infoStrSplit)
                for index in range(len(infoStrSplit)):
                    if infoStrSplit[index]=='by':
                        byIdx = index
                    if infoStrSplit[index]=='on':
                        onIdx = index
                song = ' '.join(infoStrSplit[1:byIdx])
                song = song.replace('"','')
                artist = ' '.join(infoStrSplit[byIdx+1:onIdx])
                artist = artist.replace('"','')
                self.line1.set(song)
                self.line2.set(artist)

    def getSongNameLoop(self):
        self.getSongInfo()
        root.after(2000,self.getSongNameLoop)

    def writeFifo(self, command):
        fifo_w = open(fifo, 'w')
        fifo_w.write(command)
        fifo_w.close()


root = Tk()
root.wm_title('Jukebox')
app = App(root)

root.after(2000,app.getSongNameLoop()) #Check for new song info every 2 seconds
root.mainloop()
I used Python 3 for the GUI, and I ran it from my Ubuntu desktop until I was ready to transition to the Raspberry Pi.  If you copy the code below into a text file called "input_GUI.py", you can call it from the terminal by typing "python3 input_GUI.py".  It will open up a GUI window that looks like the one below, and you can click the buttons to interface with pianobar.


Once I had the interface between Python and pianobar working correctly, I transitioned to the Raspberry Pi.  The main things to add here were the physical screen and buttons.  The most common library I was finding for GPIOs was for Python 2, not Python 3, so I switched to that.

There are two tutorials from Adafruit Industries that I used as a basis for setting up the GPIOs and the LCD library.  I've copied the pertinent commands below that you need for set up.
sudo apt-get install python-dev
sudo apt-get install python-rpi.gpio
sudo apt-get install python-setuptools
sudo easy_install -U distribute
sudo apt-get install python-pip
sudo pip install rpi.gpio
git clone http://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code.git
I think that I've listed all of the packages that are needed here.  I may have installed some a while ago that I forgot about, so if you try this and it doesn't work let me know and I'll figure out what I've left out.

Inside the package that downloads with the last command is a file named "Adafruit_CharLCD/Adafruit_CharLCD.py", which is the Python class I used to interface with the 16x2 LCD display.  You need to copy this file to wherever you are running the final Python script from.  This class is set to use the GPIO pins for a Raspberry Pi v1, and I have a Raspberry Pi v2 (you likely do as well), so that requires a minor tweak to line 57 of the file to the following because of a renaming of the pins.  The key is that the "27" has replaced a "21".
def __init__(self, pin_rs=25, pin_e=24, pins_db=[23, 17, 27, 22], GPIO = None):
This site has a good graphic that maps the pin headers on the Raspberry Pi to their GPIO numbers, and I found it to be helpful.  I used a Pi Cobbler for prototyping, and it only lists GPIO numbers for pins that don't have an alternate function.  I'm using the SPI pins as GPIOs, so I it was useful to see what numbers those were.

The circuit diagram for the display is very simple.  It basically just connects the LCD screen and buttons to the Raspberry Pi.  I drew up a quick schematic in Eagle of the connections.  Ignore the "P$" numbers on the Raspberry Pi header pins...I made a rough Eagle part to represent the header and those numbers don't mean anything.


The code should be fairly self-explanatory.  The first section is a class to interface with pianobar, which is very similar to the class I set up in the GUI version.  The second section contains a function to update the display.  Artist/song names can be longer than 16 characters, so I set it up so that if the text is too long it scrolls across the display.  The bulk of a code is a loop that lasts forever that checks for button presses every half second.  I set it up so that an LED would turn on whenever I pushed a button other than the "start/stop" button and stay on until I released it as a simple form of debouncing the inputs.  The full code is below.
#!/usr/bin/python

# rpi_jukebox.py

from Adafruit_CharLCD import Adafruit_CharLCD
from time import sleep
import subprocess
import re
import math
import RPi.GPIO as GPIO

##############################################
# Pianobar Class
##############################################

class Pianobar:
    def __init__(self):
        #FIFO location
        self.fifo = "/root/.config/pianobar/ctl"

    def start(self):
        print('Starting pianobar!')
        #Create file for pianobar output...used to get artist names and song titles
        self.pianobar_output = open("pianobar.out","w")
        self.pianobar_output.close()
        self.pianobar_output = open("pianobar.out","r+")
        self.outfilePosition = 0
        #Start pianobar
        subprocess.Popen("pianobar", stdout = self.pianobar_output)

    def stop(self):
        print('Stopping pianobar!')
        #Stop pianobar
        self.writeFifo(command = 'q')
        #Close output file
        self.pianobar_output.close()

    def playPause(self):
        print('Play/Pause')
        self.writeFifo(command = 'p')
        
    def next(self):
        print('Next')
        self.writeFifo(command = 'n')

    def love(self):
        print('Love')
        self.writeFifo(command = '+')

    def ban(self):
        print('Ban')
        #Ban song for 1 month
        self.writeFifo(command = '-')
        
    def getSongInfo(self):
        #Get artist name and song name from output file
        self.pianobar_output.seek(self.outfilePosition)
        text = self.pianobar_output.read()
        self.outfilePosition = self.pianobar_output.tell()
        infoStr = re.search('\>.*by.*on.*', text)
        if infoStr is not None:
            infoStrSplit = infoStr.group(0).split()
            for index in range(len(infoStrSplit)):
                if infoStrSplit[index]=='by':
                    byIdx = index
                if infoStrSplit[index]=='on':
                    onIdx = index
            song = ' '.join(infoStrSplit[1:byIdx])
            song = song.replace('"','')
            artist = ' '.join(infoStrSplit[byIdx+1:onIdx])
            artist = artist.replace('"','')
            songInfo = [song,artist]
        else:
            songInfo = ['','']
        return songInfo


    def writeFifo(self, command):
        #Write a command to the pianobar FIFO
        fifo_w = open(self.fifo, 'w')
        fifo_w.write(command)
        fifo_w.close()


##############################################
# Update Display Function
##############################################

def updateDisplay(songInfo,lcd,displayOld):
    display = ['',displayOld[1],displayOld[2]]
    #Song - line1
    if len(songInfo[0]) <= 16: #Center on screen
        padding = (16 - len(songInfo[0])) / 2
        line1 = (' ' * int(math.floor(padding))) + songInfo[0] + (' ' * int(math.ceil(padding)))
    else: #Scroll
        line1 = songInfo[0][display[1]:int(min(len(songInfo[0]),display[1]+16))]
        line1 = line1 + ' ' * int(16-len(line1))
        display[1]+= 1
        if display[1] == len(songInfo[0]):
            display[1] = 0
    #Artist - line2
    if len(songInfo[1]) <= 16: #Center on screen
        padding = (16 - len(songInfo[1])) / 2
        line2 = (' ' * int(math.floor(padding))) + songInfo[1] + (' ' * int(math.ceil(padding)))
    else: #Scroll
        line2 = songInfo[1][display[2]:int(min(len(songInfo[1]),display[2]+16))]
        line2 = line2 + ' ' * int(16-len(line2))
        display[2]+= 1
        if display[2] == len(songInfo[1]):
            display[2] = 0
    display[0] = str(line1 + '\n' + line2)
    if display[0] != displayOld[0]: #Only update if new
        lcd.clear()
        lcd.message(display[0])
    return display

##############################################
# Main Program
##############################################

#Set up GPIO for buttons
GPIO.setmode(GPIO.BCM) #Use GPIO numbers

#Assign buttons
startStopButton = 2 #Header Pin 3
playPauseButton = 3 #Header Pin 5
nextButton = 4      #Header Pin 7
loveButton = 10     #Header Pin 19
banButton = 9       #Header Pin 21

#Set up button pins as inputs
GPIO.setup(startStopButton, GPIO.IN)
GPIO.setup(playPauseButton, GPIO.IN)
GPIO.setup(nextButton, GPIO.IN)
GPIO.setup(loveButton, GPIO.IN)
GPIO.setup(banButton, GPIO.IN)

#Status LED
statusLED = 11      #Header Pin 23
GPIO.setup(statusLED, GPIO.OUT)
GPIO.output(statusLED, False)

#Initialize LCD 
lcd = Adafruit_CharLCD()
lcd.begin(16,2)

pianobarOnFlag = 0

while (1):
    sleep(0.5)
    if GPIO.input(startStopButton) == False:
        if pianobarOnFlag == 0:
            #Turn on pianobar
            pb = Pianobar()
            pb.start()
            songInfo = ['RPI Jukebox','Initializing...'] 
            displayOld = ['',0,0] #Display String, Song Scroll Idx, Artist Scroll Idx
            pianobarOnFlag = 1
        else:
            #Turn off pianobar
            pb.stop()
            pianobarOnFlag = 0
            songInfo = ['RPI Jukebox','Goodbye...']
            updateDisplay(songInfo,lcd,displayOld)
            sleep(5)
            songInfo = [' ',' ']
            updateDisplay(songInfo,lcd,displayOld)
    if (GPIO.input(playPauseButton) == False) and (pianobarOnFlag == 1):
        pb.playPause()
        #Flash LED to register input
        GPIO.output(statusLED, True)
        lcd.clear()
        lcd.message('   Play/Pause   ')
        displayOld = [' ',0,0] #Force display update after
        while(GPIO.input(playPauseButton) == False):
            sleep(0.1)
        GPIO.output(statusLED, False)
    if (GPIO.input(nextButton) == False) and (pianobarOnFlag == 1):
        pb.next()
        #Flash LED to register input
        GPIO.output(statusLED, True)
        lcd.clear()
        lcd.message('   Next Song    ')
        displayOld = [' ',0,0] #Force display update after
        while(GPIO.input(nextButton) == False):
            sleep(0.1)
        GPIO.output(statusLED, False)
    if (GPIO.input(loveButton) == False) and (pianobarOnFlag == 1):
        pb.love()
        #Flash LED to register input
        GPIO.output(statusLED, True)
        lcd.clear()
        lcd.message('   Love Song    ')
        displayOld = [' ',0,0] #Force display update after
        while(GPIO.input(loveButton) == False):
            sleep(0.1)
        GPIO.output(statusLED, False)
    if (GPIO.input(banButton) == False) and (pianobarOnFlag == 1):
        pb.ban()
        #Flash LED to register input
        GPIO.output(statusLED, True)
        lcd.clear()
        lcd.message('    Ban Song    ')
        displayOld = [' ',0,0] #Force display update after
        while(GPIO.input(banButton) == False):
            sleep(0.1)
        GPIO.output(statusLED, False)
    if pianobarOnFlag == 1: 
        #Update Display
        songInfoNew = pb.getSongInfo()
        if songInfoNew[0] != '' and songInfoNew[0] != songInfo[0]:
            songInfo = list(songInfoNew)
        displayOld = updateDisplay(songInfo,lcd,displayOld)
I started out by breadboarding the circuit, and I used an Adafruit Pi Cobbler breakout board for the Raspberry Pi header.  Once it all worked, I then soldered it up on a small protoboard to make it more permanent.  I soldered the screen on at as much of an angle as the header would allow, and then used a couple of long machine screws in two of the mounting holes on the protoboard to prop up the entire thing so the screen is readable while sitting on my desk.  It's not pretty or polished, but it does what I need and was fun to put together.

Here's a video showing the finished project.  I hit the limit where it stops playing songs (and does 42 seconds of silence instead) while I was filming the video, but you can see the idea of how it works.

Tuesday, April 16, 2013

First PC Build: Software - Setting Up Ubuntu 12.10

I had already decided that I wanted to install Linux, but I didn't realize until I started researching a bit just how broad the options were, and how passionate people are about which distribution is the best.  I read a lot of articles (this one on Lifehacker was pretty good), but didn't really have a good reason to select anything in particular.  I considered Fedora pretty heavily for a couple of reasons.  First, I use Red Hat at work, so the connection there seemed like it might be a good reason.  Second, there is a "spin" (repackaged distribution with extra software included) called Fedora Electronic Lab that includes an impressive collection of software for electronics design that was tempting.  However, some places I looked made it seem like Fedora tends to be a little less stable due to its emphasis on getting updates out as soon as possible.  I ended up choosing Ubuntu instead, mainly because I didn't have a great reason to pick anything else and it seemed to have a very large community in case I ran into issues.

The downloading and installation process was very straightforward.  I downloaded an ISO from the main download page and used the Windows Disc Image Burner on Windows 7 to create the install DVD.  I am using Ubuntu 12.10 "Quantal Quetzal" 64-bit.  There is a release page that has the common download options available if you want to see the other choices.  During the installation process there is an option to install restricted extras, which I selected, giving you the ability to playback some media formats without having to install additional software later.

Below, I am listing the different additional software I installed after getting the computer up and running.  I may add to this in the future, and this is mostly a log for myself in case I have to do this again later.

The Unity desktop that comes standard on Ubuntu 12.10 is going to take some getting used to.  I may stick with it, but I decided to download a few other desktop environments to try out as well before settling.  These command will install the classic Gnome desktop, the KDE desktop, and the LUbuntu desktop (which is supposed to be lighter weight).


sudo apt-get install gnome-panel
sudo apt-get install kubuntu-desktop
sudo apt-get install lubuntu-desktop

Ubuntu installs with open source graphics drivers that worked fine with the APU I have, but AMD releases a proprietary driver that I installed instead.  There is an unofficial wiki from AMD that contains installation instructions.  Make sure you find the page for your specific distribution.  I initially followed the directions for Ubuntu 12.04 and things did not go well.  I followed the manual installation instructions, which are listed below.  You could also download the zip file from the AMD website instead of the wget line below.

sudo apt-get install build-essential cdbs dh-make dkms execstack dh-modaliases linux-headers-generic fakeroot
sudo apt-get install lib32gcc1
wget http://www2.ati.com/drivers/linux/amd-driver-installer-catalyst-13.1-linux-x86.x86_64.zip
unzip amd-driver-installer-catalyst-13.1-linux-x86.x86_64.zip
chmod +x amd-driver-installer-catalyst-13.1-linux-x86.x86_64.run
sudo ./amd-driver-installer-catalyst-13.1-linux-x86.x86_64.run --buildpkg Ubuntu/quantal
sudo dpkg -i fglrx*.deb
sudo amdconfig --initial -f

After all of these steps, reboot the computer. Then run the following commands to test out the installation and make sure everything works.  The first one should print some information about your GPU, and the second should start a spinning box graphic and report out some information.

fglrxinfo
fgl_glxgears

I have Amazon Prime, which gives you access to stream a lot of TV shows and movies, and when I went to check if it worked....it didn't.  I was using Firefox and my Flash player was up-to-date, but it kept saying I needed to update the player.  I did some searching, and eventually found a solution.

sudo apt-get install libhal1 hal

That worked for a couple weeks, but soon after I did an update in the Ubuntu Software Center...and Amazon Instant Video stopped working.  Apparently there was an update to the flash plug-in that broke it.  A post on the Ubuntu forums describes the fix.

64-bit Download: https://fpdownload.macromedia.com/ge....x86_64.tar.gz

tar -xzf install_flash_player_11_linux.x86_64.tar.gz
sudo cp libflashplayer.so /usr/lib/mozilla/plugins

I don't play computer games, so I don't have anything to compare it to, but I went to Steam, which recently started putting out some Linux games, and downloaded Team Fortress 2 (free to play) to see how it runs on the APU.  It looked like it was working fine to me, and I didn't notice any slowing down or anything.

This one makes me feel a little dumb.  After leaving my computer running for a while, I would check "top" in the terminal and it looked like all of my RAM was being used when the computer was basically idling.  I got pretty frustrated trying to figure out what was going on until I found this site that explains it.  Linux uses unused memory for caching  but it gives it right back to applications when its needed.  My computer is really idling at about 1GB of RAM used, not 3GB+, which is what had me worried.  The "free" command shows how much memory is being used for caching and home much you really have used.

Now, on to the software I really care about.  I mostly want to use this computer to work on my microcontroller projects and C++/Python projects.  GCC and Python came pre-installed, so there's nothing needed to use those.  There are several IDE's available in the Ubuntu Software Center, but for now I plan on just using a text editor and terminal.

Some microcontroller vendors release their tools for Windows only, but there are several officially supported Linux tools.

I use Cadsoft Eagle for PCB layout, and they offer a Linux version.  It comes as a .run file that opens an installer and was very straightforward to install.  I opened up some of their sample project files and it seemed like everything was working correctly. 

Microchip's relatively new MPLAB-X IDE is available for Linux and it also comes as a .run file that is easy to install.  It looks like debugging in Linux is only supported on the PICKIT3, not the PICKIT2, so I'll probably end up getting on of those before I pick back on up one of my stalled PIC18 projects.  I have a storybook reader project and a RC/drone controller project that are both PIC based that haven't made much progress in a while, but hopefully I'll get back to them at some point and transition them over to my Linux development box.

I downloaded the version of the Arduino IDE that is in the Ubuntu repository.  It installed version 1.0.1, while the download page for Arduino is at 1.0.4 for a zipped tar file for Linux.  I'll see once I start working with it if I need to upgrade.  My next/current project will likely use Arduino because I'm helping out a friend with a homebrewing controller who doesn't really have any background in programming/electronics and want to learn through the project and it seemed like the best choice for that.

sudo apt-get install arduino

Atmel also releases an AVR Toolchain for Linux that includes a compiler, assembler, linker and Standard C & math libraries for AVR microcontrollers. I would imagine that the Arduino installation includes some (or all) of this, but it seemed like a useful link to keep track of as well.

Texas Instruments has also provided Linux support for their MSP430 series of microcontrollers with CSS version 5.  The free version is limited to 16kB, but that is larger than most of the low end MSP430's anyway.

Sunday, April 7, 2013

First PC Build: Hardware

I recently finished building my first PC.  My main goal here was to learn something new, because I've never really done much inside my computer and I figured the best way to learn more about it was to put one together myself.  On a more practical level, my wife's laptop is 6 years old and starting to act up, so I want to replace it.  My wife does some photo/video editing, so she needs a reasonably powerful machine, while I could get by with a much less powerful machine than my current desktop for most of what I do.  I decided to give her my current desktop as her main computer (she has a small netbook too for portability and looking up recipes in the kitchen) and build myself a cheaper computer as a learning experience.

My goal for learning here is two-fold.  First, I am interested in learning how to go about finding parts for a computer and physically assembling it.  I'm not trying to build a high-power computer for gaming or anything like that.  Second, I am trying to increase my proficiency and understanding of Linux.  I use Linux daily at work, but my capabilities are mostly limited to shell scripting and C++/Python development work, and a lot of what I would like to learn how to do is restricted to only IT people, so I have no way to learn at work.  My current desktop is set up to boot into either Windows 7 or Ubuntu, but I leave it in Windows basically all of the time because of some Windows only software I need for school.  I recently got a Raspberry Pi with intentions of using it to learn more about Linux...but it's just painfully slow for daily computing tasks.  I'll still use it in projects in the future, but I need something else for daily work.  Making my main computer into a Linux-only desktop seems like a good way to force myself to learn more.

My main uses for the computer will be web browsing, microcontroller projects, and C++/Python software development.  Some microcontroller vendors only release tools for Windows, but I'm not too worried about it.  I still have my old computer if I really need it for something, but I know that I should be able to do what I need to in Linux and I want to force myself to learn.

There is a series of articles on Lifehacker on how to build a computer from scratch that I read through, and then I headed over to Newegg to start looking for parts.  I also noticed that Tigerdirect has some pretty cheap barebones kits, but I wanted to go through the process of picking out the parts myself.  I had a couple of friends look over what I was getting, and they made some suggestions on brands to avoid, and basically made sure everything I picked out would be compatible together.

I re-used an old case from a friend, but the rest of the parts I got from Newegg.  My goal was to keep the cost under $300, and it came in at $320, so I was close.
  • Processor- AMD A8-5500 Trinity APU.  I went with AMD over Intel because it seemed to offer a better deal in the lower end processors.  This is a quad-core APU with Radeon HD 7560D integrated graphics.  I was not planning on getting a graphics card, so the choice was a regular CPU with integrated graphics on the motherboard, or an APU.  I picked the APU.  This is one of the areas I upgraded, because I was initially considering the A4-5300 Trinity APU (which is dual core instead).
  • Motherboard- MSI FM2-A75MA-E35. The AMD Trinity APUs use a FM2 socket, which is not compatible with any of their previous processors (including their first generation APU, Llano), although I believe the series to replace Trinity will share the same socket.  I would probably not recommend this motherboard.  It's working fine, but my board has a glitchy BIOS, and reading around more after purchasing I see that others have had issues as well.  Basically whenever I enter the BIOS menu, it lets me change whatever I want (in my case, upping the RAM speed), but completely freezes when I try to save the settings and I have to then reboot the computer and none of the settings are saved.  I found a forum entry by someone else who had the same issue, and the workaround is to hit the "X" instead of trying to save settings.  It will then prompt you to save, and it commits properly and then reboots.  Everything works, but it's an annoyance, and I'd rather just have it work properly.
  • RAM- G.SKILL Ripjaws X Series 4GB.  A friend suggested the 4GB of faster RAM would probably be a better choice than 8GB of slower RAM if I don't think I'll be really memory intensive, so I went with that.  When I went into the BIOS to up the RAM speed to 1866 (it defaulted to 1600 when I first booted) I had to manually change the timing to what was listed on the Newegg product page (9-10-9-28) because the "auto" setting did not work and everything froze when I tried to just increase the speed without modifying the timing (although it worked fine at 1600 with the auto timing).
  • Hard Drive- Seagate Barracuda 7200RPM 500GB.  Nothing special here.  There was a good sale when I was buying, so I got it.  I would have been fine with a much smaller drive.  I was surprised by how little the price difference was between the different levels of storage.  I also considered getting an SSD instead (because I didn't need a lot of storage space), but even the small ones were expensive and I read a few articles on things to do when running an SSD in Linux, and I decided that sticking with a regular hard drive would be simplest for now.
  • Power Supply- CORSAIR Builder Series CX430.  The power supply was another one that I upgraded based on a friend's recommendation.  Apparently there are brands with bad reputations and I had picked one of those.
  • Optical Drive- LG 24X DVD Burner.  Again, nothing special.  Just a cheap DVD drive.  I almost didn't even include this, because you can install Linux from a thumb drive and I'll probably use thumb drives for any file transfers between computers.
Assembling the computer was surprisingly simple.  Between the Lifehacker article mentioned above and a video from Newegg I had a general idea of what I needed to do, and it was all very straightforward.  There were a couple of snags, but nothing too serious.  First, the main ATX power cable would not "snap" into place.  The motherboard was flexing, so I stopped pushing, but it definitely did not click in, though it is on snug enough and appears to be working fine.  The other issue I had was figuring out what to connect for the front panel connectors on the case.  Only about half of the cables were labelled, but I eventually figured out where they all went.  One of the things I had read about being a challenge is cable management, but my case was pretty empty at the end and there was plenty of room to pull all the excess cable into the empty drive bays.

My next post will talk about setting up the software now that the hardware is assembled.

Friday, June 1, 2012

RDM6300 RFID with PIC18 Dev Board

After getting a simple LED-blink program working, I moved on to the RFID portion of my project.  It seemed to be the simplest of the three major parts (RFID, SD card, and MP3) because it's basically just configuring the UART to receive and check the card IDs.  I'm using the RDM6300 UART RFID Module from ITeadStudio, which cost $11 for the reader and $0.50 per card.  The datasheet is posted and it describes both the pinout of the module and the data format.  I started out by wiring up the power, ground, and TX line to an FTDI breakout board to verify that the RFID reader worked correctly by reading the data into Docklight.  The data format is 9600bps 8N1 serial.  The datasheet listed 13 bytes (0x02, 10 data bytes, checksum, 0x03), yet I consistently received 14 bytes.  I'm not totally sure what's going on, but I always get the same 14 bytes, so I just recorded those for each of the 10 cards I purchased.  The table below shows the hex values I receive for each of the cards.


The datasheet for the RFID module lists the operating current as <50 mA, which is high enough that I want to be able to turn the module on and off from the PIC instead of just leaving it on all the time.  To do this, I used a 2N3906 PNP transistor in between the board 5V supply and the RFID 5V supply, with the base connected to a GPIO with a 1k resistor.  Turning the GPIO high turns the RFID module on;  setting it low turns the RFID module off.  Using the transistor dropped the supply from 5V to 4.87V, but this is still within the +/-5% of 5V listed on the datasheet spec, and it appears to be working just fine.  I also added a resistor divider to the UART TX line of the RFID module to bring the 5V output down below 3.3V.  The following two pictures show a circuit diagram of this, and then what it looks like soldered onto my development board (some parts/wires were soldered underneath the RFID module).



The PIC C18 compiler comes with a set of peripheral libraries, including one for the USART, which made it very easy to configure the USART and had a good example of using it to send and receive data.  Once I verified that I could receive a single card's data, I set up a demo with three RFID tags programmed into the PIC so that it would toggle an LED based on which card was swiped.  I initially tried to use the "gets1USART" function to read a 14 characters off of the USART, but every once and a while the RFID unit spits out a random bad character (sometimes at power up) that was causing me issues.  Instead, the USART reception is handled using an interrupt service routine, and I pulled the basic setup for it from an Example #6 in an older copy of the C18 Getting Started Guide.  The code is below, followed by a video of the demo.  Note that the code assumes a 16 Mhz clock speed, like my previous post.  You'll need to adjust the USART initialization line and delay line if you're using a different clock speed.

#include <p18lf26j11.h>
#include <delays.h>
#include <usart.h>
#include <string.h>

#pragma config OSC = HS //High speed crystal
#pragma config WDTEN = OFF //Disable watchdog timer
#pragma config XINST = OFF //Disable Extended CPU mode

//LED Pin Configuration
#define LED1Pin LATAbits.LATA0
#define LED1Tris TRISAbits.TRISA0
#define LED2Pin LATAbits.LATA1
#define LED2Tris TRISAbits.TRISA1
#define LED3Pin LATAbits.LATA2
#define LED3Tris TRISAbits.TRISA2

#define RFIDVCCPin LATBbits.LATB4 //Define RFIDVCCPin as PORT B Pin 4
#define RFIDVCCTris TRISBbits.TRISB4 //Define RFIDVCCTris as TRISB Pin 4

//Flags & Data Reception Variables
volatile char tagRecdFlag;
volatile char tagComingFlag;
volatile char tagCounter;
volatile char tagRX[14] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

//RFID Tag IDs
char rfidTag1[14] = {0x02, 0x30, 0x35, 0x30, 0x30, 0x41, 0x44, 0x44, 0x38, 0x46, 0x46, 0x43, 0x3f, 0x03};
char rfidTag2[14] = {0x02, 0x30, 0x35, 0x30, 0x30, 0x41, 0x44, 0x42, 0x46, 0x35, 0x37, 0x36, 0x30, 0x03};
char rfidTag3[14] = {0x02, 0x30, 0x35, 0x30, 0x30, 0x41, 0x45, 0x33, 0x46, 0x36, 0x35, 0x44, 0x33, 0x03};

//Function Prototypes
void rx_handler(void);
//Function Prototypes

#pragma code rx_interrupt = 0x8
void rx_int(void)
{
    _asm goto rx_handler _endasm
}
#pragma code

#pragma interrupt rx_handler
void rx_handler(void)
{
    unsigned char rxByte;
    rxByte = RCREG1; //Read character received from USART

    if (tagComingFlag == 0 && rxByte == 0x02)
    {
        tagRX[tagCounter] = rxByte;
        tagComingFlag = 1;
        tagCounter++;
    }   else if (tagComingFlag == 1)
        {
            tagRX[tagCounter] = rxByte;
            tagCounter++;
            if (tagCounter == 14)
            {
                tagCounter = 0;
                tagComingFlag = 0;
                tagRecdFlag = 1;
            }
        }   else
            {
                tagComingFlag = 0;
                tagCounter = 0;
            }
    PIR1bits.RCIF = 0; //Clear interrupt flag
}

void main()
{
        //Set LED Pins data direction to OUTPUT
        LED1Tris = 0;
        LED2Tris = 0;
        LED3Tris = 0;
        //Set LED Pins to OFF
        LED1Pin = 0;
        LED2Pin = 0;
        LED3Pin = 0;

        tagRecdFlag = 0; //Set in ISR if new RFID tag has been read
        tagComingFlag = 0; //Indicates if a tag is partially received
        tagCounter = 0; //Counts byte index
        
        //USART1 Initialization
        Open1USART(USART_TX_INT_OFF & USART_RX_INT_ON & USART_ASYNCH_MODE & USART_EIGHT_BIT & USART_CONT_RX & USART_BRGH_LOW, 25);

        //Interrupts
        RCONbits.IPEN = 1;  //Enable interrupt priority
        IPR1bits.RC1IP = 1;  //Make receive interrupt high priority
        INTCONbits.GIEH = 1; //Enable all high priority interrupts
        
        //Turn on RFID Module
        RFIDVCCTris = 0; //Set to output
        RFIDVCCPin = 1; //Turn on RFID Module

        while(1)
        {
            if (tagRecdFlag)
            {                 
                //Toggle LED corresponding to which card was read
                if (memcmp(tagRX, rfidTag1, 14) == 0)
                {
                    LED1Pin = ~LED1Pin;
                }
                if (memcmp(tagRX, rfidTag2, 14) == 0)
                {
                    LED2Pin = ~LED2Pin;
                }
                if (memcmp(tagRX, rfidTag3, 14) == 0)
                {
                    LED3Pin = ~LED3Pin;
                }

                //Delay 1/2 sec & clear flags to prevent repeated card read
                Delay10KTCYx(200);
                tagRecdFlag = 0; 
                tagComingFlag = 0; 
                tagCounter = 0; 
                }
        }
}

Wednesday, May 30, 2012

PIC18 Getting Started

Now that I have my development boards in hand, I'm getting set up to work with Microchip PIC18 microcontrollers.  My first step (detailed here) is to just get an LED to blink and confirm that I can compile and run code on the microcontroller.  Going forward, I'll do a post each on testing of the other functional parts of my project as I get them working:  the UART interface to an RFID module, working with an SD card and working with the VS1003 MP3 chip.  I don't get much time to play with this stuff, so it'll probably be a few months before I'm done.

I'm going to be using the new MPLAB X IDE, which you can download for free at the MPLAB X IDE download page.  There's video on that page that describes everything you need to do to download and install the IDE.  There are several options to download different compilers.  I selected the "MPLAB IDE X v1.10", "MPLAB X IDE Release Notes/User' Guide", and "MPLAB C18 Lite Compiler for PIC18 MCUs".  The other compiler option for the PIC18 series is the HITECH C compiler, and you can also download a Lite version of this from the same page.  From the research I've done, it looks like the C18 compiler is a better option for compatibility with the application libraries provided by Microchip.  It also looks like both of these compilers will be combined and migrated to the new XC8 compiler from Microchip very soon (or maybe will already be by the time I actually post this), but I'm going to stick with the C18 compiler for now.

For a programmer/debugger, I'm using the PICkit 2, which runs about $35.  Microchip offers several packages that come with the PICkit 2 plus a development board (or the newer PICkit 3 with a development board), but since I have my own board, I bought only the programmer.  There are several other options for programmers from Microchip, and many more from third party sources.  Microchip offers a Development Tool Selector where you can put in the microcontroller you want to use and it will show you which of their development tools are compatible with it.  There's also a device support list for the PICkit 2 specifically, which shows the microcontroller I'm using, PIC18LF26J11, is supported.


After installing the IDE and PICkit 2 driver, I populated enough of my circuit board to start testing with the PIC.  I added the LDO regulators for 5V, 3.3V, and 2.5V, a power LED, the reset circuit, crystal, LEDs, and DIP socket.  My first mistake was soldering on a 48 MHz crystal when I saw 48 MHz as the max clock speed.  I started up the LED blink program, and the timing wasn't even close.  After some digging, I figure out that 48 MHz was the max clock speed, but to get that you either need an external clock or a 12 MHz crystal with the 4x PLL activated.  I'm not finding it in the datasheet right now, but I seem to recall 25 MHz was about the max crystal frequency the PIC could drive.  I'm using 16 MHz for now.


When you download the Microchip C18 compiler, be sure to check out the included documentation.  It's located in the install directory in a "doc" folder (for me it is at C:\Program Files (x86)\Microchip\mplabc18\v3.40\doc).  The "Getting Started" document and "User Guide" are obviously helpful, but the "hlpPIC18ConfigSet.chm" and "PIC18F Peripheral Library Help Document.chm" are also extremely useful.  The first one lets you select your microcontroller and then list all of the configuration settings and possible values.  The configuration settings do similar things as fuses in AVRs, but it seems to be a little smoother with PICs to use them.  The issue I had at first is that the configuration settings names between chips vary, so if you're trying to get sample code for a different PIC to work with yours and are seeing compiler issues it'd be a good idea to reference this document.  For example, the sample LED blink program I found in the user guide used "WDT" to set the watchdog timer to off, but for the  PIC18LF26J11 , I needed to use "WDTEN" instead.  The library help document shows all of the functions available in the C18 library, and again they're broken down by what's available for your specific microcontroller.

The following code is what I wrote as a sample program to make sure I had everything working correctly and could program the PIC.  I wired an LED each to pins 2, 3, & 4 on the PIC (PORTA0-2), and set them blinking at a 1, 2, & 4 Hz rate.  This is where I figured out about the crystal frequency being off, because the blink rate was much slower than in should have been.  I'm using two of the libraries that come with the C18 compiler, and had to add them to the project to get it to work (though I just referenced them from their default location).

#include <p18lf26j11.h>
#include <delays.h>

#pragma config OSC = HS //High speed crystal
#pragma config WDTEN = OFF //Disable watchdog timer
#pragma config XINST = OFF //Disable Extended CPU mode

#define LED1hzPin LATAbits.LATA0
#define LED1hzTris TRISAbits.TRISA0
#define LED2hzPin LATAbits.LATA1
#define LED2hzTris TRISAbits.TRISA1
#define LED4hzPin LATAbits.LATA2
#define LED4hzTris TRISAbits.TRISA2

char i;

void main()
{
        //Set LED Pins data direction to OUTPUT
        LED1hzTris = 0;
        LED2hzTris = 0;
        LED4hzTris = 0;
        //Set LED Pins to OFF
        LED1hzPin = 0;
        LED2hzPin = 0;
        LED4hzPin = 0;

        i = 0;
        while(1)
        {
            Delay10KTCYx(50);//Delay 1/8 sec
            LED4hzPin = ~LED4hzPin;//Toggle LED Pin
            if (i%2==0)
                LED2hzPin = ~LED2hzPin;//Toggle LED Pin
            if (i%4==0)
                LED1hzPin = ~LED1hzPin;//Toggle LED Pin
            i++;
        }
}

This step was pretty simple, but it's one that can take some time tracking down silly errors when you're starting with something new, and now I'm ready to move on to my actual project.  Next up will be getting the RFID module working.

Thursday, April 26, 2012

28 Pin PIC18F Development Board



The next project I'm working on incorporates several things I've never worked with before, and instead of buying breakout boards for some parts and breadboarding the rest, I decided to design my own development board that was tailored specifically to my project.  While this board is designed with a particular project in mind, I still tried to add generic features and as much flexibility as I could.  I'm trying to learn how to use a variety of different microcontrollers (I've mostly used AVRs and MSP430s up til now), and this project seemed like a good fit to try out PICs.  The MSP430F/G chips I have are 14 pin with 2KB flash, and I needed more than that for my project.  The AVRs I have are in the ATmegaXX8 line, which certainly could have worked, but looking through the Microchip 28pin options, I saw a lot more flexibility with peripherals.  Having multiple SPI and USART ports would be useful for this project, and USB could work well for other projects to avoid using an FTDI or MCP2200.  In the end, it was mostly the chance to learn something new that made me decide to go with a PIC18 for this project.

Several sites offer a 10 copies of 10x10 cm pcb for ~$25 deal, so I initially planned on that size, but I'm using the freeware version of Cadsoft Eagle for the PCB layout, so board size was limited to 10x8 cm.  I upgraded to Eagle 6 for this layout, but I didn't notice too many changes from version 5 on the user interface side.  My goal was to essentially make a simple breakout board for the VS1003 MP3 chip, SD card, and PIC18 on a single board, add a power supply, and then leave the rest as a prototyping area.  The prototyping area is just big enough for an RFID module from ITeadStudio with two extra rows, but also makes the space more useful in projects that don't use RFID.

Most of the eagle parts I used came from the default libraries, but I also made use of the Sparkfun library and Adafruit library.  In theory, the Sparkfun library is nice because it contains part numbers so you can order straight from them and know that your parts will match the footprint, but it's not foolproof. While Sparkfun only gives you Sparkfun part numbers, Adafruit goes further and gives part numbers for multiple major distributors (Digikey, Mouser, etc.), so I used Adafruit's library whenever a part was in both.

For the power supply, I used separate MCP1702 low dropout regulators.  This regulator is available in a variety of voltages (1.20V, 1.5V, 1.8V, 2.5V, 2.8V, 3.0V, 3.3V, 4.0V, 5.0V)  and has a typical active current of 2uA, making it a good fit for battery operated projects.  They have a typical voltage drop of 625 mA, compared to 2V for an LM7805, so the power supply input doesn't need to be as high.  I used separate regulators for each of the 3 supplies for the VS1003 chip (IOVDD, CVDD, AVDD), a regulator for the PIC CVDD, and then 3.3V and 5V generic supplies for the PIC and other parts.  There is a PTC fuse for protection, and the power input is pretty flexible and can come from USB, a barrel plug wall adapter, or two wires from a battery pack.  I used solder jumpers throughout the board so that I can configure the board as needed for each project.  In addition to the jumpers selecting the power input, there's one that allows selection of 3.3V or 5V power to the PIC.


I looked at the pinouts for the two PIC18F series chips I had on hand as samples from Microchip (PIC18LF26J11 and PIC18LF26J50) and added the basic support circuit.  This included the ICSP connector for programming/debugging, reset circuitry, a crystal, and decoupling capacitors.  One of the chips has USB built in, so I routed those pins to a mini-B connector, but solder jumpers are used so that is a select-able feature.  I will not be using USB for the project I'm working towards right now, but I'd like the option to use it in the future.


The circuit for the VS1003 mp3 chip is straight out of page 15 of the datasheet, and is definitely the part of this board in which I am the least confident.  I left out the microphone input portion of the circuit because I didn't need it, but everything else is included.  I didn't route any of the pins to the PIC, but I broke out the pins required for interfacing, so it's basically a breakout board for the VS1003 on the same PCB.   I used a Sparkfun part for a headphone jack that had a part number, but it was a mistake in their library and it wasn't until I got the board and was starting to order parts that I realized the problem.  I'll probably end up getting a separate breakout board for just the headphone jack of the part of my project.


The SD card portion of the PCB is also essentially just a breakout board.  There's a jumper that allows it to use the PIC's Vcc (which should only be selected if it's 3.3V).  I used the Adafruit SD card socket footprint, which was only available at 4UCON or Newark, but I found one that looked similar on Digikey and decided to get it, and it ended up being a match.


I put footprints for 5 LEDs on the board, mostly for status and testing as I'm developing.


I used the Open PCB service from iTeadStudio when I had my boards made, which for just $0.10 you get two random PCBs that someone else designed, and two copies of your boards are sent to random people.  The chances I'd get something useful were slim, but for ten cents it was worth it.  One of the boards I got looks like one of iTead Studio's Arduino Mega Sensor Shield.  I don't have an Arduino mega, and don't plan on getting one, so this one isn't really useful to me.  The other board was a breakout board for a TQFP-32 ATmega8.  Someone used the 5cm x 5cm service and put three copies of a simple breakout board onto a single PCB.  It looks like there's enough room that I could use my dremel to cut out 3 separate breakout boards.  This one looks more useful, and I actually have some TQFP ATmega88's on hand that I can use with it.  If anyone out there ended up with a copy of my board and happens to read this, I'd love to hear from you and see if it can be of any use to you.  Here's a picture of the two boards I received.


This is first PCB I've made where I've used silkscreen, and it was a lot easier than I thought...but it didn't turn out too well.  The first PCB I designed I used a company that didn't offer free silkscreen, so I left it off, and didn't bother with the next couple either.  Because I'm intending this to be more of a development board and will be doing quite a bit of wiring after all the components are soldered on, I realized that having labels on the rows of broken out pins would be very helpful.  The eagle "smash" command, in particular, was very helpful in laying out the board so that the text was not overlapping from different parts.  I resized a lot of the text, but didn't really have a good idea of what it would look like on the actual PCB.  A lot of it ended up being really tiny...way too small to read, and some of it didn't show up at all.  I always have the eagle files up on my computer while I'm working anyway, but it would've been nice to have better labeling on the board than I ended up with.  I'll have to pay more attention to text size in the future.  Here's a picture of the how the boards turned out.



I got all of the parts I needed from Digikey except for the VS1003 chip and the headphone jack, which I'll from Sparkfun, and the RFID module from ITeadStudio.  My end goal is to make a book reader for my daughter.  The basic idea is that I'll have extended family record themselves reading books to my daughter.  I'll put the files on an SD card, and have each one tied to an RFID tag taped inside the books so that she can swipe the book over the device and it will start reading the book to her.  My Sourceforge page for this project (https://sourceforge.net/projects/pic18devboard/) has the board design files and an Excel workbook with a (mostly complete) bill of materials with part numbers.  I'll add additional files to it as I get some of the firmware tested out and working.  My next few posts will go through getting started with PICs and implementing the different pieces needed to complete my project, assuming all goes as planned.