2012-09-24

Revealing hidden awesomeness of the TI MSP430 LaunchPad

TI (Texas Instruments) is awesome, I've always known it. What I like the most is their extremely low-cost ($4.30, who beats that?) Launchpad development kit that includes 2 "value line" (pronounced "super-cheap") micro-controllers, USB programmer and development software. That is everything you need to dive into the wonderful world of the super-lower-power microcontrollers.


Picking the test project
There are so many options for the first project: flashing LED, doomsday device, iSomething…
Hm, iSomething sounds like a cool idea. How about making a poor-guy MP3 player? Maybe downgraded to the extreme: one song, poor audio quality, no control buttons?
To make digital audio player we need two major components: storage memory for digital audio and Digital-to-Analog  Converter (DAC). Expectedly, such a simple microcontroller does not have a built-in DAC, so sounds like I would need a separate chip. But wait,

Who needs a DAC for lousy quality audio?
There is an easy alternative to DAC – Pulse Width Modulation (PWM). The idea is to generate audio signal with variable-width digital pulses, with high enough pulse frequency it should produce acceptable quality audio. Sure enough TI has an application note to demonstrate exactly that (PWM for audio application). Since I decided not to worry about audio quality, I picked 8 bit for audio resolution (as a side note - it is supposed to be roughly equal to AM radio quality). 8 bit resolution gives me 256 possible PWM widths (for example, value 10 means that output stays on for 10 cycles and after that turns off for 255-10=245 cycles).
Assuming that my TI chip runs at the max speed (16 MHz) my complete PWM cycle would be: 16,000,000Hz/256=62,500 Hz. Wow, at such high PWM frequency I should be able to generate sound with frequencies up to 62,500Hz/2=31,250 Hz (this calculation is based on Whittaker–Nyquist–Kotelnikov–Shannon sampling theorem). But it also means that I have to store 31,250 bytes of data for 1 second of music. Where am I going to store my music?

External storage
MSP430G2231 only has 2KB of memory. Because I need 31,250 bytes to store 1 second of music, I could store the whole 0.06 seconds using internal storage. This is definitely not enough. Well, it just means that I need an external storage. How about  SD/MMC Card? It is a perfect external storage for microcontrollers because it is inexpensive and supports very microcontroller friendly SPI protocol. The Launchpad came with 2 chips: MSP430G2211 and MSP430G2231 the latest has built-in universal serial interface that supports SPI protocol, perfect! And of course after checking TI website, I found this application note to interface between MSP430 and SD Card. Now I need to decide how to store the music.

Audio format battle: mp3 vs au
It would be really cool to play mp3 files on the Launchpad, but it's not going to work. For example, the smallest Helix mp3 decoder needs ~20K of program memory, and my microcontroller has only 2K. Of course I could add an external mp3 decoder - but that's not fun.
Since mp3 is not going to work, I could just write audio as a stream of samples (bytes) and add simple header to tell how many samples in my file, sample rate, etc. But it would be nice to use a standard audio file format, so I choose arguably the simplest audio file format ever created: au format. Even it is a quite old format, but still is supported by many applications including my favorite (open source) audio editor – audacity.

Getting audio file to the SD card
To store file to a SD card is also tricky. For multiple reasons I decided not to use a file system on the SD card, so I have to write .au file to the card as a raw data. This could be done by HDD imaging tools, I had good experience with DiskImage, although other disk imaging software (such as dd) should work.

Hardware
There are only two external components to the Launchpad used: SD card and earbuds headphone. I connected SD card power to one of the Launchpad pins, so I can reset the card by toggling its power. Earbuds headphone connected to the Launchpad PWM output pin through a current limiting resistor. "But where is your low-pass filter?" curious reader will ask. Well, yes, I should be using a low-pass filter to smooth-out PWM pulses. But headphones work well enough as a low-pass filter due to their mechanical nature. So, cancel the filter and connect everything together!

SD card access time issue
After fixing a few software bugs I finally heard something in the headphones. It was sort-of a music, but with some strange clicking noise. Such a disappointment! I though I was done with it.
It turned out that data on the SD card stored in 512 byte blocks and sometimes switching these blocks takes some time, so every so often I was missing a few samples and it caused those clicks. I added a small (16 samples) FIFO buffer between SD card and PWM. Buffer made its magic and I was enjoying AM quality music for a whole 15 minutes or so.

Conclusion
It's totally possible to play an acceptable quality audio using the most basic microcontroller. Sample rate could be pretty high (~31kHz in my case) and allows to achieve a much better audio quality, although PWM resolution (8 bit) is a limiting factor.
One of the options to drastically improve audio quality would be to use two independent 8 bit PWMs and summing up their signals. This would increase audio resolution to 16 bits which should sound (almost) like a CD quality. Although, MSP430G2231 has only one PWM channel, so it's not an option here.

Video
Here is a video of the Launchpad playing (kind of) music, enjoy.



Source Code
Grab it here.

2012-03-19

Read Command Line Parameters from VBA

Recently, I needed to read a command line parameter within a VBA code (it's for an Iconics SCADA application, but I developed it with Excel VBA which is essentially the same). It doesn't sound like a big deal but there were a few not-so-obvious tricks that I thought were worth sharing.
I started with the simplest solution using a "GetCommandLineA" Windows API function from kernel32.dll. It was supposed to return a pointer to the command line string, so I assumed that its return type would be string (sounds logical to me).
' The return type of the GetCommandLineA is long
' But it is supposed to be a pointer to the string. 
' Assumming that all VBA strings are passed by pointers
' Just declare it's return type to be a string
Declare Function GetCommandLineA Lib "Kernel32" () As String
 
Sub ReadCmdLine()

   Dim strCmdLine As String ' Command line string   
   ' Read command line parameters into the string
   strCmdLine = GetCommandLineA

End Sub
Surprise - it caused exception and crashed the application. Hm, apparently VBA handles a pointer to the string differently than GetCommandLineA. Not wanting to spend time figuring out why, I decided to copy the string to VBA string using another kernel32.dll function "lstrcpynA".
' Declare the return type to be a pointer (long)
Declare Function GetCommandLineA Lib "Kernel32" () As Long
Declare Function lstrcpynA Lib "kernel32" ( _ 
   ByVal pDestination As String, ByVal pSource As Long, _
   ByVal iMaxLength As Integer) As Long
 
Sub ReadCmdLine()

   Dim pCmdLine as long     ' Pointer to the string
   Dim strCmdLine As String ' Command line string     

   pCmdLine = GetCommandLineA
   ' Copy from the pointer to VBA-Style string 
   ' 300 characters for command line seems to be enough
   lstrcpynA strCmdLine , pCmdLine, 300    

End Sub

This worked better by no longer crashing the application, but the strCmdLine was always empty. After short Google search I found this Microsoft article. Apparently lstrcpynA function (as other DLL functions returning strings) can't change the size of the VBA-style string. In order to reserve the space for that return data, we need to fill the string with a bunch of zeros (vbNullChar).
Declare Function GetCommandLineA Lib "Kernel32" () As Long
Declare Function lstrcpynA Lib "kernel32" ( _
   ByVal pDestination As String, ByVal pSource As Long, _
   ByVal iMaxLength As Integer) As Long
 
Sub ReadCmdLine()

   Dim pCmdLine as long     ' Pointer to the string
   Dim strCmdLine As String ' Command line string   

   ' Get the pointer to the command line string
   pCmdLine = GetCommandLineA

   ' Fill the string with zeros
   ' 300 characters for command line seems to be enough
   strCmdLine = String$(300, vbNullChar)

   ' Copy from the pointer to VBA-style string
   lstrcpynA strCmdLine , pCmdLine, Len(strCmdLine )

   ' At this point we got the string
   ' But rest of it filled with 0 characters.
   strCmdLine = Left(strCmdLine , InStr(1, strCmdLine , _
      vbNullChar) - 1)
       
End Sub

This code finally worked as expected, returning command line arguments.
Hopefully, this article is useful in saving someone 15 minutes of frustration.

2012-03-11

Tired of "Hands Free"? - How about "Hands Full" solution?

I used to have an old Motorolla cell phone, actually it wasn't even a phone, back then it was called DPC - "Digital Personal Communicator", which is in my mind sounds much more scientific than a modern "cell phone". So this is used to be an awesome piece of equipment back in its days (actually it was the smallest  phone in 1989), but it's quite useless now, since it does not support modern networks (it was designed for AMPS network, but this service had been discontinued around 2007).Cell_Front

I was thinking of disposing this device, but luckily I managed to nearly destroy my bluetooth hands-free (Jabra BT250) by accidentally dropping it and stepping on it. Blending two useless devices together promised an interesting result...
So the first step was to get to the guts of both devices. Cracking open the handsfree was quite simple - it was already broken.
Opening a cell phone was a bit more complicated since I wanted to preserve its authentic look. There are four plastic locks underneath the battery and one screw under the label, ironically there is a warning "phone disassembly will void the warranty" on the label. But I didn't worry about warranty.
Cell_Back
After opening the back cover I realized that something else is holding it - there are two plastic shafts that hold the folding mouthpiece and both halfs of the phone together (you can see this shafts on the photo below). You have to pull both shafts towards the center, fortunately there is a small hole in each shaft which makes it easy to move by a big needle. Additionally, there are two plastic locks that hold shafts in place.
Now it's time to layout and somehow attach the new guts inside the phone:
  • Remove old PCB, cut off the bottom part of it with the connector and hot-glue it to the case. This connector is used for aestetic purpose only. Originaly I was thinking to ditch the connector, but decided that the big hole on the bottom of the phone won't look very appealing.
  • Hot-glue old display - for aesthetic purpose 
  • Extend hands-free's LED with wires, hot-glue the LED instead one of the old phone's LED (I glued it in place of unlabeled LED in the left bottom corner of the display)
  • Solder phone's mic and speaker to the hands-free PCB
  • Solder phone's volume buttons in parallel to the hands-free volume buttons
  • Trace one of the phone's keyboard button to the white connector, solder in parallelel with hands free ON button
  • Hot-glue hands-free battery (I placed it atop of the LED display), PCB and vibrator motor. Don't forget to isolate PCB back (I used pieces of electrical tape) 
This is my first attempt of the internal layout
Cell_Open_01
Oops, can't close the case because there is not enough room for the vibrator. Here is the better layout with antenna attached (it's also hot-glued to the case)
Cell_Open_02
I want the cell phone to look like a normal (super-old) phone, without any additional holes/connector visible from outside. At the same time I need somehow to charge it. The old connector wasn't an option - I couldn't find the original charger for it. The only option was to add a new connector and the best location for it was underneath a battery. So I drilled a hole and super-glued the connector (I actually reused the connector from the hands-free charging station).
Cell_Open_with_connector
This is the completed device with connected Jabra charger.
Cell_Back_with_charger
Have fun hacking the stuff :)

2012-03-10

Resurrecting the antique pressure gauge to display internet bandwidth

Who doesn't like old technologies? Like a Nokia cell phone as heavy as a brick and built to last centuries (literary)? I do like old stuff, my dear readers. That's why when one day I came across a cool looking (and rightly priced!) antique pressure gauge on eBay I bought it with very little hesitation. Presumably, gages like this had been used on steamboats and locomotives. At that point I already got an idea to upgrade it to use this piece of the ancient technology for modern purposes, specifically to measure my home internet bandwidth utilization.
And this is what I got in the mail few days later:
Original

Original
Not cool. I am not ready to hang this at my desk - it's too ugly even for me, not to mention my wife. What I really wanted to see is a brass bezel and black body not a rusty bezel and grey rusty-dusty body.
I desperately needed some magic and I found it in the local hardware store: sandpaper, paint remover and spray paint!
After applying my newly acquired magic the gauge started to look much more appealing:
Clean
That’s better. But I still needed a different kind of magic to make it measure something more useful for me than steam pressure. I was thinking of creating an application (running on the PC) to calculate bandwidth utilization and somehow send this information to the gauge via USB.
But how to make the needle move? The answer came from China in form of the miniature RC servo. RC servos are typically used by hobbyists in the radio-controlled models and robotic applications. And it’s a perfect fit for this project because:
  • It’s small, so it fits to the gauge easily
  • It needs 5V power supply (since I am planning to use USB and it provides 5V)
  • It’s easy to control it
But here is a downside - RC servo needs a precise PWM (Pulse Width Modulation) signal for positioning. PC/Windows was born to be almost useless for generating any precise timing/pulses, so I needed something else to generate it. I elected an Atmel AVR microcontroller for this job. Why AVR? Just because I used it before, know this platform reasonably well and love it (well, sort of). Unfortunately, the smallest/simplest AVR microcontroller I was planned to use didn't support USB communication, so I had to add USB-to-serial converter to the mix, since serial protocol is easy to implement even on the tiniest microcontrollers.
Now I am ready to put it all together:
  • PC runs a .NET application. This application calculates bandwidth utilization and sends it to the gage. It sends utilization as percentage, since the gauge is calibrated 0-100
  • Inside the gage there is a USB-to-serial converter that talks to the AVR microcontroller via asynchronous serial communication (this is just a fancy term for serial port)
  • Microcontroller converts utilization percentage to the PWM signal and sends it to the RC servo.
  • RC Servo controls needle position utilizing parts from the original mechanism.
Completed gauge with scale removed:Completed - Opened

On the picture above: RC servo (blue thing, covered with whitish mass), USB to Serial adapter (green board) and microcontroller (small 8-pins chip, to the right of the green board). Everything mounted inside the gauge using my favorite method - glue gun (I am pretty sure Wikipedia knows a better term for it, but "glue gun" is just sounds dangerous).
Now back to the .NET application. It turned out that it's very easy to read a whole bunch of the statistical data from my home wireless router, because it supports a SMNP protocol. SNMP is a standard network management protocol supported by most of the network devices (computers, routers, printers, etc.); it provides access to the device internal status/variables/parameters.
The most important variables to me were sent/received byte counters. There is one set of counters (upload/download) per each Ethernet port (my router has 5 ports); I care only about Ethernet port connected to my DSL modem. By querying these counters every second it's trivial to calculate upload/download speeds. But to calculate bandwidth utilization I also needed to know my maximum Internet upload/download, speedtest.net is a hero of determination of true Internet speed.
Cool, I can calculate both upload and download bandwidth utilization, but which one should I use for the gage? I tried a few options and I found out that I personally like a maximum between upload and download: it tells me when to expect web surfing bog-downs.
.NET application interface is pretty simple, nothing fancy:
PC Application
This is how completed gage looks like:
Completed
Thank you for reading my humble blog and have fun hacking the stuff.
Ah right, I forgot the video of the completed system, enjoy: