I'm glad to announce that my recent pet project, Beertraq, is now in a
(somewhat closed) beta stage! The basic idea is there and functioning,
but the extra functionality isn't done and it's far from polished.
Nonetheless, it's time to take her for a test drive!
So what is Beertraq, you ask? It's a way for you to keep track of which
beers you've tasted, compare those with others, read beer reviews, and
most importantly discover new beers to try. I originally got the idea
from The Flying Saucer's UFO Club, where members work toward a goal of
drinking 200 different beers. Once they complete the task, they get their
name on a plate which gets put on the wall of the bar. The cool part about
the UFO Club is that it's all computerized, using a magstripe card to login
at a kiosk in the bar. You can also log in to their website to check your
progress and read reviews on there. I figured that if The Flying Saucer can
have that system for their bar, I could do the same for the world.
If you're interested in becoming a BeerTraq beta user, send an email to
beertraq (at) beertraq (dot) com with the email address you want to use for
your account. All I ask is that you give feedback by filling out issue requests
with bugs you find or suggestions you might have.
I've always been utterly fascinated with graph theory, mostly with its
applications to networks. As an added bonus, they can be represented with
pretty pictures!
(click on image for full-sized version)
That graph represents the network behind Nucor-Yamato Steel and Nucor Castrip
Arkansas, sanitized of sensitive information of course. All of the nodes are
Cisco switches, the yellow boxes representing backbone switches (6500 series
to be exact). This graph is part of the network information system that I've
been working on during the majority of my internship at NYS and gets auto-generated
every day, along with more centralized graphs on a per-switch basis.
The way the system works is that a periodic Python script goes out to a list of
known switches and gathers CDP neighbor information as well as the MAC address
tables. Then Nmap scans are ran every 6 hours to scan for hosts, gathering IP
addresses, hostnames, and MAC addresses. These MAC addresses are correlated with
the MAC tables from the switches to determine which hosts are connected to which
ports on what switches. The CDP neighbor information also gives which switches
are connected to each other, giving a full scope of how the network's connected.
The script which generates the graphs grabs all of that information out of the
database, uses NetworkX and pydot to create the graph, and then graphviz to render
it into a PNG image. The graph is pretty plain, though. The real version shows
switch names and IP addresses. Since the time between graph generation is so long,
any more useful information that I could throw onto the graph would quickly become
outdated. My grand scheme is to make a quickly-updated graph showing live stats like
switch load, link load, link types (fiber, twisted pair, wireless), downed switches,
etc. That way, I (or the network supervisor, I guess...) could have a big-screen TV
displaying the live health of the network.
I've been asked what parameters I set to get that graph to look that way. I didn't set anything special in code, it's all in the command line:
Well, I finally bit the bullet and got a Linode account. So far I'm pretty
happy with it. I figured that with the costs of power and bandwidth, I was
almost spending $20/month to run my old server on my own hardware.
Incidentally, the lowest-grade Linode VM costs that much and is enough to
suit my needs.
So now that I've been setting up a webserver from scratch again, I'm doing
it right this time. I'm setting up some monitoring software to notify me when
things go down, I'm no longer relying on myself for DNS (no more dynamic IPs!),
and I'm also branching out and trying an alternative webserver.
The webserver in question is Cherokee which claims
to use less memory and
perform better than Apache. It sure does use less memory, but as a down side
it doesn't have a native PHP module, so I'm required to use FastCGI for that
purpose. Right now, there's five php-cgi processes running each using about
25-30 MB. This wouldn't be a problem except that I've only got 360 MB of memory
to play with. On the plus side its got a pretty sweet admin interface with wizards
to help you set up things like WordPress, Drupal, Ruby on Rails, Django, etc. and
you can setup some pretty complex rules for what and how files should be hosted.
On the monitoring side of things, I'm using Munin to monitor the various
stats on the server, Piwik for website visit statistics, and I plan on getting Monit going
for service monitoring. It's a bit more important now that I keep and eye on memory
and data transfer now that I'm limited on that. Also, if some process goes wild and
starts using crazy amounts of CPU power and memory, I'll be able to catch it.
Unfortunately when you move servers, you have to move everything that was running
on them. I'm still in that process, but it's been going pretty smoothly.
I said that I'd post details on my Intelligent Drink Dispenser project "soon". That
was over a month ago. Whoops. I blame my new internship for that.
For those of you not in the know, the Intelligent Drink Dispenser was my senior design
project at Missouri University of Science and Technology (which will forever in my heart
be University of Missouri-Rolla). It's basically a smart drink dispenser that's capable
of mixing, charging customers, telling the bar/restaurant owner when they need to refill
the machine, etc.
If you don't feel like reading the details and just want to look at the pretty pictures,
you can check out my Picasa album
or watch the Youtube video.
The theoretical process is that the customer would go to order a drink, and since they're
a new customer, they'd have to be entered into the system by the person running the machine
(bartender, or waiting staff). They would have their name and credit card information taken,
and would then be assigned a drinking vessel based on the first drink they were wanting to
order. Multiple vessels could also be assigned to the same person. Once the customer is
setup and is ready to purchase their drink, they set the fluid vessel on the marked reader
area on the dispenser. The system then recognizes the vessel, who it belongs to, asks the
customer for the last four digits of their credit card, and then asks the customer which
drink they'd like to order. The customer then chooses what drink they'd like to have, the
system double-checks that the vessel is the right size, and pours it.
Security and privacy was one of the major goals of the project. The only information stored
about the user is their name, a secure hash of their credit card number, the last four digits
of ther card, and their drink order history.
The project itself can pretty much be split into two major components: hardware and software.
I would probably say the hardware is more interesting and posed more challenges for us. The
first thing is how the heck do you pour the fluid? If you take into consideration that we only
had a $300 budget for the whole shebang, it's not an easy task. The way that the professionals
do it is with Carbon Dioxide-powered pumps, which are controlled by electronic valves and supplied
by a tank and pressure regulator. Three pumps, valves, and the feed system would cost us well
over $300. Our original idea was to use 24 VDC sprinkler valves, but that idea failed because the
sprinkler valves by their nature require back-pressure to operate. We came up with the idea of using
windshield washer pumps made for cars. Since this was supposed to be a prototype, we didn't have to
worry about our components being food-grade. That, coupled with the fact that the pumps operate on
12V DC and are relatively inexpensive ($15-25 a pop), that's what we went with for our design.
The rest of the hardware design was fairly straightfoward. We used an 8051 microcontroller to control
everything, an FTDI UM232 to handle the PC communications and a Parallax RFID reader to read the
tags that are on the bottom of the drinking vessels. The serial communication is pretty interesting
since the USB-to-serial device has only one serial port but two devices to talk to (the RFID reader,
and the PC). Our solution was to have the receive line go to the RFID reader (to the PC), and have
the transmit line go to the 8051 (from the PC). This meant that our 8051 couldn't talk back, so we
had to hope that things were working right. Additionally, Richard developed a simple serial language
for the 8051. If the 8051 received an ASCII 0 through 7, it would turn on that pin on the port we
were using. This could easily be modified to operate with all the ports on the 8051 to control 24
pumps, or even with some addressing logic to control a huge number of pumps.
Below is our hardware schematic, which should give you some idea of how it's all connected.
In my next post, I'll be talking about the software design. Stay tuned!
The other major half of the Intelligent Drink Dispenser project was the software side
of things. As with the hardware, there were some things we had to consider when
beginning the design of things. What language should we use? Should this be a GUI
application or web application? If we go with a GUI application, what operating system
should we target?
We decided that since we only had a semester to get everything done, we needed a
framework to take care of the more nitty-gritty stuff. We eventually decided to go
with the Django web framework, which uses the Python programming language. Python is
a language we all knew and Jon and I had used Django in the past. Also, since our
'final product' would be highly based on a client-server model, it makes sense to
go with a web app since the server can be in a central location with many clients
connecting to it.
The most important thing to us was the fact that Django abstracts the database into models
for us. No having to mess around with raw SQL, we just run some queries and get our data
from the models as if they're just plain ol' objects. After some thought, we came up with
our models and their relationships:
The development of the application was fairly straight-forward. It was just a standard
Django app after all. The interesting part was interfacing with the hardware.
If you remember from my post talking about the hardware side, we used an FTDI UM232
USB-to-serial converter. Lucky for us, there's a Linux kernel module which represents this
device as a virtual serial port, which made our jobs a whole lot easier since there's
existing serial libraries for Python (like pyserial). We ended up just writing a couple of
functions, one to read the RFID tag from the serial port, and one that takes in a Drink id
and then communicates to the microcontroller to pour it. The microcontroller's fairly dumb,
just taking a number and turning that pin on, or turning all of the pins off if a non-valid
character is sent, as you can see in the pourDrink function:
def pourDrink(id, serialPort=DEVICE):
"""Pours the drink given by the specified ID"""
# Seconds per mL of the motors, found experimentally
# These should be in the database or a config file instead of hardcoded...
secondsPerML = []
secondsPerML.append(9.858/350.0) # Pump 0
secondsPerML.append(9.858/350.0) # Pump 1
secondsPerML.append(6.385/210.0) # Pump 2
components = DrinkComponent.objects.filter(drink=id)
for c in components:
stock = IngredientStock.objects.filter(ingredient=c.ingredient)
total = 0
for s in stock:
total += s.amount
if total < c.amount:
raise UnableToPour("Not enough " + c.ingredient.name + " to pour drink!")
port = serial.Serial(serialPort, 2400)
if not port.isOpen():
raise UnableToPour("Unable to open serial port!")
for c in components:
stock = IngredientStock.objects.filter(ingredient=c.ingredient)
leftToPour = c.amount
startTime = time.time()
for s in stock:
port.write(str(s.slot))
if leftToPour < s.amount:
time.sleep(secondsPerML[s.slot]*leftToPour)
s.amount = s.amount - leftToPour
s.save()
break
else:
time.sleep(secondsPerML[s.slot]*(s.amount - ))
leftToPour = leftToPour - s.amount - 30
s.amount = 0
s.save()
print "Time to pour:" + str(time.time() - startTime)
port.write("\n")
port.close()
Not exactly the most precise in terms of pouring accuracy, but it gets the job
done as a prototype. As for the rest of the code, there's not too much else that's
very interesting. I may release the code at some point in the future.
So, I've been pretty quiet about my Intelligent Drink Dispenser project so far,
mostly because there was going to be a competition between myself and
Clint Rutkas and
I didn't want to give the enemy any details.
The communications department of my university, Missouri S&T (formerly UMR), gets
alerts from Google News whenever someone mentions the university's name and since
Clint did just that, his post showed up in their email. After talking with the
director of communications, he decided to run the story. I gotta say, it's a
pretty great way for my Senior Design class to wind down.
For those interested in the details of the Intelligent Drink Dispenser, stay
tuned! I'll be posting more information about it soon.
Side note: since the story got posted, my server's been chugging along to serve
up my website, almost maxing out the upload on my poor cable internet connection.
Here's the traffic graph from my router:
After spending all afternoon fighting with my new server and my DD-WRT router,
I finally figured out how to get my server to PXE boot and fire up an Ubuntu
install. All it really involved was setting up TFTP on another box (my desktop,
to be specific), adding a line to DD-WRT's DNSMasq options, and configuring the
damn server to boot from PXE, which was the hardest part. Luckily, for those of
you who are struggling with it, here's how I did it.
Setting up the PXE client
I had to get my server to boot PXE in the first place. For most people, this just
means poking around in the BIOS. Not for me though.
After poking around the HP site, I've found out that my server is a first
generation Proliant DL360. Since it's an older machine, this means that it doesn't
have a built-in BIOS config, but I had to actually download the old Compaq SmartStart
5.5 CD. I had to hunt around the HP website, but to save you the trouble, you can snag it here:
Once you boot from the CD, you'll want to go into the System Configuration
Utility when prompted. From there, it's just like a giant BIOS. Just turn PXE
on for whatever ethernet port you're using and it's rarin' to go.
Setting up the TFTP server
Once my server was setup for PXE booting, I had to set up a tftp server for it
to grab the boot image from. Since I was using my desktop, which runs Ubuntu, as
a host, setup was pretty easy. I just used tftpd-hpa per the Ubuntu wiki's recommendation.
# sudo aptitude install tftpd-hpa
I had to also edit the configuration file at /etc/default/tftpd-hpa. Mine looks like this:
#Defaults for tftpd-hpa
RUN_DAEMON="yes"
OPTIONS="-l -s /var/lib/tftpboot"
Since I was wanting to PXE boot into an Ubuntu install, I had to extract the
install files into /var/lib/tftpboot as I put in the config file. For example, the
netboot image files for Ubuntu 9.04 can be found here:
DD-WRT uses dnsmasq for DHCP, so if you have a system which uses it too it shouldn't
be too much different to setup. Watch out, though! I initially screwed up my configuration
which really messed with my router.
All you have to do is add a line to the Additional DNSMasq Options found under the Services
tab. If you're running plain dnsmasq, just add the line to your dnsmasq.conf file. The line
goes a little something like this:
dhcp-boot=pxelinux.0,mybox,10.0.0.100
where pxelinux.0 is the file to boot, mybox is the hostname of the tftp server, and 10.0.0.100
is the IP address of the tftp server. You could probably get away with only specifying the
hostname or just leaving it blank and supplying the IP address. You can also get more fancy
and send certain boot images to certain machines, etc. This way works just fine on a home
network like mine.
Once you get this all setup, any machines that try to PXE boot will receive the image and
boot to it. If you used the Ubuntu install image like I did, you'll be able to install Ubuntu
on any PXE-capable machine or even boot into a rescue shell! Just remember that if you can't
setup a boot order (like my Proliant) make sure to disable the PXE boot in dnsmasq before rebooting.
Thanks to a generous donation from Richard Allen, I now have a new server.
It's a Compaq Proliant DL360. From what I can tell of it's past, a place where another
friend of mine was working was getting rid of their old hardware and he snagged a bunch.
Richard got some of the servers, had no use for this one, and then gave it to me a couple
of days ago.
It's probably a first generation DL360, but that doesn't mean it sucks. It's got dual
Pentium IIIs running at 1.266 GHz each, 512 MB of RAM, and two 18.3 GB SCSI drives running
in RAID1. It's quite an upgrade from my current server, which is dual P3 450 MHz with a
little less than 512 MB of RAM. The best part of the whole thing, though, is the Remote
Insight Lights-Out Edition II card that came with it. It's a PCI-X card that redirects
video, keyboard, and mouse from the system and supplies it to a web interface. This means
that no matter where I am, I can get a physical terminal to my server. Plus, if I plug an
external power supply to it, I can even turn on my server remotely!
I'll probably be moving everything from my current server over to the new one. Since I'm
mirroring and then cutting over, there shouldn't be any problems with any of the services
that my box supplies.