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: