Saturday, July 4, 2015

LED POV Fan Update

I first posted on how to reprogram a "Programmable Message Fan: Model 45 Series" POV fan a few years ago, and the code I posted no longer works with the current versions of the Octave and Arduino software.  The original post still describes how the fan works and how I went about figuring it out, and this is just an update to the software to reprogram it.


I'm taking the same general approach, where a script run on a computer writes an Arduino sketch that can be compiled and uploaded via the Arduino IDE.  The Arduino sketch isn't elegant, but it works.  The only real change that broke the generated Arduino code is that they changed the "send" method to "write" in the newer versions of the I2C "Wire" library.  I am using Arduino software version 1.6.5 with a clone of the Arduino UNO board.

On the scripting side, though, things have changed a bit more.  I used Octave back in 2011 because I was using Matlab a lot at work for data processing and analysis, and Octave was pretty similar.  Since then, I've moved on to Python and haven't looked back, so my update here is written in Python rather than trying to update to whatever the current version of Octave is.  I have a pretty simple module called "pov.py", and a demo file called "demo_pov.py" that shows how to use it.  It does some simple checking on the inputs and will filter out invalid screens, then create the .ino file for programming the Arduino.  I tested the Python script with both Python 2.7.5 and 3.4.3.  I'm not using anything beyond the standard library here, but if you're interested in Python, I highly recommend the Anaconda distribution.

I've also switched where I'm hosting my source code files.  In the past, I would just upload a .zip file of source code to SourceForge.  Since then, I've learned a lot about version control and come to really appreciate the benefits of using a version control system.  My personal favorite is Git, and I'm using GitHub to host my projects right now.  Here's the link to the new files.

Wednesday, November 26, 2014

Think Stats: Exploratory Data Analysis by Allen B. Downey; O'Reilly Media

I recently finished reading Think Stats: Exploratory Data Analysis by Allen B. Downey, which is an introduction to using probability and statistics to perform analysis on data sets.  This book uses Python to explore and perform statistical analysis on several example data sets.


I have a decent statistics background (several undergraduate and graduate level statistics courses), and this book definitely took a different approach than I have seen before.  The focus is on an exploratory and computational approach to analyzing a data set.  This approach is very valuable, and provides a much more easily applied skill set than a traditional statistics introduction.

This book is not a thorough reference (though it often provides links to Wikipedia or other external sources for more information), and it won't replace my other statistics textbooks.  However, it is a good introduction to the field (including many more advanced topics) and is easy to follow.  I would be very interested in seeing a class that used this book as the text and followed the approach presented here.  The book flows logically, but the topics were presented in a very different order than I was originally exposed to them.

To get the most out of this book, I would definitely recommend working through the examples.  An even better approach would be to work through the topics on a data set you have at hand that is of interest to you.

Most of the examples in the book use the author's "thinkstats2.py" module.  You can get the thinkstats2.py module (along with other sample code) at the book's GitHub page, and all of the examples can be viewed in IPython Notebooks.  The examples are fairly straightforward, but I have not used to module enough to know whether I would consider it a candidate for a general purpose tool beyond working through the book.  The author assumes you are familiar with Python, and having the module available is a useful tool to allow the reader to focus on the data and analysis.

The non-core Python packages used in this book are: pandas, NumPy, SciPy, StatsModels, and matplotlib.  Pandas, in particular, is used quite heavily in thinkstats2.py.  The author recommends the Anaconda distribution, which gives you all of these packages and many more.  I've been using Anaconda as my primary distribution for the past 6 months or so and am very happy with it.

The book is also available under a CC BY-NC 3.0 license at Green Tea Press.

Disclaimer: I received a free Ebook copy of this work under the O'Reilly Blogger Review Program.

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; 
                }
        }
}